Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ following resources to learn the basic gameplay concepts:
You can play singleplayer levels using the Skirmish menu, or host/join a
multiplayer game by using the corresponding menus.

Use Load Game in the game options, or F8 while hosting a local game, to open
the saved-game browser; Back preserves the current game until another save is
selected. Loaded single-player saves resume their stored side without a new
seat-selection step. Long save descriptions can be scrolled.

### Be part of the community

As free software aficionados, we value community-based development and
Expand Down
12 changes: 7 additions & 5 deletions gui/MenuLoad.layout
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,20 @@
<Property name="BackgroundEnabled" value="False" />
</Window>
<Window type="OD/FrameWindow" name="LevelWindowFrame" >
<Property name="Area" value="{{0.5,-315},{0.5,-180},{0.5,315},{0.5,240}}" />
<Property name="Area" value="{{0.5,-425},{0.5,-180},{0.5,425},{0.5,240}}" />
<Property name="Text" value="Please choose a saved game..." />
<Property name="MaxSize" value="{{0,790},{0,470}}" />
<Property name="MinSize" value="{{0,630},{0,420}}" />
<Property name="MaxSize" value="{{0,950},{0,470}}" />
<Property name="MinSize" value="{{0,850},{0,420}}" />
<Property name="AlwaysOnTop" value="True" />
<Window type="OD/Listbox" name="SaveGameSelect" >
<Property name="Area" value="{{0,15},{0,40},{0.5,-20},{0.7,0}}" />
<Property name="Area" value="{{0,15},{0,40},{0.65,-20},{0.7,0}}" />
<Property name="ForceVertScrollbar" value="True" />
<Property name="Sort" value="True" />
</Window>
<Window type="OD/StaticText" name="MapDescriptionText" >
<Property name="Area" value="{{0.5,0},{0,40},{1,-15},{0.7,0}}" />
<Property name="LookNFeel" value="OD/MenuDescription" />
<Property name="VertScrollbar" value="True" />
<Property name="Area" value="{{0.65,0},{0,40},{1,-15},{0.7,0}}" />
<Property name="MaxSize" value="{{1,0},{1,0}}" />
<Property name="FrameEnabled" value="False" />
<Property name="HorzFormatting" value="WordWrapLeftAligned" />
Expand Down
69 changes: 69 additions & 0 deletions gui/OD.looknfeel
Original file line number Diff line number Diff line change
Expand Up @@ -4772,4 +4772,73 @@
</StateImagery>
</WidgetLook>

<WidgetLook name="OD/MenuScrollbarThumb" inherits="OD/VerticalScrollbarThumb">
<StateImagery name="Normal"><Layer><Section section="normal" /></Layer></StateImagery>
<StateImagery name="Hover"><Layer><Section section="hover" /></Layer></StateImagery>
<StateImagery name="Pushed"><Layer><Section section="hover" /></Layer></StateImagery>
<StateImagery name="Disabled"><Layer><Section section="normal" /></Layer></StateImagery>
<ImagerySection name="normal"><ImageryComponent><Area>
<Dim type="LeftEdge"><AbsoluteDim value="0" /></Dim>
<Dim type="TopEdge"><AbsoluteDim value="0" /></Dim>
<Dim type="RightEdge"><UnifiedDim scale="1" type="RightEdge" /></Dim>
<Dim type="BottomEdge"><UnifiedDim scale="1" type="BottomEdge" /></Dim>
</Area>
<Image name="OpenDungeonsSkin/SelectionBrush" />
<Colours topLeft="FFBBBBBB" topRight="FFBBBBBB" bottomLeft="FFBBBBBB" bottomRight="FFBBBBBB" />
<VertFormat type="Stretched" /><HorzFormat type="Stretched" />
</ImageryComponent></ImagerySection>
<ImagerySection name="hover"><ImageryComponent><Area>
<Dim type="LeftEdge"><AbsoluteDim value="0" /></Dim>
<Dim type="TopEdge"><AbsoluteDim value="0" /></Dim>
<Dim type="RightEdge"><UnifiedDim scale="1" type="RightEdge" /></Dim>
<Dim type="BottomEdge"><UnifiedDim scale="1" type="BottomEdge" /></Dim>
</Area>
<Image name="OpenDungeonsSkin/SelectionBrush" />
<VertFormat type="Stretched" /><HorzFormat type="Stretched" />
</ImageryComponent></ImagerySection>
</WidgetLook>

<WidgetLook name="OD/MenuScrollbar" inherits="OD/VerticalScrollbar">
<StateImagery name="Enabled"><Layer><Section section="main" /></Layer></StateImagery>
<StateImagery name="Disabled"><Layer><Section section="main" /></Layer></StateImagery>
<Child nameSuffix="__auto_thumb__" type="OD/MenuScrollbarThumb">
<Area>
<Dim type="LeftEdge"><AbsoluteDim value="2" /></Dim>
<Dim type="TopEdge"><AbsoluteDim value="0" /></Dim>
<Dim type="Width"><UnifiedDim scale="1" offset="-4" type="Width" /></Dim>
<Dim type="Height"><UnifiedDim scale="0.1" type="Height" /></Dim>
</Area>
<Property name="MinSize" value="{{0,0},{0,25}}" />
</Child>
<ImagerySection name="main"><FrameComponent>
<Area>
<Dim type="LeftEdge"><AbsoluteDim value="0" /></Dim>
<Dim type="TopEdge"><AbsoluteDim value="0" /></Dim>
<Dim type="Width"><UnifiedDim scale="1" type="Width" /></Dim>
<Dim type="Height"><UnifiedDim scale="1" type="Height" /></Dim>
</Area>
<Image component="TopLeftCorner" name="OpenDungeonsSkin/VerticalSliderTopLeft" />
<Image component="TopRightCorner" name="OpenDungeonsSkin/VerticalSliderTopRight" />
<Image component="BottomLeftCorner" name="OpenDungeonsSkin/VerticalSliderBottomLeft" />
<Image component="BottomRightCorner" name="OpenDungeonsSkin/VerticalSliderBottomRight" />
<Image component="LeftEdge" name="OpenDungeonsSkin/VerticalSliderLeft" />
<Image component="RightEdge" name="OpenDungeonsSkin/VerticalSliderRight" />
<Image component="BottomEdge" name="OpenDungeonsSkin/VerticalSliderBottom" />
<Image component="TopEdge" name="OpenDungeonsSkin/VerticalSliderTop" />

</FrameComponent>
</ImagerySection>
</WidgetLook>

<WidgetLook name="OD/MenuDescription" inherits="OD/StaticText">
<Child nameSuffix="__auto_vscrollbar__" type="OD/MenuScrollbar">
<Area>
<Dim type="LeftEdge"><AbsoluteDim value="0" /></Dim>
<Dim type="TopEdge"><AbsoluteDim value="0" /></Dim>
<Dim type="Width"><OperatorDim op="Multiply"><ImageDim name="OpenDungeonsSkin/MiniVertScrollThumbNormal" dimension="Width" /><AbsoluteDim value="1.5" /></OperatorDim></Dim>
<Dim type="Height"><UnifiedDim scale="1" type="Height" /></Dim>
</Area>
<HorzAlignment type="RightAligned" />
</Child>
</WidgetLook>
</Falagard>
2 changes: 2 additions & 0 deletions gui/ODSkin.scheme
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,6 @@
<FalagardMapping lookNFeel="OD/Tooltip" renderer="Core/Tooltip" targetType="CEGUI/Tooltip" windowType="OD/Tooltip" />
<FalagardMapping lookNFeel="OD/StaticImage" renderer="Core/StaticImage" targetType="DefaultWindow" windowType="OD/StaticImage" />
<FalagardMapping lookNFeel="OD/StaticText" renderer="Core/StaticText" targetType="DefaultWindow" windowType="OD/StaticText" />
<FalagardMapping lookNFeel="OD/MenuScrollbarThumb" renderer="Core/Button" targetType="CEGUI/Thumb" windowType="OD/MenuScrollbarThumb" />
<FalagardMapping lookNFeel="OD/MenuScrollbar" renderer="Core/Scrollbar" targetType="CEGUI/Scrollbar" windowType="OD/MenuScrollbar" />
</GUIScheme>
3 changes: 1 addition & 2 deletions gui/WindowGameOptions.layout
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@
<Property name="Area" value="{{0.5,-80},{0,180},{0.5,80},{0,213}}" />
<Property name="IconImage" value="OpenDungeonsIcons/LoadIcon" />
<Property name="Text" value="Load Game (F8)" />
<Property name="Disabled" value="True" />
<Property name="TooltipText" value="Not implemented yet" />
<Property name="TooltipText" value="Load a saved game." />
</Window>
<Window type="OD/IconButton" name="SettingsButton" >
<Property name="Area" value="{{0.5,-80},{0,235},{0.5,80},{0,268}}" />
Expand Down
59 changes: 59 additions & 0 deletions source/modes/AbstractApplicationMode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,29 @@
#include <CEGUI/System.h>
#include <CEGUI/GUIContext.h>
#include <CEGUI/widgets/PushButton.h>
#include <CEGUI/widgets/FrameWindow.h>
#include <CEGUI/widgets/Combobox.h>
#include <CEGUI/widgets/PopupMenu.h>

#include <algorithm>

namespace
{
void collectEscapeWindows(CEGUI::Window* window, std::vector<CEGUI::Window*>& windows)
{
if(window == nullptr || !window->isVisible() || window->isDisabled())
return;

CEGUI::Combobox* combo = dynamic_cast<CEGUI::Combobox*>(window);
if(dynamic_cast<CEGUI::FrameWindow*>(window) != nullptr ||
dynamic_cast<CEGUI::PopupMenu*>(window) != nullptr ||
(combo != nullptr && combo->isDropDownListVisible()))
windows.push_back(window);

for(size_t i = 0; i < window->getChildCount(); ++i)
collectEscapeWindows(window->getChildAtIdx(i), windows);
}
}

AbstractApplicationMode::~AbstractApplicationMode()
{
Expand Down Expand Up @@ -78,6 +101,11 @@ bool AbstractApplicationMode::keyPressed(const OIS::KeyEvent& arg)
{
switch (arg.key)
{
case OIS::KC_ESCAPE:
if(!CEGUI::System::getSingleton().getDefaultGUIContext().injectKeyDown(CEGUI::Key::Escape) &&
!closeTopWindow() && mModeType != ModeManager::ADVERTISMENT)
goBack();
break;
default:
CEGUI::System::getSingleton().getDefaultGUIContext().injectChar(
static_cast<CEGUI::String::value_type>(arg.text));
Expand All @@ -104,3 +132,34 @@ bool AbstractApplicationMode::goBack(const CEGUI::EventArgs&)
mModeManager->requestPreviousMode();
return true;
}

bool AbstractApplicationMode::closeTopWindow()
{
CEGUI::GUIContext& context = CEGUI::System::getSingleton().getDefaultGUIContext();
std::vector<CEGUI::Window*> windows;
collectEscapeWindows(context.getModalWindow() != nullptr ?
context.getModalWindow() : context.getRootWindow(), windows);
std::sort(windows.begin(), windows.end(), [](CEGUI::Window* left, CEGUI::Window* right)
{
return left->isInFront(*right);
});

for(CEGUI::Window* window : windows)
{
if(CEGUI::Combobox* combo = dynamic_cast<CEGUI::Combobox*>(window))
{
combo->hideDropList();
return true;
}
if(CEGUI::PopupMenu* popup = dynamic_cast<CEGUI::PopupMenu*>(window))
{
popup->closePopupMenu();
return true;
}
CEGUI::WindowEventArgs args(window);
window->fireEvent(CEGUI::FrameWindow::EventCloseClicked, args);
if(args.handled != 0)
return true;
}
return false;
}
3 changes: 3 additions & 0 deletions source/modes/AbstractApplicationMode.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ class AbstractApplicationMode :
{}

protected:
//! Close the frontmost visible GUI window through its existing cancel handler.
bool closeTopWindow();

ModeManager& getModeManager()
{
return *mModeManager;
Expand Down
12 changes: 6 additions & 6 deletions source/modes/EditorMode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,11 +340,6 @@ EditorMode::EditorMode(ModeManager* modeManager):
CEGUI::FrameWindow::EventCloseClicked,
CEGUI::Event::Subscriber(&EditorMode::toggleOptionsWindow, this)
));
addEventConnection(
mRootWindow->getChild("EditorOptionsWindow")->subscribeEvent(
CEGUI::FrameWindow::EventCloseClicked,
CEGUI::Event::Subscriber(&EditorMode::toggleOptionsWindow, this)
));
addEventConnection(
mRootWindow->getChild("EditorOptionsWindow/SaveLevelButton")
->subscribeEvent(
Expand Down Expand Up @@ -1497,9 +1492,14 @@ bool EditorMode::hidePortalWaveWindow(const CEGUI::EventArgs& /*arg*/)
bool EditorMode::keyPressed(const OIS::KeyEvent &arg)
{
// Inject key to the gui currently displayed
CEGUI::System::getSingleton().getDefaultGUIContext().injectKeyDown(static_cast<CEGUI::Key::Scan>(arg.key));
const bool guiHandledKey = CEGUI::System::getSingleton().getDefaultGUIContext().injectKeyDown(
static_cast<CEGUI::Key::Scan>(arg.key));
CEGUI::System::getSingleton().getDefaultGUIContext().injectChar(arg.text);

if(arg.key == OIS::KC_ESCAPE && mCurrentInputMode != InputModeConsole &&
mCurrentInputMode != InputModeChat && (guiHandledKey || closeTopWindow()))
return true;

if (mCurrentInputMode == InputModeChat || mCurrentInputMode == InputModeSave || mCurrentInputMode == InputModeLoad || mCurrentInputMode == InputModeNew)
return true;

Expand Down
37 changes: 35 additions & 2 deletions source/modes/GameMode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "gamemap/Pathfinding.h"
#include "modes/GameEditorModeConsole.h"
#include "modes/InputBridge.h"
#include "modes/MenuModeLoad.h"
#include "network/ChatEventMessage.h"
#include "network/ODClient.h"
#include "network/ODServer.h"
Expand Down Expand Up @@ -210,6 +211,10 @@ GameMode::GameMode(ModeManager *modeManager):
)
);
saveGameButtonWindow->setEnabled(ODServer::getSingleton().isConnected());
CEGUI::Window* loadGameButtonWindow = guiSheet->getChild("GameOptionsWindow/LoadGameButton");
addEventConnection(loadGameButtonWindow->subscribeEvent(CEGUI::PushButton::EventClicked,
CEGUI::Event::Subscriber(&GameMode::loadGame, this)));
loadGameButtonWindow->setEnabled(ODServer::getSingleton().isConnected());
addEventConnection(
guiSheet->getChild("GameOptionsWindow/SettingsButton")->subscribeEvent(
CEGUI::PushButton::EventClicked,
Expand Down Expand Up @@ -792,8 +797,11 @@ bool GameMode::mouseReleased(const OIS::MouseEvent &arg, OIS::MouseButtonID id)

bool GameMode::keyPressed(const OIS::KeyEvent& arg)
{
if(mLoadMenu && mLoadMenu->isOpenInGame())
return mLoadMenu->keyPressed(arg);
// Inject key to Gui
CEGUI::System::getSingleton().getDefaultGUIContext().injectKeyDown(static_cast<CEGUI::Key::Scan>(arg.key));
const bool guiHandledKey = CEGUI::System::getSingleton().getDefaultGUIContext().injectKeyDown(
static_cast<CEGUI::Key::Scan>(arg.key));
if (arg.text != 0 && !getConsole()->isFreshlyEnabled())
{
CEGUI::System::getSingleton().getDefaultGUIContext().injectChar(arg.text);
Expand All @@ -807,6 +815,8 @@ bool GameMode::keyPressed(const OIS::KeyEvent& arg)
return getConsole()->keyPressed(arg);
case InputModeNormal:
default:
if(arg.key == OIS::KC_ESCAPE && guiHandledKey)
return true;
return keyPressedNormal(arg);
}
}
Expand Down Expand Up @@ -837,6 +847,10 @@ bool GameMode::keyPressedNormal(const OIS::KeyEvent &arg)
saveGame();
break;

case OIS::KC_F8:
loadGame();
break;

case OIS::KC_F9:
RenderManager::getSingleton().rrToggleHandSelectorVisibility();
break;
Expand Down Expand Up @@ -934,8 +948,15 @@ bool GameMode::keyPressedNormal(const OIS::KeyEvent &arg)
break;
}

// Quit the game
// Close one GUI layer before considering a new exit confirmation.
case OIS::KC_ESCAPE:
if(closeTopWindow())
break;
if(mRootWindow->getChild("GameEventText")->isVisible())
{
mRootWindow->getChild("GameEventText")->hide();
break;
}
mExitToDesktop = false;
popupExit(!mGameMap->getGamePaused());
break;
Expand Down Expand Up @@ -1404,6 +1425,18 @@ bool GameMode::showSkillFromOptions(const CEGUI::EventArgs& /*e*/)
return true;
}

bool GameMode::loadGame(const CEGUI::EventArgs& /*e*/)
{
if(!ODServer::getSingleton().isConnected())
return true;
if(!mLoadMenu)
mLoadMenu.reset(new MenuModeLoad(&getModeManager(), true));
// Retain the options window so returning from the browser restores its caller.
showOptionsWindow();
mLoadMenu->activate();
return true;
}

bool GameMode::saveGame(const CEGUI::EventArgs& /*e*/)
{
// We can save if launching in server mode only
Expand Down
3 changes: 3 additions & 0 deletions source/modes/GameMode.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class Window;
}

class Creature;
class MenuModeLoad;

enum class SpellType;
enum class SkillType;
Expand Down Expand Up @@ -208,6 +209,7 @@ class GameMode final : public GameEditorModeBase, public InputCommand
bool showObjectivesFromOptions(const CEGUI::EventArgs& e = {});
bool showSkillFromOptions(const CEGUI::EventArgs& e = {});
bool saveGame(const CEGUI::EventArgs& e = {});
bool loadGame(const CEGUI::EventArgs& e = {});
bool showSettingsFromOptions(const CEGUI::EventArgs& e = {});

//! \brief Handle the keyboard input in normal mode
Expand All @@ -220,6 +222,7 @@ class GameMode final : public GameEditorModeBase, public InputCommand
virtual bool keyReleasedNormal (const OIS::KeyEvent &arg);

private:
std::unique_ptr<MenuModeLoad> mLoadMenu;
//! \brief Whether the pending exit confirmation should leave to the desktop
//! rather than back to the main menu. Set by the button that opened the
//! confirmation popup.
Expand Down
Loading