diff options
Diffstat (limited to 'src')
291 files changed, 9710 insertions, 3651 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 70a58272..c7e3d936 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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/widgets/layout.cpp @@ -79,12 +81,20 @@ SET(SRCS gui/chatinput.h gui/checkbox.cpp gui/checkbox.h + gui/color.cpp + gui/color.h gui/confirm_dialog.cpp gui/confirm_dialog.h gui/connection.cpp gui/connection.h gui/debugwindow.cpp gui/debugwindow.h + gui/emotecontainer.cpp + gui/emotecontainer.h + gui/emoteshortcutcontainer.cpp + gui/emoteshortcutcontainer.h + gui/emotewindow.cpp + gui/emotewindow.h gui/equipmentwindow.cpp gui/equipmentwindow.h gui/focushandler.cpp @@ -101,10 +111,12 @@ SET(SRCS gui/inventorywindow.h gui/itemcontainer.cpp gui/itemcontainer.h + gui/itemlinkhandler.cpp + gui/itemlinkhandler.h + gui/itempopup.cpp + gui/itempopup.h gui/itemshortcutcontainer.cpp - gui/itemshortcutcontainer.h - gui/itemshortcutwindow.cpp - gui/itemshortcutwindow.h + gui/itemshortcutcontainer.h\ gui/item_amount.cpp gui/item_amount.h gui/linkhandler.h @@ -138,6 +150,8 @@ SET(SRCS gui/progressbar.h gui/radiobutton.cpp gui/radiobutton.h + gui/recorder.cpp + gui/recorder.h gui/register.cpp gui/register.h gui/scrollarea.cpp @@ -146,10 +160,12 @@ SET(SRCS gui/sdlinput.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_colors.cpp + gui/setup_colors.h gui/setup_joystick.cpp gui/setup_joystick.h gui/setup_keyboard.cpp @@ -163,10 +179,16 @@ SET(SRCS gui/shop.h gui/shoplistbox.cpp gui/shoplistbox.h + gui/shortcutwindow.cpp + gui/shortcutwindow.h + gui/shortcutcontainer.cpp + gui/shortcutcontainer.h gui/skill.cpp gui/skill.h gui/slider.cpp gui/slider.h + gui/speechbubble.cpp + gui/speechbubble.h gui/status.cpp gui/status.h gui/table.cpp @@ -179,6 +201,8 @@ SET(SRCS gui/textfield.h gui/trade.cpp gui/trade.h + gui/truetypefont.cpp + gui/truetypefont.h gui/updatewindow.h gui/updatewindow.cpp gui/viewport.cpp @@ -215,6 +239,8 @@ SET(SRCS net/network.h net/npchandler.cpp net/npchandler.h + net/partyhandler.cpp + net/partyhandler.h net/playerhandler.cpp net/playerhandler.h net/protocol.cpp @@ -231,8 +257,12 @@ SET(SRCS resources/animation.h resources/buddylist.cpp resources/buddylist.h + resources/colordb.cpp + resources/colordb.h resources/dye.cpp resources/dye.h + resources/emotedb.cpp + resources/emotedb.h resources/image.cpp resources/image.h resources/imageloader.cpp @@ -285,6 +315,10 @@ SET(SRCS configlistener.h configuration.cpp configuration.h + effectmanager.cpp + effectmanager.h + emoteshortcut.cpp + emoteshortcut.h engine.cpp engine.h equipment.cpp @@ -328,9 +362,13 @@ SET(SRCS openglgraphics.h particle.cpp particle.h + particlecontainer.cpp + particlecontainer.h particleemitter.cpp particleemitter.h particleemitterprop.h + party.cpp + party.h player.cpp player.h player_relations.cpp @@ -346,6 +384,10 @@ SET(SRCS sound.cpp sound.h sprite.h + text.cpp + text.h + textmanager.cpp + textmanager.h textparticle.cpp textparticle.h tileset.h diff --git a/src/Makefile.am b/src/Makefile.am index 358b0b61..8de13464 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,9 @@ AUTOMAKE_OPTIONS = subdir-objects bin_PROGRAMS = tmw -tmw_SOURCES = gui/widgets/layout.cpp \ +tmw_SOURCES = gui/widgets/dropdown.cpp \ + gui/widgets/dropdown.h \ + gui/widgets/layout.cpp \ gui/widgets/layout.h \ gui/widgets/layouthelper.cpp \ gui/widgets/layouthelper.h \ @@ -13,30 +15,36 @@ tmw_SOURCES = gui/widgets/layout.cpp \ gui/widgets/tabbedarea.h \ gui/browserbox.cpp \ gui/browserbox.h \ - gui/buddywindow.cpp \ - gui/buddywindow.h \ gui/button.cpp \ gui/button.h \ gui/buy.cpp \ gui/buy.h \ gui/buysell.cpp \ gui/buysell.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/color.cpp \ + gui/color.h \ gui/confirm_dialog.cpp \ gui/confirm_dialog.h \ gui/connection.cpp \ gui/connection.h \ gui/debugwindow.cpp \ gui/debugwindow.h \ + gui/emotecontainer.cpp \ + gui/emotecontainer.h \ + gui/emoteshortcutcontainer.cpp \ + gui/emoteshortcutcontainer.h \ + gui/emotewindow.cpp \ + gui/emotewindow.h \ gui/equipmentwindow.cpp \ gui/equipmentwindow.h \ gui/focushandler.cpp \ @@ -53,10 +61,12 @@ tmw_SOURCES = gui/widgets/layout.cpp \ gui/inventorywindow.h \ gui/itemcontainer.cpp \ gui/itemcontainer.h \ + gui/itemlinkhandler.cpp \ + gui/itemlinkhandler.h \ + gui/itempopup.cpp \ + gui/itempopup.h \ gui/itemshortcutcontainer.cpp \ gui/itemshortcutcontainer.h \ - gui/itemshortcutwindow.cpp \ - gui/itemshortcutwindow.h \ gui/item_amount.cpp \ gui/item_amount.h \ gui/linkhandler.h \ @@ -80,8 +90,6 @@ tmw_SOURCES = gui/widgets/layout.cpp \ gui/npc_text.h \ gui/ok_dialog.cpp \ gui/ok_dialog.h \ - gui/truetypefont.cpp \ - gui/truetypefont.h \ gui/passwordfield.cpp \ gui/passwordfield.h \ gui/playerbox.cpp \ @@ -92,6 +100,8 @@ tmw_SOURCES = gui/widgets/layout.cpp \ gui/progressbar.h \ gui/radiobutton.cpp \ gui/radiobutton.h \ + gui/recorder.cpp \ + gui/recorder.h \ gui/register.cpp \ gui/register.h \ gui/scrollarea.cpp \ @@ -100,10 +110,12 @@ tmw_SOURCES = gui/widgets/layout.cpp \ gui/sdlinput.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_colors.cpp \ + gui/setup_colors.h \ gui/setup_joystick.cpp \ gui/setup_joystick.h \ gui/setup_keyboard.cpp \ @@ -117,22 +129,30 @@ tmw_SOURCES = gui/widgets/layout.cpp \ gui/shop.h \ gui/shoplistbox.cpp \ gui/shoplistbox.h \ + gui/shortcutwindow.cpp \ + gui/shortcutwindow.h \ + gui/shortcutcontainer.cpp \ + gui/shortcutcontainer.h \ gui/skill.cpp \ gui/skill.h \ gui/slider.cpp \ gui/slider.h \ + gui/speechbubble.cpp \ + gui/speechbubble.h \ gui/status.cpp \ gui/status.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 \ gui/textfield.h \ gui/trade.cpp \ gui/trade.h \ + gui/truetypefont.cpp \ + gui/truetypefont.h \ gui/updatewindow.h \ gui/updatewindow.cpp \ gui/viewport.cpp \ @@ -169,6 +189,8 @@ tmw_SOURCES = gui/widgets/layout.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 \ @@ -183,8 +205,14 @@ tmw_SOURCES = gui/widgets/layout.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/emotedb.cpp \ + resources/emotedb.h \ resources/image.cpp \ resources/image.h \ resources/imageloader.cpp \ @@ -215,8 +243,6 @@ tmw_SOURCES = gui/widgets/layout.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 \ @@ -240,6 +266,10 @@ tmw_SOURCES = gui/widgets/layout.cpp \ configlistener.h \ configuration.cpp \ configuration.h \ + effectmanager.cpp \ + effectmanager.h \ + emoteshortcut.cpp \ + emoteshortcut.h \ engine.cpp \ engine.h \ equipment.cpp \ @@ -283,11 +313,13 @@ tmw_SOURCES = gui/widgets/layout.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 \ @@ -318,5 +350,5 @@ tmw_SOURCES = gui/widgets/layout.cpp \ # set the include path found by configure INCLUDES = \ $(all_includes) \ - -DTMW_DATADIR=\""$(pkgdatadir)/"\" \ + -DPKG_DATADIR=\""$(pkgdatadir)/"\" \ -DLOCALEDIR=\""$(localedir)"\" diff --git a/src/animatedsprite.cpp b/src/animatedsprite.cpp index 25a9df6c..aa2fb4ee 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 ca4f4d31..9c1f7ccb 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 7036b8cd..dcb45a9d 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -22,52 +22,68 @@ #include "being.h" #include "animatedsprite.h" -#include "equipment.h" +#include "configuration.h" +#include "effectmanager.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 "statuseffect.h" -#include "resources/itemdb.h" -#include "resources/resourcemanager.h" +#include "gui/speechbubble.h" + +#include "resources/colordb.h" + +#include "resources/emotedb.h" +#include "resources/image.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/gettext.h" #include "utils/tostring.h" - +#include "utils/trim.h" #include "utils/xml.h" #include <cassert> +#include <cmath> #define BEING_EFFECTS_FILE "effects.xml" #define HAIR_FILE "hair.xml" int Being::instances = 0; -ImageSet *Being::emotionSet = NULL; +int Being::mNumberOfHairstyles = 1; +std::vector<AnimatedSprite*> Being::emotionSet; static const int X_SPEECH_OFFSET = 18; static const int Y_SPEECH_OFFSET = 60; +static const int DEFAULT_WIDTH = 32; +static const int DEFAULT_HEIGHT = 32; + 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), - mEquipment(new Equipment()), mId(id), mWalkSpeed(150), mDirection(DOWN), mMap(NULL), + mName(""), + mIsGM(false), + mParticleEffects(config.getValue("particleeffects", 1)), mEquippedWeapon(NULL), mHairStyle(1), mHairColor(0), mGender(GENDER_UNSPECIFIED), @@ -83,16 +99,33 @@ 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!"); + // Setup emote sprites + for (int i = 0; i <= EmoteDB::getLast(); i++) + { + EmoteInfo info = EmoteDB::get(i); + + std::string file = "graphics/sprites/" + info.sprites.front()->sprite; + int variant = info.sprites.front()->variant; + emotionSet.push_back(AnimatedSprite::load(file, variant)); + } + + // Hairstyles are encoded as negative numbers. Count how far negative we can go. + int hairstyles = 1; + while (ItemDB::get(-hairstyles).getSprite(GENDER_MALE) != "error.xml") + { + hairstyles++; + } + mNumberOfHairstyles = hairstyles; } instances++; - mSpeech = 0; + mSpeech = ""; + mNameColor = 0x202020; + mText = 0; } Being::~Being() @@ -106,11 +139,11 @@ Being::~Being() if (instances == 0) { - emotionSet->decRef(); - emotionSet = NULL; + delete_all(emotionSet); } - delete mSpeech; + delete mSpeechBubble; + delete mText; } void Being::setDestination(Uint16 destX, Uint16 destY) @@ -139,8 +172,8 @@ void Being::setPath(const Path &path) 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) @@ -152,13 +185,44 @@ void Being::setSprite(int slot, int id, std::string color) void Being::setSpeech(const std::string &text, Uint32 time) { - // don't introduce a memory leak - delete mSpeech; + mSpeech = text; + + // Trim whitespace + trim(mSpeech); + + // check for links + std::string::size_type start = mSpeech.find('['); + std::string::size_type end = mSpeech.find(']', start); - mSpeech = new Text(text, mPx + X_SPEECH_OFFSET, mPy - Y_SPEECH_OFFSET, - gcn::Graphics::CENTER, - gcn::Color(255, 255, 255), true); - mSpeechTime = 500; + while (start != std::string::npos && end != std::string::npos) + { + // Catch multiple embeds and ignore them so it doesn't crash the client. + while ((mSpeech.find('[', start + 1) != std::string::npos) && + (mSpeech.find('[', start + 1) < end)) + { + start = mSpeech.find('[', start + 1); + } + + std::string::size_type position = mSpeech.find('|'); + if (mSpeech[start + 1] == '@' && mSpeech[start + 2] == '@') + { + mSpeech.erase(end, 1); + mSpeech.erase(start, (position - start) + 1); + } + position = mSpeech.find('@'); + + while (position != std::string::npos) + { + mSpeech.erase(position, 2); + position = mSpeech.find('@'); + } + + start = mSpeech.find('[', start + 1); + end = mSpeech.find(']', start); + } + + if (!mSpeech.empty()) + mSpeechTime = time <= SPEECH_MAX_TIME ? time : SPEECH_MAX_TIME; } void Being::takeDamage(int amount) @@ -173,10 +237,6 @@ void Being::takeDamage(int amount) } else { - // Hit particle effect - controlParticle(particleEngine->addEffect( - "graphics/particles/hit.particle.xml", 0, 0)); - if (getType() == MONSTER) { font = hitBlueFont; @@ -190,6 +250,13 @@ void Being::takeDamage(int amount) // Show damage number particleEngine->addTextSplashEffect(damage, 255, 255, 255, font, mPx + 16, mPy + 16); + effectManager->trigger(26, this); +} + +void Being::showCrit() +{ + effectManager->trigger(28, this); + } void Being::handleAttack(Being *victim, int damage) @@ -225,9 +292,10 @@ 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: @@ -241,7 +309,8 @@ void Being::setAction(Uint8 action) { currentAction = mEquippedWeapon->getAttackType(); } - else { + else + { currentAction = ACTION_ATTACK; } for (int i = 0; i < VECTOREND_SPRITE; i++) @@ -278,16 +347,18 @@ void Being::setAction(Uint8 action) } } - void Being::setDirection(Uint8 direction) { + if (mDirection == direction) + return; + mDirection = direction; SpriteDirection dir = getSpriteDirection(); for (int i = 0; i < VECTOREND_SPRITE; i++) { - if (mSprites[i] != NULL) - mSprites[i]->setDirection(dir); + if (mSprites[i]) + mSprites[i]->setDirection(dir); } } @@ -307,8 +378,9 @@ SpriteDirection Being::getSpriteDirection() const { dir = DIRECTION_RIGHT; } - else { - dir = DIRECTION_LEFT; + else + { + dir = DIRECTION_LEFT; } return dir; @@ -352,28 +424,28 @@ void Being::nextStep() 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--; @@ -385,7 +457,7 @@ void Being::logic() // Update sprite animations for (int i = 0; i < VECTOREND_SPRITE; i++) { - if (mSprites[i] != NULL) + if (mSprites[i]) { mSprites[i]->update(tick_time * 10); } @@ -415,7 +487,7 @@ void Being::draw(Graphics *graphics, int offsetX, int offsetY) const for (int i = 0; i < VECTOREND_SPRITE; i++) { - if (mSprites[i] != NULL) + if (mSprites[i]) { mSprites[i]->draw(graphics, px, py); } @@ -431,8 +503,47 @@ void Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY) const int py = mPy + offsetY - 60; const int emotionIndex = mEmotion - 1; - if (emotionIndex >= 0 && emotionIndex < (int) emotionSet->size()) - graphics->drawImage(emotionSet->get(emotionIndex), px, py); + if (emotionIndex >= 0 && emotionIndex <= EmoteDB::getLast()) + emotionSet[emotionIndex]->draw(graphics, px, py); +} + +void Being::drawSpeech(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, mNameColor); + + // 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, gcn::Color(255, 255, 255)); + } + else if (mSpeechTime == 0) + { + mSpeechBubble->setVisible(false); + } } Being::Type Being::getType() const @@ -455,9 +566,11 @@ void Being::handleStatusEffect(StatusEffect *effect, int effectId) if (!effect) return; - SpriteAction action = effect->getAction(); - if (action != ACTION_INVALID) - setAction(action); + // TODO: Find out how this is meant to be used + // (SpriteAction != Being::Action) + //SpriteAction action = effect->getAction(); + //if (action != ACTION_INVALID) + // setAction(action); Particle *particle = effect->getParticle(); @@ -497,7 +610,8 @@ void Being::setStatusEffect(int index, bool active) 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; } @@ -505,27 +619,32 @@ int 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 { if (mSprites[BASE_SPRITE]) { - return mSprites[BASE_SPRITE]->getWidth(); + const int width = mSprites[BASE_SPRITE]->getWidth() > DEFAULT_WIDTH ? + mSprites[BASE_SPRITE]->getWidth() : + DEFAULT_WIDTH; + return width; } - else { - return 0; + else + { + return DEFAULT_WIDTH; } } @@ -534,10 +653,14 @@ int Being::getHeight() const { if (mSprites[BASE_SPRITE]) { - return mSprites[BASE_SPRITE]->getHeight(); + const int height = mSprites[BASE_SPRITE]->getHeight() > DEFAULT_HEIGHT ? + mSprites[BASE_SPRITE]->getHeight() : + DEFAULT_HEIGHT; + return height; } - else { - return 0; + else + { + return DEFAULT_HEIGHT; } } @@ -620,14 +743,14 @@ void Being::internalTriggerEffect(int effectId, bool sfx, bool gfx) return; } - if (gfx && ed->mGFXEffect != "") { + if (gfx && !ed->mGFXEffect.empty()) { Particle *selfFX; selfFX = particleEngine->addEffect(ed->mGFXEffect, 0, 0); controlParticle(selfFX); } - if (sfx && ed->mSFXEffect != "") { + if (sfx && !ed->mSFXEffect.empty()) { sound.playSfx(ed->mSFXEffect); } } @@ -695,7 +818,7 @@ static void initializeHair() int index = atoi(XML::getProperty(node, "id", "-1").c_str()); std::string value = XML::getProperty(node, "value", ""); - if (index >= 0 && value != "") { + if (index >= 0 && !value.empty()) { if (index >= hairColorsNr) { hairColorsNr = index + 1; hairColors.resize(hairColorsNr, "#000000"); @@ -706,7 +829,7 @@ static void initializeHair() } } // done initializing - if (hairColorsNr == 0) { // No colours -> black only + if (hairColorsNr == 0) { // No colors -> black only hairColorsNr = 1; hairColors.resize(hairColorsNr, "#000000"); } diff --git a/src/being.h b/src/being.h index 7d5ed989..3c3a9b73 100644 --- a/src/being.h +++ b/src/being.h @@ -22,34 +22,44 @@ #ifndef BEING_H #define BEING_H -#include <list> -#include <memory> -#include <string> +#include <guichan/color.hpp> + #include <SDL_types.h> + #include <set> +#include <string> +#include <vector> +#include "particlecontainer.h" #include "position.h" #include "sprite.h" -#include "map.h" -#include "animatedsprite.h" -#include "particlecontainer.h" + +#include "resources/spritedef.h" #define FIRST_IGNORE_EMOTE 14 #define STATUS_EFFECTS 32 +#define SPEECH_TIME 500 +#define SPEECH_MAX_TIME 1000 + class AnimatedSprite; -class Equipment; +class Image; class ItemInfo; class Item; class Map; class Graphics; -class ImageSet; class Particle; +class Position; +class SpeechBubble; class Text; class StatusEffect; -enum Gender { +typedef std::list<Sprite*> Sprites; +typedef Sprites::iterator SpriteIterator; + +enum Gender +{ GENDER_MALE = 0, GENDER_FEMALE = 1, GENDER_UNSPECIFIED = 2 @@ -58,7 +68,8 @@ enum Gender { class Being : public Sprite { public: - enum Type { + enum Type + { UNKNOWN, PLAYER, NPC, @@ -68,7 +79,8 @@ class Being : public Sprite /** * Action the being is currently performing. */ - enum Action { + enum Action + { STAND, WALK, ATTACK, @@ -77,7 +89,8 @@ class Being : public Sprite HURT }; - enum Sprite { + enum Sprite + { BASE_SPRITE = 0, SHOE_SPRITE, BOTTOMCLOTHES_SPRITE, @@ -93,7 +106,8 @@ class Being : public Sprite VECTOREND_SPRITE }; - enum TargetCursorSize { + enum TargetCursorSize + { TC_SMALL = 0, TC_MEDIUM, TC_LARGE, @@ -107,13 +121,13 @@ class Being : public Sprite 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. @@ -142,7 +156,7 @@ class Being : public Sprite * @param text The text that should appear. * @param time The amount of time the text should stay in milliseconds. */ - void setSpeech(const std::string &text, Uint32 time); + void setSpeech(const std::string &text, Uint32 time = 500); /** * Puts a damage bubble above this being. @@ -152,6 +166,11 @@ class Being : public Sprite 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. * * @param victim The attacked being. @@ -186,6 +205,11 @@ 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); @@ -211,11 +235,24 @@ class Being : public Sprite virtual void nextStep(); /** + * Triggers whether or not to show the name as a GM name. + * NOTE: This doesn't mean that just anyone can use this. + * If the server doesn't acknowlege you, you won't be shown + * as a GM on other people's clients. + */ + virtual void setGM() { mIsGM = true; } + + /** * Performs being logic. */ virtual void logic(); /** + * Draws the speech text above the being. + */ + void drawSpeech(int offsetX, int offsetY); + + /** * Draws the emotion picture above the being. */ void drawEmotion(Graphics *graphics, int offsetX, int offsetY); @@ -253,7 +290,7 @@ class Being : public Sprite /** * Sets the current action. */ - virtual void setAction(Uint8 action); + virtual void setAction(Action action); /** * Returns the current direction. @@ -266,6 +303,16 @@ 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) @@ -317,6 +364,8 @@ class Being : public Sprite */ void controlParticle(Particle *particle); + AnimatedSprite* getEmote(int index) { return emotionSet[index]; } + void setEmote(Uint8 emotion, Uint8 emote_time) { mEmotion = emotion; @@ -356,8 +405,8 @@ class Being : public Sprite internalTriggerEffect(effectId, false, true); } - const std::auto_ptr<Equipment> mEquipment; - + // Target cursor being used by the being + Image *mTargetCursor; static int getHairColorsNr(); @@ -365,6 +414,9 @@ class Being : public Sprite static std::string getHairColor(int index); + virtual AnimatedSprite* getSprite(int index) const + { return mSprites[index]; } + protected: /** * Sets the new path for this being. @@ -377,11 +429,6 @@ 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 @@ -416,12 +463,17 @@ 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 */ /** 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; Gender mGender; Uint32 mSpeechTime; @@ -429,6 +481,8 @@ class Being : public Sprite Uint16 mStunMode; /**< Stun mode; zero if not stunned */ std::set<int> mStatusEffects; /**< set of active status effects */ + gcn::Color mNameColor; + std::vector<AnimatedSprite*> mSprites; std::vector<int> mSpriteIDs; std::vector<std::string> mSpriteColors; @@ -443,9 +497,14 @@ class Being : public Sprite */ int getOffset(char pos, char neg) const; - static int instances; /**< Number of Being instances */ - static ImageSet *emotionSet; /**< Emoticons used by beings */ - bool mMustResetParticles; /**< Reset particle status effects on next redraw? */ + /** Reset particle status effects on next redraw? */ + bool mMustResetParticles; + + // Speech Bubble components + SpeechBubble *mSpeechBubble; + + static int instances; /**< Number of Being instances */ + static std::vector<AnimatedSprite*> emotionSet; /**< Emoticons used by beings */ }; #endif diff --git a/src/beingmanager.cpp b/src/beingmanager.cpp index 551820da..3c8edf86 100644 --- a/src/beingmanager.cpp +++ b/src/beingmanager.cpp @@ -31,6 +31,8 @@ #include "utils/dtor.h" +#include <cassert> + class FindBeingFunctor { public: @@ -128,12 +130,16 @@ Being* BeingManager::findBeingByPixel(Uint16 x, Uint16 y) for (; itr != itr_end; ++itr) { Being *being = (*itr); + + int xtol = being->getWidth(); + int uptol = being->getHeight() / 2; + if ((being->mAction != Being::DEAD) && (being != player_node) && (being->getPixelX() <= x) && - (being->getPixelX() + being->getWidth() >= x) && - (being->getPixelY() <= y) && - (being->getPixelY() + being->getHeight() >= y)) + (being->getPixelX() + xtol >= x) && + (being->getPixelY() - uptol <= y) && + (being->getPixelY() + uptol >= y)) { return being; } @@ -155,8 +161,6 @@ Being* BeingManager::findBeingByName(std::string name, Being::Type type) return NULL; } - - Beings& BeingManager::getAll() { return mBeings; @@ -204,9 +208,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 017de22e..11721709 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 009a4830..ec7d6a2c 100644 --- a/src/configlistener.h +++ b/src/configlistener.h @@ -22,8 +22,7 @@ #ifndef CONFIGLISTENER_H #define CONFIGLISTENER_H -#include <iosfwd> - +#include <string> /** * The listener interface for receiving notifications about changes to diff --git a/src/configuration.cpp b/src/configuration.cpp index 04839777..6815ea03 100644 --- a/src/configuration.cpp +++ b/src/configuration.cpp @@ -19,12 +19,8 @@ * 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" @@ -90,7 +86,6 @@ void ConfigurationObject::clear() mOptions.clear(); } - ConfigurationObject::~ConfigurationObject() { clear(); @@ -193,7 +188,6 @@ void ConfigurationObject::writeToXML(xmlTextWriterPtr writer) } } - void Configuration::write() { // Do not attempt to write to file that cannot be opened for writing diff --git a/src/configuration.h b/src/configuration.h index 9f97e221..4d28008d 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -22,11 +22,12 @@ #ifndef CONFIGURATION_H #define CONFIGURATION_H -#include <map> +#include <libxml/xmlwriter.h> + +#include <cassert> #include <list> +#include <map> #include <string> -#include <cassert> -#include <libxml/xmlwriter.h> class ConfigListener; class ConfigurationObject; diff --git a/src/effectmanager.cpp b/src/effectmanager.cpp new file mode 100644 index 00000000..f004a450 --- /dev/null +++ b/src/effectmanager.cpp @@ -0,0 +1,104 @@ +/* + * An effects manager + * Copyright (C) 2008 Fate <fate.tmw@googlemail.com> + * Copyright (C) 2008 Chuck Miller <shadowmil@gmail.com> + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "being.h" +#include "effectmanager.h" +#include "log.h" +#include "particle.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, Being* being) +{ + bool rValue = false; + for (std::list<EffectDescription>::iterator i = mEffects.begin(); i != mEffects.end(); ++i) + { + if ((*i).id == id) + { + rValue = true; + if (!(*i).GFX.empty()) + { + Particle *selfFX; + selfFX = particleEngine->addEffect((*i).GFX, 0, 0); + being->controlParticle(selfFX); + } + if (!(*i).SFX.empty()) + sound.playSfx((*i).SFX); + break; + } + } + return rValue; +} + +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) + { + rValue = true; + if (!(*i).GFX.empty()) + particleEngine->addEffect((*i).GFX, x, y); + if (!(*i).SFX.empty()) + sound.playSfx((*i).SFX); + break; + } + } + return rValue; +} diff --git a/src/effectmanager.h b/src/effectmanager.h new file mode 100644 index 00000000..01c5a6b8 --- /dev/null +++ b/src/effectmanager.h @@ -0,0 +1,62 @@ +/* + * An effects manager + * Copyright (C) 2008 Fate <fate.tmw@googlemail.com> + * Copyright (C) 2008 Chuck Miller <shadowmil@gmail.com> + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EFFECT_MANAGER_H +#define EFFECT_MANAGER_H + +#include <list> +#include <string> + +class Being; + +class EffectManager +{ + public: + struct EffectDescription + { + int id; + std::string GFX; + std::string SFX; + }; + + EffectManager(); + ~EffectManager(); + + /** + * Triggers a effect with the id, at + * the specified being. + */ + bool trigger(int id, Being* being); + + /** + * Triggers a effect with the id, at + * the specified x and y coordinate. + */ + bool trigger(int id, int x, int y); + + private: + std::list<EffectDescription> mEffects; +}; + +extern EffectManager *effectManager; + +#endif // EFFECT_MANAGER_H diff --git a/src/emoteshortcut.cpp b/src/emoteshortcut.cpp new file mode 100644 index 00000000..807fa3a2 --- /dev/null +++ b/src/emoteshortcut.cpp @@ -0,0 +1,77 @@ +/* + * Extended support for activating emotes + * Copyright (C) 2009 Aethyra Development Team + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emoteshortcut.h" + +#include "configuration.h" +#include "localplayer.h" + +#include "utils/tostring.h" + +EmoteShortcut::EmoteShortcut *emoteShortcut; + +EmoteShortcut::EmoteShortcut(): + mEmoteSelected(0) +{ + for (int i = 0; i < SHORTCUT_EMOTES; i++) + { + mEmotes[i] = i + 1; + } + load(); +} + +EmoteShortcut::~EmoteShortcut() +{ + save(); +} + +void EmoteShortcut::load() +{ + for (int i = 0; i < SHORTCUT_EMOTES; i++) + { + int emoteId = (int) config.getValue("emoteshortcut" + toString(i), i + 1); + + if (emoteId) + { + mEmotes[i] = emoteId; + } + } +} + +void EmoteShortcut::save() +{ + for (int i = 0; i < SHORTCUT_EMOTES; i++) + { + const int emoteId = mEmotes[i] ? mEmotes[i] : 0; + config.setValue("emoteshortcut" + toString(i), emoteId); + } +} + +void EmoteShortcut::useEmote(int index) +{ + if ((index > 0) && (index <= SHORTCUT_EMOTES)) + { + if (mEmotes[index - 1] > 0) + { + player_node->emote(mEmotes[index - 1]); + } + } +} diff --git a/src/emoteshortcut.h b/src/emoteshortcut.h new file mode 100644 index 00000000..ceb51a9b --- /dev/null +++ b/src/emoteshortcut.h @@ -0,0 +1,125 @@ +/* + * Extended support for activating emotes + * Copyright (C) 2009 Aethyra Development Team + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EMOTESHORTCUT_H +#define EMOTESHORTCUT_H + +#define SHORTCUT_EMOTES 12 + +/** + * The class which keeps track of the emote shortcuts. + */ +class EmoteShortcut +{ + public: + /** + * Constructor. + */ + EmoteShortcut(); + + /** + * Destructor. + */ + ~EmoteShortcut(); + + /** + * Load the configuration information. + */ + void load(); + + /** + * Returns the shortcut Emote ID specified by the index. + * + * @param index Index of the shortcut Emote. + */ + int getEmote(int index) const + { return mEmotes[index]; } + + /** + * Returns the amount of shortcut Emotes. + */ + int getEmoteCount() const + { return SHORTCUT_EMOTES; } + + /** + * Returns the emote ID that is currently selected. + */ + int getEmoteSelected() const + { return mEmoteSelected; } + + /** + * Adds the selected emote ID to the emotes specified by the index. + * + * @param index Index of the emotes. + */ + void setEmote(int index) + { mEmotes[index] = mEmoteSelected; } + + /** + * Adds a emoticon to the emotes store specified by the index. + * + * @param index Index of the emote. + * @param emoteId ID of the emote. + */ + void setEmotes(int index, int emoteId) + { mEmotes[index] = emoteId; } + + /** + * Set the Emote that is selected. + * + * @param emoteId The ID of the emote that is to be assigned. + */ + void setEmoteSelected(int emoteId) + { mEmoteSelected = emoteId; } + + /** + * A flag to check if the Emote is selected. + */ + bool isEmoteSelected() + { return mEmoteSelected; } + + /** + * Remove a Emote from the shortcut. + */ + void removeEmote(int index) + { mEmotes[index] = 0; } + + /** + * Try to use the Emote specified by the index. + * + * @param index Index of the emote shortcut. + */ + void useEmote(int index); + + private: + /** + * Save the configuration information. + */ + void save(); + + int mEmotes[SHORTCUT_EMOTES]; /**< The emote stored. */ + int mEmoteSelected; /**< The emote held by cursor. */ + +}; + +extern EmoteShortcut *emoteShortcut; + +#endif diff --git a/src/engine.cpp b/src/engine.cpp index 7a76e1b0..39b9b3ec 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -19,19 +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" #include "localplayer.h" #include "log.h" -#include "main.h" #include "map.h" #include "particle.h" #include "sound.h" @@ -44,14 +37,10 @@ #include "net/protocol.h" #include "resources/mapreader.h" -#include "resources/monsterdb.h" #include "resources/resourcemanager.h" -#include "utils/dtor.h" #include "utils/tostring.h" -extern Minimap *minimap; - char itemCurrenyQ[10] = "0"; Engine::Engine(Network *network): @@ -82,28 +71,40 @@ void Engine::changeMap(const std::string &mapPath) map_path = "maps/" + mapPath.substr(0, mapPath.rfind(".")) + ".tmx"; ResourceManager *resman = ResourceManager::getInstance(); if (!resman->exists(map_path)) - { map_path += ".gz"; - } // Attempt to load the new map Map *newMap = MapReader::readMap(map_path); - if (!newMap) { + if (!newMap) logger->error("Could not find map file"); - } // Notify the minimap and beingManager about the map change Image *mapImage = NULL; if (newMap->hasProperty("minimap")) { mapImage = resman->getImage(newMap->getProperty("minimap")); - } - if (newMap->hasProperty("name")) - { - minimap->setCaption(newMap->getProperty("name")); - } else { - minimap->setCaption("Map"); + + // Set the title for the Minimap + if (newMap->hasProperty("mapname")) + minimap->setCaption(newMap->getProperty("mapname")); + else if (newMap->hasProperty("name")) + minimap->setCaption(newMap->getProperty("name")); + 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); } minimap->setMapImage(mapImage); beingManager->setMap(newMap); @@ -116,16 +117,16 @@ void Engine::changeMap(const std::string &mapPath) // Start playing new music file when necessary std::string oldMusic = ""; - if (mCurrentMap) { + if (mCurrentMap) + { oldMusic = mCurrentMap->getProperty("music"); delete mCurrentMap; } std::string newMusic = newMap->getProperty("music"); - if (newMusic != oldMusic) { + if (newMusic != oldMusic) sound.playMusic(newMusic, -1); - } mCurrentMap = newMap; mMapName = mapPath; diff --git a/src/engine.h b/src/engine.h index f2852351..7ad6d894 100644 --- a/src/engine.h +++ b/src/engine.h @@ -22,7 +22,6 @@ #ifndef _ENGINE_H #define _ENGINE_H -#include <iosfwd> #include <string> class Map; @@ -52,7 +51,6 @@ class Engine const std::string &getCurrentMapName() { return mMapName; } - /** * Sets the currently active map. */ diff --git a/src/equipment.cpp b/src/equipment.cpp index b93beed4..cb7acd44 100644 --- a/src/equipment.cpp +++ b/src/equipment.cpp @@ -24,19 +24,16 @@ #include "inventory.h" #include "localplayer.h" -#include <algorithm> - Equipment::Equipment(): mArrows(0) { std::fill_n(mEquipment, EQUIPMENT_SIZE, 0); } -void -Equipment::setEquipment(int index, int inventoryIndex) +void Equipment::setEquipment(int index, int inventoryIndex) { mEquipment[index] = inventoryIndex; Item* item = player_node->getInventory()->getItem(inventoryIndex); if (item) - item->setEquipped(true); + item->setEquipped(true); } diff --git a/src/floor_item.cpp b/src/floor_item.cpp index e96b3652..fbe606b4 100644 --- a/src/floor_item.cpp +++ b/src/floor_item.cpp @@ -20,9 +20,12 @@ */ #include "floor_item.h" - +#include "graphics.h" +#include "item.h" #include "map.h" +#include "resources/image.h" + FloorItem::FloorItem(unsigned int id, unsigned int itemId, unsigned short x, @@ -47,3 +50,15 @@ FloorItem::~FloorItem() delete mItem; } + +unsigned int FloorItem::getItemId() const +{ + return mItem->getId(); +} + +void FloorItem::draw(Graphics *graphics, int offsetX, int offsetY) const +{ + graphics->drawImage(mItem->getImage(), + mX * 32 + offsetX, + mY * 32 + offsetY); +} diff --git a/src/floor_item.h b/src/floor_item.h index e0a67eca..444c756a 100644 --- a/src/floor_item.h +++ b/src/floor_item.h @@ -22,11 +22,16 @@ #ifndef FLOORITEM_H #define FLOORITEM_H -#include "graphics.h" -#include "item.h" -#include "map.h" +#include <list> + #include "sprite.h" -#include "resources/image.h" + +class Graphics; +class Image; +class Item; +class Map; + +typedef std::list<Sprite*> Sprites; /** * An item lying on the floor. @@ -51,47 +56,36 @@ class FloorItem : public Sprite /** * Returns instance id of this item. */ - unsigned int - getId() const { return mId; } + unsigned int getId() const { return mId; } /** * Returns the item id. */ - unsigned int - getItemId() const { return mItem->getId(); } + unsigned int getItemId() const; /** * Returns the x coordinate. */ - unsigned short - getX() const { return mX; } + unsigned short getX() const { return mX; } /** * Returns the y coordinate. */ - unsigned short - getY() const { return mY; } + unsigned short getY() const { return mY; } /** * Returns the pixel y coordinate. * * @see Sprite::getPixelY() */ - int - getPixelY() const { return mY * 32; } + int getPixelY() const { return mY * 32; } /** * Draws this floor item to the given graphics context. * * @see Sprite::draw(Graphics, int, int) */ - void - draw(Graphics *graphics, int offsetX, int offsetY) const - { - graphics->drawImage(mItem->getImage(), - mX * 32 + offsetX, - mY * 32 + offsetY); - } + void draw(Graphics *graphics, int offsetX, int offsetY) const; private: unsigned int mId; diff --git a/src/flooritemmanager.cpp b/src/flooritemmanager.cpp index 836bbe8d..65556abb 100644 --- a/src/flooritemmanager.cpp +++ b/src/flooritemmanager.cpp @@ -19,9 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "flooritemmanager.h" - #include "floor_item.h" +#include "flooritemmanager.h" #include "utils/dtor.h" diff --git a/src/game.cpp b/src/game.cpp index 2ba8f7d7..5b701fc1 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -19,8 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "game.h" - #include <fstream> #include <physfs.h> #include <sstream> @@ -30,8 +28,11 @@ #include "beingmanager.h" #include "configuration.h" +#include "effectmanager.h" +#include "emoteshortcut.h" #include "engine.h" #include "flooritemmanager.h" +#include "game.h" #include "graphics.h" #include "itemshortcut.h" #include "joystick.h" @@ -47,11 +48,14 @@ #include "gui/chat.h" #include "gui/confirm_dialog.h" #include "gui/debugwindow.h" +#include "gui/emoteshortcutcontainer.h" +#include "gui/emotewindow.h" #include "gui/equipmentwindow.h" #include "gui/gui.h" #include "gui/help.h" #include "gui/inventorywindow.h" -#include "gui/itemshortcutwindow.h" +#include "gui/shortcutwindow.h" +#include "gui/itemshortcutcontainer.h" #include "gui/menuwindow.h" #include "gui/minimap.h" #include "gui/ministatus.h" @@ -68,23 +72,23 @@ #include "gui/trade.h" #include "gui/viewport.h" -#include "net/protocol.h" #include "net/beinghandler.h" #include "net/buysellhandler.h" #include "net/chathandler.h" #include "net/equipmenthandler.h" #include "net/inventoryhandler.h" #include "net/itemhandler.h" +#include "net/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" #include "resources/imagewriter.h" -extern Graphics *graphics; +#include "utils/gettext.h" class Map; @@ -93,6 +97,7 @@ std::string map_path; bool done = false; volatile int tick_time; volatile int fps = 0, frame = 0; + Engine *engine = NULL; Joystick *joystick = NULL; @@ -109,6 +114,7 @@ BuyDialog *buyDialog; SellDialog *sellDialog; BuySellDialog *buySellDialog; InventoryWindow *inventoryWindow; +EmoteWindow *emoteWindow; NpcIntegerDialog *npcIntegerDialog; NpcListDialog *npcListDialog; NpcTextDialog *npcTextDialog; @@ -118,19 +124,20 @@ Setup* setupWindow; Minimap *minimap; EquipmentWindow *equipmentWindow; TradeWindow *tradeWindow; -//BuddyWindow *buddyWindow; HelpWindow *helpWindow; DebugWindow *debugWindow; -ItemShortcutWindow *itemShortcutWindow; +ShortcutWindow *itemShortcutWindow; +ShortcutWindow *emoteShortcutWindow; 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 +170,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; } } @@ -190,6 +200,7 @@ void createGuiWindows(Network *network) sellDialog = new SellDialog(network); buySellDialog = new BuySellDialog(); inventoryWindow = new InventoryWindow(); + emoteWindow = new EmoteWindow(); npcTextDialog = new NpcTextDialog(); npcIntegerDialog = new NpcIntegerDialog(); npcListDialog = new NpcListDialog(); @@ -197,22 +208,18 @@ void createGuiWindows(Network *network) skillDialog = new SkillDialog(); setupWindow = new Setup(); minimap = new Minimap(); - equipmentWindow = new EquipmentWindow(player_node->mEquipment.get()); + equipmentWindow = new EquipmentWindow(); tradeWindow = new TradeWindow(network); - //buddyWindow = new BuddyWindow(); helpWindow = new HelpWindow(); debugWindow = new DebugWindow(); - itemShortcutWindow = new ItemShortcutWindow(); - - // Initialize window positions - //buddyWindow->setPosition(10, minimap->getHeight() + 30); + itemShortcutWindow = new ShortcutWindow("ItemShortcut",new ItemShortcutContainer); + emoteShortcutWindow = new ShortcutWindow("emoteShortcut",new EmoteShortcutContainer); // Set initial window visibility 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); minimap->setVisible((bool) config.getValue( @@ -222,11 +229,10 @@ void createGuiWindows(Network *network) menuWindow->getWindowName() + "Visible", true)); itemShortcutWindow->setVisible((bool) config.getValue( itemShortcutWindow->getWindowName() + "Visible", true)); - - if (config.getValue("logToChat", 0)) - { - logger->setChatWindow(chatWindow); - } + emoteShortcutWindow->setVisible((bool) config.getValue( + emoteShortcutWindow->getWindowName() + "Visible", true)); + minimap->setVisible((bool) config.getValue( + minimap->getWindowName() + "Visible", true)); } /** @@ -243,6 +249,7 @@ void destroyGuiWindows() delete sellDialog; delete buySellDialog; delete inventoryWindow; + delete emoteWindow; delete npcIntegerDialog; delete npcListDialog; delete npcTextDialog; @@ -252,10 +259,10 @@ void destroyGuiWindows() delete minimap; delete equipmentWindow; delete tradeWindow; - //delete buddyWindow; delete helpWindow; delete debugWindow; delete itemShortcutWindow; + delete emoteShortcutWindow; } Game::Game(Network *network): @@ -276,6 +283,8 @@ Game::Game(Network *network): beingManager = new BeingManager(network); floorItemManager = new FloorItemManager(); + effectManager = new EffectManager(); + particleEngine = new Particle(NULL); particleEngine->setupEngine(); @@ -310,17 +319,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. - * - * 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. + * 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 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); @@ -379,12 +384,12 @@ static bool saveScreenshot() if (success) { std::stringstream chatlogentry; - chatlogentry << "Screenshot saved to ~/" << filenameSuffix.str(); + chatlogentry << _("Screenshot saved to ~/") << filenameSuffix.str(); chatWindow->chatLog(chatlogentry.str(), BY_SERVER); } else { - chatWindow->chatLog("Saving screenshot failed!", BY_SERVER); + chatWindow->chatLog(_("Saving screenshot failed!"), BY_SERVER); logger->log("Error: could not save screenshot."); } @@ -430,7 +435,7 @@ void Game::logic() // 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) + get_elapsed_time(mDrawTime / 10) > mMinFrameTime) { frame++; gui->draw(); @@ -460,9 +465,9 @@ void Game::logic() { if (!disconnectedDialog) { - disconnectedDialog = new OkDialog("Network Error", - "The connection to the server was lost, " - "the program will now quit"); + disconnectedDialog = new OkDialog(_("Network Error"), + _("The connection to the server was lost, " + "the program will now quit")); disconnectedDialog->addActionListener(&exitListener); disconnectedDialog->requestMoveToTop(); } @@ -487,114 +492,100 @@ void Game::handleInput() gcn::Window *requestedWindow = NULL; if (setupWindow->isVisible() && - keyboard.getNewKeyIndex() > keyboard.KEY_NO_VALUE) + keyboard.getNewKeyIndex() > keyboard.KEY_NO_VALUE) { keyboard.setNewKey((int) event.key.keysym.sym); keyboard.callbackNewKey(); keyboard.setNewKeyIndex(keyboard.KEY_NO_VALUE); return; } - // Keys pressed together with Alt/Meta - // Emotions and some internal gui windows - #ifndef __APPLE__ - if (event.key.keysym.mod & KMOD_LALT) - #else - if (event.key.keysym.mod & KMOD_LMETA) - #endif - { - switch (event.key.keysym.sym) - { - case SDLK_p: - // Screenshot (picture, hence the p) - saveScreenshot(); - used = true; - break; - - default: - break; - - case SDLK_f: - // Find path to mouse (debug purpose) - viewport->toggleDebugPath(); - used = true; - break; - - case SDLK_t: - // Toggle accepting of incoming trade requests - { - unsigned int deflt = player_relations.getDefault(); - if (deflt & PlayerRelation::TRADE) { - chatWindow->chatLog( - "Ignoring incoming trade requests", - BY_SERVER); - deflt &= ~PlayerRelation::TRADE; - } else { - chatWindow->chatLog( - "Accepting incoming trade requests", - BY_SERVER); - deflt |= PlayerRelation::TRADE; - } - - player_relations.setDefault(deflt); - } - used = true; - break; - } - } - // Smilie - if (keyboard.isKeyActive(keyboard.KEY_SMILIE)) + // Mode switch to emotes + if (keyboard.isKeyActive(keyboard.KEY_EMOTE)) { // 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; - } - + int emotion = keyboard.getKeyEmoteOffset(event.key.keysym.sym); if (emotion) { - player_node->emote(emotion); + emoteShortcut->useEmote(emotion); used = true; return; } } - switch (event.key.keysym.sym) + + if (keyboard.isKeyActive(keyboard.KEY_TOGGLE_CHAT) || + keyboard.isKeyActive(keyboard.KEY_OK)) + { + // Input chat window + if (!(chatWindow->isInputFocused() || + deathNotice || + weightNotice)) + { + // Quit by pressing Enter if the exit confirm is there + if (exitConfirm && + keyboard.isKeyActive(keyboard.KEY_TOGGLE_CHAT)) + done = true; + // Close the Browser if opened + else if (helpWindow->isVisible() && + keyboard.isKeyActive(keyboard.KEY_TOGGLE_CHAT)) + helpWindow->setVisible(false); + // Close the config window, cancelling changes if opened + else if (setupWindow->isVisible() && + keyboard.isKeyActive(keyboard.KEY_TOGGLE_CHAT)) + setupWindow->action(gcn::ActionEvent(NULL, "cancel")); + // Submits the text and proceeds to the next dialog + else if (npcStringDialog->isVisible() && + keyboard.isKeyActive(keyboard.KEY_OK)) + npcStringDialog->action(gcn::ActionEvent(NULL, "ok")); + // Proceed to the next dialog option, or close the window + else if (npcTextDialog->isVisible() && + keyboard.isKeyActive(keyboard.KEY_OK)) + npcTextDialog->action(gcn::ActionEvent(NULL, "ok")); + // Choose the currently highlighted dialogue option + else if (npcListDialog->isVisible() && + keyboard.isKeyActive(keyboard.KEY_OK)) + npcListDialog->action(gcn::ActionEvent(NULL, "ok")); + // Submits the text and proceeds to the next dialog + else if (npcIntegerDialog->isVisible() && + keyboard.isKeyActive(keyboard.KEY_OK)) + npcIntegerDialog->action(gcn::ActionEvent(NULL, "ok")); + else if (!(keyboard.getKeyValue( + KeyboardConfig::KEY_TOGGLE_CHAT) == + keyboard.getKeyValue( + KeyboardConfig::KEY_OK) && + (npcStringDialog->isVisible() || + npcTextDialog->isVisible() || + npcListDialog->isVisible() || + npcIntegerDialog->isVisible()))) + { + chatWindow->requestChatFocus(); + used = true; + } + } + } + + const int tKey = keyboard.getKeyIndex(event.key.keysym.sym); + switch (tKey) { - case SDLK_PAGEUP: + case KeyboardConfig::KEY_SCROLL_CHAT_UP: if (chatWindow->isVisible()) { chatWindow->scroll(-DEFAULT_CHAT_WINDOW_SCROLL); used = true; } break; - - case SDLK_PAGEDOWN: + case KeyboardConfig::KEY_SCROLL_CHAT_DOWN: if (chatWindow->isVisible()) { chatWindow->scroll(DEFAULT_CHAT_WINDOW_SCROLL); used = true; + return; } break; - - case SDLK_F1: + case KeyboardConfig::KEY_WINDOW_HELP: // In-game Help if (helpWindow->isVisible()) - { helpWindow->setVisible(false); - } else { helpWindow->loadHelp("index"); @@ -602,75 +593,48 @@ void Game::handleInput() } used = true; break; - - case SDLK_RETURN: - // Input chat window - if (chatWindow->isInputFocused() || - deathNotice != NULL || - weightNotice != NULL) + // Quitting confirmation dialog + case KeyboardConfig::KEY_QUIT: + if (!exitConfirm) { - 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 = new ConfirmDialog(_("Quit"), + _("Are you sure you " + "want to quit?")); exitConfirm->addActionListener(&exitListener); exitConfirm->requestMoveToTop(); } else { - exitConfirm->action(gcn::ActionEvent(NULL, "no")); + exitConfirm->action(gcn::ActionEvent(NULL, _("no"))); } break; - default: break; } if (keyboard.isEnabled() && !chatWindow->isInputFocused() - && !npcStringDialog->isInputFocused()) + && !npcStringDialog->isInputFocused()) { 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++) + for (int i = KeyboardConfig::KEY_SHORTCUT_1; + i <= KeyboardConfig::KEY_SHORTCUT_12; + i++) { - if (tKey == i && !used) { + if (tKey == i && !used) + { itemShortcut->useItem( - i - KeyboardConfig::KEY_SHORTCUT_0); + i - KeyboardConfig::KEY_SHORTCUT_1); break; } } } - switch (tKey) { + + switch (tKey) + { case KeyboardConfig::KEY_PICKUP: { FloorItem *item = @@ -679,7 +643,8 @@ void Game::handleInput() // If none below the player, try the tile in front // of the player - if (!item) { + if (!item) + { Uint16 x = player_node->mX; Uint16 y = player_node->mY; if (player_node->getDirection() & Being::UP) @@ -707,11 +672,12 @@ void Game::handleInput() used = true; break; case KeyboardConfig::KEY_HIDE_WINDOWS: - // Hide certain windows + // Hide certain windows if (!chatWindow->isInputFocused()) { statusWindow->setVisible(false); inventoryWindow->setVisible(false); + emoteWindow->setVisible(false); skillDialog->setVisible(false); setupWindow->setVisible(false); equipmentWindow->setVisible(false); @@ -719,7 +685,6 @@ void Game::handleInput() debugWindow->setVisible(false); } break; - case KeyboardConfig::KEY_WINDOW_STATUS: requestedWindow = statusWindow; break; @@ -733,6 +698,7 @@ void Game::handleInput() requestedWindow = skillDialog; break; case KeyboardConfig::KEY_WINDOW_MINIMAP: + minimap->toggle(); requestedWindow = minimap; break; case KeyboardConfig::KEY_WINDOW_CHAT: @@ -747,6 +713,44 @@ void Game::handleInput() case KeyboardConfig::KEY_WINDOW_DEBUG: requestedWindow = debugWindow; break; + case KeyboardConfig::KEY_WINDOW_EMOTE: + requestedWindow = emoteWindow; + break; + case KeyboardConfig::KEY_WINDOW_EMOTE_SHORTCUT: + requestedWindow = emoteShortcutWindow; + break; + case KeyboardConfig::KEY_SCREENSHOT: + // Screenshot (picture, hence the p) + saveScreenshot(); + used = true; + break; + case KeyboardConfig::KEY_PATHFIND: + // Find path to mouse (debug purpose) + viewport->toggleDebugPath(); + used = true; + break; + case KeyboardConfig::KEY_TRADE: + // Toggle accepting of incoming trade requests + unsigned int deflt = player_relations.getDefault(); + if (deflt & PlayerRelation::TRADE) + { + chatWindow->chatLog( + _("Ignoring incoming trade requests"), + BY_SERVER); + deflt &= ~PlayerRelation::TRADE; + } + else + { + chatWindow->chatLog( + _("Accepting incoming trade requests"), + BY_SERVER); + deflt |= PlayerRelation::TRADE; + } + + player_relations.setDefault(deflt); + + used = true; + break; } } @@ -754,14 +758,10 @@ void Game::handleInput() { requestedWindow->setVisible(!requestedWindow->isVisible()); if (requestedWindow->isVisible()) - { requestedWindow->requestMoveToTop(); - } used = true; } - } - // Quit event else if (event.type == SDL_QUIT) { @@ -781,7 +781,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. @@ -790,8 +789,7 @@ void Game::handleInput() // Moving player around if (player_node->mAction != Being::DEAD && - current_npc == 0 && - !chatWindow->isInputFocused()) + current_npc == 0 && !chatWindow->isInputFocused()) { // Get the state of the keyboard keys keyboard.refreshActiveKeys(); @@ -802,23 +800,23 @@ void Game::handleInput() // Translate pressed keys to movement and direction if (keyboard.isKeyActive(keyboard.KEY_MOVE_UP) || - (joystick && joystick->isUp())) + (joystick && joystick->isUp())) { direction |= Being::UP; } else if (keyboard.isKeyActive(keyboard.KEY_MOVE_DOWN) || - (joystick && joystick->isDown())) + (joystick && joystick->isDown())) { direction |= Being::DOWN; } if (keyboard.isKeyActive(keyboard.KEY_MOVE_LEFT) || - (joystick && joystick->isLeft())) + (joystick && joystick->isLeft())) { direction |= Being::LEFT; } else if (keyboard.isKeyActive(keyboard.KEY_MOVE_RIGHT) || - (joystick && joystick->isRight())) + (joystick && joystick->isRight())) { direction |= Being::RIGHT; } @@ -827,60 +825,92 @@ 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 (keyboard.isKeyActive(keyboard.KEY_TARGET_CLOSEST) - || (joystick && joystick->buttonPressed(3))) + // 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); - if (target) + player_node->setTarget(target); + } + + // 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::NPC); + + 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)) @@ -19,17 +19,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef GAME_ -#define GAME_ +#ifndef GAME_H +#define GAME_H -#include <iosfwd> #include <memory> #include "configlistener.h" -#define SPEECH_TIME 80 -#define SPEECH_MAX_TIME 100 - class MessageHandler; class Network; diff --git a/src/graphics.cpp b/src/graphics.cpp index bbe4221e..4af7b723 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -42,6 +42,8 @@ bool Graphics::setVideoMode(int w, int h, int bpp, bool fs, bool hwaccel) logger->log("Setting video mode %dx%d %s", w, h, fs ? "fullscreen" : "windowed"); + logger->log("Bits per pixel: %d", bpp); + int displayFlags = SDL_ANYFORMAT; mFullscreen = fs; @@ -69,7 +71,7 @@ bool Graphics::setVideoMode(int w, int h, int bpp, bool fs, bool hwaccel) logger->log("Using video driver: %s", videoDriverName); } else { - logger->log("Using video driver: unkown"); + logger->log("Using video driver: unknown"); } const SDL_VideoInfo *vi = SDL_GetVideoInfo(); @@ -176,13 +178,12 @@ void Graphics::drawImagePattern(Image *image, int x, int y, int w, int h) } } -void -Graphics::drawImageRect(int x, int y, int w, int h, - Image *topLeft, Image *topRight, - Image *bottomLeft, Image *bottomRight, - Image *top, Image *right, - Image *bottom, Image *left, - Image *center) +void Graphics::drawImageRect(int x, int y, int w, int h, + Image *topLeft, Image *topRight, + Image *bottomLeft, Image *bottomRight, + Image *top, Image *right, + Image *bottom, Image *left, + Image *center) { pushClipArea(gcn::Rectangle(x, y, w, h)); @@ -220,9 +221,8 @@ Graphics::drawImageRect(int x, int y, int w, int h, popClipArea(); } -void -Graphics::drawImageRect(int x, int y, int w, int h, - const ImageRect &imgRect) +void Graphics::drawImageRect(int x, int y, int w, int h, + const ImageRect &imgRect) { drawImageRect(x, y, w, h, imgRect.grid[0], imgRect.grid[2], imgRect.grid[6], imgRect.grid[8], diff --git a/src/graphics.h b/src/graphics.h index 4a695a7a..172032dc 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -46,14 +46,16 @@ struct SDL_Surface; * Sections 0, 2, 6 and 8 will remain as is. 1, 3, 4, 5 and 7 will be * repeated to fit the size of the widget. */ -struct ImageRect { +struct ImageRect +{ Image *grid[9]; }; /** * A central point of control for graphics. */ -class Graphics : public gcn::SDLGraphics { +class Graphics : public gcn::SDLGraphics +{ public: /** * Constructor. @@ -95,17 +97,15 @@ class Graphics : public gcn::SDLGraphics { * @return <code>true</code> if the image was blitted properly * <code>false</code> otherwise. */ - virtual bool - drawImage(Image *image, - int srcX, int srcY, - int dstX, int dstY, - int width, int height, - bool useColor = false); - - virtual void - drawImagePattern(Image *image, - int x, int y, - int w, int h); + virtual bool drawImage(Image *image, + int srcX, int srcY, + int dstX, int dstY, + int width, int height, + bool useColor = false); + + virtual void drawImagePattern(Image *image, + int x, int y, + int w, int h); /** * Draws a rectangle using images. 4 corner images, 4 side images and 1 @@ -153,4 +153,6 @@ class Graphics : public gcn::SDLGraphics { bool mFullscreen, mHWAccel; }; +extern Graphics *graphics; + #endif diff --git a/src/gui/browserbox.cpp b/src/gui/browserbox.cpp index 2d805b9c..a06b9a6e 100644 --- a/src/gui/browserbox.cpp +++ b/src/gui/browserbox.cpp @@ -21,15 +21,17 @@ #include <algorithm> -#include "browserbox.h" +#include <guichan/graphics.hpp> +#include "browserbox.h" +#include "color.h" #include "linkhandler.h" #include "truetypefont.h" -BrowserBox::BrowserBox(unsigned int mode): +BrowserBox::BrowserBox(unsigned int mode, bool opaque): gcn::Widget(), mMode(mode), mHighMode(UNDERLINE | BACKGROUND), - mOpaque(true), + mOpaque(opaque), mUseLinksAndUserColors(true), mSelectedLink(-1), mMaxRows(0) @@ -98,12 +100,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 != "") + if (!tmp.empty()) { - newRow += "##P"; + newRow += "##>"; } idx1 = tmp.find("@@"); } @@ -122,7 +124,18 @@ void BrowserBox::addRow(const std::string &row) //discard older rows when a row limit has been set if (mMaxRows > 0) { - while (mTextRows.size() > mMaxRows) mTextRows.pop_front(); + while (mTextRows.size() > mMaxRows) + { + mTextRows.pop_front(); + for (unsigned int i = 0; i < mLinks.size(); i++) + { + mLinks[i].y1 -= font->getHeight(); + mLinks[i].y2 -= font->getHeight(); + + if (mLinks[i].y1 < 0) + mLinks.erase(mLinks.begin() + i); + } + } } // Auto size mode @@ -210,8 +223,7 @@ struct MouseOverLink int mX, mY; }; -void -BrowserBox::mousePressed(gcn::MouseEvent &event) +void BrowserBox::mousePressed(gcn::MouseEvent &event) { LinkIterator i = find_if(mLinks.begin(), mLinks.end(), MouseOverLink(event.getX(), event.getY())); @@ -221,8 +233,7 @@ BrowserBox::mousePressed(gcn::MouseEvent &event) } } -void -BrowserBox::mouseMoved(gcn::MouseEvent &event) +void BrowserBox::mouseMoved(gcn::MouseEvent &event) { LinkIterator i = find_if(mLinks.begin(), mLinks.end(), MouseOverLink(event.getX(), event.getY())); @@ -230,8 +241,7 @@ BrowserBox::mouseMoved(gcn::MouseEvent &event) mSelectedLink = (i != mLinks.end()) ? (i - mLinks.begin()) : -1; } -void -BrowserBox::draw(gcn::Graphics *graphics) +void BrowserBox::draw(gcn::Graphics *graphics) { if (mOpaque) { @@ -241,9 +251,10 @@ BrowserBox::draw(gcn::Graphics *graphics) if (mSelectedLink >= 0) { + bool valid; if ((mHighMode & BACKGROUND)) { - graphics->setColor(gcn::Color(HIGHLIGHT)); + graphics->setColor(gcn::Color(textColor->getColor('H', valid))); graphics->fillRectangle(gcn::Rectangle( mLinks[mSelectedLink].x1, mLinks[mSelectedLink].y1, @@ -254,7 +265,7 @@ BrowserBox::draw(gcn::Graphics *graphics) if ((mHighMode & UNDERLINE)) { - graphics->setColor(gcn::Color(LINK)); + graphics->setColor(gcn::Color(textColor->getColor('<', valid))); graphics->drawLine( mLinks[mSelectedLink].x1, mLinks[mSelectedLink].y2, @@ -265,6 +276,7 @@ BrowserBox::draw(gcn::Graphics *graphics) int x = 0, y = 0; int wrappedLines = 0; + int link = 0; TrueTypeFont *font = static_cast<TrueTypeFont*>(getFont()); graphics->setColor(BLACK); @@ -311,57 +323,36 @@ BrowserBox::draw(gcn::Graphics *graphics) // Check for color change in format "##x", x = [L,P,0..9] if (row.find("##", start) == start && row.size() > start + 2) { - switch (row.at(start + 2)) + char c = row.at(start + 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 = textColor->getColor(c, valid); + if (c == '<') + { + const int size = mLinks[link].x2 - mLinks[link].x1; + mLinks[link].x1 = x; + mLinks[link].y1 = y; + mLinks[link].x2 = mLinks[link].x1 + size; + mLinks[link].y2 = y + font->getHeight(); + link++; prevColor = selColor; - selColor = BLACK; + } + if (valid) + { + selColor = rgb; + } } start += 3; + + if (start == row.size()) + { + break; + } } graphics->setColor(gcn::Color(selColor)); } @@ -424,3 +415,4 @@ BrowserBox::draw(gcn::Graphics *graphics) setHeight((mTextRows.size() + wrappedLines) * font->getHeight()); } } + diff --git a/src/gui/browserbox.h b/src/gui/browserbox.h index 4dc41e3d..5dde402e 100644 --- a/src/gui/browserbox.h +++ b/src/gui/browserbox.h @@ -22,15 +22,12 @@ #ifndef BROWSERBOX_H #define BROWSERBOX_H -#include <iosfwd> +#include <list> #include <vector> #include <guichan/mouselistener.hpp> #include <guichan/widget.hpp> -#include "../guichanfwd.h" -#include "../main.h" - class LinkHandler; struct BROWSER_LINK { @@ -49,7 +46,7 @@ class BrowserBox : public gcn::Widget, public gcn::MouseListener /** * Constructor. */ - BrowserBox(unsigned int mode = AUTO_SIZE); + BrowserBox(unsigned int mode = AUTO_SIZE, bool opaque = true); /** * Destructor. @@ -165,3 +162,4 @@ class BrowserBox : public gcn::Widget, public gcn::MouseListener }; #endif + diff --git a/src/gui/buddywindow.cpp b/src/gui/buddywindow.cpp deleted file mode 100644 index 0927ddf8..00000000 --- a/src/gui/buddywindow.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * The Mana World - * Copyright (C) 2004 The Mana World Development Team - * - * This file is part of The Mana World. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "buddywindow.h" - -#include <guichan/widgets/listbox.hpp> - -#include "button.h" -#include "chat.h" -#include "scrollarea.h" - -#include "../resources/buddylist.h" - -extern ChatWindow *chatWindow; - -BuddyWindow::BuddyWindow(): - Window("Buddy") -{ - setContentSize(124, 202); - - mBuddyList = new BuddyList(); - - mListbox = new gcn::ListBox(); - mListbox->setListModel(mBuddyList); - - ScrollArea *scrollArea = new ScrollArea(mListbox); - scrollArea->setDimension(gcn::Rectangle( - 7, 5, 110, 170)); - add(scrollArea); - - Button *talk = new Button("Talk", "Talk", this); - Button *remove = new Button("Remove", "Remove", this); - Button *cancel = new Button("Cancel", "Cancel", this); - - talk->setPosition(2,180); - remove->setPosition(talk->getWidth()+2,180); - cancel->setPosition(talk->getWidth()+remove->getWidth()+2,180); - - add(talk); - add(remove); - add(cancel); -} - -void BuddyWindow::action(const gcn::ActionEvent &event) -{ - if (event.getId() == "Talk") { - int selected = mListbox->getSelected(); - if ( selected > -1 ) - { - std::string who = mBuddyList->getElementAt(selected); - chatWindow->setInputText(who +": "); - } - } - else if (event.getId() == "Remove") { - int selected = mListbox->getSelected(); - if ( selected > -1 ) - { - std::string who = mBuddyList->getElementAt(selected); - mBuddyList->removeBuddy(who); - } - } - else if (event.getId() == "Cancel") { - setVisible(false); - } -} diff --git a/src/gui/button.cpp b/src/gui/button.cpp index caf93b94..1d3a04e4 100644 --- a/src/gui/button.cpp +++ b/src/gui/button.cpp @@ -19,8 +19,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <guichan/exception.hpp> +#include <guichan/font.hpp> + #include "button.h" +#include "../configuration.h" #include "../graphics.h" #include "../resources/image.h" @@ -28,13 +32,8 @@ #include "../utils/dtor.h" -#include <guichan/exception.hpp> -#include <guichan/graphics.hpp> -#include <guichan/font.hpp> - -#include <algorithm> - int Button::mInstances = 0; +float Button::mAlpha = config.getValue("guialpha", 0.8); enum{ BUTTON_STANDARD, // 0 @@ -100,6 +99,7 @@ void Button::init() data[x].gridX, data[y].gridY, data[x + 1].gridX - data[x].gridX + 1, data[y + 1].gridY - data[y].gridY + 1); + button[mode].grid[a]->setAlpha(mAlpha); a++; } } @@ -122,22 +122,29 @@ Button::~Button() } } -void -Button::draw(gcn::Graphics *graphics) +void Button::draw(gcn::Graphics *graphics) { int mode; - if (!isEnabled()) { + if (!isEnabled()) mode = BUTTON_DISABLED; - } - else if (isPressed() || mIsLogged) { + else if (isPressed() || mIsLogged) mode = BUTTON_PRESSED; - } - else if (mHasMouse || isFocused()) { + else if (mHasMouse || isFocused()) mode = BUTTON_HIGHLIGHTED; - } - else { + else mode = BUTTON_STANDARD; + + if (config.getValue("guialpha", 0.8) != mAlpha) + { + mAlpha = config.getValue("guialpha", 0.8); + for (int a = 0; a < 9; a++) + { + button[BUTTON_DISABLED].grid[a]->setAlpha(mAlpha); + button[BUTTON_PRESSED].grid[a]->setAlpha(mAlpha); + button[BUTTON_HIGHLIGHTED].grid[a]->setAlpha(mAlpha); + button[BUTTON_STANDARD].grid[a]->setAlpha(mAlpha); + } } static_cast<Graphics*>(graphics)-> @@ -148,7 +155,8 @@ Button::draw(gcn::Graphics *graphics) int textX; int textY = getHeight() / 2 - getFont()->getHeight() / 2; - switch (getAlignment()) { + switch (getAlignment()) + { case gcn::Graphics::LEFT: textX = 4; break; @@ -164,10 +172,8 @@ Button::draw(gcn::Graphics *graphics) graphics->setFont(getFont()); - if (isPressed()) { + if (isPressed()) graphics->drawText(getCaption(), textX + 1, textY + 1, getAlignment()); - } - else { + else graphics->drawText(getCaption(), textX, textY, getAlignment()); - } } diff --git a/src/gui/button.h b/src/gui/button.h index 0ec99245..abaf5c43 100644 --- a/src/gui/button.h +++ b/src/gui/button.h @@ -22,8 +22,6 @@ #ifndef BUTTON_H #define BUTTON_H -#include <iosfwd> - #include <guichan/widgets/button.hpp> class ImageRect; @@ -69,6 +67,7 @@ class Button : public gcn::Button static ImageRect button[4]; /**< Button state graphics */ static int mInstances; /**< Number of button instances */ + static float mAlpha; bool mIsLogged; /**< Makes the button appear pressed all the time */ }; diff --git a/src/gui/buy.cpp b/src/gui/buy.cpp index 49dd52af..0c8c4d9d 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" @@ -43,7 +42,7 @@ BuyDialog::BuyDialog(Network *network): Window(_("Buy")), mNetwork(network), mMoney(0), mAmountItems(0), mMaxItems(0) { - setWindowName("Buy"); + setWindowName(_("Buy")); setResizable(true); setMinWidth(260); setMinHeight(230); diff --git a/src/gui/buy.h b/src/gui/buy.h index 37a39890..0f1cfede 100644 --- a/src/gui/buy.h +++ b/src/gui/buy.h @@ -25,9 +25,9 @@ #include <guichan/actionlistener.hpp> #include <guichan/selectionlistener.hpp> -#include "window.h" +#include <SDL_types.h> -#include "../guichanfwd.h" +#include "window.h" class Network; class ShopItems; @@ -40,7 +40,7 @@ class ListBox; * \ingroup Interface */ class BuyDialog : public Window, public gcn::ActionListener, - gcn::SelectionListener + public gcn::SelectionListener { public: /** @@ -111,9 +111,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 cdab0855..d060db85 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/char_select.cpp b/src/gui/char_select.cpp index df57f969..8de4f5a7 100644 --- a/src/gui/char_select.cpp +++ b/src/gui/char_select.cpp @@ -19,13 +19,14 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "char_select.h" - #include <string> +#include <guichan/font.hpp> + #include <guichan/widgets/label.hpp> #include "button.h" +#include "char_select.h" #include "confirm_dialog.h" #include "ok_dialog.h" #include "playerbox.h" @@ -40,6 +41,8 @@ #include "../net/charserverhandler.h" #include "../net/messageout.h" +#include "../resources/colordb.h" + #include "../utils/gettext.h" #include "../utils/strprintf.h" #include "../utils/trim.h" @@ -69,7 +72,8 @@ CharDeleteConfirm::CharDeleteConfirm(CharSelectDialog *m): void CharDeleteConfirm::action(const gcn::ActionEvent &event) { //ConfirmDialog::action(event); - if (event.getId() == "yes") { + if (event.getId() == "yes") + { master->attemptCharDelete(); } ConfirmDialog::action(event); @@ -81,38 +85,42 @@ CharSelectDialog::CharSelectDialog(Network *network, Window(_("Select Character")), mNetwork(network), mCharInfo(charInfo), mGender(gender), mCharSelected(false) { - mSelectButton = new Button(_("Ok"), "ok", this); - mCancelButton = new Button(_("Cancel"), "cancel", this); - mNewCharButton = new Button(_("New"), "new", this); - mDelCharButton = new Button(_("Delete"), "delete", this); - mPreviousButton = new Button(_("Previous"), "previous", this); - mNextButton = new Button(_("Next"), "next", this); + // Control that shows the Player + mPlayerBox = new PlayerBox; + mPlayerBox->setWidth(74); mNameLabel = new gcn::Label(strprintf(_("Name: %s"), "")); mLevelLabel = new gcn::Label(strprintf(_("Level: %d"), 0)); mJobLevelLabel = new gcn::Label(strprintf(_("Job Level: %d"), 0)); mMoneyLabel = new gcn::Label(strprintf(_("Money: %d"), 0)); - // Control that shows the Player - mPlayerBox = new PlayerBox; - mPlayerBox->setWidth(74); + const std::string tempString = getFont()->getWidth(_("New")) < + getFont()->getWidth(_("Delete")) ? + _("Delete") : _("New"); + + mPreviousButton = new Button(_("Previous"), "previous", this); + mNextButton = new Button(_("Next"), "next", this); + mNewDelCharButton = new Button(tempString, "newdel", this); + mSelectButton = new Button(_("Ok"), "ok", this); + mCancelButton = new Button(_("Cancel"), "cancel", this); ContainerPlacer place; place = getPlacer(0, 0); + place(0, 0, mPlayerBox, 1, 6).setPadding(3); - place(1, 0, mNameLabel, 3); - place(1, 1, mLevelLabel, 3); - place(1, 2, mJobLevelLabel, 3); - place(1, 3, mMoneyLabel, 3); - place(1, 4, mPreviousButton); - place(2, 4, mNextButton); - place(1, 5, mNewCharButton); - place(2, 5, mDelCharButton); - place.getCell().matchColWidth(1, 2); + place(1, 0, mNewDelCharButton); + place(1, 1, mNameLabel, 5); + place(1, 2, mLevelLabel, 5); + place(1, 3, mJobLevelLabel, 5); + place(1, 4, mMoneyLabel, 5); + place.getCell().matchColWidth(1, 4); place = getPlacer(0, 2); - place(0, 0, mSelectButton); - place(1, 0, mCancelButton); - reflowLayout(265, 0); + place(0, 0, mPreviousButton); + place(1, 0, mNextButton); + place(4, 0, mCancelButton); + place(5, 0, mSelectButton); + + reflowLayout(250, 0); setLocationRelativeTo(getParent()); setVisible(true); @@ -125,8 +133,7 @@ void CharSelectDialog::action(const gcn::ActionEvent &event) if (event.getId() == "ok" && n_character > 0) { // Start game - mNewCharButton->setEnabled(false); - mDelCharButton->setEnabled(false); + mNewDelCharButton->setEnabled(false); mSelectButton->setEnabled(false); mPreviousButton->setEnabled(false); mNextButton->setEnabled(false); @@ -137,20 +144,21 @@ void CharSelectDialog::action(const gcn::ActionEvent &event) { state = EXIT_STATE; } - else if (event.getId() == "new" && n_character <= MAX_SLOT) + else if (event.getId() == "newdel") { - // Start new character dialog - CharCreateDialog *charCreateDialog = - new CharCreateDialog(this, mCharInfo->getPos(), mNetwork, mGender); - charServerHandler.setCharCreateDialog(charCreateDialog); - } - else if (event.getId() == "delete") - { - // Delete character - if (mCharInfo->getEntry()) + // Check for a character + if (mCharInfo->getEntry() && n_character <= MAX_SLOT ) { new CharDeleteConfirm(this); } + else + { + // Start new character dialog + CharCreateDialog *charCreateDialog = + new CharCreateDialog(this, mCharInfo->getPos(), + mNetwork, mGender); + charServerHandler.setCharCreateDialog(charCreateDialog); + } } else if (event.getId() == "previous") { @@ -174,18 +182,17 @@ void CharSelectDialog::updatePlayerInfo() mMoneyLabel->setCaption(strprintf(_("Gold: %d"), pi->mGp)); if (!mCharSelected) { - mNewCharButton->setEnabled(false); - mDelCharButton->setEnabled(true); + mNewDelCharButton->setCaption(_("Delete")); mSelectButton->setEnabled(true); } } - else { + else + { mNameLabel->setCaption(strprintf(_("Name: %s"), "")); mLevelLabel->setCaption(strprintf(_("Level: %d"), 0)); mJobLevelLabel->setCaption(strprintf(_("Job Level: %d"), 0)); mMoneyLabel->setCaption(strprintf(_("Money: %d"), 0)); - mNewCharButton->setEnabled(true); - mDelCharButton->setEnabled(false); + mNewDelCharButton->setCaption(_("New")); mSelectButton->setEnabled(false); } @@ -224,7 +231,8 @@ bool CharSelectDialog::selectByName(const std::string &name) unsigned int oldPos = mCharInfo->getPos(); mCharInfo->select(0); - do { + do + { LocalPlayer *player = mCharInfo->getEntry(); if (player && player->getName() == name) @@ -244,7 +252,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:")); @@ -258,41 +269,29 @@ CharCreateDialog::CharCreateDialog(Window *parent, int slot, Network *network, mCancelButton = new Button(_("Cancel"), "cancel", this); mPlayerBox = new PlayerBox(mPlayer); + mPlayerBox->setWidth(74); + mNameField->setActionEventId("create"); + mNameField->addActionListener(this); - int w = 200; - int h = 150; - setContentSize(w, h); - mPlayerBox->setDimension(gcn::Rectangle(80, 30, 110, 85)); - mNameLabel->setPosition(5, 5); - mNameField->setDimension( - gcn::Rectangle(45, 5, w - 45 - 7, mNameField->getHeight())); - mPrevHairColorButton->setPosition(90, 35); - mNextHairColorButton->setPosition(165, 35); - mHairColorLabel->setPosition(5, 40); - mPrevHairStyleButton->setPosition(90, 64); - mNextHairStyleButton->setPosition(165, 64); - mHairStyleLabel->setPosition(5, 70); - mCancelButton->setPosition( - w - 5 - mCancelButton->getWidth(), - h - 5 - mCancelButton->getHeight()); - mCreateButton->setPosition( - mCancelButton->getX() - 5 - mCreateButton->getWidth(), - h - 5 - mCancelButton->getHeight()); + ContainerPlacer place; + place = getPlacer(0, 0); - mNameField->addActionListener(this); + place(0, 0, mNameLabel, 1); + place(1, 0, mNameField, 6); + place(0, 1, mHairStyleLabel, 1); + place(1, 1, mPrevHairStyleButton); + place(2, 1, mPlayerBox, 1, 8).setPadding(3); + place(3, 1, mNextHairStyleButton); + place(0, 2, mHairColorLabel, 1); + place(1, 2, mPrevHairColorButton); + place(3, 2, mNextHairColorButton); + place.getCell().matchColWidth(0, 2); + place = getPlacer(0, 2); + place(4, 0, mCancelButton); + place(5, 0, mCreateButton); - add(mPlayerBox); - add(mNameField); - add(mNameLabel); - add(mNextHairColorButton); - add(mPrevHairColorButton); - add(mHairColorLabel); - add(mNextHairStyleButton); - add(mPrevHairStyleButton); - add(mHairStyleLabel); - add(mCreateButton); - add(mCancelButton); + reflowLayout(225, 0); setLocationRelativeTo(getParent()); setVisible(true); @@ -307,53 +306,54 @@ CharCreateDialog::~CharCreateDialog() charServerHandler.setCharCreateDialog(0); } -void -CharCreateDialog::action(const gcn::ActionEvent &event) +void CharCreateDialog::action(const gcn::ActionEvent &event) { - if (event.getId() == "create") { - if (getName().length() >= 4) { + int numberOfColors = ColorDB::size(); + if (event.getId() == "create") + { + if (getName().length() >= 4) + { // Attempt to create the character mCreateButton->setEnabled(false); attemptCharCreate(); } - else { + else + { new OkDialog("Error", "Your name needs to be at least 4 characters.", this); } } - else if (event.getId() == "cancel") { + else if (event.getId() == "cancel") scheduleDelete(); - } - else if (event.getId() == "nextcolor") { - mPlayer->setHairStyle(-1, mPlayer->getHairColor() + 1); - } - else if (event.getId() == "prevcolor") { - mPlayer->setHairStyle(-1, mPlayer->getHairColor() + Being::getHairColorsNr() - 1); - } - else if (event.getId() == "nextstyle") { - mPlayer->setHairStyle(mPlayer->getHairStyle() + 1, -1); - } - else if (event.getId() == "prevstyle") { - mPlayer->setHairStyle(mPlayer->getHairStyle() + Being::getHairStylesNr() - 1, -1); - } + else if (event.getId() == "nextcolor") + mPlayer->setHairStyle(mPlayer->getHairStyle(), + (mPlayer->getHairColor() + 1) % numberOfColors); + else if (event.getId() == "prevcolor") + mPlayer->setHairStyle(mPlayer->getHairStyle(), + (mPlayer->getHairColor() + numberOfColors - 1) % + numberOfColors); + else if (event.getId() == "nextstyle") + mPlayer->setHairStyle(mPlayer->getHairStyle() + 1, + mPlayer->getHairColor()); + else if (event.getId() == "prevstyle") + mPlayer->setHairStyle(mPlayer->getHairStyle() + + mPlayer->getNumOfHairstyles() - 1, + mPlayer->getHairColor()); } -std::string -CharCreateDialog::getName() +std::string CharCreateDialog::getName() { std::string name = mNameField->getText(); trim(name); return name; } -void -CharCreateDialog::unlock() +void CharCreateDialog::unlock() { mCreateButton->setEnabled(true); } -void -CharCreateDialog::attemptCharCreate() +void CharCreateDialog::attemptCharCreate() { // Send character infos MessageOut outMsg(mNetwork); diff --git a/src/gui/char_select.h b/src/gui/char_select.h index 74745788..23de061d 100644 --- a/src/gui/char_select.h +++ b/src/gui/char_select.h @@ -22,17 +22,16 @@ #ifndef _CHAR_SELECT_H #define _CHAR_SELECT_H +#include <guichan/actionlistener.hpp> + #include "window.h" -#include "../guichanfwd.h" -#include "../lockedarray.h" #include "../being.h" +#include "../lockedarray.h" -#include <guichan/actionlistener.hpp> - -class Player; class LocalPlayer; class Network; +class Player; class PlayerBox; /** @@ -65,8 +64,7 @@ class CharSelectDialog : public Window, public gcn::ActionListener gcn::Button *mSelectButton; gcn::Button *mCancelButton; - gcn::Button *mNewCharButton; - gcn::Button *mDelCharButton; + gcn::Button *mNewDelCharButton; gcn::Button *mPreviousButton; gcn::Button *mNextButton; @@ -110,14 +108,12 @@ class CharCreateDialog : public Window, public gcn::ActionListener */ ~CharCreateDialog(); - void - action(const gcn::ActionEvent &event); + void action(const gcn::ActionEvent &event); /** * Unlocks the dialog, enabling the create character button again. */ - void - unlock(); + void unlock(); private: /** diff --git a/src/gui/char_server.cpp b/src/gui/char_server.cpp index 7be2441d..bc096379 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" @@ -29,10 +28,7 @@ #include "../main.h" #include "../serverinfo.h" -#include "../net/network.h" // TODO this is just for iptostring, move that? - #include "../utils/gettext.h" -#include "../utils/strprintf.h" #include "../utils/tostring.h" extern SERVER_INFO **server_info; @@ -40,7 +36,8 @@ extern SERVER_INFO **server_info; /** * The list model for the server list. */ -class ServerListModel : public gcn::ListModel { +class ServerListModel : public gcn::ListModel +{ public: virtual ~ServerListModel() {}; @@ -81,13 +78,12 @@ ServerSelectDialog::ServerSelectDialog(LoginData *loginData, int nextState): add(mOkButton); add(mCancelButton); - if (n_server == 0) { + if (n_server == 0) // Disable Ok button mOkButton->setEnabled(false); - } else { + else // Select first server mServerList->setSelected(1); - } setLocationRelativeTo(getParent()); setVisible(true); @@ -99,31 +95,27 @@ ServerSelectDialog::~ServerSelectDialog() delete mServerListModel; } -void -ServerSelectDialog::action(const gcn::ActionEvent &event) +void ServerSelectDialog::action(const gcn::ActionEvent &event) { - if (event.getId() == "ok") { + if (event.getId() == "ok") + { mOkButton->setEnabled(false); const SERVER_INFO *si = server_info[mServerList->getSelected()]; mLoginData->hostname = iptostring(si->address); mLoginData->port = si->port; mLoginData->updateHost = si->updateHost; - state = mNextState; } - else if (event.getId() == "cancel") { + else if (event.getId() == "cancel") state = LOGIN_STATE; - } } -int -ServerListModel::getNumberOfElements() +int ServerListModel::getNumberOfElements() { return n_server; } -std::string -ServerListModel::getElementAt(int i) +std::string ServerListModel::getElementAt(int i) { const SERVER_INFO *si = server_info[i]; return si->name + " (" + toString(si->online_users) + ")"; diff --git a/src/gui/char_server.h b/src/gui/char_server.h index 9419c92d..49a5b47b 100644 --- a/src/gui/char_server.h +++ b/src/gui/char_server.h @@ -27,8 +27,6 @@ #include "window.h" -#include "../guichanfwd.h" - class LoginData; class ServerListModel; diff --git a/src/gui/chat.cpp b/src/gui/chat.cpp index 87d843a0..484426dd 100644 --- a/src/gui/chat.cpp +++ b/src/gui/chat.cpp @@ -19,57 +19,61 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <algorithm> -#include <sstream> - #include <guichan/focushandler.hpp> -#include "chat.h" - #include "browserbox.h" +#include "chat.h" #include "chatinput.h" +#include "itemlinkhandler.h" +#include "recorder.h" #include "scrollarea.h" #include "sdlinput.h" #include "windowcontainer.h" #include "widgets/layout.h" +#include "../beingmanager.h" #include "../configuration.h" #include "../game.h" #include "../localplayer.h" +#include "../party.h" #include "../net/messageout.h" #include "../net/protocol.h" +#include "../resources/iteminfo.h" +#include "../resources/itemdb.h" + #include "../utils/gettext.h" #include "../utils/strprintf.h" +#include "../utils/tostring.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"); + setWindowName(_("Chat")); setResizable(true); setDefaultSize(0, windowContainer->getHeight() - 123, 600, 123); setMinWidth(150); setMinHeight(90); + mItemLinkHandler = new ItemLinkHandler(); + mChatInput = new ChatInput; mChatInput->setActionEventId("chatinput"); mChatInput->addActionListener(this); mTextOutput = new BrowserBox(BrowserBox::AUTO_WRAP); mTextOutput->setOpaque(false); - mTextOutput->disableLinksAndUserColors(); mTextOutput->setMaxRow((int) config.getValue("ChatLogLength", 0)); + mTextOutput->setLinkHandler(mItemLinkHandler); + mScrollArea = new ScrollArea(mTextOutput); - mScrollArea->setPosition( - mScrollArea->getFrameSize(), mScrollArea->getFrameSize()); - mScrollArea->setScrollPolicy( - gcn::ScrollArea::SHOW_NEVER, gcn::ScrollArea::SHOW_ALWAYS); + mScrollArea->setScrollPolicy(gcn::ScrollArea::SHOW_NEVER, + gcn::ScrollArea::SHOW_ALWAYS); + mScrollArea->setScrollAmount(0, 1); mScrollArea->setOpaque(false); place(0, 0, mScrollArea, 5, 5).setPadding(0); @@ -84,23 +88,48 @@ 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.empty() ? '$' : partyPrefix.at(0)); + mReturnToggles = config.getValue("ReturnToggles", "0") == "1"; + mRecorder = new Recorder(this); + mParty = new Party(this, mNetwork); + + // If the player had @assert on in the last session, ask the server to + // run the @assert command for the player again. Convenience for GMs. + if (config.getValue(player_node->getName() + "GMassert", 0)) + chatSend(player_node->getName(), "@assert"); +} + +ChatWindow::~ChatWindow() +{ + char partyPrefix[2] = "."; + *partyPrefix = mPartyPrefix; + config.setValue("PartyPrefix", partyPrefix); + config.setValue("ReturnToggles", mReturnToggles ? "1" : "0"); + delete mRecorder; } -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; + std::string::size_type pos = line.find(" : "); - if (pos != std::string::npos) { + if (pos != std::string::npos) + { tmp.nick = line.substr(0, pos); tmp.text = line.substr(pos + 3); - } else { + } + else + { // Fix the owner of welcome message. if (line.substr(0, 7) == "Welcome") { @@ -108,77 +137,109 @@ void ChatWindow::chatLog(std::string line, int own) } } - std::string lineColor = "##0"; // Equiv. to BrowserBox::BLACK - switch (own) { + // *implements actions in a backwards compatible way* + if (own == BY_PLAYER && + tmp.text.at(0) == '*' && + tmp.text.at(tmp.text.length()-1) == '*') + { + tmp.text[0] = ' '; + tmp.text.erase(tmp.text.length() - 1); + own = ACT_IS; + } + + std::string lineColor = "##C"; + switch (own) + { case BY_GM: - if (tmp.nick.empty()) { - tmp.nick = _("Global announcement:"); + if (tmp.nick.empty()) + { + tmp.nick = std::string(_("Global announcement:")); tmp.nick += " "; - } else { + lineColor = "##G"; + } + else + { tmp.nick = strprintf(_("Global announcement from %s:"), tmp.nick.c_str()); tmp.nick += " "; + lineColor = "##1"; // Equiv. to BrowserBox::RED } - lineColor = "##1"; // Equiv. to BrowserBox::RED 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.nick += " "; 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 = strprintf(_("%s whispers:"), tmp.nick.c_str()); tmp.nick += " "; - 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; } + if (tmp.nick == ": ") + { + tmp.nick = ""; + lineColor = "##S"; + } + + if (tmp.nick.empty() && tmp.text.substr(0, 17) == "Visible GM status") + { + player_node->setGM(); + } + // 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) - << "] "; + 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()); + mScrollArea->setVerticalScrollAmount(mScrollArea-> + getVerticalMaxScroll()); } else { mTextOutput->addRow(line); } + + mRecorder->record(line.substr(3)); } void ChatWindow::chatLog(CHATSKILL act) @@ -192,12 +253,13 @@ void ChatWindow::action(const gcn::ActionEvent &event) { std::string message = mChatInput->getText(); - if (!message.empty()) { + if (!message.empty()) + { // If message different from previous, put it in the history - if (mHistory.empty() || message != mHistory.back()) { + if (mHistory.empty() || message != mHistory.back()) + { mHistory.push_back(message); } - // Reset history iterator mCurHist = mHistory.end(); @@ -208,13 +270,15 @@ void ChatWindow::action(const gcn::ActionEvent &event) mChatInput->setText(""); } - // Remove focus and hide input - mFocusHandler->focusNone(); + 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); + // If the chatWindow is shown up because you want to send a message + // It should hide now + if (mTmpVisible) + setVisible(false); } } } @@ -244,16 +308,15 @@ bool ChatWindow::isInputFocused() return mChatInput->isFocused(); } -void ChatWindow::whisper(const std::string &nick, std::string msg, - int prefixlen) +void ChatWindow::whisper(const std::string &nick, std::string msg) { 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) { + if (pos != std::string::npos) + { recvnick = msg.substr(1, pos - 1); msg.erase(0, pos + 2); } @@ -261,7 +324,8 @@ void ChatWindow::whisper(const std::string &nick, std::string msg, else { const std::string::size_type pos = msg.find(" "); - if (pos != std::string::npos) { + if (pos != std::string::npos) + { recvnick = msg.substr(0, pos); msg.erase(0, pos + 1); } @@ -274,8 +338,8 @@ void ChatWindow::whisper(const std::string &nick, std::string msg, outMsg.writeString(msg, msg.length()); chatLog(strprintf(_("Whispering to %s: %s"), - recvnick.c_str(), msg.c_str()), - BY_PLAYER); + recvnick.c_str(), msg.c_str()), + BY_PLAYER); } void ChatWindow::chatSend(const std::string &nick, std::string msg) @@ -284,8 +348,69 @@ void ChatWindow::chatSend(const std::string &nick, std::string msg) * require server handling by proper packet. Probably * those if elses should be replaced by protocol calls */ + trim(msg); + + if (msg.compare("") == 0) + return; + + // 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; + } + + // Check for item link + std::string::size_type start = msg.find('['); + while (start != std::string::npos && msg[start+1] != '@') + { + std::string::size_type end = msg.find(']', start); + if (start+1 != end && end != std::string::npos) + { + // Catch multiple embeds and ignore them + // so it doesn't crash the client. + while ((msg.find('[', start + 1) != std::string::npos) && + (msg.find('[', start + 1) < end)) + { + start = msg.find('[', start + 1); + } + + std::string temp = msg.substr(start+1, end - start - 1); + + trim(temp); + + for (unsigned int i = 0; i < temp.size(); i++) + { + temp[i] = (char) tolower(temp[i]); + } + + const ItemInfo itemInfo = ItemDB::get(temp); + if (itemInfo.getName() != _("Unknown item")) + { + msg.insert(end, "@@"); + msg.insert(start+1, "|"); + msg.insert(start+1, toString(itemInfo.getId())); + msg.insert(start+1, "@@"); + } + } + start = msg.find('[', start + 1); + } + // Prepare ordinary message - if (msg.substr(0, 1) != "/") { + if (msg.substr(0, 1) != "/") + { msg = nick + " : " + msg; MessageOut outMsg(mNetwork); @@ -293,21 +418,38 @@ void 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()); } - else if (msg.substr(0, IS_HELP_LENGTH) == IS_HELP) + else if (command == "help") { - msg.erase(0, IS_HELP_LENGTH + 1); trim(msg); std::size_t space = msg.find(" "); std::string msg1; + if (space == std::string::npos) { msg1 = ""; @@ -317,33 +459,161 @@ void ChatWindow::chatSend(const std::string &nick, std::string msg) msg1 = msg.substr(space + 1, msg.length()); msg = msg.substr(0, space); } - if (msg != "" && msg.at(0) == '/') + + if (!msg.empty() && msg.at(0) == '/') { msg.erase(0, 1); } + 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) + else if (command == "who") { MessageOut outMsg(mNetwork); outMsg.writeInt16(0x00c1); } - else if (msg.substr(0, IS_CLEAR_LENGTH) == IS_CLEAR) - { + else if (command == "clear") mTextOutput->clearRows(); + else if (command == "whisper" || command == "msg" || command == "w") + whisper(nick, msg); + else if (command == "record") + mRecorder->changeRecordingStatus(msg); + else if (command == "toggle") + { + if (msg.empty()) + { + chatLog(mReturnToggles ? _("Return toggles chat.") + : _("Message closes chat."), BY_SERVER); + return; + } + + msg = msg.substr(0, 1); + + if (msg == "1" || + msg == "y" || msg == "Y" || + msg == "t" || msg == "T") + { + chatLog(_("Return now toggles chat."), BY_SERVER); + mReturnToggles = true; + return; + } + else if (msg == "0" || + msg == "n" || msg == "N" || + msg == "f" || msg == "F") + { + chatLog(_("Message now closes chat."), BY_SERVER); + mReturnToggles = false; + return; + } + else + chatLog(_("Options to /toggle are \"yes\", \"no\", \"true\", " + "\"false\", \"1\", \"0\"."), BY_SERVER); + } + else if (command == "party") + { + if (msg.empty()) + { + 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.empty()) + { + msg = msg.substr(0, space); + trim(msg); + } + + party(msg, rest); + return; + } + else if (command == "cast") + { + /* + * This will eventually be replaced by a GUI, so + * we don't need to get too sophisticated + */ + 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 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.empty()) + { + 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); + } + } + else if (command == "me") + { + std::stringstream actionStr; + actionStr << "*" << msg << "*"; + chatSend(player_node->getName(), actionStr.str()); } - 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); @@ -353,8 +623,10 @@ void ChatWindow::chatSend(const std::string &nick, std::string msg) std::string ChatWindow::const_msg(CHATSKILL act) { std::string msg; - if (act.success == SKILL_FAILED && act.skill == SKILL_BASIC) { - switch (act.bskill) { + if (act.success == SKILL_FAILED && act.skill == SKILL_BASIC) + { + switch (act.bskill) + { case BSKILL_TRADE: msg = _("Trade failed!"); break; @@ -377,7 +649,8 @@ std::string ChatWindow::const_msg(CHATSKILL act) msg += " "; - switch (act.reason) { + switch (act.reason) + { case RFAIL_SKILLDEP: msg += _("You have not yet reached a high enough lvl!"); break; @@ -394,7 +667,7 @@ std::string ChatWindow::const_msg(CHATSKILL act) msg += _("You cannot do that right now!"); break; case RFAIL_ZENY: - msg += _("Seems you need more Zeny... ;-)"); + msg += _("Seems you need more GP... ;-)"); break; case RFAIL_WEAPON: msg += _("You cannot use this skill with that kind of weapon!"); @@ -412,8 +685,11 @@ std::string ChatWindow::const_msg(CHATSKILL act) msg += _("Huh? What's that?"); break; } - } else { - switch (act.skill) { + } + else + { + switch (act.skill) + { case SKILL_WARP : msg = _("Warp failed..."); break; @@ -448,11 +724,14 @@ void ChatWindow::keyPressed(gcn::KeyEvent &event) { // Move forward through the history HistoryIterator prevHist = mCurHist++; - if (mCurHist != mHistory.end()) { + + if (mCurHist != mHistory.end()) + { mChatInput->setText(*mCurHist); mChatInput->setCaretPosition(mChatInput->getText().length()); } - else { + else + { mCurHist = prevHist; } } @@ -468,10 +747,18 @@ void ChatWindow::keyPressed(gcn::KeyEvent &event) void ChatWindow::setInputText(std::string input_str) { - mChatInput->setText(input_str + " "); + mChatInput->setText(mChatInput->getText() + input_str + " "); requestChatFocus(); } +void ChatWindow::addItemText(const std::string &item) +{ + std::ostringstream text; + text << "[" << item << "] "; + mChatInput->setText(mChatInput->getText() + text.str()); + requestChatFocus(); +} + void ChatWindow::setVisible(bool isVisible) { Window::setVisible(isVisible); @@ -484,70 +771,146 @@ void 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.empty()) + { + char temp[2] = "."; + *temp = mPartyPrefix; + chatLog(_("The current party prefix is ") + std::string(temp), + BY_SERVER); + } + else 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); + } + } + } + else + mParty->respond(command, rest); +} + +void ChatWindow::help(const std::string & msg1, const std::string & msg2) { chatLog(_("-- Help --"), BY_SERVER); - if (msg1 == "") + if (msg1.empty()) { chatLog(_("/announce: Global announcement (GM only)"), BY_SERVER); chatLog(_("/clear: Clears this window"), BY_SERVER); chatLog(_("/help: Display this help"), BY_SERVER); - chatLog(_("/where: Display map name"), BY_SERVER); - chatLog(_("/whisper <nick> <message>: Sends a private <message>" - " to <nick>"), BY_SERVER); + chatLog(_("/me <message>: Tell something about yourself"), BY_SERVER); + chatLog(_("/msg <nick> <message>: Alternate form for /whisper"), + BY_SERVER); + chatLog(_("/party <command> <params>: Party commands"), BY_SERVER); + chatLog(_("/present: Get list of players present"), BY_SERVER); + chatLog(_("/record <filename>: Start recording the chat to an " + "external file"), BY_SERVER); + chatLog(_("/toggle: Determine whether <return> toggles the chat log"), + BY_SERVER); chatLog(_("/w <nick> <message>: Short form for /whisper"), BY_SERVER); + chatLog(_("/where: Display map name"), BY_SERVER); + chatLog(_("/whisper <nick> <message>: Sends a private <message> " + "to <nick>"), BY_SERVER); chatLog(_("/who: Display number of online users"), BY_SERVER); chatLog(_("For more information, type /help <command>"), BY_SERVER); - return; } - if (msg1 == "announce") + else if (msg1 == "announce") { chatLog(_("Command: /announce <msg>"), BY_SERVER); chatLog(_("*** only available to a GM ***"), BY_SERVER); chatLog(_("This command sends the message <msg> to " "all players currently online."), BY_SERVER); - return; } - if (msg1 == "clear") + else if (msg1 == "clear") { chatLog(_("Command: /clear"), BY_SERVER); chatLog(_("This command clears the chat log of previous chat."), BY_SERVER); - return; } - if (msg1 == "help") + else if (msg1 == "help") { chatLog(_("Command: /help"), BY_SERVER); chatLog(_("This command displays a list of all commands available."), BY_SERVER); chatLog(_("Command: /help <command>"), BY_SERVER); chatLog(_("This command displays help on <command>."), BY_SERVER); - return; } - if (msg1 == "where") + else if (msg1 == "me") + { + chatLog(_("Command: /me <msg>"), BY_SERVER); + chatLog(_("This command tell others you are (doing) <msg>."), + BY_SERVER); + } + else if (msg1 == "party") + { + mParty->help(msg2); + } + else 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); + } + else if (msg1 == "record") + { + chatLog(_("Command: /record <filename>"), BY_SERVER); + chatLog(_("This command starts recording the chat log to the file " + "<filename>."), BY_SERVER); + chatLog(_("Command: /record"), BY_SERVER); + chatLog(_("This command finishes a recording session."), BY_SERVER); + } + else 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); + } + else if (msg1 == "where") { chatLog(_("Command: /where"), BY_SERVER); chatLog(_("This command displays the name of the current map."), BY_SERVER); - return; } - if (msg1 == "whisper" || msg1 == "w") + else if (msg1 == "whisper" || msg1 == "msg" || msg1 == "w") { + chatLog(_("Command: /msg <nick> <msg>"), BY_SERVER); 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(_("If the <nick> has spaces in it, enclose it in " - "double quotes (\")."), BY_SERVER); - return; + "double quotes (\")."), BY_SERVER); } - if (msg1 == "who") + else if (msg1 == "who") { chatLog(_("Command: /who"), BY_SERVER); chatLog(_("This command displays the number of players currently " - "online."), BY_SERVER); - return; + "online."), BY_SERVER); + } + else + { + chatLog(_("Unknown command."), BY_SERVER); + chatLog(_("Type /help for a list of commands."), BY_SERVER); } - chatLog(_("Unknown command."), BY_SERVER); - chatLog(_("Type /help for a list of commands."), BY_SERVER); } diff --git a/src/gui/chat.h b/src/gui/chat.h index ad89c8dc..2fadb014 100644 --- a/src/gui/chat.h +++ b/src/gui/chat.h @@ -30,36 +30,23 @@ #include "window.h" -#include "../guichanfwd.h" - class BrowserBox; class Network; +class Recorder; +class Party; class ScrollArea; +class ItemLinkHandler; #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 5 // getting whispered at +#define ACT_IS 6 // equivalent to "/me" on IRC -#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 BY_LOGGER 7 /** * gets in between usernick and message text depending on @@ -127,14 +114,19 @@ class ChatWindow : public Window, public gcn::ActionListener, ChatWindow(Network *network); /** + * Destructor: used to write back values to the config file + */ + ~ChatWindow(); + + /** * 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); + void chatLog(std::string line, int own, bool ignoreRecord = false); - /* + /** * Calls original chat_log() after processing the packet. */ void chatLog(CHATSKILL); @@ -186,6 +178,9 @@ class ChatWindow : public Window, public gcn::ActionListener, /** Called to set current text */ void setInputText(std::string input_str); + /** Called to add item to chat */ + void addItemText(const std::string &item); + /** Override to reset mTmpVisible */ void setVisible(bool visible); @@ -199,6 +194,14 @@ class ChatWindow : public Window, public gcn::ActionListener, 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 @@ -207,10 +210,11 @@ class ChatWindow : public Window, public gcn::ActionListener, 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); + void whisper(const std::string &nick, std::string msg); /** One item in the chat log */ struct CHATLOG @@ -226,13 +230,19 @@ class ChatWindow : public Window, public gcn::ActionListener, gcn::TextField *mChatInput; /**< Input box for typing chat messages */ BrowserBox *mTextOutput; /**< Text box for displaying chat history */ ScrollArea *mScrollArea; /**< Scroll area around text output */ - + ItemLinkHandler *mItemLinkHandler; /** Used for showing item popup on + clicking links **/ 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 07144c5b..a4a50502 100644 --- a/src/gui/chatinput.h +++ b/src/gui/chatinput.h @@ -22,10 +22,10 @@ #ifndef CHATINPUT_H #define CHATINPUT_H -#include "textfield.h" - #include <guichan/focuslistener.hpp> +#include "textfield.h" + /** * The chat input hides when it loses focus. It is also invisible by default. */ diff --git a/src/gui/checkbox.cpp b/src/gui/checkbox.cpp index b8fca2b8..511ed34c 100644 --- a/src/gui/checkbox.cpp +++ b/src/gui/checkbox.cpp @@ -21,12 +21,14 @@ #include "checkbox.h" +#include "../configuration.h" #include "../graphics.h" #include "../resources/image.h" #include "../resources/resourcemanager.h" int CheckBox::instances = 0; +float CheckBox::mAlpha = config.getValue("guialpha", 0.8); Image *CheckBox::checkBoxNormal; Image *CheckBox::checkBoxChecked; Image *CheckBox::checkBoxDisabled; @@ -43,6 +45,13 @@ CheckBox::CheckBox(const std::string& caption, bool selected): checkBoxChecked = checkBox->getSubImage(9, 0, 9, 10); checkBoxDisabled = checkBox->getSubImage(18, 0, 9, 10); checkBoxDisabledChecked = checkBox->getSubImage(27, 0, 9, 10); + if (config.getValue("opengl", 0)) + { + checkBoxNormal->setAlpha(mAlpha); + checkBoxChecked->setAlpha(mAlpha); + checkBoxDisabled->setAlpha(mAlpha); + checkBoxDisabledChecked->setAlpha(mAlpha); + } checkBox->decRef(); } @@ -66,16 +75,26 @@ void CheckBox::drawBox(gcn::Graphics* graphics) { Image *box; - if (isSelected()) { - if (isEnabled()) { + if (isSelected()) + { + if (isEnabled()) box = checkBoxChecked; - } else { + else box = checkBoxDisabledChecked; - } - } else if (isEnabled()) { + } + else if (isEnabled()) box = checkBoxNormal; - } else { + else box = checkBoxDisabled; + + if (config.getValue("guialpha", 0.8) != mAlpha && + config.getValue("opengl", 0)) + { + mAlpha = config.getValue("guialpha", 0.8); + checkBoxNormal->setAlpha(mAlpha); + checkBoxChecked->setAlpha(mAlpha); + checkBoxDisabled->setAlpha(mAlpha); + checkBoxDisabledChecked->setAlpha(mAlpha); } static_cast<Graphics*>(graphics)->drawImage(box, 2, 2); diff --git a/src/gui/checkbox.h b/src/gui/checkbox.h index 4b312d22..20adb43c 100644 --- a/src/gui/checkbox.h +++ b/src/gui/checkbox.h @@ -22,8 +22,6 @@ #ifndef CHECKBOX_H #define CHECKBOX_H -#include <iosfwd> - #include <guichan/widgets/checkbox.hpp> class Image; @@ -33,7 +31,8 @@ class Image; * * \ingroup GUI */ -class CheckBox : public gcn::CheckBox { +class CheckBox : public gcn::CheckBox +{ public: /** * Constructor. @@ -52,6 +51,7 @@ class CheckBox : public gcn::CheckBox { private: static int instances; + static float mAlpha; static Image *checkBoxNormal; static Image *checkBoxChecked; static Image *checkBoxDisabled; diff --git a/src/gui/color.cpp b/src/gui/color.cpp new file mode 100644 index 00000000..7a29025e --- /dev/null +++ b/src/gui/color.cpp @@ -0,0 +1,146 @@ +/* + * Configurable text colors + * Copyright (C) 2008 Douglas Boffey <dougaboffey@netscape.net> + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "color.h" + +#include "../configuration.h" + +#include "../utils/gettext.h" +#include "../utils/tostring.h" + +Color::Color() +{ + addColor('C', 0x000000, _("Chat")); + addColor('G', 0xff0000, _("GM")); + addColor('H', 0xebc873, _("Highlight")); + addColor('Y', 0x1fa052, _("Player")); + addColor('W', 0x0000ff, _("Whisper")); + addColor('I', 0xa08527, _("Is")); + addColor('P', 0xff00d8, _("Party")); + addColor('S', 0x8415e2, _("Server")); + addColor('L', 0x919191, _("Logger")); + addColor('<', 0xe50d0d, _("Hyperlink")); + commit(); +} + +Color::~Color() +{ + for (ColVector::iterator col = mColVector.begin(), + colEnd = mColVector.end(); + col != colEnd; + ++col) + { + config.setValue("color" + col->text, toString(col->rgb)); + } +} + +void Color::setColor(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 Color::getColor(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 Color::getElementAt(int i) +{ + if (i < 0 || i >= getNumberOfElements()) + { + return ""; + } + return mColVector[i].text; +} + +char Color::getColorCharAt(int i) +{ + if (i < 0 || i >= getNumberOfElements()) + { + return 'C'; + } + return mColVector[i].ch; +} + +void Color::addColor(const char c, const int rgb, const std::string &text) +{ + int trueRgb = (int)config.getValue("color" + text, rgb); + mColVector.push_back(colorElem(c, trueRgb, text)); +} + +int Color::getColorAt(int i) +{ + if (i < 0 || i >= getNumberOfElements()) + { + return 0; + } + return mColVector[i].rgb; +} + +void Color::setColorAt(int i, int rgb) +{ + if (i >= 0 && i < getNumberOfElements()) + { + mColVector[i].rgb = rgb; + } +} + +void Color::commit() +{ + for (ColVector::iterator i = mColVector.begin(), iEnd = mColVector.end(); + i != iEnd; + ++i) + { + i->committedRgb = i->rgb; + } +} + +void Color::rollback() +{ + for (ColVector::iterator i = mColVector.begin(), iEnd = mColVector.end(); + i != iEnd; + ++i) + { + i->rgb = i->committedRgb; + } +} diff --git a/src/gui/color.h b/src/gui/color.h new file mode 100644 index 00000000..509448e7 --- /dev/null +++ b/src/gui/color.h @@ -0,0 +1,136 @@ +/* + * Configurable text colors + * Copyright (C) 2008 Douglas Boffey <dougaboffey@netscape.net> + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef COLOUR_H +#define COLOUR_H + +#include <string> +#include <vector> + +#include <guichan/listmodel.hpp> + +class Color : public gcn::ListModel +{ + public: + /** + * Constructor + */ + Color(); + + /** + * Destructor + */ + ~Color(); + + /** + * Define the color replacement for a character + * + * @param c charater to be replaced + * @param rgb color to replace character + */ + void setColor(const char c, const int rgb); + + /** + * Define the color replacement for a character + * + * @param c character to be replaced + * @param r red component + * @param g green component + * @param b blue component + */ + void setColor(const char c, const int r, const int g, const int b) + { + setColor(c, (r << 16) | (g << 8) | b); + } + + /** + * Return the color associated with a character, if exists + * + * @param c character requested + * @param valid indicate whether character is known + */ + int getColor(const char c, bool &valid) const; + + /** + * Return the number of colors known + */ + int getNumberOfElements() {return mColVector.size(); } + + /** + * Return the name of the ith color + * + * @param i index of color interested in + */ + std::string getElementAt(int i); + + /** + * Get the color for the element at index i in the current color + * model + */ + int getColorAt(int i); + + /** + * Get the character used by the color for the element at index i in + * the current color model + */ + char getColorCharAt(int i); + + /** + * Set the color for the element at index i + */ + void setColorAt(int i, int rgb); + + /** + * Commit the colors + */ + void commit(); + + /** + * Rollback the colors + */ + void rollback(); + + private: + struct colorElem + { + colorElem(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<colorElem> ColVector; + ColVector mColVector; + + /** + * Initialise color + * + * @param c character that needs initialising + * @param rgb default color if not found in config + * @param text identifier of color + */ + void addColor(const char c, const int rgb, const std::string &text); +}; + +extern Color *textColor; + +#endif diff --git a/src/gui/confirm_dialog.cpp b/src/gui/confirm_dialog.cpp index 46b7c971..38697f3a 100644 --- a/src/gui/confirm_dialog.cpp +++ b/src/gui/confirm_dialog.cpp @@ -19,11 +19,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "confirm_dialog.h" - -#include <guichan/widgets/label.hpp> +#include <guichan/font.hpp> #include "button.h" +#include "confirm_dialog.h" +#include "scrollarea.h" +#include "textbox.h" #include "../utils/gettext.h" @@ -31,32 +32,55 @@ 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->setTextWrapped(msg, 260); + + 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); - if (getParent()) { + if (getParent()) + { setLocationRelativeTo(getParent()); getParent()->moveToTop(this); } @@ -64,6 +88,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 69b3e9e4..3fa2b90d 100644 --- a/src/gui/confirm_dialog.h +++ b/src/gui/confirm_dialog.h @@ -26,13 +26,16 @@ #include "window.h" +class ScrollArea; +class TextBox; /** * An option dialog. * * \ingroup GUI */ -class ConfirmDialog : public Window, public gcn::ActionListener { +class ConfirmDialog : public Window, public gcn::ActionListener +{ public: /** * Constructor. @@ -42,10 +45,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 f73bb74d..a69698e9 100644 --- a/src/gui/connection.cpp +++ b/src/gui/connection.cpp @@ -19,20 +19,20 @@ * 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" #include "../utils/gettext.h" -namespace { +namespace +{ struct ConnectionActionListener : public gcn::ActionListener { void action(const gcn::ActionEvent &event) { state = EXIT_STATE; } @@ -63,10 +63,10 @@ ConnectionDialog::ConnectionDialog(): void ConnectionDialog::logic() { mProgress += 0.005f; + if (mProgress > 1.0f) - { mProgress = 0.0f; - } + mProgressBar->setProgress(mProgress); Window::logic(); } diff --git a/src/gui/debugwindow.cpp b/src/gui/debugwindow.cpp index 669aabd2..5a5acfad 100644 --- a/src/gui/debugwindow.cpp +++ b/src/gui/debugwindow.cpp @@ -19,14 +19,11 @@ * 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 "gui.h" +#include "debugwindow.h" #include "viewport.h" #include "widgets/layout.h" diff --git a/src/gui/debugwindow.h b/src/gui/debugwindow.h index 00119d15..e089de27 100644 --- a/src/gui/debugwindow.h +++ b/src/gui/debugwindow.h @@ -22,14 +22,10 @@ #ifndef DEBUGWINDOW_H #define DEBUGWINDOW_H -#include <iosfwd> - #include <guichan/actionlistener.hpp> #include "window.h" -#include "../guichanfwd.h" - /** * The debug window. * diff --git a/src/gui/emotecontainer.cpp b/src/gui/emotecontainer.cpp new file mode 100644 index 00000000..94ce9736 --- /dev/null +++ b/src/gui/emotecontainer.cpp @@ -0,0 +1,172 @@ +/* + * Extended support for activating emotes + * Copyright (C) 2009 Aethyra Development Team + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <guichan/mouseinput.hpp> +#include <guichan/selectionlistener.hpp> + +#include "emotecontainer.h" + +#include "../animatedsprite.h" +#include "../configuration.h" +#include "../emoteshortcut.h" +#include "../graphics.h" +#include "../localplayer.h" +#include "../log.h" + +#include "../resources/emotedb.h" +#include "../resources/image.h" +#include "../resources/iteminfo.h" +#include "../resources/resourcemanager.h" + +#include "../utils/dtor.h" +#include "../utils/gettext.h" +#include "../utils/tostring.h" + +const int EmoteContainer::gridWidth = 34; // emote icon width + 4 +const int EmoteContainer::gridHeight = 36; // emote icon height + 4 + +static const int NO_EMOTE = -1; + +EmoteContainer::EmoteContainer(): + mSelectedEmoteIndex(NO_EMOTE) +{ + ResourceManager *resman = ResourceManager::getInstance(); + + // Setup emote sprites + for (int i = 0; i <= EmoteDB::getLast(); i++) + { + mEmoteImg.push_back(player_node->getEmote(i)); + } + + mSelImg = resman->getImage("graphics/gui/selection.png"); + if (!mSelImg) logger->error(_("Unable to load selection.png")); + + mSelImg->setAlpha(config.getValue("guialpha", 0.8)); + + mMaxEmote = EmoteDB::getLast() + 1; + + addMouseListener(this); + addWidgetListener(this); +} + +EmoteContainer::~EmoteContainer() +{ + if (!mSelImg) + { + mSelImg->decRef(); + mSelImg = NULL; + } +} + +void EmoteContainer::draw(gcn::Graphics *graphics) +{ + int columns = getWidth() / gridWidth; + + // Have at least 1 column + if (columns < 1) + { + columns = 1; + } + + for (int i = 0; i < mMaxEmote ; i++) + { + int emoteX = ((i) % columns) * gridWidth; + int emoteY = ((i) / columns) * gridHeight; + + // Draw emote icon + mEmoteImg[i]->draw(static_cast<Graphics*>(graphics), emoteX, emoteY); + + // Draw selection image below selected item + if (mSelectedEmoteIndex == i) + { + static_cast<Graphics*>(graphics)->drawImage( + mSelImg, emoteX, emoteY); + } + } +} + +void EmoteContainer::widgetResized(const gcn::Event &event) +{ + recalculateHeight(); +} + +void EmoteContainer::recalculateHeight() +{ + int cols = getWidth() / gridWidth; + + if (cols < 1) + cols = 1; + + const int rows = (mMaxEmote / cols) + (mMaxEmote % cols > 0 ? 1 : 0); + const int height = rows * gridHeight + 8; + if (height != getHeight()) + setHeight(height); +} + +int EmoteContainer::getSelectedEmote() +{ + if (mSelectedEmoteIndex == NO_EMOTE) + return 0; + + return 1 + mSelectedEmoteIndex; +} + +void EmoteContainer::selectNone() +{ + setSelectedEmoteIndex(NO_EMOTE); +} + +void EmoteContainer::setSelectedEmoteIndex(int index) +{ + if (index < 0 || index >= mMaxEmote ) + mSelectedEmoteIndex = NO_EMOTE; + else + mSelectedEmoteIndex = index; +} + +void EmoteContainer::distributeValueChangedEvent() +{ + gcn::SelectionEvent event(this); + std::list<gcn::SelectionListener*>::iterator i_end = mListeners.end(); + std::list<gcn::SelectionListener*>::iterator i; + + for (i = mListeners.begin(); i != i_end; ++i) + { + (*i)->valueChanged(event); + } +} + +void EmoteContainer::mousePressed(gcn::MouseEvent &event) +{ + int button = event.getButton(); + if (button == gcn::MouseEvent::LEFT || button == gcn::MouseEvent::RIGHT) + { + int columns = getWidth() / gridWidth; + int mx = event.getX(); + int my = event.getY(); + int index = mx / gridWidth + ((my / gridHeight) * columns); + if (index < mMaxEmote) + { + setSelectedEmoteIndex(index); + emoteShortcut->setEmoteSelected(index + 1); + } + } +} diff --git a/src/gui/emotecontainer.h b/src/gui/emotecontainer.h new file mode 100644 index 00000000..fefce793 --- /dev/null +++ b/src/gui/emotecontainer.h @@ -0,0 +1,136 @@ +/* + * Extended support for activating emotes + * Copyright (C) 2009 Aethyra Development Team + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EMOTECONTAINER_H +#define EMOTECONTAINER_H + +#include <list> +#include <vector> + +#include <guichan/mouselistener.hpp> +#include <guichan/widget.hpp> +#include <guichan/widgetlistener.hpp> + +class AnimatedSprite; +class Image; + +namespace gcn { + class SelectionListener; +} + +/** + * An emote container. Used to show emotes in inventory and trade dialog. + * + * \ingroup GUI + */ +class EmoteContainer : public gcn::Widget, + public gcn::MouseListener, + public gcn::WidgetListener +{ + public: + /** + * Constructor. Initializes the graphic. + */ + EmoteContainer(); + + /** + * Destructor. + */ + virtual ~EmoteContainer(); + + /** + * Draws the emotes. + */ + void draw(gcn::Graphics *graphics); + + /** + * Called whenever the widget changes size. + */ + void widgetResized(const gcn::Event &event); + + /** + * Handles mouse click. + */ + void mousePressed(gcn::MouseEvent &event); + + /** + * Returns the selected emote. + */ + int getSelectedEmote(); + + /** + * Sets selected emote to NULL. + */ + void selectNone(); + + /** + * Adds a listener to the list that's notified each time a change to + * the selection occurs. + */ + void addSelectionListener(gcn::SelectionListener *listener) + { + mListeners.push_back(listener); + } + + /** + * Removes a listener from the list that's notified each time a change + * to the selection occurs. + */ + void removeSelectionListener(gcn::SelectionListener *listener) + { + mListeners.remove(listener); + } + + private: + /** + * Sets the currently selected emote. Invalid (e.g., negative) indices + * set `no emotr'. + */ + void setSelectedEmoteIndex(int index); + + /** + * Find the current emote index by the most recently used emote ID + */ + void refindSelectedEmote(void); + + /** + * Determine and set the height of the container. + */ + void recalculateHeight(void); + + /** + * Sends out selection events to the list of selection listeners. + */ + void distributeValueChangedEvent(void); + + std::vector<AnimatedSprite*> mEmoteImg; + Image *mSelImg; + int mSelectedEmoteIndex; + + int mMaxEmote; + + std::list<gcn::SelectionListener*> mListeners; + + static const int gridWidth; + static const int gridHeight; +}; + +#endif diff --git a/src/gui/emoteshortcutcontainer.cpp b/src/gui/emoteshortcutcontainer.cpp new file mode 100644 index 00000000..b66592c1 --- /dev/null +++ b/src/gui/emoteshortcutcontainer.cpp @@ -0,0 +1,204 @@ +/* + * Extended support for activating emotes + * Copyright (C) 2009 Aethyra Development Team + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emoteshortcutcontainer.h" + +#include "../animatedsprite.h" +#include "../configuration.h" +#include "../emoteshortcut.h" +#include "../graphics.h" +#include "../inventory.h" +#include "../item.h" +#include "../itemshortcut.h" +#include "../keyboardconfig.h" +#include "../localplayer.h" +#include "../log.h" + +#include "../resources/emotedb.h" +#include "../resources/image.h" +#include "../resources/resourcemanager.h" + +#include "../utils/dtor.h" +#include "../utils/gettext.h" +#include "../utils/tostring.h" + +static const int MAX_ITEMS = 12; + +EmoteShortcutContainer::EmoteShortcutContainer(): + ShortcutContainer(), + mEmoteClicked(false), + mEmoteMoved(0) +{ + addMouseListener(this); + addWidgetListener(this); + + ResourceManager *resman = ResourceManager::getInstance(); + + mBackgroundImg = resman->getImage("graphics/gui/item_shortcut_bgr.png"); + + mBackgroundImg->setAlpha(config.getValue("guialpha", 0.8)); + + // Setup emote sprites + for (int i = 0; i <= EmoteDB::getLast(); i++) + { + mEmoteImg.push_back(player_node->getEmote(i)); + } + + mMaxItems = EmoteDB::getLast() < MAX_ITEMS ? EmoteDB::getLast() : MAX_ITEMS; + + mBoxHeight = mBackgroundImg->getHeight(); + mBoxWidth = mBackgroundImg->getWidth(); +} + +EmoteShortcutContainer::~EmoteShortcutContainer() +{ + mBackgroundImg->decRef(); +} + +void EmoteShortcutContainer::draw(gcn::Graphics *graphics) +{ + Graphics *g = static_cast<Graphics*>(graphics); + + graphics->setFont(getFont()); + + for (int i = 0; i < mMaxItems; i++) + { + const int emoteX = (i % mGridWidth) * mBoxWidth; + const int emoteY = (i / mGridWidth) * mBoxHeight; + + g->drawImage(mBackgroundImg, emoteX, emoteY); + + // Draw emote keyboard shortcut. + const char *key = SDL_GetKeyName( + (SDLKey) keyboard.getKeyValue(keyboard.KEY_EMOTE_1 + i)); + graphics->setColor(0x000000); + g->drawText(key, emoteX + 2, emoteY + 2, gcn::Graphics::LEFT); + + if (emoteShortcut->getEmote(i)) + { + mEmoteImg[emoteShortcut->getEmote(i) - 1]->draw(g, emoteX + 2, emoteY + 10); + } + + } + + if (mEmoteMoved) + { + // Draw the emote image being dragged by the cursor. + AnimatedSprite* sprite = mEmoteImg[mEmoteMoved - 1]; + if (sprite) + { + const int tPosX = mCursorPosX - (sprite->getWidth() / 2); + const int tPosY = mCursorPosY - (sprite->getHeight() / 2); + + sprite->draw(g, tPosX, tPosY); + } + } + + if (config.getValue("guialpha", 0.8) != mAlpha) + { + mAlpha = config.getValue("guialpha", 0.8); + mBackgroundImg->setAlpha(mAlpha); + } +} + +void EmoteShortcutContainer::mouseDragged(gcn::MouseEvent &event) +{ + if (event.getButton() == gcn::MouseEvent::LEFT) + { + if (!mEmoteMoved && mEmoteClicked) + { + const int index = getIndexFromGrid(event.getX(), event.getY()); + const int emoteId = emoteShortcut->getEmote(index); + + if (index == -1) + { + return; + } + + if (emoteId) + { + mEmoteMoved = emoteId; + emoteShortcut->removeEmote(index); + } + } + if (mEmoteMoved) + { + mCursorPosX = event.getX(); + mCursorPosY = event.getY(); + } + } +} + +void EmoteShortcutContainer::mousePressed(gcn::MouseEvent &event) +{ + const int index = getIndexFromGrid(event.getX(), event.getY()); + + if (index == -1) + { + return; + } + + // Stores the selected emote if there is one. + if (emoteShortcut->isEmoteSelected()) + { + emoteShortcut->setEmote(index); + emoteShortcut->setEmoteSelected(0); + } + else if (emoteShortcut->getEmote(index)) + { + mEmoteClicked = true; + } +} + +void EmoteShortcutContainer::mouseReleased(gcn::MouseEvent &event) +{ + if (event.getButton() == gcn::MouseEvent::LEFT) + { + const int index = getIndexFromGrid(event.getX(), event.getY()); + + if (emoteShortcut->isEmoteSelected()) + { + emoteShortcut->setEmoteSelected(0); + } + + if (index == -1) + { + mEmoteMoved = 0; + return; + } + + if (mEmoteMoved) + { + emoteShortcut->setEmotes(index, mEmoteMoved); + mEmoteMoved = 0; + } + else if (emoteShortcut->getEmote(index) && mEmoteClicked) + { + emoteShortcut->useEmote(index + 1); + } + + if (mEmoteClicked) + { + mEmoteClicked = false; + } + } +} + diff --git a/src/gui/emoteshortcutcontainer.h b/src/gui/emoteshortcutcontainer.h new file mode 100644 index 00000000..d32a9f79 --- /dev/null +++ b/src/gui/emoteshortcutcontainer.h @@ -0,0 +1,77 @@ +/* + * Extended support for activating emotes + * Copyright (C) 2009 Aethyra Development Team + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EMOTESHORTCUTCONTAINER_H +#define EMOTESHORTCUTCONTAINER_H + +#include <vector> + +#include "shortcutcontainer.h" + +class AnimatedSprite; +class Image; + +/** + * An emote shortcut container. Used to quickly use emoticons. + * + * \ingroup GUI + */ +class EmoteShortcutContainer : public ShortcutContainer +{ + public: + /** + * Constructor. Initializes the graphic. + */ + EmoteShortcutContainer(); + + /** + * Destructor. + */ + virtual ~EmoteShortcutContainer(); + + /** + * Draws the items. + */ + void draw(gcn::Graphics *graphics); + + /** + * Handles mouse when dragged. + */ + void mouseDragged(gcn::MouseEvent &event); + + /** + * Handles mouse when pressed. + */ + void mousePressed(gcn::MouseEvent &event); + + /** + * Handles mouse release. + */ + void mouseReleased(gcn::MouseEvent &event); + + private: + std::vector<AnimatedSprite*> mEmoteImg; + + bool mEmoteClicked; + int mEmoteMoved; +}; + +#endif diff --git a/src/gui/emotewindow.cpp b/src/gui/emotewindow.cpp new file mode 100644 index 00000000..f4a8999a --- /dev/null +++ b/src/gui/emotewindow.cpp @@ -0,0 +1,77 @@ +/* + * Extended support for activating emotes + * Copyright (C) 2009 Aethyra Development Team + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "button.h" +#include "gui.h" +#include "emotewindow.h" +#include "emotecontainer.h" +#include "scrollarea.h" + +#include "widgets/layout.h" + +#include "../localplayer.h" + +#include "../utils/gettext.h" +#include "../utils/tostring.h" + +EmoteWindow::EmoteWindow(): + Window(_("Emote")) +{ + setWindowName(_("Emote")); + setResizable(true); + setCloseButton(true); + setMinWidth(80); + setMinHeight(130); + setDefaultSize(115, 25, 322, 200); + + mUseButton = new Button(_("Use"), "use", this); + + mEmotes = new EmoteContainer(); + mEmotes->addSelectionListener(this); + + mEmoteScroll = new ScrollArea(mEmotes); + mEmoteScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + + place(0, 0, mEmoteScroll, 5, 4); + place(4, 4, mUseButton); + + Layout &layout = getLayout(); + layout.setRowHeight(0, Layout::AUTO_SET); + + mUseButton->setSize(60, mUseButton->getHeight()); + + loadWindowState(); +} + +void EmoteWindow::action(const gcn::ActionEvent &event) +{ + int emote = mEmotes->getSelectedEmote(); + + if (!emote) + return; + + player_node->emote(emote); +} + +int EmoteWindow::getSelectedEmote() const +{ + return mEmotes->getSelectedEmote(); +} diff --git a/src/gui/buddywindow.h b/src/gui/emotewindow.h index 4eed3a2c..8af24a7b 100644 --- a/src/gui/buddywindow.h +++ b/src/gui/emotewindow.h @@ -1,6 +1,6 @@ /* - * The Mana World - * Copyright (C) 2004 The Mana World Development Team + * Extended support for activating emotes + * Copyright (C) 2009 Aethyra Development Team * * This file is part of The Mana World. * @@ -19,38 +19,48 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef BUDDYWINDOW_H -#define BUDDYWINDOW_H +#ifndef EMOTEWINDOW_H +#define EMOTEWINDOW_H #include <guichan/actionlistener.hpp> +#include <guichan/selectionlistener.hpp> #include "window.h" -#include "../guichanfwd.h" - -class BuddyList; +class EmoteContainer; +class TextBox; /** - * Window showing buddy list. + * Emote dialog. * * \ingroup Interface */ -class BuddyWindow : public Window, public gcn::ActionListener +class EmoteWindow : public Window, gcn::ActionListener, + gcn::SelectionListener { public: /** * Constructor. */ - BuddyWindow(); + EmoteWindow(); /** - * Performs action. + * Called when receiving actions from the widgets. */ void action(const gcn::ActionEvent &event); + /** + * Returns the selected item. + */ + int getSelectedEmote() const; + private: - BuddyList *mBuddyList; - gcn::ListBox *mListbox; + EmoteContainer *mEmotes; + + gcn::Button *mUseButton; + gcn::ScrollArea *mEmoteScroll; }; -#endif /* BUDDYWINDOW_H */ +extern EmoteWindow *emoteWindow; + +#endif diff --git a/src/gui/equipmentwindow.cpp b/src/gui/equipmentwindow.cpp index aa6825f2..a2be6b00 100644 --- a/src/gui/equipmentwindow.cpp +++ b/src/gui/equipmentwindow.cpp @@ -19,14 +19,22 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define BOX_WIDTH 36 +#define BOX_HEIGHT 36 + +#include <guichan/font.hpp> + +#include "button.h" #include "equipmentwindow.h" +#include "itempopup.h" +#include "playerbox.h" +#include "viewport.h" #include "../equipment.h" -#include "../inventory.h" -#include "../localplayer.h" #include "../graphics.h" +#include "../inventory.h" #include "../item.h" -#include "../log.h" +#include "../localplayer.h" #include "../resources/iteminfo.h" #include "../resources/resourcemanager.h" @@ -34,19 +42,60 @@ #include "../utils/gettext.h" #include "../utils/tostring.h" -EquipmentWindow::EquipmentWindow(Equipment *equipment): +// Positions of the boxes, 2nd dimension is X and Y respectively. +static const int boxPosition[][2] = { + {50, 208}, // EQUIP_LEGS_SLOT + {8, 123}, // EQUIP_FIGHT1_SLOT + {8, 78}, // EQUIP_GLOVES_SLOT + {129, 168}, // EQUIP_RING2_SLOT + {8, 168}, // EQUIP_RING1_SLOT + {129, 123}, // EQUIP_FIGHT2_SLOT + {90, 208}, // EQUIP_FEET_SLOT + {50, 40}, // EQUIP_CAPE_SLOT + {70, 0}, // EQUIP_HEAD_SLOT + {90, 40}, // EQUIP_TORSO_SLOT + {129, 78} // EQUIP_AMMO_SLOT +}; + +EquipmentWindow::EquipmentWindow(): Window(_("Equipment")), - mEquipment(equipment) + mSelected(-1) { + mItemPopup = new ItemPopup(); + + // Control that shows the Player + mPlayerBox = new PlayerBox; + mPlayerBox->setDimension(gcn::Rectangle(50, 80, 74, 123)); + mPlayerBox->setPlayer(player_node); + setWindowName("Equipment"); setCloseButton(true); - setDefaultSize(5, 230, 200, 140); + setDefaultSize(5, 195, 180, 300); loadWindowState(); + + mUnequip = new Button(_("Unequip"), "unequip", this); + gcn::Rectangle const &area = getChildrenArea(); + mUnequip->setPosition(area.width - mUnequip->getWidth() - 5, + area.height - mUnequip->getHeight() - 5); + + add(mPlayerBox); + add(mUnequip); + + for (int i = EQUIP_LEGS_SLOT; i < EQUIP_VECTOREND; i++) + { + mEquipBox[i].posX = boxPosition[i][0] + getPadding(); + mEquipBox[i].posY = boxPosition[i][1] + getTitleBarHeight(); + } + + mEquipment = player_node->mEquipment.get(); mInventory = player_node->getInventory(); } EquipmentWindow::~EquipmentWindow() { + delete mUnequip; + delete mItemPopup; + delete mPlayerBox; } void EquipmentWindow::draw(gcn::Graphics *graphics) @@ -54,37 +103,148 @@ void EquipmentWindow::draw(gcn::Graphics *graphics) // Draw window graphics Window::draw(graphics); - Item *item; - Image *image; + Item* item; - // Rectangles around items are black - graphics->setColor(gcn::Color(0, 0, 0)); + Graphics *g = static_cast<Graphics*>(graphics); - for (int i = 0; i < EQUIPMENT_SIZE; i++) { - graphics->drawRectangle(gcn::Rectangle(10 + 36 * (i % 4), - 36 * (i / 4) + 25, 32, 32)); + Window::drawChildren(graphics); - if (!(item = mInventory->getItem(mEquipment->getEquipment(i)))) - continue; + for (int i = EQUIP_LEGS_SLOT; i < EQUIP_VECTOREND; i++) + { + item = (i != EQUIP_AMMO_SLOT) ? + mInventory->getItem(mEquipment->getEquipment(i)) : + mInventory->getItem(mEquipment->getArrows()); + if (item) + { + // Draw Item. + Image* image = item->getImage(); + g->drawImage(image, mEquipBox[i].posX, mEquipBox[i].posY); + if (i == EQUIP_AMMO_SLOT) + { + g->setColor(gcn::Color(0, 0, 0)); + graphics->drawText(toString(item->getQuantity()), + mEquipBox[i].posX + (BOX_WIDTH / 2), + mEquipBox[i].posY - getFont()->getHeight(), + gcn::Graphics::CENTER); + } + } - image = item->getImage(); - if (image) + if (i == mSelected) { - static_cast<Graphics*>(graphics)->drawImage( - image, 36 * (i % 4) + 10, 36 * (i / 4) + 25); + // Set color red. + g->setColor(gcn::Color(255, 0, 0)); } + else + { + // Set color black. + g->setColor(gcn::Color(0, 0, 0)); + } + + // Draw box border. + g->drawRectangle(gcn::Rectangle(mEquipBox[i].posX, mEquipBox[i].posY, + BOX_WIDTH, BOX_HEIGHT)); } +} - graphics->drawRectangle(gcn::Rectangle(160, 25, 32, 32)); +void EquipmentWindow::action(const gcn::ActionEvent &event) +{ + if (event.getId() == "unequip" && mSelected > -1) + { + Item* item = (mSelected != EQUIP_AMMO_SLOT) ? + mInventory->getItem(mEquipment->getEquipment(mSelected)) : + mInventory->getItem(mEquipment->getArrows()); + player_node->unequipItem(item); + mSelected = -1; + } +} - if (!(item = mInventory->getItem(mEquipment->getArrows()))) - return; +Item* EquipmentWindow::getItem(const int &x, const int &y) +{ + for (int i = EQUIP_LEGS_SLOT; i < EQUIP_VECTOREND; i++) + { + gcn::Rectangle tRect(mEquipBox[i].posX, mEquipBox[i].posY, + BOX_WIDTH, BOX_HEIGHT); - image = item->getImage(); - if (image) + if (tRect.isPointInRect(x, y)) + { + return (i != EQUIP_AMMO_SLOT) ? + mInventory->getItem(mEquipment->getEquipment(i)) : + mInventory->getItem(mEquipment->getArrows()); + } + } + return NULL; +} + +void EquipmentWindow::mousePressed(gcn::MouseEvent& mouseEvent) +{ + Window::mousePressed(mouseEvent); + + const int x = mouseEvent.getX(); + const int y = mouseEvent.getY(); + + Item* item; + + if (mouseEvent.getButton() == gcn::MouseEvent::LEFT) + { + // Checks if any of the presses were in the equip boxes. + for (int i = EQUIP_LEGS_SLOT; i < EQUIP_VECTOREND; i++) + { + item = (i != EQUIP_AMMO_SLOT) ? + mInventory->getItem(mEquipment->getEquipment(i)) : + mInventory->getItem(mEquipment->getArrows()); + gcn::Rectangle tRect(mEquipBox[i].posX, mEquipBox[i].posY, + BOX_WIDTH, BOX_HEIGHT); + + if (tRect.isPointInRect(x, y)) + { + if (item) + { + mSelected = i; + } + } + } + } + else if (mouseEvent.getButton() == gcn::MouseEvent::RIGHT) + { + item = getItem(x, y); + + if (!item) + return; + + /* Convert relative to the window coordinates to absolute screen + * coordinates. + */ + const int mx = x + getX(); + const int my = y + getY(); + viewport->showPopup(mx, my, item); + } +} + +// Show ItemTooltip +void EquipmentWindow::mouseMoved(gcn::MouseEvent &event) +{ + const int x = event.getX(); + const int y = event.getY(); + + Item* item = getItem(x, y); + + if (item) + { + int mouseX, mouseY; + SDL_GetMouseState(&mouseX, &mouseY); + + mItemPopup->setItem(item->getInfo()); + mItemPopup->setOpaque(false); + mItemPopup->view(x + getX(), y + getY()); + } + else { - static_cast<Graphics*>(graphics)->drawImage(image, 160, 25); + mItemPopup->setVisible(false); } - graphics->drawText(toString(item->getQuantity()), 170, 62, - gcn::Graphics::CENTER); +} + +// Hide ItemTooltip +void EquipmentWindow::mouseExited(gcn::MouseEvent &event) +{ + mItemPopup->setVisible(false); } diff --git a/src/gui/equipmentwindow.h b/src/gui/equipmentwindow.h index e2420134..c491062a 100644 --- a/src/gui/equipmentwindow.h +++ b/src/gui/equipmentwindow.h @@ -22,23 +22,37 @@ #ifndef EQUIPMENTWINDOW_H #define EQUIPMENTWINDOW_H +#include <guichan/actionlistener.hpp> + #include "window.h" -#include "../inventory.h" class Equipment; +class Inventory; +class Item; +class ItemPopup; +class PlayerBox; + +/** + * Equipment box. + */ +struct EquipBox +{ + int posX; + int posY; +}; /** * Equipment dialog. * * \ingroup Interface */ -class EquipmentWindow : public Window +class EquipmentWindow : public Window, public gcn::ActionListener { public: /** * Constructor. */ - EquipmentWindow(Equipment *equipment); + EquipmentWindow(); /** * Destructor. @@ -50,9 +64,43 @@ class EquipmentWindow : public Window */ void draw(gcn::Graphics *graphics); + void action(const gcn::ActionEvent &event); + + void mousePressed(gcn::MouseEvent& mouseEvent); + + enum { + // Equipment rules: + EQUIP_LEGS_SLOT = 0, + EQUIP_FIGHT1_SLOT, + EQUIP_GLOVES_SLOT, + EQUIP_RING2_SLOT, + EQUIP_RING1_SLOT, + EQUIP_FIGHT2_SLOT, + EQUIP_FEET_SLOT, + EQUIP_CAPE_SLOT, + EQUIP_HEAD_SLOT, + EQUIP_TORSO_SLOT, + EQUIP_AMMO_SLOT, + EQUIP_VECTOREND + }; + + private: + void mouseExited(gcn::MouseEvent &event); + void mouseMoved(gcn::MouseEvent &event); + + Item* getItem(const int &x, const int &y); + Equipment *mEquipment; Inventory *mInventory; + gcn::Button *mUnequip; /**< Button for unequipping. */ + EquipBox mEquipBox[EQUIP_VECTOREND]; /**< Equipment Boxes. */ + + ItemPopup *mItemPopup; + + PlayerBox *mPlayerBox; + + int mSelected; /**< Index of selected item. */ }; extern EquipmentWindow *equipmentWindow; diff --git a/src/gui/focushandler.cpp b/src/gui/focushandler.cpp index 3ceed595..b9cfd789 100644 --- a/src/gui/focushandler.cpp +++ b/src/gui/focushandler.cpp @@ -21,13 +21,12 @@ #include "focushandler.h" - void FocusHandler::requestModalFocus(gcn::Widget *widget) { /* If there is another widget with modal focus, remove its modal focus * and put it on the modal widget stack. */ - if (mModalFocusedWidget != NULL && mModalFocusedWidget != widget) + if (mModalFocusedWidget && mModalFocusedWidget != widget) { mModalStack.push_front(mModalFocusedWidget); mModalFocusedWidget = NULL; diff --git a/src/gui/focushandler.h b/src/gui/focushandler.h index 124b5472..b0639bd8 100644 --- a/src/gui/focushandler.h +++ b/src/gui/focushandler.h @@ -26,8 +26,6 @@ #include <guichan/focushandler.hpp> -#include "../guichanfwd.h" - /** * The focus handler. This focus handler does exactly the same as the Guichan * focus handler, but keeps a stack of modal widgets to be able to handle diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 2da451a3..a7946993 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -19,13 +19,12 @@ * 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> #include "focushandler.h" +#include "gui.h" #include "sdlinput.h" #include "truetypefont.h" #include "viewport.h" @@ -39,12 +38,12 @@ #include "../resources/image.h" #include "../resources/imageset.h" -#include "../resources/resourcemanager.h" #include "../resources/imageloader.h" +#include "../resources/resourcemanager.h" // Guichan stuff Gui *gui = 0; -Viewport *viewport = 0; /**< Viewport on the map. */ +Viewport *viewport = 0; /**< Viewport on the map. */ SDLInput *guiInput = 0; // Fonts used in showing hits @@ -52,6 +51,9 @@ gcn::Font *hitRedFont = 0; gcn::Font *hitBlueFont = 0; gcn::Font *hitYellowFont = 0; +// Bolded font +gcn::Font *boldFont = 0; + class GuiConfigListener : public ConfigListener { public: @@ -61,7 +63,8 @@ class GuiConfigListener : public ConfigListener void optionChanged(const std::string &name) { - if (name == "customcursor") { + if (name == "customcursor") + { bool bCustomCursor = config.getValue("customcursor", 1) == 1; mGui->setUseCustomCursor(bCustomCursor); } @@ -105,8 +108,9 @@ Gui::Gui(Graphics *graphics): // Set global font std::string path = resman->getPath("fonts/dejavusans.ttf"); - try { - const int fontSize = config.getValue("fontSize", 11); + try + { + const int fontSize = (int)config.getValue("fontSize", 11); mGuiFont = new TrueTypeFont(path, fontSize); } catch (gcn::Exception e) @@ -115,14 +119,28 @@ Gui::Gui(Graphics *graphics): + e.getMessage()); } + // Set bold font + path = resman->getPath("fonts/dejavusans-bold.ttf"); + try + { + const int fontSize = (int)config.getValue("fontSize", 11); + boldFont = new TrueTypeFont(path, fontSize); + } + catch (gcn::Exception e) + { + logger->error(std::string("Unable to load dejavusans-bold.ttf: ") + + e.getMessage()); + } + gcn::Widget::setGlobalFont(mGuiFont); - // Load hits' colourful fonts - try { + // Load hits' colorful 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 "); } @@ -158,6 +176,7 @@ Gui::~Gui() mMouseCursors->decRef(); delete mGuiFont; + delete boldFont; delete viewport; delete getTop(); @@ -167,12 +186,13 @@ Gui::~Gui() void Gui::logic() { // Fade out mouse cursor after extended inactivity - if (mMouseInactivityTimer < 100 * 15) { + if (mMouseInactivityTimer < 100 * 15) + { ++mMouseInactivityTimer; mMouseCursorAlpha = std::min(1.0f, mMouseCursorAlpha + 0.05f); - } else { - mMouseCursorAlpha = std::max(0.0f, mMouseCursorAlpha - 0.005f); } + else + mMouseCursorAlpha = std::max(0.0f, mMouseCursorAlpha - 0.005f); gcn::Gui::logic(); } @@ -217,9 +237,8 @@ void Gui::setUseCustomCursor(bool customCursor) mMouseCursors = resman->getImageSet("graphics/gui/mouse.png", 40, 40); - if (!mMouseCursors) { + if (!mMouseCursors) logger->error("Unable to load mouse cursors."); - } } else { @@ -227,7 +246,8 @@ void Gui::setUseCustomCursor(bool customCursor) SDL_ShowCursor(SDL_ENABLE); // Unload the mouse cursor - if (mMouseCursors) { + if (mMouseCursors) + { mMouseCursors->decRef(); mMouseCursors = NULL; } diff --git a/src/gui/gui.h b/src/gui/gui.h index 95cd5815..5c0c24f7 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 SDLInput; class Viewport; @@ -115,7 +115,6 @@ class Gui : public gcn::Gui }; extern Gui *gui; /**< The GUI system */ -extern Viewport *viewport; /**< The viewport */ extern SDLInput *guiInput; /**< GUI input */ /** @@ -125,4 +124,9 @@ extern gcn::Font *hitRedFont; extern gcn::Font *hitBlueFont; extern gcn::Font *hitYellowFont; +/** + * Bolded text font + */ +extern gcn::Font *boldFont; + #endif diff --git a/src/gui/help.cpp b/src/gui/help.cpp index 390cb44e..ece2dce4 100644 --- a/src/gui/help.cpp +++ b/src/gui/help.cpp @@ -19,12 +19,13 @@ * 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 "widgets/layout.h" + #include "../resources/resourcemanager.h" #include "../utils/gettext.h" @@ -32,8 +33,11 @@ HelpWindow::HelpWindow(): Window(_("Help")) { + setMinWidth(300); + setMinHeight(250); setContentSize(455, 350); - setWindowName("Help"); + setWindowName(_("Help")); + setResizable(true); mBrowserBox = new BrowserBox(); mBrowserBox->setOpaque(false); @@ -48,8 +52,11 @@ HelpWindow::HelpWindow(): mBrowserBox->setLinkHandler(this); - add(mScrollArea); - add(okButton); + place(0, 0, mScrollArea, 5, 3).setPadding(3); + place(4, 3, okButton); + + Layout &layout = getLayout(); + layout.setRowHeight(0, Layout::AUTO_SET); setLocationRelativeTo(getParent()); } diff --git a/src/gui/help.h b/src/gui/help.h index e3d9246d..98e3aa67 100644 --- a/src/gui/help.h +++ b/src/gui/help.h @@ -24,10 +24,8 @@ #include <guichan/actionlistener.hpp> -#include "window.h" #include "linkhandler.h" - -#include "../guichanfwd.h" +#include "window.h" class BrowserBox; diff --git a/src/gui/inttextfield.cpp b/src/gui/inttextfield.cpp index eb61c4d7..fcbe938d 100644 --- a/src/gui/inttextfield.cpp +++ b/src/gui/inttextfield.cpp @@ -20,7 +20,6 @@ */ #include "inttextfield.h" - #include "sdlinput.h" #include "../utils/tostring.h" diff --git a/src/gui/inttextfield.h b/src/gui/inttextfield.h index 4dfef8e1..add78084 100644 --- a/src/gui/inttextfield.h +++ b/src/gui/inttextfield.h @@ -58,8 +58,7 @@ class IntTextField : public TextField /** * Responds to key presses. */ - void - keyPressed(gcn::KeyEvent &event); + void keyPressed(gcn::KeyEvent &event); private: int mMin; /**< Minimum value */ diff --git a/src/gui/inventorywindow.cpp b/src/gui/inventorywindow.cpp index 31ebb86e..af3b29a2 100644 --- a/src/gui/inventorywindow.cpp +++ b/src/gui/inventorywindow.cpp @@ -19,72 +19,101 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "inventorywindow.h" - #include <string> +#include <guichan/font.hpp> #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 "progressbar.h" #include "scrollarea.h" #include "viewport.h" +#include "widgets/layout.h" + #include "../inventory.h" #include "../item.h" -#include "../localplayer.h" #include "../resources/iteminfo.h" #include "../utils/gettext.h" #include "../utils/strprintf.h" +#include "../utils/tostring.h" -InventoryWindow::InventoryWindow(): - Window(_("Inventory")) +InventoryWindow::InventoryWindow(int invSize): + Window(_("Inventory")), + mMaxSlots(invSize), + mItemDesc(false) { - setWindowName("Inventory"); + setWindowName(_("Inventory")); 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, 200); + setDefaultSize(115, 25, 375, 300); - mUseButton = new Button(_("Use"), "use", this); + std::string longestUseString = getFont()->getWidth(_("Equip")) > + getFont()->getWidth(_("Use")) ? + _("Equip") : _("Use"); + + if (getFont()->getWidth(longestUseString) < + getFont()->getWidth(_("Unequip"))) + { + longestUseString = _("Unequip"); + } + + mUseButton = new Button(longestUseString, "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:"); - 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); - - add(mUseButton); - add(mDropButton); - add(mInvenScroll); - add(mItemNameLabel); - add(mItemDescriptionLabel); - add(mItemEffectLabel); - add(mWeightLabel); - add(mInvenSlotLabel); - - mUseButton->setSize(60, mUseButton->getHeight()); + mTotalWeight = toString(player_node->mTotalWeight); + mMaxWeight = toString(player_node->mMaxWeight); + mUsedSlots = toString(player_node->getInventory()->getNumberOfSlotsUsed()); + + mSlotsLabel = new gcn::Label(_("Slots: ")); + mWeightLabel = new gcn::Label(_("Weight: ")); + + mSlotsBar = new ProgressBar(1.0f, 100, 20, 225, 200, 25); + mWeightBar = new ProgressBar(1.0f, 100, 20, 0, 0, 255); + + setMinHeight(130); + setMinWidth(mWeightLabel->getWidth() + mSlotsLabel->getWidth() + 280); + + place(0, 0, mWeightLabel).setPadding(3); + place(1, 0, mWeightBar, 3); + place(4, 0, mSlotsLabel).setPadding(3); + place(5, 0, mSlotsBar, 2); + place(0, 1, mInvenScroll, 7, 4); + place(5, 5, mDropButton); + place(6, 5, mUseButton); + + Layout &layout = getLayout(); + layout.setRowHeight(0, mDropButton->getHeight()); loadWindowState(); + setLocationRelativeTo(getParent()); +} + +InventoryWindow::~InventoryWindow() +{ + delete mWeightBar; + delete mSlotsBar; + delete mUseButton; + delete mDropButton; + delete mItems; + delete mWeightLabel; + delete mSlotsLabel; + delete mInvenScroll; } void InventoryWindow::logic() @@ -95,15 +124,39 @@ void InventoryWindow::logic() // redesign of InventoryWindow and ItemContainer probably. updateButtons(); - // Update weight information - mWeightLabel->setCaption(strprintf(_("Weight: %d / %d"), - player_node->mTotalWeight, - player_node->mMaxWeight)); + if ((mMaxWeight != toString(player_node->mMaxWeight)) || + mTotalWeight != toString(player_node->mTotalWeight) || + mUsedSlots != toString(player_node->getInventory()->getNumberOfSlotsUsed())) + { + mTotalWeight = toString(player_node->mTotalWeight); + mMaxWeight = toString(player_node->mMaxWeight); + mUsedSlots = toString(player_node->getInventory()->getNumberOfSlotsUsed()); + + // Weight Bar coloration + if (int(player_node->mTotalWeight) < int(player_node->mMaxWeight / 3)) + { + mWeightBar->setColor(0, 0, 255); // Blue + } + else if (int(player_node->mTotalWeight) < + int((player_node->mMaxWeight / 3) * 2)) + { + mWeightBar->setColor(255, 255, 0); // Yellow + } + else + { + mWeightBar->setColor(255, 0, 0); // Red + } + + // Adjust progress bars + mSlotsBar->setProgress((float) + player_node->getInventory()->getNumberOfSlotsUsed() / mMaxSlots); + mWeightBar->setProgress((float) player_node->mTotalWeight / + player_node->mMaxWeight); - // Update number of items in inventory - mInvenSlotLabel->setCaption(strprintf(_("Slots used: %d / %d"), - player_node->getInventory()->getNumberOfSlotsUsed(), - player_node->getInventory()->getInventorySize())); + mSlotsBar->setText(strprintf("%s/%d", mUsedSlots.c_str(), mMaxSlots)); + mWeightBar->setText(strprintf("%sg/%sg", mTotalWeight.c_str(), + mMaxWeight.c_str())); + } } void InventoryWindow::action(const gcn::ActionEvent &event) @@ -113,55 +166,30 @@ void InventoryWindow::action(const gcn::ActionEvent &event) if (!item) return; - if (event.getId() == "use") { - if (item->isEquipment()) { - if (item->isEquipped()) { + if (event.getId() == "use") + { + if (item->isEquipment()) + { + if (item->isEquipped()) player_node->unequipItem(item); - } - else { + else player_node->equipItem(item); - } } - else { + else player_node->useItem(item); - } } else if (event.getId() == "drop") { - if (item->getQuantity() == 1) { + if (item->getQuantity() == 1) player_node->dropItem(item, 1); - } - else { + else + { // Choose amount of items to drop new ItemAmountWindow(AMOUNT_ITEM_DROP, this, item); } } } -void InventoryWindow::valueChanged(const gcn::SelectionEvent &event) -{ - const Item *item = mItems->getSelectedItem(); - - // Update name, effect and description - if (!item) - { - mItemNameLabel->setCaption(strprintf(_("Name: %s"), "")); - mItemEffectLabel->setCaption(strprintf(_("Effect: %s"), "")); - mItemDescriptionLabel->setCaption(strprintf(_("Description: %s"), "")); - } - else - { - const ItemInfo& itemInfo = item->getInfo(); - mItemNameLabel->setCaption( - strprintf(_("Name: %s"), itemInfo.getName().c_str())); - mItemEffectLabel->setCaption( - strprintf(_("Effect: %s"), itemInfo.getEffect().c_str())); - mItemDescriptionLabel->setCaption( - strprintf(_("Description: %s"), - itemInfo.getDescription().c_str())); - } -} - void InventoryWindow::mouseClicked(gcn::MouseEvent &event) { Window::mouseClicked(event); @@ -182,55 +210,19 @@ void InventoryWindow::mouseClicked(gcn::MouseEvent &event) } } -void InventoryWindow::widgetResized(const gcn::Event &event) -{ - Window::widgetResized(event); - - const gcn::Rectangle &area = getChildrenArea(); - const int width = area.width; - const int height = area.height; - - // Adjust widgets - mUseButton->setPosition(8, height - 8 - mUseButton->getHeight()); - mDropButton->setPosition(8 + mUseButton->getWidth() + 5, - mUseButton->getY()); - - mItemNameLabel->setDimension(gcn::Rectangle(8, - mUseButton->getY() - 5 - mItemNameLabel->getHeight(), - width - 16, - mItemNameLabel->getHeight())); - mItemEffectLabel->setDimension(gcn::Rectangle(8, - mItemNameLabel->getY() - 5 - mItemEffectLabel->getHeight(), - width - 16, - mItemEffectLabel->getHeight())); - mItemDescriptionLabel->setDimension(gcn::Rectangle(8, - mItemEffectLabel->getY() - 5 - mItemDescriptionLabel->getHeight(), - width - 16, - mItemDescriptionLabel->getHeight())); - - mInvenScroll->setSize(width - 16, - mItemDescriptionLabel->getY() - mWeightLabel->getHeight() - 18); - - mWeightLabel->setWidth(width - 16); - mInvenSlotLabel->setWidth(width - 16); -} - void InventoryWindow::updateButtons() { const Item *selectedItem = mItems->getSelectedItem(); if (selectedItem && selectedItem->isEquipment()) { - if (selectedItem->isEquipped()) { + if (selectedItem->isEquipped()) mUseButton->setCaption(_("Unequip")); - } - else { + else mUseButton->setCaption(_("Equip")); - } } - else { + else mUseButton->setCaption(_("Use")); - } mUseButton->setEnabled(selectedItem != 0); mDropButton->setEnabled(selectedItem != 0); diff --git a/src/gui/inventorywindow.h b/src/gui/inventorywindow.h index 402ab0d2..78d30461 100644 --- a/src/gui/inventorywindow.h +++ b/src/gui/inventorywindow.h @@ -27,10 +27,12 @@ #include "window.h" -#include "../guichanfwd.h" +#include "../localplayer.h" class Item; class ItemContainer; +class ProgressBar; +class TextBox; /** * Inventory dialog. @@ -38,13 +40,18 @@ class ItemContainer; * \ingroup Interface */ class InventoryWindow : public Window, gcn::ActionListener, - gcn::SelectionListener + gcn::SelectionListener { public: /** * Constructor. */ - InventoryWindow(); + InventoryWindow(int invSize = (INVENTORY_SIZE - 2)); + + /** + * Destructor. + */ + ~InventoryWindow(); /** * Logic (updates buttons and weight information). @@ -61,30 +68,30 @@ class InventoryWindow : public Window, gcn::ActionListener, */ Item* getSelectedItem() const; - /** - * Updates labels to currently selected item. - */ - void valueChanged(const gcn::SelectionEvent &event); - void mouseClicked(gcn::MouseEvent &event); - /** - * Called whenever the widget changes size. - */ - void widgetResized(const gcn::Event &event); - private: void updateButtons(); /**< Updates button states. */ ItemContainer *mItems; + std::string mWeight; + std::string mSlots; + std::string mUsedSlots; + std::string mTotalWeight; + std::string mMaxWeight; gcn::Button *mUseButton, *mDropButton; gcn::ScrollArea *mInvenScroll; - gcn::Label *mItemNameLabel; - gcn::Label *mItemDescriptionLabel; - gcn::Label *mItemEffectLabel; + gcn::Label *mWeightLabel; - gcn::Label *mInvenSlotLabel; + gcn::Label *mSlotsLabel; + + ProgressBar *mWeightBar; + ProgressBar *mSlotsBar; + + int mMaxSlots; + + bool mItemDesc; }; extern InventoryWindow *inventoryWindow; diff --git a/src/gui/item_amount.cpp b/src/gui/item_amount.cpp index 7ef3d71b..92be3d6e 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 "inttextfield.h" +#include "item_amount.h" #include "slider.h" #include "trade.h" @@ -71,7 +70,8 @@ ItemAmountWindow::ItemAmountWindow(int usage, Window *parent, Item *item): resetAmount(); - switch (usage) { + switch (usage) + { case AMOUNT_TRADE_ADD: setCaption(_("Select amount of items to trade.")); okButton->setActionEventId("AddTrade"); diff --git a/src/gui/item_amount.h b/src/gui/item_amount.h index d8eedadb..618d7d51 100644 --- a/src/gui/item_amount.h +++ b/src/gui/item_amount.h @@ -22,14 +22,10 @@ #ifndef ITEM_AMOUNT_WINDOW_H #define ITEM_AMOUNT_WINDOW_H -#include <iosfwd> - #include <guichan/actionlistener.hpp> #include "window.h" -#include "../guichanfwd.h" - class IntTextField; class Item; diff --git a/src/gui/itemcontainer.cpp b/src/gui/itemcontainer.cpp index a0885279..0beb5cfb 100644 --- a/src/gui/itemcontainer.cpp +++ b/src/gui/itemcontainer.cpp @@ -21,17 +21,21 @@ #include "itemcontainer.h" +#include "itempopup.h" + #include <guichan/mouseinput.hpp> #include <guichan/selectionlistener.hpp> +#include <SDL_mouse.h> + #include "../graphics.h" #include "../inventory.h" #include "../item.h" #include "../itemshortcut.h" +#include "../localplayer.h" #include "../log.h" #include "../resources/image.h" -#include "../resources/iteminfo.h" #include "../resources/resourcemanager.h" #include "../utils/tostring.h" @@ -41,11 +45,14 @@ 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) { + mItemPopup = new ItemPopup(); + ResourceManager *resman = ResourceManager::getInstance(); mSelImg = resman->getImage("graphics/gui/selection.png"); @@ -60,6 +67,7 @@ ItemContainer::ItemContainer(Inventory *inventory): ItemContainer::~ItemContainer() { mSelImg->decRef(); + delete mItemPopup; } void ItemContainer::logic() @@ -86,10 +94,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); @@ -115,6 +124,7 @@ void ItemContainer::draw(gcn::Graphics *graphics) } // Draw item caption + graphics->setFont(getFont()); graphics->setColor(gcn::Color(0, 0, 0)); graphics->drawText( (item->isEquipped() ? "Eq." : toString(item->getQuantity())), @@ -138,6 +148,7 @@ void ItemContainer::recalculateHeight() const int rows = (mMaxItems / cols) + (mMaxItems % cols > 0 ? 1 : 0); const int height = rows * gridHeight + 8; + if (height != getHeight()) setHeight(height); } @@ -159,8 +170,8 @@ void ItemContainer::selectNone() void ItemContainer::refindSelectedItem() { - if (mSelectedItemIndex != NO_ITEM) { - + if (mSelectedItemIndex != NO_ITEM) + { if (mInventory->getItem(mSelectedItemIndex) && mInventory->getItem(mSelectedItemIndex)->getId() == mLastSelectedItemId) return; // we're already fine @@ -170,7 +181,8 @@ void ItemContainer::refindSelectedItem() for (int i = 0; i <= mMaxItems + 1; i++) if (mInventory->getItem(i) && - mInventory->getItem(i)->getId() == mLastSelectedItemId) { + mInventory->getItem(i)->getId() == mLastSelectedItemId) + { mSelectedItemIndex = i; return; } @@ -179,14 +191,16 @@ 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; @@ -218,14 +232,14 @@ void ItemContainer::distributeValueChangedEvent() void ItemContainer::mousePressed(gcn::MouseEvent &event) { - int button = event.getButton(); + const int button = event.getButton(); if (button == gcn::MouseEvent::LEFT || button == gcn::MouseEvent::RIGHT) { 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); @@ -236,3 +250,38 @@ void ItemContainer::mousePressed(gcn::MouseEvent &event) itemShortcut->setItemSelected(item->getId()); } } + +// Show ItemTooltip +void ItemContainer::mouseMoved(gcn::MouseEvent &event) +{ + Item *item = mInventory->getItem(getSlotIndex(event.getX(), event.getY())); + + if (item) + { + int mouseX, mouseY; + SDL_GetMouseState(&mouseX, &mouseY); + + mItemPopup->setItem(item->getInfo()); + mItemPopup->setOpaque(false); + mItemPopup->view(mouseX, mouseY); + } + else + { + mItemPopup->setVisible(false); + } +} + +// Hide ItemTooltip +void ItemContainer::mouseExited(gcn::MouseEvent &event) +{ + mItemPopup->setVisible(false); +} + +int ItemContainer::getSlotIndex(const int posX, const int posY) const +{ + int columns = getWidth() / gridWidth; + int index = posX / gridWidth + ((posY / gridHeight) * columns) + mOffset; + + return (index); +} + diff --git a/src/gui/itemcontainer.h b/src/gui/itemcontainer.h index a40237af..71fcc5d0 100644 --- a/src/gui/itemcontainer.h +++ b/src/gui/itemcontainer.h @@ -22,15 +22,16 @@ #ifndef ITEMCONTAINER_H #define ITEMCONTAINER_H +#include <list> + #include <guichan/mouselistener.hpp> #include <guichan/widget.hpp> #include <guichan/widgetlistener.hpp> -#include <list> - class Image; class Inventory; class Item; +class ItemPopup; namespace gcn { class SelectionListener; @@ -41,14 +42,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. @@ -104,7 +106,11 @@ class ItemContainer : public gcn::Widget, public gcn::MouseListener, } private: + void mouseExited(gcn::MouseEvent &event); + void mouseMoved(gcn::MouseEvent &event); + /** + * Sets the currently selected item. Invalid (e.g., negative) indices set `no item'. */ void setSelectedItemIndex(int index); @@ -124,12 +130,24 @@ class ItemContainer : public gcn::Widget, public gcn::MouseListener, */ void distributeValueChangedEvent(); + /** + * Gets the slot index based on the cursor position. + * + * @param posX The X Coordinate position. + * @param posY The Y Coordinate position. + * @return The slot index on success, -1 on failure. + */ + int getSlotIndex(const int posX, const int posY) const; + Inventory *mInventory; Image *mSelImg; - int mSelectedItemIndex; - int mLastSelectedItemId; // last selected item ID. If we lose the item, find again by ID. + int mSelectedItemIndex; + int mLastSelectedItemId; // last selected item ID. If we lose the item, find again by ID. int mMaxItems; + int mOffset; + + ItemPopup *mItemPopup; std::list<gcn::SelectionListener*> mListeners; diff --git a/src/gui/itemlinkhandler.cpp b/src/gui/itemlinkhandler.cpp new file mode 100644 index 00000000..97c0b94f --- /dev/null +++ b/src/gui/itemlinkhandler.cpp @@ -0,0 +1,63 @@ +/* + * The Mana World + * Copyright 2009 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 <sstream> +#include <string> + +#include <SDL_mouse.h> + +#include "itemlinkhandler.h" +#include "itempopup.h" + +#include "../resources/iteminfo.h" +#include "../resources/itemdb.h" + +ItemLinkHandler::ItemLinkHandler() +{ + mItemPopup = new ItemPopup; +} + +ItemLinkHandler::~ItemLinkHandler() +{ + delete mItemPopup; +} + +void ItemLinkHandler::handleLink(const std::string &link) +{ + int id = 0; + std::stringstream stream; + stream << link; + stream >> id; + if (id > 0) + { + const ItemInfo &iteminfo = ItemDB::get(id); + int mouseX, mouseY; + + SDL_GetMouseState(&mouseX, &mouseY); + + mItemPopup->setItem(iteminfo); + + if (mItemPopup->isVisible()) + mItemPopup->setVisible(false); + else + mItemPopup->view(mouseX, mouseY); + } +} diff --git a/src/gui/itemlinkhandler.h b/src/gui/itemlinkhandler.h new file mode 100644 index 00000000..cd6fd900 --- /dev/null +++ b/src/gui/itemlinkhandler.h @@ -0,0 +1,40 @@ +/* + * The Mana World + * Copyright 2009 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 ITEM_LINK_HANDLER_H_ +#define ITEM_LINK_HANDLER_H_ + +#include "linkhandler.h" + +class ItemPopup; + +class ItemLinkHandler : public LinkHandler +{ + public: + ItemLinkHandler(); + ~ItemLinkHandler(); + void handleLink(const std::string &link); + + private: + ItemPopup *mItemPopup; +}; + +#endif diff --git a/src/gui/itempopup.cpp b/src/gui/itempopup.cpp new file mode 100644 index 00000000..4c117f0a --- /dev/null +++ b/src/gui/itempopup.cpp @@ -0,0 +1,216 @@ +/* + * The Mana World + * Copyright (C) 2008 The Legend of Mazzeroth Development Team + * Copyright (C) 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 <guichan/font.hpp> + +#include <guichan/widgets/label.hpp> + +#include "gui.h" +#include "itempopup.h" +#include "scrollarea.h" +#include "textbox.h" +#include "windowcontainer.h" + +#include "widgets/layout.h" + +#include "../resources/iteminfo.h" + +#include "../utils/gettext.h" +#include "../utils/tostring.h" + +ItemPopup::ItemPopup(): + Window() +{ + setResizable(false); + setShowTitle(false); + setTitleBarHeight(0); + + // Item Name + mItemName = new gcn::Label("Label"); + mItemName->setFont(boldFont); + mItemName->setPosition(2, 2); + + // 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, getFont()->getHeight())); + mItemDescScroll->setOpaque(false); + mItemDescScroll->setPosition(2, getFont()->getHeight()); + + // 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, getFont()->getHeight())); + mItemEffectScroll->setOpaque(false); + mItemEffectScroll->setPosition(2, (2 * getFont()->getHeight()) + 5); + + // Item Weight + mItemWeight = new TextBox(); + mItemWeight->setEditable(false); + mItemWeightScroll = new ScrollArea(mItemWeight); + + mItemWeightScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mItemWeightScroll->setVerticalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mItemWeightScroll->setDimension(gcn::Rectangle(0, 0, 196, getFont()->getHeight())); + mItemWeightScroll->setOpaque(false); + mItemWeightScroll->setPosition(2, (3 * getFont()->getHeight()) + 10); + + add(mItemName); + add(mItemDescScroll); + add(mItemEffectScroll); + add(mItemWeightScroll); + + setLocationRelativeTo(getParent()); +} + +ItemPopup::~ItemPopup() +{ + delete mItemName; + delete mItemDesc; + delete mItemDescScroll; + delete mItemEffect; + delete mItemEffectScroll; + delete mItemWeight; + delete mItemWeightScroll; +} + +void ItemPopup::setItem(const ItemInfo &item) +{ + mItemName->setCaption(item.getName()); + mItemName->setForegroundColor(getColor(item.getType())); + mItemName->setWidth(boldFont->getWidth(item.getName())); + mItemDesc->setTextWrapped(item.getDescription(), 196); + mItemEffect->setTextWrapped(item.getEffect(), 196); + mItemWeight->setTextWrapped(_("Weight: ") + toString(item.getWeight()) + + _(" grams"), 196); + + int minWidth = mItemName->getWidth(); + + if (mItemDesc->getMinWidth() > minWidth) + minWidth = mItemDesc->getMinWidth(); + if (mItemEffect->getMinWidth() > minWidth) + minWidth = mItemEffect->getMinWidth(); + if (mItemWeight->getMinWidth() > minWidth) + minWidth = mItemWeight->getMinWidth(); + + minWidth += 8; + setWidth(minWidth); + + int numRowsDesc = mItemDesc->getNumberOfRows(); + int numRowsEffect = mItemEffect->getNumberOfRows(); + int numRowsWeight = mItemWeight->getNumberOfRows(); + + mItemDescScroll->setDimension(gcn::Rectangle(2, 0, minWidth, + numRowsDesc * getFont()->getHeight())); + + mItemEffectScroll->setDimension(gcn::Rectangle(2, 0, minWidth, + numRowsEffect * getFont()->getHeight())); + + mItemWeightScroll->setDimension(gcn::Rectangle(2, 0, minWidth, + numRowsWeight * getFont()->getHeight())); + + if (item.getEffect().empty()) + { + setContentSize(minWidth, (numRowsDesc * getFont()->getHeight() + + (3 * getFont()->getHeight()))); + + mItemWeightScroll->setPosition(2, + (numRowsDesc * getFont()->getHeight()) + + (2 * getFont()->getHeight())); + } + else + { + setContentSize(minWidth, (numRowsDesc * getFont()->getHeight()) + + (numRowsEffect * getFont()->getHeight()) + + (3 * getFont()->getHeight())); + + mItemWeightScroll->setPosition(2, + (numRowsDesc * getFont()->getHeight()) + + (numRowsEffect * getFont()->getHeight()) + + (2 * getFont()->getHeight())); + } + + mItemDescScroll->setPosition(2, 20); + mItemEffectScroll->setPosition(2, (numRowsDesc * getFont()->getHeight()) + + (2 * getFont()->getHeight())); +} + +gcn::Color ItemPopup::getColor(const std::string& type) +{ + gcn::Color color; + + if (type.compare("generic") == 0) + color = 0x21a5b1; + else if (type.compare("equip-head") == 0) + color = 0x527fa4; + else if (type.compare("usable") == 0) + color = 0x268d24; + else if (type.compare("equip-torso") == 0) + color = 0xd12aa4; + else if (type.compare("equip-1hand") == 0) + color = 0xf42a2a; + else if (type.compare("equip-legs") == 0) + color = 0x699900; + else if (type.compare("equip-feet") == 0) + color = 0xaa1d48; + else if (type.compare("equip-2hand") == 0) + color = 0xf46d0e; + else if (type.compare("equip-shield") == 0) + color = 0x9c2424; + else if (type.compare("equip-ring") == 0) + color = 0x0000ff; + else if (type.compare("equip-arms") == 0) + color = 0x9c24e8; + else if (type.compare("equip-ammo") == 0) + color = 0x8b6311; + else + color = 0x000000; + + return color; +} + +unsigned int ItemPopup::getNumRows() +{ + return mItemDesc->getNumberOfRows() + mItemEffect->getNumberOfRows() + + mItemWeight->getNumberOfRows(); +} + +void ItemPopup::view(int x, int y) +{ + if (windowContainer->getWidth() < (x + getWidth() + 5)) + x = windowContainer->getWidth() - getWidth(); + if ((y - getHeight() - 10) < 0) + y = 0; + else + y = y - getHeight() - 10; + setPosition(x, y); + setVisible(true); + requestMoveToTop(); +} diff --git a/src/gui/itempopup.h b/src/gui/itempopup.h new file mode 100644 index 00000000..c820e3a0 --- /dev/null +++ b/src/gui/itempopup.h @@ -0,0 +1,54 @@ +/* + * The Mana World + * Copyright (C) 2008 The Legend of Mazzeroth Development Team + * Copyright (C) 2008 The Mana World Development Team + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef ITEMPOPUP_H +#define ITEMPOPUP_H + +#include "window.h" + +class ItemInfo; +class ScrollArea; +class TextBox; + +class ItemPopup : public Window +{ + public: + ItemPopup(); + ~ItemPopup(); + + void setItem(const ItemInfo &item); + unsigned int getNumRows(); + void view(int x, int y); + + private: + gcn::Label *mItemName; + TextBox *mItemDesc; + TextBox *mItemEffect; + TextBox *mItemWeight; + ScrollArea *mItemDescScroll; + ScrollArea *mItemEffectScroll; + ScrollArea *mItemWeightScroll; + + gcn::Color getColor(const std::string& type); +}; + +#endif // ITEMPOPUP_H diff --git a/src/gui/itemshortcutcontainer.cpp b/src/gui/itemshortcutcontainer.cpp index b2f70348..42e3b853 100644 --- a/src/gui/itemshortcutcontainer.cpp +++ b/src/gui/itemshortcutcontainer.cpp @@ -18,15 +18,19 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <SDL_mouse.h> #include "itemshortcutcontainer.h" +#include "itempopup.h" +#include "viewport.h" -#include "../localplayer.h" +#include "../configuration.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" @@ -34,30 +38,33 @@ #include "../utils/tostring.h" ItemShortcutContainer::ItemShortcutContainer(): - mGridWidth(1), - mGridHeight(1), + ShortcutContainer(), mItemClicked(false), mItemMoved(NULL) { addMouseListener(this); addWidgetListener(this); + mItemPopup = new ItemPopup(); + ResourceManager *resman = ResourceManager::getInstance(); mBackgroundImg = resman->getImage("graphics/gui/item_shortcut_bgr.png"); mMaxItems = itemShortcut->getItemCount(); - mBoxHeight = 42; - mBoxWidth = 36; + mBackgroundImg->setAlpha(config.getValue("guialpha", 0.8)); + + mBoxHeight = mBackgroundImg->getHeight(); + mBoxWidth = mBackgroundImg->getWidth(); } ItemShortcutContainer::~ItemShortcutContainer() { mBackgroundImg->decRef(); + delete mItemPopup; } -void -ItemShortcutContainer::logic() +void ItemShortcutContainer::logic() { gcn::Widget::logic(); @@ -70,8 +77,7 @@ ItemShortcutContainer::logic() } } -void -ItemShortcutContainer::draw(gcn::Graphics *graphics) +void ItemShortcutContainer::draw(gcn::Graphics *graphics) { Graphics *g = static_cast<Graphics*>(graphics); @@ -87,7 +93,8 @@ ItemShortcutContainer::draw(gcn::Graphics *graphics) // Draw item keyboard shortcut. const char *key = SDL_GetKeyName( - (SDLKey) keyboard.getKeyValue(keyboard.KEY_SHORTCUT_0 + i)); + (SDLKey) keyboard.getKeyValue(keyboard.KEY_SHORTCUT_1 + i)); + graphics->setColor(0x000000); g->drawText(key, itemX + 2, itemY + 2, gcn::Graphics::LEFT); if (itemShortcut->getItem(i) < 0) @@ -97,6 +104,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 = @@ -127,37 +136,30 @@ ItemShortcutContainer::draw(gcn::Graphics *graphics) gcn::Graphics::CENTER); } } -} -void ItemShortcutContainer::widgetResized(const gcn::Event &event) -{ - mGridWidth = getWidth() / mBoxWidth; - if (mGridWidth < 1) { - mGridWidth = 1; - } - - setHeight((mMaxItems / mGridWidth + - (mMaxItems % mGridWidth > 0 ? 1 : 0)) * mBoxHeight); - - mGridHeight = getHeight() / mBoxHeight; - if (mGridHeight < 1) { - mGridHeight = 1; + if (config.getValue("guialpha", 0.8) != mAlpha) + { + mBackgroundImg->setAlpha(config.getValue("guialpha", 0.8)); } } -void -ItemShortcutContainer::mouseDragged(gcn::MouseEvent &event) +void ItemShortcutContainer::mouseDragged(gcn::MouseEvent &event) { - if (event.getButton() == gcn::MouseEvent::LEFT) { - if (!mItemMoved && mItemClicked) { + if (event.getButton() == gcn::MouseEvent::LEFT) + { + if (!mItemMoved && mItemClicked) + { const int index = getIndexFromGrid(event.getX(), event.getY()); - if (index == -1) { - return; - } const int itemId = itemShortcut->getItem(index); + + if (index == -1) + return; + if (itemId < 0) return; + Item *item = player_node->getInventory()->findItem(itemId); + if (item) { mItemMoved = item; @@ -171,39 +173,56 @@ ItemShortcutContainer::mouseDragged(gcn::MouseEvent &event) } } -void -ItemShortcutContainer::mousePressed(gcn::MouseEvent &event) +void ItemShortcutContainer::mousePressed(gcn::MouseEvent &event) { const int index = getIndexFromGrid(event.getX(), event.getY()); - if (index == -1) { + if (index == -1) return; - } - // Stores the selected item if theirs one. - if (itemShortcut->isItemSelected()) { - itemShortcut->setItem(index); - itemShortcut->setItemSelected(-1); + if (event.getButton() == gcn::MouseEvent::LEFT) + { + + // Stores the selected item if theirs one. + if (itemShortcut->isItemSelected()) + { + itemShortcut->setItem(index); + itemShortcut->setItemSelected(-1); + } + else if (itemShortcut->getItem(index)) + mItemClicked = true; } - else if (itemShortcut->getItem(index)) { - mItemClicked = true; + else if (event.getButton() == gcn::MouseEvent::RIGHT) + { + Item *item = player_node->getInventory()-> + findItem(itemShortcut->getItem(index)); + + if (!item) + return; + + /* Convert relative to the window coordinates to absolute screen + * coordinates. + */ + int mx, my; + SDL_GetMouseState(&mx, &my); + viewport->showPopup(mx, my, item); } } -void -ItemShortcutContainer::mouseReleased(gcn::MouseEvent &event) +void ItemShortcutContainer::mouseReleased(gcn::MouseEvent &event) { if (event.getButton() == gcn::MouseEvent::LEFT) { if (itemShortcut->isItemSelected()) - { itemShortcut->setItemSelected(-1); - } + const int index = getIndexFromGrid(event.getX(), event.getY()); - if (index == -1) { + if (index == -1) + { mItemMoved = NULL; return; } - if (mItemMoved) { + if (mItemMoved) + { itemShortcut->setItems(index, mItemMoved->getId()); mItemMoved = NULL; } @@ -211,25 +230,43 @@ ItemShortcutContainer::mouseReleased(gcn::MouseEvent &event) { itemShortcut->useItem(index); } - if (mItemClicked) { + if (mItemClicked) mItemClicked = false; - } } } -int -ItemShortcutContainer::getIndexFromGrid(int pointX, int pointY) const +// Show ItemTooltip +void ItemShortcutContainer::mouseMoved(gcn::MouseEvent &event) { - const gcn::Rectangle tRect = gcn::Rectangle( - 0, 0, mGridWidth * mBoxWidth, mGridHeight * mBoxHeight); - if (!tRect.isPointInRect(pointX, pointY)) { - return -1; + const int index = getIndexFromGrid(event.getX(), event.getY()); + const int itemId = itemShortcut->getItem(index); + + if (index == -1) + return; + + if (itemId < 0) + return; + + Item *item = player_node->getInventory()->findItem(itemId); + + if (item) + { + int mouseX, mouseY; + SDL_GetMouseState(&mouseX, &mouseY); + + mItemPopup->setItem(item->getInfo()); + mItemPopup->setOpaque(false); + mItemPopup->view(mouseX, mouseY); } - const int index = ((pointY / mBoxHeight) * mGridWidth) + - pointX / mBoxWidth; - if (index >= mMaxItems) + else { - return -1; + mItemPopup->setVisible(false); } - return index; } + +// Hide ItemTooltip +void ItemShortcutContainer::mouseExited(gcn::MouseEvent &event) +{ + mItemPopup->setVisible(false); +} + diff --git a/src/gui/itemshortcutcontainer.h b/src/gui/itemshortcutcontainer.h index cdaf6713..22d94ec2 100644 --- a/src/gui/itemshortcutcontainer.h +++ b/src/gui/itemshortcutcontainer.h @@ -23,20 +23,19 @@ #define ITEMSHORTCUTCONTAINER_H #include <guichan/mouselistener.hpp> -#include <guichan/widget.hpp> -#include <guichan/widgetlistener.hpp> + +#include "shortcutcontainer.h" class Image; class Item; +class ItemPopup; /** * An item shortcut container. Used to quickly use items. * * \ingroup GUI */ -class ItemShortcutContainer : public gcn::Widget, - public gcn::WidgetListener, - public gcn::MouseListener +class ItemShortcutContainer : public ShortcutContainer { public: /** @@ -60,12 +59,6 @@ class ItemShortcutContainer : public gcn::Widget, void draw(gcn::Graphics *graphics); /** - * Invoked when a widget changes its size. This is used to determine - * the new height of the container. - */ - void widgetResized(const gcn::Event &event); - - /** * Handles mouse when dragged. */ void mouseDragged(gcn::MouseEvent &event); @@ -80,34 +73,14 @@ class ItemShortcutContainer : public gcn::Widget, */ void mouseReleased(gcn::MouseEvent &event); - int getMaxItems() - { return mMaxItems; } - - int getBoxWidth() - { return mBoxWidth; } - - int getBoxHeight() - { return mBoxHeight; } - private: - /** - * Gets the index from the grid provided the point is in an item box. - * - * @param pointX X coordinate of the point. - * @param pointY Y coordinate of the point. - * @return index on success, -1 on failure. - */ - int getIndexFromGrid(int pointX, int pointY) const; + void mouseExited(gcn::MouseEvent &event); + void mouseMoved(gcn::MouseEvent &event); - Image *mBackgroundImg; - - int mMaxItems; - int mBoxWidth; - int mBoxHeight; - int mCursorPosX, mCursorPosY; - int mGridWidth, mGridHeight; bool mItemClicked; Item *mItemMoved; + + ItemPopup *mItemPopup; }; #endif diff --git a/src/gui/listbox.cpp b/src/gui/listbox.cpp index b72c64cf..74d0b9ad 100644 --- a/src/gui/listbox.cpp +++ b/src/gui/listbox.cpp @@ -19,12 +19,16 @@ * 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 "color.h" +#include "listbox.h" + +#include "../configuration.h" + +float ListBox::mAlpha = config.getValue("guialpha", 0.8); ListBox::ListBox(gcn::ListModel *listModel): gcn::ListBox(listModel) @@ -36,29 +40,35 @@ void ListBox::draw(gcn::Graphics *graphics) if (!mListModel) return; - graphics->setColor(gcn::Color(110, 160, 255)); + if (config.getValue("guialpha", 0.8) != mAlpha) + mAlpha = config.getValue("guialpha", 0.8); + + bool valid; + const int red = (textColor->getColor('H', valid) >> 16) & 0xFF; + const int green = (textColor->getColor('H', valid) >> 8) & 0xFF; + const int blue = textColor->getColor('H', valid) & 0xFF; + const int alpha = (int)(mAlpha * 255.0f); + + graphics->setColor(gcn::Color(red, green, blue, alpha)); graphics->setFont(getFont()); - int fontHeight = getFont()->getHeight(); + const int fontHeight = getFont()->getHeight(); // Draw rectangle below the selected list element - if (mSelected >= 0) { + if (mSelected >= 0) graphics->fillRectangle(gcn::Rectangle(0, fontHeight * mSelected, getWidth(), fontHeight)); - } // Draw the list elements - graphics->setColor(gcn::Color(0, 0, 0)); - for (int i = 0, y = 0; - i < mListModel->getNumberOfElements(); + graphics->setColor(gcn::Color(0, 0, 0, 255)); + for (int i = 0, y = 0; i < mListModel->getNumberOfElements(); ++i, y += fontHeight) { graphics->drawText(mListModel->getElementAt(i), 1, y); } } -void -ListBox::mouseDragged(gcn::MouseEvent &event) +void ListBox::mouseDragged(gcn::MouseEvent &event) { // Pretend mouse is pressed continuously while dragged. Causes list // selection to be updated as is default in many GUIs. diff --git a/src/gui/listbox.h b/src/gui/listbox.h index 934ea82e..12fcb955 100644 --- a/src/gui/listbox.h +++ b/src/gui/listbox.h @@ -47,6 +47,9 @@ class ListBox : public gcn::ListBox void draw(gcn::Graphics *graphics); void mouseDragged(gcn::MouseEvent &event); + + private: + static float mAlpha; }; #endif diff --git a/src/gui/login.cpp b/src/gui/login.cpp index 2b87f6df..90ceab1a 100644 --- a/src/gui/login.cpp +++ b/src/gui/login.cpp @@ -19,24 +19,31 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "login.h" - -#include <string> - #include <guichan/widgets/label.hpp> -#include "../main.h" -#include "../logindata.h" - #include "button.h" #include "checkbox.h" +#include "listbox.h" +#include "login.h" #include "ok_dialog.h" #include "passwordfield.h" +#include "scrollarea.h" #include "textfield.h" +#include "widgets/dropdown.h" #include "widgets/layout.h" +#include "../main.h" +#include "../logindata.h" +#include "../configuration.h" + #include "../utils/gettext.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) @@ -44,36 +51,62 @@ LoginDialog::LoginDialog(LoginData *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("server.themanaworld.org"); + std::vector<std::string> dfltPort; + dfltPort.push_back("6901"); + mServerList = new DropDownList("MostRecent00", dfltServer, dfltPort, + MAX_SERVER_LIST_SIZE); + mServerListBox = new 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); + mServerDropDown->setOpaque(false); + mKeepCheck = new CheckBox(_("Remember Username"), mLoginData->remember); - mOkButton = new Button(_("Ok"), "ok", this); + mOkButton = new Button(_("OK"), "ok", this); mCancelButton = new Button(_("Cancel"), "cancel", this); mRegisterButton = new Button(_("Register"), "register", this); 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); place(0, 0, userLabel); place(0, 1, passLabel); place(0, 2, serverLabel); - place(1, 0, mUserField, 3).setPadding(2); - place(1, 1, mPassField, 3).setPadding(2); - place(1, 2, mServerField, 3).setPadding(2); - place(0, 3, mKeepCheck, 4); - place(0, 4, mRegisterButton).setHAlign(LayoutCell::LEFT); - place(2, 4, mOkButton); - place(3, 4, mCancelButton); + place(0, 3, portLabel); + place(0, 4, dropdownLabel); + place(1, 0, mUserField, 3).setPadding(1); + place(1, 1, mPassField, 3).setPadding(1); + place(1, 2, mServerField, 3).setPadding(1); + place(1, 3, mPortField, 3).setPadding(1); + place(1, 4, mServerDropDown, 3).setPadding(1); + place(0, 5, mKeepCheck, 4); + place(0, 6, mRegisterButton).setHAlign(LayoutCell::LEFT); + place(2, 6, mCancelButton); + place(3, 6, mOkButton); reflowLayout(250, 0); setLocationRelativeTo(getParent()); @@ -90,6 +123,9 @@ LoginDialog::LoginDialog(LoginData *loginData): LoginDialog::~LoginDialog() { + delete mServerList; + delete mServerListBox; + delete mServerScrollArea; } void LoginDialog::action(const gcn::ActionEvent &event) @@ -97,6 +133,7 @@ void 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(); @@ -104,9 +141,15 @@ void 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; @@ -115,6 +158,14 @@ void 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(); @@ -132,5 +183,138 @@ bool 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.empty()) + { + 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.empty()) + { + 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.empty()) // 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.empty()) + { + mServers.push_back(server); + mPorts.push_back(port); + } + } + if (mServers.empty()) + { + 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 7dd59a96..c0d6e755 100644 --- a/src/gui/login.h +++ b/src/gui/login.h @@ -22,14 +22,18 @@ #ifndef LOGIN_H #define LOGIN_H -#include <iosfwd> +#include <string> +#include <vector> + #include <guichan/actionlistener.hpp> #include <guichan/keylistener.hpp> +#include <guichan/listmodel.hpp> #include "window.h" -#include "../guichanfwd.h" +class DropDown; class LoginData; +class ScrollArea; /** * The login dialog. @@ -67,18 +71,65 @@ class LoginDialog : public Window, public gcn::ActionListener, * Returns whether submit can be enabled. This is true in the login * state, when all necessary fields have some text. */ - bool - canSubmit(); + 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; + gcn::ScrollArea *mServerScrollArea; }; #endif diff --git a/src/gui/menuwindow.cpp b/src/gui/menuwindow.cpp index e19cc3e8..0dcc999f 100644 --- a/src/gui/menuwindow.cpp +++ b/src/gui/menuwindow.cpp @@ -19,23 +19,24 @@ * 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" #include "../utils/gettext.h" -extern Window *setupWindow; -extern Window *inventoryWindow; +extern Window *chatWindow; extern Window *equipmentWindow; +extern Window *inventoryWindow; +extern Window *itemShortcutWindow; +extern Window *emoteWindow; +extern Window *setupWindow; extern Window *skillDialog; extern Window *statusWindow; -extern Window *itemShortcutWindow; namespace { struct MenuWindowListener : public gcn::ActionListener @@ -58,12 +59,14 @@ MenuWindow::MenuWindow(): // Buttons static const char *buttonNames[] = { - N_("Status"), - N_("Equipment"), - N_("Inventory"), - N_("Skills"), - N_("Shortcut"), - N_("Setup"), + _("Chat"), + _("Status"), + _("Equipment"), + _("Inventory"), + _("Skills"), + _("Shortcut"), + _("Emote"), + _("Setup"), 0 }; int x = 0, h = 0; @@ -91,27 +94,35 @@ 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; } - else if (event.getId() == "Equipment") + else if (event.getId() == _("Equipment")) { window = equipmentWindow; } - else if (event.getId() == "Inventory") + else if (event.getId() == _("Inventory")) { window = inventoryWindow; } - else if (event.getId() == "Skills") + else if (event.getId() == _("Skills")) { window = skillDialog; } - else if (event.getId() == "Shortcut") + else if (event.getId() == _("Shortcut")) { window = itemShortcutWindow; } - else if (event.getId() == "Setup") + else if (event.getId() == _("Emote")) + { + window = emoteWindow; + } + else if (event.getId() == _("Setup")) { window = setupWindow; } diff --git a/src/gui/menuwindow.h b/src/gui/menuwindow.h index 9b784c35..9bb54e29 100644 --- a/src/gui/menuwindow.h +++ b/src/gui/menuwindow.h @@ -24,8 +24,6 @@ #include "window.h" -#include "../guichanfwd.h" - /** * The Button Menu. * diff --git a/src/gui/minimap.cpp b/src/gui/minimap.cpp index f5c0c1e6..ccadf3e1 100644 --- a/src/gui/minimap.cpp +++ b/src/gui/minimap.cpp @@ -19,10 +19,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <guichan/font.hpp> + #include "minimap.h" #include "../being.h" #include "../beingmanager.h" +#include "../configuration.h" #include "../graphics.h" #include "../localplayer.h" @@ -30,12 +33,18 @@ #include "../utils/gettext.h" +bool Minimap::mShow = true; + Minimap::Minimap(): Window(_("MiniMap")), - mMapImage(NULL) + mMapImage(NULL), + mProportion(0.5) { - setWindowName("MiniMap"); + setWindowName(_("MiniMap")); + mShow = config.getValue(getWindowName() + "Show", true); setDefaultSize(5, 25, 100, 100); + setResizable(true); + loadWindowState(); } @@ -43,6 +52,8 @@ Minimap::~Minimap() { if (mMapImage) mMapImage->decRef(); + + config.setValue(getWindowName() + "Show", mShow); } void Minimap::setMapImage(Image *img) @@ -53,13 +64,59 @@ void Minimap::setMapImage(Image *img) mMapImage = img; if (mMapImage) - mMapImage->setAlpha(0.7); + { + const int offsetX = 2 * getPadding(); + const int offsetY = getTitleBarHeight() + getPadding(); + const int titleWidth = getFont()->getWidth(getCaption()) + 15; + const int mapWidth = mMapImage->getWidth() < 100 ? + mMapImage->getWidth() + offsetX : 100; + const int mapHeight = mMapImage->getHeight() < 100 ? + mMapImage->getHeight() + offsetY : 100; + + setMinWidth(mapWidth > titleWidth ? mapWidth : titleWidth); + setMinHeight(mapHeight); + setMaxWidth(mMapImage->getWidth() > titleWidth ? + mMapImage->getWidth() + offsetX : titleWidth); + setMaxHeight(mMapImage->getHeight() + offsetY); + + // Make sure the window is within the minimum and maximum boundaries + // TODO: Shouldn't this be happening automatically within the Window + // class? + if (getMinWidth() > getWidth()) + setWidth(getMinWidth()); + else if (getMaxWidth() < getWidth()) + setWidth(getMaxWidth()); + if (getMinHeight() > getHeight()) + setHeight(getMinHeight()); + else if (getMaxHeight() < getHeight()) + setHeight(getMaxHeight()); + + setContentSize(getWidth() - offsetX, getHeight() - offsetY); + setDefaultSize(getX(), getY(), getWidth(), getHeight()); + resetToDefaultSize(); + + setVisible(mShow); + } + else + { + setVisible(false); + } +} + +void Minimap::toggle() +{ + mShow = !mShow; } void Minimap::draw(gcn::Graphics *graphics) { + setVisible(mShow); + Window::draw(graphics); + if (!mShow) + return; + const gcn::Rectangle a = getChildrenArea(); graphics->pushClipArea(a); @@ -72,8 +129,8 @@ 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 = (int) (((a.width) / 2) - (player_node->mX * mProportion)); + mapOriginY = (int) (((a.height) / 2) - (player_node->mY * mProportion)); const int minOriginX = a.width - mMapImage->getWidth(); const int minOriginY = a.height - mMapImage->getHeight(); @@ -87,6 +144,7 @@ void Minimap::draw(gcn::Graphics *graphics) if (mapOriginY > 0) mapOriginY = 0; } + static_cast<Graphics*>(graphics)-> drawImage(mMapImage, mapOriginX, mapOriginY); } @@ -122,10 +180,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 67f5eb18..3ce0aacd 100644 --- a/src/gui/minimap.h +++ b/src/gui/minimap.h @@ -50,12 +50,24 @@ 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; } + + /** + * Toggles the displaying of the minimap. + */ + void toggle(); + + /** * Draws the minimap. */ void draw(gcn::Graphics *graphics); private: Image *mMapImage; + float mProportion; + static bool mShow; }; extern Minimap *minimap; diff --git a/src/gui/ministatus.cpp b/src/gui/ministatus.cpp index e613a745..7058d572 100644 --- a/src/gui/ministatus.cpp +++ b/src/gui/ministatus.cpp @@ -19,14 +19,14 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "ministatus.h" - #include "gui.h" +#include "ministatus.h" #include "progressbar.h" -#include "../localplayer.h" +#include "../animatedsprite.h" #include "../configuration.h" #include "../graphics.h" +#include "../localplayer.h" #include "../utils/tostring.h" @@ -90,29 +90,27 @@ void MiniStatusWindow::update() mHpBar->setColor(0, 171, 34); // Green } + float xp = (float) player_node->getXp() / player_node->mXpForNextLevel; + + if (xp != xp) xp = 0.0f; // check for NaN + if (xp < 0.0f) xp = 0.0f; // make sure the experience isn't negative (uninitialized pointer most likely) + if (xp > 1.0f) xp = 1.0f; + 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); + mXpBar->setProgress(xp); // Update labels mHpBar->setText(toString(player_node->mHp)); mMpBar->setText(toString(player_node->mMp)); std::stringstream updatedText; - updatedText << (int) ( - (float) player_node->getXp() / - player_node->mXpForNextLevel * 100) << "%"; + updatedText << (float) ((int) (xp * 10000.0f)) / 100.0f << "%"; // Displays the number of monsters to next lvl // (disabled for now but interesting idea) /* - if(config.getValue("xpBarMonsterCounterExp", 0)!=0) + if (config.getValue("xpBarMonsterCounterExp", 0)!=0) { updatedText << " | " << (int)(((float)player_node->mXpForNextLevel - (float)player_node->mXp) diff --git a/src/gui/ministatus.h b/src/gui/ministatus.h index c6a36a98..f262a2a0 100644 --- a/src/gui/ministatus.h +++ b/src/gui/ministatus.h @@ -22,14 +22,11 @@ #ifndef MINISTATUS_H #define MINISTATUS_H -#include <iosfwd> -#include <vector> - #include "window.h" -#include "../guichanfwd.h" -#include "../animatedsprite.h" +#include <vector> +class AnimatedSprite; class ProgressBar; /** diff --git a/src/gui/npc_text.cpp b/src/gui/npc_text.cpp index 6ad698bc..b4313b70 100644 --- a/src/gui/npc_text.cpp +++ b/src/gui/npc_text.cpp @@ -19,13 +19,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "npc_text.h" - -#include <string> - -#include "browserbox.h" #include "button.h" +#include "npc_text.h" #include "scrollarea.h" +#include "textbox.h" + +#include "widgets/layout.h" #include "../npc.h" @@ -39,66 +38,55 @@ NpcTextDialog::NpcTextDialog(): setMinWidth(200); setMinHeight(150); - mBrowserBox = new BrowserBox(BrowserBox::AUTO_WRAP); - mBrowserBox->setOpaque(true); + setDefaultSize(0, 0, 260, 200); + + mTextBox = new TextBox; + mTextBox->setEditable(false); + mTextBox->setOpaque(false); - scrollArea = new ScrollArea(mBrowserBox); + scrollArea = new ScrollArea(mTextBox); okButton = new Button(_("OK"), "ok", this); - 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())); - okButton->setPosition( - 260 - 5 - okButton->getWidth(), - 175 - 5 - okButton->getHeight()); - add(scrollArea); - add(okButton); + place(0, 0, scrollArea, 5).setPadding(3); + place(4, 1, okButton); - setLocationRelativeTo(getParent()); -} + Layout &layout = getLayout(); + layout.setRowHeight(0, Layout::AUTO_SET); -void NpcTextDialog::clearText() -{ - mBrowserBox->clearRows(); + loadWindowState(); + setLocationRelativeTo(getParent()); } void NpcTextDialog::setText(const std::string &text) { - mBrowserBox->clearRows(); - mBrowserBox->addRow(text); + mText = text; + mTextBox->setTextWrapped(mText, scrollArea->getWidth() - 15); } void NpcTextDialog::addText(const std::string &text) { - mBrowserBox->addRow(text); -} - -void NpcTextDialog::widgetResized(const gcn::Event &event) -{ - Window::widgetResized(event); - - const gcn::Rectangle &area = getChildrenArea(); - const int width = area.width; - const int height = area.height; - - scrollArea->setDimension(gcn::Rectangle( - 5, 5, width - 10, height - 15 - okButton->getHeight())); - okButton->setPosition( - width - 5 - okButton->getWidth(), - height - 5 - okButton->getHeight()); + setText(mText + text + "\n"); } void NpcTextDialog::action(const gcn::ActionEvent &event) { if (event.getId() == "ok") { - clearText(); + setText(""); setVisible(false); if (current_npc) current_npc->nextDialog(); current_npc = 0; } } + +void NpcTextDialog::widgetResized(const gcn::Event &event) +{ + Window::widgetResized(event); + + setText(mText); +} + diff --git a/src/gui/npc_text.h b/src/gui/npc_text.h index c881467a..a07aa04f 100644 --- a/src/gui/npc_text.h +++ b/src/gui/npc_text.h @@ -22,12 +22,13 @@ #ifndef NPC_TEXT_H #define NPC_TEXT_H -#include <iosfwd> +#include <string> + #include <guichan/actionlistener.hpp> #include "window.h" -class BrowserBox; +class TextBox; /** * The npc text dialog. @@ -45,13 +46,6 @@ class NpcTextDialog : public Window, public gcn::ActionListener NpcTextDialog(); /** - * Called when resizing the window. - * - * @param event The calling event - */ - void widgetResized(const gcn::Event &event); - - /** * Called when receiving actions from the widgets. */ void action(const gcn::ActionEvent &event); @@ -76,10 +70,19 @@ class NpcTextDialog : public Window, public gcn::ActionListener */ void addText(const std::string &string); + /** + * Called when resizing the window. + * + * @param event The calling event + */ + void widgetResized(const gcn::Event &event); + private: gcn::Button *okButton; gcn::ScrollArea *scrollArea; - BrowserBox *mBrowserBox; + TextBox *mTextBox; + + std::string mText; }; #endif // NPC_TEXT_H diff --git a/src/gui/npcintegerdialog.cpp b/src/gui/npcintegerdialog.cpp index ec91d736..c58fc460 100644 --- a/src/gui/npcintegerdialog.cpp +++ b/src/gui/npcintegerdialog.cpp @@ -19,27 +19,23 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "npcintegerdialog.h" - -#include <limits> -#include <sstream> - #include "button.h" #include "inttextfield.h" +#include "npcintegerdialog.h" + +#include "widgets/layout.h" #include "../npc.h" #include "../utils/gettext.h" -#include "../utils/tostring.h" - -#include "widgets/layout.h" NpcIntegerDialog::NpcIntegerDialog(): Window(_("NPC Number Request")) { + mValueField = new IntTextField(); + mDecButton = new Button("-", "decvalue", this); mIncButton = new Button("+", "incvalue", this); - mValueField = new IntTextField(); okButton = new Button(_("OK"), "ok", this); cancelButton = new Button(_("Cancel"), "cancel", this); resetButton = new Button(_("Reset"), "reset", this); @@ -61,9 +57,6 @@ NpcIntegerDialog::NpcIntegerDialog(): reflowLayout(175, 0); setLocationRelativeTo(getParent()); - - mValueField->setActionEventId("valuefield"); - mValueField->addKeyListener(this); } void NpcIntegerDialog::setRange(const int min, const int max) @@ -107,5 +100,16 @@ void NpcIntegerDialog::action(const gcn::ActionEvent &event) setVisible(false); current_npc->integerInput(mValueField->getValue()); current_npc = 0; + mValueField->reset(); } } + +bool NpcIntegerDialog::isInputFocused() +{ + return mValueField->isFocused(); +} + +void NpcIntegerDialog::requestFocus() +{ + mValueField->requestFocus(); +} diff --git a/src/gui/npcintegerdialog.h b/src/gui/npcintegerdialog.h index 983c46fe..10ec60b9 100644 --- a/src/gui/npcintegerdialog.h +++ b/src/gui/npcintegerdialog.h @@ -22,16 +22,10 @@ #ifndef GUI_NPCINTEGERDIALOG_H #define GUI_NPCINTEGERDIALOG_H -#include <iosfwd> -#include <vector> - #include <guichan/actionlistener.hpp> -#include <guichan/keylistener.hpp> #include "window.h" -#include "../guichanfwd.h" - class IntTextField; /** @@ -39,8 +33,7 @@ class IntTextField; * * \ingroup Interface */ -class NpcIntegerDialog : public Window, public gcn::ActionListener, - public gcn::KeyListener +class NpcIntegerDialog : public Window, public gcn::ActionListener { public: /** @@ -68,6 +61,16 @@ class NpcIntegerDialog : public Window, public gcn::ActionListener, */ void setRange(const int min, const int max); + /** + * Checks whether NpcStringDialog is Focused or not. + */ + bool isInputFocused(); + + /** + * Requests the textfield to take focus for input. + */ + void requestFocus(); + private: gcn::Button *mDecButton; gcn::Button *mIncButton; diff --git a/src/gui/npclistdialog.cpp b/src/gui/npclistdialog.cpp index 4b05df5a..7d8a362a 100644 --- a/src/gui/npclistdialog.cpp +++ b/src/gui/npclistdialog.cpp @@ -19,13 +19,14 @@ * 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 "scrollarea.h" + +#include "widgets/layout.h" #include "../npc.h" @@ -39,30 +40,25 @@ NpcListDialog::NpcListDialog(): setMinWidth(200); setMinHeight(150); + setDefaultSize(0, 0, 260, 200); + mItemList = new ListBox(this); + mItemList->setWrappingEnabled(true); scrollArea = new ScrollArea(mItemList); okButton = new Button(_("OK"), "ok", this); cancelButton = new Button(_("Cancel"), "cancel", this); setContentSize(260, 175); scrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); - scrollArea->setDimension(gcn::Rectangle( - 5, 5, 250, 160 - okButton->getHeight())); - cancelButton->setPosition( - 260 - 5 - cancelButton->getWidth(), - 175 - 5 - cancelButton->getHeight()); - okButton->setPosition( - cancelButton->getX() - 5 - okButton->getWidth(), - cancelButton->getY()); - - mItemList->setActionEventId("item"); - mItemList->addActionListener(this); + place(0, 0, scrollArea, 5).setPadding(3); + place(3, 1, okButton); + place(4, 1, cancelButton); - add(scrollArea); - add(okButton); - add(cancelButton); + Layout &layout = getLayout(); + layout.setRowHeight(0, Layout::AUTO_SET); + loadWindowState(); setLocationRelativeTo(getParent()); } @@ -90,24 +86,6 @@ void NpcListDialog::reset() mItems.clear(); } -void NpcListDialog::widgetResized(const gcn::Event &event) -{ - Window::widgetResized(event); - - const gcn::Rectangle &area = getChildrenArea(); - const int width = area.width; - const int height = area.height; - - scrollArea->setDimension(gcn::Rectangle( - 5, 5, width - 10, height - 15 - okButton->getHeight())); - cancelButton->setPosition( - width - 5 - cancelButton->getWidth(), - height - 5 - cancelButton->getHeight()); - okButton->setPosition( - cancelButton->getX() - 5 - okButton->getWidth(), - cancelButton->getY()); -} - void NpcListDialog::action(const gcn::ActionEvent &event) { int choice = 0; diff --git a/src/gui/npclistdialog.h b/src/gui/npclistdialog.h index 2ae4aae3..30167a5e 100644 --- a/src/gui/npclistdialog.h +++ b/src/gui/npclistdialog.h @@ -22,7 +22,6 @@ #ifndef GUI_NPCLISTDIALOG_H #define GUI_NPCLISTDIALOG_H -#include <iosfwd> #include <vector> #include <guichan/actionlistener.hpp> @@ -30,8 +29,6 @@ #include "window.h" -#include "../guichanfwd.h" - /** * The npc list dialog. * @@ -49,13 +46,6 @@ class NpcListDialog : public Window, public gcn::ActionListener, NpcListDialog(); /** - * Called when resizing the window - * - * @param event The calling event - */ - void widgetResized(const gcn::Event &event); - - /** * Called when receiving actions from the widgets. */ void action(const gcn::ActionEvent &event); diff --git a/src/gui/npcstringdialog.cpp b/src/gui/npcstringdialog.cpp index 1c92d620..718c416f 100644 --- a/src/gui/npcstringdialog.cpp +++ b/src/gui/npcstringdialog.cpp @@ -19,18 +19,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "npcstringdialog.h" - -#include <limits> -#include <sstream> - #include "button.h" +#include "npcstringdialog.h" #include "textfield.h" #include "../npc.h" #include "../utils/gettext.h" -#include "../utils/tostring.h" #include "widgets/layout.h" @@ -38,12 +33,13 @@ NpcStringDialog::NpcStringDialog(): Window(_("NPC Text Request")) { mValueField = new TextField(""); + okButton = new Button(_("OK"), "ok", this); cancelButton = new Button(_("Cancel"), "cancel", this); place(0, 0, mValueField, 3); - place(1, 1, okButton); - place(2, 1, cancelButton); + place(1, 1, cancelButton); + place(2, 1, okButton); reflowLayout(175, 0); setLocationRelativeTo(getParent()); @@ -69,9 +65,15 @@ void NpcStringDialog::action(const gcn::ActionEvent &event) setVisible(false); current_npc->stringInput(mValueField->getText()); current_npc = 0; + mValueField->setText(""); } bool NpcStringDialog::isInputFocused() { return mValueField->isFocused(); } + +void NpcStringDialog::requestFocus() +{ + mValueField->requestFocus(); +} diff --git a/src/gui/npcstringdialog.h b/src/gui/npcstringdialog.h index 5aea2de0..1933e0f1 100644 --- a/src/gui/npcstringdialog.h +++ b/src/gui/npcstringdialog.h @@ -22,16 +22,10 @@ #ifndef GUI_NPCSTRINGDIALOG_H #define GUI_NPCSTRINGDIALOG_H -#include <iosfwd> -#include <vector> - #include <guichan/actionlistener.hpp> -#include <guichan/keylistener.hpp> #include "window.h" -#include "../guichanfwd.h" - /** * The npc integer input dialog. * @@ -69,6 +63,11 @@ class NpcStringDialog : public Window, public gcn::ActionListener */ bool isInputFocused(); + /** + * Requests the textfield to take focus for input. + */ + void requestFocus(); + private: gcn::TextField *mValueField; gcn::Button *okButton; diff --git a/src/gui/ok_dialog.cpp b/src/gui/ok_dialog.cpp index a2134d5d..2c67e71f 100644 --- a/src/gui/ok_dialog.cpp +++ b/src/gui/ok_dialog.cpp @@ -19,10 +19,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "ok_dialog.h" -#include "textbox.h" +#include <guichan/font.hpp> + #include "button.h" +#include "ok_dialog.h" #include "scrollarea.h" +#include "textbox.h" #include "../utils/gettext.h" @@ -30,25 +32,44 @@ 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->setTextWrapped(msg, 260); - 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()); @@ -56,6 +77,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 44dc6bb9..3a438513 100644 --- a/src/gui/ok_dialog.h +++ b/src/gui/ok_dialog.h @@ -26,6 +26,9 @@ #include "window.h" +class ScrollArea; +class TextBox; + /** * An 'Ok' button dialog. * @@ -41,10 +44,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.cpp b/src/gui/passwordfield.cpp index 09b6abda..345ee1c3 100644 --- a/src/gui/passwordfield.cpp +++ b/src/gui/passwordfield.cpp @@ -21,8 +21,6 @@ #include "passwordfield.h" -#include <string> - PasswordField::PasswordField(const std::string& text): TextField(text) { diff --git a/src/gui/passwordfield.h b/src/gui/passwordfield.h index d6082e3f..42f8d187 100644 --- a/src/gui/passwordfield.h +++ b/src/gui/passwordfield.h @@ -22,6 +22,8 @@ #ifndef PASSWORDFIELD_H #define PASSWORDFIELD_H +#include <string> + #include "textfield.h" /** @@ -29,7 +31,8 @@ * * \ingroup GUI */ -class PasswordField : public TextField { +class PasswordField : public TextField +{ public: /** * Constructor, initializes the password field with the given string. diff --git a/src/gui/playerbox.cpp b/src/gui/playerbox.cpp index 8f698df5..f99ce6ef 100644 --- a/src/gui/playerbox.cpp +++ b/src/gui/playerbox.cpp @@ -19,12 +19,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <algorithm> - #include "playerbox.h" -#include "../player.h" +#include "../animatedsprite.h" +#include "../configuration.h" #include "../graphics.h" +#include "../player.h" #include "../resources/image.h" #include "../resources/resourcemanager.h" @@ -32,6 +32,7 @@ #include "../utils/dtor.h" int PlayerBox::instances = 0; +float PlayerBox::mAlpha = config.getValue("guialpha", 0.8); ImageRect PlayerBox::background; PlayerBox::PlayerBox(const Player *player): @@ -54,6 +55,7 @@ PlayerBox::PlayerBox(const Player *player): bggridx[x], bggridy[y], bggridx[x + 1] - bggridx[x] + 1, bggridy[y + 1] - bggridy[y] + 1); + background.grid[a]->setAlpha(config.getValue("guialpha", 0.8)); a++; } } @@ -83,7 +85,21 @@ void PlayerBox::draw(gcn::Graphics *graphics) bs = getFrameSize(); x = getWidth() / 2 - 16 + bs; y = getHeight() / 2 + bs; - mPlayer->draw(static_cast<Graphics*>(graphics), x, y); + for (int i = 0; i < Being::VECTOREND_SPRITE; i++) + { + if (mPlayer->getSprite(i)) + { + mPlayer->getSprite(i)->draw(static_cast<Graphics*>(graphics), x, y); + } + } + } + + if (config.getValue("guialpha", 0.8) != mAlpha) + { + for (int a = 0; a < 9; a++) + { + background.grid[a]->setAlpha(config.getValue("guialpha", 0.8)); + } } } diff --git a/src/gui/playerbox.h b/src/gui/playerbox.h index d66db4d4..7c08defd 100644 --- a/src/gui/playerbox.h +++ b/src/gui/playerbox.h @@ -51,8 +51,7 @@ class PlayerBox : public gcn::ScrollArea * player to <code>NULL</code> causes the box not to draw any * character. */ - void - setPlayer(const Player *player) { mPlayer = player; } + void setPlayer(const Player *player) { mPlayer = player; } /** * Draws the scroll area. @@ -67,6 +66,7 @@ class PlayerBox : public gcn::ScrollArea private: const Player *mPlayer; /**< The character used for display */ + static float mAlpha; static int instances; static ImageRect background; }; diff --git a/src/gui/popupmenu.cpp b/src/gui/popupmenu.cpp index 06a2ad87..3503d0ea 100644 --- a/src/gui/popupmenu.cpp +++ b/src/gui/popupmenu.cpp @@ -19,16 +19,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "popupmenu.h" - #include <cassert> -#include <iostream> - -#include <guichan/focushandler.hpp> #include "browserbox.h" +#include "chat.h" #include "inventorywindow.h" #include "item_amount.h" +#include "popupmenu.h" #include "windowcontainer.h" #include "../being.h" @@ -39,7 +36,9 @@ #include "../npc.h" #include "../player_relations.h" -#include "../resources/iteminfo.h" +#include "../net/messageout.h" +#include "../net/protocol.h" + #include "../resources/itemdb.h" #include "../utils/gettext.h" @@ -77,34 +76,35 @@ void PopupMenu::showPopup(int x, int y, Being *being) // Players can be traded with. Later also attack, follow and // add as buddy will be options in this menu. const std::string &name = being->getName(); - mBrowserBox->addRow( - strprintf(_("@@trade|Trade With %s@@"), name.c_str())); - mBrowserBox->addRow( - strprintf(_("@@attack|Attack %s@@"), name.c_str())); + mBrowserBox->addRow(strprintf(_("@@trade|Trade With %s@@"), name.c_str())); + mBrowserBox->addRow(strprintf(_("@@attack|Attack %s@@"), name.c_str())); 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; + case PlayerRelation::NEUTRAL: + mBrowserBox->addRow(strprintf(_("@@friend|Befriend %s@@"), name.c_str())); + + case PlayerRelation::FRIEND: + mBrowserBox->addRow(strprintf(_("@@disregard|Disregard %s@@"), name.c_str())); + mBrowserBox->addRow(strprintf(_("@@ignore|Ignore %s@@"), name.c_str())); + break; + + case PlayerRelation::DISREGARDED: + mBrowserBox->addRow(strprintf(_("@@unignore|Un-Ignore %s@@"), name.c_str())); + mBrowserBox->addRow(strprintf(_("@@ignore|Completely ignore %s@@"), name.c_str())); + break; + + case PlayerRelation::IGNORED: + mBrowserBox->addRow(strprintf(_("@@unignore|Un-Ignore %s@@"), name.c_str())); + break; } - //mBrowserBox->addRow("@@follow|Follow " + name + "@@"); - //mBrowserBox->addRow("@@buddy|Add " + name + " to Buddy List@@"); + //mBrowserBox->addRow(_("@@follow|Follow ") + name + "@@"); + //mBrowserBox->addRow(_("@@buddy|Add ") + name + " to Buddy List@@"); + + mBrowserBox->addRow("##3---"); + mBrowserBox->addRow(strprintf(_("@@party-invite|Invite %s to party@@"), name.c_str())); } break; @@ -148,7 +148,7 @@ void PopupMenu::handleLink(const std::string& link) // Talk To action if (link == "talk" && - being != NULL && + being && being->getType() == Being::NPC && current_npc == 0) { @@ -157,7 +157,7 @@ void PopupMenu::handleLink(const std::string& link) // Trade action else if (link == "trade" && - being != NULL && + being && being->getType() == Being::PLAYER) { player_node->trade(being); @@ -166,35 +166,35 @@ void PopupMenu::handleLink(const std::string& link) // Attack action else if (link == "attack" && - being != NULL && + being && being->getType() == Being::PLAYER) { player_node->attack(being, true); } else if (link == "unignore" && - being != NULL && + being && being->getType() == Being::PLAYER) { player_relations.setRelation(being->getName(), PlayerRelation::NEUTRAL); } else if (link == "ignore" && - being != NULL && + being && being->getType() == Being::PLAYER) { player_relations.setRelation(being->getName(), PlayerRelation::IGNORED); } else if (link == "disregard" && - being != NULL && + being && being->getType() == Being::PLAYER) { player_relations.setRelation(being->getName(), PlayerRelation::DISREGARDED); } else if (link == "friend" && - being != NULL && + being && being->getType() == Being::PLAYER) { player_relations.setRelation(being->getName(), PlayerRelation::FRIEND); @@ -208,7 +208,7 @@ void PopupMenu::handleLink(const std::string& link) /* // Add Buddy action - else if ((link == "buddy") && being != NULL && being->isPlayer()) + else if ((link == "buddy") && being && being->isPlayer()) { if (!buddyWindow->isVisible()) buddyWindow->setVisible(true); @@ -217,7 +217,7 @@ void PopupMenu::handleLink(const std::string& link) }*/ // Pick Up Floor Item action - else if ((link == "pickup") && mFloorItem != NULL) + else if ((link == "pickup") && mFloorItem) { player_node->pickUp(mFloorItem); } @@ -247,15 +247,22 @@ void PopupMenu::handleLink(const std::string& link) } } + else if (link == "chat") + { + chatWindow->addItemText(mItem->getInfo().getName()); + } + else if (link == "drop") { new ItemAmountWindow(AMOUNT_ITEM_DROP, inventoryWindow, mItem); } - - else if (link == "description") + else if (link == "party-invite" && + being && + being->getType() == Being::PLAYER) { - // do nothing for now, I need to write - // a window for the description first + MessageOut outMsg(player_node->getNetwork()); + outMsg.writeInt16(CMSG_PARTY_INVITE); + outMsg.writeInt32(being->getId()); } // Unknown actions @@ -288,7 +295,7 @@ void PopupMenu::showPopup(int x, int y, Item *item) mBrowserBox->addRow(_("@@use|Use@@")); mBrowserBox->addRow(_("@@drop|Drop@@")); - mBrowserBox->addRow(_("@@description|Description@@")); + mBrowserBox->addRow(_("@@chat|Add to Chat@@")); mBrowserBox->addRow("##3---"); mBrowserBox->addRow(_("@@cancel|Cancel@@")); diff --git a/src/gui/popupmenu.h b/src/gui/popupmenu.h index 1165bcb2..2694abd8 100644 --- a/src/gui/popupmenu.h +++ b/src/gui/popupmenu.h @@ -24,15 +24,14 @@ #include <SDL.h> // for Uint32 -#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.cpp b/src/gui/progressbar.cpp index d877bfbc..c7ccfe39 100644 --- a/src/gui/progressbar.cpp +++ b/src/gui/progressbar.cpp @@ -19,17 +19,20 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <guichan/font.hpp> + +#include "gui.h" #include "progressbar.h" +#include "../configuration.h" #include "../graphics.h" #include "../resources/image.h" #include "../resources/resourcemanager.h" -#include <guichan/font.hpp> - ImageRect ProgressBar::mBorder; int ProgressBar::mInstances = 0; +float ProgressBar::mAlpha = config.getValue("guialpha", 0.8); ProgressBar::ProgressBar(float progress, unsigned int width, unsigned int height, @@ -55,6 +58,12 @@ ProgressBar::ProgressBar(float progress, mBorder.grid[6] = dBorders->getSubImage(0, 15, 4, 4); mBorder.grid[7] = dBorders->getSubImage(4, 15, 3, 4); mBorder.grid[8] = dBorders->getSubImage(7, 15, 4, 4); + + for (int i = 0; i < 9; i++) + { + mBorder.grid[i]->setAlpha(mAlpha); + } + dBorders->decRef(); } @@ -92,12 +101,27 @@ void ProgressBar::logic() void ProgressBar::draw(gcn::Graphics *graphics) { + if (config.getValue("guialpha", 0.8) != mAlpha) + { + if (config.getValue("opengl", 0)) + mAlpha = config.getValue("guialpha", 0.8); + else + mAlpha = 1.0f; + for (int i = 0; i < 9; i++) + { + mBorder.grid[i]->setAlpha(mAlpha); + } + } + static_cast<Graphics*>(graphics)-> drawImageRect(0, 0, getWidth(), getHeight(), mBorder); + const int alpha = (int)(mAlpha * 255.0f); + // The bar if (mProgress > 0) { - graphics->setColor(gcn::Color(mRed, mGreen, mBlue, 200)); + + graphics->setColor(gcn::Color(mRed, mGreen, mBlue, alpha)); graphics->fillRectangle(gcn::Rectangle(4, 4, (int) (mProgress * (getWidth() - 8)), getHeight() - 8)); @@ -105,20 +129,22 @@ void ProgressBar::draw(gcn::Graphics *graphics) // The label if (!mText.empty()) { - gcn::Font *f = getFont(); + gcn::Font *f = boldFont; const int textX = getWidth() / 2; const int textY = (getHeight() - f->getHeight()) / 2; graphics->setFont(f); - graphics->setColor(gcn::Color(0, 0, 0)); + graphics->setColor(gcn::Color(0, 0, 0, alpha)); graphics->drawText(mText, textX + 1, textY, gcn::Graphics::CENTER); graphics->drawText(mText, textX, textY - 1, gcn::Graphics::CENTER); graphics->drawText(mText, textX, textY + 1, gcn::Graphics::CENTER); graphics->drawText(mText, textX - 1, textY, gcn::Graphics::CENTER); - graphics->setColor(gcn::Color(255, 255, 255)); + graphics->setColor(gcn::Color(255, 255, 255, alpha)); graphics->drawText(mText, textX, textY, gcn::Graphics::CENTER); + + graphics->setColor(gcn::Color(0, 0, 0)); } } diff --git a/src/gui/progressbar.h b/src/gui/progressbar.h index a4b30b04..2c1b22da 100644 --- a/src/gui/progressbar.h +++ b/src/gui/progressbar.h @@ -22,12 +22,12 @@ #ifndef PROGRESSBAR_H #define PROGRESSBAR_H +#include <string> + #include <guichan/widget.hpp> #include <SDL_types.h> -#include <string> - class ImageRect; /** @@ -81,12 +81,12 @@ class ProgressBar : public gcn::Widget Uint8 getRed() const { return mRed; } /** - * Returns the red value of color. + * Returns the green value of color. */ Uint8 getGreen() const { return mGreen; } /** - * Returns the red value of color. + * Returns the blue value of color. */ Uint8 getBlue() const { return mBlue; } @@ -110,6 +110,7 @@ class ProgressBar : public gcn::Widget static ImageRect mBorder; static int mInstances; + static float mAlpha; }; #endif diff --git a/src/gui/radiobutton.cpp b/src/gui/radiobutton.cpp index 245112a7..c8ae2fad 100644 --- a/src/gui/radiobutton.cpp +++ b/src/gui/radiobutton.cpp @@ -21,12 +21,14 @@ #include "radiobutton.h" +#include "../configuration.h" #include "../graphics.h" #include "../resources/image.h" #include "../resources/resourcemanager.h" int RadioButton::instances = 0; +float RadioButton::mAlpha = config.getValue("guialpha", 0.8); Image *RadioButton::radioNormal; Image *RadioButton::radioChecked; Image *RadioButton::radioDisabled; @@ -43,6 +45,10 @@ RadioButton::RadioButton(const std::string& caption, const std::string& group, radioChecked = resman->getImage("graphics/gui/radioin.png"); radioDisabled = resman->getImage("graphics/gui/radioout.png"); radioDisabledChecked = resman->getImage("graphics/gui/radioin.png"); + radioNormal->setAlpha(mAlpha); + radioChecked->setAlpha(mAlpha); + radioDisabled->setAlpha(mAlpha); + radioDisabledChecked->setAlpha(mAlpha); } instances++; @@ -63,32 +69,37 @@ RadioButton::~RadioButton() void RadioButton::drawBox(gcn::Graphics* graphics) { + if (config.getValue("guialpha", 0.8) != mAlpha) + { + mAlpha = config.getValue("guialpha", 0.8); + radioNormal->setAlpha(mAlpha); + radioChecked->setAlpha(mAlpha); + radioDisabled->setAlpha(mAlpha); + radioDisabledChecked->setAlpha(mAlpha); + } + Image *box = NULL; - if (isSelected()) { - if (isEnabled()) { + if (isSelected()) + { + if (isEnabled()) box = radioChecked; - } else { + else box = radioDisabledChecked; - } - } else if (isEnabled()) { + } + else if (isEnabled()) box = radioNormal; - } else { + else box = radioDisabled; - } - if (box != NULL) { + if (box) static_cast<Graphics*>(graphics)->drawImage(box, 2, 2); - } } void RadioButton::draw(gcn::Graphics* graphics) { - - graphics->pushClipArea(gcn::Rectangle(1, - 1, - getWidth() - 1, - getHeight() - 1)); + graphics->pushClipArea(gcn::Rectangle(1, 1, getWidth() - 1, + getHeight() - 1)); drawBox(graphics); diff --git a/src/gui/radiobutton.h b/src/gui/radiobutton.h index 2d2bdbb7..3d952b3f 100644 --- a/src/gui/radiobutton.h +++ b/src/gui/radiobutton.h @@ -26,11 +26,11 @@ class Image; - /* * Guichan based RadioButton with custom look */ -class RadioButton : public gcn::RadioButton { +class RadioButton : public gcn::RadioButton +{ public: /* * Constructor. @@ -56,6 +56,7 @@ class RadioButton : public gcn::RadioButton { private: static int instances; + static float mAlpha; static Image *radioNormal; static Image *radioChecked; static Image *radioDisabled; diff --git a/src/gui/recorder.cpp b/src/gui/recorder.cpp new file mode 100644 index 00000000..40ef974b --- /dev/null +++ b/src/gui/recorder.cpp @@ -0,0 +1,116 @@ +/* + * A chat recorder + * Copyright (C) 2008 Lloyd Bryant <lloyd_bryant@netzero.net> + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <physfs.h> + +#include "button.h" +#include "chat.h" +#include "recorder.h" +#include "windowcontainer.h" + +#include "widgets/layout.h" + +#include "../utils/trim.h" + +Recorder::Recorder(ChatWindow *chat, const std::string &title, + const std::string &buttonTxt) : + Window(title) +{ + setWindowName(_("Recorder")); + const int offsetX = 2 * getPadding() + 10; + const int offsetY = getTitleBarHeight() + getPadding() + 10; + + mChat = chat; + Button *button = new Button(buttonTxt, "activate", this); + setDefaultSize(0, windowContainer->getHeight() - 123 - button->getHeight() - + offsetY, button->getWidth() + offsetX, button->getHeight() + + offsetY); + + place(0, 0, button); + + Layout &layout = getLayout(); + layout.setRowHeight(0, Layout::AUTO_SET); + + loadWindowState(); +} + +Recorder::~Recorder() +{ +} + +void Recorder::record(const std::string &msg) +{ + if (mStream.is_open()) + { + mStream << msg << std::endl; + } +} + +void Recorder::changeRecordingStatus(const std::string &msg) +{ + std::string msgCopy = msg; + trim(msgCopy); + + if (msgCopy.empty()) + { + if (mStream.is_open()) + { + mStream.close(); + 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); + } + } + else 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); + const std::string file = + std::string(PHYSFS_getUserDir()) + "/.tmw/" + msgCopy; + + mStream.open(file.c_str(), std::ios_base::trunc); + + if (mStream.is_open()) + setVisible(true); + else + mChat->chatLog(_("Failed to start recording."), BY_SERVER); + } +} + +void Recorder::action(const gcn::ActionEvent &event) +{ + changeRecordingStatus(""); +} diff --git a/src/gui/recorder.h b/src/gui/recorder.h new file mode 100644 index 00000000..0bbab012 --- /dev/null +++ b/src/gui/recorder.h @@ -0,0 +1,76 @@ +/* + * A chat recorder + * Copyright (C) 2008 Lloyd Bryant <lloyd_bryant@netzero.net> + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef RECORD_H +#define RECORD_H + +#include <fstream> +#include <string> + +#include <guichan/actionlistener.hpp> + +#include "window.h" + +#include "../utils/gettext.h" + +class ChatWindow; + +class Recorder : public Window, public gcn::ActionListener +{ + public: + Recorder(ChatWindow *chat, const std::string &title = _("Recording..."), + const std::string &buttonTxt = _("Stop recording")); + + virtual ~Recorder(); + + /* + * Outputs the message to the recorder file + * + * @param msg the line to write to the recorded file. + */ + void record(const std::string &msg); + + /* + * Outputs the message to the recorder file + * + * @param msg The file to write out to. If null, then stop recording. + */ + void changeRecordingStatus(const std::string &msg); + + /* + * Whether or not the recorder is in use. + */ + bool isRecording() {return (bool) mStream.is_open();} + + /* + * called when the button is pressed + * + * @param event is the event that is generated + */ + void action(const gcn::ActionEvent &event); + + private: + ChatWindow *mChat; + + std::ofstream mStream; +}; + +#endif diff --git a/src/gui/register.cpp b/src/gui/register.cpp index 5605ef96..e17f5902 100644 --- a/src/gui/register.cpp +++ b/src/gui/register.cpp @@ -19,30 +19,27 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "register.h" - -#include <string> -#include <sstream> - #include <guichan/widgets/label.hpp> -#include "../main.h" #include "../configuration.h" #include "../log.h" #include "../logindata.h" +#include "../main.h" #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 "widgets/layout.h" #include "../utils/gettext.h" #include "../utils/strprintf.h" +#include "../utils/tostring.h" /** * Listener used while dealing with wrong data. It is used to direct the focus @@ -80,10 +77,12 @@ 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); @@ -97,10 +96,12 @@ RegisterDialog::RegisterDialog(LoginData *loginData): place(1, 3, mMaleButton); place(2, 3, mFemaleButton); place(0, 4, serverLabel); + place(0, 5, portLabel); place(1, 0, mUserField, 3).setPadding(2); place(1, 1, mPasswordField, 3).setPadding(2); place(1, 2, mConfirmField, 3).setPadding(2); place(1, 4, mServerField, 3).setPadding(2); + place(1, 5, mPortField, 3).setPadding(2); place = getPlacer(0, 2); place(1, 0, mRegisterButton); place(2, 0, mCancelButton); @@ -110,6 +111,7 @@ RegisterDialog::RegisterDialog(LoginData *loginData): 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,10 +122,13 @@ 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); setLocationRelativeTo(getParent()); setVisible(true); @@ -216,7 +221,7 @@ void 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"; @@ -238,5 +243,40 @@ bool RegisterDialog::canSubmit() const !mPasswordField->getText().empty() && !mConfirmField->getText().empty() && !mServerField->getText().empty() && + isUShort(mPortField->getText()) && state == REGISTER_STATE; } + +bool RegisterDialog::isUShort(const std::string &str) +{ + if (str.empty()) + { + 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 3dddae0f..9588e07e 100644 --- a/src/gui/register.h +++ b/src/gui/register.h @@ -22,12 +22,12 @@ #ifndef REGISTER_H #define REGISTER_H -#include <iosfwd> +#include <string> + #include <guichan/actionlistener.hpp> #include <guichan/keylistener.hpp> #include "window.h" -#include "../guichanfwd.h" class LoginData; class OkDialog; @@ -72,10 +72,29 @@ class RegisterDialog : public Window, public gcn::ActionListener, */ bool canSubmit() const; + /** + * 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.cpp b/src/gui/scrollarea.cpp index 8a74cd72..bc3a62dc 100644 --- a/src/gui/scrollarea.cpp +++ b/src/gui/scrollarea.cpp @@ -19,10 +19,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <algorithm> - #include "scrollarea.h" +#include "../configuration.h" #include "../graphics.h" #include "../resources/image.h" @@ -31,19 +30,22 @@ #include "../utils/dtor.h" int ScrollArea::instances = 0; +float ScrollArea::mAlpha = config.getValue("guialpha", 0.8); ImageRect ScrollArea::background; ImageRect ScrollArea::vMarker; Image *ScrollArea::buttons[4][2]; -ScrollArea::ScrollArea(bool gc): +ScrollArea::ScrollArea(bool gc, bool opaque): gcn::ScrollArea(), + mOpaque(opaque), mGC(gc) { init(); } -ScrollArea::ScrollArea(gcn::Widget *widget, bool gc): +ScrollArea::ScrollArea(gcn::Widget *widget, bool gc, bool opaque): gcn::ScrollArea(widget), + mOpaque(opaque), mGC(gc) { init(); @@ -52,9 +54,8 @@ ScrollArea::ScrollArea(gcn::Widget *widget, bool gc): ScrollArea::~ScrollArea() { // Garbage collection - if (mGC) { + if (mGC) delete getContent(); - } instances--; @@ -88,12 +89,15 @@ void ScrollArea::init() const int bggridy[4] = {0, 3, 28, 31}; int a = 0, x, y; - for (y = 0; y < 3; y++) { - for (x = 0; x < 3; x++) { + for (y = 0; y < 3; y++) + { + for (x = 0; x < 3; x++) + { background.grid[a] = textbox->getSubImage( bggridx[x], bggridy[y], bggridx[x + 1] - bggridx[x] + 1, bggridy[y + 1] - bggridy[y] + 1); + background.grid[a]->setAlpha(config.getValue("guialpha", 0.8)); a++; } } @@ -106,12 +110,15 @@ void ScrollArea::init() int vsgridy[4] = {0, 4, 15, 19}; a = 0; - for (y = 0; y < 3; y++) { - for (x = 0; x < 3; x++) { + for (y = 0; y < 3; y++) + { + for (x = 0; x < 3; x++) + { vMarker.grid[a] = vscroll->getSubImage( vsgridx[x], vsgridy[y], vsgridx[x + 1] - vsgridx[x], vsgridy[y + 1] - vsgridy[y]); + vMarker.grid[a]->setAlpha(config.getValue("guialpha", 0.8)); a++; } } @@ -146,7 +153,7 @@ void ScrollArea::logic() // When no scrollbar in a certain direction, adapt content size to match // the content dimension exactly. - if (content != NULL) + if (content) { if (getHorizontalScrollPolicy() == gcn::ScrollArea::SHOW_NEVER) { @@ -188,6 +195,16 @@ void ScrollArea::draw(gcn::Graphics *graphics) mScrollbarWidth)); } + if (config.getValue("guialpha", 0.8) != mAlpha) + { + mAlpha = config.getValue("guialpha", 0.8); + for (int a = 0; a < 9; a++) + { + background.grid[a]->setAlpha(mAlpha); + vMarker.grid[a]->setAlpha(mAlpha); + } + } + drawChildren(graphics); } @@ -197,7 +214,8 @@ void ScrollArea::drawFrame(gcn::Graphics *graphics) int w = getWidth() + bs * 2; int h = getHeight() + bs * 2; - if (mOpaque) { + if (mOpaque) + { static_cast<Graphics*>(graphics)-> drawImageRect(0, 0, w, h, background); } @@ -207,10 +225,12 @@ void ScrollArea::setOpaque(bool opaque) { mOpaque = opaque; - if (mOpaque) { + if (mOpaque) + { setFrameSize(2); } - else { + else + { setFrameSize(0); } } @@ -220,7 +240,8 @@ void ScrollArea::drawButton(gcn::Graphics *graphics, BUTTON_DIR dir) int state = 0; gcn::Rectangle dim; - switch(dir) { + switch (dir) + { case UP: state = mUpButtonPressed ? 1 : 0; dim = getUpButtonDimension(); diff --git a/src/gui/scrollarea.h b/src/gui/scrollarea.h index 1641f318..33ebc692 100644 --- a/src/gui/scrollarea.h +++ b/src/gui/scrollarea.h @@ -38,12 +38,12 @@ class ScrollArea : public gcn::ScrollArea /** * Constructor. */ - ScrollArea(bool gc = true); + ScrollArea(bool gc = true, bool opaque = true); /** * Constructor. */ - ScrollArea(gcn::Widget *content, bool gc = true); + ScrollArea(gcn::Widget *content, bool gc = true, bool opaque = true); /** * Destructor. @@ -100,6 +100,7 @@ class ScrollArea : public gcn::ScrollArea void drawHMarker(gcn::Graphics *graphics); static int instances; + static float mAlpha; static ImageRect background; static ImageRect vMarker; static Image *buttons[4][2]; diff --git a/src/gui/sdlinput.cpp b/src/gui/sdlinput.cpp index f68dc9c8..51442798 100644 --- a/src/gui/sdlinput.cpp +++ b/src/gui/sdlinput.cpp @@ -228,7 +228,7 @@ int SDLInput::convertMouseButton(int button) int SDLInput::convertKeyCharacter(SDL_Event event) { SDL_keysym keysym = event.key.keysym; - + int value = keysym.unicode; switch (keysym.sym) diff --git a/src/gui/sell.cpp b/src/gui/sell.cpp index cc6f02d5..e4be7921 100644 --- a/src/gui/sell.cpp +++ b/src/gui/sell.cpp @@ -19,25 +19,19 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "sell.h" - -#include <cassert> - #include <guichan/widgets/label.hpp> #include "button.h" -#include "shoplistbox.h" #include "scrollarea.h" +#include "sell.h" #include "shop.h" +#include "shoplistbox.h" #include "slider.h" #include "widgets/layout.h" -#include "../item.h" #include "../npc.h" -#include "../resources/iteminfo.h" - #include "../net/messageout.h" #include "../net/protocol.h" diff --git a/src/gui/sell.h b/src/gui/sell.h index 8e639a3d..c11a7b7c 100644 --- a/src/gui/sell.h +++ b/src/gui/sell.h @@ -25,9 +25,9 @@ #include <guichan/actionlistener.hpp> #include <guichan/selectionlistener.hpp> -#include "window.h" +#include <SDL_types.h> -#include "../guichanfwd.h" +#include "window.h" class Item; class Network; diff --git a/src/gui/setup.cpp b/src/gui/setup.cpp index d38fb2e3..09eaeff0 100644 --- a/src/gui/setup.cpp +++ b/src/gui/setup.cpp @@ -19,31 +19,30 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "setup.h" - #include "button.h" +#include "setup.h" #include "setup_audio.h" +#include "setup_colors.h" #include "setup_joystick.h" -#include "setup_video.h" #include "setup_keyboard.h" #include "setup_players.h" +#include "setup_video.h" #include "widgets/tabbedarea.h" #include "../utils/dtor.h" #include "../utils/gettext.h" -#include <algorithm> -#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; extern Window *itemShortcutWindow; +extern Window *emoteShortcutWindow; +extern Window *emoteWindow; extern Window *tradeWindow; Setup::Setup(): @@ -58,7 +57,8 @@ Setup::Setup(): N_("Apply"), N_("Cancel"), N_("Reset Windows"), 0 }; int x = width; - for (const char **curBtn = buttonNames; *curBtn; ++curBtn) { + for (const char **curBtn = buttonNames; *curBtn; ++curBtn) + { Button *btn = new Button(gettext(*curBtn), *curBtn, this); x -= btn->getWidth() + 5; btn->setPosition(x, height - btn->getHeight() - 5); @@ -90,6 +90,10 @@ Setup::Setup(): panel->addTab(_("Keyboard"), tab); mTabs.push_back(tab); + tab = new Setup_Colors(); + panel->addTab(_("Colors"), tab); + mTabs.push_back(tab); + tab = new Setup_Players(); panel->addTab(_("Players"), tab); mTabs.push_back(tab); @@ -131,6 +135,8 @@ void Setup::action(const gcn::ActionEvent &event) helpWindow->resetToDefaultSize(); skillDialog->resetToDefaultSize(); itemShortcutWindow->resetToDefaultSize(); + emoteShortcutWindow->resetToDefaultSize(); + emoteWindow->resetToDefaultSize(); tradeWindow->resetToDefaultSize(); } } diff --git a/src/gui/setup.h b/src/gui/setup.h index 881961f6..e4eb0902 100644 --- a/src/gui/setup.h +++ b/src/gui/setup.h @@ -28,6 +28,8 @@ #include "window.h" +#include "../guichanfwd.h" + class SetupTab; /** @@ -51,8 +53,7 @@ class Setup : public Window, public gcn::ActionListener /** * Event handling method. */ - void - action(const gcn::ActionEvent &event); + void action(const gcn::ActionEvent &event); private: std::list<SetupTab*> mTabs; diff --git a/src/gui/setup_audio.cpp b/src/gui/setup_audio.cpp index b9fc1d2d..5c189882 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 "widgets/layouthelper.h" @@ -110,8 +109,8 @@ void Setup_Audio::cancel() sound.setMusicVolume(mMusicVolume); mMusicSlider->setValue(mMusicVolume); - config.setValue("sound", mSoundEnabled ? 1 : 0); - config.setValue("sfxVolume", mSfxVolume ? 1 : 0); + config.setValue("sound", mSoundEnabled ? true : false); + config.setValue("sfxVolume", mSfxVolume); config.setValue("musicVolume", mMusicVolume); } diff --git a/src/gui/setup_audio.h b/src/gui/setup_audio.h index 5345d3cf..9e951895 100644 --- a/src/gui/setup_audio.h +++ b/src/gui/setup_audio.h @@ -22,11 +22,9 @@ #ifndef GUI_SETUP_AUDIO_H #define GUI_SETUP_AUDIO_H -#include "setuptab.h" - #include <guichan/actionlistener.hpp> -#include "../guichanfwd.h" +#include "setuptab.h" class Setup_Audio : public SetupTab, public gcn::ActionListener { diff --git a/src/gui/setup_colors.cpp b/src/gui/setup_colors.cpp new file mode 100644 index 00000000..372dbd17 --- /dev/null +++ b/src/gui/setup_colors.cpp @@ -0,0 +1,249 @@ +/* + * Configurable text colors + * Copyright (C) 2008 Douglas Boffey <dougaboffey@netscape.net> + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <string> +#include <cmath> + +#include <guichan/listmodel.hpp> +#include <guichan/widgets/label.hpp> +#include <guichan/widgets/slider.hpp> + +#include "browserbox.h" +#include "color.h" +#include "itemlinkhandler.h" +#include "listbox.h" +#include "scrollarea.h" +#include "setup_colors.h" +#include "slider.h" +#include "textfield.h" + +#include "widgets/layouthelper.h" + +#include "../configuration.h" + +#include "../utils/gettext.h" +#include "../utils/tostring.h" + +Setup_Colors::Setup_Colors() : + mSelected(-1) +{ + setOpaque(false); + + mColorBox = new ListBox(textColor); + mColorBox->setActionEventId("color_box"); + mColorBox->addActionListener(this); + + mScroll = new ScrollArea(mColorBox); + mScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + + mPreview = new BrowserBox(BrowserBox::AUTO_WRAP); + mPreview->setOpaque(false); + + // Replace this later with a more appropriate link handler. For now, this'll + // do, as it'll do nothing when clicked on. + mPreview->setLinkHandler(new ItemLinkHandler()); + + mPreviewBox = new ScrollArea(mPreview); + mPreviewBox->setHeight(20); + mPreviewBox->setScrollPolicy(gcn::ScrollArea::SHOW_NEVER, + gcn::ScrollArea::SHOW_NEVER); + + mRedLabel = new gcn::Label(_("Red: ")); + + mRedText = new TextField(); + mRedText->setWidth(40); + mRedText->setRange(0, 255); + mRedText->setNumeric(true); + mRedText->addListener(this); + + mRedSlider = new Slider(0, 255); + mRedSlider->setWidth(160); + mRedSlider->setValue(mRedText->getValue()); + mRedSlider->setActionEventId("slider_red"); + mRedSlider->addActionListener(this); + + mGreenLabel = new gcn::Label(_("Green: ")); + + mGreenText = new TextField(); + mGreenText->setWidth(40); + mGreenText->setRange(0, 255); + mGreenText->setNumeric(true); + mGreenText->addListener(this); + + mGreenSlider = new Slider(0, 255); + mGreenSlider->setWidth(160); + mGreenSlider->setValue(mGreenText->getValue()); + mGreenSlider->setActionEventId("slider_green"); + mGreenSlider->addActionListener(this); + + mBlueLabel = new gcn::Label(_("Blue: ")); + + mBlueText = new TextField(); + mBlueText->setWidth(40); + mBlueText->setRange(0, 255); + mBlueText->setNumeric(true); + mBlueText->addListener(this); + + mBlueSlider = new Slider(0, 255); + mBlueSlider->setWidth(160); + mBlueSlider->setValue(mBlueText->getValue()); + mBlueSlider->setActionEventId("slider_blue"); + mBlueSlider->addActionListener(this); + + setOpaque(false); + + // Do the layout + LayoutHelper h(this); + ContainerPlacer place = h.getPlacer(0, 0); + + place(0, 0, mScroll, 4, 7).setPadding(2); + place(0, 7, mPreviewBox, 4).setPadding(2); + place(0, 8, mRedLabel, 2); + place(2, 8, mRedSlider); + place(3, 8, mRedText).setPadding(1); + place(0, 9, mGreenLabel, 2); + place(2, 9, mGreenSlider); + place(3, 9, mGreenText).setPadding(1); + place(0, 10, mBlueLabel, 2); + place(2, 10, mBlueSlider); + place(3, 10, mBlueText).setPadding(1); + + setDimension(gcn::Rectangle(0, 0, 290, 250)); +} + +Setup_Colors::~Setup_Colors() +{ + delete mRedLabel; + delete mRedSlider; + delete mRedText; + + delete mGreenLabel; + delete mGreenSlider; + delete mGreenText; + + delete mBlueLabel; + delete mBlueSlider; + delete mBlueText; + + delete mScroll; +} + +void Setup_Colors::action(const gcn::ActionEvent &event) +{ + if (event.getId() == "color_box") + { + mSelected = mColorBox->getSelected(); + int col = textColor->getColorAt(mSelected); + char ch = textColor->getColorCharAt(mSelected); + std::string msg; + + if (ch == '<') + msg = toString("@@|") + + _("This is what the color looks like") + "@@"; + else + msg = "##" + toString(ch) + + _("This is what the color looks like"); + + mPreview->clearRows(); + mPreview->addRow(msg); + setEntry(mRedSlider, mRedText, col >> 16); + setEntry(mGreenSlider, mGreenText, (col >> 8) & 0xff); + setEntry(mBlueSlider, mBlueText, col & 0xff); + return; + } + + if (event.getId() == "slider_red") + { + mRedText->setText(toString(std::floor(mRedSlider->getValue()))); + updateColor(); + return; + } + + if (event.getId() == "slider_green") + { + mGreenText->setText(toString(std::floor(mGreenSlider->getValue()))); + updateColor(); + return; + } + + if (event.getId() == "slider_blue") + { + mBlueText->setText(toString(std::floor(mBlueSlider->getValue()))); + updateColor(); + return; + } +} + +void Setup_Colors::setEntry(gcn::Slider *s, TextField *t, int value) +{ + s->setValue(value); + char buffer[100]; + sprintf(buffer, "%d", value); + t->setText(buffer); +} + +void Setup_Colors::apply() +{ + textColor->commit(); +} + +void Setup_Colors::cancel() +{ + textColor->rollback(); + int col = textColor->getColorAt(mSelected); + setEntry(mRedSlider, mRedText, col >> 16); + setEntry(mGreenSlider, mGreenText, (col >> 8) & 0xff); + setEntry(mBlueSlider, mBlueText, col & 0xff); +} + +void Setup_Colors::listen(const TextField *tf) +{ + if (tf == mRedText) + { + mRedSlider->setValue(tf->getValue()); + updateColor(); + return; + } + if (tf == mGreenText) + { + mGreenSlider->setValue(tf->getValue()); + updateColor(); + return; + } + if (tf == mBlueText) + { + mBlueSlider->setValue(tf->getValue()); + updateColor(); + return; + } +} + +void Setup_Colors::updateColor() +{ + if (mSelected == -1) + { + return; + } + int rgb = static_cast<int>(mRedSlider->getValue()) << 16 | + static_cast<int>(mGreenSlider->getValue()) << 8 | + static_cast<int>(mBlueSlider->getValue()); + textColor->setColorAt(mSelected, rgb); +} diff --git a/src/gui/setup_colors.h b/src/gui/setup_colors.h new file mode 100644 index 00000000..8fd1ba59 --- /dev/null +++ b/src/gui/setup_colors.h @@ -0,0 +1,75 @@ +/* + * Configurable text colors + * Copyright (C) 2008 Douglas Boffey <dougaboffey@netscape.net> + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef SETUP_COLOURS_H +#define SETUP_COLOURS_H + +#include <string> + +#include <guichan/actionlistener.hpp> + +#include <guichan/widgets/label.hpp> +#include <guichan/widgets/listbox.hpp> + +#include "setuptab.h" +#include "textfield.h" + +#include "../guichanfwd.h" + +class BrowserBox; + +class Setup_Colors : public SetupTab, public gcn::ActionListener, + public TextFieldListener +{ + public: + Setup_Colors(); + ~Setup_Colors(); + void apply(); + void cancel(); + void action(const gcn::ActionEvent &event); + + void listen(const TextField *tf); + private: + gcn::ListBox *mColorBox; + gcn::ScrollArea *mScroll; + BrowserBox *mPreview; + gcn::ScrollArea *mPreviewBox; + int mSelected; + + gcn::Label *mRedLabel; + gcn::Slider *mRedSlider; + TextField *mRedText; + int mRedValue; + + gcn::Label *mGreenLabel; + gcn::Slider *mGreenSlider; + TextField *mGreenText; + int mGreenValue; + + gcn::Label *mBlueLabel; + gcn::Slider *mBlueSlider; + TextField *mBlueText; + int mBlueValue; + + void setEntry(gcn::Slider *s, TextField *t, int value); + void updateColor(); +}; +#endif diff --git a/src/gui/setup_joystick.cpp b/src/gui/setup_joystick.cpp index f8fc194f..2ebcdbde 100644 --- a/src/gui/setup_joystick.cpp +++ b/src/gui/setup_joystick.cpp @@ -19,12 +19,11 @@ * 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 "widgets/layouthelper.h" @@ -42,7 +41,7 @@ Setup_Joystick::Setup_Joystick(): { setOpaque(false); - mOriginalJoystickEnabled = (int)config.getValue("joystickEnabled", 0) != 0; + mOriginalJoystickEnabled = !config.getValue("joystickEnabled", false); mJoystickEnabled->setSelected(mOriginalJoystickEnabled); mJoystickEnabled->addActionListener(this); @@ -62,7 +61,8 @@ Setup_Joystick::Setup_Joystick(): void Setup_Joystick::action(const gcn::ActionEvent &event) { - if (!joystick) { + if (!joystick) + { return; } @@ -72,12 +72,15 @@ void Setup_Joystick::action(const gcn::ActionEvent &event) } else { - if (joystick->isCalibrating()) { + if (joystick->isCalibrating()) + { mCalibrateButton->setCaption(_("Calibrate")); mCalibrateLabel->setCaption (_("Press the button to start calibration")); joystick->finishCalibration(); - } else { + } + else + { mCalibrateButton->setCaption(_("Stop")); mCalibrateLabel->setCaption(_("Rotate the stick")); joystick->startCalibration(); diff --git a/src/gui/setup_joystick.h b/src/gui/setup_joystick.h index dd8c331f..eba8a2cc 100644 --- a/src/gui/setup_joystick.h +++ b/src/gui/setup_joystick.h @@ -22,11 +22,9 @@ #ifndef GUI_SETUP_JOYSTICK_H #define GUI_SETUP_JOYSTICK_H -#include "setuptab.h" - #include <guichan/actionlistener.hpp> -#include "../guichanfwd.h" +#include "setuptab.h" class Setup_Joystick : public SetupTab, public gcn::ActionListener { diff --git a/src/gui/setup_keyboard.cpp b/src/gui/setup_keyboard.cpp index cf44731c..5d7519ef 100644 --- a/src/gui/setup_keyboard.cpp +++ b/src/gui/setup_keyboard.cpp @@ -1,6 +1,7 @@ /* - * The Mana World - * Copyright (C) 2007 The Mana World Development Team + * Custom keyboard shortcuts configuration + * Copyright (C) 2007 Joshua Langley <joshlangley@optusnet.com.au> + * Copyright (C) 2009 The Mana World Development Team * * This file is part of The Mana World. * @@ -19,7 +20,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,17 +29,15 @@ #include "listbox.h" #include "ok_dialog.h" #include "scrollarea.h" +#include "setup_keyboard.h" #include "widgets/layouthelper.h" -#include "../configuration.h" #include "../keyboardconfig.h" #include "../utils/gettext.h" #include "../utils/tostring.h" -#include <SDL_keyboard.h> - /** * The list model for key function list. * @@ -79,11 +78,10 @@ Setup_Keyboard::Setup_Keyboard(): refreshKeys(); - mKeyList->setDimension(gcn::Rectangle(0, 0, 185, 140)); mKeyList->addActionListener(this); - mKeyList->setSelected(-1); ScrollArea *scrollArea = new ScrollArea(mKeyList); + scrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); mAssignKeyButton = new Button(_("Assign"), "assign", this); mAssignKeyButton->addActionListener(this); @@ -140,9 +138,8 @@ void Setup_Keyboard::action(const gcn::ActionEvent &event) { if (event.getSource() == mKeyList) { - if (!mKeySetting) { + if (!mKeySetting) mAssignKeyButton->setEnabled(true); - } } else if (event.getId() == "assign") { @@ -186,7 +183,8 @@ void Setup_Keyboard::refreshKeys() void Setup_Keyboard::keyUnresolved() { - if (mKeySetting) { + if (mKeySetting) + { newKeyCallback(keyboard.getNewKeyIndex()); keyboard.setNewKeyIndex(keyboard.KEY_NO_VALUE); } diff --git a/src/gui/setup_keyboard.h b/src/gui/setup_keyboard.h index f04be792..dee12135 100644 --- a/src/gui/setup_keyboard.h +++ b/src/gui/setup_keyboard.h @@ -1,6 +1,6 @@ /* - * The Mana World - * Copyright (C) 2007 The Mana World Development Team + * Custom keyboard shortcuts configuration + * Copyright (C) 2007 Joshua Langley <joshlangley@optusnet.com.au> * * This file is part of The Mana World. * @@ -22,14 +22,13 @@ #ifndef GUI_SETUP_KEYBOARD_H #define GUI_SETUP_KEYBOARD_H -#include "setuptab.h" -#include "button.h" -#include "../guichanfwd.h" +#include <string> #include <guichan/actionlistener.hpp> +#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 1d8649eb..d25117de 100644 --- a/src/gui/setup_players.cpp +++ b/src/gui/setup_players.cpp @@ -19,26 +19,27 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "setup_players.h" +#include <string> +#include <vector> + +#include <guichan/widgets/label.hpp> #include "button.h" #include "checkbox.h" +#include "listbox.h" #include "ok_dialog.h" +#include "scrollarea.h" +#include "setup_players.h" +#include "table.h" +#include "widgets/dropdown.h" #include "widgets/layouthelper.h" #include "../configuration.h" #include "../log.h" -#include "../player_relations.h" -#include "../sound.h" #include "../utils/gettext.h" -#include <guichan/widgets/dropdown.hpp> -#include <guichan/widgets/label.hpp> - -#include <vector> - #define COLUMNS_NR 2 // name plus listbox #define NAME_COLUMN 0 #define RELATION_CHOICE_COLUMN 1 @@ -136,8 +137,12 @@ public: std::string name = (*player_names)[r]; gcn::Widget *widget = new gcn::Label(name); mWidgets.push_back(widget); + gcn::ListModel *playerRelation = new PlayerRelationListModel(); - gcn::DropDown *choicebox = new gcn::DropDown(new PlayerRelationListModel()); + gcn::DropDown *choicebox = new DropDown(playerRelation, + new ScrollArea(), + new ListBox(playerRelation), + false); choicebox->setSelected(player_relations.getRelation(name)); mWidgets.push_back(choicebox); } @@ -198,7 +203,7 @@ public: virtual std::string getElementAt(int i) { if (i >= getNumberOfElements()) { - return "???"; + return _("???"); } return (*player_relations.getPlayerIgnoreStrategies())[i]->mDescription; } @@ -220,18 +225,22 @@ Setup_Players::Setup_Players(): player_relations.getDefault() & PlayerRelation::TRADE)), mDefaultWhisper(new CheckBox(_("Allow whispers"), player_relations.getDefault() & PlayerRelation::WHISPER)), - mDeleteButton(new Button(_("Delete"), ACTION_DELETE, this)), - mIgnoreActionChoicesBox(new gcn::DropDown(new IgnoreChoicesListModel())) + mDeleteButton(new Button(_("Delete"), ACTION_DELETE, this)) { setOpaque(false); + mPlayerTable->setOpaque(false); int table_width = NAME_COLUMN_WIDTH + RELATION_CHOICE_COLUMN_WIDTH; mPlayerTableTitleModel->fixColumnWidth(NAME_COLUMN, NAME_COLUMN_WIDTH); mPlayerTableTitleModel->fixColumnWidth(RELATION_CHOICE_COLUMN, RELATION_CHOICE_COLUMN_WIDTH); - mPlayerTitleTable->setDimension(gcn::Rectangle(10, 10, table_width, 10)); + mPlayerTitleTable->setDimension(gcn::Rectangle(10, 10, table_width - 1, 10)); mPlayerTitleTable->setBackgroundColor(gcn::Color(0xbf, 0xbf, 0xbf)); + gcn::ListModel *ignoreChoices = new IgnoreChoicesListModel(); + mIgnoreActionChoicesBox = new DropDown(ignoreChoices, new ScrollArea(), + new ListBox(ignoreChoices), false); + for (int i = 0; i < COLUMNS_NR; i++) { mPlayerTableTitleModel->set(0, i, diff --git a/src/gui/setup_players.h b/src/gui/setup_players.h index d380d9f6..74247b77 100644 --- a/src/gui/setup_players.h +++ b/src/gui/setup_players.h @@ -22,20 +22,19 @@ #ifndef GUI_SETUP_PLAYERS_H #define GUI_SETUP_PLAYERS_H -#include "setuptab.h" - -#include "scrollarea.h" -#include "button.h" -#include "table.h" #include <guichan/actionlistener.hpp> -#include "../guichanfwd.h" +#include "setuptab.h" #include "../player_relations.h" +class GuiTable; class PlayerTableModel; +class StaticTableModel; -class Setup_Players : public SetupTab, public gcn::ActionListener, public PlayerRelationsListener +class Setup_Players : public SetupTab, + public gcn::ActionListener, + public PlayerRelationsListener { public: Setup_Players(); @@ -55,13 +54,13 @@ private: PlayerTableModel *mPlayerTableModel; GuiTable *mPlayerTable; GuiTable *mPlayerTitleTable; - ScrollArea *mPlayerScrollArea; + gcn::ScrollArea *mPlayerScrollArea; gcn::CheckBox *mPersistIgnores; gcn::CheckBox *mDefaultTrading; gcn::CheckBox *mDefaultWhisper; - Button *mDeleteButton; + gcn::Button *mDeleteButton; gcn::DropDown *mIgnoreActionChoicesBox; }; diff --git a/src/gui/setup_video.cpp b/src/gui/setup_video.cpp index 53041a9c..c02025c8 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" @@ -41,6 +40,7 @@ #include "../configuration.h" #include "../graphics.h" +#include "../localplayer.h" #include "../log.h" #include "../main.h" #include "../particle.h" @@ -104,9 +104,12 @@ ModeListModel::ModeListModel() } Setup_Video::Setup_Video(): - mFullScreenEnabled(config.getValue("screen", 0)), - mOpenGLEnabled(config.getValue("opengl", 0)), - mCustomCursorEnabled(config.getValue("customcursor", 1)), + mFullScreenEnabled(config.getValue("screen", false)), + mOpenGLEnabled(config.getValue("opengl", false)), + mCustomCursorEnabled(config.getValue("customcursor", true)), + mParticleEffectsEnabled(config.getValue("particleeffects", true)), + mSpeechBubbleEnabled(config.getValue("speechbubble", true)), + mNameEnabled(config.getValue("showownname", false)), mOpacity(config.getValue("guialpha", 0.8)), mFps((int) config.getValue("fpslimit", 0)), mModeListModel(new ModeListModel), @@ -114,6 +117,9 @@ 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)), + mNameCheckBox(new CheckBox(_("Show name"), mNameEnabled)), mAlphaSlider(new Slider(0.2, 1.0)), mFpsCheckBox(new CheckBox(_("FPS Limit:"))), mFpsSlider(new Slider(10, 200)), @@ -137,13 +143,12 @@ Setup_Video::Setup_Video(): scrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); gcn::Label *alphaLabel = new gcn::Label(_("Gui opacity")); - gcn::Label *scrollRadiusLabel = new gcn::Label(_("Scroll radius")); gcn::Label *scrollLazinessLabel = new gcn::Label(_("Scroll laziness")); gcn::Label *overlayDetailLabel = new gcn::Label(_("Ambient FX")); gcn::Label *particleDetailLabel = new gcn::Label(_("Particle Detail")); - mModeList->setEnabled(false); + mModeList->setEnabled(true); #ifndef USE_OPENGL mOpenGLCheckBox->setEnabled(false); #endif @@ -159,7 +164,11 @@ Setup_Video::Setup_Video(): mFpsSlider->setEnabled(mFps > 0); mFpsCheckBox->setSelected(mFps > 0); + mModeList->setActionEventId("videomode"); mCustomCursorCheckBox->setActionEventId("customcursor"); + mParticleEffectsCheckBox->setActionEventId("particleeffects"); + mSpeechBubbleCheckBox->setActionEventId("speechbubble"); + mNameCheckBox->setActionEventId("showownname"); mAlphaSlider->setActionEventId("guialpha"); mFpsCheckBox->setActionEventId("fpslimitcheckbox"); mFpsSlider->setActionEventId("fpslimitslider"); @@ -172,7 +181,11 @@ Setup_Video::Setup_Video(): mParticleDetailSlider->setActionEventId("particledetailslider"); mParticleDetailField->setActionEventId("particledetailfield"); + mModeList->addActionListener(this); mCustomCursorCheckBox->addActionListener(this); + mParticleEffectsCheckBox->addActionListener(this); + mSpeechBubbleCheckBox->addActionListener(this); + mNameCheckBox->addActionListener(this); mAlphaSlider->addActionListener(this); mFpsCheckBox->addActionListener(this); mFpsSlider->addActionListener(this); @@ -227,30 +240,33 @@ Setup_Video::Setup_Video(): LayoutHelper h(this); ContainerPlacer place = h.getPlacer(0, 0); - place(0, 0, scrollArea, 1, 4).setPadding(2); + place(0, 0, scrollArea, 1, 6).setPadding(2); place(1, 0, mFsCheckBox, 3); place(1, 1, mOpenGLCheckBox, 3); place(1, 2, mCustomCursorCheckBox, 3); - - place(0, 4, mAlphaSlider); - place(0, 5, mFpsSlider); - place(0, 6, mScrollRadiusSlider); - place(0, 7, mScrollLazinessSlider); - place(0, 8, mOverlayDetailSlider); - place(0, 9, mParticleDetailSlider); - - place(1, 4, alphaLabel, 2); - place(1, 5, mFpsCheckBox).setPadding(3); - place(1, 6, scrollRadiusLabel); - place(1, 7, scrollLazinessLabel); - place(1, 8, overlayDetailLabel); - place(1, 9, particleDetailLabel); - - place(2, 5, mFpsField).setPadding(1); - place(2, 6, mScrollRadiusField).setPadding(1); - place(2, 7, mScrollLazinessField).setPadding(1); - place(2, 8, mOverlayDetailField, 2).setPadding(2); - place(2, 9, mParticleDetailField, 2).setPadding(2); + place(1, 3, mSpeechBubbleCheckBox, 3); + place(1, 4, mNameCheckBox, 3); + place(1, 5, mParticleEffectsCheckBox, 3); + + place(0, 7, mAlphaSlider); + place(0, 8, mFpsSlider); + place(0, 9, mScrollRadiusSlider); + place(0, 10, mScrollLazinessSlider); + place(0, 11, mOverlayDetailSlider); + place(0, 12, mParticleDetailSlider); + + place(1, 7, alphaLabel, 2); + place(1, 8, mFpsCheckBox).setPadding(3); + place(1, 9, scrollRadiusLabel); + place(1, 10, scrollLazinessLabel); + place(1, 11, overlayDetailLabel); + place(1, 12, particleDetailLabel); + + place(2, 8, mFpsField).setPadding(1); + place(2, 9, mScrollRadiusField).setPadding(1); + place(2, 10, mScrollLazinessField).setPadding(1); + place(2, 11, mOverlayDetailField, 2).setPadding(2); + place(2, 12, mParticleDetailField, 2).setPadding(2); setDimension(gcn::Rectangle(0, 0, 295, 250)); } @@ -264,7 +280,7 @@ void Setup_Video::apply() { // Full screen changes bool fullscreen = mFsCheckBox->isSelected(); - if (fullscreen != (config.getValue("screen", 0) == 1)) + if (fullscreen != (config.getValue("screen", false) == 1)) { /* The OpenGL test is only necessary on Windows, since switching * to/from full screen works fine on Linux. On Windows we'd have to @@ -275,7 +291,7 @@ void Setup_Video::apply() #if defined(WIN32) || defined(__APPLE__) // checks for opengl usage - if (!(config.getValue("opengl", 0) == 1)) + if (!(config.getValue("opengl", false) == 1)) { #endif if (!graphics->setFullscreen(fullscreen)) @@ -284,9 +300,9 @@ void Setup_Video::apply() if (!graphics->setFullscreen(fullscreen)) { std::stringstream error; - error << "Failed to switch to " << - (fullscreen ? "windowed" : "fullscreen") << - "mode and restoration of old mode also failed!" << + error << _("Failed to switch to ") << + (fullscreen ? _("windowed") : _("fullscreen")) << + _("mode and restoration of old mode also failed!") << std::endl; logger->error(error.str()); } @@ -297,13 +313,13 @@ void Setup_Video::apply() _("Restart needed for changes to take effect.")); } #endif - config.setValue("screen", fullscreen ? 1 : 0); + config.setValue("screen", fullscreen ? true : false); } // OpenGL change if (mOpenGLCheckBox->isSelected() != mOpenGLEnabled) { - config.setValue("opengl", mOpenGLCheckBox->isSelected() ? 1 : 0); + config.setValue("opengl", mOpenGLCheckBox->isSelected() ? true : false); // OpenGL can currently only be changed by restarting, notify user. new OkDialog(_("Changing OpenGL"), @@ -314,11 +330,14 @@ void Setup_Video::apply() config.setValue("fpslimit", mFps); // We sync old and new values at apply time - mFullScreenEnabled = config.getValue("screen", 0); - mCustomCursorEnabled = config.getValue("customcursor", 1); + mFullScreenEnabled = config.getValue("screen", false); + mCustomCursorEnabled = config.getValue("customcursor", true); + mParticleEffectsEnabled = config.getValue("particleeffects", true); + mSpeechBubbleEnabled = config.getValue("speechbubble", true); + mNameEnabled = config.getValue("showownname", false); mOpacity = config.getValue("guialpha", 0.8); - mOverlayDetail = (int)config.getValue("OverlayDetail", 2); - mOpenGLEnabled = config.getValue("opengl", 0); + mOverlayDetail = (int) config.getValue("OverlayDetail", 2); + mOpenGLEnabled = config.getValue("opengl", false); } int Setup_Video::updateSlider(gcn::Slider *slider, gcn::TextField *field, @@ -346,6 +365,9 @@ void Setup_Video::cancel() mFsCheckBox->setSelected(mFullScreenEnabled); mOpenGLCheckBox->setSelected(mOpenGLEnabled); mCustomCursorCheckBox->setSelected(mCustomCursorEnabled); + mParticleEffectsCheckBox->setSelected(mParticleEffectsEnabled); + mSpeechBubbleCheckBox->setSelected(mSpeechBubbleEnabled); + mNameCheckBox->setSelected(mNameEnabled); mAlphaSlider->setValue(mOpacity); mOverlayDetailSlider->setValue(mOverlayDetail); mParticleDetailSlider->setValue(mParticleDetail); @@ -355,22 +377,59 @@ void Setup_Video::cancel() updateSlider(mScrollRadiusSlider, mScrollRadiusField, "ScrollRadius"); updateSlider(mScrollLazinessSlider, mScrollLazinessField, "ScrollLaziness"); - config.setValue("screen", mFullScreenEnabled ? 1 : 0); - config.setValue("customcursor", mCustomCursorEnabled ? 1 : 0); + config.setValue("screen", mFullScreenEnabled ? true : false); + config.setValue("customcursor", mCustomCursorEnabled ? true : false); + config.setValue("particleeffects", mParticleEffectsEnabled ? true : false); + config.setValue("speechbubble", mSpeechBubbleEnabled ? true : false); + config.setValue("showownname", mNameEnabled ? true : false); config.setValue("guialpha", mOpacity); - config.setValue("opengl", mOpenGLEnabled ? 1 : 0); + config.setValue("opengl", mOpenGLEnabled ? true : false); } void Setup_Video::action(const gcn::ActionEvent &event) { - if (event.getId() == "guialpha") + if (event.getId() == "videomode") + { + const std::string mode = mModeListModel->getElementAt(mModeList->getSelected()); + const int width = atoi(mode.substr(0, mode.find("x")).c_str()); + const int height = atoi(mode.substr(mode.find("x") + 1).c_str()); + + // TODO: Find out why the drawing area doesn't resize without a restart. + new OkDialog(_("Screen resolution changed"), + _("Restart your client for the change to take effect.")); + + config.setValue("screenwidth", width); + config.setValue("screenheight", height); + } + else if (event.getId() == "guialpha") { config.setValue("guialpha", mAlphaSlider->getValue()); } else if (event.getId() == "customcursor") { config.setValue("customcursor", - mCustomCursorCheckBox->isSelected() ? 1 : 0); + mCustomCursorCheckBox->isSelected() ? true : false); + } + else if (event.getId() == "particleeffects") + { + config.setValue("particleeffects", + mParticleEffectsCheckBox->isSelected() ? true : false); + 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() ? true : false); + } + else if (event.getId() == "showownname") + { + // Notify the local player that settings have changed for the name + // and requires an update + if (player_node) + player_node->mUpdateName = true; + config.setValue("showownname", + mNameCheckBox->isSelected() ? true : false); } else if (event.getId() == "fpslimitslider") { diff --git a/src/gui/setup_video.h b/src/gui/setup_video.h index b7011186..303b5bfc 100644 --- a/src/gui/setup_video.h +++ b/src/gui/setup_video.h @@ -22,12 +22,10 @@ #ifndef GUI_SETUP_VIDEO_H #define GUI_SETUP_VIDEO_H -#include "setuptab.h" - #include <guichan/actionlistener.hpp> #include <guichan/keylistener.hpp> -#include "../guichanfwd.h" +#include "setuptab.h" class Setup_Video : public SetupTab, public gcn::ActionListener, public gcn::KeyListener @@ -53,6 +51,9 @@ class Setup_Video : public SetupTab, public gcn::ActionListener, bool mFullScreenEnabled; bool mOpenGLEnabled; bool mCustomCursorEnabled; + bool mParticleEffectsEnabled; + bool mSpeechBubbleEnabled; + bool mNameEnabled; double mOpacity; int mFps; @@ -62,6 +63,9 @@ class Setup_Video : public SetupTab, public gcn::ActionListener, gcn::CheckBox *mFsCheckBox; gcn::CheckBox *mOpenGLCheckBox; gcn::CheckBox *mCustomCursorCheckBox; + gcn::CheckBox *mParticleEffectsCheckBox; + gcn::CheckBox *mSpeechBubbleCheckBox; + gcn::CheckBox *mNameCheckBox; gcn::Slider *mAlphaSlider; gcn::CheckBox *mFpsCheckBox; diff --git a/src/gui/shop.h b/src/gui/shop.h index 4a03b2bc..e0db4c59 100644 --- a/src/gui/shop.h +++ b/src/gui/shop.h @@ -27,10 +27,10 @@ #include <guichan/listmodel.hpp> -#include "../resources/image.h" - #include "../shopitem.h" +class ShopItem; + class ShopItems : public gcn::ListModel { public: diff --git a/src/gui/shoplistbox.cpp b/src/gui/shoplistbox.cpp index 2517d749..8aed3c77 100644 --- a/src/gui/shoplistbox.cpp +++ b/src/gui/shoplistbox.cpp @@ -19,19 +19,20 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "shoplistbox.h" - #include <guichan/font.hpp> -#include <guichan/graphics.hpp> #include <guichan/listmodel.hpp> -#include <guichan/mouseinput.hpp> -#include <guichan/imagefont.hpp> -#include <guichan/basiccontainer.hpp> +#include "color.h" +#include "shop.h" +#include "shoplistbox.h" + +#include "../configuration.h" #include "../graphics.h" const int ITEM_ICON_SIZE = 32; +float ShopListBox::mAlpha = config.getValue("guialpha", 0.8); + ShopListBox::ShopListBox(gcn::ListModel *listModel): ListBox(listModel), mPlayerMoney(0) @@ -59,6 +60,15 @@ void ShopListBox::draw(gcn::Graphics *gcnGraphics) if (!mListModel) return; + if (config.getValue("guialpha", 0.8) != mAlpha) + mAlpha = config.getValue("guialpha", 0.8); + + bool valid; + const int red = (textColor->getColor('H', valid) >> 16) & 0xFF; + const int green = (textColor->getColor('H', valid) >> 8) & 0xFF; + const int blue = textColor->getColor('H', valid) & 0xFF; + const int alpha = (int)(mAlpha * 255.0f); + Graphics *graphics = static_cast<Graphics*>(gcnGraphics); graphics->setFont(getFont()); @@ -68,16 +78,16 @@ void ShopListBox::draw(gcn::Graphics *gcnGraphics) i < mListModel->getNumberOfElements(); ++i, y += mRowHeight) { - gcn::Color backgroundColor = gcn::Color(0xffffff); + gcn::Color backgroundColor = gcn::Color(255, 255, 255, alpha); if (i == mSelected) { - backgroundColor = gcn::Color(110, 160, 255); + backgroundColor = gcn::Color(red, green, blue, alpha); } else if (mShopItems && mPlayerMoney < mShopItems->at(i)->getPrice() && mPriceCheck) { - backgroundColor = gcn::Color(0x919191); + backgroundColor = gcn::Color(145, 145, 145, alpha); } graphics->setColor(backgroundColor); diff --git a/src/gui/shoplistbox.h b/src/gui/shoplistbox.h index bad848d6..cde4786e 100644 --- a/src/gui/shoplistbox.h +++ b/src/gui/shoplistbox.h @@ -23,7 +23,8 @@ #define SHOPLISTBOX_H #include "listbox.h" -#include "shop.h" + +class ShopItems; /** * A list box, meant to be used inside a scroll area. Same as the Guichan list @@ -84,6 +85,8 @@ class ShopListBox : public ListBox unsigned int mRowHeight; /**< Row Height */ + static float mAlpha; + bool mPriceCheck; }; diff --git a/src/gui/shortcutcontainer.cpp b/src/gui/shortcutcontainer.cpp new file mode 100644 index 00000000..4472818e --- /dev/null +++ b/src/gui/shortcutcontainer.cpp @@ -0,0 +1,71 @@ +/* + * 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 "shortcutcontainer.h" + +#include "../configuration.h" + +#include "../resources/image.h" + +#include "../utils/tostring.h" + +float ShortcutContainer::mAlpha = config.getValue("guialpha", 0.8); + +ShortcutContainer::ShortcutContainer(): + mGridWidth(1), + mGridHeight(1) +{ +} + +void ShortcutContainer::widgetResized(const gcn::Event &event) +{ + mGridWidth = getWidth() / mBoxWidth; + if (mGridWidth < 1) + { + mGridWidth = 1; + } + + setHeight((mMaxItems / mGridWidth + + (mMaxItems % mGridWidth > 0 ? 1 : 0)) * mBoxHeight); + + mGridHeight = getHeight() / mBoxHeight; + if (mGridHeight < 1) + { + mGridHeight = 1; + } +} + +int ShortcutContainer::getIndexFromGrid(int pointX, int pointY) const +{ + const gcn::Rectangle tRect = gcn::Rectangle( + 0, 0, mGridWidth * mBoxWidth, mGridHeight * mBoxHeight); + if (!tRect.isPointInRect(pointX, pointY)) + { + return -1; + } + const int index = ((pointY / mBoxHeight) * mGridWidth) + + pointX / mBoxWidth; + if (index >= mMaxItems) + { + return -1; + } + return index; +} diff --git a/src/gui/shortcutcontainer.h b/src/gui/shortcutcontainer.h new file mode 100644 index 00000000..f5f06163 --- /dev/null +++ b/src/gui/shortcutcontainer.h @@ -0,0 +1,107 @@ +/* + * 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 + */ + +#ifndef SHORTCUTCONTAINER_H__ +#define SHORTCUTCONTAINER_H__ + +#include <guichan/mouselistener.hpp> +#include <guichan/widget.hpp> +#include <guichan/widgetlistener.hpp> + +class Image; + +/** + * A generic shortcut container. + * + * \ingroup GUI + */ +class ShortcutContainer : public gcn::Widget, + public gcn::WidgetListener, + public gcn::MouseListener +{ + public: + /** + * Constructor. Initializes the shortcut container. + */ + ShortcutContainer(); + + /** + * Destructor. + */ + ~ShortcutContainer(){} + + /** + * Draws the shortcuts + */ + virtual void draw(gcn::Graphics *graphics) = 0; + + /** + * Invoked when a widget changes its size. This is used to determine + * the new height of the container. + */ + virtual void widgetResized(const gcn::Event &event); + + /** + * Handles mouse when dragged. + */ + virtual void mouseDragged(gcn::MouseEvent &event) = 0; + + /** + * Handles mouse when pressed. + */ + virtual void mousePressed(gcn::MouseEvent &event) = 0; + + /** + * Handles mouse release. + */ + virtual void mouseReleased(gcn::MouseEvent &event) = 0; + + virtual int getMaxItems() + { return mMaxItems; } + + virtual int getBoxWidth() + { return mBoxWidth; } + + virtual int getBoxHeight() + { return mBoxHeight; } + + protected: + /** + * Gets the index from the grid provided the point is in an item box. + * + * @param pointX X coordinate of the point. + * @param pointY Y coordinate of the point. + * @return index on success, -1 on failure. + */ + int getIndexFromGrid(int pointX, int pointY) const; + + Image *mBackgroundImg; + + static float mAlpha; + + int mMaxItems; + int mBoxWidth; + int mBoxHeight; + int mCursorPosX, mCursorPosY; + int mGridWidth, mGridHeight; +}; + +#endif diff --git a/src/gui/itemshortcutwindow.cpp b/src/gui/shortcutwindow.cpp index 6fe1a10b..b6987b3e 100644 --- a/src/gui/itemshortcutwindow.cpp +++ b/src/gui/shortcutwindow.cpp @@ -19,23 +19,27 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "itemshortcutwindow.h" - -#include "itemshortcutcontainer.h" #include "scrollarea.h" +#include "shortcutcontainer.h" +#include "shortcutwindow.h" + +#include "../configuration.h" static const int SCROLL_PADDING = 0; -ItemShortcutWindow::ItemShortcutWindow() +int ShortcutWindow::mInstances = 0; + +ShortcutWindow::ShortcutWindow(const char *title, ShortcutContainer *content) { - setWindowName("ItemShortcut"); + setWindowName(title); // no title presented, title bar is padding so window can be moved. gcn::Window::setTitleBarHeight(gcn::Window::getPadding()); setShowTitle(false); setResizable(true); - setDefaultSize(758, 174, 42, 426); - mItems = new ItemShortcutContainer; + mItems = content; + + mInstances++; const int border = SCROLL_PADDING * 2 + getPadding() * 2; setMinWidth(mItems->getBoxWidth() + border); @@ -43,6 +47,15 @@ ItemShortcutWindow::ItemShortcutWindow() setMaxWidth(mItems->getBoxWidth() * mItems->getMaxItems() + border); setMaxHeight(mItems->getBoxHeight() * mItems->getMaxItems() + border); + const int width = (int) config.getValue("screenwidth", 800); + const int height = (int) config.getValue("screenheight", 600); + + setDefaultSize(width - (mInstances * mItems->getBoxWidth()) - + (mInstances * border), height - (mItems->getBoxHeight() * + mItems->getMaxItems()) - border, mItems->getBoxWidth() + + border, (mItems->getBoxHeight() * mItems->getMaxItems()) + + border); + mScrollArea = new ScrollArea(mItems); mScrollArea->setPosition(SCROLL_PADDING, SCROLL_PADDING); mScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); @@ -53,13 +66,13 @@ ItemShortcutWindow::ItemShortcutWindow() loadWindowState(); } -ItemShortcutWindow::~ItemShortcutWindow() +ShortcutWindow::~ShortcutWindow() { delete mItems; delete mScrollArea; } -void ItemShortcutWindow::widgetResized(const gcn::Event &event) +void ShortcutWindow::widgetResized(const gcn::Event &event) { Window::widgetResized(event); diff --git a/src/gui/itemshortcutwindow.h b/src/gui/shortcutwindow.h index baa34b13..64592328 100644 --- a/src/gui/itemshortcutwindow.h +++ b/src/gui/shortcutwindow.h @@ -19,31 +19,31 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef ITEMSHORTCUTWINDOW_H -#define ITEMSHORTCUTWINDOW_H +#ifndef SHORTCUTWINDOW_H +#define SHORTCUTWINDOW_H #include "window.h" -class ItemShortcutContainer; class ScrollArea; +class ShortcutContainer; /** * A window around the ItemShortcutContainer. * * \ingroup Interface */ -class ItemShortcutWindow : public Window +class ShortcutWindow : public Window { public: /** * Constructor. */ - ItemShortcutWindow(); + ShortcutWindow(const char *title, ShortcutContainer *content); /** * Destructor. */ - ~ItemShortcutWindow(); + ~ShortcutWindow(); /** * Called whenever the widget changes size. @@ -51,11 +51,15 @@ class ItemShortcutWindow : public Window void widgetResized(const gcn::Event &event); private: - ItemShortcutContainer *mItems; + ShortcutWindow(); + ShortcutContainer *mItems; ScrollArea *mScrollArea; + + static int mInstances; }; -extern ItemShortcutWindow *itemShortcutWindow; +extern ShortcutWindow *itemShortcutWindow; +extern ShortcutWindow *emoteShortcutWindow; #endif diff --git a/src/gui/skill.cpp b/src/gui/skill.cpp index 4afd913d..03711a47 100644 --- a/src/gui/skill.cpp +++ b/src/gui/skill.cpp @@ -21,13 +21,15 @@ #include <guichan/widgets/label.hpp> -#include "skill.h" - #include "button.h" #include "listbox.h" #include "scrollarea.h" +#include "skill.h" +#include "table.h" #include "windowcontainer.h" +#include "widgets/layout.h" + #include "../localplayer.h" #include "../log.h" @@ -36,13 +38,18 @@ #include "../utils/strprintf.h" #include "../utils/xml.h" -static const char *SKILLS_FILE = "skills.xml"; +static const char *SKILLS_FILE = _("skills.xml"); struct SkillInfo { std::string name; bool modifiable; }; +static const SkillInfo fakeSkillInfo = { + _("Mystery Skill"), + false +}; + std::vector<SkillInfo> skill_db; static void initSkillinfo(); @@ -58,14 +65,17 @@ public: update(); } - virtual int getRows() { return mEntriesNr; } + virtual int getRows(void) + { + return mEntriesNr; + } virtual int getColumnWidth(int index) { - switch (index) { - case 0: return 160; - default: return 35; - } + if (index == 0) + return 160; + + return 35; } virtual int getRowHeight() @@ -75,15 +85,11 @@ public: virtual void update() { - static const SkillInfo fakeSkillInfo = { - _("Mystery Skill"), - false - }; - mEntriesNr = mDialog->getSkills().size(); resize(); - for (int i = 0; i < mEntriesNr; i++) { + for (int i = 0; i < mEntriesNr; i++) + { SKILL *skill = mDialog->getSkills()[i]; SkillInfo const *info; char tmp[128]; @@ -120,37 +126,35 @@ SkillDialog::SkillDialog(): { initSkillinfo(); mTableModel = new SkillGuiTableModel(this); - mTable.setModel(mTableModel); - mTable.setLinewiseSelection(true); - - setWindowName("Skills"); + mTable = new GuiTable(mTableModel); + mTable->setOpaque(false); + mTable->setLinewiseSelection(true); + mTable->setWrappingEnabled(true); + mTable->setActionEventId("skill"); + mTable->addActionListener(this); + + setWindowName(_("Skills")); setCloseButton(true); setDefaultSize(windowContainer->getWidth() - 260, 25, 255, 260); -// mSkillListBox = new ListBox(this); - ScrollArea *skillScrollArea = new ScrollArea(&mTable); + setMinHeight(50 + mTableModel->getHeight()); + setMinWidth(200); + + ScrollArea *skillScrollArea = new ScrollArea(mTable); mPointsLabel = new gcn::Label(strprintf(_("Skill points: %d"), 0)); - mIncButton = new Button(_("Up"), "inc", this); - mUseButton = new Button(_("Use"), "use", this); + mIncButton = new Button(_("Up"), _("inc"), this); + mUseButton = new Button(_("Use"), _("use"), this); mUseButton->setEnabled(false); -// mSkillListBox->setActionEventId("skill"); - mTable.setActionEventId("skill"); - skillScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); - skillScrollArea->setDimension(gcn::Rectangle(5, 5, 230, 180)); - mPointsLabel->setDimension(gcn::Rectangle(8, 190, 200, 16)); - mIncButton->setPosition(skillScrollArea->getX(), 210); - mUseButton->setPosition(mIncButton->getX() + mIncButton->getWidth() + 5, - 210); - add(skillScrollArea); - add(mPointsLabel); - add(mIncButton); - add(mUseButton); + place(0, 0, skillScrollArea, 5).setPadding(3); + place(0, 1, mPointsLabel, 2); + place(3, 2, mIncButton); + place(4, 2, mUseButton); -// mSkillListBox->addActionListener(this); - mTable.addActionListener(this); + Layout &layout = getLayout(); + layout.setRowHeight(0, Layout::AUTO_SET); setLocationRelativeTo(getParent()); loadWindowState(); @@ -158,6 +162,7 @@ SkillDialog::SkillDialog(): SkillDialog::~SkillDialog() { + delete mTable; } void SkillDialog::action(const gcn::ActionEvent &event) @@ -165,22 +170,25 @@ void SkillDialog::action(const gcn::ActionEvent &event) if (event.getId() == "inc") { // Increment skill - int selectedSkill = mTable.getSelectedRow();//mSkillListBox->getSelected(); + int selectedSkill = mTable->getSelectedRow(); if (selectedSkill >= 0) - { player_node->raiseSkill(mSkillList[selectedSkill]->id); - } } - else if (event.getId() == "skill") + else if (event.getId() == "skill" && mTable->getSelectedRow() > -1) { - mIncButton->setEnabled( - mTable.getSelectedRow() > -1 && - player_node->mSkillPoint > 0); + SKILL *skill = mSkillList[mTable->getSelectedRow()]; + SkillInfo const *info; + + if (skill->id >= 0 && (unsigned int) skill->id < skill_db.size()) + info = &skill_db[skill->id]; + else + info = &fakeSkillInfo; + + mIncButton->setEnabled(player_node->mSkillPoint > 0 && + info->modifiable); } else if (event.getId() == "close") - { setVisible(false); - } } void SkillDialog::update() @@ -188,9 +196,10 @@ void SkillDialog::update() mPointsLabel->setCaption(strprintf(_("Skill points: %d"), player_node->mSkillPoint)); - int selectedSkill = mTable.getSelectedRow(); + int selectedSkill = mTable->getSelectedRow(); - if (selectedSkill >= 0) { + if (selectedSkill >= 0) + { int skillId = mSkillList[selectedSkill]->id; bool modifiable; @@ -201,10 +210,12 @@ void SkillDialog::update() mIncButton->setEnabled(modifiable && player_node->mSkillPoint > 0); - } else + } + else mIncButton->setEnabled(false); mTableModel->update(); + setMinHeight(50 + mTableModel->getHeight()); } int SkillDialog::getNumberOfElements() @@ -214,10 +225,10 @@ int SkillDialog::getNumberOfElements() bool SkillDialog::hasSkill(int id) { - for (unsigned int i = 0; i < mSkillList.size(); i++) { - if (mSkillList[i]->id == id) { + for (unsigned int i = 0; i < mSkillList.size(); i++) + { + if (mSkillList[i]->id == id) return true; - } } return false; } @@ -233,8 +244,10 @@ void SkillDialog::addSkill(int id, int lvl, int mp) void SkillDialog::setSkill(int id, int lvl, int mp) { - for (unsigned int i = 0; i < mSkillList.size(); i++) { - if (mSkillList[i]->id == id) { + for (unsigned int i = 0; i < mSkillList.size(); i++) + { + if (mSkillList[i]->id == id) + { mSkillList[i]->lv = lvl; mSkillList[i]->sp = mp; } @@ -271,7 +284,8 @@ static void initSkillinfo() std::string name = XML::getProperty(node, "name", ""); bool modifiable = !atoi(XML::getProperty(node, "fixed", "0").c_str()); - if (index >= 0) { + if (index >= 0) + { skill_db.resize(index + 1, emptySkillInfo); skill_db[index].name = name; skill_db[index].modifiable = modifiable; diff --git a/src/gui/skill.h b/src/gui/skill.h index 893a61e7..0600d106 100644 --- a/src/gui/skill.h +++ b/src/gui/skill.h @@ -24,19 +24,16 @@ #include <vector> -#include <guichan/listmodel.hpp> #include <guichan/actionlistener.hpp> #include "window.h" -#include "table.h" - -#include "../guichanfwd.h" struct SKILL { short id; /**< Index into "skill_db" array */ short lv, sp; }; +class GuiTable; class SkillGuiTableModel; /** @@ -71,7 +68,8 @@ class SkillDialog : public Window, public gcn::ActionListener const std::vector<SKILL*>& getSkills() const { return mSkillList; } private: - GuiTable mTable;//gcn::ListBox *mSkillListBox; + GuiTable *mTable; + ScrollArea *skillScrollArea; SkillGuiTableModel *mTableModel; gcn::Label *mPointsLabel; gcn::Button *mIncButton; diff --git a/src/gui/slider.cpp b/src/gui/slider.cpp index 37136012..9bfa840f 100644 --- a/src/gui/slider.cpp +++ b/src/gui/slider.cpp @@ -21,6 +21,7 @@ #include "slider.h" +#include "../configuration.h" #include "../graphics.h" #include "../resources/image.h" @@ -28,6 +29,7 @@ Image *Slider::hStart, *Slider::hMid, *Slider::hEnd, *Slider::hGrip; Image *Slider::vStart, *Slider::vMid, *Slider::vEnd, *Slider::vGrip; +float Slider::mAlpha = config.getValue("guialpha", 0.8); int Slider::mInstances = 0; Slider::Slider(double scaleEnd): @@ -107,6 +109,20 @@ void Slider::draw(gcn::Graphics *graphics) int x = 0; int y = (h - hStart->getHeight()) / 2; + if (config.getValue("guialpha", 0.8) != mAlpha) + { + mAlpha = config.getValue("guialpha", 0.8); + hStart->setAlpha(mAlpha); + hMid->setAlpha(mAlpha); + hEnd->setAlpha(mAlpha); + hGrip->setAlpha(mAlpha); + + vStart->setAlpha(mAlpha); + vMid->setAlpha(mAlpha); + vEnd->setAlpha(mAlpha); + vGrip->setAlpha(mAlpha); + } + static_cast<Graphics*>(graphics)->drawImage(hStart, x, y); w -= hStart->getWidth() + hEnd->getWidth(); diff --git a/src/gui/slider.h b/src/gui/slider.h index 1fe668c5..56ea334a 100644 --- a/src/gui/slider.h +++ b/src/gui/slider.h @@ -26,7 +26,6 @@ class Image; - /** * Slider widget. Same as the Guichan slider but with custom look. * @@ -67,6 +66,7 @@ class Slider : public gcn::Slider { static Image *hStart, *hMid, *hEnd, *hGrip; static Image *vStart, *vMid, *vEnd, *vGrip; + static float mAlpha; static int mInstances; }; diff --git a/src/gui/speechbubble.cpp b/src/gui/speechbubble.cpp new file mode 100644 index 00000000..dd404a63 --- /dev/null +++ b/src/gui/speechbubble.cpp @@ -0,0 +1,95 @@ +/* + * Speech bubbles + * Copyright (C) 2008 The Legend of Mazzeroth Development Team + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <guichan/font.hpp> + +#include <guichan/widgets/label.hpp> + +#include "gui.h" +#include "scrollarea.h" +#include "speechbubble.h" +#include "textbox.h" + +#include "../utils/gettext.h" + +SpeechBubble::SpeechBubble(): + Window(_("Speech"), false, NULL, "graphics/gui/speechbubble.xml") +{ + setContentSize(140, 46); + setShowTitle(false); + setTitleBarHeight(0); + + mCaption = new gcn::Label(""); + mCaption->setFont(boldFont); + mCaption->setPosition(5, 3); + + mSpeechBox = new TextBox(); + mSpeechBox->setEditable(false); + mSpeechBox->setOpaque(false); + + mSpeechArea = new ScrollArea(mSpeechBox); + + mSpeechArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mSpeechArea->setVerticalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mSpeechArea->setDimension(gcn::Rectangle(4, boldFont->getHeight() + 3, + 130, 28)); + mSpeechArea->setOpaque(false); + + add(mCaption); + add(mSpeechArea); + + setLocationRelativeTo(getParent()); +} + +SpeechBubble::~SpeechBubble() +{ + delete mCaption; + delete mSpeechBox; + delete mSpeechArea; +} + +void SpeechBubble::setCaption(const std::string &name, const gcn::Color &color) +{ + mCaption->setCaption(name); + mCaption->adjustSize(); + mCaption->setForegroundColor(color); +} + +void SpeechBubble::setText(std::string mText) +{ + int width = mCaption->getWidth() + 3; + mSpeechBox->setTextWrapped(mText, 130 > width ? 130 : width); + + const int fontHeight = getFont()->getHeight(); + const int numRows = mSpeechBox->getNumberOfRows() + 1; + + if (width < mSpeechBox->getMinWidth()) + width = mSpeechBox->getMinWidth(); + + setContentSize(width + fontHeight, (numRows * fontHeight) + 6); + mSpeechArea->setDimension(gcn::Rectangle(4, fontHeight + 3, width + 5, + (numRows * fontHeight))); +} + +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..d2d81332 --- /dev/null +++ b/src/gui/speechbubble.h @@ -0,0 +1,48 @@ +/* + * Speech bubbles + * Copyright (C) 2008 The Legend of Mazzeroth Development Team + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef SPEECHBUBBLE_H +#define SPEECHBUBBLE_H + +#include "window.h" + +class ScrollArea; +class TextBox; + +class SpeechBubble : public Window +{ + public: + SpeechBubble(); + ~SpeechBubble(); + + void setCaption(const std::string &name, + const gcn::Color &color = 0x000000); + void setText(std::string mText); + void setLocation(int x, int y); + unsigned int getNumRows(); + + private: + gcn::Label *mCaption; + TextBox *mSpeechBox; + ScrollArea *mSpeechArea; +}; + +#endif diff --git a/src/gui/status.cpp b/src/gui/status.cpp index 39a298f7..683a9a41 100644 --- a/src/gui/status.cpp +++ b/src/gui/status.cpp @@ -19,14 +19,15 @@ * 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 "widgets/layout.h" + #include "../localplayer.h" #include "../utils/gettext.h" @@ -37,87 +38,30 @@ StatusWindow::StatusWindow(LocalPlayer *player): Window(player->getName()), mPlayer(player) { - setWindowName("Status"); - setResizable(true); + setWindowName(_("Status")); setCloseButton(true); setDefaultSize((windowContainer->getWidth() - 365) / 2, - (windowContainer->getHeight() - 255) / 2, 365, 275); - loadWindowState(); + (windowContainer->getHeight() - 255) / 2, 400, 345); // ---------------------- // Status Part // ---------------------- mLvlLabel = new gcn::Label(strprintf(_("Level: %d"), 0)); - mGpLabel = new gcn::Label(strprintf(_("Job: %d"), 0)); - mJobLvlLabel = new gcn::Label(strprintf(_("Money: %d GP"), 0)); + mJobLvlLabel = new gcn::Label(strprintf(_("Job: %d"), 0)); + mGpLabel = new gcn::Label(strprintf(_("Money: %d GP"), 0)); mHpLabel = new gcn::Label(_("HP:")); mHpBar = new ProgressBar(1.0f, 80, 15, 0, 171, 34); - mHpValueLabel = new gcn::Label; mXpLabel = new gcn::Label(_("Exp:")); mXpBar = new ProgressBar(1.0f, 80, 15, 143, 192, 211); - mXpValueLabel = new gcn::Label; mMpLabel = new gcn::Label(_("MP:")); mMpBar = new ProgressBar(1.0f, 80, 15, 26, 102, 230); - mMpValueLabel = new gcn::Label; - - mJobXpLabel = new gcn::Label(_("Job:")); - mJobXpBar = new ProgressBar(1.0f, 80, 15, 220, 135, 203); - mJobValueLabel = new gcn::Label; - - int y = 3; - int x = 5; - - mLvlLabel->setPosition(x, y); - x += mLvlLabel->getWidth() + 40; - mJobLvlLabel->setPosition(x, y); - x += mJobLvlLabel->getWidth() + 40; - mGpLabel->setPosition(x, y); - - y += mLvlLabel->getHeight() + 5; // Next Row - x = 5; - - mHpLabel->setPosition(x, y); - x += mHpLabel->getWidth() + 5; - mHpBar->setPosition(x, y); - x += mHpBar->getWidth() + 5; - mHpValueLabel->setPosition(x, y); - - mXpLabel->setPosition(175, y); - mXpBar->setPosition(205, y); - mXpValueLabel->setPosition(290, y); - - y += mHpLabel->getHeight() + 5; // Next Row - x = 5; - - mMpLabel->setPosition(x, y); - x += mMpLabel->getWidth() + 5; - mMpBar->setPosition(x, y); - x += mMpBar->getWidth() + 5; - mMpValueLabel->setPosition(x, y); - - mJobXpLabel->setPosition(175, y); - mJobXpBar->setPosition(205, y); - mJobValueLabel->setPosition(290, y); - - add(mLvlLabel); - add(mJobLvlLabel); - add(mGpLabel); - add(mHpLabel); - add(mHpValueLabel); - add(mMpLabel); - add(mMpValueLabel); - add(mXpLabel); - add(mXpValueLabel); - add(mJobXpLabel); - add(mJobValueLabel); - add(mHpBar); - add(mMpBar); - add(mXpBar); - add(mJobXpBar); + + mJobLabel = new gcn::Label(_("Job:")); + mJobBar = new ProgressBar(1.0f, 80, 15, 220, 135, 203); // ---------------------- // Stats Part @@ -127,6 +71,7 @@ StatusWindow::StatusWindow(LocalPlayer *player): gcn::Label *mStatsTitleLabel = new gcn::Label(_("Stats")); gcn::Label *mStatsTotalLabel = new gcn::Label(_("Total")); gcn::Label *mStatsCostLabel = new gcn::Label(_("Cost")); + mStatsTotalLabel->setAlignment(gcn::Graphics::CENTER); // Derived Stats mStatsAttackLabel = new gcn::Label(_("Attack:")); @@ -149,10 +94,13 @@ StatusWindow::StatusWindow(LocalPlayer *player): mStatsReflexPoints = new gcn::Label; // New labels - for (int i = 0; i < 6; i++) { - mStatsLabel[i] = new gcn::Label; + for (int i = 0; i < 6; i++) + { + mStatsLabel[i] = new gcn::Label("0"); + mStatsLabel[i]->setAlignment(gcn::Graphics::CENTER); mStatsDisplayLabel[i] = new gcn::Label; mPointsLabel[i] = new gcn::Label("0"); + mPointsLabel[i]->setAlignment(gcn::Graphics::CENTER); } mRemainingStatsPointsLabel = new gcn::Label; @@ -164,68 +112,53 @@ StatusWindow::StatusWindow(LocalPlayer *player): mStatsButton[4] = new Button("+", "DEX", this); mStatsButton[5] = new Button("+", "LUK", this); - - // Set position - mStatsTitleLabel->setPosition(mMpLabel->getX(), mMpLabel->getY() + 23 ); - mStatsTotalLabel->setPosition(110, mStatsTitleLabel->getY() + 15); - int totalLabelY = mStatsTotalLabel->getY(); - mStatsCostLabel->setPosition(170, totalLabelY); - + // Assemble + ContainerPlacer place; + place = getPlacer(0, 0); + + place(0, 0, mLvlLabel, 3); + place(5, 0, mJobLvlLabel, 3); + place(8, 0, mGpLabel, 3); + place(1, 1, mHpLabel).setPadding(3); + place(2, 1, mHpBar, 3); + place(6, 1, mXpLabel).setPadding(3); + place(7, 1, mXpBar, 3); + place(1, 2, mMpLabel).setPadding(3); + place(2, 2, mMpBar, 3); + place(6, 2, mJobLabel).setPadding(3); + place(7, 2, mJobBar, 3); + place.getCell().matchColWidth(0, 1); + place = getPlacer(0, 3); + place(0, 0, mStatsTitleLabel, 5); + place(5, 1, mStatsTotalLabel, 5); + place(12, 1, mStatsCostLabel, 5); for (int i = 0; i < 6; i++) { - mStatsLabel[i]->setPosition(5, mStatsTotalLabel->getY() + (i * 23) + 15); - mStatsDisplayLabel[i]->setPosition(115, - totalLabelY + (i * 23) + 15); - mStatsButton[i]->setPosition(145, totalLabelY + (i * 23) + 10); - mPointsLabel[i]->setPosition(175, totalLabelY + (i * 23) + 15); + place(0, 2 + i, mStatsLabel[i], 7).setPadding(5); + place(7, 2 + i, mStatsDisplayLabel[i]).setPadding(5); + place(10, 2 + i, mStatsButton[i]); + place(12, 2 + i, mPointsLabel[i]).setPadding(5); } + place(14, 2, mStatsAttackLabel, 7).setPadding(5); + place(14, 3, mStatsDefenseLabel, 7).setPadding(5); + place(14, 4, mStatsMagicAttackLabel, 7).setPadding(5); + place(14, 5, mStatsMagicDefenseLabel, 7).setPadding(5); + place(14, 6, mStatsAccuracyLabel, 7).setPadding(5); + place(14, 7, mStatsEvadeLabel, 7).setPadding(5); + place(14, 8, mStatsReflexLabel, 7).setPadding(5); + place(21, 2, mStatsAttackPoints, 3).setPadding(5); + place(21, 3, mStatsDefensePoints, 3).setPadding(5); + place(21, 4, mStatsMagicAttackPoints, 3).setPadding(5); + place(21, 5, mStatsMagicDefensePoints, 3).setPadding(5); + place(21, 6, mStatsAccuracyPoints, 3).setPadding(5); + place(21, 7, mStatsEvadePoints, 3).setPadding(5); + place(21, 8, mStatsReflexPoints, 3).setPadding(5); + place(0, 8, mRemainingStatsPointsLabel, 3).setPadding(5); + + Layout &layout = getLayout(); + layout.setRowHeight(0, Layout::AUTO_SET); - mRemainingStatsPointsLabel->setPosition(5, mPointsLabel[5]->getY() + 25); - - mStatsAttackLabel->setPosition(220, mStatsLabel[0]->getY()); - mStatsDefenseLabel->setPosition(220, mStatsLabel[1]->getY()); - mStatsMagicAttackLabel->setPosition(220, mStatsLabel[2]->getY()); - mStatsMagicDefenseLabel->setPosition(220, mStatsLabel[3]->getY()); - mStatsAccuracyLabel->setPosition(220, mStatsLabel[4]->getY()); - mStatsEvadeLabel->setPosition(220, mStatsLabel[5]->getY()); - mStatsReflexLabel->setPosition(220, mRemainingStatsPointsLabel->getY()); - - mStatsAttackPoints->setPosition(310, mStatsLabel[0]->getY()); - mStatsDefensePoints->setPosition(310, mStatsLabel[1]->getY()); - mStatsMagicAttackPoints->setPosition(310, mStatsLabel[2]->getY()); - mStatsMagicDefensePoints->setPosition(310, mStatsLabel[3]->getY()); - mStatsAccuracyPoints->setPosition(310, mStatsLabel[4]->getY()); - mStatsEvadePoints->setPosition(310, mStatsLabel[5]->getY()); - mStatsReflexPoints->setPosition(310, mRemainingStatsPointsLabel->getY()); - - // Assemble - add(mStatsTitleLabel); - add(mStatsTotalLabel); - add(mStatsCostLabel); - for(int i = 0; i < 6; i++) - { - add(mStatsLabel[i]); - add(mStatsDisplayLabel[i]); - add(mStatsButton[i]); - add(mPointsLabel[i]); - } - add(mStatsAttackLabel); - add(mStatsDefenseLabel); - add(mStatsMagicAttackLabel); - add(mStatsMagicDefenseLabel); - add(mStatsAccuracyLabel); - add(mStatsEvadeLabel); - add(mStatsReflexLabel); - - add(mStatsAttackPoints); - add(mStatsDefensePoints); - add(mStatsMagicAttackPoints); - add(mStatsMagicDefensePoints); - add(mStatsAccuracyPoints); - add(mStatsEvadePoints); - add(mStatsReflexPoints); - - add(mRemainingStatsPointsLabel); + loadWindowState(); } void StatusWindow::update() @@ -241,21 +174,17 @@ void StatusWindow::update() mGpLabel->setCaption(strprintf(_("Money: %d GP"), mPlayer->mGp)); mGpLabel->adjustSize(); - mHpValueLabel->setCaption(toString(mPlayer->mHp) + - "/" + toString(mPlayer->mMaxHp)); - mHpValueLabel->adjustSize(); + mHpBar->setText(toString(mPlayer->mHp) + + "/" + toString(mPlayer->mMaxHp)); - mMpValueLabel->setCaption(toString(mPlayer->mMp) + - "/" + toString(mPlayer->mMaxMp)); - mMpValueLabel->adjustSize(); + mMpBar->setText(toString(mPlayer->mMp) + + "/" + toString(mPlayer->mMaxMp)); - mXpValueLabel->setCaption(toString(mPlayer->getXp()) + - "/" + toString(mPlayer->mXpForNextLevel)); - mXpValueLabel->adjustSize(); + mXpBar->setText(toString(mPlayer->getXp()) + + "/" + toString(mPlayer->mXpForNextLevel)); - mJobValueLabel->setCaption(toString(mPlayer->mJobXp) + - "/" + toString(mPlayer->mJobXpForNextLevel)); - mJobValueLabel->adjustSize(); + mJobBar->setText(toString(mPlayer->mJobXp) + + "/" + toString(mPlayer->mJobXpForNextLevel)); // HP Bar coloration if (mPlayer->mHp < int(mPlayer->mMaxHp / 3)) @@ -276,7 +205,7 @@ void StatusWindow::update() mXpBar->setProgress( (float) mPlayer->getXp() / (float) mPlayer->mXpForNextLevel); - mJobXpBar->setProgress( + mJobBar->setProgress( (float) mPlayer->mJobXp / (float) mPlayer->mJobXpForNextLevel); // Stats Part @@ -341,30 +270,6 @@ void StatusWindow::update() // Reflex % mStatsReflexPoints->setCaption(toString(mPlayer->DEX / 4)); // + counter mStatsReflexPoints->adjustSize(); - - // Update Second column widgets position - mJobLvlLabel->setPosition(mLvlLabel->getX() + mLvlLabel->getWidth() + 20, - mLvlLabel->getY()); - mGpLabel->setPosition(mJobLvlLabel->getX() + mJobLvlLabel->getWidth() + 20, - mJobLvlLabel->getY()); - - mXpLabel->setPosition( - mHpValueLabel->getX() + mHpValueLabel->getWidth() + 10, - mHpLabel->getY()); - mXpBar->setPosition( - mXpLabel->getX() + mXpLabel->getWidth() + 5, - mXpLabel->getY()); - mXpValueLabel->setPosition( - mXpBar->getX() + mXpBar->getWidth() + 5, - mXpLabel->getY()); - - mJobXpLabel->setPosition(mXpBar->getX() - mJobXpLabel->getWidth() - 5, - mMpLabel->getY()); - mJobXpBar->setPosition( - mJobXpLabel->getX() + mJobXpLabel->getWidth() + 5, - mJobXpLabel->getY()); - mJobValueLabel->setPosition(mJobXpBar->getX() + mJobXpBar->getWidth() + 5, - mJobXpLabel->getY()); } void StatusWindow::draw(gcn::Graphics *g) diff --git a/src/gui/status.h b/src/gui/status.h index 6d360caf..00a48f4e 100644 --- a/src/gui/status.h +++ b/src/gui/status.h @@ -22,18 +22,13 @@ #ifndef STATUS_H #define STATUS_H -#include <iosfwd> - #include <guichan/actionlistener.hpp> #include "window.h" -#include "../guichanfwd.h" - class LocalPlayer; class ProgressBar; - /** * The player status dialog. * @@ -70,11 +65,9 @@ class StatusWindow : public Window, public gcn::ActionListener */ gcn::Label *mLvlLabel, *mJobLvlLabel; gcn::Label *mGpLabel; - gcn::Label *mHpLabel, *mHpValueLabel; - gcn::Label *mMpLabel, *mMpValueLabel; - gcn::Label *mXpLabel, *mXpValueLabel, *mJobXpLabel, *mJobValueLabel; + gcn::Label *mHpLabel, *mMpLabel, *mXpLabel, *mJobLabel; ProgressBar *mHpBar, *mMpBar; - ProgressBar *mXpBar, *mJobXpBar; + ProgressBar *mXpBar, *mJobBar; /** * Derived Statistics captions diff --git a/src/gui/table.cpp b/src/gui/table.cpp index f6678737..567272f0 100644 --- a/src/gui/table.cpp +++ b/src/gui/table.cpp @@ -19,13 +19,18 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <guichan/actionlistener.hpp> +#include <guichan/graphics.hpp> +#include <guichan/key.hpp> + +#include "color.h" #include "table.h" -#include <guichan/graphics.hpp> -#include <guichan/actionlistener.hpp> +#include "../configuration.h" -#include <cassert> +#include "../utils/dtor.h" +float GuiTable::mAlpha = config.getValue("guialpha", 0.8); class GuiTableActionListener : public gcn::ActionListener { @@ -50,7 +55,8 @@ GuiTableActionListener::GuiTableActionListener(GuiTable *table, gcn::Widget *wid mColumn(column), mWidget(widget) { - if (widget) { + if (widget) + { widget->addActionListener(this); widget->_setParent(table); } @@ -58,7 +64,8 @@ GuiTableActionListener::GuiTableActionListener(GuiTable *table, gcn::Widget *wid GuiTableActionListener::~GuiTableActionListener() { - if (mWidget) { + if (mWidget) + { mWidget->removeActionListener(this); mWidget->_setParent(NULL); } @@ -71,14 +78,20 @@ void GuiTableActionListener::action(const gcn::ActionEvent& actionEvent) } -GuiTable::GuiTable(TableModel *initial_model) : +GuiTable::GuiTable(TableModel *initial_model, gcn::Color background, + bool opacity) : mLinewiseMode(false), + mWrappingEnabled(false), + mOpaque(opacity), + mBackgroundColor(background), mModel(NULL), mSelectedRow(0), mSelectedColumn(0), mTopWidget(NULL) { setModel(initial_model); + setFocusable(true); + addMouseListener(this); addKeyListener(this); } @@ -95,7 +108,8 @@ TableModel *GuiTable::getModel() const void GuiTable::setModel(TableModel *new_model) { - if (mModel) { + if (mModel) + { uninstallActionListeners(); mModel->removeListener(this); } @@ -103,13 +117,13 @@ void GuiTable::setModel(TableModel *new_model) mModel = new_model; installActionListeners(); - if (new_model) { + if (new_model) + { new_model->installListener(this); recomputeDimensions(); } } - void GuiTable::recomputeDimensions() { int rows_nr = mModel->getRows(); @@ -169,10 +183,62 @@ int GuiTable::getColumnWidth(int i) return 0; } -void GuiTable::uninstallActionListeners() +void GuiTable::setSelectedRow(int selected) { - for (std::vector<GuiTableActionListener *>::const_iterator it = action_listeners.begin(); it != action_listeners.end(); it++) - delete *it; + if (!mModel) + { + mSelectedRow = -1; + } + else + { + if (selected < 0 && !mWrappingEnabled) + { + mSelectedRow = -1; + } + else if (selected >= mModel->getRows() && mWrappingEnabled) + { + mSelectedRow = 0; + } + else if ((selected >= mModel->getRows() && !mWrappingEnabled) || + (selected < 0 && mWrappingEnabled)) + { + mSelectedRow = mModel->getRows() - 1; + } + else + { + mSelectedRow = selected; + } + } +} + +void GuiTable::setSelectedColumn(int selected) +{ + if (!mModel) + { + mSelectedColumn = -1; + } + else + { + if ((selected >= mModel->getColumns() && mWrappingEnabled) || + (selected < 0 && !mWrappingEnabled)) + { + mSelectedColumn = 0; + } + else if ((selected >= mModel->getColumns() && !mWrappingEnabled) || + (selected < 0 && mWrappingEnabled)) + { + mSelectedColumn = mModel->getColumns() - 1; + } + else + { + mSelectedColumn = selected; + } + } +} + +void GuiTable::uninstallActionListeners(void) +{ + delete_all(action_listeners); action_listeners.clear(); } @@ -185,10 +251,11 @@ void GuiTable::installActionListeners() int columns = mModel->getColumns(); for (int row = 0; row < rows; ++row) - for (int column = 0; column < columns; ++column) { + for (int column = 0; column < columns; ++column) + { gcn::Widget *widget = mModel->getElementAt(row, column); action_listeners.push_back(new GuiTableActionListener(this, widget, - row, column)); + row, column)); } _setFocusHandler(_getFocusHandler()); // propagate focus handler to widgets @@ -197,12 +264,22 @@ void GuiTable::installActionListeners() // -- widget ops void GuiTable::draw(gcn::Graphics* graphics) { - graphics->setColor(getBackgroundColor()); - graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight())); - if (!mModel) return; + if (config.getValue("guialpha", 0.8) != mAlpha) + mAlpha = config.getValue("guialpha", 0.8); + + if (mOpaque) + { + const int red = getBackgroundColor().r; + const int green = getBackgroundColor().g; + const int blue = getBackgroundColor().b; + const int alpha = (int)(mAlpha * 255.0f); + graphics->setColor(gcn::Color(red, green, blue, alpha)); + graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight())); + } + // First, determine how many rows we need to draw, and where we should start. int first_row = -(getY() / getRowHeight()); @@ -224,44 +301,68 @@ void GuiTable::draw(gcn::Graphics* graphics) int height = getRowHeight(); int y_offset = first_row * height; - for (int r = first_row; r < first_row + rows_nr; ++r) { + for (int r = first_row; r < first_row + rows_nr; ++r) + { int x_offset = 0; - for (int c = first_column; c <= last_column; ++c) { + for (int c = first_column; c <= last_column; ++c) + { gcn::Widget *widget = mModel->getElementAt(r, c); int width = getColumnWidth(c); - if (widget) { + if (widget) + { gcn::Rectangle bounds(x_offset, y_offset, width, height); - if (widget == mTopWidget) { + if (widget == mTopWidget) + { bounds.height = widget->getHeight(); bounds.width = widget->getWidth(); } widget->setDimension(bounds); + if (!mLinewiseMode && c == mSelectedColumn && r == mSelectedRow) + { + bool valid; + const int red = + (textColor->getColor('H', valid) >> 16) & 0xFF; + const int green = + (textColor->getColor('H', valid) >> 8) & 0xFF; + const int blue = textColor->getColor('H', valid) & 0xFF; + const int alpha = (int)(mAlpha * 127.0f); + + graphics->setColor(gcn::Color(red, green, blue, alpha)); + graphics->fillRectangle(bounds); + } + graphics->pushClipArea(bounds); widget->draw(graphics); graphics->popClipArea(); - - if (!mLinewiseMode - && c == mSelectedColumn - && r == mSelectedRow) - graphics->drawRectangle(bounds); } x_offset += width; } - if (mLinewiseMode - && r == mSelectedRow) - graphics->drawRectangle(gcn::Rectangle(0, y_offset, + if (mLinewiseMode && r == mSelectedRow) + { + bool valid; + const int red = + (textColor->getColor('H', valid) >> 16) & 0xFF; + const int green = + (textColor->getColor('H', valid) >> 8) & 0xFF; + const int blue = textColor->getColor('H', valid) & 0xFF; + const int alpha = (int)(mAlpha * 127.0f); + + graphics->setColor(gcn::Color(red, green, blue, alpha)); + graphics->fillRectangle(gcn::Rectangle(0, y_offset, x_offset, height)); + } y_offset += height; } - if (mTopWidget) { + if (mTopWidget) + { gcn::Rectangle bounds = mTopWidget->getDimension(); graphics->pushClipArea(bounds); mTopWidget->draw(graphics); @@ -269,21 +370,17 @@ void GuiTable::draw(gcn::Graphics* graphics) } } -void GuiTable::logic() -{ -} - void GuiTable::moveToTop(gcn::Widget *widget) { gcn::Widget::moveToTop(widget); - this->mTopWidget = widget; + mTopWidget = widget; } void GuiTable::moveToBottom(gcn::Widget *widget) { gcn::Widget::moveToBottom(widget); - if (widget == this->mTopWidget) - this->mTopWidget = NULL; + if (widget == mTopWidget) + mTopWidget = NULL; } gcn::Rectangle GuiTable::getChildrenArea() @@ -294,17 +391,62 @@ gcn::Rectangle GuiTable::getChildrenArea() // -- KeyListener notifications void GuiTable::keyPressed(gcn::KeyEvent& keyEvent) { -} + gcn::Key key = keyEvent.getKey(); + + if (key.getValue() == gcn::Key::ENTER || key.getValue() == gcn::Key::SPACE) + { + distributeActionEvent(); + keyEvent.consume(); + } + else if (key.getValue() == gcn::Key::UP) + { + setSelectedRow(mSelectedRow - 1); + keyEvent.consume(); + } + else if (key.getValue() == gcn::Key::DOWN) + { + setSelectedRow(mSelectedRow + 1); + + keyEvent.consume(); + } + else if (key.getValue() == gcn::Key::LEFT) + { + setSelectedColumn(mSelectedColumn - 1); + + keyEvent.consume(); + } + else if (key.getValue() == gcn::Key::RIGHT) + { + setSelectedColumn(mSelectedColumn + 1); + + keyEvent.consume(); + } + else if (key.getValue() == gcn::Key::HOME) + { + setSelectedRow(0); + setSelectedColumn(0); + keyEvent.consume(); + } + else if (key.getValue() == gcn::Key::END) + { + setSelectedRow(mModel->getRows() - 1); + setSelectedColumn(mModel->getColumns() - 1); + keyEvent.consume(); + } +} // -- MouseListener notifications void GuiTable::mousePressed(gcn::MouseEvent& mouseEvent) { - if (mouseEvent.getButton() == gcn::MouseEvent::LEFT) { + if (mouseEvent.getButton() == gcn::MouseEvent::LEFT) + { int row = getRowForY(mouseEvent.getY()); int column = getColumnForX(mouseEvent.getX()); - if (row > -1 && column > -1) { + if (row > -1 && column > -1 && + row < mModel->getRows() && column < mModel->getColumns()) + { mSelectedColumn = column; mSelectedRow = row; } @@ -315,10 +457,25 @@ void GuiTable::mousePressed(gcn::MouseEvent& mouseEvent) void GuiTable::mouseWheelMovedUp(gcn::MouseEvent& mouseEvent) { + if (isFocused()) + { + if (getSelectedRow() >= 0 ) + { + setSelectedRow(getSelectedRow() - 1); + } + + mouseEvent.consume(); + } } void GuiTable::mouseWheelMovedDown(gcn::MouseEvent& mouseEvent) { + if (isFocused()) + { + setSelectedRow(getSelectedRow() + 1); + + mouseEvent.consume(); + } } void GuiTable::mouseDragged(gcn::MouseEvent& mouseEvent) @@ -328,10 +485,13 @@ void GuiTable::mouseDragged(gcn::MouseEvent& mouseEvent) // -- TableModelListener notifications void GuiTable::modelUpdated(bool completed) { - if (completed) { + if (completed) + { recomputeDimensions(); installActionListeners(); - } else { // before the update? + } + else + { // before the update? mTopWidget = NULL; // No longer valid in general uninstallActionListeners(); } @@ -342,18 +502,18 @@ gcn::Widget *GuiTable::getWidgetAt(int x, int y) int row = getRowForY(y); int column = getColumnForX(x); - if (mTopWidget - && mTopWidget->getDimension().isPointInRect(x, y)) + if (mTopWidget && mTopWidget->getDimension().isPointInRect(x, y)) return mTopWidget; - if (row > -1 - && column > -1) { + if (row > -1 && column > -1) + { gcn::Widget *w = mModel->getElementAt(row, column); if (w && w->isFocusable()) return w; else return NULL; // Grab the event locally - } else + } + else return NULL; } @@ -361,8 +521,7 @@ int GuiTable::getRowForY(int y) { int row = y / getRowHeight(); - if (row < 0 - || row >= mModel->getRows()) + if (row < 0 || row >= mModel->getRows()) return -1; else return row; @@ -373,20 +532,19 @@ int GuiTable::getColumnForX(int x) int column; int delta = 0; - for (column = 0; column < mModel->getColumns(); column++) { + for (column = 0; column < mModel->getColumns(); column++) + { delta += getColumnWidth(column); if (x <= delta) break; } - if (column < 0 - || column >= mModel->getColumns()) + if (column < 0 || column >= mModel->getColumns()) return -1; else return column; } - void GuiTable::_setFocusHandler(gcn::FocusHandler* focusHandler) { gcn::Widget::_setFocusHandler(focusHandler); diff --git a/src/gui/table.h b/src/gui/table.h index c7ede36c..841f6ef2 100644 --- a/src/gui/table.h +++ b/src/gui/table.h @@ -24,14 +24,11 @@ #include <vector> -#include <guichan/gui.hpp> #include <guichan/keylistener.hpp> #include <guichan/mouselistener.hpp> -#include <guichan/platform.hpp> #include <guichan/widget.hpp> #include "table_model.h" -#include "../guichanfwd.h" class GuiTableActionListener; @@ -53,7 +50,8 @@ class GuiTable : public gcn::Widget, friend class GuiTableActionListener; public: - GuiTable(TableModel * initial_model = NULL); + GuiTable(TableModel * initial_model = NULL, gcn::Color background = 0xffffff, + bool opacity = true); virtual ~GuiTable(); @@ -67,25 +65,36 @@ public: * * Note that actions issued by widgets returned from the model will update * the table selection, but only AFTER any event handlers installed within - * the widget have been triggered. To be notified after such an update, - * add an action listener to the table instead. + * the widget have been triggered. To be notified after such an update, add + * an action listener to the table instead. */ void setModel(TableModel *m); + const TableModel* getModel() {return mModel;} + void setSelected(int row, int column); int getSelectedRow(); int getSelectedColumn(); - gcn::Rectangle getChildrenArea(); + void setSelectedRow(int selected); + + void setSelectedColumn(int selected); + + bool isWrappingEnabled() const {return mWrappingEnabled;} + + void setWrappingEnabled(bool wrappingEnabled) + {mWrappingEnabled = wrappingEnabled;} + + gcn::Rectangle getChildrenArea(void); /** - * Toggle whether to use linewise selection mode, in which the table - * selects an entire line at a time, rather than a single cell. + * Toggle whether to use linewise selection mode, in which the table selects + * an entire line at a time, rather than a single cell. * - * Note that column information is tracked even in linewise selection - * mode; this mode therefore only affects visualisation. + * Note that column information is tracked even in linewise selection mode; + * this mode therefore only affects visualisation. * * Disabled by default. * @@ -96,8 +105,6 @@ public: // Inherited from Widget virtual void draw(gcn::Graphics* graphics); - virtual void logic(); - virtual gcn::Widget *getWidgetAt(int x, int y); virtual void moveToTop(gcn::Widget *child); @@ -109,6 +116,21 @@ public: // Inherited from KeyListener virtual void keyPressed(gcn::KeyEvent& keyEvent); + /** + * Sets the table to be opaque, that is sets the table + * to display its background. + * + * @param opaque True if the table should be opaque, false otherwise. + */ + virtual void setOpaque(bool opaque) {mOpaque = opaque;} + + /** + * Checks if the table is opaque, that is if the table area displays its + * background. + * + * @return True if the table is opaque, false otherwise. + */ + virtual bool isOpaque() const {return mOpaque;} // Inherited from MouseListener virtual void mousePressed(gcn::MouseEvent& mouseEvent); @@ -136,6 +158,15 @@ private: int getColumnForX(int x); // -1 on error void recomputeDimensions(); bool mLinewiseMode; + bool mWrappingEnabled; + bool mOpaque; + + static float mAlpha; + + /** + * Holds the background color of the table. + */ + gcn::Color mBackgroundColor; TableModel *mModel; diff --git a/src/gui/table_model.cpp b/src/gui/table_model.cpp index 7bc29b47..4fa13bae 100644 --- a/src/gui/table_model.cpp +++ b/src/gui/table_model.cpp @@ -19,10 +19,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "table_model.h" - #include <guichan/widget.hpp> -#include <cstdlib> + +#include "table_model.h" #include "../utils/dtor.h" @@ -49,7 +48,6 @@ void TableModel::signalAfterUpdate() } - #define WIDGET_AT(row, column) (((row) * mColumns) + (column)) #define DYN_SIZE(h) ((h) >= 0) // determines whether this size is tagged for auto-detection @@ -143,3 +141,21 @@ int StaticTableModel::getColumns() { return mColumns; } + +int StaticTableModel::getWidth(void) +{ + int width = 0; + + for (unsigned int i = 0; i < mWidths.size(); i++) + { + width += mWidths[i]; + } + + return width; +} + +int StaticTableModel::getHeight(void) +{ + return (mColumns * mHeight); +} + diff --git a/src/gui/table_model.h b/src/gui/table_model.h index d245d7bd..9ca36120 100644 --- a/src/gui/table_model.h +++ b/src/gui/table_model.h @@ -22,10 +22,6 @@ #ifndef TABLE_MODEL_H #define TABLE_MODEL_H -#include "../guichanfwd.h" - -#include <guichan/gui.hpp> - #include <set> #include <vector> @@ -133,6 +129,8 @@ public: virtual int getRows(); virtual int getColumns(); virtual int getRowHeight(); + virtual int getWidth(); + virtual int getHeight(); virtual int getColumnWidth(int index); virtual gcn::Widget *getElementAt(int row, int column); diff --git a/src/gui/textbox.cpp b/src/gui/textbox.cpp index 75f0b5a1..2a86d549 100644 --- a/src/gui/textbox.cpp +++ b/src/gui/textbox.cpp @@ -19,21 +19,21 @@ * 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() { setOpaque(false); setFrameSize(0); + mMinWidth = getWidth(); } -void TextBox::setTextWrapped(const std::string &text) +void TextBox::setTextWrapped(const std::string &text, int minDimension) { // Make sure parent scroll area sets width of this widget if (getParent()) @@ -41,8 +41,13 @@ void TextBox::setTextWrapped(const std::string &text) getParent()->logic(); } + // Take the supplied minimum dimension as a starting point and try to beat it + mMinWidth = minDimension; + std::stringstream wrappedStream; std::string::size_type newlinePos, lastNewlinePos = 0; + int minWidth = 0; + int xpos; do { @@ -57,7 +62,18 @@ 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; + + spacePos = text.rfind(" ", text.size()); + + if (spacePos != std::string::npos) + { + const std::string word = text.substr(spacePos + 1); + const int length = getFont()->getWidth(word); + + if (length > mMinWidth) + mMinWidth = length; + } do { @@ -73,7 +89,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,22 +101,46 @@ 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); + if (newlinePos == std::string::npos) + newlinePos = text.size(); + 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()); if (text.find("\n", lastNewlinePos) != std::string::npos) - { 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 a42562ea..10a81fc0 100644 --- a/src/gui/textbox.h +++ b/src/gui/textbox.h @@ -31,7 +31,8 @@ * * \ingroup GUI */ -class TextBox : public gcn::TextBox { +class TextBox : public gcn::TextBox +{ public: /** * Constructor. @@ -41,7 +42,15 @@ class TextBox : public gcn::TextBox { /** * Sets the text after wrapping it to the current width of the widget. */ - void setTextWrapped(const std::string &text); + void setTextWrapped(const std::string &text, int minDimension); + + /** + * Get the minimum text width for the text box. + */ + int getMinWidth() { return mMinWidth; } + + private: + int mMinWidth; }; #endif diff --git a/src/gui/textfield.cpp b/src/gui/textfield.cpp index f7b02cbf..99a95a2e 100644 --- a/src/gui/textfield.cpp +++ b/src/gui/textfield.cpp @@ -19,14 +19,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "textfield.h" - -#include <algorithm> - #include <guichan/font.hpp> #include "sdlinput.h" +#include "textfield.h" +#include "../configuration.h" #include "../graphics.h" #include "../resources/image.h" @@ -37,10 +35,13 @@ #undef DELETE //Win32 compatibility hack int TextField::instances = 0; +float TextField::mAlpha = config.getValue("guialpha", 0.8); ImageRect TextField::skin; TextField::TextField(const std::string& text): - gcn::TextField(text) + gcn::TextField(text), + mNumeric(false), + mListener(0) { setFrameSize(2); @@ -51,9 +52,6 @@ TextField::TextField(const std::string& text): Image *textbox = resman->getImage("graphics/gui/deepbox.png"); int gridx[4] = {0, 3, 28, 31}; int gridy[4] = {0, 3, 28, 31}; - //Image *textbox = resman->getImage("graphics/gui/textbox.png"); - //int gridx[4] = {0, 5, 26, 31}; - //int gridy[4] = {0, 5, 26, 31}; int a = 0, x, y; for (y = 0; y < 3; y++) { @@ -62,6 +60,7 @@ TextField::TextField(const std::string& text): gridx[x], gridy[y], gridx[x + 1] - gridx[x] + 1, gridy[y + 1] - gridy[y] + 1); + skin.grid[a]->setAlpha(config.getValue("guialpha", 0.8)); a++; } } @@ -93,6 +92,15 @@ void TextField::draw(gcn::Graphics *graphics) graphics->setColor(getForegroundColor()); graphics->setFont(getFont()); graphics->drawText(mText, 1 - mXScroll, 1); + + if (config.getValue("guialpha", 0.8) != mAlpha) + { + mAlpha = config.getValue("guialpha", 0.8); + for (int a = 0; a < 9; a++) + { + skin.grid[a]->setAlpha(mAlpha); + } + } } void TextField::drawFrame(gcn::Graphics *graphics) @@ -105,6 +113,42 @@ 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; + } + } +} + +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; +} + void TextField::keyPressed(gcn::KeyEvent &keyEvent) { int val = keyEvent.getKey().getValue(); diff --git a/src/gui/textfield.h b/src/gui/textfield.h index 3570f89d..73824615 100644 --- a/src/gui/textfield.h +++ b/src/gui/textfield.h @@ -25,6 +25,13 @@ #include <guichan/widgets/textfield.hpp> class ImageRect; +class TextField; + +class TextFieldListener +{ + public: + virtual void listen(const TextField *value) = 0; +}; /** * A text field. @@ -54,13 +61,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; } + + /** * Processes one keypress. */ 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 float mAlpha; 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 66df55b8..af30d1fe 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,26 +30,28 @@ #include "itemcontainer.h" #include "scrollarea.h" #include "textfield.h" +#include "trade.h" + +#include "widgets/layout.h" #include "../inventory.h" #include "../item.h" +#include "../localplayer.h" #include "../net/messageout.h" #include "../net/protocol.h" -#include "../resources/iteminfo.h" - #include "../utils/gettext.h" #include "../utils/strprintf.h" #include "../utils/tostring.h" TradeWindow::TradeWindow(Network *network): - Window("Trade: You"), + Window(_("Trade: You")), mNetwork(network), - mMyInventory(new Inventory), - mPartnerInventory(new Inventory) + mMyInventory(new Inventory(INVENTORY_SIZE)), + mPartnerInventory(new Inventory(INVENTORY_SIZE)) { - setWindowName("Trade"); + setWindowName(_("Trade")); setDefaultSize(115, 227, 342, 209); setResizable(true); @@ -63,47 +63,43 @@ TradeWindow::TradeWindow(Network *network): mCancelButton = new Button(_("Cancel"), "cancel", this); mTradeButton = new Button(_("Trade"), "trade", this); - mMyItemContainer = new ItemContainer(mMyInventory.get()); + mTradeButton->setEnabled(false); + + mMyItemContainer = new ItemContainer(mMyInventory.get(), 2); + mMyItemContainer->setWidth(160); 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->setWidth(160); mPartnerItemContainer->addSelectionListener(this); - mPartnerItemContainer->setPosition(2, 58); mPartnerScroll = new ScrollArea(mPartnerItemContainer); - mPartnerScroll->setPosition(8, 64); mMoneyLabel = new gcn::Label(strprintf(_("You get %d GP."), 0)); mMoneyLabel2 = new gcn::Label(_("You give:")); mMoneyField = new TextField; mMoneyField->setWidth(50); - mAddButton->adjustSize(); - mOkButton->adjustSize(); - mCancelButton->adjustSize(); - mTradeButton->adjustSize(); - - mTradeButton->setEnabled(false); - - mItemNameLabel = new gcn::Label(strprintf(_("Name: %s"), "")); - mItemDescriptionLabel = new gcn::Label( - strprintf(_("Description: %s"), "")); - - add(mMyScroll); - add(mPartnerScroll); - add(mAddButton); - add(mOkButton); - add(mCancelButton); - add(mTradeButton); - add(mItemNameLabel); - add(mItemDescriptionLabel); - add(mMoneyLabel2); - add(mMoneyField); - add(mMoneyLabel); + place(1, 0, mMoneyLabel); + place(0, 1, mMyScroll).setPadding(3); + place(1, 1, mPartnerScroll).setPadding(3); + ContainerPlacer place; + place = getPlacer(0, 0); + place(0, 0, mMoneyLabel2); + place(1, 0, mMoneyField); + place = getPlacer(0, 2); + place(0, 0, mAddButton); + place(1, 0, mOkButton); + place(2, 0, mTradeButton); + place(3, 0, mCancelButton); + Layout &layout = getLayout(); + layout.extend(0, 2, 2, 1); + layout.setRowHeight(1, Layout::AUTO_SET); + layout.setRowHeight(2, 0); + layout.setColWidth(0, Layout::AUTO_SET); + layout.setColWidth(1, Layout::AUTO_SET); loadWindowState(); } @@ -114,48 +110,13 @@ TradeWindow::~TradeWindow() void TradeWindow::widgetResized(const gcn::Event &event) { - Window::widgetResized(event); + mMyItemContainer->setWidth(mMyScroll->getWidth()); + mPartnerItemContainer->setWidth(mPartnerScroll->getWidth()); - const gcn::Rectangle &area = getChildrenArea(); - const int width = area.width; - const int height = area.height; - - mMoneyLabel2->setPosition(8, height - 8 - mMoneyLabel2->getHeight()); - mMoneyField->setPosition( - 8 + mMoneyLabel2->getWidth(), - height - 8 - mMoneyField->getHeight()); - mMoneyLabel->setPosition( - mMoneyField->getX() + mMoneyField->getWidth() + 6, - mMoneyLabel2->getY()); - - mCancelButton->setPosition(width - 8 - mCancelButton->getWidth(), - height - 8 - mCancelButton->getHeight()); - mTradeButton->setPosition( - mCancelButton->getX() - 4 - mTradeButton->getWidth(), - mCancelButton->getY()); - mOkButton->setPosition(mTradeButton->getX() - 4 - mOkButton->getWidth(), - mCancelButton->getY()); - mAddButton->setPosition(mOkButton->getX() - 4 - mAddButton->getWidth(), - mCancelButton->getY()); - - mItemDescriptionLabel->setPosition(8, - mOkButton->getY() - mItemDescriptionLabel->getHeight() - 4); - mItemNameLabel->setPosition(8, - mItemDescriptionLabel->getY() - mItemNameLabel->getHeight() - 4); - - const int remaining = mItemNameLabel->getY() - 4 - 8 - 8; - const int itemContainerHeight = remaining / 2; - - mMyItemContainer->setSize(width - 24 - 12 - 1, - (INVENTORY_SIZE * 24) / (width / 24) - 1); - mMyScroll->setSize(width - 16, itemContainerHeight); - - mPartnerItemContainer->setSize(width - 24 - 12 - 1, - (INVENTORY_SIZE * 24) / (width / 24) - 1); - mPartnerScroll->setSize(width - 16, itemContainerHeight); - mPartnerScroll->setPosition(8, 8 + itemContainerHeight + 8); + Window::widgetResized(event); } + void TradeWindow::addMoney(int amount) { mMoneyLabel->setCaption(strprintf(_("You get %d GP."), amount)); @@ -164,38 +125,40 @@ void TradeWindow::addMoney(int amount) void TradeWindow::addItem(int id, bool own, int quantity, bool equipment) { - if (own) { + if (own) + { + mMyItemContainer->setWidth(mMyScroll->getWidth()); mMyInventory->addItem(id, quantity, equipment); - } else { + } + else + { + mPartnerItemContainer->setWidth(mPartnerScroll->getWidth()); mPartnerInventory->addItem(id, quantity, equipment); } } void TradeWindow::removeItem(int id, bool own) { - if (own) { + if (own) mMyInventory->removeItem(id); - } else { + else mPartnerInventory->removeItem(id); - } } void TradeWindow::changeQuantity(int index, bool own, int quantity) { - if (own) { + if (own) mMyInventory->getItem(index)->setQuantity(quantity); - } else { + else mPartnerInventory->getItem(index)->setQuantity(quantity); - } } void TradeWindow::increaseQuantity(int index, bool own, int quantity) { - if (own) { + if (own) mMyInventory->getItem(index)->increaseQuantity(quantity); - } else { + else mPartnerInventory->getItem(index)->increaseQuantity(quantity); - } } void TradeWindow::reset() @@ -218,21 +181,30 @@ void TradeWindow::setTradeButton(bool enabled) void TradeWindow::receivedOk(bool own) { - if (own) { + if (own) + { mOkMe = true; - if (mOkOther) { + if (mOkOther) + { mTradeButton->setEnabled(true); mOkButton->setEnabled(false); - } else { + } + else + { mTradeButton->setEnabled(false); mOkButton->setEnabled(false); } - } else { + } + else + { mOkOther = true; - if (mOkMe) { + if (mOkMe) + { mTradeButton->setEnabled(true); mOkButton->setEnabled(false); - } else { + } + else + { mTradeButton->setEnabled(false); mOkButton->setEnabled(true); } @@ -256,22 +228,9 @@ void TradeWindow::valueChanged(const gcn::SelectionEvent &event) */ if (event.getSource() == mMyItemContainer && (item = mMyItemContainer->getSelectedItem())) - { mPartnerItemContainer->selectNone(); - } else if ((item = mPartnerItemContainer->getSelectedItem())) - { mMyItemContainer->selectNone(); - } - - // Update name and description - ItemInfo const *info = item ? &item->getInfo() : NULL; - mItemNameLabel->setCaption(strprintf(_("Name: %s"), - info ? info->getName().c_str() : "")); - mItemNameLabel->adjustSize(); - mItemDescriptionLabel->setCaption(strprintf(_("Description: %s"), - info ? info->getDescription().c_str() : "")); - mItemDescriptionLabel->adjustSize(); } void TradeWindow::action(const gcn::ActionEvent &event) @@ -286,16 +245,19 @@ void TradeWindow::action(const gcn::ActionEvent &event) if (mMyInventory->getFreeSlot() < 1) return; - if (mMyInventory->contains(item)) { + if (mMyInventory->contains(item)) + { chatWindow->chatLog(_("Failed adding item. You can not " "overlap one kind of item on the window."), BY_SERVER); return; } - if (item->getQuantity() == 1) { + if (item->getQuantity() == 1) + { tradeItem(item, 1); } - else { + else + { // Choose amount of items to trade new ItemAmountWindow(AMOUNT_TRADE_ADD, this, item); } @@ -317,7 +279,9 @@ void TradeWindow::action(const gcn::ActionEvent &event) outMsg.writeInt16(CMSG_TRADE_ITEM_ADD_REQUEST); outMsg.writeInt16(0); outMsg.writeInt32(tempInt); - } else { + } + else + { mMoneyField->setText(""); } mMoneyField->setEnabled(false); diff --git a/src/gui/trade.h b/src/gui/trade.h index 3129c4b9..df724038 100644 --- a/src/gui/trade.h +++ b/src/gui/trade.h @@ -128,8 +128,6 @@ class TradeWindow : public Window, gcn::ActionListener, gcn::SelectionListener ItemContainer *mMyItemContainer; ItemContainer *mPartnerItemContainer; - gcn::Label *mItemNameLabel; - gcn::Label *mItemDescriptionLabel; gcn::Label *mMoneyLabel; gcn::Label *mMoneyLabel2; gcn::Button *mAddButton, *mOkButton, *mCancelButton, *mTradeButton; diff --git a/src/gui/truetypefont.cpp b/src/gui/truetypefont.cpp index f65b3446..8e6636df 100644 --- a/src/gui/truetypefont.cpp +++ b/src/gui/truetypefont.cpp @@ -19,12 +19,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "truetypefont.h" - #include <list> #include <guichan/exception.hpp> +#include "truetypefont.h" + #include "../graphics.h" #include "../resources/image.h" @@ -74,7 +74,6 @@ class TextChunk gcn::Color color; }; - // Word surfaces cache static std::list<TextChunk> cache; typedef std::list<TextChunk>::iterator CacheIterator; @@ -92,7 +91,7 @@ TrueTypeFont::TrueTypeFont(const std::string &filename, int size) ++fontCounter; mFont = TTF_OpenFont(filename.c_str(), size); - if (mFont == NULL) + if (!mFont) { throw GCN_EXCEPTION("SDLTrueTypeFont::SDLTrueTypeFont: " + std::string(TTF_GetError())); diff --git a/src/gui/truetypefont.h b/src/gui/truetypefont.h index 6f0671a2..cd68a94e 100644 --- a/src/gui/truetypefont.h +++ b/src/gui/truetypefont.h @@ -25,7 +25,6 @@ #include <string> #include <guichan/font.hpp> -#include <guichan/graphics.hpp> #ifndef __APPLE__ #include <SDL/SDL_ttf.h> #else diff --git a/src/gui/updatewindow.cpp b/src/gui/updatewindow.cpp index 96c2e95c..b3861b27 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> @@ -28,23 +26,26 @@ #include <guichan/widgets/label.hpp> +// Curl should be included after Guichan to avoid Windows redefinitions +#include <curl/curl.h> + #include "browserbox.h" #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> +#include "widgets/layout.h" #include "../configuration.h" #include "../log.h" #include "../main.h" +#include "../resources/resourcemanager.h" + #include "../utils/gettext.h" #include "../utils/tostring.h" -#include "../resources/resourcemanager.h" - /** * Calculates the Alder-32 checksum for the given file. */ @@ -68,8 +69,7 @@ static unsigned long fadler32(FILE *file) /** * Load the given file into a vector of strings. */ -std::vector<std::string> -loadTextFile(const std::string &fileName) +std::vector<std::string> loadTextFile(const std::string &fileName) { std::vector<std::string> lines; std::ifstream fin(fileName.c_str()); @@ -107,34 +107,29 @@ UpdaterWindow::UpdaterWindow(const std::string &updateHost, { mCurlError[0] = 0; - const int h = 240; - const int w = 320; - setContentSize(w, h); - 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, 310, 20, 168, 116, 31); mCancelButton = new Button(_("Cancel"), "cancel", this); mPlayButton = new Button(_("Play"), "play", this); mBrowserBox->setOpaque(false); mPlayButton->setEnabled(false); - mCancelButton->setPosition(5, h - 5 - mCancelButton->getHeight()); - mPlayButton->setPosition( - mCancelButton->getX() + mCancelButton->getWidth() + 5, - h - 5 - mPlayButton->getHeight()); - mProgressBar->setPosition(5, mCancelButton->getY() - 20 - 5); - mLabel->setPosition(5, mProgressBar->getY() - mLabel->getHeight() - 5); + ContainerPlacer place; + place = getPlacer(0, 0); + + place(0, 0, mScrollArea, 5, 3).setPadding(3); + place(0, 3, mLabel, 5); + place(0, 4, mProgressBar, 5); + place(3, 5, mCancelButton); + place(4, 5, mPlayButton); - mScrollArea->setDimension(gcn::Rectangle(5, 5, 310, mLabel->getY() - 12)); + reflowLayout(320, 240); - add(mScrollArea); - add(mLabel); - add(mProgressBar); - add(mCancelButton); - add(mPlayButton); + Layout &layout = getLayout(); + layout.setRowHeight(0, Layout::AUTO_SET); setLocationRelativeTo(getParent()); setVisible(true); @@ -210,7 +205,7 @@ void UpdaterWindow::loadNews() // Tokenize and add each line separately char *line = strtok(mMemoryBuffer, "\n"); - while (line != NULL) + while (line) { mBrowserBox->addRow(line); line = strtok(NULL, "\n"); @@ -246,8 +241,7 @@ int UpdaterWindow::updateProgress(void *ptr, return 0; } -size_t -UpdaterWindow::memoryWrite(void *ptr, size_t size, size_t nmemb, FILE *stream) +size_t UpdaterWindow::memoryWrite(void *ptr, size_t size, size_t nmemb, FILE *stream) { UpdaterWindow *uw = reinterpret_cast<UpdaterWindow *>(stream); size_t totalMem = size * nmemb; @@ -330,8 +324,8 @@ int UpdaterWindow::downloadThread(void *ptr) { case CURLE_COULDNT_CONNECT: default: - std::cerr << "curl error " << res << ": " - << uw->mCurlError << " host: " << url.c_str() + std::cerr << _("curl error ") << res << ": " + << uw->mCurlError << _(" host: ") << url.c_str() << std::endl; break; } @@ -366,7 +360,7 @@ int UpdaterWindow::downloadThread(void *ptr) // Remove the corrupted file ::remove(outFilename.c_str()); logger->log( - "Checksum for file %s failed: (%lx/%lx)", + _("Checksum for file %s failed: (%lx/%lx)"), uw->mCurrentFile.c_str(), adler, uw->mCurrentChecksum); attempts++; @@ -414,7 +408,7 @@ void UpdaterWindow::download() mDownloadComplete = false; mThread = SDL_CreateThread(UpdaterWindow::downloadThread, this); - if (mThread == NULL) + if (!mThread) { logger->log("Unable to create mThread"); mDownloadStatus = UPDATE_ERROR; @@ -454,9 +448,9 @@ void UpdaterWindow::logic() mThread = NULL; } mBrowserBox->addRow(""); - mBrowserBox->addRow("##1 The update process is incomplete."); - mBrowserBox->addRow("##1 It is strongly recommended that"); - mBrowserBox->addRow("##1 you try again later"); + mBrowserBox->addRow(_("##1 The update process is incomplete.")); + mBrowserBox->addRow(_("##1 It is strongly recommended that")); + mBrowserBox->addRow(_("##1 you try again later")); mBrowserBox->addRow(mCurlError); mScrollArea->setVerticalScrollAmount( mScrollArea->getVerticalMaxScroll()); diff --git a/src/gui/updatewindow.h b/src/gui/updatewindow.h index 3acbfb7e..4ada3c3a 100644 --- a/src/gui/updatewindow.h +++ b/src/gui/updatewindow.h @@ -23,13 +23,12 @@ #define _UPDATERWINDOW_H #include <guichan/actionlistener.hpp> + #include <string> #include <vector> #include "window.h" -#include "../guichanfwd.h" - #include "../utils/mutex.h" class BrowserBox; diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp index ff0883f7..f5a6edb4 100644 --- a/src/gui/viewport.cpp +++ b/src/gui/viewport.cpp @@ -19,33 +19,26 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "viewport.h" - -#include "gui.h" -#include "popupmenu.h" #include "ministatus.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" #include "../npc.h" #include "../textmanager.h" -#include "../resources/animation.h" #include "../resources/monsterinfo.h" #include "../resources/resourcemanager.h" -#include "../resources/image.h" -#include "../resources/imageset.h" #include "../utils/tostring.h" -#include <cassert> - extern volatile int tick_time; Viewport::Viewport(): @@ -70,76 +63,21 @@ 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 -Viewport::setMap(Map *map) +void Viewport::setMap(Map *map) { mMap = map; } extern MiniStatusWindow *miniStatusWindow; -void -Viewport::draw(gcn::Graphics *gcnGraphics) +void Viewport::draw(gcn::Graphics *gcnGraphics) { static int lastTick = tick_time; @@ -148,6 +86,10 @@ Viewport::draw(gcn::Graphics *gcnGraphics) Graphics *graphics = static_cast<Graphics*>(gcnGraphics); + // Ensure the client doesn't freak out if a feature localplayer uses + // is dependent on a map. + player_node->mMapInitialized = true; + // Avoid freaking out when tick_time overflows if (tick_time < lastTick) { @@ -225,37 +167,39 @@ 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); + } } } + if (player_node->mUpdateName) + { + player_node->mUpdateName = false; + player_node->setName(player_node->getName()); + } + + // Draw text if (textManager) { @@ -266,6 +210,7 @@ Viewport::draw(gcn::Graphics *gcnGraphics) Beings &beings = beingManager->getAll(); for (BeingIterator i = beings.begin(); i != beings.end(); i++) { + (*i)->drawSpeech(-(int) mPixelViewX, -(int) mPixelViewY); (*i)->drawEmotion(graphics, -(int) mPixelViewX, -(int) mPixelViewY); } @@ -276,8 +221,7 @@ Viewport::draw(gcn::Graphics *gcnGraphics) WindowContainer::draw(gcnGraphics); } -void -Viewport::logic() +void Viewport::logic() { WindowContainer::logic(); @@ -294,49 +238,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) @@ -348,8 +252,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) @@ -357,13 +263,14 @@ 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))) + else if ((floorItem = floorItemManager->findByCoordinates(tilex, + tiley))) { mPopupMenu->showPopup(event.getX(), event.getY(), floorItem); return; @@ -384,9 +291,7 @@ Viewport::mousePressed(gcn::MouseEvent &event) 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()) @@ -400,59 +305,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; @@ -465,20 +358,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 8965ad95..522ea734 100644 --- a/src/gui/viewport.h +++ b/src/gui/viewport.h @@ -27,15 +27,14 @@ #include "windowcontainer.h" #include "../configlistener.h" -#include "../being.h" +#include "../position.h" -class Map; class FloorItem; +class Graphics; class ImageSet; class Item; +class Map; class PopupMenu; -class Graphics; -class SimpleAnimation; /** * The viewport on the map. Displays the current map and handles mouse input @@ -62,97 +61,65 @@ class Viewport : public WindowContainer, public gcn::MouseListener, /** * Sets the map displayed by the viewport. */ - void - setMap(Map *map); + void setMap(Map *map); /** * Draws the viewport. */ - void - draw(gcn::Graphics *graphics); + void draw(gcn::Graphics *graphics); /** * Implements player to keep following mouse. */ - void - logic(); + void logic(); /** * Toggles whether the path debug graphics are shown */ - void - toggleDebugPath() { mShowDebugPath = !mShowDebugPath; } + void toggleDebugPath() { mShowDebugPath = !mShowDebugPath; } /** * Handles mouse press on map. */ - void - mousePressed(gcn::MouseEvent &event); + void mousePressed(gcn::MouseEvent &event); /** * Handles mouse move on map */ - void - mouseDragged(gcn::MouseEvent &event); + void mouseDragged(gcn::MouseEvent &event); /** * Handles mouse button release on map. */ - void - mouseReleased(gcn::MouseEvent &event); + void mouseReleased(gcn::MouseEvent &event); /** * Shows a popup for an item. * TODO Find some way to get rid of Item here */ - void - showPopup(int x, int y, Item *item); + void showPopup(int x, int y, Item *item); /** * A relevant config option changed. */ - void - optionChanged(const std::string &name); + void optionChanged(const std::string &name); /** - * Returns camera x offset in tiles. + * Returns camera x offset in pixels. */ - int - getCameraX() { return mTileViewX; } + int getCameraX() const { return (int) mPixelViewX; } /** - * Returns camera y offset in tiles. + * Returns camera y offset in pixels. */ - int - getCameraY() { return mTileViewY; } + int getCameraY() const { return (int) mPixelViewY; } /** * Changes viewpoint by relative pixel coordinates. */ - void - scrollBy(float x, float y) { mPixelViewX += x; mPixelViewY += y; } + void 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,22 +132,12 @@ 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; PopupMenu *mPopupMenu; /**< Popup menu. */ }; +extern Viewport *viewport; /**< The viewport */ + #endif diff --git a/src/gui/widgets/dropdown.cpp b/src/gui/widgets/dropdown.cpp new file mode 100644 index 00000000..79d9ff06 --- /dev/null +++ b/src/gui/widgets/dropdown.cpp @@ -0,0 +1,203 @@ +/* + * 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 "../color.h" +#include "../listbox.h" +#include "../scrollarea.h" + +#include "../../configuration.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; +float DropDown::mAlpha = config.getValue("guialpha", 0.8); + +DropDown::DropDown(gcn::ListModel *listModel, gcn::ScrollArea *scrollArea, + gcn::ListBox *listBox, bool opacity): + gcn::DropDown::DropDown(listModel, scrollArea, listBox), + mOpaque(opacity) +{ + 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"); + + buttons[0][0]->setAlpha(mAlpha); + buttons[0][1]->setAlpha(mAlpha); + buttons[1][0]->setAlpha(mAlpha); + buttons[1][1]->setAlpha(mAlpha); + + // 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); + skin.grid[a]->setAlpha(mAlpha); + 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(); + } + + if (config.getValue("guialpha", 0.8) != mAlpha) + { + mAlpha = config.getValue("guialpha", 0.8); + + buttons[0][0]->setAlpha(mAlpha); + buttons[0][1]->setAlpha(mAlpha); + buttons[1][0]->setAlpha(mAlpha); + buttons[1][1]->setAlpha(mAlpha); + + for (int a = 0; a < 9; a++) + { + skin.grid[a]->setAlpha(mAlpha); + } + } + + bool valid; + const int alpha = (int)(mAlpha * 255.0f); + gcn::Color faceColor = getBaseColor(); + faceColor.a = alpha; + gcn::Color highlightColor = textColor->getColor('H', valid); + highlightColor.a = alpha; + gcn::Color shadowColor = faceColor - 0x303030; + shadowColor.a = alpha; + + if (mOpaque) + { + int red = getBackgroundColor().r; + int green = getBackgroundColor().g; + int blue = getBackgroundColor().b; + graphics->setColor(gcn::Color(red, green, blue, alpha)); + graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), h)); + + red = getForegroundColor().r; + green = getForegroundColor().g; + blue = getForegroundColor().b; + graphics->setColor(gcn::Color(red, green, blue, alpha)); + } + + 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 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..e5919dc7 --- /dev/null +++ b/src/gui/widgets/dropdown.h @@ -0,0 +1,99 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DROPDOWN_H +#define DROPDOWN_H + +#include <guichan/widgets/dropdown.hpp> + +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, + bool opacity = true); + + /** + * Destructor. + */ + ~DropDown(); + + void draw(gcn::Graphics* graphics); + + void drawFrame(gcn::Graphics* graphics); + + /** + * Sets the widget to be opaque, that is sets the widget to display its + * background. + * + * @param opaque True if the widget should be opaque, false otherwise. + */ + void setOpaque(bool opaque) {mOpaque = opaque;} + + /** + * Checks if the widget is opaque, that is if the widget area displays + * its background. + * + * @return True if the widget is opaque, false otherwise. + */ + bool isOpaque() const {return mOpaque;} + + + 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; + static float mAlpha; + + bool mOpaque; +}; + +#endif // end DROPDOWN_H + diff --git a/src/gui/widgets/resizegrip.cpp b/src/gui/widgets/resizegrip.cpp index 00689575..fa264e37 100644 --- a/src/gui/widgets/resizegrip.cpp +++ b/src/gui/widgets/resizegrip.cpp @@ -19,10 +19,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "resizegrip.h" - #include <guichan/graphics.hpp> +#include "resizegrip.h" + +#include "../../configuration.h" #include "../../graphics.h" #include "../../resources/image.h" @@ -30,14 +31,16 @@ Image *ResizeGrip::gripImage = 0; int ResizeGrip::mInstances = 0; +float ResizeGrip::mAlpha = config.getValue("guialpha", 0.8); -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); + gripImage->setAlpha(mAlpha); } mInstances++; @@ -56,8 +59,13 @@ ResizeGrip::~ResizeGrip() } } -void -ResizeGrip::draw(gcn::Graphics *graphics) +void ResizeGrip::draw(gcn::Graphics *graphics) { + if (config.getValue("guialpha", 0.8) != mAlpha) + { + mAlpha = config.getValue("guialpha", 0.8); + gripImage->setAlpha(mAlpha); + } + static_cast<Graphics*>(graphics)->drawImage(gripImage, 0, 0); } diff --git a/src/gui/widgets/resizegrip.h b/src/gui/widgets/resizegrip.h index 5c6ea4bd..620c133f 100644 --- a/src/gui/widgets/resizegrip.h +++ b/src/gui/widgets/resizegrip.h @@ -39,7 +39,7 @@ class ResizeGrip : public gcn::Widget /** * Constructor. */ - ResizeGrip(); + ResizeGrip(std::string image = "graphics/gui/resize.png"); /** * Destructor. @@ -54,6 +54,7 @@ class ResizeGrip : public gcn::Widget private: static Image *gripImage; /**< Resize grip image */ static int mInstances; /**< Number of resize grip instances */ + static float mAlpha; }; #endif diff --git a/src/gui/widgets/tab.cpp b/src/gui/widgets/tab.cpp index c54b2390..21402c89 100644 --- a/src/gui/widgets/tab.cpp +++ b/src/gui/widgets/tab.cpp @@ -19,12 +19,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <algorithm> +#include <guichan/widgets/label.hpp> #include "tab.h" - #include "tabbedarea.h" +#include "../../configuration.h" #include "../../graphics.h" #include "../../resources/image.h" @@ -33,6 +33,7 @@ #include "../../utils/dtor.h" int Tab::mInstances = 0; +float Tab::mAlpha = config.getValue("guialpha", 0.8); enum{ TAB_STANDARD, // 0 @@ -50,10 +51,10 @@ struct TabData }; static TabData const data[TAB_COUNT] = { - {"graphics/gui/tab.png", 0, 0}, - {"graphics/gui/tab.png", 9, 4}, - {"graphics/gui/tabselected.png", 16, 19}, - {"graphics/gui/tab.png", 25, 23} + { "graphics/gui/tab.png", 0, 0 }, + { "graphics/gui/tab.png", 9, 4 }, + { "graphics/gui/tabselected.png", 16, 19 }, + { "graphics/gui/tab.png", 25, 23 } }; ImageRect Tab::tabImg[TAB_COUNT]; @@ -79,6 +80,7 @@ Tab::~Tab() void Tab::init() { setFrameSize(0); + mHighlighted = false; if (mInstances == 0) { @@ -98,6 +100,7 @@ void Tab::init() data[x].gridX, data[y].gridY, data[x + 1].gridX - data[x].gridX + 1, data[y + 1].gridY - data[y].gridY + 1); + tabImg[mode].grid[a]->setAlpha(mAlpha); a++; } } @@ -109,16 +112,33 @@ void Tab::init() void Tab::draw(gcn::Graphics *graphics) { - int mode; + int mode = TAB_STANDARD; // check which type of tab to draw - if (mTabbedArea && mTabbedArea->isTabSelected(this)) + if (mTabbedArea) { - mode = TAB_SELECTED; + if (mTabbedArea->isTabSelected(this)) + { + mode = TAB_SELECTED; + // if tab is selected, it doesnt need to highlight activity + mLabel->setForegroundColor(gcn::Color(0, 0, 0)); + mHighlighted = false; + } + else if (mHighlighted) + { + mode = TAB_HIGHLIGHTED; + mLabel->setForegroundColor(gcn::Color(255, 0, 0)); + } } - else + + if (config.getValue("guialpha", 0.8) != mAlpha) { - mode = TAB_STANDARD; + mAlpha = config.getValue("guialpha", 0.8); + for (int a = 0; a < 9; a++) + { + tabImg[TAB_SELECTED].grid[a]->setAlpha(mAlpha); + tabImg[TAB_STANDARD].grid[a]->setAlpha(mAlpha); + } } // draw tab @@ -128,3 +148,8 @@ void Tab::draw(gcn::Graphics *graphics) // draw label drawChildren(graphics); } + +void Tab::setHighlighted(bool high) +{ + mHighlighted = high; +} diff --git a/src/gui/widgets/tab.h b/src/gui/widgets/tab.h index 8382df83..3af4e2bf 100644 --- a/src/gui/widgets/tab.h +++ b/src/gui/widgets/tab.h @@ -47,12 +47,20 @@ class Tab : public gcn::Tab */ void draw(gcn::Graphics *graphics); + /** + * Set tab highlighted + */ + void setHighlighted(bool high); + private: /** Load images if no other instances exist yet */ void init(); static ImageRect tabImg[4]; /**< Tab state graphics */ static int mInstances; /**< Number of tab instances */ + static float mAlpha; + + bool mHighlighted; }; #endif diff --git a/src/gui/widgets/tabbedarea.cpp b/src/gui/widgets/tabbedarea.cpp index c4e22bff..5ff7c4bc 100644 --- a/src/gui/widgets/tabbedarea.cpp +++ b/src/gui/widgets/tabbedarea.cpp @@ -90,7 +90,7 @@ void TabbedArea::addTab(Tab *tab, gcn::Widget *widget) mTabContainer->add(tab); mTabs.push_back(std::pair<Tab*, gcn::Widget*>(tab, widget)); - if (mSelectedTab == NULL) + if (!mSelectedTab) { setSelectedTab(tab); } diff --git a/src/gui/window.cpp b/src/gui/window.cpp index ed5bb8fc..797b4be9 100644 --- a/src/gui/window.cpp +++ b/src/gui/window.cpp @@ -24,11 +24,9 @@ #include <climits> #include <guichan/exception.hpp> -#include <guichan/widgets/icon.hpp> - -#include "window.h" #include "gui.h" +#include "window.h" #include "windowcontainer.h" #include "widgets/layout.h" @@ -36,30 +34,30 @@ #include "../configlistener.h" #include "../configuration.h" -#include "../graphics.h" #include "../log.h" #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; } }; -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), @@ -72,31 +70,23 @@ 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()); - if (!windowContainer) { + if (!windowContainer) + { 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"); @@ -130,12 +120,14 @@ Window::~Window() const std::string &name = mWindowName; // Saving X, Y and Width and Height for resizables in the config - if (!name.empty()) { + if (!name.empty()) + { config.setValue(name + "WinX", getX()); config.setValue(name + "WinY", getY()); config.setValue(name + "Visible", isVisible()); - if (mGrip) { + if (mGrip) + { config.setValue(name + "WinWidth", getWidth()); config.setValue(name + "WinHeight", getHeight()); } @@ -152,22 +144,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(); } } @@ -199,6 +188,15 @@ void Window::draw(gcn::Graphics *graphics) getPadding() ); } + + // Update window alpha values + if (mAlphaChanged) + { + for_each(border.grid, border.grid + 9, + std::bind2nd(std::mem_fun(&Image::setAlpha), + config.getValue("guialpha", 0.8))); + closeImage->setAlpha(config.getValue("guialpha", 0.8)); + } drawChildren(graphics); } @@ -524,6 +522,187 @@ 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.empty()) + 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.empty()) + { + 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"); +} + Layout &Window::getLayout() { if (!mLayout) mLayout = new Layout; diff --git a/src/gui/window.h b/src/gui/window.h index 19d59c26..518de6e9 100644 --- a/src/gui/window.h +++ b/src/gui/window.h @@ -22,9 +22,11 @@ #ifndef WINDOW_H #define WINDOW_H -#include <guichan/widgets/window.hpp> #include <guichan/widgetlistener.hpp> +#include <guichan/widgets/window.hpp> + +#include "../graphics.h" #include "../guichanfwd.h" class ConfigListener; @@ -56,9 +58,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. Deletes all the added widgets. @@ -126,6 +129,26 @@ class Window : public gcn::Window, gcn::WidgetListener void setMaxHeight(unsigned int height); /** + * Gets the minimum width of the window. + */ + int getMinWidth() { return mMinWinWidth; } + + /** + * Gets the minimum height of the window. + */ + int getMinHeight() { return mMinWinHeight; } + + /** + * Gets the maximum width of the window. + */ + int getMaxWidth() { return mMaxWinWidth; } + + /** + * Gets the minimum height of the window. + */ + int getMaxHeight() { return mMaxWinHeight; } + + /** * Sets flag to show a title or not. */ void setShowTitle(bool flag) @@ -238,6 +261,11 @@ class Window : public gcn::Window, gcn::WidgetListener void reflowLayout(int w = 0, int h = 0); /** + * Loads a window skin + */ + void loadSkin(const std::string &filename); + + /** * Adds a widget to the window and sets it at given cell. */ LayoutCell &place(int x, int y, gcn::Widget *, int w = 1, int h = 1); @@ -269,6 +297,8 @@ class Window : public gcn::Window, gcn::WidgetListener */ int getResizeHandles(gcn::MouseEvent &event); + void setGuiAlpha(); + ResizeGrip *mGrip; /**< Resize grip */ Window *mParent; /**< The parent window */ Layout *mLayout; /**< Layout handler */ @@ -277,6 +307,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 */ @@ -285,6 +316,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 config listener that listens to changes relevant to all windows. @@ -293,7 +325,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.h b/src/gui/windowcontainer.h index a25f2037..62704d1b 100644 --- a/src/gui/windowcontainer.h +++ b/src/gui/windowcontainer.h @@ -30,7 +30,8 @@ * * \ingroup GUI */ -class WindowContainer : public gcn::Container { +class WindowContainer : public gcn::Container +{ public: /** * Do GUI logic. This functions adds automatic deletion of objects that diff --git a/src/guichanfwd.h b/src/guichanfwd.h index 2e97db68..4863421c 100644 --- a/src/guichanfwd.h +++ b/src/guichanfwd.h @@ -22,7 +22,8 @@ #ifndef GUICHANFWD_H #define GUICHANFWD_H -namespace gcn { +namespace gcn +{ class ActionListener; class AllegroGraphics; class AllegroImage; diff --git a/src/imageparticle.cpp b/src/imageparticle.cpp index 845cf258..557b3553 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 7b9ec07c..3ca26e1e 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -19,30 +19,30 @@ * 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" struct SlotUsed : public std::unary_function<Item*, bool> { - bool operator()(const Item *item) const { + bool operator()(const Item *item) const + { return item && item->getId() != -1 && item->getQuantity() > 0; } }; -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,11 +58,10 @@ 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]; - } + return NULL; } @@ -73,46 +72,41 @@ 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) { + if (!mItems[index] && id > 0) + { Item *item = new Item(id, quantity, equipment); item->setInvIndex(index); mItems[index] = item; - } else if (id > 0) { + } + else if (id > 0) + { mItems[index]->setId(id); mItems[index]->setQuantity(quantity); mItems[index]->setEquipment(equipment); - } else if (mItems[index]) { + } + else if (mItems[index]) + { removeItemAt(index); } } 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++) { - if (mItems[i] && mItems[i]->getId() == id) { + for (int i = 0; i < mSize; i++) + if (mItems[i] && mItems[i]->getId() == id) removeItemAt(i); - } - } } void Inventory::removeItemAt(int index) @@ -123,34 +117,30 @@ void Inventory::removeItemAt(int index) bool Inventory::contains(Item *item) const { - for (int i = 0; i < INVENTORY_SIZE; i++) { - if (mItems[i] && mItems[i]->getId() == item->getId()) { + for (int i = 0; i < mSize; i++) + if (mItems[i] && mItems[i]->getId() == item->getId()) return true; - } - } return false; } 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--) { - if (SlotUsed()(mItems[i])) { + for (int i = mSize - 1; i >= 0; i--) + if (SlotUsed()(mItems[i])) return i; - } - } return -1; } diff --git a/src/inventory.h b/src/inventory.h index 1d2ba296..2c5d99e3 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 @@ -46,14 +46,12 @@ class Item /** * Sets the item id, identifying the item type. */ - void - setId(int id); + void setId(int id); /** * Returns the item id. */ - int - getId() const { return mId; } + int getId() const { return mId; } /** * Returns the item image. @@ -63,62 +61,52 @@ class Item /** * Sets the number of items. */ - void - setQuantity(int quantity) { mQuantity = quantity; } + void setQuantity(int quantity) { mQuantity = quantity; } /** * Increases the number of items by the given amount. */ - void - increaseQuantity(int amount) { mQuantity += amount; } + void increaseQuantity(int amount) { mQuantity += amount; } /** * Returns the number of items. */ - int - getQuantity() const { return mQuantity; } + int getQuantity() const { return mQuantity; } /** * Sets whether this item is considered equipment. */ - void - setEquipment(bool equipment) { mEquipment = equipment; } + void setEquipment(bool equipment) { mEquipment = equipment; } /** * Returns whether this item is considered equipment. */ - bool - isEquipment() const { return mEquipment; } + bool isEquipment() const { return mEquipment; } /** * Sets whether this item is equipped. */ - void - setEquipped(bool equipped) { mEquipped = equipped; } + void setEquipped(bool equipped) { mEquipped = equipped; } /** * Returns whether this item is equipped. */ - bool - isEquipped() const { return mEquipped; } + bool isEquipped() const { return mEquipped; } /** * Sets the inventory index of this item. */ - void - setInvIndex(int index) { mInvIndex = index; } + void setInvIndex(int index) { mInvIndex = index; } /** * Returns the inventory index of this item. */ - int - getInvIndex() const { return mInvIndex; } + int getInvIndex() const { return mInvIndex; } /** * Returns information about this item type. */ - const ItemInfo& - getInfo() const { return ItemDB::get(mId); } + const ItemInfo& getInfo() const { return ItemDB::get(mId); } protected: int mId; /**< Item type id. */ diff --git a/src/itemshortcut.cpp b/src/itemshortcut.cpp index ee887c73..3404b0e3 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" @@ -34,9 +33,8 @@ ItemShortcut::ItemShortcut(): mItemSelected(-1) { for (int i = 0; i < SHORTCUT_ITEMS; i++) - { mItems[i] = -1; - } + load(); } @@ -52,9 +50,7 @@ void ItemShortcut::load() int itemId = (int) config.getValue("shortcut" + toString(i), -1); if (itemId != -1) - { mItems[i] = itemId; - } } } @@ -74,13 +70,15 @@ void ItemShortcut::useItem(int index) Item *item = player_node->getInventory()->findItem(mItems[index]); if (item && item->getQuantity()) { - if (item->isEquipment()) { - if (item->isEquipped()) { + if (item->isEquipment()) + { + if (item->isEquipped()) player_node->unequipItem(item); - } else { + else player_node->equipItem(item); - } - } else { + } + else + { player_node->useItem(item); } } diff --git a/src/itemshortcut.h b/src/itemshortcut.h index bc3f32ca..95e17f44 100644 --- a/src/itemshortcut.h +++ b/src/itemshortcut.h @@ -22,7 +22,7 @@ #ifndef ITEMSHORTCUT_H #define ITEMSHORTCUT_H -#define SHORTCUT_ITEMS 10 +#define SHORTCUT_ITEMS 12 class Item; diff --git a/src/joystick.cpp b/src/joystick.cpp index 6874e9cd..7e9a2285 100644 --- a/src/joystick.cpp +++ b/src/joystick.cpp @@ -19,13 +19,12 @@ * 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> - int Joystick::joystickCount = 0; void Joystick::init() diff --git a/src/joystick.h b/src/joystick.h index aebd4b5f..4c5390c2 100644 --- a/src/joystick.h +++ b/src/joystick.h @@ -30,12 +30,16 @@ class Joystick /** * Number of buttons we can handle. */ - enum { MAX_BUTTONS = 6 }; + enum + { + MAX_BUTTONS = 6 + }; /** * Directions, to be used as bitmask values. */ - enum { + enum + { UP = 1, DOWN = 2, LEFT = 4, diff --git a/src/keyboardconfig.cpp b/src/keyboardconfig.cpp index 9ae7ed68..5a0c0678 100644 --- a/src/keyboardconfig.cpp +++ b/src/keyboardconfig.cpp @@ -1,6 +1,6 @@ /* - * The Mana World - * Copyright (C) 2007 The Mana World Development Team + * Custom keyboard shortcuts configuration + * Copyright (C) 2007 Joshua Langley <joshlangley@optusnet.com.au> * * This file is part of The Mana World. * @@ -19,52 +19,83 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "keyboardconfig.h" #include "configuration.h" +#include "keyboardconfig.h" #include "log.h" +#include "gui/sdlinput.h" #include "gui/setup_keyboard.h" +#include "utils/gettext.h" +#include "utils/strprintf.h" + struct KeyData { const char *configField; int defaultValue; - const char *caption; + std::string caption; }; // keyData must be in same order as enum keyAction. static KeyData const keyData[KeyboardConfig::KEY_TOTAL] = { - {"keyMoveUp", SDLK_UP, "Move Up"}, - {"keyMoveDown", SDLK_DOWN, "Move Down"}, - {"keyMoveLeft", SDLK_LEFT, "Move Left"}, - {"keyMoveRight", SDLK_RIGHT, "Move Right"}, - {"keyAttack", SDLK_LCTRL, "Attack"}, - {"keySmilie", SDLK_LALT, "Smilie"}, - {"keyTarget", SDLK_LSHIFT, "Target"}, - {"keyTargetClosest", SDLK_a, "Target Closest"}, - {"keyTargetPlayer", SDLK_q, "Target Player"}, - {"keyPickup", SDLK_z, "Pickup"}, - {"keyHideWindows", SDLK_h, "Hide Windows"}, - {"keyBeingSit", SDLK_s, "Sit"}, - {"keyShortcut0", SDLK_0, "Item Shortcut 0"}, - {"keyShortcut1", SDLK_1, "Item Shortcut 1"}, - {"keyShortcut2", SDLK_2, "Item Shortcut 2"}, - {"keyShortcut3", SDLK_3, "Item Shortcut 3"}, - {"keyShortcut4", SDLK_4, "Item Shortcut 4"}, - {"keyShortcut5", SDLK_5, "Item Shortcut 5"}, - {"keyShortcut6", SDLK_6, "Item Shortcut 6"}, - {"keyShortcut7", SDLK_7, "Item Shortcut 7"}, - {"keyShortcut8", SDLK_8, "Item Shortcut 8"}, - {"keyShortcut9", SDLK_9, "Item Shortcut 9"}, - {"keyWindowStatus", SDLK_F2, "Status Window"}, - {"keyWindowInventory", SDLK_F3, "Inventory Window"}, - {"keyWindowEquipment", SDLK_F4, "Equipment WIndow"}, - {"keyWindowSkill", SDLK_F5, "Skill Window"}, - {"keyWindowMinimap", SDLK_F6, "Minimap Window"}, - {"keyWindowChat", SDLK_F7, "Chat Window"}, - {"keyWindowShortcut", SDLK_F8, "Item Shortcut Window"}, - {"keyWindowSetup", SDLK_F9, "Setup Window"}, - {"keyWindowDebug", SDLK_F10, "Debug Window"} + {"keyMoveUp", SDLK_UP, _("Move Up")}, + {"keyMoveDown", SDLK_DOWN, _("Move Down")}, + {"keyMoveLeft", SDLK_LEFT, _("Move Left")}, + {"keyMoveRight", SDLK_RIGHT, _("Move Right")}, + {"keyAttack", SDLK_LCTRL, _("Attack")}, + {"keySmilie", SDLK_LALT, _("Smilie")}, + {"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")}, + {"keyBeingSit", SDLK_s, _("Sit")}, + {"keyScreenshot", SDLK_p, _("Screenshot")}, + {"keyTrade", SDLK_r, _("Enable/Disable Trading")}, + {"keyPathfind", SDLK_f, _("Find Path to Mouse")}, + {"keyShortcut1", SDLK_1, strprintf(_("Item Shortcut %d"), 1)}, + {"keyShortcut2", SDLK_2, strprintf(_("Item Shortcut %d"), 2)}, + {"keyShortcut3", SDLK_3, strprintf(_("Item Shortcut %d"), 3)}, + {"keyShortcut4", SDLK_4, strprintf(_("Item Shortcut %d"), 4)}, + {"keyShortcut5", SDLK_5, strprintf(_("Item Shortcut %d"), 5)}, + {"keyShortcut6", SDLK_6, strprintf(_("Item Shortcut %d"), 6)}, + {"keyShortcut7", SDLK_7, strprintf(_("Item Shortcut %d"), 7)}, + {"keyShortcut8", SDLK_8, strprintf(_("Item Shortcut %d"), 8)}, + {"keyShortcut9", SDLK_9, strprintf(_("Item Shortcut %d"), 9)}, + {"keyShortcut10", SDLK_0, strprintf(_("Item Shortcut %d"), 10)}, + {"keyShortcut11", SDLK_MINUS, strprintf(_("Item Shortcut %d"), 11)}, + {"keyShortcut12", SDLK_EQUALS, strprintf(_("Item Shortcut %d"), 12)}, + {"keyWindowHelp", SDLK_F1, _("Help Window")}, + {"keyWindowStatus", SDLK_F2, _("Status Window")}, + {"keyWindowInventory", SDLK_F3, _("Inventory Window")}, + {"keyWindowEquipment", SDLK_F4, _("Equipment WIndow")}, + {"keyWindowSkill", SDLK_F5, _("Skill Window")}, + {"keyWindowMinimap", SDLK_F6, _("Minimap Window")}, + {"keyWindowChat", SDLK_F7, _("Chat Window")}, + {"keyWindowShortcut", SDLK_F8, _("Item Shortcut Window")}, + {"keyWindowSetup", SDLK_F9, _("Setup Window")}, + {"keyWindowDebug", SDLK_F10, _("Debug Window")}, + {"keyWindowEmote", SDLK_F11, _("Emote Window")}, + {"keyWindowEmoteBar", SDLK_F12, _("Emote Shortcut Window")}, + {"keyEmoteShortcut1", SDLK_1, strprintf(_("Emote Shortcut %d"), 1)}, + {"keyEmoteShortcut2", SDLK_2, strprintf(_("Emote Shortcut %d"), 2)}, + {"keyEmoteShortcut3", SDLK_3, strprintf(_("Emote Shortcut %d"), 3)}, + {"keyEmoteShortcut4", SDLK_4, strprintf(_("Emote Shortcut %d"), 4)}, + {"keyEmoteShortcut5", SDLK_5, strprintf(_("Emote Shortcut %d"), 5)}, + {"keyEmoteShortcut6", SDLK_6, strprintf(_("Emote Shortcut %d"), 6)}, + {"keyEmoteShortcut7", SDLK_7, strprintf(_("Emote Shortcut %d"), 7)}, + {"keyEmoteShortcut8", SDLK_8, strprintf(_("Emote Shortcut %d"), 8)}, + {"keyEmoteShortcut9", SDLK_9, strprintf(_("Emote Shortcut %d"), 9)}, + {"keyEmoteShortcut10", SDLK_0, strprintf(_("Emote Shortcut %d"), 10)}, + {"keyEmoteShortcut11", SDLK_MINUS, strprintf(_("Emote Shortcut %d"), 11)}, + {"keyEmoteShortcut12", SDLK_EQUALS, strprintf(_("Emote Shortcut %d"), 12)}, + {"keyChat", SDLK_RETURN, _("Toggle Chat")}, + {"keyChatScrollUp", SDLK_PAGEUP, _("Scroll Chat Up")}, + {"keyChatScrollDown", SDLK_PAGEDOWN, _("Scroll Chat Down")}, + {"keyOK", SDLK_RETURN, _("Select OK")}, + {"keyQuit", SDLK_ESCAPE, _("Quit")} }; void KeyboardConfig::init() @@ -110,11 +141,20 @@ void KeyboardConfig::makeDefault() bool KeyboardConfig::hasConflicts() { int i, j; + /** + * No need to parse the square matrix: only check one triangle + * that's enough to detect conflicts + */ for (i = 0; i < KEY_TOTAL; i++) { - for (j = 0; j < KEY_TOTAL; j++) + for (j = i, j++; j < KEY_TOTAL; j++) { - if (i != j && mKey[i].value == mKey[j].value) + // Allow for item shortcut and emote keys to overlap, but no other keys + if (!((((i >= KEY_SHORTCUT_1) && (i <= KEY_SHORTCUT_12)) && + ((j >= KEY_EMOTE_1) && (j <= KEY_EMOTE_12))) || + ((i == KEY_TOGGLE_CHAT) && (j == KEY_OK))) && + (mKey[i].value == mKey[j].value) + ) { return true; } @@ -132,7 +172,7 @@ int KeyboardConfig::getKeyIndex(int keyValue) const { for (int i = 0; i < KEY_TOTAL; i++) { - if(keyValue == mKey[i].value) + if (keyValue == mKey[i].value) { return i; } @@ -140,6 +180,19 @@ int KeyboardConfig::getKeyIndex(int keyValue) const return KEY_NO_VALUE; } + +int KeyboardConfig::getKeyEmoteOffset(int keyValue) const +{ + for (int i = KEY_EMOTE_1; i <= KEY_EMOTE_12; i++) + { + if (keyValue == mKey[i].value) + { + return 1 + i - KEY_EMOTE_1; + } + } + return 0; +} + bool KeyboardConfig::isKeyActive(int index) { return mActiveKeys[ mKey[index].value]; diff --git a/src/keyboardconfig.h b/src/keyboardconfig.h index 3317460f..8949a48e 100644 --- a/src/keyboardconfig.h +++ b/src/keyboardconfig.h @@ -1,6 +1,6 @@ /* - * The Mana World - * Copyright (C) 2007 The Mana World Development Team + * Custom keyboard shortcuts configuration + * Copyright (C) 2007 Joshua Langley <joshlangley@optusnet.com.au> * * This file is part of The Mana World. * @@ -22,11 +22,9 @@ #ifndef KEYBOARDCONFIG_H #define KEYBOARDCONFIG_H +#include <SDL_types.h> #include <string> -#include "gui/sdlinput.h" -#include "gui/setup_keyboard.h" - /** * Each key represents a key function. Such as 'Move up', 'Attack' etc. */ @@ -38,6 +36,8 @@ struct KeyFunction int value; /** The actual value that is used. */ }; +class Setup_Keyboard; + class KeyboardConfig { public: @@ -101,6 +101,11 @@ class KeyboardConfig int getKeyIndex(int keyValue) const; /** + * Get the key function index for an emote by providing the offset value. + */ + int getKeyEmoteOffset(int keyValue) const; + + /** * Set the enable flag, which will stop the user from doing actions. */ void setEnabled(bool flag) @@ -141,21 +146,26 @@ class KeyboardConfig * The key assignment view gets arranged according to the order of * these values. */ - enum KeyAction { + enum KeyAction + { KEY_NO_VALUE = -1, KEY_MOVE_UP, KEY_MOVE_DOWN, KEY_MOVE_LEFT, KEY_MOVE_RIGHT, KEY_ATTACK, - KEY_SMILIE, + KEY_EMOTE, + KEY_TALK, KEY_TARGET, KEY_TARGET_CLOSEST, + KEY_TARGET_NPC, KEY_TARGET_PLAYER, KEY_PICKUP, KEY_HIDE_WINDOWS, KEY_SIT, - KEY_SHORTCUT_0, + KEY_SCREENSHOT, + KEY_TRADE, + KEY_PATHFIND, KEY_SHORTCUT_1, KEY_SHORTCUT_2, KEY_SHORTCUT_3, @@ -165,6 +175,10 @@ class KeyboardConfig KEY_SHORTCUT_7, KEY_SHORTCUT_8, KEY_SHORTCUT_9, + KEY_SHORTCUT_10, + KEY_SHORTCUT_11, + KEY_SHORTCUT_12, + KEY_WINDOW_HELP, KEY_WINDOW_STATUS, KEY_WINDOW_INVENTORY, KEY_WINDOW_EQUIPMENT, @@ -174,6 +188,25 @@ class KeyboardConfig KEY_WINDOW_SHORTCUT, KEY_WINDOW_SETUP, KEY_WINDOW_DEBUG, + KEY_WINDOW_EMOTE, + KEY_WINDOW_EMOTE_SHORTCUT, + KEY_EMOTE_1, + KEY_EMOTE_2, + KEY_EMOTE_3, + KEY_EMOTE_4, + KEY_EMOTE_5, + KEY_EMOTE_6, + KEY_EMOTE_7, + KEY_EMOTE_8, + KEY_EMOTE_9, + KEY_EMOTE_10, + KEY_EMOTE_11, + KEY_EMOTE_12, + KEY_TOGGLE_CHAT, + KEY_SCROLL_CHAT_UP, + KEY_SCROLL_CHAT_DOWN, + KEY_OK, + KEY_QUIT, KEY_TOTAL }; diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 07044ce7..a02a8b1b 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -18,19 +18,23 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <cassert> -#include "localplayer.h" - +#include "configuration.h" #include "equipment.h" #include "floor_item.h" #include "game.h" +#include "graphics.h" #include "inventory.h" #include "item.h" -#include "main.h" +#include "localplayer.h" +#include "map.h" +#include "monster.h" #include "particle.h" +#include "simpleanimation.h" #include "sound.h" -#include "monster.h" #include "statuseffect.h" +#include "text.h" #include "gui/gui.h" #include "gui/ministatus.h" @@ -38,10 +42,18 @@ #include "net/messageout.h" #include "net/protocol.h" +#include "resources/animation.h" +#include "resources/image.h" +#include "resources/imageset.h" +#include "resources/resourcemanager.h" + #include "utils/tostring.h" LocalPlayer *player_node = NULL; +static const int NAME_X_OFFSET = 15; +static const int NAME_Y_OFFSET = 30; + LocalPlayer::LocalPlayer(Uint32 id, Uint16 job, Map *map): Player(id, job, map), mCharId(0), @@ -57,23 +69,56 @@ LocalPlayer::LocalPlayer(Uint32 id, Uint16 job, Map *map): ATK_BONUS(0), MATK_BONUS(0), DEF_BONUS(0), MDEF_BONUS(0), FLEE_BONUS(0), mStatPoint(0), mSkillPoint(0), mStatsPointsToAttribute(0), + mEquipment(new Equipment()), 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)) { + // Variable to keep the local player from doing certain actions before a map + // is initialized. e.g. drawing a player's name using the TextManager, since + // it appears to be dependant upon map coordinates for updating drawing. + mMapInitialized = false; + + mUpdateName = true; + + initTargetCursor(); } LocalPlayer::~LocalPlayer() { delete mInventory; + delete mStorage; + delete mName; + + 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) { @@ -91,7 +136,6 @@ void LocalPlayer::logic() mFrame = (get_elapsed_time(mWalkTime) * frames) / mAttackSpeed; if (mFrame >= frames) { nextStep(); - attack(); } break; } @@ -100,10 +144,65 @@ 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(); } +void LocalPlayer::setGM() +{ + mIsGM = !mIsGM; + mNameColor = mIsGM ? 0x009000: 0x202020; + setName(getName()); + config.setValue(getName() + "GMassert", mIsGM); +} + +void LocalPlayer::setName(const std::string &name) +{ + if (mName) + { + delete mName; + mName = 0; + } + + if (config.getValue("showownname", false) && mMapInitialized) + { + Player::setName(name); + } + else + { + Being::setName(name); + } +} + void LocalPlayer::nextStep() { if (mPath.empty()) @@ -240,9 +339,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 || target == mTarget) + { + target = NULL; + mKeepAttacking = false; + mTargetTime = -1; + } + if (target) + { + mTargetTime = tick_time; } if (mTarget && mTarget->getType() == Being::MONSTER) { @@ -384,25 +493,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) @@ -418,13 +527,19 @@ void LocalPlayer::attack(Being *target, bool keep) setDirection(LEFT); } - setAction(ATTACK); + // Implement charging attacks here + mLastAttackTime = 0; + mWalkTime = tick_time; + mTargetTime = tick_time; + + setAction(ATTACK); if (mEquippedWeapon) { std::string soundFile = mEquippedWeapon->getSound(EQUIP_EVENT_STRIKE); - if (soundFile != "") sound.playSfx(soundFile); + if (!soundFile.empty()) + sound.playSfx(soundFile); } else { sound.playSfx("sfx/fist-swish.ogg"); @@ -434,11 +549,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 @@ -481,6 +607,7 @@ bool LocalPlayer::withinAttackRange(Being *target) void LocalPlayer::setGotoTarget(Being *target) { + mLastTarget = -1; setTarget(target); mGoingToTarget = true; setDestination(target->mX, target->mY); @@ -527,3 +654,85 @@ void LocalPlayer::handleStatusEffect(StatusEffect *effect, int effectId) } } } + +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 6b6486e5..c128f4a4 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -22,6 +22,7 @@ #ifndef LOCALPLAYER_H #define LOCALPLAYER_H +#include <memory> #include <vector> #include "player.h" @@ -29,10 +30,17 @@ // TODO move into some sane place... #define MAX_SLOT 2 +#define INVENTORY_SIZE 102 +#define STORAGE_SIZE 301 + +class Equipment; class FloorItem; +class ImageSet; class Inventory; class Item; +class Map; class Network; +class SimpleAnimation; /** * The local player character. @@ -40,7 +48,8 @@ class Network; class LocalPlayer : public Player { public: - enum Attribute { + enum Attribute + { STR = 0, AGI, VIT, INT, DEX, LUK }; @@ -54,7 +63,7 @@ class LocalPlayer : public Player */ ~LocalPlayer(); - void setName(const std::string &name) {Being::setName(name); } + virtual void setName(const std::string &name); void setNetwork(Network *network) { mNetwork = network; } Network *getNetwork() {return mNetwork; } virtual void logic(); @@ -71,6 +80,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); @@ -118,6 +132,14 @@ class LocalPlayer : public Player void attack(Being *target = NULL, bool keep = false); + /** + * Triggers whether or not to show the name as a GM name. + * NOTE: This doesn't mean that just anyone can use this. + * If the server doesn't acknowlege you, you won't be shown + * as a GM on other people's clients. + */ + virtual void setGM(); + void stopAttack(); Being* getTarget() const; @@ -166,6 +188,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 +226,22 @@ class LocalPlayer : public Player Uint16 mStatPoint, mSkillPoint; Uint16 mStatsPointsToAttribute; + bool mUpdateName; /** Whether or not the name settings have changed */ + + bool mMapInitialized; /** Whether or not the map is available yet */ + + 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]; + + const std::auto_ptr<Equipment> mEquipment; + protected: virtual void handleStatusEffect(StatusEffect *effect, int effectId); @@ -211,15 +255,35 @@ class LocalPlayer : public Player FloorItem *mPickUpTarget; bool mTrading; + bool mInStorage; /**< Whether storage is currently accessible */ bool mGoingToTarget; - int mLastAction; /**< Time stamp of the last 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. */ + 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. */ std::vector<int> mStatusEffectIcons; 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/log.cpp b/src/log.cpp index e50edeb2..b0024f80 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -19,8 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <cstdarg> -#include <cstdlib> #include <iostream> #include <sstream> @@ -22,7 +22,6 @@ #ifndef _LOG_H #define _LOG_H -#include <iosfwd> #include <fstream> class ChatWindow; diff --git a/src/main.cpp b/src/main.cpp index 91ae87e9..54320433 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,48 +19,43 @@ * 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/widgets/label.hpp> #include <libxml/parser.h> -#ifdef WIN32 -#include <SDL_syswm.h> -#endif -#ifndef WIN32 -#include <cerrno> -#include <sys/stat.h> -#endif -#if defined __APPLE__ -#include <CoreFoundation/CFBundle.h> -#endif +#include <SDL/SDL_ttf.h> #include "configuration.h" -#include "keyboardconfig.h" -#include "player_relations.h" +#include "emoteshortcut.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/button.h" #include "gui/char_server.h" #include "gui/char_select.h" +#include "gui/color.h" #include "gui/gui.h" #include "gui/login.h" #include "gui/ok_dialog.h" @@ -68,7 +63,6 @@ #include "gui/register.h" #include "gui/sdlinput.h" #include "gui/setup.h" -#include "gui/textfield.h" #include "gui/updatewindow.h" #include "net/charserverhandler.h" @@ -77,17 +71,35 @@ #include "net/messageout.h" #include "net/network.h" +#include "resources/colordb.h" +#include "resources/emotedb.h" #include "resources/image.h" #include "resources/itemdb.h" #include "resources/monsterdb.h" #include "resources/npcdb.h" #include "resources/resourcemanager.h" -#include "utils/dtor.h" #include "utils/gettext.h" #include "utils/tostring.h" -namespace { +#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 +{ Window *setupWindow = 0; struct SetupListener : public gcn::ActionListener @@ -123,6 +135,7 @@ CharServerHandler charServerHandler; LoginData loginData; LockedArray<LocalPlayer*> charInfo(MAX_SLOT + 1); +Color *textColor; // This anonymous namespace hides whatever is inside from other modules. namespace { @@ -168,10 +181,13 @@ struct Options */ void setUpdatesDir() { + std::stringstream updates; + // If updatesHost is currently empty, fill it from config file - if (updateHost.empty()) { + if (updateHost.empty()) + { updateHost = - config.getValue("updatehost", "http://updates.themanaworld.org"); + config.getValue("updatehost", "http://updates.themanaworld.org/"); } // Remove any trailing slash at the end of the update host @@ -181,29 +197,59 @@ void setUpdatesDir() // Parse out any "http://" or "ftp://", and set the updates directory size_t pos; pos = updateHost.find("://"); - if (pos != updateHost.npos) { - if (pos + 3 < updateHost.length()) { - updatesDir = - "updates/" + updateHost.substr(pos + 3); - } else { + if (pos != updateHost.npos) + { + if (pos + 3 < updateHost.length()) + { + 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; + errorMessage = _("Invalid update host: ") + updateHost; state = ERROR_STATE; } - } else { + } + 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(); // Verify that the updates directory exists. Create if necessary. - if (!resman->isDirectory("/" + updatesDir)) { - if (!resman->mkdir("/" + updatesDir)) { + if (!resman->isDirectory("/" + updatesDir)) + { + if (!resman->mkdir("/" + updatesDir)) + { +#if defined WIN32 + std::string newDir = homeDir + "\\" + updatesDir; + std::string::size_type loc = newDir.find("/", 0); + + while (loc != std::string::npos) + { + newDir.replace(loc, 1, "\\"); + loc = newDir.find("/", loc); + } + + if (!CreateDirectory(newDir.c_str(), 0) && + GetLastError() != ERROR_ALREADY_EXISTS) + { + logger->log("Error: %s can't be made, but doesn't exist!", + newDir.c_str()); + errorMessage = _("Error creating updates directory!"); + state = ERROR_STATE; + } +#else logger->log("Error: %s/%s can't be made, but doesn't exist!", - homeDir.c_str(), updatesDir.c_str()); - errorMessage = "Error creating updates directory!"; + homeDir.c_str(), updatesDir.c_str()); + errorMessage = _("Error creating updates directory!"); state = ERROR_STATE; +#endif } } } @@ -231,7 +277,7 @@ void init_engine(const Options &options) #endif { std::cout << homeDir - << " can't be created, but it doesn't exist! Exiting." + << _(" can't be created, but it doesn't exist! Exiting.") << std::endl; exit(1); } @@ -248,7 +294,7 @@ void init_engine(const Options &options) // Initialize SDL logger->log("Initializing SDL..."); if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) { - std::cerr << "Could not initialize SDL: " << + std::cerr << _("Could not initialize SDL: ") << SDL_GetError() << std::endl; exit(1); } @@ -261,7 +307,7 @@ void init_engine(const Options &options) if (!resman->setWriteDir(homeDir)) { std::cout << homeDir - << " couldn't be set as home directory! Exiting." + << _(" couldn't be set as home directory! Exiting.") << std::endl; exit(1); } @@ -281,18 +327,18 @@ void init_engine(const Options &options) if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, PATH_MAX)) { - fprintf(stderr, "Can't find Resources directory\n"); + fprintf(stderr, _("Can't find Resources directory\n")); } CFRelease(resourcesURL); strncat(path, "/data", PATH_MAX - 1); resman->addToSearchPath(path, true); #else - resman->addToSearchPath(TMW_DATADIR "data", true); + resman->addToSearchPath(PKG_DATADIR "data", true); #endif // Fill configuration with defaults logger->log("Initializing configuration..."); - config.setValue("host", "server.themanaworld.org"); + config.setValue("host", "www.themanaworld.org"); config.setValue("port", 6901); config.setValue("hwaccel", 0); #if (defined __APPLE__ || defined WIN32) && defined USE_OPENGL @@ -313,24 +359,24 @@ void init_engine(const Options &options) // Checking if the configuration file exists... otherwise creates it with // default options ! - FILE *tmwFile = 0; + FILE *configFile = 0; std::string configPath = options.configPath; if (configPath.empty()) configPath = homeDir + "/config.xml"; - tmwFile = fopen(configPath.c_str(), "r"); + configFile = fopen(configPath.c_str(), "r"); // If we can't read it, it doesn't exist ! - if (tmwFile == NULL) { + if (configFile == NULL) { // We reopen the file in write mode and we create it - tmwFile = fopen(configPath.c_str(), "wt"); + configFile = fopen(configPath.c_str(), "wt"); } - if (tmwFile == NULL) { + if (configFile == NULL) { std::cout << "Can't create " << configPath << ". " "Using Defaults." << std::endl; } else { - fclose(tmwFile); + fclose(configFile); config.init(configPath); } @@ -344,7 +390,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(PKG_DATADIR "data/icons/tmw.png"); if (icon) { SDL_SetAlpha(icon, SDL_SRCALPHA, SDL_ALPHA_OPAQUE); @@ -365,17 +411,17 @@ void init_engine(const Options &options) graphics = new Graphics(); #endif - int width = (int) config.getValue("screenwidth", defaultScreenWidth); - int height = (int) config.getValue("screenheight", defaultScreenHeight); - int bpp = 0; - bool fullscreen = ((int) config.getValue("screen", 0) == 1); - bool hwaccel = ((int) config.getValue("hwaccel", 0) == 1); + const int width = (int) config.getValue("screenwidth", defaultScreenWidth); + const int height = (int) config.getValue("screenheight", defaultScreenHeight); + const int bpp = 0; + const bool fullscreen = ((int) config.getValue("screen", 0) == 1); + const bool hwaccel = ((int) config.getValue("hwaccel", 0) == 1); // Try to set the desired video mode if (!graphics->setVideoMode(width, height, bpp, fullscreen, hwaccel)) { - std::cerr << "Couldn't set " - << width << "x" << height << "x" << bpp << " video mode: " + std::cerr << _("Couldn't set ") + << width << "x" << height << "x" << bpp << _(" video mode: ") << SDL_GetError() << std::endl; exit(1); } @@ -386,6 +432,9 @@ void init_engine(const Options &options) // Initialize the item shortcuts. itemShortcut = new ItemShortcut(); + // Initialize the emote shortcuts. + emoteShortcut = new EmoteShortcut(); + gui = new Gui(graphics); state = LOGIN_STATE; /**< Initial game state */ @@ -417,6 +466,7 @@ void exit_engine() { // Before config.write() since it writes the shortcuts to the config delete itemShortcut; + delete emoteShortcut; config.write(); @@ -430,6 +480,8 @@ void exit_engine() sound.close(); // Unload XML databases + ColorDB::unload(); + EmoteDB::unload(); ItemDB::unload(); MonsterDB::unload(); NPCDB::unload(); @@ -441,28 +493,28 @@ void exit_engine() void printHelp() { std::cout - << "tmw" << 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 - << " -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 - << " -p --playername : Login with this player" << std::endl - << " -C --configfile : Configuration file to use" << std::endl - << " -H --updatehost : Use this update host" << std::endl - << " -S --homedir : Directory to use as home directory" << std::endl; + << _("tmw") << std::endl << std::endl + << _("Options: ") << std::endl + << _(" -C --configfile : Configuration file to use") << std::endl + << _(" -d --data : Directory to load game data from") << std::endl + << _(" -D --default : Bypass the login process with default settings") << std::endl + << _(" -h --help : Display this help") << std::endl + << _(" -S --homedir : Directory to use as home directory") << std::endl + << _(" -H --updatehost : Use this update host") << std::endl + << _(" -p --playername : Login with this player") << 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 << _("The Mana World version ") << PACKAGE_VERSION << std::endl; #else - std::cout << "The Mana World version " << - "(local build?, PACKAGE_VERSION is not defined)" << std::endl; + std::cout << _("The Mana World version ") << + _"(local build?, PACKAGE_VERSION is not defined)") << std::endl; #endif } @@ -471,17 +523,17 @@ void parseOptions(int argc, char *argv[], Options &options) const char *optstring = "hvud:U:P:Dp:C:H:S:"; 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' }, - { "updatehost", required_argument, 0, 'H' }, + { "password", required_argument, 0, 'P' }, + { "help", no_argument, 0, 'h' }, { "homedir", required_argument, 0, 'S' }, + { "updatehost", required_argument, 0, 'H' }, + { "skipupdate", no_argument, 0, 'u' }, + { "username", required_argument, 0, 'U' }, + { "version", no_argument, 0, 'v' }, { 0 } }; @@ -493,36 +545,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; case 'S': homeDir = optarg; @@ -603,6 +655,13 @@ void accountLogin(Network *network, LoginData *loginData) config.setValue("remember", loginData->remember); } +static void positionDialog(Window *dialog, int screenWidth, int screenHeight) +{ + dialog->setPosition( + (screenWidth - dialog->getWidth()) / 2, + (screenHeight - dialog->getHeight()) / 2); +} + void charLogin(Network *network, LoginData *loginData) { logger->log("Trying to connect to char server..."); @@ -706,6 +765,9 @@ int main(int argc, char *argv[]) unsigned int oldstate = !state; // We start with a status change. + // Needs to be created in main, as the updater uses it + textColor = new Color(); + Game *game = NULL; Window *currentDialog = NULL; Image *login_wallpaper = NULL; @@ -716,7 +778,7 @@ int main(int argc, char *argv[]) gcn::Label *versionLabel = new gcn::Label(PACKAGE_VERSION); top->add(versionLabel, 2, 2); #endif - ProgressBar *progressBar = new ProgressBar(0.0f, 100, 20); + ProgressBar *progressBar = new ProgressBar(0.0f, 100, 20, 168, 116, 31); gcn::Label *progressLabel = new gcn::Label(); top->add(progressBar, 5, top->getHeight() - 5 - progressBar->getHeight()); top->add(progressLabel, 15 + progressBar->getWidth(), @@ -738,12 +800,37 @@ int main(int argc, char *argv[]) loginData.password = options.password; } loginData.hostname = config.getValue("host", "server.themanaworld.org"); - loginData.port = (short)config.getValue("port", 0); + loginData.port = (short)config.getValue("port", 6901); 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; + + wallpaperName = "graphics/images/login_wallpaper.png"; + if (screenWidth >= 1024 && screenWidth < 1280) + wallpaperName = "graphics/images/login_wallpaper_1024x768.png"; + else if (screenWidth >= 1280 && screenWidth < 1440) + wallpaperName = "graphics/images/login_wallpaper_1280x960.png"; + else if (screenWidth >= 1440 && screenWidth < 1600) + wallpaperName = "graphics/images/login_wallpaper_1440x1080.png"; + else if (screenWidth >= 1600) + wallpaperName = "graphics/images/login_wallpaper_1600x1200.png"; + + if (!ResourceManager::getInstance()->exists(wallpaperName)) + wallpaperName = "graphics/images/login_wallpaper.png"; + + login_wallpaper = ResourceManager::getInstance()->getImage(wallpaperName); + + if (!login_wallpaper) + logger->log("Couldn't load %s as wallpaper", wallpaperName.c_str()); + while (state != EXIT_STATE) { // Handle SDL events @@ -779,16 +866,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); @@ -817,7 +894,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 @@ -856,9 +933,12 @@ int main(int argc, char *argv[]) false); // Load XML databases + ColorDB::load(); ItemDB::load(); MonsterDB::load(); NPCDB::load(); + EmoteDB::load(); + state = CHAR_CONNECT_STATE; break; @@ -870,33 +950,50 @@ 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.empty()) + { + ((ServerSelectDialog*) currentDialog)->action( + gcn::ActionEvent(NULL, "ok")); + } } break; - case CHAR_SELECT_STATE: logger->log("State: CHAR_SELECT"); currentDialog = new CharSelectDialog(network, &charInfo, (loginData.sex == 0) ? GENDER_FEMALE : GENDER_MALE); + positionDialog(currentDialog, screenWidth, screenHeight); if (((CharSelectDialog*) currentDialog)-> selectByName(options.playername)) @@ -908,6 +1005,7 @@ int main(int argc, char *argv[]) if (options.chooseDefault) ((CharSelectDialog*) currentDialog)->action( gcn::ActionEvent(NULL, "ok")); + break; case GAME_STATE: @@ -945,13 +1043,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(); @@ -988,8 +1094,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 textColor; #ifdef PACKAGE_VERSION delete versionLabel; #endif @@ -32,8 +32,8 @@ #define PACKAGE_VERSION "0.0.28.1" #endif -#ifndef TMW_DATADIR -#define TMW_DATADIR "" +#ifndef PKG_DATADIR +#define PKG_DATADIR "" #endif /* diff --git a/src/map.cpp b/src/map.cpp index f0c84159..b5e815f3 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -19,21 +19,21 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "map.h" - #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" @@ -68,6 +68,7 @@ TileAnimation::TileAnimation(Animation *ani): { } + void TileAnimation::update() { //update animation @@ -136,6 +137,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++; @@ -256,7 +258,8 @@ void Map::draw(Graphics *graphics, int scrollX, int scrollY) // draw the game world 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, @@ -547,12 +550,15 @@ 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); + } } } @@ -27,9 +27,9 @@ #include "position.h" #include "properties.h" - #include "simpleanimation.h" +class Animation; class AmbientOverlay; class Graphics; class Image; @@ -186,8 +186,7 @@ class Map : public Properties /** * Finds the tile set that a tile with the given global id is part of. */ - Tileset* - getTilesetWithGid(int gid) const; + Tileset* getTilesetWithGid(int gid) const; /** * Get tile reference. @@ -207,26 +206,22 @@ class Map : public Properties /** * Returns the width of this map. */ - int - getWidth() const { return mWidth; } + int getWidth() const { return mWidth; } /** * Returns the height of this map. */ - int - getHeight() const { return mHeight; } + int getHeight() const { return mHeight; } /** * Returns the tile width of this map. */ - int - getTileWidth() const { return mTileWidth; } + int getTileWidth() const { return mTileWidth; } /** * Returns the tile height used by this map. */ - int - getTileHeight() const { return mTileHeight; } + int getTileHeight() const { return mTileHeight; } /** * Find a path from one location to the next. @@ -236,14 +231,12 @@ class Map : public Properties /** * Adds a sprite to the map. */ - SpriteIterator - addSprite(Sprite *sprite); + SpriteIterator addSprite(Sprite *sprite); /** * Removes a sprite from the map. */ - void - removeSprite(SpriteIterator iterator); + void removeSprite(SpriteIterator iterator); /** * Adds a particle effect @@ -270,9 +263,8 @@ class Map : public Properties /** * Draws the overlay graphic to the given graphics output. */ - void - drawOverlay(Graphics *graphics, float scrollX, float scrollY, - int detail); + void drawOverlay(Graphics *graphics, float scrollX, float scrollY, + int detail); /** * Tells whether a tile is occupied by a being. diff --git a/src/monster.cpp b/src/monster.cpp index f50855ca..8f56560b 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -19,18 +19,16 @@ * 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 "resources/monsterdb.h" - -#include "utils/tostring.h" +#include "resources/monsterinfo.h" static const int NAME_X_OFFSET = 16; static const int NAME_Y_OFFSET = 16; @@ -44,6 +42,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++) @@ -61,21 +60,26 @@ 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)); + } } + + mNameColor = 0xff2020; } Monster::~Monster() { if (mText) { - player_node->setTarget(0); + delete mText; + player_node->setTarget(NULL); } } @@ -99,9 +103,11 @@ 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) { @@ -115,13 +121,34 @@ void Monster::setAction(Uint8 action) case ATTACK: currentAction = ACTION_ATTACK; mSprites[BASE_SPRITE]->reset(); + + //attack particle effect + particleEffect = getInfo().getAttackParticleEffect(); + if (!particleEffect.empty() && 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) @@ -164,13 +191,15 @@ const MonsterInfo &Monster::getInfo() const 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, - gcn::Color(255, 64, 64)); + gcn::Graphics::CENTER, gcn::Color(255, 64, 64)); } else { diff --git a/src/monster.h b/src/monster.h index 36812f41..8d7f8ae7 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; @@ -62,8 +62,7 @@ class Monster : public Being /** * Returns the MonsterInfo, with static data about this monster. */ - const MonsterInfo& - getInfo() const; + const MonsterInfo& getInfo() const; /** * Determine whether the mob should show it's name diff --git a/src/net/beinghandler.cpp b/src/net/beinghandler.cpp index e0ade2ae..d3ba4b03 100644 --- a/src/net/beinghandler.cpp +++ b/src/net/beinghandler.cpp @@ -19,24 +19,22 @@ * 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 "../particle.h" -#include "../sound.h" -#include <iostream> -#include "../player_relations.h" #include "../npc.h" +#include "../player_relations.h" +#include "../sound.h" const int EMOTION_TIME = 150; /**< Duration of emotion icon */ @@ -73,6 +71,7 @@ void BeingHandler::handleMessage(MessageIn *msg) Uint16 headTop, headMid, headBottom; Uint16 shoes, gloves; Uint16 weapon, shield; + Uint16 gmstatus; Sint16 param1; int stunMode; Uint32 statusEffects; @@ -211,22 +210,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; @@ -244,28 +238,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); } @@ -279,8 +271,9 @@ void BeingHandler::handleMessage(MessageIn *msg) break; int effectType = msg->readInt32(); + Being* being = beingManager->findBeing(id); - beingManager->findBeing(id)->triggerEffect(effectType); + effectManager->trigger(effectType, being); break; } @@ -450,18 +443,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) @@ -482,16 +479,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()) { @@ -509,11 +506,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: @@ -544,4 +541,3 @@ void BeingHandler::handleMessage(MessageIn *msg) break; } } - diff --git a/src/net/beinghandler.h b/src/net/beinghandler.h index f72f0380..54b82075 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 b7f3ecd4..67c79ec4 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" @@ -36,9 +35,11 @@ #include "../gui/chat.h" #include "../gui/sell.h" +#include "../utils/gettext.h" + extern BuyDialog *buyDialog; -extern SellDialog *sellDialog; extern Window *buySellDialog; +extern SellDialog *sellDialog; BuySellHandler::BuySellHandler() { @@ -105,27 +106,27 @@ void BuySellHandler::handleMessage(MessageIn *msg) } } else { - chatWindow->chatLog("Nothing to sell", BY_SERVER); + chatWindow->chatLog(_("Nothing to sell"), BY_SERVER); current_npc = 0; } break; case SMSG_NPC_BUY_RESPONSE: if (msg->readInt8() == 0) { - chatWindow->chatLog("Thanks for buying", BY_SERVER); + chatWindow->chatLog(_("Thanks for buying"), BY_SERVER); } else { // Reset player money since buy dialog already assumed purchase // would go fine buyDialog->setMoney(player_node->mGp); - chatWindow->chatLog("Unable to buy", BY_SERVER); + chatWindow->chatLog(_("Unable to buy"), BY_SERVER); } break; case SMSG_NPC_SELL_RESPONSE: if (msg->readInt8() == 0) { - chatWindow->chatLog("Thanks for selling", BY_SERVER); + chatWindow->chatLog(_("Thanks for selling"), BY_SERVER); } else { - chatWindow->chatLog("Unable to sell", BY_SERVER); + chatWindow->chatLog(_("Unable to sell"), BY_SERVER); } break; } diff --git a/src/net/charserverhandler.cpp b/src/net/charserverhandler.cpp index f00615d7..932cf705 100644 --- a/src/net/charserverhandler.cpp +++ b/src/net/charserverhandler.cpp @@ -20,9 +20,7 @@ */ #include "charserverhandler.h" - #include "messagein.h" -#include "network.h" #include "protocol.h" #include "../game.h" @@ -31,8 +29,11 @@ #include "../logindata.h" #include "../main.h" -#include "../gui/ok_dialog.h" #include "../gui/char_select.h" +#include "../gui/ok_dialog.h" + +#include "../utils/gettext.h" +#include "../utils/tostring.h" CharServerHandler::CharServerHandler(): mCharCreateDialog(0) @@ -53,7 +54,7 @@ CharServerHandler::CharServerHandler(): void CharServerHandler::handleMessage(MessageIn *msg) { - int slot, code; + int slot, flags, code; LocalPlayer *tempPlayer; logger->log("CharServerHandler: Packet ID: %x, Length: %d", @@ -66,30 +67,32 @@ void CharServerHandler::handleMessage(MessageIn *msg) switch (code) { case 0: - errorMessage = "Authentication failed"; + errorMessage = _("Authentication failed"); break; case 1: - errorMessage = "Map server(s) offline"; + errorMessage = _("Map server(s) offline"); break; case 2: - errorMessage = "This account is already logged in"; + errorMessage = _("This account is already logged in"); break; case 3: - errorMessage = "Speed hack detected"; + errorMessage = _("Speed hack detected"); break; case 8: - errorMessage = "Duplicated login"; + errorMessage = _("Duplicated login"); break; default: - errorMessage = "Unknown connection error"; + errorMessage = _("Unknown connection error"); break; } state = ERROR_STATE; break; 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); + msg->skip(16); // Unused // Derive number of characters from message length n_character = (msg->getLength() - 24) / 106; @@ -100,7 +103,7 @@ void CharServerHandler::handleMessage(MessageIn *msg) mCharInfo->select(slot); mCharInfo->setEntry(tempPlayer); logger->log("CharServer: Player: %s (%d)", - tempPlayer->getName().c_str(), slot); + tempPlayer->getName().c_str(), slot); } state = CHAR_SELECT_STATE; @@ -109,13 +112,13 @@ void CharServerHandler::handleMessage(MessageIn *msg) case 0x006c: switch (msg->readInt8()) { case 0: - errorMessage = "Access denied"; + errorMessage = _("Access denied"); break; case 1: - errorMessage = "Cannot use this ID"; + errorMessage = _("Cannot use this ID"); break; default: - errorMessage = "Unknown failure to select character"; + errorMessage = _("Unknown failure to select character"); break; } mCharInfo->unlock(); @@ -137,8 +140,8 @@ void CharServerHandler::handleMessage(MessageIn *msg) break; case 0x006e: - new OkDialog("Error", "Failed to create character. Most likely" - " the name is already taken."); + new OkDialog(_("Error"), _("Failed to create character. Most likely" + " the name is already taken.")); if (mCharCreateDialog) mCharCreateDialog->unlock(); @@ -149,12 +152,12 @@ void CharServerHandler::handleMessage(MessageIn *msg) mCharInfo->setEntry(0); mCharInfo->unlock(); n_character--; - new OkDialog("Info", "Player deleted"); + new OkDialog(_("Info"), _("Player deleted")); break; case 0x0070: mCharInfo->unlock(); - new OkDialog("Error", "Failed to delete character."); + new OkDialog(_("Error"), _("Failed to delete character.")); break; case 0x0071: diff --git a/src/net/chathandler.cpp b/src/net/chathandler.cpp index d25d4bcd..25877907 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" @@ -34,6 +33,7 @@ #include "../gui/chat.h" +#include "../utils/gettext.h" #include "../utils/tostring.h" #include "../utils/trim.h" @@ -73,10 +73,10 @@ void ChatHandler::handleMessage(MessageIn *msg) //chatWindow->chatLog("Whisper sent", BY_SERVER); break; case 0x01: - chatWindow->chatLog("Whisper could not be sent, user is offline", BY_SERVER); + chatWindow->chatLog(_("Whisper could not be sent, user is offline"), BY_SERVER); break; case 0x02: - chatWindow->chatLog("Whisper could not be sent, ignored by user", BY_SERVER); + chatWindow->chatLog(_("Whisper could not be sent, ignored by user"), BY_SERVER); break; } break; diff --git a/src/net/equipmenthandler.cpp b/src/net/equipmenthandler.cpp index e5bbf6fe..9a3c396a 100644 --- a/src/net/equipmenthandler.cpp +++ b/src/net/equipmenthandler.cpp @@ -20,11 +20,9 @@ */ #include "equipmenthandler.h" - #include "messagein.h" #include "protocol.h" -#include "../beingmanager.h" #include "../equipment.h" #include "../inventory.h" #include "../item.h" @@ -33,6 +31,8 @@ #include "../gui/chat.h" +#include "../utils/gettext.h" + EquipmentHandler::EquipmentHandler() { static const Uint16 _messages[] = { @@ -98,7 +98,7 @@ void EquipmentHandler::handleMessage(MessageIn *msg) logger->log("Equipping: %i %i %i", index, equipPoint, type); if (!type) { - chatWindow->chatLog("Unable to equip.", BY_SERVER); + chatWindow->chatLog(_("Unable to equip."), BY_SERVER); break; } @@ -107,7 +107,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 +118,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); } @@ -130,7 +136,7 @@ void EquipmentHandler::handleMessage(MessageIn *msg) type = msg->readInt8(); if (!type) { - chatWindow->chatLog("Unable to unequip.", BY_SERVER); + chatWindow->chatLog(_("Unable to unequip."), BY_SERVER); break; } @@ -152,24 +158,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 +179,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 cd9b5ce0..46f03e28 100644 --- a/src/net/inventoryhandler.cpp +++ b/src/net/inventoryhandler.cpp @@ -19,22 +19,24 @@ * 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/gettext.h" +#include "../utils/strprintf.h" #include "../utils/tostring.h" InventoryHandler::InventoryHandler() @@ -45,6 +47,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 +61,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,21 +132,22 @@ 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); + chatWindow->chatLog(_("Unable to pick up item"), BY_SERVER); } else { const ItemInfo &itemInfo = ItemDB::get(itemId); const std::string amountStr = (amount > 1) ? toString(amount) : "a"; - chatWindow->chatLog("You picked up " + amountStr + " " + - itemInfo.getName(), BY_SERVER); + chatWindow->chatLog(strprintf(_("You picked up %s %s"), + amountStr.c_str(), itemInfo.getName().c_str()), BY_SERVER); if (Item *item = inventory->getItem(index)) { item->setId(itemId); @@ -141,11 +184,44 @@ void InventoryHandler::handleMessage(MessageIn *msg) amount = msg->readInt16(); if (msg->readInt8() == 0) { - chatWindow->chatLog("Failed to use item", BY_SERVER); + chatWindow->chatLog(_("Failed to use item"), BY_SERVER); } else { if (Item *item = inventory->getItem(index)) 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 03313a55..8c4af4e4 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 c9eec9f4..4fd0f373 100644 --- a/src/net/loginhandler.cpp +++ b/src/net/loginhandler.cpp @@ -20,9 +20,7 @@ */ #include "loginhandler.h" - #include "messagein.h" -#include "network.h" #include "protocol.h" #include "../log.h" @@ -30,6 +28,10 @@ #include "../main.h" #include "../serverinfo.h" +#include "../utils/gettext.h" +#include "../utils/strprintf.h" +#include "../utils/tostring.h" + extern SERVER_INFO **server_info; LoginHandler::LoginHandler() @@ -56,16 +58,16 @@ void LoginHandler::handleMessage(MessageIn *msg) switch (code) { case 0: - errorMessage = "Authentication failed"; + errorMessage = _("Authentication failed"); break; case 1: - errorMessage = "No servers available"; + errorMessage = _("No servers available"); break; case 2: - errorMessage = "This account is already logged in"; + errorMessage = _("This account is already logged in"); break; default: - errorMessage = "Unknown connection error"; + errorMessage = _("Unknown connection error"); break; } state = ERROR_STATE; @@ -78,7 +80,7 @@ void LoginHandler::handleMessage(MessageIn *msg) mUpdateHost = msg->readString(len); logger->log("Received update host \"%s\" from login server", - mUpdateHost.c_str()); + mUpdateHost.c_str()); break; case 0x0069: @@ -120,30 +122,34 @@ void LoginHandler::handleMessage(MessageIn *msg) switch (code) { case 0: - errorMessage = "Unregistered ID"; + errorMessage = _("Unregistered ID"); break; case 1: - errorMessage = "Wrong password"; + errorMessage = _("Wrong password"); break; case 2: - errorMessage = "Account expired"; + errorMessage = _("Account expired"); break; case 3: - errorMessage = "Rejected from server"; + errorMessage = _("Rejected from server"); break; case 4: - errorMessage = "You have been permanently banned from the game. Please contact the GM Team"; + errorMessage = _("You have been permanently banned from " + "the game. Please contact the GM Team."); break; case 6: - errorMessage = "You have been temporarily banned from the game until " - + msg->readString(20) + ".\n Please contact the GM team via the forums"; + errorMessage = strprintf(_("You have been temporarily " + "banned from the game until " + "%s.\n Please contact the GM " + "team via the forums."), + msg->readString(20).c_str()); break; case 9: - errorMessage = "This user name is already taken"; + errorMessage = _("This user name is already taken"); break; default: - errorMessage = "Unknown error"; + errorMessage = _("Unknown error"); break; } state = ERROR_STATE; diff --git a/src/net/loginhandler.h b/src/net/loginhandler.h index a380c767..df86b634 100644 --- a/src/net/loginhandler.h +++ b/src/net/loginhandler.h @@ -22,9 +22,10 @@ #ifndef NET_LOGINHANDLER_H #define NET_LOGINHANDLER_H -#include "messagehandler.h" #include <string> +#include "messagehandler.h" + struct LoginData; class LoginHandler : public MessageHandler diff --git a/src/net/maploginhandler.cpp b/src/net/maploginhandler.cpp index 68652c79..b5192bd7 100644 --- a/src/net/maploginhandler.cpp +++ b/src/net/maploginhandler.cpp @@ -20,7 +20,6 @@ */ #include "maploginhandler.h" - #include "messagein.h" #include "protocol.h" @@ -28,6 +27,8 @@ #include "../log.h" #include "../main.h" +#include "../utils/gettext.h" + MapLoginHandler::MapLoginHandler() { static const Uint16 _messages[] = { @@ -51,13 +52,13 @@ void MapLoginHandler::handleMessage(MessageIn *msg) switch (code) { case 0: - errorMessage = "Authentication failed"; + errorMessage = _("Authentication failed"); break; case 2: - errorMessage = "This account is already logged in"; + errorMessage = _("This account is already logged in"); break; default: - errorMessage = "Unknown connection error"; + errorMessage = _("Unknown connection error"); break; } state = ERROR_STATE; diff --git a/src/net/messagehandler.cpp b/src/net/messagehandler.cpp index 063532d4..f1561a31 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 cbcde3fe..a288d936 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), @@ -39,15 +38,13 @@ MessageIn::MessageIn(const char *data, unsigned int length): mId = readInt16(); } -Sint8 -MessageIn::readInt8() +Sint8 MessageIn::readInt8() { assert(mPos < mLength); return mData[mPos++]; } -Sint16 -MessageIn::readInt16() +Sint16 MessageIn::readInt16() { assert(mPos + 2 <= mLength); mPos += 2; @@ -58,8 +55,7 @@ MessageIn::readInt16() #endif } -Sint32 -MessageIn::readInt32() +Sint32 MessageIn::readInt32() { assert(mPos + 4 <= mLength); mPos += 4; @@ -70,8 +66,7 @@ MessageIn::readInt32() #endif } -void -MessageIn::readCoordinates(Uint16 &x, Uint16 &y, Uint8 &direction) +void MessageIn::readCoordinates(Uint16 &x, Uint16 &y, Uint8 &direction) { assert(mPos + 3 <= mLength); @@ -120,8 +115,7 @@ MessageIn::readCoordinates(Uint16 &x, Uint16 &y, Uint8 &direction) mPos += 3; } -void -MessageIn::readCoordinatePair(Uint16 &srcX, Uint16 &srcY, +void MessageIn::readCoordinatePair(Uint16 &srcX, Uint16 &srcY, Uint16 &dstX, Uint16 &dstY) { assert(mPos + 5 <= mLength); @@ -143,15 +137,13 @@ MessageIn::readCoordinatePair(Uint16 &srcX, Uint16 &srcY, mPos += 5; } -void -MessageIn::skip(unsigned int length) +void MessageIn::skip(unsigned int length) { assert(mPos + length <= mLength); mPos += length; } -std::string -MessageIn::readString(int length) +std::string MessageIn::readString(int length) { // Get string length if (length < 0) { diff --git a/src/net/messagein.h b/src/net/messagein.h index 1788aa97..0ff6e78a 100644 --- a/src/net/messagein.h +++ b/src/net/messagein.h @@ -22,8 +22,8 @@ #ifndef MESSAGEIN_ #define MESSAGEIN_ -#include <string> #include <SDL_types.h> +#include <string> /** * Used for parsing an incoming message. @@ -43,14 +43,12 @@ class MessageIn /** * Returns the message ID. */ - short - getId() { return mId; } + short getId() { return mId; } /** * Returns the message length. */ - unsigned int - getLength() { return mLength; } + unsigned int getLength() { return mLength; } Sint8 readInt8(); /**< Reads a byte. */ Sint16 readInt16(); /**< Reads a short. */ @@ -60,30 +58,26 @@ class MessageIn * Reads a special 3 byte block used by eAthena, containing x and y * coordinates and direction. */ - void - readCoordinates(Uint16 &x, Uint16 &y, Uint8 &direction); + void readCoordinates(Uint16 &x, Uint16 &y, Uint8 &direction); /** * Reads a special 5 byte block used by eAthena, containing a source * and destination coordinate pair. */ - void - readCoordinatePair(Uint16 &srcX, Uint16 &srcY, - Uint16 &dstX, Uint16 &dstY); + void readCoordinatePair(Uint16 &srcX, Uint16 &srcY, + Uint16 &dstX, Uint16 &dstY); /** * Skips a given number of bytes. */ - void - skip(unsigned int length); + void skip(unsigned int length); /** * Reads a string. If a length is not given (-1), it is assumed * that the length of the string is stored in a short at the * start of the string. */ - std::string - readString(int length = -1); + std::string readString(int length = -1); private: const char* mData; /**< The message data. */ diff --git a/src/net/messageout.cpp b/src/net/messageout.cpp index 8f361e5e..bf4957be 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 1463c696..059da779 100644 --- a/src/net/network.cpp +++ b/src/net/network.cpp @@ -19,14 +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> +#include "../utils/tostring.h" /** Warning: buffers and other variables are shared, so there can be only one connection active at a time */ @@ -419,21 +419,7 @@ void Network::receive() SDLNet_FreeSocketSet(set); } -char *iptostring(int address) -{ - static char asciiIP[16]; - - sprintf(asciiIP, "%i.%i.%i.%i", - (unsigned char)(address), - (unsigned char)(address >> 8), - (unsigned char)(address >> 16), - (unsigned char)(address >> 24)); - - return asciiIP; -} - -void -Network::setError(const std::string& error) +void Network::setError(const std::string& error) { logger->log("Network error: %s", error.c_str()); mError = error; diff --git a/src/net/network.h b/src/net/network.h index c942a819..02fe7538 100644 --- a/src/net/network.h +++ b/src/net/network.h @@ -48,47 +48,33 @@ class Network ~Network(); - bool - connect(const std::string &address, short port); + bool connect(const std::string &address, short port); - void - disconnect(); + void disconnect(); - void - registerHandler(MessageHandler *handler); + void registerHandler(MessageHandler *handler); - void - unregisterHandler(MessageHandler *handler); + void unregisterHandler(MessageHandler *handler); - void - clearHandlers(); + void clearHandlers(); - int - getState() const { return mState; } + int getState() const { return mState; } - const std::string& - getError() const { return mError; } + const std::string& getError() const { return mError; } - bool - isConnected() const { return mState == CONNECTED; } + bool isConnected() const { return mState == CONNECTED; } - int - getInSize() const { return mInSize; } + int getInSize() const { return mInSize; } - void - skip(int len); + void skip(int len); - bool - messageReady(); + bool messageReady(); - MessageIn - getNextMessage(); + MessageIn getNextMessage(); - void - dispatchMessages(); + void dispatchMessages(); - void - flush(); + void flush(); // ERROR replaced by NET_ERROR because already defined in Windows enum { @@ -100,17 +86,13 @@ class Network }; protected: - void - setError(const std::string& error); + void setError(const std::string& error); - Uint16 - readWord(int pos); + Uint16 readWord(int pos); - bool - realConnect(); + bool realConnect(); - void - receive(); + void receive(); TCPsocket mSocket; @@ -133,7 +115,4 @@ class Network MessageHandlers mMessageHandlers; }; -/** Convert an address from int format to string */ -char *iptostring(int address); - #endif diff --git a/src/net/npchandler.cpp b/src/net/npchandler.cpp index b0314e47..ea03537f 100644 --- a/src/net/npchandler.cpp +++ b/src/net/npchandler.cpp @@ -19,18 +19,18 @@ * 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/npc_text.h" #include "../gui/npcintegerdialog.h" #include "../gui/npclistdialog.h" #include "../gui/npcstringdialog.h" -#include "../gui/npc_text.h" extern NpcIntegerDialog *npcIntegerDialog; extern NpcListDialog *npcListDialog; @@ -60,6 +60,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); @@ -68,17 +69,17 @@ 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; + current_npc = NULL; break; case SMSG_NPC_NEXT: @@ -91,6 +92,7 @@ void NPCHandler::handleMessage(MessageIn *msg) current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); npcIntegerDialog->setRange(0, 2147483647); npcIntegerDialog->setVisible(true); + npcIntegerDialog->requestFocus(); break; case SMSG_NPC_STR_INPUT: @@ -99,6 +101,7 @@ void NPCHandler::handleMessage(MessageIn *msg) current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); npcStringDialog->setValue(""); npcStringDialog->setVisible(true); + npcStringDialog->requestFocus(); break; } } diff --git a/src/net/partyhandler.cpp b/src/net/partyhandler.cpp new file mode 100644 index 00000000..fe7db55d --- /dev/null +++ b/src/net/partyhandler.cpp @@ -0,0 +1,122 @@ +/* + * 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 "../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) + { + 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..048da9b1 --- /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 PARTYHANDLER_H +#define 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 5d989a66..cfdfbe63 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,9 @@ #include "../gui/skill.h" #include "../gui/viewport.h" +#include "../utils/tostring.h" +#include "../utils/gettext.h" + // TODO Move somewhere else OkDialog *weightNotice = NULL; OkDialog *deathNotice = NULL; @@ -50,8 +51,9 @@ extern BuyDialog *buyDialog; 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. */ +// Max. distance we are willing to scroll after a teleport; +// everything beyond will reset the port hard. +static const int MAP_TELEPORT_SCROLL_DISTANCE = 8; /** * Listener used for handling the overweigth message. @@ -110,10 +112,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 +135,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 +147,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; } @@ -153,7 +158,9 @@ void PlayerHandler::handleMessage(MessageIn *msg) player_node->mX = x; player_node->mY = y; - logger->log("Adjust scrolling by %d:%d", (int)scrollOffsetX, (int)scrollOffsetY); + logger->log("Adjust scrolling by %d:%d", + (int)scrollOffsetX, + (int)scrollOffsetY); viewport->scrollBy(scrollOffsetX, scrollOffsetY); } @@ -173,6 +180,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; @@ -183,20 +193,16 @@ void PlayerHandler::handleMessage(MessageIn *msg) player_node->mTotalWeight < player_node->mMaxWeight / 2) { - weightNotice = new OkDialog("Message", - "You are carrying more then half " - "your weight. You are unable to " - "regain health."); + weightNotice = new OkDialog(_("Message"), + _("You are carrying more than " + "half your weight. You are " + "unable to regain health.")); weightNotice->addActionListener( &weightListener); } 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 +211,57 @@ 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) + if (player_node->mHp == 0 && !deathNotice) { - 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."), + // NetHack reference: + _("Do you want your possessions identified?"), + // Secret of Mana reference: + _("Sadly, no trace of you was ever found..."), + // Final Fantasy VI reference: + _("Annihilated."), + // Earthbound reference: + _("Looks like you got your head handed to you."), + // Leisure Suit Larry 1 reference: + _("You screwed up again, dump your body down the tubes " + "and get you another one."), + // Monty Python references (Dead Parrot sketch mostly): + _("You're not dead yet. You're just resting."), + _("You are no more."), + _("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.") + }; + std::string message(deadMsg[rand()%27]); + + deathNotice = new OkDialog(_("Message"), message); deathNotice->addActionListener(&deathListener); player_node->setAction(Being::DEAD); } @@ -229,7 +280,7 @@ void PlayerHandler::handleMessage(MessageIn *msg) Uint32 curGp = player_node->mGp; player_node->mGp = msg->readInt32(); if (player_node->mGp > curGp) - chatWindow->chatLog("You picked up " + + chatWindow->chatLog(_("You picked up ") + toString(player_node->mGp - curGp) + " GP", BY_SERVER); } @@ -293,7 +344,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(); @@ -352,7 +403,7 @@ void PlayerHandler::handleMessage(MessageIn *msg) switch (type) { case 0: - chatWindow->chatLog("Equip arrows first", + chatWindow->chatLog(_("Equip arrows first"), BY_SERVER); break; default: @@ -361,18 +412,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 3bdeb429..e9053451 100644 --- a/src/net/protocol.h +++ b/src/net/protocol.h @@ -22,8 +22,11 @@ #ifndef PROTOCOL_ #define 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_CONNECTION_PROBLEM 0x0081 #define SMSG_UPDATE_HOST 0x0063 /**< Custom update host packet */ #define SMSG_PLAYER_UPDATE_1 0x01d8 @@ -67,6 +70,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 @@ -83,6 +87,7 @@ #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 @@ -91,7 +96,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 @@ -100,6 +125,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 @@ -118,6 +145,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 17b1f117..e2185524 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 da493302..c5465835 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" @@ -33,6 +32,8 @@ #include "../gui/confirm_dialog.h" #include "../gui/trade.h" +#include "../utils/gettext.h" + std::string tradePartnerName; /** @@ -87,9 +88,9 @@ void TradeHandler::handleMessage(MessageIn *msg) player_node->setTrading(true); ConfirmDialog *dlg; - dlg = new ConfirmDialog("Request for trade", + dlg = new ConfirmDialog(_("Request for trade"), tradePartnerName + - " wants to trade with you, do you accept?"); + _(" wants to trade with you, do you accept?")); dlg->addActionListener(&listener); } else @@ -103,37 +104,35 @@ void TradeHandler::handleMessage(MessageIn *msg) switch (msg->readInt8()) { case 0: // Too far away - chatWindow->chatLog("Trading isn't possible. " - "Trade partner is too far away.", + chatWindow->chatLog(_("Trading isn't possible. Trade partner is too far away."), BY_SERVER); break; case 1: // Character doesn't exist - chatWindow->chatLog("Trading isn't possible. " - "Character doesn't exist.", + chatWindow->chatLog(_("Trading isn't possible. Character doesn't exist."), BY_SERVER); break; case 2: // Invite request check failed... - chatWindow->chatLog("Trade cancelled due to an " - "unknown reason.", BY_SERVER); + chatWindow->chatLog(_("Trade cancelled due to an unknown reason."), + BY_SERVER); break; case 3: // Trade accepted tradeWindow->reset(); tradeWindow->setCaption( - "Trade: You and " + tradePartnerName); + _("Trade: You and ") + tradePartnerName); tradeWindow->setVisible(true); break; case 4: // Trade cancelled if (player_relations.hasPermission(tradePartnerName, PlayerRelation::SPEECH_LOG)) - chatWindow->chatLog("Trade with " + tradePartnerName + - " cancelled", BY_SERVER); + chatWindow->chatLog(_("Trade with ") + tradePartnerName + + _(" cancelled"), BY_SERVER); // otherwise ignore silently tradeWindow->setVisible(false); player_node->setTrading(false); break; default: // Shouldn't happen as well, but to be sure - chatWindow->chatLog("Unhandled trade cancel packet", + chatWindow->chatLog(_("Unhandled trade cancel packet"), BY_SERVER); break; } @@ -183,19 +182,17 @@ void TradeHandler::handleMessage(MessageIn *msg) break; case 1: // Add item failed - player overweighted - chatWindow->chatLog("Failed adding item. Trade " - "partner is over weighted.", + chatWindow->chatLog(_("Failed adding item. Trade partner is over weighted."), 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); + chatWindow->chatLog(_("Failed adding item for unknown reason."), + BY_SERVER); break; } } @@ -207,14 +204,14 @@ void TradeHandler::handleMessage(MessageIn *msg) break; case SMSG_TRADE_CANCEL: - chatWindow->chatLog("Trade canceled.", BY_SERVER); + chatWindow->chatLog(_("Trade canceled."), BY_SERVER); tradeWindow->setVisible(false); tradeWindow->reset(); player_node->setTrading(false); break; case SMSG_TRADE_COMPLETE: - chatWindow->chatLog("Trade completed.", BY_SERVER); + chatWindow->chatLog(_("Trade completed."), BY_SERVER); tradeWindow->setVisible(false); tradeWindow->reset(); player_node->setTrading(false); diff --git a/src/npc.cpp b/src/npc.cpp index aed6d87b..dfbc7d16 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -19,15 +19,15 @@ * 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" NPC *current_npc = 0; @@ -36,7 +36,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); @@ -54,28 +54,53 @@ 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; + + mNameColor = 0x21bbbb; } 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, - gcn::Color(200, 200, 255)); + 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, gcn::Color(200, 200, 255)); + Being::setName(displayName + " (NPC)"); +} + +void NPC::setGender(Gender 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 NPC::getType() const @@ -22,13 +22,13 @@ #ifndef NPC_H #define 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,9 +36,10 @@ class NPC : public Being ~NPC(); void setName(const std::string &name); + void setGender(Gender gender); + void setSprite(int slot, int id, std::string color); - virtual Type - getType() const; + virtual Type getType() const; void talk(); void nextDialog(); diff --git a/src/openglgraphics.cpp b/src/openglgraphics.cpp index 01208c36..e7e7b204 100644 --- a/src/openglgraphics.cpp +++ b/src/openglgraphics.cpp @@ -19,30 +19,23 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "main.h" - -#ifdef USE_OPENGL - -#ifndef GL_TEXTURE_RECTANGLE_ARB -#define GL_TEXTURE_RECTANGLE_ARB 0x84F5 -#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8 -#endif +#include <SDL.h> +#include "log.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), diff --git a/src/particle.cpp b/src/particle.cpp index 99a4dd36..45cbb4c9 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -22,13 +22,14 @@ #include <algorithm> #include <cmath> -#include "particle.h" +#include <guichan/color.hpp> #include "animationparticle.h" #include "configuration.h" #include "imageparticle.h" #include "log.h" #include "map.h" +#include "particle.h" #include "particleemitter.h" #include "textparticle.h" @@ -251,7 +252,7 @@ void Particle::moveTo(float x, float y) } Particle *Particle::addEffect(const std::string &particleEffectFile, - int pixelX, int pixelY) + int pixelX, int pixelY, int rotation) { Particle *newParticle = NULL; @@ -313,7 +314,7 @@ Particle *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); } diff --git a/src/particle.h b/src/particle.h index 43fdda01..e79a46dd 100644 --- a/src/particle.h +++ b/src/particle.h @@ -25,8 +25,6 @@ #include <list> #include <string> -#include <guichan/color.hpp> - #include "guichanfwd.h" #include "sprite.h" #include "vector.h" @@ -102,7 +100,7 @@ class Particle : public Sprite * particleEffectFile. */ Particle *addEffect(const std::string &particleEffectFile, - int pixelX, int pixelY); + int pixelX, int pixelY, int rotation = 0); /** * Creates a standalone text particle. diff --git a/src/particlecontainer.cpp b/src/particlecontainer.cpp index c570d6d3..63f89079 100644 --- a/src/particlecontainer.cpp +++ b/src/particlecontainer.cpp @@ -21,6 +21,7 @@ #include <cassert> +#include "particle.h" #include "particlecontainer.h" @@ -44,7 +45,6 @@ void ParticleContainer::clear() mNext->clear(); } - void ParticleContainer::moveTo(float x, float y) { if (mNext) @@ -109,9 +109,6 @@ void ParticleList::moveTo(float x, float y) } } - - - // -- particle vector ---------------------------------------- ParticleVector::ParticleVector(ParticleContainer *parent, bool delParent): diff --git a/src/particlecontainer.h b/src/particlecontainer.h index 27e02f8b..26539dd7 100644 --- a/src/particlecontainer.h +++ b/src/particlecontainer.h @@ -25,8 +25,7 @@ #include <list> #include <vector> -#include "particle.h" - +class Particle; /** * Set of particle effects. May be stacked with other ParticleContainers. All @@ -65,8 +64,6 @@ protected: ParticleContainer *mNext; /**< Contained container, if any */ }; - - /** * Linked list of particle effects. */ @@ -94,7 +91,6 @@ protected: std::list<Particle *> mElements; /**< Contained particle effects */ }; - /** * Particle container with indexing facilities */ diff --git a/src/particleemitter.cpp b/src/particleemitter.cpp index 076bd740..25e6ade5 100644 --- a/src/particleemitter.cpp +++ b/src/particleemitter.cpp @@ -19,31 +19,29 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "particleemitter.h" +#include <cmath> #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 <cmath> +#include "resources/resourcemanager.h" #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) { mMap = map; mParticleTarget = target; - //initializing default values + // Initializing default values mParticlePosX.set(0.0f); mParticlePosY.set(0.0f); mParticlePosZ.set(0.0f); @@ -93,7 +91,7 @@ ParticleEmitter::ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map * { std::string image = XML::getProperty(propertyNode, "value", ""); // Don't leak when multiple images are defined - if (image != "" && !mParticleImage) + if (!image.empty() && !mParticleImage) { ResourceManager *resman = ResourceManager::getInstance(); mParticleImage = resman->getImage(image); @@ -102,7 +100,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,26 +310,25 @@ 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; } -std::list<Particle *> -ParticleEmitter::createParticles(int tick) +std::list<Particle *> ParticleEmitter::createParticles(int tick) { std::list<Particle *> newParticles; diff --git a/src/particleemitter.h b/src/particleemitter.h index 593ecb3a..67b35ae2 100644 --- a/src/particleemitter.h +++ b/src/particleemitter.h @@ -26,10 +26,10 @@ #include "utils/xml.h" -#include "resources/animation.h" - #include "particleemitterprop.h" +#include "resources/animation.h" + class Image; class Map; class Particle; @@ -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) @@ -70,8 +70,7 @@ class ParticleEmitter /** * Sets the target of the particles that are created */ - void - setTarget(Particle *target) + void setTarget(Particle *target) { mParticleTarget = target; }; private: diff --git a/src/particleemitterprop.h b/src/particleemitterprop.h index d9b6350f..e68ac222 100644 --- a/src/particleemitterprop.h +++ b/src/particleemitterprop.h @@ -19,7 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <cstdlib> #include <cmath> /** diff --git a/src/party.cpp b/src/party.cpp new file mode 100644 index 00000000..589aa9b1 --- /dev/null +++ b/src/party.cpp @@ -0,0 +1,217 @@ +/* + * 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 "being.h" +#include "localplayer.h" +#include "party.h" + +#include "gui/chat.h" +#include "gui/confirm_dialog.h" + +#include "net/messageout.h" +#include "net/protocol.h" + +#include "utils/gettext.h" +#include "utils/strprintf.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.empty()) + { + 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(strprintf(_("%s is already a member of a party."), + nick.c_str()), BY_SERVER); + break; + case 1: + mChat->chatLog(strprintf(_("%s refused your invitation."), + nick.c_str()), BY_SERVER); + break; + case 2: + mChat->chatLog(strprintf(_("%s is now a member of your party."), + nick.c_str()), 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.empty()) + { + mChat->chatLog(_("You can\'t have a blank party name!"), BY_SERVER); + return; + } + mCreating = false; + ConfirmDialog *dlg = new ConfirmDialog(_("Invite to party"), + strprintf(_("%s invites you to join" + " the %s party, do you accept?"), + nick.c_str(), partyName.c_str())); + 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(strprintf(_("%s has left your party."), nick.c_str()), + BY_SERVER); +} + +void Party::receiveChat(Being *being, const std::string &msg) +{ + if (!being) + { + return; + } + if (being->getType() != Being::PLAYER) + { + mChat->chatLog(_("Party chat received, but being is not a player"), + BY_SERVER); + return; + } + being->setSpeech(msg, SPEECH_TIME); + mChat->chatLog(being->getName() + " : " + msg, BY_PARTY); +} + +void Party::help(const std::string &msg) +{ + if (msg.empty()) + { + 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..98252c37 --- /dev/null +++ b/src/party.h @@ -0,0 +1,76 @@ +/* + * 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 PARTY_H +#define 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(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 5ab94595..a2a263b3 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -19,20 +19,16 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "player.h" - #include "animatedsprite.h" #include "game.h" -#include "graphics.h" -#include "log.h" +#include "player.h" +#include "text.h" +#include "resources/colordb.h" #include "resources/itemdb.h" -#include "resources/iteminfo.h" #include "utils/strprintf.h" -#include <iostream> - static const int NAME_X_OFFSET = 15; static const int NAME_Y_OFFSET = 30; @@ -44,19 +40,25 @@ Player::Player(int id, int job, Map *map): Player::~Player() { - if (mName) - { - delete mName; - } + delete mName; } 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, - gcn::Color(255, 255, 255)); + if (mIsGM) + { + mNameColor = 0x009000; + mName = new FlashText("(GM) " + name, mPx + NAME_X_OFFSET, mPy + NAME_Y_OFFSET, + gcn::Graphics::CENTER, gcn::Color(0, 255, 0)); + } + else + { + mNameColor = 0x202020; + mName = new FlashText(name, mPx + NAME_X_OFFSET, mPy + NAME_Y_OFFSET, + gcn::Graphics::CENTER, gcn::Color(255, 255, 255)); + } Being::setName(name); } } @@ -64,6 +66,18 @@ void Player::setName(const std::string &name) 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) { @@ -126,13 +140,13 @@ void Player::setGender(Gender 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); } @@ -155,11 +169,12 @@ void Player::setSprite(int slot, int id, std::string color) std::string filename = ItemDB::get(id).getSprite(mGender); AnimatedSprite *equipmentSprite = NULL; - if (filename != "") + if (!filename.empty()) { - if (color!="") filename += "|" + color; + if (!color.empty()) + filename += "|" + color; equipmentSprite = AnimatedSprite::load( - "graphics/sprites/" + filename); + "graphics/sprites/" + filename); } if (equipmentSprite) @@ -187,4 +202,3 @@ void Player::updateCoords() } } - diff --git a/src/player.h b/src/player.h index 7fe2a09c..f9911bb8 100644 --- a/src/player.h +++ b/src/player.h @@ -23,8 +23,8 @@ #define PLAYER_H #include "being.h" -#include "text.h" +class FlashText; class Graphics; class Map; @@ -43,17 +43,13 @@ class Player : public Being /** * Set up mName to be the character's name */ - virtual void - setName(const std::string &name); + virtual void setName(const std::string &name); - virtual void - logic(); + virtual void logic(); - virtual Type - getType() const; + virtual Type getType() const; - virtual void - setGender(Gender gender); + virtual void setGender(Gender gender); /** * Sets the hair style and color for this player. @@ -70,8 +66,7 @@ class Player : public Being /** * Sets visible equipments for this player. */ - virtual void - setSprite(int slot, int id, std::string color = ""); + virtual void setSprite(int slot, int id, std::string color = ""); /** * Flash the player's name @@ -79,9 +74,8 @@ class Player : public Being void flash(int time); protected: - void updateCoords(); + virtual void updateCoords(); - private: FlashText *mName; }; diff --git a/src/player_relations.cpp b/src/player_relations.cpp index 09859c59..c82876e1 100644 --- a/src/player_relations.cpp +++ b/src/player_relations.cpp @@ -19,12 +19,14 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <algorithm> + +#include "being.h" #include "beingmanager.h" -#include "player_relations.h" +#include "configuration.h" #include "graphics.h" -#include "gui/gui.h" - -#include <algorithm> +#include "player.h" +#include "player_relations.h" #define PLAYER_IGNORE_STRATEGY_NOP "nop" #define PLAYER_IGNORE_STRATEGY_EMOTE0 "emote0" @@ -56,7 +58,7 @@ class PlayerConfSerialiser : public ConfigurationListManager<std::pair<std::stri std::map<std::string, PlayerRelation *> *container) { std::string name = cobj->getValue(NAME, ""); - if (name == "") + if (name.empty()) return container; if (!(*container)[name]) { @@ -90,8 +92,7 @@ PlayerRelationsManager::PlayerRelationsManager() : { } -void -PlayerRelationsManager::clear() +void PlayerRelationsManager::clear() { std::vector<std::string> *names = getPlayers(); for (std::vector<std::string>::const_iterator @@ -104,8 +105,7 @@ PlayerRelationsManager::clear() #define PLAYER_IGNORE_STRATEGY "player-ignore-strategy" #define DEFAULT_PERMISSIONS "default-player-permissions" -int -PlayerRelationsManager::getPlayerIgnoreStrategyIndex(const std::string &name) +int PlayerRelationsManager::getPlayerIgnoreStrategyIndex(const std::string &name) { std::vector<PlayerIgnoreStrategy *> *strategies = getPlayerIgnoreStrategies(); for (unsigned int i = 0; i < strategies->size(); i++) @@ -115,8 +115,7 @@ PlayerRelationsManager::getPlayerIgnoreStrategyIndex(const std::string &name) return -1; } -void -PlayerRelationsManager::load() +void PlayerRelationsManager::load() { clear(); @@ -133,8 +132,7 @@ PlayerRelationsManager::load() } -void -PlayerRelationsManager::init() +void PlayerRelationsManager::init() { load(); @@ -142,8 +140,7 @@ PlayerRelationsManager::init() clear(); // Yes, we still keep them around in the config file until the next update. } -void -PlayerRelationsManager::store() +void PlayerRelationsManager::store() { config.setList<std::map<std::string, PlayerRelation *>::const_iterator, std::pair<std::string, PlayerRelation *>, @@ -160,8 +157,7 @@ PlayerRelationsManager::store() config.write(); } -void -PlayerRelationsManager::signalUpdate(const std::string &name) +void PlayerRelationsManager::signalUpdate(const std::string &name) { store(); @@ -169,8 +165,7 @@ PlayerRelationsManager::signalUpdate(const std::string &name) (*it)->updatedPlayer(name); } -unsigned int -PlayerRelationsManager::checkPermissionSilently(const std::string &player_name, unsigned int flags) +unsigned int PlayerRelationsManager::checkPermissionSilently(const std::string &player_name, unsigned int flags) { PlayerRelation *r = mRelations[player_name]; if (!r) @@ -195,16 +190,14 @@ PlayerRelationsManager::checkPermissionSilently(const std::string &player_name, } } -bool -PlayerRelationsManager::hasPermission(Being *being, unsigned int flags) +bool PlayerRelationsManager::hasPermission(Being *being, unsigned int flags) { if (being->getType() == Being::PLAYER) return hasPermission(being->getName(), flags) == flags; return true; } -bool -PlayerRelationsManager::hasPermission(const std::string &name, unsigned int flags) +bool PlayerRelationsManager::hasPermission(const std::string &name, unsigned int flags) { unsigned int rejections = flags & ~checkPermissionSilently(name, flags); bool permitted = rejections == 0; @@ -223,8 +216,7 @@ PlayerRelationsManager::hasPermission(const std::string &name, unsigned int flag return permitted; } -void -PlayerRelationsManager::setRelation(const std::string &player_name, PlayerRelation::relation relation) +void PlayerRelationsManager::setRelation(const std::string &player_name, PlayerRelation::relation relation) { PlayerRelation *r = mRelations[player_name]; if (r == NULL) @@ -235,8 +227,7 @@ PlayerRelationsManager::setRelation(const std::string &player_name, PlayerRelati signalUpdate(player_name); } -std::vector<std::string> * -PlayerRelationsManager::getPlayers() +std::vector<std::string> * PlayerRelationsManager::getPlayers() { std::vector<std::string> *retval = new std::vector<std::string>(); @@ -249,8 +240,7 @@ PlayerRelationsManager::getPlayers() return retval; } -void -PlayerRelationsManager::removePlayer(const std::string &name) +void PlayerRelationsManager::removePlayer(const std::string &name) { if (mRelations[name]) delete mRelations[name]; @@ -261,8 +251,7 @@ PlayerRelationsManager::removePlayer(const std::string &name) } -PlayerRelation::relation -PlayerRelationsManager::getRelation(const std::string &name) +PlayerRelation::relation PlayerRelationsManager::getRelation(const std::string &name) { if (mRelations[name]) return mRelations[name]->mRelation; @@ -273,14 +262,12 @@ PlayerRelationsManager::getRelation(const std::string &name) //////////////////////////////////////// // defaults -unsigned int -PlayerRelationsManager::getDefault() const +unsigned int PlayerRelationsManager::getDefault() const { return mDefaultPermissions; } -void -PlayerRelationsManager::setDefault(unsigned int permissions) +void PlayerRelationsManager::setDefault(unsigned int permissions) { mDefaultPermissions = permissions; @@ -302,8 +289,7 @@ public: mShortName = PLAYER_IGNORE_STRATEGY_NOP; } - virtual void - ignore(Player *player, unsigned int flags) + virtual void ignore(Player *player, unsigned int flags) { } }; @@ -317,10 +303,9 @@ public: mShortName = "dotdotdot"; } - virtual void - ignore(Player *player, unsigned int flags) + virtual void ignore(Player *player, unsigned int flags) { - player->setSpeech("...", 5); + player->setSpeech("...", 500); } }; @@ -334,8 +319,7 @@ public: mShortName = "blinkname"; } - virtual void - ignore(Player *player, unsigned int flags) + virtual void ignore(Player *player, unsigned int flags) { player->flash(200); } @@ -351,8 +335,7 @@ public: mShortName = shortname; } - virtual void - ignore(Player *player, unsigned int flags) + virtual void ignore(Player *player, unsigned int flags) { player->setEmote(mEmotion, IGNORE_EMOTE_TIME); } diff --git a/src/player_relations.h b/src/player_relations.h index 6cbaf6b3..1eb4ede6 100644 --- a/src/player_relations.h +++ b/src/player_relations.h @@ -22,13 +22,13 @@ #ifndef PLAYER_RELATIONS_H #define PLAYER_RELATIONS_H -#include "being.h" -#include "player.h" -#include "configuration.h" -#include <string> +#include <list> #include <map> +#include <string> #include <vector> -#include <list> + +class Being; +class Player; struct PlayerRelation { diff --git a/src/properties.h b/src/properties.h index 016d607a..a2ce5b88 100644 --- a/src/properties.h +++ b/src/properties.h @@ -23,8 +23,8 @@ #define PROPERTIES_H #include <map> -#include <string> #include <sstream> +#include <string> /** * A class holding a set of properties. diff --git a/src/resources/action.cpp b/src/resources/action.cpp index 3fd3237e..e2cb11f2 100644 --- a/src/resources/action.cpp +++ b/src/resources/action.cpp @@ -20,12 +20,10 @@ */ #include "action.h" - #include "animation.h" #include "../utils/dtor.h" - Action::Action() { } diff --git a/src/resources/action.h b/src/resources/action.h index c557e6da..649d3828 100644 --- a/src/resources/action.h +++ b/src/resources/action.h @@ -44,11 +44,9 @@ class Action */ ~Action(); - void - setAnimation(int direction, Animation *animation); + void setAnimation(int direction, Animation *animation); - Animation* - getAnimation(int direction) const; + Animation* getAnimation(int direction) const; protected: typedef std::map<int, Animation*> Animations; diff --git a/src/resources/ambientoverlay.cpp b/src/resources/ambientoverlay.cpp index ef034acf..32ed47d1 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 62e5ac16..54c319de 100644 --- a/src/resources/animation.cpp +++ b/src/resources/animation.cpp @@ -21,8 +21,6 @@ #include "animation.h" -#include <algorithm> - #include "../utils/dtor.h" Animation::Animation(): @@ -30,22 +28,19 @@ Animation::Animation(): { } -void -Animation::addFrame(Image *image, unsigned int delay, int offsetX, int offsetY) +void Animation::addFrame(Image *image, unsigned int delay, int offsetX, int offsetY) { Frame frame = { image, delay, offsetX, offsetY }; mFrames.push_back(frame); mDuration += delay; } -void -Animation::addTerminator() +void Animation::addTerminator() { addFrame(NULL, 0, 0, 0); } -bool -Animation::isTerminator(const Frame &candidate) +bool Animation::isTerminator(const Frame &candidate) { return (candidate.image == NULL); } diff --git a/src/resources/animation.h b/src/resources/animation.h index 29c99209..0c461ebe 100644 --- a/src/resources/animation.h +++ b/src/resources/animation.h @@ -54,39 +54,33 @@ class Animation /** * Appends a new animation at the end of the sequence. */ - void - addFrame(Image *image, unsigned int delay, int offsetX, int offsetY); + void addFrame(Image *image, unsigned int delay, int offsetX, int offsetY); /** * Appends an animation terminator that states that the animation * should not loop. */ - void - addTerminator(); + void addTerminator(); /** * Returns the frame at the specified index. */ - Frame* - getFrame(int index) { return &(mFrames[index]); } + Frame* getFrame(int index) { return &(mFrames[index]); } /** * Returns the length of this animation in frames. */ - unsigned int - getLength() const { return mFrames.size(); } + unsigned int getLength() const { return mFrames.size(); } /** * Returns the duration of this animation. */ - int - getDuration() const { return mDuration; } + int getDuration() const { return mDuration; } /** * Determines whether the given animation frame is a terminator. */ - static bool - isTerminator(const Frame &phase); + static bool isTerminator(const Frame &phase); protected: std::vector<Frame> mFrames; diff --git a/src/resources/buddylist.cpp b/src/resources/buddylist.cpp index 24198f59..719ecab1 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() { @@ -55,9 +55,9 @@ void BuddyList::loadFile() char *buddy = new char[LEN_MAX_USERNAME]; inputStream.getline(buddy, LEN_MAX_USERNAME); // Ugly ? - if(strcmp(buddy,"")) mBuddylist.push_back(buddy); + if (strcmp(buddy, "")) mBuddylist.push_back(buddy); delete [] buddy; - } while(!inputStream.eof()); + } while (!inputStream.eof()); // Read buddy and close file inputStream.close(); @@ -111,7 +111,7 @@ bool BuddyList::removeBuddy(const std::string buddy) return false; } -int BuddyList::getNumberOfElements() +int BuddyList::getNumberOfElements() { return mBuddylist.size(); } diff --git a/src/resources/buddylist.h b/src/resources/buddylist.h index 33fcde4d..f0758c25 100644 --- a/src/resources/buddylist.h +++ b/src/resources/buddylist.h @@ -27,7 +27,8 @@ #include <guichan/listmodel.hpp> -class BuddyList : public gcn::ListModel { +class BuddyList : public gcn::ListModel +{ public: /** * Constructor @@ -52,7 +53,7 @@ class BuddyList : public gcn::ListModel { /** * Returns the number of buddy on the list */ - int getNumberOfElements(); + int getNumberOfElements(); /** * Returns the buddy of the number or null diff --git a/src/resources/colordb.cpp b/src/resources/colordb.cpp new file mode 100644 index 00000000..3a8754ea --- /dev/null +++ b/src/resources/colordb.cpp @@ -0,0 +1,122 @@ +/* + * Color database + * Copyright (C) 2008 Aethyra Development Team + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <libxml/tree.h> + +#include "colordb.h" + +#include "../log.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"); + } + } + + 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..c581f653 --- /dev/null +++ b/src/resources/colordb.h @@ -0,0 +1,52 @@ +/* + * Color database + * Copyright (C) 2008 Aethyra Development Team + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef COLOR_MANAGER_H +#define 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.cpp b/src/resources/dye.cpp index 1b34a403..22bd2411 100644 --- a/src/resources/dye.cpp +++ b/src/resources/dye.cpp @@ -19,7 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <algorithm> #include <sstream> #include "dye.h" diff --git a/src/resources/dye.h b/src/resources/dye.h index d32c3f22..3cef334a 100644 --- a/src/resources/dye.h +++ b/src/resources/dye.h @@ -22,6 +22,7 @@ #ifndef DYE_H #define 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(const std::string &); + Palette(const std::string &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(const std::string &); + Dye(const std::string &dye); /** * Destroys the associated palettes. diff --git a/src/resources/emotedb.cpp b/src/resources/emotedb.cpp new file mode 100644 index 00000000..77c3c2fb --- /dev/null +++ b/src/resources/emotedb.cpp @@ -0,0 +1,140 @@ +/* + * Emote database + * Copyright (C) 2008 Aethyra Development Team + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emotedb.h" + +#include "../log.h" + +#include "../utils/xml.h" + +namespace +{ + EmoteInfos mEmoteInfos; + EmoteInfo mUnknown; + bool mLoaded = false; + int mLastEmote = 0; +} + +void EmoteDB::load() +{ + if (mLoaded) + return; + + mLastEmote = 0; + + EmoteSprite *unknownSprite = new EmoteSprite; + unknownSprite->sprite = "error.xml"; + unknownSprite->name = "unknown"; + unknownSprite->variant = 0; + mUnknown.sprites.push_back(unknownSprite); + + logger->log("Initializing emote database..."); + + XML::Document doc("emotes.xml"); + xmlNodePtr rootNode = doc.rootNode(); + + if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "emotes")) + { + logger->log("Emote Database: Error while loading emotes.xml!"); + return; + } + + //iterate <emote>s + for_each_xml_child_node(emoteNode, rootNode) + { + if (!xmlStrEqual(emoteNode->name, BAD_CAST "emote")) + continue; + + int id = XML::getProperty(emoteNode, "id", -1); + if (id == -1) + { + logger->log("Emote Database: Emote with missing ID in emotes.xml!"); + continue; + } + + EmoteInfo *currentInfo = new EmoteInfo; + + for_each_xml_child_node(spriteNode, emoteNode) + { + if (xmlStrEqual(spriteNode->name, BAD_CAST "sprite")) + { + EmoteSprite *currentSprite = new EmoteSprite; + currentSprite->sprite = (const char*) spriteNode->xmlChildrenNode->content; + currentSprite->variant = XML::getProperty(spriteNode, "variant", 0); + currentInfo->sprites.push_back(currentSprite); + } + else if (xmlStrEqual(spriteNode->name, BAD_CAST "particlefx")) + { + std::string particlefx = (const char*) spriteNode->xmlChildrenNode->content; + currentInfo->particles.push_back(particlefx); + } + } + mEmoteInfos[id] = currentInfo; + if (id > mLastEmote) mLastEmote = id; + } + + mLoaded = true; +} + +void EmoteDB::unload() +{ + for ( EmoteInfosIterator i = mEmoteInfos.begin(); + i != mEmoteInfos.end(); + i++) + { + while (!i->second->sprites.empty()) + { + delete i->second->sprites.front(); + i->second->sprites.pop_front(); + } + delete i->second; + } + + mEmoteInfos.clear(); + + while (!mUnknown.sprites.empty()) + { + delete mUnknown.sprites.front(); + mUnknown.sprites.pop_front(); + } + + mLoaded = false; +} + +const EmoteInfo& EmoteDB::get(int id) +{ + EmoteInfosIterator i = mEmoteInfos.find(id); + + if (i == mEmoteInfos.end()) + { + logger->log("EmoteDB: Warning, unknown emote ID %d requested", id); + return mUnknown; + } + else + { + return *(i->second); + } +} + +const int& EmoteDB::getLast() +{ + return mLastEmote; +} diff --git a/src/resources/emotedb.h b/src/resources/emotedb.h new file mode 100644 index 00000000..2cf3af62 --- /dev/null +++ b/src/resources/emotedb.h @@ -0,0 +1,60 @@ +/* + * Emote database + * Copyright (C) 2008 Aethyra Development Team + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EMOTE_DB_H +#define EMOTE_DB_H + +#include <list> +#include <map> +#include <string> + +struct EmoteSprite +{ + std::string sprite; + std::string name; + int variant; +}; + +struct EmoteInfo +{ + std::list<EmoteSprite*> sprites; + std::list<std::string> particles; +}; + +typedef std::map<int, EmoteInfo*> EmoteInfos; + +/** + * Emote information database. + */ +namespace EmoteDB +{ + void load(); + + void unload(); + + const EmoteInfo& get(int id); + + const int& getLast(); + + typedef EmoteInfos::iterator EmoteInfosIterator; +} + +#endif diff --git a/src/resources/image.cpp b/src/resources/image.cpp index cc986c81..7a7e6ac8 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" @@ -168,7 +167,8 @@ Image *Image::load(SDL_Surface *tmpImage) tmpImage = SDL_CreateRGBSurface(SDL_SWSURFACE, realWidth, realHeight, 32, rmask, gmask, bmask, amask); - if (!tmpImage) { + if (!tmpImage) + { logger->log("Error, image convert failed: out of memory"); return NULL; } @@ -179,9 +179,8 @@ Image *Image::load(SDL_Surface *tmpImage) glGenTextures(1, &texture); glBindTexture(mTextureType, texture); - if (SDL_MUSTLOCK(tmpImage)) { + if (SDL_MUSTLOCK(tmpImage)) SDL_LockSurface(tmpImage); - } glTexImage2D( mTextureType, 0, 4, @@ -256,14 +255,13 @@ Image *Image::load(SDL_Surface *tmpImage) SDL_Surface *image; // Convert the surface to the current display format - if (hasAlpha) { + if (hasAlpha) image = SDL_DisplayFormatAlpha(tmpImage); - } - else { + else image = SDL_DisplayFormat(tmpImage); - } - if (!image) { + if (!image) + { logger->log("Error: Image convert failed."); return NULL; } @@ -275,14 +273,16 @@ void Image::unload() { mLoaded = false; - if (mImage) { + if (mImage) + { // Free the image surface. SDL_FreeSurface(mImage); mImage = NULL; } #ifdef USE_OPENGL - if (mGLImage) { + if (mGLImage) + { glDeleteTextures(1, &mGLImage); mGLImage = 0; } @@ -293,10 +293,9 @@ Image *Image::getSubImage(int x, int y, int width, int height) { // Create a new clipped sub-image #ifdef USE_OPENGL - if (mUseOpenGL) { + if (mUseOpenGL) return new SubImage(this, mGLImage, x, y, width, height, mTexWidth, mTexHeight); - } #endif return new SubImage(this, mImage, x, y, width, height); @@ -304,13 +303,13 @@ Image *Image::getSubImage(int x, int y, int width, int height) void Image::setAlpha(float a) { - if (mAlpha == a) { + if (mAlpha == a) return; - } mAlpha = a; - if (mImage) { + if (mImage) + { // Set the alpha value this image is drawn at SDL_SetAlpha(mImage, SDL_SRCALPHA, (int) (255 * mAlpha)); } @@ -322,14 +321,12 @@ float Image::getAlpha() } #ifdef USE_OPENGL -void -Image::setLoadAsOpenGL(bool useOpenGL) +void Image::setLoadAsOpenGL(bool useOpenGL) { Image::mUseOpenGL = useOpenGL; } -int -Image::powerOfTwo(int input) +int Image::powerOfTwo(int input) { int value; if (mTextureType == GL_TEXTURE_2D) @@ -354,7 +351,8 @@ Image::powerOfTwo(int input) SubImage::SubImage(Image *parent, SDL_Surface *image, int x, int y, int width, int height): - Image(image), mParent(parent) + Image(image), + mParent(parent) { mParent->incRef(); @@ -369,7 +367,8 @@ SubImage::SubImage(Image *parent, SDL_Surface *image, SubImage::SubImage(Image *parent, GLuint image, int x, int y, int width, int height, int texWidth, int texHeight): - Image(image, width, height, texWidth, texHeight), mParent(parent) + Image(image, width, height, texWidth, texHeight), + mParent(parent) { mParent->incRef(); @@ -395,3 +394,4 @@ Image *SubImage::getSubImage(int x, int y, int w, int h) { return mParent->getSubImage(mBounds.x + x, mBounds.y + y, w, h); } + diff --git a/src/resources/image.h b/src/resources/image.h index 963fbb24..fe3081ac 100644 --- a/src/resources/image.h +++ b/src/resources/image.h @@ -22,9 +22,10 @@ #ifndef IMAGE_H #define 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 @@ -39,6 +40,8 @@ #include "resource.h" class Dye; +class SDL_Rect; +class SDL_Surface; /** * Defines a class for loading and storing images. @@ -96,7 +99,6 @@ class Image : public Resource virtual int getWidth() const { return mBounds.w; } - /** * Returns the height of the image. */ @@ -114,7 +116,7 @@ class Image : public Resource /** * Sets the alpha value of this image. */ - void setAlpha(float alpha); + virtual void setAlpha(float alpha); /** * Returns the alpha value of this image. @@ -129,7 +131,6 @@ class Image : public Resource static void setLoadAsOpenGL(bool useOpenGL); #endif - protected: /** * Constructor. diff --git a/src/resources/imageloader.cpp b/src/resources/imageloader.cpp index 5aad7c52..40d62797 100644 --- a/src/resources/imageloader.cpp +++ b/src/resources/imageloader.cpp @@ -20,13 +20,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 458b0405..92bb3242 100644 --- a/src/resources/imageset.cpp +++ b/src/resources/imageset.cpp @@ -19,12 +19,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "image.h" #include "imageset.h" #include "../log.h" -#include "image.h" - #include "../utils/dtor.h" ImageSet::ImageSet(Image *img, int width, int height) @@ -45,8 +44,7 @@ ImageSet::~ImageSet() 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 00a36754..f59c76bb 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 d6bb4659..c350ac07 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/imagewriter.h b/src/resources/imagewriter.h index 8eb6cead..039d3afb 100644 --- a/src/resources/imagewriter.h +++ b/src/resources/imagewriter.h @@ -19,13 +19,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <iosfwd> - struct SDL_Surface; class ImageWriter { public: static bool writePNG(SDL_Surface *surface, - const std::string &filename); + const std::string &filename); }; diff --git a/src/resources/itemdb.cpp b/src/resources/itemdb.cpp index 1678eda8..6fa010d4 100644 --- a/src/resources/itemdb.cpp +++ b/src/resources/itemdb.cpp @@ -20,22 +20,22 @@ */ #include <cassert> + #include <libxml/tree.h> #include "itemdb.h" -#include "iteminfo.h" -#include "resourcemanager.h" - #include "../log.h" #include "../utils/dtor.h" #include "../utils/gettext.h" +#include "../utils/trim.h" #include "../utils/xml.h" namespace { ItemDB::ItemInfos mItemInfos; + ItemDB::NamedItemInfos mNamedItemInfos; ItemInfo *mUnknown; bool mLoaded = false; } @@ -52,17 +52,17 @@ void ItemDB::load() logger->log("Initializing item database..."); mUnknown = new ItemInfo(); - mUnknown->setName("Unknown item"); + mUnknown->setName(_("Unknown item")); mUnknown->setImageName(""); mUnknown->setSprite("error.xml", GENDER_MALE); mUnknown->setSprite("error.xml", GENDER_FEMALE); - XML::Document doc("items.xml"); + XML::Document doc(_("items.xml")); xmlNodePtr rootNode = doc.rootNode(); if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "items")) { - logger->error("ItemDB: Error while loading items.xml!"); + logger->error(_("ItemDB: Error while loading items.xml!")); } for_each_xml_child_node(node, rootNode) @@ -82,7 +82,7 @@ void ItemDB::load() logger->log("ItemDB: Redefinition of item ID %d", id); } - int type = XML::getProperty(node, "type", 0); + std::string type = XML::getProperty(node, "type", "other"); int weight = XML::getProperty(node, "weight", 0); int view = XML::getProperty(node, "view", 0); @@ -95,6 +95,7 @@ void ItemDB::load() if (id) { ItemInfo *itemInfo = new ItemInfo; + itemInfo->setId(id); itemInfo->setImageName(image); itemInfo->setName(name.empty() ? _("Unnamed") : name); itemInfo->setDescription(description); @@ -117,6 +118,27 @@ void ItemDB::load() } mItemInfos[id] = itemInfo; + if (!name.empty()) + { + NamedItemInfoIterator itr = mNamedItemInfos.find(name); + if (itr == mNamedItemInfos.end()) + { + std::string temp = name; + trim(temp); + + for (unsigned int i = 0; i < temp.size(); i++) + { + temp[i] = (char) tolower(temp[i]); + } + + mNamedItemInfos[temp] = itemInfo; + } + else + { + logger->log("ItemDB: Duplicate name of item found item %d", + id); + } + } } #define CHECK_PARAM(param, error_value) \ @@ -126,7 +148,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); @@ -166,6 +188,23 @@ const ItemInfo& ItemDB::get(int id) } } +const ItemInfo& ItemDB::get(const std::string &name) +{ + assert(mLoaded && !name.empty()); + + NamedItemInfoIterator i = mNamedItemInfos.find(name); + + if (i == mNamedItemInfos.end()) + { + logger->log("ItemDB: Error, unknown item name %s", name.c_str()); + return *mUnknown; + } + else + { + return *(i->second); + } +} + void loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node) { std::string gender = XML::getProperty(node, "gender", "unisex"); @@ -175,7 +214,6 @@ void loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node) { itemInfo->setSprite(filename, GENDER_MALE); } - if (gender == "female" || gender == "unisex") { itemInfo->setSprite(filename, GENDER_FEMALE); diff --git a/src/resources/itemdb.h b/src/resources/itemdb.h index 03fb1eed..08a7acd0 100644 --- a/src/resources/itemdb.h +++ b/src/resources/itemdb.h @@ -22,9 +22,11 @@ #ifndef ITEM_MANAGER_H #define ITEM_MANAGER_H +#include <map> + #include "iteminfo.h" -#include <map> +class ItemInfo; /** * The namespace that holds the item information. @@ -42,10 +44,13 @@ namespace ItemDB void unload(); const ItemInfo& get(int id); + const ItemInfo& get(const std::string &name); // Items database typedef std::map<int, ItemInfo*> ItemInfos; + typedef std::map<std::string, ItemInfo*> NamedItemInfos; typedef ItemInfos::iterator ItemInfoIterator; + typedef NamedItemInfos::iterator NamedItemInfoIterator; } #endif diff --git a/src/resources/iteminfo.cpp b/src/resources/iteminfo.cpp index b924c591..2f118284 100644 --- a/src/resources/iteminfo.cpp +++ b/src/resources/iteminfo.cpp @@ -19,12 +19,10 @@ * 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(Gender gender) const +const std::string& ItemInfo::getSprite(Gender gender) const { if (mView) { @@ -67,15 +65,12 @@ void ItemInfo::setWeaponType(int type) } } -void -ItemInfo::addSound(EquipmentSoundEvent event, const std::string &filename) +void ItemInfo::addSound(EquipmentSoundEvent event, const std::string &filename) { mSounds[event].push_back("sfx/" + filename); } - -const std::string& -ItemInfo::getSound(EquipmentSoundEvent event) const +const std::string& ItemInfo::getSound(EquipmentSoundEvent event) const { static const std::string empty; std::map< EquipmentSoundEvent, std::vector<std::string> >::const_iterator i; diff --git a/src/resources/iteminfo.h b/src/resources/iteminfo.h index a04213ff..c03dec28 100644 --- a/src/resources/iteminfo.h +++ b/src/resources/iteminfo.h @@ -47,13 +47,19 @@ class ItemInfo * Constructor. */ ItemInfo(): - mType(0), + mType(""), mWeight(0), mView(0), mAttackType(ACTION_DEFAULT) { } + void setId(int id) + { mId = id; } + + int getId() const + { return mId; } + void setName(const std::string &name) { mName = name; } @@ -75,14 +81,12 @@ class ItemInfo void setEffect(const std::string &effect) { mEffect = effect; } - const std::string& - getEffect() const { return mEffect; } + const std::string& getEffect() const { return mEffect; } - void setType(short type) + void setType(const std::string& type) { mType = type; } - short getType() const - { return mType; } + const std::string& getType() const { return mType; } void setWeight(short weight) { mWeight = weight; } @@ -112,9 +116,10 @@ class ItemInfo std::string mName; std::string mDescription; /**< Short description. */ std::string mEffect; /**< Description of effects. */ - char mType; /**< Item type. */ + std::string mType; /**< Item type. */ short mWeight; /**< Weight in grams. */ int mView; /**< Item ID of how this item looks. */ + int mId; /**< Item ID */ // Equipment related members SpriteAction mAttackType; /**< Attack type, in case of weapon. */ diff --git a/src/resources/mapreader.cpp b/src/resources/mapreader.cpp index 21125c2a..3270c665 100644 --- a/src/resources/mapreader.cpp +++ b/src/resources/mapreader.cpp @@ -19,14 +19,14 @@ * 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 "animation.h" #include "image.h" +#include "mapreader.h" +#include "resourcemanager.h" #include "../log.h" #include "../map.h" @@ -205,14 +205,11 @@ Map *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) { @@ -236,8 +233,8 @@ Map *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) { @@ -326,8 +323,8 @@ void 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; @@ -370,7 +367,7 @@ void MapReader::readLayer(xmlNodePtr node, Map *map) while (*charStart) { if (*charStart != ' ' && *charStart != '\t' && - *charStart != '\n') + *charStart != '\n') { *charIndex = *charStart; charIndex++; @@ -461,15 +458,20 @@ Tileset *MapReader::readTileset(xmlNodePtr node, const std::string &path, Map *map) { + int firstGid = XML::getProperty(node, "firstgid", 0); + XML::Document* doc = NULL; Tileset *set = NULL; if (xmlHasProp(node, BAD_CAST "source")) { - logger->log("Warning: External tilesets not supported yet."); - return set; + std::string filename = XML::getProperty(node, "source", ""); + while (filename.substr(0, 3) == "../") + filename.erase(0, 3); // Remove "../" + 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()); @@ -545,5 +547,7 @@ Tileset *MapReader::readTileset(xmlNodePtr node, } } + delete doc; + return set; } diff --git a/src/resources/mapreader.h b/src/resources/mapreader.h index 8cb2749d..0ed553c3 100644 --- a/src/resources/mapreader.h +++ b/src/resources/mapreader.h @@ -22,12 +22,10 @@ #ifndef MAPREADER_H #define MAPREADER_H -#include <iosfwd> - #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 5d452db2..15451c95 100644 --- a/src/resources/monsterdb.cpp +++ b/src/resources/monsterdb.cpp @@ -20,12 +20,12 @@ */ #include "monsterdb.h" - -#include "resourcemanager.h" +#include "monsterinfo.h" #include "../log.h" #include "../utils/dtor.h" +#include "../utils/gettext.h" #include "../utils/xml.h" namespace @@ -35,23 +35,22 @@ namespace bool mLoaded = false; } -void -MonsterDB::load() +void MonsterDB::load() { if (mLoaded) return; mUnknown.addSprite("error.xml"); - mUnknown.setName("unnamed"); + mUnknown.setName(_("unnamed")); logger->log("Initializing monster database..."); - XML::Document doc("monsters.xml"); + XML::Document doc(_("monsters.xml")); xmlNodePtr rootNode = doc.rootNode(); if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "monsters")) { - logger->error("Monster Database: Error while loading monster.xml!"); + logger->error(_("Monster Database: Error while loading monster.xml!")); } //iterate <monster>s @@ -82,7 +81,8 @@ MonsterDB::load() } else { - logger->log("MonsterDB: Unknown target cursor type \"%s\" for %s - using medium sized one", + logger->log("MonsterDB: Unknown target cursor type \"%s\" for %s -" + "using medium sized one", targetCursor.c_str(), currentInfo->getName().c_str()); currentInfo->setTargetCursorSize(Being::TC_MEDIUM); } @@ -92,7 +92,8 @@ MonsterDB::load() { if (xmlStrEqual(spriteNode->name, BAD_CAST "sprite")) { - currentInfo->addSprite((const char*) spriteNode->xmlChildrenNode->content); + currentInfo->addSprite( + (const char*) spriteNode->xmlChildrenNode->content); } if (xmlStrEqual(spriteNode->name, BAD_CAST "sound")) @@ -119,11 +120,20 @@ MonsterDB::load() } else { - logger->log("MonsterDB: Warning, sound effect %s for unknown event %s of monster %s", - filename, event.c_str(), currentInfo->getName().c_str()); + logger->log("MonsterDB: Warning, sound effect %s for " + "unknown event %s of monster %s", + filename, event.c_str(), + currentInfo->getName().c_str()); } } + 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( diff --git a/src/resources/monsterdb.h b/src/resources/monsterdb.h index 8555670b..0a218661 100644 --- a/src/resources/monsterdb.h +++ b/src/resources/monsterdb.h @@ -24,18 +24,16 @@ #include <map> -#include "monsterinfo.h" +class MonsterInfo; /** * Monster information database. */ namespace MonsterDB { - void - load(); + void load(); - void - unload(); + void unload(); const MonsterInfo& get(int id); diff --git a/src/resources/monsterinfo.cpp b/src/resources/monsterinfo.cpp index d8ff8be3..503990e7 100644 --- a/src/resources/monsterinfo.cpp +++ b/src/resources/monsterinfo.cpp @@ -36,8 +36,7 @@ MonsterInfo::~MonsterInfo() } -void -MonsterInfo::addSound(MonsterSoundEvent event, std::string filename) +void MonsterInfo::addSound(MonsterSoundEvent event, std::string filename) { if (mSounds.find(event) == mSounds.end()) { @@ -48,8 +47,7 @@ MonsterInfo::addSound(MonsterSoundEvent event, std::string filename) } -std::string -MonsterInfo::getSound(MonsterSoundEvent event) const +std::string MonsterInfo::getSound(MonsterSoundEvent event) const { std::map<MonsterSoundEvent, std::vector<std::string>* >::const_iterator i; diff --git a/src/resources/monsterinfo.h b/src/resources/monsterinfo.h index b37ac1a9..359791fd 100644 --- a/src/resources/monsterinfo.h +++ b/src/resources/monsterinfo.h @@ -22,14 +22,13 @@ #ifndef MONSTERINFO_H #define MONSTERINFO_H +#include <list> #include <map> #include <string> #include <vector> -#include <list> #include "../being.h" - enum MonsterSoundEvent { MONSTER_EVENT_HIT, @@ -57,39 +56,39 @@ class MonsterInfo */ ~MonsterInfo(); - void - setName(std::string name) { mName = name; } + void setName(std::string name) { mName = name; } - void - addSprite(std::string filename) { mSprites.push_back(filename); } + void addSprite(std::string filename) { mSprites.push_back(filename); } - void - setTargetCursorSize(Being::TargetCursorSize targetCursorSize) + void setTargetCursorSize(Being::TargetCursorSize targetCursorSize) { mTargetCursorSize = targetCursorSize; } - void - addSound(MonsterSoundEvent event, std::string filename); + void addSound(MonsterSoundEvent event, std::string filename); + + void addParticleEffect(std::string filename); + + const std::string& getName() const + { return mName; } - void - addParticleEffect(std::string filename); + const std::list<std::string>& getSprites() const + { return mSprites; } - const std::string& - getName() const { return mName; } + Being::TargetCursorSize getTargetCursorSize() const + { return mTargetCursorSize; } - const std::list<std::string>& - getSprites() const { return mSprites; } + std::string getSound(MonsterSoundEvent event) const; - Being::TargetCursorSize - getTargetCursorSize() const { return mTargetCursorSize; } + std::string getAttackParticleEffect() const { return mAttackParticle; } - std::string - getSound(MonsterSoundEvent event) const; + void addAttackParticleEffect(const std::string &particleEffect) + { mAttackParticle = particleEffect; } - const std::list<std::string>& - getParticleEffects() const { return mParticleEffects; } + 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/music.cpp b/src/resources/music.cpp index 3b81d05b..ed78a541 100644 --- a/src/resources/music.cpp +++ b/src/resources/music.cpp @@ -55,8 +55,7 @@ Resource *Music::load(void *buffer, unsigned bufferSize) } } -bool -Music::play(int loops) +bool Music::play(int loops) { /* * Warning: loops should be always set to -1 (infinite) with current @@ -71,8 +70,7 @@ Music::play(int loops) return mChannel != -1; } -void -Music::stop() +void Music::stop() { /* * Warning: very dungerous trick, it could try to stop channels occupied diff --git a/src/resources/music.h b/src/resources/music.h index cff18338..65f1ee88 100644 --- a/src/resources/music.h +++ b/src/resources/music.h @@ -56,14 +56,12 @@ class Music : public Resource * @return <code>true</code> if the playback started properly * <code>false</code> otherwise. */ - virtual bool - play(int loops); + virtual bool play(int loops); /** * Stops the music. */ - virtual void - stop(); + virtual void stop(); protected: /** diff --git a/src/resources/npcdb.cpp b/src/resources/npcdb.cpp index 9faef60d..ee65136a 100644 --- a/src/resources/npcdb.cpp +++ b/src/resources/npcdb.cpp @@ -21,11 +21,9 @@ #include "npcdb.h" -#include "resourcemanager.h" - #include "../log.h" -#include "../utils/dtor.h" +#include "../utils/gettext.h" #include "../utils/xml.h" namespace @@ -52,10 +50,10 @@ void NPCDB::load() if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "npcs")) { - logger->error("NPC Database: Error while loading items.xml!"); + logger->error(_("NPC Database: Error while loading npcs.xml!")); } - //iterate <monster>s + //iterate <npc>s for_each_xml_child_node(npcNode, rootNode) { if (!xmlStrEqual(npcNode->name, BAD_CAST "npc")) @@ -91,8 +89,7 @@ void NPCDB::load() mLoaded = true; } -void -NPCDB::unload() +void NPCDB::unload() { for ( NPCInfosIterator i = mNPCInfos.begin(); i != mNPCInfos.end(); @@ -117,8 +114,7 @@ NPCDB::unload() mLoaded = false; } -const NPCInfo& -NPCDB::get(int id) +const NPCInfo& NPCDB::get(int id) { NPCInfosIterator i = mNPCInfos.find(id); diff --git a/src/resources/npcdb.h b/src/resources/npcdb.h index a7f28c50..af6764bf 100644 --- a/src/resources/npcdb.h +++ b/src/resources/npcdb.h @@ -22,8 +22,8 @@ #ifndef NPC_DB_H #define NPC_DB_H -#include <map> #include <list> +#include <map> #include <string> struct NPCsprite @@ -45,11 +45,9 @@ typedef std::map<int, NPCInfo*> NPCInfos; */ namespace NPCDB { - void - load(); + void load(); - void - unload(); + void unload(); const NPCInfo& get(int id); diff --git a/src/resources/resource.cpp b/src/resources/resource.cpp index 4c173962..d1c3ada4 100644 --- a/src/resources/resource.cpp +++ b/src/resources/resource.cpp @@ -22,21 +22,18 @@ #include <cassert> #include "resource.h" - #include "resourcemanager.h" Resource::~Resource() { } -void -Resource::incRef() +void Resource::incRef() { mRefCount++; } -void -Resource::decRef() +void Resource::decRef() { // Reference may not already have reached zero assert(mRefCount != 0); diff --git a/src/resources/resource.h b/src/resources/resource.h index 0dc50c03..7c5f989e 100644 --- a/src/resources/resource.h +++ b/src/resources/resource.h @@ -41,8 +41,7 @@ class Resource /** * Increments the internal reference count. */ - void - incRef(); + void incRef(); /** * Decrements the reference count and deletes the object @@ -51,8 +50,7 @@ class Resource * @return <code>true</code> if the object was deleted * <code>false</code> otherwise. */ - void - decRef(); + void decRef(); /** * Return the path identifying this resource. @@ -64,8 +62,7 @@ class Resource /** * Destructor. */ - virtual - ~Resource(); + virtual ~Resource(); private: std::string mIdPath; /**< Path identifying this resource. */ diff --git a/src/resources/resourcemanager.cpp b/src/resources/resourcemanager.cpp index febcf0d0..fa8f4654 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() @@ -155,13 +153,13 @@ bool ResourceManager::addToSearchPath(const std::string &path, bool append) } void ResourceManager::searchAndAddArchives(const std::string &path, - const std::string &ext, - bool append) + const std::string &ext, + bool append) { const char *dirSep = PHYSFS_getDirSeparator(); char **list = PHYSFS_enumerateFiles(path.c_str()); - for (char **i = list; *i != NULL; i++) + for (char **i = list; *i; i++) { size_t len = strlen(*i); @@ -209,7 +207,7 @@ std::string 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(PKG_DATADIR) + std::string("data") + "/" + file; } return path; @@ -420,8 +418,7 @@ void *ResourceManager::loadFile(const std::string &fileName, int &fileSize) return buffer; } -std::vector<std::string> -ResourceManager::loadTextFile(const std::string &fileName) +std::vector<std::string> ResourceManager::loadTextFile(const std::string &fileName) { int contentsLength; char *fileContents = (char*)loadFile(fileName, contentsLength); diff --git a/src/resources/resourcemanager.h b/src/resources/resourcemanager.h index 6a7a2ed6..c3c68d88 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; @@ -100,7 +100,7 @@ class ResourceManager /** * Returns the real path to a file. Note that this method will always * return a path, it does not check whether the file exists. - * + * * @param file The file to get the real path to. * @return The real path. */ diff --git a/src/resources/soundeffect.cpp b/src/resources/soundeffect.cpp index 21488511..3a285730 100644 --- a/src/resources/soundeffect.cpp +++ b/src/resources/soundeffect.cpp @@ -47,8 +47,7 @@ Resource *SoundEffect::load(void *buffer, unsigned bufferSize) } } -bool -SoundEffect::play(int loops, int volume) +bool SoundEffect::play(int loops, int volume) { Mix_VolumeChunk(mChunk, volume); diff --git a/src/resources/soundeffect.h b/src/resources/soundeffect.h index 5f511b29..116df930 100644 --- a/src/resources/soundeffect.h +++ b/src/resources/soundeffect.h @@ -35,8 +35,7 @@ class SoundEffect : public Resource /** * Destructor. */ - virtual - ~SoundEffect(); + virtual ~SoundEffect(); /** * Loads a sample from a buffer in memory. @@ -58,8 +57,7 @@ class SoundEffect : public Resource * @return <code>true</code> if the playback started properly * <code>false</code> otherwise. */ - virtual bool - play(int loops, int volume); + virtual bool play(int loops, int volume); protected: /** diff --git a/src/resources/spritedef.cpp b/src/resources/spritedef.cpp index 64ce8b0a..20c79e73 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" @@ -322,7 +321,7 @@ SpriteDef::~SpriteDef() SpriteAction SpriteDef::makeSpriteAction(const std::string &action) { - if (action == "" || action == "default") { + if (action.empty() || action == "default") { return ACTION_DEFAULT; } if (action == "stand") { @@ -374,7 +373,7 @@ SpriteAction SpriteDef::makeSpriteAction(const std::string &action) SpriteDirection SpriteDef::makeSpriteDirection(const std::string& direction) { - if (direction == "" || direction == "default") { + if (direction.empty() || direction == "default") { return DIRECTION_DEFAULT; } else if (direction == "up") { diff --git a/src/resources/spritedef.h b/src/resources/spritedef.h index b86a4c4d..b9d7b85d 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; @@ -89,7 +89,6 @@ class SpriteDef : public Resource static SpriteDirection makeSpriteDirection(const std::string &direction); - private: /** * Constructor. @@ -140,7 +139,6 @@ class SpriteDef : public Resource */ void substituteAction(SpriteAction complete, SpriteAction with); - typedef std::map<std::string, ImageSet*> ImageSets; typedef ImageSets::iterator ImageSetIterator; diff --git a/src/simpleanimation.cpp b/src/simpleanimation.cpp index 8abb4a67..48f2f6a5 100644 --- a/src/simpleanimation.cpp +++ b/src/simpleanimation.cpp @@ -19,15 +19,21 @@ * 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/animation.h" #include "resources/image.h" -#include "resources/resourcemanager.h" #include "resources/imageset.h" +#include "resources/resourcemanager.h" +SimpleAnimation::SimpleAnimation(Animation *animation): + mAnimation(animation), + mAnimationTime(0), + mAnimationPhase(0), + mCurrentFrame(mAnimation->getFrame(0)) +{ +} SimpleAnimation::SimpleAnimation(xmlNodePtr animationNode): mAnimationTime(0), @@ -43,7 +49,7 @@ SimpleAnimation::SimpleAnimation(xmlNodePtr animationNode): // Get animation frames for ( xmlNodePtr frameNode = animationNode->xmlChildrenNode; - frameNode != NULL; + frameNode; frameNode = frameNode->next) { int delay = XML::getProperty(frameNode, "delay", 0); diff --git a/src/simpleanimation.h b/src/simpleanimation.h index a7178145..16ac2906 100644 --- a/src/simpleanimation.h +++ b/src/simpleanimation.h @@ -22,12 +22,12 @@ #ifndef SIMPLEANIMAION_H #define SIMPLEANIMAION_H -#include "resources/animation.h" - #include "utils/xml.h" +class Animation; class Frame; class Graphics; +class Image; /** * This class is a leightweight alternative to the AnimatedSprite class. @@ -39,12 +39,7 @@ class SimpleAnimation /** * Creates a simple animation with an already created animation. */ - SimpleAnimation(Animation *animation): - mAnimation(animation), - mAnimationTime(0), - mAnimationPhase(0), - mCurrentFrame(mAnimation->getFrame(0)) - {}; + SimpleAnimation(Animation *animation); /** * Creates a simple animation that creates its animation from XML Data. diff --git a/src/sound.cpp b/src/sound.cpp index c69dc023..6e0b0da0 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" @@ -128,7 +128,7 @@ void Sound::playMusic(const std::string &filename, int loop) { if (!mInstalled) return; - if (mMusic != NULL) { + if (mMusic) { stopMusic(); } @@ -154,7 +154,7 @@ void Sound::stopMusic() logger->log("Sound::stopMusic()"); - if (mMusic != NULL) { + if (mMusic) { Mix_HaltMusic(); Mix_FreeMusic(mMusic); mMusic = NULL; @@ -165,7 +165,7 @@ void Sound::fadeInMusic(const std::string &path, int loop, int ms) { if (!mInstalled) return; - if (mMusic != NULL) { + if (mMusic) { stopMusic(); } @@ -188,7 +188,7 @@ void Sound::fadeOutMusic(int ms) logger->log("Sound::fadeOutMusic() Fading-out (%i ms)", ms); - if (mMusic != NULL) { + if (mMusic) { Mix_FadeOutMusic(ms); Mix_FreeMusic(mMusic); mMusic = NULL; diff --git a/src/sound.h b/src/sound.h index 88336572..05b2def3 100644 --- a/src/sound.h +++ b/src/sound.h @@ -23,7 +23,6 @@ #define SOUND_H #include <SDL_mixer.h> - #include <string> /** Sound engine diff --git a/src/sprite.h b/src/sprite.h index 624e4e8b..a6384e94 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -44,30 +44,26 @@ class Sprite * would support setting a translation offset. It already does this * partly with the clipping rectangle support. */ - virtual void - draw(Graphics *graphics, int offsetX, int offsetY) const = 0; + virtual void draw(Graphics *graphics, int offsetX, int offsetY) const = 0; /** * Returns the horizontal size of the sprites graphical representation * in pixels or 0 when it is undefined. */ - virtual int - getWidth() const + virtual int getWidth() const { return 0; } /** * Returns the vertical size of the sprites graphical representation * in pixels or 0 when it is undefined. */ - virtual int - getHeight() const + virtual int getHeight() const { return 0; } /** * Returns the pixel Y coordinate of the sprite. */ - virtual int - getPixelY() const = 0; + virtual int getPixelY() const = 0; }; #endif diff --git a/src/statuseffect.cpp b/src/statuseffect.cpp index 8872fd7b..7a50e794 100644 --- a/src/statuseffect.cpp +++ b/src/statuseffect.cpp @@ -39,19 +39,19 @@ StatusEffect::~StatusEffect() void StatusEffect::playSFX() { - if (mSFXEffect != "") + if (!mSFXEffect.empty()) sound.playSfx(mSFXEffect); } void StatusEffect::deliverMessage() { - if (mMessage != "") + if (!mMessage.empty()) chatWindow->chatLog(mMessage, BY_SERVER); } Particle *StatusEffect::getParticle() { - if (mParticleEffect == "") + if (mParticleEffect.empty()) return NULL; else return particleEngine->addEffect(mParticleEffect, 0, 0); @@ -59,7 +59,7 @@ Particle *StatusEffect::getParticle() AnimatedSprite *StatusEffect::getIcon() { - if (mIcon == "") + if (mIcon.empty()) return NULL; else { AnimatedSprite *sprite = AnimatedSprite::load( @@ -74,7 +74,7 @@ AnimatedSprite *StatusEffect::getIcon() SpriteAction StatusEffect::getAction() { - if (mAction == "") + if (mAction.empty()) return ACTION_INVALID; else return SpriteDef::makeSpriteAction(mAction); diff --git a/src/text.cpp b/src/text.cpp index 0e458c9f..c4559879 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -1,5 +1,6 @@ /* - * The Mana World + * Support for non-overlapping floating text + * Copyright (C) 2008 Douglas Boffey <DougABoffey@netscape.net> * Copyright (C) 2008 The Mana World Development Team * * This file is part of The Mana World. @@ -21,8 +22,6 @@ #include "text.h" -#include <cstring> - #include <guichan/font.hpp> #include "configuration.h" @@ -38,9 +37,9 @@ Image *Text::mBubbleArrow; Text::Text(const std::string &text, int x, int y, gcn::Graphics::Alignment alignment, - gcn::Color colour, bool isSpeech) : + gcn::Color color, bool isSpeech) : mText(text), - mColour(colour), + mColor(color), mIsSpeech(isSpeech) { if (textManager == 0) @@ -48,7 +47,7 @@ Text::Text(const std::string &text, int x, int y, textManager = new TextManager; ResourceManager *resman = ResourceManager::getInstance(); Image *sbImage = resman->getImage("graphics/gui/bubble.png|W:#" - + config.getValue("speechBubbleColour", "000000")); + + config.getValue("speechBubblecolor", "000000")); mBubble.grid[0] = sbImage->getSubImage(0, 0, 5, 5); mBubble.grid[1] = sbImage->getSubImage(5, 0, 5, 5); mBubble.grid[2] = sbImage->getSubImage(10, 0, 5, 5); @@ -68,8 +67,8 @@ Text::Text(const std::string &text, int x, int y, sbImage->decRef(); } ++mInstances; - mHeight = gui->getFont()->getHeight(); - mWidth = gui->getFont()->getWidth(text); + mHeight = boldFont->getHeight(); + mWidth = boldFont->getWidth(text); switch (alignment) { @@ -113,9 +112,9 @@ void Text::adviseXY(int x, int y) textManager->moveText(this, x - mXOffset, y); } -void Text::draw(Graphics *graphics, int xOff, int yOff) +void Text::draw(gcn::Graphics *graphics, int xOff, int yOff) { - graphics->setFont(gui->getFont()); + graphics->setFont(boldFont); if (mIsSpeech) { static_cast<Graphics*>(graphics)->drawImageRect( @@ -162,20 +161,20 @@ void Text::draw(Graphics *graphics, int xOff, int yOff) gcn::Graphics::LEFT); } - graphics->setColor(mColour); + graphics->setColor(mColor); graphics->drawText(mText, mX - xOff, mY - yOff, gcn::Graphics::LEFT); } FlashText::FlashText(const std::string &text, int x, int y, gcn::Graphics::Alignment alignment, - gcn::Color colour) : - Text(text, x, y, alignment, colour), + gcn::Color color) : + Text(text, x, y, alignment, color), mTime(0) { } -void FlashText::draw(Graphics *graphics, int xOff, int yOff) +void FlashText::draw(gcn::Graphics *graphics, int xOff, int yOff) { if (mTime) { @@ -1,5 +1,6 @@ /* - * The Mana World + * Support for non-overlapping floating text + * Copyright (C) 2008 Douglas Boffey <DougABoffey@netscape.net> * Copyright (C) 2008 The Mana World Development Team * * This file is part of The Mana World. @@ -22,9 +23,10 @@ #ifndef TEXT_H #define TEXT_H -#include "graphics.h" +#include <guichan/color.hpp> -#include <list> +#include "graphics.h" +#include "guichanfwd.h" class TextManager; @@ -38,7 +40,7 @@ class Text */ Text(const std::string &text, int x, int y, gcn::Graphics::Alignment alignment, - gcn::Color colour, bool isSpeech = false); + gcn::Color color, bool isSpeech = false); /** * Destructor. The text is removed from the screen. @@ -53,7 +55,7 @@ class Text /** * Draws the text. */ - virtual void draw(Graphics *graphics, int xOff, int yOff); + virtual void draw(gcn::Graphics *graphics, int xOff, int yOff); private: int mX; /**< Actual x-value of left of text written. */ @@ -63,7 +65,7 @@ class Text int mXOffset; /**< The offset of mX from the desired x. */ static int mInstances; /**< Instances of text. */ std::string mText; /**< The text to display. */ - gcn::Color mColour; /**< The colour of the text. */ + gcn::Color mColor; /**< The color of the text. */ bool mIsSpeech; /**< Is this text a speech bubble? */ protected: @@ -76,7 +78,12 @@ class FlashText : public Text public: FlashText(const std::string &text, int x, int y, gcn::Graphics::Alignment alignment, - gcn::Color colour); + gcn::Color color); + + /** + * Remove the text from the screen + */ + virtual ~FlashText() {} /** * Flash the text for so many refreshes. @@ -86,10 +93,10 @@ class FlashText : public Text /** * Draws the text. */ - virtual void draw(Graphics *graphics, int xOff, int yOff); + virtual void draw(gcn::Graphics *graphics, int xOff, int yOff); private: - int mTime; /**< Time left for flashing. */ + int mTime; /**< Time left for flashing */ }; #endif // TEXT_H diff --git a/src/textmanager.cpp b/src/textmanager.cpp index bbcc271a..6bc8f8b6 100644 --- a/src/textmanager.cpp +++ b/src/textmanager.cpp @@ -1,6 +1,6 @@ /* - * The Mana World - * Copyright (C) 2008 The Mana World Development Team + * Support for non-overlapping floating text + * Copyright (C) 2008 Douglas Boffey <DougABoffey@netscape.net> * * This file is part of The Mana World. * @@ -61,7 +61,7 @@ TextManager::~TextManager() { } -void TextManager::draw(Graphics *graphics, int xOff, int yOff) +void TextManager::draw(gcn::Graphics *graphics, int xOff, int yOff) { for (TextList::iterator bPtr = mTextList.begin(), ePtr = mTextList.end(); bPtr != ePtr; ++bPtr) @@ -91,21 +91,13 @@ void TextManager::place(const Text *textObj, const Text *omit, int from = (*ptr)->mY - occupiedTop; int to = from + (*ptr)->mHeight - 1; if (to < 0 || from >= TEST) // out of range considered - { continue; - } if (from < 0) - { from = 0; - } if (to >= TEST) - { to = TEST - 1; - } for (int i = from; i <= to; ++i) - { occupied[i] = true; - } } } bool ok = true; @@ -113,10 +105,10 @@ void TextManager::place(const Text *textObj, const Text *omit, { ok = ok && !occupied[i]; } + if (ok) - { return; - } + // Have to move it up or down, so find nearest spaces either side int consec = 0; int upSlot = -1; // means not found diff --git a/src/textmanager.h b/src/textmanager.h index fd0006c9..ee8e1209 100644 --- a/src/textmanager.h +++ b/src/textmanager.h @@ -1,6 +1,6 @@ /* - * The Mana World - * Copyright (C) 2008 The Mana World Development Team + * Support for non-overlapping floating text + * Copyright (C) 2008 Douglas Boffey <DougABoffey@netscape.net> * * This file is part of The Mana World. * @@ -24,8 +24,9 @@ #include <list> +#include "guichanfwd.h" + class Text; -class Graphics; class TextManager { @@ -58,7 +59,7 @@ class TextManager /** * Draw the text */ - void draw(Graphics *graphics, int xOff, int yOff); + void draw(gcn::Graphics *graphics, int xOff, int yOff); private: /** diff --git a/src/textparticle.cpp b/src/textparticle.cpp index 0367f109..f38c32ce 100644 --- a/src/textparticle.cpp +++ b/src/textparticle.cpp @@ -19,9 +19,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "textparticle.h" +#include <guichan/color.hpp> #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 acf1e6a5..cdf99d8f 100644 --- a/src/textparticle.h +++ b/src/textparticle.h @@ -22,11 +22,8 @@ #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/strprintf.cpp b/src/utils/strprintf.cpp index bed4a7c4..07fb3508 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/tostring.h b/src/utils/tostring.h index 5ac1d3aa..62eb44e4 100644 --- a/src/utils/tostring.h +++ b/src/utils/tostring.h @@ -32,4 +32,19 @@ std::string toString(const T &arg) return ss.str(); } +// TODO: Is there a good way to suppress warnings from classes which don't use +// this function? +inline char *iptostring(int address) +{ + static char asciiIP[16]; + + sprintf(asciiIP, "%i.%i.%i.%i", + (unsigned char)(address), + (unsigned char)(address >> 8), + (unsigned char)(address >> 16), + (unsigned char)(address >> 24)); + + return asciiIP; +} + #endif diff --git a/src/utils/xml.cpp b/src/utils/xml.cpp index f6498c9f..d5dd54be 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 |