diff options
297 files changed, 8718 insertions, 2856 deletions
@@ -49,15 +49,15 @@ Use arrow keys to move around. Other keys: - F8 toggle shortcut window - F9 show setup window - F10 toggle debug window -- Alt + 0-9 show emotions -- Alt + S sit down / stand up +- Alt + 0-9 show emotions / usuable at skill level 2. +- Alt + S sit down / stand up / usuable at skill level 3. - Alt + F toggle debug pathfinding feature - Alt + P take screenshot +- Alt + T turns on anti-trade function / usuable at skill level 1. - A target nearest monster - H hide all non-sticky windows - G or Z pick up item - Enter focus chat window / send message -- Shift hold it when attacking to lock target for auto attack MOUSE: @@ -65,6 +65,38 @@ Left click to execute default action: walk, pick up an item, attack a monster and talk to NPCs (be sure to click on their feet). Right click to show up a context menu. Holding [Left Shift] prevents from walking when attacking. +/Commands: + +- /help Displays the list of commands +- /announce broadcasts a global msg(Gm Cammand only) +- /clear clears the chat window +- /who shows how many players are online +- /where displays the map name your currently on +- /whisper send a private msg to another player + (format: /whisper <charname> <message>) + If the <nick> has spaces in it, enclose it in double + quotes e.g. /whisper "char name" <message> +- /record Records the Chat output + (format: /record <filename to write to> starts the record + session /record again + stops the session) +- /party <command> <params>: Party commands + - /party new creates a new party /party new <party name> + - /party create creates a new party /party create <party name> + - /party prefix This commands sets the party prefix character + /party prefix <prefix-char> + "/party prefix" reports the current party prefix + character + - /party leave This command causes the player to leave the party. +Type /help party <option> for further help. +- /present This command gets a list of players within hearing +- /toggle make the chatlog lose focus on a blank line or after + message. (format: /toggle <option>, where option can be + '1', 'y' or 't' to make the chatlog lose focus on a + blank line, and '0', 'n' or 'f' to make the chatlog lose + focus after every message. /toggle displays the status) +For more information, type /help <command> + 3. Skills --------- @@ -73,10 +105,16 @@ point to spend on skills. BASIC SKILLS: -Level 1: enables the ability to trade with others -Level 2: enables the ability to express emotions -Level 3: enables character to sit -Other level not yet implemented. +Level 1: enables the ability to trade with others +Level 2: enables the ability to express emotions +Level 3: enables character to sit +Level 4: enables not used +level 5: enables the ability to join a party +level 6: enables not used +level 7: enables the ability to create a party +level 8: enables not used +level 9: enables not used +level 10: enables not used 4. Support ---------- diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt index d5e9e88e..35b9d4e2 100644 --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -1,5 +1,6 @@ SET(DATA_DIR ${PKG_DATADIR}/data) +ADD_SUBDIRECTORY(fonts) ADD_SUBDIRECTORY(graphics) ADD_SUBDIRECTORY(help) ADD_SUBDIRECTORY(icons) diff --git a/data/fonts/CMakeLists.txt b/data/fonts/CMakeLists.txt index dc22e031..d5b6ed85 100644 --- a/data/fonts/CMakeLists.txt +++ b/data/fonts/CMakeLists.txt @@ -1,6 +1,6 @@ SET (FILES dejavusans.ttf + dejavusans-bold.ttf ) INSTALL(FILES ${FILES} DESTINATION ${DATA_DIR}/fonts) - diff --git a/data/fonts/Makefile.am b/data/fonts/Makefile.am index 331f6c8b..5aa02999 100644 --- a/data/fonts/Makefile.am +++ b/data/fonts/Makefile.am @@ -1,5 +1,8 @@ fontsdir = $(pkgdatadir)/data/fonts -fonts_DATA = dejavusans.ttf +fonts_DATA = \ + dejavusans.ttf \ + dejavusans-bold.ttf -EXTRA_DIST = $(fonts_DATA) +EXTRA_DIST = \ + $(fonts_DATA) diff --git a/data/fonts/dejavusans-bold.ttf b/data/fonts/dejavusans-bold.ttf Binary files differnew file mode 100644 index 00000000..ebefb0ca --- /dev/null +++ b/data/fonts/dejavusans-bold.ttf diff --git a/data/fonts/dejavusans.ttf b/data/fonts/dejavusans.ttf Binary files differindex 627cef46..6253857c 100644 --- a/data/fonts/dejavusans.ttf +++ b/data/fonts/dejavusans.ttf diff --git a/data/graphics/gui/CMakeLists.txt b/data/graphics/gui/CMakeLists.txt index 3d617696..6351d5ba 100644 --- a/data/graphics/gui/CMakeLists.txt +++ b/data/graphics/gui/CMakeLists.txt @@ -8,7 +8,8 @@ SET (FILES checkbox.png close_button.png deepbox.png - fixedfont.png + default.png + gui.xml hits_blue.png hits_red.png hits_yellow.png @@ -20,16 +21,13 @@ SET (FILES hscroll_right_pressed.png item_shortcut_bgr.png mouse.png - menuitemD.png - menuitemF.png - menuitemN.png - menuitemP.png radioin.png radioout.png resize.png - rpgfont_wider.png selection.png slider.png + speech_bubble.png + speech_bubble.xml tab.png tabselected.png target-cursor-blue-l.png @@ -38,7 +36,6 @@ SET (FILES target-cursor-red-l.png target-cursor-red-m.png target-cursor-red-s.png - thickborder.png unknown-item.png vscroll_blue.png vscroll_down_default.png diff --git a/data/graphics/gui/Makefile.am b/data/graphics/gui/Makefile.am index 33abf521..eb4c7365 100644 --- a/data/graphics/gui/Makefile.am +++ b/data/graphics/gui/Makefile.am @@ -11,8 +11,8 @@ gui_DATA = \ checkbox.png \ close_button.png \ deepbox.png \ - emotions.png \ - fixedfont.png \ + default.png \ + gui.xml \ hits_blue.png \ hits_red.png \ hits_yellow.png \ @@ -24,16 +24,13 @@ gui_DATA = \ hscroll_right_pressed.png \ item_shortcut_bgr.png \ mouse.png \ - menuitemD.png \ - menuitemF.png \ - menuitemN.png \ - menuitemP.png \ radioin.png \ radioout.png \ resize.png \ - rpgfont_wider.png \ selection.png \ slider.png \ + speech_bubble.png \ + speechbubble.xml \ tab.png \ tabselected.png \ target-cursor-blue-l.png \ @@ -42,7 +39,6 @@ gui_DATA = \ target-cursor-red-l.png \ target-cursor-red-m.png \ target-cursor-red-s.png \ - thickborder.png \ unknown-item.png \ vscroll_blue.png \ vscroll_down_default.png \ diff --git a/data/graphics/gui/default.png b/data/graphics/gui/default.png Binary files differnew file mode 100644 index 00000000..29353df8 --- /dev/null +++ b/data/graphics/gui/default.png diff --git a/data/graphics/gui/emotions.png b/data/graphics/gui/emotions.png Binary files differdeleted file mode 100644 index 146f2d24..00000000 --- a/data/graphics/gui/emotions.png +++ /dev/null diff --git a/data/graphics/gui/fixedfont.png b/data/graphics/gui/fixedfont.png Binary files differdeleted file mode 100644 index 10319291..00000000 --- a/data/graphics/gui/fixedfont.png +++ /dev/null diff --git a/data/graphics/gui/gui.xml b/data/graphics/gui/gui.xml new file mode 100644 index 00000000..fe62528e --- /dev/null +++ b/data/graphics/gui/gui.xml @@ -0,0 +1,18 @@ +<skinset name="Default" image="default.png"> + <widget type="Window"> + <!-- Top Row --> + <part type="top-left-corner" xpos="0" ypos="0" width="4" height="4" /> + <part type="top-edge" xpos="4" ypos="0" width="3" height="4" /> + <part type="top-right-corner" xpos="7" ypos="0" width="4" height="4" /> + + <!-- Middle Row --> + <part type="left-edge" xpos="0" ypos="4" width="4" height="10" /> + <part type="bg-quad" xpos="11" ypos="0" width="32" height="32" /> + <part type="right-edge" xpos="7" ypos="4" width="4" height="10" /> + + <!-- Bottom Row --> + <part type="bottom-left-corner" xpos="0" ypos="15" width="4" height="4" /> + <part type="bottom-edge" xpos="4" ypos="15" width="3" height="4" /> + <part type="bottom-right-corner" xpos="7" ypos="15" width="4" height="4" /> + </widget> +</skinset>
\ No newline at end of file diff --git a/data/graphics/gui/hits_blue.png b/data/graphics/gui/hits_blue.png Binary files differindex 59458485..cfb04ab8 100644 --- a/data/graphics/gui/hits_blue.png +++ b/data/graphics/gui/hits_blue.png diff --git a/data/graphics/gui/hits_red.png b/data/graphics/gui/hits_red.png Binary files differindex da765dc4..150f1c1e 100644 --- a/data/graphics/gui/hits_red.png +++ b/data/graphics/gui/hits_red.png diff --git a/data/graphics/gui/hits_yellow.png b/data/graphics/gui/hits_yellow.png Binary files differindex d77b7c05..6975dfd5 100644 --- a/data/graphics/gui/hits_yellow.png +++ b/data/graphics/gui/hits_yellow.png diff --git a/data/graphics/gui/menuitemD.png b/data/graphics/gui/menuitemD.png Binary files differdeleted file mode 100644 index a9fe2222..00000000 --- a/data/graphics/gui/menuitemD.png +++ /dev/null diff --git a/data/graphics/gui/menuitemF.png b/data/graphics/gui/menuitemF.png Binary files differdeleted file mode 100644 index 84142a16..00000000 --- a/data/graphics/gui/menuitemF.png +++ /dev/null diff --git a/data/graphics/gui/menuitemN.png b/data/graphics/gui/menuitemN.png Binary files differdeleted file mode 100644 index bf25dd61..00000000 --- a/data/graphics/gui/menuitemN.png +++ /dev/null diff --git a/data/graphics/gui/menuitemP.png b/data/graphics/gui/menuitemP.png Binary files differdeleted file mode 100644 index 060d5a34..00000000 --- a/data/graphics/gui/menuitemP.png +++ /dev/null diff --git a/data/graphics/gui/rpgfont_wider.png b/data/graphics/gui/rpgfont_wider.png Binary files differdeleted file mode 100644 index 55ccbbd6..00000000 --- a/data/graphics/gui/rpgfont_wider.png +++ /dev/null diff --git a/data/graphics/gui/speech_bubble.png b/data/graphics/gui/speech_bubble.png Binary files differnew file mode 100644 index 00000000..3e678099 --- /dev/null +++ b/data/graphics/gui/speech_bubble.png diff --git a/data/graphics/gui/speechbubble.xml b/data/graphics/gui/speechbubble.xml new file mode 100644 index 00000000..1b11ea85 --- /dev/null +++ b/data/graphics/gui/speechbubble.xml @@ -0,0 +1,18 @@ +<skinset name="SpeechBubble" image="speech_bubble.png"> + <widget type="Window"> + <!-- Top Row --> + <part type="top-left-corner" xpos="0" ypos="0" width="14" height="14" /> + <part type="top-edge" xpos="15" ypos="0" width="1" height="14" /> + <part type="top-right-corner" xpos="17" ypos="0" width="17" height="14" /> + + <!-- Middle Row --> + <part type="left-edge" xpos="0" ypos="15" width="14" height="1" /> + <part type="bg-quad" xpos="34" ypos="0" width="32" height="32" /> + <part type="right-edge" xpos="17" ypos="15" width="17" height="1" /> + + <!-- Bottom Row --> + <part type="bottom-left-corner" xpos="0" ypos="17" width="14" height="17" /> + <part type="bottom-edge" xpos="15" ypos="17" width="1" height="17" /> + <part type="bottom-right-corner" xpos="17" ypos="17" width="17" height="17" /> + </widget> +</skinset>
\ No newline at end of file diff --git a/data/graphics/gui/thickborder.png b/data/graphics/gui/thickborder.png Binary files differdeleted file mode 100644 index da72c92f..00000000 --- a/data/graphics/gui/thickborder.png +++ /dev/null diff --git a/data/help/commands.txt b/data/help/commands.txt index 2c6e8299..c4795050 100644 --- a/data/help/commands.txt +++ b/data/help/commands.txt @@ -29,9 +29,7 @@ ##2H##P hide all non-sticky windows ##2Z##P pick up item ##2Enter##P focus chat window / send message - ##2Shift##P hold it when attacking to lock target for auto - attack - + ##2MOUSE: @@ -55,6 +53,8 @@ /whisper "<name>" <message> + For further help type /help in the chat console. + ##2IGNORING COMMUNICATION diff --git a/data/help/skills.txt b/data/help/skills.txt index 84d5bc5a..3a075ee0 100644 --- a/data/help/skills.txt +++ b/data/help/skills.txt @@ -11,5 +11,7 @@ ##2Level 1:##P enables the ability to trade with others ##2Level 2:##P enables the ability to express emotions ##2Level 3:##P enables character to sit + ##2Level 5:##P enables a char to join a party. + ##2Level 7:##P enables a char to create a party Other levels are still not implemented.
\ No newline at end of file diff --git a/docs/FAQ.txt b/docs/FAQ.txt index 2f1eee8b..538c14de 100644 --- a/docs/FAQ.txt +++ b/docs/FAQ.txt @@ -36,15 +36,6 @@ A: Did you read README? Are you really sure? If not go read it and you'll find README/ingame help is not up to date/wrong, please report it to the developers. -Q: I just typed /<command> in the chat window and then I saw "Your account has - been banished until ..." and TMW closed. Now when I try to log in nothing - happens? - -A: That's because /<commands> are not implemented at the moment of writing. - Please avoid using such commands (i.e. /help) until they're added. - Anyway the ban is temp, just wait some minutes and you should be able to - connect again. - Q: I can't recover HP anymore. A: Check your inventory, if you've got lots of stuff probably you're @@ -67,7 +58,7 @@ DEVELOPMENT Q: When will the next version be released? A: We have scheduled releases. Usually a new release is available every month. - Check http://wiki.themanaworld.org/index.php/Roadmap for further infos. + Check http://themanaworld.org/ for further info. Q: How can I contribute? @@ -82,7 +73,7 @@ A: There are a lot of ways: - You can donate money. Follow the link on the project page: http://sourceforge.net/projects/themanaworld. - - You can be a beta tester. Just play with The Mana World and report every + - You can be a beta tester. Just play The Mana World and report every error to the tracker. Read for more infos on the project page: http://themanaworld.org. diff --git a/packaging/windows/setup.nsi b/packaging/windows/setup.nsi index ec57051c..164b49a6 100644 --- a/packaging/windows/setup.nsi +++ b/packaging/windows/setup.nsi @@ -170,6 +170,7 @@ Section "Core files (required)" SecCore File "${SRCDIR}\data\fonts\*.ttf" SetOutPath "$INSTDIR\data\graphics\gui" File "${SRCDIR}\data\graphics\gui\*.png" + File "${SRCDIR}\data\graphics\gui\*.xml" SetOutPath "$INSTDIR\data\graphics\images" File /x minimap_*.png ${SRCDIR}\data\graphics\images\*.png SetOutPath "$INSTDIR\data\help" diff --git a/po/POTFILES.in b/po/POTFILES.in index 4207012b..f338e665 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -5,8 +5,12 @@ src/gui/buysell.cpp src/gui/char_select.cpp src/gui/char_server.cpp src/gui/chat.cpp +src/gui/colour.cpp src/gui/confirm_dialog.cpp src/gui/connection.cpp +src/gui/emotecontainer.cpp +src/gui/emoteshortcutcontainer.cpp +src/gui/emotewindow.cpp src/gui/equipmentwindow.cpp src/gui/help.cpp src/gui/inventorywindow.cpp @@ -14,7 +18,9 @@ src/gui/item_amount.cpp src/gui/login.cpp src/gui/menuwindow.cpp src/gui/minimap.cpp +src/gui/npcintegerdialog.cpp src/gui/npclistdialog.cpp +src/gui/npcstringdialog.cpp src/gui/npc_text.cpp src/gui/ok_dialog.cpp src/gui/popupmenu.cpp @@ -22,13 +28,24 @@ src/gui/register.cpp src/gui/sell.cpp src/gui/setup_audio.cpp src/gui/setup.cpp +src/gui/setup_colours.cpp src/gui/setup_joystick.cpp src/gui/setup_keyboard.cpp src/gui/setup_players.cpp src/gui/setup_video.cpp src/gui/skill.cpp +src/gui/speechbubble.cpp src/gui/status.cpp src/gui/trade.cpp src/gui/updatewindow.cpp +src/net/playerhandler.cpp +src/net/tradehandler.cpp +src/resources/colordb.cpp +src/resources/emotedb.cpp src/resources/itemdb.cpp +src/resources/monsterdb.cpp +src/resources/npcdb.cpp +src/being.cpp +src/game.cpp src/main.cpp +src/party.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 70a58272..fc98ae64 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 @@ -101,6 +103,10 @@ 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 @@ -167,6 +173,8 @@ SET(SRCS gui/skill.h gui/slider.cpp gui/slider.h + gui/speechbubble.cpp + gui/speechbubble.h gui/status.cpp gui/status.h gui/table.cpp @@ -231,8 +239,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,10 +297,13 @@ SET(SRCS configlistener.h configuration.cpp configuration.h + effectmanager.cpp + effectmanager.h engine.cpp engine.h equipment.cpp equipment.h + extensions.h floor_item.cpp floor_item.h flooritemmanager.cpp @@ -338,6 +353,8 @@ SET(SRCS position.cpp position.h properties.h + sdltruetypefont.cpp + sdltruetypefont.hpp serverinfo.h shopitem.cpp shopitem.h diff --git a/src/Makefile.am b/src/Makefile.am index 358b0b61..a1085d0a 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,38 @@ 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/buttonbox.cpp \ + gui/buttonbox.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/colour.cpp \ + gui/colour.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 +63,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 \ @@ -100,10 +112,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_colours.cpp \ + gui/setup_colours.h \ gui/setup_joystick.cpp \ gui/setup_joystick.h \ gui/setup_keyboard.cpp \ @@ -117,16 +131,22 @@ 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 \ @@ -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,10 +266,15 @@ 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 \ equipment.h \ + extensions.h \ floor_item.cpp \ floor_item.h \ flooritemmanager.cpp \ @@ -283,11 +314,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 \ @@ -295,6 +328,8 @@ tmw_SOURCES = gui/widgets/layout.cpp \ position.cpp \ position.h \ properties.h \ + recorder.cpp \ + recorder.h \ serverinfo.h \ shopitem.cpp \ shopitem.h \ 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..63c60390 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -22,44 +22,52 @@ #include "being.h" #include "animatedsprite.h" +#include "configuration.h" #include "equipment.h" #include "game.h" #include "graphics.h" +#include "localplayer.h" #include "log.h" #include "map.h" #include "particle.h" #include "sound.h" -#include "localplayer.h" #include "text.h" #include "statuseffect.h" -#include "resources/itemdb.h" -#include "resources/resourcemanager.h" +#include "resources/emotedb.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/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), @@ -68,6 +76,9 @@ Being::Being(int id, int job, Map *map): 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 +94,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 +134,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 +167,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 +180,48 @@ 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 + while (mSpeech[0] == ' ') + { + mSpeech = mSpeech.substr(1, mSpeech.size()); + } + while (mSpeech[mSpeech.size()] == ' ') + { + mSpeech = mSpeech.substr(0, mSpeech.size() - 1); + } + + // check for links + std::string::size_type start = mSpeech.find('['); + std::string::size_type end = mSpeech.find(']', start); + + 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); + } - mSpeech = new Text(text, mPx + X_SPEECH_OFFSET, mPy - Y_SPEECH_OFFSET, - gcn::Graphics::CENTER, - gcn::Color(255, 255, 255), true); - mSpeechTime = 500; + std::string::size_type position = mSpeech.find('|'); + 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 != "") + mSpeechTime = 500; } void Being::takeDamage(int amount) @@ -173,10 +236,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; @@ -192,6 +251,26 @@ void Being::takeDamage(int amount) mPx + 16, mPy + 16); } +void Being::showCrit() +{ + gcn::Font *font; + std::string text = "crit!"; + + // Selecting the right color + if (getType() == MONSTER) + { + font = hitBlueFont; + } + else + { + font = hitRedFont; + } + + // Show crit notice + particleEngine->addTextSplashEffect(text, 255, 255, 255, font, + mPx + 16, mPy + 16); +} + void Being::handleAttack(Being *victim, int damage) { setAction(Being::ATTACK); @@ -225,9 +304,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 +321,8 @@ void Being::setAction(Uint8 action) { currentAction = mEquippedWeapon->getAttackType(); } - else { + else + { currentAction = ACTION_ATTACK; } for (int i = 0; i < VECTOREND_SPRITE; i++) @@ -278,9 +359,11 @@ void Being::setAction(Uint8 action) } } - void Being::setDirection(Uint8 direction) { + if (mDirection == direction) + return; + mDirection = direction; SpriteDirection dir = getSpriteDirection(); @@ -307,10 +390,11 @@ SpriteDirection Being::getSpriteDirection() const { dir = DIRECTION_RIGHT; } - else { - dir = DIRECTION_LEFT; + else + { + dir = DIRECTION_LEFT; } - + return dir; } @@ -352,28 +436,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--; @@ -431,8 +515,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(Graphics *graphics, int offsetX, int offsetY) +{ + int px = mPx + offsetX; + int py = mPy + offsetY; + + // Draw speech above this being + if (mSpeechTime > 0 && config.getValue("speechbubble", 1)) + { + if (mText) + { + delete mText; + mText = 0; + } + + mSpeechBubble->setCaption(mName, 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 +578,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 +622,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 +631,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 +665,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; } } diff --git a/src/being.h b/src/being.h index 7d5ed989..9e207448 100644 --- a/src/being.h +++ b/src/being.h @@ -24,15 +24,22 @@ #include <list> #include <memory> -#include <string> #include <SDL_types.h> #include <set> +#include <string> +#include <vector> +#include <bitset> -#include "position.h" -#include "sprite.h" -#include "map.h" #include "animatedsprite.h" +#include "effectmanager.h" +#include "map.h" #include "particlecontainer.h" +#include "position.h" +#include "sprite.h" + +#include "gui/speechbubble.h" + +#include "resources/colordb.h" #define FIRST_IGNORE_EMOTE 14 #define STATUS_EFFECTS 32 @@ -43,8 +50,8 @@ class ItemInfo; class Item; class Map; class Graphics; -class ImageSet; class Particle; +class SpeechBubble; class Text; class StatusEffect; @@ -107,13 +114,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. @@ -152,6 +159,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 +198,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 +228,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(Graphics *graphics, int offsetX, int offsetY); + + /** * Draws the emotion picture above the being. */ void drawEmotion(Graphics *graphics, int offsetX, int offsetY); @@ -253,7 +283,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 +296,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 +357,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 +398,10 @@ class Being : public Sprite internalTriggerEffect(effectId, false, true); } - const std::auto_ptr<Equipment> mEquipment; + // Target cursor being used by the being + Image *mTargetCursor; + const std::auto_ptr<Equipment> mEquipment; static int getHairColorsNr(); @@ -365,6 +409,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 +424,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 +458,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 +476,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 +492,13 @@ 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? */ + + // 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..51d58144 100644 --- a/src/configlistener.h +++ b/src/configlistener.h @@ -24,7 +24,6 @@ #include <iosfwd> - /** * The listener interface for receiving notifications about changes to * configuration options. diff --git a/src/configuration.cpp b/src/configuration.cpp index 04839777..31c18c1d 100644 --- a/src/configuration.cpp +++ b/src/configuration.cpp @@ -19,12 +19,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "configuration.h" - #include <libxml/xmlwriter.h> #include "configlistener.h" +#include "configuration.h" #include "log.h" #include "utils/tostring.h" @@ -90,7 +88,6 @@ void ConfigurationObject::clear() mOptions.clear(); } - ConfigurationObject::~ConfigurationObject() { clear(); @@ -193,7 +190,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..91ae536b 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -22,11 +22,11 @@ #ifndef CONFIGURATION_H #define CONFIGURATION_H -#include <map> -#include <list> -#include <string> #include <cassert> #include <libxml/xmlwriter.h> +#include <list> +#include <map> +#include <string> class ConfigListener; class ConfigurationObject; diff --git a/src/effectmanager.cpp b/src/effectmanager.cpp new file mode 100644 index 00000000..cf77de37 --- /dev/null +++ b/src/effectmanager.cpp @@ -0,0 +1,106 @@ +/* + * The Mana World + * Copyright 2008 The Mana World Development Team + * + * This file is part of Aethyra. + * + * 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 "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) + { + printf("Found effect, playing it"); + rValue = true; + if((*i).GFX != "") + { + Particle *selfFX; + selfFX = particleEngine->addEffect((*i).GFX, 0, 0); + being->controlParticle(selfFX); + } + if((*i).SFX != "") + 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) + { + printf("Found effect, playing it"); + rValue = true; + if((*i).GFX != "") + particleEngine->addEffect((*i).GFX, x, y); + if((*i).SFX != "") + sound.playSfx((*i).SFX); + break; + } + } + return rValue; +} + diff --git a/src/effectmanager.h b/src/effectmanager.h new file mode 100644 index 00000000..a9efcdbc --- /dev/null +++ b/src/effectmanager.h @@ -0,0 +1,66 @@ +/* + * The Mana World + * Copyright 2008 The Mana World Development Team + * + * This file is part of Aethyra. + * + * 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> + +#include "being.h" + +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..66989d44 --- /dev/null +++ b/src/emoteshortcut.cpp @@ -0,0 +1,77 @@ +/* + * Aethyra + * Copyright 2009 Aethyra Development Team + * + * This file is part of Aethyra. + * + * Aethyra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * Aethyra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Aethyra; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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..3f907e1b --- /dev/null +++ b/src/emoteshortcut.h @@ -0,0 +1,125 @@ +/* + * Aethyra + * Copyright 2009 Aethyra Development Team + * + * This file is part of Aethyra. + * + * Aethyra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * Aethyra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Aethyra; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef 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..29b8921a 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -19,13 +19,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "engine.h" - #include <list> #include "being.h" #include "beingmanager.h" #include "configuration.h" +#include "engine.h" #include "flooritemmanager.h" #include "game.h" #include "graphics.h" @@ -82,28 +81,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 +127,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/equipment.cpp b/src/equipment.cpp index b93beed4..d5e0f656 100644 --- a/src/equipment.cpp +++ b/src/equipment.cpp @@ -19,24 +19,24 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <algorithm> + #include "equipment.h" #include "item.h" #include "inventory.h" #include "localplayer.h" -#include <algorithm> - Equipment::Equipment(): mArrows(0) { 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); } + diff --git a/src/extensions.h b/src/extensions.h new file mode 100644 index 00000000..5b95afc8 --- /dev/null +++ b/src/extensions.h @@ -0,0 +1,34 @@ +/* + * Aethyra + * Copyright 2008 Lloyd Bryant <sanga@aethyra.com> + * + * This file is part of the Aethyra project. + * + * Aethyra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * Aethyra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _EXTENSIONS_ +#define _EXTENSIONS_ + +struct EXTENSIONS +{ + bool aethyra_inventory; + bool aethyra_spells; + bool aethyra_misc; +}; + +extern struct EXTENSIONS extensions; +#endif diff --git a/src/floor_item.cpp b/src/floor_item.cpp index e96b3652..0c4c1c10 100644 --- a/src/floor_item.cpp +++ b/src/floor_item.cpp @@ -20,7 +20,6 @@ */ #include "floor_item.h" - #include "map.h" FloorItem::FloorItem(unsigned int id, diff --git a/src/floor_item.h b/src/floor_item.h index e0a67eca..a7299bfb 100644 --- a/src/floor_item.h +++ b/src/floor_item.h @@ -51,42 +51,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 { return mItem->getId(); } /** * 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 + void draw(Graphics *graphics, int offsetX, int offsetY) const { graphics->drawImage(mItem->getImage(), mX * 32 + offsetX, 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 1c4bb538..3b943f6b 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -19,19 +19,20 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "game.h" - #include <fstream> #include <physfs.h> #include <sstream> #include <string> #include <guichan/exception.hpp> +#include <guichan/sdl/sdlinput.hpp> #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,15 @@ #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/shortcutcontainer.h" +#include "gui/itemshortcutcontainer.h" #include "gui/menuwindow.h" #include "gui/minimap.h" #include "gui/ministatus.h" @@ -75,15 +80,19 @@ #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" +#include "utils/gettext.h" + extern Graphics *graphics; class Map; @@ -93,6 +102,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 +119,7 @@ BuyDialog *buyDialog; SellDialog *sellDialog; BuySellDialog *buySellDialog; InventoryWindow *inventoryWindow; +EmoteWindow *emoteWindow; NpcIntegerDialog *npcIntegerDialog; NpcListDialog *npcListDialog; NpcTextDialog *npcTextDialog; @@ -118,19 +129,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 +175,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 +205,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(); @@ -199,20 +215,16 @@ void createGuiWindows(Network *network) minimap = new Minimap(); equipmentWindow = new EquipmentWindow(player_node->mEquipment.get()); 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 +234,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 +254,7 @@ void destroyGuiWindows() delete sellDialog; delete buySellDialog; delete inventoryWindow; + delete emoteWindow; delete npcIntegerDialog; delete npcListDialog; delete npcTextDialog; @@ -252,10 +264,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 +288,8 @@ Game::Game(Network *network): beingManager = new BeingManager(network); floorItemManager = new FloorItemManager(); + effectManager = new EffectManager(); + particleEngine = new Particle(NULL); particleEngine->setupEngine(); @@ -310,17 +324,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, I've modified it to wait for a "ping" + * from the client to complete its initialization * - * 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. - * - * 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,13 +389,13 @@ 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); - logger->log("Error: could not save screenshot."); + chatWindow->chatLog(_("Saving screenshot failed!"), BY_SERVER); + logger->log(_("Error: could not save screenshot.")); } SDL_FreeSurface(screenshot); @@ -430,7 +440,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 +470,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(); } @@ -486,8 +496,8 @@ void Game::handleInput() { gcn::Window *requestedWindow = NULL; - if (setupWindow->isVisible() && - keyboard.getNewKeyIndex() > keyboard.KEY_NO_VALUE) + if (setupWindow->isVisible() && + keyboard.getNewKeyIndex() > keyboard.KEY_NO_VALUE) { keyboard.setNewKey((int) event.key.keysym.sym); keyboard.callbackNewKey(); @@ -496,11 +506,11 @@ void Game::handleInput() } // Keys pressed together with Alt/Meta // Emotions and some internal gui windows - #ifndef __APPLE__ +#ifndef __APPLE__ if (event.key.keysym.mod & KMOD_LALT) - #else +#else if (event.key.keysym.mod & KMOD_LMETA) - #endif +#endif { switch (event.key.keysym.sym) { @@ -521,52 +531,37 @@ void Game::handleInput() case SDLK_t: // Toggle accepting of incoming trade requests + unsigned int deflt = player_relations.getDefault(); + if (deflt & PlayerRelation::TRADE) { - 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); + 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; } @@ -580,21 +575,18 @@ void Game::handleInput() used = true; } break; - case SDLK_PAGEDOWN: if (chatWindow->isVisible()) { chatWindow->scroll(DEFAULT_CHAT_WINDOW_SCROLL); used = true; + return; } break; - case SDLK_F1: // In-game Help if (helpWindow->isVisible()) - { helpWindow->setVisible(false); - } else { helpWindow->loadHelp("index"); @@ -606,47 +598,33 @@ void Game::handleInput() case SDLK_RETURN: // Input chat window if (chatWindow->isInputFocused() || - deathNotice != NULL || - weightNotice != NULL) + deathNotice != NULL || + weightNotice != NULL) { break; } // Quit by pressing Enter if the exit confirm is there if (exitConfirm) - { - done = true; - } + 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")); - } // Submits the text and proceeds to the next dialog else if (npcStringDialog->isVisible()) - { npcStringDialog->action(gcn::ActionEvent(NULL, "ok")); - } // Proceed to the next dialog option, or close the window else if (npcTextDialog->isVisible()) - { npcTextDialog->action(gcn::ActionEvent(NULL, "ok")); - } // Choose the currently highlighted dialogue option else if (npcListDialog->isVisible()) - { npcListDialog->action(gcn::ActionEvent(NULL, "ok")); - } // Submits the text and proceeds to the next dialog else if (npcIntegerDialog->isVisible()) - { npcIntegerDialog->action(gcn::ActionEvent(NULL, "ok")); - } // Else, open the chat edit box else { @@ -654,17 +632,19 @@ void Game::handleInput() used = true; } break; - // Quitting confirmation dialog - case SDLK_ESCAPE: - if (!exitConfirm) { - exitConfirm = new ConfirmDialog( - "Quit", "Are you sure you want to quit?"); + // Quitting confirmation dialog + case SDLK_ESCAPE: + if (!exitConfirm) + { + exitConfirm = new ConfirmDialog( _("Quit"), + _("Are you sure you " + "want to quit?")); exitConfirm->addActionListener(&exitListener); exitConfirm->requestMoveToTop(); } else { - exitConfirm->action(gcn::ActionEvent(NULL, "no")); + exitConfirm->action(gcn::ActionEvent(NULL, _("no"))); } break; @@ -672,25 +652,29 @@ void Game::handleInput() 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 = @@ -699,7 +683,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) @@ -727,11 +712,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); @@ -739,7 +725,6 @@ void Game::handleInput() debugWindow->setVisible(false); } break; - case KeyboardConfig::KEY_WINDOW_STATUS: requestedWindow = statusWindow; break; @@ -753,6 +738,7 @@ void Game::handleInput() requestedWindow = skillDialog; break; case KeyboardConfig::KEY_WINDOW_MINIMAP: + minimap->toggle(); requestedWindow = minimap; break; case KeyboardConfig::KEY_WINDOW_CHAT: @@ -767,6 +753,12 @@ 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; } } @@ -774,14 +766,10 @@ void Game::handleInput() { requestedWindow->setVisible(!requestedWindow->isVisible()); if (requestedWindow->isVisible()) - { requestedWindow->requestMoveToTop(); - } used = true; } - } - // Quit event else if (event.type == SDL_QUIT) { @@ -798,10 +786,9 @@ void Game::handleInput() catch (gcn::Exception e) { const char* err = e.getMessage().c_str(); - logger->log("Warning: guichan input exception: %s", err); + logger->log(_("Warning: guichan input exception: %s"), err); } } - } // End while // If the user is configuring the keys then don't respond. @@ -810,8 +797,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(); @@ -822,23 +808,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; } @@ -847,60 +833,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)) @@ -40,35 +40,35 @@ extern volatile int tick_time; class Game : public ConfigListener { public: - Game(Network *network); - ~Game(); + Game(Network *network); + ~Game(); - void logic(); + void logic(); - void handleInput(); + void handleInput(); - void optionChanged(const std::string &name); + void optionChanged(const std::string &name); private: - Network *mNetwork; - - /** Used to determine whether to draw the next frame. */ - int mDrawTime; - - /** The minimum frame time (used for frame limiting). */ - int mMinFrameTime; - - typedef const std::auto_ptr<MessageHandler> MessageHandlerPtr; - MessageHandlerPtr mBeingHandler; - MessageHandlerPtr mBuySellHandler; - MessageHandlerPtr mChatHandler; - MessageHandlerPtr mEquipmentHandler; - MessageHandlerPtr mInventoryHandler; - MessageHandlerPtr mItemHandler; - MessageHandlerPtr mNpcHandler; - MessageHandlerPtr mPlayerHandler; - MessageHandlerPtr mSkillHandler; - MessageHandlerPtr mTradeHandler; + Network *mNetwork; + + /** Used to determine whether to draw the next frame. */ + int mDrawTime; + + /** The minimum frame time (used for frame limiting). */ + int mMinFrameTime; + + typedef const std::auto_ptr<MessageHandler> MessageHandlerPtr; + MessageHandlerPtr mBeingHandler; + MessageHandlerPtr mBuySellHandler; + MessageHandlerPtr mChatHandler; + MessageHandlerPtr mEquipmentHandler; + MessageHandlerPtr mInventoryHandler; + MessageHandlerPtr mItemHandler; + MessageHandlerPtr mNpcHandler; + MessageHandlerPtr mPlayerHandler; + MessageHandlerPtr mSkillHandler; + MessageHandlerPtr mTradeHandler; }; /** diff --git a/src/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..8009ceda 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -95,17 +95,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 diff --git a/src/gui/browserbox.cpp b/src/gui/browserbox.cpp index 2d805b9c..6fd0482a 100644 --- a/src/gui/browserbox.cpp +++ b/src/gui/browserbox.cpp @@ -22,7 +22,7 @@ #include <algorithm> #include "browserbox.h" - +#include "colour.h" #include "linkhandler.h" #include "truetypefont.h" @@ -98,12 +98,12 @@ void BrowserBox::addRow(const std::string &row) mLinks.push_back(bLink); - newRow += "##L" + bLink.caption; + newRow += "##<" + bLink.caption; tmp.erase(0, idx3 + 2); if(tmp != "") { - newRow += "##P"; + newRow += "##>"; } idx1 = tmp.find("@@"); } @@ -210,8 +210,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 +220,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 +228,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) { @@ -254,7 +251,8 @@ BrowserBox::draw(gcn::Graphics *graphics) if ((mHighMode & UNDERLINE)) { - graphics->setColor(gcn::Color(LINK)); + bool valid; + graphics->setColor(gcn::Color(textColour->getColour('<', valid))); graphics->drawLine( mLinks[mSelectedLink].x1, mLinks[mSelectedLink].y2, @@ -311,57 +309,30 @@ 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 = textColour->getColour(c, valid); + if (c == '<') + { prevColor = selColor; - selColor = BLACK; + } + if (valid) + { + selColor = rgb; + } } start += 3; + + if (start == row.size()) + { + break; + } } graphics->setColor(gcn::Color(selColor)); } @@ -424,3 +395,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..4bdf224b 100644 --- a/src/gui/browserbox.h +++ b/src/gui/browserbox.h @@ -165,3 +165,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..9b624015 100644 --- a/src/gui/button.cpp +++ b/src/gui/button.cpp @@ -122,8 +122,7 @@ Button::~Button() } } -void -Button::draw(gcn::Graphics *graphics) +void Button::draw(gcn::Graphics *graphics) { int mode; diff --git a/src/gui/button.h b/src/gui/button.h index 0ec99245..d8ed9fa7 100644 --- a/src/gui/button.h +++ b/src/gui/button.h @@ -26,6 +26,8 @@ #include <guichan/widgets/button.hpp> +#include "../guichanfwd.h" + class ImageRect; /** diff --git a/src/gui/buttonbox.cpp b/src/gui/buttonbox.cpp new file mode 100644 index 00000000..d29f3c58 --- /dev/null +++ b/src/gui/buttonbox.cpp @@ -0,0 +1,43 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "button.h" +#include "buttonbox.h" + +ButtonBox::ButtonBox(const std::string &title, const std::string &buttonTxt, + ButtonBoxListener *listener) : + Window(title), + mListener(listener) +{ + Button *button = new Button(buttonTxt, "activate", this); + setContentSize(button->getWidth() + 10, + button->getHeight() + 10); + button->setPosition(5, 5); + add(button); +} + +void ButtonBox::action(const gcn::ActionEvent &event) +{ + if (event.getId() == "activate") + { + mListener->buttonBoxRespond(); + } +} diff --git a/src/gui/buttonbox.h b/src/gui/buttonbox.h new file mode 100644 index 00000000..6d0e46b6 --- /dev/null +++ b/src/gui/buttonbox.h @@ -0,0 +1,69 @@ +/* + * 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 BUTTONBOX_H +#define BUTTONBOX_H + +#include <string> + +#include <guichan/actionlistener.hpp> + +#include "window.h" + +#include "../guichanfwd.h" + +class ButtonBoxListener +{ + public: + + /* + * function that ButtonBox calls when the button has been pressed + */ + virtual void buttonBoxRespond() = 0; +}; + +class ButtonBox : public Window, public gcn::ActionListener +{ + public: + + /* + * Constructor + * + * @param title is the text that appears at the top of the box + * @param buttonTxt is the text that appears on the button + * @param listener points to the class that should respond to the + * button press + */ + ButtonBox(const std::string &title, const std::string &buttonTxt, + ButtonBoxListener *listener); + + /* + * called when the button is pressed + * + * @param event is the event that is generated + */ + void action(const gcn::ActionEvent &event); + + private: + + ButtonBoxListener *mListener; +}; +#endif 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..423918ce 100644 --- a/src/gui/buy.h +++ b/src/gui/buy.h @@ -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/buysell.h b/src/gui/buysell.h index e3cdc52a..60a6398d 100644 --- a/src/gui/buysell.h +++ b/src/gui/buysell.h @@ -26,6 +26,8 @@ #include "window.h" +#include "../guichanfwd.h" + /** * A dialog to choose between buying or selling at a shop. * diff --git a/src/gui/char_select.cpp b/src/gui/char_select.cpp index df57f969..e556dfa0 100644 --- a/src/gui/char_select.cpp +++ b/src/gui/char_select.cpp @@ -19,13 +19,13 @@ * 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" @@ -69,7 +69,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 +82,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 +130,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 +141,21 @@ void CharSelectDialog::action(const gcn::ActionEvent &event) { state = EXIT_STATE; } - else if (event.getId() == "new" && n_character <= MAX_SLOT) - { - // Start new character dialog - CharCreateDialog *charCreateDialog = - new CharCreateDialog(this, mCharInfo->getPos(), mNetwork, mGender); - charServerHandler.setCharCreateDialog(charCreateDialog); - } - else if (event.getId() == "delete") + else if (event.getId() == "newdel") { - // 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 +179,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 +228,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 +249,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 +266,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 +303,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..5d4ecfa8 100644 --- a/src/gui/char_select.h +++ b/src/gui/char_select.h @@ -22,17 +22,17 @@ #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 <guichan/actionlistener.hpp> - -class Player; class LocalPlayer; class Network; +class Player; class PlayerBox; /** @@ -65,8 +65,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; diff --git a/src/gui/char_server.cpp b/src/gui/char_server.cpp index 7be2441d..2e823b60 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" @@ -99,8 +98,7 @@ ServerSelectDialog::~ServerSelectDialog() delete mServerListModel; } -void -ServerSelectDialog::action(const gcn::ActionEvent &event) +void ServerSelectDialog::action(const gcn::ActionEvent &event) { if (event.getId() == "ok") { mOkButton->setEnabled(false); @@ -108,7 +106,6 @@ ServerSelectDialog::action(const gcn::ActionEvent &event) mLoginData->hostname = iptostring(si->address); mLoginData->port = si->port; mLoginData->updateHost = si->updateHost; - state = mNextState; } else if (event.getId() == "cancel") { @@ -116,14 +113,12 @@ ServerSelectDialog::action(const gcn::ActionEvent &event) } } -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/chat.cpp b/src/gui/chat.cpp index 87d843a0..c9c3e9bc 100644 --- a/src/gui/chat.cpp +++ b/src/gui/chat.cpp @@ -20,56 +20,66 @@ */ #include <algorithm> -#include <sstream> +#include <fstream> #include <guichan/focushandler.hpp> -#include "chat.h" - #include "browserbox.h" +#include "chat.h" #include "chatinput.h" +#include "itemlinkhandler.h" #include "scrollarea.h" #include "sdlinput.h" #include "windowcontainer.h" #include "widgets/layout.h" +#include "../beingmanager.h" #include "../configuration.h" +#include "../extensions.h" #include "../game.h" #include "../localplayer.h" +#include "../party.h" +#include "../recorder.h" #include "../net/messageout.h" #include "../net/protocol.h" +#include "../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->setPosition(mScrollArea->getFrameSize(), + mScrollArea->getFrameSize()); + 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 +94,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 == "" ? '$' : 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"); } -void ChatWindow::chatLog(std::string line, int own) +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, 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 +143,99 @@ void ChatWindow::chatLog(std::string line, int own) } } - std::string lineColor = "##0"; // Equiv. to BrowserBox::BLACK - switch (own) { + 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 == "" && 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 +249,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 +266,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 +304,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 +320,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 +334,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 +344,67 @@ 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 */ + // 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 (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); + + while (temp[0] == ' ') + { + temp = temp.substr(1, temp.size()); + } + while (temp[temp.size()] == ' ') + { + temp = temp.substr(0, temp.size() - 1); + } + + for (unsigned int i = 0; i < temp.size(); i++) + { + temp[i] = (char) tolower(temp[i]); + } + + const ItemInfo itemInfo = ItemDB::get(temp); + 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 +412,39 @@ 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 if (msg.substr(0, IS_ANNOUNCE_LENGTH) == IS_ANNOUNCE) + else + { + msg = msg.substr(space); + trim(msg); + } + + 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); + msg.erase(0, 6); trim(msg); std::size_t space = msg.find(" "); std::string msg1; + if (space == std::string::npos) { msg1 = ""; @@ -317,33 +454,160 @@ 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) == '/') { 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->respond(msg); + else if (command == "toggle") + { + if (msg == "") + { + chatLog(mReturnToggles ? _("Return toggles chat.") + : _("Message closes chat."), BY_SERVER); + return; + } + + msg = msg.substr(0, 1); + + if (msg == "1" || + msg == "y" || msg == "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 == "") + { + chatLog(_("Unknown party command... Type \"/help\" party for more " + "information."), BY_SERVER); + return; + } + + const std::string::size_type space = msg.find(" "); + std::string rest = (space == std::string::npos ? "" + : msg.substr(space + 1, msg.length())); + + if (rest != "") + { + msg = msg.substr(0, space); + 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 + */ + if (extensions.aethyra_spells) + { + MessageOut outMsg(mNetwork); + if (msg == "heal") + { + outMsg.writeInt16(0x03f3); + outMsg.writeInt16(0x01); + outMsg.writeInt32(0); + outMsg.writeInt8(0); + outMsg.writeInt8(0); + outMsg.writeString("", 24); + } + else if (msg == "gather") + { + outMsg.writeInt16(0x03f3); + outMsg.writeInt16(0x02); + outMsg.writeInt32(0); + outMsg.writeInt8(0); + outMsg.writeInt8(0); + outMsg.writeString("", 24); + } + else + chatLog(_("No such spell!"), BY_SERVER); + } + else + chatLog(_("The current server doesn't support spells"), BY_SERVER); + } + 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 != "") + { + 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 (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 +617,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 +643,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; @@ -412,8 +679,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 +718,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 +741,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(int itemId, const std::string &item) +{ + std::ostringstream text; + text << "[@@" << itemId << "|" << item << "@@] "; + mChatInput->setText(mChatInput->getText() + text.str()); + requestChatFocus(); +} + void ChatWindow::setVisible(bool isVisible) { Window::setVisible(isVisible); @@ -484,70 +765,132 @@ 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 == "") + { + 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 == "") { chatLog(_("/announce: Global announcement (GM only)"), BY_SERVER); chatLog(_("/clear: Clears this window"), BY_SERVER); chatLog(_("/help: Display this help"), BY_SERVER); + mParty->help(); + chatLog(_("/msg <nick> <message>: Alternate form for /whisper"), BY_SERVER); + chatLog(_("/present: Get list of players present"), BY_SERVER); + mRecorder->help(); + chatLog(_("/toggle: Determine whether <return> toggles the chat log."), + BY_SERVER); chatLog(_("/where: Display map name"), BY_SERVER); + chatLog(_("/w <nick> <message>: Short form for /whisper"), BY_SERVER); chatLog(_("/whisper <nick> <message>: Sends a private <message>" " to <nick>"), BY_SERVER); - chatLog(_("/w <nick> <message>: Short form for /whisper"), BY_SERVER); chatLog(_("/who: Display number of online users"), BY_SERVER); chatLog(_("For more information, type /help <command>"), BY_SERVER); - return; } - 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 == "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") + { + mRecorder->help(msg2); + } + 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..176fccb7 100644 --- a/src/gui/chat.h +++ b/src/gui/chat.h @@ -22,6 +22,7 @@ #ifndef CHAT_H #define CHAT_H +#include <fstream> #include <list> #include <string> @@ -34,32 +35,21 @@ 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 4 // getting whispered at -#define ACT_IS 5 // equivalent to "/me" on IRC - -#define BY_LOGGER 6 - -#define IS_ANNOUNCE "/announce " -#define IS_ANNOUNCE_LENGTH 10 -#define IS_HELP "/help" -#define IS_HELP_LENGTH 5 -#define IS_WHERE "/where" -#define IS_WHERE_LENGTH 6 -#define IS_WHO "/who" -#define IS_WHO_LENGTH 4 -#define IS_CLEAR "/clear" -#define IS_CLEAR_LENGTH 6 -#define IS_WHISPER "/whisper" -#define IS_WHISPER_LENGTH 8 -#define IS_SHORT_WHISPER "/w" -#define IS_SHORT_WHISPER_LENGTH 2 +#define ACT_WHISPER 5 // getting whispered at +#define ACT_IS 6 // equivalent to "/me" on IRC + +#define BY_LOGGER 7 /** * gets in between usernick and message text depending on @@ -127,14 +117,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 +181,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(int itemid, const std::string &item); + /** Override to reset mTmpVisible */ void setVisible(bool visible); @@ -199,6 +197,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 +213,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 +233,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..d98e120b 100644 --- a/src/gui/chatinput.h +++ b/src/gui/chatinput.h @@ -22,9 +22,11 @@ #ifndef CHATINPUT_H #define CHATINPUT_H +#include <guichan/focuslistener.hpp> + #include "textfield.h" -#include <guichan/focuslistener.hpp> +#include "../guichanfwd.h" /** * The chat input hides when it loses focus. It is also invisible by default. diff --git a/src/gui/checkbox.h b/src/gui/checkbox.h index 4b312d22..d92fc822 100644 --- a/src/gui/checkbox.h +++ b/src/gui/checkbox.h @@ -26,6 +26,8 @@ #include <guichan/widgets/checkbox.hpp> +#include "../guichanfwd.h" + class Image; /** diff --git a/src/gui/colour.cpp b/src/gui/colour.cpp new file mode 100644 index 00000000..816420ed --- /dev/null +++ b/src/gui/colour.cpp @@ -0,0 +1,139 @@ +/*************************************************************************** + * Copyright (C) 2008 by Douglas Boffey * + * * + * DougABoffey@netscape.net * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed with The Mana Experiment * + * in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include <cstdio> + +#include "colour.h" + +#include "../configuration.h" + +#include "../utils/gettext.h" + +Colour::Colour() +{ + addColour('C', 0x000000, _("Chat")); + addColour('G', 0xff0000, _("GM")); + addColour('Y', 0x1fa052, _("Player")); + addColour('W', 0x0000ff, _("Whisper")); + addColour('I', 0xf1dc27, _("Is")); + addColour('P', 0xff00d8, _("Party")); + addColour('S', 0x8415e2, _("Server")); + addColour('L', 0x919191, _("Logger")); + addColour('<', 0xe50d0d, _("Hyperlink")); + commit(); +} + +Colour::~Colour() +{ + for (ColVector::iterator col = mColVector.begin(), + colEnd = mColVector.end(); + col != colEnd; + ++col) + { + char buffer[20]; + std::sprintf(buffer, "0x%06x", col->rgb); + config.setValue("Colour" + col->text, buffer); + } +} + +void Colour::setColour(const char c, const int rgb) +{ + for (ColVector::iterator col = mColVector.begin(), + colEnd = mColVector.end(); + col != colEnd; + ++col) + { + if (col->ch == c) + { + col->rgb = rgb; + return; + } + } +} + +int Colour::getColour(const char c, bool &valid) const +{ + for (ColVector::const_iterator col = mColVector.begin(), + colEnd = mColVector.end(); + col != colEnd; + ++col) + { + if (col->ch == c) + { + valid = true; + return col->rgb; + } + } + valid = false; + return 0x000000; +} + +std::string Colour::getElementAt(int i) +{ + if (i < 0 || i >= getNumberOfElements()) + { + return ""; + } + return mColVector[i].text; +} + +void Colour::addColour(const char c, const int rgb, const std::string &text) +{ + int trueRgb = config.getValue("Colour" + text, rgb); + mColVector.push_back(ColourElem(c, trueRgb, text)); +} + +int Colour::getColourAt(int i) +{ + if (i < 0 || i >= getNumberOfElements()) + { + return 0; + } + return mColVector[i].rgb; +} + +void Colour::setColourAt(int i, int rgb) +{ + if (i >= 0 && i < getNumberOfElements()) + { + mColVector[i].rgb = rgb; + } +} + +void Colour::commit() +{ + for (ColVector::iterator i = mColVector.begin(), iEnd = mColVector.end(); + i != iEnd; + ++i) + { + i->committedRgb = i->rgb; + } +} + +void Colour::rollback() +{ + for (ColVector::iterator i = mColVector.begin(), iEnd = mColVector.end(); + i != iEnd; + ++i) + { + i->rgb = i->committedRgb; + } +} diff --git a/src/gui/colour.h b/src/gui/colour.h new file mode 100644 index 00000000..1e8ba3db --- /dev/null +++ b/src/gui/colour.h @@ -0,0 +1,133 @@ +/*************************************************************************** + * Copyright (C) 2008 by Douglas Boffey * + * * + * DougABoffey@netscape.net * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed with The Mana Experiment * + * in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef _COLOUR_H +#define _COLOUR_H + +#include <cstdio> +#include <string> +#include <vector> + +#include <guichan/listmodel.hpp> + +#include "../guichanfwd.h" + +class Colour : public gcn::ListModel +{ + public: + /** + * Constructor + */ + Colour(); + + /** + * Destructor + */ + ~Colour(); + + /** + * Define the colour replacement for a character + * + * @param c charater to be replaced + * @param rgb colour to replace character + */ + void setColour(const char c, const int rgb); + + /** + * Define the colour replacement for a character + * + * @param c character to be replaced + * @param r red component + * @param g green component + * @param b blue component + */ + void setColour(const char c, const int r, const int g, const int b) + { + setColour(c, (r << 16) | (g << 8) | b); + } + + /** + * Return the colour associated with a character, if exists + * + * @param c character requested + * @param valid indicate whether character is known + */ + int getColour(const char c, bool &valid) const; + + /** + * Return the number of colours known + */ + int getNumberOfElements() {return mColVector.size(); } + + /** + * Return the name of the ith colour + * + * @param i index of colour interested in + */ + std::string getElementAt(int i); + + /** + * Get the colour for the element at index i in the current colour + * model + */ + int getColourAt(int i); + + /** + * Set the colour for the element at index i + */ + void setColourAt(int i, int rgb); + + /** + * Commit the colours + */ + void commit(); + + /** + * Rollback the colours + */ + void rollback(); + + private: + struct ColourElem + { + ColourElem(const char c, const int rgb, const std::string &text) : + ch(c), rgb(rgb), text(text) {} + char ch; + int rgb; + int committedRgb; + std::string text; + }; + typedef std::vector<ColourElem> ColVector; + ColVector mColVector; + + /** + * Initialise colour + * + * @param c character that needs initialising + * @param rgb default colour if not found in config + * @param text identifier of colour + */ + void addColour(const char c, const int rgb, const std::string &text); +}; + +extern Colour *textColour; + +#endif diff --git a/src/gui/confirm_dialog.cpp b/src/gui/confirm_dialog.cpp index 46b7c971..569fd93f 100644 --- a/src/gui/confirm_dialog.cpp +++ b/src/gui/confirm_dialog.cpp @@ -19,11 +19,9 @@ * 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 "../utils/gettext.h" @@ -31,28 +29,51 @@ ConfirmDialog::ConfirmDialog(const std::string &title, const std::string &msg, Window *parent): Window(title, true, parent) { - gcn::Label *textLabel = new gcn::Label(msg); + mTextBox = new TextBox(); + mTextBox->setEditable(false); + mTextBox->setOpaque(false); + + mTextArea = new ScrollArea(mTextBox); gcn::Button *yesButton = new Button(_("Yes"), "yes", this); gcn::Button *noButton = new Button(_("No"), "no", this); - int w = textLabel->getWidth() + 20; + mTextArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mTextArea->setVerticalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mTextArea->setOpaque(false); + + mTextBox->setMinWidth(260); + mTextBox->setTextWrapped(msg); + + int numRows = mTextBox->getNumberOfRows(); + int width = getFont()->getWidth(title); int inWidth = yesButton->getWidth() + noButton->getWidth() + 5; - int h = textLabel->getHeight() + 25 + yesButton->getHeight(); - if (w < inWidth + 10) { - w = inWidth + 10; + if (numRows > 1) + { + // 15 == height of each line of text (based on font heights) + // 14 == row top + bottom graphic pixel heights + setContentSize(mTextBox->getMinWidth() + 15, 15 + (numRows * 15) + noButton->getHeight()); + mTextArea->setDimension(gcn::Rectangle(4, 5, mTextBox->getMinWidth() + 5, + 3 + (numRows * 14))); + } + else + { + if (width < getFont()->getWidth(msg)) + width = getFont()->getWidth(msg); + if (width < inWidth) + width = inWidth; + setContentSize(width + 15, 30 + noButton->getHeight()); + mTextArea->setDimension(gcn::Rectangle(4, 5, width + 5, 17)); } - setContentSize(w, h); - textLabel->setPosition(10, 10); yesButton->setPosition( - (w - inWidth) / 2, - h - 5 - noButton->getHeight()); + (mTextBox->getMinWidth() - inWidth) / 2, + (numRows * 14) + noButton->getHeight() - 8); noButton->setPosition( yesButton->getX() + yesButton->getWidth() + 5, - h - 5 - noButton->getHeight()); + (numRows * 14) + noButton->getHeight() - 8); - add(textLabel); + add(mTextArea); add(yesButton); add(noButton); @@ -64,6 +85,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..fb8290c8 100644 --- a/src/gui/confirm_dialog.h +++ b/src/gui/confirm_dialog.h @@ -24,8 +24,12 @@ #include <guichan/actionlistener.hpp> +#include "button.h" +#include "scrollarea.h" +#include "textbox.h" #include "window.h" +#include "../guichanfwd.h" /** * An option dialog. @@ -42,10 +46,17 @@ class ConfirmDialog : public Window, public gcn::ActionListener { ConfirmDialog(const std::string &title, const std::string &msg, Window *parent = NULL); + unsigned int getNumRows(); + /** * Called when receiving actions from the widgets. */ void action(const gcn::ActionEvent &event); + + private: + TextBox *mTextBox; + ScrollArea *mTextArea; + gcn::Button *okButton; }; #endif diff --git a/src/gui/connection.cpp b/src/gui/connection.cpp index f73bb74d..15d85bbc 100644 --- a/src/gui/connection.cpp +++ b/src/gui/connection.cpp @@ -19,13 +19,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "connection.h" - #include <guichan/actionlistener.hpp> #include <guichan/widgets/label.hpp> #include "button.h" +#include "connection.h" #include "progressbar.h" #include "../main.h" diff --git a/src/gui/debugwindow.cpp b/src/gui/debugwindow.cpp index 669aabd2..223b7fbd 100644 --- a/src/gui/debugwindow.cpp +++ b/src/gui/debugwindow.cpp @@ -19,13 +19,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "debugwindow.h" - #include <SDL_mouse.h> #include <guichan/widgets/label.hpp> #include "button.h" +#include "debugwindow.h" #include "gui.h" #include "viewport.h" diff --git a/src/gui/emotecontainer.cpp b/src/gui/emotecontainer.cpp new file mode 100644 index 00000000..fa63d4bb --- /dev/null +++ b/src/gui/emotecontainer.cpp @@ -0,0 +1,172 @@ +/* + * Aethyra + * Copyright 2009 Aethyra Development Team + * + * This file is part of Aethyra. + * + * Aethyra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * Aethyra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Aethyra; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <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..8e52a206 --- /dev/null +++ b/src/gui/emotecontainer.h @@ -0,0 +1,138 @@ +/* + * Aethyra + * Copyright 2009 Aethyra Development Team + * + * This file is part of Aethyra. + * + * Aethyra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * Aethyra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Aethyra; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EMOTECONTAINER_H__ +#define EMOTECONTAINER_H__ + +#include <list> +#include <vector> + +#include <guichan/mouselistener.hpp> +#include <guichan/widget.hpp> +#include <guichan/widgetlistener.hpp> + +#include "../guichanfwd.h" + +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..f5ee9843 --- /dev/null +++ b/src/gui/emoteshortcutcontainer.cpp @@ -0,0 +1,195 @@ +/* + * Aethyra + * Copyright 2009 Aethyra Development Team + * + * This file is part of Aethyra. + * + * Aethyra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * Aethyra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Aethyra; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emoteshortcutcontainer.h" + +#include "../animatedsprite.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(): + mEmoteClicked(false), + mEmoteMoved(0) +{ + mGridWidth = 1, + mGridHeight = 1, + addMouseListener(this); + addWidgetListener(this); + + ResourceManager *resman = ResourceManager::getInstance(); + + mBackgroundImg = resman->getImage("graphics/gui/item_shortcut_bgr.png"); + + // 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); + } + } +} + +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..cffaee6f --- /dev/null +++ b/src/gui/emoteshortcutcontainer.h @@ -0,0 +1,81 @@ +/* + * Aethyra + * Copyright 2009 Aethyra Development Team + * + * This file is part of Aethyra. + * + * Aethyra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * Aethyra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Aethyra; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EMOTESHORTCUTCONTAINER_H__ +#define EMOTESHORTCUTCONTAINER_H__ + +#include <vector> + +#include <guichan/mouselistener.hpp> + +#include "shortcutcontainer.h" + +#include "../guichanfwd.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..b4e9c735 --- /dev/null +++ b/src/gui/emotewindow.cpp @@ -0,0 +1,82 @@ +/* + * Aethyra + * Copyright 2009 Aethyra Development Team + * + * This file is part of Aethyra. + * + * Aethyra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * Aethyra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Aethyra; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <string> + +#include <guichan/mouseinput.hpp> + +#include "button.h" +#include "gui.h" +#include "emotewindow.h" +#include "emotecontainer.h" +#include "scrollarea.h" +#include "textbox.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/emotewindow.h b/src/gui/emotewindow.h new file mode 100644 index 00000000..8e36e5ce --- /dev/null +++ b/src/gui/emotewindow.h @@ -0,0 +1,68 @@ +/* + * Aethyra + * Copyright 2009 Aethyra Development Team + * + * This file is part of Aethyra. + * + * Aethyra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * Aethyra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Aethyra; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EMOTEWINDOW_H +#define EMOTEWINDOW_H + +#include <guichan/actionlistener.hpp> +#include <guichan/selectionlistener.hpp> + +#include "window.h" + +#include "../guichanfwd.h" + +class EmoteContainer; +class TextBox; + +/** + * Emote dialog. + * + * \ingroup Interface + */ +class EmoteWindow : public Window, gcn::ActionListener, + gcn::SelectionListener +{ + public: + /** + * Constructor. + */ + EmoteWindow(); + + /** + * Called when receiving actions from the widgets. + */ + void action(const gcn::ActionEvent &event); + + /** + * Returns the selected item. + */ + int getSelectedEmote() const; + + private: + EmoteContainer *mEmotes; + + gcn::Button *mUseButton; + gcn::ScrollArea *mEmoteScroll; +}; + +extern EmoteWindow *emoteWindow; + +#endif diff --git a/src/gui/equipmentwindow.cpp b/src/gui/equipmentwindow.cpp index aa6825f2..1fb2b601 100644 --- a/src/gui/equipmentwindow.cpp +++ b/src/gui/equipmentwindow.cpp @@ -19,14 +19,20 @@ * 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 "playerbox.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 +40,57 @@ #include "../utils/gettext.h" #include "../utils/tostring.h" +// 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(Equipment *equipment): Window(_("Equipment")), - mEquipment(equipment) + mEquipment(equipment), + mSelected(-1) + { + // 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(); + } + mInventory = player_node->getInventory(); } EquipmentWindow::~EquipmentWindow() { + delete mUnequip; } void EquipmentWindow::draw(gcn::Graphics *graphics) @@ -54,37 +98,85 @@ 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) + { + // Set color red. + g->setColor(gcn::Color(255, 0, 0)); + } + else { - static_cast<Graphics*>(graphics)->drawImage( - image, 36 * (i % 4) + 10, 36 * (i / 4) + 25); + // 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)); + } +} + +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; } +} + +void EquipmentWindow::mousePressed(gcn::MouseEvent& mouseEvent) +{ + Window::mousePressed(mouseEvent); - graphics->drawRectangle(gcn::Rectangle(160, 25, 32, 32)); + const int x = mouseEvent.getX(); + const int y = mouseEvent.getY(); - if (!(item = mInventory->getItem(mEquipment->getArrows()))) - return; + Item* item; - image = item->getImage(); - if (image) + // Checks if any of the presses were in the equip boxes. + for (int i = EQUIP_LEGS_SLOT; i < EQUIP_VECTOREND; i++) { - static_cast<Graphics*>(graphics)->drawImage(image, 160, 25); + 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; + } + } } - graphics->drawText(toString(item->getQuantity()), 170, 62, - gcn::Graphics::CENTER); } + diff --git a/src/gui/equipmentwindow.h b/src/gui/equipmentwindow.h index e2420134..0b3aede0 100644 --- a/src/gui/equipmentwindow.h +++ b/src/gui/equipmentwindow.h @@ -22,17 +22,29 @@ #ifndef EQUIPMENTWINDOW_H #define EQUIPMENTWINDOW_H +#include <guichan/actionlistener.hpp> + #include "window.h" -#include "../inventory.h" class Equipment; +class Inventory; +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: /** @@ -50,9 +62,36 @@ 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: Equipment *mEquipment; Inventory *mInventory; + gcn::Button *mUnequip; /**< Button for unequipping. */ + EquipBox mEquipBox[EQUIP_VECTOREND]; /**< Equipment Boxes. */ + + 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..dd605be6 100644 --- a/src/gui/focushandler.cpp +++ b/src/gui/focushandler.cpp @@ -21,7 +21,6 @@ #include "focushandler.h" - void FocusHandler::requestModalFocus(gcn::Widget *widget) { /* If there is another widget with modal focus, remove its modal focus diff --git a/src/gui/gccontainer.h b/src/gui/gccontainer.h index da584a42..0a573645 100644 --- a/src/gui/gccontainer.h +++ b/src/gui/gccontainer.h @@ -26,6 +26,8 @@ #include <guichan/widgets/container.hpp> +#include "../guichanfwd.h" + /** * A garbage collecting container. Childs added to this container are * automatically deleted when the container is deleted. diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 2da451a3..642e916b 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: @@ -115,14 +117,26 @@ Gui::Gui(Graphics *graphics): + e.getMessage()); } + // Set bold font + path = resman->getPath("fonts/dejavusans-bold.ttf"); + try { + const int fontSize = 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 { 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 +172,7 @@ Gui::~Gui() mMouseCursors->decRef(); delete mGuiFont; + delete boldFont; delete viewport; delete getTop(); diff --git a/src/gui/gui.h b/src/gui/gui.h index 95cd5815..9681d44a 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; @@ -125,4 +125,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..6b14f6d8 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" @@ -33,7 +34,8 @@ HelpWindow::HelpWindow(): Window(_("Help")) { setContentSize(455, 350); - setWindowName("Help"); + setWindowName(_("Help")); + setResizable(true); mBrowserBox = new BrowserBox(); mBrowserBox->setOpaque(false); @@ -48,8 +50,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..2ba74c0a 100644 --- a/src/gui/help.h +++ b/src/gui/help.h @@ -24,8 +24,8 @@ #include <guichan/actionlistener.hpp> -#include "window.h" #include "linkhandler.h" +#include "window.h" #include "../guichanfwd.h" diff --git a/src/gui/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..2a913ef6 100644 --- a/src/gui/inttextfield.h +++ b/src/gui/inttextfield.h @@ -24,6 +24,8 @@ #include "textfield.h" +#include "../guichanfwd.h" + /** * TextBox which only accepts numbers as input. */ @@ -58,8 +60,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..58c13910 100644 --- a/src/gui/inventorywindow.cpp +++ b/src/gui/inventorywindow.cpp @@ -19,72 +19,91 @@ * 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 "textbox.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() + 310); + + place(0, 0, mInvenScroll, 7, 4); + place(0, 4, mWeightLabel).setPadding(3); + place(1, 4, mWeightBar, 2); + place(3, 4, mSlotsLabel).setPadding(3); + place(4, 4, mSlotsBar, 2); + place(5, 5, mDropButton); + place(6, 5, mUseButton); + + Layout &layout = getLayout(); + layout.setRowHeight(0, Layout::AUTO_SET); loadWindowState(); + setLocationRelativeTo(getParent()); } void InventoryWindow::logic() @@ -95,15 +114,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 +156,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 +200,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..2c19ce26 100644 --- a/src/gui/inventorywindow.h +++ b/src/gui/inventorywindow.h @@ -28,9 +28,12 @@ #include "window.h" #include "../guichanfwd.h" +#include "../localplayer.h" class Item; class ItemContainer; +class ProgressBar; +class TextBox; /** * Inventory dialog. @@ -38,13 +41,13 @@ class ItemContainer; * \ingroup Interface */ class InventoryWindow : public Window, gcn::ActionListener, - gcn::SelectionListener + gcn::SelectionListener { public: /** * Constructor. */ - InventoryWindow(); + InventoryWindow(int invSize = (INVENTORY_SIZE - 2)); /** * Logic (updates buttons and weight information). @@ -61,30 +64,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..d8682c95 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" diff --git a/src/gui/itemcontainer.cpp b/src/gui/itemcontainer.cpp index a0885279..e3ecdd4e 100644 --- a/src/gui/itemcontainer.cpp +++ b/src/gui/itemcontainer.cpp @@ -21,13 +21,18 @@ #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" @@ -41,11 +46,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"); @@ -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); @@ -138,6 +147,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 +169,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 +180,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 +190,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 +231,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 +249,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..1fa76343 100644 --- a/src/gui/itemcontainer.h +++ b/src/gui/itemcontainer.h @@ -22,15 +22,18 @@ #ifndef ITEMCONTAINER_H #define ITEMCONTAINER_H +#include <list> + #include <guichan/mouselistener.hpp> #include <guichan/widget.hpp> #include <guichan/widgetlistener.hpp> -#include <list> +#include "../guichanfwd.h" class Image; class Inventory; class Item; +class ItemPopup; namespace gcn { class SelectionListener; @@ -41,14 +44,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 +108,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 +132,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..34c12a0c --- /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 <SDL_mouse.h> + +#include "itemlinkhandler.h" +#include "itempopup.h" + +#include "../resources/iteminfo.h" +#include "../resources/itemdb.h" + +#include <sstream> +#include <string> + +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..8235d640 --- /dev/null +++ b/src/gui/itempopup.cpp @@ -0,0 +1,176 @@ +/* + * 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 "windowcontainer.h" + +#include "widgets/layout.h" + +#include "../resources/image.h" +#include "../resources/iteminfo.h" +#include "../resources/resourcemanager.h" + +#include "../utils/gettext.h" +#include "../utils/strprintf.h" +#include "../utils/tostring.h" + +ItemPopup::ItemPopup(): + Window() +{ + setResizable(false); + setShowTitle(false); + setTitleBarHeight(0); + + // Item Name + mItemName = new gcn::Label("Label"); + mItemName->setFont(gui->getFont()); + mItemName->setPosition(2, 2); + mItemName->setWidth(getWidth() - 4); + mItemName->setFont(boldFont); + + // Item Description + mItemDesc = new TextBox(); + mItemDesc->setEditable(false); + mItemDesc->setMinWidth(186); + mItemDesc->setTextWrapped(""); + 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); + mItemEffect->setMinWidth(186); + mItemEffect->setTextWrapped(""); + 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); + mItemWeight->setMinWidth(186); + mItemWeight->setTextWrapped(""); + 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()); + + // LEEOR / TODO: This causes an exception error. + //moveToBottom(getParent()); +} + +void ItemPopup::setItem(const ItemInfo &item) +{ + const gcn::Rectangle &area = getChildrenArea(); + const int width = area.width; + + mItemDesc->setMinWidth(width - 10); + mItemEffect->setMinWidth(width - 10); + mItemWeight->setMinWidth(width - 10); + + mItemName->setCaption(item.getName()); + mItemDesc->setTextWrapped(item.getDescription()); + mItemEffect->setTextWrapped(item.getEffect()); + mItemWeight->setTextWrapped(_("Weight: ") + toString(item.getWeight()) + + _(" grams")); + + int numRowsDesc = mItemDesc->getNumberOfRows(); + int numRowsEffect = mItemEffect->getNumberOfRows(); + int numRowsWeight = mItemWeight->getNumberOfRows(); + + mItemDescScroll->setDimension(gcn::Rectangle(2, 0, 196, + numRowsDesc * getFont()->getHeight())); + + mItemEffectScroll->setDimension(gcn::Rectangle(2, 0, 196, + numRowsEffect * getFont()->getHeight())); + + mItemWeightScroll->setDimension(gcn::Rectangle(2, 0, 196, + numRowsWeight * getFont()->getHeight())); + + if(item.getEffect() == "") + { + setContentSize(200, (numRowsDesc * getFont()->getHeight() + + (3 * getFont()->getHeight()))); + + mItemWeightScroll->setPosition(2, + (numRowsDesc * getFont()->getHeight()) + + (2 * getFont()->getHeight())); + } + else + { + setContentSize(200, (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())); +} + +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() - 5) < 0) + y = 0; + else + y = y - getHeight() - 5; + setPosition(x, y); + setVisible(true); + requestMoveToTop(); +} diff --git a/src/gui/buddywindow.h b/src/gui/itempopup.h index 4eed3a2c..b6120af8 100644 --- a/src/gui/buddywindow.h +++ b/src/gui/itempopup.h @@ -1,6 +1,7 @@ /* * The Mana World - * Copyright (C) 2004 The Mana World Development Team + * 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. * @@ -19,38 +20,32 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef BUDDYWINDOW_H -#define BUDDYWINDOW_H - -#include <guichan/actionlistener.hpp> +#ifndef ITEMPOPUP_H__ +#define ITEMPOPUP_H__ +#include "scrollarea.h" +#include "textbox.h" #include "window.h" -#include "../guichanfwd.h" - -class BuddyList; +#include "../item.h" -/** - * Window showing buddy list. - * - * \ingroup Interface - */ -class BuddyWindow : public Window, public gcn::ActionListener +class ItemPopup : public Window { public: - /** - * Constructor. - */ - BuddyWindow(); + ItemPopup(); - /** - * Performs action. - */ - void action(const gcn::ActionEvent &event); + void setItem(const ItemInfo &item); + unsigned int getNumRows(); + void view(int x, int y); private: - BuddyList *mBuddyList; - gcn::ListBox *mListbox; + gcn::Label *mItemName; + TextBox *mItemDesc; + TextBox *mItemEffect; + TextBox *mItemWeight; + ScrollArea *mItemDescScroll; + ScrollArea *mItemEffectScroll; + ScrollArea *mItemWeightScroll; }; -#endif /* BUDDYWINDOW_H */ +#endif // ITEMPOPUP_H__ diff --git a/src/gui/itemshortcutcontainer.cpp b/src/gui/itemshortcutcontainer.cpp index b2f70348..3735afe2 100644 --- a/src/gui/itemshortcutcontainer.cpp +++ b/src/gui/itemshortcutcontainer.cpp @@ -18,15 +18,20 @@ * 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 "gui.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,21 +39,25 @@ #include "../utils/tostring.h" ItemShortcutContainer::ItemShortcutContainer(): - mGridWidth(1), - mGridHeight(1), mItemClicked(false), mItemMoved(NULL) { + mGridWidth=1; + mGridHeight=1; 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() @@ -56,8 +65,7 @@ ItemShortcutContainer::~ItemShortcutContainer() mBackgroundImg->decRef(); } -void -ItemShortcutContainer::logic() +void ItemShortcutContainer::logic() { gcn::Widget::logic(); @@ -70,8 +78,7 @@ ItemShortcutContainer::logic() } } -void -ItemShortcutContainer::draw(gcn::Graphics *graphics) +void ItemShortcutContainer::draw(gcn::Graphics *graphics) { Graphics *g = static_cast<Graphics*>(graphics); @@ -87,7 +94,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 +105,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 = @@ -129,35 +139,23 @@ ItemShortcutContainer::draw(gcn::Graphics *graphics) } } -void ItemShortcutContainer::widgetResized(const gcn::Event &event) +void ItemShortcutContainer::mouseDragged(gcn::MouseEvent &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; - } -} - -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 +169,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 +226,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..d6a04d7b 100644 --- a/src/gui/itemshortcutcontainer.h +++ b/src/gui/itemshortcutcontainer.h @@ -23,20 +23,21 @@ #define ITEMSHORTCUTCONTAINER_H #include <guichan/mouselistener.hpp> -#include <guichan/widget.hpp> -#include <guichan/widgetlistener.hpp> + +#include "shortcutcontainer.h" + +#include "../guichanfwd.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 +61,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 +75,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; - - Image *mBackgroundImg; + void mouseExited(gcn::MouseEvent &event); + void mouseMoved(gcn::MouseEvent &event); - 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..990a0ade 100644 --- a/src/gui/listbox.cpp +++ b/src/gui/listbox.cpp @@ -19,13 +19,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "listbox.h" - #include <guichan/font.hpp> #include <guichan/graphics.hpp> #include <guichan/listmodel.hpp> #include <guichan/mouseinput.hpp> +#include "listbox.h" + ListBox::ListBox(gcn::ListModel *listModel): gcn::ListBox(listModel) { @@ -36,7 +36,7 @@ void ListBox::draw(gcn::Graphics *graphics) if (!mListModel) return; - graphics->setColor(gcn::Color(110, 160, 255)); + graphics->setColor(gcn::Color(235, 200, 115)); graphics->setFont(getFont()); int fontHeight = getFont()->getHeight(); @@ -57,8 +57,7 @@ void ListBox::draw(gcn::Graphics *graphics) } } -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..3d0062bc 100644 --- a/src/gui/listbox.h +++ b/src/gui/listbox.h @@ -24,6 +24,8 @@ #include <guichan/widgets/listbox.hpp> +#include "../guichanfwd.h" + class SelectionListener; /** diff --git a/src/gui/login.cpp b/src/gui/login.cpp index 2b87f6df..8de2867c 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 <vector> #include <guichan/widgets/label.hpp> -#include "../main.h" -#include "../logindata.h" - #include "button.h" #include "checkbox.h" +#include "login.h" #include "ok_dialog.h" #include "passwordfield.h" #include "textfield.h" #include "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,64 @@ 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"); + dfltServer.push_back("server.themanaworld.org"); + std::vector<std::string> dfltPort; + dfltPort.push_back("21001"); + dfltPort.push_back("22001"); + dfltPort.push_back("21001"); + mServerList = new DropDownList("MostRecent00", dfltServer, dfltPort, + MAX_SERVER_LIST_SIZE); + mServerListBox = new gcn::ListBox(mServerList); + mServerScrollArea = new ScrollArea(); + mUserField = new TextField(mLoginData->username); mPassField = new PasswordField(mLoginData->password); - mServerField = new TextField(mLoginData->hostname); + mServerField = new TextField(mServerList->getServerAt(0)); + mPortField = new TextField(mServerList->getPortAt(0)); + mServerDropDown = new DropDown(mServerList, + mServerScrollArea, + mServerListBox); + mKeepCheck = new CheckBox(_("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 +125,9 @@ LoginDialog::LoginDialog(LoginData *loginData): LoginDialog::~LoginDialog() { + delete mServerList; + delete mServerListBox; + delete mServerScrollArea; } void LoginDialog::action(const gcn::ActionEvent &event) @@ -97,6 +135,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 +143,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 +160,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 +185,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 == "") + { + return false; + } + unsigned long l = 0; + for (std::string::const_iterator strPtr = str.begin(), strEnd = str.end(); + strPtr != strEnd; ++strPtr) + { + if (*strPtr < '0' || *strPtr > '9') + { + return false; + } + l = l * 10 + (*strPtr - '0'); // *strPtr - '0' will never be negative + if (l > 65535) + { + return false; + } + } + return true; +} + +unsigned short LoginDialog::getUShort(const std::string &str) +{ + unsigned long l = 0; + for (std::string::const_iterator strPtr = str.begin(), strEnd = str.end(); + strPtr != strEnd; ++strPtr) + { + l = l * 10 + (*strPtr - '0'); + } + return static_cast<unsigned short>(l); +} + +/** + * LoginDialog::DropDownList + */ + +void LoginDialog::DropDownList::saveEntry(const std::string &server, + const std::string &port, int &saved) +{ + if (saved < MAX_SERVER_LIST_SIZE && server != "") + { + config.setValue(mConfigPrefix + "Server" + toString(saved), server); + config.setValue(mConfigPrefix + "Port" + toString(saved), port); + ++saved; + } +} + +LoginDialog::DropDownList::DropDownList(std::string prefix, + std::vector<std::string> dflt, + std::vector<std::string> dfltPort, + int maxEntries) : + mConfigPrefix(prefix), + mMaxEntries(maxEntries) +{ + for (int i = 0; i < maxEntries; ++i) + { + std::string server = config.getValue(mConfigPrefix + "Server" + + toString(i), ""); + if (server == "") // Just in case had original config entries + { + server = config.getValue(mConfigPrefix + "ServerList" + + toString(i), ""); + } + std::string port = config.getValue(mConfigPrefix + "Port" + + toString(i), dfltPort.front()); + + if (server != "") + { + mServers.push_back(server); + mPorts.push_back(port); + } + } + if (mServers.size() == 0) + { + mServers.assign(dflt.begin(), dflt.end()); + mPorts.assign(dfltPort.begin(), dfltPort.end()); + } +} + +void LoginDialog::DropDownList::save(const std::string &server, + const std::string &port) +{ + int position = 0; + saveEntry(server, port, position); + for (std::vector<std::string>::const_iterator sPtr = mServers.begin(), + sEnd = mServers.end(), + pPtr = mPorts.begin(), + pEnd = mPorts.end(); + sPtr != sEnd && pPtr != pEnd; + ++sPtr, ++pPtr) + { + if (*sPtr != server || *pPtr != port) + { + saveEntry(*sPtr, *pPtr, position); + } + } +} + +int LoginDialog::DropDownList::getNumberOfElements() +{ + return mServers.size(); +} + +std::string LoginDialog::DropDownList::getElementAt(int i) +{ + if (i < 0 || i >= getNumberOfElements()) + { + return ""; + } + return getServerAt(i) + ":" + getPortAt(i); +} + +std::string LoginDialog::DropDownList::getServerAt(int i) +{ + if (i < 0 || i >= getNumberOfElements()) + { + return ""; + } + return mServers.at(i); +} + +std::string LoginDialog::DropDownList::getPortAt(int i) +{ + if (i < 0 || i >= getNumberOfElements()) + { + return ""; + } + return mPorts.at(i); +} diff --git a/src/gui/login.h b/src/gui/login.h index 7dd59a96..b85e5ae1 100644 --- a/src/gui/login.h +++ b/src/gui/login.h @@ -23,10 +23,17 @@ #define LOGIN_H #include <iosfwd> +#include <string> +#include <vector> + #include <guichan/actionlistener.hpp> #include <guichan/keylistener.hpp> +#include "scrollarea.h" #include "window.h" + +#include "widgets/dropdown.h" + #include "../guichanfwd.h" class LoginData; @@ -67,18 +74,66 @@ 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; + 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/minimap.cpp b/src/gui/minimap.cpp index f5c0c1e6..7802b583 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,13 +33,17 @@ #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() + "Visible", true); setDefaultSize(5, 25, 100, 100); - loadWindowState(); + setResizable(true); } Minimap::~Minimap() @@ -53,13 +60,57 @@ 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() + offsetX); + setMaxHeight(mMapImage->getHeight() + offsetY); + + mMapImage->setAlpha(config.getValue("guialpha", 0.8)); + + // Set content size to be within the minimum and maximum boundaries + setWidth(getMinWidth() < getWidth() ? getWidth() : getMinWidth()); + if (getMaxWidth() > getWidth()) + setWidth(getMaxWidth()); + setHeight(getMinHeight() < getHeight() ? getHeight() : getMinHeight()); + if (getMaxHeight() > getHeight()) + setHeight(getMaxHeight()); + + setDefaultSize(getX(), getY(), getWidth(), getHeight()); + resetToDefaultSize(); + + loadWindowState(); + setVisible(mShow); + } + else + { + setVisible(false); + } +} + +void Minimap::toggle() +{ + mShow = !mShow; + config.setValue(getWindowName() + "Visible", 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 +123,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 +138,7 @@ void Minimap::draw(gcn::Graphics *graphics) if (mapOriginY > 0) mapOriginY = 0; } + static_cast<Graphics*>(graphics)-> drawImage(mMapImage, mapOriginX, mapOriginY); } @@ -122,10 +174,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..b4574ad5 100644 --- a/src/gui/minimap.h +++ b/src/gui/minimap.h @@ -24,6 +24,8 @@ #include "window.h" +#include "../guichanfwd.h" + class Image; /** @@ -50,12 +52,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..915db961 100644 --- a/src/gui/ministatus.cpp +++ b/src/gui/ministatus.cpp @@ -19,14 +19,13 @@ * 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 "../configuration.h" #include "../graphics.h" +#include "../localplayer.h" #include "../utils/tostring.h" @@ -90,24 +89,22 @@ 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) diff --git a/src/gui/npc_text.cpp b/src/gui/npc_text.cpp index 6ad698bc..a2e043d1 100644 --- a/src/gui/npc_text.cpp +++ b/src/gui/npc_text.cpp @@ -19,14 +19,15 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "npc_text.h" - #include <string> +#include "npc_text.h" #include "browserbox.h" #include "button.h" #include "scrollarea.h" +#include "widgets/layout.h" + #include "../npc.h" #include "../utils/gettext.h" @@ -39,24 +40,24 @@ NpcTextDialog::NpcTextDialog(): setMinWidth(200); setMinHeight(150); + setDefaultSize(0, 0, 260, 200); + mBrowserBox = new BrowserBox(BrowserBox::AUTO_WRAP); - mBrowserBox->setOpaque(true); + mBrowserBox->setOpaque(false); scrollArea = new ScrollArea(mBrowserBox); 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); + + Layout &layout = getLayout(); + layout.setRowHeight(0, Layout::AUTO_SET); + loadWindowState(); setLocationRelativeTo(getParent()); } @@ -76,21 +77,6 @@ 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()); -} - void NpcTextDialog::action(const gcn::ActionEvent &event) { if (event.getId() == "ok") diff --git a/src/gui/npc_text.h b/src/gui/npc_text.h index c881467a..b4b6f1af 100644 --- a/src/gui/npc_text.h +++ b/src/gui/npc_text.h @@ -23,8 +23,10 @@ #define NPC_TEXT_H #include <iosfwd> + #include <guichan/actionlistener.hpp> +#include "scrollarea.h" #include "window.h" class BrowserBox; @@ -45,13 +47,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); diff --git a/src/gui/npcintegerdialog.cpp b/src/gui/npcintegerdialog.cpp index ec91d736..65a1a7f1 100644 --- a/src/gui/npcintegerdialog.cpp +++ b/src/gui/npcintegerdialog.cpp @@ -107,5 +107,6 @@ void NpcIntegerDialog::action(const gcn::ActionEvent &event) setVisible(false); current_npc->integerInput(mValueField->getValue()); current_npc = 0; + mValueField->reset(); } } diff --git a/src/gui/npclistdialog.cpp b/src/gui/npclistdialog.cpp index 4b05df5a..bb815680 100644 --- a/src/gui/npclistdialog.cpp +++ b/src/gui/npclistdialog.cpp @@ -19,13 +19,12 @@ * 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 "widgets/layout.h" #include "../npc.h" @@ -39,6 +38,8 @@ NpcListDialog::NpcListDialog(): setMinWidth(200); setMinHeight(150); + setDefaultSize(0, 0, 260, 200); + mItemList = new ListBox(this); scrollArea = new ScrollArea(mItemList); okButton = new Button(_("OK"), "ok", this); @@ -46,23 +47,15 @@ NpcListDialog::NpcListDialog(): 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"); + place(0, 0, scrollArea, 5).setPadding(3); + place(3, 1, okButton); + place(4, 1, cancelButton); - mItemList->addActionListener(this); - - add(scrollArea); - add(okButton); - add(cancelButton); + Layout &layout = getLayout(); + layout.setRowHeight(0, Layout::AUTO_SET); + loadWindowState(); setLocationRelativeTo(getParent()); } @@ -90,24 +83,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..ffeced3d 100644 --- a/src/gui/npclistdialog.h +++ b/src/gui/npclistdialog.h @@ -28,6 +28,8 @@ #include <guichan/actionlistener.hpp> #include <guichan/listmodel.hpp> +#include "button.h" +#include "scrollarea.h" #include "window.h" #include "../guichanfwd.h" @@ -49,13 +51,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..ccb3c411 100644 --- a/src/gui/npcstringdialog.cpp +++ b/src/gui/npcstringdialog.cpp @@ -42,8 +42,8 @@ NpcStringDialog::NpcStringDialog(): 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,6 +69,7 @@ void NpcStringDialog::action(const gcn::ActionEvent &event) setVisible(false); current_npc->stringInput(mValueField->getText()); current_npc = 0; + mValueField->setText(""); } bool NpcStringDialog::isInputFocused() diff --git a/src/gui/ok_dialog.cpp b/src/gui/ok_dialog.cpp index a2134d5d..dc66a900 100644 --- a/src/gui/ok_dialog.cpp +++ b/src/gui/ok_dialog.cpp @@ -20,9 +20,8 @@ */ #include "ok_dialog.h" -#include "textbox.h" -#include "button.h" -#include "scrollarea.h" + +#include <guichan/font.hpp> #include "../utils/gettext.h" @@ -30,25 +29,45 @@ OkDialog::OkDialog(const std::string &title, const std::string &msg, Window *parent): Window(title, true, parent) { - TextBox *textBox = new TextBox(); - textBox->setEditable(false); + mTextBox = new TextBox(); + mTextBox->setEditable(false); + mTextBox->setOpaque(false); + + mTextArea = new ScrollArea(mTextBox); + okButton = new Button(_("Ok"), "ok", this); + + mTextArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mTextArea->setVerticalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mTextArea->setOpaque(false); - gcn::ScrollArea *scrollArea = new ScrollArea(textBox); - gcn::Button *okButton = new Button(_("Ok"), "ok", this); + mTextBox->setMinWidth(260); + mTextBox->setTextWrapped(msg); - setContentSize(260, 175); - scrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); - scrollArea->setVerticalScrollPolicy(gcn::ScrollArea::SHOW_ALWAYS); - scrollArea->setDimension(gcn::Rectangle( - 5, 5, 250, 160 - okButton->getHeight())); + int numRows = mTextBox->getNumberOfRows(); - textBox->setTextWrapped(msg); + if (numRows > 1) + { + // 15 == height of each line of text (based on font heights) + // 14 == row top + bottom graphic pixel heights + setContentSize(mTextBox->getMinWidth() + 15, 15 + (numRows * 15) + okButton->getHeight()); + mTextArea->setDimension(gcn::Rectangle(4, 5, mTextBox->getMinWidth() + 5, + 3 + (numRows * 14))); + } + else + { + int width = getFont()->getWidth(title); + if (width < getFont()->getWidth(msg)) + width = getFont()->getWidth(msg); + if (width < okButton->getWidth()) + width = okButton->getWidth(); + setContentSize(width + 15, 30 + okButton->getHeight()); + mTextArea->setDimension(gcn::Rectangle(4, 5, width + 5, 17)); + } - okButton->setPosition( - 260 - 5 - okButton->getWidth(), - 175 - 5 - okButton->getHeight()); + okButton->setPosition((mTextBox->getMinWidth() - okButton->getWidth()) / 2, + (numRows * 14) + okButton->getHeight() - 8); - add(scrollArea); + add(mTextArea); add(okButton); setLocationRelativeTo(getParent()); @@ -56,6 +75,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..78b3d44f 100644 --- a/src/gui/ok_dialog.h +++ b/src/gui/ok_dialog.h @@ -24,8 +24,13 @@ #include <guichan/actionlistener.hpp> +#include "button.h" +#include "scrollarea.h" +#include "textbox.h" #include "window.h" +#include "../guichanfwd.h" + /** * An 'Ok' button dialog. * @@ -41,10 +46,17 @@ class OkDialog : public Window, public gcn::ActionListener { OkDialog(const std::string &title, const std::string &msg, Window *parent = NULL); + unsigned int getNumRows(); + /** * Called when receiving actions from the widgets. */ void action(const gcn::ActionEvent &event); + + private: + TextBox *mTextBox; + ScrollArea *mTextArea; + gcn::Button *okButton; }; #endif diff --git a/src/gui/passwordfield.h b/src/gui/passwordfield.h index d6082e3f..e01bedbd 100644 --- a/src/gui/passwordfield.h +++ b/src/gui/passwordfield.h @@ -24,12 +24,15 @@ #include "textfield.h" +#include "../guichanfwd.h" + /** * A password field. * * \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..c22d407c 100644 --- a/src/gui/playerbox.cpp +++ b/src/gui/playerbox.cpp @@ -23,8 +23,10 @@ #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" @@ -54,6 +56,7 @@ PlayerBox::PlayerBox(const Player *player): bggridx[x], bggridy[y], bggridx[x + 1] - bggridx[x] + 1, bggridy[y + 1] - bggridy[y] + 1); + background.grid[a]->setAlpha(config.getValue("guialpha", 0.8)); a++; } } @@ -83,7 +86,13 @@ 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) != NULL) + { + mPlayer->getSprite(i)->draw(static_cast<Graphics*>(graphics), x, y); + } + } } } diff --git a/src/gui/playerbox.h b/src/gui/playerbox.h index d66db4d4..5aacd26f 100644 --- a/src/gui/playerbox.h +++ b/src/gui/playerbox.h @@ -24,6 +24,8 @@ #include <guichan/widgets/scrollarea.hpp> +#include "../guichanfwd.h" + class ImageRect; class Player; @@ -51,8 +53,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. diff --git a/src/gui/popupmenu.cpp b/src/gui/popupmenu.cpp index 06a2ad87..9b0c2370 100644 --- a/src/gui/popupmenu.cpp +++ b/src/gui/popupmenu.cpp @@ -19,16 +19,16 @@ * 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,8 +39,11 @@ #include "../npc.h" #include "../player_relations.h" -#include "../resources/iteminfo.h" +#include "../net/messageout.h" +#include "../net/protocol.h" + #include "../resources/itemdb.h" +#include "../resources/iteminfo.h" #include "../utils/gettext.h" #include "../utils/strprintf.h" @@ -72,51 +75,53 @@ void PopupMenu::showPopup(int x, int y, Being *being) switch (being->getType()) { - case Being::PLAYER: - { - // Players can be traded with. Later also attack, follow and - // add as buddy will be options in this menu. - const std::string &name = being->getName(); - 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; - } - - //mBrowserBox->addRow("@@follow|Follow " + name + "@@"); - //mBrowserBox->addRow("@@buddy|Add " + name + " to Buddy List@@"); - } - break; - - case Being::NPC: - // NPCs can be talked to (single option, candidate for removal - // unless more options would be added) - mBrowserBox->addRow(_("@@talk|Talk To NPC@@")); - break; - - default: - /* Other beings aren't interesting... */ - break; + case Being::PLAYER: + { + // Players can be traded with. Later also attack, follow and + // add as buddy will be options in this menu. + const std::string &name = being->getName(); + mBrowserBox->addRow(_("@@trade|Trade With ") + name + "@@"); + mBrowserBox->addRow(_("@@attack|Attack ") + name + "@@"); + + mBrowserBox->addRow("##3---"); + + switch (player_relations.getRelation(name)) { + case PlayerRelation::NEUTRAL: + mBrowserBox->addRow(_("@@friend|Befriend ") + name + "@@"); + + case PlayerRelation::FRIEND: + mBrowserBox->addRow(_("@@disregard|Disregard ") + name + "@@"); + mBrowserBox->addRow(_("@@ignore|Ignore ") + name + "@@"); + break; + + case PlayerRelation::DISREGARDED: + mBrowserBox->addRow(_("@@unignore|Un-Ignore ") + name + "@@"); + mBrowserBox->addRow(_("@@ignore|Completely ignore ") + name + "@@"); + break; + + case PlayerRelation::IGNORED: + mBrowserBox->addRow(_("@@unignore|Un-Ignore ") + name + "@@"); + break; + } + + //mBrowserBox->addRow(_("@@follow|Follow ") + name + "@@"); + //mBrowserBox->addRow(_("@@buddy|Add ") + name + " to Buddy List@@"); + + mBrowserBox->addRow("##3---"); + mBrowserBox->addRow(_("@@party-invite|Invite ") + name + + " to party@@"); + } + break; + + case Being::NPC: + // NPCs can be talked to (single option, candidate for removal + // unless more options would be added) + mBrowserBox->addRow(_("@@talk|Talk To NPC@@")); + break; + + default: + /* Other beings aren't interesting... */ + break; } //browserBox->addRow("@@look|Look To@@"); @@ -148,28 +153,28 @@ void PopupMenu::handleLink(const std::string& link) // Talk To action if (link == "talk" && - being != NULL && - being->getType() == Being::NPC && - current_npc == 0) + being != NULL && + being->getType() == Being::NPC && + current_npc == 0) { - dynamic_cast<NPC*>(being)->talk(); + dynamic_cast<NPC*>(being)->talk(); } // Trade action else if (link == "trade" && - being != NULL && - being->getType() == Being::PLAYER) + being != NULL && + being->getType() == Being::PLAYER) { - player_node->trade(being); - tradePartnerName = being->getName(); + player_node->trade(being); + tradePartnerName = being->getName(); } // Attack action else if (link == "attack" && - being != NULL && - being->getType() == Being::PLAYER) + being != NULL && + being->getType() == Being::PLAYER) { - player_node->attack(being, true); + player_node->attack(being, true); } else if (link == "unignore" && @@ -194,10 +199,10 @@ void PopupMenu::handleLink(const std::string& link) } else if (link == "friend" && - being != NULL && - being->getType() == Being::PLAYER) + being != NULL && + being->getType() == Being::PLAYER) { - player_relations.setRelation(being->getName(), PlayerRelation::FRIEND); + player_relations.setRelation(being->getName(), PlayerRelation::FRIEND); } /* @@ -210,16 +215,16 @@ void PopupMenu::handleLink(const std::string& link) // Add Buddy action else if ((link == "buddy") && being != NULL && being->isPlayer()) { - if (!buddyWindow->isVisible()) - buddyWindow->setVisible(true); + if (!buddyWindow->isVisible()) + buddyWindow->setVisible(true); - buddyWindow->addBuddy(being->getName()); + buddyWindow->addBuddy(being->getName()); }*/ // Pick Up Floor Item action else if ((link == "pickup") && mFloorItem != NULL) { - player_node->pickUp(mFloorItem); + player_node->pickUp(mFloorItem); } // Look To action @@ -229,39 +234,46 @@ void PopupMenu::handleLink(const std::string& link) else if (link == "use") { - assert(mItem); - if (mItem->isEquipment()) - { - if (mItem->isEquipped()) - { - player_node->unequipItem(mItem); - } - else - { - player_node->equipItem(mItem); - } - } - else - { - player_node->useItem(mItem); - } + assert(mItem); + if (mItem->isEquipment()) + { + if (mItem->isEquipped()) + { + player_node->unequipItem(mItem); + } + else + { + player_node->equipItem(mItem); + } + } + else + { + player_node->useItem(mItem); + } } - else if (link == "drop") + else if (link == "chat") { - new ItemAmountWindow(AMOUNT_ITEM_DROP, inventoryWindow, mItem); + chatWindow->addItemText(mItem->getId(), mItem->getInfo().getName()); } - else if (link == "description") + else if (link == "drop") + { + new ItemAmountWindow(AMOUNT_ITEM_DROP, inventoryWindow, mItem); + } + else if (link == "party-invite" && + being != NULL && + 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 else { - std::cout << link << std::endl; + std::cout << link << std::endl; } setVisible(false); @@ -288,7 +300,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@@")); @@ -299,9 +311,9 @@ void PopupMenu::showPopup(int x, int y) { setContentSize(mBrowserBox->getWidth() + 8, mBrowserBox->getHeight() + 8); if (windowContainer->getWidth() < (x + getWidth() + 5)) - x = windowContainer->getWidth() - getWidth(); + x = windowContainer->getWidth() - getWidth(); if (windowContainer->getHeight() < (y + getHeight() + 5)) - y = windowContainer->getHeight() - getHeight(); + y = windowContainer->getHeight() - getHeight(); setPosition(x, y); setVisible(true); requestMoveToTop(); diff --git a/src/gui/popupmenu.h b/src/gui/popupmenu.h index 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..ecc0017d 100644 --- a/src/gui/progressbar.cpp +++ b/src/gui/progressbar.cpp @@ -19,6 +19,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "gui.h" #include "progressbar.h" #include "../graphics.h" @@ -105,7 +106,7 @@ 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; diff --git a/src/gui/progressbar.h b/src/gui/progressbar.h index a4b30b04..ee0a5f81 100644 --- a/src/gui/progressbar.h +++ b/src/gui/progressbar.h @@ -25,9 +25,10 @@ #include <guichan/widget.hpp> #include <SDL_types.h> - #include <string> +#include "../guichanfwd.h" + class ImageRect; /** @@ -81,12 +82,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; } diff --git a/src/gui/radiobutton.h b/src/gui/radiobutton.h index 2d2bdbb7..dcd62802 100644 --- a/src/gui/radiobutton.h +++ b/src/gui/radiobutton.h @@ -24,8 +24,9 @@ #include <guichan/widgets/radiobutton.hpp> -class Image; +#include "../guichanfwd.h" +class Image; /* * Guichan based RadioButton with custom look diff --git a/src/gui/register.cpp b/src/gui/register.cpp index 5605ef96..9c337d9e 100644 --- a/src/gui/register.cpp +++ b/src/gui/register.cpp @@ -19,8 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "register.h" - #include <string> #include <sstream> @@ -34,10 +32,13 @@ #include "button.h" #include "checkbox.h" #include "login.h" +#include "ok_dialog.h" #include "passwordfield.h" #include "radiobutton.h" +#include "register.h" #include "textfield.h" -#include "ok_dialog.h" + +#include "../utils/tostring.h" #include "widgets/layout.h" @@ -80,10 +81,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 +100,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 +115,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 +126,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); @@ -147,7 +156,7 @@ void RegisterDialog::action(const gcn::ActionEvent &event) else if (event.getId() == "register" && canSubmit()) { const std::string user = mUserField->getText(); - logger->log("RegisterDialog::register Username is %s", user.c_str()); + logger->log(_("RegisterDialog::register Username is %s"), user.c_str()); std::string errorMsg; int error = 0; @@ -216,7 +225,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 +247,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 == "") + { + 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..4b95a07b 100644 --- a/src/gui/register.h +++ b/src/gui/register.h @@ -23,10 +23,12 @@ #define REGISTER_H #include <iosfwd> + #include <guichan/actionlistener.hpp> #include <guichan/keylistener.hpp> #include "window.h" + #include "../guichanfwd.h" class LoginData; @@ -72,10 +74,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..1d7f8472 100644 --- a/src/gui/scrollarea.cpp +++ b/src/gui/scrollarea.cpp @@ -23,6 +23,7 @@ #include "scrollarea.h" +#include "../configuration.h" #include "../graphics.h" #include "../resources/image.h" @@ -94,6 +95,7 @@ void ScrollArea::init() bggridx[x], bggridy[y], bggridx[x + 1] - bggridx[x] + 1, bggridy[y + 1] - bggridy[y] + 1); + background.grid[a]->setAlpha(config.getValue("guialpha", 0.8)); a++; } } @@ -106,12 +108,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++; } } @@ -197,7 +202,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 +213,12 @@ void ScrollArea::setOpaque(bool opaque) { mOpaque = opaque; - if (mOpaque) { + if (mOpaque) + { setFrameSize(2); } - else { + else + { setFrameSize(0); } } @@ -220,7 +228,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..4fababfa 100644 --- a/src/gui/scrollarea.h +++ b/src/gui/scrollarea.h @@ -24,6 +24,8 @@ #include <guichan/widgets/scrollarea.hpp> +#include "../guichanfwd.h" + class Image; class ImageRect; diff --git a/src/gui/sell.cpp b/src/gui/sell.cpp index cc6f02d5..7976e32e 100644 --- a/src/gui/sell.cpp +++ b/src/gui/sell.cpp @@ -19,8 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "sell.h" - #include <cassert> #include <guichan/widgets/label.hpp> @@ -28,6 +26,7 @@ #include "button.h" #include "shoplistbox.h" #include "scrollarea.h" +#include "sell.h" #include "shop.h" #include "slider.h" @@ -36,13 +35,14 @@ #include "../item.h" #include "../npc.h" -#include "../resources/iteminfo.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" SellDialog::SellDialog(Network *network): Window(_("Sell")), diff --git a/src/gui/setup.cpp b/src/gui/setup.cpp index d38fb2e3..d18f365c 100644 --- a/src/gui/setup.cpp +++ b/src/gui/setup.cpp @@ -19,31 +19,33 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "setup.h" +#include <algorithm> +#include <iostream> #include "button.h" +#include "setup.h" #include "setup_audio.h" +#include "setup_colours.h" #include "setup_joystick.h" -#include "setup_video.h" #include "setup_keyboard.h" #include "setup_players.h" +#include "setup_video.h" #include "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 +60,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 +93,10 @@ Setup::Setup(): panel->addTab(_("Keyboard"), tab); mTabs.push_back(tab); + tab = new Setup_Colours(); + panel->addTab(_("Colors"), tab); + mTabs.push_back(tab); + tab = new Setup_Players(); panel->addTab(_("Players"), tab); mTabs.push_back(tab); @@ -131,6 +138,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..7090136e 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" diff --git a/src/gui/setup_audio.h b/src/gui/setup_audio.h index 5345d3cf..2f5cd736 100644 --- a/src/gui/setup_audio.h +++ b/src/gui/setup_audio.h @@ -22,10 +22,10 @@ #ifndef GUI_SETUP_AUDIO_H #define GUI_SETUP_AUDIO_H -#include "setuptab.h" - #include <guichan/actionlistener.hpp> +#include "setuptab.h" + #include "../guichanfwd.h" class Setup_Audio : public SetupTab, public gcn::ActionListener diff --git a/src/gui/setup_colours.cpp b/src/gui/setup_colours.cpp new file mode 100644 index 00000000..c08c94ef --- /dev/null +++ b/src/gui/setup_colours.cpp @@ -0,0 +1,226 @@ +/*************************************************************************** + * Copyright (C) 2008 by Douglas Boffey * + * * + * DougABoffey@netscape.net * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed with The Mana Experiment * + * in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include <vector> + +#include <guichan/listmodel.hpp> +#include <guichan/widgets/label.hpp> +#include <guichan/widgets/slider.hpp> + +#include "colour.h" +#include "scrollarea.h" +#include "setup_colours.h" +#include "slider.h" +#include "textfield.h" + +#include "widgets/layouthelper.h" + +#include "../configuration.h" + +#include "../utils/gettext.h" + +Setup_Colours::Setup_Colours() : + mSelected(-1) +{ + setOpaque(false); + + mColourBox = new gcn::ListBox(textColour); + mColourBox->setActionEventId("colour_box"); + mColourBox->addActionListener(this); + + mScroll = new ScrollArea(mColourBox); + mScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mScroll->setWidth(90); + + 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(90); + 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(90); + 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(90); + 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, 1, 3).setPadding(2); + place(1, 0, mRedLabel, 2); + place(3, 0, mRedSlider); + place(4, 0, mRedText).setPadding(1); + place(1, 1, mGreenLabel, 2); + place(3, 1, mGreenSlider); + place(4, 1, mGreenText).setPadding(1); + place(1, 2, mBlueLabel, 2); + place(3, 2, mBlueSlider); + place(4, 2, mBlueText).setPadding(1); + + setDimension(gcn::Rectangle(0, 0, 290, 150)); +} + +Setup_Colours::~Setup_Colours() +{ + delete mRedLabel; + delete mRedSlider; + delete mRedText; + + delete mGreenLabel; + delete mGreenSlider; + delete mGreenText; + + delete mBlueLabel; + delete mBlueSlider; + delete mBlueText; + + delete mScroll; +} + +void Setup_Colours::action(const gcn::ActionEvent &event) +{ + if (event.getId() == "colour_box") + { + mSelected = mColourBox->getSelected(); + int col = textColour->getColourAt(mSelected); + setEntry(mRedSlider, mRedText, col >> 16); + setEntry(mGreenSlider, mGreenText, (col >> 8) & 0xff); + setEntry(mBlueSlider, mBlueText, col & 0xff); + return; + } + + if (event.getId() == "slider_red") + { + char buffer[30]; + std::sprintf(buffer, "%d", static_cast<int>(mRedSlider->getValue())); + mRedText->setText(buffer); + updateColour(); + return; + } + + if (event.getId() == "slider_green") + { + char buffer[30]; + std::sprintf(buffer, "%d", static_cast<int>(mGreenSlider->getValue())); + mGreenText->setText(buffer); + updateColour(); + return; + } + + if (event.getId() == "slider_blue") + { + char buffer[30]; + std::sprintf(buffer, "%d", static_cast<int>(mBlueSlider->getValue())); + mBlueText->setText(buffer); + updateColour(); + return; + } +} + +void Setup_Colours::setEntry(Slider *s, TextField *t, int value) +{ + s->setValue(value); + char buffer[100]; + sprintf(buffer, "%d", value); + t->setText(buffer); +} + +void Setup_Colours::apply() +{ + textColour->commit(); +} + +void Setup_Colours::cancel() +{ + textColour->rollback(); + int col = textColour->getColourAt(mSelected); + setEntry(mRedSlider, mRedText, col >> 16); + setEntry(mGreenSlider, mGreenText, (col >> 8) & 0xff); + setEntry(mBlueSlider, mBlueText, col & 0xff); +} + +void Setup_Colours::listen(const TextField *tf) +{ + if (tf == mRedText) + { + mRedSlider->setValue(tf->getValue()); + updateColour(); + return; + } + if (tf == mGreenText) + { + mGreenSlider->setValue(tf->getValue()); + updateColour(); + return; + } + if (tf == mBlueText) + { + mBlueSlider->setValue(tf->getValue()); + updateColour(); + return; + } +} + +void Setup_Colours::updateColour() +{ + if (mSelected == -1) + { + return; + } + int rgb = static_cast<int>(mRedSlider->getValue()) << 16 | + static_cast<int>(mGreenSlider->getValue()) << 8 | + static_cast<int>(mBlueSlider->getValue()); + textColour->setColourAt(mSelected, rgb); +} diff --git a/src/gui/setup_colours.h b/src/gui/setup_colours.h new file mode 100644 index 00000000..628efb43 --- /dev/null +++ b/src/gui/setup_colours.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (C) 2008 by Douglas Boffey * + * * + * DougABoffey@netscape.net * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed with The Mana Experiment * + * in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef _SETUP_COLOURS_H +#define _SETUP_COLOURS_H + +#include <string> +#include <vector> + +#include <guichan/actionlistener.hpp> +#include <guichan/widgets/label.hpp> +#include <guichan/widgets/listbox.hpp> + +#include "scrollarea.h" +#include "setuptab.h" +#include "slider.h" +#include "textfield.h" + +#include "../guichanfwd.h" + +class Setup_Colours : public SetupTab, public gcn::ActionListener, + public TextFieldListener +{ + public: + Setup_Colours(); + ~Setup_Colours(); + void apply(); + void cancel(); + void action(const gcn::ActionEvent &event); + + void listen(const TextField *tf); + private: + gcn::ListBox *mColourBox; + ScrollArea *mScroll; + int mSelected; + + gcn::Label *mRedLabel; + Slider *mRedSlider; + TextField *mRedText; + int mRedValue; + + gcn::Label *mGreenLabel; + Slider *mGreenSlider; + TextField *mGreenText; + int mGreenValue; + + gcn::Label *mBlueLabel; + Slider *mBlueSlider; + TextField *mBlueText; + int mBlueValue; + + void setEntry(Slider *s, TextField *t, int value); + void updateColour(); +}; +#endif diff --git a/src/gui/setup_joystick.cpp b/src/gui/setup_joystick.cpp index f8fc194f..2c726b87 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" @@ -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..2dc56439 100644 --- a/src/gui/setup_joystick.h +++ b/src/gui/setup_joystick.h @@ -22,10 +22,10 @@ #ifndef GUI_SETUP_JOYSTICK_H #define GUI_SETUP_JOYSTICK_H -#include "setuptab.h" - #include <guichan/actionlistener.hpp> +#include "setuptab.h" + #include "../guichanfwd.h" class Setup_Joystick : public SetupTab, public gcn::ActionListener diff --git a/src/gui/setup_keyboard.cpp b/src/gui/setup_keyboard.cpp index cf44731c..c6b6869a 100644 --- a/src/gui/setup_keyboard.cpp +++ b/src/gui/setup_keyboard.cpp @@ -19,7 +19,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "setup_keyboard.h" +#include <SDL_keyboard.h> #include <guichan/widgets/label.hpp> #include <guichan/listmodel.hpp> @@ -28,6 +28,9 @@ #include "listbox.h" #include "ok_dialog.h" #include "scrollarea.h" +#include "setup_keyboard.h" + +#include "widgets/layouthelper.h" #include "widgets/layouthelper.h" @@ -37,8 +40,6 @@ #include "../utils/gettext.h" #include "../utils/tostring.h" -#include <SDL_keyboard.h> - /** * The list model for key function list. * diff --git a/src/gui/setup_keyboard.h b/src/gui/setup_keyboard.h index f04be792..d4966053 100644 --- a/src/gui/setup_keyboard.h +++ b/src/gui/setup_keyboard.h @@ -22,14 +22,14 @@ #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 "button.h" +#include "setuptab.h" -#include <string> +#include "../guichanfwd.h" class Setup_Keyboard : public SetupTab, public gcn::ActionListener { diff --git a/src/gui/setup_players.cpp b/src/gui/setup_players.cpp index 1d8649eb..9a7fb441 100644 --- a/src/gui/setup_players.cpp +++ b/src/gui/setup_players.cpp @@ -19,11 +19,15 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "setup_players.h" +#include <vector> + +#include <guichan/widgets/dropdown.hpp> +#include <guichan/widgets/label.hpp> #include "button.h" #include "checkbox.h" #include "ok_dialog.h" +#include "setup_players.h" #include "widgets/layouthelper.h" @@ -34,11 +38,6 @@ #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 @@ -198,7 +197,7 @@ public: virtual std::string getElementAt(int i) { if (i >= getNumberOfElements()) { - return "???"; + return _("???"); } return (*player_relations.getPlayerIgnoreStrategies())[i]->mDescription; } diff --git a/src/gui/setup_players.h b/src/gui/setup_players.h index d380d9f6..22c8a9b6 100644 --- a/src/gui/setup_players.h +++ b/src/gui/setup_players.h @@ -22,15 +22,14 @@ #ifndef GUI_SETUP_PLAYERS_H #define GUI_SETUP_PLAYERS_H -#include "setuptab.h" +#include <guichan/actionlistener.hpp> -#include "scrollarea.h" #include "button.h" +#include "scrollarea.h" +#include "setuptab.h" #include "table.h" -#include <guichan/actionlistener.hpp> #include "../guichanfwd.h" - #include "../player_relations.h" class PlayerTableModel; diff --git a/src/gui/setup_video.cpp b/src/gui/setup_video.cpp index 53041a9c..2381ab41 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" @@ -89,9 +89,9 @@ ModeListModel::ModeListModel() /* Check which modes are available */ if (modes == (SDL_Rect **)0) { - logger->log("No modes available"); + logger->log(_("No modes available")); } else if (modes == (SDL_Rect **)-1) { - logger->log("All resolutions available"); + logger->log(_("All resolutions available")); } else { //logger->log("Available Modes"); for (int i = 0; modes[i]; ++i) { @@ -107,6 +107,9 @@ Setup_Video::Setup_Video(): mFullScreenEnabled(config.getValue("screen", 0)), mOpenGLEnabled(config.getValue("opengl", 0)), mCustomCursorEnabled(config.getValue("customcursor", 1)), + mParticleEffectsEnabled(config.getValue("particleeffects", 1)), + mSpeechBubbleEnabled(config.getValue("speechbubble", 1)), + mNameEnabled(config.getValue("showownname", 0)), 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)); } @@ -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()); } @@ -316,8 +332,11 @@ void Setup_Video::apply() // We sync old and new values at apply time mFullScreenEnabled = config.getValue("screen", 0); mCustomCursorEnabled = config.getValue("customcursor", 1); + mParticleEffectsEnabled = config.getValue("particleeffects", 1); + mSpeechBubbleEnabled = config.getValue("speechbubble", 1); + mNameEnabled = config.getValue("showownname", 0); mOpacity = config.getValue("guialpha", 0.8); - mOverlayDetail = (int)config.getValue("OverlayDetail", 2); + mOverlayDetail = (int) config.getValue("OverlayDetail", 2); mOpenGLEnabled = config.getValue("opengl", 0); } @@ -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); @@ -357,13 +379,46 @@ void Setup_Video::cancel() config.setValue("screen", mFullScreenEnabled ? 1 : 0); config.setValue("customcursor", mCustomCursorEnabled ? 1 : 0); + config.setValue("particleeffects", mParticleEffectsEnabled ? 1 : 0); + config.setValue("speechbubble", mSpeechBubbleEnabled ? 1 : 0); + config.setValue("showownname", mNameEnabled ? 1 : 0); config.setValue("guialpha", mOpacity); config.setValue("opengl", mOpenGLEnabled ? 1 : 0); } 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()); + 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: ") + << SDL_GetError() << std::endl; + exit(1); + } + + // Initialize for drawing + graphics->_endDraw(); + graphics->_beginDraw(); + graphics->updateScreen(); + + // 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()); } @@ -372,6 +427,27 @@ void Setup_Video::action(const gcn::ActionEvent &event) config.setValue("customcursor", mCustomCursorCheckBox->isSelected() ? 1 : 0); } + else if (event.getId() == "particleeffects") + { + config.setValue("particleeffects", + mParticleEffectsCheckBox->isSelected() ? 1 : 0); + new OkDialog(_("Particle effect settings changed"), + _("Restart your client or change maps for the change to take effect.")); + } + else if (event.getId() == "speechbubble") + { + config.setValue("speechbubble", + mSpeechBubbleCheckBox->isSelected() ? 1 : 0); + } + else if (event.getId() == "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() ? 1 : 0); + } else if (event.getId() == "fpslimitslider") { mFps = (int) mFpsSlider->getValue(); diff --git a/src/gui/setup_video.h b/src/gui/setup_video.h index b7011186..d863fb64 100644 --- a/src/gui/setup_video.h +++ b/src/gui/setup_video.h @@ -22,11 +22,11 @@ #ifndef GUI_SETUP_VIDEO_H #define GUI_SETUP_VIDEO_H -#include "setuptab.h" - #include <guichan/actionlistener.hpp> #include <guichan/keylistener.hpp> +#include "setuptab.h" + #include "../guichanfwd.h" class Setup_Video : public SetupTab, public gcn::ActionListener, @@ -53,6 +53,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 +65,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..22b649d0 100644 --- a/src/gui/shop.h +++ b/src/gui/shop.h @@ -27,10 +27,11 @@ #include <guichan/listmodel.hpp> -#include "../resources/image.h" - +#include "../guichanfwd.h" #include "../shopitem.h" +#include "../resources/image.h" + class ShopItems : public gcn::ListModel { public: diff --git a/src/gui/shoplistbox.cpp b/src/gui/shoplistbox.cpp index 2517d749..765b9f08 100644 --- a/src/gui/shoplistbox.cpp +++ b/src/gui/shoplistbox.cpp @@ -19,14 +19,14 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "shoplistbox.h" - +#include <guichan/basiccontainer.hpp> #include <guichan/font.hpp> #include <guichan/graphics.hpp> +#include <guichan/imagefont.hpp> #include <guichan/listmodel.hpp> #include <guichan/mouseinput.hpp> -#include <guichan/imagefont.hpp> -#include <guichan/basiccontainer.hpp> + +#include "shoplistbox.h" #include "../graphics.h" @@ -72,7 +72,7 @@ void ShopListBox::draw(gcn::Graphics *gcnGraphics) if (i == mSelected) { - backgroundColor = gcn::Color(110, 160, 255); + backgroundColor = gcn::Color(235, 200, 115); } else if (mShopItems && mPlayerMoney < mShopItems->at(i)->getPrice() && mPriceCheck) diff --git a/src/gui/shoplistbox.h b/src/gui/shoplistbox.h index bad848d6..733af4eb 100644 --- a/src/gui/shoplistbox.h +++ b/src/gui/shoplistbox.h @@ -25,6 +25,8 @@ #include "listbox.h" #include "shop.h" +#include "../guichanfwd.h" + /** * A list box, meant to be used inside a scroll area. Same as the Guichan list * box except this one doesn't have a background, instead completely relying diff --git a/src/gui/shortcutcontainer.cpp b/src/gui/shortcutcontainer.cpp new file mode 100644 index 00000000..d03bc809 --- /dev/null +++ b/src/gui/shortcutcontainer.cpp @@ -0,0 +1,76 @@ +/* + * 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 "../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" + +#include "../utils/tostring.h" + +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..66aca6c3 --- /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> + +#include "../guichanfwd.h" + +class Image; + +/** + * An item shortcut container. Used to quickly use items. + * + * \ingroup GUI + */ +class ShortcutContainer : public gcn::Widget, + public gcn::WidgetListener, + public gcn::MouseListener +{ + public: + /** + * Constructor. Initializes the graphic. + */ + ShortcutContainer(); + + /** + * Destructor. + */ + ~ShortcutContainer(){} + + /** + * Draws the items. + */ + 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; + + 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..3a7cf0e0 100644 --- a/src/gui/itemshortcutwindow.cpp +++ b/src/gui/shortcutwindow.cpp @@ -19,23 +19,23 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "itemshortcutwindow.h" - -#include "itemshortcutcontainer.h" +#include "shortcutcontainer.h" +#include "shortcutwindow.h" #include "scrollarea.h" +#include "../configuration.h" + static const int SCROLL_PADDING = 0; -ItemShortcutWindow::ItemShortcutWindow() +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; const int border = SCROLL_PADDING * 2 + getPadding() * 2; setMinWidth(mItems->getBoxWidth() + border); @@ -43,6 +43,14 @@ 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 - mItems->getBoxWidth() - 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 +61,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..0168669e 100644 --- a/src/gui/itemshortcutwindow.h +++ b/src/gui/shortcutwindow.h @@ -19,12 +19,14 @@ * 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; +#include "../guichanfwd.h" + +class ShortcutContainer; class ScrollArea; /** @@ -32,18 +34,18 @@ class ScrollArea; * * \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 +53,13 @@ class ItemShortcutWindow : public Window void widgetResized(const gcn::Event &event); private: - ItemShortcutContainer *mItems; + ShortcutWindow(); + ShortcutContainer *mItems; ScrollArea *mScrollArea; }; -extern ItemShortcutWindow *itemShortcutWindow; +extern ShortcutWindow *itemShortcutWindow; +extern ShortcutWindow *emoteShortcutWindow; #endif diff --git a/src/gui/skill.cpp b/src/gui/skill.cpp index 4afd913d..304719b6 100644 --- a/src/gui/skill.cpp +++ b/src/gui/skill.cpp @@ -21,13 +21,13 @@ #include <guichan/widgets/label.hpp> -#include "skill.h" - #include "button.h" #include "listbox.h" -#include "scrollarea.h" +#include "skill.h" #include "windowcontainer.h" +#include "widgets/layout.h" + #include "../localplayer.h" #include "../log.h" @@ -36,7 +36,7 @@ #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; @@ -51,21 +51,24 @@ class SkillGuiTableModel : public StaticTableModel { public: SkillGuiTableModel(SkillDialog *dialog) : - StaticTableModel(0, 3) + StaticTableModel(0, 3, 0xbdb5aa) { mEntriesNr = 0; mDialog = dialog; 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() @@ -83,7 +86,8 @@ public: 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]; @@ -123,31 +127,33 @@ SkillDialog::SkillDialog(): mTable.setModel(mTableModel); mTable.setLinewiseSelection(true); - setWindowName("Skills"); + setWindowName(_("Skills")); setCloseButton(true); setDefaultSize(windowContainer->getWidth() - 260, 25, 255, 260); + setMinHeight(50 + mTableModel->getHeight()); + setMinWidth(200); + // mSkillListBox = new ListBox(this); 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); + skillScrollArea->setOpaque(false); + + place(0, 0, skillScrollArea, 5).setPadding(3); + place(0, 1, mPointsLabel, 2); + place(3, 2, mIncButton); + place(4, 2, mUseButton); - add(skillScrollArea); - add(mPointsLabel); - add(mIncButton); - add(mUseButton); + Layout &layout = getLayout(); + layout.setRowHeight(0, Layout::AUTO_SET); // mSkillListBox->addActionListener(this); mTable.addActionListener(this); @@ -167,9 +173,7 @@ void SkillDialog::action(const gcn::ActionEvent &event) // Increment skill int selectedSkill = mTable.getSelectedRow();//mSkillListBox->getSelected(); if (selectedSkill >= 0) - { player_node->raiseSkill(mSkillList[selectedSkill]->id); - } } else if (event.getId() == "skill") { @@ -178,9 +182,7 @@ void SkillDialog::action(const gcn::ActionEvent &event) player_node->mSkillPoint > 0); } else if (event.getId() == "close") - { setVisible(false); - } } void SkillDialog::update() @@ -190,7 +192,8 @@ void SkillDialog::update() int selectedSkill = mTable.getSelectedRow(); - if (selectedSkill >= 0) { + if (selectedSkill >= 0) + { int skillId = mSkillList[selectedSkill]->id; bool modifiable; @@ -201,10 +204,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 +219,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 +238,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; } @@ -256,7 +263,7 @@ static void initSkillinfo() if (!root || !xmlStrEqual(root->name, BAD_CAST "skills")) { - logger->log("Error loading skills file: %s", SKILLS_FILE); + logger->log(_("Error loading skills file: %s"), SKILLS_FILE); skill_db.resize(2, emptySkillInfo); skill_db[1].name = "Basic"; skill_db[1].modifiable = true; diff --git a/src/gui/skill.h b/src/gui/skill.h index 893a61e7..582892f0 100644 --- a/src/gui/skill.h +++ b/src/gui/skill.h @@ -24,9 +24,11 @@ #include <vector> -#include <guichan/listmodel.hpp> #include <guichan/actionlistener.hpp> +#include <guichan/listmodel.hpp> +#include "scrollarea.h" +#include "table.h" #include "window.h" #include "table.h" @@ -72,6 +74,7 @@ class SkillDialog : public Window, public gcn::ActionListener private: GuiTable mTable;//gcn::ListBox *mSkillListBox; + ScrollArea *skillScrollArea; SkillGuiTableModel *mTableModel; gcn::Label *mPointsLabel; gcn::Button *mIncButton; diff --git a/src/gui/slider.h b/src/gui/slider.h index 1fe668c5..c14c5be9 100644 --- a/src/gui/slider.h +++ b/src/gui/slider.h @@ -24,8 +24,9 @@ #include <guichan/widgets/slider.hpp> -class Image; +#include "../guichanfwd.h" +class Image; /** * Slider widget. Same as the Guichan slider but with custom look. diff --git a/src/gui/speechbubble.cpp b/src/gui/speechbubble.cpp new file mode 100644 index 00000000..a6bb5563 --- /dev/null +++ b/src/gui/speechbubble.cpp @@ -0,0 +1,111 @@ +/* + * The Legend of Mazzeroth + * Copyright (C) 2008, The Legend of Mazzeroth Development Team + * + * This file is part of The Legend of Mazzeroth based on original code + * from The Mana World. + * + * The Legend of Mazzeroth is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Legend of Mazzeroth is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Legend of Mazzeroth; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <guichan/font.hpp> + +#include <guichan/widgets/label.hpp> + +#include "gui.h" +#include "speechbubble.h" + +#include "../resources/image.h" +#include "../resources/resourcemanager.h" + +#include "../utils/gettext.h" + +// TODO: Fix windows so that they can each load their own skins without the +// other windows overriding another window's skin. +SpeechBubble::SpeechBubble(): + Window(_("Speech"), false, NULL, "graphics/gui/speechbubble.xml") +{ + // Height == Top Graphic (14px) + 1 Row of Text (15px) + Bottom Graphic (17px) + 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()); + + // LEEOR / TODO: This causes an exception error. + //moveToBottom(getParent()); + + mSpeechBox->setTextWrapped( "" ); +} + +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) +{ + mSpeechBox->setMinWidth(140); + mSpeechBox->setTextWrapped(mText); + + const int fontHeight = getFont()->getHeight(); + const int numRows = mSpeechBox->getNumberOfRows() + 1; + + if (numRows > 2) + { + // 15 == height of each line of text (based on font heights) + // 14 == speechbubble Top + Bottom graphic pixel heights + setContentSize(mSpeechBox->getMinWidth() + fontHeight, + (numRows * fontHeight) + 6); + mSpeechArea->setDimension(gcn::Rectangle(4, fontHeight + 3, + mSpeechBox->getMinWidth() + 5, + (numRows * fontHeight))); + } + else + { + int width = mCaption->getWidth() + 3; + if (width < getFont()->getWidth(mText)) + width = getFont()->getWidth(mText); + setContentSize(width + fontHeight, (fontHeight * 2) + 6); + mSpeechArea->setDimension(gcn::Rectangle(4, fontHeight + 3, + width, 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..23733813 --- /dev/null +++ b/src/gui/speechbubble.h @@ -0,0 +1,48 @@ +/* + * The Legend of Mazzeroth + * Copyright (C) 2008, The Legend of Mazzeroth Development Team + * + * This file is part of The Legend of Mazzeroth based on original code + * from The Mana World. + * + * The Legend of Mazzeroth is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Legend of Mazzeroth is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Legend of Mazzeroth; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LOM_SPEECHBUBBLE_H__ +#define _LOM_SPEECHBUBBLE_H__ + +#include "scrollarea.h" +#include "textbox.h" +#include "window.h" + +class SpeechBubble : public Window +{ + public: + + SpeechBubble(); + + void 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 b95a1eb8..bd6048f3 100644 --- a/src/gui/status.cpp +++ b/src/gui/status.cpp @@ -19,12 +19,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "status.h" - #include <guichan/widgets/label.hpp> #include "button.h" #include "progressbar.h" +#include "status.h" #include "windowcontainer.h" #include "../localplayer.h" @@ -37,11 +36,10 @@ 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); + (windowContainer->getHeight() - 255) / 2, 400, 275); loadWindowState(); // ---------------------- @@ -52,19 +50,19 @@ StatusWindow::StatusWindow(LocalPlayer *player): mGpLabel = new gcn::Label(strprintf(_("Job: %d"), 0)); mJobLvlLabel = new gcn::Label(strprintf(_("Money: %d GP"), 0)); - mHpLabel = new gcn::Label("HP:"); + mHpLabel = new gcn::Label(_("HP:")); mHpBar = new ProgressBar(1.0f, 80, 15, 0, 171, 34); mHpValueLabel = new gcn::Label; - mXpLabel = new gcn::Label("Exp:"); + mXpLabel = new gcn::Label(_("Exp:")); mXpBar = new ProgressBar(1.0f, 80, 15, 143, 192, 211); mXpValueLabel = new gcn::Label; - mMpLabel = new gcn::Label("MP:"); + mMpLabel = new gcn::Label(_("MP:")); mMpBar = new ProgressBar(1.0f, 80, 15, 26, 102, 230); mMpValueLabel = new gcn::Label; - mJobXpLabel = new gcn::Label("Job:"); + mJobXpLabel = new gcn::Label(_("Job:")); mJobXpBar = new ProgressBar(1.0f, 80, 15, 220, 135, 203); mJobValueLabel = new gcn::Label; diff --git a/src/gui/status.h b/src/gui/status.h index 6d360caf..14a7617e 100644 --- a/src/gui/status.h +++ b/src/gui/status.h @@ -33,7 +33,6 @@ class LocalPlayer; class ProgressBar; - /** * The player status dialog. * diff --git a/src/gui/table.cpp b/src/gui/table.cpp index f6678737..7e855523 100644 --- a/src/gui/table.cpp +++ b/src/gui/table.cpp @@ -19,13 +19,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "table.h" +#include <cassert> #include <guichan/graphics.hpp> #include <guichan/actionlistener.hpp> -#include <cassert> - +#include "table.h" class GuiTableActionListener : public gcn::ActionListener { @@ -100,6 +99,7 @@ void GuiTable::setModel(TableModel *new_model) mModel->removeListener(this); } + mModel = new_model; installActionListeners(); @@ -109,7 +109,6 @@ void GuiTable::setModel(TableModel *new_model) } } - void GuiTable::recomputeDimensions() { int rows_nr = mModel->getRows(); @@ -296,7 +295,6 @@ void GuiTable::keyPressed(gcn::KeyEvent& keyEvent) { } - // -- MouseListener notifications void GuiTable::mousePressed(gcn::MouseEvent& mouseEvent) { @@ -386,7 +384,6 @@ int GuiTable::getColumnForX(int x) 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..9dde8900 100644 --- a/src/gui/table.h +++ b/src/gui/table.h @@ -31,6 +31,7 @@ #include <guichan/widget.hpp> #include "table_model.h" + #include "../guichanfwd.h" class GuiTableActionListener; diff --git a/src/gui/table_model.cpp b/src/gui/table_model.cpp index 7bc29b47..e69ee808 100644 --- a/src/gui/table_model.cpp +++ b/src/gui/table_model.cpp @@ -19,10 +19,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "table_model.h" +#include <cstdlib> +#include <guichan/graphics.hpp> #include <guichan/widget.hpp> -#include <cstdlib> + +#include "table_model.h" #include "../utils/dtor.h" @@ -49,14 +51,16 @@ 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 -StaticTableModel::StaticTableModel(int row, int column) : +StaticTableModel::StaticTableModel(int row, int column, + gcn::Color backgroundColor, bool opacity) : mRows(row), mColumns(column), - mHeight(1) + mHeight(1), + mOpaque(opacity), + mBackgroundColor(backgroundColor) { mTableModel.resize(row * column, NULL); mWidths.resize(column, 1); @@ -143,3 +147,42 @@ 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); +} + +void StaticTableModel::drawBackground(gcn::Graphics *graphics) +{ + if (isOpaque()) + { + for (unsigned int i = 0; i < mTableModel.size(); i++) + { + mTableModel[i]->setBackgroundColor(mBackgroundColor); + } + } +} + +void StaticTableModel::setOpaque(bool opaque) +{ + mOpaque = opaque; +} + +bool StaticTableModel::isOpaque() const +{ + return mOpaque; +} + diff --git a/src/gui/table_model.h b/src/gui/table_model.h index d245d7bd..03b69dba 100644 --- a/src/gui/table_model.h +++ b/src/gui/table_model.h @@ -22,12 +22,13 @@ #ifndef TABLE_MODEL_H #define TABLE_MODEL_H -#include "../guichanfwd.h" +#include <set> +#include <vector> +#include <guichan/color.hpp> #include <guichan/gui.hpp> -#include <set> -#include <vector> +#include "../guichanfwd.h" class TableModelListener { @@ -100,7 +101,8 @@ private: class StaticTableModel : public TableModel { public: - StaticTableModel(int width, int height); + StaticTableModel(int width, int height, gcn::Color background = 0xffffff, + bool opacity = true); virtual ~StaticTableModel(); /** @@ -130,17 +132,49 @@ public: */ virtual void resize(); + /** + * 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); + + /** + * Checks if the scroll area is opaque, that is if the scroll area + * displays its background. + * + * @return True if the scroll area is opaque, false otherwise. + */ + virtual bool isOpaque() const; + 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); protected: int mRows, mColumns; int mHeight; + bool mOpaque; std::vector<gcn::Widget *> mTableModel; std::vector<int> mWidths; + + /** + * Holds the background color of the table. + */ + gcn::Color mBackgroundColor; + + /** + * Draws the background of the table, that is + * the area behind the content. + * + * @param graphics a Graphics object to draw with. + */ + virtual void drawBackground(gcn::Graphics *graphics); }; #endif /* !defined(TABLE_MODEL_H) */ diff --git a/src/gui/textbox.cpp b/src/gui/textbox.cpp index 75f0b5a1..ee03c79d 100644 --- a/src/gui/textbox.cpp +++ b/src/gui/textbox.cpp @@ -19,13 +19,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "textbox.h" - #include <sstream> #include <guichan/basiccontainer.hpp> #include <guichan/font.hpp> +#include "textbox.h" + TextBox::TextBox(): gcn::TextBox() { @@ -43,6 +43,8 @@ void TextBox::setTextWrapped(const std::string &text) std::stringstream wrappedStream; std::string::size_type newlinePos, lastNewlinePos = 0; + int minWidth = 0; + int xpos; do { @@ -57,7 +59,7 @@ void TextBox::setTextWrapped(const std::string &text) std::string line = text.substr(lastNewlinePos, newlinePos - lastNewlinePos); std::string::size_type spacePos, lastSpacePos = 0; - int xpos = 0; + xpos = 0; do { @@ -73,7 +75,7 @@ void TextBox::setTextWrapped(const std::string &text) int width = getFont()->getWidth(word); - if (xpos != 0 && xpos + width < getWidth()) + if (xpos != 0 && xpos + width + getFont()->getWidth(" ") <= mMinWidth) { xpos += width + getFont()->getWidth(" "); wrappedStream << " " << word; @@ -85,10 +87,30 @@ void TextBox::setTextWrapped(const std::string &text) } else { + if (xpos > minWidth) + { + minWidth = xpos; + } + // The window wasn't big enough. Resize it and try again. + if (minWidth > mMinWidth) + { + mMinWidth = minWidth; + wrappedStream.clear(); + wrappedStream.str(""); + spacePos = 0; + lastNewlinePos = 0; + newlinePos = text.find("\n", lastNewlinePos); + line = text.substr(lastNewlinePos, newlinePos - + lastNewlinePos); + width = 0; + break; + } + else + { + wrappedStream << "\n" << word; + } xpos = width; - wrappedStream << "\n" << word; } - lastSpacePos = spacePos + 1; } while (spacePos != line.size()); @@ -97,10 +119,15 @@ void TextBox::setTextWrapped(const std::string &text) { wrappedStream << "\n"; } - lastNewlinePos = newlinePos + 1; } while (newlinePos != text.size()); + if (xpos > minWidth) + { + minWidth = xpos; + } + mMinWidth = minWidth; + gcn::TextBox::setText(wrappedStream.str()); } diff --git a/src/gui/textbox.h b/src/gui/textbox.h index a42562ea..98b60402 100644 --- a/src/gui/textbox.h +++ b/src/gui/textbox.h @@ -24,6 +24,8 @@ #include <guichan/widgets/textbox.hpp> +#include "../guichanfwd.h" + /** * A text box, meant to be used inside a scroll area. Same as the Guichan text * box except this one doesn't have a background or border, instead completely @@ -42,6 +44,19 @@ class TextBox : public gcn::TextBox { * Sets the text after wrapping it to the current width of the widget. */ void setTextWrapped(const std::string &text); + + /** + * Get the minimum text width for the text box. + */ + int getMinWidth() { return mMinWidth; } + + /** + * Set the minimum text width for the text box. + */ + void setMinWidth(int width) { mMinWidth = width; } + + private: + int mMinWidth; }; #endif diff --git a/src/gui/textfield.cpp b/src/gui/textfield.cpp index f7b02cbf..3369195d 100644 --- a/src/gui/textfield.cpp +++ b/src/gui/textfield.cpp @@ -19,14 +19,14 @@ * 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" @@ -40,7 +40,9 @@ int TextField::instances = 0; ImageRect TextField::skin; TextField::TextField(const std::string& text): - gcn::TextField(text) + gcn::TextField(text), + mNumeric(false), + mListener(0) { setFrameSize(2); @@ -62,6 +64,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++; } } @@ -105,6 +108,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..a2432175 100644 --- a/src/gui/textfield.h +++ b/src/gui/textfield.h @@ -24,7 +24,16 @@ #include <guichan/widgets/textfield.hpp> +#include "../guichanfwd.h" + class ImageRect; +class TextField; + +class TextFieldListener +{ + public: + virtual void listen(const TextField *value) = 0; +}; /** * A text field. @@ -54,13 +63,47 @@ 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 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..c89e55a2 100644 --- a/src/gui/trade.cpp +++ b/src/gui/trade.cpp @@ -19,8 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "trade.h" - #include <sstream> #include <guichan/widgets/label.hpp> @@ -32,9 +30,13 @@ #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" @@ -46,12 +48,12 @@ #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 +65,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 +112,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 +127,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 +183,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 +230,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 +247,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 +281,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..66855b77 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; diff --git a/src/gui/updatewindow.cpp b/src/gui/updatewindow.cpp index 96c2e95c..67e05bbd 100644 --- a/src/gui/updatewindow.cpp +++ b/src/gui/updatewindow.cpp @@ -19,8 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "updatewindow.h" - #include <iostream> #include <SDL.h> #include <SDL_thread.h> @@ -32,6 +30,9 @@ #include "button.h" #include "progressbar.h" #include "scrollarea.h" +#include "updatewindow.h" + +#include "widgets/layout.h" // Curl should be included after Guichan to avoid Windows redefinitions #include <curl/curl.h> @@ -40,11 +41,11 @@ #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,14 +69,13 @@ 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()); if (!fin) { - logger->log("Couldn't load text file: %s", fileName.c_str()); + logger->log(_("Couldn't load text file: %s"), fileName.c_str()); return lines; } @@ -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); @@ -198,7 +193,7 @@ void UpdaterWindow::loadNews() { if (!mMemoryBuffer) { - logger->log("Couldn't load news"); + logger->log(_("Couldn't load news")); return; } @@ -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++; @@ -416,7 +410,7 @@ void UpdaterWindow::download() if (mThread == NULL) { - logger->log("Unable to create 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()); @@ -510,7 +504,7 @@ void UpdaterWindow::logic() } else { - logger->log("%s already here", mCurrentFile.c_str()); + logger->log(_("%s already here"), mCurrentFile.c_str()); } mLineIndex++; } diff --git a/src/gui/updatewindow.h b/src/gui/updatewindow.h index 3acbfb7e..6450ece2 100644 --- a/src/gui/updatewindow.h +++ b/src/gui/updatewindow.h @@ -23,6 +23,7 @@ #define _UPDATERWINDOW_H #include <guichan/actionlistener.hpp> + #include <string> #include <vector> diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp index ff0883f7..326a7189 100644 --- a/src/gui/viewport.cpp +++ b/src/gui/viewport.cpp @@ -19,17 +19,18 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "viewport.h" +#include <cassert> #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" @@ -37,15 +38,13 @@ #include "../textmanager.h" #include "../resources/animation.h" -#include "../resources/monsterinfo.h" -#include "../resources/resourcemanager.h" #include "../resources/image.h" #include "../resources/imageset.h" +#include "../resources/monsterinfo.h" +#include "../resources/resourcemanager.h" #include "../utils/tostring.h" -#include <cassert> - extern volatile int tick_time; Viewport::Viewport(): @@ -70,76 +69,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 +92,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 +173,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 +216,7 @@ Viewport::draw(gcn::Graphics *gcnGraphics) Beings &beings = beingManager->getAll(); for (BeingIterator i = beings.begin(); i != beings.end(); i++) { + (*i)->drawSpeech(graphics, -(int) mPixelViewX, -(int) mPixelViewY); (*i)->drawEmotion(graphics, -(int) mPixelViewX, -(int) mPixelViewY); } @@ -276,8 +227,7 @@ Viewport::draw(gcn::Graphics *gcnGraphics) WindowContainer::draw(gcnGraphics); } -void -Viewport::logic() +void Viewport::logic() { WindowContainer::logic(); @@ -294,49 +244,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 +258,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,11 +269,11 @@ Viewport::mousePressed(gcn::MouseEvent &event) Being *being; FloorItem *floorItem; - if ((being = beingManager->findBeing(tilex, tiley)) && - being != player_node) + if ((being = beingManager->findBeingByPixel(x, y)) && + being != player_node) { - mPopupMenu->showPopup(event.getX(), event.getY(), being); - return; + mPopupMenu->showPopup(event.getX(), event.getY(), being); + return; } else if((floorItem = floorItemManager->findByCoordinates(tilex, tiley))) { @@ -382,11 +294,9 @@ Viewport::mousePressed(gcn::MouseEvent &event) { Being *being; FloorItem *item; - + // Interact with some being -// if ((being = beingManager->findBeing(tilex, tiley)) - int x = (int)((float) event.getX() + mPixelViewX); - int y = (int)((float) event.getY() + mPixelViewY); +// if ((being = beingManager->findBeing(tilex, tiley))) if ((being = beingManager->findBeingByPixel(x, y))) { switch (being->getType()) @@ -400,59 +310,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 +363,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..5ed40166 100644 --- a/src/gui/viewport.h +++ b/src/gui/viewport.h @@ -26,8 +26,9 @@ #include "windowcontainer.h" -#include "../configlistener.h" #include "../being.h" +#include "../configlistener.h" +#include "../guichanfwd.h" class Map; class FloorItem; @@ -35,7 +36,6 @@ class ImageSet; class Item; class PopupMenu; class Graphics; -class SimpleAnimation; /** * The viewport on the map. Displays the current map and handles mouse input @@ -62,97 +62,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,18 +133,6 @@ class Viewport : public WindowContainer, public gcn::MouseListener, int mTileViewY; /**< Current viewpoint in tiles. */ bool mShowDebugPath; /**< Show a path from player to pointer. */ - /** Images of in range target cursor. */ - ImageSet *mInRangeImages[Being::NUM_TC]; - - /** Images of out of range target cursor. */ - ImageSet *mOutRangeImages[Being::NUM_TC]; - - /** Animated in range target cursor. */ - SimpleAnimation *mTargetCursorInRange[Being::NUM_TC]; - - /** Animated out of range target cursor. */ - SimpleAnimation *mTargetCursorOutRange[Being::NUM_TC]; - bool mPlayerFollowMouse; int mWalkTime; diff --git a/src/gui/widgets/dropdown.cpp b/src/gui/widgets/dropdown.cpp new file mode 100644 index 00000000..88a12d68 --- /dev/null +++ b/src/gui/widgets/dropdown.cpp @@ -0,0 +1,167 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <algorithm> + +#include "dropdown.h" + +#include "../../graphics.h" + +#include "../../resources/image.h" +#include "../../resources/resourcemanager.h" + +#include "../../utils/dtor.h" + +int DropDown::instances = 0; +Image *DropDown::buttons[2][2]; +ImageRect DropDown::skin; + +DropDown::DropDown(gcn::ListModel *listModel, + gcn::ScrollArea *scrollArea, + gcn::ListBox *listBox): + gcn::DropDown::DropDown(listModel, + scrollArea, listBox) +{ + setFrameSize(2); + + // Initialize graphics + if (instances == 0) + { + // Load the background skin + ResourceManager *resman = ResourceManager::getInstance(); + + // Get the button skin + buttons[1][0] = + resman->getImage("graphics/gui/vscroll_up_default.png"); + buttons[0][0] = + resman->getImage("graphics/gui/vscroll_down_default.png"); + buttons[1][1] = + resman->getImage("graphics/gui/vscroll_up_pressed.png"); + buttons[0][1] = + resman->getImage("graphics/gui/vscroll_down_pressed.png"); + + // get the border skin + Image *boxBorder = resman->getImage("graphics/gui/deepbox.png"); + int gridx[4] = {0, 3, 28, 31}; + int gridy[4] = {0, 3, 28, 31}; + int a = 0, x, y; + + for (y = 0; y < 3; y++) { + for (x = 0; x < 3; x++) { + skin.grid[a] = boxBorder->getSubImage( + gridx[x], gridy[y], + gridx[x + 1] - gridx[x] + 1, + gridy[y + 1] - gridy[y] + 1); + a++; + } + } + + boxBorder->decRef(); + } + + instances++; +} + +DropDown::~DropDown() +{ + instances--; + // Free images memory + if (instances == 0) + { + buttons[0][0]->decRef(); + buttons[0][1]->decRef(); + buttons[1][0]->decRef(); + buttons[1][1]->decRef(); + + for_each(skin.grid, skin.grid + 9, dtor<Image*>()); + } +} + +void DropDown::draw(gcn::Graphics* graphics) +{ + int h; + + if (mDroppedDown) + { + h = mFoldedUpHeight; + } + else + { + h = getHeight(); + } + + int alpha = getBaseColor().a; + gcn::Color faceColor = getBaseColor(); + faceColor.a = alpha; + gcn::Color highlightColor = faceColor + 0x303030; + highlightColor.a = alpha; + gcn::Color shadowColor = faceColor - 0x303030; + shadowColor.a = alpha; + + + graphics->setColor(getBackgroundColor()); + graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), h)); + + graphics->setColor(getForegroundColor()); + graphics->setFont(getFont()); + + if (mListBox->getListModel() && mListBox->getSelected() >= 0) + { + graphics->drawText(mListBox->getListModel()->getElementAt(mListBox->getSelected()), 1, 0); + } + + if (isFocused()) + { + graphics->setColor(highlightColor); + graphics->drawRectangle(gcn::Rectangle(0, 0, getWidth() - h, h)); + } + + drawButton(graphics); + + if (mDroppedDown) + { + drawChildren(graphics); + + // Draw two lines separating the ListBox with se selected + // element view. + graphics->setColor(highlightColor); + graphics->drawLine(0, h, getWidth(), h); + graphics->setColor(shadowColor); + graphics->drawLine(0, h + 1, getWidth(), h + 1); + } +} + +void DropDown::drawFrame(gcn::Graphics *graphics) +{ + const int bs = getFrameSize(); + const int w = getWidth() + bs * 2; + const int h = getHeight() + bs * 2; + + static_cast<Graphics*>(graphics)->drawImageRect(0, 0, w, h, skin); +} + +void DropDown::drawButton(gcn::Graphics *graphics) +{ + int height = mDroppedDown ? mFoldedUpHeight : getHeight(); + + static_cast<Graphics*>(graphics)-> + drawImage(buttons[mDroppedDown][mPushed], getWidth() - height + 2, 1); +} diff --git a/src/gui/widgets/dropdown.h b/src/gui/widgets/dropdown.h new file mode 100644 index 00000000..25ae05f8 --- /dev/null +++ b/src/gui/widgets/dropdown.h @@ -0,0 +1,86 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DROPDOWN_H +#define DROPDOWN_H + +#include <iosfwd> + +#include <guichan/widgets/dropdown.hpp> + +#include "../listbox.h" +#include "../scrollarea.h" + +#include "../../guichanfwd.h" + +class Image; +class ImageRect; + + /** + * A drop down box from which you can select different values. It is one of + * the most complicated Widgets you will find in Guichan. For drawing the + * DroppedDown box it uses one ScrollArea and one ListBox. It also uses an + * internal FocusHandler to handle the focus of the internal ScollArea and + * ListBox. DropDown uses a ListModel to handle the list. To be able to use + * DropDown you must give DropDown an implemented ListModel which represents + * your list. + */ +class DropDown : public gcn::DropDown +{ + public: + /** + * Contructor. + * + * @param listModel the ListModel to use. + * @param scrollArea the ScrollArea to use. + * @param listBox the listBox to use. + * @see ListModel, ScrollArea, ListBox. + */ + DropDown(gcn::ListModel *listModel = NULL, + gcn::ScrollArea *scrollArea = NULL, + gcn::ListBox *listBox = NULL); + + /** + * Destructor. + */ + ~DropDown(); + + void draw(gcn::Graphics* graphics); + + void drawFrame(gcn::Graphics* graphics); + + + protected: + /** + * Draws the button with the little down arrow. + * + * @param graphics a Graphics object to draw with. + */ + void drawButton(gcn::Graphics *graphics); + + // Add own Images. + static int instances; + static Image *buttons[2][2]; + static ImageRect skin; +}; + +#endif // end DROPDOWN_H + diff --git a/src/gui/widgets/resizegrip.cpp b/src/gui/widgets/resizegrip.cpp index 00689575..4b8bb4da 100644 --- a/src/gui/widgets/resizegrip.cpp +++ b/src/gui/widgets/resizegrip.cpp @@ -19,10 +19,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "resizegrip.h" - #include <guichan/graphics.hpp> +#include "resizegrip.h" + #include "../../graphics.h" #include "../../resources/image.h" @@ -31,13 +31,13 @@ Image *ResizeGrip::gripImage = 0; int ResizeGrip::mInstances = 0; -ResizeGrip::ResizeGrip() +ResizeGrip::ResizeGrip(std::string image) { if (mInstances == 0) { // Load the grip image ResourceManager *resman = ResourceManager::getInstance(); - gripImage = resman->getImage("graphics/gui/resize.png"); + gripImage = resman->getImage(image); } mInstances++; @@ -56,8 +56,7 @@ ResizeGrip::~ResizeGrip() } } -void -ResizeGrip::draw(gcn::Graphics *graphics) +void ResizeGrip::draw(gcn::Graphics *graphics) { static_cast<Graphics*>(graphics)->drawImage(gripImage, 0, 0); } diff --git a/src/gui/widgets/resizegrip.h b/src/gui/widgets/resizegrip.h index 5c6ea4bd..7f1329a2 100644 --- a/src/gui/widgets/resizegrip.h +++ b/src/gui/widgets/resizegrip.h @@ -24,6 +24,8 @@ #include <guichan/widget.hpp> +#include "../../guichanfwd.h" + class Image; /** @@ -39,7 +41,7 @@ class ResizeGrip : public gcn::Widget /** * Constructor. */ - ResizeGrip(); + ResizeGrip(std::string image = "graphics/gui/resize.png"); /** * Destructor. diff --git a/src/gui/window.cpp b/src/gui/window.cpp index ed5bb8fc..e0e88b31 100644 --- a/src/gui/window.cpp +++ b/src/gui/window.cpp @@ -24,11 +24,12 @@ #include <climits> #include <guichan/exception.hpp> -#include <guichan/widgets/icon.hpp> -#include "window.h" +#include <guichan/widgets/icon.hpp> #include "gui.h" +#include "gccontainer.h" +#include "window.h" #include "windowcontainer.h" #include "widgets/layout.h" @@ -42,24 +43,28 @@ #include "../resources/image.h" #include "../resources/resourcemanager.h" +#include "../utils/xml.h" + ConfigListener *Window::windowConfigListener = 0; WindowContainer *Window::windowContainer = 0; int Window::instances = 0; int Window::mouseResize = 0; -ImageRect Window::border; +//ImageRect Window::border; Image *Window::closeImage = NULL; +bool Window::mAlphaChanged = false; class WindowConfigListener : public ConfigListener { void optionChanged(const std::string &) { - for_each(Window::border.grid, Window::border.grid + 9, - std::bind2nd(std::mem_fun(&Image::setAlpha), - config.getValue("guialpha", 0.8))); + Window::mAlphaChanged = true; +// for_each(Window::border.grid, Window::border.grid + 9, +// std::bind2nd(std::mem_fun(&Image::setAlpha), +// config.getValue("guialpha", 0.8))); } }; -Window::Window(const std::string& caption, bool modal, Window *parent): +Window::Window(const std::string& caption, bool modal, Window *parent, const std::string& skin): gcn::Window(caption), mGrip(0), mParent(parent), @@ -72,31 +77,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 +127,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 +151,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(); } } @@ -524,6 +520,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 == "") + logger->error("Window::loadSkin(): Invalid File Name."); + + // TODO: + // If there is an error loading the specified file, we should try to revert + // to a 'default' skin file. Only if the 'default' skin file can't be loaded + // should we have a terminating error. + XML::Document doc(filename); + xmlNodePtr rootNode = doc.rootNode(); + + if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "skinset")) + { + logger->error("Widget Skinning error"); + } + + std::string skinSetImage; + skinSetImage = XML::getProperty(rootNode, "image", ""); + Image *dBorders = NULL; + if(skinSetImage != "") + { + logger->log("Window::loadSkin(): <skinset> defines '%s' as a skin image.", skinSetImage.c_str()); + dBorders = resman->getImage("graphics/gui/" + skinSetImage);//"graphics/gui/speech_bubble.png"); + } + else + { + logger->error("Window::loadSkin(): Skinset does not define an image!"); + } + + //iterate <widget>'s + for_each_xml_child_node(widgetNode, rootNode) + { + if (!xmlStrEqual(widgetNode->name, BAD_CAST "widget")) + continue; + + std::string widgetType; + widgetType = XML::getProperty(widgetNode, "type", "unknown"); + if (widgetType == "Window") + { + // Iterate through <part>'s + // LEEOR / TODO: + // We need to make provisions to load in a CloseButton image. For now it + // can just be hard-coded. + for_each_xml_child_node(partNode, widgetNode) + { + if (!xmlStrEqual(partNode->name, BAD_CAST "part")) + { + continue; + } + + std::string partType; + partType = XML::getProperty(partNode, "type", "unknown"); + // TOP ROW + if(partType == "top-left-corner") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[0] = dBorders->getSubImage(xPos, yPos, width, height); + } + else if(partType == "top-edge") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[1] = dBorders->getSubImage(xPos, yPos, width, height); + } + else if(partType == "top-right-corner") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[2] = dBorders->getSubImage(xPos, yPos, width, height); + } + + // MIDDLE ROW + else if(partType == "left-edge") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[3] = dBorders->getSubImage(xPos, yPos, width, height); + } + else if(partType == "bg-quad") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[4] = dBorders->getSubImage(xPos, yPos, width, height); + } + else if(partType == "right-edge") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[5] = dBorders->getSubImage(xPos, yPos, width, height); + } + + // BOTTOM ROW + else if(partType == "bottom-left-corner") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[6] = dBorders->getSubImage(xPos, yPos, width, height); + } + else if(partType == "bottom-edge") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[7] = dBorders->getSubImage(xPos, yPos, width, height); + } + else if(partType == "bottom-right-corner") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[8] = dBorders->getSubImage(xPos, yPos, width, height); + } + + // Part is of an uknown type. + else + { + logger->log("Window::loadSkin(): Unknown Part Type '%s'", partType.c_str()); + } + } + } + // Widget is of an uknown type. + else + { + logger->log("Window::loadSkin(): Unknown Widget Type '%s'", widgetType.c_str()); + } + } + dBorders->decRef(); + + logger->log("Finished loading Window Skin."); + + // Hard-coded for now until we update the above code to look for window buttons. + closeImage = resman->getImage("graphics/gui/close_button.png"); +} + Layout &Window::getLayout() { if (!mLayout) mLayout = new Layout; diff --git a/src/gui/window.h b/src/gui/window.h index 19d59c26..deaf984c 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..a3e80223 100644 --- a/src/gui/windowcontainer.h +++ b/src/gui/windowcontainer.h @@ -24,6 +24,8 @@ #include <guichan/widgets/container.hpp> +#include "../guichanfwd.h" + /** * A window container. This container adds functionality for more convenient * widget (windows in particular) destruction. diff --git a/src/imageparticle.cpp b/src/imageparticle.cpp index 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..8824e1ba 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -19,11 +19,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "inventory.h" - #include <algorithm> #include <cassert> +#include "inventory.h" #include "item.h" #include "log.h" @@ -34,15 +33,16 @@ struct SlotUsed : public std::unary_function<Item*, bool> } }; -Inventory::Inventory() +Inventory::Inventory(int size): + mSize(size) { - mItems = new Item*[INVENTORY_SIZE]; - std::fill_n(mItems, INVENTORY_SIZE, (Item*) 0); + mItems = new Item*[mSize]; + std::fill_n(mItems, mSize, (Item*) 0); } Inventory::~Inventory() { - for (int i = 0; i < INVENTORY_SIZE; i++) + for (int i = 0; i < mSize; i++) delete mItems[i]; delete [] mItems; @@ -58,7 +58,7 @@ Item* Inventory::getItem(int index) const Item* Inventory::findItem(int itemId) const { - for (int i = 0; i < INVENTORY_SIZE; i++) + for (int i = 0; i < mSize; i++) { if (mItems[i] && mItems[i]->getId() == itemId) return mItems[i]; @@ -73,19 +73,11 @@ void Inventory::addItem(int id, int quantity, bool equipment) void Inventory::setItem(int index, int id, int quantity, bool equipment) { - if (index < 0 || index >= INVENTORY_SIZE) { + if (index < 0 || index >= mSize) { logger->log("Warning: invalid inventory index: %d", index); return; } - /* TODO: Check where to reenable this code. - // Dont stack equipment other than arrows. - if (equipment && !(id == 1199 || id == 529)) - mItems[index].setQuantity(quantity); - else - mItems[index].increaseQuantity(quantity); - */ - if (!mItems[index] && id > 0) { Item *item = new Item(id, quantity, equipment); item->setInvIndex(index); @@ -101,14 +93,14 @@ void Inventory::setItem(int index, int id, int quantity, bool equipment) void Inventory::clear() { - for (int i = 0; i < INVENTORY_SIZE; i++) { + for (int i = 0; i < mSize; i++) { removeItemAt(i); } } void Inventory::removeItem(int id) { - for (int i = 0; i < INVENTORY_SIZE; i++) { + for (int i = 0; i < mSize; i++) { if (mItems[i] && mItems[i]->getId() == id) { removeItemAt(i); } @@ -123,7 +115,7 @@ void Inventory::removeItemAt(int index) bool Inventory::contains(Item *item) const { - for (int i = 0; i < INVENTORY_SIZE; i++) { + for (int i = 0; i < mSize; i++) { if (mItems[i] && mItems[i]->getId() == item->getId()) { return true; } @@ -134,19 +126,19 @@ bool Inventory::contains(Item *item) const int Inventory::getFreeSlot() const { - Item **i = std::find_if(mItems + 2, mItems + INVENTORY_SIZE, + Item **i = std::find_if(mItems + 2, mItems + mSize, std::not1(SlotUsed())); - return (i == mItems + INVENTORY_SIZE) ? -1 : (i - mItems); + return (i == mItems + mSize) ? -1 : (i - mItems); } int Inventory::getNumberOfSlotsUsed() const { - return count_if(mItems, mItems + INVENTORY_SIZE, SlotUsed()); + return count_if(mItems, mItems + mSize, SlotUsed()); } int Inventory::getLastUsedSlot() const { - for (int i = INVENTORY_SIZE - 1; i >= 0; i--) { + for (int i = mSize - 1; i >= 0; i--) { if (SlotUsed()(mItems[i])) { return i; } diff --git a/src/inventory.h b/src/inventory.h index 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..7bfbc88e 100644 --- a/src/itemshortcut.cpp +++ b/src/itemshortcut.cpp @@ -19,11 +19,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "itemshortcut.h" - #include "configuration.h" #include "inventory.h" #include "item.h" +#include "itemshortcut.h" #include "localplayer.h" #include "utils/tostring.h" diff --git a/src/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..1233c37f 100644 --- a/src/joystick.cpp +++ b/src/joystick.cpp @@ -19,9 +19,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "joystick.h" +#include <cassert> #include "configuration.h" +#include "joystick.h" #include "log.h" #include <cassert> diff --git a/src/joystick.h b/src/joystick.h index aebd4b5f..a7090293 100644 --- a/src/joystick.h +++ b/src/joystick.h @@ -30,7 +30,9 @@ class Joystick /** * Number of buttons we can handle. */ - enum { MAX_BUTTONS = 6 }; + enum { + MAX_BUTTONS = 6 + }; /** * Directions, to be used as bitmask values. diff --git a/src/keyboardconfig.cpp b/src/keyboardconfig.cpp index 9ae7ed68..d7288796 100644 --- a/src/keyboardconfig.cpp +++ b/src/keyboardconfig.cpp @@ -19,8 +19,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "keyboardconfig.h" +#include <guichan/sdl/sdlinput.hpp> + #include "configuration.h" +#include "keyboardconfig.h" #include "log.h" #include "gui/setup_keyboard.h" @@ -40,13 +42,14 @@ static KeyData const keyData[KeyboardConfig::KEY_TOTAL] = { {"keyMoveRight", SDLK_RIGHT, "Move Right"}, {"keyAttack", SDLK_LCTRL, "Attack"}, {"keySmilie", SDLK_LALT, "Smilie"}, - {"keyTarget", SDLK_LSHIFT, "Target"}, + {"keyTalk", SDLK_t, "Talk"}, + {"keyTarget", SDLK_LSHIFT, "Stop Attack"}, {"keyTargetClosest", SDLK_a, "Target Closest"}, + {"keyTargetNPC", SDLK_n, "Target NPC"}, {"keyTargetPlayer", SDLK_q, "Target Player"}, {"keyPickup", SDLK_z, "Pickup"}, {"keyHideWindows", SDLK_h, "Hide Windows"}, {"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"}, @@ -56,6 +59,9 @@ static KeyData const keyData[KeyboardConfig::KEY_TOTAL] = { {"keyShortcut7", SDLK_7, "Item Shortcut 7"}, {"keyShortcut8", SDLK_8, "Item Shortcut 8"}, {"keyShortcut9", SDLK_9, "Item Shortcut 9"}, + {"keyShortcut10", SDLK_0, "Item Shortcut 10"}, + {"keyShortcut11", SDLK_MINUS, "Item Shortcut 11"}, + {"keyShortcut12", SDLK_EQUALS, "Item Shortcut 12"}, {"keyWindowStatus", SDLK_F2, "Status Window"}, {"keyWindowInventory", SDLK_F3, "Inventory Window"}, {"keyWindowEquipment", SDLK_F4, "Equipment WIndow"}, @@ -64,7 +70,21 @@ static KeyData const keyData[KeyboardConfig::KEY_TOTAL] = { {"keyWindowChat", SDLK_F7, "Chat Window"}, {"keyWindowShortcut", SDLK_F8, "Item Shortcut Window"}, {"keyWindowSetup", SDLK_F9, "Setup Window"}, - {"keyWindowDebug", SDLK_F10, "Debug Window"} + {"keyWindowDebug", SDLK_F10, "Debug Window"}, + {"keyWindowEmote", SDLK_F11, "Emote Window"}, + {"keyWindowEmoteBar", SDLK_F12, "Emote Shortcut Window"}, + {"keyEmoteShortcut1", SDLK_1, "Emote Shortcut 1"}, + {"keyEmoteShortcut2", SDLK_2, "Emote Shortcut 2"}, + {"keyEmoteShortcut3", SDLK_3, "Emote Shortcut 3"}, + {"keyEmoteShortcut4", SDLK_4, "Emote Shortcut 4"}, + {"keyEmoteShortcut5", SDLK_5, "Emote Shortcut 5"}, + {"keyEmoteShortcut6", SDLK_6, "Emote Shortcut 6"}, + {"keyEmoteShortcut7", SDLK_7, "Emote Shortcut 7"}, + {"keyEmoteShortcut8", SDLK_8, "Emote Shortcut 8"}, + {"keyEmoteShortcut9", SDLK_9, "Emote Shortcut 9"}, + {"keyEmoteShortcut10", SDLK_0, "Emote Shortcut 10"}, + {"keyEmoteShortcut11", SDLK_MINUS, "Emote Shortcut 11"}, + {"keyEmoteShortcut12", SDLK_EQUALS, "Emote Shortcut 12"} }; void KeyboardConfig::init() @@ -110,11 +130,19 @@ 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))) + && mKey[i].value == mKey[j].value + ) { return true; } @@ -140,6 +168,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..2077126d 100644 --- a/src/keyboardconfig.h +++ b/src/keyboardconfig.h @@ -24,6 +24,8 @@ #include <string> +#include <guichan/sdl/sdlinput.hpp> + #include "gui/sdlinput.h" #include "gui/setup_keyboard.h" @@ -101,6 +103,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) @@ -148,14 +155,15 @@ class KeyboardConfig 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_SHORTCUT_1, KEY_SHORTCUT_2, KEY_SHORTCUT_3, @@ -165,6 +173,9 @@ class KeyboardConfig KEY_SHORTCUT_7, KEY_SHORTCUT_8, KEY_SHORTCUT_9, + KEY_SHORTCUT_10, + KEY_SHORTCUT_11, + KEY_SHORTCUT_12, KEY_WINDOW_STATUS, KEY_WINDOW_INVENTORY, KEY_WINDOW_EQUIPMENT, @@ -174,6 +185,20 @@ 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_TOTAL }; diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 07044ce7..15f5b6b2 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -18,15 +18,17 @@ * 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 "inventory.h" #include "item.h" +#include "localplayer.h" #include "main.h" +#include "monster.h" #include "particle.h" #include "sound.h" #include "monster.h" @@ -38,10 +40,16 @@ #include "net/messageout.h" #include "net/protocol.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), @@ -60,20 +68,52 @@ LocalPlayer::LocalPlayer(Uint32 id, Uint16 job, Map *map): mXp(0), mNetwork(0), mTarget(NULL), mPickUpTarget(NULL), mTrading(false), mGoingToTarget(false), - mLastAction(-1), - mWalkingDir(0), mDestX(0), mDestY(0), - mInventory(new Inventory) + mTargetTime(-1), mLastAction(-1), + mLastTarget(-1), mWalkingDir(0), + mDestX(0), mDestY(0), + mInventory(new Inventory(INVENTORY_SIZE)), + mStorage(new Inventory(STORAGE_SIZE)) { + // 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 +131,6 @@ void LocalPlayer::logic() mFrame = (get_elapsed_time(mWalkTime) * frames) / mAttackSpeed; if (mFrame >= frames) { nextStep(); - attack(); } break; } @@ -100,10 +139,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 +334,19 @@ void LocalPlayer::walk(unsigned char dir) void LocalPlayer::setTarget(Being *target) { - if (target == mTarget) - { + if (mLastTarget != -1 || target == this) return; + mLastTarget = tick_time; + + if ((target == NULL) || target == mTarget) + { + target = NULL; + mKeepAttacking = false; + mTargetTime = -1; + } + if (target) + { + mTargetTime = tick_time; } if (mTarget && mTarget->getType() == Being::MONSTER) { @@ -384,25 +488,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,8 +522,13 @@ 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) { @@ -434,11 +543,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 +601,7 @@ bool LocalPlayer::withinAttackRange(Being *target) void LocalPlayer::setGotoTarget(Being *target) { + mLastTarget = -1; setTarget(target); mGoingToTarget = true; setDestination(target->mX, target->mY); @@ -527,3 +648,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..55e12bf1 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -25,11 +25,16 @@ #include <vector> #include "player.h" +#include "simpleanimation.h" // TODO move into some sane place... #define MAX_SLOT 2 +#define INVENTORY_SIZE 102 +#define STORAGE_SIZE 301 + class FloorItem; +class ImageSet; class Inventory; class Item; class Network; @@ -54,7 +59,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 +76,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 +128,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 +184,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 +222,20 @@ 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]; + protected: virtual void handleStatusEffect(StatusEffect *effect, int effectId); @@ -211,15 +249,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/main.cpp b/src/main.cpp index 0291fd86..6866093f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,48 +19,44 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "main.h" - #include <getopt.h> #include <iostream> #include <physfs.h> +#include <SDL_image.h> #include <unistd.h> #include <vector> -#include <SDL_image.h> #include <guichan/actionlistener.hpp> + +#include <guichan/sdl/sdlinput.hpp> + #include <guichan/widgets/label.hpp> #include <libxml/parser.h> -#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/char_server.h" #include "gui/char_select.h" +#include "gui/colour.h" #include "gui/gui.h" #include "gui/login.h" #include "gui/ok_dialog.h" @@ -77,6 +73,8 @@ #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" @@ -87,7 +85,24 @@ #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 +138,7 @@ CharServerHandler charServerHandler; LoginData loginData; LockedArray<LocalPlayer*> charInfo(MAX_SLOT + 1); +Colour *textColour; // This anonymous namespace hides whatever is inside from other modules. namespace { @@ -168,10 +184,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 +200,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 { - logger->log("Error: Invalid update host: %s", updateHost.c_str()); - errorMessage = "Invalid update host: " + updateHost; + 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; state = ERROR_STATE; } - } else { - logger->log("Warning: no protocol was specified for the update host"); - updatesDir = "updates/" + updateHost; + } + else + { + logger->log(_("Warning: no protocol was specified for the update host")); + 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)) { - logger->log("Error: %s/%s can't be made, but doesn't exist!", + 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!"; + errorMessage = _("Error creating updates directory!"); state = ERROR_STATE; +#endif } } } @@ -219,7 +268,7 @@ void init_engine(const Options &options) GetLastError() != ERROR_ALREADY_EXISTS) #elif defined __APPLE__ // Use Application Directory instead of .tmw - homeDir = std::string(PHYSFS_getUserDir()) + + homeDir = std::string(PHYSFS_getUserDir()) + "/Library/Application Support/The Mana World"; if ((mkdir(homeDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) && (errno != EEXIST)) @@ -230,7 +279,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); } @@ -239,15 +288,15 @@ void init_engine(const Options &options) logger->setLogFile(homeDir + std::string("/tmw.log")); #ifdef PACKAGE_VERSION - logger->log("Starting The Mana World Version %s", PACKAGE_VERSION); + logger->log(_("Starting The Mana World Version %s"), PACKAGE_VERSION); #else - logger->log("Starting The Mana World - Version not defined"); + logger->log(_("Starting The Mana World - Version not defined")); #endif // Initialize SDL - logger->log("Initializing 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); } @@ -260,7 +309,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); } @@ -280,7 +329,7 @@ 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); @@ -290,9 +339,9 @@ void init_engine(const Options &options) #endif // Fill configuration with defaults - logger->log("Initializing configuration..."); - config.setValue("host", "server.themanaworld.org"); - config.setValue("port", 6901); + logger->log(_("Initializing configuration...")); + config.setValue("host", "www.themanaworld.org"); + config.setValue("port", 21001); config.setValue("hwaccel", 0); #if (defined __APPLE__ || defined WIN32) && defined USE_OPENGL config.setValue("opengl", 1); @@ -312,24 +361,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); } @@ -364,17 +413,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); } @@ -385,6 +434,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 */ @@ -401,7 +453,7 @@ void init_engine(const Options &options) catch (const char *err) { state = ERROR_STATE; errorMessage = err; - logger->log("Warning: %s", err); + logger->log(_("Warning: %s"), err); } // Initialize keyboard @@ -416,6 +468,7 @@ void exit_engine() { // Before config.write() since it writes the shortcuts to the config delete itemShortcut; + delete emoteShortcut; config.write(); @@ -429,6 +482,8 @@ void exit_engine() sound.close(); // Unload XML databases + ColorDB::unload(); + EmoteDB::unload(); ItemDB::unload(); MonsterDB::unload(); NPCDB::unload(); @@ -440,27 +495,27 @@ 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; + << _("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 + << _(" -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 } @@ -469,16 +524,16 @@ void parseOptions(int argc, char *argv[], Options &options) const char *optstring = "hvud:U:P:Dp:C:H:"; const struct option long_options[] = { - { "help", no_argument, 0, 'h' }, - { "version", no_argument, 0, 'v' }, - { "skipupdate", no_argument, 0, 'u' }, + { "configfile", required_argument, 0, 'C' }, { "data", required_argument, 0, 'd' }, - { "username", required_argument, 0, 'U' }, - { "password", required_argument, 0, 'P' }, { "default", no_argument, 0, 'D' }, { "playername", required_argument, 0, 'p' }, - { "configfile", required_argument, 0, 'C' }, + { "password", required_argument, 0, 'P' }, + { "help", no_argument, 0, 'h' }, { "updatehost", required_argument, 0, 'H' }, + { "skipupdate", no_argument, 0, 'u' }, + { "username", required_argument, 0, 'U' }, + { "version", no_argument, 0, 'v' }, { 0 } }; @@ -490,36 +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; } } @@ -556,8 +611,8 @@ struct ErrorListener : public gcn::ActionListener // TODO Find some nice place for these functions void accountLogin(Network *network, LoginData *loginData) { - logger->log("Trying to connect to account server..."); - logger->log("Username is %s", loginData->username.c_str()); + logger->log(_("Trying to connect to account server...")); + logger->log(_("Username is %s"), loginData->username.c_str()); network->connect(loginData->hostname, loginData->port); network->registerHandler(&loginHandler); loginHandler.setLoginData(loginData); @@ -597,9 +652,21 @@ void accountLogin(Network *network, LoginData *loginData) config.setValue("remember", loginData->remember); } +inline int MIN(int x, int y) +{ + return x < y ? x : y; +} + +void positionDialog(Window *dialog, int screenWidth, int screenHeight) +{ + dialog->setPosition( + MIN(screenWidth * 5 / 8, screenWidth - dialog->getWidth()), + MIN(screenHeight * 5 / 8, screenHeight - dialog->getHeight())); +} + void charLogin(Network *network, LoginData *loginData) { - logger->log("Trying to connect to char server..."); + logger->log(_("Trying to connect to char server...")); network->connect(loginData->hostname, loginData->port); network->registerHandler(&charServerHandler); charServerHandler.setCharInfo(&charInfo); @@ -621,14 +688,14 @@ void charLogin(Network *network, LoginData *loginData) void mapLogin(Network *network, LoginData *loginData) { - logger->log("Memorizing selected character %s", + logger->log(_("Memorizing selected character %s"), player_node->getName().c_str()); config.setValue("lastCharacter", player_node->getName()); MessageOut outMsg(network); - logger->log("Trying to connect to map server..."); - logger->log("Map: %s", map_path.c_str()); + logger->log(_("Trying to connect to map server...")); + logger->log(_("Map: %s"), map_path.c_str()); network->connect(loginData->hostname, loginData->port); network->registerHandler(&mapLoginHandler); @@ -697,6 +764,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 + textColour = new Colour(); + Game *game = NULL; Window *currentDialog = NULL; Image *login_wallpaper = NULL; @@ -707,7 +777,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(), @@ -729,12 +799,34 @@ 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"; + + 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 @@ -770,16 +862,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); @@ -808,7 +890,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 @@ -847,9 +929,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; @@ -861,33 +946,49 @@ int main(int argc, char *argv[]) state = ACCOUNT_STATE; } else { currentDialog = new LoginDialog(&loginData); + positionDialog(currentDialog, screenWidth, + screenHeight); } break; case REGISTER_STATE: logger->log("State: REGISTER"); currentDialog = new RegisterDialog(&loginData); + positionDialog(currentDialog, screenWidth, screenHeight); break; case CHAR_SERVER_STATE: logger->log("State: CHAR_SERVER"); + + if (n_server == 1) + { + SERVER_INFO *si = *server_info; + loginData.hostname = iptostring(si->address); + loginData.port = si->port; + loginData.updateHost = si->updateHost; + state = UPDATE_STATE; + } + else { int nextState = (options.skipUpdate) ? LOADDATA_STATE : UPDATE_STATE; currentDialog = new ServerSelectDialog(&loginData, - nextState); - } - if (options.chooseDefault || options.playername != "") { - ((ServerSelectDialog*) currentDialog)->action( - gcn::ActionEvent(NULL, "ok")); + nextState); + positionDialog(currentDialog, screenWidth, + screenHeight); + if (options.chooseDefault || options.playername != "") + { + ((ServerSelectDialog*) currentDialog)->action( + gcn::ActionEvent(NULL, "ok")); + } } break; - case CHAR_SELECT_STATE: logger->log("State: CHAR_SELECT"); currentDialog = new CharSelectDialog(network, &charInfo, (loginData.sex == 0) ? GENDER_FEMALE : GENDER_MALE); + positionDialog(currentDialog, screenWidth, screenHeight); if (((CharSelectDialog*) currentDialog)-> selectByName(options.playername)) @@ -899,6 +1000,7 @@ int main(int argc, char *argv[]) if (options.chooseDefault) ((CharSelectDialog*) currentDialog)->action( gcn::ActionEvent(NULL, "ok")); + break; case GAME_STATE: @@ -936,13 +1038,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(); @@ -979,8 +1089,15 @@ int main(int argc, char *argv[]) break; } } + /* + * This loop can really stress the CPU, for no reason since it's + * just constantly redrawing the wallpaper. Added the following + * usleep to limit it to 20 FPS during the login sequence + */ + usleep(50000); } + delete textColour; #ifdef PACKAGE_VERSION delete versionLabel; #endif @@ -30,8 +30,8 @@ #include "winver.h" #endif -#ifndef TMW_DATADIR -#define TMW_DATADIR "" +#ifndef AETHYRA_DATADIR +#define AETHYRA_DATADIR "" #endif /* diff --git a/src/map.cpp b/src/map.cpp index f0c84159..17c28180 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" @@ -136,6 +136,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 +257,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 +549,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); + } } } @@ -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..68ed22f8 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -19,14 +19,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "monster.h" - #include "animatedsprite.h" #include "game.h" -#include "sound.h" +#include "localplayer.h" +#include "monster.h" #include "particle.h" +#include "sound.h" #include "text.h" -#include "localplayer.h" #include "resources/monsterdb.h" @@ -44,6 +43,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 +61,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() +Monster::~Monster() { if (mText) { - player_node->setTarget(0); + delete mText; + player_node->setTarget(NULL); } } @@ -99,9 +104,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 +122,34 @@ void Monster::setAction(Uint8 action) case ATTACK: currentAction = ACTION_ATTACK; mSprites[BASE_SPRITE]->reset(); + + //attack particle effect + particleEffect = getInfo().getAttackParticleEffect(); + if (particleEffect != "" && mParticleEffects) + { + switch (mDirection) + { + case DOWN: rotation = 0; break; + case LEFT: rotation = 90; break; + case UP: rotation = 180; break; + case RIGHT: rotation = 270; break; + default: break; + } + Particle *p; + p = particleEngine->addEffect( + particleEffect, 0, 0, rotation); + controlParticle(p); + } break; case STAND: - currentAction = ACTION_STAND; - break; + currentAction = ACTION_STAND; + break; case HURT: - // Not implemented yet - break; + // Not implemented yet + break; + case SIT: + // Also not implemented yet + break; } if (currentAction != ACTION_INVALID) @@ -164,13 +192,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..8aaede44 100644 --- a/src/net/beinghandler.cpp +++ b/src/net/beinghandler.cpp @@ -19,24 +19,24 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "beinghandler.h" - +#include <iostream> #include <SDL_types.h> +#include "beinghandler.h" #include "messagein.h" #include "protocol.h" #include "../being.h" #include "../beingmanager.h" +#include "../effectmanager.h" #include "../game.h" #include "../localplayer.h" #include "../log.h" #include "../main.h" +#include "../npc.h" #include "../particle.h" -#include "../sound.h" -#include <iostream> #include "../player_relations.h" -#include "../npc.h" +#include "../sound.h" const int EMOTION_TIME = 150; /**< Duration of emotion icon */ @@ -73,6 +73,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 +212,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 +240,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 +273,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 +445,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 +481,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 +508,11 @@ void BeingHandler::handleMessage(MessageIn *msg) case SMSG_PLAYER_MOVE_TO_ATTACK: /* - * This is an *advisory* message, telling the client that - * it needs to move the character before attacking - * a target (out of range, obstruction in line of fire). - * We can safely ignore this... - */ + * This is an *advisory* message, telling the client that + * it needs to move the character before attacking + * a target (out of range, obstruction in line of fire). + * We can safely ignore this... + */ break; case 0x0119: diff --git a/src/net/beinghandler.h b/src/net/beinghandler.h index 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..5292b6f9 100644 --- a/src/net/buysellhandler.cpp +++ b/src/net/buysellhandler.cpp @@ -19,10 +19,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "buysellhandler.h" - #include <SDL_types.h> +#include "buysellhandler.h" #include "messagein.h" #include "protocol.h" @@ -37,8 +36,8 @@ #include "../gui/sell.h" extern BuyDialog *buyDialog; -extern SellDialog *sellDialog; extern Window *buySellDialog; +extern SellDialog *sellDialog; BuySellHandler::BuySellHandler() { diff --git a/src/net/charserverhandler.cpp b/src/net/charserverhandler.cpp index cfa52c38..909100e5 100644 --- a/src/net/charserverhandler.cpp +++ b/src/net/charserverhandler.cpp @@ -20,19 +20,24 @@ */ #include "charserverhandler.h" - #include "messagein.h" #include "network.h" #include "protocol.h" +#include "../extensions.h" #include "../game.h" #include "../localplayer.h" #include "../log.h" #include "../logindata.h" #include "../main.h" -#include "../gui/ok_dialog.h" #include "../gui/char_select.h" +#include "../gui/ok_dialog.h" + +/* + * Yeah, this is a global. Get over it. + */ +struct EXTENSIONS extensions; CharServerHandler::CharServerHandler(): mCharCreateDialog(0) @@ -54,6 +59,7 @@ CharServerHandler::CharServerHandler(): void CharServerHandler::handleMessage(MessageIn *msg) { int slot; + int flags; LocalPlayer *tempPlayer; logger->log("CharServerHandler: Packet ID: %x, Length: %d", @@ -61,8 +67,13 @@ void CharServerHandler::handleMessage(MessageIn *msg) switch (msg->getId()) { case 0x006b: - // Skip length word and an additional mysterious 20 bytes - msg->skip(2 + 20); + msg->skip(2); // Length word + flags = msg->readInt32(); // Aethyra extensions flags + logger->log("Server flags are: %x", flags); + extensions.aethyra_inventory = (bool)(flags & 0x01); + extensions.aethyra_spells = (bool)(flags & 0x02); + extensions.aethyra_misc = (bool)(flags & 0x04); + msg->skip(16); // Unused // Derive number of characters from message length n_character = (msg->getLength() - 24) / 106; diff --git a/src/net/chathandler.cpp b/src/net/chathandler.cpp index d25d4bcd..bf5a5a37 100644 --- a/src/net/chathandler.cpp +++ b/src/net/chathandler.cpp @@ -19,11 +19,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "chathandler.h" - #include <SDL_types.h> #include <string> +#include "chathandler.h" #include "messagein.h" #include "protocol.h" diff --git a/src/net/equipmenthandler.cpp b/src/net/equipmenthandler.cpp index e5bbf6fe..973de0f6 100644 --- a/src/net/equipmenthandler.cpp +++ b/src/net/equipmenthandler.cpp @@ -20,7 +20,6 @@ */ #include "equipmenthandler.h" - #include "messagein.h" #include "protocol.h" @@ -107,7 +106,10 @@ void EquipmentHandler::handleMessage(MessageIn *msg) break; } - // Unequip any existing equipped item in this position + /* + * An item may occupy more than 1 slot. If so, it's + * only shown as equipped on the *first* slot. + */ mask = 1; position = 0; while (!(equipPoint & mask)) { @@ -115,7 +117,10 @@ void EquipmentHandler::handleMessage(MessageIn *msg) position++; } logger->log("Position %i", position); - item = player_node->getInventory()->getItem(player_node->mEquipment->getEquipment(position)); + + item = player_node->getInventory()->getItem(player_node->mEquipment->getEquipment(position)); + + // Unequip any existing equipped item in this position if (item) { item->setEquipped(false); } @@ -152,24 +157,11 @@ void EquipmentHandler::handleMessage(MessageIn *msg) item->setEquipped(false); - switch (item->getId()) { - case 529: - case 1199: - player_node->mEquipment->setArrows(0); - break; - case 521: - case 522: - case 530: - case 536: - case 1200: - case 1201: - player_node->setSprite(Being::WEAPON_SPRITE, 0); - // TODO: Why this break? Shouldn't a weapon be - // unequipped in inventory too? - break; - default: - player_node->mEquipment->removeEquipment(position); - break; + if (equipPoint & 0x8000) { // Arrows + player_node->mEquipment->setArrows(0); + } + else { + player_node->mEquipment->removeEquipment(position); } logger->log("Unequipping: %i %i(%i) %i", index, equipPoint, type, position); @@ -186,12 +178,12 @@ void EquipmentHandler::handleMessage(MessageIn *msg) break; item = inventory->getItem(index); - if (!item) - break; - item->setEquipped(true); - player_node->mEquipment->setArrows(index); - logger->log("Arrows equipped: %i", index); + if (item) { + item->setEquipped(true); + player_node->mEquipment->setArrows(index); + logger->log("Arrows equipped: %i", index); + } break; } } diff --git a/src/net/inventoryhandler.cpp b/src/net/inventoryhandler.cpp index cd9b5ce0..3ce0899a 100644 --- a/src/net/inventoryhandler.cpp +++ b/src/net/inventoryhandler.cpp @@ -19,22 +19,22 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "inventoryhandler.h" - #include <SDL_types.h> +#include "inventoryhandler.h" #include "messagein.h" #include "protocol.h" -#include "../resources/iteminfo.h" +#include "../inventory.h" #include "../item.h" #include "../itemshortcut.h" #include "../localplayer.h" #include "../log.h" -#include "../inventory.h" #include "../gui/chat.h" +#include "../resources/iteminfo.h" + #include "../utils/tostring.h" InventoryHandler::InventoryHandler() @@ -45,6 +45,12 @@ InventoryHandler::InventoryHandler() SMSG_PLAYER_INVENTORY_REMOVE, SMSG_PLAYER_INVENTORY_USE, SMSG_ITEM_USE_RESPONSE, + SMSG_PLAYER_STORAGE_ITEMS, + SMSG_PLAYER_STORAGE_EQUIP, + SMSG_PLAYER_STORAGE_STATUS, + SMSG_PLAYER_STORAGE_ADD, + SMSG_PLAYER_STORAGE_REMOVE, + SMSG_PLAYER_STORAGE_CLOSE, 0 }; handledMessages = _messages; @@ -53,35 +59,69 @@ InventoryHandler::InventoryHandler() void InventoryHandler::handleMessage(MessageIn *msg) { Sint32 number; - Sint16 index, amount, itemId, equipType; + Sint16 index, amount, itemId, equipType, arrow; + Sint16 identified, cards[4], itemType; Inventory *inventory = player_node->getInventory(); + Inventory *storage = player_node->getStorage(); switch (msg->getId()) { case SMSG_PLAYER_INVENTORY: - // Only called on map load / warp. First reset all items - // to not load them twice on map change. - inventory->clear(); + case SMSG_PLAYER_STORAGE_ITEMS: + case SMSG_PLAYER_STORAGE_EQUIP: + switch (msg->getId()) { + case SMSG_PLAYER_INVENTORY: + // Clear inventory - this will be a complete refresh + inventory->clear(); + break; + case SMSG_PLAYER_STORAGE_ITEMS: + /* + * This packet will always be followed by a + * SMSG_PLAYER_STORAGE_EQUIP packet. The two packets + * together comprise a complete refresh of storage, so + * clear storage here + */ + storage->clear(); + logger->log("Received SMSG_PLAYER_STORAGE_ITEMS"); + break; + default: + logger->log("Received SMSG_PLAYER_STORAGE_EQUIP"); + break; + } msg->readInt16(); // length number = (msg->getLength() - 4) / 18; - for (int loop = 0; loop < number; loop++) - { + for (int loop = 0; loop < number; loop++) { index = msg->readInt16(); itemId = msg->readInt16(); - msg->readInt8(); // type - msg->readInt8(); // identify flag - amount = msg->readInt16(); - msg->skip(2); // unknown - msg->skip(8); // card (4 shorts) - - inventory->setItem(index, itemId, amount, false); - - // Trick because arrows are not considered equipment - if (itemId == 1199 || itemId == 529) - { - if (Item *item = inventory->getItem(index)) - item->setEquipment(true); + itemType = msg->readInt8(); + identified = msg->readInt8(); + if (msg->getId() == SMSG_PLAYER_STORAGE_EQUIP) { + amount = 1; + msg->readInt16(); // Equip Point? + } else { + amount = msg->readInt16(); + } + arrow = msg->readInt16(); + if (msg->getId() == SMSG_PLAYER_STORAGE_EQUIP) { + msg->readInt8(); // Attribute (broken) + msg->readInt8(); // Refine level + } + for (int i = 0; i < 4; i++) + cards[i] = msg->readInt16(); + + if (msg->getId() == SMSG_PLAYER_INVENTORY) { + inventory->setItem(index, itemId, amount, false); + + // Trick because arrows are not considered equipment + if (arrow & 0x8000) { + if (Item *item = inventory->getItem(index)) + item->setEquipment(true); + } + } else { + logger->log("Index:%d, ID:%d, Type:%d, Identified:%d, Qty:%d, Cards:%d, %d, %d, %d", + index, itemId, itemType, identified, amount, cards[0], cards[1], cards[2], cards[3]); + storage->setItem(index, itemId, amount, false); } } break; @@ -90,12 +130,13 @@ void InventoryHandler::handleMessage(MessageIn *msg) index = msg->readInt16(); amount = msg->readInt16(); itemId = msg->readInt16(); - msg->readInt8(); // identify flag + identified = msg->readInt8(); msg->readInt8(); // attribute msg->readInt8(); // refine - msg->skip(8); // card + for (int i = 0; i < 4; i++) + cards[i] = msg->readInt16(); equipType = msg->readInt16(); - msg->readInt8(); // type + itemType = msg->readInt8(); if (msg->readInt8() > 0) { chatWindow->chatLog("Unable to pick up item", BY_SERVER); @@ -147,5 +188,38 @@ void InventoryHandler::handleMessage(MessageIn *msg) item->setQuantity(amount); } break; + + case SMSG_PLAYER_STORAGE_STATUS: + /* + * Basic slots used vs total slots info + * We don't really need this information, but this is + * the closest we get to an "Open Storage" packet + * from the server. It always comes after the two + * SMSG_PLAYER_STORAGE_... packets that update + * storage contents. + */ + logger->log("Received SMSG_PLAYER_STORAGE_STATUS"); + player_node->setInStorage(true); + break; + + case SMSG_PLAYER_STORAGE_ADD: + /* + * Move an item into storage + */ + break; + + case SMSG_PLAYER_STORAGE_REMOVE: + /* + * Move an item out of storage + */ + break; + + case SMSG_PLAYER_STORAGE_CLOSE: + /* + * Storage access has been closed + */ + player_node->setInStorage(false); + logger->log("Received SMSG_PLAYER_STORAGE_CLOSE"); + break; } } diff --git a/src/net/itemhandler.cpp b/src/net/itemhandler.cpp index 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 646c5f3c..f240618d 100644 --- a/src/net/loginhandler.cpp +++ b/src/net/loginhandler.cpp @@ -20,7 +20,6 @@ */ #include "loginhandler.h" - #include "messagein.h" #include "network.h" #include "protocol.h" @@ -35,7 +34,7 @@ extern SERVER_INFO **server_info; LoginHandler::LoginHandler() { static const Uint16 _messages[] = { - SMSG_UPDATE_HOST, + 0x0063, 0x0069, 0x006a, 0 @@ -87,7 +86,7 @@ void LoginHandler::handleMessage(MessageIn *msg) iptostring(server_info[i]->address), server_info[i]->port); } - state = CHAR_SERVER_STATE; + state = CHAR_SERVER_STATE; break; case 0x006a: diff --git a/src/net/loginhandler.h b/src/net/loginhandler.h index a380c767..c847b4c1 100644 --- a/src/net/loginhandler.h +++ b/src/net/loginhandler.h @@ -22,6 +22,8 @@ #ifndef NET_LOGINHANDLER_H #define NET_LOGINHANDLER_H +#include <string> + #include "messagehandler.h" #include <string> diff --git a/src/net/maploginhandler.cpp b/src/net/maploginhandler.cpp index 0d349a6d..1b0919fa 100644 --- a/src/net/maploginhandler.cpp +++ b/src/net/maploginhandler.cpp @@ -20,7 +20,6 @@ */ #include "maploginhandler.h" - #include "messagein.h" #include "protocol.h" diff --git a/src/net/messagehandler.cpp b/src/net/messagehandler.cpp index 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..941995c9 100644 --- a/src/net/network.cpp +++ b/src/net/network.cpp @@ -19,15 +19,14 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "network.h" +#include <sstream> #include "messagehandler.h" #include "messagein.h" +#include "network.h" #include "../log.h" -#include <sstream> - /** Warning: buffers and other variables are shared, so there can be only one connection active at a time */ @@ -432,8 +431,7 @@ char *iptostring(int address) 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..c035f55c 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; diff --git a/src/net/npchandler.cpp b/src/net/npchandler.cpp index b0314e47..82b07d41 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,15 +69,15 @@ void NPCHandler::handleMessage(MessageIn *msg) case SMSG_NPC_MESSAGE: msg->readInt16(); // length id = msg->readInt32(); + player_node->setAction(LocalPlayer::STAND); current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); npcTextDialog->addText(msg->readString(msg->getLength() - 8)); npcListDialog->setVisible(false); npcTextDialog->setVisible(true); break; - case SMSG_NPC_CLOSE: + case SMSG_NPC_CLOSE: id = msg->readInt32(); - dynamic_cast<NPC*>(beingManager->findBeing(id)); if (current_npc == dynamic_cast<NPC*>(beingManager->findBeing(id))) current_npc = NULL; break; diff --git a/src/net/partyhandler.cpp b/src/net/partyhandler.cpp new file mode 100644 index 00000000..d4b7455b --- /dev/null +++ b/src/net/partyhandler.cpp @@ -0,0 +1,123 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <guichan/actionlistener.hpp> + +#include "partyhandler.h" +#include "protocol.h" +#include "messagein.h" + +#include "../gui/chat.h" +#include "../gui/confirm_dialog.h" + +#include "../beingmanager.h" +#include "../game.h" +#include "../party.h" + +PartyHandler::PartyHandler(Party *party) : mParty(party) +{ + static const Uint16 _messages[] = { + SMSG_PARTY_CREATE, + SMSG_PARTY_INFO, + SMSG_PARTY_INVITE, + SMSG_PARTY_INVITED, + SMSG_PARTY_SETTINGS, + SMSG_PARTY_MEMBER_INFO, + SMSG_PARTY_LEAVE, + SMSG_PARTY_UPDATE_HP, + SMSG_PARTY_UPDATE_COORDS, + SMSG_PARTY_MESSAGE, + 0 + }; + handledMessages = _messages; +} + +void PartyHandler::handleMessage(MessageIn *msg) +{ + switch (msg->getId()) + { + case SMSG_PARTY_CREATE: + mParty->createResponse(msg->readInt8()); + break; + case SMSG_PARTY_INFO: + break; + case SMSG_PARTY_INVITE: + { + std::string nick = msg->readString(24); + int status = msg->readInt8(); + mParty->inviteResponse(nick, status); + break; + } + case SMSG_PARTY_INVITED: + { + int id = msg->readInt32(); + Being *being = beingManager->findBeing(id); + if (being == NULL) + { + break; + } + std::string nick; + int gender = 0; + std::string partyName = ""; + if (being->getType() != Being::PLAYER) + { + nick = ""; + } + else + { + nick = being->getName(); + gender = being->getGender(); + partyName = msg->readString(24); + } + mParty->invitedAsk(nick, gender, partyName); + break; + } + case SMSG_PARTY_SETTINGS: + break; + case SMSG_PARTY_MEMBER_INFO: + break; + case SMSG_PARTY_LEAVE: + { + /*int id = */msg->readInt32(); + std::string nick = msg->readString(24); + /*int fail = */msg->readInt8(); + mParty->leftResponse(nick); + break; + } + case SMSG_PARTY_UPDATE_HP: + break; + case SMSG_PARTY_UPDATE_COORDS: + break; + case SMSG_PARTY_MESSAGE: + { // new block to enable local variables + int msgLength = msg->readInt16() - 8; + if (msgLength <= 0) + { + return; + } + int id = msg->readInt32(); + Being *being = beingManager->findBeing(id); + std::string chatMsg = msg->readString(msgLength); + mParty->receiveChat(being, chatMsg); + } + break; + } +} diff --git a/src/net/partyhandler.h b/src/net/partyhandler.h new file mode 100644 index 00000000..08d85ad0 --- /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..d06f1108 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; @@ -51,7 +52,7 @@ extern SellDialog *sellDialog; extern Window *buySellDialog; static const int MAP_TELEPORT_SCROLL_DISTANCE = 8; /* Max. distance we are willing to scroll after a teleport; - ** everything beyond will reset the port hard. */ + * everything beyond will reset the port hard. */ /** * Listener used for handling the overweigth message. @@ -110,10 +111,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 +134,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 +146,8 @@ void PlayerHandler::handleMessage(MessageIn *msg) /* Scroll if neccessary */ if (!nearby || (abs(x - player_node->mX) > MAP_TELEPORT_SCROLL_DISTANCE) - || (abs(y - player_node->mY) > MAP_TELEPORT_SCROLL_DISTANCE)) { + || (abs(y - player_node->mY) > MAP_TELEPORT_SCROLL_DISTANCE)) + { scrollOffsetX = (x - player_node->mX) * 32; scrollOffsetY = (y - player_node->mY) * 32; } @@ -173,6 +177,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 +190,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 then 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 +208,44 @@ void PlayerHandler::handleMessage(MessageIn *msg) case 0x0031: player_node->HIT = value; break; case 0x0032: player_node->FLEE = value; break; case 0x0035: player_node->mAttackSpeed = value; break; + case 0x0037: player_node->mJobLevel = value; break; } if (player_node->mHp == 0 && deathNotice == NULL) { - deathNotice = new OkDialog("Message", - "You're now dead, press ok to restart"); + static char const *const deadMsg[] = + { + _("You are dead."), + _("We regret to inform you that your character was killed in battle."), + _("You are not that alive anymore."), + _("The cold hands of the grim reaper are grabbing for your soul."), + _("Game Over!"), + _("Insert coin to continue"), + _("No, kids. Your character did not really die. It... err... went to a better place."), + _("Your plan of breaking your enemies weapon by bashing it with your throat failed."), + _("I guess this did not run too well."), + _("Do you want your possessions identified?"), // Nethack reference + _("Sadly, no trace of you was ever found..."), // Secret of Mana reference + _("Annihilated."), // Final Fantasy VI reference + _("Looks like you got your head handed to you."), //Earthbound reference + _("You screwed up again, dump your body down the tubes and get you another one."), // Leisure Suit Larry 1 Reference + _("You're not dead yet. You're just resting."), // Monty Python reference from a couple of skits + _("You are no more."), // Monty Python reference from the dead parrot sketch starting now + _("You have ceased to be."), + _("You've expired and gone to meet your maker."), + _("You're a stiff."), + _("Bereft of life, you rest in peace."), + _("If you weren't so animated, you'd be pushing up the daisies."), + _("Your metabolic processes are now history."), + _("You're off the twig."), + _("You've kicked the bucket."), + _("You've shuffled off your mortal coil, run down the curtain and joined the bleedin' choir invisibile."), + _("You are an ex-player."), + _("You're pining for the fjords.") // Monty Python reference from the dead parrot sketch + }; + std::string message(deadMsg[rand()%27]); + + deathNotice = new OkDialog(_("Message"), message); deathNotice->addActionListener(&deathListener); player_node->setAction(Being::DEAD); } @@ -229,7 +264,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 +328,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,27 +387,14 @@ void PlayerHandler::handleMessage(MessageIn *msg) switch (type) { case 0: - chatWindow->chatLog("Equip arrows first", + chatWindow->chatLog(_("Equip arrows first"), BY_SERVER); break; default: - logger->log("0x013b: Unhandled message %i", type); + logger->log(_("0x013b: Unhandled message %i"), type); break; } } break; - - //Stop walking - //case 0x0088: // Disabled because giving some problems - //if (being = beingManager->findBeing(readInt32(2))) { - // if (being->getId() != player_node->getId()) { - // being->action = STAND; - // being->mFrame = 0; - // set_coordinates(being->coordinates, - // readWord(6), readWord(8), - // get_direction(being->coordinates)); - // } - //} - //break; } } diff --git a/src/net/protocol.h b/src/net/protocol.h index b70834c6..fd08c45d 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_UPDATE_HOST 0x0063 /**< Custom update host packet */ #define SMSG_PLAYER_UPDATE_1 0x01d8 #define SMSG_PLAYER_UPDATE_2 0x01d9 @@ -66,6 +69,7 @@ #define SMSG_BEING_ACTION 0x008a /**< Attack, sit, stand up, ... */ #define SMSG_BEING_CHAT 0x008d /**< A being talks */ #define SMSG_BEING_NAME_RESPONSE 0x0095 /**< Has to be requested */ + #define SMSG_NPC_MESSAGE 0x00b4 #define SMSG_NPC_NEXT 0x00b5 #define SMSG_NPC_CLOSE 0x00b6 @@ -82,6 +86,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 @@ -90,7 +95,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 @@ -99,6 +124,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 @@ -117,6 +144,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..0c7c9205 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..19be4c4d 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -19,15 +19,16 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "npc.h" - #include "animatedsprite.h" #include "graphics.h" +#include "localplayer.h" +#include "npc.h" #include "particle.h" #include "text.h" #include "net/messageout.h" #include "net/protocol.h" + #include "resources/npcdb.h" NPC *current_npc = 0; @@ -36,7 +37,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 +55,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..d3278c1a 100644 --- a/src/openglgraphics.cpp +++ b/src/openglgraphics.cpp @@ -19,30 +19,28 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "main.h" - -#ifdef USE_OPENGL +#include <cstring> +#include <SDL.h> -#ifndef GL_TEXTURE_RECTANGLE_ARB -#define GL_TEXTURE_RECTANGLE_ARB 0x84F5 -#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8 -#endif +#include <guichan/exception.hpp> +#include <guichan/image.hpp> +#include "log.h" +#include "main.h" #include "openglgraphics.h" -#include <cstring> -#include <SDL.h> +#include "resources/image.h" #ifdef __APPLE__ #include <OpenGL/OpenGL.h> #endif -#include <guichan/exception.hpp> -#include <guichan/image.hpp> - -#include "log.h" +#ifdef USE_OPENGL -#include "resources/image.h" +#ifndef GL_TEXTURE_RECTANGLE_ARB +#define GL_TEXTURE_RECTANGLE_ARB 0x84F5 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8 +#endif OpenGLGraphics::OpenGLGraphics(): mAlpha(false), mTexture(false), mColorAlpha(false), diff --git a/src/particle.cpp b/src/particle.cpp index 99a4dd36..a3026994 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -22,13 +22,12 @@ #include <algorithm> #include <cmath> -#include "particle.h" - #include "animationparticle.h" #include "configuration.h" #include "imageparticle.h" #include "log.h" #include "map.h" +#include "particle.h" #include "particleemitter.h" #include "textparticle.h" @@ -251,7 +250,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 +312,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..653e848f 100644 --- a/src/particle.h +++ b/src/particle.h @@ -102,7 +102,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..6300350d 100644 --- a/src/particlecontainer.cpp +++ b/src/particlecontainer.cpp @@ -44,7 +44,6 @@ void ParticleContainer::clear() mNext->clear(); } - void ParticleContainer::moveTo(float x, float y) { if (mNext) diff --git a/src/particleemitter.cpp b/src/particleemitter.cpp index 076bd740..ca9f7bf5 100644 --- a/src/particleemitter.cpp +++ b/src/particleemitter.cpp @@ -19,24 +19,23 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "particleemitter.h" - #include "animationparticle.h" #include "imageparticle.h" #include "log.h" #include "particle.h" +#include "particleemitter.h" #include "resources/animation.h" #include "resources/image.h" -#include "resources/resourcemanager.h" #include "resources/imageset.h" +#include "resources/resourcemanager.h" #include <cmath> #define SIN45 0.707106781f #define DEG_RAD_FACTOR 0.017453293f -ParticleEmitter::ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map *map): +ParticleEmitter::ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map *map, int rotation): mOutputPauseLeft(0), mParticleImage(0) { @@ -102,7 +101,9 @@ ParticleEmitter::ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map * else if (name == "horizontal-angle") { mParticleAngleHorizontal = readParticleEmitterProp(propertyNode, 0.0f); + mParticleAngleHorizontal.minVal += rotation; mParticleAngleHorizontal.minVal *= DEG_RAD_FACTOR; + mParticleAngleHorizontal.maxVal += rotation; mParticleAngleHorizontal.maxVal *= DEG_RAD_FACTOR; mParticleAngleHorizontal.changeAmplitude *= DEG_RAD_FACTOR; } @@ -310,26 +311,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..cc77f215 100644 --- a/src/particleemitter.h +++ b/src/particleemitter.h @@ -44,7 +44,7 @@ class ParticleEmitter /** * Constructor. */ - ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map *map); + ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map *map, int rotation = 0); /** * Copy Constructor (necessary for reference counting of particle images) @@ -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..fde78f8f 100644 --- a/src/particleemitterprop.h +++ b/src/particleemitterprop.h @@ -19,8 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <cstdlib> #include <cmath> +#include <cstdlib> /** * Returns a random numeric value that is larger than or equal min and smaller diff --git a/src/party.cpp b/src/party.cpp new file mode 100644 index 00000000..ecb0ab2d --- /dev/null +++ b/src/party.cpp @@ -0,0 +1,221 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "beingmanager.h" +#include "game.h" +#include "localplayer.h" +#include "party.h" + +#include "gui/chat.h" +#include "gui/confirm_dialog.h" + +#include "net/messageout.h" +#include "net/protocol.h" + +#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 == "") + { + 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 == "") + { + 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 == NULL) + { + 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() +{ + mChat->chatLog(_("/party <command> <params>: Party commands."), BY_SERVER); +} + +void Party::help(const std::string &msg) +{ + if (msg == "") + { + mChat->chatLog(_("Command: /party <command> <args>"), BY_SERVER); + mChat->chatLog(_("where <command> can be one of:"), BY_SERVER); + mChat->chatLog(_(" /new"), BY_SERVER); + mChat->chatLog(_(" /create"), BY_SERVER); + mChat->chatLog(_(" /prefix"), BY_SERVER); + mChat->chatLog(_(" /leave"), BY_SERVER); + mChat->chatLog(_("This command implements the partying function."), + BY_SERVER); + mChat->chatLog(_("Type /help party <command> for further help."), + BY_SERVER); + return; + } + if (msg == "new" || msg == "create") + { + mChat->chatLog(_("Command: /party new <party-name>"), BY_SERVER); + mChat->chatLog(_("Command: /party create <party-name>"), BY_SERVER); + mChat->chatLog(_("These commands create a new party <party-name."), + BY_SERVER); + return; + } + if (msg == "prefix") + { + mChat->chatLog(_("Command: /party prefix <prefix-char>"), BY_SERVER); + mChat->chatLog(_("This command sets the party prefix character."), + BY_SERVER); + mChat->chatLog(_("Any message preceded by <prefix-char> is sent to " + "the party instead of everyone."), BY_SERVER); + mChat->chatLog(_("Command: /party prefix"), BY_SERVER); + mChat->chatLog(_("This command reports the current party prefix " + "character."), BY_SERVER); + return; + } + //if (msg == "settings") + //if (msg == "info") + if (msg == "leave") + { + mChat->chatLog(_("Command: /party leave"), BY_SERVER); + mChat->chatLog(_("This command causes the player to leave the party."), + BY_SERVER); + return; + } + mChat->chatLog(_("Unknown /party command."), BY_SERVER); + mChat->chatLog(_("Type /help party for a list of options."), BY_SERVER); +} diff --git a/src/party.h b/src/party.h new file mode 100644 index 00000000..8d6dce47 --- /dev/null +++ b/src/party.h @@ -0,0 +1,73 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef 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(); + 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..ca8c9f14 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -19,20 +19,19 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "player.h" +#include <iostream> #include "animatedsprite.h" #include "game.h" #include "graphics.h" #include "log.h" +#include "player.h" #include "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 +43,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 +69,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 +143,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); } @@ -187,4 +204,3 @@ void Player::updateCoords() } } - diff --git a/src/player.h b/src/player.h index 7fe2a09c..5fe9963a 100644 --- a/src/player.h +++ b/src/player.h @@ -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..057eea94 100644 --- a/src/player_relations.cpp +++ b/src/player_relations.cpp @@ -19,12 +19,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <algorithm> + #include "beingmanager.h" -#include "player_relations.h" #include "graphics.h" -#include "gui/gui.h" +#include "player_relations.h" -#include <algorithm> +#include "gui/gui.h" #define PLAYER_IGNORE_STRATEGY_NOP "nop" #define PLAYER_IGNORE_STRATEGY_EMOTE0 "emote0" @@ -90,8 +91,7 @@ PlayerRelationsManager::PlayerRelationsManager() : { } -void -PlayerRelationsManager::clear() +void PlayerRelationsManager::clear() { std::vector<std::string> *names = getPlayers(); for (std::vector<std::string>::const_iterator @@ -104,8 +104,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 +114,7 @@ PlayerRelationsManager::getPlayerIgnoreStrategyIndex(const std::string &name) return -1; } -void -PlayerRelationsManager::load() +void PlayerRelationsManager::load() { clear(); @@ -133,8 +131,7 @@ PlayerRelationsManager::load() } -void -PlayerRelationsManager::init() +void PlayerRelationsManager::init() { load(); @@ -142,8 +139,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 +156,7 @@ PlayerRelationsManager::store() config.write(); } -void -PlayerRelationsManager::signalUpdate(const std::string &name) +void PlayerRelationsManager::signalUpdate(const std::string &name) { store(); @@ -169,8 +164,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 +189,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 +215,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 +226,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 +239,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 +250,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 +261,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 +288,7 @@ public: mShortName = PLAYER_IGNORE_STRATEGY_NOP; } - virtual void - ignore(Player *player, unsigned int flags) + virtual void ignore(Player *player, unsigned int flags) { } }; @@ -317,8 +302,7 @@ public: mShortName = "dotdotdot"; } - virtual void - ignore(Player *player, unsigned int flags) + virtual void ignore(Player *player, unsigned int flags) { player->setSpeech("...", 5); } @@ -334,8 +318,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 +334,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..0f8bb4e3 100644 --- a/src/player_relations.h +++ b/src/player_relations.h @@ -22,13 +22,14 @@ #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> + +#include "being.h" +#include "configuration.h" +#include "player.h" 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/recorder.cpp b/src/recorder.cpp new file mode 100644 index 00000000..07d9a771 --- /dev/null +++ b/src/recorder.cpp @@ -0,0 +1,114 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <physfs.h> + +#include "recorder.h" + +#include "gui/buttonbox.h" +#include "gui/chat.h" + +#include "utils/trim.h" + +Recorder::Recorder(ChatWindow *chat) : mChat(chat) +{ + mButtonBox = new ButtonBox("Recording...", "Stop recording", this); + mButtonBox->setY(20); +} + +void Recorder::record(const std::string &msg) +{ + if (mStream.is_open()) + { + mStream << msg << std::endl; + } +} + +void Recorder::respond(const std::string &msg) +{ + std::string msgCopy = msg; + trim(msgCopy); + if (msgCopy == "") + { + if (mStream.is_open()) + { + mStream.close(); + mButtonBox->setVisible(false); + /* + * Message should go after mStream is closed so that it isn't + * recorded. + */ + mChat->chatLog("Finishing recording.", BY_SERVER); + } + else + { + mChat->chatLog("Not currently recording.", BY_SERVER); + } + return; + } + if (mStream.is_open()) + { + mChat->chatLog("Already recording.", BY_SERVER); + } + else + { + /* + * Message should go before mStream is opened so that it isn't + * recorded. + */ + mChat->chatLog("Starting to record...", BY_SERVER); + std::string file = std::string(PHYSFS_getUserDir()) + "/.tmw/" + msgCopy; + + mStream.open(file.c_str(), std::ios_base::trunc); + if (mStream.is_open()) + { + mButtonBox->setVisible(true); + } + else + { + mChat->chatLog("Failed to start recording.", BY_SERVER); + } + } +} + +void Recorder::help() const +{ + mChat->chatLog("/record <filename>: Start recording the chat.", BY_SERVER); +} + +void Recorder::help(const std::string &args) const +{ + mChat->chatLog("Command: /record <filename>", BY_SERVER); + mChat->chatLog("This command starts recording the chat log to the file " + "<filename>.", BY_SERVER); + mChat->chatLog("Command: /record", BY_SERVER); + mChat->chatLog("This command finishes a recording session.", BY_SERVER); +} + +void Recorder::buttonBoxRespond() +{ + respond(""); +} + +Recorder::~Recorder() +{ + delete mButtonBox; +} diff --git a/src/recorder.h b/src/recorder.h new file mode 100644 index 00000000..4a220166 --- /dev/null +++ b/src/recorder.h @@ -0,0 +1,48 @@ +/* + * Aethyra + * Copyright (C) 2008 Aethyra Development Team + * + * This file is part of Aethyra. + * + * 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 "gui/buttonbox.h" + +class ChatWindow; + +class Recorder : public ButtonBoxListener +{ + private: + ChatWindow *mChat; + std::ofstream mStream; + ButtonBox *mButtonBox; + public: + Recorder(ChatWindow *chat); + void record(const std::string &msg); + void respond(const std::string &msg); + void help() const; + void help(const std::string &args) const; + void buttonBoxRespond(); + bool isRecording() {return (bool) mStream.is_open();} + virtual ~Recorder(); +}; +#endif 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..8d7156a9 100644 --- a/src/resources/animation.cpp +++ b/src/resources/animation.cpp @@ -19,10 +19,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "animation.h" - #include <algorithm> +#include "animation.h" + #include "../utils/dtor.h" Animation::Animation(): @@ -30,22 +30,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..541acabe 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() { @@ -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..d769b2b8 100644 --- a/src/resources/buddylist.h +++ b/src/resources/buddylist.h @@ -52,7 +52,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..f80ca6b3 --- /dev/null +++ b/src/resources/colordb.cpp @@ -0,0 +1,125 @@ +/* + * Aethyra + * Copyright 2008 Aethyra Development Team + * + * This file is part of Aethyra. + * + * Aethyra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * Aethyra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Aethyra; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <cassert> +#include <libxml/tree.h> + +#include "colordb.h" + +#include "../log.h" + +#include "../utils/dtor.h" +#include "../utils/gettext.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..65b7f759 --- /dev/null +++ b/src/resources/colordb.h @@ -0,0 +1,52 @@ +/* + * Aethyra + * Copyright 2008 Aethyra Development Team + * + * This file is part of Aethyra. + * + * Aethyra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * Aethyra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Aethyra; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef 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.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..2f43822d --- /dev/null +++ b/src/resources/emotedb.cpp @@ -0,0 +1,143 @@ +/* + * Aethyra + * Copyright 2009 Aethyra Development Team + * + * This file is part of Aethyra. + * + * Aethyra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * Aethyra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Aethyra; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emotedb.h" +#include "resourcemanager.h" + +#include "../log.h" + +#include "../utils/dtor.h" +#include "../utils/gettext.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..0962edad --- /dev/null +++ b/src/resources/emotedb.h @@ -0,0 +1,60 @@ +/* + * Aethyra + * Copyright 2009 Aethyra Development Team + * + * This file is part of Aethyra. + * + * Aethyra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * Aethyra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Aethyra; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef 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..c3310849 100644 --- a/src/resources/image.cpp +++ b/src/resources/image.cpp @@ -21,9 +21,8 @@ #include <SDL_image.h> -#include "image.h" - #include "dye.h" +#include "image.h" #include "../log.h" diff --git a/src/resources/image.h b/src/resources/image.h index 963fbb24..a4048803 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 @@ -96,7 +97,6 @@ class Image : public Resource virtual int getWidth() const { return mBounds.w; } - /** * Returns the height of the image. */ @@ -129,7 +129,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..7895d33d 100644 --- a/src/resources/imageloader.cpp +++ b/src/resources/imageloader.cpp @@ -21,12 +21,12 @@ #include <cassert> #include <string> + #include <guichan/color.hpp> #include <guichan/sdl/sdlpixel.hpp> -#include "imageloader.h" - #include "image.h" +#include "imageloader.h" #include "resourcemanager.h" ProxyImage::ProxyImage(SDL_Surface *s): diff --git a/src/resources/imageset.cpp b/src/resources/imageset.cpp index 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..a9133846 100644 --- a/src/resources/imagewriter.h +++ b/src/resources/imagewriter.h @@ -27,5 +27,5 @@ 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..773febd7 100644 --- a/src/resources/itemdb.cpp +++ b/src/resources/itemdb.cpp @@ -20,10 +20,10 @@ */ #include <cassert> + #include <libxml/tree.h> #include "itemdb.h" - #include "iteminfo.h" #include "resourcemanager.h" @@ -36,6 +36,7 @@ namespace { ItemDB::ItemInfos mItemInfos; + ItemDB::NamedItemInfos mNamedItemInfos; ItemInfo *mUnknown; bool mLoaded = false; } @@ -49,20 +50,20 @@ void ItemDB::load() if (mLoaded) return; - logger->log("Initializing item database..."); + 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) @@ -74,12 +75,12 @@ void ItemDB::load() if (id == 0) { - logger->log("ItemDB: Invalid or missing item ID in items.xml!"); + logger->log(_("ItemDB: Invalid or missing item ID in items.xml!")); continue; } else if (mItemInfos.find(id) != mItemInfos.end()) { - logger->log("ItemDB: Redefinition of item ID %d", id); + logger->log(_("ItemDB: Redefinition of item ID %d"), id); } int type = XML::getProperty(node, "type", 0); @@ -95,6 +96,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 +119,34 @@ void ItemDB::load() } mItemInfos[id] = itemInfo; + if (!name.empty()) + { + NamedItemInfoIterator itr = mNamedItemInfos.find(name); + if (itr == mNamedItemInfos.end()) + { + std::string temp = name; + while (temp[0] == ' ') + { + temp = temp.substr(1, temp.size()); + } + while (temp[temp.size()] == ' ') + { + temp = temp.substr(0, temp.size() - 1); + } + + 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,9 +156,9 @@ 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(weight, 0); // CHECK_PARAM(slot, 0); #undef CHECK_PARAM @@ -139,7 +169,7 @@ void ItemDB::load() void ItemDB::unload() { - logger->log("Unloading item database..."); + logger->log(_("Unloading item database...")); delete mUnknown; mUnknown = NULL; @@ -157,7 +187,24 @@ const ItemInfo& ItemDB::get(int id) if (i == mItemInfos.end()) { - logger->log("ItemDB: Error, unknown item ID# %d", id); + logger->log(_("ItemDB: Error, unknown item ID# %d"), id); + return *mUnknown; + } + else + { + return *(i->second); + } +} + +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 @@ -175,7 +222,6 @@ void loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node) { itemInfo->setSprite(filename, GENDER_MALE); } - if (gender == "female" || gender == "unisex") { itemInfo->setSprite(filename, GENDER_FEMALE); @@ -197,7 +243,7 @@ void loadSoundRef(ItemInfo *itemInfo, xmlNodePtr node) } else { - logger->log("ItemDB: Ignoring unknown sound event '%s'", + logger->log(_("ItemDB: Ignoring unknown sound event '%s'"), event.c_str()); } } diff --git a/src/resources/itemdb.h b/src/resources/itemdb.h index 03fb1eed..e7c23ca2 100644 --- a/src/resources/itemdb.h +++ b/src/resources/itemdb.h @@ -22,10 +22,10 @@ #ifndef ITEM_MANAGER_H #define ITEM_MANAGER_H -#include "iteminfo.h" - #include <map> +#include "iteminfo.h" + /** * The namespace that holds the item information. */ @@ -42,10 +42,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..86725ca2 100644 --- a/src/resources/iteminfo.h +++ b/src/resources/iteminfo.h @@ -54,6 +54,12 @@ class ItemInfo { } + void setId(int id) + { mId = id; } + + int getId() const + { return mId; } + void setName(const std::string &name) { mName = name; } @@ -115,6 +121,7 @@ class ItemInfo char 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..ddf48df0 100644 --- a/src/resources/mapreader.cpp +++ b/src/resources/mapreader.cpp @@ -19,14 +19,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "mapreader.h" - #include <cassert> #include <iostream> #include <zlib.h> -#include "resourcemanager.h" #include "image.h" +#include "mapreader.h" +#include "resourcemanager.h" #include "../log.h" #include "../map.h" @@ -205,14 +204,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 +232,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 +322,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 +366,7 @@ void MapReader::readLayer(xmlNodePtr node, Map *map) while (*charStart) { if (*charStart != ' ' && *charStart != '\t' && - *charStart != '\n') + *charStart != '\n') { *charIndex = *charStart; charIndex++; @@ -461,15 +457,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 +546,7 @@ Tileset *MapReader::readTileset(xmlNodePtr node, } } + delete doc; + return set; } diff --git a/src/resources/mapreader.h b/src/resources/mapreader.h index 8cb2749d..ef945c3f 100644 --- a/src/resources/mapreader.h +++ b/src/resources/mapreader.h @@ -26,8 +26,8 @@ #include <libxml/tree.h> -class Properties; class Map; +class Properties; class Tileset; /** diff --git a/src/resources/monsterdb.cpp b/src/resources/monsterdb.cpp index 5d452db2..c7926260 100644 --- a/src/resources/monsterdb.cpp +++ b/src/resources/monsterdb.cpp @@ -20,12 +20,12 @@ */ #include "monsterdb.h" - #include "resourcemanager.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..."); + 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,7 @@ 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); } @@ -119,11 +118,17 @@ MonsterDB::load() } else { - logger->log("MonsterDB: Warning, sound effect %s for unknown event %s of monster %s", + 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( @@ -151,7 +156,7 @@ const MonsterInfo &MonsterDB::get(int id) if (i == mMonsterInfos.end()) { - logger->log("MonsterDB: Warning, unknown monster ID %d requested", id); + logger->log(_("MonsterDB: Warning, unknown monster ID %d requested"), id); return mUnknown; } else diff --git a/src/resources/monsterdb.h b/src/resources/monsterdb.h index 8555670b..6fbde55f 100644 --- a/src/resources/monsterdb.h +++ b/src/resources/monsterdb.h @@ -31,11 +31,9 @@ */ 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..75464035 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..88572481 100644 --- a/src/resources/npcdb.cpp +++ b/src/resources/npcdb.cpp @@ -20,12 +20,12 @@ */ #include "npcdb.h" - #include "resourcemanager.h" #include "../log.h" #include "../utils/dtor.h" +#include "../utils/gettext.h" #include "../utils/xml.h" namespace @@ -45,17 +45,17 @@ void NPCDB::load() unknownSprite->variant = 0; mUnknown.sprites.push_back(unknownSprite); - logger->log("Initializing NPC database..."); + logger->log(_("Initializing NPC database...")); XML::Document doc("npcs.xml"); xmlNodePtr rootNode = doc.rootNode(); 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")) @@ -64,7 +64,7 @@ void NPCDB::load() int id = XML::getProperty(npcNode, "id", 0); if (id == 0) { - logger->log("NPC Database: NPC with missing ID in npcs.xml!"); + logger->log(_("NPC Database: NPC with missing ID in npcs.xml!")); continue; } @@ -91,8 +91,7 @@ void NPCDB::load() mLoaded = true; } -void -NPCDB::unload() +void NPCDB::unload() { for ( NPCInfosIterator i = mNPCInfos.begin(); i != mNPCInfos.end(); @@ -117,14 +116,13 @@ NPCDB::unload() mLoaded = false; } -const NPCInfo& -NPCDB::get(int id) +const NPCInfo& NPCDB::get(int id) { NPCInfosIterator i = mNPCInfos.find(id); if (i == mNPCInfos.end()) { - logger->log("NPCDB: Warning, unknown NPC ID %d requested", id); + logger->log(_("NPCDB: Warning, unknown NPC ID %d requested"), id); return mUnknown; } else 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..303b82c8 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. diff --git a/src/resources/resourcemanager.cpp b/src/resources/resourcemanager.cpp index febcf0d0..3f58076e 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,8 +153,8 @@ 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()); @@ -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..73f55fd3 100644 --- a/src/resources/resourcemanager.h +++ b/src/resources/resourcemanager.h @@ -27,11 +27,11 @@ #include <string> #include <vector> -class Resource; class Image; +class ImageSet; class Music; +class Resource; class SoundEffect; -class ImageSet; class SpriteDef; struct SDL_Surface; diff --git a/src/resources/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..05ec9e54 100644 --- a/src/resources/soundeffect.h +++ b/src/resources/soundeffect.h @@ -58,8 +58,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..af3281be 100644 --- a/src/resources/spritedef.cpp +++ b/src/resources/spritedef.cpp @@ -21,14 +21,13 @@ #include <set> -#include "spritedef.h" - #include "action.h" #include "animation.h" #include "dye.h" #include "image.h" #include "imageset.h" #include "resourcemanager.h" +#include "spritedef.h" #include "../log.h" #include "../utils/xml.h" diff --git a/src/resources/spritedef.h b/src/resources/spritedef.h index 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..17d9ce60 100644 --- a/src/simpleanimation.cpp +++ b/src/simpleanimation.cpp @@ -19,15 +19,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "simpleanimation.h" - #include "graphics.h" #include "log.h" +#include "simpleanimation.h" #include "resources/image.h" -#include "resources/resourcemanager.h" #include "resources/imageset.h" - +#include "resources/resourcemanager.h" SimpleAnimation::SimpleAnimation(xmlNodePtr animationNode): mAnimationTime(0), diff --git a/src/sound.cpp b/src/sound.cpp index c69dc023..78538c09 100644 --- a/src/sound.cpp +++ b/src/sound.cpp @@ -19,11 +19,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "sound.h" - #include <SDL.h> #include "log.h" +#include "sound.h" + #include "resources/resourcemanager.h" #include "resources/soundeffect.h" diff --git a/src/sound.h b/src/sound.h index 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/text.cpp b/src/text.cpp index 0e458c9f..e148c117 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -1,5 +1,6 @@ /* * The Mana World + * Copyright (C) 2008 Douglas Boffey <DougABoffey@netscape.net> * Copyright (C) 2008 The Mana World Development Team * * This file is part of The Mana World. @@ -68,8 +69,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) { @@ -115,7 +116,7 @@ void Text::adviseXY(int x, int y) void Text::draw(Graphics *graphics, int xOff, int yOff) { - graphics->setFont(gui->getFont()); + graphics->setFont(boldFont); if (mIsSpeech) { static_cast<Graphics*>(graphics)->drawImageRect( @@ -1,5 +1,6 @@ /* * The Mana World + * Copyright (C) 2008 Douglas Boffey <DougABoffey@netscape.net> * Copyright (C) 2008 The Mana World Development Team * * This file is part of The Mana World. @@ -23,6 +24,7 @@ #define TEXT_H #include "graphics.h" +#include "guichanfwd.h" #include <list> @@ -79,6 +81,11 @@ class FlashText : public Text gcn::Color colour); /** + * Remove the text from the screen + */ + virtual ~FlashText() {} + + /** * Flash the text for so many refreshes. */ void flash(int time) {mTime = time; } @@ -89,7 +96,7 @@ class FlashText : public Text virtual void draw(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..e22f6f4f 100644 --- a/src/textmanager.cpp +++ b/src/textmanager.cpp @@ -1,5 +1,6 @@ /* * The Mana World + * Copyright (C) 2008 Douglas Boffey <DougABoffey@netscape.net> * Copyright (C) 2008 The Mana World Development Team * * This file is part of The Mana World. diff --git a/src/textmanager.h b/src/textmanager.h index fd0006c9..15c65195 100644 --- a/src/textmanager.h +++ b/src/textmanager.h @@ -1,5 +1,6 @@ /* * The Mana World + * Copyright (C) 2008 Douglas Boffey <DougABoffey@netscape.net> * Copyright (C) 2008 The Mana World Development Team * * This file is part of The Mana World. diff --git a/src/textparticle.cpp b/src/textparticle.cpp index 0367f109..7e329b4e 100644 --- a/src/textparticle.cpp +++ b/src/textparticle.cpp @@ -19,9 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "textparticle.h" - #include "graphics.h" +#include "textparticle.h" TextParticle::TextParticle(Map *map, const std::string &text, int colorR, int colorG, int colorB, diff --git a/src/textparticle.h b/src/textparticle.h index acf1e6a5..bc7cd88c 100644 --- a/src/textparticle.h +++ b/src/textparticle.h @@ -22,11 +22,10 @@ #ifndef _TEXTPARTICLE_H #define _TEXTPARTICLE_H -#include "particle.h" - #include <guichan/color.hpp> #include "guichanfwd.h" +#include "particle.h" class TextParticle : public Particle { diff --git a/src/utils/base64.cpp b/src/utils/base64.cpp index 8cea60f9..9d8ba836 100644 --- a/src/utils/base64.cpp +++ b/src/utils/base64.cpp @@ -27,8 +27,8 @@ +----------------------------------------------------------------------+ */ -#include <string.h> #include <stdlib.h> +#include <string.h> #include "base64.h" diff --git a/src/utils/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/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 |