diff options
Diffstat (limited to 'src')
241 files changed, 6155 insertions, 2558 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 94ee854a..b54c7c6c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,7 +8,7 @@ FIND_PACKAGE(PhysFS REQUIRED) FIND_PACKAGE(PNG REQUIRED) SET(FLAGS "-DPACKAGE_VERSION=\\\"${VERSION}\\\"") -SET(FLAGS "${FLAGS} -DTMW_DATADIR=\\\"${PKG_DATADIR}/\\\"") +SET(FLAGS "${FLAGS} -DAETHYRA_DATADIR=\\\"${PKG_DATADIR}/\\\"") SET(GUICHAN_COMPONENTS "SDL") IF (WITH_OPENGL) @@ -49,6 +49,8 @@ MARK_AS_ADVANCED(SDL_INCLUDE_DIR) MARK_AS_ADVANCED(SDL_LIBRARY) SET(SRCS + gui/widgets/dropdown.cpp + gui/widgets/dropdown.h gui/widgets/resizegrip.cpp gui/widgets/resizegrip.h gui/box.cpp @@ -116,8 +118,6 @@ SET(SRCS gui/minimap.h gui/ministatus.cpp gui/ministatus.h - gui/newskill.cpp - gui/newskill.h gui/npclistdialog.cpp gui/npclistdialog.h gui/npc_text.cpp @@ -161,6 +161,8 @@ SET(SRCS gui/skill.h gui/slider.cpp gui/slider.h + gui/speechbubble.cpp + gui/speechbubble.h gui/status.cpp gui/status.h gui/tabbedcontainer.cpp @@ -229,6 +231,8 @@ SET(SRCS resources/animation.h resources/buddylist.cpp resources/buddylist.h + resources/colordb.cpp + resources/colordb.h resources/dye.cpp resources/dye.h resources/image.cpp @@ -286,6 +290,7 @@ SET(SRCS engine.h equipment.cpp equipment.h + extensions.h floor_item.cpp floor_item.h flooritemmanager.cpp @@ -345,11 +350,13 @@ SET(SRCS textparticle.h tileset.h vector.h + effectmanager.cpp + effectmanager.h ) -ADD_EXECUTABLE(tmw ${SRCS}) +ADD_EXECUTABLE(aethyra ${SRCS}) -TARGET_LINK_LIBRARIES(tmw +TARGET_LINK_LIBRARIES(aethyra ${SDL_LIBRARY} ${SDLIMAGE_LIBRARY} ${SDLMIXER_LIBRARY} @@ -362,6 +369,6 @@ TARGET_LINK_LIBRARIES(tmw ${OPENGL_LIBRARIES} ) -INSTALL(TARGETS tmw RUNTIME DESTINATION ${PKG_BINDIR}) +INSTALL(TARGETS aethyra RUNTIME DESTINATION ${PKG_BINDIR}) -SET_TARGET_PROPERTIES(tmw PROPERTIES COMPILE_FLAGS "${FLAGS}") +SET_TARGET_PROPERTIES(aethyra PROPERTIES COMPILE_FLAGS "${FLAGS}") diff --git a/src/Makefile.am b/src/Makefile.am index e9d69b5e..507db6f8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,30 +1,34 @@ -bin_PROGRAMS = tmw -tmw_SOURCES = gui/widgets/resizegrip.cpp \ +bin_PROGRAMS = aethyra +aethyra_SOURCES = gui/widgets/dropdown.cpp \ + gui/widgets/dropdown.h \ + gui/widgets/resizegrip.cpp \ gui/widgets/resizegrip.h \ - gui/box.h \ gui/box.cpp \ + gui/box.h \ gui/browserbox.cpp \ gui/browserbox.h \ - gui/buddywindow.cpp \ - gui/buddywindow.h \ gui/button.cpp \ gui/button.h \ + gui/buttonbox.cpp \ + gui/buttonbox.h \ gui/buy.cpp \ gui/buy.h \ gui/buysell.cpp \ gui/buysell.h \ gui/chargedialog.cpp \ gui/chargedialog.h \ - gui/char_server.cpp \ - gui/char_server.h \ gui/char_select.cpp \ gui/char_select.h \ + gui/char_server.cpp \ + gui/char_server.h \ gui/chat.cpp \ gui/chat.h \ gui/chatinput.cpp \ gui/chatinput.h \ gui/checkbox.cpp \ gui/checkbox.h \ + gui/colour.cpp \ + gui/colour.h \ gui/confirm_dialog.cpp \ gui/confirm_dialog.h \ gui/connection.cpp \ @@ -39,8 +43,8 @@ tmw_SOURCES = gui/widgets/resizegrip.cpp \ gui/gccontainer.h \ gui/gui.cpp \ gui/gui.h \ - gui/hbox.h \ gui/hbox.cpp \ + gui/hbox.h \ gui/help.cpp \ gui/help.h \ gui/inttextbox.h \ @@ -66,8 +70,6 @@ tmw_SOURCES = gui/widgets/resizegrip.cpp \ gui/minimap.h \ gui/ministatus.cpp \ gui/ministatus.h \ - gui/newskill.cpp \ - gui/newskill.h \ gui/npclistdialog.cpp \ gui/npclistdialog.h \ gui/npc_text.cpp \ @@ -90,10 +92,12 @@ tmw_SOURCES = gui/widgets/resizegrip.cpp \ gui/scrollarea.h \ gui/sell.cpp \ gui/sell.h \ - gui/setup_audio.cpp \ - gui/setup_audio.h \ gui/setup.cpp \ gui/setup.h \ + gui/setup_audio.cpp \ + gui/setup_audio.h \ + gui/setup_colours.cpp \ + gui/setup_colours.h \ gui/setup_joystick.cpp \ gui/setup_joystick.h \ gui/setup_keyboard.cpp \ @@ -111,14 +115,16 @@ tmw_SOURCES = gui/widgets/resizegrip.cpp \ gui/skill.h \ gui/slider.cpp \ gui/slider.h \ + gui/speechbubble.cpp \ + gui/speechbubble.h \ gui/status.cpp \ gui/status.h \ gui/tabbedcontainer.cpp \ gui/tabbedcontainer.h \ - gui/table.h \ gui/table.cpp \ - gui/table_model.h \ + gui/table.h \ gui/table_model.cpp \ + gui/table_model.h \ gui/textbox.cpp \ gui/textbox.h \ gui/textfield.cpp \ @@ -163,6 +169,8 @@ tmw_SOURCES = gui/widgets/resizegrip.cpp \ net/network.h \ net/npchandler.cpp \ net/npchandler.h \ + net/partyhandler.cpp \ + net/partyhandler.h \ net/playerhandler.cpp \ net/playerhandler.h \ net/protocol.cpp \ @@ -177,6 +185,10 @@ tmw_SOURCES = gui/widgets/resizegrip.cpp \ resources/ambientoverlay.h \ resources/animation.cpp \ resources/animation.h \ + resources/buddylist.cpp \ + resources/buddylist.h \ + resources/colordb.cpp \ + resources/colordb.h \ resources/dye.cpp \ resources/dye.h \ resources/image.cpp \ @@ -209,8 +221,6 @@ tmw_SOURCES = gui/widgets/resizegrip.cpp \ resources/soundeffect.cpp \ resources/spritedef.h \ resources/spritedef.cpp \ - resources/buddylist.h \ - resources/buddylist.cpp \ utils/base64.cpp \ utils/base64.h \ utils/dtor.h \ @@ -233,10 +243,13 @@ tmw_SOURCES = gui/widgets/resizegrip.cpp \ configlistener.h \ configuration.cpp \ configuration.h \ + effectmanager.cpp \ + effectmanager.h \ engine.cpp \ engine.h \ equipment.cpp \ equipment.h \ + extensions.h \ floor_item.cpp \ floor_item.h \ flooritemmanager.cpp \ @@ -276,16 +289,20 @@ tmw_SOURCES = gui/widgets/resizegrip.cpp \ openglgraphics.h \ particle.cpp \ particle.h \ + particlecontainer.cpp \ + particlecontainer.h \ particleemitter.cpp \ particleemitter.h \ particleemitterprop.h \ - particlecontainer.cpp \ - particlecontainer.h \ + party.cpp \ + party.h \ player.cpp \ player.h \ player_relations.cpp \ player_relations.h \ properties.h \ + recorder.cpp \ + recorder.h \ serverinfo.h \ shopitem.cpp \ shopitem.h \ @@ -301,15 +318,16 @@ tmw_SOURCES = gui/widgets/resizegrip.cpp \ textparticle.cpp \ textparticle.h \ tileset.h \ + vector.cpp \ vector.h # set the include path found by configure INCLUDES = \ $(all_includes) \ - -DTMW_DATADIR=\""$(pkgdatadir)/"\" + -DAETHYRA_DATADIR=\""$(pkgdatadir)/"\" # the library search path. -tmw_LDFLAGS = $(all_libraries) $(LIBSDL_RPATH) `pkg-config --libs libxml-2.0` -tmw_CXXFLAGS = -Wall $(LIBSDL_CFLAGS) `pkg-config --cflags libxml-2.0` $(CURL_CFLAGS) -tmw_LDADD = $(LIBSDL_LIBS) -lguichan_sdl $(CURL_LIBS) -tmw_TARGET = tmw +aethyra_LDFLAGS = $(all_libraries) $(LIBSDL_RPATH) `pkg-config --libs libxml-2.0` +aethyra_CXXFLAGS = -Wall $(LIBSDL_CFLAGS) `pkg-config --cflags libxml-2.0` $(CURL_CFLAGS) +aethyra_LDADD = $(LIBSDL_LIBS) -lguichan_sdl $(CURL_LIBS) +aethyra_TARGET = aethyra diff --git a/src/tmw.rc b/src/aethyra.rc index 9661ac6b..d2db954f 100644 --- a/src/tmw.rc +++ b/src/aethyra.rc @@ -1,23 +1,22 @@ -#include <windows.h> // include for version info constants - -#include "winver.h" - -A ICON MOVEABLE PURE LOADONCALL DISCARDABLE "data/icons/tmw.ico" - -1 VERSIONINFO -FILEVERSION VER_MAJOR,VER_MINOR,VER_RELEASE,VER_BUILD -PRODUCTVERSION VER_MAJOR,VER_MINOR,VER_RELEASE,VER_BUILD -FILETYPE VFT_APP { - BLOCK "StringFileInfo" { - BLOCK "040904E4" { - VALUE "CompanyName", "The Mana World Development Team" - VALUE "FileVersion", PACKAGE_VERSION - VALUE "FileDescription", "The Mana World" - VALUE "LegalCopyright", "2004-2006 (C)" - VALUE "OriginalFilename", "tmw.exe" - VALUE "ProductName", "The Mana World MMORPG" - VALUE "ProductVersion", PACKAGE_VERSION - } - } +#include <windows.h> // include for version info constants
+
+#include "winver.h"
+
+A ICON MOVEABLE PURE LOADONCALL DISCARDABLE "data/icons/aethyra.ico"
+
+1 VERSIONINFO
+FILEVERSION VER_MAJOR,VER_MINOR,VER_RELEASE,VER_BUILD
+PRODUCTVERSION VER_MAJOR,VER_MINOR,VER_RELEASE,VER_BUILD
+FILETYPE VFT_APP {
+ BLOCK "StringFileInfo" {
+ BLOCK "040904E4" {
+ VALUE "CompanyName", "Aethyra Development Team"
+ VALUE "FileVersion", PACKAGE_VERSION
+ VALUE "FileDescription", "Aethyra Experiment"
+ VALUE "LegalCopyright", "2008 (C)"
+ VALUE "OriginalFilename", "aethyra.exe"
+ VALUE "ProductName", "Aethyra MMORPG"
+ VALUE "ProductVersion", PACKAGE_VERSION
+ }
+ }
} - diff --git a/src/animatedsprite.cpp b/src/animatedsprite.cpp index 203a82af..ba71d0e0 100644 --- a/src/animatedsprite.cpp +++ b/src/animatedsprite.cpp @@ -20,7 +20,6 @@ */ #include "animatedsprite.h" - #include "graphics.h" #include "log.h" diff --git a/src/animationparticle.cpp b/src/animationparticle.cpp index eb260157..59eacb05 100644 --- a/src/animationparticle.cpp +++ b/src/animationparticle.cpp @@ -20,7 +20,6 @@ */ #include "animationparticle.h" - #include "graphics.h" #include "simpleanimation.h" diff --git a/src/being.cpp b/src/being.cpp index 7c6d91e7..d98af29c 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -18,31 +18,36 @@ * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "being.h" -#include <algorithm> #include <cassert> +#include <cmath> #include "animatedsprite.h" +#include "being.h" +#include "configuration.h" #include "equipment.h" #include "game.h" #include "graphics.h" +#include "localplayer.h" #include "log.h" #include "map.h" #include "particle.h" #include "sound.h" -#include "localplayer.h" #include "text.h" -#include "resources/itemdb.h" -#include "resources/resourcemanager.h" #include "resources/imageset.h" +#include "resources/itemdb.h" #include "resources/iteminfo.h" +#include "resources/resourcemanager.h" #include "gui/gui.h" +#include "gui/speechbubble.h" #include "utils/dtor.h" #include "utils/tostring.h" +#include "utils/xml.h" + +#define BEING_EFFECTS_FILE "effects.xml" #include "utils/xml.h" @@ -50,6 +55,7 @@ #define HAIR_FILE "hair.xml" int Being::instances = 0; +int Being::mNumberOfHairstyles = 1; ImageSet *Being::emotionSet = NULL; static const int X_SPEECH_OFFSET = 18; @@ -58,7 +64,7 @@ static const int Y_SPEECH_OFFSET = 60; Being::Being(int id, int job, Map *map): mJob(job), mX(0), mY(0), - mAction(0), + mAction(STAND), mWalkTime(0), mEmotion(0), mEmotionTime(0), mAttackSpeed(350), @@ -67,6 +73,8 @@ Being::Being(int id, int job, Map *map): mWalkSpeed(150), mDirection(DOWN), mMap(NULL), + mIsGM(false), + mParticleEffects(config.getValue("particleeffects", 1)), mEquippedWeapon(NULL), mHairStyle(1), mHairColor(0), mGender(2), @@ -80,21 +88,32 @@ Being::Being(int id, int job, Map *map): { setMap(map); + mSpeechBubble = new SpeechBubble(); + if (instances == 0) { // Load the emotion set ResourceManager *rm = ResourceManager::getInstance(); emotionSet = rm->getImageSet("graphics/gui/emotions.png", 30, 32); if (!emotionSet) logger->error("Unable to load emotions!"); + + // Hairstyles are encoded as negative numbers. Count how far negative we can go. + int hairstyles = 1; + while (ItemDB::get(-hairstyles).getSprite(0) != "error.xml") + { + hairstyles++; + } + mNumberOfHairstyles = hairstyles; } instances++; - mSpeech = 0; + mSpeech = ""; + mText = 0; } Being::~Being() { - std::for_each(mSprites.begin(), mSprites.end(), make_dtor(mSprites)); + delete_all(mSprites); clearPath(); setMap(NULL); @@ -107,11 +126,11 @@ Being::~Being() emotionSet = NULL; } - delete mSpeech; + delete mSpeechBubble; + delete mText; } -void -Being::setDestination(Uint16 destX, Uint16 destY) +void Being::setDestination(Uint16 destX, Uint16 destY) { if (mMap) { @@ -119,14 +138,12 @@ Being::setDestination(Uint16 destX, Uint16 destY) } } -void -Being::clearPath() +void Being::clearPath() { mPath.clear(); } -void -Being::setPath(const Path &path) +void Being::setPath(const Path &path) { mPath = path; @@ -137,35 +154,27 @@ Being::setPath(const Path &path) } } -void -Being::setHairStyle(int style, int color) +void Being::setHairStyle(int style, int color) { - mHairStyle = style < 0 ? mHairStyle : style % getHairStylesNr(); - mHairColor = color < 0 ? mHairColor : color % getHairColorsNr(); + mHairStyle = style < 0 ? mHairStyle : style % mNumberOfHairstyles; + mHairColor = color < 0 ? mHairColor : color % ColorDB::size(); } -void -Being::setSprite(int slot, int id, std::string color) +void Being::setSprite(int slot, int id, std::string color) { assert(slot >= BASE_SPRITE && slot < VECTOREND_SPRITE); mSpriteIDs[slot] = id; mSpriteColors[slot] = color; } -void -Being::setSpeech(const std::string &text, Uint32 time) +void Being::setSpeech(const std::string &text, Uint32 time) { - // don't introduce a memory leak - delete mSpeech; + mSpeech = text; - mSpeech = new Text(text, mPx + X_SPEECH_OFFSET, mPy - Y_SPEECH_OFFSET, - gcn::Graphics::CENTER, speechFont, - gcn::Color(255, 255, 255)); mSpeechTime = 500; } -void -Being::takeDamage(int amount) +void Being::takeDamage(int amount) { gcn::Font *font; std::string damage = amount ? toString(amount) : "miss"; @@ -177,10 +186,6 @@ Being::takeDamage(int amount) } else { - // Hit particle effect - controlParticle(particleEngine->addEffect( - "graphics/particles/hit.particle.xml", 0, 0)); - if (getType() == MONSTER) { font = hitBlueFont; @@ -196,16 +201,34 @@ Being::takeDamage(int amount) mPx + 16, mPy + 16); } -void -Being::handleAttack(Being *victim, int damage) +void Being::showCrit() +{ + gcn::Font *font; + std::string text = "crit!"; + + // Selecting the right color + if (getType() == MONSTER) + { + font = hitBlueFont; + } + else + { + font = hitRedFont; + } + + // Show crit notice + particleEngine->addTextSplashEffect(text, 255, 255, 255, font, + mPx + 16, mPy + 16); +} + +void Being::handleAttack(Being *victim, int damage) { setAction(Being::ATTACK); mFrame = 0; mWalkTime = tick_time; } -void -Being::setMap(Map *map) +void Being::setMap(Map *map) { // Remove sprite from potential previous map if (mMap) @@ -225,16 +248,15 @@ Being::setMap(Map *map) mChildParticleEffects.clear(); } -void -Being::controlParticle(Particle *particle) +void Being::controlParticle(Particle *particle) { mChildParticleEffects.addLocally(particle); } -void -Being::setAction(Uint8 action) +void Being::setAction(Action action) { SpriteAction currentAction = ACTION_INVALID; + switch (action) { case WALK: @@ -248,7 +270,8 @@ Being::setAction(Uint8 action) { currentAction = mEquippedWeapon->getAttackType(); } - else { + else + { currentAction = ACTION_ATTACK; } for (int i = 0; i < VECTOREND_SPRITE; i++) @@ -285,10 +308,11 @@ Being::setAction(Uint8 action) } } - -void -Being::setDirection(Uint8 direction) +void Being::setDirection(Uint8 direction) { + if (mDirection == direction) + return; + mDirection = direction; SpriteDirection dir = getSpriteDirection(); @@ -299,8 +323,7 @@ Being::setDirection(Uint8 direction) } } -SpriteDirection -Being::getSpriteDirection() const +SpriteDirection Being::getSpriteDirection() const { SpriteDirection dir; @@ -316,15 +339,15 @@ Being::getSpriteDirection() const { dir = DIRECTION_RIGHT; } - else { - dir = DIRECTION_LEFT; + else + { + dir = DIRECTION_LEFT; } - + return dir; } -void -Being::nextStep() +void Being::nextStep() { if (mPath.empty()) { @@ -359,32 +382,31 @@ Being::nextStep() mWalkTime += mWalkSpeed / 10; } -void -Being::logic() +void Being::logic() { // Reduce the time that speech is still displayed - if (mSpeechTime > 0 && mSpeech) + if (mSpeechTime > 0) + mSpeechTime--; + + // Remove text if speech boxes aren't being used + if (mSpeechTime == 0 && mText) { - if (--mSpeechTime == 0) - { - delete mSpeech; - mSpeech = 0; - } + delete mText; + mText = 0; } int oldPx = mPx; int oldPy = mPy; + // Update pixel coordinates mPx = mX * 32 + getXOffset(); mPy = mY * 32 + getYOffset(); + if (mPx != oldPx || mPy != oldPy) { - if (mSpeech) - { - mSpeech->adviseXY(mPx + X_SPEECH_OFFSET, mPy - Y_SPEECH_OFFSET); - } updateCoords(); } + if (mEmotion != 0) { mEmotionTime--; @@ -406,8 +428,7 @@ Being::logic() mChildParticleEffects.setPositions((float)mPx + 16.0f, (float)mPy + 32.0f); } -void -Being::draw(Graphics *graphics, int offsetX, int offsetY) const +void Being::draw(Graphics *graphics, int offsetX, int offsetY) const { int px = mPx + offsetX; int py = mPy + offsetY; @@ -421,8 +442,7 @@ Being::draw(Graphics *graphics, int offsetX, int offsetY) const } } -void -Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY) +void Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY) { if (!mEmotion) return; @@ -435,17 +455,56 @@ Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY) graphics->drawImage(emotionSet->get(emotionIndex), px, py); } -Being::Type -Being::getType() const +void Being::drawSpeech(Graphics *graphics, int offsetX, int offsetY) +{ + int px = mPx + offsetX; + int py = mPy + offsetY; + + // Draw speech above this being + if (mSpeechTime > 0 && config.getValue("speechbubble", 1)) + { + if (mText) + { + delete mText; + mText = 0; + } + + mSpeechBubble->setCaption(mName); + mSpeechBubble->setWindowName(mName); + // Not quite centered, but close enough. However, it's not too important to get + // it right right now, as it doesn't take bubble collision into account yet. + mSpeechBubble->setText( mSpeech ); + mSpeechBubble->setPosition(px - (mSpeechBubble->getWidth() * 4 / 11), py - 70 - + (mSpeechBubble->getNumRows()*14)); + mSpeechBubble->setVisible(true); + } + else if (mSpeechTime > 0 && !config.getValue("speechbubble", 1)) + { + mSpeechBubble->setVisible(false); + // don't introduce a memory leak + if (mText) + delete mText; + + mText = new Text(mSpeech, mPx + X_SPEECH_OFFSET, mPy - Y_SPEECH_OFFSET, + gcn::Graphics::CENTER, speechFont, + gcn::Color(255, 255, 255)); + } + else if (mSpeechTime == 0) + { + mSpeechBubble->setVisible(false); + } +} + +Being::Type Being::getType() const { return UNKNOWN; } -int -Being::getOffset(char pos, char neg) const +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 != WALK || !(mDirection & (pos | neg))) + { return 0; } @@ -453,220 +512,42 @@ Being::getOffset(char pos, char neg) const // We calculate the offset _from_ the _target_ location offset -= 32; - if (offset > 0) { + if (offset > 0) + { offset = 0; } // Going into negative direction? Invert the offset. - if (mDirection & pos) { + if (mDirection & pos) + { offset = -offset; } return offset; } - -int -Being::getWidth() const +int Being::getWidth() const { if (mSprites[BASE_SPRITE]) { return mSprites[BASE_SPRITE]->getWidth(); } - else { + else + { return 0; } } -int -Being::getHeight() const +int Being::getHeight() const { if (mSprites[BASE_SPRITE]) { return mSprites[BASE_SPRITE]->getHeight(); } - else { - return 0; - } -} - - -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) + else { - XML::Document doc(BEING_EFFECTS_FILE); - xmlNodePtr root = doc.rootNode(); - - if (!root || !xmlStrEqual(root->name, BAD_CAST "being-effects")) - { - logger->log("Error loading being effects file: " - BEING_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]; - - if (!ed) - return default_effect; - else - return ed; -} - -void -Being::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 != "") { - Particle *selfFX; - - selfFX = particleEngine->addEffect(ed->mGFXEffect, 0, 0); - controlParticle(selfFX); - } - - if (sfx && ed->mSFXEffect != "") { - sound.playSfx(ed->mSFXEffect); + return 0; } } - - - -static int hairStylesNr; -static int hairColorsNr; -static std::vector<std::string> hairColors; - -static void -initializeHair(void); - -int -Being::getHairStylesNr(void) -{ - initializeHair(); - return hairStylesNr; -} - -int -Being::getHairColorsNr(void) -{ - initializeHair(); - return hairColorsNr; -} - -std::string -Being::getHairColor(int index) -{ - initializeHair(); - if (index < 0 || index >= hairColorsNr) - return "#000000"; - - return hairColors[index]; -} - -static bool hairInitialized = false; - -static void -initializeHair(void) -{ - if (hairInitialized) - return; - - // Hairstyles are encoded as negative numbers. Count how far negative we can go. - int hairstylesCtr = -1; - while (ItemDB::get(hairstylesCtr).getSprite(0) != "error.xml") - --hairstylesCtr; - - hairStylesNr = -hairstylesCtr; // done. - if (hairStylesNr == 0) - hairStylesNr = 1; // No hair style -> no hair - - hairColorsNr = 0; - - XML::Document doc(HAIR_FILE); - xmlNodePtr root = doc.rootNode(); - - if (!root || !xmlStrEqual(root->name, BAD_CAST "colors")) - { - logger->log("Error loading being hair configuration file"); - } else { - for_each_xml_child_node(node, root) - { - if (xmlStrEqual(node->name, BAD_CAST "color")) - { - int index = atoi(XML::getProperty(node, "id", "-1").c_str()); - std::string value = XML::getProperty(node, "value", ""); - - if (index >= 0 && value != "") { - if (index >= hairColorsNr) { - hairColorsNr = index + 1; - hairColors.resize(hairColorsNr, "#000000"); - } - hairColors[index] = value; - } - } - } - } // done initializing - - if (hairColorsNr == 0) { // No colours -> black only - hairColorsNr = 1; - hairColors.resize(hairColorsNr, "#000000"); - } - - hairInitialized = 1; -} diff --git a/src/being.h b/src/being.h index 15e1e6f9..7202701a 100644 --- a/src/being.h +++ b/src/being.h @@ -24,14 +24,20 @@ #include <list> #include <memory> -#include <string> #include <SDL_types.h> +#include <string> +#include <vector> #include <bitset> -#include "sprite.h" -#include "map.h" #include "animatedsprite.h" +#include "effectmanager.h" +#include "map.h" #include "particlecontainer.h" +#include "sprite.h" + +#include "gui/speechbubble.h" + +#include "resources/colordb.h" #define FIRST_IGNORE_EMOTE 14 #define STATUS_EFFECTS 32 @@ -42,8 +48,8 @@ class ItemInfo; class Item; class Map; class Graphics; -class ImageSet; class Particle; +class SpeechBubble; class Text; /** @@ -113,20 +119,17 @@ class Being : public Sprite /** * Directions, to be used as bitmask values */ - static const char DOWN = 1; - static const char LEFT = 2; - static const char UP = 4; - static const char RIGHT = 8; + enum { DOWN = 1, LEFT = 2, UP = 4, RIGHT = 8 }; Uint16 mJob; /**< Job (player job, npc, monster, ) */ Uint16 mX, mY; /**< Tile coordinates */ - Uint8 mAction; /**< Action the being is performing */ - Uint8 mFrame; + Action mAction; /**< Action the being is performing */ + Uint16 mFrame; Uint16 mWalkTime; Uint8 mEmotion; /**< Currently showing emotion */ Uint8 mEmotionTime; /**< Time until emotion disappears */ - Uint16 mAttackSpeed; /**< Attack speed */ + Uint16 mAttackSpeed; /**< Attack speed */ /** * Constructor. @@ -162,8 +165,12 @@ class Being : public Sprite * * @param amount The amount of damage. */ - virtual void - takeDamage(int amount); + virtual void takeDamage(int amount); + + /** + * Puts a crit notification bubble above this being. + */ + virtual void showCrit(); /** * Handles an attack of another being by this being. @@ -171,22 +178,19 @@ class Being : public Sprite * @param victim The attacked being. * @param damage The amount of damage dealt (0 means miss). */ - virtual void - handleAttack(Being *victim, int damage); + virtual void handleAttack(Being *victim, int damage); /** * Returns the name of the being. */ - const std::string& - getName() const { return mName; } + const std::string& getName() const { return mName; } /** * Sets the name for the being. * * @param name The name that should appear. */ - virtual void - setName(const std::string &name) { mName = name; } + virtual void setName(const std::string &name) { mName = name; } /** * Gets the hair color for this being. @@ -201,46 +205,51 @@ class Being : public Sprite { return mHairStyle; } /** + * Get the number of hairstyles implemented + */ + static int getNumOfHairstyles() { return mNumberOfHairstyles; } + + /** * Sets the hair style and color for this being. */ - virtual void - setHairStyle(int style, int color); + virtual void setHairStyle(int style, int color); /** * Sets visible equipments for this being. */ - virtual void - setSprite(int slot, int id, std::string color = ""); + virtual void setSprite(int slot, int id, std::string color = ""); /** * Sets the gender of this being. */ - virtual void - setGender(int gender) { mGender = gender; } + virtual void setGender(int gender) { mGender = gender; } /** * Gets the gender of this being. */ - int - getGender() const { return mGender; } + int getGender() const { return mGender; } /** * Makes this being take the next step of his path. */ - virtual void - nextStep(); + virtual void nextStep(); + + virtual void setGM() { mIsGM = true; } /** * Performs being logic. */ - virtual void - logic(); + virtual void logic(); + + /** + * Draws the speech text above the being. + */ + void drawSpeech(Graphics *graphics, int offsetX, int offsetY); /** * Draws the emotion picture above the being. */ - void - drawEmotion(Graphics *graphics, int offsetX, int offsetY); + void drawEmotion(Graphics *graphics, int offsetX, int offsetY); /** * Returns the type of the being. @@ -250,26 +259,22 @@ class Being : public Sprite /** * Gets the walk speed. */ - Uint16 - getWalkSpeed() const { return mWalkSpeed; } + Uint16 getWalkSpeed() const { return mWalkSpeed; } /** * Sets the walk speed. */ - void - setWalkSpeed(Uint16 speed) { mWalkSpeed = speed; } + void setWalkSpeed(Uint16 speed) { mWalkSpeed = speed; } /** * Gets the sprite id. */ - Uint32 - getId() const { return mId; } + Uint32 getId() const { return mId; } /** * Sets the sprite id. */ - void - setId(Uint32 id) { mId = id; } + void setId(Uint32 id) { mId = id; } /** * Sets the map the being is on @@ -279,8 +284,7 @@ class Being : public Sprite /** * Sets the current action. */ - virtual void - setAction(Uint8 action); + virtual void setAction(Action action); /** * Returns the current direction. @@ -293,50 +297,53 @@ class Being : public Sprite void setDirection(Uint8 direction); /** + * Gets the current action. + */ + int getWalkTime() { return mWalkTime; } + + /** + * Returns the direction the being is facing. + */ + SpriteDirection getSpriteDirection() const; + + /** * Draws this being to the given graphics context. * * @see Sprite::draw(Graphics, int, int) */ - virtual void - draw(Graphics *graphics, int offsetX, int offsetY) const; + virtual void draw(Graphics *graphics, int offsetX, int offsetY) const; /** * Returns the pixel X coordinate. */ - int - getPixelX() const { return mPx; } + int getPixelX() const { return mPx; } /** * Returns the pixel Y coordinate. * * @see Sprite::getPixelY() */ - int - getPixelY() const { return mPy; } + int getPixelY() const { return mPy; } /** * Get the current X pixel offset. */ - int - getXOffset() const { return getOffset(LEFT, RIGHT); } + int getXOffset() const { return getOffset(LEFT, RIGHT); } /** * Get the current Y pixel offset. */ - int - getYOffset() const { return getOffset(UP, DOWN); } + int getYOffset() const { return getOffset(UP, DOWN); } /** * Returns the horizontal size of the current base sprite of the being */ - virtual int - getWidth() const; + virtual int getWidth() const; /** * Returns the vertical size of the current base sprite of the being */ - virtual int - getHeight() const; + virtual int getHeight() const; /** * Returns the required size of a target cursor for this being. @@ -355,19 +362,11 @@ class Being : public Sprite mEmotionTime = emote_time; } - /** - * 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); } + // Target cursor being used by the being + Image *mTargetCursor; const std::auto_ptr<Equipment> mEquipment; - static int getHairColorsNr(void); static int getHairStylesNr(void); @@ -386,19 +385,13 @@ class Being : public Sprite virtual void updateCoords() {} /** - * Returns the sprite direction of this being. - */ - SpriteDirection getSpriteDirection() const; - - /** * 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); + void internalTriggerEffect(int effectId, bool sfx, bool gfx); Uint32 mId; /**< Unique sprite id */ Uint16 mWalkSpeed; /**< Walking speed */ @@ -406,14 +399,19 @@ class Being : public Sprite Map *mMap; /**< Map on which this being resides */ std::string mName; /**< Name of character */ SpriteIterator mSpriteIterator; + bool mIsGM; + bool mParticleEffects; /**< Whether to display particles or not */ typedef std::bitset<STATUS_EFFECTS> StatusEffects; /** Engine-related infos about weapon. */ const ItemInfo* mEquippedWeapon; + static int mNumberOfHairstyles; /** Number of hair styles in use */ + Path mPath; - Text *mSpeech; + std::string mSpeech; + Text *mText; Uint16 mHairStyle, mHairColor; Uint8 mGender; Uint32 mSpeechTime; @@ -435,6 +433,9 @@ class Being : public Sprite */ int getOffset(char pos, char neg) const; + // Speech Bubble components + SpeechBubble *mSpeechBubble; + static int instances; /**< Number of Being instances */ static ImageSet *emotionSet; /**< Emoticons used by beings */ }; diff --git a/src/beingmanager.cpp b/src/beingmanager.cpp index 51d45213..ada1ddfa 100644 --- a/src/beingmanager.cpp +++ b/src/beingmanager.cpp @@ -19,10 +19,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <algorithm> +#include <cassert> #include "beingmanager.h" - #include "localplayer.h" #include "monster.h" #include "npc.h" @@ -157,8 +156,6 @@ Being* BeingManager::findBeingByName(std::string name, Being::Type type) return NULL; } - - Beings& BeingManager::getAll() { return mBeings; @@ -191,7 +188,7 @@ void BeingManager::clear() mBeings.remove(player_node); } - for_each(mBeings.begin(), mBeings.end(), make_dtor(mBeings)); + delete_all(mBeings); mBeings.clear(); if (player_node) @@ -206,9 +203,12 @@ Being* BeingManager::findNearestLivingBeing(Uint16 x, Uint16 y, int maxdist, Being *closestBeing = NULL; int dist = 0; - for (BeingIterator i = mBeings.begin(); i != mBeings.end(); i++) + BeingIterator itr = mBeings.begin(); + BeingIterator itr_end = mBeings.end(); + + for (; itr != itr_end; ++itr) { - Being *being = (*i); + Being *being = (*itr); int d = abs(being->mX - x) + abs(being->mY - y); if ((being->getType() == type || type == Being::UNKNOWN) diff --git a/src/beingmanager.h b/src/beingmanager.h index 7a840030..0179bed8 100644 --- a/src/beingmanager.h +++ b/src/beingmanager.h @@ -49,7 +49,7 @@ class BeingManager /** * Create a being and add it to the list of beings. */ - Being* createBeing(Uint32 id, Uint16 job); + Being *createBeing(Uint32 id, Uint16 job); /** * Remove a Being. @@ -60,33 +60,36 @@ class BeingManager * Return a specific id Being. */ Being* findBeing(Uint32 id); + Being* findBeingByPixel(Uint16 x, Uint16 y); /** - * Return a being at specific coordinates. + * Returns a being at specific coordinates. */ Being* findBeing(Uint16 x, Uint16 y, Being::Type type = Being::UNKNOWN); - Being* findBeingByPixel(Uint16 x, Uint16 y); - /** - * Return a being nearest to specific coordinates. - * - * \param maxdist maximal distance. If minimal distance is larger, - * no being is returned - */ + /** + * Returns a being nearest to specific coordinates. + * + * @param x X coordinate. + * @param y Y coordinate. + * @param maxdist Maximal distance. If minimal distance is larger, + * no being is returned. + * @param type The type of being to look for. + */ Being* findNearestLivingBeing(Uint16 x, Uint16 y, int maxdist, Being::Type type = Being::UNKNOWN); - /** - * Finds a being by name and (optionally) by type. - */ + /** + * Finds a being by name and (optionally) by type. + */ Being* findBeingByName(std::string name, Being::Type type = Being::UNKNOWN); - /** - * Return a being nearest to another being. - * - * \param maxdist maximal distance. If minimal distance is larger, - * no being is returned - */ + /** + * Returns a being nearest to another being. + * + * \param maxdist maximal distance. If minimal distance is larger, + * no being is returned + */ Being* findNearestLivingBeing(Being *aroundBeing, int maxdist, Being::Type type = Being::UNKNOWN); diff --git a/src/configlistener.h b/src/configlistener.h index b740720f..9fbc4544 100644 --- a/src/configlistener.h +++ b/src/configlistener.h @@ -24,7 +24,6 @@ #include <iosfwd> - /** * The listener interface for receiving notifications about changes to * configuration options. diff --git a/src/configuration.cpp b/src/configuration.cpp index e2deae31..8e80de18 100644 --- a/src/configuration.cpp +++ b/src/configuration.cpp @@ -19,12 +19,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "configuration.h" - #include <libxml/xmlwriter.h> #include "configlistener.h" +#include "configuration.h" #include "log.h" #include "utils/tostring.h" diff --git a/src/configuration.h b/src/configuration.h index 3751d429..197e1a41 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -22,11 +22,11 @@ #ifndef _TMW_CONFIGURATION_H #define _TMW_CONFIGURATION_H -#include <map> -#include <list> -#include <string> #include <cassert> #include <libxml/xmlwriter.h> +#include <list> +#include <map> +#include <string> class ConfigListener; class ConfigurationObject; diff --git a/src/effectmanager.cpp b/src/effectmanager.cpp new file mode 100644 index 00000000..4b835355 --- /dev/null +++ b/src/effectmanager.cpp @@ -0,0 +1,85 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "effectmanager.h" + +#include "particle.h" +#include "log.h" +#include "sound.h" + +#include "utils/xml.h" + + +EffectManager::EffectManager() +{ + XML::Document doc("effects.xml"); + xmlNodePtr root = doc.rootNode(); + + if (!root || !xmlStrEqual(root->name, BAD_CAST "being-effects")) + { + logger->log("Error loading being effects file: effects.xml"); + return; + } + else + { + logger->log("Effects are now loading"); + } + + for_each_xml_child_node(node, root) + { + int id; + + if (xmlStrEqual(node->name, BAD_CAST "effect")) + { + EffectDescription ed; + ed.id = XML::getProperty(node, "id", -1); + ed.GFX = XML::getProperty(node, "particle", ""); + ed.SFX = XML::getProperty(node, "audio", ""); + mEffects.push_back(ed); + } + } +} + +EffectManager::~EffectManager() +{ + +} + +bool EffectManager::trigger(int id, int x, int y) +{ + bool rValue = false; + for (std::list<EffectDescription>::iterator i = mEffects.begin(); i != mEffects.end(); ++i) + { + if ((*i).id == id) + { + printf("Found effect, playing it"); + rValue = true; + if((*i).GFX != "") + particleEngine->addEffect((*i).GFX, x, y); + if((*i).SFX != "") + sound.playSfx((*i).SFX); + break; + } + } + return rValue; +} + diff --git a/src/gui/buddywindow.h b/src/effectmanager.h index 6b07f470..b5451f27 100644 --- a/src/gui/buddywindow.h +++ b/src/effectmanager.h @@ -17,40 +17,41 @@ * You should have received a copy of the GNU General Public License * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * */ -#ifndef _TMW_BUDDYWINDOW_H -#define _TMW_BUDDYWINDOW_H +#ifndef _EFFECT_MANAGER_H +#define _EFFECT_MANAGER_H -#include <guichan/actionlistener.hpp> +#include <string> +#include <list> -#include "window.h" +class EffectManager +{ -#include "../guichanfwd.h" + public: + struct EffectDescription { + int id; + std::string GFX; + std::string SFX; + }; -class BuddyList; + + EffectManager(); + + ~EffectManager(); -/** - * Window showing buddy list. - * - * \ingroup Interface - */ -class BuddyWindow : public Window, public gcn::ActionListener -{ - public: /** - * Constructor. + * Triggers a effect with the id, at x,y + * returns true if ID exists */ - BuddyWindow(); + bool trigger(int id, int x = 0, int y = 0); - /** - * Performs action. - */ - void action(const gcn::ActionEvent &event); + private: + std::list<EffectDescription> mEffects; - private: - BuddyList *mBuddyList; - gcn::ListBox *mListbox; }; -#endif /* _TMW_BUDDYWINDOW_H */ +extern EffectManager *effectManager; + +#endif // _EFFECT_MANAGER_H diff --git a/src/engine.cpp b/src/engine.cpp index 2edc6550..74e11336 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -19,13 +19,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "engine.h" - #include <list> #include "being.h" #include "beingmanager.h" #include "configuration.h" +#include "engine.h" #include "flooritemmanager.h" #include "game.h" #include "graphics.h" @@ -93,6 +92,27 @@ void Engine::changeMap(const std::string &mapPath) if (newMap->hasProperty("minimap")) { mapImage = resman->getImage(newMap->getProperty("minimap")); + + // Set the title for the Minimap + if (newMap->hasProperty("mapname")) + { + minimap->setCaption(newMap->getProperty("mapname")); + } + else + { + minimap->setCaption("Unknown"); + logger->log("WARNING: Map file '%s' defines a minimap image but does not define a 'mapname' property", map_path.c_str()); + } + // How many pixels equal one tile. .5 (which is the TMW default) is 2 tiles to a pixel, + // while 1 is 1 tile to 1 pixel + if (newMap->hasProperty("minimapproportion")) + { + minimap->setProportion(atof(newMap->getProperty("minimapproportion").c_str())); + } + else + { + minimap->setProportion(0.5); + } } if (newMap->hasProperty("name")) { diff --git a/src/equipment.cpp b/src/equipment.cpp index 0bdd1c70..f1d1d4f2 100644 --- a/src/equipment.cpp +++ b/src/equipment.cpp @@ -19,13 +19,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <algorithm> + #include "equipment.h" #include "item.h" #include "inventory.h" #include "localplayer.h" -#include <algorithm> - Equipment::Equipment(): mArrows(0) { @@ -40,3 +40,4 @@ Equipment::setEquipment(int index, int inventoryIndex) if (item) item->setEquipped(true); } + diff --git a/src/extensions.h b/src/extensions.h new file mode 100644 index 00000000..5b95afc8 --- /dev/null +++ b/src/extensions.h @@ -0,0 +1,34 @@ +/* + * Aethyra + * Copyright 2008 Lloyd Bryant <sanga@aethyra.com> + * + * This file is part of the Aethyra project. + * + * Aethyra 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. + * + * Aethyra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _EXTENSIONS_ +#define _EXTENSIONS_ + +struct EXTENSIONS +{ + bool aethyra_inventory; + bool aethyra_spells; + bool aethyra_misc; +}; + +extern struct EXTENSIONS extensions; +#endif diff --git a/src/floor_item.cpp b/src/floor_item.cpp index 7ad3c0c0..0b114e14 100644 --- a/src/floor_item.cpp +++ b/src/floor_item.cpp @@ -20,7 +20,6 @@ */ #include "floor_item.h" - #include "map.h" FloorItem::FloorItem(unsigned int id, diff --git a/src/flooritemmanager.cpp b/src/flooritemmanager.cpp index 006d62c1..7445b1e9 100644 --- a/src/flooritemmanager.cpp +++ b/src/flooritemmanager.cpp @@ -19,11 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <algorithm> - -#include "flooritemmanager.h" - #include "floor_item.h" +#include "flooritemmanager.h" #include "utils/dtor.h" @@ -48,7 +45,7 @@ void FloorItemManager::destroy(FloorItem *item) void FloorItemManager::clear() { - for_each(mFloorItems.begin(), mFloorItems.end(), make_dtor(mFloorItems)); + delete_all(mFloorItems); mFloorItems.clear(); } diff --git a/src/game.cpp b/src/game.cpp index 9fa0129e..93bec013 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -26,11 +26,12 @@ #include <sstream> #include <string> -#include <guichan/sdl/sdlinput.hpp> #include <guichan/exception.hpp> +#include <guichan/sdl/sdlinput.hpp> #include "beingmanager.h" #include "configuration.h" +#include "effectmanager.h" #include "engine.h" #include "flooritemmanager.h" #include "graphics.h" @@ -74,9 +75,11 @@ #include "net/equipmenthandler.h" #include "net/inventoryhandler.h" #include "net/itemhandler.h" +#include "net/messageout.h" #include "net/network.h" #include "net/npchandler.h" #include "net/playerhandler.h" +#include "net/protocol.h" #include "net/skillhandler.h" #include "net/tradehandler.h" #include "net/messageout.h" @@ -93,6 +96,7 @@ std::string map_path; bool done = false; volatile int tick_time; volatile int fps = 0, frame = 0; + Engine *engine = NULL; Joystick *joystick = NULL; @@ -112,7 +116,6 @@ InventoryWindow *inventoryWindow; NpcListDialog *npcListDialog; NpcTextDialog *npcTextDialog; SkillDialog *skillDialog; -//NewSkillDialog *newSkillWindow; Setup* setupWindow; Minimap *minimap; EquipmentWindow *equipmentWindow; @@ -126,11 +129,12 @@ ItemShortcutWindow *itemShortcutWindow; BeingManager *beingManager = NULL; FloorItemManager *floorItemManager = NULL; Particle* particleEngine = NULL; +EffectManager *effectManager = NULL; const int MAX_TIME = 10000; /** - * Listener used for exitting handling. + * Listener used for exiting handling. */ namespace { struct ExitListener : public gcn::ActionListener @@ -163,15 +167,18 @@ Uint32 nextSecond(Uint32 interval, void *param) { fps = frame; frame = 0; + return interval; } int get_elapsed_time(int start_time) { - if (start_time <= tick_time) { + if (start_time <= tick_time) + { return (tick_time - start_time) * 10; } - else { + else + { return (tick_time + (MAX_TIME - start_time)) * 10; } } @@ -193,7 +200,6 @@ void createGuiWindows(Network *network) npcTextDialog = new NpcTextDialog(); npcListDialog = new NpcListDialog(); skillDialog = new SkillDialog(); - //newSkillWindow = new NewSkillDialog(); setupWindow = new Setup(); minimap = new Minimap(); equipmentWindow = new EquipmentWindow(player_node->mEquipment.get()); @@ -215,8 +221,7 @@ void createGuiWindows(Network *network) chatWindow->setVisible((bool) config.getValue( chatWindow->getWindowName() + "Visible", true)); miniStatusWindow->setVisible((bool) config.getValue( - miniStatusWindow->getWindowName() + "Visible", - true)); + miniStatusWindow->getWindowName() + "Visible", true)); buyDialog->setVisible(false); sellDialog->setVisible(false); tradeWindow->setVisible(false); @@ -251,7 +256,6 @@ void destroyGuiWindows() delete minimap; delete equipmentWindow; //delete chargeDialog; - //delete newSkillWindow; delete tradeWindow; //delete buddyWindow; delete helpWindow; @@ -277,6 +281,8 @@ Game::Game(Network *network): beingManager = new BeingManager(network); floorItemManager = new FloorItemManager(); + effectManager = new EffectManager(); + particleEngine = new Particle(NULL); particleEngine->setupEngine(); @@ -311,17 +317,13 @@ Game::Game(Network *network): network->registerHandler(mTradeHandler.get()); /* - * THIS IS A TEMPORARY WORKAROUND! - * - * To prevent the server from sending data before the client has - * initialized, it's been modified to wait for a "ping" from the client to - * complete its initialization. + * To prevent the server from sending data before the client + * has initialized, I've modified it to wait for a "ping" + * from the client to complete its initialization * - * The real fix is to make sure we are not throwing away messages in the - * network buffer due to not having registered the handlers above straight - * after receiving a login success from the map server. - * - * The response from eAthena on this packet is ignored by the client. + * Note: This only affects the latest eAthena version. This + * packet is handled by the older version, but its response + * is ignored by the client */ MessageOut msg(mNetwork); msg.writeInt16(CMSG_CLIENT_PING); @@ -356,17 +358,17 @@ bool saveScreenshot(SDL_Surface *screenshot) bool found = false; do { - screenshotCount++; - filename.str(""); + screenshotCount++; + filename.str(""); #if (defined __USE_UNIX98 || defined __FreeBSD__) - filename << PHYSFS_getUserDir() << ".tmw/"; + filename << PHYSFS_getUserDir() << ".aethyra/"; #elif defined __APPLE__ - filename << PHYSFS_getUserDir() << "Desktop/"; + filename << PHYSFS_getUserDir() << "Desktop/"; #endif - filename << "TMW_Screenshot_" << screenshotCount << ".png"; - testExists.open(filename.str().c_str(), std::ios::in); - found = !testExists.is_open(); - testExists.close(); + filename << "Ae_Screenshot_" << screenshotCount << ".png"; + testExists.open(filename.str().c_str(), std::ios::in); + found = !testExists.is_open(); + testExists.close(); } while (!found); if (ImageWriter::writePNG(screenshot, filename.str())) @@ -403,61 +405,61 @@ void Game::logic() while (!done) { - // Handle all necessary game logic - while (get_elapsed_time(gameTime) > 0) - { - handleInput(); - engine->logic(); - gameTime++; - } + // Handle all necessary game logic + while (get_elapsed_time(gameTime) > 0) + { + handleInput(); + engine->logic(); + gameTime++; + } - // This is done because at some point tick_time will wrap. - gameTime = tick_time; + // This is done because at some point tick_time will wrap. + gameTime = tick_time; - // Update the screen when application is active, delay otherwise. - if (SDL_GetAppState() & SDL_APPACTIVE) + // Update the screen when application is active, delay otherwise. + if (SDL_GetAppState() & SDL_APPACTIVE) + { + // Draw a frame if either frames are not limited or enough time has + // passed since the last frame. + if (!mMinFrameTime || + get_elapsed_time(mDrawTime / 10) > mMinFrameTime) { - // Draw a frame if either frames are not limited or enough time has - // passed since the last frame. - if (!mMinFrameTime || - get_elapsed_time(mDrawTime / 10) > mMinFrameTime) - { - frame++; - gui->draw(); - graphics->updateScreen(); - mDrawTime += mMinFrameTime; - - // Make sure to wrap mDrawTime, since tick_time will wrap. - if (mDrawTime > MAX_TIME * 10) - mDrawTime -= MAX_TIME * 10; - } - else - { - SDL_Delay(10); - } + frame++; + gui->draw(); + graphics->updateScreen(); + mDrawTime += mMinFrameTime; + + // Make sure to wrap mDrawTime, since tick_time will wrap. + if (mDrawTime > MAX_TIME * 10) + mDrawTime -= MAX_TIME * 10; } else { SDL_Delay(10); - mDrawTime = tick_time * 10; } + } + else + { + SDL_Delay(10); + mDrawTime = tick_time * 10; + } - // Handle network stuff - mNetwork->flush(); - mNetwork->dispatchMessages(); + // Handle network stuff + mNetwork->flush(); + mNetwork->dispatchMessages(); - if (!mNetwork->isConnected()) + if (!mNetwork->isConnected()) + { + if (!disconnectedDialog) { - if (!disconnectedDialog) - { - disconnectedDialog = new - OkDialog("Network Error", - "The connection to the server was lost, the program will now quit"); - disconnectedDialog->addActionListener(&exitListener); - disconnectedDialog->requestMoveToTop(); - } + disconnectedDialog = new + OkDialog("Network Error", + "The connection to the server was lost, the program will now quit"); + disconnectedDialog->addActionListener(&exitListener); + disconnectedDialog->requestMoveToTop(); } } + } } void Game::handleInput() @@ -478,7 +480,7 @@ void Game::handleInput() { gcn::Window *requestedWindow = NULL; - if (setupWindow->isVisible() && + if (setupWindow->isVisible() && keyboard.getNewKeyIndex() > keyboard.KEY_NO_VALUE) { keyboard.setNewKey((int) event.key.keysym.sym); @@ -534,194 +536,202 @@ void Game::handleInput() } used = true; break; - } } + } - // Smilie - if (keyboard.isKeyActive(keyboard.KEY_SMILIE)) + // Smilie + if (keyboard.isKeyActive(keyboard.KEY_SMILIE)) + { + // Emotions + Uint8 emotion; + switch (event.key.keysym.sym) { - // Emotions - Uint8 emotion; - switch (event.key.keysym.sym) + case SDLK_1: emotion = 1; break; + case SDLK_2: emotion = 2; break; + case SDLK_3: emotion = 3; break; + case SDLK_4: emotion = 4; break; + case SDLK_5: emotion = 5; break; + case SDLK_6: emotion = 6; break; + case SDLK_7: emotion = 7; break; + case SDLK_8: emotion = 8; break; + case SDLK_9: emotion = 9; break; + case SDLK_0: emotion = 10; break; + case SDLK_MINUS: emotion = 11; break; + case SDLK_EQUALS: emotion = 12; break; + default: emotion = 0; break; + } + + if (emotion) + { + player_node->emote(emotion); + used = true; + } + } + switch (event.key.keysym.sym) + { + case SDLK_PAGEUP: + if (chatWindow->isVisible()) { - case SDLK_1: emotion = 1; break; - case SDLK_2: emotion = 2; break; - case SDLK_3: emotion = 3; break; - case SDLK_4: emotion = 4; break; - case SDLK_5: emotion = 5; break; - case SDLK_6: emotion = 6; break; - case SDLK_7: emotion = 7; break; - case SDLK_8: emotion = 8; break; - case SDLK_9: emotion = 9; break; - case SDLK_0: emotion = 10; break; - case SDLK_MINUS: emotion = 11; break; - case SDLK_EQUALS: emotion = 12; break; - default: emotion = 0; break; + chatWindow->scroll(-DEFAULT_CHAT_WINDOW_SCROLL); + used = true; } + break; - if (emotion) + case SDLK_PAGEDOWN: + if (chatWindow->isVisible()) { - player_node->emote(emotion); + chatWindow->scroll(DEFAULT_CHAT_WINDOW_SCROLL); used = true; return; } - } - switch (event.key.keysym.sym) - { - case SDLK_PAGEUP: - if (chatWindow->isVisible()) - { - chatWindow->scroll(-DEFAULT_CHAT_WINDOW_SCROLL); - used = true; - } - break; + break; - case SDLK_PAGEDOWN: - if (chatWindow->isVisible()) - { - chatWindow->scroll(DEFAULT_CHAT_WINDOW_SCROLL); - used = true; - } + case SDLK_F1: + // In-game Help + if (helpWindow->isVisible()) + { + helpWindow->setVisible(false); + } + else + { + helpWindow->loadHelp("index"); + helpWindow->requestMoveToTop(); + } + used = true; + break; + + case SDLK_F2: requestedWindow = statusWindow; break; + case SDLK_F3: requestedWindow = inventoryWindow; break; + case SDLK_F4: requestedWindow = equipmentWindow; break; + case SDLK_F5: requestedWindow = skillDialog; break; + case SDLK_F6: requestedWindow = minimap; break; + case SDLK_F7: requestedWindow = chatWindow; break; + case SDLK_F8: requestedWindow = itemShortcutWindow; break; + case SDLK_F9: requestedWindow = setupWindow; break; + case SDLK_F10: requestedWindow = debugWindow; break; + + case SDLK_RETURN: + // Input chat window + if (chatWindow->isInputFocused() || + deathNotice != NULL || + weightNotice != NULL) + { break; + } - case SDLK_F1: - // In-game Help - if (helpWindow->isVisible()) - { - helpWindow->setVisible(false); - } - else - { - helpWindow->loadHelp("index"); - helpWindow->requestMoveToTop(); - } + // Quit by pressing Enter if the exit confirm is there + if (exitConfirm) + { + done = true; + } + // Close the Browser if opened + else if (helpWindow->isVisible()) + { + helpWindow->setVisible(false); + } + // Close the config window, cancelling changes if opened + else if (setupWindow->isVisible()) + { + setupWindow->action(gcn::ActionEvent(NULL, "cancel")); + } + // Proceed to the next dialog option, or close the window + else if (npcTextDialog->isVisible()) + { + npcTextDialog->action(gcn::ActionEvent(NULL, "ok")); + } + // Choose the currently highlighted dialogue option + else if (npcListDialog->isVisible()) + { + npcListDialog->action(gcn::ActionEvent(NULL, "ok")); + } + // Else, open the chat edit box + else + { + chatWindow->requestChatFocus(); used = true; - break; - - case SDLK_F2: requestedWindow = statusWindow; break; - case SDLK_F3: requestedWindow = inventoryWindow; break; - case SDLK_F4: requestedWindow = equipmentWindow; break; - case SDLK_F5: requestedWindow = skillDialog; break; - case SDLK_F6: requestedWindow = minimap; break; - case SDLK_F7: requestedWindow = chatWindow; break; - case SDLK_F8: requestedWindow = itemShortcutWindow; break; - case SDLK_F9: requestedWindow = setupWindow; break; - case SDLK_F10: requestedWindow = debugWindow; break; - //case SDLK_F11: requestedWindow = newSkillWindow; break; - - case SDLK_RETURN: - // Input chat window - if (chatWindow->isInputFocused() || - deathNotice != NULL || - weightNotice != NULL) - { - break; - } + } + break; + // Quitting confirmation dialog + case SDLK_ESCAPE: + if (!exitConfirm) { + exitConfirm = new ConfirmDialog( + "Quit", "Are you sure you want to quit?"); + exitConfirm->addActionListener(&exitListener); + exitConfirm->requestMoveToTop(); + } + else + { + exitConfirm->action(gcn::ActionEvent(NULL, "no")); + } + break; - // Quit by pressing Enter if the exit confirm is there - if (exitConfirm) - { - done = true; - } - // Close the Browser if opened - else if (helpWindow->isVisible()) - { - helpWindow->setVisible(false); - } - // Close the config window, cancelling changes if opened - else if (setupWindow->isVisible()) - { - setupWindow->action(gcn::ActionEvent(NULL, "cancel")); - } - // Else, open the chat edit box - else - { - chatWindow->requestChatFocus(); - used = true; - } - break; - // Quitting confirmation dialog - case SDLK_ESCAPE: - if (!exitConfirm) { - exitConfirm = new ConfirmDialog( - "Quit", "Are you sure you want to quit?"); - exitConfirm->addActionListener(&exitListener); - exitConfirm->requestMoveToTop(); - } - else - { - exitConfirm->action(gcn::ActionEvent(NULL, "no")); - } - break; + default: + break; + } + if (keyboard.isEnabled() && !chatWindow->isInputFocused()) + { + const int tKey = keyboard.getKeyIndex(event.key.keysym.sym); - default: - break; - } - if (keyboard.isEnabled() && !chatWindow->isInputFocused()) + // Do not activate shortcuts if tradewindow is visible + if (!tradeWindow->isVisible()) { - const int tKey = keyboard.getKeyIndex(event.key.keysym.sym); - // Do not activate shortcuts if tradewindow is visible - if (!tradeWindow->isVisible()) + // Checks if any item shortcut is pressed. + for (int i = KeyboardConfig::KEY_SHORTCUT_0; + i <= KeyboardConfig::KEY_SHORTCUT_9; + i++) { - // Checks if any item shortcut is pressed. - for (int i = KeyboardConfig::KEY_SHORTCUT_0; - i <= KeyboardConfig::KEY_SHORTCUT_9; - i++) - { - if (tKey == i && !used) { - itemShortcut->useItem( - i - KeyboardConfig::KEY_SHORTCUT_0); - break; - } + if (tKey == i && !used) { + itemShortcut->useItem(i - KeyboardConfig::KEY_SHORTCUT_0); + break; } } - switch (tKey) { - case KeyboardConfig::KEY_PICKUP: + } + switch (tKey) { + case KeyboardConfig::KEY_PICKUP: + { + FloorItem *item = floorItemManager->findByCoordinates( + player_node->mX, player_node->mY); + + // If none below the player, try the tile in front + // of the player + if (!item) { + Uint16 x = player_node->mX; + Uint16 y = player_node->mY; + switch (player_node->getSpriteDirection()) { - FloorItem *item = floorItemManager->findByCoordinates( - player_node->mX, player_node->mY); - - // If none below the player, try the tile in front of - // the player - if (!item) { - Uint16 x = player_node->mX; - Uint16 y = player_node->mY; - if (player_node->getDirection() & Being::UP) - y--; - if (player_node->getDirection() & Being::DOWN) - y++; - if (player_node->getDirection() & Being::LEFT) - x--; - if (player_node->getDirection() & Being::RIGHT) - x++; - - item = floorItemManager->findByCoordinates(x, y); - } + case DIRECTION_UP : --y; break; + case DIRECTION_DOWN : ++y; break; + case DIRECTION_LEFT : --x; break; + case DIRECTION_RIGHT: ++x; break; + default: break; + } + item = floorItemManager->findByCoordinates(x, y); + } - if (item) - player_node->pickUp(item); + if (item) + player_node->pickUp(item); - used = true; - } - break; - case KeyboardConfig::KEY_SIT: - // Player sit action - player_node->toggleSit(); - used = true; - break; - case KeyboardConfig::KEY_HIDE_WINDOWS: - // Hide certain windows - if (!chatWindow->isInputFocused()) - { - statusWindow->setVisible(false); - inventoryWindow->setVisible(false); - skillDialog->setVisible(false); - setupWindow->setVisible(false); - equipmentWindow->setVisible(false); - helpWindow->setVisible(false); - debugWindow->setVisible(false); - } - break; + used = true; + } + break; + case KeyboardConfig::KEY_SIT: + // Player sit action + player_node->toggleSit(); + used = true; + break; + case KeyboardConfig::KEY_HIDE_WINDOWS: + // Hide certain windows + if (!chatWindow->isInputFocused()) + { + statusWindow->setVisible(false); + inventoryWindow->setVisible(false); + skillDialog->setVisible(false); + setupWindow->setVisible(false); + equipmentWindow->setVisible(false); + helpWindow->setVisible(false); + debugWindow->setVisible(false); + } + break; } } @@ -756,7 +766,6 @@ void Game::handleInput() logger->log("Warning: guichan input exception: %s", err); } } - } // End while // If the user is configuring the keys then don't respond. if (!keyboard.isEnabled()) @@ -782,7 +791,7 @@ void Game::handleInput() direction |= Being::UP; } else if (keyboard.isKeyActive(keyboard.KEY_MOVE_DOWN) || - (joystick && joystick->isDown())) + (joystick && joystick->isDown())) { direction |= Being::DOWN; } @@ -802,60 +811,88 @@ void Game::handleInput() // Attacking monsters if (keyboard.isKeyActive(keyboard.KEY_ATTACK) || - (joystick && joystick->buttonPressed(0))) + (joystick && joystick->buttonPressed(0))) { - Being *target = NULL; - bool newTarget = keyboard.isKeyActive(keyboard.KEY_TARGET); + Being *target = beingManager->findNearestLivingBeing(x, y, 20, Being::MONSTER); + + bool newTarget = !keyboard.isKeyActive(keyboard.KEY_TARGET); // A set target has highest priority if (newTarget || !player_node->getTarget()) { Uint16 targetX = x, targetY = y; - if (player_node->getDirection() & Being::UP) - targetY--; - if (player_node->getDirection() & Being::DOWN) - targetY++; - if (player_node->getDirection() & Being::LEFT) - targetX--; - if (player_node->getDirection() & Being::RIGHT) - targetX++; + switch (player_node->getSpriteDirection()) + { + case DIRECTION_UP : --targetY; break; + case DIRECTION_DOWN : ++targetY; break; + case DIRECTION_LEFT : --targetX; break; + case DIRECTION_RIGHT: ++targetX; break; + default: break; + } // Attack priorioty is: Monster, Player, auto target - target = beingManager->findBeing( - targetX, targetY, Being::MONSTER); + target = beingManager->findBeing(targetX, targetY, Being::MONSTER); if (!target) - target = beingManager->findBeing( - targetX, targetY, Being::PLAYER); + target = beingManager->findBeing(targetX, targetY, Being::PLAYER); } player_node->attack(target, newTarget); } - // Target the nearest player - if (keyboard.isKeyActive(keyboard.KEY_TARGET_PLAYER)) + // Target the nearest player if 'q' is pressed + if ( keyboard.isKeyActive(keyboard.KEY_TARGET_PLAYER) && + !keyboard.isKeyActive(keyboard.KEY_TARGET) ) { - Being *target = beingManager->findNearestLivingBeing( - player_node, 20, Being::PLAYER); + Being *target = beingManager->findNearestLivingBeing(player_node, 20, Being::PLAYER); - if (target) - { - player_node->setTarget(target); - } + player_node->setTarget(target); + } + + // Target the nearest monster if 'a' pressed + if ((keyboard.isKeyActive(keyboard.KEY_TARGET_CLOSEST) || + (joystick && joystick->buttonPressed(3))) && + !keyboard.isKeyActive(keyboard.KEY_TARGET)) + { + Being *target = beingManager->findNearestLivingBeing(x, y, 20, Being::MONSTER); + + player_node->setTarget(target); } - // Target the nearest monster - if (keyboard.isKeyActive(keyboard.KEY_TARGET_CLOSEST) - || (joystick && joystick->buttonPressed(3))) + // Target the nearest npc if 'n' pressed + if ( keyboard.isKeyActive(keyboard.KEY_TARGET_NPC) && + !keyboard.isKeyActive(keyboard.KEY_TARGET) ) { - Being *target = - beingManager->findNearestLivingBeing(x, y, 20, Being::MONSTER); + Being *target = beingManager->findNearestLivingBeing(x, y, 20, Being::NPC); - if (target) + player_node->setTarget(target); + } + + // Talk to the nearest NPC if 't' pressed + if ( keyboard.isKeyActive(keyboard.KEY_TALK) ) + { + if (!npcTextDialog->isVisible() && !npcListDialog->isVisible()) { - player_node->setTarget(target); + Being *target = player_node->getTarget(); + + if (!target) + { + target = beingManager->findNearestLivingBeing(x, y, 20, Being::NPC); + } + + if (target) + { + if (target->getType() == Being::NPC) + dynamic_cast<NPC*>(target)->talk(); + } } } + // Stop attacking if shift is pressed + if (keyboard.isKeyActive(keyboard.KEY_TARGET)) + { + player_node->stopAttack(); + } + if (joystick) { if (joystick->buttonPressed(1)) @@ -40,35 +40,35 @@ extern volatile int tick_time; class Game : public ConfigListener { public: - Game(Network *network); - ~Game(); + Game(Network *network); + ~Game(); - void logic(); + void logic(); - void handleInput(); + void handleInput(); - void optionChanged(const std::string &name); + void optionChanged(const std::string &name); private: - Network *mNetwork; - - /** Used to determine whether to draw the next frame. */ - int mDrawTime; - - /** The minimum frame time (used for frame limiting). */ - int mMinFrameTime; - - typedef const std::auto_ptr<MessageHandler> MessageHandlerPtr; - MessageHandlerPtr mBeingHandler; - MessageHandlerPtr mBuySellHandler; - MessageHandlerPtr mChatHandler; - MessageHandlerPtr mEquipmentHandler; - MessageHandlerPtr mInventoryHandler; - MessageHandlerPtr mItemHandler; - MessageHandlerPtr mNpcHandler; - MessageHandlerPtr mPlayerHandler; - MessageHandlerPtr mSkillHandler; - MessageHandlerPtr mTradeHandler; + Network *mNetwork; + + /** Used to determine whether to draw the next frame. */ + int mDrawTime; + + /** The minimum frame time (used for frame limiting). */ + int mMinFrameTime; + + typedef const std::auto_ptr<MessageHandler> MessageHandlerPtr; + MessageHandlerPtr mBeingHandler; + MessageHandlerPtr mBuySellHandler; + MessageHandlerPtr mChatHandler; + MessageHandlerPtr mEquipmentHandler; + MessageHandlerPtr mInventoryHandler; + MessageHandlerPtr mItemHandler; + MessageHandlerPtr mNpcHandler; + MessageHandlerPtr mPlayerHandler; + MessageHandlerPtr mSkillHandler; + MessageHandlerPtr mTradeHandler; }; /** diff --git a/src/gui/browserbox.cpp b/src/gui/browserbox.cpp index 1c549949..430a2aa2 100644 --- a/src/gui/browserbox.cpp +++ b/src/gui/browserbox.cpp @@ -26,6 +26,7 @@ #include <guichan/mouseinput.hpp> #include "browserbox.h" +#include "colour.h" #include "linkhandler.h" #ifdef USE_OPENGL @@ -133,12 +134,12 @@ void BrowserBox::addRow(const std::string &row) mLinks.push_back(bLink); - newRow += "##L" + bLink.caption; + newRow += "##<" + bLink.caption; tmp.erase(0, idx3 + 2); if(tmp != "") { - newRow += "##P"; + newRow += "##>"; } idx1 = tmp.find("@@"); } @@ -288,7 +289,8 @@ BrowserBox::draw(gcn::Graphics *graphics) if ((mHighMode & UNDERLINE)) { - graphics->setColor(gcn::Color(LINK)); + bool valid; + graphics->setColor(gcn::Color(textColour->getColour('<', valid))); graphics->drawLine( mLinks[mSelectedLink].x1, mLinks[mSelectedLink].y2, @@ -315,58 +317,26 @@ BrowserBox::draw(gcn::Graphics *graphics) if ( (mUseLinksAndUserColors && (j + 3) <= row.size()) || (!mUseLinksAndUserColors && (j == 0)) ) { - // Check for color change in format "##x", x = [L,P,0..9] + // Check for color change in format "##x" if ((row.at(j) == '#') && (row.at(j + 1) == '#')) { - switch (row.at(j + 2)) + char c = row.at(j + 2); + if (c == '>') { - case 'L': // Link color - prevColor = selColor; - selColor = LINK; - break; - case 'P': // Previous color - selColor = prevColor; - break; - case '1': - prevColor = selColor; - selColor = RED; - break; - case '2': - prevColor = selColor; - selColor = GREEN; - break; - case '3': - prevColor = selColor; - selColor = BLUE; - break; - case '4': - prevColor = selColor; - selColor = ORANGE; - break; - case '5': - prevColor = selColor; - selColor = YELLOW; - break; - case '6': - prevColor = selColor; - selColor = PINK; - break; - case '7': - prevColor = selColor; - selColor = PURPLE; - break; - case '8': - prevColor = selColor; - selColor = GRAY; - break; - case '9': - prevColor = selColor; - selColor = BROWN; - break; - case '0': - default: + selColor = prevColor; + } + else + { + bool valid; + int rgb = textColour->getColour(c, valid); + if (c == '<') + { prevColor = selColor; - selColor = BLACK; + } + if (valid) + { + selColor = rgb; + } } j += 3; diff --git a/src/gui/browserbox.h b/src/gui/browserbox.h index 39ecbda8..267e0fea 100644 --- a/src/gui/browserbox.h +++ b/src/gui/browserbox.h @@ -25,8 +25,8 @@ #include <iosfwd> #include <vector> -#include <guichan/widget.hpp> #include <guichan/mouselistener.hpp> +#include <guichan/widget.hpp> #include "../guichanfwd.h" #include "../main.h" @@ -111,30 +111,13 @@ class BrowserBox : public gcn::Widget, public gcn::MouseListener }; /** - * BrowserBox colors. - * - * NOTES (by Javila): - * - color values is "0x" prefix followed by HTML color style. - * - we can add up to 10 different colors: [0..9]. - * - we need a link and a highlighted link colors. - * - not all colors will be fine with all backgrounds due transparent - * windows and widgets. So, I think it's better keep BrowserBox - * opaque (white background) by default. + * Some colours used in the browser box */ + enum { - BLACK = 0x000000, /**< Color 0 */ - RED = 0xff0000, /**< Color 1 */ - GREEN = 0x1fa052, /**< Color 2 */ - BLUE = 0x0000ff, /**< Color 3 */ - ORANGE = 0xe0980e, /**< Color 4 */ - YELLOW = 0xf1dc27, /**< Color 5 */ - PINK = 0xff00d8, /**< Color 6 */ - PURPLE = 0x8415e2, /**< Color 7 */ - GRAY = 0x919191, /**< Color 8 */ - BROWN = 0x8e4c17, /**< Color 9 */ - BGCOLOR = 0xffffff, /**< Bg color for opacity */ - LINK = 0xe50d0d, /**< Color L */ - HIGHLIGHT = 0xcacaca /**< Bg color for highlighted link */ + BLACK = 0x000000, + BGCOLOR = 0xffffff, + HIGHLIGHT = 0xcacaca }; /** diff --git a/src/gui/button.h b/src/gui/button.h index f451416c..eecd0dc0 100644 --- a/src/gui/button.h +++ b/src/gui/button.h @@ -26,6 +26,8 @@ #include <guichan/widgets/button.hpp> +#include "../guichanfwd.h" + class ImageRect; /** diff --git a/src/gui/buttonbox.cpp b/src/gui/buttonbox.cpp new file mode 100644 index 00000000..903d971d --- /dev/null +++ b/src/gui/buttonbox.cpp @@ -0,0 +1,44 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "button.h" +#include "buttonbox.h" + +ButtonBox::ButtonBox(const std::string &title, const std::string &buttonTxt, + ButtonBoxListener *listener) : + Window(title), + mListener(listener) +{ + Button *button = new Button(buttonTxt, "activate", this); + setContentSize(button->getWidth() + 10, + button->getHeight() + 10); + button->setPosition(5, 5); + add(button); +} + +void +ButtonBox::action(const gcn::ActionEvent &event) +{ + if (event.getId() == "activate") + { + mListener->buttonBoxRespond(); + } +} diff --git a/src/gui/newskill.h b/src/gui/buttonbox.h index 49476e5e..edde4aa4 100644 --- a/src/gui/newskill.h +++ b/src/gui/buttonbox.h @@ -19,8 +19,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _TMW_NSKILL_H -#define _TMW_NSKILL_H +#ifndef _TMW_BUTTONBOX_H +#define _TMW_BUTTONBOX_H + +#include <string> #include <guichan/actionlistener.hpp> @@ -28,42 +30,41 @@ #include "../guichanfwd.h" -class ProgressBar; - -#define N_SKILL 100 // skill count constant -#define N_SKILL_CAT 9 // skill category count -#define N_SKILL_CAT_SIZE 10 // skill category maximum size +class ButtonBoxListener +{ + public: -struct nSkill { - short level; - short exp; + /* + * function that ButtonBox calls when the button has been pressed + */ + virtual void buttonBoxRespond() = 0; }; -/** - * Dialog showing the skills in the planned skill model. - * - * \ingroup Interface - */ -class NewSkillDialog : public Window, public gcn::ActionListener +class ButtonBox : public Window, public gcn::ActionListener { public: - /** - * Constructor. - */ - NewSkillDialog(); - // action listener - void action(const gcn::ActionEvent &event); + /* + * Constructor + * + * @param title is the text that appears at the top of the box + * @param buttonTxt is the text that appears on the button + * @param listener points to the class that should respond to the + * button press + */ + ButtonBox(const std::string &title, const std::string &buttonTxt, + ButtonBoxListener *listener); + + /* + * called when the button is pressed + * + * @param event is the event that is generated + */ + void + action(const gcn::ActionEvent &event); private: - void resetNSD(); // updates the values in the dialog box - // members - int startPoint; // starting point of skill listing - ProgressBar *mSkillbar[N_SKILL_CAT_SIZE]; - gcn::Label *mSkillLabel[N_SKILL_CAT_SIZE]; - gcn::Label *mSkillLevel[N_SKILL_CAT_SIZE]; - nSkill mPlayerSkill[N_SKILL]; // pointer to an array of skill values + ButtonBoxListener *mListener; }; - #endif diff --git a/src/gui/buy.cpp b/src/gui/buy.cpp index 714f52db..597a7cad 100644 --- a/src/gui/buy.cpp +++ b/src/gui/buy.cpp @@ -19,11 +19,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "buy.h" - #include <guichan/widgets/label.hpp> #include "button.h" +#include "buy.h" #include "scrollarea.h" #include "shop.h" #include "shoplistbox.h" @@ -157,9 +156,6 @@ void BuyDialog::action(const gcn::ActionEvent &event) mSlider->setValue(mAmountItems); updateButtonsAndLabels(); } - // TODO: Actually we'd have a bug elsewhere if this check for the number - // of items to be bought ever fails, Bertram removed the assertions, is - // there a better way to ensure this fails in an _obivous_ way in C++? else if (event.getId() == "buy" && mAmountItems > 0 && mAmountItems <= mMaxItems) { diff --git a/src/gui/buy.h b/src/gui/buy.h index 0915385a..3b94bbaa 100644 --- a/src/gui/buy.h +++ b/src/gui/buy.h @@ -117,9 +117,9 @@ class BuyDialog : public Window, public gcn::ActionListener, ShopItems *mShopItems; - int mMoney; - int mAmountItems; - int mMaxItems; + Uint32 mMoney; + Uint32 mAmountItems; + Uint32 mMaxItems; }; #endif diff --git a/src/gui/buysell.cpp b/src/gui/buysell.cpp index 42380882..57c95841 100644 --- a/src/gui/buysell.cpp +++ b/src/gui/buysell.cpp @@ -19,9 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "buysell.h" - #include "button.h" +#include "buysell.h" #include "../npc.h" diff --git a/src/gui/buysell.h b/src/gui/buysell.h index 2391ed1c..d73205b6 100644 --- a/src/gui/buysell.h +++ b/src/gui/buysell.h @@ -26,6 +26,8 @@ #include "window.h" +#include "../guichanfwd.h" + /** * A dialog to choose between buying or selling at a shop. * diff --git a/src/gui/char_select.cpp b/src/gui/char_select.cpp index d951f12e..53cc1e8d 100644 --- a/src/gui/char_select.cpp +++ b/src/gui/char_select.cpp @@ -19,13 +19,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "char_select.h" - #include <string> #include <guichan/widgets/label.hpp> #include "button.h" +#include "char_select.h" #include "confirm_dialog.h" #include "ok_dialog.h" #include "playerbox.h" @@ -254,7 +253,10 @@ CharCreateDialog::CharCreateDialog(Window *parent, int slot, Network *network, { mPlayer = new Player(0, 0, NULL); mPlayer->setGender(gender); - mPlayer->setHairStyle(rand() % Being::getHairStylesNr(), rand() % Being::getHairColorsNr()); + + int numberOfHairColors = ColorDB::size(); + + mPlayer->setHairStyle(rand() % mPlayer->getNumOfHairstyles(), rand() % numberOfHairColors); mNameField = new TextField(""); mNameLabel = new gcn::Label("Name:"); @@ -320,6 +322,7 @@ CharCreateDialog::~CharCreateDialog() void CharCreateDialog::action(const gcn::ActionEvent &event) { + int numberOfColors = ColorDB::size(); if (event.getId() == "create") { if (getName().length() >= 4) { // Attempt to create the character @@ -335,16 +338,16 @@ CharCreateDialog::action(const gcn::ActionEvent &event) scheduleDelete(); } else if (event.getId() == "nextcolor") { - mPlayer->setHairStyle(-1, mPlayer->getHairColor() + 1); + mPlayer->setHairStyle(mPlayer->getHairStyle(), (mPlayer->getHairColor() + 1) % numberOfColors); } else if (event.getId() == "prevcolor") { - mPlayer->setHairStyle(-1, mPlayer->getHairColor() + Being::getHairColorsNr() - 1); + mPlayer->setHairStyle(mPlayer->getHairStyle(), (mPlayer->getHairColor() + numberOfColors - 1) % numberOfColors); } else if (event.getId() == "nextstyle") { - mPlayer->setHairStyle(mPlayer->getHairStyle() + 1, -1); + mPlayer->setHairStyle(mPlayer->getHairStyle() + 1, mPlayer->getHairColor()); } else if (event.getId() == "prevstyle") { - mPlayer->setHairStyle(mPlayer->getHairStyle() + Being::getHairStylesNr() - 1, -1); + mPlayer->setHairStyle(mPlayer->getHairStyle() + mPlayer->getNumOfHairstyles() - 1, mPlayer->getHairColor()); } } diff --git a/src/gui/char_select.h b/src/gui/char_select.h index 0890bea9..63630736 100644 --- a/src/gui/char_select.h +++ b/src/gui/char_select.h @@ -22,16 +22,16 @@ #ifndef _CHAR_SELECT_H #define _CHAR_SELECT_H +#include <guichan/actionlistener.hpp> + #include "window.h" #include "../guichanfwd.h" #include "../lockedarray.h" -#include <guichan/actionlistener.hpp> - -class Player; class LocalPlayer; class Network; +class Player; class PlayerBox; /** diff --git a/src/gui/char_server.cpp b/src/gui/char_server.cpp index ce068ad1..054aff84 100644 --- a/src/gui/char_server.cpp +++ b/src/gui/char_server.cpp @@ -19,9 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "char_server.h" - #include "button.h" +#include "char_server.h" #include "listbox.h" #include "scrollarea.h" @@ -106,7 +105,6 @@ ServerSelectDialog::action(const gcn::ActionEvent &event) mLoginData->hostname = iptostring(si->address); mLoginData->port = si->port; mLoginData->updateHost = si->updateHost; - state = mNextState; } else if (event.getId() == "cancel") { diff --git a/src/gui/chargedialog.cpp b/src/gui/chargedialog.cpp index 1c9edf45..1733c7eb 100644 --- a/src/gui/chargedialog.cpp +++ b/src/gui/chargedialog.cpp @@ -24,7 +24,6 @@ */ #include "chargedialog.h" - #include "progressbar.h" #include "../localplayer.h" diff --git a/src/gui/chargedialog.h b/src/gui/chargedialog.h index 9517ef6a..53998ab8 100644 --- a/src/gui/chargedialog.h +++ b/src/gui/chargedialog.h @@ -24,6 +24,8 @@ #include "window.h" +#include "../guichanfwd.h" + class ProgressBar; #define CHARGE_TIME 1000 // time in milliseconds it takes to charge up an attack diff --git a/src/gui/chat.cpp b/src/gui/chat.cpp index 1e22772b..3f12c462 100644 --- a/src/gui/chat.cpp +++ b/src/gui/chat.cpp @@ -20,31 +20,32 @@ */ #include <algorithm> -#include <sstream> +#include <fstream> #include <guichan/focushandler.hpp> #include <guichan/key.hpp> -#include "chat.h" - #include "browserbox.h" +#include "chat.h" #include "chatinput.h" #include "scrollarea.h" #include "windowcontainer.h" +#include "../beingmanager.h" #include "../configuration.h" +#include "../extensions.h" #include "../game.h" #include "../localplayer.h" +#include "../party.h" +#include "../recorder.h" #include "../net/messageout.h" #include "../net/protocol.h" #include "../utils/trim.h" -ChatWindow::ChatWindow(Network *network): - Window(""), - mNetwork(network), - mTmpVisible(false) +ChatWindow::ChatWindow(Network * network): +Window(""), mNetwork(network), mTmpVisible(false) { setWindowName("Chat"); @@ -61,10 +62,10 @@ ChatWindow::ChatWindow(Network *network): mTextOutput->disableLinksAndUserColors(); mTextOutput->setMaxRow((int) config.getValue("ChatLogLength", 0)); mScrollArea = new ScrollArea(mTextOutput); - mScrollArea->setPosition( - mScrollArea->getFrameSize(), mScrollArea->getFrameSize()); - mScrollArea->setScrollPolicy( - gcn::ScrollArea::SHOW_NEVER, gcn::ScrollArea::SHOW_ALWAYS); + mScrollArea->setPosition(mScrollArea->getFrameSize(), + mScrollArea->getFrameSize()); + mScrollArea->setScrollPolicy(gcn::ScrollArea::SHOW_NEVER, + gcn::ScrollArea::SHOW_ALWAYS); mScrollArea->setOpaque(false); add(mScrollArea); @@ -73,10 +74,26 @@ ChatWindow::ChatWindow(Network *network): // Add key listener to chat input to be able to respond to up/down mChatInput->addKeyListener(this); mCurHist = mHistory.end(); + + // Read the party prefix + std::string partyPrefix = config.getValue("PartyPrefix", "$"); + mPartyPrefix = (partyPrefix == "" ? '$' : partyPrefix.at(0)); + mReturnToggles = config.getValue("ReturnToggles", "0") == "1"; + mRecorder = new Recorder(this); + mParty = new Party(this, mNetwork); +} + +ChatWindow::~ChatWindow() +{ + char partyPrefix[2] = "."; + *partyPrefix = mPartyPrefix; + config.setValue("PartyPrefix", partyPrefix); + config.setValue("ReturnToggles", mReturnToggles ? "1" : "0"); + delete mRecorder; } void -ChatWindow::logic() + ChatWindow::logic() { // todo: only do this when the size changes (updateWidgets?) @@ -84,28 +101,26 @@ ChatWindow::logic() mChatInput->setPosition(mChatInput->getFrameSize(), area.height - mChatInput->getHeight() - - mChatInput->getFrameSize()); + mChatInput->getFrameSize()); mChatInput->setWidth(area.width - 2 * mChatInput->getFrameSize()); mScrollArea->setWidth(area.width - 2 * mScrollArea->getFrameSize()); mScrollArea->setHeight(area.height - 2 * mScrollArea->getFrameSize() - - mChatInput->getHeight() - 5); + mChatInput->getHeight() - 5); mScrollArea->logic(); } -void -ChatWindow::chatLog(std::string line, int own) +void ChatWindow::chatLog(std::string line, int own, bool ignoreRecord) { // Trim whitespace trim(line); CHATLOG tmp; - tmp.own = own; + tmp.own = own; tmp.nick = ""; tmp.text = line; // Fix the owner of welcome message. - if (line.substr(0, 7) == "Welcome") - { + if (line.substr(0, 7) == "Welcome") { own = BY_SERVER; } @@ -115,37 +130,41 @@ ChatWindow::chatLog(std::string line, int own) tmp.text = line.substr(pos + 3); } - std::string lineColor = "##0"; // Equiv. to BrowserBox::BLACK + std::string lineColor = "##C"; switch (own) { case BY_GM: tmp.nick += std::string("Global announcement: "); - lineColor = "##1"; // Equiv. to BrowserBox::RED + lineColor = "##G"; break; case BY_PLAYER: tmp.nick += CAT_NORMAL; - lineColor = "##2"; // Equiv. to BrowserBox::GREEN + lineColor = "##Y"; break; case BY_OTHER: tmp.nick += CAT_NORMAL; - lineColor = "##0"; // Equiv. to BrowserBox::BLACK + lineColor = "##C"; break; case BY_SERVER: tmp.nick = "Server: "; tmp.text = line; - lineColor = "##7"; // Equiv. to BrowserBox::PINK + lineColor = "##S"; + break; + case BY_PARTY: + tmp.nick += CAT_NORMAL; + lineColor = "##P"; break; case ACT_WHISPER: tmp.nick += CAT_WHISPER; - lineColor = "##3"; // Equiv. to BrowserBox::BLUE + lineColor = "##W"; break; case ACT_IS: tmp.nick += CAT_IS; - lineColor = "##5"; // Equiv. to BrowserBox::YELLOW + lineColor = "##I"; break; case BY_LOGGER: tmp.nick = ""; tmp.text = line; - lineColor = "##8"; // Equiv. to BrowserBox::GREY + lineColor = "##L"; break; } @@ -155,41 +174,37 @@ ChatWindow::chatLog(std::string line, int own) // Format the time string properly std::stringstream timeStr; - timeStr << "[" - << ((((t / 60) / 60) % 24 < 10) ? "0" : "") - << (int)(((t / 60) / 60) % 24) - << ":" - << (((t / 60) % 60 < 10) ? "0" : "") - << (int)((t / 60) % 60) - << "] "; + timeStr << "[" << ((((t / 60) / 60) % 24 < 10) ? "0" : "") + << (int) (((t / 60) / 60) % 24) + << ":" << (((t / 60) % 60 < 10) ? "0" : "") + << (int) ((t / 60) % 60) + << "] "; line = lineColor + timeStr.str() + tmp.nick + tmp.text; // 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. - if (mScrollArea->getVerticalScrollAmount() == mScrollArea->getVerticalMaxScroll()) - { + if (mScrollArea->getVerticalScrollAmount() == + mScrollArea->getVerticalMaxScroll()) { mTextOutput->addRow(line); - mScrollArea->setVerticalScrollAmount(mScrollArea->getVerticalMaxScroll()); - } - else - { + mScrollArea->setVerticalScrollAmount(mScrollArea-> + getVerticalMaxScroll()); + } else { mTextOutput->addRow(line); } + + mRecorder->record(line.substr(3)); } -void -ChatWindow::chatLog(CHATSKILL act) +void ChatWindow::chatLog(CHATSKILL act) { chatLog(const_msg(act), BY_SERVER); } -void -ChatWindow::action(const gcn::ActionEvent &event) +void ChatWindow::action(const gcn::ActionEvent & event) { - if (event.getId() == "chatinput") - { + if (event.getId() == "chatinput") { std::string message = mChatInput->getText(); if (!message.empty()) { @@ -197,7 +212,6 @@ ChatWindow::action(const gcn::ActionEvent &event) if (mHistory.empty() || message != mHistory.back()) { mHistory.push_back(message); } - // Reset history iterator mCurHist = mHistory.end(); @@ -207,24 +221,23 @@ ChatWindow::action(const gcn::ActionEvent &event) // Clear the text from the chat input mChatInput->setText(""); } - - // Remove focus and hide input - mFocusHandler->focusNone(); - - // If the chatWindow is shown up because you want to send a message - // It should hide now - if (mTmpVisible) { - setVisible(false); + if (message.empty() || !mReturnToggles) { + // Remove focus and hide input + mFocusHandler->focusNone(); + + // If the chatWindow is shown up because you want to send a message + // It should hide now + if (mTmpVisible) { + setVisible(false); + } } } } -void -ChatWindow::requestChatFocus() +void ChatWindow::requestChatFocus() { // Make sure chatWindow is visible - if (!isVisible()) - { + if (!isVisible()) { setVisible(true); /* @@ -234,57 +247,38 @@ ChatWindow::requestChatFocus() */ mTmpVisible = true; } - // Give focus to the chat input mChatInput->setVisible(true); mChatInput->requestFocus(); } -bool -ChatWindow::isInputFocused() +bool ChatWindow::isInputFocused() { return mChatInput->isFocused(); } -void -ChatWindow::whisper(const std::string &nick, std::string msg, int prefixlen) -{ - std::string recvnick = ""; - msg.erase(0, prefixlen + 1); - - if (msg.substr(0,1) == "\"") - { - const std::string::size_type pos = msg.find('"', 1); - if (pos != std::string::npos) { - recvnick = msg.substr(1, pos - 1); - msg.erase(0, pos + 2); - } - } - else - { - const std::string::size_type pos = msg.find(" "); - if (pos != std::string::npos) { - recvnick = msg.substr(0, pos); - msg.erase(0, pos + 1); - } - } - - MessageOut outMsg(mNetwork); - outMsg.writeInt16(CMSG_CHAT_WHISPER); - outMsg.writeInt16(msg.length() + 28); - outMsg.writeString(recvnick, 24); - outMsg.writeString(msg, msg.length()); - - chatLog("Whispering to " + recvnick + " : " + msg, BY_PLAYER); -} - -void -ChatWindow::chatSend(const std::string &nick, std::string msg) +void ChatWindow::chatSend(const std::string &nick, std::string msg) { /* Some messages are managed client side, while others * require server handling by proper packet. Probably * those if elses should be replaced by protocol calls */ + // Send party message + if (msg.at(0) == mPartyPrefix) { + msg.erase(0, 1); + std::size_t length = msg.length() + 1; + + if (length == 0) { + chatLog("Trying to send a blank party message.", BY_SERVER); + return; + } + MessageOut outMsg(mNetwork); + + outMsg.writeInt16(CMSG_PARTY_MESSAGE); + outMsg.writeInt16(length + 4); + outMsg.writeString(msg, length); + return; + } // Prepare ordinary message if (msg.substr(0, 1) != "/") { msg = nick + " : " + msg; @@ -294,18 +288,31 @@ ChatWindow::chatSend(const std::string &nick, std::string msg) // Added + 1 in order to let eAthena parse admin commands correctly outMsg.writeInt16(msg.length() + 4 + 1); outMsg.writeString(msg, msg.length() + 1); + return; + } + msg.erase(0, 1); + trim(msg); + + std::size_t space = msg.find(" "); + std::string command = msg.substr(0, space); + if (space == std::string::npos) { + msg = ""; + } else { + msg = msg.substr(space); + trim(msg); } - else if (msg.substr(0, IS_ANNOUNCE_LENGTH) == IS_ANNOUNCE) + + if (command == "announce") { - msg.erase(0, IS_ANNOUNCE_LENGTH); MessageOut outMsg(mNetwork); outMsg.writeInt16(0x0099); outMsg.writeInt16(msg.length() + 4); outMsg.writeString(msg, msg.length()); + return; } - else if (msg.substr(0, IS_HELP_LENGTH) == IS_HELP) + else if (command == "help") { - msg.erase(0, IS_HELP_LENGTH + 1); + msg.erase(0, 6); trim(msg); std::size_t space = msg.find(" "); std::string msg1; @@ -325,112 +332,233 @@ ChatWindow::chatSend(const std::string &nick, std::string msg) trim(msg1); help(msg, msg1); } - else if (msg.substr(0, IS_WHERE_LENGTH) == IS_WHERE) + else if (command == "where") { // Display the current map, X, and Y std::ostringstream where; where << map_path << " " << player_node->mX << "," << player_node->mY; chatLog(where.str(), BY_SERVER); } - else if (msg.substr(0, IS_WHO_LENGTH) == IS_WHO) - { + if (command == "who") { MessageOut outMsg(mNetwork); outMsg.writeInt16(0x00c1); + return; } - else if (msg.substr(0, IS_CLEAR_LENGTH) == IS_CLEAR) - { + if (command == "clear") { mTextOutput->clearRows(); + return; } - else if (msg.substr(0, IS_WHISPER_LENGTH) == IS_WHISPER) - whisper(nick, msg, IS_WHISPER_LENGTH); - else if (msg.substr(0, IS_SHORT_WHISPER_LENGTH) == IS_SHORT_WHISPER) - whisper(nick, msg, IS_SHORT_WHISPER_LENGTH); - else - { - chatLog("Unknown command", BY_SERVER); + if (command == "whisper" || command == "msg" || command == "w") { + std::string recvnick = ""; + + if (msg.substr(0, 1) == "\"") { + const std::string::size_type pos = msg.find('"', 1); + if (pos != std::string::npos) { + recvnick = msg.substr(1, pos - 1); + msg.erase(0, pos + 2); + } + } else { + const std::string::size_type pos = msg.find(" "); + if (pos != std::string::npos) { + recvnick = msg.substr(0, pos); + msg.erase(0, pos + 1); + } + } + + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_CHAT_WHISPER); + outMsg.writeInt16(msg.length() + 28); + outMsg.writeString(recvnick, 24); + outMsg.writeString(msg, msg.length()); + + chatLog("Whispering to " + recvnick + " : " + msg, BY_PLAYER); + return; + } + if (command == "record") { + mRecorder->respond(msg); + return; + } + if (command == "toggle") { + if (msg == "") { + chatLog(mReturnToggles ? "Return toggles chat." + : "Message closes chat.", BY_SERVER); + return; + } + msg = msg.substr(0, 1); + if (msg == "1" || msg == "y" || msg == "t" || msg == "Y" || msg == "T") { + chatLog("Return now toggles chat.", BY_SERVER); + mReturnToggles = true; + return; + } + if (msg == "0" || msg == "n" || msg == "f" || msg == "N" || msg == "F") { + chatLog("Message now closes chat.", BY_SERVER); + mReturnToggles = false; + return; + } + chatLog("Options to /toggle are \"yes\", \"no\", \"true\", \"false\", " + "\"1\", \"0\".", BY_SERVER); + return; } + if (command == "party") { + if (msg == "") { + chatLog("Unknown party command... Type \"/help\" party for more " + "information.", BY_SERVER); + return; + } + const std::string::size_type space = msg.find(" "); + std::string rest = (space == std::string::npos ? "" + : msg.substr(space + 1, msg.length())); + if (rest != "") { + msg = msg.substr(0, space); + while (msg != "" && msg[0] == ' ') { + msg = msg.substr(1, msg.length()); + } + } + party(msg, rest); + return; + } + if (command == "cast") { + /* + * This will eventually be replaced by a GUI, so + * we don't need to get too sophisticated + */ + if (extensions.aethyra_spells) { + MessageOut outMsg(mNetwork); + if (msg == "heal") { + outMsg.writeInt16(0x03f3); + outMsg.writeInt16(0x01); + outMsg.writeInt32(0); + outMsg.writeInt8(0); + outMsg.writeInt8(0); + outMsg.writeString("", 24); + } else if (msg == "gather") { + outMsg.writeInt16(0x03f3); + outMsg.writeInt16(0x02); + outMsg.writeInt32(0); + outMsg.writeInt8(0); + outMsg.writeInt8(0); + outMsg.writeString("", 24); + } else { + chatLog("No such spell!", BY_SERVER); + } + } else { + chatLog("The current server doesn't support spells", BY_SERVER); + } + return; + } + if (command == "present") { + Beings & beings = beingManager->getAll(); + std::string response = ""; + for (BeingIterator bi = beings.begin(), be = beings.end(); + bi != be; ++bi) { + if ((*bi)->getType() == Being::PLAYER) { + if (response != "") { + response += ", "; + } + response += (*bi)->getName(); + } + } + if (mRecorder->isRecording()) { + // Get the current system time + time_t t; + time(&t); + + // Format the time string properly + std::stringstream timeStr; + timeStr << "[" << ((((t / 60) / 60) % 24 < 10) ? "0" : "") + << (int) (((t / 60) / 60) % 24) + << ":" << (((t / 60) % 60 < 10) ? "0" : "") + << (int) ((t / 60) % 60) + << "] "; + + + mRecorder->record(timeStr.str() + "Present: " + response + "."); + chatLog("Attendance written to record log.", BY_SERVER, true); + } else { + chatLog("Present: " + response, BY_SERVER); + } + return; + } + chatLog("Unknown command", BY_SERVER); } -std::string -ChatWindow::const_msg(CHATSKILL act) +std::string ChatWindow::const_msg(CHATSKILL act) { std::string msg; if (act.success == SKILL_FAILED && act.skill == SKILL_BASIC) { switch (act.bskill) { - case BSKILL_TRADE : + case BSKILL_TRADE: msg = "Trade failed!"; break; - case BSKILL_EMOTE : + case BSKILL_EMOTE: msg = "Emote failed!"; break; - case BSKILL_SIT : + case BSKILL_SIT: msg = "Sit failed!"; break; - case BSKILL_CREATECHAT : + case BSKILL_CREATECHAT: msg = "Chat creating failed!"; break; - case BSKILL_JOINPARTY : + case BSKILL_JOINPARTY: msg = "Could not join party!"; break; - case BSKILL_SHOUT : + case BSKILL_SHOUT: msg = "Cannot shout!"; break; } switch (act.reason) { - case RFAIL_SKILLDEP : - msg += " You have not yet reached a high enough lvl!"; + case RFAIL_SKILLDEP: + msg += " You have not yet reached a high enough level!"; break; - case RFAIL_INSUFHP : + case RFAIL_INSUFHP: msg += " Insufficient HP!"; break; - case RFAIL_INSUFSP : + case RFAIL_INSUFSP: msg += " Insufficient SP!"; break; - case RFAIL_NOMEMO : + case RFAIL_NOMEMO: msg += " You have no memos!"; break; - case RFAIL_SKILLDELAY : + case RFAIL_SKILLDELAY: msg += " You cannot do that right now!"; break; - case RFAIL_ZENY : + case RFAIL_ZENY: msg += " Seems you need more Zeny... ;-)"; break; - case RFAIL_WEAPON : + case RFAIL_WEAPON: msg += " You cannot use this skill with that kind of weapon!"; break; - case RFAIL_REDGEM : + case RFAIL_REDGEM: msg += " You need another red gem!"; break; - case RFAIL_BLUEGEM : + case RFAIL_BLUEGEM: msg += " You need another blue gem!"; break; - case RFAIL_OVERWEIGHT : + case RFAIL_OVERWEIGHT: msg += " You're carrying to much to do this!"; break; - default : + default: msg += " Huh? What's that?"; break; } } else { - switch(act.skill) { - case SKILL_WARP : + switch (act.skill) { + case SKILL_WARP: msg = "Warp failed..."; break; - case SKILL_STEAL : + case SKILL_STEAL: msg = "Could not steal anything..."; break; - case SKILL_ENVENOM : + case SKILL_ENVENOM: msg = "Poison had no effect..."; break; } } - return msg; } -void -ChatWindow::scroll(int amount) +void ChatWindow::scroll(int amount) { if (!isVisible()) return; @@ -442,25 +570,20 @@ ChatWindow::scroll(int amount) mTextOutput->showPart(scr); } -void -ChatWindow::keyPressed(gcn::KeyEvent &event) +void ChatWindow::keyPressed(gcn::KeyEvent & event) { if (event.getKey().getValue() == gcn::Key::DOWN && - mCurHist != mHistory.end()) - { + mCurHist != mHistory.end()) { // Move forward through the history HistoryIterator prevHist = mCurHist++; if (mCurHist != mHistory.end()) { mChatInput->setText(*mCurHist); mChatInput->setCaretPosition(mChatInput->getText().length()); - } - else { + } else { mCurHist = prevHist; } - } - else if (event.getKey().getValue() == gcn::Key::UP && - mCurHist != mHistory.begin() && mHistory.size() > 0) - { + } else if (event.getKey().getValue() == gcn::Key::UP && + mCurHist != mHistory.begin() && mHistory.size() > 0) { // Move backward through the history mCurHist--; mChatInput->setText(*mCurHist); @@ -468,15 +591,13 @@ ChatWindow::keyPressed(gcn::KeyEvent &event) } } -void -ChatWindow::setInputText(std::string input_str) +void ChatWindow::setInputText(std::string input_str) { - mChatInput->setText(input_str + " "); - requestChatFocus(); + mChatInput->setText(input_str + " "); + requestChatFocus(); } -void -ChatWindow::setVisible(bool isVisible) +void ChatWindow::setVisible(bool isVisible) { Window::setVisible(isVisible); @@ -488,18 +609,46 @@ ChatWindow::setVisible(bool isVisible) mTmpVisible = false; } -void ChatWindow::help(const std::string &msg1, const std::string &msg2) +void ChatWindow::party(const std::string & command, const std::string & rest) +{ + if (command == "prefix") { + if (rest == "") { + char temp[2] = "."; + *temp = mPartyPrefix; + chatLog("The current party prefix is " + std::string(temp), + BY_SERVER); + return; + } + if (rest.length() != 1) { + chatLog("Party prefix must be one character long.", BY_SERVER); + } else { + if (rest == "/") { + chatLog("Cannot use a '/' as the prefix.", BY_SERVER); + } else { + mPartyPrefix = rest.at(0); + chatLog("Changing prefix to " + rest, BY_SERVER); + } + } + return; + } + mParty->respond(command, rest); +} + +void ChatWindow::help(const std::string & msg1, const std::string & msg2) { chatLog("-- Help --", BY_SERVER); - if (msg1 == "") - { + if (msg1 == "") { chatLog("/announce: Global announcement (GM only)", BY_SERVER); chatLog("/clear: Clears this window", BY_SERVER); chatLog("/help: Display this help.", BY_SERVER); + mParty->help(); + chatLog("/present: Get list of players present", BY_SERVER); + mRecorder->help(); + chatLog("/toggle: Determine whether <return> toggles the chat log.", + BY_SERVER); chatLog("/where: Display map name", BY_SERVER); chatLog("/whisper <nick> <message>: Sends a private <message>" " to <nick>", BY_SERVER); - chatLog("/w <nick> <message>: Short form for /whisper", BY_SERVER); chatLog("/who: Display number of online users", BY_SERVER); chatLog("For more information, type /help <command>", BY_SERVER); return; @@ -528,6 +677,37 @@ void ChatWindow::help(const std::string &msg1, const std::string &msg2) chatLog("This command displays help on <command>.", BY_SERVER); return; } + if (msg1 == "party") + { + mParty->help(msg2); + return; + } + if (msg1 == "present") + { + chatLog("Command: /present", BY_SERVER); + chatLog("This command gets a list of players within hearing " + "and sends it to either the record log if recording, or the " + "chat log otherwise.", BY_SERVER); + return; + } + if (msg1 == "record") + { + mRecorder->help(msg2); + return; + } + if (msg1 == "toggle") + { + chatLog("Command: /toggle <state>", BY_SERVER); + chatLog("This command sets whether the return key should toggle the " + "chat log, or whether the chat log turns off automatically.", + BY_SERVER); + chatLog("<state> can be one of \"1\", \"yes\", \"true\" to turn " + "the toggle on, or \"0\", \"no\", \"false\" to turn the " + "toggle off.", BY_SERVER); + chatLog("Command: /toggle", BY_SERVER); + chatLog("This command displays the return toggle status.", BY_SERVER); + return; + } if (msg1 == "where") { chatLog("Command: /where", BY_SERVER); @@ -535,11 +715,9 @@ void ChatWindow::help(const std::string &msg1, const std::string &msg2) BY_SERVER); return; } - if (msg1 == "whisper" || msg1 == "w") - { + if (msg1 == "whisper" || msg1 == "msg" || msg1 == "w") { chatLog("Command: /whisper <nick> <msg>", BY_SERVER); - chatLog("Command: /w <nick> <msg>", BY_SERVER); - chatLog("This command sends the message <msg> to <nick>.", BY_SERVER); + chatLog("This command sends the message <msg> to <nick.", BY_SERVER); chatLog("If the <nick> has spaces in it, enclose it in " "double quotes (\").", BY_SERVER); return; diff --git a/src/gui/chat.h b/src/gui/chat.h index 76a8146c..437dc115 100644 --- a/src/gui/chat.h +++ b/src/gui/chat.h @@ -22,6 +22,7 @@ #ifndef _TMW_CHAT_H #define _TMW_CHAT_H +#include <fstream> #include <list> #include <string> @@ -34,32 +35,20 @@ class BrowserBox; class Network; +class Recorder; +class Party; class ScrollArea; #define BY_GM 0 // those should be self-explanatory =) #define BY_PLAYER 1 #define BY_OTHER 2 #define BY_SERVER 3 +#define BY_PARTY 4 -#define ACT_WHISPER 4 // getting whispered at -#define ACT_IS 5 // equivalent to "/me" on IRC - -#define BY_LOGGER 6 - -#define IS_ANNOUNCE "/announce " -#define IS_ANNOUNCE_LENGTH 10 -#define IS_HELP "/help" -#define IS_HELP_LENGTH 5 -#define IS_WHERE "/where" -#define IS_WHERE_LENGTH 6 -#define IS_WHO "/who" -#define IS_WHO_LENGTH 4 -#define IS_CLEAR "/clear" -#define IS_CLEAR_LENGTH 6 -#define IS_WHISPER "/whisper" -#define IS_WHISPER_LENGTH 8 -#define IS_SHORT_WHISPER "/w" -#define IS_SHORT_WHISPER_LENGTH 2 +#define ACT_WHISPER 5 // getting whispered at +#define ACT_IS 6 // equivalent to "/me" on IRC + +#define BY_LOGGER 7 /** * gets in between usernick and message text depending on @@ -118,133 +107,150 @@ struct CHATSKILL * \ingroup Interface */ class ChatWindow : public Window, public gcn::ActionListener, - public gcn::KeyListener + public gcn::KeyListener { public: - /** - * Constructor. - */ - ChatWindow(Network *network); - - /** - * Logic (updates components' size) - */ - void logic(); - - /* - * Adds a line of text to our message list. Parameters: - * - * @param line Text message. - * @parem own Type of message (usually the owner-type). - */ - void chatLog(std::string line, int own); - - /* - * Calls original chat_log() after processing the packet. - */ - void chatLog(CHATSKILL); - - /** - * Performs action. - */ - void action(const gcn::ActionEvent &event); - - /** - * Request focus for typing chat message. - */ - void requestChatFocus(); - - /** - * Checks whether ChatWindow is Focused or not. - */ - bool isInputFocused(); - - /** - * Determines whether to send a command or an ordinary message, then - * contructs packets & sends them. - * - * @param nick The character's name to display in front. - * @param msg The message text which is to be send. - * - * NOTE: - * The nickname is required by the server, if not specified - * the message may not be sent unless a command was intended - * which requires another packet to be constructed! you can - * achieve this by putting a slash ("/") infront of the - * message followed by the command name and the message. - * of course all slash-commands need implemented handler- - * routines. ;-) - * remember, a line starting with "@" is not a command that needs - * to be parsed rather is sent using the normal chat-packet. - * - * EXAMPLE: - * // for an global announcement /- command - * chatlog.chat_send("", "/announce Hello to all logged in users!"); - * // for simple message by a user /- message - * chatlog.chat_send("Zaeiru", "Hello to all users on the screen!"); - */ - void - chatSend(const std::string &nick, std::string msg); - - /** Called when key is pressed */ - void - keyPressed(gcn::KeyEvent &event); - - /** Called to set current text */ - void - setInputText(std::string input_str); - - /** Override to reset mTmpVisible */ - void - setVisible(bool visible); + /** + * Constructor. + */ + ChatWindow(Network *network); + + /** + * Destructor: used to write back values to the config file + */ + ~ChatWindow(); + + /** + * Logic (updates components' size) + */ + void logic(); + + /** + * Adds a line of text to our message list. Parameters: + * + * @param line Text message. + * @parem own Type of message (usually the owner-type). + */ + void chatLog(std::string line, int own, bool ignoreRecord = false); + + /** + * Calls original chat_log() after processing the packet. + */ + void chatLog(CHATSKILL); + + /** + * Performs action. + */ + void action(const gcn::ActionEvent &event); + + /** + * Request focus for typing chat message. + */ + void requestChatFocus(); + + /** + * Checks whether ChatWindow is Focused or not. + */ + bool isInputFocused(); + + /** + * Determines whether to send a command or an ordinary message, then + * contructs packets & sends them. + * + * @param nick The character's name to display in front. + * @param msg The message text which is to be send. + * + * NOTE: + * The nickname is required by the server, if not specified + * the message may not be sent unless a command was intended + * which requires another packet to be constructed! you can + * achieve this by putting a slash ("/") infront of the + * message followed by the command name and the message. + * of course all slash-commands need implemented handler- + * routines. ;-) + * remember, a line starting with "@" is not a command that needs + * to be parsed rather is sent using the normal chat-packet. + * + * EXAMPLE: + * // for an global announcement /- command + * chatlog.chat_send("", "/announce Hello to all logged in users!"); + * // for simple message by a user /- message + * chatlog.chat_send("Zaeiru", "Hello to all users on the screen!"); + */ + void + chatSend(const std::string &nick, std::string msg); + + /** Called when key is pressed */ + void + keyPressed(gcn::KeyEvent &event); + + /** Called to set current text */ + void + setInputText(std::string input_str); + + /** Override to reset mTmpVisible */ + void + setVisible(bool visible); /** - * Scrolls the chat window - * - * @param amount direction and amount to scroll. Negative numbers scroll - * up, positive numbers scroll down. The absolute amount indicates the - * amount of 1/8ths of chat window real estate that should be scrolled. - */ - void - scroll(int amount); - - /** - * help implements the /help command - * - * @param msg1 is the command that the player needs help on - * @param msg2 is the sub-command relating to the command - */ - void - help(const std::string &msg1, const std::string &msg2); + * Scrolls the chat window + * + * @param amount direction and amount to scroll. Negative numbers scroll + * up, positive numbers scroll down. The absolute amount indicates the + * amount of 1/8ths of chat window real estate that should be scrolled. + */ + void + scroll(int amount); + + /** + * party implements the partying chat commands + * + * @param command is the party command to perform + * @param msg is the remainder of the message + */ + void + party(const std::string &command, const std::string &msg); + + /** + * help implements the /help command + * + * @param msg1 is the command that the player needs help on + * @param msg2 is the sub-command relating to the command + */ + void + help(const std::string &msg1, const std::string &msg2); private: - Network *mNetwork; - bool mTmpVisible; - - void - whisper(const std::string &nick, std::string msg, int prefixlen); - - /** One item in the chat log */ - struct CHATLOG - { - std::string nick; - std::string text; - int own; - }; - - /** Constructs failed messages for actions */ - std::string const_msg(CHATSKILL); - - gcn::TextField *mChatInput; /**< Input box for typing chat messages */ - BrowserBox *mTextOutput; /**< Text box for displaying chat history */ - ScrollArea *mScrollArea; /**< Scroll area around text output */ - - typedef std::list<std::string> History; - typedef History::iterator HistoryIterator; - History mHistory; /**< Command history */ - HistoryIterator mCurHist; /**< History iterator */ -}; + Network *mNetwork; + bool mTmpVisible; + + /** One item in the chat log */ + struct CHATLOG + { + std::string nick; + std::string text; + int own; + }; + + /** Constructs failed messages for actions */ + std::string const_msg(CHATSKILL); + + gcn::TextField *mChatInput; /**< Input box for typing chat messages */ + BrowserBox *mTextOutput; /**< Text box for displaying chat history */ + ScrollArea *mScrollArea; /**< Scroll area around text output */ + + typedef std::list<std::string> History; + typedef History::iterator HistoryIterator; + History mHistory; /**< Command history */ + HistoryIterator mCurHist; /**< History iterator */ + Recorder *mRecorder; /**< Recording class */ + char mPartyPrefix; /**< Messages beginning with the prefix are sent to + the party */ + bool mReturnToggles; /**< Marks whether <Return> toggles the chat log + or not */ + Party *mParty; +}; extern ChatWindow *chatWindow; #endif diff --git a/src/gui/chatinput.h b/src/gui/chatinput.h index e04dfa6e..44e22956 100644 --- a/src/gui/chatinput.h +++ b/src/gui/chatinput.h @@ -22,9 +22,11 @@ #ifndef _TMW_CHATINPUT_H #define _TMW_CHATINPUT_H +#include <guichan/focuslistener.hpp> + #include "textfield.h" -#include <guichan/focuslistener.hpp> +#include "../guichanfwd.h" /** * The chat input hides when it loses focus. It is also invisible by default. diff --git a/src/gui/checkbox.h b/src/gui/checkbox.h index 839ca97e..f6d8f2e5 100644 --- a/src/gui/checkbox.h +++ b/src/gui/checkbox.h @@ -26,6 +26,8 @@ #include <guichan/widgets/checkbox.hpp> +#include "../guichanfwd.h" + class Image; /** diff --git a/src/gui/colour.cpp b/src/gui/colour.cpp new file mode 100644 index 00000000..4c3782a4 --- /dev/null +++ b/src/gui/colour.cpp @@ -0,0 +1,137 @@ +/*************************************************************************** + * Copyright (C) 2008 by Douglas Boffey * + * * + * DougABoffey@netscape.net * + * 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 * + * (at your option) any later version. * + * * + * This program is distributed with The Mana Experiment * + * 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 <cstdio> + +#include "colour.h" + +#include "../configuration.h" + +Colour::Colour() +{ + addColour('C', 0x000000, "Chat"); + addColour('G', 0xff0000, "GM"); + addColour('Y', 0x1fa052, "Player"); + addColour('W', 0x0000ff, "Whisper"); + addColour('I', 0xf1dc27, "Is"); + addColour('P', 0xff00d8, "Party"); + addColour('S', 0x8415e2, "Server"); + addColour('L', 0x919191, "Logger"); + addColour('<', 0xe50d0d, "Hyperlink"); + commit(); +} + +Colour::~Colour() +{ + for (ColVector::iterator col = mColVector.begin(), + colEnd = mColVector.end(); + col != colEnd; + ++col) + { + char buffer[20]; + std::sprintf(buffer, "0x%06x", col->rgb); + config.setValue("Colour" + col->text, buffer); + } +} + +void Colour::setColour(const char c, const int rgb) +{ + for (ColVector::iterator col = mColVector.begin(), + colEnd = mColVector.end(); + col != colEnd; + ++col) + { + if (col->ch == c) + { + col->rgb = rgb; + return; + } + } +} + +int Colour::getColour(const char c, bool &valid) const +{ + for (ColVector::const_iterator col = mColVector.begin(), + colEnd = mColVector.end(); + col != colEnd; + ++col) + { + if (col->ch == c) + { + valid = true; + return col->rgb; + } + } + valid = false; + return 0x000000; +} + +std::string Colour::getElementAt(int i) +{ + if (i < 0 || i >= getNumberOfElements()) + { + return ""; + } + return mColVector[i].text; +} + +void Colour::addColour(const char c, const int rgb, const std::string &text) +{ + int trueRgb = config.getValue("Colour" + text, rgb); + mColVector.push_back(ColourElem(c, trueRgb, text)); +} + +int Colour::getColourAt(int i) +{ + if (i < 0 || i >= getNumberOfElements()) + { + return 0; + } + return mColVector[i].rgb; +} + +void Colour::setColourAt(int i, int rgb) +{ + if (i >= 0 && i < getNumberOfElements()) + { + mColVector[i].rgb = rgb; + } +} + +void Colour::commit() +{ + for (ColVector::iterator i = mColVector.begin(), iEnd = mColVector.end(); + i != iEnd; + ++i) + { + i->committedRgb = i->rgb; + } +} + +void Colour::rollback() +{ + for (ColVector::iterator i = mColVector.begin(), iEnd = mColVector.end(); + i != iEnd; + ++i) + { + i->rgb = i->committedRgb; + } +} diff --git a/src/gui/colour.h b/src/gui/colour.h new file mode 100644 index 00000000..1e8ba3db --- /dev/null +++ b/src/gui/colour.h @@ -0,0 +1,133 @@ +/*************************************************************************** + * Copyright (C) 2008 by Douglas Boffey * + * * + * DougABoffey@netscape.net * + * 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 * + * (at your option) any later version. * + * * + * This program is distributed with The Mana Experiment * + * 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 _COLOUR_H +#define _COLOUR_H + +#include <cstdio> +#include <string> +#include <vector> + +#include <guichan/listmodel.hpp> + +#include "../guichanfwd.h" + +class Colour : public gcn::ListModel +{ + public: + /** + * Constructor + */ + Colour(); + + /** + * Destructor + */ + ~Colour(); + + /** + * Define the colour replacement for a character + * + * @param c charater to be replaced + * @param rgb colour to replace character + */ + void setColour(const char c, const int rgb); + + /** + * Define the colour replacement for a character + * + * @param c character to be replaced + * @param r red component + * @param g green component + * @param b blue component + */ + void setColour(const char c, const int r, const int g, const int b) + { + setColour(c, (r << 16) | (g << 8) | b); + } + + /** + * Return the colour associated with a character, if exists + * + * @param c character requested + * @param valid indicate whether character is known + */ + int getColour(const char c, bool &valid) const; + + /** + * Return the number of colours known + */ + int getNumberOfElements() {return mColVector.size(); } + + /** + * Return the name of the ith colour + * + * @param i index of colour interested in + */ + std::string getElementAt(int i); + + /** + * Get the colour for the element at index i in the current colour + * model + */ + int getColourAt(int i); + + /** + * Set the colour for the element at index i + */ + void setColourAt(int i, int rgb); + + /** + * Commit the colours + */ + void commit(); + + /** + * Rollback the colours + */ + void rollback(); + + private: + struct ColourElem + { + ColourElem(const char c, const int rgb, const std::string &text) : + ch(c), rgb(rgb), text(text) {} + char ch; + int rgb; + int committedRgb; + std::string text; + }; + typedef std::vector<ColourElem> ColVector; + ColVector mColVector; + + /** + * Initialise colour + * + * @param c character that needs initialising + * @param rgb default colour if not found in config + * @param text identifier of colour + */ + void addColour(const char c, const int rgb, const std::string &text); +}; + +extern Colour *textColour; + +#endif diff --git a/src/gui/confirm_dialog.cpp b/src/gui/confirm_dialog.cpp index 9ef94f62..732f5769 100644 --- a/src/gui/confirm_dialog.cpp +++ b/src/gui/confirm_dialog.cpp @@ -19,39 +19,59 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "confirm_dialog.h" - -#include <guichan/widgets/label.hpp> - -#include "button.h" +#include <guichan/font.hpp> +#include "confirm_dialog.h" ConfirmDialog::ConfirmDialog(const std::string &title, const std::string &msg, Window *parent): Window(title, true, parent) { - gcn::Label *textLabel = new gcn::Label(msg); + mTextBox = new TextBox(); + mTextBox->setEditable(false); + mTextBox->setOpaque(false); + + mTextArea = new ScrollArea(mTextBox); gcn::Button *yesButton = new Button("Yes", "yes", this); gcn::Button *noButton = new Button("No", "no", this); - int w = textLabel->getWidth() + 20; + mTextArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mTextArea->setVerticalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mTextArea->setOpaque(false); + + mTextBox->setMinWidth(260); + mTextBox->setTextWrapped(msg); + + int numRows = mTextBox->getNumberOfRows(); + int width = getFont()->getWidth(title); int inWidth = yesButton->getWidth() + noButton->getWidth() + 5; - int h = textLabel->getHeight() + 25 + yesButton->getHeight(); - if (w < inWidth + 10) { - w = inWidth + 10; + if (numRows > 1) + { + // 15 == height of each line of text (based on font heights) + // 14 == row top + bottom graphic pixel heights + setContentSize(mTextBox->getMinWidth() + 15, 15 + (numRows * 15) + noButton->getHeight()); + mTextArea->setDimension(gcn::Rectangle(4, 5, mTextBox->getMinWidth() + 5, + 3 + (numRows * 14))); + } + else + { + if (width < getFont()->getWidth(msg)) + width = getFont()->getWidth(msg); + if (width < inWidth) + width = inWidth; + setContentSize(width + 15, 30 + noButton->getHeight()); + mTextArea->setDimension(gcn::Rectangle(4, 5, width + 5, 17)); } - setContentSize(w, h); - textLabel->setPosition(10, 10); yesButton->setPosition( - (w - inWidth) / 2, - h - 5 - noButton->getHeight()); + (mTextBox->getMinWidth() - inWidth) / 2, + (numRows * 14) + noButton->getHeight() - 8); noButton->setPosition( yesButton->getX() + yesButton->getWidth() + 5, - h - 5 - noButton->getHeight()); + (numRows * 14) + noButton->getHeight() - 8); - add(textLabel); + add(mTextArea); add(yesButton); add(noButton); @@ -63,6 +83,11 @@ ConfirmDialog::ConfirmDialog(const std::string &title, const std::string &msg, yesButton->requestFocus(); } +unsigned int ConfirmDialog::getNumRows() +{ + return mTextBox->getNumberOfRows(); +} + void ConfirmDialog::action(const gcn::ActionEvent &event) { // Proxy button events to our listeners diff --git a/src/gui/confirm_dialog.h b/src/gui/confirm_dialog.h index c9bfca02..109dcea0 100644 --- a/src/gui/confirm_dialog.h +++ b/src/gui/confirm_dialog.h @@ -24,8 +24,12 @@ #include <guichan/actionlistener.hpp> +#include "button.h" +#include "scrollarea.h" +#include "textbox.h" #include "window.h" +#include "../guichanfwd.h" /** * An option dialog. @@ -42,10 +46,17 @@ class ConfirmDialog : public Window, public gcn::ActionListener { ConfirmDialog(const std::string &title, const std::string &msg, Window *parent = NULL); + unsigned int getNumRows(); + /** * Called when receiving actions from the widgets. */ void action(const gcn::ActionEvent &event); + + private: + TextBox *mTextBox; + ScrollArea *mTextArea; + gcn::Button *okButton; }; #endif diff --git a/src/gui/connection.cpp b/src/gui/connection.cpp index 8ad3b436..1204b203 100644 --- a/src/gui/connection.cpp +++ b/src/gui/connection.cpp @@ -19,13 +19,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "connection.h" - #include <guichan/actionlistener.hpp> #include <guichan/widgets/label.hpp> #include "button.h" +#include "connection.h" #include "progressbar.h" #include "../main.h" diff --git a/src/gui/debugwindow.cpp b/src/gui/debugwindow.cpp index f6e9414e..7fc63096 100644 --- a/src/gui/debugwindow.cpp +++ b/src/gui/debugwindow.cpp @@ -19,18 +19,17 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "debugwindow.h" - #include <SDL_mouse.h> #include <guichan/widgets/label.hpp> #include "button.h" +#include "debugwindow.h" #include "gui.h" #include "viewport.h" -#include "../game.h" #include "../engine.h" +#include "../game.h" #include "../particle.h" #include "../map.h" diff --git a/src/gui/equipmentwindow.cpp b/src/gui/equipmentwindow.cpp index e0246787..7e1ef315 100644 --- a/src/gui/equipmentwindow.cpp +++ b/src/gui/equipmentwindow.cpp @@ -25,7 +25,9 @@ #include "../inventory.h" #include "../localplayer.h" #include "../graphics.h" +#include "../inventory.h" #include "../item.h" +#include "../localplayer.h" #include "../log.h" #include "../resources/iteminfo.h" @@ -40,7 +42,7 @@ EquipmentWindow::EquipmentWindow(Equipment *equipment): setCloseButton(true); setDefaultSize(5, 230, 200, 120); loadWindowState(); - mInventory = player_node->getInventory(); + mInventory = player_node->getInventory(); } EquipmentWindow::~EquipmentWindow() @@ -75,14 +77,15 @@ void EquipmentWindow::draw(gcn::Graphics *graphics) graphics->drawRectangle(gcn::Rectangle(160, 25, 32, 32)); - if (!(item = mInventory->getItem(mEquipment->getArrows()))) + if (!(item = mInventory->getItem(mEquipment->getArrows()))) return; image = item->getImage(); + if (image) { static_cast<Graphics*>(graphics)->drawImage(image, 160, 25); } graphics->drawText(toString(item->getQuantity()), 170, 62, - gcn::Graphics::CENTER); + gcn::Graphics::CENTER); } diff --git a/src/gui/equipmentwindow.h b/src/gui/equipmentwindow.h index 42aa7701..b669f5b1 100644 --- a/src/gui/equipmentwindow.h +++ b/src/gui/equipmentwindow.h @@ -25,6 +25,9 @@ #include "window.h" #include "../inventory.h" +#include "../guichanfwd.h" +#include "../inventory.h" + class Equipment; /** diff --git a/src/gui/focushandler.cpp b/src/gui/focushandler.cpp index 1bda568e..f9ea8b7d 100644 --- a/src/gui/focushandler.cpp +++ b/src/gui/focushandler.cpp @@ -21,7 +21,6 @@ #include "focushandler.h" - void FocusHandler::requestModalFocus(gcn::Widget *widget) { /* If there is another widget with modal focus, remove its modal focus diff --git a/src/gui/gccontainer.h b/src/gui/gccontainer.h index cc7c9336..2af7f6ad 100644 --- a/src/gui/gccontainer.h +++ b/src/gui/gccontainer.h @@ -26,6 +26,8 @@ #include <guichan/widgets/container.hpp> +#include "../guichanfwd.h" + /** * A garbage collecting container. Childs added to this container are * automatically deleted when the container is deleted. diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 96415298..70f82d9d 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -19,8 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "gui.h" - #include <guichan/exception.hpp> #include <guichan/image.hpp> #include <guichan/imagefont.hpp> @@ -29,6 +27,7 @@ #include <guichan/sdl/sdlinput.hpp> #include "focushandler.h" +#include "gui.h" #include "viewport.h" #include "window.h" #include "windowcontainer.h" @@ -40,8 +39,8 @@ #include "../resources/image.h" #include "../resources/imageset.h" -#include "../resources/resourcemanager.h" #include "../resources/imageloader.h" +#include "../resources/resourcemanager.h" // Guichan stuff Gui *gui; @@ -55,6 +54,13 @@ gcn::Font *hitYellowFont; // Font used to display speech and player names gcn::Font *speechFont; +// Font for displaying NPC names +gcn::Font *npcNameFont; +// Font for displaying mob names +gcn::Font *mobNameFont; +// Font for displaying GM names +gcn::Font *gmNameFont; + class GuiConfigListener : public ConfigListener { public: @@ -139,14 +145,54 @@ Gui::Gui(Graphics *graphics): logger->error("Unable to load rpgfont_wider.png!"); } + // Set npc name font + try { + npcNameFont = new gcn::ImageFont("graphics/gui/rpgfont_wider-blue.png", + " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789.,!?-+/():;%&`'*#=[]\"<>{}^~|_@$\\" + "áÁéÉíÍóÓúÚç륣¢¡¿àãõêñÑöüäÖÜÄßøèÈåÅ" + ); + } + catch (gcn::Exception e) + { + logger->error("Unable to load rpgfont_wider-blue.png!"); + } + + // Set monster name font + try { + mobNameFont = new gcn::ImageFont("graphics/gui/rpgfont_wider-orange.png", + " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789.,!?-+/():;%&`'*#=[]\"<>{}^~|_@$\\" + "áÁéÉíÍóÓúÚç륣¢¡¿àãõêñÑöüäÖÜÄßøèÈåÅ" + ); + } + catch (gcn::Exception e) + { + logger->error("Unable to load rpgfont_wider-orange.png!"); + } + + // Set GM name font + try { + gmNameFont = new gcn::ImageFont("graphics/gui/rpgfont_wider-green.png", + " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789.,!?-+/():;%&`'*#=[]\"<>{}^~|_@$\\" + "áÁéÉíÍóÓúÚç륣¢¡¿àãõêñÑöüäÖÜÄßøèÈåÅ" + ); + } + catch (gcn::Exception e) + { + logger->error("Unable to load rpgfont_wider-green.png!"); + } + + gcn::Widget::setGlobalFont(mGuiFont); // Load hits' colourful fonts try { hitRedFont = new gcn::ImageFont("graphics/gui/hits_red.png", - "0123456789"); + "0123456789crit! "); hitBlueFont = new gcn::ImageFont("graphics/gui/hits_blue.png", - "0123456789"); + "0123456789crit! "); hitYellowFont = new gcn::ImageFont("graphics/gui/hits_yellow.png", "0123456789misxp "); } diff --git a/src/gui/gui.h b/src/gui/gui.h index 15d5d99c..f56b1dbf 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -26,8 +26,8 @@ #include "../guichanfwd.h" -class GuiConfigListener; class Graphics; +class GuiConfigListener; class ImageSet; class Viewport; @@ -128,4 +128,12 @@ extern gcn::Font *hitYellowFont; */ extern gcn::Font *speechFont; +/** + * being name fonts + */ +extern gcn::Font *npcNameFont; +extern gcn::Font *mobNameFont; +extern gcn::Font *gmNameFont; + + #endif diff --git a/src/gui/hbox.h b/src/gui/hbox.h index 4b241383..da70a53c 100644 --- a/src/gui/hbox.h +++ b/src/gui/hbox.h @@ -24,6 +24,8 @@ #include "box.h" +#include "../guichanfwd.h" + class HBox : public Box { public: diff --git a/src/gui/help.cpp b/src/gui/help.cpp index 290679b9..19413a08 100644 --- a/src/gui/help.cpp +++ b/src/gui/help.cpp @@ -19,10 +19,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "help.h" - #include "button.h" #include "browserbox.h" +#include "help.h" #include "scrollarea.h" #include "../resources/resourcemanager.h" diff --git a/src/gui/help.h b/src/gui/help.h index 053df723..bd200ccf 100644 --- a/src/gui/help.h +++ b/src/gui/help.h @@ -24,8 +24,8 @@ #include <guichan/actionlistener.hpp> -#include "window.h" #include "linkhandler.h" +#include "window.h" #include "../guichanfwd.h" diff --git a/src/gui/inttextbox.cpp b/src/gui/inttextbox.cpp index 4825fbf5..858a3fcb 100644 --- a/src/gui/inttextbox.cpp +++ b/src/gui/inttextbox.cpp @@ -19,10 +19,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "inttextbox.h" - #include <guichan/key.hpp> +#include "inttextbox.h" + #include "../utils/tostring.h" IntTextBox::IntTextBox(int i): diff --git a/src/gui/inttextbox.h b/src/gui/inttextbox.h index 8fc8e404..922ef4c5 100644 --- a/src/gui/inttextbox.h +++ b/src/gui/inttextbox.h @@ -24,6 +24,8 @@ #include "textbox.h" +#include "../guichanfwd.h" + /** * TextBox which only accepts numbers as input. */ diff --git a/src/gui/inventorywindow.cpp b/src/gui/inventorywindow.cpp index f38c118e..424fca59 100644 --- a/src/gui/inventorywindow.cpp +++ b/src/gui/inventorywindow.cpp @@ -19,15 +19,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "inventorywindow.h" - #include <string> #include <guichan/mouseinput.hpp> -#include <guichan/widgets/label.hpp> #include "button.h" #include "gui.h" +#include "inventorywindow.h" #include "item_amount.h" #include "itemcontainer.h" #include "scrollarea.h" @@ -48,29 +46,38 @@ InventoryWindow::InventoryWindow(): setResizable(true); setCloseButton(true); setMinWidth(240); - setMinHeight(172); // If you adjust these defaults, don't forget to adjust the trade window's. setDefaultSize(115, 25, 322, 172); mUseButton = new Button("Use", "use", this); mDropButton = new Button("Drop", "drop", this); - mItems = new ItemContainer(player_node->getInventory()); + mItems = new ItemContainer(player_node->getInventory(), 2); mItems->addSelectionListener(this); mInvenScroll = new ScrollArea(mItems); mInvenScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); - mItemNameLabel = new gcn::Label("Name:"); - mItemDescriptionLabel = new gcn::Label("Description:"); - mItemEffectLabel = new gcn::Label("Effect:"); - mWeightLabel = new gcn::Label("Weight:"); + mTotalWeight = toString(player_node->mTotalWeight); + mMaxWeight = toString(player_node->mMaxWeight); + + mItemName = "Name:"; + mItemNameLabel = new TextBox(); + mItemDescription = "Description:"; + mItemDescriptionLabel = new TextBox(); + mItemEffect = "Effect:"; + mItemEffectLabel = new TextBox(); + mWeight = "Total Weight: " + mTotalWeight + " g - " + + "Maximum Weight: " + mMaxWeight + " g"; + mWeightLabel = new TextBox(); mWeightLabel->setPosition(8, 8); - mInvenScroll->setPosition(8, - mWeightLabel->getY() + mWeightLabel->getHeight() + 5); - mInvenSlotLabel = new gcn::Label("Slots used:"); - mInvenSlotLabel->setPosition(mWeightLabel->getX() - + mWeightLabel->getWidth() + 100, 8); + mSlots = "Slots: " + + toString(player_node->getInventory()->getNumberOfSlotsUsed()) + + "/" + toString(player_node->getInventory()->getInventorySize()); + mSlotsLabel = new TextBox(); + mItemEffectLabel = new TextBox(); + + draw(); add(mUseButton); add(mDropButton); @@ -79,7 +86,7 @@ InventoryWindow::InventoryWindow(): add(mItemDescriptionLabel); add(mItemEffectLabel); add(mWeightLabel); - add(mInvenSlotLabel); + add(mSlotsLabel); mUseButton->setSize(60, mUseButton->getHeight()); @@ -94,16 +101,22 @@ void InventoryWindow::logic() // redesign of InventoryWindow and ItemContainer probably. updateButtons(); - // Update weight information - mWeightLabel->setCaption( - "Weight: " + toString(player_node->mTotalWeight) + - "/" + toString(player_node->mMaxWeight)); - - // Update number of items in inventory - mInvenSlotLabel->setCaption( - "Slots used: " - + toString(player_node->getInventory()->getNumberOfSlotsUsed()) - + "/" + toString(player_node->getInventory()->getInventorySize())); + if ((mMaxWeight != toString(player_node->mMaxWeight)) || + mTotalWeight != toString(player_node->mTotalWeight)) + { + mTotalWeight = toString(player_node->mTotalWeight); + mMaxWeight = toString(player_node->mMaxWeight); + + // Adjust widgets + mWeight = "Total Weight: " + mTotalWeight + " g - " + + "Maximum Weight: " + mMaxWeight + " g"; + + mSlots = "Slots: " + + toString(player_node->getInventory()->getNumberOfSlotsUsed()) + + "/" + toString(player_node->getInventory()->getInventorySize()); + + draw(); + } } void InventoryWindow::action(const gcn::ActionEvent &event) @@ -145,21 +158,25 @@ void InventoryWindow::valueChanged(const gcn::SelectionEvent &event) // Update name, effect and description if (!item) { - mItemNameLabel->setCaption("Name:"); - mItemEffectLabel->setCaption("Effect:"); - mItemDescriptionLabel->setCaption("Description:"); + mItemName = "Name:"; + mItemNameLabel->setTextWrapped(mItemName); + mItemEffect = "Effect:"; + mItemEffectLabel->setTextWrapped(mItemEffect); + mItemDescription = "Description:"; + mItemDescriptionLabel->setTextWrapped(mItemDescription); } else { const ItemInfo& itemInfo = item->getInfo(); - std::string SomeText; - SomeText = "Name: " + itemInfo.getName(); - mItemNameLabel->setCaption(SomeText); - SomeText = "Effect: " + itemInfo.getEffect(); - mItemEffectLabel->setCaption(SomeText); - SomeText = "Description: " + itemInfo.getDescription(); - mItemDescriptionLabel->setCaption(SomeText); + mItemName = "Name: " + itemInfo.getName(); + mItemNameLabel->setTextWrapped(mItemName); + mItemEffect = "Effect: " + itemInfo.getEffect(); + mItemEffectLabel->setTextWrapped(mItemEffect); + mItemDescription = "Description: " + itemInfo.getDescription(); + mItemDescriptionLabel->setTextWrapped(mItemDescription); } + + draw(); } void InventoryWindow::mouseClicked(gcn::MouseEvent &event) @@ -181,37 +198,60 @@ void InventoryWindow::mouseClicked(gcn::MouseEvent &event) } } -void InventoryWindow::widgetResized(const gcn::Event &event) +void InventoryWindow::draw() { - Window::widgetResized(event); - const gcn::Rectangle &area = getChildrenArea(); const int width = area.width; const int height = area.height; - // Adjust widgets + // Update weight information + mWeightLabel->setTextWrapped(mWeight); + mWeightLabel->setMinWidth(width - 16); + mUseButton->setPosition(8, height - 8 - mUseButton->getHeight()); mDropButton->setPosition(8 + mUseButton->getWidth() + 5, mUseButton->getY()); + mItemNameLabel->setMinWidth(width - 16); + mItemNameLabel->setTextWrapped(mItemName); mItemNameLabel->setDimension(gcn::Rectangle(8, - mUseButton->getY() - 5 - mItemNameLabel->getHeight(), + mUseButton->getY() - 5 - (mItemNameLabel->getNumberOfRows()*15), width - 16, - mItemNameLabel->getHeight())); + (mItemNameLabel->getNumberOfRows()*15))); + mItemEffectLabel->setMinWidth(width - 16); + mItemEffectLabel->setTextWrapped(mItemEffect); mItemEffectLabel->setDimension(gcn::Rectangle(8, - mItemNameLabel->getY() - 5 - mItemEffectLabel->getHeight(), + mItemNameLabel->getY() - 5 - (mItemEffectLabel->getNumberOfRows()*15), width - 16, - mItemEffectLabel->getHeight())); + (mItemEffectLabel->getNumberOfRows()*15))); + mItemDescriptionLabel->setMinWidth(width - 16); + mItemDescriptionLabel->setTextWrapped(mItemDescription); mItemDescriptionLabel->setDimension(gcn::Rectangle(8, - mItemEffectLabel->getY() - 5 - mItemDescriptionLabel->getHeight(), + mItemEffectLabel->getY() - 5 - (mItemDescriptionLabel->getNumberOfRows()*15), width - 16, - mItemDescriptionLabel->getHeight())); + (mItemDescriptionLabel->getNumberOfRows()*15))); + mSlotsLabel->setMinWidth(width - 16); + mSlotsLabel->setTextWrapped(mSlots); + mSlotsLabel->setDimension(gcn::Rectangle(8, + mItemDescriptionLabel->getY() - 5 - (mSlotsLabel->getNumberOfRows()*15), + width - 16, + (mSlotsLabel->getNumberOfRows()*15))); mInvenScroll->setSize(width - 16, - mItemDescriptionLabel->getY() - mWeightLabel->getHeight() - 18); + mSlotsLabel->getY() - (mWeightLabel->getNumberOfRows()*15) - 18); + mInvenScroll->setPosition(8, (mWeightLabel->getNumberOfRows()*15) + 10); + + setMinHeight(130 + (mSlotsLabel->getNumberOfRows()*15) + + (mWeightLabel->getNumberOfRows()*15) + + (mItemDescriptionLabel->getNumberOfRows()*15) + + (mItemEffectLabel->getNumberOfRows()*15) + + (mItemNameLabel->getNumberOfRows()*15)); +} - mWeightLabel->setWidth(width - 16); - mInvenSlotLabel->setWidth(width - 16); +void InventoryWindow::widgetResized(const gcn::Event &event) +{ + Window::widgetResized(event); + draw(); } void InventoryWindow::updateButtons() diff --git a/src/gui/inventorywindow.h b/src/gui/inventorywindow.h index b1e3ede3..2e589471 100644 --- a/src/gui/inventorywindow.h +++ b/src/gui/inventorywindow.h @@ -25,6 +25,7 @@ #include <guichan/actionlistener.hpp> #include <guichan/selectionlistener.hpp> +#include "textbox.h" #include "window.h" #include "../guichanfwd.h" @@ -69,6 +70,11 @@ class InventoryWindow : public Window, gcn::ActionListener, void mouseClicked(gcn::MouseEvent &event); /** + * Updates window drawing. + */ + void draw(); + + /** * Called whenever the widget changes size. */ void widgetResized(const gcn::Event &event); @@ -78,13 +84,20 @@ class InventoryWindow : public Window, gcn::ActionListener, ItemContainer *mItems; + std::string mItemName; + std::string mItemDescription; + std::string mItemEffect; + std::string mWeight; + std::string mTotalWeight; + std::string mMaxWeight; + std::string mSlots; gcn::Button *mUseButton, *mDropButton; gcn::ScrollArea *mInvenScroll; - gcn::Label *mItemNameLabel; - gcn::Label *mItemDescriptionLabel; - gcn::Label *mItemEffectLabel; - gcn::Label *mWeightLabel; - gcn::Label *mInvenSlotLabel; + TextBox *mItemNameLabel; + TextBox *mItemDescriptionLabel; + TextBox *mItemEffectLabel; + TextBox *mWeightLabel; + TextBox *mSlotsLabel; }; extern InventoryWindow *inventoryWindow; diff --git a/src/gui/item_amount.cpp b/src/gui/item_amount.cpp index 2e8941e8..4ce8ac6c 100644 --- a/src/gui/item_amount.cpp +++ b/src/gui/item_amount.cpp @@ -19,10 +19,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "item_amount.h" - #include "button.h" #include "inttextbox.h" +#include "item_amount.h" #include "slider.h" #include "trade.h" diff --git a/src/gui/itemcontainer.cpp b/src/gui/itemcontainer.cpp index 9115b1fb..9e17f3da 100644 --- a/src/gui/itemcontainer.cpp +++ b/src/gui/itemcontainer.cpp @@ -28,6 +28,7 @@ #include "../inventory.h" #include "../item.h" #include "../itemshortcut.h" +#include "../localplayer.h" #include "../log.h" #include "../resources/image.h" @@ -41,10 +42,11 @@ const int ItemContainer::gridHeight = 42; // item icon height + 10 static const int NO_ITEM = -1; -ItemContainer::ItemContainer(Inventory *inventory): +ItemContainer::ItemContainer(Inventory *inventory, int offset): mInventory(inventory), mSelectedItemIndex(NO_ITEM), - mLastSelectedItemId(NO_ITEM) + mLastSelectedItemId(NO_ITEM), + mOffset(offset) { ResourceManager *resman = ResourceManager::getInstance(); @@ -86,10 +88,11 @@ void ItemContainer::draw(gcn::Graphics *graphics) } /* - * eAthena seems to start inventory from the 3rd slot. Still a mystery to - * us why, make sure not to copy this oddity to our own server. + * mOffset is used to compensate for some weirdness that eAthena inherited from + * Ragnarok Online. Inventory slots and cart slots are +2 from their actual index, + * while storage slots are +1. */ - for (int i = 2; i < INVENTORY_SIZE; i++) + for (int i = mOffset; i < mInventory->getSize(); i++) { Item *item = mInventory->getItem(i); @@ -178,18 +181,19 @@ void ItemContainer::refindSelectedItem() mLastSelectedItemId = mSelectedItemIndex = NO_ITEM; } - void ItemContainer::setSelectedItemIndex(int index) { int newSelectedItemIndex; - // mMaxItems is broken because of eAthena's odd inventory layout and the client's refusal - // to handle it properly, so we work around the issue right here. - if (index < 0 || index > mMaxItems + 1 || mInventory->getItem(index) == NULL) + /* + * mOffset is used to compensate for some weirdness that eAthena inherited from + * Ragnarok Online. Inventory slots and cart slots are +2 from their actual index, + * while storage slots are +1. + */ + if (index < 0 || index > mMaxItems + mOffset || mInventory->getItem(index) == NULL) newSelectedItemIndex = NO_ITEM; else newSelectedItemIndex = index; - if (mSelectedItemIndex != newSelectedItemIndex) { mSelectedItemIndex = newSelectedItemIndex; @@ -224,7 +228,7 @@ void ItemContainer::mousePressed(gcn::MouseEvent &event) int columns = getWidth() / gridWidth; int mx = event.getX(); int my = event.getY(); - int index = mx / gridWidth + ((my / gridHeight) * columns) + 2; + int index = mx / gridWidth + ((my / gridHeight) * columns) + mOffset; itemShortcut->setItemSelected(-1); setSelectedItemIndex(index); diff --git a/src/gui/itemcontainer.h b/src/gui/itemcontainer.h index 34545df8..8927b001 100644 --- a/src/gui/itemcontainer.h +++ b/src/gui/itemcontainer.h @@ -22,11 +22,13 @@ #ifndef _TMW_ITEMCONTAINER_H__ #define _TMW_ITEMCONTAINER_H__ +#include <list> + #include <guichan/mouselistener.hpp> #include <guichan/widget.hpp> #include <guichan/widgetlistener.hpp> -#include <list> +#include "../guichanfwd.h" class Image; class Inventory; @@ -41,14 +43,15 @@ namespace gcn { * * \ingroup GUI */ -class ItemContainer : public gcn::Widget, public gcn::MouseListener, - public gcn::WidgetListener +class ItemContainer : public gcn::Widget, + public gcn::MouseListener, + public gcn::WidgetListener { public: /** * Constructor. Initializes the graphic. */ - ItemContainer(Inventory *inventory); + ItemContainer(Inventory *inventory, int offset); /** * Destructor. @@ -105,6 +108,7 @@ class ItemContainer : public gcn::Widget, public gcn::MouseListener, private: /** + * Sets the currently selected item. Invalid (e.g., negative) indices set `no item'. */ void setSelectedItemIndex(int index); @@ -127,9 +131,10 @@ class ItemContainer : public gcn::Widget, public gcn::MouseListener, Inventory *mInventory; Image *mSelImg; int mSelectedItemIndex; - int mLastSelectedItemId; // last selected item ID. If we lose the item, find again by ID. + int mLastSelectedItemId; // last selected item ID. If we lose the item, find again by ID. int mMaxItems; + int mOffset; std::list<gcn::SelectionListener*> mListeners; diff --git a/src/gui/itempopup.cpp b/src/gui/itempopup.cpp new file mode 100644 index 00000000..055cbe44 --- /dev/null +++ b/src/gui/itempopup.cpp @@ -0,0 +1,113 @@ +/* + * The Legend of Mazzeroth + * Copyright (C) 2008, The Legend of Mazzeroth Development Team + * + * This file is part of The Legend of Mazzeroth based on original code + * from The Mana World. + * + * The Legend of Mazzeroth is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Legend of Mazzeroth is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Legend of Mazzeroth; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <guichan/widgets/label.hpp> + +#include "gui.h" +#include "itempopup.h" + +#include "widgets/layout.h" + +#include "../resources/image.h" +#include "../resources/iteminfo.h" +#include "../resources/resourcemanager.h" +#include "../utils/gettext.h" +#include "../utils/strprintf.h" + +ItemPopup::ItemPopup() +{ + + setResizable(false); + setTitleBarHeight(0); + + // Item Name + mItemName = new gcn::Label("Label"); + mItemName->setFont(gui->getFont()); + mItemName->setPosition(2, 2); + mItemName->setWidth(getWidth() - 4); + + // Item Description + mItemDesc = new TextBox(); + mItemDesc->setEditable(false); + mItemDescScroll = new ScrollArea(mItemDesc); + + mItemDescScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mItemDescScroll->setVerticalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mItemDescScroll->setDimension(gcn::Rectangle(0, 0, 196, 14)); + mItemDescScroll->setOpaque(false); + mItemDescScroll->setPosition(2, 15); + + // Item Effect + mItemEffect = new TextBox(); + mItemEffect->setEditable(false); + mItemEffectScroll = new ScrollArea(mItemEffect); + + mItemEffectScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mItemEffectScroll->setVerticalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mItemEffectScroll->setDimension(gcn::Rectangle(0, 0, 196, 14)); + mItemEffectScroll->setOpaque(false); + mItemEffectScroll->setPosition(2, 35); + + add(mItemName); + add(mItemDescScroll); + add(mItemEffectScroll); + + setLocationRelativeTo(getParent()); + + // LEEOR / TODO: This causes an exception error. + //moveToBottom(getParent()); + + mItemDesc->setTextWrapped( "" ); + mItemEffect->setTextWrapped( "" ); +} + +void ItemPopup::setItem(Item *item) +{ + + ItemInfo const *info = item ? &item->getInfo() : NULL; + + mItemName->setCaption(info->getName()); + mItemDesc->setTextWrapped( info->getDescription() ); + mItemEffect->setTextWrapped( info->getEffect() ); + + int numRowsDesc = mItemDesc->getNumberOfRows(); + int numRowsEffect = mItemEffect->getNumberOfRows(); + + if(info->getEffect() == "") + { + setContentSize(200, (numRowsDesc * 14) + 30); + } else { + setContentSize(200, (numRowsDesc * 14) + (numRowsEffect*14) + 30); + } + + mItemDescScroll->setDimension(gcn::Rectangle(2, 0, 196, numRowsDesc * 14)); + + mItemEffectScroll->setDimension(gcn::Rectangle(2, 0, 196, numRowsEffect * 14)); + + mItemDescScroll->setPosition(2, 20); + mItemEffectScroll->setPosition(2, (numRowsDesc * 15) + 25); +} + +unsigned int ItemPopup::getNumRows() +{ + return mItemDesc->getNumberOfRows(), mItemEffect->getNumberOfRows(); +} diff --git a/src/gui/itempopup.h b/src/gui/itempopup.h new file mode 100644 index 00000000..0082ec2c --- /dev/null +++ b/src/gui/itempopup.h @@ -0,0 +1,51 @@ +/* +* + * The Legend of Mazzeroth + * Copyright (C) 2008, The Legend of Mazzeroth Development Team + * + * This file is part of The Legend of Mazzeroth based on original code + * from The Mana World. + * + * The Legend of Mazzeroth is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Legend of Mazzeroth is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Legend of Mazzeroth; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LOM_ITEMPOPUP_H__ +#define _LOM_ITEMPOPUP_H__ + +#include "scrollarea.h" +#include "textbox.h" +#include "window.h" + +#include "../item.h" + +class ItemPopup : public Window + { + public: + + ItemPopup(); + + void setItem(Item *item); + unsigned int getNumRows(); + + private: + gcn::Label *mItemName; + TextBox *mItemDesc; + TextBox *mItemEffect; + ScrollArea *mItemDescScroll; + ScrollArea *mItemEffectScroll; + + }; + +#endif diff --git a/src/gui/itemshortcutcontainer.cpp b/src/gui/itemshortcutcontainer.cpp index 2ea5d584..de5e1956 100644 --- a/src/gui/itemshortcutcontainer.cpp +++ b/src/gui/itemshortcutcontainer.cpp @@ -21,12 +21,12 @@ #include "itemshortcutcontainer.h" -#include "../localplayer.h" #include "../graphics.h" #include "../inventory.h" #include "../item.h" #include "../itemshortcut.h" #include "../keyboardconfig.h" +#include "../localplayer.h" #include "../resources/image.h" #include "../resources/resourcemanager.h" @@ -96,6 +96,8 @@ ItemShortcutContainer::draw(gcn::Graphics *graphics) player_node->getInventory()->findItem(itemShortcut->getItem(i)); if (item) { // Draw item icon. + const std::string label = + item->isEquipped() ? "Eq." : toString(item->getQuantity()); Image* image = item->getImage(); if (image) { const std::string label = diff --git a/src/gui/itemshortcutcontainer.h b/src/gui/itemshortcutcontainer.h index 76ca870c..a8daca0b 100644 --- a/src/gui/itemshortcutcontainer.h +++ b/src/gui/itemshortcutcontainer.h @@ -26,6 +26,8 @@ #include <guichan/widget.hpp> #include <guichan/widgetlistener.hpp> +#include "../guichanfwd.h" + class Image; class Item; diff --git a/src/gui/itemshortcutwindow.cpp b/src/gui/itemshortcutwindow.cpp index e7364411..5a4dfacd 100644 --- a/src/gui/itemshortcutwindow.cpp +++ b/src/gui/itemshortcutwindow.cpp @@ -19,9 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "itemshortcutwindow.h" - #include "itemshortcutcontainer.h" +#include "itemshortcutwindow.h" #include "scrollarea.h" static const int SCROLL_PADDING = 0; diff --git a/src/gui/itemshortcutwindow.h b/src/gui/itemshortcutwindow.h index 017df5ec..587f15c8 100644 --- a/src/gui/itemshortcutwindow.h +++ b/src/gui/itemshortcutwindow.h @@ -24,6 +24,8 @@ #include "window.h" +#include "../guichanfwd.h" + class ItemShortcutContainer; class ScrollArea; diff --git a/src/gui/listbox.cpp b/src/gui/listbox.cpp index 28bb82e9..4dca66a0 100644 --- a/src/gui/listbox.cpp +++ b/src/gui/listbox.cpp @@ -19,13 +19,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "listbox.h" - #include <guichan/font.hpp> #include <guichan/graphics.hpp> #include <guichan/listmodel.hpp> #include <guichan/mouseinput.hpp> +#include "listbox.h" + ListBox::ListBox(gcn::ListModel *listModel): gcn::ListBox(listModel) { diff --git a/src/gui/listbox.h b/src/gui/listbox.h index 1d627b3b..30eb4c79 100644 --- a/src/gui/listbox.h +++ b/src/gui/listbox.h @@ -24,6 +24,8 @@ #include <guichan/widgets/listbox.hpp> +#include "../guichanfwd.h" + class SelectionListener; /** diff --git a/src/gui/login.cpp b/src/gui/login.cpp index 06a5f9f0..fa47af32 100644 --- a/src/gui/login.cpp +++ b/src/gui/login.cpp @@ -19,80 +19,125 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "login.h" - #include <string> +#include <vector> #include <guichan/widgets/label.hpp> -#include "../main.h" -#include "../logindata.h" - #include "button.h" #include "checkbox.h" +#include "login.h" #include "ok_dialog.h" #include "passwordfield.h" #include "textfield.h" +#include "../main.h" +#include "../logindata.h" +#include "../configuration.h" + +#include "../utils/tostring.h" + +static const int MAX_SERVER_LIST_SIZE = 5; +static const int LOGIN_DIALOG_WIDTH = 220; +static const int LOGIN_DIALOG_HEIGHT = 140; +static const int FIELD_WIDTH = LOGIN_DIALOG_WIDTH - 70; + LoginDialog::LoginDialog(LoginData *loginData): Window("Login"), mLoginData(loginData) { gcn::Label *userLabel = new gcn::Label("Name:"); gcn::Label *passLabel = new gcn::Label("Password:"); gcn::Label *serverLabel = new gcn::Label("Server:"); + gcn::Label *portLabel = new gcn::Label("Port:"); + gcn::Label *dropdownLabel = new gcn::Label("Recent:"); + std::vector<std::string> dfltServer; + dfltServer.push_back("www.aethyra.org"); + dfltServer.push_back("www.aethyra.org"); + std::vector<std::string> dfltPort; + dfltPort.push_back("21001"); + dfltPort.push_back("22001"); + mServerList = new DropDownList("MostRecent00", dfltServer, dfltPort, + MAX_SERVER_LIST_SIZE); + mServerListBox = new gcn::ListBox(mServerList); + mServerScrollArea = new ScrollArea(); + mUserField = new TextField(mLoginData->username); mPassField = new PasswordField(mLoginData->password); - mServerField = new TextField(mLoginData->hostname); + mServerField = new TextField(mServerList->getServerAt(0)); + mPortField = new TextField(mServerList->getPortAt(0)); + mServerDropDown = new DropDown(mServerList, + mServerScrollArea, + mServerListBox); + mKeepCheck = new CheckBox("Keep", mLoginData->remember); mOkButton = new Button("OK", "ok", this); mCancelButton = new Button("Cancel", "cancel", this); mRegisterButton = new Button("Register", "register", this); - const int width = 220; - const int height = 100; - - setContentSize(width, height); - - userLabel->setPosition(5, 5); - passLabel->setPosition(5, 14 + userLabel->getHeight()); - serverLabel->setPosition( - 5, 23 + userLabel->getHeight() + passLabel->getHeight()); - mUserField->setPosition(65, 5); - mPassField->setPosition(65, 14 + userLabel->getHeight()); - mServerField->setPosition( - 65, 23 + userLabel->getHeight() + passLabel->getHeight()); - mUserField->setWidth(width - 70); - mPassField->setWidth(width - 70); - mServerField->setWidth(width - 70); - mKeepCheck->setPosition(4, 77); + setContentSize(LOGIN_DIALOG_WIDTH, LOGIN_DIALOG_HEIGHT); + + const int USER_TOP = 5; + userLabel->setPosition(5, USER_TOP); + mUserField->setPosition(65, USER_TOP); + mUserField->setWidth(FIELD_WIDTH); + + const int PASS_TOP = 9 + USER_TOP + userLabel->getHeight(); + passLabel->setPosition(5, PASS_TOP); + mPassField->setPosition(65, PASS_TOP); + mPassField->setWidth(FIELD_WIDTH); + + const int SERVER_TOP = 9 + PASS_TOP + passLabel->getHeight(); + serverLabel->setPosition(5, SERVER_TOP); + mServerField->setPosition(65, SERVER_TOP); + mServerField->setWidth(FIELD_WIDTH); + + const int PORT_TOP = 9 + SERVER_TOP + serverLabel->getHeight(); + portLabel->setPosition(5, PORT_TOP); + mPortField->setPosition(65, PORT_TOP); + mPortField->setWidth(FIELD_WIDTH); + + const int DROP_DOWN_TOP = 9 + PORT_TOP + serverLabel->getHeight(); + dropdownLabel->setPosition(5, DROP_DOWN_TOP); + mServerDropDown->setPosition(65, DROP_DOWN_TOP); + mServerDropDown->setWidth(FIELD_WIDTH); + + const int REST_TOP = LOGIN_DIALOG_HEIGHT - mCancelButton->getHeight() - 5; + + mKeepCheck->setPosition(4, REST_TOP); mCancelButton->setPosition( - width - mCancelButton->getWidth() - 5, - height - mCancelButton->getHeight() - 5); + LOGIN_DIALOG_WIDTH - mCancelButton->getWidth() - 5, REST_TOP); mOkButton->setPosition( - mCancelButton->getX() - mOkButton->getWidth() - 5, - height - mOkButton->getHeight() - 5); + mCancelButton->getX() - mOkButton->getWidth() - 5, REST_TOP); mRegisterButton->setPosition( - mKeepCheck->getX() + mKeepCheck->getWidth() + 10, - height - mRegisterButton->getHeight() - 5); + mKeepCheck->getX() + mKeepCheck->getWidth() + 10, REST_TOP); mUserField->setActionEventId("ok"); mPassField->setActionEventId("ok"); mServerField->setActionEventId("ok"); + mServerDropDown->setActionEventId("changeSelection"); mUserField->addKeyListener(this); mPassField->addKeyListener(this); mServerField->addKeyListener(this); + mPortField->addKeyListener(this); + mServerDropDown->addKeyListener(this); mUserField->addActionListener(this); mPassField->addActionListener(this); mServerField->addActionListener(this); + mPortField->addActionListener(this); + mServerDropDown->addActionListener(this); mKeepCheck->addActionListener(this); add(userLabel); add(passLabel); add(serverLabel); + add(portLabel); + add(dropdownLabel); add(mUserField); add(mPassField); add(mServerField); + add(mPortField); + add(mServerDropDown); add(mKeepCheck); add(mOkButton); add(mCancelButton); @@ -112,6 +157,9 @@ LoginDialog::LoginDialog(LoginData *loginData): LoginDialog::~LoginDialog() { + delete mServerList; + delete mServerListBox; + delete mServerScrollArea; } void @@ -120,6 +168,7 @@ LoginDialog::action(const gcn::ActionEvent &event) if (event.getId() == "ok" && canSubmit()) { mLoginData->hostname = mServerField->getText(); + mLoginData->port = getUShort(mPortField->getText()); mLoginData->username = mUserField->getText(); mLoginData->password = mPassField->getText(); mLoginData->remember = mKeepCheck->isSelected(); @@ -127,9 +176,15 @@ LoginDialog::action(const gcn::ActionEvent &event) mOkButton->setEnabled(false); mRegisterButton->setEnabled(false); - + mServerList->save(mServerField->getText(), mPortField->getText()); state = ACCOUNT_STATE; } + else if (event.getId() == "changeSelection") + { + int selected = mServerListBox->getSelected(); + mServerField->setText(mServerList->getServerAt(selected)); + mPortField->setText(mServerList->getPortAt(selected)); + } else if (event.getId() == "cancel") { state = EXIT_STATE; @@ -138,6 +193,14 @@ LoginDialog::action(const gcn::ActionEvent &event) { // Transfer these fields on to the register dialog mLoginData->hostname = mServerField->getText(); + if (isUShort(mPortField->getText())) + { + mLoginData->port = getUShort(mPortField->getText()); + } + else + { + mLoginData->port = 6901; + } mLoginData->username = mUserField->getText(); mLoginData->password = mPassField->getText(); @@ -157,5 +220,147 @@ LoginDialog::canSubmit() return !mUserField->getText().empty() && !mPassField->getText().empty() && !mServerField->getText().empty() && + isUShort(mPortField->getText()) && state == LOGIN_STATE; } + +bool +LoginDialog::isUShort(const std::string &str) +{ + if (str == "") + { + return false; + } + unsigned long l = 0; + for (std::string::const_iterator strPtr = str.begin(), strEnd = str.end(); + strPtr != strEnd; ++strPtr) + { + if (*strPtr < '0' || *strPtr > '9') + { + return false; + } + l = l * 10 + (*strPtr - '0'); // *strPtr - '0' will never be negative + if (l > 65535) + { + return false; + } + } + return true; +} + +unsigned short +LoginDialog::getUShort(const std::string &str) +{ + unsigned long l = 0; + for (std::string::const_iterator strPtr = str.begin(), strEnd = str.end(); + strPtr != strEnd; ++strPtr) + { + l = l * 10 + (*strPtr - '0'); + } + return static_cast<unsigned short>(l); +} + +/** + * LoginDialog::DropDownList + */ + +void +LoginDialog::DropDownList::saveEntry(const std::string &server, + const std::string &port, int &saved) +{ + if (saved < MAX_SERVER_LIST_SIZE && server != "") + { + config.setValue(mConfigPrefix + "Server" + toString(saved), server); + config.setValue(mConfigPrefix + "Port" + toString(saved), port); + ++saved; + } +} + +LoginDialog::DropDownList::DropDownList(std::string prefix, + std::vector<std::string> dflt, + std::vector<std::string> dfltPort, + int maxEntries) : + mConfigPrefix(prefix), + mMaxEntries(maxEntries) +{ + for (int i = 0; i < maxEntries; ++i) + { + std::string server = config.getValue(mConfigPrefix + "Server" + + toString(i), ""); + if (server == "") // Just in case had original config entries + { + server = config.getValue(mConfigPrefix + "ServerList" + + toString(i), ""); + } + std::string port = config.getValue(mConfigPrefix + "Port" + + toString(i), dfltPort.front()); + + if (server != "") + { + mServers.push_back(server); + mPorts.push_back(port); + } + } + if (mServers.size() == 0) + { + mServers.assign(dflt.begin(), dflt.end()); + mPorts.assign(dfltPort.begin(), dfltPort.end()); + } +} + +void +LoginDialog::DropDownList::save(const std::string &server, + const std::string &port) +{ + int position = 0; + saveEntry(server, port, position); + for (std::vector<std::string>::const_iterator sPtr = mServers.begin(), + sEnd = mServers.end(), + pPtr = mPorts.begin(), + pEnd = mPorts.end(); + sPtr != sEnd && pPtr != pEnd; + ++sPtr, ++pPtr) + { + if (*sPtr != server || *pPtr != port) + { + saveEntry(*sPtr, *pPtr, position); + } + } +} + +int +LoginDialog::DropDownList::getNumberOfElements() +{ + return mServers.size(); +} + +std::string +LoginDialog::DropDownList::getElementAt(int i) +{ + if (i < 0 || i >= getNumberOfElements()) + { + return ""; + } + return getServerAt(i) + ":" + getPortAt(i); +} + +std::string +LoginDialog::DropDownList::getServerAt(int i) +{ + if (i < 0 || i >= getNumberOfElements()) + { + return ""; + } + return mServers.at(i); +} + + +std::string +LoginDialog::DropDownList::getPortAt(int i) +{ + if (i < 0 || i >= getNumberOfElements()) + { + return ""; + } + return mPorts.at(i); +} diff --git a/src/gui/login.h b/src/gui/login.h index 4760817c..3b911424 100644 --- a/src/gui/login.h +++ b/src/gui/login.h @@ -23,10 +23,17 @@ #define _TMW_LOGIN_H #include <iosfwd> +#include <string> +#include <vector> + #include <guichan/actionlistener.hpp> #include <guichan/keylistener.hpp> +#include "scrollarea.h" #include "window.h" + +#include "widgets/dropdown.h" + #include "../guichanfwd.h" class LoginData; @@ -70,15 +77,66 @@ class LoginDialog : public Window, public gcn::ActionListener, bool canSubmit(); + /** + * Function to decide whether string is an unsigned short or not + * + * @param str the string to parse + * + * @return true is str is an unsigned short, false otherwise + */ + static bool + isUShort(const std::string &str); + + /** + * Converts string to an unsigned short (undefined if invalid) + * + * @param str the string to parse + * + * @return the value str represents + */ + static unsigned short + getUShort(const std::string &str); + + DropDown *mServerDropDown; gcn::TextField *mUserField; gcn::TextField *mPassField; gcn::TextField *mServerField; + gcn::TextField *mPortField; gcn::CheckBox *mKeepCheck; gcn::Button *mOkButton; gcn::Button *mCancelButton; gcn::Button *mRegisterButton; LoginData *mLoginData; + + /** + * Helper class to keep a list of all the recent entries for the + * dropdown + */ + class DropDownList : public gcn::ListModel + { + private: + std::vector<std::string> mServers; + std::vector<std::string> mPorts; + std::string mConfigPrefix; + int mMaxEntries; + void saveEntry(const std::string &server, + const std::string &port, int &saved); + public: + DropDownList(std::string prefix, + std::vector<std::string> dfltServer, + std::vector<std::string> dfltPort, + int maxEntries); + void save(const std::string &server, const std::string &port); + int getNumberOfElements(); + std::string getElementAt(int i); + std::string getServerAt(int i); + std::string getPortAt(int i); + }; + DropDownList *mServerList; + gcn::ListBox *mServerListBox; + ScrollArea *mServerScrollArea; + }; #endif diff --git a/src/gui/menuwindow.cpp b/src/gui/menuwindow.cpp index 3c3e4ab8..79281631 100644 --- a/src/gui/menuwindow.cpp +++ b/src/gui/menuwindow.cpp @@ -19,21 +19,21 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "menuwindow.h" - #include <string> #include <guichan/actionlistener.hpp> #include "button.h" +#include "menuwindow.h" #include "windowcontainer.h" -extern Window *setupWindow; -extern Window *inventoryWindow; +extern Window *chatWindow; extern Window *equipmentWindow; +extern Window *inventoryWindow; +extern Window *itemShortcutWindow; +extern Window *setupWindow; extern Window *skillDialog; extern Window *statusWindow; -extern Window *itemShortcutWindow; namespace { struct MenuWindowListener : public gcn::ActionListener @@ -56,6 +56,7 @@ MenuWindow::MenuWindow(): // Buttons const char *buttonNames[] = { + "Chat", "Status", "Equipment", "Inventory", @@ -89,7 +90,11 @@ void MenuWindowListener::action(const gcn::ActionEvent &event) { Window *window = NULL; - if (event.getId() == "Status") + if (event.getId() == "Chat") + { + window = chatWindow; + } + else if (event.getId() == "Status") { window = statusWindow; } diff --git a/src/gui/minimap.cpp b/src/gui/minimap.cpp index f07cb417..8339e478 100644 --- a/src/gui/minimap.cpp +++ b/src/gui/minimap.cpp @@ -30,11 +30,11 @@ Minimap::Minimap(): Window("Map"), - mMapImage(NULL) + mMapImage(NULL), + mProportion(0.5) { + setCloseButton(true); setWindowName("MiniMap"); - setDefaultSize(5, 25, 100, 100); - loadWindowState(); } Minimap::~Minimap() @@ -51,7 +51,21 @@ void Minimap::setMapImage(Image *img) mMapImage = img; if (mMapImage) + { + int offsetX = getPadding(); + int offsetY = getTitleBarHeight(); mMapImage->setAlpha(0.7); + setDefaultSize(offsetX, offsetY, + mMapImage->getWidth() < (100 + offsetX) ? + mMapImage->getWidth() : (100 + offsetX), + mMapImage->getHeight() < (100 + offsetY) ? + mMapImage->getHeight() : (100 + offsetY)); + loadWindowState(); + } + else + { + setVisible(false); + } } void Minimap::draw(gcn::Graphics *graphics) @@ -68,9 +82,10 @@ void Minimap::draw(gcn::Graphics *graphics) if (mMapImage->getWidth() > a.width || mMapImage->getHeight() > a.height) { - mapOriginX += (a.width - player_node->mX) / 2; - mapOriginY += (a.height - player_node->mY) / 2; + mapOriginX += ((a.width) / 2) - (player_node->mX * mProportion); + mapOriginY += ((a.height) / 2) - (player_node->mY * mProportion); } + static_cast<Graphics*>(graphics)-> drawImage(mMapImage, mapOriginX, mapOriginY); } @@ -106,10 +121,11 @@ void Minimap::draw(gcn::Graphics *graphics) continue; } - const int offset = (dotSize - 1) / 2; + const int offset = (int) ((dotSize - 1) * mProportion); + graphics->fillRectangle(gcn::Rectangle( - being->mX / 2 + mapOriginX - offset, - being->mY / 2 + mapOriginY - offset, + (int) (being->mX * mProportion) + mapOriginX - offset, + (int) (being->mY * mProportion) + mapOriginY - offset, dotSize, dotSize)); } } diff --git a/src/gui/minimap.h b/src/gui/minimap.h index f91dc22d..7897ebdb 100644 --- a/src/gui/minimap.h +++ b/src/gui/minimap.h @@ -24,6 +24,8 @@ #include "window.h" +#include "../guichanfwd.h" + class Image; /** @@ -50,12 +52,18 @@ class Minimap : public Window void setMapImage(Image *img); /** + * Sets the map proportion (1 means 1 tile to one pixel, .5 means 2 tiles to 1 pixel, etc.) + */ + void setProportion(float proportion) { mProportion = proportion; } + + /** * Draws the minimap. */ void draw(gcn::Graphics *graphics); private: Image *mMapImage; + float mProportion; }; extern Minimap *minimap; diff --git a/src/gui/ministatus.cpp b/src/gui/ministatus.cpp index baae14a7..59dca0e1 100644 --- a/src/gui/ministatus.cpp +++ b/src/gui/ministatus.cpp @@ -19,16 +19,15 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "ministatus.h" - #include <guichan/widgets/label.hpp> #include "gui.h" +#include "ministatus.h" #include "progressbar.h" -#include "../localplayer.h" #include "../configuration.h" #include "../graphics.h" +#include "../localplayer.h" #include "../utils/tostring.h" @@ -97,11 +96,6 @@ void MiniStatusWindow::update() mHpBar->setProgress((float) player_node->mHp / player_node->mMaxHp); mMpBar->setProgress((float) player_node->mMp / player_node->mMaxMp); - if (player_node->MATK <= 0) - mMpBar->setColor(100, 100, 100); // grey, to indicate that we lack magic - else - mMpBar->setColor(26, 102, 230); // blue, to indicate that we have magic - mXpBar->setProgress( (float) player_node->getXp() / player_node->mXpForNextLevel); diff --git a/src/gui/npc_text.cpp b/src/gui/npc_text.cpp index 34c9cce1..b53de828 100644 --- a/src/gui/npc_text.cpp +++ b/src/gui/npc_text.cpp @@ -19,12 +19,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "npc_text.h" - #include <string> -#include "scrollarea.h" -#include "button.h" +#include "npc_text.h" #include "textbox.h" #include "../npc.h" @@ -60,7 +57,11 @@ NpcTextDialog::NpcTextDialog(): void NpcTextDialog::setText(const std::string &text) { + const gcn::Rectangle &area = getChildrenArea(); + const int width = area.width; + mText = text; + mTextBox->setMinWidth(width - 30); mTextBox->setTextWrapped(mText); } @@ -77,6 +78,9 @@ void NpcTextDialog::widgetResized(const gcn::Event &event) const int width = area.width; const int height = area.height; + mTextBox->setMinWidth(width - 30); + mTextBox->setTextWrapped(mText); + scrollArea->setDimension(gcn::Rectangle( 5, 5, width - 10, height - 15 - okButton->getHeight())); okButton->setPosition( diff --git a/src/gui/npc_text.h b/src/gui/npc_text.h index 76161f88..b647b9a1 100644 --- a/src/gui/npc_text.h +++ b/src/gui/npc_text.h @@ -23,10 +23,15 @@ #define _TMW_NPC_TEXT_H #include <iosfwd> + #include <guichan/actionlistener.hpp> +#include "button.h" +#include "scrollarea.h" #include "window.h" +#include "../guichanfwd.h" + class TextBox; /** diff --git a/src/gui/npclistdialog.cpp b/src/gui/npclistdialog.cpp index cdd38312..c17cd4aa 100644 --- a/src/gui/npclistdialog.cpp +++ b/src/gui/npclistdialog.cpp @@ -19,13 +19,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "npclistdialog.h" - #include <sstream> -#include "button.h" -#include "scrollarea.h" #include "listbox.h" +#include "npclistdialog.h" #include "../npc.h" diff --git a/src/gui/npclistdialog.h b/src/gui/npclistdialog.h index f548dbba..099d03c0 100644 --- a/src/gui/npclistdialog.h +++ b/src/gui/npclistdialog.h @@ -28,6 +28,8 @@ #include <guichan/actionlistener.hpp> #include <guichan/listmodel.hpp> +#include "button.h" +#include "scrollarea.h" #include "window.h" #include "../guichanfwd.h" diff --git a/src/gui/ok_dialog.cpp b/src/gui/ok_dialog.cpp index d43c8e69..421c873e 100644 --- a/src/gui/ok_dialog.cpp +++ b/src/gui/ok_dialog.cpp @@ -20,33 +20,52 @@ */ #include "ok_dialog.h" -#include "textbox.h" -#include "button.h" -#include "scrollarea.h" + +#include <guichan/font.hpp> OkDialog::OkDialog(const std::string &title, const std::string &msg, Window *parent): Window(title, true, parent) { - TextBox *textBox = new TextBox(); - textBox->setEditable(false); + mTextBox = new TextBox(); + mTextBox->setEditable(false); + mTextBox->setOpaque(false); + + mTextArea = new ScrollArea(mTextBox); + okButton = new Button("Ok", "ok", this); + + mTextArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mTextArea->setVerticalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mTextArea->setOpaque(false); - gcn::ScrollArea *scrollArea = new ScrollArea(textBox); - gcn::Button *okButton = new Button("Ok", "ok", this); + mTextBox->setMinWidth(260); + mTextBox->setTextWrapped(msg); - setContentSize(260, 175); - scrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); - scrollArea->setVerticalScrollPolicy(gcn::ScrollArea::SHOW_ALWAYS); - scrollArea->setDimension(gcn::Rectangle( - 5, 5, 250, 160 - okButton->getHeight())); + int numRows = mTextBox->getNumberOfRows(); - textBox->setTextWrapped(msg); + if (numRows > 1) + { + // 15 == height of each line of text (based on font heights) + // 14 == row top + bottom graphic pixel heights + setContentSize(mTextBox->getMinWidth() + 15, 15 + (numRows * 15) + okButton->getHeight()); + mTextArea->setDimension(gcn::Rectangle(4, 5, mTextBox->getMinWidth() + 5, + 3 + (numRows * 14))); + } + else + { + int width = getFont()->getWidth(title); + if (width < getFont()->getWidth(msg)) + width = getFont()->getWidth(msg); + if (width < okButton->getWidth()) + width = okButton->getWidth(); + setContentSize(width + 15, 30 + okButton->getHeight()); + mTextArea->setDimension(gcn::Rectangle(4, 5, width + 5, 17)); + } - okButton->setPosition( - 260 - 5 - okButton->getWidth(), - 175 - 5 - okButton->getHeight()); + okButton->setPosition((mTextBox->getMinWidth() - okButton->getWidth()) / 2, + (numRows * 14) + okButton->getHeight() - 8); - add(scrollArea); + add(mTextArea); add(okButton); setLocationRelativeTo(getParent()); @@ -54,6 +73,11 @@ OkDialog::OkDialog(const std::string &title, const std::string &msg, okButton->requestFocus(); } +unsigned int OkDialog::getNumRows() +{ + return mTextBox->getNumberOfRows(); +} + void OkDialog::action(const gcn::ActionEvent &event) { // Proxy button events to our listeners diff --git a/src/gui/ok_dialog.h b/src/gui/ok_dialog.h index cba12d72..a06ddd7c 100644 --- a/src/gui/ok_dialog.h +++ b/src/gui/ok_dialog.h @@ -24,8 +24,13 @@ #include <guichan/actionlistener.hpp> +#include "button.h" +#include "scrollarea.h" +#include "textbox.h" #include "window.h" +#include "../guichanfwd.h" + /** * An 'Ok' button dialog. * @@ -41,10 +46,17 @@ class OkDialog : public Window, public gcn::ActionListener { OkDialog(const std::string &title, const std::string &msg, Window *parent = NULL); + unsigned int getNumRows(); + /** * Called when receiving actions from the widgets. */ void action(const gcn::ActionEvent &event); + + private: + TextBox *mTextBox; + ScrollArea *mTextArea; + gcn::Button *okButton; }; #endif diff --git a/src/gui/passwordfield.h b/src/gui/passwordfield.h index 8a14b72a..9aa6ab49 100644 --- a/src/gui/passwordfield.h +++ b/src/gui/passwordfield.h @@ -24,6 +24,8 @@ #include "textfield.h" +#include "../guichanfwd.h" + /** * A password field. * diff --git a/src/gui/playerbox.cpp b/src/gui/playerbox.cpp index b6b7663a..79c5676f 100644 --- a/src/gui/playerbox.cpp +++ b/src/gui/playerbox.cpp @@ -23,8 +23,8 @@ #include "playerbox.h" -#include "../player.h" #include "../graphics.h" +#include "../player.h" #include "../resources/image.h" #include "../resources/resourcemanager.h" diff --git a/src/gui/playerbox.h b/src/gui/playerbox.h index 78eeee91..7aec87bf 100644 --- a/src/gui/playerbox.h +++ b/src/gui/playerbox.h @@ -24,6 +24,8 @@ #include <guichan/widgets/scrollarea.hpp> +#include "../guichanfwd.h" + class ImageRect; class Player; diff --git a/src/gui/popupmenu.cpp b/src/gui/popupmenu.cpp index 8b73abfe..c0feb68d 100644 --- a/src/gui/popupmenu.cpp +++ b/src/gui/popupmenu.cpp @@ -19,8 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "popupmenu.h" - #include <cassert> #include <iostream> @@ -29,6 +27,7 @@ #include "browserbox.h" #include "inventorywindow.h" #include "item_amount.h" +#include "popupmenu.h" #include "windowcontainer.h" #include "../being.h" @@ -38,8 +37,11 @@ #include "../npc.h" #include "../player_relations.h" -#include "../resources/iteminfo.h" +#include "../net/messageout.h" +#include "../net/protocol.h" + #include "../resources/itemdb.h" +#include "../resources/iteminfo.h" extern std::string tradePartnerName; @@ -68,49 +70,53 @@ void PopupMenu::showPopup(int x, int y, Being *being) switch (mBeing->getType()) { - case Being::PLAYER: - { - // Players can be traded with. Later also attack, follow and - // add as buddy will be options in this menu. - const std::string &name = mBeing->getName(); - mBrowserBox->addRow("@@trade|Trade With " + name + "@@"); - mBrowserBox->addRow("@@attack|Attack " + name + "@@"); - - mBrowserBox->addRow("##3---"); - - switch (player_relations.getRelation(name)) { - case PlayerRelation::NEUTRAL: - mBrowserBox->addRow("@@friend|Befriend " + name + "@@"); - - case PlayerRelation::FRIEND: - mBrowserBox->addRow("@@disregard|Disregard " + name + "@@"); - mBrowserBox->addRow("@@ignore|Ignore " + name + "@@"); - break; - - case PlayerRelation::DISREGARDED: - mBrowserBox->addRow("@@unignore|Un-Ignore " + name + "@@"); - mBrowserBox->addRow("@@ignore|Completely ignore " + name + "@@"); - break; - - case PlayerRelation::IGNORED: - mBrowserBox->addRow("@@unignore|Un-Ignore " + name + "@@"); - break; - } - - //mBrowserBox->addRow("@@follow|Follow " + name + "@@"); - //mBrowserBox->addRow("@@buddy|Add " + name + " to Buddy List@@"); - } - break; - - case Being::NPC: - // NPCs can be talked to (single option, candidate for removal - // unless more options would be added) - mBrowserBox->addRow("@@talk|Talk To NPC@@"); - break; - - default: - /* Other beings aren't interesting... */ - break; + case Being::PLAYER: + { + // Players can be traded with. Later also attack, follow and + // add as buddy will be options in this menu. + const std::string &name = mBeing->getName(); + mBrowserBox->addRow("@@trade|Trade With " + name + "@@"); + mBrowserBox->addRow("@@attack|Attack " + name + "@@"); + + mBrowserBox->addRow("##3---"); + + switch (player_relations.getRelation(name)) { + case PlayerRelation::NEUTRAL: + mBrowserBox->addRow("@@friend|Befriend " + name + "@@"); + + case PlayerRelation::FRIEND: + mBrowserBox->addRow("@@disregard|Disregard " + name + "@@"); + mBrowserBox->addRow("@@ignore|Ignore " + name + "@@"); + break; + + case PlayerRelation::DISREGARDED: + mBrowserBox->addRow("@@unignore|Un-Ignore " + name + "@@"); + mBrowserBox->addRow("@@ignore|Completely ignore " + name + "@@"); + break; + + case PlayerRelation::IGNORED: + mBrowserBox->addRow("@@unignore|Un-Ignore " + name + "@@"); + break; + } + + //mBrowserBox->addRow("@@follow|Follow " + name + "@@"); + //mBrowserBox->addRow("@@buddy|Add " + name + " to Buddy List@@"); + + mBrowserBox->addRow("##3---"); + mBrowserBox->addRow("@@party-invite|Invite " + name + + " to party@@"); + } + break; + + case Being::NPC: + // NPCs can be talked to (single option, candidate for removal + // unless more options would be added) + mBrowserBox->addRow("@@talk|Talk To NPC@@"); + break; + + default: + /* Other beings aren't interesting... */ + break; } //browserBox->addRow("@@look|Look To@@"); @@ -140,56 +146,56 @@ void PopupMenu::handleLink(const std::string& link) { // Talk To action if (link == "talk" && - mBeing != NULL && - mBeing->getType() == Being::NPC && - current_npc == 0) + mBeing != NULL && + mBeing->getType() == Being::NPC && + current_npc == 0) { - dynamic_cast<NPC*>(mBeing)->talk(); + dynamic_cast<NPC*>(mBeing)->talk(); } // Trade action else if (link == "trade" && - mBeing != NULL && - mBeing->getType() == Being::PLAYER) + mBeing != NULL && + mBeing->getType() == Being::PLAYER) { - player_node->trade(mBeing); - tradePartnerName = mBeing->getName(); + player_node->trade(mBeing); + tradePartnerName = mBeing->getName(); } // Attack action else if (link == "attack" && - mBeing != NULL && - mBeing->getType() == Being::PLAYER) + mBeing != NULL && + mBeing->getType() == Being::PLAYER) { - player_node->attack(mBeing, true); + player_node->attack(mBeing, true); } else if (link == "unignore" && - mBeing != NULL && - mBeing->getType() == Being::PLAYER) + mBeing != NULL && + mBeing->getType() == Being::PLAYER) { - player_relations.setRelation(mBeing->getName(), PlayerRelation::NEUTRAL); + player_relations.setRelation(mBeing->getName(), PlayerRelation::NEUTRAL); } else if (link == "ignore" && - mBeing != NULL && - mBeing->getType() == Being::PLAYER) + mBeing != NULL && + mBeing->getType() == Being::PLAYER) { - player_relations.setRelation(mBeing->getName(), PlayerRelation::IGNORED); + player_relations.setRelation(mBeing->getName(), PlayerRelation::IGNORED); } else if (link == "disregard" && - mBeing != NULL && - mBeing->getType() == Being::PLAYER) + mBeing != NULL && + mBeing->getType() == Being::PLAYER) { - player_relations.setRelation(mBeing->getName(), PlayerRelation::DISREGARDED); + player_relations.setRelation(mBeing->getName(), PlayerRelation::DISREGARDED); } else if (link == "friend" && - mBeing != NULL && - mBeing->getType() == Being::PLAYER) + mBeing != NULL && + mBeing->getType() == Being::PLAYER) { - player_relations.setRelation(mBeing->getName(), PlayerRelation::FRIEND); + player_relations.setRelation(mBeing->getName(), PlayerRelation::FRIEND); } /* @@ -202,16 +208,16 @@ void PopupMenu::handleLink(const std::string& link) // Add Buddy action else if ((link == "buddy") && mBeing != NULL && mBeing->isPlayer()) { - if (!buddyWindow->isVisible()) - buddyWindow->setVisible(true); + if (!buddyWindow->isVisible()) + buddyWindow->setVisible(true); - buddyWindow->addBuddy(mBeing->getName()); + buddyWindow->addBuddy(mBeing->getName()); }*/ // Pick Up Floor Item action else if ((link == "pickup") && mFloorItem != NULL) { - player_node->pickUp(mFloorItem); + player_node->pickUp(mFloorItem); } // Look To action @@ -221,39 +227,47 @@ void PopupMenu::handleLink(const std::string& link) else if (link == "use") { - assert(mItem); - if (mItem->isEquipment()) - { - if (mItem->isEquipped()) - { - player_node->unequipItem(mItem); - } - else - { - player_node->equipItem(mItem); - } - } - else - { - player_node->useItem(mItem); - } + assert(mItem); + if (mItem->isEquipment()) + { + if (mItem->isEquipped()) + { + player_node->unequipItem(mItem); + } + else + { + player_node->equipItem(mItem); + } + } + else + { + player_node->useItem(mItem); + } } else if (link == "drop") { - new ItemAmountWindow(AMOUNT_ITEM_DROP, inventoryWindow, mItem); + new ItemAmountWindow(AMOUNT_ITEM_DROP, inventoryWindow, mItem); } else if (link == "description") { - // do nothing for now, I need to write - // a window for the description first + // do nothing for now, I need to write + // a window for the description first + } + else if (link == "party-invite" && + mBeing != NULL && + mBeing->getType() == Being::PLAYER) + { + MessageOut outMsg(player_node->getNetwork()); + outMsg.writeInt16(CMSG_PARTY_INVITE); + outMsg.writeInt32(mBeing->getId()); } // Unknown actions else { - std::cout << link << std::endl; + std::cout << link << std::endl; } setVisible(false); @@ -271,13 +285,13 @@ void PopupMenu::showPopup(int x, int y, Item *item) if (item->isEquipment()) { - if (item->isEquipped()) - mBrowserBox->addRow("@@use|Unequip@@"); - else - mBrowserBox->addRow("@@use|Equip@@"); + if (item->isEquipped()) + mBrowserBox->addRow("@@use|Unequip@@"); + else + mBrowserBox->addRow("@@use|Equip@@"); } else - mBrowserBox->addRow("@@use|Use@@"); + mBrowserBox->addRow("@@use|Use@@"); mBrowserBox->addRow("@@drop|Drop@@"); mBrowserBox->addRow("@@description|Description@@"); @@ -291,9 +305,9 @@ void PopupMenu::showPopup(int x, int y) { setContentSize(mBrowserBox->getWidth() + 8, mBrowserBox->getHeight() + 8); if (windowContainer->getWidth() < (x + getWidth() + 5)) - x = windowContainer->getWidth() - getWidth(); + x = windowContainer->getWidth() - getWidth(); if (windowContainer->getHeight() < (y + getHeight() + 5)) - y = windowContainer->getHeight() - getHeight(); + y = windowContainer->getHeight() - getHeight(); setPosition(x, y); setVisible(true); requestMoveToTop(); diff --git a/src/gui/popupmenu.h b/src/gui/popupmenu.h index 2d10e6eb..3cf949b3 100644 --- a/src/gui/popupmenu.h +++ b/src/gui/popupmenu.h @@ -22,15 +22,14 @@ #ifndef _TMW_POPUP_MENU_H #define _TMW_POPUP_MENU_H -#include "window.h" #include "linkhandler.h" +#include "window.h" class Being; class BrowserBox; class FloorItem; class Item; - /** * Window showing popup menu. */ diff --git a/src/gui/progressbar.h b/src/gui/progressbar.h index 70cfa15c..a20c901f 100644 --- a/src/gui/progressbar.h +++ b/src/gui/progressbar.h @@ -26,8 +26,9 @@ #include <SDL_types.h> -class ImageRect; +#include "../guichanfwd.h" +class ImageRect; /** * A progress bar. diff --git a/src/gui/radiobutton.h b/src/gui/radiobutton.h index 6506444f..8fb6d832 100644 --- a/src/gui/radiobutton.h +++ b/src/gui/radiobutton.h @@ -24,8 +24,9 @@ #include <guichan/widgets/radiobutton.hpp> -class Image; +#include "../guichanfwd.h" +class Image; /* * Guichan based RadioButton with custom look diff --git a/src/gui/register.cpp b/src/gui/register.cpp index 2a97a3e5..ec6a9756 100644 --- a/src/gui/register.cpp +++ b/src/gui/register.cpp @@ -19,8 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "register.h" - #include <string> #include <sstream> @@ -34,10 +32,13 @@ #include "button.h" #include "checkbox.h" #include "login.h" +#include "ok_dialog.h" #include "passwordfield.h" #include "radiobutton.h" +#include "register.h" #include "textfield.h" -#include "ok_dialog.h" + +#include "../utils/tostring.h" void WrongDataNoticeListener::setTarget(gcn::TextField *textField) @@ -63,53 +64,65 @@ RegisterDialog::RegisterDialog(LoginData *loginData): gcn::Label *passwordLabel = new gcn::Label("Password:"); gcn::Label *confirmLabel = new gcn::Label("Confirm:"); gcn::Label *serverLabel = new gcn::Label("Server:"); + gcn::Label *portLabel = new gcn::Label("Port:"); + mUserField = new TextField(loginData->username); mPasswordField = new PasswordField(loginData->password); mConfirmField = new PasswordField(); mServerField = new TextField(loginData->hostname); + mPortField = new TextField(toString(loginData->port)); mMaleButton = new RadioButton("Male", "sex", true); mFemaleButton = new RadioButton("Female", "sex", false); mRegisterButton = new Button("Register", "register", this); mCancelButton = new Button("Cancel", "cancel", this); - const int width = 220; - const int height = 150; - setContentSize(width, height); - - mUserField->setPosition(65, 5); - mUserField->setWidth(width - 70); - mPasswordField->setPosition( - 65, mUserField->getY() + mUserField->getHeight() + 7); - mPasswordField->setWidth(mUserField->getWidth()); - mConfirmField->setPosition( - 65, mPasswordField->getY() + mPasswordField->getHeight() + 7); - mConfirmField->setWidth(mUserField->getWidth()); - mServerField->setPosition( - 65, 23 + mConfirmField->getY() + mConfirmField->getHeight() + 7); - mServerField->setWidth(mUserField->getWidth()); - - userLabel->setPosition(5, mUserField->getY() + 1); - passwordLabel->setPosition(5, mPasswordField->getY() + 1); - confirmLabel->setPosition(5, mConfirmField->getY() + 1); - serverLabel->setPosition(5, mServerField->getY() + 1); - - mMaleButton->setPosition( - 70, mConfirmField->getY() + mConfirmField->getHeight() + 7); - mFemaleButton->setPosition( - 70 + 10 + mMaleButton->getWidth(), - mMaleButton->getY()); + const int WIDTH = 220; + const int HEIGHT = 170; + const int FIELD_WIDTH = WIDTH - 70; + + setContentSize(WIDTH, HEIGHT); + + const int USER_TOP = 5; + userLabel->setPosition(5, USER_TOP); + mUserField->setPosition(65, USER_TOP); + mUserField->setWidth(FIELD_WIDTH); + + const int PASS_TOP = 9 + USER_TOP + userLabel->getHeight(); + passwordLabel->setPosition(5, PASS_TOP); + mPasswordField->setPosition(65, PASS_TOP); + mPasswordField->setWidth(FIELD_WIDTH); + + const int CONFIRM_TOP = 9 + PASS_TOP + passwordLabel->getHeight(); + confirmLabel->setPosition(5, CONFIRM_TOP); + mConfirmField->setPosition(65, CONFIRM_TOP); + mConfirmField->setWidth(FIELD_WIDTH); + + const int SEX_TOP = 9 + CONFIRM_TOP + confirmLabel->getHeight(); + mMaleButton->setPosition(70, SEX_TOP); + mFemaleButton->setPosition(80 + mMaleButton->getWidth(), SEX_TOP); + + const int SERVER_TOP = 9 + SEX_TOP + mMaleButton->getHeight() + 5; + serverLabel->setPosition(5, SERVER_TOP); + mServerField->setPosition(65, SERVER_TOP); + mServerField->setWidth(FIELD_WIDTH); + + const int PORT_TOP = 9 + SERVER_TOP + serverLabel->getHeight(); + portLabel->setPosition(5, PORT_TOP); + mPortField->setPosition(65, PORT_TOP); + mPortField->setWidth(FIELD_WIDTH); mCancelButton->setPosition( - width - mCancelButton->getWidth() - 5, - height - mCancelButton->getHeight() - 5); + WIDTH - mCancelButton->getWidth() - 5, + HEIGHT - mCancelButton->getHeight() - 5); mRegisterButton->setPosition( mCancelButton->getX() - mRegisterButton->getWidth() - 5, - height - mRegisterButton->getHeight() - 5); + HEIGHT - mRegisterButton->getHeight() - 5); mUserField->addKeyListener(this); mPasswordField->addKeyListener(this); mConfirmField->addKeyListener(this); mServerField->addKeyListener(this); + mPortField->addKeyListener(this); /* TODO: * This is a quick and dirty way to respond to the ENTER key, regardless of @@ -120,19 +133,26 @@ RegisterDialog::RegisterDialog(LoginData *loginData): mPasswordField->setActionEventId("register"); mConfirmField->setActionEventId("register"); mServerField->setActionEventId("register"); + mPortField->setActionEventId("register"); + mUserField->addActionListener(this); mPasswordField->addActionListener(this); mConfirmField->addActionListener(this); mServerField->addActionListener(this); + mPortField->addActionListener(this); add(userLabel); add(passwordLabel); - add(serverLabel); add(confirmLabel); + add(serverLabel); + add(portLabel); + add(mUserField); add(mPasswordField); add(mConfirmField); add(mServerField); + add(mPortField); + add(mMaleButton); add(mFemaleButton); add(mRegisterButton); @@ -230,7 +250,7 @@ RegisterDialog::action(const gcn::ActionEvent &event) mRegisterButton->setEnabled(false); mLoginData->hostname = mServerField->getText(); - mLoginData->port = (short) config.getValue("port", 0); + mLoginData->port = getUShort(mPortField->getText()); mLoginData->username = mUserField->getText(); mLoginData->password = mPasswordField->getText(); mLoginData->username += mFemaleButton->isSelected() ? "_F" : "_M"; @@ -254,5 +274,42 @@ RegisterDialog::canSubmit() !mPasswordField->getText().empty() && !mConfirmField->getText().empty() && !mServerField->getText().empty() && + isUShort(mPortField->getText()) && state == REGISTER_STATE; } + +bool +RegisterDialog::isUShort(const std::string &str) +{ + if (str == "") + { + return false; + } + unsigned long l = 0; + for (std::string::const_iterator strPtr = str.begin(), strEnd = str.end(); + strPtr != strEnd; ++strPtr) + { + if (*strPtr < '0' || *strPtr > '9') + { + return false; + } + l = l * 10 + (*strPtr - '0'); // *strPtr - '0' will never be negative + if (l > 65535) + { + return false; + } + } + return true; +} + +unsigned short +RegisterDialog::getUShort(const std::string &str) +{ + unsigned long l = 0; + for (std::string::const_iterator strPtr = str.begin(), strEnd = str.end(); + strPtr != strEnd; ++strPtr) + { + l = l * 10 + (*strPtr - '0'); + } + return static_cast<unsigned short>(l); +} diff --git a/src/gui/register.h b/src/gui/register.h index 771962cc..87a11bb9 100644 --- a/src/gui/register.h +++ b/src/gui/register.h @@ -23,10 +23,12 @@ #define _TMW_REGISTER_H #include <iosfwd> + #include <guichan/actionlistener.hpp> #include <guichan/keylistener.hpp> #include "window.h" + #include "../guichanfwd.h" class LoginData; @@ -85,10 +87,31 @@ class RegisterDialog : public Window, public gcn::ActionListener, bool canSubmit(); + /** + * Function to decide whether string is an unsigned short or not + * + * @param str the string to parse + * + * @return true if str is an unsigned short, false otherwise + */ + static bool + isUShort(const std::string &str); + + /** + * Converts string to an unsigned short (undefined if invalid) + * + * @param str the string to parse + * + * @return the value str represents + */ + static unsigned short + getUShort(const std::string &str); + gcn::TextField *mUserField; gcn::TextField *mPasswordField; gcn::TextField *mConfirmField; gcn::TextField *mServerField; + gcn::TextField *mPortField; gcn::Button *mRegisterButton; gcn::Button *mCancelButton; diff --git a/src/gui/scrollarea.h b/src/gui/scrollarea.h index d21dae11..ebe2c77b 100644 --- a/src/gui/scrollarea.h +++ b/src/gui/scrollarea.h @@ -24,6 +24,8 @@ #include <guichan/widgets/scrollarea.hpp> +#include "../guichanfwd.h" + class Image; class ImageRect; diff --git a/src/gui/sell.cpp b/src/gui/sell.cpp index 6df1cbf6..63af1aaa 100644 --- a/src/gui/sell.cpp +++ b/src/gui/sell.cpp @@ -19,8 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "sell.h" - #include <cassert> #include <guichan/widgets/label.hpp> @@ -28,17 +26,18 @@ #include "button.h" #include "shoplistbox.h" #include "scrollarea.h" +#include "sell.h" #include "shop.h" #include "slider.h" #include "../item.h" #include "../npc.h" -#include "../resources/iteminfo.h" - #include "../net/messageout.h" #include "../net/protocol.h" +#include "../resources/iteminfo.h" + #include "../utils/tostring.h" SellDialog::SellDialog(Network *network): diff --git a/src/gui/setup.cpp b/src/gui/setup.cpp index 4fae514f..2c22a426 100644 --- a/src/gui/setup.cpp +++ b/src/gui/setup.cpp @@ -20,41 +20,42 @@ */ #include <algorithm> - -#include "setup.h" +#include <iostream> #include "button.h" +#include "setup.h" #include "setup_audio.h" +#include "setup_colours.h" #include "setup_joystick.h" -#include "setup_video.h" #include "setup_keyboard.h" #include "setup_players.h" +#include "setup_video.h" #include "tabbedcontainer.h" #include "../utils/dtor.h" -#include <iostream> -extern Window *statusWindow; -extern Window *minimap; extern Window *chatWindow; -extern Window *inventoryWindow; extern Window *equipmentWindow; extern Window *helpWindow; +extern Window *inventoryWindow; +extern Window *minimap; extern Window *skillDialog; +extern Window *statusWindow; Setup::Setup(): Window("Setup") { setCloseButton(true); int width = 310; - int height = 265; + int height = 310; setContentSize(width, height); const char *buttonNames[] = { "Apply", "Cancel", "Reset Windows", 0 }; int x = width; - for (const char **curBtn = buttonNames; *curBtn; ++curBtn) { + for (const char **curBtn = buttonNames; *curBtn; ++curBtn) + { Button *btn = new Button(*curBtn, *curBtn, this); x -= btn->getWidth() + 5; btn->setPosition(x, height - btn->getHeight() - 5); @@ -65,7 +66,7 @@ Setup::Setup(): btn->setEnabled(statusWindow != NULL); } - TabbedContainer *panel = new TabbedContainer(); + TabbedContainer *panel = new TabbedContainer(width, 5, 20, 45, 5, 3); panel->setDimension(gcn::Rectangle(5, 5, width, height - 40)); panel->setOpaque(false); @@ -87,6 +88,10 @@ Setup::Setup(): panel->addTab(tab, "Keyboard"); mTabs.push_back(tab); + tab = new Setup_Colours(); + panel->addTab(tab, "Colours"); + mTabs.push_back(tab); + tab = new Setup_Players(); panel->addTab(tab, "Players"); mTabs.push_back(tab); @@ -98,7 +103,7 @@ Setup::Setup(): Setup::~Setup() { - for_each(mTabs.begin(), mTabs.end(), make_dtor(mTabs)); + delete_all(mTabs); } void Setup::action(const gcn::ActionEvent &event) diff --git a/src/gui/setup.h b/src/gui/setup.h index 5268f725..fd200f4c 100644 --- a/src/gui/setup.h +++ b/src/gui/setup.h @@ -28,6 +28,8 @@ #include "window.h" +#include "../guichanfwd.h" + class SetupTab; /** diff --git a/src/gui/setup_audio.cpp b/src/gui/setup_audio.cpp index a027e133..70b34a31 100644 --- a/src/gui/setup_audio.cpp +++ b/src/gui/setup_audio.cpp @@ -19,12 +19,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "setup_audio.h" - #include <guichan/widgets/label.hpp> #include "checkbox.h" #include "ok_dialog.h" +#include "setup_audio.h" #include "slider.h" #include "../configuration.h" diff --git a/src/gui/setup_audio.h b/src/gui/setup_audio.h index eaa55de6..9835a8fb 100644 --- a/src/gui/setup_audio.h +++ b/src/gui/setup_audio.h @@ -22,10 +22,10 @@ #ifndef _TMW_GUI_SETUP_AUDIO_H #define _TMW_GUI_SETUP_AUDIO_H -#include "setuptab.h" - #include <guichan/actionlistener.hpp> +#include "setuptab.h" + #include "../guichanfwd.h" class Setup_Audio : public SetupTab, public gcn::ActionListener diff --git a/src/gui/setup_colours.cpp b/src/gui/setup_colours.cpp new file mode 100644 index 00000000..0becd48f --- /dev/null +++ b/src/gui/setup_colours.cpp @@ -0,0 +1,207 @@ +/*************************************************************************** + * Copyright (C) 2008 by Douglas Boffey * + * * + * DougABoffey@netscape.net * + * 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 * + * (at your option) any later version. * + * * + * This program is distributed with The Mana Experiment * + * 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 <vector> + +#include <guichan/listmodel.hpp> +#include <guichan/widgets/label.hpp> +#include <guichan/widgets/slider.hpp> + +#include "colour.h" +#include "scrollarea.h" +#include "setup_colours.h" +#include "slider.h" + +#include "../configuration.h" + +Setup_Colours::Setup_Colours() : + mColourLabel("Colour:"), + mSelected(-1) +{ + mColourBox = new gcn::ListBox(textColour); + mScroll = new ScrollArea(mColourBox); + + mColourLabel.setX(5); + mColourLabel.setY(5); + + mColourBox->setDimension(gcn::Rectangle(0, 10 + mColourLabel.getHeight(), + 80, + 115 - mColourLabel.getHeight())); + mScroll->setDimension(gcn::Rectangle(5, 10 + mColourLabel.getHeight(), + 100, 115 - mColourLabel.getHeight())); + mColourBox->setSelected(-1); + mColourBox->setActionEventId("colour_box"); + mColourBox->addActionListener(this); + + setOpaque(false); + + add(&mColourLabel); + add(mScroll); + + setupPlacer(140, mLabel1, mSlider1, mText1, "R", "1"); + setupPlacer(165, mLabel2, mSlider2, mText2, "G", "2"); + setupPlacer(190, mLabel3, mSlider3, mText3, "B", "3"); + +} + +Setup_Colours::~Setup_Colours() +{ + delete mLabel1; + delete mSlider1; + delete mText1; + + delete mLabel2; + delete mSlider2; + delete mText2; + + delete mLabel3; + delete mSlider3; + delete mText3; + + delete mScroll; +} + +void Setup_Colours::action(const gcn::ActionEvent &event) +{ + if (event.getId() == "colour_box") + { + mSelected = mColourBox->getSelected(); + int col = textColour->getColourAt(mSelected); + setEntry(mSlider1, mText1, col >> 16); + setEntry(mSlider2, mText2, (col >> 8) & 0xff); + setEntry(mSlider3, mText3, col & 0xff); + return; + } + + if (event.getId() == "slider1") + { + char buffer[30]; + std::sprintf(buffer, "%d", static_cast<int>(mSlider1->getValue())); + mText1->setText(buffer); + updateColour(); + return; + } + + if (event.getId() == "slider2") + { + char buffer[30]; + std::sprintf(buffer, "%d", static_cast<int>(mSlider2->getValue())); + mText2->setText(buffer); + updateColour(); + return; + } + + if (event.getId() == "slider3") + { + char buffer[30]; + std::sprintf(buffer, "%d", static_cast<int>(mSlider3->getValue())); + mText3->setText(buffer); + updateColour(); + return; + } +} + +void Setup_Colours::setEntry(Slider *s, TextField *t, int value) +{ + s->setValue(value); + char buffer[100]; + sprintf(buffer, "%d", value); + t->setText(buffer); +} + +void Setup_Colours::apply() +{ + textColour->commit(); +} + +void Setup_Colours::cancel() +{ + textColour->rollback(); + int col = textColour->getColourAt(mSelected); + setEntry(mSlider1, mText1, col >> 16); + setEntry(mSlider2, mText2, (col >> 8) & 0xff); + setEntry(mSlider3, mText3, col & 0xff); +} + +void Setup_Colours::setupPlacer(int v, gcn::Label *&l, Slider *&s, + TextField *&t, std::string lbl, + std::string sfx) +{ + l = new gcn::Label(lbl + ":"); + l->setX(5); + l->setY(v - l->getHeight() / 2); + + s = new Slider(0, 255); + s->setHeight(10); + s->setX(25); + s->setY(v - s->getHeight() / 2); + s->setWidth(128); + s->setScale(0, 255); + + t = new TextField(); + t->setX(165); + t->setY(v - t->getHeight() / 2); + t->setWidth(40); + t->setNumeric(true); + t->setRange(0, 255); + t->addListener(this); + + s->setActionEventId("slider" + sfx); + s->addActionListener(this); + + add(l); + add(s); + add(t); +} + +void Setup_Colours::listen(const TextField *tf) +{ + if (tf == mText1) + { + mSlider1->setValue(tf->getValue()); + updateColour(); + return; + } + if (tf == mText2) + { + mSlider2->setValue(tf->getValue()); + updateColour(); + return; + } + if (tf == mText3) + { + mSlider3->setValue(tf->getValue()); + updateColour(); + return; + } +} + +void Setup_Colours::updateColour() +{ + if (mSelected == -1) + { + return; + } + int rgb = static_cast<int>(mSlider1->getValue()) << 16 | + static_cast<int>(mSlider2->getValue()) << 8 | + static_cast<int>(mSlider3->getValue()); + textColour->setColourAt(mSelected, rgb); +} diff --git a/src/gui/setup_colours.h b/src/gui/setup_colours.h new file mode 100644 index 00000000..3bd87848 --- /dev/null +++ b/src/gui/setup_colours.h @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright (C) 2008 by Douglas Boffey * + * * + * DougABoffey@netscape.net * + * 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 * + * (at your option) any later version. * + * * + * This program is distributed with The Mana Experiment * + * 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 _SETUP_COLOURS_H +#define _SETUP_COLOURS_H + +#include <string> +#include <vector> + +#include <guichan/actionlistener.hpp> +#include <guichan/widgets/label.hpp> +#include <guichan/widgets/listbox.hpp> + +#include "scrollarea.h" +#include "setuptab.h" +#include "slider.h" +#include "textfield.h" + +#include "../guichanfwd.h" + +class Setup_Colours : public SetupTab, public gcn::ActionListener, + public TextFieldListener +{ + public: + Setup_Colours(); + ~Setup_Colours(); + void apply(); + void cancel(); + void action(const gcn::ActionEvent &event); + + void listen(const TextField *tf); + private: + gcn::ListBox *mColourBox; + gcn::Label mColourLabel; + ScrollArea *mScroll; + int mSelected; + + gcn::Label *mLabel1; + Slider *mSlider1; + TextField *mText1; + int mValue1; + + gcn::Label *mLabel2; + Slider *mSlider2; + TextField *mText2; + int mValue2; + + gcn::Label *mLabel3; + Slider *mSlider3; + TextField *mText3; + int mValue3; + + void setupPlacer(int v, gcn::Label *&l, Slider *&s, TextField *&t, + std::string lbl, std::string sfx); + void setEntry(Slider *s, TextField *t, int value); + void updateColour(); +}; +#endif diff --git a/src/gui/setup_joystick.cpp b/src/gui/setup_joystick.cpp index c59068f7..723d0597 100644 --- a/src/gui/setup_joystick.cpp +++ b/src/gui/setup_joystick.cpp @@ -19,12 +19,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "setup_joystick.h" - #include <guichan/widgets/label.hpp> #include "button.h" #include "checkbox.h" +#include "setup_joystick.h" + #include "../configuration.h" #include "../joystick.h" diff --git a/src/gui/setup_joystick.h b/src/gui/setup_joystick.h index 0b7ebe98..d2973a89 100644 --- a/src/gui/setup_joystick.h +++ b/src/gui/setup_joystick.h @@ -22,10 +22,10 @@ #ifndef _TMW_GUI_SETUP_JOYSTICK_H #define _TMW_GUI_SETUP_JOYSTICK_H -#include "setuptab.h" - #include <guichan/actionlistener.hpp> +#include "setuptab.h" + #include "../guichanfwd.h" class Setup_Joystick : public SetupTab, public gcn::ActionListener diff --git a/src/gui/setup_keyboard.cpp b/src/gui/setup_keyboard.cpp index c6247487..007fcb52 100644 --- a/src/gui/setup_keyboard.cpp +++ b/src/gui/setup_keyboard.cpp @@ -19,7 +19,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "setup_keyboard.h" +#include <SDL_keyboard.h> #include <guichan/widgets/label.hpp> #include <guichan/listmodel.hpp> @@ -28,14 +28,13 @@ #include "listbox.h" #include "ok_dialog.h" #include "scrollarea.h" +#include "setup_keyboard.h" #include "../configuration.h" #include "../keyboardconfig.h" #include "../utils/tostring.h" -#include <SDL_keyboard.h> - /** * The list model for key function list. * diff --git a/src/gui/setup_keyboard.h b/src/gui/setup_keyboard.h index 50fa76fb..937790af 100644 --- a/src/gui/setup_keyboard.h +++ b/src/gui/setup_keyboard.h @@ -22,14 +22,14 @@ #ifndef _TMW_GUI_SETUP_KEYBOARD_H #define _TMW_GUI_SETUP_KEYBOARD_H -#include "setuptab.h" -#include "button.h" -#include "../guichanfwd.h" +#include <string> #include <guichan/actionlistener.hpp> +#include "button.h" +#include "setuptab.h" -#include <string> +#include "../guichanfwd.h" class Setup_Keyboard : public SetupTab, public gcn::ActionListener { diff --git a/src/gui/setup_players.cpp b/src/gui/setup_players.cpp index 7dcbb5dc..d07a9685 100644 --- a/src/gui/setup_players.cpp +++ b/src/gui/setup_players.cpp @@ -19,19 +19,19 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "setup_players.h" - #include <vector> + #include <guichan/widgets/dropdown.hpp> #include <guichan/widgets/label.hpp> #include "button.h" #include "checkbox.h" #include "ok_dialog.h" +#include "setup_players.h" -#include "../player_relations.h" #include "../configuration.h" #include "../log.h" +#include "../player_relations.h" #include "../sound.h" #define COLUMNS_NR 2 // name plus listbox @@ -46,7 +46,7 @@ #define WIDGET_AT(row, column) (((row) * COLUMNS_NR) + column) -static std::string table_titles[COLUMNS_NR] = {"name", "relation"}; +static std::string table_titles[COLUMNS_NR] = {" name", "relation "}; static const std::string RELATION_NAMES[PlayerRelation::RELATIONS_NR] = { "neutral", "friend", "disregarded", "ignored" diff --git a/src/gui/setup_players.h b/src/gui/setup_players.h index b04023ab..b693a952 100644 --- a/src/gui/setup_players.h +++ b/src/gui/setup_players.h @@ -22,15 +22,14 @@ #ifndef _TMW_GUI_SETUP_PLAYERS_H #define _TMW_GUI_SETUP_PLAYERS_H -#include "setuptab.h" +#include <guichan/actionlistener.hpp> -#include "scrollarea.h" #include "button.h" +#include "scrollarea.h" +#include "setuptab.h" #include "table.h" -#include <guichan/actionlistener.hpp> #include "../guichanfwd.h" - #include "../player_relations.h" class PlayerTableModel; diff --git a/src/gui/setup_video.cpp b/src/gui/setup_video.cpp index 5db2c262..15841615 100644 --- a/src/gui/setup_video.cpp +++ b/src/gui/setup_video.cpp @@ -19,11 +19,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "setup_video.h" - +#include <SDL.h> #include <string> #include <vector> -#include <SDL.h> #include <guichan/key.hpp> #include <guichan/listmodel.hpp> @@ -34,6 +32,7 @@ #include "listbox.h" #include "ok_dialog.h" #include "scrollarea.h" +#include "setup_video.h" #include "slider.h" #include "textfield.h" @@ -104,6 +103,8 @@ Setup_Video::Setup_Video(): mFullScreenEnabled(config.getValue("screen", 0)), mOpenGLEnabled(config.getValue("opengl", 0)), mCustomCursorEnabled(config.getValue("customcursor", 1)), + mParticleEffectsEnabled(config.getValue("particleeffects", 1)), + mSpeechBubbleEnabled(config.getValue("speechbubble", 1)), mOpacity(config.getValue("guialpha", 0.8)), mFps((int)config.getValue("fpslimit", 60)), mModeListModel(new ModeListModel()), @@ -111,6 +112,8 @@ Setup_Video::Setup_Video(): mFsCheckBox(new CheckBox("Full screen", mFullScreenEnabled)), mOpenGLCheckBox(new CheckBox("OpenGL", mOpenGLEnabled)), mCustomCursorCheckBox(new CheckBox("Custom cursor", mCustomCursorEnabled)), + mParticleEffectsCheckBox(new CheckBox("Particle effects", mParticleEffectsEnabled)), + mSpeechBubbleCheckBox(new CheckBox("Speech bubbles", mSpeechBubbleEnabled)), mAlphaSlider(new Slider(0.2, 1.0)), mFpsCheckBox(new CheckBox("FPS Limit: ")), mFpsSlider(new Slider(10, 200)), @@ -138,16 +141,18 @@ Setup_Video::Setup_Video(): mOpenGLCheckBox->setEnabled(false); #endif - mModeList->setDimension(gcn::Rectangle(0, 0, 60, 50)); - scrollArea->setDimension(gcn::Rectangle(10, 10, 90, 50)); + mModeList->setDimension(gcn::Rectangle(0, 0, 60, 70)); + scrollArea->setDimension(gcn::Rectangle(10, 10, 90, 70)); mFsCheckBox->setPosition(110, 10); mOpenGLCheckBox->setPosition(110, 30); + mParticleEffectsCheckBox->setPosition(175, 30); mCustomCursorCheckBox->setPosition(110, 50); - mAlphaSlider->setDimension(gcn::Rectangle(10, 80, 100, 10)); + mSpeechBubbleCheckBox->setPosition(110, 70); + mAlphaSlider->setDimension(gcn::Rectangle(10, 100, 75, 10)); alphaLabel->setPosition(20 + mAlphaSlider->getWidth(), mAlphaSlider->getY()); - mFpsCheckBox->setPosition(90, 100); - mFpsSlider->setDimension(gcn::Rectangle(10, 100, 75, 10)); + mFpsCheckBox->setPosition(90, 120); + mFpsSlider->setDimension(gcn::Rectangle(10, 120, 75, 10)); mFpsField->setPosition(100 + mFpsCheckBox->getWidth(), 100); mFpsField->setWidth(30); @@ -161,6 +166,8 @@ Setup_Video::Setup_Video(): mFpsCheckBox->setSelected(mFps > 0); mCustomCursorCheckBox->setActionEventId("customcursor"); + mParticleEffectsCheckBox->setActionEventId("particleeffects"); + mSpeechBubbleCheckBox->setActionEventId("speechbubble"); mAlphaSlider->setActionEventId("guialpha"); mFpsCheckBox->setActionEventId("fpslimitcheckbox"); mFpsSlider->setActionEventId("fpslimitslider"); @@ -174,6 +181,8 @@ Setup_Video::Setup_Video(): mParticleDetailField->setActionEventId("particledetailfield"); mCustomCursorCheckBox->addActionListener(this); + mParticleEffectsCheckBox->addActionListener(this); + mSpeechBubbleCheckBox->addActionListener(this); mAlphaSlider->addActionListener(this); mFpsCheckBox->addActionListener(this); mFpsSlider->addActionListener(this); @@ -187,26 +196,26 @@ Setup_Video::Setup_Video(): mParticleDetailSlider->addActionListener(this); mParticleDetailField->addKeyListener(this); - mScrollRadiusSlider->setDimension(gcn::Rectangle(10, 120, 75, 10)); + mScrollRadiusSlider->setDimension(gcn::Rectangle(10, 140, 75, 10)); gcn::Label *scrollRadiusLabel = new gcn::Label("Scroll radius"); - scrollRadiusLabel->setPosition(90, 120); - mScrollRadiusField->setPosition(mFpsField->getX(), 120); + scrollRadiusLabel->setPosition(90, 140); + mScrollRadiusField->setPosition(mFpsField->getX(), 140); mScrollRadiusField->setWidth(30); mScrollRadiusField->setText(toString(mOriginalScrollRadius)); mScrollRadiusSlider->setValue(mOriginalScrollRadius); - mScrollLazinessSlider->setDimension(gcn::Rectangle(10, 140, 75, 10)); + mScrollLazinessSlider->setDimension(gcn::Rectangle(10, 160, 75, 10)); gcn::Label *scrollLazinessLabel = new gcn::Label("Scroll laziness"); - scrollLazinessLabel->setPosition(90, 140); - mScrollLazinessField->setPosition(mFpsField->getX(), 140); + scrollLazinessLabel->setPosition(90, 160); + mScrollLazinessField->setPosition(mFpsField->getX(), 160); mScrollLazinessField->setWidth(30); mScrollLazinessField->setText(toString(mOriginalScrollLaziness)); mScrollLazinessSlider->setValue(mOriginalScrollLaziness); - mOverlayDetailSlider->setDimension(gcn::Rectangle(10, 160, 75, 10)); + mOverlayDetailSlider->setDimension(gcn::Rectangle(10, 180, 75, 10)); gcn::Label *overlayDetailLabel = new gcn::Label("Ambient FX"); - overlayDetailLabel->setPosition(90, 160); - mOverlayDetailField->setPosition(180, 160); + overlayDetailLabel->setPosition(90, 180); + mOverlayDetailField->setPosition(180, 180); mOverlayDetailField->setWidth(30); switch (mOverlayDetail) { @@ -222,10 +231,10 @@ Setup_Video::Setup_Video(): } mOverlayDetailSlider->setValue(mOverlayDetail); - mParticleDetailSlider->setDimension(gcn::Rectangle(10, 180, 75, 10)); + mParticleDetailSlider->setDimension(gcn::Rectangle(10, 200, 75, 10)); gcn::Label *particleDetailLabel = new gcn::Label("Particle Detail"); - particleDetailLabel->setPosition(90, 180); - mParticleDetailField->setPosition(180, 180); + particleDetailLabel->setPosition(90, 200); + mParticleDetailField->setPosition(180, 200); mParticleDetailField->setWidth(60); switch (mParticleDetail) { @@ -248,6 +257,8 @@ Setup_Video::Setup_Video(): add(mFsCheckBox); add(mOpenGLCheckBox); add(mCustomCursorCheckBox); + add(mParticleEffectsCheckBox); + add(mSpeechBubbleCheckBox); add(mAlphaSlider); add(alphaLabel); add(mFpsCheckBox); @@ -328,6 +339,8 @@ void Setup_Video::apply() // We sync old and new values at apply time mFullScreenEnabled = config.getValue("screen", 0); mCustomCursorEnabled = config.getValue("customcursor", 1); + mParticleEffectsEnabled = config.getValue("particleeffects", 1); + mSpeechBubbleEnabled = config.getValue("speechbubble", 1); mOpacity = config.getValue("guialpha", 0.8); mOverlayDetail = (int)config.getValue("OverlayDetail", 2); mOpenGLEnabled = config.getValue("opengl", 0); @@ -370,6 +383,8 @@ void Setup_Video::cancel() config.setValue("screen", mFullScreenEnabled ? 1 : 0); config.setValue("customcursor", mCustomCursorEnabled ? 1 : 0); + config.setValue("particleeffects", mParticleEffectsEnabled ? 1 : 0); + config.setValue("speechbubble", mSpeechBubbleEnabled ? 1 : 0); config.setValue("guialpha", mOpacity); config.setValue("opengl", mOpenGLEnabled ? 1 : 0); } @@ -385,6 +400,18 @@ void Setup_Video::action(const gcn::ActionEvent &event) config.setValue("customcursor", mCustomCursorCheckBox->isSelected() ? 1 : 0); } + else if (event.getId() == "particleeffects") + { + config.setValue("particleeffects", + mParticleEffectsCheckBox->isSelected() ? 1 : 0); + new OkDialog("Particle effect settings changed", + "Restart your client or change maps for the change to take effect."); + } + else if (event.getId() == "speechbubble") + { + config.setValue("speechbubble", + mSpeechBubbleCheckBox->isSelected() ? 1 : 0); + } else if (event.getId() == "fpslimitslider") { mFps = (int) mFpsSlider->getValue(); diff --git a/src/gui/setup_video.h b/src/gui/setup_video.h index 355a321e..4103c5ef 100644 --- a/src/gui/setup_video.h +++ b/src/gui/setup_video.h @@ -22,11 +22,11 @@ #ifndef _TMW_GUI_SETUP_VIDEO_H #define _TMW_GUI_SETUP_VIDEO_H -#include "setuptab.h" - #include <guichan/actionlistener.hpp> #include <guichan/keylistener.hpp> +#include "setuptab.h" + #include "../guichanfwd.h" class Setup_Video : public SetupTab, public gcn::ActionListener, @@ -49,6 +49,8 @@ class Setup_Video : public SetupTab, public gcn::ActionListener, bool mFullScreenEnabled; bool mOpenGLEnabled; bool mCustomCursorEnabled; + bool mParticleEffectsEnabled; + bool mSpeechBubbleEnabled; double mOpacity; int mFps; @@ -58,6 +60,8 @@ class Setup_Video : public SetupTab, public gcn::ActionListener, gcn::CheckBox *mFsCheckBox; gcn::CheckBox *mOpenGLCheckBox; gcn::CheckBox *mCustomCursorCheckBox; + gcn::CheckBox *mParticleEffectsCheckBox; + gcn::CheckBox *mSpeechBubbleCheckBox; gcn::Slider *mAlphaSlider; gcn::CheckBox *mFpsCheckBox; diff --git a/src/gui/shop.cpp b/src/gui/shop.cpp index 085e93ce..a4478a62 100644 --- a/src/gui/shop.cpp +++ b/src/gui/shop.cpp @@ -23,8 +23,6 @@ #include "../utils/dtor.h" -#include <algorithm> - ShopItems::~ShopItems() { clear(); @@ -59,7 +57,7 @@ ShopItem* ShopItems::at(int i) const void ShopItems::clear() { - std::for_each(mShopItems.begin(), mShopItems.end(), make_dtor(mShopItems)); + delete_all(mShopItems); mShopItems.clear(); } diff --git a/src/gui/shop.h b/src/gui/shop.h index 915ddd15..97b8d173 100644 --- a/src/gui/shop.h +++ b/src/gui/shop.h @@ -27,10 +27,11 @@ #include <guichan/listmodel.hpp> -#include "../resources/image.h" - +#include "../guichanfwd.h" #include "../shopitem.h" +#include "../resources/image.h" + class ShopItems : public gcn::ListModel { public: diff --git a/src/gui/shoplistbox.cpp b/src/gui/shoplistbox.cpp index bce6a48c..e31eee58 100644 --- a/src/gui/shoplistbox.cpp +++ b/src/gui/shoplistbox.cpp @@ -19,14 +19,14 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "shoplistbox.h" - +#include <guichan/basiccontainer.hpp> #include <guichan/font.hpp> #include <guichan/graphics.hpp> +#include <guichan/imagefont.hpp> #include <guichan/listmodel.hpp> #include <guichan/mouseinput.hpp> -#include <guichan/imagefont.hpp> -#include <guichan/basiccontainer.hpp> + +#include "shoplistbox.h" #include "../graphics.h" diff --git a/src/gui/shoplistbox.h b/src/gui/shoplistbox.h index 75f514ab..e856c076 100644 --- a/src/gui/shoplistbox.h +++ b/src/gui/shoplistbox.h @@ -25,6 +25,8 @@ #include "listbox.h" #include "shop.h" +#include "../guichanfwd.h" + /** * A list box, meant to be used inside a scroll area. Same as the Guichan list * box except this one doesn't have a background, instead completely relying diff --git a/src/gui/skill.cpp b/src/gui/skill.cpp index a7b598eb..8e61dea4 100644 --- a/src/gui/skill.cpp +++ b/src/gui/skill.cpp @@ -23,18 +23,16 @@ #include <guichan/widgets/label.hpp> -#include "skill.h" - #include "button.h" #include "listbox.h" -#include "scrollarea.h" +#include "skill.h" #include "windowcontainer.h" #include "../localplayer.h" +#include "../log.h" #include "../utils/dtor.h" #include "../utils/xml.h" -#include "../log.h" #define SKILLS_FILE "skills.xml" @@ -79,7 +77,7 @@ public: virtual void update(void) { - static const SkillInfo fakeSkillInfo = { "Mystery Skill", false }; + static const SkillInfo fakeSkillInfo = { "???", false }; mEntriesNr = mDialog->getSkills().size(); resize(); @@ -130,7 +128,7 @@ SkillDialog::SkillDialog(): setDefaultSize(windowContainer->getWidth() - 255, 25, 240, 240); // mSkillListBox = new ListBox(this); - ScrollArea *skillScrollArea = new ScrollArea(&mTable); + skillScrollArea = new ScrollArea(&mTable); mPointsLabel = new gcn::Label("Skill Points:"); mIncButton = new Button("Up", "inc", this); mUseButton = new Button("Use", "use", this); diff --git a/src/gui/skill.h b/src/gui/skill.h index bbd950cb..2095e098 100644 --- a/src/gui/skill.h +++ b/src/gui/skill.h @@ -24,9 +24,11 @@ #include <vector> -#include <guichan/listmodel.hpp> #include <guichan/actionlistener.hpp> +#include <guichan/listmodel.hpp> +#include "scrollarea.h" +#include "table.h" #include "window.h" #include "table.h" @@ -72,6 +74,7 @@ class SkillDialog : public Window, public gcn::ActionListener private: GuiTable mTable;//gcn::ListBox *mSkillListBox; + ScrollArea *skillScrollArea; SkillGuiTableModel *mTableModel; gcn::Label *mPointsLabel; gcn::Button *mIncButton; diff --git a/src/gui/slider.h b/src/gui/slider.h index 3b796425..36bfe698 100644 --- a/src/gui/slider.h +++ b/src/gui/slider.h @@ -24,8 +24,9 @@ #include <guichan/widgets/slider.hpp> -class Image; +#include "../guichanfwd.h" +class Image; /** * Slider widget. Same as the Guichan slider but with custom look. diff --git a/src/gui/speechbubble.cpp b/src/gui/speechbubble.cpp new file mode 100644 index 00000000..6af16496 --- /dev/null +++ b/src/gui/speechbubble.cpp @@ -0,0 +1,93 @@ +/* + * The Legend of Mazzeroth + * Copyright (C) 2008, The Legend of Mazzeroth Development Team + * + * This file is part of The Legend of Mazzeroth based on original code + * from The Mana World. + * + * The Legend of Mazzeroth is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Legend of Mazzeroth is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Legend of Mazzeroth; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <guichan/font.hpp> + +#include "speechbubble.h" + +#include "../resources/image.h" +#include "../resources/resourcemanager.h" + +// TODO: Fix windows so that they can each load their own skins without the +// other windows overriding another window's skin. +SpeechBubble::SpeechBubble(): + Window("Message", false, NULL, "graphics/gui/speechbubble.xml") +{ + mSpeechBox = new TextBox(); + mSpeechBox->setEditable(false); + mSpeechBox->setOpaque(false); + + mSpeechArea = new ScrollArea(mSpeechBox); + + // Height == Top Graphic (14px) + 1 Row of Text (15px) + Bottom Graphic (17px) + setContentSize(140, 46); + setTitleBarHeight(5); + + mSpeechArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mSpeechArea->setVerticalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mSpeechArea->setDimension(gcn::Rectangle(4, 15, 130, 28)); + mSpeechArea->setOpaque(false); + + add(mSpeechArea); + + setLocationRelativeTo(getParent()); + + // LEEOR / TODO: This causes an exception error. + //moveToBottom(getParent()); + + mSpeechBox->setTextWrapped( "" ); +} + +void SpeechBubble::setText(std::string mText) +{ + while (mText[0] == ' ') + { + mText = mText.substr(1, mText.size()); + } + + mSpeechBox->setMinWidth(140); + mSpeechBox->setTextWrapped( mText ); + + int numRows = mSpeechBox->getNumberOfRows(); + + if (numRows > 1) + { + // 15 == height of each line of text (based on font heights) + // 14 == speechbubble Top + Bottom graphic pixel heights + setContentSize(mSpeechBox->getMinWidth() + 15, 15 + (numRows * 15)); + mSpeechArea->setDimension(gcn::Rectangle(4, 15, mSpeechBox->getMinWidth() + 5, + 3 +(numRows * 14))); + } + else + { + int width = getFont()->getWidth(this->getCaption()); + if (width < getFont()->getWidth(mText)) + width = getFont()->getWidth(mText); + setContentSize(width + 15, 30); + mSpeechArea->setDimension(gcn::Rectangle(4, 15, width + 5, 17)); + } +} + +unsigned int SpeechBubble::getNumRows() +{ + return mSpeechBox->getNumberOfRows(); +} diff --git a/src/gui/speechbubble.h b/src/gui/speechbubble.h new file mode 100644 index 00000000..9b8eab70 --- /dev/null +++ b/src/gui/speechbubble.h @@ -0,0 +1,45 @@ +/* + * The Legend of Mazzeroth + * Copyright (C) 2008, The Legend of Mazzeroth Development Team + * + * This file is part of The Legend of Mazzeroth based on original code + * from The Mana World. + * + * The Legend of Mazzeroth is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Legend of Mazzeroth is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Legend of Mazzeroth; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LOM_SPEECHBUBBLE_H__ +#define _LOM_SPEECHBUBBLE_H__ + +#include "scrollarea.h" +#include "textbox.h" +#include "window.h" + +class SpeechBubble : public Window +{ + public: + + SpeechBubble(); + + void setText(std::string mText); + void setLocation(int x, int y); + unsigned int getNumRows(); + + private: + TextBox *mSpeechBox; + ScrollArea *mSpeechArea; +}; + +#endif diff --git a/src/gui/status.cpp b/src/gui/status.cpp index 1a257ae8..fbcc01d6 100644 --- a/src/gui/status.cpp +++ b/src/gui/status.cpp @@ -19,12 +19,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "status.h" - #include <guichan/widgets/label.hpp> #include "button.h" #include "progressbar.h" +#include "status.h" #include "windowcontainer.h" #include "../localplayer.h" @@ -36,7 +35,6 @@ StatusWindow::StatusWindow(LocalPlayer *player): mPlayer(player) { setWindowName("Status"); - setResizable(true); setCloseButton(true); setDefaultSize((windowContainer->getWidth() - 365) / 2, (windowContainer->getHeight() - 255) / 2, 365, 275); diff --git a/src/gui/tabbedcontainer.cpp b/src/gui/tabbedcontainer.cpp index fba4e846..8e95aa7c 100644 --- a/src/gui/tabbedcontainer.cpp +++ b/src/gui/tabbedcontainer.cpp @@ -21,18 +21,23 @@ #include <algorithm> -#include "tabbedcontainer.h" - #include "button.h" +#include "tabbedcontainer.h" -#include "../utils/tostring.h" #include "../utils/dtor.h" +#include "../utils/tostring.h" -#define TABWIDTH 60 -#define TABHEIGHT 20 - -TabbedContainer::TabbedContainer(): - mActiveContent(0) +TabbedContainer::TabbedContainer(int width, int padX, int buttonHeight, + int height, int padY, int buttonsPerRow): + mActiveContent(0), + mWidth(width), + mPadX(padX), + mButtonHeight(buttonHeight), + mHeight(height), + mPadY(padY), + mButtonsPerRow(buttonsPerRow), + + mButtonWidth((width - (buttonsPerRow - 1) * padX) / buttonsPerRow - padX) { } @@ -50,13 +55,14 @@ void TabbedContainer::addTab(gcn::Widget *widget, const std::string &caption) Button *tab = new Button(caption, toString(tabNumber), this); - tab->setSize(TABWIDTH, TABHEIGHT); - add(tab, TABWIDTH * tabNumber, 0); + tab->setSize(mButtonWidth, mButtonHeight); + add(tab, (mButtonWidth + mPadX) * (tabNumber % mButtonsPerRow), + (mButtonHeight + mPadY) * (tabNumber / mButtonsPerRow)); mTabs[caption] = tab; mContents.push_back(widget); - widget->setPosition(0, TABHEIGHT); + widget->setPosition(0, mHeight); // If this is the first tab in this container, make it visible if (!mActiveContent) { @@ -81,7 +87,7 @@ void TabbedContainer::logic() if (mActiveContent) { mActiveContent->setSize( getWidth() - 2 * mActiveContent->getFrameSize(), - getHeight() - TABHEIGHT - 2 * mActiveContent->getFrameSize()); + getHeight() - mHeight - 2 * mActiveContent->getFrameSize()); } Container::logic(); diff --git a/src/gui/tabbedcontainer.h b/src/gui/tabbedcontainer.h index babf68a2..2fc41247 100644 --- a/src/gui/tabbedcontainer.h +++ b/src/gui/tabbedcontainer.h @@ -23,8 +23,8 @@ #define _TMW_TABPANE_H #include <iosfwd> -#include <vector> #include <map> +#include <vector> #include <guichan/actionlistener.hpp> @@ -35,7 +35,8 @@ class TabbedContainer : public gcn::Container, public gcn::ActionListener { public: - TabbedContainer(); + TabbedContainer(int width, int padX, int buttonHeight, + int height, int padY, int buttonsPerRow); ~TabbedContainer(); void addTab(gcn::Widget *widget, const std::string &caption); @@ -60,6 +61,14 @@ class TabbedContainer : public gcn::Container, public gcn::ActionListener std::map<gcn::Widget*, std::string> mWidgets; gcn::Widget *mActiveContent; + + int mWidth; /**< The total width of all buttons */ + int mPadX; /**< The horizontal gap between buttons */ + int mButtonHeight; /**< The height of each button */ + int mHeight; /**< Height of the panel */ + int mPadY; /**< The vertical gap between buttons */ + int mButtonsPerRow; /**< The number of buttons on each row */ + int mButtonWidth; /**< The width of each button */ }; #endif diff --git a/src/gui/table.cpp b/src/gui/table.cpp index bddfbfed..e4d7812e 100644 --- a/src/gui/table.cpp +++ b/src/gui/table.cpp @@ -19,12 +19,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <guichan/graphics.hpp> -#include <guichan/actionlistener.hpp> -#include "table.h" #include <cassert> +#include <guichan/graphics.hpp> +#include <guichan/actionlistener.hpp> +#include "table.h" class GuiTableActionListener : public gcn::ActionListener { @@ -102,6 +102,7 @@ GuiTable::setModel(TableModel *new_model) mModel->removeListener(this); } + mModel = new_model; installActionListeners(); diff --git a/src/gui/table.h b/src/gui/table.h index cef82d5d..b4c607ae 100644 --- a/src/gui/table.h +++ b/src/gui/table.h @@ -31,6 +31,7 @@ #include <guichan/widget.hpp> #include "table_model.h" + #include "../guichanfwd.h" class GuiTableActionListener; diff --git a/src/gui/table_model.cpp b/src/gui/table_model.cpp index 57da29d9..e1afef96 100644 --- a/src/gui/table_model.cpp +++ b/src/gui/table_model.cpp @@ -19,8 +19,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <guichan/widget.hpp> #include <cstdlib> + +#include <guichan/widget.hpp> + #include "table_model.h" void diff --git a/src/gui/table_model.h b/src/gui/table_model.h index 4be4e60e..a52a7561 100644 --- a/src/gui/table_model.h +++ b/src/gui/table_model.h @@ -22,9 +22,11 @@ #ifndef TMW_TABLE_MODEL_H_ #define TMW_TABLE_MODEL_H_ -#include <guichan/gui.hpp> #include <set> #include <vector> + +#include <guichan/gui.hpp> + #include "../guichanfwd.h" class TableModelListener diff --git a/src/gui/textbox.cpp b/src/gui/textbox.cpp index 619265ec..d7b589fa 100644 --- a/src/gui/textbox.cpp +++ b/src/gui/textbox.cpp @@ -19,13 +19,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "textbox.h" - #include <sstream> #include <guichan/basiccontainer.hpp> #include <guichan/font.hpp> +#include "textbox.h" + TextBox::TextBox(): gcn::TextBox() { @@ -43,6 +43,8 @@ void TextBox::setTextWrapped(const std::string &text) std::stringstream wrappedStream; std::string::size_type newlinePos, lastNewlinePos = 0; + int minWidth = 0; + int xpos; do { @@ -57,7 +59,7 @@ void TextBox::setTextWrapped(const std::string &text) std::string line = text.substr(lastNewlinePos, newlinePos - lastNewlinePos); std::string::size_type spacePos, lastSpacePos = 0; - int xpos = 0; + xpos = 0; do { @@ -73,7 +75,7 @@ void TextBox::setTextWrapped(const std::string &text) int width = getFont()->getWidth(word); - if (xpos != 0 && xpos + width < getWidth()) + if (xpos != 0 && xpos + width + getFont()->getWidth(" ") <= mMinWidth) { xpos += width + getFont()->getWidth(" "); wrappedStream << " " << word; @@ -85,10 +87,30 @@ void TextBox::setTextWrapped(const std::string &text) } else { + if (xpos > minWidth) + { + minWidth = xpos; + } + // The window wasn't big enough. Resize it and try again. + if (minWidth > mMinWidth) + { + mMinWidth = minWidth; + wrappedStream.clear(); + wrappedStream.str(""); + spacePos = 0; + lastNewlinePos = 0; + newlinePos = text.find("\n", lastNewlinePos); + line = text.substr(lastNewlinePos, newlinePos - + lastNewlinePos); + width = 0; + break; + } + else + { + wrappedStream << "\n" << word; + } xpos = width; - wrappedStream << "\n" << word; } - lastSpacePos = spacePos + 1; } while (spacePos != line.size()); @@ -97,10 +119,15 @@ void TextBox::setTextWrapped(const std::string &text) { wrappedStream << "\n"; } - lastNewlinePos = newlinePos + 1; } while (newlinePos != text.size()); + if (xpos > minWidth) + { + minWidth = xpos; + } + mMinWidth = minWidth; + gcn::TextBox::setText(wrappedStream.str()); } diff --git a/src/gui/textbox.h b/src/gui/textbox.h index 2060e377..a0f0f947 100644 --- a/src/gui/textbox.h +++ b/src/gui/textbox.h @@ -24,6 +24,8 @@ #include <guichan/widgets/textbox.hpp> +#include "../guichanfwd.h" + /** * A text box, meant to be used inside a scroll area. Same as the Guichan text * box except this one doesn't have a background or border, instead completely @@ -42,6 +44,19 @@ class TextBox : public gcn::TextBox { * Sets the text after wrapping it to the current width of the widget. */ void setTextWrapped(const std::string &text); + + /** + * Get the minimum text width for the text box. + */ + int getMinWidth() { return mMinWidth; } + + /** + * Set the minimum text width for the text box. + */ + void setMinWidth(int width) { mMinWidth = width; } + + private: + int mMinWidth; }; #endif diff --git a/src/gui/textfield.cpp b/src/gui/textfield.cpp index ea82ba77..bd016a8d 100644 --- a/src/gui/textfield.cpp +++ b/src/gui/textfield.cpp @@ -23,6 +23,8 @@ #include <guichan/font.hpp> +#include <guichan/sdl/sdlinput.hpp> + #include "textfield.h" #include "../graphics.h" @@ -36,7 +38,9 @@ int TextField::instances = 0; ImageRect TextField::skin; TextField::TextField(const std::string& text): - gcn::TextField(text) + gcn::TextField(text), + mNumeric(false), + mListener(0) { setFrameSize(2); @@ -100,3 +104,67 @@ void TextField::drawFrame(gcn::Graphics *graphics) static_cast<Graphics*>(graphics)->drawImageRect(0, 0, w, h, skin); } + +void TextField::setNumeric(bool numeric) +{ + mNumeric = numeric; + if (!numeric) + { + return; + } + const char *text = mText.c_str(); + for (const char *textPtr = text; *textPtr; ++textPtr) + { + if (*textPtr < '0' || *textPtr > '9') + { + setText(mText.substr(0, textPtr - text)); + return; + } + } +} + +void TextField::keyPressed(gcn::KeyEvent &keyEvent) +{ + if (mNumeric) + { + while (true) + { + const gcn::Key &key = keyEvent.getKey(); + if (key.isNumber()) + { + break; + } + int value = key.getValue(); + if (value == SDLK_LEFT || value == SDLK_RIGHT || + value == SDLK_HOME || value == SDLK_END || + value == SDLK_BACKSPACE || value == SDLK_DELETE) + { + break; + } + return; + } + } + gcn::TextField::keyPressed(keyEvent); + if (mListener) + { + mListener->listen(this); + } +} + +int TextField::getValue() const +{ + if (!mNumeric) + { + return 0; + } + int value = atoi(mText.c_str()); + if (value < mMinimum) + { + return mMinimum; + } + if (value > mMaximum) + { + return mMaximum; + } + return value; +} diff --git a/src/gui/textfield.h b/src/gui/textfield.h index 60a50c69..6def784d 100644 --- a/src/gui/textfield.h +++ b/src/gui/textfield.h @@ -24,8 +24,18 @@ #include <guichan/widgets/textfield.hpp> +#include "../guichanfwd.h" + class ImageRect; +class TextField; + +class TextFieldListener +{ + public: + virtual void listen(const TextField *value) = 0; +}; + /** * A text field. * @@ -53,9 +63,48 @@ class TextField : public gcn::TextField { */ void drawFrame(gcn::Graphics *graphics); + /** + * Determine whether the field should be numeric or not + */ + void setNumeric(bool numeric); + + /** + * Set the range on the field if it is numeric + */ + void setRange(int min, int max) {mMinimum = min; mMaximum = max; } + + /** + * Restrict keyboard input if numeric + */ + void keyPressed(gcn::KeyEvent &keyEvent); + + /** + * Set the minimum value for a range + */ + void setMinimum(int min) {mMinimum = min; } + + /** + * Set the maximum value for a range + */ + void setMaximum(int max) {mMaximum = max; } + + /** + * Return the value for a numeric field + */ + int getValue() const; + + /** + * Add a listener + */ + void addListener(TextFieldListener *listener) {mListener = listener; } + private: static int instances; static ImageRect skin; + bool mNumeric; + int mMinimum; + int mMaximum; + TextFieldListener *mListener; }; #endif diff --git a/src/gui/trade.cpp b/src/gui/trade.cpp index 9cb0d34f..8c02ab01 100644 --- a/src/gui/trade.cpp +++ b/src/gui/trade.cpp @@ -19,8 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "trade.h" - #include <sstream> #include <guichan/widgets/label.hpp> @@ -32,9 +30,11 @@ #include "itemcontainer.h" #include "scrollarea.h" #include "textfield.h" +#include "trade.h" #include "../inventory.h" #include "../item.h" +#include "../localplayer.h" #include "../net/messageout.h" #include "../net/protocol.h" @@ -46,8 +46,8 @@ TradeWindow::TradeWindow(Network *network): Window("Trade: You"), mNetwork(network), - mMyInventory(new Inventory()), - mPartnerInventory(new Inventory()) + mMyInventory(new Inventory(INVENTORY_SIZE)), + mPartnerInventory(new Inventory(INVENTORY_SIZE)) { setWindowName("Trade"); setDefaultSize(115, 197, 332, 209); @@ -57,14 +57,14 @@ TradeWindow::TradeWindow(Network *network): mCancelButton = new Button("Cancel", "cancel", this); mTradeButton = new Button("Trade", "trade", this); - mMyItemContainer = new ItemContainer(mMyInventory.get()); + mMyItemContainer = new ItemContainer(mMyInventory.get(), 2); mMyItemContainer->addSelectionListener(this); mMyItemContainer->setPosition(2, 2); mMyScroll = new ScrollArea(mMyItemContainer); mMyScroll->setPosition(8, 8); - mPartnerItemContainer = new ItemContainer(mPartnerInventory.get()); + mPartnerItemContainer = new ItemContainer(mPartnerInventory.get(), 2); mPartnerItemContainer->addSelectionListener(this); mPartnerItemContainer->setPosition(2, 58); diff --git a/src/gui/updatewindow.cpp b/src/gui/updatewindow.cpp index 7f7d45fc..36d00bec 100644 --- a/src/gui/updatewindow.cpp +++ b/src/gui/updatewindow.cpp @@ -19,8 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "updatewindow.h" - #include <iostream> #include <SDL.h> #include <SDL_thread.h> @@ -32,6 +30,7 @@ #include "button.h" #include "progressbar.h" #include "scrollarea.h" +#include "updatewindow.h" // Curl should be included after Guichan to avoid Windows redefinitions #include <curl/curl.h> @@ -40,10 +39,10 @@ #include "../log.h" #include "../main.h" -#include "../utils/tostring.h" - #include "../resources/resourcemanager.h" +#include "../utils/tostring.h" + /** * Calculates the Alder-32 checksum for the given file. */ @@ -113,7 +112,7 @@ UpdaterWindow::UpdaterWindow(const std::string &updateHost, mBrowserBox = new BrowserBox(); mScrollArea = new ScrollArea(mBrowserBox); mLabel = new gcn::Label("Connecting..."); - mProgressBar = new ProgressBar(0.0, w - 10, 20, 37, 70, 200); + mProgressBar = new ProgressBar(0.0, w - 10, 20, 168, 116, 31); mCancelButton = new Button("Cancel", "cancel", this); mPlayButton = new Button("Play", "play", this); diff --git a/src/gui/updatewindow.h b/src/gui/updatewindow.h index a7dfe2cb..9cd3405e 100644 --- a/src/gui/updatewindow.h +++ b/src/gui/updatewindow.h @@ -23,6 +23,7 @@ #define _UPDATERWINDOW_H #include <guichan/actionlistener.hpp> + #include <string> #include <vector> diff --git a/src/gui/vbox.h b/src/gui/vbox.h index 2072ab24..4538338f 100644 --- a/src/gui/vbox.h +++ b/src/gui/vbox.h @@ -24,6 +24,8 @@ #include "box.h" +#include "../guichanfwd.h" + class VBox : public Box { public: diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp index 37e7bcce..e50903b6 100644 --- a/src/gui/viewport.cpp +++ b/src/gui/viewport.cpp @@ -19,18 +19,19 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "viewport.h" +#include <cassert> #include <guichan/sdl/sdlinput.hpp> #include "gui.h" #include "popupmenu.h" +#include "viewport.h" -#include "../simpleanimation.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" @@ -38,14 +39,14 @@ #include "../textmanager.h" #include "../resources/animation.h" -#include "../resources/monsterinfo.h" -#include "../resources/resourcemanager.h" #include "../resources/image.h" #include "../resources/imageset.h" +#include "../resources/monsterinfo.h" +#include "../resources/resourcemanager.h" #include "../utils/tostring.h" -#include <cassert> +extern volatile int tick_time; extern volatile int tick_time; @@ -71,64 +72,11 @@ Viewport::Viewport(): config.addListener("ScrollRadius", this); mPopupMenu = new PopupMenu(); - - // Load target cursors - loadTargetCursor("graphics/gui/target-cursor-blue-s.png", 44, 35, - false, Being::TC_SMALL); - loadTargetCursor("graphics/gui/target-cursor-red-s.png", 44, 35, - true, Being::TC_SMALL); - loadTargetCursor("graphics/gui/target-cursor-blue-m.png", 62, 44, - false, Being::TC_MEDIUM); - loadTargetCursor("graphics/gui/target-cursor-red-m.png", 62, 44, - true, Being::TC_MEDIUM); - loadTargetCursor("graphics/gui/target-cursor-blue-l.png", 82, 60, - false, Being::TC_LARGE); - loadTargetCursor("graphics/gui/target-cursor-red-l.png", 82, 60, - true, Being::TC_LARGE); -} - -void -Viewport::loadTargetCursor(std::string filename, int width, int height, - bool outRange, Being::TargetCursorSize size) -{ - assert(size > -1); - assert(size < 3); - - ImageSet* currentImageSet; - SimpleAnimation* currentCursor; - - ResourceManager *resman = ResourceManager::getInstance(); - - currentImageSet = resman->getImageSet(filename, width, height); - Animation *anim = new Animation(); - for (unsigned int i = 0; i < currentImageSet->size(); ++i) - { - anim->addFrame(currentImageSet->get(i), 75, 0, 0); - } - currentCursor = new SimpleAnimation(anim); - - if (outRange) - { - mOutRangeImages[size] = currentImageSet; - mTargetCursorOutRange[size] = currentCursor; - } - else { - mInRangeImages[size] = currentImageSet; - mTargetCursorInRange[size] = currentCursor; - } } Viewport::~Viewport() { delete mPopupMenu; - - for (int i = Being::TC_SMALL; i < Being::NUM_TC; i++) - { - delete mTargetCursorInRange[i]; - delete mTargetCursorOutRange[i]; - mInRangeImages[i]->decRef(); - mOutRangeImages[i]->decRef(); - } } void @@ -224,34 +172,29 @@ Viewport::draw(gcn::Graphics *gcnGraphics) if (mMap) { mMap->draw(graphics, (int) mPixelViewX, (int) mPixelViewY); - drawTargetCursor(graphics); // TODO: Draw the cursor with the sprite - } - // Find a path from the player to the mouse, and draw it. This is for debug - // purposes. - if (mShowDebugPath && mMap) - { - // Get the current mouse position - int mouseX, mouseY; - SDL_GetMouseState(&mouseX, &mouseY); + // Find a path from the player to the mouse, and draw it. This is for debug + // purposes. + if (mShowDebugPath) + { + // Get the current mouse position + int mouseX, mouseY; + SDL_GetMouseState(&mouseX, &mouseY); - int mouseTileX = mouseX / 32 + mTileViewX; - int mouseTileY = mouseY / 32 + mTileViewY; + int mouseTileX = mouseX / 32 + mTileViewX; + int mouseTileY = mouseY / 32 + mTileViewY; - Path debugPath = mMap->findPath( - player_node->mX, player_node->mY, - mouseTileX, mouseTileY); + Path debugPath = mMap->findPath(player_node->mX, player_node->mY, mouseTileX, mouseTileY); - graphics->setColor(gcn::Color(255, 0, 0)); - for (PathIterator i = debugPath.begin(); i != debugPath.end(); i++) - { - int squareX = i->x * 32 - (int) mPixelViewX + 12; - int squareY = i->y * 32 - (int) mPixelViewY + 12; + graphics->setColor(gcn::Color(255, 0, 0)); + for (PathIterator i = debugPath.begin(); i != debugPath.end(); i++) + { + int squareX = i->x * 32 - (int) mPixelViewX + 12; + int squareY = i->y * 32 - (int) mPixelViewY + 12; - graphics->fillRectangle(gcn::Rectangle(squareX, squareY, 8, 8)); - graphics->drawText( - toString(mMap->getMetaTile(i->x, i->y)->Gcost), - squareX + 4, squareY + 12, gcn::Graphics::CENTER); + graphics->fillRectangle(gcn::Rectangle(squareX, squareY, 8, 8)); + graphics->drawText(toString(mMap->getMetaTile(i->x, i->y)->Gcost), squareX + 4, squareY + 12, gcn::Graphics::CENTER); + } } } @@ -272,8 +215,7 @@ Viewport::draw(gcn::Graphics *gcnGraphics) WindowContainer::draw(gcnGraphics); } -void -Viewport::logic() +void Viewport::logic() { WindowContainer::logic(); @@ -290,49 +232,9 @@ Viewport::logic() mouseY / 32 + mTileViewY); mWalkTime = player_node->mWalkTime; } - - for (int i = 0; i < 3; i++) - { - mTargetCursorInRange[i]->update(10); - mTargetCursorOutRange[i]->update(10); - } -} - -void -Viewport::drawTargetCursor(Graphics *graphics) -{ - // Draw target marker if needed - Being *target = player_node->getTarget(); - if (target) - { - // Calculate target circle position - - // Find whether target is in range - int rangeX = abs(target->mX - player_node->mX); - int rangeY = abs(target->mY - player_node->mY); - int attackRange = player_node->getAttackRange(); - - // Get the correct target cursors graphic - Being::TargetCursorSize cursorSize = target->getTargetCursorSize(); - Image* targetCursor; - if (rangeX > attackRange || rangeY > attackRange) - { - targetCursor = mTargetCursorOutRange[cursorSize]->getCurrentImage(); - } - else { - targetCursor = mTargetCursorInRange[cursorSize]->getCurrentImage(); - } - - // Draw the target cursor at the correct position - int posX = target->getPixelX() + 16 - targetCursor->getWidth() / 2 - (int) mPixelViewX; - int posY = target->getPixelY() + 16 - targetCursor->getHeight() / 2 - (int) mPixelViewY; - - graphics->drawImage(targetCursor, posX, posY); - } } -void -Viewport::mousePressed(gcn::MouseEvent &event) +void Viewport::mousePressed(gcn::MouseEvent &event) { // Check if we are alive and kickin' if (!mMap || !player_node || player_node->mAction == Being::DEAD) @@ -344,8 +246,10 @@ Viewport::mousePressed(gcn::MouseEvent &event) mPlayerFollowMouse = false; - int tilex = event.getX() / 32 + mTileViewX; - int tiley = event.getY() / 32 + mTileViewY; + const int tilex = event.getX() / 32 + mTileViewX; + const int tiley = event.getY() / 32 + mTileViewY; + const int x = (int)((float) event.getX() + mPixelViewX); + const int y = (int)((float) event.getY() + mPixelViewY); // Right click might open a popup if (event.getButton() == gcn::MouseEvent::RIGHT) @@ -353,11 +257,11 @@ Viewport::mousePressed(gcn::MouseEvent &event) Being *being; FloorItem *floorItem; - if ((being = beingManager->findBeing(tilex, tiley)) && - being != player_node) + if ((being = beingManager->findBeingByPixel(x, y)) && + being != player_node) { - mPopupMenu->showPopup(event.getX(), event.getY(), being); - return; + mPopupMenu->showPopup(event.getX(), event.getY(), being); + return; } else if((floorItem = floorItemManager->findByCoordinates(tilex, tiley))) { @@ -378,11 +282,9 @@ Viewport::mousePressed(gcn::MouseEvent &event) { Being *being; FloorItem *item; - + // Interact with some being -// if ((being = beingManager->findBeing(tilex, tiley)) - int x = (int)((float) event.getX() + mPixelViewX); - int y = (int)((float) event.getY() + mPixelViewY); +// if ((being = beingManager->findBeing(tilex, tiley))) if ((being = beingManager->findBeingByPixel(x, y))) { switch (being->getType()) @@ -396,59 +298,47 @@ Viewport::mousePressed(gcn::MouseEvent &event) if (being->mAction == Being::DEAD) break; - if (player_node->withinAttackRange(being)) + if (player_node->withinAttackRange(being) || keyboard.isKeyActive(keyboard.KEY_ATTACK)) { - player_node->attack(being, true); + player_node->setGotoTarget(being); + player_node->attack(being, !keyboard.isKeyActive(keyboard.KEY_TARGET)); } else { - Uint8 *keys = SDL_GetKeyState(NULL); - if (!(keys[SDLK_LSHIFT] || keys[SDLK_RSHIFT])) - { - player_node->stopAttack(); - player_node->setGotoTarget(being); - } + player_node->setDestination(tilex, tiley); } break; default: break; - } + } } // Pick up some item else if ((item = floorItemManager->findByCoordinates(tilex, tiley))) { - player_node->pickUp(item); + player_node->pickUp(item); } // Just walk around else { - // XXX XXX XXX REALLY UGLY! - Uint8 *keys = SDL_GetKeyState(NULL); - if (!(keys[SDLK_LSHIFT] || keys[SDLK_RSHIFT])) - { - player_node->setDestination(tilex, tiley); - player_node->stopAttack(); - } + player_node->stopAttack(); + player_node->setDestination(tilex, tiley); mPlayerFollowMouse = true; } } else if (event.getButton() == gcn::MouseEvent::MIDDLE) { // Find the being nearest to the clicked position - Being *target = beingManager->findNearestLivingBeing( - tilex, tiley, - 20, Being::MONSTER); - + Being *target = beingManager->findBeingByPixel(x, y); + if (target) { - player_node->setTarget(target); + player_node->setTarget(target); } } } -void -Viewport::mouseDragged(gcn::MouseEvent &event) +void Viewport::mouseDragged(gcn::MouseEvent &event) { if (!mMap || !player_node) return; @@ -461,20 +351,17 @@ Viewport::mouseDragged(gcn::MouseEvent &event) } } -void -Viewport::mouseReleased(gcn::MouseEvent &event) +void Viewport::mouseReleased(gcn::MouseEvent &event) { mPlayerFollowMouse = false; } -void -Viewport::showPopup(int x, int y, Item *item) +void Viewport::showPopup(int x, int y, Item *item) { mPopupMenu->showPopup(x, y, item); } -void -Viewport::optionChanged(const std::string &name) +void Viewport::optionChanged(const std::string &name) { mScrollLaziness = (int) config.getValue("ScrollLaziness", 32); mScrollRadius = (int) config.getValue("ScrollRadius", 32); diff --git a/src/gui/viewport.h b/src/gui/viewport.h index 707ad33b..3120de91 100644 --- a/src/gui/viewport.h +++ b/src/gui/viewport.h @@ -26,8 +26,9 @@ #include "windowcontainer.h" -#include "../configlistener.h" #include "../being.h" +#include "../configlistener.h" +#include "../guichanfwd.h" class Map; class FloorItem; @@ -35,7 +36,6 @@ class ImageSet; class Item; class PopupMenu; class Graphics; -class SimpleAnimation; /** * The viewport on the map. Displays the current map and handles mouse input @@ -115,16 +115,16 @@ class Viewport : public WindowContainer, public gcn::MouseListener, optionChanged(const std::string &name); /** - * Returns camera x offset in tiles. + * Returns camera x offset in pixels. */ int - getCameraX() { return mTileViewX; } + getCameraX() const { return (int) mPixelViewX; } /** - * Returns camera y offset in tiles. + * Returns camera y offset in pixels. */ int - getCameraY() { return mTileViewY; } + getCameraY() const { return (int) mPixelViewY; } /** * Changes viewpoint by relative pixel coordinates. @@ -133,26 +133,6 @@ class Viewport : public WindowContainer, public gcn::MouseListener, scrollBy(float x, float y) { mPixelViewX += x; mPixelViewY += y; } private: - /** - * Helper function for loading target cursors - */ - void - loadTargetCursor(std::string filename, int width, int height, - bool outRange, Being::TargetCursorSize size); - - /** - * Draws range based target cursor - */ - void - drawTargetCursor(Graphics *graphics); - - /** - * Draws target name - */ - void - drawTargetName(Graphics *graphics); - - Map *mMap; /**< The current map. */ int mScrollRadius; @@ -165,18 +145,6 @@ class Viewport : public WindowContainer, public gcn::MouseListener, int mTileViewY; /**< Current viewpoint in tiles. */ bool mShowDebugPath; /**< Show a path from player to pointer. */ - /** Images of in range target cursor. */ - ImageSet *mInRangeImages[Being::NUM_TC]; - - /** Images of out of range target cursor. */ - ImageSet *mOutRangeImages[Being::NUM_TC]; - - /** Animated in range target cursor. */ - SimpleAnimation *mTargetCursorInRange[Being::NUM_TC]; - - /** Animated out of range target cursor. */ - SimpleAnimation *mTargetCursorOutRange[Being::NUM_TC]; - bool mPlayerFollowMouse; int mWalkTime; diff --git a/src/gui/widgets/dropdown.cpp b/src/gui/widgets/dropdown.cpp new file mode 100644 index 00000000..88a12d68 --- /dev/null +++ b/src/gui/widgets/dropdown.cpp @@ -0,0 +1,167 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <algorithm> + +#include "dropdown.h" + +#include "../../graphics.h" + +#include "../../resources/image.h" +#include "../../resources/resourcemanager.h" + +#include "../../utils/dtor.h" + +int DropDown::instances = 0; +Image *DropDown::buttons[2][2]; +ImageRect DropDown::skin; + +DropDown::DropDown(gcn::ListModel *listModel, + gcn::ScrollArea *scrollArea, + gcn::ListBox *listBox): + gcn::DropDown::DropDown(listModel, + scrollArea, listBox) +{ + setFrameSize(2); + + // Initialize graphics + if (instances == 0) + { + // Load the background skin + ResourceManager *resman = ResourceManager::getInstance(); + + // Get the button skin + buttons[1][0] = + resman->getImage("graphics/gui/vscroll_up_default.png"); + buttons[0][0] = + resman->getImage("graphics/gui/vscroll_down_default.png"); + buttons[1][1] = + resman->getImage("graphics/gui/vscroll_up_pressed.png"); + buttons[0][1] = + resman->getImage("graphics/gui/vscroll_down_pressed.png"); + + // get the border skin + Image *boxBorder = resman->getImage("graphics/gui/deepbox.png"); + int gridx[4] = {0, 3, 28, 31}; + int gridy[4] = {0, 3, 28, 31}; + int a = 0, x, y; + + for (y = 0; y < 3; y++) { + for (x = 0; x < 3; x++) { + skin.grid[a] = boxBorder->getSubImage( + gridx[x], gridy[y], + gridx[x + 1] - gridx[x] + 1, + gridy[y + 1] - gridy[y] + 1); + a++; + } + } + + boxBorder->decRef(); + } + + instances++; +} + +DropDown::~DropDown() +{ + instances--; + // Free images memory + if (instances == 0) + { + buttons[0][0]->decRef(); + buttons[0][1]->decRef(); + buttons[1][0]->decRef(); + buttons[1][1]->decRef(); + + for_each(skin.grid, skin.grid + 9, dtor<Image*>()); + } +} + +void DropDown::draw(gcn::Graphics* graphics) +{ + int h; + + if (mDroppedDown) + { + h = mFoldedUpHeight; + } + else + { + h = getHeight(); + } + + int alpha = getBaseColor().a; + gcn::Color faceColor = getBaseColor(); + faceColor.a = alpha; + gcn::Color highlightColor = faceColor + 0x303030; + highlightColor.a = alpha; + gcn::Color shadowColor = faceColor - 0x303030; + shadowColor.a = alpha; + + + graphics->setColor(getBackgroundColor()); + graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), h)); + + graphics->setColor(getForegroundColor()); + graphics->setFont(getFont()); + + if (mListBox->getListModel() && mListBox->getSelected() >= 0) + { + graphics->drawText(mListBox->getListModel()->getElementAt(mListBox->getSelected()), 1, 0); + } + + if (isFocused()) + { + graphics->setColor(highlightColor); + graphics->drawRectangle(gcn::Rectangle(0, 0, getWidth() - h, h)); + } + + drawButton(graphics); + + if (mDroppedDown) + { + drawChildren(graphics); + + // Draw two lines separating the ListBox with se selected + // element view. + graphics->setColor(highlightColor); + graphics->drawLine(0, h, getWidth(), h); + graphics->setColor(shadowColor); + graphics->drawLine(0, h + 1, getWidth(), h + 1); + } +} + +void DropDown::drawFrame(gcn::Graphics *graphics) +{ + const int bs = getFrameSize(); + const int w = getWidth() + bs * 2; + const int h = getHeight() + bs * 2; + + static_cast<Graphics*>(graphics)->drawImageRect(0, 0, w, h, skin); +} + +void DropDown::drawButton(gcn::Graphics *graphics) +{ + int height = mDroppedDown ? mFoldedUpHeight : getHeight(); + + static_cast<Graphics*>(graphics)-> + drawImage(buttons[mDroppedDown][mPushed], getWidth() - height + 2, 1); +} diff --git a/src/gui/widgets/dropdown.h b/src/gui/widgets/dropdown.h new file mode 100644 index 00000000..25ae05f8 --- /dev/null +++ b/src/gui/widgets/dropdown.h @@ -0,0 +1,86 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DROPDOWN_H +#define DROPDOWN_H + +#include <iosfwd> + +#include <guichan/widgets/dropdown.hpp> + +#include "../listbox.h" +#include "../scrollarea.h" + +#include "../../guichanfwd.h" + +class Image; +class ImageRect; + + /** + * A drop down box from which you can select different values. It is one of + * the most complicated Widgets you will find in Guichan. For drawing the + * DroppedDown box it uses one ScrollArea and one ListBox. It also uses an + * internal FocusHandler to handle the focus of the internal ScollArea and + * ListBox. DropDown uses a ListModel to handle the list. To be able to use + * DropDown you must give DropDown an implemented ListModel which represents + * your list. + */ +class DropDown : public gcn::DropDown +{ + public: + /** + * Contructor. + * + * @param listModel the ListModel to use. + * @param scrollArea the ScrollArea to use. + * @param listBox the listBox to use. + * @see ListModel, ScrollArea, ListBox. + */ + DropDown(gcn::ListModel *listModel = NULL, + gcn::ScrollArea *scrollArea = NULL, + gcn::ListBox *listBox = NULL); + + /** + * Destructor. + */ + ~DropDown(); + + void draw(gcn::Graphics* graphics); + + void drawFrame(gcn::Graphics* graphics); + + + protected: + /** + * Draws the button with the little down arrow. + * + * @param graphics a Graphics object to draw with. + */ + void drawButton(gcn::Graphics *graphics); + + // Add own Images. + static int instances; + static Image *buttons[2][2]; + static ImageRect skin; +}; + +#endif // end DROPDOWN_H + diff --git a/src/gui/widgets/resizegrip.cpp b/src/gui/widgets/resizegrip.cpp index c3b537db..87527f0a 100644 --- a/src/gui/widgets/resizegrip.cpp +++ b/src/gui/widgets/resizegrip.cpp @@ -19,10 +19,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "resizegrip.h" - #include <guichan/graphics.hpp> +#include "resizegrip.h" + #include "../../graphics.h" #include "../../resources/image.h" @@ -31,13 +31,13 @@ Image *ResizeGrip::gripImage = 0; int ResizeGrip::mInstances = 0; -ResizeGrip::ResizeGrip() +ResizeGrip::ResizeGrip(std::string image) { if (mInstances == 0) { // Load the grip image ResourceManager *resman = ResourceManager::getInstance(); - gripImage = resman->getImage("graphics/gui/resize.png"); + gripImage = resman->getImage(image); } mInstances++; diff --git a/src/gui/widgets/resizegrip.h b/src/gui/widgets/resizegrip.h index f57eda94..acb934d2 100644 --- a/src/gui/widgets/resizegrip.h +++ b/src/gui/widgets/resizegrip.h @@ -24,6 +24,8 @@ #include <guichan/widget.hpp> +#include "../../guichanfwd.h" + class Image; /** @@ -39,7 +41,7 @@ class ResizeGrip : public gcn::Widget /** * Constructor. */ - ResizeGrip(); + ResizeGrip(std::string image = "graphics/gui/resize.png"); /** * Destructor. diff --git a/src/gui/window.cpp b/src/gui/window.cpp index 58544f7e..30456a81 100644 --- a/src/gui/window.cpp +++ b/src/gui/window.cpp @@ -20,16 +20,17 @@ */ #include <algorithm> +#include <cassert> #include <climits> #include <cassert> #include <guichan/exception.hpp> -#include <guichan/widgets/icon.hpp> -#include "window.h" +#include <guichan/widgets/icon.hpp> #include "gui.h" #include "gccontainer.h" +#include "window.h" #include "windowcontainer.h" #include "widgets/resizegrip.h" @@ -42,24 +43,28 @@ #include "../resources/image.h" #include "../resources/resourcemanager.h" +#include "../utils/xml.h" + ConfigListener *Window::windowConfigListener = 0; WindowContainer *Window::windowContainer = 0; int Window::instances = 0; int Window::mouseResize = 0; -ImageRect Window::border; +//ImageRect Window::border; Image *Window::closeImage = NULL; +bool Window::mAlphaChanged = false; class WindowConfigListener : public ConfigListener { void optionChanged(const std::string &) { - for_each(Window::border.grid, Window::border.grid + 9, - std::bind2nd(std::mem_fun(&Image::setAlpha), - config.getValue("guialpha", 0.8))); + Window::mAlphaChanged = true; +// for_each(Window::border.grid, Window::border.grid + 9, +// std::bind2nd(std::mem_fun(&Image::setAlpha), +// config.getValue("guialpha", 0.8))); } }; -Window::Window(const std::string& caption, bool modal, Window *parent): +Window::Window(const std::string& caption, bool modal, Window *parent, const std::string& skin): gcn::Window(caption), mGrip(0), mParent(parent), @@ -71,7 +76,8 @@ Window::Window(const std::string& caption, bool modal, Window *parent): mMinWinWidth(100), mMinWinHeight(40), mMaxWinWidth(INT_MAX), - mMaxWinHeight(INT_MAX) + mMaxWinHeight(INT_MAX), + mSkin(skin) { logger->log("Window::Window(\"%s\")", caption.c_str()); @@ -79,23 +85,13 @@ Window::Window(const std::string& caption, bool modal, Window *parent): throw GCN_EXCEPTION("Window::Window. no windowContainer set"); } + // Loads the skin + loadSkin(mSkin); + + setGuiAlpha(); + if (instances == 0) { - // Load static resources - ResourceManager *resman = ResourceManager::getInstance(); - Image *dBorders = resman->getImage("graphics/gui/vscroll_grey.png"); - border.grid[0] = dBorders->getSubImage(0, 0, 4, 4); - border.grid[1] = dBorders->getSubImage(4, 0, 3, 4); - border.grid[2] = dBorders->getSubImage(7, 0, 4, 4); - border.grid[3] = dBorders->getSubImage(0, 4, 4, 10); - border.grid[4] = resman->getImage("graphics/gui/bg_quad_dis.png"); - border.grid[5] = dBorders->getSubImage(7, 4, 4, 10); - border.grid[6] = dBorders->getSubImage(0, 15, 4, 4); - border.grid[7] = dBorders->getSubImage(4, 15, 3, 4); - border.grid[8] = dBorders->getSubImage(7, 15, 4, 4); - dBorders->decRef(); - closeImage = resman->getImage("graphics/gui/close_button.png"); - windowConfigListener = new WindowConfigListener(); // Send GUI alpha changed for initialization windowConfigListener->optionChanged("guialpha"); @@ -147,22 +143,19 @@ Window::~Window() instances--; + // Clean up static resources + for( int i = 0; i < 9; i++ ) + { + delete border.grid[i]; + border.grid[i] = NULL; + } + if (instances == 0) { config.removeListener("guialpha", windowConfigListener); delete windowConfigListener; windowConfigListener = NULL; - // Clean up static resources - delete border.grid[0]; - delete border.grid[1]; - delete border.grid[2]; - delete border.grid[3]; - border.grid[4]->decRef(); - delete border.grid[5]; - delete border.grid[6]; - delete border.grid[7]; - delete border.grid[8]; closeImage->decRef(); } @@ -537,3 +530,185 @@ int Window::getResizeHandles(gcn::MouseEvent &event) return resizeHandles; } + +void Window::setGuiAlpha() +{ + //logger->log("Window::setGuiAlpha: Alpha Value %f", config.getValue("guialpha", 0.8)); + for(int i = 0; i < 9; i++) + { + //logger->log("Window::setGuiAlpha: Border Image (%i)", i); + border.grid[i]->setAlpha(config.getValue("guialpha", 0.8)); + } + + mAlphaChanged = false; +} + +void Window::loadSkin(const std::string filename) +{ + const std::string windowId = Window::getId(); + + ResourceManager *resman = ResourceManager::getInstance(); + + logger->log("Loading Window Skin '%s'.", filename.c_str()); + logger->log("Loading Window ID '%s'.", windowId.c_str()); + + + if(filename == "") + logger->error("Window::loadSkin(): Invalid File Name."); + + // TODO: + // If there is an error loading the specified file, we should try to revert + // to a 'default' skin file. Only if the 'default' skin file can't be loaded + // should we have a terminating error. + XML::Document doc(filename); + xmlNodePtr rootNode = doc.rootNode(); + + if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "skinset")) + { + logger->error("Widget Skinning error"); + } + + std::string skinSetImage; + skinSetImage = XML::getProperty(rootNode, "image", ""); + Image *dBorders = NULL; + if(skinSetImage != "") + { + logger->log("Window::loadSkin(): <skinset> defines '%s' as a skin image.", skinSetImage.c_str()); + dBorders = resman->getImage("graphics/gui/" + skinSetImage);//"graphics/gui/speech_bubble.png"); + } + else + { + logger->error("Window::loadSkin(): Skinset does not define an image!"); + } + + //iterate <widget>'s + for_each_xml_child_node(widgetNode, rootNode) + { + if (!xmlStrEqual(widgetNode->name, BAD_CAST "widget")) + continue; + + std::string widgetType; + widgetType = XML::getProperty(widgetNode, "type", "unknown"); + if (widgetType == "Window") + { + // Iterate through <part>'s + // LEEOR / TODO: + // We need to make provisions to load in a CloseButton image. For now it + // can just be hard-coded. + for_each_xml_child_node(partNode, widgetNode) + { + if (!xmlStrEqual(partNode->name, BAD_CAST "part")) + { + continue; + } + + std::string partType; + partType = XML::getProperty(partNode, "type", "unknown"); + // TOP ROW + if(partType == "top-left-corner") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[0] = dBorders->getSubImage(xPos, yPos, width, height); + } + else if(partType == "top-edge") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[1] = dBorders->getSubImage(xPos, yPos, width, height); + } + else if(partType == "top-right-corner") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[2] = dBorders->getSubImage(xPos, yPos, width, height); + } + + // MIDDLE ROW + else if(partType == "left-edge") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[3] = dBorders->getSubImage(xPos, yPos, width, height); + } + else if(partType == "bg-quad") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[4] = dBorders->getSubImage(xPos, yPos, width, height); + } + else if(partType == "right-edge") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[5] = dBorders->getSubImage(xPos, yPos, width, height); + } + + // BOTTOM ROW + else if(partType == "bottom-left-corner") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[6] = dBorders->getSubImage(xPos, yPos, width, height); + } + else if(partType == "bottom-edge") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[7] = dBorders->getSubImage(xPos, yPos, width, height); + } + else if(partType == "bottom-right-corner") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[8] = dBorders->getSubImage(xPos, yPos, width, height); + } + + // Part is of an uknown type. + else + { + logger->log("Window::loadSkin(): Unknown Part Type '%s'", partType.c_str()); + } + } + } + // Widget is of an uknown type. + else + { + logger->log("Window::loadSkin(): Unknown Widget Type '%s'", widgetType.c_str()); + } + } + dBorders->decRef(); + + logger->log("Finished loading Window Skin."); + + // Hard-coded for now until we update the above code to look for window buttons. + closeImage = resman->getImage("graphics/gui/close_button.png"); +} + diff --git a/src/gui/window.h b/src/gui/window.h index 984c6f06..9380fc88 100644 --- a/src/gui/window.h +++ b/src/gui/window.h @@ -22,17 +22,17 @@ #ifndef _TMW_WINDOW_H__ #define _TMW_WINDOW_H__ -#include <guichan/widgets/window.hpp> #include <guichan/widgetlistener.hpp> +#include <guichan/widgets/window.hpp> + +#include "../graphics.h" #include "../guichanfwd.h" class ConfigListener; class GCContainer; -class ImageRect; class ResizeGrip; class WindowContainer; -class Image; /** * A window. This window can be dragged around and has a title bar. Windows are @@ -54,9 +54,10 @@ class Window : public gcn::Window, gcn::WidgetListener * @param parent The parent window. This is the window standing above * this one in the window hiearchy. When reordering, * a window will never go below its parent window. + * @param skin The location where the window's skin XML can be found. */ Window(const std::string &caption = "Window", bool modal = false, - Window *parent = NULL); + Window *parent = NULL, const std::string &skin = "graphics/gui/gui.xml"); /** * Destructor. @@ -235,6 +236,11 @@ class Window : public gcn::Window, gcn::WidgetListener */ virtual void resetToDefaultSize(); + /** + * Loads a window skin + */ + void loadSkin(const std::string filename); + enum ResizeHandles { TOP = 0x01, @@ -253,6 +259,8 @@ class Window : public gcn::Window, gcn::WidgetListener */ int getResizeHandles(gcn::MouseEvent &event); + void setGuiAlpha(); + GCContainer *mChrome; /**< Contained container */ ResizeGrip *mGrip; /**< Resize grip */ Window *mParent; /**< The parent window */ @@ -261,6 +269,7 @@ class Window : public gcn::Window, gcn::WidgetListener bool mModal; /**< Window is modal */ bool mCloseButton; /**< Window has a close button */ bool mSticky; /**< Window resists minimization */ + static bool mAlphaChanged; /**< Whether the alpha percent was changed */ int mMinWinWidth; /**< Minimum window width */ int mMinWinHeight; /**< Minimum window height */ int mMaxWinWidth; /**< Maximum window width */ @@ -269,6 +278,7 @@ class Window : public gcn::Window, gcn::WidgetListener int mDefaultY; /**< Default window Y position */ int mDefaultWidth; /**< Default window width */ int mDefaultHeight; /**< Default window height */ + std::string mSkin; /**< Name of the skin to use */ /** The window container windows add themselves to. */ static WindowContainer *windowContainer; @@ -280,7 +290,7 @@ class Window : public gcn::Window, gcn::WidgetListener static int mouseResize; /**< Active resize handles */ static int instances; /**< Number of Window instances */ - static ImageRect border; /**< The window border and background */ + ImageRect border; /**< The window border and background */ static Image *closeImage; /**< Close Button Image */ /** diff --git a/src/gui/windowcontainer.cpp b/src/gui/windowcontainer.cpp index f92e8388..d8535f73 100644 --- a/src/gui/windowcontainer.cpp +++ b/src/gui/windowcontainer.cpp @@ -19,15 +19,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <algorithm> - #include "windowcontainer.h" #include "../utils/dtor.h" void WindowContainer::logic() { - for_each(mDeathList.begin(), mDeathList.end(), make_dtor(mDeathList)); + delete_all(mDeathList); mDeathList.clear(); gcn::Container::logic(); diff --git a/src/gui/windowcontainer.h b/src/gui/windowcontainer.h index 88a13d31..d783fefd 100644 --- a/src/gui/windowcontainer.h +++ b/src/gui/windowcontainer.h @@ -24,6 +24,8 @@ #include <guichan/widgets/container.hpp> +#include "../guichanfwd.h" + /** * A window container. This container adds functionality for more convenient * widget (windows in particular) destruction. diff --git a/src/imageparticle.cpp b/src/imageparticle.cpp index 65780345..6d74801e 100644 --- a/src/imageparticle.cpp +++ b/src/imageparticle.cpp @@ -19,9 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "imageparticle.h" - #include "graphics.h" +#include "imageparticle.h" #include "resources/image.h" diff --git a/src/inventory.cpp b/src/inventory.cpp index 58c75f2c..da9aed02 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -19,11 +19,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "inventory.h" - #include <algorithm> #include <cassert> +#include "inventory.h" #include "item.h" #include "log.h" @@ -34,15 +33,16 @@ struct SlotUsed : public std::unary_function<Item*, bool> } }; -Inventory::Inventory() +Inventory::Inventory(int size): + mSize(size) { - mItems = new Item*[INVENTORY_SIZE]; - std::fill_n(mItems, INVENTORY_SIZE, (Item*) 0); + mItems = new Item*[mSize]; + std::fill_n(mItems, mSize, (Item*) 0); } Inventory::~Inventory() { - for (int i = 0; i < INVENTORY_SIZE; i++) + for (int i = 0; i < mSize; i++) delete mItems[i]; delete [] mItems; @@ -58,7 +58,7 @@ Item* Inventory::getItem(int index) const Item* Inventory::findItem(int itemId) const { - for (int i = 0; i < INVENTORY_SIZE; i++) + for (int i = 0; i < mSize; i++) { if (mItems[i] && mItems[i]->getId() == itemId) return mItems[i]; @@ -73,19 +73,11 @@ void Inventory::addItem(int id, int quantity, bool equipment) void Inventory::setItem(int index, int id, int quantity, bool equipment) { - if (index < 0 || index >= INVENTORY_SIZE) { + if (index < 0 || index >= mSize) { logger->log("Warning: invalid inventory index: %d", index); return; } - /* TODO: Check where to reenable this code. - // Dont stack equipment other than arrows. - if (equipment && !(id == 1199 || id == 529)) - mItems[index].setQuantity(quantity); - else - mItems[index].increaseQuantity(quantity); - */ - if (!mItems[index] && id > 0) { Item *item = new Item(id, quantity, equipment); item->setInvIndex(index); @@ -101,14 +93,14 @@ void Inventory::setItem(int index, int id, int quantity, bool equipment) void Inventory::clear() { - for (int i = 0; i < INVENTORY_SIZE; i++) { + for (int i = 0; i < mSize; i++) { removeItemAt(i); } } void Inventory::removeItem(int id) { - for (int i = 0; i < INVENTORY_SIZE; i++) { + for (int i = 0; i < mSize; i++) { if (mItems[i] && mItems[i]->getId() == id) { removeItemAt(i); } @@ -123,7 +115,7 @@ void Inventory::removeItemAt(int index) bool Inventory::contains(Item *item) const { - for (int i = 0; i < INVENTORY_SIZE; i++) { + for (int i = 0; i < mSize; i++) { if (mItems[i] && mItems[i]->getId() == item->getId()) { return true; } @@ -134,19 +126,19 @@ bool Inventory::contains(Item *item) const int Inventory::getFreeSlot() const { - Item **i = std::find_if(mItems + 2, mItems + INVENTORY_SIZE, + Item **i = std::find_if(mItems + 2, mItems + mSize, std::not1(SlotUsed())); - return (i == mItems + INVENTORY_SIZE) ? -1 : (i - mItems); + return (i == mItems + mSize) ? -1 : (i - mItems); } int Inventory::getNumberOfSlotsUsed() const { - return count_if(mItems, mItems + INVENTORY_SIZE, SlotUsed()); + return count_if(mItems, mItems + mSize, SlotUsed()); } int Inventory::getLastUsedSlot() const { - for (int i = INVENTORY_SIZE - 1; i >= 0; i--) { + for (int i = mSize - 1; i >= 0; i--) { if (SlotUsed()(mItems[i])) { return i; } diff --git a/src/inventory.h b/src/inventory.h index 2fbbbf4c..91bb7d04 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -32,7 +32,7 @@ class Inventory /** * Constructor. */ - Inventory(); + Inventory(int size); /** * Destructor. @@ -40,6 +40,11 @@ class Inventory ~Inventory(); /** + * Returns the size that this instance is configured for + */ + int getSize() { return mSize; } + + /** * Returns the item at the specified index. */ Item* getItem(int index) const; @@ -104,6 +109,7 @@ class Inventory protected: Item **mItems; /**< The holder of items */ + int mSize; /**< The max number of inventory items */ }; #endif diff --git a/src/itemshortcut.cpp b/src/itemshortcut.cpp index babe3dfb..cfe46238 100644 --- a/src/itemshortcut.cpp +++ b/src/itemshortcut.cpp @@ -19,11 +19,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "itemshortcut.h" - #include "configuration.h" #include "inventory.h" #include "item.h" +#include "itemshortcut.h" #include "localplayer.h" #include "utils/tostring.h" diff --git a/src/joystick.cpp b/src/joystick.cpp index b05e9b5f..4cee4464 100644 --- a/src/joystick.cpp +++ b/src/joystick.cpp @@ -19,9 +19,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "joystick.h" +#include <cassert> #include "configuration.h" +#include "joystick.h" #include "log.h" #include <cassert> diff --git a/src/joystick.h b/src/joystick.h index ee029915..2baf3e61 100644 --- a/src/joystick.h +++ b/src/joystick.h @@ -30,7 +30,9 @@ class Joystick /** * Number of buttons we can handle. */ - enum { MAX_BUTTONS = 6 }; + enum { + MAX_BUTTONS = 6 + }; /** * Directions, to be used as bitmask values. diff --git a/src/keyboardconfig.cpp b/src/keyboardconfig.cpp index 19cbb945..f67c9534 100644 --- a/src/keyboardconfig.cpp +++ b/src/keyboardconfig.cpp @@ -19,12 +19,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "keyboardconfig.h" +#include <guichan/sdl/sdlinput.hpp> + #include "configuration.h" +#include "keyboardconfig.h" #include "log.h" -#include <guichan/sdl/sdlinput.hpp> - #include "gui/setup_keyboard.h" struct KeyData @@ -42,8 +42,10 @@ static KeyData const keyData[KeyboardConfig::KEY_TOTAL] = { {"keyMoveRight", SDLK_RIGHT, "Move Right"}, {"keyAttack", SDLK_LCTRL, "Attack"}, {"keySmilie", SDLK_LALT, "Smilie"}, - {"keyTarget", SDLK_LSHIFT, "Target"}, + {"keyTalk", SDLK_t, "Talk"}, + {"keyTarget", SDLK_LSHIFT, "Stop Attack"}, {"keyTargetClosest", SDLK_a, "Target Closest"}, + {"keyTargetNPC", SDLK_n, "Target NPC"}, {"keyTargetPlayer", SDLK_q, "Target Player"}, {"keyPickup", SDLK_z, "Pickup"}, {"keyHideWindows", SDLK_h, "Hide Windows"}, diff --git a/src/keyboardconfig.h b/src/keyboardconfig.h index 9c5fe943..158252d4 100644 --- a/src/keyboardconfig.h +++ b/src/keyboardconfig.h @@ -24,10 +24,10 @@ #include <string> -#include "gui/setup_keyboard.h" - #include <guichan/sdl/sdlinput.hpp> +#include "gui/setup_keyboard.h" + /** * Each key represents a key function. Such as 'Move up', 'Attack' etc. */ @@ -150,8 +150,10 @@ class KeyboardConfig KEY_MOVE_RIGHT, KEY_ATTACK, KEY_SMILIE, + KEY_TALK, KEY_TARGET, KEY_TARGET_CLOSEST, + KEY_TARGET_NPC, KEY_TARGET_PLAYER, KEY_PICKUP, KEY_HIDE_WINDOWS, diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 3550b092..292f70e0 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -18,15 +18,16 @@ * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "localplayer.h" +#include <cassert> #include "equipment.h" #include "floor_item.h" #include "game.h" #include "inventory.h" #include "item.h" +#include "localplayer.h" #include "main.h" +#include "monster.h" #include "particle.h" #include "sound.h" #include "monster.h" @@ -36,6 +37,9 @@ #include "net/messageout.h" #include "net/protocol.h" +#include "resources/imageset.h" +#include "resources/resourcemanager.h" + #include "utils/tostring.h" LocalPlayer *player_node = NULL; @@ -48,20 +52,44 @@ LocalPlayer::LocalPlayer(Uint32 id, Uint16 job, Map *map): mXp(0), mNetwork(0), mTarget(NULL), mPickUpTarget(NULL), mTrading(false), mGoingToTarget(false), - mLastAction(-1), - mWalkingDir(0), mDestX(0), mDestY(0), - mInventory(new Inventory) + mTargetTime(-1), mLastAction(-1), + mLastTarget(-1), mWalkingDir(0), + mDestX(0), mDestY(0), + mInventory(new Inventory(INVENTORY_SIZE)), + mStorage(new Inventory(STORAGE_SIZE)) { + initTargetCursor(); } LocalPlayer::~LocalPlayer() { delete mInventory; + delete mStorage; + + for (int i = Being::TC_SMALL; i < Being::NUM_TC; i++) + { + delete mTargetCursorInRange[i]; + delete mTargetCursorOutRange[i]; + mInRangeImages[i]->decRef(); + mOutRangeImages[i]->decRef(); + } } void LocalPlayer::logic() { switch (mAction) { + case STAND: + break; + + case SIT: + break; + + case DEAD: + break; + + case HURT: + break; + case WALK: mFrame = (get_elapsed_time(mWalkTime) * 6) / mWalkSpeed; if (mFrame >= 6) { @@ -79,7 +107,6 @@ void LocalPlayer::logic() mFrame = (get_elapsed_time(mWalkTime) * frames) / mAttackSpeed; if (mFrame >= frames) { nextStep(); - attack(); } break; } @@ -88,6 +115,35 @@ void LocalPlayer::logic() if (get_elapsed_time(mLastAction) >= 1000) { mLastAction = -1; } + // Targeting allowed 4 times a second + if (get_elapsed_time(mLastTarget) >= 250) { + mLastTarget = -1; + } + // Remove target if its been on a being for more than a minute + if (get_elapsed_time(mTargetTime) >= 60000) + { + mTargetTime = -1; + setTarget(NULL); + mLastTarget = -1; + } + + if (mTarget) + { + if (mTarget->mAction == DEAD) + { + stopAttack(); + } + if (mKeepAttacking && mTarget) + { + attack(mTarget, true); + } + + for (int i = Being::TC_SMALL; i < Being::NUM_TC; i++) + { + player_node->mTargetCursorInRange[i]->update(10); + player_node->mTargetCursorOutRange[i]->update(10); + } + } Being::logic(); } @@ -228,9 +284,19 @@ void LocalPlayer::walk(unsigned char dir) void LocalPlayer::setTarget(Being *target) { - if (target == mTarget) - { + if (mLastTarget != -1 || target == this) return; + mLastTarget = tick_time; + + if ((target == NULL) || target == mTarget) + { + target = NULL; + mKeepAttacking = false; + mTargetTime = -1; + } + if (target) + { + mTargetTime = tick_time; } if (mTarget && mTarget->getType() == Being::MONSTER) { @@ -375,25 +441,25 @@ bool LocalPlayer::tradeRequestOk() const void LocalPlayer::attack(Being *target, bool keep) { - // Can only attack when standing still - if (mAction != STAND) + mKeepAttacking = keep; + + if (!target) return; - if (keep && target) + if ((mTarget != target) || !mTarget) { + mLastTarget = -1; setTarget(target); } - else if (mTarget) - { - target = mTarget; - } - - if (!target) - return; int dist_x = target->mX - mX; int dist_y = target->mY - mY; + // Must be standing and be within attack range to continue + if ((mAction != STAND) || (mAttackRange < abs(dist_x)) || + (mAttackRange < abs(dist_y))) + return; + if (abs(dist_y) >= abs(dist_x)) { if (dist_y > 0) @@ -412,8 +478,10 @@ void LocalPlayer::attack(Being *target, bool keep) // Implement charging attacks here mLastAttackTime = 0; - setAction(ATTACK); mWalkTime = tick_time; + mTargetTime = tick_time; + + setAction(ATTACK); if (mEquippedWeapon) { @@ -428,11 +496,22 @@ void LocalPlayer::attack(Being *target, bool keep) outMsg.writeInt16(0x0089); outMsg.writeInt32(target->getId()); outMsg.writeInt8(0); + + if (!keep) + { + stopAttack(); + } } void LocalPlayer::stopAttack() { + if (mTarget) + { + setAction(STAND); + mLastTarget = -1; + } setTarget(NULL); + mLastTarget = -1; } Being* LocalPlayer::getTarget() const @@ -475,7 +554,90 @@ bool LocalPlayer::withinAttackRange(Being *target) void LocalPlayer::setGotoTarget(Being *target) { + mLastTarget = -1; setTarget(target); mGoingToTarget = true; setDestination(target->mX, target->mY); } + +void LocalPlayer::initTargetCursor() +{ + // Load target cursors + loadTargetCursor("graphics/gui/target-cursor-blue-s.png", 44, 35, + false, TC_SMALL); + loadTargetCursor("graphics/gui/target-cursor-red-s.png", 44, 35, + true, TC_SMALL); + loadTargetCursor("graphics/gui/target-cursor-blue-m.png", 62, 44, + false, TC_MEDIUM); + loadTargetCursor("graphics/gui/target-cursor-red-m.png", 62, 44, + true, TC_MEDIUM); + loadTargetCursor("graphics/gui/target-cursor-blue-l.png", 82, 60, + false, TC_LARGE); + loadTargetCursor("graphics/gui/target-cursor-red-l.png", 82, 60, + true, TC_LARGE); +} + +void LocalPlayer::loadTargetCursor(std::string filename, int width, int height, + bool outRange, TargetCursorSize size) +{ + assert(size > -1); + assert(size < 3); + + ImageSet* currentImageSet; + SimpleAnimation* currentCursor; + + ResourceManager *resman = ResourceManager::getInstance(); + + currentImageSet = resman->getImageSet(filename, width, height); + Animation *anim = new Animation(); + for (unsigned int i = 0; i < currentImageSet->size(); ++i) + { + anim->addFrame(currentImageSet->get(i), 75, 0, 0); + } + currentCursor = new SimpleAnimation(anim); + + if (outRange) + { + mOutRangeImages[size] = currentImageSet; + mTargetCursorOutRange[size] = currentCursor; + } + else + { + mInRangeImages[size] = currentImageSet; + mTargetCursorInRange[size] = currentCursor; + } +} + +void LocalPlayer::drawTargetCursor(Graphics *graphics, int scrollX, int scrollY) +{ + + // Draw target marker if needed + if (mTarget) + { + // Calculate target circle position + + // Find whether target is in range + int rangeX = abs(mTarget->mX - mX); + int rangeY = abs(mTarget->mY - mY); + int attackRange = getAttackRange(); + + // Get the correct target cursors graphic + TargetCursorSize cursorSize = mTarget->getTargetCursorSize(); + + if (rangeX > attackRange || rangeY > attackRange) + { + mTarget->mTargetCursor = mTargetCursorOutRange[cursorSize]->getCurrentImage(); + } + else + { + mTarget->mTargetCursor = mTargetCursorInRange[cursorSize]->getCurrentImage(); + } + + // Draw the target cursor at the correct position + int posX = mTarget->getPixelX() + 16 - mTarget->mTargetCursor->getWidth() / 2 - scrollX; + int posY = mTarget->getPixelY() + 16 - mTarget->mTargetCursor->getHeight() / 2 - scrollY; + + graphics->drawImage(mTarget->mTargetCursor, posX, posY); + } + return; +} diff --git a/src/localplayer.h b/src/localplayer.h index 34f10a4a..ad59d138 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -23,11 +23,16 @@ #define _TMW_LOCALPLAYER_H #include "player.h" +#include "simpleanimation.h" // TODO move into some sane place... #define MAX_SLOT 2 +#define INVENTORY_SIZE 102 +#define STORAGE_SIZE 301 + class FloorItem; +class ImageSet; class Inventory; class Item; class Network; @@ -69,6 +74,11 @@ class LocalPlayer : public Player Inventory* getInventory() const { return mInventory; } /** + * Returns the player's storage + */ + Inventory* getStorage() const { return mStorage; } + + /** * Equips an item. */ void equipItem(Item *item); @@ -164,6 +174,12 @@ class LocalPlayer : public Player void revive(); /** + * Accessors for mInStorage + */ + bool getInStorage() { return mInStorage; } + void setInStorage(bool inStorage) { mInStorage = inStorage; } + + /** * Sets the amount of XP. Shows XP gaining effect if the player is on * a map. */ @@ -198,6 +214,14 @@ class LocalPlayer : public Player float mLastAttackTime; /**< Used to synchronize the charge dialog */ + void drawTargetCursor(Graphics *graphics, int offsetX, int offsetY); + + /** Animated in range target cursor. */ + SimpleAnimation *mTargetCursorInRange[NUM_TC]; + + /** Animated out of range target cursor. */ + SimpleAnimation *mTargetCursorOutRange[NUM_TC]; + protected: void walk(unsigned char dir); @@ -208,13 +232,33 @@ class LocalPlayer : public Player FloorItem *mPickUpTarget; bool mTrading; + bool mInStorage; /**< Whether storage is currently accessible */ bool mGoingToTarget; + bool mKeepAttacking;/** Whether or not to continue to attack */ + int mTargetTime; /** How long the being has been targeted **/ int mLastAction; /**< Time stamp of the last action, -1 if none. */ + int mLastTarget; /** Time stamp of last targeting action, -1 if none. */ int mWalkingDir; /**< The direction the player is walking in. */ int mDestX; /**< X coordinate of destination. */ int mDestY; /**< Y coordinate of destination. */ Inventory *mInventory; + Inventory *mStorage; + + /** + * Helper function for loading target cursors + */ + void loadTargetCursor(std::string filename, int width, int height, + bool outRange, Being::TargetCursorSize size); + + /** Images of in range target cursor. */ + ImageSet *mInRangeImages[NUM_TC]; + + /** Images of out of range target cursor. */ + ImageSet *mOutRangeImages[NUM_TC]; + + // Load the target cursors into memory + void initTargetCursor(); }; extern LocalPlayer *player_node; diff --git a/src/main.cpp b/src/main.cpp index e246b6a9..4049aa78 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,46 +19,41 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "main.h" - #include <getopt.h> #include <iostream> #include <physfs.h> +#include <SDL_image.h> #include <unistd.h> #include <vector> -#include <SDL_image.h> #include <guichan/actionlistener.hpp> + #include <guichan/sdl/sdlinput.hpp> + #include <guichan/widgets/label.hpp> #include <libxml/parser.h> -#ifndef WIN32 -#include <cerrno> -#include <sys/stat.h> -#endif -#if defined __APPLE__ -#include <CoreFoundation/CFBundle.h> -#endif - #include "configuration.h" -#include "keyboardconfig.h" -#include "player_relations.h" #include "game.h" #include "graphics.h" #include "itemshortcut.h" -#include "lockedarray.h" +#include "keyboardconfig.h" #include "localplayer.h" +#include "lockedarray.h" #include "log.h" #include "logindata.h" +#include "main.h" #ifdef USE_OPENGL #include "openglgraphics.h" #endif +#include "player_relations.h" +#include "serverinfo.h" #include "sound.h" #include "gui/char_server.h" #include "gui/char_select.h" +#include "gui/colour.h" #include "gui/gui.h" #include "gui/login.h" #include "gui/ok_dialog.h" @@ -67,6 +62,7 @@ #include "gui/setup.h" #include "gui/updatewindow.h" #include "gui/textfield.h" +#include "gui/updatewindow.h" #include "net/charserverhandler.h" #include "net/loginhandler.h" @@ -74,6 +70,7 @@ #include "net/messageout.h" #include "net/network.h" +#include "resources/colordb.h" #include "resources/image.h" #include "resources/itemdb.h" #include "resources/monsterdb.h" @@ -83,8 +80,20 @@ #include "utils/dtor.h" #include "utils/tostring.h" +#ifdef __APPLE__ +#include <CoreFoundation/CFBundle.h> +#endif + +#ifdef __MINGW32__ +#include <windows.h> +#define usleep(usec) (Sleep ((usec) / 1000), 0) +#endif + #ifdef WIN32 #include <SDL_syswm.h> +#else +#include <cerrno> +#include <sys/stat.h> #endif namespace { @@ -123,6 +132,7 @@ CharServerHandler charServerHandler; LoginData loginData; LockedArray<LocalPlayer*> charInfo(MAX_SLOT + 1); +Colour *textColour; // This anonymous namespace hides whatever is inside from other modules. namespace { @@ -168,10 +178,12 @@ struct Options */ void setUpdatesDir() { + std::stringstream updates; + // If updatesHost is currently empty, fill it from config file if (updateHost.empty()) { updateHost = - config.getValue("updatehost", "http://updates.themanaworld.org"); + config.getValue("updatehost", "http://www.aethyra.org/updates"); } // Remove any trailing slash at the end of the update host @@ -183,8 +195,9 @@ void setUpdatesDir() pos = updateHost.find("://"); if (pos != updateHost.npos) { if (pos + 3 < updateHost.length()) { - updatesDir = - "updates/" + updateHost.substr(pos + 3); + updates << "updates/" << updateHost.substr(pos + 3) + << "/" << loginData.port; + updatesDir = updates.str(); } else { logger->log("Error: Invalid update host: %s", updateHost.c_str()); errorMessage = "Invalid update host: " + updateHost; @@ -192,7 +205,8 @@ void setUpdatesDir() } } else { logger->log("Warning: no protocol was specified for the update host"); - updatesDir = "updates/" + updateHost; + updates << "updates/" << updateHost << "/" << loginData.port; + updatesDir = updates.str(); } ResourceManager *resman = ResourceManager::getInstance(); @@ -213,18 +227,18 @@ void setUpdatesDir() */ void init_engine(const Options &options) { - homeDir = std::string(PHYSFS_getUserDir()) + "/.tmw"; + homeDir = std::string(PHYSFS_getUserDir()) + "/.aethyra"; #if defined WIN32 if (!CreateDirectory(homeDir.c_str(), 0) && GetLastError() != ERROR_ALREADY_EXISTS) #elif defined __APPLE__ - // Use Application Directory instead of .tmw - homeDir = std::string(PHYSFS_getUserDir()) + - "/Library/Application Support/The Mana World"; + // Use Application Directory instead of .aethyra + homeDir = std::string(PHYSFS_getUserDir()) + + "/Library/Application Support/Aethyra"; if ((mkdir(homeDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) && (errno != EEXIST)) #else - // Checking if /home/user/.tmw folder exists. + // Checking if /home/user/.Aethyra folder exists. if ((mkdir(homeDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) && (errno != EEXIST)) #endif @@ -236,12 +250,12 @@ void init_engine(const Options &options) } // Set log file - logger->setLogFile(homeDir + std::string("/tmw.log")); + logger->setLogFile(homeDir + std::string("/aethyra.log")); #ifdef PACKAGE_VERSION - logger->log("Starting The Mana World Version %s", PACKAGE_VERSION); + logger->log("Starting Aethyra Version %s", PACKAGE_VERSION); #else - logger->log("Starting The Mana World - Version not defined"); + logger->log("Starting Aethyra - Version not defined"); #endif // Initialize SDL @@ -286,13 +300,13 @@ void init_engine(const Options &options) strncat(path, "/data", PATH_MAX - 1); resman->addToSearchPath(path, true); #else - resman->addToSearchPath(TMW_DATADIR "data", true); + resman->addToSearchPath(AETHYRA_DATADIR "data", true); #endif // Fill configuration with defaults logger->log("Initializing configuration..."); - config.setValue("host", "server.themanaworld.org"); - config.setValue("port", 6901); + config.setValue("host", "www.aethyra.org"); + config.setValue("port", 21001); config.setValue("hwaccel", 0); #if (defined __APPLE__ || defined WIN32) && defined USE_OPENGL config.setValue("opengl", 1); @@ -306,34 +320,34 @@ void init_engine(const Options &options) config.setValue("sfxVolume", 100); config.setValue("musicVolume", 60); config.setValue("fpslimit", 60); - config.setValue("updatehost", "http://updates.themanaworld.org"); + config.setValue("updatehost", "http://www.aethyra.org/updates"); config.setValue("customcursor", 1); config.setValue("ChatLogLength", 128); // Checking if the configuration file exists... otherwise creates it with // default options ! - FILE *tmwFile = 0; + FILE *aethyraFile = 0; std::string configPath = options.configPath; if (configPath.empty()) configPath = homeDir + "/config.xml"; - tmwFile = fopen(configPath.c_str(), "r"); + aethyraFile = fopen(configPath.c_str(), "r"); // If we can't read it, it doesn't exist ! - if (tmwFile == NULL) { + if (aethyraFile == NULL) { // We reopen the file in write mode and we create it - tmwFile = fopen(configPath.c_str(), "wt"); + aethyraFile = fopen(configPath.c_str(), "wt"); } - if (tmwFile == NULL) { + if (aethyraFile == NULL) { std::cout << "Can't create " << configPath << ". " "Using Defaults." << std::endl; } else { - fclose(tmwFile); + fclose(aethyraFile); config.init(configPath); } - SDL_WM_SetCaption("The Mana World", NULL); + SDL_WM_SetCaption("Aethyra", NULL); #ifdef WIN32 static SDL_SysWMinfo pInfo; SDL_GetWMInfo(&pInfo); @@ -343,7 +357,7 @@ void init_engine(const Options &options) SetClassLong(pInfo.window, GCL_HICON, (LONG) icon); } #else - SDL_Surface *icon = IMG_Load(TMW_DATADIR "data/icons/tmw.png"); + SDL_Surface *icon = IMG_Load(AETHYRA_DATADIR "data/icons/aethyra.png"); if (icon) { SDL_SetAlpha(icon, SDL_SRCALPHA, SDL_ALPHA_OPAQUE); @@ -429,6 +443,7 @@ void exit_engine() sound.close(); // Unload XML databases + ColorDB::unload(); ItemDB::unload(); MonsterDB::unload(); NPCDB::unload(); @@ -440,26 +455,28 @@ void exit_engine() void printHelp() { std::cout - << "tmw" << std::endl << std::endl + << "aethyra" << std::endl << std::endl << "Options: " << std::endl - << " -h --help : Display this help" << std::endl - << " -v --version : Display the version" << std::endl - << " -u --skipupdate : Skip the update process" << std::endl + << " -C --configfile : Configuration file to use" << std::endl << " -d --data : Directory to load game data from" << std::endl - << " -U --username : Login with this username" << std::endl - << " -P --password : Login with this password" << std::endl - << " -D --default : Bypass the login process with default settings" << std::endl + << " -D --default : Bypass the login process with default " + "settings" << std::endl + << " -h --help : Display this help" << std::endl + << " -H --updatehost : Use this update host" << std::endl << " -p --playername : Login with this player" << std::endl - << " -C --configfile : Configuration file to use" << std::endl - << " -H --updatehost : Use this update host" << std::endl; + << " -P --password : Login with this password" << std::endl + << " -u --skipupdate : Skip the update downloads" << std::endl + << " -U --username : Login with this username" << std::endl + << " -v --version : Display the version" << std::endl; } void printVersion() { #ifdef PACKAGE_VERSION - std::cout << "The Mana World version " << PACKAGE_VERSION << std::endl; + std::cout << "Aethyra version " << PACKAGE_VERSION << + std::endl; #else - std::cout << "The Mana World version " << + std::cout << "Aethyra version " << "(local build?, PACKAGE_VERSION is not defined)" << std::endl; #endif } @@ -469,16 +486,16 @@ void parseOptions(int argc, char *argv[], Options &options) const char *optstring = "hvud:U:P:Dp:C:H:"; const struct option long_options[] = { - { "help", no_argument, 0, 'h' }, - { "version", no_argument, 0, 'v' }, - { "skipupdate", no_argument, 0, 'u' }, + { "configfile", required_argument, 0, 'C' }, { "data", required_argument, 0, 'd' }, - { "username", required_argument, 0, 'U' }, - { "password", required_argument, 0, 'P' }, { "default", no_argument, 0, 'D' }, { "playername", required_argument, 0, 'p' }, - { "configfile", required_argument, 0, 'C' }, + { "password", required_argument, 0, 'P' }, + { "help", no_argument, 0, 'h' }, { "updatehost", required_argument, 0, 'H' }, + { "skipupdate", no_argument, 0, 'u' }, + { "username", required_argument, 0, 'U' }, + { "version", no_argument, 0, 'v' }, { 0 } }; @@ -490,36 +507,36 @@ void parseOptions(int argc, char *argv[], Options &options) break; switch (result) { - default: // Unknown option - case 'h': - options.printHelp = true; - break; - case 'v': - options.printVersion = true; - break; - case 'u': - options.skipUpdate = true; + case 'C': + options.configPath = optarg; break; case 'd': options.dataPath = optarg; break; - case 'U': - options.username = optarg; - break; - case 'P': - options.password = optarg; - break; case 'D': options.chooseDefault = true; break; + default: // Unknown option + case 'h': + options.printHelp = true; + break; + case 'H': + options.updateHost = optarg; + break; case 'p': options.playername = optarg; break; - case 'C': - options.configPath = optarg; + case 'P': + options.password = optarg; break; - case 'H': - options.updateHost = optarg; + case 'u': + options.skipUpdate = true; + break; + case 'U': + options.username = optarg; + break; + case 'v': + options.printVersion = true; break; } } @@ -597,6 +614,18 @@ void accountLogin(Network *network, LoginData *loginData) config.setValue("remember", loginData->remember); } +inline int MIN(int x, int y) +{ + return x < y ? x : y; +} + +void positionDialog(Window *dialog, int screenWidth, int screenHeight) +{ + dialog->setPosition( + MIN(screenWidth * 5 / 8, screenWidth - dialog->getWidth()), + MIN(screenHeight * 5 / 8, screenHeight - dialog->getHeight())); +} + void charLogin(Network *network, LoginData *loginData) { logger->log("Trying to connect to char server..."); @@ -716,13 +745,39 @@ int main(int argc, char *argv[]) if (!options.password.empty()) { loginData.password = options.password; } - loginData.hostname = config.getValue("host", "server.themanaworld.org"); - loginData.port = (short)config.getValue("port", 0); + loginData.hostname = config.getValue("host", "www.aethyra.org"); + loginData.port = (short)config.getValue("port", 21001); loginData.remember = config.getValue("remember", 0); loginData.registerLogin = false; SDLNet_Init(); Network *network = new Network(); + + // Set the most appropriate wallpaper, based on screen width + int screenWidth = (int) config.getValue("screenwidth", defaultScreenWidth); + int screenHeight = static_cast<int>(config.getValue("screenheight", + defaultScreenHeight)); + std::string wallpaperName; + + if (screenWidth <= 800) + wallpaperName = "graphics/images/login_wallpaper.png"; + else if (screenWidth <= 1024) + wallpaperName = "graphics/images/login_wallpaper_1024x768.png"; + else if (screenWidth <= 1280) + wallpaperName = "graphics/images/login_wallpaper_1280x960.png"; + else if (screenWidth <= 1440) + wallpaperName = "graphics/images/login_wallpaper_1440x1080.png"; + else + wallpaperName = "graphics/images/login_wallpaper_1600x1200.png"; + + login_wallpaper = ResourceManager::getInstance()-> getImage(wallpaperName); + + if (!login_wallpaper) + logger->log("Couldn't load %s as wallpaper", wallpaperName.c_str()); + + // Needs to be created in main, as the updater uses it + textColour = new Colour(); + while (state != EXIT_STATE) { // Handle SDL events @@ -758,16 +813,6 @@ int main(int argc, char *argv[]) } } - if (!login_wallpaper) - { - login_wallpaper = ResourceManager::getInstance()-> - getImage("graphics/images/login_wallpaper.png"); - if (!login_wallpaper) - { - logger->error("Couldn't load login_wallpaper.png"); - } - } - if (progressBar->isVisible()) { progressBar->setProgress(progressBar->getProgress() + 0.005f); @@ -796,7 +841,7 @@ int main(int argc, char *argv[]) // Reload the wallpaper in case that it was updated login_wallpaper->decRef(); login_wallpaper = ResourceManager::getInstance()-> - getImage("graphics/images/login_wallpaper.png"); + getImage(wallpaperName); break; // Those states don't cause a network disconnect @@ -835,6 +880,7 @@ int main(int argc, char *argv[]) false); // Load XML databases + ColorDB::load(); ItemDB::load(); MonsterDB::load(); NPCDB::load(); @@ -849,33 +895,48 @@ int main(int argc, char *argv[]) state = ACCOUNT_STATE; } else { currentDialog = new LoginDialog(&loginData); + positionDialog(currentDialog, screenWidth, + screenHeight); } break; case REGISTER_STATE: logger->log("State: REGISTER"); currentDialog = new RegisterDialog(&loginData); + positionDialog(currentDialog, screenWidth, screenHeight); break; case CHAR_SERVER_STATE: logger->log("State: CHAR_SERVER"); + + if (n_server == 1) + { + SERVER_INFO *si = *server_info; + loginData.hostname = iptostring(si->address); + loginData.port = si->port; + loginData.updateHost = si->updateHost; + state = UPDATE_STATE; + } + else { int nextState = (options.skipUpdate) ? LOADDATA_STATE : UPDATE_STATE; currentDialog = new ServerSelectDialog(&loginData, - nextState); - } - if (options.chooseDefault || options.playername != "") { - ((ServerSelectDialog*) currentDialog)->action( - gcn::ActionEvent(NULL, "ok")); + nextState); + positionDialog(currentDialog, screenWidth, + screenHeight); + if (options.chooseDefault || options.playername != "") + { + ((ServerSelectDialog*) currentDialog)->action( + gcn::ActionEvent(NULL, "ok")); + } } break; - case CHAR_SELECT_STATE: logger->log("State: CHAR_SELECT"); currentDialog = new CharSelectDialog(network, &charInfo, 1 - loginData.sex); - + positionDialog(currentDialog, screenWidth, screenHeight); if (((CharSelectDialog*) currentDialog)-> selectByName(options.playername)) options.chooseDefault = true; @@ -923,13 +984,21 @@ int main(int argc, char *argv[]) setUpdatesDir(); logger->log("State: UPDATE"); - currentDialog = new UpdaterWindow(updateHost, - homeDir + "/" + updatesDir); + + if (options.skipUpdate) { + state = LOADDATA_STATE; + } else { + currentDialog = new UpdaterWindow(updateHost, + homeDir + "/" + updatesDir); + positionDialog(currentDialog, screenWidth, + screenHeight); + } break; case ERROR_STATE: logger->log("State: ERROR"); currentDialog = new OkDialog("Error", errorMessage); + positionDialog(currentDialog, screenWidth, screenHeight); currentDialog->addActionListener(&errorListener); currentDialog = NULL; // OkDialog deletes itself network->disconnect(); @@ -965,8 +1034,15 @@ int main(int argc, char *argv[]) break; } } + /* + * This loop can really stress the CPU, for no reason since it's + * just constantly redrawing the wallpaper. Added the following + * usleep to limit it to 20 FPS during the login sequence + */ + usleep(50000); } + delete textColour; #ifdef PACKAGE_VERSION delete versionLabel; #endif @@ -30,8 +30,8 @@ #include "winver.h" #endif -#ifndef TMW_DATADIR -#define TMW_DATADIR "" +#ifndef AETHYRA_DATADIR +#define AETHYRA_DATADIR "" #endif /* diff --git a/src/map.cpp b/src/map.cpp index 928a0154..02b046fb 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -19,22 +19,21 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "map.h" - -#include <algorithm> #include <queue> #include "beingmanager.h" #include "configuration.h" #include "game.h" #include "graphics.h" +#include "localplayer.h" +#include "map.h" #include "particle.h" #include "sprite.h" #include "tileset.h" -#include "resources/resourcemanager.h" #include "resources/ambientoverlay.h" #include "resources/image.h" +#include "resources/resourcemanager.h" #include "utils/dtor.h" #include "utils/tostring.h" @@ -111,6 +110,7 @@ void MapLayer::draw(Graphics *graphics, // If drawing the fringe layer, make sure all sprites above this row of // tiles have been drawn if (mIsFringeLayer) { + player_node->drawTargetCursor(graphics, scrollX, scrollY); while (si != sprites.end() && (*si)->getPixelY() <= y * 32 - 32) { (*si)->draw(graphics, -scrollX, -scrollY); si++; @@ -153,9 +153,9 @@ Map::~Map() { // delete metadata, layers, tilesets and overlays delete[] mMetaTiles; - for_each(mLayers.begin(), mLayers.end(), make_dtor(mLayers)); - for_each(mTilesets.begin(), mTilesets.end(), make_dtor(mTilesets)); - for_each(mOverlays.begin(), mOverlays.end(), make_dtor(mOverlays)); + delete_all(mLayers); + delete_all(mTilesets); + delete_all(mOverlays); } void Map::initializeOverlays() @@ -218,7 +218,8 @@ void Map::draw(Graphics *graphics, int scrollX, int scrollY) mSprites.sort(spriteCompare); Layers::const_iterator layeri = mLayers.begin(); - for (; layeri != mLayers.end(); ++layeri) { + for (; layeri != mLayers.end(); ++layeri) + { (*layeri)->draw(graphics, startX, startY, endX, endY, scrollX, scrollY, @@ -285,7 +286,7 @@ Tileset* Map::getTilesetWithGid(int gid) const containsGid.gid = gid; Tilesets::const_iterator i = find_if(mTilesets.begin(), mTilesets.end(), - containsGid); + containsGid); return (i == mTilesets.end()) ? NULL : *i; } @@ -294,7 +295,7 @@ void Map::setWalk(int x, int y, bool walkable) { mMetaTiles[x + y * mWidth].walkable = walkable; } - + bool Map::occupied(int x, int y) const { Beings &beings = beingManager->getAll(); @@ -312,7 +313,7 @@ bool Map::occupied(int x, int y) const bool Map::tileCollides(int x, int y) const { - return !(contains(x, y) && mMetaTiles[x + y * mWidth].walkable); + return !(contains(x, y) && mMetaTiles[x + y * mWidth].walkable); } bool Map::contains(int x, int y) const @@ -414,13 +415,6 @@ Path Map::findPath(int startX, int startY, int destX, int destY) // 14 for moving diagonal (sqrt(200) = 14.1421...) int Gcost = curr.tile->Gcost + ((dx == 0 || dy == 0) ? 10 : 14); - // It costs extra to walk through a being (needs to be enough - // to make it more attractive to walk around). - if (occupied(x, y)) - { - Gcost += 30; - } - // Skip if Gcost becomes too much // Warning: probably not entirely accurate if (Gcost > 200) @@ -509,11 +503,14 @@ void Map::addParticleEffect(const std::string &effectFile, int x, int y) void Map::initializeParticleEffects(Particle* particleEngine) { - for (std::list<ParticleEffectData>::iterator i = particleEffects.begin(); - i != particleEffects.end(); - i++ - ) + if (config.getValue("particleeffects", 1)) { - particleEngine->addEffect(i->file, i->x, i->y); + for (std::list<ParticleEffectData>::iterator i = particleEffects.begin(); + i != particleEffects.end(); + i++ + ) + { + particleEngine->addEffect(i->file, i->x, i->y); + } } } diff --git a/src/monster.cpp b/src/monster.cpp index 8666fe26..04624b8c 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -19,14 +19,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "monster.h" - #include "animatedsprite.h" #include "game.h" -#include "sound.h" +#include "localplayer.h" +#include "monster.h" #include "particle.h" +#include "sound.h" #include "text.h" -#include "localplayer.h" #include "gui/gui.h" @@ -46,6 +45,7 @@ Monster::Monster(Uint32 id, Uint16 job, Map *map): // Setup Monster sprites int c = BASE_SPRITE; const std::list<std::string> &sprites = info.getSprites(); + for (std::list<std::string>::const_iterator i = sprites.begin(); i != sprites.end(); i++) @@ -63,26 +63,28 @@ Monster::Monster(Uint32 id, Uint16 job, Map *map): mSprites[c] = AnimatedSprite::load("graphics/sprites/error.xml"); } - const std::list<std::string> &particleEffects = info.getParticleEffects(); - for ( std::list<std::string>::const_iterator i = particleEffects.begin(); - i != particleEffects.end(); - i++ - ) + if (mParticleEffects) { - controlParticle(particleEngine->addEffect((*i), 0, 0)); + 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)); + } } } -Monster::~Monster() +Monster::~Monster() { if (mText) { - player_node->setTarget(0); + delete mText; + player_node->setTarget(NULL); } } -void -Monster::logic() +void Monster::logic() { if (mAction != STAND) { @@ -97,16 +99,16 @@ Monster::logic() Being::logic(); } -Being::Type -Monster::getType() const +Being::Type Monster::getType() const { return MONSTER; } -void -Monster::setAction(Uint8 action) +void Monster::setAction(Action action) { SpriteAction currentAction = ACTION_INVALID; + int rotation = 0; + std::string particleEffect; switch (action) { @@ -120,13 +122,34 @@ Monster::setAction(Uint8 action) case ATTACK: currentAction = ACTION_ATTACK; mSprites[BASE_SPRITE]->reset(); + + //attack particle effect + particleEffect = getInfo().getAttackParticleEffect(); + if (particleEffect != "" && mParticleEffects) + { + 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); + } break; case STAND: - currentAction = ACTION_STAND; - break; + currentAction = ACTION_STAND; + break; case HURT: - // Not implemented yet - break; + // Not implemented yet + break; + case SIT: + // Also not implemented yet + break; } if (currentAction != ACTION_INVALID) @@ -142,8 +165,7 @@ Monster::setAction(Uint8 action) } } -void -Monster::handleAttack(Being *victim, int damage) +void Monster::handleAttack(Being *victim, int damage) { Being::handleAttack(victim, damage); @@ -152,34 +174,34 @@ Monster::handleAttack(Being *victim, int damage) MONSTER_EVENT_HIT : MONSTER_EVENT_MISS)); } -void -Monster::takeDamage(int amount) +void Monster::takeDamage(int amount) { if (amount > 0) sound.playSfx(getInfo().getSound(MONSTER_EVENT_HURT)); Being::takeDamage(amount); } -Being::TargetCursorSize -Monster::getTargetCursorSize() const +Being::TargetCursorSize Monster::getTargetCursorSize() const { return getInfo().getTargetCursorSize(); } -const MonsterInfo& -Monster::getInfo() const +const MonsterInfo& Monster::getInfo() const { return MonsterDB::get(mJob - 1002); } void Monster::showName(bool show) { - delete mText; + if (mText) + { + delete mText; + } if (show) { mText = new Text(getInfo().getName(), mPx + NAME_X_OFFSET, mPy + NAME_Y_OFFSET - getHeight(), gcn::Graphics::CENTER, - speechFont, gcn::Color(255, 32, 32)); + mobNameFont, gcn::Color(255, 32, 32)); } else { diff --git a/src/monster.h b/src/monster.h index c8f0a8f7..cb8f7f18 100644 --- a/src/monster.h +++ b/src/monster.h @@ -36,7 +36,7 @@ class Monster : public Being virtual void logic(); - virtual void setAction(Uint8 action); + virtual void setAction(Action action); virtual Type getType() const; diff --git a/src/net/beinghandler.cpp b/src/net/beinghandler.cpp index 086b4280..3c022af6 100644 --- a/src/net/beinghandler.cpp +++ b/src/net/beinghandler.cpp @@ -19,24 +19,24 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "beinghandler.h" - +#include <iostream> #include <SDL_types.h> +#include "beinghandler.h" #include "messagein.h" #include "protocol.h" #include "../being.h" #include "../beingmanager.h" +#include "../effectmanager.h" #include "../game.h" #include "../localplayer.h" #include "../log.h" #include "../main.h" +#include "../npc.h" #include "../particle.h" -#include "../sound.h" -#include <iostream> #include "../player_relations.h" -#include "../npc.h" +#include "../sound.h" const int EMOTION_TIME = 150; /**< Duration of emotion icon */ @@ -72,6 +72,7 @@ void BeingHandler::handleMessage(MessageIn *msg) Uint16 headTop, headMid, headBottom; Uint16 shoes, gloves, cape, misc1, misc2; Uint16 weapon, shield; + Uint16 gmstatus; Sint16 param1; Sint8 type; Being *srcBeing, *dstBeing; @@ -128,8 +129,8 @@ void BeingHandler::handleMessage(MessageIn *msg) headTop = msg->readInt16(); headMid = msg->readInt16(); hairColor = msg->readInt16(); - shoes = msg->readInt16(); // clothes color - "abused" as shoes - gloves = msg->readInt16(); // head dir - "abused" as gloves + shoes = msg->readInt16(); + gloves = msg->readInt16(); msg->readInt16(); // guild msg->readInt16(); // unknown msg->readInt16(); // unknown @@ -201,22 +202,17 @@ void BeingHandler::handleMessage(MessageIn *msg) if (!dstBeing) break; + // If this is player's current target, clear it. if (dstBeing == player_node->getTarget()) - { player_node->stopAttack(); - } if (dstBeing == current_npc) current_npc = NULL; if (msg->readInt8() == 1) - { dstBeing->setAction(Being::DEAD); - } else - { beingManager->destroyBeing(dstBeing); - } break; @@ -234,28 +230,26 @@ void BeingHandler::handleMessage(MessageIn *msg) switch (type) { case 0x0a: // Critical Damage - if (dstBeing) { - dstBeing->controlParticle(particleEngine->addEffect( - "graphics/particles/crit.particle.xml", 0, 0)); - } + if (dstBeing) + dstBeing->showCrit(); case 0x00: // Damage - if (dstBeing) { + if (dstBeing) dstBeing->takeDamage(param1); - } - if (srcBeing) { + if (srcBeing) srcBeing->handleAttack(dstBeing, param1); - } break; case 0x02: // Sit - if (srcBeing) { + if (srcBeing) + { srcBeing->mFrame = 0; srcBeing->setAction(Being::SIT); } break; case 0x03: // Stand up - if (srcBeing) { + if (srcBeing) + { srcBeing->mFrame = 0; srcBeing->setAction(Being::STAND); } @@ -269,8 +263,10 @@ void BeingHandler::handleMessage(MessageIn *msg) break; int effectType = msg->readInt32(); + Being* being = beingManager->findBeing(id); - beingManager->findBeing(id)->triggerEffect(effectType); + effectManager->trigger(effectType, (int) being->getPixelX(), + (int) being->getPixelY()); break; } @@ -403,8 +399,8 @@ void BeingHandler::handleMessage(MessageIn *msg) headTop = msg->readInt16(); headMid = msg->readInt16(); hairColor = msg->readInt16(); - msg->readInt16(); // clothes color - Aethyra-"abused" as shoes, we ignore it - msg->readInt16(); // head dir - Aethyra-"abused" as gloves, we ignore it + shoes = msg->readInt16(); + gloves = msg->readInt16(); msg->readInt32(); // guild msg->readInt32(); // emblem msg->readInt16(); // manner @@ -417,6 +413,10 @@ void BeingHandler::handleMessage(MessageIn *msg) dstBeing->setSprite(Being::BOTTOMCLOTHES_SPRITE, headBottom); dstBeing->setSprite(Being::TOPCLOTHES_SPRITE, headMid); dstBeing->setSprite(Being::HAT_SPRITE, headTop); + dstBeing->setSprite(Being::SHOE_SPRITE, shoes); + // Compensation for the unpatched TMW server + if (gloves > 10) + dstBeing->setSprite(Being::GLOVES_SPRITE, gloves); dstBeing->setSprite(Being::CAPE_SPRITE, cape); dstBeing->setSprite(Being::MISC1_SPRITE, misc1); dstBeing->setSprite(Being::MISC2_SPRITE, misc2); @@ -437,18 +437,22 @@ void BeingHandler::handleMessage(MessageIn *msg) dstBeing->setDirection(dir); } - msg->readInt16(); // GM status + gmstatus = msg->readInt16(); + if (gmstatus & 0x80) + dstBeing->setGM(); if (msg->getId() == SMSG_PLAYER_UPDATE_1) { - switch (msg->readInt8()) { - - case 1: dstBeing->setAction(Being::DEAD); - break; - - case 2: dstBeing->setAction(Being::SIT); - break; - + switch (msg->readInt8()) + { + case 1: + if (dstBeing->getType() != Being::NPC) + dstBeing->setAction(Being::DEAD); + break; + + case 2: + dstBeing->setAction(Being::SIT); + break; } } else if (msg->getId() == SMSG_PLAYER_MOVE) @@ -465,16 +469,16 @@ void BeingHandler::handleMessage(MessageIn *msg) case SMSG_PLAYER_STOP: /* - * Instruction from server to stop walking at x, y. - * - * Some people like having this enabled. Others absolutely - * despise it. So I'm setting to so that it only affects the - * local player if the person has set a key "EnableSync" to "1" - * in their config.xml file. - * - * This packet will be honored for all other beings, regardless - * of the config setting. - */ + * Instruction from server to stop walking at x, y. + * + * Some people like having this enabled. Others absolutely + * despise it. So I'm setting to so that it only affects the + * local player if the person has set a key "EnableSync" to "1" + * in their config.xml file. + * + * This packet will be honored for all other beings, regardless + * of the config setting. + */ id = msg->readInt32(); if (mSync || id != player_node->getId()) { @@ -492,11 +496,11 @@ void BeingHandler::handleMessage(MessageIn *msg) case SMSG_PLAYER_MOVE_TO_ATTACK: /* - * This is an *advisory* message, telling the client that - * it needs to move the character before attacking - * a target (out of range, obstruction in line of fire). - * We can safely ignore this... - */ + * This is an *advisory* message, telling the client that + * it needs to move the character before attacking + * a target (out of range, obstruction in line of fire). + * We can safely ignore this... + */ break; case 0x0119: diff --git a/src/net/beinghandler.h b/src/net/beinghandler.h index 5e22a670..9e736751 100644 --- a/src/net/beinghandler.h +++ b/src/net/beinghandler.h @@ -27,7 +27,7 @@ class BeingHandler : public MessageHandler { public: - BeingHandler(bool); + BeingHandler(bool enableSync); void handleMessage(MessageIn *msg); diff --git a/src/net/buysellhandler.cpp b/src/net/buysellhandler.cpp index 16cfdc06..b2fe99b9 100644 --- a/src/net/buysellhandler.cpp +++ b/src/net/buysellhandler.cpp @@ -19,10 +19,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "buysellhandler.h" - #include <SDL_types.h> +#include "buysellhandler.h" #include "messagein.h" #include "protocol.h" @@ -37,8 +36,8 @@ #include "../gui/sell.h" extern BuyDialog *buyDialog; -extern SellDialog *sellDialog; extern Window *buySellDialog; +extern SellDialog *sellDialog; BuySellHandler::BuySellHandler() { diff --git a/src/net/charserverhandler.cpp b/src/net/charserverhandler.cpp index 9fb67ea5..281923f8 100644 --- a/src/net/charserverhandler.cpp +++ b/src/net/charserverhandler.cpp @@ -20,19 +20,24 @@ */ #include "charserverhandler.h" - #include "messagein.h" #include "network.h" #include "protocol.h" +#include "../extensions.h" #include "../game.h" #include "../localplayer.h" #include "../log.h" #include "../logindata.h" #include "../main.h" -#include "../gui/ok_dialog.h" #include "../gui/char_select.h" +#include "../gui/ok_dialog.h" + +/* + * Yeah, this is a global. Get over it. + */ +struct EXTENSIONS extensions; CharServerHandler::CharServerHandler(): mCharCreateDialog(0) @@ -54,6 +59,7 @@ CharServerHandler::CharServerHandler(): void CharServerHandler::handleMessage(MessageIn *msg) { int slot; + int flags; LocalPlayer *tempPlayer; logger->log("CharServerHandler: Packet ID: %x, Length: %d", @@ -61,8 +67,13 @@ void CharServerHandler::handleMessage(MessageIn *msg) switch (msg->getId()) { case 0x006b: - // Skip length word and an additional mysterious 20 bytes - msg->skip(2 + 20); + msg->skip(2); // Length word + flags = msg->readInt32(); // Aethyra extensions flags + logger->log("Server flags are: %x", flags); + extensions.aethyra_inventory = (bool)(flags & 0x01); + extensions.aethyra_spells = (bool)(flags & 0x02); + extensions.aethyra_misc = (bool)(flags & 0x04); + msg->skip(16); // Unused // Derive number of characters from message length n_character = (msg->getLength() - 24) / 106; diff --git a/src/net/chathandler.cpp b/src/net/chathandler.cpp index d852798d..b73b86b4 100644 --- a/src/net/chathandler.cpp +++ b/src/net/chathandler.cpp @@ -19,11 +19,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "chathandler.h" - #include <SDL_types.h> #include <string> +#include "chathandler.h" #include "messagein.h" #include "protocol.h" diff --git a/src/net/equipmenthandler.cpp b/src/net/equipmenthandler.cpp index 580cef6b..2e0cc562 100644 --- a/src/net/equipmenthandler.cpp +++ b/src/net/equipmenthandler.cpp @@ -20,7 +20,6 @@ */ #include "equipmenthandler.h" - #include "messagein.h" #include "protocol.h" @@ -107,7 +106,10 @@ void EquipmentHandler::handleMessage(MessageIn *msg) break; } - // Unequip any existing equipped item in this position + /* + * An item may occupy more than 1 slot. If so, it's + * only shown as equipped on the *first* slot. + */ mask = 1; position = 0; while (!(equipPoint & mask)) { @@ -115,7 +117,10 @@ void EquipmentHandler::handleMessage(MessageIn *msg) position++; } logger->log("Position %i", position); - item = player_node->getInventory()->getItem(player_node->mEquipment->getEquipment(position)); + + item = player_node->getInventory()->getItem(player_node->mEquipment->getEquipment(position)); + + // Unequip any existing equipped item in this position if (item) { item->setEquipped(false); } @@ -152,24 +157,11 @@ void EquipmentHandler::handleMessage(MessageIn *msg) item->setEquipped(false); - switch (item->getId()) { - case 529: - case 1199: - player_node->mEquipment->setArrows(0); - break; - case 521: - case 522: - case 530: - case 536: - case 1200: - case 1201: - player_node->setSprite(Being::WEAPON_SPRITE, 0); - // TODO: Why this break? Shouldn't a weapon be - // unequipped in inventory too? - break; - default: - player_node->mEquipment->removeEquipment(position); - break; + if (equipPoint & 0x8000) { // Arrows + player_node->mEquipment->setArrows(0); + } + else { + player_node->mEquipment->removeEquipment(position); } logger->log("Unequipping: %i %i(%i) %i", index, equipPoint, type, position); @@ -186,12 +178,12 @@ void EquipmentHandler::handleMessage(MessageIn *msg) break; item = inventory->getItem(index); - if (!item) - break; - item->setEquipped(true); - player_node->mEquipment->setArrows(index); - logger->log("Arrows equipped: %i", index); + if (item) { + item->setEquipped(true); + player_node->mEquipment->setArrows(index); + logger->log("Arrows equipped: %i", index); + } break; } } diff --git a/src/net/inventoryhandler.cpp b/src/net/inventoryhandler.cpp index 9ddbd62f..12b7d5ef 100644 --- a/src/net/inventoryhandler.cpp +++ b/src/net/inventoryhandler.cpp @@ -19,22 +19,22 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "inventoryhandler.h" - #include <SDL_types.h> +#include "inventoryhandler.h" #include "messagein.h" #include "protocol.h" -#include "../resources/iteminfo.h" +#include "../inventory.h" #include "../item.h" #include "../itemshortcut.h" #include "../localplayer.h" #include "../log.h" -#include "../inventory.h" #include "../gui/chat.h" +#include "../resources/iteminfo.h" + #include "../utils/tostring.h" InventoryHandler::InventoryHandler() @@ -45,6 +45,12 @@ InventoryHandler::InventoryHandler() SMSG_PLAYER_INVENTORY_REMOVE, SMSG_PLAYER_INVENTORY_USE, SMSG_ITEM_USE_RESPONSE, + SMSG_PLAYER_STORAGE_ITEMS, + SMSG_PLAYER_STORAGE_EQUIP, + SMSG_PLAYER_STORAGE_STATUS, + SMSG_PLAYER_STORAGE_ADD, + SMSG_PLAYER_STORAGE_REMOVE, + SMSG_PLAYER_STORAGE_CLOSE, 0 }; handledMessages = _messages; @@ -53,35 +59,69 @@ InventoryHandler::InventoryHandler() void InventoryHandler::handleMessage(MessageIn *msg) { Sint32 number; - Sint16 index, amount, itemId, equipType; + Sint16 index, amount, itemId, equipType, arrow; + Sint16 identified, cards[4], itemType; Inventory *inventory = player_node->getInventory(); + Inventory *storage = player_node->getStorage(); switch (msg->getId()) { case SMSG_PLAYER_INVENTORY: - // Only called on map load / warp. First reset all items - // to not load them twice on map change. - inventory->clear(); + case SMSG_PLAYER_STORAGE_ITEMS: + case SMSG_PLAYER_STORAGE_EQUIP: + switch (msg->getId()) { + case SMSG_PLAYER_INVENTORY: + // Clear inventory - this will be a complete refresh + inventory->clear(); + break; + case SMSG_PLAYER_STORAGE_ITEMS: + /* + * This packet will always be followed by a + * SMSG_PLAYER_STORAGE_EQUIP packet. The two packets + * together comprise a complete refresh of storage, so + * clear storage here + */ + storage->clear(); + logger->log("Received SMSG_PLAYER_STORAGE_ITEMS"); + break; + default: + logger->log("Received SMSG_PLAYER_STORAGE_EQUIP"); + break; + } msg->readInt16(); // length number = (msg->getLength() - 4) / 18; - for (int loop = 0; loop < number; loop++) - { + for (int loop = 0; loop < number; loop++) { index = msg->readInt16(); itemId = msg->readInt16(); - msg->readInt8(); // type - msg->readInt8(); // identify flag - amount = msg->readInt16(); - msg->skip(2); // unknown - msg->skip(8); // card (4 shorts) - - inventory->setItem(index, itemId, amount, false); - - // Trick because arrows are not considered equipment - if (itemId == 1199 || itemId == 529) - { - if (Item *item = inventory->getItem(index)) - item->setEquipment(true); + itemType = msg->readInt8(); + identified = msg->readInt8(); + if (msg->getId() == SMSG_PLAYER_STORAGE_EQUIP) { + amount = 1; + msg->readInt16(); // Equip Point? + } else { + amount = msg->readInt16(); + } + arrow = msg->readInt16(); + if (msg->getId() == SMSG_PLAYER_STORAGE_EQUIP) { + msg->readInt8(); // Attribute (broken) + msg->readInt8(); // Refine level + } + for (int i = 0; i < 4; i++) + cards[i] = msg->readInt16(); + + if (msg->getId() == SMSG_PLAYER_INVENTORY) { + inventory->setItem(index, itemId, amount, false); + + // Trick because arrows are not considered equipment + if (arrow & 0x8000) { + if (Item *item = inventory->getItem(index)) + item->setEquipment(true); + } + } else { + logger->log("Index:%d, ID:%d, Type:%d, Identified:%d, Qty:%d, Cards:%d, %d, %d, %d", + index, itemId, itemType, identified, amount, cards[0], cards[1], cards[2], cards[3]); + storage->setItem(index, itemId, amount, false); } } break; @@ -90,12 +130,13 @@ void InventoryHandler::handleMessage(MessageIn *msg) index = msg->readInt16(); amount = msg->readInt16(); itemId = msg->readInt16(); - msg->readInt8(); // identify flag + identified = msg->readInt8(); msg->readInt8(); // attribute msg->readInt8(); // refine - msg->skip(8); // card + for (int i = 0; i < 4; i++) + cards[i] = msg->readInt16(); equipType = msg->readInt16(); - msg->readInt8(); // type + itemType = msg->readInt8(); if (msg->readInt8() > 0) { chatWindow->chatLog("Unable to pick up item", BY_SERVER); @@ -147,5 +188,38 @@ void InventoryHandler::handleMessage(MessageIn *msg) item->setQuantity(amount); } break; + + case SMSG_PLAYER_STORAGE_STATUS: + /* + * Basic slots used vs total slots info + * We don't really need this information, but this is + * the closest we get to an "Open Storage" packet + * from the server. It always comes after the two + * SMSG_PLAYER_STORAGE_... packets that update + * storage contents. + */ + logger->log("Received SMSG_PLAYER_STORAGE_STATUS"); + player_node->setInStorage(true); + break; + + case SMSG_PLAYER_STORAGE_ADD: + /* + * Move an item into storage + */ + break; + + case SMSG_PLAYER_STORAGE_REMOVE: + /* + * Move an item out of storage + */ + break; + + case SMSG_PLAYER_STORAGE_CLOSE: + /* + * Storage access has been closed + */ + player_node->setInStorage(false); + logger->log("Received SMSG_PLAYER_STORAGE_CLOSE"); + break; } } diff --git a/src/net/itemhandler.cpp b/src/net/itemhandler.cpp index 487b98bf..9cf85ce7 100644 --- a/src/net/itemhandler.cpp +++ b/src/net/itemhandler.cpp @@ -20,7 +20,6 @@ */ #include "itemhandler.h" - #include "messagein.h" #include "protocol.h" diff --git a/src/net/loginhandler.cpp b/src/net/loginhandler.cpp index 169503da..2b98e4e4 100644 --- a/src/net/loginhandler.cpp +++ b/src/net/loginhandler.cpp @@ -20,7 +20,6 @@ */ #include "loginhandler.h" - #include "messagein.h" #include "network.h" #include "protocol.h" @@ -35,7 +34,7 @@ extern SERVER_INFO **server_info; LoginHandler::LoginHandler() { static const Uint16 _messages[] = { - SMSG_UPDATE_HOST, + 0x0063, 0x0069, 0x006a, 0 @@ -87,7 +86,7 @@ void LoginHandler::handleMessage(MessageIn *msg) iptostring(server_info[i]->address), server_info[i]->port); } - state = CHAR_SERVER_STATE; + state = CHAR_SERVER_STATE; break; case 0x006a: diff --git a/src/net/loginhandler.h b/src/net/loginhandler.h index 8a952cd5..047434b4 100644 --- a/src/net/loginhandler.h +++ b/src/net/loginhandler.h @@ -22,6 +22,8 @@ #ifndef _TMW_NET_LOGINHANDLER_H #define _TMW_NET_LOGINHANDLER_H +#include <string> + #include "messagehandler.h" #include <string> diff --git a/src/net/maploginhandler.cpp b/src/net/maploginhandler.cpp index b1000c12..bc08d5d6 100644 --- a/src/net/maploginhandler.cpp +++ b/src/net/maploginhandler.cpp @@ -20,7 +20,6 @@ */ #include "maploginhandler.h" - #include "messagein.h" #include "protocol.h" diff --git a/src/net/messagehandler.cpp b/src/net/messagehandler.cpp index 29e34a29..7a41e1ad 100644 --- a/src/net/messagehandler.cpp +++ b/src/net/messagehandler.cpp @@ -19,10 +19,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "messagehandler.h" - #include <cassert> +#include "messagehandler.h" #include "network.h" MessageHandler::MessageHandler(): diff --git a/src/net/messagein.cpp b/src/net/messagein.cpp index 8e31fded..345e02fc 100644 --- a/src/net/messagein.cpp +++ b/src/net/messagein.cpp @@ -19,17 +19,16 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "messagein.h" - #include <cassert> #include <SDL.h> #include <SDL_endian.h> +#include "messagein.h" + #define MAKEWORD(low,high) \ ((unsigned short)(((unsigned char)(low)) | \ ((unsigned short)((unsigned char)(high))) << 8)) - MessageIn::MessageIn(const char *data, unsigned int length): mData(data), mLength(length), diff --git a/src/net/messagein.h b/src/net/messagein.h index da80df9e..81db6cdc 100644 --- a/src/net/messagein.h +++ b/src/net/messagein.h @@ -22,8 +22,8 @@ #ifndef _TMW_MESSAGEIN_ #define _TMW_MESSAGEIN_ -#include <string> #include <SDL_types.h> +#include <string> /** * Used for parsing an incoming message. diff --git a/src/net/messageout.cpp b/src/net/messageout.cpp index 96678f19..6aa25411 100644 --- a/src/net/messageout.cpp +++ b/src/net/messageout.cpp @@ -20,13 +20,12 @@ */ #include <cstring> -#include <string> #include <SDL.h> #include <SDL_endian.h> - -#include "network.h" +#include <string> #include "messageout.h" +#include "network.h" MessageOut::MessageOut(Network *network): mNetwork(network), diff --git a/src/net/network.cpp b/src/net/network.cpp index e8b5e949..c9f8d1bd 100644 --- a/src/net/network.cpp +++ b/src/net/network.cpp @@ -19,15 +19,14 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "network.h" +#include <sstream> #include "messagehandler.h" #include "messagein.h" +#include "network.h" #include "../log.h" -#include <sstream> - /** Warning: buffers and other variables are shared, so there can be only one connection active at a time */ diff --git a/src/net/npchandler.cpp b/src/net/npchandler.cpp index b633835c..4f3c4354 100644 --- a/src/net/npchandler.cpp +++ b/src/net/npchandler.cpp @@ -19,16 +19,16 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "npchandler.h" - #include "messagein.h" +#include "npchandler.h" #include "protocol.h" #include "../beingmanager.h" +#include "../localplayer.h" #include "../npc.h" -#include "../gui/npclistdialog.h" #include "../gui/npc_text.h" +#include "../gui/npclistdialog.h" extern NpcListDialog *npcListDialog; extern NpcTextDialog *npcTextDialog; @@ -54,6 +54,7 @@ void NPCHandler::handleMessage(MessageIn *msg) case SMSG_NPC_CHOICE: msg->readInt16(); // length id = msg->readInt32(); + player_node->setAction(LocalPlayer::STAND); current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); npcListDialog->parseItems(msg->readString(msg->getLength() - 8)); npcListDialog->setVisible(true); @@ -62,15 +63,15 @@ void NPCHandler::handleMessage(MessageIn *msg) case SMSG_NPC_MESSAGE: msg->readInt16(); // length id = msg->readInt32(); + player_node->setAction(LocalPlayer::STAND); current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); npcTextDialog->addText(msg->readString(msg->getLength() - 8)); npcListDialog->setVisible(false); npcTextDialog->setVisible(true); break; - case SMSG_NPC_CLOSE: + case SMSG_NPC_CLOSE: id = msg->readInt32(); - dynamic_cast<NPC*>(beingManager->findBeing(id)); if (current_npc == dynamic_cast<NPC*>(beingManager->findBeing(id))) current_npc = NULL; break; diff --git a/src/net/partyhandler.cpp b/src/net/partyhandler.cpp new file mode 100644 index 00000000..9b5f3080 --- /dev/null +++ b/src/net/partyhandler.cpp @@ -0,0 +1,124 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <guichan/actionlistener.hpp> + +#include "partyhandler.h" +#include "protocol.h" +#include "messagein.h" + +#include "../gui/chat.h" +#include "../gui/confirm_dialog.h" + +#include "../beingmanager.h" +#include "../game.h" +#include "../party.h" + +PartyHandler::PartyHandler(Party *party) : mParty(party) +{ + static const Uint16 _messages[] = { + SMSG_PARTY_CREATE, + SMSG_PARTY_INFO, + SMSG_PARTY_INVITE, + SMSG_PARTY_INVITED, + SMSG_PARTY_SETTINGS, + SMSG_PARTY_MEMBER_INFO, + SMSG_PARTY_LEAVE, + SMSG_PARTY_UPDATE_HP, + SMSG_PARTY_UPDATE_COORDS, + SMSG_PARTY_MESSAGE, + 0 + }; + handledMessages = _messages; +} + +void +PartyHandler::handleMessage(MessageIn *msg) +{ + switch (msg->getId()) + { + case SMSG_PARTY_CREATE: + mParty->createResponse(msg->readInt8()); + break; + case SMSG_PARTY_INFO: + break; + case SMSG_PARTY_INVITE: + { + std::string nick = msg->readString(24); + int status = msg->readInt8(); + mParty->inviteResponse(nick, status); + break; + } + case SMSG_PARTY_INVITED: + { + int id = msg->readInt32(); + Being *being = beingManager->findBeing(id); + if (being == NULL) + { + break; + } + std::string nick; + int gender = 0; + std::string partyName = ""; + if (being->getType() != Being::PLAYER) + { + nick = ""; + } + else + { + nick = being->getName(); + gender = being->getGender(); + partyName = msg->readString(24); + } + mParty->invitedAsk(nick, gender, partyName); + break; + } + case SMSG_PARTY_SETTINGS: + break; + case SMSG_PARTY_MEMBER_INFO: + break; + case SMSG_PARTY_LEAVE: + { + /*int id = */msg->readInt32(); + std::string nick = msg->readString(24); + /*int fail = */msg->readInt8(); + mParty->leftResponse(nick); + break; + } + case SMSG_PARTY_UPDATE_HP: + break; + case SMSG_PARTY_UPDATE_COORDS: + break; + case SMSG_PARTY_MESSAGE: + { // new block to enable local variables + int msgLength = msg->readInt16() - 8; + if (msgLength <= 0) + { + return; + } + int id = msg->readInt32(); + Being *being = beingManager->findBeing(id); + std::string chatMsg = msg->readString(msgLength); + mParty->receiveChat(being, chatMsg); + } + break; + } +} diff --git a/src/net/partyhandler.h b/src/net/partyhandler.h new file mode 100644 index 00000000..daabc52f --- /dev/null +++ b/src/net/partyhandler.h @@ -0,0 +1,39 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _TMW_NET_PARTYHANDLER_H +#define _TMW_NET_PARTYHANDLER_H + +#include "messagehandler.h" + +class Party; + +class PartyHandler : public MessageHandler +{ + public: + PartyHandler(Party *party); + + void handleMessage(MessageIn *msg); + private: + Party *mParty; +}; + +#endif diff --git a/src/net/playerhandler.cpp b/src/net/playerhandler.cpp index 50546b1e..a4a2dcc9 100644 --- a/src/net/playerhandler.cpp +++ b/src/net/playerhandler.cpp @@ -19,16 +19,14 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "playerhandler.h" - #include "messagein.h" +#include "playerhandler.h" #include "protocol.h" #include "../engine.h" #include "../localplayer.h" #include "../log.h" #include "../npc.h" -#include "../utils/tostring.h" #include "../gui/buy.h" #include "../gui/chat.h" @@ -40,6 +38,8 @@ #include "../gui/skill.h" #include "../gui/viewport.h" +#include "../utils/tostring.h" + // TODO Move somewhere else OkDialog *weightNotice = NULL; OkDialog *deathNotice = NULL; @@ -51,7 +51,7 @@ extern SellDialog *sellDialog; extern Window *buySellDialog; static const int MAP_TELEPORT_SCROLL_DISTANCE = 8; /* Max. distance we are willing to scroll after a teleport; - ** everything beyond will reset the port hard. */ + * everything beyond will reset the port hard. */ /** * Listener used for handling the overweigth message. @@ -110,10 +110,11 @@ void PlayerHandler::handleMessage(MessageIn *msg) switch (msg->getId()) { case SMSG_WALK_RESPONSE: - // It is assumed by the client any request to walk actually - // succeeds on the server. The plan is to have a correction - // message when the server senses the client has the wrong - // idea. + /* + * This client assumes that all walk messages succeed, + * and that the server will send a correction notice + * otherwise. + */ break; case SMSG_PLAYER_WARP: @@ -132,6 +133,7 @@ void PlayerHandler::handleMessage(MessageIn *msg) player_node->stopAttack(); nearby = (engine->getCurrentMapName() == mapPath); + // Switch the actual map, deleting the previous one if necessary engine->changeMap(mapPath); @@ -143,7 +145,8 @@ void PlayerHandler::handleMessage(MessageIn *msg) /* Scroll if neccessary */ if (!nearby || (abs(x - player_node->mX) > MAP_TELEPORT_SCROLL_DISTANCE) - || (abs(y - player_node->mY) > MAP_TELEPORT_SCROLL_DISTANCE)) { + || (abs(y - player_node->mY) > MAP_TELEPORT_SCROLL_DISTANCE)) + { scrollOffsetX = (x - player_node->mX) * 32; scrollOffsetY = (y - player_node->mY) * 32; } @@ -173,6 +176,9 @@ void PlayerHandler::handleMessage(MessageIn *msg) case 0x0006: player_node->mMaxHp = value; break; case 0x0007: player_node->mMp = value; break; case 0x0008: player_node->mMaxMp = value; break; + case 0x0009: + player_node->mStatsPointsToAttribute = value; + break; case 0x000b: player_node->mLevel = value; break; case 0x000c: player_node->mSkillPoint = value; @@ -193,10 +199,6 @@ void PlayerHandler::handleMessage(MessageIn *msg) player_node->mTotalWeight = value; break; case 0x0019: player_node->mMaxWeight = value; break; - case 0x0037: player_node->mJobLevel = value; break; - case 0x0009: - player_node->mStatsPointsToAttribute = value; - break; case 0x0029: player_node->ATK = value; break; case 0x002b: player_node->MATK = value; break; case 0x002d: player_node->DEF = value; break; @@ -205,12 +207,44 @@ void PlayerHandler::handleMessage(MessageIn *msg) case 0x0031: player_node->HIT = value; break; case 0x0032: player_node->FLEE = value; break; case 0x0035: player_node->mAttackSpeed = value; break; + case 0x0037: player_node->mJobLevel = value; break; } if (player_node->mHp == 0 && deathNotice == NULL) { - deathNotice = new OkDialog("Message", - "You're now dead, press ok to restart"); + static char const *const deadMsg[] = + { + "You are dead.", + "We regret to inform you that your character was killed in battle.", + "You are not that alive anymore.", + "The cold hands of the grim reaper are grabbing for your soul.", + "Game Over!", + "Insert coin to continue", + "No, kids. Your character did not really die. It... err... went to a better place.", + "Your plan of breaking your enemies weapon by bashing it with your throat failed.", + "I guess this did not run too well.", + "Do you want your possessions identified?", // Nethack reference + "Sadly, no trace of you was ever found...", // Secret of Mana reference + "Annihilated.", // Final Fantasy VI reference + "Looks like you got your head handed to you.", //Earthbound reference + "You screwed up again, dump your body down the tubes and get you another one.", // Leisure Suit Larry 1 Reference + "You're not dead yet. You're just resting.", // Monty Python reference from a couple of skits + "You are no more.", // Monty Python reference from the dead parrot sketch starting now + "You have ceased to be.", + "You've expired and gone to meet your maker.", + "You're a stiff.", + "Bereft of life, you rest in peace.", + "If you weren't so animated, you'd be pushing up the daisies.", + "Your metabolic processes are now history.", + "You're off the twig.", + "You've kicked the bucket.", + "You've shuffled off your mortal coil, run down the curtain and joined the bleedin' choir invisibile.", + "You are an ex-player.", + "You're pining for the fjords." // Monty Python reference from the dead parrot sketch + }; + std::string message(deadMsg[rand()%27]); + + deathNotice = new OkDialog("Message", message); deathNotice->addActionListener(&deathListener); player_node->setAction(Being::DEAD); } @@ -293,7 +327,7 @@ void PlayerHandler::handleMessage(MessageIn *msg) } break; - // Updates stats and status points + // Updates stats and status points case SMSG_PLAYER_STAT_UPDATE_5: player_node->mStatsPointsToAttribute = msg->readInt16(); player_node->mAttr[LocalPlayer::STR] = msg->readInt8(); @@ -361,18 +395,5 @@ void PlayerHandler::handleMessage(MessageIn *msg) } } break; - - //Stop walking - //case 0x0088: // Disabled because giving some problems - //if (being = beingManager->findBeing(readInt32(2))) { - // if (being->getId() != player_node->getId()) { - // being->action = STAND; - // being->mFrame = 0; - // set_coordinates(being->coordinates, - // readWord(6), readWord(8), - // get_direction(being->coordinates)); - // } - //} - //break; } } diff --git a/src/net/protocol.h b/src/net/protocol.h index d7bdd041..783283ba 100644 --- a/src/net/protocol.h +++ b/src/net/protocol.h @@ -22,8 +22,11 @@ #ifndef _TMW_PROTOCOL_ #define _TMW_PROTOCOL_ -// Packets from server to client +/********************************* + * Packets from server to client * + *********************************/ #define SMSG_LOGIN_SUCCESS 0x0073 /**< Contains starting location */ +#define SMSG_SERVER_PING 0x007f /**< Contains server tick */ #define SMSG_UPDATE_HOST 0x0063 /**< Custom update host packet */ #define SMSG_PLAYER_UPDATE_1 0x01d8 #define SMSG_PLAYER_UPDATE_2 0x01d9 @@ -66,6 +69,7 @@ #define SMSG_BEING_ACTION 0x008a /**< Attack, sit, stand up, ... */ #define SMSG_BEING_CHAT 0x008d /**< A being talks */ #define SMSG_BEING_NAME_RESPONSE 0x0095 /**< Has to be requested */ + #define SMSG_NPC_MESSAGE 0x00b4 #define SMSG_NPC_NEXT 0x00b5 #define SMSG_NPC_CLOSE 0x00b6 @@ -75,11 +79,13 @@ #define SMSG_NPC_SELL 0x00c7 #define SMSG_NPC_BUY_RESPONSE 0x00ca #define SMSG_NPC_SELL_RESPONSE 0x00cb + #define SMSG_PLAYER_CHAT 0x008e /**< Player talks */ #define SMSG_WHISPER 0x0097 /**< Whisper Recieved */ #define SMSG_WHISPER_RESPONSE 0x0098 #define SMSG_GM_CHAT 0x009a /**< GM announce */ #define SMSG_WALK_RESPONSE 0x0087 + #define SMSG_TRADE_REQUEST 0x00e5 /**< Receiving a request to trade */ #define SMSG_TRADE_RESPONSE 0x00e7 #define SMSG_TRADE_ITEM_ADD 0x00e9 @@ -88,7 +94,27 @@ #define SMSG_TRADE_CANCEL 0x00ee #define SMSG_TRADE_COMPLETE 0x00f0 -// Packets from client to server +#define SMSG_PARTY_CREATE 0x00fa +#define SMSG_PARTY_INFO 0x00fb +#define SMSG_PARTY_INVITE 0x00fd +#define SMSG_PARTY_INVITED 0x00fe +#define SMSG_PARTY_SETTINGS 0x0102 +#define SMSG_PARTY_MEMBER_INFO 0x0104 +#define SMSG_PARTY_LEAVE 0x0105 +#define SMSG_PARTY_UPDATE_HP 0x0106 +#define SMSG_PARTY_UPDATE_COORDS 0x0107 +#define SMSG_PARTY_MESSAGE 0x0109 + +#define SMSG_PLAYER_STORAGE_ITEMS 0x01f0 /**< Item list for storage */ +#define SMSG_PLAYER_STORAGE_EQUIP 0x00a6 /**< Equipment list for storage */ +#define SMSG_PLAYER_STORAGE_STATUS 0x00f2 /**< Slots used and total slots */ +#define SMSG_PLAYER_STORAGE_ADD 0x00f4 /**< Add item/equip to storage */ +#define SMSG_PLAYER_STORAGE_REMOVE 0x00f6 /**< Remove item/equip from storage */ +#define SMSG_PLAYER_STORAGE_CLOSE 0x00f8 /**< Storage access closed */ + +/********************************** + * Packets from client to server * + **********************************/ #define CMSG_CLIENT_PING 0x007e /**< Send to server with tick */ #define CMSG_TRADE_RESPONSE 0x00e6 #define CMSG_ITEM_PICKUP 0x009f @@ -97,6 +123,8 @@ #define CMSG_NPC_BUY_SELL_REQUEST 0x00c5 #define CMSG_CHAT_MESSAGE 0x008c #define CMSG_CHAT_WHISPER 0x0096 +#define CMSG_CHAT_ANNOUNCE 0x0099 +#define CMSG_CHAT_WHO 0x00c1 #define CMSG_NPC_LIST_CHOICE 0x00b8 #define CMSG_NPC_NEXT_REQUEST 0x00b9 #define CMSG_NPC_SELL_REQUEST 0x00c9 @@ -113,6 +141,17 @@ #define CMSG_PLAYER_EQUIP 0x00a9 #define CMSG_PLAYER_UNEQUIP 0x00ab +#define CMSG_PARTY_CREATE 0x00f9 +#define CMSG_PARTY_INVITE 0x00fc +#define CMSG_PARTY_INVITED 0x00ff +#define CMSG_PARTY_LEAVE 0x0100 /** Undocumented */ +#define CMSG_PARTY_SETTINGS 0x0101 +#define CMSG_PARTY_MESSAGE 0x0108 + +#define CMSG_MOVE_TO_STORAGE 0x00f3 /** Move item to storage */ +#define CSMG_MOVE_FROM_STORAGE 0x00f5 /** Remove item from storage */ +#define CMSG_CLOSE_STORAGE 0x00f7 /** Request storage close */ + /** Encodes coords and direction in 3 bytes data */ void set_coordinates(char *data, unsigned short x, unsigned short y, unsigned char direction); diff --git a/src/net/skillhandler.cpp b/src/net/skillhandler.cpp index 2bb5d9dc..b9a232fb 100644 --- a/src/net/skillhandler.cpp +++ b/src/net/skillhandler.cpp @@ -19,10 +19,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "skillhandler.h" - #include "messagein.h" #include "protocol.h" +#include "skillhandler.h" #include "../log.h" diff --git a/src/net/tradehandler.cpp b/src/net/tradehandler.cpp index 955aeff1..746e1d06 100644 --- a/src/net/tradehandler.cpp +++ b/src/net/tradehandler.cpp @@ -19,10 +19,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "tradehandler.h" - #include "messagein.h" #include "protocol.h" +#include "tradehandler.h" #include "../inventory.h" #include "../item.h" @@ -188,11 +187,11 @@ void TradeHandler::handleMessage(MessageIn *msg) BY_SERVER); break; case 2: - // Add item failed - player has no free slot - chatWindow->chatLog("Failed adding item. Trade " - "partner has no free slot.", - BY_SERVER); - break; + // Add item failed - player has no free slot + chatWindow->chatLog("Failed adding item. Trade " + "partner has no free slot.", + BY_SERVER); + break; default: chatWindow->chatLog("Failed adding item for " "unknown reason.", BY_SERVER); diff --git a/src/npc.cpp b/src/npc.cpp index 66048005..8dbbf83c 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -19,15 +19,16 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "npc.h" - #include "animatedsprite.h" #include "graphics.h" +#include "localplayer.h" +#include "npc.h" #include "particle.h" #include "text.h" #include "net/messageout.h" #include "net/protocol.h" + #include "resources/npcdb.h" #include "gui/gui.h" @@ -38,7 +39,7 @@ static const int NAME_X_OFFSET = 15; static const int NAME_Y_OFFSET = 30; NPC::NPC(Uint32 id, Uint16 job, Map *map, Network *network): - Being(id, job, map), mNetwork(network) + Player(id, job, map), mNetwork(network) { NPCInfo info = NPCDB::get(job); @@ -56,28 +57,52 @@ NPC::NPC(Uint32 id, Uint16 job, Map *map, Network *network): c++; } - // Setup particle effects - for (std::list<std::string>::const_iterator i = info.particles.begin(); - i != info.particles.end(); - i++) + if (mParticleEffects) { - Particle *p = particleEngine->addEffect(*i, 0, 0); - this->controlParticle(p); + //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); + } } mName = 0; } NPC::~NPC() { - delete mName; + if (mName) + { + delete mName; + player_node->setTarget(NULL); + } } void NPC::setName(const std::string &name) { - delete mName; - mName = new Text(name, mPx + NAME_X_OFFSET, mPy + NAME_Y_OFFSET, - gcn::Graphics::CENTER, speechFont, + if (mName) + { + delete mName; + } + std::string displayName = name.substr(0, name.find('#', 0)); + + mName = new Text(displayName, mPx + NAME_X_OFFSET, mPy + NAME_Y_OFFSET, + gcn::Graphics::CENTER, npcNameFont, gcn::Color(200, 200, 255)); + Being::setName(displayName + " (NPC)"); +} + +void NPC::setGender(int gender) +{ + Being::setGender(gender); +} + +void NPC::setSprite(int slot, int id, std::string color) +{ + // Fix this later should it not be adequate enough. + Being::setSprite(slot, id, color); } Being::Type @@ -22,13 +22,13 @@ #ifndef _TMW_NPC_H #define _TMW_NPC_H -#include "being.h" +#include "player.h" class Network; class Graphics; class Text; -class NPC : public Being +class NPC : public Player { public: NPC(Uint32 id, Uint16 job, Map *map, Network *network); @@ -36,6 +36,8 @@ class NPC : public Being ~NPC(); void setName(const std::string &name); + void setGender(int gender); + void setSprite(int slot, int id, std::string color); virtual Type getType() const; diff --git a/src/openglgraphics.cpp b/src/openglgraphics.cpp index d8d7a807..b3a8db4b 100644 --- a/src/openglgraphics.cpp +++ b/src/openglgraphics.cpp @@ -19,33 +19,32 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "main.h" - -#ifdef USE_OPENGL +#include <cstring> +#include <SDL.h> -#ifndef GL_TEXTURE_RECTANGLE_ARB -#define GL_TEXTURE_RECTANGLE_ARB 0x84F5 -#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8 -#endif +#include <guichan/exception.hpp> +#include <guichan/image.hpp> +#include "log.h" +#include "main.h" #include "openglgraphics.h" -#include <cstring> -#include <SDL.h> +#include "resources/image.h" #ifdef __APPLE__ #include <OpenGL/OpenGL.h> #endif -#include <guichan/exception.hpp> -#include <guichan/image.hpp> - -#include "log.h" +#ifdef USE_OPENGL -#include "resources/image.h" +#ifndef GL_TEXTURE_RECTANGLE_ARB +#define GL_TEXTURE_RECTANGLE_ARB 0x84F5 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8 +#endif OpenGLGraphics::OpenGLGraphics(): - mAlpha(false), mTexture(false), mColorAlpha(false) + mAlpha(false), mTexture(false), mColorAlpha(false), + mSync(false) { } @@ -53,6 +52,11 @@ OpenGLGraphics::~OpenGLGraphics() { } +void OpenGLGraphics::setSync(bool sync) +{ + mSync = sync; +} + bool OpenGLGraphics::setVideoMode(int w, int h, int bpp, bool fs, bool hwaccel) { logger->log("Setting video mode %dx%d %s", @@ -76,6 +80,10 @@ bool OpenGLGraphics::setVideoMode(int w, int h, int bpp, bool fs, bool hwaccel) #ifdef __APPLE__ // long VBL = 1; // CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &VBL); + if (mSync) { + const GLint VBL = 1; + CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &VBL); + } #endif // Setup OpenGL diff --git a/src/openglgraphics.h b/src/openglgraphics.h index 7d39e306..ea30e019 100644 --- a/src/openglgraphics.h +++ b/src/openglgraphics.h @@ -31,6 +31,13 @@ class OpenGLGraphics : public Graphics ~OpenGLGraphics(); + /** + * Sets whether vertical refresh syncing is enabled. Takes effect after + * the next call to setVideoMode(). Only implemented on MacOS for now. + */ + void setSync(bool sync); + bool getSync() const { return mSync; } + bool setVideoMode(int w, int h, int bpp, bool fs, bool hwaccel); bool drawImage(Image *image, @@ -72,6 +79,7 @@ class OpenGLGraphics : public Graphics private: bool mAlpha, mTexture; bool mColorAlpha; + bool mSync; }; #endif diff --git a/src/particle.cpp b/src/particle.cpp index d4266df2..f1896ae2 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -22,13 +22,12 @@ #include <algorithm> #include <cmath> -#include "particle.h" - #include "animationparticle.h" #include "configuration.h" #include "imageparticle.h" #include "log.h" #include "map.h" +#include "particle.h" #include "particleemitter.h" #include "textparticle.h" @@ -224,7 +223,7 @@ Particle::update() Particle* Particle::addEffect(const std::string &particleEffectFile, - int pixelX, int pixelY) + int pixelX, int pixelY, int rotation) { Particle *newParticle = NULL; @@ -288,7 +287,7 @@ Particle::addEffect(const std::string &particleEffectFile, continue; ParticleEmitter *newEmitter; - newEmitter = new ParticleEmitter(emitterNode, newParticle, mMap); + newEmitter = new ParticleEmitter(emitterNode, newParticle, mMap, rotation); newParticle->addEmitter(newEmitter); } @@ -358,11 +357,9 @@ Particle::~Particle() void Particle::clear() { - std::for_each(mChildEmitters.begin(), mChildEmitters.end(), - make_dtor(mChildEmitters)); + delete_all(mChildEmitters); mChildEmitters.clear(); - std::for_each(mChildParticles.begin(), mChildParticles.end(), - make_dtor(mChildParticles)); + delete_all(mChildParticles); mChildParticles.clear(); } diff --git a/src/particle.h b/src/particle.h index 6c00eadd..0a53f5af 100644 --- a/src/particle.h +++ b/src/particle.h @@ -107,7 +107,7 @@ class Particle : public Sprite */ Particle* addEffect(const std::string &particleEffectFile, - int pixelX, int pixelY); + int pixelX, int pixelY, int rotation = 0); /** * Creates a standalone text particle. @@ -316,7 +316,7 @@ class Particle : public Sprite // follow-point particles Particle *mTarget; /**< The particle that attracts this particle*/ - float mAcceleration; /**< Acceleration towards the target particle in pixels per game-tick²*/ + float mAcceleration; /**< Acceleration towards the target particle in pixels per game-tick�*/ float mInvDieDistance; /**< Distance in pixels from the target particle that causes the destruction of the particle*/ float mMomentum; /**< How much speed the particle retains after each game tick*/ }; diff --git a/src/particleemitter.cpp b/src/particleemitter.cpp index 5ae1aae5..03fe4672 100644 --- a/src/particleemitter.cpp +++ b/src/particleemitter.cpp @@ -19,24 +19,23 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "particleemitter.h" - #include "animationparticle.h" #include "imageparticle.h" #include "log.h" #include "particle.h" +#include "particleemitter.h" #include "resources/animation.h" #include "resources/image.h" -#include "resources/resourcemanager.h" #include "resources/imageset.h" +#include "resources/resourcemanager.h" #include <cmath> #define SIN45 0.707106781f #define DEG_RAD_FACTOR 0.017453293f -ParticleEmitter::ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map *map): +ParticleEmitter::ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map *map, int rotation): mOutputPauseLeft(0), mParticleImage(0) { @@ -102,7 +101,9 @@ ParticleEmitter::ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map * else if (name == "horizontal-angle") { mParticleAngleHorizontal = readParticleEmitterProp(propertyNode, 0.0f); + mParticleAngleHorizontal.minVal += rotation; mParticleAngleHorizontal.minVal *= DEG_RAD_FACTOR; + mParticleAngleHorizontal.maxVal += rotation; mParticleAngleHorizontal.maxVal *= DEG_RAD_FACTOR; mParticleAngleHorizontal.changeAmplitude *= DEG_RAD_FACTOR; } @@ -310,19 +311,19 @@ ParticleEmitter::readParticleEmitterProp(xmlNodePtr propertyNode, T def) retval.set((T) XML::getFloatProperty(propertyNode, "min", (double) def), (T) XML::getFloatProperty(propertyNode, "max", (double) def)); - std::string change = XML::getProperty(propertyNode, "change-func", "none"); - T amplitude = (T) XML::getFloatProperty(propertyNode, "change-amplitude", 0.0); - int period = XML::getProperty(propertyNode, "change-period", 0); - int phase = XML::getProperty(propertyNode, "change-phase", 0); - if (change == "saw" || change == "sawtooth") { - retval.setFunction(FUNC_SAW, amplitude, period, phase); - } else if (change == "sine" || change == "sinewave") { - retval.setFunction(FUNC_SINE, amplitude, period, phase); - } else if (change == "triangle") { - retval.setFunction(FUNC_TRIANGLE, amplitude, period, phase); - } else if (change == "square"){ - retval.setFunction(FUNC_SQUARE, amplitude, period, phase); - } + std::string change = XML::getProperty(propertyNode, "change-func", "none"); + T amplitude = (T) XML::getFloatProperty(propertyNode, "change-amplitude", 0.0); + int period = XML::getProperty(propertyNode, "change-period", 0); + int phase = XML::getProperty(propertyNode, "change-phase", 0); + if (change == "saw" || change == "sawtooth") { + retval.setFunction(FUNC_SAW, amplitude, period, phase); + } else if (change == "sine" || change == "sinewave") { + retval.setFunction(FUNC_SINE, amplitude, period, phase); + } else if (change == "triangle") { + retval.setFunction(FUNC_TRIANGLE, amplitude, period, phase); + } else if (change == "square"){ + retval.setFunction(FUNC_SQUARE, amplitude, period, phase); + } return retval; } diff --git a/src/particleemitter.h b/src/particleemitter.h index ad0e33f8..809a6ded 100644 --- a/src/particleemitter.h +++ b/src/particleemitter.h @@ -44,7 +44,7 @@ class ParticleEmitter /** * Constructor. */ - ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map *map); + ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map *map, int rotation = 0); /** * Copy Constructor (necessary for reference counting of particle images) diff --git a/src/particleemitterprop.h b/src/particleemitterprop.h index 70a04aee..f9c329a9 100644 --- a/src/particleemitterprop.h +++ b/src/particleemitterprop.h @@ -19,8 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <cstdlib> #include <cmath> +#include <cstdlib> /** * Returns a random numeric value that is larger than or equal min and smaller diff --git a/src/party.cpp b/src/party.cpp new file mode 100644 index 00000000..3df2eedc --- /dev/null +++ b/src/party.cpp @@ -0,0 +1,216 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "beingmanager.h" +#include "game.h" +#include "localplayer.h" +#include "party.h" + +#include "gui/chat.h" +#include "gui/confirm_dialog.h" + +#include "net/messageout.h" +#include "net/protocol.h" + +Party::Party(ChatWindow *chat, Network *network) : + mChat(chat), mNetwork(network), mInviteListener(network, &mInParty) +{ +} + +void Party::respond(const std::string &command, const std::string &args) +{ + if (command == "new" || command == "create") + { + create(args); + return; + } + if (command == "leave") + { + leave(args); + return; + } + if (command == "settings") + { + mChat->chatLog("Not yet implemented!", BY_SERVER); + return; + /* + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_PARTY_SETTINGS); + outMsg.writeInt16(0); // Experience + outMsg.writeInt16(0); // Item + */ + } + mChat->chatLog("Party command not known.", BY_SERVER); +} + +void Party::create(const std::string &party) +{ + if (party == "") + { + mChat->chatLog("Party name is missing.", BY_SERVER); + return; + } + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_PARTY_CREATE); + outMsg.writeString(party.substr(0, 23), 24); + mCreating = true; +} + +void Party::leave(const std::string &args) +{ + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_PARTY_LEAVE); + mChat->chatLog("Left party.", BY_SERVER); + mInParty = false; +} + +void Party::createResponse(bool ok) +{ + if (ok) + { + mChat->chatLog("Party successfully created.", BY_SERVER); + mInParty = true; + } + else + { + mChat->chatLog("Could not create party.", BY_SERVER); + } +} + +void Party::inviteResponse(const std::string &nick, int status) +{ + switch (status) + { + case 0: + mChat->chatLog(nick + " is already a member of a party.", + BY_SERVER); + break; + case 1: + mChat->chatLog(nick + " refused your invitation.", BY_SERVER); + break; + case 2: + mChat->chatLog(nick + " is now a member of your party.", + BY_SERVER); + break; + } +} + +void Party::invitedAsk(const std::string &nick, int gender, + const std::string &partyName) +{ + mPartyName = partyName; /* Quick and nasty - needs redoing */ + if (nick == "") + { + mChat->chatLog("Something\'s wrong!", BY_SERVER); + return; + } + mCreating = false; + ConfirmDialog *dlg = new ConfirmDialog("Invite to party", + nick + " invites you to join " + + (gender == 0 ? "his" : "her") + + " party, " + partyName + + ", do you accept?"); + dlg->addActionListener(&mInviteListener); +} + +void Party::InviteListener::action(const gcn::ActionEvent &event) +{ + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_PARTY_INVITED); + outMsg.writeInt32(player_node->getId()); + bool accept = event.getId() == "yes"; + outMsg.writeInt32(accept ? 1 : 0); + *mInParty = *mInParty || accept; +} + +void Party::leftResponse(const std::string &nick) +{ + mChat->chatLog(nick + " has left your party.", BY_SERVER); +} + +void Party::receiveChat(Being *being, const std::string &msg) +{ + if (being == NULL) + { + return; + } + if (being->getType() != Being::PLAYER) + { + mChat->chatLog("Something\'s wrong!", BY_SERVER); + return; + } + being->setSpeech(msg, SPEECH_TIME); + mChat->chatLog(being->getName() + " : " + msg, BY_PARTY); +} + +void Party::help() +{ + mChat->chatLog("/party <command> <params>: Party commands.", BY_SERVER); +} + +void Party::help(const std::string &msg) +{ + if (msg == "") + { + mChat->chatLog("Command: /party <command> <args>", BY_SERVER); + mChat->chatLog("where <command> can be one of:", BY_SERVER); + mChat->chatLog(" /new", BY_SERVER); + mChat->chatLog(" /create", BY_SERVER); + mChat->chatLog(" /prefix", BY_SERVER); + mChat->chatLog(" /leave", BY_SERVER); + mChat->chatLog("This command implements the partying function.", + BY_SERVER); + mChat->chatLog("Type /help party <command> for further help.", + BY_SERVER); + return; + } + if (msg == "new" || msg == "create") + { + mChat->chatLog("Command: /party new <party-name>", BY_SERVER); + mChat->chatLog("Command: /party create <party-name>", BY_SERVER); + mChat->chatLog("These commands create a new party <party-name.", + BY_SERVER); + return; + } + if (msg == "prefix") + { + mChat->chatLog("Command: /party prefix <prefix-char>", BY_SERVER); + mChat->chatLog("This command sets the party prefix character.", + BY_SERVER); + mChat->chatLog("Any message preceded by <prefix-char> is sent to " + "the party instead of everyone.", BY_SERVER); + mChat->chatLog("Command: /party prefix", BY_SERVER); + mChat->chatLog("This command reports the current party prefix " + "character.", BY_SERVER); + return; + } + //if (msg == "settings") + //if (msg == "info") + if (msg == "leave") + { + mChat->chatLog("Command: /party leave", BY_SERVER); + mChat->chatLog("This command causes the player to leave the party.", + BY_SERVER); + return; + } + mChat->chatLog("Unknown /party command.", BY_SERVER); + mChat->chatLog("Type /help party for a list of options.", BY_SERVER); +} diff --git a/src/party.h b/src/party.h new file mode 100644 index 00000000..0e1afc3c --- /dev/null +++ b/src/party.h @@ -0,0 +1,73 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _TMW_PARTY_H +#define _TMW_PARTY_H + +#include <string> + +#include <guichan/actionlistener.hpp> + +class PartyHandler; +class Being; +class ChatWindow; +class Network; + +class Party +{ + public: + Party(ChatWindow *chat, Network *network); + void respond(const std::string &command, const std::string &args); + + void create(const std::string &party); + void leave(const std::string &args); + + void createResponse(bool ok); + void inviteResponse(const std::string &nick, int status); + void invitedAsk(const std::string &nick, int gender, + const std::string &partyName); + void leftResponse(const std::string &nick); + void receiveChat(Being *being, const std::string &msg); + + void help(); + void help(const std::string &msg); + private: + ChatWindow *mChat; + std::string mPartyName; + Network *mNetwork; + bool mInParty; + bool mCreating; /**< Used to give an appropriate response to + failure */ + PartyHandler *handler; + + class InviteListener : public gcn::ActionListener + { + public: + InviteListener(Network *network, bool *inParty) : + mNetwork(network), mInParty(inParty) {}; + void action(const gcn::ActionEvent &event); + Network *mNetwork; + private: + bool *mInParty; + }; + InviteListener mInviteListener; +}; +#endif diff --git a/src/player.cpp b/src/player.cpp index f43e54d5..e6244bb5 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -19,21 +19,21 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "player.h" +#include <iostream> #include "animatedsprite.h" #include "game.h" #include "graphics.h" #include "log.h" +#include "player.h" + +#include "gui/gui.h" #include "resources/itemdb.h" #include "resources/iteminfo.h" #include "utils/strprintf.h" -#include "gui/gui.h" -#include <iostream> - static const int NAME_X_OFFSET = 15; static const int NAME_Y_OFFSET = 30; @@ -41,6 +41,7 @@ Player::Player(int id, int job, Map *map): Being(id, job, map) { mName = 0; + mIsGM = false; } Player::~Player() @@ -55,9 +56,15 @@ void Player::setName(const std::string &name) { if (mName == 0) { - mName = new FlashText(name, mPx + NAME_X_OFFSET, mPy + NAME_Y_OFFSET, - gcn::Graphics::CENTER, - speechFont, gcn::Color(255, 255, 255)); + if (mIsGM) { + mName = new FlashText("(GM) " + name, mPx + NAME_X_OFFSET, mPy + NAME_Y_OFFSET, + gcn::Graphics::CENTER, + gmNameFont, gcn::Color(255, 255, 255)); + } else { + mName = new FlashText(name, mPx + NAME_X_OFFSET, mPy + NAME_Y_OFFSET, + gcn::Graphics::CENTER, + speechFont, gcn::Color(255, 255, 255)); + } Being::setName(name); } } @@ -66,6 +73,18 @@ void Player::logic() { switch (mAction) { + case STAND: + break; + + case SIT: + break; + + case DEAD: + break; + + case HURT: + break; + case WALK: mFrame = (get_elapsed_time(mWalkTime) * 6) / mWalkSpeed; if (mFrame >= 6) { @@ -138,13 +157,13 @@ void Player::setGender(int gender) void Player::setHairStyle(int style, int color) { - style = style < 0 ? mHairStyle : style % getHairStylesNr(); - color = color < 0 ? mHairColor : color % getHairColorsNr(); + style = style < 0 ? mHairStyle : style % mNumberOfHairstyles; + color = color < 0 ? mHairColor : color % ColorDB::size(); if (style == mHairStyle && color == mHairColor) return; Being::setHairStyle(style, color); - setSprite(HAIR_SPRITE, style * -1, getHairColor(color)); + setSprite(HAIR_SPRITE, style * -1, ColorDB::get(color)); setAction(mAction); } @@ -194,4 +213,3 @@ void Player::updateCoords() } } - diff --git a/src/player.h b/src/player.h index 4676124c..c91ad3c2 100644 --- a/src/player.h +++ b/src/player.h @@ -80,7 +80,6 @@ class Player : public Being protected: void updateCoords(); - private: FlashText *mName; }; diff --git a/src/player_relations.cpp b/src/player_relations.cpp index c494dc74..ef00a738 100644 --- a/src/player_relations.cpp +++ b/src/player_relations.cpp @@ -19,12 +19,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <algorithm> + #include "beingmanager.h" -#include "player_relations.h" #include "graphics.h" -#include "gui/gui.h" +#include "player_relations.h" -#include <algorithm> +#include "gui/gui.h" #define PLAYER_IGNORE_STRATEGY_NOP "nop" #define PLAYER_IGNORE_STRATEGY_EMOTE0 "emote0" diff --git a/src/player_relations.h b/src/player_relations.h index ec93b4dc..56f3d5a4 100644 --- a/src/player_relations.h +++ b/src/player_relations.h @@ -22,13 +22,14 @@ #ifndef TMW_PLAYER_RELATIONS_H_ #define TMW_PLAYER_RELATIONS_H_ -#include "being.h" -#include "player.h" -#include "configuration.h" -#include <string> +#include <list> #include <map> +#include <string> #include <vector> -#include <list> + +#include "being.h" +#include "configuration.h" +#include "player.h" struct PlayerRelation { diff --git a/src/properties.h b/src/properties.h index 2eafeeca..86fffea3 100644 --- a/src/properties.h +++ b/src/properties.h @@ -23,8 +23,8 @@ #define _TMW_PROPERTIES_H_ #include <map> -#include <string> #include <sstream> +#include <string> /** * A class holding a set of properties. diff --git a/src/recorder.cpp b/src/recorder.cpp new file mode 100644 index 00000000..7bf618c5 --- /dev/null +++ b/src/recorder.cpp @@ -0,0 +1,110 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "recorder.h" + +#include "gui/buttonbox.h" +#include "gui/chat.h" + +#include "utils/trim.h" + +Recorder::Recorder(ChatWindow *chat) : mChat(chat) +{ + mButtonBox = new ButtonBox("Recording...", "Stop recording", this); + mButtonBox->setY(20); +} + +void Recorder::record(const std::string &msg) +{ + if (mStream.is_open()) + { + mStream << msg << std::endl; + } +} + +void Recorder::respond(const std::string &msg) +{ + std::string msgCopy = msg; + trim(msgCopy); + if (msgCopy == "") + { + if (mStream.is_open()) + { + mStream.close(); + mButtonBox->setVisible(false); + /* + * Message should go after mStream is closed so that it isn't + * recorded. + */ + mChat->chatLog("Finishing recording.", BY_SERVER); + } + else + { + mChat->chatLog("Not currently recording.", BY_SERVER); + } + return; + } + if (mStream.is_open()) + { + mChat->chatLog("Already recording.", BY_SERVER); + } + else + { + /* + * Message should go before mStream is opened so that it isn't + * recorded. + */ + mChat->chatLog("Starting to record...", BY_SERVER); + mStream.open(msg.c_str(), std::ios_base::trunc); + if (mStream.is_open()) + { + mButtonBox->setVisible(true); + } + else + { + mChat->chatLog("Failed to start recording.", BY_SERVER); + } + } +} + +void Recorder::help() const +{ + mChat->chatLog("/record <filename>: Start recording the chat.", BY_SERVER); +} + +void Recorder::help(const std::string &args) const +{ + mChat->chatLog("Command: /record <filename>", BY_SERVER); + mChat->chatLog("This command starts recording the chat log to the file " + "<filename>.", BY_SERVER); + mChat->chatLog("Command: /record", BY_SERVER); + mChat->chatLog("This command finishes a recording session.", BY_SERVER); +} + +void Recorder::buttonBoxRespond() +{ + respond(""); +} + +Recorder::~Recorder() +{ + delete mButtonBox; +} diff --git a/src/recorder.h b/src/recorder.h new file mode 100644 index 00000000..9f30184f --- /dev/null +++ b/src/recorder.h @@ -0,0 +1,48 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _TMW_RECORD_H +#define _TMW_RECORD_H + +#include <fstream> +#include <string> + +#include "gui/buttonbox.h" + +class ChatWindow; + +class Recorder : public ButtonBoxListener +{ + public: + Recorder(ChatWindow *chat); + void record(const std::string &msg); + void respond(const std::string &msg); + void help() const; + void help(const std::string &args) const; + void buttonBoxRespond(); + bool isRecording() const {return mStream;} + virtual ~Recorder(); + private: + ChatWindow *mChat; + std::ofstream mStream; + ButtonBox *mButtonBox; +}; +#endif diff --git a/src/resources/action.cpp b/src/resources/action.cpp index ffbbffb2..f40d3109 100644 --- a/src/resources/action.cpp +++ b/src/resources/action.cpp @@ -20,22 +20,17 @@ */ #include "action.h" - -#include <algorithm> - #include "animation.h" #include "../utils/dtor.h" - Action::Action() { } Action::~Action() { - std::for_each(mAnimations.begin(), mAnimations.end(), - make_dtor(mAnimations)); + delete_all(mAnimations); } Animation* diff --git a/src/resources/ambientoverlay.cpp b/src/resources/ambientoverlay.cpp index 9eee57f0..38d8fc46 100644 --- a/src/resources/ambientoverlay.cpp +++ b/src/resources/ambientoverlay.cpp @@ -20,7 +20,6 @@ */ #include "ambientoverlay.h" - #include "image.h" #include "../graphics.h" diff --git a/src/resources/animation.cpp b/src/resources/animation.cpp index d2794e61..596c5fac 100644 --- a/src/resources/animation.cpp +++ b/src/resources/animation.cpp @@ -19,10 +19,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "animation.h" - #include <algorithm> +#include "animation.h" + #include "../utils/dtor.h" Animation::Animation(): diff --git a/src/resources/buddylist.cpp b/src/resources/buddylist.cpp index c85105c5..1bd98680 100644 --- a/src/resources/buddylist.cpp +++ b/src/resources/buddylist.cpp @@ -21,13 +21,13 @@ #include <algorithm> #include <cstring> -#include <iostream> #include <fstream> +#include <iostream> #include "buddylist.h" -#include "../main.h" #include "../configuration.h" +#include "../main.h" BuddyList::BuddyList() { diff --git a/src/resources/colordb.cpp b/src/resources/colordb.cpp new file mode 100644 index 00000000..3d2e15e0 --- /dev/null +++ b/src/resources/colordb.cpp @@ -0,0 +1,126 @@ +/* + * Aethyra + * Copyright 2008 Aethyra Development Team + * + * This file is part of Aethyra. + * + * Aethyra 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. + * + * Aethyra 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 Aethyra; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <cassert> +#include <libxml/tree.h> + +#include "colordb.h" + +#include "../log.h" + +#include "../utils/dtor.h" +#include "../utils/xml.h" + +#define HAIR_COLOR_FILE "colors.xml" +#define TMW_COLOR_FILE "hair.xml" + +namespace +{ + ColorDB::Colors mColors; + bool mLoaded = false; + std::string mFail = "#ffffff"; +} + +void ColorDB::load() +{ + if (mLoaded) + { + return; + } + + XML::Document *doc = new XML::Document(HAIR_COLOR_FILE); + xmlNodePtr root = doc->rootNode(); + bool TMWHair = false; + + if (!root || !xmlStrEqual(root->name, BAD_CAST "colors")) + { + logger->log("Trying TMW's color file, %s.", TMW_COLOR_FILE); + + TMWHair = true; + + delete doc; + + doc = new XML::Document(TMW_COLOR_FILE); + root = doc->rootNode(); + if (!root || !xmlStrEqual(root->name, BAD_CAST "colors")) + { + logger->log("ColorDB: Failed"); + mColors[0] = mFail; + mLoaded = true; + + delete doc; + + return; + } + } + for_each_xml_child_node(node, root) + { + if (xmlStrEqual(node->name, BAD_CAST "color")) + { + int id = XML::getProperty(node, "id", 0); + + if (mColors.find(id) != mColors.end()) + { + logger->log("ColorDB: Redefinition of dye ID %d", id); + } + + TMWHair ? mColors[id] = XML::getProperty(node, "value", "#FFFFFF") : + mColors[id] = XML::getProperty(node, "dye", "#FFFFFF"); + + logger->log("%d %s", id, mColors[id].c_str()); + } + } + + delete doc; + + mLoaded = true; +} + +void ColorDB::unload() +{ + logger->log("Unloading color database..."); + + mColors.clear(); + mLoaded = false; +} + +std::string& ColorDB::get(int id) +{ + if(!mLoaded) + load(); + + ColorIterator i = mColors.find(id); + + if (i == mColors.end()) + { + logger->log("ColorDB: Error, unknown dye ID# %d", id); + return mFail; + } + else + { + return i->second; + } +} + +int ColorDB::size() +{ + return mColors.size(); +} diff --git a/src/resources/colordb.h b/src/resources/colordb.h new file mode 100644 index 00000000..2b750fa3 --- /dev/null +++ b/src/resources/colordb.h @@ -0,0 +1,52 @@ +/* + * Aethyra + * Copyright 2008 Aethyra Development Team + * + * This file is part of Aethyra. + * + * Aethyra 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. + * + * Aethyra 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 Aethyra; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _AETHYRA_COLOR_MANAGER_H +#define _AETHYRA_COLOR_MANAGER_H + +#include <map> +#include <string> + +/** + * The class that holds the color information. + */ +namespace ColorDB +{ + /** + * Loads the color data from <code>colors.xml</code>. + */ + void load(); + + /** + * Clear the color data + */ + void unload(); + + std::string& get(int id); + + int size(); + + // Color DB + typedef std::map<int, std::string> Colors; + typedef Colors::iterator ColorIterator; +}; + +#endif diff --git a/src/resources/dye.h b/src/resources/dye.h index 528a1d91..4fb8fd40 100644 --- a/src/resources/dye.h +++ b/src/resources/dye.h @@ -22,6 +22,7 @@ #ifndef _TMW_DYE_H #define _TMW_DYE_H +#include <string> #include <vector> /** @@ -36,7 +37,7 @@ class Palette * The string is either a file name or a sequence of hexadecimal RGB * values separated by ',' and starting with '#'. */ - Palette(std::string const &); + Palette(std::string const &pallete); /** * Gets a pixel color depending on its intensity. @@ -63,7 +64,7 @@ class Dye * The parts of string are separated by semi-colons. Each part starts * by an uppercase letter, followed by a colon and then a palette name. */ - Dye(std::string const &); + Dye(std::string const &dye); /** * Destroys the associated palettes. diff --git a/src/resources/image.cpp b/src/resources/image.cpp index 77d77f96..35b9c254 100644 --- a/src/resources/image.cpp +++ b/src/resources/image.cpp @@ -21,9 +21,8 @@ #include <SDL_image.h> -#include "image.h" - #include "dye.h" +#include "image.h" #include "../log.h" diff --git a/src/resources/image.h b/src/resources/image.h index 3677696f..6eb33ed9 100644 --- a/src/resources/image.h +++ b/src/resources/image.h @@ -22,9 +22,10 @@ #ifndef _TMW_IMAGE_H #define _TMW_IMAGE_H +#include <SDL.h> + #include "../main.h" -#include <SDL.h> #ifdef USE_OPENGL /* The definition of OpenGL extensions by SDL is giving problems with recent diff --git a/src/resources/imageloader.cpp b/src/resources/imageloader.cpp index 29458ba3..a7e813d7 100644 --- a/src/resources/imageloader.cpp +++ b/src/resources/imageloader.cpp @@ -21,12 +21,12 @@ #include <cassert> #include <string> + #include <guichan/color.hpp> #include <guichan/sdl/sdlpixel.hpp> -#include "imageloader.h" - #include "image.h" +#include "imageloader.h" #include "resourcemanager.h" ProxyImage::ProxyImage(SDL_Surface *s): diff --git a/src/resources/imageset.cpp b/src/resources/imageset.cpp index d7398c17..b321439a 100644 --- a/src/resources/imageset.cpp +++ b/src/resources/imageset.cpp @@ -19,14 +19,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <algorithm> - +#include "image.h" #include "imageset.h" #include "../log.h" -#include "image.h" - #include "../utils/dtor.h" ImageSet::ImageSet(Image *img, int width, int height) @@ -44,11 +41,10 @@ ImageSet::ImageSet(Image *img, int width, int height) ImageSet::~ImageSet() { - for_each(mImages.begin(), mImages.end(), make_dtor(mImages)); + delete_all(mImages); } -Image* -ImageSet::get(size_type i) const +Image* ImageSet::get(size_type i) const { if (i >= mImages.size()) { diff --git a/src/resources/imageset.h b/src/resources/imageset.h index 58b7a8ea..26ce99ea 100644 --- a/src/resources/imageset.h +++ b/src/resources/imageset.h @@ -28,7 +28,6 @@ class Image; - /** * Stores a set of subimages originating from a single image. */ diff --git a/src/resources/imagewriter.cpp b/src/resources/imagewriter.cpp index d6d8a6c2..36805646 100644 --- a/src/resources/imagewriter.cpp +++ b/src/resources/imagewriter.cpp @@ -19,11 +19,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "imagewriter.h" - #include <png.h> -#include <string> #include <SDL.h> +#include <string> + +#include "imagewriter.h" #include "../log.h" diff --git a/src/resources/itemdb.cpp b/src/resources/itemdb.cpp index e6f2fd1f..f4ccc511 100644 --- a/src/resources/itemdb.cpp +++ b/src/resources/itemdb.cpp @@ -19,12 +19,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <algorithm> #include <cassert> + #include <libxml/tree.h> #include "itemdb.h" - #include "iteminfo.h" #include "resourcemanager.h" @@ -83,7 +82,7 @@ void ItemDB::load() } int type = XML::getProperty(node, "type", 0); - int weight = XML::getProperty(node, "weight", 0); + //int weight = XML::getProperty(node, "weight", 0); int view = XML::getProperty(node, "view", 0); std::string name = XML::getProperty(node, "name", ""); @@ -101,7 +100,7 @@ void ItemDB::load() itemInfo->setEffect(effect); itemInfo->setType(type); itemInfo->setView(view); - itemInfo->setWeight(weight); + //itemInfo->setWeight(weight); itemInfo->setWeaponType(weaponType); for_each_xml_child_node(itemChild, node) @@ -126,7 +125,7 @@ void ItemDB::load() CHECK_PARAM(name, ""); CHECK_PARAM(image, ""); CHECK_PARAM(description, ""); - CHECK_PARAM(effect, ""); + // CHECK_PARAM(effect, ""); // CHECK_PARAM(type, 0); // CHECK_PARAM(weight, 0); // CHECK_PARAM(slot, 0); @@ -144,7 +143,7 @@ void ItemDB::unload() delete mUnknown; mUnknown = NULL; - for_each(mItemInfos.begin(), mItemInfos.end(), make_dtor(mItemInfos)); + delete_all(mItemInfos); mItemInfos.clear(); mLoaded = false; } @@ -175,7 +174,6 @@ void loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node) { itemInfo->setSprite(filename, 0); } - if (gender == "female" || gender == "unisex") { itemInfo->setSprite(filename, 1); diff --git a/src/resources/itemdb.h b/src/resources/itemdb.h index 62a1e94c..9b661a60 100644 --- a/src/resources/itemdb.h +++ b/src/resources/itemdb.h @@ -22,10 +22,10 @@ #ifndef _TMW_ITEM_MANAGER_H #define _TMW_ITEM_MANAGER_H -#include "iteminfo.h" - #include <map> +#include "iteminfo.h" + /** * The namespace that holds the item information. */ diff --git a/src/resources/iteminfo.cpp b/src/resources/iteminfo.cpp index fb2c8ffe..5daeafe6 100644 --- a/src/resources/iteminfo.cpp +++ b/src/resources/iteminfo.cpp @@ -19,9 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "iteminfo.h" - #include "itemdb.h" +#include "iteminfo.h" const std::string& ItemInfo::getSprite(int gender) const diff --git a/src/resources/mapreader.cpp b/src/resources/mapreader.cpp index 4c37c239..b4beb558 100644 --- a/src/resources/mapreader.cpp +++ b/src/resources/mapreader.cpp @@ -19,14 +19,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "mapreader.h" - #include <cassert> #include <iostream> #include <zlib.h> -#include "resourcemanager.h" #include "image.h" +#include "mapreader.h" +#include "resourcemanager.h" #include "../log.h" #include "../map.h" @@ -143,8 +142,7 @@ inflateMemory(unsigned char *in, unsigned int inLength, return outLength; } -Map* -MapReader::readMap(const std::string &filename) +Map* MapReader::readMap(const std::string &filename) { // Load the file through resource manager ResourceManager *resman = ResourceManager::getInstance(); @@ -207,14 +205,11 @@ MapReader::readMap(xmlNodePtr node, const std::string &path) // Take the filename off the path const std::string pathDir = path.substr(0, path.rfind("/") + 1); - //xmlChar *prop = xmlGetProp(node, BAD_CAST "version"); - //xmlFree(prop); - const int w = XML::getProperty(node, "width", 0); const int h = XML::getProperty(node, "height", 0); - const int tw = XML::getProperty(node, "tilewidth", DEFAULT_TILE_WIDTH); - const int th = XML::getProperty(node, "tileheight", DEFAULT_TILE_HEIGHT); - Map *map = new Map(w, h, tw, th); + const int tilew = XML::getProperty(node, "tilewidth", DEFAULT_TILE_WIDTH); + const int tileh = XML::getProperty(node, "tileheight", DEFAULT_TILE_HEIGHT); + Map *map = new Map(w, h, tilew, tileh); for_each_xml_child_node(childNode, node) { @@ -238,8 +233,8 @@ MapReader::readMap(xmlNodePtr node, const std::string &path) // The object group offset is applied to each object individually const int tileOffsetX = XML::getProperty(childNode, "x", 0); const int tileOffsetY = XML::getProperty(childNode, "y", 0); - const int offsetX = tileOffsetX * tw; - const int offsetY = tileOffsetY * th; + const int offsetX = tileOffsetX * tilew; + const int offsetY = tileOffsetY * tileh; for_each_xml_child_node(objectNode, childNode) { @@ -289,8 +284,7 @@ MapReader::readMap(xmlNodePtr node, const std::string &path) return map; } -void -MapReader::readProperties(xmlNodePtr node, Properties* props) +void MapReader::readProperties(xmlNodePtr node, Properties* props) { for_each_xml_child_node(childNode, node) { @@ -329,8 +323,8 @@ MapReader::readLayer(xmlNodePtr node, Map *map) const int offsetY = XML::getProperty(node, "y", 0); const std::string name = XML::getProperty(node, "name", ""); - const bool isFringeLayer = (name == "Fringe"); - const bool isCollisionLayer = (name == "Collision"); + const bool isFringeLayer = (name.substr(0,6) == "Fringe"); + const bool isCollisionLayer = (name.substr(0,9) == "Collision"); MapLayer *layer = 0; @@ -365,15 +359,15 @@ MapReader::readLayer(xmlNodePtr node, Map *map) xmlNodePtr dataChild = childNode->xmlChildrenNode; if (!dataChild) continue; - + int len = strlen((const char*)dataChild->content) + 1; unsigned char *charData = new unsigned char[len + 1]; const char *charStart = (const char*)dataChild->content; unsigned char *charIndex = charData; - + while (*charStart) { if (*charStart != ' ' && *charStart != '\t' && - *charStart != '\n') + *charStart != '\n') { *charIndex = *charStart; charIndex++; @@ -426,10 +420,10 @@ MapReader::readLayer(xmlNodePtr node, Map *map) } } else { - // Read plain XML map file - for_each_xml_child_node(childNode2, childNode) - { - if (!xmlStrEqual(childNode2->name, BAD_CAST "tile")) + // Read plain XML map file + for_each_xml_child_node(childNode2, childNode) + { + if (!xmlStrEqual(childNode2->name, BAD_CAST "tile")) continue; const int gid = XML::getProperty(childNode2, "gid", -1); @@ -443,12 +437,12 @@ MapReader::readLayer(xmlNodePtr node, Map *map) } } } - + if (y < h) std::cerr << "TOO SMALL!\n"; if (x) std::cerr << "TOO SMALL!\n"; - + // There can be only one data element break; } @@ -459,13 +453,17 @@ MapReader::readTileset(xmlNodePtr node, const std::string &path, Map *map) { + int firstGid = XML::getProperty(node, "firstgid", 0); + XML::Document* doc = NULL; + if (xmlHasProp(node, BAD_CAST "source")) { - logger->log("Warning: External tilesets not supported yet."); - return NULL; + std::string filename = XML::getProperty(node, "source", ""); + doc = new XML::Document(filename); + node = doc->rootNode(); + firstGid += XML::getProperty(node, "firstgid", 0); } - const int firstGid = XML::getProperty(node, "firstgid", 0); const int tw = XML::getProperty(node, "tilewidth", map->getTileWidth()); const int th = XML::getProperty(node, "tileheight", map->getTileHeight()); @@ -479,7 +477,8 @@ MapReader::readTileset(xmlNodePtr node, if (!source.empty()) { std::string sourceStr = source; - sourceStr.erase(0, 3); // Remove "../" + while (sourceStr.substr(0, 3) == "../") + sourceStr.erase(0, 3); // Remove "../" ResourceManager *resman = ResourceManager::getInstance(); Image* tilebmp = resman->getImage(sourceStr); @@ -488,11 +487,11 @@ MapReader::readTileset(xmlNodePtr node, { Tileset *set = new Tileset(tilebmp, tw, th, firstGid); tilebmp->decRef(); + delete doc; return set; } else { - logger->log("Warning: Failed to load tileset (%s)", - source.c_str()); + logger->log("Warning: Failed to load tileset (%s)", source.c_str()); } } @@ -500,5 +499,7 @@ MapReader::readTileset(xmlNodePtr node, break; } + delete doc; + return NULL; } diff --git a/src/resources/mapreader.h b/src/resources/mapreader.h index 0142eb45..ef52564e 100644 --- a/src/resources/mapreader.h +++ b/src/resources/mapreader.h @@ -26,8 +26,8 @@ #include <libxml/tree.h> -class Properties; class Map; +class Properties; class Tileset; /** diff --git a/src/resources/monsterdb.cpp b/src/resources/monsterdb.cpp index 11b2baf7..4d52b8ad 100644 --- a/src/resources/monsterdb.cpp +++ b/src/resources/monsterdb.cpp @@ -19,10 +19,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <algorithm> - #include "monsterdb.h" - #include "resourcemanager.h" #include "../log.h" @@ -126,6 +123,12 @@ MonsterDB::load() } } + if (xmlStrEqual(spriteNode->name, BAD_CAST "attack")) + { + std::string event = XML::getProperty(spriteNode, "particle-effect", ""); + currentInfo->addAttackParticleEffect(event); + } + if (xmlStrEqual(spriteNode->name, BAD_CAST "particlefx")) { currentInfo->addParticleEffect( @@ -141,8 +144,7 @@ MonsterDB::load() void MonsterDB::unload() { - for_each(mMonsterInfos.begin(), mMonsterInfos.end(), - make_dtor(mMonsterInfos)); + delete_all(mMonsterInfos); mMonsterInfos.clear(); mLoaded = false; diff --git a/src/resources/monsterinfo.cpp b/src/resources/monsterinfo.cpp index 7661c86b..4a71a122 100644 --- a/src/resources/monsterinfo.cpp +++ b/src/resources/monsterinfo.cpp @@ -19,8 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <algorithm> - #include "monsterinfo.h" #include "../utils/dtor.h" @@ -33,8 +31,7 @@ MonsterInfo::MonsterInfo() MonsterInfo::~MonsterInfo() { // kill vectors in mSoundEffects - for_each (mSounds.begin(), mSounds.end(), - make_dtor(mSounds)); + delete_all(mSounds); mSounds.clear(); } diff --git a/src/resources/monsterinfo.h b/src/resources/monsterinfo.h index 84b131c6..05a78c5a 100644 --- a/src/resources/monsterinfo.h +++ b/src/resources/monsterinfo.h @@ -22,14 +22,13 @@ #ifndef _TMW_MONSTERINFO_H_ #define _TMW_MONSTERINFO_H_ +#include <list> #include <map> #include <string> #include <vector> -#include <list> #include "../being.h" - enum MonsterSoundEvent { MONSTER_EVENT_HIT, @@ -85,11 +84,19 @@ class MonsterInfo std::string getSound(MonsterSoundEvent event) const; + std::string + getAttackParticleEffect() const { return mAttackParticle; } + + void + addAttackParticleEffect(const std::string &particleEffect) + { mAttackParticle = particleEffect; } + const std::list<std::string>& getParticleEffects() const { return mParticleEffects; } private: std::string mName; + std::string mAttackParticle; std::list<std::string> mSprites; Being::TargetCursorSize mTargetCursorSize; std::map<MonsterSoundEvent, std::vector<std::string>* > mSounds; diff --git a/src/resources/npcdb.cpp b/src/resources/npcdb.cpp index 2f8d78d4..3ae58067 100644 --- a/src/resources/npcdb.cpp +++ b/src/resources/npcdb.cpp @@ -20,7 +20,6 @@ */ #include "npcdb.h" - #include "resourcemanager.h" #include "../log.h" diff --git a/src/resources/npcdb.h b/src/resources/npcdb.h index 00b4f99b..b4539866 100644 --- a/src/resources/npcdb.h +++ b/src/resources/npcdb.h @@ -22,8 +22,8 @@ #ifndef _TMW_NPC_DB_H #define _TMW_NPC_DB_H -#include <map> #include <list> +#include <map> #include <string> struct NPCsprite diff --git a/src/resources/resource.cpp b/src/resources/resource.cpp index 449caf55..e9310905 100644 --- a/src/resources/resource.cpp +++ b/src/resources/resource.cpp @@ -22,7 +22,6 @@ #include <cassert> #include "resource.h" - #include "resourcemanager.h" Resource::~Resource() diff --git a/src/resources/resourcemanager.cpp b/src/resources/resourcemanager.cpp index 90b29374..510a16bd 100644 --- a/src/resources/resourcemanager.cpp +++ b/src/resources/resourcemanager.cpp @@ -20,24 +20,22 @@ */ #include <cassert> -#include <sstream> -#include <sys/time.h> - #include <physfs.h> #include <SDL_image.h> +#include <sstream> -#include "resourcemanager.h" +#include <sys/time.h> #include "dye.h" #include "image.h" +#include "imageset.h" #include "music.h" +#include "resourcemanager.h" #include "soundeffect.h" -#include "imageset.h" #include "spritedef.h" #include "../log.h" - ResourceManager *ResourceManager::instance = NULL; ResourceManager::ResourceManager() @@ -208,7 +206,7 @@ ResourceManager::getPath(const std::string &file) // get the real path to the file const char* tmp = PHYSFS_getRealDir(file.c_str()); std::string path; - + // if the file is not in the search path, then its NULL if (tmp) { @@ -217,9 +215,9 @@ ResourceManager::getPath(const std::string &file) else { // if not found in search path return the default path - path = std::string(TMW_DATADIR) + std::string("data") + "/" + file; + path = std::string(AETHYRA_DATADIR) + std::string("data") + "/" + file; } - + return path; } diff --git a/src/resources/resourcemanager.h b/src/resources/resourcemanager.h index 66813a9c..c814d752 100644 --- a/src/resources/resourcemanager.h +++ b/src/resources/resourcemanager.h @@ -27,11 +27,11 @@ #include <string> #include <vector> -class Resource; class Image; +class ImageSet; class Music; +class Resource; class SoundEffect; -class ImageSet; class SpriteDef; struct SDL_Surface; diff --git a/src/resources/spritedef.cpp b/src/resources/spritedef.cpp index 289df2e5..b4193fd3 100644 --- a/src/resources/spritedef.cpp +++ b/src/resources/spritedef.cpp @@ -21,14 +21,13 @@ #include <set> -#include "spritedef.h" - #include "action.h" #include "animation.h" #include "dye.h" #include "image.h" #include "imageset.h" #include "resourcemanager.h" +#include "spritedef.h" #include "../log.h" #include "../utils/xml.h" diff --git a/src/resources/spritedef.h b/src/resources/spritedef.h index c7b94d9a..4b712340 100644 --- a/src/resources/spritedef.h +++ b/src/resources/spritedef.h @@ -25,10 +25,10 @@ #include <map> #include <string> -#include "resource.h" - #include <libxml/tree.h> +#include "resource.h" + class Action; class ImageSet; diff --git a/src/simpleanimation.cpp b/src/simpleanimation.cpp index e8c26df1..544d02b5 100644 --- a/src/simpleanimation.cpp +++ b/src/simpleanimation.cpp @@ -19,15 +19,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "simpleanimation.h" - #include "graphics.h" #include "log.h" +#include "simpleanimation.h" #include "resources/image.h" -#include "resources/resourcemanager.h" #include "resources/imageset.h" - +#include "resources/resourcemanager.h" SimpleAnimation::SimpleAnimation(xmlNodePtr animationNode): mAnimationTime(0), diff --git a/src/sound.cpp b/src/sound.cpp index 888dcc31..6d440134 100644 --- a/src/sound.cpp +++ b/src/sound.cpp @@ -19,11 +19,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "sound.h" - #include <SDL.h> #include "log.h" +#include "sound.h" + #include "resources/resourcemanager.h" #include "resources/soundeffect.h" diff --git a/src/sound.h b/src/sound.h index 0c2af74b..72180607 100644 --- a/src/sound.h +++ b/src/sound.h @@ -23,7 +23,6 @@ #define _TMW_SOUND_H #include <SDL_mixer.h> - #include <string> /** Sound engine diff --git a/src/text.cpp b/src/text.cpp index 22228ccb..4d36b8fc 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -1,30 +1,29 @@ -/* - * The Mana World - * Copyright 2008 The Mana World Development Team - * - * This file is part of The Mana World. - * - * The Mana World is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * The Mana World is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with The Mana World; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "text.h" +/*************************************************************************** + * Copyright (C) 2008 by Douglas Boffey * + * * + * DougABoffey@netscape.net * + * 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 * + * (at your option) any later version. * + * * + * This program is distributed with The Mana Experiment * + * 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 <cstring> #include <guichan/font.hpp> +#include "text.h" #include "textmanager.h" int Text::mInstances = 0; @@ -1,28 +1,29 @@ -/* - * The Mana World - * Copyright 2008 The Mana World Development Team - * - * This file is part of The Mana World. - * - * The Mana World is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * The Mana World is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with The Mana World; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ +/*************************************************************************** + * Copyright (C) 2008 by Douglas Boffey * + * * + * DougABoffey@netscape.net * + * 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 * + * (at your option) any later version. * + * * + * This program is distributed with The Mana Experiment * + * 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 _TMW_TEXT_H -#define _TMW_TEXT_H +#ifndef _AETHYRA_TEXT_H +#define _AETHYRA_TEXT_H #include "graphics.h" +#include "guichanfwd.h" #include <list> @@ -75,17 +76,21 @@ class FlashText : public Text gcn::Color colour); /** - * Flash the text for so many refreshes. + * Remove the text from the screen + */ + virtual ~FlashText() {} + + /** + * Flash the text for so many refreshes */ void flash(int time) {mTime = time; } /** - * Draws the text. + * Draws the text */ virtual void draw(Graphics *graphics, int xOff, int yOff); private: - int mTime; /**< Time left for flashing. */ + int mTime; /**< Time left for flashing */ }; - -#endif // _TMW_TEXT_H +#endif diff --git a/src/textmanager.cpp b/src/textmanager.cpp index 49ad1824..cb5d0bf2 100644 --- a/src/textmanager.cpp +++ b/src/textmanager.cpp @@ -1,29 +1,28 @@ -/* - * The Mana World - * Copyright 2008 The Mana World Development Team - * - * This file is part of The Mana World. - * - * The Mana World is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * The Mana World is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with The Mana World; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "textmanager.h" +/*************************************************************************** + * Copyright (C) 2008 by Douglas Boffey * + * * + * DougABoffey@netscape.net * + * 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 * + * (at your option) any later version. * + * * + * This program is distributed with The Mana Experiment * + * 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 <cstring> #include "text.h" +#include "textmanager.h" TextManager *textManager = 0; diff --git a/src/textmanager.h b/src/textmanager.h index ec82c61f..5b21ba81 100644 --- a/src/textmanager.h +++ b/src/textmanager.h @@ -1,26 +1,26 @@ -/* - * The Mana World - * Copyright 2008 The Mana World Development Team - * - * This file is part of The Mana World. - * - * The Mana World is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * The Mana World is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with The Mana World; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ +/*************************************************************************** + * Copyright (C) 2008 by Douglas Boffey * + * * + * DougABoffey@netscape.net * + * 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 * + * (at your option) any later version. * + * * + * This program is distributed with The Mana Experiment * + * 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 _TMW_TEXTMANAGER_H -#define _TMW_TEXTMANAGER_H +#ifndef _AETHYRA_TEXTMANAGER_H +#define _AETHYRA_TEXTMANAGER_H #include <list> @@ -72,5 +72,4 @@ class TextManager }; extern TextManager *textManager; - -#endif // _TMW_TEXTMANAGER_H +#endif diff --git a/src/textparticle.cpp b/src/textparticle.cpp index 308c043d..ed9d5717 100644 --- a/src/textparticle.cpp +++ b/src/textparticle.cpp @@ -19,9 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "textparticle.h" - #include "graphics.h" +#include "textparticle.h" TextParticle::TextParticle(Map *map, const std::string &text, int colorR, int colorG, int colorB, diff --git a/src/textparticle.h b/src/textparticle.h index 3a0ba674..f662621f 100644 --- a/src/textparticle.h +++ b/src/textparticle.h @@ -22,11 +22,10 @@ #ifndef _TEXTPARTICLE_H #define _TEXTPARTICLE_H -#include "particle.h" - #include <guichan/color.hpp> #include "guichanfwd.h" +#include "particle.h" class TextParticle : public Particle { diff --git a/src/utils/base64.cpp b/src/utils/base64.cpp index 8cea60f9..9d8ba836 100644 --- a/src/utils/base64.cpp +++ b/src/utils/base64.cpp @@ -27,8 +27,8 @@ +----------------------------------------------------------------------+ */ -#include <string.h> #include <stdlib.h> +#include <string.h> #include "base64.h" diff --git a/src/utils/dtor.h b/src/utils/dtor.h index f7c790c6..514ea9e7 100644 --- a/src/utils/dtor.h +++ b/src/utils/dtor.h @@ -22,6 +22,7 @@ #ifndef _TMW_UTILS_DTOR_H #define _TMW_UTILS_DTOR_H +#include <algorithm> #include <functional> #include <utility> @@ -44,4 +45,11 @@ inline dtor<typename Cont::value_type> make_dtor(Cont const&) return dtor<typename Cont::value_type>(); } +template<typename Container> +inline void delete_all(Container &c) +{ + std::for_each(c.begin(), c.end(), make_dtor(c)); +} + + #endif diff --git a/src/utils/strprintf.cpp b/src/utils/strprintf.cpp index c5d7a595..c532dd0d 100644 --- a/src/utils/strprintf.cpp +++ b/src/utils/strprintf.cpp @@ -45,3 +45,4 @@ std::string strprintf(char const *format, ...) delete [] buf2; return res; } + diff --git a/src/utils/xml.cpp b/src/utils/xml.cpp index 47f1bd04..e511ced3 100644 --- a/src/utils/xml.cpp +++ b/src/utils/xml.cpp @@ -20,7 +20,9 @@ */ #include "xml.h" + #include "../log.h" + #include "../resources/resourcemanager.h" namespace XML diff --git a/src/utils/xml.h b/src/utils/xml.h index 5a5c756b..9e691963 100644 --- a/src/utils/xml.h +++ b/src/utils/xml.h @@ -22,10 +22,10 @@ #ifndef _TMW_XML_H #define _TMW_XML_H -#include <libxml/tree.h> - #include <string> +#include <libxml/tree.h> + /** * XML helper functions. */ diff --git a/src/vector.cpp b/src/vector.cpp new file mode 100644 index 00000000..7d5f055a --- /dev/null +++ b/src/vector.cpp @@ -0,0 +1,28 @@ +/* + * The Mana World + * Copyright 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "vector.h" + +std::ostream& operator <<(std::ostream &os, const Vector &v) +{ + os << "Vector(" << v.x << ", " << v.y << ", " << v.z << ")"; + return os; +} diff --git a/src/vector.h b/src/vector.h index b19f6c64..f32b201a 100644 --- a/src/vector.h +++ b/src/vector.h @@ -22,6 +22,10 @@ #ifndef _TMW_VECTOR_H_ #define _TMW_VECTOR_H_ +#include <math.h> + +#include <iostream> + /** * Vector class. Represents either a 3D point in space, a velocity or a force. * Provides several convenient operator overloads. @@ -41,7 +45,7 @@ class Vector /** * Constructor. */ - Vector(float x, float y, float z): + Vector(float x, float y, float z = 0.0f): x(x), y(y), z(z) @@ -69,11 +73,12 @@ class Vector /** * In-place scale vector operator. */ - void operator*=(float c) + Vector &operator*=(float c) { x *= c; y *= c; z *= c; + return *this; } /** @@ -87,6 +92,17 @@ class Vector } /** + * In-place scale vector operator. + */ + Vector &operator/=(float c) + { + x /= c; + y /= c; + z /= c; + return *this; + } + + /** * Add vector operator. */ Vector operator+(const Vector &v) const @@ -99,11 +115,12 @@ class Vector /** * In-place add vector operator. */ - void operator+=(const Vector &v) + Vector &operator+=(const Vector &v) { x += v.x; y += v.y; z += v.z; + return *this; } /** @@ -119,14 +136,55 @@ class Vector /** * In-place substract vector operator. */ - void operator-=(const Vector &v) + Vector &operator-=(const Vector &v) { x -= v.x; y -= v.y; z -= v.z; + return *this; + } + + /** + * Returns the length of this vector. This method does a relatively + * slow square root. + */ + float length() const + { + return sqrtf(x * x + y * y + z * z); + } + + /** + * Returns the squared length of this vector. Avoids the square root. + */ + float squaredLength() const + { + return x * x + y * y + z * z; + } + + /** + * Returns the manhattan length of this vector. + */ + float manhattanLength() const + { + return fabsf(x) + fabsf(y) + fabsf(z); + } + + /** + * Returns a normalized version of this vector. This is a unit vector + * running parallel to it. + */ + Vector normalized() const + { + const float l = length(); + return Vector(x / l, y / l, z / l); } float x, y, z; }; -#endif +/** + * Appends a string representation of a vector to the output stream. + */ +std::ostream& operator <<(std::ostream &os, const Vector &v); + +#endif // _TMW_VECTOR_H_ diff --git a/src/winver.h b/src/winver.h index 4452784e..f56d0c6b 100644 --- a/src/winver.h +++ b/src/winver.h @@ -1,6 +1,6 @@ -/* VERSION DEFINITIONS */ -#define VER_MAJOR 0 -#define VER_MINOR 0 -#define VER_RELEASE 26 -#define VER_BUILD 0 -#define PACKAGE_VERSION "0.0.26" +/* VERSION DEFINITIONS */
+#define VER_MAJOR 0
+#define VER_MINOR 0
+#define VER_RELEASE 27
+#define VER_BUILD 0
+#define PACKAGE_VERSION "0.0.27" |