summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/packages.yml1
-rw-r--r--CMakeLists.txt10
-rw-r--r--NEWS5
-rw-r--r--data/graphics/gui/button-icon-abilities.png (renamed from data/graphics/gui/button-icon-specials.png)bin766 -> 766 bytes
-rw-r--r--data/graphics/gui/colors.xml50
-rw-r--r--data/graphics/gui/resize.pngbin443 -> 4466 bytes
-rw-r--r--data/graphics/gui/speechbubble.xml18
-rw-r--r--data/graphics/gui/theme.xml280
-rw-r--r--data/graphics/gui/window.xml18
m---------libs/guichan0
-rw-r--r--snap/snapcraft.yaml7
-rw-r--r--src/CMakeLists.txt36
-rw-r--r--src/actor.h19
-rw-r--r--src/actorsprite.cpp178
-rw-r--r--src/actorsprite.h86
-rw-r--r--src/actorspritemanager.cpp8
-rw-r--r--src/actorspritemanager.h11
-rw-r--r--src/animatedsprite.h92
-rw-r--r--src/animationparticle.h5
-rw-r--r--src/avatar.h7
-rw-r--r--src/being.cpp399
-rw-r--r--src/being.h72
-rw-r--r--src/channel.h5
-rw-r--r--src/channelmanager.h5
-rw-r--r--src/chatlogger.h5
-rw-r--r--src/client.cpp156
-rw-r--r--src/client.h10
-rw-r--r--src/commandhandler.cpp16
-rw-r--r--src/commandhandler.h5
-rw-r--r--src/compoundsprite.cpp46
-rw-r--r--src/compoundsprite.h67
-rw-r--r--src/configuration.cpp6
-rw-r--r--src/configuration.h5
-rw-r--r--src/defaults.cpp5
-rw-r--r--src/defaults.h5
-rw-r--r--src/effectmanager.cpp75
-rw-r--r--src/effectmanager.h16
-rw-r--r--src/emoteshortcut.h5
-rw-r--r--src/equipment.h5
-rw-r--r--src/event.cpp12
-rw-r--r--src/event.h7
-rw-r--r--src/eventlistener.h5
-rw-r--r--src/flooritem.cpp33
-rw-r--r--src/flooritem.h20
-rw-r--r--src/game.cpp20
-rw-r--r--src/game.h5
-rw-r--r--src/graphics.cpp171
-rw-r--r--src/graphics.h38
-rw-r--r--src/gui/abilitieswindow.cpp (renamed from src/gui/specialswindow.cpp)88
-rw-r--r--src/gui/abilitieswindow.h (renamed from src/gui/specialswindow.h)23
-rw-r--r--src/gui/beingpopup.h5
-rw-r--r--src/gui/buydialog.h5
-rw-r--r--src/gui/buyselldialog.h5
-rw-r--r--src/gui/changeemaildialog.h5
-rw-r--r--src/gui/changepassworddialog.h5
-rw-r--r--src/gui/charcreatedialog.cpp67
-rw-r--r--src/gui/charcreatedialog.h11
-rw-r--r--src/gui/charselectdialog.cpp4
-rw-r--r--src/gui/charselectdialog.h5
-rw-r--r--src/gui/chatwindow.cpp28
-rw-r--r--src/gui/chatwindow.h21
-rw-r--r--src/gui/confirmdialog.h5
-rw-r--r--src/gui/connectiondialog.h5
-rw-r--r--src/gui/customserverdialog.cpp65
-rw-r--r--src/gui/customserverdialog.h25
-rw-r--r--src/gui/debugwindow.cpp18
-rw-r--r--src/gui/debugwindow.h16
-rw-r--r--src/gui/emotepopup.cpp19
-rw-r--r--src/gui/emotepopup.h8
-rw-r--r--src/gui/equipmentwindow.cpp17
-rw-r--r--src/gui/equipmentwindow.h14
-rw-r--r--src/gui/focushandler.h5
-rw-r--r--src/gui/gui.cpp54
-rw-r--r--src/gui/gui.h53
-rw-r--r--src/gui/helpwindow.cpp20
-rw-r--r--src/gui/helpwindow.h5
-rw-r--r--src/gui/inventorywindow.cpp124
-rw-r--r--src/gui/inventorywindow.h39
-rw-r--r--src/gui/itemamountwindow.cpp6
-rw-r--r--src/gui/itemamountwindow.h8
-rw-r--r--src/gui/itempopup.cpp7
-rw-r--r--src/gui/itempopup.h5
-rw-r--r--src/gui/logindialog.cpp4
-rw-r--r--src/gui/logindialog.h5
-rw-r--r--src/gui/minimap.cpp59
-rw-r--r--src/gui/minimap.h5
-rw-r--r--src/gui/ministatuswindow.cpp208
-rw-r--r--src/gui/ministatuswindow.h33
-rw-r--r--src/gui/npcdialog.cpp2
-rw-r--r--src/gui/npcdialog.h5
-rw-r--r--src/gui/npcpostdialog.h5
-rw-r--r--src/gui/okdialog.h5
-rw-r--r--src/gui/outfitwindow.cpp26
-rw-r--r--src/gui/outfitwindow.h9
-rw-r--r--src/gui/palette.h5
-rw-r--r--src/gui/popupmenu.cpp39
-rw-r--r--src/gui/popupmenu.h5
-rw-r--r--src/gui/quitdialog.cpp10
-rw-r--r--src/gui/quitdialog.h5
-rw-r--r--src/gui/recorder.cpp15
-rw-r--r--src/gui/recorder.h5
-rw-r--r--src/gui/register.cpp4
-rw-r--r--src/gui/register.h5
-rw-r--r--src/gui/sdlinput.h5
-rw-r--r--src/gui/selldialog.cpp2
-rw-r--r--src/gui/selldialog.h5
-rw-r--r--src/gui/serverdialog.cpp272
-rw-r--r--src/gui/serverdialog.h40
-rw-r--r--src/gui/setup.h5
-rw-r--r--src/gui/setup_audio.cpp7
-rw-r--r--src/gui/setup_audio.h5
-rw-r--r--src/gui/setup_colors.cpp10
-rw-r--r--src/gui/setup_colors.h5
-rw-r--r--src/gui/setup_interface.h5
-rw-r--r--src/gui/setup_joystick.h5
-rw-r--r--src/gui/setup_keyboard.h5
-rw-r--r--src/gui/setup_players.cpp8
-rw-r--r--src/gui/setup_players.h5
-rw-r--r--src/gui/setup_video.cpp8
-rw-r--r--src/gui/setup_video.h8
-rw-r--r--src/gui/shortcutwindow.cpp10
-rw-r--r--src/gui/shortcutwindow.h5
-rw-r--r--src/gui/skilldialog.cpp146
-rw-r--r--src/gui/skilldialog.h16
-rw-r--r--src/gui/socialwindow.cpp29
-rw-r--r--src/gui/socialwindow.h5
-rw-r--r--src/gui/speechbubble.cpp13
-rw-r--r--src/gui/speechbubble.h5
-rw-r--r--src/gui/statuswindow.cpp2
-rw-r--r--src/gui/statuswindow.h5
-rw-r--r--src/gui/textdialog.h5
-rw-r--r--src/gui/textpopup.h5
-rw-r--r--src/gui/tradewindow.h5
-rw-r--r--src/gui/truetypefont.cpp25
-rw-r--r--src/gui/truetypefont.h5
-rw-r--r--src/gui/unregisterdialog.h5
-rw-r--r--src/gui/updaterwindow.cpp406
-rw-r--r--src/gui/updaterwindow.h97
-rw-r--r--src/gui/viewport.cpp14
-rw-r--r--src/gui/viewport.h5
-rw-r--r--src/gui/widgets/avatarlistbox.cpp18
-rw-r--r--src/gui/widgets/avatarlistbox.h10
-rw-r--r--src/gui/widgets/browserbox.cpp123
-rw-r--r--src/gui/widgets/browserbox.h18
-rw-r--r--src/gui/widgets/button.cpp205
-rw-r--r--src/gui/widgets/button.h23
-rw-r--r--src/gui/widgets/channeltab.h5
-rw-r--r--src/gui/widgets/chattab.h5
-rw-r--r--src/gui/widgets/checkbox.cpp126
-rw-r--r--src/gui/widgets/checkbox.h32
-rw-r--r--src/gui/widgets/container.h8
-rw-r--r--src/gui/widgets/desktop.cpp2
-rw-r--r--src/gui/widgets/desktop.h5
-rw-r--r--src/gui/widgets/dropdown.cpp197
-rw-r--r--src/gui/widgets/dropdown.h25
-rw-r--r--src/gui/widgets/emoteshortcutcontainer.cpp57
-rw-r--r--src/gui/widgets/emoteshortcutcontainer.h7
-rw-r--r--src/gui/widgets/flowcontainer.h5
-rw-r--r--src/gui/widgets/icon.cpp2
-rw-r--r--src/gui/widgets/icon.h5
-rw-r--r--src/gui/widgets/inttextfield.h5
-rw-r--r--src/gui/widgets/itemcontainer.cpp1
-rw-r--r--src/gui/widgets/itemcontainer.h12
-rw-r--r--src/gui/widgets/itemlinkhandler.h5
-rw-r--r--src/gui/widgets/itemshortcutcontainer.cpp55
-rw-r--r--src/gui/widgets/itemshortcutcontainer.h9
-rw-r--r--src/gui/widgets/label.cpp35
-rw-r--r--src/gui/widgets/label.h30
-rw-r--r--src/gui/widgets/layout.h14
-rw-r--r--src/gui/widgets/layouthelper.h5
-rw-r--r--src/gui/widgets/linkhandler.h5
-rw-r--r--src/gui/widgets/listbox.cpp19
-rw-r--r--src/gui/widgets/listbox.h14
-rw-r--r--src/gui/widgets/passwordfield.h5
-rw-r--r--src/gui/widgets/playerbox.cpp76
-rw-r--r--src/gui/widgets/playerbox.h23
-rw-r--r--src/gui/widgets/popup.cpp39
-rw-r--r--src/gui/widgets/popup.h33
-rw-r--r--src/gui/widgets/progressbar.cpp100
-rw-r--r--src/gui/widgets/progressbar.h28
-rw-r--r--src/gui/widgets/progressindicator.cpp10
-rw-r--r--src/gui/widgets/progressindicator.h8
-rw-r--r--src/gui/widgets/radiobutton.cpp118
-rw-r--r--src/gui/widgets/radiobutton.h35
-rw-r--r--src/gui/widgets/resizegrip.cpp40
-rw-r--r--src/gui/widgets/resizegrip.h16
-rw-r--r--src/gui/widgets/scrollarea.cpp285
-rw-r--r--src/gui/widgets/scrollarea.h46
-rw-r--r--src/gui/widgets/setuptab.h5
-rw-r--r--src/gui/widgets/shopitems.h5
-rw-r--r--src/gui/widgets/shoplistbox.h5
-rw-r--r--src/gui/widgets/shortcutcontainer.cpp10
-rw-r--r--src/gui/widgets/shortcutcontainer.h25
-rw-r--r--src/gui/widgets/slider.cpp167
-rw-r--r--src/gui/widgets/slider.h29
-rw-r--r--src/gui/widgets/spacer.h5
-rw-r--r--src/gui/widgets/tab.cpp169
-rw-r--r--src/gui/widgets/tab.h32
-rw-r--r--src/gui/widgets/tabbedarea.cpp57
-rw-r--r--src/gui/widgets/tabbedarea.h10
-rw-r--r--src/gui/widgets/table.cpp52
-rw-r--r--src/gui/widgets/table.h19
-rw-r--r--src/gui/widgets/tablemodel.h5
-rw-r--r--src/gui/widgets/textbox.h5
-rw-r--r--src/gui/widgets/textfield.cpp98
-rw-r--r--src/gui/widgets/textfield.h46
-rw-r--r--src/gui/widgets/textpreview.h5
-rw-r--r--src/gui/widgets/vertcontainer.h5
-rw-r--r--src/gui/widgets/whispertab.h5
-rw-r--r--src/gui/widgets/window.cpp223
-rw-r--r--src/gui/widgets/window.h33
-rw-r--r--src/gui/widgets/windowcontainer.cpp9
-rw-r--r--src/gui/widgets/windowcontainer.h11
-rw-r--r--src/gui/windowmenu.cpp10
-rw-r--r--src/gui/windowmenu.h5
-rw-r--r--src/gui/worldselectdialog.h5
-rw-r--r--src/guichanfwd.h5
-rw-r--r--src/guild.h5
-rw-r--r--src/imageparticle.cpp11
-rw-r--r--src/imageparticle.h8
-rw-r--r--src/imagesprite.cpp39
-rw-r--r--src/imagesprite.h72
-rw-r--r--src/inventory.cpp2
-rw-r--r--src/inventory.h6
-rw-r--r--src/item.cpp9
-rw-r--r--src/item.h22
-rw-r--r--src/itemshortcut.h5
-rw-r--r--src/joystick.h5
-rw-r--r--src/keyboardconfig.cpp31
-rw-r--r--src/keyboardconfig.h10
-rw-r--r--src/localplayer.cpp103
-rw-r--r--src/localplayer.h33
-rw-r--r--src/log.h5
-rw-r--r--src/main.cpp11
-rw-r--r--src/main.h5
-rw-r--r--src/map.cpp4
-rw-r--r--src/map.h5
-rw-r--r--src/net/abilityhandler.h (renamed from src/net/specialhandler.h)19
-rw-r--r--src/net/adminhandler.h5
-rw-r--r--src/net/charhandler.h5
-rw-r--r--src/net/chathandler.h5
-rw-r--r--src/net/download.cpp298
-rw-r--r--src/net/download.h92
-rw-r--r--src/net/gamehandler.h5
-rw-r--r--src/net/generalhandler.h7
-rw-r--r--src/net/guildhandler.h5
-rw-r--r--src/net/inventoryhandler.h7
-rw-r--r--src/net/logindata.h9
-rw-r--r--src/net/loginhandler.h5
-rw-r--r--src/net/manaserv/abilityhandler.cpp (renamed from src/net/manaserv/specialhandler.cpp)29
-rw-r--r--src/net/manaserv/abilityhandler.h (renamed from src/net/manaserv/specialhandler.h)17
-rw-r--r--src/net/manaserv/adminhandler.cpp6
-rw-r--r--src/net/manaserv/adminhandler.h5
-rw-r--r--src/net/manaserv/beinghandler.cpp153
-rw-r--r--src/net/manaserv/beinghandler.h10
-rw-r--r--src/net/manaserv/buysellhandler.h5
-rw-r--r--src/net/manaserv/charhandler.cpp83
-rw-r--r--src/net/manaserv/charhandler.h16
-rw-r--r--src/net/manaserv/chathandler.h5
-rw-r--r--src/net/manaserv/connection.cpp5
-rw-r--r--src/net/manaserv/connection.h5
-rw-r--r--src/net/manaserv/defines.h5
-rw-r--r--src/net/manaserv/effecthandler.h5
-rw-r--r--src/net/manaserv/gamehandler.cpp4
-rw-r--r--src/net/manaserv/gamehandler.h5
-rw-r--r--src/net/manaserv/generalhandler.cpp35
-rw-r--r--src/net/manaserv/generalhandler.h9
-rw-r--r--src/net/manaserv/guildhandler.h5
-rw-r--r--src/net/manaserv/internal.h5
-rw-r--r--src/net/manaserv/inventoryhandler.cpp273
-rw-r--r--src/net/manaserv/inventoryhandler.h26
-rw-r--r--src/net/manaserv/itemhandler.cpp2
-rw-r--r--src/net/manaserv/itemhandler.h5
-rw-r--r--src/net/manaserv/loginhandler.cpp2
-rw-r--r--src/net/manaserv/loginhandler.h5
-rw-r--r--src/net/manaserv/manaserv_protocol.h212
-rw-r--r--src/net/manaserv/messagehandler.h5
-rw-r--r--src/net/manaserv/messagein.h5
-rw-r--r--src/net/manaserv/messageout.h5
-rw-r--r--src/net/manaserv/network.cpp4
-rw-r--r--src/net/manaserv/network.h5
-rw-r--r--src/net/manaserv/npchandler.cpp4
-rw-r--r--src/net/manaserv/npchandler.h5
-rw-r--r--src/net/manaserv/partyhandler.h5
-rw-r--r--src/net/manaserv/playerhandler.cpp82
-rw-r--r--src/net/manaserv/playerhandler.h5
-rw-r--r--src/net/manaserv/tradehandler.h5
-rw-r--r--src/net/messagehandler.h10
-rw-r--r--src/net/net.cpp53
-rw-r--r--src/net/net.h9
-rw-r--r--src/net/npchandler.h5
-rw-r--r--src/net/partyhandler.h5
-rw-r--r--src/net/playerhandler.h5
-rw-r--r--src/net/serverinfo.h43
-rw-r--r--src/net/tmwa/abilityhandler.cpp (renamed from src/net/tmwa/specialhandler.cpp)50
-rw-r--r--src/net/tmwa/abilityhandler.h (renamed from src/net/tmwa/specialhandler.h)17
-rw-r--r--src/net/tmwa/adminhandler.cpp3
-rw-r--r--src/net/tmwa/adminhandler.h5
-rw-r--r--src/net/tmwa/beinghandler.cpp146
-rw-r--r--src/net/tmwa/beinghandler.h5
-rw-r--r--src/net/tmwa/buysellhandler.h5
-rw-r--r--src/net/tmwa/charserverhandler.cpp29
-rw-r--r--src/net/tmwa/charserverhandler.h7
-rw-r--r--src/net/tmwa/chathandler.cpp2
-rw-r--r--src/net/tmwa/chathandler.h5
-rw-r--r--src/net/tmwa/gamehandler.cpp2
-rw-r--r--src/net/tmwa/gamehandler.h5
-rw-r--r--src/net/tmwa/generalhandler.cpp17
-rw-r--r--src/net/tmwa/generalhandler.h9
-rw-r--r--src/net/tmwa/gui/guildtab.h5
-rw-r--r--src/net/tmwa/gui/partytab.h5
-rw-r--r--src/net/tmwa/guildhandler.cpp5
-rw-r--r--src/net/tmwa/guildhandler.h5
-rw-r--r--src/net/tmwa/inventoryhandler.cpp5
-rw-r--r--src/net/tmwa/inventoryhandler.h9
-rw-r--r--src/net/tmwa/itemhandler.h5
-rw-r--r--src/net/tmwa/loginhandler.cpp12
-rw-r--r--src/net/tmwa/loginhandler.h5
-rw-r--r--src/net/tmwa/messagehandler.h5
-rw-r--r--src/net/tmwa/messagein.cpp43
-rw-r--r--src/net/tmwa/messagein.h5
-rw-r--r--src/net/tmwa/messageout.h5
-rw-r--r--src/net/tmwa/network.cpp11
-rw-r--r--src/net/tmwa/network.h5
-rw-r--r--src/net/tmwa/npchandler.h5
-rw-r--r--src/net/tmwa/partyhandler.h5
-rw-r--r--src/net/tmwa/playerhandler.h5
-rw-r--r--src/net/tmwa/protocol.h19
-rw-r--r--src/net/tmwa/token.h7
-rw-r--r--src/net/tmwa/tradehandler.cpp6
-rw-r--r--src/net/tmwa/tradehandler.h5
-rw-r--r--src/net/tradehandler.h5
-rw-r--r--src/net/worldinfo.h5
-rw-r--r--src/openglgraphics.cpp40
-rw-r--r--src/openglgraphics.h7
-rw-r--r--src/particle.cpp35
-rw-r--r--src/particle.h56
-rw-r--r--src/particlecontainer.cpp172
-rw-r--r--src/particlecontainer.h121
-rw-r--r--src/particleemitter.cpp152
-rw-r--r--src/particleemitter.h4
-rw-r--r--src/particleemitterprop.h2
-rw-r--r--src/party.cpp2
-rw-r--r--src/party.h5
-rw-r--r--src/playerinfo.cpp36
-rw-r--r--src/playerinfo.h25
-rw-r--r--src/playerrelations.cpp10
-rw-r--r--src/playerrelations.h14
-rw-r--r--src/position.h5
-rw-r--r--src/properties.h5
-rw-r--r--src/resources/abilitydb.cpp106
-rw-r--r--src/resources/abilitydb.h (renamed from src/resources/specialdb.h)38
-rw-r--r--src/resources/action.h5
-rw-r--r--src/resources/ambientlayer.h5
-rw-r--r--src/resources/animation.cpp92
-rw-r--r--src/resources/animation.h15
-rw-r--r--src/resources/attributes.cpp34
-rw-r--r--src/resources/attributes.h5
-rw-r--r--src/resources/beinginfo.cpp20
-rw-r--r--src/resources/beinginfo.h15
-rw-r--r--src/resources/chardb.h5
-rw-r--r--src/resources/dye.h5
-rw-r--r--src/resources/emotedb.cpp17
-rw-r--r--src/resources/emotedb.h14
-rw-r--r--src/resources/hairdb.h5
-rw-r--r--src/resources/image.cpp41
-rw-r--r--src/resources/image.h5
-rw-r--r--src/resources/imageset.h5
-rw-r--r--src/resources/imagewriter.h2
-rw-r--r--src/resources/itemdb.cpp120
-rw-r--r--src/resources/itemdb.h12
-rw-r--r--src/resources/iteminfo.h37
-rw-r--r--src/resources/mapreader.cpp2
-rw-r--r--src/resources/mapreader.h5
-rw-r--r--src/resources/monsterdb.cpp10
-rw-r--r--src/resources/monsterdb.h5
-rw-r--r--src/resources/music.cpp2
-rw-r--r--src/resources/music.h11
-rw-r--r--src/resources/npcdb.h5
-rw-r--r--src/resources/resource.h13
-rw-r--r--src/resources/resourcemanager.cpp283
-rw-r--r--src/resources/resourcemanager.h155
-rw-r--r--src/resources/settingsmanager.cpp43
-rw-r--r--src/resources/settingsmanager.h6
-rw-r--r--src/resources/soundeffect.cpp13
-rw-r--r--src/resources/soundeffect.h17
-rw-r--r--src/resources/specialdb.cpp115
-rw-r--r--src/resources/spritedef.cpp9
-rw-r--r--src/resources/spritedef.h13
-rw-r--r--src/resources/statuseffectdb.cpp96
-rw-r--r--src/resources/statuseffectdb.h67
-rw-r--r--src/resources/theme.cpp801
-rw-r--r--src/resources/theme.h211
-rw-r--r--src/resources/userpalette.h5
-rw-r--r--src/resources/wallpaper.cpp29
-rw-r--r--src/resources/wallpaper.h5
-rw-r--r--src/rotationalparticle.cpp6
-rw-r--r--src/rotationalparticle.h5
-rw-r--r--src/sdlgraphics.cpp74
-rw-r--r--src/sdlgraphics.h14
-rw-r--r--src/shopitem.h5
-rw-r--r--src/simpleanimation.cpp116
-rw-r--r--src/simpleanimation.h12
-rw-r--r--src/sound.cpp100
-rw-r--r--src/sound.h31
-rw-r--r--src/sprite.cpp (renamed from src/animatedsprite.cpp)53
-rw-r--r--src/sprite.h77
-rw-r--r--src/statuseffect.cpp164
-rw-r--r--src/statuseffect.h103
-rw-r--r--src/text.cpp61
-rw-r--r--src/text.h13
-rw-r--r--src/textmanager.h5
-rw-r--r--src/textparticle.h5
-rw-r--r--src/textrenderer.h101
-rw-r--r--src/tileset.h5
-rw-r--r--src/units.cpp3
-rw-r--r--src/units.h5
-rw-r--r--src/utils/base64.h5
-rw-r--r--src/utils/copynpaste.h2
-rw-r--r--src/utils/dtor.h5
-rw-r--r--src/utils/filesystem.h297
-rw-r--r--src/utils/gettext.h5
-rw-r--r--src/utils/mathutils.h5
-rw-r--r--src/utils/mkdir.h5
-rw-r--r--src/utils/mutex.h42
-rw-r--r--src/utils/path.h5
-rw-r--r--src/utils/sha256.h5
-rw-r--r--src/utils/specialfolder.h5
-rw-r--r--src/utils/stringutils.cpp90
-rw-r--r--src/utils/stringutils.h78
-rw-r--r--src/utils/time.h5
-rw-r--r--src/utils/xml.cpp24
-rw-r--r--src/utils/xml.h5
-rw-r--r--src/utils/zlib.cpp52
-rw-r--r--src/utils/zlib.h18
-rw-r--r--src/variabledata.h5
-rw-r--r--src/vector.h5
-rw-r--r--src/video.h5
438 files changed, 6538 insertions, 8368 deletions
diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml
index 779491ae..1c124dd0 100644
--- a/.github/workflows/packages.yml
+++ b/.github/workflows/packages.yml
@@ -46,6 +46,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
+ fetch-depth: 0 # required to generate the version from git tags
submodules: true
- uses: snapcore/action-build@v1
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 11cdaf03..64221f96 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -147,7 +147,8 @@ if(WIN32)
$ENV{MINGW_PREFIX}/bin/libaom.dll
$ENV{MINGW_PREFIX}/bin/libdav1d-7.dll
$ENV{MINGW_PREFIX}/bin/libsharpyuv-0.dll
- $ENV{MINGW_PREFIX}/bin/libSvtAv1Enc-2.dll
+ $ENV{MINGW_PREFIX}/bin/libSvtAv1Enc-3.dll
+ $ENV{MINGW_PREFIX}/bin/libFLAC.dll
$ENV{MINGW_PREFIX}/bin/libyuv.dll
$ENV{MINGW_PREFIX}/bin/libbrotlienc.dll
$ENV{MINGW_PREFIX}/bin/libhwy.dll
@@ -156,14 +157,17 @@ if(WIN32)
$ENV{MINGW_PREFIX}/bin/libjbig-0.dll
$ENV{MINGW_PREFIX}/bin/libLerc.dll
$ENV{MINGW_PREFIX}/bin/libopus-0.dll
- $ENV{MINGW_PREFIX}/bin/rav1e.dll
+ $ENV{MINGW_PREFIX}/bin/librav1e.dll
$ENV{MINGW_PREFIX}/bin/libbz2-1.dll
$ENV{MINGW_PREFIX}/bin/libglib-2.0-0.dll
$ENV{MINGW_PREFIX}/bin/libogg-0.dll
+ $ENV{MINGW_PREFIX}/bin/libvorbisfile-3.dll
+ $ENV{MINGW_PREFIX}/bin/libvorbis-0.dll
+ $ENV{MINGW_PREFIX}/bin/libwavpack-1.dll
$ENV{MINGW_PREFIX}/bin/libgraphite2.dll
$ENV{MINGW_PREFIX}/bin/liblcms2-2.dll
$ENV{MINGW_PREFIX}/bin/libpcre2-8-0.dll
- $ENV{MINGW_PREFIX}/bin/libmodplug-1.dll
+ $ENV{MINGW_PREFIX}/bin/libxmp.dll
DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
include(CPack)
diff --git a/NEWS b/NEWS
index 802e57cd..2898a7cb 100644
--- a/NEWS
+++ b/NEWS
@@ -3,12 +3,15 @@
- Added VSync and windowed fullscreen options
- Added support for scaling the graphics
- Added ability to open external links in news, chat and NPC dialogs
+- Added ability to mention assigned keys in NPC dialogs
- Added support for text formatting to NPC dialogs
- Added support for reading most client-data settings from settings.xml
- Added support for XML includes, both absolute and relative
- Added support for map/layer mask
+- Added support for item sprite replacements
- Added support for particle effects on equipment
- Added support for hit/miss sounds on equipment for all players
+- Added support for players changing into monsters or NPCs
- Added online player list to Social window
- Added notification sound on receiving whisper
- Added default ports when connecting to a custom server
@@ -56,6 +59,8 @@
- Fixed empty Equipment window on freshly created character
- Fixed choosing default world when using -D command-line parameter
- Fixed storing of player relations
+- Fixed handling of custom port in update URL
+- Fixed stutter when new music starts playing
- Updated to tmwAthena protocol changes
- Updated to Manaserv protocol changes (specials, guilds, debug mode, skills, text particles)
- CMake: Use GNUInstallDirs and made PKG_DATADIR / PKG_BINDIR paths modifiable
diff --git a/data/graphics/gui/button-icon-specials.png b/data/graphics/gui/button-icon-abilities.png
index 31c66e81..31c66e81 100644
--- a/data/graphics/gui/button-icon-specials.png
+++ b/data/graphics/gui/button-icon-abilities.png
Binary files differ
diff --git a/data/graphics/gui/colors.xml b/data/graphics/gui/colors.xml
deleted file mode 100644
index 4a35d081..00000000
--- a/data/graphics/gui/colors.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<colors>
- <color id="TEXT" color="#000000" />
- <color id="SHADOW" color="#000000" />
- <color id="OUTLINE" color="#000000" />
- <color id="PROGRESS_BAR" color="#ffffff" />
- <color id="BUTTON" color="#000000" />
- <color id="BUTTON_DISABLED" color="#333333" />
- <color id="TAB" color="#000000" />
- <color id="PARTY_CHAT_TAB" color="#f48055" />
- <color id="PARTY_SOCIAL_TAB" color="#ff00d8" />
- <color id="BACKGROUND" color="#ffffff" />
- <color id="HIGHLIGHT" color="#c0c0c0" />
- <color id="TAB_FLASH" color="#ff0000" effect="pulse" />
- <color id="SHOP_WARNING" color="#910000" />
- <color id="ITEM_EQUIPPED" color="#000091" />
- <color id="CHAT" color="#000000" />
- <color id="GM" color="#ff0000" />
- <color id="PLAYER" color="#1fa052" />
- <color id="WHISPER" color="#0000ff" />
- <color id="IS" color="#a08527" />
- <color id="PARTY" color="#ff00d8" />
- <color id="GUILD" color="#ff00d8" />
- <color id="SERVER" color="#8415e2" />
- <color id="LOGGER" color="#919191" />
- <color id="HYPERLINK" color="#e50d0d" />
- <color id="UNKNOWN_ITEM" color="#000000" />
- <color id="GENERIC" color="#21a5b1" />
- <color id="HEAD" color="#527fa4" />
- <color id="USABLE" color="#268d24" />
- <color id="TORSO" color="#d12aa4" />
- <color id="ONEHAND" color="#f42a2a" />
- <color id="LEGS" color="#699900" />
- <color id="FEET" color="#aa1d48" />
- <color id="TWOHAND" color="#f46d0e" />
- <color id="SHIELD" color="#9c2424" />
- <color id="RING" color="#0000ff" />
- <color id="NECKLACE" color="#ff00ff" />
- <color id="ARMS" color="#9c24e8" />
- <color id="AMMO" color="#8b6311" />
- <color id="SERVER_VERSION_NOT_SUPPORTED" color="#DC0000" />
-
- <progressbar id="DEFAULT" color="#969696" />
- <progressbar id="HP" color="#ff0000,e28000,c38948,0f6a20" />
- <progressbar id="MP" color="#1a66e6" />
- <progressbar id="NO_MP" color="#646464" />
- <progressbar id="EXP" color="#8fc0d3" />
- <progressbar id="INVY_SLOTS" color="#e1c819" />
- <progressbar id="WEIGHT" color="#0000ff,ffff00,ff0000" />
- <progressbar id="JOB" color="#e187cb" />
-</colors>
diff --git a/data/graphics/gui/resize.png b/data/graphics/gui/resize.png
index 6b31ac64..61cb2cc2 100644
--- a/data/graphics/gui/resize.png
+++ b/data/graphics/gui/resize.png
Binary files differ
diff --git a/data/graphics/gui/speechbubble.xml b/data/graphics/gui/speechbubble.xml
deleted file mode 100644
index 84b6557b..00000000
--- a/data/graphics/gui/speechbubble.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<skinset name="SpeechBubble" image="bubble.png">
- <widget type="Window">
- <!-- Top Row -->
- <part type="top-left-corner" xpos="0" ypos="0" width="5" height="5" />
- <part type="top-edge" xpos="5" ypos="0" width="5" height="5" />
- <part type="top-right-corner" xpos="10" ypos="0" width="5" height="5" />
-
- <!-- Middle Row -->
- <part type="left-edge" xpos="0" ypos="5" width="5" height="5" />
- <part type="bg-quad" xpos="5" ypos="5" width="5" height="5" />
- <part type="right-edge" xpos="10" ypos="5" width="5" height="5" />
-
- <!-- Bottom Row -->
- <part type="bottom-left-corner" xpos="0" ypos="10" width="5" height="5" />
- <part type="bottom-edge" xpos="5" ypos="10" width="5" height="5" />
- <part type="bottom-right-corner" xpos="10" ypos="10" width="5" height="5" />
- </widget>
-</skinset>
diff --git a/data/graphics/gui/theme.xml b/data/graphics/gui/theme.xml
new file mode 100644
index 00000000..b0484c45
--- /dev/null
+++ b/data/graphics/gui/theme.xml
@@ -0,0 +1,280 @@
+<theme name="Mana">
+ <color id="TEXT" color="#000000" />
+ <color id="SHADOW" color="#000000" />
+ <color id="OUTLINE" color="#000000" />
+ <color id="PARTY_CHAT_TAB" color="#f48055" />
+ <color id="PARTY_SOCIAL_TAB" color="#ff00d8" />
+ <color id="BACKGROUND" color="#ffffff" />
+ <color id="HIGHLIGHT" color="#c0c0c0" />
+ <color id="TAB_FLASH" color="#ff0000" effect="pulse" />
+ <color id="SHOP_WARNING" color="#910000" />
+ <color id="ITEM_EQUIPPED" color="#000091" />
+ <color id="CHAT" color="#000000" />
+ <color id="BUBBLE_TEXT" color="#ffffff" />
+ <color id="GM" color="#ff0000" />
+ <color id="PLAYER" color="#1fa052" />
+ <color id="WHISPER" color="#0000ff" />
+ <color id="IS" color="#a08527" />
+ <color id="PARTY" color="#ff00d8" />
+ <color id="GUILD" color="#ff00d8" />
+ <color id="SERVER" color="#8415e2" />
+ <color id="LOGGER" color="#919191" />
+ <color id="HYPERLINK" color="#e50d0d" />
+ <color id="UNKNOWN_ITEM" color="#000000" />
+ <color id="GENERIC" color="#21a5b1" />
+ <color id="HEAD" color="#527fa4" />
+ <color id="USABLE" color="#268d24" />
+ <color id="TORSO" color="#d12aa4" />
+ <color id="ONEHAND" color="#f42a2a" />
+ <color id="LEGS" color="#699900" />
+ <color id="FEET" color="#aa1d48" />
+ <color id="TWOHAND" color="#f46d0e" />
+ <color id="SHIELD" color="#9c2424" />
+ <color id="RING" color="#0000ff" />
+ <color id="NECKLACE" color="#ff00ff" />
+ <color id="ARMS" color="#9c24e8" />
+ <color id="AMMO" color="#8b6311" />
+ <color id="SERVER_VERSION_NOT_SUPPORTED" color="#DC0000" />
+
+ <progressbar id="DEFAULT" color="#969696" />
+ <progressbar id="HP" color="#ff0000,e28000,c38948,0f6a20" />
+ <progressbar id="MP" color="#1a66e6" />
+ <progressbar id="NO_MP" color="#646464" />
+ <progressbar id="EXP" color="#8fc0d3" />
+ <progressbar id="INVY_SLOTS" color="#e1c819" />
+ <progressbar id="WEIGHT" color="#0000ff,ffff00,ff0000" />
+ <progressbar id="JOB" color="#e187cb" />
+
+ <skin type="Window" frameSize="0" padding="3" titleBarHeight="20" titleOffsetX="7" titleOffsetY="5">
+ <state>
+ <img src="window.png" left="4" right="4" top="4" bottom="4" fill="repeat" />
+ </state>
+ </skin>
+
+ <skin type="ResizeGrip" padding="3">
+ <state>
+ <img src="resize.png" />
+ </state>
+ </skin>
+
+ <skin type="Popup" padding="6">
+ <state>
+ <img src="window.png" left="4" right="4" top="4" bottom="4" fill="repeat" />
+ </state>
+ </skin>
+
+ <skin type="SpeechBubble">
+ <state>
+ <img src="bubble.png|W:#000000" left="5" right="5" top="5" bottom="5" height="15" />
+ </state>
+ </skin>
+
+ <skin type="Button" padding="4">
+ <state disabled="true">
+ <text color="#333333" />
+ <img src="button_disabled.png" left="10" right="10" top="5" bottom="5" />
+ </state>
+ <state selected="true">
+ <img src="buttonpress.png" left="10" right="10" top="5" bottom="5" />
+ </state>
+ <state hovered="true">
+ <img src="buttonhi.png" left="10" right="10" top="5" bottom="5" />
+ </state>
+ <state>
+ <img src="button.png" left="10" right="10" top="5" bottom="5" />
+ </state>
+ </skin>
+
+ <skin type="Tab" padding="4">
+ <state selected="true">
+ <img src="tabselected.png" left="10" right="10" top="5" bottom="9" />
+ </state>
+ <state hovered="true">
+ <img src="tab_hilight.png" left="10" right="10" top="14" bottom="2" />
+ </state>
+ <state>
+ <img src="tab.png" left="10" right="10" top="14" bottom="2" />
+ </state>
+ </skin>
+
+ <skin type="CheckBox" padding="3" spacing="4">
+ <state disabled="true" selected="true">
+ <img src="checkbox.png" x="27" y="0" width="9" height="10" offsetX="2" offsetY="5" />
+ </state>
+ <state disabled="true">
+ <img src="checkbox.png" x="18" y="0" width="9" height="10" offsetX="2" offsetY="5" />
+ </state>
+ <state selected="true" hovered="true">
+ <img src="checkbox.png" x="45" y="0" width="9" height="10" offsetX="2" offsetY="5" />
+ </state>
+ <state hovered="true">
+ <img src="checkbox.png" x="36" y="0" width="9" height="10" offsetX="2" offsetY="5" />
+ </state>
+ <state selected="true">
+ <img src="checkbox.png" x="9" y="0" width="9" height="10" offsetX="2" offsetY="5" />
+ </state>
+ <state>
+ <img src="checkbox.png" x="0" y="0" width="9" height="10" offsetX="2" offsetY="5" />
+ </state>
+ </skin>
+
+ <skin type="RadioButton" padding="3" spacing="4">
+ <state hovered="true" selected="true">
+ <img src="radioin_highlight.png" offsetX="2" offsetY="5" />
+ </state>
+ <state hovered="true">
+ <img src="radioout_highlight.png" offsetX="2" offsetY="5" />
+ </state>
+ <state selected="true">
+ <img src="radioin.png" offsetX="2" offsetY="5" />
+ </state>
+ <state>
+ <img src="radioout.png" offsetX="2" offsetY="5" />
+ </state>
+ </skin>
+
+ <skin type="TextField" frameSize="2" padding="2">
+ <state>
+ <img src="deepbox.png" left="4" right="4" top="4" bottom="4" />
+ </state>
+ </skin>
+
+ <skin type="ScrollArea" frameSize="2" padding="2">
+ <state>
+ <img src="deepbox.png" left="4" right="4" top="4" bottom="4" />
+ </state>
+ </skin>
+
+ <skin type="DropDownFrame" frameSize="2" padding="1">
+ <state>
+ <img src="deepbox.png" left="4" right="4" top="4" bottom="4" />
+ </state>
+ </skin>
+
+ <skin type="DropDownButton">
+ <state selected="true" hovered="true">
+ <img src="vscroll_up_pressed.png" offsetX="-2" offsetY="2" />
+ </state>
+ <state selected="true">
+ <img src="vscroll_up_default.png" offsetX="-2" offsetY="2" />
+ </state>
+ <state hovered="true">
+ <img src="vscroll_down_pressed.png" offsetX="-2" offsetY="2" />
+ </state>
+ <state>
+ <img src="vscroll_down_default.png" offsetX="-2" offsetY="2" />
+ </state>
+ </skin>
+
+ <skin type="ScrollAreaVBar">
+ <state>
+ <rect color="#000000" alpha="32" />
+ </state>
+ </skin>
+
+ <skin type="ScrollAreaHBar">
+ <state>
+ <rect color="#000000" alpha="32" />
+ </state>
+ </skin>
+
+ <skin type="ScrollAreaHMarker">
+ <state hovered="true">
+ <img src="vscroll_highlight.png" left="4" right="4" top="4" bottom="4" fill="repeat" />
+ </state>
+ <state>
+ <img src="vscroll_grey.png" left="4" right="4" top="4" bottom="4" fill="repeat" />
+ </state>
+ </skin>
+
+ <skin type="ScrollAreaVMarker">
+ <state hovered="true">
+ <img src="vscroll_highlight.png" left="4" right="4" top="4" bottom="4" fill="repeat" />
+ </state>
+ <state>
+ <img src="vscroll_grey.png" left="4" right="4" top="4" bottom="4" fill="repeat" />
+ </state>
+ </skin>
+
+ <skin type="ProgressBar">
+ <state>
+ <text color="#ffffff" outlineColor="#000000"/>
+ <img src="vscroll_grey.png" left="4" right="4" top="4" bottom="4" fill="repeat" />
+ </state>
+ </skin>
+
+ <skin type="Slider" padding="4">
+ <state hovered="true">
+ <img src="slider_hilight.png" height="6" left="4" right="4" top="6" offsetY="4" />
+ </state>
+ <state>
+ <img src="slider.png" height="6" left="4" right="4" top="6" offsetY="4" />
+ </state>
+ </skin>
+
+ <skin type="SliderHandle">
+ <state hovered="true">
+ <img src="slider_hilight.png" x="6" y="8" width="9" height="10" offsetY="2" />
+ </state>
+ <state>
+ <img src="slider.png" x="6" y="8" width="9" height="10" offsetY="2" />
+ </state>
+ </skin>
+
+ <skin type="ButtonUp">
+ <state selected="true">
+ <img src="vscroll_up_pressed.png" />
+ </state>
+ <state>
+ <img src="vscroll_up_default.png" />
+ </state>
+ </skin>
+
+ <skin type="ButtonDown">
+ <state selected="true">
+ <img src="vscroll_down_pressed.png" />
+ </state>
+ <state>
+ <img src="vscroll_down_default.png" />
+ </state>
+ </skin>
+
+ <skin type="ButtonLeft">
+ <state selected="true">
+ <img src="hscroll_left_pressed.png" />
+ </state>
+ <state>
+ <img src="hscroll_left_default.png" />
+ </state>
+ </skin>
+
+ <skin type="ButtonRight">
+ <state selected="true">
+ <img src="hscroll_right_pressed.png" />
+ </state>
+ <state>
+ <img src="hscroll_right_default.png" />
+ </state>
+ </skin>
+
+ <skin type="ButtonClose" padding="3">
+ <state>
+ <img src="close_button.png" />
+ </state>
+ </skin>
+
+ <skin type="ButtonSticky" padding="3">
+ <state selected="true">
+ <img src="sticky_button.png" x="15" width="15" height="15" />
+ </state>
+ <state>
+ <img src="sticky_button.png" width="15" height="15" />
+ </state>
+ </skin>
+
+ <skin type="ShortcutBox">
+ <state>
+ <img src="item_shortcut_bgr.png" />
+ </state>
+ </skin>
+</theme>
diff --git a/data/graphics/gui/window.xml b/data/graphics/gui/window.xml
deleted file mode 100644
index f27dbc7f..00000000
--- a/data/graphics/gui/window.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<skinset name="Default" image="window.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="32" height="4" />
- <part type="top-right-corner" xpos="36" ypos="0" width="4" height="4" />
-
- <!-- Middle Row -->
- <part type="left-edge" xpos="0" ypos="4" width="4" height="216" />
- <part type="bg-quad" xpos="4" ypos="4" width="32" height="216" />
- <part type="right-edge" xpos="36" ypos="4" width="4" height="216" />
-
- <!-- Bottom Row -->
- <part type="bottom-left-corner" xpos="0" ypos="220" width="4" height="4" />
- <part type="bottom-edge" xpos="4" ypos="220" width="32" height="4" />
- <part type="bottom-right-corner" xpos="36" ypos="220" width="4" height="4" />
- </widget>
-</skinset>
diff --git a/libs/guichan b/libs/guichan
-Subproject c13a939ed4d205f2686f8a555cd42fbeaa89366
+Subproject c30941892acb9334d43bb5290bf7e6d5942b91e
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index 34e3d36c..711552fa 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -1,7 +1,6 @@
name: mana
-version: git
adopt-info: mana
-base: core22
+base: core24
grade: stable
confinement: strict
@@ -32,9 +31,13 @@ parts:
parse-info:
- usr/share/metainfo/org.manasource.Mana.metainfo.xml
source: .
+ override-pull: |
+ craftctl default
+ craftctl set version=$(git describe --tags | sed 's/v//')
build-packages:
- build-essential
- gettext
+ - git
- libcurl4-openssl-dev
- libguichan-dev
- libphysfs-dev
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d5d512e8..23505dcd 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,6 +1,6 @@
include(FindPkgConfig)
pkg_check_modules(SDL2 REQUIRED
- sdl2>=2.0.5
+ sdl2>=2.0.10
SDL2_image
SDL2_mixer
SDL2_net
@@ -266,8 +266,8 @@ set(SRCS
gui/socialwindow.h
gui/speechbubble.cpp
gui/speechbubble.h
- gui/specialswindow.cpp
- gui/specialswindow.h
+ gui/abilitieswindow.cpp
+ gui/abilitieswindow.h
gui/statuswindow.cpp
gui/statuswindow.h
gui/textdialog.cpp
@@ -307,7 +307,7 @@ set(SRCS
net/partyhandler.h
net/playerhandler.h
net/serverinfo.h
- net/specialhandler.h
+ net/abilityhandler.h
net/tradehandler.h
net/worldinfo.h
resources/action.cpp
@@ -354,10 +354,12 @@ set(SRCS
resources/settingsmanager.h
resources/soundeffect.h
resources/soundeffect.cpp
- resources/specialdb.cpp
- resources/specialdb.h
+ resources/abilitydb.cpp
+ resources/abilitydb.h
resources/spritedef.h
resources/spritedef.cpp
+ resources/statuseffectdb.cpp
+ resources/statuseffectdb.h
resources/theme.cpp
resources/theme.h
resources/userpalette.cpp
@@ -369,8 +371,12 @@ set(SRCS
utils/copynpaste.cpp
utils/copynpaste.h
utils/dtor.h
+ utils/filesystem.h
utils/gettext.h
utils/mathutils.h
+ utils/mkdir.cpp
+ utils/mkdir.h
+ utils/mutex.h
utils/path.cpp
utils/path.h
utils/physfsrwops.c
@@ -383,9 +389,6 @@ set(SRCS
utils/stringutils.h
utils/time.cpp
utils/time.h
- utils/mutex.h
- utils/mkdir.cpp
- utils/mkdir.h
utils/xml.cpp
utils/xml.h
utils/zlib.cpp
@@ -396,8 +399,6 @@ set(SRCS
actorsprite.h
actorspritemanager.cpp
actorspritemanager.h
- animatedsprite.cpp
- animatedsprite.h
animationparticle.cpp
animationparticle.h
avatar.cpp
@@ -440,8 +441,6 @@ set(SRCS
guild.h
imageparticle.cpp
imageparticle.h
- imagesprite.cpp
- imagesprite.h
inventory.cpp
inventory.h
item.cpp
@@ -464,8 +463,6 @@ set(SRCS
openglgraphics.h
particle.cpp
particle.h
- particlecontainer.cpp
- particlecontainer.h
particleemitter.cpp
particleemitter.h
particleemitterprop.h
@@ -488,6 +485,7 @@ set(SRCS
simpleanimation.h
sound.cpp
sound.h
+ sprite.cpp
sprite.h
statuseffect.cpp
statuseffect.h
@@ -549,8 +547,8 @@ set(SRCS_TMWA
net/tmwa/playerhandler.cpp
net/tmwa/playerhandler.h
net/tmwa/protocol.h
- net/tmwa/specialhandler.cpp
- net/tmwa/specialhandler.h
+ net/tmwa/abilityhandler.cpp
+ net/tmwa/abilityhandler.h
net/tmwa/token.h
net/tmwa/tradehandler.cpp
net/tmwa/tradehandler.h)
@@ -600,8 +598,8 @@ set(SRCS_MANA
net/manaserv/playerhandler.cpp
net/manaserv/playerhandler.h
net/manaserv/manaserv_protocol.h
- net/manaserv/specialhandler.cpp
- net/manaserv/specialhandler.h
+ net/manaserv/abilityhandler.cpp
+ net/manaserv/abilityhandler.h
net/manaserv/tradehandler.cpp
net/manaserv/tradehandler.h)
diff --git a/src/actor.h b/src/actor.h
index cd0da7fb..a54fba0a 100644
--- a/src/actor.h
+++ b/src/actor.h
@@ -18,8 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef ACTOR_H
-#define ACTOR_H
+#pragma once
#include "vector.h"
@@ -49,20 +48,6 @@ public:
virtual bool draw(Graphics *graphics, int offsetX, int offsetY) const = 0;
/**
- * Returns the horizontal size of the actors graphical representation
- * in pixels or 0 when it is undefined.
- */
- virtual int getWidth() const
- { return 0; }
-
- /**
- * Returns the vertical size of the actors graphical representation
- * in pixels or 0 when it is undefined.
- */
- virtual int getHeight() const
- { return 0; }
-
- /**
* Returns the pixel position of this actor.
*/
const Vector &getPosition() const
@@ -131,5 +116,3 @@ protected:
private:
Actors::iterator mMapActor;
};
-
-#endif // ACTOR_H
diff --git a/src/actorsprite.cpp b/src/actorsprite.cpp
index 989de8d9..7cb46bd4 100644
--- a/src/actorsprite.cpp
+++ b/src/actorsprite.cpp
@@ -22,40 +22,34 @@
#include "configuration.h"
#include "event.h"
-#include "imagesprite.h"
#include "localplayer.h"
#include "log.h"
+#include "particle.h"
#include "simpleanimation.h"
-#include "statuseffect.h"
+#include "sprite.h"
#include "resources/animation.h"
-#include "resources/image.h"
#include "resources/imageset.h"
#include "resources/resourcemanager.h"
#include "resources/theme.h"
#include "utils/time.h"
+#include <algorithm>
#include <cassert>
#define EFFECTS_FILE "effects.xml"
-ImageSet *ActorSprite::targetCursorImages[2][NUM_TC];
+ResourceRef<ImageSet> ActorSprite::targetCursorImages[2][NUM_TC];
SimpleAnimation *ActorSprite::targetCursor[2][NUM_TC];
bool ActorSprite::loaded = false;
-ActorSprite::ActorSprite(int id):
- mId(id),
- mStatusParticleEffects(&mStunParticleEffects, false),
- mChildParticleEffects(&mStatusParticleEffects, false)
+ActorSprite::ActorSprite(int id)
+ : mId(id)
{}
ActorSprite::~ActorSprite()
{
- setMap(nullptr);
-
- mUsedTargetCursor = nullptr;
-
// Notify listeners of the destruction.
Event event(Event::Destroyed);
event.setActor("source", this);
@@ -64,13 +58,7 @@ ActorSprite::~ActorSprite()
int ActorSprite::getDrawOrder() const
{
- int drawOrder = Actor::getDrawOrder();
-
- // See note at ActorSprite::draw
- if (mMap)
- drawOrder += mMap->getTileHeight() / 2;
-
- return drawOrder;
+ return Actor::getDrawOrder() + paths.getIntValue("spriteOffsetY");
}
bool ActorSprite::draw(Graphics *graphics, int offsetX, int offsetY) const
@@ -81,47 +69,33 @@ bool ActorSprite::draw(Graphics *graphics, int offsetX, int offsetY) const
if (mUsedTargetCursor)
mUsedTargetCursor->draw(graphics, px, py);
- // This is makes sure that actors positioned on the center of a tile have
- // their sprite aligned to the bottom of that tile, mainly to maintain
- // compatibility with older clients.
- if (mMap)
- py += mMap->getTileHeight() / 2;
-
return drawSpriteAt(graphics, px, py);
}
bool ActorSprite::drawSpriteAt(Graphics *graphics, int x, int y) const
{
- return CompoundSprite::draw(graphics, x, y);
+ y += paths.getIntValue("spriteOffsetY");
+ return mSprites.draw(graphics, x, y);
}
void ActorSprite::logic()
{
// Update sprite animations
- update(Time::deltaTimeMs());
+ mSprites.update(Time::deltaTimeMs());
if (mUsedTargetCursor)
mUsedTargetCursor->update(Time::deltaTimeMs());
- // Restart status/particle effects, if needed
- if (mMustResetParticles)
- {
- mMustResetParticles = false;
- for (int statusEffect : mStatusEffects)
- {
- const StatusEffect *effect = StatusEffect::getStatusEffect(statusEffect, true);
- if (effect && effect->particleEffectIsPersistent())
- updateStatusEffect(statusEffect, true);
- }
- }
-
- // See note at ActorSprite::draw
- float py = mPos.y;
- if (mMap)
- py += mMap->getTileHeight() / 2;
+ // Erase all extinct particle effects
+ mChildParticleEffects.erase(std::remove_if(mChildParticleEffects.begin(),
+ mChildParticleEffects.end(),
+ [](const Particle *p) { return p->isExtinct(); }),
+ mChildParticleEffects.end());
- // Update particle effects
- mChildParticleEffects.moveTo(mPos.x, py);
+ // Move the remaining
+ const float py = mPos.y + paths.getIntValue("spriteOffsetY");
+ for (Particle *p : mChildParticleEffects)
+ p->moveTo(mPos.x, py);
}
void ActorSprite::setMap(Map* map)
@@ -130,12 +104,11 @@ void ActorSprite::setMap(Map* map)
// Clear particle effect list because child particles became invalid
mChildParticleEffects.clear();
- mMustResetParticles = true; // Reset status particles on next redraw
}
void ActorSprite::controlParticle(Particle *particle)
{
- mChildParticleEffects.addLocally(particle);
+ mChildParticleEffects.emplace_back(particle);
}
void ActorSprite::setTargetType(TargetCursorType type)
@@ -146,115 +119,22 @@ void ActorSprite::setTargetType(TargetCursorType type)
mUsedTargetCursor = targetCursor[type][getTargetCursorSize()];
}
-void ActorSprite::setStatusEffect(int index, bool active)
-{
- const bool wasActive = mStatusEffects.find(index) != mStatusEffects.end();
-
- if (active != wasActive)
- {
- updateStatusEffect(index, active);
- if (active)
- mStatusEffects.insert(index);
- else
- mStatusEffects.erase(index);
- }
-}
-
-void ActorSprite::setStatusEffectBlock(int offset, uint16_t newEffects)
-{
- for (int i = 0; i < STATUS_EFFECTS; i++)
- {
- int index = StatusEffect::blockEffectIndexToEffectIndex(offset + i);
-
- if (index != -1)
- setStatusEffect(index, (newEffects & (1 << i)) > 0);
- }
-}
-
-void ActorSprite::updateStunMode(int oldMode, int newMode)
-{
- if (this == local_player)
- {
- Event event(Event::Stun);
- event.setInt("oldMode", oldMode);
- event.setInt("newMode", newMode);
- event.trigger(Event::ActorSpriteChannel);
- }
-
- handleStatusEffect(StatusEffect::getStatusEffect(oldMode, false), -1);
- handleStatusEffect(StatusEffect::getStatusEffect(newMode, true), -1);
-}
-
-void ActorSprite::updateStatusEffect(int index, bool newStatus)
-{
- if (this == local_player)
- {
- Event event(Event::UpdateStatusEffect);
- event.setInt("index", index);
- event.setBool("newStatus", newStatus);
- event.trigger(Event::ActorSpriteChannel);
- }
-
- handleStatusEffect(StatusEffect::getStatusEffect(index, newStatus), index);
-}
-
-void ActorSprite::handleStatusEffect(StatusEffect *effect, int effectId)
-{
- if (!effect)
- return;
-
- // TODO: Find out how this is meant to be used
- // (SpriteAction != Being::Action)
- //SpriteAction action = effect->getAction();
- //if (action != ACTION_INVALID)
- // setAction(action);
-
- Particle *particle = effect->getParticle();
-
- if (effectId >= 0)
- {
- mStatusParticleEffects.setLocally(effectId, particle);
- }
- else
- {
- mStunParticleEffects.clearLocally();
- if (particle)
- mStunParticleEffects.addLocally(particle);
- }
-}
-
void ActorSprite::setupSpriteDisplay(const SpriteDisplay &display,
bool forceDisplay)
{
- clear();
+ mSprites.clear();
for (const auto &sprite : display.sprites)
{
std::string file = paths.getStringValue("sprites") + sprite.sprite;
- addSprite(AnimatedSprite::load(file, sprite.variant));
+ mSprites.add(Sprite::load(file, sprite.variant));
}
// Ensure that something is shown, if desired
- if (size() == 0 && forceDisplay)
+ if (mSprites.size() == 0 && forceDisplay)
{
- if (display.image.empty())
- {
- addSprite(AnimatedSprite::load(paths.getStringValue("sprites")
- + paths.getStringValue("spriteErrorFile")));
- }
- else
- {
- ResourceManager *resman = ResourceManager::getInstance();
- std::string imagePath = paths.getStringValue("itemIcons")
- + display.image;
- Image *img = resman->getImage(imagePath);
-
- if (!img)
- img = Theme::getImageFromTheme(
- paths.getStringValue("unknownItemFile"));
-
- addSprite(new ImageSprite(img));
- }
+ mSprites.add(Sprite::load(paths.getStringValue("sprites")
+ + paths.getStringValue("spriteErrorFile")));
}
mChildParticleEffects.clear();
@@ -265,8 +145,6 @@ void ActorSprite::setupSpriteDisplay(const SpriteDisplay &display,
for (const auto &particle : display.particles)
controlParticle(particleEngine->addEffect(particle, 0, 0));
}
-
- mMustResetParticles = true;
}
void ActorSprite::load()
@@ -320,8 +198,7 @@ void ActorSprite::cleanupTargetCursors()
for (int type = 0; type < NUM_TCT; type++)
{
delete targetCursor[type][size];
- if (targetCursorImages[type][size])
- targetCursorImages[type][size]->decRef();
+ targetCursorImages[type][size] = nullptr;
}
}
}
@@ -333,8 +210,7 @@ void ActorSprite::loadTargetCursor(const std::string &filename,
assert(size < 3);
ResourceManager *resman = ResourceManager::getInstance();
- ImageSet *currentImageSet = resman->getImageSet(filename, width, height);
-
+ auto currentImageSet = resman->getImageSet(filename, width, height);
if (!currentImageSet)
{
logger->log("Error loading target cursor: %s", filename.c_str());
diff --git a/src/actorsprite.h b/src/actorsprite.h
index 8c20f81f..5993e4ea 100644
--- a/src/actorsprite.h
+++ b/src/actorsprite.h
@@ -18,21 +18,16 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef ACTORSPRITE_H
-#define ACTORSPRITE_H
+#pragma once
#include "actor.h"
#include "compoundsprite.h"
#include "map.h"
-#include "particlecontainer.h"
-
-#include <cstdint>
-#include <set>
+#include "particle.h"
class SimpleAnimation;
-class StatusEffect;
-class ActorSprite : public CompoundSprite, public Actor
+class ActorSprite : public Actor
{
public:
enum Type
@@ -41,7 +36,8 @@ public:
PLAYER,
NPC,
MONSTER,
- FLOOR_ITEM
+ FLOOR_ITEM,
+ PORTAL
};
enum TargetCursorSize
@@ -78,7 +74,7 @@ public:
bool draw(Graphics *graphics, int offsetX, int offsetY) const override;
- virtual bool drawSpriteAt(Graphics *graphics, int x, int y) const;
+ bool drawSpriteAt(Graphics *graphics, int x, int y) const;
virtual void logic();
@@ -111,79 +107,25 @@ public:
*/
void untarget() { mUsedTargetCursor = nullptr; }
- /**
- * Sets the actor's stun mode. If zero, the being is `normal', otherwise it
- * is `stunned' in some fashion.
- */
- void setStunMode(int stunMode)
- {
- if (mStunMode != stunMode)
- updateStunMode(mStunMode, stunMode);
- mStunMode = stunMode;
- }
-
- void setStatusEffect(int index, bool active);
-
- /**
- * A status effect block is a 16 bit mask of status effects. We assign each
- * such flag a block ID of offset + bitnr.
- *
- * These are NOT the same as the status effect indices.
- */
- void setStatusEffectBlock(int offset, uint16_t flags);
-
- void setAlpha(float alpha) override
- { CompoundSprite::setAlpha(alpha); }
+ void setAlpha(float alpha) override { mSprites.setAlpha(alpha); }
+ float getAlpha() const override { return mSprites.getAlpha(); }
- float getAlpha() const override
- { return CompoundSprite::getAlpha(); }
-
- int getWidth() const override
- { return CompoundSprite::getWidth(); }
-
- int getHeight() const override
- { return CompoundSprite::getHeight(); }
+ int getWidth() const { return mSprites.getWidth(); }
+ int getHeight() const { return mSprites.getHeight(); }
static void load();
-
static void unload();
protected:
- /**
- * Notify self that the stun mode has been updated. Invoked by
- * setStunMode if something changed.
- */
- virtual void updateStunMode(int oldMode, int newMode);
-
- /**
- * Notify self that a status effect has flipped.
- * The new flag is passed.
- */
- virtual void updateStatusEffect(int index, bool newStatus);
-
- /**
- * Handle an update to a status or stun effect
- *
- * \param effect The StatusEffect to effect
- * \param effectId -1 for stun, otherwise the effect index
- */
- virtual void handleStatusEffect(StatusEffect *effect, int effectId);
-
void setupSpriteDisplay(const SpriteDisplay &display,
bool forceDisplay = true);
int mId;
- uint16_t mStunMode = 0; /**< Stun mode; zero if not stunned */
- std::set<int> mStatusEffects; /**< set of active status effects */
+ std::vector<ParticleHandle> mChildParticleEffects;
- ParticleList mStunParticleEffects;
- ParticleVector mStatusParticleEffects;
- ParticleList mChildParticleEffects;
+ CompoundSprite mSprites;
private:
- /** Reset particle status effects on next redraw? */
- bool mMustResetParticles = false;
-
/** Load the target cursors into memory */
static void initTargetCursor();
@@ -197,7 +139,7 @@ private:
int width, int height, int type, int size);
/** Images of the target cursor. */
- static ImageSet *targetCursorImages[NUM_TCT][NUM_TC];
+ static ResourceRef<ImageSet> targetCursorImages[NUM_TCT][NUM_TC];
/** Animated target cursors. */
static SimpleAnimation *targetCursor[NUM_TCT][NUM_TC];
@@ -207,5 +149,3 @@ private:
/** Target cursor being used */
SimpleAnimation *mUsedTargetCursor = nullptr;
};
-
-#endif // ACTORSPRITE_H
diff --git a/src/actorspritemanager.cpp b/src/actorspritemanager.cpp
index f7da58ec..74705f80 100644
--- a/src/actorspritemanager.cpp
+++ b/src/actorspritemanager.cpp
@@ -65,8 +65,8 @@ class PlayerNPCNamesLister : public AutoCompleteLister
ActorSpriteManager::ActorSpriteManager()
{
- mPlayerNames = new PlayerNamesLister;
- mPlayerNPCNames = new PlayerNPCNamesLister;
+ mPlayerNames = std::make_unique<PlayerNamesLister>();
+ mPlayerNPCNames = std::make_unique<PlayerNPCNamesLister>();
listen(Event::ConfigChannel);
}
@@ -342,12 +342,12 @@ bool ActorSpriteManager::hasActorSprite(ActorSprite *someActor) const
AutoCompleteLister *ActorSpriteManager::getPlayerNameLister() const
{
- return mPlayerNames;
+ return mPlayerNames.get();
}
AutoCompleteLister *ActorSpriteManager::getPlayerNPCNameLister() const
{
- return mPlayerNPCNames;
+ return mPlayerNPCNames.get();
}
void ActorSpriteManager::updatePlayerNames()
diff --git a/src/actorspritemanager.h b/src/actorspritemanager.h
index edbe51eb..5df50006 100644
--- a/src/actorspritemanager.h
+++ b/src/actorspritemanager.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef ACTORSPRITEMANAGER_H
-#define ACTORSPRITEMANAGER_H
+#pragma once
#include "actorsprite.h"
#include "being.h"
@@ -29,6 +28,8 @@
#include "gui/widgets/textfield.h"
+#include <memory>
+
class LocalPlayer;
class Map;
@@ -169,13 +170,11 @@ class ActorSpriteManager : public EventListener
friend class PlayerNamesLister;
friend class PlayerNPCNamesLister;
- AutoCompleteLister *mPlayerNames;
- AutoCompleteLister *mPlayerNPCNames;
+ std::unique_ptr<AutoCompleteLister> mPlayerNames;
+ std::unique_ptr<AutoCompleteLister> mPlayerNPCNames;
ActorSprites mActors;
ActorSprites mDeleteActors;
Map *mMap;
};
extern ActorSpriteManager *actorSpriteManager;
-
-#endif // ACTORSPRITEMANAGER_H
diff --git a/src/animatedsprite.h b/src/animatedsprite.h
deleted file mode 100644
index 34655bff..00000000
--- a/src/animatedsprite.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * The Mana Client
- * Copyright (C) 2004-2009 The Mana World Development Team
- * Copyright (C) 2009-2012 The Mana Developers
- *
- * This file is part of The Mana Client.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ANIMATEDSPRITE_H
-#define ANIMATEDSPRITE_H
-
-#include "sprite.h"
-
-#include <string>
-
-class Animation;
-struct Frame;
-
-/**
- * Animates a sprite by adding playback state.
- */
-class AnimatedSprite final : public Sprite
-{
- public:
- /**
- * Constructor.
- * @param sprite the sprite to animate
- */
- AnimatedSprite(SpriteDef *sprite);
-
- /**
- * An helper function, which will request the sprite to animate
- * from the resource manager.
- *
- * @param filename the file of the sprite to animate
- * @param variant the sprite variant
- */
- static AnimatedSprite *load(const std::string &filename,
- int variant = 0);
-
- ~AnimatedSprite() override;
-
- bool reset() override;
-
- bool play(const std::string &action) override;
-
- bool update(int time) override;
-
- bool draw(Graphics *graphics, int posX, int posY) const override;
-
- int getWidth() const override;
-
- int getHeight() const override;
-
- int getOffsetX() const override;
-
- int getOffsetY() const override;
-
- const Image *getImage() const override;
-
- bool setDirection(SpriteDirection direction) override;
-
- int getDuration() const override;
-
- private:
- bool updateCurrentAnimation(int dt);
-
- SpriteDirection mDirection = DIRECTION_DOWN; /**< The sprite direction. */
-
- int mFrameIndex = 0; /**< The index of the current frame. */
- int mFrameTime = 0; /**< The time since start of frame. */
-
- ResourceRef<SpriteDef> mSprite; /**< The sprite definition. */
- Action *mAction = nullptr; /**< The currently active action. */
- Animation *mAnimation = nullptr; /**< The currently active animation. */
- Frame *mFrame = nullptr; /**< The currently active frame. */
-};
-
-#endif
diff --git a/src/animationparticle.h b/src/animationparticle.h
index 432664e5..162eb192 100644
--- a/src/animationparticle.h
+++ b/src/animationparticle.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef ANIMATION_PARTICLE_H
-#define ANIMATION_PARTICLE_H
+#pragma once
#include "imageparticle.h"
#include "simpleanimation.h"
@@ -44,5 +43,3 @@ class AnimationParticle : public ImageParticle
private:
SimpleAnimation mAnimation; /**< Used animation for this particle */
};
-
-#endif
diff --git a/src/avatar.h b/src/avatar.h
index 29fc8c56..7c8de4fa 100644
--- a/src/avatar.h
+++ b/src/avatar.h
@@ -19,15 +19,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef AVATAR_H
-#define AVATAR_H
+#pragma once
#include <string>
class Avatar
{
public:
- Avatar(const std::string &name = std::string());
+ explicit Avatar(const std::string &name = {});
/**
* Returns the avatar's name.
@@ -68,5 +67,3 @@ private:
bool mOnline;
bool mDisplayBold;
};
-
-#endif // AVATAR_H
diff --git a/src/being.cpp b/src/being.cpp
index 56ed65f7..db9a9deb 100644
--- a/src/being.cpp
+++ b/src/being.cpp
@@ -22,7 +22,6 @@
#include "being.h"
#include "actorspritemanager.h"
-#include "animatedsprite.h"
#include "client.h"
#include "configuration.h"
#include "effectmanager.h"
@@ -36,6 +35,8 @@
#include "party.h"
#include "playerrelations.h"
#include "sound.h"
+#include "sprite.h"
+#include "statuseffect.h"
#include "text.h"
#include "gui/gui.h"
@@ -53,6 +54,7 @@
#include "resources/iteminfo.h"
#include "resources/monsterdb.h"
#include "resources/npcdb.h"
+#include "resources/statuseffectdb.h"
#include "resources/theme.h"
#include "resources/userpalette.h"
@@ -60,25 +62,18 @@
#include <cmath>
-Being::Being(int id, Type type, int subtype, Map *map):
- ActorSprite(id),
- mInfo(BeingInfo::Unknown),
- mType(type)
+Being::Being(int id, Type type, int subtype, Map *map)
+ : ActorSprite(id)
+ , mInfo(BeingInfo::Unknown)
{
setMap(map);
- setSubtype(subtype);
+ setType(type, subtype);
mSpeechBubble = new SpeechBubble;
+ mSpeechBubble->addDeathListener(this);
mMoveSpeed = Net::getPlayerHandler()->getDefaultMoveSpeed();
- if (getType() == PLAYER)
- mShowName = config.visibleNames;
-
- if (getType() == PLAYER || getType() == NPC)
- setShowName(true);
-
- updateColors();
listen(Event::ConfigChannel);
listen(Event::ChatChannel);
}
@@ -88,20 +83,27 @@ Being::~Being()
delete mSpeechBubble;
delete mDispName;
delete mText;
- mSpeechBubble = nullptr;
- mDispName = nullptr;
- mText = nullptr;
-
- removeAllSpriteParticles();
}
-void Being::setSubtype(Uint16 subtype)
+/**
+ * Can be used to change the type of the being.
+ *
+ * Practical use: players (usually GMs) can change into monsters and back.
+ */
+void Being::setType(Type type, int subtype)
{
- if (subtype == mSubType)
+ if (mType == type && mSubType == subtype)
return;
+ mType = type;
mSubType = subtype;
+ for (auto &spriteState : mSpriteStates)
+ {
+ spriteState.visibleId = 0;
+ spriteState.particles.clear();
+ }
+
switch (getType())
{
case MONSTER:
@@ -112,8 +114,12 @@ void Being::setSubtype(Uint16 subtype)
case NPC:
mInfo = NPCDB::get(mSubType);
setupSpriteDisplay(mInfo->display, false);
+ mShowName = true;
break;
case PLAYER: {
+ mSprites.clear();
+ mChildParticleEffects.clear();
+
int id = -100 - subtype;
// Prevent showing errors when sprite doesn't exist
@@ -121,12 +127,22 @@ void Being::setSubtype(Uint16 subtype)
id = -100;
setSprite(Net::getCharHandler()->baseSprite(), id);
+ restoreAllSpriteParticles();
+ mShowName = this == local_player ? config.showOwnName
+ : config.visibleNames;
break;
}
case FLOOR_ITEM:
+ case PORTAL:
case UNKNOWN:
break;
}
+
+ mSprites.doRedraw();
+
+ updateName();
+ updateNamePosition();
+ updateColors();
}
bool Being::isTargetSelection() const
@@ -172,7 +188,7 @@ void Being::setPosition(const Vector &pos)
{
Actor::setPosition(pos);
- updateCoords();
+ updateNamePosition();
if (mText)
mText->adviseXY(getPixelX(), getSpeechTextYPosition());
@@ -294,7 +310,7 @@ void Being::setSpeech(const std::string &text, int time)
mText = new Text(mSpeech,
getPixelX(), getSpeechTextYPosition(),
gcn::Graphics::CENTER,
- &userPalette->getColor(UserPalette::PARTICLE),
+ &Theme::getThemeColor(Theme::BUBBLE_TEXT),
true);
}
}
@@ -303,8 +319,8 @@ void Being::takeDamage(Being *attacker, int amount,
AttackType type, int attackId)
{
gcn::Font *font;
- std::string damage = amount ? toString(amount) : type == FLEE ?
- "dodge" : "miss";
+ std::string damage = amount ? toString(amount)
+ : (type == FLEE ? "dodge" : "miss");
const gcn::Color *color;
font = gui->getInfoParticleFont();
@@ -360,7 +376,7 @@ void Being::takeDamage(Being *attacker, int amount,
if (amount > 0)
{
- auto &hurtSfx = mInfo->getSound(SoundEvent::HURT);
+ auto &hurtSfx = mInfo->getSound(SoundEvent::Hurt);
if (attacker)
sound.playSfx(hurtSfx, attacker->getPixelX(), attacker->getPixelY());
else
@@ -433,14 +449,14 @@ void Being::handleAttack(Being *victim, int damage, int attackId)
if (!itemInfo)
itemInfo = &itemDb->get(-100 - mSubType);
- const auto event = damage > 0 ? EquipmentSoundEvent::HIT
- : EquipmentSoundEvent::STRIKE;
+ const auto event = damage > 0 ? EquipmentSoundEvent::Hit
+ : EquipmentSoundEvent::Strike;
const auto &soundFile = itemInfo->getSound(event);
sound.playSfx(soundFile, getPixelX(), getPixelY());
}
else
{
- const auto event = damage > 0 ? SoundEvent::HIT : SoundEvent::MISS;
+ const auto event = damage > 0 ? SoundEvent::Hit : SoundEvent::Miss;
const auto &soundFile = mInfo->getSound(event);
sound.playSfx(soundFile, getPixelX(), getPixelY());
}
@@ -449,17 +465,11 @@ void Being::handleAttack(Being *victim, int damage, int attackId)
void Being::setName(const std::string &name)
{
if (getType() == NPC)
- {
mName = name.substr(0, name.find('#', 0));
- showName();
- }
else
- {
mName = name;
- if (getType() == PLAYER && getShowName())
- showName();
- }
+ updateName();
}
void Being::setShowName(bool doShowName)
@@ -468,14 +478,7 @@ void Being::setShowName(bool doShowName)
return;
mShowName = doShowName;
-
- if (doShowName)
- showName();
- else
- {
- delete mDispName;
- mDispName = nullptr;
- }
+ updateName();
}
void Being::setGuildName(const std::string &name)
@@ -590,7 +593,34 @@ void Being::fireMissile(Being *victim, const std::string &particle)
missile->setDieDistance(8);
missile->setLifetime(900);
}
+}
+
+void Being::setStatusEffect(int id, bool active)
+{
+ const auto it = mStatusEffects.find(id);
+ const bool wasActive = it != mStatusEffects.end();
+ if (active != wasActive)
+ {
+ if (active)
+ mStatusEffects.insert(id);
+ else
+ mStatusEffects.erase(it);
+
+ updateStatusEffect(id, active);
+ }
+}
+
+void Being::updateStatusEffect(int id, bool newStatus)
+{
+ auto effect = StatusEffectDB::getStatusEffect(id);
+ if (!effect)
+ return;
+
+ if (Particle *particle = effect->getParticle(newStatus))
+ mStatusParticleEffects[id] = ParticleHandle(particle);
+ else
+ mStatusParticleEffects.erase(id);
}
void Being::setAction(Action action, int attackId)
@@ -612,12 +642,12 @@ void Being::setAction(Action action, int attackId)
if (mEquippedWeapon)
{
currentAction = mEquippedWeapon->attackAction;
- reset();
+ mSprites.reset();
}
else
{
currentAction = mInfo->getAttack(attackId).action;
- reset();
+ mSprites.reset();
// Attack particle effect
if (Particle::enabled)
@@ -634,7 +664,6 @@ void Being::setAction(Action action, int attackId)
}
effectManager->trigger(effectId, this, rotation);
}
-
}
break;
@@ -646,7 +675,7 @@ void Being::setAction(Action action, int attackId)
break;
case DEAD:
currentAction = SpriteAction::DEAD;
- sound.playSfx(mInfo->getSound(SoundEvent::DIE),
+ sound.playSfx(mInfo->getSound(SoundEvent::Die),
getPixelX(), getPixelY());
break;
case STAND:
@@ -656,7 +685,7 @@ void Being::setAction(Action action, int attackId)
if (currentAction != SpriteAction::INVALID)
{
- play(currentAction);
+ mSprites.play(currentAction);
mAction = action;
}
@@ -664,6 +693,14 @@ void Being::setAction(Action action, int attackId)
mActionTimer.set();
}
+void Being::setAction(const std::string &action)
+{
+ // Actions are triggered by strings from abilities when using manaserv,
+ // it's not necessarily an attack, but it seems the most appropriate value.
+ mAction = ATTACK;
+ mSprites.play(action);
+}
+
void Being::lookAt(const Vector &destPos)
{
// We first handle simple cases
@@ -744,14 +781,14 @@ void Being::lookAt(const Vector &destPos)
}
}
-void Being::setDirection(Uint8 direction)
+void Being::setDirection(uint8_t direction)
{
if (!direction || mDirection == direction)
return;
mDirection = direction;
- SpriteDirection dir;
+ SpriteDirection dir = DIRECTION_DEFAULT;
if (mDirection & UP)
dir = DIRECTION_UP;
else if (mDirection & DOWN)
@@ -762,7 +799,8 @@ void Being::setDirection(Uint8 direction)
dir = DIRECTION_LEFT;
mSpriteDirection = dir;
- CompoundSprite::setDirection(dir);
+ updatePlayerSprites();
+ mSprites.setDirection(dir);
}
int Being::getCollisionRadius() const
@@ -780,15 +818,34 @@ void Being::logic()
mText = nullptr;
}
- if (mRestoreSpriteParticlesOnLogic)
+ if (mRestoreParticlesOnLogic)
{
- mRestoreSpriteParticlesOnLogic = false;
+ mRestoreParticlesOnLogic = false;
+
restoreAllSpriteParticles();
+
+ // Restart status/particle effects, if needed
+ for (int id : mStatusEffects)
+ {
+ const StatusEffect *effect = StatusEffectDB::getStatusEffect(id);
+ if (effect && effect->persistentParticleEffect)
+ updateStatusEffect(id, true);
+ }
}
if (mAction != DEAD && !mSpeedPixelsPerSecond.isNull())
{
updateMovement();
+
+ // Update particle effects
+ const float py = mPos.y + paths.getIntValue("spriteOffsetY");
+
+ for (auto &spriteState : mSpriteStates)
+ for (auto &particle : spriteState.particles)
+ particle->moveTo(mPos.x, py);
+
+ for (auto &[_, p] : mStatusParticleEffects)
+ p->moveTo(mPos.x, py);
}
ActorSprite::logic();
@@ -796,7 +853,7 @@ void Being::logic()
// Remove it after 1.5 secs if the dead animation isn't long enough,
// or simply play it until it's finished.
if (!isAlive() && Net::getGameHandler()->removeDeadBeings() && getType() != PLAYER)
- if (mActionTimer.elapsed() > std::max(getDuration(), 1500))
+ if (mActionTimer.elapsed() > std::max(mSprites.getMaxDuration(), 1500))
actorSpriteManager->scheduleDelete(this);
}
@@ -940,7 +997,7 @@ void Being::drawSpeech(int offsetX, int offsetY)
mText = new Text(mSpeech,
getPixelX(), getPixelY() - getHeight(),
gcn::Graphics::CENTER,
- &userPalette->getColor(UserPalette::PARTICLE),
+ &Theme::getThemeColor(Theme::BUBBLE_TEXT),
true);
}
}
@@ -953,7 +1010,7 @@ void Being::drawSpeech(int offsetX, int offsetY)
}
}
-void Being::updateCoords()
+void Being::updateNamePosition()
{
if (!mDispName)
return;
@@ -971,24 +1028,28 @@ void Being::flashName(int time)
mDispName->flash(time);
}
-void Being::showName()
+void Being::updateName()
{
delete mDispName;
mDispName = nullptr;
+
+ if (!mShowName)
+ return;
+
std::string mDisplayName(mName);
if (getType() == PLAYER)
{
if (config.showGender)
{
- if (getGender() == Gender::FEMALE)
+ if (getGender() == Gender::Female)
mDisplayName += " \u2640";
- else if (getGender() == Gender::MALE)
+ else if (getGender() == Gender::Male)
mDisplayName += " \u2642";
}
// Display the IP when under tmw-Athena (GM only).
- if (Net::getNetworkType() == ServerType::TMWATHENA && local_player
+ if (Net::getNetworkType() == ServerType::TmwAthena && local_player
&& local_player->getShowIp() && getIp())
{
mDisplayName += strprintf(" %s", ipToString(getIp()));
@@ -1013,7 +1074,7 @@ void Being::showName()
mDispName = new FlashText(mDisplayName, getPixelX(), getPixelY(),
gcn::Graphics::CENTER, mNameColor, font);
- updateCoords();
+ updateNamePosition();
}
void Being::addSpriteParticles(SpriteState &spriteState, const SpriteDisplay &display)
@@ -1026,27 +1087,15 @@ void Being::addSpriteParticles(SpriteState &spriteState, const SpriteDisplay &di
for (const auto &particle : display.particles)
{
Particle *p = particleEngine->addEffect(particle, 0, 0, 0);
- controlParticle(p);
- spriteState.particles.push_back(p);
+ spriteState.particles.emplace_back(p);
}
}
-void Being::removeSpriteParticles(SpriteState &spriteState)
-{
- for (auto particle : spriteState.particles)
- mChildParticleEffects.removeLocally(particle);
-
- spriteState.particles.clear();
-}
-
-void Being::removeAllSpriteParticles()
-{
- for (auto &spriteState : mSpriteStates)
- removeSpriteParticles(spriteState);
-}
-
void Being::restoreAllSpriteParticles()
{
+ if (mType != PLAYER)
+ return;
+
for (auto &spriteState : mSpriteStates)
{
if (spriteState.id)
@@ -1094,17 +1143,121 @@ void Being::updateColors()
}
if (mDispName)
- {
mDispName->setColor(mNameColor);
+}
+
+/**
+ * Updates the visible sprite IDs of the player, taking into account the item
+ * replacements.
+ */
+void Being::updatePlayerSprites()
+{
+ if (mType != PLAYER)
+ return;
+
+ // hack for allow different logic in dead player
+ const int direction = mAction == DEAD ? DIRECTION_DEAD : mSpriteDirection;
+
+ // Get the current item IDs
+ std::vector<int> itemIDs(mSpriteStates.size());
+ for (size_t i = 0; i < mSpriteStates.size(); i++)
+ itemIDs[i] = mSpriteStates[i].id;
+
+ // Apply the replacements
+ for (auto &spriteState : mSpriteStates)
+ {
+ if (!spriteState.id)
+ continue;
+
+ auto &itemInfo = itemDb->get(spriteState.id);
+ for (const auto &replacement : itemInfo.replacements)
+ {
+ if (replacement.direction != DIRECTION_ALL && replacement.direction != direction)
+ continue;
+
+ if (replacement.sprite == SPRITE_ALL)
+ {
+ if (replacement.items.empty())
+ {
+ itemIDs.assign(itemIDs.size(), 0);
+ }
+ else
+ {
+ for (int &id : itemIDs)
+ {
+ for (auto &item : replacement.items)
+ if (!item.from || id == item.from)
+ id = item.to;
+ }
+ }
+ }
+ else if (replacement.sprite < itemIDs.size())
+ {
+ int &id = itemIDs[replacement.sprite];
+
+ if (replacement.items.empty())
+ {
+ id = 0;
+ }
+ else
+ {
+ for (auto &item : replacement.items)
+ if (!item.from || id == item.from)
+ id = item.to;
+ }
+ }
+ }
}
+
+ // Set the new sprites
+ bool newSpriteSet = false;
+
+ mSprites.ensureSize(mSpriteStates.size());
+
+ for (size_t i = 0; i < mSpriteStates.size(); i++)
+ {
+ auto &spriteState = mSpriteStates[i];
+ if (spriteState.visibleId == itemIDs[i])
+ continue;
+
+ spriteState.visibleId = itemIDs[i];
+
+ if (spriteState.visibleId == 0)
+ {
+ mSprites.set(i, nullptr);
+ }
+ else
+ {
+ newSpriteSet = true;
+
+ auto &itemInfo = itemDb->get(spriteState.visibleId);
+ std::string filename = itemInfo.getSprite(mGender, mSubType);
+ Sprite *equipmentSprite = nullptr;
+
+ if (!filename.empty())
+ {
+ if (!spriteState.color.empty())
+ filename += "|" + spriteState.color;
+
+ equipmentSprite = Sprite::load(
+ paths.getStringValue("sprites") + filename);
+
+ if (equipmentSprite)
+ equipmentSprite->setDirection(getSpriteDirection());
+ }
+
+ mSprites.set(i, equipmentSprite);
+ }
+ }
+
+ // Make sure any new sprites are set to the correct action
+ if (newSpriteSet)
+ setAction(mAction);
}
void Being::setSprite(unsigned slot, int id, const std::string &color,
bool isWeapon)
{
- if (slot >= size())
- ensureSize(slot + 1);
-
if (slot >= mSpriteStates.size())
mSpriteStates.resize(slot + 1);
@@ -1112,45 +1265,35 @@ void Being::setSprite(unsigned slot, int id, const std::string &color,
// Clear current particles when the ID changes
if (spriteState.id != id)
- removeSpriteParticles(spriteState);
+ spriteState.particles.clear();
+
+ // Clear the current sprite when the color changes
+ if (spriteState.color != color && spriteState.visibleId)
+ {
+ spriteState.visibleId = 0;
+ mSprites.set(slot, nullptr);
+ }
spriteState.id = id;
spriteState.color = color;
if (id == 0) // id = 0 means unequip
{
- removeSprite(slot);
-
if (isWeapon)
mEquippedWeapon = nullptr;
}
else
{
auto &itemInfo = itemDb->get(id);
- std::string filename = itemInfo.getSprite(mGender, mSubType);
- AnimatedSprite *equipmentSprite = nullptr;
-
- if (!filename.empty())
- {
- if (!color.empty())
- filename += "|" + color;
-
- equipmentSprite = AnimatedSprite::load(
- paths.getStringValue("sprites") + filename);
- }
-
- if (equipmentSprite)
- equipmentSprite->setDirection(getSpriteDirection());
- CompoundSprite::setSprite(slot, equipmentSprite);
-
- addSpriteParticles(spriteState, itemInfo.display);
+ if (mType == PLAYER)
+ addSpriteParticles(spriteState, itemInfo.display);
if (isWeapon)
mEquippedWeapon = &itemInfo;
-
- setAction(mAction);
}
+
+ updatePlayerSprites();
}
void Being::setSpriteID(unsigned slot, int id)
@@ -1168,13 +1311,7 @@ void Being::setSpriteColor(unsigned slot, const std::string &color)
bool Being::drawnWhenBehind() const
{
// For now, just draw actors with only one layer when obscured
- return CompoundSprite::getNumberOfLayers() == 1;
-}
-
-void Being::updateName()
-{
- if (mShowName)
- showName();
+ return mSprites.getNumberOfLayers() == 1;
}
void Being::setGender(Gender gender)
@@ -1183,15 +1320,21 @@ void Being::setGender(Gender gender)
{
mGender = gender;
- // Reload all subsprites
+ // Reset all sprites to force reload with the correct gender
for (size_t i = 0; i < mSpriteStates.size(); i++)
{
- auto &sprite = mSpriteStates[i];
- if (sprite.id != 0)
- setSprite(i, sprite.id, sprite.color);
+ auto &spriteState = mSpriteStates[i];
+ if (spriteState.visibleId)
+ {
+ mSprites.set(i, nullptr);
+ spriteState.visibleId = 0;
+ }
}
- updateName();
+ updatePlayerSprites();
+
+ if (config.showGender)
+ updateName();
}
}
@@ -1202,6 +1345,17 @@ void Being::setGM(bool gm)
updateColors();
}
+void Being::setIp(int ip)
+{
+ if (mIp == ip)
+ return;
+
+ mIp = ip;
+
+ if (local_player && local_player->getShowIp())
+ updateName();
+}
+
bool Being::canTalk()
{
return mType == NPC;
@@ -1239,11 +1393,20 @@ void Being::event(Event::Channel channel, const Event &event)
}
}
+void Being::death(const gcn::Event &event)
+{
+ if (event.getSource() == mSpeechBubble)
+ mSpeechBubble = nullptr;
+}
+
void Being::setMap(Map *map)
{
- // Remove sprite particles because ActorSprite is going to kill them all
- removeAllSpriteParticles();
- mRestoreSpriteParticlesOnLogic = true;
+ for (auto &spriteState : mSpriteStates)
+ spriteState.particles.clear();
+
+ mStatusParticleEffects.clear();
+
+ mRestoreParticlesOnLogic = true;
ActorSprite::setMap(map);
diff --git a/src/being.h b/src/being.h
index b8c5a0f3..96749651 100644
--- a/src/being.h
+++ b/src/being.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef BEING_H
-#define BEING_H
+#pragma once
#include "actorsprite.h"
#include "eventlistener.h"
@@ -32,8 +31,11 @@
#include "utils/time.h"
#include <guichan/color.hpp>
+#include <guichan/deathlistener.hpp>
+#include <cstdint>
#include <map>
+#include <set>
#include <string>
#include <vector>
@@ -54,13 +56,13 @@ class Text;
enum class Gender
{
- MALE = 0,
- FEMALE = 1,
- UNSPECIFIED = 2,
- HIDDEN = 3
+ Male = 0,
+ Female = 1,
+ Unspecified = 2,
+ Hidden = 3
};
-class Being : public ActorSprite, public EventListener
+class Being : public ActorSprite, public EventListener, public gcn::DeathListener
{
public:
/**
@@ -110,7 +112,8 @@ class Being : public ActorSprite, public EventListener
* Constructor.
*
* @param id a unique being id
- * @param subtype partly determines the type of the being
+ * @param type type of being
+ * @param subtype refers to a specific npc, monster, species, etc.
* @param map the map the being is on
*/
Being(int id, Type type, int subtype, Map *map);
@@ -120,6 +123,8 @@ class Being : public ActorSprite, public EventListener
Type getType() const final
{ return mType; }
+ void setType(Type type, int subtype);
+
/**
* Removes all path nodes from this being.
*/
@@ -270,6 +275,9 @@ class Being : public ActorSprite, public EventListener
void setSpriteColor(unsigned slot,
const std::string &color = std::string());
+ unsigned getSpriteCount() const
+ { return mSpriteStates.size(); }
+
bool drawnWhenBehind() const override;
/**
@@ -284,11 +292,6 @@ class Being : public ActorSprite, public EventListener
uint16_t getSubType() const { return mSubType; }
- /**
- * Set Being's subtype (mostly for view for monsters and NPCs)
- */
- void setSubtype(uint16_t subtype);
-
const BeingInfo &getInfo() const
{ return *mInfo; }
@@ -334,6 +337,11 @@ class Being : public ActorSprite, public EventListener
virtual void setAction(Action action, int attackId = 1);
/**
+ * Sets the current action by name.
+ */
+ void setAction(const std::string &action);
+
+ /**
* Get the being's action currently performed.
*/
Action getCurrentAction() const { return mAction; }
@@ -381,6 +389,8 @@ class Being : public ActorSprite, public EventListener
*/
void fireMissile(Being *target, const std::string &particle);
+ void setStatusEffect(int id, bool active);
+
/**
* Returns the path this being is following. An empty path is returned
* when this being isn't following any path currently.
@@ -417,7 +427,7 @@ class Being : public ActorSprite, public EventListener
* Sets the IP or an IP hash.
* The TMW-Athena server sends this information only to GMs.
*/
- void setIp(int ip) { mIp = ip; }
+ void setIp(int ip);
/**
* Returns the player's IP or an IP hash.
@@ -429,8 +439,12 @@ class Being : public ActorSprite, public EventListener
void talkTo();
+ // EventListener
void event(Event::Channel channel, const Event &event) override;
+ // gcn::DeathListener
+ void death(const gcn::Event &event) override;
+
void setMap(Map *map) final;
/**
@@ -443,8 +457,9 @@ class Being : public ActorSprite, public EventListener
protected:
struct SpriteState {
int id = 0;
+ int visibleId = 0;
std::string color;
- std::vector<Particle*> particles;
+ std::vector<ParticleHandle> particles;
};
/**
@@ -455,16 +470,13 @@ class Being : public ActorSprite, public EventListener
/**
* Updates name's location.
*/
- void updateCoords();
-
- void showName();
+ void updateNamePosition();
void addSpriteParticles(SpriteState &spriteState, const SpriteDisplay &display);
- void removeSpriteParticles(SpriteState &spriteState);
- void removeAllSpriteParticles();
void restoreAllSpriteParticles();
void updateColors();
+ void updatePlayerSprites();
/**
* Gets the advised Y chat text position.
@@ -476,6 +488,12 @@ class Being : public ActorSprite, public EventListener
*/
virtual void pathFinished() {}
+ /**
+ * Notify self that a status effect has flipped.
+ * The new flag is passed.
+ */
+ virtual void updateStatusEffect(int index, bool newStatus);
+
const BeingInfo *mInfo;
Timer mActionTimer; /**< Time spent in current action. TODO: Remove use of it */
@@ -486,7 +504,7 @@ class Being : public ActorSprite, public EventListener
int mAttackSpeed = 350; /**< Attack speed */
Action mAction = STAND; /**< Action the being is performing */
- uint16_t mSubType = 0xFFFF; /**< Subtype (graphical view, basically) */
+ int mSubType = 0xFFFF; /**< Subtype (graphical view, basically) */
uint8_t mDirection = DOWN; /**< Facing direction */
uint8_t mSpriteDirection = DIRECTION_DOWN; /**< Facing direction */
@@ -511,9 +529,9 @@ class Being : public ActorSprite, public EventListener
Vector mDest; /**< destination coordinates. */
std::vector<SpriteState> mSpriteStates;
- bool mRestoreSpriteParticlesOnLogic = false;
+ bool mRestoreParticlesOnLogic = false;
- Gender mGender = Gender::UNSPECIFIED;
+ Gender mGender = Gender::Unspecified;
// Character guild information
std::map<int, Guild*> mGuilds;
@@ -524,7 +542,10 @@ class Being : public ActorSprite, public EventListener
private:
void updateMovement();
- const Type mType;
+ Type mType = UNKNOWN;
+
+ std::set<int> mStatusEffects; /**< set of active status effects */
+ std::map<int, ParticleHandle> mStatusParticleEffects;
/** Speech Bubble components */
SpeechBubble *mSpeechBubble;
@@ -542,8 +563,5 @@ class Being : public ActorSprite, public EventListener
Vector mSpeedPixelsPerSecond;
int mDamageTaken = 0;
-
int mIp = 0;
};
-
-#endif
diff --git a/src/channel.h b/src/channel.h
index c0d48632..8d8acf15 100644
--- a/src/channel.h
+++ b/src/channel.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef CHANNEL_H
-#define CHANNEL_H
+#pragma once
#include <string>
@@ -79,5 +78,3 @@ class Channel
std::string mAnnouncement;
ChannelTab *mTab;
};
-
-#endif // CHANNEL_H
diff --git a/src/channelmanager.h b/src/channelmanager.h
index ffff3047..baaa196a 100644
--- a/src/channelmanager.h
+++ b/src/channelmanager.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef CHANNELMANAGER_H
-#define CHANNELMANAGER_H
+#pragma once
#include <list>
#include <string>
@@ -46,5 +45,3 @@ private:
};
extern ChannelManager *channelManager;
-
-#endif
diff --git a/src/chatlogger.h b/src/chatlogger.h
index e9e53798..45fe7da2 100644
--- a/src/chatlogger.h
+++ b/src/chatlogger.h
@@ -19,8 +19,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#ifndef CHATLOG_H
-#define CHATLOG_H
+#pragma once
#include <fstream>
@@ -66,5 +65,3 @@ class ChatLogger
};
extern ChatLogger *chatLogger;
-
-#endif
diff --git a/src/client.cpp b/src/client.cpp
index 40f41b30..99c5bbcf 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -68,6 +68,7 @@
#include "resources/userpalette.h"
#include "resources/settingsmanager.h"
+#include "utils/filesystem.h"
#include "utils/gettext.h"
#include "utils/mkdir.h"
#if defined(_WIN32) || defined(__APPLE__)
@@ -76,7 +77,6 @@
#include "utils/stringutils.h"
#include "utils/time.h"
-#include <physfs.h>
#include <SDL_image.h>
#ifdef _WIN32
@@ -220,9 +220,7 @@ Client::Client(const Options &options):
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
- ResourceManager *resman = ResourceManager::getInstance();
-
- if (!resman->setWriteDir(mLocalDataDir))
+ if (!FS::setWriteDir(mLocalDataDir))
{
logger->error(strprintf("%s couldn't be set as write directory! "
"Exiting.", mLocalDataDir.c_str()));
@@ -233,9 +231,8 @@ Client::Client(const Options &options):
#else
mPackageDir = PKG_DATADIR "data";
#endif
- resman->addToSearchPath(mPackageDir, false);
-
- resman->addToSearchPath("data", false);
+ ResourceManager::addToSearchPath(mPackageDir, false);
+ ResourceManager::addToSearchPath("data", false);
// Add branding/data to PhysFS search path
if (!options.brandingPath.empty())
@@ -251,15 +248,15 @@ Client::Client(const Options &options):
int loc = path.find_last_of('/');
#endif
if (loc > 0)
- resman->addToSearchPath(path.substr(0, loc + 1) + "data", false);
+ ResourceManager::addToSearchPath(path.substr(0, loc + 1) + "data", false);
}
// Add the main data directories to our PhysicsFS search path
if (!options.dataPath.empty())
- resman->addToSearchPath(options.dataPath, false);
+ ResourceManager::addToSearchPath(options.dataPath, false);
// Add the local data directory to PhysicsFS search path
- resman->addToSearchPath(mLocalDataDir, false);
+ ResourceManager::addToSearchPath(mLocalDataDir, false);
bool useOpenGL = !mOptions.noOpenGL && config.opengl;
@@ -287,7 +284,7 @@ Client::Client(const Options &options):
#else
iconFile += ".png";
#endif
- iconFile = resman->getPath(iconFile);
+ iconFile = ResourceManager::getPath(iconFile);
logger->log("Loading icon from file: %s", iconFile.c_str());
#ifdef _WIN32
static SDL_SysWMinfo pInfo;
@@ -310,13 +307,11 @@ Client::Client(const Options &options):
}
#endif
- Theme::prepareThemePath();
-
// Initialize the item and emote shortcuts.
itemShortcut = new ItemShortcut;
emoteShortcut = new EmoteShortcut;
- gui = new Gui(graphics);
+ gui = new Gui(graphics, Theme::prepareThemePath());
// Initialize sound engine
try
@@ -355,12 +350,12 @@ Client::Client(const Options &options):
loginData.remember = config.remember;
loginData.registerLogin = false;
- if (mCurrentServer.type == ServerType::UNKNOWN && mCurrentServer.port != 0)
+ if (mCurrentServer.type == ServerType::Unknown && mCurrentServer.port != 0)
{
mCurrentServer.type = ServerInfo::defaultServerTypeForPort(mCurrentServer.port);
}
- if (mCurrentServer.type == ServerType::UNKNOWN)
+ if (mCurrentServer.type == ServerType::Unknown)
{
mCurrentServer.type = ServerInfo::parseType(
branding.getValue("defaultServerType", "tmwathena"));
@@ -386,7 +381,13 @@ Client::Client(const Options &options):
loginData.username = config.username;
if (mState != STATE_ERROR)
- mState = STATE_CHOOSE_SERVER;
+ {
+ // If a server was passed on the command line, or branding
+ // provides a server and a blank server list, we skip the
+ // server selection dialog.
+ mState = mCurrentServer.isValid() ? STATE_CONNECT_SERVER
+ : STATE_CHOOSE_SERVER;
+ }
// Initialize seconds counter
mSecondsCounterId = SDL_AddTimer(1000, nextSecond, nullptr);
@@ -404,6 +405,8 @@ Client::~Client()
CharDB::unload();
delete itemDb;
+ ActorSprite::unload();
+
// Before config.write() since it writes the shortcuts to the config
delete itemShortcut;
delete emoteShortcut;
@@ -567,27 +570,12 @@ int Client::exec()
case STATE_CHOOSE_SERVER:
logger->log("State: CHOOSE SERVER");
- // If a server was passed on the command line, or branding
- // provides a server and a blank server list, we skip the
- // server selection dialog.
- if (!mCurrentServer.hostname.empty() && mCurrentServer.port)
- {
- mState = STATE_CONNECT_SERVER;
-
- // Reset options so that cancelling or connect
- // timeout will show the server dialog.
- mOptions.serverName.clear();
- mOptions.serverPort = 0;
- }
- else
- {
- // Don't allow an alpha opacity
- // lower than the default value
- Theme::instance()->setMinimumOpacity(0.8f);
+ // Don't allow an alpha opacity
+ // lower than the default value
+ gui->getTheme()->setMinimumOpacity(0.8f);
- mCurrentDialog = new ServerDialog(&mCurrentServer,
- mConfigDir);
- }
+ mCurrentDialog = new ServerDialog(&mCurrentServer,
+ mConfigDir);
break;
case STATE_CONNECT_SERVER:
@@ -603,7 +591,7 @@ int Client::exec()
logger->log("State: LOGIN");
// Don't allow an alpha opacity
// lower than the default value
- Theme::instance()->setMinimumOpacity(0.8f);
+ gui->getTheme()->setMinimumOpacity(0.8f);
if (mOptions.username.empty() || mOptions.password.empty())
{
@@ -654,22 +642,17 @@ int Client::exec()
break;
case STATE_UPDATE:
- // Determine which source to use for the update host
- if (!mOptions.updateHost.empty())
- mUpdateHost = mOptions.updateHost;
- else
- mUpdateHost = loginData.updateHost;
- initUpdatesDir();
+ logger->log("State: UPDATE");
if (mOptions.skipUpdate)
{
mState = STATE_LOAD_DATA;
}
- else
+ else if (initUpdatesDir())
{
- logger->log("State: UPDATE");
mCurrentDialog = new UpdaterWindow(mUpdateHost,
- mLocalDataDir + "/" + mUpdatesDir,mOptions.dataPath.empty());
+ mLocalDataDir + "/" + mUpdatesDir,
+ mOptions.dataPath.empty());
}
break;
@@ -681,7 +664,7 @@ int Client::exec()
if (mOptions.dataPath.empty())
{
// Add customdata directory
- ResourceManager::getInstance()->searchAndAddArchives(
+ ResourceManager::searchAndAddArchives(
"customdata/",
"zip",
false);
@@ -697,10 +680,10 @@ int Client::exec()
switch (Net::getNetworkType())
{
- case ServerType::TMWATHENA:
+ case ServerType::TmwAthena:
itemDb = new TmwAthena::TaItemDB;
break;
- case ServerType::MANASERV:
+ case ServerType::ManaServ:
itemDb = new ManaServ::ManaServItemDB;
break;
default:
@@ -732,7 +715,7 @@ int Client::exec()
logger->log("State: CHAR SELECT");
// Don't allow an alpha opacity
// lower than the default value
- Theme::instance()->setMinimumOpacity(0.8f);
+ gui->getTheme()->setMinimumOpacity(0.8f);
mCurrentDialog = new CharSelectDialog(&loginData);
@@ -749,6 +732,7 @@ int Client::exec()
// Choosing character on the command line should work only
// once, clear it so that 'switch character' works.
mOptions.character.clear();
+ mOptions.chooseDefault = false;
break;
@@ -758,7 +742,7 @@ int Client::exec()
Net::getGameHandler()->connect();
mCurrentDialog = new ConnectionDialog(
_("Connecting to the game server"),
- Net::getNetworkType() == ServerType::TMWATHENA ?
+ Net::getNetworkType() == ServerType::TmwAthena ?
STATE_CHOOSE_SERVER : STATE_SWITCH_CHARACTER);
break;
@@ -781,7 +765,7 @@ int Client::exec()
sound.fadeOutMusic(1000);
// Allow any alpha opacity
- Theme::instance()->setMinimumOpacity(-1.0f);
+ gui->getTheme()->setMinimumOpacity(0.0f);
delete mSetupButton;
delete mDesktop;
@@ -919,13 +903,10 @@ int Client::exec()
case STATE_EXIT:
logger->log("State: EXIT");
- Net::unload();
break;
case STATE_FORCE_QUIT:
logger->log("State: FORCE QUIT");
- if (Net::getGeneralHandler())
- Net::getGeneralHandler()->unload();
mState = STATE_EXIT;
break;
@@ -943,6 +924,8 @@ int Client::exec()
}
}
+ Net::unload();
+
return 0;
}
@@ -981,7 +964,7 @@ void Client::action(const gcn::ActionEvent &event)
void Client::initRootDir()
{
- mRootDir = PHYSFS_getBaseDir();
+ mRootDir = FS::getBaseDir();
#ifdef _WIN32
std::string portableName = mRootDir + "portable.xml";
struct stat statbuf;
@@ -1041,15 +1024,15 @@ void Client::initHomeDir()
if (mLocalDataDir.empty())
{
#if defined __HAIKU__
- mLocalDataDir = PHYSFS_getUserDir();
+ mLocalDataDir = FS::getUserDir();
mLocalDataDir += "/config/data/Mana";
#elif defined _WIN32
mLocalDataDir = getSpecialFolderLocation(FOLDERID_LocalAppData);
if (mLocalDataDir.empty())
- mLocalDataDir = PHYSFS_getUserDir();
+ mLocalDataDir = FS::getUserDir();
mLocalDataDir += "/Mana";
#else
- mLocalDataDir = PHYSFS_getPrefDir("manasource.org", "mana");
+ mLocalDataDir = FS::getPrefDir("manasource.org", "mana");
#endif
}
@@ -1067,12 +1050,12 @@ void Client::initHomeDir()
#ifdef __APPLE__
mConfigDir = mLocalDataDir + "/" + app;
#elif defined __HAIKU__
- mConfigDir = PHYSFS_getPrefDir("manasource.org", "Mana");
+ mConfigDir = FS::getPrefDir("manasource.org", "Mana");
mConfigDir += app;
#elif defined _WIN32
- mConfigDir = PHYSFS_getPrefDir("Mana", app.c_str());
+ mConfigDir = FS::getPrefDir("Mana", app.c_str());
#else
- mConfigDir = std::string(PHYSFS_getUserDir()) + ".config/mana/" + app;
+ mConfigDir = std::string(FS::getUserDir()) + ".config/mana/" + app;
#endif
}
@@ -1104,39 +1087,37 @@ void Client::initConfiguration()
* Parse the update host and determine the updates directory
* Then verify that the directory exists (creating if needed).
*/
-void Client::initUpdatesDir()
+bool Client::initUpdatesDir()
{
- // If updatesHost is currently empty, fill it from config file
- if (mUpdateHost.empty())
+ // Determine which source to use for the update host
+ if (!mOptions.updateHost.empty())
+ mUpdateHost = mOptions.updateHost;
+ else if (!loginData.updateHost.empty())
+ mUpdateHost = loginData.updateHost;
+ else
mUpdateHost = config.updatehost;
- // Exit on empty update host.
- if (mUpdateHost.empty())
- return;
+ // Remove any trailing slashes at the end of the URL
+ while (!mUpdateHost.empty() && mUpdateHost.back() == '/')
+ mUpdateHost.pop_back();
- logger->log("Setting update host: %s", mUpdateHost.c_str());
-
- std::string updateHost = getHostNameFromURL(mUpdateHost);
-
- // Exit on a wrong update host.
- if (updateHost.length() < 2)
+ if (mUpdateHost.empty())
{
- // Show the original updateHostname in the error message.
- errorMessage = strprintf(_("Invalid update host: %s"),
- mUpdateHost.c_str());
- mState = STATE_ERROR;
- return;
+ logger->log("No update host provided");
+ mUpdatesDir.clear();
+ mState = STATE_LOAD_DATA;
+ return false;
}
- mUpdateHost = updateHost;
- mUpdatesDir = "updates/" + mUpdateHost;
+ mUpdatesDir = "updates/" + getDirectoryFromURL(mUpdateHost);
- ResourceManager *resman = ResourceManager::getInstance();
+ logger->log("Update host: %s", mUpdateHost.c_str());
+ logger->log("Updates dir: %s", mUpdatesDir.c_str());
// Verify that the updates directory exists. Create if necessary.
- if (!resman->isDirectory("/" + mUpdatesDir))
+ if (!FS::isDirectory(mUpdatesDir))
{
- if (!resman->mkdir("/" + mUpdatesDir))
+ if (!FS::mkdir(mUpdatesDir))
{
#if defined _WIN32
std::string newDir = mLocalDataDir + "\\" + mUpdatesDir;
@@ -1165,9 +1146,12 @@ void Client::initUpdatesDir()
strprintf(_("Error creating updates directory!\n(%s/%s)"),
mLocalDataDir.c_str(), mUpdatesDir.c_str());
mState = STATE_ERROR;
+ return false;
#endif
}
}
+
+ return true;
}
void Client::initScreenshotDir()
@@ -1183,7 +1167,7 @@ void Client::initScreenshotDir()
if (mScreenshotDir.empty())
mScreenshotDir = getSpecialFolderLocation(FOLDERID_Desktop);
#else
- mScreenshotDir = std::string(PHYSFS_getUserDir()) + "Desktop";
+ mScreenshotDir = std::string(FS::getUserDir()) + "Desktop";
#endif
if (config.useScreenshotDirectorySuffix)
diff --git a/src/client.h b/src/client.h
index e7127ddc..a3e9c572 100644
--- a/src/client.h
+++ b/src/client.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef CLIENT_H
-#define CLIENT_H
+#pragma once
#include "video.h"
@@ -135,7 +134,7 @@ public:
std::string configDir;
std::string localDataDir;
std::string screenshotDir;
- ServerType serverType = ServerType::UNKNOWN;
+ ServerType serverType = ServerType::Unknown;
std::string serverName;
uint16_t serverPort = 0;
@@ -203,7 +202,7 @@ private:
void initRootDir();
void initHomeDir();
void initConfiguration();
- void initUpdatesDir();
+ bool initUpdatesDir();
void initScreenshotDir();
void accountLogin(LoginData *loginData);
@@ -236,8 +235,5 @@ private:
SDL_Surface *mIcon = nullptr;
SDL_TimerID mSecondsCounterId = 0;
-
FpsManager mFpsManager;
};
-
-#endif // CLIENT_H
diff --git a/src/commandhandler.cpp b/src/commandhandler.cpp
index 75cca1ab..dd4cb2c0 100644
--- a/src/commandhandler.cpp
+++ b/src/commandhandler.cpp
@@ -122,7 +122,7 @@ void CommandHandler::handleCommand(const std::string &command, ChatTab *tab)
{
handlePresent(args, tab);
}
- else if (type == "showip" && Net::getNetworkType() == ServerType::TMWATHENA)
+ else if (type == "showip" && Net::getNetworkType() == ServerType::TmwAthena)
{
handleShowIp(args, tab);
}
@@ -523,15 +523,15 @@ void CommandHandler::handleIgnore(const std::string &args, ChatTab *tab)
return;
}
- if (player_relations.getRelation(args) == PlayerRelation::IGNORED)
+ if (player_relations.getRelation(args) == PlayerRelation::Ignored)
{
tab->chatLog(_("Player already ignored!"), BY_SERVER);
return;
}
else
- player_relations.setRelation(args, PlayerRelation::IGNORED);
+ player_relations.setRelation(args, PlayerRelation::Ignored);
- if (player_relations.getRelation(args) == PlayerRelation::IGNORED)
+ if (player_relations.getRelation(args) == PlayerRelation::Ignored)
tab->chatLog(_("Player successfully ignored!"), BY_SERVER);
else
tab->chatLog(_("Player could not be ignored!"), BY_SERVER);
@@ -545,7 +545,7 @@ void CommandHandler::handleUnignore(const std::string &args, ChatTab *tab)
return;
}
- if (player_relations.getRelation(args) == PlayerRelation::IGNORED)
+ if (player_relations.getRelation(args) == PlayerRelation::Ignored)
player_relations.removePlayer(args);
else
{
@@ -553,7 +553,7 @@ void CommandHandler::handleUnignore(const std::string &args, ChatTab *tab)
return;
}
- if (player_relations.getRelation(args) != PlayerRelation::IGNORED)
+ if (player_relations.getRelation(args) != PlayerRelation::Ignored)
tab->chatLog(_("Player no longer ignored!"), BY_SERVER);
else
tab->chatLog(_("Player could not be unignored!"), BY_SERVER);
@@ -561,5 +561,7 @@ void CommandHandler::handleUnignore(const std::string &args, ChatTab *tab)
void CommandHandler::handleAway(const std::string &args, ChatTab *tab)
{
- local_player->setAway(args);
+ if (!args.empty())
+ config.afkMessage = args;
+ local_player->setAwayMode(!local_player->getAwayMode());
}
diff --git a/src/commandhandler.h b/src/commandhandler.h
index 42f68b2f..e13b0562 100644
--- a/src/commandhandler.h
+++ b/src/commandhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef COMMANDHANDLER_H
-#define COMMANDHANDLER_H
+#pragma once
#include <string>
@@ -143,5 +142,3 @@ class CommandHandler
};
extern CommandHandler *commandHandler;
-
-#endif // COMMANDHANDLER_H
diff --git a/src/compoundsprite.cpp b/src/compoundsprite.cpp
index 2e39fb47..8193d8c0 100644
--- a/src/compoundsprite.cpp
+++ b/src/compoundsprite.cpp
@@ -29,15 +29,9 @@
#include <SDL.h>
-CompoundSprite::CompoundSprite()
-{
-}
-
CompoundSprite::~CompoundSprite()
{
delete_all(mSprites);
- mSprites.clear();
-
delete mImage;
delete mAlphaImage;
}
@@ -80,8 +74,7 @@ bool CompoundSprite::update(int time)
bool CompoundSprite::draw(Graphics *graphics, int posX, int posY) const
{
- if (mNeedsRedraw)
- redraw();
+ doRedraw();
if (mSprites.empty()) // Nothing to draw
return false;
@@ -96,19 +89,15 @@ bool CompoundSprite::draw(Graphics *graphics, int posX, int posY) const
if (mAlpha && mAlphaImage)
{
- if (mAlphaImage->getAlpha() != mAlpha)
- mAlphaImage->setAlpha(mAlpha);
-
- return graphics->drawImage(mAlphaImage,
- posX, posY);
+ mAlphaImage->setAlpha(mAlpha);
+ return graphics->drawImage(mAlphaImage, posX, posY);
}
for (auto sprite : mSprites)
{
if (sprite)
{
- if (sprite->getAlpha() != mAlpha)
- sprite->setAlpha(mAlpha);
+ sprite->setAlpha(mAlpha);
sprite->draw(graphics, posX - sprite->getWidth() / 2, posY - sprite->getHeight());
}
}
@@ -116,11 +105,6 @@ bool CompoundSprite::draw(Graphics *graphics, int posX, int posY) const
return false;
}
-const Image *CompoundSprite::getImage() const
-{
- return mImage;
-}
-
bool CompoundSprite::setDirection(SpriteDirection direction)
{
bool ret = false;
@@ -141,13 +125,13 @@ int CompoundSprite::getNumberOfLayers() const
return size();
}
-void CompoundSprite::addSprite(Sprite *sprite)
+void CompoundSprite::add(Sprite *sprite)
{
mSprites.push_back(sprite);
mNeedsRedraw = true;
}
-void CompoundSprite::setSprite(int layer, Sprite *sprite)
+void CompoundSprite::set(int layer, Sprite *sprite)
{
// Skip if it won't change anything
if (mSprites.at(layer) == sprite)
@@ -158,17 +142,6 @@ void CompoundSprite::setSprite(int layer, Sprite *sprite)
mNeedsRedraw = true;
}
-void CompoundSprite::removeSprite(int layer)
-{
- // Skip if it won't change anything
- if (!mSprites.at(layer))
- return;
-
- delete mSprites.at(layer);
- mSprites.at(layer) = nullptr;
- mNeedsRedraw = true;
-}
-
void CompoundSprite::clear()
{
// Skip if it won't change anything
@@ -189,7 +162,7 @@ void CompoundSprite::ensureSize(size_t layerCount)
mSprites.resize(layerCount);
}
-int CompoundSprite::getDuration() const
+int CompoundSprite::getMaxDuration() const
{
int duration = 0;
for (auto sprite : mSprites)
@@ -220,8 +193,9 @@ static void updateValues(int &dimension, int &pos, int imgDimUL, int imgDimRD, i
void CompoundSprite::redraw() const
{
#if 1 // TODO_SDL2: Does it make sense to implement CompoundSprite?
- mWidth = mSprites.at(0)->getWidth();
- mHeight = mSprites.at(0)->getHeight();
+ auto baseSprite = mSprites.empty() ? nullptr : mSprites.at(0);
+ mWidth = baseSprite ? baseSprite->getWidth() : 0;
+ mHeight = baseSprite ? baseSprite->getHeight() : 0;
mOffsetX = 0;
mOffsetY = 0;
mNeedsRedraw = false;
diff --git a/src/compoundsprite.h b/src/compoundsprite.h
index 4daffccb..5aa11686 100644
--- a/src/compoundsprite.h
+++ b/src/compoundsprite.h
@@ -18,74 +18,50 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef COMPOUNDSPRITE_H
-#define COMPOUNDSPRITE_H
+#pragma once
#include "sprite.h"
#include <vector>
-class Image;
-
-class CompoundSprite : public Sprite
+class CompoundSprite
{
public:
- CompoundSprite();
-
- ~CompoundSprite() override;
-
- bool reset() final;
-
- bool play(const std::string &action) final;
+ CompoundSprite() = default;
+ ~CompoundSprite();
- bool update(int time) final;
-
- bool draw(Graphics *graphics, int posX, int posY) const override;
+ bool reset();
+ bool play(const std::string &action);
+ bool update(int time);
+ bool draw(Graphics *graphics, int posX, int posY) const;
/**
* Gets the width in pixels of the first sprite in the list.
*/
- int getWidth() const override
- { return mWidth; }
+ int getWidth() const { doRedraw(); return mWidth; }
/**
* Gets the height in pixels of the first sprite in the list.
*/
- int getHeight() const override
- { return mHeight; }
-
- int getOffsetX() const final
- { return mOffsetX; }
-
- int getOffsetY() const final
- { return mOffsetY; }
+ int getHeight() const { doRedraw(); return mHeight; }
- const Image *getImage() const final;
+ float getAlpha() const { return mAlpha; }
+ void setAlpha(float alpha) { mAlpha = alpha; }
- bool setDirection(SpriteDirection direction) final;
+ bool setDirection(SpriteDirection direction);
int getNumberOfLayers() const;
- int getDuration() const final;
+ int getMaxDuration() const;
- size_t size() const
- { return mSprites.size(); }
-
- void addSprite(Sprite *sprite);
-
- void setSprite(int layer, Sprite *sprite);
-
- Sprite *getSprite(int layer) const
- { return mSprites.at(layer); }
-
- void removeSprite(int layer);
+ size_t size() const { return mSprites.size(); }
+ void add(Sprite *sprite);
+ void set(int layer, Sprite *sprite);
void clear();
-
void ensureSize(size_t layerCount);
- void doRedraw()
- { mNeedsRedraw = true; }
+ void doRedraw() const;
private:
void redraw() const;
@@ -98,7 +74,12 @@ private:
mutable bool mNeedsRedraw = false;
+ float mAlpha = 1.0f;
std::vector<Sprite*> mSprites;
};
-#endif // COMPOUNDSPRITE_H
+inline void CompoundSprite::doRedraw() const
+{
+ if (mNeedsRedraw)
+ redraw();
+}
diff --git a/src/configuration.cpp b/src/configuration.cpp
index ead228c4..5b45a21d 100644
--- a/src/configuration.cpp
+++ b/src/configuration.cpp
@@ -302,9 +302,9 @@ static const char *serverTypeToString(ServerType type)
{
switch (type)
{
- case ServerType::TMWATHENA:
+ case ServerType::TmwAthena:
return "TmwAthena";
- case ServerType::MANASERV:
+ case ServerType::ManaServ:
return "ManaServ";
default:
return "";
@@ -550,7 +550,7 @@ void deserialize(XML::Node node, Config &config)
for (auto node : node.children()) {
if (node.name() == "player") {
std::string playerName;
- PlayerRelation relation = PlayerRelation::NEUTRAL;
+ PlayerRelation relation = PlayerRelation::Neutral;
for (auto node : node.children()) {
if (node.name() == "option") {
diff --git a/src/configuration.h b/src/configuration.h
index e0ffae51..510ac269 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef CONFIGURATION_H
-#define CONFIGURATION_H
+#pragma once
#include "being.h"
#include "defaults.h"
@@ -273,5 +272,3 @@ void setConfigValue(T Config::*member, const T &value)
config.*member = value;
Event(Event::ConfigOptionChanged, member).trigger(Event::ConfigChannel);
}
-
-#endif
diff --git a/src/defaults.cpp b/src/defaults.cpp
index dff06bbb..8a0f18a3 100644
--- a/src/defaults.cpp
+++ b/src/defaults.cpp
@@ -98,6 +98,11 @@ DefaultsData* getPathsDefaults()
AddDEF(pathsData, "hitEffectId", 26);
AddDEF(pathsData, "criticalHitEffectId", 28);
+ // This is makes sure that actors positioned on the center of a tile have
+ // their sprite aligned to the bottom of that tile. The default maintains
+ // compatibility with existing sprites.
+ AddDEF(pathsData, "spriteOffsetY", 16);
+
AddDEF(pathsData, "minimaps", "graphics/minimaps/");
AddDEF(pathsData, "maps", "maps/");
diff --git a/src/defaults.h b/src/defaults.h
index a588a543..8180c694 100644
--- a/src/defaults.h
+++ b/src/defaults.h
@@ -18,8 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef DEFAULTS_H
-#define DEFAULTS_H
+#pragma once
#include <map>
#include <string>
@@ -29,5 +28,3 @@ using DefaultsData = std::map<std::string, VariableData *>;
DefaultsData* getBrandingDefaults();
DefaultsData* getPathsDefaults();
-
-#endif
diff --git a/src/effectmanager.cpp b/src/effectmanager.cpp
index 903f08b2..32280555 100644
--- a/src/effectmanager.cpp
+++ b/src/effectmanager.cpp
@@ -49,56 +49,57 @@ EffectManager::EffectManager()
for (auto node : root.children())
{
- if (node.name() == "effect")
+ int effectId;
+
+ if (node.name() == "effect" && node.attribute("id", effectId))
{
- EffectDescription &ed = mEffects.emplace_back();
- ed.id = node.getProperty("id", -1);
- ed.GFX = node.getProperty("particle", "");
- ed.SFX = node.getProperty("audio", "");
+ EffectDescription &ed = mEffects[effectId];
+ node.attribute("particle", ed.particle);
+ node.attribute("audio", ed.sfx);
}
}
}
-EffectManager::~EffectManager()
-{
-}
+EffectManager::~EffectManager() = default;
-bool EffectManager::trigger(int id, Being* being, int rotation)
+bool EffectManager::trigger(int id, Being *being, int rotation)
{
- bool rValue = false;
- for (auto &effect : mEffects)
+ auto it = mEffects.find(id);
+ if (it == mEffects.end())
{
- if (effect.id == id)
- {
- rValue = true;
- if (!effect.GFX.empty())
- {
- Particle *selfFX;
- selfFX = particleEngine->addEffect(effect.GFX, 0, 0, rotation);
- being->controlParticle(selfFX);
- }
- if (!effect.SFX.empty())
- sound.playSfx(effect.SFX);
- break;
- }
+ logger->log("EffectManager::trigger: effect %d not found", id);
+ return false;
+ }
+
+ EffectDescription &effect = it->second;
+
+ if (!effect.particle.empty())
+ {
+ if (Particle *selfFX = particleEngine->addEffect(effect.particle, 0, 0, rotation))
+ being->controlParticle(selfFX);
}
- return rValue;
+
+ if (!effect.sfx.empty())
+ sound.playSfx(effect.sfx);
+
+ return true;
}
bool EffectManager::trigger(int id, int x, int y, int rotation)
{
- bool rValue = false;
- for (auto &effect : mEffects)
+ auto it = mEffects.find(id);
+ if (it == mEffects.end())
{
- if (effect.id == id)
- {
- rValue = true;
- if (!effect.GFX.empty())
- particleEngine->addEffect(effect.GFX, x, y, rotation);
- if (!effect.SFX.empty())
- sound.playSfx(effect.SFX);
- break;
- }
+ logger->log("EffectManager::trigger: effect %d not found", id);
+ return false;
}
- return rValue;
+
+ EffectDescription &effect = it->second;
+
+ if (!effect.particle.empty())
+ particleEngine->addEffect(effect.particle, x, y, rotation);
+ if (!effect.sfx.empty())
+ sound.playSfx(effect.sfx);
+
+ return true;
}
diff --git a/src/effectmanager.h b/src/effectmanager.h
index 5936f633..fdc92418 100644
--- a/src/effectmanager.h
+++ b/src/effectmanager.h
@@ -20,10 +20,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef EFFECT_MANAGER_H
-#define EFFECT_MANAGER_H
+#pragma once
-#include <list>
+#include <map>
#include <string>
class Being;
@@ -33,9 +32,8 @@ class EffectManager
public:
struct EffectDescription
{
- int id;
- std::string GFX;
- std::string SFX;
+ std::string particle;
+ std::string sfx;
};
EffectManager();
@@ -47,7 +45,7 @@ class EffectManager
* and with the given rotation in degree:
* 0° = Down, 90° = left, ...
*/
- bool trigger(int id, Being* being, int rotation = 0);
+ bool trigger(int id, Being *being, int rotation = 0);
/**
* Triggers a effect with the id, at
@@ -58,9 +56,7 @@ class EffectManager
bool trigger(int id, int x, int y, int rotation = 0);
private:
- std::list<EffectDescription> mEffects;
+ std::map<int, EffectDescription> mEffects;
};
extern EffectManager *effectManager;
-
-#endif // EFFECT_MANAGER_H
diff --git a/src/emoteshortcut.h b/src/emoteshortcut.h
index 598cc124..97648b62 100644
--- a/src/emoteshortcut.h
+++ b/src/emoteshortcut.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef EMOTESHORTCUT_H
-#define EMOTESHORTCUT_H
+#pragma once
#define SHORTCUT_EMOTES 12
@@ -115,5 +114,3 @@ class EmoteShortcut
};
extern EmoteShortcut *emoteShortcut;
-
-#endif
diff --git a/src/equipment.h b/src/equipment.h
index d40ca55d..af059d7a 100644
--- a/src/equipment.h
+++ b/src/equipment.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef EQUIPMENT_H
-#define EQUIPMENT_H
+#pragma once
#include <string>
@@ -72,5 +71,3 @@ class Equipment
private:
Backend *mBackend;
};
-
-#endif
diff --git a/src/event.cpp b/src/event.cpp
index 11d46a2f..64df4f70 100644
--- a/src/event.cpp
+++ b/src/event.cpp
@@ -27,8 +27,8 @@ Event::ListenMap Event::mBindings;
Event::~Event()
{
- for (auto it = mData.begin(); it != mData.end(); ++it)
- delete it->second;
+ for (auto &[_, data] : mData)
+ delete data;
}
// Integers
@@ -191,10 +191,6 @@ void Event::unbind(EventListener *listener, Channel channel)
void Event::remove(EventListener *listener)
{
- auto it = mBindings.begin();
- while (it != mBindings.end())
- {
- it->second.erase(listener);
- it++;
- }
+ for (auto &[_, listeners] : mBindings)
+ listeners.erase(listener);
}
diff --git a/src/event.h b/src/event.h
index d3d670e3..06e38007 100644
--- a/src/event.h
+++ b/src/event.h
@@ -18,8 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef EVENT_H
-#define EVENT_H
+#pragma once
#include <any>
#include <map>
@@ -75,7 +74,6 @@ public:
DoDrop,
DoEquip,
DoMove,
- DoSplit,
DoUnequip,
DoUse,
End,
@@ -98,7 +96,6 @@ public:
StateChange,
StorageCount,
StringInput,
- Stun,
UpdateAttribute,
UpdateStat,
UpdateStatusEffect,
@@ -324,5 +321,3 @@ inline void serverNotice(const std::string &message)
event.setString("message", message);
event.trigger(Event::NoticesChannel);
}
-
-#endif // EVENT_H
diff --git a/src/eventlistener.h b/src/eventlistener.h
index 3d4f8967..8c931fa4 100644
--- a/src/eventlistener.h
+++ b/src/eventlistener.h
@@ -18,8 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef EVENTLISTENER_H
-#define EVENTLISTENER_H
+#pragma once
#include "event.h"
@@ -34,5 +33,3 @@ public:
virtual void event(Event::Channel channel, const Event &event) = 0;
};
-
-#endif // EVENTLISTENER_H
diff --git a/src/flooritem.cpp b/src/flooritem.cpp
index 15975687..9c545560 100644
--- a/src/flooritem.cpp
+++ b/src/flooritem.cpp
@@ -21,8 +21,13 @@
#include "flooritem.h"
+#include "configuration.h"
+
+#include "resources/image.h"
#include "resources/itemdb.h"
#include "resources/iteminfo.h"
+#include "resources/resourcemanager.h"
+#include "resources/theme.h"
FloorItem::FloorItem(int id,
int itemId,
@@ -39,7 +44,33 @@ FloorItem::FloorItem(int id,
mX = (int)position.x / map->getTileWidth();
mY = (int)position.y / map->getTileHeight();
- setupSpriteDisplay(itemDb->get(itemId).display);
+ // Set up sprites and particle effects
+ auto &info = getInfo();
+ setupSpriteDisplay(info.display, false);
+
+ // If no sprites are defined, fall back to the item icon
+ if (info.display.sprites.empty())
+ {
+ ResourceManager *resman = ResourceManager::getInstance();
+ std::string imagePath = paths.getStringValue("itemIcons") + info.display.image;
+
+ mImage = resman->getImage(imagePath);
+ if (!mImage)
+ mImage = Theme::getImageFromTheme(paths.getStringValue("unknownItemFile"));
+ }
+}
+
+bool FloorItem::draw(Graphics *graphics, int offsetX, int offsetY) const
+{
+ if (mImage)
+ {
+ mImage->setAlpha(getAlpha());
+ return graphics->drawImage(mImage,
+ getPixelX() + offsetX - mImage->getWidth() / 2,
+ getPixelY() + offsetY - mImage->getHeight() / 2);
+ }
+
+ return ActorSprite::draw(graphics, offsetX, offsetY);
}
const ItemInfo &FloorItem::getInfo() const
diff --git a/src/flooritem.h b/src/flooritem.h
index 8326982f..909d6195 100644
--- a/src/flooritem.h
+++ b/src/flooritem.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef FLOORITEM_H
-#define FLOORITEM_H
+#pragma once
#include "actorsprite.h"
@@ -29,7 +28,7 @@ class ItemInfo;
/**
* An item lying on the floor.
*/
-class FloorItem : public ActorSprite
+class FloorItem final : public ActorSprite
{
public:
/**
@@ -47,11 +46,12 @@ class FloorItem : public ActorSprite
Type getType() const override { return FLOOR_ITEM; }
+ bool draw(Graphics *graphics, int offsetX, int offsetY) const override;
+
/**
* Returns the item ID.
*/
- int getItemId() const
- { return mItemId; }
+ int getItemId() const { return mItemId; }
/**
* Returns the item info for this floor item. Useful for adding an item
@@ -59,15 +59,11 @@ class FloorItem : public ActorSprite
*/
const ItemInfo &getInfo() const;
- int getTileX() const override
- { return mX; }
-
- int getTileY() const override
- { return mY; }
+ int getTileX() const override { return mX; }
+ int getTileY() const override { return mY; }
private:
int mItemId;
int mX, mY;
+ ResourceRef<Image> mImage;
};
-
-#endif // FLOORITEM_H
diff --git a/src/game.cpp b/src/game.cpp
index c8b295bb..9657bee7 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -57,7 +57,7 @@
#include "gui/sdlinput.h"
#include "gui/setup.h"
#include "gui/socialwindow.h"
-#include "gui/specialswindow.h"
+#include "gui/abilitieswindow.h"
#include "gui/skilldialog.h"
#include "gui/statuswindow.h"
#include "gui/textdialog.h"
@@ -74,16 +74,14 @@
#include "resources/imagewriter.h"
#include "resources/mapreader.h"
-#include "resources/resourcemanager.h"
+#include "utils/filesystem.h"
#include "utils/gettext.h"
#include "utils/mkdir.h"
#include <guichan/exception.hpp>
#include <guichan/focushandler.hpp>
-#include <physfs.h>
-
#include <fstream>
#include <sstream>
@@ -106,7 +104,7 @@ DebugWindow *debugWindow;
ShortcutWindow *itemShortcutWindow;
ShortcutWindow *emoteShortcutWindow;
OutfitWindow *outfitWindow;
-SpecialsWindow *specialsWindow;
+AbilitiesWindow *abilitiesWindow;
SocialWindow *socialWindow;
ActorSpriteManager *actorSpriteManager;
@@ -131,7 +129,7 @@ static void initEngines()
effectManager = new EffectManager;
particleEngine = new Particle(nullptr);
- particleEngine->setupEngine();
+ Particle::setupEngine();
Event::trigger(Event::GameChannel, Event::EnginesInitialized);
}
@@ -161,7 +159,7 @@ static void createGuiWindows()
emoteShortcutWindow = new ShortcutWindow("EmoteShortcut",
new EmoteShortcutContainer);
outfitWindow = new OutfitWindow();
- specialsWindow = new SpecialsWindow();
+ abilitiesWindow = new AbilitiesWindow();
socialWindow = new SocialWindow();
localChatTab = new ChatTab(_("General"));
@@ -194,7 +192,7 @@ static void destroyGuiWindows()
del_0(itemShortcutWindow)
del_0(emoteShortcutWindow)
del_0(outfitWindow)
- del_0(specialsWindow)
+ del_0(abilitiesWindow)
del_0(socialWindow)
Event::trigger(Event::NpcChannel, Event::CloseAll); // Cleanup remaining NPC dialogs
@@ -252,6 +250,7 @@ Game::~Game()
del_0(actorSpriteManager)
if (Client::getState() != STATE_CHANGE_MAP)
del_0(local_player)
+ del_0(effectManager)
del_0(channelManager)
del_0(commandHandler)
del_0(joystick)
@@ -304,7 +303,7 @@ static bool saveScreenshot()
logger->log("Directory %s doesn't exist and can't be created! "
"Setting screenshot directory to home.",
screenshotDirectory.c_str());
- screenshotDirectory = PHYSFS_getUserDir();
+ screenshotDirectory = FS::getUserDir();
}
do
@@ -928,9 +927,6 @@ void Game::changeMap(const std::string &mapPath)
std::string fullMap = paths.getValue("maps", "maps/")
+ mMapName + ".tmx";
- ResourceManager *resman = ResourceManager::getInstance();
- if (!resman->exists(fullMap))
- fullMap += ".gz";
// Attempt to load the new map
Map *newMap = MapReader::readMap(fullMap);
diff --git a/src/game.h b/src/game.h
index 9f88ba90..c7ff720e 100644
--- a/src/game.h
+++ b/src/game.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GAME_H
-#define GAME_H
+#pragma once
#include <string>
@@ -97,5 +96,3 @@ class Game
static Game *mInstance;
};
-
-#endif
diff --git a/src/graphics.cpp b/src/graphics.cpp
index afdac4f6..9b8cce83 100644
--- a/src/graphics.cpp
+++ b/src/graphics.cpp
@@ -25,14 +25,40 @@
#include <guichan/exception.hpp>
+ImageRect::ImageRect()
+{
+ memset(grid, 0, sizeof(grid));
+}
+
+ImageRect::ImageRect(ImageRect &&r)
+{
+ memcpy(grid, r.grid, sizeof(grid));
+ memset(r.grid, 0, sizeof(grid));
+}
+
+ImageRect::~ImageRect()
+{
+ for (auto img : grid)
+ delete img;
+}
+
void ImageRect::setAlpha(float alpha)
{
for (auto img : grid)
- {
img->setAlpha(alpha);
- }
}
+int ImageRect::minWidth() const
+{
+ return grid[ImageRect::UPPER_LEFT]->getWidth() + grid[ImageRect::UPPER_RIGHT]->getWidth();
+}
+
+int ImageRect::minHeight() const
+{
+ return grid[ImageRect::UPPER_LEFT]->getHeight() + grid[ImageRect::LOWER_LEFT]->getHeight();
+}
+
+
void Graphics::updateSize(int width, int height, float /*scale*/)
{
mWidth = width;
@@ -55,6 +81,14 @@ bool Graphics::drawImageF(const Image *image, float x, float y)
return drawImageF(image, 0, 0, x, y, image->getWidth(), image->getHeight());
}
+bool Graphics::drawRescaledImage(const Image *image, int x, int y, int width, int height)
+{
+ if (!image)
+ return false;
+
+ return drawRescaledImage(image, 0, 0, x, y, image->getWidth(), image->getHeight(), width, height);
+}
+
bool Graphics::drawRescaledImageF(const Image *image, int srcX, int srcY, float dstX, float dstY, int width, int height, float desiredWidth, float desiredHeight, bool useColor)
{
return drawRescaledImage(image,
@@ -103,51 +137,95 @@ void Graphics::drawImageRect(int x, int y, int w, int h,
const Image *bottomLeft, const Image *bottomRight,
const Image *top, const Image *right,
const Image *bottom, const Image *left,
- const Image *center)
-{
- pushClipArea(gcn::Rectangle(x, y, w, h));
-
- // Draw the center area
- drawImagePattern(center,
- topLeft->getWidth(), topLeft->getHeight(),
- w - topLeft->getWidth() - topRight->getWidth(),
- h - topLeft->getHeight() - bottomLeft->getHeight());
-
- // Draw the sides
- drawImagePattern(top,
- left->getWidth(), 0,
- w - left->getWidth() - right->getWidth(), top->getHeight());
- drawImagePattern(bottom,
- left->getWidth(), h - bottom->getHeight(),
- w - left->getWidth() - right->getWidth(),
- bottom->getHeight());
- drawImagePattern(left,
- 0, top->getHeight(),
- left->getWidth(),
- h - top->getHeight() - bottom->getHeight());
- drawImagePattern(right,
- w - right->getWidth(), top->getHeight(),
- right->getWidth(),
- h - top->getHeight() - bottom->getHeight());
+ const Image *center,
+ FillMode fillMode)
+{
+ switch (fillMode) {
+ case FillMode::Stretch:
+ // Draw the center area
+ drawRescaledImage(center,
+ x + topLeft->getWidth(),
+ y + topLeft->getHeight(),
+ w - topLeft->getWidth() - topRight->getWidth(),
+ h - topLeft->getHeight() - bottomLeft->getHeight());
+
+ // Draw the sides
+ drawRescaledImage(top,
+ x + left->getWidth(),
+ y,
+ w - left->getWidth() - right->getWidth(),
+ top->getHeight());
+
+ drawRescaledImage(bottom,
+ x + left->getWidth(),
+ y + h - bottom->getHeight(),
+ w - left->getWidth() - right->getWidth(),
+ bottom->getHeight());
+
+ drawRescaledImage(left,
+ x,
+ y + top->getHeight(),
+ left->getWidth(),
+ h - top->getHeight() - bottom->getHeight());
+
+ drawRescaledImage(right,
+ x + w - right->getWidth(),
+ y + top->getHeight(),
+ right->getWidth(),
+ h - top->getHeight() - bottom->getHeight());
+ break;
+ case FillMode::Repeat:
+ // Draw the center area
+ drawImagePattern(center,
+ x + topLeft->getWidth(),
+ y + topLeft->getHeight(),
+ w - topLeft->getWidth() - topRight->getWidth(),
+ h - topLeft->getHeight() - bottomLeft->getHeight());
+
+ // Draw the sides
+ drawImagePattern(top,
+ x + left->getWidth(),
+ y,
+ w - left->getWidth() - right->getWidth(),
+ top->getHeight());
+
+ drawImagePattern(bottom,
+ x + left->getWidth(),
+ y + h - bottom->getHeight(),
+ w - left->getWidth() - right->getWidth(),
+ bottom->getHeight());
+
+ drawImagePattern(left,
+ x,
+ y + top->getHeight(),
+ left->getWidth(),
+ h - top->getHeight() - bottom->getHeight());
+
+ drawImagePattern(right,
+ x + w - right->getWidth(),
+ y + top->getHeight(),
+ right->getWidth(),
+ h - top->getHeight() - bottom->getHeight());
+ break;
+ }
// Draw the corners
- drawImage(topLeft, 0, 0);
- drawImage(topRight, w - topRight->getWidth(), 0);
- drawImage(bottomLeft, 0, h - bottomLeft->getHeight());
+ drawImage(topLeft, x, y);
+ drawImage(topRight, x + w - topRight->getWidth(), y);
+ drawImage(bottomLeft, x, y + h - bottomLeft->getHeight());
drawImage(bottomRight,
- w - bottomRight->getWidth(),
- h - bottomRight->getHeight());
-
- popClipArea();
+ x + w - bottomRight->getWidth(),
+ y + h - bottomRight->getHeight());
}
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],
- imgRect.grid[1], imgRect.grid[5], imgRect.grid[7], imgRect.grid[3],
- imgRect.grid[4]);
+ imgRect.grid[0], imgRect.grid[2], imgRect.grid[6], imgRect.grid[8],
+ imgRect.grid[1], imgRect.grid[5], imgRect.grid[7], imgRect.grid[3],
+ imgRect.grid[4],
+ imgRect.fillMode);
}
void Graphics::_beginDraw()
@@ -159,3 +237,20 @@ void Graphics::_endDraw()
{
popClipArea();
}
+
+void Graphics::pushClipRect(const gcn::Rectangle &rect)
+{
+ const gcn::ClipRectangle &carea = mClipStack.top();
+ mClipRects.emplace(rect.x + carea.xOffset,
+ rect.y + carea.yOffset,
+ rect.width,
+ rect.height);
+
+ updateClipRect();
+}
+
+void Graphics::popClipRect()
+{
+ mClipRects.pop();
+ updateClipRect();
+}
diff --git a/src/graphics.h b/src/graphics.h
index 9db92034..4b03023d 100644
--- a/src/graphics.h
+++ b/src/graphics.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GRAPHICS_H
-#define GRAPHICS_H
+#pragma once
#include <SDL.h>
@@ -29,6 +28,12 @@
class Image;
+enum class FillMode
+{
+ Stretch,
+ Repeat,
+};
+
/**
* 9 images defining a rectangle. 4 corners, 4 sides and a middle area. The
* topology is as follows:
@@ -62,9 +67,20 @@ public:
LOWER_RIGHT = 8
};
+ ImageRect();
+ ImageRect(const ImageRect &) = delete;
+ ImageRect(ImageRect &&);
+ ~ImageRect();
+
+ ImageRect &operator=(const ImageRect &) = delete;
+ ImageRect &operator=(ImageRect &&r) = delete;
+
Image *grid[9];
+ FillMode fillMode = FillMode::Stretch;
void setAlpha(float alpha);
+ int minWidth() const;
+ int minHeight() const;
};
/**
@@ -106,6 +122,11 @@ class Graphics : public gcn::Graphics
/**
* Draws a rescaled version of the image.
*/
+ bool drawRescaledImage(const Image *image, int x, int y, int width, int height);
+
+ /**
+ * Draws a rescaled version of the image.
+ */
virtual bool drawRescaledImage(const Image *image, int srcX, int srcY,
int dstX, int dstY,
int width, int height,
@@ -167,7 +188,8 @@ class Graphics : public gcn::Graphics
const Image *bottomLeft, const Image *bottomRight,
const Image *top, const Image *right,
const Image *bottom, const Image *left,
- const Image *center);
+ const Image *center,
+ FillMode fillMode = FillMode::Stretch);
/**
* Draws a rectangle using images. 4 corner images, 4 side images and 1
@@ -239,13 +261,19 @@ class Graphics : public gcn::Graphics
return mColor;
}
+ void pushClipRect(const gcn::Rectangle &rect);
+ void popClipRect();
+
protected:
+ virtual void updateClipRect() = 0;
+
int mWidth = 0;
int mHeight = 0;
float mScale = 1.0f;
gcn::Color mColor;
+
+ // Actual clipping rects. Clipping by gcn::Graphics::mClipStack is disabled.
+ std::stack<gcn::Rectangle> mClipRects;
};
extern Graphics *graphics;
-
-#endif
diff --git a/src/gui/specialswindow.cpp b/src/gui/abilitieswindow.cpp
index 340a1e41..700fa7ff 100644
--- a/src/gui/specialswindow.cpp
+++ b/src/gui/abilitieswindow.cpp
@@ -18,7 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "gui/specialswindow.h"
+#include "gui/abilitieswindow.h"
#include "log.h"
@@ -32,9 +32,9 @@
#include "gui/widgets/windowcontainer.h"
#include "net/net.h"
-#include "net/specialhandler.h"
+#include "net/abilityhandler.h"
-#include "resources/specialdb.h"
+#include "resources/abilitydb.h"
#include "resources/theme.h"
#include "utils/dtor.h"
@@ -44,19 +44,19 @@
#include <string>
-#define SPECIALS_WIDTH 200
-#define SPECIALS_HEIGHT 32
+#define ABILITIES_WIDTH 200
+#define ABILITIES_HEIGHT 32
-class SpecialEntry : public Container
+class AbilityEntry : public Container
{
public:
- SpecialEntry(SpecialInfo *info);
+ AbilityEntry(AbilityInfo *info);
void update(int current, int needed);
protected:
- friend class SpecialsWindow;
- SpecialInfo *mInfo;
+ friend class AbilitiesWindow;
+ AbilityInfo *mInfo;
private:
Icon *mIcon = nullptr; // icon to display
@@ -65,46 +65,46 @@ class SpecialEntry : public Container
ProgressBar *mRechargeBar = nullptr; // recharge bar (only shown when applicable)
};
-SpecialsWindow::SpecialsWindow():
- Window(_("Specials"))
+AbilitiesWindow::AbilitiesWindow():
+ Window(_("Abilities"))
{
- setWindowName("Specials");
+ setWindowName("Abilities");
setCloseButton(true);
setResizable(true);
setSaveVisible(true);
- setDefaultSize(windowContainer->getWidth() - 280, 40, SPECIALS_WIDTH + 20, 225);
+ setDefaultSize(windowContainer->getWidth() - 280, 40, ABILITIES_WIDTH + 20, 225);
setupWindow->registerWindowForReset(this);
center();
loadWindowState();
}
-SpecialsWindow::~SpecialsWindow()
+AbilitiesWindow::~AbilitiesWindow()
{
// Clear gui
}
-void SpecialsWindow::action(const gcn::ActionEvent &event)
+void AbilitiesWindow::action(const gcn::ActionEvent &event)
{
if (event.getId() == "use")
{
- auto *disp = dynamic_cast<SpecialEntry*>(event.getSource()->getParent());
+ auto *disp = dynamic_cast<AbilityEntry*>(event.getSource()->getParent());
if (disp)
{
- if (disp->mInfo->targetMode == SpecialInfo::TARGET_BEING)
+ if (disp->mInfo->targetMode == AbilityInfo::TARGET_BEING)
{
Being *target = local_player->getTarget();
if (target)
- Net::getSpecialHandler()->use(disp->mInfo->id, 1, target->getId());
+ Net::getAbilityHandler()->useOn(disp->mInfo->id, target->getId());
else
- Net::getSpecialHandler()->use(disp->mInfo->id);
+ Net::getAbilityHandler()->use(disp->mInfo->id);
}
else
{
// TODO: Allow the player to aim at a position on the map and
- // Use special on the map position.
+ // Use abilities on the map position.
}
}
}
@@ -114,73 +114,73 @@ void SpecialsWindow::action(const gcn::ActionEvent &event)
}
}
-void SpecialsWindow::draw(gcn::Graphics *graphics)
+void AbilitiesWindow::draw(gcn::Graphics *graphics)
{
// update the progress bars
- std::map<int, Special> specialData = PlayerInfo::getSpecialStatus();
+ const std::map<int, Ability> &abilityData = PlayerInfo::getAbilityStatus();
bool foundNew = false;
- unsigned int found = 0; // number of entries in specialData which match mEntries
+ unsigned int found = 0; // number of entries in abilityData which match mEntries
- for (auto &special : specialData)
+ for (auto &[id, ability] : abilityData)
{
- auto e = mEntries.find(special.first);
+ auto e = mEntries.find(id);
if (e == mEntries.end())
{
- // found a new special - abort update and rebuild from scratch
+ // found a new ability - abort update and rebuild from scratch
foundNew = true;
break;
}
- // update progress bar of special
- e->second->update(special.second.currentMana, special.second.neededMana);
+ // update progress bar of ability
+ e->second->update(ability.currentMana, ability.neededMana);
found++;
}
- // a rebuild is needed when a) the number of specials changed or b) an existing entry isn't found anymore
+ // a rebuild is needed when a) the number of abilities changed or b) an existing entry isn't found anymore
if (foundNew || found != mEntries.size())
- rebuild(specialData);
+ rebuild(abilityData);
Window::draw(graphics);
}
-void SpecialsWindow::rebuild(const std::map<int, Special> &specialData)
+void AbilitiesWindow::rebuild(const std::map<int, Ability> &abilityData)
{
delete_all(mEntries);
mEntries.clear();
int vPos = 0; //vertical position of next placed element
- for (auto special : specialData)
+ for (auto &[id, ability] : abilityData)
{
- logger->log("Updating special GUI for %d", special.first);
+ logger->log("Updating ability GUI for %d", id);
- SpecialInfo *info = SpecialDB::get(special.first);
+ AbilityInfo *info = AbilityDB::get(id);
if (info)
{
- info->rechargeCurrent = special.second.currentMana;
- info->rechargeNeeded = special.second.neededMana;
- auto* entry = new SpecialEntry(info);
+ info->rechargeCurrent = ability.currentMana;
+ info->rechargeNeeded = ability.neededMana;
+ auto *entry = new AbilityEntry(info);
entry->setPosition(0, vPos);
vPos += entry->getHeight() + 3;
add(entry);
- mEntries[special.first] = entry;
+ mEntries[id] = entry;
}
else
{
- logger->log("Warning: No info available of special %d", special.first);
+ logger->log("Warning: No info available of ability %d", id);
}
}
}
-SpecialEntry::SpecialEntry(SpecialInfo *info) :
+AbilityEntry::AbilityEntry(AbilityInfo *info) :
mInfo(info)
{
- setSize(SPECIALS_WIDTH, SPECIALS_HEIGHT);
+ setSize(ABILITIES_WIDTH, ABILITIES_HEIGHT);
if (!info->icon.empty())
mIcon = new Icon(info->icon);
else
- mIcon = new Icon(Theme::resolveThemePath("unknown-item.png"));
+ mIcon = new Icon(Theme::getImageFromTheme("unknown-item.png"));
mIcon->setPosition(1, 0);
add(mIcon);
@@ -189,7 +189,7 @@ SpecialEntry::SpecialEntry(SpecialInfo *info) :
mNameLabel->setPosition(35, 0);
add(mNameLabel);
- mUse = new Button("Use", "use", specialsWindow);
+ mUse = new Button("Use", "use", abilitiesWindow);
mUse->setPosition(getWidth() - mUse->getWidth(), 5);
add(mUse);
@@ -203,7 +203,7 @@ SpecialEntry::SpecialEntry(SpecialInfo *info) :
}
}
-void SpecialEntry::update(int current, int needed)
+void AbilityEntry::update(int current, int needed)
{
if (mRechargeBar)
{
diff --git a/src/gui/specialswindow.h b/src/gui/abilitieswindow.h
index 4ed4db3e..ddc5a9e4 100644
--- a/src/gui/specialswindow.h
+++ b/src/gui/abilitieswindow.h
@@ -18,8 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SPECIALSWINDOW_H
-#define SPECIALSWINDOW_H
+#pragma once
#include "playerinfo.h"
@@ -29,14 +28,14 @@
#include <map>
-class SpecialEntry;
+class AbilityEntry;
-class SpecialsWindow : public Window, public gcn::ActionListener
+class AbilitiesWindow : public Window, public gcn::ActionListener
{
public:
- SpecialsWindow();
+ AbilitiesWindow();
- ~SpecialsWindow() override;
+ ~AbilitiesWindow() override;
/**
* Called when receiving actions from widget.
@@ -45,16 +44,14 @@ class SpecialsWindow : public Window, public gcn::ActionListener
void draw(gcn::Graphics *graphics) override;
- bool hasSpecials() const
+ bool hasAbilities() const
{ return !mEntries.empty(); }
private:
- // (re)constructs the list of specials
- void rebuild(const std::map<int, Special> &specialData);
+ // (re)constructs the list of abilities
+ void rebuild(const std::map<int, Ability> &abilityData);
- std::map<int, SpecialEntry *> mEntries;
+ std::map<int, AbilityEntry *> mEntries;
};
-extern SpecialsWindow *specialsWindow;
-
-#endif // SPECIALSWINDOW_H
+extern AbilitiesWindow *abilitiesWindow;
diff --git a/src/gui/beingpopup.h b/src/gui/beingpopup.h
index 45169464..377b1b4e 100644
--- a/src/gui/beingpopup.h
+++ b/src/gui/beingpopup.h
@@ -18,8 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef BEINGPOPUP_H
-#define BEINGPOPUP_H
+#pragma once
#include "gui/widgets/popup.h"
@@ -47,5 +46,3 @@ class BeingPopup : public Popup
Label *mBeingName;
Label *mBeingParty;
};
-
-#endif // BEINGPOPUP_H
diff --git a/src/gui/buydialog.h b/src/gui/buydialog.h
index b1126225..4c67ef0d 100644
--- a/src/gui/buydialog.h
+++ b/src/gui/buydialog.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef BUY_H
-#define BUY_H
+#pragma once
#include "guichanfwd.h"
@@ -124,5 +123,3 @@ class BuyDialog : public Window, public gcn::ActionListener,
int mAmountItems;
int mMaxItems;
};
-
-#endif
diff --git a/src/gui/buyselldialog.h b/src/gui/buyselldialog.h
index 2e324d42..be382ffa 100644
--- a/src/gui/buyselldialog.h
+++ b/src/gui/buyselldialog.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef BUYSELL_H
-#define BUYSELL_H
+#pragma once
#include "gui/widgets/window.h"
@@ -63,5 +62,3 @@ class BuySellDialog : public Window, public gcn::ActionListener
int mNpcId;
gcn::Button *mBuyButton;
};
-
-#endif
diff --git a/src/gui/changeemaildialog.h b/src/gui/changeemaildialog.h
index 37d3ce01..cdf5664c 100644
--- a/src/gui/changeemaildialog.h
+++ b/src/gui/changeemaildialog.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GUI_CHANGEEMAIL_H
-#define GUI_CHANGEEMAIL_H
+#pragma once
#include "gui/widgets/window.h"
@@ -64,5 +63,3 @@ class ChangeEmailDialog : public Window, public gcn::ActionListener
LoginData *mLoginData;
};
-
-#endif // GUI_CHANGEEMAIL_H
diff --git a/src/gui/changepassworddialog.h b/src/gui/changepassworddialog.h
index 22d449d1..541e0444 100644
--- a/src/gui/changepassworddialog.h
+++ b/src/gui/changepassworddialog.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef CHANGEPASSWORDDIALOG_H
-#define CHANGEPASSWORDDIALOG_H
+#pragma once
#include "gui/widgets/window.h"
@@ -59,5 +58,3 @@ class ChangePasswordDialog : public Window, public gcn::ActionListener
LoginData *mLoginData;
};
-
-#endif
diff --git a/src/gui/charcreatedialog.cpp b/src/gui/charcreatedialog.cpp
index fa1144b6..cff7d822 100644
--- a/src/gui/charcreatedialog.cpp
+++ b/src/gui/charcreatedialog.cpp
@@ -50,7 +50,7 @@ CharCreateDialog::CharCreateDialog(CharSelectDialog *parent, int slot):
mSlot(slot)
{
mPlayer = new Being(0, ActorSprite::PLAYER, 0, nullptr);
- mPlayer->setGender(Gender::MALE);
+ mPlayer->setGender(Gender::Male);
const std::vector<int> &items = CharDB::getDefaultItems();
for (size_t i = 0; i < items.size(); ++i)
@@ -70,18 +70,6 @@ CharCreateDialog::CharCreateDialog(CharSelectDialog *parent, int slot):
mNameField = new TextField(std::string());
mNameLabel = new Label(_("Name:"));
- mNextHairColorButton = new Button("", "nextcolor", this);
- mPrevHairColorButton = new Button("", "prevcolor", this);
- mPrevHairColorButton->setButtonIcon("tab_arrows_left.png");
- mNextHairColorButton->setButtonIcon("tab_arrows_right.png");
-
- mHairColorLabel = new Label(_("Hair color:"));
- mNextHairStyleButton = new Button("", "nextstyle", this);
- mPrevHairStyleButton = new Button("", "prevstyle", this);
- mPrevHairStyleButton->setButtonIcon("tab_arrows_left.png");
- mNextHairStyleButton->setButtonIcon("tab_arrows_right.png");
-
- mHairStyleLabel = new Label(_("Hair style:"));
mCreateButton = new Button(_("Create"), "create", this);
mCancelButton = new Button(_("Cancel"), "cancel", this);
mMale = new RadioButton(_("Male"), "gender");
@@ -112,12 +100,6 @@ CharCreateDialog::CharCreateDialog(CharSelectDialog *parent, int slot):
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);
mAttributesLeft->setPosition(15, 280);
updateSliders();
mCancelButton->setPosition(
@@ -136,16 +118,36 @@ CharCreateDialog::CharCreateDialog(CharSelectDialog *parent, int slot):
if (mHairColorsIds.size() > 1)
{
- add(mNextHairColorButton);
- add(mPrevHairColorButton);
- add(mHairColorLabel);
+ auto hairColorLabel = new Label(_("Hair color:"));
+ auto nextHairColorButton = new Button("", "nextcolor", this);
+ auto prevHairColorButton = new Button("", "prevcolor", this);
+ prevHairColorButton->setButtonIcon("tab_arrows_left.png");
+ nextHairColorButton->setButtonIcon("tab_arrows_right.png");
+
+ hairColorLabel->setPosition(5, 40);
+ prevHairColorButton->setPosition(90, 35);
+ nextHairColorButton->setPosition(165, 35);
+
+ add(nextHairColorButton);
+ add(prevHairColorButton);
+ add(hairColorLabel);
}
if (mHairStylesIds.size() > 1)
{
- add(mNextHairStyleButton);
- add(mPrevHairStyleButton);
- add(mHairStyleLabel);
+ auto hairStyleLabel = new Label(_("Hair style:"));
+ auto nextHairStyleButton = new Button("", "nextstyle", this);
+ auto prevHairStyleButton = new Button("", "prevstyle", this);
+ prevHairStyleButton->setButtonIcon("tab_arrows_left.png");
+ nextHairStyleButton->setButtonIcon("tab_arrows_right.png");
+
+ hairStyleLabel->setPosition(5, 70);
+ prevHairStyleButton->setPosition(90, 64);
+ nextHairStyleButton->setPosition(165, 64);
+
+ add(nextHairStyleButton);
+ add(prevHairStyleButton);
+ add(hairStyleLabel);
}
add(mAttributesLeft);
@@ -165,14 +167,15 @@ CharCreateDialog::~CharCreateDialog()
delete mPlayer;
// Make sure the char server handler knows that we're gone
- Net::getCharHandler()->setCharCreateDialog(nullptr);
+ if (auto charHandler = Net::getCharHandler())
+ charHandler->setCharCreateDialog(nullptr);
}
void CharCreateDialog::action(const gcn::ActionEvent &event)
{
if (event.getId() == "create")
{
- if (Net::getNetworkType() == ServerType::MANASERV
+ if (Net::getNetworkType() == ServerType::ManaServ
|| getName().length() >= 4)
{
// Attempt to create the character
@@ -180,7 +183,7 @@ void CharCreateDialog::action(const gcn::ActionEvent &event)
int characterSlot = mSlot;
// On Manaserv, the slots start at 1, so we offset them.
- if (Net::getNetworkType() == ServerType::MANASERV)
+ if (Net::getNetworkType() == ServerType::ManaServ)
++characterSlot;
// Should avoid the most common crash case
@@ -231,9 +234,9 @@ void CharCreateDialog::action(const gcn::ActionEvent &event)
else if (event.getId() == "gender")
{
if (mMale->isSelected())
- mPlayer->setGender(Gender::MALE);
+ mPlayer->setGender(Gender::Male);
else
- mPlayer->setGender(Gender::FEMALE);
+ mPlayer->setGender(Gender::Female);
}
}
@@ -341,7 +344,7 @@ void CharCreateDialog::setAttributes(const std::vector<std::string> &labels,
add(attributeLabel);
auto *attributeSlider = new Slider(min, max);
- attributeSlider->setDimension(gcn::Rectangle(75, y, 100, 10));
+ attributeSlider->setDimension(gcn::Rectangle(75, y, 100, attributeSlider->getHeight()));
attributeSlider->setActionEventId("statslider");
attributeSlider->addActionListener(this);
add(attributeSlider);
@@ -373,7 +376,7 @@ void CharCreateDialog::setAttributes(const std::vector<std::string> &labels,
void CharCreateDialog::setDefaultGender(Gender gender)
{
- if (gender == Gender::FEMALE)
+ if (gender == Gender::Female)
{
mFemale->setSelected(true);
mMale->setSelected(false);
diff --git a/src/gui/charcreatedialog.h b/src/gui/charcreatedialog.h
index 46db6229..f1759650 100644
--- a/src/gui/charcreatedialog.h
+++ b/src/gui/charcreatedialog.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef CHAR_CREATE_DIALOG_H
-#define CHAR_CREATE_DIALOG_H
+#pragma once
#include "being.h"
#include "guichanfwd.h"
@@ -82,12 +81,6 @@ class CharCreateDialog : public Window, public gcn::ActionListener
gcn::TextField *mNameField;
gcn::Label *mNameLabel;
- Button *mNextHairColorButton;
- Button *mPrevHairColorButton;
- gcn::Label *mHairColorLabel;
- Button *mNextHairStyleButton;
- Button *mPrevHairStyleButton;
- gcn::Label *mHairStyleLabel;
gcn::RadioButton *mMale;
gcn::RadioButton *mFemale;
@@ -114,5 +107,3 @@ class CharCreateDialog : public Window, public gcn::ActionListener
int mSlot;
};
-
-#endif // CHAR_CREATE_DIALOG_H
diff --git a/src/gui/charselectdialog.cpp b/src/gui/charselectdialog.cpp
index 7792813c..2485be69 100644
--- a/src/gui/charselectdialog.cpp
+++ b/src/gui/charselectdialog.cpp
@@ -150,7 +150,7 @@ CharSelectDialog::CharSelectDialog(LoginData *loginData):
for (int i = 0; i < (int)mLoginData->characterSlots; i++)
{
mCharacterEntries.push_back(new CharacterDisplay(this));
- place(i % SLOTS_PER_ROW, (int)i / SLOTS_PER_ROW, mCharacterEntries[i]);
+ place(i % SLOTS_PER_ROW, i / SLOTS_PER_ROW, mCharacterEntries[i]);
}
reflowLayout();
@@ -266,7 +266,7 @@ void CharSelectDialog::setCharacters(const Net::Characters &characters)
{
// Slots Number start at 1 for Manaserv, so we offset them by one.
int characterSlot = character->slot;
- if (Net::getNetworkType() == ServerType::MANASERV && characterSlot > 0)
+ if (Net::getNetworkType() == ServerType::ManaServ && characterSlot > 0)
--characterSlot;
if (characterSlot >= (int)mCharacterEntries.size())
diff --git a/src/gui/charselectdialog.h b/src/gui/charselectdialog.h
index 2deb5a7d..e5e558c9 100644
--- a/src/gui/charselectdialog.h
+++ b/src/gui/charselectdialog.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef CHAR_SELECT_H
-#define CHAR_SELECT_H
+#pragma once
#include "gui/widgets/window.h"
@@ -100,5 +99,3 @@ class CharSelectDialog : public Window, public gcn::ActionListener,
Net::CharHandler *mCharHandler;
};
-
-#endif
diff --git a/src/gui/chatwindow.cpp b/src/gui/chatwindow.cpp
index 453baea2..8a34c961 100644
--- a/src/gui/chatwindow.cpp
+++ b/src/gui/chatwindow.cpp
@@ -76,18 +76,17 @@ class ChatAutoComplete : public AutoCompleteLister
{
void getAutoCompleteList(std::vector<std::string> &list) const override
{
- auto *tab = static_cast<ChatTab*>(chatWindow->mChatTabs
- ->getSelectedTab());
-
- return tab->getAutoCompleteList(list);
+ auto tab = static_cast<ChatTab *>(chatWindow->mChatTabs->getSelectedTab());
+ tab->getAutoCompleteList(list);
}
};
ChatWindow::ChatWindow():
Window(_("Chat")),
- mHistory(new TextHistory()),
+ mItemLinkHandler(new ItemLinkHandler(this)),
+ mChatInput(new ChatInput),
mAutoComplete(new ChatAutoComplete),
- mTmpVisible(false)
+ mChatTabs(new TabbedArea)
{
listen(Event::ChatChannel);
listen(Event::NoticesChannel);
@@ -106,21 +105,16 @@ ChatWindow::ChatWindow():
setMinWidth(150);
setMinHeight(90);
- mItemLinkHandler = new ItemLinkHandler(this);
-
- mChatInput = new ChatInput;
mChatInput->setActionEventId("chatinput");
mChatInput->addActionListener(this);
- mChatTabs = new TabbedArea;
-
getLayout().setPadding(3);
place(0, 0, mChatTabs, 3, 3);
place(0, 3, mChatInput, 3);
loadWindowState();
- mChatInput->setHistory(mHistory);
+ mChatInput->setHistory(&mHistory);
mChatInput->setAutoComplete(mAutoComplete);
mRecorder = new Recorder(this);
@@ -131,7 +125,6 @@ ChatWindow::~ChatWindow()
delete mRecorder;
removeAllWhispers();
delete mItemLinkHandler;
- delete mHistory;
delete mAutoComplete;
}
@@ -146,15 +139,10 @@ ChatTab *ChatWindow::getFocused() const
return static_cast<ChatTab*>(mChatTabs->getSelectedTab());
}
-void ChatWindow::clearTab(ChatTab *tab)
-{
- if (tab)
- tab->clearText();
-}
-
void ChatWindow::clearTab()
{
- clearTab(getFocused());
+ if (auto tab = getFocused())
+ tab->clearText();
}
void ChatWindow::prevTab()
diff --git a/src/gui/chatwindow.h b/src/gui/chatwindow.h
index 59ab6d99..6cac6f8e 100644
--- a/src/gui/chatwindow.h
+++ b/src/gui/chatwindow.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef CHAT_H
-#define CHAT_H
+#pragma once
#include "eventlistener.h"
@@ -32,21 +31,14 @@
#include <guichan/widget.hpp>
#include <guichan/widgetlistener.hpp>
-#include <list>
#include <string>
#include <map>
-#include <vector>
-class BrowserBox;
class ChatTab;
-class Channel;
class ChatInput;
class Recorder;
-class ScrollArea;
class TabbedArea;
class ItemLinkHandler;
-class Tab;
-class WhisperTab;
#define DEFAULT_CHAT_WINDOW_SCROLL 7 // 1 means `1/8th of the window size'.
@@ -99,11 +91,6 @@ class ChatWindow : public Window,
ChatTab *getFocused() const;
/**
- * Clear the given tab.
- */
- void clearTab(ChatTab *tab);
-
- /**
* Clear the current tab.
*/
void clearTab();
@@ -202,11 +189,11 @@ class ChatWindow : public Window,
/** Input box for typing chat messages. */
ChatInput *mChatInput;
- TextHistory *mHistory;
+ TextHistory mHistory;
AutoCompleteLister *mAutoComplete;
private:
- bool mTmpVisible;
+ bool mTmpVisible = false;
/** Tabbed area for holding each channel. */
TabbedArea *mChatTabs;
@@ -216,5 +203,3 @@ class ChatWindow : public Window,
};
extern ChatWindow *chatWindow;
-
-#endif
diff --git a/src/gui/confirmdialog.h b/src/gui/confirmdialog.h
index 8eb3f46b..ba51558e 100644
--- a/src/gui/confirmdialog.h
+++ b/src/gui/confirmdialog.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef OPTION_DIALOG_H
-#define OPTION_DIALOG_H
+#pragma once
#include "gui/widgets/window.h"
@@ -47,5 +46,3 @@ class ConfirmDialog : public Window, public gcn::ActionListener
private:
TextBox *mTextBox;
};
-
-#endif
diff --git a/src/gui/connectiondialog.h b/src/gui/connectiondialog.h
index e76a0a3e..6e477435 100644
--- a/src/gui/connectiondialog.h
+++ b/src/gui/connectiondialog.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef CONNECTION_H
-#define CONNECTION_H
+#pragma once
#include "client.h"
@@ -58,5 +57,3 @@ class ConnectionDialog : public Window, gcn::ActionListener
gcn::Label *mLabel;
State mCancelState;
};
-
-#endif
diff --git a/src/gui/customserverdialog.cpp b/src/gui/customserverdialog.cpp
index c7c03b1a..5524e459 100644
--- a/src/gui/customserverdialog.cpp
+++ b/src/gui/customserverdialog.cpp
@@ -37,11 +37,10 @@ std::string TypeListModel::getElementAt(int elementIndex)
if (elementIndex == 0)
return "TmwAthena";
#ifdef MANASERV_SUPPORT
- else if (elementIndex == 1)
+ if (elementIndex == 1)
return "ManaServ";
#endif
- else
- return "Unknown";
+ return "Unknown";
}
CustomServerDialog::CustomServerDialog(ServerDialog *parent, int index):
@@ -62,8 +61,8 @@ CustomServerDialog::CustomServerDialog(ServerDialog *parent, int index):
mPortField = new TextField(std::string());
#ifdef MANASERV_SUPPORT
- mTypeListModel = new TypeListModel();
- mTypeField = new DropDown(mTypeListModel);
+ mTypeListModel = std::make_unique<TypeListModel>();
+ mTypeField = new DropDown(mTypeListModel.get());
mTypeField->setSelected(0); // TmwAthena by default for now.
#endif
@@ -77,46 +76,25 @@ CustomServerDialog::CustomServerDialog(ServerDialog *parent, int index):
mPortField->addActionListener(this);
place(0, 0, nameLabel);
- place(1, 0, mNameField, 4).setPadding(3);
+ place(1, 0, mNameField, 4).setPadding(2);
place(0, 1, serverAdressLabel);
- place(1, 1, mServerAddressField, 4).setPadding(3);
+ place(1, 1, mServerAddressField, 4).setPadding(2);
place(0, 2, portLabel);
- place(1, 2, mPortField, 4).setPadding(3);
+ place(1, 2, mPortField, 4).setPadding(2);
#ifdef MANASERV_SUPPORT
place(0, 3, typeLabel);
- place(1, 3, mTypeField).setPadding(3);
+ place(1, 3, mTypeField).setPadding(2);
#endif
place(0, 4, descriptionLabel);
- place(1, 4, mDescriptionField, 4).setPadding(3);
+ place(1, 4, mDescriptionField, 4).setPadding(2);
place(4, 5, mOkButton);
place(3, 5, mCancelButton);
- // Do this manually instead of calling reflowLayout so we can enforce a
- // minimum width.
- int width = 0, height = 0;
- getLayout().reflow(width, height);
- if (width < 300)
- {
- width = 300;
- getLayout().reflow(width, height);
- }
- if (height < 120)
- {
- height = 120;
- getLayout().reflow(width, height);
- }
-
- setContentSize(width, height);
-
- setMinWidth(getWidth());
- setMinHeight(getHeight());
- setDefaultSize(getWidth(), getHeight(), ImageRect::CENTER);
+ reflowLayout();
+ setLocationRelativeTo(getParentWindow());
- setResizable(false);
addKeyListener(this);
- loadWindowState();
-
// Add the entry's info when in modify mode.
if (index > -1)
{
@@ -126,28 +104,17 @@ CustomServerDialog::CustomServerDialog(ServerDialog *parent, int index):
mServerAddressField->setText(serverInfo.hostname);
mPortField->setText(toString(serverInfo.port));
#ifdef MANASERV_SUPPORT
- mTypeField->setSelected(serverInfo.type == ServerType::TMWATHENA ?
+ mTypeField->setSelected(serverInfo.type == ServerType::TmwAthena ?
0 : 1);
#endif
}
- setLocationRelativeTo(getParentWindow());
setVisible(true);
mNameField->requestFocus();
}
-CustomServerDialog::~CustomServerDialog()
-{
-#ifdef MANASERV_SUPPORT
- delete mTypeListModel;
-#endif
-}
-
-void CustomServerDialog::logic()
-{
- Window::logic();
-}
+CustomServerDialog::~CustomServerDialog() = default;
void CustomServerDialog::action(const gcn::ActionEvent &event)
{
@@ -178,13 +145,13 @@ void CustomServerDialog::action(const gcn::ActionEvent &event)
switch (mTypeField->getSelected())
{
case 0:
- serverInfo.type = ServerType::TMWATHENA;
+ serverInfo.type = ServerType::TmwAthena;
break;
case 1:
- serverInfo.type = ServerType::MANASERV;
+ serverInfo.type = ServerType::ManaServ;
break;
default:
- serverInfo.type = ServerType::UNKNOWN;
+ serverInfo.type = ServerType::Unknown;
}
#else
serverInfo.type = ServerType::TMWATHENA;
diff --git a/src/gui/customserverdialog.h b/src/gui/customserverdialog.h
index 8b0af4c8..e523260c 100644
--- a/src/gui/customserverdialog.h
+++ b/src/gui/customserverdialog.h
@@ -18,15 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef CUSTOMSERVERDIALOG_H
-#define CUSTOMSERVERDIALOG_H
-
-class Button;
-class Label;
-class TextField;
-class DropDown;
-class ServerDialog;
-class TypeListModel;
+#pragma once
#include "gui/widgets/window.h"
@@ -34,6 +26,12 @@ class TypeListModel;
#include <guichan/keylistener.hpp>
#include <guichan/listmodel.hpp>
+#include <memory>
+
+class Button;
+class DropDown;
+class ServerDialog;
+class TextField;
/**
* Server Type List Model
@@ -65,7 +63,6 @@ class CustomServerDialog : public Window,
{
public:
CustomServerDialog(ServerDialog *parent, int index = -1);
-
~CustomServerDialog() override;
/**
@@ -75,22 +72,18 @@ class CustomServerDialog : public Window,
void keyPressed(gcn::KeyEvent &keyEvent) override;
- void logic() override;
-
private:
TextField *mServerAddressField;
TextField *mPortField;
- TextField *mNameField;
+ TextField *mNameField;
TextField *mDescriptionField;
Button *mOkButton;
Button *mCancelButton;
#ifdef MANASERV_SUPPORT
DropDown *mTypeField;
- TypeListModel *mTypeListModel;
+ std::unique_ptr<TypeListModel> mTypeListModel;
#endif
ServerDialog *mServerDialog;
// The index of the entry to modify, -1 when only adding a new entry.
int mIndex;
};
-
-#endif // CUSTOMSERVERDIALOG_H
diff --git a/src/gui/debugwindow.cpp b/src/gui/debugwindow.cpp
index 3c514191..17020062 100644
--- a/src/gui/debugwindow.cpp
+++ b/src/gui/debugwindow.cpp
@@ -244,11 +244,15 @@ DebugWindow::DebugWindow()
place(0, 0, tabs, 2, 2);
loadWindowState();
- Tab *tabInfo = new Tab;
- tabInfo->setCaption(_("Info"));
- tabs->addTab(tabInfo, new DebugInfo);
-
- Tab *tabSwitches = new Tab;
- tabSwitches->setCaption(_("Switches"));
- tabs->addTab(tabSwitches, new DebugSwitches);
+ mInfoTab = std::make_unique<Tab>();
+ mInfoTab->setCaption(_("Info"));
+ mInfoWidget = std::make_unique<DebugInfo>();
+ tabs->addTab(mInfoTab.get(), mInfoWidget.get());
+
+ mSwitchesTab = std::make_unique<Tab>();
+ mSwitchesTab->setCaption(_("Switches"));
+ mSwitchesWidget = std::make_unique<DebugSwitches>();
+ tabs->addTab(mSwitchesTab.get(), mSwitchesWidget.get());
}
+
+DebugWindow::~DebugWindow() = default;
diff --git a/src/gui/debugwindow.h b/src/gui/debugwindow.h
index 3376ae18..807f0d0f 100644
--- a/src/gui/debugwindow.h
+++ b/src/gui/debugwindow.h
@@ -19,11 +19,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef DEBUGWINDOW_H
-#define DEBUGWINDOW_H
+#pragma once
#include "gui/widgets/window.h"
+#include <memory>
+
+class Tab;
+
/**
* The debug window.
*
@@ -33,8 +36,13 @@ class DebugWindow : public Window
{
public:
DebugWindow();
+ ~DebugWindow() override;
+
+ private:
+ std::unique_ptr<Tab> mInfoTab;
+ std::unique_ptr<Tab> mSwitchesTab;
+ std::unique_ptr<gcn::Widget> mInfoWidget;
+ std::unique_ptr<gcn::Widget> mSwitchesWidget;
};
extern DebugWindow *debugWindow;
-
-#endif
diff --git a/src/gui/emotepopup.cpp b/src/gui/emotepopup.cpp
index 589d5087..e759ab25 100644
--- a/src/gui/emotepopup.cpp
+++ b/src/gui/emotepopup.cpp
@@ -25,7 +25,6 @@
#include "configuration.h"
#include "emoteshortcut.h"
#include "graphics.h"
-#include "imagesprite.h"
#include "log.h"
#include "resources/emotedb.h"
@@ -53,13 +52,12 @@ EmotePopup::EmotePopup()
setVisible(true);
}
-EmotePopup::~EmotePopup()
-{
- mSelectionImage->decRef();
-}
+EmotePopup::~EmotePopup() = default;
void EmotePopup::draw(gcn::Graphics *graphics)
{
+ auto *g = static_cast<Graphics*>(graphics);
+
Popup::draw(graphics);
const int emoteCount = EmoteDB::getEmoteCount();
@@ -81,13 +79,14 @@ void EmotePopup::draw(gcn::Graphics *graphics)
// Draw selection image below hovered item
if (i == mHoveredEmoteIndex)
- {
- static_cast<Graphics*>(graphics)->drawImage(
- mSelectionImage, emoteX, emoteY + 4);
- }
+ g->drawImage(mSelectionImage, emoteX, emoteY + 4);
// Draw emote icon
- EmoteDB::getByIndex(i).sprite->draw(static_cast<Graphics*>(graphics), emoteX, emoteY);
+ if (auto image = EmoteDB::getByIndex(i).image)
+ {
+ image->setAlpha(1.0f);
+ g->drawImage(image, emoteX, emoteY);
+ }
}
}
diff --git a/src/gui/emotepopup.h b/src/gui/emotepopup.h
index ef3fffed..c95c5723 100644
--- a/src/gui/emotepopup.h
+++ b/src/gui/emotepopup.h
@@ -20,10 +20,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef EMOTEPOPUP_H
-#define EMOTEPOPUP_H
+#pragma once
#include "gui/widgets/popup.h"
+#include "resources/resource.h"
#include <guichan/mouselistener.hpp>
@@ -104,7 +104,7 @@ class EmotePopup : public Popup
*/
void distributeValueChangedEvent();
- Image *mSelectionImage;
+ ResourceRef<Image> mSelectionImage;
int mSelectedEmoteId = -1;
int mHoveredEmoteIndex = -1;
@@ -116,5 +116,3 @@ class EmotePopup : public Popup
static const int gridWidth;
static const int gridHeight;
};
-
-#endif
diff --git a/src/gui/equipmentwindow.cpp b/src/gui/equipmentwindow.cpp
index e7eeb048..ff3c6630 100644
--- a/src/gui/equipmentwindow.cpp
+++ b/src/gui/equipmentwindow.cpp
@@ -65,13 +65,14 @@ EquipmentWindow::EquipmentWindow(Equipment *equipment):
setWindowName("Equipment");
setCloseButton(true);
setSaveVisible(true);
- setDefaultSize(180, 300, ImageRect::CENTER);
+ setContentSize(175, 290);
+ setDefaultSize(getWidth(), getHeight(), ImageRect::CENTER);
loadWindowState();
mUnequip = new Button(_("Unequip"), "unequip", this);
const gcn::Rectangle &area = getChildrenArea();
- mUnequip->setPosition(area.width - mUnequip->getWidth() - 5,
- area.height - mUnequip->getHeight() - 5);
+ mUnequip->setPosition(area.width - mUnequip->getWidth() - getPadding(),
+ area.height - mUnequip->getHeight() - getPadding());
mUnequip->setEnabled(false);
add(playerBox);
@@ -96,10 +97,7 @@ void EquipmentWindow::loadEquipBoxes()
Net::getInventoryHandler()->getBoxBackground(i);
if (!backgroundFile.empty())
- {
- box.backgroundImage =
- Theme::instance()->getImageFromTheme(backgroundFile);
- }
+ box.backgroundImage = Theme::getImageFromTheme(backgroundFile);
}
}
@@ -111,7 +109,6 @@ EquipmentWindow::~EquipmentWindow()
void EquipmentWindow::draw(gcn::Graphics *graphics)
{
Window::draw(graphics);
- Window::drawChildren(graphics);
// Draw equipment boxes
auto *g = static_cast<Graphics*>(graphics);
@@ -240,6 +237,8 @@ void EquipmentWindow::mousePressed(gcn::MouseEvent& mouseEvent)
void EquipmentWindow::mouseMoved(gcn::MouseEvent &event)
{
+ Window::mouseMoved(event);
+
const int x = event.getX();
const int y = event.getY();
@@ -264,6 +263,8 @@ void EquipmentWindow::mouseMoved(gcn::MouseEvent &event)
void EquipmentWindow::mouseExited(gcn::MouseEvent &event)
{
+ Window::mouseExited(event);
+
mItemPopup->setVisible(false);
}
diff --git a/src/gui/equipmentwindow.h b/src/gui/equipmentwindow.h
index 1b63c866..f46d1175 100644
--- a/src/gui/equipmentwindow.h
+++ b/src/gui/equipmentwindow.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef EQUIPMENTWINDOW_H
-#define EQUIPMENTWINDOW_H
+#pragma once
#include "equipment.h"
#include "resources/image.h"
@@ -29,6 +28,8 @@
#include <guichan/actionlistener.hpp>
+#include <vector>
+
class Inventory;
class Item;
class ItemPopup;
@@ -53,6 +54,8 @@ class EquipmentWindow : public Window, public gcn::ActionListener
void action(const gcn::ActionEvent &event) override;
void mousePressed(gcn::MouseEvent& mouseEvent) override;
+ void mouseMoved(gcn::MouseEvent &event) override;
+ void mouseExited(gcn::MouseEvent &event) override;
/**
* Loads the correct amount of displayed equip boxes.
@@ -73,7 +76,7 @@ class EquipmentWindow : public Window, public gcn::ActionListener
{
int posX = 0;
int posY = 0;
- Image *backgroundImage = nullptr;
+ ResourceRef<Image> backgroundImage;
};
std::vector<EquipBox> mBoxes; /**< Equipment boxes. */
@@ -82,9 +85,6 @@ class EquipmentWindow : public Window, public gcn::ActionListener
Equipment *mEquipment;
private:
- void mouseExited(gcn::MouseEvent &event) override;
- void mouseMoved(gcn::MouseEvent &event) override;
-
int getBoxIndex(int x, int y) const;
Item *getItem(int x, int y) const;
std::string getSlotName(int x, int y) const;
@@ -96,5 +96,3 @@ class EquipmentWindow : public Window, public gcn::ActionListener
};
extern EquipmentWindow *equipmentWindow;
-
-#endif // EQUIPMENTWINDOW_H
diff --git a/src/gui/focushandler.h b/src/gui/focushandler.h
index eb59bcf3..d90898bd 100644
--- a/src/gui/focushandler.h
+++ b/src/gui/focushandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef FOCUSHANDLER_H
-#define FOCUSHANDLER_H
+#pragma once
#include <guichan/focushandler.hpp>
@@ -73,5 +72,3 @@ class FocusHandler : public gcn::FocusHandler
*/
std::list<gcn::Widget*> mModalStack;
};
-
-#endif
diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp
index a59242dc..d72bd56d 100644
--- a/src/gui/gui.cpp
+++ b/src/gui/gui.cpp
@@ -37,6 +37,7 @@
#include "resources/resourcemanager.h"
#include "resources/theme.h"
+#include "utils/filesystem.h"
#include <guichan/exception.hpp>
#include <guichan/image.hpp>
@@ -55,8 +56,9 @@ gcn::Font *monoFont = nullptr;
bool Gui::debugDraw;
-Gui::Gui(Graphics *graphics)
- : mCustomCursorScale(Client::getVideo().settings().scale())
+Gui::Gui(Graphics *graphics, const std::string &themePath)
+ : mTheme(new Theme(themePath))
+ , mCustomCursorScale(Client::getVideo().settings().scale())
{
logger->log("Initializing GUI...");
// Set graphics
@@ -78,12 +80,10 @@ Gui::Gui(Graphics *graphics)
Window::setWindowContainer(guiTop);
setTop(guiTop);
- ResourceManager *resman = ResourceManager::getInstance();
-
// Set global font
const int fontSize = config.fontSize;
std::string fontFile = branding.getValue("font", "fonts/dejavusans.ttf");
- std::string path = resman->getPath(fontFile);
+ std::string path = ResourceManager::getPath(fontFile);
// Initialize the font scale before creating the fonts
TrueTypeFont::updateFontScale(graphics->getScale());
@@ -101,7 +101,7 @@ Gui::Gui(Graphics *graphics)
// Set bold font
fontFile = branding.getValue("boldFont", "fonts/dejavusans-bold.ttf");
- path = resman->getPath(fontFile);
+ path = ResourceManager::getPath(fontFile);
try
{
boldFont = new TrueTypeFont(path, fontSize);
@@ -114,7 +114,7 @@ Gui::Gui(Graphics *graphics)
// Set mono font
fontFile = branding.getValue("monoFont", "fonts/dejavusans-mono.ttf");
- path = resman->getPath(fontFile);
+ path = ResourceManager::getPath(fontFile);
try
{
monoFont = new TrueTypeFont(path, fontSize);
@@ -151,8 +151,6 @@ Gui::~Gui()
delete getTop();
delete guiInput;
-
- Theme::deleteInstance();
}
void Gui::logic()
@@ -258,7 +256,7 @@ void Gui::handleTextInput(const TextInput &textInput)
static SDL_Surface *loadSurface(const std::string &path)
{
- if (SDL_RWops *file = ResourceManager::getInstance()->open(path))
+ if (SDL_RWops *file = FS::openRWops(path))
return IMG_Load_RW(file, 1);
return nullptr;
}
@@ -270,7 +268,7 @@ void Gui::loadCustomCursors()
mCustomMouseCursors.clear();
- const std::string cursorPath = Theme::resolveThemePath("mouse.png");
+ const std::string cursorPath = mTheme->resolvePath("mouse.png");
SDL_Surface *mouseSurface = loadSurface(cursorPath);
if (!mouseSurface)
{
@@ -301,7 +299,7 @@ void Gui::loadCustomCursors()
0, targetCursorSize, targetCursorSize, 32,
rmask, gmask, bmask, amask);
- for (int i = 0; i <= static_cast<int>(Cursor::LAST); ++i)
+ for (int i = 0; i < static_cast<int>(Cursor::Count); ++i)
{
int x = i % columns * cursorSize;
int y = i / columns * cursorSize;
@@ -330,22 +328,22 @@ void Gui::loadSystemCursors()
constexpr struct {
Cursor cursor;
SDL_SystemCursor systemCursor;
- } cursors[static_cast<int>(Cursor::LAST) + 1] = {
- { Cursor::POINTER, SDL_SYSTEM_CURSOR_ARROW },
- { Cursor::RESIZE_ACROSS, SDL_SYSTEM_CURSOR_SIZEWE },
- { Cursor::RESIZE_DOWN, SDL_SYSTEM_CURSOR_SIZENS },
- { Cursor::RESIZE_DOWN_LEFT, SDL_SYSTEM_CURSOR_SIZENESW },
- { Cursor::RESIZE_DOWN_RIGHT, SDL_SYSTEM_CURSOR_SIZENWSE },
- { Cursor::FIGHT, SDL_SYSTEM_CURSOR_HAND },
- { Cursor::PICKUP, SDL_SYSTEM_CURSOR_HAND },
- { Cursor::TALK, SDL_SYSTEM_CURSOR_HAND },
- { Cursor::ACTION, SDL_SYSTEM_CURSOR_HAND },
- { Cursor::LEFT, SDL_SYSTEM_CURSOR_ARROW },
- { Cursor::UP, SDL_SYSTEM_CURSOR_ARROW },
- { Cursor::RIGHT, SDL_SYSTEM_CURSOR_ARROW },
- { Cursor::DOWN, SDL_SYSTEM_CURSOR_ARROW },
- { Cursor::DRAG, SDL_SYSTEM_CURSOR_SIZEALL },
- { Cursor::HAND, SDL_SYSTEM_CURSOR_HAND },
+ } cursors[static_cast<int>(Cursor::Count)] = {
+ { Cursor::Pointer, SDL_SYSTEM_CURSOR_ARROW },
+ { Cursor::ResizeAcross, SDL_SYSTEM_CURSOR_SIZEWE },
+ { Cursor::ResizeDown, SDL_SYSTEM_CURSOR_SIZENS },
+ { Cursor::ResizeDownLeft, SDL_SYSTEM_CURSOR_SIZENESW },
+ { Cursor::ResizeDownRight, SDL_SYSTEM_CURSOR_SIZENWSE },
+ { Cursor::Fight, SDL_SYSTEM_CURSOR_HAND },
+ { Cursor::PickUp, SDL_SYSTEM_CURSOR_HAND },
+ { Cursor::Talk, SDL_SYSTEM_CURSOR_HAND },
+ { Cursor::Action, SDL_SYSTEM_CURSOR_HAND },
+ { Cursor::Left, SDL_SYSTEM_CURSOR_ARROW },
+ { Cursor::Up, SDL_SYSTEM_CURSOR_ARROW },
+ { Cursor::Right, SDL_SYSTEM_CURSOR_ARROW },
+ { Cursor::Down, SDL_SYSTEM_CURSOR_ARROW },
+ { Cursor::Drag, SDL_SYSTEM_CURSOR_SIZEALL },
+ { Cursor::Hand, SDL_SYSTEM_CURSOR_HAND },
};
for (auto cursor : cursors)
diff --git a/src/gui/gui.h b/src/gui/gui.h
index fd1dcf94..450514f5 100644
--- a/src/gui/gui.h
+++ b/src/gui/gui.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GUI_H
-#define GUI_H
+#pragma once
#include "eventlistener.h"
#include "guichanfwd.h"
@@ -31,8 +30,10 @@
#include <SDL.h>
+#include <memory>
#include <vector>
+class Theme;
class TextInput;
class Graphics;
class SDLInput;
@@ -49,23 +50,24 @@ class SDLInput;
* Cursors are in graphic order from left to right.
* CURSOR_POINTER should be left untouched.
*/
-enum class Cursor {
- POINTER = 0,
- RESIZE_ACROSS,
- RESIZE_DOWN,
- RESIZE_DOWN_LEFT,
- RESIZE_DOWN_RIGHT,
- FIGHT,
- PICKUP,
- TALK,
- ACTION,
- LEFT,
- UP,
- RIGHT,
- DOWN,
- DRAG,
- HAND,
- LAST = HAND,
+enum class Cursor
+{
+ Pointer = 0,
+ ResizeAcross,
+ ResizeDown,
+ ResizeDownLeft,
+ ResizeDownRight,
+ Fight,
+ PickUp,
+ Talk,
+ Action,
+ Left,
+ Up,
+ Right,
+ Down,
+ Drag,
+ Hand,
+ Count,
};
/**
@@ -76,7 +78,7 @@ enum class Cursor {
class Gui final : public gcn::Gui, public EventListener
{
public:
- Gui(Graphics *screen);
+ Gui(Graphics *screen, const std::string &themePath);
~Gui() override;
@@ -121,6 +123,12 @@ class Gui final : public gcn::Gui, public EventListener
*/
void setCursorType(Cursor cursor);
+ /**
+ * The global GUI theme.
+ */
+ Theme *getTheme() const
+ { return mTheme.get(); }
+
static bool debugDraw;
protected:
@@ -133,6 +141,7 @@ class Gui final : public gcn::Gui, public EventListener
void loadCustomCursors();
void loadSystemCursors();
+ std::unique_ptr<Theme> mTheme; /**< The global GUI theme */
gcn::Font *mGuiFont; /**< The global GUI font */
gcn::Font *mInfoParticleFont; /**< Font for Info Particles*/
bool mCustomCursor = false; /**< Show custom cursor */
@@ -140,7 +149,7 @@ class Gui final : public gcn::Gui, public EventListener
std::vector<SDL_Cursor *> mSystemMouseCursors;
std::vector<SDL_Cursor *> mCustomMouseCursors;
Timer mMouseActivityTimer;
- Cursor mCursorType = Cursor::POINTER;
+ Cursor mCursorType = Cursor::Pointer;
};
extern Gui *gui; /**< The GUI system */
@@ -155,5 +164,3 @@ extern gcn::Font *boldFont;
* Monospaced text font
*/
extern gcn::Font *monoFont;
-
-#endif // GUI_H
diff --git a/src/gui/helpwindow.cpp b/src/gui/helpwindow.cpp
index 7bb31188..e0e21610 100644
--- a/src/gui/helpwindow.cpp
+++ b/src/gui/helpwindow.cpp
@@ -29,9 +29,10 @@
#include "gui/widgets/layout.h"
#include "gui/widgets/scrollarea.h"
-#include "resources/resourcemanager.h"
#include "configuration.h"
+#include "log.h"
+#include "utils/filesystem.h"
#include "utils/gettext.h"
HelpWindow::HelpWindow():
@@ -47,7 +48,6 @@ HelpWindow::HelpWindow():
setDefaultSize(500, 400, ImageRect::CENTER);
mBrowserBox = new BrowserBox;
- mBrowserBox->setFrameSize(4);
mScrollArea = new ScrollArea(mBrowserBox);
auto *okButton = new Button(_("Close"), "close", this);
@@ -88,12 +88,20 @@ void HelpWindow::loadHelp(const std::string &helpFile)
void HelpWindow::loadFile(const std::string &file)
{
- ResourceManager *resman = ResourceManager::getInstance();
std::string helpPath = branding.getStringValue("helpPath");
if (helpPath.empty())
helpPath = paths.getStringValue("help");
- const auto lines = resman->loadTextFile(helpPath + file + ".txt");
- for (auto &line : lines)
- mBrowserBox->addRow(line);
+ const std::string fileName = helpPath + file + ".txt";
+
+ size_t contentsLength;
+ char *fileContents = (char *) FS::loadFile(fileName, contentsLength);
+ if (!fileContents)
+ {
+ logger->log("Couldn't load text file: %s", fileName.c_str());
+ return;
+ }
+
+ mBrowserBox->addRows(std::string_view(fileContents, contentsLength));
+ SDL_free(fileContents);
}
diff --git a/src/gui/helpwindow.h b/src/gui/helpwindow.h
index 30fa450e..2daf2480 100644
--- a/src/gui/helpwindow.h
+++ b/src/gui/helpwindow.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef HELP_H
-#define HELP_H
+#pragma once
#include "gui/widgets/linkhandler.h"
#include "gui/widgets/window.h"
@@ -62,5 +61,3 @@ class HelpWindow : public Window, public LinkHandler,
};
extern HelpWindow *helpWindow;
-
-#endif
diff --git a/src/gui/inventorywindow.cpp b/src/gui/inventorywindow.cpp
index 048f83f5..ab2e9c86 100644
--- a/src/gui/inventorywindow.cpp
+++ b/src/gui/inventorywindow.cpp
@@ -30,7 +30,6 @@
#include "gui/itemamountwindow.h"
#include "gui/setup.h"
-#include "gui/sdlinput.h"
#include "gui/viewport.h"
#include "gui/widgets/button.h"
@@ -40,9 +39,6 @@
#include "gui/widgets/progressbar.h"
#include "gui/widgets/scrollarea.h"
-#include "net/inventoryhandler.h"
-#include "net/net.h"
-
#include "resources/iteminfo.h"
#include "resources/theme.h"
@@ -80,7 +76,7 @@ InventoryWindow::InventoryWindow(Inventory *inventory):
mItems = new ItemContainer(mInventory);
mItems->addSelectionListener(this);
- gcn::ScrollArea *invenScroll = new ScrollArea(mItems);
+ auto invenScroll = new ScrollArea(mItems);
invenScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER);
mSlotsLabel = new Label(_("Slots:"));
@@ -105,7 +101,6 @@ InventoryWindow::InventoryWindow(Inventory *inventory):
mEquipButton = new Button(_("Equip"), "equip", this);
mUseButton = new Button(_("Activate"), "activate", this);
mDropButton = new Button(_("Drop..."), "drop", this);
- mSplitButton = new Button(_("Split"), "split", this);
mOutfitButton = new Button(_("Outfits"), "outfit", this);
mWeightLabel = new Label(_("Weight:"));
@@ -121,7 +116,6 @@ InventoryWindow::InventoryWindow(Inventory *inventory):
place(0, 3, mUseButton);
place(1, 3, mEquipButton);
place(3, 3, mDropButton);
- place(4, 3, mSplitButton);
place(7, 3, mOutfitButton);
updateWeight();
@@ -182,7 +176,6 @@ void InventoryWindow::action(const gcn::ActionEvent &event)
if (!inventoryWindow->isVisible()) return;
Item *item = inventoryWindow->getSelectedItem();
-
if (!item)
return;
@@ -190,12 +183,13 @@ void InventoryWindow::action(const gcn::ActionEvent &event)
}
Item *item = mItems->getSelectedItem();
-
if (!item)
return;
if (event.getId() == "activate")
+ {
item->doEvent(Event::DoUse);
+ }
else if (event.getId() == "equip")
{
if (item->isEquippable())
@@ -214,15 +208,9 @@ void InventoryWindow::action(const gcn::ActionEvent &event)
{
ItemAmountWindow::showWindow(ItemAmountWindow::ItemDrop, this, item);
}
- else if (event.getId() == "split")
- {
- ItemAmountWindow::showWindow(ItemAmountWindow::ItemSplit, this, item,
- (item->getQuantity() - 1));
- }
else if (event.getId() == "retrieve")
{
Item *item = mItems->getSelectedItem();
-
if (!item)
return;
@@ -247,26 +235,27 @@ void InventoryWindow::mouseClicked(gcn::MouseEvent &event)
Window::mouseClicked(event);
Item *item = mItems->getSelectedItem();
+ if (!item)
+ return;
- if (event.getSource() == mItems && item && isDoubleClick(item->getInvIndex()))
+ if (event.getSource() == mItems && isDoubleClick(item->getInvIndex())
+ && isMainInventory())
{
- if (isMainInventory() && item->getInfo().activatable)
+ if (item->getInfo().activatable)
{
action(gcn::ActionEvent(mUseButton,
mUseButton->getActionEventId()));
}
- else if (isMainInventory() && item->isEquippable())
+ else if (item->isEquippable())
{
action(gcn::ActionEvent(mEquipButton,
mEquipButton->getActionEventId()));
}
+ return;
}
if (event.getButton() == gcn::MouseEvent::RIGHT)
{
- if (!item)
- return;
-
/* Convert relative to the window coordinates to absolute screen
* coordinates.
*/
@@ -279,10 +268,6 @@ void InventoryWindow::mouseClicked(gcn::MouseEvent &event)
{
if (instances.size() > 1 && keyboard.isKeyActive(KeyboardConfig::KEY_EMOTE))
{
- Item *item = mItems->getSelectedItem();
-
- if(!item)
- return;
if (mInventory->isMainInventory())
{
Event event(Event::DoMove);
@@ -305,32 +290,10 @@ void InventoryWindow::mouseClicked(gcn::MouseEvent &event)
}
}
-void InventoryWindow::keyPressed(gcn::KeyEvent &event)
-{
- switch (event.getKey().getValue())
- {
- case Key::LEFT_SHIFT:
- case Key::RIGHT_SHIFT:
- mSplit = true;
- break;
- }
-}
-
void InventoryWindow::keyReleased(gcn::KeyEvent &event)
{
if (isInputFocused())
- {
mItems->setFilter(mFilterText->getText());
- return;
- }
-
- switch (event.getKey().getValue())
- {
- case Key::LEFT_SHIFT:
- case Key::RIGHT_SHIFT:
- mSplit = false;
- break;
- }
}
void InventoryWindow::valueChanged(const gcn::SelectionEvent &event)
@@ -338,15 +301,6 @@ void InventoryWindow::valueChanged(const gcn::SelectionEvent &event)
if (!mInventory->isMainInventory())
return;
- Item *item = mItems->getSelectedItem();
-
- if (mSplit && Net::getInventoryHandler()->
- canSplit(mItems->getSelectedItem()) && item)
- {
- ItemAmountWindow::showWindow(ItemAmountWindow::ItemSplit, this, item,
- (item->getQuantity() - 1));
- }
-
updateButtons();
}
@@ -359,44 +313,19 @@ void InventoryWindow::updateButtons()
mUseButton->setEnabled(false);
mEquipButton->setEnabled(false);
mDropButton->setEnabled(false);
- mSplitButton->setEnabled(false);
-
return;
}
mDropButton->setEnabled(true);
- if (item->isEquippable())
- {
- if (item->isEquipped())
- mEquipButton->setCaption(_("Unequip"));
- else
- mEquipButton->setCaption(_("Equip"));
- mEquipButton->setEnabled(true);
- }
- else
- mEquipButton->setEnabled(false);
-
+ mEquipButton->setCaption(item->isEquipped() ? _("Unequip") : _("Equip"));
+ mEquipButton->setEnabled(item->isEquippable());
mEquipButton->adjustSize();
mUseButton->setEnabled(item->getInfo().activatable);
- if (item->getQuantity() > 1)
- mDropButton->setCaption(_("Drop..."));
- else
- mDropButton->setCaption(_("Drop"));
-
- if (Net::getInventoryHandler()->canSplit(item))
- mSplitButton->setEnabled(true);
- else
- mSplitButton->setEnabled(false);
-
- mSplitButton->adjustSize();
-}
-
-void InventoryWindow::setSplitAllowed(bool allowed)
-{
- mSplitButton->setVisible(allowed);
+ mDropButton->setCaption(item->getQuantity() > 1 ? _("Drop...") : _("Drop"));
+ mDropButton->adjustSize();
}
void InventoryWindow::close()
@@ -418,12 +347,9 @@ void InventoryWindow::event(Event::Channel channel, const Event &event)
{
if (event.getType() == Event::UpdateAttribute)
{
- int id = event.getInt("id");
- if (id == TOTAL_WEIGHT ||
- id == MAX_WEIGHT)
- {
+ const int id = event.getInt("id");
+ if (id == TOTAL_WEIGHT || id == MAX_WEIGHT)
updateWeight();
- }
}
}
@@ -432,8 +358,8 @@ void InventoryWindow::updateWeight()
if (!isMainInventory())
return;
- int total = PlayerInfo::getAttribute(TOTAL_WEIGHT);
- int max = PlayerInfo::getAttribute(MAX_WEIGHT);
+ const int total = PlayerInfo::getAttribute(TOTAL_WEIGHT);
+ const int max = PlayerInfo::getAttribute(MAX_WEIGHT);
if (max <= 0)
return;
@@ -451,21 +377,14 @@ bool InventoryWindow::isInputFocused() const
bool InventoryWindow::isAnyInputFocused()
{
- auto it = instances.begin();
- auto it_end = instances.end();
-
- for (; it != it_end; it++)
- {
- if ((*it)->isInputFocused())
- {
+ for (auto instance : instances)
+ if (instance->isInputFocused())
return true;
- }
- }
return false;
}
-void InventoryWindow::slotsChanged(Inventory* inventory)
+void InventoryWindow::slotsChanged(Inventory *inventory)
{
if (inventory == mInventory)
{
@@ -473,7 +392,6 @@ void InventoryWindow::slotsChanged(Inventory* inventory)
const int maxSlots = mInventory->getSize();
mSlotsBar->setProgress((float) usedSlots / maxSlots);
-
mSlotsBar->setText(strprintf("%d/%d", usedSlots, maxSlots));
}
}
diff --git a/src/gui/inventorywindow.h b/src/gui/inventorywindow.h
index 048b229c..267d8dc1 100644
--- a/src/gui/inventorywindow.h
+++ b/src/gui/inventorywindow.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef INVENTORYWINDOW_H
-#define INVENTORYWINDOW_H
+#pragma once
#include "inventory.h"
#include "eventlistener.h"
@@ -28,8 +27,6 @@
#include "gui/widgets/window.h"
#include "gui/widgets/textfield.h"
-#include "net/inventoryhandler.h"
-
#include <guichan/actionlistener.hpp>
#include <guichan/keylistener.hpp>
#include <guichan/selectionlistener.hpp>
@@ -44,12 +41,12 @@ class TextBox;
*
* \ingroup Interface
*/
-class InventoryWindow : public Window,
- public gcn::ActionListener,
- public gcn::KeyListener,
- public gcn::SelectionListener,
- public InventoryListener,
- public EventListener
+class InventoryWindow final : public Window,
+ public gcn::ActionListener,
+ public gcn::KeyListener,
+ public gcn::SelectionListener,
+ public InventoryListener,
+ public EventListener
{
public:
InventoryWindow(Inventory *inventory);
@@ -64,7 +61,7 @@ class InventoryWindow : public Window,
/**
* Returns the selected item.
*/
- Item* getSelectedItem() const;
+ Item *getSelectedItem() const;
/**
* Handles closing of the window
@@ -77,11 +74,6 @@ class InventoryWindow : public Window,
void mouseClicked(gcn::MouseEvent &event) override;
/**
- * Handles the key presses.
- */
- void keyPressed(gcn::KeyEvent &event) override;
-
- /**
* Handles the key releases.
*/
void keyReleased(gcn::KeyEvent &event) override;
@@ -92,11 +84,6 @@ class InventoryWindow : public Window,
void valueChanged(const gcn::SelectionEvent &event) override;
/**
- * Sets whether the split button should be shown.
- */
- void setSplitAllowed(bool allowed);
-
- /**
* Closes the Storage Window, as well as telling the server that the
* window has been closed.
*/
@@ -111,9 +98,9 @@ class InventoryWindow : public Window,
static bool isAnyInputFocused();
- void slotsChanged(Inventory* inventory) override;
+ void slotsChanged(Inventory *inventory) override;
- bool isMainInventory() { return mInventory->isMainInventory(); }
+ bool isMainInventory() const { return mInventory->isMainInventory(); }
void event(Event::Channel channel, const Event &event) override;
@@ -134,16 +121,12 @@ class InventoryWindow : public Window,
std::string mWeight, mSlots;
- gcn::Button *mUseButton, *mEquipButton, *mDropButton, *mSplitButton,
+ gcn::Button *mUseButton, *mEquipButton, *mDropButton,
*mOutfitButton, *mStoreButton, *mRetrieveButton;
gcn::Label *mWeightLabel, *mSlotsLabel, *mFilterLabel;
ProgressBar *mWeightBar, *mSlotsBar;
-
- bool mSplit = false;
};
extern InventoryWindow *inventoryWindow;
-
-#endif
diff --git a/src/gui/itemamountwindow.cpp b/src/gui/itemamountwindow.cpp
index 947b5bdc..da5dc073 100644
--- a/src/gui/itemamountwindow.cpp
+++ b/src/gui/itemamountwindow.cpp
@@ -49,9 +49,6 @@ void ItemAmountWindow::finish(Item *item, int amount, Usage usage)
case ItemDrop:
item->doEvent(Event::DoDrop, amount);
break;
- case ItemSplit:
- item->doEvent(Event::DoSplit, amount);
- break;
case StoreAdd:
{
Event event(Event::DoMove);
@@ -148,9 +145,6 @@ ItemAmountWindow::ItemAmountWindow(Usage usage, Window *parent, Item *item,
case StoreRemove:
setCaption(_("Select amount of items to retrieve."));
break;
- case ItemSplit:
- setCaption(_("Select amount of items to split."));
- break;
}
setLocationRelativeTo(getParentWindow());
diff --git a/src/gui/itemamountwindow.h b/src/gui/itemamountwindow.h
index d0ac52d5..489fdbef 100644
--- a/src/gui/itemamountwindow.h
+++ b/src/gui/itemamountwindow.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef ITEM_AMOUNT_WINDOW_H
-#define ITEM_AMOUNT_WINDOW_H
+#pragma once
#include "gui/widgets/window.h"
@@ -33,7 +32,7 @@ class ItemPopup;
class Icon;
/**
- * Window used for selecting the amount of items to drop, trade or split.
+ * Window used for selecting the amount of items to drop, trade or store.
*
* \ingroup Interface
*/
@@ -47,7 +46,6 @@ class ItemAmountWindow : public Window,
ItemDrop,
StoreAdd,
StoreRemove,
- ItemSplit
};
/**
@@ -100,5 +98,3 @@ class ItemAmountWindow : public Window,
bool mEnabledKeyboard;
};
-
-#endif // ITEM_AMOUNT_WINDOW_H
diff --git a/src/gui/itempopup.cpp b/src/gui/itempopup.cpp
index 7f1dec3b..35951331 100644
--- a/src/gui/itempopup.cpp
+++ b/src/gui/itempopup.cpp
@@ -23,7 +23,6 @@
#include "gui/itempopup.h"
#include "configuration.h"
-#include "graphics.h"
#include "units.h"
#include "gui/gui.h"
@@ -35,7 +34,6 @@
#include "utils/gettext.h"
#include "utils/stringutils.h"
-#include "resources/image.h"
#include "resources/resourcemanager.h"
#include "resources/theme.h"
@@ -139,6 +137,7 @@ void ItemPopup::setNoItem()
mItemDesc->setText(std::string());
mItemEffect->setText(std::string());
+ mItemWeight->setText(std::string());
setContentSize(mItemName->getWidth(), mItemName->getHeight());
}
@@ -153,8 +152,8 @@ void ItemPopup::setItem(const ItemInfo &item, bool showImage)
if (showImage)
{
ResourceManager *resman = ResourceManager::getInstance();
- auto image = resman->getImageRef(paths.getStringValue("itemIcons") +
- item.display.image);
+ auto image = resman->getImage(paths.getStringValue("itemIcons") +
+ item.display.image);
mIcon->setImage(image);
if (image)
diff --git a/src/gui/itempopup.h b/src/gui/itempopup.h
index 3b213633..535104cf 100644
--- a/src/gui/itempopup.h
+++ b/src/gui/itempopup.h
@@ -20,8 +20,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef ITEMPOPUP_H
-#define ITEMPOPUP_H
+#pragma once
#include "gui/widgets/popup.h"
@@ -74,5 +73,3 @@ class ItemPopup : public Popup
ItemType mItemType;
Icon *mIcon;
};
-
-#endif // ITEMPOPUP_H
diff --git a/src/gui/logindialog.cpp b/src/gui/logindialog.cpp
index 1f96e02d..42ec0842 100644
--- a/src/gui/logindialog.cpp
+++ b/src/gui/logindialog.cpp
@@ -64,8 +64,8 @@ LoginDialog::LoginDialog(LoginData *loginData):
place(0, 0, userLabel);
place(0, 1, passLabel);
- place(1, 0, mUserField, 3).setPadding(1);
- place(1, 1, mPassField, 3).setPadding(1);
+ place(1, 0, mUserField, 3).setPadding(2);
+ place(1, 1, mPassField, 3).setPadding(2);
place(0, 5, mKeepCheck, 4);
place(0, 6, mRegisterButton).setHAlign(LayoutCell::LEFT);
place(2, 6, mServerButton);
diff --git a/src/gui/logindialog.h b/src/gui/logindialog.h
index 67814cd4..fc3fa249 100644
--- a/src/gui/logindialog.h
+++ b/src/gui/logindialog.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef LOGIN_H
-#define LOGIN_H
+#pragma once
#include "gui/widgets/window.h"
@@ -69,5 +68,3 @@ class LoginDialog : public Window, public gcn::ActionListener,
LoginData *mLoginData;
};
-
-#endif
diff --git a/src/gui/minimap.cpp b/src/gui/minimap.cpp
index f5206eda..8924bc26 100644
--- a/src/gui/minimap.cpp
+++ b/src/gui/minimap.cpp
@@ -34,8 +34,10 @@
#include "resources/resourcemanager.h"
#include "resources/userpalette.h"
+#include "utils/filesystem.h"
#include "utils/gettext.h"
+#include <algorithm>
#include <guichan/font.hpp>
Minimap::Minimap():
@@ -58,8 +60,7 @@ Minimap::Minimap():
setVisible(config.showMinimap, isSticky());
}
-Minimap::~Minimap()
-{}
+Minimap::~Minimap() = default;
void Minimap::setMap(Map *map)
{
@@ -86,11 +87,11 @@ void Minimap::setMap(Map *map)
std::string minimapName = map->getProperty("minimap");
- if (minimapName.empty() && resman->exists(tempname))
+ if (minimapName.empty() && FS::exists(tempname))
minimapName = tempname;
if (!minimapName.empty())
- mMapImage = resman->getImageRef(minimapName);
+ mMapImage = resman->getImage(minimapName);
}
if (mMapImage)
@@ -136,40 +137,38 @@ void Minimap::draw(gcn::Graphics *graphics)
{
Window::draw(graphics);
+ if (!mMap)
+ return;
+
+ auto g = static_cast<Graphics*>(graphics);
const gcn::Rectangle a = getChildrenArea();
- graphics->pushClipArea(a);
+ g->pushClipRect(a); // does actual clipping
+ g->pushClipArea(a); // only applies an offset
+
+ const int tileWidth = mMap->getTileWidth();
+ const int tileHeight = mMap->getTileHeight();
int mapOriginX = 0;
int mapOriginY = 0;
- if (mMapImage && mMap)
+ if (mMapImage)
{
if (mMapImage->getWidth() > a.width ||
mMapImage->getHeight() > a.height)
{
const Vector &p = local_player->getPosition();
- mapOriginX = (int) (((a.width) / 2) - (int) (p.x * mWidthProportion)
- / mMap->getTileWidth());
- mapOriginY = (int) (((a.height) / 2)
- - (int) (p.y * mHeightProportion)
- / mMap->getTileHeight());
+ mapOriginX = (a.width / 2) - (int) (p.x * mWidthProportion) / tileWidth;
+ mapOriginY = (a.height / 2) - (int) (p.y * mHeightProportion) / tileHeight;
const int minOriginX = a.width - mMapImage->getWidth();
const int minOriginY = a.height - mMapImage->getHeight();
- if (mapOriginX < minOriginX)
- mapOriginX = minOriginX;
- if (mapOriginY < minOriginY)
- mapOriginY = minOriginY;
- if (mapOriginX > 0)
- mapOriginX = 0;
- if (mapOriginY > 0)
- mapOriginY = 0;
+ mapOriginX = std::clamp(mapOriginX, minOriginX, 0);
+ mapOriginY = std::clamp(mapOriginY, minOriginY, 0);
}
- static_cast<Graphics*>(graphics)->
- drawImage(mMapImage, mapOriginX, mapOriginY);
+ g->drawImage(mMapImage, mapOriginX, mapOriginY);
}
for (auto actor : actorSpriteManager->getAll())
@@ -218,16 +217,14 @@ void Minimap::draw(gcn::Graphics *graphics)
const int offsetWidth = (int) ((dotSize - 1) * mWidthProportion);
const Vector &pos = being->getPosition();
- if (mMap)
- {
- graphics->fillRectangle(gcn::Rectangle(
- (int) (pos.x * mWidthProportion) / mMap->getTileWidth()
- + mapOriginX - offsetWidth,
- (int) (pos.y * mHeightProportion) / mMap->getTileHeight()
- + mapOriginY - offsetHeight,
- dotSize, dotSize));
- }
+ g->fillRectangle(
+ gcn::Rectangle((int) (pos.x * mWidthProportion) / tileWidth + mapOriginX - offsetWidth,
+ (int) (pos.y * mHeightProportion) / tileHeight + mapOriginY
+ - offsetHeight,
+ dotSize,
+ dotSize));
}
- graphics->popClipArea();
+ g->popClipArea();
+ g->popClipRect();
}
diff --git a/src/gui/minimap.h b/src/gui/minimap.h
index 8212f5b7..bf6cd89d 100644
--- a/src/gui/minimap.h
+++ b/src/gui/minimap.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef MINIMAP_H
-#define MINIMAP_H
+#pragma once
#include "gui/widgets/window.h"
@@ -67,5 +66,3 @@ class Minimap : public Window
};
extern Minimap *minimap;
-
-#endif
diff --git a/src/gui/ministatuswindow.cpp b/src/gui/ministatuswindow.cpp
index 598cb722..9d695954 100644
--- a/src/gui/ministatuswindow.cpp
+++ b/src/gui/ministatuswindow.cpp
@@ -21,10 +21,11 @@
#include "gui/ministatuswindow.h"
-#include "animatedsprite.h"
#include "configuration.h"
+#include "game.h"
#include "graphics.h"
#include "playerinfo.h"
+#include "sprite.h"
#include "statuseffect.h"
#include "gui/gui.h"
@@ -39,16 +40,22 @@
#include "net/tmwa/protocol.h"
+#include "resources/statuseffectdb.h"
#include "resources/theme.h"
#include "utils/gettext.h"
#include "utils/stringutils.h"
#include "utils/time.h"
+#include <algorithm>
+
+static constexpr int ICON_SPACING = 3;
+
MiniStatusWindow::MiniStatusWindow():
Popup("MiniStatus")
{
setPadding(3);
+ setMinHeight(0);
listen(Event::AttributesChannel);
listen(Event::ActorSpriteChannel);
@@ -83,8 +90,7 @@ MiniStatusWindow::MiniStatusWindow():
add(mMpBar);
add(mXpBar);
- setContentSize(mXpBar->getX() + mXpBar->getWidth(),
- mXpBar->getY() + mXpBar->getHeight());
+ updateSize();
auto stateIt = config.windows.find(getPopupName());
setVisible(stateIt != config.windows.end() ? stateIt->second.visible.value_or(true)
@@ -95,36 +101,27 @@ MiniStatusWindow::MiniStatusWindow():
addMouseListener(this);
}
-void MiniStatusWindow::setIcon(int index, AnimatedSprite *sprite)
-{
- if (index >= (int) mIcons.size())
- mIcons.resize(index + 1);
-
- delete mIcons[index];
- mIcons[index] = sprite;
-}
-
-void MiniStatusWindow::eraseIcon(int index)
-{
- mIcons.erase(mIcons.begin() + index);
-}
+MiniStatusWindow::~MiniStatusWindow() = default;
void MiniStatusWindow::drawIcons(Graphics *graphics)
{
- // Draw icons
- int icon_x = mXpBar->getX() + mXpBar->getWidth() + 14;
- for (auto &icon : mIcons)
+ const auto game = Game::instance();
+ const int tileWidth = game->getCurrentTileWidth();
+ const int tileHeight = game->getCurrentTileHeight();
+
+ int iconX = mXpBar->getX() + mXpBar->getWidth() + ICON_SPACING + tileWidth / 2;
+ int iconY = ICON_SPACING + tileHeight;
+
+ for (auto &icon : mStatusIcons)
{
- if (icon)
- {
- icon->draw(graphics, icon_x, 3);
- icon_x += 2 + icon->getWidth();
- }
+ icon.sprite->draw(graphics,
+ iconX - icon.sprite->getWidth() / 2,
+ iconY - icon.sprite->getHeight());
+ iconX += ICON_SPACING + icon.sprite->getWidth();
}
}
-void MiniStatusWindow::event(Event::Channel channel,
- const Event &event)
+void MiniStatusWindow::event(Event::Channel channel, const Event &event)
{
if (channel == Event::AttributesChannel)
{
@@ -146,7 +143,7 @@ void MiniStatusWindow::event(Event::Channel channel,
}
if (event.getType() == Event::UpdateStat)
{
- if (Net::getNetworkType() == ServerType::TMWATHENA &&
+ if (Net::getNetworkType() == ServerType::TmwAthena &&
event.getInt("id") == TmwAthena::MATK)
{
StatusWindow::updateMPBar(mMpBar);
@@ -157,54 +154,28 @@ void MiniStatusWindow::event(Event::Channel channel,
{
if (event.getType() == Event::UpdateStatusEffect)
{
- int index = event.getInt("index");
- bool newStatus = event.getBool("newStatus");
+ const int id = event.getInt("index");
+ const bool newStatus = event.getBool("newStatus");
- StatusEffect *effect = StatusEffect::getStatusEffect(index,
- newStatus);
+ auto effect = StatusEffectDB::getStatusEffect(id);
+ if (!effect)
+ return;
- if (effect)
- {
- effect->deliverMessage();
- effect->playSFX();
-
- AnimatedSprite *sprite = effect->getIcon();
-
- if (!sprite)
- {
- // delete sprite, if necessary
- for (unsigned int i = 0; i < mStatusEffectIcons.size();)
- if (mStatusEffectIcons[i] == index)
- {
- mStatusEffectIcons.erase(mStatusEffectIcons.begin()
- + i);
- miniStatusWindow->eraseIcon(i);
- }
- else
- i++;
- }
- else
- {
- // replace sprite or append
- bool found = false;
-
- for (unsigned int i = 0; i < mStatusEffectIcons.size();
- i++)
- if (mStatusEffectIcons[i] == index)
- {
- miniStatusWindow->setIcon(i, sprite);
- found = true;
- break;
- }
-
- if (!found)
- { // add new
- int offset = mStatusEffectIcons.size();
- miniStatusWindow->setIcon(offset, sprite);
- mStatusEffectIcons.push_back(index);
- }
- }
- }
+ effect->deliverMessage(newStatus);
+ effect->playSfx(newStatus);
+
+ Sprite *sprite = newStatus ? effect->getIconSprite() : nullptr;
+ auto it = std::find_if(mStatusIcons.begin(), mStatusIcons.end(),
+ [id](const StatusIcon &icon) {
+ return icon.effectId == id;
+ });
+
+ if (!sprite && it != mStatusIcons.end())
+ mStatusIcons.erase(it);
+ else if (sprite && it == mStatusIcons.end())
+ mStatusIcons.push_back(StatusIcon{id, std::unique_ptr<Sprite>(sprite)});
+
+ updateSize();
}
}
}
@@ -226,43 +197,84 @@ void MiniStatusWindow::logic()
}
*/
- for (auto &icon : mIcons)
- if (icon)
- icon->update(Time::absoluteTimeMs());
+ for (auto &icon : mStatusIcons)
+ icon.sprite->update(Time::deltaTimeMs());
+}
+
+void MiniStatusWindow::draw(gcn::Graphics *graphics)
+{
+ drawChildren(graphics);
+
+ drawIcons(static_cast<Graphics*>(graphics));
}
void MiniStatusWindow::mouseMoved(gcn::MouseEvent &event)
{
Popup::mouseMoved(event);
- const int x = event.getX();
- const int y = event.getY();
+ std::string tooltip1;
+ std::string tooltip2;
if (event.getSource() == mXpBar)
{
- mTextPopup->show(x + getX(), y + getY(),
- strprintf("%u/%u", PlayerInfo::getAttribute(EXP),
- PlayerInfo::getAttribute(EXP_NEEDED)),
- strprintf("%s: %u", _("Need"),
- PlayerInfo::getAttribute(EXP_NEEDED)
- - PlayerInfo::getAttribute(EXP)));
+ const int xp = PlayerInfo::getAttribute(EXP);
+ const int xpNeeded = PlayerInfo::getAttribute(EXP_NEEDED);
+ tooltip1 = strprintf("%u/%u", xp, xpNeeded);
+ tooltip2 = strprintf("%s: %u", _("Need"), xpNeeded - xp);
}
else if (event.getSource() == mHpBar)
{
- mTextPopup->show(x + getX(), y + getY(),
- strprintf("%u/%u", PlayerInfo::getAttribute(HP),
- PlayerInfo::getAttribute(MAX_HP)));
+ const int hp = PlayerInfo::getAttribute(HP);
+ const int maxHp = PlayerInfo::getAttribute(MAX_HP);
+ tooltip1 = strprintf("%u/%u", hp, maxHp);
}
else if (event.getSource() == mMpBar)
{
- mTextPopup->show(x + getX(), y + getY(),
- strprintf("%u/%u", PlayerInfo::getAttribute(MP),
- PlayerInfo::getAttribute(MAX_MP)));
+ const int mp = PlayerInfo::getAttribute(MP);
+ const int maxMp = PlayerInfo::getAttribute(MAX_MP);
+ tooltip1 = strprintf("%u/%u", mp, maxMp);
}
else
{
+ // Check if the mouse is over one of the status icons
+ const auto game = Game::instance();
+ const int tileWidth = game->getCurrentTileWidth();
+ const int tileHeight = game->getCurrentTileHeight();
+
+ int iconX = mXpBar->getX() + mXpBar->getWidth() + ICON_SPACING + tileWidth / 2;
+ int iconY = ICON_SPACING + tileHeight;
+
+ for (const auto &icon : mStatusIcons)
+ {
+ int spriteX = iconX + icon.sprite->getOffsetX() - icon.sprite->getWidth() / 2;
+ int spriteY = iconY + icon.sprite->getOffsetY() - icon.sprite->getHeight();
+
+ if (event.getX() >= spriteX &&
+ event.getX() < spriteX + icon.sprite->getWidth() &&
+ event.getY() >= spriteY &&
+ event.getY() < spriteY + icon.sprite->getHeight())
+ {
+ auto effect = StatusEffectDB::getStatusEffect(icon.effectId);
+ if (effect)
+ tooltip1 = effect->name;
+ break;
+ }
+
+ iconX += ICON_SPACING + icon.sprite->getWidth();
+ }
+ }
+
+ if (tooltip1.empty())
+ {
mTextPopup->setVisible(false);
}
+ else
+ {
+ mTextPopup->show(event.getX() + getX(),
+ event.getY() + getY(),
+ tooltip1,
+ tooltip2);
+ }
}
void MiniStatusWindow::mouseExited(gcn::MouseEvent &event)
@@ -271,3 +283,19 @@ void MiniStatusWindow::mouseExited(gcn::MouseEvent &event)
mTextPopup->setVisible(false);
}
+
+void MiniStatusWindow::updateSize()
+{
+ int width = mXpBar->getX() + mXpBar->getWidth();
+ int height = mXpBar->getY() + mXpBar->getHeight();
+
+ // Increase width based on the size of the status icons
+ if (!mStatusIcons.empty())
+ {
+ width += ICON_SPACING;
+ for (const auto &icon : mStatusIcons)
+ width += ICON_SPACING + icon.sprite->getWidth();
+ }
+
+ setContentSize(width, height);
+}
diff --git a/src/gui/ministatuswindow.h b/src/gui/ministatuswindow.h
index db9e4c69..21c4b76e 100644
--- a/src/gui/ministatuswindow.h
+++ b/src/gui/ministatuswindow.h
@@ -19,16 +19,16 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef MINISTATUS_H
-#define MINISTATUS_H
+#pragma once
#include "eventlistener.h"
#include "gui/widgets/popup.h"
+#include <memory>
#include <vector>
-class AnimatedSprite;
+class Sprite;
class Graphics;
class ProgressBar;
class TextPopup;
@@ -42,28 +42,20 @@ class MiniStatusWindow : public Popup, public EventListener
{
public:
MiniStatusWindow();
-
- void drawIcons(Graphics *graphics);
+ ~MiniStatusWindow() override;
void event(Event::Channel channel, const Event &event) override;
void logic() override; // Updates icons
- void draw(gcn::Graphics *graphics) override
- { drawChildren(graphics); }
+ void draw(gcn::Graphics *graphics) override;
void mouseMoved(gcn::MouseEvent &mouseEvent) override;
void mouseExited(gcn::MouseEvent &event) override;
private:
- bool isInBar(ProgressBar *bar, int x, int y) const;
-
- /**
- * Sets one of the icons.
- */
- void setIcon(int index, AnimatedSprite *sprite);
-
- void eraseIcon(int index);
+ void drawIcons(Graphics *graphics);
+ void updateSize();
/*
* Mini Status Bars
@@ -73,10 +65,13 @@ class MiniStatusWindow : public Popup, public EventListener
ProgressBar *mXpBar;
TextPopup *mTextPopup;
- std::vector<int> mStatusEffectIcons;
- std::vector<AnimatedSprite *> mIcons;
+ struct StatusIcon
+ {
+ int effectId;
+ std::unique_ptr<Sprite> sprite;
+ };
+
+ std::vector<StatusIcon> mStatusIcons;
};
extern MiniStatusWindow *miniStatusWindow;
-
-#endif
diff --git a/src/gui/npcdialog.cpp b/src/gui/npcdialog.cpp
index 18b3ff1b..e2e7b040 100644
--- a/src/gui/npcdialog.cpp
+++ b/src/gui/npcdialog.cpp
@@ -87,8 +87,8 @@ NpcDialog::NpcDialog(int npcId)
// Setup output text box
mTextBox = new BrowserBox(BrowserBox::AUTO_WRAP);
mTextBox->setWrapIndent(15);
- mTextBox->setFrameSize(2);
mTextBox->setLinkHandler(mItemLinkHandler.get());
+ mTextBox->setEnableKeys(true);
mScrollArea = new ScrollArea(mTextBox);
mScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER);
diff --git a/src/gui/npcdialog.h b/src/gui/npcdialog.h
index f2b50370..8a18a455 100644
--- a/src/gui/npcdialog.h
+++ b/src/gui/npcdialog.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NPCDIALOG_H
-#define NPCDIALOG_H
+#pragma once
#include "gui/widgets/window.h"
@@ -214,5 +213,3 @@ class NpcDialog final : public Window,
NpcInputState mInputState = NPC_INPUT_NONE;
NpcActionState mActionState = NPC_ACTION_WAIT;
};
-
-#endif // NPCDIALOG_H
diff --git a/src/gui/npcpostdialog.h b/src/gui/npcpostdialog.h
index 61c17ca6..7021b5b4 100644
--- a/src/gui/npcpostdialog.h
+++ b/src/gui/npcpostdialog.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GUI_NPCPOSTDIALOG_H
-#define GUI_NPCPOSTDIALOG_H
+#pragma once
#include "gui/widgets/window.h"
@@ -57,5 +56,3 @@ private:
TextBox *mText;
TextField *mSender;
};
-
-#endif
diff --git a/src/gui/okdialog.h b/src/gui/okdialog.h
index fcb327a9..f56f24e2 100644
--- a/src/gui/okdialog.h
+++ b/src/gui/okdialog.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef OK_DIALOG_H
-#define OK_DIALOG_H
+#pragma once
#include "gui/widgets/window.h"
@@ -47,5 +46,3 @@ class OkDialog : public Window, public gcn::ActionListener
private:
TextBox *mTextBox;
};
-
-#endif // OK_DIALOG_H
diff --git a/src/gui/outfitwindow.cpp b/src/gui/outfitwindow.cpp
index b5033d70..f914c0cc 100644
--- a/src/gui/outfitwindow.cpp
+++ b/src/gui/outfitwindow.cpp
@@ -78,8 +78,8 @@ OutfitWindow::~OutfitWindow()
void OutfitWindow::load()
{
- for (int o = 0; o < OUTFITS_COUNT; o++)
- memset(mOutfits[o].items, -1, sizeof(mOutfits[o].items));
+ for (auto &mOutfit : mOutfits)
+ memset(mOutfit.items, -1, sizeof(mOutfit.items));
for (auto &outfit : config.outfits)
{
@@ -89,10 +89,8 @@ void OutfitWindow::load()
std::string buf;
std::stringstream ss(outfit.items);
- for (size_t i = 0; (ss >> buf) && i < OUTFIT_ITEM_COUNT; i++)
- {
+ for (int i = 0; (ss >> buf) && i < OUTFIT_ITEM_COUNT; i++)
mOutfits[outfit.index].items[i] = atoi(buf.c_str());
- }
mOutfits[outfit.index].unequip = outfit.unequip;
}
@@ -105,16 +103,15 @@ void OutfitWindow::save()
std::string outfitStr;
for (int o = 0; o < OUTFITS_COUNT; o++)
{
- auto &items = mOutfits[o].items;
bool emptyOutfit = true;
- for (int i = 0; i < OUTFIT_ITEM_COUNT; i++)
+ for (int item : mOutfits[o].items)
{
if (!outfitStr.empty())
outfitStr += " ";
- outfitStr += items[i] ? toString(items[i]) : toString(-1);
- emptyOutfit &= items[i] <= 0;
+ outfitStr += item ? toString(item) : toString(-1);
+ emptyOutfit &= item <= 0;
}
if (!emptyOutfit)
@@ -159,10 +156,9 @@ void OutfitWindow::wearOutfit(int outfit)
if (mOutfits[outfit].unequip)
unequipNotInOutfit(outfit);
- Item *item;
- for (int i = 0; i < OUTFIT_ITEM_COUNT; i++)
+ for (int i : mOutfits[outfit].items)
{
- item = PlayerInfo::getInventory()->findItem(mOutfits[outfit].items[i]);
+ Item *item = PlayerInfo::getInventory()->findItem(i);
if (item && !item->isEquipped() && item->getQuantity())
{
if (item->isEquippable())
@@ -174,9 +170,7 @@ void OutfitWindow::wearOutfit(int outfit)
void OutfitWindow::copyOutfit(int outfit)
{
for (int i = 0; i < OUTFIT_ITEM_COUNT; i++)
- {
mOutfits[mCurrentOutfit].items[i] = mOutfits[outfit].items[i];
- }
}
void OutfitWindow::draw(gcn::Graphics *graphics)
@@ -328,9 +322,9 @@ void OutfitWindow::unequipNotInOutfit(int outfit)
if (inventory->getItem(i) && inventory->getItem(i)->isEquipped())
{
bool found = false;
- for (int f = 0; f < OUTFIT_ITEM_COUNT; f++)
+ for (int item : mOutfits[outfit].items)
{
- if (inventory->getItem(i)->getId() == mOutfits[outfit].items[f])
+ if (inventory->getItem(i)->getId() == item)
{
found = true;
break;
diff --git a/src/gui/outfitwindow.h b/src/gui/outfitwindow.h
index 56e96795..10de5321 100644
--- a/src/gui/outfitwindow.h
+++ b/src/gui/outfitwindow.h
@@ -19,15 +19,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef OUTFITWINDOW_H
-#define OUTFITWINDOW_H
+#pragma once
#include "gui/widgets/window.h"
#include <guichan/actionlistener.hpp>
-#define OUTFITS_COUNT 15
-#define OUTFIT_ITEM_COUNT 9
+constexpr int OUTFITS_COUNT = 15;
+constexpr int OUTFIT_ITEM_COUNT = 9;
class Button;
class CheckBox;
@@ -91,5 +90,3 @@ class OutfitWindow : public Window, gcn::ActionListener
};
extern OutfitWindow *outfitWindow;
-
-#endif
diff --git a/src/gui/palette.h b/src/gui/palette.h
index 9de911d5..145a93ac 100644
--- a/src/gui/palette.h
+++ b/src/gui/palette.h
@@ -20,8 +20,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef PALETTE_H
-#define PALETTE_H
+#pragma once
#include "utils/time.h"
@@ -169,5 +168,3 @@ class Palette
std::vector<ColorElem> mColors;
std::vector<ColorElem*> mGradVector;
};
-
-#endif
diff --git a/src/gui/popupmenu.cpp b/src/gui/popupmenu.cpp
index b1165f27..4bafc074 100644
--- a/src/gui/popupmenu.cpp
+++ b/src/gui/popupmenu.cpp
@@ -91,12 +91,12 @@ void PopupMenu::showPopup(int x, int y, Being *being)
switch (player_relations.getRelation(name))
{
- case PlayerRelation::NEUTRAL:
+ case PlayerRelation::Neutral:
mBrowserBox->addRow(strprintf("@@friend|%s@@",
strprintf(_("Befriend %s"),
name.c_str()).c_str()));
- case PlayerRelation::FRIEND:
+ case PlayerRelation::Friend:
mBrowserBox->addRow(strprintf("@@disregard|%s@@",
strprintf(_("Disregard %s"),
name.c_str()).c_str()));
@@ -105,7 +105,7 @@ void PopupMenu::showPopup(int x, int y, Being *being)
name.c_str()).c_str()));
break;
- case PlayerRelation::DISREGARDED:
+ case PlayerRelation::Disregarded:
mBrowserBox->addRow(strprintf("@@unignore|%s@@",
strprintf(_("Unignore %s"),
name.c_str()).c_str()));
@@ -114,7 +114,7 @@ void PopupMenu::showPopup(int x, int y, Being *being)
name.c_str()).c_str()));
break;
- case PlayerRelation::IGNORED:
+ case PlayerRelation::Ignored:
mBrowserBox->addRow(strprintf("@@unignore|%s@@",
strprintf(_("Unignore %s"),
name.c_str()).c_str()));
@@ -126,7 +126,7 @@ void PopupMenu::showPopup(int x, int y, Being *being)
strprintf(_("Invite %s to join your guild"),
name.c_str()).c_str()));
if (local_player->isInParty() ||
- Net::getNetworkType() == ServerType::MANASERV)
+ Net::getNetworkType() == ServerType::ManaServ)
{
mBrowserBox->addRow(strprintf("@@party|%s@@",
strprintf(_("Invite %s to join your party"),
@@ -223,25 +223,25 @@ void PopupMenu::handleLink(const std::string &link)
else if (link == "unignore" && being &&
being->getType() == ActorSprite::PLAYER)
{
- player_relations.setRelation(being->getName(), PlayerRelation::NEUTRAL);
+ player_relations.setRelation(being->getName(), PlayerRelation::Neutral);
}
else if (link == "ignore" && being &&
being->getType() == ActorSprite::PLAYER)
{
- player_relations.setRelation(being->getName(), PlayerRelation::IGNORED);
+ player_relations.setRelation(being->getName(), PlayerRelation::Ignored);
}
else if (link == "disregard" && being &&
being->getType() == ActorSprite::PLAYER)
{
- player_relations.setRelation(being->getName(), PlayerRelation::DISREGARDED);
+ player_relations.setRelation(being->getName(), PlayerRelation::Disregarded);
}
else if (link == "friend" && being &&
being->getType() == ActorSprite::PLAYER)
{
- player_relations.setRelation(being->getName(), PlayerRelation::FRIEND);
+ player_relations.setRelation(being->getName(), PlayerRelation::Friend);
}
// Guild action
else if (link == "guild" && being &&
@@ -249,18 +249,15 @@ void PopupMenu::handleLink(const std::string &link)
{
local_player->inviteToGuild(being);
}
-
// Pick Up Floor Item action
else if ((link == "pickup") && mFloorItem)
{
local_player->pickUp(mFloorItem);
}
-
// Look To action
else if (link == "look")
{
}
-
else if (link == "activate" || link == "equip" || link == "unequip")
{
assert(mItem);
@@ -288,49 +285,36 @@ void PopupMenu::handleLink(const std::string &link)
else if (mFloorItem)
chatWindow->addItemText(mFloorItem->getInfo().name);
}
-
- else if (link == "split")
- {
- ItemAmountWindow::showWindow(ItemAmountWindow::ItemSplit,
- inventoryWindow, mItem);
- }
-
else if (link == "drop")
{
ItemAmountWindow::showWindow(ItemAmountWindow::ItemDrop,
inventoryWindow, mItem);
}
-
else if (link == "store")
{
ItemAmountWindow::showWindow(ItemAmountWindow::StoreAdd,
inventoryWindow, mItem);
}
-
else if (link == "retrieve")
{
ItemAmountWindow::showWindow(ItemAmountWindow::StoreRemove, mWindow,
mItem);
}
-
else if (link == "party" && being &&
being->getType() == ActorSprite::PLAYER)
{
Net::getPartyHandler()->invite(being);
}
-
else if (link == "name" && being)
{
const std::string &name = being->getName();
chatWindow->addInputText(name);
}
-
else if (link == "admin-kick" && being &&
being->getType() == ActorSprite::PLAYER)
{
Net::getAdminHandler()->kick(being->getName());
}
-
// Unknown actions
else if (link != "cancel")
{
@@ -376,11 +360,6 @@ void PopupMenu::showPopup(Window *parent, int x, int y, Item *item,
else
mBrowserBox->addRow(strprintf("@@drop|%s@@", _("Drop")));
}
-
- if (Net::getInventoryHandler()->canSplit(item))
- {
- mBrowserBox->addRow(strprintf("@@split|%s@@", _("Split")));
- }
}
// Assume in storage for now
// TODO: make this whole system more flexible, if needed
diff --git a/src/gui/popupmenu.h b/src/gui/popupmenu.h
index c7199b78..5a5a88ee 100644
--- a/src/gui/popupmenu.h
+++ b/src/gui/popupmenu.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef POPUP_MENU_H
-#define POPUP_MENU_H
+#pragma once
#include "gui/widgets/linkhandler.h"
#include "gui/widgets/popup.h"
@@ -76,5 +75,3 @@ class PopupMenu : public Popup, public LinkHandler
*/
void showPopup(int x, int y);
};
-
-#endif
diff --git a/src/gui/quitdialog.cpp b/src/gui/quitdialog.cpp
index 4afbd419..51831c4c 100644
--- a/src/gui/quitdialog.cpp
+++ b/src/gui/quitdialog.cpp
@@ -71,7 +71,8 @@ QuitDialog::QuitDialog(QuitDialog** pointerToMe):
placeOption(place, mSwitchAccountServer);
// Only added if we are connected to a gameserver
- if (state == STATE_GAME) placeOption(place, mSwitchCharacter);
+ if (state == STATE_GAME)
+ placeOption(place, mSwitchCharacter);
}
mOptions[0]->setSelected(true);
@@ -90,7 +91,9 @@ QuitDialog::QuitDialog(QuitDialog** pointerToMe):
QuitDialog::~QuitDialog()
{
- if (mMyPointer) *mMyPointer = nullptr;
+ if (mMyPointer)
+ *mMyPointer = nullptr;
+
// Optional widgets, so delete them by hand.
delete mForceQuit;
delete mLogoutQuit;
@@ -166,7 +169,8 @@ void QuitDialog::keyPressed(gcn::KeyEvent &keyEvent)
mOptions[0]->setSelected(true);
return;
}
- else if (it == mOptions.begin() && dir < 0)
+
+ if (it == mOptions.begin() && dir < 0)
it = mOptions.end();
it += dir;
diff --git a/src/gui/quitdialog.h b/src/gui/quitdialog.h
index d62d5c51..4672e45f 100644
--- a/src/gui/quitdialog.h
+++ b/src/gui/quitdialog.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef QUITDIALOG_H
-#define QUITDIALOG_H
+#pragma once
#include "gui/widgets/window.h"
@@ -69,5 +68,3 @@ class QuitDialog : public Window, public gcn::ActionListener,
QuitDialog **mMyPointer;
};
-
-#endif
diff --git a/src/gui/recorder.cpp b/src/gui/recorder.cpp
index 8dd0f8ed..894e3631 100644
--- a/src/gui/recorder.cpp
+++ b/src/gui/recorder.cpp
@@ -32,15 +32,16 @@
#include "utils/gettext.h"
#include "utils/stringutils.h"
-Recorder::Recorder(ChatWindow *chat, const std::string &title,
- const std::string &buttonTxt) :
- Window(title)
+Recorder::Recorder(ChatWindow *chat,
+ const std::string &title,
+ const std::string &buttonTxt)
+ : Window(title)
+ , mChat(chat)
{
setWindowName("Recorder");
const int offsetX = 2 * getPadding() + 10;
const int offsetY = getTitleBarHeight() + getPadding() + 10;
- mChat = chat;
auto *button = new Button(buttonTxt, "activate", this);
// 123 is the default chat window height. If you change this in Chat, please
@@ -56,16 +57,12 @@ Recorder::Recorder(ChatWindow *chat, const std::string &title,
loadWindowState();
}
-Recorder::~Recorder()
-{
-}
+Recorder::~Recorder() = default;
void Recorder::record(const std::string &msg)
{
if (mStream.is_open())
- {
mStream << msg << std::endl;
- }
}
void Recorder::setRecordingFile(const std::string &msg)
diff --git a/src/gui/recorder.h b/src/gui/recorder.h
index efd73021..8a84f423 100644
--- a/src/gui/recorder.h
+++ b/src/gui/recorder.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef RECORD_H
-#define RECORD_H
+#pragma once
#include "gui/widgets/window.h"
@@ -72,5 +71,3 @@ class Recorder : public Window, public gcn::ActionListener
std::ofstream mStream;
};
-
-#endif
diff --git a/src/gui/register.cpp b/src/gui/register.cpp
index d4ebb59c..62114c10 100644
--- a/src/gui/register.cpp
+++ b/src/gui/register.cpp
@@ -22,14 +22,12 @@
#include "gui/register.h"
#include "client.h"
-#include "configuration.h"
#include "log.h"
#include "gui/logindialog.h"
#include "gui/okdialog.h"
#include "gui/widgets/button.h"
-#include "gui/widgets/checkbox.h"
#include "gui/widgets/label.h"
#include "gui/widgets/layout.h"
#include "gui/widgets/passwordfield.h"
@@ -227,7 +225,7 @@ void RegisterDialog::action(const gcn::ActionEvent &event)
mLoginData->password = mPasswordField->getText();
if (mFemaleButton)
mLoginData->gender = mFemaleButton->isSelected() ?
- Gender::FEMALE : Gender::MALE;
+ Gender::Female : Gender::Male;
if (mEmailField)
mLoginData->email = mEmailField->getText();
mLoginData->registerLogin = true;
diff --git a/src/gui/register.h b/src/gui/register.h
index 1996cd47..ae9ee582 100644
--- a/src/gui/register.h
+++ b/src/gui/register.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef REGISTER_H
-#define REGISTER_H
+#pragma once
#include "gui/widgets/window.h"
@@ -94,5 +93,3 @@ class RegisterDialog : public Window, public gcn::ActionListener,
LoginData *mLoginData;
};
-
-#endif
diff --git a/src/gui/sdlinput.h b/src/gui/sdlinput.h
index a29f17ca..599aafe7 100644
--- a/src/gui/sdlinput.h
+++ b/src/gui/sdlinput.h
@@ -56,8 +56,7 @@
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SDLINPUT_H
-#define SDLINPUT_H
+#pragma once
#include <queue>
@@ -204,5 +203,3 @@ protected:
bool mMouseDown = false;
};
-
-#endif
diff --git a/src/gui/selldialog.cpp b/src/gui/selldialog.cpp
index 5f499982..4aeacd6f 100644
--- a/src/gui/selldialog.cpp
+++ b/src/gui/selldialog.cpp
@@ -205,7 +205,7 @@ void SellDialog::action(const gcn::ActionEvent &event)
sellCount = item->sellCurrentDuplicate(mAmountItems);
// For Manaserv, the Item id is to be given as index.
- if ((Net::getNetworkType() == ServerType::MANASERV))
+ if ((Net::getNetworkType() == ServerType::ManaServ))
itemIndex = item->getId();
Net::getNpcHandler()->sellItem(mNpcId, itemIndex, sellCount);
diff --git a/src/gui/selldialog.h b/src/gui/selldialog.h
index d59343cb..165d9cc8 100644
--- a/src/gui/selldialog.h
+++ b/src/gui/selldialog.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SELL_H
-#define SELL_H
+#pragma once
#include "gui/widgets/window.h"
@@ -113,5 +112,3 @@ class SellDialog : public Window, gcn::ActionListener, gcn::SelectionListener
int mMaxItems = 0;
int mAmountItems = 0;
};
-
-#endif
diff --git a/src/gui/serverdialog.cpp b/src/gui/serverdialog.cpp
index 01b477da..d86d751a 100644
--- a/src/gui/serverdialog.cpp
+++ b/src/gui/serverdialog.cpp
@@ -32,26 +32,21 @@
#include "gui/sdlinput.h"
#include "gui/widgets/button.h"
-#include "gui/widgets/dropdown.h"
#include "gui/widgets/label.h"
#include "gui/widgets/layout.h"
#include "gui/widgets/listbox.h"
#include "gui/widgets/scrollarea.h"
-#include "gui/widgets/textfield.h"
#include "resources/theme.h"
#include "utils/gettext.h"
#include "utils/stringutils.h"
-#include "utils/xml.h"
#include <guichan/font.hpp>
#include <cstdlib>
#include <string>
-static const int MAX_SERVERLIST = 6;
-
ServersListModel::ServersListModel(ServerInfos *servers, ServerDialog *parent):
mServers(servers),
mVersionStrings(servers->size(), VersionString(0, std::string())),
@@ -61,13 +56,11 @@ ServersListModel::ServersListModel(ServerInfos *servers, ServerDialog *parent):
int ServersListModel::getNumberOfElements()
{
- MutexLocker lock(mParent->getMutex());
return mServers->size();
}
std::string ServersListModel::getElementAt(int elementIndex)
{
- MutexLocker lock(mParent->getMutex());
const ServerInfo &server = mServers->at(elementIndex);
std::string myServer;
myServer += server.hostname;
@@ -102,16 +95,15 @@ public:
auto *model = static_cast<ServersListModel*>(mListModel);
- updateAlpha();
+ const int alpha = gui->getTheme()->getGuiAlpha();
- graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT,
- (int) (mAlpha * 255.0f)));
+ graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT, alpha));
graphics->setFont(getFont());
const int height = getRowHeight();
const gcn::Color unsupported =
Theme::getThemeColor(Theme::SERVER_VERSION_NOT_SUPPORTED,
- (int) (mAlpha * 255.0f));
+ alpha);
// Draw filled rectangle around the selected list element
if (mSelected >= 0)
@@ -122,7 +114,7 @@ public:
for (int i = 0, y = 0; i < model->getNumberOfElements();
++i, y += height)
{
- ServerInfo info = model->getServer(i);
+ const ServerInfo &info = model->getServer(i);
graphics->setColor(Theme::getThemeColor(Theme::TEXT));
@@ -141,7 +133,6 @@ public:
if (info.version.first > 0)
{
graphics->setColor(unsupported);
-
graphics->drawText(info.version.second,
getWidth() - info.version.first - 2, top);
}
@@ -164,9 +155,8 @@ ServerDialog::ServerDialog(ServerInfo *serverInfo, const std::string &dir):
loadCustomServers();
- mServersListModel = new ServersListModel(&mServers, this);
-
- mServersList = new ServersListBox(mServersListModel);
+ mServersListModel = std::make_unique<ServersListModel>(&mServers, this);
+ mServersList = new ServersListBox(mServersListModel.get());
auto *usedScroll = new ScrollArea(mServersList);
usedScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER);
@@ -185,8 +175,8 @@ ServerDialog::ServerDialog(ServerInfo *serverInfo, const std::string &dir):
usedScroll->setVerticalScrollAmount(0);
place(0, 0, usedScroll, 6, 5).setPadding(3);
- place(0, 5, mDescription, 5);
- place(0, 6, mDownloadText, 5);
+ place(0, 5, mDescription, 6);
+ place(0, 6, mDownloadText, 6);
place(0, 7, mManualEntryButton);
place(1, 7, mModifyButton);
place(2, 7, mDeleteButton);
@@ -226,16 +216,7 @@ ServerDialog::ServerDialog(ServerInfo *serverInfo, const std::string &dir):
downloadServerList();
}
-ServerDialog::~ServerDialog()
-{
- if (mDownload)
- {
- mDownload->cancel();
- delete mDownload;
- mDownload = nullptr;
- }
- delete mServersListModel;
-}
+ServerDialog::~ServerDialog() = default;
void ServerDialog::action(const gcn::ActionEvent &event)
{
@@ -262,6 +243,7 @@ void ServerDialog::action(const gcn::ActionEvent &event)
else
{
mDownload->cancel();
+
mQuitButton->setEnabled(false);
mConnectButton->setEnabled(false);
mDeleteButton->setEnabled(false);
@@ -345,7 +327,6 @@ void ServerDialog::valueChanged(const gcn::SelectionEvent &)
// Update the server and post fields according to the new selection
const ServerInfo &myServer = mServersListModel->getServer(index);
mDescription->setCaption(myServer.description);
-
mDeleteButton->setEnabled(myServer.save);
mModifyButton->setEnabled(myServer.save);
}
@@ -362,36 +343,42 @@ void ServerDialog::mouseClicked(gcn::MouseEvent &mouseEvent)
void ServerDialog::logic()
{
- {
- MutexLocker lock(&mMutex);
- if (mDownloadStatus == DOWNLOADING_COMPLETE)
- {
- mDownloadStatus = DOWNLOADING_OVER;
+ Window::logic();
- mDescription->setCaption(mServers[0].description);
- mDownloadText->setCaption(std::string());
- }
- else if (mDownloadStatus == DOWNLOADING_IN_PROGRESS)
- {
- mDownloadText->setCaption(strprintf(_("Downloading server list..."
- "%2.2f%%"),
- mDownloadProgress * 100));
- }
- else if (mDownloadStatus == DOWNLOADING_IDLE)
- {
- mDownloadText->setCaption(_("Waiting for server..."));
- }
- else if (mDownloadStatus == DOWNLOADING_PREPARING)
+ if (mDownloadDone)
+ return;
+
+ auto state = mDownload->getState();
+
+ switch (state.status) {
+ case DownloadStatus::InProgress:
+ mDownloadText->setCaption(strprintf(_("Downloading server list..."
+ "%2.0f%%"),
+ state.progress * 100));
+ break;
+
+ case DownloadStatus::Canceled:
+ case DownloadStatus::Error:
+ mDownloadDone = true;
+ logger->log("Error retrieving server list: %s", mDownload->getError());
+ mDownloadText->setCaption(_("Error retrieving server list!"));
+ break;
+
+ case DownloadStatus::Complete:
+ mDownloadDone = true;
+ loadServers();
+
+ if (mServers.empty())
{
- mDownloadText->setCaption(_("Preparing download"));
+ mDownloadText->setCaption(_("No servers found!"));
}
- else if (mDownloadStatus == DOWNLOADING_ERROR)
+ else
{
- mDownloadText->setCaption(_("Error retreiving server list!"));
+ mDownloadText->setCaption(std::string());
+ mDescription->setCaption(mServers[0].description);
}
+ break;
}
-
- Window::logic();
}
void ServerDialog::downloadServerList()
@@ -406,7 +393,7 @@ void ServerDialog::downloadServerList()
if (listFile.empty())
listFile = "https://www.manasource.org/serverlist.xml";
- mDownload = new Net::Download(this, listFile, &downloadUpdate);
+ mDownload = std::make_unique<Net::Download>(listFile);
mDownload->setFile(mDir + "/serverlist.xml");
mDownload->start();
}
@@ -432,90 +419,92 @@ void ServerDialog::loadServers()
for (auto serverNode : rootNode.children())
{
- if (serverNode.name() != "server")
- continue;
+ if (serverNode.name() == "server")
+ loadServer(serverNode);
+ }
+}
- ServerInfo server;
+void ServerDialog::loadServer(XML::Node serverNode)
+{
+ ServerInfo server;
- std::string type = serverNode.getProperty("type", "unknown");
+ std::string type = serverNode.getProperty("type", "unknown");
- server.type = ServerInfo::parseType(type);
+ server.type = ServerInfo::parseType(type);
- // Ignore unknown server types
- if (server.type == ServerType::UNKNOWN
+ // Ignore unknown server types
+ if (server.type == ServerType::Unknown
#ifndef MANASERV_SUPPORT
- || server.type == ServerType::MANASERV
+ || server.type == ServerType::MANASERV
#endif
)
- {
- logger->log("Ignoring server entry with unknown type: %s",
- type.c_str());
- continue;
- }
+ {
+ logger->log("Ignoring server entry with unknown type: %s",
+ type.c_str());
+ return;
+ }
- server.name = serverNode.getProperty("name", std::string());
+ server.name = serverNode.getProperty("name", std::string());
- std::string version = serverNode.getProperty("minimumVersion",
- std::string());
+ std::string version = serverNode.getProperty("minimumVersion",
+ std::string());
- bool meetsMinimumVersion = compareStrI(version, PACKAGE_VERSION) <= 0;
+ bool meetsMinimumVersion = strcmp(version.c_str(), PACKAGE_VERSION) <= 0;
- // For display in the list
- if (meetsMinimumVersion)
- version.clear();
- else if (version.empty())
- version = _("requires a newer version");
- else
- version = strprintf(_("requires v%s"), version.c_str());
+ // For display in the list
+ if (meetsMinimumVersion)
+ version.clear();
+ else if (version.empty())
+ version = _("requires a newer version");
+ else
+ version = strprintf(_("requires v%s"), version.c_str());
- for (auto subNode : serverNode.children())
+ for (auto subNode : serverNode.children())
+ {
+ if (subNode.name() == "connection")
{
- if (subNode.name() == "connection")
- {
- server.hostname = subNode.getProperty("hostname", std::string());
- server.port = subNode.getProperty("port", 0);
- if (server.port == 0)
- {
- // If no port is given, use the default for the given type
- server.port = ServerInfo::defaultPortForServerType(server.type);
- }
- }
- else if (subNode.name() == "description")
- {
- server.description = subNode.textContent();
- }
- else if (subNode.name() == "persistentIp")
+ server.hostname = subNode.getProperty("hostname", std::string());
+ server.port = subNode.getProperty("port", 0);
+ if (server.port == 0)
{
- const auto text = subNode.textContent();
- server.persistentIp = text == "1" || text == "true";
+ // If no port is given, use the default for the given type
+ server.port = ServerInfo::defaultPortForServerType(server.type);
}
}
+ else if (subNode.name() == "description")
+ {
+ server.description = subNode.textContent();
+ }
+ else if (subNode.name() == "persistentIp")
+ {
+ const auto text = subNode.textContent();
+ server.persistentIp = text == "1" || text == "true";
+ }
+ }
- server.version.first = gui->getFont()->getWidth(version);
- server.version.second = version;
+ server.version.first = gui->getFont()->getWidth(version);
+ server.version.second = version;
- MutexLocker lock(&mMutex);
- // Add the server to the local list if it's not already present
- bool found = false;
- int i = 0;
- for (auto &s : mServers)
+ // Add the server to the local list if it's not already present
+ bool found = false;
+ int i = 0;
+ for (auto &s : mServers)
+ {
+ if (s == server)
{
- if (s == server)
- {
- // Use the name listed in the server list
- s.name = server.name;
- s.version = server.version;
- s.description = server.description;
- mServersListModel->setVersionString(i, version);
- found = true;
- break;
- }
- ++i;
+ // Use the name listed in the server list
+ s.name = server.name;
+ s.version = server.version;
+ s.description = server.description;
+ mServersListModel->setVersionString(i, version);
+ found = true;
+ break;
}
-
- if (!found)
- mServers.push_back(server);
+ ++i;
}
+
+ if (!found)
+ mServers.push_back(server);
}
void ServerDialog::loadCustomServers()
@@ -558,51 +547,6 @@ void ServerDialog::saveCustomServers(const ServerInfo &currentServer, int index)
// Restore the correct description
if (index < 0)
index = 0;
- mDescription->setCaption(mServers[index].description);
-}
-
-int ServerDialog::downloadUpdate(void *ptr, DownloadStatus status,
- size_t total, size_t remaining)
-{
- if (status == DOWNLOAD_STATUS_CANCELLED)
- return -1;
-
- auto *sd = reinterpret_cast<ServerDialog*>(ptr);
- bool finished = false;
-
- if (status == DOWNLOAD_STATUS_COMPLETE)
- {
- finished = true;
- }
- else if (status < 0)
- {
- logger->log("Error retreiving server list: %s",
- sd->mDownload->getError());
- sd->mDownloadStatus = DOWNLOADING_ERROR;
- }
- else
- {
- float progress = (float) remaining / total;
-
- if (progress != progress)
- progress = 0.0f; // check for NaN
- else if (progress < 0.0f)
- progress = 0.0f; // no idea how this could ever happen, but why not check for it anyway.
- else if (progress > 1.0f)
- progress = 1.0f;
-
- MutexLocker lock(&sd->mMutex);
- sd->mDownloadStatus = DOWNLOADING_IN_PROGRESS;
- sd->mDownloadProgress = progress;
- }
-
- if (finished)
- {
- sd->loadServers();
-
- MutexLocker lock(&sd->mMutex);
- sd->mDownloadStatus = DOWNLOADING_COMPLETE;
- }
-
- return 0;
+ if (static_cast<size_t>(index) < mServers.size())
+ mDescription->setCaption(mServers[index].description);
}
diff --git a/src/gui/serverdialog.h b/src/gui/serverdialog.h
index 4db36462..cc2725be 100644
--- a/src/gui/serverdialog.h
+++ b/src/gui/serverdialog.h
@@ -19,21 +19,20 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SERVERDIALOG_H
-#define SERVERDIALOG_H
+#pragma once
#include "gui/widgets/window.h"
#include "net/download.h"
#include "net/serverinfo.h"
-
-#include "utils/mutex.h"
+#include "utils/xml.h"
#include <guichan/actionlistener.hpp>
#include <guichan/keylistener.hpp>
#include <guichan/listmodel.hpp>
#include <guichan/selectionlistener.hpp>
+#include <memory>
#include <string>
#include <vector>
@@ -90,7 +89,6 @@ class ServerDialog : public Window,
{
public:
ServerDialog(ServerInfo *serverInfo, const std::string &dir);
-
~ServerDialog() override;
/**
@@ -110,10 +108,8 @@ class ServerDialog : public Window,
void logic() override;
protected:
- friend class ServersListModel;
- Mutex *getMutex() { return &mMutex; }
-
friend class CustomServerDialog;
+
/**
* Saves the new server entry in the custom server list.
* Removes the given entry when the serverInfo is empty.
@@ -128,12 +124,10 @@ class ServerDialog : public Window,
*/
void downloadServerList();
void loadServers();
+ void loadServer(XML::Node serverNode);
void loadCustomServers();
- static int downloadUpdate(void *ptr, DownloadStatus status,
- size_t total, size_t remaining);
-
Label *mDescription;
Button *mQuitButton;
Button *mConnectButton;
@@ -142,31 +136,13 @@ class ServerDialog : public Window,
Button *mDeleteButton;
ListBox *mServersList;
- ServersListModel *mServersListModel;
+ std::unique_ptr<ServersListModel> mServersListModel;
const std::string &mDir;
- enum ServerDialogDownloadStatus
- {
- DOWNLOADING_ERROR,
- DOWNLOADING_PREPARING,
- DOWNLOADING_IDLE,
- DOWNLOADING_IN_PROGRESS,
- DOWNLOADING_COMPLETE,
- DOWNLOADING_OVER
- };
-
- /** Status of the current download. */
- ServerDialogDownloadStatus mDownloadStatus = DOWNLOADING_PREPARING;
-
- Net::Download *mDownload = nullptr;
+ std::unique_ptr<Net::Download> mDownload;
+ bool mDownloadDone = false;
Label *mDownloadText;
-
- Mutex mMutex;
- float mDownloadProgress = -1.0f;
-
ServerInfos mServers;
ServerInfo *mServerInfo;
};
-
-#endif
diff --git a/src/gui/setup.h b/src/gui/setup.h
index d510267d..ec45f93b 100644
--- a/src/gui/setup.h
+++ b/src/gui/setup.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SETUP_H
-#define SETUP_H
+#pragma once
#include "gui/widgets/window.h"
@@ -72,5 +71,3 @@ class Setup : public Window, public gcn::ActionListener
};
extern Setup* setupWindow;
-
-#endif
diff --git a/src/gui/setup_audio.cpp b/src/gui/setup_audio.cpp
index f51bfcb6..43b132d8 100644
--- a/src/gui/setup_audio.cpp
+++ b/src/gui/setup_audio.cpp
@@ -29,6 +29,7 @@
#include "gui/widgets/checkbox.h"
#include "gui/widgets/label.h"
+#include "gui/widgets/layout.h"
#include "gui/widgets/slider.h"
#include "utils/gettext.h"
@@ -71,11 +72,11 @@ Setup_Audio::Setup_Audio():
// Do the layout
place(0, 0, mSoundCheckBox);
- place(0, 1, mSfxSlider);
+ place(0, 1, mSfxSlider).setVAlign(LayoutCell::CENTER);
place(1, 1, sfxLabel);
- place(0, 2, mNotificationsSlider);
+ place(0, 2, mNotificationsSlider).setVAlign(LayoutCell::CENTER);
place(1, 2, notificationsLabel);
- place(0, 3, mMusicSlider);
+ place(0, 3, mMusicSlider).setVAlign(LayoutCell::CENTER);
place(1, 3, musicLabel);
place(0, 4, mDownloadMusicCheckBox);
}
diff --git a/src/gui/setup_audio.h b/src/gui/setup_audio.h
index 4ee277ef..c19b38b6 100644
--- a/src/gui/setup_audio.h
+++ b/src/gui/setup_audio.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GUI_SETUP_AUDIO_H
-#define GUI_SETUP_AUDIO_H
+#pragma once
#include "guichanfwd.h"
@@ -51,5 +50,3 @@ class Setup_Audio : public SetupTab, public gcn::ActionListener
gcn::Slider *mNotificationsSlider;
gcn::Slider *mMusicSlider;
};
-
-#endif // GUI_SETUP_AUDIO_H
diff --git a/src/gui/setup_colors.cpp b/src/gui/setup_colors.cpp
index 880f6f8a..c3d6a8c8 100644
--- a/src/gui/setup_colors.cpp
+++ b/src/gui/setup_colors.cpp
@@ -150,19 +150,19 @@ Setup_Colors::Setup_Colors() :
place(0, 0, mScroll, 6, 6).setPadding(2);
place(0, 6, mPreviewBox, 6).setPadding(2);
place(0, 7, mGradTypeLabel, 3);
- place(3, 7, mGradTypeSlider);
+ place(3, 7, mGradTypeSlider).setVAlign(Layout::CENTER);
place(4, 7, mGradTypeText, 2).setPadding(1);
place(0, 8, mRedLabel, 3);
- place(3, 8, mRedSlider);
+ place(3, 8, mRedSlider).setVAlign(Layout::CENTER);
place(5, 8, mRedText).setPadding(1);
place(0, 9, mGreenLabel, 3);
- place(3, 9, mGreenSlider);
+ place(3, 9, mGreenSlider).setVAlign(Layout::CENTER);
place(5, 9, mGreenText).setPadding(1);
place(0, 10, mBlueLabel, 3);
- place(3, 10, mBlueSlider);
+ place(3, 10, mBlueSlider).setVAlign(Layout::CENTER);
place(5, 10, mBlueText).setPadding(1);
place(0, 11, mGradDelayLabel, 3);
- place(3, 11, mGradDelaySlider);
+ place(3, 11, mGradDelaySlider).setVAlign(Layout::CENTER);
place(5, 11, mGradDelayText).setPadding(1);
mGradTypeText->setCaption(std::string());
diff --git a/src/gui/setup_colors.h b/src/gui/setup_colors.h
index b534ca8a..9aa5be74 100644
--- a/src/gui/setup_colors.h
+++ b/src/gui/setup_colors.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SETUP_COLORS_H
-#define SETUP_COLORS_H
+#pragma once
#include "guichanfwd.h"
@@ -87,5 +86,3 @@ class Setup_Colors : public SetupTab,
void updateColor();
void updateGradType();
};
-
-#endif // SETUP_COLORS_H
diff --git a/src/gui/setup_interface.h b/src/gui/setup_interface.h
index 530d575a..9f5bbf1a 100644
--- a/src/gui/setup_interface.h
+++ b/src/gui/setup_interface.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GUI_SETUP_INTERFACE_H
-#define GUI_SETUP_INTERFACE_H
+#pragma once
#include "being.h"
#include "guichanfwd.h"
@@ -77,5 +76,3 @@ class Setup_Interface : public SetupTab, public gcn::ActionListener,
gcn::DropDown *mFontSizeDropDown;
};
-
-#endif
diff --git a/src/gui/setup_joystick.h b/src/gui/setup_joystick.h
index e51ccbe8..8b319ad2 100644
--- a/src/gui/setup_joystick.h
+++ b/src/gui/setup_joystick.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GUI_SETUP_JOYSTICK_H
-#define GUI_SETUP_JOYSTICK_H
+#pragma once
#include "guichanfwd.h"
@@ -44,5 +43,3 @@ class Setup_Joystick : public SetupTab, public gcn::ActionListener
bool mJoystickEnabled;
gcn::CheckBox *mJoystickCheckBox;
};
-
-#endif
diff --git a/src/gui/setup_keyboard.h b/src/gui/setup_keyboard.h
index 1c9d1733..e953bdf3 100644
--- a/src/gui/setup_keyboard.h
+++ b/src/gui/setup_keyboard.h
@@ -20,8 +20,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GUI_SETUP_KEYBOARD_H
-#define GUI_SETUP_KEYBOARD_H
+#pragma once
#include "guichanfwd.h"
@@ -71,5 +70,3 @@ class Setup_Keyboard : public SetupTab, public gcn::ActionListener
bool mKeySetting; /**< flag to check if key being set. */
};
-
-#endif
diff --git a/src/gui/setup_players.cpp b/src/gui/setup_players.cpp
index 67fe79da..7cfa572a 100644
--- a/src/gui/setup_players.cpp
+++ b/src/gui/setup_players.cpp
@@ -267,13 +267,13 @@ Setup_Players::Setup_Players():
place(0, 0, mPlayerTitleTable, 4);
place(0, 1, mPlayerScrollArea, 4, 4).setPadding(2);
place(0, 5, mDeleteButton);
- place(0, 6, mShowGenderCheckBox, 2).setPadding(2);
- place(0, 7, mEnableChatLogCheckBox, 2).setPadding(2);
+ place(0, 6, mShowGenderCheckBox, 2);
+ place(0, 7, mEnableChatLogCheckBox, 2);
place(2, 5, ignore_action_label);
- place(2, 6, mIgnoreActionChoicesBox, 2).setPadding(2);
+ place(2, 6, mIgnoreActionChoicesBox, 2);
place(0, 8, mDefaultTrading);
place(0, 9, mDefaultWhisper);
- place(0, 10, mWhisperTabCheckBox, 4).setPadding(4);
+ place(0, 10, mWhisperTabCheckBox, 4);
player_relations.addListener(this);
}
diff --git a/src/gui/setup_players.h b/src/gui/setup_players.h
index 126d621b..3ca422d2 100644
--- a/src/gui/setup_players.h
+++ b/src/gui/setup_players.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GUI_SETUP_PLAYERS_H
-#define GUI_SETUP_PLAYERS_H
+#pragma once
#include "guichanfwd.h"
#include "playerrelations.h"
@@ -70,5 +69,3 @@ private:
gcn::CheckBox *mShowGenderCheckBox;
gcn::CheckBox *mEnableChatLogCheckBox;
};
-
-#endif
diff --git a/src/gui/setup_video.cpp b/src/gui/setup_video.cpp
index 38602a5c..b5322525 100644
--- a/src/gui/setup_video.cpp
+++ b/src/gui/setup_video.cpp
@@ -325,17 +325,17 @@ Setup_Video::Setup_Video():
place(0, 2, mDisableSDLTransparencyCheckBox, 4);
place(0, 3, mFpsCheckBox);
- place(1, 3, mFpsSlider, 2);
+ place(1, 3, mFpsSlider, 2).setVAlign(LayoutCell::CENTER);
place(3, 3, mFpsLabel);
place(0, 4, mParticleEffectsCheckBox, 4);
place(0, 5, particleDetailLabel);
- place(1, 5, mParticleDetailSlider, 2);
+ place(1, 5, mParticleDetailSlider, 2).setVAlign(LayoutCell::CENTER);
place(3, 5, mParticleDetailField);
place(0, 6, overlayDetailLabel);
- place(1, 6, mOverlayDetailSlider, 2);
+ place(1, 6, mOverlayDetailSlider, 2).setVAlign(LayoutCell::CENTER);
place(3, 6, mOverlayDetailField);
}
@@ -557,6 +557,6 @@ void Setup_Video::refreshScaleList()
}
mScaleListModel->setVideoSettings(mVideoSettings);
- mScaleDropDown->setListModel(mScaleListModel.get());
+ mScaleDropDown->adjustHeight();
mScaleDropDown->setSelected(mVideoSettings.userScale);
}
diff --git a/src/gui/setup_video.h b/src/gui/setup_video.h
index 6d4fbe77..969d8e8f 100644
--- a/src/gui/setup_video.h
+++ b/src/gui/setup_video.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GUI_SETUP_VIDEO_H
-#define GUI_SETUP_VIDEO_H
+#pragma once
#include "guichanfwd.h"
@@ -30,6 +29,7 @@
#include <guichan/actionlistener.hpp>
#include <guichan/keylistener.hpp>
+class DropDown;
class ResolutionListModel;
class ScaleListModel;
@@ -60,7 +60,7 @@ class Setup_Video : public SetupTab, public gcn::ActionListener,
gcn::DropDown *mWindowModeDropDown;
gcn::DropDown *mResolutionDropDown;
- gcn::DropDown *mScaleDropDown;
+ DropDown *mScaleDropDown;
gcn::CheckBox *mVSyncCheckBox;
gcn::CheckBox *mOpenGLCheckBox;
gcn::CheckBox *mCustomCursorCheckBox;
@@ -80,5 +80,3 @@ class Setup_Video : public SetupTab, public gcn::ActionListener,
gcn::CheckBox *mDisableSDLTransparencyCheckBox;
};
-
-#endif
diff --git a/src/gui/shortcutwindow.cpp b/src/gui/shortcutwindow.cpp
index 7d299d2c..82c7678e 100644
--- a/src/gui/shortcutwindow.cpp
+++ b/src/gui/shortcutwindow.cpp
@@ -41,7 +41,11 @@ ShortcutWindow::ShortcutWindow(const std::string &title,
setSaveVisible(true);
setupWindow->registerWindowForReset(this);
- const int border = getPadding() * 2;
+ auto scrollArea = new ScrollArea(content);
+ scrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER);
+ scrollArea->setOpaque(false);
+
+ const int border = (getPadding() + content->getFrameSize()) * 2;
setMinWidth(content->getBoxWidth() + border);
setMinHeight(content->getBoxHeight() + border + GRAB_MARGIN);
setMaxWidth(content->getBoxWidth() * content->getMaxItems() + border);
@@ -49,10 +53,6 @@ ShortcutWindow::ShortcutWindow(const std::string &title,
setDefaultSize(getMinWidth(), getMaxHeight(), ImageRect::LOWER_RIGHT);
- auto scrollArea = new ScrollArea(content);
- scrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER);
- scrollArea->setOpaque(false);
-
place(0, 0, scrollArea, 5, 5).setPadding(0);
Layout &layout = getLayout();
diff --git a/src/gui/shortcutwindow.h b/src/gui/shortcutwindow.h
index 7494dfed..68e031ad 100644
--- a/src/gui/shortcutwindow.h
+++ b/src/gui/shortcutwindow.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SHORTCUTWINDOW_H
-#define SHORTCUTWINDOW_H
+#pragma once
#include "gui/widgets/window.h"
@@ -39,5 +38,3 @@ class ShortcutWindow : public Window
extern ShortcutWindow *itemShortcutWindow;
extern ShortcutWindow *emoteShortcutWindow;
-
-#endif
diff --git a/src/gui/skilldialog.cpp b/src/gui/skilldialog.cpp
index 5b85252d..c1911ac5 100644
--- a/src/gui/skilldialog.cpp
+++ b/src/gui/skilldialog.cpp
@@ -30,7 +30,6 @@
#include "gui/widgets/button.h"
#include "gui/widgets/label.h"
#include "gui/widgets/listbox.h"
-#include "gui/widgets/progressbar.h"
#include "gui/widgets/scrollarea.h"
#include "gui/widgets/tab.h"
#include "gui/widgets/tabbedarea.h"
@@ -43,7 +42,6 @@
#include "resources/resourcemanager.h"
#include "resources/theme.h"
-#include "utils/dtor.h"
#include "utils/gettext.h"
#include "utils/stringutils.h"
#include "utils/xml.h"
@@ -61,7 +59,7 @@ struct SkillInfo
{
unsigned short id;
std::string name;
- Image *icon = nullptr;
+ ResourceRef<Image> icon;
bool modifiable;
bool visible;
SkillModel *model = nullptr;
@@ -73,25 +71,16 @@ struct SkillInfo
float progress;
gcn::Color color;
- ~SkillInfo()
- {
- if (icon)
- icon->decRef();
- }
+ ~SkillInfo() = default;
void setIcon(const std::string &iconPath)
{
ResourceManager *res = ResourceManager::getInstance();
if (!iconPath.empty())
- {
icon = res->getImage(iconPath);
- }
if (!icon)
- {
- icon = Theme::getImageFromTheme(
- paths.getStringValue("unknownItemFile"));
- }
+ icon = Theme::getImageFromTheme(paths.getStringValue("unknownItemFile"));
}
void update();
@@ -113,19 +102,19 @@ public:
void updateVisibilities();
- void addSkill(SkillInfo *info)
- { mSkills.push_back(info); }
+ void addSkill(std::unique_ptr<SkillInfo> info)
+ { mSkills.push_back(std::move(info)); }
private:
- std::vector<SkillInfo *> mSkills;
+ std::vector<std::unique_ptr<SkillInfo>> mSkills;
std::vector<SkillInfo *> mVisibleSkills;
};
class SkillListBox : public ListBox
{
public:
- SkillListBox(SkillModel *model):
- ListBox(model)
+ SkillListBox(SkillModel *model)
+ : ListBox(model)
{}
SkillInfo *getSelectedInfo()
@@ -142,14 +131,12 @@ public:
if (!mListModel)
return;
- auto* model = static_cast<SkillModel*>(mListModel);
-
- updateAlpha();
+ auto *model = static_cast<SkillModel *>(mListModel);
+ auto *graphics = static_cast<Graphics *>(gcnGraphics);
- auto *graphics = static_cast<Graphics*>(gcnGraphics);
+ const int alpha = gui->getTheme()->getGuiAlpha();
- graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT,
- (int) (mAlpha * 255.0f)));
+ graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT, alpha));
graphics->setFont(getFont());
// Draw filled rectangle around the selected list element
@@ -165,12 +152,8 @@ public:
i < model->getNumberOfElements();
++i, y += getRowHeight())
{
- SkillInfo *e = model->getSkillAt(i);
-
- if (e)
- {
+ if (SkillInfo *e = model->getSkillAt(i))
e->draw(graphics, y, getWidth());
- }
}
}
@@ -180,8 +163,8 @@ public:
class SkillTab : public Tab
{
public:
- SkillTab(const std::string &name, SkillListBox *listBox):
- mListBox(listBox)
+ SkillTab(const std::string &name, SkillListBox *listBox)
+ : mListBox(listBox)
{
setCaption(name);
}
@@ -189,7 +172,6 @@ public:
~SkillTab() override
{
delete mListBox;
- mListBox = nullptr;
}
SkillInfo *getSelectedInfo()
@@ -215,11 +197,11 @@ SkillDialog::SkillDialog():
setMinWidth(240);
setupWindow->registerWindowForReset(this);
- mTabs = new TabbedArea();
+ mTabbedArea = new TabbedArea;
mPointsLabel = new Label("0");
mIncreaseButton = new Button(_("Up"), "inc", this);
- place(0, 0, mTabs, 5, 5);
+ place(0, 0, mTabbedArea, 5, 5);
place(0, 5, mPointsLabel, 4);
place(4, 5, mIncreaseButton);
@@ -236,7 +218,7 @@ void SkillDialog::action(const gcn::ActionEvent &event)
{
if (event.getId() == "inc")
{
- auto *tab = static_cast<SkillTab*>(mTabs->getSelectedTab());
+ auto *tab = static_cast<SkillTab*>(mTabbedArea->getSelectedTab());
if (SkillInfo *info = tab->getSelectedInfo())
Net::getPlayerHandler()->increaseSkill(info->id);
}
@@ -249,12 +231,11 @@ void SkillDialog::action(const gcn::ActionEvent &event)
std::string SkillDialog::update(int id)
{
auto i = mSkills.find(id);
-
if (i != mSkills.end())
{
- SkillInfo *info = i->second;
- info->update();
- return info->name;
+ SkillInfo &info = *i->second;
+ info.update();
+ return info.name;
}
return std::string();
@@ -267,9 +248,7 @@ void SkillDialog::update()
mPointsLabel->adjustSize();
for (auto &skill : mSkills)
- {
skill.second->update();
- }
}
void SkillDialog::event(Event::Channel channel, const Event &event)
@@ -291,20 +270,11 @@ void SkillDialog::event(Event::Channel channel, const Event &event)
void SkillDialog::clearSkills()
{
- // Fixes issues with removing tabs
- if (mTabs->getSelectedTabIndex() != -1)
- {
- mTabs->setSelectedTab((unsigned int) 0);
-
- while (mTabs->getSelectedTabIndex() != -1)
- {
- gcn::Tab *tab = mTabs->getSelectedTab();
- mTabs->removeTabWithIndex(mTabs->getSelectedTabIndex());
- delete tab;
- }
- }
+ for (auto &tab : mTabs)
+ mTabbedArea->removeTab(tab.get());
- delete_all(mSkills);
+ mTabs.clear();
+ mSkillModels.clear();
mSkills.clear();
}
@@ -317,40 +287,40 @@ void SkillDialog::loadSkills()
int setCount = 0;
std::string setName;
- ScrollArea *scroll;
- SkillListBox *listbox;
- SkillTab *tab;
if (!root || root.name() != "skills")
{
logger->log("Error loading skills file: %s", SKILLS_FILE);
- if (Net::getNetworkType() == ServerType::TMWATHENA)
+ if (Net::getNetworkType() == ServerType::TmwAthena)
{
- auto *model = new SkillModel();
- auto *skill = new SkillInfo;
+ auto model = std::make_unique<SkillModel>();
+ auto skill = std::make_unique<SkillInfo>();
skill->id = 1;
skill->name = "basic";
skill->setIcon(std::string());
skill->modifiable = true;
skill->visible = true;
- skill->model = model;
+ skill->model = model.get();
skill->update();
- model->addSkill(skill);
- mSkills[1] = skill;
+ mSkills[1] = skill.get();
+ model->addSkill(std::move(skill));
model->updateVisibilities();
- listbox = new SkillListBox(model);
- scroll = new ScrollArea(listbox);
+ auto listbox = new SkillListBox(model.get());
+ auto scroll = std::make_unique<ScrollArea>(listbox);
scroll->setOpaque(false);
scroll->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER);
scroll->setVerticalScrollPolicy(ScrollArea::SHOW_ALWAYS);
- tab = new SkillTab("Skills", listbox);
+ auto tab = std::make_unique<SkillTab>("Skills", listbox);
+ mTabbedArea->addTab(tab.get(), scroll.get());
- mTabs->addTab(tab, scroll);
+ mTabs.push_back(std::move(tab));
+ mTabWidgets.push_back(std::move(scroll));
+ mSkillModels.push_back(std::move(model));
update();
}
@@ -365,7 +335,7 @@ void SkillDialog::loadSkills()
setCount++;
setName = set.getProperty("name", strprintf(_("Skill Set %d"), setCount));
- auto *model = new SkillModel();
+ auto model = std::make_unique<SkillModel>();
for (auto node : set.children())
{
@@ -375,34 +345,39 @@ void SkillDialog::loadSkills()
std::string name = node.getProperty("name", strprintf(_("Skill %d"), id));
std::string icon = node.getProperty("icon", "");
- auto *skill = new SkillInfo;
+ auto skill = std::make_unique<SkillInfo>();
skill->id = id;
skill->name = name;
skill->setIcon(icon);
skill->modifiable = false;
skill->visible = false;
- skill->model = model;
+ skill->model = model.get();
skill->update();
- model->addSkill(skill);
+ mSkills[id] = skill.get();
- mSkills[id] = skill;
+ model->addSkill(std::move(skill));
}
}
model->updateVisibilities();
- listbox = new SkillListBox(model);
- scroll = new ScrollArea(listbox);
+ auto listbox = new SkillListBox(model.get());
+ auto scroll = std::make_unique<ScrollArea>(listbox);
scroll->setOpaque(false);
scroll->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER);
scroll->setVerticalScrollPolicy(ScrollArea::SHOW_ALWAYS);
- tab = new SkillTab(setName, listbox);
+ auto tab = std::make_unique<SkillTab>(setName, listbox);
+
+ mTabbedArea->addTab(tab.get(), scroll.get());
- mTabs->addTab(tab, scroll);
+ mTabs.push_back(std::move(tab));
+ mTabWidgets.push_back(std::move(scroll));
+ mSkillModels.push_back(std::move(model));
}
}
+
update();
}
@@ -412,9 +387,9 @@ void SkillDialog::setModifiable(int id, bool modifiable)
if (it != mSkills.end())
{
- SkillInfo *info = it->second;
- info->modifiable = modifiable;
- info->update();
+ SkillInfo &info = *it->second;
+ info.modifiable = modifiable;
+ info.update();
}
}
@@ -423,12 +398,8 @@ void SkillModel::updateVisibilities()
mVisibleSkills.clear();
for (auto &skill : mSkills)
- {
if (skill->visible)
- {
- mVisibleSkills.push_back(skill);
- }
- }
+ mVisibleSkills.push_back(skill.get());
}
void SkillInfo::update()
@@ -504,8 +475,7 @@ void SkillInfo::draw(Graphics *graphics, int y, int width)
if (!skillExp.empty())
{
- gcn::Rectangle rect(33, y + 15, width - 33, 17);
-
- ProgressBar::render(graphics, rect, color, progress, skillExp);
+ const gcn::Rectangle rect(33, y + 15, width - 33, 17);
+ gui->getTheme()->drawProgressBar(graphics, rect, color, progress, skillExp);
}
}
diff --git a/src/gui/skilldialog.h b/src/gui/skilldialog.h
index e88c279f..83dc8dd2 100644
--- a/src/gui/skilldialog.h
+++ b/src/gui/skilldialog.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SKILLDIALOG_H
-#define SKILLDIALOG_H
+#pragma once
#include "gui/widgets/window.h"
#include "eventlistener.h"
@@ -28,10 +27,13 @@
#include <guichan/actionlistener.hpp>
#include <map>
+#include <memory>
+#include <vector>
class Button;
class Label;
class ScrollArea;
+class SkillModel;
class Tab;
class TabbedArea;
@@ -46,7 +48,6 @@ class SkillDialog : public Window, public gcn::ActionListener, public EventListe
{
public:
SkillDialog();
-
~SkillDialog() override;
void event(Event::Channel channel, const Event &event) override;
@@ -75,12 +76,13 @@ class SkillDialog : public Window, public gcn::ActionListener, public EventListe
bool hasSkills() { return !mSkills.empty(); }
private:
- std::map<int, SkillInfo *> mSkills;
- TabbedArea *mTabs;
+ std::vector<std::unique_ptr<SkillModel>> mSkillModels;
+ std::vector<std::unique_ptr<Tab>> mTabs;
+ std::vector<std::unique_ptr<gcn::Widget>> mTabWidgets;
+ std::map<int, SkillInfo*> mSkills;
+ TabbedArea *mTabbedArea;
Label *mPointsLabel;
Button *mIncreaseButton;
};
extern SkillDialog *skillDialog;
-
-#endif
diff --git a/src/gui/socialwindow.cpp b/src/gui/socialwindow.cpp
index 5a15da8f..072d87bc 100644
--- a/src/gui/socialwindow.cpp
+++ b/src/gui/socialwindow.cpp
@@ -34,6 +34,7 @@
#include "gui/widgets/avatarlistbox.h"
#include "gui/widgets/browserbox.h"
#include "gui/widgets/button.h"
+#include "gui/widgets/layout.h"
#include "gui/widgets/linkhandler.h"
#include "gui/widgets/popup.h"
#include "gui/widgets/scrollarea.h"
@@ -236,6 +237,11 @@ private:
class PlayerList : public AvatarListModel
{
public:
+ ~PlayerList() override
+ {
+ delete_all(mPlayers);
+ }
+
void setPlayers(const std::vector<Avatar*> &players)
{
delete_all(mPlayers);
@@ -273,7 +279,7 @@ public:
mScroll->setVerticalScrollPolicy(gcn::ScrollArea::SHOW_AUTO);
}
- ~PlayerListTab()
+ ~PlayerListTab() override
{
delete mPlayerList;
}
@@ -355,13 +361,8 @@ SocialWindow::SocialWindow() :
setResizable(true);
setSaveVisible(true);
setCloseButton(true);
- setMinWidth(120);
- setMinHeight(55);
- setDefaultSize(590, 200, 150, 124);
setupWindow->registerWindowForReset(this);
- loadWindowState();
-
mCreateButton = new Button(_("Create"), "create", this);
mInviteButton = new Button(_("Invite"), "invite", this);
mLeaveButton = new Button(_("Leave"), "leave", this);
@@ -372,19 +373,23 @@ SocialWindow::SocialWindow() :
place(2, 0, mLeaveButton);
place(0, 1, mTabs, 4, 4);
- widgetResized(nullptr);
+ // Determine minimum size
+ int width = 0, height = 0;
+ getLayout().reflow(width, height);
+ setMinimumContentSize(width, height);
+
+ setDefaultSize(590, 200, 150, 124);
+ loadWindowState();
mCreatePopup = new CreatePopup;
mPlayerListTab = new PlayerListTab;
- mPlayerListTab->setCaption(strprintf(_("Online (%zu)"), 0ul));
+ mPlayerListTab->setCaption(strprintf(_("Online (%u)"), 0u));
mTabs->addTab(mPlayerListTab, mPlayerListTab->mScroll.get());
if (local_player->getParty())
- {
addTab(local_player->getParty());
- }
else
updateButtons();
}
@@ -673,7 +678,9 @@ void SocialWindow::showPartyCreate()
void SocialWindow::setPlayersOnline(const std::vector<Avatar*> &players)
{
mPlayerListTab->setPlayers(players);
- mPlayerListTab->setCaption(strprintf(_("Online (%zu)"), players.size()));
+
+ unsigned playerCount = static_cast<unsigned>(players.size());
+ mPlayerListTab->setCaption(strprintf(_("Online (%u)"), playerCount));
}
void SocialWindow::logic()
diff --git a/src/gui/socialwindow.h b/src/gui/socialwindow.h
index 350f919c..d23f1b86 100644
--- a/src/gui/socialwindow.h
+++ b/src/gui/socialwindow.h
@@ -18,8 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SOCIALWINDOW_H
-#define SOCIALWINDOW_H
+#pragma once
#include "utils/time.h"
@@ -111,5 +110,3 @@ protected:
};
extern SocialWindow *socialWindow;
-
-#endif // SOCIALWINDOW_H
diff --git a/src/gui/speechbubble.cpp b/src/gui/speechbubble.cpp
index 58747d57..72ec8bd2 100644
--- a/src/gui/speechbubble.cpp
+++ b/src/gui/speechbubble.cpp
@@ -27,14 +27,11 @@
#include "gui/widgets/label.h"
#include "gui/widgets/textbox.h"
-#include "resources/theme.h"
-
#include <guichan/font.hpp>
-
#include <guichan/widgets/label.hpp>
-SpeechBubble::SpeechBubble():
- Popup("Speech", "speechbubble.xml")
+SpeechBubble::SpeechBubble()
+ : Popup("Speech", SkinType::SpeechBubble)
{
setMinWidth(0);
setMinHeight(0);
@@ -45,7 +42,7 @@ SpeechBubble::SpeechBubble():
mSpeechBox = new TextBox;
mSpeechBox->setEditable(false);
mSpeechBox->setOpaque(false);
- mSpeechBox->setTextColor(&Theme::getThemeColor(Theme::CHAT));
+ mSpeechBox->setTextColor(&Theme::getThemeColor(Theme::BUBBLE_TEXT));
add(mCaption);
add(mSpeechBox);
@@ -60,11 +57,9 @@ void SpeechBubble::setCaption(const std::string &name, const gcn::Color *color)
void SpeechBubble::setText(const std::string &text, bool showName)
{
- if (text == mText && (mCaption->getWidth() <= mSpeechBox->getMinWidth()))
+ if (text == mText && mCaption->getWidth() <= mSpeechBox->getMinWidth())
return;
- mSpeechBox->setTextColor(&Theme::getThemeColor(Theme::TEXT));
-
int width = mCaption->getWidth();
mSpeechBox->setTextWrapped(text, 130 > width ? 130 : width);
const int speechWidth = mSpeechBox->getMinWidth();
diff --git a/src/gui/speechbubble.h b/src/gui/speechbubble.h
index da677dcd..fbb1a21e 100644
--- a/src/gui/speechbubble.h
+++ b/src/gui/speechbubble.h
@@ -20,8 +20,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SPEECHBUBBLE_H
-#define SPEECHBUBBLE_H
+#pragma once
#include "gui/widgets/popup.h"
@@ -51,5 +50,3 @@ class SpeechBubble : public Popup
gcn::Label *mCaption;
TextBox *mSpeechBox;
};
-
-#endif
diff --git a/src/gui/statuswindow.cpp b/src/gui/statuswindow.cpp
index ffcfa350..108d68cc 100644
--- a/src/gui/statuswindow.cpp
+++ b/src/gui/statuswindow.cpp
@@ -290,7 +290,7 @@ void StatusWindow::event(Event::Channel channel,
it->second->update();
}
- if (Net::getNetworkType() == ServerType::TMWATHENA &&
+ if (Net::getNetworkType() == ServerType::TmwAthena &&
id == TmwAthena::MATK)
{
updateMPBar(mMpBar, true);
diff --git a/src/gui/statuswindow.h b/src/gui/statuswindow.h
index 99c3b46a..fc7ad67c 100644
--- a/src/gui/statuswindow.h
+++ b/src/gui/statuswindow.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef STATUS_H
-#define STATUS_H
+#pragma once
#include "eventlistener.h"
@@ -85,5 +84,3 @@ class StatusWindow : public Window, public EventListener
};
extern StatusWindow *statusWindow;
-
-#endif
diff --git a/src/gui/textdialog.h b/src/gui/textdialog.h
index 66ab3e53..d9634434 100644
--- a/src/gui/textdialog.h
+++ b/src/gui/textdialog.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GUI_GUILD_DIALOG_H
-#define GUI_GUILD_DIALOG_H
+#pragma once
#include "gui/widgets/window.h"
@@ -59,5 +58,3 @@ private:
TextField *mTextField;
gcn::Button *mOkButton;
};
-
-#endif
diff --git a/src/gui/textpopup.h b/src/gui/textpopup.h
index b1c6b2b3..db7d48e7 100644
--- a/src/gui/textpopup.h
+++ b/src/gui/textpopup.h
@@ -21,8 +21,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#ifndef TEXTPOPUP_H
-#define TEXTPOPUP_H
+#pragma once
#include "gui/widgets/popup.h"
@@ -51,5 +50,3 @@ class TextPopup : public Popup
gcn::Label *mText1;
gcn::Label *mText2;
};
-
-#endif // TEXTPOPUP_H
diff --git a/src/gui/tradewindow.h b/src/gui/tradewindow.h
index e9d8d4f1..d54ec35e 100644
--- a/src/gui/tradewindow.h
+++ b/src/gui/tradewindow.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef TRADE_H
-#define TRADE_H
+#pragma once
#include "gui/widgets/window.h"
@@ -134,5 +133,3 @@ class TradeWindow : public Window, gcn::ActionListener, gcn::SelectionListener
};
extern TradeWindow *tradeWindow;
-
-#endif
diff --git a/src/gui/truetypefont.cpp b/src/gui/truetypefont.cpp
index 30037f84..444641b5 100644
--- a/src/gui/truetypefont.cpp
+++ b/src/gui/truetypefont.cpp
@@ -56,16 +56,16 @@ bool operator==(SDL_Color lhs, SDL_Color rhs)
class TextChunk
{
public:
- TextChunk(const std::string &text, SDL_Color color)
+ TextChunk(const std::string &text)
: text(text)
- , color(color)
{}
void generate(TTF_Font *font)
{
+ // Always render in white, we'll use color modulation when rendering
SDL_Surface *surface = TTF_RenderUTF8_Blended(font,
getSafeUtf8String(text),
- color);
+ SDL_Color { 255, 255, 255, 255 });
if (!surface)
return;
@@ -77,7 +77,6 @@ public:
std::unique_ptr<Image> img;
const std::string text;
- const SDL_Color color;
};
std::list<TrueTypeFont*> TrueTypeFont::mFonts;
@@ -124,24 +123,13 @@ void TrueTypeFont::drawString(gcn::Graphics *graphics,
return;
auto *g = static_cast<Graphics *>(graphics);
- const gcn::Color col = g->getColor();
-
- /* The alpha value is ignored at image generation to avoid caching the
- * same text with different alpha values.
- */
- const SDL_Color color = {
- static_cast<Uint8>(col.r),
- static_cast<Uint8>(col.g),
- static_cast<Uint8>(col.b),
- 255
- };
bool found = false;
for (auto i = mCache.begin(); i != mCache.end(); ++i)
{
auto &chunk = *i;
- if (chunk.text == text && chunk.color == color)
+ if (chunk.text == text)
{
// Raise priority: move it to front
mCache.splice(mCache.begin(), mCache, i);
@@ -154,18 +142,17 @@ void TrueTypeFont::drawString(gcn::Graphics *graphics,
{
if (mCache.size() >= CACHE_SIZE)
mCache.pop_back();
- mCache.emplace_front(text, color);
+ mCache.emplace_front(text);
mCache.front().generate(mFont);
}
if (auto img = mCache.front().img.get())
{
- img->setAlpha(col.a / 255.0f);
g->drawRescaledImageF(img, 0, 0, x, y,
img->getWidth(),
img->getHeight(),
img->getWidth() / mScale,
- img->getHeight() / mScale);
+ img->getHeight() / mScale, true);
}
}
diff --git a/src/gui/truetypefont.h b/src/gui/truetypefont.h
index 9aa308b1..a479537d 100644
--- a/src/gui/truetypefont.h
+++ b/src/gui/truetypefont.h
@@ -20,8 +20,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef TRUETYPEFONT_H
-#define TRUETYPEFONT_H
+#pragma once
#include <guichan/font.hpp>
@@ -82,5 +81,3 @@ class TrueTypeFont : public gcn::Font
static std::list<TrueTypeFont*> mFonts;
static float mScale;
};
-
-#endif
diff --git a/src/gui/unregisterdialog.h b/src/gui/unregisterdialog.h
index c82d7a78..0c94e0af 100644
--- a/src/gui/unregisterdialog.h
+++ b/src/gui/unregisterdialog.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef UNREGISTERDIALOG_H
-#define UNREGISTERDIALOG_H
+#pragma once
#include "gui/widgets/window.h"
@@ -57,5 +56,3 @@ class UnRegisterDialog : public Window, public gcn::ActionListener
LoginData *mLoginData;
};
-
-#endif
diff --git a/src/gui/updaterwindow.cpp b/src/gui/updaterwindow.cpp
index 772df725..5cfb45cd 100644
--- a/src/gui/updaterwindow.cpp
+++ b/src/gui/updaterwindow.cpp
@@ -46,8 +46,8 @@
#include <iostream>
#include <fstream>
-const std::string xmlUpdateFile = "resources.xml";
-const std::string txtUpdateFile = "resources2.txt";
+constexpr char xmlUpdateFile[] = "resources.xml";
+constexpr char txtUpdateFile[] = "resources2.txt";
/**
* Load the given file into a vector of updateFiles.
@@ -123,7 +123,6 @@ UpdaterWindow::UpdaterWindow(const std::string &updateHost,
Window(_("Updating...")),
mUpdateHost(updateHost),
mUpdatesDir(updatesDir),
- mCurrentFile("news.txt"),
mLoadUpdates(applyUpdates),
mLinkHandler(std::make_unique<ItemLinkHandler>(this))
{
@@ -141,7 +140,6 @@ UpdaterWindow::UpdaterWindow(const std::string &updateHost,
mPlayButton = new Button(_("Play"), "play", this);
mBrowserBox->setLinkHandler(mLinkHandler.get());
- mBrowserBox->setFrameSize(4);
mProgressBar->setSmoothProgress(false);
mScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER);
mPlayButton->setEnabled(false);
@@ -161,40 +159,22 @@ UpdaterWindow::UpdaterWindow(const std::string &updateHost,
setVisible(true);
mCancelButton->requestFocus();
- // Try to download the updates list
- download();
+ startDownload("news.txt", true);
}
UpdaterWindow::~UpdaterWindow()
{
if (mLoadUpdates)
loadUpdates();
-
- if (mDownload)
- {
- mDownload->cancel();
-
- delete mDownload;
- mDownload = nullptr;
- }
- free(mMemoryBuffer);
-}
-
-void UpdaterWindow::setProgress(float progress)
-{
- // Do delayed progress bar update, since Guichan isn't thread-safe
- MutexLocker lock(&mDownloadMutex);
- mDownloadProgress = progress;
}
void UpdaterWindow::setLabel(const std::string &str)
{
- // Do delayed label text update, since Guichan isn't thread-safe
- MutexLocker lock(&mDownloadMutex);
- mNewLabelCaption = str;
+ mLabel->setCaption(str);
+ mLabel->adjustSize();
}
-void UpdaterWindow::enable()
+void UpdaterWindow::enablePlay()
{
mCancelButton->setEnabled(false);
mPlayButton->setEnabled(true);
@@ -204,20 +184,9 @@ void UpdaterWindow::enable()
void UpdaterWindow::action(const gcn::ActionEvent &event)
{
if (event.getId() == "cancel")
- {
- // Register the user cancel
- mUserCancel = true;
- // Skip the updating process
- if (mDownloadStatus != UPDATE_COMPLETE)
- {
- mDownload->cancel();
- mDownloadStatus = UPDATE_ERROR;
- }
- }
+ cancel();
else if (event.getId() == "play")
- {
- Client::setState(STATE_LOAD_DATA);
- }
+ play();
}
void UpdaterWindow::keyPressed(gcn::KeyEvent &keyEvent)
@@ -226,140 +195,63 @@ void UpdaterWindow::keyPressed(gcn::KeyEvent &keyEvent)
if (key.getValue() == Key::ESCAPE)
{
- action(gcn::ActionEvent(nullptr, mCancelButton->getActionEventId()));
- Client::setState(STATE_WORLD_SELECT);
+ if (!cancel())
+ {
+ mLoadUpdates = false;
+ Client::setState(STATE_WORLD_SELECT);
+ }
}
else if (key.getValue() == Key::ENTER)
{
- if (mDownloadStatus == UPDATE_COMPLETE ||
- mDownloadStatus == UPDATE_ERROR)
- {
- action(gcn::ActionEvent(nullptr, mPlayButton->getActionEventId()));
- }
- else
- {
- action(gcn::ActionEvent(nullptr, mCancelButton->getActionEventId()));
- }
+ play();
}
}
-void UpdaterWindow::loadNews()
+bool UpdaterWindow::cancel()
{
- if (!mMemoryBuffer)
+ // Skip the updating process
+ if (mDialogState != DialogState::Done)
{
- logger->log("Couldn't load news");
- return;
- }
-
- // Reallocate and include terminating 0 character
- mMemoryBuffer = (char*)realloc(mMemoryBuffer, mDownloadedBytes + 1);
- mMemoryBuffer[mDownloadedBytes] = '\0';
-
- mBrowserBox->clearRows();
-
- // Tokenize and add each line separately
- char *line = strtok(mMemoryBuffer, "\n");
- while (line)
- {
- mBrowserBox->addRow(line);
- line = strtok(nullptr, "\n");
+ mDownload->cancel();
+ return true;
}
-
- // Free the memory buffer now that we don't need it anymore
- free(mMemoryBuffer);
- mMemoryBuffer = nullptr;
-
- mScrollArea->setVerticalScrollAmount(0);
+ return false;
}
-int UpdaterWindow::updateProgress(void *ptr, DownloadStatus status,
- size_t dt, size_t dn)
+void UpdaterWindow::play()
{
- auto *uw = reinterpret_cast<UpdaterWindow *>(ptr);
-
- if (status == DOWNLOAD_STATUS_COMPLETE)
- {
- uw->mDownloadComplete = true;
- }
- else if (status == DOWNLOAD_STATUS_ERROR ||
- status == DOWNLOAD_STATUS_CANCELLED)
- {
- uw->mDownloadStatus = UPDATE_ERROR;
- }
-
- float progress = (float) dn / dt;
-
- if (progress != progress)
- progress = 0.0f; // check for NaN
- if (progress < 0.0f)
- progress = 0.0f; // no idea how this could ever happen, but why not check for it anyway.
- if (progress > 1.0f)
- progress = 1.0f;
-
- uw->setLabel(
- uw->mCurrentFile + " (" + toString((int) (progress * 100)) + "%)");
- uw->setProgress(progress);
-
- if (Client::getState() != STATE_UPDATE || uw->mDownloadStatus == UPDATE_ERROR)
- {
- // If the action was canceled return an error code to stop the mThread
- return -1;
- }
-
- return 0;
+ if (mPlayButton->isEnabled())
+ Client::setState(STATE_LOAD_DATA);
}
-size_t UpdaterWindow::memoryWrite(void *ptr, size_t size, size_t nmemb, void *stream)
+void UpdaterWindow::loadNews()
{
- auto *uw = reinterpret_cast<UpdaterWindow *>(stream);
- size_t totalMem = size * nmemb;
- uw->mMemoryBuffer = (char*) realloc(uw->mMemoryBuffer,
- uw->mDownloadedBytes + totalMem);
- if (uw->mMemoryBuffer)
- {
- memcpy(&(uw->mMemoryBuffer[uw->mDownloadedBytes]), ptr, totalMem);
- uw->mDownloadedBytes += totalMem;
- }
+ mBrowserBox->clearRows();
+ mBrowserBox->addRows(mDownload->getBuffer());
- return totalMem;
+ mScrollArea->setVerticalScrollAmount(0);
}
-void UpdaterWindow::download()
+void UpdaterWindow::startDownload(const std::string &fileName,
+ bool storeInMemory,
+ std::optional<unsigned long> adler32)
{
- mDownload = new Net::Download(this, mUpdateHost + "/" + mCurrentFile,
- updateProgress);
+ mDownload = std::make_unique<Net::Download>(mUpdateHost + "/" + fileName);
+ mCurrentFile = fileName;
- if (mStoreInMemory)
- {
- mDownload->setWriteFunction(UpdaterWindow::memoryWrite);
- }
+ if (storeInMemory)
+ mDownload->setUseBuffer();
else
- {
- if (mDownloadStatus == UPDATE_RESOURCES)
- {
- mDownload->setFile(mUpdatesDir + "/" + mCurrentFile,
- mCurrentChecksum);
- }
- else
- {
- mDownload->setFile(mUpdatesDir + "/" + mCurrentFile);
- }
- }
+ mDownload->setFile(mUpdatesDir + "/" + fileName, adler32);
- if (mDownloadStatus != UPDATE_RESOURCES)
+ if (mDialogState != DialogState::DownloadResources)
mDownload->noCache();
- setLabel(mCurrentFile + " (0%)");
- mDownloadComplete = false;
-
- // TODO: check return
mDownload->start();
}
void UpdaterWindow::loadUpdates()
{
- ResourceManager *resman = ResourceManager::getInstance();
-
if (mUpdateFiles.empty())
{
// updates not downloaded
@@ -367,142 +259,144 @@ void UpdaterWindow::loadUpdates()
if (mUpdateFiles.empty())
{
logger->log("Warning this server does not have a"
- " %s file falling back to %s", xmlUpdateFile.c_str(),
- txtUpdateFile.c_str());
+ " %s file falling back to %s", xmlUpdateFile,
+ txtUpdateFile);
mUpdateFiles = loadTxtFile(mUpdatesDir + "/" + txtUpdateFile);
}
}
for (const UpdateFile &file : mUpdateFiles)
- {
- resman->addToSearchPath(mUpdatesDir + "/" + file.name, false);
- }
+ ResourceManager::addToSearchPath(mUpdatesDir + "/" + file.name, false);
}
void UpdaterWindow::logic()
{
- const std::string xmlUpdateFile = "resources.xml";
- const std::string txtUpdateFile = "resources2.txt";
+ Window::logic();
- // Update Scroll logic
- mScrollArea->logic();
+ if (mDialogState == DialogState::Done)
+ return;
- // Synchronize label caption when necessary
- {
- MutexLocker lock(&mDownloadMutex);
+ const auto state = mDownload->getState();
+ float progress = 0.0f;
- if (mLabel->getCaption() != mNewLabelCaption)
- {
- mLabel->setCaption(mNewLabelCaption);
- mLabel->adjustSize();
- }
+ switch (state.status) {
+ case DownloadStatus::InProgress: {
+ setLabel(mCurrentFile + " (" + toString((int) (state.progress * 100)) + "%)");
+ progress = state.progress;
+ break;
+ }
+
+ case DownloadStatus::Canceled:
+ mDialogState = DialogState::Done;
+
+ enablePlay();
+ setLabel(_("Download canceled"));
+ break;
+
+ case DownloadStatus::Error: {
+ mDialogState = DialogState::Done;
- mProgressBar->setProgress(mDownloadProgress);
+ std::string error = "##1";
+ error += mDownload->getError();
+ error += "\n\n##1";
+ error += _("The update process is incomplete. "
+ "It is strongly recommended that you try again later.");
+ mBrowserBox->addRows(error);
+
+ int maxScroll = mScrollArea->getVerticalMaxScroll();
+ mScrollArea->setVerticalScrollAmount(maxScroll);
+
+ enablePlay();
+ setLabel(_("Error while downloading"));
+ break;
}
- switch (mDownloadStatus)
+ case DownloadStatus::Complete:
+ downloadCompleted();
+ break;
+ }
+
+ mProgressBar->setProgress(progress);
+}
+
+void UpdaterWindow::downloadCompleted()
+{
+ switch (mDialogState)
{
- case UPDATE_ERROR:
- // TODO: Only send complete sentences to gettext
- mBrowserBox->addRow(std::string());
- mBrowserBox->addRow(_("##1 The update process is incomplete."));
- // TRANSLATORS: Continues "you try again later.".
- mBrowserBox->addRow(_("##1 It is strongly recommended that"));
- // TRANSLATORS: Begins "It is strongly recommended that".
- mBrowserBox->addRow(_("##1 you try again later."));
-
- mBrowserBox->addRow(mDownload->getError());
- mScrollArea->setVerticalScrollAmount(
- mScrollArea->getVerticalMaxScroll());
- mDownloadStatus = UPDATE_COMPLETE;
- break;
- case UPDATE_NEWS:
- if (mDownloadComplete)
- {
- // Parse current memory buffer as news and dispose of the data
- loadNews();
+ case DialogState::DownloadNews:
+ loadNews();
- mCurrentFile = xmlUpdateFile;
- mStoreInMemory = false;
- mDownloadStatus = UPDATE_LIST;
- download(); // download() changes mDownloadComplete to false
- }
- break;
- case UPDATE_LIST:
- if (mDownloadComplete)
+ mDialogState = DialogState::DownloadList;
+ startDownload(xmlUpdateFile, false);
+ break;
+
+ case DialogState::DownloadList:
+ if (mCurrentFile == xmlUpdateFile)
+ {
+ mUpdateFiles = loadXMLFile(mUpdatesDir + "/" + xmlUpdateFile);
+ if (mUpdateFiles.empty())
{
- if (mCurrentFile == xmlUpdateFile)
- {
- mUpdateFiles = loadXMLFile(mUpdatesDir + "/" + xmlUpdateFile);
- if (mUpdateFiles.empty())
- {
- logger->log("Warning this server does not have a %s"
- " file falling back to %s",
- xmlUpdateFile.c_str(), txtUpdateFile.c_str());
-
- // If the resources.xml file fails, fall back onto a older version
- mCurrentFile = txtUpdateFile;
- mStoreInMemory = false;
- mDownloadStatus = UPDATE_LIST;
- download();
- break;
- }
- }
- else if (mCurrentFile == txtUpdateFile)
- {
- mUpdateFiles = loadTxtFile(mUpdatesDir + "/" + txtUpdateFile);
- }
- mStoreInMemory = false;
- mDownloadStatus = UPDATE_RESOURCES;
+ logger->log("Warning this server does not have a %s"
+ " file falling back to %s",
+ xmlUpdateFile, txtUpdateFile);
+
+ // If the resources.xml file fails, fall back onto a older version
+ mDialogState = DialogState::DownloadList;
+ startDownload(txtUpdateFile, false);
+ break;
}
- break;
- case UPDATE_RESOURCES:
- if (mDownloadComplete)
+ }
+ else if (mCurrentFile == txtUpdateFile)
+ {
+ mUpdateFiles = loadTxtFile(mUpdatesDir + "/" + txtUpdateFile);
+ }
+
+ mDialogState = DialogState::DownloadResources;
+ break;
+
+ case DialogState::DownloadResources:
+ if (mUpdateIndex < mUpdateFiles.size())
+ {
+ const UpdateFile &thisFile = mUpdateFiles[mUpdateIndex];
+ if (!thisFile.required)
{
- if (mUpdateIndex < mUpdateFiles.size())
+ if (!(thisFile.type == "music" && config.downloadMusic))
{
- const UpdateFile &thisFile = mUpdateFiles[mUpdateIndex];
- if (!thisFile.required)
- {
- if (!(thisFile.type == "music" && config.downloadMusic))
- {
- mUpdateIndex++;
- break;
- }
- }
- mCurrentFile = thisFile.name;
- std::stringstream ss(thisFile.hash);
- ss >> std::hex >> mCurrentChecksum;
-
- std::string filename = mUpdatesDir + "/" + mCurrentFile;
- FILE *file = fopen(filename.c_str(), "r+b");
-
- if (!file || Net::Download::fadler32(file) != mCurrentChecksum)
- {
- if (file)
- fclose(file);
- download();
- }
- else
- {
- fclose(file);
- logger->log("%s already here", mCurrentFile.c_str());
- }
mUpdateIndex++;
- }
- else
- {
- // Download of updates completed
- mDownloadStatus = UPDATE_COMPLETE;
+ break;
}
}
- break;
- case UPDATE_COMPLETE:
- enable();
+
+ unsigned long checksum;
+ std::stringstream ss(thisFile.hash);
+ ss >> std::hex >> checksum;
+
+ std::string filename = mUpdatesDir + "/" + thisFile.name;
+ FILE *file = fopen(filename.c_str(), "r+b");
+
+ if (!file || Net::Download::fadler32(file) != checksum)
+ {
+ if (file)
+ fclose(file);
+ startDownload(thisFile.name, false, checksum);
+ }
+ else
+ {
+ fclose(file);
+ logger->log("%s already here", thisFile.name.c_str());
+ }
+ mUpdateIndex++;
+ }
+ else
+ {
+ // Download of updates completed
+ mDialogState = DialogState::Done;
+ enablePlay();
setLabel(_("Completed"));
- mDownloadStatus = UPDATE_IDLE;
- break;
- case UPDATE_IDLE:
- break;
+ }
+ break;
+
+ case DialogState::Done:
+ break;
}
}
diff --git a/src/gui/updaterwindow.h b/src/gui/updaterwindow.h
index 6ea1d754..dd1400ba 100644
--- a/src/gui/updaterwindow.h
+++ b/src/gui/updaterwindow.h
@@ -19,15 +19,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef UPDATERWINDOW_H
-#define UPDATERWINDOW_H
+#pragma once
#include "gui/widgets/window.h"
#include "net/download.h"
-#include "utils/mutex.h"
-
#include <guichan/actionlistener.hpp>
#include <guichan/keylistener.hpp>
@@ -65,7 +62,7 @@ class UpdaterWindow : public Window, public gcn::ActionListener,
* @param updateHost Host where to get the updated files.
* @param updatesDir Directory where to store updates (should be absolute
* and already created).
- * @param applyUpdates If true, the update window will pass the updates to teh
+ * @param applyUpdates If true, the update window will pass the updates to the
* resource manager
*/
UpdaterWindow(const std::string &updateHost,
@@ -74,27 +71,6 @@ class UpdaterWindow : public Window, public gcn::ActionListener,
~UpdaterWindow() override;
- /**
- * Set's progress bar status
- */
- void setProgress(float progress);
-
- /**
- * Set's label above progress
- */
- void setLabel(const std::string &);
-
- /**
- * Enables play button
- */
- void enable();
-
- /**
- * Loads and display news. Assumes the news file contents have been loaded
- * into the memory buffer.
- */
- void loadNews();
-
void action(const gcn::ActionEvent &event) override;
void keyPressed(gcn::KeyEvent &keyEvent) override;
@@ -102,38 +78,38 @@ class UpdaterWindow : public Window, public gcn::ActionListener,
void logic() override;
private:
- void download();
+ bool cancel();
+ void play();
- /**
- * Loads the updates this window has gotten into the resource manager
- */
- void loadUpdates();
+ void setLabel(const std::string &);
+ void enablePlay();
+ void startDownload(const std::string &fileName,
+ bool storeInMemory,
+ std::optional<unsigned long> adler32 = {});
+ void downloadCompleted();
/**
- * A download callback for progress updates.
+ * Loads and display news. Assumes the news file contents have been loaded
+ * into the memory buffer.
*/
- static int updateProgress(void *ptr, DownloadStatus status,
- size_t dt, size_t dn);
+ void loadNews();
/**
- * A libcurl callback for writing to memory.
+ * Loads the updates this window has gotten into the resource manager
*/
- static size_t memoryWrite(void *ptr, size_t size, size_t nmemb,
- void *stream);
+ void loadUpdates();
- enum UpdateDownloadStatus
+ enum class DialogState
{
- UPDATE_ERROR,
- UPDATE_IDLE,
- UPDATE_LIST,
- UPDATE_COMPLETE,
- UPDATE_NEWS,
- UPDATE_RESOURCES
+ DownloadNews,
+ DownloadList,
+ DownloadResources,
+ Done,
};
/** Status of the current download. */
- UpdateDownloadStatus mDownloadStatus = UPDATE_NEWS;
+ DialogState mDialogState = DialogState::DownloadNews;
/** Host where we get the updated files. */
std::string mUpdateHost;
@@ -144,35 +120,8 @@ private:
/** The file currently downloading. */
std::string mCurrentFile;
- /** The new label caption to be set in the logic method. */
- std::string mNewLabelCaption;
-
- /** The new progress value to be set in the logic method. */
- float mDownloadProgress = 0.0f;
-
- /** The mutex used to guard access to mNewLabelCaption and mDownloadProgress. */
- Mutex mDownloadMutex;
-
- /** The Adler32 checksum of the file currently downloading. */
- unsigned long mCurrentChecksum = 0;
-
- /** A flag to indicate whether to use a memory buffer or a regular file. */
- bool mStoreInMemory = true;
-
- /** Flag that show if current download is complete. */
- bool mDownloadComplete = true;
-
- /** Flag that show if the user has canceled the update. */
- bool mUserCancel = false;
-
- /** Byte count currently downloaded in mMemoryBuffer. */
- int mDownloadedBytes = 0;
-
- /** Buffer for files downloaded to memory. */
- char *mMemoryBuffer = nullptr;
-
/** Download handle. */
- Net::Download *mDownload = nullptr;
+ std::unique_ptr<Net::Download> mDownload;
/** List of files to download. */
std::vector<UpdateFile> mUpdateFiles;
@@ -191,5 +140,3 @@ private:
ScrollArea *mScrollArea; /**< Used to scroll news box. */
std::unique_ptr<LinkHandler> mLinkHandler;
};
-
-#endif
diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp
index 558ce0e6..9e529063 100644
--- a/src/gui/viewport.cpp
+++ b/src/gui/viewport.cpp
@@ -32,7 +32,6 @@
#include "textmanager.h"
#include "gui/gui.h"
-#include "gui/ministatuswindow.h"
#include "gui/popupmenu.h"
#include "gui/beingpopup.h"
@@ -237,9 +236,6 @@ void Viewport::draw(gcn::Graphics *gcnGraphics)
}
}
- if (miniStatusWindow)
- miniStatusWindow->drawIcons(graphics);
-
// Draw contained widgets
WindowContainer::draw(gcnGraphics);
}
@@ -309,10 +305,10 @@ void Viewport::_drawDebugPath(Graphics *graphics)
unsigned char walkMask;
switch (Net::getNetworkType())
{
- case ServerType::TMWATHENA:
+ case ServerType::TmwAthena:
walkMask = Map::BLOCKMASK_WALL | Map::BLOCKMASK_CHARACTER;
break;
- case ServerType::MANASERV:
+ case ServerType::ManaServ:
default:
walkMask = Map::BLOCKMASK_WALL;
break;
@@ -579,18 +575,18 @@ void Viewport::updateCursorType()
gui->setCursorType(mHoverBeing->getHoverCursor());
break;
default:
- gui->setCursorType(Cursor::POINTER);
+ gui->setCursorType(Cursor::Pointer);
break;
}
// Item mouseover
}
else if (mHoverItem)
{
- gui->setCursorType(Cursor::PICKUP);
+ gui->setCursorType(Cursor::PickUp);
}
else
{
- gui->setCursorType(Cursor::POINTER);
+ gui->setCursorType(Cursor::Pointer);
}
}
diff --git a/src/gui/viewport.h b/src/gui/viewport.h
index b69a73b6..bf73f8ca 100644
--- a/src/gui/viewport.h
+++ b/src/gui/viewport.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef VIEWPORT_H
-#define VIEWPORT_H
+#pragma once
#include "eventlistener.h"
#include "position.h"
@@ -221,5 +220,3 @@ class Viewport : public WindowContainer, public gcn::MouseListener,
};
extern Viewport *viewport; /**< The viewport. */
-
-#endif
diff --git a/src/gui/widgets/avatarlistbox.cpp b/src/gui/widgets/avatarlistbox.cpp
index ec3327b2..f7d6e801 100644
--- a/src/gui/widgets/avatarlistbox.cpp
+++ b/src/gui/widgets/avatarlistbox.cpp
@@ -33,8 +33,8 @@
#include <guichan/font.hpp>
int AvatarListBox::instances = 0;
-Image *AvatarListBox::onlineIcon = nullptr;
-Image *AvatarListBox::offlineIcon = nullptr;
+ResourceRef<Image> AvatarListBox::onlineIcon;
+ResourceRef<Image> AvatarListBox::offlineIcon;
AvatarListBox::AvatarListBox(AvatarListModel *model):
ListBox(model)
@@ -56,8 +56,8 @@ AvatarListBox::~AvatarListBox()
if (instances == 0)
{
- onlineIcon->decRef();
- offlineIcon->decRef();
+ onlineIcon = nullptr;
+ offlineIcon = nullptr;
}
}
@@ -66,14 +66,12 @@ void AvatarListBox::draw(gcn::Graphics *gcnGraphics)
if (!mListModel)
return;
- auto* model = static_cast<AvatarListModel*>(mListModel);
+ auto *model = static_cast<AvatarListModel *>(mListModel);
+ auto *graphics = static_cast<Graphics *>(gcnGraphics);
- updateAlpha();
+ const int alpha = gui->getTheme()->getGuiAlpha();
- auto *graphics = static_cast<Graphics*>(gcnGraphics);
-
- graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT,
- (int) (mAlpha * 255.0f)));
+ graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT, alpha));
graphics->setFont(getFont());
const int fontHeight = getFont()->getHeight();
diff --git a/src/gui/widgets/avatarlistbox.h b/src/gui/widgets/avatarlistbox.h
index 7ee36d1e..9b0588ac 100644
--- a/src/gui/widgets/avatarlistbox.h
+++ b/src/gui/widgets/avatarlistbox.h
@@ -18,12 +18,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GUI_GUILDLISTBOX_H
-#define GUI_GUILDLISTBOX_H
+#pragma once
#include "avatar.h"
#include "gui/widgets/listbox.h"
+#include "resources/resource.h"
#include <string>
@@ -54,8 +54,6 @@ public:
private:
static int instances;
- static Image *onlineIcon;
- static Image *offlineIcon;
+ static ResourceRef<Image> onlineIcon;
+ static ResourceRef<Image> offlineIcon;
};
-
-#endif
diff --git a/src/gui/widgets/browserbox.cpp b/src/gui/widgets/browserbox.cpp
index 9eee9448..91366720 100644
--- a/src/gui/widgets/browserbox.cpp
+++ b/src/gui/widgets/browserbox.cpp
@@ -22,6 +22,9 @@
#include "gui/widgets/browserbox.h"
+#include "keyboardconfig.h"
+#include "textrenderer.h"
+
#include "gui/gui.h"
#include "gui/truetypefont.h"
#include "gui/widgets/linkhandler.h"
@@ -38,6 +41,38 @@
#include <algorithm>
+/**
+ * Check for key replacements in format "###key;"
+ */
+static void replaceKeys(std::string &text)
+{
+ auto keyStart = text.find("###");
+
+ while (keyStart != std::string::npos)
+ {
+ const auto keyEnd = text.find(";", keyStart + 3);
+ if (keyEnd == std::string::npos)
+ break;
+
+ std::string_view key(text.data() + keyStart + 3, keyEnd - keyStart - 3);
+
+ // Remove "key" prefix
+ if (key.size() > 3 && key.substr(0, 3) == "key")
+ key.remove_prefix(3);
+
+ const auto keyName = keyboard.getKeyName(key);
+ if (!keyName.empty())
+ {
+ text.replace(keyStart, keyEnd - keyStart + 1, keyName);
+ keyStart = text.find("###", keyStart + keyName.size());
+ }
+ else
+ {
+ keyStart = text.find("###", keyEnd + 1);
+ }
+ }
+}
+
struct LayoutContext
{
LayoutContext(gcn::Font *font);
@@ -75,28 +110,38 @@ BrowserBox::BrowserBox(Mode mode):
BrowserBox::~BrowserBox() = default;
-void BrowserBox::addRow(const std::string &row)
+void BrowserBox::addRows(std::string_view rows)
+{
+ std::string_view::size_type start = 0;
+ std::string_view::size_type end = 0;
+ while (end != std::string::npos)
+ {
+ end = rows.find('\n', start);
+ addRow(rows.substr(start, end - start));
+ start = end + 1;
+ }
+}
+
+void BrowserBox::addRow(std::string_view row)
{
TextRow &newRow = mTextRows.emplace_back();
// Use links and user defined colors
if (mUseLinksAndUserColors)
{
- std::string tmp = row;
-
// Check for links in format "@@link|Caption@@"
- auto idx1 = tmp.find("@@");
- while (idx1 != std::string::npos)
+ auto linkStart = row.find("@@");
+ while (linkStart != std::string::npos)
{
- const auto idx2 = tmp.find("|", idx1);
- const auto idx3 = tmp.find("@@", idx2);
+ const auto linkSep = row.find("|", linkStart);
+ const auto linkEnd = row.find("@@", linkSep);
- if (idx2 == std::string::npos || idx3 == std::string::npos)
+ if (linkSep == std::string::npos || linkEnd == std::string::npos)
break;
BrowserLink &link = newRow.links.emplace_back();
- link.link = tmp.substr(idx1 + 2, idx2 - (idx1 + 2));
- link.caption = tmp.substr(idx2 + 1, idx3 - (idx2 + 1));
+ link.link = row.substr(linkStart + 2, linkSep - (linkStart + 2));
+ link.caption = row.substr(linkSep + 1, linkEnd - (linkSep + 1));
if (link.caption.empty())
{
@@ -107,18 +152,18 @@ void BrowserBox::addRow(const std::string &row)
link.caption = link.link;
}
- newRow.text += tmp.substr(0, idx1);
+ newRow.text += row.substr(0, linkStart);
newRow.text += "##<" + link.caption;
- tmp.erase(0, idx3 + 2);
- if (!tmp.empty())
+ row = row.substr(linkEnd + 2);
+ if (!row.empty())
{
newRow.text += "##>";
}
- idx1 = tmp.find("@@");
+ linkStart = row.find("@@");
}
- newRow.text += tmp;
+ newRow.text += row;
}
// Don't use links and user defined colors
else
@@ -126,6 +171,9 @@ void BrowserBox::addRow(const std::string &row)
newRow.text = row;
}
+ if (mEnableKeys)
+ replaceKeys(newRow.text);
+
// Layout the newly added row
LayoutContext context(getFont());
context.y = getHeight();
@@ -175,14 +223,14 @@ void BrowserBox::mousePressed(gcn::MouseEvent &event)
if (mHoveredLink) {
mLinkHandler->handleLink(mHoveredLink->link);
- gui->setCursorType(Cursor::POINTER);
+ gui->setCursorType(Cursor::Pointer);
}
}
void BrowserBox::mouseMoved(gcn::MouseEvent &event)
{
updateHoveredLink(event.getX(), event.getY());
- gui->setCursorType(mHoveredLink ? Cursor::HAND : Cursor::POINTER);
+ gui->setCursorType(mHoveredLink ? Cursor::Hand : Cursor::Pointer);
event.consume(); // Suppress mouse cursor change by parent
}
@@ -233,34 +281,15 @@ void BrowserBox::draw(gcn::Graphics *graphics)
if (part.y > yEnd)
return;
- auto font = part.font;
-
- // Handle text shadows
- if (mShadows)
- {
- graphics->setColor(Theme::getThemeColor(Theme::SHADOW,
- part.color.a / 2));
-
- if (mOutline)
- font->drawString(graphics, part.text, part.x + 2, part.y + 2);
- else
- font->drawString(graphics, part.text, part.x + 1, part.y + 1);
- }
-
- if (mOutline)
- {
- // Text outline
- graphics->setColor(Theme::getThemeColor(Theme::OUTLINE,
- part.color.a / 4));
- font->drawString(graphics, part.text, part.x + 1, part.y);
- font->drawString(graphics, part.text, part.x - 1, part.y);
- font->drawString(graphics, part.text, part.x, part.y + 1);
- font->drawString(graphics, part.text, part.x, part.y - 1);
- }
-
- // the main text
- graphics->setColor(part.color);
- font->drawString(graphics, part.text, part.x, part.y);
+ TextRenderer::renderText(graphics,
+ part.text,
+ part.x,
+ part.y,
+ Graphics::LEFT,
+ part.color,
+ part.font,
+ mOutline,
+ mShadows);
}
}
}
@@ -276,7 +305,7 @@ void BrowserBox::relayoutText()
layoutTextRow(row, context);
mLastLayoutWidth = getWidth();
- mLayoutTimer.set(100);
+ mLayoutTimer.set(33);
setHeight(context.y);
}
@@ -506,7 +535,7 @@ void BrowserBox::updateHoveredLink(int x, int y)
void BrowserBox::maybeRelayoutText()
{
// Reduce relayouting frequency when there is a lot of text
- if (mTextRows.size() > 100)
+ if (mTextRows.size() > 1000)
if (!mLayoutTimer.passed())
return;
diff --git a/src/gui/widgets/browserbox.h b/src/gui/widgets/browserbox.h
index 7278bb59..7066585d 100644
--- a/src/gui/widgets/browserbox.h
+++ b/src/gui/widgets/browserbox.h
@@ -20,8 +20,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef BROWSERBOX_H
-#define BROWSERBOX_H
+#pragma once
#include "utils/time.h"
@@ -118,9 +117,19 @@ class BrowserBox : public gcn::Widget,
void disableLinksAndUserColors() { mUseLinksAndUserColors = false; }
/**
+ * Enable or disable the replacement of keys.
+ */
+ void setEnableKeys(bool enable) { mEnableKeys = enable; }
+
+ /**
+ * Adds one or more text rows to the browser, separated by '\n'.
+ */
+ void addRows(std::string_view rows);
+
+ /**
* Adds a text row to the browser.
*/
- void addRow(const std::string &row);
+ void addRow(std::string_view row);
/**
* Remove all rows.
@@ -192,10 +201,9 @@ class BrowserBox : public gcn::Widget,
bool mShadows = false;
bool mOutline = false;
bool mUseLinksAndUserColors = true;
+ bool mEnableKeys = false;
std::optional<BrowserLink> mHoveredLink;
unsigned int mMaxRows = 0;
int mLastLayoutWidth = 0;
Timer mLayoutTimer;
};
-
-#endif
diff --git a/src/gui/widgets/button.cpp b/src/gui/widgets/button.cpp
index 274f329b..31c3a677 100644
--- a/src/gui/widgets/button.cpp
+++ b/src/gui/widgets/button.cpp
@@ -21,25 +21,22 @@
#include "gui/widgets/button.h"
-#include "configuration.h"
#include "graphics.h"
+#include "gui/gui.h"
#include "gui/textpopup.h"
#include "resources/image.h"
#include "resources/theme.h"
-
-#include "utils/dtor.h"
+#include "textrenderer.h"
#include <guichan/exception.hpp>
#include <guichan/font.hpp>
int Button::mInstances = 0;
-float Button::mAlpha = 1.0;
-ImageRect *Button::mButton;
TextPopup *Button::mTextPopup = nullptr;
-enum{
+enum {
BUTTON_STANDARD, // 0
BUTTON_HIGHLIGHTED, // 1
BUTTON_PRESSED, // 2
@@ -47,20 +44,6 @@ enum{
BUTTON_COUNT // 4 - Must be last.
};
-struct ButtonData
-{
- char const *file;
- int gridX;
- int gridY;
-};
-
-static ButtonData const data[BUTTON_COUNT] = {
- { "button.png", 0, 0 },
- { "buttonhi.png", 9, 4 },
- { "buttonpress.png", 16, 19 },
- { "button_disabled.png", 25, 23 }
-};
-
Button::Button()
{
init();
@@ -80,18 +63,19 @@ Button::Button(const std::string &caption, const std::string &actionEventId,
adjustSize();
}
-bool Button::setButtonIcon(const std::string& iconFile)
+Button::~Button() = default;
+
+bool Button::setButtonIcon(const std::string &iconFile)
{
// We clean up possible older references.
- if (mButtonIcon)
- removeButtonIcon();
+ removeButtonIcon();
// If nothing relevant was set, we can quit now.
if (iconFile.empty())
return false;
// Load the icon frames.
- Image *btnIcons = Theme::getImageFromTheme(iconFile);
+ auto btnIcons = Theme::getImageFromTheme(iconFile);
if (!btnIcons)
return false;
@@ -101,141 +85,76 @@ bool Button::setButtonIcon(const std::string& iconFile)
if (frameWidth > 0 && frameHeight > 0)
{
- mButtonIcon = new Image*[BUTTON_COUNT];
+ mButtonIcon.resize(BUTTON_COUNT);
+
for (int mode = 0; mode < BUTTON_COUNT; ++mode)
{
- mButtonIcon[mode] = btnIcons->getSubImage(mode * frameWidth, 0,
- frameWidth, frameHeight);
+ mButtonIcon[mode].reset(
+ btnIcons->getSubImage(mode * frameWidth, 0, frameWidth, frameHeight));
}
adjustSize();
}
- btnIcons->decRef();
- return (mButtonIcon);
+ return !mButtonIcon.empty();
}
-void Button::removeButtonIcon(bool adjustButtonSize)
+void Button::removeButtonIcon()
{
- if (!mButtonIcon)
+ if (mButtonIcon.empty())
return;
- // Delete potential button icons
- for (int mode = 0; mode < BUTTON_COUNT; ++mode)
- {
- delete mButtonIcon[mode];
- mButtonIcon[mode] = nullptr;
- }
- delete[] mButtonIcon;
- mButtonIcon = nullptr;
-
- if (adjustButtonSize)
- adjustSize();
+ mButtonIcon.clear();
+ adjustSize();
}
void Button::init()
{
- setFrameSize(0);
+ auto &skin = gui->getTheme()->getSkin(SkinType::Button);
+ setFrameSize(skin.frameSize);
+ setSpacing(skin.padding);
if (mInstances == 0)
{
- // Load the skin
- mButton = new ImageRect[BUTTON_COUNT];
-
- for (int mode = 0; mode < BUTTON_COUNT; ++mode)
- {
- Image *modeImage = Theme::getImageFromTheme(data[mode].file);
- int a = 0;
- for (int y = 0; y < 3; y++)
- {
- for (int x = 0; x < 3; x++)
- {
- mButton[mode].grid[a] = modeImage->getSubImage(
- data[x].gridX, data[y].gridY,
- data[x + 1].gridX - data[x].gridX + 1,
- data[y + 1].gridY - data[y].gridY + 1);
- a++;
- }
- }
- modeImage->decRef();
- }
- updateAlpha();
-
- // Load the popup
+ // Create the tooltip popup. It is shared by all buttons and will get
+ // deleted by the WindowContainer.
if (!mTextPopup)
- mTextPopup = new TextPopup();
+ mTextPopup = new TextPopup;
}
mInstances++;
}
-Button::~Button()
+void Button::draw(gcn::Graphics *graphics)
{
- mInstances--;
-
- if (mInstances == 0)
- {
- for (int mode = 0; mode < BUTTON_COUNT; ++mode)
- {
- std::for_each(mButton[mode].grid, mButton[mode].grid + 9,
- dtor<Image*>());
- }
- delete[] mButton;
+ WidgetState widgetState(this);
+ if (mHasMouse)
+ widgetState.flags |= STATE_HOVERED;
+ if (isPressed())
+ widgetState.flags |= STATE_SELECTED;
- // Remove the popup
- delete mTextPopup;
- mTextPopup = nullptr;
- }
- // Don' try to readjust the size when it's about to be deleted.
- removeButtonIcon(false);
-}
+ auto &skin = gui->getTheme()->getSkin(SkinType::Button);
+ skin.draw(static_cast<Graphics *>(graphics), widgetState);
-void Button::updateAlpha()
-{
- float alpha = std::max(config.guiAlpha,
- Theme::instance()->getMinimumOpacity());
+ auto skinState = skin.getState(widgetState.flags);
+ auto font = (skinState && skinState->textFormat.bold) ? boldFont : getFont();
- if (mAlpha != alpha)
- {
- mAlpha = alpha;
- for (int mode = 0; mode < BUTTON_COUNT; ++mode)
- {
- mButton[mode].setAlpha(mAlpha);
- }
- }
-}
-
-void Button::draw(gcn::Graphics *graphics)
-{
int mode;
- if (!isEnabled())
+ if (widgetState.flags & STATE_DISABLED)
mode = BUTTON_DISABLED;
- else if (isPressed())
+ else if (widgetState.flags & STATE_SELECTED)
mode = BUTTON_PRESSED;
- else if (mHasMouse || isFocused())
+ else if (widgetState.flags & (STATE_HOVERED | STATE_FOCUSED))
mode = BUTTON_HIGHLIGHTED;
else
mode = BUTTON_STANDARD;
- updateAlpha();
-
- static_cast<Graphics*>(graphics)->
- drawImageRect(0, 0, getWidth(), getHeight(), mButton[mode]);
-
- if (mode == BUTTON_DISABLED)
- graphics->setColor(Theme::getThemeColor(Theme::BUTTON_DISABLED));
- else
- graphics->setColor(Theme::getThemeColor(Theme::BUTTON));
-
+ Image *icon = mButtonIcon.empty() ? nullptr : mButtonIcon[mode].get();
int textX = 0;
- int textY = getHeight() / 2 - getFont()->getHeight() / 2;
+ int textY = getHeight() / 2 - font->getHeight() / 2;
int btnIconX = 0;
- int btnIconY = getHeight() / 2
- - ((mButtonIcon && mButtonIcon[mode]) ?
- mButtonIcon[mode]->getHeight() / 2 : 0);
-
- int btnIconWidth = (mButtonIcon && mButtonIcon[mode]) ?
- mButtonIcon[mode]->getWidth() : 0;
+ int btnIconY = getHeight() / 2 - (icon ? icon->getHeight() / 2 : 0);
+ int btnIconWidth = icon ? icon->getWidth() : 0;
switch (getAlignment())
{
@@ -243,7 +162,7 @@ void Button::draw(gcn::Graphics *graphics)
if (btnIconWidth)
{
btnIconX = 4;
- textX = btnIconX + mButtonIcon[mode]->getWidth() + 2;
+ textX = btnIconX + icon->getWidth() + 2;
}
else
{
@@ -253,9 +172,8 @@ void Button::draw(gcn::Graphics *graphics)
case gcn::Graphics::CENTER:
if (btnIconWidth)
{
- btnIconX = getWidth() / 2 - (getFont()->getWidth(mCaption)
- + mButtonIcon[mode]->getWidth() + 2) / 2;
- textX = getWidth() / 2 + mButtonIcon[mode]->getWidth() / 2 + 2;
+ btnIconX = (getWidth() - font->getWidth(mCaption) - icon->getWidth() - 2) / 2;
+ textX = (getWidth() + icon->getWidth()) / 2 + 2;
}
else
{
@@ -264,15 +182,13 @@ void Button::draw(gcn::Graphics *graphics)
break;
case gcn::Graphics::RIGHT:
if (btnIconWidth)
- btnIconX = getWidth() - 4 - getFont()->getWidth(mCaption) - 2;
+ btnIconX = getWidth() - 4 - font->getWidth(mCaption) - 2;
textX = getWidth() - 4;
break;
default:
throw GCN_EXCEPTION("Button::draw(). Unknown alignment.");
}
- graphics->setFont(getFont());
-
if (isPressed())
{
textX++; textY++;
@@ -280,23 +196,32 @@ void Button::draw(gcn::Graphics *graphics)
}
if (btnIconWidth)
- static_cast<Graphics*>(graphics)->drawImage(mButtonIcon[mode],
- btnIconX, btnIconY);
- graphics->drawText(getCaption(), textX, textY, getAlignment());
+ static_cast<Graphics *>(graphics)->drawImage(icon, btnIconX, btnIconY);
+
+ if (auto skinState = skin.getState(widgetState.flags))
+ {
+ auto &textFormat = skinState->textFormat;
+ TextRenderer::renderText(static_cast<Graphics *>(graphics),
+ getCaption(),
+ textX,
+ textY,
+ getAlignment(),
+ font,
+ textFormat);
+ }
}
void Button::adjustSize()
{
// Size of the image button.
int iconWidth = 0, iconHeight = 0;
- if (mButtonIcon)
+ if (!mButtonIcon.empty())
{
for (int mode = 0; mode < BUTTON_COUNT; ++mode)
{
- iconWidth = std::max(iconWidth, mButtonIcon[mode] ?
- mButtonIcon[mode]->getWidth() + 2 : 0);
- iconHeight = std::max(iconHeight, mButtonIcon[mode] ?
- mButtonIcon[mode]->getHeight() : 0);
+ const Image *icon = mButtonIcon[mode].get();
+ iconWidth = std::max(iconWidth, icon->getWidth() + 2);
+ iconHeight = std::max(iconHeight, icon->getHeight());
}
}
@@ -311,16 +236,9 @@ void Button::setCaption(const std::string& caption)
adjustSize();
}
-void Button::logic()
-{
- gcn::Button::logic();
- mTextPopup->logic();
-}
-
void Button::mouseMoved(gcn::MouseEvent &event)
{
gcn::Button::mouseMoved(event);
- mTextPopup->mouseMoved(event);
int x = event.getX();
int y = event.getY();
@@ -344,7 +262,6 @@ void Button::mouseMoved(gcn::MouseEvent &event)
void Button::mouseExited(gcn::MouseEvent &event)
{
gcn::Button::mouseExited(event);
- mTextPopup->mouseExited(event);
mTextPopup->setVisible(false);
}
diff --git a/src/gui/widgets/button.h b/src/gui/widgets/button.h
index a09b4445..97f7307b 100644
--- a/src/gui/widgets/button.h
+++ b/src/gui/widgets/button.h
@@ -19,12 +19,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef BUTTON_H
-#define BUTTON_H
+#pragma once
#include <guichan/widgets/button.hpp>
-class ImageRect;
+#include <memory>
+#include <vector>
+
class Image;
class TextPopup;
@@ -55,11 +56,6 @@ class Button : public gcn::Button
*/
void draw(gcn::Graphics *graphics) override;
- /**
- * Update the alpha value to the button components.
- */
- void updateAlpha();
-
void adjustSize();
void setCaption(const std::string &caption);
@@ -82,27 +78,22 @@ class Button : public gcn::Button
void setButtonPopupText(const std::string &text)
{ mPopupText = text; }
- void logic() override;
void mouseMoved(gcn::MouseEvent &event) override;
void mouseExited(gcn::MouseEvent &event) override;
private:
void init();
- void removeButtonIcon(bool adjustButtonSize = true);
+ void removeButtonIcon();
- static ImageRect* mButton; /**< Button state graphics */
static int mInstances; /**< Number of button instances */
- static float mAlpha;
- Image** mButtonIcon = nullptr; /**< Button Icons graphics */
+ std::vector<std::unique_ptr<Image>> mButtonIcon; /**< Button Icons graphics */
/**
* The buttons popup
* @note: This is a global object. One for all the buttons.
*/
- static TextPopup* mTextPopup;
+ static TextPopup *mTextPopup;
std::string mPopupText; /**< the current button text */
};
-
-#endif
diff --git a/src/gui/widgets/channeltab.h b/src/gui/widgets/channeltab.h
index 2894dacd..d6dc1268 100644
--- a/src/gui/widgets/channeltab.h
+++ b/src/gui/widgets/channeltab.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef CHANNELTAB_H
-#define CHANNELTAB_H
+#pragma once
#include "chattab.h"
@@ -51,5 +50,3 @@ class ChannelTab : public ChatTab
private:
Channel *mChannel;
};
-
-#endif // CHANNELTAB_H
diff --git a/src/gui/widgets/chattab.h b/src/gui/widgets/chattab.h
index 3e770fe1..dfc07638 100644
--- a/src/gui/widgets/chattab.h
+++ b/src/gui/widgets/chattab.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef CHATTAB_H
-#define CHATTAB_H
+#pragma once
#include "gui/chatwindow.h"
@@ -135,5 +134,3 @@ class ChatTab : public Tab, public AutoCompleteLister, public EventListener
};
extern ChatTab *localChatTab;
-
-#endif // CHATTAB_H
diff --git a/src/gui/widgets/checkbox.cpp b/src/gui/widgets/checkbox.cpp
index 274855fd..e6079f2f 100644
--- a/src/gui/widgets/checkbox.cpp
+++ b/src/gui/widgets/checkbox.cpp
@@ -21,121 +21,43 @@
#include "gui/widgets/checkbox.h"
-#include "configuration.h"
-#include "graphics.h"
+#include "textrenderer.h"
-#include "resources/image.h"
+#include "gui/gui.h"
#include "resources/theme.h"
-int CheckBox::instances = 0;
-float CheckBox::mAlpha = 1.0;
-Image *CheckBox::checkBoxNormal;
-Image *CheckBox::checkBoxChecked;
-Image *CheckBox::checkBoxDisabled;
-Image *CheckBox::checkBoxDisabledChecked;
-Image *CheckBox::checkBoxNormalHi;
-Image *CheckBox::checkBoxCheckedHi;
+#include <guichan/font.hpp>
-CheckBox::CheckBox(const std::string &caption, bool selected):
- gcn::CheckBox(caption, selected)
+CheckBox::CheckBox(const std::string &caption, bool selected)
+ : gcn::CheckBox(caption, selected)
{
- if (instances == 0)
- {
- Image *checkBox = Theme::getImageFromTheme("checkbox.png");
- checkBoxNormal = checkBox->getSubImage(0, 0, 9, 10);
- checkBoxChecked = checkBox->getSubImage(9, 0, 9, 10);
- checkBoxDisabled = checkBox->getSubImage(18, 0, 9, 10);
- checkBoxDisabledChecked = checkBox->getSubImage(27, 0, 9, 10);
- checkBoxNormalHi = checkBox->getSubImage(36, 0, 9, 10);
- checkBoxCheckedHi = checkBox->getSubImage(45, 0, 9, 10);
- checkBoxNormal->setAlpha(mAlpha);
- checkBoxChecked->setAlpha(mAlpha);
- checkBoxDisabled->setAlpha(mAlpha);
- checkBoxDisabledChecked->setAlpha(mAlpha);
- checkBoxNormalHi->setAlpha(mAlpha);
- checkBoxCheckedHi->setAlpha(mAlpha);
- checkBox->decRef();
- }
-
- instances++;
-}
-
-CheckBox::~CheckBox()
-{
- instances--;
-
- if (instances == 0)
- {
- delete checkBoxNormal;
- delete checkBoxChecked;
- delete checkBoxDisabled;
- delete checkBoxDisabledChecked;
- delete checkBoxNormalHi;
- delete checkBoxCheckedHi;
- }
+ auto &skin = gui->getTheme()->getSkin(SkinType::CheckBox);
+ setWidth(skin.getMinWidth() + 2 * skin.padding + skin.spacing + getFont()->getWidth(caption));
+ setHeight(skin.getMinHeight() + 2 * skin.padding);
}
void CheckBox::draw(gcn::Graphics* graphics)
{
- drawBox(graphics);
-
- graphics->setFont(getFont());
- graphics->setColor(Theme::getThemeColor(Theme::TEXT));
-
- const int h = getHeight() + getHeight() / 2;
-
- graphics->drawText(getCaption(), h - 2, 0);
-}
-
-void CheckBox::updateAlpha()
-{
- float alpha = std::max(config.guiAlpha,
- Theme::instance()->getMinimumOpacity());
-
- if (mAlpha != alpha)
- {
- mAlpha = alpha;
- checkBoxNormal->setAlpha(mAlpha);
- checkBoxChecked->setAlpha(mAlpha);
- checkBoxDisabled->setAlpha(mAlpha);
- checkBoxDisabledChecked->setAlpha(mAlpha);
- checkBoxNormal->setAlpha(mAlpha);
- checkBoxCheckedHi->setAlpha(mAlpha);
- }
-}
+ WidgetState widgetState(this);
+ if (mHasMouse)
+ widgetState.flags |= STATE_HOVERED;
+ if (isSelected())
+ widgetState.flags |= STATE_SELECTED;
-void CheckBox::drawBox(gcn::Graphics* graphics)
-{
- Image *box;
+ auto &skin = gui->getTheme()->getSkin(SkinType::CheckBox);
+ skin.draw(static_cast<Graphics *>(graphics), widgetState);
- if (isEnabled())
- {
- if (isSelected())
- {
- if (mHasMouse)
- box = checkBoxCheckedHi;
- else
- box = checkBoxChecked;
- }
- else
- {
- if (mHasMouse)
- box = checkBoxNormalHi;
- else
- box = checkBoxNormal;
- }
- }
- else
+ if (auto skinState = skin.getState(widgetState.flags))
{
- if (isSelected())
- box = checkBoxDisabledChecked;
- else
- box = checkBoxDisabled;
+ auto &textFormat = skinState->textFormat;
+ TextRenderer::renderText(static_cast<Graphics *>(graphics),
+ getCaption(),
+ skin.getMinWidth() + skin.padding + skin.spacing,
+ skin.padding,
+ Graphics::LEFT,
+ textFormat.bold ? boldFont : getFont(),
+ textFormat);
}
-
- updateAlpha();
-
- static_cast<Graphics*>(graphics)->drawImage(box, 2, 2);
}
void CheckBox::mouseEntered(gcn::MouseEvent& event)
diff --git a/src/gui/widgets/checkbox.h b/src/gui/widgets/checkbox.h
index f77b1761..ea1a20e7 100644
--- a/src/gui/widgets/checkbox.h
+++ b/src/gui/widgets/checkbox.h
@@ -19,13 +19,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef CHECKBOX_H
-#define CHECKBOX_H
+#pragma once
#include <guichan/widgets/checkbox.hpp>
-class Image;
-
/**
* Check box widget. Same as the Guichan check box but with custom look.
*
@@ -36,43 +33,26 @@ class CheckBox : public gcn::CheckBox
public:
CheckBox(const std::string &caption, bool selected = false);
- ~CheckBox() override;
-
/**
* Draws the caption, then calls drawBox to draw the check box.
*/
- void draw(gcn::Graphics* graphics) override;
-
- /**
- * Update the alpha value to the checkbox components.
- */
- void updateAlpha();
+ void draw(gcn::Graphics *graphics) override;
/**
- * Draws the check box, not the caption.
+ * Overridden because box is drawn in CheckBox::draw.
*/
- void drawBox(gcn::Graphics* graphics) override;
+ void drawBox(gcn::Graphics *graphics) override {}
/**
* Called when the mouse enteres the widget area.
*/
- void mouseEntered(gcn::MouseEvent& event) override;
+ void mouseEntered(gcn::MouseEvent &event) override;
/**
* Called when the mouse leaves the widget area.
*/
- void mouseExited(gcn::MouseEvent& event) override;
+ void mouseExited(gcn::MouseEvent &event) override;
private:
- static int instances;
- static float mAlpha;
bool mHasMouse = false;
- static Image *checkBoxNormal;
- static Image *checkBoxChecked;
- static Image *checkBoxDisabled;
- static Image *checkBoxDisabledChecked;
- static Image *checkBoxNormalHi;
- static Image *checkBoxCheckedHi;
};
-
-#endif
diff --git a/src/gui/widgets/container.h b/src/gui/widgets/container.h
index ef44c8cd..fbdaa1d4 100644
--- a/src/gui/widgets/container.h
+++ b/src/gui/widgets/container.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GUI_CONTAINER_H
-#define GUI_CONTAINER_H
+#pragma once
#include <guichan/widgets/container.hpp>
@@ -44,6 +43,9 @@ class Container : public gcn::Container
Container();
~Container() override;
+ // Overridden to disable drawing of the frame
+ void drawFrame(gcn::Graphics *graphics) override {}
+
protected:
/**
* Gets the layout handler for this container.
@@ -63,5 +65,3 @@ class Container : public gcn::Container
private:
LayoutHelper *mLayoutHelper = nullptr;
};
-
-#endif
diff --git a/src/gui/widgets/desktop.cpp b/src/gui/widgets/desktop.cpp
index c8ded9f5..e424beec 100644
--- a/src/gui/widgets/desktop.cpp
+++ b/src/gui/widgets/desktop.cpp
@@ -105,7 +105,7 @@ void Desktop::setBestFittingWallpaper()
return;
ResourceManager *resman = ResourceManager::getInstance();
- auto wallpaper = resman->getImageRef(wallpaperName);
+ auto wallpaper = resman->getImage(wallpaperName);
if (wallpaper)
{
diff --git a/src/gui/widgets/desktop.h b/src/gui/widgets/desktop.h
index 5909ac72..a7aa4a1e 100644
--- a/src/gui/widgets/desktop.h
+++ b/src/gui/widgets/desktop.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef DESKTOP_H
-#define DESKTOP_H
+#pragma once
#include "guichanfwd.h"
@@ -66,5 +65,3 @@ class Desktop : public Container, gcn::WidgetListener
ResourceRef<Image> mWallpaper;
gcn::Label *mVersionLabel;
};
-
-#endif // DESKTOP_H
diff --git a/src/gui/widgets/dropdown.cpp b/src/gui/widgets/dropdown.cpp
index 8811eb8d..45c8e53f 100644
--- a/src/gui/widgets/dropdown.cpp
+++ b/src/gui/widgets/dropdown.cpp
@@ -21,127 +21,43 @@
#include "gui/widgets/dropdown.h"
-#include "configuration.h"
#include "graphics.h"
+#include "gui/gui.h"
#include "gui/sdlinput.h"
#include "gui/widgets/listbox.h"
#include "gui/widgets/scrollarea.h"
-#include "resources/image.h"
#include "resources/theme.h"
-#include "utils/dtor.h"
-
-#include <algorithm>
-
-int DropDown::instances = 0;
-Image *DropDown::buttons[2][2];
-ImageRect DropDown::skin;
-float DropDown::mAlpha = 1.0;
+#include <guichan/font.hpp>
DropDown::DropDown(gcn::ListModel *listModel):
gcn::DropDown::DropDown(listModel,
new ScrollArea,
new ListBox(listModel))
{
- setFrameSize(2);
-
- // Initialize graphics
- if (instances == 0)
- {
- // Load the background skin
-
- // Get the button skin
- buttons[1][0] = Theme::getImageFromTheme("vscroll_up_default.png");
- buttons[0][0] = Theme::getImageFromTheme("vscroll_down_default.png");
- buttons[1][1] = Theme::getImageFromTheme("vscroll_up_pressed.png");
- buttons[0][1] = Theme::getImageFromTheme("vscroll_down_pressed.png");
-
- buttons[0][0]->setAlpha(mAlpha);
- buttons[0][1]->setAlpha(mAlpha);
- buttons[1][0]->setAlpha(mAlpha);
- buttons[1][1]->setAlpha(mAlpha);
-
- // get the border skin
- Image *boxBorder = Theme::getImageFromTheme("deepbox.png");
- int gridx[4] = {0, 3, 28, 31};
- int gridy[4] = {0, 3, 28, 31};
- int a = 0;
-
- for (int y = 0; y < 3; y++)
- {
- for (int 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++;
- }
- }
+ auto &skin = gui->getTheme()->getSkin(SkinType::DropDownFrame);
+ setFrameSize(skin.frameSize);
+ mPadding = skin.padding;
- skin.setAlpha(mAlpha);
-
- boxBorder->decRef();
- }
-
- instances++;
+ setHeight(getFont()->getHeight() + 2 * mPadding);
}
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();
-
- std::for_each(skin.grid, skin.grid + 9, dtor<Image*>());
- }
-
delete mScrollArea;
}
-void DropDown::updateAlpha()
-{
- float alpha = std::max(config.guiAlpha,
- Theme::instance()->getMinimumOpacity());
-
- if (mAlpha != alpha)
- {
- mAlpha = alpha;
-
- buttons[0][0]->setAlpha(mAlpha);
- buttons[0][1]->setAlpha(mAlpha);
- buttons[1][0]->setAlpha(mAlpha);
- buttons[1][1]->setAlpha(mAlpha);
-
- skin.setAlpha(mAlpha);
- }
-}
-
void DropDown::draw(gcn::Graphics* graphics)
{
- int h;
+ const int h = mDroppedDown ? mFoldedUpHeight : getHeight();
- if (mDroppedDown)
- h = mFoldedUpHeight;
- else
- h = getHeight();
-
- updateAlpha();
-
- const int alpha = (int) (mAlpha * 255.0f);
+ const int alpha = gui->getTheme()->getGuiAlpha();
gcn::Color faceColor = getBaseColor();
faceColor.a = alpha;
- const gcn::Color *highlightColor = &Theme::getThemeColor(Theme::HIGHLIGHT,
- alpha);
+ const gcn::Color *highlightColor = &Theme::getThemeColor(Theme::HIGHLIGHT, alpha);
gcn::Color shadowColor = faceColor - 0x303030;
shadowColor.a = alpha;
@@ -149,13 +65,16 @@ void DropDown::draw(gcn::Graphics* graphics)
{
graphics->setFont(getFont());
graphics->setColor(Theme::getThemeColor(Theme::TEXT));
- graphics->drawText(mListBox->getListModel()->getElementAt(mListBox->getSelected()), 1, 0);
+ graphics->drawText(mListBox->getListModel()->getElementAt(mListBox->getSelected()),
+ mPadding,
+ mPadding);
}
if (isFocused())
{
graphics->setColor(*highlightColor);
- graphics->drawRectangle(gcn::Rectangle(0, 0, getWidth() - h, h));
+ graphics->drawRectangle(
+ gcn::Rectangle(mPadding, mPadding, getWidth() - h - mPadding * 2, h - 2 * mPadding));
}
drawButton(graphics);
@@ -176,18 +95,65 @@ void DropDown::draw(gcn::Graphics* graphics)
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);
+ WidgetState state(this);
+ state.width += bs * 2;
+ state.height += bs * 2;
+
+ gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::DropDownFrame, state);
+}
+
+// Overridden so that we can take mPadding into account
+void DropDown::adjustHeight()
+{
+ const int listBoxHeight = mListBox->getHeight();
+ int height = getFont()->getHeight() + 2 * mPadding;
+
+ // The addition/subtraction of 2 compensates for the seperation lines
+ // seperating the selected element view and the scroll area.
+
+ if (mDroppedDown && getParent())
+ {
+ int availableHeight = getParent()->getChildrenArea().height - getY();
+
+ if (listBoxHeight > availableHeight - height - 2)
+ {
+ mScrollArea->setHeight(availableHeight - height - 2);
+ height = availableHeight;
+ }
+ else
+ {
+ height += listBoxHeight + 2;
+ mScrollArea->setHeight(listBoxHeight);
+ }
+ }
+
+ setHeight(height);
+
+ mScrollArea->setWidth(getWidth());
+ // Resize the ListBox to exactly fit the ScrollArea.
+ mListBox->setWidth(mScrollArea->getChildrenArea().width);
+ mScrollArea->setPosition(0, 0);
}
void DropDown::drawButton(gcn::Graphics *graphics)
{
- int height = mDroppedDown ? mFoldedUpHeight : getHeight();
+ WidgetState state(this);
+ if (mDroppedDown)
+ {
+ state.height = mFoldedUpHeight;
+ state.flags |= STATE_SELECTED;
+ }
+ if (mPushed)
+ state.flags |= STATE_HOVERED;
+
+ const auto theme = gui->getTheme();
+ const int buttonWidth = theme->getMinWidth(SkinType::DropDownButton);
+
+ // FIXME: Needs support for setting alignment in the theme.
+ state.x = state.width - buttonWidth;
- static_cast<Graphics*>(graphics)->
- drawImage(buttons[mDroppedDown][mPushed], getWidth() - height + 2, 1);
+ theme->drawSkin(static_cast<Graphics *>(graphics), SkinType::DropDownButton, state);
}
// -- KeyListener notifications
@@ -253,3 +219,32 @@ void DropDown::mouseWheelMovedDown(gcn::MouseEvent& mouseEvent)
mouseEvent.consume();
distributeActionEvent();
}
+
+// Overridden to call our version of adjustHeight
+void DropDown::dropDown()
+{
+ if (!mDroppedDown)
+ {
+ mDroppedDown = true;
+ mFoldedUpHeight = getHeight();
+ adjustHeight();
+
+ if (getParent())
+ {
+ getParent()->moveToTop(this);
+ }
+ }
+
+ mListBox->requestFocus();
+}
+
+// Overridden to call our version of adjustHeight
+void DropDown::foldUp()
+{
+ if (mDroppedDown)
+ {
+ mDroppedDown = false;
+ adjustHeight();
+ mInternalFocusHandler.focusNone();
+ }
+}
diff --git a/src/gui/widgets/dropdown.h b/src/gui/widgets/dropdown.h
index f92c7dd5..a5e2e2f7 100644
--- a/src/gui/widgets/dropdown.h
+++ b/src/gui/widgets/dropdown.h
@@ -19,14 +19,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef DROPDOWN_H
-#define DROPDOWN_H
+#pragma once
#include <guichan/widgets/dropdown.hpp>
-class Image;
-class ImageRect;
-
/**
* A drop down box from which you can select different values.
*
@@ -47,15 +43,12 @@ class DropDown : public gcn::DropDown
~DropDown() override;
- /**
- * Update the alpha value to the graphic components.
- */
- static void updateAlpha();
-
void draw(gcn::Graphics *graphics) override;
void drawFrame(gcn::Graphics *graphics) override;
+ void adjustHeight();
+
// Inherited from FocusListener
void focusLost(const gcn::Event& event) override;
@@ -80,12 +73,8 @@ class DropDown : public gcn::DropDown
*/
void drawButton(gcn::Graphics *graphics) override;
- // Add own Images.
- static int instances;
- static Image *buttons[2][2];
- static ImageRect skin;
- static float mAlpha;
-};
-
-#endif // end DROPDOWN_H
+ void dropDown() override;
+ void foldUp() override;
+ int mPadding = 1;
+};
diff --git a/src/gui/widgets/emoteshortcutcontainer.cpp b/src/gui/widgets/emoteshortcutcontainer.cpp
index 8ecbc9bf..06d80ec2 100644
--- a/src/gui/widgets/emoteshortcutcontainer.cpp
+++ b/src/gui/widgets/emoteshortcutcontainer.cpp
@@ -21,13 +21,12 @@
#include "gui/widgets/emoteshortcutcontainer.h"
-#include "configuration.h"
#include "emoteshortcut.h"
#include "graphics.h"
-#include "imagesprite.h"
-#include "item.h"
#include "keyboardconfig.h"
+#include "gui/gui.h"
+
#include "resources/emotedb.h"
#include "resources/image.h"
#include "resources/theme.h"
@@ -36,65 +35,52 @@ static const int MAX_ITEMS = 12;
EmoteShortcutContainer::EmoteShortcutContainer()
{
- addMouseListener(this);
- addWidgetListener(this);
-
- mBackgroundImg = Theme::getImageFromTheme("item_shortcut_bgr.png");
-
- mBackgroundImg->setAlpha(config.guiAlpha);
-
mMaxItems = std::min(EmoteDB::getEmoteCount(), MAX_ITEMS);
-
- mBoxHeight = mBackgroundImg->getHeight();
- mBoxWidth = mBackgroundImg->getWidth();
-}
-
-EmoteShortcutContainer::~EmoteShortcutContainer()
-{
- mBackgroundImg->decRef();
}
void EmoteShortcutContainer::draw(gcn::Graphics *graphics)
{
- if (config.guiAlpha != mAlpha)
- {
- mAlpha = config.guiAlpha;
- mBackgroundImg->setAlpha(mAlpha);
- }
-
auto *g = static_cast<Graphics*>(graphics);
+ auto theme = gui->getTheme();
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);
+ WidgetState state;
+ state.x = (i % mGridWidth) * mBoxWidth;
+ state.y = (i / mGridWidth) * mBoxHeight;
+ theme->drawSkin(g, SkinType::ShortcutBox, state);
// Draw emote keyboard shortcut.
const char *key = SDL_GetKeyName(
keyboard.getKeyValue(KeyboardConfig::KEY_EMOTE_1 + i));
graphics->setColor(Theme::getThemeColor(Theme::TEXT));
- g->drawText(key, emoteX + 2, emoteY + 2, gcn::Graphics::LEFT);
+ g->drawText(key, state.x + 2, state.y + 2, gcn::Graphics::LEFT);
int emoteId = emoteShortcut->getEmote(i);
if (emoteId != -1)
{
- EmoteDB::get(emoteId).sprite->draw(g, emoteX + 2, emoteY + 10);
+ if (auto image = EmoteDB::get(emoteId).image)
+ {
+ image->setAlpha(1.0f);
+ g->drawImage(image, state.x + 2, state.y + 10);
+ }
}
}
if (mEmoteMoved != -1)
{
// Draw the emote image being dragged by the cursor.
- const ImageSprite *sprite = EmoteDB::get(mEmoteMoved).sprite.get();
+ if (auto image = EmoteDB::get(mEmoteMoved).image)
+ {
+ image->setAlpha(1.0f);
- const int tPosX = mCursorPosX - (sprite->getWidth() / 2);
- const int tPosY = mCursorPosY - (sprite->getHeight() / 2);
+ const int tPosX = mCursorPosX - (image->getWidth() / 2);
+ const int tPosY = mCursorPosY - (image->getHeight() / 2);
- sprite->draw(g, tPosX, tPosY);
+ g->drawImage(image, tPosX, tPosY);
+ }
}
}
@@ -115,6 +101,7 @@ void EmoteShortcutContainer::mouseDragged(gcn::MouseEvent &event)
emoteShortcut->removeEmote(index);
}
}
+
if (mEmoteMoved != -1)
{
mCursorPosX = event.getX();
@@ -126,7 +113,6 @@ void EmoteShortcutContainer::mouseDragged(gcn::MouseEvent &event)
void EmoteShortcutContainer::mousePressed(gcn::MouseEvent &event)
{
const int index = getIndexFromGrid(event.getX(), event.getY());
-
if (index == -1)
return;
@@ -170,4 +156,3 @@ void EmoteShortcutContainer::mouseReleased(gcn::MouseEvent &event)
mEmoteClicked = false;
}
}
-
diff --git a/src/gui/widgets/emoteshortcutcontainer.h b/src/gui/widgets/emoteshortcutcontainer.h
index ecd41736..57d5efd2 100644
--- a/src/gui/widgets/emoteshortcutcontainer.h
+++ b/src/gui/widgets/emoteshortcutcontainer.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef EMOTESHORTCUTCONTAINER_H
-#define EMOTESHORTCUTCONTAINER_H
+#pragma once
#include "gui/widgets/shortcutcontainer.h"
@@ -34,8 +33,6 @@ class EmoteShortcutContainer : public ShortcutContainer
public:
EmoteShortcutContainer();
- ~EmoteShortcutContainer() override;
-
/**
* Draws the items.
*/
@@ -60,5 +57,3 @@ class EmoteShortcutContainer : public ShortcutContainer
bool mEmoteClicked = false;
int mEmoteMoved = -1;
};
-
-#endif
diff --git a/src/gui/widgets/flowcontainer.h b/src/gui/widgets/flowcontainer.h
index 21daae16..46be0919 100644
--- a/src/gui/widgets/flowcontainer.h
+++ b/src/gui/widgets/flowcontainer.h
@@ -18,8 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef FLOWCONTAINER_H
-#define FLOWCONTAINER_H
+#pragma once
#include "container.h"
@@ -56,5 +55,3 @@ class FlowContainer : public Container,
int mGridWidth = 1;
int mGridHeight = 1;
};
-
-#endif
diff --git a/src/gui/widgets/icon.cpp b/src/gui/widgets/icon.cpp
index 67fd8384..61506a6b 100644
--- a/src/gui/widgets/icon.cpp
+++ b/src/gui/widgets/icon.cpp
@@ -27,7 +27,7 @@
#include "resources/resourcemanager.h"
Icon::Icon(const std::string &file)
- : Icon(ResourceManager::getInstance()->getImageRef(file))
+ : Icon(ResourceManager::getInstance()->getImage(file))
{
}
diff --git a/src/gui/widgets/icon.h b/src/gui/widgets/icon.h
index 3ebc2c16..5e61520c 100644
--- a/src/gui/widgets/icon.h
+++ b/src/gui/widgets/icon.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef ICON_H
-#define ICON_H
+#pragma once
#include "resources/resource.h"
@@ -68,5 +67,3 @@ class Icon : public gcn::Widget
private:
ResourceRef<Image> mImage;
};
-
-#endif // ICON_H
diff --git a/src/gui/widgets/inttextfield.h b/src/gui/widgets/inttextfield.h
index d5829404..bebad71d 100644
--- a/src/gui/widgets/inttextfield.h
+++ b/src/gui/widgets/inttextfield.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef INTTEXTFIELD_H
-#define INTTEXTFIELD_H
+#pragma once
#include "textfield.h"
@@ -71,5 +70,3 @@ class IntTextField : public TextField
int mDefault; /**< Default value */
int mValue; /**< Current value */
};
-
-#endif
diff --git a/src/gui/widgets/itemcontainer.cpp b/src/gui/widgets/itemcontainer.cpp
index 940c69f4..d1d00677 100644
--- a/src/gui/widgets/itemcontainer.cpp
+++ b/src/gui/widgets/itemcontainer.cpp
@@ -64,7 +64,6 @@ ItemContainer::ItemContainer(Inventory *inventory):
ItemContainer::~ItemContainer()
{
- mSelImg->decRef();
delete mItemPopup;
}
diff --git a/src/gui/widgets/itemcontainer.h b/src/gui/widgets/itemcontainer.h
index 51807aba..0a8ac1e2 100644
--- a/src/gui/widgets/itemcontainer.h
+++ b/src/gui/widgets/itemcontainer.h
@@ -19,8 +19,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef ITEMCONTAINER_H
-#define ITEMCONTAINER_H
+#pragma once
+
+#include "resources/resource.h"
#include <guichan/keylistener.hpp>
#include <guichan/mouselistener.hpp>
@@ -71,6 +72,9 @@ class ItemContainer : public gcn::Widget,
*/
void draw(gcn::Graphics *graphics) override;
+ // Overridden to disable drawing of the frame
+ void drawFrame(gcn::Graphics *graphics) override {}
+
// KeyListener
void keyPressed(gcn::KeyEvent &event) override;
void keyReleased(gcn::KeyEvent &event) override;
@@ -178,7 +182,7 @@ class ItemContainer : public gcn::Widget,
Inventory *mInventory;
int mGridColumns = 1;
int mGridRows = 1;
- Image *mSelImg;
+ ResourceRef<Image> mSelImg;
int mSelectedIndex = -1;
int mHighlightedIndex = -1;
int mLastUsedSlot = -1;
@@ -196,5 +200,3 @@ class ItemContainer : public gcn::Widget,
std::list<gcn::SelectionListener *> mSelectionListeners;
};
-
-#endif // ITEMCONTAINER_H
diff --git a/src/gui/widgets/itemlinkhandler.h b/src/gui/widgets/itemlinkhandler.h
index 28e9c11c..58202d33 100644
--- a/src/gui/widgets/itemlinkhandler.h
+++ b/src/gui/widgets/itemlinkhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef ITEM_LINK_HANDLER_H
-#define ITEM_LINK_HANDLER_H
+#pragma once
#include "gui/widgets/linkhandler.h"
@@ -49,5 +48,3 @@ class ItemLinkHandler : public LinkHandler, gcn::ActionListener
Window *mParent = nullptr;
std::string mLink;
};
-
-#endif
diff --git a/src/gui/widgets/itemshortcutcontainer.cpp b/src/gui/widgets/itemshortcutcontainer.cpp
index 0b8f0c8c..4d0641b1 100644
--- a/src/gui/widgets/itemshortcutcontainer.cpp
+++ b/src/gui/widgets/itemshortcutcontainer.cpp
@@ -21,7 +21,6 @@
#include "gui/widgets/itemshortcutcontainer.h"
-#include "configuration.h"
#include "graphics.h"
#include "inventory.h"
#include "item.h"
@@ -39,51 +38,32 @@
#include "utils/stringutils.h"
ItemShortcutContainer::ItemShortcutContainer()
+ : mItemPopup(new ItemPopup)
{
- addMouseListener(this);
- addWidgetListener(this);
-
- mItemPopup = new ItemPopup;
-
- mBackgroundImg = Theme::getImageFromTheme("item_shortcut_bgr.png");
mMaxItems = itemShortcut->getItemCount();
-
- mBackgroundImg->setAlpha(config.guiAlpha);
-
- mBoxHeight = mBackgroundImg->getHeight();
- mBoxWidth = mBackgroundImg->getWidth();
}
-ItemShortcutContainer::~ItemShortcutContainer()
-{
- mBackgroundImg->decRef();
- delete mItemPopup;
-}
+ItemShortcutContainer::~ItemShortcutContainer() = default;
void ItemShortcutContainer::draw(gcn::Graphics *graphics)
{
- if (config.guiAlpha != mAlpha)
- {
- mAlpha = config.guiAlpha;
- mBackgroundImg->setAlpha(mAlpha);
- }
-
auto *g = static_cast<Graphics*>(graphics);
+ auto theme = gui->getTheme();
graphics->setFont(getFont());
for (int i = 0; i < mMaxItems; i++)
{
- const int itemX = (i % mGridWidth) * mBoxWidth;
- const int itemY = (i / mGridWidth) * mBoxHeight;
-
- g->drawImage(mBackgroundImg, itemX, itemY);
+ WidgetState state;
+ state.x = (i % mGridWidth) * mBoxWidth;
+ state.y = (i / mGridWidth) * mBoxHeight;
+ theme->drawSkin(g, SkinType::ShortcutBox, state);
// Draw item keyboard shortcut.
const char *key = SDL_GetKeyName(
keyboard.getKeyValue(KeyboardConfig::KEY_SHORTCUT_1 + i));
graphics->setColor(Theme::getThemeColor(Theme::TEXT));
- g->drawText(key, itemX + 2, itemY + 2, gcn::Graphics::LEFT);
+ g->drawText(key, state.x + 2, state.y + 2, gcn::Graphics::LEFT);
if (itemShortcut->getItem(i) < 0)
continue;
@@ -105,11 +85,11 @@ void ItemShortcutContainer::draw(gcn::Graphics *graphics)
caption = "Eq.";
image->setAlpha(1.0f);
- g->drawImage(image, itemX, itemY);
+ g->drawImage(image, state.x, state.y);
if (item->isEquipped())
g->setColor(Theme::getThemeColor(Theme::ITEM_EQUIPPED));
- g->drawText(caption, itemX + mBoxWidth / 2,
- itemY + mBoxHeight - 14, gcn::Graphics::CENTER);
+ g->drawText(caption, state.x + mBoxWidth / 2,
+ state.y + mBoxHeight - 14, gcn::Graphics::CENTER);
}
}
}
@@ -137,23 +117,20 @@ void ItemShortcutContainer::mouseDragged(gcn::MouseEvent &event)
if (!mItemMoved && mItemClicked)
{
const int index = getIndexFromGrid(event.getX(), event.getY());
-
if (index == -1)
return;
const int itemId = itemShortcut->getItem(index);
-
if (itemId < 0)
return;
- Item *item = PlayerInfo::getInventory()->findItem(itemId);
-
- if (item)
+ if (Item *item = PlayerInfo::getInventory()->findItem(itemId))
{
mItemMoved = item;
itemShortcut->removeItem(index);
}
}
+
if (mItemMoved)
{
mCursorPosX = event.getX();
@@ -226,18 +203,14 @@ void ItemShortcutContainer::mouseReleased(gcn::MouseEvent &event)
void ItemShortcutContainer::mouseMoved(gcn::MouseEvent &event)
{
const int index = getIndexFromGrid(event.getX(), event.getY());
-
if (index == -1)
return;
const int itemId = itemShortcut->getItem(index);
-
if (itemId < 0)
return;
- Item *item = PlayerInfo::getInventory()->findItem(itemId);
-
- if (item)
+ if (Item *item = PlayerInfo::getInventory()->findItem(itemId))
{
mItemPopup->setItem(item->getInfo());
mItemPopup->position(viewport->getMouseX(), viewport->getMouseY());
diff --git a/src/gui/widgets/itemshortcutcontainer.h b/src/gui/widgets/itemshortcutcontainer.h
index 243920a0..63d9e0ef 100644
--- a/src/gui/widgets/itemshortcutcontainer.h
+++ b/src/gui/widgets/itemshortcutcontainer.h
@@ -19,13 +19,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef ITEMSHORTCUTCONTAINER_H
-#define ITEMSHORTCUTCONTAINER_H
+#pragma once
#include "gui/widgets/shortcutcontainer.h"
#include <guichan/mouselistener.hpp>
+#include <memory>
+
class Image;
class Item;
class ItemPopup;
@@ -69,7 +70,5 @@ class ItemShortcutContainer : public ShortcutContainer
bool mItemClicked = false;
Item *mItemMoved = nullptr;
- ItemPopup *mItemPopup;
+ std::unique_ptr<ItemPopup> mItemPopup;
};
-
-#endif
diff --git a/src/gui/widgets/label.cpp b/src/gui/widgets/label.cpp
index af5220ef..53a82e14 100644
--- a/src/gui/widgets/label.cpp
+++ b/src/gui/widgets/label.cpp
@@ -21,8 +21,13 @@
#include "gui/widgets/label.h"
+#include "textrenderer.h"
+
#include "resources/theme.h"
+#include <guichan/exception.hpp>
+#include <guichan/font.hpp>
+
Label::Label()
{
setForegroundColor(Theme::getThemeColor(Theme::TEXT));
@@ -36,5 +41,33 @@ Label::Label(const std::string &caption) :
void Label::draw(gcn::Graphics *graphics)
{
- gcn::Label::draw(static_cast<gcn::Graphics*>(graphics));
+ int textX;
+ int textY = getHeight() / 2 - getFont()->getHeight() / 2;
+
+ switch (getAlignment())
+ {
+ case Graphics::LEFT:
+ textX = 0;
+ break;
+ case Graphics::CENTER:
+ textX = getWidth() / 2;
+ break;
+ case Graphics::RIGHT:
+ textX = getWidth();
+ break;
+ default:
+ throw GCN_EXCEPTION("Unknown alignment.");
+ }
+
+ TextRenderer::renderText(static_cast<Graphics *>(graphics),
+ getCaption(),
+ textX,
+ textY,
+ getAlignment(),
+ getForegroundColor(),
+ getFont(),
+ mOutlineColor.has_value(),
+ mShadowColor.has_value(),
+ mOutlineColor,
+ mShadowColor);
}
diff --git a/src/gui/widgets/label.h b/src/gui/widgets/label.h
index cb7a8b1c..85bcbe23 100644
--- a/src/gui/widgets/label.h
+++ b/src/gui/widgets/label.h
@@ -19,14 +19,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef LABEL_H
-#define LABEL_H
+#pragma once
#include <guichan/widgets/label.hpp>
+#include <optional>
/**
* Label widget. Same as the Guichan label but modified to use the palette
- * system.
+ * system and support outlines and shadows.
*
* \ingroup GUI
*/
@@ -42,9 +42,31 @@ class Label : public gcn::Label
Label(const std::string &caption);
/**
+ * Sets the color of the outline.
+ */
+ void setOutlineColor(std::optional<gcn::Color> color);
+
+ /**
+ * Sets the color of the shadow.
+ */
+ void setShadowColor(std::optional<gcn::Color> color);
+
+ /**
* Draws the label.
*/
void draw(gcn::Graphics *graphics) override;
+
+ private:
+ std::optional<gcn::Color> mOutlineColor;
+ std::optional<gcn::Color> mShadowColor;
};
-#endif
+inline void Label::setOutlineColor(std::optional<gcn::Color> color)
+{
+ mOutlineColor = color;
+}
+
+inline void Label::setShadowColor(std::optional<gcn::Color> color)
+{
+ mShadowColor = color;
+}
diff --git a/src/gui/widgets/layout.h b/src/gui/widgets/layout.h
index 4e4b28c5..42f08758 100644
--- a/src/gui/widgets/layout.h
+++ b/src/gui/widgets/layout.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef WIDGET_LAYOUT_H
-#define WIDGET_LAYOUT_H
+#pragma once
#include <guichan/widgets/container.hpp>
@@ -168,9 +167,12 @@ class LayoutCell
};
LayoutCell() = default;
-
~LayoutCell();
+ // Copy not allowed, as the cell may own an array.
+ LayoutCell(LayoutCell const &) = delete;
+ LayoutCell &operator=(LayoutCell const &) = delete;
+
/**
* Sets the padding around the cell content.
*/
@@ -232,10 +234,6 @@ class LayoutCell
void computeSizes();
private:
- // Copy not allowed, as the cell may own an array.
- LayoutCell(LayoutCell const &);
- LayoutCell &operator=(LayoutCell const &);
-
union
{
gcn::Widget *mWidget;
@@ -310,5 +308,3 @@ class Layout : public LayoutCell
private:
bool mComputed;
};
-
-#endif // WIDGET_LAYOUT_H
diff --git a/src/gui/widgets/layouthelper.h b/src/gui/widgets/layouthelper.h
index 26360a9a..ad01c565 100644
--- a/src/gui/widgets/layouthelper.h
+++ b/src/gui/widgets/layouthelper.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef LAYOUTHELPER_H
-#define LAYOUTHELPER_H
+#pragma once
#include "gui/widgets/layout.h"
@@ -74,5 +73,3 @@ class LayoutHelper : public gcn::WidgetListener
Layout mLayout; /**< Layout handler */
gcn::Container *mContainer; /**< Managed container */
};
-
-#endif // LAYOUTHELPER_H
diff --git a/src/gui/widgets/linkhandler.h b/src/gui/widgets/linkhandler.h
index 33263a25..48b182a1 100644
--- a/src/gui/widgets/linkhandler.h
+++ b/src/gui/widgets/linkhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef LINK_HANDLER_H
-#define LINK_HANDLER_H
+#pragma once
#include <string>
@@ -35,5 +34,3 @@ class LinkHandler
virtual void handleLink(const std::string &link) = 0;
};
-
-#endif
diff --git a/src/gui/widgets/listbox.cpp b/src/gui/widgets/listbox.cpp
index 55f0f422..f1fcfd53 100644
--- a/src/gui/widgets/listbox.cpp
+++ b/src/gui/widgets/listbox.cpp
@@ -21,8 +21,7 @@
#include "gui/widgets/listbox.h"
-#include "configuration.h"
-
+#include "gui/gui.h"
#include "gui/sdlinput.h"
#include "resources/theme.h"
@@ -32,31 +31,19 @@
#include <guichan/key.hpp>
#include <guichan/listmodel.hpp>
-float ListBox::mAlpha = 1.0;
-
ListBox::ListBox(gcn::ListModel *listModel):
gcn::ListBox(listModel)
{
}
-void ListBox::updateAlpha()
-{
- float alpha = std::max(config.guiAlpha,
- Theme::instance()->getMinimumOpacity());
-
- if (mAlpha != alpha)
- mAlpha = alpha;
-}
-
void ListBox::draw(gcn::Graphics *graphics)
{
if (!mListModel)
return;
- updateAlpha();
+ const int alpha = gui->getTheme()->getGuiAlpha();
- graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT,
- (int) (mAlpha * 255.0f)));
+ graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT, alpha));
graphics->setFont(getFont());
const int height = getRowHeight();
diff --git a/src/gui/widgets/listbox.h b/src/gui/widgets/listbox.h
index d16256b1..40bc2fbc 100644
--- a/src/gui/widgets/listbox.h
+++ b/src/gui/widgets/listbox.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef LISTBOX_H
-#define LISTBOX_H
+#pragma once
#include <guichan/widgets/listbox.hpp>
@@ -43,10 +42,8 @@ class ListBox : public gcn::ListBox
*/
void draw(gcn::Graphics *graphics) override;
- /**
- * Update the alpha value to the graphic components.
- */
- static void updateAlpha();
+ // Overridden to disable drawing of the frame
+ void drawFrame(gcn::Graphics *graphics) override {}
// Inherited from KeyListener
@@ -61,9 +58,4 @@ class ListBox : public gcn::ListBox
void mouseWheelMovedDown(gcn::MouseEvent& mouseEvent) override;
void mouseDragged(gcn::MouseEvent &event) override;
-
- protected:
- static float mAlpha;
};
-
-#endif
diff --git a/src/gui/widgets/passwordfield.h b/src/gui/widgets/passwordfield.h
index 4bed0e05..36964843 100644
--- a/src/gui/widgets/passwordfield.h
+++ b/src/gui/widgets/passwordfield.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef PASSWORDFIELD_H
-#define PASSWORDFIELD_H
+#pragma once
#include "textfield.h"
@@ -42,5 +41,3 @@ class PasswordField : public TextField
*/
void draw(gcn::Graphics *graphics) override;
};
-
-#endif
diff --git a/src/gui/widgets/playerbox.cpp b/src/gui/widgets/playerbox.cpp
index 3bdd6bd1..f251035d 100644
--- a/src/gui/widgets/playerbox.cpp
+++ b/src/gui/widgets/playerbox.cpp
@@ -22,86 +22,22 @@
#include "gui/widgets/playerbox.h"
#include "being.h"
-#include "configuration.h"
#include "graphics.h"
-#include "resources/image.h"
-#include "resources/theme.h"
-
-#include "utils/dtor.h"
-
-int PlayerBox::instances = 0;
-float PlayerBox::mAlpha = 1.0;
-ImageRect PlayerBox::background;
-
-PlayerBox::PlayerBox(const Being *being):
- mBeing(being)
+PlayerBox::PlayerBox(const Being *being)
+ : mBeing(being)
{
- setFrameSize(2);
-
- if (instances == 0)
- {
- // Load the background skin
- Image *textbox = Theme::getImageFromTheme("deepbox.png");
- int bggridx[4] = {0, 3, 28, 31};
- int bggridy[4] = {0, 3, 28, 31};
- int a = 0;
-
- for (int y = 0; y < 3; y++)
- {
- for (int x = 0; x < 3; x++)
- {
- background.grid[a] = textbox->getSubImage(
- bggridx[x], bggridy[y],
- bggridx[x + 1] - bggridx[x] + 1,
- bggridy[y + 1] - bggridy[y] + 1);
- a++;
- }
- }
-
- background.setAlpha(config.guiAlpha);
-
- textbox->decRef();
- }
-
- instances++;
-}
-
-PlayerBox::~PlayerBox()
-{
- instances--;
-
- mBeing = nullptr;
-
- if (instances == 0)
- {
- std::for_each(background.grid, background.grid + 9, dtor<Image*>());
- }
}
void PlayerBox::draw(gcn::Graphics *graphics)
{
+ ScrollArea::draw(graphics);
+
if (mBeing)
{
// Draw character
- const int bs = getFrameSize();
- const int x = getWidth() / 2 + bs;
- const int y = getHeight() - bs;
+ const int x = getWidth() / 2;
+ const int y = (getHeight() + mBeing->getHeight()) / 2 - 12;
mBeing->drawSpriteAt(static_cast<Graphics*>(graphics), x, y);
}
-
- if (config.guiAlpha != mAlpha)
- {
- mAlpha = config.guiAlpha;
- background.setAlpha(config.guiAlpha);
- }
-}
-
-void PlayerBox::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, background);
}
diff --git a/src/gui/widgets/playerbox.h b/src/gui/widgets/playerbox.h
index 68dd670e..39392c63 100644
--- a/src/gui/widgets/playerbox.h
+++ b/src/gui/widgets/playerbox.h
@@ -19,20 +19,18 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef PLAYERBOX_H
-#define PLAYERBOX_H
+#pragma once
-#include <guichan/widgets/scrollarea.hpp>
+#include "scrollarea.h"
class Being;
-class ImageRect;
/**
* A box showing a player character.
*
* \ingroup GUI
*/
-class PlayerBox : public gcn::ScrollArea
+class PlayerBox : public ScrollArea
{
public:
/**
@@ -41,8 +39,6 @@ class PlayerBox : public gcn::ScrollArea
*/
PlayerBox(const Being *being = nullptr);
- ~PlayerBox() override;
-
/**
* Sets a new player character to be displayed by this box. Setting the
* player to <code>NULL</code> causes the box not to draw any
@@ -52,21 +48,10 @@ class PlayerBox : public gcn::ScrollArea
{ mBeing = being; }
/**
- * Draws the scroll area.
+ * Draws the scroll area and the player.
*/
void draw(gcn::Graphics *graphics) override;
- /**
- * Draws the background and border of the scroll area.
- */
- void drawFrame(gcn::Graphics *graphics) override;
-
private:
const Being *mBeing; /**< The character used for display */
-
- static float mAlpha;
- static int instances;
- static ImageRect background;
};
-
-#endif
diff --git a/src/gui/widgets/popup.cpp b/src/gui/widgets/popup.cpp
index 94d8cf85..b7c70fe5 100644
--- a/src/gui/widgets/popup.cpp
+++ b/src/gui/widgets/popup.cpp
@@ -25,28 +25,26 @@
#include "graphics.h"
#include "log.h"
+#include "gui/gui.h"
#include "gui/viewport.h"
-
#include "gui/widgets/windowcontainer.h"
-#include "resources/theme.h"
-
#include <guichan/exception.hpp>
-Popup::Popup(const std::string &name, const std::string &skin):
- mPopupName(name),
- mMaxWidth(graphics->getWidth()),
- mMaxHeight(graphics->getHeight())
+Popup::Popup(const std::string &name, SkinType skinType)
+ : mPopupName(name)
+ , mMaxWidth(graphics->getWidth())
+ , mMaxHeight(graphics->getHeight())
+ , mSkinType(skinType)
{
logger->log("Popup::Popup(\"%s\")", name.c_str());
if (!windowContainer)
throw GCN_EXCEPTION("Popup::Popup(): no windowContainer set");
- setPadding(6);
-
- // Loads the skin
- mSkin = Theme::instance()->load(skin);
+ auto &skin = gui->getTheme()->getSkin(skinType);
+ setFrameSize(skin.frameSize);
+ setPadding(skin.padding);
// Add this window to the window container
windowContainer->add(this);
@@ -58,8 +56,6 @@ Popup::Popup(const std::string &name, const std::string &skin):
Popup::~Popup()
{
logger->log("Popup::~Popup(\"%s\")", mPopupName.c_str());
-
- mSkin->instances--;
}
void Popup::setWindowContainer(WindowContainer *wc)
@@ -69,13 +65,20 @@ void Popup::setWindowContainer(WindowContainer *wc)
void Popup::draw(gcn::Graphics *graphics)
{
- auto *g = static_cast<Graphics*>(graphics);
-
- g->drawImageRect(0, 0, getWidth(), getHeight(), mSkin->getBorder());
+ if (getFrameSize() == 0)
+ drawFrame(graphics);
drawChildren(graphics);
}
+void Popup::drawFrame(gcn::Graphics *graphics)
+{
+ WidgetState state(this);
+ state.width += getFrameSize() * 2;
+ state.height += getFrameSize() * 2;
+ gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), mSkinType, state);
+}
+
gcn::Rectangle Popup::getChildrenArea()
{
return gcn::Rectangle(getPadding(), getPadding(),
@@ -116,12 +119,12 @@ void Popup::setLocationRelativeTo(gcn::Widget *widget)
void Popup::setMinWidth(int width)
{
- mMinWidth = std::max(width, mSkin->getMinWidth());
+ mMinWidth = std::max(gui->getTheme()->getMinWidth(mSkinType), width);
}
void Popup::setMinHeight(int height)
{
- mMinHeight = std::max(height, mSkin->getMinHeight());
+ mMinHeight = std::max(gui->getTheme()->getMinHeight(mSkinType), height);
}
void Popup::setMaxWidth(int width)
diff --git a/src/gui/widgets/popup.h b/src/gui/widgets/popup.h
index c77bf814..012b55de 100644
--- a/src/gui/widgets/popup.h
+++ b/src/gui/widgets/popup.h
@@ -20,12 +20,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef POPUP_H
-#define POPUP_H
+#pragma once
#include "guichanfwd.h"
#include "gui/widgets/container.h"
+#include "resources/theme.h"
#include <guichan/mouselistener.hpp>
@@ -53,10 +53,10 @@ class Popup : public Container, public gcn::MouseListener
*
* @param name A human readable name for the popup. Only useful for
* debugging purposes.
- * @param skin The location where the Popup's skin XML can be found.
+ * @param skinType The skin type used when drawing the popup.
*/
- Popup(const std::string &name = std::string(),
- const std::string &skin = "window.xml");
+ explicit Popup(const std::string &name = std::string(),
+ SkinType skinType = SkinType::Popup);
/**
* Destructor. Deletes all the added widgets.
@@ -74,6 +74,11 @@ class Popup : public Container, public gcn::MouseListener
void draw(gcn::Graphics *graphics) override;
/**
+ * Draws the popup frame.
+ */
+ void drawFrame(gcn::Graphics *graphics) override;
+
+ /**
* Sets the size of this popup.
*/
void setContentSize(int width, int height);
@@ -152,14 +157,12 @@ class Popup : public Container, public gcn::MouseListener
void position(int x, int y);
private:
- std::string mPopupName; /**< Name of the popup */
- int mMinWidth = 100; /**< Minimum popup width */
- int mMinHeight = 40; /**< Minimum popup height */
- int mMaxWidth; /**< Maximum popup width */
- int mMaxHeight; /**< Maximum popup height */
- int mPadding; /**< Holds the padding of the popup. */
-
- Skin *mSkin; /**< Skin in use by this popup */
+ std::string mPopupName; /**< Name of the popup */
+ int mMinWidth = 100; /**< Minimum popup width */
+ int mMinHeight = 40; /**< Minimum popup height */
+ int mMaxWidth; /**< Maximum popup width */
+ int mMaxHeight; /**< Maximum popup height */
+ int mPadding; /**< Holds the padding of the popup. */
+
+ SkinType mSkinType; /**< The skin type used when drawing the popup widget. */
};
-
-#endif
diff --git a/src/gui/widgets/progressbar.cpp b/src/gui/widgets/progressbar.cpp
index 9d41d1af..5cf1b05a 100644
--- a/src/gui/widgets/progressbar.cpp
+++ b/src/gui/widgets/progressbar.cpp
@@ -21,23 +21,14 @@
#include "gui/widgets/progressbar.h"
-#include "configuration.h"
#include "graphics.h"
-#include "textrenderer.h"
#include "gui/gui.h"
-#include "resources/image.h"
#include "resources/theme.h"
-#include "utils/dtor.h"
-
#include <guichan/font.hpp>
-ImageRect ProgressBar::mBorder;
-int ProgressBar::mInstances = 0;
-float ProgressBar::mAlpha = 1.0;
-
ProgressBar::ProgressBar(float progress,
int width, int height,
int color):
@@ -53,36 +44,6 @@ ProgressBar::ProgressBar(float progress,
mProgress);
setSize(width, height);
-
- if (mInstances == 0)
- {
- Image *dBorders = Theme::getImageFromTheme("vscroll_grey.png");
- mBorder.grid[0] = dBorders->getSubImage(0, 0, 4, 4);
- mBorder.grid[1] = dBorders->getSubImage(4, 0, 3, 4);
- mBorder.grid[2] = dBorders->getSubImage(7, 0, 4, 4);
- mBorder.grid[3] = dBorders->getSubImage(0, 4, 4, 10);
- mBorder.grid[4] = dBorders->getSubImage(4, 4, 3, 10);
- mBorder.grid[5] = dBorders->getSubImage(7, 4, 4, 10);
- mBorder.grid[6] = dBorders->getSubImage(0, 15, 4, 4);
- mBorder.grid[7] = dBorders->getSubImage(4, 15, 3, 4);
- mBorder.grid[8] = dBorders->getSubImage(7, 15, 4, 4);
-
- mBorder.setAlpha(mAlpha);
-
- dBorders->decRef();
- }
-
- mInstances++;
-}
-
-ProgressBar::~ProgressBar()
-{
- mInstances--;
-
- if (mInstances == 0)
- {
- std::for_each(mBorder.grid, mBorder.grid + 9, dtor<Image*>());
- }
}
void ProgressBar::logic()
@@ -114,30 +75,19 @@ void ProgressBar::logic()
}
}
-void ProgressBar::updateAlpha()
-{
- float alpha = std::max(config.guiAlpha,
- Theme::instance()->getMinimumOpacity());
-
- if (mAlpha != alpha)
- {
- mAlpha = alpha;
- mBorder.setAlpha(mAlpha);
- }
-}
-
void ProgressBar::draw(gcn::Graphics *graphics)
{
- updateAlpha();
-
- mColor.a = (int) (mAlpha * 255);
+ mColor.a = gui->getTheme()->getGuiAlpha();
gcn::Rectangle rect = getDimension();
rect.x = 0;
rect.y = 0;
- render(static_cast<Graphics*>(graphics), rect, mColor,
- mProgress, mText);
+ gui->getTheme()->drawProgressBar(static_cast<Graphics *>(graphics),
+ rect,
+ mColor,
+ mProgress,
+ mText);
}
void ProgressBar::setProgress(float progress)
@@ -149,9 +99,7 @@ void ProgressBar::setProgress(float progress)
mProgress = p;
if (mProgressPalette >= 0)
- {
mColorToGo = Theme::getProgressColor(mProgressPalette, progress);
- }
}
void ProgressBar::setProgressPalette(int progressPalette)
@@ -160,9 +108,7 @@ void ProgressBar::setProgressPalette(int progressPalette)
mProgressPalette = progressPalette;
if (mProgressPalette != oldPalette && mProgressPalette >= 0)
- {
mColorToGo = Theme::getProgressColor(mProgressPalette, mProgressToGo);
- }
}
void ProgressBar::setColor(const gcn::Color &color)
@@ -172,37 +118,3 @@ void ProgressBar::setColor(const gcn::Color &color)
if (!mSmoothColorChange)
mColor = color;
}
-
-void ProgressBar::render(Graphics *graphics, const gcn::Rectangle &area,
- const gcn::Color &color, float progress,
- const std::string &text)
-{
- gcn::Font *oldFont = graphics->getFont();
- gcn::Color oldColor = graphics->getColor();
-
- graphics->drawImageRect(area, mBorder);
-
- // The bar
- if (progress > 0)
- {
- graphics->setColor(color);
- graphics->fillRectangle(gcn::Rectangle(area.x + 4, area.y + 4,
- (int) (progress * (area.width - 8)),
- area.height - 8));
- }
-
- // The label
- if (!text.empty())
- {
- const int textX = area.x + area.width / 2;
- const int textY = area.y + (area.height - boldFont->getHeight()) / 2;
-
- TextRenderer::renderText(graphics, text, textX, textY,
- gcn::Graphics::CENTER,
- Theme::getThemeColor(Theme::PROGRESS_BAR),
- gui->getFont(), true, false);
- }
-
- graphics->setFont(oldFont);
- graphics->setColor(oldColor);
-}
diff --git a/src/gui/widgets/progressbar.h b/src/gui/widgets/progressbar.h
index 2f9e665f..52904f5a 100644
--- a/src/gui/widgets/progressbar.h
+++ b/src/gui/widgets/progressbar.h
@@ -19,16 +19,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef PROGRESSBAR_H
-#define PROGRESSBAR_H
+#pragma once
#include <guichan/widget.hpp>
#include <string>
-class Graphics;
-class ImageRect;
-
/**
* A progress bar.
*
@@ -44,19 +40,12 @@ class ProgressBar : public gcn::Widget
int width = 40, int height = 7,
int color = -1);
- ~ProgressBar() override;
-
/**
* Performs progress bar logic (fading colors)
*/
void logic() override;
/**
- * Update the alpha value to the graphic components.
- */
- static void updateAlpha();
-
- /**
* Draws the progress bar.
*/
void draw(gcn::Graphics *graphics) override;
@@ -111,13 +100,6 @@ class ProgressBar : public gcn::Widget
void setSmoothColorChange(bool smoothColorChange)
{ mSmoothColorChange = smoothColorChange; }
- /**
- * Renders a progressbar with the given properties.
- */
- static void render(Graphics *graphics, const gcn::Rectangle &area,
- const gcn::Color &color, float progress,
- const std::string &text = std::string());
-
private:
float mProgress, mProgressToGo;
bool mSmoothProgress = true;
@@ -128,12 +110,4 @@ class ProgressBar : public gcn::Widget
bool mSmoothColorChange = true;
std::string mText;
-
- static ImageRect mBorder;
- static int mInstances;
- static float mAlpha;
-
- static const gcn::Color TEXT_COLOR;
};
-
-#endif
diff --git a/src/gui/widgets/progressindicator.cpp b/src/gui/widgets/progressindicator.cpp
index 496bd8a1..ccd4fd54 100644
--- a/src/gui/widgets/progressindicator.cpp
+++ b/src/gui/widgets/progressindicator.cpp
@@ -21,10 +21,10 @@
#include "progressindicator.h"
#include "graphics.h"
+#include "gui/gui.h"
#include "simpleanimation.h"
#include "resources/animation.h"
-#include "resources/imageset.h"
#include "resources/resourcemanager.h"
#include "resources/theme.h"
@@ -32,12 +32,12 @@
ProgressIndicator::ProgressIndicator()
{
- ImageSet *images = Theme::getImageSetFromTheme("progress-indicator.png",
- 32, 32);
+ const std::string path = gui->getTheme()->resolvePath("progress-indicator.png");
+ mImageSet = ResourceManager::getInstance()->getImageSet(path, 32, 32);
Animation anim;
- for (size_t i = 0; i < images->size(); ++i)
- anim.addFrame(images->get(i), 100, 0, 0);
+ for (size_t i = 0; i < mImageSet->size(); ++i)
+ anim.addFrame(mImageSet->get(i), 100, 0, 0);
mIndicator = std::make_unique<SimpleAnimation>(std::move(anim));
diff --git a/src/gui/widgets/progressindicator.h b/src/gui/widgets/progressindicator.h
index 428bbd02..4a6ea339 100644
--- a/src/gui/widgets/progressindicator.h
+++ b/src/gui/widgets/progressindicator.h
@@ -18,8 +18,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef PROGRESSINDICATOR_H
-#define PROGRESSINDICATOR_H
+#pragma once
+
+#include "resources/imageset.h"
#include <guichan/widget.hpp>
@@ -42,6 +43,5 @@ public:
private:
std::unique_ptr<SimpleAnimation> mIndicator;
+ ResourceRef<ImageSet> mImageSet;
};
-
-#endif // PROGRESSINDICATOR_H
diff --git a/src/gui/widgets/radiobutton.cpp b/src/gui/widgets/radiobutton.cpp
index 92cdacd1..ceba78eb 100644
--- a/src/gui/widgets/radiobutton.cpp
+++ b/src/gui/widgets/radiobutton.cpp
@@ -21,109 +21,43 @@
#include "gui/widgets/radiobutton.h"
-#include "configuration.h"
-#include "graphics.h"
+#include "textrenderer.h"
-#include "resources/image.h"
+#include "gui/gui.h"
#include "resources/theme.h"
-int RadioButton::instances = 0;
-float RadioButton::mAlpha = 1.0;
-Image *RadioButton::radioNormal;
-Image *RadioButton::radioChecked;
-Image *RadioButton::radioDisabled;
-Image *RadioButton::radioDisabledChecked;
-Image *RadioButton::radioNormalHi;
-Image *RadioButton::radioCheckedHi;
-
-RadioButton::RadioButton(const std::string &caption, const std::string &group,
- bool marked):
- gcn::RadioButton(caption, group, marked)
+RadioButton::RadioButton(const std::string &caption,
+ const std::string &group,
+ bool marked)
+ : gcn::RadioButton(caption, group, marked)
{
- if (instances == 0)
- {
- radioNormal = Theme::getImageFromTheme("radioout.png");
- radioChecked = Theme::getImageFromTheme("radioin.png");
- radioDisabled = Theme::getImageFromTheme("radioout.png");
- radioDisabledChecked = Theme::getImageFromTheme("radioin.png");
- radioNormalHi = Theme::getImageFromTheme("radioout_highlight.png");
- radioCheckedHi = Theme::getImageFromTheme("radioin_highlight.png");
- radioNormal->setAlpha(mAlpha);
- radioChecked->setAlpha(mAlpha);
- radioDisabled->setAlpha(mAlpha);
- radioDisabledChecked->setAlpha(mAlpha);
- radioNormalHi->setAlpha(mAlpha);
- radioCheckedHi->setAlpha(mAlpha);
- }
-
- instances++;
+ auto &skin = gui->getTheme()->getSkin(SkinType::RadioButton);
+ setWidth(skin.getMinWidth() + 2 * skin.padding + skin.spacing + getFont()->getWidth(caption));
+ setHeight(skin.getMinHeight() + 2 * skin.padding);
}
-RadioButton::~RadioButton()
+void RadioButton::draw(gcn::Graphics* graphics)
{
- instances--;
+ WidgetState widgetState(this);
+ if (mHasMouse)
+ widgetState.flags |= STATE_HOVERED;
+ if (isSelected())
+ widgetState.flags |= STATE_SELECTED;
- if (instances == 0)
- {
- radioNormal->decRef();
- radioChecked->decRef();
- radioDisabled->decRef();
- radioDisabledChecked->decRef();
- radioNormalHi->decRef();
- radioCheckedHi->decRef();
- }
-}
+ auto &skin = gui->getTheme()->getSkin(SkinType::RadioButton);
+ skin.draw(static_cast<Graphics *>(graphics), widgetState);
-void RadioButton::drawBox(gcn::Graphics* graphics)
-{
- if (config.guiAlpha != mAlpha)
+ if (auto skinState = skin.getState(widgetState.flags))
{
- mAlpha = config.guiAlpha;
- radioNormal->setAlpha(mAlpha);
- radioChecked->setAlpha(mAlpha);
- radioDisabled->setAlpha(mAlpha);
- radioDisabledChecked->setAlpha(mAlpha);
- radioNormalHi->setAlpha(mAlpha);
- radioCheckedHi->setAlpha(mAlpha);
+ auto &textFormat = skinState->textFormat;
+ TextRenderer::renderText(static_cast<Graphics *>(graphics),
+ getCaption(),
+ skin.getMinWidth() + skin.padding + skin.spacing,
+ skin.padding,
+ Graphics::LEFT,
+ textFormat.bold ? boldFont : getFont(),
+ textFormat);
}
-
- Image *box = nullptr;
-
- if (isEnabled())
- if (isSelected())
- if (mHasMouse)
- box = radioCheckedHi;
- else
- box = radioChecked;
- else
- if (mHasMouse)
- box = radioNormalHi;
- else
- box = radioNormal;
- else
- if (isSelected())
- box = radioDisabledChecked;
- else
- box = radioDisabled;
-
- if (box)
- static_cast<Graphics*>(graphics)->drawImage(box, 2, 2);
-}
-
-void RadioButton::draw(gcn::Graphics* graphics)
-{
- graphics->pushClipArea(gcn::Rectangle(1, 1, getWidth() - 1,
- getHeight() - 1));
-
- drawBox(graphics);
-
- graphics->popClipArea();
-
- graphics->setFont(getFont());
- graphics->setColor(getForegroundColor());
-
- int h = getHeight() + getHeight() / 2;
- graphics->drawText(getCaption(), h - 2, 0);
}
void RadioButton::mouseEntered(gcn::MouseEvent& event)
diff --git a/src/gui/widgets/radiobutton.h b/src/gui/widgets/radiobutton.h
index 2a96ff6e..fda43d01 100644
--- a/src/gui/widgets/radiobutton.h
+++ b/src/gui/widgets/radiobutton.h
@@ -19,55 +19,40 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef RADIOBUTTON_H
-#define RADIOBUTTON_H
+#pragma once
#include <guichan/widgets/radiobutton.hpp>
-class Image;
-
/**
* Guichan based RadioButton with custom look
*/
class RadioButton : public gcn::RadioButton
{
public:
- RadioButton(const std::string &caption,const std::string &group,
- bool marked = false);
-
- ~RadioButton() override;
+ RadioButton(const std::string &caption,
+ const std::string &group,
+ bool marked = false);
/**
- * Draws the radiobutton, not the caption.
+ * Implementation of the draw method.
*/
- void drawBox(gcn::Graphics* graphics) override;
+ void draw(gcn::Graphics *graphics) override;
/**
- * Implementation of the draw methods.
- * Thus, avoiding the rhomb around the radio button.
+ * Overridden because box is drawn in RadioButton::draw.
*/
- void draw(gcn::Graphics* graphics) override;
+ void drawBox(gcn::Graphics *graphics) override {}
/**
* Called when the mouse enteres the widget area.
*/
- void mouseEntered(gcn::MouseEvent& event) override;
+ void mouseEntered(gcn::MouseEvent &event) override;
/**
* Called when the mouse leaves the widget area.
*/
- void mouseExited(gcn::MouseEvent& event) override;
+ void mouseExited(gcn::MouseEvent &event) override;
private:
- static int instances;
- static float mAlpha;
bool mHasMouse = false;
- static Image *radioNormal;
- static Image *radioChecked;
- static Image *radioDisabled;
- static Image *radioDisabledChecked;
- static Image *radioNormalHi;
- static Image *radioCheckedHi;
};
-
-#endif // RADIOBUTTON_H
diff --git a/src/gui/widgets/resizegrip.cpp b/src/gui/widgets/resizegrip.cpp
index dd29a977..c802a405 100644
--- a/src/gui/widgets/resizegrip.cpp
+++ b/src/gui/widgets/resizegrip.cpp
@@ -21,48 +21,22 @@
#include "gui/widgets/resizegrip.h"
-#include "configuration.h"
#include "graphics.h"
-#include "resources/image.h"
+#include "gui/gui.h"
#include "resources/theme.h"
#include <guichan/graphics.hpp>
-Image *ResizeGrip::gripImage = nullptr;
-int ResizeGrip::mInstances = 0;
-float ResizeGrip::mAlpha = 1.0;
-
-ResizeGrip::ResizeGrip(const std::string &image)
-{
- if (mInstances == 0)
- {
- // Load the grip image
- gripImage = Theme::getImageFromTheme(image);
- gripImage->setAlpha(mAlpha);
- }
-
- mInstances++;
-
- setWidth(gripImage->getWidth() + 2);
- setHeight(gripImage->getHeight() + 2);
-}
-
-ResizeGrip::~ResizeGrip()
+ResizeGrip::ResizeGrip()
{
- mInstances--;
-
- if (mInstances == 0)
- gripImage->decRef();
+ auto &skin = gui->getTheme()->getSkin(SkinType::ResizeGrip);
+ setSize(skin.getMinWidth() + skin.padding, skin.getMinHeight() + skin.padding);
}
void ResizeGrip::draw(gcn::Graphics *graphics)
{
- if (config.guiAlpha != mAlpha)
- {
- mAlpha = config.guiAlpha;
- gripImage->setAlpha(mAlpha);
- }
-
- static_cast<Graphics*>(graphics)->drawImage(gripImage, 0, 0);
+ gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics),
+ SkinType::ResizeGrip,
+ WidgetState(this));
}
diff --git a/src/gui/widgets/resizegrip.h b/src/gui/widgets/resizegrip.h
index d2f8ca4d..9b4e0611 100644
--- a/src/gui/widgets/resizegrip.h
+++ b/src/gui/widgets/resizegrip.h
@@ -19,13 +19,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef RESIZEGRIP_H
-#define RESIZEGRIP_H
+#pragma once
#include <guichan/widget.hpp>
-class Image;
-
/**
* Resize grip. The resize grip is part of a resizable Window. It relies on the
* fact that uncaught mouse events are automatically routed to the parent
@@ -36,19 +33,10 @@ class Image;
class ResizeGrip : public gcn::Widget
{
public:
- ResizeGrip(const std::string &image = "resize.png");
-
- ~ResizeGrip() override;
+ ResizeGrip();
/**
* Draws the resize grip.
*/
void draw(gcn::Graphics *graphics) override;
-
- private:
- static Image *gripImage; /**< Resize grip image */
- static int mInstances; /**< Number of resize grip instances */
- static float mAlpha;
};
-
-#endif
diff --git a/src/gui/widgets/scrollarea.cpp b/src/gui/widgets/scrollarea.cpp
index 225a231d..c4d55072 100644
--- a/src/gui/widgets/scrollarea.cpp
+++ b/src/gui/widgets/scrollarea.cpp
@@ -21,24 +21,12 @@
#include "gui/widgets/scrollarea.h"
-#include "configuration.h"
#include "graphics.h"
-#include "resources/image.h"
-#include "resources/theme.h"
-
-#include "utils/dtor.h"
-
-int ScrollArea::instances = 0;
-float ScrollArea::mAlpha = 1.0;
-ImageRect ScrollArea::background;
-ImageRect ScrollArea::vMarker;
-ImageRect ScrollArea::vMarkerHi;
-Image *ScrollArea::buttons[4][2];
+#include "gui/gui.h"
ScrollArea::ScrollArea()
{
- addWidgetListener(this);
init();
}
@@ -51,24 +39,6 @@ ScrollArea::ScrollArea(gcn::Widget *widget):
ScrollArea::~ScrollArea()
{
delete getContent();
-
- instances--;
-
- if (instances == 0)
- {
- std::for_each(background.grid, background.grid + 9, dtor<Image*>());
- std::for_each(vMarker.grid, vMarker.grid + 9, dtor<Image*>());
- std::for_each(vMarkerHi.grid, vMarkerHi.grid + 9, dtor<Image*>());
-
- buttons[UP][0]->decRef();
- buttons[UP][1]->decRef();
- buttons[DOWN][0]->decRef();
- buttons[DOWN][1]->decRef();
- buttons[LEFT][0]->decRef();
- buttons[LEFT][1]->decRef();
- buttons[RIGHT][0]->decRef();
- buttons[RIGHT][1]->decRef();
- }
}
void ScrollArea::init()
@@ -76,83 +46,24 @@ void ScrollArea::init()
// Draw background by default
setOpaque(true);
- setUpButtonScrollAmount(2);
- setDownButtonScrollAmount(2);
- setLeftButtonScrollAmount(2);
- setRightButtonScrollAmount(2);
+ auto theme = gui->getTheme();
- if (instances == 0)
- {
- // Load the background skin
- Image *textbox = Theme::getImageFromTheme("deepbox.png");
- const int bggridx[4] = {0, 3, 28, 31};
- const int bggridy[4] = {0, 3, 28, 31};
- int a = 0;
+ int minWidth = theme->getSkin(SkinType::ScrollAreaVBar).getMinWidth();
+ if (minWidth > 0)
+ setScrollbarWidth(minWidth);
- for (int y = 0; y < 3; y++)
- {
- for (int x = 0; x < 3; x++)
- {
- background.grid[a] = textbox->getSubImage(
- bggridx[x], bggridy[y],
- bggridx[x + 1] - bggridx[x] + 1,
- bggridy[y + 1] - bggridy[y] + 1);
- a++;
- }
- }
- background.setAlpha(config.guiAlpha);
-
- textbox->decRef();
+ if (auto content = getContent())
+ content->setFrameSize(theme->getSkin(SkinType::ScrollArea).padding);
- // Load vertical scrollbar skin
- Image *vscroll = Theme::getImageFromTheme("vscroll_grey.png");
- Image *vscrollHi = Theme::getImageFromTheme("vscroll_highlight.png");
+ // The base color is only used when rendering a square in the corner where
+ // the scrollbars meet. We disable rendering of this square by setting the
+ // base color to transparent.
+ setBaseColor(gcn::Color(0, 0, 0, 0));
- int vsgridx[4] = {0, 4, 7, 11};
- int vsgridy[4] = {0, 4, 15, 19};
- a = 0;
-
- for (int y = 0; y < 3; y++)
- {
- for (int x = 0; x < 3; x++)
- {
- vMarker.grid[a] = vscroll->getSubImage(
- vsgridx[x], vsgridy[y],
- vsgridx[x + 1] - vsgridx[x],
- vsgridy[y + 1] - vsgridy[y]);
- vMarkerHi.grid[a] = vscrollHi->getSubImage(
- vsgridx[x], vsgridy[y],
- vsgridx[x + 1] - vsgridx[x],
- vsgridy[y + 1] - vsgridy[y]);
- a++;
- }
- }
-
- vMarker.setAlpha(config.guiAlpha);
- vMarkerHi.setAlpha(config.guiAlpha);
-
- vscroll->decRef();
- vscrollHi->decRef();
-
- buttons[UP][0] =
- Theme::getImageFromTheme("vscroll_up_default.png");
- buttons[DOWN][0] =
- Theme::getImageFromTheme("vscroll_down_default.png");
- buttons[LEFT][0] =
- Theme::getImageFromTheme("hscroll_left_default.png");
- buttons[RIGHT][0] =
- Theme::getImageFromTheme("hscroll_right_default.png");
- buttons[UP][1] =
- Theme::getImageFromTheme("vscroll_up_pressed.png");
- buttons[DOWN][1] =
- Theme::getImageFromTheme("vscroll_down_pressed.png");
- buttons[LEFT][1] =
- Theme::getImageFromTheme("hscroll_left_pressed.png");
- buttons[RIGHT][1] =
- Theme::getImageFromTheme("hscroll_right_pressed.png");
- }
-
- instances++;
+ setUpButtonScrollAmount(5);
+ setDownButtonScrollAmount(5);
+ setLeftButtonScrollAmount(5);
+ setRightButtonScrollAmount(5);
}
void ScrollArea::logic()
@@ -201,159 +112,117 @@ void ScrollArea::logic()
}
}
-void ScrollArea::updateAlpha()
+void ScrollArea::draw(gcn::Graphics *graphics)
{
- float alpha = std::max(config.guiAlpha,
- Theme::instance()->getMinimumOpacity());
-
- if (alpha != mAlpha)
- {
- mAlpha = alpha;
+ if (getFrameSize() == 0)
+ drawFrame(graphics);
- background.setAlpha(mAlpha);
- vMarker.setAlpha(mAlpha);
- vMarkerHi.setAlpha(mAlpha);
- }
+ gcn::ScrollArea::draw(graphics);
}
-void ScrollArea::draw(gcn::Graphics *graphics)
+void ScrollArea::drawFrame(gcn::Graphics *graphics)
{
- if (mVBarVisible)
- {
- drawUpButton(graphics);
- drawDownButton(graphics);
- drawVBar(graphics);
- drawVMarker(graphics);
- }
-
- if (mHBarVisible)
- {
- drawLeftButton(graphics);
- drawRightButton(graphics);
- drawHBar(graphics);
- drawHMarker(graphics);
- }
+ if (!mOpaque)
+ return;
- if (mHBarVisible && mVBarVisible)
- {
- graphics->setColor(getBaseColor());
- graphics->fillRectangle(gcn::Rectangle(getWidth() - mScrollbarWidth,
- getHeight() - mScrollbarWidth,
- mScrollbarWidth,
- mScrollbarWidth));
- }
+ const int bs = getFrameSize();
- updateAlpha();
+ WidgetState state(this);
+ state.width += bs * 2;
+ state.height += + bs * 2;
- drawChildren(graphics);
+ gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::ScrollArea, state);
}
-void ScrollArea::drawFrame(gcn::Graphics *graphics)
+void ScrollArea::drawChildren(gcn::Graphics *graphics)
{
- if (mOpaque)
- {
- const int bs = getFrameSize();
- const int w = getWidth() + bs * 2;
- const int h = getHeight() + bs * 2;
+ auto g = static_cast<Graphics*>(graphics);
+ g->pushClipRect(getChildrenArea());
- static_cast<Graphics*>(graphics)->
- drawImageRect(0, 0, w, h, background);
- }
+ gcn::ScrollArea::drawChildren(graphics);
+
+ g->popClipRect();
}
void ScrollArea::setOpaque(bool opaque)
{
mOpaque = opaque;
- setFrameSize(mOpaque ? 2 : 0);
+
+ auto &skin = gui->getTheme()->getSkin(SkinType::ScrollArea);
+ setFrameSize(mOpaque ? skin.frameSize : 0);
}
-void ScrollArea::drawButton(gcn::Graphics *graphics, BUTTON_DIR dir)
+void ScrollArea::drawBackground(gcn::Graphics *graphics)
{
- int state = 0;
- gcn::Rectangle dim;
-
- switch (dir)
- {
- case UP:
- state = mUpButtonPressed ? 1 : 0;
- dim = getUpButtonDimension();
- break;
- case DOWN:
- state = mDownButtonPressed ? 1 : 0;
- dim = getDownButtonDimension();
- break;
- case LEFT:
- state = mLeftButtonPressed ? 1 : 0;
- dim = getLeftButtonDimension();
- break;
- case RIGHT:
- state = mRightButtonPressed ? 1 : 0;
- dim = getRightButtonDimension();
- break;
- }
-
- static_cast<Graphics*>(graphics)->
- drawImage(buttons[dir][state], dim.x, dim.y);
+ // background is drawn as part of the frame instead
}
void ScrollArea::drawUpButton(gcn::Graphics *graphics)
{
- drawButton(graphics, UP);
+ drawButton(graphics, SkinType::ButtonUp, mUpButtonPressed, getUpButtonDimension());
}
void ScrollArea::drawDownButton(gcn::Graphics *graphics)
{
- drawButton(graphics, DOWN);
+ drawButton(graphics, SkinType::ButtonDown, mDownButtonPressed, getDownButtonDimension());
}
void ScrollArea::drawLeftButton(gcn::Graphics *graphics)
{
- drawButton(graphics, LEFT);
+ drawButton(graphics, SkinType::ButtonLeft, mLeftButtonPressed, getLeftButtonDimension());
}
void ScrollArea::drawRightButton(gcn::Graphics *graphics)
{
- drawButton(graphics, RIGHT);
+ drawButton(graphics, SkinType::ButtonRight, mRightButtonPressed, getRightButtonDimension());
}
void ScrollArea::drawVBar(gcn::Graphics *graphics)
{
- const gcn::Rectangle dim = getVerticalBarDimension();
- graphics->setColor(gcn::Color(0, 0, 0, 32));
- graphics->fillRectangle(dim);
- graphics->setColor(gcn::Color(255, 255, 255));
+ WidgetState state(getVerticalBarDimension());
+ if (mHasMouse && (mX > (getWidth() - getScrollbarWidth())))
+ state.flags |= STATE_HOVERED;
+
+ gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::ScrollAreaVBar, state);
}
void ScrollArea::drawHBar(gcn::Graphics *graphics)
{
- const gcn::Rectangle dim = getHorizontalBarDimension();
- graphics->setColor(gcn::Color(0, 0, 0, 32));
- graphics->fillRectangle(dim);
- graphics->setColor(gcn::Color(255, 255, 255));
+ WidgetState state(getHorizontalBarDimension());
+ if (mHasMouse && (mY > (getHeight() - getScrollbarWidth())))
+ state.flags |= STATE_HOVERED;
+
+ gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::ScrollAreaHBar, state);
}
void ScrollArea::drawVMarker(gcn::Graphics *graphics)
{
- gcn::Rectangle dim = getVerticalMarkerDimension();
-
- if ((mHasMouse) && (mX > (getWidth() - getScrollbarWidth())))
- static_cast<Graphics*>(graphics)->
- drawImageRect(dim.x, dim.y, dim.width, dim.height, vMarkerHi);
- else
- static_cast<Graphics*>(graphics)->
- drawImageRect(dim.x, dim.y, dim.width, dim.height,vMarker);
+ WidgetState state(getVerticalMarkerDimension());
+ if (mHasMouse && (mX > (getWidth() - getScrollbarWidth())))
+ state.flags |= STATE_HOVERED;
+
+ gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::ScrollAreaVMarker, state);
}
void ScrollArea::drawHMarker(gcn::Graphics *graphics)
{
- gcn::Rectangle dim = getHorizontalMarkerDimension();
-
- if ((mHasMouse) && (mY > (getHeight() - getScrollbarWidth())))
- static_cast<Graphics*>(graphics)->
- drawImageRect(dim.x, dim.y, dim.width, dim.height, vMarkerHi);
- else
- static_cast<Graphics*>(graphics)->
- drawImageRect(dim.x, dim.y, dim.width, dim.height, vMarker);
+ WidgetState state(getHorizontalMarkerDimension());
+ if (mHasMouse && (mY > (getHeight() - getScrollbarWidth())))
+ state.flags |= STATE_HOVERED;
+
+ gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::ScrollAreaHMarker, state);
+}
+
+void ScrollArea::drawButton(gcn::Graphics *graphics,
+ SkinType skinType,
+ bool pressed,
+ const gcn::Rectangle &dim)
+{
+ WidgetState state(dim);
+ if (pressed)
+ state.flags |= STATE_SELECTED;
+
+ gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), skinType, state);
}
void ScrollArea::mouseMoved(gcn::MouseEvent& event)
@@ -371,9 +240,3 @@ void ScrollArea::mouseExited(gcn::MouseEvent& event)
{
mHasMouse = false;
}
-
-void ScrollArea::widgetResized(const gcn::Event &event)
-{
- getContent()->setSize(getWidth() - 2 * getFrameSize(),
- getHeight() - 2 * getFrameSize());
-}
diff --git a/src/gui/widgets/scrollarea.h b/src/gui/widgets/scrollarea.h
index 2fae2d4b..40f1adc1 100644
--- a/src/gui/widgets/scrollarea.h
+++ b/src/gui/widgets/scrollarea.h
@@ -19,14 +19,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SCROLLAREA_H
-#define SCROLLAREA_H
+#pragma once
-#include <guichan/widgets/scrollarea.hpp>
-#include <guichan/widgetlistener.hpp>
+#include "resources/theme.h"
-class Image;
-class ImageRect;
+#include <guichan/widgets/scrollarea.hpp>
/**
* A scroll area.
@@ -37,7 +34,7 @@ class ImageRect;
*
* \ingroup GUI
*/
-class ScrollArea : public gcn::ScrollArea, public gcn::WidgetListener
+class ScrollArea : public gcn::ScrollArea
{
public:
/**
@@ -65,12 +62,7 @@ class ScrollArea : public gcn::ScrollArea, public gcn::WidgetListener
void logic() override;
/**
- * Update the alpha value to the graphic components.
- */
- static void updateAlpha();
-
- /**
- * Draws the scroll area.
+ * Overridden to draw the frame if its size is 0.
*/
void draw(gcn::Graphics *graphics) override;
@@ -80,6 +72,11 @@ class ScrollArea : public gcn::ScrollArea, public gcn::WidgetListener
void drawFrame(gcn::Graphics *graphics) override;
/**
+ * Applies clipping to the contents.
+ */
+ void drawChildren(gcn::Graphics* graphics) override;
+
+ /**
* Sets whether the widget should draw its background or not.
*/
void setOpaque(bool opaque);
@@ -104,22 +101,13 @@ class ScrollArea : public gcn::ScrollArea, public gcn::WidgetListener
*/
void mouseExited(gcn::MouseEvent& event) override;
- void widgetResized(const gcn::Event &event) override;
-
protected:
- enum BUTTON_DIR {
- UP,
- DOWN,
- LEFT,
- RIGHT
- };
-
/**
* Initializes the scroll area.
*/
void init();
- void drawButton(gcn::Graphics *graphics, BUTTON_DIR dir);
+ void drawBackground(gcn::Graphics *graphics) override;
void drawUpButton(gcn::Graphics *graphics) override;
void drawDownButton(gcn::Graphics *graphics) override;
void drawLeftButton(gcn::Graphics *graphics) override;
@@ -129,17 +117,13 @@ class ScrollArea : public gcn::ScrollArea, public gcn::WidgetListener
void drawVMarker(gcn::Graphics *graphics) override;
void drawHMarker(gcn::Graphics *graphics) override;
- static int instances;
- static float mAlpha;
- static ImageRect background;
- static ImageRect vMarker;
- static ImageRect vMarkerHi;
- static Image *buttons[4][2];
+ static void drawButton(gcn::Graphics *graphics,
+ SkinType skinType,
+ bool pressed,
+ const gcn::Rectangle &dim);
int mX = 0;
int mY = 0;
bool mHasMouse = false;
bool mOpaque = true;
};
-
-#endif
diff --git a/src/gui/widgets/setuptab.h b/src/gui/widgets/setuptab.h
index 0cc35a98..78cef5b2 100644
--- a/src/gui/widgets/setuptab.h
+++ b/src/gui/widgets/setuptab.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GUI_SETUPTAB_H
-#define GUI_SETUPTAB_H
+#pragma once
#include "gui/widgets/container.h"
@@ -58,5 +57,3 @@ protected:
private:
std::string mName;
};
-
-#endif
diff --git a/src/gui/widgets/shopitems.h b/src/gui/widgets/shopitems.h
index e213f67c..1b6e1727 100644
--- a/src/gui/widgets/shopitems.h
+++ b/src/gui/widgets/shopitems.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SHOP_H
-#define SHOP_H
+#pragma once
#include <guichan/listmodel.hpp>
@@ -111,5 +110,3 @@ class ShopItems : public gcn::ListModel
/** Look for duplicate entries on addition. */
bool mMergeDuplicates;
};
-
-#endif // SHOP_H
diff --git a/src/gui/widgets/shoplistbox.h b/src/gui/widgets/shoplistbox.h
index 4dbd756b..f6a1b12a 100644
--- a/src/gui/widgets/shoplistbox.h
+++ b/src/gui/widgets/shoplistbox.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SHOPLISTBOX_H
-#define SHOPLISTBOX_H
+#pragma once
#include "gui/widgets/listbox.h"
@@ -97,5 +96,3 @@ class ShopListBox : public ListBox
bool mPriceCheck;
};
-
-#endif // SHOPLISTBOX_H
diff --git a/src/gui/widgets/shortcutcontainer.cpp b/src/gui/widgets/shortcutcontainer.cpp
index 5925752e..ccf4b082 100644
--- a/src/gui/widgets/shortcutcontainer.cpp
+++ b/src/gui/widgets/shortcutcontainer.cpp
@@ -21,10 +21,18 @@
#include "gui/widgets/shortcutcontainer.h"
-float ShortcutContainer::mAlpha = 1.0;
+#include "gui/gui.h"
+
+#include "resources/theme.h"
ShortcutContainer::ShortcutContainer()
{
+ addMouseListener(this);
+ addWidgetListener(this);
+
+ auto &skin = gui->getTheme()->getSkin(SkinType::ShortcutBox);
+ mBoxWidth = skin.getMinWidth();
+ mBoxHeight = skin.getMinHeight();
}
void ShortcutContainer::widgetResized(const gcn::Event &event)
diff --git a/src/gui/widgets/shortcutcontainer.h b/src/gui/widgets/shortcutcontainer.h
index cab20f27..35a88d7f 100644
--- a/src/gui/widgets/shortcutcontainer.h
+++ b/src/gui/widgets/shortcutcontainer.h
@@ -19,15 +19,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SHORTCUTCONTAINER_H
-#define SHORTCUTCONTAINER_H
+#pragma once
#include <guichan/mouselistener.hpp>
#include <guichan/widget.hpp>
#include <guichan/widgetlistener.hpp>
-class Image;
-
/**
* A generic shortcut container.
*
@@ -45,20 +42,18 @@ class ShortcutContainer : public gcn::Widget,
*/
void draw(gcn::Graphics *graphics) override = 0;
+ // Overridden to disable drawing of the frame
+ void drawFrame(gcn::Graphics *graphics) override {}
+
/**
* Invoked when a widget changes its size. This is used to determine
* the new height of the container.
*/
void widgetResized(const gcn::Event &event) override;
- int getMaxItems() const
- { return mMaxItems; }
-
- int getBoxWidth() const
- { return mBoxWidth; }
-
- int getBoxHeight() const
- { return mBoxHeight; }
+ int getMaxItems() const { return mMaxItems; }
+ int getBoxWidth() const { return mBoxWidth; }
+ int getBoxHeight() const { return mBoxHeight; }
protected:
/**
@@ -70,10 +65,6 @@ class ShortcutContainer : public gcn::Widget,
*/
int getIndexFromGrid(int pointX, int pointY) const;
- Image *mBackgroundImg;
-
- static float mAlpha;
-
int mMaxItems = 0;
int mBoxWidth = 0;
int mBoxHeight = 0;
@@ -82,5 +73,3 @@ class ShortcutContainer : public gcn::Widget,
int mGridWidth = 1;
int mGridHeight = 1;
};
-
-#endif
diff --git a/src/gui/widgets/slider.cpp b/src/gui/widgets/slider.cpp
index a7ba37e8..bad10c15 100644
--- a/src/gui/widgets/slider.cpp
+++ b/src/gui/widgets/slider.cpp
@@ -21,19 +21,11 @@
#include "gui/widgets/slider.h"
-#include "configuration.h"
#include "graphics.h"
-#include "resources/image.h"
+#include "gui/gui.h"
#include "resources/theme.h"
-Image *Slider::hStart, *Slider::hMid, *Slider::hEnd, *Slider::hGrip;
-Image *Slider::vStart, *Slider::vMid, *Slider::vEnd, *Slider::vGrip;
-Image *Slider::hStartHi, *Slider::hMidHi, *Slider::hEndHi, *Slider::hGripHi;
-Image *Slider::vStartHi, *Slider::vMidHi, *Slider::vEndHi, *Slider::vGripHi;
-float Slider::mAlpha = 1.0;
-int Slider::mInstances = 0;
-
Slider::Slider(double scaleEnd):
gcn::Slider(scaleEnd)
{
@@ -46,154 +38,30 @@ Slider::Slider(double scaleStart, double scaleEnd):
init();
}
-Slider::~Slider()
-{
- mInstances--;
-
- if (mInstances == 0)
- {
- delete hStart;
- delete hMid;
- delete hEnd;
- delete hGrip;
- delete vStart;
- delete vMid;
- delete vEnd;
- delete vGrip;
- delete hStartHi;
- delete hMidHi;
- delete hEndHi;
- delete hGripHi;
- delete vStartHi;
- delete vMidHi;
- delete vEndHi;
- delete vGripHi;
- }
-}
-
void Slider::init()
{
- int x, y, w, h,o1,o2;
- setFrameSize(0);
-
- // Load resources
- if (mInstances == 0)
- {
- Image *slider = Theme::getImageFromTheme("slider.png");
- Image *sliderHi = Theme::getImageFromTheme("slider_hilight.png");
-
- x = 0; y = 0;
- w = 15; h = 6;
- o1 = 4; o2 = 11;
- hStart = slider->getSubImage(x, y, o1 - x, h);
- hMid = slider->getSubImage(o1, y, o2 - o1, h);
- hEnd = slider->getSubImage(o2, y, w - o2 + x, h);
- hStartHi = sliderHi->getSubImage(x, y, o1 - x, h);
- hMidHi = sliderHi->getSubImage(o1, y, o2 - o1, h);
- hEndHi = sliderHi->getSubImage(o2, y, w - o2 + x, h);
-
- x = 6; y = 8;
- w = 9; h = 10;
- hGrip = slider->getSubImage(x, y, w, h);
- hGripHi = sliderHi->getSubImage(x, y, w, h);
-
- x = 0; y = 6;
- w = 6; h = 21;
- o1 = 10; o2 = 18;
- vStart = slider->getSubImage(x, y, w, o1 - y);
- vMid = slider->getSubImage(x, o1, w, o2 - o1);
- vEnd = slider->getSubImage(x, o2, w, h - o2 + y);
- vStartHi = sliderHi->getSubImage(x, y, w, o1 - y);
- vMidHi = sliderHi->getSubImage(x, o1, w, o2 - o1);
- vEndHi = sliderHi->getSubImage(x, o2, w, h - o2 + y);
-
- x = 6; y = 8;
- w = 9; h = 10;
- vGrip = slider->getSubImage(x, y, w, h);
- vGripHi = sliderHi->getSubImage(x, y, w, h);
-
- slider->decRef();
- sliderHi->decRef();
- }
-
- mInstances++;
-
- setMarkerLength(hGrip->getWidth());
-}
-
-void Slider::updateAlpha()
-{
- float alpha = std::max(config.guiAlpha,
- Theme::instance()->getMinimumOpacity());
-
- if (alpha != mAlpha)
- {
- mAlpha = alpha;
- hStart->setAlpha(mAlpha);
- hMid->setAlpha(mAlpha);
- hEnd->setAlpha(mAlpha);
- hGrip->setAlpha(mAlpha);
- hStartHi->setAlpha(mAlpha);
- hMidHi->setAlpha(mAlpha);
- hEndHi->setAlpha(mAlpha);
- hGripHi->setAlpha(mAlpha);
-
- vStart->setAlpha(mAlpha);
- vMid->setAlpha(mAlpha);
- vEnd->setAlpha(mAlpha);
- vGrip->setAlpha(mAlpha);
- vStartHi->setAlpha(mAlpha);
- vMidHi->setAlpha(mAlpha);
- vEndHi->setAlpha(mAlpha);
- vGripHi->setAlpha(mAlpha);
- }
-
+ auto theme = gui->getTheme();
+ auto &sliderSkin = theme->getSkin(SkinType::Slider);
+ auto &sliderHandleSkin = theme->getSkin(SkinType::SliderHandle);
+ setFrameSize(sliderSkin.frameSize);
+ setMarkerLength(sliderHandleSkin.getMinWidth());
+
+ setWidth(100);
+ setHeight(sliderSkin.getMinHeight() + 2 * sliderSkin.padding);
}
void Slider::draw(gcn::Graphics *graphics)
{
- int w = getWidth();
- int h = getHeight();
- int x = 0;
- int y = mHasMouse?(h - hStartHi->getHeight()) / 2:(h - hStart->getHeight()) / 2;
-
- updateAlpha();
-
- if (!mHasMouse)
- {
- static_cast<Graphics*>(graphics)->drawImage(hStart, x, y);
-
- w -= hStart->getWidth() + hEnd->getWidth();
- x += hStart->getWidth();
-
- static_cast<Graphics*>(graphics)->
- drawImagePattern(hMid, x, y, w, hMid->getHeight());
+ WidgetState state(this);
+ if (mHasMouse)
+ state.flags |= STATE_HOVERED;
- x += w;
- static_cast<Graphics*>(graphics)->drawImage(hEnd, x, y);
- }
- else
- {
- static_cast<Graphics*>(graphics)->drawImage(hStartHi, x, y);
+ auto theme = gui->getTheme();
+ theme->drawSkin(static_cast<Graphics*>(graphics), SkinType::Slider, state);
- w -= hStartHi->getWidth() + hEndHi->getWidth();
- x += hStartHi->getWidth();
-
- static_cast<Graphics*>(graphics)->
- drawImagePattern(hMidHi, x, y, w, hMidHi->getHeight());
-
- x += w;
- static_cast<Graphics*>(graphics)->drawImage(hEndHi, x, y);
- }
-
- drawMarker(graphics);
-}
-
-void Slider::drawMarker(gcn::Graphics *graphics)
-{
- static_cast<Graphics*>(graphics)->
- drawImage(mHasMouse?hGripHi:hGrip, getMarkerPosition(),
- (getHeight() - (mHasMouse?hGripHi:hGrip)->getHeight()) / 2);
+ WidgetState handleState(state);
+ handleState.x += getMarkerPosition();
+ theme->drawSkin(static_cast<Graphics*>(graphics), SkinType::SliderHandle, handleState);
}
void Slider::mouseEntered(gcn::MouseEvent& event)
@@ -205,4 +73,3 @@ void Slider::mouseExited(gcn::MouseEvent& event)
{
mHasMouse = false;
}
-
diff --git a/src/gui/widgets/slider.h b/src/gui/widgets/slider.h
index 3896cb52..52b3b3af 100644
--- a/src/gui/widgets/slider.h
+++ b/src/gui/widgets/slider.h
@@ -19,13 +19,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SLIDER_H
-#define SLIDER_H
+#pragma once
#include <guichan/widgets/slider.hpp>
-class Image;
-
/**
* Slider widget. Same as the Guichan slider but with custom look.
*
@@ -47,22 +44,16 @@ class Slider : public gcn::Slider
*/
Slider(double scaleStart, double scaleEnd);
- ~Slider() override;
-
- /**
- * Update the alpha value to the graphic components.
- */
- static void updateAlpha();
-
/**
* Draws the slider.
*/
void draw(gcn::Graphics *graphics) override;
- /**
- * Draws the marker.
- */
- void drawMarker(gcn::Graphics *graphics) override;
+ // Overridden to disable drawing of the frame
+ void drawFrame(gcn::Graphics *graphics) override {}
+
+ // Marker is drawn in Slider::draw
+ void drawMarker(gcn::Graphics *graphics) override {}
/**
* Called when the mouse enteres the widget area.
@@ -80,13 +71,5 @@ class Slider : public gcn::Slider
*/
void init();
- static Image *hStart, *hMid, *hEnd, *hGrip;
- static Image *vStart, *vMid, *vEnd, *vGrip;
- static Image *hStartHi, *hMidHi, *hEndHi, *hGripHi;
- static Image *vStartHi, *vMidHi, *vEndHi, *vGripHi;
bool mHasMouse = false;
- static float mAlpha;
- static int mInstances;
};
-
-#endif
diff --git a/src/gui/widgets/spacer.h b/src/gui/widgets/spacer.h
index f6a210dc..2fda6e8c 100644
--- a/src/gui/widgets/spacer.h
+++ b/src/gui/widgets/spacer.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SPACER_H
-#define SPACER_H
+#pragma once
#include "guichan/graphics.hpp"
#include "guichan/widget.hpp"
@@ -48,5 +47,3 @@ class Spacer : public gcn::Widget
*/
void draw(gcn::Graphics *g) override {}
};
-
-#endif // SPACER_H
diff --git a/src/gui/widgets/tab.cpp b/src/gui/widgets/tab.cpp
index 64f49eac..b2779c4f 100644
--- a/src/gui/widgets/tab.cpp
+++ b/src/gui/widgets/tab.cpp
@@ -21,149 +21,90 @@
#include "gui/widgets/tab.h"
-#include "configuration.h"
#include "graphics.h"
+#include "gui/gui.h"
+#include "gui/widgets/label.h"
#include "gui/widgets/tabbedarea.h"
-#include "resources/image.h"
#include "resources/theme.h"
-#include "utils/dtor.h"
-
#include <guichan/widgets/label.hpp>
-int Tab::mInstances = 0;
-float Tab::mAlpha = 1.0;
-
-enum {
- TAB_STANDARD, // 0
- TAB_HIGHLIGHTED, // 1
- TAB_SELECTED, // 2
- TAB_UNUSED, // 3
- TAB_COUNT // 4 - Must be last.
-};
-
-struct TabData
-{
- char const *file;
- int gridX[4];
- int gridY[4];
-};
-
-static TabData const data[TAB_COUNT] = {
- { "tab.png", {0, 9, 16, 25}, {0, 13, 19, 20} },
- { "tab_hilight.png", {0, 9, 16, 25}, {0, 13, 19, 20} },
- { "tabselected.png", {0, 9, 16, 25}, {0, 4, 12, 20} },
- { "tab.png", {0, 9, 16, 25}, {0, 13, 19, 20} }
-};
-
-ImageRect Tab::tabImg[TAB_COUNT];
-
-Tab::Tab() :
- mTabColor(&Theme::getThemeColor(Theme::TAB))
-{
- init();
-}
-
-Tab::~Tab()
-{
- mInstances--;
-
- if (mInstances == 0)
- {
- for (auto &imgRect : tabImg)
- {
- std::for_each(imgRect.grid, imgRect.grid + 9, dtor<Image*>());
- }
- }
-}
-
-void Tab::init()
+Tab::Tab()
{
setFocusable(false);
- setFrameSize(0);
- mFlash = false;
- if (mInstances == 0)
- {
- // Load the skin
- Image *tab[TAB_COUNT];
-
- for (int mode = 0; mode < TAB_COUNT; mode++)
- {
- tab[mode] = Theme::getImageFromTheme(data[mode].file);
- int a = 0;
- for (int y = 0; y < 3; y++)
- {
- for (int x = 0; x < 3; x++)
- {
- tabImg[mode].grid[a] = tab[mode]->getSubImage(
- data[mode].gridX[x], data[mode].gridY[y],
- data[mode].gridX[x + 1] - data[mode].gridX[x] + 1,
- data[mode].gridY[y + 1] - data[mode].gridY[y] + 1);
- a++;
- }
- }
- tabImg[mode].setAlpha(mAlpha);
- tab[mode]->decRef();
- }
- }
- mInstances++;
+ // Replace the label with customized version
+ delete mLabel;
+ mLabel = new Label();
+ add(mLabel);
+
+ auto &skin = gui->getTheme()->getSkin(SkinType::Tab);
+ setFrameSize(skin.frameSize);
+ mPadding = skin.padding;
+ mLabel->setPosition(mPadding, mPadding);
}
-void Tab::updateAlpha()
+void Tab::setCaption(const std::string &caption)
{
- float alpha = std::max(config.guiAlpha,
- Theme::instance()->getMinimumOpacity());
+ mLabel->setCaption(caption);
+ mLabel->adjustSize();
- // TODO We don't need to do this for every tab on every draw
- // Maybe use a config listener to do it as the value changes.
- if (alpha != mAlpha)
- {
- mAlpha = alpha;
+ setSize(mLabel->getWidth() + mPadding * 2,
+ mLabel->getHeight() + mPadding * 2);
- for (auto &t : tabImg)
- {
- t.setAlpha(mAlpha);
- }
- }
+ if (mTabbedArea)
+ static_cast<TabbedArea*>(mTabbedArea)->adjustTabPositions();
}
void Tab::draw(gcn::Graphics *graphics)
{
- int mode = TAB_STANDARD;
+ if (getFrameSize() == 0)
+ drawFrame(graphics);
- // check which type of tab to draw
- if (mTabbedArea)
+ // if tab is selected, it doesnt need to highlight activity
+ if (mTabbedArea && mTabbedArea->isTabSelected(this))
+ mFlash = false;
+
+ uint8_t flags = 0;
+ if (mHasMouse)
+ flags |= STATE_HOVERED;
+ if (mTabbedArea && mTabbedArea->isTabSelected(this))
+ flags |= STATE_SELECTED;
+
+ auto &skin = gui->getTheme()->getSkin(SkinType::Tab);
+ if (auto state = skin.getState(flags))
{
- mLabel->setForegroundColor(*mTabColor);
- if (mTabbedArea->isTabSelected(this))
- {
- mode = TAB_SELECTED;
- // if tab is selected, it doesnt need to highlight activity
- mFlash = false;
- }
- else if (mHasMouse)
- {
- mode = TAB_HIGHLIGHTED;
- }
+ gcn::Color foregroundColor = state->textFormat.color;
if (mFlash)
- {
- mLabel->setForegroundColor(Theme::getThemeColor(Theme::TAB_FLASH));
- }
+ foregroundColor = Theme::getThemeColor(Theme::TAB_FLASH);
+ else if (mTabColor)
+ foregroundColor = *mTabColor;
+
+ auto label = static_cast<Label*>(mLabel);
+ label->setForegroundColor(foregroundColor);
+ label->setOutlineColor(state->textFormat.outlineColor);
+ label->setShadowColor(state->textFormat.shadowColor);
}
- updateAlpha();
-
- // draw tab
- static_cast<Graphics*>(graphics)->
- drawImageRect(0, 0, getWidth(), getHeight(), tabImg[mode]);
-
// draw label
drawChildren(graphics);
}
+void Tab::drawFrame(gcn::Graphics *graphics)
+{
+ WidgetState state(this);
+ state.width += getFrameSize() * 2;
+ state.height += getFrameSize() * 2;
+ if (mHasMouse)
+ state.flags |= STATE_HOVERED;
+ if (mTabbedArea && mTabbedArea->isTabSelected(this))
+ state.flags |= STATE_SELECTED;
+
+ gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::Tab, state);
+}
+
void Tab::setTabColor(const gcn::Color *color)
{
mTabColor = color;
diff --git a/src/gui/widgets/tab.h b/src/gui/widgets/tab.h
index 86650257..534abaff 100644
--- a/src/gui/widgets/tab.h
+++ b/src/gui/widgets/tab.h
@@ -19,12 +19,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef TAB_H
-#define TAB_H
+#pragma once
#include <guichan/widgets/tab.hpp>
-class ImageRect;
class TabbedArea;
/**
@@ -35,19 +33,25 @@ class Tab : public gcn::Tab
{
public:
Tab();
- ~Tab() override;
/**
- * Update the alpha value to the graphic components.
+ * Sets the caption of the tab. Shadowing gcn::Tab::setCaption, which
+ * shouldn't be used because it calls gcn::Tab::adjustSize, which does
+ * not take into account the padding.
*/
- static void updateAlpha();
+ void setCaption(const std::string& caption);
/**
- * Draw the tabbed area.
+ * Draw the tab.
*/
void draw(gcn::Graphics *graphics) override;
/**
+ * Draw the tab frame.
+ */
+ void drawFrame(gcn::Graphics *graphics) override;
+
+ /**
* Set the normal color fo the tab's text.
*/
void setTabColor(const gcn::Color *color);
@@ -62,15 +66,7 @@ class Tab : public gcn::Tab
virtual void setCurrent() {}
private:
- /** Load images if no other instances exist yet */
- void init();
-
- static ImageRect tabImg[4]; /**< Tab state graphics */
- static int mInstances; /**< Number of tab instances */
- static float mAlpha;
-
- const gcn::Color *mTabColor;
- bool mFlash;
+ const gcn::Color *mTabColor = nullptr;
+ bool mFlash = false;
+ int mPadding = 8;
};
-
-#endif
diff --git a/src/gui/widgets/tabbedarea.cpp b/src/gui/widgets/tabbedarea.cpp
index af0c11cb..fb5436e0 100644
--- a/src/gui/widgets/tabbedarea.cpp
+++ b/src/gui/widgets/tabbedarea.cpp
@@ -21,6 +21,8 @@
#include "gui/widgets/tabbedarea.h"
+#include "graphics.h"
+
#include "gui/widgets/tab.h"
#include <guichan/widgets/container.hpp>
@@ -30,13 +32,13 @@ TabbedArea::TabbedArea()
mWidgetContainer->setOpaque(false);
addWidgetListener(this);
- mArrowButton[0] = new Button(std::string(), "shift_left", this);
- mArrowButton[1] = new Button(std::string(), "shift_right", this);
+ mArrowButton[0] = std::make_unique<Button>(std::string(), "shift_left", this);
+ mArrowButton[1] = std::make_unique<Button>(std::string(), "shift_right", this);
mArrowButton[0]->setButtonIcon("tab_arrows_left.png");
mArrowButton[1]->setButtonIcon("tab_arrows_right.png");
- add(mArrowButton[0]);
- add(mArrowButton[1]);
+ add(mArrowButton[0].get());
+ add(mArrowButton[1].get());
widgetResized(nullptr);
}
@@ -61,7 +63,12 @@ void TabbedArea::draw(gcn::Graphics *graphics)
if (mTabs.empty())
return;
+ auto g = static_cast<Graphics*>(graphics);
+ g->pushClipRect(getChildrenArea());
+
drawChildren(graphics);
+
+ g->popClipRect();
}
gcn::Widget *TabbedArea::getWidget(const std::string &name) const
@@ -206,9 +213,8 @@ void TabbedArea::updateTabsWidth()
{
mTabsWidth = 0;
for (const auto &[tab, _] : mTabs)
- {
mTabsWidth += tab->getWidth();
- }
+
updateVisibleTabsWidth();
}
@@ -216,9 +222,7 @@ void TabbedArea::updateVisibleTabsWidth()
{
mVisibleTabsWidth = 0;
for (unsigned int i = mTabScrollIndex; i < mTabs.size(); ++i)
- {
mVisibleTabsWidth += mTabs[i].first->getWidth();
- }
}
void TabbedArea::adjustTabPositions()
@@ -265,7 +269,7 @@ void TabbedArea::action(const gcn::ActionEvent& actionEvent)
{
if (actionEvent.getId() == "shift_left")
{
- if (mTabScrollIndex)
+ if (mTabScrollIndex > 0)
--mTabScrollIndex;
}
else if (actionEvent.getId() == "shift_right")
@@ -282,33 +286,18 @@ void TabbedArea::action(const gcn::ActionEvent& actionEvent)
void TabbedArea::updateArrowEnableState()
{
updateTabsWidth();
- if (mTabsWidth > getWidth() - 2)
- {
- mArrowButton[0]->setVisible(true);
- mArrowButton[1]->setVisible(true);
- }
- else
- {
- mArrowButton[0]->setVisible(false);
- mArrowButton[1]->setVisible(false);
+
+ const bool arrowButtonsVisible = mTabsWidth > getWidth() - 2;
+ mArrowButton[0]->setVisible(arrowButtonsVisible);
+ mArrowButton[1]->setVisible(arrowButtonsVisible);
+
+ if (!arrowButtonsVisible)
mTabScrollIndex = 0;
- }
- // Left arrow consistency check
- if (!mTabScrollIndex)
- mArrowButton[0]->setEnabled(false);
- else
- mArrowButton[0]->setEnabled(true);
+ mArrowButton[0]->setEnabled(mTabScrollIndex > 0);
// Right arrow consistency check
- if (mVisibleTabsWidth < getWidth() - 2
- - mArrowButton[0]->getWidth()
- - mArrowButton[1]->getWidth())
- {
- mArrowButton[1]->setEnabled(false);
- }
- else
- {
- mArrowButton[1]->setEnabled(true);
- }
+ const int availableWidth = getWidth() - 2 - mArrowButton[0]->getWidth()
+ - mArrowButton[1]->getWidth();
+ mArrowButton[1]->setEnabled(mVisibleTabsWidth >= availableWidth);
}
diff --git a/src/gui/widgets/tabbedarea.h b/src/gui/widgets/tabbedarea.h
index 8e6dcb5f..558b2696 100644
--- a/src/gui/widgets/tabbedarea.h
+++ b/src/gui/widgets/tabbedarea.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef TABBEDAREA_H
-#define TABBEDAREA_H
+#pragma once
#include <guichan/widget.hpp>
#include <guichan/widgetlistener.hpp>
@@ -29,6 +28,7 @@
#include "gui/widgets/button.h"
+#include <memory>
#include <string>
class Tab;
@@ -36,7 +36,7 @@ class Tab;
/**
* A tabbed area, the same as the guichan tabbed area in 0.8, but extended
*/
-class TabbedArea : public gcn::TabbedArea, public gcn::WidgetListener
+class TabbedArea final : public gcn::TabbedArea, public gcn::WidgetListener
{
public:
TabbedArea();
@@ -115,7 +115,7 @@ class TabbedArea : public gcn::TabbedArea, public gcn::WidgetListener
private:
/** The tab arrows */
- Button *mArrowButton[2];
+ std::unique_ptr<Button> mArrowButton[2];
/** Check whether the arrow should be clickable */
void updateArrowEnableState();
@@ -151,5 +151,3 @@ class TabbedArea : public gcn::TabbedArea, public gcn::WidgetListener
*/
unsigned mTabScrollIndex = 0;
};
-
-#endif
diff --git a/src/gui/widgets/table.cpp b/src/gui/widgets/table.cpp
index 905bb166..7cddc1fa 100644
--- a/src/gui/widgets/table.cpp
+++ b/src/gui/widgets/table.cpp
@@ -21,8 +21,7 @@
#include "gui/widgets/table.h"
-#include "configuration.h"
-
+#include "gui/gui.h"
#include "gui/sdlinput.h"
#include "resources/theme.h"
@@ -33,13 +32,10 @@
#include <guichan/graphics.hpp>
#include <guichan/key.hpp>
-float GuiTable::mAlpha = 1.0;
-
class GuiTableActionListener : public gcn::ActionListener
{
public:
- GuiTableActionListener(GuiTable *_table, gcn::Widget *_widget, int _row, int _column);
-
+ GuiTableActionListener(GuiTable *table, gcn::Widget *widget, int row, int column);
~GuiTableActionListener() override;
void action(const gcn::ActionEvent& actionEvent) override;
@@ -269,13 +265,11 @@ void GuiTable::draw(gcn::Graphics* graphics)
if (!mModel)
return;
- if (config.guiAlpha != mAlpha)
- mAlpha = config.guiAlpha;
+ const auto guiAlpha = gui->getTheme()->getGuiAlpha();
if (mOpaque)
{
- graphics->setColor(Theme::getThemeColor(Theme::BACKGROUND,
- (int)(mAlpha * 255.0f)));
+ graphics->setColor(Theme::getThemeColor(Theme::BACKGROUND, guiAlpha));
graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight()));
}
@@ -320,8 +314,7 @@ void GuiTable::draw(gcn::Graphics* graphics)
widget->setDimension(bounds);
- graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT,
- (int)(mAlpha * 255.0f)));
+ graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT, guiAlpha));
if (mLinewiseMode && r == mSelectedRow && c == 0)
{
@@ -497,14 +490,12 @@ gcn::Widget *GuiTable::getWidgetAt(int x, int y) const
if (row > -1 && column > -1)
{
- gcn::Widget *w = mModel->getElementAt(row, column);
- if (w && w->isFocusable())
- return w;
- else
- return nullptr; // Grab the event locally
+ if (gcn::Widget *w = mModel->getElementAt(row, column))
+ if (w->isFocusable())
+ return w;
}
- else
- return nullptr;
+
+ return nullptr; // Grab the event locally
}
int GuiTable::getRowForY(int y) const
@@ -516,8 +507,8 @@ int GuiTable::getRowForY(int y) const
if (row < 0 || row >= mModel->getRows())
return -1;
- else
- return row;
+
+ return row;
}
int GuiTable::getColumnForX(int x) const
@@ -534,24 +525,23 @@ int GuiTable::getColumnForX(int x) const
if (column < 0 || column >= mModel->getColumns())
return -1;
- else
- return column;
+
+ return column;
}
void GuiTable::_setFocusHandler(gcn::FocusHandler* focusHandler)
{
gcn::Widget::_setFocusHandler(focusHandler);
- if (mModel)
+ if (!mModel)
+ return;
+
+ for (int r = 0; r < mModel->getRows(); ++r)
{
- for (int r = 0; r < mModel->getRows(); ++r)
+ for (int c = 0; c < mModel->getColumns(); ++c)
{
- for (int c = 0; c < mModel->getColumns(); ++c)
- {
- gcn::Widget *w = mModel->getElementAt(r, c);
- if (w)
- w->_setFocusHandler(focusHandler);
- }
+ if (gcn::Widget *w = mModel->getElementAt(r, c))
+ w->_setFocusHandler(focusHandler);
}
}
}
diff --git a/src/gui/widgets/table.h b/src/gui/widgets/table.h
index a9202022..81071267 100644
--- a/src/gui/widgets/table.h
+++ b/src/gui/widgets/table.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef TABLE_H
-#define TABLE_H
+#pragma once
#include "tablemodel.h"
@@ -41,10 +40,10 @@ class GuiTableActionListener;
*
* \ingroup GUI
*/
-class GuiTable : public gcn::Widget,
- public gcn::MouseListener,
- public gcn::KeyListener,
- public TableModelListener
+class GuiTable final : public gcn::Widget,
+ public gcn::MouseListener,
+ public gcn::KeyListener,
+ public TableModelListener
{
// so that the action listener can call distributeActionEvent
friend class GuiTableActionListener;
@@ -103,6 +102,9 @@ public:
// Inherited from Widget
void draw(gcn::Graphics* graphics) override;
+ // Overridden to disable drawing of the frame
+ void drawFrame(gcn::Graphics* graphics) override {}
+
virtual gcn::Widget *getWidgetAt(int x, int y) const;
void moveToTop(gcn::Widget *child) override;
@@ -159,8 +161,6 @@ private:
bool mWrappingEnabled;
bool mOpaque;
- static float mAlpha;
-
/**
* Holds the background color of the table.
*/
@@ -180,6 +180,3 @@ private:
/** Vector for compactness; used as a list in practice. */
std::vector<GuiTableActionListener *> mActionListeners;
};
-
-
-#endif // TABLE_H
diff --git a/src/gui/widgets/tablemodel.h b/src/gui/widgets/tablemodel.h
index d4274e39..2ba36556 100644
--- a/src/gui/widgets/tablemodel.h
+++ b/src/gui/widgets/tablemodel.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef TABLE_MODEL_H
-#define TABLE_MODEL_H
+#pragma once
#include <guichanfwd.h>
@@ -143,5 +142,3 @@ protected:
std::vector<gcn::Widget *> mTableModel;
std::vector<int> mWidths;
};
-
-#endif // TABLE_MODEL_H
diff --git a/src/gui/widgets/textbox.h b/src/gui/widgets/textbox.h
index bcf09ee2..e4596b9a 100644
--- a/src/gui/widgets/textbox.h
+++ b/src/gui/widgets/textbox.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef TEXTBOX_H
-#define TEXTBOX_H
+#pragma once
#include <guichan/widgets/textbox.hpp>
@@ -62,5 +61,3 @@ class TextBox : public gcn::TextBox
int mMinWidth;
const gcn::Color *mTextColor;
};
-
-#endif
diff --git a/src/gui/widgets/textfield.cpp b/src/gui/widgets/textfield.cpp
index dd0adecd..9f35a5dd 100644
--- a/src/gui/widgets/textfield.cpp
+++ b/src/gui/widgets/textfield.cpp
@@ -21,107 +21,63 @@
#include "gui/widgets/textfield.h"
-#include "configuration.h"
#include "graphics.h"
+#include "gui/gui.h"
#include "gui/sdlinput.h"
-#include "resources/image.h"
#include "resources/theme.h"
#include "utils/copynpaste.h"
-#include "utils/dtor.h"
#include "utils/stringutils.h"
#include <guichan/font.hpp>
#include <SDL.h>
-#undef DELETE //Win32 compatibility hack
-
-int TextField::instances = 0;
-float TextField::mAlpha = 1.0;
-ImageRect TextField::skin;
-
-TextField::TextField(const std::string &text, bool loseFocusOnTab):
- gcn::TextField(text)
-{
- setFrameSize(2);
-
- mLoseFocusOnTab = loseFocusOnTab;
-
- if (instances == 0)
- {
- // Load the skin
- Image *textbox = Theme::getImageFromTheme("deepbox.png");
- int gridx[4] = {0, 3, 28, 31};
- int gridy[4] = {0, 3, 28, 31};
- int a = 0;
-
- for (int y = 0; y < 3; y++)
- {
- for (int x = 0; x < 3; x++)
- {
- skin.grid[a] = textbox->getSubImage(
- gridx[x], gridy[y],
- gridx[x + 1] - gridx[x] + 1,
- gridy[y + 1] - gridy[y] + 1);
- a++;
- }
- }
- skin.setAlpha(config.guiAlpha);
-
- textbox->decRef();
- }
-
- instances++;
-}
-
-TextField::~TextField()
+TextField::TextField(const std::string &text, bool loseFocusOnTab)
+ : gcn::TextField(text)
+ , mLoseFocusOnTab(loseFocusOnTab)
{
- instances--;
-
- if (instances == 0)
- std::for_each(skin.grid, skin.grid + 9, dtor<Image*>());
-}
+ auto &skin = gui->getTheme()->getSkin(SkinType::TextField);
+ setFrameSize(skin.frameSize);
+ mPadding = skin.padding;
-void TextField::updateAlpha()
-{
- float alpha = std::max(config.guiAlpha,
- Theme::instance()->getMinimumOpacity());
-
- if (alpha != mAlpha)
- {
- mAlpha = alpha;
- skin.setAlpha(mAlpha);
- }
+ setWidth(getFont()->getWidth(mText) + 2 * mPadding);
+ setHeight(getFont()->getHeight() + 2 * mPadding);
+ fixScroll();
}
void TextField::draw(gcn::Graphics *graphics)
{
- updateAlpha();
+ if (getFrameSize() == 0)
+ drawFrame(graphics);
+
+ auto g = static_cast<Graphics *>(graphics);
+ g->pushClipRect(gcn::Rectangle(0, 0, getWidth(), getHeight()));
if (isFocused())
{
drawCaret(graphics,
- getFont()->getWidth(mText.substr(0, mCaretPosition)) -
- mXScroll);
+ getFont()->getWidth(mText.substr(0, mCaretPosition)) - mXScroll);
}
graphics->setColor(Theme::getThemeColor(Theme::TEXT));
graphics->setFont(getFont());
- graphics->drawText(mText, 1 - mXScroll, 1);
+ graphics->drawText(mText, mPadding - mXScroll, mPadding);
+
+ g->popClipRect();
}
void TextField::drawFrame(gcn::Graphics *graphics)
{
- //updateAlpha(); -> Not useful...
+ const int bs = getFrameSize();
- int bs = getFrameSize();
- int w = getWidth() + bs * 2;
- int h = getHeight() + bs * 2;
+ WidgetState state(this);
+ state.width += bs * 2;
+ state.height += bs * 2;
- static_cast<Graphics*>(graphics)->drawImageRect(0, 0, w, h, skin);
+ gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::TextField, state);
}
void TextField::setNumeric(bool numeric)
@@ -156,6 +112,12 @@ int TextField::getValue() const
return value;
}
+void TextField::drawCaret(gcn::Graphics *graphics, int x)
+{
+ graphics->setColor(getForegroundColor());
+ graphics->drawLine(mPadding + x, mPadding, mPadding + x, getHeight() - mPadding);
+}
+
void TextField::keyPressed(gcn::KeyEvent &keyEvent)
{
switch (keyEvent.getKey().getValue())
diff --git a/src/gui/widgets/textfield.h b/src/gui/widgets/textfield.h
index 9235f4b8..b84dd723 100644
--- a/src/gui/widgets/textfield.h
+++ b/src/gui/widgets/textfield.h
@@ -19,16 +19,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef TEXTFIELD_H
-#define TEXTFIELD_H
+#pragma once
#include <guichan/widgets/textfield.hpp>
#include <vector>
class TextInput;
-class ImageRect;
-class TextField;
struct TextHistory
{
@@ -79,7 +76,6 @@ class TextField : public gcn::TextField
*/
TextField(const std::string &text = std::string(),
bool loseFocusOnTab = true);
- ~TextField() override;
/**
* Draws the text field.
@@ -87,11 +83,6 @@ class TextField : public gcn::TextField
void draw(gcn::Graphics *graphics) override;
/**
- * Update the alpha value to the graphic components.
- */
- static void updateAlpha();
-
- /**
* Draws the background and border.
*/
void drawFrame(gcn::Graphics *graphics) override;
@@ -139,42 +130,41 @@ class TextField : public gcn::TextField
* Sets the TextField's source of autocomplete. Passing null will
* disable autocomplete.
*/
- void setAutoComplete(AutoCompleteLister *lister)
- { mAutoComplete = lister; }
+ void setAutoComplete(AutoCompleteLister *lister)
+ { mAutoComplete = lister; }
- /**
+ /**
* Returns the TextField's source of autocomplete.
*/
- AutoCompleteLister *getAutoComplete() const
- { return mAutoComplete; }
+ AutoCompleteLister *getAutoComplete() const
+ { return mAutoComplete; }
- /**
+ /**
* Sets the TextField's source of input history.
*/
- void setHistory(TextHistory *history)
- { mHistory = history; }
+ void setHistory(TextHistory *history)
+ { mHistory = history; }
- /**
+ /**
* Returns the TextField's source of input history.
*/
- TextHistory *getHistory() const
- { return mHistory; }
+ TextHistory *getHistory() const
+ { return mHistory; }
+
+ protected:
+ void drawCaret(gcn::Graphics *graphics, int x) override;
private:
void autoComplete();
void handlePaste();
- static int instances;
- static float mAlpha;
- static ImageRect skin;
bool mNumeric = false;
- int mMinimum;
- int mMaximum;
+ int mMinimum = 0;
+ int mMaximum = 0;
bool mLoseFocusOnTab;
+ int mPadding = 1;
AutoCompleteLister *mAutoComplete = nullptr;
TextHistory *mHistory = nullptr; /**< Text history. */
};
-
-#endif
diff --git a/src/gui/widgets/textpreview.h b/src/gui/widgets/textpreview.h
index 7e88248f..da24b61e 100644
--- a/src/gui/widgets/textpreview.h
+++ b/src/gui/widgets/textpreview.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef TEXTPREVIEW_H
-#define TEXTPREVIEW_H
+#pragma once
#include <guichan/color.hpp>
#include <guichan/font.hpp>
@@ -138,5 +137,3 @@ class TextPreview : public gcn::Widget
bool mShadow = false;
bool mOutline = false;
};
-
-#endif
diff --git a/src/gui/widgets/vertcontainer.h b/src/gui/widgets/vertcontainer.h
index b66957d3..a684453f 100644
--- a/src/gui/widgets/vertcontainer.h
+++ b/src/gui/widgets/vertcontainer.h
@@ -18,8 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GUI_VERTCONTAINER_H
-#define GUI_VERTCONTAINER_H
+#pragma once
#include "gui/widgets/container.h"
@@ -42,5 +41,3 @@ class VertContainer : public Container, public gcn::WidgetListener
int mSpacing;
int mCount = 0;
};
-
-#endif
diff --git a/src/gui/widgets/whispertab.h b/src/gui/widgets/whispertab.h
index 0f01bacc..1a0a4a0f 100644
--- a/src/gui/widgets/whispertab.h
+++ b/src/gui/widgets/whispertab.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef WHISPERTAB_H
-#define WHISPERTAB_H
+#pragma once
#include "chattab.h"
@@ -63,5 +62,3 @@ class WhisperTab : public ChatTab
private:
std::string mNick;
};
-
-#endif // CHANNELTAB_H
diff --git a/src/gui/widgets/window.cpp b/src/gui/widgets/window.cpp
index 8bf9d081..61ed7896 100644
--- a/src/gui/widgets/window.cpp
+++ b/src/gui/widgets/window.cpp
@@ -23,6 +23,7 @@
#include "configuration.h"
#include "log.h"
+#include "textrenderer.h"
#include "gui/gui.h"
#include "gui/viewport.h"
@@ -31,7 +32,6 @@
#include "gui/widgets/resizegrip.h"
#include "gui/widgets/windowcontainer.h"
-#include "resources/image.h"
#include "resources/theme.h"
#include <guichan/exception.hpp>
@@ -43,13 +43,12 @@
int Window::instances = 0;
int Window::mouseResize = 0;
-Window::Window(const std::string &caption, bool modal, Window *parent,
- const std::string &skin):
- gcn::Window(caption),
- mParent(parent),
- mModal(modal),
- mMaxWinWidth(graphics->getWidth()),
- mMaxWinHeight(graphics->getHeight())
+Window::Window(const std::string &caption, bool modal, Window *parent)
+ : gcn::Window(caption)
+ , mParent(parent)
+ , mModal(modal)
+ , mMaxWinWidth(graphics->getWidth())
+ , mMaxWinHeight(graphics->getHeight())
{
logger->log("Window::Window(\"%s\")", caption.c_str());
@@ -58,19 +57,17 @@ Window::Window(const std::string &caption, bool modal, Window *parent,
instances++;
- setFrameSize(0);
- setPadding(3);
- setTitleBarHeight(20);
-
- // Loads the skin
- mSkin = Theme::instance()->load(skin);
+ auto &skin = gui->getTheme()->getSkin(SkinType::Window);
+ setFrameSize(skin.frameSize);
+ setPadding(skin.padding);
+ setTitleBarHeight(skin.titleBarHeight);
// Add this window to the window container
windowContainer->add(this);
if (mModal)
{
- gui->setCursorType(Cursor::POINTER);
+ gui->setCursorType(Cursor::Pointer);
requestModalFocus();
}
@@ -94,8 +91,6 @@ Window::~Window()
removeWidgetListener(this);
instances--;
-
- mSkin->instances--;
}
void Window::setWindowContainer(WindowContainer *wc)
@@ -105,44 +100,58 @@ void Window::setWindowContainer(WindowContainer *wc)
void Window::draw(gcn::Graphics *graphics)
{
- auto *g = static_cast<Graphics*>(graphics);
+ if (getFrameSize() == 0)
+ drawFrame(graphics);
- g->drawImageRect(0, 0, getWidth(), getHeight(), mSkin->getBorder());
+ auto g = static_cast<Graphics*>(graphics);
- // Draw title
- if (mShowTitle)
- {
- g->setColor(Theme::getThemeColor(Theme::TEXT));
- g->setFont(getFont());
- g->drawText(getCaption(), 7, 5, gcn::Graphics::LEFT);
- }
-
- // Draw Close Button
if (mCloseButton)
{
- g->drawImage(mSkin->getCloseImage(),
- getWidth() - mSkin->getCloseImage()->getWidth() - getPadding(),
- getPadding());
+ WidgetState state(getCloseButtonRect(), mCloseButtonHovered ? STATE_HOVERED : 0);
+ gui->getTheme()->drawSkin(g, SkinType::ButtonClose, state);
}
- // Draw Sticky Button
if (mStickyButton)
{
- Image *button = mSkin->getStickyImage(mSticky);
- int x = getWidth() - button->getWidth() - getPadding();
- if (mCloseButton)
- x -= mSkin->getCloseImage()->getWidth();
-
- g->drawImage(button, x, getPadding());
+ WidgetState state(getStickyButtonRect(), mSticky ? STATE_SELECTED : 0);
+ gui->getTheme()->drawSkin(g, SkinType::ButtonSticky, state);
}
drawChildren(graphics);
}
+void Window::drawFrame(gcn::Graphics *graphics)
+{
+ auto g = static_cast<Graphics*>(graphics);
+ auto theme = gui->getTheme();
+
+ WidgetState widgetState(this);
+ widgetState.width += getFrameSize() * 2;
+ widgetState.height += getFrameSize() * 2;
+
+ auto &skin = theme->getSkin(SkinType::Window);
+ skin.draw(g, widgetState);
+
+ if (mShowTitle)
+ {
+ if (auto skinState = skin.getState(widgetState.flags))
+ {
+ auto &textFormat = skinState->textFormat;
+ TextRenderer::renderText(g,
+ getCaption(),
+ getFrameSize() + skin.titleOffsetX,
+ getFrameSize() + skin.titleOffsetY,
+ gcn::Graphics::LEFT,
+ textFormat.bold ? boldFont : getFont(),
+ textFormat);
+ }
+ }
+}
+
void Window::setContentSize(int width, int height)
{
- width = width + 2 * getPadding();
- height = height + getPadding() + getTitleBarHeight();
+ width += 2 * getPadding();
+ height += getPadding() + getTitleBarHeight();
if (getMinWidth() > width)
width = getMinWidth();
@@ -156,6 +165,16 @@ void Window::setContentSize(int width, int height)
setSize(width, height);
}
+void Window::setMinimumContentSize(int width, int height)
+{
+ const int padding = getPadding();
+ const int titleBarHeight = getTitleBarHeight();
+ auto &skin = gui->getTheme()->getSkin(SkinType::Window);
+
+ setMinWidth(std::max(skin.getMinWidth(), width + 2 * padding));
+ setMinHeight(std::max(skin.getMinHeight(), height + padding + titleBarHeight));
+}
+
void Window::setLocationRelativeTo(gcn::Widget *widget)
{
int wx;
@@ -218,13 +237,12 @@ void Window::setLocationRelativeTo(ImageRect::ImagePosition position,
void Window::setMinWidth(int width)
{
- mMinWinWidth = width > mSkin->getMinWidth() ? width : mSkin->getMinWidth();
+ mMinWinWidth = std::max(gui->getTheme()->getMinWidth(SkinType::Window), width);
}
void Window::setMinHeight(int height)
{
- mMinWinHeight = height > mSkin->getMinHeight() ?
- height : mSkin->getMinHeight();
+ mMinWinHeight = std::max(gui->getTheme()->getMinHeight(SkinType::Window), height);
}
void Window::setMaxWidth(int width)
@@ -276,9 +294,7 @@ void Window::widgetResized(const gcn::Event &event)
void Window::widgetHidden(const gcn::Event &event)
{
if (gui)
- {
- gui->setCursorType(Cursor::POINTER);
- }
+ gui->setCursorType(Cursor::Pointer);
WidgetListIterator it;
@@ -341,38 +357,13 @@ void Window::mousePressed(gcn::MouseEvent &event)
const int x = event.getX();
const int y = event.getY();
- // Handle close button
- if (mCloseButton)
- {
- gcn::Rectangle closeButtonRect(
- getWidth() - mSkin->getCloseImage()->getWidth() - getPadding(),
- getPadding(),
- mSkin->getCloseImage()->getWidth(),
- mSkin->getCloseImage()->getHeight());
-
- if (closeButtonRect.isPointInRect(x, y))
- {
- close();
- }
- }
+ if (mCloseButton && getCloseButtonRect().isPointInRect(x, y))
+ close();
- // Handle sticky button
- if (mStickyButton)
- {
- Image *button = mSkin->getStickyImage(mSticky);
- int rx = getWidth() - button->getWidth() - getPadding();
- if (mCloseButton)
- rx -= mSkin->getCloseImage()->getWidth();
- gcn::Rectangle stickyButtonRect(rx, getPadding(),
- button->getWidth(), button->getHeight());
-
- if (stickyButtonRect.isPointInRect(x, y))
- {
- setSticky(!isSticky());
- }
- }
+ if (mStickyButton && getStickyButtonRect().isPointInRect(x, y))
+ setSticky(!isSticky());
- // Handle window resizing
+ // Update resizing state and disable moving if we're resizing the window
mouseResize = getResizeHandles(event);
if (mouseResize)
mMoved = false;
@@ -394,11 +385,15 @@ void Window::mouseReleased(gcn::MouseEvent &event)
void Window::mouseExited(gcn::MouseEvent &event)
{
if (mGrip && !mouseResize)
- gui->setCursorType(Cursor::POINTER);
+ gui->setCursorType(Cursor::Pointer);
+
+ mCloseButtonHovered = false;
}
void Window::mouseMoved(gcn::MouseEvent &event)
{
+ mCloseButtonHovered = false;
+
// Make sure BeingPopup is hidden (Viewport does not receive mouseExited)
if (viewport)
viewport->hideBeingPopup();
@@ -408,30 +403,36 @@ void Window::mouseMoved(gcn::MouseEvent &event)
if (event.isConsumed())
return;
- int resizeHandles = getResizeHandles(event);
+ mCloseButtonHovered = getCloseButtonRect().isPointInRect(event.getX(), event.getY());
+ Cursor cursor = Cursor::Pointer;
// Changes the custom mouse cursor based on its current position.
- switch (resizeHandles)
+ if (!mCloseButtonHovered)
{
+ switch (getResizeHandles(event))
+ {
case BOTTOM | RIGHT:
case TOP | LEFT:
- gui->setCursorType(Cursor::RESIZE_DOWN_RIGHT);
+ cursor = Cursor::ResizeDownRight;
break;
case BOTTOM | LEFT:
case TOP | RIGHT:
- gui->setCursorType(Cursor::RESIZE_DOWN_LEFT);
+ cursor = Cursor::ResizeDownLeft;
break;
case BOTTOM:
case TOP:
- gui->setCursorType(Cursor::RESIZE_DOWN);
+ cursor = Cursor::ResizeDown;
break;
case RIGHT:
case LEFT:
- gui->setCursorType(Cursor::RESIZE_ACROSS);
+ cursor = Cursor::ResizeAcross;
break;
default:
- gui->setCursorType(Cursor::POINTER);
+ break;
+ }
}
+
+ gui->setCursorType(cursor);
}
void Window::mouseDragged(gcn::MouseEvent &event)
@@ -675,6 +676,13 @@ int Window::getResizeHandles(gcn::MouseEvent &event)
if (inPadding && event.getSource() == this)
{
+ /**
+ * The width of the resize border. Is independent of the actual window
+ * border width, and determines mostly the size of the corner area
+ * where two borders are moved at the same time.
+ */
+ const int resizeBorderWidth = std::max(mGrip->getWidth(), 10);
+
resizeHandles |= (x >= getWidth() - resizeBorderWidth) ? RIGHT :
(x < resizeBorderWidth) ? LEFT : 0;
resizeHandles |= (y >= getHeight() - resizeBorderWidth) ? BOTTOM :
@@ -692,10 +700,55 @@ int Window::getResizeHandles(gcn::MouseEvent &event)
return resizeHandles;
}
+gcn::Rectangle Window::getCloseButtonRect() const
+{
+ if (!mCloseButton)
+ return {};
+
+ auto theme = gui->getTheme();
+
+ auto &closeButtonSkin = theme->getSkin(SkinType::ButtonClose);
+ const int closeButtonWidth = closeButtonSkin.getMinWidth();
+ const int closeButtonHeight = closeButtonSkin.getMinHeight();
+
+ return {
+ getWidth() - closeButtonWidth - closeButtonSkin.padding,
+ closeButtonSkin.padding,
+ closeButtonWidth,
+ closeButtonHeight
+ };
+}
+
+gcn::Rectangle Window::getStickyButtonRect() const
+{
+ if (!mStickyButton)
+ return {};
+
+ auto theme = gui->getTheme();
+
+ auto &closeButtonSkin = theme->getSkin(SkinType::ButtonClose);
+ const int closeButtonWidth = closeButtonSkin.getMinWidth();
+
+ auto &stickyButtonSkin = theme->getSkin(SkinType::ButtonSticky);
+ const int stickyButtonWidth = stickyButtonSkin.getMinWidth();
+ const int stickyButtonHeight = stickyButtonSkin.getMinHeight();
+
+ int stickyButtonX = getWidth() - stickyButtonWidth - stickyButtonSkin.padding;
+ if (mCloseButton)
+ stickyButtonX -= closeButtonWidth + closeButtonSkin.padding;
+
+ return {
+ stickyButtonX,
+ stickyButtonSkin.padding,
+ stickyButtonWidth,
+ stickyButtonHeight
+ };
+}
+
int Window::getGuiAlpha()
{
float alpha = std::max(config.guiAlpha,
- Theme::instance()->getMinimumOpacity());
+ gui->getTheme()->getMinimumOpacity());
return (int) (alpha * 255.0f);
}
diff --git a/src/gui/widgets/window.h b/src/gui/widgets/window.h
index bf6f363c..bf459a32 100644
--- a/src/gui/widgets/window.h
+++ b/src/gui/widgets/window.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef WINDOW_H
-#define WINDOW_H
+#pragma once
#include "graphics.h"
#include "guichanfwd.h"
@@ -54,10 +53,9 @@ 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 = nullptr, const std::string &skin = "window.xml");
+ Window *parent = nullptr);
/**
* Destructor. Deletes all the added widgets.
@@ -70,16 +68,26 @@ class Window : public gcn::Window, gcn::WidgetListener
static void setWindowContainer(WindowContainer *windowContainer);
/**
- * Draws the window.
+ * Draws the window contents.
*/
void draw(gcn::Graphics *graphics) override;
/**
+ * Draws the window frame.
+ */
+ void drawFrame(gcn::Graphics *graphics) override;
+
+ /**
* Sets the size of this window.
*/
void setContentSize(int width, int height);
/**
+ * Sets the minimum size of the window content.
+ */
+ void setMinimumContentSize(int width, int height);
+
+ /**
* Sets the location relative to the given widget.
*/
void setLocationRelativeTo(gcn::Widget *widget);
@@ -377,6 +385,9 @@ class Window : public gcn::Window, gcn::WidgetListener
*/
int getResizeHandles(gcn::MouseEvent &event);
+ gcn::Rectangle getCloseButtonRect() const;
+ gcn::Rectangle getStickyButtonRect() const;
+
ResizeGrip *mGrip = nullptr; /**< Resize grip */
Window *mParent; /**< The parent window */
Layout *mLayout = nullptr; /**< Layout handler */
@@ -384,6 +395,7 @@ class Window : public gcn::Window, gcn::WidgetListener
bool mShowTitle = true; /**< Window has a title bar */
bool mModal; /**< Window is modal */
bool mCloseButton = false; /**< Window has a close button */
+ bool mCloseButtonHovered = false;
bool mDefaultVisible = false; /**< Window's default visibility */
bool mSaveVisible = false; /**< Window will save visibility */
bool mStickyButton = false; /**< Window has a sticky button */
@@ -398,15 +410,4 @@ class Window : public gcn::Window, gcn::WidgetListener
int mDefaultHeight; /**< Default window height */
static int instances; /**< Number of Window instances */
-
- Skin *mSkin; /**< Skin in use by this window */
-
- /**
- * The width of the resize border. Is independent of the actual window
- * border width, and determines mostly the size of the corner area
- * where two borders are moved at the same time.
- */
- static const int resizeBorderWidth = 10;
};
-
-#endif
diff --git a/src/gui/widgets/windowcontainer.cpp b/src/gui/widgets/windowcontainer.cpp
index 36f0998f..4e350a9e 100644
--- a/src/gui/widgets/windowcontainer.cpp
+++ b/src/gui/widgets/windowcontainer.cpp
@@ -24,16 +24,15 @@
#include "gui/gui.h"
#include "gui/widgets/window.h"
-#include "utils/dtor.h"
-
#include <guichan/focushandler.hpp>
WindowContainer *windowContainer = nullptr;
void WindowContainer::logic()
{
- delete_all(mDeathList);
- mDeathList.clear();
+ for (auto widget : mScheduledDeletions)
+ delete widget;
+ mScheduledDeletions.clear();
gcn::Container::logic();
}
@@ -48,7 +47,7 @@ void WindowContainer::draw(gcn::Graphics *graphics)
void WindowContainer::scheduleDelete(gcn::Widget *widget)
{
- mDeathList.push_back(widget);
+ mScheduledDeletions.insert(widget);
}
void WindowContainer::adjustAfterResize(int oldScreenWidth,
diff --git a/src/gui/widgets/windowcontainer.h b/src/gui/widgets/windowcontainer.h
index 861839c9..ff03a903 100644
--- a/src/gui/widgets/windowcontainer.h
+++ b/src/gui/widgets/windowcontainer.h
@@ -19,11 +19,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef WINDOWCONTAINER_H
-#define WINDOWCONTAINER_H
+#pragma once
#include "gui/widgets/container.h"
+#include <set>
+
/**
* A window container. This container adds functionality for more convenient
* widget (windows in particular) destruction.
@@ -65,11 +66,9 @@ class WindowContainer : public Container
bool widgetIsVisible(gcn::Widget *widget);
/**
- * List of widgets that are scheduled to be deleted.
+ * Set of widgets that are scheduled to be deleted.
*/
- std::list<gcn::Widget *> mDeathList;
+ std::set<gcn::Widget *> mScheduledDeletions;
};
extern WindowContainer *windowContainer;
-
-#endif
diff --git a/src/gui/windowmenu.cpp b/src/gui/windowmenu.cpp
index c6e52ee7..0b2d126f 100644
--- a/src/gui/windowmenu.cpp
+++ b/src/gui/windowmenu.cpp
@@ -25,7 +25,7 @@
#include "gui/emotepopup.h"
#include "gui/skilldialog.h"
-#include "gui/specialswindow.h"
+#include "gui/abilitieswindow.h"
#include "gui/widgets/button.h"
#include "gui/widgets/window.h"
@@ -61,8 +61,8 @@ WindowMenu::WindowMenu()
addButton(N_("Skills"), x, h, "button-icon-skills.png",
KeyboardConfig::KEY_WINDOW_SKILL);
- if (specialsWindow->hasSpecials())
- addButton(N_("Specials"), x, h, "button-icon-specials.png");
+ if (abilitiesWindow->hasAbilities())
+ addButton(N_("Abilities"), x, h, "button-icon-abilities.png");
addButton(N_("Social"), x, h, "button-icon-social.png",
KeyboardConfig::KEY_WINDOW_SOCIAL);
@@ -122,9 +122,9 @@ void WindowMenu::action(const gcn::ActionEvent &event)
{
window = skillDialog;
}
- else if (event.getId() == "Specials")
+ else if (event.getId() == "Abilities")
{
- window = specialsWindow;
+ window = abilitiesWindow;
}
else if (event.getId() == "Social")
{
diff --git a/src/gui/windowmenu.h b/src/gui/windowmenu.h
index 0eabc247..b80b62b0 100644
--- a/src/gui/windowmenu.h
+++ b/src/gui/windowmenu.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef WINDOWMENU_H
-#define WINDOWMENU_H
+#pragma once
#include "keyboardconfig.h"
@@ -62,5 +61,3 @@ class WindowMenu : public Container,
EmotePopup *mEmotePopup = nullptr;
};
-
-#endif
diff --git a/src/gui/worldselectdialog.h b/src/gui/worldselectdialog.h
index a1150cb8..702a5616 100644
--- a/src/gui/worldselectdialog.h
+++ b/src/gui/worldselectdialog.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef WORLD_SELECT_DIALOG_H
-#define WORLD_SELECT_DIALOG_H
+#pragma once
#include "gui/widgets/window.h"
@@ -62,5 +61,3 @@ class WorldSelectDialog : public Window, public gcn::ActionListener,
gcn::Button *mChangeLoginButton;
gcn::Button *mChooseWorld;
};
-
-#endif // WORLD_SELECT_DIALOG_H
diff --git a/src/guichanfwd.h b/src/guichanfwd.h
index 7ef1a419..9c4d8b5f 100644
--- a/src/guichanfwd.h
+++ b/src/guichanfwd.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GUICHANFWD_H
-#define GUICHANFWD_H
+#pragma once
namespace gcn
{
@@ -97,5 +96,3 @@ namespace gcn
class WidgetListener;
class Window;
}
-
-#endif
diff --git a/src/guild.h b/src/guild.h
index e24893b4..cdd3a357 100644
--- a/src/guild.h
+++ b/src/guild.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GUILD_H
-#define GUILD_H
+#pragma once
#include "avatar.h"
@@ -170,5 +169,3 @@ private:
short mId;
bool mCanInviteUsers = false;
};
-
-#endif // GUILD_H
diff --git a/src/imageparticle.cpp b/src/imageparticle.cpp
index 9cde91d0..86f955c0 100644
--- a/src/imageparticle.cpp
+++ b/src/imageparticle.cpp
@@ -27,17 +27,12 @@
ImageParticle::ImageParticle(Map *map, Image *image):
Particle(map),
- mImage(image)
+ mImageRef(image)
{
- if (mImage)
- mImage->incRef();
+ mImage = mImageRef;
}
-ImageParticle::~ImageParticle()
-{
- if (mImage)
- mImage->decRef();
-}
+ImageParticle::~ImageParticle() = default;
bool ImageParticle::draw(Graphics *graphics, int offsetX, int offsetY) const
{
diff --git a/src/imageparticle.h b/src/imageparticle.h
index ac677389..0f135c33 100644
--- a/src/imageparticle.h
+++ b/src/imageparticle.h
@@ -19,11 +19,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef IMAGEPARTICLE_H
-#define IMAGEPARTICLE_H
+#pragma once
#include "particle.h"
+#include "resources/resource.h"
+
class Image;
class Map;
@@ -50,6 +51,5 @@ class ImageParticle : public Particle
protected:
Image *mImage; /**< The image used for this particle. */
+ ResourceRef<Image> mImageRef;
};
-
-#endif
diff --git a/src/imagesprite.cpp b/src/imagesprite.cpp
deleted file mode 100644
index 9ef27cd1..00000000
--- a/src/imagesprite.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * The Mana Client
- * Copyright (C) 2010-2012 The Mana Developers
- *
- * This file is part of The Mana Client.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "imagesprite.h"
-
-#include "graphics.h"
-
-ImageSprite::ImageSprite(Image *image):
- mImage(image)
-{
- mAlpha = mImage->getAlpha();
-}
-
-ImageSprite::~ImageSprite() = default;
-
-bool ImageSprite::draw(Graphics *graphics, int posX, int posY) const
-{
- if (mImage->getAlpha() != mAlpha)
- mImage->setAlpha(mAlpha);
-
- return graphics->drawImage(mImage, posX, posY);
-}
diff --git a/src/imagesprite.h b/src/imagesprite.h
deleted file mode 100644
index 4fc69927..00000000
--- a/src/imagesprite.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * The Mana Client
- * Copyright (C) 2010-2012 The Mana Developers
- *
- * This file is part of The Mana Client.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef IMAGESPRITE_H
-#define IMAGESPRITE_H
-
-#include "sprite.h"
-
-#include "resources/image.h"
-
-class Graphics;
-
-class ImageSprite final : public Sprite
-{
-public:
- ImageSprite(Image *image);
-
- ~ImageSprite() override;
-
- bool reset() override
- { return false; }
-
- bool play(const std::string &action) override
- { return false; }
-
- bool update(int time) override
- { return false; }
-
- bool draw(Graphics *graphics, int posX, int posY) const override;
-
- int getWidth() const override
- { return mImage->getWidth(); }
-
- int getHeight() const override
- { return mImage->getHeight(); }
-
- const Image *getImage() const override
- { return mImage; }
-
- bool setDirection(SpriteDirection direction) override
- { return false; }
-
- int getDuration() const override
- { return 0; }
-
- // Hack to allow the ImageSprite to be used with SubImage instances, which
- // are not reference counted.
- void releaseImageRef()
- { mImage.release(); }
-
-private:
- ResourceRef<Image> mImage;
-};
-
-#endif // IMAGESPRITE_H
diff --git a/src/inventory.cpp b/src/inventory.cpp
index e689f8bb..6aa30c00 100644
--- a/src/inventory.cpp
+++ b/src/inventory.cpp
@@ -159,7 +159,5 @@ void Inventory::removeInventoryListener(InventoryListener* listener)
void Inventory::distributeSlotsChangedEvent()
{
for (auto inventoryListener : mInventoryListeners)
- {
inventoryListener->slotsChanged(this);
- }
}
diff --git a/src/inventory.h b/src/inventory.h
index 7aea3013..40a9403a 100644
--- a/src/inventory.h
+++ b/src/inventory.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef INVENTORY_H
-#define INVENTORY_H
+#pragma once
#include <list>
@@ -125,7 +124,6 @@ class Inventory
int getLastUsedSlot() const;
void addInventoryListener(InventoryListener* listener);
-
void removeInventoryListener(InventoryListener* listener);
int getType() const
@@ -144,5 +142,3 @@ class Inventory
int mSize; /**< The max number of inventory items */
int mUsed = 0; /**< THe number of slots in use */
};
-
-#endif // INVENTORY_H
diff --git a/src/item.cpp b/src/item.cpp
index cae545b1..65fda05b 100644
--- a/src/item.cpp
+++ b/src/item.cpp
@@ -24,7 +24,6 @@
#include "configuration.h"
#include "event.h"
-#include "resources/image.h"
#include "resources/iteminfo.h"
#include "resources/resourcemanager.h"
#include "resources/theme.h"
@@ -36,8 +35,6 @@ Item::Item(int id, int quantity, bool equipped):
setId(id);
}
-Item::~Item() = default;
-
void Item::setId(int id)
{
mId = id;
@@ -48,12 +45,10 @@ void Item::setId(int id)
mImage = resman->getImage(paths.getStringValue("itemIcons") + display.image);
if (!mImage)
+ {
mImage = Theme::getImageFromTheme(paths.getValue("unknownItemFile",
"unknown-item.png"));
-
- // Remove the automatic reference added by the ResourceManager
- if (mImage)
- mImage->decRef();
+ }
}
void Item::doEvent(Event::Type eventName)
diff --git a/src/item.h b/src/item.h
index b5dd1fe2..4ed552ed 100644
--- a/src/item.h
+++ b/src/item.h
@@ -19,15 +19,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef ITEM_H
-#define ITEM_H
+#pragma once
#include "event.h"
+#include "resources/image.h"
#include "resources/itemdb.h"
-#include "resources/resource.h"
-
-class Image;
const int ITEM_ICON_SIZE = 32;
@@ -39,7 +36,7 @@ class Item
public:
Item(int id = -1, int quantity = 0, bool equipped = false);
- virtual ~Item();
+ virtual ~Item() = default;
/**
* Sets the item id, identifying the item type.
@@ -82,16 +79,6 @@ class Item
bool isEquipped() const { return mEquipped; }
/**
- * Sets whether this item is in equipment.
- */
- void setInEquipment(bool inEquipment) { mInEquipment = inEquipment; }
-
- /**
- * Returns whether this item is in equipment.
- */
- bool isInEquipment() const { return mInEquipment; }
-
- /**
* Returns whether this item is equippable.
*/
bool isEquippable() const;
@@ -120,8 +107,5 @@ class Item
ResourceRef<Image> mImage; /**< Item image. */
int mQuantity; /**< Number of items. */
bool mEquipped; /**< Item is equipped. */
- bool mInEquipment = false; /**< Item is in equipment */
int mInvIndex; /**< Inventory index. */
};
-
-#endif // ITEM_H
diff --git a/src/itemshortcut.h b/src/itemshortcut.h
index 620973b6..fcec3d7d 100644
--- a/src/itemshortcut.h
+++ b/src/itemshortcut.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef ITEMSHORTCUT_H
-#define ITEMSHORTCUT_H
+#pragma once
#define SHORTCUT_ITEMS 12
@@ -115,5 +114,3 @@ class ItemShortcut
};
extern ItemShortcut *itemShortcut;
-
-#endif
diff --git a/src/joystick.h b/src/joystick.h
index 0a5e51a4..c3dea9e1 100644
--- a/src/joystick.h
+++ b/src/joystick.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef JOYSTICK_H
-#define JOYSTICK_H
+#pragma once
#include <SDL.h>
@@ -99,5 +98,3 @@ class Joystick
void doCalibration();
};
-
-#endif // JOYSTICK_H
diff --git a/src/keyboardconfig.cpp b/src/keyboardconfig.cpp
index c4d7a26d..ec5b5e37 100644
--- a/src/keyboardconfig.cpp
+++ b/src/keyboardconfig.cpp
@@ -153,24 +153,21 @@ void KeyboardConfig::store()
void KeyboardConfig::makeDefault()
{
for (auto &key : mKey)
- {
key.value = key.defaultValue;
- }
}
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 (int i = 0; i < KEY_TOTAL; i++)
{
if (mKey[i].value == KEY_NO_VALUE)
continue;
- for (j = i, j++; j < KEY_TOTAL; j++)
+ for (int j = i + 1; j < KEY_TOTAL; j++)
{
if (mKey[j].value == KEY_NO_VALUE)
continue;
@@ -220,25 +217,33 @@ const std::string &KeyboardConfig::getKeyCaption(int index) const
int KeyboardConfig::getKeyIndex(SDL_Keycode keyValue) const
{
for (int i = 0; i < KEY_TOTAL; i++)
- {
if (keyValue == mKey[i].value)
- {
return i;
- }
- }
+
return KEY_NO_VALUE;
}
+std::string_view KeyboardConfig::getKeyName(std::string_view configName) const
+{
+ for (auto key : mKey)
+ {
+ if (configName == key.configField)
+ {
+ if (key.value == KEY_NO_VALUE)
+ return {};
+ return SDL_GetKeyName(key.value);
+ }
+ }
+
+ return {};
+}
int KeyboardConfig::getKeyEmoteOffset(SDL_Keycode keyValue) const
{
for (int i = KEY_EMOTE_1; i <= KEY_EMOTE_12; i++)
- {
if (keyValue == mKey[i].value)
- {
return i - KEY_EMOTE_1;
- }
- }
+
return -1;
}
diff --git a/src/keyboardconfig.h b/src/keyboardconfig.h
index e2d91e29..6fc79ced 100644
--- a/src/keyboardconfig.h
+++ b/src/keyboardconfig.h
@@ -20,8 +20,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef KEYBOARDCONFIG_H
-#define KEYBOARDCONFIG_H
+#pragma once
#include <cstdint>
#include <string>
@@ -102,6 +101,11 @@ class KeyboardConfig
int getKeyIndex(SDL_Keycode keyValue) const;
/**
+ * Get the key name by providing the keys config name.
+ */
+ std::string_view getKeyName(std::string_view configName) const;
+
+ /**
* Get the key function index for an emote by providing the offset value.
*/
int getKeyEmoteOffset(SDL_Keycode keyValue) const;
@@ -235,5 +239,3 @@ class KeyboardConfig
};
extern KeyboardConfig keyboard;
-
-#endif
diff --git a/src/localplayer.cpp b/src/localplayer.cpp
index e2af5fa3..043e9d25 100644
--- a/src/localplayer.cpp
+++ b/src/localplayer.cpp
@@ -55,24 +55,18 @@ constexpr unsigned ACTION_TIMEOUT = 182;
LocalPlayer *local_player = nullptr;
-LocalPlayer::LocalPlayer(int id, int subtype):
- Being(id, PLAYER, subtype, nullptr)
+LocalPlayer::LocalPlayer(int id, int subtype)
+ : Being(id, PLAYER, subtype, nullptr)
+ , mAwayListener(std::make_unique<AwayListener>())
{
- listen(Event::AttributesChannel);
-
- mAwayListener = new AwayListener();
-
setShowName(config.showOwnName);
- listen(Event::ConfigChannel);
listen(Event::ActorSpriteChannel);
+ listen(Event::AttributesChannel);
+ listen(Event::ConfigChannel);
}
-LocalPlayer::~LocalPlayer()
-{
- delete mAwayDialog;
- delete mAwayListener;
-}
+LocalPlayer::~LocalPlayer() = default;
void LocalPlayer::logic()
{
@@ -910,7 +904,7 @@ void LocalPlayer::setAttackRange(int range)
{
mAttackRange = range;
}
- else if (Net::getNetworkType() == ServerType::TMWATHENA)
+ else if (Net::getNetworkType() == ServerType::TmwAthena)
{
// TODO: Fix this to be more generic
Item *weapon = PlayerInfo::getEquipment(TmwAthena::EQUIP_FIGHT1_SLOT);
@@ -932,7 +926,7 @@ bool LocalPlayer::withinRange(Actor *target, int range) const
const Vector &pos = getPosition();
const int dx = abs(targetPos.x - pos.x);
const int dy = abs(targetPos.y - pos.y);
- return !(dx > range || dy > range);
+ return dx <= range && dy <= range;
}
void LocalPlayer::setGotoTarget(Being *target)
@@ -1018,27 +1012,38 @@ void LocalPlayer::event(Event::Channel channel, const Event &event)
Being::event(channel, event);
}
-void LocalPlayer::changeAwayMode()
+void LocalPlayer::updateStatusEffect(int id, bool newStatus)
{
- mAwayMode = !mAwayMode;
- mAfkTimer.reset();
+ Event event(Event::UpdateStatusEffect);
+ event.setInt("index", id);
+ event.setBool("newStatus", newStatus);
+ event.trigger(Event::ActorSpriteChannel);
- if (mAwayMode)
- {
- auto msg = config.afkMessage.empty() ? _("I am away from keyboard")
- : config.afkMessage;
- mAwayDialog = new OkDialog(_("Away"), msg);
- mAwayDialog->addActionListener(mAwayListener);
- }
+ Being::updateStatusEffect(id, newStatus);
+}
- mAwayDialog = nullptr;
+static std::string afkMessage()
+{
+ return config.afkMessage.empty() ? _("I am away from keyboard")
+ : config.afkMessage;
}
-void LocalPlayer::setAway(const std::string &message)
+void LocalPlayer::setAwayMode(bool away)
{
- if (!message.empty())
- config.afkMessage = message;
- changeAwayMode();
+ if (mAwayMode == away)
+ return;
+
+ mAwayMode = away;
+
+ if (mAwayMode)
+ {
+ mAwayListener->showDialog(afkMessage());
+ mAfkTimer.reset();
+ }
+ else
+ {
+ mAwayListener->closeDialog();
+ }
}
void LocalPlayer::afkRespond(ChatTab *tab, const std::string &nick)
@@ -1046,9 +1051,7 @@ void LocalPlayer::afkRespond(ChatTab *tab, const std::string &nick)
if (!mAwayMode || !mAfkTimer.passed())
return;
- auto msg = config.afkMessage.empty() ? _("I am away from keyboard")
- : config.afkMessage;
- msg = strprintf(_("*AFK*: %s"), msg.c_str());
+ auto msg = strprintf(_("*AFK*: %s"), afkMessage().c_str());
Net::getChatHandler()->privateMessage(nick, msg);
if (!tab)
@@ -1064,10 +1067,40 @@ void LocalPlayer::afkRespond(ChatTab *tab, const std::string &nick)
mAfkTimer.set(AWAY_MESSAGE_TIMEOUT);
}
-void AwayListener::action(const gcn::ActionEvent &event)
+AwayListener::~AwayListener()
{
- if (event.getId() == "ok" && local_player->getAwayMode())
+ if (mAwayDialog)
{
- local_player->changeAwayMode();
+ mAwayDialog->removeActionListener(this);
+ mAwayDialog->removeDeathListener(this);
+ mAwayDialog->scheduleDelete();
}
}
+
+void AwayListener::showDialog(const std::string &message)
+{
+ if (mAwayDialog)
+ return;
+
+ mAwayDialog = new OkDialog(_("Away"), message);
+ mAwayDialog->addActionListener(this);
+ mAwayDialog->addDeathListener(this);
+}
+
+void AwayListener::closeDialog()
+{
+ if (mAwayDialog)
+ mAwayDialog->scheduleDelete();
+}
+
+void AwayListener::action(const gcn::ActionEvent &event)
+{
+ if (event.getId() == "ok")
+ local_player->setAwayMode(false);
+}
+
+void AwayListener::death(const gcn::Event &event)
+{
+ if (mAwayDialog == event.getSource())
+ mAwayDialog = nullptr;
+}
diff --git a/src/localplayer.h b/src/localplayer.h
index 34021cbd..a5b08449 100644
--- a/src/localplayer.h
+++ b/src/localplayer.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef LOCALPLAYER_H
-#define LOCALPLAYER_H
+#pragma once
#include "being.h"
@@ -29,6 +28,9 @@
#include "utils/time.h"
#include <guichan/actionlistener.hpp>
+#include <guichan/deathlistener.hpp>
+
+#include <memory>
class ChatTab;
class FloorItem;
@@ -36,10 +38,20 @@ class ImageSet;
class Map;
class OkDialog;
-class AwayListener : public gcn::ActionListener
+class AwayListener : public gcn::ActionListener, public gcn::DeathListener
{
public:
+ AwayListener() = default;
+ ~AwayListener() override;
+
+ void showDialog(const std::string &message);
+ void closeDialog();
+
void action(const gcn::ActionEvent &event) override;
+ void death(const gcn::Event &event) override;
+
+ private:
+ OkDialog *mAwayDialog = nullptr;
};
/**
@@ -62,8 +74,7 @@ enum
class LocalPlayer final : public Being
{
public:
- LocalPlayer(int id= 65535, int subtype = 0);
-
+ LocalPlayer(int id = 65535, int subtype = 0);
~LocalPlayer() override;
void logic() override;
@@ -171,13 +182,10 @@ class LocalPlayer final : public Being
bool isPathSetByMouse() const
{ return mPathSetByMouse; }
- void changeAwayMode();
-
+ void setAwayMode(bool away);
bool getAwayMode() const
{ return mAwayMode; }
- void setAway(const std::string &message);
-
void afkRespond(ChatTab *tab, const std::string &nick);
void addMessageToQueue(const std::string &message,
@@ -186,6 +194,8 @@ class LocalPlayer final : public Being
void event(Event::Channel channel, const Event &event) override;
protected:
+ void updateStatusEffect(int id, bool newStatus) override;
+
/** Make the character starts to walk. */
void startWalking(unsigned char dir);
@@ -229,12 +239,9 @@ class LocalPlayer final : public Being
bool mShowIp = false;
- AwayListener *mAwayListener;
- OkDialog *mAwayDialog = nullptr;
+ std::unique_ptr<AwayListener> mAwayListener;
Timer mAfkTimer;
bool mAwayMode = false;
};
extern LocalPlayer *local_player;
-
-#endif // LOCALPLAYER_H
diff --git a/src/log.h b/src/log.h
index 14e0a560..81e6bc95 100644
--- a/src/log.h
+++ b/src/log.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef LOG_H
-#define LOG_H
+#pragma once
#include <fstream>
@@ -69,5 +68,3 @@ class Logger
};
extern Logger *logger;
-
-#endif
diff --git a/src/main.cpp b/src/main.cpp
index b27152e5..3531356f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -23,12 +23,13 @@
#include "client.h"
+#include "utils/filesystem.h"
#include "utils/gettext.h"
+#include "utils/stringutils.h"
#include "utils/xml.h"
#include <getopt.h>
#include <iostream>
-#include <physfs.h>
#ifdef __MINGW32__
#include <windows.h>
@@ -168,7 +169,7 @@ static void parseOptions(int argc, char *argv[], Client::Options &options)
break;
case 'y':
options.serverType = ServerInfo::parseType(optarg);
- if (options.serverType == ServerType::UNKNOWN)
+ if (options.serverType == ServerType::Unknown)
{
std::cerr << _("Invalid server type, expected one of: tmwathena, manaserv") << std::endl;
options.exitWithError = true;
@@ -257,12 +258,12 @@ int main(int argc, char *argv[])
initInternationalization();
// Initialize PhysicsFS
- if (!PHYSFS_init(argv[0])) {
+ if (!FS::init(argv[0])) {
std::cout << "Error while initializing PhysFS: "
- << PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()) << std::endl;
+ << FS::getLastError() << std::endl;
return 1;
}
- atexit((void(*)()) PHYSFS_deinit);
+ atexit((void(*)()) FS::deinit);
XML::init();
diff --git a/src/main.h b/src/main.h
index c1cf3e3f..fdb44d6a 100644
--- a/src/main.h
+++ b/src/main.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef MAIN_H
-#define MAIN_H
+#pragma once
/**
* \mainpage
@@ -88,5 +87,3 @@
#ifndef PKG_DATADIR
#define PKG_DATADIR ""
#endif
-
-#endif
diff --git a/src/map.cpp b/src/map.cpp
index 929d66f8..908d6178 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -245,7 +245,7 @@ void Map::initializeAmbientLayers()
auto addAmbientLayer = [=](const std::string &name, std::vector<AmbientLayer> &list)
{
- if (auto img = resman->getImageRef(getProperty(name + "image")))
+ if (auto img = resman->getImage(getProperty(name + "image")))
{
auto &ambientLayer = list.emplace_back(img);
ambientLayer.mParallax = getFloatProperty(name + "parallax");
@@ -843,7 +843,7 @@ Path Map::findPath(int startX, int startY, int destX, int destY,
// It costs extra to walk through a being (needs to be enough
// to make it more attractive to walk around).
// N.B.: Specific to TmwAthena for now.
- if (Net::getNetworkType() == ServerType::TMWATHENA &&
+ if (Net::getNetworkType() == ServerType::TmwAthena &&
occupied(x, y))
{
Gcost += 3 * basicCost;
diff --git a/src/map.h b/src/map.h
index 3d2ce190..f3850722 100644
--- a/src/map.h
+++ b/src/map.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef MAP_H
-#define MAP_H
+#pragma once
#include "actor.h"
#include "position.h"
@@ -421,5 +420,3 @@ class Map : public Properties
int mMask = 1;
};
-
-#endif
diff --git a/src/net/specialhandler.h b/src/net/abilityhandler.h
index fb8ffdfd..8e61d0c5 100644
--- a/src/net/specialhandler.h
+++ b/src/net/abilityhandler.h
@@ -19,25 +19,22 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SPECIALHANDLER_H
-#define SPECIALHANDLER_H
-
-#include <iosfwd>
+#pragma once
namespace Net {
-class SpecialHandler
+
+class AbilityHandler
{
public:
- virtual ~SpecialHandler () {}
+ virtual ~AbilityHandler () {}
virtual void use(int id) = 0;
- virtual void use(int id, int level, int beingId) = 0;
+ virtual void useOn(int id, int beingId) = 0;
- virtual void use(int id, int level, int x, int y) = 0;
+ virtual void useAt(int id, int x, int y) = 0;
- virtual void use(int id, const std::string &map) = 0;
+ virtual void useInDirection(int id, int direction) = 0;
};
-}
-#endif // SPECIALHANDLER_H
+} // namespace Net
diff --git a/src/net/adminhandler.h b/src/net/adminhandler.h
index 492918a7..87853317 100644
--- a/src/net/adminhandler.h
+++ b/src/net/adminhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef ADMINHANDLER_H
-#define ADMINHANDLER_H
+#pragma once
#include <string>
@@ -39,5 +38,3 @@ class AdminHandler
};
} // namespace Net
-
-#endif // ADMINHANDLER_H
diff --git a/src/net/charhandler.h b/src/net/charhandler.h
index 72e67739..3f0c096f 100644
--- a/src/net/charhandler.h
+++ b/src/net/charhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef CHARHANDLER_H
-#define CHARHANDLER_H
+#pragma once
#include "localplayer.h"
#include "playerinfo.h"
@@ -111,5 +110,3 @@ class CharHandler
};
} // namespace Net
-
-#endif // CHARHANDLER_H
diff --git a/src/net/chathandler.h b/src/net/chathandler.h
index 382ac39a..5524f458 100644
--- a/src/net/chathandler.h
+++ b/src/net/chathandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef CHATHANDLER_H
-#define CHATHANDLER_H
+#pragma once
#include <string>
@@ -62,5 +61,3 @@ class ChatHandler
virtual void requestOnlineList() = 0;
};
}
-
-#endif // CHATHANDLER_H
diff --git a/src/net/download.cpp b/src/net/download.cpp
index 8a41ebfa..7aab3b2f 100644
--- a/src/net/download.cpp
+++ b/src/net/download.cpp
@@ -31,7 +31,7 @@
#include <zlib.h>
-const char *DOWNLOAD_ERROR_MESSAGE_THREAD = "Could not create download thread!";
+constexpr char DOWNLOAD_ERROR_MESSAGE_THREAD[] = "Could not create download thread!";
namespace Net {
@@ -59,32 +59,26 @@ unsigned long Download::fadler32(FILE *file)
return adler;
}
-
-Download::Download(void *ptr, const std::string &url,
- DownloadUpdate updateFunction):
- mPtr(ptr),
- mUrl(url),
- mUpdateFunction(updateFunction)
+Download::Download(const std::string &url)
+ : mUrl(url)
{
- mError = (char*) malloc(CURL_ERROR_SIZE);
mError[0] = 0;
-
- mOptions.cancel = false;
}
Download::~Download()
{
- if (mHeaders)
- curl_slist_free_all(mHeaders);
+ mCancel = true;
+ SDL_WaitThread(mThread, nullptr);
- int status;
- SDL_WaitThread(mThread, &status);
- free(mError);
+ curl_slist_free_all(mHeaders);
+ free(mBuffer);
}
-void Download::addHeader(const std::string &header)
+void Download::addHeader(const char *header)
{
- mHeaders = curl_slist_append(mHeaders, header.c_str());
+ assert(!mThread); // Cannot add headers after starting download
+
+ mHeaders = curl_slist_append(mHeaders, header);
}
void Download::noCache()
@@ -96,19 +90,24 @@ void Download::noCache()
void Download::setFile(const std::string &filename,
std::optional<unsigned long> adler32)
{
- mOptions.memoryWrite = false;
+ assert(!mThread); // Cannot set file after starting download
+
+ mMemoryWrite = false;
mFileName = filename;
mAdler = adler32;
}
-void Download::setWriteFunction(WriteFunction write)
+void Download::setUseBuffer()
{
- mOptions.memoryWrite = true;
- mWriteFunction = write;
+ assert(!mThread); // Cannot set write function after starting download
+
+ mMemoryWrite = true;
}
bool Download::start()
{
+ assert(!mThread); // Download already started
+
logger->log("Starting download: %s", mUrl.c_str());
mThread = SDL_CreateThread(downloadThread, "Download", this);
@@ -116,9 +115,8 @@ bool Download::start()
if (!mThread)
{
logger->log("%s", DOWNLOAD_ERROR_MESSAGE_THREAD);
- strcpy(mError, DOWNLOAD_ERROR_MESSAGE_THREAD);
- mUpdateFunction(mPtr, DOWNLOAD_STATUS_THREAD_ERROR, 0, 0);
-
+ strncpy(mError, DOWNLOAD_ERROR_MESSAGE_THREAD, CURL_ERROR_SIZE - 1);
+ mState.lock()->status = DownloadStatus::Error;
return false;
}
@@ -128,196 +126,186 @@ bool Download::start()
void Download::cancel()
{
logger->log("Canceling download: %s", mUrl.c_str());
-
- mOptions.cancel = true;
- if (mThread && SDL_GetThreadID(mThread) != 0)
- {
- SDL_WaitThread(mThread, nullptr);
- mThread = nullptr;
- }
+ mCancel = true;
}
-const char *Download::getError() const
+std::string_view Download::getBuffer() const
{
- return mError;
+ assert(mMemoryWrite); // Buffer not used
+ return std::string_view(mBuffer, mDownloadedBytes);
}
+/**
+ * A libcurl callback for reporting progress.
+ */
int Download::downloadProgress(void *clientp,
curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow)
{
auto *d = reinterpret_cast<Download*>(clientp);
- if (d->mOptions.cancel)
+ auto state = d->mState.lock();
+ state->status = DownloadStatus::InProgress;
+ state->progress = 0.0f;
+ if (dltotal > 0)
+ state->progress = static_cast<float>(dlnow) / dltotal;
+
+ return d->mCancel;
+}
+
+/**
+ * A libcurl callback for writing to memory.
+ */
+size_t Download::writeBuffer(char *ptr, size_t size, size_t nmemb, void *stream)
+{
+ auto *d = reinterpret_cast<Download *>(stream);
+
+ const size_t totalMem = size * nmemb;
+ d->mBuffer = (char *) realloc(d->mBuffer, d->mDownloadedBytes + totalMem);
+ if (d->mBuffer)
{
- return d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_CANCELLED,
- (size_t) dltotal, (size_t) dlnow);
- return -5;
+ memcpy(d->mBuffer + d->mDownloadedBytes, ptr, totalMem);
+ d->mDownloadedBytes += totalMem;
}
- return d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_IDLE,
- (size_t) dltotal, (size_t) dlnow);
+ return totalMem;
}
int Download::downloadThread(void *ptr)
{
- int attempts = 0;
- bool complete = false;
auto *d = reinterpret_cast<Download*>(ptr);
- CURLcode res;
+ bool complete = false;
std::string outFilename;
- if (!d)
- {
- return 0;
- }
- if (!d->mOptions.memoryWrite)
- {
+ if (!d->mMemoryWrite)
outFilename = d->mFileName + ".part";
- }
- while (attempts < 3 && !complete && !d->mOptions.cancel)
+ for (int attempts = 0; attempts < 3 && !complete && !d->mCancel; ++attempts)
{
- FILE *file = nullptr;
+ CURL *curl = curl_easy_init();
+ if (!curl)
+ break;
- d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_STARTING, 0, 0);
+ logger->log("Downloading: %s", d->mUrl.c_str());
- if (d->mOptions.cancel)
- {
- d->mThread = nullptr;
- return 0;
- }
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, d->mHeaders);
- d->mCurl = curl_easy_init();
+ FILE *file = nullptr;
- if (d->mCurl && !d->mOptions.cancel)
+ if (d->mMemoryWrite)
+ {
+ curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &Download::writeBuffer);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, ptr);
+ }
+ else
{
- logger->log("Downloading: %s", d->mUrl.c_str());
+ file = fopen(outFilename.c_str(), "w+b");
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
+ }
- curl_easy_setopt(d->mCurl, CURLOPT_FOLLOWLOCATION, 1);
- curl_easy_setopt(d->mCurl, CURLOPT_HTTPHEADER, d->mHeaders);
+ const std::string appShort = branding.getStringValue("appShort");
+ const std::string userAgent =
+ strprintf(PACKAGE_EXTENDED_VERSION, appShort.c_str());
- if (d->mOptions.memoryWrite)
- {
- curl_easy_setopt(d->mCurl, CURLOPT_FAILONERROR, 1);
- curl_easy_setopt(d->mCurl, CURLOPT_WRITEFUNCTION, d->mWriteFunction);
- curl_easy_setopt(d->mCurl, CURLOPT_WRITEDATA, d->mPtr);
- }
- else
- {
- file = fopen(outFilename.c_str(), "w+b");
- curl_easy_setopt(d->mCurl, CURLOPT_WRITEDATA, file);
- }
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent.c_str());
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, d->mError);
+ curl_easy_setopt(curl, CURLOPT_URL, d->mUrl.c_str());
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
+ curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, &Download::downloadProgress);
+ curl_easy_setopt(curl, CURLOPT_XFERINFODATA, ptr);
+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15);
- const std::string appShort = branding.getStringValue("appShort");
- const std::string userAgent =
- strprintf(PACKAGE_EXTENDED_VERSION, appShort.c_str());
+ const CURLcode res = curl_easy_perform(curl);
+ curl_easy_cleanup(curl);
- curl_easy_setopt(d->mCurl, CURLOPT_USERAGENT, userAgent.c_str());
- curl_easy_setopt(d->mCurl, CURLOPT_ERRORBUFFER, d->mError);
- curl_easy_setopt(d->mCurl, CURLOPT_URL, d->mUrl.c_str());
- curl_easy_setopt(d->mCurl, CURLOPT_NOPROGRESS, 0);
- curl_easy_setopt(d->mCurl, CURLOPT_XFERINFOFUNCTION, downloadProgress);
- curl_easy_setopt(d->mCurl, CURLOPT_XFERINFODATA, ptr);
- curl_easy_setopt(d->mCurl, CURLOPT_NOSIGNAL, 1);
- curl_easy_setopt(d->mCurl, CURLOPT_CONNECTTIMEOUT, 15);
+ if (res == CURLE_ABORTED_BY_CALLBACK)
+ {
+ d->mCancel = true;
- if ((res = curl_easy_perform(d->mCurl)) != 0 && !d->mOptions.cancel)
+ if (file)
{
- switch (res)
- {
- case CURLE_ABORTED_BY_CALLBACK:
- d->mOptions.cancel = true;
- break;
- case CURLE_COULDNT_CONNECT:
- default:
- logger->log("curl error %d: %s host: %s",
- res, d->mError, d->mUrl.c_str());
- break;
- }
+ fclose(file);
+ ::remove(outFilename.c_str());
+ }
- if (d->mOptions.cancel)
- {
- break;
- }
+ break;
+ }
- d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_ERROR, 0, 0);
+ if (res != CURLE_OK)
+ {
+ logger->log("curl error %d: %s host: %s",
+ res, d->mError, d->mUrl.c_str());
- if (!d->mOptions.memoryWrite)
- {
- fclose(file);
- ::remove(outFilename.c_str());
- }
- attempts++;
- continue;
+ if (file)
+ {
+ fclose(file);
+ ::remove(outFilename.c_str());
}
- curl_easy_cleanup(d->mCurl);
+ break;
+ }
- if (!d->mOptions.memoryWrite)
+ if (!d->mMemoryWrite)
+ {
+ // Check the checksum if available
+ if (d->mAdler)
{
- // Don't check resources.xml checksum
- if (d->mAdler)
- {
- unsigned long adler = fadler32(file);
+ unsigned long adler = fadler32(file);
- if (d->mAdler != adler)
- {
+ if (d->mAdler != adler)
+ {
+ if (file)
fclose(file);
- // Remove the corrupted file
- ::remove(d->mFileName.c_str());
- logger->log("Checksum for file %s failed: (%lx/%lx)",
- d->mFileName.c_str(),
- adler, *d->mAdler);
- attempts++;
- continue; // Bail out here to avoid the renaming
- }
+ // Remove the corrupted file
+ ::remove(outFilename.c_str());
+ logger->log("Checksum for file %s failed: (%lx/%lx)",
+ d->mFileName.c_str(),
+ adler, *d->mAdler);
+
+ continue; // Bail out here to avoid the renaming
}
+ }
+
+ if (file)
fclose(file);
- // Any existing file with this name is deleted first, otherwise
- // the rename will fail on Windows.
- ::remove(d->mFileName.c_str());
- ::rename(outFilename.c_str(), d->mFileName.c_str());
+ // Any existing file with this name is deleted first, otherwise
+ // the rename will fail on Windows.
+ ::remove(d->mFileName.c_str());
+ ::rename(outFilename.c_str(), d->mFileName.c_str());
- // Check if we can open it and no errors were encountered
- // during renaming
- file = fopen(d->mFileName.c_str(), "rb");
- if (file)
- {
- fclose(file);
- complete = true;
- }
- }
- else
+ // Check if we can open it and no errors were encountered
+ // during renaming
+ file = fopen(d->mFileName.c_str(), "rb");
+ if (file)
{
- // It's stored in memory, we're done
+ fclose(file);
+ file = nullptr;
complete = true;
}
}
- if (d->mOptions.cancel)
+ else
{
- d->mThread = nullptr;
- return 0;
+ // It's stored in memory, we're done
+ complete = true;
}
- attempts++;
- }
- if (d->mOptions.cancel)
- {
- // Nothing to do...
- }
- else if (!complete || attempts >= 3)
- {
- d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_ERROR, 0, 0);
+ if (file)
+ fclose(file);
}
+
+ auto state = d->mState.lock();
+ if (d->mCancel)
+ state->status = DownloadStatus::Canceled;
+ else if (complete)
+ state->status = DownloadStatus::Complete;
else
- {
- d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_COMPLETE, 0, 0);
- }
+ state->status = DownloadStatus::Error;
- d->mThread = nullptr;
return 0;
}
diff --git a/src/net/download.h b/src/net/download.h
index 0ce8cc8a..e9483fa5 100644
--- a/src/net/download.h
+++ b/src/net/download.h
@@ -18,44 +18,41 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <cstdlib> // pulls in int64_t
+#include "utils/mutex.h"
+
#include <cstdio>
-#include <string>
#include <optional>
+#include <string>
#include <curl/curl.h>
-#ifndef NET_DOWNLOAD_H
-#define NET_DOWNLOAD_H
+#pragma once
-enum DownloadStatus
+enum class DownloadStatus
{
- DOWNLOAD_STATUS_CANCELLED = -3,
- DOWNLOAD_STATUS_THREAD_ERROR = -2,
- DOWNLOAD_STATUS_ERROR = -1,
- DOWNLOAD_STATUS_STARTING = 0,
- DOWNLOAD_STATUS_IDLE,
- DOWNLOAD_STATUS_COMPLETE
+ InProgress,
+ Canceled,
+ Error,
+ Complete
};
-using DownloadUpdate = int (*)(void *, DownloadStatus, size_t, size_t);
-
-// Matches what CURL expects
-using WriteFunction = size_t (*)(void *, size_t, size_t, void *);
-
struct SDL_Thread;
-using CURL = void;
-struct curl_slist;
namespace Net {
+
class Download
{
public:
- Download(void *ptr, const std::string &url, DownloadUpdate updateFunction);
+ struct State
+ {
+ DownloadStatus status = DownloadStatus::InProgress;
+ float progress = 0.0f;
+ };
+ Download(const std::string &url);
~Download();
- void addHeader(const std::string &header);
+ void addHeader(const char *header);
/**
* Convience method for adding no-cache headers.
@@ -65,47 +62,66 @@ class Download
void setFile(const std::string &filename,
std::optional<unsigned long> adler32 = {});
- void setWriteFunction(WriteFunction write);
+ void setUseBuffer();
/**
* Starts the download thread.
- * @returns true if thread was created
- * false if the thread could not be made or download wasn't
- * properly setup
+ * @returns whether the thread could be created
*/
bool start();
/**
- * Cancels the download. Returns immediately, the cancelled status will
- * be noted in the next avialable update call.
+ * Cancels the download. Returns immediately, the canceled status will
+ * be noted in the next available update call.
*/
void cancel();
+ /**
+ * Returns a view on the downloaded data.
+ */
+ std::string_view getBuffer() const;
+
+ State getState();
+
const char *getError() const;
static unsigned long fadler32(FILE *file);
private:
- static int downloadThread(void *ptr);
static int downloadProgress(void *clientp,
curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow);
- void *mPtr;
+
+ static size_t writeBuffer(char *ptr, size_t size, size_t nmemb,
+ void *stream);
+
+ static int downloadThread(void *ptr);
+
+ ThreadSafe<State> mState;
std::string mUrl;
- struct {
- unsigned cancel : 1;
- unsigned memoryWrite: 1;
- } mOptions;
+ bool mCancel = false;
+ bool mMemoryWrite = false;
std::string mFileName;
- WriteFunction mWriteFunction = nullptr;
std::optional<unsigned long> mAdler;
- DownloadUpdate mUpdateFunction;
SDL_Thread *mThread = nullptr;
- CURL *mCurl = nullptr;
curl_slist *mHeaders = nullptr;
- char *mError;
+ char mError[CURL_ERROR_SIZE];
+
+ /** Byte count currently downloaded in mMemoryBuffer. */
+ size_t mDownloadedBytes = 0;
+
+ /** Buffer for files downloaded to memory. */
+ char *mBuffer = nullptr;
};
-} // namespace Net
+inline Download::State Download::getState()
+{
+ return *mState.lock();
+}
-#endif // NET_DOWNLOAD_H
+inline const char *Download::getError() const
+{
+ return mError;
+}
+
+} // namespace Net
diff --git a/src/net/gamehandler.h b/src/net/gamehandler.h
index 3f47aba5..08c6c33f 100644
--- a/src/net/gamehandler.h
+++ b/src/net/gamehandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef MAPHANDLER_H
-#define MAPHANDLER_H
+#pragma once
#include <iosfwd>
@@ -58,5 +57,3 @@ class GameHandler
};
} // namespace Net
-
-#endif // MAPHANDLER_H
diff --git a/src/net/generalhandler.h b/src/net/generalhandler.h
index 76cde7ce..93d76421 100644
--- a/src/net/generalhandler.h
+++ b/src/net/generalhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GENERALHANDLER_H
-#define GENERALHANDLER_H
+#pragma once
namespace Net {
@@ -36,10 +35,6 @@ class GeneralHandler
virtual void unload() = 0;
virtual void flushNetwork() = 0;
-
- virtual void clearHandlers() = 0;
};
} // namespace Net
-
-#endif // GENERALHANDLER_H
diff --git a/src/net/guildhandler.h b/src/net/guildhandler.h
index 00eae99c..37127599 100644
--- a/src/net/guildhandler.h
+++ b/src/net/guildhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GUILDHANDLER_H
-#define GUILDHANDLER_H
+#pragma once
#include "guild.h"
@@ -64,5 +63,3 @@ class GuildHandler
};
}
-
-#endif // GUILDHANDLER_H
diff --git a/src/net/inventoryhandler.h b/src/net/inventoryhandler.h
index 8a67a7db..323dec6c 100644
--- a/src/net/inventoryhandler.h
+++ b/src/net/inventoryhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef INVENTORYHANDLER_H
-#define INVENTORYHANDLER_H
+#pragma once
#include "equipment.h"
#include "inventory.h"
@@ -67,8 +66,6 @@ class InventoryHandler
public:
virtual ~InventoryHandler() {}
- virtual bool canSplit(const Item *item) = 0;
-
// TODO: fix/remove me
virtual size_t getSize(int type) const = 0;
@@ -99,5 +96,3 @@ class InventoryHandler
};
} // namespace Net
-
-#endif // INVENTORYHANDLER_H
diff --git a/src/net/logindata.h b/src/net/logindata.h
index 34b259fb..4af402ec 100644
--- a/src/net/logindata.h
+++ b/src/net/logindata.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef LOGINDATA_H
-#define LOGINDATA_H
+#pragma once
#include "being.h"
@@ -40,7 +39,7 @@ public:
std::string email;
std::string captchaResponse;
- Gender gender = Gender::UNSPECIFIED;
+ Gender gender = Gender::Unspecified;
bool remember; /**< Whether to store the username. */
bool registerLogin; /**< Whether an account is being registered. */
@@ -64,9 +63,7 @@ public:
updateHost.clear();
email.clear();
captchaResponse.clear();
- gender = Gender::UNSPECIFIED;
+ gender = Gender::Unspecified;
resetCharacterSlots();
}
};
-
-#endif // LOGINDATA_H
diff --git a/src/net/loginhandler.h b/src/net/loginhandler.h
index 43d15b77..992e0709 100644
--- a/src/net/loginhandler.h
+++ b/src/net/loginhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef LOGINHANDLER_H
-#define LOGINHANDLER_H
+#pragma once
#include "net/logindata.h"
#include "net/serverinfo.h"
@@ -98,5 +97,3 @@ class LoginHandler
};
} // namespace Net
-
-#endif // LOGINHANDLER_H
diff --git a/src/net/manaserv/specialhandler.cpp b/src/net/manaserv/abilityhandler.cpp
index 0a477ff8..a9ce8e37 100644
--- a/src/net/manaserv/specialhandler.cpp
+++ b/src/net/manaserv/abilityhandler.cpp
@@ -19,57 +19,60 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "net/manaserv/specialhandler.h"
+#include "net/manaserv/abilityhandler.h"
#include "net/manaserv/connection.h"
#include "net/manaserv/messagein.h"
#include "net/manaserv/messageout.h"
#include "net/manaserv/manaserv_protocol.h"
-extern Net::SpecialHandler *specialHandler;
+extern Net::AbilityHandler *abilityHandler;
namespace ManaServ {
extern Connection *gameServerConnection;
-SpecialHandler::SpecialHandler()
+AbilityHandler::AbilityHandler()
{
- specialHandler = this;
+ abilityHandler = this;
}
-void SpecialHandler::handleMessage(MessageIn &msg)
+void AbilityHandler::handleMessage(MessageIn &msg)
{
// TODO
}
-void SpecialHandler::use(int id)
+void AbilityHandler::use(int id)
{
- MessageOut msg(PGMSG_USE_SPECIAL_ON_BEING);
+ MessageOut msg(PGMSG_USE_ABILITY_ON_BEING);
msg.writeInt8(id);
msg.writeInt16(0);
gameServerConnection->send(msg);
}
-void SpecialHandler::use(int id, int level, int beingId)
+void AbilityHandler::useOn(int id, int beingId)
{
- MessageOut msg(PGMSG_USE_SPECIAL_ON_BEING);
+ MessageOut msg(PGMSG_USE_ABILITY_ON_BEING);
msg.writeInt8(id);
msg.writeInt16(beingId);
gameServerConnection->send(msg);
}
-void SpecialHandler::use(int id, int level, int x, int y)
+void AbilityHandler::useAt(int id, int x, int y)
{
- MessageOut msg(PGMSG_USE_SPECIAL_ON_POINT);
+ MessageOut msg(PGMSG_USE_ABILITY_ON_POINT);
msg.writeInt8(id);
msg.writeInt16(x);
msg.writeInt16(y);
gameServerConnection->send(msg);
}
-void SpecialHandler::use(int id, const std::string &map)
+void AbilityHandler::useInDirection(int id, int direction)
{
- // TODO
+ MessageOut msg(PGMSG_USE_ABILITY_ON_DIRECTION);
+ msg.writeInt8(id);
+ msg.writeInt8(direction);
+ gameServerConnection->send(msg);
}
} // namespace ManaServ
diff --git a/src/net/manaserv/specialhandler.h b/src/net/manaserv/abilityhandler.h
index dbd203d8..e8263989 100644
--- a/src/net/manaserv/specialhandler.h
+++ b/src/net/manaserv/abilityhandler.h
@@ -19,31 +19,28 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_SKILLHANDLER_H
-#define NET_MANASERV_SKILLHANDLER_H
+#pragma once
-#include "net/specialhandler.h"
+#include "net/abilityhandler.h"
#include "net/manaserv/messagehandler.h"
namespace ManaServ {
-class SpecialHandler final : public MessageHandler, public Net::SpecialHandler
+class AbilityHandler final : public MessageHandler, public Net::AbilityHandler
{
public:
- SpecialHandler();
+ AbilityHandler();
void handleMessage(MessageIn &msg) override;
void use(int id) override;
- void use(int id, int level, int beingId) override;
+ void useOn(int id, int beingId) override;
- void use(int id, int level, int x, int y) override;
+ void useAt(int id, int x, int y) override;
- void use(int id, const std::string &map) override;
+ void useInDirection(int id, int direction) override;
};
} // namespace ManaServ
-
-#endif // NET_MANASERV_SKILLHANDLER_H
diff --git a/src/net/manaserv/adminhandler.cpp b/src/net/manaserv/adminhandler.cpp
index 78e6acb2..ef155a38 100644
--- a/src/net/manaserv/adminhandler.cpp
+++ b/src/net/manaserv/adminhandler.cpp
@@ -31,17 +31,11 @@ extern Connection *chatServerConnection;
AdminHandler::AdminHandler()
{
- static const uint16_t _messages[] =
- {
- 0
- };
- handledMessages = _messages;
adminHandler = this;
}
void AdminHandler::handleMessage(MessageIn &msg)
{
-
}
void AdminHandler::kick(const std::string &name)
diff --git a/src/net/manaserv/adminhandler.h b/src/net/manaserv/adminhandler.h
index fd0c7de0..bc92872b 100644
--- a/src/net/manaserv/adminhandler.h
+++ b/src/net/manaserv/adminhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_ADMINHANDLER_H
-#define NET_MANASERV_ADMINHANDLER_H
+#pragma once
#include "net/adminhandler.h"
@@ -43,5 +42,3 @@ class AdminHandler final : public Net::AdminHandler, public MessageHandler
};
} // namespace ManaServ
-
-#endif
diff --git a/src/net/manaserv/beinghandler.cpp b/src/net/manaserv/beinghandler.cpp
index 186239c1..9bb52ac1 100644
--- a/src/net/manaserv/beinghandler.cpp
+++ b/src/net/manaserv/beinghandler.cpp
@@ -23,6 +23,7 @@
#include "actorspritemanager.h"
#include "being.h"
+#include "effectmanager.h"
#include "localplayer.h"
#include "gui/okdialog.h"
@@ -34,6 +35,9 @@
#include "net/manaserv/playerhandler.h"
#include "net/manaserv/manaserv_protocol.h"
+#include "playerrelations.h"
+#include "resources/abilitydb.h"
+#include "resources/emotedb.h"
#include "resources/hairdb.h"
#include "utils/gettext.h"
@@ -45,10 +49,13 @@ namespace ManaServ {
BeingHandler::BeingHandler()
{
static const Uint16 _messages[] = {
- GPMSG_BEING_ATTACK,
GPMSG_BEING_ENTER,
GPMSG_BEING_LEAVE,
+ GPMSG_BEING_EMOTE,
GPMSG_BEINGS_MOVE,
+ GPMSG_BEING_ABILITY_POINT,
+ GPMSG_BEING_ABILITY_BEING,
+ GPMSG_BEING_ABILITY_DIRECTION,
GPMSG_BEINGS_DAMAGE,
GPMSG_BEING_ACTION_CHANGE,
GPMSG_BEING_LOOKS_CHANGE,
@@ -68,11 +75,20 @@ void BeingHandler::handleMessage(MessageIn &msg)
case GPMSG_BEING_LEAVE:
handleBeingLeaveMessage(msg);
break;
+ case GPMSG_BEING_EMOTE:
+ handleBeingEmoteMessage(msg);
+ break;
case GPMSG_BEINGS_MOVE:
handleBeingsMoveMessage(msg);
break;
- case GPMSG_BEING_ATTACK:
- handleBeingAttackMessage(msg);
+ case GPMSG_BEING_ABILITY_POINT:
+ handleBeingAbilityPointMessage(msg);
+ break;
+ case GPMSG_BEING_ABILITY_BEING:
+ handleBeingAbilityBeingMessage(msg);
+ break;
+ case GPMSG_BEING_ABILITY_DIRECTION:
+ handleBeingAbilityDirectionMessage(msg);
break;
case GPMSG_BEINGS_DAMAGE:
handleBeingsDamageMessage(msg);
@@ -91,17 +107,40 @@ void BeingHandler::handleMessage(MessageIn &msg)
static void handleLooks(Being *being, MessageIn &msg)
{
- int lookChanges = msg.readInt8();
+ const int hairStyle = msg.readInt8();
+ const int hairColor = msg.readInt8();
+ being->setSprite(SPRITE_LAYER_HAIR, hairStyle * -1,
+ hairDB.getHairColor(hairColor));
+
+ std::map<unsigned, int> equippedSlots;
+
+ if (msg.getUnreadLength() > 1) {
+ int equippedSlotCount = msg.readInt8();
+ while (equippedSlotCount-- > 0) {
+ unsigned slot = msg.readInt8();
+ int itemId = msg.readInt16();
+ equippedSlots[slot] = itemId;
+ }
+ }
- if (lookChanges <= 0)
- return;
+ unsigned endSlot = equippedSlots.empty() ? 0 : equippedSlots.rbegin()->first + 1;
+ if (being->getSpriteCount() > endSlot + FIXED_SPRITE_LAYER_SIZE)
+ endSlot = being->getSpriteCount() - FIXED_SPRITE_LAYER_SIZE;
- while (lookChanges-- > 0)
+ for (unsigned slot = 0; slot < endSlot; ++slot)
{
- unsigned int slotTypeId = msg.readInt8();
- being->setSprite(slotTypeId + FIXED_SPRITE_LAYER_SIZE,
- msg.readInt16(), "",
- Net::getInventoryHandler()->isWeaponSlot(slotTypeId));
+ auto it = equippedSlots.find(slot);
+ if (it == equippedSlots.end())
+ {
+ being->setSprite(slot + FIXED_SPRITE_LAYER_SIZE, 0);
+ }
+ else
+ {
+ being->setSprite(slot + FIXED_SPRITE_LAYER_SIZE,
+ it->second,
+ std::string(),
+ Net::getInventoryHandler()->isWeaponSlot(slot));
+ }
}
}
@@ -113,14 +152,19 @@ void BeingHandler::handleBeingEnterMessage(MessageIn &msg)
int px = msg.readInt16();
int py = msg.readInt16();
auto direction = (BeingDirection)msg.readInt8();
- Gender gender;
- int genderAsInt = msg.readInt8();
- if (genderAsInt == GENDER_FEMALE)
- gender = Gender::FEMALE;
- else if (genderAsInt == GENDER_MALE)
- gender = Gender::MALE;
- else
- gender = Gender::UNSPECIFIED;
+
+ Gender gender = Gender::Unspecified;
+ switch (getGender(msg.readInt8())) {
+ case GENDER_MALE:
+ gender = Gender::Male;
+ break;
+ case GENDER_FEMALE:
+ gender = Gender::Female;
+ break;
+ case GENDER_UNSPECIFIED:
+ break;
+ }
+
Being *being;
switch (type)
@@ -139,9 +183,7 @@ void BeingHandler::handleBeingEnterMessage(MessageIn &msg)
ActorSprite::PLAYER, 0);
being->setName(name);
}
- int hs = msg.readInt8(), hc = msg.readInt8();
- being->setSprite(SPRITE_LAYER_HAIR, hs * -1,
- hairDB.getHairColor(hc));
+
handleLooks(being, msg);
} break;
@@ -152,7 +194,8 @@ void BeingHandler::handleBeingEnterMessage(MessageIn &msg)
being = actorSpriteManager->createBeing(id, type == OBJECT_MONSTER
? ActorSprite::MONSTER : ActorSprite::NPC, subtype);
std::string name = msg.readString();
- if (name.length() > 0) being->setName(name);
+ if (!name.empty())
+ being->setName(name);
} break;
default:
@@ -175,6 +218,19 @@ void BeingHandler::handleBeingLeaveMessage(MessageIn &msg)
actorSpriteManager->destroyActor(being);
}
+void BeingHandler::handleBeingEmoteMessage(MessageIn &msg)
+{
+ Being *being = actorSpriteManager->findBeing(msg.readInt16());
+ if (!being)
+ return;
+
+ if (player_relations.hasPermission(being, PlayerPermissions::EMOTE))
+ {
+ const int fx = EmoteDB::get(msg.readInt8() - 1).effectId;
+ effectManager->trigger(fx, being);
+ }
+}
+
void BeingHandler::handleBeingsMoveMessage(MessageIn &msg)
{
while (msg.getUnreadLength())
@@ -235,18 +291,51 @@ void BeingHandler::handleBeingsMoveMessage(MessageIn &msg)
}
}
-void BeingHandler::handleBeingAttackMessage(MessageIn &msg)
+void BeingHandler::handleBeingAbilityPointMessage(MessageIn &msg)
{
Being *being = actorSpriteManager->findBeing(msg.readInt16());
- const auto direction = (BeingDirection) msg.readInt8();
- const int attackId = msg.readInt8();
+ if (!being)
+ return;
+
+ const int abilityId = msg.readInt8();
+ const int x = msg.readInt16();
+ const int y = msg.readInt16();
+
+ being->lookAt(Vector(x, y));
+
+ if (auto ability = AbilityDB::get(abilityId))
+ being->setAction(ability->useAction);
+}
+void BeingHandler::handleBeingAbilityBeingMessage(MessageIn &msg)
+{
+ Being *being = actorSpriteManager->findBeing(msg.readInt16());
if (!being)
return;
+ const int abilityId = msg.readInt8();
+ const int targetId = msg.readInt16();
+
+ if (Being *target = actorSpriteManager->findBeing(targetId))
+ being->lookAt(target->getPosition());
+
+ if (auto ability = AbilityDB::get(abilityId))
+ being->setAction(ability->useAction);
+}
+
+void BeingHandler::handleBeingAbilityDirectionMessage(MessageIn &msg)
+{
+ Being *being = actorSpriteManager->findBeing(msg.readInt16());
+ if (!being)
+ return;
+
+ const int abilityId = msg.readInt8();
+ const int direction = msg.readInt8();
+
being->setDirection(direction);
- being->setAction(Being::ATTACK, attackId);
+ if (auto ability = AbilityDB::get(abilityId))
+ being->setAction(ability->useAction);
}
void BeingHandler::handleBeingsDamageMessage(MessageIn &msg)
@@ -256,9 +345,7 @@ void BeingHandler::handleBeingsDamageMessage(MessageIn &msg)
Being *being = actorSpriteManager->findBeing(msg.readInt16());
int damage = msg.readInt16();
if (being)
- {
being->takeDamage(nullptr, damage, Being::HIT);
- }
}
}
@@ -306,14 +393,8 @@ void BeingHandler::handleBeingLooksChangeMessage(MessageIn &msg)
Being *being = actorSpriteManager->findBeing(msg.readInt16());
if (!being || being->getType() != ActorSprite::PLAYER)
return;
+
handleLooks(being, msg);
- if (msg.getUnreadLength())
- {
- int style = msg.readInt16();
- int color = msg.readInt16();
- being->setSprite(SPRITE_LAYER_HAIR, style * -1,
- hairDB.getHairColor(color));
- }
}
void BeingHandler::handleBeingDirChangeMessage(MessageIn &msg)
diff --git a/src/net/manaserv/beinghandler.h b/src/net/manaserv/beinghandler.h
index 63424de9..9580c284 100644
--- a/src/net/manaserv/beinghandler.h
+++ b/src/net/manaserv/beinghandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_BEINGHANDLER_H
-#define NET_MANASERV_BEINGHANDLER_H
+#pragma once
#include "net/manaserv/messagehandler.h"
@@ -44,10 +43,13 @@ class BeingHandler final : public MessageHandler
void handleMessage(MessageIn &msg) override;
private:
- void handleBeingAttackMessage(MessageIn &msg);
void handleBeingEnterMessage(MessageIn &msg);
void handleBeingLeaveMessage(MessageIn &msg);
+ void handleBeingEmoteMessage(MessageIn &msg);
void handleBeingsMoveMessage(MessageIn &msg);
+ void handleBeingAbilityPointMessage(MessageIn &msg);
+ void handleBeingAbilityBeingMessage(MessageIn &msg);
+ void handleBeingAbilityDirectionMessage(MessageIn &msg);
void handleBeingsDamageMessage(MessageIn &msg);
void handleBeingActionChangeMessage(MessageIn &msg);
void handleBeingLooksChangeMessage(MessageIn &msg);
@@ -55,5 +57,3 @@ class BeingHandler final : public MessageHandler
};
} // namespace ManaServ
-
-#endif
diff --git a/src/net/manaserv/buysellhandler.h b/src/net/manaserv/buysellhandler.h
index 0629bd7b..c19090e1 100644
--- a/src/net/manaserv/buysellhandler.h
+++ b/src/net/manaserv/buysellhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_BUYSELLHANDLER_H
-#define NET_MANASERV_BUYSELLHANDLER_H
+#pragma once
#include "net/manaserv/messagehandler.h"
@@ -35,5 +34,3 @@ class BuySellHandler final : public MessageHandler
};
} // namespace ManaServ
-
-#endif
diff --git a/src/net/manaserv/charhandler.cpp b/src/net/manaserv/charhandler.cpp
index 8a922e70..98591669 100644
--- a/src/net/manaserv/charhandler.cpp
+++ b/src/net/manaserv/charhandler.cpp
@@ -44,6 +44,7 @@
#include "utils/dtor.h"
#include "utils/gettext.h"
+#include "utils/stringutils.h"
extern Net::CharHandler *charHandler;
extern ManaServ::GameHandler *gameHandler;
@@ -99,28 +100,44 @@ void CharHandler::handleMessage(MessageIn &msg)
void CharHandler::handleCharacterInfo(MessageIn &msg)
{
- CachedCharacterInfo info;
- info.slot = msg.readInt8();
- info.name = msg.readString();
- info.gender = msg.readInt8() == ManaServ::GENDER_MALE ? Gender::MALE
- : Gender::FEMALE;
- info.hairStyle = msg.readInt8();
- info.hairColor = msg.readInt8();
- info.level = msg.readInt16();
- info.characterPoints = msg.readInt16();
- info.correctionPoints = msg.readInt16();
-
while (msg.getUnreadLength() > 0)
{
- int id = msg.readInt32();
- CachedAttrbiute attr;
- attr.base = msg.readInt32() / 256.0;
- attr.mod = msg.readInt32() / 256.0;
+ CachedCharacterInfo &info = mCachedCharacterInfos.emplace_back();
- info.attribute[id] = attr;
- }
+ info.slot = msg.readInt8();
+ info.name = msg.readString();
+ switch (getGender(msg.readInt8())) {
+ case GENDER_MALE:
+ info.gender = Gender::Male;
+ break;
+ case GENDER_FEMALE:
+ info.gender = Gender::Female;
+ break;
+ case GENDER_UNSPECIFIED:
+ info.gender = Gender::Unspecified;
+ break;
+ }
+ info.hairStyle = msg.readInt8();
+ info.hairColor = msg.readInt8();
+ info.characterPoints = msg.readInt16();
+ info.correctionPoints = msg.readInt16();
- mCachedCharacterInfos.push_back(info);
+ int equipmentCount = msg.readInt8();
+ while (equipmentCount--)
+ {
+ auto &slot = info.equipment.emplace_back();
+ slot.id = msg.readInt16();
+ slot.itemId = msg.readInt16();
+ }
+
+ int attributeCount = msg.readInt8();
+ while (attributeCount--)
+ {
+ CachedAttribute &attr = info.attributes[msg.readInt32()];
+ attr.base = msg.readInt32() / 256.0;
+ attr.mod = msg.readInt32() / 256.0;
+ }
+ }
updateCharacters();
}
@@ -182,6 +199,8 @@ void CharHandler::handleCharacterCreateResponse(MessageIn &msg)
}
else
{
+ handleCharacterInfo(msg);
+
// Close the character create dialog
if (mCharCreateDialog)
{
@@ -380,10 +399,8 @@ void CharHandler::updateCharacters()
return;
// Create new characters and initialize them from the cached infos
- for (unsigned i = 0; i < mCachedCharacterInfos.size(); ++i)
+ for (const auto &info : mCachedCharacterInfos)
{
- const CachedCharacterInfo &info = mCachedCharacterInfos.at(i);
-
auto *character = new Net::Character;
character->slot = info.slot;
LocalPlayer *player = character->dummy = new LocalPlayer;
@@ -391,14 +408,30 @@ void CharHandler::updateCharacters()
player->setGender(info.gender);
player->setSprite(SPRITE_LAYER_HAIR, info.hairStyle * -1,
hairDB.getHairColor(info.hairColor));
- character->data.mAttributes[LEVEL] = info.level;
+
+ for (auto &slot : info.equipment)
+ {
+ player->setSprite(slot.id + FIXED_SPRITE_LAYER_SIZE,
+ slot.itemId,
+ std::string(),
+ Net::getInventoryHandler()->isWeaponSlot(slot.id));
+ }
+
character->data.mAttributes[CHAR_POINTS] = info.characterPoints;
character->data.mAttributes[CORR_POINTS] = info.correctionPoints;
- for (const auto &it : info.attribute)
+ for (const auto &[id, attr] : info.attributes)
{
- character->data.mStats[i].base = it.second.base;
- character->data.mStats[i].mod = it.second.mod;
+ int playerInfoId = Attributes::getPlayerInfoIdFromAttrId(id);
+ if (playerInfoId > -1)
+ {
+ character->data.mAttributes[playerInfoId] = attr.mod;
+ }
+ else
+ {
+ character->data.mStats[id].base = attr.base;
+ character->data.mStats[id].mod = attr.mod;
+ }
}
mCharacters.push_back(character);
diff --git a/src/net/manaserv/charhandler.h b/src/net/manaserv/charhandler.h
index 9ec5cdbb..e962bdfa 100644
--- a/src/net/manaserv/charhandler.h
+++ b/src/net/manaserv/charhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_CHARSERVERHANDLER_H
-#define NET_MANASERV_CHARSERVERHANDLER_H
+#pragma once
#include "gui/charselectdialog.h"
@@ -89,12 +88,15 @@ class CharHandler final : public MessageHandler, public Net::CharHandler
* we have loaded the dynamic data, so we can't resolve load any
* sprites yet.
*/
- struct CachedAttrbiute {
+ struct CachedAttribute {
double base;
double mod;
};
- using CachedAttributes = std::map<int, CachedAttrbiute>;
+ struct EquipmentSlot {
+ int id;
+ int itemId;
+ };
struct CachedCharacterInfo {
int slot;
@@ -102,10 +104,10 @@ class CharHandler final : public MessageHandler, public Net::CharHandler
Gender gender;
int hairStyle;
int hairColor;
- int level;
int characterPoints;
int correctionPoints;
- CachedAttributes attribute;
+ std::vector<EquipmentSlot> equipment;
+ std::map<int, CachedAttribute> attributes;
};
void handleCharacterInfo(MessageIn &msg);
@@ -120,5 +122,3 @@ class CharHandler final : public MessageHandler, public Net::CharHandler
};
} // namespace ManaServ
-
-#endif
diff --git a/src/net/manaserv/chathandler.h b/src/net/manaserv/chathandler.h
index f17883ae..6099fe93 100644
--- a/src/net/manaserv/chathandler.h
+++ b/src/net/manaserv/chathandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_CHATHANDLER_H
-#define NET_MANASERV_CHATHANDLER_H
+#pragma once
#include "net/chathandler.h"
@@ -127,5 +126,3 @@ class ChatHandler final : public MessageHandler, public Net::ChatHandler
};
} // namespace ManaServ
-
-#endif
diff --git a/src/net/manaserv/connection.cpp b/src/net/manaserv/connection.cpp
index 896d86ad..1b6f757a 100644
--- a/src/net/manaserv/connection.cpp
+++ b/src/net/manaserv/connection.cpp
@@ -45,6 +45,8 @@ Connection::~Connection()
bool Connection::connect(const std::string &address, enet_uint16 port)
{
logger->log("Net::Connection::connect(%s, %i)", address.c_str(), port);
+ if (mConnection)
+ disconnect();
if (address.empty())
{
@@ -87,8 +89,7 @@ void Connection::disconnect()
bool Connection::isConnected()
{
- return (mConnection) ?
- (mConnection->state == ENET_PEER_STATE_CONNECTED) : false;
+ return mConnection && mConnection->state == ENET_PEER_STATE_CONNECTED;
}
void Connection::send(const ManaServ::MessageOut &msg)
diff --git a/src/net/manaserv/connection.h b/src/net/manaserv/connection.h
index dfd45e31..196d034b 100644
--- a/src/net/manaserv/connection.h
+++ b/src/net/manaserv/connection.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_CONNECTION_H
-#define NET_MANASERV_CONNECTION_H
+#pragma once
#include <enet/enet.h>
#include "net/manaserv/network.h"
@@ -76,5 +75,3 @@ namespace ManaServ
State mState = OK;
};
}
-
-#endif // NET_MANASERV_CONNECTION_H
diff --git a/src/net/manaserv/defines.h b/src/net/manaserv/defines.h
index f1d12339..e224c045 100644
--- a/src/net/manaserv/defines.h
+++ b/src/net/manaserv/defines.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef MANASERV_DEFINES_H
-#define MANASERV_DEFINES_H
+#pragma once
/**
* Attributes used during combat. Available to all the beings.
@@ -72,5 +71,3 @@ enum
NB_CHARACTER_ATTRIBUTES = CHAR_ATTR_END
};
-
-#endif // MANASERV_DEFINES_H
diff --git a/src/net/manaserv/effecthandler.h b/src/net/manaserv/effecthandler.h
index 4ba711d7..9b4e7adf 100644
--- a/src/net/manaserv/effecthandler.h
+++ b/src/net/manaserv/effecthandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_EFFECTSHANDLER_H
-#define NET_MANASERV_EFFECTSHANDLER_H
+#pragma once
#include "net/manaserv/messagehandler.h"
@@ -41,5 +40,3 @@ class EffectHandler final : public MessageHandler
};
} // namespace ManaServ
-
-#endif // NET_MANASERV_EFFECTSHANDLER_H
diff --git a/src/net/manaserv/gamehandler.cpp b/src/net/manaserv/gamehandler.cpp
index 1bf4d69f..3dae5640 100644
--- a/src/net/manaserv/gamehandler.cpp
+++ b/src/net/manaserv/gamehandler.cpp
@@ -44,6 +44,7 @@ extern ServerInfo chatServer;
GameHandler::GameHandler()
{
static const Uint16 _messages[] = {
+ GPMSG_CONNECT_RESPONSE,
GPMSG_DISCONNECT_RESPONSE,
0
};
@@ -55,6 +56,9 @@ void GameHandler::handleMessage(MessageIn &msg)
{
switch (msg.getId())
{
+ case GPMSG_CONNECT_RESPONSE:
+ break;
+
case GPMSG_DISCONNECT_RESPONSE:
{
int errMsg = msg.readInt8();
diff --git a/src/net/manaserv/gamehandler.h b/src/net/manaserv/gamehandler.h
index 019c2dfa..4db8a9ee 100644
--- a/src/net/manaserv/gamehandler.h
+++ b/src/net/manaserv/gamehandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_MAPHANDLER_H
-#define NET_MANASERV_MAPHANDLER_H
+#pragma once
#include "net/gamehandler.h"
@@ -62,5 +61,3 @@ class GameHandler final : public MessageHandler, public Net::GameHandler
};
} // namespace ManaServ
-
-#endif // NET_MANASERV_MAPHANDLER_H
diff --git a/src/net/manaserv/generalhandler.cpp b/src/net/manaserv/generalhandler.cpp
index 0692ee82..32141c29 100644
--- a/src/net/manaserv/generalhandler.cpp
+++ b/src/net/manaserv/generalhandler.cpp
@@ -23,7 +23,6 @@
#include "client.h"
-#include "gui/inventorywindow.h"
#include "gui/skilldialog.h"
#include "net/manaserv/adminhandler.h"
@@ -42,13 +41,11 @@
#include "net/manaserv/npchandler.h"
#include "net/manaserv/partyhandler.h"
#include "net/manaserv/playerhandler.h"
-#include "net/manaserv/specialhandler.h"
+#include "net/manaserv/abilityhandler.h"
#include "net/manaserv/tradehandler.h"
#include "resources/attributes.h"
-extern Net::GeneralHandler *generalHandler;
-
extern ManaServ::LoginHandler *loginHandler;
namespace ManaServ {
@@ -76,7 +73,7 @@ GeneralHandler::GeneralHandler():
mPartyHandler(new PartyHandler),
mPlayerHandler(new PlayerHandler),
mTradeHandler(new TradeHandler),
- mSpecialHandler(new SpecialHandler)
+ mAbilityHandler(new AbilityHandler)
{
initialize();
@@ -84,8 +81,6 @@ GeneralHandler::GeneralHandler():
gameServerConnection = getConnection();
chatServerConnection = getConnection();
- generalHandler = this;
-
listen(Event::ClientChannel);
listen(Event::GameChannel);
}
@@ -129,18 +124,28 @@ void GeneralHandler::reload()
void GeneralHandler::unload()
{
- clearHandlers();
+ clearNetworkHandlers();
if (accountServerConnection)
+ {
accountServerConnection->disconnect();
+ delete accountServerConnection;
+ accountServerConnection = nullptr;
+ }
+
if (gameServerConnection)
+ {
gameServerConnection->disconnect();
+ delete gameServerConnection;
+ gameServerConnection = nullptr;
+ }
+
if (chatServerConnection)
+ {
chatServerConnection->disconnect();
-
- delete accountServerConnection;
- delete gameServerConnection;
- delete chatServerConnection;
+ delete chatServerConnection;
+ chatServerConnection = nullptr;
+ }
finalize();
}
@@ -157,11 +162,6 @@ void GeneralHandler::flushNetwork()
}
}
-void GeneralHandler::clearHandlers()
-{
- clearNetworkHandlers();
-}
-
void GeneralHandler::event(Event::Channel channel,
const Event &event)
{
@@ -182,7 +182,6 @@ void GeneralHandler::event(Event::Channel channel,
{
if (event.getType() == Event::GuiWindowsLoaded)
{
- inventoryWindow->setSplitAllowed(true);
skillDialog->loadSkills();
PlayerInfo::setAttribute(EXP_NEEDED, 100);
diff --git a/src/net/manaserv/generalhandler.h b/src/net/manaserv/generalhandler.h
index 582c1796..de0e3138 100644
--- a/src/net/manaserv/generalhandler.h
+++ b/src/net/manaserv/generalhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_GENERALHANDLER_H
-#define NET_MANASERV_GENERALHANDLER_H
+#pragma once
#include "eventlistener.h"
@@ -44,8 +43,6 @@ class GeneralHandler : public Net::GeneralHandler, public EventListener
void flushNetwork() override;
- void clearHandlers() override;
-
void event(Event::Channel channel, const Event &event) override;
protected:
@@ -64,9 +61,7 @@ class GeneralHandler : public Net::GeneralHandler, public EventListener
MessageHandlerPtr mPartyHandler;
MessageHandlerPtr mPlayerHandler;
MessageHandlerPtr mTradeHandler;
- MessageHandlerPtr mSpecialHandler;
+ MessageHandlerPtr mAbilityHandler;
};
} // namespace ManaServ
-
-#endif // NET_MANASERV_GENERALHANDLER_H
diff --git a/src/net/manaserv/guildhandler.h b/src/net/manaserv/guildhandler.h
index 666ae862..d7eb798c 100644
--- a/src/net/manaserv/guildhandler.h
+++ b/src/net/manaserv/guildhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_GUILDHANDLER_H
-#define NET_MANASERV_GUILDHANDLER_H
+#pragma once
#include "net/guildhandler.h"
@@ -67,5 +66,3 @@ protected:
};
} // namespace ManaServ
-
-#endif
diff --git a/src/net/manaserv/internal.h b/src/net/manaserv/internal.h
index 2961f4bb..701c9ee6 100644
--- a/src/net/manaserv/internal.h
+++ b/src/net/manaserv/internal.h
@@ -19,12 +19,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_INTERNAL_H
-#define NET_MANASERV_INTERNAL_H
+#pragma once
namespace ManaServ
{
extern int connections;
}
-
-#endif
diff --git a/src/net/manaserv/inventoryhandler.cpp b/src/net/manaserv/inventoryhandler.cpp
index 9754beb0..e1dc5dea 100644
--- a/src/net/manaserv/inventoryhandler.cpp
+++ b/src/net/manaserv/inventoryhandler.cpp
@@ -43,16 +43,6 @@ extern Net::InventoryHandler *inventoryHandler;
namespace ManaServ {
-struct EquipItemInfo
-{
-
- EquipItemInfo(int itemId, int slotTypeId, int amountUsed):
- mItemId(itemId), mSlotTypeId(slotTypeId), mAmountUsed(amountUsed)
- {}
-
- int mItemId, mSlotTypeId, mAmountUsed;
-};
-
extern Connection *gameServerConnection;
EquipBackend::EquipBackend()
@@ -61,15 +51,13 @@ EquipBackend::EquipBackend()
mVisibleSlots = 0;
}
-EquipBackend::~EquipBackend()
-{
- clear();
-}
-
Item *EquipBackend::getEquipment(int slotIndex) const
{
auto it = mSlots.find(slotIndex);
- return it == mSlots.end() ? nullptr : it->second.item;
+ if (it == mSlots.end())
+ return nullptr;
+
+ return PlayerInfo::getInventory()->getItem(it->second.inventorySlot);
}
std::string EquipBackend::getSlotName(int slotIndex) const
@@ -80,111 +68,55 @@ std::string EquipBackend::getSlotName(int slotIndex) const
void EquipBackend::triggerUnequip(int slotIndex) const
{
- // First get the itemInstance
- auto it = mSlots.find(slotIndex);
-
- if (it == mSlots.end() || it->second.itemInstance == 0 || !it->second.item)
+ auto item = getEquipment(slotIndex);
+ if (!item)
return;
Event event(Event::DoUnequip);
- event.setItem("item", it->second.item);
- event.setInt("itemInstance", it->second.itemInstance);
+ event.setItem("item", item);
event.trigger(Event::ItemChannel);
}
-
void EquipBackend::clear()
{
- for (auto &slot : mSlots)
- {
- if (slot.second.item)
- {
- delete slot.second.item;
- slot.second.item = nullptr;
- }
- }
- mSlots.clear();
+ for (auto &[_, slot] : mSlots)
+ slot.inventorySlot = -1;
}
-void EquipBackend::equip(int itemId, int slotTypeId, int amountUsed,
- int itemInstance)
+void EquipBackend::equip(int inventorySlot, int equipmentSlot)
{
- if (itemInstance <= 0)
- {
- logger->log("ManaServ::EquipBackend: Equipment slot %i"
- " has an invalid item instance.", slotTypeId);
- return;
- }
-
- auto it = mSlots.begin();
- auto it_end = mSlots.end();
- bool slotTypeFound = false;
- for (; it != it_end; ++it)
- if (it->second.slotTypeId == (unsigned)slotTypeId)
- slotTypeFound = true;
-
- if (!slotTypeFound)
+ auto slotIt = mSlots.find(equipmentSlot);
+ if (slotIt == mSlots.end())
{
logger->log("ManaServ::EquipBackend: Equipment slot %i"
- " is not existing.", slotTypeId);
+ " is not existing.",
+ equipmentSlot);
return;
}
- if (!itemDb->exists(itemId))
- {
- logger->log("ManaServ::EquipBackend: No item with id %d",
- itemId);
- return;
- }
+ slotIt->second.inventorySlot = inventorySlot;
- // Place the item in the slots with corresponding id until
- // the capacity requested has been reached
- for (it = mSlots.begin(); it != it_end && amountUsed > 0; ++it)
- {
- // If we're on the right slot type and that its unit
- // isn't already equipped, we can equip there.
- // The slots are already sorted by id, and subId anyway.
- if (it->second.slotTypeId == (unsigned)slotTypeId
- && (!it->second.itemInstance) && (!it->second.item))
- {
- it->second.itemInstance = itemInstance;
- it->second.item = new Item(itemId, 1, true);
- --amountUsed;
- }
- }
+ if (auto item = PlayerInfo::getInventory()->getItem(inventorySlot))
+ item->setEquipped(true);
}
-void EquipBackend::unequip(int itemInstance)
+void EquipBackend::unequip(int inventorySlot)
{
- auto it = mSlots.begin();
- auto it_end = mSlots.end();
- bool itemInstanceFound = false;
- for (; it != it_end; ++it)
- if (it->second.itemInstance == (unsigned)itemInstance)
- itemInstanceFound = true;
-
- if (!itemInstanceFound)
+ for (auto &[_, slot] : mSlots)
{
- logger->log("ManaServ::EquipBackend: Equipment item instance %i"
- " is not existing. The item couldn't be unequipped!",
- itemInstance);
- return;
- }
+ if (slot.inventorySlot == inventorySlot)
+ {
+ slot.inventorySlot = -1;
- for (it = mSlots.begin(); it != it_end; ++it)
- {
- if (it->second.itemInstance != (unsigned)itemInstance)
- continue;
+ if (auto item = PlayerInfo::getInventory()->getItem(inventorySlot))
+ item->setEquipped(false);
- // We remove the item
- it->second.itemInstance = 0;
- // We also delete the item objects
- if (it->second.item)
- {
- delete it->second.item;
- it->second.item = nullptr;
+ return;
}
}
+
+ logger->log("ManaServ::EquipBackend: No equipped item found at inventory "
+ "slot %i!", inventorySlot);
}
void EquipBackend::event(Event::Channel, const Event &event)
@@ -195,7 +127,7 @@ void EquipBackend::event(Event::Channel, const Event &event)
void EquipBackend::readEquipFile()
{
- clear();
+ mSlots.clear();
XML::Document doc(EQUIP_FILE);
XML::Node rootNode = doc.rootNode();
@@ -245,7 +177,7 @@ void EquipBackend::readEquipFile()
}
slot.subId = i;
- mSlots.insert(std::make_pair(slotIndex, slot));
+ mSlots.insert(std::make_pair(slotIndex, std::move(slot)));
++slotIndex;
}
}
@@ -262,10 +194,10 @@ void EquipBackend::readBoxNode(XML::Node slotNode)
if (boxNode.name() != "box")
continue;
- int x = boxNode.getProperty("x" , 0);
- int y = boxNode.getProperty("y" , 0);
+ const int x = boxNode.getProperty("x" , 0);
+ const int y = boxNode.getProperty("y" , 0);
- mBoxesPositions.push_back(Position(x, y));
+ mBoxesPositions.emplace_back(x, y);
std::string backgroundFile =
boxNode.getProperty("background" , std::string());
@@ -313,6 +245,7 @@ InventoryHandler::InventoryHandler()
GPMSG_INVENTORY_FULL,
GPMSG_INVENTORY,
GPMSG_EQUIP,
+ GPMSG_UNEQUIP,
0
};
handledMessages = _messages;
@@ -331,87 +264,43 @@ void InventoryHandler::handleMessage(MessageIn &msg)
int count = msg.readInt16();
while (count--)
{
- int slot = msg.readInt16();
- int id = msg.readInt16();
- int amount = msg.readInt16();
- PlayerInfo::setInventoryItem(slot, id, amount);
+ const int slot = msg.readInt16();
+ const int itemId = msg.readInt16();
+ const int amount = msg.readInt16();
+ const int equipmentSlot = msg.readInt16();
+ PlayerInfo::setInventoryItem(slot, itemId, amount);
+
+ if (equipmentSlot > 0)
+ mEquipBackend.equip(slot, equipmentSlot);
}
- // A map of { item instance, {slot type id, item id, amount used}}
- std::map<int, EquipItemInfo> equipItemsInfo;
- std::map<int, EquipItemInfo>::iterator it;
- while (msg.getUnreadLength())
- {
- int slotTypeId = msg.readInt16();
- int itemId = msg.readInt16();
- int itemInstance = msg.readInt16();
-
- // Turn the data received into a usable format
- it = equipItemsInfo.find(itemInstance);
- if (it == equipItemsInfo.end())
- {
- // Add a new entry
- equipItemsInfo.insert(std::make_pair(itemInstance,
- EquipItemInfo(itemId, slotTypeId, 1)));
- }
- else
- {
- // Add amount to the existing entry
- it->second.mAmountUsed++;
- }
- }
-
- for (it = equipItemsInfo.begin(); it != equipItemsInfo.end();
- ++it)
- {
- mEquipBackend.equip(it->second.mItemId,
- it->second.mSlotTypeId,
- it->second.mAmountUsed,
- it->first);
- }
+ inventoryWindow->updateButtons();
}
break;
case GPMSG_INVENTORY:
while (msg.getUnreadLength())
{
- unsigned int slot = msg.readInt16();
- int id = msg.readInt16();
- unsigned int amount = id ? msg.readInt16() : 0;
+ const unsigned int slot = msg.readInt16();
+ const int id = msg.readInt16();
+ const unsigned int amount = id ? msg.readInt16() : 0;
PlayerInfo::setInventoryItem(slot, id, amount);
}
break;
case GPMSG_EQUIP:
{
- int itemId = msg.readInt16();
- int equipSlotCount = msg.readInt16();
-
- if (equipSlotCount <= 0)
- break;
-
- // Otherwise equip the item in the given slots
- while (equipSlotCount--)
- {
- unsigned int parameter = msg.readInt16();
- unsigned int amountUsed = msg.readInt16();
-
- if (amountUsed == 0)
- {
- // No amount means to unequip this item
- // Note that in that case, the parameter is
- // in fact the itemInstanceId
- mEquipBackend.unequip(parameter);
- }
- else
- {
- int itemInstance = msg.readInt16();
- // The parameter is in that case the slot type id.
- mEquipBackend.equip(itemId, parameter,
- amountUsed, itemInstance);
- }
- }
+ const int inventorySlot = msg.readInt16();
+ const int equipmentSlot = msg.readInt16();
+ mEquipBackend.equip(inventorySlot, equipmentSlot);
+ inventoryWindow->updateButtons();
+ }
+ case GPMSG_UNEQUIP:
+ {
+ const int inventorySlot = msg.readInt16();
+ mEquipBackend.unequip(inventorySlot);
+ inventoryWindow->updateButtons();
}
break;
}
@@ -423,9 +312,7 @@ void InventoryHandler::event(Event::Channel channel,
if (channel == Event::ItemChannel)
{
Item *item = event.getItem("item");
- int itemInstance = event.getInt("itemInstance", 0);
-
- if (!item && itemInstance == 0)
+ if (!item)
return;
int index = item->getInvIndex();
@@ -439,7 +326,7 @@ void InventoryHandler::event(Event::Channel channel,
else if (event.getType() == Event::DoUnequip)
{
MessageOut msg(PGMSG_UNEQUIP);
- msg.writeInt16(itemInstance);
+ msg.writeInt16(index);
gameServerConnection->send(msg);
}
else if (event.getType() == Event::DoUse)
@@ -457,53 +344,9 @@ void InventoryHandler::event(Event::Channel channel,
msg.writeInt16(amount);
gameServerConnection->send(msg);
}
- else if (event.getType() == Event::DoSplit)
- {
- int amount = event.getInt("amount", 1);
-
- int newIndex = PlayerInfo::getInventory()->getFreeSlot();
- if (newIndex > Inventory::NO_SLOT_INDEX)
- {
- MessageOut msg(PGMSG_MOVE_ITEM);
- msg.writeInt16(index);
- msg.writeInt16(newIndex);
- msg.writeInt16(amount);
- gameServerConnection->send(msg);
- }
- }
- else if (event.getType() == Event::DoMove)
- {
- int newIndex = event.getInt("newIndex", -1);
-
- if (newIndex >= 0)
- {
- if (index == newIndex)
- return;
-
- MessageOut msg(PGMSG_MOVE_ITEM);
- msg.writeInt16(index);
- msg.writeInt16(newIndex);
- msg.writeInt16(item->getQuantity());
- gameServerConnection->send(msg);
- }
- else
- {
- /*int source = event.getInt("source");
- int destination = event.getInt("destination");
- int amount = event.getInt("amount", 1);*/
-
- // TODO Support drag'n'drop to the map ground, or with other
- // windows.
- }
- }
}
}
-bool InventoryHandler::canSplit(const Item *item)
-{
- return item && item->getQuantity() > 1;
-}
-
size_t InventoryHandler::getSize(int type) const
{
switch (type)
diff --git a/src/net/manaserv/inventoryhandler.h b/src/net/manaserv/inventoryhandler.h
index e01bb5d8..02ce90df 100644
--- a/src/net/manaserv/inventoryhandler.h
+++ b/src/net/manaserv/inventoryhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_INVENTORYHANDLER_H
-#define NET_MANASERV_INVENTORYHANDLER_H
+#pragma once
#include "eventlistener.h"
@@ -28,6 +27,7 @@
#include "net/manaserv/messagehandler.h"
+#include <map>
#include <vector>
namespace ManaServ {
@@ -37,15 +37,12 @@ class EquipBackend final : public Equipment::Backend, public EventListener
public:
EquipBackend();
- ~EquipBackend() override;
-
Item *getEquipment(int slotIndex) const override;
std::string getSlotName(int slotIndex) const override;
void clear() override;
- void equip(int itemId, int slotTypeId, int amountUsed = 1,
- int itemInstance = 0);
- void unequip(int slotTypeId);
+ void equip(int inventorySlot, int equipmentSlot);
+ void unequip(int inventorySlot);
void event(Event::Channel channel, const Event &event) override;
@@ -73,9 +70,7 @@ class EquipBackend final : public Equipment::Backend, public EventListener
// Generic info
std::string name;
- // The Item reference, used for graphical representation
- // and info.
- Item *item = nullptr;
+ int inventorySlot = 0;
// Manaserv specific info
@@ -89,10 +84,6 @@ class EquipBackend final : public Equipment::Backend, public EventListener
// This is used to sort the multimap along with the slot id.
unsigned int subId = 0;
- // This is the (per character) unique item Id, used especially when
- // equipping the same item multiple times on the same slot type.
- unsigned int itemInstance = 0;
-
// Tell whether the slot is a weapon slot
bool weaponSlot = false;
@@ -103,8 +94,7 @@ class EquipBackend final : public Equipment::Backend, public EventListener
unsigned int mVisibleSlots;
// slot client index, slot info
- using Slots = std::map<unsigned int, Slot>;
- Slots mSlots;
+ std::map<unsigned int, Slot> mSlots;
std::vector<Position> mBoxesPositions;
std::vector<std::string> mBoxesBackgroundFile;
};
@@ -119,8 +109,6 @@ class InventoryHandler final : public MessageHandler, Net::InventoryHandler,
void event(Event::Channel channel, const Event &event) override;
- bool canSplit(const Item *item) override;
-
size_t getSize(int type) const override;
bool isWeaponSlot(unsigned int slotTypeId) const override
@@ -146,5 +134,3 @@ class InventoryHandler final : public MessageHandler, Net::InventoryHandler,
};
} // namespace ManaServ
-
-#endif // NET_MANASERV_INVENTORYHANDLER_H
diff --git a/src/net/manaserv/itemhandler.cpp b/src/net/manaserv/itemhandler.cpp
index de5b36fa..870e7a9f 100644
--- a/src/net/manaserv/itemhandler.cpp
+++ b/src/net/manaserv/itemhandler.cpp
@@ -26,8 +26,6 @@
#include "net/manaserv/manaserv_protocol.h"
#include "net/manaserv/messagein.h"
-#include "log.h"
-
namespace ManaServ {
ItemHandler::ItemHandler()
diff --git a/src/net/manaserv/itemhandler.h b/src/net/manaserv/itemhandler.h
index 22adf8fb..c15d4638 100644
--- a/src/net/manaserv/itemhandler.h
+++ b/src/net/manaserv/itemhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_ITEMHANDLER_H
-#define NET_MANASERV_ITEMHANDLER_H
+#pragma once
#include "net/manaserv/messagehandler.h"
@@ -35,5 +34,3 @@ class ItemHandler final : public MessageHandler
};
} // namespace ManaServ
-
-#endif // NET_MANASERV_ITEMHANDLER_H
diff --git a/src/net/manaserv/loginhandler.cpp b/src/net/manaserv/loginhandler.cpp
index b9a56834..1c398990 100644
--- a/src/net/manaserv/loginhandler.cpp
+++ b/src/net/manaserv/loginhandler.cpp
@@ -399,8 +399,6 @@ void LoginHandler::loginAccount(LoginData *loginData)
mTmpPassword = loginData->password;
MessageOut msg(PAMSG_LOGIN_RNDTRGR);
- msg.writeString(mLoginData->username);
-
accountServerConnection->send(msg);
}
diff --git a/src/net/manaserv/loginhandler.h b/src/net/manaserv/loginhandler.h
index 87fbe9bc..e8d40835 100644
--- a/src/net/manaserv/loginhandler.h
+++ b/src/net/manaserv/loginhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_LOGINHANDLER_H
-#define NET_MANASERV_LOGINHANDLER_H
+#pragma once
#include "net/loginhandler.h"
@@ -91,5 +90,3 @@ class LoginHandler final : public MessageHandler, public Net::LoginHandler
};
} // namespace ManaServ
-
-#endif // NET_MANASERV_LOGINHANDLER_H
diff --git a/src/net/manaserv/manaserv_protocol.h b/src/net/manaserv/manaserv_protocol.h
index 760fe655..cb27d6f4 100644
--- a/src/net/manaserv/manaserv_protocol.h
+++ b/src/net/manaserv/manaserv_protocol.h
@@ -19,14 +19,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef MANASERV_PROTOCOL_H
-#define MANASERV_PROTOCOL_H
+#pragma once
namespace ManaServ {
enum {
- PROTOCOL_VERSION = 3,
- SUPPORTED_DB_VERSION = 21
+ PROTOCOL_VERSION = 9,
+ MIN_PROTOCOL_VERSION = 9,
+ SUPPORTED_DB_VERSION = 27
};
/**
@@ -69,19 +69,22 @@ enum {
PAMSG_REQUEST_REGISTER_INFO = 0x0005, //
APMSG_REGISTER_INFO_RESPONSE = 0x0006, // B byte registration Allowed, byte minNameLength, byte maxNameLength, string captchaURL, string captchaInstructions
PAMSG_LOGIN = 0x0010, // D version, S username, S password
- APMSG_LOGIN_RESPONSE = 0x0012, // B error, S updatehost, S Client data URL, B Character slots
+ APMSG_LOGIN_RESPONSE = 0x0012, // B error, S updatehost, S Client data URL, B Character slots, {content of APMSG_CHAR_CREATE_RESPONSE (without error code)}*
PAMSG_LOGOUT = 0x0013, // -
APMSG_LOGOUT_RESPONSE = 0x0014, // B error
- PAMSG_LOGIN_RNDTRGR = 0x0015, // S username
+ PAMSG_LOGIN_RNDTRGR = 0x0015, // -
APMSG_LOGIN_RNDTRGR_RESPONSE = 0x0016, // S random seed
+ PAMSG_STELLAR_LOGIN = 0x0017, // D version
+ APMSG_STELLAR_LOGIN_RESPONSE = 0x0018, // B error, S token, S url
PAMSG_CHAR_CREATE = 0x0020, // S name, B hair style, B hair color, B gender, B slot, {W stats}*
- APMSG_CHAR_CREATE_RESPONSE = 0x0021, // B error
+ APMSG_CHAR_CREATE_RESPONSE = 0x0021, // B error, on success: B slot, S name, B gender, B hair style, B hair color,
+ // W character points, W correction points, B amount of items equipped,
+ // { W slot, W itemId }*
+ // B attributeCount,
+ // {D attr id, D base value (in 1/256ths) D mod value (in 256ths) }*
PAMSG_CHAR_DELETE = 0x0022, // B slot
APMSG_CHAR_DELETE_RESPONSE = 0x0023, // B error
- // B slot, S name, B gender, B hair style, B hair color, W level,
- // W character points, W correction points,
- // {D attr id, D base value (in 1/256ths) D mod value (in 256ths) }*
- APMSG_CHAR_INFO = 0x0024, // ^
+ APMSG_CHAR_INFO = 0x0024, // {content of APMSG_CHAR_CREATE_RESPONSE (without error code)}*
PAMSG_CHAR_SELECT = 0x0026, // B slot
APMSG_CHAR_SELECT_RESPONSE = 0x0027, // B error, B*32 token, S game address, W game port, S chat address, W chat port
PAMSG_EMAIL_CHANGE = 0x0030, // S email
@@ -109,26 +112,28 @@ enum {
PGMSG_DROP = 0x0111, // W slot, W amount
PGMSG_EQUIP = 0x0112, // W inventory slot
PGMSG_UNEQUIP = 0x0113, // W item Instance id
- PGMSG_MOVE_ITEM = 0x0114, // W slot1, W slot2, W amount
GPMSG_INVENTORY = 0x0120, // { W slot, W item id [, W amount] (if item id is nonzero) }*
- GPMSG_INVENTORY_FULL = 0x0121, // W inventory slot count { W slot, W itemId, W amount }, { W equip slot, W item Id, W item Instance}*
- GPMSG_EQUIP = 0x0122, // W item Id, W equip slot type count //{ W equip slot, W capacity used}*//<- When equipping, //{ W item instance, W 0}*//<- When unequipping
+ GPMSG_INVENTORY_FULL = 0x0121, // W inventory slot count { W slot, W itemId, W amount, W equipmentSlot }
+ GPMSG_EQUIP = 0x0122, // W equipped inventory slot, W slot equipmentSlot
+ GPMSG_EQUIP_RESPONSE = 0x0123, // B error, W slot
+ GPMSG_UNEQUIP = 0x0124, // W equipped inventory slot
+ GPMSG_UNEQUIP_RESPONSE = 0x0125, // B error, W slot
GPMSG_PLAYER_ATTRIBUTE_CHANGE = 0x0130, // { W attribute, D base value (in 1/256ths), D modified value (in 1/256ths)}*
- GPMSG_PLAYER_EXP_CHANGE = 0x0140, // { W skill, D exp got, D exp needed, W skill level }*
- GPMSG_LEVELUP = 0x0150, // W new level, W character points, W correction points
- GPMSG_LEVEL_PROGRESS = 0x0151, // B percent completed to next levelup
+ GPMSG_ATTRIBUTE_POINTS_STATUS = 0x0140, // W character points, W correction points
PGMSG_RAISE_ATTRIBUTE = 0x0160, // W attribute
GPMSG_RAISE_ATTRIBUTE_RESPONSE = 0x0161, // B error, W attribute
PGMSG_LOWER_ATTRIBUTE = 0x0170, // W attribute
GPMSG_LOWER_ATTRIBUTE_RESPONSE = 0x0171, // B error, W attribute
PGMSG_RESPAWN = 0x0180, // -
GPMSG_BEING_ENTER = 0x0200, // B type, W being id, B action, W*2 position, B direction, B gender
- // character: S name, B hair style, B hair color, B sprite layers changed, { B slot type, W item id }*
+ // character: S name, B hair style, B hair color [, B sprite layers changed, { B slot type, W item id }*]
// monster: W type id
// npc: W type id
GPMSG_BEING_LEAVE = 0x0201, // W being id
GPMSG_ITEM_APPEAR = 0x0202, // W item id, W*2 position
- GPMSG_BEING_LOOKS_CHANGE = 0x0210, // B sprite layers changed, { B slot type, W item id }*
+ GPMSG_BEING_LOOKS_CHANGE = 0x0210, // B hairstyle, B haircolor [, B sprite layers changed, { B slot type, W item id }*]
+ GPMSG_BEING_EMOTE = 0x0211, // W being id, W emote id
+ PGMSG_BEING_EMOTE = 0x0212, // W emoticon id
PGMSG_WALK = 0x0260, // W*2 destination
PGMSG_ACTION_CHANGE = 0x0270, // B Action
GPMSG_BEING_ACTION_CHANGE = 0x0271, // W being id, B action
@@ -137,45 +142,50 @@ enum {
GPMSG_BEING_HEALTH_CHANGE = 0x0274, // W being id, W hp, W max hp
GPMSG_BEINGS_MOVE = 0x0280, // { W being id, B flags [, [W*2 position,] W*2 destination, B speed] }*
GPMSG_ITEMS = 0x0281, // { W item id, W*2 position }*
- PGMSG_ATTACK = 0x0290, // W being id
- GPMSG_BEING_ATTACK = 0x0291, // W being id, B direction, B attack Id
- PGMSG_USE_SPECIAL_ON_BEING = 0x0292, // B specialID, W being id
- GPMSG_SPECIAL_STATUS = 0x0293, // { B specialID, D current, D max, D recharge }
- PGMSG_USE_SPECIAL_ON_POINT = 0x0294, // B specialID, W*2 position
- PGMSG_SAY = 0x02A0, // S text
- GPMSG_SAY = 0x02A1, // W being id, S text
- GPMSG_NPC_CHOICE = 0x02B0, // W being id, { S text }*
- GPMSG_NPC_MESSAGE = 0x02B1, // W being id, B* text
- PGMSG_NPC_TALK = 0x02B2, // W being id
- PGMSG_NPC_TALK_NEXT = 0x02B3, // W being id
- PGMSG_NPC_SELECT = 0x02B4, // W being id, B choice
- GPMSG_NPC_BUY = 0x02B5, // W being id, { W item id, W amount, W cost }*
- GPMSG_NPC_SELL = 0x02B6, // W being id, { W item id, W amount, W cost }*
- PGMSG_NPC_BUYSELL = 0x02B7, // W item id, W amount
- GPMSG_NPC_ERROR = 0x02B8, // B error
- GPMSG_NPC_CLOSE = 0x02B9, // W being id
+ GPMSG_BEING_ABILITY_POINT = 0x0282, // W being id, B abilityId, W*2 point
+ GPMSG_BEING_ABILITY_BEING = 0x0283, // W being id, B abilityId, W target being id
+ GPMSG_BEING_ABILITY_DIRECTION = 0x0284, // W being id, B abilityId, B direction
+ PGMSG_USE_ABILITY_ON_BEING = 0x0290, // B abilityID, W being id
+ PGMSG_USE_ABILITY_ON_POINT = 0x0291, // B abilityID, W*2 position
+ PGMSG_USE_ABILITY_ON_DIRECTION = 0x0292, // B abilityID, B direction
+ GPMSG_ABILITY_STATUS = 0x02A0, // { B abilityID, D remainingTicks }
+ GPMSG_ABILITY_REMOVED = 0x02A1, // B abilityID
+ GPMSG_ABILITY_COOLDOWN = 0x02A2, // W ticks to wait
+ PGMSG_SAY = 0x02B0, // S text
+ GPMSG_SAY = 0x02B1, // W being id, S text
+ GPMSG_NPC_CHOICE = 0x02C0, // W being id, { S text }*
+ GPMSG_NPC_MESSAGE = 0x02C1, // W being id, B* text
+ PGMSG_NPC_TALK = 0x02C2, // W being id
+ PGMSG_NPC_TALK_NEXT = 0x02C3, // W being id
+ PGMSG_NPC_SELECT = 0x02C4, // W being id, B choice
+ GPMSG_NPC_BUY = 0x02C5, // W being id, { W item id, W amount, W cost }*
+ GPMSG_NPC_SELL = 0x02C6, // W being id, { W item id, W amount, W cost }*
+ PGMSG_NPC_BUYSELL = 0x02C7, // W item id, W amount
+ GPMSG_NPC_ERROR = 0x02C8, // B error
+ GPMSG_NPC_CLOSE = 0x02C9, // W being id
GPMSG_NPC_POST = 0x02D0, // W being id
PGMSG_NPC_POST_SEND = 0x02D1, // W being id, { S name, S text, W item id }
GPMSG_NPC_POST_GET = 0x02D2, // W being id, { S name, S text, W item id }
PGMSG_NPC_NUMBER = 0x02D3, // W being id, D number
PGMSG_NPC_STRING = 0x02D4, // W being id, S string
- GPMSG_NPC_NUMBER = 0x02D5, // W being id, D max, D min, D default
+ GPMSG_NPC_NUMBER = 0x02D5, // W being id, D min, D max, D default
GPMSG_NPC_STRING = 0x02D6, // W being id
- PGMSG_TRADE_REQUEST = 0x02C0, // W being id
- GPMSG_TRADE_REQUEST = 0x02C1, // W being id
- GPMSG_TRADE_START = 0x02C2, // -
- GPMSG_TRADE_COMPLETE = 0x02C3, // -
- PGMSG_TRADE_CANCEL = 0x02C4, // -
- GPMSG_TRADE_CANCEL = 0x02C5, // -
- PGMSG_TRADE_AGREED = 0x02C6, // -
- GPMSG_TRADE_AGREED = 0x02C7, // -
- PGMSG_TRADE_CONFIRM = 0x02C8, // -
- GPMSG_TRADE_CONFIRM = 0x02C9, // -
- PGMSG_TRADE_ADD_ITEM = 0x02CA, // B slot, B amount
- GPMSG_TRADE_ADD_ITEM = 0x02CB, // W item id, B amount
- PGMSG_TRADE_SET_MONEY = 0x02CC, // D amount
- GPMSG_TRADE_SET_MONEY = 0x02CD, // D amount
- GPMSG_TRADE_BOTH_CONFIRM = 0x02CE, // -
+ GPMSG_NPC_BUYSELL_RESPONSE = 0x02D7, // B error, W item id, W amount
+ PGMSG_TRADE_REQUEST = 0x02E0, // W being id
+ GPMSG_TRADE_REQUEST = 0x02E1, // W being id
+ GPMSG_TRADE_START = 0x02E2, // -
+ GPMSG_TRADE_COMPLETE = 0x02E3, // -
+ PGMSG_TRADE_CANCEL = 0x02E4, // -
+ GPMSG_TRADE_CANCEL = 0x02E5, // -
+ PGMSG_TRADE_AGREED = 0x02E6, // -
+ GPMSG_TRADE_AGREED = 0x02E7, // -
+ PGMSG_TRADE_CONFIRM = 0x02E8, // -
+ GPMSG_TRADE_CONFIRM = 0x02E9, // -
+ PGMSG_TRADE_ADD_ITEM = 0x02EA, // B slot, B amount
+ GPMSG_TRADE_ADD_ITEM = 0x02EB, // W item id, B amount
+ PGMSG_TRADE_SET_MONEY = 0x02EC, // D amount
+ GPMSG_TRADE_SET_MONEY = 0x02ED, // D amount
+ GPMSG_TRADE_BOTH_CONFIRM = 0x02EE, // -
PGMSG_USE_ITEM = 0x0300, // B slot
GPMSG_USE_RESPONSE = 0x0301, // B error
GPMSG_BEINGS_DAMAGE = 0x0310, // { W being id, W amount }*
@@ -243,8 +253,11 @@ enum {
PCMSG_USER_MODE = 0x0465, // W channel id, S name, B mode
PCMSG_KICK_USER = 0x0466, // W channel id, S name
+ // -- Questlog
+ GPMSG_QUESTLOG_STATUS = 0x0470, // {W quest id, B flags, [B status], [S questname], [S questdescription]}*
+
// Inter-server
- GAMSG_REGISTER = 0x0500, // S address, W port, S password, D items db revision, { W map id }*
+ GAMSG_REGISTER = 0x0500, // S address, W port, S password, D items db revision
AGMSG_REGISTER_RESPONSE = 0x0501, // W item version, W password response, { S globalvar_key, S globalvar_value }
AGMSG_ACTIVE_MAP = 0x0502, // W map id, W Number of mapvar_key mapvar_value sent, { S mapvar_key, S mapvar_value }, W Number of map items, { D item Id, W amount, W posX, W posY }
AGMSG_PLAYER_ENTER = 0x0510, // B*32 token, D id, S name, serialised character data
@@ -263,7 +276,6 @@ enum {
GAMSG_SET_VAR_WORLD = 0x0547, // S name, S value
AGMSG_SET_VAR_WORLD = 0x0548, // S name, S value
GAMSG_BAN_PLAYER = 0x0550, // D id, W duration
- GAMSG_CHANGE_PLAYER_LEVEL = 0x0555, // D id, W level
GAMSG_CHANGE_ACCOUNT_LEVEL = 0x0556, // D id, W level
GAMSG_STATISTICS = 0x0560, // { W map id, W entity nb, W monster nb, W player nb, { D character id }* }*
CGMSG_CHANGED_PARTY = 0x0590, // D character id, D party id
@@ -295,7 +307,8 @@ enum {
ERRMSG_TIME_OUT, // data failed to arrive in due time
ERRMSG_LIMIT_REACHED, // limit reached
ERRMSG_ADMINISTRATIVE_LOGOFF, // kicked by server administrator
- ERRMSG_ALREADY_MEMBER // is already member of guild/party
+ ERRMSG_ALREADY_MEMBER, // is already member of guild/party
+ ERRMSG_LOGIN_WAS_TAKEN_OVER // a different connection took over
};
// used in AGMSG_REGISTER_RESPONSE to show state of item db
@@ -314,7 +327,6 @@ enum {
enum {
SYNC_CHARACTER_POINTS = 0x01, // D charId, D charPoints, D corrPoints
SYNC_CHARACTER_ATTRIBUTE = 0x02, // D charId, D attrId, DF base, DF mod
- SYNC_CHARACTER_SKILL = 0x03, // D charId, B skillId, D skill value
SYNC_ONLINE_STATUS = 0x04 // D charId, B 0 = offline, 1 = online
};
@@ -358,21 +370,18 @@ enum AttribmodResponseCode {
enum EntityType
{
// A simple item.
- OBJECT_ITEM = 0,
- // An item that toggle map/quest actions (doors, switchs, ...)
- // and can speak (map panels).
- OBJECT_ACTOR,
+ OBJECT_ITEM = 0,
// Non-Playable-Character is an actor capable of movement and maybe actions.
- OBJECT_NPC,
+ OBJECT_NPC = 2,
// A monster (moving actor with AI. Should be able to toggle map/quest
// actions, too).
- OBJECT_MONSTER,
+ OBJECT_MONSTER = 3,
// A normal being.
- OBJECT_CHARACTER,
+ OBJECT_CHARACTER = 4,
// A effect to be shown.
- OBJECT_EFFECT,
+ OBJECT_EFFECT = 5,
// Server-only object.
- OBJECT_OTHER
+ OBJECT_OTHER = 6
};
// Moving object flags
@@ -406,6 +415,13 @@ enum {
GUILD_EVENT_OFFLINE_PLAYER
};
+enum {
+ QUESTLOG_UPDATE_STATE = 1,
+ QUESTLOG_UPDATE_TITLE = 2,
+ QUESTLOG_UPDATE_DESCRIPTION = 4,
+ QUESTLOG_SHOW_NOTIFICATION = 8
+};
+
/**
* Moves enum for beings and actors for others players vision.
* WARNING: Has to be in sync with the same enum in the Being class
@@ -415,27 +431,12 @@ enum BeingAction
{
STAND,
WALK,
- ATTACK,
SIT,
DEAD,
HURT
};
/**
- * Moves enum for beings and actors for others players attack types.
- * WARNING: Has to be in sync with the same enum in the Being class
- * of the client!
- */
-enum AttackType
-{
- HIT = 0x00,
- CRITICAL = 0x0a,
- MULTI = 0x08,
- REFLECT = 0x04,
- FLEE = 0x0b
-};
-
-/**
* Beings and actors directions
* WARNING: Has to be in sync with the same enum in the Being class
* of the client!
@@ -458,6 +459,55 @@ enum BeingGender
GENDER_UNSPECIFIED
};
+// Helper functions for gender
+
+/**
+* Helper function for getting gender by int
+*/
+inline ManaServ::BeingGender getGender(int gender)
+{
+ switch (gender)
+ {
+ case 0:
+ return ManaServ::GENDER_MALE;
+ case 1:
+ return ManaServ::GENDER_FEMALE;
+ default:
+ return ManaServ::GENDER_UNSPECIFIED;
+ }
+}
+
+/**
+ * Quest states
+ */
+enum QuestStatus
+{
+ STATUS_OPEN = 0,
+ STATUS_STARTED,
+ STATUS_FINISHED,
+ STATUS_INVALID
+};
+
+/**
+ * Helper function for getting quest status by id
+ * @param status id of the status
+ * @return the status as enum value
+ */
+inline ManaServ::QuestStatus getQuestStatus(int status)
+{
+ switch (status)
+ {
+ case 0:
+ return ManaServ::STATUS_OPEN;
+ case 1:
+ return ManaServ::STATUS_STARTED;
+ case 2:
+ return ManaServ::STATUS_FINISHED;
+ default:
+ return ManaServ::STATUS_INVALID;
+ }
+}
+
/** The permited range to pick up an item */
const int PICKUP_RANGE = 32 + 16;
@@ -465,5 +515,3 @@ const int PICKUP_RANGE = 32 + 16;
const int NPC_TALK_RANGE = 32 * 7;
} // namespace ManaServ
-
-#endif // MANASERV_PROTOCOL_H
diff --git a/src/net/manaserv/messagehandler.h b/src/net/manaserv/messagehandler.h
index f9ee09ab..2c68cde5 100644
--- a/src/net/manaserv/messagehandler.h
+++ b/src/net/manaserv/messagehandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_MESSAGEHANDLER_H
-#define NET_MANASERV_MESSAGEHANDLER_H
+#pragma once
#include "net/messagehandler.h"
@@ -44,5 +43,3 @@ class MessageHandler : public Net::MessageHandler
using MessageHandlerPtr = const std::unique_ptr<MessageHandler>;
} // namespace ManaServ
-
-#endif // NET_MANASERV_MESSAGEHANDLER_H
diff --git a/src/net/manaserv/messagein.h b/src/net/manaserv/messagein.h
index 1edc4fe7..cd072fe0 100644
--- a/src/net/manaserv/messagein.h
+++ b/src/net/manaserv/messagein.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_MESSAGEIN_H
-#define NET_MANASERV_MESSAGEIN_H
+#pragma once
#include "net/manaserv/manaserv_protocol.h"
@@ -95,5 +94,3 @@ class MessageIn
};
} // namespace ManaServ
-
-#endif // NET_MANASERV_MESSAGEIN_H
diff --git a/src/net/manaserv/messageout.h b/src/net/manaserv/messageout.h
index d452f784..d9d2e68a 100644
--- a/src/net/manaserv/messageout.h
+++ b/src/net/manaserv/messageout.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_MESSAGEOUT_H
-#define NET_MANASERV_MESSAGEOUT_H
+#pragma once
#include "net/manaserv/manaserv_protocol.h"
@@ -91,5 +90,3 @@ class MessageOut
};
} // namespace ManaServ
-
-#endif // NET_MANASERV_MESSAGEOUT_H
diff --git a/src/net/manaserv/network.cpp b/src/net/manaserv/network.cpp
index b8d3fa93..d69d3397 100644
--- a/src/net/manaserv/network.cpp
+++ b/src/net/manaserv/network.cpp
@@ -88,17 +88,13 @@ Connection *getConnection()
void registerHandler(MessageHandler *handler)
{
for (const uint16_t *i = handler->handledMessages; *i; i++)
- {
mMessageHandlers[*i] = handler;
- }
}
void unregisterHandler(MessageHandler *handler)
{
for (const uint16_t *i = handler->handledMessages; *i; i++)
- {
mMessageHandlers.erase(*i);
- }
}
void clearNetworkHandlers()
diff --git a/src/net/manaserv/network.h b/src/net/manaserv/network.h
index f484d54d..a51a8168 100644
--- a/src/net/manaserv/network.h
+++ b/src/net/manaserv/network.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_NETWORK_H
-#define NET_MANASERV_NETWORK_H
+#pragma once
#include <iosfwd>
@@ -71,5 +70,3 @@ namespace ManaServ
*/
void flush();
} // namespace ManaServ
-
-#endif
diff --git a/src/net/manaserv/npchandler.cpp b/src/net/manaserv/npchandler.cpp
index f19bf134..44729654 100644
--- a/src/net/manaserv/npchandler.cpp
+++ b/src/net/manaserv/npchandler.cpp
@@ -47,6 +47,7 @@ NpcHandler::NpcHandler()
GPMSG_NPC_CLOSE,
GPMSG_NPC_NUMBER,
GPMSG_NPC_STRING,
+ GPMSG_NPC_BUYSELL_RESPONSE,
0
};
handledMessages = _messages;
@@ -122,6 +123,9 @@ void NpcHandler::handleMessage(MessageIn &msg)
event->setInt("id", npcId);
event->trigger(Event::NpcChannel);
break;
+
+ case GPMSG_NPC_BUYSELL_RESPONSE:
+ break;
}
delete event;
diff --git a/src/net/manaserv/npchandler.h b/src/net/manaserv/npchandler.h
index ee3a9a12..6c485650 100644
--- a/src/net/manaserv/npchandler.h
+++ b/src/net/manaserv/npchandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_NPCHANDLER_H
-#define NET_MANASERV_NPCHANDLER_H
+#pragma once
#include "net/npchandler.h"
@@ -65,5 +64,3 @@ class NpcHandler final : public MessageHandler, public Net::NpcHandler
};
} // namespace ManaServ
-
-#endif // NET_MANASERV_NPCHANDLER_H
diff --git a/src/net/manaserv/partyhandler.h b/src/net/manaserv/partyhandler.h
index ac4249a9..ab5aad9a 100644
--- a/src/net/manaserv/partyhandler.h
+++ b/src/net/manaserv/partyhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_PARTYHANDLER_H
-#define NET_MANASERV_PARTYHANDLER_H
+#pragma once
#include "net/partyhandler.h"
@@ -70,5 +69,3 @@ private:
};
} // namespace ManaServ
-
-#endif // NET_MANASERV_PARTYHANDLER_H
diff --git a/src/net/manaserv/playerhandler.cpp b/src/net/manaserv/playerhandler.cpp
index bf5694c6..8ee9ed80 100644
--- a/src/net/manaserv/playerhandler.cpp
+++ b/src/net/manaserv/playerhandler.cpp
@@ -29,17 +29,18 @@
#include "log.h"
#include "particle.h"
#include "playerinfo.h"
-#include "configuration.h"
#include "gui/viewport.h"
#include "net/net.h"
+#include "net/abilityhandler.h"
#include "net/manaserv/connection.h"
#include "net/manaserv/messagein.h"
#include "net/manaserv/messageout.h"
#include "net/manaserv/manaserv_protocol.h"
+#include "resources/abilitydb.h"
#include "resources/attributes.h"
/**
@@ -50,6 +51,7 @@
const int MAP_TELEPORT_SCROLL_DISTANCE = 256;
extern Net::PlayerHandler *playerHandler;
+extern Net::AbilityHandler *abilityHandler;
namespace ManaServ {
@@ -70,12 +72,11 @@ PlayerHandler::PlayerHandler()
GPMSG_PLAYER_MAP_CHANGE,
GPMSG_PLAYER_SERVER_CHANGE,
GPMSG_PLAYER_ATTRIBUTE_CHANGE,
- GPMSG_PLAYER_EXP_CHANGE,
- GPMSG_LEVELUP,
- GPMSG_LEVEL_PROGRESS,
+ GPMSG_ATTRIBUTE_POINTS_STATUS,
GPMSG_RAISE_ATTRIBUTE_RESPONSE,
GPMSG_LOWER_ATTRIBUTE_RESPONSE,
- GPMSG_SPECIAL_STATUS,
+ GPMSG_ABILITY_STATUS,
+ GPMSG_ABILITY_REMOVED,
0
};
handledMessages = _messages;
@@ -129,39 +130,10 @@ void PlayerHandler::handleMessage(MessageIn &msg)
}
} break;
- case GPMSG_PLAYER_EXP_CHANGE:
- {
- logger->log("EXP Update");
- while (msg.getUnreadLength())
- {
- int skill = msg.readInt16();
- int current = msg.readInt32();
- int next = msg.readInt32();
- int level = msg.readInt16();
-
- PlayerInfo::setStatExperience(skill, current, next);
- PlayerInfo::setStatBase(skill, level);
- }
- } break;
-
- case GPMSG_LEVELUP:
- {
- PlayerInfo::setAttribute(LEVEL, msg.readInt16());
+ case GPMSG_ATTRIBUTE_POINTS_STATUS:
PlayerInfo::setAttribute(CHAR_POINTS, msg.readInt16());
PlayerInfo::setAttribute(CORR_POINTS, msg.readInt16());
- Particle* effect = particleEngine->addEffect(
- paths.getStringValue("particles")
- + paths.getStringValue("levelUpEffectFile")
- ,0, 0);
- local_player->controlParticle(effect);
- } break;
-
-
- case GPMSG_LEVEL_PROGRESS:
- {
- PlayerInfo::setAttribute(EXP, msg.readInt8());
- } break;
-
+ break;
case GPMSG_RAISE_ATTRIBUTE_RESPONSE:
{
@@ -243,19 +215,24 @@ void PlayerHandler::handleMessage(MessageIn &msg)
} break;
- case GPMSG_SPECIAL_STATUS :
+ case GPMSG_ABILITY_STATUS:
{
- PlayerInfo::clearSpecialStatus();
while (msg.getUnreadLength())
{
- // { B specialID, L current, L max, L recharge }
int id = msg.readInt8();
int current = msg.readInt32();
int max = msg.readInt32();
int recharge = msg.readInt32();
- PlayerInfo::setSpecialStatus(id, current, max, recharge);
+ PlayerInfo::setAbilityStatus(id, current, max, recharge);
}
} break;
+
+ case GPMSG_ABILITY_REMOVED:
+ {
+ int id = msg.readInt8();
+ PlayerInfo::clearAbilityStatus(id);
+ } break;
+
/*
case SMSG_PLAYER_ARROW_MESSAGE:
{
@@ -315,14 +292,31 @@ void PlayerHandler::handleMapChangeMessage(MessageIn &msg)
void PlayerHandler::attack(int id)
{
- MessageOut msg(PGMSG_ATTACK);
- msg.writeInt16(id);
- gameServerConnection->send(msg);
+ auto ability = AbilityDB::find("Strike");
+ if (!ability)
+ {
+ logger->log("PlayerHandler::attack: 'Strike' ability not found.");
+ return;
+ }
+
+ switch (ability->targetMode) {
+ case AbilityInfo::TARGET_BEING:
+ abilityHandler->useOn(ability->id, id);
+ break;
+ case AbilityInfo::TARGET_POINT:
+ logger->log("PlayerHandler::attack: Unsupported target mode 'point' for 'Strike' ability.");
+ break;
+ case AbilityInfo::TARGET_DIRECTION:
+ abilityHandler->useInDirection(ability->id, local_player->getDirection());
+ break;
+ }
}
void PlayerHandler::emote(int emoteId)
{
- // TODO
+ MessageOut msg(PGMSG_BEING_EMOTE);
+ msg.writeInt8(emoteId);
+ gameServerConnection->send(msg);
}
void PlayerHandler::increaseAttribute(int attr)
diff --git a/src/net/manaserv/playerhandler.h b/src/net/manaserv/playerhandler.h
index a6839112..134b449c 100644
--- a/src/net/manaserv/playerhandler.h
+++ b/src/net/manaserv/playerhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_PLAYERHANDLER_H
-#define NET_MANASERV_PLAYERHANDLER_H
+#pragma once
#include "net/playerhandler.h"
@@ -77,5 +76,3 @@ class PlayerHandler final : public MessageHandler, public Net::PlayerHandler
};
} // namespace ManaServ
-
-#endif // NET_MANASERV_PLAYERHANDLER_H
diff --git a/src/net/manaserv/tradehandler.h b/src/net/manaserv/tradehandler.h
index 928a62d9..374dde16 100644
--- a/src/net/manaserv/tradehandler.h
+++ b/src/net/manaserv/tradehandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MANASERV_TRADEHANDLER_H
-#define NET_MANASERV_TRADEHANDLER_H
+#pragma once
#include "net/tradehandler.h"
@@ -72,5 +71,3 @@ class TradeHandler final : public MessageHandler, public Net::TradeHandler
};
} // namespace ManaServ
-
-#endif // NET_MANASERV_TRADEHANDLER_H
diff --git a/src/net/messagehandler.h b/src/net/messagehandler.h
index 3bf6c419..21973fa9 100644
--- a/src/net/messagehandler.h
+++ b/src/net/messagehandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_MESSAGEHANDLER_H
-#define NET_MESSAGEHANDLER_H
+#pragma once
#include <cstdint>
@@ -32,11 +31,12 @@ namespace Net {
class MessageHandler
{
public:
- const uint16_t *handledMessages;
+ const uint16_t *handledMessages = _no_messages;
virtual ~MessageHandler() {}
+
+ private:
+ static constexpr uint16_t _no_messages[] = { 0 };
};
} // namespace Net
-
-#endif // NET_MESSAGEHANDLER_H
diff --git a/src/net/net.cpp b/src/net/net.cpp
index 3a389db2..1d157b3d 100644
--- a/src/net/net.cpp
+++ b/src/net/net.cpp
@@ -34,7 +34,7 @@
#include "net/npchandler.h"
#include "net/partyhandler.h"
#include "net/playerhandler.h"
-#include "net/specialhandler.h"
+#include "net/abilityhandler.h"
#include "net/tradehandler.h"
#include "net/tmwa/generalhandler.h"
@@ -54,7 +54,7 @@ Net::GuildHandler *guildHandler = nullptr;
Net::NpcHandler *npcHandler = nullptr;
Net::PartyHandler *partyHandler = nullptr;
Net::PlayerHandler *playerHandler = nullptr;
-Net::SpecialHandler *specialHandler = nullptr;
+Net::AbilityHandler *abilityHandler = nullptr;
Net::TradeHandler *tradeHandler = nullptr;
Net::AdminHandler *Net::getAdminHandler()
@@ -112,9 +112,9 @@ Net::PlayerHandler *Net::getPlayerHandler()
return playerHandler;
}
-Net::SpecialHandler *Net::getSpecialHandler()
+Net::AbilityHandler *Net::getAbilityHandler()
{
- return specialHandler;
+ return abilityHandler;
}
Net::TradeHandler *Net::getTradeHandler()
@@ -124,18 +124,19 @@ Net::TradeHandler *Net::getTradeHandler()
namespace Net
{
-ServerType networkType = ServerType::UNKNOWN;
+
+static ServerType networkType = ServerType::Unknown;
void connectToServer(ServerInfo &server)
{
- if (server.type == ServerType::UNKNOWN)
+ if (server.type == ServerType::Unknown)
{
// TODO: Query the server about itself and choose the netcode based on
// that
if (server.port == 6901)
- server.type = ServerType::TMWATHENA;
+ server.type = ServerType::TmwAthena;
else if (server.port == 9601)
- server.type = ServerType::MANASERV;
+ server.type = ServerType::ManaServ;
else
logger->error(_("Unknown Server Type! Exiting."));
}
@@ -146,20 +147,17 @@ void connectToServer(ServerInfo &server)
}
else
{
- if (networkType != ServerType::UNKNOWN && getGeneralHandler() != nullptr)
- {
- getGeneralHandler()->unload();
- }
+ unload();
switch (server.type)
{
#ifdef MANASERV_SUPPORT
- case ServerType::MANASERV:
- new ManaServ::GeneralHandler;
+ case ServerType::ManaServ:
+ generalHandler = new ManaServ::GeneralHandler;
break;
#endif
- case ServerType::TMWATHENA:
- new TmwAthena::GeneralHandler;
+ case ServerType::TmwAthena:
+ generalHandler = new TmwAthena::GeneralHandler;
break;
default:
logger->error(_("Server protocol unsupported"));
@@ -178,10 +176,25 @@ void connectToServer(ServerInfo &server)
void unload()
{
- if (GeneralHandler *handler = getGeneralHandler())
- {
- handler->unload();
- }
+ if (!generalHandler)
+ return;
+
+ generalHandler->unload();
+ delete generalHandler;
+
+ adminHandler = nullptr;
+ charHandler = nullptr;
+ chatHandler = nullptr;
+ generalHandler = nullptr;
+ inventoryHandler = nullptr;
+ loginHandler = nullptr;
+ gameHandler = nullptr;
+ guildHandler = nullptr;
+ npcHandler = nullptr;
+ partyHandler = nullptr;
+ playerHandler = nullptr;
+ abilityHandler = nullptr;
+ tradeHandler = nullptr;
}
ServerType getNetworkType()
diff --git a/src/net/net.h b/src/net/net.h
index 135f2cb7..ced0f7ba 100644
--- a/src/net/net.h
+++ b/src/net/net.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_H
-#define NET_H
+#pragma once
/**
* \namespace Net
@@ -45,7 +44,7 @@ class LoginHandler;
class NpcHandler;
class PartyHandler;
class PlayerHandler;
-class SpecialHandler;
+class AbilityHandler;
class TradeHandler;
AdminHandler *getAdminHandler();
@@ -59,7 +58,7 @@ LoginHandler *getLoginHandler();
NpcHandler *getNpcHandler();
PartyHandler *getPartyHandler();
PlayerHandler *getPlayerHandler();
-SpecialHandler *getSpecialHandler();
+AbilityHandler *getAbilityHandler();
TradeHandler *getTradeHandler();
ServerType getNetworkType();
@@ -72,5 +71,3 @@ void connectToServer(ServerInfo &server);
void unload();
} // namespace Net
-
-#endif // NET_H
diff --git a/src/net/npchandler.h b/src/net/npchandler.h
index ac575605..7eeb54f1 100644
--- a/src/net/npchandler.h
+++ b/src/net/npchandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NPCHANDLER_H
-#define NPCHANDLER_H
+#pragma once
#include <iosfwd>
@@ -61,5 +60,3 @@ class NpcHandler
};
} // namespace Net
-
-#endif // NPCHANDLER_H
diff --git a/src/net/partyhandler.h b/src/net/partyhandler.h
index 4e14ddbd..58871358 100644
--- a/src/net/partyhandler.h
+++ b/src/net/partyhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef PARTYHANDLER_H
-#define PARTYHANDLER_H
+#pragma once
#include <string>
@@ -74,5 +73,3 @@ class PartyHandler
};
} // namespace Net
-
-#endif // PARTYHANDLER_H
diff --git a/src/net/playerhandler.h b/src/net/playerhandler.h
index 6187da2a..b9cf1abf 100644
--- a/src/net/playerhandler.h
+++ b/src/net/playerhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef PLAYERHANDLER_H
-#define PLAYERHANDLER_H
+#pragma once
#include "being.h"
#include "flooritem.h"
@@ -84,5 +83,3 @@ class PlayerHandler
};
} // namespace Net
-
-#endif // PLAYERHANDLER_H
diff --git a/src/net/serverinfo.h b/src/net/serverinfo.h
index fd66c676..ad862427 100644
--- a/src/net/serverinfo.h
+++ b/src/net/serverinfo.h
@@ -19,10 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SERVERINFO_H
-#define SERVERINFO_H
-
-#include "utils/stringutils.h"
+#pragma once
#include <cstdint>
#include <deque>
@@ -30,9 +27,9 @@
enum class ServerType
{
- UNKNOWN,
- MANASERV,
- TMWATHENA
+ Unknown,
+ ManaServ,
+ TmwAthena
};
class ServerInfo
@@ -40,7 +37,7 @@ class ServerInfo
public:
using VersionString = std::pair<int, std::string>;
- ServerType type = ServerType::UNKNOWN;
+ ServerType type = ServerType::Unknown;
std::string name;
std::string hostname;
uint16_t port = 0;
@@ -53,7 +50,7 @@ public:
bool isValid() const
{
- return !hostname.empty() && port != 0 && type != ServerType::UNKNOWN;
+ return !hostname.empty() && port != 0 && type != ServerType::Unknown;
}
void clear()
@@ -75,14 +72,14 @@ public:
static ServerType parseType(const std::string &type)
{
- if (compareStrI(type, "tmwathena") == 0)
- return ServerType::TMWATHENA;
+ if (type == "tmwathena")
+ return ServerType::TmwAthena;
// Used for backward compatibility
- if (compareStrI(type, "eathena") == 0)
- return ServerType::TMWATHENA;
- if (compareStrI(type, "manaserv") == 0)
- return ServerType::MANASERV;
- return ServerType::UNKNOWN;
+ if (type == "eathena")
+ return ServerType::TmwAthena;
+ if (type == "manaserv")
+ return ServerType::ManaServ;
+ return ServerType::Unknown;
}
static uint16_t defaultPortForServerType(ServerType type)
@@ -90,11 +87,11 @@ public:
switch (type)
{
default:
- case ServerType::UNKNOWN:
+ case ServerType::Unknown:
return 0;
- case ServerType::TMWATHENA:
+ case ServerType::TmwAthena:
return 6901;
- case ServerType::MANASERV:
+ case ServerType::ManaServ:
return 9601;
}
}
@@ -102,13 +99,11 @@ public:
static ServerType defaultServerTypeForPort(uint16_t port)
{
if (port == 6901)
- return ServerType::TMWATHENA;
+ return ServerType::TmwAthena;
if (port == 9601)
- return ServerType::MANASERV;
- return ServerType::UNKNOWN;
+ return ServerType::ManaServ;
+ return ServerType::Unknown;
}
};
using ServerInfos = std::deque<ServerInfo>;
-
-#endif // SERVERINFO_H
diff --git a/src/net/tmwa/specialhandler.cpp b/src/net/tmwa/abilityhandler.cpp
index 2e22b00a..ab891b40 100644
--- a/src/net/tmwa/specialhandler.cpp
+++ b/src/net/tmwa/abilityhandler.cpp
@@ -19,7 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "net/tmwa/specialhandler.h"
+#include "net/tmwa/abilityhandler.h"
#include "event.h"
#include "log.h"
@@ -64,11 +64,11 @@
/** should always be zero if failed */
#define SKILL_FAILED 0x00
-extern Net::SpecialHandler *specialHandler;
+extern Net::AbilityHandler *abilityHandler;
namespace TmwAthena {
-SpecialHandler::SpecialHandler()
+AbilityHandler::AbilityHandler()
{
static const Uint16 _messages[] = {
SMSG_PLAYER_SKILLS,
@@ -77,23 +77,20 @@ SpecialHandler::SpecialHandler()
0
};
handledMessages = _messages;
- specialHandler = this;
+ abilityHandler = this;
}
-void SpecialHandler::handleMessage(MessageIn &msg)
+void AbilityHandler::handleMessage(MessageIn &msg)
{
- int skillCount;
- int skillId;
-
switch (msg.getId())
{
- case SMSG_PLAYER_SKILLS:
+ case SMSG_PLAYER_SKILLS: {
msg.readInt16(); // length
- skillCount = (msg.getLength() - 4) / 37;
+ const int skillCount = (msg.getLength() - 4) / 37;
for (int k = 0; k < skillCount; k++)
{
- skillId = msg.readInt16();
+ int skillId = msg.readInt16();
msg.readInt16(); // target type
msg.skip(2); // unused
int level = msg.readInt16();
@@ -107,10 +104,11 @@ void SpecialHandler::handleMessage(MessageIn &msg)
skillDialog->setModifiable(skillId, up);
}
break;
+ }
case SMSG_PLAYER_SKILL_UP:
{
- skillId = msg.readInt16();
+ int skillId = msg.readInt16();
int level = msg.readInt16();
msg.readInt16(); // sp
msg.readInt16(); // range
@@ -124,20 +122,20 @@ void SpecialHandler::handleMessage(MessageIn &msg)
case SMSG_SKILL_FAILED:
// Action failed (ex. sit because you have not reached the
// right level)
- skillId = msg.readInt16();
- short bskill = msg.readInt16();
- msg.readInt16(); // unknown
- char success = msg.readInt8();
- char reason = msg.readInt8();
- if (success != SKILL_FAILED && bskill == BSKILL_EMOTE)
+ int skillId = msg.readInt16();
+ auto btype = msg.readInt16();
+ msg.readInt16(); // zero1
+ msg.readInt8(); // zero2
+ auto type = msg.readInt8();
+ if (btype == BSKILL_EMOTE)
{
- logger->log("Action: %d/%d", bskill, success);
+ logger->log("Action: %d", btype);
}
std::string msg;
- if (success == SKILL_FAILED && skillId == SKILL_BASIC)
+ if (skillId == SKILL_BASIC)
{
- switch (bskill)
+ switch (btype)
{
case BSKILL_TRADE:
msg = _("Trade failed!");
@@ -161,7 +159,7 @@ void SpecialHandler::handleMessage(MessageIn &msg)
msg += " ";
- switch (reason)
+ switch (type)
{
case RFAIL_SKILLDEP:
msg += _("You have not yet reached a high enough lvl!");
@@ -219,19 +217,19 @@ void SpecialHandler::handleMessage(MessageIn &msg)
}
}
-void SpecialHandler::use(int id)
+void AbilityHandler::use(int id)
{
}
-void SpecialHandler::use(int id, int level, int beingId)
+void AbilityHandler::useOn(int id, int beingId)
{
}
-void SpecialHandler::use(int id, int level, int x, int y)
+void AbilityHandler::useAt(int id, int x, int y)
{
}
-void SpecialHandler::use(int id, const std::string &map)
+void AbilityHandler::useInDirection(int id, int direction)
{
}
diff --git a/src/net/tmwa/specialhandler.h b/src/net/tmwa/abilityhandler.h
index 09b6ce8d..171f7d41 100644
--- a/src/net/tmwa/specialhandler.h
+++ b/src/net/tmwa/abilityhandler.h
@@ -19,32 +19,29 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_TA_SKILLHANDLER_H
-#define NET_TA_SKILLHANDLER_H
+#pragma once
#include "net/net.h"
-#include "net/specialhandler.h"
+#include "net/abilityhandler.h"
#include "net/tmwa/messagehandler.h"
namespace TmwAthena {
-class SpecialHandler final : public MessageHandler, public Net::SpecialHandler
+class AbilityHandler final : public MessageHandler, public Net::AbilityHandler
{
public:
- SpecialHandler();
+ AbilityHandler();
void handleMessage(MessageIn &msg) override;
void use(int id) override;
- void use(int id, int level, int beingId) override;
+ void useOn(int id, int beingId) override;
- void use(int id, int level, int x, int y) override;
+ void useAt(int id, int x, int y) override;
- void use(int id, const std::string &map) override;
+ void useInDirection(int id, int direction) override;
};
} // namespace TmwAthena
-
-#endif // NET_TA_SKILLHANDLER_H
diff --git a/src/net/tmwa/adminhandler.cpp b/src/net/tmwa/adminhandler.cpp
index c60050bd..4c4ecdad 100644
--- a/src/net/tmwa/adminhandler.cpp
+++ b/src/net/tmwa/adminhandler.cpp
@@ -68,10 +68,7 @@ void AdminHandler::handleMessage(MessageIn &msg)
id = msg.readInt32();
int ip = msg.readInt32();
if (Being *player = actorSpriteManager->findBeing(id))
- {
player->setIp(ip);
- player->updateName();
- }
break;
}
}
diff --git a/src/net/tmwa/adminhandler.h b/src/net/tmwa/adminhandler.h
index 17d547a8..6a233823 100644
--- a/src/net/tmwa/adminhandler.h
+++ b/src/net/tmwa/adminhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_TA_ADMINHANDLER_H
-#define NET_TA_ADMINHANDLER_H
+#pragma once
#include "net/adminhandler.h"
#include "net/net.h"
@@ -44,5 +43,3 @@ class AdminHandler final : public MessageHandler, public Net::AdminHandler
};
} // namespace TmwAthena
-
-#endif // NET_TA_ADMINHANDLER_H
diff --git a/src/net/tmwa/beinghandler.cpp b/src/net/tmwa/beinghandler.cpp
index e2c4f158..c5979e9f 100644
--- a/src/net/tmwa/beinghandler.cpp
+++ b/src/net/tmwa/beinghandler.cpp
@@ -40,6 +40,7 @@
#include "resources/emotedb.h"
#include "resources/hairdb.h"
+#include "resources/statuseffectdb.h"
#include <cmath>
@@ -78,29 +79,51 @@ BeingHandler::BeingHandler(bool enableSync):
handledMessages = _messages;
}
-static Being *createBeing(int id, short job)
+static ActorSprite::Type typeFromJob(short job)
{
- ActorSprite::Type type = ActorSprite::UNKNOWN;
if (job <= 25 || (job >= 4001 && job <= 4049))
- type = ActorSprite::PLAYER;
- else if (job >= 46 && job <= 1000)
- type = ActorSprite::NPC;
- else if (job > 1000 && job <= 2000)
- type = ActorSprite::MONSTER;
- else if (job == 45)
+ return ActorSprite::PLAYER;
+ if (job >= 46 && job <= 1000)
+ return ActorSprite::NPC;
+ if (job > 1000 && job <= 2000)
+ return ActorSprite::MONSTER;
+ if (job == 45)
+ return ActorSprite::PORTAL;
+
+ return ActorSprite::UNKNOWN;
+}
+
+static Being *createBeing(int id, short job)
+{
+ const auto type = typeFromJob(job);
+ if (type == ActorSprite::PORTAL)
return nullptr; // Skip portals
Being *being = actorSpriteManager->createBeing(id, type, job);
if (type == ActorSprite::PLAYER || type == ActorSprite::NPC)
{
- MessageOut outMsg(0x0094);
- outMsg.writeInt32(id);//readLong(2));
+ MessageOut outMsg(CMSG_NAME_REQUEST);
+ outMsg.writeInt32(id);
}
return being;
}
+static void updateBeingType(Being *being, short job)
+{
+ const auto type = typeFromJob(job);
+ const bool typeChanged = being->getType() != type;
+
+ being->setType(type, job);
+
+ if (typeChanged && type == ActorSprite::PLAYER)
+ {
+ MessageOut outMsg(CMSG_NAME_REQUEST);
+ outMsg.writeInt32(being->getId());
+ }
+}
+
static void handleMoveMessage(Map *map, Being *dstBeing,
Uint16 srcX, Uint16 srcY,
Uint16 dstX, Uint16 dstY)
@@ -146,6 +169,42 @@ static void handlePosMessage(Map *map, Being *dstBeing, Uint16 x, Uint16 y,
}
}
+static void applyStatusEffectsByOption1(Being *being,
+ const StatusEffectDB::OptionsMap &map,
+ uint16_t option)
+{
+ for (auto &[opt, id] : map)
+ being->setStatusEffect(id, option == opt);
+}
+
+static void applyStatusEffectsByOption(Being *being,
+ const StatusEffectDB::OptionsMap &map,
+ uint16_t option)
+{
+ for (auto &[opt, id] : map)
+ {
+ const bool enabled = (option & opt) != 0;
+ being->setStatusEffect(id, enabled);
+ }
+}
+
+/**
+ * Maps flags or indexes to their corresponding status effect index and
+ * updates the state of the given being. This is tmwAthena-specific.
+ */
+static void applyStatusEffects(Being *being,
+ uint16_t opt0,
+ uint16_t opt1,
+ uint16_t opt2,
+ std::optional<uint16_t> opt3 = {})
+{
+ applyStatusEffectsByOption(being, StatusEffectDB::opt0ToIdMap(), opt0);
+ applyStatusEffectsByOption1(being, StatusEffectDB::opt1ToIdMap(), opt1);
+ applyStatusEffectsByOption(being, StatusEffectDB::opt2ToIdMap(), opt2);
+ if (opt3)
+ applyStatusEffectsByOption(being, StatusEffectDB::opt3ToIdMap(), *opt3);
+}
+
void BeingHandler::handleMessage(MessageIn &msg)
{
if (!actorSpriteManager)
@@ -159,8 +218,10 @@ void BeingHandler::handleMessage(MessageIn &msg)
Uint16 weapon, shield;
Uint16 gmstatus;
int param1;
- int stunMode;
- Uint32 statusEffects;
+ uint16_t opt0;
+ uint16_t opt1;
+ uint16_t opt2;
+ uint16_t opt3;
int type, guild;
Uint16 status;
Being *srcBeing, *dstBeing;
@@ -176,9 +237,9 @@ void BeingHandler::handleMessage(MessageIn &msg)
// Information about a being in range
id = msg.readInt32();
speed = (float)msg.readInt16();
- stunMode = msg.readInt16(); // opt1
- statusEffects = msg.readInt16(); // opt2
- statusEffects |= ((Uint32)msg.readInt16()) << 16; // option
+ opt1 = msg.readInt16();
+ opt2 = msg.readInt16();
+ opt0 = msg.readInt16();
job = msg.readInt16(); // class
dstBeing = actorSpriteManager->findBeing(id);
@@ -209,7 +270,7 @@ void BeingHandler::handleMessage(MessageIn &msg)
speed = 150.0f; // In ticks per tile * 10
dstBeing->setMoveSpeed(Vector(speed / 10, speed / 10));
- dstBeing->setSubtype(job);
+ updateBeingType(dstBeing, job);
hairStyle = msg.readInt16();
weapon = msg.readInt16();
headBottom = msg.readInt16();
@@ -236,14 +297,14 @@ void BeingHandler::handleMessage(MessageIn &msg)
}
msg.readInt16(); // guild emblem
msg.readInt16(); // manner
- dstBeing->setStatusEffectBlock(32, msg.readInt16()); // opt3
+ opt3 = msg.readInt16();
msg.readInt8(); // karma
gender = msg.readInt8();
if (dstBeing->getType() == ActorSprite::PLAYER)
{
- dstBeing->setGender(gender == 0 ? Gender::FEMALE
- : Gender::MALE);
+ dstBeing->setGender(gender == 0 ? Gender::Female
+ : Gender::Male);
// Set these after the gender, as the sprites may be gender-specific
dstBeing->setSprite(SPRITE_HAIR, hairStyle * -1,
hairDB.getHairColor(hairColor));
@@ -274,9 +335,7 @@ void BeingHandler::handleMessage(MessageIn &msg)
msg.readInt8(); // unknown
msg.readInt8(); // unknown / sit
- dstBeing->setStunMode(stunMode);
- dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff);
- dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff);
+ applyStatusEffects(dstBeing, opt0, opt1, opt2, opt3);
break;
case SMSG_BEING_SPAWN:
@@ -417,6 +476,9 @@ void BeingHandler::handleMessage(MessageIn &msg)
switch (type)
{
+ case LOOK::BASE:
+ updateBeingType(dstBeing, id);
+ break;
case LOOK::HAIR:
{
// const int look = id / 256;
@@ -505,10 +567,9 @@ void BeingHandler::handleMessage(MessageIn &msg)
// An update about a player, potentially including movement.
id = msg.readInt32();
speed = msg.readInt16();
- stunMode = msg.readInt16(); // opt1; Aethyra use this as cape
- statusEffects = msg.readInt16(); // opt2; Aethyra use this as misc1
- statusEffects |= ((Uint32) msg.readInt16())
- << 16; // status.options; Aethyra uses this as misc2
+ opt1 = msg.readInt16();
+ opt2 = msg.readInt16();
+ opt0 = msg.readInt16();
job = msg.readInt16();
dstBeing = actorSpriteManager->findBeing(id);
@@ -531,7 +592,7 @@ void BeingHandler::handleMessage(MessageIn &msg)
else
dstBeing->setMoveSpeed(Net::getPlayerHandler()->getDefaultMoveSpeed());
- dstBeing->setSubtype(job);
+ updateBeingType(dstBeing, job);
hairStyle = msg.readInt16();
weapon = msg.readInt16();
shield = msg.readInt16();
@@ -545,15 +606,16 @@ void BeingHandler::handleMessage(MessageIn &msg)
headTop = msg.readInt16();
headMid = msg.readInt16();
hairColor = msg.readInt16();
- shoes = msg.readInt16();
- gloves = msg.readInt16();
+ msg.readInt16(); // clothes_color
+ msg.readInt8(); // head_dir
+ msg.readInt8(); // unused2
msg.readInt32(); // guild
msg.readInt16(); // emblem
msg.readInt16(); // manner
- dstBeing->setStatusEffectBlock(32, msg.readInt16()); // opt3
+ opt3 = msg.readInt16();
msg.readInt8(); // karma
- dstBeing->setGender(msg.readInt8() == 0 ? Gender::FEMALE
- : Gender::MALE);
+ dstBeing->setGender(msg.readInt8() == 0 ? Gender::Female
+ : Gender::Male);
// Set these after the gender, as the sprites may be gender-specific
dstBeing->setSprite(SPRITE_WEAPON, weapon, "", true);
dstBeing->setSprite(SPRITE_SHIELD, shield);
@@ -601,15 +663,13 @@ void BeingHandler::handleMessage(MessageIn &msg)
}
else if (msg.getId() == SMSG_PLAYER_MOVE)
{
- msg.readInt8(); // unknown
+ msg.readInt8(); // five
}
msg.readInt8(); // Lv
- msg.readInt8(); // unknown
+ msg.readInt8(); // unused
- dstBeing->setStunMode(stunMode);
- dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff);
- dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff);
+ applyStatusEffects(dstBeing, opt0, opt1, opt2, opt3);
break;
case SMSG_PLAYER_STOP:
@@ -654,14 +714,12 @@ void BeingHandler::handleMessage(MessageIn &msg)
if (!dstBeing)
break;
- stunMode = msg.readInt16();
- statusEffects = msg.readInt16();
- statusEffects |= ((Uint32) msg.readInt16()) << 16;
- msg.readInt8(); // Unused?
+ opt1 = msg.readInt16();
+ opt2 = msg.readInt16();
+ opt0 = msg.readInt16();
+ msg.readInt8(); // zero
- dstBeing->setStunMode(stunMode);
- dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff);
- dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff);
+ applyStatusEffects(dstBeing, opt0, opt1, opt2);
break;
case SMSG_BEING_STATUS_CHANGE:
diff --git a/src/net/tmwa/beinghandler.h b/src/net/tmwa/beinghandler.h
index f1c8887f..fd8f08f5 100644
--- a/src/net/tmwa/beinghandler.h
+++ b/src/net/tmwa/beinghandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_TA_BEINGHANDLER_H
-#define NET_TA_BEINGHANDLER_H
+#pragma once
#include "net/tmwa/messagehandler.h"
@@ -39,5 +38,3 @@ class BeingHandler final : public MessageHandler
};
} // namespace TmwAthena
-
-#endif // NET_TA_BEINGHANDLER_H
diff --git a/src/net/tmwa/buysellhandler.h b/src/net/tmwa/buysellhandler.h
index 8f7a324f..cad9f943 100644
--- a/src/net/tmwa/buysellhandler.h
+++ b/src/net/tmwa/buysellhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_TA_BUYSELLHANDLER_H
-#define NET_TA_BUYSELLHANDLER_H
+#pragma once
#include "net/tmwa/messagehandler.h"
@@ -41,5 +40,3 @@ class BuySellHandler final : public MessageHandler
};
} // namespace TmwAthena
-
-#endif // NET_TA_BUYSELLHANDLER_H
diff --git a/src/net/tmwa/charserverhandler.cpp b/src/net/tmwa/charserverhandler.cpp
index 636b58ce..0ecbb135 100644
--- a/src/net/tmwa/charserverhandler.cpp
+++ b/src/net/tmwa/charserverhandler.cpp
@@ -28,7 +28,6 @@
#include "gui/charcreatedialog.h"
#include "gui/okdialog.h"
-#include "net/logindata.h"
#include "net/net.h"
#include "net/tmwa/gamehandler.h"
@@ -210,21 +209,20 @@ void CharServerHandler::readPlayerData(MessageIn &msg, Net::Character *character
const Token &token =
static_cast<LoginHandler*>(Net::getLoginHandler())->getToken();
- auto *tempPlayer = new LocalPlayer(msg.readInt32(), 0);
- tempPlayer->setGender(token.sex);
+ const int id = msg.readInt32();
character->data.mAttributes[EXP] = msg.readInt32();
character->data.mAttributes[MONEY] = msg.readInt32();
character->data.mStats[JOB].exp = msg.readInt32();
- int temp = msg.readInt32();
+ const int temp = msg.readInt32();
character->data.mStats[JOB].base = temp;
character->data.mStats[JOB].mod = temp;
- tempPlayer->setSprite(SPRITE_SHOE, msg.readInt16());
- tempPlayer->setSprite(SPRITE_GLOVES, msg.readInt16());
- tempPlayer->setSprite(SPRITE_CAPE, msg.readInt16());
- tempPlayer->setSprite(SPRITE_MISC1, msg.readInt16());
+ const int shoe = msg.readInt16();
+ const int gloves = msg.readInt16();
+ const int cape = msg.readInt16();
+ const int misc1 = msg.readInt16();
msg.readInt32(); // option
msg.readInt32(); // karma
@@ -240,8 +238,15 @@ void CharServerHandler::readPlayerData(MessageIn &msg, Net::Character *character
const uint16_t race = msg.readInt16(); // class (used for race)
int hairStyle = msg.readInt8();
msg.readInt8(); // look
- tempPlayer->setSubtype(race);
- Uint16 weapon = msg.readInt16();
+ const uint16_t weapon = msg.readInt16();
+
+ auto *tempPlayer = new LocalPlayer(id, race);
+ tempPlayer->setGender(token.sex);
+
+ tempPlayer->setSprite(SPRITE_SHOE, shoe);
+ tempPlayer->setSprite(SPRITE_GLOVES, gloves);
+ tempPlayer->setSprite(SPRITE_CAPE, cape);
+ tempPlayer->setSprite(SPRITE_MISC1, misc1);
tempPlayer->setSprite(SPRITE_WEAPON, weapon, "", true);
character->data.mAttributes[LEVEL] = msg.readInt16();
@@ -263,7 +268,7 @@ void CharServerHandler::readPlayerData(MessageIn &msg, Net::Character *character
character->slot = msg.readInt8(); // character slot
const uint8_t sex = msg.readInt8();
- tempPlayer->setGender(sex ? Gender::MALE : Gender::FEMALE);
+ tempPlayer->setGender(sex ? Gender::Male : Gender::Female);
}
void CharServerHandler::setCharSelectDialog(CharSelectDialog *window)
@@ -395,7 +400,7 @@ void CharServerHandler::connect()
// [Fate] The next word is unused by the old char server, so we squeeze in
// mana client version information
outMsg.writeInt16(CLIENT_PROTOCOL_VERSION);
- outMsg.writeInt8(token.sex == Gender::MALE ? 1 : 0);
+ outMsg.writeInt8(token.sex == Gender::Male ? 1 : 0);
// We get 4 useless bytes before the real answer comes in (what are these?)
mNetwork->skip(4);
diff --git a/src/net/tmwa/charserverhandler.h b/src/net/tmwa/charserverhandler.h
index 9633c8d9..b0d3e970 100644
--- a/src/net/tmwa/charserverhandler.h
+++ b/src/net/tmwa/charserverhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_TA_CHARSERVERHANDLER_H
-#define NET_TA_CHARSERVERHANDLER_H
+#pragma once
#include "net/charhandler.h"
@@ -75,9 +74,7 @@ class CharServerHandler final : public MessageHandler, public Net::CharHandler
void connect();
private:
- void readPlayerData(MessageIn &msg, Net::Character *character);
+ static void readPlayerData(MessageIn &msg, Net::Character *character);
};
} // namespace TmwAthena
-
-#endif // NET_TA_CHARSERVERHANDLER_H
diff --git a/src/net/tmwa/chathandler.cpp b/src/net/tmwa/chathandler.cpp
index 4f998922..f9061f63 100644
--- a/src/net/tmwa/chathandler.cpp
+++ b/src/net/tmwa/chathandler.cpp
@@ -271,7 +271,7 @@ void ChatHandler::handleMessage(MessageIn &msg)
msg.readInt8(); // gm level
msg.readInt8(); // gender
- Avatar *avatar = new Avatar(nick);
+ auto *avatar = new Avatar(nick);
avatar->setOnline(true);
players.push_back(avatar);
}
diff --git a/src/net/tmwa/chathandler.h b/src/net/tmwa/chathandler.h
index d545b221..b16b3dc4 100644
--- a/src/net/tmwa/chathandler.h
+++ b/src/net/tmwa/chathandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_TA_CHATHANDLER_H
-#define NET_TA_CHATHANDLER_H
+#pragma once
#include "net/chathandler.h"
#include "net/net.h"
@@ -73,5 +72,3 @@ class ChatHandler final : public MessageHandler, public Net::ChatHandler
};
} // namespace TmwAthena
-
-#endif // NET_TA_CHATHANDLER_H
diff --git a/src/net/tmwa/gamehandler.cpp b/src/net/tmwa/gamehandler.cpp
index abc9c73c..0a3bb9d9 100644
--- a/src/net/tmwa/gamehandler.cpp
+++ b/src/net/tmwa/gamehandler.cpp
@@ -140,7 +140,7 @@ void GameHandler::connect()
outMsg.writeInt32(mCharID);
outMsg.writeInt32(token.session_ID1);
outMsg.writeInt32(token.session_ID2);
- outMsg.writeInt8(token.sex == Gender::MALE ? 1 : 0);
+ outMsg.writeInt8(token.sex == Gender::Male ? 1 : 0);
// We get 4 useless bytes before the real answer comes in (what are these?)
mNetwork->skip(4);
diff --git a/src/net/tmwa/gamehandler.h b/src/net/tmwa/gamehandler.h
index ecd8df2d..2f876b82 100644
--- a/src/net/tmwa/gamehandler.h
+++ b/src/net/tmwa/gamehandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_TA_MAPHANDLER_H
-#define NET_TA_MAPHANDLER_H
+#pragma once
#include "eventlistener.h"
@@ -74,5 +73,3 @@ class GameHandler final : public MessageHandler, public Net::GameHandler,
};
} // namespace TmwAthena
-
-#endif // NET_TA_MAPHANDLER_H
diff --git a/src/net/tmwa/generalhandler.cpp b/src/net/tmwa/generalhandler.cpp
index e1b1a9ea..d6eb3b34 100644
--- a/src/net/tmwa/generalhandler.cpp
+++ b/src/net/tmwa/generalhandler.cpp
@@ -25,7 +25,6 @@
#include "configuration.h"
#include "log.h"
-#include "gui/inventorywindow.h"
#include "gui/skilldialog.h"
#include "gui/socialwindow.h"
#include "gui/statuswindow.h"
@@ -49,7 +48,7 @@
#include "net/tmwa/playerhandler.h"
#include "net/tmwa/protocol.h"
#include "net/tmwa/tradehandler.h"
-#include "net/tmwa/specialhandler.h"
+#include "net/tmwa/abilityhandler.h"
#include "net/tmwa/gui/guildtab.h"
#include "net/tmwa/gui/partytab.h"
@@ -60,8 +59,6 @@
#include <list>
-extern Net::GeneralHandler *generalHandler;
-
namespace TmwAthena {
ServerInfo charServer;
@@ -84,7 +81,7 @@ GeneralHandler::GeneralHandler():
mNpcHandler(new NpcHandler),
mPartyHandler(new PartyHandler),
mPlayerHandler(new PlayerHandler),
- mSpecialHandler(new SpecialHandler),
+ mAbilityHandler(new AbilityHandler),
mTradeHandler(new TradeHandler)
{
static const Uint16 _messages[] = {
@@ -92,7 +89,6 @@ GeneralHandler::GeneralHandler():
0
};
handledMessages = _messages;
- generalHandler = this;
std::list<ItemStat> stats;
stats.emplace_back("str", _("Strength %+d"));
@@ -155,6 +151,7 @@ void GeneralHandler::handleMessage(MessageIn &msg)
void GeneralHandler::load()
{
+ // This sets mNetwork to the created Network instance
(new Network)->registerHandler(this);
mNetwork->registerHandler(mAdminHandler.get());
@@ -169,7 +166,7 @@ void GeneralHandler::load()
mNetwork->registerHandler(mLoginHandler.get());
mNetwork->registerHandler(mNpcHandler.get());
mNetwork->registerHandler(mPlayerHandler.get());
- mNetwork->registerHandler(mSpecialHandler.get());
+ mNetwork->registerHandler(mAbilityHandler.get());
mNetwork->registerHandler(mTradeHandler.get());
mNetwork->registerHandler(mPartyHandler.get());
}
@@ -209,11 +206,6 @@ void GeneralHandler::flushNetwork()
}
}
-void GeneralHandler::clearHandlers()
-{
- mNetwork->clearHandlers();
-}
-
void GeneralHandler::event(Event::Channel channel,
const Event &event)
{
@@ -221,7 +213,6 @@ void GeneralHandler::event(Event::Channel channel,
{
if (event.getType() == Event::GuiWindowsLoaded)
{
- inventoryWindow->setSplitAllowed(false);
skillDialog->loadSkills();
statusWindow->addAttribute(STRENGTH, _("Strength"), true, "");
diff --git a/src/net/tmwa/generalhandler.h b/src/net/tmwa/generalhandler.h
index f105da96..1da81ba8 100644
--- a/src/net/tmwa/generalhandler.h
+++ b/src/net/tmwa/generalhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_TMWA_GENERALHANDLER_H
-#define NET_TMWA_GENERALHANDLER_H
+#pragma once
#include "eventlistener.h"
@@ -49,8 +48,6 @@ class GeneralHandler final : public MessageHandler, public Net::GeneralHandler,
void flushNetwork() override;
- void clearHandlers() override;
-
void event(Event::Channel channel, const Event &event) override;
protected:
@@ -67,10 +64,8 @@ class GeneralHandler final : public MessageHandler, public Net::GeneralHandler,
MessageHandlerPtr mNpcHandler;
MessageHandlerPtr mPartyHandler;
MessageHandlerPtr mPlayerHandler;
- MessageHandlerPtr mSpecialHandler;
+ MessageHandlerPtr mAbilityHandler;
MessageHandlerPtr mTradeHandler;
};
} // namespace TmwAthena
-
-#endif // NET_TMWA_GENERALHANDLER_H
diff --git a/src/net/tmwa/gui/guildtab.h b/src/net/tmwa/gui/guildtab.h
index 3f864748..c51e63d0 100644
--- a/src/net/tmwa/gui/guildtab.h
+++ b/src/net/tmwa/gui/guildtab.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef TA_GUILDTAB_H
-#define TA_GUILDTAB_H
+#pragma once
#include "gui/widgets/chattab.h"
@@ -49,5 +48,3 @@ class GuildTab : public ChatTab
extern GuildTab *guildTab;
} // namespace TmwAthena
-
-#endif // TA_GUILDTAB_H
diff --git a/src/net/tmwa/gui/partytab.h b/src/net/tmwa/gui/partytab.h
index a6c52f58..39a1f978 100644
--- a/src/net/tmwa/gui/partytab.h
+++ b/src/net/tmwa/gui/partytab.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef TA_PARTYTAB_H
-#define TA_PARTYTAB_H
+#pragma once
#include "gui/widgets/chattab.h"
@@ -49,5 +48,3 @@ class PartyTab : public ChatTab
extern PartyTab *partyTab;
} // namespace TmwAthena
-
-#endif // TA_PARTYTAB_H
diff --git a/src/net/tmwa/guildhandler.cpp b/src/net/tmwa/guildhandler.cpp
index 1877f0f1..e8ee4848 100644
--- a/src/net/tmwa/guildhandler.cpp
+++ b/src/net/tmwa/guildhandler.cpp
@@ -40,11 +40,6 @@ Guild *taGuild;
GuildHandler::GuildHandler()
{
- static const Uint16 _messages[] = {
- 0
- };
- handledMessages = _messages;
-
guildHandler = this;
}
diff --git a/src/net/tmwa/guildhandler.h b/src/net/tmwa/guildhandler.h
index 9bbe9b4a..6faca0cc 100644
--- a/src/net/tmwa/guildhandler.h
+++ b/src/net/tmwa/guildhandler.h
@@ -18,8 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_TA_GUILDHANDLER_H
-#define NET_TA_GUILDHANDLER_H
+#pragma once
#include "net/guildhandler.h"
@@ -67,5 +66,3 @@ class GuildHandler final : public Net::GuildHandler, public MessageHandler
};
}
-
-#endif // NET_TA_GUILDHANDLER_H
diff --git a/src/net/tmwa/inventoryhandler.cpp b/src/net/tmwa/inventoryhandler.cpp
index 496ce621..0d8e3005 100644
--- a/src/net/tmwa/inventoryhandler.cpp
+++ b/src/net/tmwa/inventoryhandler.cpp
@@ -521,11 +521,6 @@ void InventoryHandler::event(Event::Channel channel,
}
}
-bool InventoryHandler::canSplit(const Item *item)
-{
- return false;
-}
-
size_t InventoryHandler::getSize(int type) const
{
switch (type)
diff --git a/src/net/tmwa/inventoryhandler.h b/src/net/tmwa/inventoryhandler.h
index 51a0fe51..47226bea 100644
--- a/src/net/tmwa/inventoryhandler.h
+++ b/src/net/tmwa/inventoryhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_TA_INVENTORYHANDLER_H
-#define NET_TA_INVENTORYHANDLER_H
+#pragma once
#include "eventlistener.h"
#include "inventory.h"
@@ -38,7 +37,7 @@
#include "utils/gettext.h"
-#include <list>
+#include <vector>
namespace TmwAthena {
@@ -166,8 +165,6 @@ class InventoryHandler final : public MessageHandler, public Net::InventoryHandl
void event(Event::Channel channel, const Event &event) override;
- bool canSplit(const Item *item) override;
-
size_t getSize(int type) const override;
// Note the slot type id is equal to the slot Index for tA.
@@ -195,5 +192,3 @@ class InventoryHandler final : public MessageHandler, public Net::InventoryHandl
};
} // namespace TmwAthena
-
-#endif // NET_TA_INVENTORYHANDLER_H
diff --git a/src/net/tmwa/itemhandler.h b/src/net/tmwa/itemhandler.h
index 0c6175d8..5e42e0d8 100644
--- a/src/net/tmwa/itemhandler.h
+++ b/src/net/tmwa/itemhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_TA_ITEMHANDLER_H
-#define NET_TA_ITEMHANDLER_H
+#pragma once
#include "net/tmwa/messagehandler.h"
@@ -35,5 +34,3 @@ class ItemHandler final : public MessageHandler
};
} // namespace TmwAthena
-
-#endif // NET_TA_ITEMHANDLER_H
diff --git a/src/net/tmwa/loginhandler.cpp b/src/net/tmwa/loginhandler.cpp
index 24bc9ab2..a7162ee6 100644
--- a/src/net/tmwa/loginhandler.cpp
+++ b/src/net/tmwa/loginhandler.cpp
@@ -103,17 +103,15 @@ void LoginHandler::handleMessage(MessageIn &msg)
}
break;
- case SMSG_UPDATE_HOST:
- int len;
-
- len = msg.readInt16() - 4;
+ case SMSG_UPDATE_HOST: {
+ int len = msg.readInt16() - 4;
mUpdateHost = msg.readString(len);
loginData.updateHost = mUpdateHost;
logger->log("Received update host \"%s\" from login server.",
mUpdateHost.c_str());
break;
-
+ }
case SMSG_LOGIN_DATA:
// Skip the length word
msg.skip(2);
@@ -126,7 +124,7 @@ void LoginHandler::handleMessage(MessageIn &msg)
mToken.account_ID = msg.readInt32();
mToken.session_ID2 = msg.readInt32();
msg.skip(30); // unused
- mToken.sex = msg.readInt8() ? Gender::MALE : Gender::FEMALE;
+ mToken.sex = msg.readInt8() ? Gender::Male : Gender::Female;
for (int i = 0; i < worldCount; i++)
{
@@ -306,7 +304,7 @@ void LoginHandler::chooseServer(unsigned int server)
void LoginHandler::registerAccount(LoginData *loginData)
{
std::string username = loginData->username;
- username.append(loginData->gender == Gender::FEMALE ? "_F" : "_M");
+ username.append(loginData->gender == Gender::Female ? "_F" : "_M");
sendLoginRegister(username, loginData->password);
}
diff --git a/src/net/tmwa/loginhandler.h b/src/net/tmwa/loginhandler.h
index 25dd414d..f6b8a530 100644
--- a/src/net/tmwa/loginhandler.h
+++ b/src/net/tmwa/loginhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_TMWA_LOGINHANDLER_H
-#define NET_TMWA_LOGINHANDLER_H
+#pragma once
#include "net/loginhandler.h"
@@ -94,5 +93,3 @@ class LoginHandler final : public MessageHandler, public Net::LoginHandler
};
} // namespace TmwAthena
-
-#endif // NET_TA_LOGINHANDLER_H
diff --git a/src/net/tmwa/messagehandler.h b/src/net/tmwa/messagehandler.h
index 0d278c15..8e3c6a19 100644
--- a/src/net/tmwa/messagehandler.h
+++ b/src/net/tmwa/messagehandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_TA_MESSAGEHANDLER_H
-#define NET_TA_MESSAGEHANDLER_H
+#pragma once
#include "net/messagehandler.h"
@@ -52,5 +51,3 @@ class MessageHandler : public Net::MessageHandler
using MessageHandlerPtr = const std::unique_ptr<MessageHandler>;
}
-
-#endif // NET_TA_MESSAGEHANDLER_H
diff --git a/src/net/tmwa/messagein.cpp b/src/net/tmwa/messagein.cpp
index 7c142619..c0db0fca 100644
--- a/src/net/tmwa/messagein.cpp
+++ b/src/net/tmwa/messagein.cpp
@@ -21,6 +21,9 @@
#include "net/tmwa/messagein.h"
+#include "being.h"
+#include "net/tmwa/protocol.h"
+
#include <SDL_endian.h>
#define MAKEWORD(low,high) \
@@ -88,38 +91,20 @@ void MessageIn::readCoordinates(uint16_t &x, uint16_t &y, uint8_t &direction)
direction = data[2] & 0x000f;
// Translate from tmwAthena format
- switch (direction)
+ switch (static_cast<DIR>(direction))
{
- case 0:
- direction = 1;
- break;
- case 1:
- direction = 3;
- break;
- case 2:
- direction = 2;
- break;
- case 3:
- direction = 6;
- break;
- case 4:
- direction = 4;
- break;
- case 5:
- direction = 12;
- break;
- case 6:
- direction = 8;
- break;
- case 7:
- direction = 9;
- break;
- case 8:
- direction = 8;
- break;
+ case DIR::S: direction = Being::DOWN; break;
+ case DIR::SW: direction = Being::DOWN | Being::LEFT; break;
+ case DIR::W: direction = Being::LEFT; break;
+ case DIR::NW: direction = Being::UP | Being::LEFT; break;
+ case DIR::N: direction = Being::UP; break;
+ case DIR::NE: direction = Being::UP | Being::RIGHT; break;
+ case DIR::E: direction = Being::RIGHT; break;
+ case DIR::SE: direction = Being::DOWN | Being::RIGHT; break;
default:
// OOPSIE! Impossible or unknown
direction = 0;
+ break;
}
}
mPos += 3;
@@ -162,7 +147,7 @@ std::string MessageIn::readString(int length)
if (length < 0 || mPos + length > mLength)
{
mPos = mLength + 1;
- return "";
+ return std::string();
}
// Read the string
diff --git a/src/net/tmwa/messagein.h b/src/net/tmwa/messagein.h
index 2f66ca28..b2fb6716 100644
--- a/src/net/tmwa/messagein.h
+++ b/src/net/tmwa/messagein.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_TA_MESSAGEIN_H
-#define NET_TA_MESSAGEIN_H
+#pragma once
#include <cstdint>
#include <string>
@@ -109,5 +108,3 @@ class MessageIn
};
} // TmwAthena
-
-#endif // NET_TA_MESSAGEIN_H
diff --git a/src/net/tmwa/messageout.h b/src/net/tmwa/messageout.h
index b60644de..ba2d3c75 100644
--- a/src/net/tmwa/messageout.h
+++ b/src/net/tmwa/messageout.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_TA_MESSAGEOUT_H
-#define NET_TA_MESSAGEOUT_H
+#pragma once
#include <cstdint>
#include <string>
@@ -73,5 +72,3 @@ class MessageOut
};
} // namespace TmwAthena
-
-#endif // NET_TA_MESSAGEOUT_H
diff --git a/src/net/tmwa/network.cpp b/src/net/tmwa/network.cpp
index 5a2dd0d0..353495da 100644
--- a/src/net/tmwa/network.cpp
+++ b/src/net/tmwa/network.cpp
@@ -331,20 +331,16 @@ void Network::disconnect()
void Network::registerHandler(MessageHandler *handler)
{
- for (const Uint16 *i = handler->handledMessages; *i; ++i)
- {
+ for (const uint16_t *i = handler->handledMessages; *i; ++i)
mMessageHandlers[*i] = handler;
- }
handler->setNetwork(this);
}
void Network::unregisterHandler(MessageHandler *handler)
{
- for (const Uint16 *i = handler->handledMessages; *i; ++i)
- {
+ for (const uint16_t *i = handler->handledMessages; *i; ++i)
mMessageHandlers.erase(*i);
- }
handler->setNetwork(nullptr);
}
@@ -352,9 +348,8 @@ void Network::unregisterHandler(MessageHandler *handler)
void Network::clearHandlers()
{
for (auto& [_, messageHandler] : mMessageHandlers)
- {
messageHandler->setNetwork(nullptr);
- }
+
mMessageHandlers.clear();
}
diff --git a/src/net/tmwa/network.h b/src/net/tmwa/network.h
index fa0237dd..53f15761 100644
--- a/src/net/tmwa/network.h
+++ b/src/net/tmwa/network.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_TA_NETWORK_H
-#define NET_TA_NETWORK_H
+#pragma once
#include "utils/mutex.h"
@@ -125,5 +124,3 @@ class Network
};
} // namespace TmwAthena
-
-#endif // NET_TA_NETWORK_H
diff --git a/src/net/tmwa/npchandler.h b/src/net/tmwa/npchandler.h
index b55a8155..df23531f 100644
--- a/src/net/tmwa/npchandler.h
+++ b/src/net/tmwa/npchandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_TA_NPCHANDLER_H
-#define NET_TA_NPCHANDLER_H
+#pragma once
#include "net/npchandler.h"
@@ -65,5 +64,3 @@ class NpcHandler final : public MessageHandler, public Net::NpcHandler
};
} // namespace TmwAthena
-
-#endif // NET_TA_NPCHANDLER_H
diff --git a/src/net/tmwa/partyhandler.h b/src/net/tmwa/partyhandler.h
index 14c6d9f3..3f99de20 100644
--- a/src/net/tmwa/partyhandler.h
+++ b/src/net/tmwa/partyhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_TA_PARTYHANDLER_H
-#define NET_TA_PARTYHANDLER_H
+#pragma once
#include "net/net.h"
#include "net/partyhandler.h"
@@ -71,5 +70,3 @@ class PartyHandler final : public MessageHandler, public Net::PartyHandler
};
} // namespace TmwAthena
-
-#endif // NET_TA_PARTYHANDLER_H
diff --git a/src/net/tmwa/playerhandler.h b/src/net/tmwa/playerhandler.h
index 1a90532e..f1a67e94 100644
--- a/src/net/tmwa/playerhandler.h
+++ b/src/net/tmwa/playerhandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_TA_PLAYERHANDLER_H
-#define NET_TA_PLAYERHANDLER_H
+#pragma once
#include "net/net.h"
#include "net/playerhandler.h"
@@ -67,5 +66,3 @@ class PlayerHandler final : public MessageHandler, public Net::PlayerHandler
};
} // namespace TmwAthena
-
-#endif // NET_TA_PLAYERHANDLER_H
diff --git a/src/net/tmwa/protocol.h b/src/net/tmwa/protocol.h
index 609b18a5..532ac90e 100644
--- a/src/net/tmwa/protocol.h
+++ b/src/net/tmwa/protocol.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef TA_PROTOCOL_H
-#define TA_PROTOCOL_H
+#pragma once
#include <cstdint>
@@ -80,6 +79,20 @@ enum class LOOK : uint8_t
MISC2 = 13,
};
+enum class DIR : uint8_t
+{
+ S = 0,
+ SW = 1,
+ W = 2,
+ NW = 3,
+ N = 4,
+ NE = 5,
+ E = 6,
+ SE = 7,
+
+ COUNT,
+};
+
enum NpcCommand
{
NPC_REQUEST_LANG = 0,
@@ -283,5 +296,3 @@ enum {
};
}
-
-#endif
diff --git a/src/net/tmwa/token.h b/src/net/tmwa/token.h
index c0a2b3c8..b563bf65 100644
--- a/src/net/tmwa/token.h
+++ b/src/net/tmwa/token.h
@@ -21,8 +21,7 @@
#include "being.h"
-#ifndef NET_TA_TOKEN_H
-#define NET_TA_TOKEN_H
+#pragma once
struct Token
{
@@ -36,8 +35,6 @@ struct Token
account_ID = 0;
session_ID1 = 0;
session_ID2 = 0;
- sex = Gender::UNSPECIFIED;
+ sex = Gender::Unspecified;
}
};
-
-#endif // NET_TA_TOKEN_H
diff --git a/src/net/tmwa/tradehandler.cpp b/src/net/tmwa/tradehandler.cpp
index 60732eef..c129cfd4 100644
--- a/src/net/tmwa/tradehandler.cpp
+++ b/src/net/tmwa/tradehandler.cpp
@@ -131,7 +131,7 @@ void TradeHandler::handleMessage(MessageIn &msg)
"doesn't exist."));
break;
case 2: // Invite request check failed...
- serverNotice(_("Trade cancelled due to an unknown "
+ serverNotice(_("Trade canceled due to an unknown "
"reason."));
break;
case 3: // Trade accepted
@@ -140,11 +140,11 @@ void TradeHandler::handleMessage(MessageIn &msg)
tradePartnerName.c_str()));
tradeWindow->setVisible(true);
break;
- case 4: // Trade cancelled
+ case 4: // Trade canceled
if (player_relations.hasPermission(tradePartnerName,
PlayerPermissions::SPEECH_LOG))
{
- serverNotice(strprintf(_("Trade with %s cancelled."),
+ serverNotice(strprintf(_("Trade with %s canceled."),
tradePartnerName.c_str()));
}
// otherwise ignore silently
diff --git a/src/net/tmwa/tradehandler.h b/src/net/tmwa/tradehandler.h
index fa7ec034..1a2cfcef 100644
--- a/src/net/tmwa/tradehandler.h
+++ b/src/net/tmwa/tradehandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NET_TA_TRADEHANDLER_H
-#define NET_TA_TRADEHANDLER_H
+#pragma once
#include "net/net.h"
#include "net/tradehandler.h"
@@ -57,5 +56,3 @@ class TradeHandler final : public MessageHandler, public Net::TradeHandler
};
} // namespace TmwAthena
-
-#endif // NET_TA_TRADEHANDLER_H
diff --git a/src/net/tradehandler.h b/src/net/tradehandler.h
index 609f8087..605d122b 100644
--- a/src/net/tradehandler.h
+++ b/src/net/tradehandler.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef TRADEHANDLER_H
-#define TRADEHANDLER_H
+#pragma once
#include "being.h"
@@ -49,5 +48,3 @@ class TradeHandler
virtual void cancel() {}
};
}
-
-#endif // TRADEHANDLER_H
diff --git a/src/net/worldinfo.h b/src/net/worldinfo.h
index d9596239..acc8a376 100644
--- a/src/net/worldinfo.h
+++ b/src/net/worldinfo.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef WORLD_INFO_H
-#define WORLD_INFO_H
+#pragma once
#include <string>
#include <vector>
@@ -34,5 +33,3 @@ struct WorldInfo {
};
using Worlds = std::vector<WorldInfo *>;
-
-#endif // WORLD_INFO_H
diff --git a/src/openglgraphics.cpp b/src/openglgraphics.cpp
index cb242ff7..51d8cde3 100644
--- a/src/openglgraphics.cpp
+++ b/src/openglgraphics.cpp
@@ -34,6 +34,8 @@
#include <SDL.h>
+#include <cmath>
+
#ifndef GL_TEXTURE_RECTANGLE_ARB
#define GL_TEXTURE_RECTANGLE_ARB 0x84F5
#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8
@@ -624,14 +626,6 @@ bool OpenGLGraphics::pushClipArea(gcn::Rectangle area)
glPushMatrix();
glTranslatef(transX, transY, 0);
- int x = (int) (mClipStack.top().x * mScaleX);
- int y = (int) ((mHeight - mClipStack.top().y -
- mClipStack.top().height) * mScaleY);
- int width = (int) (mClipStack.top().width * mScaleX);
- int height = (int) (mClipStack.top().height * mScaleY);
-
- glScissor(x, y, width, height);
-
return result;
}
@@ -640,17 +634,6 @@ void OpenGLGraphics::popClipArea()
Graphics::popClipArea();
glPopMatrix();
-
- if (mClipStack.empty())
- return;
-
- int x = (int) (mClipStack.top().x * mScaleX);
- int y = (int) ((mHeight - mClipStack.top().y -
- mClipStack.top().height) * mScaleY);
- int width = (int) (mClipStack.top().width * mScaleX);
- int height = (int) (mClipStack.top().height * mScaleY);
-
- glScissor(x, y, width, height);
}
void OpenGLGraphics::setColor(const gcn::Color &color)
@@ -661,6 +644,25 @@ void OpenGLGraphics::setColor(const gcn::Color &color)
mColorAlpha = (color.a != 255);
}
+void OpenGLGraphics::updateClipRect()
+{
+ if (mClipRects.empty())
+ {
+ glDisable(GL_SCISSOR_TEST);
+ return;
+ }
+
+ const gcn::Rectangle &clipRect = mClipRects.top();
+
+ const int x = (int) (clipRect.x * mScaleX);
+ const int y = (int) ((mHeight - clipRect.y - clipRect.height) * mScaleY);
+ const int width = (int) (clipRect.width * mScaleX);
+ const int height = (int) (clipRect.height * mScaleY);
+
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(x, y, width, height);
+}
+
void OpenGLGraphics::drawPoint(int x, int y)
{
setTexturingAndBlending(false);
diff --git a/src/openglgraphics.h b/src/openglgraphics.h
index 7178128b..b342d9e5 100644
--- a/src/openglgraphics.h
+++ b/src/openglgraphics.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef OPENGLGRAPHICS_H
-#define OPENGLGRAPHICS_H
+#pragma once
#ifdef USE_OPENGL
#include "graphics.h"
@@ -116,6 +115,8 @@ class OpenGLGraphics final : public Graphics
protected:
void setTexturingAndBlending(bool enable);
+ void updateClipRect() override;
+
private:
void drawQuadArrayfi(int size);
@@ -135,5 +136,3 @@ class OpenGLGraphics final : public Graphics
bool mReduceInputLag = true;
};
#endif //USE_OPENGL
-
-#endif
diff --git a/src/particle.cpp b/src/particle.cpp
index 754cc43b..cb79c86f 100644
--- a/src/particle.cpp
+++ b/src/particle.cpp
@@ -74,7 +74,6 @@ void Particle::setupEngine()
Particle::fastPhysics = config.particleFastPhysics;
Particle::emitterSkip = config.particleEmitterSkip + 1;
Particle::enabled = config.particleEffects;
- disableAutoDelete();
logger->log("Particle engine set up");
}
@@ -204,39 +203,35 @@ bool Particle::update()
// Update child particles
- for (auto p = mChildParticles.begin();
- p != mChildParticles.end();)
+ for (auto p = mChildParticles.begin(); p != mChildParticles.end(); )
{
+ auto particle = *p;
//move particle with its parent if desired
- if ((*p)->doesFollow())
+ if (particle->doesFollow())
{
- (*p)->moveBy(change);
+ particle->moveBy(change);
}
- //update particle
- if ((*p)->update())
+ if (particle->update())
{
p++;
}
else
{
- delete (*p);
+ delete particle;
p = mChildParticles.erase(p);
}
}
- return mAlive == ALIVE || !mChildParticles.empty() || !mAutoDelete;
+ return isAlive() || !mChildParticles.empty() || !mAutoDelete;
}
void Particle::moveBy(const Vector &change)
{
mPos += change;
+
for (auto &childParticle : mChildParticles)
- {
if (childParticle->doesFollow())
- {
childParticle->moveBy(change);
- }
- }
}
void Particle::moveTo(float x, float y)
@@ -299,7 +294,7 @@ Particle *Particle::addEffect(const std::string &particleEffectFile,
if (!imageSrc.empty() && !dyePalettes.empty())
Dye::instantiate(imageSrc, dyePalettes);
- auto img = resman->getImageRef(imageSrc);
+ auto img = resman->getImage(imageSrc);
newParticle = new ImageParticle(mMap, img);
}
// Other
@@ -405,13 +400,11 @@ Particle *Particle::addTextRiseFadeOutEffect(const std::string &text,
void Particle::adjustEmitterSize(int w, int h)
{
- if (mAllowSizeAdjust)
- {
- for (auto &childEmitter : mChildEmitters)
- {
- childEmitter->adjustSize(w, h);
- }
- }
+ if (!mAllowSizeAdjust)
+ return;
+
+ for (auto &childEmitter : mChildEmitters)
+ childEmitter->adjustSize(w, h);
}
float Particle::getCurrentAlpha() const
diff --git a/src/particle.h b/src/particle.h
index 1c991878..a130a36e 100644
--- a/src/particle.h
+++ b/src/particle.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef PARTICLE_H
-#define PARTICLE_H
+#pragma once
#include "actor.h"
#include "guichanfwd.h"
@@ -77,7 +76,7 @@ class Particle : public Actor
* Gives a particle the properties of an engine root particle and loads
* the particle-related config settings.
*/
- void setupEngine();
+ static void setupEngine();
/**
* Updates particle position, returns false when the particle should
@@ -292,6 +291,53 @@ class Particle : public Actor
float mMomentum = 1.0f; /**< How much speed the particle retains after each game tick*/
};
-extern Particle *particleEngine;
+/**
+ * A handle on a particle. The handle prevents automatic deletion of the
+ * particle by its parent and kills the particle when the handle is destroyed.
+ */
+class ParticleHandle
+{
+ public:
+ explicit ParticleHandle(Particle *particle = nullptr):
+ mParticle(particle)
+ {
+ if (mParticle)
+ mParticle->disableAutoDelete();
+ }
+
+ ParticleHandle(const ParticleHandle &) = delete;
+
+ ParticleHandle(ParticleHandle &&other):
+ mParticle(other.mParticle)
+ {
+ other.mParticle = nullptr;
+ }
-#endif
+ ~ParticleHandle()
+ {
+ if (mParticle)
+ mParticle->kill();
+ }
+
+ ParticleHandle &operator=(const ParticleHandle &) = delete;
+
+ ParticleHandle &operator=(ParticleHandle &&other)
+ {
+ if (this != &other)
+ {
+ if (mParticle)
+ mParticle->kill();
+ mParticle = other.mParticle;
+ other.mParticle = nullptr;
+ }
+ return *this;
+ }
+
+ Particle *operator->() const { return mParticle; }
+ operator Particle *() const { return mParticle; }
+
+ private:
+ Particle *mParticle;
+};
+
+extern Particle *particleEngine;
diff --git a/src/particlecontainer.cpp b/src/particlecontainer.cpp
deleted file mode 100644
index 60485c44..00000000
--- a/src/particlecontainer.cpp
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * The Mana Client
- * Copyright (C) 2008-2009 The Mana World Development Team
- * Copyright (C) 2009-2012 The Mana Developers
- *
- * This file is part of The Mana Client.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <cassert>
-
-#include "particle.h"
-#include "particlecontainer.h"
-
-ParticleContainer::ParticleContainer(ParticleContainer *parent,
- bool delParent):
- mDelParent(delParent),
- mNext(parent)
-{}
-
-ParticleContainer::~ParticleContainer()
-{
- clearLocally();
- if (mDelParent)
- delete mNext;
-}
-
-void ParticleContainer::clear()
-{
- clearLocally();
- if (mNext)
- mNext->clear();
-}
-
-void ParticleContainer::moveTo(float x, float y)
-{
- if (mNext)
- mNext->moveTo(x, y);
-}
-
-// -- particle list ----------------------------------------
-
-ParticleList::ParticleList(ParticleContainer *parent, bool delParent):
- ParticleContainer(parent, delParent)
-{}
-
-ParticleList::~ParticleList() = default;
-
-void ParticleList::addLocally(Particle *particle)
-{
- if (particle)
- {
- // The effect may not die without the beings permission or we segfault
- particle->disableAutoDelete();
- mElements.push_back(particle);
- }
-}
-
-void ParticleList::removeLocally(Particle *particle)
-{
- for (auto it = mElements.begin(), it_end = mElements.end(); it != it_end;)
- {
- if (*it == particle)
- {
- (*it)->kill();
- it = mElements.erase(it);
- }
- else
- it++;
- }
-}
-
-void ParticleList::clearLocally()
-{
- for (auto &element : mElements)
- element->kill();
-
- mElements.clear();
-}
-
-void ParticleList::moveTo(float x, float y)
-{
- ParticleContainer::moveTo(x, y);
-
- for (auto it = mElements.begin();
- it != mElements.end();)
- {
- (*it)->moveTo(x, y);
- if ((*it)->isExtinct())
- {
- (*it)->kill();
- it = mElements.erase(it);
- }
- else
- it++;
- }
-}
-
-// -- particle vector ----------------------------------------
-
-ParticleVector::ParticleVector(ParticleContainer *parent, bool delParent):
- ParticleContainer(parent, delParent)
-{}
-
-ParticleVector::~ParticleVector() = default;
-
-void ParticleVector::setLocally(int index, Particle *particle)
-{
- assert(index >= 0);
-
- delLocally(index);
-
- if (mIndexedElements.size() <= (unsigned) index)
- mIndexedElements.resize(index + 1);
-
- if (particle)
- particle->disableAutoDelete();
- mIndexedElements[index] = particle;
-}
-
-void ParticleVector::delLocally(int index)
-{
- assert(index >= 0);
-
- if (mIndexedElements.size() <= (unsigned) index)
- return;
-
- Particle *p = mIndexedElements[index];
- if (p)
- {
- mIndexedElements[index] = nullptr;
- p->kill();
- }
-}
-
-void ParticleVector::clearLocally()
-{
- for (unsigned int i = 0; i < mIndexedElements.size(); i++)
- delLocally(i);
-}
-
-void ParticleVector::moveTo(float x, float y)
-{
- ParticleContainer::moveTo(x, y);
-
- for (auto &indexedElement : mIndexedElements)
- {
- if (indexedElement)
- {
- indexedElement->moveTo(x, y);
-
- if (indexedElement->isExtinct())
- {
- indexedElement->kill();
- indexedElement = nullptr;
- }
- }
- }
-}
-
diff --git a/src/particlecontainer.h b/src/particlecontainer.h
deleted file mode 100644
index 3905ee49..00000000
--- a/src/particlecontainer.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * The Mana Client
- * Copyright (C) 2008-2009 The Mana World Development Team
- * Copyright (C) 2009-2012 The Mana Developers
- *
- * This file is part of The Mana Client.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PARTICLE_CONTAINER_H
-#define PARTICLE_CONTAINER_H
-
-#include <list>
-#include <vector>
-
-class Particle;
-
-/**
- * Set of particle effects. May be stacked with other ParticleContainers. All
- * operations herein affect such stacked containers, unless the operations end
- * in `Locally'.
- */
-class ParticleContainer
-{
-public:
- /**
- * Constructs a new particle container and assumes responsibility for
- * its parent (for all operations defined herein, except when ending in `Locally')
- *
- * delParent means that the destructor should also free the parent.
- */
- ParticleContainer(ParticleContainer *parent = nullptr, bool delParent = true);
- virtual ~ParticleContainer();
-
- /**
- * Kills and removes all particle effects
- */
- void clear();
-
- /**
- * Kills and removes all particle effects (only in this container)
- */
- virtual void clearLocally() {}
-
- /**
- * Sets the positions of all elements
- */
- virtual void moveTo(float x, float y);
-
-protected:
- bool mDelParent; /**< Delete mNext in destructor */
- ParticleContainer *mNext; /**< Contained container, if any */
-};
-
-/**
- * Linked list of particle effects.
- */
-class ParticleList final : public ParticleContainer
-{
-public:
- ParticleList(ParticleContainer *parent = nullptr, bool delParent = true);
- ~ParticleList() override;
-
- /**
- * Takes control of and adds a particle
- */
- void addLocally(Particle *);
-
- /**
- * `kills' and removes a particle
- */
- void removeLocally(Particle *);
-
- void clearLocally() override;
-
- void moveTo(float x, float y) override;
-
-protected:
- std::list<Particle *> mElements; /**< Contained particle effects */
-};
-
-/**
- * Particle container with indexing facilities
- */
-class ParticleVector final : public ParticleContainer
-{
-public:
- ParticleVector(ParticleContainer *parent = nullptr, bool delParent = true);
- ~ParticleVector() override;
-
- /**
- * Sets a particle at a specified index. Kills the previous particle
- * there, if needed.
- */
- void setLocally(int index, Particle *particle);
-
- /**
- * Removes a particle at a specified index
- */
- void delLocally(int index);
-
- void clearLocally() override;
- void moveTo(float x, float y) override;
-
-protected:
- std::vector<Particle *> mIndexedElements;
-};
-
-#endif
diff --git a/src/particleemitter.cpp b/src/particleemitter.cpp
index f45d39bd..4954d317 100644
--- a/src/particleemitter.cpp
+++ b/src/particleemitter.cpp
@@ -29,11 +29,8 @@
#include "resources/dye.h"
#include "resources/image.h"
-#include "resources/imageset.h"
#include "resources/resourcemanager.h"
-#include "utils/stringutils.h"
-
#include <cmath>
#define SIN45 0.707106781f
@@ -102,7 +99,7 @@ ParticleEmitter::ParticleEmitter(XML::Node emitterNode, Particle *target,
Dye::instantiate(image, dyePalettes);
ResourceManager *resman = ResourceManager::getInstance();
- mParticleImage = resman->getImageRef(image);
+ mParticleImage = resman->getImage(image);
}
}
else if (name == "horizontal-angle")
@@ -178,7 +175,7 @@ ParticleEmitter::ParticleEmitter(XML::Node emitterNode, Particle *target,
}
else if (name == "follow-parent")
{
- mParticleFollow = true;
+ mParticleFollow = propertyNode.getBoolProperty("value", true);
}
else
{
@@ -195,150 +192,13 @@ ParticleEmitter::ParticleEmitter(XML::Node emitterNode, Particle *target,
}
else if (propertyNode.name() == "rotation")
{
- ImageSet *imageset = ResourceManager::getInstance()->getImageSet(
- propertyNode.getProperty("imageset", ""),
- propertyNode.getProperty("width", 0),
- propertyNode.getProperty("height", 0)
- );
-
- // Get animation frames
- for (auto frameNode : propertyNode.children())
- {
- int delay = frameNode.getProperty("delay", 0);
- int offsetX = frameNode.getProperty("offsetX", 0);
- int offsetY = frameNode.getProperty("offsetY", 0);
- if (mMap)
- {
- offsetX -= imageset->getWidth() / 2
- - mMap->getTileWidth() / 2;
- offsetY -= imageset->getHeight() - mMap->getTileHeight();
- }
-
- if (frameNode.name() == "frame")
- {
- int index = frameNode.getProperty("index", -1);
-
- if (index < 0)
- {
- logger->log("No valid value for 'index'");
- continue;
- }
-
- Image *img = imageset->get(index);
-
- if (!img)
- {
- logger->log("No image at index %d", index);
- continue;
- }
-
- mParticleRotation.addFrame(img, delay, offsetX, offsetY);
- }
- else if (frameNode.name() == "sequence")
- {
- int start = frameNode.getProperty("start", -1);
- int end = frameNode.getProperty("end", -1);
-
- if (start < 0 || end < 0)
- {
- logger->log("No valid value for 'start' or 'end'");
- continue;
- }
-
- while (end >= start)
- {
- Image *img = imageset->get(start);
-
- if (!img)
- {
- logger->log("No image at index %d", start);
- continue;
- }
-
- mParticleRotation.addFrame(img, delay, offsetX, offsetY);
- start++;
- }
- }
- else if (frameNode.name() == "end")
- {
- mParticleRotation.addTerminator();
- }
- } // for frameNode
+ mParticleRotation = Animation::fromXML(propertyNode);
}
else if (propertyNode.name() == "animation")
{
- std::string imagesetPath =
- propertyNode.getProperty("imageset", "");
- ImageSet *imageset = ResourceManager::getInstance()->getImageSet(
- imagesetPath,
- propertyNode.getProperty("width", 0),
- propertyNode.getProperty("height", 0)
- );
-
- if (!imageset)
- logger->error(strprintf("Failed to load \"%s\"",
- imagesetPath.c_str()));
-
- // Get animation frames
- for (auto frameNode : propertyNode.children())
- {
- int delay = frameNode.getProperty("delay", 0);
- int offsetX = frameNode.getProperty("offsetX", 0);
- int offsetY = frameNode.getProperty("offsetY", 0);
- offsetY -= imageset->getHeight() - 32;
- offsetX -= imageset->getWidth() / 2 - 16;
-
- if (frameNode.name() == "frame")
- {
- int index = frameNode.getProperty("index", -1);
-
- if (index < 0)
- {
- logger->log("No valid value for 'index'");
- continue;
- }
-
- Image *img = imageset->get(index);
-
- if (!img)
- {
- logger->log("No image at index %d", index);
- continue;
- }
-
- mParticleAnimation.addFrame(img, delay, offsetX, offsetY);
- }
- else if (frameNode.name() == "sequence")
- {
- int start = frameNode.getProperty("start", -1);
- int end = frameNode.getProperty("end", -1);
-
- if (start < 0 || end < 0)
- {
- logger->log("No valid value for 'start' or 'end'");
- continue;
- }
-
- while (end >= start)
- {
- Image *img = imageset->get(start);
-
- if (!img)
- {
- logger->log("No image at index %d", start);
- continue;
- }
-
- mParticleAnimation.addFrame(img, delay, offsetX, offsetY);
- start++;
- }
- }
- else if (frameNode.name() == "end")
- {
- mParticleAnimation.addTerminator();
- }
- } // for frameNode
- } else if (propertyNode.name() == "deatheffect")
+ mParticleAnimation = Animation::fromXML(propertyNode);
+ }
+ else if (propertyNode.name() == "deatheffect")
{
mDeathEffect = propertyNode.textContent();
mDeathEffectConditions = 0x00;
diff --git a/src/particleemitter.h b/src/particleemitter.h
index 9d226209..ede5a71d 100644
--- a/src/particleemitter.h
+++ b/src/particleemitter.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef PARTICLEEMITTER_H
-#define PARTICLEEMITTER_H
+#pragma once
#include "particleemitterprop.h"
@@ -140,4 +139,3 @@ class ParticleEmitter
/** List of emitters the spawned particles are equipped with */
std::list<ParticleEmitter> mParticleChildEmitters;
};
-#endif
diff --git a/src/particleemitterprop.h b/src/particleemitterprop.h
index eb719e57..78f349b8 100644
--- a/src/particleemitterprop.h
+++ b/src/particleemitterprop.h
@@ -19,6 +19,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#pragma once
+
#include <cmath>
static const double PI = 3.14159265;
diff --git a/src/party.cpp b/src/party.cpp
index 92b6c22f..8db773fd 100644
--- a/src/party.cpp
+++ b/src/party.cpp
@@ -45,7 +45,7 @@ Party::~Party()
PartyMember *Party::addMember(int id, const std::string &name)
{
PartyMember *m;
- if (Net::getNetworkType() == ServerType::TMWATHENA && (m = getMember(id)))
+ if (Net::getNetworkType() == ServerType::TmwAthena && (m = getMember(id)))
{
return m;
}
diff --git a/src/party.h b/src/party.h
index d96500ca..14d82352 100644
--- a/src/party.h
+++ b/src/party.h
@@ -18,8 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef PARTY_H
-#define PARTY_H
+#pragma once
#include "avatar.h"
@@ -171,5 +170,3 @@ private:
short mId;
bool mCanInviteUsers = false;
};
-
-#endif // PARTY_H
diff --git a/src/playerinfo.cpp b/src/playerinfo.cpp
index aacff3a1..a05ffaaa 100644
--- a/src/playerinfo.cpp
+++ b/src/playerinfo.cpp
@@ -50,8 +50,8 @@ static bool mNPCPostCount = false;
static BuySellState mBuySellState = BUYSELL_NONE;
-static std::map<int, Special> mSpecials;
-static Timer mSpecialRechargeUpdateTimer;
+static std::map<int, Ability> mAbilities;
+static Timer mAbilityRechargeUpdateTimer;
// --- Triggers ---------------------------------------------------------------
@@ -278,25 +278,25 @@ void setBuySellState(BuySellState buySellState)
}
}
-// --- Specials ---------------------------------------------------------------
+// --- Abilities --------------------------------------------------------------
-void clearSpecialStatus()
+void clearAbilityStatus(int id)
{
- mSpecials.clear();
+ mAbilities.erase(id);
}
-void setSpecialStatus(int id, int current, int max, int recharge)
+void setAbilityStatus(int id, int current, int max, int recharge)
{
- logger->log("SpecialUpdate Skill #%d -- (%d/%d) -> %d", id, current, max,
+ logger->log("AbilityUpdate Skill #%d -- (%d/%d) -> %d", id, current, max,
recharge);
- mSpecials[id].currentMana = current;
- mSpecials[id].neededMana = max;
- mSpecials[id].recharge = recharge;
+ mAbilities[id].currentMana = current;
+ mAbilities[id].neededMana = max;
+ mAbilities[id].recharge = recharge;
}
-const SpecialsMap &getSpecialStatus()
+const std::map<int, Ability> &getAbilityStatus()
{
- return mSpecials;
+ return mAbilities;
}
// --- Misc -------------------------------------------------------------------
@@ -314,16 +314,16 @@ bool isTalking()
void logic()
{
- if (mSpecialRechargeUpdateTimer.passed())
+ if (mAbilityRechargeUpdateTimer.passed())
{
- mSpecialRechargeUpdateTimer.set(100);
+ mAbilityRechargeUpdateTimer.set(100);
- for (auto &special : mSpecials)
+ for (auto &[id, ability] : mAbilities)
{
- special.second.currentMana += special.second.recharge;
- if (special.second.currentMana > special.second.neededMana)
+ ability.currentMana += ability.recharge;
+ if (ability.currentMana > ability.neededMana)
{
- special.second.currentMana = special.second.neededMana;
+ ability.currentMana = ability.neededMana;
}
}
}
diff --git a/src/playerinfo.h b/src/playerinfo.h
index cbb6c748..492ccf42 100644
--- a/src/playerinfo.h
+++ b/src/playerinfo.h
@@ -18,8 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef PLAYERINFO_H
-#define PLAYERINFO_H
+#pragma once
#include <map>
@@ -71,17 +70,15 @@ enum BuySellState
};
/**
- * Special information storage structure.
+ * Ability information storage structure.
*/
-struct Special
+struct Ability
{
int currentMana;
int neededMana;
int recharge;
};
-using SpecialsMap = std::map<int, Special>;
-
/**
* A database like namespace which holds global info about the localplayer
*
@@ -218,22 +215,22 @@ namespace PlayerInfo
*/
void setBuySellState(BuySellState buySellState);
-// --- Specials ---------------------------------------------------------------
+// --- Abilities --------------------------------------------------------------
/**
- * Removes all specials.
+ * Removes the status for the given ability.
*/
- void clearSpecialStatus();
+ void clearAbilityStatus(int id);
/**
- * Changes the status of the given special.
+ * Changes the status of the given ability.
*/
- void setSpecialStatus(int id, int current, int max, int recharge);
+ void setAbilityStatus(int id, int current, int max, int recharge);
/**
- * Returns the status of the given special.
+ * Returns the status all abilities.
*/
- const SpecialsMap &getSpecialStatus();
+ const std::map<int, Ability> &getAbilityStatus();
// --- Misc -------------------------------------------------------------------
@@ -259,5 +256,3 @@ namespace PlayerInfo
void init();
} // namespace PlayerInfo
-
-#endif
diff --git a/src/playerrelations.cpp b/src/playerrelations.cpp
index bec1d081..8da6c06b 100644
--- a/src/playerrelations.cpp
+++ b/src/playerrelations.cpp
@@ -80,11 +80,11 @@ unsigned int PlayerRelationsManager::checkPermissionSilently(
switch (getRelation(playerName))
{
- case PlayerRelation::NEUTRAL:
+ case PlayerRelation::Neutral:
break;
// widen permissions for friends
- case PlayerRelation::FRIEND:
+ case PlayerRelation::Friend:
permissions |=
PlayerPermissions::EMOTE |
PlayerPermissions::SPEECH_FLOAT |
@@ -94,12 +94,12 @@ unsigned int PlayerRelationsManager::checkPermissionSilently(
break;
// narrow permissions for disregarded and ignored players
- case PlayerRelation::DISREGARDED:
+ case PlayerRelation::Disregarded:
permissions &=
PlayerPermissions::EMOTE |
PlayerPermissions::SPEECH_FLOAT;
break;
- case PlayerRelation::IGNORED:
+ case PlayerRelation::Ignored:
permissions &= 0;
break;
}
@@ -163,7 +163,7 @@ void PlayerRelationsManager::removePlayer(const std::string &name)
PlayerRelation PlayerRelationsManager::getRelation(const std::string &name) const
{
auto it = mRelations.find(name);
- return it != mRelations.end() ? it->second : PlayerRelation::NEUTRAL;
+ return it != mRelations.end() ? it->second : PlayerRelation::Neutral;
}
////////////////////////////////////////
diff --git a/src/playerrelations.h b/src/playerrelations.h
index 38452c2f..0d2ee8d8 100644
--- a/src/playerrelations.h
+++ b/src/playerrelations.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef PLAYER_RELATIONS_H
-#define PLAYER_RELATIONS_H
+#pragma once
#include <list>
#include <map>
@@ -31,10 +30,10 @@ class Being;
enum class PlayerRelation
{
- NEUTRAL = 0,
- FRIEND = 1,
- DISREGARDED = 2,
- IGNORED = 3
+ Neutral = 0,
+ Friend = 1,
+ Disregarded = 2,
+ Ignored = 3
};
constexpr unsigned int RELATIONS_NR = 4;
@@ -201,6 +200,3 @@ private:
extern PlayerRelationsManager player_relations; // singleton representation of player relations
-
-
-#endif // PLAYER_RELATIONS_H
diff --git a/src/position.h b/src/position.h
index c9c8e6f2..7093351d 100644
--- a/src/position.h
+++ b/src/position.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef POSITION_H
-#define POSITION_H
+#pragma once
#include <iostream>
#include <list>
@@ -50,5 +49,3 @@ std::ostream& operator <<(std::ostream &os, const Position &p);
* output stream.
*/
std::ostream& operator <<(std::ostream &os, const Path &path);
-
-#endif // POSITION_H
diff --git a/src/properties.h b/src/properties.h
index 7ccf86dc..5a7a19b3 100644
--- a/src/properties.h
+++ b/src/properties.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef PROPERTIES_H
-#define PROPERTIES_H
+#pragma once
#include <map>
#include <sstream>
@@ -140,5 +139,3 @@ class Properties
using PropertyMap = std::map<std::string, std::string>;
PropertyMap mProperties;
};
-
-#endif
diff --git a/src/resources/abilitydb.cpp b/src/resources/abilitydb.cpp
new file mode 100644
index 00000000..311ee9eb
--- /dev/null
+++ b/src/resources/abilitydb.cpp
@@ -0,0 +1,106 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2010-2013 The Mana Developers
+ *
+ * This file is part of The Mana Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "resources/abilitydb.h"
+
+#include "log.h"
+
+#include "utils/dtor.h"
+
+#include <map>
+
+namespace
+{
+ std::map<int, AbilityInfo *> mAbilityInfos;
+ bool mLoaded = false;
+}
+
+static AbilityInfo::TargetMode targetModeFromString(const std::string& str)
+{
+ if (str == "being")
+ return AbilityInfo::TARGET_BEING;
+ if (str == "point")
+ return AbilityInfo::TARGET_POINT;
+ if (str == "direction")
+ return AbilityInfo::TARGET_DIRECTION;
+
+ logger->log("AbilityDB: Warning, unknown target mode \"%s\"", str.c_str() );
+ return AbilityInfo::TARGET_BEING;
+}
+
+
+void AbilityDB::init()
+{
+ if (mLoaded)
+ unload();
+}
+
+void AbilityDB::readAbilityNode(XML::Node node, const std::string &filename)
+{
+ auto *info = new AbilityInfo();
+ int id = node.getProperty("id", 0);
+ info->id = id;
+ info->name = node.getProperty("name", std::string());
+ info->icon = node.getProperty("icon", std::string());
+ info->useAction = node.getProperty("useaction", std::string());
+
+ info->targetMode = targetModeFromString(node.getProperty("target", "being"));
+
+ info->rechargeable = node.getBoolProperty("rechargeable", true);
+ info->rechargeNeeded = 0;
+ info->rechargeCurrent = 0;
+
+ if (mAbilityInfos.find(id) != mAbilityInfos.end())
+ logger->log("AbilityDB: Duplicate ability ID %d in %s, ignoring", id, filename.c_str());
+ else
+ mAbilityInfos[id] = info;
+}
+
+void AbilityDB::checkStatus()
+{
+ mLoaded = true;
+}
+
+void AbilityDB::unload()
+{
+ delete_all(mAbilityInfos);
+ mAbilityInfos.clear();
+
+ mLoaded = false;
+}
+
+AbilityInfo *AbilityDB::get(int id)
+{
+ auto i = mAbilityInfos.find(id);
+ if (i != mAbilityInfos.end())
+ return i->second;
+
+ return nullptr;
+}
+
+AbilityInfo *AbilityDB::find(std::string_view name)
+{
+ for (auto &[_, abilityInfo] : mAbilityInfos)
+ {
+ if (abilityInfo->name == name)
+ return abilityInfo;
+ }
+ return nullptr;
+}
diff --git a/src/resources/specialdb.h b/src/resources/abilitydb.h
index 20ba0075..6b3de5a6 100644
--- a/src/resources/specialdb.h
+++ b/src/resources/abilitydb.h
@@ -18,50 +18,52 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SPECIAL_DB_H
-#define SPECIAL_DB_H
+#pragma once
#include <string>
#include "utils/xml.h"
-struct SpecialInfo
+struct AbilityInfo
{
enum TargetMode
{
- TARGET_BEING, // target any being
- TARGET_POINT // target map location
+ TARGET_BEING, // target any being
+ TARGET_POINT, // target map location
+ TARGET_DIRECTION // target a direction
};
int id;
- std::string set; // tab on which the special is shown
- std::string name; // displayed name of special
+ std::string name; // displayed name of ability
std::string icon; // filename of graphical icon
+ std::string useAction; // action when using the ability
- TargetMode targetMode; // target mode
+ TargetMode targetMode;
- bool rechargeable; // true when the special has a recharge bar
+ bool rechargeable; // true when the ability has a recharge bar
int rechargeNeeded; // maximum recharge when applicable
int rechargeCurrent; // current recharge when applicable
};
/**
- * Special information database.
+ * Ability information database.
*/
-namespace SpecialDB
+namespace AbilityDB
{
void init();
- void readSpecialSetNode(XML::Node node, const std::string &filename);
+ void readAbilityNode(XML::Node node, const std::string &filename);
void checkStatus();
void unload();
- /** gets the special info for ID. Will return 0 when it is
- * a server-specific special.
+ /** Gets the ability info for ID. Will return nullptr when it is
+ * a server-specific ability.
*/
- SpecialInfo *get(int id);
+ AbilityInfo *get(int id);
- SpecialInfo::TargetMode targetModeFromString(const std::string& str);
+ /**
+ * Finds an ability by name. Returns nullptr when the ability could not be
+ * found.
+ */
+ AbilityInfo *find(std::string_view name);
}
-
-#endif
diff --git a/src/resources/action.h b/src/resources/action.h
index 37f29810..c3e47dbb 100644
--- a/src/resources/action.h
+++ b/src/resources/action.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef ACTION_H
-#define ACTION_H
+#pragma once
#include <map>
@@ -43,5 +42,3 @@ class Action
protected:
std::map<int, Animation *> mAnimations;
};
-
-#endif
diff --git a/src/resources/ambientlayer.h b/src/resources/ambientlayer.h
index e62af33f..8e0137b1 100644
--- a/src/resources/ambientlayer.h
+++ b/src/resources/ambientlayer.h
@@ -18,8 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef RESOURCES_AMBIENTOVERLAY_H
-#define RESOURCES_AMBIENTOVERLAY_H
+#pragma once
#include "resource.h"
@@ -52,5 +51,3 @@ class AmbientLayer
float mPosX = 0; /**< Current layer X position. */
float mPosY = 0; /**< Current layer Y position. */
};
-
-#endif
diff --git a/src/resources/animation.cpp b/src/resources/animation.cpp
index b48e8cff..91c22236 100644
--- a/src/resources/animation.cpp
+++ b/src/resources/animation.cpp
@@ -21,6 +21,11 @@
#include "resources/animation.h"
+#include "dye.h"
+#include "game.h"
+#include "log.h"
+#include "resourcemanager.h"
+
void Animation::addFrame(Image *image, int delay, int offsetX, int offsetY)
{
auto &frame = mFrames.emplace_back();
@@ -41,3 +46,90 @@ bool Animation::isTerminator(const Frame &candidate)
{
return candidate.image == nullptr;
}
+
+Animation Animation::fromXML(XML::Node node, const std::string &dyePalettes)
+{
+ Animation animation;
+
+ std::string imagePath = node.getProperty("imageset", std::string());
+
+ // Instanciate the dye coloration.
+ Dye::instantiate(imagePath, dyePalettes);
+
+ auto imageSet = ResourceManager::getInstance()->getImageSet(
+ imagePath,
+ node.getProperty("width", 0),
+ node.getProperty("height", 0)
+ );
+
+ if (!imageSet)
+ return animation;
+
+ // Get animation frames
+ for (auto frameNode : node.children())
+ {
+ int delay = frameNode.getProperty("delay", 0);
+ int offsetX = frameNode.getProperty("offsetX", 0);
+ int offsetY = frameNode.getProperty("offsetY", 0);
+ Game *game = Game::instance();
+ if (game)
+ {
+ offsetX -= imageSet->getWidth() / 2 - game->getCurrentTileWidth() / 2;
+ offsetY -= imageSet->getHeight() - game->getCurrentTileHeight();
+ }
+
+ if (frameNode.name() == "frame")
+ {
+ int index = frameNode.getProperty("index", -1);
+
+ if (index < 0)
+ {
+ logger->log("No valid value for 'index'");
+ continue;
+ }
+
+ Image *img = imageSet->get(index);
+
+ if (!img)
+ {
+ logger->log("No image at index %d", index);
+ continue;
+ }
+
+ animation.addFrame(img, delay, offsetX, offsetY);
+ }
+ else if (frameNode.name() == "sequence")
+ {
+ int start = frameNode.getProperty("start", -1);
+ int end = frameNode.getProperty("end", -1);
+
+ if (start < 0 || end < 0)
+ {
+ logger->log("No valid value for 'start' or 'end'");
+ continue;
+ }
+
+ while (end >= start)
+ {
+ Image *img = imageSet->get(start);
+
+ if (!img)
+ {
+ logger->log("No image at index %d", start);
+ continue;
+ }
+
+ animation.addFrame(img, delay, offsetX, offsetY);
+ start++;
+ }
+ }
+ else if (frameNode.name() == "end")
+ {
+ animation.addTerminator();
+ }
+ }
+
+ animation.mImageSet = imageSet;
+
+ return animation;
+}
diff --git a/src/resources/animation.h b/src/resources/animation.h
index 812e0547..cc3abd58 100644
--- a/src/resources/animation.h
+++ b/src/resources/animation.h
@@ -19,8 +19,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef ANIMATION_H
-#define ANIMATION_H
+#pragma once
+
+#include "resources/imageset.h"
+#include "utils/xml.h"
#include <vector>
@@ -77,9 +79,14 @@ class Animation final
*/
static bool isTerminator(const Frame &phase);
+ /**
+ * Loads an animation from XML.
+ */
+ static Animation fromXML(XML::Node node,
+ const std::string &dyePalettes = {});
+
protected:
+ ResourceRef<ImageSet> mImageSet;
std::vector<Frame> mFrames;
int mDuration = 0;
};
-
-#endif
diff --git a/src/resources/attributes.cpp b/src/resources/attributes.cpp
index ab270b65..7ec6b516 100644
--- a/src/resources/attributes.cpp
+++ b/src/resources/attributes.cpp
@@ -40,7 +40,7 @@
namespace Attributes {
- using Attribute = struct
+ struct Attribute
{
unsigned int id;
std::string name;
@@ -94,11 +94,11 @@ namespace Attributes {
{
// Fill up the modifiable attribute label list.
attributeLabels.clear();
- for (auto it = attributes.cbegin(), it_end = attributes.cend(); it != it_end; it++)
+ for (const auto &[_, attribute] : attributes)
{
- if (it->second.modifiable &&
- (it->second.scope == "character" || it->second.scope == "being"))
- attributeLabels.push_back(it->second.name + ":");
+ if (attribute.modifiable &&
+ (attribute.scope == "character" || attribute.scope == "being"))
+ attributeLabels.push_back(attribute.name + ":");
}
}
@@ -228,7 +228,7 @@ namespace Attributes {
void init()
{
- if (attributes.size())
+ if (!attributes.empty())
unload();
}
@@ -238,14 +238,14 @@ namespace Attributes {
void readAttributeNode(XML::Node node, const std::string &filename)
{
int id = node.getProperty("id", 0);
-
if (!id)
{
logger->log("Attributes: Invalid or missing stat ID in "
DEFAULT_ATTRIBUTESDB_FILE "!");
return;
}
- else if (attributes.find(id) != attributes.end())
+
+ if (attributes.find(id) != attributes.end())
{
logger->log("Attributes: Redefinition of stat ID %d", id);
}
@@ -364,23 +364,23 @@ namespace Attributes {
{
std::list<ItemStat> dbStats;
- for (auto it = tags.cbegin(), it_end = tags.cend(); it != it_end; ++it)
- dbStats.emplace_back(it->first, it->second);
+ for (const auto &[tag, format] : tags)
+ dbStats.emplace_back(tag, format);
setStatsList(std::move(dbStats));
}
void informStatusWindow()
{
- for (auto it = attributes.cbegin(), it_end = attributes.cend(); it != it_end; it++)
+ for (const auto &[_, attribute] : attributes)
{
- if (it->second.playerInfoId == -1 &&
- (it->second.scope == "character" || it->second.scope == "being"))
+ if (attribute.playerInfoId == -1 &&
+ (attribute.scope == "character" || attribute.scope == "being"))
{
- statusWindow->addAttribute(it->second.id,
- it->second.name,
- it->second.modifiable,
- it->second.description);
+ statusWindow->addAttribute(attribute.id,
+ attribute.name,
+ attribute.modifiable,
+ attribute.description);
}
}
}
diff --git a/src/resources/attributes.h b/src/resources/attributes.h
index 9071d6b8..e70a5435 100644
--- a/src/resources/attributes.h
+++ b/src/resources/attributes.h
@@ -18,8 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef RESOURCES_ATTRIBUTES_H
-#define RESOURCES_ATTRIBUTES_H
+#pragma once
#include <string>
#include <vector>
@@ -73,5 +72,3 @@ namespace Attributes
unsigned int getAttributeMaximum();
} // namespace Attributes
-
-#endif // RESOURCES_ATTRIBUTES_H
diff --git a/src/resources/beinginfo.cpp b/src/resources/beinginfo.cpp
index 17e270dc..20c24d4f 100644
--- a/src/resources/beinginfo.cpp
+++ b/src/resources/beinginfo.cpp
@@ -52,15 +52,15 @@ static std::optional<ActorSprite::TargetCursorSize> targetCursorSizeFromString(c
static std::optional<Cursor> cursorFromString(const std::string &cursor)
{
- if (cursor == "pointer") return Cursor::POINTER;
- if (cursor == "attack") return Cursor::FIGHT;
- if (cursor == "pickup") return Cursor::PICKUP;
- if (cursor == "talk") return Cursor::TALK;
- if (cursor == "action") return Cursor::ACTION;
- if (cursor == "left") return Cursor::LEFT;
- if (cursor == "up") return Cursor::UP;
- if (cursor == "right") return Cursor::RIGHT;
- if (cursor == "down") return Cursor::DOWN;
+ if (cursor == "pointer") return Cursor::Pointer;
+ if (cursor == "attack") return Cursor::Fight;
+ if (cursor == "pickup") return Cursor::PickUp;
+ if (cursor == "talk") return Cursor::Talk;
+ if (cursor == "action") return Cursor::Action;
+ if (cursor == "left") return Cursor::Left;
+ if (cursor == "up") return Cursor::Up;
+ if (cursor == "right") return Cursor::Right;
+ if (cursor == "down") return Cursor::Down;
return {};
}
@@ -87,7 +87,7 @@ void BeingInfo::setHoverCursor(const std::string &cursorName)
logger->log("Unknown hoverCursor value \"%s\" for %s",
cursorName.c_str(), name.c_str());
}
- hoverCursor = cursor.value_or(Cursor::POINTER);
+ hoverCursor = cursor.value_or(Cursor::Pointer);
}
void BeingInfo::addSound(SoundEvent event, const std::string &filename)
diff --git a/src/resources/beinginfo.h b/src/resources/beinginfo.h
index 2eac4237..91343d4f 100644
--- a/src/resources/beinginfo.h
+++ b/src/resources/beinginfo.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef BEINGINFO_H
-#define BEINGINFO_H
+#pragma once
#include "actorsprite.h"
#include "map.h"
@@ -43,10 +42,10 @@ struct Attack
enum class SoundEvent
{
- HIT,
- MISS,
- HURT,
- DIE
+ Hit,
+ Miss,
+ Hurt,
+ Die
};
/**
@@ -67,7 +66,7 @@ public:
std::string name;
SpriteDisplay display;
ActorSprite::TargetCursorSize targetCursorSize = ActorSprite::TC_MEDIUM;
- Cursor hoverCursor = Cursor::POINTER;
+ Cursor hoverCursor = Cursor::Pointer;
unsigned char walkMask = Map::BLOCKMASK_ALL;
Map::BlockType blockType = Map::BLOCKTYPE_CHARACTER;
bool targetSelection = true;
@@ -85,5 +84,3 @@ private:
std::map<SoundEvent, std::vector<std::string>> mSounds;
std::map<int, Attack> mAttacks;
};
-
-#endif // BEINGINFO_H
diff --git a/src/resources/chardb.h b/src/resources/chardb.h
index 10530b26..de49dad6 100644
--- a/src/resources/chardb.h
+++ b/src/resources/chardb.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef RESOURCES_CHARDB_H
-#define RESOURCES_CHARDB_H
+#pragma once
#include <vector>
@@ -44,5 +43,3 @@ namespace CharDB
const std::vector<int> &getDefaultItems();
}
-
-#endif // RESOURCES_CHARDB_H
diff --git a/src/resources/dye.h b/src/resources/dye.h
index ce5565f8..61ad4df2 100644
--- a/src/resources/dye.h
+++ b/src/resources/dye.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef DYE_H
-#define DYE_H
+#pragma once
#include <string>
#include <vector>
@@ -97,5 +96,3 @@ class Dye
*/
DyePalette *mDyePalettes[7];
};
-
-#endif
diff --git a/src/resources/emotedb.cpp b/src/resources/emotedb.cpp
index 6f1ec6e4..d29483d1 100644
--- a/src/resources/emotedb.cpp
+++ b/src/resources/emotedb.cpp
@@ -22,10 +22,9 @@
#include "resources/emotedb.h"
#include "log.h"
-#include "imagesprite.h"
-#include "resources/resourcemanager.h"
#include "resources/imageset.h"
+#include "resources/resourcemanager.h"
#include <algorithm>
#include <vector>
@@ -44,8 +43,7 @@ void EmoteDB::init()
mUnknown.name = "unknown";
mUnknown.effectId = -1;
- mUnknown.sprite = std::make_unique<ImageSprite>(
- ResourceManager::getInstance()->getImageRef("graphics/sprites/error.png"));
+ mUnknown.image = ResourceManager::getInstance()->getImage("graphics/sprites/error.png");
}
void EmoteDB::readEmoteNode(XML::Node node, const std::string &filename)
@@ -84,7 +82,6 @@ void EmoteDB::readEmoteNode(XML::Node node, const std::string &filename)
emote.is = ResourceManager::getInstance()->getImageSet(imageName,
width,
height);
- emote.is->decRef(); // clear automatic reference
if (!emote.is || emote.is->size() == 0)
{
@@ -94,7 +91,7 @@ void EmoteDB::readEmoteNode(XML::Node node, const std::string &filename)
}
// For now we just use the first image in the animation
- emote.sprite = std::make_unique<ImageSprite>(emote.is->get(0));
+ emote.image = emote.is->get(0);
mEmotes.push_back(std::move(emote));
}
@@ -106,14 +103,12 @@ void EmoteDB::checkStatus()
void EmoteDB::unload()
{
+ // These images are owned by the ImageSet
for (auto &emote : mEmotes)
- emote.sprite->releaseImageRef();
+ emote.image.release();
mEmotes.clear();
-
- if (mUnknown.sprite)
- mUnknown.sprite->releaseImageRef();
-
+ mUnknown.image = nullptr;
mLoaded = false;
}
diff --git a/src/resources/emotedb.h b/src/resources/emotedb.h
index 7c6f4644..b722c88b 100644
--- a/src/resources/emotedb.h
+++ b/src/resources/emotedb.h
@@ -19,26 +19,22 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef EMOTE_DB_H
-#define EMOTE_DB_H
+#pragma once
-#include <memory>
#include <string>
-#include "resources/resource.h"
+#include "resources/image.h"
+#include "resources/imageset.h"
#include "utils/xml.h"
-class ImageSet;
-class ImageSprite;
-
struct Emote
{
int id;
int effectId;
std::string name;
ResourceRef<ImageSet> is;
- std::unique_ptr<ImageSprite> sprite;
+ ResourceRef<Image> image;
};
/**
@@ -59,5 +55,3 @@ namespace EmoteDB
int getEmoteCount();
}
-
-#endif // EMOTE_DB_H
diff --git a/src/resources/hairdb.h b/src/resources/hairdb.h
index 374f2e03..a228c72e 100644
--- a/src/resources/hairdb.h
+++ b/src/resources/hairdb.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef HAIR_MANAGER_H
-#define HAIR_MANAGER_H
+#pragma once
#include <map>
#include <set>
@@ -93,5 +92,3 @@ private:
};
extern HairDB hairDB;
-
-#endif
diff --git a/src/resources/image.cpp b/src/resources/image.cpp
index 328ea9b8..158d5956 100644
--- a/src/resources/image.cpp
+++ b/src/resources/image.cpp
@@ -113,37 +113,40 @@ Resource *Image::load(SDL_RWops *rw)
Resource *Image::load(SDL_RWops *rw, const Dye &dye)
{
- SDL_Surface *tmpImage = IMG_Load_RW(rw, 1);
+ SDL_Surface *surf = IMG_Load_RW(rw, 1);
- if (!tmpImage)
+ if (!surf)
{
logger->log("Error, image load failed: %s", IMG_GetError());
return nullptr;
}
- SDL_PixelFormat rgba;
- rgba.palette = nullptr;
- rgba.BitsPerPixel = 32;
- rgba.BytesPerPixel = 4;
- rgba.Rmask = 0xFF000000; rgba.Rloss = 0; rgba.Rshift = 24;
- rgba.Gmask = 0x00FF0000; rgba.Gloss = 0; rgba.Gshift = 16;
- rgba.Bmask = 0x0000FF00; rgba.Bloss = 0; rgba.Bshift = 8;
- rgba.Amask = 0x000000FF; rgba.Aloss = 0; rgba.Ashift = 0;
+ if (surf->format->format != SDL_PIXELFORMAT_ABGR8888)
+ {
+ logger->log("Warning: image format is %s, not SDL_PIXELFORMAT_ABGR8888. Converting...",
+ SDL_GetPixelFormatName(surf->format->format));
- SDL_Surface *surf = SDL_ConvertSurface(tmpImage, &rgba, 0);
- SDL_FreeSurface(tmpImage);
+ SDL_Surface *convertedSurf = SDL_ConvertSurfaceFormat(surf, SDL_PIXELFORMAT_ABGR8888, 0);
+ SDL_FreeSurface(surf);
+ if (!convertedSurf)
+ {
+ logger->log("Error, image convert failed: %s", SDL_GetError());
+ return nullptr;
+ }
+ surf = convertedSurf;
+ }
- auto *pixels = static_cast< Uint32 * >(surf->pixels);
- for (Uint32 *p_end = pixels + surf->w * surf->h; pixels != p_end; ++pixels)
+ auto *pixels = static_cast< uint32_t * >(surf->pixels);
+ for (uint32_t *p_end = pixels + surf->w * surf->h; pixels != p_end; ++pixels)
{
- int alpha = *pixels & 255;
+ const uint32_t alpha = (*pixels >> 24) & 255;
if (!alpha) continue;
int v[3];
- v[0] = (*pixels >> 24) & 255;
- v[1] = (*pixels >> 16) & 255;
- v[2] = (*pixels >> 8 ) & 255;
+ v[0] = (*pixels) & 255;
+ v[1] = (*pixels >> 8) & 255;
+ v[2] = (*pixels >> 16) & 255;
dye.update(v);
- *pixels = (v[0] << 24) | (v[1] << 16) | (v[2] << 8) | alpha;
+ *pixels = (alpha << 24) | (v[2] << 16) | (v[1] << 8) | v[0];
}
Image *image = load(surf);
diff --git a/src/resources/image.h b/src/resources/image.h
index e2e240c3..37dd5e1d 100644
--- a/src/resources/image.h
+++ b/src/resources/image.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef IMAGE_H
-#define IMAGE_H
+#pragma once
#include "resources/resource.h"
@@ -216,5 +215,3 @@ class SubImage : public Image
private:
ResourceRef<Image> mParent;
};
-
-#endif
diff --git a/src/resources/imageset.h b/src/resources/imageset.h
index a6501cc9..97dbec90 100644
--- a/src/resources/imageset.h
+++ b/src/resources/imageset.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef IMAGESET_H
-#define IMAGESET_H
+#pragma once
#include "resources/resource.h"
@@ -78,5 +77,3 @@ class ImageSet : public Resource
int mOffsetX = 0;
int mOffsetY = 0;
};
-
-#endif
diff --git a/src/resources/imagewriter.h b/src/resources/imagewriter.h
index 23e85bd8..41ca267a 100644
--- a/src/resources/imagewriter.h
+++ b/src/resources/imagewriter.h
@@ -19,6 +19,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#pragma once
+
#include <iosfwd>
struct SDL_Surface;
diff --git a/src/resources/itemdb.cpp b/src/resources/itemdb.cpp
index 68ddcd75..05f6ad0b 100644
--- a/src/resources/itemdb.cpp
+++ b/src/resources/itemdb.cpp
@@ -21,6 +21,7 @@
#include "resources/itemdb.h"
+#include "configuration.h"
#include "log.h"
#include "resources/hairdb.h"
@@ -29,10 +30,11 @@
#include "utils/dtor.h"
#include "utils/gettext.h"
#include "utils/stringutils.h"
-#include "configuration.h"
+#include "net/tmwa/protocol.h"
#include <cassert>
+#include <string_view>
void setStatsList(std::list<ItemStat> stats)
{
@@ -60,14 +62,70 @@ static ItemType itemTypeFromString(const std::string &name, int id = 0)
return ITEM_UNUSABLE;
}
+static uint8_t spriteFromString(std::string_view name)
+{
+ if (name.empty())
+ return SPRITE_ALL;
+ if (name == "race" || name == "type")
+ return TmwAthena::SPRITE_BASE;
+ if (name == "shoes" || name == "boot" || name == "boots")
+ return TmwAthena::SPRITE_SHOE;
+ if (name == "bottomclothes" || name == "bottom" || name == "pants")
+ return TmwAthena::SPRITE_BOTTOMCLOTHES;
+ if (name == "topclothes" || name == "top" || name == "torso" || name == "body")
+ return TmwAthena::SPRITE_TOPCLOTHES;
+ if (name == "misc1")
+ return TmwAthena::SPRITE_MISC1;
+ if (name == "misc2" || name == "scarf" || name == "scarfs")
+ return TmwAthena::SPRITE_MISC2;
+ if (name == "hair")
+ return TmwAthena::SPRITE_HAIR;
+ if (name == "hat" || name == "hats")
+ return TmwAthena::SPRITE_HAT;
+ if (name == "wings")
+ return TmwAthena::SPRITE_CAPE;
+ if (name == "glove" || name == "gloves")
+ return TmwAthena::SPRITE_GLOVES;
+ if (name == "weapon" || name == "weapons")
+ return TmwAthena::SPRITE_WEAPON;
+ if (name == "shield" || name == "shields")
+ return TmwAthena::SPRITE_SHIELD;
+ if (name == "amulet" || name == "amulets")
+ return 12;
+ if (name == "ring" || name == "rings")
+ return 13;
+
+ return SPRITE_UNKNOWN;
+}
+
+static uint8_t directionFromString(std::string_view name)
+{
+ if (name.empty())
+ return DIRECTION_ALL;
+ if (name == "down" || name == "downall")
+ return DIRECTION_DOWN;
+ if (name == "left")
+ return DIRECTION_LEFT;
+ if (name == "up" || name == "upall")
+ return DIRECTION_UP;
+ if (name == "right")
+ return DIRECTION_RIGHT;
+
+ // hack for died action.
+ if (name == "died")
+ return DIRECTION_DEAD;
+
+ return DIRECTION_UNKNOWN;
+}
+
void ItemDB::loadEmptyItemDefinition()
{
mUnknown->name = _("Unknown item");
mUnknown->display = SpriteDisplay();
std::string errFile = paths.getStringValue("spriteErrorFile");
- mUnknown->setSprite(errFile, Gender::MALE, 0);
- mUnknown->setSprite(errFile, Gender::FEMALE, 0);
- mUnknown->setSprite(errFile, Gender::HIDDEN, 0);
+ mUnknown->setSprite(errFile, Gender::Male, 0);
+ mUnknown->setSprite(errFile, Gender::Female, 0);
+ mUnknown->setSprite(errFile, Gender::Hidden, 0);
mUnknown->hitEffectId = paths.getIntValue("hitEffectId");
mUnknown->criticalHitEffectId = paths.getIntValue("criticalHitEffectId");
}
@@ -122,11 +180,11 @@ void ItemDB::loadSpriteRef(ItemInfo &itemInfo, XML::Node node)
const int race = node.getProperty("race", 0);
if (gender == "male" || gender == "unisex")
- itemInfo.setSprite(filename, Gender::MALE, race);
+ itemInfo.setSprite(filename, Gender::Male, race);
if (gender == "female" || gender == "unisex")
- itemInfo.setSprite(filename, Gender::FEMALE, race);
+ itemInfo.setSprite(filename, Gender::Female, race);
if (gender == "hidden" || gender == "other" || gender == "unisex")
- itemInfo.setSprite(filename, Gender::HIDDEN, race);
+ itemInfo.setSprite(filename, Gender::Hidden, race);
}
void ItemDB::loadSoundRef(ItemInfo &itemInfo, XML::Node node)
@@ -136,11 +194,11 @@ void ItemDB::loadSoundRef(ItemInfo &itemInfo, XML::Node node)
if (event == "hit")
{
- itemInfo.addSound(EquipmentSoundEvent::HIT, filename);
+ itemInfo.addSound(EquipmentSoundEvent::Hit, filename);
}
else if (event == "strike" || event == "miss")
{
- itemInfo.addSound(EquipmentSoundEvent::STRIKE, filename);
+ itemInfo.addSound(EquipmentSoundEvent::Strike, filename);
}
else
{
@@ -166,6 +224,46 @@ void ItemDB::loadFloorSprite(SpriteDisplay &display, XML::Node floorNode)
}
}
+void ItemDB::loadReplacement(ItemInfo &info, XML::Node replaceNode)
+{
+ std::string_view spriteString;
+ std::string_view directionString;
+
+ replaceNode.attribute("sprite", spriteString);
+ replaceNode.attribute("direction", directionString);
+
+ const uint8_t sprite = spriteFromString(spriteString);
+ const uint8_t direction = directionFromString(directionString);
+
+ if (sprite == SPRITE_UNKNOWN)
+ {
+ logger->log("ItemDB: Invalid sprite name '%s' in replace tag",
+ spriteString.data());
+ return;
+ }
+
+ if (direction == DIRECTION_UNKNOWN)
+ {
+ logger->log("ItemDB: Invalid direction name '%s' in replace tag",
+ directionString.data());
+ return;
+ }
+
+ Replacement &replace = info.replacements.emplace_back();
+ replace.sprite = sprite;
+ replace.direction = direction;
+
+ for (auto child : replaceNode.children())
+ {
+ if (child.name() == "item")
+ {
+ Replacement::Item &item = replace.items.emplace_back();
+ child.attribute("from", item.from);
+ child.attribute("to", item.to);
+ }
+ }
+}
+
void ItemDB::unload()
{
logger->log("Unloading item database...");
@@ -228,6 +326,10 @@ void ItemDB::loadCommonRef(ItemInfo &itemInfo, XML::Node node, const std::string
{
loadFloorSprite(itemInfo.display, itemChild);
}
+ else if (itemChild.name() == "replace")
+ {
+ loadReplacement(itemInfo, itemChild);
+ }
}
}
diff --git a/src/resources/itemdb.h b/src/resources/itemdb.h
index ef0985a3..69620122 100644
--- a/src/resources/itemdb.h
+++ b/src/resources/itemdb.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef ITEM_MANAGER_H
-#define ITEM_MANAGER_H
+#pragma once
#include <list>
#include <map>
@@ -134,13 +133,18 @@ class ItemDB
/**
* Loads the sound references contained in a <sound> tag.
*/
- void loadSoundRef(ItemInfo &itemInfo, XML::Node node);
+ void loadSoundRef(ItemInfo &itemInfo, XML::Node node);
/**
* Loads the floor item references contained in a <floor> tag.
*/
void loadFloorSprite(SpriteDisplay &display, XML::Node node);
+ /**
+ * Loads the <replace> tag.
+ */
+ void loadReplacement(ItemInfo &info, XML::Node replaceNode);
+
// Items database
std::map<int, ItemInfo *> mItemInfos;
std::map<std::string, ItemInfo *> mNamedItemInfos;
@@ -204,5 +208,3 @@ class ManaServItemDB : public ItemDB
} // namespace ManaServ
extern ItemDB *itemDb;
-
-#endif
diff --git a/src/resources/iteminfo.h b/src/resources/iteminfo.h
index 78c808da..62e4796d 100644
--- a/src/resources/iteminfo.h
+++ b/src/resources/iteminfo.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef ITEMINFO_H
-#define ITEMINFO_H
+#pragma once
#include "being.h"
@@ -32,8 +31,8 @@
enum class EquipmentSoundEvent
{
- STRIKE,
- HIT
+ Strike,
+ Hit
};
/**
@@ -68,6 +67,32 @@ namespace ManaServ {
class ManaServItemDB;
}
+enum ReplacementDirection : uint8_t
+{
+ DIRECTION_ALL = DIRECTION_DEFAULT,
+ DIRECTION_DEAD = DIRECTION_INVALID,
+ DIRECTION_UNKNOWN,
+};
+
+enum ReplacementSprite : uint8_t
+{
+ SPRITE_UNKNOWN = 254,
+ SPRITE_ALL = 255,
+};
+
+struct Replacement
+{
+ struct Item
+ {
+ int from = 0; // ID to replace (0: any)
+ int to = 0; // Replace with this ID (0: remove)
+ };
+
+ uint8_t sprite = SPRITE_ALL; // sprite slot to replace
+ uint8_t direction = DIRECTION_ALL; // direction in which to replace
+ std::vector<Item> items; // specific items to replace (empty: remove)
+};
+
/**
* Defines a class for storing generic item infos.
*/
@@ -110,6 +135,8 @@ public:
ItemType type = ITEM_UNUSABLE; /**< Item type. */
+ std::vector<Replacement> replacements;
+
const std::string &getSprite(Gender gender, int race) const;
const std::string &getSound(EquipmentSoundEvent event) const;
@@ -162,5 +189,3 @@ enum EquipmentSlot
};
} // namespace TmwAthena
-
-#endif // ITEMINFO_H
diff --git a/src/resources/mapreader.cpp b/src/resources/mapreader.cpp
index b5a5e258..b952cdcc 100644
--- a/src/resources/mapreader.cpp
+++ b/src/resources/mapreader.cpp
@@ -527,7 +527,7 @@ static Tileset *readTileset(XML::Node node, const std::string &path,
std::string sourceStr = resolveRelativePath(pathDir, source);
ResourceManager *resman = ResourceManager::getInstance();
- auto tilebmp = resman->getImageRef(sourceStr);
+ auto tilebmp = resman->getImage(sourceStr);
if (tilebmp)
{
diff --git a/src/resources/mapreader.h b/src/resources/mapreader.h
index 105c5d1d..e646fb04 100644
--- a/src/resources/mapreader.h
+++ b/src/resources/mapreader.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef MAPREADER_H
-#define MAPREADER_H
+#pragma once
#include "utils/xml.h"
@@ -45,5 +44,3 @@ public:
*/
static Map *readMap(XML::Node node, const std::string &path);
};
-
-#endif // MAPREADER_H
diff --git a/src/resources/monsterdb.cpp b/src/resources/monsterdb.cpp
index 215ca2f8..4963f93f 100644
--- a/src/resources/monsterdb.cpp
+++ b/src/resources/monsterdb.cpp
@@ -54,7 +54,7 @@ void MonsterDB::init()
unload();
// This can be overridden by an 'offset' attribute on a 'monsters' root tag.
- mMonsterIdOffset = Net::getNetworkType() == ServerType::TMWATHENA ? OLD_TMWATHENA_OFFSET : 0;
+ mMonsterIdOffset = Net::getNetworkType() == ServerType::TmwAthena ? OLD_TMWATHENA_OFFSET : 0;
}
void MonsterDB::setMonsterIdOffset(int offset)
@@ -95,19 +95,19 @@ void MonsterDB::readMonsterNode(XML::Node node, const std::string &filename)
if (event == "hit")
{
- currentInfo->addSound(SoundEvent::HIT, soundFile);
+ currentInfo->addSound(SoundEvent::Hit, soundFile);
}
else if (event == "miss")
{
- currentInfo->addSound(SoundEvent::MISS, soundFile);
+ currentInfo->addSound(SoundEvent::Miss, soundFile);
}
else if (event == "hurt")
{
- currentInfo->addSound(SoundEvent::HURT, soundFile);
+ currentInfo->addSound(SoundEvent::Hurt, soundFile);
}
else if (event == "die")
{
- currentInfo->addSound(SoundEvent::DIE, soundFile);
+ currentInfo->addSound(SoundEvent::Die, soundFile);
}
else
{
diff --git a/src/resources/monsterdb.h b/src/resources/monsterdb.h
index ff709486..ec71952f 100644
--- a/src/resources/monsterdb.h
+++ b/src/resources/monsterdb.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef MONSTER_DB_H
-#define MONSTER_DB_H
+#pragma once
#include "utils/xml.h"
@@ -43,5 +42,3 @@ namespace MonsterDB
BeingInfo *get(int id);
}
-
-#endif
diff --git a/src/resources/music.cpp b/src/resources/music.cpp
index 12c723bd..069af588 100644
--- a/src/resources/music.cpp
+++ b/src/resources/music.cpp
@@ -33,7 +33,7 @@ Music::~Music()
Mix_FreeMusic(mMusic);
}
-Resource *Music::load(SDL_RWops *rw)
+Music *Music::load(SDL_RWops *rw)
{
if (Mix_Music *music = Mix_LoadMUS_RW(rw, 1))
{
diff --git a/src/resources/music.h b/src/resources/music.h
index 0c445b2b..d22257da 100644
--- a/src/resources/music.h
+++ b/src/resources/music.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef MUSIC_H
-#define MUSIC_H
+#pragma once
#include "resources/resource.h"
@@ -39,10 +38,10 @@ class Music : public Resource
*
* @param rw The SDL_RWops to load the music data from.
*
- * @return <code>NULL</code> if the an error occurred, a valid pointer
- * otherwise.
+ * @return <code>nullptr</code> if the an error occurred, a valid
+ * pointer otherwise.
*/
- static Resource *load(SDL_RWops *rw);
+ static Music *load(SDL_RWops *rw);
/**
* Plays the music.
@@ -61,5 +60,3 @@ class Music : public Resource
Mix_Music *mMusic;
};
-
-#endif
diff --git a/src/resources/npcdb.h b/src/resources/npcdb.h
index 306167de..779f4919 100644
--- a/src/resources/npcdb.h
+++ b/src/resources/npcdb.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NPC_DB_H
-#define NPC_DB_H
+#pragma once
#include <string>
#include "utils/xml.h"
@@ -42,5 +41,3 @@ namespace NPCDB
BeingInfo *get(int id);
}
-
-#endif
diff --git a/src/resources/resource.h b/src/resources/resource.h
index e1f37d73..ba8d17cc 100644
--- a/src/resources/resource.h
+++ b/src/resources/resource.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef RESOURCE_H
-#define RESOURCE_H
+#pragma once
#include <ctime>
#include <string>
@@ -151,11 +150,13 @@ public:
* This is currently necessary to avoid calls to decRef on instances of
* SubImage, which are not reference counted resources.
*/
- void release()
- { mResource = nullptr; }
+ RESOURCE *release()
+ {
+ RESOURCE *resource = mResource;
+ mResource = nullptr;
+ return resource;
+ }
private:
RESOURCE *mResource;
};
-
-#endif
diff --git a/src/resources/resourcemanager.cpp b/src/resources/resourcemanager.cpp
index ff83f422..e62407e3 100644
--- a/src/resources/resourcemanager.cpp
+++ b/src/resources/resourcemanager.cpp
@@ -31,10 +31,7 @@
#include "resources/soundeffect.h"
#include "resources/spritedef.h"
-#include "utils/zlib.h"
-#include "utils/physfsrwops.h"
-
-#include <physfs.h>
+#include "utils/filesystem.h"
#include <SDL_image.h>
@@ -47,56 +44,42 @@
ResourceManager *ResourceManager::instance = nullptr;
ResourceManager::ResourceManager()
- : mOldestOrphan(0)
{
logger->log("Initializing resource manager...");
}
ResourceManager::~ResourceManager()
{
- mResources.insert(mOrphanedResources.begin(), mOrphanedResources.end());
-
- // Release any remaining spritedefs first because they depend on image sets
- auto iter = mResources.begin();
- while (iter != mResources.end())
+ auto cleanupResources = [&](auto match)
{
- if (dynamic_cast<SpriteDef*>(iter->second) != nullptr)
- {
- cleanUp(iter->second);
- auto toErase = iter;
- ++iter;
- mResources.erase(toErase);
- }
- else
- {
- ++iter;
- }
- }
+ // Include any orphaned resources into the main list for cleanup
+ mResources.insert(mOrphanedResources.begin(), mOrphanedResources.end());
+ mOrphanedResources.clear();
- // Release any remaining image sets first because they depend on images
- iter = mResources.begin();
- while (iter != mResources.end())
- {
- if (dynamic_cast<ImageSet*>(iter->second) != nullptr)
- {
- cleanUp(iter->second);
- auto toErase = iter;
- ++iter;
- mResources.erase(toErase);
- }
- else
+ for (auto iter = mResources.begin(); iter != mResources.end(); )
{
- ++iter;
+ if (match(iter->second))
+ {
+ cleanUp(iter->second);
+ iter = mResources.erase(iter);
+ }
+ else
+ {
+ ++iter;
+ }
}
- }
+ };
- // Release remaining resources, logging the number of dangling references.
- iter = mResources.begin();
- while (iter != mResources.end())
- {
- cleanUp(iter->second);
- ++iter;
- }
+ // SpriteDef references ImageSet
+ cleanupResources([](Resource *res) { return dynamic_cast<SpriteDef *>(res); });
+
+ // ImageSet references Image
+ cleanupResources([](Resource *res) { return dynamic_cast<ImageSet *>(res); });
+
+ // Release remaining resources
+ cleanupResources([](Resource *res) { return true; });
+
+ assert(mOrphanedResources.empty());
}
void ResourceManager::cleanUp(Resource *res)
@@ -117,7 +100,7 @@ void ResourceManager::cleanOrphans()
{
// Delete orphaned resources after 30 seconds.
time_t oldest = time(nullptr);
- time_t threshold = oldest - 30;
+ const time_t threshold = oldest - 30;
if (mOrphanedResources.empty() || mOldestOrphan >= threshold)
return;
@@ -144,17 +127,12 @@ void ResourceManager::cleanOrphans()
mOldestOrphan = oldest;
}
-bool ResourceManager::setWriteDir(const std::string &path)
-{
- return (bool) PHYSFS_setWriteDir(path.c_str());
-}
-
bool ResourceManager::addToSearchPath(const std::string &path, bool append)
{
logger->log("Adding to PhysicsFS: %s", path.c_str());
- if (!PHYSFS_mount(path.c_str(), nullptr, append ? 1 : 0))
+ if (!FS::addToSearchPath(path, append))
{
- logger->log("Error: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
+ logger->log("Error: %s", FS::getLastError());
return false;
}
return true;
@@ -164,58 +142,33 @@ void ResourceManager::searchAndAddArchives(const std::string &path,
const std::string &ext,
bool append)
{
- const char *dirSep = PHYSFS_getDirSeparator();
- char **list = PHYSFS_enumerateFiles(path.c_str());
+ const char *dirSep = FS::getDirSeparator();
- for (char **i = list; *i; i++)
+ for (auto fileName : FS::enumerateFiles(path))
{
- size_t len = strlen(*i);
+ const size_t len = strlen(fileName);
- if (len > ext.length() && !ext.compare((*i)+(len - ext.length())))
+ if (len > ext.length() && ext != (fileName + (len - ext.length())))
{
- std::string file, realPath, archive;
-
- file = path + (*i);
- realPath = std::string(PHYSFS_getRealDir(file.c_str()));
- archive = realPath + dirSep + file;
-
- addToSearchPath(archive, append);
+ std::string file = path + fileName;
+ if (auto realDir = FS::getRealDir(file))
+ {
+ std::string archive = std::string(*realDir) + dirSep + file;
+ addToSearchPath(archive, append);
+ }
}
}
-
- PHYSFS_freeList(list);
-}
-
-bool ResourceManager::mkdir(const std::string &path)
-{
- return (bool) PHYSFS_mkdir(path.c_str());
-}
-
-bool ResourceManager::exists(const std::string &path)
-{
- return PHYSFS_exists(path.c_str());
-}
-
-bool ResourceManager::isDirectory(const std::string &path)
-{
- PHYSFS_Stat stat;
- if (PHYSFS_stat(path.c_str(), &stat) != 0)
- {
- return stat.filetype == PHYSFS_FILETYPE_DIRECTORY;
- }
- return false;
}
std::string ResourceManager::getPath(const std::string &file)
{
- // get the real path to the file
- const char* tmp = PHYSFS_getRealDir(file.c_str());
+ // Get the real directory of the file
+ auto realDir = FS::getRealDir(file);
std::string path;
- // if the file is not in the search path, then its NULL
- if (tmp)
+ if (realDir)
{
- path = std::string(tmp) + "/" + file;
+ path = std::string(*realDir) + "/" + file;
}
else
{
@@ -226,11 +179,6 @@ std::string ResourceManager::getPath(const std::string &file)
return path;
}
-SDL_RWops *ResourceManager::open(const std::string &path)
-{
- return PHYSFSRWOPS_openRead(path.c_str());
-}
-
Resource *ResourceManager::get(const std::string &idPath,
const std::function<Resource *()> &generator)
{
@@ -238,7 +186,6 @@ Resource *ResourceManager::get(const std::string &idPath,
auto resIter = mResources.find(idPath);
if (resIter != mResources.end())
{
- resIter->second->incRef();
return resIter->second;
}
@@ -248,44 +195,41 @@ Resource *ResourceManager::get(const std::string &idPath,
Resource *res = resIter->second;
mResources.insert(*resIter);
mOrphanedResources.erase(resIter);
- res->incRef();
return res;
}
Resource *resource = generator();
-
if (resource)
{
- resource->incRef();
resource->mIdPath = idPath;
mResources[idPath] = resource;
cleanOrphans();
}
- // Returns NULL if the object could not be created.
return resource;
}
-Resource *ResourceManager::get(const std::string &path, loader fun)
+ResourceRef<Music> ResourceManager::getMusic(const std::string &path)
{
- return get(path, [&] () -> Resource * {
- if (SDL_RWops *rw = open(path))
- return fun(rw);
+ return static_cast<Music*>(get(path, [&] () -> Resource * {
+ if (SDL_RWops *rw = FS::openBufferedRWops(path))
+ return Music::load(rw);
+
return nullptr;
- });
+ }));
}
-Music *ResourceManager::getMusic(const std::string &idPath)
+ResourceRef<SoundEffect> ResourceManager::getSoundEffect(const std::string &path)
{
- return static_cast<Music*>(get(idPath, Music::load));
-}
+ return static_cast<SoundEffect*>(get(path, [&] () -> Resource * {
+ if (SDL_RWops *rw = FS::openBufferedRWops(path))
+ return SoundEffect::load(rw);
-SoundEffect *ResourceManager::getSoundEffect(const std::string &idPath)
-{
- return static_cast<SoundEffect*>(get(idPath, SoundEffect::load));
+ return nullptr;
+ }));
}
-Image *ResourceManager::getImage(const std::string &idPath)
+ResourceRef<Image> ResourceManager::getImage(const std::string &idPath)
{
return static_cast<Image*>(get(idPath, [&] () -> Resource * {
std::string path = idPath;
@@ -296,7 +240,7 @@ Image *ResourceManager::getImage(const std::string &idPath)
d = std::make_unique<Dye>(path.substr(p + 1));
path = path.substr(0, p);
}
- SDL_RWops *rw = open(path);
+ SDL_RWops *rw = FS::openRWops(path);
if (!rw)
return nullptr;
@@ -306,21 +250,14 @@ Image *ResourceManager::getImage(const std::string &idPath)
}));
}
-ResourceRef<Image> ResourceManager::getImageRef(const std::string &idPath)
-{
- ResourceRef<Image> img = getImage(idPath);
- img->decRef(); // remove ref added by ResourceManager::get
- return img;
-}
-
-ImageSet *ResourceManager::getImageSet(const std::string &imagePath,
- int w, int h)
+ResourceRef<ImageSet> ResourceManager::getImageSet(const std::string &imagePath,
+ int w, int h)
{
std::stringstream ss;
ss << imagePath << "[" << w << "x" << h << "]";
return static_cast<ImageSet*>(get(ss.str(), [&] () -> Resource * {
- auto img = getImageRef(imagePath);
+ auto img = getImage(imagePath);
if (!img)
return nullptr;
@@ -328,7 +265,7 @@ ImageSet *ResourceManager::getImageSet(const std::string &imagePath,
}));
}
-SpriteDef *ResourceManager::getSprite(const std::string &path, int variant)
+ResourceRef<SpriteDef> ResourceManager::getSprite(const std::string &path, int variant)
{
std::stringstream ss;
ss << path << "[" << variant << "]";
@@ -373,101 +310,3 @@ void ResourceManager::deleteInstance()
delete instance;
instance = nullptr;
}
-
-void *ResourceManager::loadFile(const std::string &filename, int &filesize,
- bool inflate)
-{
- // Attempt to open the specified file using PhysicsFS
- PHYSFS_file *file = PHYSFS_openRead(filename.c_str());
-
- // If the handler is an invalid pointer indicate failure
- if (file == nullptr)
- {
- logger->log("Warning: Failed to load %s: %s",
- filename.c_str(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
- return nullptr;
- }
-
- // Log the real dir of the file
- logger->log("Loaded %s/%s", PHYSFS_getRealDir(filename.c_str()),
- filename.c_str());
-
- // Get the size of the file
- filesize = PHYSFS_fileLength(file);
-
- // Allocate memory and load the file
- void *buffer = malloc(filesize);
- PHYSFS_readBytes(file, buffer, filesize);
-
- // Close the file and let the user deallocate the memory
- PHYSFS_close(file);
-
- if (inflate && filename.find(".gz", filename.length() - 3)
- != std::string::npos)
- {
- unsigned char *inflated;
-
- // Inflate the gzipped map data
- filesize = inflateMemory((unsigned char*) buffer, filesize, inflated);
- free(buffer);
-
- buffer = inflated;
-
- if (!buffer)
- {
- logger->log("Could not decompress file: %s", filename.c_str());
- }
- }
-
- return buffer;
-}
-
-bool ResourceManager::copyFile(const std::string &src, const std::string &dst)
-{
- PHYSFS_file *srcFile = PHYSFS_openRead(src.c_str());
- if (!srcFile)
- {
- logger->log("Read error: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
- return false;
- }
- PHYSFS_file *dstFile = PHYSFS_openWrite(dst.c_str());
- if (!dstFile)
- {
- logger->log("Write error: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
- PHYSFS_close(srcFile);
- return false;
- }
-
- int fileSize = PHYSFS_fileLength(srcFile);
- void *buf = malloc(fileSize);
- PHYSFS_readBytes(srcFile, buf, fileSize);
- PHYSFS_writeBytes(dstFile, buf, fileSize);
-
- PHYSFS_close(srcFile);
- PHYSFS_close(dstFile);
- free(buf);
- return true;
-}
-
-std::vector<std::string> ResourceManager::loadTextFile(
- const std::string &fileName)
-{
- int contentsLength;
- char *fileContents = (char*)loadFile(fileName, contentsLength);
- std::vector<std::string> lines;
-
- if (!fileContents)
- {
- logger->log("Couldn't load text file: %s", fileName.c_str());
- return lines;
- }
-
- std::istringstream iss(std::string(fileContents, contentsLength));
- std::string line;
-
- while (getline(iss, line))
- lines.push_back(line);
-
- free(fileContents);
- return lines;
-}
diff --git a/src/resources/resourcemanager.h b/src/resources/resourcemanager.h
index d1c32d8c..728a9b74 100644
--- a/src/resources/resourcemanager.h
+++ b/src/resources/resourcemanager.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef RESOURCE_MANAGER_H
-#define RESOURCE_MANAGER_H
+#pragma once
#include "resources/resource.h"
@@ -28,7 +27,6 @@
#include <functional>
#include <map>
#include <string>
-#include <vector>
class Image;
class ImageSet;
@@ -36,8 +34,6 @@ class Music;
class SoundEffect;
class SpriteDef;
-struct SDL_RWops;
-
/**
* A class for loading and managing resources.
*/
@@ -46,9 +42,6 @@ class ResourceManager
friend class Resource;
public:
-
- using loader = Resource *(*)(SDL_RWops *);
-
ResourceManager();
/**
@@ -58,43 +51,20 @@ class ResourceManager
~ResourceManager();
/**
- * Sets the write directory.
- *
- * @param path The path of the directory to be added.
- * @return <code>true</code> on success, <code>false</code> otherwise.
- */
- bool setWriteDir(const std::string &path);
-
- /**
* Adds a directory or archive to the search path. If append is true
* then the directory is added to the end of the search path, otherwise
* it is added at the front.
*
* @return <code>true</code> on success, <code>false</code> otherwise.
*/
- bool addToSearchPath(const std::string &path, bool append);
+ static bool addToSearchPath(const std::string &path, bool append);
/**
* Searches for zip files and adds them to the search path.
*/
- void searchAndAddArchives(const std::string &path,
- const std::string &ext,
- bool append);
-
- /**
- * Creates a directory in the write path
- */
- bool mkdir(const std::string &path);
-
- /**
- * Checks whether the given file or directory exists in the search path
- */
- bool exists(const std::string &path);
-
- /**
- * Checks whether the given path is a directory.
- */
- bool isDirectory(const std::string &path);
+ static void searchAndAddArchives(const std::string &path,
+ const std::string &ext,
+ bool append);
/**
* Returns the real path to a file. Note that this method will always
@@ -103,106 +73,34 @@ class ResourceManager
* @param file The file to get the real path to.
* @return The real path.
*/
- std::string getPath(const std::string &file);
-
- /**
- * Opens a file for reading. The caller is responsible for closing the
- * file.
- *
- * @param path The file name.
- * @return A valid SDL_RWops pointer or <code>NULL</code> if the file
- * could not be opened.
- */
- SDL_RWops *open(const std::string &path);
-
- /**
- * Creates a resource and adds it to the resource map.
- *
- * @param idPath The resource identifier path.
- * @param fun A function for generating the resource.
- * @param data Extra parameters for the generator.
- * @return A valid resource or <code>NULL</code> if the resource could
- * not be generated.
- */
- Resource *get(const std::string &idPath,
- const std::function<Resource *()> &generator);
-
- /**
- * Loads a resource from a file and adds it to the resource map.
- *
- * @param path The file name.
- * @param fun A function for parsing the file.
- * @return A valid resource or <code>NULL</code> if the resource could
- * not be loaded.
- */
- Resource *get(const std::string &path, loader fun);
-
- /**
- * Convenience wrapper around ResourceManager::get for loading
- * images.
- */
- Image *getImage(const std::string &idPath);
+ static std::string getPath(const std::string &file);
/**
- * Convenience wrapper around ResourceManager::get for loading
- * images. Returns an automatically reference-counted resource.
+ * Loads the Image resource found at the given identifier path. The
+ * path can include a dye specification after a '|' character.
*/
- ResourceRef<Image> getImageRef(const std::string &idPath);
+ ResourceRef<Image> getImage(const std::string &idPath);
/**
- * Convenience wrapper around ResourceManager::get for loading
- * songs.
+ * Loads the Music resource found at the given path.
*/
- Music *getMusic(const std::string &idPath);
+ ResourceRef<Music> getMusic(const std::string &path);
/**
- * Convenience wrapper around ResourceManager::get for loading
- * samples.
+ * Loads the SoundEffect resource found at the given path.
*/
- SoundEffect *getSoundEffect(const std::string &idPath);
+ ResourceRef<SoundEffect> getSoundEffect(const std::string &path);
/**
- * Creates a image set based on the image referenced by the given
- * path and the supplied sprite sizes
+ * Loads a image set based on the image referenced by the given path
+ * and the supplied sprite sizes.
*/
- ImageSet *getImageSet(const std::string &imagePath, int w, int h);
+ ResourceRef<ImageSet> getImageSet(const std::string &imagePath, int w, int h);
/**
- * Creates a sprite definition based on a given path and the supplied
- * variant.
+ * Loads a SpriteDef based on a given path and the supplied variant.
*/
- SpriteDef *getSprite(const std::string &path, int variant = 0);
-
- /**
- * Allocates data into a buffer pointer for raw data loading. The
- * returned data is expected to be freed using <code>free()</code>.
- *
- * @param filename The name of the file to be loaded.
- * @param filesize The size of the file that was loaded.
- * @param inflate True to uncompress the file if the filename ends in
- * ".gz", false to ignore that.
- *
- * @return An allocated byte array containing the data that was loaded,
- * or <code>NULL</code> on fail.
- */
- void *loadFile(const std::string &filename, int &filesize,
- bool inflate = true);
-
- /**
- * Copies a file from one place to another (useful for extracting
- * raw files from a zip archive, for example)
- *
- * @param src Source file name
- * @param dst Destination file name
- * @return true on success, false on failure. An error message should be
- * in the log file.
- */
- bool copyFile(const std::string &src, const std::string &dst);
-
- /**
- * Retrieves the contents of a text file.
- */
- std::vector<std::string> loadTextFile(const std::string &fileName);
+ ResourceRef<SpriteDef> getSprite(const std::string &path, int variant = 0);
/**
* Returns an instance of the class, creating one if it does not
@@ -217,6 +115,19 @@ class ResourceManager
private:
/**
+ * Looks up a resource, creating it with the generator function if it
+ * does not exist. Does not increment the reference count of the
+ * resource.
+ *
+ * @param idPath The resource identifier path.
+ * @param generator A function for generating the resource.
+ * @return A valid resource or <code>nullptr</code> if the resource could
+ * not be generated.
+ */
+ Resource *get(const std::string &idPath,
+ const std::function<Resource *()> &generator);
+
+ /**
* Releases a resource, placing it in the set of orphaned resources.
* Only called from Resource::decRef,
*/
@@ -238,7 +149,5 @@ class ResourceManager
static ResourceManager *instance;
std::map<std::string, Resource *> mResources;
std::map<std::string, Resource *> mOrphanedResources;
- time_t mOldestOrphan;
+ time_t mOldestOrphan = 0;
};
-
-#endif
diff --git a/src/resources/settingsmanager.cpp b/src/resources/settingsmanager.cpp
index 8966f976..9323d4d1 100644
--- a/src/resources/settingsmanager.cpp
+++ b/src/resources/settingsmanager.cpp
@@ -20,22 +20,23 @@
#include "resources/settingsmanager.h"
-#include "configuration.h"
#include "resources/attributes.h"
+#include "resources/emotedb.h"
#include "resources/hairdb.h"
#include "resources/itemdb.h"
#include "resources/monsterdb.h"
-#include "resources/specialdb.h"
#include "resources/npcdb.h"
-#include "resources/emotedb.h"
-#include "statuseffect.h"
-#include "units.h"
+#include "resources/abilitydb.h"
+#include "resources/statuseffectdb.h"
#include "net/net.h"
#include "utils/xml.h"
#include "utils/path.h"
+
+#include "configuration.h"
#include "log.h"
+#include "units.h"
namespace SettingsManager
{
@@ -52,10 +53,10 @@ namespace SettingsManager
hairDB.init();
itemDb->init();
MonsterDB::init();
- SpecialDB::init();
+ AbilityDB::init();
NPCDB::init();
EmoteDB::init();
- StatusEffect::init();
+ StatusEffectDB::init();
Units::init();
// load stuff from settings
@@ -76,13 +77,13 @@ namespace SettingsManager
hairDB.checkStatus();
itemDb->checkStatus();
MonsterDB::checkStatus();
- SpecialDB::checkStatus();
+ AbilityDB::checkStatus();
NPCDB::checkStatus();
EmoteDB::checkStatus();
- StatusEffect::checkStatus();
+ StatusEffectDB::checkStatus();
Units::checkStatus();
- if (Net::getNetworkType() == ServerType::MANASERV)
+ if (Net::getNetworkType() == ServerType::ManaServ)
{
Attributes::informItemDB();
}
@@ -90,10 +91,10 @@ namespace SettingsManager
void unload()
{
- StatusEffect::unload();
+ StatusEffectDB::unload();
EmoteDB::unload();
NPCDB::unload();
- SpecialDB::unload();
+ AbilityDB::unload();
MonsterDB::unload();
if (itemDb)
itemDb->unload();
@@ -180,7 +181,6 @@ namespace SettingsManager
}
else if (childNode.name() == "attribute")
{
- // map config
Attributes::readAttributeNode(childNode, filename);
}
else if (childNode.name() == "points")
@@ -213,9 +213,9 @@ namespace SettingsManager
{
MonsterDB::readMonsterNode(childNode, filename);
}
- else if (childNode.name() == "special-set")
+ else if (childNode.name() == "ability")
{
- SpecialDB::readSpecialSetNode(childNode, filename);
+ AbilityDB::readAbilityNode(childNode, filename);
}
else if (childNode.name() == "npc")
{
@@ -225,23 +225,14 @@ namespace SettingsManager
{
EmoteDB::readEmoteNode(childNode, filename);
}
- else if (childNode.name() == "status-effect" || childNode.name() == "stun-effect")
+ else if (childNode.name() == "status-effect")
{
- StatusEffect::readStatusEffectNode(childNode, filename);
+ StatusEffectDB::readStatusEffectNode(childNode, filename);
}
else if (childNode.name() == "unit")
{
Units::readUnitNode(childNode, filename);
}
- else
- {
- // compatibility stuff with older configs/games
- if (node.name() == "specials" && childNode.name() == "set")
- {
- // specials.xml:/specials/set
- SpecialDB::readSpecialSetNode(childNode, filename);
- }
- }
}
mIncludedFiles.erase(filename);
diff --git a/src/resources/settingsmanager.h b/src/resources/settingsmanager.h
index 25feb86b..5b70f865 100644
--- a/src/resources/settingsmanager.h
+++ b/src/resources/settingsmanager.h
@@ -18,8 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SETTINGSMANAGER_HPP
-#define SETTINGSMANAGER_HPP
+#pragma once
#include <string>
#include <list>
@@ -30,6 +29,3 @@ namespace SettingsManager
void load();
void unload();
}
-
-
-#endif // SETTINGSMANAGER_HPP
diff --git a/src/resources/soundeffect.cpp b/src/resources/soundeffect.cpp
index 8f8cdfc5..19d7a820 100644
--- a/src/resources/soundeffect.cpp
+++ b/src/resources/soundeffect.cpp
@@ -28,23 +28,20 @@ SoundEffect::~SoundEffect()
Mix_FreeChunk(mChunk);
}
-Resource *SoundEffect::load(SDL_RWops *rw)
+SoundEffect *SoundEffect::load(SDL_RWops *rw)
{
// Load the music data and free the RWops structure
- Mix_Chunk *tmpSoundEffect = Mix_LoadWAV_RW(rw, 1);
-
- if (tmpSoundEffect)
+ if (Mix_Chunk *soundEffect = Mix_LoadWAV_RW(rw, 1))
{
- return new SoundEffect(tmpSoundEffect);
+ return new SoundEffect(soundEffect);
}
logger->log("Error, failed to load sound effect: %s", Mix_GetError());
return nullptr;
}
-bool SoundEffect::play(int loops, int volume, int channel)
+int SoundEffect::play(int loops, int volume, int channel)
{
Mix_VolumeChunk(mChunk, volume);
-
- return Mix_PlayChannel(channel, mChunk, loops) != -1;
+ return Mix_PlayChannel(channel, mChunk, loops);
}
diff --git a/src/resources/soundeffect.h b/src/resources/soundeffect.h
index eada80b5..9ca8490d 100644
--- a/src/resources/soundeffect.h
+++ b/src/resources/soundeffect.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SOUND_EFFECT_H
-#define SOUND_EFFECT_H
+#pragma once
#include "resources/resource.h"
@@ -37,12 +36,12 @@ class SoundEffect : public Resource
/**
* Loads a sample from a buffer in memory.
*
- * @param rw The SDL_RWops to load the sample data from.
+ * @param rw The SDL_RWops to load the sample data from.
*
- * @return <code>NULL</code> if the an error occurred, a valid pointer
+ * @return <code>nullptr</code> if the an error occurred, a valid pointer
* otherwise.
*/
- static Resource *load(SDL_RWops *rw);
+ static SoundEffect *load(SDL_RWops *rw);
/**
* Plays the sample.
@@ -51,15 +50,13 @@ class SoundEffect : public Resource
* @param volume Sample playback volume.
* @param channel Sample playback channel.
*
- * @return <code>true</code> if the playback started properly
- * <code>false</code> otherwise.
+ * @return which channel was used to play the sound, or -1 if sound could not
+ * be played.
*/
- bool play(int loops, int volume, int channel = -1);
+ int play(int loops, int volume, int channel = -1);
protected:
SoundEffect(Mix_Chunk *soundEffect): mChunk(soundEffect) {}
Mix_Chunk *mChunk;
};
-
-#endif // SOUND_EFFECT_H
diff --git a/src/resources/specialdb.cpp b/src/resources/specialdb.cpp
deleted file mode 100644
index ec0b3f2f..00000000
--- a/src/resources/specialdb.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * The Mana Client
- * Copyright (C) 2010-2013 The Mana Developers
- *
- * This file is part of The Mana Client.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "resources/specialdb.h"
-
-#include "log.h"
-
-#include "utils/dtor.h"
-
-#include <map>
-
-namespace
-{
- std::map<int, SpecialInfo *> mSpecialInfos;
- bool mLoaded = false;
-}
-
-SpecialInfo::TargetMode SpecialDB::targetModeFromString(const std::string& str)
-{
- if (str == "being")
- return SpecialInfo::TARGET_BEING;
- if (str == "point")
- return SpecialInfo::TARGET_POINT;
-
- logger->log("SpecialDB: Warning, unknown target mode \"%s\"", str.c_str() );
- return SpecialInfo::TARGET_BEING;
-}
-
-
-void SpecialDB::init()
-{
- if (mLoaded)
- unload();
-}
-
-void SpecialDB::readSpecialSetNode(XML::Node node, const std::string &filename)
-{
- std::string setName = node.getProperty("name", "Actions");
-
- for (auto special : node.children())
- {
- if (special.name() == "special")
- {
- auto *info = new SpecialInfo();
- int id = special.getProperty("id", 0);
- info->id = id;
- info->set = setName;
- info->name = special.getProperty("name", "");
- info->icon = special.getProperty("icon", "");
-
- info->targetMode = targetModeFromString(special.getProperty("target", "being"));
-
- info->rechargeable = special.getBoolProperty("rechargeable", true);
- info->rechargeNeeded = 0;
- info->rechargeCurrent = 0;
-
- if (mSpecialInfos.find(id) != mSpecialInfos.end())
- {
- logger->log("SpecialDB: Duplicate special ID %d in %s, ignoring", id, filename.c_str());
- } else {
- mSpecialInfos[id] = info;
- }
- }
- }
-
-}
-
-void SpecialDB::checkStatus()
-{
- mLoaded = true;
-}
-
-
-void SpecialDB::unload()
-{
-
- delete_all(mSpecialInfos);
- mSpecialInfos.clear();
-
- mLoaded = false;
-}
-
-
-SpecialInfo *SpecialDB::get(int id)
-{
-
- auto i = mSpecialInfos.find(id);
-
- if (i == mSpecialInfos.end())
- {
- return nullptr;
- }
- else
- {
- return i->second;
- }
- return nullptr;
-}
diff --git a/src/resources/spritedef.cpp b/src/resources/spritedef.cpp
index f42e623e..85e5e566 100644
--- a/src/resources/spritedef.cpp
+++ b/src/resources/spritedef.cpp
@@ -28,7 +28,6 @@
#include "resources/animation.h"
#include "resources/dye.h"
#include "resources/image.h"
-#include "resources/imageset.h"
#include "resources/resourcemanager.h"
#include "configuration.h"
@@ -155,8 +154,7 @@ void SpriteDef::loadImageSet(XML::Node node, const std::string &palettes)
Dye::instantiate(imageSrc, palettes);
ResourceManager *resman = ResourceManager::getInstance();
- ImageSet *imageSet = resman->getImageSet(imageSrc, width, height);
-
+ auto imageSet = resman->getImageSet(imageSrc, width, height);
if (!imageSet)
{
logger->error(strprintf("Couldn't load imageset (%s)!",
@@ -328,11 +326,6 @@ SpriteDef::~SpriteDef()
{
delete action;
}
-
- for (auto &imageSet : mImageSets)
- {
- imageSet.second->decRef();
- }
}
SpriteDirection SpriteDef::makeSpriteDirection(const std::string &direction)
diff --git a/src/resources/spritedef.h b/src/resources/spritedef.h
index fa44deea..0d48d145 100644
--- a/src/resources/spritedef.h
+++ b/src/resources/spritedef.h
@@ -19,10 +19,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SPRITEDEF_H
-#define SPRITEDEF_H
+#pragma once
-#include "resources/resource.h"
+#include "resources/imageset.h"
#include "utils/xml.h"
@@ -50,7 +49,7 @@ struct SpriteDisplay
* Remember those are the main action.
* Action subtypes, e.g.: "attack_bow" are to be passed by items.xml after
* an ACTION_ATTACK call.
- * Which special to be use to to be passed with the USE_SPECIAL call.
+ * Which ability to be use to to be passed with the USE_ABILITY call.
* Running, walking, ... is a sub-type of moving.
* ...
* Please don't add hard-coded subtypes here!
@@ -65,7 +64,7 @@ namespace SpriteAction
static const std::string MOVE = "walk";
static const std::string ATTACK = "attack";
static const std::string HURT = "hurt";
- static const std::string USE_SPECIAL = "special";
+ static const std::string USE_ABILITY = "ability";
static const std::string CAST_MAGIC = "magic";
static const std::string USE_ITEM = "item";
static const std::string INVALID;
@@ -147,8 +146,6 @@ class SpriteDef : public Resource
*/
void substituteAction(std::string complete, std::string with);
- std::map<std::string, ImageSet *> mImageSets;
+ std::map<std::string, ResourceRef<ImageSet>> mImageSets;
std::map<std::string, Action *> mActions;
};
-
-#endif // SPRITEDEF_H
diff --git a/src/resources/statuseffectdb.cpp b/src/resources/statuseffectdb.cpp
new file mode 100644
index 00000000..fe179191
--- /dev/null
+++ b/src/resources/statuseffectdb.cpp
@@ -0,0 +1,96 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2025 The Mana Developers
+ *
+ * This file is part of The Mana Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "statuseffectdb.h"
+
+bool StatusEffectDB::mLoaded = false;
+std::map<int, StatusEffect> StatusEffectDB::mStatusEffects;
+StatusEffectDB::OptionsMap StatusEffectDB::mOpt0ToIdMap;
+StatusEffectDB::OptionsMap StatusEffectDB::mOpt1ToIdMap;
+StatusEffectDB::OptionsMap StatusEffectDB::mOpt2ToIdMap;
+StatusEffectDB::OptionsMap StatusEffectDB::mOpt3ToIdMap;
+
+
+const StatusEffect *StatusEffectDB::getStatusEffect(int id)
+{
+ auto it = mStatusEffects.find(id);
+ if (it == mStatusEffects.end())
+ return nullptr;
+ return &it->second;
+}
+
+void StatusEffectDB::init()
+{
+ if (mLoaded)
+ unload();
+}
+
+void StatusEffectDB::readStatusEffectNode(XML::Node node, const std::string &/* filename */)
+{
+ const int id = node.getProperty("id", -1);
+
+ const int opt0 = node.getProperty("option", 0);
+ const int opt1 = node.getProperty("opt1", 0);
+ const int opt2 = node.getProperty("opt2", 0);
+ const int opt3 = node.getProperty("opt3", 0);
+ if (opt0 != 0 && opt0 <= UINT16_MAX)
+ mOpt0ToIdMap[opt0] = id;
+ if (opt1 != 0 && opt1 <= UINT16_MAX)
+ mOpt1ToIdMap[opt1] = id;
+ if (opt2 != 0 && opt2 <= UINT16_MAX)
+ mOpt2ToIdMap[opt2] = id;
+ if (opt3 != 0 && opt3 <= UINT16_MAX)
+ mOpt3ToIdMap[opt3] = id;
+
+ auto &effect = mStatusEffects[id];
+
+ node.attribute("name", effect.name);
+
+ node.attribute("start-message", effect.start.message);
+ node.attribute("start-audio", effect.start.sfx);
+ node.attribute("start-particle", effect.start.particleEffect);
+
+ // For now we don't support separate particle effect for "already applied"
+ // status effects.
+ if (effect.start.particleEffect.empty())
+ node.attribute("particle", effect.start.particleEffect);
+
+ node.attribute("end-message", effect.end.message);
+ node.attribute("end-audio", effect.end.sfx);
+ node.attribute("end-particle", effect.end.particleEffect);
+
+ node.attribute("icon", effect.icon);
+ node.attribute("persistent-particle-effect", effect.persistentParticleEffect);
+}
+
+void StatusEffectDB::checkStatus()
+{
+ mLoaded = true;
+}
+
+void StatusEffectDB::unload()
+{
+ if (!mLoaded)
+ return;
+
+ mStatusEffects.clear();
+ mLoaded = false;
+}
diff --git a/src/resources/statuseffectdb.h b/src/resources/statuseffectdb.h
new file mode 100644
index 00000000..d1f1a6bf
--- /dev/null
+++ b/src/resources/statuseffectdb.h
@@ -0,0 +1,67 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2025 The Mana Developers
+ *
+ * This file is part of The Mana Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef STATUSEFFECTDB_H
+#define STATUSEFFECTDB_H
+
+#include "statuseffect.h"
+#include "utils/xml.h"
+
+#include <cstdint>
+#include <map>
+
+class StatusEffectDB
+{
+public:
+ /**
+ * Retrieves a status effect.
+ *
+ * \param id ID of the status effect.
+ */
+ static const StatusEffect *getStatusEffect(int id);
+
+ using OptionsMap = std::map<uint16_t, int>;
+
+ /**
+ * These map flags or indexes to their corresponding status effect ID.
+ * This is tmwAthena-specific.
+ */
+ static const OptionsMap &opt0ToIdMap() { return mOpt0ToIdMap; }
+ static const OptionsMap &opt1ToIdMap() { return mOpt1ToIdMap; }
+ static const OptionsMap &opt2ToIdMap() { return mOpt2ToIdMap; }
+ static const OptionsMap &opt3ToIdMap() { return mOpt3ToIdMap; }
+
+ static void init();
+ static void readStatusEffectNode(XML::Node node, const std::string &filename);
+ static void checkStatus();
+ static void unload();
+
+private:
+ static bool mLoaded;
+
+ static std::map<int, StatusEffect> mStatusEffects;
+ static OptionsMap mOpt0ToIdMap;
+ static OptionsMap mOpt1ToIdMap;
+ static OptionsMap mOpt2ToIdMap;
+ static OptionsMap mOpt3ToIdMap;
+};
+
+#endif // STATUSEFFECTDB_H
diff --git a/src/resources/theme.cpp b/src/resources/theme.cpp
index ad686e19..694f2210 100644
--- a/src/resources/theme.cpp
+++ b/src/resources/theme.cpp
@@ -25,85 +25,191 @@
#include "configuration.h"
#include "log.h"
+#include "textrenderer.h"
#include "resources/dye.h"
#include "resources/image.h"
#include "resources/imageset.h"
#include "resources/resourcemanager.h"
-#include "utils/dtor.h"
-#include "utils/stringutils.h"
-#include "utils/xml.h"
+#include "utils/filesystem.h"
-#include <physfs.h>
+#include <guichan/font.hpp>
+#include <guichan/widget.hpp>
#include <algorithm>
+#include <optional>
+/**
+ * Initializes the directory in which the client looks for GUI themes, which at
+ * the same time functions as a fallback directory when looking up files
+ * relevant for the GUI theme.
+ */
static std::string defaultThemePath;
-std::string Theme::mThemePath;
-Theme *Theme::mInstance = nullptr;
-// Set the theme path...
static void initDefaultThemePath()
{
- ResourceManager *resman = ResourceManager::getInstance();
defaultThemePath = branding.getStringValue("guiThemePath");
- if (defaultThemePath.empty() || !resman->isDirectory(defaultThemePath))
+ if (defaultThemePath.empty() || !FS::isDirectory(defaultThemePath))
defaultThemePath = "graphics/gui/";
}
-Skin::Skin(ImageRect skin, Image *close, Image *stickyUp, Image *stickyDown):
- mBorder(skin),
- mCloseImage(close),
- mStickyImageUp(stickyUp),
- mStickyImageDown(stickyDown)
+static std::optional<std::string> findThemePath(const std::string &theme)
+{
+ if (theme.empty())
+ return {};
+
+ std::string themePath = defaultThemePath;
+ themePath += theme;
+
+ if (FS::isDirectory(themePath))
+ return themePath;
+
+ return {};
+}
+
+
+WidgetState::WidgetState(const gcn::Widget *widget)
+ : width(widget->getWidth())
+ , height(widget->getHeight())
+{
+ // x and y are not set based on the widget because the rendering usually
+ // happens in local coordinates.
+
+ if (!widget->isEnabled())
+ flags |= STATE_DISABLED;
+ if (widget->isFocused())
+ flags |= STATE_FOCUSED;
+}
+
+WidgetState::WidgetState(const gcn::Rectangle &dim, uint8_t flags)
+ : x(dim.x)
+ , y(dim.y)
+ , width(dim.width)
+ , height(dim.height)
+ , flags(flags)
{}
+
Skin::~Skin()
{
- // Clean up static resources
- for (auto img : mBorder.grid)
- delete img;
+ // Raw Image* need explicit deletion
+ for (auto &state : mStates)
+ for (auto &part : state.parts)
+ if (auto image = std::get_if<Image *>(&part.data))
+ delete *image;
+}
- mCloseImage->decRef();
- delete mStickyImageUp;
- delete mStickyImageDown;
+void Skin::addState(SkinState state)
+{
+ mStates.emplace_back(std::move(state));
}
-void Skin::updateAlpha(float minimumOpacityAllowed)
+void Skin::draw(Graphics *graphics, const WidgetState &state) const
{
- const float alpha = std::max(minimumOpacityAllowed,
- config.guiAlpha);
+ // Only draw the first matching state
+ auto skinState = getState(state.flags);
+ if (!skinState)
+ return;
- mBorder.setAlpha(alpha);
+ for (const auto &part : skinState->parts)
+ {
+ std::visit([&](const auto &data) {
+ using T = std::decay_t<decltype(data)>;
- mCloseImage->setAlpha(alpha);
- mStickyImageUp->setAlpha(alpha);
- mStickyImageDown->setAlpha(alpha);
+ if constexpr (std::is_same_v<T, ImageRect>)
+ {
+ graphics->drawImageRect(state.x + part.offsetX,
+ state.y + part.offsetY,
+ state.width,
+ state.height,
+ data);
+ }
+ else if constexpr (std::is_same_v<T, Image*>)
+ {
+ graphics->drawImage(data, state.x + part.offsetX, state.y + part.offsetY);
+ }
+ else if constexpr (std::is_same_v<T, ColoredRectangle>)
+ {
+ graphics->setColor(data.color);
+ graphics->fillRectangle(gcn::Rectangle(state.x + part.offsetX,
+ state.y + part.offsetY,
+ state.width,
+ state.height));
+ graphics->setColor(gcn::Color(255, 255, 255));
+ }
+ }, part.data);
+ }
+}
+
+const SkinState *Skin::getState(uint8_t flags) const
+{
+ for (const auto &skinState : mStates)
+ if (skinState.stateFlags == (skinState.setFlags & flags))
+ return &skinState;
+
+ return nullptr;
}
int Skin::getMinWidth() const
{
- return mBorder.grid[ImageRect::UPPER_LEFT]->getWidth() +
- mBorder.grid[ImageRect::UPPER_RIGHT]->getWidth();
+ int minWidth = 0;
+
+ for (const auto &state : mStates)
+ {
+ for (const auto &part : state.parts)
+ {
+ if (auto imageRect = std::get_if<ImageRect>(&part.data))
+ minWidth = std::max(minWidth, imageRect->minWidth());
+ else if (auto img = std::get_if<Image *>(&part.data))
+ minWidth = std::max(minWidth, (*img)->getWidth());
+ }
+ }
+
+ return minWidth;
}
int Skin::getMinHeight() const
{
- return mBorder.grid[ImageRect::UPPER_LEFT]->getHeight() +
- mBorder.grid[ImageRect::LOWER_LEFT]->getHeight();
+ int minHeight = 0;
+
+ for (const auto &state : mStates)
+ {
+ for (const auto &part : state.parts)
+ {
+ if (auto imageRect = std::get_if<ImageRect>(&part.data))
+ minHeight = std::max(minHeight, imageRect->minHeight());
+ else if (auto img = std::get_if<Image *>(&part.data))
+ minHeight = std::max(minHeight, (*img)->getHeight());
+ }
+ }
+
+ return minHeight;
}
-Theme::Theme():
- Palette(THEME_COLORS_END),
- mMinimumOpacity(-1.0f),
- mProgressColors(THEME_PROG_END)
+void Skin::updateAlpha(float alpha)
{
- initDefaultThemePath();
+ for (auto &state : mStates)
+ {
+ for (auto &part : state.parts)
+ {
+ if (auto rect = std::get_if<ImageRect>(&part.data))
+ rect->setAlpha(alpha);
+ else if (auto img = std::get_if<Image *>(&part.data))
+ (*img)->setAlpha(alpha);
+ }
+ }
+}
+
+Theme::Theme(const std::string &path)
+ : Palette(THEME_COLORS_END)
+ , mThemePath(path)
+ , mProgressColors(THEME_PROG_END)
+{
listen(Event::ConfigChannel);
- loadColors();
+ readTheme("theme.xml");
mColors[HIGHLIGHT].ch = 'H';
mColors[CHAT].ch = 'C';
@@ -118,72 +224,140 @@ Theme::Theme():
mColors[HYPERLINK].ch = '<';
}
-Theme::~Theme()
+Theme::~Theme() = default;
+
+std::string Theme::prepareThemePath()
{
- delete_all(mSkins);
- delete_all(mProgressColors);
+ initDefaultThemePath();
+
+ // Try theme from settings
+ auto themePath = findThemePath(config.theme);
+
+ // Try theme from branding
+ if (!themePath)
+ themePath = findThemePath(branding.getStringValue("theme"));
+
+ return themePath.value_or(defaultThemePath);
}
-Theme *Theme::instance()
+std::string Theme::resolvePath(const std::string &path) const
{
- if (!mInstance)
- mInstance = new Theme;
+ // Need to strip off any dye info for the existence tests
+ int pos = path.find('|');
+ std::string file;
+ if (pos > 0)
+ file = path.substr(0, pos);
+ else
+ file = path;
+
+ // Try the theme
+ file = mThemePath + "/" + file;
+ if (FS::exists(file))
+ return mThemePath + "/" + path;
- return mInstance;
+ // Backup
+ return defaultThemePath + "/" + path;
}
-void Theme::deleteInstance()
+ResourceRef<Image> Theme::getImage(const std::string &path) const
{
- delete mInstance;
- mInstance = nullptr;
+ return ResourceManager::getInstance()->getImage(resolvePath(path));
}
-gcn::Color Theme::getProgressColor(int type, float progress)
+ResourceRef<Image> Theme::getImageFromTheme(const std::string &path)
{
- DyePalette *dye = mInstance->mProgressColors[type];
+ return gui->getTheme()->getImage(path);
+}
+const gcn::Color &Theme::getThemeColor(int type, int alpha)
+{
+ return gui->getTheme()->getColor(type, alpha);
+}
+
+const gcn::Color &Theme::getThemeColor(char c, bool &valid)
+{
+ return gui->getTheme()->getColor(c, valid);
+}
+
+gcn::Color Theme::getProgressColor(int type, float progress)
+{
int color[3] = {0, 0, 0};
- dye->getColor(progress, color);
+
+ if (const auto &dye = gui->getTheme()->mProgressColors[type])
+ dye->getColor(progress, color);
return gcn::Color(color[0], color[1], color[2]);
}
-Skin *Theme::load(const std::string &filename, const std::string &defaultPath)
+void Theme::drawSkin(Graphics *graphics, SkinType type, const WidgetState &state) const
{
- // Check if this skin was already loaded
- auto skinIterator = mSkins.find(filename);
- if (skinIterator != mSkins.end())
- {
- Skin *skin = skinIterator->second;
- skin->instances++;
- return skin;
- }
+ getSkin(type).draw(graphics, state);
+}
- Skin *skin = readSkin(filename);
+void Theme::drawProgressBar(Graphics *graphics, const gcn::Rectangle &area,
+ const gcn::Color &color, float progress,
+ const std::string &text) const
+{
+ gcn::Font *oldFont = graphics->getFont();
+ gcn::Color oldColor = graphics->getColor();
- if (!skin)
- {
- // Try falling back on the defaultPath if this makes sense
- if (filename != defaultPath)
- {
- logger->log("Error loading skin '%s', falling back on default.",
- filename.c_str());
+ WidgetState widgetState;
+ widgetState.x = area.x;
+ widgetState.y = area.y;
+ widgetState.width = area.width;
+ widgetState.height = area.height;
- skin = readSkin(defaultPath);
- }
+ auto &skin = getSkin(SkinType::ProgressBar);
+ skin.draw(graphics, widgetState);
+
+ // The bar
+ if (progress > 0)
+ {
+ graphics->setColor(color);
+ graphics->fillRectangle(gcn::Rectangle(area.x + 4,
+ area.y + 4,
+ (int) (progress * (area.width - 8)),
+ area.height - 8));
+ }
- if (!skin)
+ // The label
+ if (!text.empty())
+ {
+ if (auto skinState = skin.getState(widgetState.flags))
{
- logger->error(strprintf("Error: Loading default skin '%s' failed. "
- "Make sure the skin file is valid.",
- defaultPath.c_str()));
+ auto font = skinState->textFormat.bold ? boldFont : gui->getFont();
+ const int textX = area.x + area.width / 2;
+ const int textY = area.y + (area.height - font->getHeight()) / 2;
+
+ TextRenderer::renderText(graphics,
+ text,
+ textX,
+ textY,
+ gcn::Graphics::CENTER,
+ font,
+ skinState->textFormat);
}
}
- // Add the skin to the loaded skins
- mSkins[filename] = skin;
+ graphics->setFont(oldFont);
+ graphics->setColor(oldColor);
+}
+
+const Skin &Theme::getSkin(SkinType skinType) const
+{
+ static Skin emptySkin;
+ const auto it = mSkins.find(skinType);
+ return it != mSkins.end() ? it->second : emptySkin;
+}
+
+int Theme::getMinWidth(SkinType skinType) const
+{
+ return getSkin(skinType).getMinWidth();
+}
- return skin;
+int Theme::getMinHeight(SkinType skinType) const
+{
+ return getSkin(skinType).getMinHeight();
}
void Theme::setMinimumOpacity(float minimumOpacity)
@@ -197,8 +371,14 @@ void Theme::setMinimumOpacity(float minimumOpacity)
void Theme::updateAlpha()
{
+ const float alpha = std::max(config.guiAlpha, mMinimumOpacity);
+ if (mAlpha == alpha)
+ return;
+
+ mAlpha = alpha;
+
for (auto &skin : mSkins)
- skin.second->updateAlpha(mMinimumOpacity);
+ skin.second.updateAlpha(mAlpha);
}
void Theme::event(Event::Channel channel, const Event &event)
@@ -211,189 +391,267 @@ void Theme::event(Event::Channel channel, const Event &event)
}
}
-Skin *Theme::readSkin(const std::string &filename)
+static bool check(bool value, const char *msg, ...)
{
- if (filename.empty())
- return nullptr;
+ if (!value)
+ {
+ va_list args;
+ va_start(args, msg);
+ logger->log(msg, args);
+ va_end(args);
+ }
+ return !value;
+}
- logger->log("Loading skin '%s'.", filename.c_str());
+bool Theme::readTheme(const std::string &filename)
+{
+ logger->log("Loading theme '%s'.", filename.c_str());
- XML::Document doc(resolveThemePath(filename));
+ XML::Document doc(resolvePath(filename));
XML::Node rootNode = doc.rootNode();
- if (!rootNode || rootNode.name() != "skinset")
- return nullptr;
-
- const std::string skinSetImage = rootNode.getProperty("image", "");
+ if (!rootNode || rootNode.name() != "theme")
+ return false;
- if (skinSetImage.empty())
+ for (auto childNode : rootNode.children())
{
- logger->log("Theme::readSkin(): Skinset does not define an image!");
- return nullptr;
+ if (childNode.name() == "skin")
+ readSkinNode(childNode);
+ else if (childNode.name() == "color")
+ readColorNode(childNode);
+ else if (childNode.name() == "progressbar")
+ readProgressBarNode(childNode);
}
- logger->log("Theme::load(): <skinset> defines '%s' as a skin image.",
- skinSetImage.c_str());
+ logger->log("Finished loading theme.");
- Image *dBorders = Theme::getImageFromTheme(skinSetImage);
- ImageRect border;
- memset(&border, 0, sizeof(ImageRect));
+ for (auto &[_, skin] : mSkins)
+ skin.updateAlpha(mAlpha);
- // iterate <widget>'s
- for (auto widgetNode : rootNode.children())
- {
- if (widgetNode.name() != "widget")
- continue;
+ return true;
+}
- const std::string widgetType =
- widgetNode.getProperty("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 (auto partNode : widgetNode.children())
- {
- if (partNode.name() != "part")
- continue;
-
- const std::string partType =
- partNode.getProperty("type", "unknown");
- // TOP ROW
- const int xPos = partNode.getProperty("xpos", 0);
- const int yPos = partNode.getProperty("ypos", 0);
- const int width = partNode.getProperty("width", 1);
- const int height = partNode.getProperty("height", 1);
-
- if (partType == "top-left-corner")
- border.grid[0] = dBorders->getSubImage(xPos, yPos, width, height);
- else if (partType == "top-edge")
- border.grid[1] = dBorders->getSubImage(xPos, yPos, width, height);
- else if (partType == "top-right-corner")
- border.grid[2] = dBorders->getSubImage(xPos, yPos, width, height);
-
- // MIDDLE ROW
- else if (partType == "left-edge")
- border.grid[3] = dBorders->getSubImage(xPos, yPos, width, height);
- else if (partType == "bg-quad")
- border.grid[4] = dBorders->getSubImage(xPos, yPos, width, height);
- else if (partType == "right-edge")
- border.grid[5] = dBorders->getSubImage(xPos, yPos, width, height);
-
- // BOTTOM ROW
- else if (partType == "bottom-left-corner")
- border.grid[6] = dBorders->getSubImage(xPos, yPos, width, height);
- else if (partType == "bottom-edge")
- border.grid[7] = dBorders->getSubImage(xPos, yPos, width, height);
- else if (partType == "bottom-right-corner")
- border.grid[8] = dBorders->getSubImage(xPos, yPos, width, height);
-
- else
- logger->log("Theme::readSkin(): Unknown part type '%s'",
- partType.c_str());
- }
- }
- else
- {
- logger->log("Theme::readSkin(): Unknown widget type '%s'",
- widgetType.c_str());
- }
- }
+static std::optional<SkinType> readSkinType(std::string_view type)
+{
+ if (type == "Window") return SkinType::Window;
+ if (type == "Popup") return SkinType::Popup;
+ if (type == "SpeechBubble") return SkinType::SpeechBubble;
+ if (type == "Button") return SkinType::Button;
+ if (type == "ButtonUp") return SkinType::ButtonUp;
+ if (type == "ButtonDown") return SkinType::ButtonDown;
+ if (type == "ButtonLeft") return SkinType::ButtonLeft;
+ if (type == "ButtonRight") return SkinType::ButtonRight;
+ if (type == "ButtonClose") return SkinType::ButtonClose;
+ if (type == "ButtonSticky") return SkinType::ButtonSticky;
+ if (type == "CheckBox") return SkinType::CheckBox;
+ if (type == "RadioButton") return SkinType::RadioButton;
+ if (type == "TextField") return SkinType::TextField;
+ if (type == "Tab") return SkinType::Tab;
+ if (type == "ScrollArea") return SkinType::ScrollArea;
+ if (type == "ScrollAreaHBar") return SkinType::ScrollAreaHBar;
+ if (type == "ScrollAreaHMarker") return SkinType::ScrollAreaHMarker;
+ if (type == "ScrollAreaVBar") return SkinType::ScrollAreaVBar;
+ if (type == "ScrollAreaVMarker") return SkinType::ScrollAreaVMarker;
+ if (type == "DropDownFrame") return SkinType::DropDownFrame;
+ if (type == "DropDownButton") return SkinType::DropDownButton;
+ if (type == "ProgressBar") return SkinType::ProgressBar;
+ if (type == "Slider") return SkinType::Slider;
+ if (type == "SliderHandle") return SkinType::SliderHandle;
+ if (type == "ResizeGrip") return SkinType::ResizeGrip;
+ if (type == "ShortcutBox") return SkinType::ShortcutBox;
+ return {};
+}
- dBorders->decRef();
+void Theme::readSkinNode(XML::Node node)
+{
+ const auto skinTypeStr = node.getProperty("type", std::string());
+ const auto skinType = readSkinType(skinTypeStr);
+ if (check(skinType.has_value(), "Theme: Unknown skin type '%s'", skinTypeStr.c_str()))
+ return;
- logger->log("Finished loading skin.");
+ auto &skin = mSkins[*skinType];
- // Hard-coded for now until we update the above code to look for window buttons
- Image *closeImage = Theme::getImageFromTheme("close_button.png");
- Image *sticky = Theme::getImageFromTheme("sticky_button.png");
- Image *stickyImageUp = sticky->getSubImage(0, 0, 15, 15);
- Image *stickyImageDown = sticky->getSubImage(15, 0, 15, 15);
- sticky->decRef();
+ node.attribute("frameSize", skin.frameSize);
+ node.attribute("padding", skin.padding);
+ node.attribute("spacing", skin.spacing);
+ node.attribute("titleBarHeight", skin.titleBarHeight);
+ node.attribute("titleOffsetX", skin.titleOffsetX);
+ node.attribute("titleOffsetY", skin.titleOffsetY);
- Skin *skin = new Skin(border, closeImage, stickyImageUp, stickyImageDown);
- skin->updateAlpha(mMinimumOpacity);
- return skin;
+ for (auto childNode : node.children())
+ if (childNode.name() == "state")
+ readSkinStateNode(childNode, skin);
}
-bool Theme::tryThemePath(std::string themePath)
+void Theme::readSkinStateNode(XML::Node node, Skin &skin) const
{
- if (!themePath.empty())
+ SkinState state;
+
+ auto readFlag = [&] (const char *name, int flag)
{
- themePath = defaultThemePath + themePath;
+ std::optional<bool> value;
+ node.attribute(name, value);
- if (PHYSFS_exists(themePath.c_str()))
+ if (value.has_value())
{
- mThemePath = themePath;
- return true;
+ state.setFlags |= flag;
+ state.stateFlags |= *value ? flag : 0;
}
+ };
+
+ readFlag("selected", STATE_SELECTED);
+ readFlag("disabled", STATE_DISABLED);
+ readFlag("hovered", STATE_HOVERED);
+ readFlag("focused", STATE_FOCUSED);
+
+ for (auto childNode : node.children())
+ {
+ if (childNode.name() == "img")
+ readSkinStateImgNode(childNode, state);
+ else if (childNode.name() == "rect")
+ readSkinStateRectNode(childNode, state);
+ else if (childNode.name() == "text")
+ readSkinStateTextNode(childNode, state);
}
- return false;
+ skin.addState(std::move(state));
}
-void Theme::prepareThemePath()
+void Theme::readSkinStateTextNode(XML::Node node, SkinState &state) const
{
- // Ensure the Theme object has been created
- instance();
-
- // Try theme from settings
- if (!tryThemePath(config.theme))
- // Try theme from branding
- if (!tryThemePath(branding.getStringValue("theme")))
- // Use default
- mThemePath = defaultThemePath;
+ auto &textFormat = state.textFormat;
+ node.attribute("bold", textFormat.bold);
+ node.attribute("color", textFormat.color);
+ node.attribute("outlineColor", textFormat.outlineColor);
+ node.attribute("shadowColor", textFormat.shadowColor);
+}
- instance()->loadColors(mThemePath);
+template<>
+inline void fromString(const char *str, FillMode &value)
+{
+ if (strcmp(str, "repeat") == 0)
+ value = FillMode::Repeat;
+ else if (strcmp(str, "stretch") == 0)
+ value = FillMode::Stretch;
}
-std::string Theme::resolveThemePath(const std::string &path)
+void Theme::readSkinStateImgNode(XML::Node node, SkinState &state) const
{
- // Need to strip off any dye info for the existence tests
- int pos = path.find('|');
- std::string file;
- if (pos > 0)
- file = path.substr(0, pos);
- else
- file = path;
+ const std::string src = node.getProperty("src", std::string());
+ if (check(!src.empty(), "Theme: 'img' element has empty 'src' attribute!"))
+ return;
- // Might be a valid path already
- if (PHYSFS_exists(file.c_str()))
- return path;
+ auto image = getImage(src);
+ if (check(image, "Theme: Failed to load image '%s'!", src.c_str()))
+ return;
- // Try the theme
- file = getThemePath() + "/" + file;
- if (PHYSFS_exists(file.c_str()))
- return getThemePath() + "/" + path;
+ int left = 0;
+ int right = 0;
+ int top = 0;
+ int bottom = 0;
+ int x = 0;
+ int y = 0;
+ int width = image->getWidth();
+ int height = image->getHeight();
+
+ node.attribute("left", left);
+ node.attribute("right", right);
+ node.attribute("top", top);
+ node.attribute("bottom", bottom);
+ node.attribute("x", x);
+ node.attribute("y", y);
+ node.attribute("width", width);
+ node.attribute("height", height);
+
+ if (check(left >= 0 || right >= 0 || top >= 0 || bottom >= 0, "Theme: Invalid border value!"))
+ return;
+ if (check(x >= 0 || y >= 0, "Theme: Invalid position value!"))
+ return;
+ if (check(width >= 0 || height >= 0, "Theme: Invalid size value!"))
+ return;
+ if (check(x + width <= image->getWidth() || y + height <= image->getHeight(), "Theme: Image size out of bounds!"))
+ return;
- // Backup
- return std::string(defaultThemePath) + "/" + path;
+ auto &part = state.parts.emplace_back();
+
+ node.attribute("offsetX", part.offsetX);
+ node.attribute("offsetY", part.offsetY);
+
+ if (left + right + top + bottom > 0)
+ {
+ auto &border = part.data.emplace<ImageRect>();
+
+ node.attribute("fill", border.fillMode);
+
+ const int gridx[4] = {x, x + left, x + width - right, x + width};
+ const int gridy[4] = {y, y + top, y + height - bottom, y + height};
+ unsigned a = 0;
+
+ for (unsigned y = 0; y < 3; y++)
+ {
+ for (unsigned x = 0; x < 3; x++)
+ {
+ border.grid[a] = image->getSubImage(gridx[x],
+ gridy[y],
+ gridx[x + 1] - gridx[x],
+ gridy[y + 1] - gridy[y]);
+ a++;
+ }
+ }
+ }
+ else
+ {
+ part.data = image->getSubImage(x, y, width, height);
+ }
}
-Image *Theme::getImageFromTheme(const std::string &path)
+template<>
+inline void fromString(const char *str, gcn::Color &value)
{
- ResourceManager *resman = ResourceManager::getInstance();
- return resman->getImage(resolveThemePath(path));
+ if (strlen(str) < 7 || str[0] != '#')
+ {
+ error:
+ logger->log("Error, invalid theme color palette: %s", str);
+ value = Palette::BLACK;
+ return;
+ }
+
+ int v = 0;
+ for (int i = 1; i < 7; ++i)
+ {
+ char c = str[i];
+ int n;
+
+ if ('0' <= c && c <= '9')
+ n = c - '0';
+ else if ('A' <= c && c <= 'F')
+ n = c - 'A' + 10;
+ else if ('a' <= c && c <= 'f')
+ n = c - 'a' + 10;
+ else
+ goto error;
+
+ v = (v << 4) | n;
+ }
+
+ value = gcn::Color(v);
}
-ImageSet *Theme::getImageSetFromTheme(const std::string &path,
- int w, int h)
+void Theme::readSkinStateRectNode(XML::Node node, SkinState &state) const
{
- ResourceManager *resman = ResourceManager::getInstance();
- return resman->getImageSet(resolveThemePath(path), w, h);
+ auto &part = state.parts.emplace_back();
+ auto &rect = part.data.emplace<ColoredRectangle>();
+
+ node.attribute("color", rect.color);
+ node.attribute("alpha", rect.color.a);
}
static int readColorType(const std::string &type)
{
- static std::string colors[] = {
+ static constexpr const char *colors[Theme::THEME_COLORS_END] = {
"TEXT",
"SHADOW",
"OUTLINE",
- "PROGRESS_BAR",
- "BUTTON",
- "BUTTON_DISABLED",
- "TAB",
"PARTY_CHAT_TAB",
"PARTY_SOCIAL_TAB",
"BACKGROUND",
@@ -402,6 +660,7 @@ static int readColorType(const std::string &type)
"SHOP_WARNING",
"ITEM_EQUIPPED",
"CHAT",
+ "BUBBLE_TEXT",
"GM",
"PLAYER",
"WHISPER",
@@ -432,51 +691,15 @@ static int readColorType(const std::string &type)
return -1;
for (int i = 0; i < Theme::THEME_COLORS_END; i++)
- {
- if (compareStrI(type, colors[i]) == 0)
- {
+ if (type == colors[i])
return i;
- }
- }
return -1;
}
-static gcn::Color readColor(const std::string &description)
-{
- int size = description.length();
- if (size < 7 || description[0] != '#')
- {
- error:
- logger->log("Error, invalid theme color palette: %s",
- description.c_str());
- return Palette::BLACK;
- }
-
- int v = 0;
- for (int i = 1; i < 7; ++i)
- {
- char c = description[i];
- int n;
-
- if ('0' <= c && c <= '9')
- n = c - '0';
- else if ('A' <= c && c <= 'F')
- n = c - 'A' + 10;
- else if ('a' <= c && c <= 'f')
- n = c - 'a' + 10;
- else
- goto error;
-
- v = (v << 4) | n;
- }
-
- return gcn::Color(v);
-}
-
static Palette::GradientType readColorGradient(const std::string &grad)
{
- static std::string grads[] = {
+ static constexpr const char *grads[] = {
"STATIC",
"PULSE",
"SPECTRUM",
@@ -487,17 +710,30 @@ static Palette::GradientType readColorGradient(const std::string &grad)
return Palette::STATIC;
for (int i = 0; i < 4; i++)
- {
- if (compareStrI(grad, grads[i]))
- return (Palette::GradientType) i;
- }
+ if (grad == grads[i])
+ return static_cast<Palette::GradientType>(i);
return Palette::STATIC;
}
+void Theme::readColorNode(XML::Node node)
+{
+ const int type = readColorType(node.getProperty("id", std::string()));
+ if (check(type > 0, "Theme: 'color' element has invalid or no 'type' attribute!"))
+ return;
+
+ gcn::Color color;
+ if (check(node.attribute("color", color), "Theme: 'color' element missing 'color' attribute!"))
+ return;
+
+ const GradientType grad = readColorGradient(node.getProperty("effect", std::string()));
+
+ mColors[type].set(type, color, grad, 10);
+}
+
static int readProgressType(const std::string &type)
{
- static std::string colors[] = {
+ static constexpr const char *colors[Theme::THEME_PROG_END] = {
"DEFAULT",
"HP",
"MP",
@@ -512,62 +748,17 @@ static int readProgressType(const std::string &type)
return -1;
for (int i = 0; i < Theme::THEME_PROG_END; i++)
- {
- if (compareStrI(type, colors[i]) == 0)
+ if (type == colors[i])
return i;
- }
return -1;
}
-void Theme::loadColors(std::string file)
+void Theme::readProgressBarNode(XML::Node node)
{
- if (file == defaultThemePath)
- return; // No need to reload
-
- if (file.empty())
- file = defaultThemePath;
-
- file += "/colors.xml";
-
- XML::Document doc(file);
- XML::Node root = doc.rootNode();
-
- if (!root || root.name() != "colors")
- {
- logger->log("Error loading colors file: %s", file.c_str());
+ const int type = readProgressType(node.getProperty("id", std::string()));
+ if (type < 0) // invalid or no type given
return;
- }
-
- int type;
- std::string temp;
- gcn::Color color;
- GradientType grad;
-
- for (auto node : root.children())
- {
- if (node.name() == "color")
- {
- type = readColorType(node.getProperty("id", ""));
- if (type < 0) // invalid or no type given
- continue;
- temp = node.getProperty("color", "");
- if (temp.empty()) // no color set, so move on
- continue;
-
- color = readColor(temp);
- grad = readColorGradient(node.getProperty("effect", ""));
-
- mColors[type].set(type, color, grad, 10);
- }
- else if (node.name() == "progressbar")
- {
- type = readProgressType(node.getProperty("id", ""));
- if (type < 0) // invalid or no type given
- continue;
-
- mProgressColors[type] = new DyePalette(node.getProperty( "color", ""));
- }
- }
+ mProgressColors[type] = std::make_unique<DyePalette>(node.getProperty("color", std::string()));
}
diff --git a/src/resources/theme.h b/src/resources/theme.h
index 7edae416..6d71b067 100644
--- a/src/resources/theme.h
+++ b/src/resources/theme.h
@@ -21,44 +21,119 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SKIN_H
-#define SKIN_H
+#pragma once
#include "graphics.h"
#include "eventlistener.h"
#include "gui/palette.h"
+#include "resources/image.h"
+#include "utils/xml.h"
#include <map>
+#include <memory>
#include <string>
+#include <variant>
+
+namespace gcn {
+class Widget;
+}
class DyePalette;
class Image;
class ImageSet;
class ProgressBar;
+enum class SkinType
+{
+ Window,
+ Popup,
+ SpeechBubble,
+ Button,
+ ButtonUp,
+ ButtonDown,
+ ButtonLeft,
+ ButtonRight,
+ ButtonClose,
+ ButtonSticky,
+ CheckBox,
+ RadioButton,
+ TextField,
+ Tab,
+ ScrollArea,
+ ScrollAreaHBar,
+ ScrollAreaHMarker,
+ ScrollAreaVBar,
+ ScrollAreaVMarker,
+ DropDownFrame,
+ DropDownButton,
+ ProgressBar,
+ Slider,
+ SliderHandle,
+ ResizeGrip,
+ ShortcutBox,
+};
+
+enum StateFlags : uint8_t
+{
+ STATE_HOVERED = 0x01,
+ STATE_SELECTED = 0x02,
+ STATE_DISABLED = 0x04,
+ STATE_FOCUSED = 0x08,
+};
+
+struct ColoredRectangle
+{
+ gcn::Color color;
+};
+
+struct SkinPart
+{
+ int offsetX = 0;
+ int offsetY = 0;
+ std::variant<ImageRect, Image *, ColoredRectangle> data;
+};
+
+struct TextFormat
+{
+ bool bold = false;
+ gcn::Color color;
+ std::optional<gcn::Color> outlineColor;
+ std::optional<gcn::Color> shadowColor;
+};
+
+struct SkinState
+{
+ uint8_t stateFlags = 0;
+ uint8_t setFlags = 0;
+ TextFormat textFormat;
+ std::vector<SkinPart> parts;
+};
+
+struct WidgetState
+{
+ WidgetState() = default;
+ explicit WidgetState(const gcn::Widget *widget);
+ explicit WidgetState(const gcn::Rectangle &dim, uint8_t flags = 0);
+
+ int x = 0;
+ int y = 0;
+ int width = 0;
+ int height = 0;
+ uint8_t flags = 0;
+};
+
class Skin
{
public:
- Skin(ImageRect skin, Image *close, Image *stickyUp, Image *stickyDown);
-
+ Skin() = default;
~Skin();
- /**
- * Returns the background skin.
- */
- const ImageRect &getBorder() const { return mBorder; }
+ void addState(SkinState state);
- /**
- * Returns the image used by a close button for this skin.
- */
- Image *getCloseImage() const { return mCloseImage; }
+ void draw(Graphics *graphics, const WidgetState &state) const;
- /**
- * Returns the image used by a sticky button for this skin.
- */
- Image *getStickyImage(bool state) const
- { return state ? mStickyImageDown : mStickyImageUp; }
+ const SkinState *getState(uint8_t flags) const;
/**
* Returns the minimum width which can be used with this skin.
@@ -73,44 +148,38 @@ class Skin
/**
* Updates the alpha value of the skin
*/
- void updateAlpha(float minimumOpacityAllowed = 0.0f);
+ void updateAlpha(float alpha);
- int instances = 0;
+ int frameSize = 0;
+ int padding = 0;
+ int spacing = 0;
+ int titleBarHeight = 0;
+ int titleOffsetX = 0;
+ int titleOffsetY = 0;
private:
- ImageRect mBorder; /**< The window border and background */
- Image *mCloseImage; /**< Close Button Image */
- Image *mStickyImageUp; /**< Sticky Button Image */
- Image *mStickyImageDown; /**< Sticky Button Image */
+ std::vector<SkinState> mStates;
};
class Theme : public Palette, public EventListener
{
public:
- static Theme *instance();
- static void deleteInstance();
+ static std::string prepareThemePath();
- static void prepareThemePath();
- static const std::string &getThemePath() { return mThemePath; }
+ Theme(const std::string &path);
+ ~Theme() override;
/**
- * Returns the patch to the given gui resource relative to the theme
+ * Returns the patch to the given GUI resource relative to the theme
* or, if it isn't in the theme, relative to 'graphics/gui'.
*/
- static std::string resolveThemePath(const std::string &path);
-
- static Image *getImageFromTheme(const std::string &path);
- static ImageSet *getImageSetFromTheme(const std::string &path,
- int w, int h);
+ std::string resolvePath(const std::string &path) const;
+ static ResourceRef<Image> getImageFromTheme(const std::string &path);
enum ThemePalette {
TEXT,
SHADOW,
OUTLINE,
- PROGRESS_BAR,
- BUTTON,
- BUTTON_DISABLED,
- TAB,
PARTY_CHAT_TAB,
PARTY_SOCIAL_TAB,
BACKGROUND,
@@ -119,6 +188,7 @@ class Theme : public Palette, public EventListener
SHOP_WARNING,
ITEM_EQUIPPED,
CHAT,
+ BUBBLE_TEXT,
GM,
PLAYER,
WHISPER,
@@ -167,34 +237,32 @@ class Theme : public Palette, public EventListener
*
* @return the requested color
*/
- static const gcn::Color &getThemeColor(int type, int alpha = 255)
- {
- return mInstance->getColor(type, alpha);
- }
-
- static const gcn::Color &getThemeColor(char c, bool &valid)
- {
- return mInstance->getColor(c, valid);
- }
+ static const gcn::Color &getThemeColor(int type, int alpha = 255);
+ static const gcn::Color &getThemeColor(char c, bool &valid);
static gcn::Color getProgressColor(int type, float progress);
- /**
- * Loads a skin.
- */
- Skin *load(const std::string &filename,
- const std::string &defaultPath = getThemePath());
+ void drawSkin(Graphics *graphics, SkinType type, const WidgetState &state) const;
+ void drawProgressBar(Graphics *graphics,
+ const gcn::Rectangle &area,
+ const gcn::Color &color,
+ float progress,
+ const std::string &text = std::string()) const;
+
+ const Skin &getSkin(SkinType skinType) const;
+
+ int getMinWidth(SkinType skinType) const;
+ int getMinHeight(SkinType skinType) const;
/**
- * Updates the alpha values of all of the skins.
+ * Get the current GUI alpha value.
*/
- void updateAlpha();
+ int getGuiAlpha() const { return static_cast<int>(mAlpha * 255.0f); }
/**
* Get the minimum opacity allowed to skins.
*/
- float getMinimumOpacity() const
- { return mMinimumOpacity; }
+ float getMinimumOpacity() const { return mMinimumOpacity; }
/**
* Set the minimum opacity allowed to skins.
@@ -205,28 +273,31 @@ class Theme : public Palette, public EventListener
void event(Event::Channel channel, const Event &event) override;
private:
- Theme();
- ~Theme() override;
-
- Skin *readSkin(const std::string &filename);
-
- // Map containing all window skins
- std::map<std::string, Skin *> mSkins;
+ /**
+ * Updates the alpha values of all of the skins and images.
+ */
+ void updateAlpha();
- static std::string mThemePath;
- static Theme *mInstance;
+ ResourceRef<Image> getImage(const std::string &path) const;
- static bool tryThemePath(std::string themePath);
+ bool readTheme(const std::string &filename);
+ void readSkinNode(XML::Node node);
+ void readSkinStateNode(XML::Node node, Skin &skin) const;
+ void readSkinStateTextNode(XML::Node node, SkinState &state) const;
+ void readSkinStateImgNode(XML::Node node, SkinState &state) const;
+ void readSkinStateRectNode(XML::Node node, SkinState &state) const;
+ void readColorNode(XML::Node node);
+ void readProgressBarNode(XML::Node node);
- void loadColors(std::string file = std::string());
+ std::string mThemePath;
+ std::map<SkinType, Skin> mSkins;
/**
* Tells if the current skins opacity
* should not get less than the given value
*/
- float mMinimumOpacity;
+ float mMinimumOpacity = 0.0f;
+ float mAlpha = 1.0;
- std::vector<DyePalette *> mProgressColors;
+ std::vector<std::unique_ptr<DyePalette>> mProgressColors;
};
-
-#endif
diff --git a/src/resources/userpalette.h b/src/resources/userpalette.h
index 946a4725..347491f1 100644
--- a/src/resources/userpalette.h
+++ b/src/resources/userpalette.h
@@ -20,8 +20,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef USER_PALETTE_H
-#define USER_PALETTE_H
+#pragma once
#include "gui/palette.h"
@@ -185,5 +184,3 @@ class UserPalette : public Palette, public gcn::ListModel
};
extern UserPalette *userPalette;
-
-#endif // USER_PALETTE_H
diff --git a/src/resources/wallpaper.cpp b/src/resources/wallpaper.cpp
index e8167b6b..2bdcd656 100644
--- a/src/resources/wallpaper.cpp
+++ b/src/resources/wallpaper.cpp
@@ -23,7 +23,7 @@
#include "configuration.h"
-#include <physfs.h>
+#include "utils/filesystem.h"
#include <algorithm>
#include <cstring>
@@ -90,35 +90,24 @@ void Wallpaper::loadWallpapers()
initWallpaperPaths();
- char **fileNames = PHYSFS_enumerateFiles(wallpaperPath.c_str());
-
- for (char **fileName = fileNames; *fileName; fileName++)
+ for (auto fileName : FS::enumerateFiles(wallpaperPath))
{
- int width;
- int height;
-
// If the backup file is found, we tell it.
- if (strncmp(*fileName, wallpaperFile.c_str(), strlen(*fileName)) == 0)
+ if (wallpaperFile == fileName)
haveBackup = true;
// If the image format is terminated by: "_<width>x<height>.png"
// It is taken as a potential wallpaper.
-
- // First, get the base filename of the image:
- std::string filename = *fileName;
- filename = filename.substr(0, filename.rfind("_"));
-
- // Check that the base filename doesn't have any '%' markers.
- if (filename.find("%") == std::string::npos)
+ if (auto sizeSuffix = strrchr(fileName, '_'))
{
- // Then, append the width and height search mask.
- filename.append("_%dx%d.png");
+ int width;
+ int height;
- if (sscanf(*fileName, filename.c_str(), &width, &height) == 2)
+ if (sscanf(sizeSuffix, "_%dx%d.png", &width, &height) == 2)
{
WallpaperData wp;
wp.filename = wallpaperPath;
- wp.filename.append(*fileName);
+ wp.filename.append(fileName);
wp.width = width;
wp.height = height;
wallpaperData.push_back(wp);
@@ -126,8 +115,6 @@ void Wallpaper::loadWallpapers()
}
}
- PHYSFS_freeList(fileNames);
-
std::sort(wallpaperData.begin(), wallpaperData.end(), wallpaperCompare);
}
diff --git a/src/resources/wallpaper.h b/src/resources/wallpaper.h
index 532dfd38..7e72e2f0 100644
--- a/src/resources/wallpaper.h
+++ b/src/resources/wallpaper.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef WALLPAPER_H
-#define WALLPAPER_H
+#pragma once
#include <string>
@@ -46,5 +45,3 @@ class Wallpaper
*/
static std::string getWallpaper(int width, int height);
};
-
-#endif // WALLPAPER_H
diff --git a/src/rotationalparticle.cpp b/src/rotationalparticle.cpp
index bcdb9bad..cdd7de61 100644
--- a/src/rotationalparticle.cpp
+++ b/src/rotationalparticle.cpp
@@ -35,11 +35,7 @@ RotationalParticle::RotationalParticle(Map *map, XML::Node animationNode,
mAnimation(animationNode, dyePalettes)
{}
-RotationalParticle::~RotationalParticle()
-{
- // Prevent ImageParticle from decreasing the reference count of the image
- mImage = nullptr;
-}
+RotationalParticle::~RotationalParticle() = default;
bool RotationalParticle::update()
{
diff --git a/src/rotationalparticle.h b/src/rotationalparticle.h
index 4ddfa7a9..bc7d1a6d 100644
--- a/src/rotationalparticle.h
+++ b/src/rotationalparticle.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef ROTATIONAL_PARTICLE_H
-#define ROTATIONAL_PARTICLE_H
+#pragma once
#include "imageparticle.h"
#include "simpleanimation.h"
@@ -46,5 +45,3 @@ class RotationalParticle : public ImageParticle
private:
SimpleAnimation mAnimation; /**< Used animation for this particle */
};
-
-#endif
diff --git a/src/sdlgraphics.cpp b/src/sdlgraphics.cpp
index 37643190..0fec9ab7 100644
--- a/src/sdlgraphics.cpp
+++ b/src/sdlgraphics.cpp
@@ -28,6 +28,41 @@
#include <guichan/exception.hpp>
+#include <cmath>
+
+class SetColorAlphaMod
+{
+public:
+ SetColorAlphaMod(SDL_Texture *texture, gcn::Color color, bool enabled)
+ : mTexture(texture)
+ , mEnabled(texture != nullptr && enabled)
+ {
+ if (mEnabled)
+ {
+ SDL_GetTextureColorMod(texture, &mOriginal.r, &mOriginal.g, &mOriginal.b);
+ SDL_GetTextureAlphaMod(texture, &mOriginal.a);
+
+ SDL_SetTextureColorMod(texture, color.r, color.g, color.b);
+ SDL_SetTextureAlphaMod(texture, color.a * mOriginal.a / 255);
+ }
+ }
+
+ ~SetColorAlphaMod()
+ {
+ if (mEnabled)
+ {
+ SDL_SetTextureAlphaMod(mTexture, mOriginal.a);
+ SDL_SetTextureColorMod(mTexture, mOriginal.r, mOriginal.g, mOriginal.b);
+ }
+ }
+
+private:
+ SDL_Texture *mTexture = nullptr;
+ SDL_Color mOriginal;
+ const bool mEnabled;
+};
+
+
std::unique_ptr<Graphics> SDLGraphics::create(SDL_Window *window, const VideoSettings &settings)
{
int rendererFlags = 0;
@@ -132,7 +167,8 @@ bool SDLGraphics::drawRescaledImage(const Image *image,
dstRect.w = desiredWidth;
dstRect.h = desiredHeight;
- return !(SDL_RenderCopy(mRenderer, image->mTexture, &srcRect, &dstRect) < 0);
+ SetColorAlphaMod mod(image->mTexture, mColor, useColor);
+ return SDL_RenderCopy(mRenderer, image->mTexture, &srcRect, &dstRect) != 0;
}
#if SDL_VERSION_ATLEAST(2, 0, 10)
@@ -162,7 +198,8 @@ bool SDLGraphics::drawRescaledImageF(const Image *image,
dstRect.w = desiredWidth;
dstRect.h = desiredHeight;
- return !(SDL_RenderCopyF(mRenderer, image->mTexture, &srcRect, &dstRect) < 0);
+ SetColorAlphaMod mod(image->mTexture, mColor, useColor);
+ return SDL_RenderCopyF(mRenderer, image->mTexture, &srcRect, &dstRect) == 0;
}
#endif
@@ -196,7 +233,8 @@ void SDLGraphics::drawRescaledImagePattern(const Image *image,
SDL_Rect dstRect;
dstRect.x = dstX; dstRect.y = dstY;
dstRect.w = dw; dstRect.h = dh;
- srcRect.w = dw; srcRect.h = dh;
+ srcRect.w = image->mBounds.w * dw / scaledWidth;
+ srcRect.h = image->mBounds.h * dh / scaledHeight;
if (SDL_RenderCopy(mRenderer, image->mTexture, &srcRect, &dstRect))
return;
@@ -255,33 +293,21 @@ SDL_Surface *SDLGraphics::getScreenshot()
return screenshot;
}
-bool SDLGraphics::pushClipArea(gcn::Rectangle area)
-{
- bool result = Graphics::pushClipArea(area);
- updateSDLClipRect();
- return result;
-}
-
-void SDLGraphics::popClipArea()
+void SDLGraphics::updateClipRect()
{
- Graphics::popClipArea();
- updateSDLClipRect();
-}
-
-void SDLGraphics::updateSDLClipRect()
-{
- if (mClipStack.empty())
+ if (mClipRects.empty())
{
SDL_RenderSetClipRect(mRenderer, nullptr);
return;
}
- const gcn::ClipRectangle &carea = mClipStack.top();
- SDL_Rect rect;
- rect.x = carea.x;
- rect.y = carea.y;
- rect.w = carea.width;
- rect.h = carea.height;
+ const gcn::Rectangle &clipRect = mClipRects.top();
+ const SDL_Rect rect = {
+ clipRect.x,
+ clipRect.y,
+ clipRect.width,
+ clipRect.height
+ };
SDL_RenderSetClipRect(mRenderer, &rect);
}
diff --git a/src/sdlgraphics.h b/src/sdlgraphics.h
index e2d9b5a5..2b7635a5 100644
--- a/src/sdlgraphics.h
+++ b/src/sdlgraphics.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SDLGRAPHICS_H
-#define SDLGRAPHICS_H
+#pragma once
#include "graphics.h"
@@ -70,10 +69,6 @@ public:
SDL_Surface *getScreenshot() override;
- bool pushClipArea(gcn::Rectangle area) override;
-
- void popClipArea() override;
-
void drawPoint(int x, int y) override;
void drawLine(int x1, int y1, int x2, int y2) override;
@@ -82,10 +77,9 @@ public:
void fillRectangle(const gcn::Rectangle &rectangle) override;
-private:
- void updateSDLClipRect();
+protected:
+ void updateClipRect() override;
+private:
SDL_Renderer *mRenderer = nullptr;
};
-
-#endif // SDLGRAPHICS_H
diff --git a/src/shopitem.h b/src/shopitem.h
index dc1188e0..2bb5cb55 100644
--- a/src/shopitem.h
+++ b/src/shopitem.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SHOPITEM_H
-#define SHOPITEM_H
+#pragma once
#include "item.h"
@@ -117,5 +116,3 @@ class ShopItem : public Item
};
std::stack<DuplicateItem> mDuplicates; /** <-- Stores duplicates */
};
-
-#endif
diff --git a/src/simpleanimation.cpp b/src/simpleanimation.cpp
index 506714d2..1ef22d84 100644
--- a/src/simpleanimation.cpp
+++ b/src/simpleanimation.cpp
@@ -21,28 +21,32 @@
#include "simpleanimation.h"
-#include "game.h"
#include "graphics.h"
-#include "log.h"
#include "resources/animation.h"
-#include "resources/dye.h"
#include "resources/image.h"
#include "resources/imageset.h"
#include "resources/resourcemanager.h"
-SimpleAnimation::SimpleAnimation(Animation animation):
- mAnimation(std::move(animation)),
- mCurrentFrame(mAnimation.getFrame(0)),
- mInitialized(true)
+SimpleAnimation::SimpleAnimation(Animation animation)
+ : mAnimation(std::move(animation))
+ , mInitialized(true)
{
+ if (mAnimation.getLength() > 0)
+ mCurrentFrame = mAnimation.getFrame(0);
}
SimpleAnimation::SimpleAnimation(XML::Node animationNode,
const std::string &dyePalettes)
{
- initializeAnimation(animationNode, dyePalettes);
- mCurrentFrame = mAnimation.getFrame(0);
+ if (animationNode)
+ {
+ mAnimation = Animation::fromXML(animationNode, dyePalettes);
+ mInitialized = true;
+ }
+
+ if (mAnimation.getLength() > 0)
+ mCurrentFrame = mAnimation.getFrame(0);
}
bool SimpleAnimation::draw(Graphics *graphics, int posX, int posY) const
@@ -90,103 +94,9 @@ void SimpleAnimation::update(int dt)
}
}
-int SimpleAnimation::getLength() const
-{
- return mAnimation.getLength();
-}
-
Image *SimpleAnimation::getCurrentImage() const
{
if (mCurrentFrame)
return mCurrentFrame->image;
return nullptr;
}
-
-void SimpleAnimation::initializeAnimation(XML::Node animationNode,
- const std::string& dyePalettes)
-{
- if (!animationNode)
- return;
-
- std::string imagePath = animationNode.getProperty( "imageset", "");
-
- // Instanciate the dye coloration.
- if (!imagePath.empty() && !dyePalettes.empty())
- Dye::instantiate(imagePath, dyePalettes);
-
- ImageSet *imageset = ResourceManager::getInstance()->getImageSet(
- animationNode.getProperty("imageset", ""),
- animationNode.getProperty("width", 0),
- animationNode.getProperty("height", 0)
- );
-
- if (!imageset)
- return;
-
- // Get animation frames
- for (auto frameNode : animationNode.children())
- {
- int delay = frameNode.getProperty("delay", 0);
- int offsetX = frameNode.getProperty("offsetX", 0);
- int offsetY = frameNode.getProperty("offsetY", 0);
- Game *game = Game::instance();
- if (game)
- {
- offsetX -= imageset->getWidth() / 2
- - game->getCurrentTileWidth() / 2;
- offsetY -= imageset->getHeight() - game->getCurrentTileHeight();
- }
-
- if (frameNode.name() == "frame")
- {
- int index = frameNode.getProperty("index", -1);
-
- if (index < 0)
- {
- logger->log("No valid value for 'index'");
- continue;
- }
-
- Image *img = imageset->get(index);
-
- if (!img)
- {
- logger->log("No image at index %d", index);
- continue;
- }
-
- mAnimation.addFrame(img, delay, offsetX, offsetY);
- }
- else if (frameNode.name() == "sequence")
- {
- int start = frameNode.getProperty("start", -1);
- int end = frameNode.getProperty("end", -1);
-
- if (start < 0 || end < 0)
- {
- logger->log("No valid value for 'start' or 'end'");
- continue;
- }
-
- while (end >= start)
- {
- Image *img = imageset->get(start);
-
- if (!img)
- {
- logger->log("No image at index %d", start);
- continue;
- }
-
- mAnimation.addFrame(img, delay, offsetX, offsetY);
- start++;
- }
- }
- else if (frameNode.name() == "end")
- {
- mAnimation.addTerminator();
- }
- }
-
- mInitialized = true;
-}
diff --git a/src/simpleanimation.h b/src/simpleanimation.h
index 33168fd9..299b243f 100644
--- a/src/simpleanimation.h
+++ b/src/simpleanimation.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SIMPLEANIMAION_H
-#define SIMPLEANIMAION_H
+#pragma once
#include "resources/animation.h"
@@ -29,7 +28,7 @@
class Graphics;
/**
- * This class is a leightweight alternative to the AnimatedSprite class.
+ * This class is a leightweight alternative to the Sprite class.
* It hosts a looping animation without actions and directions.
*/
class SimpleAnimation final
@@ -49,7 +48,7 @@ class SimpleAnimation final
void setFrame(int frame);
- int getLength() const;
+ int getLength() const { return mAnimation.getLength(); }
void update(int dt);
@@ -63,9 +62,6 @@ class SimpleAnimation final
Image *getCurrentImage() const;
private:
- void initializeAnimation(XML::Node animationNode,
- const std::string& dyePalettes = std::string());
-
/** The hosted animation. */
Animation mAnimation;
@@ -81,5 +77,3 @@ class SimpleAnimation final
/** Tell whether the animation is ready */
bool mInitialized = false;
};
-
-#endif
diff --git a/src/sound.cpp b/src/sound.cpp
index 60cc2c10..c97951a3 100644
--- a/src/sound.cpp
+++ b/src/sound.cpp
@@ -30,42 +30,32 @@
#include "resources/resourcemanager.h"
#include "resources/soundeffect.h"
-enum {
- CHANNEL_NOTIFICATIONS = 0
-};
-
/**
- * This will be set to true, when a music can be freed after a fade out
- * Currently used by fadeOutCallBack()
+ * This will be set to true when the music that was playing can be freed.
*/
-static bool sFadingOutEnded = false;
+static bool sMusicFinished;
+static bool sChannelFinished[Sound::CHANNEL_COUNT];
-/**
- * Callback used at end of fadeout.
- * It is called by Mix_MusicFadeFinished().
- */
-static void fadeOutCallBack()
+static void musicFinishedCallBack()
{
- sFadingOutEnded = true;
+ sMusicFinished = true;
}
-Sound::Sound():
- mInstalled(false),
- mSfxVolume(100),
- mNotificationsVolume(100),
- mMusicVolume(60),
- mMusic(nullptr)
+static void channelFinishedCallBack(int channel)
{
- // This set up our callback function used to
- // handle fade outs endings.
- sFadingOutEnded = false;
- Mix_HookMusicFinished(fadeOutCallBack);
+ sChannelFinished[channel] = true;
+}
+
+Sound::Sound()
+{
+ Mix_HookMusicFinished(musicFinishedCallBack);
+ Mix_ChannelFinished(channelFinishedCallBack);
}
Sound::~Sound()
{
- // Unlink the callback function.
Mix_HookMusicFinished(nullptr);
+ Mix_ChannelFinished(nullptr);
}
void Sound::init()
@@ -93,8 +83,8 @@ void Sound::init()
return;
}
- Mix_AllocateChannels(16);
- Mix_ReserveChannels(1); // reserve one channel for notification sounds
+ Mix_AllocateChannels(CHANNEL_COUNT);
+ Mix_ReserveChannels(CHANNEL_RESERVED_COUNT);
Mix_VolumeMusic(mMusicVolume);
Mix_Volume(-1, mSfxVolume);
Mix_Volume(CHANNEL_NOTIFICATIONS, mNotificationsVolume);
@@ -173,25 +163,9 @@ void Sound::setNotificationsVolume(int volume)
Mix_Volume(CHANNEL_NOTIFICATIONS, mNotificationsVolume);
}
-static Music *loadMusic(const std::string &fileName)
-{
- ResourceManager *resman = ResourceManager::getInstance();
- return resman->getMusic(paths.getStringValue("music") + fileName);
-}
-
-
void Sound::playMusic(const std::string &fileName)
{
- mCurrentMusicFile = fileName;
-
- if (!mInstalled)
- return;
-
- haltMusic();
-
- mMusic = loadMusic(fileName);
- if (mMusic)
- mMusic->play();
+ fadeInMusic(fileName, 0);
}
void Sound::stopMusic()
@@ -213,7 +187,9 @@ void Sound::fadeInMusic(const std::string &fileName, int ms)
haltMusic();
- mMusic = loadMusic(fileName);
+ ResourceManager *resman = ResourceManager::getInstance();
+ mMusic = resman->getMusic(paths.getStringValue("music") + fileName);
+
if (mMusic)
mMusic->play(-1, ms);
}
@@ -230,12 +206,12 @@ void Sound::fadeOutMusic(int ms)
if (mMusic)
{
Mix_FadeOutMusic(ms);
- // Note: The fadeOutCallBack handler will take care about freeing
+ // Note: The musicFinishedCallBack will take care about freeing
// the music file at fade out ending.
}
else
{
- sFadingOutEnded = true;
+ sMusicFinished = true;
}
}
@@ -247,14 +223,10 @@ void Sound::fadeOutAndPlayMusic(const std::string &fileName, int ms)
void Sound::logic()
{
- if (sFadingOutEnded)
+ if (sMusicFinished)
{
- if (mMusic)
- {
- mMusic->decRef();
- mMusic = nullptr;
- }
- sFadingOutEnded = false;
+ sMusicFinished = false;
+ mMusic = nullptr;
if (!mNextMusicFile.empty())
{
@@ -262,6 +234,15 @@ void Sound::logic()
mNextMusicFile.clear();
}
}
+
+ for (int i = 0; i < CHANNEL_COUNT; i++)
+ {
+ if (sChannelFinished[i])
+ {
+ sChannelFinished[i] = false;
+ mSounds[i] = nullptr;
+ }
+ }
}
void Sound::playSfx(const std::string &path, int x, int y)
@@ -277,7 +258,7 @@ void Sound::playSfx(const std::string &path, int x, int y)
ResourceManager *resman = ResourceManager::getInstance();
- if (SoundEffect *sample = resman->getSoundEffect(tmpPath))
+ if (ResourceRef<SoundEffect> sound = resman->getSoundEffect(tmpPath))
{
logger->log("Sound::playSfx() Playing: %s", path.c_str());
int vol = 120;
@@ -293,7 +274,9 @@ void Sound::playSfx(const std::string &path, int x, int y)
vol -= std::min(120, dist / 4);
}
- sample->play(0, vol);
+ int channel = sound->play(0, vol);
+ if (channel != -1)
+ mSounds[channel] = sound;
}
}
@@ -302,9 +285,11 @@ void Sound::playNotification(const std::string &path)
const std::string fullPath = paths.getValue("sfx", "sfx/") + path;
ResourceManager *resman = ResourceManager::getInstance();
- if (SoundEffect *sample = resman->getSoundEffect(fullPath))
+ if (ResourceRef<SoundEffect> sound = resman->getSoundEffect(fullPath))
{
- sample->play(0, 128, CHANNEL_NOTIFICATIONS);
+ int channel = sound->play(0, MIX_MAX_VOLUME, CHANNEL_NOTIFICATIONS);
+ if (channel != -1)
+ mSounds[channel] = sound;
}
}
@@ -326,6 +311,5 @@ void Sound::haltMusic()
return;
Mix_HaltMusic();
- mMusic->decRef();
mMusic = nullptr;
}
diff --git a/src/sound.h b/src/sound.h
index d4d0e8da..98286d76 100644
--- a/src/sound.h
+++ b/src/sound.h
@@ -19,14 +19,16 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SOUND_H
-#define SOUND_H
+#pragma once
+
+#include "resources/resource.h"
#include <SDL_mixer.h>
#include <string>
class Music;
+class SoundEffect;
/** Sound engine
*
@@ -108,11 +110,19 @@ class Sound
/**
* The sound logic.
- * Currently used to check whether the music file can be freed after
- * a fade out, and whether new music has to be played.
+ *
+ * Checks whether the music and sound effects can be freed after they
+ * finished playing, and whether new music has to be played.
*/
void logic();
+ enum Channel {
+ CHANNEL_NOTIFICATIONS = 0,
+ CHANNEL_RESERVED_COUNT,
+
+ CHANNEL_COUNT = 16,
+ };
+
private:
/** Logs various info about sound device. */
void info();
@@ -126,16 +136,15 @@ class Sound
*/
std::string mNextMusicFile;
- bool mInstalled;
+ bool mInstalled = false;
- int mSfxVolume;
- int mNotificationsVolume;
- int mMusicVolume;
+ int mSfxVolume = 100;
+ int mNotificationsVolume = 100;
+ int mMusicVolume = 60;
std::string mCurrentMusicFile;
- Music *mMusic;
+ ResourceRef<Music> mMusic;
+ ResourceRef<SoundEffect> mSounds[CHANNEL_COUNT];
};
extern Sound sound;
-
-#endif
diff --git a/src/animatedsprite.cpp b/src/sprite.cpp
index ec7aa1e3..c2e434e7 100644
--- a/src/animatedsprite.cpp
+++ b/src/sprite.cpp
@@ -19,7 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "animatedsprite.h"
+#include "sprite.h"
#include "graphics.h"
@@ -30,7 +30,7 @@
#include <cassert>
-AnimatedSprite::AnimatedSprite(SpriteDef *sprite):
+Sprite::Sprite(SpriteDef *sprite):
mSprite(sprite)
{
assert(mSprite);
@@ -39,30 +39,32 @@ AnimatedSprite::AnimatedSprite(SpriteDef *sprite):
play(SpriteAction::STAND);
}
-AnimatedSprite *AnimatedSprite::load(const std::string &filename, int variant)
+Sprite *Sprite::load(const std::string &filename, int variant)
{
ResourceManager *resman = ResourceManager::getInstance();
- SpriteDef *s = resman->getSprite(filename, variant);
- if (!s)
+ auto spriteDef = resman->getSprite(filename, variant);
+ if (!spriteDef)
return nullptr;
- auto *as = new AnimatedSprite(s);
- s->decRef();
- return as;
+ return new Sprite(spriteDef);
}
-AnimatedSprite::~AnimatedSprite() = default;
+Sprite::~Sprite() = default;
-bool AnimatedSprite::reset()
+bool Sprite::reset()
{
bool ret = mFrameIndex !=0 || mFrameTime != 0;
mFrameIndex = 0;
mFrameTime = 0;
+ if (mAnimation)
+ mFrame = mAnimation->getFrame(0);
+ else
+ mFrame = nullptr;
return ret;
}
-bool AnimatedSprite::play(const std::string &spriteAction)
+bool Sprite::play(const std::string &spriteAction)
{
Action *action = mSprite->getAction(spriteAction);
if (!action)
@@ -74,8 +76,6 @@ bool AnimatedSprite::play(const std::string &spriteAction)
if (animation && animation != mAnimation && animation->getLength() > 0)
{
mAnimation = animation;
- mFrame = mAnimation->getFrame(0);
-
reset();
return true;
@@ -84,7 +84,7 @@ bool AnimatedSprite::play(const std::string &spriteAction)
return false;
}
-bool AnimatedSprite::update(int time)
+bool Sprite::update(int dt)
{
if (!mAnimation)
return false;
@@ -92,7 +92,7 @@ bool AnimatedSprite::update(int time)
Animation *animation = mAnimation;
Frame *frame = mFrame;
- if (!updateCurrentAnimation(time))
+ if (!updateCurrentAnimation(dt))
{
// Animation finished, reset to default
play(SpriteAction::STAND);
@@ -102,12 +102,12 @@ bool AnimatedSprite::update(int time)
return animation != mAnimation || frame != mFrame;
}
-bool AnimatedSprite::updateCurrentAnimation(int time)
+bool Sprite::updateCurrentAnimation(int dt)
{
if (!mFrame || Animation::isTerminator(*mFrame))
return false;
- mFrameTime += time;
+ mFrameTime += dt;
while (mFrameTime > mFrame->delay && mFrame->delay > 0)
{
@@ -130,13 +130,13 @@ bool AnimatedSprite::updateCurrentAnimation(int time)
return true;
}
-bool AnimatedSprite::draw(Graphics *graphics, int posX, int posY) const
+bool Sprite::draw(Graphics *graphics, int posX, int posY) const
{
if (!mFrame)
return false;
if (!mFrame->image)
- return false;
+ return false;
if (mFrame->image->getAlpha() != mAlpha)
mFrame->image->setAlpha(mAlpha);
@@ -146,7 +146,7 @@ bool AnimatedSprite::draw(Graphics *graphics, int posX, int posY) const
posY + mFrame->offsetY);
}
-bool AnimatedSprite::setDirection(SpriteDirection direction)
+bool Sprite::setDirection(SpriteDirection direction)
{
if (mDirection != direction)
{
@@ -160,7 +160,6 @@ bool AnimatedSprite::setDirection(SpriteDirection direction)
if (animation && animation != mAnimation && animation->getLength() > 0)
{
mAnimation = animation;
- mFrame = mAnimation->getFrame(0);
reset();
}
@@ -170,38 +169,38 @@ bool AnimatedSprite::setDirection(SpriteDirection direction)
return false;
}
-int AnimatedSprite::getDuration() const
+int Sprite::getDuration() const
{
if (mAnimation)
return mAnimation->getDuration();
return 0;
}
-int AnimatedSprite::getWidth() const
+int Sprite::getWidth() const
{
if (mFrame && mFrame->image)
return mFrame->image->getWidth();
return 0;
}
-int AnimatedSprite::getHeight() const
+int Sprite::getHeight() const
{
if (mFrame && mFrame->image)
return mFrame->image->getHeight();
return 0;
}
-int AnimatedSprite::getOffsetX() const
+int Sprite::getOffsetX() const
{
return mFrame ? mFrame->offsetX : 0;
}
-int AnimatedSprite::getOffsetY() const
+int Sprite::getOffsetY() const
{
return mFrame ? mFrame->offsetY : 0;
}
-const Image *AnimatedSprite::getImage() const
+const Image *Sprite::getImage() const
{
return mFrame ? mFrame->image : nullptr;
}
diff --git a/src/sprite.h b/src/sprite.h
index d7f5f1d7..b82bf7bb 100644
--- a/src/sprite.h
+++ b/src/sprite.h
@@ -18,35 +18,54 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SPRITE_H
-#define SPRITE_H
+#pragma once
#include "resources/spritedef.h"
+class Animation;
class Graphics;
class Image;
+struct Frame;
// Default frame display delay in milliseconds
const int DEFAULT_FRAME_DELAY = 75;
+/**
+ * Animates a sprite by adding playback state.
+ */
class Sprite
{
public:
- virtual ~Sprite() = default;
+ /**
+ * Constructor.
+ * @param sprite the sprite to animate
+ */
+ Sprite(SpriteDef *sprite);
+
+ /**
+ * An helper function, which will request the sprite to animate
+ * from the resource manager.
+ *
+ * @param filename the file of the sprite to animate
+ * @param variant the sprite variant
+ */
+ static Sprite *load(const std::string &filename, int variant = 0);
+
+ ~Sprite();
/**
* Resets the sprite.
*
* @returns true if the sprite changed, false otherwise
*/
- virtual bool reset() = 0;
+ bool reset();
/**
* Plays an action using the current direction.
*
* @returns true if the sprite changed, false otherwise
*/
- virtual bool play(const std::string &action) = 0;
+ bool play(const std::string &action);
/**
* Inform the animation of the passed time so that it can output the
@@ -54,67 +73,73 @@ class Sprite
*
* @returns true if the sprite changed, false otherwise
*/
- virtual bool update(int time) = 0;
+ bool update(int time);
/**
* Draw the current animation frame at the coordinates given in screen
* pixels.
*/
- virtual bool draw(Graphics *graphics, int posX, int posY) const = 0;
+ bool draw(Graphics *graphics, int posX, int posY) const;
/**
* Gets the width in pixels of the image
*/
- virtual int getWidth() const = 0;
+ int getWidth() const;
/**
* Gets the height in pixels of the image
*/
- virtual int getHeight() const = 0;
+ int getHeight() const;
/**
* Gets the horizontal offset that the sprite will be drawn at
*/
- virtual int getOffsetX() const
- { return 0; }
+ int getOffsetX() const;
/**
* Gets the vertical offset that the sprite will be drawn at
*/
- virtual int getOffsetY() const
- { return 0; }
+ int getOffsetY() const;
/**
* Returns a reference to the current image being drawn.
*/
- virtual const Image *getImage() const = 0;
+ const Image *getImage() const;
/**
* Sets the direction.
*
* @returns true if the sprite changed, false otherwise
*/
- virtual bool setDirection(SpriteDirection direction) = 0;
+ bool setDirection(SpriteDirection direction);
/**
- * Sets the alpha value of the animated sprite
+ * Sets the alpha value of the sprite.
*/
- virtual void setAlpha(float alpha)
- { mAlpha = alpha; }
+ void setAlpha(float alpha) { mAlpha = alpha; }
/**
- * Returns the current alpha opacity of the animated sprite.
+ * Returns the current alpha opacity of the sprite.
*/
- virtual float getAlpha() const
- { return mAlpha; }
+ float getAlpha() const { return mAlpha; }
/**
* Returns the duration of the current sprite animation in milliseconds.
*/
- virtual int getDuration() const = 0;
+ int getDuration() const;
- protected:
- float mAlpha = 1.0f; /**< The alpha opacity used to draw */
-};
+ private:
+ bool updateCurrentAnimation(int dt);
+
+ float mAlpha = 1.0f; /**< The alpha opacity used to draw */
-#endif // SPRITE_H
+ SpriteDirection mDirection = DIRECTION_DOWN; /**< The sprite direction. */
+
+ int mFrameIndex = 0; /**< The index of the current frame. */
+ int mFrameTime = 0; /**< The time since start of frame. */
+
+ ResourceRef<SpriteDef> mSprite; /**< The sprite definition. */
+ Action *mAction = nullptr; /**< The currently active action. */
+ Animation *mAnimation = nullptr; /**< The currently active animation. */
+ Frame *mFrame = nullptr; /**< The currently active frame. */
+};
diff --git a/src/statuseffect.cpp b/src/statuseffect.cpp
index f06ab827..7cb035bd 100644
--- a/src/statuseffect.cpp
+++ b/src/statuseffect.cpp
@@ -1,7 +1,7 @@
/*
* The Mana Client
* Copyright (C) 2008-2009 The Mana World Development Team
- * Copyright (C) 2009-2013 The Mana Developers
+ * Copyright (C) 2009-2025 The Mana Developers
*
* This file is part of The Mana Client.
*
@@ -22,152 +22,50 @@
#include "statuseffect.h"
#include "event.h"
+#include "particle.h"
#include "sound.h"
#include "configuration.h"
-#include <map>
-
-#define STATUS_EFFECTS_FILE "status-effects.xml"
-
-bool StatusEffect::mLoaded = false;
-
-StatusEffect::StatusEffect() = default;
-StatusEffect::~StatusEffect() = default;
-
-void StatusEffect::playSFX()
+/**
+ * Plays the sound effect associated with this status effect, if possible.
+ */
+void StatusEffect::playSfx(bool enabled) const
{
- if (!mSFXEffect.empty())
- sound.playSfx(mSFXEffect);
+ auto &sfx = enabled ? start.sfx : end.sfx;
+ if (!sfx.empty())
+ sound.playSfx(sfx);
}
-void StatusEffect::deliverMessage()
+/**
+ * Delivers the chat message associated with this status effect, if
+ * possible.
+ */
+void StatusEffect::deliverMessage(bool enabled) const
{
- if (!mMessage.empty())
- serverNotice(mMessage);
+ auto &message = enabled ? start.message : end.message;
+ if (!message.empty())
+ serverNotice(message);
}
-Particle *StatusEffect::getParticle()
+/**
+ * Creates the particle effect associated with this status effect, if
+ * possible.
+ */
+Particle *StatusEffect::getParticle(bool enabled) const
{
- if (mParticleEffect.empty())
+ auto &particleEffect = enabled ? start.particleEffect : end.particleEffect;
+ if (particleEffect.empty())
return nullptr;
- return particleEngine->addEffect(mParticleEffect, 0, 0);
+ return particleEngine->addEffect(particleEffect, 0, 0);
}
-AnimatedSprite *StatusEffect::getIcon()
+/**
+ * Retrieves the status icon for this effect, if applicable.
+ */
+Sprite *StatusEffect::getIconSprite() const
{
- if (mIcon.empty())
+ if (icon.empty())
return nullptr;
-
- AnimatedSprite *sprite = AnimatedSprite::load(
- paths.getStringValue("sprites") + mIcon);
- if (false && sprite)
- {
- sprite->play(SpriteAction::DEFAULT);
- sprite->reset();
- }
- return sprite;
-}
-
-std::string StatusEffect::getAction() const
-{
- if (mAction.empty())
- return SpriteAction::INVALID;
- return mAction;
-}
-
-
-// -- initialisation and static parts --
-
-
-typedef std::map<int, StatusEffect *> status_effect_map[2];
-
-static status_effect_map statusEffects;
-static status_effect_map stunEffects;
-static std::map<int, int> blockEffectIndexMap;
-
-int StatusEffect::blockEffectIndexToEffectIndex(int blockIndex)
-{
- if (blockEffectIndexMap.find(blockIndex) == blockEffectIndexMap.end())
- return -1;
- return blockEffectIndexMap[blockIndex];
-}
-
-StatusEffect *StatusEffect::getStatusEffect(int index, bool enabling)
-{
- return statusEffects[enabling][index];
-}
-
-StatusEffect *StatusEffect::getStunEffect(int index, bool enabling)
-{
- return stunEffects[enabling][index];
-}
-
-void StatusEffect::init()
-{
- if (mLoaded)
- unload();
-}
-
-void StatusEffect::readStatusEffectNode(XML::Node node, const std::string &filename)
-{
- status_effect_map *the_map = nullptr;
- int index = atoi(node.getProperty("id", "-1").c_str());
- if (node.name() == "status-effect")
- {
- the_map = &statusEffects;
- int block_index = atoi(node.getProperty("block-id", "-1").c_str());
-
- if (index >= 0 && block_index >= 0)
- blockEffectIndexMap[block_index] = index;
- }
- else if (node.name() == "stun-effect")
- the_map = &stunEffects;
-
- if (the_map)
- {
- auto *startEffect = new StatusEffect;
- auto *endEffect = new StatusEffect;
-
- startEffect->mMessage = node.getProperty("start-message", "");
- startEffect->mSFXEffect = node.getProperty("start-audio", "");
- startEffect->mParticleEffect = node.getProperty("start-particle", "");
- startEffect->mIcon = node.getProperty("icon", "");
- startEffect->mAction = node.getProperty("action", "");
- startEffect->mPersistentParticleEffect = (node.getProperty("persistent-particle-effect", "no")) != "no";
-
- endEffect->mMessage = node.getProperty("end-message", "");
- endEffect->mSFXEffect = node.getProperty("end-audio", "");
- endEffect->mParticleEffect = node.getProperty("end-particle", "");
-
- (*the_map)[1][index] = startEffect;
- (*the_map)[0][index] = endEffect;
- }
-
-}
-
-void StatusEffect::checkStatus()
-{
- mLoaded = true;
-}
-
-void unloadMap(std::map<int, StatusEffect *> map)
-{
- for (auto &[_, effect] : map)
- delete effect;
-
- map.clear();
-}
-
-void StatusEffect::unload()
-{
- if (!mLoaded)
- return;
-
- unloadMap(statusEffects[0]);
- unloadMap(statusEffects[1]);
- unloadMap(stunEffects[0]);
- unloadMap(stunEffects[1]);
-
- mLoaded = false;
+ return Sprite::load(paths.getStringValue("sprites") + icon);
}
diff --git a/src/statuseffect.h b/src/statuseffect.h
index 05b80e6b..2ab98d4f 100644
--- a/src/statuseffect.h
+++ b/src/statuseffect.h
@@ -1,7 +1,7 @@
/*
* The Mana Client
* Copyright (C) 2008-2009 The Mana World Development Team
- * Copyright (C) 2009-2013 The Mana Developers
+ * Copyright (C) 2009-2025 The Mana Developers
*
* This file is part of The Mana Client.
*
@@ -19,97 +19,38 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef STATUS_EFFECT_H
-#define STATUS_EFFECT_H
+#pragma once
-#include "particle.h"
-#include "animatedsprite.h"
+#include <string>
-#include "utils/xml.h"
+class Particle;
+class Sprite;
class StatusEffect
{
public:
- StatusEffect();
- ~StatusEffect();
+ struct Event
+ {
+ std::string message;
+ std::string sfx;
+ std::string particleEffect;
+ };
- /**
- * Plays the sound effect associated with this status effect, if possible.
- */
- void playSFX();
-
- /**
- * Delivers the chat message associated with this status effect, if
- * possible.
- */
- void deliverMessage();
-
- /**
- * Creates the particle effect associated with this status effect, if
- * possible.
- */
- Particle *getParticle();
-
- /**
- * Retrieves the status icon for this effect, if applicable
- */
- AnimatedSprite *getIcon();
-
- /**
- * Retrieves an action to perform, or SpriteAction::INVALID
- */
- std::string getAction() const;
+ std::string name;
+ Event start;
+ Event end;
+ std::string icon;
/**
* Determines whether the particle effect should be restarted when the
- * being changes maps
+ * being changes maps.
*/
- bool particleEffectIsPersistent() const { return mPersistentParticleEffect; }
+ bool persistentParticleEffect = false;
+ StatusEffect() = default;
- /**
- * Retrieves a status effect.
- *
- * \param index Index of the status effect.
- * \param enabling Whether to retrieve the activating effect (true) or
- * the deactivating effect (false).
- */
- static StatusEffect *getStatusEffect(int index, bool enabling);
-
- /**
- * Retrieves a stun effect.
- *
- * \param index Index of the stun effect.
- * \param enabling Whether to retrieve the activating effect (true) or
- * the deactivating effect (false).
- */
- static StatusEffect *getStunEffect(int index, bool enabling);
-
- /**
- * Maps a block effect index to its corresponding effect index. Block
- * effect indices are used for opt2/opt3/status.option blocks; their
- * mapping to regular effect indices is handled in the config file.
- *
- * Returns -1 on failure.
- */
- static int blockEffectIndexToEffectIndex(int blocKIndex);
-
- static void init();
-
- static void readStatusEffectNode(XML::Node node, const std::string &filename);
-
- static void checkStatus();
-
- static void unload();
-private:
- static bool mLoaded;
-
- std::string mMessage;
- std::string mSFXEffect;
- std::string mParticleEffect;
- std::string mIcon;
- std::string mAction;
- bool mPersistentParticleEffect = false;
+ void playSfx(bool enabled) const;
+ void deliverMessage(bool enabled) const;
+ Particle *getParticle(bool enabled) const;
+ Sprite *getIconSprite() const;
};
-
-#endif // !defined(STATUS_EFFECT_H)
diff --git a/src/text.cpp b/src/text.cpp
index e3776410..4698aa87 100644
--- a/src/text.cpp
+++ b/src/text.cpp
@@ -22,54 +22,34 @@
#include "text.h"
-#include "configuration.h"
#include "textmanager.h"
#include "textrenderer.h"
#include "gui/gui.h"
-#include "resources/image.h"
#include "resources/theme.h"
#include <guichan/font.hpp>
int Text::mInstances = 0;
-ImageRect Text::mBubble;
-Image *Text::mBubbleArrow;
-Text::Text(const std::string &text, int x, int y,
+Text::Text(const std::string &text,
+ int x,
+ int y,
gcn::Graphics::Alignment alignment,
- const gcn::Color* color, bool isSpeech,
- gcn::Font *font) :
- mText(text),
- mColor(color),
- mIsSpeech(isSpeech)
+ const gcn::Color *color,
+ bool isSpeech,
+ gcn::Font *font)
+ : mText(text)
+ , mColor(color)
+ , mFont(font ? font : gui->getFont())
+ , mIsSpeech(isSpeech)
{
- if (!font)
- mFont = gui->getFont();
- else
- mFont = font;
-
if (textManager == nullptr)
- {
textManager = new TextManager;
- Image *sbImage = Theme::getImageFromTheme("bubble.png|W:#"
- + config.speechBubblecolor);
- mBubble.grid[0] = sbImage->getSubImage(0, 0, 5, 5);
- mBubble.grid[1] = sbImage->getSubImage(5, 0, 5, 5);
- mBubble.grid[2] = sbImage->getSubImage(10, 0, 5, 5);
- mBubble.grid[3] = sbImage->getSubImage(0, 5, 5, 5);
- mBubble.grid[4] = sbImage->getSubImage(5, 5, 5, 5);
- mBubble.grid[5] = sbImage->getSubImage(10, 5, 5, 5);
- mBubble.grid[6] = sbImage->getSubImage(0, 10, 5, 5);
- mBubble.grid[7] = sbImage->getSubImage(5, 10, 5, 5);
- mBubble.grid[8] = sbImage->getSubImage(10, 10, 5, 5);
- mBubbleArrow = sbImage->getSubImage(0, 15, 15, 10);
- mBubble.setAlpha(config.speechBubbleAlpha);
- mBubbleArrow->setAlpha(config.speechBubbleAlpha);
- sbImage->decRef();
- }
+
++mInstances;
+
mHeight = mFont->getHeight();
mWidth = mFont->getWidth(text);
@@ -85,8 +65,10 @@ Text::Text(const std::string &text, int x, int y,
mXOffset = mWidth;
break;
}
+
mX = x - mXOffset;
mY = y;
+
textManager->addText(this);
}
@@ -97,9 +79,6 @@ Text::~Text()
{
delete textManager;
textManager = nullptr;
- for (auto img : mBubble.grid)
- delete img;
- delete mBubbleArrow;
}
}
@@ -117,9 +96,15 @@ void Text::draw(gcn::Graphics *graphics, int xOff, int yOff)
{
if (mIsSpeech)
{
- static_cast<Graphics*>(graphics)->drawImageRect(
- mX - xOff - 5, mY - yOff - 5, mWidth + 10, mHeight + 10,
- mBubble);
+ WidgetState state;
+ state.x = mX - xOff - 5;
+ state.y = mY - yOff - 5;
+ state.width = mWidth + 10;
+ state.height = mHeight + 10;
+
+ auto theme = gui->getTheme();
+ theme->drawSkin(static_cast<Graphics *>(graphics), SkinType::SpeechBubble, state);
+
/*
if (mWidth >= 15)
{
diff --git a/src/text.h b/src/text.h
index 9fa6104f..b71dde37 100644
--- a/src/text.h
+++ b/src/text.h
@@ -20,14 +20,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef TEXT_H
-#define TEXT_H
-
-#include "graphics.h"
+#pragma once
#include "utils/time.h"
#include <guichan/color.hpp>
+#include <guichan/font.hpp>
+#include <guichan/graphics.hpp>
class TextManager;
@@ -75,10 +74,6 @@ class Text
const gcn::Color *mColor; /**< The color of the text. */
gcn::Font *mFont; /**< The font of the text */
bool mIsSpeech; /**< Is this text a speech bubble? */
-
- protected:
- static ImageRect mBubble; /**< Speech bubble graphic */
- static Image *mBubbleArrow; /**< Speech bubble arrow graphic */
};
class FlashText : public Text
@@ -99,5 +94,3 @@ class FlashText : public Text
private:
Timer mTimer; /**< Time left for flashing */
};
-
-#endif // TEXT_H
diff --git a/src/textmanager.h b/src/textmanager.h
index f736e87c..c4d38d82 100644
--- a/src/textmanager.h
+++ b/src/textmanager.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef TEXTMANAGER_H
-#define TEXTMANAGER_H
+#pragma once
#include <list>
@@ -69,5 +68,3 @@ class TextManager
};
extern TextManager *textManager;
-
-#endif // TEXTMANAGER_H
diff --git a/src/textparticle.h b/src/textparticle.h
index d70cc5b9..89a8e90c 100644
--- a/src/textparticle.h
+++ b/src/textparticle.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef TEXTPARTICLE_H
-#define TEXTPARTICLE_H
+#pragma once
#include "particle.h"
@@ -53,5 +52,3 @@ class TextParticle : public Particle
const gcn::Color *mColor; /**< Color used for drawing the text. */
bool mOutline; /**< Make the text better readable */
};
-
-#endif
diff --git a/src/textrenderer.h b/src/textrenderer.h
index d402e146..c9cd9310 100644
--- a/src/textrenderer.h
+++ b/src/textrenderer.h
@@ -19,16 +19,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef TEXT_RENDERER_H
-#define TEXT_RENDERER_H
-
-#include "graphics.h"
+#pragma once
#include "resources/theme.h"
+#include <guichan/exception.hpp>
+#include <guichan/font.hpp>
+
/**
- * Class for text rendering. Used by the TextParticle, the Text and FlashText
- * objects and the Preview in the color dialog.
+ * Class for text rendering which can apply an outline and shadow.
*/
class TextRenderer
{
@@ -39,48 +38,90 @@ public:
static void renderText(gcn::Graphics *graphics,
const std::string &text,
int x, int y,
- gcn::Graphics::Alignment align,
+ gcn::Graphics::Alignment alignment,
const gcn::Color &color,
gcn::Font *font,
bool outline = false,
- bool shadow = false)
+ bool shadow = false,
+ const std::optional<gcn::Color> &outlineColor = {},
+ const std::optional<gcn::Color> &shadowColor = {})
{
- graphics->setFont(font);
+ switch (alignment)
+ {
+ case gcn::Graphics::LEFT:
+ break;
+ case gcn::Graphics::CENTER:
+ x -= font->getWidth(text) / 2;
+ break;
+ case gcn::Graphics::RIGHT:
+ x -= font->getWidth(text);
+ break;
+ default:
+ throw GCN_EXCEPTION("Unknown alignment.");
+ }
// Text shadow
if (shadow)
{
- graphics->setColor(Theme::getThemeColor(Theme::SHADOW,
- color.a / 2));
+ if (shadowColor)
+ graphics->setColor(*shadowColor);
+ else
+ graphics->setColor(Theme::getThemeColor(Theme::SHADOW, color.a / 2));
+
if (outline)
- {
- graphics->drawText(text, x + 2, y + 2, align);
- }
+ font->drawString(graphics, text, x + 2, y + 2);
else
- {
- graphics->drawText(text, x + 1, y + 1, align);
- }
+ font->drawString(graphics, text, x + 1, y + 1);
}
- if (outline) {
-/* graphics->setColor(guiPalette->getColor(Palette::OUTLINE,
+ if (outline)
+ {
+ /*
+ graphics->setColor(guiPalette->getColor(Palette::OUTLINE,
alpha/4));
// TODO: Reanable when we can draw it nicely in software mode
- graphics->drawText(text, x + 2, y + 2, align);
- graphics->drawText(text, x + 1, y + 2, align);
- graphics->drawText(text, x + 2, y + 1, align);*/
+ font->drawString(graphics, text, x + 2, y + 2);
+ font->drawString(graphics, text, x + 1, y + 2);
+ font->drawString(graphics, text, x + 2, y + 1);
+ */
// Text outline
- graphics->setColor(Theme::getThemeColor(Theme::OUTLINE, color.a));
- graphics->drawText(text, x + 1, y, align);
- graphics->drawText(text, x - 1, y, align);
- graphics->drawText(text, x, y + 1, align);
- graphics->drawText(text, x, y - 1, align);
+ if (outlineColor)
+ graphics->setColor(*outlineColor);
+ else
+ graphics->setColor(Theme::getThemeColor(Theme::OUTLINE, color.a));
+
+ font->drawString(graphics, text, x + 1, y);
+ font->drawString(graphics, text, x - 1, y);
+ font->drawString(graphics, text, x, y + 1);
+ font->drawString(graphics, text, x, y - 1);
}
graphics->setColor(color);
- graphics->drawText(text, x, y, align);
+ font->drawString(graphics, text, x, y);
}
-};
-#endif
+ /**
+ * Renders a specified text.
+ */
+ static void renderText(gcn::Graphics *graphics,
+ const std::string &text,
+ int x,
+ int y,
+ gcn::Graphics::Alignment align,
+ gcn::Font *font,
+ const TextFormat &format)
+ {
+ renderText(graphics,
+ text,
+ x,
+ y,
+ align,
+ format.color,
+ font,
+ format.outlineColor.has_value(),
+ format.shadowColor.has_value(),
+ format.outlineColor,
+ format.shadowColor);
+ }
+};
diff --git a/src/tileset.h b/src/tileset.h
index 4ba9f016..daf8faf3 100644
--- a/src/tileset.h
+++ b/src/tileset.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef TILESET_H
-#define TILESET_H
+#pragma once
#include "resources/imageset.h"
@@ -48,5 +47,3 @@ class Tileset : public ImageSet
private:
unsigned mFirstGid;
};
-
-#endif // TILESET_H
diff --git a/src/units.cpp b/src/units.cpp
index fd01afd8..ee8fa6ea 100644
--- a/src/units.cpp
+++ b/src/units.cpp
@@ -112,8 +112,7 @@ void Units::readUnitNode(XML::Node node, const std::string &filename)
if (uLevel.name() == "level")
{
UnitLevel ul;
- ul.symbol = uLevel.getProperty("symbol",
- strprintf("¤%d",level));
+ ul.symbol = uLevel.getProperty("symbol", strprintf("¤%d", level));
ul.count = uLevel.getProperty("count", -1);
ul.round = uLevel.getProperty("round", bu.round);
diff --git a/src/units.h b/src/units.h
index eb6b6712..2a4bd458 100644
--- a/src/units.h
+++ b/src/units.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef UNITS_H
-#define UNITS_H
+#pragma once
#include <string>
#include "utils/xml.h"
@@ -44,5 +43,3 @@ class Units
*/
static std::string formatWeight(int value);
};
-
-#endif // UNITS_H
diff --git a/src/utils/base64.h b/src/utils/base64.h
index 92c23016..8fc7e79f 100644
--- a/src/utils/base64.h
+++ b/src/utils/base64.h
@@ -27,10 +27,7 @@
+----------------------------------------------------------------------+
*/
-#ifndef BASE64_H
-#define BASE64_H
+#pragma once
extern unsigned char *php3_base64_encode(const unsigned char *, int, int *);
extern unsigned char *php3_base64_decode(const unsigned char *, int, int *);
-
-#endif /* BASE64_H */
diff --git a/src/utils/copynpaste.h b/src/utils/copynpaste.h
index b6aa0de0..83e48891 100644
--- a/src/utils/copynpaste.h
+++ b/src/utils/copynpaste.h
@@ -18,6 +18,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#pragma once
+
#include <string>
/**
diff --git a/src/utils/dtor.h b/src/utils/dtor.h
index 76c68725..79b374d0 100644
--- a/src/utils/dtor.h
+++ b/src/utils/dtor.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef UTILS_DTOR_H
-#define UTILS_DTOR_H
+#pragma once
#include <algorithm>
#include <utility>
@@ -48,5 +47,3 @@ inline void delete_all(Container &c)
{
std::for_each(c.begin(), c.end(), make_dtor(c));
}
-
-#endif
diff --git a/src/utils/filesystem.h b/src/utils/filesystem.h
new file mode 100644
index 00000000..3474e7c4
--- /dev/null
+++ b/src/utils/filesystem.h
@@ -0,0 +1,297 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2024 The Mana Developers
+ *
+ * This file is part of The Mana Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+// Suppress deprecation warnings for PHYSFS_getUserDir
+#define PHYSFS_DEPRECATED
+
+#include "utils/physfsrwops.h"
+
+#include <optional>
+#include <string>
+
+/**
+ * These functions wrap PHYSFS functions to provide a more user-friendly
+ * interface and to limit the direct use of the PHYSFS API to a single file.
+ */
+namespace FS {
+
+inline bool init(const char *argv0)
+{
+ return PHYSFS_init(argv0) != 0;
+}
+
+inline void deinit()
+{
+ PHYSFS_deinit();
+}
+
+inline const char *getDirSeparator()
+{
+ return PHYSFS_getDirSeparator();
+}
+
+inline const char *getBaseDir()
+{
+ return PHYSFS_getBaseDir();
+}
+
+inline const char *getUserDir()
+{
+ return PHYSFS_getUserDir();
+}
+
+inline const char *getPrefDir(const char *org, const char *app)
+{
+ return PHYSFS_getPrefDir(org, app);
+}
+
+/**
+ * Sets the write directory.
+ *
+ * @param path The path of the directory to be added.
+ * @return <code>true</code> on success, <code>false</code> otherwise.
+ */
+inline bool setWriteDir(const std::string &path)
+{
+ return PHYSFS_setWriteDir(path.c_str()) != 0;
+}
+
+/**
+ * Adds a directory or archive to the search path. If append is true
+ * then the directory is added to the end of the search path, otherwise
+ * it is added at the front.
+ *
+ * @return <code>true</code> on success, <code>false</code> otherwise.
+ */
+inline bool addToSearchPath(const std::string &path, bool append)
+{
+ return PHYSFS_mount(path.c_str(), "/", append ? 1 : 0) != 0;
+}
+
+/**
+ * Checks whether the given file or directory exists in the search path.
+ */
+inline bool exists(const std::string &path)
+{
+ return PHYSFS_exists(path.c_str()) != 0;
+}
+
+inline std::optional<const char *> getRealDir(const std::string &path)
+{
+ auto dir = PHYSFS_getRealDir(path.c_str());
+ return dir ? std::optional<const char *>(dir) : std::nullopt;
+}
+
+/**
+ * Checks whether the given path is a directory.
+ */
+inline bool isDirectory(const std::string &path)
+{
+ PHYSFS_Stat stat;
+ if (PHYSFS_stat(path.c_str(), &stat) != 0)
+ {
+ return stat.filetype == PHYSFS_FILETYPE_DIRECTORY;
+ }
+ return false;
+}
+
+/**
+ * Creates a directory in the write path.
+ */
+inline bool mkdir(const std::string &path)
+{
+ return PHYSFS_mkdir(path.c_str()) != 0;
+}
+
+/**
+ * Helper class to iterate over the files in a directory.
+ * Based on https://stackoverflow.com/a/79051293/355419.
+ */
+class Files
+{
+public:
+ struct End {};
+ friend bool operator!=(const char *const *files, End)
+ { return *files != nullptr; }
+
+ explicit Files(char **files) : mFiles(files) {}
+ ~Files() { PHYSFS_freeList(mFiles); }
+
+ Files(const Files &) = delete;
+ Files &operator=(const Files &) = delete;
+
+ // Relies on C++17 support for begin/end to not have the same return type
+ const char* const *begin() const { return mFiles; }
+ End end() const { return End(); }
+
+private:
+ char **mFiles;
+};
+
+/**
+ * Returns a list of files in the given directory.
+ */
+inline Files enumerateFiles(const std::string &dir)
+{
+ return Files(PHYSFS_enumerateFiles(dir.c_str()));
+}
+
+/**
+ * File wrapper class to provide a more convenient API and automatic closing.
+ */
+class File
+{
+public:
+ explicit File(PHYSFS_file *file)
+ : file(file)
+ {}
+
+ ~File()
+ {
+ if (isOpen())
+ close();
+ }
+
+ bool isOpen() const
+ {
+ return file != nullptr;
+ }
+
+ operator bool() const
+ {
+ return isOpen();
+ }
+
+ bool close()
+ {
+ if (PHYSFS_close(file) != 0)
+ {
+ file = nullptr;
+ return true;
+ }
+ return false;
+ }
+
+ std::optional<size_t> read(void *data, size_t size)
+ {
+ auto len = PHYSFS_readBytes(file, data, size);
+ return len >= 0 ? std::optional<size_t>(len) : std::nullopt;
+ }
+
+ std::optional<size_t> write(const void *data, size_t size)
+ {
+ auto len = PHYSFS_writeBytes(file, data, size);
+ return len >= 0 ? std::optional<size_t>(len) : std::nullopt;
+ }
+
+ bool flush()
+ {
+ return PHYSFS_flush(file) != 0;
+ }
+
+ bool seek(size_t pos)
+ {
+ return PHYSFS_seek(file, pos) != 0;
+ }
+
+ std::optional<size_t> fileLength() const
+ {
+ auto len = PHYSFS_fileLength(file);
+ return len >= 0 ? std::optional<size_t>(len) : std::nullopt;
+ }
+
+ std::optional<size_t> tell() const
+ {
+ auto pos = PHYSFS_tell(file);
+ return pos >= 0 ? std::optional<size_t>(pos) : std::nullopt;
+ }
+
+ bool eof() const
+ {
+ return PHYSFS_eof(file) != 0;
+ }
+
+private:
+ PHYSFS_file *file;
+};
+
+inline File openWrite(const std::string &path)
+{
+ return File(PHYSFS_openWrite(path.c_str()));
+}
+
+inline File openAppend(const std::string &path)
+{
+ return File(PHYSFS_openAppend(path.c_str()));
+}
+
+inline File openRead(const std::string &path)
+{
+ return File(PHYSFS_openRead(path.c_str()));
+}
+
+inline const char *getLastError()
+{
+ return PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode());
+}
+
+//
+// Helper functions for loading files through SDL_RWops
+//
+
+inline SDL_RWops *openRWops(const std::string &path)
+{
+ return PHYSFSRWOPS_openRead(path.c_str());
+}
+
+/**
+ * Creates a buffered SDL_RWops.
+ *
+ * Used to workaround a performance issue when SDL_mixer is using stb_vorbis,
+ * in which case the file is read one byte at a time.
+ *
+ * See https://github.com/libsdl-org/SDL_mixer/issues/670
+ */
+inline SDL_RWops *openBufferedRWops(const std::string &path,
+ PHYSFS_uint64 bufferSize = 2048)
+{
+ if (auto file = PHYSFS_openRead(path.c_str()))
+ {
+ PHYSFS_setBuffer(file, bufferSize);
+ if (auto rw = PHYSFSRWOPS_makeRWops(file))
+ return rw;
+ else
+ PHYSFS_close(file);
+ }
+ return nullptr;
+}
+
+inline void *loadFile(const std::string &path, size_t &datasize)
+{
+ auto file = openRWops(path);
+ if (!file)
+ return nullptr;
+
+ return SDL_LoadFile_RW(file, &datasize, 1);
+}
+
+} // namespace FS
diff --git a/src/utils/gettext.h b/src/utils/gettext.h
index f3b12ada..1f40a4be 100644
--- a/src/utils/gettext.h
+++ b/src/utils/gettext.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef UTILS_GETTEXT_H
-#define UTILS_GETTEXT_H
+#pragma once
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -40,5 +39,3 @@
#define N_(s) ((char const *)s)
#endif
-
-#endif // UTILS_GETTEXT_H
diff --git a/src/utils/mathutils.h b/src/utils/mathutils.h
index 21eba16f..0a169df5 100644
--- a/src/utils/mathutils.h
+++ b/src/utils/mathutils.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef UTILS_MATHUTILS_H
-#define UTILS_MATHUTILS_H
+#pragma once
/* A very fast function to calculate the approximate inverse square root of a
* floating point value and a helper function that uses it for getting the
@@ -57,5 +56,3 @@ inline float weightedAverage(float n1, float n2, float w)
return w * n2 + (1.0f - w) * n1;
}
-
-#endif // UTILS_MATHUTILS_H
diff --git a/src/utils/mkdir.h b/src/utils/mkdir.h
index 3bd76145..817c79df 100644
--- a/src/utils/mkdir.h
+++ b/src/utils/mkdir.h
@@ -18,9 +18,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef MKDIR_H
-#define MKDIR_H
+#pragma once
int mkdir_r(const char *pathname);
-
-#endif
diff --git a/src/utils/mutex.h b/src/utils/mutex.h
index b4661c70..a0c72e95 100644
--- a/src/utils/mutex.h
+++ b/src/utils/mutex.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef MUTEX_H
-#define MUTEX_H
+#pragma once
#include "log.h"
@@ -104,4 +103,41 @@ inline MutexLocker::~MutexLocker()
mMutex->unlock();
}
-#endif // MUTEX_H
+/**
+ * A template class for wrapping data that is accessed by multiple threads.
+ */
+template <typename T>
+class ThreadSafe
+{
+ class Locked : private MutexLocker
+ {
+ public:
+ Locked(T &data, Mutex &mutex)
+ : MutexLocker(&mutex)
+ , mData(data)
+ {}
+
+ Locked(Locked&& rhs) = delete;
+ Locked(const Locked&) = delete;
+ Locked& operator=(const Locked&) = delete;
+ Locked& operator=(Locked&&) = delete;
+
+ T &operator*() const { return mData; }
+ T *operator->() const { return &mData; }
+
+ private:
+ T &mData;
+ };
+
+public:
+ ThreadSafe() = default;
+ ThreadSafe(const T &data)
+ : mData(data)
+ {}
+
+ Locked lock() { return { mData, mMutex }; }
+
+private:
+ T mData;
+ Mutex mMutex;
+};
diff --git a/src/utils/path.h b/src/utils/path.h
index 5338c74e..0b7019eb 100644
--- a/src/utils/path.h
+++ b/src/utils/path.h
@@ -18,8 +18,7 @@
* along with The Mana Server. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef UTILS_PATH_H
-#define UTILS_PATH_H
+#pragma once
#include <string>
@@ -29,5 +28,3 @@ namespace utils
std::string joinPaths(std::string_view path1, std::string_view path2);
std::string cleanPath(const std::string &path);
}
-
-#endif // UTILS_PATH_H
diff --git a/src/utils/sha256.h b/src/utils/sha256.h
index 8a9c2b70..6c2c3107 100644
--- a/src/utils/sha256.h
+++ b/src/utils/sha256.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef UTILS_SHA256_H
-#define UTILS_SHA256_H
+#pragma once
#include <string>
@@ -31,5 +30,3 @@
* @return the SHA-256 hash for the given string.
*/
std::string sha256(const std::string &string);
-
-#endif // UTILS_SHA256_H
diff --git a/src/utils/specialfolder.h b/src/utils/specialfolder.h
index 36a4e0c1..9e069310 100644
--- a/src/utils/specialfolder.h
+++ b/src/utils/specialfolder.h
@@ -18,8 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef SPECIALFOLDER_H
-#define SPECIALFOLDER_H
+#pragma once
#ifdef _WIN32
#include <shlobj.h>
@@ -31,5 +30,3 @@ std::string getSpecialFolderLocation(const KNOWNFOLDERID &folderId);
#include <string>
std::string getResourcesLocation();
#endif
-
-#endif
diff --git a/src/utils/stringutils.cpp b/src/utils/stringutils.cpp
index 747243fd..ba5f575b 100644
--- a/src/utils/stringutils.cpp
+++ b/src/utils/stringutils.cpp
@@ -21,8 +21,6 @@
#include "utils/stringutils.h"
-#include "log.h"
-
#include <cstring>
#include <algorithm>
#include <cstdarg>
@@ -101,16 +99,15 @@ std::string strprintf(char const *format, ...)
return res;
}
-std::string &removeBadChars(std::string &str)
+std::string &replaceCharacters(std::string &str,
+ std::string_view chars,
+ char replacement)
{
- std::string::size_type pos;
- do
+ for (auto &c : str)
{
- pos = str.find_first_of("@#[]");
- if (pos != std::string::npos)
- str.erase(pos, 1);
- } while (pos != std::string::npos);
-
+ if (chars.find(c) != std::string::npos)
+ c = replacement;
+ }
return str;
}
@@ -126,29 +123,6 @@ std::string removeColors(std::string msg)
return msg;
}
-int compareStrI(const std::string &a, const std::string &b)
-{
- std::string::const_iterator itA = a.begin();
- std::string::const_iterator endA = a.end();
- std::string::const_iterator itB = b.begin();
- std::string::const_iterator endB = b.end();
-
- for (; itA < endA && itB < endB; ++itA, ++itB)
- {
- int comp = tolower(*itA) - tolower(*itB);
- if (comp)
- return comp;
- }
-
- // Check string lengths
- if (itA == endA && itB == endB)
- return 0;
- else if (itA == endA)
- return -1;
- else
- return 1;
-}
-
bool isWordSeparator(char chr)
{
return (chr == ' ' || chr == ',' || chr == '.' || chr == '"');
@@ -220,49 +194,27 @@ std::string normalize(const std::string &name)
return toLower(trim(normalized));
}
-std::string removeTrailingSymbol(const std::string &s, const char c)
-{
- // Remove the trailing symblol at the end of the string
- if (!s.empty() && s.at(s.size() - 1) == c)
- return s.substr(0, s.size() - 1);
- return std::string(s);
-}
-
-std::string getHostNameFromURL(const std::string &url)
+std::string getDirectoryFromURL(const std::string &url)
{
- std::string myHostName;
+ std::string directory = url;
- // Don't go out of range in the next check
- if (url.length() < 2)
- return myHostName;
+ // Parse out any "http://", "ftp://", etc...
+ size_t pos = directory.find("://");
+ if (pos != std::string::npos)
+ directory.erase(0, pos + 3);
- // Remove any trailing slash at the end of the update host
- myHostName = removeTrailingSymbol(url, '/');
+ // Replace characters which are not valid or difficult in file system paths
+ replaceCharacters(directory, ":*?\"<>| ", '_');
- // Parse out any "http://", "ftp://", ect...
- size_t pos = myHostName.find("://");
- if (pos == std::string::npos)
+ // Replace ".." (double dots) with "_" to avoid directory traversal.
+ pos = directory.find("..");
+ while (pos != std::string::npos)
{
- logger->log("Warning: no protocol was specified for the url: %s",
- url.c_str());
+ directory.replace(pos, 2, "_");
+ pos = directory.find("..");
}
- if (myHostName.empty() || pos + 3 >= myHostName.length())
- {
- logger->log("Error: Invalid url: %s", url.c_str());
- return myHostName;
- }
- myHostName = myHostName.substr(pos + 3);
-
- // Remove possible trailing port (i.e.: localhost:8000 -> localhost)
- pos = myHostName.find(":");
- if (pos != std::string::npos)
- myHostName = myHostName.substr(0, pos);
-
- // remove possible other junk
- removeBadChars(myHostName);
-
- return myHostName;
+ return directory;
}
std::string join(const std::vector<std::string> &strings, const char *separator)
diff --git a/src/utils/stringutils.h b/src/utils/stringutils.h
index e7ff1332..1fa8e1e6 100644
--- a/src/utils/stringutils.h
+++ b/src/utils/stringutils.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef UTILS_STRINGUTILS_H
-#define UTILS_STRINGUTILS_H
+#pragma once
#include <optional>
#include <sstream>
@@ -96,12 +95,16 @@ std::string strprintf(char const *, ...)
;
/**
- * Removes bad characters from a string
+ * Replaces a set of characters with another character.
*
* @param str the string to remove the bad chars from
+ * @param chars the bad characters to remove
+ * @param replacement the character to replace the bad chars with
* @return a reference to the string without bad chars
*/
-std::string &removeBadChars(std::string &str);
+std::string &replaceCharacters(std::string &str,
+ std::string_view chars,
+ char replacement = '_');
/**
* Removes colors from a string
@@ -112,16 +115,6 @@ std::string &removeBadChars(std::string &str);
std::string removeColors(std::string msg);
/**
- * Compares the two strings case-insensitively.
- *
- * @param a the first string in the comparison
- * @param b the second string in the comparison
- * @return 0 if the strings are equal, positive if the first is greater,
- * negative if the second is greater
- */
-int compareStrI(const std::string &a, const std::string &b);
-
-/**
* Returns whether a string starts with a given prefix.
*/
inline bool startsWith(const std::string &str, const char *prefix)
@@ -145,11 +138,31 @@ std::string findSameSubstring(const std::string &str1,
*/
bool getBoolFromString(std::string text, bool def = false);
+/**
+ * This class can be partially specialized to provide custom string conversion.
+ *
+ * This is done instead of overloading the base function template to avoid
+ * ambiguity.
+ */
+template<typename T, typename Enable = void>
+struct FromString;
+
+template<typename T>
+void fromString(const char *str, T &value)
+{
+ FromString<T>()(str, value);
+}
+
inline void fromString(const char *str, std::string &value)
{
value = str;
}
+inline void fromString(const char *str, std::string_view &value)
+{
+ value = str;
+}
+
inline void fromString(const char *str, int &value)
{
value = atoi(str);
@@ -180,19 +193,23 @@ inline void fromString(const char *str, bool &value)
value = getBoolFromString(str);
}
-template<typename Enum, std::enable_if_t<std::is_enum_v<Enum>, bool> = true>
-inline void fromString(const char *str, Enum &value)
+template<typename T>
+struct FromString<T, std::enable_if_t<std::is_enum_v<T>>>
{
- value = static_cast<Enum>(atoi(str));
-}
+ void operator() (const char *str, T &value)
+ {
+ fromString(str, reinterpret_cast<std::underlying_type_t<T>&>(value));
+ }
+};
template<typename T>
-inline void fromString(const char *str, std::optional<T> &value)
+struct FromString<std::optional<T>>
{
- T v;
- fromString(str, v);
- value = v;
-}
+ void operator() (const char *str, std::optional<T> &value)
+ {
+ fromString(str, value.emplace());
+ }
+};
/**
* Returns the most approaching string of base from candidates.
@@ -206,20 +223,15 @@ std::string autocomplete(const std::vector<std::string> &candidates,
std::string normalize(const std::string &name);
/**
- * Remove a potential trailing symbol from a string.
- */
-std::string removeTrailingSymbol(const std::string &s, const char c);
-
-/**
- * Gets the hostname out of the given URL.
- * i.e.: http://www.manasource.org:9601 -> www.manasource.org
+ * Derives a directory from the given URL, stripping the schema and replacing
+ * certain invalid characters.
+ *
+ * i.e.: http://www.manasource.org:9601/updates/ -> www.manasource.org_9601/updates/
*/
-std::string getHostNameFromURL(const std::string &url);
+std::string getDirectoryFromURL(const std::string &url);
/**
* Joins a vector of strings into one string, separated by the given
* separator.
*/
std::string join(const std::vector<std::string> &strings, const char *separator);
-
-#endif // UTILS_STRINGUTILS_H
diff --git a/src/utils/time.h b/src/utils/time.h
index 58b8164a..590041e3 100644
--- a/src/utils/time.h
+++ b/src/utils/time.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
- #ifndef UTILS_TIME_H
- #define UTILS_TIME_H
+#pragma once
#include <cstdint>
@@ -107,5 +106,3 @@ public:
private:
uint32_t mTimeout = 0;
};
-
-#endif // UTILS_TIME_H
diff --git a/src/utils/xml.cpp b/src/utils/xml.cpp
index 7ea5b9d6..c408c9c2 100644
--- a/src/utils/xml.cpp
+++ b/src/utils/xml.cpp
@@ -25,16 +25,13 @@
#include "log.h"
-#include "resources/resourcemanager.h"
-
-#include "utils/zlib.h"
+#include "utils/filesystem.h"
namespace XML
{
struct XMLContext
{
std::string file;
- bool resman;
};
#if LIBXML_VERSION >= 21200
@@ -49,7 +46,7 @@ namespace XML
logger->log("Error in XML file '%s' on line %d",
context->file.c_str(), error->line);
else
- logger->log("Error in unknown xml file on line %d",
+ logger->log("Error in unknown XML file on line %d",
error->line);
logger->log("%s", error->message);
@@ -63,32 +60,27 @@ namespace XML
{
XMLContext ctx;
ctx.file = filename;
- ctx.resman = useResman;
xmlSetStructuredErrorFunc(&ctx, xmlLogger);
- int size;
+ size_t size;
char *data = nullptr;
+
if (useResman)
- {
- ResourceManager *resman = ResourceManager::getInstance();
- data = (char*) resman->loadFile(filename, size);
- }
+ data = (char *) FS::loadFile(filename, size);
else
- {
- data = (char *) loadCompressedFile(filename, size);
- }
+ data = (char *) SDL_LoadFile(filename.c_str(), &size);
if (data)
{
mDoc = xmlParseMemory(data, size);
- free(data);
+ SDL_free(data);
if (!mDoc)
logger->log("Error parsing XML file %s", filename.c_str());
}
else
{
- logger->log("Error loading %s", filename.c_str());
+ logger->log("Error loading %s: %s", filename.c_str(), SDL_GetError());
}
xmlSetStructuredErrorFunc(nullptr, xmlLogger);
diff --git a/src/utils/xml.h b/src/utils/xml.h
index 13c06988..787504bb 100644
--- a/src/utils/xml.h
+++ b/src/utils/xml.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef XML_H
-#define XML_H
+#pragma once
#include "utils/stringutils.h"
@@ -295,5 +294,3 @@ namespace XML
xmlTextWriterWriteString(mWriter, BAD_CAST text.c_str());
}
}
-
-#endif // XML_H
diff --git a/src/utils/zlib.cpp b/src/utils/zlib.cpp
index 773c7484..f78b235e 100644
--- a/src/utils/zlib.cpp
+++ b/src/utils/zlib.cpp
@@ -25,8 +25,6 @@
#include <cassert>
#include <cstdlib>
-#include <fstream>
-#include <iostream>
#include <zlib.h>
/**
@@ -131,53 +129,3 @@ int inflateMemory(unsigned char *in, unsigned int inLength,
return outLength;
}
-
-void *loadCompressedFile(const std::string &filename, int &filesize)
-{
- std::ifstream file;
- file.open(filename, std::ios::in);
-
- if (file.is_open())
- {
- // Get length of file
- file.seekg (0, std::ios::end);
- filesize = file.tellg();
- file.seekg(0, std::ios::beg);
-
- char *buffer = (char *) malloc(filesize);
-
- file.read(buffer, filesize);
- file.close();
-
- unsigned char *inflated;
- unsigned int inflatedSize;
-
- if (filename.find(".gz", filename.length() - 3) != std::string::npos)
- {
- // Inflate the gzipped map data
- inflatedSize =
- inflateMemory((unsigned char*) buffer, filesize, inflated);
- free(buffer);
-
- if (!inflated)
- {
- logger->log("Could not decompress file: %s",
- filename.c_str());
- return nullptr;
- }
-
- filesize = inflatedSize;
- return inflated;
- }
- else
- {
- return buffer;
- }
- }
- else
- {
- logger->log("Error loading file from drive: %s", filename.c_str());
- }
-
- return nullptr;
-}
diff --git a/src/utils/zlib.h b/src/utils/zlib.h
index 53dfd613..46429c0b 100644
--- a/src/utils/zlib.h
+++ b/src/utils/zlib.h
@@ -19,10 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef UTILS_ZLIB_H
-#define UTILS_ZLIB_H
-
-#include <string>
+#pragma once
/**
* Inflates either zlib or gzip deflated memory. The inflated memory is
@@ -33,16 +30,3 @@ int inflateMemory(unsigned char *in, unsigned int inLength,
int inflateMemory(unsigned char *in, unsigned int inLength,
unsigned char *&out);
-
-/**
- * Loads the given file from the filesystem, uncompressing if it ends in ".gz".
- *
- * @param filename The name of the file to be loaded and uncompressed
- * @param filesize The size of the file that was loaded and uncompressed.
- *
- * @return An allocated byte array containing the data that was loaded and
- * uncompressed, or <code>NULL</code> on fail.
- */
-void *loadCompressedFile(const std::string &filename, int &filesize);
-
-#endif // UTILS_ZLIB_H
diff --git a/src/variabledata.h b/src/variabledata.h
index 4135e115..4f6695a7 100644
--- a/src/variabledata.h
+++ b/src/variabledata.h
@@ -18,8 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef VARIABLEDATA_H
-#define VARIABLEDATA_H
+#pragma once
#include <string>
@@ -122,5 +121,3 @@ public:
private:
ActorSprite *mData;
};
-
-#endif
diff --git a/src/vector.h b/src/vector.h
index 6bf6d81a..142bb077 100644
--- a/src/vector.h
+++ b/src/vector.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef VECTOR_H
-#define VECTOR_H
+#pragma once
#include <cmath>
@@ -183,5 +182,3 @@ inline bool operator == (const Vector &a, const Vector &b)
* Appends a string representation of a vector to the output stream.
*/
std::ostream& operator <<(std::ostream &os, const Vector &v);
-
-#endif // VECTOR_H
diff --git a/src/video.h b/src/video.h
index c98434e6..47aed627 100644
--- a/src/video.h
+++ b/src/video.h
@@ -19,8 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef VIDEO_H
-#define VIDEO_H
+#pragma once
#include "graphics.h"
@@ -113,5 +112,3 @@ private:
std::unique_ptr<Graphics> mGraphics;
SDL_Window *mWindow = nullptr;
};
-
-#endif // VIDEO_H