diff options
Diffstat (limited to 'src')
297 files changed, 10714 insertions, 7245 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 35af94fb..f0c988dd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -308,8 +308,6 @@ SET(SRCS gui/textdialog.h gui/textpopup.cpp gui/textpopup.h - gui/theme.cpp - gui/theme.h gui/trade.cpp gui/trade.h gui/truetypefont.cpp @@ -318,8 +316,6 @@ SET(SRCS gui/unregisterdialog.h gui/updatewindow.cpp gui/updatewindow.h - gui/userpalette.cpp - gui/userpalette.h gui/viewport.cpp gui/viewport.h gui/windowmenu.cpp @@ -358,6 +354,8 @@ SET(SRCS resources/ambientlayer.h resources/animation.cpp resources/animation.h + resources/beinginfo.cpp + resources/beinginfo.h resources/colordb.cpp resources/colordb.h resources/dye.cpp @@ -380,8 +378,6 @@ SET(SRCS resources/mapreader.h resources/monsterdb.cpp resources/monsterdb.h - resources/monsterinfo.cpp - resources/monsterinfo.h resources/music.cpp resources/music.h resources/npcdb.cpp @@ -392,8 +388,14 @@ SET(SRCS resources/resourcemanager.h resources/soundeffect.h resources/soundeffect.cpp + resources/specialdb.cpp + resources/specialdb.h resources/spritedef.h resources/spritedef.cpp + resources/theme.cpp + resources/theme.h + resources/userpalette.cpp + resources/userpalette.h resources/wallpaper.cpp resources/wallpaper.h utils/base64.cpp @@ -414,6 +416,12 @@ SET(SRCS utils/xml.h utils/zlib.cpp utils/zlib.h + actor.cpp + actor.h + actorsprite.cpp + actorsprite.h + actorspritemanager.cpp + actorspritemanager.h animatedsprite.cpp animatedsprite.h animationparticle.cpp @@ -422,8 +430,8 @@ SET(SRCS avatar.h being.cpp being.h - beingmanager.cpp - beingmanager.h + chatlog.cpp + chatlog.h client.cpp client.h channel.cpp @@ -432,18 +440,21 @@ SET(SRCS channelmanager.h commandhandler.cpp commandhandler.h - configlistener.h + compoundsprite.cpp + compoundsprite.h configuration.cpp configuration.h + defaults.cpp + defaults.h effectmanager.cpp effectmanager.h emoteshortcut.cpp emoteshortcut.h equipment.h + event.cpp + event.h flooritem.cpp flooritem.h - flooritemmanager.cpp - flooritemmanager.h game.cpp game.h graphics.cpp @@ -453,6 +464,8 @@ SET(SRCS guild.h imageparticle.cpp imageparticle.h + imagesprite.cpp + imagesprite.h inventory.cpp inventory.h item.cpp @@ -463,6 +476,8 @@ SET(SRCS joystick.h keyboardconfig.cpp keyboardconfig.h + listener.cpp + listener.h localplayer.cpp localplayer.h log.cpp @@ -471,10 +486,6 @@ SET(SRCS main.h map.cpp map.h - monster.cpp - monster.h - npc.cpp - npc.h openglgraphics.cpp openglgraphics.h particle.cpp @@ -486,8 +497,8 @@ SET(SRCS particleemitterprop.h party.cpp party.h - player.cpp - player.h + playerinfo.cpp + playerinfo.h playerrelations.cpp playerrelations.h position.cpp @@ -514,6 +525,7 @@ SET(SRCS tileset.h units.cpp units.h + variabledata.h vector.cpp vector.h ) @@ -570,6 +582,8 @@ SET(SRCS_TMWA SET(SRCS_MANA net/manaserv/adminhandler.cpp net/manaserv/adminhandler.h + net/manaserv/attributes.cpp + net/manaserv/attributes.h net/manaserv/beinghandler.cpp net/manaserv/beinghandler.h net/manaserv/buysellhandler.cpp @@ -580,6 +594,7 @@ SET(SRCS_MANA net/manaserv/chathandler.h net/manaserv/connection.cpp net/manaserv/connection.h + net/manaserv/defines.h net/manaserv/effecthandler.cpp net/manaserv/effecthandler.h net/manaserv/gamehandler.cpp @@ -610,11 +625,9 @@ SET(SRCS_MANA net/manaserv/partyhandler.h net/manaserv/playerhandler.cpp net/manaserv/playerhandler.h - net/manaserv/protocol.h + net/manaserv/manaserv_protocol.h net/manaserv/specialhandler.cpp net/manaserv/specialhandler.h - net/manaserv/stats.cpp - net/manaserv/stats.h net/manaserv/tradehandler.cpp net/manaserv/tradehandler.h ) diff --git a/src/actor.cpp b/src/actor.cpp new file mode 100644 index 00000000..5ab7ab51 --- /dev/null +++ b/src/actor.cpp @@ -0,0 +1,57 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "actor.h" + +#include "map.h" + +#include "resources/image.h" + +Actor::Actor(): + mMap(NULL) +{} + +Actor::~Actor() +{ + setMap(NULL); +} + +void Actor::setMap(Map *map) +{ + // Remove Actor from potential previous map + if (mMap) + mMap->removeActor(mMapActor); + + mMap = map; + + // Add Actor to potential new map + if (mMap) + mMapActor = mMap->addActor(this); +} + +int Actor::getTileX() const +{ + return getPixelX() / mMap->getTileWidth(); +} + +int Actor::getTileY() const +{ + return getPixelY() / mMap->getTileHeight(); +} diff --git a/src/actor.h b/src/actor.h new file mode 100644 index 00000000..367bcd75 --- /dev/null +++ b/src/actor.h @@ -0,0 +1,128 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef ACTOR_H +#define ACTOR_H + +#include "vector.h" + +#include <list> + +class Actor; +class Graphics; +class Image; +class Map; + +typedef std::list<Actor*> Actors; + +class Actor +{ +public: + Actor(); + + virtual ~Actor(); + + /** + * Draws the Actor to the given graphics context. + * + * Note: this function could be simplified if the graphics context + * would support setting a translation offset. It already does this + * partly with the clipping rectangle support. + */ + virtual bool draw(Graphics *graphics, int offsetX, int offsetY) const = 0; + + /** + * Returns the horizontal size of the actors graphical representation + * in pixels or 0 when it is undefined. + */ + virtual int getWidth() const + { return 0; } + + /** + * Returns the vertical size of the actors graphical representation + * in pixels or 0 when it is undefined. + */ + virtual int getHeight() const + { return 0; } + + /** + * Returns the pixel position of this actor. + */ + const Vector &getPosition() const + { return mPos; } + + /** + * Sets the pixel position of this actor. + */ + virtual void setPosition(const Vector &pos) + { mPos = pos; } + + /** + * Returns the pixels X coordinate of the actor. + */ + int getPixelX() const + { return (int) mPos.x; } + + /** + * Returns the pixel Y coordinate of the actor. + */ + virtual int getPixelY() const + { return (int) mPos.y; } + + /** + * Returns the x coordinate in tiles of the actor. + */ + virtual int getTileX() const; + + /** + * Returns the y coordinate in tiles of the actor. + */ + virtual int getTileY() const; + + /** + * Returns the number of Image layers used to draw the actor. + */ + virtual int getNumberOfLayers() const + { return 0; } + + /** + * Returns the current alpha value used to draw the actor. + */ + virtual float getAlpha() const = 0; + + /** + * Sets the alpha value used to draw the actor. + */ + virtual void setAlpha(float alpha) = 0; + + void setMap(Map *map); + + Map* getMap() const + { return mMap; } + +protected: + Map *mMap; + Vector mPos; /**< Position in pixels relative to map. */ + +private: + Actors::iterator mMapActor; +}; + +#endif // ACTOR_H diff --git a/src/actorsprite.cpp b/src/actorsprite.cpp new file mode 100644 index 00000000..e43d94e1 --- /dev/null +++ b/src/actorsprite.cpp @@ -0,0 +1,463 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "actorsprite.h" + +#include "client.h" +#include "event.h" +#include "imagesprite.h" +#include "localplayer.h" +#include "log.h" +#include "simpleanimation.h" +#include "sound.h" +#include "statuseffect.h" + +#include "net/net.h" + +#include "resources/image.h" +#include "resources/imageset.h" +#include "resources/resourcemanager.h" +#include "resources/theme.h" + +#include <cassert> + +#define EFFECTS_FILE "effects.xml" + +ImageSet *ActorSprite::targetCursorImages[2][NUM_TC]; +SimpleAnimation *ActorSprite::targetCursor[2][NUM_TC]; +bool ActorSprite::loaded = false; + +ActorSprite::ActorSprite(int id): + mId(id), + mStunMode(0), + mStatusParticleEffects(&mStunParticleEffects, false), + mChildParticleEffects(&mStatusParticleEffects, false), + mMustResetParticles(false), + mUsedTargetCursor(NULL) +{} + +ActorSprite::~ActorSprite() +{ + setMap(NULL); + + mUsedTargetCursor = NULL; + + // Notify listeners of the destruction. + Mana::Event event(EVENT_DESTROYED); + event.setActor("source", this); + event.trigger(CHANNEL_ACTORSPRITE); +} + +bool ActorSprite::draw(Graphics *graphics, int offsetX, int offsetY) const +{ + // TODO: Eventually, we probably should fix all sprite offsets so that + // these translations aren't necessary anymore. The sprites know + // best where their base point should be. + const int px = getPixelX() + offsetX - 16; + // Temporary fix to the Y offset. + const int py = getPixelY() + offsetY - + ((Net::getNetworkType() == ServerInfo::MANASERV) ? 15 : 32); + + if (mUsedTargetCursor) + { + mUsedTargetCursor->reset(); + mUsedTargetCursor->update(tick_time * MILLISECONDS_IN_A_TICK); + mUsedTargetCursor->draw(graphics, px, py); + } + + return drawSpriteAt(graphics, px, py); +} + +bool ActorSprite::drawSpriteAt(Graphics *graphics, int x, int y) const +{ + return CompoundSprite::draw(graphics, x, y); +} + +void ActorSprite::logic() +{ + // Update sprite animations + update(tick_time * MILLISECONDS_IN_A_TICK); + + // Restart status/particle effects, if needed + if (mMustResetParticles) + { + mMustResetParticles = false; + for (std::set<int>::iterator it = mStatusEffects.begin(); + it != mStatusEffects.end(); it++) + { + const StatusEffect *effect = StatusEffect::getStatusEffect(*it, true); + if (effect && effect->particleEffectIsPersistent()) + updateStatusEffect(*it, true); + } + } + + // Update particle effects + mChildParticleEffects.moveTo(mPos.x, mPos.y); +} + +void ActorSprite::actorLogic() +{ +} + +void ActorSprite::setMap(Map* map) +{ + Actor::setMap(map); + + // Clear particle effect list because child particles became invalid + mChildParticleEffects.clear(); + mMustResetParticles = true; // Reset status particles on next redraw +} + +void ActorSprite::controlParticle(Particle *particle) +{ + mChildParticleEffects.addLocally(particle); +} + +void ActorSprite::setTargetType(TargetCursorType type) +{ + if (type == TCT_NONE) + untarget(); + else + mUsedTargetCursor = targetCursor[type][getTargetCursorSize()]; +} + +struct EffectDescription { + std::string mGFXEffect; + std::string mSFXEffect; +}; + +static EffectDescription *default_effect = NULL; +static std::map<int, EffectDescription *> effects; +static bool effects_initialized = false; + +static EffectDescription *getEffectDescription(xmlNodePtr node, int *id) +{ + EffectDescription *ed = new EffectDescription; + + *id = atoi(XML::getProperty(node, "id", "-1").c_str()); + ed->mSFXEffect = XML::getProperty(node, "audio", ""); + ed->mGFXEffect = XML::getProperty(node, "particle", ""); + + return ed; +} + +static EffectDescription *getEffectDescription(int effectId) +{ + if (!effects_initialized) + { + XML::Document doc(EFFECTS_FILE); + xmlNodePtr root = doc.rootNode(); + + if (!root || !xmlStrEqual(root->name, BAD_CAST "being-effects")) + { + logger->log("Error loading being effects file: " + EFFECTS_FILE); + return NULL; + } + + for_each_xml_child_node(node, root) + { + int id; + + if (xmlStrEqual(node->name, BAD_CAST "effect")) + { + EffectDescription *EffectDescription = + getEffectDescription(node, &id); + effects[id] = EffectDescription; + } + else if (xmlStrEqual(node->name, BAD_CAST "default")) + { + EffectDescription *effectDescription = + getEffectDescription(node, &id); + + if (default_effect) + delete default_effect; + + default_effect = effectDescription; + } + } + + effects_initialized = true; + } // done initializing + + EffectDescription *ed = effects[effectId]; + + return ed ? ed : default_effect; +} + +void ActorSprite::setStatusEffect(int index, bool active) +{ + const bool wasActive = mStatusEffects.find(index) != mStatusEffects.end(); + + if (active != wasActive) + { + updateStatusEffect(index, active); + if (active) + mStatusEffects.insert(index); + else + mStatusEffects.erase(index); + } +} + +void ActorSprite::setStatusEffectBlock(int offset, Uint16 newEffects) +{ + for (int i = 0; i < STATUS_EFFECTS; i++) + { + int index = StatusEffect::blockEffectIndexToEffectIndex(offset + i); + + if (index != -1) + setStatusEffect(index, (newEffects & (1 << i)) > 0); + } +} + +void ActorSprite::internalTriggerEffect(int effectId, bool sfx, bool gfx) +{ + logger->log("Special effect #%d on %s", effectId, + getId() == player_node->getId() ? "self" : "other"); + + EffectDescription *ed = getEffectDescription(effectId); + + if (!ed) + { + logger->log("Unknown special effect and no default recorded"); + return; + } + + if (gfx && !ed->mGFXEffect.empty()) + { + Particle *selfFX; + + selfFX = particleEngine->addEffect(ed->mGFXEffect, 0, 0); + controlParticle(selfFX); + } + + if (sfx && !ed->mSFXEffect.empty()) + sound.playSfx(ed->mSFXEffect); +} + +void ActorSprite::updateStunMode(int oldMode, int newMode) +{ + if (this == player_node) + { + Mana::Event event(EVENT_STUN); + event.setInt("oldMode", oldMode); + event.setInt("newMode", newMode); + event.trigger(CHANNEL_ACTORSPRITE); + } + + handleStatusEffect(StatusEffect::getStatusEffect(oldMode, false), -1); + handleStatusEffect(StatusEffect::getStatusEffect(newMode, true), -1); +} + +void ActorSprite::updateStatusEffect(int index, bool newStatus) +{ + if (this == player_node) + { + Mana::Event event(EVENT_UPDATESTATUSEFFECT); + event.setInt("index", index); + event.setBool("newStatus", newStatus); + event.trigger(CHANNEL_ACTORSPRITE); + } + + handleStatusEffect(StatusEffect::getStatusEffect(index, newStatus), index); +} + +void ActorSprite::handleStatusEffect(StatusEffect *effect, int effectId) +{ + if (!effect) + return; + + // TODO: Find out how this is meant to be used + // (SpriteAction != Being::Action) + //SpriteAction action = effect->getAction(); + //if (action != ACTION_INVALID) + // setAction(action); + + Particle *particle = effect->getParticle(); + + if (effectId >= 0) + { + mStatusParticleEffects.setLocally(effectId, particle); + } + else + { + mStunParticleEffects.clearLocally(); + if (particle) + mStunParticleEffects.addLocally(particle); + } +} + +void ActorSprite::setupSpriteDisplay(const SpriteDisplay &display, + bool forceDisplay) +{ + clear(); + + SpriteRefs it, it_end; + + for (it = display.sprites.begin(), it_end = display.sprites.end(); + it != it_end; it++) + { + std::string file = "graphics/sprites/" + (*it)->sprite; + int variant = (*it)->variant; + addSprite(AnimatedSprite::load(file, variant)); + } + + // Ensure that something is shown, if desired + if (size() == 0 && forceDisplay) + { + if (display.image.empty()) + addSprite(AnimatedSprite::load("graphics/sprites/error.xml")); + else + { + ResourceManager *resman = ResourceManager::getInstance(); + std::string imagePath = "graphics/items/" + display.image; + Image *img = resman->getImage(imagePath); + + if (!img) + img = Theme::getImageFromTheme("unknown-item.png"); + + addSprite(new ImageSprite(img)); + } + } + + mChildParticleEffects.clear(); + + //setup particle effects + if (Particle::enabled) + { + std::list<std::string>::const_iterator it, it_end; + for (it = display.particles.begin(), it_end = display.particles.end(); + it != it_end; it++) + { + Particle *p = particleEngine->addEffect(*it, 0, 0); + controlParticle(p); + } + } + + mMustResetParticles = true; +} + +void ActorSprite::load() +{ + if (loaded) + unload(); + + initTargetCursor(); + + loaded = true; +} + +void ActorSprite::unload() +{ + if (!loaded) + return; + + cleanupTargetCursors(); + loaded = false; +} + +static const char *cursorType(int type) +{ + switch (type) + { + case ActorSprite::TCT_IN_RANGE: + return "in-range"; + case ActorSprite::TCT_NORMAL: + return "normal"; + default: + assert(false); + } +} + +static const char *cursorSize(int size) +{ + switch (size) + { + case ActorSprite::TC_LARGE: + return "l"; + case ActorSprite::TC_MEDIUM: + return "m"; + case ActorSprite::TC_SMALL: + return "s"; + default: + assert(false); + } +} + +void ActorSprite::initTargetCursor() +{ + static std::string targetCursor = "graphics/target-cursor-%s-%s.png"; + static int targetWidths[NUM_TC] = {44, 62, 82}; + static int targetHeights[NUM_TC] = {35, 44, 60}; + + // Load target cursors + for (int size = TC_SMALL; size < NUM_TC; size++) + { + for (int type = TCT_NORMAL; type < NUM_TCT; type++) + { + loadTargetCursor(strprintf(targetCursor.c_str(), cursorType(type), + cursorSize(size)), targetWidths[size], + targetHeights[size], type, size); + } + } +} + +void ActorSprite::cleanupTargetCursors() +{ + for (int size = TC_SMALL; size < NUM_TC; size++) + { + for (int type = TCT_NORMAL; type < NUM_TCT; type++) + { + delete targetCursor[type][size]; + if (targetCursorImages[type][size]) + targetCursorImages[type][size]->decRef(); + } + } +} + +void ActorSprite::loadTargetCursor(const std::string &filename, + int width, int height, int type, int size) +{ + assert(size > -1); + assert(size < 3); + + ResourceManager *resman = ResourceManager::getInstance(); + ImageSet *currentImageSet = resman->getImageSet(filename, width, height); + + if (!currentImageSet) + { + logger->log("Error loading target cursor: %s", filename.c_str()); + return; + } + + Animation *anim = new Animation; + + for (unsigned int i = 0; i < currentImageSet->size(); ++i) + { + anim->addFrame(currentImageSet->get(i), 75, + (16 - (currentImageSet->getWidth() / 2)), + (16 - (currentImageSet->getHeight() / 2))); + } + + SimpleAnimation *currentCursor = new SimpleAnimation(anim); + + targetCursorImages[type][size] = currentImageSet; + targetCursor[type][size] = currentCursor; +} diff --git a/src/actorsprite.h b/src/actorsprite.h new file mode 100644 index 00000000..ab945d2f --- /dev/null +++ b/src/actorsprite.h @@ -0,0 +1,233 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef ACTORSPRITE_H +#define ACTORSPRITE_H + +#include "actor.h" +#include "compoundsprite.h" +#include "map.h" +#include "particlecontainer.h" + +#include <SDL_types.h> + +#include <set> +#include <list> + +class SimpleAnimation; +class StatusEffect; + +class ActorSprite : public CompoundSprite, public Actor +{ +public: + enum Type + { + UNKNOWN, + PLAYER, + NPC, + MONSTER, + FLOOR_ITEM + }; + + enum TargetCursorSize + { + TC_SMALL = 0, + TC_MEDIUM, + TC_LARGE, + NUM_TC + }; + + enum TargetCursorType + { + TCT_NONE = -1, + TCT_NORMAL = 0, + TCT_IN_RANGE, + NUM_TCT + }; + + ActorSprite(int id); + + ~ActorSprite(); + + int getId() const + { return mId; } + + void setId(int id) { mId = id; } + + /** + * Returns the type of the ActorSprite. + */ + virtual Type getType() const { return UNKNOWN; } + + virtual bool draw(Graphics *graphics, int offsetX, int offsetY) const; + + virtual bool drawSpriteAt(Graphics *graphics, int x, int y) const; + + virtual void logic(); + + static void actorLogic(); + + void setMap(Map* map); + + /** + * Gets the way the object blocks pathfinding for other objects + */ + virtual Map::BlockType getBlockType() const + { return Map::BLOCKTYPE_NONE; } + + /** + * Take control of a particle. + */ + void controlParticle(Particle *particle); + + /** + * Returns the required size of a target cursor for this being. + */ + virtual TargetCursorSize getTargetCursorSize() const + { return TC_MEDIUM; } + + /** + * Sets the target animation for this actor. + */ + void setTargetType(TargetCursorType type); + + /** + * Untargets the actor. + */ + void untarget() { mUsedTargetCursor = NULL; } + + /** + * Triggers a visual effect, such as `level up'. Only draws the visual + * effect, does not play sound effects. + * + * \param effectId ID of the effect to trigger + */ + virtual void triggerEffect(int effectId) + { + internalTriggerEffect(effectId, false, true); + } + + /** + * Sets the actor's stun mode. If zero, the being is `normal', otherwise it + * is `stunned' in some fashion. + */ + void setStunMode(int stunMode) + { + if (mStunMode != stunMode) + updateStunMode(mStunMode, stunMode); + mStunMode = stunMode; + } + + void setStatusEffect(int index, bool active); + + /** + * A status effect block is a 16 bit mask of status effects. We assign each + * such flag a block ID of offset + bitnr. + * + * These are NOT the same as the status effect indices. + */ + void setStatusEffectBlock(int offset, Uint16 flags); + + virtual void setAlpha(float alpha) + { CompoundSprite::setAlpha(alpha); } + + virtual float getAlpha() const + { return CompoundSprite::getAlpha(); } + + virtual int getWidth() const + { return CompoundSprite::getWidth(); } + + virtual int getHeight() const + { return CompoundSprite::getHeight(); } + + static void load(); + + static void unload(); + +protected: + /** + * Trigger visual effect, with components + * + * \param effectId ID of the effect to trigger + * \param sfx Whether to trigger sound effects + * \param gfx Whether to trigger graphical effects + */ + void internalTriggerEffect(int effectId, bool sfx, bool gfx); + + /** + * Notify self that the stun mode has been updated. Invoked by + * setStunMode if something changed. + */ + virtual void updateStunMode(int oldMode, int newMode); + + /** + * Notify self that a status effect has flipped. + * The new flag is passed. + */ + virtual void updateStatusEffect(int index, bool newStatus); + + /** + * Handle an update to a status or stun effect + * + * \param The StatusEffect to effect + * \param effectId -1 for stun, otherwise the effect index + */ + virtual void handleStatusEffect(StatusEffect *effect, int effectId); + + void setupSpriteDisplay(const SpriteDisplay &display, + bool forceDisplay = true); + + int mId; + Uint16 mStunMode; /**< Stun mode; zero if not stunned */ + std::set<int> mStatusEffects; /**< set of active status effects */ + + ParticleList mStunParticleEffects; + ParticleVector mStatusParticleEffects; + ParticleList mChildParticleEffects; + +private: + /** Reset particle status effects on next redraw? */ + bool mMustResetParticles; + + /** Load the target cursors into memory */ + static void initTargetCursor(); + + /** Remove the target cursors from memory */ + static void cleanupTargetCursors(); + + /** + * Helper function for loading target cursors + */ + static void loadTargetCursor(const std::string &filename, + int width, int height, int type, int size); + + /** Images of the target cursor. */ + static ImageSet *targetCursorImages[NUM_TCT][NUM_TC]; + + /** Animated target cursors. */ + static SimpleAnimation *targetCursor[NUM_TCT][NUM_TC]; + + static bool loaded; + + /** Target cursor being used */ + SimpleAnimation *mUsedTargetCursor; +}; + +#endif // ACTORSPRITE_H diff --git a/src/actorspritemanager.cpp b/src/actorspritemanager.cpp new file mode 100644 index 00000000..a4b61ed3 --- /dev/null +++ b/src/actorspritemanager.cpp @@ -0,0 +1,343 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "actorspritemanager.h" + +#include "localplayer.h" + +#include "utils/dtor.h" + +#include <cassert> + +#define for_actors ActorSpritesConstIterator it, it_end; \ +for (it = mActors.begin(), it_end = mActors.end() ; it != it_end; it++) + +class FindBeingFunctor +{ + public: + bool operator() (ActorSprite *actor) + { + if (actor->getType() == ActorSprite::FLOOR_ITEM) + return false; + Being* b = static_cast<Being*>(actor); + + Uint16 other_y = y + ((b->getType() == ActorSprite::NPC) ? 1 : 0); + const Vector &pos = b->getPosition(); + return ((int) pos.x / 32 == x && + ((int) pos.y / 32 == y || (int) pos.y / 32 == other_y) && + b->isAlive() && + (type == ActorSprite::UNKNOWN || b->getType() == type)); + } + + Uint16 x, y; + ActorSprite::Type type; +} beingFinder; + +class PlayerNamesLister : public AutoCompleteLister +{ + void getAutoCompleteList(std::vector<std::string>& names) const + { + names.clear(); + + const ActorSprites &mActors = actorSpriteManager->getAll(); + for_actors + { + if ((*it)->getType() == ActorSprite::FLOOR_ITEM) + continue; + + Being *being = static_cast<Being*>(*it); + if (being->getType() == Being::PLAYER && being->getName() != "") + names.push_back(being->getName()); + } + } +}; + +class PlayerNPCNamesLister : public AutoCompleteLister +{ + void getAutoCompleteList(std::vector<std::string>& names) const + { + names.clear(); + + const ActorSprites &mActors = actorSpriteManager->getAll(); + for_actors + { + if ((*it)->getType() == ActorSprite::FLOOR_ITEM) + continue; + + Being *being = static_cast<Being*>(*it); + if ((being->getType() == Being::PLAYER + || being->getType() == Being::NPC) + && being->getName() != "") + names.push_back(being->getName()); + } + } +}; + +ActorSpriteManager::ActorSpriteManager() +{ + mPlayerNames = new PlayerNamesLister; + mPlayerNPCNames = new PlayerNPCNamesLister; +} + +ActorSpriteManager::~ActorSpriteManager() +{ + clear(); +} + +void ActorSpriteManager::setMap(Map *map) +{ + mMap = map; + + if (player_node) + player_node->setMap(map); +} + +void ActorSpriteManager::setPlayer(LocalPlayer *player) +{ + player_node = player; + mActors.insert(player); +} + +Being *ActorSpriteManager::createBeing(int id, ActorSprite::Type type, int subtype) +{ + Being *being = new Being(id, type, subtype, mMap); + + mActors.insert(being); + return being; +} + +FloorItem *ActorSpriteManager::createItem(int id, int itemId, int x, int y) +{ + FloorItem *floorItem = new FloorItem(id, itemId, x, y, mMap); + + mActors.insert(floorItem); + return floorItem; +} + +void ActorSpriteManager::destroy(ActorSprite *actor) +{ + if (!actor || actor == player_node) + return; + + mDeleteActors.insert(actor); +} + +Being *ActorSpriteManager::findBeing(int id) const +{ + for_actors + { + ActorSprite *actor = *it; + if (actor->getId() == id && + actor->getType() != ActorSprite::FLOOR_ITEM) + return static_cast<Being*>(actor); + } + + return NULL; +} + +Being *ActorSpriteManager::findBeing(int x, int y, ActorSprite::Type type) const +{ + beingFinder.x = x; + beingFinder.y = y; + beingFinder.type = type; + + ActorSpritesConstIterator it = find_if(mActors.begin(), mActors.end(), + beingFinder); + + return (it == mActors.end()) ? NULL : static_cast<Being*>(*it); +} + +Being *ActorSpriteManager::findBeingByPixel(int x, int y) const +{ + for_actors + { + if ((*it)->getType() == ActorSprite::FLOOR_ITEM) + continue; + + Being *being = static_cast<Being*>(*it); + + int xtol = being->getWidth() / 2; + int uptol = being->getHeight(); + + if ((being->isAlive()) && + (being != player_node) && + (being->getPixelX() - xtol <= x) && + (being->getPixelX() + xtol >= x) && + (being->getPixelY() - uptol <= y) && + (being->getPixelY() >= y)) + return being; + } + + return NULL; +} + +FloorItem *ActorSpriteManager::findItem(int id) const +{ + for_actors + { + if ((*it)->getId() == id && + (*it)->getType() == ActorSprite::FLOOR_ITEM) + { + return static_cast<FloorItem*>(*it); + } + } + + return NULL; +} + +FloorItem *ActorSpriteManager::findItem(int x, int y) const +{ + for_actors + { + if ((*it)->getTileX() == x && (*it)->getTileY() == y && + (*it)->getType() == ActorSprite::FLOOR_ITEM) + { + return static_cast<FloorItem*>(*it); + } + } + + return NULL; +} + +Being *ActorSpriteManager::findBeingByName(const std::string &name, + ActorSprite::Type type) const +{ + for_actors + { + if ((*it)->getType() == ActorSprite::FLOOR_ITEM) + continue; + + Being *being = static_cast<Being*>(*it); + if (being->getName() == name && + (type == ActorSprite::UNKNOWN || type == being->getType())) + return being; + } + return NULL; +} + +const ActorSprites &ActorSpriteManager::getAll() const +{ + return mActors; +} + +void ActorSpriteManager::logic() +{ + for_actors + (*it)->logic(); + + for (it = mDeleteActors.begin(), it_end = mDeleteActors.end(); + it != it_end; ++it) + { + mActors.erase(*it); + delete *it; + } + + mDeleteActors.clear(); +} + +void ActorSpriteManager::clear() +{ + if (player_node) + mActors.erase(player_node); + + for_actors + delete *it; + mActors.clear(); + mDeleteActors.clear(); + + if (player_node) + mActors.insert(player_node); +} + +Being *ActorSpriteManager::findNearestLivingBeing(int x, int y, + int maxTileDist, + ActorSprite::Type type, + Being *excluded) const +{ + Being *closestBeing = 0; + int dist = 0; + + const int maxDist = maxTileDist * 32; + + for_actors + { + if ((*it)->getType() == ActorSprite::FLOOR_ITEM) + continue; + + Being *being = static_cast<Being*>(*it); + const Vector &pos = being->getPosition(); + int d = abs(((int) pos.x) - x) + abs(((int) pos.y) - y); + + if ((being->getType() == type || type == ActorSprite::UNKNOWN) + && (d < dist || !closestBeing) // it is closer + && being->isAlive() // no dead beings + && being != excluded) + { + dist = d; + closestBeing = being; + } + } + + return (maxDist >= dist) ? closestBeing : 0; +} + +Being *ActorSpriteManager::findNearestLivingBeing(Being *aroundBeing, + int maxDist, + ActorSprite::Type type) const +{ + const Vector &pos = aroundBeing->getPosition(); + return findNearestLivingBeing((int)pos.x, (int)pos.y, maxDist, type, + aroundBeing); +} + +bool ActorSpriteManager::hasActorSprite(ActorSprite *actor) const +{ + for_actors + { + if (actor == *it) + return true; + } + + return false; +} + +AutoCompleteLister *ActorSpriteManager::getPlayerNameLister() +{ + return mPlayerNames; +} + +AutoCompleteLister *ActorSpriteManager::getPlayerNPCNameLister() +{ + return mPlayerNPCNames; +} + +void ActorSpriteManager::updatePlayerNames() +{ + for_actors + { + if ((*it)->getType() == ActorSprite::FLOOR_ITEM) + continue; + + Being *being = static_cast<Being*>(*it); + if (being->getType() == ActorSprite::PLAYER && being->getName() != "") + being->updateName(); + } +} diff --git a/src/beingmanager.h b/src/actorspritemanager.h index f2f8eb6d..d6aa609b 100644 --- a/src/beingmanager.h +++ b/src/actorspritemanager.h @@ -19,27 +19,31 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef BEINGMANAGER_H -#define BEINGMANAGER_H +#ifndef ACTORSPRITEMANAGER_H +#define ACTORSPRITEMANAGER_H +#include "actorsprite.h" #include "being.h" +#include "flooritem.h" #include "gui/widgets/textfield.h" class LocalPlayer; class Map; -typedef std::list<Being*> Beings; +typedef std::set<ActorSprite*> ActorSprites; +typedef ActorSprites::iterator ActorSpritesIterator; +typedef ActorSprites::const_iterator ActorSpritesConstIterator; -class BeingManager +class ActorSpriteManager { public: - BeingManager(); + ActorSpriteManager(); - ~BeingManager(); + ~ActorSpriteManager(); /** - * Sets the map on which beings are created. + * Sets the map on which ActorSprites are created. */ void setMap(Map *map); @@ -49,27 +53,48 @@ class BeingManager void setPlayer(LocalPlayer *player); /** - * Create a being and add it to the list of beings. + * Create a Being and add it to the list of ActorSprites. */ - Being *createBeing(int id, Being::Type type, int subtype); + Being *createBeing(int id, ActorSprite::Type type, int subtype); /** - * Remove a Being. + * Create a FloorItem and add it to the list of ActorSprites. */ - void destroyBeing(Being *being); + FloorItem *createItem(int id, int itemId, int x, int y); /** - * Returns a specific id Being. + * Destroys the given ActorSprite at the end of + * ActorSpriteManager::logic. + */ + void destroy(ActorSprite *actor); + + /** + * Returns a specific Being, by id; */ Being *findBeing(int id) const; /** * Returns a being at specific coordinates. */ - Being *findBeing(int x, int y, Being::Type type = Being::UNKNOWN) const; + Being *findBeing(int x, int y, + ActorSprite::Type type = ActorSprite::UNKNOWN) const; + + /** + * Returns a being at the specific pixel. + */ Being *findBeingByPixel(int x, int y) const; /** + * Returns a specific FloorItem, by id. + */ + FloorItem *findItem(int id) const; + + /** + * Returns a FloorItem at specific coordinates. + */ + FloorItem *findItem(int x, int y) const; + + /** * Returns a being nearest to specific coordinates. * * @param x X coordinate in pixels. @@ -80,7 +105,7 @@ class BeingManager * @param excluded The being to exclude from the search. */ Being *findNearestLivingBeing(int x, int y, int maxTileDist, - Being::Type type = Being::UNKNOWN, + ActorSprite::Type type = Being::UNKNOWN, Being *excluded = 0) const; /** @@ -92,35 +117,35 @@ class BeingManager * @param type The type of being to look for. */ Being *findNearestLivingBeing(Being *aroundBeing, int maxTileDist, - Being::Type type = Being::UNKNOWN) const; + ActorSprite::Type type = Being::UNKNOWN) const; /** * Finds a being by name and (optionally) by type. */ Being *findBeingByName(const std::string &name, - Being::Type type = Being::UNKNOWN) const; + ActorSprite::Type type = Being::UNKNOWN) const; /** * Returns the whole list of beings. */ - const Beings &getAll() const; + const ActorSprites &getAll() const; /** - * Returns true if the given being is in the manager's list, false - * otherwise. + * Returns true if the given ActorSprite is in the manager's list, + * false otherwise. * - * \param being the being to search for + * \param actor the ActorSprite to search for */ - bool hasBeing(Being *being) const; + bool hasActorSprite(ActorSprite *actor) const; /** - * Performs being logic and deletes dead beings when they have been - * dead long enough. + * Performs ActorSprite logic and deletes ActorSprite scheduled to be + * deleted. */ void logic(); /** - * Destroys all beings except the local player + * Destroys all ActorSprites except the local player */ void clear(); @@ -136,10 +161,11 @@ class BeingManager AutoCompleteLister *mPlayerNames; AutoCompleteLister *mPlayerNPCNames; - Beings mBeings; + ActorSprites mActors; + ActorSprites mDeleteActors; Map *mMap; }; -extern BeingManager *beingManager; +extern ActorSpriteManager *actorSpriteManager; -#endif +#endif // ACTORSPRITEMANAGER_H diff --git a/src/animatedsprite.cpp b/src/animatedsprite.cpp index 59bf2f88..9f4e46bd 100644 --- a/src/animatedsprite.cpp +++ b/src/animatedsprite.cpp @@ -22,15 +22,12 @@ #include "animatedsprite.h" #include "graphics.h" -#include "log.h" #include "resources/action.h" #include "resources/animation.h" #include "resources/image.h" #include "resources/resourcemanager.h" -#include "utils/xml.h" - #include <cassert> AnimatedSprite::AnimatedSprite(SpriteDef *sprite): @@ -41,16 +38,17 @@ AnimatedSprite::AnimatedSprite(SpriteDef *sprite): mSprite(sprite), mAction(0), mAnimation(0), - mFrame(0), - mAlpha(1.0f) + mFrame(0) { assert(mSprite); + mAlpha = 1.0f; + // Take possession of the sprite mSprite->incRef(); // Play the stand animation by default - play(ACTION_STAND); + play(SpriteAction::STAND); } AnimatedSprite *AnimatedSprite::load(const std::string &filename, int variant) @@ -69,18 +67,22 @@ AnimatedSprite::~AnimatedSprite() mSprite->decRef(); } -void AnimatedSprite::reset() +bool AnimatedSprite::reset() { + bool ret = mFrameIndex !=0 || mFrameTime != 0 || mLastTime != 0; + mFrameIndex = 0; mFrameTime = 0; mLastTime = 0; + + return ret; } -void AnimatedSprite::play(SpriteAction spriteAction) +bool AnimatedSprite::play(std::string spriteAction) { Action *action = mSprite->getAction(spriteAction); if (!action) - return; + return false; mAction = action; Animation *animation = mAction->getAnimation(mDirection); @@ -91,10 +93,14 @@ void AnimatedSprite::play(SpriteAction spriteAction) mFrame = mAnimation->getFrame(0); reset(); + + return true; } + + return false; } -void AnimatedSprite::update(int time) +bool AnimatedSprite::update(int time) { // Avoid freaking out at first frame or when tick_time overflows if (time < mLastTime || mLastTime == 0) @@ -102,16 +108,22 @@ void AnimatedSprite::update(int time) // If not enough time has passed yet, do nothing if (time <= mLastTime || !mAnimation) - return; + return false; unsigned int dt = time - mLastTime; mLastTime = time; + Animation *animation = mAnimation; + Frame *frame = mFrame; + if (!updateCurrentAnimation(dt)) { // Animation finished, reset to default - play(ACTION_STAND); + play(SpriteAction::STAND); } + + // Make sure something actually changed + return animation != mAnimation || frame != mFrame; } bool AnimatedSprite::updateCurrentAnimation(unsigned int time) @@ -158,14 +170,14 @@ bool AnimatedSprite::draw(Graphics *graphics, int posX, int posY) const posY + mFrame->offsetY); } -void AnimatedSprite::setDirection(SpriteDirection direction) +bool AnimatedSprite::setDirection(SpriteDirection direction) { if (mDirection != direction) { mDirection = direction; if (!mAction) - return; + return false; Animation *animation = mAction->getAnimation(mDirection); @@ -175,7 +187,23 @@ void AnimatedSprite::setDirection(SpriteDirection direction) mFrame = mAnimation->getFrame(0); reset(); } + + return true; } + + return false; +} + +size_t AnimatedSprite::getCurrentFrame() const +{ + return mFrameIndex; +} + +size_t AnimatedSprite::getFrameCount() const +{ + if (mAnimation) + return mAnimation->getLength(); + return 0; } int AnimatedSprite::getWidth() const @@ -193,3 +221,8 @@ int AnimatedSprite::getHeight() const else return 0; } + +const Image* AnimatedSprite::getImage() const +{ + return mFrame ? mFrame->image : 0; +} diff --git a/src/animatedsprite.h b/src/animatedsprite.h index 54b63cc0..bd39c267 100644 --- a/src/animatedsprite.h +++ b/src/animatedsprite.h @@ -22,19 +22,18 @@ #ifndef ANIMATEDSPRITE_H #define ANIMATEDSPRITE_H -#include "resources/spritedef.h" +#include "sprite.h" #include <map> #include <string> class Animation; -class Graphics; struct Frame; /** * Animates a sprite by adding playback state. */ -class AnimatedSprite +class AnimatedSprite : public Sprite { public: /** @@ -53,59 +52,30 @@ class AnimatedSprite static AnimatedSprite *load(const std::string &filename, int variant = 0); - /** - * Destructor. - */ virtual ~AnimatedSprite(); - /** - * Resets the animated sprite. - */ - void reset(); + bool reset(); - /** - * Plays an action using the current direction - */ - void play(SpriteAction action); + bool play(std::string action); - /** - * Inform the animation of the passed time so that it can output the - * correct animation frame. - */ - void update(int time); + bool update(int time); - /** - * Draw the current animation frame at the coordinates given in screen - * pixels. - */ bool draw(Graphics* graphics, int posX, int posY) const; - /** - * gets the width in pixels of the image of the current frame - */ int getWidth() const; - /** - * gets the height in pixels of the image of the current frame - */ int getHeight() const; - /** - * Sets the direction. - */ - void setDirection(SpriteDirection direction); + const Image* getImage() const; - /** - * Sets the alpha value of the animated sprite - */ - void setAlpha(float alpha) - { mAlpha = alpha; } + bool setDirection(SpriteDirection direction); - /** - * Returns the current alpha opacity of the animated sprite. - */ - virtual float getAlpha() const - { return mAlpha; } + int getNumberOfLayers() + { return 1; } + + size_t getCurrentFrame() const; + + size_t getFrameCount() const; private: bool updateCurrentAnimation(unsigned int dt); @@ -120,7 +90,6 @@ class AnimatedSprite Action *mAction; /**< The currently active action. */ Animation *mAnimation; /**< The currently active animation. */ Frame *mFrame; /**< The currently active frame. */ - float mAlpha; /**< The alpha opacity used to draw */ }; #endif diff --git a/src/avatar.cpp b/src/avatar.cpp index f11bf535..b47333b6 100644 --- a/src/avatar.cpp +++ b/src/avatar.cpp @@ -20,10 +20,6 @@ #include "avatar.h" -#include "localplayer.h" - -#include <sstream> - Avatar::Avatar(const std::string &name): mName(name), mHp(0), mMaxHp(0), diff --git a/src/being.cpp b/src/being.cpp index be8afa79..4d682ab8 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -21,104 +21,156 @@ #include "being.h" +#include "actorspritemanager.h" #include "animatedsprite.h" #include "client.h" #include "configuration.h" #include "effectmanager.h" +#include "event.h" #include "graphics.h" +#include "guild.h" #include "localplayer.h" #include "log.h" #include "map.h" #include "particle.h" +#include "party.h" +#include "playerrelations.h" #include "simpleanimation.h" #include "sound.h" #include "text.h" -#include "statuseffect.h" #include "gui/gui.h" +#include "gui/socialwindow.h" #include "gui/speechbubble.h" -#include "gui/theme.h" -#include "gui/userpalette.h" +#include "net/charhandler.h" +#include "net/gamehandler.h" +#include "net/net.h" +#include "net/playerhandler.h" +#include "net/npchandler.h" + +#include "resources/beinginfo.h" #include "resources/colordb.h" #include "resources/emotedb.h" #include "resources/image.h" #include "resources/itemdb.h" #include "resources/iteminfo.h" -#include "resources/resourcemanager.h" +#include "resources/monsterdb.h" +#include "resources/npcdb.h" +#include "resources/theme.h" +#include "resources/userpalette.h" - -#include "utils/dtor.h" #include "utils/stringutils.h" -#include "utils/xml.h" -#include "net/net.h" -#include "net/playerhandler.h" #include <cassert> #include <cmath> -#define BEING_EFFECTS_FILE "effects.xml" #define HAIR_FILE "hair.xml" static const int DEFAULT_BEING_WIDTH = 32; static const int DEFAULT_BEING_HEIGHT = 32; - - int Being::mNumberOfHairstyles = 1; // TODO: mWalkTime used by eAthena only -Being::Being(int id, int subtype, Map *map): - mFrame(0), - mWalkTime(0), - mEmotion(0), mEmotionTime(0), +Being::Being(int id, Type type, int subtype, Map *map): + ActorSprite(id), + mInfo(BeingInfo::Unknown), + mActionTime(0), mSpeechTime(0), + mAttackType(1), mAttackSpeed(350), mAction(STAND), - mSubType(subtype), - mId(id), + mSubType(0xFFFF), mDirection(DOWN), mSpriteDirection(DIRECTION_DOWN), - mMap(NULL), mDispName(0), mShowName(false), mEquippedWeapon(NULL), mText(0), - mStunMode(0), - mAlpha(1.0f), - mStatusParticleEffects(&mStunParticleEffects, false), - mChildParticleEffects(&mStatusParticleEffects, false), - mMustResetParticles(false), + mGender(GENDER_UNSPECIFIED), + mParty(NULL), + mIsGM(false), + mType(type), mX(0), mY(0), mDamageTaken(0), - mUsedTargetCursor(NULL) + mIp(0) { setMap(map); + setSubtype(subtype); mSpeechBubble = new SpeechBubble; - mNameColor = &userPalette->getColor(UserPalette::NPC); - mTextColor = &Theme::getThemeColor(Theme::CHAT); mWalkSpeed = Net::getPlayerHandler()->getDefaultWalkSpeed(); -} -Being::~Being() -{ - mUsedTargetCursor = NULL; - delete_all(mSprites); + if (getType() == PLAYER) + mShowName = config.getBoolValue("visiblenames"); - if (player_node && player_node->getTarget() == this) - player_node->setTarget(NULL); + if (getType() == PLAYER || getType() == NPC) + setShowName(true); - setMap(NULL); + updateColors(); + listen(CHANNEL_CONFIG); + listen(CHANNEL_CHAT); +} +Being::~Being() +{ delete mSpeechBubble; delete mDispName; delete mText; + mSpeechBubble = 0; + mDispName = 0; + mText = 0; +} + +void Being::setSubtype(Uint16 subtype) +{ + if (subtype == mSubType) + return; + + mSubType = subtype; + + if (getType() == MONSTER) + { + mInfo = MonsterDB::get(mSubType); + setName(mInfo->getName()); + setupSpriteDisplay(mInfo->getDisplay()); + } + else if (getType() == NPC) + { + mInfo = NPCDB::get(mSubType); + setupSpriteDisplay(mInfo->getDisplay(), false); + } + else if (getType() == PLAYER) + { + int id = -100 - subtype; + + // Prevent showing errors when sprite doesn't exist + if (!itemDb->exists(id)) + id = -100; + + setSprite(Net::getCharHandler()->baseSprite(), id); + } +} + +ActorSprite::TargetCursorSize Being::getTargetCursorSize() const +{ + return mInfo->getTargetCursorSize(); +} + +unsigned char Being::getWalkMask() const +{ + return mInfo->getWalkMask(); +} + +Map::BlockType Being::getBlockType() const +{ + return mInfo->getBlockType(); } void Being::setPosition(const Vector &pos) { - mPos = pos; + Actor::setPosition(pos); updateCoords(); @@ -152,8 +204,9 @@ void Being::setDestination(int dstX, int dstY) Position dest = mMap->checkNodeOffsets(getCollisionRadius(), getWalkMask(), dstX, dstY); - Path thisPath = mMap->findPixelPath(mPos.x, mPos.y, dest.x, dest.y, - getCollisionRadius(), getWalkMask()); + Path thisPath = mMap->findPixelPath((int) mPos.x, (int) mPos.y, + dest.x, dest.y, + getCollisionRadius(), getWalkMask()); if (thisPath.empty()) { @@ -186,10 +239,10 @@ void Being::setPath(const Path &path) mPath = path; if ((Net::getNetworkType() == ServerInfo::TMWATHENA) && - mAction != WALK && mAction != DEAD) + mAction != MOVE && mAction != DEAD) { nextTile(); - mWalkTime = tick_time; + mActionTime = tick_time; } } @@ -235,7 +288,7 @@ void Being::setSpeech(const std::string &text, int time) if (!mSpeech.empty()) mSpeechTime = time <= SPEECH_MAX_TIME ? time : SPEECH_MAX_TIME; - const int speech = (int) config.getValue("speech", TEXT_OVERHEAD); + const int speech = config.getIntValue("speech"); if (speech == TEXT_OVERHEAD) { if (mText) @@ -309,6 +362,19 @@ void Being::takeDamage(Being *attacker, int amount, AttackType type) if (amount > 0) { + if (mInfo) + { + if (attacker) + { + sound.playSfx(mInfo->getSound(SOUND_EVENT_HURT), + attacker->getTileX(), attacker->getTileY()); + } + else + { + sound.playSfx(mInfo->getSound(SOUND_EVENT_HURT)); + } + } + if (getType() == MONSTER) { mDamageTaken += amount; @@ -327,76 +393,159 @@ void Being::handleAttack(Being *victim, int damage, AttackType type) if (this != player_node) setAction(Being::ATTACK, 1); - if (getType() == PLAYER && victim) - { - if (mEquippedWeapon) - { - fireMissile(victim, mEquippedWeapon->getMissileParticle()); - } - } + if (getType() == PLAYER && victim && mEquippedWeapon) + fireMissile(victim, mEquippedWeapon->getMissileParticle()); + else + fireMissile(victim, mInfo->getAttack(mAttackType)->missileParticle); + if (Net::getNetworkType() == ServerInfo::TMWATHENA) { - mFrame = 0; - mWalkTime = tick_time; + reset(); + mActionTime = tick_time; } + + sound.playSfx(mInfo->getSound((damage > 0) ? + SOUND_EVENT_HIT : SOUND_EVENT_MISS), mX, mY); } void Being::setName(const std::string &name) { - mName = name; - - if (getShowName()) + if (getType() == NPC) + { + mName = name.substr(0, name.find('#', 0)); showName(); + } + else + { + mName = name; + + if (getType() == PLAYER && getShowName()) + showName(); + } } void Being::setShowName(bool doShowName) { - bool oldShow = mShowName; + if (mShowName == doShowName) + return; + mShowName = doShowName; - if (doShowName != oldShow) + if (doShowName) + showName(); + else { - if (doShowName) - showName(); - else - { - delete mDispName; - mDispName = 0; - } + delete mDispName; + mDispName = 0; } } void Being::setGuildName(const std::string &name) { - logger->log("Got guild name \"%s\" for being %s(%i)", name.c_str(), mName.c_str(), mId); + logger->log("Got guild name \"%s\" for being %s(%i)", name.c_str(), + mName.c_str(), mId); } void Being::setGuildPos(const std::string &pos) { - logger->log("Got guild position \"%s\" for being %s(%i)", pos.c_str(), mName.c_str(), mId); + logger->log("Got guild position \"%s\" for being %s(%i)", pos.c_str(), + mName.c_str(), mId); } -void Being::setMap(Map *map) +void Being::addGuild(Guild *guild) { - // Remove sprite from potential previous map - if (mMap) - mMap->removeSprite(mMapSprite); + mGuilds[guild->getId()] = guild; + guild->addMember(mId, mName); - mMap = map; + if (this == player_node && socialWindow) + { + socialWindow->addTab(guild); + } +} - // Add sprite to potential new map - if (mMap) - mMapSprite = mMap->addSprite(this); +void Being::removeGuild(int id) +{ + if (this == player_node && socialWindow) + { + socialWindow->removeTab(mGuilds[id]); + } - // Clear particle effect list because child particles became invalid - mChildParticleEffects.clear(); - mMustResetParticles = true; // Reset status particles on next redraw + mGuilds[id]->removeMember(mId); + mGuilds.erase(id); } -void Being::controlParticle(Particle *particle) +Guild *Being::getGuild(const std::string &guildName) const { - mChildParticleEffects.addLocally(particle); + std::map<int, Guild*>::const_iterator itr, itr_end = mGuilds.end(); + for (itr = mGuilds.begin(); itr != itr_end; ++itr) + { + Guild *guild = itr->second; + if (guild->getName() == guildName) + { + return guild; + } + } + + return NULL; +} + +Guild *Being::getGuild(int id) const +{ + std::map<int, Guild*>::const_iterator itr; + itr = mGuilds.find(id); + if (itr != mGuilds.end()) + { + return itr->second; + } + + return NULL; +} + +void Being::clearGuilds() +{ + std::map<int, Guild*>::const_iterator itr, itr_end = mGuilds.end(); + for (itr = mGuilds.begin(); itr != itr_end; ++itr) + { + Guild *guild = itr->second; + + if (this == player_node && socialWindow) + socialWindow->removeTab(guild); + + guild->removeMember(mId); + } + + mGuilds.clear(); +} + +void Being::setParty(Party *party) +{ + if (party == mParty) + return; + + Party *old = mParty; + mParty = party; + + if (old) + { + old->removeMember(mId); + } + + if (party) + { + party->addMember(mId, mName); + } + + updateColors(); + + if (this == player_node && socialWindow) + { + if (old) + socialWindow->removeTab(old); + + if (party) + socialWindow->addTab(party); + } } void Being::fireMissile(Being *victim, const std::string &particle) @@ -404,63 +553,98 @@ void Being::fireMissile(Being *victim, const std::string &particle) if (!victim || particle.empty()) return; - Particle *target = particleEngine->createChild(); - Particle *missile = target->addEffect(particle, getPixelX(), getPixelY()); + Particle *missile = particleEngine->addEffect(particle, + getPixelX(), getPixelY()); if (missile) { - target->setLifetime(2000); + Particle *target = particleEngine->createChild(); target->moveBy(Vector(0.0f, 0.0f, 32.0f)); + target->setLifetime(1000); victim->controlParticle(target); missile->setDestination(target, 7, 0); missile->setDieDistance(8); missile->setLifetime(900); } + } void Being::setAction(Action action, int attackType) { - SpriteAction currentAction = ACTION_INVALID; + std::string currentAction = SpriteAction::INVALID; switch (action) { - case WALK: - currentAction = ACTION_WALK; + case MOVE: + currentAction = SpriteAction::MOVE; + // Note: When adding a run action, + // Differentiate walk and run with action name, + // while using only the ACTION_MOVE. break; case SIT: - currentAction = ACTION_SIT; + currentAction = SpriteAction::SIT; break; case ATTACK: if (mEquippedWeapon) - currentAction = mEquippedWeapon->getAttackType(); + { + currentAction = mEquippedWeapon->getAttackAction(); + reset(); + } else - currentAction = ACTION_ATTACK; + { + mAttackType = attackType; + currentAction = mInfo->getAttack(attackType)->action; + reset(); + + if (Net::getNetworkType() == ServerInfo::MANASERV) + { + int rotation = 0; + //attack particle effect + std::string particleEffect = mInfo->getAttack(attackType) + ->particleEffect; + if (!particleEffect.empty() && Particle::enabled) + { + switch (mSpriteDirection) + { + case DIRECTION_DOWN: rotation = 0; break; + case DIRECTION_LEFT: rotation = 90; break; + case DIRECTION_UP: rotation = 180; break; + case DIRECTION_RIGHT: rotation = 270; break; + default: break; + } + Particle *p; + p = particleEngine->addEffect(particleEffect, 0, 0, + rotation); + controlParticle(p); + } + } + } - for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) - if (*it) - (*it)->reset(); break; case HURT: - //currentAction = ACTION_HURT; // Buggy: makes the player stop + //currentAction = SpriteAction::HURT;// Buggy: makes the player stop // attacking and unable to attack - // again until he moves + // again until he moves. + // TODO: fix this! break; case DEAD: - currentAction = ACTION_DEAD; + currentAction = SpriteAction::DEAD; + sound.playSfx(mInfo->getSound(SOUND_EVENT_DIE), mX, mY); break; case STAND: - currentAction = ACTION_STAND; + currentAction = SpriteAction::STAND; break; } - if (currentAction != ACTION_INVALID) + if (currentAction != SpriteAction::INVALID) { - for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) - if (*it) - (*it)->play(currentAction); + play(currentAction); mAction = action; } + + if (currentAction != SpriteAction::MOVE) + mActionTime = tick_time; } void Being::setDirection(Uint8 direction) @@ -484,12 +668,10 @@ void Being::setDirection(Uint8 direction) dir = DIRECTION_LEFT; mSpriteDirection = dir; - for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) - if (*it) - (*it)->setDirection(dir); + CompoundSprite::setDirection(dir); } -/** TODO: Used by eAthena only */ +/** Note: Used by Tmw-Athena only */ void Being::nextTile() { if (mPath.empty()) @@ -521,8 +703,8 @@ void Being::nextTile() mX = pos.x; mY = pos.y; - setAction(WALK); - mWalkTime += (int)(mWalkSpeed.x / 10); + setAction(MOVE); + mActionTime += (int)(mWalkSpeed.x / 10); } int Being::getCollisionRadius() const @@ -544,24 +726,32 @@ void Being::logic() mText = 0; } - if ((Net::getNetworkType() == ServerInfo::MANASERV) && (mAction != DEAD)) + if ((Net::getNetworkType() == ServerInfo::MANASERV) && (mAction != DEAD) + && !mWalkSpeed.isNull()) { const Vector dest = (mPath.empty()) ? mDest : Vector(mPath.front().x, mPath.front().y); - // This is a hack that stops NPCs from running off the map... + // Avoid going to flawed destinations if (mDest.x <= 0 && mDest.y <= 0) + { + // We make the being stop move in that case. + mDest = mPos; + mPath.clear(); + // By returning now, we're losing one tick for the rest of the logic + // but as we have reset the destination, the next tick will be fine. return; + } // The Vector representing the difference between current position // and the next destination path node. Vector dir = dest - mPos; - const float nominalLength = dir.length(); + float distance = dir.length(); // When we've not reached our destination, move to it. - if (nominalLength > 0.0f && !mWalkSpeed.isNull()) + if (distance > 0.0f) { // The deplacement of a point along a vector is calculated // using the Unit Vector (â) multiplied by the point speed. @@ -572,7 +762,7 @@ void Being::logic() normalizedDir.y * mWalkSpeed.y); // Test if we don't miss the destination by a move too far: - if (diff.length() > nominalLength) + if (diff.length() > distance) { setPosition(mPos + dir); @@ -581,32 +771,43 @@ void Being::logic() if (!mPath.empty()) mPath.pop_front(); } - // Otherwise, go to it using the nominal speed. else + { + // Otherwise, go to it using the nominal speed. setPosition(mPos + diff); + // And reset the nominalLength to the actual move length + distance = diff.length(); + } - if (mAction != WALK) - setAction(WALK); + if (mAction != MOVE) + setAction(MOVE); // Update the player sprite direction. - // N.B.: We only change this if the distance is more than one pixel. - if (nominalLength > 1.0f) + // N.B.: We only change this if the distance is more than one pixel + // to avoid flawing the ending direction. + if (distance > 1.0f) { - int direction = 0; - const float dx = std::abs(dir.x); - float dy = std::abs(dir.y); - - // When not using mouse for the player, we slightly prefer - // UP and DOWN position, especially when walking diagonally. - if (this == player_node && !player_node->isPathSetByMouse()) - dy = dy + 2; - - if (dx > dy) - direction |= (dir.x > 0) ? RIGHT : LEFT; - else - direction |= (dir.y > 0) ? DOWN : UP; - - setDirection(direction); + // The player direction is handled for keyboard + // by LocalPlayer::startWalking(), we shouldn't get + // in the way here for other cases. + // Hence, we set the direction in Being::logic() only when: + // 1. It is not the localPlayer + // 2. When it is the localPlayer but only by mouse + // (because in that case, the path can have more than one tile.) + if ((player_node == this && player_node->isPathSetByMouse()) + || player_node != this) + { + int direction = 0; + const float dx = std::abs(dir.x); + float dy = std::abs(dir.y); + + if (dx > dy) + direction |= (dir.x > 0) ? RIGHT : LEFT; + else + direction |= (dir.y > 0) ? DOWN : UP; + + setDirection(direction); + } } } else if (!mPath.empty()) @@ -615,108 +816,101 @@ void Being::logic() // remove it and go to the next one. mPath.pop_front(); } - else if (mAction == WALK) + else if (mAction == MOVE) { setAction(STAND); } } else if (Net::getNetworkType() == ServerInfo::TMWATHENA) { - // Update pixel coordinates - setPosition(mX * 32 + 16 + getXOffset(), - mY * 32 + 32 + getYOffset()); - } - - if (mEmotion != 0) - { - mEmotionTime--; - if (mEmotionTime == 0) - mEmotion = 0; - } - - // Update sprite animations - if (mUsedTargetCursor) - mUsedTargetCursor->update(tick_time * MILLISECONDS_IN_A_TICK); - - for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) - if (*it) - (*it)->update(tick_time * MILLISECONDS_IN_A_TICK); + int frameCount = getFrameCount(); - // Restart status/particle effects, if needed - if (mMustResetParticles) - { - mMustResetParticles = false; - for (std::set<int>::iterator it = mStatusEffects.begin(); - it != mStatusEffects.end(); it++) + switch (mAction) { - const StatusEffect *effect = StatusEffect::getStatusEffect(*it, true); - if (effect && effect->particleEffectIsPersistent()) - updateStatusEffect(*it, true); + case STAND: + case SIT: + case DEAD: + case HURT: + break; + + case MOVE: + if ((int) ((get_elapsed_time(mActionTime) * frameCount) + / getWalkSpeed().x) >= frameCount) + nextTile(); + break; + + case ATTACK: + int rotation = 0; + std::string particleEffect = ""; + + int curFrame = (get_elapsed_time(mActionTime) * frameCount) + / mAttackSpeed; + + //attack particle effect + if (mEquippedWeapon) + { + particleEffect = mEquippedWeapon->getParticleEffect(); + + if (!particleEffect.empty() && + findSameSubstring(particleEffect, + paths.getStringValue("particles")).empty()) + particleEffect = paths.getStringValue("particles") + + particleEffect; + } + else + { + particleEffect = mInfo->getAttack(mAttackType) + ->particleEffect; + } + + if (!particleEffect.empty() && Particle::enabled + && curFrame == 1) + { + switch (mDirection) + { + case DOWN: rotation = 0; break; + case LEFT: rotation = 90; break; + case UP: rotation = 180; break; + case RIGHT: rotation = 270; break; + default: break; + } + Particle *p; + p = particleEngine->addEffect(particleEffect, 0, 0, + rotation); + controlParticle(p); + } + + if (curFrame >= frameCount) + nextTile(); + + break; } - } - - // Update particle effects - mChildParticleEffects.moveTo(mPos.x, mPos.y); -} -void Being::draw(Graphics *graphics, int offsetX, int offsetY) const -{ - // TODO: Eventually, we probably should fix all sprite offsets so that - // these translations aren't necessary anymore. The sprites know - // best where their base point should be. - const int px = getPixelX() + offsetX - 16; - // Temporary fix to the Y offset. - const int py = getPixelY() + offsetY - - ((Net::getNetworkType() == ServerInfo::MANASERV) ? 15 : 32); - - if (mUsedTargetCursor) - mUsedTargetCursor->draw(graphics, px, py); - - for (SpriteConstIterator it = mSprites.begin(); it != mSprites.end(); it++) - { - if (*it) - { - if ((*it)->getAlpha() != mAlpha) - (*it)->setAlpha(mAlpha); - (*it)->draw(graphics, px, py); - } + // Update pixel coordinates + setPosition(mX * 32 + 16 + getXOffset(), + mY * 32 + 32 + getYOffset()); } -} -void Being::drawSpriteAt(Graphics *graphics, int x, int y) const -{ - const int px = x - 16; - const int py = y - 32; + ActorSprite::logic(); + + int frameCount = getFrameCount(); + if (frameCount < 10) + frameCount = 10; - for (SpriteConstIterator it = mSprites.begin(); it != mSprites.end(); it++) + if (!isAlive() && Net::getGameHandler()->removeDeadBeings() && + (int) ((get_elapsed_time(mActionTime) + / getWalkSpeed().x) >= frameCount)) { - if (*it) - { - if ((*it)->getAlpha() != mAlpha) - (*it)->setAlpha(mAlpha); - (*it)->draw(graphics, px, py); - } + if (getType() != PLAYER) + actorSpriteManager->destroy(this); } } -void Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY) -{ - if (!mEmotion) - return; - - const int px = getPixelX() - offsetX - 16; - const int py = getPixelY() - offsetY - 64 - 32; - const int emotionIndex = mEmotion - 1; - - if (emotionIndex >= 0 && emotionIndex <= EmoteDB::getLast()) - EmoteDB::getAnimation(emotionIndex)->draw(graphics, px, py); -} - void Being::drawSpeech(int offsetX, int offsetY) { const int px = getPixelX() - offsetX; const int py = getPixelY() - offsetY; - const int speech = (int) config.getValue("speech", TEXT_OVERHEAD); + const int speech = config.getIntValue("speech"); // Draw speech above this being if (mSpeechTime == 0) @@ -766,72 +960,11 @@ void Being::drawSpeech(int offsetX, int offsetY) } } -void Being::setStatusEffectBlock(int offset, Uint16 newEffects) -{ - for (int i = 0; i < STATUS_EFFECTS; i++) - { - int index = StatusEffect::blockEffectIndexToEffectIndex(offset + i); - - if (index != -1) - setStatusEffect(index, (newEffects & (1 << i)) > 0); - } -} - -void Being::handleStatusEffect(StatusEffect *effect, int effectId) -{ - if (!effect) - return; - - // TODO: Find out how this is meant to be used - // (SpriteAction != Being::Action) - //SpriteAction action = effect->getAction(); - //if (action != ACTION_INVALID) - // setAction(action); - - Particle *particle = effect->getParticle(); - - if (effectId >= 0) - { - mStatusParticleEffects.setLocally(effectId, particle); - } - else - { - mStunParticleEffects.clearLocally(); - if (particle) - mStunParticleEffects.addLocally(particle); - } -} - -void Being::updateStunMode(int oldMode, int newMode) -{ - handleStatusEffect(StatusEffect::getStatusEffect(oldMode, false), -1); - handleStatusEffect(StatusEffect::getStatusEffect(newMode, true), -1); -} - -void Being::updateStatusEffect(int index, bool newStatus) -{ - handleStatusEffect(StatusEffect::getStatusEffect(index, newStatus), index); -} - -void Being::setStatusEffect(int index, bool active) -{ - const bool wasActive = mStatusEffects.find(index) != mStatusEffects.end(); - - if (active != wasActive) - { - updateStatusEffect(index, active); - if (active) - mStatusEffects.insert(index); - else - mStatusEffects.erase(index); - } -} - -/** TODO: eAthena only */ +/** Note: Used by Tmw-Athena only */ int Being::getOffset(char pos, char neg) const { // Check whether we're walking in the requested direction - if (mAction != WALK || !(mDirection & (pos | neg))) + if (mAction != MOVE || !(mDirection & (pos | neg))) return 0; int offset = 0; @@ -839,9 +972,9 @@ int Being::getOffset(char pos, char neg) const if (mMap) { offset = (pos == LEFT && neg == RIGHT) ? - (int)((get_elapsed_time(mWalkTime) + (int)((get_elapsed_time(mActionTime) * mMap->getTileWidth()) / mWalkSpeed.x) : - (int)((get_elapsed_time(mWalkTime) + (int)((get_elapsed_time(mActionTime) * mMap->getTileHeight()) / mWalkSpeed.y); } @@ -859,181 +992,185 @@ int Being::getOffset(char pos, char neg) const int Being::getWidth() const { - AnimatedSprite *base = NULL; - - for (SpriteConstIterator it = mSprites.begin(); it != mSprites.end(); it++) - if ((base = (*it))) - break; - - if (base) - return std::max(base->getWidth(), DEFAULT_BEING_WIDTH); - - return DEFAULT_BEING_WIDTH; + return std::max(CompoundSprite::getWidth(), DEFAULT_BEING_WIDTH); } int Being::getHeight() const { - AnimatedSprite *base = NULL; - - for (SpriteConstIterator it = mSprites.begin(); it != mSprites.end(); it++) - if ((base = (*it))) - break; - - if (base) - return std::max(base->getHeight(), DEFAULT_BEING_HEIGHT); - - return DEFAULT_BEING_HEIGHT; + return std::max(CompoundSprite::getHeight(), DEFAULT_BEING_HEIGHT); } -void Being::setTargetAnimation(SimpleAnimation *animation) +void Being::updateCoords() { - mUsedTargetCursor = animation; - mUsedTargetCursor->reset(); -} - -struct EffectDescription { - std::string mGFXEffect; - std::string mSFXEffect; -}; + if (!mDispName) + return; -static EffectDescription *default_effect = NULL; -static std::map<int, EffectDescription *> effects; -static bool effects_initialized = false; + // Monster names show above the sprite instead of below it + if (getType() == MONSTER) + mDispName->adviseXY(getPixelX(), + getPixelY() - getHeight() - mDispName->getHeight()); + else + mDispName->adviseXY(getPixelX(), getPixelY()); +} -static EffectDescription *getEffectDescription(xmlNodePtr node, int *id) +void Being::flashName(int time) { - EffectDescription *ed = new EffectDescription; - - *id = atoi(XML::getProperty(node, "id", "-1").c_str()); - ed->mSFXEffect = XML::getProperty(node, "audio", ""); - ed->mGFXEffect = XML::getProperty(node, "particle", ""); - - return ed; + if (mDispName) + mDispName->flash(time); } -static EffectDescription *getEffectDescription(int effectId) +void Being::showName() { - if (!effects_initialized) - { - XML::Document doc(BEING_EFFECTS_FILE); - xmlNodePtr root = doc.rootNode(); + delete mDispName; + mDispName = 0; + std::string mDisplayName(mName); - if (!root || !xmlStrEqual(root->name, BAD_CAST "being-effects")) + Being* player = static_cast<Being*>(this); + if (player) + { + if (config.getBoolValue("showgender")) { - logger->log("Error loading being effects file: " - BEING_EFFECTS_FILE); - return NULL; + if (getGender() == GENDER_FEMALE) + mDisplayName += " \u2640"; + else if (getGender() == GENDER_MALE) + mDisplayName += " \u2642"; } - for_each_xml_child_node(node, root) + // Display the IP when under tmw-Athena (GM only). + if (Net::getNetworkType() == ServerInfo::TMWATHENA && player_node + && player_node->getShowIp() && player->getIp()) { - int id; - - if (xmlStrEqual(node->name, BAD_CAST "effect")) - { - EffectDescription *EffectDescription = - getEffectDescription(node, &id); - effects[id] = EffectDescription; - } - else if (xmlStrEqual(node->name, BAD_CAST "default")) - { - EffectDescription *effectDescription = - getEffectDescription(node, &id); - - if (default_effect) - delete default_effect; + mDisplayName += strprintf(" %s", ipToString(player->getIp())); + } + } - default_effect = effectDescription; - } + if (getType() == MONSTER) + { + if (config.getBoolValue("showMonstersTakedDamage")) + { + mDisplayName += ", " + toString(getDamageTaken()); } + } - effects_initialized = true; - } // done initializing + gcn::Font *font = 0; + if (player_node && player_node->getTarget() == this + && getType() != MONSTER) + { + font = boldFont; + } - EffectDescription *ed = effects[effectId]; + mDispName = new FlashText(mDisplayName, getPixelX(), getPixelY(), + gcn::Graphics::CENTER, mNameColor, font); - return ed ? ed : default_effect; + updateCoords(); } -void Being::internalTriggerEffect(int effectId, bool sfx, bool gfx) +void Being::updateColors() { - logger->log("Special effect #%d on %s", effectId, - getId() == player_node->getId() ? "self" : "other"); - - EffectDescription *ed = getEffectDescription(effectId); - - if (!ed) + if (getType() == MONSTER) { - logger->log("Unknown special effect and no default recorded"); - return; + mNameColor = &userPalette->getColor(UserPalette::MONSTER); + mTextColor = &userPalette->getColor(UserPalette::MONSTER); } - - if (gfx && !ed->mGFXEffect.empty()) + else if (getType() == NPC) { - Particle *selfFX; - - selfFX = particleEngine->addEffect(ed->mGFXEffect, 0, 0); - controlParticle(selfFX); + mNameColor = &userPalette->getColor(UserPalette::NPC); + mTextColor = &userPalette->getColor(UserPalette::NPC); } + else if (this == player_node) + { + mNameColor = &userPalette->getColor(UserPalette::SELF); + mTextColor = &Theme::getThemeColor(Theme::PLAYER); + } + else + { + mTextColor = &userPalette->getColor(Theme::PLAYER); - if (sfx && !ed->mSFXEffect.empty()) - sound.playSfx(ed->mSFXEffect); -} + if (mIsGM) + { + mTextColor = &userPalette->getColor(UserPalette::GM); + mNameColor = &userPalette->getColor(UserPalette::GM); + } + else if (mParty && mParty == player_node->getParty()) + { + mNameColor = &userPalette->getColor(UserPalette::PARTY); + } + else + { + mNameColor = &userPalette->getColor(UserPalette::PC); + } + } -void Being::updateCoords() -{ if (mDispName) { - mDispName->adviseXY(getPixelX(), getPixelY()); + mDispName->setColor(mNameColor); } } -void Being::flashName(int time) +void Being::setSprite(unsigned int slot, int id, const std::string &color, + bool isWeapon) { - if (mDispName) - mDispName->flash(time); -} + assert(slot < Net::getCharHandler()->maxSprite()); -void Being::showName() -{ - delete mDispName; - mDispName = 0; - std::string mDisplayName(mName); + if (slot >= size()) + ensureSize(slot + 1); - if (getType() == PLAYER) + if (slot >= mSpriteIDs.size()) + mSpriteIDs.resize(slot + 1, 0); + + if (slot >= mSpriteColors.size()) + mSpriteColors.resize(slot + 1, ""); + + // id = 0 means unequip + if (id == 0) { - Player* player = static_cast<Player*>(this); - if (player) - { - if (config.getValue("showgender", false)) - { - if (player->getGender() == GENDER_FEMALE) - mDisplayName += " \u2640"; - else - mDisplayName += " \u2642"; - } - if (Net::getNetworkType() == ServerInfo::TMWATHENA && player_node - && player_node->getShowIp() && player->getIp()) - { - mDisplayName += strprintf(" %s", ipToString(player->getIp())); - } - } + removeSprite(slot); + + if (isWeapon) + mEquippedWeapon = NULL; } - else if (getType() == MONSTER) + else { - if (config.getValue("showMonstersTakedDamage", false)) + std::string filename = itemDb->get(id).getSprite(mGender); + AnimatedSprite *equipmentSprite = NULL; + + if (!filename.empty()) { - mDisplayName += ", " + toString(getDamageTaken()); + if (!color.empty()) + filename += "|" + color; + + equipmentSprite = AnimatedSprite::load( + paths.getStringValue("sprites") + filename); } + + if (equipmentSprite) + equipmentSprite->setDirection(getSpriteDirection()); + + CompoundSprite::setSprite(slot, equipmentSprite); + + if (isWeapon) + mEquippedWeapon = &itemDb->get(id); + + setAction(mAction); } - mDispName = new FlashText(mDisplayName, getPixelX(), getPixelY(), - gcn::Graphics::CENTER, mNameColor); + mSpriteIDs[slot] = id; + mSpriteColors[slot] = color; +} + +void Being::setSpriteID(unsigned int slot, int id) +{ + setSprite(slot, id, mSpriteColors[slot]); +} + +void Being::setSpriteColor(unsigned int slot, const std::string &color) +{ + setSprite(slot, mSpriteIDs[slot], color); } int Being::getNumberOfLayers() const { - return mSprites.size(); + return CompoundSprite::getNumberOfLayers(); } void Being::load() @@ -1042,8 +1179,8 @@ void Being::load() // we can go. int hairstyles = 1; - while (ItemDB::get(-hairstyles).getSprite(GENDER_MALE) != - paths.getValue("spriteErrorFile", "error.xml")) + while (itemDb->get(-hairstyles).getSprite(GENDER_MALE) != + paths.getStringValue("spriteErrorFile")) hairstyles++; mNumberOfHairstyles = hairstyles; @@ -1054,3 +1191,64 @@ void Being::updateName() if (mShowName) showName(); } + +void Being::setGender(Gender gender) +{ + if (gender != mGender) + { + mGender = gender; + + // Reload all subsprites + for (unsigned int i = 0; i < mSpriteIDs.size(); i++) + { + if (mSpriteIDs.at(i) != 0) + setSprite(i, mSpriteIDs.at(i), mSpriteColors.at(i)); + } + + updateName(); + } +} + +void Being::setGM(bool gm) +{ + mIsGM = gm; + + updateColors(); +} + +bool Being::canTalk() +{ + return mType == NPC; +} + +void Being::talkTo() +{ + Net::getNpcHandler()->talk(mId); +} + +void Being::event(Channels channel, const Mana::Event &event) +{ + if (channel == CHANNEL_CHAT && + (event.getName() == EVENT_BEING || event.getName() == EVENT_PLAYER) && + event.getInt("permissions") & PlayerRelation::SPEECH_FLOAT) + { + try + { + if (mId == event.getInt("beingId")) + { + setSpeech(event.getString("text")); + } + } + catch (Mana::BadEvent badEvent) + {} + } + else if (channel == CHANNEL_CONFIG && + event.getName() == EVENT_CONFIGOPTIONCHANGED) + { + if (getType() == PLAYER && event.getString("option") == "visiblenames") + { + setShowName(config.getBoolValue("visiblenames")); + } + } + +} diff --git a/src/being.h b/src/being.h index 71b3e2cf..7f6f8007 100644 --- a/src/being.h +++ b/src/being.h @@ -22,19 +22,17 @@ #ifndef BEING_H #define BEING_H -#include "configlistener.h" +#include "actorsprite.h" +#include "listener.h" #include "map.h" -#include "particlecontainer.h" #include "position.h" -#include "sprite.h" #include "vector.h" -#include "resources/spritedef.h" - #include <guichan/color.hpp> #include <SDL_types.h> +#include <map> #include <set> #include <string> #include <vector> @@ -45,31 +43,27 @@ #define SPEECH_TIME 500 #define SPEECH_MAX_TIME 1000 -class AnimatedSprite; +class BeingInfo; class FlashText; -class Graphics; -class Image; +class Guild; class ItemInfo; class Item; class Particle; +class Party; class Position; -class SimpleAnimation; class SpeechBubble; class Text; -class StatusEffect; +enum Gender +{ + GENDER_MALE = 0, + GENDER_FEMALE = 1, + GENDER_UNSPECIFIED = 2 +}; -class Being : public Sprite, public ConfigListener +class Being : public ActorSprite, public Mana::Listener { public: - enum Type - { - UNKNOWN, - PLAYER, - NPC, - MONSTER - }; - /** * Action the being is currently performing * WARNING: Has to be in sync with the same enum in the Being class @@ -78,21 +72,13 @@ class Being : public Sprite, public ConfigListener enum Action { STAND, - WALK, + MOVE, ATTACK, SIT, DEAD, HURT }; - enum TargetCursorSize - { - TC_SMALL = 0, - TC_MEDIUM, - TC_LARGE, - NUM_TC - }; - enum Speech { NO_SPEECH = 0, @@ -128,29 +114,27 @@ class Being : public Sprite, public ConfigListener * @param subtype partly determines the type of the being * @param map the map the being is on */ - Being(int id, int subtype, Map *map); + Being(int id, Type type, int subtype, Map *map); virtual ~Being(); + Type getType() const { return mType; } + /** * Removes all path nodes from this being. */ void clearPath(); /** - * Returns the walk time. - * Used to know which frame to display and trigger - * the next Tile step. - * TODO: Used by eAthena only? + * Returns the time spent in the current action. */ - int getWalkTime() const { return mWalkTime; } + int getActionTime() const { return mActionTime; } /** - * Set the current WalkTime value. - * TODO: Used by eAthena only? + * Set the current action time. * @see Ea::BeingHandler that set it to tick time. */ - void setWalkTime(int walkTime) { mWalkTime = walkTime; } + void setActionTime(int actionTime) { mActionTime = actionTime; } /** * Makes this being take the next tile of its path. @@ -216,7 +200,7 @@ class Being : public Sprite, public ConfigListener * @param damage the amount of damage recieved (0 means miss) * @param type the attack type */ - virtual void takeDamage(Being *attacker, int damage, AttackType type); + void takeDamage(Being *attacker, int damage, AttackType type); /** * Handles an attack of another being by this being. @@ -238,23 +222,82 @@ class Being : public Sprite, public ConfigListener * * @param name The name that should appear. */ - virtual void setName(const std::string &name); + void setName(const std::string &name); bool getShowName() const { return mShowName; } - virtual void setShowName(bool doShowName); + void setShowName(bool doShowName); /** - * Following are set from the server (mainly for players) + * Sets the name of the party the being is in. Shown in BeingPopup. */ void setPartyName(const std::string &name) { mPartyName = name; } const std::string &getPartyName() const { return mPartyName; } - virtual void setGuildName(const std::string &name); + /** + * Sets the name of the primary guild the being is in. Shown in + * BeingPopup (eventually). + */ + void setGuildName(const std::string &name); + + void setGuildPos(const std::string &pos); + + /** + * Adds a guild to the being. + */ + void addGuild(Guild *guild); + + /** + * Removers a guild from the being. + */ + void removeGuild(int id); + + /** + * Returns a pointer to the specified guild that the being is in. + */ + Guild *getGuild(const std::string &guildName) const; + + /** + * Returns a pointer to the specified guild that the being is in. + */ + Guild *getGuild(int id) const; + + /** + * Returns all guilds the being is in. + */ + const std::map<int, Guild*> &getGuilds() const + { return mGuilds; } + + /** + * Removes all guilds the being is in. + */ + void clearGuilds(); + + /** + * Get number of guilds the being belongs to. + */ + short getNumberOfGuilds() const + { return mGuilds.size(); } + + bool isInParty() const + { return mParty != NULL; } + + void setParty(Party *party); + + Party *getParty() const + { return mParty; } - virtual void setGuildPos(const std::string &pos); + /** + * Sets visible equipments for this being. + */ + void setSprite(unsigned int slot, int id, + const std::string &color = "", bool isWeapon = false); + + void setSpriteID(unsigned int slot, int id); + + void setSpriteColor(unsigned int slot, const std::string &color = ""); /** * Get the number of hairstyles implemented @@ -277,25 +320,27 @@ class Being : public Sprite, public ConfigListener */ void drawSpeech(int offsetX, int offsetY); + Uint16 getSubType() const { return mSubType; } + + /** + * Set Being's subtype (mostly for view for monsters and NPCs) + */ + void setSubtype(Uint16 subtype); + + const BeingInfo *getInfo() const + { return mInfo; } + + TargetCursorSize getTargetCursorSize() const; + /** - * Draws the emotion picture above the being. + * Gets the way the object is blocked by other objects. */ - void drawEmotion(Graphics *graphics, int offsetX, int offsetY); + unsigned char getWalkMask() const; /** - * Returns the type of the being. + * Gets the way the monster blocks pathfinding for other objects */ - virtual Type getType() const { return UNKNOWN; } - - /** - * Return Being's current Job (player job, npc, monster, creature ) - */ - Uint16 getSubType() const { return mSubType; } - - /** - * Set Being's current Job (player job, npc, monster, creature ) - */ - virtual void setSubtype(Uint16 subtype) { mSubType = subtype; } + Map::BlockType getBlockType() const; /** * Sets the walk speed. @@ -324,18 +369,6 @@ class Being : public Sprite, public ConfigListener int getAttackSpeed() const { return mAttackSpeed; } /** - * Sets the sprite id. - */ - void setId(int id) { mId = id; } - - int getId() const { return mId; } - - /** - * Sets the map the being is on - */ - void setMap(Map *map); - - /** * Sets the current action. */ virtual void setAction(Action action, int attackType = 0); @@ -361,65 +394,11 @@ class Being : public Sprite, public ConfigListener void setDirection(Uint8 direction); /** - * Returns the being's current sprite frame number. - */ - int getCurrentFrame() const { return mFrame; } - - /** - * Set the being's current sprite frame number. - */ - void setFrame(int frame) { mFrame = frame; } - - /** * Returns the direction the being is facing. */ SpriteDirection getSpriteDirection() const { return SpriteDirection(mSpriteDirection); } - /** - * Draws this being to the given graphics context. - * - * @see Sprite::draw(Graphics, int, int) - * - * TODO: The following two functions should be combined. - * at some point draw(), was changed to use mPx and mPy, with arugements - * only for the offset, drawSpriteAt() takes x, and y and draws the sprite - * exactly at those coords (though it does do some computing to work how the - * old draw() worked). - */ - virtual void draw(Graphics *graphics, int offsetX, int offsetY) const; - - virtual void drawSpriteAt(Graphics *graphics, int x, int y) const; - - /** - * Set the alpha opacity used to draw the being. - */ - virtual void setAlpha(float alpha) - { mAlpha = alpha; } - - /** - * Returns the current alpha opacity of the Being. - */ - virtual float getAlpha() const - { return mAlpha; } - - /** - * Returns the X coordinate in pixels. - */ - int getPixelX() const - { return (int) mPos.x; } - - /** - * Returns the Y coordinate in pixels. - * - * @see Sprite::getPixelY() - */ - int getPixelY() const - { return (int) mPos.y; } - - /** - * Sets the position of this being. - */ void setPosition(const Vector &pos); /** @@ -427,17 +406,12 @@ class Being : public Sprite, public ConfigListener * * @see setPosition(const Vector &pos) */ - void setPosition(float x, float y, float z = 0.0f) + inline void setPosition(float x, float y, float z = 0.0f) { setPosition(Vector(x, y, z)); } /** - * Returns the position of this being. - */ - const Vector &getPosition() const { return mPos; } - - /** * Returns the horizontal size of the current base sprite of the being. */ virtual int getWidth() const; @@ -453,106 +427,61 @@ class Being : public Sprite, public ConfigListener virtual int getCollisionRadius() const; /** - * Returns the required size of a target cursor for this being. - */ - virtual Being::TargetCursorSize getTargetCursorSize() const - { return TC_MEDIUM; } - - /** - * Take control of a particle. - */ - void controlParticle(Particle *particle); - - /** * Shoots a missile particle from this being, to target being */ void fireMissile(Being *target, const std::string &particle); /** - * Gets the way the object is blocked by other objects. - */ - virtual unsigned char getWalkMask() const - { return 0x00; } //can walk through everything - - /** * Returns the path this being is following. An empty path is returned * when this being isn't following any path currently. */ const Path &getPath() const { return mPath; } - /** - * Sets the target animation for this being. - */ - void setTargetAnimation(SimpleAnimation *animation); + static void load(); - /** - * Untargets the being - */ - void untarget() { mUsedTargetCursor = NULL; } + void flashName(int time); + int getDamageTaken() const + { return mDamageTaken; } + + void updateName(); /** - * Set the Emoticon type and time displayed above - * the being. + * Sets the gender of this being. */ - void setEmote(Uint8 emotion, Uint8 emote_time) - { - mEmotion = emotion; - mEmotionTime = emote_time; - } + virtual void setGender(Gender gender); + + Gender getGender() const + { return mGender; } /** - * Get the current Emoticon type displayed above - * the being. + * Whether or not this player is a GM. */ - Uint8 getEmotion() const { return mEmotion; } + bool isGM() const + { return mIsGM; } /** - * Sets the being's stun mode. If zero, the being is `normal', - * otherwise it is `stunned' in some fashion. + * Triggers whether or not to show the name as a GM name. */ - void setStunMode(int stunMode) - { - if (mStunMode != stunMode) - updateStunMode(mStunMode, stunMode); - mStunMode = stunMode; - }; - - void setStatusEffect(int index, bool active); + void setGM(bool gm); /** - * A status effect block is a 16 bit mask of status effects. - * We assign each such flag a block ID of offset + bitnr. - * - * These are NOT the same as the status effect indices. + * Sets the IP or an IP hash. + * The TMW-Athena server sends this information only to GMs. */ - void setStatusEffectBlock(int offset, Uint16 flags); + void setIp(int ip) { mIp = ip; } /** - * Triggers a visual effect, such as `level up' - * - * Only draws the visual effect, does not play sound effects - * - * \param effectId ID of the effect to trigger + * Returns the player's IP or an IP hash. + * Value is 0 if not set by the server. */ - virtual void triggerEffect(int effectId) - { - internalTriggerEffect(effectId, false, true); - } - - virtual AnimatedSprite *getSprite(int index) const - { return mSprites[index]; } + int getIp() const { return mIp; } - static void load(); - - virtual void optionChanged(const std::string &value) {} + bool canTalk(); - void flashName(int time); - - int getDamageTaken() const - { return mDamageTaken; } + void talkTo(); - void updateName(); + void event(Channels channel, const Mana::Event &event); protected: /** @@ -563,69 +492,29 @@ class Being : public Sprite, public ConfigListener /** * Updates name's location. */ - virtual void updateCoords(); + void updateCoords(); - /** - * Gets the way the object blocks pathfinding for other objects - */ - virtual Map::BlockType getBlockType() const - { return Map::BLOCKTYPE_NONE; } + void showName(); - /** - * Trigger visual effect, with components - * - * \param effectId ID of the effect to trigger - * \param sfx Whether to trigger sound effects - * \param gfx Whether to trigger graphical effects - */ - void internalTriggerEffect(int effectId, bool sfx, bool gfx); - - /** - * Notify self that the stun mode has been updated. Invoked by - * setStunMode if something changed. - */ - virtual void updateStunMode(int oldMode, int newMode); + void updateColors(); - /** - * Notify self that a status effect has flipped. - * The new flag is passed. - */ - virtual void updateStatusEffect(int index, bool newStatus); + BeingInfo *mInfo; - /** - * Handle an update to a status or stun effect - * - * \param The StatusEffect to effect - * \param effectId -1 for stun, otherwise the effect index - */ - virtual void handleStatusEffect(StatusEffect *effect, int effectId); + int mActionTime; /**< Time spent in current action */ - virtual void showName(); - - /** The current sprite Frame number to be displayed */ - int mFrame; - - /** Used to trigger the nextStep (walking on next Tile) - * TODO: Used by eAthena only? - */ - int mWalkTime; - - int mEmotion; /**< Currently showing emotion */ - int mEmotionTime; /**< Time until emotion disappears */ /** Time until the last speech sentence disappears */ int mSpeechTime; + int mAttackType; int mAttackSpeed; /**< Attack speed */ + Action mAction; /**< Action the being is performing */ Uint16 mSubType; /**< Subtype (graphical view, basically) */ - int mId; /**< Unique sprite id */ Uint8 mDirection; /**< Facing direction */ Uint8 mSpriteDirection; /**< Facing direction */ - Map *mMap; /**< Map on which this being resides */ std::string mName; /**< Name of character */ std::string mPartyName; - MapSprite mMapSprite; /** * Holds a text object when the being displays it's name, 0 otherwise @@ -643,20 +532,18 @@ class Being : public Sprite, public ConfigListener std::string mSpeech; Text *mText; const gcn::Color *mTextColor; - Uint16 mStunMode; /**< Stun mode; zero if not stunned */ - std::set<int> mStatusEffects; /**< set of active status effects */ - typedef std::vector<AnimatedSprite*> Sprites; - typedef Sprites::iterator SpriteIterator; - typedef Sprites::const_iterator SpriteConstIterator; - Sprites mSprites; - float mAlpha; /**< Alpha opacity to draw the sprite */ + Vector mDest; /**< destination coordinates. */ + + std::vector<int> mSpriteIDs; + std::vector<std::string> mSpriteColors; + Gender mGender; - ParticleList mStunParticleEffects; - ParticleVector mStatusParticleEffects; - ParticleList mChildParticleEffects; + // Character guild information + std::map<int, Guild*> mGuilds; + Party *mParty; - Vector mDest; /**< destination coordinates. */ + bool mIsGM; private: @@ -667,8 +554,7 @@ class Being : public Sprite, public ConfigListener */ int getOffset(char pos, char neg) const; - /** Reset particle status effects on next redraw? */ - bool mMustResetParticles; + const Type mType; /** Speech Bubble components */ SpeechBubble *mSpeechBubble; @@ -681,13 +567,11 @@ class Being : public Sprite, public ConfigListener */ Vector mWalkSpeed; - Vector mPos; /**< Position coordinates. */ int mX, mY; /**< Position in tile */ int mDamageTaken; - /** Target cursor being used */ - SimpleAnimation *mUsedTargetCursor; + int mIp; }; #endif diff --git a/src/beingmanager.cpp b/src/beingmanager.cpp deleted file mode 100644 index d7045684..00000000 --- a/src/beingmanager.cpp +++ /dev/null @@ -1,327 +0,0 @@ -/* - * The Mana Client - * Copyright (C) 2004-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * - * This file is part of The Mana Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "beingmanager.h" - -#include "localplayer.h" -#include "monster.h" -#include "npc.h" -#include "player.h" - -#include "gui/viewport.h" - -#include "net/gamehandler.h" -#include "net/net.h" - -#include "utils/stringutils.h" -#include "utils/dtor.h" - -#include <cassert> - -class FindBeingFunctor -{ - public: - bool operator() (Being *being) - { - Uint16 other_y = y + ((being->getType() == Being::NPC) ? 1 : 0); - const Vector &pos = being->getPosition(); - return ((int) pos.x / 32 == x && - ((int) pos.y / 32 == y || (int) pos.y / 32 == other_y) && - being->isAlive() && - (type == Being::UNKNOWN || being->getType() == type)); - } - - Uint16 x, y; - Being::Type type; -} beingFinder; - -class PlayerNamesLister : public AutoCompleteLister -{ - void getAutoCompleteList(std::vector<std::string>& names) const - { - Beings::iterator i = beingManager->mBeings.begin(); - names.clear(); - - while (i != beingManager->mBeings.end()) - { - Being *being = (*i); - if (being->getType() == Being::PLAYER && being->getName() != "") - names.push_back(being->getName()); - - ++i; - } - } -}; - -class PlayerNPCNamesLister : public AutoCompleteLister -{ - void getAutoCompleteList(std::vector<std::string>& names) const - { - Beings::iterator i = beingManager->mBeings.begin(); - names.clear(); - - while (i != beingManager->mBeings.end()) - { - Being *being = (*i); - if ((being->getType() == Being::PLAYER - || being->getType() == Being::NPC) - && being->getName() != "") - names.push_back(being->getName()); - - ++i; - } - } -}; - -BeingManager::BeingManager() -{ - mPlayerNames = new PlayerNamesLister; - mPlayerNPCNames = new PlayerNPCNamesLister; -} - -BeingManager::~BeingManager() -{ - clear(); -} - -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(int id, Being::Type type, int subtype) -{ - Being *being; - - switch (type) - { - case Being::PLAYER: - being = new Player(id, subtype, mMap); - break; - case Being::NPC: - being = new NPC(id, subtype, mMap); - break; - case Being::MONSTER: - being = new Monster(id, subtype, mMap); - break; - case Being::UNKNOWN: - being = new Being(id, subtype, mMap); - break; - default: - assert(false); - } - - mBeings.push_back(being); - return being; -} - -void BeingManager::destroyBeing(Being *being) -{ - mBeings.remove(being); - viewport->clearHoverBeing(being); - delete being; -} - -Being *BeingManager::findBeing(int id) const -{ - for (Beings::const_iterator i = mBeings.begin(), i_end = mBeings.end(); - i != i_end; ++i) - { - Being *being = (*i); - if (being->getId() == id) - return being; - } - return NULL; -} - -Being *BeingManager::findBeing(int x, int y, Being::Type type) const -{ - beingFinder.x = x; - beingFinder.y = y; - beingFinder.type = type; - - Beings::const_iterator i = find_if(mBeings.begin(), mBeings.end(), - beingFinder); - - return (i == mBeings.end()) ? NULL : *i; -} - -Being *BeingManager::findBeingByPixel(int x, int y) const -{ - Beings::const_iterator itr = mBeings.begin(); - Beings::const_iterator itr_end = mBeings.end(); - - for (; itr != itr_end; ++itr) - { - Being *being = (*itr); - - int xtol = being->getWidth() / 2; - int uptol = being->getHeight(); - - if ((being->isAlive()) && - (being != player_node) && - (being->getPixelX() - xtol <= x) && - (being->getPixelX() + xtol >= x) && - (being->getPixelY() - uptol <= y) && - (being->getPixelY() >= y)) - { - return being; - } - } - - return NULL; -} - -Being *BeingManager::findBeingByName(const std::string &name, - Being::Type type) const -{ - for (Beings::const_iterator i = mBeings.begin(), i_end = mBeings.end(); - i != i_end; ++i) - { - Being *being = (*i); - if (!compareStrI(being->getName(),name) && - (type == Being::UNKNOWN || type == being->getType())) - return being; - } - return NULL; -} - -const Beings &BeingManager::getAll() const -{ - return mBeings; -} - -void BeingManager::logic() -{ - Beings::iterator i = mBeings.begin(); - while (i != mBeings.end()) - { - Being *being = (*i); - - being->logic(); - - if (!being->isAlive() && - Net::getGameHandler()->removeDeadBeings() && - being->getCurrentFrame() >= 20) - { - delete being; - i = mBeings.erase(i); - } - else - { - ++i; - } - } -} - -void BeingManager::clear() -{ - if (player_node) - mBeings.remove(player_node); - - delete_all(mBeings); - mBeings.clear(); - - if (player_node) - mBeings.push_back(player_node); -} - -Being *BeingManager::findNearestLivingBeing(int x, int y, - int maxTileDist, Being::Type type, - Being *excluded) const -{ - Being *closestBeing = 0; - int dist = 0; - - const int maxDist = maxTileDist * 32; - - Beings::const_iterator itr = mBeings.begin(); - Beings::const_iterator itr_end = mBeings.end(); - - for (; itr != itr_end; ++itr) - { - Being *being = (*itr); - const Vector &pos = being->getPosition(); - int d = abs(((int) pos.x) - x) + abs(((int) pos.y) - y); - - if ((being->getType() == type || type == Being::UNKNOWN) - && (d < dist || !closestBeing) // it is closer - && being->isAlive() // no dead beings - && being != excluded) - { - dist = d; - closestBeing = being; - } - } - - return (maxDist >= dist) ? closestBeing : 0; -} - -Being *BeingManager::findNearestLivingBeing(Being *aroundBeing, int maxDist, - Being::Type type) const -{ - const Vector &pos = aroundBeing->getPosition(); - return findNearestLivingBeing((int)pos.x, (int)pos.y, maxDist, type, - aroundBeing); -} - -bool BeingManager::hasBeing(Being *being) const -{ - for (Beings::const_iterator i = mBeings.begin(), i_end = mBeings.end(); - i != i_end; ++i) - { - if (being == *i) - return true; - } - - return false; -} - -AutoCompleteLister *BeingManager::getPlayerNameLister() -{ - return mPlayerNames; -} - -AutoCompleteLister *BeingManager::getPlayerNPCNameLister() -{ - return mPlayerNPCNames; -} - -void BeingManager::updatePlayerNames() -{ - Beings::iterator i = mBeings.begin(); - - while (i != mBeings.end()) - { - Being *being = (*i); - if (being->getType() == Being::PLAYER && being->getName() != "") - being->updateName(); - ++i; - } -} diff --git a/src/chatlog.cpp b/src/chatlog.cpp new file mode 100644 index 00000000..f33b1aff --- /dev/null +++ b/src/chatlog.cpp @@ -0,0 +1,175 @@ +/* + * The Mana World + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "chatlog.h" + +#include <iostream> +#include <sstream> +#include <dirent.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/time.h> + +#ifdef WIN32 +#include <windows.h> +#elif defined __APPLE__ +#include <Carbon/Carbon.h> +#endif + +#include "configuration.h" + +#include "utils/stringutils.h" + +ChatLogger::ChatLogger() +{ +} + +ChatLogger::~ChatLogger() +{ + if (mLogFile.is_open()) + mLogFile.close(); +} + +void ChatLogger::setLogFile(const std::string &logFilename) +{ + if (mLogFile.is_open()) + mLogFile.close(); + + mLogFile.open(logFilename.c_str(), std::ios_base::app); + + if (!mLogFile.is_open()) + { + std::cout << "Warning: error while opening " << logFilename << + " for writing.\n"; + } +} + +void ChatLogger::setLogDir(const std::string &logDir) +{ + mLogDir = logDir; + + if (mLogFile.is_open()) + mLogFile.close(); + + DIR *dir = opendir(mLogDir.c_str()); + if (!dir) + makeDir(mLogDir); + else + closedir(dir); +} + +void ChatLogger::log(std::string str) +{ + std::string dateStr = getDateString(); + if (!mLogFile.is_open() || dateStr != mLogDate) + { + mLogDate = dateStr; + setLogFile(strprintf("%s/%s/#General_%s.log", mLogDir.c_str(), + mServerName.c_str(), dateStr.c_str())); + } + + str = removeColors(str); + writeTo(mLogFile, str); +} + +void ChatLogger::log(std::string name, std::string str) +{ + std::ofstream logFile; + logFile.open(strprintf("%s/%s/%s_%s.log", mLogDir.c_str(), mServerName.c_str(), + secureName(name).c_str(), getDateString().c_str()).c_str(), + std::ios_base::app); + + if (!logFile.is_open()) + return; + + str = removeColors(str); + writeTo(logFile, str); + + if (logFile.is_open()) + logFile.close(); +} + +std::string ChatLogger::getDateString() const +{ + std::string date; + + time_t rawtime; + struct tm *timeinfo; + char buffer [80]; + + time (&rawtime); + timeinfo = localtime(&rawtime); + + strftime(buffer, 79, "%y-%m-%d", timeinfo); + date = buffer; + return date; +} + +std::string ChatLogger::secureName(std::string &name) const +{ + for (unsigned int f = 0; f < name.length(); f ++) + { + if (name[f] < '0' && name[f] > '9' && name[f] < 'a' && name[f] > 'z' + && name[f] < 'A' && name[f] > 'Z' + && name[f] != '-' && name[f] != '+' && name[f] != '=' + && name[f] != '.' && name[f] != ','&& name[f] != ')' + && name[f] != '(' && name[f] != '[' && name[f] != ')') + { + name[f] = '_'; + } + } + return name; +} + +void ChatLogger::writeTo(std::ofstream &file, const std::string &str) const +{ + file << str << std::endl; +} + +void ChatLogger::setServerName(const std::string &serverName) +{ + mServerName = serverName; + if (mServerName == "") + mServerName = config.getStringValue("MostUsedServerName0"); + + if (mLogFile.is_open()) + mLogFile.close(); + + secureName(mServerName); + if (mLogDir != "") + { + DIR *dir = opendir((mLogDir + "/" + mServerName).c_str()); + if (!dir) + makeDir(mLogDir + "/" + mServerName); + else + closedir(dir); + } +} + +void ChatLogger::makeDir(const std::string &dir) +{ +#ifdef WIN32 + mkdir(dir.c_str()); +#else + mkdir(dir.c_str(), 0750); +#endif +} diff --git a/src/chatlog.h b/src/chatlog.h new file mode 100644 index 00000000..c359e953 --- /dev/null +++ b/src/chatlog.h @@ -0,0 +1,73 @@ +/* + * The Mana World + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _CHATLOG_H +#define _CHATLOG_H + +#include <fstream> + +class ChatLogger +{ + public: + /** + * Constructor. + */ + ChatLogger(); + + /** + * Destructor, closes log file. + */ + ~ChatLogger(); + + void setLogDir(const std::string &logDir); + + /** + * Enters a message in the log. The message will be timestamped. + */ + void log(std::string str); + + void log(std::string name, std::string str); + + std::string getDateString() const; + + std::string secureName(std::string &str) const; + + void setServerName(const std::string &serverName); + + private: + /** + * Sets the file to log to and opens it + */ + void setLogFile(const std::string &logFilename); + + void writeTo(std::ofstream &file, const std::string &str) const; + + void makeDir(const std::string &dir); + + std::ofstream mLogFile; + std::string mLogDir; + std::string mServerName; + std::string mLogDate; +}; + +extern ChatLogger *chatLogger; + +#endif diff --git a/src/client.cpp b/src/client.cpp index b57c3ae8..f61b5612 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -22,8 +22,10 @@ #include "client.h" #include "main.h" +#include "chatlog.h" #include "configuration.h" #include "emoteshortcut.h" +#include "event.h" #include "game.h" #include "itemshortcut.h" #include "keyboardconfig.h" @@ -47,10 +49,8 @@ #include "gui/sdlinput.h" #include "gui/serverdialog.h" #include "gui/setup.h" -#include "gui/theme.h" #include "gui/unregisterdialog.h" #include "gui/updatewindow.h" -#include "gui/userpalette.h" #include "gui/worldselectdialog.h" #include "gui/widgets/button.h" @@ -69,8 +69,11 @@ #include "resources/image.h" #include "resources/itemdb.h" #include "resources/monsterdb.h" +#include "resources/specialdb.h" #include "resources/npcdb.h" #include "resources/resourcemanager.h" +#include "resources/theme.h" +#include "resources/userpalette.h" #include "utils/gettext.h" #include "utils/mkdir.h" @@ -111,11 +114,14 @@ Configuration config; /**< XML file configuration reader */ Configuration branding; /**< XML branding information reader */ Configuration paths; /**< XML default paths information reader */ Logger *logger; /**< Log object */ +ChatLogger *chatLogger; /**< Chat log object */ KeyboardConfig keyboard; UserPalette *userPalette; Graphics *graphics; +ItemDB *itemDb; + Sound sound; void ErrorListener::action(const gcn::ActionEvent &) @@ -166,6 +172,22 @@ int get_elapsed_time(int start_time) * MILLISECONDS_IN_A_TICK; } +bool isDoubleClick(int selected) +{ + const Uint32 maximumDelay = 500; + static Uint32 lastTime = 0; + static int lastSelected = -1; + + if (selected == lastSelected && lastTime + maximumDelay >= SDL_GetTicks()) + { + lastTime = 0; + return true; + } + + lastTime = SDL_GetTicks(); + lastSelected = selected; + return false; +} // This anonymous namespace hides whatever is inside from other modules. namespace { @@ -188,8 +210,16 @@ public: } } loginListener; -} // anonymous namespace +class ServerChoiceListener : public gcn::ActionListener +{ +public: + void action(const gcn::ActionEvent &) + { + Client::setState(STATE_CHOOSE_SERVER); + } +} serverChoiceListener; +} // anonymous namespace Client *Client::mInstance = 0; @@ -216,15 +246,22 @@ Client::Client(const Options &options): if (!options.brandingPath.empty()) { branding.init(options.brandingPath); + branding.setDefaultValues(getBrandingDefaults()); } initRootDir(); initHomeDir(); initConfiguration(); + chatLogger = new ChatLogger; + if (options.chatLogDir == "") + chatLogger->setLogDir(mLocalDataDir + std::string("/logs/")); + else + chatLogger->setLogDir(options.chatLogDir); + // Configure logger logger->setLogFile(mLocalDataDir + std::string("/mana.log")); - logger->setLogToStandardOut(config.getValue("logToStandardOut", 0)); + logger->setLogToStandardOut(config.getBoolValue("logToStandardOut")); // Log the mana version logger->log("Mana %s", FULL_VERSION); @@ -347,11 +384,11 @@ Client::Client(const Options &options): graphics = new Graphics; #endif - const int width = (int) config.getValue("screenwidth", defaultScreenWidth); - const int height = (int) config.getValue("screenheight", defaultScreenHeight); + const int width = config.getIntValue("screenwidth"); + const int height = config.getIntValue("screenheight"); const int bpp = 0; - const bool fullscreen = ((int) config.getValue("screen", 0) == 1); - const bool hwaccel = ((int) config.getValue("hwaccel", 0) == 1); + const bool fullscreen = config.getBoolValue("screen"); + const bool hwaccel = config.getBoolValue("hwaccel"); // Try to set the desired video mode if (!graphics->setVideoMode(width, height, bpp, fullscreen, hwaccel)) @@ -374,13 +411,11 @@ Client::Client(const Options &options): // Initialize sound engine try { - if (config.getValue("sound", 0) == 1) + if (config.getBoolValue("sound")) sound.init(); - sound.setSfxVolume((int) config.getValue("sfxVolume", - defaultSfxVolume)); - sound.setMusicVolume((int) config.getValue("musicVolume", - defaultMusicVolume)); + sound.setSfxVolume(config.getIntValue("sfxVolume")); + sound.setMusicVolume(config.getIntValue("musicVolume")); } catch (const char *err) { @@ -405,25 +440,25 @@ Client::Client(const Options &options): mCurrentServer.port = options.serverPort; loginData.username = options.username; loginData.password = options.password; - loginData.remember = config.getValue("remember", 0); + loginData.remember = config.getBoolValue("remember"); loginData.registerLogin = false; if (mCurrentServer.hostname.empty()) - { - mCurrentServer.hostname = branding.getValue("defaultServer", - "").c_str(); - } + mCurrentServer.hostname = branding.getValue("defaultServer","").c_str(); if (mCurrentServer.port == 0) { mCurrentServer.port = (short) branding.getValue("defaultPort", - DEFAULT_PORT); + DEFAULT_PORT); mCurrentServer.type = ServerInfo::parseType( - branding.getValue("defaultServerType", "tmwathena")); + branding.getValue("defaultServerType", "tmwathena")); } + if (chatLogger) + chatLogger->setServerName(mCurrentServer.hostname); + if (loginData.username.empty() && loginData.remember) - loginData.username = config.getValue("username", ""); + loginData.username = config.getStringValue("username"); if (mState != STATE_ERROR) mState = STATE_CHOOSE_SERVER; @@ -435,8 +470,16 @@ Client::Client(const Options &options): // Initialize frame limiting SDL_initFramerate(&mFpsManager); - config.addListener("fpslimit", this); - optionChanged("fpslimit"); + + listen(CHANNEL_CONFIG); + + //TODO: fix having to fake a option changed event + Mana::Event fakeevent(EVENT_CONFIGOPTIONCHANGED); + fakeevent.setString("option", "fpslimit"); + event(CHANNEL_CONFIG, fakeevent); + + // Initialize PlayerInfo + PlayerInfo::init(); } Client::~Client() @@ -447,7 +490,7 @@ Client::~Client() // Unload XML databases ColorDB::unload(); EmoteDB::unload(); - ItemDB::unload(); + delete itemDb; MonsterDB::unload(); NPCDB::unload(); StatusEffect::unload(); @@ -577,10 +620,8 @@ int Client::exec() - 3, 3); top->add(mSetupButton); - int screenWidth = (int) config.getValue("screenwidth", - defaultScreenWidth); - int screenHeight = (int) config.getValue("screenheight", - defaultScreenHeight); + int screenWidth = config.getIntValue("screenwidth"); + int screenHeight = config.getIntValue("screenheight"); mDesktop->setSize(screenWidth, screenHeight); } @@ -592,9 +633,12 @@ int Client::exec() if (mState != mOldState) { - Net::GeneralHandler *generalHandler = Net::getGeneralHandler(); - if (generalHandler) - generalHandler->stateChanged(mOldState, mState); + { + Mana::Event event(EVENT_STATECHANGE); + event.setInt("oldState", mOldState); + event.setInt("newState", mState); + event.trigger(CHANNEL_CLIENT); + } if (mOldState == STATE_GAME) { @@ -751,17 +795,48 @@ int Client::exec() // Read default paths file 'data/paths.xml' paths.init("paths.xml", true); + paths.setDefaultValues(getPathsDefaults()); + + Mana::Event::trigger(CHANNEL_CLIENT, EVENT_DBSLOADING); // Load XML databases ColorDB::load(); - ItemDB::load(); + switch (Net::getNetworkType()) + { + case ServerInfo::TMWATHENA: + itemDb = new TmwAthena::TaItemDB; + break; + case ServerInfo::MANASERV: + itemDb = new ManaServ::ManaServItemDB; + break; + default: + // Nothing + itemDb = 0; + break; + } + if (!itemDb || !itemDb->isLoaded()) + { + // Warn and return to login screen + errorMessage = + _("This server is missing needed world data. " + "Please contact the administrator(s)."); + mCurrentDialog = new OkDialog( + _("ItemDB: Error while loading " ITEMS_DB_FILE "!"), + errorMessage); + mCurrentDialog->addActionListener(&serverChoiceListener); + mCurrentDialog = NULL; // OkDialog deletes itself + break; + } Being::load(); // Hairstyles MonsterDB::load(); + SpecialDB::load(); NPCDB::load(); EmoteDB::load(); StatusEffect::load(); Units::loadUnits(); + ActorSprite::load(); + mDesktop->reloadWallpaper(); mState = STATE_GET_CHARACTERS; @@ -787,7 +862,7 @@ int Client::exec() mOptions.character, CharSelectDialog::Choose)) { ((CharSelectDialog*) mCurrentDialog)->selectByName( - config.getValue("lastCharacter", ""), + config.getStringValue("lastCharacter"), mOptions.chooseDefault ? CharSelectDialog::Choose : CharSelectDialog::Focus); @@ -997,12 +1072,18 @@ int Client::exec() return 0; } -void Client::optionChanged(const std::string &name) +void Client::event(Channels channel, const Mana::Event &event) { - const int fpsLimit = (int) config.getValue("fpslimit", 60); - mLimitFps = fpsLimit > 0; - if (mLimitFps) - SDL_setFramerate(&mFpsManager, fpsLimit); + if (channel == CHANNEL_CONFIG && + event.getName() == EVENT_CONFIGOPTIONCHANGED && + event.getString("option") == "fpslimit") + { + const int fpsLimit = config.getIntValue("fpslimit"); + mLimitFps = fpsLimit > 0; + if (mLimitFps) + SDL_setFramerate(&mFpsManager, fpsLimit); + } + } void Client::action(const gcn::ActionEvent &event) @@ -1203,6 +1284,7 @@ void Client::initConfiguration() { fclose(configFile); config.init(configPath); + config.setDefaultValues(getConfigDefaults()); } } @@ -1217,7 +1299,7 @@ void Client::initUpdatesDir() // If updatesHost is currently empty, fill it from config file if (mUpdateHost.empty()) { - mUpdateHost = config.getValue("updatehost", ""); + mUpdateHost = config.getStringValue("updatehost"); } // Don't go out of range int he next check @@ -1225,7 +1307,7 @@ void Client::initUpdatesDir() return; // Remove any trailing slash at the end of the update host - if (mUpdateHost.at(mUpdateHost.size() - 1) == '/') + if (!mUpdateHost.empty() && mUpdateHost.at(mUpdateHost.size() - 1) == '/') mUpdateHost.resize(mUpdateHost.size() - 1); // Parse out any "http://" or "ftp://", and set the updates directory @@ -1233,7 +1315,7 @@ void Client::initUpdatesDir() pos = mUpdateHost.find("://"); if (pos != mUpdateHost.npos) { - if (pos + 3 < mUpdateHost.length()) + if (pos + 3 < mUpdateHost.length() && !mUpdateHost.empty()) { updates << "updates/" << mUpdateHost.substr(pos + 3); mUpdatesDir = updates.str(); @@ -1304,7 +1386,7 @@ void Client::initScreenshotDir() mScreenshotDir = std::string(PHYSFS_getUserDir()) + "Desktop"; #endif - if (config.getValue("useScreenshotDirectorySuffix", true)) + if (config.getBoolValue("useScreenshotDirectorySuffix")) { std::string configScreenshotSuffix = config.getValue("screenshotDirectorySuffix", diff --git a/src/client.h b/src/client.h index f44d8bf2..8d2c23d5 100644 --- a/src/client.h +++ b/src/client.h @@ -22,7 +22,7 @@ #ifndef CLIENT_H #define CLIENT_H -#include "configlistener.h" +#include "listener.h" #include "net/serverinfo.h" @@ -68,6 +68,12 @@ extern LoginData loginData; int get_elapsed_time(int start_time); /** + * Returns if this call and the last call were done for the same + * selected index and within a short time. + */ +bool isDoubleClick(int selected); + +/** * All client states. */ enum State { @@ -113,7 +119,7 @@ enum State { * The core part of the client. This class initializes all subsystems, runs * the event loop, and shuts everything down again. */ -class Client : public ConfigListener, public gcn::ActionListener +class Client : public Mana::Listener, public gcn::ActionListener { public: /** @@ -142,6 +148,7 @@ public: std::string brandingPath; std::string updateHost; std::string dataPath; + std::string chatLogDir; std::string configDir; std::string localDataDir; std::string screenshotDir; @@ -178,7 +185,7 @@ public: static const std::string &getScreenshotDirectory() { return instance()->mScreenshotDir; } - void optionChanged(const std::string &name); + void event(Channels channel, const Mana::Event &event); void action(const gcn::ActionEvent &event); private: diff --git a/src/commandhandler.cpp b/src/commandhandler.cpp index 1c375ad9..dcaf6f0a 100644 --- a/src/commandhandler.cpp +++ b/src/commandhandler.cpp @@ -21,15 +21,13 @@ #include "commandhandler.h" -#include "beingmanager.h" +#include "actorspritemanager.h" #include "channelmanager.h" #include "channel.h" #include "game.h" #include "localplayer.h" #include "playerrelations.h" -#include "gui/chat.h" - #include "gui/widgets/channeltab.h" #include "gui/widgets/chattab.h" @@ -49,7 +47,8 @@ void CommandHandler::handleCommand(const std::string &command, ChatTab *tab) { std::string::size_type pos = command.find(' '); std::string type(command, 0, pos); - std::string args(command, pos == std::string::npos ? command.size() : pos + 1); + std::string args(command, pos == std::string::npos ? + command.size() : pos + 1); if (type == "help") // Do help before tabs so they can't override it { @@ -123,14 +122,14 @@ void CommandHandler::handleCommand(const std::string &command, ChatTab *tab) { handlePresent(args, tab); } - else if (type == "away") - { - handleAway(args, tab); - } else if (type == "showip" && Net::getNetworkType() == ServerInfo::TMWATHENA) { handleShowIp(args, tab); } + else if (type == "away") + { + handleAway(args, tab); + } else { tab->chatLog(_("Unknown command.")); @@ -394,7 +393,7 @@ void CommandHandler::handleMsg(const std::string &args, ChatTab *tab) if (tempNick.compare(playerName) == 0 || args.empty()) return; - chatWindow->whisper(recvnick, msg, true); + chatWindow->whisper(recvnick, msg, BY_PLAYER); } else tab->chatLog(_("Cannot send empty whispers!"), BY_SERVER); @@ -510,7 +509,7 @@ void CommandHandler::handleShowIp(const std::string &args, ChatTab *tab) return; } - beingManager->updatePlayerNames(); + actorSpriteManager->updatePlayerNames(); } void CommandHandler::handlePresent(const std::string &args, ChatTab *tab) diff --git a/src/compoundsprite.cpp b/src/compoundsprite.cpp new file mode 100644 index 00000000..ec45825f --- /dev/null +++ b/src/compoundsprite.cpp @@ -0,0 +1,364 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "compoundsprite.h" + +#include "game.h" +#include "graphics.h" +#include "map.h" + +#include "resources/image.h" + +#include <SDL.h> + +#define BUFFER_WIDTH 100 +#define BUFFER_HEIGHT 100 + +CompoundSprite::CompoundSprite(): + mImage(NULL), + mAlphaImage(NULL), + mNeedsRedraw(false) +{ + mAlpha = 1.0f; +} + +CompoundSprite::~CompoundSprite() +{ + SpriteIterator it, it_end; + for (it = begin(), it_end = end(); it != it_end; it++) + delete (*it); + + clear(); + + delete mImage; + delete mAlphaImage; +} + +bool CompoundSprite::reset() +{ + bool ret = false; + + SpriteIterator it, it_end; + for (it = begin(), it_end = end(); it != it_end; it++) + if (*it) + ret |= (*it)->reset(); + + mNeedsRedraw |= ret; + return ret; +} + +bool CompoundSprite::play(std::string action) +{ + bool ret = false; + + SpriteIterator it, it_end; + for (it = begin(), it_end = end(); it != it_end; it++) + if (*it) + ret |= (*it)->play(action); + + mNeedsRedraw |= ret; + return ret; +} + +bool CompoundSprite::update(int time) +{ + bool ret = false; + + SpriteIterator it, it_end; + for (it = begin(), it_end = end(); it != it_end; it++) + if (*it) + ret |= (*it)->update(time); + + mNeedsRedraw |= ret; + return ret; +} + +bool CompoundSprite::draw(Graphics* graphics, int posX, int posY) const +{ + if (mNeedsRedraw) + redraw(); + + if (mAlpha == 1.0f && mImage) + { + return graphics->drawImage(mImage, posX + mOffsetX, posY + mOffsetY); + } + else if (mAlpha && mAlphaImage) + { + if (mAlphaImage->getAlpha() != mAlpha) + mAlphaImage->setAlpha(mAlpha); + + return graphics->drawImage(mAlphaImage, + posX + mOffsetX, posY + mOffsetY); + } + else + { + SpriteConstIterator it, it_end; + for (it = begin(), it_end = end(); it != it_end; it++) + { + if (*it) + { + if ((*it)->getAlpha() != mAlpha) + (*it)->setAlpha(mAlpha); + (*it)->draw(graphics, posX, posY); + } + } + } + + return false; +} + +int CompoundSprite::getWidth() const +{ + Sprite *base = NULL; + + SpriteConstIterator it, it_end; + for (it = begin(), it_end = end(); it != it_end; it++) + if ((base = (*it))) + break; + + if (base) + return base->getWidth(); + + return 0; +} + +int CompoundSprite::getHeight() const +{ + Sprite *base = NULL; + + SpriteConstIterator it, it_end; + for (it = begin(), it_end = end(); it != it_end; it++) + if ((base = (*it))) + break; + + if (base) + return base->getHeight(); + + return 0; +} + +const Image* CompoundSprite::getImage() const +{ + return mImage; +} + +bool CompoundSprite::setDirection(SpriteDirection direction) +{ + bool ret = false; + + SpriteIterator it, it_end; + for (it = begin(), it_end = end(); it != it_end; it++) + if (*it) + ret |= (*it)->setDirection(direction); + + mNeedsRedraw |= ret; + return ret; +} + +int CompoundSprite::getNumberOfLayers() const +{ + if (mImage || mAlphaImage) + { + return 1; + } + else + { + return size(); + } +} + +size_t CompoundSprite::getCurrentFrame() const +{ + SpriteConstIterator it, it_end; + for (it = begin(), it_end = end(); it != it_end; it++) + if (*it) + return (*it)->getCurrentFrame(); + + return 0; +} + +size_t CompoundSprite::getFrameCount() const +{ + SpriteConstIterator it, it_end; + for (it = begin(), it_end = end(); it != it_end; it++) + if (*it) + return (*it)->getFrameCount(); + + return 0; +} + +void CompoundSprite::addSprite(Sprite* sprite) +{ + push_back(sprite); + mNeedsRedraw = true; +} + +void CompoundSprite::setSprite(int layer, Sprite* sprite) +{ + // Skip if it won't change anything + if (at(layer) == sprite) + return; + + if (at(layer)) + delete at(layer); + at(layer) = sprite; + mNeedsRedraw = true; +} + +void CompoundSprite::removeSprite(int layer) +{ + // Skip if it won't change anything + if (at(layer) == NULL) + return; + + delete at(layer); + at(layer) = NULL; + mNeedsRedraw = true; +} + +void CompoundSprite::clear() +{ + // Skip if it won't change anything + if (empty()) + return; + + std::vector<Sprite*>::clear(); + mNeedsRedraw = true; +} + +void CompoundSprite::ensureSize(size_t layerCount) +{ + // Skip if it won't change anything + if (size() >= layerCount) + return; + + resize(layerCount, NULL); + mNeedsRedraw = true; +} + +/** + * Returns the curent frame in the current animation of the given layer. + */ +size_t CompoundSprite::getCurrentFrame(size_t layer) +{ + if (layer >= size()) + return 0; + + Sprite *s = getSprite(layer); + if (s) + return s->getCurrentFrame(); + + return 0; +} + +/** + * Returns the frame count in the current animation of the given layer. + */ +size_t CompoundSprite::getFrameCount(size_t layer) +{ + if (layer >= size()) + return 0; + + Sprite *s = getSprite(layer); + if (s) + return s->getFrameCount(); + + return 0; +} + +void CompoundSprite::redraw() const +{ + // TODO OpenGL support + if (Image::getLoadAsOpenGL()) + { + mNeedsRedraw = false; + return; + } + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + int rmask = 0xff000000; + int gmask = 0x00ff0000; + int bmask = 0x0000ff00; + int amask = 0x000000ff; +#else + int rmask = 0x000000ff; + int gmask = 0x0000ff00; + int bmask = 0x00ff0000; + int amask = 0xff000000; +#endif + + SDL_Surface *surface = SDL_CreateRGBSurface(SDL_HWSURFACE, + BUFFER_WIDTH, BUFFER_HEIGHT, + 32, rmask, gmask, bmask, amask); + + if (!surface) + return; + + Graphics *graphics = new Graphics(); + graphics->setBlitMode(Graphics::BLIT_GFX); + graphics->setTarget(surface); + graphics->_beginDraw(); + + int tileX = 32 / 2; + int tileY = 32; + + Game *game = Game::instance(); + if (game) + { + Map *map = game->getCurrentMap(); + tileX = map->getTileWidth() / 2; + tileY = map->getTileWidth(); + } + + int posX = BUFFER_WIDTH / 2 - tileX; + int posY = BUFFER_HEIGHT - tileY; + + mOffsetX = tileX - BUFFER_WIDTH / 2; + mOffsetY = tileY - BUFFER_HEIGHT; + + SpriteConstIterator it, it_end; + for (it = begin(), it_end = end(); it != it_end; it++) + { + if (*it) + { + (*it)->draw(graphics, posX, posY); + } + } + + delete graphics; + + SDL_Surface *surfaceA = SDL_CreateRGBSurface(SDL_HWSURFACE, + BUFFER_WIDTH, BUFFER_HEIGHT, + 32, rmask, gmask, bmask, amask); + + SDL_SetAlpha(surface, 0, SDL_ALPHA_OPAQUE); + SDL_BlitSurface(surface, NULL, surfaceA, NULL); + + delete mImage; + delete mAlphaImage; + + mImage = Image::load(surface); + SDL_FreeSurface(surface); + + mAlphaImage = Image::load(surfaceA); + SDL_FreeSurface(surfaceA); + + mNeedsRedraw = false; +} diff --git a/src/compoundsprite.h b/src/compoundsprite.h new file mode 100644 index 00000000..3b443219 --- /dev/null +++ b/src/compoundsprite.h @@ -0,0 +1,105 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef COMPOUNDSPRITE_H +#define COMPOUNDSPRITE_H + +#include "sprite.h" + +#include <vector> + +class Image; + +class CompoundSprite : public Sprite, private std::vector<Sprite*> +{ +public: + CompoundSprite(); + + ~CompoundSprite(); + + virtual bool reset(); + + virtual bool play(std::string action); + + virtual bool update(int time); + + virtual bool draw(Graphics* graphics, int posX, int posY) const; + + /** + * Gets the width in pixels of the first sprite in the list. + */ + virtual int getWidth() const; + + /** + * Gets the height in pixels of the first sprite in the list. + */ + virtual int getHeight() const; + + virtual const Image* getImage() const; + + virtual bool setDirection(SpriteDirection direction); + + int getNumberOfLayers() const; + + size_t getCurrentFrame() const; + + size_t getFrameCount() const; + + size_t size() const + { return std::vector<Sprite*>::size(); } + + void addSprite(Sprite* sprite); + + void setSprite(int layer, Sprite* sprite); + + Sprite *getSprite(int layer) const + { return at(layer); } + + void removeSprite(int layer); + + void clear(); + + void ensureSize(size_t layerCount); + + /** + * Returns the curent frame in the current animation of the given layer. + */ + virtual size_t getCurrentFrame(size_t layer); + + /** + * Returns the frame count in the current animation of the given layer. + */ + virtual size_t getFrameCount(size_t layer); + +private: + typedef CompoundSprite::iterator SpriteIterator; + typedef CompoundSprite::const_iterator SpriteConstIterator; + + void redraw() const; + + mutable Image *mImage; + mutable Image *mAlphaImage; + + mutable int mOffsetX, mOffsetY; + + mutable bool mNeedsRedraw; +}; + +#endif // COMPOUNDSPRITE_H diff --git a/src/configuration.cpp b/src/configuration.cpp index d8b11034..44fb6e2e 100644 --- a/src/configuration.cpp +++ b/src/configuration.cpp @@ -21,7 +21,7 @@ #include "configuration.h" -#include "configlistener.h" +#include "event.h" #include "log.h" #include "utils/stringutils.h" @@ -38,15 +38,9 @@ void Configuration::setValue(const std::string &key, const std::string &value) ConfigurationObject::setValue(key, value); // Notify listeners - ListenerMapIterator list = mListenerMap.find(key); - if (list != mListenerMap.end()) - { - Listeners listeners = list->second; - for (ListenerIterator i = listeners.begin(); i != listeners.end(); i++) - { - (*i)->optionChanged(key); - } - } + Mana::Event event(EVENT_CONFIGOPTIONCHANGED); + event.setString("option", key); + event.trigger(CHANNEL_CONFIG); } std::string ConfigurationObject::getValue(const std::string &key, @@ -79,8 +73,11 @@ double ConfigurationObject::getValue(const std::string &key, void ConfigurationObject::deleteList(const std::string &name) { for (ConfigurationList::const_iterator - it = mContainerOptions[name].begin(); it != mContainerOptions[name].end(); it++) + it = mContainerOptions[name].begin(); + it != mContainerOptions[name].end(); it++) + { delete *it; + } mContainerOptions[name].clear(); } @@ -88,8 +85,11 @@ void ConfigurationObject::deleteList(const std::string &name) void ConfigurationObject::clear() { for (std::map<std::string, ConfigurationList>::const_iterator - it = mContainerOptions.begin(); it != mContainerOptions.end(); it++) + it = mContainerOptions.begin(); it != mContainerOptions.end(); it++) + { deleteList(it->first); + } + mOptions.clear(); } @@ -98,6 +98,131 @@ ConfigurationObject::~ConfigurationObject() clear(); } +void Configuration::cleanDefaults() +{ + if (mDefaultsData) + { + for (DefaultsData::const_iterator iter = mDefaultsData->begin(); + iter != mDefaultsData->end(); iter++) + { + if (iter->second) + delete(iter->second); + } + mDefaultsData->clear(); + delete mDefaultsData; + mDefaultsData = 0; + } +} + +Configuration::~Configuration() +{ + cleanDefaults(); +} + +void Configuration::setDefaultValues(DefaultsData *defaultsData) +{ + cleanDefaults(); + mDefaultsData = defaultsData; +} + +Mana::VariableData* Configuration::getDefault(const std::string &key, + Mana::VariableData::DataType type + ) const +{ + if (mDefaultsData) + { + DefaultsData::const_iterator itdef = mDefaultsData->find(key); + + if (itdef != mDefaultsData->end() && itdef->second + && itdef->second->getType() == type) + { + return itdef->second; + } + else + { + logger->log("%s: No value in registry for key %s", + mConfigPath.c_str(), key.c_str()); + } + } + return NULL; +} + +int Configuration::getIntValue(const std::string &key) const +{ + int defaultValue = 0; + Options::const_iterator iter = mOptions.find(key); + if (iter == mOptions.end()) + { + Mana::VariableData* vd = getDefault(key, Mana::VariableData::DATA_INT); + if (vd) + defaultValue = ((Mana::IntData*)vd)->getData(); + } + else + { + defaultValue = atoi(iter->second.c_str()); + } + return defaultValue; +} + +std::string Configuration::getStringValue(const std::string &key) const +{ + std::string defaultValue = ""; + Options::const_iterator iter = mOptions.find(key); + if (iter == mOptions.end()) + { + Mana::VariableData* vd = getDefault(key, + Mana::VariableData::DATA_STRING); + + if (vd) + defaultValue = ((Mana::StringData*)vd)->getData(); + } + else + { + defaultValue = iter->second; + } + return defaultValue; +} + + +float Configuration::getFloatValue(const std::string &key) const +{ + float defaultValue = 0.0f; + Options::const_iterator iter = mOptions.find(key); + if (iter == mOptions.end()) + { + Mana::VariableData* vd = getDefault(key, + Mana::VariableData::DATA_FLOAT); + + if (vd) + defaultValue = ((Mana::FloatData*)vd)->getData(); + } + else + { + defaultValue = atof(iter->second.c_str()); + } + return defaultValue; +} + +bool Configuration::getBoolValue(const std::string &key) const +{ + bool defaultValue = false; + Options::const_iterator iter = mOptions.find(key); + if (iter == mOptions.end()) + { + Mana::VariableData* vd = getDefault(key, + Mana::VariableData::DATA_BOOL); + + if (vd) + defaultValue = ((Mana::BoolData*)vd)->getData(); + } + else + { + return getBoolFromString(iter->second, defaultValue); + } + + return defaultValue; +} + void ConfigurationObject::initFromXML(xmlNodePtr parent_node) { clear(); @@ -106,8 +231,7 @@ void ConfigurationObject::initFromXML(xmlNodePtr parent_node) { if (xmlStrEqual(node->name, BAD_CAST "list")) { - // list option handling - + // List option handling. std::string name = XML::getProperty(node, "name", std::string()); for_each_xml_child_node(subnode, node) @@ -117,7 +241,7 @@ void ConfigurationObject::initFromXML(xmlNodePtr parent_node) { ConfigurationObject *cobj = new ConfigurationObject; - cobj->initFromXML(subnode); // recurse + cobj->initFromXML(subnode); // Recurse mContainerOptions[name].push_back(cobj); } @@ -126,14 +250,13 @@ void ConfigurationObject::initFromXML(xmlNodePtr parent_node) } else if (xmlStrEqual(node->name, BAD_CAST "option")) { - // single option handling - + // Single option handling. std::string name = XML::getProperty(node, "name", std::string()); std::string value = XML::getProperty(node, "value", std::string()); if (!name.empty()) mOptions[name] = value; - } // otherwise ignore + } // Otherwise ignore } } @@ -177,16 +300,16 @@ void ConfigurationObject::writeToXML(xmlTextWriterPtr writer) } for (std::map<std::string, ConfigurationList>::const_iterator - it = mContainerOptions.begin(); it != mContainerOptions.end(); it++) + it = mContainerOptions.begin(); it != mContainerOptions.end(); it++) { const char *name = it->first.c_str(); xmlTextWriterStartElement(writer, BAD_CAST "list"); xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST name); - // recurse on all elements + // Recurse on all elements for (ConfigurationList::const_iterator - elt_it = it->second.begin(); elt_it != it->second.end(); elt_it++) + elt_it = it->second.begin(); elt_it != it->second.end(); elt_it++) { xmlTextWriterStartElement(writer, BAD_CAST name); (*elt_it)->writeToXML(writer); @@ -231,15 +354,3 @@ void Configuration::write() xmlTextWriterEndDocument(writer); xmlFreeTextWriter(writer); } - -void Configuration::addListener( - const std::string &key, ConfigListener *listener) -{ - mListenerMap[key].push_front(listener); -} - -void Configuration::removeListener( - const std::string &key, ConfigListener *listener) -{ - mListenerMap[key].remove(listener); -} diff --git a/src/configuration.h b/src/configuration.h index 908d13a4..a46b0718 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -23,6 +23,7 @@ #define CONFIGURATION_H #include "utils/stringutils.h" +#include "defaults.h" #include <libxml/xmlwriter.h> @@ -31,7 +32,6 @@ #include <map> #include <string> -class ConfigListener; class ConfigurationObject; /** @@ -192,7 +192,7 @@ class ConfigurationObject class Configuration : public ConfigurationObject { public: - virtual ~Configuration() {} + ~Configuration(); /** * Reads config file and parse all options into memory. @@ -203,20 +203,16 @@ class Configuration : public ConfigurationObject void init(const std::string &filename, bool useResManager = false); /** - * Writes the current settings back to the config file. - */ - void write(); - - /** - * Adds a listener to the listen list of the specified config option. + * Set the default values for each keys. + * + * @param defaultsData data used as defaults. */ - void addListener(const std::string &key, ConfigListener *listener); + void setDefaultValues(DefaultsData *defaultsData); /** - * Removes a listener from the listen list of the specified config - * option. + * Writes the current settings back to the config file. */ - void removeListener(const std::string &key, ConfigListener *listener); + void write(); void setValue(const std::string &key, const std::string &value); @@ -238,14 +234,29 @@ class Configuration : public ConfigurationObject inline void setValue(const std::string &key, bool value) { setValue(key, value ? "1" : "0"); } + /** + * returns a value corresponding to the given key. + * The default value returned in based on fallbacks registry. + * @see defaults.h + */ + int getIntValue(const std::string &key) const; + + float getFloatValue(const std::string &key) const; + + std::string getStringValue(const std::string &key) const; + + bool getBoolValue(const std::string &key) const; + + Mana::VariableData* getDefault(const std::string &key, + Mana::VariableData::DataType type) const; private: - typedef std::list<ConfigListener*> Listeners; - typedef Listeners::iterator ListenerIterator; - typedef std::map<std::string, Listeners> ListenerMap; - typedef ListenerMap::iterator ListenerMapIterator; - ListenerMap mListenerMap; + /** + * Clean up the default values member. + */ + void cleanDefaults(); - std::string mConfigPath; /**< Location of config file */ + std::string mConfigPath; /**< Location of config file */ + DefaultsData *mDefaultsData; /**< Defaults of value for a given key */ }; extern Configuration branding; diff --git a/src/defaults.cpp b/src/defaults.cpp new file mode 100644 index 00000000..e0e48d60 --- /dev/null +++ b/src/defaults.cpp @@ -0,0 +1,181 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "defaults.h" + +#include "being.h" +#include "graphics.h" +#include "client.h" + +#include <stdlib.h> + +using namespace Mana; + +VariableData* createData(int defData) +{ + return new IntData(defData); +} + +VariableData* createData(double defData) +{ + return new FloatData(defData); +} + +VariableData* createData(float defData) +{ + return new FloatData(defData); +} + +VariableData* createData(const std::string &defData) +{ + return new StringData(defData); +} + +VariableData* createData(const char* defData) +{ + return new StringData(defData); +} + +VariableData* createData(bool defData) +{ + return new BoolData(defData); +} + +#define AddDEF(defaultsData, key, value) \ + defaultsData->insert(std::pair<std::string, VariableData*> \ + (key, createData(value))); + + +DefaultsData* getConfigDefaults() +{ + DefaultsData* configData = new DefaultsData; + // Init main config defaults + AddDEF(configData, "OverlayDetail", 2); + AddDEF(configData, "speechBubblecolor", "000000"); + AddDEF(configData, "speechBubbleAlpha", 1.0f); + AddDEF(configData, "MostUsedServerName0", "server.themanaworld.org"); + AddDEF(configData, "visiblenames", true); + AddDEF(configData, "speech", Being::TEXT_OVERHEAD); + AddDEF(configData, "showgender", false); + AddDEF(configData, "showMonstersTakedDamage", false); + AddDEF(configData, "particleMaxCount", 3000); + AddDEF(configData, "particleFastPhysics", 0); + AddDEF(configData, "particleEmitterSkip", 1); + AddDEF(configData, "particleeffects", true); + AddDEF(configData, "logToStandardOut", false); + AddDEF(configData, "opengl", false); + AddDEF(configData, "screenwidth", defaultScreenWidth); + AddDEF(configData, "screenheight", defaultScreenHeight); + AddDEF(configData, "screen", false); + AddDEF(configData, "hwaccel", false); + AddDEF(configData, "sound", false); + AddDEF(configData, "sfxVolume", 100); + AddDEF(configData, "musicVolume", 60); + AddDEF(configData, "remember", false); + AddDEF(configData, "username", ""); + AddDEF(configData, "lastCharacter", ""); + AddDEF(configData, "fpslimit", 60); + AddDEF(configData, "updatehost", ""); + AddDEF(configData, "screenshotDirectory", ""); + AddDEF(configData, "useScreenshotDirectorySuffix", true); + AddDEF(configData, "screenshotDirectorySuffix", ""); + AddDEF(configData, "EnableSync", false); + AddDEF(configData, "joystickEnabled", false); + AddDEF(configData, "upTolerance", 100); + AddDEF(configData, "downTolerance", 100); + AddDEF(configData, "leftTolerance", 100); + AddDEF(configData, "rightTolerance", 100); + AddDEF(configData, "logNpcInGui", true); + AddDEF(configData, "download-music", false); + AddDEF(configData, "guialpha", 0.8f); + AddDEF(configData, "ChatLogLength", 0); + AddDEF(configData, "enableChatLog", false); + AddDEF(configData, "whispertab", false); + AddDEF(configData, "customcursor", true); + AddDEF(configData, "showownname", false); + AddDEF(configData, "showpickupparticle", false); + AddDEF(configData, "showpickupchat", true); + AddDEF(configData, "fontSize", 11); + AddDEF(configData, "ReturnToggles", false); + AddDEF(configData, "ScrollLaziness", 16); + AddDEF(configData, "ScrollRadius", 0); + AddDEF(configData, "ScrollCenterOffsetX", 0); + AddDEF(configData, "ScrollCenterOffsetY", 0); + AddDEF(configData, "onlineServerList", ""); + AddDEF(configData, "theme", ""); + AddDEF(configData, "disableTransparency", false); + + return configData; +} + +DefaultsData* getBrandingDefaults() +{ + DefaultsData* brandingData = new DefaultsData; + // Init config defaults + AddDEF(brandingData, "wallpapersPath", ""); + AddDEF(brandingData, "wallpapersFile", ""); + AddDEF(brandingData, "appName", "Mana"); + AddDEF(brandingData, "appIcon", "icons/mana"); + AddDEF(brandingData, "loginMusic", "Magick - Real.ogg"); + AddDEF(brandingData, "defaultServer", ""); + AddDEF(brandingData, "defaultPort", DEFAULT_PORT); + AddDEF(brandingData, "defaultServerType", "tmwathena"); + AddDEF(brandingData, "onlineServerList", "a"); + AddDEF(brandingData, "appShort", "mana"); + AddDEF(brandingData, "defaultUpdateHost", ""); + AddDEF(brandingData, "helpPath", ""); + AddDEF(brandingData, "onlineServerList", ""); + AddDEF(brandingData, "guiThemePath", ""); + AddDEF(brandingData, "theme", ""); + AddDEF(brandingData, "font", "fonts/dejavusans.ttf"); + AddDEF(brandingData, "boldFont", "fonts/dejavusans-bold.ttf"); + + return brandingData; +} + +DefaultsData* getPathsDefaults() +{ + DefaultsData *pathsData = new DefaultsData; + // Init paths.xml defaults + AddDEF(pathsData, "itemIcons", "graphics/items/"); + AddDEF(pathsData, "unknownItemFile", "unknown-item.png"); + AddDEF(pathsData, "sprites", "graphics/sprites/"); + AddDEF(pathsData, "spriteErrorFile", "error.xml"); + + AddDEF(pathsData, "particles", "graphics/particles/"); + AddDEF(pathsData, "levelUpEffectFile", "levelup.particle.xml"); + AddDEF(pathsData, "portalEffectFile", "warparea.particle.xml"); + + AddDEF(pathsData, "minimaps", "graphics/minimaps/"); + AddDEF(pathsData, "maps", "maps/"); + + AddDEF(pathsData, "sfx", "sfx/"); + AddDEF(pathsData, "attackSfxFile", "fist-swish.ogg"); + AddDEF(pathsData, "music", "music/"); + + AddDEF(pathsData, "wallpapers", "graphics/images/"); + AddDEF(pathsData, "wallpaperFile", "login_wallpaper.png"); + + AddDEF(pathsData, "help", "help/"); + + return pathsData; +} + +#undef AddDEF diff --git a/src/configlistener.h b/src/defaults.h index 923b3115..b9dfa511 100644 --- a/src/configlistener.h +++ b/src/defaults.h @@ -1,7 +1,6 @@ /* * The Mana Client - * Copyright (C) 2004-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2010 The Mana Developers * * This file is part of The Mana Client. * @@ -19,30 +18,17 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef CONFIGLISTENER_H -#define CONFIGLISTENER_H +#ifndef DEFAULTS_H +#define DEFAULTS_H +#include <map> #include <string> +#include "variabledata.h" -/** - * The listener interface for receiving notifications about changes to - * configuration options. - * - * \ingroup CORE - */ -class ConfigListener -{ - public: - /** - * Destructor. - */ - virtual ~ConfigListener() {} +typedef std::map<std::string, Mana::VariableData*> DefaultsData; - /** - * Called when an option changed. The config listener will have to be - * registered to the option name first. - */ - virtual void optionChanged(const std::string &name) = 0; -}; +DefaultsData* getConfigDefaults(); +DefaultsData* getBrandingDefaults(); +DefaultsData* getPathsDefaults(); #endif diff --git a/src/equipment.h b/src/equipment.h index 6c099324..0aa10fb0 100644 --- a/src/equipment.h +++ b/src/equipment.h @@ -39,22 +39,6 @@ class Equipment */ ~Equipment() { mBackend = 0; } - enum Slot - { - EQUIP_TORSO_SLOT = 0, - EQUIP_GLOVES_SLOT = 1, - EQUIP_HEAD_SLOT = 2, - EQUIP_LEGS_SLOT = 3, - EQUIP_FEET_SLOT = 4, - EQUIP_RING1_SLOT = 5, - EQUIP_RING2_SLOT = 6, - EQUIP_NECK_SLOT = 7, - EQUIP_FIGHT1_SLOT = 8, - EQUIP_FIGHT2_SLOT = 9, - EQUIP_PROJECTILE_SLOT = 10, - EQUIP_VECTOREND - }; - class Backend { public: virtual Item *getEquipment(int index) const = 0; diff --git a/src/event.cpp b/src/event.cpp new file mode 100644 index 00000000..c8de2ffd --- /dev/null +++ b/src/event.cpp @@ -0,0 +1,255 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "event.h" + +#include "listener.h" +#include "variabledata.h" + +namespace Mana +{ + +ListenMap Event::mBindings; + +Event::~Event() +{ + VariableMap::iterator it = mData.begin(); + while (it != mData.end()) + { + delete it->second; + it++; + } +} + +// Integers + +void Event::setInt(const std::string &key, int value) throw (BadEvent) +{ + if (mData.find(key) != mData.end()) + throw KEY_ALREADY_EXISTS; + + mData[key] = new IntData(value); +} + +int Event::getInt(const std::string &key) const throw (BadEvent) +{ + VariableMap::const_iterator it = mData.find(key); + if (it == mData.end()) + throw BAD_KEY; + + if (it->second->getType() != VariableData::DATA_INT) + throw BAD_VALUE; + + return static_cast<IntData *>(it->second)->getData(); +} + +bool Event::hasInt(const std::string &key) const +{ + VariableMap::const_iterator it = mData.find(key); + return !(it == mData.end() + || it->second->getType() != VariableData::DATA_INT); +} + +// Strings + +void Event::setString(const std::string &key, const std::string &value) throw (BadEvent) +{ + if (mData.find(key) != mData.end()) + throw KEY_ALREADY_EXISTS; + + mData[key] = new StringData(value); +} + +const std::string &Event::getString(const std::string &key) const throw (BadEvent) +{ + VariableMap::const_iterator it = mData.find(key); + if (it == mData.end()) + throw BAD_KEY; + + if (it->second->getType() != VariableData::DATA_STRING) + throw BAD_VALUE; + + return static_cast<StringData *>(it->second)->getData(); +} + + +bool Event::hasString(const std::string &key) const +{ + VariableMap::const_iterator it = mData.find(key); + return !(it == mData.end() + || it->second->getType() != VariableData::DATA_STRING); +} + +// Floats + +void Event::setFloat(const std::string &key, double value) throw (BadEvent) +{ + if (mData.find(key) != mData.end()) + throw KEY_ALREADY_EXISTS; + + mData[key] = new FloatData(value); +} + +double Event::getFloat(const std::string &key) const throw (BadEvent) +{ + VariableMap::const_iterator it = mData.find(key); + if (it == mData.end()) + throw BAD_KEY; + + if (it->second->getType() != VariableData::DATA_FLOAT) + throw BAD_VALUE; + + return static_cast<FloatData *>(it->second)->getData(); +} + +bool Event::hasFloat(const std::string &key) const +{ + VariableMap::const_iterator it = mData.find(key); + return !(it == mData.end() + || it->second->getType() != VariableData::DATA_FLOAT); +} + +// Booleans + +void Event::setBool(const std::string &key, bool value) throw (BadEvent) +{ + if (mData.find(key) != mData.end()) + throw KEY_ALREADY_EXISTS; + + mData[key] = new BoolData(value); +} + +bool Event::getBool(const std::string &key) const throw (BadEvent) +{ + VariableMap::const_iterator it = mData.find(key); + if (it == mData.end()) + throw BAD_KEY; + + if (it->second->getType() != VariableData::DATA_BOOL) + throw BAD_VALUE; + + return static_cast<BoolData *>(it->second)->getData(); +} + +bool Event::hasBool(const std::string &key) const +{ + VariableMap::const_iterator it = mData.find(key); + return !(it == mData.end() + || it->second->getType() != VariableData::DATA_BOOL); +} + +// Items + +void Event::setItem(const std::string &key, Item *value) throw (BadEvent) +{ + if (mData.find(key) != mData.end()) + throw KEY_ALREADY_EXISTS; + + mData[key] = new ItemData(value); +} + +Item *Event::getItem(const std::string &key) const throw (BadEvent) +{ + VariableMap::const_iterator it = mData.find(key); + if (it == mData.end()) + throw BAD_KEY; + + if (it->second->getType() != VariableData::DATA_ITEM) + throw BAD_VALUE; + + return static_cast<ItemData *>(it->second)->getData(); +} + +bool Event::hasItem(const std::string &key) const +{ + VariableMap::const_iterator it = mData.find(key); + return !(it == mData.end() + || it->second->getType() != VariableData::DATA_ITEM); +} + +// Actors + +void Event::setActor(const std::string &key, ActorSprite *value) throw (BadEvent) +{ + if (mData.find(key) != mData.end()) + throw KEY_ALREADY_EXISTS; + + mData[key] = new ActorData(value); +} + +ActorSprite *Event::getActor(const std::string &key) const throw (BadEvent) +{ + VariableMap::const_iterator it = mData.find(key); + if (it == mData.end()) + throw BAD_KEY; + + if (it->second->getType() != VariableData::DATA_ACTOR) + throw BAD_VALUE; + + return static_cast<ActorData *>(it->second)->getData(); +} + +bool Event::hasActor(const std::string &key) const +{ + VariableMap::const_iterator it = mData.find(key); + return !(it == mData.end() + || it->second->getType() != VariableData::DATA_ACTOR); +} + +// Triggers + +void Event::trigger(Channels channel, const Event &event) +{ + ListenMap::iterator it = mBindings.find(channel); + + // Make sure something is listening + if (it == mBindings.end()) + return; + + // Loop though all listeners + ListenerSet::iterator lit = it->second.begin(); + while (lit != it->second.end()) + { + (*lit)->event(channel, event); + lit++; + } +} + +void Event::bind(Listener *listener, Channels channel) +{ + mBindings[channel].insert(listener); +} + +void Event::unbind(Listener *listener, Channels channel) +{ + mBindings[channel].erase(listener); +} + +void Event::remove(Listener *listener) +{ + ListenMap::iterator it = mBindings.begin(); + while (it != mBindings.end()) + { + it->second.erase(listener); + it++; + } +} + +} // namespace Mana diff --git a/src/event.h b/src/event.h new file mode 100644 index 00000000..758d6b34 --- /dev/null +++ b/src/event.h @@ -0,0 +1,339 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef EVENT_H +#define EVENT_H + +#include <map> +#include <set> +#include <string> + +class ActorSprite; +class Item; + +enum Channels +{ + CHANNEL_ACTORSPRITE, + CHANNEL_ATTRIBUTES, + CHANNEL_BUYSELL, + CHANNEL_CHAT, + CHANNEL_CLIENT, + CHANNEL_CONFIG, + CHANNEL_GAME, + CHANNEL_ITEM, + CHANNEL_NOTICES, + CHANNEL_NPC, + CHANNEL_STATUS, + CHANNEL_STORAGE +}; + +enum Events +{ + EVENT_ANNOUNCEMENT, + EVENT_BEING, + EVENT_CLOSE, + EVENT_CLOSEALL, + EVENT_CLOSESENT, + EVENT_CONFIGOPTIONCHANGED, + EVENT_CONSTRUCTED, + EVENT_DBSLOADING, + EVENT_DESTROYED, + EVENT_DESTRUCTED, + EVENT_DESTRUCTING, + EVENT_DOCLOSEINVENTORY, + EVENT_DODROP, + EVENT_DOEQUIP, + EVENT_DOMOVE, + EVENT_DOSPLIT, + EVENT_DOUNEQUIP, + EVENT_DOUSE, + EVENT_END, + EVENT_ENGINESINITALIZED, + EVENT_ENGINESINITALIZING, + EVENT_GUIWINDOWSLOADED, + EVENT_GUIWINDOWSLOADING, + EVENT_GUIWINDOWSUNLOADED, + EVENT_GUIWINDOWSUNLOADING, + EVENT_INTEGERINPUT, + EVENT_INTEGERINPUTSENT, + EVENT_MAPLOADED, + EVENT_MENU, + EVENT_MENUSENT, + EVENT_MESSAGE, + EVENT_NEXT, + EVENT_NEXTSENT, + EVENT_NPCCOUNT, + EVENT_PLAYER, + EVENT_POST, + EVENT_POSTCOUNT, + EVENT_SENDLETTERSENT, + EVENT_SERVERNOTICE, + EVENT_STATECHANGE, + EVENT_STORAGECOUNT, + EVENT_STRINGINPUT, + EVENT_STRINGINPUTSENT, + EVENT_STUN, + EVENT_TALKSENT, + EVENT_TRADING, + EVENT_UPDATEATTRIBUTE, + EVENT_UPDATESTAT, + EVENT_UPDATESTATUSEFFECT, + EVENT_WHISPER, + EVENT_WHISPERERROR +}; + +namespace Mana +{ + +// Possible exception that can be thrown +enum BadEvent { + BAD_KEY, + BAD_VALUE, + KEY_ALREADY_EXISTS +}; + +class Listener; + +typedef std::set<Listener *> ListenerSet; +typedef std::map<Channels, ListenerSet > ListenMap; + +class VariableData; +typedef std::map<std::string, VariableData *> VariableMap; + +#define SERVER_NOTICE(message) { \ +Mana::Event event(EVENT_SERVERNOTICE); \ +event.setString("message", message); \ +event.trigger(CHANNEL_NOTICES, event); } + +class Event +{ +public: + /** + * Makes an event with the given name. + */ + Event(Events name) + { mEventName = name; } + + ~Event(); + + /** + * Returns the name of the event. + */ + Events getName() const + { return mEventName; } + +// Integers + + /** + * Sets the given variable to the given integer, if it isn't already set. + */ + void setInt(const std::string &key, int value) throw (BadEvent); + + /** + * Returns the given variable if it is set and an integer. + */ + int getInt(const std::string &key) const throw (BadEvent); + + /** + * Returns the given variable if it is set and an integer, returning the + * given default otherwise. + */ + inline int getInt(const std::string &key, int defaultValue) const + { try { return getInt(key); } catch (BadEvent) { return defaultValue; }} + + /** + * Returns true if the given variable exists and is an integer. + */ + bool hasInt(const std::string &key) const; + +// Strings + + /** + * Sets the given variable to the given string, if it isn't already set. + */ + void setString(const std::string &key, const std::string &value) throw (BadEvent); + + /** + * Returns the given variable if it is set and a string. + */ + const std::string &getString(const std::string &key) const throw (BadEvent); + + /** + * Returns the given variable if it is set and a string, returning the + * given default otherwise. + */ + inline std::string getString(const std::string &key, + const std::string &defaultValue) const + { try { return getString(key); } catch (BadEvent) { return defaultValue; }} + + /** + * Returns true if the given variable exists and is a string. + */ + bool hasString(const std::string &key) const; + +// Floats + + /** + * Sets the given variable to the given floating-point, if it isn't already + * set. + */ + void setFloat(const std::string &key, double value) throw (BadEvent); + + /** + * Returns the given variable if it is set and a floating-point. + */ + double getFloat(const std::string &key) const throw (BadEvent); + + /** + * Returns the given variable if it is set and a floating-point, returning + * the given default otherwise. + */ + inline double getFloat(const std::string &key, float defaultValue) const + { try { return getFloat(key); } catch (BadEvent) { return defaultValue; }} + + /** + * Returns true if the given variable exists and is a floating-point. + */ + bool hasFloat(const std::string &key) const; + +// Booleans + + /** + * Sets the given variable to the given boolean, if it isn't already set. + */ + void setBool(const std::string &key, bool value) throw (BadEvent); + + /** + * Returns the given variable if it is set and a boolean. + */ + bool getBool(const std::string &key) const throw (BadEvent); + + /** + * Returns the given variable if it is set and a boolean, returning the + * given default otherwise. + */ + inline bool getBool(const std::string &key, bool defaultValue) const + { try { return getBool(key); } catch (BadEvent) { return defaultValue; }} + + /** + * Returns true if the given variable exists and is a boolean. + */ + bool hasBool(const std::string &key) const; + +// Items + + /** + * Sets the given variable to the given Item, if it isn't already set. + */ + void setItem(const std::string &key, Item *value) throw (BadEvent); + + /** + * Returns the given variable if it is set and an Item. + */ + Item *getItem(const std::string &key) const throw (BadEvent); + + /** + * Returns the given variable if it is set and an Item, returning the + * given default otherwise. + */ + inline Item *getItem(const std::string &key, Item *defaultValue) const + { try { return getItem(key); } catch (BadEvent) { return defaultValue; }} + + /** + * Returns true if the given variable exists and is an Item. + */ + bool hasItem(const std::string &key) const; + +// ActorSprites + + /** + * Sets the given variable to the given actor, if it isn't already set. + */ + void setActor(const std::string &key, ActorSprite *value) throw (BadEvent); + + /** + * Returns the given variable if it is set and an actor. + */ + ActorSprite *getActor(const std::string &key) const throw (BadEvent); + + /** + * Returns the given variable if it is set and an actor, returning the + * given default otherwise. + */ + inline ActorSprite *getActor(const std::string &key, + ActorSprite *defaultValue) const + { try { return getActor(key); } catch (BadEvent) { return defaultValue; }} + + /** + * Returns true if the given variable exists and is an actor. + */ + bool hasActor(const std::string &key) const; + +// Triggers + + /** + * Sends this event to all classes listening to the given channel. + */ + inline void trigger(Channels channel) const + { trigger(channel, *this); } + + /** + * Sends the given event to all classes listening to the given channel. + */ + static void trigger(Channels channel, const Event &event); + + /** + * Sends an empty event with the given name to all classes listening to the + * given channel. + */ + static inline void trigger(Channels channel, Events name) + { trigger(channel, Mana::Event(name)); } + +protected: + friend class Listener; + + /** + * Binds the given listener to the given channel. The listener will receive + * all events triggered on the channel. + */ + static void bind(Listener *listener, Channels channel); + + /** + * Unbinds the given listener from the given channel. The listener will no + * longer receive any events from the channel. + */ + static void unbind(Listener *listener, Channels channel); + + /** + * Unbinds the given listener from all channels. + */ + static void remove(Listener *listener); + +private: + static ListenMap mBindings; + + Events mEventName; + + VariableMap mData; +}; + +} // namespace Mana + +#endif diff --git a/src/flooritem.cpp b/src/flooritem.cpp index c3442a86..db62ce9a 100644 --- a/src/flooritem.cpp +++ b/src/flooritem.cpp @@ -21,59 +21,34 @@ #include "flooritem.h" -#include "graphics.h" -#include "item.h" -#include "map.h" +#include "net/net.h" -#include "resources/image.h" +#include "resources/itemdb.h" +#include "resources/iteminfo.h" FloorItem::FloorItem(int id, int itemId, int x, int y, Map *map): - mId(id), + ActorSprite(id), + mItemId(itemId), mX(x), - mY(y), - mMap(map), - mAlpha(1.0f) + mY(y) { - // Create a corresponding item instance - mItem = new Item(itemId); + setMap(map); - // Add ourselves to the map - mMapSprite = mMap->addSprite(this); -} - -FloorItem::~FloorItem() -{ - // Remove ourselves from the map - mMap->removeSprite(mMapSprite); + // TODO: Eventually, we probably should fix all sprite offsets so that + // these translations aren't necessary anymore. The sprites know + // best where their base point should be. + mPos.x = x * map->getTileWidth() + 16; + mPos.y = y * map->getTileHeight() + + ((Net::getNetworkType() == ServerInfo::MANASERV) ? 15 : 32); - delete mItem; -} - -int FloorItem::getItemId() const -{ - return mItem->getId(); + setupSpriteDisplay(itemDb->get(itemId).getDisplay()); } -Item *FloorItem::getItem() const +const ItemInfo &FloorItem::getInfo() const { - return mItem; -} - -void FloorItem::draw(Graphics *graphics, int offsetX, int offsetY) const -{ - if (mItem) - { - Image *image = mItem->getDrawImage(); - - if (image) - if (mAlpha != image->getAlpha()) - image->setAlpha(mAlpha); - - graphics->drawImage(image, mX * mMap->getTileWidth() + offsetX, - mY * mMap->getTileHeight() + offsetY); - } + return itemDb->get(mItemId); } diff --git a/src/flooritem.h b/src/flooritem.h index ec8c37cd..e599c939 100644 --- a/src/flooritem.h +++ b/src/flooritem.h @@ -22,19 +22,14 @@ #ifndef FLOORITEM_H #define FLOORITEM_H -#include "map.h" -#include "sprite.h" +#include "actorsprite.h" -#include <list> - -class Graphics; -class Image; -class Item; +class ItemInfo; /** * An item lying on the floor. */ -class FloorItem : public Sprite +class FloorItem : public ActorSprite { public: /** @@ -52,72 +47,29 @@ class FloorItem : public Sprite int y, Map *map); - ~FloorItem(); - - /** - * Returns instance ID of this item. - */ - int getId() const { return mId; } + Type getType() const { return FLOOR_ITEM; } /** * Returns the item ID. */ - int getItemId() const; - - /** - * Returns the item object. Useful for adding an item link for the - * floor item to chat. - */ - Item *getItem() const; - - /** - * Returns the x coordinate in tiles. - */ - int getX() const { return mX; } + int getItemId() const + { return mItemId; } /** - * Returns the y coordinate in tiles. + * Returns the item info for this floor item. Useful for adding an item + * link for the floor item to chat. */ - int getY() const { return mY; } + const ItemInfo &getInfo() const; - /** - * Returns the pixel y coordinate. - * - * @see Sprite::getPixelY() - */ - int getPixelY() const - { return mY * mMap->getTileHeight() + mMap->getTileHeight() / 2; } - - /** - * Draws this floor item to the given graphics context. - * - * @see Sprite::draw(Graphics, int, int) - */ - void draw(Graphics *graphics, int offsetX, int offsetY) const; - - /** - * Sets the alpha value of the floor item - */ - void setAlpha(float alpha) - { mAlpha = alpha; } - - /** - * Returns the current alpha opacity of the floor item. - */ - virtual float getAlpha() const - { return mAlpha; } + virtual int getTileX() const + { return mX; } - /** We consider flooritems (at least for now) to be one layer-sprites */ - virtual int getNumberOfLayers() const - { return 1; } + virtual int getTileY() const + { return mY; } private: - int mId; + int mItemId; int mX, mY; - Item *mItem; - MapSprite mMapSprite; - Map *mMap; - float mAlpha; }; #endif diff --git a/src/flooritemmanager.cpp b/src/flooritemmanager.cpp deleted file mode 100644 index a190a168..00000000 --- a/src/flooritemmanager.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * The Mana Client - * Copyright (C) 2004-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * - * This file is part of The Mana Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "flooritemmanager.h" -#include "flooritem.h" - -#include "game.h" - -#include "utils/dtor.h" - -FloorItemManager::~FloorItemManager() -{ - clear(); -} - -FloorItem *FloorItemManager::create(int id, int itemId, int x, int y) -{ - Map *map = Game::instance()->getCurrentMap(); - FloorItem *floorItem = new FloorItem(id, itemId, x, y, map); - mFloorItems.push_back(floorItem); - return floorItem; -} - -void FloorItemManager::destroy(FloorItem *item) -{ - mFloorItems.remove(item); - delete item; -} - -void FloorItemManager::clear() -{ - delete_all(mFloorItems); - mFloorItems.clear(); -} - -FloorItem *FloorItemManager::findById(int id) const -{ - FloorItems::const_iterator i; - for (i = mFloorItems.begin(); i != mFloorItems.end(); i++) - { - if ((*i)->getId() == id) - { - return *i; - } - } - - return NULL; -} - -FloorItem *FloorItemManager::findByCoordinates(int x, int y) const -{ - FloorItems::const_iterator i; - for (i = mFloorItems.begin(); i != mFloorItems.end(); i++) - { - if ((*i)->getX() == x && (*i)->getY() == y) - { - return *i; - } - } - - return NULL; -} diff --git a/src/flooritemmanager.h b/src/flooritemmanager.h deleted file mode 100644 index 62ca8dc2..00000000 --- a/src/flooritemmanager.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * The Mana Client - * Copyright (C) 2004-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * - * This file is part of The Mana Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef FLOORITEMMANAGER_H -#define FLOORITEMMANAGER_H - -#include <list> - -class FloorItem; -class Map; - -class FloorItemManager -{ - public: - ~FloorItemManager(); - - FloorItem *create(int id, int itemId, int x, int y); - - void destroy(FloorItem *item); - - void clear(); - - FloorItem *findById(int id) const; - FloorItem *findByCoordinates(int x, int y) const; - - private: - typedef std::list<FloorItem*> FloorItems; - typedef FloorItems::iterator FloorItemIterator; - FloorItems mFloorItems; - -}; - -// TODO Get rid of the global? -extern FloorItemManager *floorItemManager; - -#endif diff --git a/src/game.cpp b/src/game.cpp index 512b8b5f..fd453434 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -21,14 +21,15 @@ #include "game.h" -#include "beingmanager.h" +#include "actorspritemanager.h" +#include "actorsprite.h" #include "channelmanager.h" #include "client.h" #include "commandhandler.h" #include "configuration.h" #include "effectmanager.h" +#include "event.h" #include "emoteshortcut.h" -#include "flooritemmanager.h" #include "graphics.h" #include "itemshortcut.h" #include "joystick.h" @@ -36,7 +37,6 @@ #include "localplayer.h" #include "log.h" #include "map.h" -#include "npc.h" #include "particle.h" #include "playerrelations.h" #include "sound.h" @@ -52,7 +52,6 @@ #include "gui/minimap.h" #include "gui/ministatus.h" #include "gui/npcdialog.h" -#include "gui/npcpostdialog.h" #include "gui/okdialog.h" #include "gui/outfitwindow.h" #include "gui/quitdialog.h" @@ -115,8 +114,7 @@ OutfitWindow *outfitWindow; SpecialsWindow *specialsWindow; SocialWindow *socialWindow; -BeingManager *beingManager = NULL; -FloorItemManager *floorItemManager = NULL; +ActorSpriteManager *actorSpriteManager = NULL; ChannelManager *channelManager = NULL; CommandHandler *commandHandler = NULL; Particle *particleEngine = NULL; @@ -130,14 +128,17 @@ ChatTab *localChatTab = NULL; */ static void initEngines() { - beingManager = new BeingManager; + Mana::Event::trigger(CHANNEL_GAME, EVENT_ENGINESINITALIZING); + + actorSpriteManager = new ActorSpriteManager; commandHandler = new CommandHandler; - floorItemManager = new FloorItemManager; channelManager = new ChannelManager; effectManager = new EffectManager; particleEngine = new Particle(NULL); particleEngine->setupEngine(); + + Mana::Event::trigger(CHANNEL_GAME, EVENT_ENGINESINITALIZED); } /** @@ -145,6 +146,8 @@ static void initEngines() */ static void createGuiWindows() { + Mana::Event::trigger(CHANNEL_GAME, EVENT_GUIWINDOWSLOADING); + setupWindow->clearWindowsForReset(); // Create dialogs @@ -152,9 +155,19 @@ static void createGuiWindows() minimap = new Minimap; chatWindow = new ChatWindow; tradeWindow = new TradeWindow; - equipmentWindow = new EquipmentWindow(player_node->mEquipment.get()); + switch (Net::getNetworkType()) + { + case ServerInfo::TMWATHENA: + equipmentWindow = new TmwAthena::TaEquipmentWindow( + PlayerInfo::getEquipment()); + break; + case ServerInfo::MANASERV: + default: + equipmentWindow = new EquipmentWindow(PlayerInfo::getEquipment()); + break; + } statusWindow = new StatusWindow; - inventoryWindow = new InventoryWindow(player_node->getInventory()); + inventoryWindow = new InventoryWindow(PlayerInfo::getInventory()); skillDialog = new SkillDialog; helpWindow = new HelpWindow; debugWindow = new DebugWindow; @@ -168,12 +181,9 @@ static void createGuiWindows() localChatTab = new ChatTab(_("General")); - if (config.getValue("logToChat", 0)) - { - logger->setChatWindow(chatWindow); - } + NpcDialog::setup(); - Net::getGeneralHandler()->guiWindowsLoaded(); + Mana::Event::trigger(CHANNEL_GAME, EVENT_GUIWINDOWSLOADED); } #define del_0(X) { delete X; X = 0; } @@ -183,8 +193,8 @@ static void createGuiWindows() */ static void destroyGuiWindows() { - Net::getGeneralHandler()->guiWindowsUnloaded(); - logger->setChatWindow(NULL); + Mana::Event::trigger(CHANNEL_GAME, EVENT_GUIWINDOWSUNLOADING); + del_0(localChatTab) // Need to do this first, so it can remove itself del_0(chatWindow) del_0(statusWindow) @@ -201,12 +211,16 @@ static void destroyGuiWindows() del_0(outfitWindow) del_0(specialsWindow) del_0(socialWindow) + + Mana::Event::trigger(CHANNEL_NPC, EVENT_CLOSEALL); // Cleanup remaining NPC dialogs + + Mana::Event::trigger(CHANNEL_GAME, EVENT_GUIWINDOWSUNLOADED); } Game *Game::mInstance = 0; Game::Game(): - mLastTarget(Being::UNKNOWN), + mLastTarget(ActorSprite::UNKNOWN), mCurrentMap(0), mMapName("") { assert(!mInstance); @@ -230,10 +244,8 @@ Game::Game(): initEngines(); - Net::getGameHandler()->inGame(); - // Initialize beings - beingManager->setPlayer(player_node); + actorSpriteManager->setPlayer(player_node); /* * To prevent the server from sending data before the client @@ -253,18 +265,21 @@ Game::Game(): joystick = new Joystick(0); setupWindow->setInGame(true); + + Mana::Event::trigger(CHANNEL_GAME, EVENT_CONSTRUCTED); } Game::~Game() { + Mana::Event::trigger(CHANNEL_GAME, EVENT_DESTRUCTING); + delete mWindowMenu; destroyGuiWindows(); - del_0(beingManager) + del_0(actorSpriteManager) if (Client::getState() != STATE_CHANGE_MAP) del_0(player_node) - del_0(floorItemManager) del_0(channelManager) del_0(commandHandler) del_0(joystick) @@ -273,6 +288,8 @@ Game::~Game() del_0(mCurrentMap) mInstance = 0; + + Mana::Event::trigger(CHANNEL_GAME, EVENT_DESTRUCTED); } static bool saveScreenshot() @@ -284,7 +301,7 @@ static bool saveScreenshot() if (showip) { player_node->setShowIp(false); - beingManager->updatePlayerNames(); + actorSpriteManager->updatePlayerNames(); gui->draw(); } @@ -293,7 +310,7 @@ static bool saveScreenshot() if (showip) { player_node->setShowIp(true); - beingManager->updatePlayerNames(); + actorSpriteManager->updatePlayerNames(); } // Search for an unused screenshot name @@ -311,7 +328,8 @@ static bool saveScreenshot() screenshotDirectory = std::string(PHYSFS_getUserDir()); } - do { + do + { screenshotCount++; filenameSuffix.str(""); filename.str(""); @@ -322,7 +340,8 @@ static bool saveScreenshot() testExists.open(filename.str().c_str(), std::ios::in); found = !testExists.is_open(); testExists.close(); - } while (!found); + } + while (!found); const bool success = ImageWriter::writePNG(screenshot, filename.str()); @@ -331,11 +350,11 @@ static bool saveScreenshot() std::stringstream chatlogentry; // TODO: Make it one complete gettext string below chatlogentry << _("Screenshot saved as ") << filenameSuffix.str(); - localChatTab->chatLog(chatlogentry.str(), BY_SERVER); + SERVER_NOTICE(chatlogentry.str()) } else { - localChatTab->chatLog(_("Saving screenshot failed!"), BY_SERVER); + SERVER_NOTICE(_("Saving screenshot failed!")) logger->log("Error: could not save screenshot."); } @@ -349,7 +368,8 @@ void Game::logic() handleInput(); // Handle all necessary game logic - beingManager->logic(); + ActorSprite::actorLogic(); + actorSpriteManager->logic(); particleEngine->update(); if (mCurrentMap) mCurrentMap->update(); @@ -407,7 +427,7 @@ void Game::handleInput() // send straight to gui for certain windows if (quitDialog || TextDialog::isActive() || - NpcPostDialog::isActive()) + PlayerInfo::getNPCPostCount() > 0) { try { @@ -465,7 +485,8 @@ void Game::handleInput() } - if (!chatWindow->isInputFocused() || (event.key.keysym.mod & KMOD_ALT)) + if (!chatWindow->isInputFocused() || (event.key.keysym.mod & + KMOD_ALT)) { if (keyboard.isKeyActive(keyboard.KEY_PREV_CHAT_TAB)) { @@ -612,7 +633,7 @@ void Game::handleInput() int y = player_node->getTileY(); FloorItem *item = - floorItemManager->findByCoordinates(x, y); + actorSpriteManager->findItem(x, y); // If none below the player, try the tile in front // of the player @@ -629,8 +650,7 @@ void Game::handleInput() default: break; } - item = floorItemManager->findByCoordinates( - x, y); + item = actorSpriteManager->findItem(x, y); } if (item) @@ -709,16 +729,12 @@ void Game::handleInput() unsigned int deflt = player_relations.getDefault(); if (deflt & PlayerRelation::TRADE) { - localChatTab->chatLog( - _("Ignoring incoming trade requests"), - BY_SERVER); + SERVER_NOTICE(_("Ignoring incoming trade requests")) deflt &= ~PlayerRelation::TRADE; } else { - localChatTab->chatLog( - _("Accepting incoming trade requests"), - BY_SERVER); + SERVER_NOTICE(_("Accepting incoming trade requests")) deflt |= PlayerRelation::TRADE; } @@ -764,7 +780,7 @@ void Game::handleInput() return; // Moving player around - if (player_node->isAlive() && !NPC::isTalking() && + if (player_node->isAlive() && !PlayerInfo::isTalking() && !chatWindow->isInputFocused() && !quitDialog && !TextDialog::isActive()) { // Get the state of the keyboard keys @@ -835,8 +851,8 @@ void Game::handleInput() if (!player_node->getTarget()) { // Only auto target Monsters - target = beingManager->findNearestLivingBeing(player_node, - 20, Being::MONSTER); + target = actorSpriteManager->findNearestLivingBeing(player_node, + 20, ActorSprite::MONSTER); } player_node->attack(target, newTarget); } @@ -848,16 +864,16 @@ void Game::handleInput() (joystick && joystick->buttonPressed(3))) && !keyboard.isKeyActive(keyboard.KEY_TARGET)) { - Being::Type currentTarget = Being::UNKNOWN; + ActorSprite::Type currentTarget = ActorSprite::UNKNOWN; if (keyboard.isKeyActive(keyboard.KEY_TARGET_CLOSEST) || (joystick && joystick->buttonPressed(3))) - currentTarget = Being::MONSTER; + currentTarget = ActorSprite::MONSTER; else if (keyboard.isKeyActive(keyboard.KEY_TARGET_PLAYER)) - currentTarget = Being::PLAYER; + currentTarget = ActorSprite::PLAYER; else if (keyboard.isKeyActive(keyboard.KEY_TARGET_NPC)) - currentTarget = Being::NPC; + currentTarget = ActorSprite::NPC; - Being *target = beingManager->findNearestLivingBeing(player_node, + Being *target = actorSpriteManager->findNearestLivingBeing(player_node, 20, currentTarget); if (target && (target != player_node->getTarget() || @@ -869,7 +885,7 @@ void Game::handleInput() } else { - mLastTarget = Being::UNKNOWN; // Reset last target + mLastTarget = ActorSprite::UNKNOWN; // Reset last target } // Talk to the nearest NPC if 't' pressed @@ -880,8 +896,8 @@ void Game::handleInput() if (target) { - if (target->getType() == Being::NPC) - static_cast<NPC*>(target)->talk(); + if (target->canTalk()) + target->talkTo(); } } @@ -899,7 +915,7 @@ void Game::handleInput() const int x = player_node->getTileX(); const int y = player_node->getTileY(); - FloorItem *item = floorItemManager->findByCoordinates(x, y); + FloorItem *item = actorSpriteManager->findItem(x, y); if (item) player_node->pickUp(item); @@ -919,8 +935,7 @@ void Game::handleInput() void Game::changeMap(const std::string &mapPath) { // Clean up floor items, beings and particles - floorItemManager->clear(); - beingManager->clear(); + actorSpriteManager->clear(); // Close the popup menu on map change so that invalid options can't be // executed. @@ -953,7 +968,7 @@ void Game::changeMap(const std::string &mapPath) // Notify the minimap and beingManager about the map change minimap->setMap(newMap); - beingManager->setMap(newMap); + actorSpriteManager->setMap(newMap); particleEngine->setMap(newMap); viewport->setMap(newMap); @@ -975,5 +990,7 @@ void Game::changeMap(const std::string &mapPath) delete mCurrentMap; mCurrentMap = newMap; - Net::getGameHandler()->mapLoaded(mapPath); + Mana::Event event(EVENT_MAPLOADED); + event.setString("mapPath", mapPath); + event.trigger(CHANNEL_GAME); } diff --git a/src/graphics.cpp b/src/graphics.cpp index 9815e1ad..24f92544 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -27,12 +27,15 @@ #include "resources/image.h" #include "resources/imageloader.h" +#include <SDL_gfxBlitFunc.h> + Graphics::Graphics(): mWidth(0), mHeight(0), mBpp(0), mFullscreen(false), - mHWAccel(false) + mHWAccel(false), + mBlitMode(BLIT_NORMAL) { } @@ -183,7 +186,10 @@ bool Graphics::drawImage(Image *image, int srcX, int srcY, int dstX, int dstY, srcRect.w = width; srcRect.h = height; - return !(SDL_BlitSurface(image->mSDLSurface, &srcRect, mTarget, &dstRect) < 0); + if (mBlitMode == BLIT_NORMAL) + return !(SDL_BlitSurface(image->mSDLSurface, &srcRect, mTarget, &dstRect) < 0); + else + return !(SDL_gfxBlitRGBA(image->mSDLSurface, &srcRect, mTarget, &dstRect) < 0); } void Graphics::drawImage(gcn::Image const *image, int srcX, int srcY, diff --git a/src/graphics.h b/src/graphics.h index 211fb901..344c31c3 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -73,6 +73,11 @@ struct ImageRect class Graphics : public gcn::SDLGraphics { public: + enum BlitMode { + BLIT_NORMAL = 0, + BLIT_GFX + }; + /** * Constructor. */ @@ -182,6 +187,12 @@ class Graphics : public gcn::SDLGraphics drawImageRect(area.x, area.y, area.width, area.height, imgRect); } + void setBlitMode(BlitMode mode) + { mBlitMode = mode; } + + BlitMode getBlitMode() + { return mBlitMode; } + /** * Updates the screen. This is done by either copying the buffer to the * screen or swapping pages. @@ -211,6 +222,7 @@ class Graphics : public gcn::SDLGraphics int mBpp; bool mFullscreen; bool mHWAccel; + BlitMode mBlitMode; }; extern Graphics *graphics; diff --git a/src/gui/beingpopup.cpp b/src/gui/beingpopup.cpp index 2150f7e5..ae0b43fd 100644 --- a/src/gui/beingpopup.cpp +++ b/src/gui/beingpopup.cpp @@ -20,8 +20,8 @@ #include "gui/beingpopup.h" +#include "being.h" #include "graphics.h" -#include "player.h" #include "units.h" #include "gui/gui.h" @@ -57,24 +57,24 @@ BeingPopup::~BeingPopup() { } -void BeingPopup::show(int x, int y, Player *p) +void BeingPopup::show(int x, int y, Being *b) { - if (!p) + if (!b) { setVisible(false); return; } - mBeingName->setCaption(p->getName()); + mBeingName->setCaption(b->getName()); mBeingName->adjustSize(); int minWidth = mBeingName->getWidth(); const int height = getFont()->getHeight(); - if (!(p->getPartyName().empty())) + if (!(b->getPartyName().empty())) { mBeingParty->setCaption(strprintf(_("Party: %s"), - p->getPartyName().c_str())); + b->getPartyName().c_str())); mBeingParty->adjustSize(); if (minWidth < mBeingParty->getWidth()) diff --git a/src/gui/beingpopup.h b/src/gui/beingpopup.h index f397e374..514a6e7e 100644 --- a/src/gui/beingpopup.h +++ b/src/gui/beingpopup.h @@ -23,8 +23,8 @@ #include "gui/widgets/popup.h" +class Being; class Label; -class Player; /** * A popup that displays information about a being. @@ -45,7 +45,7 @@ class BeingPopup : public Popup /** * Sets the info to be displayed given a particular player. */ - void show(int x, int y, Player *p); + void show(int x, int y, Being *b); // TODO: Add a version for monsters, NPCs, etc? diff --git a/src/gui/buy.cpp b/src/gui/buy.cpp index faa86cc9..4a8dae17 100644 --- a/src/gui/buy.cpp +++ b/src/gui/buy.cpp @@ -21,6 +21,10 @@ #include "gui/buy.h" +#include "playerinfo.h" +#include "shopitem.h" +#include "units.h" + #include "gui/setup.h" #include "gui/widgets/button.h" @@ -31,10 +35,6 @@ #include "gui/widgets/shoplistbox.h" #include "gui/widgets/slider.h" -#include "npc.h" -#include "shopitem.h" -#include "units.h" - #include "net/net.h" #include "net/npchandler.h" @@ -112,6 +112,8 @@ BuyDialog::BuyDialog(int npcId): instances.push_back(this); setVisible(true); + + PlayerInfo::setBuySellState(BUYSELL_BUYING); } BuyDialog::~BuyDialog() @@ -119,6 +121,9 @@ BuyDialog::~BuyDialog() delete mShopItems; instances.remove(this); + + if (PlayerInfo::getBuySellState() == BUYSELL_BUYING) + PlayerInfo::setBuySellState(BUYSELL_NONE); } void BuyDialog::setMoney(int amount) diff --git a/src/gui/buy.h b/src/gui/buy.h index 4b273bcc..c3cb3229 100644 --- a/src/gui/buy.h +++ b/src/gui/buy.h @@ -100,11 +100,6 @@ class BuyDialog : public Window, public gcn::ActionListener, void setVisible(bool visible); /** - * Returns true if any instances exist. - */ - static bool isActive() { return instances.size() > 0; } - - /** * Closes all instances. */ static void closeAll(); diff --git a/src/gui/buysell.cpp b/src/gui/buysell.cpp index c6b4ef41..4419ffcc 100644 --- a/src/gui/buysell.cpp +++ b/src/gui/buysell.cpp @@ -21,7 +21,7 @@ #include "buysell.h" -#include "npc.h" +#include "playerinfo.h" #include "gui/setup.h" @@ -67,11 +67,16 @@ BuySellDialog::BuySellDialog(int npcId): instances.push_back(this); setVisible(true); + + PlayerInfo::setBuySellState(BUYSELL_CHOOSING); } BuySellDialog::~BuySellDialog() { instances.remove(this); + + if (PlayerInfo::getBuySellState() == BUYSELL_CHOOSING) + PlayerInfo::setBuySellState(BUYSELL_NONE); } void BuySellDialog::setVisible(bool visible) diff --git a/src/gui/buysell.h b/src/gui/buysell.h index cf7ec91e..3408821a 100644 --- a/src/gui/buysell.h +++ b/src/gui/buysell.h @@ -52,11 +52,6 @@ class BuySellDialog : public Window, public gcn::ActionListener void action(const gcn::ActionEvent &event); /** - * Returns true if any instances exist. - */ - static bool isActive() { return instances.size() > 0; } - - /** * Closes all instances. */ static void closeAll(); diff --git a/src/gui/changeemaildialog.h b/src/gui/changeemaildialog.h index 7e5f04fa..84838d15 100644 --- a/src/gui/changeemaildialog.h +++ b/src/gui/changeemaildialog.h @@ -22,8 +22,6 @@ #ifndef GUI_CHANGEEMAIL_H #define GUI_CHANGEEMAIL_H -#include "guichanfwd.h" - #include "gui/widgets/window.h" #include <guichan/actionlistener.hpp> diff --git a/src/gui/changepassworddialog.h b/src/gui/changepassworddialog.h index 361debe4..d356a5df 100644 --- a/src/gui/changepassworddialog.h +++ b/src/gui/changepassworddialog.h @@ -22,8 +22,6 @@ #ifndef CHANGEPASSWORDDIALOG_H #define CHANGEPASSWORDDIALOG_H -#include "guichanfwd.h" - #include "gui/widgets/window.h" #include <guichan/actionlistener.hpp> diff --git a/src/gui/charcreatedialog.cpp b/src/gui/charcreatedialog.cpp index e4a5fd01..746295b5 100644 --- a/src/gui/charcreatedialog.cpp +++ b/src/gui/charcreatedialog.cpp @@ -54,7 +54,7 @@ CharCreateDialog::CharCreateDialog(CharSelectDialog *parent, int slot): mCharSelectDialog(parent), mSlot(slot) { - mPlayer = new Player(0, 0, NULL); + mPlayer = new Being(0, ActorSprite::PLAYER, 0, NULL); mPlayer->setGender(GENDER_MALE); int numberOfHairColors = ColorDB::size(); @@ -65,10 +65,10 @@ CharCreateDialog::CharCreateDialog(CharSelectDialog *parent, int slot): mNameField = new TextField(""); mNameLabel = new Label(_("Name:")); - // TRANSLATORS: This is a narrow symbol used to denote 'next'. + // TRANSLATORS: This is an arrow symbol used to denote 'next'. // You may change this symbol if your language uses another. mNextHairColorButton = new Button(_(">"), "nextcolor", this); - // TRANSLATORS: This is a narrow symbol used to denote 'previous'. + // TRANSLATORS: This is an arrow symbol used to denote 'previous'. // You may change this symbol if your language uses another. mPrevHairColorButton = new Button(_("<"), "prevcolor", this); mHairColorLabel = new Label(_("Hair color:")); @@ -156,7 +156,8 @@ void CharCreateDialog::action(const gcn::ActionEvent &event) { if (event.getId() == "create") { - if (getName().length() >= 4) + if (Net::getNetworkType() == ServerInfo::MANASERV + || getName().length() >= 4) { // Attempt to create the character mCreateButton->setEnabled(false); @@ -167,7 +168,12 @@ void CharCreateDialog::action(const gcn::ActionEvent &event) atts.push_back((int) mAttributeSlider[i]->getValue()); } - Net::getCharHandler()->newCharacter(getName(), mSlot, + int characterSlot = mSlot; + // On Manaserv, the slots start at 1, so we offset them. + if (Net::getNetworkType() == ServerInfo::MANASERV) + ++characterSlot; + + Net::getCharHandler()->newCharacter(getName(), characterSlot, mFemale->isSelected(), mHairStyle, mHairColor, atts); @@ -277,7 +283,8 @@ int CharCreateDialog::getDistributedPoints() const } void CharCreateDialog::setAttributes(const std::vector<std::string> &labels, - int available, int min, int max) + unsigned int available, unsigned int min, + unsigned int max) { mMaxPoints = available; diff --git a/src/gui/charcreatedialog.h b/src/gui/charcreatedialog.h index 902e650e..d6b6d390 100644 --- a/src/gui/charcreatedialog.h +++ b/src/gui/charcreatedialog.h @@ -22,7 +22,7 @@ #ifndef CHAR_CREATE_DIALOG_H #define CHAR_CREATE_DIALOG_H -#include "player.h" +#include "being.h" #include "guichanfwd.h" #include "gui/charselectdialog.h" @@ -63,8 +63,8 @@ class CharCreateDialog : public Window, public gcn::ActionListener void unlock(); void setAttributes(const std::vector<std::string> &labels, - int available, - int min, int max); + unsigned int available, + unsigned int min, unsigned int max); void setFixedGender(bool fixed, Gender gender = GENDER_FEMALE); @@ -110,7 +110,7 @@ class CharCreateDialog : public Window, public gcn::ActionListener gcn::Button *mCreateButton; gcn::Button *mCancelButton; - Player *mPlayer; + Being *mPlayer; PlayerBox *mPlayerBox; int mHairStyle; diff --git a/src/gui/charselectdialog.cpp b/src/gui/charselectdialog.cpp index f8c28c6a..42c514ec 100644 --- a/src/gui/charselectdialog.cpp +++ b/src/gui/charselectdialog.cpp @@ -59,6 +59,9 @@ #include <string> #include <cassert> +// Character slots per row in the dialog +static const int SLOTS_PER_ROW = 5; + /** * Listener for confirming character deletion. */ @@ -118,6 +121,7 @@ CharSelectDialog::CharSelectDialog(LoginData *loginData): mLocked(false), mUnregisterButton(0), mChangeEmailButton(0), + mCharacterEntries(0), mLoginData(loginData), mCharHandler(Net::getCharHandler()) { @@ -154,9 +158,10 @@ CharSelectDialog::CharSelectDialog(LoginData *loginData): place = getPlacer(0, 1); - for (int i = 0; i < MAX_CHARACTER_COUNT; i++) { - mCharacterEntries[i] = new CharacterDisplay(this); - place(i, 0, mCharacterEntries[i]); + for (int i = 0; i < (int)mLoginData->characterSlots; i++) + { + mCharacterEntries.push_back(new CharacterDisplay(this)); + place(i % SLOTS_PER_ROW, (int)i / SLOTS_PER_ROW, mCharacterEntries[i]); } reflowLayout(); @@ -179,9 +184,14 @@ void CharSelectDialog::action(const gcn::ActionEvent &event) // Check if a button of a character was pressed const gcn::Widget *sourceParent = event.getSource()->getParent(); int selected = -1; - for (int i = 0; i < MAX_CHARACTER_COUNT; ++i) + for (int i = 0; i < (int)mCharacterEntries.size(); ++i) + { if (mCharacterEntries[i] == sourceParent) + { selected = i; + break; + } + } const std::string &eventId = event.getId(); @@ -191,7 +201,8 @@ void CharSelectDialog::action(const gcn::ActionEvent &event) { attemptCharacterSelect(selected); } - else if (eventId == "new" && !mCharacterEntries[selected]->getCharacter()) + else if (eventId == "new" + && !mCharacterEntries[selected]->getCharacter()) { // Start new character dialog CharCreateDialog *charCreateDialog = @@ -261,19 +272,29 @@ void CharSelectDialog::attemptCharacterSelect(int index) void CharSelectDialog::setCharacters(const Net::Characters &characters) { // Reset previous characters - for (int i = 0; i < MAX_CHARACTER_COUNT; ++i) - mCharacterEntries[i]->setCharacter(0); + std::vector<CharacterDisplay*>::iterator iter, iter_end; + for (iter = mCharacterEntries.begin(), iter_end = mCharacterEntries.end(); + iter != iter_end; ++iter) + (*iter)->setCharacter(0); Net::Characters::const_iterator i, i_end = characters.end(); for (i = characters.begin(); i != i_end; ++i) { Net::Character *character = *i; - if (character->slot >= MAX_CHARACTER_COUNT) { + + // Slots Number start at 1 for Manaserv, so we offset them by one. + int characterSlot = character->slot; + if (Net::getNetworkType() == ServerInfo::MANASERV + && characterSlot > 0) + --characterSlot; + + if (characterSlot >= (int)mCharacterEntries.size()) + { logger->log("Warning: slot out of range: %d", character->slot); continue; } - mCharacterEntries[character->slot]->setCharacter(character); + mCharacterEntries[characterSlot]->setCharacter(character); } } @@ -299,7 +320,7 @@ void CharSelectDialog::setLocked(bool locked) if (mChangeEmailButton) mChangeEmailButton->setEnabled(!locked); - for (int i = 0; i < MAX_CHARACTER_COUNT; ++i) + for (int i = 0; i < (int)mCharacterEntries.size(); ++i) mCharacterEntries[i]->setActive(!mLocked); } @@ -309,9 +330,12 @@ bool CharSelectDialog::selectByName(const std::string &name, if (mLocked) return false; - for (int i = 0; i < MAX_CHARACTER_COUNT; ++i) { - if (Net::Character *character = mCharacterEntries[i]->getCharacter()) { - if (character->dummy->getName() == name) { + for (int i = 0; i < (int)mCharacterEntries.size(); ++i) + { + if (Net::Character *character = mCharacterEntries[i]->getCharacter()) + { + if (character->dummy->getName() == name) + { mCharacterEntries[i]->requestFocus(); if (action == Choose) attemptCharacterSelect(i); @@ -380,8 +404,10 @@ void CharacterDisplay::update() mButton->setCaption(_("Choose")); mButton->setActionEventId("use"); mName->setCaption(strprintf("%s", character->getName().c_str())); - mLevel->setCaption(strprintf("Level %d", character->getLevel())); - mMoney->setCaption(Units::formatCurrency(character->getMoney())); + mLevel->setCaption(strprintf("Level %d", + mCharacter->data.mAttributes[LEVEL])); + mMoney->setCaption(Units::formatCurrency( + mCharacter->data.mAttributes[MONEY])); mDelete->setVisible(true); } diff --git a/src/gui/charselectdialog.h b/src/gui/charselectdialog.h index b6e71715..455ec2df 100644 --- a/src/gui/charselectdialog.h +++ b/src/gui/charselectdialog.h @@ -22,10 +22,6 @@ #ifndef CHAR_SELECT_H #define CHAR_SELECT_H -#include "guichanfwd.h" -#include "main.h" -#include "player.h" - #include "gui/widgets/window.h" #include "net/charhandler.h" @@ -100,8 +96,8 @@ class CharSelectDialog : public Window, public gcn::ActionListener, gcn::Button *mUnregisterButton; gcn::Button *mChangeEmailButton; - enum { MAX_CHARACTER_COUNT = 3 }; - CharacterDisplay *mCharacterEntries[MAX_CHARACTER_COUNT]; + /** The player boxes */ + std::vector<CharacterDisplay*> mCharacterEntries; LoginData *mLoginData; diff --git a/src/gui/chat.cpp b/src/gui/chat.cpp index 6d900e98..2ac5100d 100644 --- a/src/gui/chat.cpp +++ b/src/gui/chat.cpp @@ -21,10 +21,11 @@ #include "chat.h" -#include "beingmanager.h" +#include "actorspritemanager.h" #include "configuration.h" #include "localplayer.h" #include "party.h" +#include "playerrelations.h" #include "gui/recorder.h" #include "gui/setup.h" @@ -89,6 +90,9 @@ ChatWindow::ChatWindow(): mAutoComplete(new ChatAutoComplete), mTmpVisible(false) { + listen(CHANNEL_CHAT); + listen(CHANNEL_NOTICES); + setWindowName("Chat"); setupWindow->registerWindowForReset(this); @@ -119,7 +123,7 @@ ChatWindow::ChatWindow(): mChatInput->setHistory(mHistory); mChatInput->setAutoComplete(mAutoComplete); - mReturnToggles = config.getValue("ReturnToggles", "0") == "1"; + mReturnToggles = config.getBoolValue("ReturnToggles"); mRecorder = new Recorder(this); } @@ -286,20 +290,20 @@ void ChatWindow::chatInput(const std::string &msg) void ChatWindow::doPresent() { - const Beings &beings = beingManager->getAll(); + const ActorSprites &actors = actorSpriteManager->getAll(); std::string response = ""; int playercount = 0; - for (Beings::const_iterator bi = beings.begin(), be = beings.end(); - bi != be; ++bi) + for (ActorSpritesConstIterator it = actors.begin(), it_end = actors.end(); + it != it_end; it++) { - if ((*bi)->getType() == Being::PLAYER) + if ((*it)->getType() == ActorSprite::PLAYER) { if (!response.empty()) { response += ", "; } - response += (*bi)->getName(); + response += static_cast<Being*>(*it)->getName(); ++playercount; } } @@ -371,6 +375,40 @@ void ChatWindow::mouseDragged(gcn::MouseEvent &event) } } +void ChatWindow::event(Channels channel, const Mana::Event &event) +{ + if (channel == CHANNEL_NOTICES) + { + if (event.getName() == EVENT_SERVERNOTICE) + localChatTab->chatLog(event.getString("message"), BY_SERVER); + } + else if (channel == CHANNEL_CHAT) + { + if (event.getName() == EVENT_WHISPER) + { + whisper(event.getString("nick"), event.getString("message")); + } + else if (event.getName() == EVENT_WHISPERERROR) + { + whisper(event.getString("nick"), + event.getString("error"), BY_SERVER); + } + else if (event.getName() == EVENT_PLAYER) + { + localChatTab->chatLog(event.getString("message"), BY_PLAYER); + } + else if (event.getName() == EVENT_ANNOUNCEMENT) + { + localChatTab->chatLog(event.getString("message"), BY_GM); + } + else if (event.getName() == EVENT_BEING) + { + if (event.getInt("permissions") & PlayerRelation::SPEECH_LOG) + localChatTab->chatLog(event.getString("message"), BY_OTHER); + } + } +} + void ChatWindow::addInputText(const std::string &text) { const int caretPos = mChatInput->getCaretPosition(); @@ -409,7 +447,7 @@ void ChatWindow::setRecordingFile(const std::string &msg) } void ChatWindow::whisper(const std::string &nick, - const std::string &mes, bool own) + const std::string &mes, Own own) { if (mes.empty()) return; @@ -428,15 +466,19 @@ void ChatWindow::whisper(const std::string &nick, if (i != mWhispers.end()) tab = i->second; - else if (config.getValue("whispertab", true)) + else if (config.getBoolValue("whispertab")) tab = addWhisperTab(nick); if (tab) { - if (own) + if (own == BY_PLAYER) { tab->chatInput(mes); } + else if (own == BY_SERVER) + { + tab->chatLog(mes); + } else { tab->chatLog(nick, mes); @@ -445,7 +487,7 @@ void ChatWindow::whisper(const std::string &nick, } else { - if (own) + if (own == BY_PLAYER) { Net::getChatHandler()->privateMessage(nick, mes); diff --git a/src/gui/chat.h b/src/gui/chat.h index 1c673556..f546502c 100644 --- a/src/gui/chat.h +++ b/src/gui/chat.h @@ -22,6 +22,8 @@ #ifndef CHAT_H #define CHAT_H +#include "listener.h" + #include "gui/widgets/window.h" #include "gui/widgets/textfield.h" @@ -36,8 +38,8 @@ #include <vector> class BrowserBox; -class Channel; class ChatTab; +class Channel; class ChatInput; class Recorder; class ScrollArea; @@ -48,12 +50,24 @@ class WhisperTab; #define DEFAULT_CHAT_WINDOW_SCROLL 7 // 1 means `1/8th of the window size'. +enum Own +{ + BY_GM, + BY_PLAYER, + BY_OTHER, + BY_SERVER, + BY_CHANNEL, + ACT_WHISPER, // getting whispered at + ACT_IS, // equivalent to "/me" on IRC + BY_LOGGER +}; + /** One item in the chat log */ struct CHATLOG { std::string nick; std::string text; - int own; + Own own; }; /** @@ -62,7 +76,8 @@ struct CHATLOG * \ingroup Interface */ class ChatWindow : public Window, - public gcn::ActionListener + public gcn::ActionListener, + public Mana::Listener { public: /** @@ -143,6 +158,8 @@ class ChatWindow : public Window, void mousePressed(gcn::MouseEvent &event); void mouseDragged(gcn::MouseEvent &event); + void event(Channels channel, const Mana::Event &event); + /** * Scrolls the chat window * @@ -165,7 +182,7 @@ class ChatWindow : public Window, void doPresent(); void whisper(const std::string &nick, const std::string &mes, - bool own = false); + Own own = BY_OTHER); ChatTab *addWhisperTab(const std::string &nick, bool switchTo = false); diff --git a/src/gui/emotepopup.cpp b/src/gui/emotepopup.cpp index 7c05e263..bd40a932 100644 --- a/src/gui/emotepopup.cpp +++ b/src/gui/emotepopup.cpp @@ -22,18 +22,17 @@ #include "gui/emotepopup.h" -#include "animatedsprite.h" +#include "imagesprite.h" #include "configuration.h" #include "emoteshortcut.h" #include "graphics.h" #include "localplayer.h" #include "log.h" -#include "gui/theme.h" - #include "resources/emotedb.h" #include "resources/image.h" #include "resources/iteminfo.h" +#include "resources/theme.h" #include "utils/dtor.h" @@ -54,14 +53,14 @@ EmotePopup::EmotePopup(): // Setup emote sprites for (int i = 0; i <= EmoteDB::getLast(); ++i) { - mEmotes.push_back(EmoteDB::getAnimation(i)); + mEmotes.push_back(EmoteDB::get(i)->sprite); } mSelectionImage = Theme::getImageFromTheme("selection.png"); if (!mSelectionImage) logger->error("Unable to load selection.png"); - mSelectionImage->setAlpha(config.getValue("guialpha", 0.8)); + mSelectionImage->setAlpha(config.getFloatValue("guialpha")); addMouseListener(this); recalculateSize(); diff --git a/src/gui/emotepopup.h b/src/gui/emotepopup.h index 62a3f24a..d2cabc44 100644 --- a/src/gui/emotepopup.h +++ b/src/gui/emotepopup.h @@ -30,7 +30,7 @@ #include <list> #include <vector> -class AnimatedSprite; +class ImageSprite; class Image; namespace gcn { @@ -105,7 +105,7 @@ class EmotePopup : public Popup */ void distributeValueChangedEvent(); - std::vector<const AnimatedSprite*> mEmotes; + std::vector<const ImageSprite*> mEmotes; Image *mSelectionImage; int mSelectedEmoteIndex; int mHoveredEmoteIndex; diff --git a/src/gui/equipmentwindow.cpp b/src/gui/equipmentwindow.cpp index 43e330f4..02fc2a96 100644 --- a/src/gui/equipmentwindow.cpp +++ b/src/gui/equipmentwindow.cpp @@ -29,7 +29,6 @@ #include "gui/equipmentwindow.h" #include "gui/itempopup.h" -#include "gui/theme.h" #include "gui/setup.h" #include "gui/viewport.h" @@ -41,6 +40,7 @@ #include "resources/image.h" #include "resources/iteminfo.h" #include "resources/resourcemanager.h" +#include "resources/theme.h" #include "utils/gettext.h" #include "utils/stringutils.h" @@ -67,8 +67,9 @@ static const int boxPosition[][2] = { EquipmentWindow::EquipmentWindow(Equipment *equipment): Window(_("Equipment")), - mEquipment(equipment), - mSelected(-1) + mEquipBox(0), + mSelected(-1), + mEquipment(equipment) { mItemPopup = new ItemPopup; setupWindow->registerWindowForReset(this); @@ -92,12 +93,6 @@ EquipmentWindow::EquipmentWindow(Equipment *equipment): add(playerBox); add(mUnequip); - - for (int i = 0; i < Equipment::EQUIP_VECTOREND; i++) - { - mEquipBox[i].posX = boxPosition[i][0] + getPadding(); - mEquipBox[i].posY = boxPosition[i][1] + getTitleBarHeight(); - } } EquipmentWindow::~EquipmentWindow() @@ -110,47 +105,7 @@ void EquipmentWindow::draw(gcn::Graphics *graphics) // Draw window graphics Window::draw(graphics); - Graphics *g = static_cast<Graphics*>(graphics); - Window::drawChildren(graphics); - - for (int i = 0; i < Equipment::EQUIP_VECTOREND; i++) - { - if (i == mSelected) - { - const gcn::Color color = Theme::getThemeColor(Theme::HIGHLIGHT); - - // Set color to the highlight color - g->setColor(gcn::Color(color.r, color.g, color.b, getGuiAlpha())); - g->fillRectangle(gcn::Rectangle(mEquipBox[i].posX, mEquipBox[i].posY, - BOX_WIDTH, BOX_HEIGHT)); - } - - // Set color black - g->setColor(gcn::Color(0, 0, 0)); - // Draw box border - g->drawRectangle(gcn::Rectangle(mEquipBox[i].posX, mEquipBox[i].posY, - BOX_WIDTH, BOX_HEIGHT)); - - Item *item = mEquipment->getEquipment(i); - if (item) - { - // Draw Item. - Image *image = item->getImage(); - image->setAlpha(1.0f); // Ensure the image is drawn with maximum opacity - g->drawImage(image, - mEquipBox[i].posX + 2, - mEquipBox[i].posY + 2); - if (i == EQUIP_PROJECTILE_SLOT) - { - g->setColor(Theme::getThemeColor(Theme::TEXT)); - graphics->drawText(toString(item->getQuantity()), - mEquipBox[i].posX + (BOX_WIDTH / 2), - mEquipBox[i].posY - getFont()->getHeight(), - gcn::Graphics::CENTER); - } - } - } } void EquipmentWindow::action(const gcn::ActionEvent &event) @@ -158,21 +113,22 @@ void EquipmentWindow::action(const gcn::ActionEvent &event) if (event.getId() == "unequip" && mSelected > -1) { Item *item = mEquipment->getEquipment(mSelected); - Net::getInventoryHandler()->unequipItem(item); + item->doEvent(EVENT_DOUNEQUIP); setSelected(-1); } } Item *EquipmentWindow::getItem(int x, int y) const { - for (int i = 0; i < Equipment::EQUIP_VECTOREND; i++) + if (Net::getNetworkType() == ServerInfo::TMWATHENA) { - gcn::Rectangle tRect(mEquipBox[i].posX, mEquipBox[i].posY, - BOX_WIDTH, BOX_HEIGHT); - - if (tRect.isPointInRect(x, y)) + for (int i = 0; i < TmwAthena::EQUIP_VECTOR_END; i++) { - return mEquipment->getEquipment(i); + gcn::Rectangle tRect(mEquipBox[i].posX, mEquipBox[i].posY, + BOX_WIDTH, BOX_HEIGHT); + + if (tRect.isPointInRect(x, y)) + return mEquipment->getEquipment(i); } } return NULL; @@ -188,14 +144,17 @@ void EquipmentWindow::mousePressed(gcn::MouseEvent& mouseEvent) if (mouseEvent.getButton() == gcn::MouseEvent::LEFT) { // Checks if any of the presses were in the equip boxes. - for (int i = 0; i < Equipment::EQUIP_VECTOREND; i++) + if (Net::getNetworkType() == ServerInfo::TMWATHENA) { - Item *item = mEquipment->getEquipment(i); - gcn::Rectangle tRect(mEquipBox[i].posX, mEquipBox[i].posY, - BOX_WIDTH, BOX_HEIGHT); + for (int i = 0; i < TmwAthena::EQUIP_VECTOR_END; i++) + { + Item *item = mEquipment->getEquipment(i); + gcn::Rectangle tRect(mEquipBox[i].posX, mEquipBox[i].posY, + BOX_WIDTH, BOX_HEIGHT); - if (tRect.isPointInRect(x, y) && item) - setSelected(i); + if (tRect.isPointInRect(x, y) && item) + setSelected(i); + } } } else if (mouseEvent.getButton() == gcn::MouseEvent::RIGHT) @@ -245,3 +204,71 @@ void EquipmentWindow::setSelected(int index) mSelected = index; mUnequip->setEnabled(mSelected != -1); } + +namespace TmwAthena { + +TaEquipmentWindow::TaEquipmentWindow(Equipment *equipment): + EquipmentWindow(equipment) +{ + // Load equipment boxes. + mEquipBox = new EquipBox[TmwAthena::EQUIP_VECTOR_END]; + + for (int i = 0; i < TmwAthena::EQUIP_VECTOR_END; i++) + { + mEquipBox[i].posX = boxPosition[i][0] + getPadding(); + mEquipBox[i].posY = boxPosition[i][1] + getTitleBarHeight(); + } +} + +TaEquipmentWindow::~TaEquipmentWindow() +{ + delete[] mEquipBox; +} + +void TaEquipmentWindow::draw(gcn::Graphics *graphics) +{ + EquipmentWindow::draw(graphics); + + // Draw equipment boxes + Graphics *g = static_cast<Graphics*>(graphics); + + for (int i = 0; i < TmwAthena::EQUIP_VECTOR_END; i++) + { + if (i == mSelected) + { + const gcn::Color color = Theme::getThemeColor(Theme::HIGHLIGHT); + + // Set color to the highlight color + g->setColor(gcn::Color(color.r, color.g, color.b, getGuiAlpha())); + g->fillRectangle(gcn::Rectangle(mEquipBox[i].posX, mEquipBox[i].posY, + BOX_WIDTH, BOX_HEIGHT)); + } + + // Set color black + g->setColor(gcn::Color(0, 0, 0)); + // Draw box border + g->drawRectangle(gcn::Rectangle(mEquipBox[i].posX, mEquipBox[i].posY, + BOX_WIDTH, BOX_HEIGHT)); + + Item *item = mEquipment->getEquipment(i); + if (item) + { + // Draw Item. + Image *image = item->getImage(); + image->setAlpha(1.0f); // Ensure the image is drawn with maximum opacity + g->drawImage(image, + mEquipBox[i].posX + 2, + mEquipBox[i].posY + 2); + if (i == TmwAthena::EQUIP_PROJECTILE_SLOT) + { + g->setColor(Theme::getThemeColor(Theme::TEXT)); + graphics->drawText(toString(item->getQuantity()), + mEquipBox[i].posX + (BOX_WIDTH / 2), + mEquipBox[i].posY - getFont()->getHeight(), + gcn::Graphics::CENTER); + } + } + } +} + +}; diff --git a/src/gui/equipmentwindow.h b/src/gui/equipmentwindow.h index 5688bf30..a0fa6acb 100644 --- a/src/gui/equipmentwindow.h +++ b/src/gui/equipmentwindow.h @@ -23,7 +23,6 @@ #define EQUIPMENTWINDOW_H #include "equipment.h" -#include "guichanfwd.h" #include "gui/widgets/window.h" @@ -60,6 +59,21 @@ class EquipmentWindow : public Window, public gcn::ActionListener void mousePressed(gcn::MouseEvent& mouseEvent); + protected: + /** + * Equipment box. + */ + struct EquipBox + { + int posX; + int posY; + }; + + EquipBox *mEquipBox; /**< Equipment Boxes. */ + + int mSelected; /**< Index of selected item. */ + Equipment *mEquipment; + private: void mouseExited(gcn::MouseEvent &event); void mouseMoved(gcn::MouseEvent &event); @@ -68,25 +82,34 @@ class EquipmentWindow : public Window, public gcn::ActionListener void setSelected(int index); - Equipment *mEquipment; + ItemPopup *mItemPopup; + gcn::Button *mUnequip; +}; +namespace TmwAthena { + +class TaEquipmentWindow : public EquipmentWindow +{ + public: /** - * Equipment box. + * Constructor. */ - struct EquipBox - { - int posX; - int posY; - }; + TaEquipmentWindow(Equipment *equipment); - EquipBox mEquipBox[Equipment::EQUIP_VECTOREND]; /**< Equipment Boxes. */ + /** + * Destructor. + */ + ~TaEquipmentWindow(); - ItemPopup *mItemPopup; - gcn::Button *mUnequip; + /** + * Draws the equipment window using TmwAthena routine. + */ + void draw(gcn::Graphics *graphics); - int mSelected; /**< Index of selected item. */ }; +}; // namespace TmwAthena + extern EquipmentWindow *equipmentWindow; #endif diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 75f67435..c0d1babf 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -24,14 +24,13 @@ #include "gui/focushandler.h" #include "gui/palette.h" #include "gui/sdlinput.h" -#include "gui/theme.h" #include "gui/truetypefont.h" #include "gui/widgets/window.h" #include "gui/widgets/windowcontainer.h" -#include "configlistener.h" #include "configuration.h" +#include "listener.h" #include "graphics.h" #include "log.h" @@ -39,6 +38,7 @@ #include "resources/imageset.h" #include "resources/imageloader.h" #include "resources/resourcemanager.h" +#include "resources/theme.h" #include <guichan/exception.hpp> #include <guichan/image.hpp> @@ -50,19 +50,23 @@ SDLInput *guiInput = 0; // Bolded font gcn::Font *boldFont = 0; -class GuiConfigListener : public ConfigListener +class GuiConfigListener : public Mana::Listener { public: GuiConfigListener(Gui *g): mGui(g) {} - void optionChanged(const std::string &name) + void event(Channels channel, const Mana::Event &event) { - if (name == "customcursor") + if (channel == CHANNEL_CONFIG) { - bool bCustomCursor = config.getValue("customcursor", 1) == 1; - mGui->setUseCustomCursor(bCustomCursor); + if (event.getName() == EVENT_CONFIGOPTIONCHANGED && + event.getString("option") == "customcursor") + { + bool bCustomCursor = config.getBoolValue("customcursor"); + mGui->setUseCustomCursor(bCustomCursor); + } } } private: @@ -104,7 +108,7 @@ Gui::Gui(Graphics *graphics): ResourceManager *resman = ResourceManager::getInstance(); // Set global font - const int fontSize = (int) config.getValue("fontSize", 11); + const int fontSize = config.getValue("fontSize", 11); std::string fontFile = branding.getValue("font", "fonts/dejavusans.ttf"); std::string path = resman->getPath(fontFile); @@ -135,14 +139,13 @@ Gui::Gui(Graphics *graphics): gcn::Widget::setGlobalFont(mGuiFont); // Initialize mouse cursor and listen for changes to the option - setUseCustomCursor(config.getValue("customcursor", 1) == 1); + setUseCustomCursor(config.getBoolValue("customcursor")); mConfigListener = new GuiConfigListener(this); - config.addListener("customcursor", mConfigListener); + mConfigListener->listen(CHANNEL_CONFIG); } Gui::~Gui() { - config.removeListener("customcursor", mConfigListener); delete mConfigListener; if (mMouseCursors) diff --git a/src/gui/help.cpp b/src/gui/help.cpp index f3c6a0af..aca036c1 100644 --- a/src/gui/help.cpp +++ b/src/gui/help.cpp @@ -94,9 +94,9 @@ void HelpWindow::loadHelp(const std::string &helpFile) void HelpWindow::loadFile(const std::string &file) { ResourceManager *resman = ResourceManager::getInstance(); - std::string helpPath = branding.getValue("helpPath", ""); + std::string helpPath = branding.getStringValue("helpPath"); if (helpPath.empty()) - helpPath = paths.getValue("help", "help/"); + helpPath = paths.getStringValue("help"); std::vector<std::string> lines = resman->loadTextFile(helpPath + file + ".txt"); diff --git a/src/gui/inventorywindow.cpp b/src/gui/inventorywindow.cpp index 16ac5409..31743c57 100644 --- a/src/gui/inventorywindow.cpp +++ b/src/gui/inventorywindow.cpp @@ -23,14 +23,13 @@ #include "inventory.h" #include "item.h" -#include "localplayer.h" #include "units.h" #include "keyboardconfig.h" +#include "playerinfo.h" #include "gui/itemamount.h" #include "gui/setup.h" #include "gui/sdlinput.h" -#include "gui/theme.h" #include "gui/viewport.h" #include "gui/widgets/button.h" @@ -44,6 +43,7 @@ #include "net/net.h" #include "resources/iteminfo.h" +#include "resources/theme.h" #include "utils/gettext.h" #include "utils/stringutils.h" @@ -60,6 +60,8 @@ InventoryWindow::InventoryWindow(Inventory *inventory): mInventory(inventory), mSplit(false) { + listen(CHANNEL_ATTRIBUTES); + setWindowName(isMainInventory() ? "Inventory" : "Storage"); setupWindow->registerWindowForReset(this); setResizable(true); @@ -95,7 +97,8 @@ InventoryWindow::InventoryWindow(Inventory *inventory): longestUseString = unequip; } - mUseButton = new Button(longestUseString, "use", this); + mEquipButton = new Button(_("Equip"), "equip", this); + mUseButton = new Button(_("Activate"), "activate", this); mDropButton = new Button(_("Drop..."), "drop", this); mSplitButton = new Button(_("Split"), "split", this); mOutfitButton = new Button(_("Outfits"), "outfit", this); @@ -109,8 +112,9 @@ InventoryWindow::InventoryWindow(Inventory *inventory): place(5, 0, mSlotsBar, 2); place(0, 1, invenScroll, 7).setPadding(3); place(0, 2, mUseButton); - place(1, 2, mDropButton); - place(2, 2, mSplitButton); + place(1, 2, mEquipButton); + place(2, 2, mDropButton); + place(3, 2, mSplitButton); place(6, 2, mOutfitButton); updateWeight(); @@ -138,13 +142,19 @@ InventoryWindow::InventoryWindow(Inventory *inventory): slotsChanged(mInventory); if (!isMainInventory()) + { setVisible(true); + PlayerInfo::setStorageCount(PlayerInfo::getStorageCount() + 1); + } } InventoryWindow::~InventoryWindow() { instances.remove(this); mInventory->removeInventoyListener(this); + + if (!isMainInventory()) + PlayerInfo::setStorageCount(PlayerInfo::getStorageCount() - 1); } void InventoryWindow::action(const gcn::ActionEvent &event) @@ -175,17 +185,21 @@ void InventoryWindow::action(const gcn::ActionEvent &event) if (!item) return; - if (event.getId() == "use") + if (event.getId() == "activate") + item->doEvent(EVENT_DOUSE); + else if (event.getId() == "equip") { - if (item->isEquipment()) + if (item->isEquippable()) { if (item->isEquipped()) - Net::getInventoryHandler()->unequipItem(item); + item->doEvent(EVENT_DOUNEQUIP); else - Net::getInventoryHandler()->equipItem(item); + item->doEvent(EVENT_DOEQUIP); } else - Net::getInventoryHandler()->useItem(item); + { + item->doEvent(EVENT_DOUSE); + } } else if (event.getId() == "drop") { @@ -234,20 +248,30 @@ void InventoryWindow::mouseClicked(gcn::MouseEvent &event) if (event.getButton() == gcn::MouseEvent::LEFT) { - if (isStorageActive() && keyboard.isKeyActive(keyboard.KEY_EMOTE)) + if (instances.size() > 1 && keyboard.isKeyActive(keyboard.KEY_EMOTE)) { Item *item = mItems->getSelectedItem(); if(!item) return; if (mInventory->isMainInventory()) - Net::getInventoryHandler()->moveItem(Inventory::INVENTORY, - item->getInvIndex(), item->getQuantity(), - Inventory::STORAGE); + { + Mana::Event event(EVENT_DOMOVE); + event.setItem("item", item); + event.setInt("amount", item->getQuantity()); + event.setInt("source", Inventory::INVENTORY); + event.setInt("destination", Inventory::STORAGE); + event.trigger(CHANNEL_ITEM); + } else - Net::getInventoryHandler()->moveItem(Inventory::STORAGE, - item->getInvIndex(), item->getQuantity(), - Inventory::INVENTORY); + { + Mana::Event event(EVENT_DOMOVE); + event.setItem("item", item); + event.setInt("amount", item->getQuantity()); + event.setInt("source", Inventory::STORAGE); + event.setInt("destination", Inventory::INVENTORY); + event.trigger(CHANNEL_ITEM); + } } } } @@ -298,26 +322,27 @@ void InventoryWindow::updateButtons() if (!item || item->getQuantity() == 0) { mUseButton->setEnabled(false); + mEquipButton->setEnabled(false); mDropButton->setEnabled(false); mSplitButton->setEnabled(false); return; } - mUseButton->setEnabled(true); mDropButton->setEnabled(true); - if (item->isEquipment()) + if (item->getInfo().getEquippable()) { if (item->isEquipped()) - mUseButton->setCaption(_("Unequip")); + mEquipButton->setCaption(_("Unequip")); else - mUseButton->setCaption(_("Equip")); + mEquipButton->setCaption(_("Equip")); + mEquipButton->setEnabled(true); } else - { - mUseButton->setCaption(_("Use")); - } + mEquipButton->setEnabled(false); + + mUseButton->setEnabled(item->getInfo().getActivatable()); if (item->getQuantity() > 1) mDropButton->setCaption(_("Drop...")); @@ -343,15 +368,36 @@ void InventoryWindow::close() } else { - Net::getInventoryHandler()->closeStorage(Inventory::STORAGE); + Mana::Event event(EVENT_DOCLOSEINVENTORY); + event.setInt("type", mInventory->getType()); + event.trigger(CHANNEL_ITEM); scheduleDelete(); } } +void InventoryWindow::event(Channels channel, const Mana::Event &event) +{ + if (event.getName() == EVENT_UPDATEATTRIBUTE) + { + int id = event.getInt("id"); + if (id == TOTAL_WEIGHT || + id == MAX_WEIGHT) + { + updateWeight(); + } + } +} + void InventoryWindow::updateWeight() { - int total = player_node->getTotalWeight(); - int max = player_node->getMaxWeight(); + if (!isMainInventory()) + return; + + int total = PlayerInfo::getAttribute(TOTAL_WEIGHT); + int max = PlayerInfo::getAttribute(MAX_WEIGHT); + + if (max <= 0) + return; // Adjust progress bar mWeightBar->setProgress((float) total / max); diff --git a/src/gui/inventorywindow.h b/src/gui/inventorywindow.h index 0dce0611..0ddd13f7 100644 --- a/src/gui/inventorywindow.h +++ b/src/gui/inventorywindow.h @@ -23,11 +23,11 @@ #define INVENTORYWINDOW_H #include "inventory.h" +#include "listener.h" #include "gui/widgets/window.h" #include "net/inventoryhandler.h" -#include "net/net.h" #include <guichan/actionlistener.hpp> #include <guichan/keylistener.hpp> @@ -47,7 +47,8 @@ class InventoryWindow : public Window, public gcn::ActionListener, public gcn::KeyListener, public gcn::SelectionListener, - public InventoryListener + public InventoryListener, + public Mana::Listener { public: /** @@ -106,21 +107,19 @@ class InventoryWindow : public Window, */ void updateButtons(); - /** - * Updates the weight bar. - */ - void updateWeight(); - void slotsChanged(Inventory* inventory); bool isMainInventory() { return mInventory->isMainInventory(); } + void event(Channels channel, const Mana::Event &event); + + private: /** - * Returns true if any instances exist. + * Updates the weight bar. */ - static bool isStorageActive() { return instances.size() > 1; } + void updateWeight(); + - private: typedef std::list<InventoryWindow*> WindowList; static WindowList instances; @@ -129,8 +128,8 @@ class InventoryWindow : public Window, std::string mWeight, mSlots; - gcn::Button *mUseButton, *mDropButton, *mSplitButton, *mOutfitButton, - *mStoreButton, *mRetrieveButton; + gcn::Button *mUseButton, *mEquipButton, *mDropButton, *mSplitButton, + *mOutfitButton, *mStoreButton, *mRetrieveButton; gcn::Label *mWeightLabel, *mSlotsLabel; diff --git a/src/gui/itemamount.cpp b/src/gui/itemamount.cpp index a98a67ab..85325c66 100644 --- a/src/gui/itemamount.cpp +++ b/src/gui/itemamount.cpp @@ -47,20 +47,30 @@ void ItemAmountWindow::finish(Item *item, int amount, Usage usage) tradeWindow->tradeItem(item, amount); break; case ItemDrop: - Net::getInventoryHandler()->dropItem(item, amount); + item->doEvent(EVENT_DODROP, amount); break; case ItemSplit: - Net::getInventoryHandler()->splitItem(item, amount); + item->doEvent(EVENT_DOSPLIT, amount); break; case StoreAdd: - Net::getInventoryHandler()->moveItem(Inventory::INVENTORY, - item->getInvIndex(), amount, - Inventory::STORAGE); + { + Mana::Event event(EVENT_DOMOVE); + event.setItem("item", item); + event.setInt("amount", amount); + event.setInt("source", Inventory::INVENTORY); + event.setInt("destination", Inventory::STORAGE); + event.trigger(CHANNEL_ITEM); + } break; case StoreRemove: - Net::getInventoryHandler()->moveItem(Inventory::STORAGE, - item->getInvIndex(), amount, - Inventory::INVENTORY); + { + Mana::Event event(EVENT_DOMOVE); + event.setItem("item", item); + event.setInt("amount", amount); + event.setInt("source", Inventory::STORAGE); + event.setInt("destination", Inventory::INVENTORY); + event.trigger(CHANNEL_ITEM); + } break; default: break; diff --git a/src/gui/itempopup.cpp b/src/gui/itempopup.cpp index b71ca529..60943756 100644 --- a/src/gui/itempopup.cpp +++ b/src/gui/itempopup.cpp @@ -26,27 +26,67 @@ #include "units.h" #include "gui/gui.h" -#include "gui/theme.h" #include "gui/widgets/icon.h" +#include "gui/widgets/label.h" #include "gui/widgets/textbox.h" #include "utils/gettext.h" #include "utils/stringutils.h" +#include "net/net.h" + #include "resources/image.h" #include "resources/resourcemanager.h" +#include "resources/theme.h" #include <guichan/font.hpp> #include <guichan/widgets/label.hpp> +#define ITEMPOPUP_WRAP_WIDTH 196 + +static gcn::Color getColorFromItemType(ItemType type) +{ + switch (type) + { + case ITEM_UNUSABLE: + return Theme::getThemeColor(Theme::GENERIC); + case ITEM_USABLE: + return Theme::getThemeColor(Theme::USABLE); + case ITEM_EQUIPMENT_ONE_HAND_WEAPON: + return Theme::getThemeColor(Theme::ONEHAND); + case ITEM_EQUIPMENT_TWO_HANDS_WEAPON: + return Theme::getThemeColor(Theme::TWOHAND); + case ITEM_EQUIPMENT_TORSO: + return Theme::getThemeColor(Theme::TORSO); + case ITEM_EQUIPMENT_ARMS: + return Theme::getThemeColor(Theme::ARMS); + case ITEM_EQUIPMENT_HEAD: + return Theme::getThemeColor(Theme::HEAD); + case ITEM_EQUIPMENT_LEGS: + return Theme::getThemeColor(Theme::LEGS); + case ITEM_EQUIPMENT_SHIELD: + return Theme::getThemeColor(Theme::SHIELD); + case ITEM_EQUIPMENT_RING: + return Theme::getThemeColor(Theme::RING); + case ITEM_EQUIPMENT_NECKLACE: + return Theme::getThemeColor(Theme::NECKLACE); + case ITEM_EQUIPMENT_FEET: + return Theme::getThemeColor(Theme::FEET); + case ITEM_EQUIPMENT_AMMO: + return Theme::getThemeColor(Theme::AMMO); + default: + return Theme::getThemeColor(Theme::UNKNOWN_ITEM); + } +} + ItemPopup::ItemPopup(): Popup("ItemPopup"), mIcon(0) { // Item Name - mItemName = new gcn::Label; + mItemName = new Label; mItemName->setFont(boldFont); mItemName->setPosition(getPadding(), getPadding()); @@ -98,8 +138,9 @@ void ItemPopup::setItem(const ItemInfo &item, bool showImage) { ResourceManager *resman = ResourceManager::getInstance(); Image *image = resman->getImage( - paths.getValue("itemIcons", "graphics/items/") - + item.getImageName()); + paths.getStringValue("itemIcons") + + item.getDisplay().image); + mIcon->setImage(image); if (image) { @@ -114,18 +155,25 @@ void ItemPopup::setItem(const ItemInfo &item, bool showImage) mIcon->setImage(0); } - mItemType = item.getType(); + mItemType = item.getItemType(); mItemName->setCaption(item.getName()); mItemName->adjustSize(); - mItemName->setForegroundColor(getColor(mItemType)); + mItemName->setForegroundColor(getColorFromItemType(mItemType)); mItemName->setPosition(getPadding() + space, getPadding()); - mItemDesc->setTextWrapped(item.getDescription(), 196); - mItemEffect->setTextWrapped(item.getEffect(), 196); + mItemDesc->setTextWrapped(item.getDescription(), ITEMPOPUP_WRAP_WIDTH); + { + const std::vector<std::string> &effect = item.getEffect(); + std::string temp = ""; + for (std::vector<std::string>::const_iterator it = effect.begin(), + it_end = effect.end(); it != it_end; ++it) + temp += temp.empty() ? *it : "\n" + *it; + mItemEffect->setTextWrapped(temp, ITEMPOPUP_WRAP_WIDTH); + } mItemWeight->setTextWrapped(strprintf(_("Weight: %s"), Units::formatWeight(item.getWeight()).c_str()), - 196); + ITEMPOPUP_WRAP_WIDTH); int minWidth = mItemName->getWidth() + space; @@ -172,41 +220,6 @@ void ItemPopup::setItem(const ItemInfo &item, bool showImage) (numRowsDesc + 1) * fontHeight); } -gcn::Color ItemPopup::getColor(ItemType type) -{ - switch (type) - { - case ITEM_UNUSABLE: - return Theme::getThemeColor(Theme::GENERIC); - case ITEM_USABLE: - return Theme::getThemeColor(Theme::USABLE); - case ITEM_EQUIPMENT_ONE_HAND_WEAPON: - return Theme::getThemeColor(Theme::ONEHAND); - case ITEM_EQUIPMENT_TWO_HANDS_WEAPON: - return Theme::getThemeColor(Theme::TWOHAND); - case ITEM_EQUIPMENT_TORSO: - return Theme::getThemeColor(Theme::TORSO); - case ITEM_EQUIPMENT_ARMS: - return Theme::getThemeColor(Theme::ARMS); - case ITEM_EQUIPMENT_HEAD: - return Theme::getThemeColor(Theme::HEAD); - case ITEM_EQUIPMENT_LEGS: - return Theme::getThemeColor(Theme::LEGS); - case ITEM_EQUIPMENT_SHIELD: - return Theme::getThemeColor(Theme::SHIELD); - case ITEM_EQUIPMENT_RING: - return Theme::getThemeColor(Theme::RING); - case ITEM_EQUIPMENT_NECKLACE: - return Theme::getThemeColor(Theme::NECKLACE); - case ITEM_EQUIPMENT_FEET: - return Theme::getThemeColor(Theme::FEET); - case ITEM_EQUIPMENT_AMMO: - return Theme::getThemeColor(Theme::AMMO); - default: - return Theme::getThemeColor(Theme::UNKNOWN_ITEM); - } -} - void ItemPopup::mouseMoved(gcn::MouseEvent &event) { Popup::mouseMoved(event); diff --git a/src/gui/itempopup.h b/src/gui/itempopup.h index a3976a11..f054ddf5 100644 --- a/src/gui/itempopup.h +++ b/src/gui/itempopup.h @@ -62,8 +62,6 @@ class ItemPopup : public Popup TextBox *mItemWeight; ItemType mItemType; Icon *mIcon; - - static gcn::Color getColor(ItemType type); }; #endif // ITEMPOPUP_H diff --git a/src/gui/minimap.cpp b/src/gui/minimap.cpp index 8713d3f9..993814ea 100644 --- a/src/gui/minimap.cpp +++ b/src/gui/minimap.cpp @@ -21,20 +21,19 @@ #include "gui/minimap.h" +#include "actorspritemanager.h" #include "being.h" -#include "beingmanager.h" #include "configuration.h" #include "graphics.h" #include "localplayer.h" #include "log.h" #include "map.h" -#include "player.h" #include "gui/setup.h" -#include "gui/userpalette.h" #include "resources/image.h" #include "resources/resourcemanager.h" +#include "resources/userpalette.h" #include "utils/gettext.h" @@ -186,52 +185,46 @@ void Minimap::draw(gcn::Graphics *graphics) drawImage(mMapImage, mapOriginX, mapOriginY); } - const Beings &beings = beingManager->getAll(); + const ActorSprites &actors = actorSpriteManager->getAll(); - for (Beings::const_iterator bi = beings.begin(), bi_end = beings.end(); - bi != bi_end; ++bi) + for (ActorSpritesConstIterator it = actors.begin(), it_end = actors.end(); + it != it_end; it++) { - const Being *being = (*bi); + if ((*it)->getType() == ActorSprite::FLOOR_ITEM) + continue; + + const Being *being = static_cast<Being*>(*it); int dotSize = 2; - switch (being->getType()) + int type = UserPalette::PC; + + if (being == player_node) + { + type = UserPalette::SELF; + dotSize = 3; + } + else if (being->isGM()) + type = UserPalette::GM; + else if (being->isInParty()) + type = UserPalette::PARTY; + else { - case Being::PLAYER: - { - const Player *player = static_cast<const Player*>(being); - - int type = UserPalette::PC; - - if (being == player_node) - { - type = UserPalette::SELF; - dotSize = 3; - } - else if (player->isGM()) - { - type = UserPalette::GM; - } - else if (player->isInParty()) - { - type = UserPalette::PARTY; - } - - graphics->setColor(userPalette->getColor(type)); + switch (being->getType()) + { + case ActorSprite::MONSTER: + graphics->setColor(userPalette->getColor(UserPalette::MONSTER)); break; - } - - case Being::MONSTER: - graphics->setColor(userPalette->getColor(UserPalette::MONSTER)); - break; - case Being::NPC: - graphics->setColor(userPalette->getColor(UserPalette::NPC)); - break; + case ActorSprite::NPC: + graphics->setColor(userPalette->getColor(UserPalette::NPC)); + break; - default: - continue; + default: + continue; + } } + graphics->setColor(userPalette->getColor(type)); const int offsetHeight = (int) ((dotSize - 1) * mHeightProportion); const int offsetWidth = (int) ((dotSize - 1) * mWidthProportion); diff --git a/src/gui/ministatus.cpp b/src/gui/ministatus.cpp index 90581f61..fae40d03 100644 --- a/src/gui/ministatus.cpp +++ b/src/gui/ministatus.cpp @@ -24,17 +24,20 @@ #include "animatedsprite.h" #include "configuration.h" #include "graphics.h" -#include "localplayer.h" +#include "playerinfo.h" +#include "statuseffect.h" #include "gui/gui.h" #include "gui/statuswindow.h" #include "gui/textpopup.h" -#include "gui/theme.h" #include "gui/widgets/progressbar.h" #include "net/net.h" #include "net/playerhandler.h" +#include "net/gamehandler.h" + +#include "resources/theme.h" #include "utils/gettext.h" #include "utils/stringutils.h" @@ -44,22 +47,36 @@ extern volatile int tick_time; MiniStatusWindow::MiniStatusWindow(): Popup("MiniStatus") { - int max = player_node->getMaxHp(); - mHpBar = new ProgressBar(max ? (float) player_node->getHp() / max : 0, - 100, 20, Theme::PROG_HP); - max = player_node->getMaxMP(); - mMpBar = new ProgressBar(max ? (float) player_node->getMP() / max : 0, - 100, 20, Net::getPlayerHandler()->canUseMagic() ? - Theme::PROG_MP : Theme::PROG_NO_MP); - max = player_node->getExpNeeded(); - mXpBar = new ProgressBar(max ? (float) player_node->getExp() / max : 0, - 100, 20, Theme::PROG_EXP); + listen(CHANNEL_ATTRIBUTES); + + mHpBar = new ProgressBar(0, 100, 20, Theme::PROG_HP); + StatusWindow::updateHPBar(mHpBar); + + if (Net::getGameHandler()->canUseMagicBar()) + { + mMpBar = new ProgressBar(0, 100, 20, + Net::getPlayerHandler()->canUseMagic() + ? Theme::PROG_MP : Theme::PROG_NO_MP); + + StatusWindow::updateMPBar(mMpBar); + } + else + mMpBar = 0; + + mXpBar = new ProgressBar(0, 100, 20, Theme::PROG_EXP); + StatusWindow::updateXPBar(mXpBar); + + // Add the progressbars to the window + mHpBar->setPosition(0, 3); - mMpBar->setPosition(mHpBar->getWidth() + 3, 3); - mXpBar->setPosition(mMpBar->getX() + mMpBar->getWidth() + 3, 3); + if (mMpBar) + mMpBar->setPosition(mHpBar->getWidth() + 3, 3); + mXpBar->setPosition(mMpBar ? mMpBar->getX() + mMpBar->getWidth() + 3 : + mHpBar->getX() + mHpBar->getWidth() + 3, 3); add(mHpBar); - add(mMpBar); + if (mMpBar) + add(mMpBar); add(mXpBar); setContentSize(mXpBar->getX() + mXpBar->getWidth(), @@ -70,8 +87,6 @@ MiniStatusWindow::MiniStatusWindow(): mTextPopup = new TextPopup(); addMouseListener(this); - - update(StatusWindow::HP); } void MiniStatusWindow::setIcon(int index, AnimatedSprite *sprite) @@ -104,19 +119,82 @@ void MiniStatusWindow::drawIcons(Graphics *graphics) } } -void MiniStatusWindow::update(int id) +void MiniStatusWindow::event(Channels channel, const Mana::Event &event) { - if (id == StatusWindow::HP) - { - StatusWindow::updateHPBar(mHpBar); - } - else if (id == StatusWindow::MP) + if (channel == CHANNEL_ATTRIBUTES) { - StatusWindow::updateMPBar(mMpBar); + if (event.getName() == EVENT_UPDATEATTRIBUTE) + { + int id = event.getInt("id"); + if (id == HP || id == MAX_HP) + { + StatusWindow::updateHPBar(mHpBar); + } + else if (id == MP || id == MAX_MP) + { + StatusWindow::updateMPBar(mMpBar); + } + else if (id == EXP || id == EXP_NEEDED) + { + StatusWindow::updateXPBar(mXpBar); + } + } } - else if (id == StatusWindow::EXP) + else if (channel == CHANNEL_ACTORSPRITE) { - StatusWindow::updateXPBar(mXpBar); + if (event.getName() == EVENT_UPDATESTATUSEFFECT) + { + int index = event.getInt("index"); + bool newStatus = event.getBool("newStatus"); + + StatusEffect *effect = StatusEffect::getStatusEffect(index, + newStatus); + + if (effect) + { + effect->deliverMessage(); + effect->playSFX(); + + AnimatedSprite *sprite = effect->getIcon(); + + typedef std::vector<int> IntMap; + + if (!sprite) + { + // delete sprite, if necessary + for (unsigned int i = 0; i < mStatusEffectIcons.size();) + if (mStatusEffectIcons[i] == index) + { + mStatusEffectIcons.erase(mStatusEffectIcons.begin() + + i); + miniStatusWindow->eraseIcon(i); + } + else + i++; + } + else + { + // replace sprite or append + bool found = false; + + for (unsigned int i = 0; i < mStatusEffectIcons.size(); + i++) + if (mStatusEffectIcons[i] == index) + { + miniStatusWindow->setIcon(i, sprite); + found = true; + break; + } + + if (!found) + { // add new + int offset = mStatusEffectIcons.size(); + miniStatusWindow->setIcon(offset, sprite); + mStatusEffectIcons.push_back(index); + } + } + } + } } } @@ -152,23 +230,23 @@ void MiniStatusWindow::mouseMoved(gcn::MouseEvent &event) if (event.getSource() == mXpBar) { mTextPopup->show(x + getX(), y + getY(), - strprintf("%u/%u", player_node->getExp(), - player_node->getExpNeeded()), + strprintf("%u/%u", PlayerInfo::getAttribute(EXP), + PlayerInfo::getAttribute(EXP_NEEDED)), strprintf("%s: %u", _("Need"), - player_node->getExpNeeded() - - player_node->getExp())); + PlayerInfo::getAttribute(EXP_NEEDED) + - PlayerInfo::getAttribute(EXP))); } else if (event.getSource() == mHpBar) { mTextPopup->show(x + getX(), y + getY(), - strprintf("%u/%u", player_node->getHp(), - player_node->getMaxHp())); + strprintf("%u/%u", PlayerInfo::getAttribute(HP), + PlayerInfo::getAttribute(MAX_HP))); } else if (event.getSource() == mMpBar) { mTextPopup->show(x + getX(), y + getY(), - strprintf("%u/%u", player_node->getMP(), - player_node->getMaxMP())); + strprintf("%u/%u", PlayerInfo::getAttribute(MP), + PlayerInfo::getAttribute(MAX_MP))); } else { @@ -182,5 +260,3 @@ void MiniStatusWindow::mouseExited(gcn::MouseEvent &event) mTextPopup->setVisible(false); } - - diff --git a/src/gui/ministatus.h b/src/gui/ministatus.h index bb8d4094..9dfcaeae 100644 --- a/src/gui/ministatus.h +++ b/src/gui/ministatus.h @@ -22,6 +22,8 @@ #ifndef MINISTATUS_H #define MINISTATUS_H +#include "listener.h" + #include "gui/widgets/popup.h" #include <vector> @@ -36,21 +38,14 @@ class TextPopup; * * \ingroup Interface */ -class MiniStatusWindow : public Popup +class MiniStatusWindow : public Popup, public Mana::Listener { public: MiniStatusWindow(); - /** - * Sets one of the icons. - */ - void setIcon(int index, AnimatedSprite *sprite); - - void eraseIcon(int index); - void drawIcons(Graphics *graphics); - void update(int id); // Same types as status window + void event(Channels channel, const Mana::Event &event); void logic(); // Updates icons @@ -63,6 +58,13 @@ class MiniStatusWindow : public Popup private: bool isInBar(ProgressBar *bar, int x, int y) const; + /** + * Sets one of the icons. + */ + void setIcon(int index, AnimatedSprite *sprite); + + void eraseIcon(int index); + /* * Mini Status Bars */ @@ -71,6 +73,7 @@ class MiniStatusWindow : public Popup ProgressBar *mXpBar; TextPopup *mTextPopup; + std::vector<int> mStatusEffectIcons; std::vector<AnimatedSprite *> mIcons; }; diff --git a/src/gui/npcdialog.cpp b/src/gui/npcdialog.cpp index c4128588..590001b0 100644 --- a/src/gui/npcdialog.cpp +++ b/src/gui/npcdialog.cpp @@ -22,8 +22,11 @@ #include "gui/npcdialog.h" #include "configuration.h" -#include "npc.h" +#include "event.h" +#include "listener.h" +#include "playerinfo.h" +#include "gui/npcpostdialog.h" #include "gui/setup.h" #include "gui/widgets/button.h" @@ -47,12 +50,29 @@ #define CAPTION_CLOSE _("Close") #define CAPTION_SUBMIT _("Submit") +typedef std::map<int, NpcDialog*> NpcDialogs; + +class NpcEventListener : public Mana::Listener +{ +public: + void event(Channels channel, const Mana::Event &event); + + NpcDialog *getDialog(int id, bool make = true); + + void removeDialog(int id); + +private: + NpcDialogs mNpcDialogs; +}; + +static NpcEventListener *npcListener = NULL; + NpcDialog::DialogList NpcDialog::instances; NpcDialog::NpcDialog(int npcId) : Window(_("NPC")), mNpcId(npcId), - mLogInteraction(config.getValue("logNpcInGui", true)), + mLogInteraction(config.getBoolValue("logNpcInGui")), mDefaultInt(0), mInputState(NPC_INPUT_NONE), mActionState(NPC_ACTION_WAIT) @@ -123,7 +143,9 @@ NpcDialog::NpcDialog(int npcId) setVisible(true); requestFocus(); - config.addListener("logNpcInGui", this); + listen(CHANNEL_CONFIG); + PlayerInfo::setNPCInteractionCount(PlayerInfo::getNPCInteractionCount() + + 1); } NpcDialog::~NpcDialog() @@ -139,7 +161,10 @@ NpcDialog::~NpcDialog() instances.remove(this); - config.removeListener("logNpcInGui", this); + PlayerInfo::setNPCInteractionCount(PlayerInfo::getNPCInteractionCount() + - 1); + + npcListener->removeDialog(mNpcId); } void NpcDialog::setText(const std::string &text) @@ -192,26 +217,25 @@ void NpcDialog::action(const gcn::ActionEvent &event) if (mInputState == NPC_INPUT_LIST) { - int choice = 0; int selectedIndex = mItemList->getSelected(); if (selectedIndex >= (int) mItems.size() || selectedIndex < 0) - { return; - } - choice = selectedIndex + 1; + printText = mItems[selectedIndex]; - Net::getNpcHandler()->listInput(mNpcId, choice); + Net::getNpcHandler()->menuSelect(mNpcId, selectedIndex + 1); } else if (mInputState == NPC_INPUT_STRING) { printText = mTextField->getText(); + Net::getNpcHandler()->stringInput(mNpcId, printText); } else if (mInputState == NPC_INPUT_INTEGER) { printText = strprintf("%d", mIntField->getValue()); + Net::getNpcHandler()->integerInput(mNpcId, mIntField->getValue()); } // addText will auto remove the input layout @@ -256,6 +280,7 @@ void NpcDialog::nextDialog() void NpcDialog::closeDialog() { Net::getNpcHandler()->closeDialog(mNpcId); + close(); } int NpcDialog::getNumberOfElements() @@ -281,15 +306,6 @@ void NpcDialog::addChoice(const std::string &choice) mItems.push_back(choice); } -void NpcDialog::parseListItems(const std::string &itemString) -{ - std::istringstream iss(itemString); - - std::string tmp; - while (getline(iss, tmp, ':')) - mItems.push_back(tmp); -} - void NpcDialog::textRequest(const std::string &defaultText) { mActionState = NPC_ACTION_INPUT; @@ -371,11 +387,15 @@ void NpcDialog::setVisible(bool visible) } } -void NpcDialog::optionChanged(const std::string &name) +void NpcDialog::event(Channels channel, const Mana::Event &event) { - if (name == "logNpcInGui") + if (channel != CHANNEL_CONFIG) + return; + + if (event.getName() == EVENT_CONFIGOPTIONCHANGED && + event.getString("option") == "logNpcInGui") { - mLogInteraction = config.getValue("logNpcInGui", true); + mLogInteraction = config.getBoolValue("logNpcInGui"); } } @@ -409,6 +429,16 @@ void NpcDialog::closeAll() } } +void NpcDialog::setup() +{ + if (npcListener) + return; + + npcListener = new NpcEventListener(); + + npcListener->listen(CHANNEL_NPC); +} + void NpcDialog::buildLayout() { clearLayout(); @@ -475,3 +505,123 @@ void NpcDialog::buildLayout() mScrollArea->setVerticalScrollAmount(mScrollArea->getVerticalMaxScroll()); } + +void NpcEventListener::event(Channels channel, + const Mana::Event &event) +{ + if (channel != CHANNEL_NPC) + return; + + if (event.getName() == EVENT_MESSAGE) + { + NpcDialog *dialog = getDialog(event.getInt("id")); + + dialog->addText(event.getString("text")); + } + else if (event.getName() == EVENT_MENU) + { + NpcDialog *dialog = getDialog(event.getInt("id")); + + dialog->choiceRequest(); + + int count = event.getInt("choiceCount"); + for (int i = 1; i <= count; i++) + dialog->addChoice(event.getString("choice" + toString(i))); + } + else if (event.getName() == EVENT_INTEGERINPUT) + { + NpcDialog *dialog = getDialog(event.getInt("id")); + + int defaultValue = event.getInt("default", 0); + int min = event.getInt("min", 0); + int max = event.getInt("max", 2147483647); + + dialog->integerRequest(defaultValue, min, max); + } + else if (event.getName() == EVENT_STRINGINPUT) + { + NpcDialog *dialog = getDialog(event.getInt("id")); + + try + { + dialog->textRequest(event.getString("default")); + } + catch (Mana::BadEvent) + { + dialog->textRequest(""); + } + } + else if (event.getName() == EVENT_NEXT) + { + int id = event.getInt("id"); + NpcDialog *dialog = getDialog(id, false); + + if (!dialog) + { + int mNpcId = id; + Net::getNpcHandler()->nextDialog(mNpcId); + return; + } + + dialog->showNextButton(); + } + else if (event.getName() == EVENT_CLOSE) + { + int id = event.getInt("id"); + NpcDialog *dialog = getDialog(id, false); + + if (!dialog) + { + int mNpcId = id; + Net::getNpcHandler()->closeDialog(mNpcId); + return; + } + + dialog->showCloseButton(); + } + else if (event.getName() == EVENT_CLOSEALL) + { + NpcDialog::closeAll(); + } + else if (event.getName() == EVENT_END) + { + int id = event.getInt("id"); + NpcDialog *dialog = getDialog(id, false); + + if (dialog) + dialog->close(); + } + else if (event.getName() == EVENT_POST) + { + new NpcPostDialog(event.getInt("id")); + } +} + +NpcDialog *NpcEventListener::getDialog(int id, bool make) +{ + NpcDialogs::iterator diag = mNpcDialogs.find(id); + NpcDialog *dialog = 0; + + if (diag == mNpcDialogs.end()) + { + // Empty dialogs don't help + if (make) + { + dialog = new NpcDialog(id); + mNpcDialogs[id] = dialog; + } + } + else + { + dialog = diag->second; + } + + return dialog; +} + +void NpcEventListener::removeDialog(int id) +{ + NpcDialogs::iterator it = mNpcDialogs.find(id); + if (it != mNpcDialogs.end()) + mNpcDialogs.erase(it); +} diff --git a/src/gui/npcdialog.h b/src/gui/npcdialog.h index 337da6f2..d0131d0e 100644 --- a/src/gui/npcdialog.h +++ b/src/gui/npcdialog.h @@ -22,8 +22,7 @@ #ifndef NPCDIALOG_H #define NPCDIALOG_H -#include "configlistener.h" -#include "npc.h" +#include "listener.h" #include "gui/widgets/window.h" @@ -46,7 +45,7 @@ class Button; * \ingroup Interface */ class NpcDialog : public Window, public gcn::ActionListener, - public gcn::ListModel, public ConfigListener + public gcn::ListModel, public Mana::Listener { public: /** @@ -120,13 +119,6 @@ class NpcDialog : public Window, public gcn::ActionListener, void addChoice(const std::string &); /** - * Fills the options list for an NPC dialog. - * - * @param itemString A string with the options separated with colons. - */ - void parseListItems(const std::string &itemString); - - /** * Requests a text string from the user. */ void textRequest(const std::string &defaultText = ""); @@ -140,8 +132,7 @@ class NpcDialog : public Window, public gcn::ActionListener, /** * Requests a interger from the user. */ - void integerRequest(int defaultValue = 0, int min = 0, - int max = 2147483647); + void integerRequest(int defaultValue, int min, int max); void move(int amount); @@ -154,12 +145,7 @@ class NpcDialog : public Window, public gcn::ActionListener, void setVisible(bool visible); - void optionChanged(const std::string &name); - - /** - * Returns true if any instances exist. - */ - static bool isActive() { return instances.size() > 0; } + void event(Channels channel, const Mana::Event &event); /** * Returns the first active instance. Useful for pushing user @@ -172,6 +158,8 @@ class NpcDialog : public Window, public gcn::ActionListener, */ static void closeAll(); + static void setup(); + private: typedef std::list<NpcDialog*> DialogList; static DialogList instances; diff --git a/src/gui/npcpostdialog.cpp b/src/gui/npcpostdialog.cpp index 19d0cf61..c53203be 100644 --- a/src/gui/npcpostdialog.cpp +++ b/src/gui/npcpostdialog.cpp @@ -21,10 +21,10 @@ #include "gui/npcpostdialog.h" -#include "npc.h" +#include "event.h" +#include "playerinfo.h" #include "gui/widgets/button.h" -#include "gui/widgets/chattab.h" #include "gui/widgets/label.h" #include "gui/widgets/textbox.h" #include "gui/widgets/textfield.h" @@ -80,11 +80,14 @@ NpcPostDialog::NpcPostDialog(int npcId): instances.push_back(this); setVisible(true); + + PlayerInfo::setNPCPostCount(PlayerInfo::getNPCPostCount() + 1); } NpcPostDialog::~NpcPostDialog() { instances.remove(this); + PlayerInfo::setNPCPostCount(PlayerInfo::getNPCPostCount() - 1); } void NpcPostDialog::action(const gcn::ActionEvent &event) @@ -93,12 +96,12 @@ void NpcPostDialog::action(const gcn::ActionEvent &event) { if (mSender->getText().empty() || mText->getText().empty()) { - localChatTab->chatLog(_("Failed to send as sender or letter " - "invalid.")); + SERVER_NOTICE(_("Failed to send as sender or letter invalid.")) } else { - Net::getNpcHandler()->sendLetter(mNpcId, mSender->getText(), + Net::getNpcHandler()->sendLetter(mNpcId, + mSender->getText(), mText->getText()); } setVisible(false); diff --git a/src/gui/npcpostdialog.h b/src/gui/npcpostdialog.h index ad0053a3..248e4515 100644 --- a/src/gui/npcpostdialog.h +++ b/src/gui/npcpostdialog.h @@ -47,11 +47,6 @@ public: void setVisible(bool visible); /** - * Returns true if any instances exist. - */ - static bool isActive() { return instances.size() > 0; } - - /** * Closes all instances. */ static void closeAll(); diff --git a/src/gui/outfitwindow.cpp b/src/gui/outfitwindow.cpp index 89bf47da..8da8914a 100644 --- a/src/gui/outfitwindow.cpp +++ b/src/gui/outfitwindow.cpp @@ -22,18 +22,17 @@ #include "outfitwindow.h" #include "configuration.h" -#include "localplayer.h" +#include "equipment.h" #include "graphics.h" #include "inventory.h" -#include "equipment.h" #include "item.h" #include "log.h" +#include "playerinfo.h" #include "gui/chat.h" #include "gui/widgets/button.h" #include "gui/widgets/checkbox.h" -#include "gui/widgets/chattab.h" #include "gui/widgets/label.h" #include "gui/widgets/layout.h" @@ -41,6 +40,7 @@ #include "net/net.h" #include "resources/image.h" +#include "resources/iteminfo.h" #include "utils/gettext.h" #include "utils/stringutils.h" @@ -168,11 +168,11 @@ void OutfitWindow::wearOutfit(int outfit) Item *item; for (int i = 0; i < OUTFIT_ITEM_COUNT; i++) { - item = player_node->getInventory()->findItem(mItems[outfit][i]); + item = PlayerInfo::getInventory()->findItem(mItems[outfit][i]); if (item && !item->isEquipped() && item->getQuantity()) { - if (item->isEquipment()) - Net::getInventoryHandler()->equipItem(item); + if (item->isEquippable()) + item->doEvent(EVENT_DOEQUIP); } } } @@ -206,7 +206,7 @@ void OutfitWindow::draw(gcn::Graphics *graphics) } Item *item = - player_node->getInventory()->findItem(mItems[mCurrentOutfit][i]); + PlayerInfo::getInventory()->findItem(mItems[mCurrentOutfit][i]); if (item) { // Draw item icon. @@ -245,7 +245,7 @@ void OutfitWindow::mouseDragged(gcn::MouseEvent &event) const int itemId = mItems[mCurrentOutfit][index]; if (itemId < 0) return; - Item *item = player_node->getInventory()->findItem(itemId); + Item *item = PlayerInfo::getInventory()->findItem(itemId); if (item) { mItemMoved = item; @@ -319,7 +319,7 @@ int OutfitWindow::getIndexFromGrid(int pointX, int pointY) const void OutfitWindow::unequipNotInOutfit(int outfit) { - Inventory *inventory = player_node->getInventory(); + Inventory *inventory = PlayerInfo::getInventory(); if (!inventory) return; @@ -338,7 +338,10 @@ void OutfitWindow::unequipNotInOutfit(int outfit) } if (!found) { - Net::getInventoryHandler()->unequipItem(inventory->getItem(i)); + Item *item = inventory->getItem(i); + + if (item) + item->doEvent(EVENT_DOUNEQUIP); } } } diff --git a/src/gui/popupmenu.cpp b/src/gui/popupmenu.cpp index 409a0eda..8ff638c2 100644 --- a/src/gui/popupmenu.cpp +++ b/src/gui/popupmenu.cpp @@ -21,14 +21,14 @@ #include "gui/popupmenu.h" +#include "actorspritemanager.h" #include "being.h" -#include "beingmanager.h" #include "flooritem.h" #include "graphics.h" #include "item.h" #include "localplayer.h" #include "log.h" -#include "npc.h" +#include "playerinfo.h" #include "playerrelations.h" #include "gui/chat.h" @@ -76,7 +76,7 @@ void PopupMenu::showPopup(int x, int y, Being *being) switch (being->getType()) { - case Being::PLAYER: + case ActorSprite::PLAYER: { // Players can be traded with. mBrowserBox->addRow(strprintf("@@trade|%s@@", @@ -143,7 +143,7 @@ void PopupMenu::showPopup(int x, int y, Being *being) } break; - case Being::NPC: + case ActorSprite::NPC: // NPCs can be talked to (single option, candidate for removal // unless more options would be added) mBrowserBox->addRow(strprintf("@@talk|%s@@", @@ -151,7 +151,7 @@ void PopupMenu::showPopup(int x, int y, Being *being) name.c_str()).c_str())); break; - case Being::MONSTER: + case ActorSprite::MONSTER: { // Monsters can be attacked mBrowserBox->addRow(strprintf("@@attack|%s@@", @@ -180,11 +180,11 @@ void PopupMenu::showPopup(int x, int y, Being *being) void PopupMenu::showPopup(int x, int y, FloorItem *floorItem) { mFloorItem = floorItem; - mItem = floorItem->getItem(); + ItemInfo info = floorItem->getInfo(); mBrowserBox->clearRows(); // Floor item can be picked up (single option, candidate for removal) - std::string name = ItemDB::get(mFloorItem->getItemId()).getName(); + std::string name = info.getName(); mBrowserBox->addRow(strprintf("@@pickup|%s@@", strprintf(_("Pick up %s"), name.c_str()).c_str())); mBrowserBox->addRow(strprintf("@@chat|%s@@", _("Add to chat"))); @@ -198,16 +198,17 @@ void PopupMenu::showPopup(int x, int y, FloorItem *floorItem) void PopupMenu::handleLink(const std::string &link) { - Being *being = beingManager->findBeing(mBeingId); + Being *being = actorSpriteManager->findBeing(mBeingId); // Talk To action - if (link == "talk" && being && being->getType() == Being::NPC) + if (link == "talk" && being && being->canTalk()) { - static_cast<NPC*>(being)->talk(); + being->talkTo(); } // Trade action - else if (link == "trade" && being && being->getType() == Being::PLAYER) + else if (link == "trade" && being && + being->getType() == ActorSprite::PLAYER) { Net::getTradeHandler()->request(being); tradePartnerName = being->getName(); @@ -221,27 +222,32 @@ void PopupMenu::handleLink(const std::string &link) { chatWindow->addInputText("/w \"" + being->getName() + "\" "); } - else if (link == "unignore" && being && being->getType() == Being::PLAYER) + else if (link == "unignore" && being && + being->getType() == ActorSprite::PLAYER) { player_relations.setRelation(being->getName(), PlayerRelation::NEUTRAL); } - else if (link == "ignore" && being && being->getType() == Being::PLAYER) + else if (link == "ignore" && being && + being->getType() == ActorSprite::PLAYER) { player_relations.setRelation(being->getName(), PlayerRelation::IGNORED); } - else if (link == "disregard" && being && being->getType() == Being::PLAYER) + else if (link == "disregard" && being && + being->getType() == ActorSprite::PLAYER) { player_relations.setRelation(being->getName(), PlayerRelation::DISREGARDED); } - else if (link == "friend" && being && being->getType() == Being::PLAYER) + else if (link == "friend" && being && + being->getType() == ActorSprite::PLAYER) { player_relations.setRelation(being->getName(), PlayerRelation::FRIEND); } // Guild action - else if (link == "guild" && being && being->getType() == Being::PLAYER) + else if (link == "guild" && being && + being->getType() == ActorSprite::PLAYER) { player_node->inviteToGuild(being); } @@ -257,25 +263,27 @@ void PopupMenu::handleLink(const std::string &link) { } - else if (link == "use") + else if (link == "activate") { assert(mItem); - if (mItem->isEquipment()) + if (mItem->isEquippable()) { if (mItem->isEquipped()) - Net::getInventoryHandler()->unequipItem(mItem); + mItem->doEvent(EVENT_DOUNEQUIP); else - Net::getInventoryHandler()->equipItem(mItem); + mItem->doEvent(EVENT_DOEQUIP); } else { - Net::getInventoryHandler()->useItem(mItem); + mItem->doEvent(EVENT_DOUSE); } } - else if (link == "chat") { - chatWindow->addItemText(mItem->getInfo().getName()); + if (mItem) + chatWindow->addItemText(mItem->getInfo().getName()); + else if (mFloorItem) + chatWindow->addItemText(mFloorItem->getInfo().getName()); } else if (link == "split") @@ -302,9 +310,10 @@ void PopupMenu::handleLink(const std::string &link) mItem); } - else if (link == "party" && being && being->getType() == Being::PLAYER) + else if (link == "party" && being && + being->getType() == ActorSprite::PLAYER) { - Net::getPartyHandler()->invite(static_cast<Player*>(being)); + Net::getPartyHandler()->invite(being); } else if (link == "name" && being) @@ -315,8 +324,8 @@ void PopupMenu::handleLink(const std::string &link) else if (link == "admin-kick" && being && - (being->getType() == Being::PLAYER || - being->getType() == Being::MONSTER)) + (being->getType() == ActorSprite::PLAYER || + being->getType() == ActorSprite::MONSTER)) { Net::getAdminHandler()->kick(being->getId()); } @@ -344,15 +353,15 @@ void PopupMenu::showPopup(Window *parent, int x, int y, Item *item, if (isInventory) { - if (item->isEquipment()) + if (item->getInfo().getEquippable()) { if (item->isEquipped()) - mBrowserBox->addRow(strprintf("@@use|%s@@", _("Unequip"))); + mBrowserBox->addRow(strprintf("@@equip|%s@@", _("Unequip"))); else - mBrowserBox->addRow(strprintf("@@use|%s@@", _("Equip"))); + mBrowserBox->addRow(strprintf("@@equip|%s@@", _("Equip"))); } - else - mBrowserBox->addRow(strprintf("@@use|%s@@", _("Use"))); + if (item->getInfo().getActivatable()) + mBrowserBox->addRow(strprintf("@@activate|%s@@", _("Activate"))); if (item->getQuantity() > 1) mBrowserBox->addRow(strprintf("@@drop|%s@@", _("Drop..."))); @@ -364,7 +373,7 @@ void PopupMenu::showPopup(Window *parent, int x, int y, Item *item, mBrowserBox->addRow(strprintf("@@split|%s@@", _("Split"))); } - if (InventoryWindow::isStorageActive()) + if (PlayerInfo::getStorageCount() > 0) { mBrowserBox->addRow(strprintf("@@store|%s@@", _("Store"))); } diff --git a/src/gui/quitdialog.h b/src/gui/quitdialog.h index 8fa1052c..d0dc2c69 100644 --- a/src/gui/quitdialog.h +++ b/src/gui/quitdialog.h @@ -22,8 +22,6 @@ #ifndef QUITDIALOG_H #define QUITDIALOG_H -#include "guichanfwd.h" - #include "gui/widgets/window.h" #include <guichan/actionlistener.hpp> diff --git a/src/gui/recorder.cpp b/src/gui/recorder.cpp index 257afd7f..2345369e 100644 --- a/src/gui/recorder.cpp +++ b/src/gui/recorder.cpp @@ -21,11 +21,11 @@ #include "gui/recorder.h" #include "client.h" +#include "event.h" #include "gui/chat.h" #include "gui/widgets/button.h" -#include "gui/widgets/chattab.h" #include "gui/widgets/layout.h" #include "gui/widgets/windowcontainer.h" @@ -84,16 +84,16 @@ void Recorder::setRecordingFile(const std::string &msg) * Message should go after mStream is closed so that it isn't * recorded. */ - localChatTab->chatLog(_("Finishing recording."), BY_SERVER); + SERVER_NOTICE(_("Finishing recording.")) } else { - localChatTab->chatLog(_("Not currently recording."), BY_SERVER); + SERVER_NOTICE(_("Not currently recording.")) } } else if (mStream.is_open()) { - localChatTab->chatLog(_("Already recording."), BY_SERVER); + SERVER_NOTICE(_("Already recording.")) } else { @@ -101,7 +101,7 @@ void Recorder::setRecordingFile(const std::string &msg) * Message should go before mStream is opened so that it isn't * recorded. */ - localChatTab->chatLog(_("Starting to record..."), BY_SERVER); + SERVER_NOTICE(_("Starting to record...")) const std::string file = Client::getLocalDataDirectory() + "/" + msgCopy; mStream.open(file.c_str(), std::ios_base::trunc); @@ -109,7 +109,7 @@ void Recorder::setRecordingFile(const std::string &msg) if (mStream.is_open()) setVisible(true); else - localChatTab->chatLog(_("Failed to start recording."), BY_SERVER); + SERVER_NOTICE(_("Failed to start recording.")) } } diff --git a/src/gui/register.h b/src/gui/register.h index 645b0be8..3c65695b 100644 --- a/src/gui/register.h +++ b/src/gui/register.h @@ -22,8 +22,6 @@ #ifndef REGISTER_H #define REGISTER_H -#include "player.h" - #include "gui/widgets/window.h" #include <guichan/actionlistener.hpp> diff --git a/src/gui/sell.cpp b/src/gui/sell.cpp index 13e0ba99..f33111d7 100644 --- a/src/gui/sell.cpp +++ b/src/gui/sell.cpp @@ -21,7 +21,7 @@ #include "gui/sell.h" -#include "npc.h" +#include "playerinfo.h" #include "shopitem.h" #include "units.h" @@ -111,6 +111,8 @@ SellDialog::SellDialog(int npcId): instances.push_back(this); setVisible(true); + + PlayerInfo::setBuySellState(BUYSELL_SELLING); } SellDialog::~SellDialog() @@ -118,6 +120,9 @@ SellDialog::~SellDialog() delete mShopItems; instances.remove(this); + + if (PlayerInfo::getBuySellState() == BUYSELL_SELLING) + PlayerInfo::setBuySellState(BUYSELL_NONE); } void SellDialog::reset() @@ -197,6 +202,11 @@ void SellDialog::action(const gcn::ActionEvent &event) // the inventory index of the next Duplicate otherwise. itemIndex = item->getCurrentInvIndex(); sellCount = item->sellCurrentDuplicate(mAmountItems); + + // For Manaserv, the Item id is to be given as index. + if ((Net::getNetworkType() == ServerInfo::MANASERV)) + itemIndex = item->getId(); + Net::getNpcHandler()->sellItem(mNpcId, itemIndex, sellCount); mAmountItems -= sellCount; } diff --git a/src/gui/sell.h b/src/gui/sell.h index 32a4dc55..c286dcc2 100644 --- a/src/gui/sell.h +++ b/src/gui/sell.h @@ -86,11 +86,6 @@ class SellDialog : public Window, gcn::ActionListener, gcn::SelectionListener void setVisible(bool visible); /** - * Returns true if any instances exist. - */ - static bool isActive() { return instances.size() > 0; } - - /** * Closes all instances. */ static void closeAll(); diff --git a/src/gui/serverdialog.cpp b/src/gui/serverdialog.cpp index abe898b0..ca8da9b4 100644 --- a/src/gui/serverdialog.cpp +++ b/src/gui/serverdialog.cpp @@ -21,6 +21,7 @@ #include "gui/serverdialog.h" +#include "chatlog.h" #include "client.h" #include "configuration.h" #include "gui.h" @@ -29,7 +30,6 @@ #include "gui/okdialog.h" #include "gui/sdlinput.h" -#include "gui/theme.h" #include "gui/widgets/button.h" #include "gui/widgets/dropdown.h" @@ -41,10 +41,11 @@ #include "net/net.h" +#include "resources/theme.h" + #include "utils/gettext.h" #include "utils/stringutils.h" #include "utils/xml.h" -#include "widgets/dropdown.h" #include <guichan/font.hpp> @@ -198,18 +199,13 @@ ServerDialog::ServerDialog(ServerInfo *serverInfo, const std::string &dir): mDownloadStatus(DOWNLOADING_PREPARING), mDownloadProgress(-1.0f), mServers(ServerInfos()), -#ifndef MANASERV_SUPPORT - mManaservServers(ServerInfos()), -#endif mServerInfo(serverInfo) { setWindowName("ServerDialog"); Label *serverLabel = new Label(_("Server:")); Label *portLabel = new Label(_("Port:")); -#ifdef MANASERV_SUPPORT Label *typeLabel = new Label(_("Server type:")); -#endif mServerNameField = new TextField(mServerInfo->hostname); mPortField = new TextField(toString(mServerInfo->port)); @@ -247,7 +243,6 @@ ServerDialog::ServerDialog(ServerInfo *serverInfo, const std::string &dir): place(1, 0, mServerNameField, 4).setPadding(3); place(0, 1, portLabel); place(1, 1, mPortField, 4).setPadding(3); -#ifdef MANASERV_SUPPORT place(0, 2, typeLabel); place(1, 2, mTypeField, 4).setPadding(3); place(0, 3, usedScroll, 5, 5).setPadding(3); @@ -256,14 +251,6 @@ ServerDialog::ServerDialog(ServerInfo *serverInfo, const std::string &dir): place(1, 9, mDeleteButton); place(3, 9, mQuitButton); place(4, 9, mConnectButton); -#else - place(0, 2, usedScroll, 5, 5).setPadding(3); - place(0, 7, mDescription, 5); - place(0, 8, mManualEntryButton); - place(1, 8, mDeleteButton); - place(3, 8, mQuitButton); - place(4, 8, mConnectButton); -#endif // Make sure the list has enough height getLayout().setRowHeight(3, 80); @@ -360,6 +347,8 @@ void ServerDialog::action(const gcn::ActionEvent &event) // Save the selected server mServerInfo->save = true; + chatLogger->setServerName(mServerInfo->hostname); + saveCustomServers(*mServerInfo); Client::setState(STATE_CONNECT_SERVER); @@ -427,6 +416,16 @@ void ServerDialog::valueChanged(const gcn::SelectionEvent &) mDeleteButton->setEnabled(myServer.save); } +void ServerDialog::mouseClicked(gcn::MouseEvent &mouseEvent) +{ + if (mouseEvent.getSource() == mServersList && + isDoubleClick(mServersList->getSelected())) + { + action(gcn::ActionEvent(mConnectButton, + mConnectButton->getActionEventId())); + } +} + void ServerDialog::logic() { { @@ -468,12 +467,7 @@ void ServerDialog::setFieldsReadOnly(bool readOnly) mServersList->setSelected(-1); mServerNameField->setText(std::string()); -#ifdef MANASERV_SUPPORT mPortField->setText(std::string()); -#else - mPortField->setText(std::string("6901")); -#endif - mServerNameField->requestFocus(); } @@ -489,10 +483,10 @@ void ServerDialog::setFieldsReadOnly(bool readOnly) void ServerDialog::downloadServerList() { // Try to load the configuration value for the onlineServerList - std::string listFile = branding.getValue("onlineServerList", std::string()); + std::string listFile = branding.getStringValue("onlineServerList"); if (listFile.empty()) - listFile = config.getValue("onlineServerList", std::string()); + listFile = config.getStringValue("onlineServerList"); // Fall back to manasource.org when neither branding nor config set it if (listFile.empty()) @@ -594,11 +588,7 @@ void ServerDialog::loadServers() } } -#ifdef MANASERV_SUPPORT if (!found) -#else - if (!found && server.type != ServerInfo::MANASERV) -#endif mServers.push_back(server); } } @@ -625,14 +615,7 @@ void ServerDialog::loadCustomServers() server.save = true; -#ifdef MANASERV_SUPPORT mServers.push_back(server); -#else - if (server.type == ServerInfo::MANASERV) - mManaservServers.push_back(server); - else - mServers.push_back(server); -#endif } } @@ -675,27 +658,6 @@ void ServerDialog::saveCustomServers(const ServerInfo ¤tServer) ++savedServerCount; } -#ifndef MANASERV_SUPPORT - for (unsigned i = 0; - i < mManaservServers.size() && savedServerCount < MAX_SERVERLIST; ++i) - { - const ServerInfo &server = mManaservServers.at(i); - - // Only save servers that were loaded from settings - if (!(server.save && server.isValid())) - continue; - - const std::string index = toString(savedServerCount); - const std::string nameKey = "MostUsedServerName" + index; - const std::string typeKey = "MostUsedServerType" + index; - const std::string portKey = "MostUsedServerPort" + index; - - config.setValue(nameKey, toString(server.hostname)); - config.setValue(typeKey, serverTypeToString(server.type)); - config.setValue(portKey, toString(server.port)); - ++savedServerCount; - } -#endif // Insert an invalid entry at the end to make the loading stop there if (savedServerCount < MAX_SERVERLIST) config.setValue("MostUsedServerName" + toString(savedServerCount), ""); diff --git a/src/gui/serverdialog.h b/src/gui/serverdialog.h index aae8b2e0..f1d9c9b8 100644 --- a/src/gui/serverdialog.h +++ b/src/gui/serverdialog.h @@ -135,6 +135,8 @@ class ServerDialog : public Window, */ void valueChanged(const gcn::SelectionEvent &event); + void mouseClicked(gcn::MouseEvent &mouseEvent); + void logic(); protected: @@ -191,9 +193,6 @@ class ServerDialog : public Window, float mDownloadProgress; ServerInfos mServers; -#ifndef MANASERV_SUPPORT - ServerInfos mManaservServers; -#endif ServerInfo *mServerInfo; }; diff --git a/src/gui/setup.h b/src/gui/setup.h index 43e83a68..4be94bb8 100644 --- a/src/gui/setup.h +++ b/src/gui/setup.h @@ -22,8 +22,6 @@ #ifndef SETUP_H #define SETUP_H -#include "guichanfwd.h" - #include "gui/widgets/window.h" #include <guichan/actionlistener.hpp> diff --git a/src/gui/setup_audio.cpp b/src/gui/setup_audio.cpp index 2c6b89e8..8e9f5e98 100644 --- a/src/gui/setup_audio.cpp +++ b/src/gui/setup_audio.cpp @@ -35,10 +35,10 @@ #include "utils/gettext.h" Setup_Audio::Setup_Audio(): - mMusicVolume((int)config.getValue("musicVolume", 60)), - mSfxVolume((int)config.getValue("sfxVolume", 100)), - mSoundEnabled(config.getValue("sound", 0)), - mDownloadEnabled(config.getValue("download-music", false)), + mMusicVolume(config.getIntValue("musicVolume")), + mSfxVolume(config.getIntValue("sfxVolume")), + mSoundEnabled(config.getBoolValue("sound")), + mDownloadEnabled(config.getBoolValue("download-music")), mSoundCheckBox(new CheckBox(_("Sound"), mSoundEnabled)), mDownloadMusicCheckBox(new CheckBox(_("Download music"), mDownloadEnabled)), mSfxSlider(new Slider(0, sound.getMaxVolume())), @@ -82,14 +82,14 @@ void Setup_Audio::apply() { mSoundEnabled = mSoundCheckBox->isSelected(); mDownloadEnabled = mDownloadMusicCheckBox->isSelected(); - mSfxVolume = (int) config.getValue("sfxVolume", 100); - mMusicVolume = (int) config.getValue("musicVolume", 60); + mSfxVolume = config.getIntValue("sfxVolume"); + mMusicVolume = config.getIntValue("musicVolume"); config.setValue("sound", mSoundEnabled); // Display a message if user has selected to download music, // And if downloadmusic is not already enabled - if (mDownloadEnabled && !config.getValue("download-music", false)) + if (mDownloadEnabled && !config.getBoolValue("download-music")) { new OkDialog(_("Notice"),_("You may have to restart your client if you want to download new music")); } diff --git a/src/gui/setup_colors.cpp b/src/gui/setup_colors.cpp index 12dba82a..6b3b3fec 100644 --- a/src/gui/setup_colors.cpp +++ b/src/gui/setup_colors.cpp @@ -23,8 +23,6 @@ #include "configuration.h" #include "gui/gui.h" -#include "gui/theme.h" -#include "gui/userpalette.h" #include "gui/widgets/browserbox.h" #include "gui/widgets/itemlinkhandler.h" @@ -36,6 +34,9 @@ #include "gui/widgets/textfield.h" #include "gui/widgets/textpreview.h" +#include "resources/theme.h" +#include "resources/userpalette.h" + #include "utils/gettext.h" #include "utils/stringutils.h" diff --git a/src/gui/setup_joystick.cpp b/src/gui/setup_joystick.cpp index 965f5712..7ac5b5ed 100644 --- a/src/gui/setup_joystick.cpp +++ b/src/gui/setup_joystick.cpp @@ -40,7 +40,7 @@ Setup_Joystick::Setup_Joystick(): { setName(_("Joystick")); - mOriginalJoystickEnabled = !config.getValue("joystickEnabled", false); + mOriginalJoystickEnabled = !config.getBoolValue("joystickEnabled"); mJoystickEnabled->setSelected(mOriginalJoystickEnabled); mJoystickEnabled->addActionListener(this); diff --git a/src/gui/setup_players.cpp b/src/gui/setup_players.cpp index 93551689..e1948bb0 100644 --- a/src/gui/setup_players.cpp +++ b/src/gui/setup_players.cpp @@ -21,7 +21,7 @@ #include "gui/setup_players.h" -#include "beingmanager.h" +#include "actorspritemanager.h" #include "configuration.h" #include "log.h" @@ -214,6 +214,7 @@ public: #define ACTION_STRATEGY "strategy" #define ACTION_WHISPER_TAB "whisper tab" #define ACTION_SHOW_GENDER "show gender" +#define ACTION_ENABLE_CHAT_LOG "enable log" Setup_Players::Setup_Players(): mPlayerTableTitleModel(new StaticTableModel(1, COLUMNS_NR)), @@ -226,10 +227,12 @@ Setup_Players::Setup_Players(): mDefaultWhisper(new CheckBox(_("Allow whispers"), player_relations.getDefault() & PlayerRelation::WHISPER)), mDeleteButton(new Button(_("Delete"), ACTION_DELETE, this)), - mWhisperTab(config.getValue("whispertab", false)), + mWhisperTab(config.getBoolValue("whispertab")), mWhisperTabCheckBox(new CheckBox(_("Put all whispers in tabs"), mWhisperTab)), - mShowGender(config.getValue("showgender", false)), - mShowGenderCheckBox(new CheckBox(_("Show gender"), mShowGender)) + mShowGender(config.getBoolValue("showgender")), + mShowGenderCheckBox(new CheckBox(_("Show gender"), mShowGender)), + mEnableChatLog(config.getBoolValue("enableChatLog")), + mEnableChatLogCheckBox(new CheckBox(_("Enable Chat log"), mEnableChatLog)) { setName(_("Players")); @@ -279,6 +282,9 @@ Setup_Players::Setup_Players(): mShowGenderCheckBox->setActionEventId(ACTION_SHOW_GENDER); mShowGenderCheckBox->addActionListener(this); + mEnableChatLogCheckBox->setActionEventId(ACTION_ENABLE_CHAT_LOG); + mEnableChatLogCheckBox->addActionListener(this); + reset(); // Do the layout @@ -289,11 +295,12 @@ Setup_Players::Setup_Players(): place(0, 1, mPlayerScrollArea, 4, 4).setPadding(2); place(0, 5, mDeleteButton); place(0, 6, mShowGenderCheckBox, 2).setPadding(2); + place(0, 7, mEnableChatLogCheckBox, 2).setPadding(2); place(2, 5, ignore_action_label); place(2, 6, mIgnoreActionChoicesBox, 2).setPadding(2); - place(0, 7, mDefaultTrading); - place(0, 8, mDefaultWhisper); - place(0, 9, mWhisperTabCheckBox, 4).setPadding(4); + place(0, 8, mDefaultTrading); + place(0, 9, mDefaultWhisper); + place(0, 10, mWhisperTabCheckBox, 4).setPadding(4); player_relations.addListener(this); @@ -341,20 +348,24 @@ void Setup_Players::apply() PlayerRelation::WHISPER : 0)); config.setValue("whispertab", mWhisperTab); - bool showGender = config.getValue("showgender", false); + bool showGender = config.getBoolValue("showgender"); config.setValue("showgender", mShowGender); - if (beingManager && mShowGender != showGender) - beingManager->updatePlayerNames(); + if (actorSpriteManager && mShowGender != showGender) + actorSpriteManager->updatePlayerNames(); + + config.setValue("enableChatLog", mEnableChatLog); } void Setup_Players::cancel() { - mWhisperTab = config.getValue("whispertab", false); + mWhisperTab = config.getBoolValue("whispertab"); mWhisperTabCheckBox->setSelected(mWhisperTab); - mShowGender = config.getValue("showgender", false); + mShowGender = config.getBoolValue("showgender"); mShowGenderCheckBox->setSelected(mShowGender); + mEnableChatLog = config.getBoolValue("enableChatLog"); + mEnableChatLogCheckBox->setSelected(mEnableChatLog); } void Setup_Players::action(const gcn::ActionEvent &event) @@ -402,6 +413,10 @@ void Setup_Players::action(const gcn::ActionEvent &event) { mShowGender = mShowGenderCheckBox->isSelected(); } + else if (event.getId() == ACTION_ENABLE_CHAT_LOG) + { + mEnableChatLog = mEnableChatLogCheckBox->isSelected(); + } } void Setup_Players::updatedPlayer(const std::string &name) diff --git a/src/gui/setup_players.h b/src/gui/setup_players.h index 5337b213..a62ffe1f 100644 --- a/src/gui/setup_players.h +++ b/src/gui/setup_players.h @@ -70,6 +70,9 @@ private: bool mShowGender; gcn::CheckBox *mShowGenderCheckBox; + + bool mEnableChatLog; + gcn::CheckBox *mEnableChatLogCheckBox; }; #endif diff --git a/src/gui/setup_video.cpp b/src/gui/setup_video.cpp index 1a5e17b6..c8af218f 100644 --- a/src/gui/setup_video.cpp +++ b/src/gui/setup_video.cpp @@ -171,7 +171,7 @@ static const char *speechModeToString(Being::Speech mode) const char *Setup_Video::overlayDetailToString(int detail) { if (detail == -1) - detail = config.getValue("OverlayDetail", -1); + detail = config.getIntValue("OverlayDetail"); switch (detail) { @@ -185,7 +185,7 @@ const char *Setup_Video::overlayDetailToString(int detail) const char *Setup_Video::particleDetailToString(int detail) { if (detail == -1) - detail = 3 - config.getValue("particleEmitterSkip", -1); + detail = 3 - config.getIntValue("particleEmitterSkip"); switch (detail) { @@ -198,22 +198,20 @@ const char *Setup_Video::particleDetailToString(int detail) } Setup_Video::Setup_Video(): - mFullScreenEnabled(config.getValue("screen", false)), - mOpenGLEnabled(config.getValue("opengl", false)), - mCustomCursorEnabled(config.getValue("customcursor", true)), - mShowMonsterDamageEnabled(config.getValue("showMonstersTakedDamage", - false)), - mVisibleNamesEnabled(config.getValue("visiblenames", true)), - mParticleEffectsEnabled(config.getValue("particleeffects", true)), - mNameEnabled(config.getValue("showownname", false)), - mNPCLogEnabled(config.getValue("logNpcInGui", true)), - mPickupChatEnabled(config.getValue("showpickupchat", true)), - mPickupParticleEnabled(config.getValue("showpickupparticle", false)), - mOpacity(config.getValue("guialpha", 0.8)), - mFps((int) config.getValue("fpslimit", 60)), - mSDLTransparencyDisabled(config.getValue("disableTransparency", true)), - mSpeechMode(static_cast<Being::Speech>( - config.getValue("speech", Being::TEXT_OVERHEAD))), + mFullScreenEnabled(config.getBoolValue("screen")), + mOpenGLEnabled(config.getBoolValue("opengl")), + mCustomCursorEnabled(config.getBoolValue("customcursor")), + mShowMonsterDamageEnabled(config.getBoolValue("showMonstersTakedDamage")), + mVisibleNamesEnabled(config.getBoolValue("visiblenames")), + mParticleEffectsEnabled(config.getBoolValue("particleeffects")), + mNameEnabled(config.getBoolValue("showownname")), + mNPCLogEnabled(config.getBoolValue("logNpcInGui")), + mPickupChatEnabled(config.getBoolValue("showpickupchat")), + mPickupParticleEnabled(config.getBoolValue("showpickupparticle")), + mOpacity(config.getFloatValue("guialpha")), + mFps(config.getIntValue("fpslimit")), + mSDLTransparencyDisabled(config.getBoolValue("disableTransparency")), + mSpeechMode(static_cast<Being::Speech>(config.getIntValue("speech"))), mModeListModel(new ModeListModel), mModeList(new ListBox(mModeListModel)), mFsCheckBox(new CheckBox(_("Full screen"), mFullScreenEnabled)), @@ -238,13 +236,13 @@ Setup_Video::Setup_Video(): mFpsCheckBox(new CheckBox(_("FPS limit:"))), mFpsSlider(new Slider(10, 120)), mFpsLabel(new Label), - mOverlayDetail((int) config.getValue("OverlayDetail", 2)), + mOverlayDetail(config.getIntValue("OverlayDetail")), mOverlayDetailSlider(new Slider(0, 2)), mOverlayDetailField(new Label), - mParticleDetail(3 - (int) config.getValue("particleEmitterSkip", 1)), + mParticleDetail(3 - config.getIntValue("particleEmitterSkip")), mParticleDetailSlider(new Slider(0, 3)), mParticleDetailField(new Label), - mFontSize((int) config.getValue("fontSize", 11)), + mFontSize(config.getIntValue("fontSize")), mDisableSDLTransparencyCheckBox( new CheckBox(_("Disable transparency (Low CPU mode)"), mSDLTransparencyDisabled)) @@ -403,7 +401,7 @@ void Setup_Video::apply() { // Full screen changes bool fullscreen = mFsCheckBox->isSelected(); - if (fullscreen != (config.getValue("screen", false) == 1)) + if (fullscreen != config.getBoolValue("screen")) { /* The OpenGL test is only necessary on Windows, since switching * to/from full screen works fine on Linux. On Windows we'd have to @@ -414,7 +412,7 @@ void Setup_Video::apply() #if defined(WIN32) || defined(__APPLE__) // checks for opengl usage - if (!(config.getValue("opengl", false) == 1)) + if (!config.getBoolValue("opengl")) { #endif if (!graphics->setFullscreen(fullscreen)) @@ -497,21 +495,20 @@ void Setup_Video::apply() config.setValue("fontSize", mFontSizeDropDown->getSelected() + 10); // We sync old and new values at apply time - mFullScreenEnabled = config.getValue("screen", false); - mCustomCursorEnabled = config.getValue("customcursor", true); - mShowMonsterDamageEnabled = config.getValue("showMonstersTakedDamage", false); - mVisibleNamesEnabled = config.getValue("visiblenames", true); - mParticleEffectsEnabled = config.getValue("particleeffects", true); - mNameEnabled = config.getValue("showownname", false); - mNPCLogEnabled = config.getValue("logNpcInGui", true); - mSpeechMode = static_cast<Being::Speech>( - config.getValue("speech", Being::TEXT_OVERHEAD)); - mOpacity = config.getValue("guialpha", 0.8); - mOverlayDetail = (int) config.getValue("OverlayDetail", 2); - mOpenGLEnabled = config.getValue("opengl", false); - mPickupChatEnabled = config.getValue("showpickupchat", true); - mPickupParticleEnabled = config.getValue("showpickupparticle", false); - mSDLTransparencyDisabled = config.getValue("disableTransparency", true); + mFullScreenEnabled = config.getBoolValue("screen"); + mCustomCursorEnabled = config.getBoolValue("customcursor"); + mShowMonsterDamageEnabled = config.getBoolValue("showMonstersTakedDamage"); + mVisibleNamesEnabled = config.getBoolValue("visiblenames"); + mParticleEffectsEnabled = config.getBoolValue("particleeffects"); + mNameEnabled = config.getBoolValue("showownname"); + mNPCLogEnabled = config.getBoolValue("logNpcInGui"); + mSpeechMode = static_cast<Being::Speech>(config.getIntValue("speech")); + mOpacity = config.getFloatValue("guialpha"); + mOverlayDetail = config.getIntValue("OverlayDetail"); + mOpenGLEnabled = config.getBoolValue("opengl"); + mPickupChatEnabled = config.getBoolValue("showpickupchat"); + mPickupParticleEnabled = config.getBoolValue("showpickupparticle"); + mSDLTransparencyDisabled = config.getBoolValue("disableTransparency"); } void Setup_Video::cancel() diff --git a/src/gui/skilldialog.cpp b/src/gui/skilldialog.cpp index 207e3ded..be46132e 100644 --- a/src/gui/skilldialog.cpp +++ b/src/gui/skilldialog.cpp @@ -21,11 +21,11 @@ #include "gui/skilldialog.h" -#include "localplayer.h" #include "log.h" +#include "playerinfo.h" +#include "configuration.h" #include "gui/setup.h" -#include "gui/theme.h" #include "gui/widgets/button.h" #include "gui/widgets/container.h" @@ -43,6 +43,7 @@ #include "resources/image.h" #include "resources/resourcemanager.h" +#include "resources/theme.h" #include "utils/dtor.h" #include "utils/gettext.h" @@ -93,7 +94,8 @@ struct SkillInfo if (!icon) { - icon = Theme::getImageFromTheme("unknown-item.png"); + icon = Theme::getImageFromTheme( + paths.getStringValue("unknownItemFile")); } } @@ -246,10 +248,6 @@ void SkillDialog::action(const gcn::ActionEvent &event) { setVisible(false); } - else - { - printf("Unknown event '%s'\n", event.getId().c_str()); - } } std::string SkillDialog::update(int id) @@ -269,7 +267,7 @@ std::string SkillDialog::update(int id) void SkillDialog::update() { mPointsLabel->setCaption(strprintf(_("Skill points available: %d"), - player_node->getSkillPoints())); + PlayerInfo::getAttribute(SKILL_POINTS))); mPointsLabel->adjustSize(); for (SkillMap::iterator it = mSkills.begin(); it != mSkills.end(); it++) @@ -420,10 +418,10 @@ void SkillModel::updateVisibilities() void SkillInfo::update() { - int baseLevel = player_node->getAttributeBase(id); - int effLevel = player_node->getAttributeEffective(id); + int baseLevel = PlayerInfo::getStatBase(id); + int effLevel = PlayerInfo::getStatEffective(id); - std::pair<int, int> exp = player_node->getExperience(id); + std::pair<int, int> exp = PlayerInfo::getStatExperience(id); if (!modifiable && baseLevel == 0 && effLevel == 0 && exp.second == 0) { diff --git a/src/gui/skilldialog.h b/src/gui/skilldialog.h index 95f8ef25..3b1b1832 100644 --- a/src/gui/skilldialog.h +++ b/src/gui/skilldialog.h @@ -19,10 +19,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SKILL_H -#define SKILL_H - -#include "guichanfwd.h" +#ifndef SKILLDIALOG_H +#define SKILLDIALOG_H #include "gui/widgets/window.h" diff --git a/src/gui/socialwindow.cpp b/src/gui/socialwindow.cpp index d52e073c..b4321b25 100644 --- a/src/gui/socialwindow.cpp +++ b/src/gui/socialwindow.cpp @@ -20,22 +20,19 @@ #include "gui/socialwindow.h" -#include "beingmanager.h" +#include "event.h" #include "guild.h" #include "localplayer.h" #include "party.h" -#include "player.h" #include "gui/confirmdialog.h" #include "gui/okdialog.h" #include "gui/setup.h" #include "gui/textdialog.h" -#include "gui/theme.h" #include "gui/widgets/avatarlistbox.h" #include "gui/widgets/browserbox.h" #include "gui/widgets/button.h" -#include "gui/widgets/chattab.h" #include "gui/widgets/container.h" #include "gui/widgets/label.h" #include "gui/widgets/layouthelper.h" @@ -49,6 +46,8 @@ #include "net/guildhandler.h" #include "net/partyhandler.h" +#include "resources/theme.h" + #include "utils/dtor.h" #include "utils/gettext.h" #include "utils/stringutils.h" @@ -124,13 +123,10 @@ public: if (!name.empty()) { - Net::getGuildHandler()->invite(mGuild->getId(), name); - localChatTab->chatLog(strprintf(_("Invited user %s to guild %s."), - name.c_str(), - mGuild->getName().c_str()), - BY_SERVER); + SERVER_NOTICE(strprintf(_("Invited user %s to guild %s."), + name.c_str(), + mGuild->getName().c_str())) } - mInviteDialog = NULL; } else if (event.getId() == "~do invite") @@ -140,8 +136,8 @@ public: else if (event.getId() == "yes") { Net::getGuildHandler()->leave(mGuild->getId()); - localChatTab->chatLog(strprintf(_("Guild %s quit requested."), - mGuild->getName().c_str()), BY_SERVER); + SERVER_NOTICE(strprintf(_("Guild %s quit requested."), + mGuild->getName().c_str())) mConfirmDialog = NULL; } else if (event.getId() == "no") @@ -208,8 +204,8 @@ public: std::string name = mInviteDialog->getText(); if (!name.empty()) - Net::getPartyHandler()->invite(name); - + SERVER_NOTICE(strprintf(_("Invited user %s to party."), + name.c_str())) mInviteDialog = NULL; } else if (event.getId() == "~do invite") @@ -219,8 +215,8 @@ public: else if (event.getId() == "yes") { Net::getPartyHandler()->leave(); - localChatTab->chatLog(strprintf(_("Party %s quit requested."), - mParty->getName().c_str()), BY_SERVER); + SERVER_NOTICE(strprintf(_("Party %s quit requested."), + mParty->getName().c_str())) mConfirmDialog = NULL; } else if (event.getId() == "no") @@ -444,14 +440,14 @@ void SocialWindow::action(const gcn::ActionEvent &event) // check if they accepted the invite if (eventId == "yes") { - localChatTab->chatLog(strprintf(_("Accepted party invite from %s."), - mPartyInviter.c_str())); + SERVER_NOTICE(strprintf(_("Accepted party invite from %s."), + mPartyInviter.c_str())) Net::getPartyHandler()->inviteResponse(mPartyInviter, true); } else if (eventId == "no") { - localChatTab->chatLog(strprintf(_("Rejected party invite from %s."), - mPartyInviter.c_str())); + SERVER_NOTICE(strprintf(_("Rejected party invite from %s."), + mPartyInviter.c_str())) Net::getPartyHandler()->inviteResponse(mPartyInviter, false); } @@ -463,14 +459,14 @@ void SocialWindow::action(const gcn::ActionEvent &event) // check if they accepted the invite if (eventId == "yes") { - localChatTab->chatLog(strprintf(_("Accepted guild invite from %s."), - mPartyInviter.c_str())); + SERVER_NOTICE(strprintf(_("Accepted guild invite from %s."), + mPartyInviter.c_str())) Net::getGuildHandler()->inviteResponse(mGuildInvited, true); } else if (eventId == "no") { - localChatTab->chatLog(strprintf(_("Rejected guild invite from %s."), - mPartyInviter.c_str())); + SERVER_NOTICE(strprintf(_("Rejected guild invite from %s."), + mPartyInviter.c_str())) Net::getGuildHandler()->inviteResponse(mGuildInvited, false); } @@ -498,14 +494,15 @@ void SocialWindow::action(const gcn::ActionEvent &event) if (name.size() > 16) { - localChatTab->chatLog(_("Creating guild failed, please choose a " - "shorter name."), BY_SERVER); + SERVER_NOTICE(_("Creating guild failed, please choose a " + "shorter name.")); + return; } else if (!name.empty()) { Net::getGuildHandler()->create(name); - localChatTab->chatLog(strprintf(_("Creating guild called %s."), - name.c_str()), BY_SERVER); + SERVER_NOTICE(strprintf(_("Creating guild called %s."), + name.c_str())); } mGuildCreateDialog = NULL; @@ -520,14 +517,15 @@ void SocialWindow::action(const gcn::ActionEvent &event) if (name.size() > 16) { - localChatTab->chatLog(_("Creating party failed, please choose a " - "shorter name."), BY_SERVER); + SERVER_NOTICE(_("Creating party failed, please choose a " + "shorter name.")); + return; } else if (!name.empty()) { Net::getPartyHandler()->create(name); - localChatTab->chatLog(strprintf(_("Creating party called %s."), - name.c_str()), BY_SERVER); + SERVER_NOTICE(strprintf(_("Creating party called %s."), + name.c_str())); } mPartyCreateDialog = NULL; @@ -553,14 +551,14 @@ void SocialWindow::showGuildInvite(const std::string &guildName, // check there isnt already an invite showing if (mGuildInvited != 0) { - localChatTab->chatLog(_("Received guild request, but one already " - "exists."), BY_SERVER); + SERVER_NOTICE(_("Received guild request, but one already " + "exists.")) return; } std::string msg = strprintf(_("%s has invited you to join the guild %s."), inviterName.c_str(), guildName.c_str()); - localChatTab->chatLog(msg, BY_SERVER); + SERVER_NOTICE(msg) // show invite mGuildAcceptDialog = new ConfirmDialog(_("Accept Guild Invite"), msg, this); @@ -575,8 +573,7 @@ void SocialWindow::showPartyInvite(const std::string &partyName, // check there isnt already an invite showing if (mPartyInviter != "") { - localChatTab->chatLog(_("Received party request, but one already " - "exists."), BY_SERVER); + SERVER_NOTICE(_("Received party request, but one already exists.")) return; } @@ -607,7 +604,7 @@ void SocialWindow::showPartyInvite(const std::string &partyName, } } - localChatTab->chatLog(msg, BY_SERVER); + SERVER_NOTICE(msg) // show invite mPartyAcceptDialog = new ConfirmDialog(_("Accept Party Invite"), msg, this); diff --git a/src/gui/specialswindow.cpp b/src/gui/specialswindow.cpp index 44551825..b511e4a3 100644 --- a/src/gui/specialswindow.cpp +++ b/src/gui/specialswindow.cpp @@ -20,11 +20,9 @@ #include "gui/specialswindow.h" -#include "localplayer.h" #include "log.h" #include "gui/setup.h" -#include "gui/theme.h" #include "gui/widgets/button.h" #include "gui/widgets/container.h" @@ -42,6 +40,9 @@ #include "net/net.h" #include "net/specialhandler.h" +#include "resources/specialdb.h" +#include "resources/theme.h" + #include "utils/dtor.h" #include "utils/gettext.h" #include "utils/stringutils.h" @@ -54,31 +55,24 @@ class SpecialEntry; -struct SpecialInfo -{ - unsigned short id; - std::string name; - std::string icon; - SpecialEntry *display; -}; class SpecialEntry : public Container { public: SpecialEntry(SpecialInfo *info); - void update(); + void update(int current, int needed); protected: friend class SpecialsWindow; SpecialInfo *mInfo; private: - Icon *mIcon; - Label *mNameLabel; - Label *mLevelLabel; - Label *mTechLabel; - Button *mUse; + Icon *mIcon; // icon to display + Label *mNameLabel; // name to display + Label *mLevelLabel; // level number label (only shown when applicable) + Button *mUse; // use button (only shown when applicable) + ProgressBar *mRechargeBar; // recharge bar (only shown when applicable) }; SpecialsWindow::SpecialsWindow(): @@ -102,7 +96,6 @@ SpecialsWindow::SpecialsWindow(): SpecialsWindow::~SpecialsWindow() { // Clear gui - loadSpecials(""); } void SpecialsWindow::action(const gcn::ActionEvent &event) @@ -127,91 +120,70 @@ void SpecialsWindow::action(const gcn::ActionEvent &event) } } -std::string SpecialsWindow::update(int id) -{ - // TODO - - return std::string(); -} - -void SpecialsWindow::loadSpecials(const std::string &file) +void SpecialsWindow::draw(gcn::Graphics *graphics) { - // TODO: mTabs->clear(); - while (mTabs->getSelectedTabIndex() != -1) + // update the progress bars + std::map<int, Special> specialData = PlayerInfo::getSpecialStatus(); + bool foundNew = false; + unsigned int found = 0; // number of entries in specialData which match mEntries + + for (std::map<int, Special>::iterator i = specialData.begin(); + i != specialData.end(); + i++) { - mTabs->removeTabWithIndex(mTabs->getSelectedTabIndex()); - } - - for (SpecialMap::iterator it = mSpecials.begin(); it != mSpecials.end(); it++) - { - delete (*it).second->display; + std::map<int, SpecialEntry *>::iterator e = mEntries.find(i->first); + if (e == mEntries.end()) + { + // found a new special - abort update and rebuild from scratch + foundNew = true; + break; + } else { + // update progress bar of special + e->second->update(i->second.currentMana, i->second.neededMana); + found++; + } } - delete_all(mSpecials); - mSpecials.clear(); + // a rebuild is needed when a) the number of specials changed or b) an existing entry isn't found anymore + if (foundNew || found != mEntries.size()) rebuild(specialData); - if (file.length() == 0) - return; + Window::draw(graphics); +} - XML::Document doc(file); - xmlNodePtr root = doc.rootNode(); +void SpecialsWindow::rebuild(const std::map<int, Special> &specialData) +{ + make_dtor(mEntries); + mEntries.clear(); + int vPos = 0; //vertical position of next placed element - if (!root || !xmlStrEqual(root->name, BAD_CAST "specials")) + for (std::map<int, Special>::const_iterator i = specialData.begin(); + i != specialData.end(); + i++) { - logger->log("Error loading specials file: %s", file.c_str()); - return; - } - - int setCount = 0; - std::string setName; - ScrollArea *scroll; - FlowContainer *container; + logger->log("Updating special GUI for %d", i->first); - for_each_xml_child_node(set, root) - { - if (xmlStrEqual(set->name, BAD_CAST "set")) + SpecialInfo* info = SpecialDB::get(i->first); + if (info) { - setCount++; - setName = XML::getProperty(set, "name", strprintf(_("Specials Set %d"), setCount)); - - container = new FlowContainer(SPECIALS_WIDTH, SPECIALS_HEIGHT); - container->setOpaque(false); - scroll = new ScrollArea(container); - scroll->setOpaque(false); - scroll->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER); - scroll->setVerticalScrollPolicy(ScrollArea::SHOW_ALWAYS); - - mTabs->addTab(setName, scroll); - for_each_xml_child_node(node, set) - { - if (xmlStrEqual(node->name, BAD_CAST "special")) - { - int id = atoi(XML::getProperty(node, "id", "-1").c_str()); - if (id == -1) - continue; - std::string name = XML::getProperty(node, "name", strprintf(_("Special %d"), id)); - std::string icon = XML::getProperty(node, "icon", ""); - - SpecialInfo *special = new SpecialInfo; - special->id = id; - special->name = name; - special->icon = icon; - special->display = new SpecialEntry(special); - - container->add(special->display); - - mSpecials[id] = special; - } - } + info->rechargeCurrent = i->second.currentMana; + info->rechargeNeeded = i->second.neededMana; + SpecialEntry* entry = new SpecialEntry(info); + entry->setPosition(0, vPos); + vPos += entry->getHeight(); + add(entry); + mEntries[i->first] = entry; + } else { + logger->log("Warning: No info available of special %d", i->first); } } } + SpecialEntry::SpecialEntry(SpecialInfo *info) : mInfo(info), mIcon(NULL), - mNameLabel(new Label(info->name)), - mLevelLabel(new Label("999")), - mUse(new Button("Use", "use", specialsWindow)) + mLevelLabel(NULL), + mUse(NULL), + mRechargeBar(NULL) { setFrameSize(1); setOpaque(false); @@ -225,21 +197,42 @@ SpecialEntry::SpecialEntry(SpecialInfo *info) : mIcon->setPosition(1, 0); add(mIcon); + + mNameLabel = new Label(info->name); mNameLabel->setPosition(35, 0); add(mNameLabel); - mLevelLabel->setPosition(getWidth() - mLevelLabel->getWidth(), 0); - add(mLevelLabel); + if (info->hasLevel) + { + mLevelLabel = new Label(toString(info->level)); + mLevelLabel->setPosition(getWidth() - mLevelLabel->getWidth(), 0); + add(mLevelLabel); + } + - mNameLabel->setWidth(mLevelLabel->getX() - mNameLabel->getX() - 1); + if (info->isActive) + { + mUse = new Button("Use", "use", specialsWindow); + mUse->setPosition(getWidth() - mUse->getWidth(), 13); + add(mUse); + } - mUse->setPosition(getWidth() - mUse->getWidth(), 13); - add(mUse); + if (info->hasRechargeBar) + { + float progress = (float)info->rechargeCurrent / (float)info->rechargeNeeded; + mRechargeBar = new ProgressBar(progress, 100, 10, Theme::PROG_MP); + mRechargeBar->setSmoothProgress(false); + mRechargeBar->setPosition(0, 13); + add(mRechargeBar); + } - update(); } -void SpecialEntry::update() +void SpecialEntry::update(int current, int needed) { - // TODO + if (mRechargeBar) + { + float progress = (float)current / (float)needed; + mRechargeBar->setProgress(progress); + } } diff --git a/src/gui/specialswindow.h b/src/gui/specialswindow.h index 81384856..dedeeffc 100644 --- a/src/gui/specialswindow.h +++ b/src/gui/specialswindow.h @@ -23,7 +23,7 @@ #include <vector> -#include "guichanfwd.h" +#include "playerinfo.h" #include "gui/widgets/window.h" @@ -36,7 +36,7 @@ class ScrollArea; class Tab; class TabbedArea; -struct SpecialInfo; +struct SpecialEntry; class SpecialsWindow : public Window, public gcn::ActionListener { public: @@ -49,20 +49,14 @@ class SpecialsWindow : public Window, public gcn::ActionListener { */ void action(const gcn::ActionEvent &actionEvent); - /** - * Update the given special's display - */ - std::string update(int id); - - void loadSpecials(const std::string &file); - - bool hasSpecials() { return !mSpecials.empty(); } + void draw(gcn::Graphics *graphics); private: - std::vector<gcn::Button *> mSpellButtons; - typedef std::map<int, SpecialInfo*> SpecialMap; - SpecialMap mSpecials; + // (re)constructs the list of specials + void rebuild(const std::map<int, Special> &specialData); + TabbedArea *mTabs; + std::map<int, SpecialEntry *> mEntries; }; extern SpecialsWindow *specialsWindow; diff --git a/src/gui/speechbubble.cpp b/src/gui/speechbubble.cpp index 08d00038..e0a9f8a0 100644 --- a/src/gui/speechbubble.cpp +++ b/src/gui/speechbubble.cpp @@ -25,10 +25,12 @@ #include "graphics.h" #include "gui/gui.h" -#include "gui/theme.h" +#include "gui/widgets/label.h" #include "gui/widgets/textbox.h" +#include "resources/theme.h" + #include <guichan/font.hpp> #include <guichan/widgets/label.hpp> @@ -40,7 +42,7 @@ SpeechBubble::SpeechBubble(): setMinWidth(29); setMinHeight(29); - mCaption = new gcn::Label; + mCaption = new Label; mCaption->setFont(boldFont); mSpeechBox = new TextBox; diff --git a/src/gui/speechbubble.h b/src/gui/speechbubble.h index 8682ab7e..6017398a 100644 --- a/src/gui/speechbubble.h +++ b/src/gui/speechbubble.h @@ -23,10 +23,10 @@ #ifndef SPEECHBUBBLE_H #define SPEECHBUBBLE_H -#include "gui/theme.h" - #include "gui/widgets/popup.h" +#include "resources/theme.h" + class TextBox; class SpeechBubble : public Popup diff --git a/src/gui/statuswindow.cpp b/src/gui/statuswindow.cpp index 91f832f5..a3420f06 100644 --- a/src/gui/statuswindow.cpp +++ b/src/gui/statuswindow.cpp @@ -22,11 +22,10 @@ #include "gui/statuswindow.h" #include "localplayer.h" +#include "playerinfo.h" #include "units.h" -#include "gui/ministatus.h" #include "gui/setup.h" -#include "gui/theme.h" #include "gui/widgets/button.h" #include "gui/widgets/label.h" @@ -38,6 +37,9 @@ #include "net/net.h" #include "net/playerhandler.h" +#include "net/gamehandler.h" + +#include "resources/theme.h" #include "utils/gettext.h" #include "utils/mathutils.h" @@ -94,6 +96,8 @@ class ChangeDisplay : public AttrDisplay, gcn::ActionListener StatusWindow::StatusWindow(): Window(player_node->getName()) { + listen(CHANNEL_ATTRIBUTES); + setWindowName("Status"); setupWindow->registerWindowForReset(this); setResizable(true); @@ -109,21 +113,26 @@ StatusWindow::StatusWindow(): mLvlLabel = new Label(strprintf(_("Level: %d"), 0)); mMoneyLabel = new Label(strprintf(_("Money: %s"), "")); - int max = player_node->getMaxHp(); + int max = PlayerInfo::getAttribute(MAX_HP); mHpLabel = new Label(_("HP:")); - mHpBar = new ProgressBar(max ? (float) player_node->getHp() / max: 0, - 80, 15, Theme::PROG_HP); + mHpBar = new ProgressBar(max ? (float) PlayerInfo::getAttribute(HP) / max : + 0, 80, 15, Theme::PROG_HP); - max = player_node->getExpNeeded(); + max = PlayerInfo::getAttribute(EXP_NEEDED); mXpLabel = new Label(_("Exp:")); - mXpBar = new ProgressBar(max ? (float) player_node->getExp() / max : 0, - 80, 15, Theme::PROG_EXP); + mXpBar = new ProgressBar(max ? (float) PlayerInfo::getAttribute(EXP) / max : + 0, 80, 15, Theme::PROG_EXP); - max = player_node->getMaxMP(); - mMpLabel = new Label(_("MP:")); - mMpBar = new ProgressBar(max ? (float) player_node->getMaxMP() / max : 0, - 80, 15, Net::getPlayerHandler()->canUseMagic() ? + bool magicBar = Net::getGameHandler()->canUseMagicBar(); + if (magicBar) + { + max = PlayerInfo::getAttribute(MAX_MP); + mMpLabel = new Label(_("MP:")); + mMpBar = new ProgressBar(max ? + (float) PlayerInfo::getAttribute(MAX_MP) / max : + 0, 80, 15, Net::getPlayerHandler()->canUseMagic() ? Theme::PROG_MP : Theme::PROG_NO_MP); + } place(0, 0, mLvlLabel, 3); // 5, 0 Job Level @@ -132,9 +141,17 @@ StatusWindow::StatusWindow(): place(1, 1, mHpBar, 4); place(5, 1, mXpLabel).setPadding(3); place(6, 1, mXpBar, 5); - place(0, 2, mMpLabel).setPadding(3); - // 5, 2 and 6, 2 Job Progress Bar - place(1, 2, mMpBar, 4); + + int attributesFirstRow = 2; + if (magicBar) + { + place(0, 2, mMpLabel).setPadding(3); + // 5, 2 and 6, 2 Job Progress Bar + place(1, 2, mMpBar, 4); + + // We move the attribute row to the next one + attributesFirstRow = 3; + } if (Net::getPlayerHandler()->getJobLocation() > 0) { @@ -145,128 +162,131 @@ StatusWindow::StatusWindow(): place(5, 0, mJobLvlLabel, 3); place(5, 2, mJobLabel).setPadding(3); place(6, 2, mJobBar, 5); + + // We move the attribute row to the next one + attributesFirstRow = 3; } // ---------------------- // Stats Part // ---------------------- - mAttrCont = new VertContainer(32); + mAttrCont = new VertContainer(28); mAttrScroll = new ScrollArea(mAttrCont); mAttrScroll->setOpaque(false); mAttrScroll->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER); mAttrScroll->setVerticalScrollPolicy(ScrollArea::SHOW_AUTO); - place(0, 3, mAttrScroll, 5, 3); + place(0, attributesFirstRow, mAttrScroll, 5, 3); - mDAttrCont = new VertContainer(32); + mDAttrCont = new VertContainer(28); mDAttrScroll = new ScrollArea(mDAttrCont); mDAttrScroll->setOpaque(false); mDAttrScroll->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER); mDAttrScroll->setVerticalScrollPolicy(ScrollArea::SHOW_AUTO); - place(6, 3, mDAttrScroll, 5, 3); + place(6, attributesFirstRow, mDAttrScroll, 5, 3); - getLayout().setRowHeight(3, Layout::AUTO_SET); + getLayout().setRowHeight(attributesFirstRow, Layout::AUTO_SET); - mCharacterPointsLabel = new Label("C"); - place(0, 6, mCharacterPointsLabel, 5); + mCharacterPointsLabel = new Label("Character points: 0"); + place(0, attributesFirstRow + 3, mCharacterPointsLabel, 4); if (Net::getPlayerHandler()->canCorrectAttributes()) { - mCorrectionPointsLabel = new Label("C"); - place(0, 7, mCorrectionPointsLabel, 5); + mCorrectionPointsLabel = new Label("Correction points: 0"); + place(4, attributesFirstRow + 3, mCorrectionPointsLabel, 4); } loadWindowState(); - update(HP); - update(MP); - update(EXP); - update(MONEY); - update(CHAR_POINTS); // This also updates all attributes (none atm) - update(LEVEL); - int job = Net::getPlayerHandler()->getJobLocation(); - if (job > 0) - { - update(job); - } -} - -std::string StatusWindow::update(int id) -{ - if (miniStatusWindow) - miniStatusWindow->update(id); - - if (id == HP) - { - updateHPBar(mHpBar, true); - - return _("HP"); - } - else if (id == MP) - { + // Update bars + updateHPBar(mHpBar, true); + if (magicBar) updateMPBar(mMpBar, true); + updateXPBar(mXpBar, false); - return _("MP"); - } - else if (id == EXP) - { - updateXPBar(mXpBar, false); - return _("Exp"); - } - else if (id == MONEY) - { - int money = player_node->getMoney(); - mMoneyLabel->setCaption(strprintf(_("Money: %s"), - Units::formatCurrency(money).c_str())); - mMoneyLabel->adjustSize(); + mMoneyLabel->setCaption(strprintf(_("Money: %s"), + Units::formatCurrency(PlayerInfo::getAttribute(MONEY)).c_str())); + mMoneyLabel->adjustSize(); + mCharacterPointsLabel->setCaption(strprintf(_("Character points: %d"), + PlayerInfo::getAttribute(CHAR_POINTS))); + mCharacterPointsLabel->adjustSize(); - return _("Money"); - } - else if (id == Net::getPlayerHandler()->getJobLocation()) - { - mJobLvlLabel->setCaption(strprintf(_("Job: %d"), - player_node->getAttributeBase(id))); - mJobLvlLabel->adjustSize(); - - updateProgressBar(mJobBar, id, false); + mLvlLabel->setCaption(strprintf(_("Level: %d"), + PlayerInfo::getAttribute(LEVEL))); + mLvlLabel->adjustSize(); +} - return _("Job"); - } - else if (id == CHAR_POINTS) +void StatusWindow::event(Channels channel, const Mana::Event &event) +{ + if (event.getName() == EVENT_UPDATEATTRIBUTE) { - mCharacterPointsLabel->setCaption(strprintf(_("Character points: %d"), - player_node->getCharacterPoints())); - mCharacterPointsLabel->adjustSize(); - - if (Net::getPlayerHandler()->canCorrectAttributes()) + switch(event.getInt("id")) { - mCorrectionPointsLabel->setCaption(strprintf(_("Correction points: %d"), - player_node->getCorrectionPoints())); - mCorrectionPointsLabel->adjustSize(); + case HP: case MAX_HP: + updateHPBar(mHpBar, true); + break; + + case MP: case MAX_MP: + updateMPBar(mMpBar, true); + break; + + case EXP: case EXP_NEEDED: + updateXPBar(mXpBar, false); + break; + + case MONEY: + mMoneyLabel->setCaption(strprintf(_("Money: %s"), + Units::formatCurrency( + event.getInt("newValue")).c_str())); + mMoneyLabel->adjustSize(); + break; + + case CHAR_POINTS: + mCharacterPointsLabel->setCaption(strprintf( + _("Character points: %d"), + event.getInt("newValue"))); + mCharacterPointsLabel->adjustSize(); + updateAttrs(); + break; + + case CORR_POINTS: + mCorrectionPointsLabel->setCaption(strprintf( + _("Correction points: %d"), + event.getInt("newValue"))); + mCorrectionPointsLabel->adjustSize(); + updateAttrs(); + break; + + case LEVEL: + mLvlLabel->setCaption(strprintf(_("Level: %d"), + event.getInt("newValue"))); + mLvlLabel->adjustSize(); + break; } - - updateAttrs(); } - else if (id == LEVEL) + else if (event.getName() == EVENT_UPDATESTAT) { - mLvlLabel->setCaption(strprintf(_("Level: %d"), - player_node->getLevel())); - mLvlLabel->adjustSize(); + int id = event.getInt("id"); - return _("Level"); - } - else - { - Attrs::iterator it = mAttrs.find(id); + if (id == Net::getPlayerHandler()->getJobLocation()) + { + + mJobLvlLabel->setCaption(strprintf(_("Job: %d"), + PlayerInfo::getStatBase(id))); + mJobLvlLabel->adjustSize(); - if (it != mAttrs.end()) + updateProgressBar(mJobBar, id, false); + } + else { - return it->second->update(); + Attrs::iterator it = mAttrs.find(id); + if (it != mAttrs.end()) + { + it->second->update(); + } } } - - return ""; } void StatusWindow::updateAttrs() @@ -311,32 +331,39 @@ void StatusWindow::addAttribute(int id, const std::string &name, void StatusWindow::updateHPBar(ProgressBar *bar, bool showMax) { + if (!bar) + return; if (showMax) - bar->setText(toString(player_node->getHp()) + - "/" + toString(player_node->getMaxHp())); + bar->setText(toString(PlayerInfo::getAttribute(HP)) + + "/" + toString(PlayerInfo::getAttribute(MAX_HP))); else - bar->setText(toString(player_node->getHp())); + bar->setText(toString(PlayerInfo::getAttribute(HP))); float prog = 1.0; - if (player_node->getMaxHp() > 0) - prog = (float) player_node->getHp() / player_node->getMaxHp(); + if (PlayerInfo::getAttribute(MAX_HP) > 0) + prog = (float) PlayerInfo::getAttribute(HP) + / PlayerInfo::getAttribute(MAX_HP); bar->setProgress(prog); } void StatusWindow::updateMPBar(ProgressBar *bar, bool showMax) { + if (!bar) + return; + if (showMax) - bar->setText(toString(player_node->getMP()) + - "/" + toString(player_node->getMaxMP())); + bar->setText(toString(PlayerInfo::getAttribute(MP)) + + "/" + toString(PlayerInfo::getAttribute(MAX_MP))); else - bar->setText(toString(player_node->getMP())); + bar->setText(toString(PlayerInfo::getAttribute(MP))); float prog = 1.0f; - if (player_node->getMaxMP() > 0) - prog = (float) player_node->getMP() / player_node->getMaxMP(); + if (PlayerInfo::getAttribute(MAX_MP) > 0) + prog = (float) PlayerInfo::getAttribute(MP) + / PlayerInfo::getAttribute(MAX_MP); if (Net::getPlayerHandler()->canUseMagic()) bar->setProgressPalette(Theme::PROG_MP); @@ -347,8 +374,11 @@ void StatusWindow::updateMPBar(ProgressBar *bar, bool showMax) } void StatusWindow::updateProgressBar(ProgressBar *bar, int value, int max, - bool percent) + bool percent) { + if (!bar) + return; + if (max == 0) { bar->setText(_("Max")); @@ -369,13 +399,16 @@ void StatusWindow::updateProgressBar(ProgressBar *bar, int value, int max, void StatusWindow::updateXPBar(ProgressBar *bar, bool percent) { - updateProgressBar(bar, player_node->getExp(), - player_node->getExpNeeded(), percent); + if (!bar) + return; + + updateProgressBar(bar, PlayerInfo::getAttribute(EXP), + PlayerInfo::getAttribute(EXP_NEEDED), percent); } void StatusWindow::updateProgressBar(ProgressBar *bar, int id, bool percent) { - std::pair<int, int> exp = player_node->getExperience(id); + std::pair<int, int> exp = PlayerInfo::getStatExperience(id); updateProgressBar(bar, exp.first, exp.second, percent); } @@ -400,8 +433,8 @@ AttrDisplay::~AttrDisplay() std::string AttrDisplay::update() { - int base = player_node->getAttributeBase(mId); - int bonus = player_node->getAttributeEffective(mId) - base; + int base = PlayerInfo::getStatBase(mId); + int bonus = PlayerInfo::getStatMod(mId); std::string value = toString(base); if (bonus) value += strprintf(" (%+d)", bonus); @@ -465,9 +498,9 @@ std::string ChangeDisplay::update() if (mDec) { - mDec->setEnabled(player_node->getCorrectionPoints()); + mDec->setEnabled(PlayerInfo::getAttribute(CORR_POINTS)); } - mInc->setEnabled(player_node->getCharacterPoints() >= mNeeded && + mInc->setEnabled(PlayerInfo::getAttribute(CHAR_POINTS) >= mNeeded && mNeeded > 0); return AttrDisplay::update(); @@ -485,24 +518,25 @@ void ChangeDisplay::action(const gcn::ActionEvent &event) if (Net::getPlayerHandler()->canCorrectAttributes() && event.getSource() == mDec) { - int newcorpoints = player_node->getCorrectionPoints() - 1; - player_node->setCorrectionPoints(newcorpoints); - int newpoints = player_node->getCharacterPoints() + 1; - player_node->setCharacterPoints(newpoints); - int newbase = player_node->getAttributeBase(mId) - 1; - player_node->setAttributeBase(mId, newbase); - int newmod = player_node->getAttributeEffective(mId) - 1; - player_node->setAttributeEffective(mId, newmod); + int newcorpoints = PlayerInfo::getAttribute(CORR_POINTS) - 1; + PlayerInfo::setAttribute(CORR_POINTS, newcorpoints); + + int newpoints = PlayerInfo::getAttribute(CHAR_POINTS) + 1; + PlayerInfo::setAttribute(CHAR_POINTS, newpoints); + + int newbase = PlayerInfo::getStatBase(mId) - 1; + PlayerInfo::setStatBase(mId, newbase); + Net::getPlayerHandler()->decreaseAttribute(mId); } else if (event.getSource() == mInc) { - int newpoints = player_node->getCharacterPoints() - 1; - player_node->setCharacterPoints(newpoints); - int newbase = player_node->getAttributeBase(mId) + 1; - player_node->setAttributeBase(mId, newbase); - int newmod = player_node->getAttributeEffective(mId) + 1; - player_node->setAttributeEffective(mId, newmod); + int newpoints = PlayerInfo::getAttribute(CHAR_POINTS) - 1; + PlayerInfo::setAttribute(CHAR_POINTS, newpoints); + + int newbase = PlayerInfo::getStatBase(mId) + 1; + PlayerInfo::setStatBase(mId, newbase); + Net::getPlayerHandler()->increaseAttribute(mId); } } diff --git a/src/gui/statuswindow.h b/src/gui/statuswindow.h index d99368b8..103111a7 100644 --- a/src/gui/statuswindow.h +++ b/src/gui/statuswindow.h @@ -22,7 +22,7 @@ #ifndef STATUS_H #define STATUS_H -#include "guichanfwd.h" +#include "listener.h" #include "gui/widgets/window.h" @@ -40,24 +40,15 @@ class VertContainer; * * \ingroup Interface */ -class StatusWindow : public Window +class StatusWindow : public Window, public Mana::Listener { public: - enum { // Some update constants - HP = -1, - MP = -2, - EXP = -3, - MONEY = -4, - CHAR_POINTS = -5, - LEVEL = -6 - }; - /** * Constructor. */ StatusWindow(); - std::string update(int id); + void event(Channels channel, const Mana::Event &event); void updateAttrs(); diff --git a/src/gui/textdialog.cpp b/src/gui/textdialog.cpp index d9728357..f88a6afa 100644 --- a/src/gui/textdialog.cpp +++ b/src/gui/textdialog.cpp @@ -21,7 +21,7 @@ #include "gui/textdialog.h" -#include "beingmanager.h" +#include "actorspritemanager.h" #include "gui/widgets/button.h" #include "gui/widgets/label.h" @@ -42,7 +42,7 @@ TextDialog::TextDialog(const std::string &title, const std::string &msg, // In TextField the escape key will either cause autoComplete or lose focus mTextField = new TextField("", ! autoCompleteEnabled); if (autoCompleteEnabled) - mTextField->setAutoComplete(beingManager->getPlayerNameLister()); + mTextField->setAutoComplete(actorSpriteManager->getPlayerNameLister()); mTextField->addActionListener(this); diff --git a/src/gui/textpopup.cpp b/src/gui/textpopup.cpp index 6aeae319..d0a0c495 100644 --- a/src/gui/textpopup.cpp +++ b/src/gui/textpopup.cpp @@ -26,6 +26,8 @@ #include "gui/gui.h" #include "gui/palette.h" +#include "gui/widgets/label.h" + #include "graphics.h" #include "units.h" @@ -40,10 +42,10 @@ TextPopup::TextPopup(): { const int fontHeight = getFont()->getHeight(); - mText1 = new gcn::Label; + mText1 = new Label; mText1->setPosition(getPadding(), getPadding()); - mText2 = new gcn::Label; + mText2 = new Label; mText2->setPosition(getPadding(), fontHeight + getPadding()); add(mText1); diff --git a/src/gui/trade.cpp b/src/gui/trade.cpp index dcb38e8e..37662bef 100644 --- a/src/gui/trade.cpp +++ b/src/gui/trade.cpp @@ -21,9 +21,11 @@ #include "gui/trade.h" +#include "event.h" #include "inventory.h" #include "item.h" #include "localplayer.h" +#include "playerinfo.h" #include "units.h" #include "gui/inventorywindow.h" @@ -31,7 +33,6 @@ #include "gui/setup.h" #include "gui/widgets/button.h" -#include "gui/widgets/chattab.h" #include "gui/widgets/itemcontainer.h" #include "gui/widgets/label.h" #include "gui/widgets/scrollarea.h" @@ -59,7 +60,7 @@ TradeWindow::TradeWindow(): mMyInventory(new Inventory(Inventory::TRADE)), mPartnerInventory(new Inventory(Inventory::TRADE)), mStatus(PROPOSING) -{ +{ setWindowName("Trade"); setResizable(true); setCloseButton(true); @@ -96,7 +97,7 @@ TradeWindow::TradeWindow(): mMoneyLabel = new Label(strprintf(_("You get %s"), "")); gcn::Label *mMoneyLabel2 = new Label(_("You give:")); - + mMoneyField = new TextField; mMoneyField->setWidth(40); mMoneyChangeButton = new Button(_("Change"), "money", this); @@ -140,18 +141,6 @@ void TradeWindow::addItem(int id, bool own, int quantity) (own ? mMyInventory : mPartnerInventory)->addItem(id, quantity); } -void TradeWindow::addItem(int id, bool own, int quantity, bool equipment) -{ - if (own) - { - mMyInventory->addItem(id, quantity, equipment); - } - else - { - mPartnerInventory->addItem(id, quantity, equipment); - } -} - void TradeWindow::changeQuantity(int index, bool own, int quantity) { if (own) @@ -270,9 +259,8 @@ void TradeWindow::action(const gcn::ActionEvent &event) if (mMyInventory->contains(item)) { - localChatTab->chatLog(_("Failed adding item. You can not " - "overlap one kind of item on the window."), - BY_SERVER); + SERVER_NOTICE(_("Failed adding item. You can not " + "overlap one kind of item on the window.")) return; } @@ -285,7 +273,7 @@ void TradeWindow::action(const gcn::ActionEvent &event) { setVisible(false); reset(); - player_node->setTrading(false); + PlayerInfo::setTrading(false); Net::getTradeHandler()->cancel(); } @@ -310,11 +298,10 @@ void TradeWindow::action(const gcn::ActionEvent &event) return; int v = atoi(mMoneyField->getText().c_str()); - int curMoney = player_node->getMoney(); + int curMoney = PlayerInfo::getAttribute(MONEY); if (v > curMoney) { - localChatTab->chatLog(_("You don't have enough money."), - BY_SERVER); + SERVER_NOTICE(_("You don't have enough money.")) v = curMoney; } Net::getTradeHandler()->setMoney(v); diff --git a/src/gui/trade.h b/src/gui/trade.h index ac602669..e22f8863 100644 --- a/src/gui/trade.h +++ b/src/gui/trade.h @@ -22,8 +22,6 @@ #ifndef TRADE_H #define TRADE_H -#include "guichanfwd.h" - #include "gui/widgets/window.h" #include <guichan/actionlistener.hpp> diff --git a/src/gui/unregisterdialog.h b/src/gui/unregisterdialog.h index dd330afd..87999d5d 100644 --- a/src/gui/unregisterdialog.h +++ b/src/gui/unregisterdialog.h @@ -22,8 +22,6 @@ #ifndef UNREGISTERDIALOG_H #define UNREGISTERDIALOG_H -#include "guichanfwd.h" - #include "gui/widgets/window.h" #include <guichan/actionlistener.hpp> diff --git a/src/gui/updatewindow.cpp b/src/gui/updatewindow.cpp index 7448a102..23d23b5e 100644 --- a/src/gui/updatewindow.cpp +++ b/src/gui/updatewindow.cpp @@ -476,7 +476,7 @@ void UpdaterWindow::logic() // This statement checks to see if the file type is music, and if download-music is true // If it fails, this statement returns true, and results in not downloading the file // Else it will ignore the break, and download the file. - if ( !(thisFile.type == "music" && config.getValue("download-music", false)) ) + if ( !(thisFile.type == "music" && config.getBoolValue("download-music")) ) { mUpdateIndex++; break; diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp index b18b9b0b..ac910d5f 100644 --- a/src/gui/viewport.cpp +++ b/src/gui/viewport.cpp @@ -21,16 +21,14 @@ #include "gui/viewport.h" +#include "actorspritemanager.h" #include "client.h" -#include "beingmanager.h" #include "configuration.h" -#include "flooritemmanager.h" #include "graphics.h" #include "keyboardconfig.h" #include "localplayer.h" #include "map.h" -#include "monster.h" -#include "npc.h" +#include "playerinfo.h" #include "textmanager.h" #include "gui/gui.h" @@ -40,7 +38,6 @@ #include "net/net.h" -#include "resources/monsterinfo.h" #include "resources/resourcemanager.h" #include "utils/stringutils.h" @@ -62,18 +59,18 @@ Viewport::Viewport(): setOpaque(false); addMouseListener(this); - mScrollLaziness = (int) config.getValue("ScrollLaziness", 16); - mScrollRadius = (int) config.getValue("ScrollRadius", 0); - mScrollCenterOffsetX = (int) config.getValue("ScrollCenterOffsetX", 0); - mScrollCenterOffsetY = (int) config.getValue("ScrollCenterOffsetY", 0); - - config.addListener("ScrollLaziness", this); - config.addListener("ScrollRadius", this); + mScrollLaziness = config.getIntValue("ScrollLaziness"); + mScrollRadius = config.getIntValue("ScrollRadius"); + mScrollCenterOffsetX = config.getIntValue("ScrollCenterOffsetX"); + mScrollCenterOffsetY = config.getIntValue("ScrollCenterOffsetY"); mPopupMenu = new PopupMenu; mBeingPopup = new BeingPopup; setFocusable(true); + + listen(CHANNEL_CONFIG); + listen(CHANNEL_ACTORSPRITE); } Viewport::~Viewport() @@ -207,12 +204,15 @@ void Viewport::draw(gcn::Graphics *gcnGraphics) } // Draw player names, speech, and emotion sprite as needed - const Beings &beings = beingManager->getAll(); - for (Beings::const_iterator i = beings.begin(), i_end = beings.end(); - i != i_end; ++i) + const ActorSprites &actors = actorSpriteManager->getAll(); + for (ActorSpritesConstIterator it = actors.begin(), it_end = actors.end(); + it != it_end; it++) { - (*i)->drawSpeech((int) mPixelViewX, (int) mPixelViewY); - (*i)->drawEmotion(graphics, (int) mPixelViewX, (int) mPixelViewY); + if ((*it)->getType() == ActorSprite::FLOOR_ITEM) + continue; + + Being *b = static_cast<Being*>(*it); + b->drawSpeech((int) mPixelViewX, (int) mPixelViewY); } if (miniStatusWindow) @@ -345,7 +345,7 @@ void Viewport::mousePressed(gcn::MouseEvent &event) return; // Check if we are busy - if (NPC::isTalking()) + if (PlayerInfo::isTalking()) return; mPlayerFollowMouse = false; @@ -354,9 +354,8 @@ void Viewport::mousePressed(gcn::MouseEvent &event) const int pixelX = event.getX() + (int) mPixelViewX; const int pixelY = event.getY() + (int) mPixelViewY; - mHoverBeing = beingManager->findBeingByPixel(pixelX, pixelY); - mHoverItem = floorItemManager-> - findByCoordinates(pixelX / mMap->getTileWidth(), + mHoverBeing = actorSpriteManager->findBeingByPixel(pixelX, pixelY); + mHoverItem = actorSpriteManager->findItem(pixelX / mMap->getTileWidth(), pixelY / mMap->getTileHeight()); updateCursorType(); @@ -389,33 +388,20 @@ void Viewport::mousePressed(gcn::MouseEvent &event) // Interact with some being if (mHoverBeing) { - switch (mHoverBeing->getType()) + if (mHoverBeing->canTalk()) + mHoverBeing->talkTo(); + else { - // Talk to NPCs - case Being::NPC: - static_cast<NPC*>(mHoverBeing)->talk(); - break; - - // Attack or walk to monsters or players - case Being::MONSTER: - case Being::PLAYER: - // Ignore it if its dead - if (!mHoverBeing->isAlive()) - break; - + // Ignore it if its dead + if (mHoverBeing->isAlive()) + { if (player_node->withinAttackRange(mHoverBeing) || keyboard.isKeyActive(keyboard.KEY_ATTACK)) - { player_node->attack(mHoverBeing, !keyboard.isKeyActive(keyboard.KEY_TARGET)); - } else - { player_node->setGotoTarget(mHoverBeing); - } - break; - default: - break; + } } // Picks up a item if we clicked on one } @@ -440,8 +426,8 @@ void Viewport::mousePressed(gcn::MouseEvent &event) else if (event.getButton() == gcn::MouseEvent::MIDDLE) { // Find the being nearest to the clicked position - Being *target = beingManager->findNearestLivingBeing( - pixelX, pixelY, 20, Being::MONSTER); + Being *target = actorSpriteManager->findNearestLivingBeing( + pixelX, pixelY, 20, ActorSprite::MONSTER); if (target) player_node->setTarget(target); @@ -467,9 +453,9 @@ void Viewport::mouseDragged(gcn::MouseEvent &event) } else { - if (mLocalWalkTime != player_node->getWalkTime()) + if (mLocalWalkTime != player_node->getActionTime()) { - mLocalWalkTime = player_node->getWalkTime(); + mLocalWalkTime = player_node->getActionTime(); int destX = (event.getX() + mPixelViewX) / mMap->getTileWidth(); int destY = (event.getY() + mPixelViewY) / mMap->getTileHeight(); player_node->setDestination(destX, destY); @@ -497,12 +483,6 @@ void Viewport::closePopupMenu() mPopupMenu->handleLink("cancel"); } -void Viewport::optionChanged(const std::string &name) -{ - mScrollLaziness = (int) config.getValue("ScrollLaziness", 32); - mScrollRadius = (int) config.getValue("ScrollRadius", 32); -} - void Viewport::mouseMoved(gcn::MouseEvent &event) { // Check if we are on the map @@ -512,15 +492,11 @@ void Viewport::mouseMoved(gcn::MouseEvent &event) const int x = (event.getX() + (int) mPixelViewX); const int y = (event.getY() + (int) mPixelViewY); - mHoverBeing = beingManager->findBeingByPixel(x, y); - if (mHoverBeing && mHoverBeing->getType() == Being::PLAYER) - mBeingPopup->show(getMouseX(), getMouseY(), - static_cast<Player*>(mHoverBeing)); - else - mBeingPopup->setVisible(false); + mHoverBeing = actorSpriteManager->findBeingByPixel(x, y); + mBeingPopup->show(getMouseX(), getMouseY(), mHoverBeing); - mHoverItem = floorItemManager->findByCoordinates(x / mMap->getTileWidth(), - y / mMap->getTileHeight()); + mHoverItem = actorSpriteManager->findItem(x / mMap->getTileWidth(), + y / mMap->getTileHeight()); updateCursorType(); } @@ -532,12 +508,12 @@ void Viewport::updateCursorType() switch (mHoverBeing->getType()) { // NPCs - case Being::NPC: + case ActorSprite::NPC: gui->setCursorType(Gui::CURSOR_TALK); break; // Monsters - case Being::MONSTER: + case ActorSprite::MONSTER: gui->setCursorType(Gui::CURSOR_FIGHT); break; default: @@ -559,7 +535,7 @@ void Viewport::updateCursorType() void Viewport::toggleDebugPath() { mShowDebugPath++; - if (mShowDebugPath > Map::MAP_SPECIAL) + if (mShowDebugPath > Map::MAP_SPECIAL3) mShowDebugPath = Map::MAP_NORMAL; if (mMap) { @@ -572,8 +548,26 @@ void Viewport::hideBeingPopup() mBeingPopup->setVisible(false); } -void Viewport::clearHoverBeing(Being *being) +void Viewport::event(Channels channel, const Mana::Event &event) { - if (mHoverBeing == being) - mHoverBeing = 0; + if (channel == CHANNEL_ACTORSPRITE && event.getName() == EVENT_DESTROYED) + { + ActorSprite *actor = event.getActor("source"); + + if (mHoverBeing == actor) + mHoverBeing = 0; + + if (mHoverItem == actor) + mHoverItem = 0; + } + else if (channel == CHANNEL_CONFIG && + event.getName() == EVENT_CONFIGOPTIONCHANGED) + { + const std::string option = event.getString("option"); + if (option == "ScrollLaziness" || option == "ScrollRadius") + { + mScrollLaziness = config.getIntValue("ScrollLaziness"); + mScrollRadius = config.getIntValue("ScrollRadius"); + } + } } diff --git a/src/gui/viewport.h b/src/gui/viewport.h index 616b88be..93e36b5b 100644 --- a/src/gui/viewport.h +++ b/src/gui/viewport.h @@ -22,14 +22,14 @@ #ifndef VIEWPORT_H #define VIEWPORT_H -#include "beingmanager.h" -#include "configlistener.h" +#include "listener.h" #include "position.h" #include "gui/widgets/windowcontainer.h" #include <guichan/mouselistener.hpp> +class ActorSprite; class Being; class BeingPopup; class FloorItem; @@ -52,7 +52,7 @@ const int walkingMouseDelay = 500; * coordinates. */ class Viewport : public WindowContainer, public gcn::MouseListener, - public ConfigListener + public Mana::Listener { public: /** @@ -119,11 +119,6 @@ class Viewport : public WindowContainer, public gcn::MouseListener, void closePopupMenu(); /** - * A relevant config option changed. - */ - void optionChanged(const std::string &name); - - /** * Returns camera x offset in pixels. */ int getCameraX() const { return (int) mPixelViewX; } @@ -158,11 +153,7 @@ class Viewport : public WindowContainer, public gcn::MouseListener, */ void hideBeingPopup(); - protected: - friend class BeingManager; - - /// Clears the hovered being if it matches - void clearHoverBeing(Being *being); + void event(Channels channel, const Mana::Event &event); private: /** diff --git a/src/gui/widgets/avatarlistbox.cpp b/src/gui/widgets/avatarlistbox.cpp index cc2c7d6a..60837ea1 100644 --- a/src/gui/widgets/avatarlistbox.cpp +++ b/src/gui/widgets/avatarlistbox.cpp @@ -24,10 +24,10 @@ #include "gui/gui.h" #include "gui/palette.h" -#include "gui/theme.h" #include "resources/image.h" #include "resources/resourcemanager.h" +#include "resources/theme.h" #include "utils/stringutils.h" diff --git a/src/gui/widgets/browserbox.cpp b/src/gui/widgets/browserbox.cpp index b74e4e0e..d553312b 100644 --- a/src/gui/widgets/browserbox.cpp +++ b/src/gui/widgets/browserbox.cpp @@ -24,10 +24,10 @@ #include "client.h" -#include "gui/theme.h" - #include "gui/widgets/linkhandler.h" +#include "resources/theme.h" + #include <guichan/graphics.hpp> #include <guichan/font.hpp> #include <guichan/cliprectangle.hpp> diff --git a/src/gui/widgets/button.cpp b/src/gui/widgets/button.cpp index 26e0ad90..3d3a07c2 100644 --- a/src/gui/widgets/button.cpp +++ b/src/gui/widgets/button.cpp @@ -25,9 +25,9 @@ #include "graphics.h" #include "gui/palette.h" -#include "gui/theme.h" #include "resources/image.h" +#include "resources/theme.h" #include "utils/dtor.h" @@ -125,8 +125,8 @@ Button::~Button() void Button::updateAlpha() { - float alpha = std::max(config.getValue("guialpha", 0.8f), - (double) Theme::instance()->getMinimumOpacity()); + float alpha = std::max(config.getFloatValue("guialpha"), + Theme::instance()->getMinimumOpacity()); if (mAlpha != alpha) { diff --git a/src/gui/widgets/chattab.cpp b/src/gui/widgets/chattab.cpp index bbb2b8bb..c86eb2ea 100644 --- a/src/gui/widgets/chattab.cpp +++ b/src/gui/widgets/chattab.cpp @@ -21,7 +21,8 @@ #include "gui/widgets/chattab.h" -#include "beingmanager.h" +#include "actorspritemanager.h" +#include "chatlog.h" #include "commandhandler.h" #include "configuration.h" #include "localplayer.h" @@ -51,7 +52,7 @@ ChatTab::ChatTab(const std::string &name) : Tab() mTextOutput = new BrowserBox(BrowserBox::AUTO_WRAP); mTextOutput->setOpaque(false); - mTextOutput->setMaxRow((int) config.getValue("ChatLogLength", 0)); + mTextOutput->setMaxRow((int) config.getIntValue("ChatLogLength")); mTextOutput->setLinkHandler(chatWindow->mItemLinkHandler); mTextOutput->setAlwaysUpdate(false); @@ -73,7 +74,7 @@ ChatTab::~ChatTab() delete mScrollArea; } -void ChatTab::chatLog(std::string line, int own, bool ignoreRecord) +void ChatTab::chatLog(std::string line, Own own, bool ignoreRecord) { // Trim whitespace trim(line); @@ -182,6 +183,9 @@ void ChatTab::chatLog(std::string line, int own, bool ignoreRecord) line = lineColor + timeStr.str() + tmp.nick + tmp.text; + if (config.getBoolValue("enableChatLog")) + saveToLogFile(line); + // We look if the Vertical Scroll Bar is set at the max before // adding a row, otherwise the max will always be a row higher // at comparison. @@ -234,7 +238,7 @@ void ChatTab::chatInput(const std::string &message) std::string temp = msg.substr(start + 1, end - start - 1); - const ItemInfo itemInfo = ItemDB::get(temp); + const ItemInfo itemInfo = itemDb->get(temp); if (itemInfo.getId() != 0) { msg.insert(end, "@@"); @@ -279,7 +283,13 @@ void ChatTab::handleCommand(const std::string &msg) void ChatTab::getAutoCompleteList(std::vector<std::string> &names) const { - beingManager->getPlayerNPCNameLister()->getAutoCompleteList(names); + actorSpriteManager->getPlayerNPCNameLister()->getAutoCompleteList(names); +} + +void ChatTab::saveToLogFile(std::string &msg) +{ + if (chatLogger) + chatLogger->log(msg); } void ChatTab::addRow(std::string &line) diff --git a/src/gui/widgets/chattab.h b/src/gui/widgets/chattab.h index c2dfa1c1..1e187f23 100644 --- a/src/gui/widgets/chattab.h +++ b/src/gui/widgets/chattab.h @@ -31,18 +31,6 @@ class BrowserBox; class Recorder; class ScrollArea; -enum -{ - BY_GM, - BY_PLAYER, - BY_OTHER, - BY_SERVER, - BY_CHANNEL, - ACT_WHISPER, // getting whispered at - ACT_IS, // equivalent to "/me" on IRC - BY_LOGGER -}; - /** * A tab for the chat window. This is special to ease chat handling. */ @@ -63,7 +51,8 @@ class ChatTab : public Tab, public AutoCompleteLister * @param channelName which channel to send the message to. * @param ignoreRecord should this not be recorded? */ - void chatLog(std::string line, int own = BY_SERVER, bool ignoreRecord = false); + void chatLog(std::string line, Own own = BY_SERVER, + bool ignoreRecord = false); /** * Adds the text to the message list @@ -112,8 +101,11 @@ class ChatTab : public Tab, public AutoCompleteLister const std::string &args) { return false; } + void getAutoCompleteList(std::vector<std::string> &names) const; + virtual void saveToLogFile(std::string &msg); + protected: friend class ChatWindow; friend class WhisperWindow; diff --git a/src/gui/widgets/checkbox.cpp b/src/gui/widgets/checkbox.cpp index f9002166..6a44132d 100644 --- a/src/gui/widgets/checkbox.cpp +++ b/src/gui/widgets/checkbox.cpp @@ -25,9 +25,9 @@ #include "graphics.h" #include "gui/palette.h" -#include "gui/theme.h" #include "resources/image.h" +#include "resources/theme.h" int CheckBox::instances = 0; float CheckBox::mAlpha = 1.0; @@ -92,8 +92,8 @@ void CheckBox::draw(gcn::Graphics* graphics) void CheckBox::updateAlpha() { - float alpha = std::max(config.getValue("guialpha", 0.8f), - (double) Theme::instance()->getMinimumOpacity()); + float alpha = std::max(config.getFloatValue("guialpha"), + Theme::instance()->getMinimumOpacity()); if (mAlpha != alpha) { diff --git a/src/gui/widgets/dropdown.cpp b/src/gui/widgets/dropdown.cpp index 6c3417e7..ced9c38b 100644 --- a/src/gui/widgets/dropdown.cpp +++ b/src/gui/widgets/dropdown.cpp @@ -26,12 +26,12 @@ #include "gui/palette.h" #include "gui/sdlinput.h" -#include "gui/theme.h" #include "gui/widgets/listbox.h" #include "gui/widgets/scrollarea.h" #include "resources/image.h" +#include "resources/theme.h" #include "utils/dtor.h" @@ -110,8 +110,8 @@ DropDown::~DropDown() void DropDown::updateAlpha() { - float alpha = std::max(config.getValue("guialpha", 0.8f), - (double) Theme::instance()->getMinimumOpacity()); + float alpha = std::max(config.getFloatValue("guialpha"), + Theme::instance()->getMinimumOpacity()); if (mAlpha != alpha) { diff --git a/src/gui/widgets/emoteshortcutcontainer.cpp b/src/gui/widgets/emoteshortcutcontainer.cpp index 82fb9f8d..f7a6ca2b 100644 --- a/src/gui/widgets/emoteshortcutcontainer.cpp +++ b/src/gui/widgets/emoteshortcutcontainer.cpp @@ -20,11 +20,11 @@ #include "gui/widgets/emoteshortcutcontainer.h" -#include "animatedsprite.h" #include "configuration.h" #include "emoteshortcut.h" #include "graphics.h" #include "inventory.h" +#include "imagesprite.h" #include "item.h" #include "itemshortcut.h" #include "keyboardconfig.h" @@ -32,10 +32,10 @@ #include "log.h" #include "gui/palette.h" -#include "gui/theme.h" #include "resources/emotedb.h" #include "resources/image.h" +#include "resources/theme.h" #include "utils/dtor.h" @@ -51,12 +51,12 @@ EmoteShortcutContainer::EmoteShortcutContainer(): mBackgroundImg = Theme::getImageFromTheme("item_shortcut_bgr.png"); - mBackgroundImg->setAlpha(config.getValue("guialpha", 0.8)); + mBackgroundImg->setAlpha(config.getFloatValue("guialpha")); // Setup emote sprites for (int i = 0; i <= EmoteDB::getLast(); i++) { - mEmoteImg.push_back(EmoteDB::getAnimation(i)); + mEmoteImg.push_back(EmoteDB::get(i)->sprite); } mMaxItems = EmoteDB::getLast() < MAX_ITEMS ? EmoteDB::getLast() : MAX_ITEMS; @@ -72,9 +72,9 @@ EmoteShortcutContainer::~EmoteShortcutContainer() void EmoteShortcutContainer::draw(gcn::Graphics *graphics) { - if (config.getValue("guialpha", 0.8) != mAlpha) + if (config.getFloatValue("guialpha") != mAlpha) { - mAlpha = config.getValue("guialpha", 0.8); + mAlpha = config.getFloatValue("guialpha"); mBackgroundImg->setAlpha(mAlpha); } @@ -106,7 +106,7 @@ void EmoteShortcutContainer::draw(gcn::Graphics *graphics) if (mEmoteMoved) { // Draw the emote image being dragged by the cursor. - const AnimatedSprite* sprite = mEmoteImg[mEmoteMoved - 1]; + const ImageSprite* sprite = mEmoteImg[mEmoteMoved - 1]; if (sprite) { const int tPosX = mCursorPosX - (sprite->getWidth() / 2); diff --git a/src/gui/widgets/emoteshortcutcontainer.h b/src/gui/widgets/emoteshortcutcontainer.h index e90612b4..c3fb9d14 100644 --- a/src/gui/widgets/emoteshortcutcontainer.h +++ b/src/gui/widgets/emoteshortcutcontainer.h @@ -25,8 +25,7 @@ #include <vector> -class AnimatedSprite; -class Image; +class ImageSprite; /** * An emote shortcut container. Used to quickly use emoticons. @@ -67,7 +66,7 @@ class EmoteShortcutContainer : public ShortcutContainer void mouseReleased(gcn::MouseEvent &event); private: - std::vector<const AnimatedSprite*> mEmoteImg; + std::vector<const ImageSprite*> mEmoteImg; bool mEmoteClicked; int mEmoteMoved; diff --git a/src/gui/widgets/itemcontainer.cpp b/src/gui/widgets/itemcontainer.cpp index fb5105b2..2e9b698d 100644 --- a/src/gui/widgets/itemcontainer.cpp +++ b/src/gui/widgets/itemcontainer.cpp @@ -32,7 +32,6 @@ #include "gui/outfitwindow.h" #include "gui/palette.h" #include "gui/sdlinput.h" -#include "gui/theme.h" #include "gui/viewport.h" #include "net/net.h" @@ -40,6 +39,7 @@ #include "resources/image.h" #include "resources/iteminfo.h" +#include "resources/theme.h" #include "utils/stringutils.h" @@ -263,7 +263,7 @@ void ItemContainer::mousePressed(gcn::MouseEvent &event) mSelectionStatus = SEL_SELECTING; itemShortcut->setItemSelected(item->getId()); - if (item->isEquipment()) + if (item->getInfo().getEquippable()) outfitWindow->setItemSelected(item->getId()); } else @@ -305,7 +305,14 @@ void ItemContainer::mouseReleased(gcn::MouseEvent &event) return; if (index == mSelectedIndex || mSelectedIndex == -1) return; - Net::getInventoryHandler()->moveItem(mSelectedIndex, index); + + Item *item = getSelectedItem(); + { + Mana::Event event(EVENT_DOMOVE); + event.setItem("item", item); + event.setInt("newIndex", index); + event.trigger(CHANNEL_ITEM); + } selectNone(); } @@ -372,8 +379,11 @@ void ItemContainer::keyAction() mSelectedIndex != -1 && mHighlightedIndex != -1) { - Net::getInventoryHandler()->moveItem( - mSelectedIndex, mHighlightedIndex); + Item *item = getSelectedItem(); + Mana::Event event(EVENT_DOMOVE); + event.setItem("item", item); + event.setInt("newIndex", mHighlightedIndex); + event.trigger(CHANNEL_ITEM); setSelectedIndex(mHighlightedIndex); } // If the highlight is on an item then select it. @@ -385,8 +395,11 @@ void ItemContainer::keyAction() // If the highlight is on a blank space then move it. else if (mSelectedIndex != -1) { - Net::getInventoryHandler()->moveItem( - mSelectedIndex, mHighlightedIndex); + Item *item = getSelectedItem(); + Mana::Event event(EVENT_DOMOVE); + event.setItem("item", item); + event.setInt("newIndex", mHighlightedIndex); + event.trigger(CHANNEL_ITEM); selectNone(); } } diff --git a/src/gui/widgets/itemlinkhandler.cpp b/src/gui/widgets/itemlinkhandler.cpp index b7341084..8477225f 100644 --- a/src/gui/widgets/itemlinkhandler.cpp +++ b/src/gui/widgets/itemlinkhandler.cpp @@ -49,7 +49,7 @@ void ItemLinkHandler::handleLink(const std::string &link) if (id > 0) { - const ItemInfo &itemInfo = ItemDB::get(id); + const ItemInfo &itemInfo = itemDb->get(id); mItemPopup->setItem(itemInfo, true); if (mItemPopup->isVisible()) diff --git a/src/gui/widgets/itemshortcutcontainer.cpp b/src/gui/widgets/itemshortcutcontainer.cpp index 682d71e5..fb4f558f 100644 --- a/src/gui/widgets/itemshortcutcontainer.cpp +++ b/src/gui/widgets/itemshortcutcontainer.cpp @@ -27,16 +27,16 @@ #include "item.h" #include "itemshortcut.h" #include "keyboardconfig.h" -#include "localplayer.h" +#include "playerinfo.h" #include "gui/inventorywindow.h" #include "gui/itempopup.h" #include "gui/palette.h" -#include "gui/theme.h" #include "gui/viewport.h" #include "resources/image.h" #include "resources/iteminfo.h" +#include "resources/theme.h" #include "utils/stringutils.h" @@ -53,7 +53,7 @@ ItemShortcutContainer::ItemShortcutContainer(): mBackgroundImg = Theme::getImageFromTheme("item_shortcut_bgr.png"); mMaxItems = itemShortcut->getItemCount(); - mBackgroundImg->setAlpha(config.getValue("guialpha", 0.8)); + mBackgroundImg->setAlpha(config.getFloatValue("guialpha")); mBoxHeight = mBackgroundImg->getHeight(); mBoxWidth = mBackgroundImg->getWidth(); @@ -67,9 +67,9 @@ ItemShortcutContainer::~ItemShortcutContainer() void ItemShortcutContainer::draw(gcn::Graphics *graphics) { - if (config.getValue("guialpha", 0.8) != mAlpha) + if (config.getFloatValue("guialpha") != mAlpha) { - mAlpha = config.getValue("guialpha", 0.8); + mAlpha = config.getFloatValue("guialpha"); mBackgroundImg->setAlpha(mAlpha); } @@ -94,7 +94,7 @@ void ItemShortcutContainer::draw(gcn::Graphics *graphics) continue; Item *item = - player_node->getInventory()->findItem(itemShortcut->getItem(i)); + PlayerInfo::getInventory()->findItem(itemShortcut->getItem(i)); if (item) { @@ -152,7 +152,7 @@ void ItemShortcutContainer::mouseDragged(gcn::MouseEvent &event) if (itemId < 0) return; - Item *item = player_node->getInventory()->findItem(itemId); + Item *item = PlayerInfo::getInventory()->findItem(itemId); if (item) { @@ -188,7 +188,7 @@ void ItemShortcutContainer::mousePressed(gcn::MouseEvent &event) } else if (event.getButton() == gcn::MouseEvent::RIGHT) { - Item *item = player_node->getInventory()-> + Item *item = PlayerInfo::getInventory()-> findItem(itemShortcut->getItem(index)); if (!item) @@ -241,7 +241,7 @@ void ItemShortcutContainer::mouseMoved(gcn::MouseEvent &event) if (itemId < 0) return; - Item *item = player_node->getInventory()->findItem(itemId); + Item *item = PlayerInfo::getInventory()->findItem(itemId); if (item) { diff --git a/src/gui/widgets/label.cpp b/src/gui/widgets/label.cpp index 4c607edf..939e9fc0 100644 --- a/src/gui/widgets/label.cpp +++ b/src/gui/widgets/label.cpp @@ -20,19 +20,20 @@ #include "gui/widgets/label.h" -#include "gui/theme.h" +#include "resources/theme.h" Label::Label() { + setForegroundColor(Theme::getThemeColor(Theme::TEXT)); } Label::Label(const std::string &caption) : gcn::Label(caption) { + setForegroundColor(Theme::getThemeColor(Theme::TEXT)); } void Label::draw(gcn::Graphics *graphics) { - setForegroundColor(Theme::getThemeColor(Theme::TEXT)); gcn::Label::draw(static_cast<gcn::Graphics*>(graphics)); } diff --git a/src/gui/widgets/listbox.cpp b/src/gui/widgets/listbox.cpp index ef591023..d79d8d0c 100644 --- a/src/gui/widgets/listbox.cpp +++ b/src/gui/widgets/listbox.cpp @@ -25,7 +25,8 @@ #include "gui/palette.h" #include "gui/sdlinput.h" -#include "gui/theme.h" + +#include "resources/theme.h" #include <guichan/font.hpp> #include <guichan/graphics.hpp> @@ -45,8 +46,8 @@ ListBox::~ListBox() void ListBox::updateAlpha() { - float alpha = std::max(config.getValue("guialpha", 0.8), - (double) Theme::instance()->getMinimumOpacity()); + float alpha = std::max(config.getFloatValue("guialpha"), + Theme::instance()->getMinimumOpacity()); if (mAlpha != alpha) mAlpha = alpha; diff --git a/src/gui/widgets/playerbox.cpp b/src/gui/widgets/playerbox.cpp index 57cbec6f..559ac5a6 100644 --- a/src/gui/widgets/playerbox.cpp +++ b/src/gui/widgets/playerbox.cpp @@ -22,13 +22,12 @@ #include "gui/widgets/playerbox.h" #include "animatedsprite.h" +#include "being.h" #include "configuration.h" #include "graphics.h" -#include "player.h" - -#include "gui/theme.h" #include "resources/image.h" +#include "resources/theme.h" #include "utils/dtor.h" @@ -36,8 +35,8 @@ int PlayerBox::instances = 0; float PlayerBox::mAlpha = 1.0; ImageRect PlayerBox::background; -PlayerBox::PlayerBox(const Player *player): - mPlayer(player) +PlayerBox::PlayerBox(const Being *being): + mBeing(being) { setFrameSize(2); @@ -57,7 +56,7 @@ PlayerBox::PlayerBox(const Player *player): bggridx[x], bggridy[y], bggridx[x + 1] - bggridx[x] + 1, bggridy[y + 1] - bggridy[y] + 1); - background.grid[a]->setAlpha(config.getValue("guialpha", 0.8)); + background.grid[a]->setAlpha(config.getFloatValue("guialpha")); a++; } } @@ -72,7 +71,7 @@ PlayerBox::~PlayerBox() { instances--; - mPlayer = 0; + mBeing = 0; if (instances == 0) { @@ -82,20 +81,20 @@ PlayerBox::~PlayerBox() void PlayerBox::draw(gcn::Graphics *graphics) { - if (mPlayer) + if (mBeing) { // Draw character const int bs = getFrameSize(); - const int x = getWidth() / 2 + bs; - const int y = getHeight() - bs; - mPlayer->drawSpriteAt(static_cast<Graphics*>(graphics), x, y); + const int x = getWidth() / 2 + bs - 16; + const int y = getHeight() - bs - 32; + mBeing->drawSpriteAt(static_cast<Graphics*>(graphics), x, y); } - if (config.getValue("guialpha", 0.8) != mAlpha) + if (config.getFloatValue("guialpha") != mAlpha) { for (int a = 0; a < 9; a++) { - background.grid[a]->setAlpha(config.getValue("guialpha", 0.8)); + background.grid[a]->setAlpha(config.getFloatValue("guialpha")); } } } diff --git a/src/gui/widgets/playerbox.h b/src/gui/widgets/playerbox.h index 33b4a628..4ce6782d 100644 --- a/src/gui/widgets/playerbox.h +++ b/src/gui/widgets/playerbox.h @@ -24,8 +24,8 @@ #include <guichan/widgets/scrollarea.hpp> +class Being; class ImageRect; -class Player; /** * A box showing a player character. @@ -39,7 +39,7 @@ class PlayerBox : public gcn::ScrollArea * Constructor. Takes the initial player character that this box should * display, which defaults to <code>NULL</code>. */ - PlayerBox(const Player *player = 0); + PlayerBox(const Being *being = 0); /** * Destructor. @@ -51,7 +51,8 @@ class PlayerBox : public gcn::ScrollArea * player to <code>NULL</code> causes the box not to draw any * character. */ - void setPlayer(const Player *player) { mPlayer = player; } + void setPlayer(const Being *being) + { mBeing = being; } /** * Draws the scroll area. @@ -64,7 +65,7 @@ class PlayerBox : public gcn::ScrollArea void drawFrame(gcn::Graphics *graphics); private: - const Player *mPlayer; /**< The character used for display */ + const Being *mBeing; /**< The character used for display */ static float mAlpha; static int instances; diff --git a/src/gui/widgets/popup.cpp b/src/gui/widgets/popup.cpp index 4dc58f72..e242bcf4 100644 --- a/src/gui/widgets/popup.cpp +++ b/src/gui/widgets/popup.cpp @@ -26,12 +26,12 @@ #include "graphics.h" #include "log.h" -#include "gui/theme.h" #include "gui/viewport.h" #include "gui/widgets/windowcontainer.h" #include "resources/image.h" +#include "resources/theme.h" #include <guichan/exception.hpp> diff --git a/src/gui/widgets/progressbar.cpp b/src/gui/widgets/progressbar.cpp index 028658ab..15838952 100644 --- a/src/gui/widgets/progressbar.cpp +++ b/src/gui/widgets/progressbar.cpp @@ -27,9 +27,9 @@ #include "gui/gui.h" #include "gui/palette.h" -#include "gui/theme.h" #include "resources/image.h" +#include "resources/theme.h" #include "utils/dtor.h" @@ -123,8 +123,8 @@ void ProgressBar::logic() void ProgressBar::updateAlpha() { - float alpha = std::max(config.getValue("guialpha", 0.8), - (double) Theme::instance()->getMinimumOpacity()); + float alpha = std::max(config.getFloatValue("guialpha"), + Theme::instance()->getMinimumOpacity()); if (mAlpha != alpha) { diff --git a/src/gui/widgets/progressindicator.cpp b/src/gui/widgets/progressindicator.cpp index 6bda617f..91b40751 100644 --- a/src/gui/widgets/progressindicator.cpp +++ b/src/gui/widgets/progressindicator.cpp @@ -23,11 +23,10 @@ #include "graphics.h" #include "simpleanimation.h" -#include "gui/theme.h" - #include "resources/animation.h" #include "resources/imageset.h" #include "resources/resourcemanager.h" +#include "resources/theme.h" #include <guichan/widgets/label.hpp> diff --git a/src/gui/widgets/radiobutton.cpp b/src/gui/widgets/radiobutton.cpp index 96797225..1296feb6 100644 --- a/src/gui/widgets/radiobutton.cpp +++ b/src/gui/widgets/radiobutton.cpp @@ -24,9 +24,8 @@ #include "configuration.h" #include "graphics.h" -#include "gui/theme.h" - #include "resources/image.h" +#include "resources/theme.h" int RadioButton::instances = 0; float RadioButton::mAlpha = 1.0; @@ -78,9 +77,9 @@ RadioButton::~RadioButton() void RadioButton::drawBox(gcn::Graphics* graphics) { - if (config.getValue("guialpha", 0.8) != mAlpha) + if (config.getFloatValue("guialpha") != mAlpha) { - mAlpha = config.getValue("guialpha", 0.8); + mAlpha = config.getFloatValue("guialpha"); radioNormal->setAlpha(mAlpha); radioChecked->setAlpha(mAlpha); radioDisabled->setAlpha(mAlpha); diff --git a/src/gui/widgets/resizegrip.cpp b/src/gui/widgets/resizegrip.cpp index f0c3691c..7850643f 100644 --- a/src/gui/widgets/resizegrip.cpp +++ b/src/gui/widgets/resizegrip.cpp @@ -24,9 +24,8 @@ #include "configuration.h" #include "graphics.h" -#include "gui/theme.h" - #include "resources/image.h" +#include "resources/theme.h" #include <guichan/graphics.hpp> @@ -59,9 +58,9 @@ ResizeGrip::~ResizeGrip() void ResizeGrip::draw(gcn::Graphics *graphics) { - if (config.getValue("guialpha", 0.8) != mAlpha) + if (config.getFloatValue("guialpha") != mAlpha) { - mAlpha = config.getValue("guialpha", 0.8); + mAlpha = config.getFloatValue("guialpha"); gripImage->setAlpha(mAlpha); } diff --git a/src/gui/widgets/scrollarea.cpp b/src/gui/widgets/scrollarea.cpp index 0c7f4d7d..7d42883a 100644 --- a/src/gui/widgets/scrollarea.cpp +++ b/src/gui/widgets/scrollarea.cpp @@ -24,9 +24,8 @@ #include "configuration.h" #include "graphics.h" -#include "gui/theme.h" - #include "resources/image.h" +#include "resources/theme.h" #include "utils/dtor.h" @@ -108,7 +107,7 @@ void ScrollArea::init() bggridx[x], bggridy[y], bggridx[x + 1] - bggridx[x] + 1, bggridy[y + 1] - bggridy[y] + 1); - background.grid[a]->setAlpha(config.getValue("guialpha", 0.8)); + background.grid[a]->setAlpha(config.getFloatValue("guialpha")); a++; } } @@ -135,8 +134,8 @@ void ScrollArea::init() vsgridx[x], vsgridy[y], vsgridx[x + 1] - vsgridx[x], vsgridy[y + 1] - vsgridy[y]); - vMarker.grid[a]->setAlpha(config.getValue("guialpha", 0.8)); - vMarkerHi.grid[a]->setAlpha(config.getValue("guialpha", 0.8)); + vMarker.grid[a]->setAlpha(config.getFloatValue("guialpha")); + vMarkerHi.grid[a]->setAlpha(config.getFloatValue("guialpha")); a++; } } @@ -213,8 +212,8 @@ void ScrollArea::logic() void ScrollArea::updateAlpha() { - float alpha = std::max(config.getValue("guialpha", 0.8), - (double) Theme::instance()->getMinimumOpacity()); + float alpha = std::max(config.getFloatValue("guialpha"), + Theme::instance()->getMinimumOpacity()); if (alpha != mAlpha) { diff --git a/src/gui/widgets/shoplistbox.cpp b/src/gui/widgets/shoplistbox.cpp index a5033570..ae7d4d9b 100644 --- a/src/gui/widgets/shoplistbox.cpp +++ b/src/gui/widgets/shoplistbox.cpp @@ -26,12 +26,12 @@ #include "shopitem.h" #include "gui/itempopup.h" -#include "gui/theme.h" #include "gui/viewport.h" #include "gui/widgets/shopitems.h" #include "resources/image.h" +#include "resources/theme.h" #include <guichan/font.hpp> #include <guichan/listmodel.hpp> @@ -76,8 +76,8 @@ void ShopListBox::draw(gcn::Graphics *gcnGraphics) if (!mListModel) return; - if (config.getValue("guialpha", 0.8) != mAlpha) - mAlpha = config.getValue("guialpha", 0.8); + if (config.getFloatValue("guialpha") != mAlpha) + mAlpha = config.getFloatValue("guialpha"); int alpha = (int)(mAlpha * 255.0f); const gcn::Color* highlightColor = diff --git a/src/gui/widgets/slider.cpp b/src/gui/widgets/slider.cpp index 6a9a5c7c..c044d55d 100644 --- a/src/gui/widgets/slider.cpp +++ b/src/gui/widgets/slider.cpp @@ -24,9 +24,8 @@ #include "configuration.h" #include "graphics.h" -#include "gui/theme.h" - #include "resources/image.h" +#include "resources/theme.h" Image *Slider::hStart, *Slider::hMid, *Slider::hEnd, *Slider::hGrip; Image *Slider::vStart, *Slider::vMid, *Slider::vEnd, *Slider::vGrip; @@ -126,8 +125,8 @@ void Slider::init() void Slider::updateAlpha() { - float alpha = std::max(config.getValue("guialpha", 0.8), - (double) Theme::instance()->getMinimumOpacity()); + float alpha = std::max(config.getFloatValue("guialpha"), + Theme::instance()->getMinimumOpacity()); if (alpha != mAlpha) { diff --git a/src/gui/widgets/tab.cpp b/src/gui/widgets/tab.cpp index 2ab126dd..10a51afe 100644 --- a/src/gui/widgets/tab.cpp +++ b/src/gui/widgets/tab.cpp @@ -25,11 +25,11 @@ #include "graphics.h" #include "gui/palette.h" -#include "gui/theme.h" #include "gui/widgets/tabbedarea.h" #include "resources/image.h" +#include "resources/theme.h" #include "utils/dtor.h" @@ -118,8 +118,8 @@ void Tab::init() void Tab::updateAlpha() { - float alpha = std::max(config.getValue("guialpha", 0.8), - (double) Theme::instance()->getMinimumOpacity()); + float alpha = std::max(config.getFloatValue("guialpha"), + Theme::instance()->getMinimumOpacity()); // TODO We don't need to do this for every tab on every draw // Maybe use a config listener to do it as the value changes. diff --git a/src/gui/widgets/table.cpp b/src/gui/widgets/table.cpp index f0887ed7..3d8680ce 100644 --- a/src/gui/widgets/table.cpp +++ b/src/gui/widgets/table.cpp @@ -24,7 +24,8 @@ #include "configuration.h" #include "gui/sdlinput.h" -#include "gui/theme.h" + +#include "resources/theme.h" #include "utils/dtor.h" @@ -270,8 +271,8 @@ void GuiTable::draw(gcn::Graphics* graphics) if (!mModel) return; - if (config.getValue("guialpha", 0.8) != mAlpha) - mAlpha = config.getValue("guialpha", 0.8); + if (config.getFloatValue("guialpha") != mAlpha) + mAlpha = config.getFloatValue("guialpha"); if (mOpaque) { diff --git a/src/gui/widgets/textbox.cpp b/src/gui/widgets/textbox.cpp index f248f35d..5b112e54 100644 --- a/src/gui/widgets/textbox.cpp +++ b/src/gui/widgets/textbox.cpp @@ -21,7 +21,7 @@ #include "gui/widgets/textbox.h" -#include "gui/theme.h" +#include "resources/theme.h" #include <guichan/font.hpp> diff --git a/src/gui/widgets/textfield.cpp b/src/gui/widgets/textfield.cpp index 4989ae83..d06df376 100644 --- a/src/gui/widgets/textfield.cpp +++ b/src/gui/widgets/textfield.cpp @@ -21,15 +21,14 @@ #include "gui/widgets/textfield.h" -#include "beingmanager.h" #include "configuration.h" #include "graphics.h" #include "gui/palette.h" #include "gui/sdlinput.h" -#include "gui/theme.h" #include "resources/image.h" +#include "resources/theme.h" #include "utils/copynpaste.h" #include "utils/dtor.h" @@ -69,7 +68,7 @@ TextField::TextField(const std::string &text, bool loseFocusOnTab): gridx[x], gridy[y], gridx[x + 1] - gridx[x] + 1, gridy[y + 1] - gridy[y] + 1); - skin.grid[a]->setAlpha(config.getValue("guialpha", 0.8)); + skin.grid[a]->setAlpha(config.getFloatValue("guialpha")); a++; } } @@ -90,8 +89,8 @@ TextField::~TextField() void TextField::updateAlpha() { - float alpha = std::max(config.getValue("guialpha", 0.8), - (double) Theme::instance()->getMinimumOpacity()); + float alpha = std::max(config.getFloatValue("guialpha"), + Theme::instance()->getMinimumOpacity()); if (alpha != mAlpha) { diff --git a/src/gui/widgets/textpreview.cpp b/src/gui/widgets/textpreview.cpp index 10426d7c..869ebd35 100644 --- a/src/gui/widgets/textpreview.cpp +++ b/src/gui/widgets/textpreview.cpp @@ -45,8 +45,8 @@ TextPreview::TextPreview(const std::string &text): void TextPreview::draw(gcn::Graphics* graphics) { - if (config.getValue("guialpha", 0.8) != mAlpha) - mAlpha = config.getValue("guialpha", 0.8); + if (config.getFloatValue("guialpha") != mAlpha) + mAlpha = config.getFloatValue("guialpha"); int alpha = (int) (mAlpha * 255.0f); diff --git a/src/gui/widgets/whispertab.cpp b/src/gui/widgets/whispertab.cpp index 7542e251..864f1f51 100644 --- a/src/gui/widgets/whispertab.cpp +++ b/src/gui/widgets/whispertab.cpp @@ -21,14 +21,15 @@ #include "whispertab.h" +#include "chatlog.h" #include "commandhandler.h" #include "localplayer.h" -#include "gui/theme.h" - #include "net/chathandler.h" #include "net/net.h" +#include "resources/theme.h" + #include "utils/gettext.h" #include "utils/stringutils.h" @@ -115,3 +116,9 @@ bool WhisperTab::handleCommand(const std::string &type, return true; } + +void WhisperTab::saveToLogFile(std::string &msg) +{ + if (chatLogger) + chatLogger->log(getNick(), msg); +} diff --git a/src/gui/widgets/whispertab.h b/src/gui/widgets/whispertab.h index 447a8fe0..20a07449 100644 --- a/src/gui/widgets/whispertab.h +++ b/src/gui/widgets/whispertab.h @@ -39,6 +39,8 @@ class WhisperTab : public ChatTab bool handleCommand(const std::string &type, const std::string &args); + void saveToLogFile(std::string &msg); + protected: friend class ChatWindow; diff --git a/src/gui/widgets/window.cpp b/src/gui/widgets/window.cpp index 118ee7c0..aa8e6df3 100644 --- a/src/gui/widgets/window.cpp +++ b/src/gui/widgets/window.cpp @@ -26,7 +26,6 @@ #include "gui/gui.h" #include "gui/palette.h" -#include "gui/theme.h" #include "gui/viewport.h" #include "gui/widgets/layout.h" @@ -34,6 +33,7 @@ #include "gui/widgets/windowcontainer.h" #include "resources/image.h" +#include "resources/theme.h" #include <guichan/exception.hpp> #include <guichan/focushandler.hpp> @@ -697,8 +697,8 @@ int Window::getResizeHandles(gcn::MouseEvent &event) int Window::getGuiAlpha() { - float alpha = std::max(config.getValue("guialpha", 0.8), - (double) Theme::instance()->getMinimumOpacity()); + float alpha = std::max(config.getFloatValue("guialpha"), + Theme::instance()->getMinimumOpacity()); return (int) (alpha * 255.0f); } diff --git a/src/gui/windowmenu.cpp b/src/gui/windowmenu.cpp index 4b18de89..542ab4a0 100644 --- a/src/gui/windowmenu.cpp +++ b/src/gui/windowmenu.cpp @@ -58,7 +58,7 @@ WindowMenu::WindowMenu(): if (skillDialog->hasSkills()) addButton(N_("Skills"), x, h); - if (specialsWindow->hasSpecials()) + // if (specialsWindow->hasSpecials()) addButton(N_("Specials"), x, h); addButton(N_("Social"), x, h); diff --git a/src/gui/worldselectdialog.cpp b/src/gui/worldselectdialog.cpp index 3219b83d..3207f394 100644 --- a/src/gui/worldselectdialog.cpp +++ b/src/gui/worldselectdialog.cpp @@ -137,3 +137,13 @@ void WorldSelectDialog::keyPressed(gcn::KeyEvent &keyEvent) action(gcn::ActionEvent(NULL, mChooseWorld->getActionEventId())); } } + +void WorldSelectDialog::mouseClicked(gcn::MouseEvent &mouseEvent) +{ + if (mouseEvent.getSource() == mWorldList && + isDoubleClick(mWorldList->getSelected())) + { + action(gcn::ActionEvent(mChooseWorld, + mChooseWorld->getActionEventId())); + } +} diff --git a/src/gui/worldselectdialog.h b/src/gui/worldselectdialog.h index 2d4f0189..b51110b4 100644 --- a/src/gui/worldselectdialog.h +++ b/src/gui/worldselectdialog.h @@ -62,6 +62,8 @@ class WorldSelectDialog : public Window, public gcn::ActionListener, void keyPressed(gcn::KeyEvent &keyEvent); + void mouseClicked(gcn::MouseEvent &mouseEvent); + private: WorldListModel *mWorldListModel; gcn::ListBox *mWorldList; diff --git a/src/guild.cpp b/src/guild.cpp index 029cde7f..00d8614e 100644 --- a/src/guild.cpp +++ b/src/guild.cpp @@ -21,8 +21,7 @@ #include "guild.h" -#include "beingmanager.h" -#include "player.h" +#include "actorspritemanager.h" GuildMember::GuildMember(Guild *guild, int id, const std::string &name): Avatar(name), mId(id), mGuild(guild) @@ -150,10 +149,8 @@ void Guild::removeFromMembers() itr_end = mMembers.end(); while(itr != itr_end) { - Being *b = beingManager->findBeing((*itr)->getID()); - - if (b->getType() == Being::PLAYER) - static_cast<Player*>(b)->removeGuild(getId()); + Being *b = actorSpriteManager->findBeing((*itr)->getID()); + b->removeGuild(getId()); ++itr; } } diff --git a/src/imageparticle.cpp b/src/imageparticle.cpp index 8ef2cce9..b7d92277 100644 --- a/src/imageparticle.cpp +++ b/src/imageparticle.cpp @@ -39,23 +39,21 @@ ImageParticle::~ImageParticle() mImage->decRef(); } -void ImageParticle::draw(Graphics *graphics, int offsetX, int offsetY) const +bool ImageParticle::draw(Graphics *graphics, int offsetX, int offsetY) const { - if (!mAlive || !mImage) - return; + if (!isAlive() || !mImage) + return false; int screenX = (int) mPos.x + offsetX - mImage->getWidth() / 2; int screenY = (int) mPos.y - (int)mPos.z + offsetY - mImage->getHeight()/2; // Check if on screen - if (screenX + mImage->getWidth() < 0 || - screenX > graphics->getWidth() || - screenY + mImage->getHeight() < 0 || - screenY > graphics->getHeight()) - { - return; - } + if (screenX + mImage->getWidth() < 0 + || screenX > graphics->getWidth() + || screenY + mImage->getHeight() < 0 + || screenY > graphics->getHeight()) + return false; mImage->setAlpha(getCurrentAlpha()); - graphics->drawImage(mImage, screenX, screenY); + return graphics->drawImage(mImage, screenX, screenY); } diff --git a/src/imageparticle.h b/src/imageparticle.h index bc32400d..23909fa3 100644 --- a/src/imageparticle.h +++ b/src/imageparticle.h @@ -49,7 +49,7 @@ class ImageParticle : public Particle /** * Draws the particle image */ - virtual void draw(Graphics *graphics, int offsetX, int offsetY) const; + virtual bool draw(Graphics *graphics, int offsetX, int offsetY) const; protected: Image *mImage; /**< The image used for this particle. */ diff --git a/src/imagesprite.cpp b/src/imagesprite.cpp new file mode 100644 index 00000000..504aba96 --- /dev/null +++ b/src/imagesprite.cpp @@ -0,0 +1,44 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "imagesprite.h" + +#include "graphics.h" + +ImageSprite::ImageSprite(Image *image): + mImage(image) +{ + mAlpha = mImage->getAlpha(); + + mImage->incRef(); +} + +ImageSprite::~ImageSprite() +{ + mImage->decRef(); +} + +bool ImageSprite::draw(Graphics* graphics, int posX, int posY) const +{ + if (mImage->getAlpha() != mAlpha) + mImage->setAlpha(mAlpha); + + return graphics->drawImage(mImage, posX, posY); +} diff --git a/src/imagesprite.h b/src/imagesprite.h new file mode 100644 index 00000000..3a3678d9 --- /dev/null +++ b/src/imagesprite.h @@ -0,0 +1,73 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef IMAGESPRITE_H +#define IMAGESPRITE_H + +#include "sprite.h" + +#include "resources/image.h" + +class Graphics; + +class ImageSprite : public Sprite +{ +public: + ImageSprite(Image *image); + + ~ImageSprite(); + + bool reset() + { return false; } + + bool play(std::string action) + { return false; } + + bool update(int time) + { return false; } + + bool draw(Graphics* graphics, int posX, int posY) const; + + int getWidth() const + { return mImage->getWidth(); } + + int getHeight() const + { return mImage->getHeight(); } + + const Image* getImage() const + { return mImage; } + + virtual bool setDirection(SpriteDirection direction) + { return false; } + + int getNumberOfLayers() + { return 1; } + + size_t getCurrentFrame() const + { return 0; } + + size_t getFrameCount() const + { return 1; } + +private: + Image *mImage; +}; + +#endif // IMAGESPRITE_H diff --git a/src/inventory.cpp b/src/inventory.cpp index a6038c85..3c556f01 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -36,7 +36,7 @@ struct SlotUsed : public std::unary_function<Item*, bool> } }; -Inventory::Inventory(int type, int size): +Inventory::Inventory(Type type, int size): mType(type), mSize(size == -1 ? Net::getInventoryHandler()->getSize(type) : size), mUsed(0) @@ -70,12 +70,12 @@ Item *Inventory::findItem(int itemId) const return NULL; } -void Inventory::addItem(int id, int quantity, bool equipment) +void Inventory::addItem(int id, int quantity) { - setItem(getFreeSlot(), id, quantity, equipment); + setItem(getFreeSlot(), id, quantity); } -void Inventory::setItem(int index, int id, int quantity, bool equipment) +void Inventory::setItem(int index, int id, int quantity) { if (index < 0 || index >= mSize) { @@ -85,7 +85,7 @@ void Inventory::setItem(int index, int id, int quantity, bool equipment) if (!mItems[index] && id > 0) { - Item *item = new Item(id, quantity, equipment); + Item *item = new Item(id, quantity); item->setInvIndex(index); mItems[index] = item; mUsed++; @@ -95,7 +95,6 @@ void Inventory::setItem(int index, int id, int quantity, bool equipment) { mItems[index]->setId(id); mItems[index]->setQuantity(quantity); - mItems[index]->setEquipment(equipment); } else if (mItems[index]) { diff --git a/src/inventory.h b/src/inventory.h index 0ee516d6..7af9f160 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -43,7 +43,7 @@ class Inventory public: static const int NO_SLOT_INDEX = -1; /**< Slot has no index. */ - enum { + enum Type { INVENTORY, STORAGE, TRADE, @@ -56,7 +56,7 @@ class Inventory * * @param size the number of items that fit in the inventory */ - Inventory(int type, int size = -1); + Inventory(Type type, int size = -1); /** * Destructor. @@ -84,12 +84,12 @@ class Inventory /** * Adds a new item in a free slot. */ - void addItem(int id, int quantity, bool equipment = false); + void addItem(int id, int quantity); /** * Sets the item at the given position. */ - void setItem(int index, int id, int quantity, bool equipment = false); + void setItem(int index, int id, int quantity); /** * Remove a item from the inventory. @@ -143,7 +143,7 @@ class Inventory void distributeSlotsChangedEvent(); - int mType; + Type mType; Item **mItems; /**< The holder of items */ int mSize; /**< The max number of inventory items */ int mUsed; /**< THe number of slots in use */ diff --git a/src/item.cpp b/src/item.cpp index b434387e..fee7e86f 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -21,18 +21,19 @@ #include "item.h" -#include "gui/theme.h" +#include "configuration.h" +#include "event.h" #include "resources/image.h" #include "resources/iteminfo.h" #include "resources/resourcemanager.h" -#include "configuration.h" +#include "resources/theme.h" -Item::Item(int id, int quantity, bool equipment, bool equipped): +Item::Item(int id, int quantity, bool equipped): mImage(0), mDrawImage(0), mQuantity(quantity), - mEquipment(equipment), mEquipped(equipped), mInEquipment(false) + mEquipped(equipped), mInEquipment(false) { setId(id); } @@ -47,9 +48,6 @@ void Item::setId(int id) { mId = id; - // Types 0 and 1 are not equippable items. - mEquipment = id && getInfo().getType() >= 2; - // Load the associated image if (mImage) mImage->decRef(); @@ -58,8 +56,9 @@ void Item::setId(int id) mDrawImage->decRef(); ResourceManager *resman = ResourceManager::getInstance(); - std::string imagePath = paths.getValue("itemIcons", "graphics/items/") - + getInfo().getImageName(); + SpriteDisplay display = getInfo().getDisplay(); + std::string imagePath = paths.getStringValue("itemIcons") + + display.image; mImage = resman->getImage(imagePath); mDrawImage = resman->getImage(imagePath); @@ -72,3 +71,23 @@ void Item::setId(int id) paths.getValue("unknownItemFile", "unknown-item.png")); } + +void Item::doEvent(Events eventName) +{ + Mana::Event event(eventName); + event.setItem("item", this); + event.trigger(CHANNEL_ITEM); +} + +void Item::doEvent(Events eventName, int amount) +{ + Mana::Event event(eventName); + event.setItem("item", this); + event.setInt("amount", amount); + event.trigger(CHANNEL_ITEM); +} + +bool Item::isEquippable() const +{ + return getInfo().getEquippable(); +} @@ -22,6 +22,8 @@ #ifndef ITEM_H #define ITEM_H +#include "event.h" + #include "resources/itemdb.h" class Image; @@ -35,8 +37,7 @@ class Item /** * Constructor. */ - Item(int id = -1, int quantity = 0, bool equipment = false, - bool equipped = false); + Item(int id = -1, int quantity = 0, bool equipped = false); /** * Destructor. @@ -79,16 +80,6 @@ class Item int getQuantity() const { return mQuantity; } /** - * Sets whether this item is considered equipment. - */ - void setEquipment(bool equipment) { mEquipment = equipment; } - - /** - * Returns whether this item is considered equipment. - */ - bool isEquipment() const { return mEquipment; } - - /** * Sets whether this item is equipped. */ void setEquipped(bool equipped) { mEquipped = equipped; } @@ -109,6 +100,11 @@ class Item bool isInEquipment() const { return mInEquipment; } /** + * Returns whether this item is equippable. + */ + bool isEquippable() const; + + /** * Sets the inventory index of this item. */ void setInvIndex(int index) { mInvIndex = index; } @@ -118,20 +114,24 @@ class Item */ int getInvIndex() const { return mInvIndex; } + void doEvent(Events eventName); + + void doEvent(Events eventName, int amount); + /** * Returns information about this item type. */ - const ItemInfo &getInfo() const { return ItemDB::get(mId); } + const ItemInfo &getInfo() const { return itemDb->get(mId); } protected: int mId; /**< Item type id. */ Image *mImage; /**< Item image. */ Image *mDrawImage; /**< Draw image. */ int mQuantity; /**< Number of items. */ - bool mEquipment; /**< Item is equipment. */ bool mEquipped; /**< Item is equipped. */ bool mInEquipment; /**< Item is in equipment */ int mInvIndex; /**< Inventory index. */ + }; #endif diff --git a/src/itemshortcut.cpp b/src/itemshortcut.cpp index 88b04347..0e5abef8 100644 --- a/src/itemshortcut.cpp +++ b/src/itemshortcut.cpp @@ -20,13 +20,15 @@ */ #include "configuration.h" +#include "event.h" #include "inventory.h" #include "item.h" #include "itemshortcut.h" -#include "localplayer.h" +#include "playerinfo.h" #include "net/inventoryhandler.h" -#include "net/net.h" + +#include "resources/iteminfo.h" #include "utils/stringutils.h" @@ -66,19 +68,19 @@ void ItemShortcut::useItem(int index) { if (mItems[index]) { - Item *item = player_node->getInventory()->findItem(mItems[index]); + Item *item = PlayerInfo::getInventory()->findItem(mItems[index]); if (item && item->getQuantity()) { - if (item->isEquipment()) + if (item->getInfo().getEquippable()) { if (item->isEquipped()) - Net::getInventoryHandler()->unequipItem(item); + item->doEvent(EVENT_DOUNEQUIP); else - Net::getInventoryHandler()->equipItem(item); + item->doEvent(EVENT_DOEQUIP); } else { - Net::getInventoryHandler()->useItem(item); + item->doEvent(EVENT_DOUSE); } } } diff --git a/src/joystick.cpp b/src/joystick.cpp index f440c61f..9864a12b 100644 --- a/src/joystick.cpp +++ b/src/joystick.cpp @@ -61,11 +61,11 @@ Joystick::Joystick(int no): logger->log("Hats: %i", SDL_JoystickNumHats(mJoystick)); logger->log("Buttons: %i", SDL_JoystickNumButtons(mJoystick)); - mEnabled = (int) config.getValue("joystickEnabled", 0) != 0; - mUpTolerance = (int) config.getValue("upTolerance", 100); - mDownTolerance = (int) config.getValue("downTolerance", 100); - mLeftTolerance = (int) config.getValue("leftTolerance", 100); - mRightTolerance = (int) config.getValue("rightTolerance", 100); + mEnabled = config.getBoolValue("joystickEnabled"); + mUpTolerance = config.getIntValue("upTolerance"); + mDownTolerance = config.getIntValue("downTolerance"); + mLeftTolerance = config.getIntValue("leftTolerance"); + mRightTolerance = config.getIntValue("rightTolerance"); } Joystick::~Joystick() diff --git a/src/keyboardconfig.cpp b/src/keyboardconfig.cpp index 65143d69..ec2bf6ed 100644 --- a/src/keyboardconfig.cpp +++ b/src/keyboardconfig.cpp @@ -20,7 +20,6 @@ #include "configuration.h" #include "keyboardconfig.h" -#include "log.h" #include "gui/sdlinput.h" #include "gui/setup_keyboard.h" diff --git a/src/listener.cpp b/src/listener.cpp new file mode 100644 index 00000000..f9acac95 --- /dev/null +++ b/src/listener.cpp @@ -0,0 +1,43 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "listener.h" + +#include "event.h" + +namespace Mana +{ + +Listener::~Listener() +{ + Event::remove(this); +} + +void Listener::listen(Channels channel) +{ + Event::bind(this, channel); +} + +void Listener::ignore(Channels channel) +{ + Event::unbind(this, channel); +} + +} // namespace Mana diff --git a/src/net/manaserv/stats.h b/src/listener.h index 63349095..84b613eb 100644 --- a/src/net/manaserv/stats.h +++ b/src/listener.h @@ -18,24 +18,28 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef NET_MANASERV_STATS_H -#define NET_MANASERV_STATS_H +#ifndef LISTENER_H +#define LISTENER_H + +#include "event.h" #include <string> -#include <vector> -namespace ManaServ { -namespace Stats { - void load(); +namespace Mana +{ + +class Listener +{ +public: + virtual ~Listener(); - void unload(); + void listen(Channels channel); - void informItemDB(); + void ignore(Channels channel); - void informStatusWindow(); + virtual void event(Channels channel, const Event &event) = 0; +}; - std::vector<std::string> getLabelVector(); -} // namespace Stats -} // namespace ManaServ +} // namespace Mana -#endif // NET_MANASERV_STATS_H +#endif diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 7826596c..81fb1cee 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -23,30 +23,19 @@ #include "client.h" #include "configuration.h" -#include "effectmanager.h" -#include "equipment.h" +#include "event.h" #include "flooritem.h" #include "graphics.h" #include "guild.h" -#include "inventory.h" #include "item.h" -#include "log.h" #include "map.h" -#include "monster.h" #include "particle.h" +#include "playerinfo.h" #include "simpleanimation.h" #include "sound.h" -#include "statuseffect.h" -#include "text.h" #include "gui/gui.h" -#include "gui/inventorywindow.h" -#include "gui/ministatus.h" #include "gui/okdialog.h" -#include "gui/skilldialog.h" -#include "gui/statuswindow.h" -#include "gui/theme.h" -#include "gui/userpalette.h" #include "gui/widgets/chattab.h" @@ -61,9 +50,8 @@ #include "resources/animation.h" #include "resources/imageset.h" -#include "resources/itemdb.h" #include "resources/iteminfo.h" -#include "resources/resourcemanager.h" +#include "resources/userpalette.h" #include "utils/gettext.h" #include "utils/stringutils.h" @@ -80,62 +68,37 @@ const short walkingKeyboardDelay = 1000; LocalPlayer *player_node = NULL; LocalPlayer::LocalPlayer(int id, int subtype): - Player(id, subtype, 0), - mEquipment(new Equipment), + Being(id, PLAYER, subtype, 0), mAttackRange(0), mTargetTime(-1), mLastTarget(-1), - mCharacterPoints(0), - mCorrectionPoints(0), - mSpecialRechargeUpdateNeeded(0), - mLevel(1), - mExp(0), mExpNeeded(0), - mMp(0), mMaxMp(0), - mMoney(0), - mTotalWeight(1), mMaxWeight(1), - mHp(1), mMaxHp(1), - mSkillPoints(0), mTarget(NULL), mPickUpTarget(NULL), - mTrading(false), mGoingToTarget(false), mKeepAttacking(false), + mGoingToTarget(false), mKeepAttacking(false), mLastAction(-1), mWalkingDir(0), mPathSetByMouse(false), - mInventory(new Inventory(Inventory::INVENTORY)), mLocalWalkTime(-1), mMessageTime(0), + mShowIp(false), mAwayDialog(0), mAfkTime(0), - mAwayMode(false), - mShowIp(false) + mAwayMode(false) { + listen(CHANNEL_ATTRIBUTES); + mAwayListener = new AwayListener(); mUpdateName = true; - mTextColor = &Theme::getThemeColor(Theme::PLAYER); - mNameColor = &userPalette->getColor(UserPalette::SELF); - - initTargetCursor(); - - config.addListener("showownname", this); setShowName(config.getValue("showownname", 1)); + + listen(CHANNEL_CONFIG); + listen(CHANNEL_ACTORSPRITE); } LocalPlayer::~LocalPlayer() { - delete mInventory; - - config.removeListener("showownname", this); - - for (int i = Being::TC_SMALL; i < Being::NUM_TC; i++) - { - delete mTargetCursor[0][i]; - delete mTargetCursor[1][i]; - mTargetCursorImages[0][i]->decRef(); - mTargetCursorImages[1][i]->decRef(); - } - delete mAwayDialog; delete mAwayListener; } @@ -170,21 +133,7 @@ void LocalPlayer::logic() mMessageTime--; } - if ((mSpecialRechargeUpdateNeeded%11) == 0) - { - mSpecialRechargeUpdateNeeded = 0; - for (std::map<int, Special>::iterator i = mSpecials.begin(); - i != mSpecials.end(); - i++) - { - i->second.currentMana += i->second.recharge; - if (i->second.currentMana > i->second.neededMana) - { - i->second.currentMana = i->second.neededMana; - } - } - } - mSpecialRechargeUpdateNeeded++; + PlayerInfo::logic(); // Targeting allowed 4 times a second if (get_elapsed_time(mLastTarget) >= 250) @@ -200,11 +149,10 @@ void LocalPlayer::logic() if (mTarget) { - if (mTarget->getType() == Being::NPC) + if (mTarget->getType() == ActorSprite::NPC) { // NPCs are always in range - mTarget->setTargetAnimation( - mTargetCursor[0][mTarget->getTargetCursorSize()]); + mTarget->setTargetType(TCT_IN_RANGE); } else { @@ -220,10 +168,10 @@ void LocalPlayer::logic() abs(mTarget->getTileY() - getTileY()); const int attackRange = getAttackRange(); - const int inRange = rangeX > attackRange || rangeY > attackRange - ? 1 : 0; - mTarget->setTargetAnimation( - mTargetCursor[inRange][mTarget->getTargetCursorSize()]); + const TargetCursorType targetType = rangeX > attackRange || + rangeY > attackRange ? + TCT_NORMAL : TCT_IN_RANGE; + mTarget->setTargetType(targetType); if (!mTarget->isAlive()) stopAttack(); @@ -233,7 +181,7 @@ void LocalPlayer::logic() } } - Player::logic(); + Being::logic(); } void LocalPlayer::setAction(Action action, int attackType) @@ -244,12 +192,7 @@ void LocalPlayer::setAction(Action action, int attackType) setTarget(NULL); } - Player::setAction(action, attackType); -} - -void LocalPlayer::setGM(bool gm) -{ - mIsGM = gm; + Being::setAction(action, attackType); } void LocalPlayer::setGMLevel(int level) @@ -635,7 +578,7 @@ void LocalPlayer::nextTile(unsigned char dir = 0) } - Player::nextTile(); + Being::nextTile(); } else { @@ -674,7 +617,6 @@ void LocalPlayer::inviteToGuild(Being *being) { if (being->getType() != PLAYER) return; - Player *player = static_cast<Player*>(being); // TODO: Allow user to choose which guild to invite being to // For now, just invite to the first guild you have permissions to invite with @@ -684,31 +626,20 @@ void LocalPlayer::inviteToGuild(Being *being) { if (checkInviteRights(itr->second->getName())) { - Net::getGuildHandler()->invite(itr->second->getId(), player); + Net::getGuildHandler()->invite(itr->second->getId(), being); return; } } } -void LocalPlayer::clearInventory() -{ - mEquipment->clear(); - mInventory->clear(); -} - -void LocalPlayer::setInvItem(int index, int id, int amount) -{ - bool equipment = false; - int itemType = ItemDB::get(id).getType(); - if (itemType != ITEM_UNUSABLE && itemType != ITEM_USABLE) - equipment = true; - mInventory->setItem(index, id, amount, equipment); -} - void LocalPlayer::pickUp(FloorItem *item) { - int dx = item->getX() - getTileX(); - int dy = item->getY() - getTileY(); + if (!item) + return; + + int dx = item->getTileX() - (int) getPosition().x / mMap->getTileWidth(); + int dy = item->getTileY() - ((int) getPosition().y - 1) + / mMap->getTileHeight(); if (dx * dx + dy * dy < 4) { @@ -719,12 +650,12 @@ void LocalPlayer::pickUp(FloorItem *item) { if (Net::getNetworkType() == ServerInfo::MANASERV) { - setDestination(item->getX() * 32 + 16, item->getY() * 32 + 16); + setDestination(item->getPixelX() + 16, item->getPixelY() + 16); mPickUpTarget = item; } else { - setDestination(item->getX(), item->getY()); + setDestination(item->getTileX(), item->getTileY()); mPickUpTarget = item; stopAttack(); } @@ -757,15 +688,24 @@ void LocalPlayer::setTarget(Being *target) mTargetTime = -1; } + Being *oldTarget = 0; if (mTarget) + { mTarget->untarget(); + oldTarget = mTarget; + } - if (mTarget && mTarget->getType() == Being::MONSTER) + if (mTarget && mTarget->getType() == ActorSprite::MONSTER) mTarget->setShowName(false); mTarget = target; - if (target && target->getType() == Being::MONSTER) + if (oldTarget) + oldTarget->updateName(); + if (mTarget) + mTarget->updateName(); + + if (target && target->getType() == ActorSprite::MONSTER) target->setShowName(true); } @@ -805,13 +745,10 @@ void LocalPlayer::setWalkingDir(int dir) // Else, he is not pressing a key, // and the current path hasn't been sent by mouse, - // then, stop (sending to server). + // then let the path die (1/2 tile after that.) + // This permit to avoid desyncs with other clients. else if (!dir) - { - if (!mPathSetByMouse) - player_node->stopWalking(true); return; - } // If the delay to send another walk message to the server hasn't expired, // don't do anything or we could get disconnected for spamming the server @@ -822,11 +759,11 @@ void LocalPlayer::setWalkingDir(int dir) mWalkingDir = dir; // If we're not already walking, start walking. - if (mAction != WALK && dir) + if (mAction != MOVE && dir) { startWalking(dir); } - else if (mAction == WALK && (Net::getNetworkType() == ServerInfo::MANASERV)) + else if (mAction == MOVE && (Net::getNetworkType() == ServerInfo::MANASERV)) { nextTile(dir); } @@ -839,7 +776,7 @@ void LocalPlayer::startWalking(unsigned char dir) if (!mMap || !dir) return; - if (mAction == WALK && !mPath.empty()) + if (mAction == MOVE && !mPath.empty()) { // Just finish the current action, otherwise we get out of sync if (Net::getNetworkType() == ServerInfo::MANASERV) @@ -862,6 +799,10 @@ void LocalPlayer::startWalking(unsigned char dir) if (dir & RIGHT) dx++; + // Update the direction when the walk just start + if (Net::getNetworkType() == ServerInfo::MANASERV) + setDirection(dir); + if (Net::getNetworkType() == ServerInfo::TMWATHENA) { // Prevent skipping corners over colliding tiles @@ -894,7 +835,7 @@ void LocalPlayer::startWalking(unsigned char dir) void LocalPlayer::stopWalking(bool sendToServer) { - if (mAction == WALK && mWalkingDir) + if (mAction == MOVE && mWalkingDir) { mWalkingDir = 0; mLocalWalkTime = 0; @@ -938,20 +879,6 @@ void LocalPlayer::emote(Uint8 emotion) Net::getPlayerHandler()->emote(emotion); } -void LocalPlayer::useSpecial(int special) -{ - Net::getSpecialHandler()->use(special); -} - -void LocalPlayer::setSpecialStatus(int id, int current, int max, int recharge) -{ - logger->log("SpecialUpdate Skill #%d -- (%d/%d) -> %d", id, current, max, - recharge); - mSpecials[id].currentMana = current; - mSpecials[id].neededMana = max; - mSpecials[id].recharge = recharge; -} - void LocalPlayer::attack(Being *target, bool keep) { if (Net::getNetworkType() == ServerInfo::MANASERV) @@ -966,7 +893,7 @@ void LocalPlayer::attack(Being *target, bool keep) mKeepAttacking = keep; - if (!target || target->getType() == Being::NPC) + if (!target || target->getType() == ActorSprite::NPC) return; if (mTarget != target || !mTarget) @@ -974,6 +901,7 @@ void LocalPlayer::attack(Being *target, bool keep) mLastTarget = -1; setTarget(target); } + if (Net::getNetworkType() == ServerInfo::MANASERV) { Vector plaPos = this->getPosition(); @@ -1025,7 +953,7 @@ void LocalPlayer::attack(Being *target, bool keep) Net::getPlayerHandler()->setDirection(direction); setDirection(direction); - mWalkTime = tick_time; + mActionTime = tick_time; mTargetTime = tick_time; } @@ -1056,196 +984,13 @@ void LocalPlayer::stopAttack() mLastTarget = -1; } -void LocalPlayer::raiseAttribute(int attr) -{ - // we assume that the server allows the change. - // When not we will undo it later. - mCharacterPoints--; - IntMap::iterator it = mAttributeBase.find(attr); - if (it != mAttributeBase.end()) - (*it).second++; - Net::getPlayerHandler()->increaseAttribute(attr); -} - -void LocalPlayer::lowerAttribute(int attr) -{ - // we assume that the server allows the change. - // When not we will undo it later. - mCorrectionPoints--; - mCharacterPoints++; - IntMap::iterator it = mAttributeBase.find(attr); - if (it != mAttributeBase.end()) - (*it).second--; - Net::getPlayerHandler()->decreaseAttribute(attr); -} - -void LocalPlayer::setTotalWeight(int value) -{ - mTotalWeight = value; - - inventoryWindow->updateWeight(); -} - -void LocalPlayer::setMaxWeight(int value) -{ - mMaxWeight = value; - - inventoryWindow->updateWeight(); -} - -void LocalPlayer::setAttributeBase(int num, int value, bool notify) -{ - int old = mAttributeBase[num]; - - mAttributeBase[num] = value; - if (skillDialog) - { - if (skillDialog->update(num).empty() || !(value > old)) - return; - - if (old != 0 && notify) - effectManager->trigger(1, this); - } - - if (statusWindow) - statusWindow->update(num); -} - -void LocalPlayer::setAttributeEffective(int num, int value) -{ - mAttributeEffective[num] = value; - if (skillDialog) - skillDialog->update(num); - - if (statusWindow) - statusWindow->update(num); -} - -void LocalPlayer::setCharacterPoints(int n) -{ - mCharacterPoints = n; - - if (statusWindow) - statusWindow->update(StatusWindow::CHAR_POINTS); -} - -void LocalPlayer::setCorrectionPoints(int n) -{ - mCorrectionPoints = n; - - if (statusWindow) - statusWindow->update(StatusWindow::CHAR_POINTS); -} - -void LocalPlayer::setSkillPoints(int points) -{ - mSkillPoints = points; - if (skillDialog) - skillDialog->update(); -} - -void LocalPlayer::setExperience(int skill, int current, int next, bool notify) -{ - std::pair<int, int> cur = getExperience(skill); - int diff = current - cur.first; - - cur = std::pair<int, int>(current, next); - - mSkillExp[skill] = cur; - - std::string name; - if (skillDialog) - name = skillDialog->update(skill); - - if (mMap && notify && cur.first != -1 && diff > 0 && !name.empty()) - { - addMessageToQueue(strprintf("%d %s xp", diff, name.c_str())); - } - - if (statusWindow) - statusWindow->update(skill); -} - -std::pair<int, int> LocalPlayer::getExperience(int skill) -{ - return mSkillExp[skill]; -} - -void LocalPlayer::setHp(int value) -{ - mHp = value; - - if (statusWindow) - statusWindow->update(StatusWindow::HP); -} - -void LocalPlayer::setMaxHp(int value) -{ - mMaxHp = value; - - if (statusWindow) - statusWindow->update(StatusWindow::HP); -} - -void LocalPlayer::setLevel(int value) -{ - mLevel = value; - - if (statusWindow) - statusWindow->update(StatusWindow::LEVEL); -} - -void LocalPlayer::setExp(int value, bool notify) -{ - if (mMap && notify && value > mExp) - { - addMessageToQueue(toString(value - mExp) + " xp"); - } - mExp = value; - - if (statusWindow) - statusWindow->update(StatusWindow::EXP); -} - -void LocalPlayer::setExpNeeded(int value) -{ - mExpNeeded = value; - - if (statusWindow) - statusWindow->update(StatusWindow::EXP); -} - -void LocalPlayer::setMP(int value) -{ - mMp = value; - - if (statusWindow) - statusWindow->update(StatusWindow::MP); -} - -void LocalPlayer::setMaxMP(int value) -{ - mMaxMp = value; - - if (statusWindow) - statusWindow->update(StatusWindow::MP); -} - -void LocalPlayer::setMoney(int value) -{ - mMoney = value; - - if (statusWindow) - statusWindow->update(StatusWindow::MONEY); -} - void LocalPlayer::pickedUp(const ItemInfo &itemInfo, int amount) { if (!amount) { if (config.getValue("showpickupchat", 1)) { - localChatTab->chatLog(_("Unable to pick up item."), BY_SERVER); + SERVER_NOTICE(_("Unable to pick up item.")) } } else @@ -1254,10 +999,9 @@ void LocalPlayer::pickedUp(const ItemInfo &itemInfo, int amount) { // TRANSLATORS: This sentence may be translated differently // for different grammatical numbers (singular, plural, ...) - localChatTab->chatLog(strprintf(ngettext("You picked up %d " + SERVER_NOTICE(strprintf(ngettext("You picked up %d " "[@@%d|%s@@].", "You picked up %d [@@%d|%s@@].", amount), - amount, itemInfo.getId(), itemInfo.getName().c_str()), - BY_SERVER); + amount, itemInfo.getId(), itemInfo.getName().c_str())) } if (mMap && config.getValue("showpickupparticle", 0)) @@ -1276,11 +1020,16 @@ int LocalPlayer::getAttackRange() } else { - Item *weapon = mEquipment->getEquipment(EQUIP_FIGHT1_SLOT); - if (weapon) + if (Net::getNetworkType() == ServerInfo::TMWATHENA) { - const ItemInfo info = weapon->getInfo(); - return info.getAttackRange(); + // TODO: Fix this to be more generic + Item *weapon = PlayerInfo::getEquipment( + TmwAthena::EQUIP_FIGHT1_SLOT); + if (weapon) + { + const ItemInfo info = weapon->getInfo(); + return info.getAttackRange(); + } } return 48; // unarmed range } @@ -1328,102 +1077,50 @@ void LocalPlayer::setGotoTarget(Being *target) } } -extern MiniStatusWindow *miniStatusWindow; - -void LocalPlayer::handleStatusEffect(StatusEffect *effect, int effectId) +void LocalPlayer::addMessageToQueue(const std::string &message, int color) { - Being::handleStatusEffect(effect, effectId); + mMessages.push_back(MessagePair(message, color)); +} - if (effect) +void LocalPlayer::event(Channels channel, const Mana::Event &event) +{ + if (channel == CHANNEL_ACTORSPRITE) { - effect->deliverMessage(); - effect->playSFX(); + if (event.getName() == EVENT_DESTROYED) + { + ActorSprite *actor = event.getActor("source"); - AnimatedSprite *sprite = effect->getIcon(); + if (mPickUpTarget == actor) + mPickUpTarget = 0; - if (!sprite) - { - // delete sprite, if necessary - for (unsigned int i = 0; i < mStatusEffectIcons.size();) - if (mStatusEffectIcons[i] == effectId) - { - mStatusEffectIcons.erase(mStatusEffectIcons.begin() + i); - miniStatusWindow->eraseIcon(i); - } - else - i++; + if (mTarget == actor) + mTarget = 0; } - else + } + else if (channel == CHANNEL_ATTRIBUTES) + { + if (event.getName() == EVENT_UPDATEATTRIBUTE) { - // replace sprite or append - bool found = false; - - for (unsigned int i = 0; i < mStatusEffectIcons.size(); i++) - if (mStatusEffectIcons[i] == effectId) - { - miniStatusWindow->setIcon(i, sprite); - found = true; - break; - } + if (event.getInt("id") == EXP) + { + int change = event.getInt("newValue") + - event.getInt("oldValue"); - if (!found) - { // add new - int offset = mStatusEffectIcons.size(); - miniStatusWindow->setIcon(offset, sprite); - mStatusEffectIcons.push_back(effectId); + addMessageToQueue(toString(change) + " xp"); } } } -} - -void LocalPlayer::initTargetCursor() -{ - // Load target cursors - loadTargetCursor("target-cursor-blue-s.png", 44, 35, false, TC_SMALL); - loadTargetCursor("target-cursor-red-s.png", 44, 35, true, TC_SMALL); - loadTargetCursor("target-cursor-blue-m.png", 62, 44, false, TC_MEDIUM); - loadTargetCursor("target-cursor-red-m.png", 62, 44, true, TC_MEDIUM); - loadTargetCursor("target-cursor-blue-l.png", 82, 60, false, TC_LARGE); - loadTargetCursor("target-cursor-red-l.png", 82, 60, true, TC_LARGE); -} - -void LocalPlayer::loadTargetCursor(const std::string &filename, - int width, int height, - bool outRange, TargetCursorSize size) -{ - assert(size > -1); - assert(size < 3); - - ImageSet *currentImageSet = Theme::getImageSetFromTheme(filename, - width, height); - Animation *anim = new Animation; - - for (unsigned int i = 0; i < currentImageSet->size(); ++i) + else if (channel == CHANNEL_CONFIG) { - anim->addFrame(currentImageSet->get(i), 75, - (16 - (currentImageSet->getWidth() / 2)), - (16 - (currentImageSet->getHeight() / 2))); - } - - SimpleAnimation *currentCursor = new SimpleAnimation(anim); - - const int index = outRange ? 1 : 0; - - mTargetCursorImages[index][size] = currentImageSet; - mTargetCursor[index][size] = currentCursor; -} - -void LocalPlayer::addMessageToQueue(const std::string &message, int color) -{ - mMessages.push_back(MessagePair(message, color)); -} + if (event.getName() == EVENT_CONFIGOPTIONCHANGED && + event.getString("option") == "showownname") + { + setShowName(config.getValue("showownname", 1)); + } -void LocalPlayer::optionChanged(const std::string &value) -{ - if (value == "showownname") - { - setShowName(config.getValue("showownname", 1)); } + + Being::event(channel, event); } void LocalPlayer::changeAwayMode() diff --git a/src/localplayer.h b/src/localplayer.h index 7706caeb..a5328182 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -22,9 +22,9 @@ #ifndef LOCALPLAYER_H #define LOCALPLAYER_H -#include "player.h" +#include "being.h" -#include "gui/userpalette.h" +#include "resources/userpalette.h" #include <guichan/actionlistener.hpp> @@ -32,84 +32,22 @@ #include <vector> class ChatTab; -class Equipment; class FloorItem; class ImageSet; -class Inventory; class Item; class Map; class OkDialog; - -struct Special -{ - int currentMana; - int neededMana; - int recharge; -}; - class AwayListener : public gcn::ActionListener { public: void action(const gcn::ActionEvent &event); }; - -/** - * Attributes used during combat. Available to all the beings. - */ -enum -{ -BASE_ATTR_BEGIN = 0, - BASE_ATTR_PHY_ATK_MIN = BASE_ATTR_BEGIN, - BASE_ATTR_PHY_ATK_DELTA, - /**< Physical attack power. */ - BASE_ATTR_MAG_ATK, /**< Magical attack power. */ - BASE_ATTR_PHY_RES, /**< Resistance to physical damage. */ - BASE_ATTR_MAG_RES, /**< Resistance to magical damage. */ - BASE_ATTR_EVADE, /**< Ability to avoid hits. */ - BASE_ATTR_HIT, /**< Ability to hit stuff. */ - BASE_ATTR_HP, /**< Hit Points (Base value: maximum, Modded value: current) */ - BASE_ATTR_HP_REGEN,/**< number of HP regenerated every 10 game ticks */ - BASE_ATTR_END, - BASE_ATTR_NB = BASE_ATTR_END - BASE_ATTR_BEGIN, - - BASE_ELEM_BEGIN = BASE_ATTR_END, - BASE_ELEM_NEUTRAL = BASE_ELEM_BEGIN, - BASE_ELEM_FIRE, - BASE_ELEM_WATER, - BASE_ELEM_EARTH, - BASE_ELEM_AIR, - BASE_ELEM_SACRED, - BASE_ELEM_DEATH, - BASE_ELEM_END, - BASE_ELEM_NB = BASE_ELEM_END - BASE_ELEM_BEGIN, - - NB_BEING_ATTRIBUTES = BASE_ELEM_END -}; - -/** - * Attributes of characters. Used to derive being attributes. - */ -enum -{ - CHAR_ATTR_BEGIN = NB_BEING_ATTRIBUTES, - CHAR_ATTR_STRENGTH = CHAR_ATTR_BEGIN, - CHAR_ATTR_AGILITY, - CHAR_ATTR_DEXTERITY, - CHAR_ATTR_VITALITY, - CHAR_ATTR_INTELLIGENCE, - CHAR_ATTR_WILLPOWER, - CHAR_ATTR_END, - CHAR_ATTR_NB = CHAR_ATTR_END - CHAR_ATTR_BEGIN, - - NB_CHARACTER_ATTRIBUTES = CHAR_ATTR_END -}; - /** * The local player character. */ -class LocalPlayer : public Player +class LocalPlayer : public Being { public: /** @@ -144,11 +82,6 @@ class LocalPlayer : public Player virtual void nextTile(unsigned char dir); /** - * Returns the player's inventory. - */ - Inventory *getInventory() const { return mInventory; } - - /** * Check the player has permission to invite users to specific guild */ bool checkInviteRights(const std::string &guildName); @@ -158,9 +91,6 @@ class LocalPlayer : public Player */ void inviteToGuild(Being *being); - void clearInventory(); - void setInvItem(int index, int id, int amount); - void pickUp(FloorItem *item); /** @@ -172,33 +102,8 @@ class LocalPlayer : public Player * Gets the attack range. */ int getAttackRange(); - - /** - * Returns true when the player is ready to accept a trade offer. - * Returns false otherwise. - */ - bool tradeRequestOk() const { return !mTrading; } - - /** - * 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 useSpecial(int id); - - void setSpecialStatus(int id, int current, int max, int recharge); - - const std::map<int, Special> &getSpecialStatus() const - { return mSpecials; } - void attack(Being *target = NULL, bool keep = false); - /** - * Triggers whether or not to show the name as a GM name. - */ - virtual void setGM(bool gm); - void setGMLevel(int level); void stopAttack(); @@ -257,16 +162,6 @@ class LocalPlayer : public Player */ void stopWalking(bool sendToServer = true); - /** - * Uses a character point to raise an attribute - */ - void raiseAttribute(int attr); - - /** - * Uses a correction point to lower an attribute - */ - void lowerAttribute(int attr); - void toggleSit(); void emote(Uint8 emotion); @@ -275,85 +170,6 @@ class LocalPlayer : public Player */ void pickedUp(const ItemInfo &itemInfo, int amount); - int getHp() const - { return mHp; } - - int getMaxHp() const - { return mMaxHp; } - - void setHp(int value); - - void setMaxHp(int value); - - int getLevel() const - { return mLevel; } - - void setLevel(int value); - - void setExp(int value, bool notify = true); - - int getExp() const - { return mExp; } - - void setExpNeeded(int value); - - int getExpNeeded() const - { return mExpNeeded; } - - void setMP(int value); - - int getMP() const - { return mMp; } - - void setMaxMP(int value); - - int getMaxMP() const - { return mMaxMp; } - - int getMoney() const - { return mMoney; } - - void setMoney(int value); - - int getTotalWeight() const - { return mTotalWeight; } - - void setTotalWeight(int value); - - int getMaxWeight() const - { return mMaxWeight; } - - void setMaxWeight(int value); - - int getAttributeBase(int num) - { return mAttributeBase[num]; } - - void setAttributeBase(int num, int value, bool notify = true); - - int getAttributeEffective(int num) - { return mAttributeEffective[num]; } - - void setAttributeEffective(int num, int value); - - int getCharacterPoints() const - { return mCharacterPoints; } - - void setCharacterPoints(int n); - - int getCorrectionPoints() const - { return mCorrectionPoints; } - - void setCorrectionPoints(int n); - - int getSkillPoints() const - { return mSkillPoints; } - - void setSkillPoints(int points); - - void setExperience(int skill, int current, int next, bool notify = true); - - std::pair<int, int> getExperience(int skill); - void setShowIp(bool show) { mShowIp = show; } @@ -380,10 +196,7 @@ class LocalPlayer : public Player void addMessageToQueue(const std::string &message, int color = UserPalette::EXP_INFO); - /** - * Called when a option (set with config.addListener()) is changed - */ - void optionChanged(const std::string &value); + void event(Channels channel, const Mana::Event &event); /** * Tells the engine wether to check @@ -397,19 +210,10 @@ class LocalPlayer : public Player */ bool getCheckNameSetting() const { return mUpdateName; } - /** Keeps the Equipment related values */ - const std::auto_ptr<Equipment> mEquipment; - protected: - /** Whether or not the name settings have changed */ bool mUpdateName; - virtual void handleStatusEffect(StatusEffect *effect, int effectId); - - // Colors don't change for local player - virtual void updateColors() {} - void startWalking(unsigned char dir); int mAttackRange; @@ -417,73 +221,32 @@ class LocalPlayer : public Player int mTargetTime; /** How long the being has been targeted **/ int mLastTarget; /** Time stamp of last targeting action, -1 if none. */ - // Character status: - typedef std::map<int, int> IntMap; - IntMap mAttributeBase; - IntMap mAttributeEffective; - std::map<int, std::pair<int, int> > mSkillExp; - int mCharacterPoints; - int mCorrectionPoints; - int mLevelProgress; - std::map<int, Special> mSpecials; - char mSpecialRechargeUpdateNeeded; - int mLevel; - int mExp, mExpNeeded; - int mMp, mMaxMp; - int mMoney; - int mTotalWeight; - int mMaxWeight; - int mHp; - int mMaxHp; - int mSkillPoints; - int mGMLevel; Being *mTarget; FloorItem *mPickUpTarget; - bool mTrading; bool mGoingToTarget; bool mKeepAttacking; /** Whether or not to continue to attack */ int mLastAction; /**< Time stamp of the last action, -1 if none. */ int mWalkingDir; /**< The direction the player is walking in. */ bool mPathSetByMouse; /**< Tells if the path was set using mouse */ - std::vector<int> mStatusEffectIcons; - - Inventory *mInventory; - int mLocalWalkTime; /**< Timestamp used to control keyboard walk messages flooding */ - /** Load the target cursors into memory */ - void initTargetCursor(); - - /** - * Helper function for loading target cursors - */ - void loadTargetCursor(const std::string &filename, - int width, int height, - bool outRange, Being::TargetCursorSize size); - - /** Images of the target cursor. */ - ImageSet *mTargetCursorImages[2][NUM_TC]; - - /** Animated target cursors. */ - SimpleAnimation *mTargetCursor[2][NUM_TC]; - typedef std::pair<std::string, int> MessagePair; - /** Queued exp messages*/ + /** Queued messages*/ std::list<MessagePair> mMessages; int mMessageTime; + + bool mShowIp; + AwayListener *mAwayListener; OkDialog *mAwayDialog; - int mAfkTime; bool mAwayMode; - - bool mShowIp; }; extern LocalPlayer *player_node; diff --git a/src/log.cpp b/src/log.cpp index 5880e108..a147c107 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -21,8 +21,6 @@ #include "log.h" -#include "gui/widgets/chattab.h" - #ifdef WIN32 #include <windows.h> #elif __APPLE__ @@ -32,11 +30,12 @@ #include <sys/time.h> #include <iostream> #include <sstream> +#include <stdarg.h> +#include <stdio.h> #include <stdlib.h> Logger::Logger(): - mLogToStandardOut(true), - mChatWindow(NULL) + mLogToStandardOut(true) { } @@ -99,11 +98,6 @@ void Logger::log(const char *log_text, ...) std::cout << timeStr.str() << buf << std::endl; } - if (mChatWindow) - { - localChatTab->chatLog(buf, BY_LOGGER); - } - // Delete temporary buffer delete[] buf; } @@ -24,8 +24,6 @@ #include <fstream> -class ChatWindow; - /** * The Log Class : Useful to write debug or info messages */ @@ -53,11 +51,6 @@ class Logger void setLogToStandardOut(bool value) { mLogToStandardOut = value; } /** - * Enables logging to chat window - */ - void setChatWindow(ChatWindow *window) { mChatWindow = window; } - - /** * Enters a message in the log. The message will be timestamped. */ void log(const char *log_text, ...) @@ -75,7 +68,6 @@ class Logger private: std::ofstream mLogFile; bool mLogToStandardOut; - ChatWindow *mChatWindow; }; extern Logger *logger; diff --git a/src/main.cpp b/src/main.cpp index c130bba9..41f1c3d5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -40,6 +40,10 @@ static void printHelp() std::cout << _("mana [options] [mana-file]") << endl << endl + << _("[mana-file] : The mana file is an XML file (.mana)") << endl + << _(" used to set custom parameters") << endl + << _(" to the mana client.") + << endl << endl << _("Options:") << endl << _(" -v --version : Display the version") << endl << _(" -h --help : Display this help") << endl @@ -54,7 +58,8 @@ static void printHelp() "character") << endl << _(" -u --skip-update : Skip the update downloads") << endl << _(" -d --data : Directory to load game data from") << endl - << _(" -L --localdata-dir : Directory to use as local data directory") << endl + << _(" --localdata-dir : Directory to use as local data directory") << endl + << _(" --chat-log-dir : Chat log dir to use") << endl << _(" --screenshot-dir : Directory to store screenshots") << endl #ifdef USE_OPENGL << _(" --no-opengl : Disable OpenGL for this session") << endl @@ -69,7 +74,7 @@ static void printVersion() static void parseOptions(int argc, char *argv[], Client::Options &options) { - const char *optstring = "hvud:U:P:Dc:s:p:C:L:"; + const char *optstring = "hvud:U:P:Dc:s:p:C:"; const struct option long_options[] = { { "config-dir", required_argument, 0, 'C' }, @@ -85,6 +90,7 @@ static void parseOptions(int argc, char *argv[], Client::Options &options) { "skip-update", no_argument, 0, 'u' }, { "username", required_argument, 0, 'U' }, { "no-opengl", no_argument, 0, 'O' }, + { "chat-log-dir", required_argument, 0, 'T' }, { "version", no_argument, 0, 'v' }, { "screenshot-dir", required_argument, 0, 'i' }, { 0 } @@ -143,6 +149,8 @@ static void parseOptions(int argc, char *argv[], Client::Options &options) case 'O': options.noOpenGL = true; break; + case 'T': + options.chatLogDir = std::string(optarg); case 'i': options.screenshotDir = optarg; break; diff --git a/src/map.cpp b/src/map.cpp index f845f2ff..09782699 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -21,19 +21,20 @@ #include "map.h" -#include "beingmanager.h" +#include "actorspritemanager.h" #include "client.h" #include "configuration.h" #include "graphics.h" #include "particle.h" #include "simpleanimation.h" -#include "sprite.h" #include "tileset.h" #include "resources/ambientlayer.h" #include "resources/image.h" #include "resources/resourcemanager.h" +#include "net/net.h" + #include "utils/dtor.h" #include "utils/stringutils.h" @@ -122,7 +123,7 @@ Image* MapLayer::getTile(int x, int y) const void MapLayer::draw(Graphics *graphics, int startX, int startY, int endX, int endY, int scrollX, int scrollY, - const MapSprites &sprites, int debugFlags) const + const Actors &actors, int debugFlags) const { startX -= mX; startY -= mY; @@ -134,47 +135,86 @@ void MapLayer::draw(Graphics *graphics, int startX, int startY, if (endX > mWidth) endX = mWidth; if (endY > mHeight) endY = mHeight; - MapSprites::const_iterator si = sprites.begin(); + Actors::const_iterator ai = actors.begin(); + + int dx = (mX * 32) - scrollX; + int dy = (mY * 32) - scrollY + 32; for (int y = startY; y < endY; y++) { - // If drawing the fringe layer, make sure all sprites above this row of + int y32 = y * 32; + + // If drawing the fringe layer, make sure all actors above this row of // tiles have been drawn if (mIsFringeLayer) { - while (si != sprites.end() && (*si)->getPixelY() <= y * 32) + while (ai != actors.end() && (*ai)->getPixelY() <= y * 32) { - (*si)->setAlpha(1.0f); - (*si)->draw(graphics, -scrollX, -scrollY); - si++; + (*ai)->draw(graphics, -scrollX, -scrollY); + ai++; } } - for (int x = startX; x < endX; x++) + if (debugFlags != Map::MAP_SPECIAL3) { - Image *img = getTile(x, y); - if (img) + const int py0 = y32 + dy; + + for (int x = startX; x < endX; x++) { - const int px = (x + mX) * 32 - scrollX; - const int py = (y + mY) * 32 - scrollY + 32 - img->getHeight(); - if (debugFlags != Map::MAP_SPECIAL || img->getHeight() <= 32) - graphics->drawImage(img, px, py); + Image *img = getTile(x, y); + if (img) + { + const int px = (x * 32) + dx; + const int py = py0 - img->getHeight(); + if ((debugFlags != Map::MAP_SPECIAL + && debugFlags != Map::MAP_SPECIAL2) + || img->getHeight() <= 32) + { + int width = 0; + int c = getTileDrawWidth(x, y, endX, width); + if (!c) + { + graphics->drawImage(img, px, py); + } + else + { + graphics->drawImagePattern(img, px, py, + width, img->getHeight()); + } + x += c; + } + } } } } - // Draw any remaining sprites + // Draw any remaining actors if (mIsFringeLayer) { - while (si != sprites.end()) + while (ai != actors.end()) { - (*si)->setAlpha(1.0f); - (*si)->draw(graphics, -scrollX, -scrollY); - si++; + (*ai)->draw(graphics, -scrollX, -scrollY); + ai++; } } } +int MapLayer::getTileDrawWidth(int x1, int y1, int endX, int &width) const +{ + Image *img1 = getTile(x1, y1); + int c = 0; + width = img1->getWidth(); + for (int x = x1 + 1; x < endX; x++) + { + Image *img = getTile(x, y1); + if (img != img1) + break; + c ++; + width += img->getWidth(); + } + return c; +} + Map::Map(int width, int height, int tileWidth, int tileHeight): mWidth(width), mHeight(height), mTileWidth(tileWidth), mTileHeight(tileHeight), @@ -283,7 +323,7 @@ void Map::addTileset(Tileset *tileset) mMaxTileHeight = tileset->getHeight(); } -bool spriteCompare(const Sprite *a, const Sprite *b) +bool actorCompare(const Actor *a, const Actor *b) { return a->getPixelY() < b->getPixelY(); } @@ -309,25 +349,47 @@ void Map::draw(Graphics *graphics, int scrollX, int scrollY) int endX = (graphics->getWidth() + scrollX + mTileWidth - 1) / mTileWidth; int endY = endPixelY / mTileHeight; - // Make sure sprites are sorted ascending by Y-coordinate + // Make sure actors are sorted ascending by Y-coordinate // so that they overlap correctly - mSprites.sort(spriteCompare); + mActors.sort(actorCompare); // update scrolling of all ambient layers updateAmbientLayers(scrollX, scrollY); // Draw backgrounds drawAmbientLayers(graphics, BACKGROUND_LAYERS, scrollX, scrollY, - (int) config.getValue("OverlayDetail", 2)); + config.getIntValue("OverlayDetail")); // draw the game world Layers::const_iterator layeri = mLayers.begin(); - for (; layeri != mLayers.end(); ++layeri) + + bool overFringe = false; + + if (mDebugFlags == MAP_SPECIAL3) { - (*layeri)->draw(graphics, - startX, startY, endX, endY, - scrollX, scrollY, - mSprites, mDebugFlags); + for (; layeri != mLayers.end(); ++layeri) + { + if ((*layeri)->isFringeLayer()) + { + (*layeri)->draw(graphics, + startX, startY, endX, endY, + scrollX, scrollY, + mActors, mDebugFlags); + } + } + } + else + { + for (; layeri != mLayers.end() && !overFringe; ++layeri) + { + if ((*layeri)->isFringeLayer() && mDebugFlags == MAP_SPECIAL2) + overFringe = true; + + (*layeri)->draw(graphics, + startX, startY, endX, endY, + scrollX, scrollY, + mActors, mDebugFlags); + } } // If the transparency hasn't been disabled, @@ -335,24 +397,25 @@ void Map::draw(Graphics *graphics, int scrollX, int scrollY) { // We draw beings with a lower opacity to make them visible // even when covered by a wall or some other elements... - MapSprites::const_iterator si = mSprites.begin(); - while (si != mSprites.end()) + Actors::const_iterator ai = mActors.begin(); + while (ai != mActors.end()) { - if (Sprite *sprite = *si) + if (Actor *actor = *ai) { - // For now, just draw sprites with only one layer. - if (sprite->getNumberOfLayers() == 1) + // For now, just draw actors with only one layer. + if (actor->getNumberOfLayers() == 1) { - sprite->setAlpha(0.3f); - sprite->draw(graphics, -scrollX, -scrollY); + actor->setAlpha(0.3f); + actor->draw(graphics, -scrollX, -scrollY); + actor->setAlpha(1.0f); } } - si++; + ai++; } } drawAmbientLayers(graphics, FOREGROUND_LAYERS, scrollX, scrollY, - (int) config.getValue("OverlayDetail", 2)); + config.getIntValue("OverlayDetail")); } void Map::drawCollision(Graphics *graphics, int scrollX, int scrollY, @@ -528,12 +591,14 @@ bool Map::getWalk(int x, int y, unsigned char walkmask) const bool Map::occupied(int x, int y) const { - const Beings &beings = beingManager->getAll(); - for (Beings::const_iterator i = beings.begin(); i != beings.end(); i++) + const ActorSprites &actors = actorSpriteManager->getAll(); + ActorSpritesConstIterator it, it_end; + for (it = actors.begin(), it_end = actors.end(); it != it_end; it++) { - const Being *being = *i; + const ActorSprite *actor = *it; - if (being->getTileX() == x && being->getTileY() == y) + if (actor->getTileX() == x && actor->getTileY() == y && + actor->getType() != ActorSprite::FLOOR_ITEM) return true; } @@ -550,15 +615,15 @@ MetaTile *Map::getMetaTile(int x, int y) const return &mMetaTiles[x + y * mWidth]; } -MapSprite Map::addSprite(Sprite *sprite) +Actors::iterator Map::addActor(Actor *actor) { - mSprites.push_front(sprite); - return mSprites.begin(); + mActors.push_front(actor); + return mActors.begin(); } -void Map::removeSprite(MapSprite iterator) +void Map::removeActor(Actors::iterator iterator) { - mSprites.erase(iterator); + mActors.erase(iterator); } const std::string Map::getMusicFile() const @@ -791,7 +856,9 @@ Path Map::findPath(int startX, int startY, int destX, int destY, // It costs extra to walk through a being (needs to be enough // to make it more attractive to walk around). - if (occupied(x, y)) + // N.B.: Specific to TmwAthena for now. + if (Net::getNetworkType() == ServerInfo::TMWATHENA && + occupied(x, y)) { Gcost += 3 * basicCost; } @@ -897,7 +964,7 @@ void Map::initializeParticleEffects(Particle *particleEngine) { Particle *p; - if (config.getValue("particleeffects", 1)) + if (config.getBoolValue("particleeffects")) { for (std::list<ParticleEffectData>::iterator i = particleEffects.begin(); i != particleEffects.end(); @@ -22,6 +22,7 @@ #ifndef MAP_H #define MAP_H +#include "actor.h" #include "position.h" #include "properties.h" @@ -31,16 +32,12 @@ class Animation; class AmbientLayer; class Graphics; -class Image; class MapLayer; class Particle; class SimpleAnimation; -class Sprite; class Tileset; typedef std::vector<Tileset*> Tilesets; -typedef std::list<Sprite*> MapSprites; -typedef MapSprites::iterator MapSprite; typedef std::vector<MapLayer*> Layers; /** @@ -91,7 +88,7 @@ class MapLayer public: /** * Constructor, taking layer origin, size and whether this layer is the - * fringe layer. The fringe layer is the layer that draws the sprites. + * fringe layer. The fringe layer is the layer that draws the actors. * There can be only one fringe layer per map. */ MapLayer(int x, int y, int width, int height, bool isFringeLayer); @@ -121,20 +118,25 @@ class MapLayer * expected to be in map range and will be translated to local layer * coordinates and clipped to the layer's dimensions. * - * The given sprites are only drawn when this layer is the fringe + * The given actors are only drawn when this layer is the fringe * layer. */ void draw(Graphics *graphics, int startX, int startY, int endX, int endY, int scrollX, int scrollY, - const MapSprites &sprites, + const Actors &actors, int mDebugFlags) const; + bool isFringeLayer() + { return mIsFringeLayer; } + + int getTileDrawWidth(int x1, int y1, int endX, int &width) const; + private: int mX, mY; int mWidth, mHeight; - bool mIsFringeLayer; /**< Whether the sprites are drawn. */ + bool mIsFringeLayer; /**< Whether the actors are drawn. */ Image **mTiles; }; @@ -164,7 +166,9 @@ class Map : public Properties { MAP_NORMAL = 0, MAP_DEBUG = 1, - MAP_SPECIAL = 2 + MAP_SPECIAL = 2, + MAP_SPECIAL2 = 3, + MAP_SPECIAL3 = 4 }; /** @@ -190,7 +194,7 @@ class Map : public Properties /** * Draws the map to the given graphics output. This method draws all - * layers, sprites and overlay effects. + * layers, actors and overlay effects. * * TODO: For efficiency reasons, this method could take into account * the clipping rectangle set on the Graphics object. However, @@ -295,16 +299,6 @@ class Map : public Properties unsigned char walkmask, int maxCost = 20); /** - * Adds a sprite to the map. - */ - MapSprite addSprite(Sprite *sprite); - - /** - * Removes a sprite from the map. - */ - void removeSprite(MapSprite iterator); - - /** * Adds a particle effect */ void addParticleEffect(const std::string &effectFile, int x, int y, int w = 0, int h = 0); @@ -329,6 +323,19 @@ class Map : public Properties */ TileAnimation *getAnimationForGid(int gid) const; + protected: + friend class Actor; + + /** + * Adds an actor to the map. + */ + Actors::iterator addActor(Actor *actor); + + /** + * Removes an actor from the map. + */ + void removeActor(Actors::iterator iterator); + private: enum LayerType @@ -364,7 +371,7 @@ class Map : public Properties MetaTile *mMetaTiles; Layers mLayers; Tilesets mTilesets; - MapSprites mSprites; + Actors mActors; // debug flags int mDebugFlags; diff --git a/src/monster.cpp b/src/monster.cpp deleted file mode 100644 index ca156821..00000000 --- a/src/monster.cpp +++ /dev/null @@ -1,203 +0,0 @@ -/* - * The Mana Client - * Copyright (C) 2004-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * - * This file is part of The Mana Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "monster.h" - -#include "animatedsprite.h" -#include "client.h" -#include "localplayer.h" -#include "particle.h" -#include "sound.h" -#include "text.h" - -#include "gui/userpalette.h" - -#include "net/net.h" - -#include "resources/monsterdb.h" -#include "resources/monsterinfo.h" -#include "configuration.h" - -Monster::Monster(int id, int subtype, Map *map): - Being(id, subtype, map), - mAttackType(1) -{ - setSubtype(subtype); - - mNameColor = &userPalette->getColor(UserPalette::MONSTER); - mTextColor = &userPalette->getColor(UserPalette::MONSTER); - - Being::setName(getInfo().getName()); -} - -void Monster::logic() -{ - if ((Net::getNetworkType() == ServerInfo::TMWATHENA) && (mAction != STAND)) - { - mFrame = (int) ((get_elapsed_time(mWalkTime) * 4) / getWalkSpeed().x); - - if (mFrame >= 4 && mAction != DEAD) - nextTile(); - } - - Being::logic(); -} - - -void Monster::setAction(Action action, int attackType) -{ - SpriteAction currentAction = ACTION_INVALID; - int rotation = 0; - std::string particleEffect; - - switch (action) - { - case WALK: - currentAction = ACTION_WALK; - break; - case DEAD: - currentAction = ACTION_DEAD; - sound.playSfx(getInfo().getSound(MONSTER_EVENT_DIE)); - break; - case ATTACK: - mAttackType = attackType; - currentAction = getInfo().getAttackAction(attackType); - for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) - (*it)->reset(); - - //attack particle effect - particleEffect = getInfo().getAttackParticleEffect(attackType); - if (!particleEffect.empty() && Particle::enabled) - { - switch (mSpriteDirection) - { - case DIRECTION_DOWN: rotation = 0; break; - case DIRECTION_LEFT: rotation = 90; break; - case DIRECTION_UP: rotation = 180; break; - case DIRECTION_RIGHT: rotation = 270; break; - default: break; - } - Particle *p; - p = particleEngine->addEffect(particleEffect, 0, 0, rotation); - controlParticle(p); - } - break; - case STAND: - currentAction = ACTION_STAND; - break; - case HURT: - // Not implemented yet - break; - case SIT: - // Also not implemented yet - break; - } - - if (currentAction != ACTION_INVALID) - { - for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) - if (*it) - (*it)->play(currentAction); - mAction = action; - } -} - -void Monster::setSubtype(Uint16 subtype) -{ - Being::setSubtype(subtype); - - const MonsterInfo &info = getInfo(); - - // Setup Monster sprites - const std::list<std::string> &sprites = info.getSprites(); - - mSprites.clear(); - for (std::list<std::string>::const_iterator i = sprites.begin(); - i != sprites.end(); i++) - { - std::string file = paths.getValue("sprites", - "graphics/sprites/") + *i; - mSprites.push_back(AnimatedSprite::load(file)); - } - - // Ensure that something is shown - if (mSprites.size() == 0) - { - mSprites.push_back(AnimatedSprite::load( - paths.getValue("sprites", "graphics/sprites/") + - paths.getValue("spriteErrorFile", "error.xml") )); - } - - if (Particle::enabled) - { - const std::list<std::string> &particleEffects = info.getParticleEffects(); - for (std::list<std::string>::const_iterator i = particleEffects.begin(); - i != particleEffects.end(); i++) - { - controlParticle(particleEngine->addEffect((*i), 0, 0)); - } - } -} - -void Monster::handleAttack(Being *victim, int damage, AttackType type) -{ - Being::handleAttack(victim, damage, type); - - const MonsterInfo &mi = getInfo(); - sound.playSfx(mi.getSound((damage > 0) ? - MONSTER_EVENT_HIT : MONSTER_EVENT_MISS)); - - fireMissile(victim, mi.getAttackMissileParticle(mAttackType)); -} - -void Monster::takeDamage(Being *attacker, int amount, AttackType type) -{ - if (amount > 0) - sound.playSfx(getInfo().getSound(MONSTER_EVENT_HURT)); - - Being::takeDamage(attacker, amount, type); -} - -Being::TargetCursorSize Monster::getTargetCursorSize() const -{ - return getInfo().getTargetCursorSize(); -} - -const MonsterInfo &Monster::getInfo() const -{ - return MonsterDB::get(mSubType); -} - -void Monster::updateCoords() -{ - if (mDispName) - { - mDispName->adviseXY(getPixelX(), - getPixelY() - getHeight() - mDispName->getHeight()); - } -} - -void Monster::showName() -{ - Being::showName(); - - updateCoords(); -} diff --git a/src/monster.h b/src/monster.h deleted file mode 100644 index 9bb8e3b9..00000000 --- a/src/monster.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * The Mana Client - * Copyright (C) 2004-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * - * This file is part of The Mana Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef MONSTER_H -#define MONSTER_H - -#include "being.h" - -class MonsterInfo; -class Text; - -class Monster : public Being -{ - public: - Monster(int id, int subtype, Map *map); - - virtual void logic(); - - virtual void setAction(Action action, int attackType = 0); - - virtual Type getType() const { return MONSTER; } - - virtual void setSubtype(Uint16 subtype); - - virtual TargetCursorSize - getTargetCursorSize() const; - - /** - * Handles an attack of another being by this monster. Plays a hit or - * miss sound when appropriate. - * - * @param victim the victim being - * @param damage the amount of damage dealt (0 means miss) - * @param type the attack type - */ - virtual void handleAttack(Being *victim, int damage, AttackType type); - - /** - * Puts a damage bubble above this monster and plays the hurt sound - * - * @param attacker the attacking being - * @param damage the amount of damage recieved (0 means miss) - * @param type the attack type - */ - virtual void takeDamage(Being *attacker, int amount, AttackType type); - - /** - * Returns the MonsterInfo, with static data about this monster. - */ - const MonsterInfo& getInfo() const; - - /** - * Gets the way the monster is blocked by other objects - */ - virtual unsigned char getWalkMask() const - { - return Map::BLOCKMASK_WALL - | Map::BLOCKMASK_CHARACTER - | Map::BLOCKMASK_MONSTER; - } - - protected: - /** - * Gets the way the monster blocks pathfinding for other objects - */ - virtual Map::BlockType getBlockType() const - { return Map::BLOCKTYPE_MONSTER; } - - /** - * Update the text when the monster moves - */ - void updateCoords(); - - void showName(); - - private: - int mAttackType; -}; - -#endif diff --git a/src/net/adminhandler.h b/src/net/adminhandler.h index 23e9abc0..3ed96dbd 100644 --- a/src/net/adminhandler.h +++ b/src/net/adminhandler.h @@ -29,6 +29,8 @@ namespace Net { class AdminHandler { public: + virtual ~AdminHandler() {} + virtual void announce(const std::string &text) = 0; virtual void localAnnounce(const std::string &text) = 0; @@ -49,8 +51,6 @@ class AdminHandler virtual void mute(int playerId, int type, int limit) = 0; - virtual ~AdminHandler() {} - // TODO }; diff --git a/src/net/charhandler.h b/src/net/charhandler.h index 4a813e21..0694e39e 100644 --- a/src/net/charhandler.h +++ b/src/net/charhandler.h @@ -23,14 +23,13 @@ #define CHARHANDLER_H #include "localplayer.h" -#include "logindata.h" +#include "playerinfo.h" #include <iosfwd> #include <vector> class CharCreateDialog; class CharSelectDialog; -class LocalPlayer; namespace Net { @@ -41,7 +40,7 @@ struct Character { Character() : slot(0), - dummy(new LocalPlayer) + dummy(0) { } @@ -52,6 +51,7 @@ struct Character int slot; /**< The index in the list of characters */ LocalPlayer *dummy; /**< A dummy representing this character */ + PlayerInfoBackend data; }; typedef std::list<Character*> Characters; @@ -59,6 +59,8 @@ typedef std::list<Character*> Characters; class CharHandler { public: + virtual ~CharHandler() {} + virtual void setCharSelectDialog(CharSelectDialog *window) = 0; virtual void setCharCreateDialog(CharCreateDialog *window) = 0; @@ -75,13 +77,11 @@ class CharHandler virtual void switchCharacter() = 0; - virtual int baseSprite() const = 0; + virtual unsigned int baseSprite() const = 0; - virtual int hairSprite() const = 0; + virtual unsigned int hairSprite() const = 0; - virtual int maxSprite() const = 0; - - virtual ~CharHandler() {} + virtual unsigned int maxSprite() const = 0; protected: CharHandler(): diff --git a/src/net/chathandler.h b/src/net/chathandler.h index d1449698..fbaa8dba 100644 --- a/src/net/chathandler.h +++ b/src/net/chathandler.h @@ -28,6 +28,8 @@ namespace Net { class ChatHandler { public: + virtual ~ChatHandler() {} + virtual void talk(const std::string &text) = 0; virtual void me(const std::string &text) = 0; @@ -53,8 +55,6 @@ class ChatHandler virtual void kickUser(int channelId, const std::string &name) = 0; virtual void who() = 0; - - virtual ~ChatHandler() {} }; } diff --git a/src/net/download.cpp b/src/net/download.cpp index a2cd4910..83ab180f 100644 --- a/src/net/download.cpp +++ b/src/net/download.cpp @@ -221,8 +221,8 @@ int Download::downloadThread(void *ptr) } curl_easy_setopt(d->mCurl, CURLOPT_USERAGENT, - strprintf(PACKAGE_EXTENDED_VERSION, branding - .getValue("appShort", "mana").c_str()).c_str()); + strprintf(PACKAGE_EXTENDED_VERSION, + branding.getStringValue("appShort").c_str()).c_str()); curl_easy_setopt(d->mCurl, CURLOPT_ERRORBUFFER, d->mError); curl_easy_setopt(d->mCurl, CURLOPT_URL, d->mUrl.c_str()); curl_easy_setopt(d->mCurl, CURLOPT_NOPROGRESS, 0); diff --git a/src/net/gamehandler.h b/src/net/gamehandler.h index 774de16c..0c5d889f 100644 --- a/src/net/gamehandler.h +++ b/src/net/gamehandler.h @@ -22,8 +22,6 @@ #ifndef MAPHANDLER_H #define MAPHANDLER_H -#include "logindata.h" - #include <iosfwd> namespace Net { @@ -31,16 +29,14 @@ namespace Net { class GameHandler { public: + virtual ~GameHandler() {} + virtual void connect() = 0; virtual bool isConnected() = 0; virtual void disconnect() = 0; - virtual void inGame() = 0; - - virtual void mapLoaded(const std::string &mapName) = 0; - virtual void who() = 0; virtual void quit() = 0; @@ -49,7 +45,10 @@ class GameHandler virtual bool removeDeadBeings() const = 0; - virtual ~GameHandler() {} + /** + * Tells whether the protocol is using the MP status bar + */ + virtual bool canUseMagicBar() const = 0; }; } // namespace Net diff --git a/src/net/generalhandler.h b/src/net/generalhandler.h index 222b430a..4b8474dd 100644 --- a/src/net/generalhandler.h +++ b/src/net/generalhandler.h @@ -19,9 +19,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "client.h" -#include "main.h" - #ifndef GENERALHANDLER_H #define GENERALHANDLER_H @@ -40,13 +37,7 @@ class GeneralHandler virtual void flushNetwork() = 0; - virtual void guiWindowsLoaded() = 0; - - virtual void guiWindowsUnloaded() = 0; - virtual void clearHandlers() = 0; - - virtual void stateChanged(State oldState, State newState) = 0; }; } // namespace Net diff --git a/src/net/guildhandler.h b/src/net/guildhandler.h index 1696b2d5..e4513cbb 100644 --- a/src/net/guildhandler.h +++ b/src/net/guildhandler.h @@ -23,10 +23,11 @@ #define GUILDHANDLER_H #include "guild.h" -#include "player.h" #include <iosfwd> +class Being; + namespace Net { class GuildHandler @@ -40,7 +41,7 @@ class GuildHandler virtual void invite(int guildId, const std::string &name) = 0; - virtual void invite(int guildId, Player *player) = 0; + virtual void invite(int guildId, Being *being) = 0; virtual void inviteResponse(int guildId, bool response) = 0; diff --git a/src/net/inventoryhandler.h b/src/net/inventoryhandler.h index e48043a7..93b56a40 100644 --- a/src/net/inventoryhandler.h +++ b/src/net/inventoryhandler.h @@ -32,33 +32,12 @@ namespace Net { class InventoryHandler { public: - virtual void equipItem(const Item *item) = 0; - - virtual void unequipItem(const Item *item) = 0; - - virtual void useItem(const Item *item) = 0; - - virtual void dropItem(const Item *item, int amount) = 0; + virtual ~InventoryHandler() {} virtual bool canSplit(const Item *item) = 0; - virtual void splitItem(const Item *item, int amount) = 0; - - virtual void moveItem(int oldIndex, int newIndex) = 0; - - virtual void openStorage(int type) = 0; - - virtual void closeStorage(int type) = 0; - - //void changeCart() = 0; - - virtual void moveItem(int source, int slot, int amount, - int destination) = 0; - // TODO: fix/remove me virtual size_t getSize(int type) const = 0; - - virtual ~InventoryHandler() {} }; } // namespace Net diff --git a/src/net/logindata.h b/src/net/logindata.h index 9bbeed4f..4a1c1a9f 100644 --- a/src/net/logindata.h +++ b/src/net/logindata.h @@ -22,15 +22,21 @@ #ifndef LOGINDATA_H #define LOGINDATA_H -#include "player.h" - -#include "net/serverinfo.h" +#include "being.h" #include <string> class LoginData { public: + /** + * Constructor + */ + LoginData() + { + resetCharacterSlots(); + } + std::string username; std::string password; std::string newPassword; @@ -44,6 +50,16 @@ public: bool remember; /**< Whether to store the username. */ bool registerLogin; /**< Whether an account is being registered. */ + unsigned short characterSlots; /**< The number of character slots */ + + /** + * Initialize character slots to 3 for TmwAthena compatibility + */ + void resetCharacterSlots() + { + characterSlots = 3; // Default value, used for TmwAthena. + } + void clear() { username.clear(); @@ -53,6 +69,7 @@ public: email.clear(); captchaResponse.clear(); gender = GENDER_UNSPECIFIED; + resetCharacterSlots(); } }; diff --git a/src/net/manaserv/adminhandler.cpp b/src/net/manaserv/adminhandler.cpp index 8a30e01b..db6c22ed 100644 --- a/src/net/manaserv/adminhandler.cpp +++ b/src/net/manaserv/adminhandler.cpp @@ -23,7 +23,7 @@ #include "net/manaserv/connection.h" #include "net/manaserv/messageout.h" -#include "net/manaserv/protocol.h" +#include "net/manaserv/manaserv_protocol.h" extern Net::AdminHandler *adminHandler; diff --git a/src/net/manaserv/attributes.cpp b/src/net/manaserv/attributes.cpp new file mode 100644 index 00000000..e57c6278 --- /dev/null +++ b/src/net/manaserv/attributes.cpp @@ -0,0 +1,408 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "net/manaserv/attributes.h" + +#include "log.h" +#include "playerinfo.h" + +#include "gui/statuswindow.h" + +#include "resources/itemdb.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" +#include "utils/xml.h" + +#include <list> +#include <map> + +#define DEFAULT_ATTRIBUTESDB_FILE "attributes.xml" +#define DEFAULT_POINTS 60 +#define DEFAULT_MIN_PTS 1 +#define DEFAULT_MAX_PTS 20 + +namespace ManaServ { +namespace Attributes { + + typedef struct + { + unsigned int id; + std::string name; + std::string description; + /** Whether the attribute value can be modified by the player */ + bool modifiable; + /**< Attribute scope. */ + std::string scope; + /** The playerInfo core Id the attribute is linked with or -1 if not */ + int playerInfoId; + } Attribute; + + /** Map for attributes. */ + typedef std::map<unsigned int, Attribute> AttributeMap; + static AttributeMap attributes; + + /** tags = effects on attributes. */ + typedef std::map< std::string, std::string > TagMap; + static TagMap tags; + + /** List of modifiable attribute names used at character's creation. */ + static std::vector<std::string> attributeLabels; + + /** Characters creation points. */ + static unsigned int creationPoints = 0; + static unsigned int attributeMinimum = 0; + static unsigned int attributeMaximum = 0; + + unsigned int getCreationPoints() + { + return creationPoints; + } + + unsigned int getAttributeMinimum() + { + return attributeMinimum; + } + + unsigned int getAttributeMaximum() + { + return attributeMaximum; + } + + std::vector<std::string>& getLabels() + { + return attributeLabels; + } + + /** + * Fills the list of base attribute labels. + */ + static void fillLabels() + { + // Fill up the modifiable attribute label list. + attributeLabels.clear(); + AttributeMap::const_iterator it, it_end; + for (it = attributes.begin(), it_end = attributes.end(); it != it_end; + it++) + { + if (it->second.modifiable && + (it->second.scope == "character" || it->second.scope == "being")) + attributeLabels.push_back(it->second.name + ":"); + } + } + + /** + * Fills the list of base attribute labels. + */ + static int getPlayerInfoIdFromAttrType(std::string attrType) + { + toLower(attrType); + if (attrType == "level") + return ::LEVEL; + else if (attrType == "hp") + return ::HP; + else if (attrType == "max-hp") + return ::MAX_HP; + else if (attrType == "mp") + return ::MP; + else if (attrType == "max-mp") + return ::MAX_MP; + else if (attrType == "exp") + return ::EXP; + else if (attrType == "exp-needed") + return ::EXP_NEEDED; + else if (attrType == "money") + return ::MONEY; + else if (attrType == "total-weight") + return ::TOTAL_WEIGHT; + else if (attrType == "max-weight") + return ::MAX_WEIGHT; + else if (attrType == "skill-points") + return ::SKILL_POINTS; + else if (attrType == "char-points") + return ::CHAR_POINTS; + else if (attrType == "corr-points") + return ::CORR_POINTS; + else if (attrType == "none") + return -2; // Used to hide the attribute display. + + return -1; // Not linked to a playerinfo stat. + } + + int getPlayerInfoIdFromAttrId(int attrId) + { + AttributeMap::const_iterator it = attributes.find(attrId); + + if (it != attributes.end()) + { + return it->second.playerInfoId; + } + + return -1; + } + + static void loadBuiltins() + { + { + Attribute a; + a.id = 16; + a.name = _("Strength"); + a.description = ""; + a.modifiable = true; + a.scope = "character"; + a.playerInfoId = -1; + + attributes[a.id] = a; + tags.insert(std::make_pair("str", _("Strength %+.1f"))); + } + + { + Attribute a; + a.id = 17; + a.name = _("Agility"); + a.description = ""; + a.modifiable = true; + a.scope = "character"; + a.playerInfoId = -1; + + attributes[a.id] = a; + tags.insert(std::make_pair("agi", _("Agility %+.1f"))); + } + + { + Attribute a; + a.id = 18; + a.name = _("Dexterity"); + a.description = ""; + a.modifiable = true; + a.scope = "character"; + a.playerInfoId = -1; + + attributes[a.id] = a; + tags.insert(std::make_pair("dex", _("Dexterity %+.1f"))); + } + + { + Attribute a; + a.id = 19; + a.name = _("Vitality"); + a.description = ""; + a.modifiable = true; + a.scope = "character"; + a.playerInfoId = -1; + + attributes[a.id] = a; + tags.insert(std::make_pair("vit", _("Vitality %+.1f"))); + } + + { + Attribute a; + a.id = 20; + a.name = _("Intelligence"); + a.description = ""; + a.modifiable = true; + a.scope = "character"; + a.playerInfoId = -1; + + attributes[a.id] = a; + tags.insert(std::make_pair("int", _("Intelligence %+.1f"))); + } + + { + Attribute a; + a.id = 21; + a.name = _("Willpower"); + a.description = ""; + a.modifiable = true; + a.scope = "character"; + a.playerInfoId = -1; + + attributes[a.id] = a; + tags.insert(std::make_pair("wil", _("Willpower %+.1f"))); + } + } + + void load() + { + logger->log("Initializing attributes database..."); + + XML::Document doc(DEFAULT_ATTRIBUTESDB_FILE); + xmlNodePtr rootNode = doc.rootNode(); + + if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "attributes")) + { + logger->log("Attributes: Error while loading " + DEFAULT_ATTRIBUTESDB_FILE ". Using Built-ins."); + loadBuiltins(); + fillLabels(); + return; + } + + for_each_xml_child_node(node, rootNode) + { + if (xmlStrEqual(node->name, BAD_CAST "attribute")) + { + int id = XML::getProperty(node, "id", 0); + + if (!id) + { + logger->log("Attributes: Invalid or missing stat ID in " + DEFAULT_ATTRIBUTESDB_FILE "!"); + continue; + } + else if (attributes.find(id) != attributes.end()) + { + logger->log("Attributes: Redefinition of stat ID %d", id); + } + + std::string name = XML::getProperty(node, "name", ""); + + if (name.empty()) + { + logger->log("Attributes: Invalid or missing stat name in " + DEFAULT_ATTRIBUTESDB_FILE "!"); + continue; + } + + // Create the attribute. + Attribute a; + a.id = id; + a.name = name; + a.description = XML::getProperty(node, "desc", ""); + a.modifiable = XML::getBoolProperty(node, "modifiable", false); + a.scope = XML::getProperty(node, "scope", "none"); + a.playerInfoId = getPlayerInfoIdFromAttrType( + XML::getProperty(node, "player-info", "")); + + attributes[id] = a; + + unsigned int count = 0; + for_each_xml_child_node(effectNode, node) + { + if (!xmlStrEqual(effectNode->name, BAD_CAST "modifier")) + continue; + ++count; + std::string tag = XML::getProperty(effectNode, "tag", ""); + if (tag.empty()) + { + if (name.empty()) + { + logger->log("Attribute modifier in attribute %u:%s: " + "Empty name definition " + "on empty tag definition, skipping.", + a.id, a.name.c_str()); + --count; + continue; + } + tag = name.substr(0, name.size() > 3 ? 3 : name.size()); + tag = toLower(tag) + toString(count); + } + + std::string effect = XML::getProperty(effectNode, "effect", ""); + if (effect.empty()) + { + if (name.empty()) + { + logger->log("Attribute modifier in attribute %u:%s: " + "Empty name definition " + "on empty effect definition, skipping.", + a.id, a.name.c_str()); + --count; + continue; + } + else + effect = name + " %+f"; + } + tags.insert(std::make_pair(tag, effect)); + } + logger->log("Found %d tags for attribute %d.", count, id); + + }// End attribute + else if (xmlStrEqual(node->name, BAD_CAST "points")) + { + creationPoints = XML::getProperty(node, "start",DEFAULT_POINTS); + attributeMinimum = XML::getProperty(node, "minimum", + DEFAULT_MIN_PTS); + attributeMaximum = XML::getProperty(node, "maximum", + DEFAULT_MAX_PTS); + logger->log("Loaded points: start: %i, min: %i, max: %i.", + creationPoints, attributeMinimum, attributeMaximum); + } + else + { + continue; + } + } + logger->log("Found %d tags for %d attributes.", int(tags.size()), + int(attributes.size())); + + fillLabels(); + + // Sanity checks on starting points + float modifiableAttributeCount = (float) attributeLabels.size(); + float averageValue = ((float) creationPoints) / modifiableAttributeCount; + if (averageValue > attributeMaximum || averageValue < attributeMinimum + || creationPoints < 1) + { + logger->log("Attributes: Character's point values make " + "the character's creation impossible. " + "Switch back to defaults."); + creationPoints = DEFAULT_POINTS; + attributeMinimum = DEFAULT_MIN_PTS; + attributeMaximum = DEFAULT_MAX_PTS; + } + } + + void unload() + { + attributes.clear(); + } + + void informItemDB() + { + std::list<ItemStat> dbStats; + + TagMap::const_iterator it, it_end; + for (it = tags.begin(), it_end = tags.end(); it != it_end; ++it) + dbStats.push_back(ItemStat(it->first, + it->second)); + + setStatsList(dbStats); + } + + void informStatusWindow() + { + AttributeMap::const_iterator it, it_end; + for (it = attributes.begin(), it_end = attributes.end(); it != it_end; + it++) + { + if (it->second.playerInfoId == -1 && + (it->second.scope == "character" || it->second.scope == "being")) + { + statusWindow->addAttribute(it->second.id, + it->second.name, + it->second.modifiable, + it->second.description); + } + } + } + +} // namespace Attributes +} // namespace ManaServ diff --git a/src/net/manaserv/attributes.h b/src/net/manaserv/attributes.h new file mode 100644 index 00000000..aced85ec --- /dev/null +++ b/src/net/manaserv/attributes.h @@ -0,0 +1,70 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef NET_MANASERV_ATTRIBUTES_H +#define NET_MANASERV_ATTRIBUTES_H + +#include <string> +#include <vector> + +namespace ManaServ { +namespace Attributes { + + void load(); + + void unload(); + + void informItemDB(); + + void informStatusWindow(); + + /** + * Returns the list of base attribute labels. + */ + std::vector<std::string>& getLabels(); + + /** + * Give back the corresponding playerinfo Id from the attribute id + * defined in the xml file. + */ + int getPlayerInfoIdFromAttrId(int attrId); + + /** + * Give the attribute points given to a character + * at its creation. + */ + unsigned int getCreationPoints(); + + /** + * Give the minimum attribute point possible + * at character's creation. + */ + unsigned int getAttributeMinimum(); + + /** + * Give the maximum attribute point possible + * at character's creation. + */ + unsigned int getAttributeMaximum(); + +} // namespace Attributes +} // namespace ManaServ + +#endif // NET_MANASERV_ATTRIBUTES_H diff --git a/src/net/manaserv/beinghandler.cpp b/src/net/manaserv/beinghandler.cpp index 8df9a8ab..6e9b3645 100644 --- a/src/net/manaserv/beinghandler.cpp +++ b/src/net/manaserv/beinghandler.cpp @@ -21,13 +21,12 @@ #include "net/manaserv/beinghandler.h" +#include "actorspritemanager.h" #include "being.h" -#include "beingmanager.h" #include "client.h" #include "game.h" #include "localplayer.h" #include "log.h" -#include "npc.h" #include "particle.h" #include "gui/okdialog.h" @@ -35,7 +34,7 @@ #include "net/messagein.h" #include "net/manaserv/playerhandler.h" -#include "net/manaserv/protocol.h" +#include "net/manaserv/manaserv_protocol.h" #include "resources/colordb.h" @@ -121,7 +120,7 @@ Vector BeingHandler::giveSpeedInPixelsPerTicks(float speedInTilesPerSeconds) return speedInTicks; } -static void handleLooks(Player *being, Net::MessageIn &msg) +static void handleLooks(Being *being, Net::MessageIn &msg) { // Order of sent slots. Has to be in sync with the server code. static int const nb_slots = 4; @@ -145,7 +144,7 @@ static void handleLooks(Player *being, Net::MessageIn &msg) { if (!(mask & (1 << i))) continue; int id = msg.readInt16(); - being->setSprite(slots[i], id); + being->setSprite(slots[i], id,"", (slots[i] == SPRITE_WEAPON)); } } @@ -156,6 +155,7 @@ void BeingHandler::handleBeingEnterMessage(Net::MessageIn &msg) Being::Action action = (Being::Action)msg.readInt8(); int px = msg.readInt16(); int py = msg.readInt16(); + BeingDirection direction = (BeingDirection)msg.readInt8(); Being *being; switch (type) @@ -170,23 +170,23 @@ void BeingHandler::handleBeingEnterMessage(Net::MessageIn &msg) } else { - being = beingManager->createBeing(id, Being::PLAYER, 0); + being = actorSpriteManager->createBeing(id, + ActorSprite::PLAYER, 0); being->setName(name); } - Player *p = static_cast< Player * >(being); int hs = msg.readInt8(), hc = msg.readInt8(); - p->setSprite(SPRITE_HAIR, hs * -1, ColorDB::get(hc)); - p->setGender(msg.readInt8() == GENDER_MALE ? - GENDER_MALE : GENDER_FEMALE); - handleLooks(p, msg); + being->setSprite(SPRITE_HAIR, hs * -1, ColorDB::get(hc)); + being->setGender(msg.readInt8() == GENDER_MALE ? + GENDER_MALE : GENDER_FEMALE); + handleLooks(being, msg); } break; case OBJECT_MONSTER: case OBJECT_NPC: { int subtype = msg.readInt16(); - being = beingManager->createBeing(id, type == OBJECT_MONSTER ? - Being::MONSTER : Being::NPC, subtype); + being = actorSpriteManager->createBeing(id, type == OBJECT_MONSTER + ? ActorSprite::MONSTER : ActorSprite::NPC, subtype); std::string name = msg.readString(); if (name.length() > 0) being->setName(name); } break; @@ -197,16 +197,17 @@ void BeingHandler::handleBeingEnterMessage(Net::MessageIn &msg) being->setPosition(px, py); being->setDestination(px, py); + being->setDirection(direction); being->setAction(action); } void BeingHandler::handleBeingLeaveMessage(Net::MessageIn &msg) { - Being *being = beingManager->findBeing(msg.readInt16()); + Being *being = actorSpriteManager->findBeing(msg.readInt16()); if (!being) return; - beingManager->destroyBeing(being); + actorSpriteManager->destroy(being); } void BeingHandler::handleBeingsMoveMessage(Net::MessageIn &msg) @@ -215,7 +216,7 @@ void BeingHandler::handleBeingsMoveMessage(Net::MessageIn &msg) { int id = msg.readInt16(); int flags = msg.readInt8(); - Being *being = beingManager->findBeing(id); + Being *being = actorSpriteManager->findBeing(id); int sx = 0; int sy = 0; int speed = 0; @@ -257,20 +258,14 @@ void BeingHandler::handleBeingsMoveMessage(Net::MessageIn &msg) void BeingHandler::handleBeingAttackMessage(Net::MessageIn &msg) { - Being *being = beingManager->findBeing(msg.readInt16()); - const int direction = msg.readInt8(); + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + const BeingDirection direction = (BeingDirection) msg.readInt8(); const int attackType = msg.readInt8(); if (!being) return; - switch (direction) - { - case DIRECTION_UP: being->setDirection(Being::UP); break; - case DIRECTION_DOWN: being->setDirection(Being::DOWN); break; - case DIRECTION_LEFT: being->setDirection(Being::LEFT); break; - case DIRECTION_RIGHT: being->setDirection(Being::RIGHT); break; - } + being->setDirection(direction); being->setAction(Being::ATTACK, attackType); } @@ -279,7 +274,7 @@ void BeingHandler::handleBeingsDamageMessage(Net::MessageIn &msg) { while (msg.getUnreadLength()) { - Being *being = beingManager->findBeing(msg.readInt16()); + Being *being = actorSpriteManager->findBeing(msg.readInt16()); int damage = msg.readInt16(); if (being) { @@ -290,7 +285,7 @@ void BeingHandler::handleBeingsDamageMessage(Net::MessageIn &msg) void BeingHandler::handleBeingActionChangeMessage(Net::MessageIn &msg) { - Being *being = beingManager->findBeing(msg.readInt16()); + Being *being = actorSpriteManager->findBeing(msg.readInt16()); Being::Action action = (Being::Action) msg.readInt8(); if (!being) return; @@ -329,38 +324,28 @@ void BeingHandler::handleBeingActionChangeMessage(Net::MessageIn &msg) void BeingHandler::handleBeingLooksChangeMessage(Net::MessageIn &msg) { - Being *being = beingManager->findBeing(msg.readInt16()); - if (!being || being->getType() != Being::PLAYER) + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + if (!being || being->getType() != ActorSprite::PLAYER) return; - Player *player = static_cast<Player *>(being); - handleLooks(player, msg); + handleLooks(being, msg); if (msg.getUnreadLength()) { int style = msg.readInt16(); int color = msg.readInt16(); - player->setSprite(SPRITE_HAIR, style * -1, ColorDB::get(color)); + being->setSprite(SPRITE_HAIR, style * -1, ColorDB::get(color)); } } void BeingHandler::handleBeingDirChangeMessage(Net::MessageIn &msg) { - Being *being = beingManager->findBeing(msg.readInt16()); + Being *being = actorSpriteManager->findBeing(msg.readInt16()); if (!being) return; int data = msg.readInt8(); // The direction for the player's character is handled on client side. if (being != player_node) - { - switch (data) - { - case DIRECTION_UP: being->setDirection(Being::UP); break; - case DIRECTION_DOWN: being->setDirection(Being::DOWN); break; - case DIRECTION_LEFT: being->setDirection(Being::LEFT); break; - case DIRECTION_RIGHT: being->setDirection(Being::RIGHT); break; - default: break; - } - } + being->setDirection((BeingDirection) data); } } // namespace ManaServ diff --git a/src/net/manaserv/buysellhandler.cpp b/src/net/manaserv/buysellhandler.cpp index a4ce6aa0..c375ed75 100644 --- a/src/net/manaserv/buysellhandler.cpp +++ b/src/net/manaserv/buysellhandler.cpp @@ -21,18 +21,16 @@ #include "net/manaserv/buysellhandler.h" -#include "beingmanager.h" +#include "actorspritemanager.h" #include "item.h" -#include "localplayer.h" -#include "npc.h" +#include "playerinfo.h" #include "gui/buy.h" -#include "gui/chat.h" #include "gui/sell.h" #include "net/messagein.h" -#include "net/manaserv/protocol.h" +#include "net/manaserv/manaserv_protocol.h" namespace ManaServ { @@ -49,8 +47,8 @@ BuySellHandler::BuySellHandler() void BuySellHandler::handleMessage(Net::MessageIn &msg) { - Being *being = beingManager->findBeing(msg.readInt16()); - if (!being || being->getType() != Being::NPC) + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + if (!being || being->getType() != ActorSprite::NPC) { return; } @@ -64,7 +62,7 @@ void BuySellHandler::handleMessage(Net::MessageIn &msg) BuyDialog* dialog = new BuyDialog(npcId); dialog->reset(); - dialog->setMoney(player_node->getMoney()); + dialog->setMoney(PlayerInfo::getAttribute(MONEY)); while (msg.getUnreadLength()) { @@ -81,7 +79,7 @@ void BuySellHandler::handleMessage(Net::MessageIn &msg) SellDialog* dialog = new SellDialog(npcId); dialog->reset(); - dialog->setMoney(player_node->getMoney()); + dialog->setMoney(PlayerInfo::getAttribute(MONEY)); while (msg.getUnreadLength()) { diff --git a/src/net/manaserv/charhandler.cpp b/src/net/manaserv/charhandler.cpp index e6723226..961b364a 100644 --- a/src/net/manaserv/charhandler.cpp +++ b/src/net/manaserv/charhandler.cpp @@ -36,8 +36,8 @@ #include "net/manaserv/gamehandler.h" #include "net/manaserv/messagein.h" #include "net/manaserv/messageout.h" -#include "net/manaserv/protocol.h" -#include "net/manaserv/stats.h" +#include "net/manaserv/manaserv_protocol.h" +#include "net/manaserv/attributes.h" #include "resources/colordb.h" @@ -108,11 +108,15 @@ void CharHandler::handleCharacterInfo(Net::MessageIn &msg) info.level = msg.readInt16(); info.characterPoints = msg.readInt16(); info.correctionPoints = msg.readInt16(); - info.money = msg.readInt32(); - for (int i = 0; i < 7; i++) + while (msg.getUnreadLength() > 0) { - info.attribute[i] = msg.readInt8(); + int id = msg.readInt32(); + CachedAttrbiute attr; + attr.base = msg.readInt32() / 256.0; + attr.mod = msg.readInt32() / 256.0; + + info.attribute[id] = attr; } mCachedCharacterInfos.push_back(info); @@ -157,8 +161,14 @@ void CharHandler::handleCharacterCreateResponse(Net::MessageIn &msg) case CREATE_ATTRIBUTES_TOO_LOW: errorMessage = _("Character's stats are too low."); break; - case CREATE_ATTRIBUTES_EQUAL_TO_ZERO: - errorMessage = _("One stat is zero."); + case CREATE_ATTRIBUTES_OUT_OF_RANGE: + errorMessage = strprintf( _("At least one stat" + "is out of the permitted range: (%u - %u)."), + Attributes::getAttributeMinimum(), + Attributes::getAttributeMaximum()); + break; + case CREATE_INVALID_SLOT: + errorMessage = _("Invalid slot number."); break; default: errorMessage = _("Unknown error."); @@ -189,7 +199,6 @@ void CharHandler::handleCharacterDeleteResponse(Net::MessageIn &msg) delete mSelectedCharacter; mCharacters.remove(mSelectedCharacter); updateCharSelectDialog(); - unlockCharSelectDialog(); new OkDialog(_("Info"), _("Player deleted.")); } else @@ -210,6 +219,7 @@ void CharHandler::handleCharacterDeleteResponse(Net::MessageIn &msg) new OkDialog(_("Error"), errorMessage); } mSelectedCharacter = 0; + unlockCharSelectDialog(); } void CharHandler::handleCharacterSelectResponse(Net::MessageIn &msg) @@ -233,6 +243,7 @@ void CharHandler::handleCharacterSelectResponse(Net::MessageIn &msg) // Prevent the selected local player from being deleted player_node = mSelectedCharacter->dummy; + PlayerInfo::setBackend(mSelectedCharacter->data); mSelectedCharacter->dummy = 0; Client::setState(STATE_CONNECT_GAME); @@ -259,7 +270,10 @@ void CharHandler::setCharCreateDialog(CharCreateDialog *window) if (!mCharCreateDialog) return; - mCharCreateDialog->setAttributes(Stats::getLabelVector(), 60, 1, 20); + mCharCreateDialog->setAttributes(Attributes::getLabels(), + Attributes::getCreationPoints(), + Attributes::getAttributeMinimum(), + Attributes::getAttributeMaximum()); } void CharHandler::requestCharacters() @@ -285,7 +299,7 @@ void CharHandler::chooseCharacter(Net::Character *character) } void CharHandler::newCharacter(const std::string &name, - int /* slot */, + int slot, bool gender, int hairstyle, int hairColor, @@ -297,6 +311,7 @@ void CharHandler::newCharacter(const std::string &name, msg.writeInt8(hairstyle); msg.writeInt8(hairColor); msg.writeInt8(gender); + msg.writeInt8(slot); std::vector<int>::const_iterator it, it_end; for (it = stats.begin(), it_end = stats.end(); it != it_end; it++) @@ -319,17 +334,17 @@ void CharHandler::switchCharacter() gameHandler->quit(true); } -int CharHandler::baseSprite() const +unsigned int CharHandler::baseSprite() const { return SPRITE_BASE; } -int CharHandler::hairSprite() const +unsigned int CharHandler::hairSprite() const { return SPRITE_HAIR; } -int CharHandler::maxSprite() const +unsigned int CharHandler::maxSprite() const { return SPRITE_VECTOREND; } @@ -350,19 +365,20 @@ void CharHandler::updateCharacters() Net::Character *character = new Net::Character; character->slot = info.slot; - LocalPlayer *player = character->dummy; + LocalPlayer *player = character->dummy = new LocalPlayer; player->setName(info.name); player->setGender(info.gender); player->setSprite(SPRITE_HAIR, info.hairStyle * -1, ColorDB::get(info.hairColor)); - player->setLevel(info.level); - player->setCharacterPoints(info.characterPoints); - player->setCorrectionPoints(info.correctionPoints); - player->setMoney(info.money); + character->data.mAttributes[LEVEL] = info.level; + character->data.mAttributes[CHAR_POINTS] = info.characterPoints; + character->data.mAttributes[CORR_POINTS] = info.correctionPoints; - for (int i = 0; i < 7; i++) + for (CachedAttributes::const_iterator it = info.attribute.begin(), + it_end = info.attribute.end(); it != it_end; it++) { - player->setAttributeBase(i, info.attribute[i], false); + character->data.mStats[i].base = it->second.base; + character->data.mStats[i].mod = it->second.mod; } mCharacters.push_back(character); diff --git a/src/net/manaserv/charhandler.h b/src/net/manaserv/charhandler.h index 26a7bf4e..2f335688 100644 --- a/src/net/manaserv/charhandler.h +++ b/src/net/manaserv/charhandler.h @@ -28,6 +28,8 @@ #include "net/manaserv/messagehandler.h" +#include <map.h> + class LoginData; namespace ManaServ { @@ -65,11 +67,11 @@ class CharHandler : public MessageHandler, public Net::CharHandler void switchCharacter(); - int baseSprite() const; + unsigned int baseSprite() const; - int hairSprite() const; + unsigned int hairSprite() const; - int maxSprite() const; + unsigned int maxSprite() const; void clear(); @@ -79,6 +81,13 @@ class CharHandler : public MessageHandler, public Net::CharHandler * we have loaded the dynamic data, so we can't resolve load any * sprites yet. */ + struct CachedAttrbiute { + double base; + double mod; + }; + + typedef std::map<int, CachedAttrbiute> CachedAttributes; + struct CachedCharacterInfo { int slot; std::string name; @@ -88,8 +97,7 @@ class CharHandler : public MessageHandler, public Net::CharHandler int level; int characterPoints; int correctionPoints; - int money; - int attribute[7]; + CachedAttributes attribute; }; void handleCharacterInfo(Net::MessageIn &msg); diff --git a/src/net/manaserv/chathandler.cpp b/src/net/manaserv/chathandler.cpp index a452281f..da5dc79b 100644 --- a/src/net/manaserv/chathandler.cpp +++ b/src/net/manaserv/chathandler.cpp @@ -21,20 +21,20 @@ #include "net/manaserv/chathandler.h" +#include "actorspritemanager.h" #include "being.h" -#include "beingmanager.h" #include "client.h" #include "channel.h" #include "channelmanager.h" - -#include "gui/chat.h" +#include "event.h" +#include "playerrelations.h" #include "gui/widgets/channeltab.h" #include "net/manaserv/connection.h" #include "net/manaserv/messagein.h" #include "net/manaserv/messageout.h" -#include "net/manaserv/protocol.h" +#include "net/manaserv/manaserv_protocol.h" #include "utils/gettext.h" #include "utils/stringutils.h" @@ -149,22 +149,29 @@ void ChatHandler::handleGameChatMessage(Net::MessageIn &msg) if (id == 0) { - localChatTab->chatLog(chatMsg, BY_SERVER); + SERVER_NOTICE(chatMsg) return; } - Being *being = beingManager->findBeing(id); + Being *being = actorSpriteManager->findBeing(id); std::string mes; if (being) { mes = being->getName() + " : " + chatMsg; - being->setSpeech(chatMsg, SPEECH_TIME); } else mes = "Unknown : " + chatMsg; - localChatTab->chatLog(mes, being == player_node ? BY_PLAYER : BY_OTHER); + Mana::Event event(being == player_node ? EVENT_PLAYER : EVENT_BEING); + event.setString("message", mes); + event.setString("text", chatMsg); + event.setString("nick", being->getName()); + event.setInt("beingId", id); + event.setInt("permissions", player_relations + .checkPermissionSilently(being->getName(), + PlayerRelation::SPEECH_LOG | PlayerRelation::SPEECH_FLOAT)); + event.trigger(CHANNEL_CHAT); } void ChatHandler::handleEnterChannelResponse(Net::MessageIn &msg) @@ -198,13 +205,13 @@ void ChatHandler::handleEnterChannelResponse(Net::MessageIn &msg) } else { - localChatTab->chatLog(_("Error joining channel."), BY_SERVER); + SERVER_NOTICE(_("Error joining channel.")) } } void ChatHandler::handleListChannelsResponse(Net::MessageIn &msg) { - localChatTab->chatLog(_("Listing channels."), BY_SERVER); + SERVER_NOTICE(_("Listing channels.")) while (msg.getUnreadLength()) { std::string channelName = msg.readString(); @@ -214,9 +221,9 @@ void ChatHandler::handleListChannelsResponse(Net::MessageIn &msg) numUsers << msg.readInt16(); channelName += " - "; channelName += numUsers.str(); - localChatTab->chatLog(channelName, BY_SERVER); + SERVER_NOTICE(channelName) } - localChatTab->chatLog(_("End of channel list."), BY_SERVER); + SERVER_NOTICE(_("End of channel list.")) } void ChatHandler::handlePrivateMessage(Net::MessageIn &msg) @@ -224,13 +231,18 @@ void ChatHandler::handlePrivateMessage(Net::MessageIn &msg) std::string userNick = msg.readString(); std::string chatMsg = msg.readString(); - chatWindow->whisper(userNick, chatMsg); + Mana::Event event(EVENT_WHISPER); + event.setString("nick", userNick); + event.setString("message", chatMsg); + event.trigger(CHANNEL_CHAT); } void ChatHandler::handleAnnouncement(Net::MessageIn &msg) { std::string chatMsg = msg.readString(); - localChatTab->chatLog(chatMsg, BY_GM); + Mana::Event event(EVENT_ANNOUNCEMENT); + event.setString("message", chatMsg); + event.trigger(CHANNEL_CHAT); } void ChatHandler::handleChatMessage(Net::MessageIn &msg) @@ -341,7 +353,7 @@ void ChatHandler::handleWhoResponse(Net::MessageIn &msg) { break; } - localChatTab->chatLog(userNick, BY_SERVER); + SERVER_NOTICE(userNick) } } diff --git a/src/net/manaserv/connection.cpp b/src/net/manaserv/connection.cpp index fbd2ed22..b404191f 100644 --- a/src/net/manaserv/connection.cpp +++ b/src/net/manaserv/connection.cpp @@ -60,7 +60,7 @@ bool Connection::connect(const std::string &address, short port) enetAddress.port = port; // Initiate the connection, allocating channel 0. -#ifdef ENET_VERSION_MAJOR +#if defined(ENET_VERSION) && ENET_VERSION >= ENET_CUTOFF mConnection = enet_host_connect(mClient, &enetAddress, 1, 0); #else mConnection = enet_host_connect(mClient, &enetAddress, 1); diff --git a/src/net/manaserv/connection.h b/src/net/manaserv/connection.h index b39f8957..808a6d40 100644 --- a/src/net/manaserv/connection.h +++ b/src/net/manaserv/connection.h @@ -26,6 +26,12 @@ #include <iosfwd> +#ifdef ENET_VERSION_CREATE +#define ENET_CUTOFF ENET_VERSION_CREATE(1,3,0) +#else +#define ENET_CUTOFF 0xFFFFFFFF +#endif + namespace ManaServ { class MessageOut; diff --git a/src/net/manaserv/defines.h b/src/net/manaserv/defines.h new file mode 100644 index 00000000..e97866df --- /dev/null +++ b/src/net/manaserv/defines.h @@ -0,0 +1,76 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef MANASERV_DEFINES_H +#define MANASERV_DEFINES_H + +/** + * Attributes used during combat. Available to all the beings. + */ +enum +{ +BASE_ATTR_BEGIN = 0, + BASE_ATTR_PHY_ATK_MIN = BASE_ATTR_BEGIN, + BASE_ATTR_PHY_ATK_DELTA, + /**< Physical attack power. */ + BASE_ATTR_MAG_ATK, /**< Magical attack power. */ + BASE_ATTR_PHY_RES, /**< Resistance to physical damage. */ + BASE_ATTR_MAG_RES, /**< Resistance to magical damage. */ + BASE_ATTR_EVADE, /**< Ability to avoid hits. */ + BASE_ATTR_HIT, /**< Ability to hit stuff. */ + BASE_ATTR_HP, /**< Hit Points (Base value: maximum, Modded value: current) */ + BASE_ATTR_HP_REGEN,/**< number of HP regenerated every 10 game ticks */ + BASE_ATTR_END, + BASE_ATTR_NB = BASE_ATTR_END - BASE_ATTR_BEGIN, + + BASE_ELEM_BEGIN = BASE_ATTR_END, + BASE_ELEM_NEUTRAL = BASE_ELEM_BEGIN, + BASE_ELEM_FIRE, + BASE_ELEM_WATER, + BASE_ELEM_EARTH, + BASE_ELEM_AIR, + BASE_ELEM_SACRED, + BASE_ELEM_DEATH, + BASE_ELEM_END, + BASE_ELEM_NB = BASE_ELEM_END - BASE_ELEM_BEGIN, + + NB_BEING_ATTRIBUTES = BASE_ELEM_END +}; + +/** + * Attributes of characters. Used to derive being attributes. + */ +enum +{ + CHAR_ATTR_BEGIN = NB_BEING_ATTRIBUTES, + CHAR_ATTR_STRENGTH = CHAR_ATTR_BEGIN, + CHAR_ATTR_AGILITY, + CHAR_ATTR_DEXTERITY, + CHAR_ATTR_VITALITY, + CHAR_ATTR_INTELLIGENCE, + CHAR_ATTR_WILLPOWER, + CHAR_ATTR_END, + CHAR_ATTR_NB = CHAR_ATTR_END - CHAR_ATTR_BEGIN, + + NB_CHARACTER_ATTRIBUTES = CHAR_ATTR_END +}; + +#endif // MANASERV_DEFINES_H diff --git a/src/net/manaserv/effecthandler.cpp b/src/net/manaserv/effecthandler.cpp index 27db9b59..2df3fe0b 100644 --- a/src/net/manaserv/effecthandler.cpp +++ b/src/net/manaserv/effecthandler.cpp @@ -21,13 +21,13 @@ #include "net/manaserv/effecthandler.h" -#include "beingmanager.h" +#include "actorspritemanager.h" #include "effectmanager.h" #include "log.h" #include "net/messagein.h" -#include "net/manaserv/protocol.h" +#include "net/manaserv/manaserv_protocol.h" namespace ManaServ { @@ -68,7 +68,7 @@ void EffectHandler::handleCreateEffectBeing(Net::MessageIn &msg) { int eid = msg.readInt16(); int bid = msg.readInt16(); - Being* b = beingManager->findBeing(bid); + Being* b = actorSpriteManager->findBeing(bid); if (b) effectManager->trigger(eid, b); else diff --git a/src/net/manaserv/gamehandler.cpp b/src/net/manaserv/gamehandler.cpp index 5e29a896..040a5e6c 100644 --- a/src/net/manaserv/gamehandler.cpp +++ b/src/net/manaserv/gamehandler.cpp @@ -27,7 +27,7 @@ #include "net/manaserv/chathandler.h" #include "net/manaserv/connection.h" #include "net/manaserv/messageout.h" -#include "net/manaserv/protocol.h" +#include "net/manaserv/manaserv_protocol.h" extern Net::GameHandler *gameHandler; @@ -115,16 +115,6 @@ void GameHandler::disconnect() chatHandler->disconnect(); } -void GameHandler::inGame() -{ - // TODO -} - -void GameHandler::mapLoaded(const std::string &mapName) -{ - // TODO -} - void GameHandler::who() { // TODO diff --git a/src/net/manaserv/gamehandler.h b/src/net/manaserv/gamehandler.h index dde1748f..2e9f37fe 100644 --- a/src/net/manaserv/gamehandler.h +++ b/src/net/manaserv/gamehandler.h @@ -42,10 +42,6 @@ class GameHandler : public MessageHandler, public Net::GameHandler void disconnect(); - void inGame(); - - void mapLoaded(const std::string &mapName); - void who(); void quit(bool reconnectAccount); @@ -53,12 +49,15 @@ class GameHandler : public MessageHandler, public Net::GameHandler void quit() { quit(false); } void ping(int tick); - + bool removeDeadBeings() const { return false; } void clear(); void gameLoading(); + + /** The ManaServ protocol doesn't use the MP status bar. */ + bool canUseMagicBar() const { return false; } }; } // namespace ManaServ diff --git a/src/net/manaserv/generalhandler.cpp b/src/net/manaserv/generalhandler.cpp index 0d3073f1..d2151307 100644 --- a/src/net/manaserv/generalhandler.cpp +++ b/src/net/manaserv/generalhandler.cpp @@ -46,7 +46,7 @@ #include "net/manaserv/partyhandler.h" #include "net/manaserv/playerhandler.h" #include "net/manaserv/specialhandler.h" -#include "net/manaserv/stats.h" +#include "net/manaserv/attributes.h" #include "net/manaserv/tradehandler.h" #include "utils/gettext.h" @@ -90,6 +90,9 @@ GeneralHandler::GeneralHandler(): chatServerConnection = getConnection(); generalHandler = this; + + listen(CHANNEL_CLIENT); + listen(CHANNEL_GAME); } void GeneralHandler::load() @@ -127,9 +130,9 @@ void GeneralHandler::reload() gameServer.clear(); chatServer.clear(); - Stats::unload(); - Stats::load(); - Stats::informItemDB(); + Attributes::unload(); + Attributes::load(); + Attributes::informItemDB(); } void GeneralHandler::unload() @@ -147,7 +150,7 @@ void GeneralHandler::unload() delete gameServerConnection; delete chatServerConnection; - Stats::unload(); + Attributes::unload(); finalize(); } @@ -163,38 +166,43 @@ void GeneralHandler::flushNetwork() } } -void GeneralHandler::guiWindowsLoaded() -{ - inventoryWindow->setSplitAllowed(true); - skillDialog->loadSkills("mana-skills.xml"); - specialsWindow->loadSpecials("specials.xml"); - - player_node->setExpNeeded(100); - - Stats::informStatusWindow(); -} - -void GeneralHandler::guiWindowsUnloaded() -{ - // TODO -} - void GeneralHandler::clearHandlers() { clearNetworkHandlers(); } -void GeneralHandler::stateChanged(State oldState, State newState) +void GeneralHandler::event(Channels channel, + const Mana::Event &event) { - if (newState == STATE_GAME) + if (channel == CHANNEL_CLIENT) { - GameHandler *game = static_cast<GameHandler*>(Net::getGameHandler()); - game->gameLoading(); + if (event.getName() == EVENT_STATECHANGE) + { + int newState = event.getInt("newState"); + + if (newState == STATE_GAME) + { + GameHandler *game = static_cast<GameHandler*>(Net::getGameHandler()); + game->gameLoading(); + } + } + else if (event.getName() == EVENT_DBSLOADING) + { + Attributes::load(); + Attributes::informItemDB(); + } } - else if (newState == STATE_LOAD_DATA) + else if (channel == CHANNEL_GAME) { - Stats::load(); - Stats::informItemDB(); + if (event.getName() == EVENT_GUIWINDOWSLOADED) + { + inventoryWindow->setSplitAllowed(true); + skillDialog->loadSkills("mana-skills.xml"); + + PlayerInfo::setAttribute(EXP_NEEDED, 100); + + Attributes::informStatusWindow(); + } } } diff --git a/src/net/manaserv/generalhandler.h b/src/net/manaserv/generalhandler.h index 58b95529..c8671ec1 100644 --- a/src/net/manaserv/generalhandler.h +++ b/src/net/manaserv/generalhandler.h @@ -22,6 +22,8 @@ #ifndef NET_MANASERV_GENERALHANDLER_H #define NET_MANASERV_GENERALHANDLER_H +#include "listener.h" + #include "net/generalhandler.h" #include "net/net.h" @@ -29,7 +31,7 @@ namespace ManaServ { -class GeneralHandler : public Net::GeneralHandler +class GeneralHandler : public Net::GeneralHandler, public Mana::Listener { public: GeneralHandler(); @@ -42,13 +44,9 @@ class GeneralHandler : public Net::GeneralHandler void flushNetwork(); - void guiWindowsLoaded(); - - void guiWindowsUnloaded(); - void clearHandlers(); - void stateChanged(State oldState, State newState); + void event(Channels channel, const Mana::Event &event); protected: MessageHandlerPtr mBeingHandler; diff --git a/src/net/manaserv/guildhandler.cpp b/src/net/manaserv/guildhandler.cpp index 253efb01..a2c571bc 100644 --- a/src/net/manaserv/guildhandler.cpp +++ b/src/net/manaserv/guildhandler.cpp @@ -21,23 +21,24 @@ #include "net/manaserv/guildhandler.h" +#include "event.h" #include "guild.h" #include "log.h" #include "localplayer.h" #include "channel.h" #include "channelmanager.h" -#include "gui/widgets/channeltab.h" -#include "gui/chat.h" #include "gui/socialwindow.h" +#include "gui/widgets/channeltab.h" + #include "net/messagein.h" #include "net/net.h" #include "net/manaserv/connection.h" #include "net/manaserv/messagein.h" #include "net/manaserv/messageout.h" -#include "net/manaserv/protocol.h" +#include "net/manaserv/manaserv_protocol.h" #include "utils/gettext.h" #include "utils/stringutils.h" @@ -78,12 +79,12 @@ void GuildHandler::handleMessage(Net::MessageIn &msg) if (msg.readInt8() == ERRMSG_OK) { // TODO - Acknowledge guild was created - localChatTab->chatLog(_("Guild created.")); + SERVER_NOTICE(_("Guild created.")) joinedGuild(msg); } else { - localChatTab->chatLog(_("Error creating guild.")); + SERVER_NOTICE(_("Error creating guild.")) } } break; @@ -93,7 +94,7 @@ void GuildHandler::handleMessage(Net::MessageIn &msg) if (msg.readInt8() == ERRMSG_OK) { // TODO - Acknowledge invite was sent - localChatTab->chatLog(_("Invite sent.")); + SERVER_NOTICE(_("Invite sent.")) } } break; @@ -200,12 +201,12 @@ void GuildHandler::handleMessage(Net::MessageIn &msg) if (msg.readInt8() == ERRMSG_OK) { // promotion succeeded - localChatTab->chatLog(_("Member was promoted successfully.")); + SERVER_NOTICE(_("Member was promoted successfully.")) } else { // promotion failed - localChatTab->chatLog(_("Failed to promote member.")); + SERVER_NOTICE(_("Failed to promote member.")) } } @@ -275,9 +276,9 @@ void GuildHandler::invite(int guildId, const std::string &name) chatServerConnection->send(msg); } -void GuildHandler::invite(int guildId, Player *player) +void GuildHandler::invite(int guildId, Being *being) { - invite(guildId, player->getName()); + invite(guildId, being->getName()); } void GuildHandler::inviteResponse(int guildId, bool response) diff --git a/src/net/manaserv/guildhandler.h b/src/net/manaserv/guildhandler.h index 9929d135..bde677fb 100644 --- a/src/net/manaserv/guildhandler.h +++ b/src/net/manaserv/guildhandler.h @@ -41,7 +41,7 @@ public: void invite(int guildId, const std::string &name); - void invite(int guidId, Player *player); + void invite(int guidId, Being *being); void inviteResponse(int guidId, bool response); diff --git a/src/net/manaserv/inventoryhandler.cpp b/src/net/manaserv/inventoryhandler.cpp index 76fca7ae..5edf3597 100644 --- a/src/net/manaserv/inventoryhandler.cpp +++ b/src/net/manaserv/inventoryhandler.cpp @@ -26,18 +26,15 @@ #include "item.h" #include "itemshortcut.h" #include "localplayer.h" - -#include "gui/chat.h" +#include "playerinfo.h" #include "net/manaserv/connection.h" #include "net/manaserv/messagein.h" #include "net/manaserv/messageout.h" -#include "net/manaserv/protocol.h" +#include "net/manaserv/manaserv_protocol.h" #include "resources/iteminfo.h" -#include "log.h" // <<< REMOVE ME! - extern Net::InventoryHandler *inventoryHandler; namespace ManaServ { @@ -49,10 +46,13 @@ InventoryHandler::InventoryHandler() static const Uint16 _messages[] = { GPMSG_INVENTORY_FULL, GPMSG_INVENTORY, + GPMSG_EQUIP, 0 }; handledMessages = _messages; inventoryHandler = this; + + listen(CHANNEL_ITEM); } void InventoryHandler::handleMessage(Net::MessageIn &msg) @@ -60,114 +60,142 @@ void InventoryHandler::handleMessage(Net::MessageIn &msg) switch (msg.getId()) { case GPMSG_INVENTORY_FULL: - player_node->clearInventory(); - player_node->mEquipment->setBackend(&mEquips); - // no break! - - case GPMSG_INVENTORY: - while (msg.getUnreadLength()) { - unsigned int slot = msg.readInt8(); - if (slot == 255) + PlayerInfo::clearInventory(); + PlayerInfo::getEquipment()->setBackend(&mEquips); + int count = msg.readInt16(); + while (count--) { - player_node->setMoney(msg.readInt32()); - continue; + unsigned int slot = msg.readInt16(); + int id = msg.readInt16(); + unsigned int amount = msg.readInt16(); + PlayerInfo::setInventoryItem(slot, id, amount); } - - int id = msg.readInt16(); - if (slot < EQUIPMENT_SIZE) - { - mEquips.setEquipment(slot, id); - } - else if (slot >= 32 && slot < 32 + getSize(Inventory::INVENTORY)) + while (msg.getUnreadLength()) { - int amount = id ? msg.readInt8() : 0; - player_node->setInvItem(slot - 32, id, amount); + unsigned int slot = msg.readInt8(); + unsigned int ref = msg.readInt16(); + + mEquips.addEquipment(slot, ref); } - }; + } break; - } -} -void InventoryHandler::equipItem(const Item *item) -{ - MessageOut msg(PGMSG_EQUIP); - msg.writeInt8(item->getInvIndex()); - gameServerConnection->send(msg); -} - -void InventoryHandler::unequipItem(const Item *item) -{ - MessageOut msg(PGMSG_UNEQUIP); - msg.writeInt8(item->getInvIndex()); - gameServerConnection->send(msg); - - // Tidy equipment directly to avoid weapon still shown bug, for instance - int equipSlot = item->getInvIndex(); - logger->log("Unequipping %d", equipSlot); - mEquips.setEquipment(equipSlot, 0); -} - -void InventoryHandler::useItem(const Item *item) -{ - MessageOut msg(PGMSG_USE_ITEM); - msg.writeInt8(item->getInvIndex()); - gameServerConnection->send(msg); -} - -void InventoryHandler::dropItem(const Item *item, int amount) -{ - MessageOut msg(PGMSG_DROP); - msg.writeInt8(item->getInvIndex()); - msg.writeInt8(amount); - gameServerConnection->send(msg); -} + case GPMSG_INVENTORY: + while (msg.getUnreadLength()) + { + unsigned int slot = msg.readInt16(); + int id = msg.readInt16(); + unsigned int amount = id ? msg.readInt16() : 0; + PlayerInfo::setInventoryItem(slot, id, amount); + } + break; -bool InventoryHandler::canSplit(const Item *item) -{ - return item && !item->isEquipment() && item->getQuantity() > 1; -} + case GPMSG_EQUIP: + while (msg.getUnreadLength()) + { + unsigned int ref = msg.readInt16(); + int count = msg.readInt8(); + while (count--) + { + unsigned int slot = msg.readInt8(); + unsigned int used = msg.readInt8(); -void InventoryHandler::splitItem(const Item *item, int amount) -{ - int newIndex = player_node->getInventory()->getFreeSlot(); - if (newIndex > Inventory::NO_SLOT_INDEX) - { - MessageOut msg(PGMSG_MOVE_ITEM); - msg.writeInt8(item->getInvIndex()); - msg.writeInt8(newIndex); - msg.writeInt8(amount); - gameServerConnection->send(msg); + mEquips.setEquipment(slot, used, ref); + } + } + break; } } -void InventoryHandler::moveItem(int oldIndex, int newIndex) -{ - if (oldIndex == newIndex) - return; - - MessageOut msg(PGMSG_MOVE_ITEM); - msg.writeInt8(oldIndex); - msg.writeInt8(newIndex); - msg.writeInt8(player_node->getInventory()->getItem(oldIndex) - ->getQuantity()); - gameServerConnection->send(msg); -} - -void InventoryHandler::openStorage(int type) +void InventoryHandler::event(Channels channel, + const Mana::Event &event) { - // TODO -} + if (channel == CHANNEL_ITEM) + { + Item *item = event.getItem("item"); + + if (!item) + return; + + int index = item->getInvIndex(); + + if (event.getName() == EVENT_DOEQUIP) + { + MessageOut msg(PGMSG_EQUIP); + msg.writeInt8(index); + gameServerConnection->send(msg); + } + else if (event.getName() == EVENT_DOUNEQUIP) + { + MessageOut msg(PGMSG_UNEQUIP); + msg.writeInt8(index); + gameServerConnection->send(msg); + + // Tidy equipment directly to avoid weapon still shown bug, + // for instance. + mEquips.setEquipment(index, 0, 0); + } + else if (event.getName() == EVENT_DOUSE) + { + MessageOut msg(PGMSG_USE_ITEM); + msg.writeInt8(index); + gameServerConnection->send(msg); + } + else if (event.getName() == EVENT_DODROP) + { + int amount = event.getInt("amount", 1); + + MessageOut msg(PGMSG_DROP); + msg.writeInt8(index); + msg.writeInt8(amount); + gameServerConnection->send(msg); + } + else if (event.getName() == EVENT_DOSPLIT) + { + int amount = event.getInt("amount", 1); + + int newIndex = PlayerInfo::getInventory()->getFreeSlot(); + if (newIndex > Inventory::NO_SLOT_INDEX) + { + MessageOut msg(PGMSG_MOVE_ITEM); + msg.writeInt8(index); + msg.writeInt8(newIndex); + msg.writeInt8(amount); + gameServerConnection->send(msg); + } + } + else if (event.getName() == EVENT_DOMOVE) + { + int newIndex = event.getInt("newIndex", -1); + + if (newIndex >= 0) + { + if (index == newIndex) + return; + + MessageOut msg(PGMSG_MOVE_ITEM); + msg.writeInt8(index); + msg.writeInt8(newIndex); + msg.writeInt8(item->getQuantity()); + gameServerConnection->send(msg); + } + else + { + /*int source = event.getInt("source"); + int destination = event.getInt("destination"); + int amount = event.getInt("amount", 1);*/ -void InventoryHandler::closeStorage(int type) -{ - // TODO + // TODO + } + } + } } -void InventoryHandler::moveItem(int source, int slot, int amount, - int destination) +bool InventoryHandler::canSplit(const Item *item) { - // TODO + return item && !item->getInfo().getEquippable() + && item->getQuantity() > 1; } size_t InventoryHandler::getSize(int type) const diff --git a/src/net/manaserv/inventoryhandler.h b/src/net/manaserv/inventoryhandler.h index fd08b95e..bb68ceeb 100644 --- a/src/net/manaserv/inventoryhandler.h +++ b/src/net/manaserv/inventoryhandler.h @@ -23,6 +23,7 @@ #define NET_MANASERV_INVENTORYHANDLER_H #include "equipment.h" +#include "listener.h" #include "net/inventoryhandler.h" @@ -37,64 +38,38 @@ class EquipBackend : public Equipment::Backend { memset(mEquipment, 0, sizeof(mEquipment)); } Item *getEquipment(int index) const - { return mEquipment[index]; } + { return 0; } void clear() { - for (int i = 0; i < EQUIPMENT_SIZE; ++i) - delete mEquipment[i]; + } - std::fill_n(mEquipment, EQUIPMENT_SIZE, (Item*) 0); + void setEquipment(unsigned int slot, unsigned int used, int reference) + { + printf("Equip: %d at %dx%d\n", reference, slot, used); } - void setEquipment(int index, int id, int quantity = 0) + void addEquipment(unsigned int slot, int reference) { - if (mEquipment[index] && mEquipment[index]->getId() == id) - return; - - delete mEquipment[index]; - mEquipment[index] = (id > 0) ? new Item(id, quantity) : 0; - - if (mEquipment[index]) - { - mEquipment[index]->setInvIndex(index); - mEquipment[index]->setEquipped(true); - mEquipment[index]->setInEquipment(true); - } + printf("Equip: %d at %d\n", reference, slot); } private: Item *mEquipment[EQUIPMENT_SIZE]; }; -class InventoryHandler : public MessageHandler, Net::InventoryHandler +class InventoryHandler : public MessageHandler, Net::InventoryHandler, + public Mana::Listener { public: InventoryHandler(); void handleMessage(Net::MessageIn &msg); - void equipItem(const Item *item); - - void unequipItem(const Item *item); - - void useItem(const Item *item); - - void dropItem(const Item *item, int amount); + void event(Channels channel, const Mana::Event &event); bool canSplit(const Item *item); - void splitItem(const Item *item, int amount); - - void moveItem(int oldIndex, int newIndex); - - void openStorage(int type); - - void closeStorage(int type); - - void moveItem(int source, int slot, int amount, - int destination); - size_t getSize(int type) const; private: diff --git a/src/net/manaserv/itemhandler.cpp b/src/net/manaserv/itemhandler.cpp index dc3b9f14..af3457db 100644 --- a/src/net/manaserv/itemhandler.cpp +++ b/src/net/manaserv/itemhandler.cpp @@ -21,9 +21,9 @@ #include "net/manaserv/itemhandler.h" -#include "flooritemmanager.h" +#include "actorspritemanager.h" -#include "net/manaserv/protocol.h" +#include "net/manaserv/manaserv_protocol.h" #include "net/manaserv/messagein.h" #include "game.h" @@ -62,8 +62,7 @@ void ItemHandler::handleMessage(Net::MessageIn &msg) { if (Map *map = game->getCurrentMap()) { - floorItemManager->create(id, - itemId, + actorSpriteManager->createItem(id, itemId, x / map->getTileWidth(), y / map->getTileHeight()); } @@ -75,9 +74,9 @@ void ItemHandler::handleMessage(Net::MessageIn &msg) } } } - else if (FloorItem *item = floorItemManager->findById(id)) + else if (FloorItem *item = actorSpriteManager->findItem(id)) { - floorItemManager->destroy(item); + actorSpriteManager->destroy(item); } } } break; diff --git a/src/net/manaserv/loginhandler.cpp b/src/net/manaserv/loginhandler.cpp index 61671824..2f802e21 100644 --- a/src/net/manaserv/loginhandler.cpp +++ b/src/net/manaserv/loginhandler.cpp @@ -29,7 +29,7 @@ #include "net/manaserv/connection.h" #include "net/manaserv/messagein.h" #include "net/manaserv/messageout.h" -#include "net/manaserv/protocol.h" +#include "net/manaserv/manaserv_protocol.h" #include "utils/gettext.h" #include "utils/sha256.h" @@ -196,7 +196,7 @@ void LoginHandler::handleMessage(Net::MessageIn &msg) // Successful unregistration if (errMsg == ERRMSG_OK) { - Client::setState(STATE_UNREGISTER); + Client::setState(STATE_UNREGISTER_SUCCESS); } // Unregistration failed else @@ -251,7 +251,7 @@ void LoginHandler::handleLoginResponse(Net::MessageIn &msg) if (errMsg == ERRMSG_OK) { - readUpdateHost(msg); + readServerInfo(msg); // No worlds atm, but future use :-D Client::setState(STATE_WORLD_SELECT); } @@ -289,7 +289,7 @@ void LoginHandler::handleRegisterResponse(Net::MessageIn &msg) if (errMsg == ERRMSG_OK) { - readUpdateHost(msg); + readServerInfo(msg); Client::setState(STATE_WORLD_SELECT); } else @@ -320,7 +320,7 @@ void LoginHandler::handleRegisterResponse(Net::MessageIn &msg) } } -void LoginHandler::readUpdateHost(Net::MessageIn &msg) +void LoginHandler::readServerInfo(Net::MessageIn &msg) { // Safety check for outdated manaserv versions (remove me later) if (msg.getUnreadLength() == 0) @@ -332,6 +332,13 @@ void LoginHandler::readUpdateHost(Net::MessageIn &msg) mLoginData->updateHost = updateHost; else logger->log("Warning: server does not have an update host set!"); + + // Read the client data folder for dynamic data loading. + // This is only used by the QT client. + msg.readString(); + + // Read the number of character slots + mLoginData->characterSlots = msg.readInt8(); } void LoginHandler::connect() @@ -381,7 +388,7 @@ void LoginHandler::loginAccount(LoginData *loginData) MessageOut msg(PAMSG_LOGIN); - msg.writeInt32(0); // client version + msg.writeInt32(PROTOCOL_VERSION); // client version msg.writeString(loginData->username); msg.writeString(sha256(loginData->username + loginData->password)); @@ -429,7 +436,7 @@ void LoginHandler::registerAccount(LoginData *loginData) MessageOut msg(PAMSG_REGISTER); - msg.writeInt32(0); // client version + msg.writeInt32(PROTOCOL_VERSION); // client version msg.writeString(loginData->username); // Use a hashed password for privacy reasons msg.writeString(sha256(loginData->username + loginData->password)); diff --git a/src/net/manaserv/loginhandler.h b/src/net/manaserv/loginhandler.h index d2ffbc3d..2062dcb5 100644 --- a/src/net/manaserv/loginhandler.h +++ b/src/net/manaserv/loginhandler.h @@ -80,7 +80,7 @@ class LoginHandler : public MessageHandler, public Net::LoginHandler void handleLoginResponse(Net::MessageIn &msg); void handleRegisterResponse(Net::MessageIn &msg); - void readUpdateHost(Net::MessageIn &msg); + void readServerInfo(Net::MessageIn &msg); LoginData *mLoginData; unsigned int mMinUserNameLength; diff --git a/src/net/manaserv/protocol.h b/src/net/manaserv/manaserv_protocol.h index 226a27a0..84f1c1a5 100644 --- a/src/net/manaserv/protocol.h +++ b/src/net/manaserv/manaserv_protocol.h @@ -22,6 +22,10 @@ #ifndef MANASERV_PROTOCOL_H #define MANASERV_PROTOCOL_H +namespace ManaServ { + +enum { PROTOCOL_VERSION = 1 }; + /** * Enumerated type for communicated messages: * @@ -44,21 +48,24 @@ enum { // Login/Register PAMSG_REGISTER = 0x0000, // D version, S username, S password, S email, S captcha response - APMSG_REGISTER_RESPONSE = 0x0002, // B error, [S updatehost] + APMSG_REGISTER_RESPONSE = 0x0002, // B error, S updatehost, S Client data URL, B Character slots PAMSG_UNREGISTER = 0x0003, // S username, S password APMSG_UNREGISTER_RESPONSE = 0x0004, // B error PAMSG_REQUEST_REGISTER_INFO = 0x0005, // APMSG_REGISTER_INFO_RESPONSE = 0x0006, // B byte registration Allowed, byte minNameLength, byte maxNameLength, string captchaURL, string captchaInstructions PAMSG_LOGIN = 0x0010, // D version, S username, S password - APMSG_LOGIN_RESPONSE = 0x0012, // B error, [S updatehost] + APMSG_LOGIN_RESPONSE = 0x0012, // B error, S updatehost, S Client data URL, B Character slots PAMSG_LOGOUT = 0x0013, // - APMSG_LOGOUT_RESPONSE = 0x0014, // B error - PAMSG_CHAR_CREATE = 0x0020, // S name, B hair style, B hair color, B gender, W*6 stats + PAMSG_CHAR_CREATE = 0x0020, // S name, B hair style, B hair color, B gender, B slot, {W stats}* APMSG_CHAR_CREATE_RESPONSE = 0x0021, // B error - PAMSG_CHAR_DELETE = 0x0022, // B index + PAMSG_CHAR_DELETE = 0x0022, // B slot APMSG_CHAR_DELETE_RESPONSE = 0x0023, // B error - APMSG_CHAR_INFO = 0x0024, // B index, S name, B gender, B hair style, B hair color, W level, W character points, W correction points, D money, W*6 stats - PAMSG_CHAR_SELECT = 0x0026, // B index + // B slot, S name, B gender, B hair style, B hair color, W level, + // W character points, W correction points, + // {D attr id, D base value (in 1/256ths) D mod value (in 256ths) }* + APMSG_CHAR_INFO = 0x0024, // ^ + PAMSG_CHAR_SELECT = 0x0026, // B slot APMSG_CHAR_SELECT_RESPONSE = 0x0027, // B error, B*32 token, S game address, W game port, S chat address, W chat port PAMSG_EMAIL_CHANGE = 0x0030, // S email APMSG_EMAIL_CHANGE_RESPONSE = 0x0031, // B error @@ -86,18 +93,19 @@ enum { PGMSG_EQUIP = 0x0112, // B slot PGMSG_UNEQUIP = 0x0113, // B slot PGMSG_MOVE_ITEM = 0x0114, // B slot1, B slot2, B amount - GPMSG_INVENTORY = 0x0120, // { B slot, W item id [, B amount] }* - GPMSG_INVENTORY_FULL = 0x0121, // { B slot, W item id [, B amount] }* - GPMSG_PLAYER_ATTRIBUTE_CHANGE = 0x0130, // { W attribute, W base value, W modified value }* + GPMSG_INVENTORY = 0x0120, // { W slot, W item id [, W amount] (if item id is nonzero) }* + GPMSG_INVENTORY_FULL = 0x0121, // W inventory slot count { W slot, W itemId, W amount }, { B equip slot, W invy slot}* + GPMSG_EQUIP = 0x0122, // { W Invy slot, B equip slot type count { B equip slot, B number used} }* + GPMSG_PLAYER_ATTRIBUTE_CHANGE = 0x0130, // { W attribute, D base value (in 1/256ths), D modified value (in 1/256ths)}* GPMSG_PLAYER_EXP_CHANGE = 0x0140, // { W skill, D exp got, D exp needed }* GPMSG_LEVELUP = 0x0150, // W new level, W character points, W correction points GPMSG_LEVEL_PROGRESS = 0x0151, // B percent completed to next levelup - PGMSG_RAISE_ATTRIBUTE = 0x0160, // B attribute - GPMSG_RAISE_ATTRIBUTE_RESPONSE = 0x0161, // B error, B attribute - PGMSG_LOWER_ATTRIBUTE = 0x0170, // B attribute - GPMSG_LOWER_ATTRIBUTE_RESPONSE = 0x0171, // B error, B attribute + PGMSG_RAISE_ATTRIBUTE = 0x0160, // W attribute + GPMSG_RAISE_ATTRIBUTE_RESPONSE = 0x0161, // B error, W attribute + PGMSG_LOWER_ATTRIBUTE = 0x0170, // W attribute + GPMSG_LOWER_ATTRIBUTE_RESPONSE = 0x0171, // B error, W attribute PGMSG_RESPAWN = 0x0180, // - - GPMSG_BEING_ENTER = 0x0200, // B type, W being id, B action, W*2 position + GPMSG_BEING_ENTER = 0x0200, // B type, W being id, B action, W*2 position, B direction // character: S name, B hair style, B hair color, B gender, B item bitmask, { W item id }* // monster: W type id // npc: W type id @@ -109,7 +117,7 @@ enum { GPMSG_BEING_ACTION_CHANGE = 0x0271, // W being id, B action PGMSG_DIRECTION_CHANGE = 0x0272, // B Direction GPMSG_BEING_DIR_CHANGE = 0x0273, // W being id, B direction - GPMSG_BEING_HEALTH_CHANGE = 0x0274, // W being id, W health + GPMSG_BEING_HEALTH_CHANGE = 0x0274, // W being id, W hp, W max hp GPMSG_BEINGS_MOVE = 0x0280, // { W being id, B flags [, W*2 position, B speed] }* GPMSG_ITEMS = 0x0281, // { W item id, W*2 position }* PGMSG_ATTACK = 0x0290, // W being id @@ -255,7 +263,8 @@ enum { ERRMSG_ALREADY_TAKEN, // name used was already taken ERRMSG_SERVER_FULL, // the server is overloaded ERRMSG_TIME_OUT, // data failed to arrive in due time - ERRMSG_LIMIT_REACHED // limit reached + ERRMSG_LIMIT_REACHED, // limit reached + ERRMSG_ADMINISTRATIVE_LOGOFF // kicked by server administrator }; // used in AGMSG_REGISTER_RESPONSE to show state of item db @@ -272,10 +281,11 @@ enum { // used to identify part of sync message enum { - SYNC_CHARACTER_POINTS = 0x01, // D charId, D charPoints, D corrPoints, B attribute id, D attribute value - SYNC_CHARACTER_SKILL = 0x02, // D charId, B skillId, D skill value - SYNC_ONLINE_STATUS = 0x03, // D charId, B 0x00 = offline, 0x01 = online - SYNC_END_OF_BUFFER = 0xFF // shows, that the buffer ends here. + SYNC_CHARACTER_POINTS = 0x01, // D charId, D charPoints, D corrPoints + SYNC_CHARACTER_ATTRIBUTE = 0x02, // D charId, D attrId, DF base, DF mod + SYNC_CHARACTER_SKILL = 0x03, // D charId, B skillId, D skill value + SYNC_ONLINE_STATUS = 0x04, // D charId, B 0x00 = offline, 0x01 = online + SYNC_END_OF_BUFFER = 0xFF // shows, that the buffer ends here. }; // Login specific return values @@ -300,9 +310,10 @@ enum { CREATE_INVALID_GENDER, CREATE_ATTRIBUTES_TOO_HIGH, CREATE_ATTRIBUTES_TOO_LOW, - CREATE_ATTRIBUTES_EQUAL_TO_ZERO, + CREATE_ATTRIBUTES_OUT_OF_RANGE, CREATE_EXISTS_NAME, - CREATE_TOO_MUCH_CHARACTERS + CREATE_TOO_MUCH_CHARACTERS, + CREATE_INVALID_SLOT }; // Character attribute modification specific return value @@ -370,8 +381,54 @@ enum { GUILD_EVENT_OFFLINE_PLAYER }; +/** + * Moves enum for beings and actors for others players vision. + * WARNING: Has to be in sync with the same enum in the Being class + * of the client! + */ +enum BeingAction +{ + STAND, + WALK, + ATTACK, + SIT, + DEAD, + HURT +}; + +/** + * Moves enum for beings and actors for others players attack types. + * WARNING: Has to be in sync with the same enum in the Being class + * of the client! + */ +enum AttackType +{ + HIT = 0x00, + CRITICAL = 0x0a, + MULTI = 0x08, + REFLECT = 0x04, + FLEE = 0x0b +}; + +/** + * Beings and actors directions + * WARNING: Has to be in sync with the same enum in the Being class + * of the client! + */ +enum BeingDirection +{ + DOWN = 1, + LEFT = 2, + UP = 4, + RIGHT = 8 +}; -enum +/** + * enum for sprites layers. + * WARNING: Has to be in sync with the same enum in the Sprite class + * of the client! + */ +enum SpriteLayer { SPRITE_BASE = 0, SPRITE_SHOE, @@ -383,4 +440,6 @@ enum SPRITE_VECTOREND }; +}; // Namespace ManaServ + #endif // MANASERV_PROTOCOL_H diff --git a/src/net/manaserv/network.cpp b/src/net/manaserv/network.cpp index 636585c9..a5bf6186 100644 --- a/src/net/manaserv/network.cpp +++ b/src/net/manaserv/network.cpp @@ -53,7 +53,7 @@ void initialize() logger->error("Failed to initialize ENet."); } -#ifdef ENET_VERSION_MAJOR +#if defined(ENET_VERSION) && ENET_VERSION >= ENET_CUTOFF client = enet_host_create(NULL, 3, 0, 0, 0); #else client = enet_host_create(NULL, 3, 0, 0); diff --git a/src/net/manaserv/npchandler.cpp b/src/net/manaserv/npchandler.cpp index 392ec4fd..ca7d7415 100644 --- a/src/net/manaserv/npchandler.cpp +++ b/src/net/manaserv/npchandler.cpp @@ -21,16 +21,15 @@ #include "net/manaserv/npchandler.h" -#include "beingmanager.h" -#include "npc.h" - -#include "gui/npcdialog.h" -#include "gui/npcpostdialog.h" +#include "actorspritemanager.h" +#include "event.h" #include "net/manaserv/connection.h" #include "net/manaserv/messagein.h" #include "net/manaserv/messageout.h" -#include "net/manaserv/protocol.h" +#include "net/manaserv/manaserv_protocol.h" + +#include "utils/stringutils.h" extern Net::NpcHandler *npcHandler; @@ -56,76 +55,112 @@ NpcHandler::NpcHandler() void NpcHandler::handleMessage(Net::MessageIn &msg) { - Being *being = beingManager->findBeing(msg.readInt16()); - if (!being || being->getType() != Being::NPC) + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + if (!being || being->getType() != ActorSprite::NPC) { return; } - int npcId = being->getId(); - NpcDialogs::iterator diag = mNpcDialogs.find(npcId); - NpcDialog *dialog; - - if (diag == mNpcDialogs.end()) - { - if (msg.getId() == GPMSG_NPC_ERROR || msg.getId() == GPMSG_NPC_CLOSE) - return; // Dialog is pointless in these cases - - dialog = new NpcDialog(npcId); - Wrapper wrap; - wrap.dialog = dialog; - mNpcDialogs[npcId] = wrap; - } - else - { - dialog = diag->second.dialog; - } + int npcId = being->getId(), count = 0; + Mana::Event *event = 0; switch (msg.getId()) { - case GPMSG_NPC_CHOICE: - dialog->choiceRequest(); - while (msg.getUnreadLength()) - { - dialog->addChoice(msg.readString()); - } - break; - - case GPMSG_NPC_NUMBER: + case GPMSG_NPC_CHOICE: + event = new Mana::Event(EVENT_MENU); + event->setInt("id", npcId); + while (msg.getUnreadLength()) { - int min_num = msg.readInt32(); - int max_num = msg.readInt32(); - dialog->integerRequest(msg.readInt32(), min_num, max_num); - break; + count++; + event->setString("choice" + toString(count), msg.readString()); } + event->setInt("choiceCount", count); + event->trigger(CHANNEL_NPC); + break; + + case GPMSG_NPC_NUMBER: + event = new Mana::Event(EVENT_INTEGERINPUT); + event->setInt("id", npcId); + event->setInt("min", msg.readInt32()); + event->setInt("max", msg.readInt32()); + event->setInt("default", msg.readInt32()); + event->trigger(CHANNEL_NPC); + break; + + case GPMSG_NPC_STRING: + event = new Mana::Event(EVENT_STRINGINPUT); + event->setInt("id", npcId); + event->trigger(CHANNEL_NPC); + break; + + case GPMSG_NPC_POST: + event = new Mana::Event(EVENT_POST); + event->setInt("id", npcId); + event->trigger(CHANNEL_NPC); + break; + + case GPMSG_NPC_ERROR: + event = new Mana::Event(EVENT_END); + event->setInt("id", npcId); + event->trigger(CHANNEL_NPC); + break; + + case GPMSG_NPC_MESSAGE: + event = new Mana::Event(EVENT_MESSAGE); + event->setInt("id", npcId); + event->setString("text", msg.readString(msg.getUnreadLength())); + event->trigger(CHANNEL_NPC); + delete event; + + event = new Mana::Event(EVENT_NEXT); + event->setInt("id", npcId); + event->trigger(CHANNEL_NPC); + break; + + case GPMSG_NPC_CLOSE: + event = new Mana::Event(EVENT_CLOSE); + event->setInt("id", npcId); + event->trigger(CHANNEL_NPC); + break; + } - case GPMSG_NPC_STRING: - dialog->textRequest(""); - break; + delete event; +} - case GPMSG_NPC_POST: - { - new NpcPostDialog(npcId); - break; - } +void NpcHandler::startShopping(int beingId) +{ + // TODO +} - case GPMSG_NPC_ERROR: - dialog->close(); - if (diag != mNpcDialogs.end()) - { - mNpcDialogs.erase(diag); - } - break; - - case GPMSG_NPC_MESSAGE: - dialog->addText(msg.readString(msg.getUnreadLength())); - dialog->showNextButton(); - break; - - case GPMSG_NPC_CLOSE: - dialog->showCloseButton(); - break; - } +void NpcHandler::buy(int beingId) +{ + // TODO +} + +void NpcHandler::sell(int beingId) +{ + // TODO +} + +void NpcHandler::buyItem(int beingId, int itemId, int amount) +{ + MessageOut msg(PGMSG_NPC_BUYSELL); + msg.writeInt16(itemId); + msg.writeInt16(amount); + gameServerConnection->send(msg); +} + +void NpcHandler::sellItem(int beingId, int itemId, int amount) +{ + MessageOut msg(PGMSG_NPC_BUYSELL); + msg.writeInt16(itemId); + msg.writeInt16(amount); + gameServerConnection->send(msg); +} + +void NpcHandler::endShopping(int beingId) +{ + // TODO } void NpcHandler::talk(int npcId) @@ -133,6 +168,10 @@ void NpcHandler::talk(int npcId) MessageOut msg(PGMSG_NPC_TALK); msg.writeInt16(npcId); gameServerConnection->send(msg); + + Mana::Event event(EVENT_TALKSENT); + event.setInt("npcId", npcId); + event.trigger(CHANNEL_NPC); } void NpcHandler::nextDialog(int npcId) @@ -140,6 +179,10 @@ void NpcHandler::nextDialog(int npcId) MessageOut msg(PGMSG_NPC_TALK_NEXT); msg.writeInt16(npcId); gameServerConnection->send(msg); + + Mana::Event event(EVENT_NEXTSENT); + event.setInt("npcId", npcId); + event.trigger(CHANNEL_NPC); } void NpcHandler::closeDialog(int npcId) @@ -148,20 +191,22 @@ void NpcHandler::closeDialog(int npcId) msg.writeInt16(npcId); gameServerConnection->send(msg); - NpcDialogs::iterator it = mNpcDialogs.find(npcId); - if (it != mNpcDialogs.end()) - { - (*it).second.dialog->close(); - mNpcDialogs.erase(it); - } + Mana::Event event(EVENT_CLOSESENT); + event.setInt("npcId", npcId); + event.trigger(CHANNEL_NPC); } -void NpcHandler::listInput(int npcId, int value) +void NpcHandler::menuSelect(int npcId, int choice) { MessageOut msg(PGMSG_NPC_SELECT); msg.writeInt16(npcId); - msg.writeInt8(value); + msg.writeInt8(choice); gameServerConnection->send(msg); + + Mana::Event event(EVENT_MENUSENT); + event.setInt("npcId", npcId); + event.setInt("choice", choice); + event.trigger(CHANNEL_NPC); } void NpcHandler::integerInput(int npcId, int value) @@ -170,6 +215,11 @@ void NpcHandler::integerInput(int npcId, int value) msg.writeInt16(npcId); msg.writeInt32(value); gameServerConnection->send(msg); + + Mana::Event event(EVENT_INTEGERINPUTSENT); + event.setInt("npcId", npcId); + event.setInt("value", value); + event.trigger(CHANNEL_NPC); } void NpcHandler::stringInput(int npcId, const std::string &value) @@ -178,56 +228,26 @@ void NpcHandler::stringInput(int npcId, const std::string &value) msg.writeInt16(npcId); msg.writeString(value); gameServerConnection->send(msg); + + Mana::Event event(EVENT_STRINGINPUTSENT); + event.setInt("npcId", npcId); + event.setString("value", value); + event.trigger(CHANNEL_NPC); } void NpcHandler::sendLetter(int npcId, const std::string &recipient, - const std::string &text) + const std::string &text) { MessageOut msg(PGMSG_NPC_POST_SEND); msg.writeString(recipient); msg.writeString(text); gameServerConnection->send(msg); -} - -void NpcHandler::startShopping(int beingId) -{ - // TODO -} - -void NpcHandler::buy(int beingId) -{ - // TODO -} - -void NpcHandler::sell(int beingId) -{ - // TODO -} -void NpcHandler::buyItem(int beingId, int itemId, int amount) -{ - MessageOut msg(PGMSG_NPC_BUYSELL); - msg.writeInt16(itemId); - msg.writeInt16(amount); - gameServerConnection->send(msg); -} - -void NpcHandler::sellItem(int beingId, int itemId, int amount) -{ - MessageOut msg(PGMSG_NPC_BUYSELL); - msg.writeInt16(itemId); - msg.writeInt16(amount); - gameServerConnection->send(msg); -} - -void NpcHandler::endShopping(int beingId) -{ - // TODO -} - -void NpcHandler::clearDialogs() -{ - mNpcDialogs.clear(); + Mana::Event event(EVENT_SENDLETTERSENT); + event.setInt("npcId", npcId); + event.setString("recipient", recipient); + event.setString("text", text); + event.trigger(CHANNEL_NPC); } } // namespace ManaServ diff --git a/src/net/manaserv/npchandler.h b/src/net/manaserv/npchandler.h index 689fdc1d..cb8fd67d 100644 --- a/src/net/manaserv/npchandler.h +++ b/src/net/manaserv/npchandler.h @@ -22,14 +22,14 @@ #ifndef NET_MANASERV_NPCHANDLER_H #define NET_MANASERV_NPCHANDLER_H +#include "listener.h" + #include "net/npchandler.h" #include "net/manaserv/messagehandler.h" #include <map> -class NpcDialog; - namespace ManaServ { class NpcHandler : public MessageHandler, public Net::NpcHandler @@ -39,21 +39,6 @@ class NpcHandler : public MessageHandler, public Net::NpcHandler void handleMessage(Net::MessageIn &msg); - void talk(int npcId); - - void nextDialog(int npcId); - - void closeDialog(int npcId); - - void listInput(int npcId, int value); - - void integerInput(int npcId, int value); - - void stringInput(int npcId, const std::string &value); - - void sendLetter(int npcId, const std::string &recipient, - const std::string &text); - void startShopping(int beingId); void buy(int beingId); @@ -66,14 +51,21 @@ class NpcHandler : public MessageHandler, public Net::NpcHandler void endShopping(int beingId); - void clearDialogs(); + void talk(int npcId); + + void nextDialog(int npcId); + + void closeDialog(int npcId); + + void menuSelect(int npcId, int choice); + + void integerInput(int npcId, int value); + + void stringInput(int npcId, const std::string &value); + + void sendLetter(int npcId, const std::string &recipient, + const std::string &text); - private: - typedef struct { - NpcDialog* dialog; - } Wrapper; - typedef std::map<int, Wrapper> NpcDialogs; - NpcDialogs mNpcDialogs; }; } // namespace ManaServ diff --git a/src/net/manaserv/partyhandler.cpp b/src/net/manaserv/partyhandler.cpp index ec153fa8..b30d5391 100644 --- a/src/net/manaserv/partyhandler.cpp +++ b/src/net/manaserv/partyhandler.cpp @@ -21,17 +21,16 @@ #include "net/manaserv/partyhandler.h" +#include "event.h" #include "log.h" #include "localplayer.h" #include "gui/socialwindow.h" -#include "gui/widgets/chattab.h" - #include "net/manaserv/connection.h" #include "net/manaserv/messagein.h" #include "net/manaserv/messageout.h" -#include "net/manaserv/protocol.h" +#include "net/manaserv/manaserv_protocol.h" #include "utils/gettext.h" #include "utils/stringutils.h" @@ -85,7 +84,7 @@ void PartyHandler::handleMessage(Net::MessageIn &msg) if (msg.readInt8() == ERRMSG_OK) { // - localChatTab->chatLog(_("Joined party.")); + SERVER_NOTICE(_("Joined party.")); } } @@ -103,7 +102,7 @@ void PartyHandler::handleMessage(Net::MessageIn &msg) int id = msg.readInt16(); // being id std::string name = msg.readString(); - localChatTab->chatLog(strprintf(_("%s joined the party."), + SERVER_NOTICE(strprintf(_("%s joined the party."), name.c_str())); if (id == player_node->getId()) @@ -120,8 +119,8 @@ void PartyHandler::handleMessage(Net::MessageIn &msg) case CPMSG_PARTY_REJECTED: { std::string name = msg.readString(); - localChatTab->chatLog(strprintf(_("%s rejected your invite."), - name.c_str())); + SERVER_NOTICE(strprintf( + _("%s rejected your invite."), name.c_str())); } break; } } @@ -136,9 +135,9 @@ void PartyHandler::join(int partyId) // TODO } -void PartyHandler::invite(Player *player) +void PartyHandler::invite(Being *being) { - invite(player->getName()); + invite(being->getName()); } void PartyHandler::invite(const std::string &name) @@ -167,7 +166,7 @@ void PartyHandler::leave() chatServerConnection->send(msg); } -void PartyHandler::kick(Player *player) +void PartyHandler::kick(Being *being) { // TODO } diff --git a/src/net/manaserv/partyhandler.h b/src/net/manaserv/partyhandler.h index 0777b49e..29dc280d 100644 --- a/src/net/manaserv/partyhandler.h +++ b/src/net/manaserv/partyhandler.h @@ -43,7 +43,7 @@ public: void join(int partyId); - void invite(Player *player); + void invite(Being *being); void invite(const std::string &name); @@ -51,7 +51,7 @@ public: void leave(); - void kick(Player *player); + void kick(Being *being); void kick(const std::string &name); diff --git a/src/net/manaserv/playerhandler.cpp b/src/net/manaserv/playerhandler.cpp index 33367927..c071ca04 100644 --- a/src/net/manaserv/playerhandler.cpp +++ b/src/net/manaserv/playerhandler.cpp @@ -24,14 +24,14 @@ #include "client.h" #include "effectmanager.h" +#include "event.h" #include "game.h" #include "localplayer.h" #include "log.h" #include "particle.h" -#include "npc.h" +#include "playerinfo.h" #include "configuration.h" -#include "gui/chat.h" #include "gui/gui.h" #include "gui/okdialog.h" #include "gui/viewport.h" @@ -39,10 +39,11 @@ #include "net/net.h" #include "net/manaserv/connection.h" +#include "net/manaserv/defines.h" #include "net/manaserv/messagein.h" #include "net/manaserv/messageout.h" -#include "net/manaserv/npchandler.h" -#include "net/manaserv/protocol.h" +#include "net/manaserv/manaserv_protocol.h" +#include "net/manaserv/attributes.h" /** * Max. distance we are willing to scroll after a teleport; @@ -64,9 +65,7 @@ void RespawnRequestListener::action(const gcn::ActionEvent &event) { Net::getPlayerHandler()->respawn(); - ManaServ::NpcHandler *handler = - static_cast<ManaServ::NpcHandler*>(Net::getNpcHandler()); - handler->clearDialogs(); + Mana::Event::trigger(CHANNEL_NPC, EVENT_CLOSEALL); } PlayerHandler::PlayerHandler() @@ -112,23 +111,24 @@ void PlayerHandler::handleMessage(Net::MessageIn &msg) case GPMSG_PLAYER_ATTRIBUTE_CHANGE: { - logger->log("ATTRIBUTE UPDATE:"); while (msg.getUnreadLength()) { - int stat = msg.readInt16(); - int base = msg.readInt16(); - int value = msg.readInt16(); - logger->log("%d set to %d %d", stat, base, value); - - if (stat == BASE_ATTR_HP) + int attrId = msg.readInt16(); + double base = msg.readInt32() / 256.0; + double value = msg.readInt32() / 256.0; + + // Set the core player attribute the stat + // depending on attribute link. + int playerInfoId = + Attributes::getPlayerInfoIdFromAttrId(attrId); + if (playerInfoId > -1) { - player_node->setMaxHp(base); - player_node->setHp(value); + PlayerInfo::setAttribute(playerInfoId, value); } else { - player_node->setAttributeBase(stat, base); - player_node->setAttributeEffective(stat, value); + PlayerInfo::setStatBase(attrId, base); + PlayerInfo::setStatMod(attrId, value - base); } } } break; @@ -142,33 +142,33 @@ void PlayerHandler::handleMessage(Net::MessageIn &msg) int current = msg.readInt32(); int next = msg.readInt32(); - player_node->setExperience(skill, current, next); + PlayerInfo::setStatExperience(skill, current, next); } } break; case GPMSG_LEVELUP: { - player_node->setLevel(msg.readInt16()); - player_node->setCharacterPoints(msg.readInt16()); - player_node->setCorrectionPoints(msg.readInt16()); + PlayerInfo::setAttribute(LEVEL, msg.readInt16()); + PlayerInfo::setAttribute(CHAR_POINTS, msg.readInt16()); + PlayerInfo::setAttribute(CORR_POINTS, msg.readInt16()); Particle* effect = particleEngine->addEffect( - paths.getValue("particles", "graphics/particles/") - + paths.getValue("levelUpEffectFile", "levelup.particle.xml"), - 0, 0); + paths.getStringValue("particles") + + paths.getStringValue("levelUpEffectFile") + ,0, 0); player_node->controlParticle(effect); } break; case GPMSG_LEVEL_PROGRESS: { - player_node->setExp(msg.readInt8(), false); + PlayerInfo::setAttribute(EXP, msg.readInt8()); } break; case GPMSG_RAISE_ATTRIBUTE_RESPONSE: { int errCode = msg.readInt8(); - int attrNum = msg.readInt8() - CHAR_ATTR_BEGIN; + int attrNum = msg.readInt16(); switch (errCode) { case ATTRIBMOD_OK: @@ -185,18 +185,19 @@ void PlayerHandler::handleMessage(Net::MessageIn &msg) // has to be correct. The server is always right! // undo attribute change and set points to 0 logger->log("Warning: Server denied increase of attribute %d (no points left) ", attrNum); - int attrValue = player_node->getAttributeBase(attrNum) - 1; - player_node->setCharacterPoints(0); - player_node->setAttributeBase(attrNum, attrValue); + int attrValue = PlayerInfo::getStatBase(attrNum) - 1; + PlayerInfo::setAttribute(CHAR_POINTS, 0); + PlayerInfo::setStatBase(attrNum, attrValue); } break; case ATTRIBMOD_DENIED: { // undo attribute change logger->log("Warning: Server denied increase of attribute %d (reason unknown) ", attrNum); - int points = player_node->getCharacterPoints() - 1; - player_node->setCharacterPoints(points); - int attrValue = player_node->getAttributeBase(attrNum) - 1; - player_node->setAttributeBase(attrNum, attrValue); + int points = PlayerInfo::getAttribute(CHAR_POINTS) - 1; + PlayerInfo::setAttribute(CHAR_POINTS, points); + + int attrValue = PlayerInfo::getStatBase(attrNum) - 1; + PlayerInfo::setStatBase(attrNum, attrValue); } break; } } break; @@ -204,7 +205,7 @@ void PlayerHandler::handleMessage(Net::MessageIn &msg) case GPMSG_LOWER_ATTRIBUTE_RESPONSE: { int errCode = msg.readInt8(); - int attrNum = msg.readInt8() - CHAR_ATTR_BEGIN; + int attrNum = msg.readInt16(); switch (errCode) { case ATTRIBMOD_OK: @@ -221,21 +222,24 @@ void PlayerHandler::handleMessage(Net::MessageIn &msg) // has to be correct. The server is always right! // undo attribute change and set points to 0 logger->log("Warning: Server denied reduction of attribute %d (no points left) ", attrNum); - int attrValue = player_node->getAttributeBase(attrNum) + 1; - player_node->setCorrectionPoints(0); - player_node->setAttributeBase(attrNum, attrValue); - break; + int attrValue = PlayerInfo::getStatBase(attrNum) + 1; + // TODO are these right? + PlayerInfo::setAttribute(CHAR_POINTS, 0); + PlayerInfo::setAttribute(CORR_POINTS, 0); + PlayerInfo::setStatBase(attrNum, attrValue); } break; case ATTRIBMOD_DENIED: { // undo attribute change logger->log("Warning: Server denied reduction of attribute %d (reason unknown) ", attrNum); - int charaPoints = player_node->getCharacterPoints() - 1; - player_node->setCharacterPoints(charaPoints); - int correctPoints = player_node->getCorrectionPoints() + 1; - player_node->setCorrectionPoints(correctPoints); - int attrValue = player_node->getAttributeBase(attrNum) + 1; - player_node->setAttributeBase(attrNum, attrValue); + int charaPoints = PlayerInfo::getAttribute(CHAR_POINTS) - 1; + PlayerInfo::setAttribute(CHAR_POINTS, charaPoints); + + int correctPoints = PlayerInfo::getAttribute(CORR_POINTS) + 1; + PlayerInfo::setAttribute(CORR_POINTS, correctPoints); + + int attrValue = PlayerInfo::getStatBase(attrNum) + 1; + PlayerInfo::setStatBase(attrNum, attrValue); } break; } @@ -250,7 +254,7 @@ void PlayerHandler::handleMessage(Net::MessageIn &msg) int current = msg.readInt32(); int max = msg.readInt32(); int recharge = msg.readInt32(); - player_node->setSpecialStatus(id, current, max, recharge); + PlayerInfo::setSpecialStatus(id, current, max, recharge); } } break; /* @@ -325,14 +329,14 @@ void PlayerHandler::emote(int emoteId) void PlayerHandler::increaseAttribute(int attr) { MessageOut msg(PGMSG_RAISE_ATTRIBUTE); - msg.writeInt8(attr); + msg.writeInt16(attr); gameServerConnection->send(msg); } void PlayerHandler::decreaseAttribute(int attr) { MessageOut msg(PGMSG_LOWER_ATTRIBUTE); - msg.writeInt8(attr); + msg.writeInt16(attr); gameServerConnection->send(msg); } @@ -343,11 +347,14 @@ void PlayerHandler::increaseSkill(int skillId) void PlayerHandler::pickUp(FloorItem *floorItem) { - int id = floorItem->getId(); - MessageOut msg(PGMSG_PICKUP); - msg.writeInt16(id >> 16); - msg.writeInt16(id & 0xFFFF); - gameServerConnection->send(msg); + if (floorItem) + { + int id = floorItem->getId(); + MessageOut msg(PGMSG_PICKUP); + msg.writeInt16(id >> 16); + msg.writeInt16(id & 0xFFFF); + gameServerConnection->send(msg); + } } void PlayerHandler::setDirection(char direction) diff --git a/src/net/manaserv/specialhandler.cpp b/src/net/manaserv/specialhandler.cpp index 144111c2..11d361c8 100644 --- a/src/net/manaserv/specialhandler.cpp +++ b/src/net/manaserv/specialhandler.cpp @@ -24,7 +24,7 @@ #include "net/manaserv/connection.h" #include "net/manaserv/messagein.h" #include "net/manaserv/messageout.h" -#include "net/manaserv/protocol.h" +#include "net/manaserv/manaserv_protocol.h" extern Net::SpecialHandler *specialHandler; diff --git a/src/net/manaserv/stats.cpp b/src/net/manaserv/stats.cpp deleted file mode 100644 index ece0e72a..00000000 --- a/src/net/manaserv/stats.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/* - * The Mana Client - * Copyright (C) 2010 The Mana Developers - * - * This file is part of The Mana Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "net/manaserv/stats.h" - -#include "log.h" - -#include "gui/statuswindow.h" - -#include "resources/itemdb.h" - -#include "utils/gettext.h" -#include "utils/xml.h" - -#include <list> -#include <map> - -#define DEFAULT_ATTRIBUTESDB_FILE "attributes.xml" - -namespace ManaServ { -namespace Stats { - typedef struct { - unsigned int id; - std::string name; - std::string tag; - std::string effect; - std::string description; - bool modifiable; - } Stat; - - typedef std::map<unsigned int, Stat> StatMap; - StatMap stats; - - static void loadBuiltins() - { - { - Stat s; - s.id = 16; - s.name = _("Strength"); - s.tag = "str"; - s.effect = _("Strength %+d"); - s.description = ""; - s.modifiable = true; - - stats[s.id] = s; - } - - { - Stat s; - s.id = 17; - s.name = _("Agility"); - s.tag = "agi"; - s.effect = _("Agility %+d"); - s.description = ""; - s.modifiable = true; - - stats[s.id] = s; - } - - { - Stat s; - s.id = 18; - s.name = _("Dexterity"); - s.tag = "dex"; - s.effect = _("Dexterity %+d"); - s.description = ""; - s.modifiable = true; - - stats[s.id] = s; - } - - { - Stat s; - s.id = 19; - s.name = _("Vitality"); - s.tag = "vit"; - s.effect = _("Vitality %+d"); - s.description = ""; - s.modifiable = true; - - stats[s.id] = s; - } - - { - Stat s; - s.id = 20; - s.name = _("Intelligence"); - s.tag = "int"; - s.effect = _("Intelligence %+d"); - s.description = ""; - s.modifiable = true; - - stats[s.id] = s; - } - - { - Stat s; - s.id = 21; - s.name = _("Willpower"); - s.tag = "will"; - s.effect = _("Willpower %+d"); - s.description = ""; - s.modifiable = true; - - stats[s.id] = s; - } - } - - void load() - { - XML::Document doc(DEFAULT_ATTRIBUTESDB_FILE); - xmlNodePtr rootNode = doc.rootNode(); - - if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "stats")) - { - logger->log("Stats: Error while loading " - DEFAULT_ATTRIBUTESDB_FILE ". Using Built-ins."); - loadBuiltins(); - return; - } - - for_each_xml_child_node(node, rootNode) - { - if (!xmlStrEqual(node->name, BAD_CAST "stat")) - continue; - - int id = XML::getProperty(node, "id", 0); - - if (id == 0) - { - logger->log("Stats: Invalid or missing stat ID in " - DEFAULT_ATTRIBUTESDB_FILE "!"); - continue; - } - else if (stats.find(id) != stats.end()) - { - logger->log("Stats: Redefinition of stat ID %d", id); - } - - std::string name = XML::getProperty(node, "name", ""); - - if (name.empty()) - { - logger->log("Stats: Invalid or missing stat name in " - DEFAULT_ATTRIBUTESDB_FILE "!"); - continue; - } - - Stat s; - s.id = id; - s.name = name; - s.tag = XML::getProperty(node, "tag", ""); - s.effect = XML::getProperty(node, "effect", ""); - s.description = XML::getProperty(node, "desc", ""); - s.modifiable = XML::getProperty(node, "modifiable", "false") - == "true"; - - stats[id] = s; - } - } - - void unload() - { - stats.clear(); - } - - void informItemDB() - { - std::list<ItemDB::Stat> dbStats; - - StatMap::const_iterator it, it_end; - for (it = stats.begin(), it_end = stats.end(); it != it_end; it++) - if (!it->second.tag.empty()) - dbStats.push_back(ItemDB::Stat(it->second.tag, - it->second.effect)); - - ItemDB::setStatsList(dbStats); - } - - void informStatusWindow() - { - StatMap::const_iterator it, it_end; - for (it = stats.begin(), it_end = stats.end(); it != it_end; it++) - statusWindow->addAttribute(it->second.id, it->second.name, - it->second.modifiable, - it->second.description); - } - - std::vector<std::string> getLabelVector() - { - std::vector<std::string> attributes; - StatMap::const_iterator it, it_end; - for (it = stats.begin(), it_end = stats.end(); it != it_end; it++) - if (it->second.modifiable) - attributes.push_back(it->second.name + ":"); - - return attributes; - } -} // namespace Stats -} // namespace ManaServ diff --git a/src/net/manaserv/tradehandler.cpp b/src/net/manaserv/tradehandler.cpp index 234a18d6..6e205e24 100644 --- a/src/net/manaserv/tradehandler.cpp +++ b/src/net/manaserv/tradehandler.cpp @@ -21,21 +21,21 @@ #include "net/manaserv/tradehandler.h" -#include "beingmanager.h" +#include "actorspritemanager.h" +#include "event.h" #include "item.h" #include "localplayer.h" +#include "playerinfo.h" #include "gui/confirmdialog.h" #include "gui/trade.h" -#include "gui/widgets/chattab.h" - #include "net/net.h" #include "net/manaserv/connection.h" #include "net/manaserv/messagein.h" #include "net/manaserv/messageout.h" -#include "net/manaserv/protocol.h" +#include "net/manaserv/manaserv_protocol.h" #include "utils/gettext.h" #include "utils/stringutils.h" @@ -86,16 +86,15 @@ TradeHandler::TradeHandler(): }; handledMessages = _messages; tradeHandler = this; - } void TradeHandler::setAcceptTradeRequests(bool acceptTradeRequests) { mAcceptTradeRequests = acceptTradeRequests; if (mAcceptTradeRequests) - localChatTab->chatLog(_("Accepting incoming trade requests."), BY_SERVER); + SERVER_NOTICE(_("Accepting incoming trade requests.")) else - localChatTab->chatLog(_("Ignoring incoming trade requests."), BY_SERVER); + SERVER_NOTICE(_("Ignoring incoming trade requests.")) } void TradeHandler::handleMessage(Net::MessageIn &msg) @@ -104,13 +103,13 @@ void TradeHandler::handleMessage(Net::MessageIn &msg) { case GPMSG_TRADE_REQUEST: { - Being *being = beingManager->findBeing(msg.readInt16()); + Being *being = actorSpriteManager->findBeing(msg.readInt16()); if (!being || !mAcceptTradeRequests) { respond(false); break; } - player_node->setTrading(true); + PlayerInfo::setTrading(true); tradePartnerName = being->getName(); tradePartnerID = being->getId(); ConfirmDialog *dlg = new ConfirmDialog(_("Request for Trade"), @@ -144,19 +143,19 @@ void TradeHandler::handleMessage(Net::MessageIn &msg) case GPMSG_TRADE_AGREED: tradeWindow->receivedOk(false); break; - + case GPMSG_TRADE_CANCEL: - localChatTab->chatLog(_("Trade canceled."), BY_SERVER); + SERVER_NOTICE(_("Trade canceled.")) tradeWindow->setVisible(false); tradeWindow->reset(); - player_node->setTrading(false); + PlayerInfo::setTrading(false); break; case GPMSG_TRADE_COMPLETE: - localChatTab->chatLog(_("Trade completed."), BY_SERVER); + SERVER_NOTICE(_("Trade completed.")) tradeWindow->setVisible(false); tradeWindow->reset(); - player_node->setTrading(false); + PlayerInfo::setTrading(false); break; } } @@ -177,7 +176,7 @@ void TradeHandler::respond(bool accept) gameServerConnection->send(msg); if (!accept) - player_node->setTrading(false); + PlayerInfo::setTrading(false); } void TradeHandler::addItem(Item *item, int amount) diff --git a/src/net/net.cpp b/src/net/net.cpp index 1b4bbf36..7dae6b35 100644 --- a/src/net/net.cpp +++ b/src/net/net.cpp @@ -133,17 +133,12 @@ void connectToServer(ServerInfo &server) { // TODO: Query the server about itself and choose the netcode based on // that - -#ifndef MANASERV_SUPPORT - server.type = ServerInfo::TMWATHENA; -#else if (server.port == 6901) server.type = ServerInfo::TMWATHENA; else if (server.port == 9601) server.type = ServerInfo::MANASERV; else logger->error(_("Unknown Server Type! Exiting.")); -#endif } if (networkType == server.type && getGeneralHandler() != NULL) @@ -159,17 +154,14 @@ void connectToServer(ServerInfo &server) switch (server.type) { -#ifdef MANASERV_SUPPORT case ServerInfo::MANASERV: new ManaServ::GeneralHandler; break; -#endif case ServerInfo::TMWATHENA: new TmwAthena::GeneralHandler; break; - default: - // Shouldn't happen... + logger->error(_("Server protocol unsupported")); break; } diff --git a/src/net/npchandler.h b/src/net/npchandler.h index bba8dc31..35535c61 100644 --- a/src/net/npchandler.h +++ b/src/net/npchandler.h @@ -29,13 +29,27 @@ namespace Net { class NpcHandler { public: + virtual ~NpcHandler() {} + + virtual void startShopping(int beingId) = 0; + + virtual void buy(int beingId) = 0; + + virtual void sell(int beingId) = 0; + + virtual void buyItem(int beingId, int itemId, int amount) = 0; + + virtual void sellItem(int beingId, int itemId, int amount) = 0; + + virtual void endShopping(int beingId) = 0; + virtual void talk(int npcId) = 0; virtual void nextDialog(int npcId) = 0; virtual void closeDialog(int npcId) = 0; - virtual void listInput(int npcId, int value) = 0; + virtual void menuSelect(int npcId, int choice) = 0; virtual void integerInput(int npcId, int value) = 0; @@ -44,19 +58,6 @@ class NpcHandler virtual void sendLetter(int npcId, const std::string &recipient, const std::string &text) = 0; - virtual void startShopping(int beingId) = 0; - - virtual void buy(int beingId) = 0; - - virtual void sell(int beingId) = 0; - - virtual void buyItem(int beingId, int itemId, int amount) = 0; - - virtual void sellItem(int beingId, int itemId, int amount) = 0; - - virtual void endShopping(int beingId) = 0; - - virtual ~NpcHandler() {} }; } // namespace Net diff --git a/src/net/partyhandler.h b/src/net/partyhandler.h index dd1103fc..7ca13546 100644 --- a/src/net/partyhandler.h +++ b/src/net/partyhandler.h @@ -24,7 +24,7 @@ #include <string> -class Player; +class Being; enum PartyShare { PARTY_SHARE_UNKNOWN = -1, @@ -38,11 +38,13 @@ namespace Net { class PartyHandler { public: + virtual ~PartyHandler() {} + virtual void create(const std::string &name = "") = 0; virtual void join(int partyId) = 0; - virtual void invite(Player *player) = 0; + virtual void invite(Being *player) = 0; virtual void invite(const std::string &name) = 0; @@ -50,7 +52,7 @@ class PartyHandler virtual void leave() = 0; - virtual void kick(Player *player) = 0; + virtual void kick(Being *player) = 0; virtual void kick(const std::string &name) = 0; @@ -69,8 +71,6 @@ class PartyHandler // virtual void options() = 0; // virtual void message() = 0; - - virtual ~PartyHandler() {} }; } // namespace Net diff --git a/src/net/playerhandler.h b/src/net/playerhandler.h index 399afb5e..d7676a92 100644 --- a/src/net/playerhandler.h +++ b/src/net/playerhandler.h @@ -24,13 +24,14 @@ #include "being.h" #include "flooritem.h" -#include "localplayer.h" namespace Net { class PlayerHandler { public: + virtual ~PlayerHandler() {} + virtual void attack(int id) = 0; virtual void emote(int emoteId) = 0; @@ -62,8 +63,6 @@ class PlayerHandler virtual int getJobLocation() = 0; virtual Vector getDefaultWalkSpeed() = 0; - - virtual ~PlayerHandler() {} }; } // namespace Net diff --git a/src/net/specialhandler.h b/src/net/specialhandler.h index 21e3a4b7..89fcdf7d 100644 --- a/src/net/specialhandler.h +++ b/src/net/specialhandler.h @@ -28,6 +28,8 @@ namespace Net { class SpecialHandler { public: + virtual ~SpecialHandler () {} + virtual void use(int id) = 0; virtual void use(int id, int level, int beingId) = 0; @@ -35,8 +37,6 @@ class SpecialHandler virtual void use(int id, int level, int x, int y) = 0; virtual void use(int id, const std::string &map) = 0; - - virtual ~SpecialHandler () {} }; } diff --git a/src/net/tmwa/adminhandler.cpp b/src/net/tmwa/adminhandler.cpp index e2c3c74b..53e4bfd8 100644 --- a/src/net/tmwa/adminhandler.cpp +++ b/src/net/tmwa/adminhandler.cpp @@ -21,14 +21,12 @@ #include "net/tmwa/adminhandler.h" +#include "actorspritemanager.h" #include "being.h" -#include "beingmanager.h" +#include "event.h" #include "game.h" -#include "player.h" #include "playerrelations.h" -#include "gui/widgets/chattab.h" - #include "net/chathandler.h" #include "net/net.h" @@ -45,7 +43,8 @@ namespace TmwAthena { AdminHandler::AdminHandler() { - static const Uint16 _messages[] = { + static const Uint16 _messages[] = + { SMSG_ADMIN_KICK_ACK, SMSG_ADMIN_IP, 0 @@ -62,15 +61,14 @@ void AdminHandler::handleMessage(Net::MessageIn &msg) case SMSG_ADMIN_KICK_ACK: id = msg.readInt32(); if (id == 0) - localChatTab->chatLog(_("Kick failed!"), BY_SERVER); + SERVER_NOTICE(_("Kick failed!")) else - localChatTab->chatLog(_("Kick succeeded!"), BY_SERVER); + SERVER_NOTICE(_("Kick succeeded!")) break; case SMSG_ADMIN_IP: id = msg.readInt32(); int ip = msg.readInt32(); - Being *being = beingManager->findBeing(id); - if (Player *player = dynamic_cast<Player *>(being)) + if (Being *player = actorSpriteManager->findBeing(id)) { player->setIp(ip); player->updateName(); diff --git a/src/net/tmwa/beinghandler.cpp b/src/net/tmwa/beinghandler.cpp index 2fe962c7..61491692 100644 --- a/src/net/tmwa/beinghandler.cpp +++ b/src/net/tmwa/beinghandler.cpp @@ -21,31 +21,30 @@ #include "net/tmwa/beinghandler.h" +#include "actorspritemanager.h" #include "being.h" -#include "beingmanager.h" #include "client.h" #include "effectmanager.h" #include "guild.h" #include "localplayer.h" #include "log.h" -#include "npc.h" #include "party.h" #include "playerrelations.h" #include "net/tmwa/protocol.h" #include "resources/colordb.h" +#include "resources/emotedb.h" #include <iostream> namespace TmwAthena { -const int EMOTION_TIME = 150; /**< Duration of emotion icon */ - BeingHandler::BeingHandler(bool enableSync): mSync(enableSync) { - static const Uint16 _messages[] = { + static const Uint16 _messages[] = + { SMSG_BEING_VISIBLE, SMSG_BEING_MOVE, SMSG_BEING_SPAWN, @@ -75,19 +74,19 @@ BeingHandler::BeingHandler(bool enableSync): Being *createBeing(int id, short job) { - Being::Type type = Being::UNKNOWN; + ActorSprite::Type type = ActorSprite::UNKNOWN; if (job <= 25 || (job >= 4001 && job <= 4049)) - type = Being::PLAYER; + type = ActorSprite::PLAYER; else if (job >= 46 && job <= 1000) - type = Being::NPC; + type = ActorSprite::NPC; else if (job > 1000 && job <= 2000) - type = Being::MONSTER; + type = ActorSprite::MONSTER; else if (job == 45) return NULL; // Skip portals - Being *being = beingManager->createBeing(id, type, job); + Being *being = actorSpriteManager->createBeing(id, type, job); - if (type == Being::PLAYER || type == Being::NPC) + if (type == ActorSprite::PLAYER || type == ActorSprite::NPC) { MessageOut outMsg(0x0094); outMsg.writeInt32(id);//readLong(2)); @@ -98,7 +97,7 @@ Being *createBeing(int id, short job) void BeingHandler::handleMessage(Net::MessageIn &msg) { - if (!beingManager) + if (!actorSpriteManager) return; int id; @@ -113,7 +112,6 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) int type, guild; Uint16 status; Being *srcBeing, *dstBeing; - Player *player = 0; int hairStyle, hairColor, flag; switch (msg.getId()) @@ -128,7 +126,7 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) statusEffects |= ((Uint32)msg.readInt16()) << 16; // option job = msg.readInt16(); // class - dstBeing = beingManager->findBeing(id); + dstBeing = actorSpriteManager->findBeing(id); if (!dstBeing) { @@ -145,14 +143,10 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) break; } - if (dstBeing->getType() == Being::PLAYER) - player = static_cast<Player*>(dstBeing); - - if (msg.getId() == 0x0078) + if (msg.getId() == SMSG_BEING_VISIBLE) { dstBeing->clearPath(); - dstBeing->setFrame(0); - dstBeing->setWalkTime(tick_time); + dstBeing->setActionTime(tick_time); dstBeing->setAction(Being::STAND); } @@ -178,16 +172,13 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) shoes = msg.readInt16(); // clothes color - "abused" as shoes gloves = msg.readInt16(); // head dir - "abused" as gloves guild = msg.readInt32(); // guild - if (player) + if (guild == 0) { - if (guild == 0) - { - player->clearGuilds(); - } - else - { - player->addGuild(Guild::getGuild(guild)); - } + dstBeing->clearGuilds(); + } + else + { + dstBeing->addGuild(Guild::getGuild(guild)); } msg.readInt16(); // guild emblem msg.readInt16(); // manner @@ -195,19 +186,19 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) msg.readInt8(); // karma gender = msg.readInt8(); - if (player) + if (dstBeing->getType() == ActorSprite::PLAYER) { - player->setGender((gender == 0) - ? GENDER_FEMALE : GENDER_MALE); + dstBeing->setGender((gender == 0) + ? GENDER_FEMALE : GENDER_MALE); // Set these after the gender, as the sprites may be gender-specific - player->setSprite(SPRITE_HAIR, hairStyle * -1, ColorDB::get(hairColor)); - player->setSprite(SPRITE_BOTTOMCLOTHES, headBottom); - player->setSprite(SPRITE_TOPCLOTHES, headMid); - player->setSprite(SPRITE_HAT, headTop); - player->setSprite(SPRITE_SHOE, shoes); - player->setSprite(SPRITE_GLOVES, gloves); - player->setSprite(SPRITE_WEAPON, weapon, "", true); - player->setSprite(SPRITE_SHIELD, shield); + dstBeing->setSprite(SPRITE_HAIR, hairStyle * -1, ColorDB::get(hairColor)); + dstBeing->setSprite(SPRITE_BOTTOMCLOTHES, headBottom); + dstBeing->setSprite(SPRITE_TOPCLOTHES, headMid); + dstBeing->setSprite(SPRITE_HAT, headTop); + dstBeing->setSprite(SPRITE_SHOE, shoes); + dstBeing->setSprite(SPRITE_GLOVES, gloves); + dstBeing->setSprite(SPRITE_WEAPON, weapon, "", true); + dstBeing->setSprite(SPRITE_SHIELD, shield); } if (msg.getId() == SMSG_BEING_MOVE) @@ -249,11 +240,7 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) * later versions of eAthena for both mobs and * players */ - dstBeing = beingManager->findBeing(msg.readInt32()); - - Uint16 srcX, srcY, dstX, dstY; - msg.readCoordinatePair(srcX, srcY, dstX, dstY); - msg.readInt32(); // Server tick + dstBeing = actorSpriteManager->findBeing(msg.readInt32()); /* * This packet doesn't have enough info to actually @@ -261,21 +248,23 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) * we'll just pretend the packet didn't happen */ - if (dstBeing) - { - dstBeing->setAction(Being::STAND); - dstBeing->setTileCoords(srcX, srcY); - dstBeing->setDestination(dstX, dstY); - } + if (!dstBeing) + break; + + Uint16 srcX, srcY, dstX, dstY; + msg.readCoordinatePair(srcX, srcY, dstX, dstY); + msg.readInt32(); // Server tick + + dstBeing->setAction(Being::STAND); + dstBeing->setTileCoords(srcX, srcY); + dstBeing->setDestination(dstX, dstY); break; case SMSG_BEING_REMOVE: // A being should be removed or has died id = msg.readInt32(); - - dstBeing = beingManager->findBeing(id); - + dstBeing = actorSpriteManager->findBeing(id); if (!dstBeing) break; @@ -286,16 +275,14 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) if (msg.readInt8() == 1) dstBeing->setAction(Being::DEAD); else - beingManager->destroyBeing(dstBeing); + actorSpriteManager->destroy(dstBeing); break; case SMSG_BEING_RESURRECT: // A being changed mortality status id = msg.readInt32(); - - dstBeing = beingManager->findBeing(id); - + dstBeing = actorSpriteManager->findBeing(id); if (!dstBeing) break; @@ -310,8 +297,8 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) case SMSG_SKILL_DAMAGE: msg.readInt16(); // Skill Id - srcBeing = beingManager->findBeing(msg.readInt32()); - dstBeing = beingManager->findBeing(msg.readInt32()); + srcBeing = actorSpriteManager->findBeing(msg.readInt32()); + dstBeing = actorSpriteManager->findBeing(msg.readInt32()); msg.readInt32(); // Server tick msg.readInt32(); // src speed msg.readInt32(); // dst speed @@ -326,8 +313,8 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) break; case SMSG_BEING_ACTION: - srcBeing = beingManager->findBeing(msg.readInt32()); - dstBeing = beingManager->findBeing(msg.readInt32()); + srcBeing = actorSpriteManager->findBeing(msg.readInt32()); + dstBeing = actorSpriteManager->findBeing(msg.readInt32()); msg.readInt32(); // server tick msg.readInt32(); // src speed msg.readInt32(); // dst speed @@ -354,7 +341,6 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) case 0x02: // Sit if (srcBeing) { - srcBeing->setFrame(0); srcBeing->setAction(Being::SIT); } break; @@ -362,37 +348,36 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) case 0x03: // Stand up if (srcBeing) { - srcBeing->setFrame(0); srcBeing->setAction(Being::STAND); } break; } break; - case SMSG_BEING_SELFEFFECT: { + case SMSG_BEING_SELFEFFECT: + { id = (Uint32)msg.readInt32(); - if (!beingManager->findBeing(id)) + Being* being = actorSpriteManager->findBeing(id); + if (!being) break; int effectType = msg.readInt32(); - Being* being = beingManager->findBeing(id); effectManager->trigger(effectType, being); - break; } case SMSG_BEING_EMOTION: - if (!(dstBeing = beingManager->findBeing(msg.readInt32()))) + if (!(dstBeing = actorSpriteManager->findBeing(msg.readInt32()))) { break; } if (player_relations.hasPermission(dstBeing, PlayerRelation::EMOTE)) { - // only set emote if one doesnt already exist - if (!dstBeing->getEmotion()) - dstBeing->setEmote(msg.readInt8(), EMOTION_TIME); + const int fx = EmoteDB::get(msg.readInt8())->effect; + //TODO: figure out why the -1 is needed + effectManager->trigger(fx - 1, dstBeing); } break; @@ -412,14 +397,11 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) * 16 bit value will be 0. */ - if (!(dstBeing = beingManager->findBeing(msg.readInt32()))) + if (!(dstBeing = actorSpriteManager->findBeing(msg.readInt32()))) { break; } - if (dstBeing->getType() == Being::PLAYER) - player = static_cast<Player*>(dstBeing); - int type = msg.readInt8(); int id = 0; int id2 = 0; @@ -437,41 +419,41 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) switch (type) { case 1: // eAthena LOOK_HAIR - player->setSpriteID(SPRITE_HAIR, id *-1); + dstBeing->setSpriteID(SPRITE_HAIR, id *-1); break; case 2: // Weapon ID in id, Shield ID in id2 - player->setSprite(SPRITE_WEAPON, id, "", true); - player->setSprite(SPRITE_SHIELD, id2); + dstBeing->setSprite(SPRITE_WEAPON, id, "", true); + dstBeing->setSprite(SPRITE_SHIELD, id2); break; case 3: // Change lower headgear for eAthena, pants for us - player->setSprite(SPRITE_BOTTOMCLOTHES, id); + dstBeing->setSprite(SPRITE_BOTTOMCLOTHES, id); break; case 4: // Change upper headgear for eAthena, hat for us - player->setSprite(SPRITE_HAT, id); + dstBeing->setSprite(SPRITE_HAT, id); break; case 5: // Change middle headgear for eathena, armor for us - player->setSprite(SPRITE_TOPCLOTHES, id); + dstBeing->setSprite(SPRITE_TOPCLOTHES, id); break; case 6: // eAthena LOOK_HAIR_COLOR - player->setSpriteColor(SPRITE_HAIR, ColorDB::get(id)); + dstBeing->setSpriteColor(SPRITE_HAIR, ColorDB::get(id)); break; case 8: // eAthena LOOK_SHIELD - player->setSprite(SPRITE_SHIELD, id); + dstBeing->setSprite(SPRITE_SHIELD, id); break; case 9: // eAthena LOOK_SHOES - player->setSprite(SPRITE_SHOE, id); + dstBeing->setSprite(SPRITE_SHOE, id); break; case 10: // LOOK_GLOVES - player->setSprite(SPRITE_GLOVES, id); + dstBeing->setSprite(SPRITE_GLOVES, id); break; case 11: // LOOK_CAPE - player->setSprite(SPRITE_CAPE, id); + dstBeing->setSprite(SPRITE_CAPE, id); break; case 12: - player->setSprite(SPRITE_MISC1, id); + dstBeing->setSprite(SPRITE_MISC1, id); break; case 13: - player->setSprite(SPRITE_MISC2, id); + dstBeing->setSprite(SPRITE_MISC2, id); break; default: logger->log("SMSG_BEING_CHANGE_LOOKS: unsupported type: " @@ -482,13 +464,13 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) break; case SMSG_BEING_NAME_RESPONSE: - if ((dstBeing = beingManager->findBeing(msg.readInt32()))) + if ((dstBeing = actorSpriteManager->findBeing(msg.readInt32()))) { dstBeing->setName(msg.readString(24)); } break; case SMSG_PLAYER_GUILD_PARTY_INFO: - if ((dstBeing = beingManager->findBeing(msg.readInt32()))) + if ((dstBeing = actorSpriteManager->findBeing(msg.readInt32()))) { dstBeing->setPartyName(msg.readString(24)); dstBeing->setGuildName(msg.readString(24)); @@ -497,7 +479,7 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) } break; case SMSG_BEING_CHANGE_DIRECTION: - if (!(dstBeing = beingManager->findBeing(msg.readInt32()))) + if (!(dstBeing = actorSpriteManager->findBeing(msg.readInt32()))) { break; } @@ -520,7 +502,7 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) << 16; // status.options; Aethyra uses this as misc2 job = msg.readInt16(); - dstBeing = beingManager->findBeing(id); + dstBeing = actorSpriteManager->findBeing(id); if (!dstBeing) { @@ -530,13 +512,10 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) break; } - if (dstBeing->getType() == Being::PLAYER) - player = static_cast<Player*>(dstBeing); - if (Party *party = player_node->getParty()){ if (party->isMember(id)) { - player->setParty(party); + dstBeing->setParty(party); } } @@ -562,21 +541,21 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) msg.readInt16(); // manner dstBeing->setStatusEffectBlock(32, msg.readInt16()); // opt3 msg.readInt8(); // karma - player->setGender((msg.readInt8() == 0) + dstBeing->setGender((msg.readInt8() == 0) ? GENDER_FEMALE : GENDER_MALE); // Set these after the gender, as the sprites may be gender-specific - player->setSprite(SPRITE_WEAPON, weapon, "", true); - player->setSprite(SPRITE_SHIELD, shield); - //player->setSprite(SPRITE_SHOE, shoes); - player->setSprite(SPRITE_BOTTOMCLOTHES, headBottom); - player->setSprite(SPRITE_TOPCLOTHES, headMid); - player->setSprite(SPRITE_HAT, headTop); - //player->setSprite(SPRITE_GLOVES, gloves); - //player->setSprite(SPRITE_CAPE, cape); - //player->setSprite(SPRITE_MISC1, misc1); - //player->setSprite(SPRITE_MISC2, misc2); - player->setSprite(SPRITE_HAIR, hairStyle * -1, ColorDB::get(hairColor)); + dstBeing->setSprite(SPRITE_WEAPON, weapon, "", true); + dstBeing->setSprite(SPRITE_SHIELD, shield); + //dstBeing->setSprite(SPRITE_SHOE, shoes); + dstBeing->setSprite(SPRITE_BOTTOMCLOTHES, headBottom); + dstBeing->setSprite(SPRITE_TOPCLOTHES, headMid); + dstBeing->setSprite(SPRITE_HAT, headTop); + //dstBeing->setSprite(SPRITE_GLOVES, gloves); + //dstBeing->setSprite(SPRITE_CAPE, cape); + //dstBeing->setSprite(SPRITE_MISC1, misc1); + //dstBeing->setSprite(SPRITE_MISC2, misc2); + dstBeing->setSprite(SPRITE_HAIR, hairStyle * -1, ColorDB::get(hairColor)); if (msg.getId() == SMSG_PLAYER_MOVE) { @@ -596,7 +575,7 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) gmstatus = msg.readInt16(); if (gmstatus & 0x80) - player->setGM(true); + dstBeing->setGM(true); if (msg.getId() == SMSG_PLAYER_UPDATE_1) { @@ -619,8 +598,8 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) msg.readInt8(); // Lv msg.readInt8(); // unknown - dstBeing->setWalkTime(tick_time); - dstBeing->setFrame(0); + dstBeing->setActionTime(tick_time); + dstBeing->reset(); dstBeing->setStunMode(stunMode); dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff); @@ -643,18 +622,15 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) id = msg.readInt32(); if (mSync || id != player_node->getId()) { - dstBeing = beingManager->findBeing(id); + dstBeing = actorSpriteManager->findBeing(id); if (dstBeing) { Uint16 x, y; x = msg.readInt16(); y = msg.readInt16(); dstBeing->setTileCoords(x, y); - if (dstBeing->getCurrentAction() == Being::WALK) - { - dstBeing->setFrame(0); + if (dstBeing->getCurrentAction() == Being::MOVE) dstBeing->setAction(Being::STAND); - } } } break; @@ -671,18 +647,18 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) case SMSG_PLAYER_STATUS_CHANGE: // Change in players' flags id = msg.readInt32(); - dstBeing = beingManager->findBeing(id); + dstBeing = actorSpriteManager->findBeing(id); + if (!dstBeing) + break; + stunMode = msg.readInt16(); statusEffects = msg.readInt16(); statusEffects |= ((Uint32) msg.readInt16()) << 16; - msg.readInt8(); + msg.readInt8(); // Unused? - if (dstBeing) - { - dstBeing->setStunMode(stunMode); - dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff); - dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff); - } + dstBeing->setStunMode(stunMode); + dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff); + dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff); break; case SMSG_BEING_STATUS_CHANGE: @@ -691,7 +667,7 @@ void BeingHandler::handleMessage(Net::MessageIn &msg) id = msg.readInt32(); flag = msg.readInt8(); // 0: stop, 1: start - dstBeing = beingManager->findBeing(id); + dstBeing = actorSpriteManager->findBeing(id); if (dstBeing) dstBeing->setStatusEffect(status, flag); break; diff --git a/src/net/tmwa/buysellhandler.cpp b/src/net/tmwa/buysellhandler.cpp index 209f034d..5368ba9d 100644 --- a/src/net/tmwa/buysellhandler.cpp +++ b/src/net/tmwa/buysellhandler.cpp @@ -21,18 +21,17 @@ #include "net/tmwa/buysellhandler.h" -#include "beingmanager.h" +#include "actorspritemanager.h" +#include "event.h" #include "inventory.h" #include "item.h" #include "localplayer.h" -#include "npc.h" +#include "playerinfo.h" #include "gui/buy.h" #include "gui/buysell.h" #include "gui/sell.h" -#include "gui/widgets/chattab.h" - #include "net/messagein.h" #include "net/tmwa/protocol.h" @@ -62,7 +61,7 @@ void BuySellHandler::handleMessage(Net::MessageIn &msg) switch (msg.getId()) { case SMSG_NPC_BUY_SELL_CHOICE: - if (!BuySellDialog::isActive()) + if (PlayerInfo::getBuySellState() != BUYSELL_CHOOSING) { mNpcId = msg.readInt32(); new BuySellDialog(mNpcId); @@ -73,7 +72,7 @@ void BuySellHandler::handleMessage(Net::MessageIn &msg) msg.readInt16(); // length n_items = (msg.getLength() - 4) / 11; mBuyDialog = new BuyDialog(mNpcId); - mBuyDialog->setMoney(player_node->getMoney()); + mBuyDialog->setMoney(PlayerInfo::getAttribute(MONEY)); for (int k = 0; k < n_items; k++) { @@ -91,7 +90,7 @@ void BuySellHandler::handleMessage(Net::MessageIn &msg) if (n_items > 0) { SellDialog *dialog = new SellDialog(mNpcId); - dialog->setMoney(player_node->getMoney()); + dialog->setMoney(PlayerInfo::getAttribute(MONEY)); for (int k = 0; k < n_items; k++) { @@ -99,7 +98,7 @@ void BuySellHandler::handleMessage(Net::MessageIn &msg) int value = msg.readInt32(); msg.readInt32(); // OCvalue - Item *item = player_node->getInventory()->getItem(index); + Item *item = PlayerInfo::getInventory()->getItem(index); if (item && !(item->isEquipped())) dialog->addItem(item, value); @@ -107,29 +106,29 @@ void BuySellHandler::handleMessage(Net::MessageIn &msg) } else { - localChatTab->chatLog(_("Nothing to sell."), BY_SERVER); + SERVER_NOTICE(_("Nothing to sell.")) } break; case SMSG_NPC_BUY_RESPONSE: if (msg.readInt8() == 0) { - localChatTab->chatLog(_("Thanks for buying."), BY_SERVER); + SERVER_NOTICE(_("Thanks for buying.")) } else { // Reset player money since buy dialog already assumed purchase // would go fine - mBuyDialog->setMoney(player_node->getMoney()); - localChatTab->chatLog(_("Unable to buy."), BY_SERVER); + mBuyDialog->setMoney(PlayerInfo::getAttribute(MONEY)); + SERVER_NOTICE(_("Unable to buy.")) } break; case SMSG_NPC_SELL_RESPONSE: if (msg.readInt8() == 0) - localChatTab->chatLog(_("Thanks for selling."), BY_SERVER); + SERVER_NOTICE(_("Thanks for selling.")) else - localChatTab->chatLog(_("Unable to sell."), BY_SERVER); + SERVER_NOTICE(_("Unable to sell.")) break; } diff --git a/src/net/tmwa/charserverhandler.cpp b/src/net/tmwa/charserverhandler.cpp index dc9b3108..1063ee39 100644 --- a/src/net/tmwa/charserverhandler.cpp +++ b/src/net/tmwa/charserverhandler.cpp @@ -86,12 +86,10 @@ void CharServerHandler::handleMessage(Net::MessageIn &msg) for (int i = 0; i < count; ++i) { Net::Character *character = new Net::Character; - int slot; - character->dummy = readPlayerData(msg, &slot); - character->slot = slot; + readPlayerData(msg, character); mCharacters.push_back(character); logger->log("CharServer: Player: %s (%d)", - character->dummy->getName().c_str(), slot); + character->dummy->getName().c_str(), character->slot); } Client::setState(STATE_CHAR_SELECT); @@ -118,9 +116,7 @@ void CharServerHandler::handleMessage(Net::MessageIn &msg) case SMSG_CHAR_CREATE_SUCCEEDED: { Net::Character *character = new Net::Character; - int slot; - character->dummy = readPlayerData(msg, &slot); - character->slot = slot; + readPlayerData(msg, character); mCharacters.push_back(character); updateCharSelectDialog(); @@ -165,6 +161,8 @@ void CharServerHandler::handleMessage(Net::MessageIn &msg) // Prevent the selected local player from being deleted player_node = mSelectedCharacter->dummy; + PlayerInfo::setBackend(mSelectedCharacter->data); + mSelectedCharacter->dummy = 0; delete_all(mCharacters); @@ -194,7 +192,7 @@ void CharServerHandler::handleMessage(Net::MessageIn &msg) } } -LocalPlayer *CharServerHandler::readPlayerData(Net::MessageIn &msg, int *slot) +void CharServerHandler::readPlayerData(Net::MessageIn &msg, Net::Character *character) { const Token &token = static_cast<LoginHandler*>(Net::getLoginHandler())->getToken(); @@ -202,30 +200,37 @@ LocalPlayer *CharServerHandler::readPlayerData(Net::MessageIn &msg, int *slot) LocalPlayer *tempPlayer = new LocalPlayer(msg.readInt32(), 0); tempPlayer->setGender(token.sex); - tempPlayer->setExp(msg.readInt32()); - tempPlayer->setMoney(msg.readInt32()); - tempPlayer->setExperience(JOB, msg.readInt32(), 1); + character->data.mAttributes[EXP] = msg.readInt32(); + character->data.mAttributes[MONEY] = msg.readInt32(); + character->data.mStats[JOB].exp = msg.readInt32(); + int temp = msg.readInt32(); - tempPlayer->setAttributeBase(JOB, temp, false); - tempPlayer->setAttributeEffective(JOB, temp); + character->data.mStats[JOB].base = temp; + character->data.mStats[JOB].mod = temp; + tempPlayer->setSprite(SPRITE_SHOE, msg.readInt16()); tempPlayer->setSprite(SPRITE_GLOVES, msg.readInt16()); tempPlayer->setSprite(SPRITE_CAPE, msg.readInt16()); tempPlayer->setSprite(SPRITE_MISC1, msg.readInt16()); + msg.readInt32(); // option msg.readInt32(); // karma msg.readInt32(); // manner msg.skip(2); // unknown - tempPlayer->setHp(msg.readInt16()); - tempPlayer->setMaxHp(msg.readInt16()); - tempPlayer->setMP(msg.readInt16()); - tempPlayer->setMaxMP(msg.readInt16()); + + character->data.mAttributes[HP] = msg.readInt16(); + character->data.mAttributes[MAX_HP] = msg.readInt16(); + character->data.mAttributes[MP] = msg.readInt16(); + character->data.mAttributes[MAX_MP] = msg.readInt16(); + msg.readInt16(); // speed tempPlayer->setSubtype(msg.readInt16()); // class (used for race) int hairStyle = msg.readInt16(); Uint16 weapon = msg.readInt16(); tempPlayer->setSprite(SPRITE_WEAPON, weapon, "", true); - tempPlayer->setLevel(msg.readInt16()); + + character->data.mAttributes[LEVEL] = msg.readInt16(); + msg.readInt16(); // skill point tempPlayer->setSprite(SPRITE_BOTTOMCLOTHES, msg.readInt16()); // head bottom tempPlayer->setSprite(SPRITE_SHIELD, msg.readInt16()); @@ -234,12 +239,14 @@ LocalPlayer *CharServerHandler::readPlayerData(Net::MessageIn &msg, int *slot) tempPlayer->setSprite(SPRITE_HAIR, hairStyle * -1, ColorDB::get(msg.readInt16())); tempPlayer->setSprite(SPRITE_MISC2, msg.readInt16()); tempPlayer->setName(msg.readString(24)); + + character->dummy = tempPlayer; + for (int i = 0; i < 6; i++) - tempPlayer->setAttributeBase(i + STR, msg.readInt8(), false); - *slot = msg.readInt8(); // character slot - msg.readInt8(); // unknown + character->data.mStats[i + STR].base = msg.readInt8(); - return tempPlayer; + character->slot = msg.readInt8(); // character slot + msg.readInt8(); // unknown } void CharServerHandler::setCharSelectDialog(CharSelectDialog *window) @@ -315,17 +322,17 @@ void CharServerHandler::switchCharacter() outMsg.writeInt8(1); } -int CharServerHandler::baseSprite() const +unsigned int CharServerHandler::baseSprite() const { return SPRITE_BASE; } -int CharServerHandler::hairSprite() const +unsigned int CharServerHandler::hairSprite() const { return SPRITE_HAIR; } -int CharServerHandler::maxSprite() const +unsigned int CharServerHandler::maxSprite() const { return SPRITE_VECTOREND; } diff --git a/src/net/tmwa/charserverhandler.h b/src/net/tmwa/charserverhandler.h index e80d22c4..2076cbae 100644 --- a/src/net/tmwa/charserverhandler.h +++ b/src/net/tmwa/charserverhandler.h @@ -63,16 +63,16 @@ class CharServerHandler : public MessageHandler, public Net::CharHandler void switchCharacter(); - int baseSprite() const; + unsigned int baseSprite() const; - int hairSprite() const; + unsigned int hairSprite() const; - int maxSprite() const; + unsigned int maxSprite() const; void connect(); private: - LocalPlayer *readPlayerData(Net::MessageIn &msg, int *slot); + void readPlayerData(Net::MessageIn &msg, Net::Character *character); }; } // namespace TmwAthena diff --git a/src/net/tmwa/chathandler.cpp b/src/net/tmwa/chathandler.cpp index 00d29662..33826762 100644 --- a/src/net/tmwa/chathandler.cpp +++ b/src/net/tmwa/chathandler.cpp @@ -21,14 +21,13 @@ #include "net/tmwa/chathandler.h" +#include "actorspritemanager.h" #include "being.h" -#include "beingmanager.h" +#include "event.h" #include "game.h" #include "localplayer.h" #include "playerrelations.h" -#include "gui/widgets/chattab.h" - #include "net/messagein.h" #include "net/messageout.h" @@ -60,8 +59,6 @@ ChatHandler::ChatHandler() void ChatHandler::handleMessage(Net::MessageIn &msg) { - if (!localChatTab) return; - Being *being; std::string chatMsg; std::string nick; @@ -70,19 +67,36 @@ void ChatHandler::handleMessage(Net::MessageIn &msg) switch (msg.getId()) { case SMSG_WHISPER_RESPONSE: + if (mSentWhispers.empty()) + nick = "user"; + else + { + nick = mSentWhispers.front(); + mSentWhispers.pop(); + } + switch (msg.readInt8()) { case 0x00: - // comment out since we'll local echo in chat.cpp instead, then only report failures - //localChatTab->chatLog("Whisper sent", BY_SERVER); + // Success (don't need to report) break; case 0x01: - localChatTab->chatLog(_("Whisper could not be sent, user " - "is offline."), BY_SERVER); + { + Mana::Event event(EVENT_WHISPERERROR); + event.setString("nick", nick); + event.setString("error", strprintf(_("Whisper could " + "not be sent, %s is offline."), nick.c_str())); + event.trigger(CHANNEL_CHAT); + } break; case 0x02: - localChatTab->chatLog(_("Whisper could not be sent, " - "ignored by user."), BY_SERVER); + { + Mana::Event event(EVENT_WHISPERERROR); + event.setString("nick", nick); + event.setString("error", strprintf(_("Whisper could " + "not be sent, ignored by %s."), nick.c_str())); + event.Event::trigger(CHANNEL_CHAT); + } break; } break; @@ -100,11 +114,16 @@ void ChatHandler::handleMessage(Net::MessageIn &msg) if (nick != "Server") { if (player_relations.hasPermission(nick, PlayerRelation::WHISPER)) - chatWindow->whisper(nick, chatMsg); + { + Mana::Event event(EVENT_WHISPER); + event.setString("nick", nick); + event.setString("message", chatMsg); + event.trigger(CHANNEL_CHAT); + } } else { - localChatTab->chatLog(chatMsg, BY_SERVER); + SERVER_NOTICE(chatMsg) } break; @@ -113,7 +132,8 @@ void ChatHandler::handleMessage(Net::MessageIn &msg) case SMSG_BEING_CHAT: { chatMsgLength = msg.readInt16() - 8; - being = beingManager->findBeing(msg.readInt32()); + int beingId = msg.readInt32(); + being = actorSpriteManager->findBeing(beingId); if (!being || chatMsgLength <= 0) break; @@ -135,23 +155,33 @@ void ChatHandler::handleMessage(Net::MessageIn &msg) chatMsg.erase(0, pos + 3); } - trim(chatMsg); + int perms; - // We use getIgnorePlayer instead of ignoringPlayer here - // because ignorePlayer' side effects are triggered - // right below for Being::IGNORE_SPEECH_FLOAT. - if (player_relations.checkPermissionSilently(sender_name, - PlayerRelation::SPEECH_LOG) && chatWindow) + if (being->getType() == Being::PLAYER) { - localChatTab->chatLog(removeColors(sender_name) + " : " - + chatMsg, BY_OTHER); + perms = player_relations.checkPermissionSilently(sender_name, + PlayerRelation::SPEECH_LOG | PlayerRelation::SPEECH_FLOAT); } - - if (player_relations.hasPermission(sender_name, - PlayerRelation::SPEECH_FLOAT)) + else { - being->setSpeech(chatMsg, SPEECH_TIME); + perms = player_relations.getDefault() + & (PlayerRelation::SPEECH_LOG + | PlayerRelation::SPEECH_FLOAT); } + + trim(chatMsg); + + std::string reducedMessage = chatMsg; + chatMsg = removeColors(sender_name) + " : " + reducedMessage; + + Mana::Event event(EVENT_BEING); + event.setString("message", chatMsg); + event.setString("text", reducedMessage); + event.setString("nick", sender_name); + event.setInt("beingId", beingId); + event.setInt("permissions", perms); + event.trigger(CHANNEL_CHAT); + break; } @@ -164,22 +194,32 @@ void ChatHandler::handleMessage(Net::MessageIn &msg) break; chatMsg = msg.readString(chatMsgLength); - std::string::size_type pos = chatMsg.find(" : ", 0); if (msg.getId() == SMSG_PLAYER_CHAT) { - localChatTab->chatLog(chatMsg, BY_PLAYER); + std::string::size_type pos = chatMsg.find(" : ", 0); + std::string mes = chatMsg; if (pos != std::string::npos) chatMsg.erase(0, pos + 3); trim(chatMsg); - player_node->setSpeech(chatMsg, SPEECH_TIME); + Mana::Event event(EVENT_PLAYER); + event.setString("message", mes); + event.setString("text", chatMsg); + event.setString("nick", player_node->getName()); + event.setInt("beingId", player_node->getId()); + event.setInt("permissions", player_relations.getDefault() + & (PlayerRelation::SPEECH_LOG + | PlayerRelation::SPEECH_FLOAT)); + event.trigger(CHANNEL_CHAT); } else { - localChatTab->chatLog(chatMsg, BY_GM); + Mana::Event event(EVENT_ANNOUNCEMENT); + event.setString("message", chatMsg); + event.trigger(CHANNEL_CHAT); } break; } @@ -187,7 +227,7 @@ void ChatHandler::handleMessage(Net::MessageIn &msg) case SMSG_MVP: // Display MVP player msg.readInt32(); // id - localChatTab->chatLog(_("MVP player."), BY_SERVER); + SERVER_NOTICE(_("MVP player.")) break; } } @@ -216,47 +256,49 @@ void ChatHandler::privateMessage(const std::string &recipient, outMsg.writeInt16(text.length() + 28); outMsg.writeString(recipient, 24); outMsg.writeString(text, text.length()); + + mSentWhispers.push(recipient); } void ChatHandler::channelList() { - localChatTab->chatLog(_("Channels are not supported!"), BY_SERVER); + SERVER_NOTICE(_("Channels are not supported!")) } void ChatHandler::enterChannel(const std::string &channel, const std::string &password) { - localChatTab->chatLog(_("Channels are not supported!"), BY_SERVER); + SERVER_NOTICE(_("Channels are not supported!")) } void ChatHandler::quitChannel(int channelId) { - localChatTab->chatLog(_("Channels are not supported!"), BY_SERVER); + SERVER_NOTICE(_("Channels are not supported!")) } void ChatHandler::sendToChannel(int channelId, const std::string &text) { - localChatTab->chatLog(_("Channels are not supported!"), BY_SERVER); + SERVER_NOTICE(_("Channels are not supported!")) } void ChatHandler::userList(const std::string &channel) { - localChatTab->chatLog(_("Channels are not supported!"), BY_SERVER); + SERVER_NOTICE(_("Channels are not supported!")) } void ChatHandler::setChannelTopic(int channelId, const std::string &text) { - localChatTab->chatLog(_("Channels are not supported!"), BY_SERVER); + SERVER_NOTICE(_("Channels are not supported!")) } void ChatHandler::setUserMode(int channelId, const std::string &name, int mode) { - localChatTab->chatLog(_("Channels are not supported!"), BY_SERVER); + SERVER_NOTICE(_("Channels are not supported!")) } void ChatHandler::kickUser(int channelId, const std::string &name) { - localChatTab->chatLog(_("Channels are not supported!"), BY_SERVER); + SERVER_NOTICE(_("Channels are not supported!")) } void ChatHandler::who() diff --git a/src/net/tmwa/chathandler.h b/src/net/tmwa/chathandler.h index 3e035f7e..6426a71e 100644 --- a/src/net/tmwa/chathandler.h +++ b/src/net/tmwa/chathandler.h @@ -27,6 +27,8 @@ #include "net/tmwa/messagehandler.h" +#include <queue> + namespace TmwAthena { class ChatHandler : public MessageHandler, public Net::ChatHandler @@ -61,6 +63,10 @@ class ChatHandler : public MessageHandler, public Net::ChatHandler void kickUser(int channelId, const std::string &name); void who(); + + private: + typedef std::queue<std::string> WhisperQueue; + WhisperQueue mSentWhispers; }; } // namespace TmwAthena diff --git a/src/net/tmwa/gamehandler.cpp b/src/net/tmwa/gamehandler.cpp index 435d5d30..63f5fcec 100644 --- a/src/net/tmwa/gamehandler.cpp +++ b/src/net/tmwa/gamehandler.cpp @@ -22,12 +22,11 @@ #include "net/tmwa/gamehandler.h" #include "client.h" +#include "event.h" #include "game.h" #include "localplayer.h" #include "log.h" -#include "gui/widgets/chattab.h" - #include "gui/okdialog.h" #include "net/messagein.h" @@ -58,6 +57,8 @@ GameHandler::GameHandler() }; handledMessages = _messages; gameHandler = this; + + listen(CHANNEL_GAME); } void GameHandler::handleMessage(Net::MessageIn &msg) @@ -84,8 +85,7 @@ void GameHandler::handleMessage(Net::MessageIn &msg) break; case SMSG_WHO_ANSWER: - localChatTab->chatLog(strprintf(_("Online users: %d"), - msg.readInt32()), BY_SERVER); + SERVER_NOTICE(strprintf(_("Online users: %d"), msg.readInt32())) break; case SMSG_CHAR_SWITCH_RESPONSE: @@ -105,6 +105,21 @@ void GameHandler::handleMessage(Net::MessageIn &msg) } } +void GameHandler::event(Channels channel, const Mana::Event &event) +{ + if (channel == CHANNEL_GAME) + { + if (event.getName() == EVENT_ENGINESINITALIZED) + { + Game::instance()->changeMap(mMap); + } + else if (event.getName() == EVENT_MAPLOADED) + { + MessageOut outMsg(CMSG_MAP_LOADED); + } + } +} + void GameHandler::connect() { mNetwork->connect(mapServer); @@ -142,16 +157,6 @@ void GameHandler::disconnect() mNetwork->disconnect(); } -void GameHandler::inGame() -{ - Game::instance()->changeMap(mMap); -} - -void GameHandler::mapLoaded(const std::string &mapName) -{ - MessageOut outMsg(CMSG_MAP_LOADED); -} - void GameHandler::who() { } diff --git a/src/net/tmwa/gamehandler.h b/src/net/tmwa/gamehandler.h index ca8d27e6..18317445 100644 --- a/src/net/tmwa/gamehandler.h +++ b/src/net/tmwa/gamehandler.h @@ -22,6 +22,8 @@ #ifndef NET_TA_MAPHANDLER_H #define NET_TA_MAPHANDLER_H +#include "listener.h" + #include "net/gamehandler.h" #include "net/net.h" #include "net/serverinfo.h" @@ -31,23 +33,22 @@ namespace TmwAthena { -class GameHandler : public MessageHandler, public Net::GameHandler +class GameHandler : public MessageHandler, public Net::GameHandler, + public Mana::Listener { public: GameHandler(); void handleMessage(Net::MessageIn &msg); + void event(Channels channel, const Mana::Event &event); + void connect(); bool isConnected(); void disconnect(); - void inGame(); - - void mapLoaded(const std::string &mapName); - void who(); void quit(); @@ -60,6 +61,9 @@ class GameHandler : public MessageHandler, public Net::GameHandler void setMap(const std::string map); + /** The tmwAthena protocol is making use of the MP status bar. */ + bool canUseMagicBar() const { return true; } + private: std::string mMap; int mCharID; /// < Saved for map-server switching diff --git a/src/net/tmwa/generalhandler.cpp b/src/net/tmwa/generalhandler.cpp index 14f48055..1935ad72 100644 --- a/src/net/tmwa/generalhandler.cpp +++ b/src/net/tmwa/generalhandler.cpp @@ -32,6 +32,10 @@ #include "gui/socialwindow.h" #include "gui/statuswindow.h" +#include "net/messagein.h" +#include "net/messageout.h" +#include "net/serverinfo.h" + #include "net/tmwa/adminhandler.h" #include "net/tmwa/beinghandler.h" #include "net/tmwa/buysellhandler.h" @@ -53,9 +57,6 @@ #include "net/tmwa/gui/guildtab.h" #include "net/tmwa/gui/partytab.h" -#include "net/messagein.h" -#include "net/messageout.h" - #include "resources/itemdb.h" #include "utils/gettext.h" @@ -75,7 +76,7 @@ extern Party *taParty; GeneralHandler::GeneralHandler(): mAdminHandler(new AdminHandler), - mBeingHandler(new BeingHandler(config.getValue("EnableSync", 0) == 1)), + mBeingHandler(new BeingHandler(config.getBoolValue("EnableSync"))), mBuySellHandler(new BuySellHandler), mCharHandler(new CharServerHandler), mChatHandler(new ChatHandler), @@ -97,15 +98,17 @@ GeneralHandler::GeneralHandler(): handledMessages = _messages; generalHandler = this; - std::list<ItemDB::Stat> stats; - stats.push_back(ItemDB::Stat("str", _("Strength %+d"))); - stats.push_back(ItemDB::Stat("agi", _("Agility %+d"))); - stats.push_back(ItemDB::Stat("vit", _("Vitality %+d"))); - stats.push_back(ItemDB::Stat("int", _("Intelligence %+d"))); - stats.push_back(ItemDB::Stat("dex", _("Dexterity %+d"))); - stats.push_back(ItemDB::Stat("luck", _("Luck %+d"))); + std::list<ItemStat> stats; + stats.push_back(ItemStat("str", _("Strength %+d"))); + stats.push_back(ItemStat("agi", _("Agility %+d"))); + stats.push_back(ItemStat("vit", _("Vitality %+d"))); + stats.push_back(ItemStat("int", _("Intelligence %+d"))); + stats.push_back(ItemStat("dex", _("Dexterity %+d"))); + stats.push_back(ItemStat("luck", _("Luck %+d"))); + + setStatsList(stats); - ItemDB::setStatsList(stats); + listen(CHANNEL_GAME); } GeneralHandler::~GeneralHandler() @@ -209,47 +212,48 @@ void GeneralHandler::flushNetwork() } } -void GeneralHandler::guiWindowsLoaded() -{ - inventoryWindow->setSplitAllowed(false); - skillDialog->loadSkills("ea-skills.xml"); - - statusWindow->addAttribute(STR, _("Strength"), true, ""); - statusWindow->addAttribute(AGI, _("Agility"), true, ""); - statusWindow->addAttribute(VIT, _("Vitality"), true, ""); - statusWindow->addAttribute(INT, _("Intelligence"), true, ""); - statusWindow->addAttribute(DEX, _("Dexterity"), true, ""); - statusWindow->addAttribute(LUK, _("Luck"), true, ""); - - statusWindow->addAttribute(ATK, _("Attack"), false, ""); - statusWindow->addAttribute(DEF, _("Defense"), false, ""); - statusWindow->addAttribute(MATK, _("M.Attack"), false, ""); - statusWindow->addAttribute(MDEF, _("M.Defense"), false, ""); - statusWindow->addAttribute(HIT, _("% Accuracy"), false, ""); - statusWindow->addAttribute(FLEE, _("% Evade"), false, ""); - statusWindow->addAttribute(CRIT, _("% Critical"), false, ""); -} - -void GeneralHandler::guiWindowsUnloaded() -{ - socialWindow->removeTab(taGuild); - socialWindow->removeTab(taParty); - - delete guildTab; - guildTab = 0; - - delete partyTab; - partyTab = 0; -} - void GeneralHandler::clearHandlers() { mNetwork->clearHandlers(); } -void GeneralHandler::stateChanged(State oldState, State newState) +void GeneralHandler::event(Channels channel, + const Mana::Event &event) { - // + if (channel == CHANNEL_GAME) + { + if (event.getName() == EVENT_GUIWINDOWSLOADED) + { + inventoryWindow->setSplitAllowed(false); + skillDialog->loadSkills("ea-skills.xml"); + + statusWindow->addAttribute(STR, _("Strength"), true, ""); + statusWindow->addAttribute(AGI, _("Agility"), true, ""); + statusWindow->addAttribute(VIT, _("Vitality"), true, ""); + statusWindow->addAttribute(INT, _("Intelligence"), true, ""); + statusWindow->addAttribute(DEX, _("Dexterity"), true, ""); + statusWindow->addAttribute(LUK, _("Luck"), true, ""); + + statusWindow->addAttribute(ATK, _("Attack"), false, ""); + statusWindow->addAttribute(DEF, _("Defense"), false, ""); + statusWindow->addAttribute(MATK, _("M.Attack"), false, ""); + statusWindow->addAttribute(MDEF, _("M.Defense"), false, ""); + statusWindow->addAttribute(HIT, _("% Accuracy"), false, ""); + statusWindow->addAttribute(FLEE, _("% Evade"), false, ""); + statusWindow->addAttribute(CRIT, _("% Critical"), false, ""); + } + else if (event.getName() == EVENT_GUIWINDOWSUNLOADING) + { + socialWindow->removeTab(taGuild); + socialWindow->removeTab(taParty); + + delete guildTab; + guildTab = 0; + + delete partyTab; + partyTab = 0; + } + } } } // namespace TmwAthena diff --git a/src/net/tmwa/generalhandler.h b/src/net/tmwa/generalhandler.h index d680f215..722c3215 100644 --- a/src/net/tmwa/generalhandler.h +++ b/src/net/tmwa/generalhandler.h @@ -22,15 +22,17 @@ #ifndef NET_TMWA_GENERALHANDLER_H #define NET_TMWA_GENERALHANDLER_H +#include "listener.h" + #include "net/generalhandler.h" #include "net/net.h" -#include "net/serverinfo.h" #include "net/tmwa/messagehandler.h" namespace TmwAthena { -class GeneralHandler : public MessageHandler, public Net::GeneralHandler +class GeneralHandler : public MessageHandler, public Net::GeneralHandler, + public Mana::Listener { public: GeneralHandler(); @@ -47,13 +49,9 @@ class GeneralHandler : public MessageHandler, public Net::GeneralHandler void flushNetwork(); - void guiWindowsLoaded(); - - void guiWindowsUnloaded(); - void clearHandlers(); - void stateChanged(State oldState, State newState); + void event(Channels channel, const Mana::Event &event); protected: MessageHandlerPtr mAdminHandler; diff --git a/src/net/tmwa/gui/guildtab.cpp b/src/net/tmwa/gui/guildtab.cpp index 794ad5cc..ca922e55 100644 --- a/src/net/tmwa/gui/guildtab.cpp +++ b/src/net/tmwa/gui/guildtab.cpp @@ -21,17 +21,17 @@ #include "net/tmwa/gui/guildtab.h" +#include "chatlog.h" #include "commandhandler.h" #include "guild.h" #include "localplayer.h" -#include "gui/theme.h" - #include "net/net.h" #include "net/guildhandler.h" #include "resources/iteminfo.h" #include "resources/itemdb.h" +#include "resources/theme.h" #include "utils/dtor.h" #include "utils/gettext.h" @@ -114,4 +114,10 @@ void GuildTab::getAutoCompleteList(std::vector<std::string> &names) const taGuild->getNames(names); } +void GuildTab::saveToLogFile(std::string &msg) +{ + if (chatLogger) + chatLogger->log("#Guild", msg); +} + } // namespace TmwAthena diff --git a/src/net/tmwa/gui/guildtab.h b/src/net/tmwa/gui/guildtab.h index 031c81bf..12e15e16 100644 --- a/src/net/tmwa/gui/guildtab.h +++ b/src/net/tmwa/gui/guildtab.h @@ -39,6 +39,8 @@ class GuildTab : public ChatTab bool handleCommand(const std::string &type, const std::string &args); + void saveToLogFile(std::string &msg); + protected: void handleInput(const std::string &msg); diff --git a/src/net/tmwa/gui/partytab.cpp b/src/net/tmwa/gui/partytab.cpp index b541c498..6833831c 100644 --- a/src/net/tmwa/gui/partytab.cpp +++ b/src/net/tmwa/gui/partytab.cpp @@ -21,17 +21,17 @@ #include "net/tmwa/gui/partytab.h" +#include "chatlog.h" #include "commandhandler.h" #include "localplayer.h" #include "party.h" -#include "gui/theme.h" - #include "net/net.h" #include "net/partyhandler.h" #include "resources/iteminfo.h" #include "resources/itemdb.h" +#include "resources/theme.h" #include "utils/dtor.h" #include "utils/gettext.h" @@ -206,4 +206,10 @@ void PartyTab::getAutoCompleteList(std::vector<std::string> &names) const p->getNames(names); } +void PartyTab::saveToLogFile(std::string &msg) +{ + if (chatLogger) + chatLogger->log("#Party", msg); +} + } // namespace TmwAthena diff --git a/src/net/tmwa/gui/partytab.h b/src/net/tmwa/gui/partytab.h index 62027726..4c16ab46 100644 --- a/src/net/tmwa/gui/partytab.h +++ b/src/net/tmwa/gui/partytab.h @@ -39,6 +39,8 @@ class PartyTab : public ChatTab bool handleCommand(const std::string &type, const std::string &args); + void saveToLogFile(std::string &msg); + protected: void handleInput(const std::string &msg); diff --git a/src/net/tmwa/guildhandler.cpp b/src/net/tmwa/guildhandler.cpp index 93bc7807..1ff2f22a 100644 --- a/src/net/tmwa/guildhandler.cpp +++ b/src/net/tmwa/guildhandler.cpp @@ -21,6 +21,7 @@ #include "net/tmwa/guildhandler.h" #include "guild.h" +#include "event.h" #include "localplayer.h" #include "log.h" @@ -389,8 +390,7 @@ void GuildHandler::handleMessage(Net::MessageIn &msg) void GuildHandler::create(const std::string &name) { - localChatTab->chatLog(_("Guild creation isn't supported yet."), - BY_SERVER); + SERVER_NOTICE(_("Guild creation isn't supported yet.")) return; MessageOut msg(CMSG_GUILD_CREATE); @@ -403,10 +403,10 @@ void GuildHandler::invite(int guildId, const std::string &name) // TODO? } -void GuildHandler::invite(int guildId, Player *player) +void GuildHandler::invite(int guildId, Being *being) { MessageOut msg(CMSG_GUILD_INVITE); - msg.writeInt32(player->getId()); + msg.writeInt32(being->getId()); msg.writeInt32(0); // Unused msg.writeInt32(0); // Unused } diff --git a/src/net/tmwa/guildhandler.h b/src/net/tmwa/guildhandler.h index 39dbe486..8bde222f 100644 --- a/src/net/tmwa/guildhandler.h +++ b/src/net/tmwa/guildhandler.h @@ -40,7 +40,7 @@ class GuildHandler : public Net::GuildHandler, public MessageHandler void invite(int guildId, const std::string &name); - void invite(int guildId, Player *player); + void invite(int guildId, Being *being); void inviteResponse(int guildId, bool response); diff --git a/src/net/tmwa/inventoryhandler.cpp b/src/net/tmwa/inventoryhandler.cpp index 3809399d..4aedd9f2 100644 --- a/src/net/tmwa/inventoryhandler.cpp +++ b/src/net/tmwa/inventoryhandler.cpp @@ -23,6 +23,7 @@ #include "configuration.h" #include "equipment.h" +#include "event.h" #include "inventory.h" #include "item.h" #include "itemshortcut.h" @@ -45,30 +46,30 @@ extern Net::InventoryHandler *inventoryHandler; -const Equipment::Slot EQUIP_POINTS[Equipment::EQUIP_VECTOREND] = { - Equipment::EQUIP_LEGS_SLOT, - Equipment::EQUIP_FIGHT1_SLOT, - Equipment::EQUIP_GLOVES_SLOT, - Equipment::EQUIP_RING2_SLOT, - Equipment::EQUIP_RING1_SLOT, - Equipment::EQUIP_FIGHT2_SLOT, - Equipment::EQUIP_FEET_SLOT, - Equipment::EQUIP_NECK_SLOT, - Equipment::EQUIP_HEAD_SLOT, - Equipment::EQUIP_TORSO_SLOT, - Equipment::EQUIP_PROJECTILE_SLOT}; - namespace TmwAthena { +const EquipmentSlot EQUIP_POINTS[EQUIP_VECTOR_END] = { + EQUIP_LEGS_SLOT, + EQUIP_FIGHT1_SLOT, + EQUIP_ARMS_SLOT, + EQUIP_RING2_SLOT, + EQUIP_RING1_SLOT, + EQUIP_FIGHT2_SLOT, + EQUIP_FEET_SLOT, + EQUIP_NECKLACE_SLOT, + EQUIP_HEAD_SLOT, + EQUIP_TORSO_SLOT, + EQUIP_PROJECTILE_SLOT}; + int getSlot(int eAthenaSlot) { if (eAthenaSlot == 0) { - return Equipment::EQUIP_VECTOREND; + return EQUIP_VECTOR_END; } if (eAthenaSlot & 0x8000) - return Equipment::EQUIP_PROJECTILE_SLOT; + return EQUIP_PROJECTILE_SLOT; int mask = 1; int position = 0; @@ -108,6 +109,8 @@ InventoryHandler::InventoryHandler() mStorage = 0; mStorageWindow = 0; + + listen(CHANNEL_ITEM); } InventoryHandler::~InventoryHandler() @@ -126,8 +129,8 @@ void InventoryHandler::handleMessage(Net::MessageIn &msg) int number, flag; int index, amount, itemId, equipType, arrow; int identified, cards[4], itemType; - Inventory *inventory = player_node->getInventory(); - player_node->mEquipment->setBackend(&mEquips); + Inventory *inventory = PlayerInfo::getInventory(); + PlayerInfo::getEquipment()->setBackend(&mEquips); switch (msg.getId()) { @@ -170,17 +173,10 @@ void InventoryHandler::handleMessage(Net::MessageIn &msg) } if (msg.getId() == SMSG_PLAYER_INVENTORY) - { - // Trick because arrows are not considered equipment - bool isEquipment = arrow & 0x8000; - - inventory->setItem(index, itemId, amount, isEquipment); - } + inventory->setItem(index, itemId, amount); else - { mInventoryItems.push_back(InventoryItem(index, itemId, amount, false)); - } } break; @@ -224,11 +220,11 @@ void InventoryHandler::handleMessage(Net::MessageIn &msg) msg.readInt8(); // refine for (int i = 0; i < 4; i++) cards[i] = msg.readInt16(); - equipType = msg.readInt16(); + msg.readInt16(); // EquipType itemType = msg.readInt8(); { - const ItemInfo &itemInfo = ItemDB::get(itemId); + const ItemInfo &itemInfo = itemDb->get(itemId); if (msg.readInt8() > 0) { @@ -243,7 +239,7 @@ void InventoryHandler::handleMessage(Net::MessageIn &msg) if (item && item->getId() == itemId) amount += inventory->getItem(index)->getQuantity(); - inventory->setItem(index, itemId, amount, equipType != 0); + inventory->setItem(index, itemId, amount); } inventoryWindow->updateButtons(); @@ -286,7 +282,7 @@ void InventoryHandler::handleMessage(Net::MessageIn &msg) if (msg.readInt8() == 0) { - localChatTab->chatLog(_("Failed to use item."), BY_SERVER); + SERVER_NOTICE(_("Failed to use item.")) } else { @@ -318,8 +314,7 @@ void InventoryHandler::handleMessage(Net::MessageIn &msg) InventoryItems::iterator it = mInventoryItems.begin(); InventoryItems::iterator it_end = mInventoryItems.end(); for (; it != it_end; it++) - mStorage->setItem((*it).slot, (*it).id, (*it).quantity, - (*it).equip); + mStorage->setItem((*it).slot, (*it).id, (*it).quantity); mInventoryItems.clear(); if (!mStorageWindow) @@ -344,9 +339,7 @@ void InventoryHandler::handleMessage(Net::MessageIn &msg) item->increaseQuantity(amount); } else - { - mStorage->setItem(index, itemId, amount, false); - } + mStorage->setItem(index, itemId, amount); break; case SMSG_PLAYER_STORAGE_REMOVE: @@ -388,7 +381,7 @@ void InventoryHandler::handleMessage(Net::MessageIn &msg) msg.readInt8(); // refine msg.skip(8); // card - inventory->setItem(index, itemId, 1, true); + inventory->setItem(index, itemId, 1); if (equipType) { @@ -403,7 +396,7 @@ void InventoryHandler::handleMessage(Net::MessageIn &msg) flag = msg.readInt8(); if (!flag) - localChatTab->chatLog(_("Unable to equip."), BY_SERVER); + SERVER_NOTICE(_("Unable to equip.")) else mEquips.setEquipment(getSlot(equipType), index); break; @@ -414,7 +407,7 @@ void InventoryHandler::handleMessage(Net::MessageIn &msg) flag = msg.readInt8(); if (!flag) - localChatTab->chatLog(_("Unable to unequip."), BY_SERVER); + SERVER_NOTICE(_("Unable to unequip.")) else mEquips.setEquipment(getSlot(equipType), -1); break; @@ -432,46 +425,90 @@ void InventoryHandler::handleMessage(Net::MessageIn &msg) index -= INVENTORY_OFFSET; logger->log("Arrows equipped: %i", index); - mEquips.setEquipment(Equipment::EQUIP_PROJECTILE_SLOT, index); + mEquips.setEquipment(EQUIP_PROJECTILE_SLOT, index); break; } } -void InventoryHandler::equipItem(const Item *item) +void InventoryHandler::event(Channels channel, + const Mana::Event &event) { - if (!item) - return; + if (channel == CHANNEL_ITEM) + { + if (event.getName() == EVENT_DOCLOSEINVENTORY) + { + // No need to worry about type + MessageOut outMsg(CMSG_CLOSE_STORAGE); + } + else + { + Item *item = event.getItem("item"); - MessageOut outMsg(CMSG_PLAYER_EQUIP); - outMsg.writeInt16(item->getInvIndex() + INVENTORY_OFFSET); - outMsg.writeInt16(0); -} + if (!item) + return; -void InventoryHandler::unequipItem(const Item *item) -{ - if (!item) - return; + int index = item->getInvIndex() + INVENTORY_OFFSET; - MessageOut outMsg(CMSG_PLAYER_UNEQUIP); - outMsg.writeInt16(item->getInvIndex() + INVENTORY_OFFSET); -} - -void InventoryHandler::useItem(const Item *item) -{ - if (!item) - return; + if (event.getName() == EVENT_DOEQUIP) + { + MessageOut outMsg(CMSG_PLAYER_EQUIP); + outMsg.writeInt16(index); + outMsg.writeInt16(0); + } + else if (event.getName() == EVENT_DOUNEQUIP) + { + MessageOut outMsg(CMSG_PLAYER_UNEQUIP); + outMsg.writeInt16(index); + } + else if (event.getName() == EVENT_DOUSE) + { + MessageOut outMsg(CMSG_PLAYER_INVENTORY_USE); + outMsg.writeInt16(index); + outMsg.writeInt32(item->getId()); // unused + } + else if (event.getName() == EVENT_DODROP) + { + int amount = event.getInt("amount", 1); - MessageOut outMsg(CMSG_PLAYER_INVENTORY_USE); - outMsg.writeInt16(item->getInvIndex() + INVENTORY_OFFSET); - outMsg.writeInt32(item->getId()); // unused -} + // TODO: Fix wrong coordinates of drops, serverside? + // (what's wrong here?) + MessageOut outMsg(CMSG_PLAYER_INVENTORY_DROP); + outMsg.writeInt16(index); + outMsg.writeInt16(amount); + } + else if (event.getName() == EVENT_DOMOVE) + { + int newIndex = event.getInt("newIndex", -1); -void InventoryHandler::dropItem(const Item *item, int amount) -{ - // TODO: Fix wrong coordinates of drops, serverside? (what's wrong here?) - MessageOut outMsg(CMSG_PLAYER_INVENTORY_DROP); - outMsg.writeInt16(item->getInvIndex() + INVENTORY_OFFSET); - outMsg.writeInt16(amount); + if (newIndex >= 0) + { + // Not implemented for tmwAthena (possible?) + } + else + { + int source = event.getInt("source"); + int destination = event.getInt("destination"); + int amount = event.getInt("amount", 1); + + if (source == Inventory::INVENTORY + && destination == Inventory::STORAGE) + { + MessageOut outMsg(CMSG_MOVE_TO_STORAGE); + outMsg.writeInt16(index); + outMsg.writeInt32(amount); + } + else if (source == Inventory::STORAGE + && destination == Inventory::INVENTORY) + { + MessageOut outMsg(CSMG_MOVE_FROM_STORAGE); + outMsg.writeInt16(index - INVENTORY_OFFSET + + STORAGE_OFFSET); + outMsg.writeInt32(amount); + } + } + } + } + } } bool InventoryHandler::canSplit(const Item *item) @@ -479,43 +516,6 @@ bool InventoryHandler::canSplit(const Item *item) return false; } -void InventoryHandler::splitItem(const Item *item, int amount) -{ - // Not implemented for eAthena (possible?) -} - -void InventoryHandler::moveItem(int oldIndex, int newIndex) -{ - // Not implemented for eAthena (possible?) -} - -void InventoryHandler::openStorage(int type) -{ - // Doesn't apply to eAthena, since opening happens through NPCs? -} - -void InventoryHandler::closeStorage(int type) -{ - MessageOut outMsg(CMSG_CLOSE_STORAGE); -} - -void InventoryHandler::moveItem(int source, int slot, int amount, - int destination) -{ - if (source == Inventory::INVENTORY && destination == Inventory::STORAGE) - { - MessageOut outMsg(CMSG_MOVE_TO_STORAGE); - outMsg.writeInt16(slot + INVENTORY_OFFSET); - outMsg.writeInt32(amount); - } - else if (source == Inventory::STORAGE && destination == Inventory::INVENTORY) - { - MessageOut outMsg(CSMG_MOVE_FROM_STORAGE); - outMsg.writeInt16(slot + STORAGE_OFFSET); - outMsg.writeInt32(amount); - } -} - size_t InventoryHandler::getSize(int type) const { switch (type) diff --git a/src/net/tmwa/inventoryhandler.h b/src/net/tmwa/inventoryhandler.h index 0c4ad092..dfbefaa8 100644 --- a/src/net/tmwa/inventoryhandler.h +++ b/src/net/tmwa/inventoryhandler.h @@ -24,7 +24,8 @@ #include "equipment.h" #include "inventory.h" -#include "localplayer.h" +#include "listener.h" +#include "playerinfo.h" #include "gui/inventorywindow.h" @@ -51,7 +52,7 @@ class EquipBackend : public Equipment::Backend { { return NULL; } - return player_node->getInventory()->getItem(invyIndex); + return PlayerInfo::getInventory()->getItem(invyIndex); } void clear() @@ -60,7 +61,7 @@ class EquipBackend : public Equipment::Backend { { if (mEquipment[i] != -1) { - Item* item = player_node->getInventory()->getItem(i); + Item* item = PlayerInfo::getInventory()->getItem(i); if (item) { item->setEquipped(false); @@ -74,7 +75,7 @@ class EquipBackend : public Equipment::Backend { void setEquipment(int index, int inventoryIndex) { // Unequip existing item - Item* item = player_node->getInventory()->getItem(mEquipment[index]); + Item* item = PlayerInfo::getInventory()->getItem(mEquipment[index]); if (item) { item->setEquipped(false); @@ -82,7 +83,7 @@ class EquipBackend : public Equipment::Backend { mEquipment[index] = inventoryIndex; - item = player_node->getInventory()->getItem(inventoryIndex); + item = PlayerInfo::getInventory()->getItem(inventoryIndex); if (item) { item->setEquipped(true); @@ -117,7 +118,8 @@ class InventoryItem typedef std::list<InventoryItem> InventoryItems; -class InventoryHandler : public MessageHandler, public Net::InventoryHandler +class InventoryHandler : public MessageHandler, public Net::InventoryHandler, + public Mana::Listener { public: enum { @@ -131,27 +133,10 @@ class InventoryHandler : public MessageHandler, public Net::InventoryHandler void handleMessage(Net::MessageIn &msg); - void equipItem(const Item *item); - - void unequipItem(const Item *item); - - void useItem(const Item *item); - - void dropItem(const Item *item, int amount); + void event(Channels channel, const Mana::Event &event); bool canSplit(const Item *item); - void splitItem(const Item *item, int amount); - - void moveItem(int oldIndex, int newIndex); - - void openStorage(int type); - - void closeStorage(int type); - - void moveItem(int source, int slot, int amount, - int destination); - size_t getSize(int type) const; private: diff --git a/src/net/tmwa/itemhandler.cpp b/src/net/tmwa/itemhandler.cpp index abc8103b..a8e98860 100644 --- a/src/net/tmwa/itemhandler.cpp +++ b/src/net/tmwa/itemhandler.cpp @@ -21,7 +21,7 @@ #include "net/tmwa/itemhandler.h" -#include "flooritemmanager.h" +#include "actorspritemanager.h" #include "net/messagein.h" @@ -54,13 +54,13 @@ void ItemHandler::handleMessage(Net::MessageIn &msg) int y = msg.readInt16(); msg.skip(4); // amount,subX,subY / subX,subY,amount - floorItemManager->create(id, itemId, x, y); + actorSpriteManager->createItem(id, itemId, x, y); } break; case SMSG_ITEM_REMOVE: - if (FloorItem *item = floorItemManager->findById(msg.readInt32())) - floorItemManager->destroy(item); + if (FloorItem *item = actorSpriteManager->findItem(msg.readInt32())) + actorSpriteManager->destroy(item); break; } } diff --git a/src/net/tmwa/loginhandler.cpp b/src/net/tmwa/loginhandler.cpp index e58acb4d..00b7b145 100644 --- a/src/net/tmwa/loginhandler.cpp +++ b/src/net/tmwa/loginhandler.cpp @@ -235,6 +235,12 @@ void LoginHandler::getRegistrationDetails() void LoginHandler::loginAccount(LoginData *loginData) { + // Since we're attempting to use the tAthena protocol, + // let's reset the character slots to the good value, + // in case we just logged out a Manaserv server + // with a different config. + loginData->resetCharacterSlots(); + sendLoginRegister(loginData->username, loginData->password); } diff --git a/src/net/tmwa/npchandler.cpp b/src/net/tmwa/npchandler.cpp index 5888c679..337226a9 100644 --- a/src/net/tmwa/npchandler.cpp +++ b/src/net/tmwa/npchandler.cpp @@ -21,11 +21,9 @@ #include "net/tmwa/npchandler.h" -#include "beingmanager.h" +#include "actorspritemanager.h" +#include "event.h" #include "localplayer.h" -#include "npc.h" - -#include "gui/npcdialog.h" #include "net/messagein.h" #include "net/messageout.h" @@ -34,10 +32,27 @@ #include "net/tmwa/protocol.h" +#include "utils/stringutils.h" + #include <SDL_types.h> extern Net::NpcHandler *npcHandler; +static void parseMenu(Mana::Event *event, const std::string &options) +{ + std::istringstream iss(options); + + int count = 0; + std::string tmp; + while (getline(iss, tmp, ':')) + { + count++; + event->setString("choice" + toString(count), tmp); + } + + event->setInt("choiceCount", count); +} + namespace TmwAthena { NpcHandler::NpcHandler() @@ -63,82 +78,118 @@ void NpcHandler::handleMessage(Net::MessageIn &msg) } int npcId = msg.readInt32(); - NpcDialogs::iterator diag = mNpcDialogs.find(npcId); - NpcDialog *dialog = 0; - - if (diag == mNpcDialogs.end()) - { - // Empty dialogs don't help - if (msg.getId() == SMSG_NPC_CLOSE) - { - closeDialog(npcId); - return; - } - else if (msg.getId() == SMSG_NPC_NEXT) - { - nextDialog(npcId); - return; - } - else - { - dialog = new NpcDialog(npcId); - Wrapper wrap; - wrap.dialog = dialog; - mNpcDialogs[npcId] = wrap; - } - } - else - { - dialog = diag->second.dialog; - } + Mana::Event *event = 0; switch (msg.getId()) { - case SMSG_NPC_CHOICE: - dialog->choiceRequest(); - dialog->parseListItems(msg.readString(msg.getLength() - 8)); - break; - - case SMSG_NPC_MESSAGE: - dialog->addText(msg.readString(msg.getLength() - 8)); - break; - - case SMSG_NPC_CLOSE: - // Show the close button - dialog->showCloseButton(); - break; - - case SMSG_NPC_NEXT: - // Show the next button - dialog->showNextButton(); - break; - - case SMSG_NPC_INT_INPUT: - // Request for an integer - dialog->integerRequest(0); - break; - - case SMSG_NPC_STR_INPUT: - // Request for a string - dialog->textRequest(""); - break; + case SMSG_NPC_CHOICE: + event = new Mana::Event(EVENT_MENU); + event->setInt("id", npcId); + parseMenu(event, msg.readString(msg.getLength() - 8)); + event->trigger(CHANNEL_NPC); + break; + + case SMSG_NPC_MESSAGE: + event = new Mana::Event(EVENT_MESSAGE); + event->setInt("id", npcId); + event->setString("text", msg.readString(msg.getLength() - 8)); + event->trigger(CHANNEL_NPC); + break; + + case SMSG_NPC_CLOSE: + // Show the close button + event = new Mana::Event(EVENT_CLOSE); + event->setInt("id", npcId); + event->trigger(CHANNEL_NPC); + break; + + case SMSG_NPC_NEXT: + // Show the next button + event = new Mana::Event(EVENT_NEXT); + event->setInt("id", npcId); + event->trigger(CHANNEL_NPC); + break; + + case SMSG_NPC_INT_INPUT: + // Request for an integer + event = new Mana::Event(EVENT_INTEGERINPUT); + event->setInt("id", npcId); + event->trigger(CHANNEL_NPC); + break; + + case SMSG_NPC_STR_INPUT: + // Request for a string + event = new Mana::Event(EVENT_STRINGINPUT); + event->setInt("id", npcId); + event->trigger(CHANNEL_NPC); + break; } + delete event; + if (player_node->getCurrentAction() != Being::SIT) player_node->setAction(Being::STAND); } +void NpcHandler::startShopping(int beingId) +{ + // TODO +} + +void NpcHandler::buy(int beingId) +{ + MessageOut outMsg(CMSG_NPC_BUY_SELL_REQUEST); + outMsg.writeInt32(beingId); + outMsg.writeInt8(0); // Buy +} + +void NpcHandler::sell(int beingId) +{ + MessageOut outMsg(CMSG_NPC_BUY_SELL_REQUEST); + outMsg.writeInt32(beingId); + outMsg.writeInt8(1); // Sell +} + +void NpcHandler::buyItem(int beingId, int itemId, int amount) +{ + MessageOut outMsg(CMSG_NPC_BUY_REQUEST); + outMsg.writeInt16(8); // One item (length of packet) + outMsg.writeInt16(amount); + outMsg.writeInt16(itemId); +} + +void NpcHandler::sellItem(int beingId, int itemId, int amount) +{ + MessageOut outMsg(CMSG_NPC_SELL_REQUEST); + outMsg.writeInt16(8); // One item (length of packet) + outMsg.writeInt16(itemId + INVENTORY_OFFSET); + outMsg.writeInt16(amount); +} + +void NpcHandler::endShopping(int beingId) +{ + // TODO +} + void NpcHandler::talk(int npcId) { MessageOut outMsg(CMSG_NPC_TALK); outMsg.writeInt32(npcId); outMsg.writeInt8(0); // Unused + + Mana::Event event(EVENT_TALKSENT); + event.setInt("npcId", npcId); + event.trigger(CHANNEL_NPC); } void NpcHandler::nextDialog(int npcId) { MessageOut outMsg(CMSG_NPC_NEXT_REQUEST); outMsg.writeInt32(npcId); + + Mana::Event event(EVENT_NEXTSENT); + event.setInt("npcId", npcId); + event.trigger(CHANNEL_NPC); } void NpcHandler::closeDialog(int npcId) @@ -146,19 +197,21 @@ void NpcHandler::closeDialog(int npcId) MessageOut outMsg(CMSG_NPC_CLOSE); outMsg.writeInt32(npcId); - NpcDialogs::iterator it = mNpcDialogs.find(npcId); - if (it != mNpcDialogs.end()) - { - (*it).second.dialog->close(); - mNpcDialogs.erase(it); - } + Mana::Event event(EVENT_CLOSESENT); + event.setInt("npcId", npcId); + event.trigger(CHANNEL_NPC); } -void NpcHandler::listInput(int npcId, int value) +void NpcHandler::menuSelect(int npcId, int choice) { MessageOut outMsg(CMSG_NPC_LIST_CHOICE); outMsg.writeInt32(npcId); - outMsg.writeInt8(value); + outMsg.writeInt8(choice); + + Mana::Event event(EVENT_MENUSENT); + event.setInt("npcId", npcId); + event.setInt("choice", choice); + event.trigger(CHANNEL_NPC); } void NpcHandler::integerInput(int npcId, int value) @@ -166,6 +219,11 @@ void NpcHandler::integerInput(int npcId, int value) MessageOut outMsg(CMSG_NPC_INT_RESPONSE); outMsg.writeInt32(npcId); outMsg.writeInt32(value); + + Mana::Event event(EVENT_INTEGERINPUTSENT); + event.setInt("npcId", npcId); + event.setInt("value", value); + event.trigger(CHANNEL_NPC); } void NpcHandler::stringInput(int npcId, const std::string &value) @@ -175,57 +233,17 @@ void NpcHandler::stringInput(int npcId, const std::string &value) outMsg.writeInt32(npcId); outMsg.writeString(value, value.length()); outMsg.writeInt8(0); // Prevent problems with string reading -} -void NpcHandler::sendLetter(int npcId, const std::string &recipient, - const std::string &text) -{ - // TODO + Mana::Event event(EVENT_STRINGINPUTSENT); + event.setInt("npcId", npcId); + event.setString("value", value); + event.trigger(CHANNEL_NPC); } -void NpcHandler::startShopping(int beingId) -{ - // TODO -} - -void NpcHandler::buy(int beingId) -{ - MessageOut outMsg(CMSG_NPC_BUY_SELL_REQUEST); - outMsg.writeInt32(beingId); - outMsg.writeInt8(0); // Buy -} - -void NpcHandler::sell(int beingId) -{ - MessageOut outMsg(CMSG_NPC_BUY_SELL_REQUEST); - outMsg.writeInt32(beingId); - outMsg.writeInt8(1); // Sell -} - -void NpcHandler::buyItem(int beingId, int itemId, int amount) -{ - MessageOut outMsg(CMSG_NPC_BUY_REQUEST); - outMsg.writeInt16(8); // One item (length of packet) - outMsg.writeInt16(amount); - outMsg.writeInt16(itemId); -} - -void NpcHandler::sellItem(int beingId, int itemId, int amount) -{ - MessageOut outMsg(CMSG_NPC_SELL_REQUEST); - outMsg.writeInt16(8); // One item (length of packet) - outMsg.writeInt16(itemId + INVENTORY_OFFSET); - outMsg.writeInt16(amount); -} - -void NpcHandler::endShopping(int beingId) -{ - // TODO -} - -void NpcHandler::clearDialogs() +void NpcHandler::sendLetter(int npcId, const std::string &recipient, + const std::string &text) { - mNpcDialogs.clear(); + //NOTE: eA doesn't have letters } } // namespace TmwAthena diff --git a/src/net/tmwa/npchandler.h b/src/net/tmwa/npchandler.h index bd696bdd..1e933418 100644 --- a/src/net/tmwa/npchandler.h +++ b/src/net/tmwa/npchandler.h @@ -22,15 +22,14 @@ #ifndef NET_TA_NPCHANDLER_H #define NET_TA_NPCHANDLER_H -#include "net/net.h" +#include "listener.h" + #include "net/npchandler.h" #include "net/tmwa/messagehandler.h" #include <map> -class NpcDialog; - namespace TmwAthena { class NpcHandler : public MessageHandler, public Net::NpcHandler @@ -40,13 +39,25 @@ class NpcHandler : public MessageHandler, public Net::NpcHandler void handleMessage(Net::MessageIn &msg); + void startShopping(int beingId); + + void buy(int beingId); + + void sell(int beingId); + + void buyItem(int beingId, int itemId, int amount); + + void sellItem(int beingId, int itemId, int amount); + + void endShopping(int beingId); + void talk(int npcId); void nextDialog(int npcId); void closeDialog(int npcId); - void listInput(int npcId, int value); + void menuSelect(int npcId, int choice); void integerInput(int npcId, int value); @@ -55,26 +66,6 @@ class NpcHandler : public MessageHandler, public Net::NpcHandler void sendLetter(int npcId, const std::string &recipient, const std::string &text); - void startShopping(int beingId); - - void buy(int beingId); - - void sell(int beingId); - - void buyItem(int beingId, int itemId, int amount); - - void sellItem(int beingId, int itemId, int amount); - - void endShopping(int beingId); - - void clearDialogs(); - - private: - typedef struct { - NpcDialog* dialog; - } Wrapper; - typedef std::map<int, Wrapper> NpcDialogs; - NpcDialogs mNpcDialogs; }; } // namespace TmwAthena diff --git a/src/net/tmwa/partyhandler.cpp b/src/net/tmwa/partyhandler.cpp index 611fe3e6..00b1e621 100644 --- a/src/net/tmwa/partyhandler.cpp +++ b/src/net/tmwa/partyhandler.cpp @@ -20,7 +20,8 @@ #include "net/tmwa/partyhandler.h" -#include "beingmanager.h" +#include "actorspritemanager.h" +#include "event.h" #include "localplayer.h" #include "log.h" @@ -78,12 +79,9 @@ void PartyHandler::handleMessage(Net::MessageIn &msg) { case SMSG_PARTY_CREATE: if (msg.readInt8()) - localChatTab->chatLog(_("Could not create party."), BY_SERVER); + SERVER_NOTICE(_("Could not create party.")) else - { - localChatTab->chatLog(_("Party successfully created."), - BY_SERVER); - } + SERVER_NOTICE(_("Party successfully created.")) break; case SMSG_PARTY_INFO: { @@ -143,12 +141,9 @@ void PartyHandler::handleMessage(Net::MessageIn &msg) std::string nick = ""; Being *being; - if ((being = beingManager->findBeing(id))) + if ((being = actorSpriteManager->findBeing(id))) { - if (being->getType() == Being::PLAYER) - { - nick = being->getName(); - } + nick = being->getName(); } socialWindow->showPartyInvite(partyName, nick); @@ -238,8 +233,7 @@ void PartyHandler::handleMessage(Net::MessageIn &msg) { taParty->removeFromMembers(); taParty->clearMembers(); - localChatTab->chatLog(_("You have left the party."), - BY_SERVER); + SERVER_NOTICE(_("You have left the party.")) if (partyTab) { delete partyTab; @@ -252,9 +246,10 @@ void PartyHandler::handleMessage(Net::MessageIn &msg) partyTab->chatLog(strprintf(_("%s has left your party."), nick.c_str()), BY_SERVER); - Being *b = beingManager->findBeing(id); - if (b && b->getType() == Being::PLAYER) - static_cast<Player*>(b)->setParty(NULL); + if (Being *b = actorSpriteManager->findBeing(id)) + { + b->setParty(NULL); + } taParty->removeMember(id); } @@ -274,9 +269,9 @@ void PartyHandler::handleMessage(Net::MessageIn &msg) // The server only sends this when the member is in range, so // lets make sure they get the party hilight. - if (Being *b = beingManager->findBeing(id)) + if (Being *b = actorSpriteManager->findBeing(id)) { - static_cast<Player*>(b)->setParty(taParty); + b->setParty(taParty); } } break; @@ -319,19 +314,19 @@ void PartyHandler::join(int partyId) // TODO? } -void PartyHandler::invite(Player *player) +void PartyHandler::invite(Being *being) { MessageOut outMsg(CMSG_PARTY_INVITE); - outMsg.writeInt32(player->getId()); + outMsg.writeInt32(being->getId()); } void PartyHandler::invite(const std::string &name) { - Being *invitee = beingManager->findBeingByName(name, Being::PLAYER); + Being *invitee = actorSpriteManager->findBeingByName(name, Being::PLAYER); if (invitee) { - invite((Player *)invitee); + invite(invitee); partyTab->chatLog(strprintf(_("Invited user %s to party."), invitee->getName().c_str()), BY_SERVER); } @@ -342,8 +337,7 @@ void PartyHandler::invite(const std::string &name) } else { - localChatTab->chatLog(_("You can only inivte when you are in a party!"), - BY_SERVER); + SERVER_NOTICE(_("You can only inivte when you are in a party!")) } } @@ -359,10 +353,10 @@ void PartyHandler::leave() MessageOut outMsg(CMSG_PARTY_LEAVE); } -void PartyHandler::kick(Player *player) +void PartyHandler::kick(Being *being) { MessageOut outMsg(CMSG_PARTY_KICK); - outMsg.writeInt32(player->getId()); + outMsg.writeInt32(being->getId()); outMsg.writeString("", 24); //Unused } diff --git a/src/net/tmwa/partyhandler.h b/src/net/tmwa/partyhandler.h index fc8d741f..5afc8e53 100644 --- a/src/net/tmwa/partyhandler.h +++ b/src/net/tmwa/partyhandler.h @@ -43,7 +43,7 @@ class PartyHandler : public MessageHandler, public Net::PartyHandler void join(int partyId); - void invite(Player *player); + void invite(Being *being); void invite(const std::string &name); @@ -51,7 +51,7 @@ class PartyHandler : public MessageHandler, public Net::PartyHandler void leave(); - void kick(Player *player); + void kick(Being *being); void kick(const std::string &name); diff --git a/src/net/tmwa/playerhandler.cpp b/src/net/tmwa/playerhandler.cpp index 48e7f4b3..b82968a3 100644 --- a/src/net/tmwa/playerhandler.cpp +++ b/src/net/tmwa/playerhandler.cpp @@ -21,28 +21,25 @@ #include "net/tmwa/playerhandler.h" +#include "event.h" #include "game.h" #include "localplayer.h" #include "log.h" -#include "npc.h" +#include "playerinfo.h" #include "units.h" #include "gui/buy.h" #include "gui/buysell.h" #include "gui/gui.h" -#include "gui/npcdialog.h" #include "gui/okdialog.h" #include "gui/sell.h" #include "gui/statuswindow.h" #include "gui/viewport.h" -#include "gui/widgets/chattab.h" - #include "net/messagein.h" #include "net/messageout.h" #include "net/tmwa/protocol.h" -#include "net/tmwa/npchandler.h" #include "utils/stringutils.h" #include "utils/gettext.h" @@ -54,9 +51,6 @@ extern OkDialog *deathNotice; // everything beyond will reset the port hard. static const int MAP_TELEPORT_SCROLL_DISTANCE = 8; -#define ATTR_BONUS(atr) \ -(player_node->getAttributeEffective(atr) - player_node->getAttributeBase(atr)) - // TODO Move somewhere else namespace { @@ -83,14 +77,11 @@ namespace { BuyDialog::closeAll(); BuySellDialog::closeAll(); - NpcDialog::closeAll(); SellDialog::closeAll(); viewport->closePopupMenu(); - TmwAthena::NpcHandler *handler = - static_cast<TmwAthena::NpcHandler*>(Net::getNpcHandler()); - handler->clearDialogs(); + Mana::Event::trigger(CHANNEL_NPC, EVENT_CLOSEALL); } } deathListener; @@ -220,7 +211,6 @@ void PlayerHandler::handleMessage(Net::MessageIn &msg) } player_node->setAction(Being::STAND); - player_node->setFrame(0); player_node->setTileCoords(x, y); logger->log("Adjust scrolling by %d:%d", (int) scrollOffsetX, @@ -241,17 +231,17 @@ void PlayerHandler::handleMessage(Net::MessageIn &msg) player_node->setWalkSpeed(Vector(value, value, 0)); break; case 0x0004: break; // manner - case 0x0005: player_node->setHp(value); break; - case 0x0006: player_node->setMaxHp(value); break; - case 0x0007: player_node->setMP(value); break; - case 0x0008: player_node->setMaxMP(value); break; - case 0x0009: player_node->setCharacterPoints(value); break; - case 0x000b: player_node->setLevel(value); break; - case 0x000c: player_node->setSkillPoints(value); break; + case 0x0005: PlayerInfo::setAttribute(HP, value); break; + case 0x0006: PlayerInfo::setAttribute(MAX_HP, value); break; + case 0x0007: PlayerInfo::setAttribute(MP, value); break; + case 0x0008: PlayerInfo::setAttribute(MAX_MP, value); break; + case 0x0009: PlayerInfo::setAttribute(CHAR_POINTS, value); break; + case 0x000b: PlayerInfo::setAttribute(LEVEL, value); break; + case 0x000c: PlayerInfo::setAttribute(SKILL_POINTS, value); break; case 0x0018: - if (value >= player_node->getMaxWeight() / 2 && - player_node->getTotalWeight() < - player_node->getMaxWeight() / 2) + if (value >= PlayerInfo::getAttribute(MAX_WEIGHT) / 2 && + PlayerInfo::getAttribute(TOTAL_WEIGHT) < + PlayerInfo::getAttribute(MAX_WEIGHT) / 2) { weightNotice = new OkDialog(_("Message"), _("You are carrying more than " @@ -260,59 +250,37 @@ void PlayerHandler::handleMessage(Net::MessageIn &msg) weightNotice->addActionListener( &weightListener); } - player_node->setTotalWeight(value); + PlayerInfo::setAttribute(TOTAL_WEIGHT, value); break; - case 0x0019: player_node->setMaxWeight(value); break; + case 0x0019: PlayerInfo::setAttribute(MAX_WEIGHT, value); break; - case 0x0029: player_node->setAttributeEffective(ATK, value - + ATTR_BONUS(ATK)); - player_node->setAttributeBase(ATK, value); - break; - case 0x002a: value += player_node->getAttributeBase(ATK); - player_node->setAttributeEffective(ATK, value); break; - - case 0x002b: player_node->setAttributeEffective(MATK, value - + ATTR_BONUS(MATK)); - player_node->setAttributeBase(MATK, value); - if (statusWindow) - statusWindow->update(StatusWindow::MP); - break; - case 0x002c: value += player_node->getAttributeBase(MATK); - player_node->setAttributeEffective(MATK, value); - if (statusWindow) - statusWindow->update(StatusWindow::MP); - break; - case 0x002d: player_node->setAttributeEffective(DEF, value - + ATTR_BONUS(DEF)); - player_node->setAttributeBase(DEF, value); break; - case 0x002e: value += player_node->getAttributeBase(DEF); - player_node->setAttributeEffective(DEF, value); break; - - case 0x002f: player_node->setAttributeEffective(MDEF, value - + ATTR_BONUS(MDEF)); - player_node->setAttributeBase(MDEF, value); break; - case 0x0030: value += player_node->getAttributeBase(MDEF); - player_node->setAttributeEffective(MDEF, value); break; - - case 0x0031: player_node->setAttributeBase(HIT, value); - player_node->setAttributeEffective(HIT, value); break; - - case 0x0032: player_node->setAttributeEffective(FLEE, value - + ATTR_BONUS(FLEE)); - player_node->setAttributeBase(FLEE, value); break; - case 0x0033: value += player_node->getAttributeBase(FLEE); - player_node->setAttributeEffective(FLEE, value); break; - - case 0x0034: player_node->setAttributeBase(CRIT, value); - player_node->setAttributeEffective(CRIT, value); break; + case 0x0029: PlayerInfo::setStatBase(ATK, value); break; + case 0x002a: PlayerInfo::setStatMod(ATK, value); break; + + case 0x002b: PlayerInfo::setStatBase(MATK, value); break; + case 0x002c: PlayerInfo::setStatMod(MATK, value); break; + + case 0x002d: PlayerInfo::setStatBase(DEF, value); break; + case 0x002e: PlayerInfo::setStatMod(DEF, value); break; + + case 0x002f: PlayerInfo::setStatBase(MDEF, value); break; + case 0x0030: PlayerInfo::setStatMod(MDEF, value); break; + + case 0x0031: PlayerInfo::setStatBase(HIT, value); break; + + case 0x0032: PlayerInfo::setStatBase(FLEE, value); break; + case 0x0033: PlayerInfo::setStatMod(FLEE, value); break; + + case 0x0034: PlayerInfo::setStatBase(CRIT, value); break; case 0x0035: player_node->setAttackSpeed(value); break; - case 0x0037: player_node->setAttributeBase(JOB, value); - player_node->setAttributeEffective(JOB, value); break; + + case 0x0037: PlayerInfo::setStatBase(JOB, value); break; + case 500: player_node->setGMLevel(value); break; } - if (player_node->getHp() == 0 && !deathNotice) + if (PlayerInfo::getAttribute(HP) == 0 && !deathNotice) { deathNotice = new OkDialog(_("Message"), randomDeathMessage(), @@ -331,29 +299,29 @@ void PlayerHandler::handleMessage(Net::MessageIn &msg) switch (msg.readInt16()) { case 0x0001: - player_node->setExp(msg.readInt32()); + PlayerInfo::setAttribute(EXP, msg.readInt32()); break; case 0x0002: - player_node->setExperience(JOB, msg.readInt32(), - player_node->getExperience(JOB).second); + PlayerInfo::setStatExperience(JOB, msg.readInt32(), + PlayerInfo::getStatExperience(JOB).second); break; case 0x0014: { - int curGp = player_node->getMoney(); - player_node->setMoney(msg.readInt32()); - if (player_node->getMoney() > curGp) - localChatTab->chatLog(strprintf(_("You picked up " - "%s."), - Units::formatCurrency(player_node->getMoney() - - curGp).c_str()), BY_SERVER); + int oldMoney = PlayerInfo::getAttribute(MONEY); + int newMoney = msg.readInt32(); + PlayerInfo::setAttribute(MONEY, newMoney); + if (newMoney > oldMoney) + SERVER_NOTICE(strprintf(_("You picked up %s."), + Units::formatCurrency(newMoney - + oldMoney).c_str())) } break; case 0x0016: - player_node->setExpNeeded(msg.readInt32()); + PlayerInfo::setAttribute(EXP_NEEDED, msg.readInt32()); break; case 0x0017: - player_node->setExperience(JOB, - player_node->getExperience(JOB).first, - msg.readInt32()); + PlayerInfo::setStatExperience(JOB, + PlayerInfo::getStatExperience(JOB).first, + msg.readInt32()); break; } break; @@ -364,8 +332,8 @@ void PlayerHandler::handleMessage(Net::MessageIn &msg) int base = msg.readInt32(); int bonus = msg.readInt32(); - player_node->setAttributeBase(type, base); - player_node->setAttributeEffective(type, base + bonus); + PlayerInfo::setStatBase(type, base, false); + PlayerInfo::setStatMod(type, bonus); } break; @@ -377,25 +345,20 @@ void PlayerHandler::handleMessage(Net::MessageIn &msg) if (ok != 1) { - localChatTab->chatLog(_("Cannot raise skill!"), - BY_SERVER); + SERVER_NOTICE(_("Cannot raise skill!")) } - int bonus = ATTR_BONUS(type); - - player_node->setAttributeBase(type, value); - player_node->setAttributeEffective(type, value + bonus); + PlayerInfo::setStatBase(type, value); } break; // Updates stats and status points case SMSG_PLAYER_STAT_UPDATE_5: - player_node->setCharacterPoints(msg.readInt16()); + PlayerInfo::setAttribute(CHAR_POINTS, msg.readInt16()); { int val = msg.readInt8(); - player_node->setAttributeEffective(STR, val + ATTR_BONUS(STR)); - player_node->setAttributeBase(STR, val); + PlayerInfo::setStatBase(STR, val); if (val >= 99) { statusWindow->setPointsNeeded(STR, 0); @@ -407,8 +370,7 @@ void PlayerHandler::handleMessage(Net::MessageIn &msg) } val = msg.readInt8(); - player_node->setAttributeEffective(AGI, val + ATTR_BONUS(AGI)); - player_node->setAttributeBase(AGI, val); + PlayerInfo::setStatBase(AGI, val); if (val >= 99) { statusWindow->setPointsNeeded(AGI, 0); @@ -420,8 +382,7 @@ void PlayerHandler::handleMessage(Net::MessageIn &msg) } val = msg.readInt8(); - player_node->setAttributeEffective(VIT, val + ATTR_BONUS(VIT)); - player_node->setAttributeBase(VIT, val); + PlayerInfo::setStatBase(VIT, val); if (val >= 99) { statusWindow->setPointsNeeded(VIT, 0); @@ -433,8 +394,7 @@ void PlayerHandler::handleMessage(Net::MessageIn &msg) } val = msg.readInt8(); - player_node->setAttributeEffective(INT, val + ATTR_BONUS(INT)); - player_node->setAttributeBase(INT, val); + PlayerInfo::setStatBase(INT, val); if (val >= 99) { statusWindow->setPointsNeeded(INT, 0); @@ -446,8 +406,7 @@ void PlayerHandler::handleMessage(Net::MessageIn &msg) } val = msg.readInt8(); - player_node->setAttributeEffective(DEX, val + ATTR_BONUS(DEX)); - player_node->setAttributeBase(DEX, val); + PlayerInfo::setStatBase(DEX, val); if (val >= 99) { statusWindow->setPointsNeeded(DEX, 0); @@ -459,8 +418,7 @@ void PlayerHandler::handleMessage(Net::MessageIn &msg) } val = msg.readInt8(); - player_node->setAttributeEffective(LUK, val + ATTR_BONUS(LUK)); - player_node->setAttributeBase(LUK, val); + PlayerInfo::setStatBase(LUK, val); if (val >= 99) { statusWindow->setPointsNeeded(LUK, 0); @@ -471,39 +429,25 @@ void PlayerHandler::handleMessage(Net::MessageIn &msg) statusWindow->setPointsNeeded(LUK, msg.readInt8()); } - val = msg.readInt16(); // ATK - player_node->setAttributeBase(ATK, val); - val += msg.readInt16(); // ATK bonus - player_node->setAttributeEffective(ATK, val); - - val = msg.readInt16(); // MATK - player_node->setAttributeBase(MATK, val); - val += msg.readInt16(); // MATK bonus - player_node->setAttributeEffective(MATK, val); - statusWindow->update(StatusWindow::MP); - - val = msg.readInt16(); // DEF - player_node->setAttributeBase(DEF, val); - val += msg.readInt16(); // DEF bonus - player_node->setAttributeEffective(DEF, val); - - val = msg.readInt16(); // MDEF - player_node->setAttributeBase(MDEF, val); - val += msg.readInt16(); // MDEF bonus - player_node->setAttributeEffective(MDEF, val); - - val = msg.readInt16(); // HIT - player_node->setAttributeBase(HIT, val); - player_node->setAttributeEffective(HIT, val); - - val = msg.readInt16(); // FLEE - player_node->setAttributeBase(FLEE, val); - val += msg.readInt16(); // FLEE bonus - player_node->setAttributeEffective(FLEE, val); - - val = msg.readInt16(); - player_node->setAttributeBase(CRIT, val); - player_node->setAttributeEffective(CRIT, val); + PlayerInfo::setStatBase(ATK, msg.readInt16(), false); + PlayerInfo::setStatMod(ATK, msg.readInt16()); + + PlayerInfo::setStatBase(MATK, msg.readInt16(), false); + PlayerInfo::setStatMod(MATK, msg.readInt16()); + + + PlayerInfo::setStatBase(DEF, msg.readInt16(), false); + PlayerInfo::setStatMod(DEF, msg.readInt16()); + + PlayerInfo::setStatBase(MDEF, msg.readInt16(), false); + PlayerInfo::setStatMod(MDEF, msg.readInt16()); + + PlayerInfo::setStatBase(HIT, msg.readInt16()); + + PlayerInfo::setStatBase(FLEE, msg.readInt16(), false); + PlayerInfo::setStatMod(FLEE, msg.readInt16()); + + PlayerInfo::setStatBase(CRIT, msg.readInt16()); } msg.readInt16(); // manner @@ -540,8 +484,9 @@ void PlayerHandler::handleMessage(Net::MessageIn &msg) switch (type) { case 0: - localChatTab->chatLog(_("Equip arrows first."), - BY_SERVER); + { + SERVER_NOTICE(_("Equip arrows first.")) + } break; default: logger->log("0x013b: Unhandled message %i", type); @@ -582,7 +527,7 @@ void PlayerHandler::decreaseAttribute(int attr) void PlayerHandler::increaseSkill(int skillId) { - if (player_node->getSkillPoints() <= 0) + if (PlayerInfo::getAttribute(SKILL_POINTS) <= 0) return; MessageOut outMsg(CMSG_SKILL_LEVELUP_REQUEST); @@ -591,8 +536,11 @@ void PlayerHandler::increaseSkill(int skillId) void PlayerHandler::pickUp(FloorItem *floorItem) { - MessageOut outMsg(CMSG_ITEM_PICKUP); - outMsg.writeInt32(floorItem->getId()); + if (floorItem) + { + MessageOut outMsg(CMSG_ITEM_PICKUP); + outMsg.writeInt32(floorItem->getId()); + } } void PlayerHandler::setDirection(char direction) @@ -641,7 +589,7 @@ void PlayerHandler::ignoreAll(bool ignore) bool PlayerHandler::canUseMagic() { - return player_node->getAttributeEffective(MATK) > 0; + return PlayerInfo::getStatEffective(MATK) > 0; } bool PlayerHandler::canCorrectAttributes() diff --git a/src/net/tmwa/specialhandler.cpp b/src/net/tmwa/specialhandler.cpp index c5f5d540..577bda7e 100644 --- a/src/net/tmwa/specialhandler.cpp +++ b/src/net/tmwa/specialhandler.cpp @@ -21,13 +21,12 @@ #include "net/tmwa/specialhandler.h" -#include "localplayer.h" +#include "event.h" #include "log.h" +#include "playerinfo.h" #include "gui/skilldialog.h" -#include "gui/widgets/chattab.h" - #include "net/messagein.h" #include "net/messageout.h" @@ -105,8 +104,7 @@ void SpecialHandler::handleMessage(Net::MessageIn &msg) msg.skip(24); // unused int up = msg.readInt8(); - player_node->setAttributeBase(skillId, level); - player_node->setAttributeEffective(skillId, level); + PlayerInfo::setStatBase(skillId, level); skillDialog->setModifiable(skillId, up); } break; @@ -119,8 +117,7 @@ void SpecialHandler::handleMessage(Net::MessageIn &msg) msg.readInt16(); // range int up = msg.readInt8(); - player_node->setAttributeBase(skillId, level); - player_node->setAttributeEffective(skillId, level); + PlayerInfo::setStatBase(skillId, level); skillDialog->setModifiable(skillId, up); } break; @@ -218,7 +215,7 @@ void SpecialHandler::handleMessage(Net::MessageIn &msg) } } - localChatTab->chatLog(msg); + SERVER_NOTICE(msg) break; } } diff --git a/src/net/tmwa/token.h b/src/net/tmwa/token.h index d2a21012..3e781cd8 100644 --- a/src/net/tmwa/token.h +++ b/src/net/tmwa/token.h @@ -19,7 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "player.h" +#include "being.h" #ifndef NET_TA_TOKEN_H #define NET_TA_TOKEN_H diff --git a/src/net/tmwa/tradehandler.cpp b/src/net/tmwa/tradehandler.cpp index 9089f8e6..034b959d 100644 --- a/src/net/tmwa/tradehandler.cpp +++ b/src/net/tmwa/tradehandler.cpp @@ -21,22 +21,24 @@ #include "net/tmwa/tradehandler.h" +#include "event.h" #include "inventory.h" #include "item.h" #include "localplayer.h" +#include "playerinfo.h" #include "playerrelations.h" #include "gui/confirmdialog.h" #include "gui/trade.h" -#include "gui/widgets/chattab.h" - #include "net/inventoryhandler.h" #include "net/messagein.h" #include "net/messageout.h" #include "net/tmwa/protocol.h" +#include "resources/iteminfo.h" + #include "utils/gettext.h" #include "utils/stringutils.h" @@ -96,14 +98,14 @@ void TradeHandler::handleMessage(Net::MessageIn &msg) if (player_relations.hasPermission(tradePartnerName, PlayerRelation::TRADE)) { - if (!player_node->tradeRequestOk() || confirmDlg) + if (PlayerInfo::isTrading() || confirmDlg) { Net::getTradeHandler()->respond(false); break; } tradePartnerName = tradePartnerNameTemp; - player_node->setTrading(true); + PlayerInfo::setTrading(true); confirmDlg = new ConfirmDialog(_("Request for Trade"), strprintf(_("%s wants to trade with you, do you " "accept?"), tradePartnerName.c_str())); @@ -121,16 +123,16 @@ void TradeHandler::handleMessage(Net::MessageIn &msg) switch (msg.readInt8()) { case 0: // Too far away - localChatTab->chatLog(_("Trading isn't possible. Trade " - "partner is too far away."), BY_SERVER); + SERVER_NOTICE(_("Trading isn't possible. Trade " + "partner is too far away.")) break; case 1: // Character doesn't exist - localChatTab->chatLog(_("Trading isn't possible. Character " - "doesn't exist."), BY_SERVER); + SERVER_NOTICE(_("Trading isn't possible. Character " + "doesn't exist.")) break; case 2: // Invite request check failed... - localChatTab->chatLog(_("Trade cancelled due to an unknown " - "reason."), BY_SERVER); + SERVER_NOTICE(_("Trade cancelled due to an unknown " + "reason.")) break; case 3: // Trade accepted tradeWindow->reset(); @@ -141,17 +143,15 @@ void TradeHandler::handleMessage(Net::MessageIn &msg) case 4: // Trade cancelled if (player_relations.hasPermission(tradePartnerName, PlayerRelation::SPEECH_LOG)) - localChatTab->chatLog(strprintf(_("Trade with %s " - "cancelled."), tradePartnerName.c_str()), - BY_SERVER); + SERVER_NOTICE(strprintf(_("Trade with %s cancelled."), + tradePartnerName.c_str())) // otherwise ignore silently tradeWindow->setVisible(false); - player_node->setTrading(false); + PlayerInfo::setTrading(false); break; default: // Shouldn't happen as well, but to be sure - localChatTab->chatLog(_("Unhandled trade cancel packet."), - BY_SERVER); + SERVER_NOTICE(_("Unhandled trade cancel packet.")) break; } break; @@ -169,7 +169,7 @@ void TradeHandler::handleMessage(Net::MessageIn &msg) if (type == 0) tradeWindow->setMoney(amount); else - tradeWindow->addItem(type, false, amount, false); + tradeWindow->addItem(type, false, amount); } break; @@ -177,7 +177,7 @@ void TradeHandler::handleMessage(Net::MessageIn &msg) // Trade: New Item add response (was 0x00ea, now 01b1) { const int index = msg.readInt16() - INVENTORY_OFFSET; - Item *item = player_node->getInventory()->getItem(index); + Item *item = PlayerInfo::getInventory()->getItem(index); if (!item) { tradeWindow->receivedOk(true); @@ -189,27 +189,27 @@ void TradeHandler::handleMessage(Net::MessageIn &msg) { case 0: // Successfully added item - if (item->isEquipment() && item->isEquipped()) + if (item->isEquippable() && item->isEquipped()) { - Net::getInventoryHandler()->unequipItem(item); + item->doEvent(EVENT_DOUNEQUIP); } - tradeWindow->addItem(item->getId(), true, quantity, - item->isEquipment()); + tradeWindow->addItem(item->getId(), true, quantity); + item->increaseQuantity(-quantity); break; case 1: // Add item failed - player overweighted - localChatTab->chatLog(_("Failed adding item. Trade " - "partner is over weighted."), BY_SERVER); + SERVER_NOTICE(_("Failed adding item. Trade " + "partner is over weighted.")) break; case 2: // Add item failed - player has no free slot - localChatTab->chatLog(_("Failed adding item. Trade " - "partner has no free slot."), BY_SERVER); + SERVER_NOTICE(_("Failed adding item. Trade " + "partner has no free slot.")) break; default: - localChatTab->chatLog(_("Failed adding item for " - "unknown reason."), BY_SERVER); + SERVER_NOTICE(_("Failed adding item for " + "unknown reason.")) break; } } @@ -221,17 +221,17 @@ void TradeHandler::handleMessage(Net::MessageIn &msg) break; case SMSG_TRADE_CANCEL: - localChatTab->chatLog(_("Trade canceled."), BY_SERVER); + SERVER_NOTICE(_("Trade canceled.")) tradeWindow->setVisible(false); tradeWindow->reset(); - player_node->setTrading(false); + PlayerInfo::setTrading(false); break; case SMSG_TRADE_COMPLETE: - localChatTab->chatLog(_("Trade completed."), BY_SERVER); + SERVER_NOTICE(_("Trade completed.")) tradeWindow->setVisible(false); tradeWindow->reset(); - player_node->setTrading(false); + PlayerInfo::setTrading(false); break; } } @@ -245,7 +245,7 @@ void TradeHandler::request(Being *being) void TradeHandler::respond(bool accept) { if (!accept) - player_node->setTrading(false); + PlayerInfo::setTrading(false); MessageOut outMsg(CMSG_TRADE_RESPONSE); outMsg.writeInt8(accept ? 3 : 4); diff --git a/src/net/tradehandler.h b/src/net/tradehandler.h index 30798c41..ea3c4550 100644 --- a/src/net/tradehandler.h +++ b/src/net/tradehandler.h @@ -30,6 +30,8 @@ namespace Net { class TradeHandler { public: + virtual ~TradeHandler() {} + virtual void request(Being *being) {} virtual void respond(bool accept) {} @@ -45,8 +47,6 @@ class TradeHandler virtual void finish() {} virtual void cancel() {} - - virtual ~TradeHandler() {} }; } diff --git a/src/npc.cpp b/src/npc.cpp deleted file mode 100644 index a3c26618..00000000 --- a/src/npc.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - * The Mana Client - * Copyright (C) 2004-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * - * This file is part of The Mana Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "animatedsprite.h" -#include "beingmanager.h" -#include "npc.h" -#include "particle.h" -#include "text.h" - -#include "gui/buy.h" -#include "gui/buysell.h" -#include "gui/npcdialog.h" -#include "gui/npcpostdialog.h" -#include "gui/userpalette.h" -#include "gui/sell.h" - -#include "net/net.h" -#include "net/npchandler.h" - -#include "resources/npcdb.h" -#include "configuration.h" - -NPC::NPC(int id, int subtype, Map *map): - Player(id, subtype, map, true) -{ - setSubtype(subtype); - - setShowName(true); -} - -void NPC::setName(const std::string &name) -{ - const std::string displayName = name.substr(0, name.find('#', 0)); - - Being::setName(displayName); - - mNameColor = &userPalette->getColor(UserPalette::NPC); - - mDispName->setColor(mNameColor); -} - -void NPC::setSubtype(Uint16 subtype) -{ - Being::setSubtype(subtype); - - NPCInfo info = NPCDB::get(subtype); - - mSprites.clear(); - // Setup NPC sprites - for (std::list<NPCsprite*>::const_iterator i = info.sprites.begin(); - i != info.sprites.end(); - i++) - { - std::string file = paths.getValue("sprites", - "graphics/sprites/") + (*i)->sprite; - int variant = (*i)->variant; - mSprites.push_back(AnimatedSprite::load(file, variant)); - mSpriteIDs.push_back(0); - mSpriteColors.push_back(""); - } - - if (Particle::enabled) - { - //setup particle effects - for (std::list<std::string>::const_iterator i = info.particles.begin(); - i != info.particles.end(); - i++) - { - Particle *p = particleEngine->addEffect(*i, 0, 0); - this->controlParticle(p); - } - } -} - -void NPC::talk() -{ - Net::getNpcHandler()->talk(mId); -} - -void NPC::setSprite(unsigned int slot, int id, const std::string &color) -{ - // Do nothing -} - -bool NPC::isTalking() -{ - return NpcDialog::isActive() || BuyDialog::isActive() || - SellDialog::isActive() || BuySellDialog::isActive() || - NpcPostDialog::isActive(); -} diff --git a/src/npc.h b/src/npc.h deleted file mode 100644 index 0abd2395..00000000 --- a/src/npc.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * The Mana Client - * Copyright (C) 2004-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * - * This file is part of The Mana Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef NPC_H -#define NPC_H - -#include "player.h" - -class Graphics; -class Text; - -class NPC : public Player -{ - public: - NPC(int id, int subtype, Map *map); - - void setName(const std::string &name); - - virtual Type getType() const { return Being::NPC; } - - virtual void setSubtype(Uint16 subtype); - - void talk(); - - void setSprite(unsigned int slot, int id, - const std::string &color = ""); - - /** - * Gets the way an NPC is blocked by other things on the map - */ - virtual unsigned char getWalkMask() const - { - return Map::BLOCKMASK_WALL - | Map::BLOCKMASK_CHARACTER - | Map::BLOCKMASK_MONSTER; - } - - /** We consider NPCs (at least for now) to be one layer-sprites */ - virtual int getNumberOfLayers() const - { return 1; } - - static bool isTalking(); - - protected: - /** - * Gets the way a monster blocks pathfinding for other objects - */ - virtual Map::BlockType getBlockType() const - { return Map::BLOCKTYPE_CHARACTER; } //blocks like a player character - - // Colors don't change for NPCs - virtual void updateColors() {} -}; - -#endif diff --git a/src/openglgraphics.cpp b/src/openglgraphics.cpp index 7818e9d2..c24b7d64 100644 --- a/src/openglgraphics.cpp +++ b/src/openglgraphics.cpp @@ -25,8 +25,6 @@ #include "resources/image.h" -#include "utils/stringutils.h" - #ifdef USE_OPENGL #ifdef __APPLE__ diff --git a/src/openglgraphics.h b/src/openglgraphics.h index ee96e19c..23638eec 100644 --- a/src/openglgraphics.h +++ b/src/openglgraphics.h @@ -22,8 +22,6 @@ #ifndef OPENGLGRAPHICS_H #define OPENGLGRAPHICS_H -#include "main.h" - #include "graphics.h" #ifdef USE_OPENGL diff --git a/src/particle.cpp b/src/particle.cpp index 0c4a7d7e..f147a9f2 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -56,15 +56,15 @@ bool Particle::enabled = true; const float Particle::PARTICLE_SKY = 800.0f; Particle::Particle(Map *map): - mAlive(true), + mAlpha(1.0f), mLifetimeLeft(-1), mLifetimePast(0), mFadeOut(0), mFadeIn(0), - mAlpha(1.0f), + mAlive(ALIVE), mAutoDelete(true), - mMap(map), mAllowSizeAdjust(false), + mDeathEffectConditions(0x00), mGravity(0.0f), mRandomness(0), mBounce(0.0f), @@ -74,33 +74,31 @@ Particle::Particle(Map *map): mInvDieDistance(-1.0f), mMomentum(1.0f) { + setMap(map); Particle::particleCount++; - if (mMap) - setSpriteIterator(mMap->addSprite(this)); } Particle::~Particle() { - // Remove from map sprite list - if (mMap) - mMap->removeSprite(mSpriteIterator); // Delete child emitters and child particles clear(); + //update particle count Particle::particleCount--; } void Particle::setupEngine() { - Particle::maxCount = (int)config.getValue("particleMaxCount", 3000); - Particle::fastPhysics = (int)config.getValue("particleFastPhysics", 0); - Particle::emitterSkip = (int)config.getValue("particleEmitterSkip", 1) + 1; - Particle::enabled = (bool)config.getValue("particleeffects", true); + Particle::maxCount = config.getIntValue("particleMaxCount"); + Particle::fastPhysics = config.getIntValue("particleFastPhysics"); + Particle::emitterSkip = config.getIntValue("particleEmitterSkip") + 1; + Particle::enabled = config.getBoolValue("particleeffects"); disableAutoDelete(); logger->log("Particle engine set up"); } -void Particle::draw(Graphics *, int, int) const +bool Particle::draw(Graphics *, int, int) const { + return false; } bool Particle::update() @@ -108,12 +106,12 @@ bool Particle::update() if (!mMap) return false; - if (mLifetimeLeft == 0) - mAlive = false; + if (mLifetimeLeft == 0 && mAlive == ALIVE) + mAlive = DEAD_TIMEOUT; Vector oldPos = mPos; - if (mAlive) + if (mAlive == ALIVE) { //calculate particle movement if (mMomentum != 1.0f) @@ -147,7 +145,7 @@ bool Particle::update() { if (mInvDieDistance > 0.0f && invHypotenuse > mInvDieDistance) { - mAlive = false; + mAlive = DEAD_IMPACT; } float accFactor = invHypotenuse * mAcceleration; mVelocity -= dist * accFactor; @@ -175,7 +173,7 @@ bool Particle::update() } mLifetimePast++; - if (mPos.z > PARTICLE_SKY || mPos.z < 0.0f) + if (mPos.z < 0.0f) { if (mBounce > 0.0f) { @@ -185,9 +183,13 @@ bool Particle::update() } else { - mAlive = false; + mAlive = DEAD_FLOOR; } } + else if (mPos.z > PARTICLE_SKY) + { + mAlive = DEAD_SKY; + } // Update child emitters if ((mLifetimePast-1)%Particle::emitterSkip == 0) @@ -206,6 +208,17 @@ bool Particle::update() } } + // create death effect when the particle died + if (mAlive != ALIVE && mAlive != DEAD_LONG_AGO) + { + if ((mAlive & mDeathEffectConditions) > 0x00 && !mDeathEffect.empty()) + { + Particle* deathEffect = particleEngine->addEffect(mDeathEffect, 0, 0); + deathEffect->moveBy(mPos); + } + mAlive = DEAD_LONG_AGO; + } + Vector change = mPos - oldPos; // Update child particles @@ -229,7 +242,7 @@ bool Particle::update() p = mChildParticles.erase(p); } } - if (!mAlive && mChildParticles.empty() && mAutoDelete) + if (mAlive != ALIVE && mChildParticles.empty() && mAutoDelete) { return false; } @@ -329,13 +342,39 @@ Particle *Particle::addEffect(const std::string &particleEffectFile, // Look for additional emitters for this particle for_each_xml_child_node(emitterNode, effectChildNode) { - if (!xmlStrEqual(emitterNode->name, BAD_CAST "emitter")) - continue; - - ParticleEmitter *newEmitter; - newEmitter = new ParticleEmitter(emitterNode, newParticle, mMap, - rotation); - newParticle->addEmitter(newEmitter); + if (xmlStrEqual(emitterNode->name, BAD_CAST "emitter")) + { + ParticleEmitter *newEmitter; + newEmitter = new ParticleEmitter(emitterNode, newParticle, mMap, + rotation); + newParticle->addEmitter(newEmitter); + } + else if (xmlStrEqual(emitterNode->name, BAD_CAST "deatheffect")) + { + std::string deathEffect = (const char*)emitterNode->xmlChildrenNode->content; + char deathEffectConditions = 0x00; + if (XML::getBoolProperty(emitterNode, "on-floor", true)) + { + deathEffectConditions += Particle::DEAD_FLOOR; + } + if (XML::getBoolProperty(emitterNode, "on-sky", true)) + { + deathEffectConditions += Particle::DEAD_SKY; + } + if (XML::getBoolProperty(emitterNode, "on-other", false)) + { + deathEffectConditions += Particle::DEAD_OTHER; + } + if (XML::getBoolProperty(emitterNode, "on-impact", true)) + { + deathEffectConditions += Particle::DEAD_IMPACT; + } + if (XML::getBoolProperty(emitterNode, "on-timeout", true)) + { + deathEffectConditions += Particle::DEAD_TIMEOUT; + } + newParticle->setDeathEffect(deathEffect, deathEffectConditions); + } } mChildParticles.push_back(newParticle); @@ -406,13 +445,6 @@ float Particle::getCurrentAlpha() const return alpha; } -void Particle::setMap(Map *map) -{ - mMap = map; - if (mMap) - setSpriteIterator(mMap->addSprite(this)); -} - void Particle::clear() { delete_all(mChildEmitters); diff --git a/src/particle.h b/src/particle.h index 69f8c2be..0e39883b 100644 --- a/src/particle.h +++ b/src/particle.h @@ -22,8 +22,8 @@ #ifndef PARTICLE_H #define PARTICLE_H +#include "actor.h" #include "guichanfwd.h" -#include "sprite.h" #include "vector.h" #include <list> @@ -41,9 +41,19 @@ typedef Emitters::iterator EmitterIterator; /** * A particle spawned by a ParticleEmitter. */ -class Particle : public Sprite +class Particle : public Actor { public: + enum AliveStatus + { + ALIVE = 0, + DEAD_TIMEOUT = 1, + DEAD_FLOOR = 2, + DEAD_SKY = 4, + DEAD_IMPACT = 8, + DEAD_OTHER = 16, + DEAD_LONG_AGO = 128 + }; static const float PARTICLE_SKY; /**< Maximum Z position of particles */ static int fastPhysics; /**< Mode of squareroot calculation */ static int particleCount; /**< Current number of particles */ @@ -83,7 +93,7 @@ class Particle : public Sprite /** * Draws the particle image. */ - virtual void draw(Graphics *graphics, int offsetX, int offsetY) const; + virtual bool draw(Graphics *graphics, int offsetX, int offsetY) const; /** * Necessary for sorting with the other sprites. @@ -92,12 +102,6 @@ class Particle : public Sprite { return (int) (mPos.y + mPos.z) - 64; } /** - * Sets the map the particle is on. - */ - void setMap(Map *map); - - - /** * Creates a blank particle as a child of the current particle * Useful for creating target particles */ @@ -142,12 +146,6 @@ class Particle : public Sprite void moveTo(float x, float y); /** - * Returns the particle position. - */ - const Vector& getPosition() const - { return mPos; } - - /** * Changes the particle position relative */ void moveBy (const Vector &change); @@ -173,32 +171,6 @@ class Particle : public Sprite { mFadeIn = fadeIn; } /** - * Sets the alpha value of the particle - */ - void setAlpha(float alpha) - { mAlpha = alpha; } - - /** - * Returns the current alpha opacity of the particle. - */ - virtual float getAlpha() const - { return mAlpha; } - - /** - * Sets the sprite iterator of the particle on the current map to make - * it easier to remove the particle from the map when it is destroyed. - */ - void setSpriteIterator(std::list<Sprite*>::iterator spriteIterator) - { mSpriteIterator = spriteIterator; } - - /** - * Gets the sprite iterator of the particle on the current map. - */ - std::list<Sprite*>::iterator - getSpriteIterator() const - { return mSpriteIterator; } - - /** * Sets the current velocity in 3 dimensional space. */ void setVelocity(float x, float y, float z) @@ -259,20 +231,20 @@ class Particle : public Sprite void setAllowSizeAdjust(bool adjust) { mAllowSizeAdjust = adjust; } - bool isAlive() - { return mAlive; } + bool isAlive() const + { return mAlive == ALIVE; } /** * Determines whether the particle and its children are all dead */ - bool isExtinct() + bool isExtinct() const { return !isAlive() && mChildParticles.empty(); } /** * Manually marks the particle for deletion. */ void kill() - { mAlive = false; mAutoDelete = true; } + { mAlive = DEAD_OTHER; mAutoDelete = true; } /** * After calling this function the particle will only request @@ -285,28 +257,38 @@ class Particle : public Sprite virtual int getNumberOfLayers() const { return 1; } + virtual float getAlpha() const + { return 1.0f; } + + virtual void setAlpha(float alpha) {} + + virtual void setDeathEffect(const std::string &effectFile, char conditions) + { mDeathEffect = effectFile; mDeathEffectConditions = conditions; } + protected: + /** Opacity of the graphical representation of the particle */ + float mAlpha; + /** Calculates the current alpha transparency taking current fade status into account*/ float getCurrentAlpha() const; - bool mAlive; /**< Is the particle supposed to be drawn and updated?*/ - Vector mPos; /**< Position in pixels relative to map. */ int mLifetimeLeft; /**< Lifetime left in game ticks*/ int mLifetimePast; /**< Age of the particle in game ticks*/ int mFadeOut; /**< Lifetime in game ticks left where fading out begins*/ int mFadeIn; /**< Age in game ticks where fading in is finished*/ - float mAlpha; /**< Opacity of the graphical representation of the particle */ + Vector mVelocity; /**< Speed in pixels per game-tick. */ + private: + AliveStatus mAlive; /**< Is the particle supposed to be drawn and updated?*/ // generic properties bool mAutoDelete; /**< May the particle request its deletion by the parent particle? */ - Map *mMap; /**< Map the particle is on. */ - std::list<Sprite*>::iterator mSpriteIterator; /**< iterator of the particle on the current map */ Emitters mChildEmitters; /**< List of child emitters. */ Particles mChildParticles; /**< List of particles controlled by this particle */ bool mAllowSizeAdjust; /**< Can the effect size be adjusted by the object props in the map file? */ + std::string mDeathEffect; /**< Particle effect file to be spawned when the particle dies */ + char mDeathEffectConditions;/**< Bitfield of death conditions which trigger spawning of the death particle */ // dynamic particle - Vector mVelocity; /**< Speed in pixels per game-tick. */ float mGravity; /**< Downward acceleration in pixels per game-tick. */ int mRandomness; /**< Ammount of random vector change */ float mBounce; /**< How much the particle bounces off when hitting the ground */ diff --git a/src/particleemitter.cpp b/src/particleemitter.cpp index dc9931a5..b9855c10 100644 --- a/src/particleemitter.cpp +++ b/src/particleemitter.cpp @@ -320,6 +320,30 @@ ParticleEmitter::ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map * mParticleAnimation.addTerminator(); } } // for frameNode + } else if (xmlStrEqual(propertyNode->name, BAD_CAST "deatheffect")) + { + mDeathEffect = (const char*)propertyNode->xmlChildrenNode->content; + mDeathEffectConditions = 0x00; + if (XML::getBoolProperty(propertyNode, "on-floor", true)) + { + mDeathEffectConditions += Particle::DEAD_FLOOR; + } + if (XML::getBoolProperty(propertyNode, "on-sky", true)) + { + mDeathEffectConditions += Particle::DEAD_SKY; + } + if (XML::getBoolProperty(propertyNode, "on-other", false)) + { + mDeathEffectConditions += Particle::DEAD_OTHER; + } + if (XML::getBoolProperty(propertyNode, "on-impact", true)) + { + mDeathEffectConditions += Particle::DEAD_IMPACT; + } + if (XML::getBoolProperty(propertyNode, "on-timeout", true)) + { + mDeathEffectConditions += Particle::DEAD_TIMEOUT; + } } } } @@ -469,6 +493,11 @@ std::list<Particle *> ParticleEmitter::createParticles(int tick) newParticle->addEmitter(new ParticleEmitter(*i)); } + if (!mDeathEffect.empty()) + { + newParticle->setDeathEffect(mDeathEffect, mDeathEffectConditions); + } + newParticles.push_back(newParticle); } diff --git a/src/particleemitter.h b/src/particleemitter.h index cc073c1c..9baaa73c 100644 --- a/src/particleemitter.h +++ b/src/particleemitter.h @@ -127,13 +127,19 @@ class ParticleEmitter int mOutputPauseLeft; /* - * Graphical representation of the particle + * Graphical representation of the particles */ Image *mParticleImage; /**< Particle image, if used */ Animation mParticleAnimation; /**< Filename of particle animation file */ Animation mParticleRotation; /**< Filename of particle rotation file */ ParticleEmitterProp<float> mParticleAlpha; /**< Opacity of the graphical representation of the particles */ + /* + * Death effect of the particles + */ + std::string mDeathEffect; + char mDeathEffectConditions; + /** List of emitters the spawned particles are equipped with */ std::list<ParticleEmitter> mParticleChildEmitters; }; diff --git a/src/particleemitterprop.h b/src/particleemitterprop.h index c7b12097..c73d044c 100644 --- a/src/particleemitterprop.h +++ b/src/particleemitterprop.h @@ -21,10 +21,6 @@ #include <cmath> -/** - * Returns a random numeric value that is larger than or equal min and smaller - * than max - */ enum ChangeFunc { diff --git a/src/party.cpp b/src/party.cpp index 155de2ba..99295792 100644 --- a/src/party.cpp +++ b/src/party.cpp @@ -20,8 +20,7 @@ #include "party.h" -#include "beingmanager.h" -#include "player.h" +#include "actorspritemanager.h" PartyMember::PartyMember(Party *party, int id, const std::string &name): Avatar(name), mId(id), mParty(party), mLeader(false) @@ -144,9 +143,9 @@ void Party::removeFromMembers() itr_end = mMembers.end(); while(itr != itr_end) { - Being *b = beingManager->findBeing((*itr)->getID()); - if (b && b->getType() == Being::PLAYER) - static_cast<Player*>(b)->setParty(NULL); + Being *b = actorSpriteManager->findBeing((*itr)->getID()); + if (b) + b->setParty(NULL); ++itr; } } diff --git a/src/player.cpp b/src/player.cpp deleted file mode 100644 index a5a79ff1..00000000 --- a/src/player.cpp +++ /dev/null @@ -1,361 +0,0 @@ -/* - * The Mana Client - * Copyright (C) 2004-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * - * This file is part of The Mana Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "player.h" - -#include "animatedsprite.h" -#include "client.h" -#include "configuration.h" -#include "guild.h" -#include "localplayer.h" -#include "particle.h" -#include "party.h" -#include "text.h" - -#include "gui/socialwindow.h" -#include "gui/theme.h" -#include "gui/userpalette.h" - -#include "net/charhandler.h" -#include "net/net.h" - -#include "resources/colordb.h" -#include "resources/itemdb.h" -#include "resources/iteminfo.h" - -#include "utils/stringutils.h" - -Player::Player(int id, int subtype, Map *map, bool isNPC): - Being(id, subtype, map), - mGender(GENDER_UNSPECIFIED), - mParty(NULL), - mIsGM(false), - mIp(0) -{ - if (!isNPC) - { - for (int i = 0; i < Net::getCharHandler()->maxSprite(); i++) - { - mSprites.push_back(NULL); - mSpriteIDs.push_back(0); - mSpriteColors.push_back(""); - } - - setSubtype(subtype); - } - mShowName = config.getValue("visiblenames", 1); - config.addListener("visiblenames", this); - - updateColors(); -} - -Player::~Player() -{ - config.removeListener("visiblenames", this); -} - -void Player::logic() -{ - if (Net::getNetworkType() == ServerInfo::TMWATHENA) - { - switch (mAction) - { - case STAND: - case SIT: - case DEAD: - case HURT: - break; - - case WALK: - mFrame = (int) ((get_elapsed_time(mWalkTime) * 6) - / getWalkSpeed().x); - if (mFrame >= 6) - nextTile(); - break; - - case ATTACK: - int rotation = 0; - std::string particleEffect = ""; - int frames = 4; - - if (mEquippedWeapon && - mEquippedWeapon->getAttackType() == ACTION_ATTACK_BOW) - { - frames = 5; - } - - mFrame = (get_elapsed_time(mWalkTime) * frames) / mAttackSpeed; - - //attack particle effect - if (mEquippedWeapon) - particleEffect = mEquippedWeapon->getParticleEffect(); - - if (!particleEffect.empty() && Particle::enabled && mFrame == 1) - { - switch (mDirection) - { - case DOWN: rotation = 0; break; - case LEFT: rotation = 90; break; - case UP: rotation = 180; break; - case RIGHT: rotation = 270; break; - default: break; - } - Particle *p; - p = particleEngine->addEffect( - paths.getValue("particles", - "graphics/particles/") - + particleEffect, 0, 0, rotation); - controlParticle(p); - } - - if (mFrame >= frames) - nextTile(); - - break; - } - } - - Being::logic(); -} - -void Player::setSubtype(Uint16 subtype) -{ - Being::setSubtype(subtype); - - int id = -100 - subtype; - if (ItemDB::exists(id)) // Prevent showing errors when sprite doesn't exist - setSprite(Net::getCharHandler()->baseSprite(), id); - else - setSprite(Net::getCharHandler()->baseSprite(), -100); -} - -void Player::setGender(Gender gender) -{ - if (gender != mGender) - { - mGender = gender; - - // Reload all subsprites - for (unsigned int i = 0; i < mSprites.size(); i++) - { - if (mSpriteIDs.at(i) != 0) - setSprite(i, mSpriteIDs.at(i), mSpriteColors.at(i)); - } - } -} - -void Player::setGM(bool gm) -{ - mIsGM = gm; - - updateColors(); -} - -void Player::setSprite(int slot, int id, const std::string &color, - bool isWeapon) -{ - if (getType() == NPC) - return; - - assert(slot < Net::getCharHandler()->maxSprite()); - - // id = 0 means unequip - if (id == 0) - { - delete mSprites[slot]; - mSprites[slot] = NULL; - - if (isWeapon) - mEquippedWeapon = NULL; - } - else - { - std::string filename = ItemDB::get(id).getSprite(mGender); - AnimatedSprite *equipmentSprite = NULL; - - if (!filename.empty()) - { - if (!color.empty()) - filename += "|" + color; - - equipmentSprite = AnimatedSprite::load( - paths.getValue("sprites", "graphics/sprites/") + filename); - } - - if (equipmentSprite) - equipmentSprite->setDirection(getSpriteDirection()); - - if (mSprites[slot]) - delete mSprites[slot]; - - mSprites[slot] = equipmentSprite; - - if (isWeapon) - mEquippedWeapon = &ItemDB::get(id); - - setAction(mAction); - } - - mSpriteIDs[slot] = id; - mSpriteColors[slot] = color; -} - -void Player::setSpriteID(unsigned int slot, int id) -{ - setSprite(slot, id, mSpriteColors[slot]); -} - -void Player::setSpriteColor(unsigned int slot, const std::string &color) -{ - setSprite(slot, mSpriteIDs[slot], color); -} - -void Player::addGuild(Guild *guild) -{ - mGuilds[guild->getId()] = guild; - guild->addMember(mId, mName); - - if (this == player_node && socialWindow) - { - socialWindow->addTab(guild); - } -} - -void Player::removeGuild(int id) -{ - if (this == player_node && socialWindow) - { - socialWindow->removeTab(mGuilds[id]); - } - - mGuilds[id]->removeMember(mId); - mGuilds.erase(id); -} - -Guild *Player::getGuild(const std::string &guildName) const -{ - std::map<int, Guild*>::const_iterator itr, itr_end = mGuilds.end(); - for (itr = mGuilds.begin(); itr != itr_end; ++itr) - { - Guild *guild = itr->second; - if (guild->getName() == guildName) - { - return guild; - } - } - - return NULL; -} - -Guild *Player::getGuild(int id) const -{ - std::map<int, Guild*>::const_iterator itr; - itr = mGuilds.find(id); - if (itr != mGuilds.end()) - { - return itr->second; - } - - return NULL; -} - -const std::map<int, Guild*> &Player::getGuilds() const -{ - return mGuilds; -} - -void Player::clearGuilds() -{ - std::map<int, Guild*>::const_iterator itr, itr_end = mGuilds.end(); - for (itr = mGuilds.begin(); itr != itr_end; ++itr) - { - Guild *guild = itr->second; - - if (this == player_node && socialWindow) - socialWindow->removeTab(guild); - - guild->removeMember(mId); - } - - mGuilds.clear(); -} - -void Player::setParty(Party *party) -{ - if (party == mParty) - return; - - Party *old = mParty; - mParty = party; - - if (old) - { - old->removeMember(mId); - } - - if (party) - { - party->addMember(mId, mName); - } - - updateColors(); - - if (this == player_node && socialWindow) - { - if (old) - socialWindow->removeTab(old); - - if (party) - socialWindow->addTab(party); - } -} - -void Player::optionChanged(const std::string &value) -{ - if (value == "visiblenames") - { - setShowName(config.getValue("visiblenames", 1)); - } -} - -void Player::updateColors() -{ - mTextColor = &userPalette->getColor(Theme::PLAYER); - - if (mIsGM) - { - mTextColor = &userPalette->getColor(Theme::GM); - mNameColor = &userPalette->getColor(UserPalette::GM); - } - else if (mParty && mParty == player_node->getParty()) - { - mNameColor = &userPalette->getColor(UserPalette::PARTY); - } - else - { - mNameColor = &userPalette->getColor(UserPalette::PC); - } - - if (mDispName) - { - mDispName->setColor(mNameColor); - } -} diff --git a/src/player.h b/src/player.h deleted file mode 100644 index 4ac9d0eb..00000000 --- a/src/player.h +++ /dev/null @@ -1,175 +0,0 @@ -/* - * The Mana Client - * Copyright (C) 2004-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * - * This file is part of The Mana Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef PLAYER_H -#define PLAYER_H - -#include "being.h" - -class Graphics; -class Guild; -class Map; -class Party; - -enum Gender -{ - GENDER_MALE = 0, - GENDER_FEMALE = 1, - GENDER_UNSPECIFIED = 2 -}; - -/** - * A player being. Players have their name drawn beneath them. This class also - * implements player-specific loading of base sprite, hair sprite and equipment - * sprites. - */ -class Player : public Being -{ - public: - /** - * Constructor. - */ - Player(int id, int subtype, Map *map, bool isNPC = false); - - ~Player(); - - virtual void logic(); - - virtual Type getType() const { return PLAYER; } - - virtual void setSubtype(Uint16 subtype); - - /** - * Sets the gender of this being. - */ - virtual void setGender(Gender gender); - - Gender getGender() const { return mGender; } - - /** - * Whether or not this player is a GM. - */ - bool isGM() const { return mIsGM; } - - /** - * Triggers whether or not to show the name as a GM name. - */ - virtual void setGM(bool gm); - - /** - * Sets visible equipments for this player. - */ - virtual void setSprite(int slot, int id, - const std::string &color = "", - bool isWeapon = false); - - virtual void setSpriteID(unsigned int slot, int id); - - virtual void setSpriteColor(unsigned int slot, - const std::string &color = ""); - - /** - * Adds a guild to the player. - */ - void addGuild(Guild *guild); - - /** - * Removers a guild from the player. - */ - void removeGuild(int id); - - /** - * Returns a pointer to the specified guild. - */ - Guild *getGuild(const std::string &guildName) const; - - /** - * Returns a pointer to the guild with matching id. - */ - Guild *getGuild(int id) const; - - /** - * Returns all guilds the player is in. - */ - const std::map<int, Guild*> &getGuilds() const; - - /** - * Removes all guilds the player is in. - */ - void clearGuilds(); - - /** - * Get number of guilds the player belongs to. - */ - short getNumberOfGuilds() const { return mGuilds.size(); } - - bool isInParty() const { return mParty != NULL; } - - void setParty(Party *party); - - Party *getParty() const { return mParty; } - - /** - * Gets the way the character is blocked by other objects. - */ - virtual unsigned char getWalkMask() const - { return Map::BLOCKMASK_WALL | Map::BLOCKMASK_MONSTER; } - - /** - * Called when a option (set with config.addListener()) is changed - */ - virtual void optionChanged(const std::string &value); - - /* - * Sets the IP or an IP hash. - * The TMW-Athena server sends this information only to GMs. - */ - void setIp(int ip) { mIp = ip; } - - /** - * Returns the player's IP or an IP hash. - * Value is 0 if not set by the server. - */ - int getIp() const { return mIp; } - - protected: - /** - * Gets the way the monster blocks pathfinding for other objects. - */ - virtual Map::BlockType getBlockType() const - { return Map::BLOCKTYPE_CHARACTER; } - - virtual void updateColors(); - - Gender mGender; - std::vector<int> mSpriteIDs; - std::vector<std::string> mSpriteColors; - - // Character guild information - std::map<int, Guild*> mGuilds; - Party *mParty; - - bool mIsGM; - - int mIp; -}; - -#endif diff --git a/src/playerinfo.cpp b/src/playerinfo.cpp new file mode 100644 index 00000000..1915822f --- /dev/null +++ b/src/playerinfo.cpp @@ -0,0 +1,400 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "playerinfo.h" + +#include "client.h" +#include "equipment.h" +#include "event.h" +#include "inventory.h" +#include "listener.h" +#include "log.h" + +#include "resources/iteminfo.h" + +namespace PlayerInfo { + +class PlayerLogic; + +PlayerLogic *mListener = 0; + +PlayerInfoBackend mData; + +Inventory *mInventory = 0; +Equipment *mEquipment = 0; + +bool mStorageCount = 0; + +bool mNPCCount = 0; +bool mNPCPostCount = 0; + +BuySellState mBuySellState = BUYSELL_NONE; +bool mTrading = false; + +std::map<int, Special> mSpecials; +char mSpecialRechargeUpdateNeeded = 0; + +int mLevelProgress = 0; + +// --- Triggers --------------------------------------------------------------- + +void triggerAttr(int id, int old) +{ + Mana::Event event(EVENT_UPDATEATTRIBUTE); + event.setInt("id", id); + event.setInt("oldValue", old); + event.setInt("newValue", mData.mAttributes.find(id)->second); + event.trigger(CHANNEL_ATTRIBUTES); +} + +void triggerStat(int id, const std::string &changed, int old1, int old2 = 0) +{ + StatMap::iterator it = mData.mStats.find(id); + Mana::Event event(EVENT_UPDATESTAT); + event.setInt("id", id); + event.setInt("base", it->second.base); + event.setInt("mod", it->second.mod); + event.setInt("exp", it->second.exp); + event.setInt("expNeeded", it->second.expNeed); + event.setString("changed", changed); + event.setInt("oldValue1", old1); + event.setInt("oldValue2", old2); + event.trigger(CHANNEL_ATTRIBUTES); +} + +// --- Attributes ------------------------------------------------------------- + +int getAttribute(int id) +{ + IntMap::const_iterator it = mData.mAttributes.find(id); + if (it != mData.mAttributes.end()) + return it->second; + else + return 0; +} + +void setAttribute(int id, int value, bool notify) +{ + int old = mData.mAttributes[id]; + mData.mAttributes[id] = value; + if (notify) + triggerAttr(id, old); +} + +// --- Stats ------------------------------------------------------------------ + +int getStatBase(int id) +{ + StatMap::const_iterator it = mData.mStats.find(id); + if (it != mData.mStats.end()) + return it->second.base; + else + return 0; +} + +void setStatBase(int id, int value, bool notify) +{ + int old = mData.mStats[id].base; + mData.mStats[id].base = value; + if (notify) + triggerStat(id, "base", old); +} + +int getStatMod(int id) +{ + StatMap::const_iterator it = mData.mStats.find(id); + if (it != mData.mStats.end()) + return it->second.mod; + else + return 0; +} + +void setStatMod(int id, int value, bool notify) +{ + int old = mData.mStats[id].mod; + mData.mStats[id].mod = value; + if (notify) + triggerStat(id, "mod", old); +} + +int getStatEffective(int id) +{ + StatMap::const_iterator it = mData.mStats.find(id); + if (it != mData.mStats.end()) + return it->second.base + it->second.mod; + else + return 0; +} + +std::pair<int, int> getStatExperience(int id) +{ + StatMap::const_iterator it = mData.mStats.find(id); + int a, b; + if (it != mData.mStats.end()) + { + a = it->second.exp; + b = it->second.expNeed; + } + else + { + a = 0; + b = 0; + } + return std::pair<int, int>(a, b); +} + +void setStatExperience(int id, int have, int need, bool notify) +{ + int oldExp = mData.mStats[id].exp; + int oldExpNeed = mData.mStats[id].expNeed; + mData.mStats[id].exp = have; + mData.mStats[id].expNeed = need; + if (notify) + triggerStat(id, "exp", oldExp, oldExpNeed); +} + +// --- Inventory / Equipment -------------------------------------------------- + +Inventory *getInventory() +{ + return mInventory; +} + +void clearInventory() +{ + mEquipment->clear(); + mInventory->clear(); +} + +void setInventoryItem(int index, int id, int amount) +{ + mInventory->setItem(index, id, amount); +} + +Equipment *getEquipment() +{ + return mEquipment; +} + +Item *getEquipment(unsigned int slot) +{ + return mEquipment->getEquipment(slot); +} + +void setEquipmentBackend(Equipment::Backend *backend) +{ + mEquipment->setBackend(backend); +} + +int getStorageCount() +{ + return mStorageCount; +} + +void setStorageCount(int count) +{ + int old = mStorageCount; + mStorageCount = count; + + if (count != old) + { + Mana::Event event(EVENT_STORAGECOUNT); + event.setInt("oldCount", old); + event.setInt("newCount", count); + event.trigger(CHANNEL_STORAGE); + } +} + +// -- NPC --------------------------------------------------------------------- + +int getNPCInteractionCount() +{ + return mNPCCount; +} + +void setNPCInteractionCount(int count) +{ + int old = mNPCCount; + mNPCCount = count; + + if (count != old) + { + Mana::Event event(EVENT_NPCCOUNT); + event.setInt("oldCount", old); + event.setInt("newCount", count); + event.trigger(CHANNEL_NPC); + } +} + +int getNPCPostCount() +{ + return mNPCPostCount; +} + +void setNPCPostCount(int count) +{ + int old = mNPCPostCount; + mNPCPostCount = count; + + if (count != old) + { + Mana::Event event(EVENT_POSTCOUNT); + event.setInt("oldCount", old); + event.setInt("newCount", count); + event.trigger(CHANNEL_NPC); + } +} + +// -- Buy/Sell/Trade ---------------------------------------------------------- + +BuySellState getBuySellState() +{ + return mBuySellState; +} + +void setBuySellState(BuySellState buySellState) +{ + BuySellState old = mBuySellState; + mBuySellState = buySellState; + + if (buySellState != old) + { + Mana::Event event(EVENT_STATECHANGE); + event.setInt("oldState", old); + event.setInt("newState", buySellState); + event.trigger(CHANNEL_BUYSELL); + } +} + +bool isTrading() +{ + return mTrading; +} + +void setTrading(bool trading) +{ + bool notify = mTrading != trading; + mTrading = trading; + + if (notify) + { + Mana::Event event(EVENT_TRADING); + event.setBool("trading", trading); + event.trigger(CHANNEL_STATUS); + } +} + +// --- Specials --------------------------------------------------------------- + +void setSpecialStatus(int id, int current, int max, int recharge) +{ + logger->log("SpecialUpdate Skill #%d -- (%d/%d) -> %d", id, current, max, + recharge); + mSpecials[id].currentMana = current; + mSpecials[id].neededMana = max; + mSpecials[id].recharge = recharge; +} + +const SpecialsMap &getSpecialStatus() +{ + return mSpecials; +} + +// --- Misc ------------------------------------------------------------------- + +void setBackend(const PlayerInfoBackend &backend) +{ + mData = backend; +} + +bool isTalking() +{ + return getNPCInteractionCount() || getNPCPostCount() + || getBuySellState() != BUYSELL_NONE; +} + +void logic() +{ + if ((mSpecialRechargeUpdateNeeded%11) == 0) + { + mSpecialRechargeUpdateNeeded = 0; + for (SpecialsMap::iterator it = mSpecials.begin(), + it_end = mSpecials.end(); it != it_end; it++) + { + it->second.currentMana += it->second.recharge; + if (it->second.currentMana > it->second.neededMana) + { + it->second.currentMana = it->second.neededMana; + } + } + } + mSpecialRechargeUpdateNeeded++; +} + +class PlayerLogic : Mana::Listener +{ +public: + PlayerLogic() + { + listen(CHANNEL_CLIENT); + listen(CHANNEL_GAME); + } + + void event(Channels channel, const Mana::Event &event) + { + if (channel == CHANNEL_CLIENT) + { + if (event.getName() == EVENT_STATECHANGE) + { + int newState = event.getInt("newState"); + + if (newState == STATE_GAME) + { + if (mInventory == 0) + { + mInventory = new Inventory(Inventory::INVENTORY); + mEquipment = new Equipment(); + } + } + } + } + else if (channel == CHANNEL_GAME) + { + if (event.getName() == EVENT_DESTRUCTED) + { + delete mInventory; + delete mEquipment; + + mInventory = 0; + mEquipment = 0; + } + } + } +}; + +void init() +{ + if (mListener) + return; + + mListener = new PlayerLogic(); +} + +} // namespace PlayerInfo diff --git a/src/playerinfo.h b/src/playerinfo.h new file mode 100644 index 00000000..43e7da6e --- /dev/null +++ b/src/playerinfo.h @@ -0,0 +1,273 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PLAYERINFO_H +#define PLAYERINFO_H + +#include <map> +#include <string> + +/** + * Standard attributes for players. + */ +enum Attribute +{ + LEVEL, + HP, MAX_HP, + MP, MAX_MP, + EXP, EXP_NEEDED, + MONEY, + TOTAL_WEIGHT, MAX_WEIGHT, + SKILL_POINTS, + CHAR_POINTS, CORR_POINTS +}; + +/** + * Stat information storage structure. + */ +struct Stat +{ + int base; + int mod; + int exp; + int expNeed; +}; + +typedef std::map<int, int> IntMap; +typedef std::map<int, Stat> StatMap; + +/** + * Backend for core player information. + */ +struct PlayerInfoBackend +{ + IntMap mAttributes; + StatMap mStats; +}; + +class Equipment; +class Inventory; +class Item; + +enum BuySellState +{ + BUYSELL_NONE, + BUYSELL_CHOOSING, + BUYSELL_BUYING, + BUYSELL_SELLING +}; + +/** + * Special information storage structure. + */ +struct Special +{ + int currentMana; + int neededMana; + int recharge; +}; + +typedef std::map<int, Special> SpecialsMap; + +/** + * A database like namespace which holds global info about the localplayer + * + * NOTE: 'bool notify' is used to determine if a event is to be triggered. + */ +namespace PlayerInfo +{ + +// --- Attributes ------------------------------------------------------------- + + /** + * Returns the value of the given attribute. + */ + int getAttribute(int id); + + /** + * Changes the value of the given attribute. + */ + void setAttribute(int id, int value, bool notify = true); + +// --- Stats ------------------------------------------------------------------ + + /** + * Returns the base value of the given stat. + */ + int getStatBase(int id); + + /** + * Changes the base value of the given stat. + */ + void setStatBase(int id, int value, bool notify = true); + + /** + * Returns the modifier for the given stat. + */ + int getStatMod(int id); + + /** + * Changes the modifier for the given stat. + */ + void setStatMod(int id, int value, bool notify = true); + + /** + * Returns the current effective value of the given stat. Effective is base + * + mod + */ + int getStatEffective(int id); + + /** + * Changes the level of the given stat. + */ + void setStatLevel(int id, int value, bool notify = true); + + /** + * Returns the experience of the given stat. + */ + std::pair<int, int> getStatExperience(int id); + + /** + * Changes the experience of the given stat. + */ + void setStatExperience(int id, int have, int need, bool notify = true); + +// --- Inventory / Equipment / Storage ---------------------------------------- + + /** + * Returns the player's inventory. + */ + Inventory *getInventory(); + + /** + * Clears the player's inventory and equipment. + */ + void clearInventory(); + + /** + * Changes the inventory item at the given slot. + */ + void setInventoryItem(int index, int id, int amount); + + /** + * Returns the player's equipment. + */ + Equipment *getEquipment(); + + /** + * Returns the player's equipment at the given slot. + */ + Item *getEquipment(unsigned int slot); + + /** + * Returns the number of currently open storage windows. + */ + int getStorageCount(); + + /** + * Sets the number of currently open storage windows. + */ + void setStorageCount(int count); + +// -- NPC --------------------------------------------------------------------- + + /** + * Returns the number of currently open NPC interaction windows. + */ + int getNPCInteractionCount(); + + /** + * Sets the number of currently open NPC interaction windows. + */ + void setNPCInteractionCount(int count); + + /** + * Returns the number of currently open NPC post windows. + */ + int getNPCPostCount(); + + /** + * Sets the number of currently open NPC post windows. + */ + void setNPCPostCount(int count); + +// -- Buy/Sell/Trade ---------------------------------------------------------- + + /** + * Returns the current buy, sell, or related interaction the player is + * involved in. + */ + BuySellState getBuySellState(); + + /** + * Sets which buy, sell, or related interaction the player is currently + * involved in. + */ + void setBuySellState(BuySellState buySellState); + + /** + * Returns true if the player is involved in a trade at the moment, false + * otherwise. + */ + bool isTrading(); + + /** + * Sets whether the player is currently involved in trade or not. + */ + void setTrading(bool trading); + +// --- Specials --------------------------------------------------------------- + + /** + * Changes the status of the given special. + */ + void setSpecialStatus(int id, int current, int max, int recharge); + + /** + * Returns the status of the given special. + */ + const SpecialsMap &getSpecialStatus(); + +// --- Misc ------------------------------------------------------------------- + + /** + * Changes the internal PlayerInfoBackend reference; + */ + void setBackend(const PlayerInfoBackend &backend); + + /** + * Returns true if the player is involved in a NPC interaction, false + * otherwise. + */ + bool isTalking(); + + /** + * Does necessary updates every tick. + */ + void logic(); + + /** + * Initializes some internals. + */ + void init(); + +} // namespace PlayerInfo + +#endif diff --git a/src/playerrelations.cpp b/src/playerrelations.cpp index 14d9eb6b..8b6e6255 100644 --- a/src/playerrelations.cpp +++ b/src/playerrelations.cpp @@ -21,11 +21,10 @@ #include <algorithm> +#include "actorspritemanager.h" #include "being.h" -#include "beingmanager.h" #include "configuration.h" #include "graphics.h" -#include "player.h" #include "playerrelations.h" #include "utils/dtor.h" @@ -38,8 +37,6 @@ #define NAME "name" // constant for xml serialisation #define RELATION "relation" // constant for xml serialisation -#define IGNORE_EMOTE_TIME 100 - // (De)serialisation class class PlayerConfSerialiser : public ConfigurationListManager<std::pair<std::string, PlayerRelation *>, std::map<std::string, PlayerRelation *> *> @@ -214,7 +211,7 @@ unsigned int PlayerRelationsManager::checkPermissionSilently(const std::string & bool PlayerRelationsManager::hasPermission(Being *being, unsigned int flags) { - if (being->getType() == Being::PLAYER) + if (being->getType() == ActorSprite::PLAYER) return hasPermission(being->getName(), flags) == flags; return true; } @@ -230,9 +227,10 @@ bool PlayerRelationsManager::hasPermission(const std::string &name, // execute `ignore' strategy, if possible if (mIgnoreStrategy) { - Being *b = beingManager->findBeingByName(name, Being::PLAYER); - if (b && b->getType() == Being::PLAYER) - mIgnoreStrategy->ignore(static_cast<Player *>(b), rejections); + Being *b = actorSpriteManager->findBeingByName(name, + ActorSprite::PLAYER); + if (b && b->getType() == ActorSprite::PLAYER) + mIgnoreStrategy->ignore(b, rejections); } } @@ -313,7 +311,7 @@ public: mShortName = PLAYER_IGNORE_STRATEGY_NOP; } - virtual void ignore(Player *player, unsigned int flags) + virtual void ignore(Being *being, unsigned int flags) { } }; @@ -327,9 +325,9 @@ public: mShortName = "dotdotdot"; } - virtual void ignore(Player *player, unsigned int flags) + virtual void ignore(Being *being, unsigned int flags) { - player->setSpeech("...", 500); + being->setSpeech("...", 500); } }; @@ -343,44 +341,17 @@ public: mShortName = "blinkname"; } - virtual void ignore(Player *player, unsigned int flags) - { - player->flashName(200); - } -}; - -class PIS_emote : public PlayerIgnoreStrategy -{ -public: - PIS_emote(int emote_nr, const std::string &description, const std::string &shortname) : - mEmotion(emote_nr) + virtual void ignore(Being *being, unsigned int flags) { - mDescription = description; - mShortName = shortname; + being->flashName(200); } - - virtual void ignore(Player *player, unsigned int flags) - { - player->setEmote(mEmotion, IGNORE_EMOTE_TIME); - } -private: - int mEmotion; }; - - std::vector<PlayerIgnoreStrategy *> * PlayerRelationsManager::getPlayerIgnoreStrategies() { if (mIgnoreStrategies.size() == 0) { - // not initialised yet? - mIgnoreStrategies.push_back(new PIS_emote(FIRST_IGNORE_EMOTE, - _("Floating '...' bubble"), - PLAYER_IGNORE_STRATEGY_EMOTE0)); - mIgnoreStrategies.push_back(new PIS_emote(FIRST_IGNORE_EMOTE + 1, - _("Floating bubble"), - "emote1")); mIgnoreStrategies.push_back(new PIS_nothing()); mIgnoreStrategies.push_back(new PIS_dotdotdot()); mIgnoreStrategies.push_back(new PIS_blinkname()); diff --git a/src/playerrelations.h b/src/playerrelations.h index 3ff1e5fd..d6ca31ad 100644 --- a/src/playerrelations.h +++ b/src/playerrelations.h @@ -28,7 +28,6 @@ #include <vector> class Being; -class Player; struct PlayerRelation { @@ -73,7 +72,7 @@ public: /** * Handle the ignoring of the indicated action by the indicated player. */ - virtual void ignore(Player *player, unsigned int flags) = 0; + virtual void ignore(Being *being, unsigned int flags) = 0; }; class PlayerRelationsListener diff --git a/src/resources/beinginfo.cpp b/src/resources/beinginfo.cpp new file mode 100644 index 00000000..e8824391 --- /dev/null +++ b/src/resources/beinginfo.cpp @@ -0,0 +1,107 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "resources/beinginfo.h" + +#include "log.h" + +#include "utils/dtor.h" +#include "utils/gettext.h" + +BeingInfo *BeingInfo::Unknown = new BeingInfo; + +BeingInfo::BeingInfo(): + mName(_("unnamed")), + mTargetCursorSize(ActorSprite::TC_MEDIUM), + mWalkMask(Map::BLOCKMASK_WALL | Map::BLOCKMASK_CHARACTER + | Map::BLOCKMASK_MONSTER), + mBlockType(Map::BLOCKTYPE_CHARACTER) +{ + SpriteDisplay display; + display.sprites.push_back(SpriteReference::Empty); + + setDisplay(display); +} + +BeingInfo::~BeingInfo() +{ + delete_all(mSounds); + delete_all(mAttacks); + mSounds.clear(); +} + +void BeingInfo::setDisplay(SpriteDisplay display) +{ + mDisplay = display; +} + +void BeingInfo::setTargetCursorSize(const std::string &size) +{ + if (size == "small") + setTargetCursorSize(ActorSprite::TC_SMALL); + else if (size == "medium") + setTargetCursorSize(ActorSprite::TC_MEDIUM); + else if (size == "large") + setTargetCursorSize(ActorSprite::TC_LARGE); + else + { + logger->log("Unknown target cursor type \"%s\" for %s - using medium " + "sized one", size.c_str(), getName().c_str()); + setTargetCursorSize(ActorSprite::TC_MEDIUM); + } +} + +void BeingInfo::addSound(SoundEvent event, const std::string &filename) +{ + if (mSounds.find(event) == mSounds.end()) + { + mSounds[event] = new std::vector<std::string>; + } + + mSounds[event]->push_back("sfx/" + filename); +} + +const std::string &BeingInfo::getSound(SoundEvent event) const +{ + static std::string empty(""); + + SoundEvents::const_iterator i = mSounds.find(event); + return (i == mSounds.end()) ? empty : + i->second->at(rand() % i->second->size()); +} + +const Attack *BeingInfo::getAttack(int type) const +{ + static Attack *empty = new Attack(SpriteAction::ATTACK, "", ""); + + Attacks::const_iterator i = mAttacks.find(type); + return (i == mAttacks.end()) ? empty : (*i).second; +} + +void BeingInfo::addAttack(int id, std::string action, + const std::string &particleEffect, + const std::string &missileParticle) +{ + if (mAttacks[id]) + delete mAttacks[id]; + + mAttacks[id] = new Attack(action, particleEffect, missileParticle); +} diff --git a/src/resources/beinginfo.h b/src/resources/beinginfo.h new file mode 100644 index 00000000..52390976 --- /dev/null +++ b/src/resources/beinginfo.h @@ -0,0 +1,132 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef BEINGINFO_H +#define BEINGINFO_H + +#include "actorsprite.h" + +#include "resources/spritedef.h" + +#include <list> +#include <map> +#include <string> +#include <vector> + +struct Attack { + std::string action; + std::string particleEffect; + std::string missileParticle; + + Attack(std::string action, std::string particleEffect, + std::string missileParticle) + { + this->action = action; + this->particleEffect = particleEffect; + this->missileParticle = missileParticle; + } +}; + +typedef std::map<int, Attack*> Attacks; + +enum SoundEvent +{ + SOUND_EVENT_HIT, + SOUND_EVENT_MISS, + SOUND_EVENT_HURT, + SOUND_EVENT_DIE +}; + +typedef std::map<SoundEvent, std::vector<std::string>* > SoundEvents; + +/** + * Holds information about a certain type of monster. This includes the name + * of the monster, the sprite to display and the sounds the monster makes. + * + * @see MonsterDB + * @see NPCDB + */ +class BeingInfo +{ + public: + static BeingInfo *Unknown; + + BeingInfo(); + + ~BeingInfo(); + + void setName(const std::string &name) { mName = name; } + + const std::string &getName() const + { return mName; } + + void setDisplay(SpriteDisplay display); + + const SpriteDisplay &getDisplay() const + { return mDisplay; } + + void setTargetCursorSize(const std::string &size); + + void setTargetCursorSize(ActorSprite::TargetCursorSize targetSize) + { mTargetCursorSize = targetSize; } + + ActorSprite::TargetCursorSize getTargetCursorSize() const + { return mTargetCursorSize; } + + void addSound(SoundEvent event, const std::string &filename); + + const std::string &getSound(SoundEvent event) const; + + void addAttack(int id, std::string action, + const std::string &particleEffect, + const std::string &missileParticle); + + const Attack *getAttack(int type) const; + + void setWalkMask(unsigned char mask) + { mWalkMask = mask; } + + /** + * Gets the way the being is blocked by other objects + */ + unsigned char getWalkMask() const + { return mWalkMask; } + + void setBlockType(Map::BlockType blockType) + { mBlockType = blockType; } + + Map::BlockType getBlockType() const + { return mBlockType; } + + private: + SpriteDisplay mDisplay; + std::string mName; + ActorSprite::TargetCursorSize mTargetCursorSize; + SoundEvents mSounds; + Attacks mAttacks; + unsigned char mWalkMask; + Map::BlockType mBlockType; +}; + +typedef std::map<int, BeingInfo*> BeingInfos; +typedef BeingInfos::iterator BeingInfoIterator; + +#endif // BEINGINFO_H diff --git a/src/resources/emotedb.cpp b/src/resources/emotedb.cpp index c24e760b..c542f7d0 100644 --- a/src/resources/emotedb.cpp +++ b/src/resources/emotedb.cpp @@ -20,16 +20,20 @@ #include "resources/emotedb.h" -#include "animatedsprite.h" +#include "configuration.h" #include "log.h" +#include "imagesprite.h" + +#include "resources/resourcemanager.h" +#include "resources/image.h" +#include "resources/imageset.h" #include "utils/xml.h" -#include "configuration.h" namespace { - EmoteInfos mEmoteInfos; - EmoteInfo mUnknown; + Emotes mEmotes; + Emote mUnknown; bool mLoaded = false; int mLastEmote = 0; } @@ -39,13 +43,12 @@ void EmoteDB::load() if (mLoaded) unload(); - mLastEmote = 0; + mUnknown.name = "unknown"; + mUnknown.effect = 0; + mUnknown.sprite = new ImageSprite( + ResourceManager::getInstance()->getImage("graphics/sprites/error.png")); - EmoteSprite *unknownSprite = new EmoteSprite; - unknownSprite->sprite = AnimatedSprite::load( - paths.getValue("spriteErrorFile", "error.xml") ); - unknownSprite->name = "unknown"; - mUnknown.sprites.push_back(unknownSprite); + mLastEmote = 0; logger->log("Initializing emote database..."); @@ -71,28 +74,46 @@ void EmoteDB::load() continue; } - EmoteInfo *currentInfo = new EmoteInfo; + Emote *currentEmote = new Emote; + + currentEmote->name = XML::getProperty(emoteNode, "name", "unknown"); + currentEmote->effect = XML::getProperty(emoteNode, "effectid", -1); + + if (currentEmote->effect == -1) + { + logger->log("Emote Database: Warning: Emote with no attached effect!"); + delete currentEmote; + continue; + } + + const std::string imageName = XML::getProperty(emoteNode, "image", ""); + const int width = XML::getProperty(emoteNode, "width", 0); + const int height = XML::getProperty(emoteNode, "height", 0); - for_each_xml_child_node(spriteNode, emoteNode) + if (imageName.empty() || !(width > 0) || !(height > 0)) { - if (xmlStrEqual(spriteNode->name, BAD_CAST "sprite")) - { - EmoteSprite *currentSprite = new EmoteSprite; - std::string file = paths.getValue("sprites", - "graphics/sprites/") - + (std::string) (const char*) - spriteNode->xmlChildrenNode->content; - currentSprite->sprite = AnimatedSprite::load(file, - XML::getProperty(spriteNode, "variant", 0)); - currentInfo->sprites.push_back(currentSprite); - } - else if (xmlStrEqual(spriteNode->name, BAD_CAST "particlefx")) - { - std::string particlefx = (const char*) spriteNode->xmlChildrenNode->content; - currentInfo->particles.push_back(particlefx); - } + logger->log("Emote Database: Warning: Emote with bad imageset values"); + delete currentEmote; + continue; } - mEmoteInfos[id] = currentInfo; + + ImageSet *is = ResourceManager::getInstance()->getImageSet(imageName, + width, + height); + if (!is || !(is->size() > 0)) + { + logger->log("Emote Database: Error loading imageset"); + delete is; + delete currentEmote; + continue; + } + else + { + // For now we just use the first image in the animation + currentEmote->sprite = new ImageSprite(is->get(0)); + } + + mEmotes[id] = currentEmote; if (id > mLastEmote) mLastEmote = id; } @@ -102,36 +123,22 @@ void EmoteDB::load() void EmoteDB::unload() { - for (EmoteInfos::const_iterator i = mEmoteInfos.begin(); - i != mEmoteInfos.end(); - i++) + Emotes::iterator i; + for (i = mEmotes.begin(); i != mEmotes.end(); i++) { - while (!i->second->sprites.empty()) - { - delete i->second->sprites.front()->sprite; - delete i->second->sprites.front(); - i->second->sprites.pop_front(); - } delete i->second; } - mEmoteInfos.clear(); - - while (!mUnknown.sprites.empty()) - { - delete mUnknown.sprites.front()->sprite; - delete mUnknown.sprites.front(); - mUnknown.sprites.pop_front(); - } + mEmotes.clear(); mLoaded = false; } -const EmoteInfo *EmoteDB::get(int id) +const Emote *EmoteDB::get(int id) { - EmoteInfos::const_iterator i = mEmoteInfos.find(id); + Emotes::const_iterator i = mEmotes.find(id); - if (i == mEmoteInfos.end()) + if (i == mEmotes.end()) { logger->log("EmoteDB: Warning, unknown emote ID %d requested", id); return &mUnknown; @@ -142,12 +149,6 @@ const EmoteInfo *EmoteDB::get(int id) } } -const AnimatedSprite *EmoteDB::getAnimation(int id) -{ - const EmoteInfo *info = get(id); - return info->sprites.front()->sprite; -} - const int &EmoteDB::getLast() { return mLastEmote; diff --git a/src/resources/emotedb.h b/src/resources/emotedb.h index 3d80ce8f..a2887a74 100644 --- a/src/resources/emotedb.h +++ b/src/resources/emotedb.h @@ -25,21 +25,16 @@ #include <map> #include <string> -class AnimatedSprite; +class ImageSprite; -struct EmoteSprite +struct Emote { - const AnimatedSprite *sprite; std::string name; + ImageSprite *sprite; + int effect; }; -struct EmoteInfo -{ - std::list<EmoteSprite*> sprites; - std::list<std::string> particles; -}; - -typedef std::map<int, EmoteInfo*> EmoteInfos; +typedef std::map<int, Emote*> Emotes; /** * Emote information database. @@ -50,13 +45,11 @@ namespace EmoteDB void unload(); - const EmoteInfo *get(int id); - - const AnimatedSprite *getAnimation(int id); + const Emote *get(int id); const int &getLast(); - typedef EmoteInfos::iterator EmoteInfosIterator; + typedef Emotes::iterator EmotesIterator; } #endif diff --git a/src/resources/image.h b/src/resources/image.h index 1db52ca0..9fdc0997 100644 --- a/src/resources/image.h +++ b/src/resources/image.h @@ -22,8 +22,6 @@ #ifndef IMAGE_H #define IMAGE_H -#include "main.h" - #include "resources/resource.h" #include <SDL.h> diff --git a/src/resources/imageset.cpp b/src/resources/imageset.cpp index bd28cd6e..d33fac32 100644 --- a/src/resources/imageset.cpp +++ b/src/resources/imageset.cpp @@ -27,11 +27,11 @@ #include "utils/dtor.h" -ImageSet::ImageSet(Image *img, int width, int height) +ImageSet::ImageSet(Image *img, int width, int height, int margin, int spacing) { - for (int y = 0; y + height <= img->getHeight(); y += height) + for (int y = margin; y + height <= img->getHeight() - margin; y += height + spacing) { - for (int x = 0; x + width <= img->getWidth(); x += width) + for (int x = margin; x + width <= img->getWidth() - margin; x += width + spacing) { mImages.push_back(img->getSubImage(x, y, width, height)); } diff --git a/src/resources/imageset.h b/src/resources/imageset.h index 763b9880..3289788f 100644 --- a/src/resources/imageset.h +++ b/src/resources/imageset.h @@ -37,7 +37,7 @@ class ImageSet : public Resource /** * Cuts the passed image in a grid of sub images. */ - ImageSet(Image *img, int w, int h); + ImageSet(Image *img, int w, int h, int margin = 0, int spacing = 0); /** * Destructor. diff --git a/src/resources/itemdb.cpp b/src/resources/itemdb.cpp index cb91e8d3..2271abef 100644 --- a/src/resources/itemdb.cpp +++ b/src/resources/itemdb.cpp @@ -23,6 +23,8 @@ #include "log.h" +#include "net/net.h" + #include "resources/iteminfo.h" #include "resources/resourcemanager.h" @@ -36,29 +38,7 @@ #include <cassert> -namespace -{ - ItemDB::ItemInfos mItemInfos; - ItemDB::NamedItemInfos mNamedItemInfos; - ItemInfo *mUnknown; - bool mLoaded = false; -} - -// Forward declarations -static void loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node); -static void loadSoundRef(ItemInfo *itemInfo, xmlNodePtr node); - -static char const *const fields[][2] = -{ - { "attack", N_("Attack %+d") }, - { "defense", N_("Defense %+d") }, - { "hp", N_("HP %+d") }, - { "mp", N_("MP %+d") } -}; - -static std::list<ItemDB::Stat> extraStats; - -void ItemDB::setStatsList(const std::list<ItemDB::Stat> &stats) +void setStatsList(const std::list<ItemStat> &stats) { extraStats = stats; } @@ -84,119 +64,172 @@ static ItemType itemTypeFromString(const std::string &name, int id = 0) else return ITEM_UNUSABLE; } -static WeaponType weaponTypeFromString(const std::string &name, int id = 0) +/* + * Common itemDB functions + */ + +bool ItemDB::exists(int id) { - if (name=="knife") return WPNTYPE_KNIFE; - else if (name=="sword") return WPNTYPE_SWORD; - else if (name=="polearm") return WPNTYPE_POLEARM; - else if (name=="staff") return WPNTYPE_STAFF; - else if (name=="whip") return WPNTYPE_WHIP; - else if (name=="bow") return WPNTYPE_BOW; - else if (name=="shooting") return WPNTYPE_SHOOTING; - else if (name=="mace") return WPNTYPE_MACE; - else if (name=="axe") return WPNTYPE_AXE; - else if (name=="thrown") return WPNTYPE_THROWN; - - else return WPNTYPE_NONE; + assert(mLoaded); + + ItemInfos::const_iterator i = mItemInfos.find(id); + + return i != mItemInfos.end(); } -static std::string normalized(const std::string &name) +const ItemInfo &ItemDB::get(int id) { - std::string normalized = name; - return toLower(trim(normalized));; + assert(mLoaded); + + ItemInfos::const_iterator i = mItemInfos.find(id); + + if (i == mItemInfos.end()) + { + logger->log("ItemDB: Warning, unknown item ID# %d", id); + return *mUnknown; + } + + return *(i->second); } -void ItemDB::load() +const ItemInfo &ItemDB::get(const std::string &name) { - if (mLoaded) - unload(); + assert(mLoaded); - logger->log("Initializing item database..."); + NamedItemInfos::const_iterator i = mNamedItemInfos.find(normalize(name)); - mUnknown = new ItemInfo; - mUnknown->setName(_("Unknown item")); - mUnknown->setImageName(""); - std::string errFile = paths.getValue("spriteErrorFile", "error.xml"); - mUnknown->setSprite(errFile, GENDER_MALE); - mUnknown->setSprite(errFile, GENDER_FEMALE); + if (i == mNamedItemInfos.end()) + { + if (!name.empty()) + { + logger->log("ItemDB: Warning, unknown item name \"%s\"", + name.c_str()); + } + return *mUnknown; + } - XML::Document doc("items.xml"); - xmlNodePtr rootNode = doc.rootNode(); + return *(i->second); +} - if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "items")) +void ItemDB::loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node) +{ + std::string gender = XML::getProperty(node, "gender", "unisex"); + std::string filename = (const char*) node->xmlChildrenNode->content; + + if (gender == "male" || gender == "unisex") + { + itemInfo->setSprite(filename, GENDER_MALE); + } + if (gender == "female" || gender == "unisex") + { + itemInfo->setSprite(filename, GENDER_FEMALE); + } +} + +void ItemDB::loadSoundRef(ItemInfo *itemInfo, xmlNodePtr node) +{ + std::string event = XML::getProperty(node, "event", ""); + std::string filename = (const char*) node->xmlChildrenNode->content; + + if (event == "hit") { - logger->error("ItemDB: Error while loading items.xml!"); + itemInfo->addSound(EQUIP_EVENT_HIT, filename); } + else if (event == "strike") + { + itemInfo->addSound(EQUIP_EVENT_STRIKE, filename); + } + else + { + logger->log("ItemDB: Ignoring unknown sound event '%s'", + event.c_str()); + } +} - for_each_xml_child_node(node, rootNode) +void ItemDB::loadFloorSprite(SpriteDisplay *display, xmlNodePtr floorNode) +{ + for_each_xml_child_node(spriteNode, floorNode) { + if (xmlStrEqual(spriteNode->name, BAD_CAST "sprite")) + { + SpriteReference *currentSprite = new SpriteReference; + currentSprite->sprite = (const char*)spriteNode->xmlChildrenNode->content; + currentSprite->variant = XML::getProperty(spriteNode, "variant", 0); + display->sprites.push_back(currentSprite); + } + else if (xmlStrEqual(spriteNode->name, BAD_CAST "particlefx")) + { + std::string particlefx = (const char*)spriteNode->xmlChildrenNode->content; + display->particles.push_back(particlefx); + } + } +} + +void ItemDB::unload() +{ + logger->log("Unloading item database..."); + + delete mUnknown; + mUnknown = NULL; + + delete_all(mItemInfos); + mItemInfos.clear(); + mNamedItemInfos.clear(); + mLoaded = false; +} + +void ItemDB::loadCommonRef(ItemInfo *itemInfo, xmlNodePtr node) +{ if (!xmlStrEqual(node->name, BAD_CAST "item")) - continue; + return; int id = XML::getProperty(node, "id", 0); - if (id == 0) + if (!id) { - logger->log("ItemDB: Invalid or missing item ID in items.xml!"); - continue; + logger->log("ItemDB: Invalid or missing item Id in " + ITEMS_DB_FILE "!"); + return; } else if (mItemInfos.find(id) != mItemInfos.end()) - { - logger->log("ItemDB: Redefinition of item ID %d", id); - } + logger->log("ItemDB: Redefinition of item Id %d", id); - std::string typeStr = XML::getProperty(node, "type", "other"); - int weight = XML::getProperty(node, "weight", 0); int view = XML::getProperty(node, "view", 0); std::string name = XML::getProperty(node, "name", ""); std::string image = XML::getProperty(node, "image", ""); std::string description = XML::getProperty(node, "description", ""); - int weaponType = weaponTypeFromString(XML::getProperty(node, "weapon-type", "")); + std::string attackAction = XML::getProperty(node, "attack-action", ""); int attackRange = XML::getProperty(node, "attack-range", 0); std::string missileParticle = XML::getProperty(node, "missile-particle", ""); - ItemInfo *itemInfo = new ItemInfo; - itemInfo->setId(id); - itemInfo->setImageName(image); - itemInfo->setName(name.empty() ? _("unnamed") : name); - itemInfo->setDescription(description); - itemInfo->setType(itemTypeFromString(typeStr)); - itemInfo->setView(view); - itemInfo->setWeight(weight); - itemInfo->setWeaponType(weaponType); - itemInfo->setAttackRange(attackRange); - itemInfo->setMissileParticle(missileParticle); + // Load Ta Item Type + std::string typeStr = XML::getProperty(node, "type", "other"); + itemInfo->mType = itemTypeFromString(typeStr); - std::string effect; - for (int i = 0; i < int(sizeof(fields) / sizeof(fields[0])); ++i) - { - int value = XML::getProperty(node, fields[i][0], 0); - if (!value) continue; - if (!effect.empty()) effect += " / "; - effect += strprintf(gettext(fields[i][1]), value); - } - for (std::list<Stat>::iterator it = extraStats.begin(); - it != extraStats.end(); it++) - { - int value = XML::getProperty(node, it->tag.c_str(), 0); - if (!value) continue; - if (!effect.empty()) effect += " / "; - effect += strprintf(it->format.c_str(), value); - } - std::string temp = XML::getProperty(node, "effect", ""); - if (!effect.empty() && !temp.empty()) - effect += " / "; - effect += temp; - itemInfo->setEffect(effect); + int weight = XML::getProperty(node, "weight", 0); + itemInfo->mWeight = weight > 0 ? weight : 0; + + SpriteDisplay display; + display.image = image; + + itemInfo->mId = id; + itemInfo->mName = name; + itemInfo->mDescription = description; + itemInfo->mView = view; + itemInfo->mWeight = weight; + itemInfo->setAttackAction(attackAction); + itemInfo->mAttackRange = attackRange; + itemInfo->setMissileParticle(missileParticle); + // Load <sprite>, <sound>, and <floor> for_each_xml_child_node(itemChild, node) { if (xmlStrEqual(itemChild->name, BAD_CAST "sprite")) { std::string attackParticle = XML::getProperty( itemChild, "particle-effect", ""); - itemInfo->setParticleEffect(attackParticle); + itemInfo->mParticle = attackParticle; loadSpriteRef(itemInfo, itemChild); } @@ -204,136 +237,306 @@ void ItemDB::load() { loadSoundRef(itemInfo, itemChild); } - } - - mItemInfos[id] = itemInfo; - if (!name.empty()) - { - std::string temp = normalized(name); - - NamedItemInfos::const_iterator itr = mNamedItemInfos.find(temp); - if (itr == mNamedItemInfos.end()) - { - mNamedItemInfos[temp] = itemInfo; - } - else + else if (xmlStrEqual(itemChild->name, BAD_CAST "floor")) { - logger->log("ItemDB: Duplicate name of item found item %d", id); + loadFloorSprite(&display, itemChild); } + } - if (weaponType > 0) - if (attackRange == 0) - logger->log("ItemDB: Missing attack range from weapon %i!", id); + // If the item has got a floor image, we bind the good reference. + itemInfo->mDisplay = display; +} -#define CHECK_PARAM(param, error_value) \ - if (param == error_value) \ - logger->log("ItemDB: Missing " #param " attribute for item %i!",id) +void ItemDB::addItem(ItemInfo *itemInfo) +{ + std::string itemName = itemInfo->mName; + itemInfo->mName = itemName.empty() ? _("unnamed") : itemName; + mItemInfos[itemInfo->mId] = itemInfo; + if (!itemName.empty()) + { + std::string temp = normalize(itemName); - if (id >= 0) - { - CHECK_PARAM(name, ""); - CHECK_PARAM(description, ""); - CHECK_PARAM(image, ""); - } - // CHECK_PARAM(effect, ""); - // CHECK_PARAM(type, 0); - // CHECK_PARAM(weight, 0); - // CHECK_PARAM(slot, 0); + NamedItemInfos::const_iterator itr = mNamedItemInfos.find(temp); + if (itr == mNamedItemInfos.end()) + mNamedItemInfos[temp] = itemInfo; + else + logger->log("ItemDB: Duplicate name (%s) for item id %d found.", + temp.c_str(), itemInfo->mId); -#undef CHECK_PARAM } - - mLoaded = true; } -void ItemDB::unload() +template <class T> +static void checkParameter(int id, const T param, const T errorValue) { - logger->log("Unloading item database..."); + if (param == errorValue) + { + std::stringstream errMsg; + errMsg << "ItemDB: Missing " << param << " attribute for item id " + << id << "!"; + logger->log(errMsg.str().c_str()); + } +} - delete mUnknown; - mUnknown = NULL; +void ItemDB::checkItemInfo(ItemInfo* itemInfo) +{ + int id = itemInfo->mId; + if (!itemInfo->getAttackAction().empty()) + if (itemInfo->mAttackRange == 0) + logger->log("ItemDB: Missing attack range from weapon %i!", id); - delete_all(mItemInfos); - mItemInfos.clear(); - mNamedItemInfos.clear(); - mLoaded = false; + if (id >= 0) + { + checkParameter(id, itemInfo->mName, std::string("")); + checkParameter(id, itemInfo->mDescription, std::string("")); + checkParameter(id, itemInfo->mDisplay.image, std::string("")); + checkParameter(id, itemInfo->mWeight, 0); + } } -bool ItemDB::exists(int id) -{ - assert(mLoaded); +namespace TmwAthena { - ItemInfos::const_iterator i = mItemInfos.find(id); +// Description fields used by TaItemDB *itemInfo->mEffect. - return i != mItemInfos.end(); -} +static char const *const fields[][2] = +{ + { "attack", N_("Attack %+d") }, + { "defense", N_("Defense %+d") }, + { "hp", N_("HP %+d") }, + { "mp", N_("MP %+d") } +}; -const ItemInfo &ItemDB::get(int id) +void TaItemDB::load() { - assert(mLoaded); + if (mLoaded) + unload(); - ItemInfos::const_iterator i = mItemInfos.find(id); + logger->log("Initializing TmwAthena item database..."); - if (i == mItemInfos.end()) + mUnknown = new TaItemInfo; + mUnknown->mName = _("Unknown item"); + mUnknown->mDisplay = SpriteDisplay(); + std::string errFile = paths.getStringValue("spriteErrorFile"); + mUnknown->setSprite(errFile, GENDER_MALE); + mUnknown->setSprite(errFile, GENDER_FEMALE); + + XML::Document doc(ITEMS_DB_FILE); + xmlNodePtr rootNode = doc.rootNode(); + + if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "items")) { - logger->log("ItemDB: Warning, unknown item ID# %d", id); - return *mUnknown; + logger->error("ItemDB: Error while loading " ITEMS_DB_FILE "!"); + return; } - return *(i->second); -} + for_each_xml_child_node(node, rootNode) + { + TaItemInfo *itemInfo = new TaItemInfo; -const ItemInfo &ItemDB::get(const std::string &name) -{ - assert(mLoaded); + loadCommonRef(itemInfo, node); - NamedItemInfos::const_iterator i = mNamedItemInfos.find(normalized(name)); + // Everything not unusable or usable is equippable by the Ta type system. + itemInfo->mEquippable = itemInfo->mType != ITEM_UNUSABLE + && itemInfo->mType != ITEM_USABLE; - if (i == mNamedItemInfos.end()) - { - if (!name.empty()) + // Load nano description + std::vector<std::string> effect; + for (int i = 0; i < int(sizeof(fields) / sizeof(fields[0])); ++i) { - logger->log("ItemDB: Warning, unknown item name \"%s\"", - name.c_str()); + int value = XML::getProperty(node, fields[i][0], 0); + if (!value) + continue; + effect.push_back(strprintf(gettext(fields[i][1]), value)); } - return *mUnknown; + for (std::list<ItemStat>::iterator it = extraStats.begin(); + it != extraStats.end(); it++) + { + int value = XML::getProperty(node, it->mTag.c_str(), 0); + if (!value) + continue; + effect.push_back(strprintf(it->mFormat.c_str(), value)); + } + std::string temp = XML::getProperty(node, "effect", ""); + if (!temp.empty()) + effect.push_back(temp); + + itemInfo->mEffect = effect; + + checkItemInfo(itemInfo); + + addItem(itemInfo); } - return *(i->second); + checkHairWeaponsRacesSpecialIds(); + + mLoaded = true; } -void loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node) +void TaItemDB::checkItemInfo(ItemInfo* itemInfo) { - std::string gender = XML::getProperty(node, "gender", "unisex"); - std::string filename = (const char*) node->xmlChildrenNode->content; + ItemDB::checkItemInfo(itemInfo); - if (gender == "male" || gender == "unisex") - { - itemInfo->setSprite(filename, GENDER_MALE); - } - if (gender == "female" || gender == "unisex") + // Check for unusable items? + //checkParameter(id, itemInfo->mType, 0); +} + +}; // namespace TmwAthena + +namespace ManaServ { + +static std::map<std::string, const char* > triggerTable; + +static void initTriggerTable() +{ + if (triggerTable.empty()) { - itemInfo->setSprite(filename, GENDER_FEMALE); + // FIXME: This should ideally be softcoded via XML or similar. + logger->log("Initializing ManaServ trigger table..."); + triggerTable["existence"] = " when it is in the inventory"; + triggerTable["activation"] = " upon activation"; + triggerTable["equip"] = " upon successful equip"; + triggerTable["leave-inventory"] = " when it leaves the inventory"; + triggerTable["unequip"] = " when it is unequipped"; + triggerTable["equip-change"] = " when it changes the way it is equipped"; } } -void loadSoundRef(ItemInfo *itemInfo, xmlNodePtr node) +void ManaServItemDB::load() { - std::string event = XML::getProperty(node, "event", ""); - std::string filename = (const char*) node->xmlChildrenNode->content; + if (mLoaded) + unload(); - if (event == "hit") - { - itemInfo->addSound(EQUIP_EVENT_HIT, filename); - } - else if (event == "strike") + // Initialize the trigger table for effect descriptions + initTriggerTable(); + + logger->log("Initializing ManaServ item database..."); + + mUnknown = new ManaServItemInfo; + mUnknown->mName = _("Unknown item"); + mUnknown->mDisplay = SpriteDisplay(); + std::string errFile = paths.getStringValue("spriteErrorFile"); + mUnknown->setSprite(errFile, GENDER_MALE); + mUnknown->setSprite(errFile, GENDER_FEMALE); + + XML::Document doc(ITEMS_DB_FILE); + xmlNodePtr rootNode = doc.rootNode(); + + if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "items")) { - itemInfo->addSound(EQUIP_EVENT_STRIKE, filename); + logger->log("ItemDB: Error while loading " ITEMS_DB_FILE "!"); + return; } - else + + for_each_xml_child_node(node, rootNode) { - logger->log("ItemDB: Ignoring unknown sound event '%s'", - event.c_str()); + ManaServItemInfo *itemInfo = new ManaServItemInfo; + + loadCommonRef(itemInfo, node); + + // We default eqippable and activatable to false as their actual value will be set + // within the <equip> and <effect> sub-nodes.. + itemInfo->mActivatable = false; + itemInfo->mEquippable = false; + + // Load <equip>, and <effect> sub nodes. + std::vector<std::string> effect; + for_each_xml_child_node(itemChild, node) + { + if (xmlStrEqual(itemChild->name, BAD_CAST "equip")) + { + // The fact that there is a way to equip is enough. + // Discard any details, but mark the item as equippable. + itemInfo->mEquippable = true; + } + else if (xmlStrEqual(itemChild->name, BAD_CAST "effect")) + { + std::string trigger = XML::getProperty( + itemChild, "trigger", ""); + if (trigger.empty()) + { + logger->log("Found empty trigger effect label, skipping."); + continue; + } + + if (trigger == "activation") + itemInfo->mActivatable = true; + + std::map<std::string, const char* >::const_iterator triggerLabel = + triggerTable.find(trigger); + if (triggerLabel == triggerTable.end()) + { + logger->log("Warning: unknown trigger %s in item %d!", + trigger.c_str(), itemInfo->mId); + continue; + } + + for_each_xml_child_node(effectChild, itemChild) + { + if (xmlStrEqual(effectChild->name, BAD_CAST "modifier")) + { + std::string attribute = XML::getProperty( + effectChild, "attribute", ""); + double value = XML::getFloatProperty( + effectChild, "value", 0.0); + int duration = XML::getProperty( + effectChild, "duration", 0); + if (attribute.empty() || !value) + { + logger->log("Warning: incomplete modifier definition, skipping."); + continue; + } + std::list<ItemStat>::const_iterator + it = extraStats.begin(), + it_end = extraStats.end(); + while (it != it_end && !(*it == attribute)) + ++it; + if (it == extraStats.end()) + { + logger->log("Warning: unknown modifier tag %s, skipping.", attribute.c_str()); + continue; + } + effect.push_back( + strprintf(strprintf( + duration ? + strprintf("%%s%%s. This effect lasts %d ticks.", duration).c_str() + : "%s%s.", it->mFormat.c_str(), triggerLabel->second).c_str(), value)); + } + else if (xmlStrEqual(effectChild->name, BAD_CAST "modifier")) + effect.push_back(strprintf("Provides an autoattack%s.", + triggerLabel->second)); + else if (xmlStrEqual(effectChild->name, BAD_CAST "consumes")) + effect.push_back(strprintf("This will be consumed%s.", + triggerLabel->second)); + else if (xmlStrEqual(effectChild->name, BAD_CAST "label")) + effect.push_back( + (const char*)effectChild->xmlChildrenNode->content); + } + } + // Set Item Type based on subnodes info + // TODO: Improve it once the itemTypes are loaded through xml + itemInfo->mType = ITEM_UNUSABLE; + if (itemInfo->mActivatable) + itemInfo->mType = ITEM_USABLE; + else if (itemInfo->mEquippable) + itemInfo->mType = ITEM_EQUIPMENT_TORSO; + } // end for_each_xml_child_node(itemChild, node) + + itemInfo->mEffect = effect; + + checkItemInfo(itemInfo); + + addItem(itemInfo); } + + mLoaded = true; +} + +void ManaServItemDB::checkItemInfo(ItemInfo* itemInfo) +{ + ItemDB::checkItemInfo(itemInfo); + + // Add specific Manaserv checks here } + +}; // namespace ManaServ diff --git a/src/resources/itemdb.h b/src/resources/itemdb.h index be023073..e80f2cd0 100644 --- a/src/resources/itemdb.h +++ b/src/resources/itemdb.h @@ -26,45 +26,185 @@ #include <map> #include <string> +#include "utils/xml.h" + +#define ITEMS_DB_FILE "items.xml" + class ItemInfo; +class SpriteDisplay; + +// Used to make the compiler uderstand the iteminfo friendship. +namespace TmwAthena { class TaItemDB; }; +namespace ManaServ { class ManaServItemDB; }; /** - * Item information database. + * Nano-description functions */ -namespace ItemDB +class ItemStat { - /** - * Loads the item data from <code>items.xml</code>. - */ - void load(); + friend class ItemDB; + friend class TmwAthena::TaItemDB; + friend class ManaServ::ManaServItemDB; - /** - * Frees item data. - */ - void unload(); + public: + ItemStat(const std::string &tag, + const std::string &format): + mTag(tag), mFormat(format) {} - bool exists(int id); + bool operator ==(std::string &name) const + { return mTag == name; } - const ItemInfo &get(int id); - const ItemInfo &get(const std::string &name); + private: + std::string mTag; + std::string mFormat; +}; - struct Stat - { - Stat(const std::string &tag, - const std::string &format): - tag(tag), - format(format) +// Used to set nano-description +static std::list<ItemStat> extraStats; +void setStatsList(const std::list<ItemStat> &stats); + +/** + * Item information database generic definition. + */ +class ItemDB +{ + public: + ItemDB() : + mUnknown(0), + mLoaded(false) + {} + + ~ItemDB() + {} + + /** + * Loads the item data from <code>items.xml</code>. + */ + virtual void load() = 0; + + /** + * Frees item data. + */ + virtual void unload(); + + /** + * Tells whether the item database is loaded. + */ + bool isLoaded() const + { return mLoaded; } + + bool exists(int id); + + const ItemInfo &get(int id); + const ItemInfo &get(const std::string &name); + + protected: + /** + * Permits to load item definitions which are common + * for each protocols to avoid code duplication. + */ + void loadCommonRef(ItemInfo *itemInfo, xmlNodePtr node); + + /** + * Checks the items parameters consistency. + */ + virtual void checkItemInfo(ItemInfo* itemInfo); + + /** + * Register the item to mItemInfos and mNamedItemsInfos + */ + void addItem(ItemInfo *itemInfo); + + // Default unknown reference + ItemInfo *mUnknown; + + bool mLoaded; + + private: + /** + * Loads the sprite references contained in a <sprite> tag. + */ + void loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node); + + /** + * Loads the sound references contained in a <sound> tag. + */ + void loadSoundRef(ItemInfo *itemInfo, xmlNodePtr node); + + /** + * Loads the floor item references contained in a <floor> tag. + */ + void loadFloorSprite(SpriteDisplay *display, xmlNodePtr node); + + // Items database + typedef std::map<int, ItemInfo*> ItemInfos; + typedef std::map<std::string, ItemInfo*> NamedItemInfos; + + ItemInfos mItemInfos; + NamedItemInfos mNamedItemInfos; +}; + +namespace TmwAthena { + +class TaItemInfo; + +/** + * Item information database TmwAthena specific class. + */ +class TaItemDB: public ItemDB +{ + public: + TaItemDB() : ItemDB() + { load(); } + + ~TaItemDB() + { unload(); } + + /** + * Loads the item data from <code>items.xml</code>. + */ + void load(); + + private: + /** + * Check items id specific hard limits and log errors found. + * TODO: Do it. + */ + void checkHairWeaponsRacesSpecialIds() {} - std::string tag; - std::string format; - }; + void checkItemInfo(ItemInfo* itemInfo); +}; + +}; // namespace TmwAthena + +namespace ManaServ { + +class ManaServItemInfo; + +/** + * Item information database TmwAthena specific class. + */ +class ManaServItemDB: public ItemDB +{ + public: + ManaServItemDB() : ItemDB() + { load(); } + + ~ManaServItemDB() + { unload(); } + + /** + * Loads the item data from <code>items.xml</code>. + */ + void load(); + + private: + void checkItemInfo(ItemInfo* itemInfo); +}; - void setStatsList(const std::list<Stat> &stats); +}; // namespace ManaServ - // Items database - typedef std::map<int, ItemInfo*> ItemInfos; - typedef std::map<std::string, ItemInfo*> NamedItemInfos; -} +extern ItemDB *itemDb; #endif diff --git a/src/resources/iteminfo.cpp b/src/resources/iteminfo.cpp index 4b1d82ea..32331e35 100644 --- a/src/resources/iteminfo.cpp +++ b/src/resources/iteminfo.cpp @@ -29,7 +29,7 @@ const std::string &ItemInfo::getSprite(Gender gender) const if (mView) { // Forward the request to the item defining how to view this item - return ItemDB::get(mView).getSprite(gender); + return itemDb->get(mView).getSprite(gender); } else { @@ -41,35 +41,17 @@ const std::string &ItemInfo::getSprite(Gender gender) const } } -void ItemInfo::setWeaponType(int type) +void ItemInfo::setAttackAction(std::string attackAction) { - // See server item.hpp file for type values. - switch (type) - { - case WPNTYPE_NONE: - mAttackType = ACTION_DEFAULT; - break; - case WPNTYPE_KNIFE: - case WPNTYPE_SWORD: - mAttackType = ACTION_ATTACK_STAB; - break; - case WPNTYPE_THROWN: - mAttackType = ACTION_ATTACK_THROW; - break; - case WPNTYPE_BOW: - mAttackType = ACTION_ATTACK_BOW; - break; - case WPNTYPE_POLEARM: - mAttackType = ACTION_ATTACK_SWING; - break; - default: - mAttackType = ACTION_ATTACK; - } + if (attackAction.empty()) + mAttackAction = SpriteAction::ATTACK; // (Equal to unarmed animation) + else + mAttackAction = attackAction; } void ItemInfo::addSound(EquipmentSoundEvent event, const std::string &filename) { - mSounds[event].push_back(paths.getValue("sfx", "sfx/") + filename); + mSounds[event].push_back(paths.getStringValue("sfx") + filename); } const std::string &ItemInfo::getSound(EquipmentSoundEvent event) const diff --git a/src/resources/iteminfo.h b/src/resources/iteminfo.h index a7c0ddca..50633f71 100644 --- a/src/resources/iteminfo.h +++ b/src/resources/iteminfo.h @@ -22,7 +22,7 @@ #ifndef ITEMINFO_H #define ITEMINFO_H -#include "player.h" +#include "being.h" #include "resources/spritedef.h" @@ -36,38 +36,9 @@ enum EquipmentSoundEvent EQUIP_EVENT_HIT }; -enum EquipmentSlot -{ - // Equipment rules: - // 1 Brest equipment - EQUIP_TORSO_SLOT = 0, - // 1 arms equipment - EQUIP_ARMS_SLOT = 1, - // 1 head equipment - EQUIP_HEAD_SLOT = 2, - // 1 legs equipment - EQUIP_LEGS_SLOT = 3, - // 1 feet equipment - EQUIP_FEET_SLOT = 4, - // 2 rings - EQUIP_RING1_SLOT = 5, - EQUIP_RING2_SLOT = 6, - // 1 necklace - EQUIP_NECKLACE_SLOT = 7, - // Fight: - // 2 one-handed weapons - // or 1 two-handed weapon - // or 1 one-handed weapon + 1 shield. - EQUIP_FIGHT1_SLOT = 8, - EQUIP_FIGHT2_SLOT = 9, - // Projectile: - // this item does not amount to one, it only indicates the chosen projectile. - EQUIP_PROJECTILE_SLOT = 10 -}; - - /** * Enumeration of available Item types. + * TODO: Dynamise this using an xml. */ enum ItemType { @@ -89,30 +60,21 @@ enum ItemType ITEM_SPRITE_HAIR // 15 }; -/** - * Enumeration of available weapon's types. - */ -enum WeaponType -{ - WPNTYPE_NONE = 0, - WPNTYPE_KNIFE, - WPNTYPE_SWORD, - WPNTYPE_POLEARM, - WPNTYPE_STAFF, - WPNTYPE_WHIP, - WPNTYPE_BOW, - WPNTYPE_SHOOTING, - WPNTYPE_MACE, - WPNTYPE_AXE, - WPNTYPE_THROWN -}; +// Used to make the compiler uderstand the iteminfo friendship. +namespace TmwAthena { class TaItemDB; }; +namespace ManaServ { class ManaServItemDB; }; /** - * Defines a class for storing item infos. This includes information used when - * the item is equipped. + * Defines a class for storing generic item infos. + * Specialized version for one or another protocol are defined below. */ class ItemInfo { + friend class ItemDB; + friend void loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node); + friend class TmwAthena::TaItemDB; + friend class ManaServ::ManaServItemDB; + public: /** * Constructor. @@ -122,98 +84,86 @@ class ItemInfo mWeight(0), mView(0), mId(0), - mAttackType(ACTION_DEFAULT) + mAttackAction(SpriteAction::INVALID) { } - void setId(int id) - { mId = id; } - int getId() const { return mId; } - void setName(const std::string &name) - { mName = name; } - const std::string &getName() const { return mName; } - void setParticleEffect(const std::string &particleEffect) - { mParticle = particleEffect; } - - std::string getParticleEffect() const { return mParticle; } + std::string getParticleEffect() const + { return mParticle; } - void setImageName(const std::string &imageName) - { mImageName = imageName; } - - const std::string &getImageName() const - { return mImageName; } - - void setDescription(const std::string &description) - { mDescription = description; } + const SpriteDisplay &getDisplay() const + { return mDisplay; } const std::string &getDescription() const { return mDescription; } - void setEffect(const std::string &effect) - { mEffect = effect; } - - const std::string &getEffect() const { return mEffect; } - - void setType(ItemType type) - { mType = type; } - - ItemType getType() const - { return mType; } - - void setWeight(int weight) - { mWeight = weight; } + const std::vector<std::string> &getEffect() const + { return mEffect; } int getWeight() const { return mWeight; } - void setView(int view) - { mView = view; } - - void setSprite(const std::string &animationFile, Gender gender) - { mAnimationFiles[gender] = animationFile; } - const std::string &getSprite(Gender gender) const; - void setWeaponType(int); + void setAttackAction(std::string attackAction); // Handlers for seting and getting the string used for particles when attacking - void setMissileParticle(std::string s) { mMissileParticle = s; } + void setMissileParticle(std::string s) + { mMissileParticle = s; } - std::string getMissileParticle() const { return mMissileParticle; } + std::string getMissileParticle() const + { return mMissileParticle; } - SpriteAction getAttackType() const - { return mAttackType; } + std::string getAttackAction() const + { return mAttackAction; } int getAttackRange() const { return mAttackRange; } - void setAttackRange(int r) - { mAttackRange = r; } + const std::string &getSound(EquipmentSoundEvent event) const; - void addSound(EquipmentSoundEvent event, const std::string &filename); + bool getEquippable() const + { return mEquippable; } - const std::string &getSound(EquipmentSoundEvent event) const; + bool getActivatable() const + { return mActivatable; } + + ItemType getItemType() const + { return mType; } + + private: + + void setSprite(const std::string &animationFile, Gender gender) + { mAnimationFiles[gender] = animationFile; } + + void addSound(EquipmentSoundEvent event, const std::string &filename); - protected: - std::string mImageName; /**< The filename of the icon image. */ + SpriteDisplay mDisplay; /**< Display info (like icon) */ std::string mName; - std::string mDescription; /**< Short description. */ - std::string mEffect; /**< Description of effects. */ - ItemType mType; /**< Item type. */ - std::string mParticle; /**< Particle effect used with this item */ - int mWeight; /**< Weight in grams. */ - int mView; /**< Item ID of how this item looks. */ - int mId; /**< Item ID */ - - // Equipment related members - SpriteAction mAttackType; /**< Attack type, in case of weapon. */ - int mAttackRange; /**< Attack range, will be zero if non weapon. */ + std::string mDescription; /**< Short description. */ + std::vector<std::string> mEffect; /**< Description of effects. */ + ItemType mType; /**< Item type. */ + std::string mParticle; /**< Particle effect used with this item */ + int mWeight; /**< Weight in grams. */ + int mView; /**< Item ID of how this item looks. */ + int mId; /**< Item ID */ + + bool mEquippable; /**< Whether this item can be equipped. */ + bool mActivatable; /**< Whether this item can be activated. */ + + // Equipment related members. + /** Attack type, in case of weapon. + * See SpriteAction in spritedef.h for more info. + * Attack action sub-types (bow, sword, ...) are defined in items.xml. + */ + std::string mAttackAction; + int mAttackRange; /**< Attack range, will be zero if non weapon. */ // Particle to be shown when weapon attacks std::string mMissileParticle; @@ -225,4 +175,80 @@ class ItemInfo std::map< EquipmentSoundEvent, std::vector<std::string> > mSounds; }; +/* + * TmwAthena specialization of the itemInfo for TmwAthena + */ +namespace TmwAthena { + +enum EquipmentSlot +{ + // Equipment rules: + // 1 Brest equipment + EQUIP_TORSO_SLOT = 0, + // 1 arms equipment + EQUIP_ARMS_SLOT = 1, + // 1 head equipment + EQUIP_HEAD_SLOT = 2, + // 1 legs equipment + EQUIP_LEGS_SLOT = 3, + // 1 feet equipment + EQUIP_FEET_SLOT = 4, + // 2 rings + EQUIP_RING1_SLOT = 5, + EQUIP_RING2_SLOT = 6, + // 1 necklace + EQUIP_NECKLACE_SLOT = 7, + // Fight: + // 2 one-handed weapons + // or 1 two-handed weapon + // or 1 one-handed weapon + 1 shield. + EQUIP_FIGHT1_SLOT = 8, + EQUIP_FIGHT2_SLOT = 9, + // Projectile: + // this item does not amount to one, it only indicates the chosen projectile. + EQUIP_PROJECTILE_SLOT = 10, + EQUIP_VECTOR_END = 11 +}; + +/** + * Defines a class for storing TmwAthena specific item infos. + * Specialized version for one or another protocol are defined below. + */ +class TaItemInfo: public ItemInfo +{ + friend class TaItemDB; + + public: + /** + * Constructor. + */ + TaItemInfo():ItemInfo() + {} + + // Declare TmwAthena Specific item info here +}; + +}; // namespace TmwAthena + +namespace ManaServ { + +/** + * Defines a class for storing Manaserv Specific item infos. + * Specialized version for one or another protocol are defined below. + */ +class ManaServItemInfo: public ItemInfo +{ + public: + /** + * Constructor. + */ + ManaServItemInfo():ItemInfo() + {} + + // Declare Manaserv Specific item info here +}; + + +}; // namespace ManaServ + #endif diff --git a/src/resources/mapreader.cpp b/src/resources/mapreader.cpp index b0720af5..07a1262c 100644 --- a/src/resources/mapreader.cpp +++ b/src/resources/mapreader.cpp @@ -185,11 +185,9 @@ Map *MapReader::readMap(xmlNodePtr node, const std::string &path) if (config.getValue("showWarps", 1)) { map->addParticleEffect( - paths.getValue("particles", - "graphics/particles/") - + paths.getValue("portalEffectFile", - "warparea.particle.xml"), - objX, objY, objW, objH); + paths.getStringValue("particles") + + paths.getStringValue("portalEffectFile"), + objX, objY, objW, objH); } } else @@ -394,6 +392,8 @@ Tileset *MapReader::readTileset(xmlNodePtr node, const std::string &path, Map *map) { int firstGid = XML::getProperty(node, "firstgid", 0); + int margin = XML::getProperty(node, "margin", 0); + int spacing = XML::getProperty(node, "spacing", 0); XML::Document* doc = NULL; Tileset *set = NULL; std::string pathDir(path); @@ -428,7 +428,8 @@ Tileset *MapReader::readTileset(xmlNodePtr node, const std::string &path, if (tilebmp) { - set = new Tileset(tilebmp, tw, th, firstGid); + set = new Tileset(tilebmp, tw, th, firstGid, margin, + spacing); tilebmp->decRef(); } else diff --git a/src/resources/monsterdb.cpp b/src/resources/monsterdb.cpp index b08d87f2..6bc64a89 100644 --- a/src/resources/monsterdb.cpp +++ b/src/resources/monsterdb.cpp @@ -23,21 +23,21 @@ #include "log.h" -#include "resources/monsterinfo.h" +#include "net/net.h" + +#include "resources/beinginfo.h" #include "utils/dtor.h" #include "utils/gettext.h" #include "utils/xml.h" -#include "net/net.h" #include "configuration.h" #define OLD_TMWATHENA_OFFSET 1002 namespace { - MonsterDB::MonsterInfos mMonsterInfos; - MonsterInfo mUnknown; + BeingInfos mMonsterInfos; bool mLoaded = false; } @@ -46,8 +46,6 @@ void MonsterDB::load() if (mLoaded) unload(); - mUnknown.addSprite(paths.getValue("spriteErrorFile", "error.xml")); - logger->log("Initializing monster database..."); XML::Document doc("monsters.xml"); @@ -69,39 +67,29 @@ void MonsterDB::load() continue; } - MonsterInfo *currentInfo = new MonsterInfo; + BeingInfo *currentInfo = new BeingInfo; + + currentInfo->setWalkMask(Map::BLOCKMASK_WALL + | Map::BLOCKMASK_CHARACTER + | Map::BLOCKMASK_MONSTER); + currentInfo->setBlockType(Map::BLOCKTYPE_MONSTER); currentInfo->setName(XML::getProperty(monsterNode, "name", _("unnamed"))); - std::string targetCursor; - targetCursor = XML::getProperty(monsterNode, "targetCursor", "medium"); - if (targetCursor == "small") - { - currentInfo->setTargetCursorSize(Being::TC_SMALL); - } - else if (targetCursor == "medium") - { - currentInfo->setTargetCursorSize(Being::TC_MEDIUM); - } - else if (targetCursor == "large") - { - currentInfo->setTargetCursorSize(Being::TC_LARGE); - } - else - { - logger->log("MonsterDB: Unknown target cursor type \"%s\" for %s -" - "using medium sized one", - targetCursor.c_str(), currentInfo->getName().c_str()); - currentInfo->setTargetCursorSize(Being::TC_MEDIUM); - } + currentInfo->setTargetCursorSize(XML::getProperty(monsterNode, + "targetCursor", "medium")); + + SpriteDisplay display; //iterate <sprite>s and <sound>s for_each_xml_child_node(spriteNode, monsterNode) { if (xmlStrEqual(spriteNode->name, BAD_CAST "sprite")) { - currentInfo->addSprite( - (const char*) spriteNode->xmlChildrenNode->content); + SpriteReference *currentSprite = new SpriteReference; + currentSprite->sprite = (const char*)spriteNode->xmlChildrenNode->content; + currentSprite->variant = XML::getProperty(spriteNode, "variant", 0); + display.sprites.push_back(currentSprite); } else if (xmlStrEqual(spriteNode->name, BAD_CAST "sound")) { @@ -111,19 +99,19 @@ void MonsterDB::load() if (event == "hit") { - currentInfo->addSound(MONSTER_EVENT_HIT, filename); + currentInfo->addSound(SOUND_EVENT_HIT, filename); } else if (event == "miss") { - currentInfo->addSound(MONSTER_EVENT_MISS, filename); + currentInfo->addSound(SOUND_EVENT_MISS, filename); } else if (event == "hurt") { - currentInfo->addSound(MONSTER_EVENT_HURT, filename); + currentInfo->addSound(SOUND_EVENT_HURT, filename); } else if (event == "die") { - currentInfo->addSound(MONSTER_EVENT_DIE, filename); + currentInfo->addSound(SOUND_EVENT_DIE, filename); } else { @@ -138,18 +126,22 @@ void MonsterDB::load() const int id = XML::getProperty(spriteNode, "id", 0); const std::string particleEffect = XML::getProperty( spriteNode, "particle-effect", ""); - SpriteAction spriteAction = SpriteDef::makeSpriteAction( - XML::getProperty(spriteNode, "action", "attack")); + const std::string spriteAction = XML::getProperty(spriteNode, + "action", + "attack"); const std::string missileParticle = XML::getProperty( spriteNode, "missile-particle", ""); - currentInfo->addMonsterAttack(id, particleEffect, spriteAction, missileParticle); + currentInfo->addAttack(id, spriteAction, + particleEffect, missileParticle); } else if (xmlStrEqual(spriteNode->name, BAD_CAST "particlefx")) { - currentInfo->addParticleEffect( + display.particles.push_back( (const char*) spriteNode->xmlChildrenNode->content); } } + currentInfo->setDisplay(display); + mMonsterInfos[XML::getProperty(monsterNode, "id", 0) + offset] = currentInfo; } @@ -165,17 +157,17 @@ void MonsterDB::unload() } -const MonsterInfo &MonsterDB::get(int id) +BeingInfo *MonsterDB::get(int id) { - MonsterInfoIterator i = mMonsterInfos.find(id); + BeingInfoIterator i = mMonsterInfos.find(id); if (i == mMonsterInfos.end()) { logger->log("MonsterDB: Warning, unknown monster ID %d requested", id); - return mUnknown; + return BeingInfo::Unknown; } else { - return *(i->second); + return i->second; } } diff --git a/src/resources/monsterdb.h b/src/resources/monsterdb.h index 0fc8d2cf..50f70438 100644 --- a/src/resources/monsterdb.h +++ b/src/resources/monsterdb.h @@ -22,9 +22,7 @@ #ifndef MONSTER_DB_H #define MONSTER_DB_H -#include <map> - -class MonsterInfo; +class BeingInfo; /** * Monster information database. @@ -35,10 +33,7 @@ namespace MonsterDB void unload(); - const MonsterInfo &get(int id); - - typedef std::map<int, MonsterInfo*> MonsterInfos; - typedef MonsterInfos::iterator MonsterInfoIterator; + BeingInfo *get(int id); } #endif diff --git a/src/resources/monsterinfo.cpp b/src/resources/monsterinfo.cpp deleted file mode 100644 index 12cdbe3e..00000000 --- a/src/resources/monsterinfo.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - * The Mana Client - * Copyright (C) 2004-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * - * This file is part of The Mana Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "resources/monsterinfo.h" - -#include "utils/dtor.h" -#include "utils/gettext.h" -#include "configuration.h" - -MonsterInfo::MonsterInfo(): - mName(_("unnamed")), - mTargetCursorSize(Being::TC_MEDIUM) -{ -} - -MonsterInfo::~MonsterInfo() -{ - // kill vectors in mSoundEffects - delete_all(mSounds); - delete_all(mMonsterAttacks); - mSounds.clear(); -} - -void MonsterInfo::addSound(MonsterSoundEvent event, const std::string &filename) -{ - if (mSounds.find(event) == mSounds.end()) - { - mSounds[event] = new std::vector<std::string>; - } - - mSounds[event]->push_back(paths.getValue("sfx", "sfx/") - + filename); -} - -const std::string &MonsterInfo::getSound(MonsterSoundEvent event) const -{ - static std::string empty(""); - std::map<MonsterSoundEvent, std::vector<std::string>* >::const_iterator i = - mSounds.find(event); - return (i == mSounds.end()) ? empty : - i->second->at(rand() % i->second->size()); -} - -const std::string &MonsterInfo::getAttackParticleEffect(int attackType) const -{ - static std::string empty(""); - std::map<int, MonsterAttack*>::const_iterator i = - mMonsterAttacks.find(attackType); - return (i == mMonsterAttacks.end()) ? empty : (*i).second->particleEffect; -} - -const std::string &MonsterInfo::getAttackMissileParticle(int attackType) const -{ - static std::string empty(""); - std::map<int, MonsterAttack*>::const_iterator i = - mMonsterAttacks.find(attackType); - return (i == mMonsterAttacks.end()) ? empty : (*i).second->missileParticle; -} - -SpriteAction MonsterInfo::getAttackAction(int attackType) const -{ - std::map<int, MonsterAttack*>::const_iterator i = - mMonsterAttacks.find(attackType); - return (i == mMonsterAttacks.end()) ? ACTION_ATTACK : (*i).second->action; -} - -void MonsterInfo::addMonsterAttack(int id, - const std::string &particleEffect, - SpriteAction action, - const std::string &missileParticle) -{ - MonsterAttack *a = new MonsterAttack; - a->particleEffect = particleEffect; - a->missileParticle = missileParticle; - a->action = action; - mMonsterAttacks[id] = a; -} - -void MonsterInfo::addParticleEffect(const std::string &filename) -{ - mParticleEffects.push_back(filename); -} diff --git a/src/resources/monsterinfo.h b/src/resources/monsterinfo.h deleted file mode 100644 index f074254a..00000000 --- a/src/resources/monsterinfo.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * The Mana Client - * Copyright (C) 2004-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * - * This file is part of The Mana Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef MONSTERINFO_H -#define MONSTERINFO_H - -#include "being.h" - -#include <list> -#include <map> -#include <string> -#include <vector> - -enum MonsterSoundEvent -{ - MONSTER_EVENT_HIT, - MONSTER_EVENT_MISS, - MONSTER_EVENT_HURT, - MONSTER_EVENT_DIE -}; - -struct MonsterAttack -{ - std::string missileParticle; - std::string particleEffect; - SpriteAction action; -}; - -/** - * Holds information about a certain type of monster. This includes the name - * of the monster, the sprite to display and the sounds the monster makes. - * - * @see MonsterDB - */ -class MonsterInfo -{ - public: - MonsterInfo(); - - ~MonsterInfo(); - - void setName(const std::string &name) { mName = name; } - - void addSprite(const std::string &filename) - { mSprites.push_back(filename); } - - void setTargetCursorSize(Being::TargetCursorSize targetCursorSize) - { mTargetCursorSize = targetCursorSize; } - - void addSound(MonsterSoundEvent event, const std::string &filename); - - void addParticleEffect(const std::string &filename); - - const std::string &getName() const - { return mName; } - - const std::list<std::string>& getSprites() const - { return mSprites; } - - Being::TargetCursorSize getTargetCursorSize() const - { return mTargetCursorSize; } - - const std::string &getSound(MonsterSoundEvent event) const; - - void addMonsterAttack(int id, - const std::string &particleEffect, - SpriteAction action, - const std::string &missileParticle); - - const std::string &getAttackParticleEffect(int attackType) const; - - const std::string &getAttackMissileParticle(int attackType) const; - - SpriteAction getAttackAction(int attackType) const; - - const std::list<std::string>& getParticleEffects() const - { return mParticleEffects; } - - private: - std::string mName; - std::list<std::string> mSprites; - Being::TargetCursorSize mTargetCursorSize; - std::map<MonsterSoundEvent, std::vector<std::string>* > mSounds; - std::map<int, MonsterAttack*> mMonsterAttacks; - std::list<std::string> mParticleEffects; -}; - -#endif // MONSTERINFO_H diff --git a/src/resources/npcdb.cpp b/src/resources/npcdb.cpp index 21a2e9a1..ec22c225 100644 --- a/src/resources/npcdb.cpp +++ b/src/resources/npcdb.cpp @@ -23,13 +23,15 @@ #include "log.h" +#include "resources/beinginfo.h" + +#include "utils/dtor.h" #include "utils/xml.h" #include "configuration.h" namespace { - NPCInfos mNPCInfos; - NPCInfo mUnknown; + BeingInfos mNPCInfos; bool mLoaded = false; } @@ -38,12 +40,6 @@ void NPCDB::load() if (mLoaded) unload(); - NPCsprite *unknownSprite = new NPCsprite; - unknownSprite->sprite = paths.getValue("spriteErrorFile", - "error.xml"); - unknownSprite->variant = 0; - mUnknown.sprites.push_back(unknownSprite); - logger->log("Initializing NPC database..."); XML::Document doc("npcs.xml"); @@ -67,23 +63,30 @@ void NPCDB::load() continue; } - NPCInfo *currentInfo = new NPCInfo; + BeingInfo *currentInfo = new BeingInfo; + currentInfo->setTargetCursorSize(XML::getProperty(npcNode, + "targetCursor", "medium")); + + SpriteDisplay display; for_each_xml_child_node(spriteNode, npcNode) { if (xmlStrEqual(spriteNode->name, BAD_CAST "sprite")) { - NPCsprite *currentSprite = new NPCsprite; + SpriteReference *currentSprite = new SpriteReference; currentSprite->sprite = (const char*)spriteNode->xmlChildrenNode->content; currentSprite->variant = XML::getProperty(spriteNode, "variant", 0); - currentInfo->sprites.push_back(currentSprite); + display.sprites.push_back(currentSprite); } else if (xmlStrEqual(spriteNode->name, BAD_CAST "particlefx")) { std::string particlefx = (const char*)spriteNode->xmlChildrenNode->content; - currentInfo->particles.push_back(particlefx); + display.particles.push_back(particlefx); } } + + currentInfo->setDisplay(display); + mNPCInfos[id] = currentInfo; } @@ -92,40 +95,23 @@ void NPCDB::load() void NPCDB::unload() { - for ( NPCInfosIterator i = mNPCInfos.begin(); - i != mNPCInfos.end(); - i++) - { - while (!i->second->sprites.empty()) - { - delete i->second->sprites.front(); - i->second->sprites.pop_front(); - } - delete i->second; - } - + delete_all(mNPCInfos); mNPCInfos.clear(); - while (!mUnknown.sprites.empty()) - { - delete mUnknown.sprites.front(); - mUnknown.sprites.pop_front(); - } - mLoaded = false; } -const NPCInfo& NPCDB::get(int id) +BeingInfo *NPCDB::get(int id) { - NPCInfosIterator i = mNPCInfos.find(id); + BeingInfoIterator i = mNPCInfos.find(id); if (i == mNPCInfos.end()) { logger->log("NPCDB: Warning, unknown NPC ID %d requested", id); - return mUnknown; + return BeingInfo::Unknown; } else { - return *(i->second); + return i->second; } } diff --git a/src/resources/npcdb.h b/src/resources/npcdb.h index 9da873e4..b0c89c80 100644 --- a/src/resources/npcdb.h +++ b/src/resources/npcdb.h @@ -22,23 +22,7 @@ #ifndef NPC_DB_H #define NPC_DB_H -#include <list> -#include <map> -#include <string> - -struct NPCsprite -{ - std::string sprite; - int variant; -}; - -struct NPCInfo -{ - std::list<NPCsprite*> sprites; - std::list<std::string> particles; -}; - -typedef std::map<int, NPCInfo*> NPCInfos; +class BeingInfo; /** * NPC information database. @@ -49,9 +33,7 @@ namespace NPCDB void unload(); - const NPCInfo& get(int id); - - typedef NPCInfos::iterator NPCInfosIterator; + BeingInfo *get(int id); } #endif diff --git a/src/resources/specialdb.cpp b/src/resources/specialdb.cpp new file mode 100644 index 00000000..ac591c4f --- /dev/null +++ b/src/resources/specialdb.cpp @@ -0,0 +1,132 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "resources/specialdb.h" + +#include "log.h" + +#include "utils/dtor.h" +#include "utils/xml.h" + + +namespace +{ + SpecialInfos mSpecialInfos; + bool mLoaded = false; +} + +SpecialInfo::TargetMode SpecialDB::targetModeFromString(const std::string& str) +{ + if (str=="self") return SpecialInfo::TARGET_SELF; + else if (str=="friend") return SpecialInfo::TARGET_FRIEND; + else if (str=="enemy") return SpecialInfo::TARGET_ENEMY; + else if (str=="being") return SpecialInfo::TARGET_BEING; + else if (str=="point") return SpecialInfo::TARGET_POINT; + + logger->log("SpecialDB: Warning, unknown target mode \"%s\"", str.c_str() ); + return SpecialInfo::TARGET_SELF; +} + +void SpecialDB::load() +{ + if (mLoaded) + unload(); + + logger->log("Initializing special database..."); + + XML::Document doc("specials.xml"); + xmlNodePtr root = doc.rootNode(); + + if (!root || !xmlStrEqual(root->name, BAD_CAST "specials")) + { + logger->log("Error loading specials file specials.xml"); + return; + } + + std::string setName; + + for_each_xml_child_node(set, root) + { + if (xmlStrEqual(set->name, BAD_CAST "set")) + { + setName = XML::getProperty(set, "name", "Actions"); + + + for_each_xml_child_node(special, set) + { + if (xmlStrEqual(special->name, BAD_CAST "special")) + { + SpecialInfo *info = new SpecialInfo(); + int id = XML::getProperty(special, "id", 0); + info->id = id; + info->set = setName; + info->name = XML::getProperty(special, "name", ""); + info->icon = XML::getProperty(special, "icon", ""); + + info->isActive = XML::getBoolProperty(special, "active", false); + info->targetMode = targetModeFromString(XML::getProperty(special, "target", "self")); + + info->level = XML::getProperty(special, "level", -1); + info->hasLevel = info->level > -1; + + info->hasRechargeBar = XML::getBoolProperty(special, "recharge", false); + info->rechargeNeeded = 0; + info->rechargeCurrent = 0; + + if (mSpecialInfos.find(id) != mSpecialInfos.end()) + { + logger->log("SpecialDB: Duplicate special ID %d (ignoring)", id); + } else { + mSpecialInfos[id] = info; + } + } + } + } + } + + mLoaded = true; +} + +void SpecialDB::unload() +{ + + delete_all(mSpecialInfos); + mSpecialInfos.clear(); + + mLoaded = false; +} + + +SpecialInfo *SpecialDB::get(int id) +{ + + SpecialInfos::iterator i = mSpecialInfos.find(id); + + if (i == mSpecialInfos.end()) + { + return NULL; + } + else + { + return i->second; + } + return NULL; +} + diff --git a/src/resources/specialdb.h b/src/resources/specialdb.h new file mode 100644 index 00000000..38612c2a --- /dev/null +++ b/src/resources/specialdb.h @@ -0,0 +1,72 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef SPECIAL_DB_H +#define SPECIAL_DB_H + +#include <string> +#include <map> + +struct SpecialInfo +{ + enum TargetMode + { + TARGET_SELF, // no target selection + TARGET_FRIEND, // target friendly being + TARGET_ENEMY, // target hostile being + TARGET_BEING, // target any being + TARGET_POINT // target map location + }; + int id; + std::string set; // tab on which the special is shown + std::string name; // displayed name of special + std::string icon; // filename of graphical icon + + bool isActive; // true when the special can be used + TargetMode targetMode; // target mode + + bool hasLevel; // true when the special has levels + int level; // level of special when applicable + + bool hasRechargeBar; // true when the special has a recharge bar + int rechargeNeeded; // maximum recharge when applicable + int rechargeCurrent; // current recharge when applicable +}; + +/** + * Special information database. + */ +namespace SpecialDB +{ + void load(); + + void unload(); + + /** gets the special info for ID. Will return 0 when it is + * a server-specific special. + */ + SpecialInfo *get(int id); + + SpecialInfo::TargetMode targetModeFromString(const std::string& str); +} + +typedef std::map<int, SpecialInfo *> SpecialInfos; + +#endif diff --git a/src/resources/spritedef.cpp b/src/resources/spritedef.cpp index c524c43c..311c9d1a 100644 --- a/src/resources/spritedef.cpp +++ b/src/resources/spritedef.cpp @@ -36,13 +36,16 @@ #include <set> -Action *SpriteDef::getAction(SpriteAction action) const +SpriteReference *SpriteReference::Empty = new SpriteReference( + paths.getStringValue("spriteErrorFile"), 0); + +Action *SpriteDef::getAction(const std::string &action) const { Actions::const_iterator i = mActions.find(action); if (i == mActions.end()) { - logger->log("Warning: no action \"%u\" defined!", action); + logger->log("Warning: no action \"%s\" defined!", action.c_str()); return NULL; } @@ -63,9 +66,8 @@ SpriteDef *SpriteDef::load(const std::string &animationFile, int variant) { logger->log("Error, failed to parse %s", animationFile.c_str()); - std::string errorFile = paths.getValue("sprites", "graphics/sprites") - + paths.getValue("spriteErrorFile", - "error.xml"); + std::string errorFile = paths.getStringValue("sprites") + + paths.getStringValue("spriteErrorFile"); if (animationFile != errorFile) { return load(errorFile, 0); @@ -82,22 +84,29 @@ SpriteDef *SpriteDef::load(const std::string &animationFile, int variant) return def; } +void SpriteDef::substituteAction(std::string complete, std::string with) +{ + if (mActions.find(complete) == mActions.end()) + { + Actions::iterator i = mActions.find(with); + if (i != mActions.end()) + { + mActions[complete] = i->second; + } + } +} + void SpriteDef::substituteActions() { - substituteAction(ACTION_STAND, ACTION_DEFAULT); - substituteAction(ACTION_WALK, ACTION_STAND); - substituteAction(ACTION_WALK, ACTION_RUN); - substituteAction(ACTION_ATTACK, ACTION_STAND); - substituteAction(ACTION_ATTACK_SWING, ACTION_ATTACK); - substituteAction(ACTION_ATTACK_STAB, ACTION_ATTACK_SWING); - substituteAction(ACTION_ATTACK_BOW, ACTION_ATTACK_STAB); - substituteAction(ACTION_ATTACK_THROW, ACTION_ATTACK_SWING); - substituteAction(ACTION_CAST_MAGIC, ACTION_ATTACK_SWING); - substituteAction(ACTION_USE_ITEM, ACTION_CAST_MAGIC); - substituteAction(ACTION_SIT, ACTION_STAND); - substituteAction(ACTION_SLEEP, ACTION_SIT); - substituteAction(ACTION_HURT, ACTION_STAND); - substituteAction(ACTION_DEAD, ACTION_HURT); + substituteAction(SpriteAction::STAND, SpriteAction::DEFAULT); + substituteAction(SpriteAction::MOVE, SpriteAction::STAND); + substituteAction(SpriteAction::ATTACK, SpriteAction::STAND); + substituteAction(SpriteAction::CAST_MAGIC, SpriteAction::ATTACK); + substituteAction(SpriteAction::USE_ITEM, SpriteAction::CAST_MAGIC); + substituteAction(SpriteAction::SIT, SpriteAction::STAND); + substituteAction(SpriteAction::SLEEP, SpriteAction::SIT); + substituteAction(SpriteAction::HURT, SpriteAction::STAND); + substituteAction(SpriteAction::DEAD, SpriteAction::HURT); } void SpriteDef::loadSprite(xmlNodePtr spriteNode, int variant, @@ -169,20 +178,19 @@ void SpriteDef::loadAction(xmlNodePtr node, int variant_offset) } ImageSet *imageSet = si->second; - SpriteAction actionType = makeSpriteAction(actionName); - if (actionType == ACTION_INVALID) + if (actionName == SpriteAction::INVALID) { logger->log("Warning: Unknown action \"%s\" defined in %s", actionName.c_str(), getIdPath().c_str()); return; } Action *action = new Action; - mActions[actionType] = action; + mActions[actionName] = action; // When first action set it as default direction - if (mActions.empty()) + if (mActions.size() == 1) { - mActions[ACTION_DEFAULT] = action; + mActions[SpriteAction::DEFAULT] = action; } // Load animations @@ -283,8 +291,7 @@ void SpriteDef::includeSprite(xmlNodePtr includeNode) if (filename.empty()) return; - XML::Document doc(paths.getValue("sprites", "graphics/sprites/") - + filename); + XML::Document doc(paths.getStringValue("sprites") + filename); xmlNodePtr rootNode = doc.rootNode(); if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "sprite")) @@ -296,18 +303,6 @@ void SpriteDef::includeSprite(xmlNodePtr includeNode) loadSprite(rootNode, 0); } -void SpriteDef::substituteAction(SpriteAction complete, SpriteAction with) -{ - if (mActions.find(complete) == mActions.end()) - { - Actions::iterator i = mActions.find(with); - if (i != mActions.end()) - { - mActions[complete] = i->second; - } - } -} - SpriteDef::~SpriteDef() { // Actions are shared, so ensure they are deleted only once. @@ -331,63 +326,6 @@ SpriteDef::~SpriteDef() } } -SpriteAction SpriteDef::makeSpriteAction(const std::string &action) -{ - if (action.empty() || action == "default") - return ACTION_DEFAULT; - - if (action == "stand") - return ACTION_STAND; - else if (action == "walk") - return ACTION_WALK; - else if (action == "run") - return ACTION_RUN; - else if (action == "attack") - return ACTION_ATTACK; - else if (action == "attack_swing") - return ACTION_ATTACK_SWING; - else if (action == "attack_stab") - return ACTION_ATTACK_STAB; - else if (action == "attack_bow") - return ACTION_ATTACK_BOW; - else if (action == "attack_throw") - return ACTION_ATTACK_THROW; - else if (action == "special0") - return ACTION_SPECIAL_0; - else if (action == "special1") - return ACTION_SPECIAL_1; - else if (action == "special2") - return ACTION_SPECIAL_2; - else if (action == "special3") - return ACTION_SPECIAL_3; - else if (action == "special4") - return ACTION_SPECIAL_4; - else if (action == "special5") - return ACTION_SPECIAL_5; - else if (action == "special6") - return ACTION_SPECIAL_6; - else if (action == "special7") - return ACTION_SPECIAL_7; - else if (action == "special8") - return ACTION_SPECIAL_8; - else if (action == "special9") - return ACTION_SPECIAL_9; - else if (action == "cast_magic") - return ACTION_CAST_MAGIC; - else if (action == "use_item") - return ACTION_USE_ITEM; - else if (action == "sit") - return ACTION_SIT; - else if (action == "sleep") - return ACTION_SLEEP; - else if (action == "hurt") - return ACTION_HURT; - else if (action == "dead") - return ACTION_DEAD; - else - return ACTION_INVALID; -} - SpriteDirection SpriteDef::makeSpriteDirection(const std::string &direction) { if (direction.empty() || direction == "default") diff --git a/src/resources/spritedef.h b/src/resources/spritedef.h index 5bb6078e..18a70c9b 100644 --- a/src/resources/spritedef.h +++ b/src/resources/spritedef.h @@ -26,42 +26,60 @@ #include <libxml/tree.h> +#include <list> #include <map> #include <string> class Action; class ImageSet; -enum SpriteAction +struct SpriteReference { - ACTION_DEFAULT = 0, - ACTION_STAND, - ACTION_WALK, - ACTION_RUN, - ACTION_ATTACK, - ACTION_ATTACK_SWING, - ACTION_ATTACK_STAB, - ACTION_ATTACK_BOW, - ACTION_ATTACK_THROW, - ACTION_SPECIAL_0, - ACTION_SPECIAL_1, - ACTION_SPECIAL_2, - ACTION_SPECIAL_3, - ACTION_SPECIAL_4, - ACTION_SPECIAL_5, - ACTION_SPECIAL_6, - ACTION_SPECIAL_7, - ACTION_SPECIAL_8, - ACTION_SPECIAL_9, - ACTION_CAST_MAGIC, - ACTION_USE_ITEM, - ACTION_SIT, - ACTION_SLEEP, - ACTION_HURT, - ACTION_DEAD, - ACTION_INVALID + static SpriteReference *Empty; + + SpriteReference() {} + + SpriteReference(std::string sprite, int variant) + { this->sprite = sprite; this->variant = variant; } + + std::string sprite; + int variant; }; +struct SpriteDisplay +{ + std::string image; + std::list<SpriteReference*> sprites; + std::list<std::string> particles; +}; + +typedef std::list<SpriteReference*>::const_iterator SpriteRefs; + +/* + * Remember those are the main action. + * Action subtypes, e.g.: "attack_bow" are to be passed by items.xml after + * an ACTION_ATTACK call. + * Which special to be use to to be passed with the USE_SPECIAL call. + * Running, walking, ... is a sub-type of moving. + * ... + * Please don't add hard-coded subtypes here! + */ +namespace SpriteAction +{ + static const std::string DEFAULT = "stand"; + static const std::string STAND = "stand"; + static const std::string SIT = "sit"; + static const std::string SLEEP = "sleep"; + static const std::string DEAD = "dead"; + static const std::string MOVE = "walk"; + static const std::string ATTACK = "attack"; + static const std::string HURT = "hurt"; + static const std::string USE_SPECIAL = "special"; + static const std::string CAST_MAGIC = "magic"; + static const std::string USE_ITEM = "item"; + static const std::string INVALID = ""; +} + enum SpriteDirection { DIRECTION_DEFAULT = 0, @@ -86,12 +104,7 @@ class SpriteDef : public Resource /** * Returns the specified action. */ - Action *getAction(SpriteAction action) const; - - /** - * Converts a string into a SpriteAction enum. - */ - static SpriteAction makeSpriteAction(const std::string &action); + Action *getAction(const std::string &action) const; /** * Converts a string into a SpriteDirection enum. @@ -147,12 +160,12 @@ class SpriteDef : public Resource * When there are no animations defined for the action "complete", its * animations become a copy of those of the action "with". */ - void substituteAction(SpriteAction complete, SpriteAction with); + void substituteAction(std::string complete, std::string with); typedef std::map<std::string, ImageSet*> ImageSets; typedef ImageSets::iterator ImageSetIterator; - typedef std::map<SpriteAction, Action*> Actions; + typedef std::map<std::string, Action*> Actions; ImageSets mImageSets; Actions mActions; diff --git a/src/gui/theme.cpp b/src/resources/theme.cpp index 3d0bd5d3..8de275ae 100644 --- a/src/gui/theme.cpp +++ b/src/resources/theme.cpp @@ -21,7 +21,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "gui/theme.h" +#include "resources/theme.h" #include "client.h" #include "configuration.h" @@ -48,7 +48,7 @@ Theme *Theme::mInstance = 0; static void initDefaultThemePath() { ResourceManager *resman = ResourceManager::getInstance(); - defaultThemePath = branding.getValue("guiThemePath", ""); + defaultThemePath = branding.getStringValue("guiThemePath"); if (!defaultThemePath.empty() && resman->isDirectory(defaultThemePath)) return; @@ -81,8 +81,8 @@ Skin::~Skin() void Skin::updateAlpha(float minimumOpacityAllowed) { - const float alpha = std::max((double)minimumOpacityAllowed, - config.getValue("guialpha", 0.8f)); + const float alpha = std::max(minimumOpacityAllowed, + config.getFloatValue("guialpha")); for_each(mBorder.grid, mBorder.grid + 9, std::bind2nd(std::mem_fun(&Image::setAlpha), alpha)); @@ -111,7 +111,7 @@ Theme::Theme(): { initDefaultThemePath(); - config.addListener("guialpha", this); + listen(CHANNEL_CONFIG); loadColors(); mColors[HIGHLIGHT].ch = 'H'; @@ -130,7 +130,6 @@ Theme::Theme(): Theme::~Theme() { delete_all(mSkins); - config.removeListener("guialpha", this); delete_all(mProgressColors); } @@ -209,9 +208,14 @@ void Theme::updateAlpha() iter->second->updateAlpha(mMinimumOpacity); } -void Theme::optionChanged(const std::string &) +void Theme::event(Channels channel, const Mana::Event &event) { - updateAlpha(); + if (channel == CHANNEL_CONFIG && + event.getName() == EVENT_CONFIGOPTIONCHANGED && + event.getString("option") == "guialpha") + { + updateAlpha(); + } } Skin *Theme::readSkin(const std::string &filename) @@ -343,9 +347,9 @@ void Theme::prepareThemePath() instance(); // Try theme from settings - if (!tryThemePath(config.getValue("theme", ""))) + if (!tryThemePath(config.getStringValue("theme"))) // Try theme from branding - if (!tryThemePath(branding.getValue("theme", ""))) + if (!tryThemePath(branding.getStringValue("theme"))) // Use default mThemePath = defaultThemePath; diff --git a/src/gui/theme.h b/src/resources/theme.h index 3a5aa41a..f830c94f 100644 --- a/src/gui/theme.h +++ b/src/resources/theme.h @@ -24,8 +24,8 @@ #ifndef SKIN_H #define SKIN_H -#include "configlistener.h" #include "graphics.h" +#include "listener.h" #include "gui/palette.h" @@ -100,7 +100,7 @@ class Skin Image *mStickyImageDown; /**< Sticky Button Image */ }; -class Theme : public Palette, public ConfigListener +class Theme : public Palette, public Mana::Listener { public: static Theme *instance(); @@ -218,7 +218,7 @@ class Theme : public Palette, public ConfigListener */ void setMinimumOpacity(float minimumOpacity); - void optionChanged(const std::string &); + void event(Channels channel, const Mana::Event &event); private: Theme(); diff --git a/src/gui/userpalette.cpp b/src/resources/userpalette.cpp index 9e202fe9..a6b5bc03 100644 --- a/src/gui/userpalette.cpp +++ b/src/resources/userpalette.cpp @@ -20,7 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "userpalette.h" +#include "resources/userpalette.h" #include "configuration.h" #include "client.h" diff --git a/src/gui/userpalette.h b/src/resources/userpalette.h index be02db10..be02db10 100644 --- a/src/gui/userpalette.h +++ b/src/resources/userpalette.h diff --git a/src/resources/wallpaper.cpp b/src/resources/wallpaper.cpp index c8857745..0fd1c291 100644 --- a/src/resources/wallpaper.cpp +++ b/src/resources/wallpaper.cpp @@ -53,21 +53,21 @@ static void initDefaultWallpaperPaths() ResourceManager *resman = ResourceManager::getInstance(); // Init the path - wallpaperPath = branding.getValue("wallpapersPath", ""); + wallpaperPath = branding.getStringValue("wallpapersPath"); if (wallpaperPath.empty() || !resman->isDirectory(wallpaperPath)) - wallpaperPath = paths.getValue("wallpapers", ""); + wallpaperPath = paths.getStringValue("wallpapers"); if (wallpaperPath.empty() || !resman->isDirectory(wallpaperPath)) wallpaperPath = "graphics/images/"; // Init the default file - wallpaperFile = branding.getValue("wallpaperFile", ""); + wallpaperFile = branding.getStringValue("wallpaperFile"); if (!wallpaperFile.empty() && !resman->isDirectory(wallpaperFile)) return; else - wallpaperFile = paths.getValue("wallpaperFile", ""); + wallpaperFile = paths.getStringValue("wallpaperFile"); if (wallpaperFile.empty() || resman->isDirectory(wallpaperFile)) wallpaperFile = "login_wallpaper.png"; @@ -80,7 +80,7 @@ bool wallpaperCompare(WallpaperData a, WallpaperData b) return (aa > ab || (aa == ab && a.width > b.width)); } - +#include <iostream> void Wallpaper::loadWallpapers() { wallpaperData.clear(); diff --git a/src/simpleanimation.cpp b/src/simpleanimation.cpp index a8d33a43..7ef433ea 100644 --- a/src/simpleanimation.cpp +++ b/src/simpleanimation.cpp @@ -33,14 +33,16 @@ SimpleAnimation::SimpleAnimation(Animation *animation): mAnimation(animation), mAnimationTime(0), mAnimationPhase(0), - mCurrentFrame(mAnimation->getFrame(0)) + mCurrentFrame(mAnimation->getFrame(0)), + mInitialized(true) { } SimpleAnimation::SimpleAnimation(xmlNodePtr animationNode): mAnimation(new Animation), mAnimationTime(0), - mAnimationPhase(0) + mAnimationPhase(0), + mInitialized(false) { initializeAnimation(animationNode); mCurrentFrame = mAnimation->getFrame(0); @@ -48,7 +50,8 @@ SimpleAnimation::SimpleAnimation(xmlNodePtr animationNode): SimpleAnimation::~SimpleAnimation() { - delete mAnimation; + if (mAnimation) + delete mAnimation; } bool SimpleAnimation::draw(Graphics *graphics, int posX, int posY) const @@ -79,38 +82,55 @@ void SimpleAnimation::setFrame(int frame) void SimpleAnimation::update(int timePassed) { - mAnimationTime += timePassed; - - while (mAnimationTime > mCurrentFrame->delay && mCurrentFrame->delay > 0) + if (mInitialized) { - mAnimationTime -= mCurrentFrame->delay; - mAnimationPhase++; + mAnimationTime += timePassed; - if (mAnimationPhase >= mAnimation->getLength()) - mAnimationPhase = 0; + while (mAnimationTime > mCurrentFrame->delay && mCurrentFrame->delay > 0) + { + mAnimationTime -= mCurrentFrame->delay; + mAnimationPhase++; - mCurrentFrame = mAnimation->getFrame(mAnimationPhase); + if (mAnimationPhase >= mAnimation->getLength()) + mAnimationPhase = 0; + + mCurrentFrame = mAnimation->getFrame(mAnimationPhase); + } } } int SimpleAnimation::getLength() const { - return mAnimation->getLength(); + if (mAnimation) + return mAnimation->getLength(); + else + return 0; } Image *SimpleAnimation::getCurrentImage() const { - return mCurrentFrame->image; + if (mCurrentFrame) + return mCurrentFrame->image; + else + return NULL; } void SimpleAnimation::initializeAnimation(xmlNodePtr animationNode) { + mInitialized = false; + + if (!animationNode) + return; + ImageSet *imageset = ResourceManager::getInstance()->getImageSet( XML::getProperty(animationNode, "imageset", ""), XML::getProperty(animationNode, "width", 0), XML::getProperty(animationNode, "height", 0) ); + if (!imageset) + return; + // Get animation frames for ( xmlNodePtr frameNode = animationNode->xmlChildrenNode; frameNode; @@ -172,4 +192,6 @@ void SimpleAnimation::initializeAnimation(xmlNodePtr animationNode) mAnimation->addTerminator(); } } + + mInitialized = true; } diff --git a/src/simpleanimation.h b/src/simpleanimation.h index a8a43b33..e679442e 100644 --- a/src/simpleanimation.h +++ b/src/simpleanimation.h @@ -78,6 +78,9 @@ class SimpleAnimation /** Current animation phase. */ Frame *mCurrentFrame; + + /** Tell whether the animation is ready */ + bool mInitialized; }; #endif diff --git a/src/sound.cpp b/src/sound.cpp index fa39e49b..c64e10d8 100644 --- a/src/sound.cpp +++ b/src/sound.cpp @@ -21,14 +21,14 @@ #include <SDL.h> +#include "configuration.h" +#include "localplayer.h" #include "log.h" #include "sound.h" #include "resources/resourcemanager.h" #include "resources/soundeffect.h" -#include "configuration.h" - Sound::Sound(): mInstalled(false), mSfxVolume(100), @@ -142,7 +142,7 @@ void Sound::setSfxVolume(int volume) static Mix_Music *loadMusic(const std::string &filename) { ResourceManager *resman = ResourceManager::getInstance(); - std::string path = resman->getPath("music/" + filename); + std::string path = resman->getPath(paths.getStringValue("music") + filename); if (path.find(".zip/") != std::string::npos || path.find(".zip\\") != std::string::npos) @@ -152,7 +152,7 @@ static Mix_Music *loadMusic(const std::string &filename) logger->log("Loading music \"%s\" from temporary file tempMusic.ogg", path.c_str()); bool success = resman->copyFile( - paths.getValue("music", "music/") + paths.getStringValue("music") + filename, "tempMusic.ogg"); if (success) path = resman->getPath("tempMusic.ogg"); @@ -228,17 +228,38 @@ void Sound::fadeOutMusic(int ms) } } -void Sound::playSfx(const std::string &path) +void Sound::playSfx(const std::string &path, int x, int y) { if (!mInstalled || path.empty()) return; + std::string tmpPath; + if (!path.find("sfx/")) + tmpPath = path; + else + tmpPath = paths.getValue("sfx", "sfx/") + path; ResourceManager *resman = ResourceManager::getInstance(); - SoundEffect *sample = resman->getSoundEffect(path); + SoundEffect *sample = resman->getSoundEffect(tmpPath); if (sample) { logger->log("Sound::playSfx() Playing: %s", path.c_str()); - sample->play(0, 120); + int vol = 120; + if (player_node && x > 0 && y > 0) + { + int dx = player_node->getTileX() - x; + int dy = player_node->getTileY() - y; + if (dx < 0) + dx = -dx; + if (dy < 0) + dy = -dy; + int dist = dx > dy ? dx : dy; + + // Check for negative values + if (dist * 8 > vol) + return; + vol -= dist * 8; + } + sample->play(0, vol); } } diff --git a/src/sound.h b/src/sound.h index bf5dc3f6..bfb3837b 100644 --- a/src/sound.h +++ b/src/sound.h @@ -86,8 +86,10 @@ class Sound * Plays an item. * * @param path The resource path to the sound file. + * @param x The vertical distance of the sound in tiles. + * @param y The horizontal distance of the sound in tiles. */ - void playSfx(const std::string &path); + void playSfx(const std::string &path, int x = 0, int y = 0); private: /** Logs various info about sound device. */ diff --git a/src/sprite.h b/src/sprite.h index 847c01a6..38db8b41 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -1,7 +1,6 @@ /* * The Mana Client - * Copyright (C) 2004-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2010 The Mana Developers * * This file is part of The Mana Client. * @@ -22,64 +21,90 @@ #ifndef SPRITE_H #define SPRITE_H +#include "resources/spritedef.h" + class Graphics; +class Image; -/** - * A sprite is some visible object on a map. This abstract class defines the - * interface used by the map to sort and display the sprite. - */ class Sprite { public: + virtual ~Sprite() {} + + /** + * Resets the sprite. + * + * @returns true if the sprite changed, false otherwise + */ + virtual bool reset() = 0; + /** - * Destructor. + * Plays an action using the current direction. + * + * @returns true if the sprite changed, false otherwise */ - virtual - ~Sprite() {} + virtual bool play(std::string action) = 0; /** - * Draws the sprite to the given graphics context. + * Inform the animation of the passed time so that it can output the + * correct animation frame. * - * Note: this function could be simplified if the graphics context - * would support setting a translation offset. It already does this - * partly with the clipping rectangle support. + * @returns true if the sprite changed, false otherwise */ - virtual void draw(Graphics *graphics, int offsetX, int offsetY) const = 0; + virtual bool update(int time) = 0; /** - * Returns the horizontal size of the sprites graphical representation - * in pixels or 0 when it is undefined. + * Draw the current animation frame at the coordinates given in screen + * pixels. */ - virtual int getWidth() const - { return 0; } + virtual bool draw(Graphics* graphics, int posX, int posY) const = 0; /** - * Returns the vertical size of the sprites graphical representation - * in pixels or 0 when it is undefined. + * Gets the width in pixels of the image of the current frame */ - virtual int getHeight() const - { return 0; } + virtual int getWidth() const = 0; /** - * Returns the pixel Y coordinate of the sprite. + * Gets the height in pixels of the image of the current frame */ - virtual int getPixelY() const = 0; + virtual int getHeight() const = 0; /** - * Returns the number of Image layers used to draw the sprite. + * Returns a reference to the current image being drawn. */ - virtual int getNumberOfLayers() const - { return 0; } + virtual const Image* getImage() const = 0; /** - * Returns the current alpha value used to draw the sprite. + * Sets the direction. + * + * @returns true if the sprite changed, false otherwise */ - virtual float getAlpha() const = 0; + virtual bool setDirection(SpriteDirection direction) = 0; /** - * Sets the alpha value used to draw the sprite. + * Sets the alpha value of the animated sprite */ - virtual void setAlpha(float alpha) = 0; + virtual void setAlpha(float alpha) + { mAlpha = alpha; } + + /** + * Returns the current alpha opacity of the animated sprite. + */ + virtual float getAlpha() const + { return mAlpha; } + + /** + * Returns the current frame number for the sprite. + */ + virtual size_t getCurrentFrame() const = 0; + + /** + * Returns the frame count for the sprite. + */ + virtual size_t getFrameCount() const = 0; + + protected: + float mAlpha; /**< The alpha opacity used to draw */ }; -#endif +#endif // SPRITE_H diff --git a/src/statuseffect.cpp b/src/statuseffect.cpp index 1f913f4a..d7c3f17a 100644 --- a/src/statuseffect.cpp +++ b/src/statuseffect.cpp @@ -21,11 +21,10 @@ #include "statuseffect.h" +#include "event.h" #include "log.h" #include "sound.h" -#include "gui/widgets/chattab.h" - #include "utils/xml.h" #include "configuration.h" @@ -52,7 +51,7 @@ void StatusEffect::playSFX() void StatusEffect::deliverMessage() { if (!mMessage.empty()) - localChatTab->chatLog(mMessage, BY_SERVER); + SERVER_NOTICE(mMessage) } Particle *StatusEffect::getParticle() @@ -70,22 +69,22 @@ AnimatedSprite *StatusEffect::getIcon() else { AnimatedSprite *sprite = AnimatedSprite::load( - paths.getValue("sprites", "graphics/sprites/") + mIcon); + paths.getStringValue("sprites") + mIcon); if (false && sprite) { - sprite->play(ACTION_DEFAULT); + sprite->play(SpriteAction::DEFAULT); sprite->reset(); } return sprite; } } -SpriteAction StatusEffect::getAction() +std::string StatusEffect::getAction() { if (mAction.empty()) - return ACTION_INVALID; + return SpriteAction::INVALID; else - return SpriteDef::makeSpriteAction(mAction); + return mAction; } diff --git a/src/statuseffect.h b/src/statuseffect.h index fc0e7336..3f715a16 100644 --- a/src/statuseffect.h +++ b/src/statuseffect.h @@ -56,9 +56,9 @@ public: AnimatedSprite *getIcon(); /** - * Retrieves an action to perform, or ACTION_INVALID + * Retrieves an action to perform, or SpriteAction::INVALID */ - SpriteAction getAction(); + std::string getAction(); /** * Determines whether the particle effect should be restarted when the diff --git a/src/text.cpp b/src/text.cpp index f6c71dba..53b21e79 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -27,11 +27,9 @@ #include "textrenderer.h" #include "gui/gui.h" -#include "gui/palette.h" -#include "gui/theme.h" -#include "resources/resourcemanager.h" #include "resources/image.h" +#include "resources/theme.h" #include <guichan/font.hpp> @@ -41,17 +39,22 @@ Image *Text::mBubbleArrow; Text::Text(const std::string &text, int x, int y, gcn::Graphics::Alignment alignment, - const gcn::Color* color, bool isSpeech) : + const gcn::Color* color, bool isSpeech, + gcn::Font *font) : mText(text), mColor(color), - mFont(gui->getFont()), mIsSpeech(isSpeech) { + if (!font) + mFont = gui->getFont(); + else + mFont = font; + if (textManager == 0) { textManager = new TextManager; Image *sbImage = Theme::getImageFromTheme("bubble.png|W:#" - + config.getValue("speechBubblecolor", "000000")); + + config.getStringValue("speechBubblecolor")); mBubble.grid[0] = sbImage->getSubImage(0, 0, 5, 5); mBubble.grid[1] = sbImage->getSubImage(5, 0, 5, 5); mBubble.grid[2] = sbImage->getSubImage(10, 0, 5, 5); @@ -62,7 +65,7 @@ Text::Text(const std::string &text, int x, int y, mBubble.grid[7] = sbImage->getSubImage(5, 10, 5, 5); mBubble.grid[8] = sbImage->getSubImage(10, 10, 5, 5); mBubbleArrow = sbImage->getSubImage(0, 15, 15, 10); - const float bubbleAlpha = config.getValue("speechBubbleAlpha", 1.0); + const float bubbleAlpha = config.getFloatValue("speechBubbleAlpha"); for (int i = 0; i < 9; i++) { mBubble.grid[i]->setAlpha(bubbleAlpha); @@ -145,8 +148,8 @@ void Text::draw(gcn::Graphics *graphics, int xOff, int yOff) FlashText::FlashText(const std::string &text, int x, int y, gcn::Graphics::Alignment alignment, - const gcn::Color *color) : - Text(text, x, y, alignment, color), + const gcn::Color *color, gcn::Font *font) : + Text(text, x, y, alignment, color, false, font), mTime(0) { } @@ -24,7 +24,6 @@ #define TEXT_H #include "graphics.h" -#include "guichanfwd.h" #include <guichan/color.hpp> @@ -40,7 +39,8 @@ class Text */ Text(const std::string &text, int x, int y, gcn::Graphics::Alignment alignment, - const gcn::Color *color, bool isSpeech = false); + const gcn::Color *color, bool isSpeech = false, + gcn::Font *font = 0); /** * Destructor. The text is removed from the screen. @@ -84,7 +84,8 @@ class FlashText : public Text public: FlashText(const std::string &text, int x, int y, gcn::Graphics::Alignment alignment, - const gcn::Color* color); + const gcn::Color* color, + gcn::Font *font = 0); /** * Remove the text from the screen diff --git a/src/textparticle.cpp b/src/textparticle.cpp index c9b5fc18..0753cc38 100644 --- a/src/textparticle.cpp +++ b/src/textparticle.cpp @@ -36,10 +36,10 @@ TextParticle::TextParticle(Map *map, const std::string &text, { } -void TextParticle::draw(Graphics *graphics, int offsetX, int offsetY) const +bool TextParticle::draw(Graphics *graphics, int offsetX, int offsetY) const { - if (!mAlive) - return; + if (!isAlive()) + return false; int screenX = (int) mPos.x + offsetX; int screenY = (int) mPos.y - (int) mPos.z + offsetY; @@ -50,4 +50,6 @@ void TextParticle::draw(Graphics *graphics, int offsetX, int offsetY) const TextRenderer::renderText(graphics, mText, screenX, screenY, gcn::Graphics::CENTER, color, mTextFont, mOutline, false); + + return true; } diff --git a/src/textparticle.h b/src/textparticle.h index a61bf8d9..79af7406 100644 --- a/src/textparticle.h +++ b/src/textparticle.h @@ -22,7 +22,6 @@ #ifndef TEXTPARTICLE_H #define TEXTPARTICLE_H -#include "guichanfwd.h" #include "particle.h" class TextParticle : public Particle @@ -38,7 +37,7 @@ class TextParticle : public Particle /** * Draws the particle image. */ - virtual void draw(Graphics *graphics, int offsetX, int offsetY) const; + virtual bool draw(Graphics *graphics, int offsetX, int offsetY) const; // hack to improve text visibility virtual int getPixelY() const diff --git a/src/textrenderer.h b/src/textrenderer.h index 4da868f1..fccdd3c7 100644 --- a/src/textrenderer.h +++ b/src/textrenderer.h @@ -24,7 +24,7 @@ #include "graphics.h" -#include "gui/theme.h" +#include "resources/theme.h" /** * Class for text rendering. Used by the TextParticle, the Text and FlashText diff --git a/src/tileset.h b/src/tileset.h index 56bc4547..6c2ee394 100644 --- a/src/tileset.h +++ b/src/tileset.h @@ -33,8 +33,8 @@ class Tileset : public ImageSet /** * Constructor. */ - Tileset(Image *img, int w, int h, int firstGid): - ImageSet(img, w, h), + Tileset(Image *img, int w, int h, int firstGid, int margin, int spacing): + ImageSet(img, w, h, margin, spacing), mFirstGid(firstGid) { } diff --git a/src/utils/copynpaste.cpp b/src/utils/copynpaste.cpp index 31aa7bf9..3d2e3b80 100644 --- a/src/utils/copynpaste.cpp +++ b/src/utils/copynpaste.cpp @@ -269,7 +269,6 @@ static char* getSelection(Display *dpy, Window us, Atom selection) return (char*)data; } } - printf("Timeout\n"); return NULL; } diff --git a/src/utils/stringutils.cpp b/src/utils/stringutils.cpp index 445427fe..96b67370 100644 --- a/src/utils/stringutils.cpp +++ b/src/utils/stringutils.cpp @@ -26,7 +26,7 @@ #include <cstdarg> #include <cstdio> -const int UTF8_MAX_SIZE = 10; +static int UTF8_MAX_SIZE = 10; std::string &trim(std::string &str) { @@ -155,7 +155,8 @@ bool isWordSeparator(char chr) return (chr == ' ' || chr == ',' || chr == '.' || chr == '"'); } -const std::string findSameSubstring(const std::string &str1, const std::string &str2) +const std::string findSameSubstring(const std::string &str1, + const std::string &str2) { int minLength = str1.length() > str2.length() ? str2.length() : str1.length(); for (int f = 0; f < minLength; f ++) @@ -176,6 +177,18 @@ const char* getSafeUtf8String(std::string text) return buf; } +bool getBoolFromString(const std::string &text, bool def) +{ + std::string a = text; + toLower(trim(a)); + if (a == "true" || a == "1" || a == "on" || a == "yes" || a == "y") + return true; + if (a == "false" || a == "0" || a == "off" || a == "no" || a == "n") + return false; + else + return def; +} + std::string autocomplete(std::vector<std::string> &candidates, std::string base) { @@ -210,3 +223,9 @@ std::string autocomplete(std::vector<std::string> &candidates, return newName; } + +std::string normalize(const std::string &name) +{ + std::string normalized = name; + return toLower(trim(normalized));; +} diff --git a/src/utils/stringutils.h b/src/utils/stringutils.h index 5f1f05f0..2c6fad78 100644 --- a/src/utils/stringutils.h +++ b/src/utils/stringutils.h @@ -120,13 +120,33 @@ std::string removeColors(std::string msg); */ int compareStrI(const std::string &a, const std::string &b); +/** + * Tells wether the character is a word separator. + */ bool isWordSeparator(char chr); -const std::string findSameSubstring(const std::string &str1, const std::string &str2); +const std::string findSameSubstring(const std::string &str1, + const std::string &str2); const char* getSafeUtf8String(std::string text); +/** + * Returns a bool value depending on the given string value. + * + * @param text the string used to get the bool value + * @return a boolean value.. + */ +bool getBoolFromString(const std::string &text, bool def = false); + +/** + * Returns the most approaching string of base from candidates. + */ std::string autocomplete(std::vector<std::string> &candidates, std::string base); +/** + * Normalize a string, which means lowercase and trim it. + */ +std::string normalize(const std::string &name); + #endif // UTILS_STRINGUTILS_H diff --git a/src/utils/xml.cpp b/src/utils/xml.cpp index 9835f88c..65eb1370 100644 --- a/src/utils/xml.cpp +++ b/src/utils/xml.cpp @@ -21,15 +21,20 @@ #include "utils/xml.h" +#include <iostream> +#include <fstream> +#include <cstring> + +#include <libxml/parser.h> +#include <libxml/xmlerror.h> + #include "log.h" #include "resources/resourcemanager.h" +#include "utils/stringutils.h" #include "utils/zlib.h" -#include <libxml/parser.h> -#include <libxml/xmlerror.h> - namespace XML { static void xmlLogger(void *ctx, xmlErrorPtr error); @@ -129,6 +134,18 @@ namespace XML return def; } + bool getBoolProperty(xmlNodePtr node, const char* name, bool def) + { + bool ret = def; + xmlChar *prop = xmlGetProp(node, BAD_CAST name); + if (prop) + { + ret = getBoolFromString((char*) prop, def); + xmlFree(prop); + } + return ret; + } + xmlNodePtr findFirstChildByName(xmlNodePtr parent, const char *name) { for_each_xml_child_node(child, parent) @@ -149,7 +166,7 @@ namespace XML logger->log("Error in unknown xml file on line %d", error->line); - logger->log(error->message); + logger->log("%s", error->message); // No need to keep errors around xmlCtxtResetLastError(error->ctxt); diff --git a/src/utils/xml.h b/src/utils/xml.h index 8ffecb76..48e66787 100644 --- a/src/utils/xml.h +++ b/src/utils/xml.h @@ -60,14 +60,14 @@ namespace XML }; /** - * Gets an integer property from an xmlNodePtr. + * Gets an floating point property from an xmlNodePtr. */ - int getProperty(xmlNodePtr node, const char *name, int def); + double getFloatProperty(xmlNodePtr node, const char *name, double def); /** - * Gets an floating point property from an xmlNodePtr. + * Gets an integer property from an xmlNodePtr. */ - double getFloatProperty(xmlNodePtr node, const char *name, double def); + int getProperty(xmlNodePtr node, const char *name, int def); /** * Gets a string property from an xmlNodePtr. @@ -76,6 +76,11 @@ namespace XML const std::string &def); /** + * Gets a boolean property from an xmlNodePtr. + */ + bool getBoolProperty(xmlNodePtr node, const char *name, bool def); + + /** * Finds the first child node with the given name */ xmlNodePtr findFirstChildByName(xmlNodePtr parent, const char *name); diff --git a/src/variabledata.h b/src/variabledata.h new file mode 100644 index 00000000..19e09795 --- /dev/null +++ b/src/variabledata.h @@ -0,0 +1,131 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef VARIABLEDATA_H +#define VARIABLEDATA_H + +#include <string> + +class ActorSprite; +class Item; + +namespace Mana +{ + +class VariableData +{ + public: + enum DataType + { + DATA_NONE, + DATA_INT, + DATA_STRING, + DATA_FLOAT, + DATA_BOOL, + DATA_ITEM, + DATA_ACTOR + }; + + virtual ~VariableData() {}; + + virtual int getType() const = 0; +}; + +class IntData : public VariableData +{ +public: + IntData(int value) { mData = value; } + + int getData() const { return mData; } + + int getType() const { return DATA_INT; } + +private: + int mData; +}; + +class StringData : public VariableData +{ +public: + StringData(const std::string &value) { mData = value; } + + const std::string &getData() const { return mData; } + + int getType() const { return DATA_STRING; } + +private: + std::string mData; +}; + +class FloatData : public VariableData +{ +public: + FloatData(double value) { mData = value; } + + double getData() const { return mData; } + + int getType() const { return DATA_FLOAT; } + +private: + double mData; +}; + +class BoolData : public VariableData +{ +public: + BoolData(bool value) { mData = value; } + + bool getData() const { return mData; } + + int getType() const { return DATA_BOOL; } + +private: + bool mData; +}; + +class ItemData : public VariableData +{ +public: + ItemData(Item *value) { mData = value; } + + Item *getData() const { return mData; } + + int getType() const { return DATA_ITEM; } + +private: + Item *mData; +}; + +class ActorData : public VariableData +{ +public: + ActorData(ActorSprite *value) { mData = value; } + + ActorSprite *getData() const { return mData; } + + int getType() const { return DATA_ACTOR; } + +private: + ActorSprite *mData; +}; + +} // namespace Mana + +#endif diff --git a/src/winver.h b/src/winver.h index ea8d8e9c..773693bb 100644 --- a/src/winver.h +++ b/src/winver.h @@ -1,6 +1,6 @@ /* VERSION DEFINITIONS */ -#define VER_MAJOR 0 -#define VER_MINOR 5 +#define VER_MAJOR 1 +#define VER_MINOR 0 #define VER_RELEASE 0 #define VER_BUILD 0 -#define PACKAGE_VERSION "0.5.0.0" +#define PACKAGE_VERSION "1.0.0.0" |