diff --git a/distrib/autobuild.py b/distrib/autobuild.py index da6ccdadb3..de2116d0c3 100755 --- a/distrib/autobuild.py +++ b/distrib/autobuild.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python2.7 # coding=utf-8 # # Script for performing automated build events. @@ -64,9 +64,12 @@ def todays_platform_release(): os.chdir(builder.config.DISTRIB_DIR) oldFiles = DirState('releases', subdirs=False) - print 'platform_release.py...' - os.system("python platform_release.py > %s 2> %s" % \ - ('buildlog.txt', 'builderrors.txt')) + try: + print 'platform_release.py...' + run_python2("platform_release.py > %s 2> %s" % \ + ('buildlog.txt', 'builderrors.txt')) + except Exception, x: + print 'Error during platform_release:', x for n in DirState('releases', subdirs=False).list_new_files(oldFiles): # Copy any new files. diff --git a/distrib/build_number.py b/distrib/build_number.py index 800c873561..a9bd3f73ae 100755 --- a/distrib/build_number.py +++ b/distrib/build_number.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python2.7 # Determining the number of a build. import time diff --git a/distrib/build_version.py b/distrib/build_version.py index 487f58a259..bf3a68edf8 100644 --- a/distrib/build_version.py +++ b/distrib/build_version.py @@ -3,7 +3,8 @@ import os import string -DOOMSDAY_DIR = os.path.join(os.getcwd(), '..', 'doomsday') +DOOMSDAY_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', 'doomsday') + DOOMSDAY_VERSION_FULL = "0.0.0-Name" DOOMSDAY_VERSION_FULL_PLAIN = "0.0.0" DOOMSDAY_VERSION_MAJOR = 0 diff --git a/distrib/builder/utils.py b/distrib/builder/utils.py index a10e0f95eb..672866fd86 100644 --- a/distrib/builder/utils.py +++ b/distrib/builder/utils.py @@ -192,4 +192,17 @@ def version_cmp(a, b): def system_command(cmd): result = subprocess.call(cmd, shell=True) if result != 0: - raise Exception("Error from " + cmd) + raise Exception("System command \"%s\" returned error code %i" % (cmd, result)) + + +def python2_executable(): + if sys.platform[:3] == 'win': + return 'python' + elif mac_os_version() == '10.5': + return '/usr/bin/env python2.5' # required by Snowberry + else: + return '/usr/bin/env python2.7' + + +def run_python2(script): + system_command(python2_executable() + " " + script) diff --git a/distrib/pilot.py b/distrib/pilot.py index 15dcd994f6..2ad518b485 100755 --- a/distrib/pilot.py +++ b/distrib/pilot.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python2.7 # coding=utf-8 # # The Doomsday Build Pilot @@ -35,6 +35,7 @@ import struct import time import SocketServer +import builder.utils def homeDir(): """Determines the path of the pilot home directory.""" @@ -414,8 +415,7 @@ def handleCompletedTasks(): def autobuild(cmd): - cmdLine = "python %s %s" % (os.path.join(pilotcfg.DISTRIB_DIR, - 'autobuild.py'), cmd) + cmdLine = "%s %s" % (os.path.join(pilotcfg.DISTRIB_DIR, 'autobuild.py'), cmd) cmdLine += " --distrib %s" % pilotcfg.DISTRIB_DIR if 'EVENTS_DIR' in dir(pilotcfg): cmdLine += " --events %s" % pilotcfg.EVENTS_DIR @@ -424,7 +424,7 @@ def autobuild(cmd): cmdLine += " --branch %s" % currentBranch() - systemCommand(cmdLine) + builder.utils.run_python2(cmdLine) return True diff --git a/distrib/platform_release.py b/distrib/platform_release.py index 0111fa765e..318e92ae3d 100755 --- a/distrib/platform_release.py +++ b/distrib/platform_release.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python2.7 # This script builds the distribution packages platform-independently. # No parameters needed; config is auto-detected. @@ -160,7 +160,7 @@ def mac_package_snowberry(): f = file('VERSION', 'wt') f.write(DOOMSDAY_VERSION_FULL) f.close() - os.system('python buildapp.py py2app') + builder.utils.run_python2('buildapp.py py2app') # Share it. duptree('dist/Doomsday Engine.app', 'shared/') diff --git a/doomsday/api/api_player.h b/doomsday/api/api_player.h index 99eb739097..7b34a88fad 100644 --- a/doomsday/api/api_player.h +++ b/doomsday/api/api_player.h @@ -111,7 +111,7 @@ typedef struct { #define LOOKDIR2DEG(x) ((x) * 85.0f/LOOKDIRMAX) /// Player lookdir (view pitch) conversion to radians. @ingroup player -#define LOOKDIR2RAD(x) (LOOKDIR2DEG(x)/180*PI) +#define LOOKDIR2RAD(x) (LOOKDIR2DEG(x)/180*DD_PI) struct mobj_s; struct polyobj_s; diff --git a/doomsday/apidoc/.gitignore b/doomsday/apidoc/.gitignore index e3697969d4..66ff4985db 100644 --- a/doomsday/apidoc/.gitignore +++ b/doomsday/apidoc/.gitignore @@ -5,3 +5,6 @@ jdoom jheretic jhexen shell +gui +appfw + diff --git a/doomsday/build/build.pro b/doomsday/build/build.pro index 790e1c4a2c..8468b1b070 100644 --- a/doomsday/build/build.pro +++ b/doomsday/build/build.pro @@ -9,8 +9,10 @@ TEMPLATE = subdirs # Let's print the build configuration during this qmake invocation. CONFIG += deng_verbosebuildconfig +include(../macros.pri) + # Always update versions.pri. -system(cd "$$PWD/.." && python configure.py) +runPython2InDir($$PWD/.., configure.py) include(../config.pri) @@ -19,7 +21,7 @@ QMAKE_STRIP = true # Update the PK3 files. !deng_nopackres { - system(cd $$PWD/scripts/ && python packres.py --quiet \"$$OUT_PWD/..\") + runPython2InDir($$PWD/scripts/, packres.py --quiet \"$$OUT_PWD/..\") } # Install the launcher. diff --git a/doomsday/build/scripts/codex.py b/doomsday/build/scripts/codex.py index d20be2902c..af8ee76363 100755 --- a/doomsday/build/scripts/codex.py +++ b/doomsday/build/scripts/codex.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python2.7 # coding=utf-8 # # Repository Codex Generator by diff --git a/doomsday/build/scripts/makehelp.py b/doomsday/build/scripts/makehelp.py index ef0b486169..68e45743dc 100755 --- a/doomsday/build/scripts/makehelp.py +++ b/doomsday/build/scripts/makehelp.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python2.7 import os, sys import conhelp diff --git a/doomsday/build/scripts/packres.py b/doomsday/build/scripts/packres.py index cc9c7e25b2..05caa31c9d 100755 --- a/doomsday/build/scripts/packres.py +++ b/doomsday/build/scripts/packres.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python2.7 # This Python script will create a set of PK3 files that contain the files # that Doomsday needs at runtime. The PK3 files are organized using the # traditional data/ and defs/ structure. diff --git a/doomsday/build/scripts/wadcompiler.py b/doomsday/build/scripts/wadcompiler.py index b79d09314e..37e5b6631f 100755 --- a/doomsday/build/scripts/wadcompiler.py +++ b/doomsday/build/scripts/wadcompiler.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python2.7 import sys, os.path, struct diff --git a/doomsday/build/win32/qmake_msvc.py b/doomsday/build/win32/qmake_msvc.py index 93e17a4dbb..bc7370eb8c 100644 --- a/doomsday/build/win32/qmake_msvc.py +++ b/doomsday/build/win32/qmake_msvc.py @@ -1,6 +1,6 @@ -# qmake_msvc.py is a script that generates a full Visual Studio solution with -# a .vcxproj for each subproject. You must set up envconfig.bat before running -# the script. The solution is always placed in a folder called +# qmake_msvc.py is a Python 2 script that generates a full Visual Studio +# solution with a .vcxproj for each subproject. You must set up envconfig.bat +# before running the script. The solution is always placed in a folder called # "doomsday-msvc-build" at the repository root. # # qmake_msvc.py must be called whenever the .pro/.pri files change. The .sln diff --git a/doomsday/client/client.pro b/doomsday/client/client.pro index bdf9ac4946..1df0e68461 100644 --- a/doomsday/client/client.pro +++ b/doomsday/client/client.pro @@ -379,6 +379,7 @@ DENG_HEADERS += \ include/ui/dialogs/gamesdialog.h \ include/ui/dialogs/inputsettingsdialog.h \ include/ui/dialogs/logsettingsdialog.h \ + include/ui/dialogs/manualconnectiondialog.h \ include/ui/dialogs/networksettingsdialog.h \ include/ui/dialogs/renderersettingsdialog.h \ include/ui/dialogs/videosettingsdialog.h \ @@ -392,6 +393,7 @@ DENG_HEADERS += \ include/ui/widgets/cvarlineeditwidget.h \ include/ui/widgets/cvarsliderwidget.h \ include/ui/widgets/cvartogglewidget.h \ + include/ui/widgets/gamefilterwidget.h \ include/ui/widgets/gameselectionwidget.h \ include/ui/widgets/gamesessionwidget.h \ include/ui/widgets/gameuiwidget.h \ @@ -704,6 +706,7 @@ SOURCES += \ src/ui/dialogs/gamesdialog.cpp \ src/ui/dialogs/inputsettingsdialog.cpp \ src/ui/dialogs/logsettingsdialog.cpp \ + src/ui/dialogs/manualconnectiondialog.cpp \ src/ui/dialogs/networksettingsdialog.cpp \ src/ui/dialogs/videosettingsdialog.cpp \ src/ui/dialogs/vrsettingsdialog.cpp \ @@ -728,6 +731,7 @@ SOURCES += \ src/ui/widgets/cvarlineeditwidget.cpp \ src/ui/widgets/cvarsliderwidget.cpp \ src/ui/widgets/cvartogglewidget.cpp \ + src/ui/widgets/gamefilterwidget.cpp \ src/ui/widgets/gameselectionwidget.cpp \ src/ui/widgets/gamesessionwidget.cpp \ src/ui/widgets/gamewidget.cpp \ diff --git a/doomsday/client/data/defaultstyle.pack/colors.dei b/doomsday/client/data/defaultstyle.pack/colors.dei index cb0ec71fda..f37983e06f 100644 --- a/doomsday/client/data/defaultstyle.pack/colors.dei +++ b/doomsday/client/data/defaultstyle.pack/colors.dei @@ -42,6 +42,10 @@ group choice { color popup { rgb $= gui.colorAlpha(background.rgb, 1.0) } } +group tab { + color selected { rgb $= accent.rgb } +} + group progress { group light { color wheel { rgb <1.0, 1.0, 1.0, 0.25> } diff --git a/doomsday/client/data/defaultstyle.pack/fonts.dei b/doomsday/client/data/defaultstyle.pack/fonts.dei index ed850add7f..79c9cd2647 100644 --- a/doomsday/client/data/defaultstyle.pack/fonts.dei +++ b/doomsday/client/data/defaultstyle.pack/fonts.dei @@ -97,6 +97,10 @@ font heading inherits title { size $: gui.scale(default.size, 1.2) } +font small inherits default { + size $: gui.scale(__this__.size, 0.75) +} + group editor { font plaintext inherits default {} font hint inherits default { @@ -109,8 +113,7 @@ group separator { font empty inherits default { size $: gui.scale(__this__.size, 0.5) } - font label inherits default { - size $: gui.scale(__this__.size, 0.75) + font label inherits small { weight: bold } } @@ -121,10 +124,15 @@ group choice { } } -group slider { - font label inherits default { - size $: gui.scale(__this__.size, 0.75) +group tab { + font label inherits small { + weight: bold } + font selected inherits tab.label {} +} + +group slider { + font label inherits small {} font value inherits slider.label { weight: bold } diff --git a/doomsday/client/data/defaultstyle.pack/graphics/fold.png b/doomsday/client/data/defaultstyle.pack/graphics/fold.png new file mode 100644 index 0000000000..361ca75623 Binary files /dev/null and b/doomsday/client/data/defaultstyle.pack/graphics/fold.png differ diff --git a/doomsday/client/data/defaultstyle.pack/images.dei b/doomsday/client/data/defaultstyle.pack/images.dei index bb8db98777..95ea57a9f0 100644 --- a/doomsday/client/data/defaultstyle.pack/images.dei +++ b/doomsday/client/data/defaultstyle.pack/images.dei @@ -20,6 +20,7 @@ group logo { } image alert { path = "graphics/alert.png" } +image fold { path = "graphics/fold.png" } image gauge { path = "graphics/gauge.png" } image gear { path = "graphics/gear.png" } image display { path = "graphics/display.png" } diff --git a/doomsday/client/include/ui/dialogs/manualconnectiondialog.h b/doomsday/client/include/ui/dialogs/manualconnectiondialog.h new file mode 100644 index 0000000000..f89fa63f2e --- /dev/null +++ b/doomsday/client/include/ui/dialogs/manualconnectiondialog.h @@ -0,0 +1,52 @@ +/** @file manualconnectiondialog.h Dialog for connecting to a server. + * + * @authors Copyright (c) 2014 Jaakko Keränen + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed 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 DENG_CLIENT_MANUALCONNECTIONDIALOG_H +#define DENG_CLIENT_MANUALCONNECTIONDIALOG_H + +#include +#include + +/** + * Dialog for connecting to a multiplayer server manually using an IP address or domain + * name. The TCP port number can also be optionally provided. + * + * The dialog stores the previously used address persistently. + */ +class ManualConnectionDialog : public de::InputDialog, public de::IPersistent +{ + Q_OBJECT + +public: + ManualConnectionDialog(de::String const &name = "manualconnection"); + + // Implements IPersistent. + void operator >> (de::PersistentState &toState) const; + void operator << (de::PersistentState const &fromState); + +public slots: + void validate(); + +protected: + void finish(int result); + +private: + DENG2_PRIVATE(d) +}; + +#endif // DENG_CLIENT_MANUALCONNECTIONDIALOG_H diff --git a/doomsday/client/include/ui/widgets/consolewidget.h b/doomsday/client/include/ui/widgets/consolewidget.h index bf9c6c9cf3..1903d9cbe2 100644 --- a/doomsday/client/include/ui/widgets/consolewidget.h +++ b/doomsday/client/include/ui/widgets/consolewidget.h @@ -23,6 +23,7 @@ #include #include #include +#include #include "consolecommandwidget.h" @@ -34,7 +35,7 @@ * * @ingroup gui */ -class ConsoleWidget : public de::GuiWidget +class ConsoleWidget : public de::GuiWidget, public de::IPersistent { Q_OBJECT @@ -63,6 +64,10 @@ class ConsoleWidget : public de::GuiWidget void update(); bool handleEvent(de::Event const &event); + // Implements IPersistent. + void operator >> (de::PersistentState &toState) const; + void operator << (de::PersistentState const &fromState); + signals: void commandModeChanged(); void commandLineGotFocus(); diff --git a/doomsday/client/include/ui/widgets/gamefilterwidget.h b/doomsday/client/include/ui/widgets/gamefilterwidget.h new file mode 100644 index 0000000000..fba3bf7cdf --- /dev/null +++ b/doomsday/client/include/ui/widgets/gamefilterwidget.h @@ -0,0 +1,75 @@ +/** @file gamefilterwidget.h + * + * @authors Copyright (c) 2014 Jaakko Keränen + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed 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 DENG_CLIENT_GAMEFILTERWIDGET_H +#define DENG_CLIENT_GAMEFILTERWIDGET_H + +#include +#include + +/** + * Filtering and sorting parameters for a game selection widget. Allows picking the type + * of games to show (singleplayer or multiplayer) and how the games are sorted. + * + * The widget defines its own height, but width must be set manually. + * + * The filter and sort setting is saved persistently. + */ +class GameFilterWidget : public de::GuiWidget, public de::IPersistent +{ + Q_OBJECT + +public: + enum FilterFlag + { + Singleplayer = 0x1, + Multiplayer = 0x2, + AllGames = Singleplayer | Multiplayer + }; + Q_DECLARE_FLAGS(Filter, FilterFlag) + + enum SortOrder + { + SortByTitle, + SortByIdentityKey + }; + +public: + GameFilterWidget(de::String const &name = "gamefilter"); + + void useInvertedStyle(); + + Filter filter() const; + + SortOrder sortOrder() const; + + // Implements IPersistent. + void operator >> (de::PersistentState &toState) const; + void operator << (de::PersistentState const &fromState); + +signals: + void filterChanged(); + void sortOrderChanged(); + +private: + DENG2_PRIVATE(d) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(GameFilterWidget::Filter) + +#endif // DENG_CLIENT_GAMEFILTERWIDGET_H diff --git a/doomsday/client/include/ui/widgets/gameselectionwidget.h b/doomsday/client/include/ui/widgets/gameselectionwidget.h index 6b9662b703..cd4a22259e 100644 --- a/doomsday/client/include/ui/widgets/gameselectionwidget.h +++ b/doomsday/client/include/ui/widgets/gameselectionwidget.h @@ -22,6 +22,8 @@ #include #include +#include "gamefilterwidget.h" + /** * Menu for selecting */ @@ -37,6 +39,12 @@ class GameSelectionWidget : public de::ScrollAreaWidget de::ButtonWidget::HoverColorMode mode = de::ButtonWidget::ModulateColor); void setTitleFont(de::DotPath const &fontId); + /** + * Returns the filter header widget. It can be placed manually by the user + * of the widget. + */ + GameFilterWidget &filter(); + // Events. void update(); @@ -45,6 +53,7 @@ class GameSelectionWidget : public de::ScrollAreaWidget protected slots: void updateSubsetLayout(); + void updateSort(); private: DENG2_PRIVATE(d) diff --git a/doomsday/client/include/ui/widgets/gamesessionwidget.h b/doomsday/client/include/ui/widgets/gamesessionwidget.h index 21a0cc201f..8c009a9e66 100644 --- a/doomsday/client/include/ui/widgets/gamesessionwidget.h +++ b/doomsday/client/include/ui/widgets/gamesessionwidget.h @@ -36,6 +36,9 @@ class GameSessionWidget : public de::GuiWidget de::ButtonWidget &infoButton(); de::DocumentWidget &document(); + /** + * Called immediately before the Info button is pressed. + */ virtual void updateInfoContent(); private: diff --git a/doomsday/client/include/ui/widgets/taskbarwidget.h b/doomsday/client/include/ui/widgets/taskbarwidget.h index 481d2f6718..b41faec0f6 100644 --- a/doomsday/client/include/ui/widgets/taskbarwidget.h +++ b/doomsday/client/include/ui/widgets/taskbarwidget.h @@ -64,6 +64,7 @@ public slots: void showAbout(); void showUpdaterSettings(); void showGames(); + void connectToServerManually(); protected slots: void updateCommandLineLayout(); diff --git a/doomsday/client/src/ui/clientwindow.cpp b/doomsday/client/src/ui/clientwindow.cpp index 5a64091915..05d4431699 100644 --- a/doomsday/client/src/ui/clientwindow.cpp +++ b/doomsday/client/src/ui/clientwindow.cpp @@ -190,16 +190,20 @@ DENG2_PIMPL(ClientWindow) gameSelMenu = new GameSelectionWidget; gameSelMenu->rule() .setInput(Rule::AnchorX, root.viewLeft() + root.viewWidth() / 2) - //.setInput(Rule::AnchorY, root.viewTop() + root.viewHeight() / 2) .setInput(Rule::Width, OperatorRule::minimum(root.viewWidth(), style.rules().rule("gameselection.max.width"))) .setAnchorPoint(Vector2f(.5f, .5f)); + gameSelMenu->filter().useInvertedStyle(); + gameSelMenu->filter().rule() + .setInput(Rule::Left, gameSelMenu->rule().left() + gameSelMenu->margins().left()) + .setInput(Rule::Width, gameSelMenu->rule().width() - gameSelMenu->margins().width()) + .setInput(Rule::Top, root.viewTop() + style.rules().rule("gap")); container().add(gameSelMenu); // Common notification area. notifications = new NotificationWidget; notifications->rule() - .setInput(Rule::Top, root.viewTop() + style.rules().rule("gap") - notifications->shift()) + .setInput(Rule::Top, root.viewTop() + style.rules().rule("gap") - notifications->shift()) .setInput(Rule::Right, game->rule().right() - style.rules().rule("gap")); container().add(notifications); diff --git a/doomsday/client/src/ui/dialogs/gamesdialog.cpp b/doomsday/client/src/ui/dialogs/gamesdialog.cpp index 3c9e3a827a..a9b59c5ec0 100644 --- a/doomsday/client/src/ui/dialogs/gamesdialog.cpp +++ b/doomsday/client/src/ui/dialogs/gamesdialog.cpp @@ -46,17 +46,12 @@ GamesDialog::GamesDialog(String const &name) { connect(d->gameSel, SIGNAL(gameSessionSelected()), this, SLOT(accept())); - //heading().setText(tr("Games")); - - //LabelWidget *lab = LabelWidget::newWithText(tr("Games from Master Server and local network:"), &area()); - - //d->gameSel->rule().setInput(Rule::Height, d->gameSel->contentRule().height()/*style().rules().rule("gameselection.max.height")*/); - GridLayout layout(area().contentRule().left(), area().contentRule().top()); layout.setGridSize(1, 0); - //layout.setColumnAlignment(0, ui::AlignRight); - layout << *d->gameSel; + d->gameSel->filter().rule().setInput(Rule::Width, d->gameSel->rule().width()); + + layout << d->gameSel->filter() << *d->gameSel; area().setContentSize(layout.width(), layout.height()); diff --git a/doomsday/client/src/ui/dialogs/manualconnectiondialog.cpp b/doomsday/client/src/ui/dialogs/manualconnectiondialog.cpp new file mode 100644 index 0000000000..155b05e21b --- /dev/null +++ b/doomsday/client/src/ui/dialogs/manualconnectiondialog.cpp @@ -0,0 +1,79 @@ +/** @file manualconnectiondialog.cpp Dialog for connecting to a server. + * + * @authors Copyright (c) 2014 Jaakko Keränen + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed 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 "ui/dialogs/manualconnectiondialog.h" +#include + +using namespace de; + +DENG2_PIMPL_NOREF(ManualConnectionDialog) +{ + String usedAddress; +}; + +ManualConnectionDialog::ManualConnectionDialog(String const &name) + : InputDialog(name), d(new Instance) +{ + title().setText(tr("Connect to Server")); + message().setText(tr("Enter the address of the multiplayer server you want to connect to. " + "The address can be a domain name or an IP address. " + "Optionally, you may include a TCP port number, for example " + _E(b) "10.0.1.1:13209" _E(.) ".")); + + defaultActionItem()->setLabel(tr("Connect")); + buttonWidget(tr("Connect")).disable(); + + connect(&editor(), SIGNAL(editorContentChanged()), this, SLOT(validate())); +} + +void ManualConnectionDialog::operator >> (PersistentState &toState) const +{ + toState.names().set(name() + ".address", d->usedAddress); +} + +void ManualConnectionDialog::operator << (PersistentState const &fromState) +{ + d->usedAddress = fromState.names()[name() + ".address"]; + editor().setText(d->usedAddress); + validate(); +} + +void ManualConnectionDialog::validate() +{ + bool valid = true; + + if(editor().text().isEmpty() || editor().text().contains(';') || + editor().text().endsWith(":") || editor().text().startsWith(":")) + { + valid = false; + } + + buttonWidget(tr("Connect")).enable(valid); +} + +void ManualConnectionDialog::finish(int result) +{ + if(result) + { + // The dialog was accepted. + d->usedAddress = editor().text(); + } + + InputDialog::finish(result); +} + diff --git a/doomsday/client/src/ui/widgets/consolewidget.cpp b/doomsday/client/src/ui/widgets/consolewidget.cpp index ee0488b6e1..b3227acb57 100644 --- a/doomsday/client/src/ui/widgets/consolewidget.cpp +++ b/doomsday/client/src/ui/widgets/consolewidget.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include using namespace de; @@ -422,6 +423,22 @@ bool ConsoleWidget::handleEvent(Event const &event) return false; } +void ConsoleWidget::operator >> (PersistentState &toState) const +{ + toState.names().set("console.width", d->width->value()); +} + +void ConsoleWidget::operator << (PersistentState const &fromState) +{ + d->width->set(fromState.names()["console.width"]); + + if(!d->opened) + { + // Make sure it stays out of the view. + d->horizShift->set(-rule().width().valuei() - 1); + } +} + void ConsoleWidget::openLog() { if(d->opened) return; diff --git a/doomsday/client/src/ui/widgets/gamefilterwidget.cpp b/doomsday/client/src/ui/widgets/gamefilterwidget.cpp new file mode 100644 index 0000000000..c22a6c0010 --- /dev/null +++ b/doomsday/client/src/ui/widgets/gamefilterwidget.cpp @@ -0,0 +1,111 @@ +/** @file gamefilterwidget.cpp + * + * @authors Copyright (c) 2014 Jaakko Keränen + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed 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 "ui/widgets/gamefilterwidget.h" + +#include +#include +#include +#include +#include +#include + +using namespace de; + +DENG2_PIMPL(GameFilterWidget) +{ + TabWidget *tabs; + LabelWidget *sortLabel; + ChoiceWidget *sortBy; + DialogContentStylist stylist; + + Instance(Public *i) : Base(i) + { + stylist.setContainer(self); + + // Create widgets. + self.add(tabs = new TabWidget); + sortLabel = LabelWidget::newWithText(tr("Sort By:"), &self); + self.add(sortBy = new ChoiceWidget); + + tabs->items() + << new TabItem(tr("Singleplayer"), Singleplayer) + << new TabItem(tr("Multiplayer"), Multiplayer) + << new TabItem(tr("All Games"), AllGames); + + sortLabel->setFont("small"); + sortBy->setFont("tab.label"); + sortBy->setOpeningDirection(ui::Down); + sortBy->items() + << new ChoiceItem(tr("Title"), SortByTitle) + << new ChoiceItem(tr("Identity key"), SortByIdentityKey); + + SequentialLayout layout(self.rule().right(), self.rule().top(), ui::Left); + layout << *sortBy << *sortLabel; + + tabs->rule() + .setInput(Rule::Width, self.rule().width()) + .setInput(Rule::Left, self.rule().left()) + .setInput(Rule::Top, self.rule().top()); + } + + String persistId(String const &name) const + { + return self.name() + "." + name; + } +}; + +GameFilterWidget::GameFilterWidget(String const &name) + : GuiWidget(name), d(new Instance(this)) +{ + connect(d->tabs, SIGNAL(currentTabChanged()), this, SIGNAL(filterChanged())); + connect(d->sortBy, SIGNAL(selectionChanged(uint)), this, SIGNAL(sortOrderChanged())); + + rule().setInput(Rule::Height, d->tabs->rule().height()); +} + +void GameFilterWidget::useInvertedStyle() +{ + d->sortLabel->setTextColor("inverted.text"); +} + +GameFilterWidget::Filter GameFilterWidget::filter() const +{ + return Filter(d->tabs->currentItem().data().toUInt()); +} + +GameFilterWidget::SortOrder GameFilterWidget::sortOrder() const +{ + return SortOrder(d->sortBy->selectedItem().data().toInt()); +} + +void GameFilterWidget::operator >> (PersistentState &toState) const +{ + Record &st = toState.names(); + + st.set(d->persistId("filter"), dint(filter())); + st.set(d->persistId("order"), dint(sortOrder())); +} + +void GameFilterWidget::operator << (PersistentState const &fromState) +{ + Record const &st = fromState.names(); + + d->tabs->setCurrent (d->tabs->items() .findData(int(st[d->persistId("filter")]))); + d->sortBy->setSelected(d->sortBy->items().findData(int(st[d->persistId("order" )]))); +} diff --git a/doomsday/client/src/ui/widgets/gameselectionwidget.cpp b/doomsday/client/src/ui/widgets/gameselectionwidget.cpp index b49092cff8..02a90b1e22 100644 --- a/doomsday/client/src/ui/widgets/gameselectionwidget.cpp +++ b/doomsday/client/src/ui/widgets/gameselectionwidget.cpp @@ -19,6 +19,7 @@ #include "ui/widgets/gameselectionwidget.h" #include "ui/widgets/gamesessionwidget.h" #include "ui/widgets/mpselectionwidget.h" +#include "ui/widgets/gamefilterwidget.h" #include "CommandAction" #include "clientapp.h" #include "games.h" @@ -42,11 +43,12 @@ DENG_GUI_PIMPL(GameSelectionWidget) , DENG2_OBSERVES(App, StartupComplete) , DENG2_OBSERVES(App, GameChange) , public ChildWidgetOrganizer::IWidgetFactory -{ +{ /// ActionItem with a Game member, for loading a particular game. struct GameItem : public ui::ActionItem { - GameItem(Game const &gameRef, de::String const &label, RefArg action) - : ui::ActionItem(label, action), game(gameRef) { + GameItem(Game const &gameRef, de::String const &label, RefArg action, + GameSelectionWidget &owner) + : ui::ActionItem(label, action), game(gameRef), widget(owner) { setData(&gameRef); } String sortKey() const { @@ -54,6 +56,7 @@ DENG_GUI_PIMPL(GameSelectionWidget) return game.identityKey(); } Game const &game; + GameSelectionWidget &widget; }; struct GameWidget : public GameSessionWidget @@ -70,7 +73,9 @@ DENG_GUI_PIMPL(GameSelectionWidget) /** * Foldable group of games. */ - struct Subset : public FoldPanelWidget + struct SubsetWidget + : public FoldPanelWidget + , DENG2_OBSERVES(ui::Data, Addition) { enum Type { NormalGames, @@ -80,9 +85,11 @@ DENG_GUI_PIMPL(GameSelectionWidget) String titleText; Type type; MenuWidget *menu; + LabelWidget *noGames; + int numCols; - Subset(Type selType, String const &headingText, GameSelectionWidget::Instance *owner) - : titleText(headingText), type(selType) + SubsetWidget(Type selType, String const &headingText, GameSelectionWidget::Instance *owner) + : titleText(headingText), type(selType), numCols(3) { owner->self.add(makeTitle(headingText)); title().setFont("title"); @@ -105,6 +112,8 @@ DENG_GUI_PIMPL(GameSelectionWidget) break; } + menu->items().audienceForAddition += this; + setContent(menu); menu->enableScrolling(false); menu->margins().set(""); @@ -114,10 +123,33 @@ DENG_GUI_PIMPL(GameSelectionWidget) owner->self.margins().width()); setColumns(3); + + // This will be shown if there are no games in the subset. + noGames = LabelWidget::newWithText(_E(b) + tr("No games"), menu); + noGames->margins().setTop(style().rules().rule("gap") * 2); + noGames->margins().setBottom(noGames->margins().top()); + noGames->setFont("heading"); + noGames->setTextColor("inverted.text"); + noGames->setOpacity(.4f); + noGames->hide(); + } + + void dataItemAdded(ui::Data::Pos, ui::Item const &) + { + // Time to get rid of the notice. + noGames->hide(); } void setColumns(int cols) { + numCols = cols; + + // However, if the subset is empty, just use a single column for noGames. + if(items().isEmpty()) + { + cols = 1; + } + if(menu->layout().maxGridSize().x != cols) { menu->setGridSize(cols, ui::Filled, 0, ui::Expand); @@ -156,30 +188,40 @@ DENG_GUI_PIMPL(GameSelectionWidget) { title().setText(textForTitle(isOpen())); } + + void setTitleColor(DotPath const &colorId, + DotPath const &hoverColorId, + ButtonWidget::HoverColorMode mode) + { + title().setTextColor(colorId); + title().setHoverTextColor(hoverColorId, mode); + noGames->setTextColor(colorId); + } }; FIFO pendingGames; SequentialLayout superLayout; - Subset *available; - Subset *incomplete; - Subset *multi; + GameFilterWidget *filter; + SubsetWidget *available; + SubsetWidget *incomplete; + SubsetWidget *multi; Instance(Public *i) : Base(i) , superLayout(i->contentRule().left(), i->contentRule().top(), ui::Down) { // Menu of available games. - self.add(available = new Subset(Subset::NormalGames, + self.add(available = new SubsetWidget(SubsetWidget::NormalGames, App_GameLoaded()? tr("Switch Game") : tr("Available Games"), this)); // Menu of incomplete games. - self.add(incomplete = new Subset(Subset::NormalGames, - tr("Games with Missing Resources"), this)); + self.add(incomplete = new SubsetWidget(SubsetWidget::NormalGames, tr("Games with Missing Resources"), this)); // Menu of multiplayer games. - self.add(multi = new Subset(Subset::MultiplayerGames, - tr("Multiplayer Games"), this)); + self.add(multi = new SubsetWidget(SubsetWidget::MultiplayerGames, tr("Multiplayer Games"), this)); + + self.add(filter = new GameFilterWidget); superLayout.setOverrideWidth(self.rule().width() - self.margins().width()); @@ -197,6 +239,23 @@ DENG_GUI_PIMPL(GameSelectionWidget) App::app().audienceForGameChange -= this; } + void updateSubsetVisibility() + { + GameFilterWidget::Filter flt = filter->filter(); + + bool const sp = flt.testFlag(GameFilterWidget::Singleplayer); + bool const mp = flt.testFlag(GameFilterWidget::Multiplayer); + + available->show(sp); + available->title().show(sp); + + incomplete->show(sp); + incomplete->title().show(sp); + + multi->show(mp); + multi->title().show(mp); + } + /** * Subsets are visible only when they have games in them. The title and content * of a subset are hidden when empty. @@ -205,7 +264,7 @@ DENG_GUI_PIMPL(GameSelectionWidget) { superLayout.clear(); - QList order; + QList order; if(!App_GameLoaded()) { order << available << multi << incomplete; @@ -215,21 +274,37 @@ DENG_GUI_PIMPL(GameSelectionWidget) order << multi << available << incomplete; } - foreach(Subset *s, order) + updateSubsetVisibility(); + + // Filter out the requested subsets. + GameFilterWidget::Filter flt = filter->filter(); + if(!flt.testFlag(GameFilterWidget::Singleplayer)) { - // The first group should not have extra space above it. - s->title().margins().setTop(""); - if(s != order.first()) superLayout << style().rules().rule("gap"); + order.removeOne(available); + order.removeOne(incomplete); + } + if(!flt.testFlag(GameFilterWidget::Multiplayer)) + { + order.removeOne(multi); + } + + foreach(SubsetWidget *s, order) + { + s->updateTitleText(); + superLayout << s->title() << *s; - if(!s->items().isEmpty()) + // Show a notice when there are no games in the group. + if(s->items().isEmpty()) { - s->updateTitleText(); - s->title().show(); - superLayout << s->title() << *s; + // Go to one-column layout for the "no games" indicator. + s->menu->setGridSize(1, ui::Filled, 1, ui::Expand); + s->noGames->show(); } else { - s->title().hide(); + // Restore the correct number of columns. + s->setColumns(s->numCols); + s->noGames->hide(); } } @@ -297,13 +372,11 @@ DENG_GUI_PIMPL(GameSelectionWidget) { String const idKey = game.identityKey(); - String label = String(_E(b) "%1" _E(.) /*_E(s)_E(C) " %2\n" _E(.)_E(.)*/ "\n" - _E(l)_E(D) "%2") + String label = String(_E(b) "%1" _E(.) "\n" _E(l)_E(D) "%2") .arg(game.title()) - //.arg(game.author()) .arg(idKey); - GameItem *item = new GameItem(game, label, new LoadGameAction(String("load ") + idKey, self)); + GameItem *item = new GameItem(game, label, new LoadGameAction(String("load ") + idKey, self), self); /// @todo The name of the plugin should be accessible via the plugin loader. String plugName; @@ -349,10 +422,27 @@ DENG_GUI_PIMPL(GameSelectionWidget) updateGameAvailability(); } + static bool gameSorter(ui::Item const &a, ui::Item const &b) + { + GameItem const &x = a.as(); + GameItem const &y = b.as(); + + switch(x.widget.d->filter->sortOrder()) + { + case GameFilterWidget::SortByTitle: + return x.label().compareWithoutCase(y.label()) < 0; + + case GameFilterWidget::SortByIdentityKey: + return x.sortKey().compareWithoutCase(y.sortKey()) < 0; + } + + return false; + } + void sortGames() { - available->items().sort(); - incomplete->items().sort(); + available->items().sort(gameSorter); + incomplete->items().sort(gameSorter); } void updateGameAvailability() @@ -407,42 +497,52 @@ DENG_GUI_PIMPL(GameSelectionWidget) GameSelectionWidget::GameSelectionWidget(String const &name) : ScrollAreaWidget(name), d(new Instance(this)) { + // By default attach the filter above the widget. + d->filter->rule() + .setInput(Rule::Width, rule().width()) + .setInput(Rule::Bottom, rule().top()) + .setInput(Rule::Left, rule().left()); + d->multi->open(); if(!App_GameLoaded()) { d->available->open(); - d->incomplete->open(); + //d->incomplete->open(); } // We want the full menu to be visible even when it doesn't fit the // designated area. unsetBehavior(ChildVisibilityClipping); + unsetBehavior(ChildHitClipping); // Maybe there are games loaded already. d->addExistingGames(); + + connect(d->filter, SIGNAL(sortOrderChanged()), this, SLOT(updateSort())); + connect(d->filter, SIGNAL(filterChanged()), this, SLOT(updateSubsetLayout())); } void GameSelectionWidget::setTitleColor(DotPath const &colorId, DotPath const &hoverColorId, ButtonWidget::HoverColorMode mode) { - d->available->title().setTextColor(colorId); - d->available->title().setHoverTextColor(hoverColorId, mode); - - d->multi->title().setTextColor(colorId); - d->multi->title().setHoverTextColor(hoverColorId, mode); - - d->incomplete->title().setTextColor(colorId); - d->incomplete->title().setHoverTextColor(hoverColorId, mode); + d->available ->setTitleColor(colorId, hoverColorId, mode); + d->multi ->setTitleColor(colorId, hoverColorId, mode); + d->incomplete->setTitleColor(colorId, hoverColorId, mode); } void GameSelectionWidget::setTitleFont(DotPath const &fontId) { - d->available->title().setFont(fontId); - d->multi->title().setFont(fontId); + d->available ->title().setFont(fontId); + d->multi ->title().setFont(fontId); d->incomplete->title().setFont(fontId); } +GameFilterWidget &GameSelectionWidget::filter() +{ + return *d->filter; +} + void GameSelectionWidget::update() { d->addPendingGames(); @@ -461,3 +561,8 @@ void GameSelectionWidget::updateSubsetLayout() { d->updateSubsetLayout(); } + +void GameSelectionWidget::updateSort() +{ + d->sortGames(); +} diff --git a/doomsday/client/src/ui/widgets/taskbarwidget.cpp b/doomsday/client/src/ui/widgets/taskbarwidget.cpp index 3a412778dd..ea0307018a 100644 --- a/doomsday/client/src/ui/widgets/taskbarwidget.cpp +++ b/doomsday/client/src/ui/widgets/taskbarwidget.cpp @@ -25,6 +25,7 @@ #include "ui/dialogs/inputsettingsdialog.h" #include "ui/dialogs/networksettingsdialog.h" #include "ui/dialogs/renderersettingsdialog.h" +#include "ui/dialogs/manualconnectiondialog.h" #include "ui/dialogs/vrsettingsdialog.h" #include "ui/dialogs/gamesdialog.h" #include "updater/updatersettingsdialog.h" @@ -62,6 +63,8 @@ enum MenuItemPositions POS_GAMES = 0, POS_UNLOAD = 1, POS_GAMES_SEPARATOR = 2, + POS_CONNECT = 3, + POS_CONNECT_SEPARATOR = 4, // Config menu: POS_RENDERER_SETTINGS = 0, @@ -259,9 +262,11 @@ DENG_GUI_PIMPL(TaskBarWidget) { updateStatus(); - itemWidget(mainMenu, POS_GAMES) .show(!newGame.isNull()); - itemWidget(mainMenu, POS_UNLOAD) .show(!newGame.isNull()); - itemWidget(mainMenu, POS_GAMES_SEPARATOR).show(!newGame.isNull()); + itemWidget(mainMenu, POS_GAMES) .show(!newGame.isNull()); + itemWidget(mainMenu, POS_UNLOAD) .show(!newGame.isNull()); + itemWidget(mainMenu, POS_GAMES_SEPARATOR) .show(!newGame.isNull()); + itemWidget(mainMenu, POS_CONNECT) .show(!newGame.isNull()); + itemWidget(mainMenu, POS_CONNECT_SEPARATOR).show(!newGame.isNull()); itemWidget(configMenu, POS_RENDERER_SETTINGS).show(!newGame.isNull()); itemWidget(configMenu, POS_VR_SETTINGS) .show(!newGame.isNull()); @@ -410,6 +415,8 @@ TaskBarWidget::TaskBarWidget() : GuiWidget("taskbar"), d(new Instance(this)) << new ui::ActionItem(tr("Games..."), new SignalAction(this, SLOT(showGames()))) << unloadMenu // hidden with null-game << new ui::Item(ui::Item::Separator) + << new ui::ActionItem(tr("Connect to Server..."), new SignalAction(this, SLOT(connectToServerManually()))) + << new ui::Item(ui::Item::Separator) << new ui::ActionItem(tr("Check for Updates..."), new CommandAction("updateandnotify")) << new ui::ActionItem(tr("About Doomsday"), new SignalAction(this, SLOT(showAbout()))) << new ui::Item(ui::Item::Separator) @@ -418,6 +425,8 @@ TaskBarWidget::TaskBarWidget() : GuiWidget("taskbar"), d(new Instance(this)) d->itemWidget(d->mainMenu, POS_GAMES).hide(); d->itemWidget(d->mainMenu, POS_UNLOAD).hide(); d->itemWidget(d->mainMenu, POS_GAMES_SEPARATOR).hide(); + d->itemWidget(d->mainMenu, POS_CONNECT).hide(); + d->itemWidget(d->mainMenu, POS_CONNECT_SEPARATOR).hide(); d->itemWidget(d->configMenu, POS_RENDERER_SETTINGS).hide(); d->itemWidget(d->configMenu, POS_VR_SETTINGS).hide(); @@ -707,6 +716,17 @@ void TaskBarWidget::showGames() games->exec(root()); } +void TaskBarWidget::connectToServerManually() +{ + ManualConnectionDialog *dlg = new ManualConnectionDialog; + dlg->setDeleteAfterDismissed(true); + if(dlg->exec(root())) + { + // Connect to the provided address. + Con_Executef(CMDS_DDAY, false, "connect %s", dlg->editor().text().toLatin1().constData()); + } +} + void TaskBarWidget::updateCommandLineLayout() { SequentialLayout layout(rule().right(), rule().top(), ui::Left); diff --git a/doomsday/config.pri b/doomsday/config.pri index e5491a15cb..bc8ea7bc88 100644 --- a/doomsday/config.pri +++ b/doomsday/config.pri @@ -13,6 +13,7 @@ # CONFIG options for Doomsday: # - deng_aptstable Include the stable apt repository .list # - deng_aptunstable Include the unstable apt repository .list +# - deng_ccache (Unix|Mac) Use ccache when compiling # - deng_fluidsynth Build the FluidSynth sound driver # - deng_fmod Build the FMOD Ex sound driver # - deng_nativesdk (Mac) Use current OS's SDK for non-distrib use @@ -26,6 +27,9 @@ # - deng_notools Do not build and deploy the tools # - deng_openal Build the OpenAL sound driver # - deng_qtautoselect (Mac) Select OS X SDK based on Qt version +# - deng_qtgui Use the QtGui module in dep_deng2.pri +# - deng_qtopengl Use the QtOpenGL module in dep_deng2.pri +# - deng_qtwidgets Use the QtWidgets module in dep_deng2.pri # - deng_nopackres Do not package the Doomsday resources # - deng_rangecheck Parameter range checking/value assertions # - deng_snowberry (Unix) Include Snowberry in installation @@ -47,67 +51,16 @@ DENG_MAC_INCLUDE_DIR = $$DENG_INCLUDE_DIR/macx DENG_WIN_INCLUDE_DIR = $$DENG_INCLUDE_DIR/windows DENG_MODULES_DIR = $$PWD/libdeng2/modules -# Versions ------------------------------------------------------------------- - -# Parse versions from the header files. -!exists(versions.pri): system(python configure.py) - -include(versions.pri) - # Macros --------------------------------------------------------------------- -defineTest(echo) { - deng_verbosebuildconfig { - !win32 { - message($$1) - } else { - # We don't want to get the printed messages after everything else, - # so print to stdout. - system(echo $$1) - } - } -} +include(macros.pri) -defineTest(useLibDir) { - btype = "" - win32 { - deng_debug: btype = "/Debug" - else: btype = "/Release" - } - exists($${1}$${btype}) { - LIBS += -L$${1}$${btype} - export(LIBS) - return(true) - } - return(false) -} +# Versions ------------------------------------------------------------------- -defineTest(doPostLink) { - isEmpty(QMAKE_POST_LINK) { - QMAKE_POST_LINK = $$1 - } else { - QMAKE_POST_LINK = $$QMAKE_POST_LINK && $$1 - } - export(QMAKE_POST_LINK) -} +# Parse versions from the header files. +!exists(versions.pri): runPython2(configure.py) -macx { - defineTest(removeQtLibPrefix) { - doPostLink("install_name_tool -change $$[QT_INSTALL_LIBS]/$$2 $$2 $$1") - } - defineTest(fixInstallName) { - # 1: binary file - # 2: library name - # 3: path to Frameworks/ - removeQtLibPrefix($$1, $$2) - doPostLink("install_name_tool -change $$2 @executable_path/$$3/Frameworks/$$2 $$1") - } - defineTest(fixPluginInstallId) { - # 1: target name - # 2: version - doPostLink("install_name_tool -id @executable_path/../DengPlugins/$${1}.bundle/Versions/$$2/$$1 $${1}.bundle/Versions/$$2/$$1") - } -} +include(versions.pri) # Build Options -------------------------------------------------------------- diff --git a/doomsday/config_unix_any.pri b/doomsday/config_unix_any.pri index ee45361ca1..4b8c87b1b0 100644 --- a/doomsday/config_unix_any.pri +++ b/doomsday/config_unix_any.pri @@ -36,11 +36,21 @@ deng_qt5 { # Unix System Tools ---------------------------------------------------------- -# Python to be used in generated scripts. +# Python 2 to be used in generated scripts. isEmpty(SCRIPT_PYTHON) { - exists(/usr/bin/python): SCRIPT_PYTHON = /usr/bin/python + exists(/usr/bin/python2.7): SCRIPT_PYTHON = /usr/bin/python2.7 + exists(/usr/bin/python2): SCRIPT_PYTHON = /usr/bin/python2 + exists(/usr/bin/python): SCRIPT_PYTHON = /usr/bin/python exists(/usr/local/bin/python): SCRIPT_PYTHON = /usr/local/bin/python } +isEmpty(SCRIPT_PYTHON) { + # Check the system path. + SCRIPT_PYTHON = $$system(which python2.7) +} +isEmpty(SCRIPT_PYTHON) { + # Check the system path. + SCRIPT_PYTHON = $$system(which python2) +} isEmpty(SCRIPT_PYTHON) { # Check the system path. SCRIPT_PYTHON = $$system(which python) diff --git a/doomsday/configure.py b/doomsday/configure.py index 83288c304c..9478914834 100755 --- a/doomsday/configure.py +++ b/doomsday/configure.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python2.7 # # Doomsday Build Configuration Script # diff --git a/doomsday/doc/libcommon/command/inspectgamesave.ame b/doomsday/doc/libcommon/command/inspectgamesave.ame new file mode 100644 index 0000000000..467c10fcdf --- /dev/null +++ b/doomsday/doc/libcommon/command/inspectgamesave.ame @@ -0,0 +1,6 @@ +@summary{ + Print detailed information about a saved game session to the console. +} +@description{ + Params: inspectgamesave (slotId) @cbr For example, 'inspectgamesave 1'. +} diff --git a/doomsday/libappfw/appfw.doxy b/doomsday/libappfw/appfw.doxy new file mode 100644 index 0000000000..1ab69deb3e --- /dev/null +++ b/doomsday/libappfw/appfw.doxy @@ -0,0 +1,25 @@ +# Public API documentation for libappfw +@INCLUDE = ../doomsday.doxy + +PROJECT_NAME = "libappfw" +PROJECT_NUMBER = 1.14.0 +PROJECT_BRIEF = "Application Framework" +OUTPUT_DIRECTORY = ../apidoc/appfw/ + +INPUT = include src +FILE_PATTERNS = * +EXCLUDE_PATTERNS = .DS_Store +PREDEFINED = __cplusplus __LIBAPPFW__ \ + "DENG2_PIMPL(ClassName)=typedef ClassName Public; struct ClassName::Instance : public de::Private" \ + "DENG2_PIMPL_NOREF(C)=struct C::Instance : public de::IPrivate" \ + "DENG2_PRIVATE(Var)=struct Instance; Instance *Var;" \ + "DENG2_ERROR(N)=class N : public de::Error {};" \ + "DENG2_SUB_ERROR(B,N)=class N : public B {};" + +INCLUDED_BY_GRAPH = NO +COLLABORATION_GRAPH = NO +REFERENCED_BY_RELATION = NO +OPTIMIZE_OUTPUT_FOR_C = NO +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = NO +INTERNAL_DOCS = NO diff --git a/doomsday/libappfw/include/de/IPersistent b/doomsday/libappfw/include/de/IPersistent new file mode 100644 index 0000000000..c076c746bb --- /dev/null +++ b/doomsday/libappfw/include/de/IPersistent @@ -0,0 +1,2 @@ +#include "framework/ipersistent.h" + diff --git a/doomsday/libappfw/include/de/PersistentState b/doomsday/libappfw/include/de/PersistentState new file mode 100644 index 0000000000..c1623a7c24 --- /dev/null +++ b/doomsday/libappfw/include/de/PersistentState @@ -0,0 +1,2 @@ +#include "framework/persistentstate.h" + diff --git a/doomsday/libappfw/include/de/TabWidget b/doomsday/libappfw/include/de/TabWidget new file mode 100644 index 0000000000..0df8926686 --- /dev/null +++ b/doomsday/libappfw/include/de/TabWidget @@ -0,0 +1 @@ +#include "widgets/tabwidget.h" diff --git a/doomsday/libappfw/include/de/framework/actionitem.h b/doomsday/libappfw/include/de/framework/actionitem.h index 2838d19719..cd9b61b022 100644 --- a/doomsday/libappfw/include/de/framework/actionitem.h +++ b/doomsday/libappfw/include/de/framework/actionitem.h @@ -19,7 +19,7 @@ #ifndef LIBAPPFW_UI_ACTIONITEM_H #define LIBAPPFW_UI_ACTIONITEM_H -#include "item.h" +#include "imageitem.h" #include #include @@ -30,37 +30,34 @@ namespace ui { /** * UI context item that represents a user action. */ -class LIBAPPFW_PUBLIC ActionItem : public Item +class LIBAPPFW_PUBLIC ActionItem : public ImageItem { public: ActionItem(String const &label = "", RefArg action = RefArg()) - : Item(ShownAsButton | ActivationClosesPopup, label) + : ImageItem(ShownAsButton | ActivationClosesPopup, label) , _action(action.holdRef()) {} ActionItem(Semantics semantics, String const &label = "", RefArg action = RefArg()) - : Item(semantics, label) + : ImageItem(semantics, label) , _action(action.holdRef()) {} ActionItem(Semantics semantics, Image const &img, String const &label = "", RefArg action = RefArg()) - : Item(semantics, label) - , _action(action.holdRef()) - , _image(img) {} + : ImageItem(semantics, img, label) + , _action(action.holdRef()) {} ActionItem(Image const &img, String const &label = "", RefArg action = RefArg()) - : Item(ShownAsButton | ActivationClosesPopup, label) - , _action(action.holdRef()) - , _image(img) {} + : ImageItem(ShownAsButton | ActivationClosesPopup, img, label) + , _action(action.holdRef()) {} Action const *action() const { return _action; } - Image const &image() const { return _image; } void setAction(RefArg action) { @@ -68,15 +65,8 @@ class LIBAPPFW_PUBLIC ActionItem : public Item notifyChange(); } - void setImage(Image const &image) - { - _image = image; - notifyChange(); - } - private: AutoRef _action; - Image _image; }; } // namespace ui diff --git a/doomsday/libappfw/include/de/framework/baseguiapp.h b/doomsday/libappfw/include/de/framework/baseguiapp.h index 93b9c42cee..dd9ed308c3 100644 --- a/doomsday/libappfw/include/de/framework/baseguiapp.h +++ b/doomsday/libappfw/include/de/framework/baseguiapp.h @@ -20,6 +20,7 @@ #define LIBAPPFW_BASEGUIAPP_H #include "../libappfw.h" +#include "../PersistentState" #include #include @@ -43,8 +44,11 @@ class LIBAPPFW_PUBLIC BaseGuiApp : public GuiApp public: BaseGuiApp(int &argc, char **argv); + void initSubsystems(SubsystemInitFlags flags = DefaultSubsystems); + public: static BaseGuiApp &app(); + static PersistentState &persistentUIState(); static GLShaderBank &shaders(); static VRConfig &vr(); diff --git a/doomsday/libappfw/include/de/framework/childwidgetorganizer.h b/doomsday/libappfw/include/de/framework/childwidgetorganizer.h index be5bd13299..7c4dfe1b90 100644 --- a/doomsday/libappfw/include/de/framework/childwidgetorganizer.h +++ b/doomsday/libappfw/include/de/framework/childwidgetorganizer.h @@ -150,6 +150,8 @@ class LIBAPPFW_PUBLIC ChildWidgetOrganizer GuiWidget *itemWidget(ui::Item const &item) const; GuiWidget *itemWidget(de::String const &label) const; + ui::Item const *findItemForWidget(GuiWidget const &widget) const; + private: DENG2_PRIVATE(d) }; diff --git a/doomsday/libappfw/include/de/framework/data.h b/doomsday/libappfw/include/de/framework/data.h index 0d9465b3bc..3a766e700e 100644 --- a/doomsday/libappfw/include/de/framework/data.h +++ b/doomsday/libappfw/include/de/framework/data.h @@ -50,16 +50,16 @@ class LIBAPPFW_PUBLIC Data /** * Notified when a new item is added to the data context. */ - DENG2_DEFINE_AUDIENCE(Addition, void contextItemAdded(Pos id, Item const &item)) + DENG2_DEFINE_AUDIENCE(Addition, void dataItemAdded(Pos id, Item const &item)) /** * Notified when an item has been removed from the data context. When this * is called @a item is no longer in the context and can be modified at * will. */ - DENG2_DEFINE_AUDIENCE(Removal, void contextItemRemoved(Pos oldId, Item &item)) + DENG2_DEFINE_AUDIENCE(Removal, void dataItemRemoved(Pos oldId, Item &item)) - DENG2_DEFINE_AUDIENCE(OrderChange, void contextItemOrderChanged()) + DENG2_DEFINE_AUDIENCE(OrderChange, void dataItemOrderChanged()) public: virtual ~Data() {} diff --git a/doomsday/libappfw/include/de/framework/guirootwidget.h b/doomsday/libappfw/include/de/framework/guirootwidget.h index 3c5631001a..6ab41fc55f 100644 --- a/doomsday/libappfw/include/de/framework/guirootwidget.h +++ b/doomsday/libappfw/include/de/framework/guirootwidget.h @@ -62,6 +62,7 @@ class LIBAPPFW_PUBLIC GuiRootWidget : public RootWidget Id borderGlow() const; Id toggleOnOff() const; Id tinyDot() const; + Id fold() const; static GLShaderBank &shaders(); diff --git a/doomsday/libappfw/include/de/framework/guiwidget.h b/doomsday/libappfw/include/de/framework/guiwidget.h index 8c6fccffda..09cb8f0b9f 100644 --- a/doomsday/libappfw/include/de/framework/guiwidget.h +++ b/doomsday/libappfw/include/de/framework/guiwidget.h @@ -49,6 +49,10 @@ class BlurWidget; * * The common features GuiWidget offers to all widgets are: * + * - Automatically saving and restoring persistent state. Classes that implement + * IPersistent will automatically be saved and restored when the widget is + * (de)initialized. + * * - Background geometry builder. All widgets may use this to build geometry for * the background of the widget. However, widgets are also allowed to fully * generate all of their geometry from scratch. @@ -69,7 +73,8 @@ class BlurWidget; * hit testing to their particular visual shape. * * - Logic for handling more complicated interactions such as a mouse pointer - * click (press then release inside or outside). + * click (press then release inside or outside), and passing received events + * to separately registered event handler objects. * * QObject is a base class for the signals and slots capabilities. * diff --git a/doomsday/libappfw/include/de/framework/imageitem.h b/doomsday/libappfw/include/de/framework/imageitem.h new file mode 100644 index 0000000000..bfd6c837ab --- /dev/null +++ b/doomsday/libappfw/include/de/framework/imageitem.h @@ -0,0 +1,55 @@ +/** @file imageitem.h Data item with an image. + * + * @authors Copyright (c) 2014 Jaakko Keränen + * + * @par License + * LGPL: http://www.gnu.org/licenses/lgpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) 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 Lesser + * General Public License for more details. You should have received a copy of + * the GNU Lesser General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#ifndef LIBAPPFW_UI_IMAGEITEM_H +#define LIBAPPFW_UI_IMAGEITEM_H + +#include "item.h" +#include + +namespace de { +namespace ui { + +/** + * UI context item that represents a user action. + */ +class LIBAPPFW_PUBLIC ImageItem : public Item +{ +public: + ImageItem(Semantics semantics, String const &label = "") + : Item(semantics, label) {} + + ImageItem(Semantics semantics, Image const &image, String const &label = "") + : Item(semantics, label), _image(image) {} + + Image const &image() const { return _image; } + + void setImage(Image const &image) + { + _image = image; + notifyChange(); + } + +private: + Image _image; +}; + +} // namespace ui +} // namespace de + +#endif // LIBAPPFW_UI_IMAGEITEM_H diff --git a/doomsday/libappfw/include/de/framework/ipersistent.h b/doomsday/libappfw/include/de/framework/ipersistent.h new file mode 100644 index 0000000000..5d3b69e637 --- /dev/null +++ b/doomsday/libappfw/include/de/framework/ipersistent.h @@ -0,0 +1,45 @@ +/** @file ipersistent.h + * + * @authors Copyright (c) 2014 Jaakko Keränen + * + * @par License + * LGPL: http://www.gnu.org/licenses/lgpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) 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 Lesser + * General Public License for more details. You should have received a copy of + * the GNU Lesser General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#ifndef LIBAPPFW_IPERSISTENT_H +#define LIBAPPFW_IPERSISTENT_H + +#include "../libappfw.h" + +namespace de { + +class PersistentState; + +/** + * Interface for objects whose state can be stored persistently using PersistentState. + * + * GuiWidget instances that implement IPersistent will automatically be saved and + * restored when the widget is (de)initialized. + */ +class LIBAPPFW_PUBLIC IPersistent +{ +public: + virtual ~IPersistent() {} + + virtual void operator >> (PersistentState &toState) const = 0; + virtual void operator << (PersistentState const &fromState) = 0; +}; + +} // namespace de + +#endif // LIBAPPFW_IPERSISTENT_H diff --git a/doomsday/libappfw/include/de/framework/persistentstate.h b/doomsday/libappfw/include/de/framework/persistentstate.h new file mode 100644 index 0000000000..cbf96c6539 --- /dev/null +++ b/doomsday/libappfw/include/de/framework/persistentstate.h @@ -0,0 +1,44 @@ +/** @file persistentstate.h Persistent UI state. + * + * @authors Copyright (c) 2014 Jaakko Keränen + * + * @par License + * LGPL: http://www.gnu.org/licenses/lgpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) 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 Lesser + * General Public License for more details. You should have received a copy of + * the GNU Lesser General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#ifndef LIBAPPFW_PERSISTENTSTATE_H +#define LIBAPPFW_PERSISTENTSTATE_H + +#include "../libappfw.h" +#include +#include + +namespace de { + +class IPersistent; + +/** + * Stores and recalls persistent state across running sessions. + */ +class LIBAPPFW_PUBLIC PersistentState : public Refuge +{ +public: + PersistentState(String const &name); + + PersistentState &operator << (IPersistent const &object); + PersistentState &operator >> (IPersistent &object); +}; + +} // namespace de + +#endif // LIBAPPFW_PERSISTENTSTATE_H diff --git a/doomsday/libappfw/include/de/ui/ImageItem b/doomsday/libappfw/include/de/ui/ImageItem new file mode 100644 index 0000000000..e85081ba27 --- /dev/null +++ b/doomsday/libappfw/include/de/ui/ImageItem @@ -0,0 +1,2 @@ +#include "../framework/imageitem.h" + diff --git a/doomsday/libappfw/include/de/widgets/buttonwidget.h b/doomsday/libappfw/include/de/widgets/buttonwidget.h index a7c9eeeea5..65fa2d9d9a 100644 --- a/doomsday/libappfw/include/de/widgets/buttonwidget.h +++ b/doomsday/libappfw/include/de/widgets/buttonwidget.h @@ -74,6 +74,8 @@ class LIBAPPFW_PUBLIC ButtonWidget : public LabelWidget */ void setHoverTextColor(DotPath const &hoverTextId, HoverColorMode mode = ModulateColor); + void setBackgroundColor(DotPath const &bgColorId); + /** * Sets the action of the button. It gets triggered when the button is * pressed. diff --git a/doomsday/libappfw/include/de/widgets/lineeditwidget.h b/doomsday/libappfw/include/de/widgets/lineeditwidget.h index fd9c0cebc8..27908e18a8 100644 --- a/doomsday/libappfw/include/de/widgets/lineeditwidget.h +++ b/doomsday/libappfw/include/de/widgets/lineeditwidget.h @@ -75,6 +75,7 @@ class LIBAPPFW_PUBLIC LineEditWidget : public GuiWidget, public shell::AbstractL signals: void enterPressed(QString text); + void editorContentChanged(); protected: void glInit(); diff --git a/doomsday/libappfw/include/de/widgets/panelwidget.h b/doomsday/libappfw/include/de/widgets/panelwidget.h index 69ae8ecf4d..547031ba1d 100644 --- a/doomsday/libappfw/include/de/widgets/panelwidget.h +++ b/doomsday/libappfw/include/de/widgets/panelwidget.h @@ -86,6 +86,8 @@ class LIBAPPFW_PUBLIC PanelWidget : public GuiWidget bool isOpen() const; + bool isOpeningOrClosing() const; + void close(TimeDelta delayBeforeClosing); // Events. diff --git a/doomsday/libappfw/include/de/widgets/tabwidget.h b/doomsday/libappfw/include/de/widgets/tabwidget.h new file mode 100644 index 0000000000..c7cf9907da --- /dev/null +++ b/doomsday/libappfw/include/de/widgets/tabwidget.h @@ -0,0 +1,85 @@ +/** @file tabwidget.h Tab widget. + * + * @authors Copyright (c) 2014 Jaakko Keränen + * + * @par License + * LGPL: http://www.gnu.org/licenses/lgpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) 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 Lesser + * General Public License for more details. You should have received a copy of + * the GNU Lesser General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#ifndef LIBAPPFW_TABWIDGET_H +#define LIBAPPFW_TABWIDGET_H + +#include "../GuiWidget" +#include "../ui/ImageItem" +#include "../ui/Data" + +namespace de { + +/** + * Tab buttons. Behaves like radio buttons, with one of the buttons being selected at a + * time. One of the tabs is always selected. + * + * The widget sets its own height based on the height of the tab buttons. Tab buttons + * are centered in the width of the widget. + */ +class TabWidget : public GuiWidget +{ + Q_OBJECT + +public: + class TabItem : public ui::ImageItem + { + public: + TabItem(String const &label, QVariant const &userData = QVariant()) + : ImageItem(ShownAsButton, label) + { + setData(userData); + } + TabItem(Image const &img, String const &label) + : ImageItem(ShownAsButton, img, label) {} + }; + +public: + TabWidget(String const &name = ""); + + /** + * Items representing the tabs in the widget. + * + * @return + */ + ui::Data &items(); + + /** + * Returns the currently selected tab index. One of the tabs is always selected. + */ + ui::Data::Pos current() const; + + TabItem ¤tItem(); + + void setCurrent(ui::Data::Pos itemPos); + + // Events. + void update(); + +signals: + void currentTabChanged(); + +private: + DENG2_PRIVATE(d) +}; + +typedef TabWidget::TabItem TabItem; + +} // namespace de + +#endif // LIBAPPFW_TABWIDGET_H diff --git a/doomsday/libappfw/libappfw.pro b/doomsday/libappfw/libappfw.pro index 5580dec584..28ea1c51ce 100644 --- a/doomsday/libappfw/libappfw.pro +++ b/doomsday/libappfw/libappfw.pro @@ -51,6 +51,7 @@ HEADERS += \ include/de/GuiRootWidget \ include/de/GuiWidget \ include/de/InputDialog \ + include/de/IPersistent \ include/de/LabelWidget \ include/de/LineEditWidget \ include/de/LogWidget \ @@ -59,6 +60,7 @@ HEADERS += \ include/de/NotificationWidget \ include/de/OculusRift \ include/de/PanelWidget \ + include/de/PersistentState \ include/de/PopupMenuWidget \ include/de/PopupWidget \ include/de/ProceduralImage \ @@ -68,6 +70,7 @@ HEADERS += \ include/de/SequentialLayout \ include/de/SignalAction \ include/de/SliderWidget \ + include/de/TabWidget \ include/de/TextDrawable \ include/de/ToggleWidget \ include/de/VRWindowTransform \ @@ -75,6 +78,7 @@ HEADERS += \ include/de/WindowTransform \ include/de/ui/ActionItem \ include/de/ui/Data \ + include/de/ui/ImageItem \ include/de/ui/Item \ include/de/ui/ListData \ include/de/ui/Margins \ @@ -101,9 +105,12 @@ HEADERS += \ include/de/framework/guirootwidget.h \ include/de/framework/guiwidget.h \ include/de/framework/guiwidgetprivate.h \ + include/de/framework/imageitem.h \ include/de/framework/item.h \ + include/de/framework/ipersistent.h \ include/de/framework/listdata.h \ include/de/framework/margins.h \ + include/de/framework/persistentstate.h \ include/de/framework/proceduralimage.h \ include/de/framework/sequentiallayout.h \ include/de/framework/signalaction.h \ @@ -142,10 +149,10 @@ HEADERS += \ include/de/widgets/scriptcommandwidget.h \ include/de/widgets/scrollareawidget.h \ include/de/widgets/sliderwidget.h \ + include/de/widgets/tabwidget.h \ include/de/widgets/togglewidget.h \ include/de/widgets/variablechoicewidget.h \ - include/de/widgets/variabletogglewidget.h \ - include/de/vr/vrconfig.h + include/de/widgets/variabletogglewidget.h # Sources and private headers. SOURCES += \ @@ -164,6 +171,7 @@ SOURCES += \ src/item.cpp \ src/listdata.cpp \ src/margins.cpp \ + src/persistentstate.cpp \ src/proceduralimage.cpp \ src/sequentiallayout.cpp \ src/signalaction.cpp \ @@ -194,6 +202,7 @@ SOURCES += \ src/widgets/scriptcommandwidget.cpp \ src/widgets/scrollareawidget.cpp \ src/widgets/sliderwidget.cpp \ + src/widgets/tabwidget.cpp \ src/widgets/togglewidget.cpp \ src/widgets/variablechoicewidget.cpp \ src/widgets/variabletogglewidget.cpp \ diff --git a/doomsday/libappfw/src/baseguiapp.cpp b/doomsday/libappfw/src/baseguiapp.cpp index 055bea565d..74438a48be 100644 --- a/doomsday/libappfw/src/baseguiapp.cpp +++ b/doomsday/libappfw/src/baseguiapp.cpp @@ -23,6 +23,7 @@ namespace de { DENG2_PIMPL_NOREF(BaseGuiApp) { + QScopedPointer uiState; GLShaderBank shaders; VRConfig vr; }; @@ -31,11 +32,23 @@ BaseGuiApp::BaseGuiApp(int &argc, char **argv) : GuiApp(argc, argv), d(new Instance) {} +void BaseGuiApp::initSubsystems(SubsystemInitFlags flags) +{ + GuiApp::initSubsystems(flags); + + d->uiState.reset(new PersistentState("UIState")); +} + BaseGuiApp &BaseGuiApp::app() { return static_cast(App::app()); } +PersistentState &BaseGuiApp::persistentUIState() +{ + return *app().d->uiState; +} + GLShaderBank &BaseGuiApp::shaders() { return app().d->shaders; diff --git a/doomsday/libappfw/src/childwidgetorganizer.cpp b/doomsday/libappfw/src/childwidgetorganizer.cpp index 40568a16cb..9b21d3f4ed 100644 --- a/doomsday/libappfw/src/childwidgetorganizer.cpp +++ b/doomsday/libappfw/src/childwidgetorganizer.cpp @@ -172,12 +172,12 @@ DENG2_OBSERVES(ui::Item, Change ) } } - void contextItemAdded(ui::Data::Pos pos, ui::Item const &) + void dataItemAdded(ui::Data::Pos pos, ui::Item const &) { addItemWidget(pos); } - void contextItemRemoved(ui::Data::Pos, ui::Item &item) + void dataItemRemoved(ui::Data::Pos, ui::Item &item) { Mapping::const_iterator found = mapping.constFind(&item); if(found != mapping.constEnd()) @@ -188,7 +188,7 @@ DENG2_OBSERVES(ui::Item, Change ) } } - void contextItemOrderChanged() + void dataItemOrderChanged() { // Remove all widgets and put them back in the correct order. DENG2_FOR_EACH_CONST(Mapping, i, mapping) @@ -240,6 +240,18 @@ DENG2_OBSERVES(ui::Item, Change ) } return 0; } + + ui::Item const *findByWidget(GuiWidget const &widget) const + { + DENG2_FOR_EACH_CONST(Mapping, i, mapping) + { + if(i.value() == &widget) + { + return i.key(); + } + } + return 0; + } }; ChildWidgetOrganizer::ChildWidgetOrganizer(GuiWidget &container) @@ -293,6 +305,11 @@ GuiWidget *ChildWidgetOrganizer::itemWidget(String const &label) const return d->findByLabel(label); } +ui::Item const *ChildWidgetOrganizer::findItemForWidget(GuiWidget const &widget) const +{ + return d->findByWidget(widget); +} + GuiWidget *DefaultWidgetFactory::makeItemWidget(ui::Item const &, GuiWidget const *) { return new LabelWidget; diff --git a/doomsday/libappfw/src/guirootwidget.cpp b/doomsday/libappfw/src/guirootwidget.cpp index 716e34be11..4a95eab8b2 100644 --- a/doomsday/libappfw/src/guirootwidget.cpp +++ b/doomsday/libappfw/src/guirootwidget.cpp @@ -45,14 +45,15 @@ DENG2_PIMPL(GuiRootWidget) Id borderGlow; Id toggleOnOff; Id tinyDot; + Id fold; bool noFramesDrawnYet; Instance(Public *i, CanvasWindow *win) - : Base(i), - window(win), - atlas(0), - uTexAtlas("uTex", GLUniform::Sampler2D), - noFramesDrawnYet(true) + : Base(i) + , window(win) + , atlas(0) + , uTexAtlas("uTex", GLUniform::Sampler2D) + , noFramesDrawnYet(true) { self.audienceForChildAddition += this; } @@ -162,6 +163,9 @@ DENG2_PIMPL(GuiRootWidget) // On/Off toggle. toggleOnOff = atlas->alloc(st.images().image("toggle.onoff")); + // Fold indicator. + fold = atlas->alloc(st.images().image("fold")); + // Tiny dot. { QImage dot(QSize(5, 5), QImage::Format_ARGB32); @@ -251,6 +255,12 @@ Id GuiRootWidget::tinyDot() const return d->tinyDot; } +Id de::GuiRootWidget::fold() const +{ + d->initAtlas(); + return d->fold; +} + GLShaderBank &GuiRootWidget::shaders() { return BaseGuiApp::shaders(); diff --git a/doomsday/libappfw/src/guiwidget.cpp b/doomsday/libappfw/src/guiwidget.cpp index 03ee488810..3b65c4832d 100644 --- a/doomsday/libappfw/src/guiwidget.cpp +++ b/doomsday/libappfw/src/guiwidget.cpp @@ -20,6 +20,8 @@ #include "de/GuiRootWidget" #include "de/BlurWidget" #include "de/Style" +#include "de/BaseGuiApp" +#include "de/IPersistent" #include #include @@ -314,6 +316,39 @@ DENG2_PIMPL(GuiWidget) opacityWhenDisabled.finish(); } } + + void restoreState() + { + try + { + if(IPersistent *po = self.maybeAs()) + { + DENG2_BASE_GUI_APP->persistentUIState() >> *po; + } + } + catch(Error const &er) + { + // Benign: widget will use default state. + LOG_VERBOSE("Failed to restore state of widget '%s': %s") + << self.path() << er.asText(); + } + } + + void saveState() + { + try + { + if(IPersistent *po = self.maybeAs()) + { + DENG2_BASE_GUI_APP->persistentUIState() << *po; + } + } + catch(Error const &er) + { + LOG_WARNING("Failed to save state of widget '%s': %s") + << self.path() << er.asText(); + } + } }; GuiWidget::GuiWidget(String const &name) : Widget(name), d(new Instance(this)) @@ -508,10 +543,12 @@ void GuiWidget::initialize() { d->inited = true; glInit(); + + d->restoreState(); } catch(Error const &er) { - LOG_WARNING("Error when initializing widget '%s':\n") + LOG_WARNING("Error when initializing widget '%s': %s") << name() << er.asText(); } } @@ -522,13 +559,15 @@ void GuiWidget::deinitialize() try { + d->saveState(); + d->inited = false; d->deinitBlur(); glDeinit(); } catch(Error const &er) { - LOG_WARNING("Error when deinitializing widget '%s':\n") + LOG_WARNING("Error when deinitializing widget '%s': %s") << name() << er.asText(); } } diff --git a/doomsday/libappfw/src/listdata.cpp b/doomsday/libappfw/src/listdata.cpp index f7239a233a..9abd60a795 100644 --- a/doomsday/libappfw/src/listdata.cpp +++ b/doomsday/libappfw/src/listdata.cpp @@ -83,7 +83,7 @@ Data &ListData::insert(Pos pos, Item *item) // Notify. DENG2_FOR_AUDIENCE(Addition, i) { - i->contextItemAdded(pos, *item); + i->dataItemAdded(pos, *item); } return *this; @@ -103,7 +103,7 @@ Item *ListData::take(Data::Pos pos) // Notify. DENG2_FOR_AUDIENCE(Removal, i) { - i->contextItemRemoved(pos, *taken); + i->dataItemRemoved(pos, *taken); } return taken; @@ -125,7 +125,7 @@ void ListData::sort(LessThanFunc lessThan) // Notify. DENG2_FOR_AUDIENCE(OrderChange, i) { - i->contextItemOrderChanged(); + i->dataItemOrderChanged(); } } @@ -136,7 +136,7 @@ void ListData::stableSort(LessThanFunc lessThan) // Notify. DENG2_FOR_AUDIENCE(OrderChange, i) { - i->contextItemOrderChanged(); + i->dataItemOrderChanged(); } } diff --git a/doomsday/libappfw/src/persistentstate.cpp b/doomsday/libappfw/src/persistentstate.cpp new file mode 100644 index 0000000000..3033423a39 --- /dev/null +++ b/doomsday/libappfw/src/persistentstate.cpp @@ -0,0 +1,39 @@ +/** @file persistentstate.cpp Persistent UI state. + * + * @authors Copyright (c) 2014 Jaakko Keränen + * + * @par License + * LGPL: http://www.gnu.org/licenses/lgpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) 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 Lesser + * General Public License for more details. You should have received a copy of + * the GNU Lesser General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#include "de/PersistentState" +#include "de/IPersistent" + +namespace de { + +PersistentState::PersistentState(String const &name) : Refuge(name) +{} + +PersistentState &PersistentState::operator << (IPersistent const &object) +{ + object >> *this; + return *this; +} + +PersistentState &PersistentState::operator >> (IPersistent &object) +{ + object << *this; + return *this; +} + +} // namespace de diff --git a/doomsday/libappfw/src/vr/oculusrift.cpp b/doomsday/libappfw/src/vr/oculusrift.cpp index 9c0217bd4c..8b8bf7f784 100644 --- a/doomsday/libappfw/src/vr/oculusrift.cpp +++ b/doomsday/libappfw/src/vr/oculusrift.cpp @@ -271,6 +271,8 @@ void OculusRift::setPredictionLatency(float latency) { d->oculusTracker->setLatency(latency); } +#else + DENG2_UNUSED(latency); #endif } diff --git a/doomsday/libappfw/src/widgets/buttonwidget.cpp b/doomsday/libappfw/src/widgets/buttonwidget.cpp index 00eca114b1..f47bb0aad9 100644 --- a/doomsday/libappfw/src/widgets/buttonwidget.cpp +++ b/doomsday/libappfw/src/widgets/buttonwidget.cpp @@ -30,6 +30,7 @@ DENG2_OBSERVES(Action, Triggered) State state; DotPath hoverTextColor; DotPath originalTextColor; + DotPath bgColorId; HoverColorMode hoverColorMode; Action *action; Animation scale; @@ -39,6 +40,7 @@ DENG2_OBSERVES(Action, Triggered) Instance(Public *i) : Base(i) , state(Up) + , bgColorId("background") , hoverColorMode(ReplaceColor) , action(0) , scale(1.f) @@ -138,7 +140,7 @@ DENG2_OBSERVES(Action, Triggered) void setDefaultBackground() { - self.set(Background(style().colors().colorf("background"), + self.set(Background(style().colors().colorf(bgColorId), Background::GradientFrame, Vector4f(1, 1, 1, frameOpacity), 6)); } @@ -147,7 +149,7 @@ DENG2_OBSERVES(Action, Triggered) Background bg = self.background(); if(bg.type == Background::GradientFrame) { - bg.solidFill = style().colors().colorf("background"); + bg.solidFill = style().colors().colorf(bgColorId); bg.color = Vector4f(1, 1, 1, frameOpacity); self.set(bg); } @@ -184,6 +186,12 @@ void ButtonWidget::setHoverTextColor(DotPath const &hoverTextId, HoverColorMode d->hoverColorMode = mode; } +void ButtonWidget::setBackgroundColor(DotPath const &bgColorId) +{ + d->bgColorId = bgColorId; + d->updateBackground(); +} + void ButtonWidget::setAction(RefArg action) { if(d->action) diff --git a/doomsday/libappfw/src/widgets/choicewidget.cpp b/doomsday/libappfw/src/widgets/choicewidget.cpp index 34a1b58609..9989bb157e 100644 --- a/doomsday/libappfw/src/widgets/choicewidget.cpp +++ b/doomsday/libappfw/src/widgets/choicewidget.cpp @@ -134,7 +134,7 @@ DENG2_OBSERVES(ChildWidgetOrganizer, WidgetUpdate) return selected < items().size(); } - void contextItemAdded(Data::Pos id, ui::Item const &) + void dataItemAdded(Data::Pos id, ui::Item const &) { updateMaximumWidth(); @@ -154,7 +154,7 @@ DENG2_OBSERVES(ChildWidgetOrganizer, WidgetUpdate) } } - void contextItemRemoved(Data::Pos id, ui::Item &) + void dataItemRemoved(Data::Pos id, ui::Item &) { if(id <= selected && selected > 0) { @@ -165,7 +165,7 @@ DENG2_OBSERVES(ChildWidgetOrganizer, WidgetUpdate) updateMaximumWidth(); } - void contextItemOrderChanged() + void dataItemOrderChanged() { updateButtonWithSelection(); } diff --git a/doomsday/libappfw/src/widgets/dialogwidget.cpp b/doomsday/libappfw/src/widgets/dialogwidget.cpp index 7a7add73cc..b1cc1df1e1 100644 --- a/doomsday/libappfw/src/widgets/dialogwidget.cpp +++ b/doomsday/libappfw/src/widgets/dialogwidget.cpp @@ -243,12 +243,12 @@ public ChildWidgetOrganizer::IFilter return false; } - void contextItemAdded(ui::Data::Pos, ui::Item const &) + void dataItemAdded(ui::Data::Pos, ui::Item const &) { needButtonUpdate = true; } - void contextItemRemoved(ui::Data::Pos, ui::Item &) + void dataItemRemoved(ui::Data::Pos, ui::Item &) { needButtonUpdate = true; } diff --git a/doomsday/libappfw/src/widgets/foldpanelwidget.cpp b/doomsday/libappfw/src/widgets/foldpanelwidget.cpp index 7f907be19a..c36265070e 100644 --- a/doomsday/libappfw/src/widgets/foldpanelwidget.cpp +++ b/doomsday/libappfw/src/widgets/foldpanelwidget.cpp @@ -27,7 +27,6 @@ using namespace ui; DENG2_PIMPL_NOREF(FoldPanelWidget) { - /* struct FoldImage : public ProceduralImage { FoldPanelWidget &fold; @@ -38,28 +37,28 @@ DENG2_PIMPL_NOREF(FoldPanelWidget) void update() { - setSize(fold.root().atlas().imageRect(fold.root().roundCorners()).size()); + float h = fold.title().font().height().value(); + setSize(Vector2f(h, h)); } void glMakeGeometry(DefaultVertexBuf::Builder &verts, Rectanglef const &rect) { GuiRootWidget &root = fold.root(); Atlas &atlas = root.atlas(); - - ColorBank::Colorf const &textColor = fold.style().colors().colorf("text"); - ColorBank::Colorf accentColor = fold.style().colors().colorf("accent") - * Vector4f(1, 1, 1, fold.isOpen()? .5f : 1); - - verts.makeQuad(rect, accentColor, - atlas.imageRectf(root.roundCorners())); - - Vector2ui dotSize = atlas.imageRect(root.tinyDot()).size(); - verts.makeQuad(Rectanglef::fromSize(rect.middle() - dotSize/2, - dotSize), - fold.isOpen()? accentColor : textColor, - atlas.imageRectf(root.tinyDot())); + ColorBank::Colorf const &textColor = fold.title().textColorf(); + + verts.makeFlexibleFrame(rect.toRectanglei(), 5, textColor, + atlas.imageRectf(root.roundCorners())); + + Rectanglef uv = atlas.imageRectf(root.fold()); + if(!fold.isOpen()) + { + // Flip it. + uv = Rectanglef(uv.bottomLeft(), uv.topRight()); + } + verts.makeQuad(rect, textColor * Vector4f(1, 1, 1, .5f), uv); } - };*/ + }; ButtonWidget *title; // not owned GuiWidget *container; ///< Held here while not part of the widget tree. @@ -85,9 +84,10 @@ ButtonWidget *FoldPanelWidget::makeTitle(String const &text) d->title->setAction(new SignalAction(this, SLOT(toggleFold()))); d->title->setOpacity(.8f); - // Icon is disabled for now, doesn't look quite right. - //d->title->setImage(new Instance::FoldImage(*this)); - //d->title->setTextAlignment(ui::AlignRight); // Text is on the right from the image. + // Fold indicator. + d->title->setImage(new Instance::FoldImage(*this)); + d->title->setTextAlignment(ui::AlignRight); + d->title->setTextGap("gap"); return d->title; } diff --git a/doomsday/libappfw/src/widgets/lineeditwidget.cpp b/doomsday/libappfw/src/widgets/lineeditwidget.cpp index 8b78454009..1edf43b2d9 100644 --- a/doomsday/libappfw/src/widgets/lineeditwidget.cpp +++ b/doomsday/libappfw/src/widgets/lineeditwidget.cpp @@ -204,6 +204,8 @@ DENG_GUI_PIMPL(LineEditWidget) void contentChanged() { composer.setText(self.text()); + + emit self.editorContentChanged(); } void atlasContentRepositioned(Atlas &) diff --git a/doomsday/libappfw/src/widgets/menuwidget.cpp b/doomsday/libappfw/src/widgets/menuwidget.cpp index f01aadc312..21f5d03806 100644 --- a/doomsday/libappfw/src/widgets/menuwidget.cpp +++ b/doomsday/libappfw/src/widgets/menuwidget.cpp @@ -196,19 +196,19 @@ DENG2_PIMPL(MenuWidget) organizer.setContext(*items); // recreates widgets } - void contextItemAdded(Data::Pos, Item const &) + void dataItemAdded(Data::Pos, Item const &) { // Make sure we determine the layout for the new item. needLayout = true; } - void contextItemRemoved(Data::Pos, Item &) + void dataItemRemoved(Data::Pos, Item &) { // Make sure we determine the layout after this item is gone. needLayout = true; } - void contextItemOrderChanged() + void dataItemOrderChanged() { // Make sure we determine the layout for the new order. needLayout = true; diff --git a/doomsday/libappfw/src/widgets/panelwidget.cpp b/doomsday/libappfw/src/widgets/panelwidget.cpp index 39d405eec9..ada96f8c15 100644 --- a/doomsday/libappfw/src/widgets/panelwidget.cpp +++ b/doomsday/libappfw/src/widgets/panelwidget.cpp @@ -190,6 +190,8 @@ GuiWidget &PanelWidget::content() const GuiWidget *PanelWidget::takeContent() { GuiWidget *w = d->content; + if(!w) return 0; + d->content = 0; w->rule().clearInput(Rule::Left); @@ -225,6 +227,11 @@ bool PanelWidget::isOpen() const return d->opened; } +bool PanelWidget::isOpeningOrClosing() const +{ + return !d->openingRule->animation().done(); +} + void PanelWidget::close(TimeDelta delayBeforeClosing) { d->close(delayBeforeClosing); diff --git a/doomsday/libappfw/src/widgets/tabwidget.cpp b/doomsday/libappfw/src/widgets/tabwidget.cpp new file mode 100644 index 0000000000..809d6c77ad --- /dev/null +++ b/doomsday/libappfw/src/widgets/tabwidget.cpp @@ -0,0 +1,144 @@ +/** @file tabwidget.cpp Tab widget. + * + * @authors Copyright (c) 2014 Jaakko Keränen + * + * @par License + * LGPL: http://www.gnu.org/licenses/lgpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) 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 Lesser + * General Public License for more details. You should have received a copy of + * the GNU Lesser General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#include "de/TabWidget" +#include "de/ui/ListData" +#include "de/MenuWidget" + +Q_DECLARE_METATYPE(de::ui::Item const *) + +namespace de { + +DENG2_PIMPL(TabWidget) +, DENG2_OBSERVES(ChildWidgetOrganizer, WidgetCreation) +, DENG2_OBSERVES(ui::Data, Addition) +, DENG2_OBSERVES(ui::Data, OrderChange) +, DENG2_OBSERVES(ButtonWidget, Press) +{ + ui::Data::Pos current; + MenuWidget *buttons; + bool needUpdate; + + Instance(Public *i) + : Base(i) + , current(0) + , needUpdate(false) + { + self.add(buttons = new MenuWidget); + buttons->enableScrolling(false); + buttons->margins().set(""); + buttons->setGridSize(0, ui::Expand, 1, ui::Expand, GridLayout::ColumnFirst); + + buttons->organizer().audienceForWidgetCreation += this; + buttons->items().audienceForAddition += this; + buttons->items().audienceForOrderChange += this; + + // Center the buttons inside the widget. + buttons->rule() + .setInput(Rule::AnchorX, self.rule().left() + self.rule().width() / 2) + .setInput(Rule::Top, self.rule().top()) + .setAnchorPoint(Vector2f(.5f, 0)); + } + + void widgetCreatedForItem(GuiWidget &widget, ui::Item const &) + { + // Set the font and style. + ButtonWidget &btn = widget.as(); + btn.setSizePolicy(ui::Expand, ui::Expand); + btn.setFont("tab.label"); + btn.margins().set("dialog.gap"); + + btn.audienceForPress += this; + } + + void buttonPressed(ButtonWidget &button) + { + self.setCurrent(buttons->items().find(*buttons->organizer().findItemForWidget(button))); + } + + void dataItemAdded(ui::Data::Pos, ui::Item const &) + { + needUpdate = true; + } + + void dataItemOrderChanged() + { + needUpdate = true; + } + + void setCurrent(ui::Data::Pos pos) + { + if(current != pos && pos < buttons->items().size()) + { + current = pos; + updateSelected(); + emit self.currentTabChanged(); + } + } + + void updateSelected() + { + for(ui::Data::Pos i = 0; i < buttons->items().size(); ++i) + { + bool const sel = (i == current); + ButtonWidget &w = buttons->itemWidget(buttons->items().at(i)); + w.setFont(sel? "tab.selected" : "tab.label"); + w.setTextColor(sel? "tab.selected" : "text"); + } + } +}; + +TabWidget::TabWidget(String const &name) + : GuiWidget(name), d(new Instance(this)) +{ + rule().setInput(Rule::Height, d->buttons->rule().height()); +} + +ui::Data &TabWidget::items() +{ + return d->buttons->items(); +} + +ui::Data::Pos TabWidget::current() const +{ + return d->current; +} + +TabItem &TabWidget::currentItem() +{ + DENG2_ASSERT(d->current < items().size()); + return items().at(d->current).as(); +} + +void TabWidget::setCurrent(ui::Data::Pos itemPos) +{ + d->setCurrent(itemPos); +} + +void TabWidget::update() +{ + GuiWidget::update(); + + if(d->needUpdate) + { + d->updateSelected(); + d->needUpdate = false; + } +} + +} // namespace de diff --git a/doomsday/libdeng1/include/de/libdeng1.h b/doomsday/libdeng1/include/de/libdeng1.h index c05a80cc3c..8703ce185e 100644 --- a/doomsday/libdeng1/include/de/libdeng1.h +++ b/doomsday/libdeng1/include/de/libdeng1.h @@ -81,10 +81,10 @@ * Utility macros. */ -#define PI 3.14159265359f -#define PI_D 3.14159265358979323846 -#define DEG2RAD(a) (((a) * PI_D) / 180.0) -#define RAD2DEG(a) (((a) / PI_D) * 180.0) +#define DD_PI 3.14159265359f +#define DD_PI_D 3.14159265358979323846 +#define DEG2RAD(a) (((a) * DD_PI_D) / 180.0) +#define RAD2DEG(a) (((a) / DD_PI_D) * 180.0) #define FLOATEPSILON .000001f /** diff --git a/doomsday/libdeng1/src/mathutil.c b/doomsday/libdeng1/src/mathutil.c index 9b71b82aa8..74bd7ec5a7 100644 --- a/doomsday/libdeng1/src/mathutil.c +++ b/doomsday/libdeng1/src/mathutil.c @@ -1856,7 +1856,7 @@ double M_DirectionToAngleXY(double dx, double dy) if(dx == 0) return (dy > 0? 90.0 : 270.0); - angle = atan2((double) dy, (double) dx) * 180.0 / PI_D; + angle = atan2((double) dy, (double) dx) * 180.0 / DD_PI_D; if(angle < 0) angle += 360.0; @@ -1943,7 +1943,7 @@ double M_TriangleArea(double const v1[], double const v2[], double const v3[]) */ void M_RotateVector(float vec[3], float degYaw, float degPitch) { - float radYaw = degYaw / 180 * PI, radPitch = degPitch / 180 * PI; + float radYaw = degYaw / 180 * DD_PI, radPitch = degPitch / 180 * DD_PI; float Cos, Sin, res[3]; // Yaw. diff --git a/doomsday/libdeng1/src/memoryzone.c b/doomsday/libdeng1/src/memoryzone.c index 8e50b38b07..0cbc366acb 100644 --- a/doomsday/libdeng1/src/memoryzone.c +++ b/doomsday/libdeng1/src/memoryzone.c @@ -165,7 +165,7 @@ static memvolume_t *createVolume(size_t volumeSize) unlockZone(); App_Log(DE2_LOG_MESSAGE, - "Created a new %.1f MB memory volume.\n", vol->size / 1024.0 / 1024.0); + "Created a new %.1f MB memory volume.", vol->size / 1024.0 / 1024.0); Z_CheckHeap(); @@ -213,7 +213,7 @@ void Z_Shutdown(void) } App_Log(DE2_LOG_NOTE, - "Z_Shutdown: Used %i volumes, total %u bytes.\n", numVolumes, totalMemory); + "Z_Shutdown: Used %i volumes, total %u bytes.", numVolumes, totalMemory); Sys_DestroyMutex(zoneMutex); zoneMutex = 0; @@ -264,7 +264,7 @@ static void freeBlock(void *ptr, memblock_t **tracked) unlockZone(); DENG_ASSERT(block->id == LIBDENG_ZONEID); App_Log(DE2_LOG_WARNING, - "Attempted to free pointer without ZONEID.\n"); + "Attempted to free pointer without ZONEID."); return; } @@ -449,7 +449,7 @@ void *Z_Malloc(size_t size, int tag, void *user) if(tag < PU_APPSTATIC || tag > PU_PURGELEVEL) { - App_Log(DE2_LOG_WARNING, "Z_Malloc: Invalid purgelevel %i, cannot allocate memory.\n", tag); + App_Log(DE2_LOG_WARNING, "Z_Malloc: Invalid purgelevel %i, cannot allocate memory.", tag); return NULL; } if(!size) @@ -563,7 +563,7 @@ void *Z_Malloc(size_t size, int tag, void *user) // Scanned all the way through, no suitable space found. gotoNextVolume = true; App_Log(DE2_LOG_DEBUG, - "Z_Malloc: gave up on volume after %i checks\n", numChecked); + "Z_Malloc: gave up on volume after %i checks", numChecked); break; } } @@ -681,7 +681,7 @@ void Z_FreeTags(int lowTag, int highTag) memblock_t *block, *next; App_Log(DE2_LOG_DEBUG, - "MemoryZone: Freeing all blocks in tag range:[%i, %i)\n", + "MemoryZone: Freeing all blocks in tag range:[%i, %i)", lowTag, highTag+1); for(volume = volumeRoot; volume; volume = volume->next) @@ -715,7 +715,7 @@ void Z_CheckHeap(void) memblock_t *block; dd_bool isDone; - App_Log(DE2_LOG_TRACE, "Z_CheckHeap\n"); + App_Log(DE2_LOG_TRACE, "Z_CheckHeap"); lockZone(); @@ -727,7 +727,7 @@ void Z_CheckHeap(void) if(allocatedMemoryInVolume(volume) != volume->allocatedBytes) { App_Log(DE2_LOG_CRITICAL, - "Z_CheckHeap: allocated bytes counter is off (counter:%u != actual:%u)\n", + "Z_CheckHeap: allocated bytes counter is off (counter:%u != actual:%u)", volume->allocatedBytes, allocatedMemoryInVolume(volume)); App_FatalError("Z_CheckHeap: zone book-keeping is wrong"); } @@ -741,7 +741,7 @@ void Z_CheckHeap(void) if(total != volume->size - sizeof(memzone_t)) { App_Log(DE2_LOG_CRITICAL, - "Z_CheckHeap: invalid total size of blocks (%u != %u)\n", + "Z_CheckHeap: invalid total size of blocks (%u != %u)", total, volume->size - sizeof(memzone_t)); App_FatalError("Z_CheckHeap: zone book-keeping is wrong"); } @@ -751,7 +751,7 @@ void Z_CheckHeap(void) if((byte *)block - ((byte *)volume->zone + sizeof(memzone_t)) + block->size != volume->size - sizeof(memzone_t)) { App_Log(DE2_LOG_CRITICAL, - "Z_CheckHeap: last block does not cover the end (%u != %u)\n", + "Z_CheckHeap: last block does not cover the end (%u != %u)", (byte *)block - ((byte *)volume->zone + sizeof(memzone_t)) + block->size, volume->size - sizeof(memzone_t)); App_FatalError("Z_CheckHeap: zone is corrupted"); @@ -825,7 +825,7 @@ void Z_ChangeTag2(void *ptr, int tag) if(tag >= PU_PURGELEVEL && PTR2INT(block->user) < 0x100) { App_Log(DE2_LOG_ERROR, - "Z_ChangeTag: An owner is required for purgable blocks.\n"); + "Z_ChangeTag: An owner is required for purgable blocks."); } else { @@ -1043,7 +1043,7 @@ void Z_PrintStatus(void) size_t wasted = Z_FreeMemory(); App_Log(DE2_LOG_DEBUG, - "Memory zone status: %u volumes, %u bytes allocated, %u bytes free (%f%% in use)\n", + "Memory zone status: %u volumes, %u bytes allocated, %u bytes free (%f%% in use)", Z_VolumeCount(), (uint)allocated, (uint)wasted, (float)allocated/(float)(allocated+wasted)*100.f); } @@ -1070,7 +1070,7 @@ static void addBlockToSet(zblockset_t *set) set->_blocks = Z_Recalloc(set->_blocks, sizeof(zblockset_block_t) * set->_blockCount, set->_tag); App_Log(DE2_LOG_DEBUG, - "addBlockToSet: set=%p blockCount=%u elemSize=%u elemCount=%u (total=%u)\n", + "addBlockToSet: set=%p blockCount=%u elemSize=%u elemCount=%u (total=%u)", set, set->_blockCount, (uint)set->_elementSize, set->_elementsPerBlock, (uint)(set->_blockCount * set->_elementSize * set->_elementsPerBlock)); diff --git a/doomsday/libdeng1/src/reader.c b/doomsday/libdeng1/src/reader.c index 76403d4d64..03ee76ae19 100644 --- a/doomsday/libdeng1/src/reader.c +++ b/doomsday/libdeng1/src/reader.c @@ -79,7 +79,7 @@ static dd_bool Reader_Check(Reader const *reader, size_t len) if(reader->pos > reader->size - len) { App_Log(DE2_LOG_ERROR, - "Reader_Check: Position %lu[+%lu] out of bounds, size=%lu.\n", + "Reader_Check: Position %lu[+%lu] out of bounds, size=%lu.", (unsigned long) reader->pos, (unsigned long) len, (unsigned long) reader->size); diff --git a/doomsday/libdeng1/src/stack.c b/doomsday/libdeng1/src/stack.c index 53742e2353..53e3875631 100644 --- a/doomsday/libdeng1/src/stack.c +++ b/doomsday/libdeng1/src/stack.c @@ -75,7 +75,7 @@ void *Stack_Pop(ddstack_t *s) if(!s->height) { - App_Log(DE2_LOG_DEBUG, "Stack::Pop: Underflow.\n"); + App_Log(DE2_LOG_DEBUG, "Stack::Pop: Underflow."); return NULL; } diff --git a/doomsday/libdeng1/src/writer.c b/doomsday/libdeng1/src/writer.c index d19558fa8a..4af8575654 100644 --- a/doomsday/libdeng1/src/writer.c +++ b/doomsday/libdeng1/src/writer.c @@ -90,7 +90,7 @@ static dd_bool Writer_Check(Writer const *writer, size_t len) return true; } App_Log(DE2_LOG_ERROR, - "Writer_Check: Position %lu[+%lu] out of bounds, size=%lu, dynamic=%i.\n", + "Writer_Check: Position %lu[+%lu] out of bounds, size=%lu, dynamic=%i.", (unsigned long) writer->pos, (unsigned long) len, (unsigned long) writer->size, @@ -142,6 +142,7 @@ Writer *Writer_NewWithCallbacks(Writer_Callback_WriteInt8 writeInt8, void Writer_Delete(Writer *writer) { + if(!writer) return; if(writer->isDynamic) { // The buffer was allocated by us. @@ -328,7 +329,7 @@ void Writer_WritePackedUInt16(Writer *writer, uint16_t v) if(v & 0x8000) { App_Log(DE2_LOG_ERROR, - "Writer_WritePackedUInt16: Cannot write %i (%x).\n", v, v); + "Writer_WritePackedUInt16: Cannot write %i (%x).", v, v); return; } diff --git a/doomsday/libdeng2/data.pri b/doomsday/libdeng2/data.pri index 1d2d220509..8d1998566a 100644 --- a/doomsday/libdeng2/data.pri +++ b/doomsday/libdeng2/data.pri @@ -38,6 +38,7 @@ HEADERS += \ include/de/Reader \ include/de/Record \ include/de/RecordValue \ + include/de/Refuge \ include/de/RefValue \ include/de/Shared \ include/de/String \ @@ -91,6 +92,7 @@ HEADERS += \ include/de/data/reader.h \ include/de/data/record.h \ include/de/data/recordvalue.h \ + include/de/data/refuge.h \ include/de/data/refvalue.h \ include/de/data/shared.h \ include/de/data/string.h \ @@ -134,6 +136,7 @@ SOURCES += \ src/data/reader.cpp \ src/data/record.cpp \ src/data/recordvalue.cpp \ + src/data/refuge.cpp \ src/data/refvalue.cpp \ src/data/string.cpp \ src/data/stringpool.cpp \ diff --git a/doomsday/libdeng2/include/de/Refuge b/doomsday/libdeng2/include/de/Refuge new file mode 100644 index 0000000000..8d5dcc90f3 --- /dev/null +++ b/doomsday/libdeng2/include/de/Refuge @@ -0,0 +1,2 @@ +#include "data/refuge.h" + diff --git a/doomsday/libdeng2/include/de/c_wrapper.h b/doomsday/libdeng2/include/de/c_wrapper.h index 0fdf7636ab..5fba7334b9 100644 --- a/doomsday/libdeng2/include/de/c_wrapper.h +++ b/doomsday/libdeng2/include/de/c_wrapper.h @@ -1,7 +1,7 @@ -/* - * The Doomsday Engine Project -- libdeng2 +/** @file c_wrapper.h C wrapper for various libdeng2 classes. + * @ingroup core * - * Copyright (c) 2011-2013 Jaakko Keränen + * @authors Copyright © 2011-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/charsymbols.h b/doomsday/libdeng2/include/de/charsymbols.h index df8a804641..ed5f056f75 100644 --- a/doomsday/libdeng2/include/de/charsymbols.h +++ b/doomsday/libdeng2/include/de/charsymbols.h @@ -1,6 +1,6 @@ /** @file charsymbols.h Character (Unicode) symbols. * - * @authors Copyright (c) 2014 Jaakko Keränen + * @authors Copyright © 2014 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/concurrency/guard.h b/doomsday/libdeng2/include/de/concurrency/guard.h index b8f8800604..8a06f06811 100644 --- a/doomsday/libdeng2/include/de/concurrency/guard.h +++ b/doomsday/libdeng2/include/de/concurrency/guard.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2011-2013 Jaakko Keränen + * Copyright © 2011-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/concurrency/lockable.h b/doomsday/libdeng2/include/de/concurrency/lockable.h index 44cb735caa..5a743cd46f 100644 --- a/doomsday/libdeng2/include/de/concurrency/lockable.h +++ b/doomsday/libdeng2/include/de/concurrency/lockable.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/concurrency/readwritelockable.h b/doomsday/libdeng2/include/de/concurrency/readwritelockable.h index 4efbaa393e..b6e3cc6206 100644 --- a/doomsday/libdeng2/include/de/concurrency/readwritelockable.h +++ b/doomsday/libdeng2/include/de/concurrency/readwritelockable.h @@ -1,6 +1,6 @@ /** @file readwritelockable.h Read-write lock. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/concurrency/task.h b/doomsday/libdeng2/include/de/concurrency/task.h index 8b83ee43de..07176646cd 100644 --- a/doomsday/libdeng2/include/de/concurrency/task.h +++ b/doomsday/libdeng2/include/de/concurrency/task.h @@ -1,6 +1,6 @@ /** @file task.h Concurrent task. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/concurrency/taskpool.h b/doomsday/libdeng2/include/de/concurrency/taskpool.h index 77534fdd28..8b6e96b8da 100644 --- a/doomsday/libdeng2/include/de/concurrency/taskpool.h +++ b/doomsday/libdeng2/include/de/concurrency/taskpool.h @@ -1,6 +1,6 @@ /** @file taskpool.h Pool of tasks. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/concurrency/waitable.h b/doomsday/libdeng2/include/de/concurrency/waitable.h index a105d51a6c..82712574e8 100644 --- a/doomsday/libdeng2/include/de/concurrency/waitable.h +++ b/doomsday/libdeng2/include/de/concurrency/waitable.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/core/app.h b/doomsday/libdeng2/include/de/core/app.h index 1d32e342a7..2db4bd8f1c 100644 --- a/doomsday/libdeng2/include/de/core/app.h +++ b/doomsday/libdeng2/include/de/core/app.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2010-2013 Jaakko Keränen + * Copyright © 2010-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -133,7 +133,7 @@ class DENG2_PUBLIC App : DENG2_OBSERVES(Clock, TimeChange) * * @param flags How to/which subsystems to initialize. */ - void initSubsystems(SubsystemInitFlags flags = DefaultSubsystems); + virtual void initSubsystems(SubsystemInitFlags flags = DefaultSubsystems); /** * Adds a system to the application. The order of systems is preserved; the diff --git a/doomsday/libdeng2/include/de/core/asset.h b/doomsday/libdeng2/include/de/core/asset.h index 6402fd9196..eec86d9913 100644 --- a/doomsday/libdeng2/include/de/core/asset.h +++ b/doomsday/libdeng2/include/de/core/asset.h @@ -1,6 +1,6 @@ /** @file asset.h Information about the state of an asset (e.g., resource). * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -35,6 +35,8 @@ namespace de { * OpenGL shader may or may not be compiled and ready to be used, but a native * file in the FileSystem is always considered available (as it can be read via * the native file system at any time). + * + * @ingroup core */ class DENG2_PUBLIC Asset { @@ -81,6 +83,8 @@ class DENG2_PUBLIC Asset * AssetGroup is derived from Asset so it is possible to group assets * together and depend on the groups as a whole. * + * @ingroup core + * * @todo Any better name for this class? */ class DENG2_PUBLIC AssetGroup : public Asset, diff --git a/doomsday/libdeng2/include/de/core/clock.h b/doomsday/libdeng2/include/de/core/clock.h index 55cb0096a8..57bf0bd3a1 100644 --- a/doomsday/libdeng2/include/de/core/clock.h +++ b/doomsday/libdeng2/include/de/core/clock.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/core/commandline.h b/doomsday/libdeng2/include/de/core/commandline.h index 635edce2ea..85c63bfb67 100644 --- a/doomsday/libdeng2/include/de/core/commandline.h +++ b/doomsday/libdeng2/include/de/core/commandline.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/core/config.h b/doomsday/libdeng2/include/de/core/config.h index 5ef3e8d63d..8887bfc155 100644 --- a/doomsday/libdeng2/include/de/core/config.h +++ b/doomsday/libdeng2/include/de/core/config.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -30,18 +30,22 @@ namespace de { class ArrayValue; /** - * Stores the configuration of everything. The application owns a Config. - * The default configuration is produced by executing the .de scripts - * in the config directories. The resulting namespace is serialized for - * storage, and is restored from the serialized version directly before the - * config scripts are run. + * Stores the configuration of everything. * - * The version of the engine is stored in the serialized config namespace. - * This is for actions needed when upgrading: the config script can check - * the previous version and apply changes accordingly. + * The application owns a Config. The default configuration is produced by executing the + * .de scripts in the config directories. The resulting namespace is serialized for + * storage, and is restored from the serialized version directly before the config + * scripts are run. * - * In practice, Config is a specialized script namespace stored in a Record. It - * gets written to the application's persistent data store (persist.pack). + * The version of the engine is stored in the serialized config namespace. This is for + * actions needed when upgrading: the config script can check the previous version and + * apply changes accordingly. + * + * In practice, Config is a specialized script namespace stored in a Record. It gets + * written to the application's persistent data store (persist.pack) using a Refuge. The + * Config is automatically written persistently before being destroyed. + * + * @ingroup core */ class DENG2_PUBLIC Config { @@ -57,11 +61,6 @@ class DENG2_PUBLIC Config */ Config(Path const &path); - /** - * Destructor. The configuration is automatically saved. - */ - virtual ~Config(); - /// Read configuration from files. void read(); diff --git a/doomsday/libdeng2/include/de/core/debuglogsink.h b/doomsday/libdeng2/include/de/core/debuglogsink.h index ec05433eb3..e2f16ba1aa 100644 --- a/doomsday/libdeng2/include/de/core/debuglogsink.h +++ b/doomsday/libdeng2/include/de/core/debuglogsink.h @@ -26,6 +26,7 @@ namespace de { /** + * Log sink that uses QDebug for output. * @ingroup core */ class DebugLogSink : public LogSink diff --git a/doomsday/libdeng2/include/de/core/event.h b/doomsday/libdeng2/include/de/core/event.h index 50820c3454..e10abd5e33 100644 --- a/doomsday/libdeng2/include/de/core/event.h +++ b/doomsday/libdeng2/include/de/core/event.h @@ -26,7 +26,7 @@ namespace de { /** * Base class for events. * - * @ingroup widgets + * @ingroup core */ class DENG2_PUBLIC Event { diff --git a/doomsday/libdeng2/include/de/core/garbage.h b/doomsday/libdeng2/include/de/core/garbage.h index d9ffb94f0a..b2892a45a0 100644 --- a/doomsday/libdeng2/include/de/core/garbage.h +++ b/doomsday/libdeng2/include/de/core/garbage.h @@ -68,7 +68,7 @@ DENG2_PUBLIC int Garbage_IsTrashed(void const *ptr); * @warning Do not call this if there is a chance that the pointer has already * been freed. * - * @param ptr Pointer to memory allocated from the @ref memzone. + * @param ptr Pointer to memory previously put in the trash. */ DENG2_PUBLIC void Garbage_Untrash(void *ptr); diff --git a/doomsday/libdeng2/include/de/core/highperformancetimer.h b/doomsday/libdeng2/include/de/core/highperformancetimer.h index 0413685fc2..da77168fdf 100644 --- a/doomsday/libdeng2/include/de/core/highperformancetimer.h +++ b/doomsday/libdeng2/include/de/core/highperformancetimer.h @@ -1,6 +1,6 @@ /** @file highperformancetimer.h Timer for performance-critical use. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -25,6 +25,7 @@ namespace de { /** * Timer for high-performance use. + * @ingroup core */ class DENG2_PUBLIC HighPerformanceTimer { diff --git a/doomsday/libdeng2/include/de/core/id.h b/doomsday/libdeng2/include/de/core/id.h index fea1c8ab9e..a6b9fc4a48 100644 --- a/doomsday/libdeng2/include/de/core/id.h +++ b/doomsday/libdeng2/include/de/core/id.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -32,6 +32,7 @@ class String; /** * Unique identifier number. Zero is not a valid identifier, as it reserved * for the "no identifier" special case. + * @ingroup core */ class DENG2_PUBLIC Id : public ISerializable, public LogEntry::Arg::Base { diff --git a/doomsday/libdeng2/include/de/core/library.h b/doomsday/libdeng2/include/de/core/library.h index cb80d1745f..8633b8f15b 100644 --- a/doomsday/libdeng2/include/de/core/library.h +++ b/doomsday/libdeng2/include/de/core/library.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/core/log.h b/doomsday/libdeng2/include/de/core/log.h index 7e7e3e7309..bdb15cd8e3 100644 --- a/doomsday/libdeng2/include/de/core/log.h +++ b/doomsday/libdeng2/include/de/core/log.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/core/logbuffer.h b/doomsday/libdeng2/include/de/core/logbuffer.h index 02442b9ddf..d722dc5b08 100644 --- a/doomsday/libdeng2/include/de/core/logbuffer.h +++ b/doomsday/libdeng2/include/de/core/logbuffer.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/core/logfilter.h b/doomsday/libdeng2/include/de/core/logfilter.h index 6666bd13cd..5ca15b7f02 100644 --- a/doomsday/libdeng2/include/de/core/logfilter.h +++ b/doomsday/libdeng2/include/de/core/logfilter.h @@ -1,6 +1,6 @@ /** @file logfilter.h Log entry filter. * - * @authors Copyright (c) 2014 Jaakko Keränen + * @authors Copyright © 2014 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/core/loop.h b/doomsday/libdeng2/include/de/core/loop.h index da68235ebc..a4932244a5 100644 --- a/doomsday/libdeng2/include/de/core/loop.h +++ b/doomsday/libdeng2/include/de/core/loop.h @@ -1,6 +1,6 @@ /** @file loop.h Continually triggered loop. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/core/memorylogsink.h b/doomsday/libdeng2/include/de/core/memorylogsink.h index 1bb7b42ab7..5f386f5f28 100644 --- a/doomsday/libdeng2/include/de/core/memorylogsink.h +++ b/doomsday/libdeng2/include/de/core/memorylogsink.h @@ -1,6 +1,6 @@ /** @file memorylogsink.h Log sink that stores log entries in memory. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -28,6 +28,7 @@ namespace de { /** * Log sink that stores log entries in memory. + * @ingroup core */ class DENG2_PUBLIC MemoryLogSink : public LogSink, public Lockable { diff --git a/doomsday/libdeng2/include/de/core/range.h b/doomsday/libdeng2/include/de/core/range.h index f52b8891f3..d6a363677c 100644 --- a/doomsday/libdeng2/include/de/core/range.h +++ b/doomsday/libdeng2/include/de/core/range.h @@ -1,6 +1,6 @@ /** @file range.h Linear range. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -31,6 +31,8 @@ namespace de { /** * Linear value range. The start point is inclusive while the end point is * exclusive. The end point should be larger in value than the start point. + * + * @ingroup math */ template struct Range diff --git a/doomsday/libdeng2/include/de/core/rectangle.h b/doomsday/libdeng2/include/de/core/rectangle.h index e2adad4108..dcf6ad77e2 100644 --- a/doomsday/libdeng2/include/de/core/rectangle.h +++ b/doomsday/libdeng2/include/de/core/rectangle.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -115,9 +115,12 @@ class Rectangle RectangleType adjusted(CornerVectorType const &tl, CornerVectorType const &br) const { return RectangleType(topLeft + tl, bottomRight + br); } + Rectangle toRectanglei() const { + return Rectangle(topLeft.toVector2i(), bottomRight.toVector2i()); + } Rectangle toRectangleui() const { - Vector2ui tl(duint(de::max(0, topLeft.x)), duint(de::max(0, topLeft.y))); - Vector2ui br(duint(de::max(0, bottomRight.x)), duint(de::max(0, bottomRight.y))); + Vector2ui tl(duint(de::max(Type(0), topLeft.x)), duint(de::max(Type(0), topLeft.y))); + Vector2ui br(duint(de::max(Type(0), bottomRight.x)), duint(de::max(Type(0), bottomRight.y))); return Rectangle(tl, br); } bool contains(Corner const &point) const { diff --git a/doomsday/libdeng2/include/de/core/system.h b/doomsday/libdeng2/include/de/core/system.h index 57458c9331..3f49a17bd6 100644 --- a/doomsday/libdeng2/include/de/core/system.h +++ b/doomsday/libdeng2/include/de/core/system.h @@ -1,6 +1,6 @@ /** @file system.h Base class for application subsystems. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/core/unixinfo.h b/doomsday/libdeng2/include/de/core/unixinfo.h index 3f3ab8761a..d4b21b0407 100644 --- a/doomsday/libdeng2/include/de/core/unixinfo.h +++ b/doomsday/libdeng2/include/de/core/unixinfo.h @@ -41,6 +41,8 @@ namespace de { * Mac OS X, ~/Library/Preferences/) but these are not directly used by * libdeng2. Instead of these, one should use Config (or QSettings) for * platform-independent persistent configuration. + * + * @ingroup core */ class UnixInfo { diff --git a/doomsday/libdeng2/include/de/core/version.h b/doomsday/libdeng2/include/de/core/version.h index 1d2f471621..4f3a5b6e60 100644 --- a/doomsday/libdeng2/include/de/core/version.h +++ b/doomsday/libdeng2/include/de/core/version.h @@ -2,8 +2,8 @@ * @file version.h * Version numbering and labeling for libdeng2. * - * @authors Copyright © 2011-2013 Jaakko Keränen - * @authors Copyright © 2011-2013 Daniel Swanson + * @authors Copyright © 2011-2013 Jaakko Keränen + * @authors Copyright © 2011-2013 Daniel Swanson * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -38,6 +38,8 @@ namespace de { * switches to major version 2, libdeng2 version will be synced with the rest * of the project. Also note that unlike libdeng1, there is only ever three * components in the version (or four, counting the build number). + * + * @ingroup core */ class DENG2_PUBLIC Version { diff --git a/doomsday/libdeng2/include/de/data/accessorvalue.h b/doomsday/libdeng2/include/de/data/accessorvalue.h index f1b29d0b5c..d14e7097fa 100644 --- a/doomsday/libdeng2/include/de/data/accessorvalue.h +++ b/doomsday/libdeng2/include/de/data/accessorvalue.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/archive.h b/doomsday/libdeng2/include/de/data/archive.h index a1acb5b437..72e932214a 100644 --- a/doomsday/libdeng2/include/de/data/archive.h +++ b/doomsday/libdeng2/include/de/data/archive.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/arrayvalue.h b/doomsday/libdeng2/include/de/data/arrayvalue.h index fcd88adefd..f88af229a8 100644 --- a/doomsday/libdeng2/include/de/data/arrayvalue.h +++ b/doomsday/libdeng2/include/de/data/arrayvalue.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/bank.h b/doomsday/libdeng2/include/de/data/bank.h index 4dc540142f..84529de04e 100644 --- a/doomsday/libdeng2/include/de/data/bank.h +++ b/doomsday/libdeng2/include/de/data/bank.h @@ -1,6 +1,6 @@ /** @file bank.h Abstract data bank with multi-tiered caching. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/bitfield.h b/doomsday/libdeng2/include/de/data/bitfield.h index 0bee0a9a39..c566eda821 100644 --- a/doomsday/libdeng2/include/de/data/bitfield.h +++ b/doomsday/libdeng2/include/de/data/bitfield.h @@ -1,6 +1,6 @@ /** @file bitfield.h Array of integers packed together. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -29,6 +29,8 @@ namespace de { /** * Array of integer values packed tightly together. + * + * @ingroup data */ class DENG2_PUBLIC BitField { diff --git a/doomsday/libdeng2/include/de/data/block.h b/doomsday/libdeng2/include/de/data/block.h index 599740be5d..865aab7594 100644 --- a/doomsday/libdeng2/include/de/data/block.h +++ b/doomsday/libdeng2/include/de/data/block.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/blockvalue.h b/doomsday/libdeng2/include/de/data/blockvalue.h index ff1f436c02..108366acae 100644 --- a/doomsday/libdeng2/include/de/data/blockvalue.h +++ b/doomsday/libdeng2/include/de/data/blockvalue.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/byteorder.h b/doomsday/libdeng2/include/de/data/byteorder.h index f75c2f3200..b517243fd7 100644 --- a/doomsday/libdeng2/include/de/data/byteorder.h +++ b/doomsday/libdeng2/include/de/data/byteorder.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/byterefarray.h b/doomsday/libdeng2/include/de/data/byterefarray.h index c534c18530..2ee626255a 100644 --- a/doomsday/libdeng2/include/de/data/byterefarray.h +++ b/doomsday/libdeng2/include/de/data/byterefarray.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2010-2013 Jaakko Keränen + * Copyright © 2010-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -27,7 +27,7 @@ namespace de { /** * Byte array that operates on a pointer-based array. Instances of * ByteRefArray are fixed size: one cannot write past the end of - * the array. + * the array. @ingroup data */ class DENG2_PUBLIC ByteRefArray : public IByteArray { diff --git a/doomsday/libdeng2/include/de/data/bytesubarray.h b/doomsday/libdeng2/include/de/data/bytesubarray.h index 6e6453e1b4..8782d8fa89 100644 --- a/doomsday/libdeng2/include/de/data/bytesubarray.h +++ b/doomsday/libdeng2/include/de/data/bytesubarray.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/counted.h b/doomsday/libdeng2/include/de/data/counted.h index 4563cbe7c8..e31ad6f520 100644 --- a/doomsday/libdeng2/include/de/data/counted.h +++ b/doomsday/libdeng2/include/de/data/counted.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -215,14 +215,14 @@ inline void releaseRef(CountedType const *&ref) { /** * Utility for passing Counted objects as arguments. * - * RefArg enforces the following conventions when used as an method argument type: + * RefArg enforces the following conventions when used as a method argument type: * - If a Counted non-const pointer is given as an argument, it is assumed the * caller has already held a reference and is giving that reference's ownership * away. For instance, when constructing new Counted objects. * - If a Counted const reference is given as an argument, no changes occur in the * object's refcount. * - * The method that uses RefArgs must hold a reference to each object. + * The method that uses RefArgs must hold a reference to each object that is passed in. */ template class RefArg diff --git a/doomsday/libdeng2/include/de/data/date.h b/doomsday/libdeng2/include/de/data/date.h index 41d386dd90..a72e59cba6 100644 --- a/doomsday/libdeng2/include/de/data/date.h +++ b/doomsday/libdeng2/include/de/data/date.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/dictionaryvalue.h b/doomsday/libdeng2/include/de/data/dictionaryvalue.h index 2009cc476f..3e906496fd 100644 --- a/doomsday/libdeng2/include/de/data/dictionaryvalue.h +++ b/doomsday/libdeng2/include/de/data/dictionaryvalue.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/escapeparser.h b/doomsday/libdeng2/include/de/data/escapeparser.h index 2f35407c32..4dcb0cd0c9 100644 --- a/doomsday/libdeng2/include/de/data/escapeparser.h +++ b/doomsday/libdeng2/include/de/data/escapeparser.h @@ -1,6 +1,6 @@ /** @file escapeparser.h Text escape sequence parser. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -26,7 +26,7 @@ namespace de { /** - * Escape sequence parser for text strings. + * Escape sequence parser for text strings. @ingroup data * * @see DENG2_ESC() macro */ diff --git a/doomsday/libdeng2/include/de/data/fifo.h b/doomsday/libdeng2/include/de/data/fifo.h index 8e61244f9e..362137782f 100644 --- a/doomsday/libdeng2/include/de/data/fifo.h +++ b/doomsday/libdeng2/include/de/data/fifo.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/fixedbytearray.h b/doomsday/libdeng2/include/de/data/fixedbytearray.h index 4d5ac39f07..f4ed731d2c 100644 --- a/doomsday/libdeng2/include/de/data/fixedbytearray.h +++ b/doomsday/libdeng2/include/de/data/fixedbytearray.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/huffman.h b/doomsday/libdeng2/include/de/data/huffman.h index 0ba34353ad..036cf0e1e7 100644 --- a/doomsday/libdeng2/include/de/data/huffman.h +++ b/doomsday/libdeng2/include/de/data/huffman.h @@ -1,6 +1,6 @@ /** * @file huffman.h - * Huffman codes. @ingroup math + * Huffman codes. @ingroup data * * @see CLR 2nd Ed, p. 388 * diff --git a/doomsday/libdeng2/include/de/data/iblock.h b/doomsday/libdeng2/include/de/data/iblock.h index a225080f2e..4d17eba690 100644 --- a/doomsday/libdeng2/include/de/data/iblock.h +++ b/doomsday/libdeng2/include/de/data/iblock.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -27,6 +27,8 @@ namespace de { /** * Interface for a resizable block of memory that provides direct access * to the bytes. + * + * @ingroup data */ class IBlock { diff --git a/doomsday/libdeng2/include/de/data/ibytearray.h b/doomsday/libdeng2/include/de/data/ibytearray.h index 51aeea7fb6..58d547b097 100644 --- a/doomsday/libdeng2/include/de/data/ibytearray.h +++ b/doomsday/libdeng2/include/de/data/ibytearray.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/info.h b/doomsday/libdeng2/include/de/data/info.h index 56b0f39d23..e96dff87dd 100644 --- a/doomsday/libdeng2/include/de/data/info.h +++ b/doomsday/libdeng2/include/de/data/info.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2012-2013 Jaakko Keränen + * Copyright © 2012-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -29,6 +29,7 @@ namespace de { /** * Key/value tree. The tree is parsed from the "Snowberry" Info file format. + * @ingroup data * * See the Doomsday Wiki for an example of the syntax: * http://dengine.net/dew/index.php?title=Info diff --git a/doomsday/libdeng2/include/de/data/infobank.h b/doomsday/libdeng2/include/de/data/infobank.h index 63cba3a34b..fbf37d8c37 100644 --- a/doomsday/libdeng2/include/de/data/infobank.h +++ b/doomsday/libdeng2/include/de/data/infobank.h @@ -1,6 +1,6 @@ /** @file infobank.h Abstract Bank read from Info definitions. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -35,6 +35,8 @@ class File; * * InfoBank handles the common plumbing of parsing an Info file and iterating * through it for creating bank sources. + * + * @ingroup data */ class DENG2_PUBLIC InfoBank : public Bank { diff --git a/doomsday/libdeng2/include/de/data/ireadable.h b/doomsday/libdeng2/include/de/data/ireadable.h index 55050adf30..36ca8eadf9 100644 --- a/doomsday/libdeng2/include/de/data/ireadable.h +++ b/doomsday/libdeng2/include/de/data/ireadable.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/iserializable.h b/doomsday/libdeng2/include/de/data/iserializable.h index e91af07a5e..41c7ffb5b8 100644 --- a/doomsday/libdeng2/include/de/data/iserializable.h +++ b/doomsday/libdeng2/include/de/data/iserializable.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/iwritable.h b/doomsday/libdeng2/include/de/data/iwritable.h index 21971c55a3..04832cd020 100644 --- a/doomsday/libdeng2/include/de/data/iwritable.h +++ b/doomsday/libdeng2/include/de/data/iwritable.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/json.h b/doomsday/libdeng2/include/de/data/json.h index 80b5af67fb..d3964962b6 100644 --- a/doomsday/libdeng2/include/de/data/json.h +++ b/doomsday/libdeng2/include/de/data/json.h @@ -1,10 +1,8 @@ -/** @file json.cpp JSON parser. +/** @file json.h JSON parser. * @ingroup data * * Parses JSON and outputs a QVariant with the data. * - * @todo Move this to libdeng2. - * * @authors Copyright © 2012-2013 Jaakko Keränen * * @par License diff --git a/doomsday/libdeng2/include/de/data/nonevalue.h b/doomsday/libdeng2/include/de/data/nonevalue.h index 683eac7d94..5279ed6f6a 100644 --- a/doomsday/libdeng2/include/de/data/nonevalue.h +++ b/doomsday/libdeng2/include/de/data/nonevalue.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/numbervalue.h b/doomsday/libdeng2/include/de/data/numbervalue.h index dd1c5d55b3..c9c26d87dd 100644 --- a/doomsday/libdeng2/include/de/data/numbervalue.h +++ b/doomsday/libdeng2/include/de/data/numbervalue.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/observers.h b/doomsday/libdeng2/include/de/data/observers.h index d811ef2cd6..dee0ea4ade 100644 --- a/doomsday/libdeng2/include/de/data/observers.h +++ b/doomsday/libdeng2/include/de/data/observers.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/path.h b/doomsday/libdeng2/include/de/data/path.h index fc7a8a3d80..4454e80efc 100644 --- a/doomsday/libdeng2/include/de/data/path.h +++ b/doomsday/libdeng2/include/de/data/path.h @@ -1,7 +1,7 @@ /** @file path.h Textual path composed of segments. * - * @author Copyright © 2010-2013 Daniel Swanson - * @author Copyright © 2010-2013 Jaakko Keränen + * @author Copyright © 2010-2013 Daniel Swanson + * @author Copyright © 2010-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/pathtree.h b/doomsday/libdeng2/include/de/data/pathtree.h index 7480c8f6cc..f82128d3ce 100644 --- a/doomsday/libdeng2/include/de/data/pathtree.h +++ b/doomsday/libdeng2/include/de/data/pathtree.h @@ -1,7 +1,7 @@ /** @file pathtree.h Tree of Path/data pairs. * - * @authors Copyright © 2003-2013 Jaakko Keränen - * @authors Copyright © 2009-2013 Daniel Swanson + * @authors Copyright © 2003-2013 Jaakko Keränen + * @authors Copyright © 2009-2013 Daniel Swanson * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/property.h b/doomsday/libdeng2/include/de/data/property.h index 244bdb1423..fc04d818e4 100644 --- a/doomsday/libdeng2/include/de/data/property.h +++ b/doomsday/libdeng2/include/de/data/property.h @@ -1,6 +1,6 @@ /** @file property.h Utility for observable properties. * - * @authors Copyright (c) 2014 Jaakko Keränen + * @authors Copyright © 2014 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -28,6 +28,8 @@ namespace de { * * libdeng2 properties are a utility for conveniently defining observable objects that * automatically send out a notification when their value changes. + * + * @ingroup data */ template class BaseProperty @@ -54,6 +56,8 @@ class BaseProperty * * Unlike script variables, properties deal with native value types and cannot accept * more than one type of value. + * + * @ingroup data */ #define DENG2_DEFINE_PROPERTY(PropName, ValueType) \ class PropName : public de::BaseProperty { \ diff --git a/doomsday/libdeng2/include/de/data/reader.h b/doomsday/libdeng2/include/de/data/reader.h index 4d8d75a539..79984ebe82 100644 --- a/doomsday/libdeng2/include/de/data/reader.h +++ b/doomsday/libdeng2/include/de/data/reader.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/record.h b/doomsday/libdeng2/include/de/data/record.h index 81ae5318d6..625733fcb4 100644 --- a/doomsday/libdeng2/include/de/data/record.h +++ b/doomsday/libdeng2/include/de/data/record.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/recordvalue.h b/doomsday/libdeng2/include/de/data/recordvalue.h index d8fa1f77ea..d162963559 100644 --- a/doomsday/libdeng2/include/de/data/recordvalue.h +++ b/doomsday/libdeng2/include/de/data/recordvalue.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/refuge.h b/doomsday/libdeng2/include/de/data/refuge.h new file mode 100644 index 0000000000..bb590d4e17 --- /dev/null +++ b/doomsday/libdeng2/include/de/data/refuge.h @@ -0,0 +1,77 @@ +/** @file refuge.h Persistent data storage. + * + * @authors Copyright © 2014 Jaakko Keränen + * + * @par License + * LGPL: http://www.gnu.org/licenses/lgpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) 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 Lesser + * General Public License for more details. You should have received a copy of + * the GNU Lesser General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#ifndef LIBDENG2_REFUGE_H +#define LIBDENG2_REFUGE_H + +#include "../Record" + +namespace de { + +/** + * Persistent data storage. + * + * A Record that can be saved and restored to the application's persistent data archive. + * + * @ingroup data + */ +class DENG2_PUBLIC Refuge +{ +public: + /** + * Constructs a Refuge and reads any existing contents from the persistent data + * archive. + * + * @param persistentPath Path of the serialized data file written to the persistent + * data archive. + */ + Refuge(String const &persistentPath); + + /** + * Returns the path of the serialized data in the persistent archive. + */ + String path() const; + + /** + * The contents are automatically written to the persistent archive. + */ + virtual ~Refuge(); + + /** + * Deserialized the contents of the Refuge from the persistent archive. + */ + void read(); + + /** + * Serializes the contents of the Refuge to the persistent archive. + */ + void write() const; + + Time lastWrittenAt() const; + + Record &names(); + + Record const &names() const; + +private: + DENG2_PRIVATE(d) +}; + +} // namespace de + +#endif // LIBDENG2_REFUGE_H diff --git a/doomsday/libdeng2/include/de/data/refvalue.h b/doomsday/libdeng2/include/de/data/refvalue.h index ebd27136ab..cfd9798de1 100644 --- a/doomsday/libdeng2/include/de/data/refvalue.h +++ b/doomsday/libdeng2/include/de/data/refvalue.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/shared.h b/doomsday/libdeng2/include/de/data/shared.h index d0a2813bd0..b14a08509a 100644 --- a/doomsday/libdeng2/include/de/data/shared.h +++ b/doomsday/libdeng2/include/de/data/shared.h @@ -1,6 +1,6 @@ /** @file shared.h Reference-counted, shared object. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/string.h b/doomsday/libdeng2/include/de/data/string.h index 7fc1898318..0a016c2e72 100644 --- a/doomsday/libdeng2/include/de/data/string.h +++ b/doomsday/libdeng2/include/de/data/string.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/stringpool.h b/doomsday/libdeng2/include/de/data/stringpool.h index 209df547f0..5afc2d8356 100644 --- a/doomsday/libdeng2/include/de/data/stringpool.h +++ b/doomsday/libdeng2/include/de/data/stringpool.h @@ -2,8 +2,8 @@ * @file stringpool.h * Pool of strings (case insensitive). * - * @author Copyright © 2010-2013 Daniel Swanson - * @author Copyright © 2012-2013 Jaakko Keränen + * @author Copyright © 2010-2013 Daniel Swanson + * @author Copyright © 2012-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/textvalue.h b/doomsday/libdeng2/include/de/data/textvalue.h index bfaa923c18..320d9ae77d 100644 --- a/doomsday/libdeng2/include/de/data/textvalue.h +++ b/doomsday/libdeng2/include/de/data/textvalue.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/time.h b/doomsday/libdeng2/include/de/data/time.h index 5c636f92a6..4e4589eaa1 100644 --- a/doomsday/libdeng2/include/de/data/time.h +++ b/doomsday/libdeng2/include/de/data/time.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/value.h b/doomsday/libdeng2/include/de/data/value.h index ed6635f979..c96e913184 100644 --- a/doomsday/libdeng2/include/de/data/value.h +++ b/doomsday/libdeng2/include/de/data/value.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/variable.h b/doomsday/libdeng2/include/de/data/variable.h index 70c5cfdf50..0fc5d6e962 100644 --- a/doomsday/libdeng2/include/de/data/variable.h +++ b/doomsday/libdeng2/include/de/data/variable.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/waitablefifo.h b/doomsday/libdeng2/include/de/data/waitablefifo.h index b293a69753..c61fc1dcf7 100644 --- a/doomsday/libdeng2/include/de/data/waitablefifo.h +++ b/doomsday/libdeng2/include/de/data/waitablefifo.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/writer.h b/doomsday/libdeng2/include/de/data/writer.h index 7e07662491..3bc91e815f 100644 --- a/doomsday/libdeng2/include/de/data/writer.h +++ b/doomsday/libdeng2/include/de/data/writer.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/data/zeroed.h b/doomsday/libdeng2/include/de/data/zeroed.h index ebbda4fd53..2afb2dcae4 100644 --- a/doomsday/libdeng2/include/de/data/zeroed.h +++ b/doomsday/libdeng2/include/de/data/zeroed.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -26,6 +26,8 @@ namespace de { /** * Template for primitive types that are automatically initialized to zero. + * + * @ingroup data */ template class Zeroed diff --git a/doomsday/libdeng2/include/de/data/ziparchive.h b/doomsday/libdeng2/include/de/data/ziparchive.h index b86cebb327..784dee14c6 100644 --- a/doomsday/libdeng2/include/de/data/ziparchive.h +++ b/doomsday/libdeng2/include/de/data/ziparchive.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/error.h b/doomsday/libdeng2/include/de/error.h index 3648add25a..a7ccd5f791 100644 --- a/doomsday/libdeng2/include/de/error.h +++ b/doomsday/libdeng2/include/de/error.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/filesys/archiveentryfile.h b/doomsday/libdeng2/include/de/filesys/archiveentryfile.h index e8d508251d..f91dfce44c 100644 --- a/doomsday/libdeng2/include/de/filesys/archiveentryfile.h +++ b/doomsday/libdeng2/include/de/filesys/archiveentryfile.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/filesys/archivefeed.h b/doomsday/libdeng2/include/de/filesys/archivefeed.h index 7af43a71fa..87b7753188 100644 --- a/doomsday/libdeng2/include/de/filesys/archivefeed.h +++ b/doomsday/libdeng2/include/de/filesys/archivefeed.h @@ -1,7 +1,6 @@ -/* - * The Doomsday Engine Project -- libdeng2 +/** @file archivefeed.h Archive Feed. * - * Copyright (c) 2009-2013 Jaakko Keränen + * @author Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/filesys/bytearrayfile.h b/doomsday/libdeng2/include/de/filesys/bytearrayfile.h index 9f8563dd7b..f4a5012e5e 100644 --- a/doomsday/libdeng2/include/de/filesys/bytearrayfile.h +++ b/doomsday/libdeng2/include/de/filesys/bytearrayfile.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2012-2013 Jaakko Keränen + * Copyright © 2012-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/filesys/directoryfeed.h b/doomsday/libdeng2/include/de/filesys/directoryfeed.h index 035e5163c7..64fc27acfb 100644 --- a/doomsday/libdeng2/include/de/filesys/directoryfeed.h +++ b/doomsday/libdeng2/include/de/filesys/directoryfeed.h @@ -1,7 +1,6 @@ -/* - * The Doomsday Engine Project -- libdeng2 +/** @file directoryfeed.h Directory Feed. * - * Copyright (c) 2009-2013 Jaakko Keränen + * @author Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/filesys/feed.h b/doomsday/libdeng2/include/de/filesys/feed.h index 7fdecb91fc..060664a414 100644 --- a/doomsday/libdeng2/include/de/filesys/feed.h +++ b/doomsday/libdeng2/include/de/filesys/feed.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/filesys/file.h b/doomsday/libdeng2/include/de/filesys/file.h index 694c3d9c48..630ca84d55 100644 --- a/doomsday/libdeng2/include/de/filesys/file.h +++ b/doomsday/libdeng2/include/de/filesys/file.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/filesys/filesystem.h b/doomsday/libdeng2/include/de/filesys/filesystem.h index a3e96d8bb0..6b15399f6c 100644 --- a/doomsday/libdeng2/include/de/filesys/filesystem.h +++ b/doomsday/libdeng2/include/de/filesys/filesystem.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/filesys/folder.h b/doomsday/libdeng2/include/de/filesys/folder.h index 1f21a2fc6d..740f8d7de0 100644 --- a/doomsday/libdeng2/include/de/filesys/folder.h +++ b/doomsday/libdeng2/include/de/filesys/folder.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/filesys/libraryfile.h b/doomsday/libdeng2/include/de/filesys/libraryfile.h index 9f6b985440..794e3805a1 100644 --- a/doomsday/libdeng2/include/de/filesys/libraryfile.h +++ b/doomsday/libdeng2/include/de/filesys/libraryfile.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/filesys/nativefile.h b/doomsday/libdeng2/include/de/filesys/nativefile.h index 467ac0b101..ba78212ae9 100644 --- a/doomsday/libdeng2/include/de/filesys/nativefile.h +++ b/doomsday/libdeng2/include/de/filesys/nativefile.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/filesys/nativepath.h b/doomsday/libdeng2/include/de/filesys/nativepath.h index e47d2bb023..5ff72b640f 100644 --- a/doomsday/libdeng2/include/de/filesys/nativepath.h +++ b/doomsday/libdeng2/include/de/filesys/nativepath.h @@ -1,6 +1,4 @@ -/** - * @file nativepath.h - * File paths for the native file system. +/** @file nativepath.h File paths for the native file system. * * @authors Copyright © 2012-2013 Jaakko Keränen * @authors Copyright © 2013 Daniel Swanson @@ -35,6 +33,8 @@ namespace de { * The public interface of NativePath closely mirrors that of String, e.g., * String::fileNamePath(), so that equivalent operations are provided except * with native separator characters. + * + * @ingroup fs */ class DENG2_PUBLIC NativePath : public Path { diff --git a/doomsday/libdeng2/include/de/filesys/packagefolder.h b/doomsday/libdeng2/include/de/filesys/packagefolder.h index 19309fdb59..692270255c 100644 --- a/doomsday/libdeng2/include/de/filesys/packagefolder.h +++ b/doomsday/libdeng2/include/de/filesys/packagefolder.h @@ -1,5 +1,4 @@ /** @file packagefolder.h Folder that hosts a data package archive. - * @ingroup fs * * @authors Copyright © 2012-2013 Jaakko Keränen * @@ -27,6 +26,7 @@ namespace de { /** * Specialized Folder that hosts a data package. + * @ingroup fs * * A @em package is a collection of files packaged into a single unit (possibly * using an Archive). Examples of packages are add-on packages (in various diff --git a/doomsday/libdeng2/include/de/game/game.h b/doomsday/libdeng2/include/de/game/game.h index 6af94ec35e..2d250c49ca 100644 --- a/doomsday/libdeng2/include/de/game/game.h +++ b/doomsday/libdeng2/include/de/game/game.h @@ -1,6 +1,6 @@ -/** @file game.h Base class for games. +/** @file de/game/game.h Base class for games. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/libdeng2.h b/doomsday/libdeng2/include/de/libdeng2.h index 14f914da50..4ddcbc3f46 100644 --- a/doomsday/libdeng2/include/de/libdeng2.h +++ b/doomsday/libdeng2/include/de/libdeng2.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2011-2013 Jaakko Keränen + * Copyright © 2011-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/math.h b/doomsday/libdeng2/include/de/math.h index 065349e81c..c22bf42d33 100644 --- a/doomsday/libdeng2/include/de/math.h +++ b/doomsday/libdeng2/include/de/math.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -33,7 +33,7 @@ namespace de { -#undef PI +//#undef PI ddouble const PI = 3.1415926535897932384626433832795028841971693993751; ddouble const EPSILON = 1.0e-7; dfloat const FLOAT_EPSILON = 1.0e-5f; diff --git a/doomsday/libdeng2/include/de/net/address.h b/doomsday/libdeng2/include/de/net/address.h index 7c84b245c6..fc0ce171ef 100644 --- a/doomsday/libdeng2/include/de/net/address.h +++ b/doomsday/libdeng2/include/de/net/address.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/net/blockpacket.h b/doomsday/libdeng2/include/de/net/blockpacket.h index 3f10fbca67..d554066d9b 100644 --- a/doomsday/libdeng2/include/de/net/blockpacket.h +++ b/doomsday/libdeng2/include/de/net/blockpacket.h @@ -1,6 +1,6 @@ /** @file blockpacket.h Packet that contains a Block. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/net/identifiedpacket.h b/doomsday/libdeng2/include/de/net/identifiedpacket.h index 535e8da579..eea592e765 100644 --- a/doomsday/libdeng2/include/de/net/identifiedpacket.h +++ b/doomsday/libdeng2/include/de/net/identifiedpacket.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project * - * Copyright (c) 2010-2013 Jaakko Keränen + * Copyright © 2010-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/net/listensocket.h b/doomsday/libdeng2/include/de/net/listensocket.h index 6288ccaf5f..cfbfd8ab30 100644 --- a/doomsday/libdeng2/include/de/net/listensocket.h +++ b/doomsday/libdeng2/include/de/net/listensocket.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/net/message.h b/doomsday/libdeng2/include/de/net/message.h index e11352e958..28a35cd565 100644 --- a/doomsday/libdeng2/include/de/net/message.h +++ b/doomsday/libdeng2/include/de/net/message.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/net/packet.h b/doomsday/libdeng2/include/de/net/packet.h index 73dbc78c21..07bc25f1f3 100644 --- a/doomsday/libdeng2/include/de/net/packet.h +++ b/doomsday/libdeng2/include/de/net/packet.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/net/protocol.h b/doomsday/libdeng2/include/de/net/protocol.h index df3f25b112..e03a3e5ed7 100644 --- a/doomsday/libdeng2/include/de/net/protocol.h +++ b/doomsday/libdeng2/include/de/net/protocol.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/net/recordpacket.h b/doomsday/libdeng2/include/de/net/recordpacket.h index 734c3b2b42..66f30f2a60 100644 --- a/doomsday/libdeng2/include/de/net/recordpacket.h +++ b/doomsday/libdeng2/include/de/net/recordpacket.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/net/socket.h b/doomsday/libdeng2/include/de/net/socket.h index cf79f8982d..e977cceabd 100644 --- a/doomsday/libdeng2/include/de/net/socket.h +++ b/doomsday/libdeng2/include/de/net/socket.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/net/transmitter.h b/doomsday/libdeng2/include/de/net/transmitter.h index 67af9bb84e..ae91393fbd 100644 --- a/doomsday/libdeng2/include/de/net/transmitter.h +++ b/doomsday/libdeng2/include/de/net/transmitter.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/arrayexpression.h b/doomsday/libdeng2/include/de/scriptsys/arrayexpression.h index 0721316db4..08edb41897 100644 --- a/doomsday/libdeng2/include/de/scriptsys/arrayexpression.h +++ b/doomsday/libdeng2/include/de/scriptsys/arrayexpression.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/assignstatement.h b/doomsday/libdeng2/include/de/scriptsys/assignstatement.h index 3fd8f824a5..49ddcaefd4 100644 --- a/doomsday/libdeng2/include/de/scriptsys/assignstatement.h +++ b/doomsday/libdeng2/include/de/scriptsys/assignstatement.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/builtinexpression.h b/doomsday/libdeng2/include/de/scriptsys/builtinexpression.h index a612ef930c..61f23b637e 100644 --- a/doomsday/libdeng2/include/de/scriptsys/builtinexpression.h +++ b/doomsday/libdeng2/include/de/scriptsys/builtinexpression.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/catchstatement.h b/doomsday/libdeng2/include/de/scriptsys/catchstatement.h index 1be239b4c7..2cef595c85 100644 --- a/doomsday/libdeng2/include/de/scriptsys/catchstatement.h +++ b/doomsday/libdeng2/include/de/scriptsys/catchstatement.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/compound.h b/doomsday/libdeng2/include/de/scriptsys/compound.h index 32d3af979b..a74b43d302 100644 --- a/doomsday/libdeng2/include/de/scriptsys/compound.h +++ b/doomsday/libdeng2/include/de/scriptsys/compound.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/constantexpression.h b/doomsday/libdeng2/include/de/scriptsys/constantexpression.h index 74286a7492..ef08c8079c 100644 --- a/doomsday/libdeng2/include/de/scriptsys/constantexpression.h +++ b/doomsday/libdeng2/include/de/scriptsys/constantexpression.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/context.h b/doomsday/libdeng2/include/de/scriptsys/context.h index 33527f22b0..f8f1f4c143 100644 --- a/doomsday/libdeng2/include/de/scriptsys/context.h +++ b/doomsday/libdeng2/include/de/scriptsys/context.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/deletestatement.h b/doomsday/libdeng2/include/de/scriptsys/deletestatement.h index b2b44a2083..de95d4b776 100644 --- a/doomsday/libdeng2/include/de/scriptsys/deletestatement.h +++ b/doomsday/libdeng2/include/de/scriptsys/deletestatement.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2012-2013 Jaakko Keränen + * Copyright © 2012-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/dictionaryexpression.h b/doomsday/libdeng2/include/de/scriptsys/dictionaryexpression.h index 91add1e351..3a4f660301 100644 --- a/doomsday/libdeng2/include/de/scriptsys/dictionaryexpression.h +++ b/doomsday/libdeng2/include/de/scriptsys/dictionaryexpression.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/evaluator.h b/doomsday/libdeng2/include/de/scriptsys/evaluator.h index 91e851082f..642730ad23 100644 --- a/doomsday/libdeng2/include/de/scriptsys/evaluator.h +++ b/doomsday/libdeng2/include/de/scriptsys/evaluator.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/expression.h b/doomsday/libdeng2/include/de/scriptsys/expression.h index 9e4cbdfc70..90497fdbdc 100644 --- a/doomsday/libdeng2/include/de/scriptsys/expression.h +++ b/doomsday/libdeng2/include/de/scriptsys/expression.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/expressionstatement.h b/doomsday/libdeng2/include/de/scriptsys/expressionstatement.h index 3349ee6537..64886a3596 100644 --- a/doomsday/libdeng2/include/de/scriptsys/expressionstatement.h +++ b/doomsday/libdeng2/include/de/scriptsys/expressionstatement.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/flowstatement.h b/doomsday/libdeng2/include/de/scriptsys/flowstatement.h index 809dd07a4b..ec39a87b25 100644 --- a/doomsday/libdeng2/include/de/scriptsys/flowstatement.h +++ b/doomsday/libdeng2/include/de/scriptsys/flowstatement.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/forstatement.h b/doomsday/libdeng2/include/de/scriptsys/forstatement.h index cf1a4387f7..0140125081 100644 --- a/doomsday/libdeng2/include/de/scriptsys/forstatement.h +++ b/doomsday/libdeng2/include/de/scriptsys/forstatement.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/function.h b/doomsday/libdeng2/include/de/scriptsys/function.h index 9e6d56fc47..e7788c8c99 100644 --- a/doomsday/libdeng2/include/de/scriptsys/function.h +++ b/doomsday/libdeng2/include/de/scriptsys/function.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/functionstatement.h b/doomsday/libdeng2/include/de/scriptsys/functionstatement.h index 701ee8bea2..9be73c53c8 100644 --- a/doomsday/libdeng2/include/de/scriptsys/functionstatement.h +++ b/doomsday/libdeng2/include/de/scriptsys/functionstatement.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/functionvalue.h b/doomsday/libdeng2/include/de/scriptsys/functionvalue.h index b998ede214..e449c53b86 100644 --- a/doomsday/libdeng2/include/de/scriptsys/functionvalue.h +++ b/doomsday/libdeng2/include/de/scriptsys/functionvalue.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/ifstatement.h b/doomsday/libdeng2/include/de/scriptsys/ifstatement.h index d6c75dab49..b9dd1d07f5 100644 --- a/doomsday/libdeng2/include/de/scriptsys/ifstatement.h +++ b/doomsday/libdeng2/include/de/scriptsys/ifstatement.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/iparser.h b/doomsday/libdeng2/include/de/scriptsys/iparser.h index 9f9bb37f7b..e5bc34cbee 100644 --- a/doomsday/libdeng2/include/de/scriptsys/iparser.h +++ b/doomsday/libdeng2/include/de/scriptsys/iparser.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/lex.h b/doomsday/libdeng2/include/de/scriptsys/lex.h index 686dbb49a8..10fec39b19 100644 --- a/doomsday/libdeng2/include/de/scriptsys/lex.h +++ b/doomsday/libdeng2/include/de/scriptsys/lex.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/module.h b/doomsday/libdeng2/include/de/scriptsys/module.h index 47747b8ba8..1d77024a74 100644 --- a/doomsday/libdeng2/include/de/scriptsys/module.h +++ b/doomsday/libdeng2/include/de/scriptsys/module.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/nameexpression.h b/doomsday/libdeng2/include/de/scriptsys/nameexpression.h index f435fd9d5b..804911a5bd 100644 --- a/doomsday/libdeng2/include/de/scriptsys/nameexpression.h +++ b/doomsday/libdeng2/include/de/scriptsys/nameexpression.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/operator.h b/doomsday/libdeng2/include/de/scriptsys/operator.h index c6a1489a27..e0a85164cb 100644 --- a/doomsday/libdeng2/include/de/scriptsys/operator.h +++ b/doomsday/libdeng2/include/de/scriptsys/operator.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/operatorexpression.h b/doomsday/libdeng2/include/de/scriptsys/operatorexpression.h index d817f91311..b8f5cb3cbe 100644 --- a/doomsday/libdeng2/include/de/scriptsys/operatorexpression.h +++ b/doomsday/libdeng2/include/de/scriptsys/operatorexpression.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/parser.h b/doomsday/libdeng2/include/de/scriptsys/parser.h index f4976b5238..418f0aea9f 100644 --- a/doomsday/libdeng2/include/de/scriptsys/parser.h +++ b/doomsday/libdeng2/include/de/scriptsys/parser.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/printstatement.h b/doomsday/libdeng2/include/de/scriptsys/printstatement.h index e59ee90e0d..833ef87229 100644 --- a/doomsday/libdeng2/include/de/scriptsys/printstatement.h +++ b/doomsday/libdeng2/include/de/scriptsys/printstatement.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/process.h b/doomsday/libdeng2/include/de/scriptsys/process.h index 95ae902033..87317c9fd4 100644 --- a/doomsday/libdeng2/include/de/scriptsys/process.h +++ b/doomsday/libdeng2/include/de/scriptsys/process.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/script.h b/doomsday/libdeng2/include/de/scriptsys/script.h index 11069144db..06550cf8cb 100644 --- a/doomsday/libdeng2/include/de/scriptsys/script.h +++ b/doomsday/libdeng2/include/de/scriptsys/script.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/scriptedinfo.h b/doomsday/libdeng2/include/de/scriptsys/scriptedinfo.h index ba05b16cbe..5ccd07b154 100644 --- a/doomsday/libdeng2/include/de/scriptsys/scriptedinfo.h +++ b/doomsday/libdeng2/include/de/scriptsys/scriptedinfo.h @@ -1,6 +1,6 @@ /** @file scriptedinfo.h Info document tree with script context. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/scriptlex.h b/doomsday/libdeng2/include/de/scriptsys/scriptlex.h index 25b1ddf420..a50312800c 100644 --- a/doomsday/libdeng2/include/de/scriptsys/scriptlex.h +++ b/doomsday/libdeng2/include/de/scriptsys/scriptlex.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/scriptsystem.h b/doomsday/libdeng2/include/de/scriptsys/scriptsystem.h index 73ce944581..c17ccef16f 100644 --- a/doomsday/libdeng2/include/de/scriptsys/scriptsystem.h +++ b/doomsday/libdeng2/include/de/scriptsys/scriptsystem.h @@ -1,6 +1,6 @@ /** @file scriptsystem.h Subsystem for the scripts. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/statement.h b/doomsday/libdeng2/include/de/scriptsys/statement.h index b37b77b359..8422809c1d 100644 --- a/doomsday/libdeng2/include/de/scriptsys/statement.h +++ b/doomsday/libdeng2/include/de/scriptsys/statement.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/tokenbuffer.h b/doomsday/libdeng2/include/de/scriptsys/tokenbuffer.h index 7b482b1ebc..0b2a4991b7 100755 --- a/doomsday/libdeng2/include/de/scriptsys/tokenbuffer.h +++ b/doomsday/libdeng2/include/de/scriptsys/tokenbuffer.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/tokenrange.h b/doomsday/libdeng2/include/de/scriptsys/tokenrange.h index 6dfd5159b7..c142518d58 100644 --- a/doomsday/libdeng2/include/de/scriptsys/tokenrange.h +++ b/doomsday/libdeng2/include/de/scriptsys/tokenrange.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/trystatement.h b/doomsday/libdeng2/include/de/scriptsys/trystatement.h index 101a5f157f..706f8850a6 100644 --- a/doomsday/libdeng2/include/de/scriptsys/trystatement.h +++ b/doomsday/libdeng2/include/de/scriptsys/trystatement.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/scriptsys/whilestatement.h b/doomsday/libdeng2/include/de/scriptsys/whilestatement.h index c26ea0a1cc..e278371884 100644 --- a/doomsday/libdeng2/include/de/scriptsys/whilestatement.h +++ b/doomsday/libdeng2/include/de/scriptsys/whilestatement.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/widgets/constantrule.h b/doomsday/libdeng2/include/de/widgets/constantrule.h index c635b58ba8..9f90931882 100644 --- a/doomsday/libdeng2/include/de/widgets/constantrule.h +++ b/doomsday/libdeng2/include/de/widgets/constantrule.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project * - * Copyright (c) 2011-2013 Jaakko Keränen + * Copyright © 2011-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/widgets/indirectrule.h b/doomsday/libdeng2/include/de/widgets/indirectrule.h index a344b1531d..6c752beb1d 100644 --- a/doomsday/libdeng2/include/de/widgets/indirectrule.h +++ b/doomsday/libdeng2/include/de/widgets/indirectrule.h @@ -1,6 +1,6 @@ /** @file indirectrule.h Indirect rule. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -31,6 +31,8 @@ namespace de { * change dynamically. Anyone relying on the indirect rule will be duly * notified of changes in the source of the indirect rule, without having to * change anything in the existing rule relationships. + * + * @ingroup widgets */ class DENG2_PUBLIC IndirectRule : public Rule { diff --git a/doomsday/libdeng2/include/de/widgets/operatorrule.h b/doomsday/libdeng2/include/de/widgets/operatorrule.h index ad9048292f..a8ade16c32 100644 --- a/doomsday/libdeng2/include/de/widgets/operatorrule.h +++ b/doomsday/libdeng2/include/de/widgets/operatorrule.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project * - * Copyright (c) 2011-2013 Jaakko Keränen + * Copyright © 2011-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/widgets/rule.h b/doomsday/libdeng2/include/de/widgets/rule.h index 155b55e647..7a0a91667e 100644 --- a/doomsday/libdeng2/include/de/widgets/rule.h +++ b/doomsday/libdeng2/include/de/widgets/rule.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project * - * Copyright (c) 2011-2013 Jaakko Keränen + * Copyright © 2011-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/widgets/rulebank.h b/doomsday/libdeng2/include/de/widgets/rulebank.h index 5c9b9ca5cb..e583eae80e 100644 --- a/doomsday/libdeng2/include/de/widgets/rulebank.h +++ b/doomsday/libdeng2/include/de/widgets/rulebank.h @@ -1,6 +1,6 @@ /** @file rulebank.h Bank of length Rules. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -29,6 +29,7 @@ class Record; /** * Bank of Rules where each is identified by a Path. + * @ingroup widgets */ class DENG2_PUBLIC RuleBank : public InfoBank { diff --git a/doomsday/libdeng2/include/de/widgets/rulerectangle.h b/doomsday/libdeng2/include/de/widgets/rulerectangle.h index 9e042c1b1c..433248798c 100644 --- a/doomsday/libdeng2/include/de/widgets/rulerectangle.h +++ b/doomsday/libdeng2/include/de/widgets/rulerectangle.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project * - * Copyright (c) 2011-2013 Jaakko Keränen + * Copyright © 2011-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/widgets/rules.h b/doomsday/libdeng2/include/de/widgets/rules.h index 8cb7b90bc0..fc0edebf55 100644 --- a/doomsday/libdeng2/include/de/widgets/rules.h +++ b/doomsday/libdeng2/include/de/widgets/rules.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project * - * Copyright (c) 2011-2013 Jaakko Keränen + * Copyright © 2011-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/include/de/widgets/scalarrule.h b/doomsday/libdeng2/include/de/widgets/scalarrule.h index 2915e5fe11..e2b62750b7 100644 --- a/doomsday/libdeng2/include/de/widgets/scalarrule.h +++ b/doomsday/libdeng2/include/de/widgets/scalarrule.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project * - * Copyright (c) 2011-2013 Jaakko Keränen + * Copyright © 2011-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/c_wrapper.cpp b/doomsday/libdeng2/src/c_wrapper.cpp index 2daa580cd8..470dc87f46 100644 --- a/doomsday/libdeng2/src/c_wrapper.cpp +++ b/doomsday/libdeng2/src/c_wrapper.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2011-2013 Jaakko Keränen + * Copyright © 2011-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -79,14 +79,15 @@ void App_Log(unsigned int metadata, char const *format, ...) DENG2_ASSERT(nc < sizeof(buffer) - 2); if(!nc) return; + LOG().enter(metadata, buffer); + // Make sure there's a newline in the end. - if(buffer[nc - 1] != '\n') + /*if(buffer[nc - 1] != '\n') { buffer[nc++] = '\n'; buffer[nc] = 0; } - - logFragmentPrinter(metadata, buffer); + logFragmentPrinter(metadata, buffer);*/ } void App_Timer(unsigned int milliseconds, void (*callback)(void)) diff --git a/doomsday/libdeng2/src/concurrency/guard.cpp b/doomsday/libdeng2/src/concurrency/guard.cpp index a5373d21b3..872bbbe9f0 100644 --- a/doomsday/libdeng2/src/concurrency/guard.cpp +++ b/doomsday/libdeng2/src/concurrency/guard.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2011-2013 Jaakko Keränen + * Copyright © 2011-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/concurrency/lockable.cpp b/doomsday/libdeng2/src/concurrency/lockable.cpp index dfc802033e..1fe0772651 100644 --- a/doomsday/libdeng2/src/concurrency/lockable.cpp +++ b/doomsday/libdeng2/src/concurrency/lockable.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/concurrency/readwritelockable.cpp b/doomsday/libdeng2/src/concurrency/readwritelockable.cpp index 8da8a1268a..7be17107d1 100644 --- a/doomsday/libdeng2/src/concurrency/readwritelockable.cpp +++ b/doomsday/libdeng2/src/concurrency/readwritelockable.cpp @@ -1,6 +1,6 @@ /** @file readwritelockable.cpp Read-write lock. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/concurrency/task.cpp b/doomsday/libdeng2/src/concurrency/task.cpp index 43fc4eb191..cebf56ed7e 100644 --- a/doomsday/libdeng2/src/concurrency/task.cpp +++ b/doomsday/libdeng2/src/concurrency/task.cpp @@ -1,6 +1,6 @@ /** @file task.cpp Concurrent task. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/concurrency/taskpool.cpp b/doomsday/libdeng2/src/concurrency/taskpool.cpp index 97786e9d78..852d5e0a2f 100644 --- a/doomsday/libdeng2/src/concurrency/taskpool.cpp +++ b/doomsday/libdeng2/src/concurrency/taskpool.cpp @@ -1,6 +1,6 @@ /** @file taskpool.cpp * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/concurrency/waitable.cpp b/doomsday/libdeng2/src/concurrency/waitable.cpp index 8bfca01a21..9fd05c640c 100644 --- a/doomsday/libdeng2/src/concurrency/waitable.cpp +++ b/doomsday/libdeng2/src/concurrency/waitable.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/core/app.cpp b/doomsday/libdeng2/src/core/app.cpp index 3893c4e86e..65ba957e3d 100644 --- a/doomsday/libdeng2/src/core/app.cpp +++ b/doomsday/libdeng2/src/core/app.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2010-2013 Jaakko Keränen + * Copyright © 2010-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/core/asset.cpp b/doomsday/libdeng2/src/core/asset.cpp index 6008481584..49164ed71f 100644 --- a/doomsday/libdeng2/src/core/asset.cpp +++ b/doomsday/libdeng2/src/core/asset.cpp @@ -1,6 +1,6 @@ /** @file asset.cpp Information about the state of an asset (e.g., resource). * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/core/callbacktimer.cpp b/doomsday/libdeng2/src/core/callbacktimer.cpp index c0717a5f7f..7960b1eb96 100644 --- a/doomsday/libdeng2/src/core/callbacktimer.cpp +++ b/doomsday/libdeng2/src/core/callbacktimer.cpp @@ -2,7 +2,7 @@ * @file callbacktimer.cpp * Internal helper class for making callbacks to C code. * - * @authors Copyright (c) 2012-2013 Jaakko Keränen + * @authors Copyright © 2012-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/core/callbacktimer.h b/doomsday/libdeng2/src/core/callbacktimer.h index acc4b8be18..8a0d238adf 100644 --- a/doomsday/libdeng2/src/core/callbacktimer.h +++ b/doomsday/libdeng2/src/core/callbacktimer.h @@ -2,7 +2,7 @@ * @file callbacktimer.h * Internal helper class for making callbacks to C code. * - * @authors Copyright (c) 2012-2013 Jaakko Keränen + * @authors Copyright © 2012-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/core/commandline.cpp b/doomsday/libdeng2/src/core/commandline.cpp index 8eced2eb56..9068450dab 100644 --- a/doomsday/libdeng2/src/core/commandline.cpp +++ b/doomsday/libdeng2/src/core/commandline.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/core/config.cpp b/doomsday/libdeng2/src/core/config.cpp index 6cb1108ed6..bc1e4f1422 100644 --- a/doomsday/libdeng2/src/core/config.cpp +++ b/doomsday/libdeng2/src/core/config.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html @@ -20,6 +20,7 @@ #include "de/Config" #include "de/App" #include "de/Archive" +#include "de/Refuge" #include "de/Log" #include "de/Folder" #include "de/ArrayValue" @@ -36,8 +37,8 @@ DENG2_PIMPL_NOREF(Config) /// Configuration file name. Path configPath; - /// Path where the configuration is written (inside persist.pack). - Path persistentPath; + /// Saved configuration data (inside persist.pack). + Refuge refuge; /// The configuration namespace. Process config; @@ -46,8 +47,9 @@ DENG2_PIMPL_NOREF(Config) Version oldVersion; Instance(Path const &path) - : configPath(path), - persistentPath("modules/Config") + : configPath(path) + , refuge("modules/Config") + , config(&refuge.names()) {} void setOldVersion(Value const &old) @@ -68,19 +70,6 @@ DENG2_PIMPL_NOREF(Config) Config::Config(Path const &path) : d(new Instance(path)) {} -Config::~Config() -{ - LOG_AS("~Config"); - try - { - write(); - } - catch(Error const &err) - { - LOG_ERROR("") << err.asText(); - } -} - void Config::read() { LOG_AS("Config::read"); @@ -99,8 +88,7 @@ void Config::read() try { // If we already have a saved copy of the config, read it. - Archive const &persist = App::persistentData(); - Reader(persist.entryBlock(d->persistentPath)).withHeader() >> names(); + d->refuge.read(); LOG_DEBUG("Found serialized Config:\n") << names(); @@ -117,15 +105,15 @@ void Config::read() else { // Versions match. - LOG_MSG("") << d->persistentPath << " matches version " << version->asText(); + LOG_MSG("") << d->refuge.path() << " matches version " << version->asText(); } // Also check the timestamp of written config vs. the config script. // If script is newer, it should be rerun. - if(scriptFile.status().modifiedAt > persist.entryStatus(d->persistentPath).modifiedAt) + if(scriptFile.status().modifiedAt > d->refuge.lastWrittenAt()) { LOG_MSG("%s is newer than %s, rerunning the script.") - << d->configPath << d->persistentPath; + << d->configPath << d->refuge.path(); shouldRunScript = true; } } @@ -157,7 +145,7 @@ void Config::read() void Config::write() const { - Writer(App::persistentData().entryBlock(d->persistentPath)).withHeader() << names(); + d->refuge.write(); } Record &Config::names() diff --git a/doomsday/libdeng2/src/core/debuglogsink.cpp b/doomsday/libdeng2/src/core/debuglogsink.cpp index 95889b43e3..d29dc3e82d 100644 --- a/doomsday/libdeng2/src/core/debuglogsink.cpp +++ b/doomsday/libdeng2/src/core/debuglogsink.cpp @@ -1,5 +1,4 @@ /** @file debuglogsink.cpp Log sink that uses QDebug for output. - * @ingroup core * * @authors Copyright © 2013 Jaakko Keränen * diff --git a/doomsday/libdeng2/src/core/filelogsink.cpp b/doomsday/libdeng2/src/core/filelogsink.cpp index e35d5c89c8..455c5a5ad3 100644 --- a/doomsday/libdeng2/src/core/filelogsink.cpp +++ b/doomsday/libdeng2/src/core/filelogsink.cpp @@ -1,5 +1,4 @@ /** @file filelogsink.cpp Log sink that uses a File for output. - * @ingroup core * * @authors Copyright © 2013 Jaakko Keränen * diff --git a/doomsday/libdeng2/src/core/garbage.cpp b/doomsday/libdeng2/src/core/garbage.cpp index 1862e57287..ef0e1e47c6 100644 --- a/doomsday/libdeng2/src/core/garbage.cpp +++ b/doomsday/libdeng2/src/core/garbage.cpp @@ -1,4 +1,4 @@ -/** @file garbage.cpp Garbage collector. @ingroup core +/** @file garbage.cpp Garbage collector. * * @authors Copyright © 2012-2014 Jaakko Keränen * diff --git a/doomsday/libdeng2/src/core/highperformancetimer.cpp b/doomsday/libdeng2/src/core/highperformancetimer.cpp index 8ad6aeab38..63fd0132a1 100644 --- a/doomsday/libdeng2/src/core/highperformancetimer.cpp +++ b/doomsday/libdeng2/src/core/highperformancetimer.cpp @@ -1,6 +1,6 @@ /** @file highperformancetimer.cpp Timer for performance-critical use. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/core/id.cpp b/doomsday/libdeng2/src/core/id.cpp index 4eae7b9320..179948e5e7 100644 --- a/doomsday/libdeng2/src/core/id.cpp +++ b/doomsday/libdeng2/src/core/id.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/core/library.cpp b/doomsday/libdeng2/src/core/library.cpp index 5877f09012..002a8fd3b8 100644 --- a/doomsday/libdeng2/src/core/library.cpp +++ b/doomsday/libdeng2/src/core/library.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/core/log.cpp b/doomsday/libdeng2/src/core/log.cpp index 701c3c81f4..810cc1764e 100644 --- a/doomsday/libdeng2/src/core/log.cpp +++ b/doomsday/libdeng2/src/core/log.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/core/logbuffer.cpp b/doomsday/libdeng2/src/core/logbuffer.cpp index af026543ae..2bd0e6cbbe 100644 --- a/doomsday/libdeng2/src/core/logbuffer.cpp +++ b/doomsday/libdeng2/src/core/logbuffer.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/core/logfilter.cpp b/doomsday/libdeng2/src/core/logfilter.cpp index 51ab28ea3d..fbc9f7765c 100644 --- a/doomsday/libdeng2/src/core/logfilter.cpp +++ b/doomsday/libdeng2/src/core/logfilter.cpp @@ -1,6 +1,6 @@ /** @file logfilter.cpp Log entry filter. * - * @authors Copyright (c) 2014 Jaakko Keränen + * @authors Copyright © 2014 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/core/logsink.cpp b/doomsday/libdeng2/src/core/logsink.cpp index 8b57003a11..a6a39d4c6d 100644 --- a/doomsday/libdeng2/src/core/logsink.cpp +++ b/doomsday/libdeng2/src/core/logsink.cpp @@ -1,5 +1,4 @@ -/** @file logsink.h Sink where log entries are flushed from the LogBuffer. - * @ingroup core +/** @file logsink.cpp Sink where log entries are flushed from the LogBuffer. * * @authors Copyright © 2013 Jaakko Keränen * diff --git a/doomsday/libdeng2/src/core/logtextstyle.h b/doomsday/libdeng2/src/core/logtextstyle.h index cdd48640b4..e0be680131 100644 --- a/doomsday/libdeng2/src/core/logtextstyle.h +++ b/doomsday/libdeng2/src/core/logtextstyle.h @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/core/loop.cpp b/doomsday/libdeng2/src/core/loop.cpp index 162a75111e..5bac69d247 100644 --- a/doomsday/libdeng2/src/core/loop.cpp +++ b/doomsday/libdeng2/src/core/loop.cpp @@ -1,6 +1,6 @@ /** @file loop.cpp * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/core/memorylogsink.cpp b/doomsday/libdeng2/src/core/memorylogsink.cpp index 35b30ecff2..559ee9b5e9 100644 --- a/doomsday/libdeng2/src/core/memorylogsink.cpp +++ b/doomsday/libdeng2/src/core/memorylogsink.cpp @@ -1,6 +1,6 @@ /** @file memorylogsink.h Log sink that stores log entries in memory. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/core/monospacelogsinkformatter.cpp b/doomsday/libdeng2/src/core/monospacelogsinkformatter.cpp index 5d0a7fe3fa..d11f5b7ac8 100644 --- a/doomsday/libdeng2/src/core/monospacelogsinkformatter.cpp +++ b/doomsday/libdeng2/src/core/monospacelogsinkformatter.cpp @@ -1,5 +1,4 @@ /** @file monospacelogsinkformatter.cpp Fixed-width log entry formatter. - * @ingroup core * * @authors Copyright © 2013 Jaakko Keränen * @@ -106,7 +105,7 @@ struct TabFiller } else { - stop = ln.at(i).toAscii() - 'a'; + stop = ln.at(i).toLatin1() - 'a'; } maxStop = max(stop, maxStop); } @@ -141,7 +140,7 @@ struct TabFiller // continue to the tab-replacing phase. goto replaceTabs; } - if(ln.at(i) == '+' || ln.at(i).toAscii() - 'a' == stop) + if(ln.at(i) == '+' || ln.at(i).toLatin1() - 'a' == stop) { // This is it. tabWidth = max(tabWidth, w); @@ -174,7 +173,7 @@ struct TabFiller resetAt = Vector2i(idx, i - 1); goto nextStop; } - if(ln.at(i) == '+' || ln.at(i).toAscii() - 'a' == stop) + if(ln.at(i) == '+' || ln.at(i).toLatin1() - 'a' == stop) { // Replace this stop with spaces. ln.remove(--i, 2); diff --git a/doomsday/libdeng2/src/core/system.cpp b/doomsday/libdeng2/src/core/system.cpp index 6409f1810d..9fbfce91a1 100644 --- a/doomsday/libdeng2/src/core/system.cpp +++ b/doomsday/libdeng2/src/core/system.cpp @@ -1,6 +1,6 @@ /** @file system.cpp Base class for application subsystems. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/core/textstreamlogsink.cpp b/doomsday/libdeng2/src/core/textstreamlogsink.cpp index 6a8beb7803..9e39ada705 100644 --- a/doomsday/libdeng2/src/core/textstreamlogsink.cpp +++ b/doomsday/libdeng2/src/core/textstreamlogsink.cpp @@ -1,5 +1,4 @@ /** @file textstreamlogsink.cpp Log sink that uses a QTextStream for output. - * @ingroup core * * @authors Copyright © 2013 Jaakko Keränen * diff --git a/doomsday/libdeng2/src/data/accessorvalue.cpp b/doomsday/libdeng2/src/data/accessorvalue.cpp index 021a2be7a4..152813b01b 100644 --- a/doomsday/libdeng2/src/data/accessorvalue.cpp +++ b/doomsday/libdeng2/src/data/accessorvalue.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/archive.cpp b/doomsday/libdeng2/src/data/archive.cpp index 9dac927143..56f4fbe34e 100644 --- a/doomsday/libdeng2/src/data/archive.cpp +++ b/doomsday/libdeng2/src/data/archive.cpp @@ -1,6 +1,6 @@ /** @file archive.cpp Collection of named memory blocks stored inside a byte array. * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/arrayvalue.cpp b/doomsday/libdeng2/src/data/arrayvalue.cpp index a57a2c8fb9..713edbe033 100644 --- a/doomsday/libdeng2/src/data/arrayvalue.cpp +++ b/doomsday/libdeng2/src/data/arrayvalue.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/bank.cpp b/doomsday/libdeng2/src/data/bank.cpp index 773b1d4495..ceadea9b78 100644 --- a/doomsday/libdeng2/src/data/bank.cpp +++ b/doomsday/libdeng2/src/data/bank.cpp @@ -1,6 +1,6 @@ /** @file bank.cpp Abstract data bank with multi-tiered caching. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/binarytree_wrapper.cpp b/doomsday/libdeng2/src/data/binarytree_wrapper.cpp index 9d0233f18a..c6a72eacbf 100644 --- a/doomsday/libdeng2/src/data/binarytree_wrapper.cpp +++ b/doomsday/libdeng2/src/data/binarytree_wrapper.cpp @@ -1,5 +1,4 @@ -/** @file binarytree_wrapper.cpp Binary tree (C wrapper). - * @ingroup data +/** @file binarytree_wrapper.cpp Binary tree (C wrapper). * * @authors Copyright © 2009-2013 Daniel Swanson * @authors Copyright © 2012-2013 Jaakko Keränen diff --git a/doomsday/libdeng2/src/data/bitfield.cpp b/doomsday/libdeng2/src/data/bitfield.cpp index 33c972459b..1e89654b69 100644 --- a/doomsday/libdeng2/src/data/bitfield.cpp +++ b/doomsday/libdeng2/src/data/bitfield.cpp @@ -1,6 +1,6 @@ /** @file bitfield.cpp Array of integers packed together. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/block.cpp b/doomsday/libdeng2/src/data/block.cpp index 05d5420d83..8bfe8145b5 100644 --- a/doomsday/libdeng2/src/data/block.cpp +++ b/doomsday/libdeng2/src/data/block.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/blockvalue.cpp b/doomsday/libdeng2/src/data/blockvalue.cpp index 4263d7a4d5..71bfa764a1 100644 --- a/doomsday/libdeng2/src/data/blockvalue.cpp +++ b/doomsday/libdeng2/src/data/blockvalue.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/byteorder.cpp b/doomsday/libdeng2/src/data/byteorder.cpp index b1fc9c75f5..3d6c63e0dc 100644 --- a/doomsday/libdeng2/src/data/byteorder.cpp +++ b/doomsday/libdeng2/src/data/byteorder.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/byterefarray.cpp b/doomsday/libdeng2/src/data/byterefarray.cpp index 3f432dfd9a..ea64a72f53 100644 --- a/doomsday/libdeng2/src/data/byterefarray.cpp +++ b/doomsday/libdeng2/src/data/byterefarray.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2010-2013 Jaakko Keränen + * Copyright © 2010-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/bytesubarray.cpp b/doomsday/libdeng2/src/data/bytesubarray.cpp index fd6dbf6378..140fdbe04f 100644 --- a/doomsday/libdeng2/src/data/bytesubarray.cpp +++ b/doomsday/libdeng2/src/data/bytesubarray.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/counted.cpp b/doomsday/libdeng2/src/data/counted.cpp index ae16d00177..fd206fd840 100644 --- a/doomsday/libdeng2/src/data/counted.cpp +++ b/doomsday/libdeng2/src/data/counted.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/date.cpp b/doomsday/libdeng2/src/data/date.cpp index 815d93efc4..a41a0bc5a6 100644 --- a/doomsday/libdeng2/src/data/date.cpp +++ b/doomsday/libdeng2/src/data/date.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/dictionaryvalue.cpp b/doomsday/libdeng2/src/data/dictionaryvalue.cpp index 6852476225..18e71b3691 100644 --- a/doomsday/libdeng2/src/data/dictionaryvalue.cpp +++ b/doomsday/libdeng2/src/data/dictionaryvalue.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/escapeparser.cpp b/doomsday/libdeng2/src/data/escapeparser.cpp index fb345117d0..2305015a96 100644 --- a/doomsday/libdeng2/src/data/escapeparser.cpp +++ b/doomsday/libdeng2/src/data/escapeparser.cpp @@ -1,6 +1,6 @@ /** @file escapeparser.cpp * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/fixedbytearray.cpp b/doomsday/libdeng2/src/data/fixedbytearray.cpp index ef8ceac3a1..62b5974793 100644 --- a/doomsday/libdeng2/src/data/fixedbytearray.cpp +++ b/doomsday/libdeng2/src/data/fixedbytearray.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/info.cpp b/doomsday/libdeng2/src/data/info.cpp index 6a7d897bab..55111ff0a9 100644 --- a/doomsday/libdeng2/src/data/info.cpp +++ b/doomsday/libdeng2/src/data/info.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2012-2013 Jaakko Keränen + * Copyright © 2012-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/infobank.cpp b/doomsday/libdeng2/src/data/infobank.cpp index a56f3df56e..1162d492c8 100644 --- a/doomsday/libdeng2/src/data/infobank.cpp +++ b/doomsday/libdeng2/src/data/infobank.cpp @@ -1,6 +1,6 @@ /** @file infobank.cpp Abstract Bank read from Info definitions. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/json.cpp b/doomsday/libdeng2/src/data/json.cpp index dd3b81fb3a..d782d9a439 100644 --- a/doomsday/libdeng2/src/data/json.cpp +++ b/doomsday/libdeng2/src/data/json.cpp @@ -1,5 +1,4 @@ /** @file json.cpp JSON parser. - * @ingroup data * * Parses JSON and outputs a QVariant with the data. * diff --git a/doomsday/libdeng2/src/data/nonevalue.cpp b/doomsday/libdeng2/src/data/nonevalue.cpp index de7e0e4598..04d9802fb3 100644 --- a/doomsday/libdeng2/src/data/nonevalue.cpp +++ b/doomsday/libdeng2/src/data/nonevalue.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/numbervalue.cpp b/doomsday/libdeng2/src/data/numbervalue.cpp index 60a54188b6..6e23583d2c 100644 --- a/doomsday/libdeng2/src/data/numbervalue.cpp +++ b/doomsday/libdeng2/src/data/numbervalue.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/path.cpp b/doomsday/libdeng2/src/data/path.cpp index 0473eca504..ef89062bf6 100644 --- a/doomsday/libdeng2/src/data/path.cpp +++ b/doomsday/libdeng2/src/data/path.cpp @@ -1,7 +1,7 @@ /** @file path.cpp Textual path composed of segments. * - * @author Copyright © 2010-2013 Daniel Swanson - * @author Copyright © 2010-2013 Jaakko Keränen + * @author Copyright © 2010-2013 Daniel Swanson + * @author Copyright © 2010-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/pathtree.cpp b/doomsday/libdeng2/src/data/pathtree.cpp index d86017b30c..0a1acb2666 100644 --- a/doomsday/libdeng2/src/data/pathtree.cpp +++ b/doomsday/libdeng2/src/data/pathtree.cpp @@ -1,7 +1,7 @@ /** @file pathtree.cpp Tree of Path/data pairs. * - * @authors Copyright © 2003-2013 Jaakko Keränen - * @authors Copyright © 2006-2013 Daniel Swanson + * @authors Copyright © 2003-2013 Jaakko Keränen + * @authors Copyright © 2006-2013 Daniel Swanson * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/pathtreenode.cpp b/doomsday/libdeng2/src/data/pathtreenode.cpp index bae3cb700f..677e2ba335 100644 --- a/doomsday/libdeng2/src/data/pathtreenode.cpp +++ b/doomsday/libdeng2/src/data/pathtreenode.cpp @@ -1,7 +1,7 @@ /** @file pathtreenode.cpp PathTree::Node implementation. * - * @authors Copyright © 2003-2013 Jaakko Keränen - * @authors Copyright © 2006-2013 Daniel Swanson + * @authors Copyright © 2003-2013 Jaakko Keränen + * @authors Copyright © 2006-2013 Daniel Swanson * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/reader.cpp b/doomsday/libdeng2/src/data/reader.cpp index cb48018512..a2131caed1 100644 --- a/doomsday/libdeng2/src/data/reader.cpp +++ b/doomsday/libdeng2/src/data/reader.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/record.cpp b/doomsday/libdeng2/src/data/record.cpp index b8715c51a2..95d9bc4ab7 100644 --- a/doomsday/libdeng2/src/data/record.cpp +++ b/doomsday/libdeng2/src/data/record.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/recordvalue.cpp b/doomsday/libdeng2/src/data/recordvalue.cpp index 9196e861a6..e92bed2f15 100644 --- a/doomsday/libdeng2/src/data/recordvalue.cpp +++ b/doomsday/libdeng2/src/data/recordvalue.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/refuge.cpp b/doomsday/libdeng2/src/data/refuge.cpp new file mode 100644 index 0000000000..94ff7a1ab4 --- /dev/null +++ b/doomsday/libdeng2/src/data/refuge.cpp @@ -0,0 +1,92 @@ +/** @file refuge.cpp Persistent data storage. + * + * @authors Copyright © 2014 Jaakko Keränen + * + * @par License + * LGPL: http://www.gnu.org/licenses/lgpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) 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 Lesser + * General Public License for more details. You should have received a copy of + * the GNU Lesser General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#include "de/Refuge" +#include "de/App" +#include "de/Archive" +#include "de/Reader" +#include "de/Writer" + +namespace de { + +DENG2_PIMPL_NOREF(Refuge) +{ + String persistentPath; + Record names; +}; + +Refuge::Refuge(String const &persistentPath) : d(new Instance) +{ + d->persistentPath = persistentPath; + + try + { + read(); + } + catch(Error const &er) + { + LOG_AS("Refuge"); + LOG_RES_VERBOSE("\"%s\" could not be read: %s") << persistentPath << er.asText(); + } +} + +String Refuge::path() const +{ + return d->persistentPath; +} + +Refuge::~Refuge() +{ + // Musn't throw exceptions from destructor... + try + { + write(); + } + catch(Error const &er) + { + LOG_AS("~Refuge"); + LOG_ERROR("\"%s\" could not be written: %s") << d->persistentPath << er.asText(); + } +} + +void Refuge::read() +{ + Reader(App::persistentData().entryBlock(d->persistentPath)).withHeader() >> d->names; +} + +void Refuge::write() const +{ + Writer(App::persistentData().entryBlock(d->persistentPath)).withHeader() << d->names; +} + +Time Refuge::lastWrittenAt() const +{ + return App::persistentData().entryStatus(d->persistentPath).modifiedAt; +} + +Record &Refuge::names() +{ + return d->names; +} + +Record const &Refuge::names() const +{ + return d->names; +} + +} // namespace de diff --git a/doomsday/libdeng2/src/data/refvalue.cpp b/doomsday/libdeng2/src/data/refvalue.cpp index c02a05372a..ef1d8de1cb 100644 --- a/doomsday/libdeng2/src/data/refvalue.cpp +++ b/doomsday/libdeng2/src/data/refvalue.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/string.cpp b/doomsday/libdeng2/src/data/string.cpp index bdd3f34391..b7165e1475 100644 --- a/doomsday/libdeng2/src/data/string.cpp +++ b/doomsday/libdeng2/src/data/string.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/stringpool.cpp b/doomsday/libdeng2/src/data/stringpool.cpp index 8694f57a93..109179d013 100644 --- a/doomsday/libdeng2/src/data/stringpool.cpp +++ b/doomsday/libdeng2/src/data/stringpool.cpp @@ -2,8 +2,8 @@ * @file stringpool.cpp * Pool of strings (case insensitive). * - * @author Copyright © 2010-2013 Daniel Swanson - * @author Copyright © 2012-2013 Jaakko Keränen + * @author Copyright © 2010-2013 Daniel Swanson + * @author Copyright © 2012-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/textvalue.cpp b/doomsday/libdeng2/src/data/textvalue.cpp index f1a8e3980d..f806cfd6bb 100644 --- a/doomsday/libdeng2/src/data/textvalue.cpp +++ b/doomsday/libdeng2/src/data/textvalue.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/time.cpp b/doomsday/libdeng2/src/data/time.cpp index 1f46b5cf65..773e162935 100644 --- a/doomsday/libdeng2/src/data/time.cpp +++ b/doomsday/libdeng2/src/data/time.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/value.cpp b/doomsday/libdeng2/src/data/value.cpp index 29f29332f3..f128642e08 100644 --- a/doomsday/libdeng2/src/data/value.cpp +++ b/doomsday/libdeng2/src/data/value.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/variable.cpp b/doomsday/libdeng2/src/data/variable.cpp index b6e6be7f8e..ef1dd483f8 100644 --- a/doomsday/libdeng2/src/data/variable.cpp +++ b/doomsday/libdeng2/src/data/variable.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/writer.cpp b/doomsday/libdeng2/src/data/writer.cpp index 14635acea2..a41f8e436c 100644 --- a/doomsday/libdeng2/src/data/writer.cpp +++ b/doomsday/libdeng2/src/data/writer.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/data/ziparchive.cpp b/doomsday/libdeng2/src/data/ziparchive.cpp index 35c89630fe..270bca8d9f 100644 --- a/doomsday/libdeng2/src/data/ziparchive.cpp +++ b/doomsday/libdeng2/src/data/ziparchive.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/filesys/archiveentryfile.cpp b/doomsday/libdeng2/src/filesys/archiveentryfile.cpp index c5f877918c..a348b8ab1f 100644 --- a/doomsday/libdeng2/src/filesys/archiveentryfile.cpp +++ b/doomsday/libdeng2/src/filesys/archiveentryfile.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/filesys/archivefeed.cpp b/doomsday/libdeng2/src/filesys/archivefeed.cpp index c2ac250775..5757ae626a 100644 --- a/doomsday/libdeng2/src/filesys/archivefeed.cpp +++ b/doomsday/libdeng2/src/filesys/archivefeed.cpp @@ -1,8 +1,7 @@ /** @file archivefeed.cpp Archive Feed. - * @ingroup fs * - * @author Copyright © 2009-2013 Jaakko Keränen - * @author Copyright © 2013 Daniel Swanson + * @author Copyright © 2009-2013 Jaakko Keränen + * @author Copyright © 2013 Daniel Swanson * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/filesys/bytearrayfile.cpp b/doomsday/libdeng2/src/filesys/bytearrayfile.cpp index f0552a6415..5f25490b8d 100644 --- a/doomsday/libdeng2/src/filesys/bytearrayfile.cpp +++ b/doomsday/libdeng2/src/filesys/bytearrayfile.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2012-2013 Jaakko Keränen + * Copyright © 2012-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/filesys/directoryfeed.cpp b/doomsday/libdeng2/src/filesys/directoryfeed.cpp index 54800c223d..987ed2f793 100644 --- a/doomsday/libdeng2/src/filesys/directoryfeed.cpp +++ b/doomsday/libdeng2/src/filesys/directoryfeed.cpp @@ -1,8 +1,7 @@ /** @file directoryfeed.cpp Directory Feed. - * @ingroup fs * - * @author Copyright © 2009-2013 Jaakko Keränen - * @author Copyright © 2013 Daniel Swanson + * @author Copyright © 2009-2013 Jaakko Keränen + * @author Copyright © 2013 Daniel Swanson * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/filesys/feed.cpp b/doomsday/libdeng2/src/filesys/feed.cpp index 8bfc8779ed..9b5eed52e6 100644 --- a/doomsday/libdeng2/src/filesys/feed.cpp +++ b/doomsday/libdeng2/src/filesys/feed.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/filesys/file.cpp b/doomsday/libdeng2/src/filesys/file.cpp index 7c80c97c39..79dda257b4 100644 --- a/doomsday/libdeng2/src/filesys/file.cpp +++ b/doomsday/libdeng2/src/filesys/file.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/filesys/filesystem.cpp b/doomsday/libdeng2/src/filesys/filesystem.cpp index 3a7ec7a690..06991f8bee 100644 --- a/doomsday/libdeng2/src/filesys/filesystem.cpp +++ b/doomsday/libdeng2/src/filesys/filesystem.cpp @@ -1,7 +1,7 @@ /** @file filesystem.cpp File System. * - * @author Copyright © 2009-2013 Jaakko Keränen - * @author Copyright © 2013 Daniel Swanson + * @author Copyright © 2009-2013 Jaakko Keränen + * @author Copyright © 2013 Daniel Swanson * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/filesys/folder.cpp b/doomsday/libdeng2/src/filesys/folder.cpp index f3f84d791d..bfd4b25340 100644 --- a/doomsday/libdeng2/src/filesys/folder.cpp +++ b/doomsday/libdeng2/src/filesys/folder.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/filesys/libraryfile.cpp b/doomsday/libdeng2/src/filesys/libraryfile.cpp index f87cda6161..c069b9533a 100644 --- a/doomsday/libdeng2/src/filesys/libraryfile.cpp +++ b/doomsday/libdeng2/src/filesys/libraryfile.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/filesys/nativefile.cpp b/doomsday/libdeng2/src/filesys/nativefile.cpp index 9de32813e9..de5baa25a6 100644 --- a/doomsday/libdeng2/src/filesys/nativefile.cpp +++ b/doomsday/libdeng2/src/filesys/nativefile.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/filesys/packagefolder.cpp b/doomsday/libdeng2/src/filesys/packagefolder.cpp index ca9c499b43..f771fcbc6d 100644 --- a/doomsday/libdeng2/src/filesys/packagefolder.cpp +++ b/doomsday/libdeng2/src/filesys/packagefolder.cpp @@ -1,5 +1,4 @@ /** @file packagefolder.cpp Folder that hosts a data package archive. - * @ingroup fs * * @authors Copyright © 2012-2013 Jaakko Keränen * diff --git a/doomsday/libdeng2/src/game/game.cpp b/doomsday/libdeng2/src/game/game.cpp index f045a0d4e1..0914497629 100644 --- a/doomsday/libdeng2/src/game/game.cpp +++ b/doomsday/libdeng2/src/game/game.cpp @@ -1,6 +1,6 @@ /** @file game.cpp Base class for games. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/net/address.cpp b/doomsday/libdeng2/src/net/address.cpp index b31c6f95bc..f0980d9362 100644 --- a/doomsday/libdeng2/src/net/address.cpp +++ b/doomsday/libdeng2/src/net/address.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/net/blockpacket.cpp b/doomsday/libdeng2/src/net/blockpacket.cpp index 2c1350a64d..b8d0aeea0e 100644 --- a/doomsday/libdeng2/src/net/blockpacket.cpp +++ b/doomsday/libdeng2/src/net/blockpacket.cpp @@ -1,6 +1,6 @@ /** @file blockpacket.cpp Packet that contains a block of data. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/net/doorman.h b/doomsday/libdeng2/src/net/doorman.h deleted file mode 100644 index 9eb5675509..0000000000 --- a/doomsday/libdeng2/src/net/doorman.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2011 Jaakko Keränen - * - * @par License - * LGPL: http://www.gnu.org/licenses/lgpl.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 3 of the License, or (at your - * option) 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 Lesser - * General Public License for more details. You should have received a copy of - * the GNU Lesser General Public License along with this program; if not, see: - * http://www.gnu.org/licenses - */ - -#ifndef LIBDENG2_DOORMAN_H -#define LIBDENG2_DOORMAN_H - -#include "de/libdeng2.h" -#include -#include - -namespace de { -namespace internal { - -/** - * Overrides QTcpServer's handling of incoming connections. - */ -class TcpServer : public QTcpServer -{ - Q_OBJECT - -public: - TcpServer() : QTcpServer() {} - - void incomingConnection(int handle) { - qDebug() << "TcpServer: Incoming connection available, handle" << handle; - emit takeIncomingSocketDesc(handle); - } - -signals: - void takeIncomingSocketDesc(int handle); -}; - -/** - * Separate thread for listening to incoming connections. This is needed - * when the application's main event loop is not available. - */ -class Doorman : public QThread -{ - Q_OBJECT - -public: - Doorman(duint16 port) : _port(port), _socket(0), _shouldStop(false) {} - void run(); - void stopAndWait() { - _shouldStop = true; - wait(); - } - -signals: - void incomingSocketDesc(int handle); - -private: - duint16 _port; - TcpServer *_socket; - volatile bool _shouldStop; -}; - -} // namespace internal -} // namespace de - -#endif // LIBDENG2_DOORMAN_H diff --git a/doomsday/libdeng2/src/net/identifiedpacket.cpp b/doomsday/libdeng2/src/net/identifiedpacket.cpp index aab0f61ac5..4aebfe27a6 100644 --- a/doomsday/libdeng2/src/net/identifiedpacket.cpp +++ b/doomsday/libdeng2/src/net/identifiedpacket.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project * - * Copyright (c) 2010-2013 Jaakko Keränen + * Copyright © 2010-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/net/listensocket.cpp b/doomsday/libdeng2/src/net/listensocket.cpp index 555ad6a451..18780510b2 100644 --- a/doomsday/libdeng2/src/net/listensocket.cpp +++ b/doomsday/libdeng2/src/net/listensocket.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/net/message.cpp b/doomsday/libdeng2/src/net/message.cpp index a1687feb4f..2d90d52b45 100644 --- a/doomsday/libdeng2/src/net/message.cpp +++ b/doomsday/libdeng2/src/net/message.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/net/packet.cpp b/doomsday/libdeng2/src/net/packet.cpp index f14599310c..662c6d7da4 100644 --- a/doomsday/libdeng2/src/net/packet.cpp +++ b/doomsday/libdeng2/src/net/packet.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/net/protocol.cpp b/doomsday/libdeng2/src/net/protocol.cpp index 342c490b3b..94e782c248 100644 --- a/doomsday/libdeng2/src/net/protocol.cpp +++ b/doomsday/libdeng2/src/net/protocol.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/net/recordpacket.cpp b/doomsday/libdeng2/src/net/recordpacket.cpp index 4857212847..81ad2d1693 100644 --- a/doomsday/libdeng2/src/net/recordpacket.cpp +++ b/doomsday/libdeng2/src/net/recordpacket.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/net/socket.cpp b/doomsday/libdeng2/src/net/socket.cpp index b71895dfb3..018aeb4ef4 100644 --- a/doomsday/libdeng2/src/net/socket.cpp +++ b/doomsday/libdeng2/src/net/socket.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/net/transmitter.cpp b/doomsday/libdeng2/src/net/transmitter.cpp index e3010d9876..54557cd581 100644 --- a/doomsday/libdeng2/src/net/transmitter.cpp +++ b/doomsday/libdeng2/src/net/transmitter.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/arrayexpression.cpp b/doomsday/libdeng2/src/scriptsys/arrayexpression.cpp index 5ae27bb00d..13c9774816 100644 --- a/doomsday/libdeng2/src/scriptsys/arrayexpression.cpp +++ b/doomsday/libdeng2/src/scriptsys/arrayexpression.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/assignstatement.cpp b/doomsday/libdeng2/src/scriptsys/assignstatement.cpp index 739c884a01..845c560710 100644 --- a/doomsday/libdeng2/src/scriptsys/assignstatement.cpp +++ b/doomsday/libdeng2/src/scriptsys/assignstatement.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/builtinexpression.cpp b/doomsday/libdeng2/src/scriptsys/builtinexpression.cpp index 4b65a803af..644a6220e3 100644 --- a/doomsday/libdeng2/src/scriptsys/builtinexpression.cpp +++ b/doomsday/libdeng2/src/scriptsys/builtinexpression.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/catchstatement.cpp b/doomsday/libdeng2/src/scriptsys/catchstatement.cpp index 35d1124110..343d5e0086 100644 --- a/doomsday/libdeng2/src/scriptsys/catchstatement.cpp +++ b/doomsday/libdeng2/src/scriptsys/catchstatement.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/compound.cpp b/doomsday/libdeng2/src/scriptsys/compound.cpp index 90b828c0ca..a675591cbb 100644 --- a/doomsday/libdeng2/src/scriptsys/compound.cpp +++ b/doomsday/libdeng2/src/scriptsys/compound.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/constantexpression.cpp b/doomsday/libdeng2/src/scriptsys/constantexpression.cpp index 149b098dd0..615b7171ec 100644 --- a/doomsday/libdeng2/src/scriptsys/constantexpression.cpp +++ b/doomsday/libdeng2/src/scriptsys/constantexpression.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/context.cpp b/doomsday/libdeng2/src/scriptsys/context.cpp index 1c62d3c8f1..05120e5bbb 100644 --- a/doomsday/libdeng2/src/scriptsys/context.cpp +++ b/doomsday/libdeng2/src/scriptsys/context.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/deletestatement.cpp b/doomsday/libdeng2/src/scriptsys/deletestatement.cpp index 51285e50d0..dc117689a0 100644 --- a/doomsday/libdeng2/src/scriptsys/deletestatement.cpp +++ b/doomsday/libdeng2/src/scriptsys/deletestatement.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2012-2013 Jaakko Keränen + * Copyright © 2012-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/dictionaryexpression.cpp b/doomsday/libdeng2/src/scriptsys/dictionaryexpression.cpp index 48ff86c11e..02d2f0d837 100644 --- a/doomsday/libdeng2/src/scriptsys/dictionaryexpression.cpp +++ b/doomsday/libdeng2/src/scriptsys/dictionaryexpression.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/evaluator.cpp b/doomsday/libdeng2/src/scriptsys/evaluator.cpp index 8bab2992c7..f8028d66b3 100644 --- a/doomsday/libdeng2/src/scriptsys/evaluator.cpp +++ b/doomsday/libdeng2/src/scriptsys/evaluator.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/expression.cpp b/doomsday/libdeng2/src/scriptsys/expression.cpp index 06cc058e40..e84b90fc2e 100644 --- a/doomsday/libdeng2/src/scriptsys/expression.cpp +++ b/doomsday/libdeng2/src/scriptsys/expression.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/expressionstatement.cpp b/doomsday/libdeng2/src/scriptsys/expressionstatement.cpp index 24eae4409b..14241f2a78 100644 --- a/doomsday/libdeng2/src/scriptsys/expressionstatement.cpp +++ b/doomsday/libdeng2/src/scriptsys/expressionstatement.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/flowstatement.cpp b/doomsday/libdeng2/src/scriptsys/flowstatement.cpp index 70386a6008..759af51e7d 100644 --- a/doomsday/libdeng2/src/scriptsys/flowstatement.cpp +++ b/doomsday/libdeng2/src/scriptsys/flowstatement.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/forstatement.cpp b/doomsday/libdeng2/src/scriptsys/forstatement.cpp index 74532bdc4b..2570283c8c 100644 --- a/doomsday/libdeng2/src/scriptsys/forstatement.cpp +++ b/doomsday/libdeng2/src/scriptsys/forstatement.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/function.cpp b/doomsday/libdeng2/src/scriptsys/function.cpp index 2546ecd692..787cbadf5f 100644 --- a/doomsday/libdeng2/src/scriptsys/function.cpp +++ b/doomsday/libdeng2/src/scriptsys/function.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/functionstatement.cpp b/doomsday/libdeng2/src/scriptsys/functionstatement.cpp index f54734011d..a814412059 100644 --- a/doomsday/libdeng2/src/scriptsys/functionstatement.cpp +++ b/doomsday/libdeng2/src/scriptsys/functionstatement.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/functionvalue.cpp b/doomsday/libdeng2/src/scriptsys/functionvalue.cpp index e12976af71..64b126dc43 100644 --- a/doomsday/libdeng2/src/scriptsys/functionvalue.cpp +++ b/doomsday/libdeng2/src/scriptsys/functionvalue.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/ifstatement.cpp b/doomsday/libdeng2/src/scriptsys/ifstatement.cpp index 3a09d88cbb..a5e94b7fe5 100644 --- a/doomsday/libdeng2/src/scriptsys/ifstatement.cpp +++ b/doomsday/libdeng2/src/scriptsys/ifstatement.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/lex.cpp b/doomsday/libdeng2/src/scriptsys/lex.cpp index 10c08137d5..2a1e9aa6ce 100644 --- a/doomsday/libdeng2/src/scriptsys/lex.cpp +++ b/doomsday/libdeng2/src/scriptsys/lex.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/module.cpp b/doomsday/libdeng2/src/scriptsys/module.cpp index e35e50ab87..60495ad3ef 100644 --- a/doomsday/libdeng2/src/scriptsys/module.cpp +++ b/doomsday/libdeng2/src/scriptsys/module.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/nameexpression.cpp b/doomsday/libdeng2/src/scriptsys/nameexpression.cpp index e9e4f2838f..096e468d0c 100644 --- a/doomsday/libdeng2/src/scriptsys/nameexpression.cpp +++ b/doomsday/libdeng2/src/scriptsys/nameexpression.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/operator.cpp b/doomsday/libdeng2/src/scriptsys/operator.cpp index a1bf652994..8dbddb5c98 100644 --- a/doomsday/libdeng2/src/scriptsys/operator.cpp +++ b/doomsday/libdeng2/src/scriptsys/operator.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/operatorexpression.cpp b/doomsday/libdeng2/src/scriptsys/operatorexpression.cpp index f8a9fcb1c5..eb82d2a0ff 100644 --- a/doomsday/libdeng2/src/scriptsys/operatorexpression.cpp +++ b/doomsday/libdeng2/src/scriptsys/operatorexpression.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/parser.cpp b/doomsday/libdeng2/src/scriptsys/parser.cpp index e3fe80ddbd..a3754cf858 100644 --- a/doomsday/libdeng2/src/scriptsys/parser.cpp +++ b/doomsday/libdeng2/src/scriptsys/parser.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/printstatement.cpp b/doomsday/libdeng2/src/scriptsys/printstatement.cpp index 7fc5edac52..b7da377bc9 100644 --- a/doomsday/libdeng2/src/scriptsys/printstatement.cpp +++ b/doomsday/libdeng2/src/scriptsys/printstatement.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/process.cpp b/doomsday/libdeng2/src/scriptsys/process.cpp index 98693165ac..eb663f2c57 100644 --- a/doomsday/libdeng2/src/scriptsys/process.cpp +++ b/doomsday/libdeng2/src/scriptsys/process.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/script.cpp b/doomsday/libdeng2/src/scriptsys/script.cpp index 514f827f57..e427e097b6 100644 --- a/doomsday/libdeng2/src/scriptsys/script.cpp +++ b/doomsday/libdeng2/src/scriptsys/script.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/scriptedinfo.cpp b/doomsday/libdeng2/src/scriptsys/scriptedinfo.cpp index e05d2f3de8..a73fcbe027 100644 --- a/doomsday/libdeng2/src/scriptsys/scriptedinfo.cpp +++ b/doomsday/libdeng2/src/scriptsys/scriptedinfo.cpp @@ -1,6 +1,6 @@ /** @file scriptedinfo.cpp Info document tree with script context. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/scriptlex.cpp b/doomsday/libdeng2/src/scriptsys/scriptlex.cpp index 5782b01657..820f83a005 100644 --- a/doomsday/libdeng2/src/scriptsys/scriptlex.cpp +++ b/doomsday/libdeng2/src/scriptsys/scriptlex.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/scriptsystem.cpp b/doomsday/libdeng2/src/scriptsys/scriptsystem.cpp index 919ae05fa3..482c7acc45 100644 --- a/doomsday/libdeng2/src/scriptsys/scriptsystem.cpp +++ b/doomsday/libdeng2/src/scriptsys/scriptsystem.cpp @@ -1,6 +1,6 @@ /** @file scriptsystem.cpp Subsystem for scripts. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/statement.cpp b/doomsday/libdeng2/src/scriptsys/statement.cpp index 94c8a37a75..b1011c23a7 100644 --- a/doomsday/libdeng2/src/scriptsys/statement.cpp +++ b/doomsday/libdeng2/src/scriptsys/statement.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/tokenbuffer.cpp b/doomsday/libdeng2/src/scriptsys/tokenbuffer.cpp index 1f2a2ad7c4..326c395585 100644 --- a/doomsday/libdeng2/src/scriptsys/tokenbuffer.cpp +++ b/doomsday/libdeng2/src/scriptsys/tokenbuffer.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/tokenrange.cpp b/doomsday/libdeng2/src/scriptsys/tokenrange.cpp index faf70d09b7..1f60648e05 100644 --- a/doomsday/libdeng2/src/scriptsys/tokenrange.cpp +++ b/doomsday/libdeng2/src/scriptsys/tokenrange.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/trystatement.cpp b/doomsday/libdeng2/src/scriptsys/trystatement.cpp index 22cf4381a5..86bd6efa36 100644 --- a/doomsday/libdeng2/src/scriptsys/trystatement.cpp +++ b/doomsday/libdeng2/src/scriptsys/trystatement.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2009-2013 Jaakko Keränen + * Copyright © 2009-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/scriptsys/whilestatement.cpp b/doomsday/libdeng2/src/scriptsys/whilestatement.cpp index c7b3ff861f..5b967d9a8d 100644 --- a/doomsday/libdeng2/src/scriptsys/whilestatement.cpp +++ b/doomsday/libdeng2/src/scriptsys/whilestatement.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project -- libdeng2 * - * Copyright (c) 2004-2013 Jaakko Keränen + * Copyright © 2004-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/version.cpp b/doomsday/libdeng2/src/version.cpp index 4b997a101e..e166711cb7 100644 --- a/doomsday/libdeng2/src/version.cpp +++ b/doomsday/libdeng2/src/version.cpp @@ -2,8 +2,8 @@ * @file version.cpp * Version numbering and labeling for libdeng2. * - * @authors Copyright © 2011-2013 Jaakko Keränen - * @authors Copyright © 2011-2013 Daniel Swanson + * @authors Copyright © 2011-2013 Jaakko Keränen + * @authors Copyright © 2011-2013 Daniel Swanson * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/widgets/constantrule.cpp b/doomsday/libdeng2/src/widgets/constantrule.cpp index 639c10b5cd..4a1d771633 100644 --- a/doomsday/libdeng2/src/widgets/constantrule.cpp +++ b/doomsday/libdeng2/src/widgets/constantrule.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project * - * Copyright (c) 2011-2013 Jaakko Keränen + * Copyright © 2011-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/widgets/indirectrule.cpp b/doomsday/libdeng2/src/widgets/indirectrule.cpp index cf868ce2f0..f2f1ba62b3 100644 --- a/doomsday/libdeng2/src/widgets/indirectrule.cpp +++ b/doomsday/libdeng2/src/widgets/indirectrule.cpp @@ -1,6 +1,6 @@ /** @file indirectrule.cpp Indirect rule. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/widgets/operatorrule.cpp b/doomsday/libdeng2/src/widgets/operatorrule.cpp index 379011a0a9..20e10c98c0 100644 --- a/doomsday/libdeng2/src/widgets/operatorrule.cpp +++ b/doomsday/libdeng2/src/widgets/operatorrule.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project * - * Copyright (c) 2011-2013 Jaakko Keränen + * Copyright © 2011-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/widgets/rule.cpp b/doomsday/libdeng2/src/widgets/rule.cpp index 8bd3f0b7a4..52f6676cd2 100644 --- a/doomsday/libdeng2/src/widgets/rule.cpp +++ b/doomsday/libdeng2/src/widgets/rule.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project * - * Copyright (c) 2011-2013 Jaakko Keränen + * Copyright © 2011-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/widgets/rulebank.cpp b/doomsday/libdeng2/src/widgets/rulebank.cpp index 51d226d578..f4c7316a6a 100644 --- a/doomsday/libdeng2/src/widgets/rulebank.cpp +++ b/doomsday/libdeng2/src/widgets/rulebank.cpp @@ -1,6 +1,6 @@ /** @file rulebank.cpp Bank of Rules. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/widgets/rulerectangle.cpp b/doomsday/libdeng2/src/widgets/rulerectangle.cpp index 38f7648195..21379dc52f 100644 --- a/doomsday/libdeng2/src/widgets/rulerectangle.cpp +++ b/doomsday/libdeng2/src/widgets/rulerectangle.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project * - * Copyright (c) 2011-2013 Jaakko Keränen + * Copyright © 2011-2013 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libdeng2/src/widgets/scalarrule.cpp b/doomsday/libdeng2/src/widgets/scalarrule.cpp index 54e5804328..ae9faac45a 100644 --- a/doomsday/libdeng2/src/widgets/scalarrule.cpp +++ b/doomsday/libdeng2/src/widgets/scalarrule.cpp @@ -1,7 +1,7 @@ /* * The Doomsday Engine Project * - * Copyright (c) 2011 Jaakko Keränen + * Copyright © 2011 Jaakko Keränen * * @par License * LGPL: http://www.gnu.org/licenses/lgpl.html diff --git a/doomsday/libgui/gui.doxy b/doomsday/libgui/gui.doxy new file mode 100644 index 0000000000..d82e5063f8 --- /dev/null +++ b/doomsday/libgui/gui.doxy @@ -0,0 +1,25 @@ +# Public API documentation for libgui +@INCLUDE = ../doomsday.doxy + +PROJECT_NAME = "libgui" +PROJECT_NUMBER = 1.14.0 +PROJECT_BRIEF = "GUI extension for libdeng2" +OUTPUT_DIRECTORY = ../apidoc/gui/ + +INPUT = include src +FILE_PATTERNS = * +EXCLUDE_PATTERNS = .DS_Store +PREDEFINED = __cplusplus __LIBGUI__ \ + "DENG2_PIMPL(ClassName)=typedef ClassName Public; struct ClassName::Instance : public de::Private" \ + "DENG2_PIMPL_NOREF(C)=struct C::Instance : public de::IPrivate" \ + "DENG2_PRIVATE(Var)=struct Instance; Instance *Var;" \ + "DENG2_ERROR(N)=class N : public de::Error {};" \ + "DENG2_SUB_ERROR(B,N)=class N : public B {};" + +INCLUDED_BY_GRAPH = NO +COLLABORATION_GRAPH = NO +REFERENCED_BY_RELATION = NO +OPTIMIZE_OUTPUT_FOR_C = NO +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = NO +INTERNAL_DOCS = NO diff --git a/doomsday/libgui/src/canvas.cpp b/doomsday/libgui/src/canvas.cpp index 4d267d955c..af575009d8 100644 --- a/doomsday/libgui/src/canvas.cpp +++ b/doomsday/libgui/src/canvas.cpp @@ -365,9 +365,8 @@ void Canvas::updateSize() return; } */ -#endif - qDebug() << this << "resizing now"; +#endif makeCurrent(); d->currentSize = d->pendingSize; diff --git a/doomsday/macros.pri b/doomsday/macros.pri new file mode 100644 index 0000000000..97f0078b3e --- /dev/null +++ b/doomsday/macros.pri @@ -0,0 +1,66 @@ +# The Doomsday Engine Project +# Copyright (c) 2011-2014 Jaakko Keränen +# Copyright (c) 2011-2013 Daniel Swanson + +defineTest(runPython2) { + win32: system(python $$1) # 2.7 still expected + else: system(/usr/bin/env python2.7 $$1) +} + +defineTest(runPython2InDir) { + win32: system(cd "$$1" && python $$2) + else: system(cd "$$1" && /usr/bin/env python2.7 $$2) +} + +defineTest(echo) { + deng_verbosebuildconfig { + !win32 { + message($$1) + } else { + # We don't want to get the printed messages after everything else, + # so print to stdout. + system(echo $$1) + } + } +} + +defineTest(useLibDir) { + btype = "" + win32 { + deng_debug: btype = "/Debug" + else: btype = "/Release" + } + exists($${1}$${btype}) { + LIBS += -L$${1}$${btype} + export(LIBS) + return(true) + } + return(false) +} + +defineTest(doPostLink) { + isEmpty(QMAKE_POST_LINK) { + QMAKE_POST_LINK = $$1 + } else { + QMAKE_POST_LINK = $$QMAKE_POST_LINK && $$1 + } + export(QMAKE_POST_LINK) +} + +macx { + defineTest(removeQtLibPrefix) { + doPostLink("install_name_tool -change $$[QT_INSTALL_LIBS]/$$2 $$2 $$1") + } + defineTest(fixInstallName) { + # 1: binary file + # 2: library name + # 3: path to Frameworks/ + removeQtLibPrefix($$1, $$2) + doPostLink("install_name_tool -change $$2 @executable_path/$$3/Frameworks/$$2 $$1") + } + defineTest(fixPluginInstallId) { + # 1: target name + # 2: version + doPostLink("install_name_tool -id @executable_path/../DengPlugins/$${1}.bundle/Versions/$$2/$$1 $${1}.bundle/Versions/$$2/$$1") + } +} diff --git a/doomsday/plugins/common/common.pri b/doomsday/plugins/common/common.pri index c35d642628..9c986457a6 100644 --- a/doomsday/plugins/common/common.pri +++ b/doomsday/plugins/common/common.pri @@ -71,6 +71,7 @@ HEADERS += \ $$common_inc/r_common.h \ $$common_inc/saveinfo.h \ $$common_inc/saveslots.h \ + $$common_inc/thingarchive.h \ $$common_inc/thinkerinfo.h \ $$common_inc/x_hair.h \ $$common_inc/xgclass.h @@ -87,7 +88,7 @@ SOURCES += \ $$common_src/g_controls.c \ $$common_src/g_defs.c \ $$common_src/g_eventsequence.cpp \ - $$common_src/g_game.c \ + $$common_src/g_game.cpp \ $$common_src/g_update.c \ $$common_src/gamerules.c \ $$common_src/gamestatereader.cpp \ @@ -99,7 +100,7 @@ SOURCES += \ $$common_src/hu_inventory.c \ $$common_src/hu_lib.c \ $$common_src/hu_log.c \ - $$common_src/hu_menu.c \ + $$common_src/hu_menu.cpp \ $$common_src/hu_msg.c \ $$common_src/hu_pspr.c \ $$common_src/hu_stuff.cpp \ @@ -137,5 +138,6 @@ SOURCES += \ $$common_src/r_common.c \ $$common_src/saveinfo.cpp \ $$common_src/saveslots.cpp \ + $$common_src/thingarchive.cpp \ $$common_src/thinkerinfo.cpp \ $$common_src/x_hair.c diff --git a/doomsday/plugins/common/include/common.h b/doomsday/plugins/common/include/common.h index 5055ca27c2..600f6f6909 100644 --- a/doomsday/plugins/common/include/common.h +++ b/doomsday/plugins/common/include/common.h @@ -50,6 +50,14 @@ DENG_EXTERN_C dd_bool sc_FileScripts; DENG_EXTERN_C char const *sc_ScriptsDir; +#ifdef __cplusplus +extern "C" { +#endif + int Common_GetInteger(int id); +#ifdef __cplusplus +} // extern "C" +#endif + #endif // LIBCOMMON_GAME_INCLUDES diff --git a/doomsday/plugins/common/include/g_common.h b/doomsday/plugins/common/include/g_common.h index a9f2f67400..f0d94aa140 100644 --- a/doomsday/plugins/common/include/g_common.h +++ b/doomsday/plugins/common/include/g_common.h @@ -52,6 +52,8 @@ void G_ChangeGameState(gamestate_t state); gameaction_t G_GameAction(void); void G_SetGameAction(gameaction_t action); +AutoStr *G_IdentityKeyForLegacyGamemode(int gamemode, int saveVersion); + char const *P_GetGameModeName(void); uint G_GenerateSessionId(void); @@ -135,7 +137,7 @@ uint G_LogicalMapNumber(uint episode, uint map); /// @return Logical map number. uint G_CurrentLogicalMapNumber(void); -AutoStr *G_GenerateSaveGameName(void); +AutoStr *G_GenerateUserSaveDescription(void); D_CMD( CCmdMakeLocal ); D_CMD( CCmdSetCamera ); diff --git a/doomsday/plugins/common/include/gamestatereader.h b/doomsday/plugins/common/include/gamestatereader.h index 1e180494f0..9b9d06c0d3 100644 --- a/doomsday/plugins/common/include/gamestatereader.h +++ b/doomsday/plugins/common/include/gamestatereader.h @@ -24,30 +24,137 @@ #include "common.h" #include "saveinfo.h" #include +#include /** - * @ingroup libcommon - * @see GameStateWriter + * Interface for game state (savegame) readers. */ -class GameStateReader +class IGameStateReader { public: /// An error occurred attempting to open the input file. @ingroup errors DENG2_ERROR(FileAccessError); + /// Base class for read errors. @ingroup errors + DENG2_ERROR(ReadError); + public: - GameStateReader(); + virtual ~IGameStateReader() {} /** - * Determines whether the resource file on @a path is interpretable as a game state which can - * be loaded with a GameStateReader. + * Attempt to load (read/interpret) the saved game state. * - * @param info SaveInfo to attempt to read game session header into. - * @param path Path to the resource file to be recognized. + * @param info SaveInfo for the saved game state to be read/interpreted. */ - static bool recognize(SaveInfo *info, Str const *path); + virtual void read(SaveInfo &info) = 0; +}; + +/** + * Game state recognizer function ptr. + * + * The job of a recognizer function is to determine whether the game session associated + * with the given save @a info is interpretable as a potentially loadable savegame state. + * + * @param info SaveInfo to attempt to read game session header into. + */ +typedef bool (*GameStateRecognizeFunc)(SaveInfo &info); + +/// Game state reader instantiator function ptr. +typedef IGameStateReader *(*GameStateReaderMakeFunc)(); + +/** + * Factory for the construction of new IGameStateReader-derived instances. + */ +class GameStateReaderFactory +{ +public: + /** + * Register a game state reader. + * + * @param recognizer Game state recognizer function. + * @param maker Game state reader instantiator function. + */ + void declareReader(GameStateRecognizeFunc recognizer, GameStateReaderMakeFunc maker) + { + DENG_ASSERT(recognizer != 0 && maker != 0); + ReaderInfo info; + info.recognize = recognizer; + info.newReader = maker; + readers.push_back(info); + } + + /** + * Determines whether a IGameStateReader appropriate for the specified save game @a info + * is available and if so, reads the game session header. + * + * @param saveInfo The game session header will be written here if recognized. + * + * @return @c true= the game session header was read successfully. + * + * @see recognizeAndMakeReader() + */ + bool recognize(SaveInfo &saveInfo) const + { + return readGameSessionHeader(saveInfo) != 0; + } + + /** + * Determines whether a IGameStateReader appropriate for the specified save game @a info + * is available and if so, reads the game session header and returns a new reader instance + * for deserializing the game state. + * + * @param saveInfo The game session header will be written here if recognized. + * + * @return New reader instance if recognized; otherwise @c 0. Ownership given to the caller. + * + * @see recognize() + */ + IGameStateReader *recognizeAndMakeReader(SaveInfo &saveInfo) const + { + if(ReaderInfo const *rdrInfo = readGameSessionHeader(saveInfo)) + { + return rdrInfo->newReader(); + } + return 0; // Unrecognized + } + +private: + struct ReaderInfo { + GameStateRecognizeFunc recognize; + GameStateReaderMakeFunc newReader; + }; + typedef std::list ReaderInfos; + ReaderInfos readers; + + ReaderInfo const *readGameSessionHeader(SaveInfo &info) const + { + DENG2_FOR_EACH_CONST(ReaderInfos, i, readers) + { + if(i->recognize(info)) + { + return &*i; + } + } + return 0; // Unrecognized + } +}; + +/** + * Native saved game state reader. + * + * @ingroup libcommon + * @see GameStateWriter + */ +class GameStateReader : public IGameStateReader +{ +public: + GameStateReader(); + ~GameStateReader(); + + static IGameStateReader *make(); + static bool recognize(SaveInfo &info); - void read(SaveInfo *info, Str const *path); + void read(SaveInfo &info); private: DENG2_PRIVATE(d) diff --git a/doomsday/plugins/common/include/gamestatewriter.h b/doomsday/plugins/common/include/gamestatewriter.h index cd4b872a08..d76a05eea4 100644 --- a/doomsday/plugins/common/include/gamestatewriter.h +++ b/doomsday/plugins/common/include/gamestatewriter.h @@ -23,8 +23,11 @@ #include "common.h" #include "saveinfo.h" +#include /** + * Native saved game state writer. + * * @ingroup libcommon * @see GameStateReader */ @@ -37,7 +40,7 @@ class GameStateWriter public: GameStateWriter(); - void write(SaveInfo *saveInfo, Str const *path); + void write(SaveInfo &info); private: DENG2_PRIVATE(d) diff --git a/doomsday/plugins/common/include/hu_lib.h b/doomsday/plugins/common/include/hu_lib.h index 09dde3fd64..d31cea494e 100644 --- a/doomsday/plugins/common/include/hu_lib.h +++ b/doomsday/plugins/common/include/hu_lib.h @@ -214,6 +214,10 @@ typedef struct mn_object_s { int timer; } mn_object_t; +#ifdef __cplusplus +extern "C" { +#endif + mn_obtype_e MNObject_Type(const mn_object_t* obj); struct mn_page_s* MNObject_Page(const mn_object_t* obj); @@ -298,6 +302,10 @@ dd_bool MNObject_HasAction(mn_object_t* obj, mn_actionid_t action); */ int MNObject_ExecAction(mn_object_t* obj, mn_actionid_t action, void* paramaters); +#ifdef __cplusplus +} // extern "C" +#endif + typedef enum { MENU_COLOR1 = 0, MENU_COLOR2, @@ -380,6 +388,10 @@ typedef struct mn_page_s { int timer; } mn_page_t; +#ifdef __cplusplus +extern "C" { +#endif + void MNPage_Initialize(mn_page_t* page); /// Call the ticker routine for each object. @@ -461,6 +473,10 @@ int MNPage_LineHeight(mn_page_t *page/*, lineOffset = 0*/); /// @return Current time in tics since page activation. int MNPage_Timer(mn_page_t* page); +#ifdef __cplusplus +} // extern "C" +#endif + /** * Rect objects. */ @@ -472,6 +488,10 @@ typedef struct mndata_rect_s { patchid_t patch; } mndata_rect_t; +#ifdef __cplusplus +extern "C" { +#endif + mn_object_t* MNRect_New(void); void MNRect_Delete(mn_object_t* ob); @@ -488,6 +508,10 @@ void MNRect_UpdateGeometry(mn_object_t* ob, mn_page_t* page); */ void MNRect_SetBackgroundPatch(mn_object_t* ob, patchid_t patch); +#ifdef __cplusplus +} // extern "C" +#endif + /** * @defgroup mnTextFlags MNText Flags */ @@ -499,15 +523,19 @@ void MNRect_SetBackgroundPatch(mn_object_t* ob, patchid_t patch); * Text objects. */ typedef struct mndata_text_s { - const char* text; + char const *text; /// Patch to be used when drawing this instead of text if Patch Replacement is in use. - patchid_t* patch; + patchid_t *patch; /// @ref mnTextFlags int flags; } mndata_text_t; +#ifdef __cplusplus +extern "C" { +#endif + mn_object_t* MNText_New(void); void MNText_Delete(mn_object_t* ob); @@ -517,6 +545,10 @@ void MNText_UpdateGeometry(mn_object_t* ob, mn_page_t* page); int MNText_SetFlags(mn_object_t* ob, flagop_t op, int flags); +#ifdef __cplusplus +} // extern "C" +#endif + /** * @defgroup mnButtonFlags MNButton Flags */ @@ -530,20 +562,24 @@ int MNText_SetFlags(mn_object_t* ob, flagop_t op, int flags); typedef struct mndata_button_s { dd_bool staydownMode; /// @c true= this is operating in two-state "staydown" mode. - void* data; + void *data; /// Label text. - const char* text; + char const *text; /// Patch to be used when drawing this instead of text. - patchid_t* patch; + patchid_t *patch; - const char* yes, *no; + char const *yes, *no; /// @ref mnButtonFlags int flags; } mndata_button_t; +#ifdef __cplusplus +extern "C" { +#endif + mn_object_t* MNButton_New(void); void MNButton_Delete(mn_object_t* ob); @@ -554,6 +590,10 @@ void MNButton_UpdateGeometry(mn_object_t* ob, mn_page_t* page); int MNButton_SetFlags(mn_object_t* ob, flagop_t op, int flags); +#ifdef __cplusplus +} // extern "C" +#endif + /** * Edit field. */ @@ -587,6 +627,10 @@ typedef struct mndata_edit_s { int data2; } mndata_edit_t; +#ifdef __cplusplus +extern "C" { +#endif + mn_object_t* MNEdit_New(void); void MNEdit_Delete(mn_object_t* ob); @@ -617,6 +661,10 @@ const ddstring_t* MNEdit_Text(mn_object_t* ob); */ void MNEdit_SetText(mn_object_t* ob, int flags, const char* string); +#ifdef __cplusplus +} // extern "C" +#endif + /** * List selection. */ @@ -632,15 +680,19 @@ typedef struct { /// @note Also used for MN_LISTINLINE! typedef struct mndata_list_s { - void* items; + void *items; int count; // Number of items. - void* data; + void *data; int mask; int selection; // Selected item (-1 if none). int first; // First visible item. int numvis; } mndata_list_t; +#ifdef __cplusplus +extern "C" { +#endif + mn_object_t* MNList_New(void); void MNList_Delete(mn_object_t* ob); @@ -693,6 +745,10 @@ dd_bool MNList_SelectItem(mn_object_t* ob, int flags, int itemIndex); */ dd_bool MNList_SelectItemByValue(mn_object_t* ob, int flags, int itemIndex); +#ifdef __cplusplus +} // extern "C" +#endif + /** * Color preview box. */ @@ -705,12 +761,16 @@ typedef struct mndata_colorbox_s { int width, height; float r, g, b, a; dd_bool rgbaMode; - void* data1; - void* data2; - void* data3; - void* data4; + void *data1; + void *data2; + void *data3; + void *data4; } mndata_colorbox_t; +#ifdef __cplusplus +extern "C" { +#endif + mn_object_t* MNColorBox_New(void); void MNColorBox_Delete(mn_object_t* ob); @@ -777,6 +837,10 @@ dd_bool MNColorBox_SetAlphaf(mn_object_t* ob, int flags, float alpha); */ dd_bool MNColorBox_CopyColor(mn_object_t* ob, int flags, const mn_object_t* otherObj); +#ifdef __cplusplus +} // extern "C" +#endif + /** * Graphical slider. */ @@ -803,14 +867,18 @@ typedef struct mndata_slider_s { float value; float step; // Button step. dd_bool floatMode; // Otherwise only integers are allowed. - /// \todo Turn this into a property record or something. - void* data1; - void* data2; - void* data3; - void* data4; - void* data5; + /// @todo Turn this into a property record or something. + void *data1; + void *data2; + void *data3; + void *data4; + void *data5; } mndata_slider_t; +#ifdef __cplusplus +extern "C" { +#endif + mn_object_t* MNSlider_New(void); void MNSlider_Delete(mn_object_t* ob); @@ -839,6 +907,10 @@ float MNSlider_Value(const mn_object_t* ob); */ void MNSlider_SetValue(mn_object_t* ob, int flags, float value); +#ifdef __cplusplus +} // extern "C" +#endif + /** * Mobj preview visual. */ @@ -852,6 +924,10 @@ typedef struct mndata_mobjpreview_s { int plrClass; /// Player class identifier. } mndata_mobjpreview_t; +#ifdef __cplusplus +extern "C" { +#endif + mn_object_t* MNMobjPreview_New(void); void MNMobjPreview_Delete(mn_object_t* ob); @@ -864,6 +940,10 @@ void MNMobjPreview_SetTranslationMap(mn_object_t* ob, int tMap); void MNMobjPreview_Drawer(mn_object_t* ob, const Point2Raw* origin); void MNMobjPreview_UpdateGeometry(mn_object_t* ob, mn_page_t* page); +#ifdef __cplusplus +} // extern "C" +#endif + // Menu render state: typedef struct mn_rendstate_s { float pageAlpha; @@ -872,7 +952,8 @@ typedef struct mn_rendstate_s { float textColors[MENU_COLOR_COUNT][4]; fontid_t textFonts[MENU_FONT_COUNT]; } mn_rendstate_t; -extern const mn_rendstate_t* mnRendState; + +DENG_EXTERN_C mn_rendstate_t const *mnRendState; /** * @defgroup menuEffectFlags Menu Effect Flags @@ -885,6 +966,10 @@ extern const mn_rendstate_t* mnRendState; #define MEF_EVERYTHING (MEF_TEXT_TYPEIN|MEF_TEXT_SHADOW|MEF_TEXT_GLITTER) ///@} +#ifdef __cplusplus +extern "C" { +#endif + short MN_MergeMenuEffectWithDrawTextFlags(short f); mn_object_t* MN_MustFindObjectOnPage(mn_page_t* page, int group, int flags); @@ -896,6 +981,10 @@ void MN_DrawPage(mn_page_t* page, float alpha, dd_bool showFocusCursor); */ void Hu_MenuCommand(menucommand_e cmd); +#ifdef __cplusplus +} // extern "C" +#endif + typedef enum { GUI_NONE, GUI_BOX, @@ -982,6 +1071,10 @@ typedef struct uiwidget_s { void* typedata; } uiwidget_t; +#ifdef __cplusplus +extern "C" { +#endif + void GUI_DrawWidget(uiwidget_t* obj, const Point2Raw* origin); void GUI_DrawWidgetXY(uiwidget_t* obj, int x, int y); @@ -1018,6 +1111,10 @@ void UIWidget_SetMaximumSize(uiwidget_t* obj, const Size2Raw* size); void UIWidget_SetMaximumWidth(uiwidget_t* obj, int width); +#ifdef __cplusplus +} // extern "C" +#endif + /** * @defgroup uiWidgetGroupFlags UIWidget Group Flags */ @@ -1039,12 +1136,20 @@ typedef struct { uiwidgetid_t* widgetIds; } guidata_group_t; +#ifdef __cplusplus +extern "C" { +#endif + void UIGroup_AddWidget(uiwidget_t* obj, uiwidget_t* other); int UIGroup_Flags(uiwidget_t* obj); void UIGroup_SetFlags(uiwidget_t* obj, int flags); void UIGroup_UpdateGeometry(uiwidget_t* obj); +#ifdef __cplusplus +} // extern "C" +#endif + typedef struct { int value; } guidata_health_t; @@ -1206,6 +1311,10 @@ typedef struct { } guidata_flight_t; #endif +#ifdef __cplusplus +extern "C" { +#endif + void GUI_Register(void); void GUI_Init(void); @@ -1225,9 +1334,14 @@ uiwidgetid_t GUI_CreateWidget(guiwidgettype_t type, int player, int alignFlags, uiwidgetid_t GUI_CreateGroup(int groupFlags, int player, int alignFlags, order_t order, int padding); +#ifdef __cplusplus +} // extern "C" +#endif + typedef struct ui_rendstate_s { float pageAlpha; } ui_rendstate_t; -extern const ui_rendstate_t* uiRendState; + +DENG_EXTERN_C const ui_rendstate_t* uiRendState; #endif /* LIBCOMMON_UI_LIBRARY_H */ diff --git a/doomsday/plugins/common/include/hu_menu.h b/doomsday/plugins/common/include/hu_menu.h index ee04b9ec53..5a722ab16b 100644 --- a/doomsday/plugins/common/include/hu_menu.h +++ b/doomsday/plugins/common/include/hu_menu.h @@ -33,6 +33,9 @@ #include "dd_types.h" #include "hu_lib.h" +DENG_EXTERN_C int menuTime; +DENG_EXTERN_C dd_bool menuNominatingQuickSaveSlot; + #ifdef __cplusplus extern "C" { #endif @@ -86,9 +89,6 @@ extern "C" { #define MENU_CURSOR_FRAMECOUNT 2 #define MENU_CURSOR_TICSPERFRAME 8 -extern int menuTime; -extern dd_bool menuNominatingQuickSaveSlot; - /// Register the console commands, variables, etc..., of this module. void Hu_MenuRegister(void); diff --git a/doomsday/plugins/common/include/hu_stuff.h b/doomsday/plugins/common/include/hu_stuff.h index cffc25ec81..8087232852 100644 --- a/doomsday/plugins/common/include/hu_stuff.h +++ b/doomsday/plugins/common/include/hu_stuff.h @@ -27,10 +27,26 @@ #include "doomsday.h" #include "gl_drawpatch.h" -#ifdef __cplusplus -extern "C" { +DENG_EXTERN_C patchid_t *pMapNames; // Name graphics of each map. +DENG_EXTERN_C uint pMapNamesSize; + +#if __JHERETIC__ || __JHEXEN__ +DENG_EXTERN_C patchid_t pInvItemBox; +DENG_EXTERN_C patchid_t pInvSelectBox; +DENG_EXTERN_C patchid_t pInvPageLeft[2]; +DENG_EXTERN_C patchid_t pInvPageRight[2]; #endif +#if __JDOOM__ || __JDOOM64__ +// Quit messages. +#define NUM_QUITMESSAGES 22 +DENG_EXTERN_C char const *endmsg[NUM_QUITMESSAGES + 1]; +#endif + +DENG_EXTERN_C dd_bool shiftdown; +DENG_EXTERN_C char const shiftXForm[]; +DENG_EXTERN_C patchid_t borderPatches[8]; + // The fonts. typedef enum { GF_FIRST = 1, @@ -50,6 +66,9 @@ typedef enum { NUM_GAME_FONTS } gamefontid_t; +DENG_EXTERN_C fontid_t fonts[NUM_GAME_FONTS]; +#define FID(idx) (fonts[idx]) + // Vector graphics. enum { VG_FIRST = 1, @@ -67,30 +86,10 @@ enum { NUM_VECTOR_GRAPHICS }; /* svgid_t*/ -#define FID(idx) (fonts[idx]) - -extern fontid_t fonts[NUM_GAME_FONTS]; - -extern patchid_t* pMapNames; // Name graphics of each map. -extern uint pMapNamesSize; - -#if __JHERETIC__ || __JHEXEN__ -extern patchid_t pInvItemBox; -extern patchid_t pInvSelectBox; -extern patchid_t pInvPageLeft[2]; -extern patchid_t pInvPageRight[2]; -#endif - -#if __JDOOM__ || __JDOOM64__ -// Quit messages. -#define NUM_QUITMESSAGES 22 -extern const char* endmsg[NUM_QUITMESSAGES + 1]; +#ifdef __cplusplus +extern "C" { #endif -extern dd_bool shiftdown; -extern const char shiftXForm[]; -extern patchid_t borderPatches[8]; - void Hu_LoadData(void); void Hu_Drawer(void); void Hu_Ticker(void); diff --git a/doomsday/plugins/common/include/m_ctrl.h b/doomsday/plugins/common/include/m_ctrl.h index 050a80f5ea..3e5c6a3919 100644 --- a/doomsday/plugins/common/include/m_ctrl.h +++ b/doomsday/plugins/common/include/m_ctrl.h @@ -31,6 +31,10 @@ #include "hu_lib.h" +#ifdef __cplusplus +extern "C" { +#endif + void Hu_MenuInitControlsPage(void); void Hu_MenuDrawControlsPage(mn_page_t* page, const Point2Raw* origin); void Hu_MenuControlGrabDrawer(const char* niceName, float alpha); @@ -57,4 +61,8 @@ void MNBindings_UpdateGeometry(mn_object_t* ob, mn_page_t* page); const char* MNBindings_ControlName(mn_object_t* ob); +#ifdef __cplusplus +} // extern "C" +#endif + #endif /// LIBCOMMON_MENU_CONTROLS diff --git a/doomsday/plugins/common/include/mapstatereader.h b/doomsday/plugins/common/include/mapstatereader.h index 71df37bf56..61cc4d9cd7 100644 --- a/doomsday/plugins/common/include/mapstatereader.h +++ b/doomsday/plugins/common/include/mapstatereader.h @@ -22,7 +22,7 @@ #define LIBCOMMON_MAPSTATEREADER_H #include "common.h" -#include "dmu_archiveindex.h" +#include "thingarchive.h" #include /** @@ -41,7 +41,7 @@ class MapStateReader /** * @param saveVersion Logical saved state version number. */ - MapStateReader(int saveVersion); + MapStateReader(int saveVersion, int thingArchiveSize = 0); /** * Deserialize the saved map state using the specified @a reader. @@ -59,7 +59,12 @@ class MapStateReader Reader *reader(); /** - * Finds and returns a material with the identifier @a serialId. + * Lookup a pointer to a Mobj with the given @a serialId. + */ + struct mobj_s *mobj(ThingArchive::SerialId serialId, void *address) const; + + /** + * Lookup a pointer to a Material with the given @a serialId. * * @param serialId Unique identifier for the material in the material archive. * @param group Used with previous versions of the material archive, which @@ -67,12 +72,20 @@ class MapStateReader * * @return Pointer to the associated material; otherwise @c 0 (not archived). */ - Material *material(materialarchive_serialid_t serialId, int group); + Material *material(materialarchive_serialid_t serialId, int group) const; /** - * Provides access to the side archive to use when deserializing the map state. + * Lookup a pointer to a Side with the given @a sideIndex. */ - dmu_lib::SideArchive &sideArchive(); + Side *side(int sideIndex) const; + + /** + * Lookup a pointer to a player with the given @a serialId. + */ + struct player_s *player(int serialId) const; + +public: /// @todo refactor away: + void addMobjToThingArchive(struct mobj_s *mobj, ThingArchive::SerialId); private: DENG2_PRIVATE(d) diff --git a/doomsday/plugins/common/include/mapstatewriter.h b/doomsday/plugins/common/include/mapstatewriter.h index ac83229094..3c32803462 100644 --- a/doomsday/plugins/common/include/mapstatewriter.h +++ b/doomsday/plugins/common/include/mapstatewriter.h @@ -22,6 +22,7 @@ #define LIBCOMMON_MAPSTATEWRITER_H #include "common.h" +#include "thingarchive.h" /** * Performs saved game map state serialization. @@ -32,26 +33,21 @@ class MapStateWriter { public: - /** - * @param excludePlayers Exclude player data. Used by Hexen when serializing hubs. - */ - MapStateWriter(bool excludePlayers = false); + MapStateWriter(ThingArchive &thingArchive); /** * Serialize the map state using the specified @a reader. */ void write(Writer *writer); - /** - * Returns a unique SerialId for the specified @a material. - */ - materialarchive_serialid_t serialIdFor(Material *material); - /** * Returns the writer to use when serializing the map state. */ Writer *writer(); + ThingArchive::SerialId serialIdFor(struct mobj_s *mobj); + materialarchive_serialid_t serialIdFor(Material *material); + private: DENG2_PRIVATE(d) }; diff --git a/doomsday/plugins/common/include/p_savedef.h b/doomsday/plugins/common/include/p_savedef.h index 77d467006a..8f1775009a 100644 --- a/doomsday/plugins/common/include/p_savedef.h +++ b/doomsday/plugins/common/include/p_savedef.h @@ -1,4 +1,4 @@ -/** @file common/p_savedef.h Common game-save state management. +/** @file p_savedef.h Common game-save state management. * * @authors Copyright © 2003-2013 Jaakko Keränen * @authors Copyright © 2005-2013 Daniel Swanson @@ -67,18 +67,6 @@ # define BASE_SLOT 6 # define AUTO_SLOT 7 -typedef union saveptr_u { - byte *b; - short *w; - int *l; - float *f; -} saveptr_t; - -typedef struct targetplraddress_s { - void **address; - struct targetplraddress_s *next; -} targetplraddress_t; - #endif #if !__JHEXEN__ diff --git a/doomsday/plugins/common/include/p_saveg.h b/doomsday/plugins/common/include/p_saveg.h index 0f2b3b5202..365c127981 100644 --- a/doomsday/plugins/common/include/p_saveg.h +++ b/doomsday/plugins/common/include/p_saveg.h @@ -1,4 +1,4 @@ -/** @file p_saveg.h Common game-save state management. +/** @file p_saveg.h Common game-save state management. * * @authors Copyright © 2003-2013 Jaakko Keränen * @authors Copyright © 2006-2013 Daniel Swanson @@ -22,23 +22,17 @@ #define LIBCOMMON_SAVESTATE_H #include "common.h" -#include "p_savedef.h" /// @todo remove me -#include "saveinfo.h" -#include "saveslots.h" -DENG_EXTERN_C int thingArchiveVersion; -DENG_EXTERN_C uint thingArchiveSize; -DENG_EXTERN_C dd_bool thingArchiveExcludePlayers; DENG_EXTERN_C int saveToRealPlayerNum[MAXPLAYERS]; -DENG_EXTERN_C SaveInfo const *curInfo; -DENG_EXTERN_C dd_bool playerHeaderOK; - #if __JHEXEN__ -DENG_EXTERN_C byte *saveBuffer; -#endif +typedef struct targetplraddress_s { + void **address; + struct targetplraddress_s *next; +} targetplraddress_t; -DENG_EXTERN_C SaveSlots *saveSlots; +DENG_EXTERN_C targetplraddress_t *targetPlayerAddrs; +#endif #ifdef __cplusplus extern "C" { @@ -50,32 +44,22 @@ void SV_Initialize(void); /// Shutdown this module. void SV_Shutdown(void); -void SV_SaveInfo_Read(SaveInfo *info, Reader *reader); - -#if __JHEXEN__ -/** - * Returns @c true iff a game-save is present and serialized @a map state is - * present for logical save @a slot. - */ -dd_bool SV_HxHaveMapStateForSlot(int slot, uint map); -#endif - /** - * Save the current game state to the specified @a slot number. + * Save the current game state to the specified @a slotNumber. * * @param description Textual description to include in the save info. Can be @c 0 * in which case a description will be auto-generated. * * @return @c true iff the game state was saved successfully. */ -dd_bool SV_SaveGame(int slot, char const *description); +dd_bool SV_SaveGame(int slotNumber, char const *description); /** - * Load the game state associated with the specified @a slot number. + * Load the game state associated with the specified @a slotNumber. * * @return @c true iff the game state was loaded successfully. */ -dd_bool SV_LoadGame(int slot); +dd_bool SV_LoadGame(int slotNumber); #if !__JHEXEN__ /** @@ -87,38 +71,13 @@ void SV_SaveGameClient(uint gameId); void SV_LoadGameClient(uint gameId); #endif -/// Unique identifier associated with each archived thing. #if __JHEXEN__ -typedef int ThingSerialId; -#else -typedef ushort ThingSerialId; -#endif - -void SV_ClearThingArchive(void); - /** - * To be called when writing a game state to acquire a unique identifier for - * the specified @a mobj from the thing archive. If the given mobj is already - * present in the archived, the existing archive Id is returned. - * - * @param mobj Mobj to lookup the archive Id for. - * - * @return Identifier for the specified mobj (may be zero). - */ -ThingSerialId SV_ThingArchiveId(mobj_t const *mobj); - -void SV_InitThingArchiveForSave(dd_bool excludePlayers); -void SV_InsertThingInArchive(mobj_t const *mobj, ThingSerialId thingId); - -/** - * To be called after reading a game state has been read to lookup a pointer - * to the mobj which is associated with the specified @a thingId. - * - * @param thingId Unique identifier for the mobj in the thing archive. - * - * @return Pointer to the associated mobj; otherwise @c 0 (not archived). + * Returns @c true iff a game-save is present and serialized @a map state is + * present for logical save @a slotNumber. */ -mobj_t *SV_GetArchiveThing(ThingSerialId thingid, void *address); +dd_bool SV_HxHaveMapStateForSlot(int slotNumber, uint map); +#endif /** * Update mobj flag values from those used in legacy game-save formats @@ -144,33 +103,13 @@ typedef struct playerheader_s { #if __JHEXEN__ int numArmorTypes; #endif -} playerheader_t; - -void SV_WritePlayerHeader(Writer *writer); -void SV_ReadPlayerHeader(Reader *reader, int saveVersion); - -playerheader_t *SV_GetPlayerHeader(void); - -#if __JHEXEN__ -void SV_HxSaveHubMap(void); -void SV_HxLoadHubMap(void); - -typedef struct { - player_t player; - uint numInventoryItems[NUM_INVENTORYITEM_TYPES]; - inventoryitemtype_t readyItem; -} playerbackup_t; -void SV_HxBackupPlayersInHub(playerbackup_t playerBackup[MAXPLAYERS]); - -/** - * @param playerBackup Player state backup. - * @param mapEntrance Logical entry point number used to enter the map. - */ -void SV_HxRestorePlayersInHub(playerbackup_t playerBackup[MAXPLAYERS], uint mapEntrance); +#ifdef __cplusplus + void write(Writer *writer); + void read(Reader *reader, int saveVersion); #endif +} playerheader_t; -void SV_InitThingArchiveForLoad(uint size); #if __JHEXEN__ void SV_InitTargetPlayers(void); void SV_ClearTargetPlayers(void); @@ -180,14 +119,34 @@ void SV_ClearTargetPlayers(void); } // extern "C" #endif -#ifdef __cplusplus +# ifdef __cplusplus +#include "gamestatereader.h" + class MapStateReader; class MapStateWriter; +class SaveInfo; +class SaveSlots; -#if __JHEXEN__ -void SV_WriteMovePoly(struct polyevent_s const *movepoly, MapStateWriter *msw); -int SV_ReadMovePoly(struct polyevent_s *movepoly, MapStateReader *msr); -#endif +/** + * Returns the game's SaveSlots. + */ +SaveSlots &SV_SaveSlots(); + +/** + * Declare a new saved game state reader/interpreter. + * + * @param recognizer Format recognizer function. + * @param maker Reader instantiator function. + */ +void SV_DeclareGameStateReader(GameStateRecognizeFunc recognizer, GameStateReaderMakeFunc maker); + +/** + * Determines whether the game session associated with save @a info is interpretable as a + * potentially loadable savegame state. + * + * @param info SaveInfo to attempt to read game session header into. + */ +bool SV_RecognizeGameState(SaveInfo &info); void SV_WriteLine(Line *line, MapStateWriter *msw); void SV_ReadLine(Line *line, MapStateReader *msr); @@ -195,11 +154,11 @@ void SV_ReadLine(Line *line, MapStateReader *msr); void SV_WriteSector(Sector *sec, MapStateWriter *msw); void SV_ReadSector(Sector *sec, MapStateReader *msr); -#endif // __cplusplus +# if __JHEXEN__ +void SV_HxSaveHubMap(void); -dd_bool SV_OpenGameSaveFile(Str const *fileName, dd_bool write); -#if __JHEXEN__ -void SV_OpenMapSaveFile(Str const *path); -#endif +void SV_HxLoadHubMap(void); +# endif // __JHEXEN__ +# endif // __cplusplus #endif // LIBCOMMON_SAVESTATE_H diff --git a/doomsday/plugins/common/include/p_saveio.h b/doomsday/plugins/common/include/p_saveio.h index ea503478ac..bd66b290ea 100644 --- a/doomsday/plugins/common/include/p_saveio.h +++ b/doomsday/plugins/common/include/p_saveio.h @@ -21,10 +21,10 @@ #ifndef LIBCOMMON_SAVESTATE_INPUT_OUTPUT_H #define LIBCOMMON_SAVESTATE_INPUT_OUTPUT_H -#include "saveinfo.h" #include "api_materialarchive.h" -#include "lzss.h" #include "p_savedef.h" +#include +#include "lzss.h" typedef enum savestatesegment_e { ASEG_MAP_HEADER = 102, // Hexen only @@ -43,33 +43,58 @@ typedef enum savestatesegment_e { ASEG_WORLDSCRIPTDATA // Hexen only } savestatesegment_t; -#ifdef __cplusplus -extern "C" { +#if __JHEXEN__ +typedef union saveptr_u { + byte *b; + short *w; + int *l; + float *f; +} saveptr_t; #endif -void SV_InitIO(void); -void SV_ShutdownIO(void); +void SV_InitIO(); +void SV_ShutdownIO(); + +/** + * Create the saved game directories. + */ +void SV_SetupSaveDirectory(de::Path); + +de::Path SV_SavePath(); -void SV_ConfigureSavePaths(void); -char const *SV_SavePath(void); #if !__JHEXEN__ -char const *SV_ClientSavePath(void); +de::Path SV_ClientSavePath(); #endif /* * File management */ -LZFILE *SV_OpenFile(Str const *filePath, char const *mode); -void SV_CloseFile(void); -LZFILE *SV_File(void); -dd_bool SV_ExistingFile(Str const *filePath); -int SV_RemoveFile(Str const *filePath); -void SV_CopyFile(Str const *srcPath, Str const *destPath); +LZFILE *SV_OpenFile(de::Path filePath, de::String mode); + +void SV_CloseFile(); + +LZFILE *SV_File(); + +bool SV_ExistingFile(de::Path filePath); + +int SV_RemoveFile(de::Path filePath); + +void SV_CopyFile(de::Path srcPath, de::Path destPath); + +bool SV_OpenGameSaveFile(de::Path fileName, bool write); + +#if __JHEXEN__ +bool SV_OpenMapSaveFile(de::Path path); +#endif #if __JHEXEN__ -saveptr_t *SV_HxSavePtr(void); +saveptr_t *SV_HxSavePtr(); + void SV_HxSetSaveEndPtr(void *endPtr); -dd_bool SV_HxBytesLeft(void); + +size_t SV_HxBytesLeft(); + +void SV_HxReleaseSaveBuffer(); #endif // __JHEXEN__ /** @@ -81,21 +106,20 @@ dd_bool SV_HxBytesLeft(void); void SV_AssertSegment(int segmentId); void SV_BeginSegment(int segmentId); + void SV_EndSegment(); -void SV_WriteConsistencyBytes(void); -void SV_ReadConsistencyBytes(void); +void SV_WriteConsistencyBytes(); + +void SV_ReadConsistencyBytes(); /** * Seek forward @a offset bytes in the save file. */ void SV_Seek(uint offset); -Writer *SV_NewWriter(void); -Reader *SV_NewReader(void); +Writer *SV_NewWriter(); -#ifdef __cplusplus -} // extern "C" -#endif +Reader *SV_NewReader(); #endif // LIBCOMMON_SAVESTATE_INPUT_OUTPUT_H diff --git a/doomsday/plugins/common/include/p_xgline.h b/doomsday/plugins/common/include/p_xgline.h index f6d50e4d37..496cfe7230 100644 --- a/doomsday/plugins/common/include/p_xgline.h +++ b/doomsday/plugins/common/include/p_xgline.h @@ -32,6 +32,10 @@ #include "doomsday.h" #include "xgclass.h" +#ifdef __cplusplus +# include "mapstatereader.h" +# include "mapstatewriter.h" +#endif // Line events. #define XLE_CHAIN 0x001 @@ -326,10 +330,10 @@ typedef struct { } xgline_t; // The XG line Classes -extern struct xgclass_s xgClasses[]; +DENG_EXTERN_C struct xgclass_s xgClasses[]; // Used as the activator if there is no real activator. -extern struct mobj_s dummyThing; +DENG_EXTERN_C struct mobj_s dummyThing; #ifdef __cplusplus extern "C" { @@ -368,10 +372,10 @@ int XL_HitLine(Line *line, int sideNum, struct mobj_s *thing); int XG_RandomInt(int min, int max); -void SV_WriteXGLine(Line *li, Writer *writer); -void SV_ReadXGLine(Line *li, Reader *reader, int mapVersion); - #ifdef __cplusplus +void SV_WriteXGLine(Line *li, MapStateWriter *msw); +void SV_ReadXGLine(Line *li, MapStateReader *msr); + } // extern "C" #endif diff --git a/doomsday/plugins/common/include/polyobjs.h b/doomsday/plugins/common/include/polyobjs.h index ccc00e7521..8d822dee48 100644 --- a/doomsday/plugins/common/include/polyobjs.h +++ b/doomsday/plugins/common/include/polyobjs.h @@ -1,4 +1,4 @@ -/** @file polyobjs.h Polyobject thinkers and management. +/** @file polyobjs.h Polyobject thinkers and management. * * @authors Copyright © 2003-2013 Jaakko Keränen * @authors Copyright © 2005-2013 Daniel Swanson @@ -34,6 +34,9 @@ typedef enum { PODOOR_SWING } podoortype_t; +/** + * @note Used with both @ref T_RotatePoly() and @ref T_MovePoly() + */ typedef struct polyevent_s { thinker_t thinker; int polyobj; // tag @@ -48,6 +51,11 @@ typedef struct polyevent_s { #endif } polyevent_t; +#ifdef __cplusplus +void SV_WriteMovePoly(polyevent_t const *movepoly, MapStateWriter *msw); +int SV_ReadMovePoly(polyevent_t *movepoly, MapStateReader *msr); +#endif + typedef struct polydoor_s { thinker_t thinker; int polyobj; // tag diff --git a/doomsday/plugins/common/include/saveinfo.h b/doomsday/plugins/common/include/saveinfo.h index eae25e9cf6..5112eee580 100644 --- a/doomsday/plugins/common/include/saveinfo.h +++ b/doomsday/plugins/common/include/saveinfo.h @@ -21,10 +21,9 @@ #ifndef LIBCOMMON_SAVEINFO #define LIBCOMMON_SAVEINFO -#include "doomsday.h" #include "common.h" - -#ifdef __cplusplus +#include +#include /** * Represents a saved game session state. @@ -33,44 +32,108 @@ */ class SaveInfo { -public: /// @todo make private: - Str _description; - uint _sessionId; - int _magic; - int _version; - gamemode_t _gameMode; - Uri *_mapUri; +public: + /// Logical game session status: + enum SessionStatus { + Loadable, + Incompatible, + Unused + }; + #if !__JHEXEN__ - int _mapTime; - byte _players[MAXPLAYERS]; + // Info data about players present (or not) in the game session. + typedef byte Players[MAXPLAYERS]; #endif - GameRuleset _gameRules; public: - SaveInfo(); + SaveInfo(de::String const &fileName = ""); SaveInfo(SaveInfo const &other); - ~SaveInfo(); - static SaveInfo *newWithCurrentSessionMetadata(Str const *description); + static SaveInfo *newWithCurrentSessionMetadata(de::String const &fileName = "", + de::String const &userDescription = ""); SaveInfo &operator = (SaveInfo const &other); /** - * Determines whether the saved game session is compatibile with the current - * game session (and @em should therefore be loadable). + * Determines the logical status of the saved game session. + * + * @see statusAsText() + */ + SessionStatus status() const; + + /** + * Returns a textual representation of the current status of the saved game session. + * + * @see status() + */ + de::String statusAsText() const; + + /** + * Composes a human-friendly, styled, textual description of the saved game session. + */ + de::String description() const; + + /** + * Determines whether a saved game session exists. However, it may not be compatible with + * the current game session. + * + * @see gameSessionIsLoadable() + */ + bool haveGameSession() const; + + /** + * Determines whether a saved game session exists and is compatibile with the current game + * session (and @em should therefore be loadable). + */ + bool gameSessionIsLoadable() const; + + /** + * Attempt to update the save info from the named saved game session file. If the save path + * is invalid, unreachable, or the game state is not recognized -- the save info is returned + * to a valid but non-loadable state. + * + * @see gameSessionIsLoadable() + */ + void updateFromFile(); + + /** + * Returns the name of the resource file (with extension) containing the game session header. + */ + de::String fileName() const; + void setFileName(de::String newName); + + /** + * Returns the name of the resource file (with extension) containing the map session state. + * + * @param map Logical map index. + * + * @see fileName() + */ + de::String fileNameForMap(uint map) const; + + /** + * Update the metadata associated with the save using values derived from the current game + * session. Note that this does @em not affect the copy of this save on disk. */ - bool isLoadable(); + void applyCurrentSessionMetadata(); + + /** + * Returns the unique "identity key" of the game session. + */ + de::String const &gameIdentityKey() const; + void setGameIdentityKey(de::String newGameIdentityKey); /** * Returns the logical version of the serialized game session state. */ int version() const; + void setVersion(int newVersion); /** - * Returns the textual description of the game session (provided by the user). + * Returns the textual description of the game session provided by the user. */ - Str const *description() const; - void setDescription(Str const *newDesc); + de::String const &userDescription() const; + void setUserDescription(de::String newUserDescription); /** * @see G_GenerateSessionId() @@ -82,76 +145,47 @@ class SaveInfo * Returns the URI of the @em current map of the game session. */ Uri const *mapUri() const; + void setMapUri(Uri const *newMapUri); #if !__JHEXEN__ + /** * Returns the expired time in tics since the @em current map of the game session began. */ int mapTime() const; -#endif + void setMapTime(int newMapTime); /** - * Returns the game ruleset for the game session. + * Returns the player info data for the game session. */ - GameRuleset const &gameRules() const; + Players const &players() const; + void setPlayers(Players const &newPlayers); - /** - * Serializes the game session info using @a writer. - */ - void write(Writer *writer) const; +#endif // !__JHEXEN__ /** - * Deserializes the game session info using @a reader. + * Returns the game ruleset for the game session. */ - void read(Reader *reader); + GameRuleset const &gameRules() const; + void setGameRules(GameRuleset const &newRules); /** - * Hexen-specific version for deserializing legacy v.9 game session info. + * Serializes the game session header using @a writer. */ -#if __JHEXEN__ - void read_Hx_v9(Reader *reader); -#endif + void write(Writer *writer) const; /** - * Update the metadata associated with the save using values derived from the - * current game session. Note that this does @em not affect the copy of this save - * on disk. + * Deserializes the game session header using @a reader. */ - void applyCurrentSessionMetadata(); + void read(Reader *reader); public: /// @todo refactor away: int magic() const; -}; - -#endif // __cplusplus - -// C wrapper API --------------------------------------------------------------- + void setMagic(int newMagic); + static SaveInfo *fromReader(Reader *reader); -#ifdef __cplusplus -extern "C" { -#else -typedef void *SaveInfo; -#endif - -SaveInfo *SaveInfo_New(void); -SaveInfo *SaveInfo_Dup(SaveInfo const *other); - -void SaveInfo_Delete(SaveInfo *info); - -SaveInfo *SaveInfo_Copy(SaveInfo *info, SaveInfo const *other); -dd_bool SaveInfo_IsLoadable(SaveInfo *info); -Str const *SaveInfo_Description(SaveInfo const *info); -void SaveInfo_SetDescription(SaveInfo *info, Str const *newName); -uint SaveInfo_SessionId(SaveInfo const *info); -void SaveInfo_SetSessionId(SaveInfo *info, uint newSessionId); -void SaveInfo_Write(SaveInfo *info, Writer *writer); -void SaveInfo_Read(SaveInfo *info, Reader *reader); -#if __JHEXEN__ -void SaveInfo_Read_Hx_v9(SaveInfo *info, Reader *reader); -#endif - -#ifdef __cplusplus -} // extern "C" -#endif +private: + DENG2_PRIVATE(d) +}; #endif // LIBCOMMON_SAVEINFO diff --git a/doomsday/plugins/common/include/saveslots.h b/doomsday/plugins/common/include/saveslots.h index a1ca14884b..e24211fb06 100644 --- a/doomsday/plugins/common/include/saveslots.h +++ b/doomsday/plugins/common/include/saveslots.h @@ -20,12 +20,13 @@ #ifndef LIBCOMMON_SAVESLOTS_H #define LIBCOMMON_SAVESLOTS_H +#ifdef __cplusplus #include "common.h" -#include "saveinfo.h" - -#ifdef __cplusplus #include +#include + +class SaveInfo; /** * Maps saved games into a finite set of "save slots". @@ -42,41 +43,92 @@ class SaveSlots /// An invalid slot was specified. @ingroup errors DENG2_ERROR(InvalidSlotError); -public: /** - * @param numSlots Number of logical slots. + * Logical save slot. */ - SaveSlots(int numSlots); - - void clearAllSaveInfo(); + class Slot + { + public: + /// Required SaveInfo is missing. @ingroup errors + DENG2_ERROR(MissingInfoError); + + public: + Slot(de::String const &fileName = ""); + + /** + * Returns the save game file name bound to the logical save slot. + */ + de::String fileName() const; + + /** + * Change the save game file name bound to the logical save slot. + * + * @param newName New save game file name to be bound. + */ + void bindFileName(de::String newName); + + /** + * Returns @c true iff a saved game state exists for the logical save slot. + */ + bool isUsed() const; + + /** + * Returns @c true iff save info exists for the logical save slot. + */ + bool hasSaveInfo() const; + + /** + * Clear the save info for the logical save slot. + * + * @see hasSaveInfo() + */ + void clearSaveInfo(); + + /** + * Returns the SaveInfo associated with the logical save slot. + * + * @see hasSaveInfo() + */ + SaveInfo &saveInfo() const; + + /** + * Replace the existing save info with @a newInfo. + * + * @param newInfo New SaveInfo to replace with. Ownership is given. + */ + void replaceSaveInfo(SaveInfo *newInfo); + + private: + DENG2_PRIVATE(d) + }; +public: /** - * Force an update of the cached game-save info. To be called (sparingly) at strategic - * points when an update is necessary (e.g., the game-save paths have changed). - * - * @note It is not necessary to call this after a game-save is made, this module will do - * so automatically. + * @param numSlots Number of logical slots. */ - void updateAllSaveInfo(); + SaveSlots(int numSlots); /** * Returns the total number of logical save slots. */ int slotCount() const; + /// @see slotCount() + inline int size() const { return slotCount(); } + /** - * Returns @c true iff @a slot is a valid logical slot number (in range). + * Returns @c true iff @a value is interpretable as logical slot number (in range). * * @see slotCount() */ - bool isValidSlot(int slot) const; + bool isKnownSlot(int value) const; /** - * Composes the textual identifier/name for save @a slot. + * Composes the textual, symbolic identifier/name for save @a slotNumber. * * @see parseSlotIdentifier() */ - AutoStr *composeSlotIdentifier(int slot) const; + de::String slotIdentifier(int slotNumber) const; /** * Parse @a str and determine whether it references a logical game-save slot. @@ -93,69 +145,63 @@ class SaveSlots * * @return The parsed slot number if valid; otherwise @c -1 * - * @see composeSlotIdentifier() + * @see slotIdentifier() */ - int parseSlotIdentifier(char const *str) const; + int parseSlotIdentifier(de::String str) const; - /** - * Lookup a save slot by searching for a match on game-save description. The search is in - * ascending logical slot order 0...N (where N is the number of available save slots). - * - * @param description Description of the game-save to look for (not case sensitive). - * - * @return Logical slot number of the found game-save else @c -1 - */ - int findSlotWithSaveDescription(char const *description) const; + /// @see slot() + inline Slot &operator [] (int slotNumber) { + return slot(slotNumber); + } /** - * Returns @c true iff a saved game state exists for save @a slot. + * Returns the logical save slot associated with @a slotNumber. + * + * @see isKnownSlot() */ - bool slotInUse(int slot) const; + Slot &slot(int slotNumber) const; /** - * Returns @c true iff save @a slot is user-writable (i.e., not a special slot, such as - * the @em auto and @em base slots). + * Clears save info for all logical save slots. */ - bool slotIsUserWritable(int slot) const; + void clearAll(); /** - * Returns the SaveInfo associated with the logical save @a slot. + * Force an update of the cached game-save info. To be called (sparingly) at strategic + * points when an update is necessary (e.g., the game-save paths have changed). * - * @see isValidSlot() + * @note It is not necessary to call this after a game-save is made, this module will do + * so automatically. */ - SaveInfo &saveInfo(int slot) const; - - inline SaveInfo *saveInfoPtr(int slot) const { - return isValidSlot(slot)? &saveInfo(slot) : 0; - } + void updateAll(); /** - * Deletes all save game files associated with the specified save @a slot. + * Lookup a save slot by searching for a match on game-save description. The search is in + * ascending logical slot order 0...N (where N is the number of available save slots). + * + * @param description Description of the game-save to look for (not case sensitive). * - * @see isValidSlot() + * @return Logical slot number of the found game-save else @c -1 */ - void clearSlot(int slot); + int findSlotWithUserSaveDescription(de::String description) const; /** - * @param slot Slot to replace the info of. - * @param newInfo New SaveInfo to replace with. Ownership is given. + * Returns @c true iff save @a slotNumber is user-writable (i.e., not a special slot, such + * as the @em auto and @em base slots). */ - void replaceSaveInfo(int slot, SaveInfo *newInfo); + bool slotIsUserWritable(int slotNumber) const; /** - * Copies all the save game files from one slot to another. + * Deletes all save game files associated with the specified save @a slotNumber. + * + * @see isKnownSlot() */ - void copySlot(int sourceSlot, int destSlot); + void clearSlot(int slotNumber); /** - * Compose the (possibly relative) file path to the game state associated with save @a slot. - * - * @param slot Slot to compose the identifier of. - * @param map If @c >= 0 include this logical map index in the composed path. - * - * @return The composed path if reachable (else a zero-length string). + * Copies all the save game files from one slot to another. */ - AutoStr *composeSavePathForSlot(int slot, int map = -1) const; + void copySlot(int sourceSlotNumber, int destSlotNumber); /** * Register the console commands and variables of this module. @@ -168,38 +214,8 @@ class SaveSlots private: DENG2_PRIVATE(d) }; -#endif // __cplusplus - -// C wrapper API --------------------------------------------------------------- - -#ifdef __cplusplus -extern "C" { -#else -typedef void *SaveSlots; -#endif - -SaveSlots *SaveSlots_New(int slotCount); -void SaveSlots_Delete(SaveSlots *sslots); - -void SaveSlots_ClearAllSaveInfo(SaveSlots *sslots); -void SaveSlots_UpdateAllSaveInfo(SaveSlots *sslots); -int SaveSlots_SlotCount(SaveSlots const *sslots); -dd_bool SaveSlots_IsValidSlot(SaveSlots const *sslots, int slot); -AutoStr *SaveSlots_ComposeSlotIdentifier(SaveSlots const *sslots, int slot); -int SaveSlots_ParseSlotIdentifier(SaveSlots const *sslots, char const *str); -int SaveSlots_FindSlotWithSaveDescription(SaveSlots const *sslots, char const *description); -dd_bool SaveSlots_SlotInUse(SaveSlots const *sslots, int slot); -dd_bool SaveSlots_SlotIsUserWritable(SaveSlots const *sslots, int slot); -SaveInfo *SaveSlots_SaveInfo(SaveSlots *sslots, int slot); -void SaveSlots_ReplaceSaveInfo(SaveSlots *sslots, int slot, SaveInfo *newInfo); -void SaveSlots_ClearSlot(SaveSlots *sslots, int slot); -void SaveSlots_CopySlot(SaveSlots *sslots, int sourceSlot, int destSlot); -AutoStr *SaveSlots_ComposeSavePathForSlot(SaveSlots const *sslots, int slot, int map); - -void SaveSlots_ConsoleRegister(); -#ifdef __cplusplus -} // extern "C" -#endif +typedef SaveSlots::Slot SaveSlot; +#endif // __cplusplus #endif // LIBCOMMON_SAVESLOTS_H diff --git a/doomsday/plugins/common/include/thingarchive.h b/doomsday/plugins/common/include/thingarchive.h new file mode 100644 index 0000000000..8d4fd659a3 --- /dev/null +++ b/doomsday/plugins/common/include/thingarchive.h @@ -0,0 +1,81 @@ +/** @file thingarchive.h Map save state thing archive. + * + * @authors Copyright © 2003-2013 Jaakko Keränen + * @authors Copyright © 2005-2013 Daniel Swanson + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef LIBCOMMON_THINGARCHIVE_H +#define LIBCOMMON_THINGARCHIVE_H + +#include "common.h" + +/** + * @ingroup libcommon + */ +class ThingArchive +{ +public: + /// Unique identifier associated with each archived thing. +#if __JHEXEN__ + typedef int SerialId; +#else + typedef ushort SerialId; +#endif + +public: + ThingArchive(int version = 0); + + int version() const; + + bool excludePlayers() const; + + uint size() const; + + void clear(); + + void initForLoad(uint size); + + void initForSave(bool excludePlayers); + + /** + * To be called when writing a game state to acquire a unique identifier for + * the specified @a mobj from the thing archive. If the given mobj is already + * present in the archived, the existing archive Id is returned. + * + * @param mobj Mobj to lookup the archive Id for. + * + * @return Identifier for the specified mobj (may be zero). + */ + SerialId serialIdFor(struct mobj_s const *mobj); + + void insert(struct mobj_s const *mobj, SerialId serialId); + + /** + * To be called after reading a game state has been read to lookup a pointer + * to the mobj which is associated with the specified @a thingId. + * + * @param serialId Unique identifier for the mobj in the thing archive. + * + * @return Pointer to the associated mobj; otherwise @c 0 (not archived). + */ + struct mobj_s *mobj(SerialId serialId, void *address); + +private: + DENG2_PRIVATE(d) +}; + +#endif // LIBCOMMON_P_ACTOR_H diff --git a/doomsday/plugins/common/include/x_hair.h b/doomsday/plugins/common/include/x_hair.h index 4a937079ad..1bbcb435f8 100644 --- a/doomsday/plugins/common/include/x_hair.h +++ b/doomsday/plugins/common/include/x_hair.h @@ -23,9 +23,12 @@ #ifndef LIBCOMMON_HUD_CROSSHAIR_H #define LIBCOMMON_HUD_CROSSHAIR_H - #define NUM_XHAIRS (5) +#ifdef __cplusplus +extern "C" { +#endif + void X_Drawer(int player); /** @@ -33,4 +36,8 @@ void X_Drawer(int player); */ void X_Register(void); +#ifdef __cplusplus +} // extern "C" +#endif + #endif // LIBCOMMON_HUD_CROSSHAIR_H diff --git a/doomsday/plugins/common/src/d_netcl.c b/doomsday/plugins/common/src/d_netcl.c index 88436e916b..f571144351 100644 --- a/doomsday/plugins/common/src/d_netcl.c +++ b/doomsday/plugins/common/src/d_netcl.c @@ -45,7 +45,7 @@ void NetCl_UpdateGameState(Reader* msg) Uri *mapUri; uint gsEpisode = 0; uint gsMap = 0; - uint gsMapEntrance = 0; + //uint gsMapEntrance = 0; byte configFlags = 0; //byte gsDeathmatch = 0; //byte gsMonsters = 0; diff --git a/doomsday/plugins/common/src/d_netsv.c b/doomsday/plugins/common/src/d_netsv.c index 8a75b15683..701444c663 100644 --- a/doomsday/plugins/common/src/d_netsv.c +++ b/doomsday/plugins/common/src/d_netsv.c @@ -1365,12 +1365,12 @@ static void NetSv_HitFloorCallback(mobj_t* mo, void* param) P_HitFloor(mo); } -void NetSv_DoFloorHit(int player, Reader* msg) +void NetSv_DoFloorHit(int player, Reader *msg) { - player_t* plr = &players[player]; - mobj_t* mo; + player_t *plr = &players[player]; + mobj_t *mo; coord_t pos[3]; - coord_t mom[3]; + //coord_t mom[3]; if(player < 0 || player >= MAXPLAYERS) return; @@ -1383,9 +1383,9 @@ void NetSv_DoFloorHit(int player, Reader* msg) pos[VZ] = Reader_ReadFloat(msg); // The momentum is included, although we don't really need it. - mom[MX] = Reader_ReadFloat(msg); - mom[MY] = Reader_ReadFloat(msg); - mom[MZ] = Reader_ReadFloat(msg); + /*mom[MX] =*/ Reader_ReadFloat(msg); + /*mom[MY] =*/ Reader_ReadFloat(msg); + /*mom[MZ] =*/ Reader_ReadFloat(msg); NetSv_TemporaryPlacedCallback(mo, 0, pos, mo->angle, NetSv_HitFloorCallback); } @@ -1606,7 +1606,7 @@ D_CMD(MapCycle) map = NetSv_ScanCycle(cycleIndex = 0, 0); if(map < 0) { - App_Log(DE2_SCR_ERROR, "MapCycle \"%s\" is invalid.\n", mapCycle); + App_Log(DE2_SCR_ERROR, "MapCycle \"%s\" is invalid.", mapCycle); return false; } for(i = 0; i < MAXPLAYERS; ++i) diff --git a/doomsday/plugins/common/src/fi_lib.c b/doomsday/plugins/common/src/fi_lib.c index e68a97def4..a6a963a3a4 100644 --- a/doomsday/plugins/common/src/fi_lib.c +++ b/doomsday/plugins/common/src/fi_lib.c @@ -632,7 +632,7 @@ D_CMD(StartFinale) return false; if(!Def_Get(DD_DEF_FINALE, argv[1], &fin)) { - App_Log(DE2_SCR_ERROR, "Script '%s' is not defined.\n", argv[1]); + App_Log(DE2_SCR_ERROR, "Script '%s' is not defined.", argv[1]); return false; } G_SetGameAction(GA_NONE); diff --git a/doomsday/plugins/common/src/g_controls.c b/doomsday/plugins/common/src/g_controls.c index 19a045edf1..19a74efd8c 100644 --- a/doomsday/plugins/common/src/g_controls.c +++ b/doomsday/plugins/common/src/g_controls.c @@ -73,9 +73,9 @@ typedef enum joyaxis_e { D_CMD(DefaultGameBinds); // Input devices; state controls. -static int povangle = -1; // -1 means centered (really 0 - 7). -static float mousex; -static float mousey; +//static int povangle = -1; ///< @c -1= means centered (really 0 - 7). +//static float mousex; +//static float mousey; // Player control state. static pcontrolstate_t controlStates[MAXPLAYERS]; diff --git a/doomsday/plugins/common/src/g_game.c b/doomsday/plugins/common/src/g_game.cpp similarity index 71% rename from doomsday/plugins/common/src/g_game.c rename to doomsday/plugins/common/src/g_game.cpp index c85a746682..b51166a8ae 100644 --- a/doomsday/plugins/common/src/g_game.c +++ b/doomsday/plugins/common/src/g_game.cpp @@ -20,53 +20,49 @@ * 02110-1301 USA */ -#include -#include -#include -#include -#include -#include -#include - #include "common.h" +#include "g_common.h" +#include "am_map.h" +#include "d_net.h" #include "dmu_lib.h" #include "fi_lib.h" -#include "hu_lib.h" -#include "p_saveg.h" -#include "p_sound.h" #include "g_controls.h" #include "g_eventsequence.h" -#include "p_mapsetup.h" -#include "p_user.h" -#include "p_actor.h" -#include "p_tick.h" -#include "am_map.h" -#include "hu_stuff.h" -#include "hu_menu.h" -#include "hu_log.h" +#include "g_update.h" +#include "hu_lib.h" #include "hu_chat.h" +#include "hu_inventory.h" +#include "hu_log.h" +#include "hu_menu.h" #include "hu_msg.h" #include "hu_pspr.h" -#include "g_common.h" -#include "g_update.h" -#include "g_eventsequence.h" -#include "d_net.h" -#include "x_hair.h" -#include "player.h" -#include "r_common.h" -#include "p_map.h" -#include "p_mapspec.h" -#include "p_start.h" +#include "hu_stuff.h" +#include "p_actor.h" #include "p_inventory.h" -#if __JHERETIC__ || __JHEXEN__ -# include "hu_inventory.h" -# include "p_inventory.h" -#endif +#include "p_map.h" #if __JHEXEN__ # include "p_mapinfo.h" #endif -#include "d_net.h" +#include "p_mapsetup.h" +#include "p_mapspec.h" +#include "p_saveg.h" +#include "p_saveio.h" +#include "p_sound.h" +#include "p_start.h" +#include "p_tick.h" +#include "p_user.h" +#include "player.h" +#include "r_common.h" +#include "x_hair.h" + +#include "saveslots.h" +#include +#include +#include +#include +#include +#include #define BODYQUEUESIZE (32) @@ -80,35 +76,36 @@ struct missileinfo_s { MonsterMissileInfo[] = { #if __JDOOM__ || __JDOOM64__ - {MT_BRUISERSHOT, {15, 20}}, - {MT_HEADSHOT, {10, 20}}, - {MT_TROOPSHOT, {10, 20}}, + { MT_BRUISERSHOT, {15, 20} }, + { MT_HEADSHOT, {10, 20} }, + { MT_TROOPSHOT, {10, 20} }, # if __JDOOM64__ - {MT_BRUISERSHOTRED, {15, 20}}, - {MT_NTROSHOT, {20, 40}}, + { MT_BRUISERSHOTRED, {15, 20} }, + { MT_NTROSHOT, {20, 40} }, # endif #elif __JHERETIC__ - {MT_IMPBALL, {10, 20}}, - {MT_MUMMYFX1, {9, 18}}, - {MT_KNIGHTAXE, {9, 18}}, - {MT_REDAXE, {9, 18}}, - {MT_BEASTBALL, {12, 20}}, - {MT_WIZFX1, {18, 24}}, - {MT_SNAKEPRO_A, {14, 20}}, - {MT_SNAKEPRO_B, {14, 20}}, - {MT_HEADFX1, {13, 20}}, - {MT_HEADFX3, {10, 18}}, - {MT_MNTRFX1, {20, 26}}, - {MT_MNTRFX2, {14, 20}}, - {MT_SRCRFX1, {20, 28}}, - {MT_SOR2FX1, {20, 28}}, -#endif - {-1, {-1, -1}} // Terminator + { MT_IMPBALL, {10, 20} }, + { MT_MUMMYFX1, {9, 18} }, + { MT_KNIGHTAXE, {9, 18} }, + { MT_REDAXE, {9, 18} }, + { MT_BEASTBALL, {12, 20} }, + { MT_WIZFX1, {18, 24} }, + { MT_SNAKEPRO_A, {14, 20} }, + { MT_SNAKEPRO_B, {14, 20} }, + { MT_HEADFX1, {13, 20} }, + { MT_HEADFX3, {10, 18} }, + { MT_MNTRFX1, {20, 26} }, + { MT_MNTRFX2, {14, 20} }, + { MT_SRCRFX1, {20, 28} }, + { MT_SOR2FX1, {20, 28} }, +#endif + { mobjtype_t(-1), {-1, -1} } }; #endif D_CMD(CycleTextureGamma); D_CMD(DeleteGameSave); +D_CMD(InspectGameSave); D_CMD(EndGame); D_CMD(HelpScreen); D_CMD(ListMaps); @@ -120,44 +117,45 @@ D_CMD(SaveGame); D_CMD(OpenSaveMenu); D_CMD(WarpMap); -void G_PlayerReborn(int player); -void G_DoReborn(int playernum); +void G_PlayerReborn(int player); +void G_DoReborn(int playernum); dd_bool G_StartDebriefing(); -typedef struct { +struct loadmap_params_t +{ Uri *mapUri; dd_bool revisit; -} loadmap_params_t; -int G_DoLoadMap(loadmap_params_t* params); - -void G_DoLoadGame(void); -void G_DoPlayDemo(void); -void G_DoMapCompleted(void); -void G_DoEndDebriefing(void); -void G_DoVictory(void); -void G_DoLeaveMap(void); -void G_DoRestartMap(void); -void G_DoSaveGame(void); -void G_DoScreenShot(void); -void G_DoQuitGame(void); - -void G_StopDemo(void); +}; +int G_DoLoadMap(loadmap_params_t *parm); + +void G_DoLoadGame(); +void G_DoPlayDemo(); +void G_DoMapCompleted(); +void G_DoEndDebriefing(); +void G_DoVictory(); +void G_DoLeaveMap(); +void G_DoRestartMap(); +void G_DoSaveGame(); +void G_DoScreenShot(); +void G_DoQuitGame(); + +void G_StopDemo(); /** * Updates game status cvars for the specified player. */ -void G_UpdateGSVarsForPlayer(player_t* pl); +void G_UpdateGSVarsForPlayer(player_t *pl); /** * Updates game status cvars for the current map. */ -void G_UpdateGSVarsForMap(void); +void G_UpdateGSVarsForMap(); -void R_LoadVectorGraphics(void); +void R_LoadVectorGraphics(); -int Hook_DemoStop(int hookType, int val, void* parameters); +int Hook_DemoStop(int hookType, int val, void *parm); -static void G_InitNewGame(void); +static void G_InitNewGame(); game_config_t cfg; // The global cfg. @@ -178,7 +176,7 @@ dd_bool secretExit; #endif #if __JHEXEN__ -uint mapHub = 0; +uint mapHub; #endif dd_bool monsterInfight; @@ -188,20 +186,20 @@ player_t players[MAXPLAYERS]; int totalKills, totalItems, totalSecret; // For intermission. dd_bool singledemo; // Quit after playing a demo from cmdline. -dd_bool briefDisabled = false; +dd_bool briefDisabled; dd_bool precache = true; // If @c true, load all graphics at start. -dd_bool customPal = false; // If @c true, a non-IWAD palette is in use. +dd_bool customPal; // If @c true, a non-IWAD palette is in use. #if __JDOOM__ || __JDOOM64__ || __JHERETIC__ wbstartstruct_t wmInfo; // Params for world map / intermission. #endif // Game Action Variables: -int gaSaveGameSlot = 0; -dd_bool gaSaveGameGenerateName = true; -ddstring_t* gaSaveGameName; -int gaLoadGameSlot = 0; +int gaSaveGameSlot; +dd_bool gaSaveGameGenerateDescription = true; +ddstring_t *gaSaveGameUserDescription; +int gaLoadGameSlot; #if __JDOOM__ || __JDOOM64__ mobj_t *bodyQueue[BODYQUEUESIZE]; @@ -209,22 +207,22 @@ int bodyQueueSlot; #endif // vars used with game status cvars -int gsvEpisode = 0; -int gsvMap = 0; -char *gsvMapAuthor = "Unknown"; +int gsvEpisode; +int gsvMap; +char *gsvMapAuthor;// = "Unknown"; int gsvMapMusic = -1; -char *gsvMapTitle = "Unknown"; +char *gsvMapTitle;// = "Unknown"; -int gsvInMap = 0; -int gsvCurrentMusic = 0; +int gsvInMap; +int gsvCurrentMusic; -int gsvArmor = 0; -int gsvHealth = 0; +int gsvArmor; +int gsvHealth; #if !__JHEXEN__ -int gsvKills = 0; -int gsvItems = 0; -int gsvSecrets = 0; +int gsvKills; +int gsvItems; +int gsvSecrets; #endif int gsvCurrentWeapon; @@ -244,164 +242,165 @@ static gamestate_t gameState = GS_STARTUP; cvartemplate_t gamestatusCVars[] = { - {"game-music", READONLYCVAR, CVT_INT, &gsvCurrentMusic, 0, 0}, - {"game-skill", READONLYCVAR, CVT_INT, &gameRules.skill, 0, 0}, - {"game-state", READONLYCVAR, CVT_INT, &gameState, 0, 0}, - {"game-state-map", READONLYCVAR, CVT_INT, &gsvInMap, 0, 0}, + {"game-music", READONLYCVAR, CVT_INT, &gsvCurrentMusic, 0, 0, 0}, + {"game-skill", READONLYCVAR, CVT_INT, &gameRules.skill, 0, 0, 0}, + {"game-state", READONLYCVAR, CVT_INT, &gameState, 0, 0, 0}, + {"game-state-map", READONLYCVAR, CVT_INT, &gsvInMap, 0, 0, 0}, #if !__JHEXEN__ - {"game-stats-kills", READONLYCVAR, CVT_INT, &gsvKills, 0, 0}, - {"game-stats-items", READONLYCVAR, CVT_INT, &gsvItems, 0, 0}, - {"game-stats-secrets", READONLYCVAR, CVT_INT, &gsvSecrets, 0, 0}, + {"game-stats-kills", READONLYCVAR, CVT_INT, &gsvKills, 0, 0, 0}, + {"game-stats-items", READONLYCVAR, CVT_INT, &gsvItems, 0, 0, 0}, + {"game-stats-secrets", READONLYCVAR, CVT_INT, &gsvSecrets, 0, 0, 0}, #endif - {"map-author", READONLYCVAR, CVT_CHARPTR, &gsvMapAuthor, 0, 0}, - {"map-episode", READONLYCVAR, CVT_INT, &gsvEpisode, 0, 0}, + {"map-author", READONLYCVAR, CVT_CHARPTR, &gsvMapAuthor, 0, 0, 0}, + {"map-episode", READONLYCVAR, CVT_INT, &gsvEpisode, 0, 0, 0}, #if __JHEXEN__ - {"map-hub", READONLYCVAR, CVT_INT, &mapHub, 0, 0}, + {"map-hub", READONLYCVAR, CVT_INT, &mapHub, 0, 0, 0}, #endif - {"map-id", READONLYCVAR, CVT_INT, &gsvMap, 0, 0}, - {"map-music", READONLYCVAR, CVT_INT, &gsvMapMusic, 0, 0}, - {"map-name", READONLYCVAR, CVT_CHARPTR, &gsvMapTitle, 0, 0}, + {"map-id", READONLYCVAR, CVT_INT, &gsvMap, 0, 0, 0}, + {"map-music", READONLYCVAR, CVT_INT, &gsvMapMusic, 0, 0, 0}, + {"map-name", READONLYCVAR, CVT_CHARPTR, &gsvMapTitle, 0, 0, 0}, - {"player-health", READONLYCVAR, CVT_INT, &gsvHealth, 0, 0}, - {"player-armor", READONLYCVAR, CVT_INT, &gsvArmor, 0, 0}, - {"player-weapon-current", READONLYCVAR, CVT_INT, &gsvCurrentWeapon, 0, 0}, + {"player-health", READONLYCVAR, CVT_INT, &gsvHealth, 0, 0, 0}, + {"player-armor", READONLYCVAR, CVT_INT, &gsvArmor, 0, 0, 0}, + {"player-weapon-current", READONLYCVAR, CVT_INT, &gsvCurrentWeapon, 0, 0, 0}, #if __JDOOM__ || __JDOOM64__ // Ammo - {"player-ammo-bullets", READONLYCVAR, CVT_INT, &gsvAmmo[AT_CLIP], 0, 0}, - {"player-ammo-shells", READONLYCVAR, CVT_INT, &gsvAmmo[AT_SHELL], 0, 0}, - {"player-ammo-cells", READONLYCVAR, CVT_INT, &gsvAmmo[AT_CELL], 0, 0}, - {"player-ammo-missiles", READONLYCVAR, CVT_INT, &gsvAmmo[AT_MISSILE], 0, 0}, + {"player-ammo-bullets", READONLYCVAR, CVT_INT, &gsvAmmo[AT_CLIP], 0, 0, 0}, + {"player-ammo-shells", READONLYCVAR, CVT_INT, &gsvAmmo[AT_SHELL], 0, 0, 0}, + {"player-ammo-cells", READONLYCVAR, CVT_INT, &gsvAmmo[AT_CELL], 0, 0, 0}, + {"player-ammo-missiles", READONLYCVAR, CVT_INT, &gsvAmmo[AT_MISSILE], 0, 0, 0}, // Weapons - {"player-weapon-fist", READONLYCVAR, CVT_INT, &gsvWeapons[WT_FIRST], 0, 0}, - {"player-weapon-pistol", READONLYCVAR, CVT_INT, &gsvWeapons[WT_SECOND], 0, 0}, - {"player-weapon-shotgun", READONLYCVAR, CVT_INT, &gsvWeapons[WT_THIRD], 0, 0}, - {"player-weapon-chaingun", READONLYCVAR, CVT_INT, &gsvWeapons[WT_FOURTH], 0, 0}, - {"player-weapon-mlauncher", READONLYCVAR, CVT_INT, &gsvWeapons[WT_FIFTH], 0, 0}, - {"player-weapon-plasmarifle", READONLYCVAR, CVT_INT, &gsvWeapons[WT_SIXTH], 0, 0}, - {"player-weapon-bfg", READONLYCVAR, CVT_INT, &gsvWeapons[WT_SEVENTH], 0, 0}, - {"player-weapon-chainsaw", READONLYCVAR, CVT_INT, &gsvWeapons[WT_EIGHTH], 0, 0}, - {"player-weapon-sshotgun", READONLYCVAR, CVT_INT, &gsvWeapons[WT_NINETH], 0, 0}, + {"player-weapon-fist", READONLYCVAR, CVT_INT, &gsvWeapons[WT_FIRST], 0, 0, 0}, + {"player-weapon-pistol", READONLYCVAR, CVT_INT, &gsvWeapons[WT_SECOND], 0, 0, 0}, + {"player-weapon-shotgun", READONLYCVAR, CVT_INT, &gsvWeapons[WT_THIRD], 0, 0, 0}, + {"player-weapon-chaingun", READONLYCVAR, CVT_INT, &gsvWeapons[WT_FOURTH], 0, 0, 0}, + {"player-weapon-mlauncher", READONLYCVAR, CVT_INT, &gsvWeapons[WT_FIFTH], 0, 0, 0}, + {"player-weapon-plasmarifle", READONLYCVAR, CVT_INT, &gsvWeapons[WT_SIXTH], 0, 0, 0}, + {"player-weapon-bfg", READONLYCVAR, CVT_INT, &gsvWeapons[WT_SEVENTH], 0, 0, 0}, + {"player-weapon-chainsaw", READONLYCVAR, CVT_INT, &gsvWeapons[WT_EIGHTH], 0, 0, 0}, + {"player-weapon-sshotgun", READONLYCVAR, CVT_INT, &gsvWeapons[WT_NINETH], 0, 0, 0}, // Keys - {"player-key-blue", READONLYCVAR, CVT_INT, &gsvKeys[KT_BLUECARD], 0, 0}, - {"player-key-yellow", READONLYCVAR, CVT_INT, &gsvKeys[KT_YELLOWCARD], 0, 0}, - {"player-key-red", READONLYCVAR, CVT_INT, &gsvKeys[KT_REDCARD], 0, 0}, - {"player-key-blueskull", READONLYCVAR, CVT_INT, &gsvKeys[KT_BLUESKULL], 0, 0}, - {"player-key-yellowskull", READONLYCVAR, CVT_INT, &gsvKeys[KT_YELLOWSKULL], 0, 0}, - {"player-key-redskull", READONLYCVAR, CVT_INT, &gsvKeys[KT_REDSKULL], 0, 0}, + {"player-key-blue", READONLYCVAR, CVT_INT, &gsvKeys[KT_BLUECARD], 0, 0, 0}, + {"player-key-yellow", READONLYCVAR, CVT_INT, &gsvKeys[KT_YELLOWCARD], 0, 0, 0}, + {"player-key-red", READONLYCVAR, CVT_INT, &gsvKeys[KT_REDCARD], 0, 0, 0}, + {"player-key-blueskull", READONLYCVAR, CVT_INT, &gsvKeys[KT_BLUESKULL], 0, 0, 0}, + {"player-key-yellowskull", READONLYCVAR, CVT_INT, &gsvKeys[KT_YELLOWSKULL], 0, 0, 0}, + {"player-key-redskull", READONLYCVAR, CVT_INT, &gsvKeys[KT_REDSKULL], 0, 0, 0}, #elif __JHERETIC__ // Ammo - {"player-ammo-goldwand", READONLYCVAR, CVT_INT, &gsvAmmo[AT_CRYSTAL], 0, 0}, - {"player-ammo-crossbow", READONLYCVAR, CVT_INT, &gsvAmmo[AT_ARROW], 0, 0}, - {"player-ammo-dragonclaw", READONLYCVAR, CVT_INT, &gsvAmmo[AT_ORB], 0, 0}, - {"player-ammo-hellstaff", READONLYCVAR, CVT_INT, &gsvAmmo[AT_RUNE], 0, 0}, - {"player-ammo-phoenixrod", READONLYCVAR, CVT_INT, &gsvAmmo[AT_FIREORB], 0, 0}, - {"player-ammo-mace", READONLYCVAR, CVT_INT, &gsvAmmo[AT_MSPHERE], 0, 0}, + {"player-ammo-goldwand", READONLYCVAR, CVT_INT, &gsvAmmo[AT_CRYSTAL], 0, 0, 0}, + {"player-ammo-crossbow", READONLYCVAR, CVT_INT, &gsvAmmo[AT_ARROW], 0, 0, 0}, + {"player-ammo-dragonclaw", READONLYCVAR, CVT_INT, &gsvAmmo[AT_ORB], 0, 0, 0}, + {"player-ammo-hellstaff", READONLYCVAR, CVT_INT, &gsvAmmo[AT_RUNE], 0, 0, 0}, + {"player-ammo-phoenixrod", READONLYCVAR, CVT_INT, &gsvAmmo[AT_FIREORB], 0, 0, 0}, + {"player-ammo-mace", READONLYCVAR, CVT_INT, &gsvAmmo[AT_MSPHERE], 0, 0, 0}, // Weapons - {"player-weapon-staff", READONLYCVAR, CVT_INT, &gsvWeapons[WT_FIRST], 0, 0}, - {"player-weapon-goldwand", READONLYCVAR, CVT_INT, &gsvWeapons[WT_SECOND], 0, 0}, - {"player-weapon-crossbow", READONLYCVAR, CVT_INT, &gsvWeapons[WT_THIRD], 0, 0}, - {"player-weapon-dragonclaw", READONLYCVAR, CVT_INT, &gsvWeapons[WT_FOURTH], 0, 0}, - {"player-weapon-hellstaff", READONLYCVAR, CVT_INT, &gsvWeapons[WT_FIFTH], 0, 0}, - {"player-weapon-phoenixrod", READONLYCVAR, CVT_INT, &gsvWeapons[WT_SIXTH], 0, 0}, - {"player-weapon-mace", READONLYCVAR, CVT_INT, &gsvWeapons[WT_SEVENTH], 0, 0}, - {"player-weapon-gauntlets", READONLYCVAR, CVT_INT, &gsvWeapons[WT_EIGHTH], 0, 0}, + {"player-weapon-staff", READONLYCVAR, CVT_INT, &gsvWeapons[WT_FIRST], 0, 0, 0}, + {"player-weapon-goldwand", READONLYCVAR, CVT_INT, &gsvWeapons[WT_SECOND], 0, 0, 0}, + {"player-weapon-crossbow", READONLYCVAR, CVT_INT, &gsvWeapons[WT_THIRD], 0, 0, 0}, + {"player-weapon-dragonclaw", READONLYCVAR, CVT_INT, &gsvWeapons[WT_FOURTH], 0, 0, 0}, + {"player-weapon-hellstaff", READONLYCVAR, CVT_INT, &gsvWeapons[WT_FIFTH], 0, 0, 0}, + {"player-weapon-phoenixrod", READONLYCVAR, CVT_INT, &gsvWeapons[WT_SIXTH], 0, 0, 0}, + {"player-weapon-mace", READONLYCVAR, CVT_INT, &gsvWeapons[WT_SEVENTH], 0, 0, 0}, + {"player-weapon-gauntlets", READONLYCVAR, CVT_INT, &gsvWeapons[WT_EIGHTH], 0, 0, 0}, // Keys - {"player-key-yellow", READONLYCVAR, CVT_INT, &gsvKeys[KT_YELLOW], 0, 0}, - {"player-key-green", READONLYCVAR, CVT_INT, &gsvKeys[KT_GREEN], 0, 0}, - {"player-key-blue", READONLYCVAR, CVT_INT, &gsvKeys[KT_BLUE], 0, 0}, + {"player-key-yellow", READONLYCVAR, CVT_INT, &gsvKeys[KT_YELLOW], 0, 0, 0}, + {"player-key-green", READONLYCVAR, CVT_INT, &gsvKeys[KT_GREEN], 0, 0, 0}, + {"player-key-blue", READONLYCVAR, CVT_INT, &gsvKeys[KT_BLUE], 0, 0, 0}, // Inventory items - {"player-artifact-ring", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_INVULNERABILITY], 0, 0}, - {"player-artifact-shadowsphere", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_INVISIBILITY], 0, 0}, - {"player-artifact-crystalvial", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_HEALTH], 0, 0}, - {"player-artifact-mysticurn", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_SUPERHEALTH], 0, 0}, - {"player-artifact-tomeofpower", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_TOMBOFPOWER], 0, 0}, - {"player-artifact-torch", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_TORCH], 0, 0}, - {"player-artifact-firebomb", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_FIREBOMB], 0, 0}, - {"player-artifact-egg", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_EGG], 0, 0}, - {"player-artifact-wings", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_FLY], 0, 0}, - {"player-artifact-chaosdevice", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_TELEPORT], 0, 0}, + {"player-artifact-ring", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_INVULNERABILITY], 0, 0, 0}, + {"player-artifact-shadowsphere", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_INVISIBILITY], 0, 0, 0}, + {"player-artifact-crystalvial", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_HEALTH], 0, 0, 0}, + {"player-artifact-mysticurn", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_SUPERHEALTH], 0, 0, 0}, + {"player-artifact-tomeofpower", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_TOMBOFPOWER], 0, 0, 0}, + {"player-artifact-torch", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_TORCH], 0, 0, 0}, + {"player-artifact-firebomb", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_FIREBOMB], 0, 0, 0}, + {"player-artifact-egg", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_EGG], 0, 0, 0}, + {"player-artifact-wings", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_FLY], 0, 0, 0}, + {"player-artifact-chaosdevice", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_TELEPORT], 0, 0, 0}, #elif __JHEXEN__ // Mana - {"player-mana-blue", READONLYCVAR, CVT_INT, &gsvAmmo[AT_BLUEMANA], 0, 0}, - {"player-mana-green", READONLYCVAR, CVT_INT, &gsvAmmo[AT_GREENMANA], 0, 0}, + {"player-mana-blue", READONLYCVAR, CVT_INT, &gsvAmmo[AT_BLUEMANA], 0, 0, 0}, + {"player-mana-green", READONLYCVAR, CVT_INT, &gsvAmmo[AT_GREENMANA], 0, 0, 0}, // Keys - {"player-key-steel", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEY1], 0, 0}, - {"player-key-cave", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEY2], 0, 0}, - {"player-key-axe", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEY3], 0, 0}, - {"player-key-fire", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEY4], 0, 0}, - {"player-key-emerald", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEY5], 0, 0}, - {"player-key-dungeon", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEY6], 0, 0}, - {"player-key-silver", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEY7], 0, 0}, - {"player-key-rusted", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEY8], 0, 0}, - {"player-key-horn", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEY9], 0, 0}, - {"player-key-swamp", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEYA], 0, 0}, - {"player-key-castle", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEYB], 0, 0}, + {"player-key-steel", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEY1], 0, 0, 0}, + {"player-key-cave", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEY2], 0, 0, 0}, + {"player-key-axe", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEY3], 0, 0, 0}, + {"player-key-fire", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEY4], 0, 0, 0}, + {"player-key-emerald", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEY5], 0, 0, 0}, + {"player-key-dungeon", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEY6], 0, 0, 0}, + {"player-key-silver", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEY7], 0, 0, 0}, + {"player-key-rusted", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEY8], 0, 0, 0}, + {"player-key-horn", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEY9], 0, 0, 0}, + {"player-key-swamp", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEYA], 0, 0, 0}, + {"player-key-castle", READONLYCVAR, CVT_INT, &gsvKeys[KT_KEYB], 0, 0, 0}, // Weapons - {"player-weapon-first", READONLYCVAR, CVT_INT, &gsvWeapons[WT_FIRST], 0, 0}, - {"player-weapon-second", READONLYCVAR, CVT_INT, &gsvWeapons[WT_SECOND], 0, 0}, - {"player-weapon-third", READONLYCVAR, CVT_INT, &gsvWeapons[WT_THIRD], 0, 0}, - {"player-weapon-fourth", READONLYCVAR, CVT_INT, &gsvWeapons[WT_FOURTH], 0, 0}, + {"player-weapon-first", READONLYCVAR, CVT_INT, &gsvWeapons[WT_FIRST], 0, 0, 0}, + {"player-weapon-second", READONLYCVAR, CVT_INT, &gsvWeapons[WT_SECOND], 0, 0, 0}, + {"player-weapon-third", READONLYCVAR, CVT_INT, &gsvWeapons[WT_THIRD], 0, 0, 0}, + {"player-weapon-fourth", READONLYCVAR, CVT_INT, &gsvWeapons[WT_FOURTH], 0, 0, 0}, // Weapon Pieces - {"player-weapon-piece1", READONLYCVAR, CVT_INT, &gsvWPieces[0], 0, 0}, - {"player-weapon-piece2", READONLYCVAR, CVT_INT, &gsvWPieces[1], 0, 0}, - {"player-weapon-piece3", READONLYCVAR, CVT_INT, &gsvWPieces[2], 0, 0}, - {"player-weapon-allpieces", READONLYCVAR, CVT_INT, &gsvWPieces[3], 0, 0}, + {"player-weapon-piece1", READONLYCVAR, CVT_INT, &gsvWPieces[0], 0, 0, 0}, + {"player-weapon-piece2", READONLYCVAR, CVT_INT, &gsvWPieces[1], 0, 0, 0}, + {"player-weapon-piece3", READONLYCVAR, CVT_INT, &gsvWPieces[2], 0, 0, 0}, + {"player-weapon-allpieces", READONLYCVAR, CVT_INT, &gsvWPieces[3], 0, 0, 0}, // Inventory items - {"player-artifact-defender", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_INVULNERABILITY], 0, 0}, - {"player-artifact-quartzflask", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_HEALTH], 0, 0}, - {"player-artifact-mysticurn", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_SUPERHEALTH], 0, 0}, - {"player-artifact-mysticambit", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_HEALINGRADIUS], 0, 0}, - {"player-artifact-darkservant", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_SUMMON], 0, 0}, - {"player-artifact-torch", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_TORCH], 0, 0}, - {"player-artifact-porkalator", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_EGG], 0, 0}, - {"player-artifact-wings", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_FLY], 0, 0}, - {"player-artifact-repulsion", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_BLASTRADIUS], 0, 0}, - {"player-artifact-flechette", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_POISONBAG], 0, 0}, - {"player-artifact-banishment", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_TELEPORTOTHER], 0, 0}, - {"player-artifact-speed", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_SPEED], 0, 0}, - {"player-artifact-might", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_BOOSTMANA], 0, 0}, - {"player-artifact-bracers", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_BOOSTARMOR], 0, 0}, - {"player-artifact-chaosdevice", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_TELEPORT], 0, 0}, - {"player-artifact-skull", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZSKULL], 0, 0}, - {"player-artifact-heart", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZGEMBIG], 0, 0}, - {"player-artifact-ruby", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZGEMRED], 0, 0}, - {"player-artifact-emerald1", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZGEMGREEN1], 0, 0}, - {"player-artifact-emerald2", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZGEMGREEN2], 0, 0}, - {"player-artifact-sapphire1", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZGEMBLUE1], 0, 0}, - {"player-artifact-sapphire2", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZGEMBLUE2], 0, 0}, - {"player-artifact-daemoncodex", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZBOOK1], 0, 0}, - {"player-artifact-liberoscura", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZBOOK2], 0, 0}, - {"player-artifact-flamemask", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZSKULL2], 0, 0}, - {"player-artifact-glaiveseal", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZFWEAPON], 0, 0}, - {"player-artifact-holyrelic", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZCWEAPON], 0, 0}, - {"player-artifact-sigilmagus", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZMWEAPON], 0, 0}, - {"player-artifact-gear1", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZGEAR1], 0, 0}, - {"player-artifact-gear2", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZGEAR2], 0, 0}, - {"player-artifact-gear3", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZGEAR3], 0, 0}, - {"player-artifact-gear4", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZGEAR4], 0, 0}, -#endif - {NULL} + {"player-artifact-defender", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_INVULNERABILITY], 0, 0, 0}, + {"player-artifact-quartzflask", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_HEALTH], 0, 0, 0}, + {"player-artifact-mysticurn", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_SUPERHEALTH], 0, 0, 0}, + {"player-artifact-mysticambit", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_HEALINGRADIUS], 0, 0, 0}, + {"player-artifact-darkservant", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_SUMMON], 0, 0, 0}, + {"player-artifact-torch", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_TORCH], 0, 0, 0}, + {"player-artifact-porkalator", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_EGG], 0, 0, 0}, + {"player-artifact-wings", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_FLY], 0, 0, 0}, + {"player-artifact-repulsion", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_BLASTRADIUS], 0, 0, 0}, + {"player-artifact-flechette", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_POISONBAG], 0, 0, 0}, + {"player-artifact-banishment", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_TELEPORTOTHER], 0, 0, 0}, + {"player-artifact-speed", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_SPEED], 0, 0, 0}, + {"player-artifact-might", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_BOOSTMANA], 0, 0, 0}, + {"player-artifact-bracers", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_BOOSTARMOR], 0, 0, 0}, + {"player-artifact-chaosdevice", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_TELEPORT], 0, 0, 0}, + {"player-artifact-skull", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZSKULL], 0, 0, 0}, + {"player-artifact-heart", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZGEMBIG], 0, 0, 0}, + {"player-artifact-ruby", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZGEMRED], 0, 0, 0}, + {"player-artifact-emerald1", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZGEMGREEN1], 0, 0, 0}, + {"player-artifact-emerald2", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZGEMGREEN2], 0, 0, 0}, + {"player-artifact-sapphire1", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZGEMBLUE1], 0, 0, 0}, + {"player-artifact-sapphire2", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZGEMBLUE2], 0, 0, 0}, + {"player-artifact-daemoncodex", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZBOOK1], 0, 0, 0}, + {"player-artifact-liberoscura", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZBOOK2], 0, 0, 0}, + {"player-artifact-flamemask", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZSKULL2], 0, 0, 0}, + {"player-artifact-glaiveseal", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZFWEAPON], 0, 0, 0}, + {"player-artifact-holyrelic", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZCWEAPON], 0, 0, 0}, + {"player-artifact-sigilmagus", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZMWEAPON], 0, 0, 0}, + {"player-artifact-gear1", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZGEAR1], 0, 0, 0}, + {"player-artifact-gear2", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZGEAR2], 0, 0, 0}, + {"player-artifact-gear3", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZGEAR3], 0, 0, 0}, + {"player-artifact-gear4", READONLYCVAR, CVT_INT, &gsvInvItems[IIT_PUZZGEAR4], 0, 0, 0}, +#endif + {"", 0, CVT_NULL, 0, 0, 0, 0} }; ccmdtemplate_t gameCmds[] = { - { "deletegamesave", "ss", CCmdDeleteGameSave }, - { "deletegamesave", "s", CCmdDeleteGameSave }, - { "endgame", "", CCmdEndGame }, - { "helpscreen", "", CCmdHelpScreen }, - { "listmaps", "", CCmdListMaps }, - { "loadgame", "ss", CCmdLoadGame }, - { "loadgame", "s", CCmdLoadGame }, - { "loadgame", "", CCmdOpenLoadMenu }, - { "quickload", "", CCmdQuickLoadGame }, - { "quicksave", "", CCmdQuickSaveGame }, - { "savegame", "sss", CCmdSaveGame }, - { "savegame", "ss", CCmdSaveGame }, - { "savegame", "s", CCmdSaveGame }, - { "savegame", "", CCmdOpenSaveMenu }, - { "togglegamma", "", CCmdCycleTextureGamma }, - { NULL } + { "deletegamesave", "ss", CCmdDeleteGameSave, 0 }, + { "deletegamesave", "s", CCmdDeleteGameSave, 0 }, + { "endgame", "", CCmdEndGame, 0 }, + { "helpscreen", "", CCmdHelpScreen, 0 }, + { "inspectgamesave", "s", CCmdInspectGameSave, 0 }, + { "listmaps", "", CCmdListMaps, 0 }, + { "loadgame", "ss", CCmdLoadGame, 0 }, + { "loadgame", "s", CCmdLoadGame, 0 }, + { "loadgame", "", CCmdOpenLoadMenu, 0 }, + { "quickload", "", CCmdQuickLoadGame, 0 }, + { "quicksave", "", CCmdQuickSaveGame, 0 }, + { "savegame", "sss", CCmdSaveGame, 0 }, + { "savegame", "ss", CCmdSaveGame, 0 }, + { "savegame", "s", CCmdSaveGame, 0 }, + { "savegame", "", CCmdOpenSaveMenu, 0 }, + { "togglegamma", "", CCmdCycleTextureGamma, 0 }, + { "", "", 0, 0 } }; // Deferred new game arguments: @@ -412,15 +411,27 @@ static GameRuleset dRules; static gameaction_t gameAction; static dd_bool quitInProgress; -void G_Register(void) +void G_Register() { - int i; - - for(i = 0; gamestatusCVars[i].path; ++i) + for(int i = 0; gamestatusCVars[i].path[0]; ++i) + { Con_AddVariable(gamestatusCVars + i); + } - for(i = 0; gameCmds[i].name; ++i) +#if !__JHEXEN__ + C_VAR_BYTE("game-save-auto-loadonreborn", &cfg.loadAutoSaveOnReborn, 0, 0, 1); +#endif + C_VAR_BYTE("game-save-confirm", &cfg.confirmQuickGameSave, 0, 0, 1); + C_VAR_BYTE("game-save-confirm-loadonreborn", &cfg.confirmRebornLoad, 0, 0, 1); + C_VAR_BYTE("game-save-last-loadonreborn", &cfg.loadLastSaveOnReborn, 0, 0, 1); + + // Aliases for obsolete cvars: + C_VAR_BYTE("menu-quick-ask", &cfg.confirmQuickGameSave, 0, 0, 1); + + for(int i = 0; gameCmds[i].name[0]; ++i) + { Con_AddCommand(gameCmds + i); + } C_CMD("warp", "i", WarpMap); C_CMD("setmap", "i", WarpMap); // alias @@ -435,7 +446,7 @@ void G_Register(void) #endif } -dd_bool G_QuitInProgress(void) +dd_bool G_QuitInProgress() { return quitInProgress; } @@ -448,7 +459,7 @@ void G_SetGameAction(gameaction_t action) gameAction = action; } -gameaction_t G_GameAction(void) +gameaction_t G_GameAction() { return gameAction; } @@ -457,7 +468,7 @@ gameaction_t G_GameAction(void) * Common Pre Game Initialization routine. * Game-specfic pre init actions should be placed in eg D_PreInit() (for jDoom) */ -void G_CommonPreInit(void) +void G_CommonPreInit() { int i, j; @@ -498,10 +509,10 @@ void G_CommonPreInit(void) // Add our cvars and ccmds to the console databases. G_ConsoleRegistration(); // Main command list. D_NetConsoleRegistration(); // For network. - G_Register(); // Read-only game status cvars (for playsim). + G_Register(); // Top level game cvars and commands. Pause_Register(); G_ControlRegister(); // For controls/input. - SaveSlots_ConsoleRegister(); // Game-save system. + SaveSlots::consoleRegister(); // Game-save system. Hu_MenuRegister(); // For the menu. GUI_Register(); // For the UI library. Hu_MsgRegister(); // For the game messages. @@ -576,7 +587,7 @@ void Mobj_UpdateTranslationClassAndMap(mobj_t* mo) } #endif // __JHEXEN__ -void R_LoadColorPalettes(void) +void R_LoadColorPalettes() { #define PALLUMPNAME "PLAYPAL" #define PALENTRIES (256) @@ -691,59 +702,60 @@ void R_LoadColorPalettes(void) * @todo Read this information from a definition (ideally with more user * friendly mnemonics...). */ -void R_LoadVectorGraphics(void) +void R_LoadVectorGraphics() { -#define R (1.0f) -#define NUMITEMS(x) (sizeof(x)/sizeof((x)[0])) +#define R (1.0f) +#define NUMITEMS(x) (sizeof(x)/sizeof((x)[0])) +#define Pt Point2Rawf - const Point2Rawf keyPoints[] = { - {-3 * R / 4, 0}, {-3 * R / 4, -R / 4}, // Mid tooth. - { 0, 0}, { -R, 0}, { -R, -R / 2}, // Shaft and end tooth. + Point2Rawf const keyPoints[] = { + Pt(-3 * R / 4, 0), Pt(-3 * R / 4, -R / 4), // Mid tooth. + Pt( 0, 0), Pt( -R, 0), Pt( -R, -R / 2), // Shaft and end tooth. - { 0, 0}, {R / 4, -R / 2}, // Bow. - {R / 2, -R / 2}, {R / 2, R / 2}, - {R / 4, R / 2}, { 0, 0}, + Pt( 0, 0), Pt(R / 4, -R / 2), // Bow. + Pt(R / 2, -R / 2), Pt(R / 2, R / 2), + Pt(R / 4, R / 2), Pt( 0, 0), }; - const def_svgline_t key[] = { + def_svgline_t const key[] = { { 2, &keyPoints[ 0] }, { 3, &keyPoints[ 2] }, { 6, &keyPoints[ 5] } }; - const Point2Rawf thintrianglePoints[] = { - {-R / 2, R - R / 2}, - { R, 0}, // ` - {-R / 2, -R + R / 2}, // / - {-R / 2, R - R / 2} // |> + Point2Rawf const thintrianglePoints[] = { + Pt(-R / 2, R - R / 2), + Pt( R, 0), // ` + Pt(-R / 2, -R + R / 2), // / + Pt(-R / 2, R - R / 2) // |> }; - const def_svgline_t thintriangle[] = { + def_svgline_t const thintriangle[] = { { 4, thintrianglePoints }, }; #if __JDOOM__ || __JDOOM64__ - const Point2Rawf arrowPoints[] = { - { -R + R / 8, 0}, { R, 0}, // ----- - { R - R / 2, -R / 4}, { R, 0}, { R - R / 2, R / 4}, // -----> - {-R - R / 8, -R / 4}, { -R + R / 8, 0}, {-R - R / 8, R / 4}, // >----> - {-R + R / 8, -R / 4}, {-R + 3 * R / 8, 0}, {-R + R / 8, R / 4}, // >>---> + Point2Rawf const arrowPoints[] = { + Pt( -R + R / 8, 0 ), Pt( R, 0), // ----- + Pt( R - R / 2, -R / 4), Pt( R, 0), Pt( R - R / 2, R / 4), // -----> + Pt(-R - R / 8, -R / 4), Pt( -R + R / 8, 0), Pt(-R - R / 8, R / 4), // >----> + Pt(-R + R / 8, -R / 4), Pt(-R + 3 * R / 8, 0), Pt(-R + R / 8, R / 4), // >>---> }; - const def_svgline_t arrow[] = { + def_svgline_t const arrow[] = { { 2, &arrowPoints[ 0] }, { 3, &arrowPoints[ 2] }, { 3, &arrowPoints[ 5] }, { 3, &arrowPoints[ 8] } }; #elif __JHERETIC__ || __JHEXEN__ - const Point2Rawf arrowPoints[] = { - {-R + R / 4, 0}, { 0, 0}, // center line. - {-R + R / 4, R / 8}, { R, 0}, {-R + R / 4, -R / 8}, // blade + Point2Rawf const arrowPoints[] = { + Pt(-R + R / 4, 0), Pt( 0, 0), // center line. + Pt(-R + R / 4, R / 8), Pt( R, 0), Pt(-R + R / 4, -R / 8), // blade - {-R + R / 8, -R / 4}, {-R + R / 4, -R / 4}, // guard - {-R + R / 4, R / 4}, {-R + R / 8, R / 4}, - {-R + R / 8, -R / 4}, + Pt(-R + R / 8, -R / 4), Pt(-R + R / 4, -R / 4), // guard + Pt(-R + R / 4, R / 4), Pt(-R + R / 8, R / 4), + Pt(-R + R / 8, -R / 4), - {-R + R / 8, -R / 8}, {-R - R / 4, -R / 8}, // hilt - {-R - R / 4, R / 8}, {-R + R / 8, R / 8}, + Pt(-R + R / 8, -R / 8), Pt(-R - R / 4, -R / 8), // hilt + Pt(-R - R / 4, R / 8), Pt(-R + R / 8, R / 8), }; - const def_svgline_t arrow[] = { + def_svgline_t const arrow[] = { { 2, &arrowPoints[ 0] }, { 3, &arrowPoints[ 2] }, { 5, &arrowPoints[ 5] }, @@ -751,22 +763,22 @@ void R_LoadVectorGraphics(void) }; #endif #if __JDOOM__ - const Point2Rawf cheatarrowPoints[] = { - { -R + R / 8, 0}, { R, 0}, // ----- - { R - R / 2, -R / 4}, { R, 0}, { R - R / 2, R / 4}, // -----> - {-R - R / 8, -R / 4}, { -R + R / 8, 0}, {-R - R / 8, R / 4}, // >----> - {-R + R / 8, -R / 4}, {-R + 3 * R / 8, 0}, {-R + R / 8, R / 4}, // >>---> + Point2Rawf const cheatarrowPoints[] = { + Pt( -R + R / 8, 0 ), Pt( R, 0), // ----- + Pt( R - R / 2, -R / 4), Pt( R, 0), Pt( R - R / 2, R / 4), // -----> + Pt(-R - R / 8, -R / 4), Pt( -R + R / 8, 0), Pt(-R - R / 8, R / 4), // >----> + Pt(-R + R / 8, -R / 4), Pt(-R + 3 * R / 8, 0), Pt(-R + R / 8, R / 4), // >>---> - { -R / 2, 0}, { -R / 2, -R / 6}, // >>-d---> - {-R / 2 + R / 6, -R / 6}, {-R / 2 + R / 6, R / 4}, + Pt( -R / 2, 0), Pt( -R / 2, -R / 6), // >>-d---> + Pt(-R / 2 + R / 6, -R / 6), Pt(-R / 2 + R / 6, R / 4), - { -R / 6, 0}, { -R / 6, -R / 6}, // >>-dd--> - { 0, -R / 6}, { 0, R / 4}, + Pt( -R / 6, 0), Pt( -R / 6, -R / 6), // >>-dd--> + Pt( 0, -R / 6), Pt( 0, R / 4), - { R / 6, R / 4}, { R / 6, -R / 7}, // >>-ddt-> - {R / 6 + R / 32, -R / 7 - R / 32}, {R / 6 + R / 10, -R / 7} + Pt( R / 6, R / 4), Pt( R / 6, -R / 7), // >>-ddt-> + Pt(R / 6 + R / 32, -R / 7 - R / 32), Pt(R / 6 + R / 10, -R / 7) }; - const def_svgline_t cheatarrow[] = { + def_svgline_t const cheatarrow[] = { { 2, &cheatarrowPoints[ 0] }, { 3, &cheatarrowPoints[ 2] }, { 3, &cheatarrowPoints[ 5] }, @@ -777,50 +789,50 @@ void R_LoadVectorGraphics(void) }; #endif - const Point2Rawf crossPoints[] = { // + (open center) - {-R, 0}, {-R / 5 * 2, 0}, - { 0, -R}, { 0, -R / 5 * 2}, - { R, 0}, { R / 5 * 2, 0}, - { 0, R}, { 0, R / 5 * 2} + Point2Rawf const crossPoints[] = { // + (open center) + Pt(-R, 0), Pt(-R / 5 * 2, 0), + Pt( 0, -R), Pt( 0, -R / 5 * 2), + Pt( R, 0), Pt( R / 5 * 2, 0), + Pt( 0, R), Pt( 0, R / 5 * 2) }; - const def_svgline_t cross[] = { + def_svgline_t const cross[] = { { 2, &crossPoints[0] }, { 2, &crossPoints[2] }, { 2, &crossPoints[4] }, { 2, &crossPoints[6] } }; - const Point2Rawf twinanglesPoints[] = { // > < - {-R, -R * 10 / 14}, {-(R - (R * 10 / 14)), 0}, {-R, R * 10 / 14}, // > - { R, -R * 10 / 14}, { R - (R * 10 / 14) , 0}, { R, R * 10 / 14}, // < + Point2Rawf const twinanglesPoints[] = { // > < + Pt(-R, -R * 10 / 14), Pt(-(R - (R * 10 / 14)), 0), Pt(-R, R * 10 / 14), // > + Pt( R, -R * 10 / 14), Pt( R - (R * 10 / 14) , 0), Pt( R, R * 10 / 14), // < }; - const def_svgline_t twinangles[] = { + def_svgline_t const twinangles[] = { { 3, &twinanglesPoints[0] }, { 3, &twinanglesPoints[3] } }; - const Point2Rawf squarePoints[] = { // square - {-R, -R}, {-R, R}, - { R, R}, { R, -R}, - {-R, -R} + Point2Rawf const squarePoints[] = { // square + Pt(-R, -R), Pt(-R, R), + Pt( R, R), Pt( R, -R), + Pt(-R, -R) }; - const def_svgline_t square[] = { + def_svgline_t const square[] = { { 5, squarePoints }, }; - const Point2Rawf squarecornersPoints[] = { // square (open center) - { -R, -R / 2}, {-R, -R}, {-R / 2, -R}, // topleft - {R / 2, -R}, { R, -R}, { R, -R / 2}, // topright - { -R, R / 2}, {-R, R}, {-R / 2, R}, // bottomleft - {R / 2, R}, { R, R}, { R, R / 2}, // bottomright + Point2Rawf const squarecornersPoints[] = { // square (open center) + Pt( -R, -R / 2), Pt(-R, -R), Pt(-R / 2, -R), // topleft + Pt(R / 2, -R), Pt( R, -R), Pt( R, -R / 2), // topright + Pt( -R, R / 2), Pt(-R, R), Pt(-R / 2, R), // bottomleft + Pt(R / 2, R), Pt( R, R), Pt( R, R / 2), // bottomright }; - const def_svgline_t squarecorners[] = { + def_svgline_t const squarecorners[] = { { 3, &squarecornersPoints[ 0] }, { 3, &squarecornersPoints[ 3] }, { 3, &squarecornersPoints[ 6] }, { 3, &squarecornersPoints[ 9] } }; - const Point2Rawf anglePoints[] = { // v - {-R, -R}, { 0, 0}, { R, -R} + Point2Rawf const anglePoints[] = { // v + Pt(-R, -R), Pt(0, 0), Pt(R, -R) }; - const def_svgline_t angle[] = { + def_svgline_t const angle[] = { { 3, anglePoints } }; @@ -838,8 +850,9 @@ void R_LoadVectorGraphics(void) R_NewSvg(VG_XHAIR4, squarecorners, NUMITEMS(squarecorners)); R_NewSvg(VG_XHAIR5, angle, NUMITEMS(angle)); -#undef NUMITEMS +#undef P #undef R +#undef NUMITEMS } /** @@ -856,7 +869,7 @@ fontid_t R_MustFindFontForName(const char* name) exit(1); // Unreachable. } -void R_InitRefresh(void) +void R_InitRefresh() { if(IS_DEDICATED) return; @@ -895,7 +908,7 @@ void R_InitRefresh(void) } } -void R_InitHud(void) +void R_InitHud() { Hu_LoadData(); @@ -914,20 +927,37 @@ void R_InitHud(void) Hu_MsgInit(); } -/** - * Common Post Game Initialization routine. - * Game-specific post init actions should be placed in eg D_PostInit() - * (for jDoom) and NOT here. - */ -void G_CommonPostInit(void) +de::Path G_ChooseRootSaveDirectory() +{ + // Use a custom root save directory? + if(CommandLine_CheckWith("-savedir", 1)) + { + return de::NativePath(CommandLine_NextAsPath()).withSeparators('/'); + } + + // Use the default. + GameInfo gameInfo; + if(DD_GameInfo(&gameInfo)) + { + return de::Path(SAVEGAME_DEFAULT_DIR) / Str_Text(gameInfo.identityKey); + } + + /// @throw Error GameInfo is unavailable. + throw de::Error("G_ChooseRootSaveDirectory", "Failed retrieving GameInfo"); +} + +void G_CommonPostInit() { R_InitRefresh(); FI_StackInit(); GUI_Init(); - // Init the save system and create the game save directory. + // Init the save system. SV_Initialize(); + // (Re)Initialize the saved game paths, possibly creating them if they do not exist. + SV_SetupSaveDirectory(G_ChooseRootSaveDirectory()); + #if __JDOOM__ || __JDOOM64__ || __JHERETIC__ XG_ReadTypes(); #endif @@ -954,7 +984,7 @@ void G_CommonPostInit(void) * Common game shutdown routine. * @note Game-specific actions should be placed in G_Shutdown rather than here. */ -void G_CommonShutdown(void) +void G_CommonShutdown() { Plug_RemoveHook(HOOK_DEMO_STOP, Hook_DemoStop); @@ -979,7 +1009,7 @@ void G_CommonShutdown(void) * * @return The current game state. */ -gamestate_t G_GameState(void) +gamestate_t G_GameState() { return gameState; } @@ -987,8 +1017,8 @@ gamestate_t G_GameState(void) static char const *getGameStateStr(gamestate_t state) { struct statename_s { - gamestate_t state; - const char* name; + gamestate_t state; + char const *name; } stateNames[] = { { GS_MAP, "GS_MAP" }, @@ -997,21 +1027,20 @@ static char const *getGameStateStr(gamestate_t state) { GS_STARTUP, "GS_STARTUP" }, { GS_WAITING, "GS_WAITING" }, { GS_INFINE, "GS_INFINE" }, - { -1, NULL } + { gamestate_t(-1), 0 } }; - uint i; - - for(i = 0; stateNames[i].name; ++i) + for(uint i = 0; stateNames[i].name; ++i) + { if(stateNames[i].state == state) return stateNames[i].name; - - return NULL; + } + return 0; } /** * Called when the gameui binding context is active. Triggers the menu. */ -int G_UIResponder(event_t* ev) +int G_UIResponder(event_t *ev) { // Handle "Press any key to continue" messages. if(Hu_MsgResponder(ev)) @@ -1108,7 +1137,7 @@ dd_bool G_StartFinale(const char* script, int flags, finale_mode_t mode, const c return true; } -void G_StartTitle(void) +void G_StartTitle() { ddfinale_t fin; @@ -1122,7 +1151,7 @@ void G_StartTitle(void) G_StartFinale(fin.script, FF_LOCAL, FIMODE_NORMAL, "title"); } -void G_StartHelp(void) +void G_StartHelp() { ddfinale_t fin; @@ -1146,7 +1175,7 @@ void G_StartHelp(void) * Prints a banner to the console containing information pertinent to the * current map (e.g., map title, author...). */ -static void printMapBanner(void) +static void printMapBanner() { char const *title = P_MapTitle(0/*current map*/); @@ -1157,9 +1186,9 @@ static void printMapBanner(void) #if __JHEXEN__ mapinfo_t const *mapInfo = P_MapInfo(0/*current map*/); int warpNum = (mapInfo? mapInfo->warpTrans : -1); - dd_snprintf(buf, 64, "Map %u (%u): " DE2_ESC(b) "%s", warpNum + 1, gameMap + 1, title); + dd_snprintf(buf, 64, "Map: %s (%u) - " DE2_ESC(b) "%s", Str_Text(Uri_ToString(gameMapUri)), warpNum + 1, title); #else - dd_snprintf(buf, 64, "Map %u: " DE2_ESC(b) "%s", gameMap + 1, title); + dd_snprintf(buf, 64, "Map: %s - " DE2_ESC(b) "%s", Str_Text(Uri_ToString(gameMapUri)), title); #endif App_Log(DE2_MAP_NOTE, "%s", buf); } @@ -1175,7 +1204,7 @@ static void printMapBanner(void) App_Log(DE2_LOG_MAP, ""); } -void G_BeginMap(void) +void G_BeginMap() { G_ChangeGameState(GS_MAP); @@ -1197,7 +1226,7 @@ void G_BeginMap(void) S_PauseMusic(false); } -int G_EndGameResponse(msgresponse_t response, int userValue, void* userPointer) +int G_EndGameResponse(msgresponse_t response, int /*userValue*/, void * /*context*/) { if(response == MSG_YES) { @@ -1213,7 +1242,7 @@ int G_EndGameResponse(msgresponse_t response, int userValue, void* userPointer) return true; } -void G_EndGame(void) +void G_EndGame() { if(G_QuitInProgress()) return; @@ -1355,46 +1384,44 @@ int G_PrivilegedResponder(event_t *ev) return false; // Not eaten. } -void G_UpdateGSVarsForPlayer(player_t* pl) +void G_UpdateGSVarsForPlayer(player_t *pl) { - int i, plrnum; - gamestate_t gameState; - if(!pl) return; - plrnum = pl - players; - gameState = G_GameState(); - - gsvHealth = pl->health; + gsvHealth = pl->health; #if !__JHEXEN__ // Map stats - gsvKills = pl->killCount; - gsvItems = pl->itemCount; + gsvKills = pl->killCount; + gsvItems = pl->itemCount; gsvSecrets = pl->secretCount; #endif // armor #if __JHEXEN__ - gsvArmor = FixedDiv(PCLASS_INFO(pl->class_)->autoArmorSave + - pl->armorPoints[ARMOR_ARMOR] + - pl->armorPoints[ARMOR_SHIELD] + - pl->armorPoints[ARMOR_HELMET] + - pl->armorPoints[ARMOR_AMULET], 5 * FRACUNIT) >> FRACBITS; + gsvArmor = FixedDiv(PCLASS_INFO(pl->class_)->autoArmorSave + + pl->armorPoints[ARMOR_ARMOR] + + pl->armorPoints[ARMOR_SHIELD] + + pl->armorPoints[ARMOR_HELMET] + + pl->armorPoints[ARMOR_AMULET], 5 * FRACUNIT) >> FRACBITS; #else - gsvArmor = pl->armorPoints; + gsvArmor = pl->armorPoints; #endif // Owned keys - for(i = 0; i < NUM_KEY_TYPES; ++i) + for(int i = 0; i < NUM_KEY_TYPES; ++i) + { #if __JHEXEN__ gsvKeys[i] = (pl->keys & (1 << i))? 1 : 0; #else gsvKeys[i] = pl->keys[i]; #endif + } // current weapon gsvCurrentWeapon = pl->readyWeapon; // owned weapons - for(i = 0; i < NUM_WEAPON_TYPES; ++i) + for(int i = 0; i < NUM_WEAPON_TYPES; ++i) + { gsvWeapons[i] = pl->weapons[i].owned; + } #if __JHEXEN__ // weapon pieces @@ -1404,22 +1431,24 @@ void G_UpdateGSVarsForPlayer(player_t* pl) gsvWPieces[3] = (pl->pieces == 7)? 1 : 0; #endif // Current ammo amounts. - for(i = 0; i < NUM_AMMO_TYPES; ++i) + for(int i = 0; i < NUM_AMMO_TYPES; ++i) + { gsvAmmo[i] = pl->ammo[i].owned; + } #if __JHERETIC__ || __JHEXEN__ || __JDOOM64__ // Inventory items. - for(i = 0; i < NUM_INVENTORYITEM_TYPES; ++i) + for(int i = 0; i < NUM_INVENTORYITEM_TYPES; ++i) { - if(pl->plr->inGame && gameState == GS_MAP) - gsvInvItems[i] = P_InventoryCount(plrnum, IIT_FIRST + i); + if(pl->plr->inGame && G_GameState() == GS_MAP) + gsvInvItems[i] = P_InventoryCount(pl - players, inventoryitemtype_t(IIT_FIRST + i)); else gsvInvItems[i] = 0; } #endif } -void G_UpdateGSVarsForMap(void) +void G_UpdateGSVarsForMap() { char const *mapAuthor = P_MapAuthor(0/*current map*/, false/*don't supress*/); char const *mapTitle = P_MapTitle(0/*current map*/); @@ -1431,7 +1460,7 @@ void G_UpdateGSVarsForMap(void) Con_SetString2("map-name", mapTitle, SVF_WRITE_OVERRIDE); } -void G_DoQuitGame(void) +void G_DoQuitGame() { #define QUITWAIT_MILLISECONDS 1500 @@ -1533,7 +1562,7 @@ void G_QueMapMusic(Uri const *mapUri) S_PauseMusic(true); } -static void runGameAction(void) +static void runGameAction() { gameaction_t currentAction; @@ -1598,14 +1627,13 @@ static void runGameAction(void) /** * Do needed reborns for any fallen players. */ -static void rebornPlayers(void) +static void rebornPlayers() { - int i; - for(i = 0; i < MAXPLAYERS; ++i) + for(int i = 0; i < MAXPLAYERS; ++i) { - player_t* plr = &players[i]; - ddplayer_t* ddplr = plr->plr; - mobj_t* mo = ddplr->mo; + player_t *plr = &players[i]; + ddplayer_t *ddplr = plr->plr; + mobj_t *mo = ddplr->mo; if(ddplr->inGame && plr->playerState == PST_REBORN && !P_MobjIsCamera(mo)) { @@ -1613,7 +1641,7 @@ static void rebornPlayers(void) } // Player has left? - if(plr->playerState == PST_GONE) + if((int)plr->playerState == PST_GONE) { plr->playerState = PST_REBORN; if(mo) @@ -1627,7 +1655,7 @@ static void rebornPlayers(void) App_Log(DE2_DEV_MAP_MSG, "rebornPlayers: Removing player %i's mobj", i); P_MobjRemove(mo, true); - ddplr->mo = NULL; + ddplr->mo = 0; } } } @@ -1640,7 +1668,7 @@ static void rebornPlayers(void) */ void G_Ticker(timespan_t ticLength) { - static gamestate_t oldGameState = -1; + static gamestate_t oldGameState = gamestate_t(-1); // Always tic: Hu_FogEffectTicker(ticLength); @@ -1755,44 +1783,39 @@ void G_Ticker(timespan_t ticLength) */ void G_PlayerLeaveMap(int player) { -#if __JHERETIC__ || __JHEXEN__ - uint i; - int flightPower; -#endif player_t *p = &players[player]; - dd_bool newHub = true; #if __JHEXEN__ - { - Uri *nextMapUri = G_ComposeMapUri(gameEpisode, nextMap); - - newHub = (P_MapInfo(0/*current map*/)->hub != P_MapInfo(nextMapUri)->hub); + dd_bool newHub = true; - Uri_Delete(nextMapUri); - } + Uri *nextMapUri = G_ComposeMapUri(gameEpisode, nextMap); + newHub = (P_MapInfo(0/*current map*/)->hub != P_MapInfo(nextMapUri)->hub); + Uri_Delete(nextMapUri); nextMapUri = 0; #endif -#if __JHERETIC__ || __JHEXEN__ +#if __JHEXEN__ // Remember if flying. - flightPower = p->powers[PT_FLIGHT]; + int flightPower = p->powers[PT_FLIGHT]; #endif #if __JHERETIC__ // Empty the inventory of excess items - for(i = 0; i < NUM_INVENTORYITEM_TYPES; ++i) + for(int i = 0; i < NUM_INVENTORYITEM_TYPES; ++i) { - inventoryitemtype_t type = IIT_FIRST + i; + inventoryitemtype_t type = inventoryitemtype_t(IIT_FIRST + i); uint count = P_InventoryCount(player, type); if(count) { - uint j; - if(type != IIT_FLY) + { count--; + } - for(j = 0; j < count; ++j) + for(uint j = 0; j < count; ++j) + { P_InventoryTake(player, type, true); + } } } #endif @@ -1801,28 +1824,33 @@ void G_PlayerLeaveMap(int player) if(newHub) { uint count = P_InventoryCount(player, IIT_FLY); - - for(i = 0; i < count; ++i) + for(uint i = 0; i < count; ++i) + { P_InventoryTake(player, IIT_FLY, true); + } } #endif // Remove their powers. p->update |= PSF_POWERS; - memset(p->powers, 0, sizeof(p->powers)); + de::zap(p->powers); #if __JHEXEN__ if(!newHub && !gameRules.deathmatch) + { p->powers[PT_FLIGHT] = flightPower; // Restore flight. + } #endif // Remove their keys. #if __JDOOM__ || __JHERETIC__ || __JDOOM64__ p->update |= PSF_KEYS; - memset(p->keys, 0, sizeof(p->keys)); + de::zap(p->keys); #else if(!gameRules.deathmatch && newHub) + { p->keys = 0; + } #endif // Misc @@ -1836,7 +1864,7 @@ void G_PlayerLeaveMap(int player) p->update |= PSF_MORPH_TIME; if(p->morphTics) { - p->readyWeapon = p->plr->mo->special1; // Restore weapon. + p->readyWeapon = weapontype_t(p->plr->mo->special1); // Restore weapon. p->morphTics = 0; } #endif @@ -1909,41 +1937,27 @@ void ClearPlayer(player_t *p) */ void G_PlayerReborn(int player) { - player_t *p; - int frags[MAXPLAYERS]; - int killcount, itemcount, secretcount; - -#if __JDOOM__ || __JHERETIC__ || __JDOOM64__ - int i; -#endif -#if __JHERETIC__ - dd_bool secret = false; - int spot; -#elif __JHEXEN__ - uint worldTimer; -#endif - if(player < 0 || player >= MAXPLAYERS) return; // Wha? App_Log(DE2_DEV_MAP_NOTE, "G_PlayerReborn: reseting player %i", player); - p = &players[player]; + player_t *p = &players[player]; + int frags[MAXPLAYERS]; DENG_ASSERT(sizeof(p->frags) == sizeof(frags)); memcpy(frags, p->frags, sizeof(frags)); - killcount = p->killCount; - itemcount = p->itemCount; - secretcount = p->secretCount; + int killcount = p->killCount; + int itemcount = p->itemCount; + int secretcount = p->secretCount; #if __JHEXEN__ - worldTimer = p->worldTimer; + uint worldTimer = p->worldTimer; #endif #if __JHERETIC__ - if(p->didSecret) - secret = true; - spot = p->startSpot; + dd_bool secret = p->didSecret; + int spot = p->startSpot; #endif // Clears (almost) everything. @@ -1954,14 +1968,14 @@ void G_PlayerReborn(int player) #endif memcpy(p->frags, frags, sizeof(p->frags)); - p->killCount = killcount; - p->itemCount = itemcount; + p->killCount = killcount; + p->itemCount = itemcount; p->secretCount = secretcount; #if __JHEXEN__ - p->worldTimer = worldTimer; + p->worldTimer = worldTimer; #endif - p->colorMap = cfg.playerColor[player]; - p->class_ = P_ClassForPlayerWhenRespawning(player, false); + p->colorMap = cfg.playerColor[player]; + p->class_ = P_ClassForPlayerWhenRespawning(player, false); #if __JHEXEN__ if(p->class_ == PCLASS_FIGHTER && !IS_NETGAME) { @@ -1969,18 +1983,18 @@ void G_PlayerReborn(int player) p->colorMap = 2; } #endif - p->useDown = p->attackDown = true; // Don't do anything immediately. - p->playerState = PST_LIVE; - p->health = maxHealth; + p->useDown = p->attackDown = true; // Don't do anything immediately. + p->playerState = PST_LIVE; + p->health = maxHealth; p->brain.changeWeapon = WT_NOCHANGE; #if __JDOOM__ || __JDOOM64__ - p->readyWeapon = p->pendingWeapon = WT_SECOND; - p->weapons[WT_FIRST].owned = true; + p->readyWeapon = p->pendingWeapon = WT_SECOND; + p->weapons[WT_FIRST ].owned = true; p->weapons[WT_SECOND].owned = true; // Initalize the player's ammo counts. - memset(p->ammo, 0, sizeof(p->ammo)); + de::zap(p->ammo); p->ammo[AT_CLIP].owned = 50; // See if the Values specify anything. @@ -1998,12 +2012,9 @@ void G_PlayerReborn(int player) } #ifdef _DEBUG + for(int k = 0; k < NUM_WEAPON_TYPES; ++k) { - int k; - for(k = 0; k < NUM_WEAPON_TYPES; ++k) - { - App_Log(DE2_DEV_MAP_MSG, "Player %i owns wpn %i: %i", player, k, p->weapons[k].owned); - } + App_Log(DE2_DEV_MAP_MSG, "Player %i owns wpn %i: %i", player, k, p->weapons[k].owned); } #endif @@ -2015,12 +2026,14 @@ void G_PlayerReborn(int player) #if __JDOOM__ || __JHERETIC__ || __JDOOM64__ // Reset maxammo. - for(i = 0; i < NUM_AMMO_TYPES; ++i) + for(int i = 0; i < NUM_AMMO_TYPES; ++i) + { p->ammo[i].max = maxAmmo[i]; + } #endif // Reset viewheight. - p->viewHeight = cfg.plrViewHeight; + p->viewHeight = cfg.plrViewHeight; p->viewHeightDelta = 0; // We'll need to update almost everything. @@ -2037,23 +2050,23 @@ void G_PlayerReborn(int player) } #if __JDOOM__ || __JDOOM64__ -void G_QueueBody(mobj_t* mo) +void G_QueueBody(mobj_t *mo) { - if(!mo) - return; + if(!mo) return; // Flush an old corpse if needed. if(bodyQueueSlot >= BODYQUEUESIZE) + { P_MobjRemove(bodyQueue[bodyQueueSlot % BODYQUEUESIZE], false); + } bodyQueue[bodyQueueSlot % BODYQUEUESIZE] = mo; bodyQueueSlot++; } #endif -int rebornLoadConfirmResponse(msgresponse_t response, int userValue, void* userPointer) +static int rebornLoadConfirmResponse(msgresponse_t response, int userValue, void * /*context*/) { - DENG_UNUSED(userPointer); if(response == MSG_YES) { gaLoadGameSlot = userValue; @@ -2063,7 +2076,7 @@ int rebornLoadConfirmResponse(msgresponse_t response, int userValue, void* userP { #if __JHEXEN__ // Load the last autosave? (Not optional in Hexen). - if(SaveSlots_SlotInUse(saveSlots, AUTO_SLOT)) + if(SV_SaveSlots()[AUTO_SLOT].isUsed()) { gaLoadGameSlot = AUTO_SLOT; G_SetGameAction(GA_LOADGAME); @@ -2100,13 +2113,13 @@ void G_DoReborn(int plrNum) int lastSlot = -1; // First ensure we have up-to-date info. - SaveSlots_UpdateAllSaveInfo(saveSlots); + SV_SaveSlots().updateAll(); // Use the latest save? if(cfg.loadLastSaveOnReborn) { lastSlot = Con_GetInteger("game-save-last-slot"); - if(!SaveSlots_SlotInUse(saveSlots, lastSlot)) lastSlot = -1; + if(!SV_SaveSlots()[lastSlot].isUsed()) lastSlot = -1; } // Use the latest autosave? (Not optional in Hexen). @@ -2114,7 +2127,7 @@ void G_DoReborn(int plrNum) if(cfg.loadAutoSaveOnReborn) { autoSlot = AUTO_SLOT; - if(!SaveSlots_SlotInUse(saveSlots, autoSlot)) autoSlot = -1; + if(!SV_SaveSlots()[autoSlot].isUsed()) autoSlot = -1; } #endif @@ -2127,9 +2140,9 @@ void G_DoReborn(int plrNum) { // Everything appears to be in order - schedule the game-save load! #if !__JHEXEN__ - const int chosenSlot = (lastSlot >= 0? lastSlot : autoSlot); + int const chosenSlot = (lastSlot >= 0? lastSlot : autoSlot); #else - const int chosenSlot = lastSlot; + int const chosenSlot = lastSlot; #endif if(!cfg.confirmRebornLoad) { @@ -2139,8 +2152,8 @@ void G_DoReborn(int plrNum) else { // Compose the confirmation message. - SaveInfo *info = SaveSlots_SaveInfo(saveSlots, chosenSlot); - AutoStr *msg = Str_Appendf(AutoStr_NewStd(), REBORNLOAD_CONFIRM, Str_Text(SaveInfo_Description(info))); + SaveInfo &saveInfo = SV_SaveSlots()[chosenSlot].saveInfo(); + AutoStr *msg = Str_Appendf(AutoStr_NewStd(), REBORNLOAD_CONFIRM, saveInfo.userDescription().toUtf8().constData()); S_LocalSound(SFX_REBORNLOAD_CONFIRM, NULL); Hu_MsgStart(MSG_YESNO, Str_Text(msg), rebornLoadConfirmResponse, chosenSlot, 0); } @@ -2149,7 +2162,7 @@ void G_DoReborn(int plrNum) // Autosave loading cannot be disabled in Hexen. #if __JHEXEN__ - if(SaveSlots_SlotInUse(saveSlots, AUTO_SLOT)) + if(SV_SaveSlots()[AUTO_SLOT].isUsed()) { gaLoadGameSlot = AUTO_SLOT; G_SetGameAction(GA_LOADGAME); @@ -2162,16 +2175,16 @@ void G_DoReborn(int plrNum) G_SetGameAction(GA_RESTARTMAP); } -static void G_InitNewGame(void) +static void G_InitNewGame() { #if __JHEXEN__ - SaveSlots_ClearSlot(saveSlots, BASE_SLOT); + SV_SaveSlots().clearSlot(BASE_SLOT); #endif /// @todo Do not clear this save slot. Instead we should set a game state /// flag to signal when a new game should be started instead of loading /// the autosave slot. - SaveSlots_ClearSlot(saveSlots, AUTO_SLOT); + SV_SaveSlots().clearSlot(AUTO_SLOT); #if __JHEXEN__ Game_InitACScriptsForNewGame(); @@ -2182,19 +2195,24 @@ static void G_InitNewGame(void) static void G_ApplyGameRuleFastMonsters(dd_bool fast) { static dd_bool oldFast = false; - int i; // Only modify when the rule changes state. if(fast == oldFast) return; oldFast = fast; /// @fixme Kludge: Assumes the original values speed values haven't been modified! - for(i = S_SARG_RUN1; i <= S_SARG_RUN8; ++i) + for(int i = S_SARG_RUN1; i <= S_SARG_RUN8; ++i) + { STATES[i].tics = fast? 1 : 2; - for(i = S_SARG_ATK1; i <= S_SARG_ATK3; ++i) + } + for(int i = S_SARG_ATK1; i <= S_SARG_ATK3; ++i) + { STATES[i].tics = fast? 4 : 8; - for(i = S_SARG_PAIN; i <= S_SARG_PAIN2; ++i) + } + for(int i = S_SARG_PAIN; i <= S_SARG_PAIN2; ++i) + { STATES[i].tics = fast? 1 : 2; + } // Kludge end. } #endif @@ -2203,14 +2221,13 @@ static void G_ApplyGameRuleFastMonsters(dd_bool fast) static void G_ApplyGameRuleFastMissiles(dd_bool fast) { static dd_bool oldFast = false; - int i; // Only modify when the rule changes state. if(fast == oldFast) return; oldFast = fast; /// @fixme Kludge: Assumes the original values speed values haven't been modified! - for(i = 0; MonsterMissileInfo[i].type != -1; ++i) + for(int i = 0; MonsterMissileInfo[i].type != -1; ++i) { MOBJINFO[MonsterMissileInfo[i].type].speed = MonsterMissileInfo[i].speed[fast? 1 : 0]; @@ -2228,12 +2245,12 @@ static void G_ApplyNewGameRules() if(gameRules.skill < SM_NOTHINGS) gameRules.skill = SM_NOTHINGS; if(gameRules.skill > NUM_SKILL_MODES - 1) - gameRules.skill = NUM_SKILL_MODES - 1; + gameRules.skill = skillmode_t(NUM_SKILL_MODES - 1); #if __JDOOM__ || __JHERETIC__ || __JDOOM64__ if(!IS_NETGAME) { - gameRules.deathmatch = false; + gameRules.deathmatch = false; gameRules.respawnMonsters = false; gameRules.noMonsters = CommandLine_Exists("-nomonsters")? true : false; @@ -2247,29 +2264,33 @@ static void G_ApplyNewGameRules() #if __JDOOM__ || __JHERETIC__ // Is respawning enabled at all in nightmare skill? if(gameRules.skill == SM_NIGHTMARE) + { gameRules.respawnMonsters = cfg.respawnMonstersNightmare; + } #endif // Fast monsters? #if __JDOOM__ || __JDOOM64__ - { - dd_bool fastMonsters = gameRules.fast; + dd_bool fastMonsters = gameRules.fast; # if __JDOOM__ - if(gameRules.skill == SM_NIGHTMARE) fastMonsters = true; -# endif - G_ApplyGameRuleFastMonsters(fastMonsters); + if(gameRules.skill == SM_NIGHTMARE) + { + fastMonsters = true; } +# endif + G_ApplyGameRuleFastMonsters(fastMonsters); #endif // Fast missiles? #if __JDOOM__ || __JHERETIC__ || __JDOOM64__ - { - dd_bool fastMissiles = gameRules.fast; + dd_bool fastMissiles = gameRules.fast; # if !__JDOOM64__ - if(gameRules.skill == SM_NIGHTMARE) fastMissiles = true; -# endif - G_ApplyGameRuleFastMissiles(fastMissiles); + if(gameRules.skill == SM_NIGHTMARE) + { + fastMissiles = true; } +# endif + G_ApplyGameRuleFastMissiles(fastMissiles); #endif if(IS_DEDICATED) @@ -2280,7 +2301,14 @@ static void G_ApplyNewGameRules() void G_LeaveMap(uint newMap, uint _entryPoint, dd_bool _secretExit) { - if(IS_CLIENT || (cyclingMaps && mapCycleNoExit)) return; +#if __JHEXEN__ + DENG2_UNUSED(_secretExit); +#else + DENG2_UNUSED2(newMap, _entryPoint); +#endif + + if(IS_CLIENT) return; + if(cyclingMaps && mapCycleNoExit) return; #if __JHEXEN__ if((gameMode == hexen_betademo || gameMode == hexen_demo) && newMap != DDMAXINT && newMap > 3) @@ -2292,18 +2320,21 @@ void G_LeaveMap(uint newMap, uint _entryPoint, dd_bool _secretExit) #endif #if __JHEXEN__ - nextMap = newMap; + nextMap = newMap; nextMapEntrance = _entryPoint; #else - secretExit = _secretExit; + secretExit = _secretExit; + # if __JDOOM__ // If no Wolf3D maps, no secret exit! if(secretExit && (gameModeBits & GM_ANY_DOOM2)) { - Uri* mapUri = G_ComposeMapUri(0, 30); - AutoStr* mapPath = Uri_Compose(mapUri); + Uri *mapUri = G_ComposeMapUri(0, 30); + AutoStr *mapPath = Uri_Compose(mapUri); if(!P_MapExists(Str_Text(mapPath))) + { secretExit = false; + } Uri_Delete(mapUri); } # endif @@ -2313,9 +2344,9 @@ void G_LeaveMap(uint newMap, uint _entryPoint, dd_bool _secretExit) } /** - * @return @c true, if the game has been completed. + * @return @c true iff the game has been completed. */ -dd_bool G_IfVictory(void) +dd_bool G_IfVictory() { #if __JDOOM64__ if(gameMap == 27) @@ -2326,16 +2357,19 @@ dd_bool G_IfVictory(void) if(gameMode == doom_chex) { if(gameMap == 4) + { return true; + } } else if((gameModeBits & GM_ANY_DOOM) && gameMap == 7) + { return true; + } #elif __JHERETIC__ if(gameMap == 7) { return true; } - #elif __JHEXEN__ if(nextMap == DDMAXINT && nextMapEntrance == DDMAXINT) { @@ -2345,18 +2379,18 @@ dd_bool G_IfVictory(void) return false; } -static int prepareIntermission(void* paramaters) +static int prepareIntermission(void * /*context*/) { #if __JDOOM__ || __JDOOM64__ || __JHERETIC__ - wmInfo.episode = gameEpisode; + wmInfo.episode = gameEpisode; wmInfo.currentMap = gameMap; - wmInfo.nextMap = nextMap; - wmInfo.didSecret = players[CONSOLEPLAYER].didSecret; + wmInfo.nextMap = nextMap; + wmInfo.didSecret = players[CONSOLEPLAYER].didSecret; # if __JDOOM__ || __JDOOM64__ - wmInfo.maxKills = totalKills; - wmInfo.maxItems = totalItems; - wmInfo.maxSecret = totalSecret; + wmInfo.maxKills = totalKills; + wmInfo.maxItems = totalItems; + wmInfo.maxSecret = totalSecret; G_PrepareWIData(); # endif @@ -2375,13 +2409,11 @@ static int prepareIntermission(void* paramaters) return 0; } -void G_DoMapCompleted(void) +void G_DoMapCompleted() { - int i; - G_SetGameAction(GA_NONE); - for(i = 0; i < MAXPLAYERS; ++i) + for(int i = 0; i < MAXPLAYERS; ++i) { if(players[i].plr->inGame) { @@ -2409,14 +2441,12 @@ void G_DoMapCompleted(void) // Go to an intermission? #if __JDOOM__ || __JHERETIC__ || __JDOOM64__ + ddmapinfo_t minfo; + if(Def_Get(DD_DEF_MAP_INFO, Str_Text(Uri_Compose(gameMapUri)), &minfo) && + (minfo.flags & MIF_NO_INTERMISSION)) { - ddmapinfo_t minfo; - if(Def_Get(DD_DEF_MAP_INFO, Str_Text(Uri_Compose(gameMapUri)), &minfo) && - (minfo.flags & MIF_NO_INTERMISSION)) - { - G_IntermissionDone(); - return; - } + G_IntermissionDone(); + return; } #elif __JHEXEN__ @@ -2431,9 +2461,10 @@ void G_DoMapCompleted(void) # if __JDOOM__ if((gameModeBits & (GM_DOOM|GM_DOOM_SHAREWARE|GM_DOOM_ULTIMATE)) && gameMap == 8) { - int i; - for(i = 0; i < MAXPLAYERS; ++i) + for(int i = 0; i < MAXPLAYERS; ++i) + { players[i].didSecret = true; + } } # endif @@ -2470,16 +2501,14 @@ void G_DoMapCompleted(void) } #if __JDOOM__ || __JDOOM64__ -void G_PrepareWIData(void) +void G_PrepareWIData() { - AutoStr *mapPath = Uri_Compose(gameMapUri); wbstartstruct_t *info = &wmInfo; - ddmapinfo_t minfo; - int i; - info->maxFrags = 0; // See if there is a par time definition. + AutoStr *mapPath = Uri_Compose(gameMapUri); + ddmapinfo_t minfo; if(Def_Get(DD_DEF_MAP_INFO, Str_Text(mapPath), &minfo) && minfo.parTime > 0) { info->parTime = TICRATE * (int) minfo.parTime; @@ -2490,16 +2519,16 @@ void G_PrepareWIData(void) } info->pNum = CONSOLEPLAYER; - for(i = 0; i < MAXPLAYERS; ++i) + for(int i = 0; i < MAXPLAYERS; ++i) { - player_t* p = &players[i]; - wbplayerstruct_t* pStats = &info->plyr[i]; + player_t *p = &players[i]; + wbplayerstruct_t *pStats = &info->plyr[i]; pStats->inGame = p->plr->inGame; - pStats->kills = p->killCount; - pStats->items = p->itemCount; + pStats->kills = p->killCount; + pStats->items = p->itemCount; pStats->secret = p->secretCount; - pStats->time = mapTime; + pStats->time = mapTime; memcpy(pStats->frags, p->frags, sizeof(pStats->frags)); } } @@ -2517,7 +2546,7 @@ dd_bool G_StartDebriefing() return G_StartFinale(fin.script, 0, FIMODE_AFTER, 0); } -void G_IntermissionDone(void) +void G_IntermissionDone() { // We have left Intermission, however if there is an InFine for debriefing // we should run it now. @@ -2532,7 +2561,9 @@ void G_IntermissionDone(void) #if __JDOOM__ || __JDOOM64__ if(secretExit) + { players[CONSOLEPLAYER].didSecret = true; + } #endif // Clear the currently playing script, if any. @@ -2549,36 +2580,206 @@ void G_IntermissionDone(void) G_SetGameAction(GA_LEAVEMAP); } -void G_DoEndDebriefing(void) +void G_DoEndDebriefing() { briefDisabled = true; G_IntermissionDone(); } -typedef struct { - const char* name; +#if __JHEXEN__ + +struct playerbackup_t +{ + player_t player; + uint numInventoryItems[NUM_INVENTORYITEM_TYPES]; + inventoryitemtype_t readyItem; +}; + +static void backupPlayersInHub(playerbackup_t playerBackup[MAXPLAYERS]) +{ + DENG_ASSERT(playerBackup != 0); + + for(int i = 0; i < MAXPLAYERS; ++i) + { + playerbackup_t *pb = playerBackup + i; + player_t *plr = players + i; + + memcpy(&pb->player, plr, sizeof(player_t)); + + // Make a copy of the inventory states also. + for(int k = 0; k < NUM_INVENTORYITEM_TYPES; ++k) + { + pb->numInventoryItems[k] = P_InventoryCount(i, inventoryitemtype_t(k)); + } + pb->readyItem = P_InventoryReadyItem(i); + } +} + +/** + * @param playerBackup Player state backup. + * @param mapEntrance Logical entry point number used to enter the map. + */ +static void restorePlayersInHub(playerbackup_t playerBackup[MAXPLAYERS], uint mapEntrance) +{ + DENG_ASSERT(playerBackup != 0); + + mobj_t *targetPlayerMobj = 0; + + for(int i = 0; i < MAXPLAYERS; ++i) + { + playerbackup_t *pb = playerBackup + i; + player_t *plr = players + i; + ddplayer_t *ddplr = plr->plr; + int oldKeys = 0, oldPieces = 0; + dd_bool oldWeaponOwned[NUM_WEAPON_TYPES]; + + if(!ddplr->inGame) continue; + + memcpy(plr, &pb->player, sizeof(player_t)); + + for(int k = 0; k < NUM_INVENTORYITEM_TYPES; ++k) + { + // Don't give back the wings of wrath if reborn. + if(k == IIT_FLY && plr->playerState == PST_REBORN) + continue; + + for(uint m = 0; m < pb->numInventoryItems[k]; ++m) + { + P_InventoryGive(i, inventoryitemtype_t(k), true); + } + } + P_InventorySetReadyItem(i, pb->readyItem); + + ST_LogEmpty(i); + plr->attacker = 0; + plr->poisoner = 0; + + if(IS_NETGAME || gameRules.deathmatch) + { + // In a network game, force all players to be alive + if(plr->playerState == PST_DEAD) + { + plr->playerState = PST_REBORN; + } + + if(!gameRules.deathmatch) + { + // Cooperative net-play; retain keys and weapons. + oldKeys = plr->keys; + oldPieces = plr->pieces; + + for(int k = 0; k < NUM_WEAPON_TYPES; ++k) + { + oldWeaponOwned[k] = plr->weapons[k].owned; + } + } + } + + bool wasReborn = (plr->playerState == PST_REBORN); + + if(gameRules.deathmatch) + { + de::zap(plr->frags); + ddplr->mo = 0; + G_DeathMatchSpawnPlayer(i); + } + else + { + if(playerstart_t const *start = P_GetPlayerStart(mapEntrance, i, false)) + { + mapspot_t const *spot = &mapSpots[start->spot]; + P_SpawnPlayer(i, cfg.playerClass[i], spot->origin[VX], + spot->origin[VY], spot->origin[VZ], spot->angle, + spot->flags, false, true); + } + else + { + P_SpawnPlayer(i, cfg.playerClass[i], 0, 0, 0, 0, + MSF_Z_FLOOR, true, true); + } + } + + if(wasReborn && IS_NETGAME && !gameRules.deathmatch) + { + int bestWeapon = 0; + + // Restore keys and weapons when reborn in co-op. + plr->keys = oldKeys; + plr->pieces = oldPieces; + + for(int k = 0; k < NUM_WEAPON_TYPES; ++k) + { + if(oldWeaponOwned[k]) + { + bestWeapon = k; + plr->weapons[k].owned = true; + } + } + + plr->ammo[AT_BLUEMANA].owned = 25; /// @todo values.ded + plr->ammo[AT_GREENMANA].owned = 25; /// @todo values.ded + + // Bring up the best weapon. + if(bestWeapon) + { + plr->pendingWeapon = weapontype_t(bestWeapon); + } + } + } + + for(int i = 0; i < MAXPLAYERS; ++i) + { + player_t *plr = players + i; + ddplayer_t *ddplr = plr->plr; + + if(!ddplr->inGame) continue; + + if(!targetPlayerMobj) + { + targetPlayerMobj = ddplr->mo; + } + } + + /// @todo Redirect anything targeting a player mobj + /// FIXME! This only supports single player games!! + if(targetPlayerAddrs) + { + for(targetplraddress_t *p = targetPlayerAddrs; p; p = p->next) + { + *(p->address) = targetPlayerMobj; + } + + SV_ClearTargetPlayers(); + + /* + * When XG is available in Hexen, call this after updating target player + * references (after a load) - ds + // The activator mobjs must be set. + XL_UpdateActivators(); + */ + } + + // Destroy all things touching players. + P_TelefragMobjsTouchingPlayers(); +} +#endif // __JHEXEN__ + +struct savestateworker_params_t +{ + char const *name; int slot; -} savestateworker_params_t; +}; -static int G_SaveStateWorker(void* parameters) +static int G_SaveStateWorker(void *context) { - savestateworker_params_t* p = (savestateworker_params_t*)parameters; + savestateworker_params_t *p = (savestateworker_params_t *)context; int result = SV_SaveGame(p->slot, p->name); BusyMode_WorkerEnd(); return result; } -void G_DoLeaveMap(void) +void G_DoLeaveMap() { -#if __JHEXEN__ - playerbackup_t playerBackup[MAXPLAYERS]; - dd_bool oldRandomClassesRule; -#endif - loadmap_params_t p; - ddfinale_t fin; - dd_bool revisit = false; - dd_bool hasBrief; - // Unpause the current game. Pause_End(); @@ -2591,53 +2792,54 @@ void G_DoLeaveMap(void) // Ensure that the episode and map indices are good. G_ValidateMap(&gameEpisode, &nextMap); + bool revisit = false; #if __JHEXEN__ /* * First, determine whether we've been to this map previously and if so, * whether we need to load the archived map state. */ revisit = SV_HxHaveMapStateForSlot(BASE_SLOT, nextMap); - if(gameRules.deathmatch) revisit = false; + if(gameRules.deathmatch) + { + revisit = false; + } // Same hub? + Uri *nextMapUri = G_ComposeMapUri(gameEpisode, nextMap); + if(P_MapInfo(0/*current map*/)->hub == P_MapInfo(nextMapUri)->hub) { - Uri *nextMapUri = G_ComposeMapUri(gameEpisode, nextMap); - if(P_MapInfo(0/*current map*/)->hub == P_MapInfo(nextMapUri)->hub) + if(!gameRules.deathmatch) { - if(!gameRules.deathmatch) - { - // Save current map. - SV_HxSaveHubMap(); - } + // Save current map. + SV_HxSaveHubMap(); } - else // Entering new hub. + } + else // Entering new hub. + { + if(!gameRules.deathmatch) { - if(!gameRules.deathmatch) - { - SaveSlots_ClearSlot(saveSlots, BASE_SLOT); - } + SV_SaveSlots().clearSlot(BASE_SLOT); } - - Uri_Delete(nextMapUri); } + Uri_Delete(nextMapUri); nextMapUri = 0; // Take a copy of the player objects (they will be cleared in the process // of calling P_SetupMap() and we need to restore them after). - SV_HxBackupPlayersInHub(playerBackup); + playerbackup_t playerBackup[MAXPLAYERS]; + backupPlayersInHub(playerBackup); // Disable class randomization (all players must spawn as their existing class). - oldRandomClassesRule = gameRules.randomClasses; + byte oldRandomClassesRule = gameRules.randomClasses; gameRules.randomClasses = false; // We don't want to see a briefing if we've already visited this map. if(revisit) briefDisabled = true; /// @todo Necessary? - { uint i; - for(i = 0; i < MAXPLAYERS; ++i) + for(uint i = 0; i < MAXPLAYERS; ++i) { - player_t* plr = players + i; - ddplayer_t* ddplr = plr->plr; + player_t *plr = players + i; + ddplayer_t *ddplr = plr->plr; if(!ddplr->inGame) continue; @@ -2650,7 +2852,7 @@ void G_DoLeaveMap(void) ST_AutomapOpen(i, false, true); Hu_InventoryOpen(i, false); - }} + } //<- todo end. // In Hexen the RNG is re-seeded each time the map changes. @@ -2663,16 +2865,19 @@ void G_DoLeaveMap(void) gameMapEntrance = 0; #endif + loadmap_params_t p; de::zap(p); p.mapUri = G_ComposeMapUri(gameEpisode, nextMap); p.revisit = revisit; - hasBrief = G_BriefingEnabled(p.mapUri, &fin); + ddfinale_t fin; + bool hasBrief = G_BriefingEnabled(p.mapUri, &fin); if(!hasBrief) { G_QueMapMusic(p.mapUri); } gameMap = nextMap; + Uri_Copy(gameMapUri, p.mapUri); // If we're the server, let clients know the map will change. NetSv_UpdateGameConfigDescription(); @@ -2698,7 +2903,7 @@ void G_DoLeaveMap(void) P_RemoveAllPlayerMobjs(); } - SV_HxRestorePlayersInHub(playerBackup, nextMapEntrance); + restorePlayersInHub(playerBackup, nextMapEntrance); // Restore the random class rule. gameRules.randomClasses = oldRandomClassesRule; @@ -2712,10 +2917,8 @@ void G_DoLeaveMap(void) // In a non-network, non-deathmatch game, save immediately into the autosave slot. if(!IS_NETGAME && !gameRules.deathmatch) { - AutoStr *name = G_GenerateSaveGameName(); - savestateworker_params_t p; - - p.name = Str_Text(name); + savestateworker_params_t p; de::zap(p); + p.name = Str_Text(G_GenerateUserSaveDescription()); p.slot = AUTO_SLOT; /// @todo Use progress bar mode and update progress during the setup. @@ -2724,7 +2927,7 @@ void G_DoLeaveMap(void) } } -void G_DoRestartMap(void) +void G_DoRestartMap() { #if __JHEXEN__ // This is a restart, so we won't brief again. @@ -2763,12 +2966,12 @@ void G_DoRestartMap(void) #endif } -dd_bool G_IsLoadGamePossible(void) +dd_bool G_IsLoadGamePossible() { return !(IS_CLIENT && !Get(DD_PLAYBACK)); } -dd_bool G_LoadGame(int slot) +dd_bool G_LoadGame(int slotNumber) { if(!G_IsLoadGamePossible()) return false; @@ -2777,27 +2980,27 @@ dd_bool G_LoadGame(int slot) // no guarantee that the game-save will be accessible come load time. // First ensure we have up-to-date info. - SaveSlots_UpdateAllSaveInfo(saveSlots); + SV_SaveSlots().updateAll(); - if(!SaveSlots_SlotInUse(saveSlots, slot)) + if(SV_SaveSlots()[slotNumber].isUsed()) { - App_Log(DE2_RES_ERROR, "Cannot load from save slot #%i: not in use", slot); - return false; + // Everything appears to be in order - schedule the game-save load! + gaLoadGameSlot = slotNumber; + G_SetGameAction(GA_LOADGAME); + return true; } - // Everything appears to be in order - schedule the game-save load! - gaLoadGameSlot = slot; - G_SetGameAction(GA_LOADGAME); - return true; + App_Log(DE2_RES_ERROR, "Cannot load from save slot #%i: not in use", slotNumber); + return false; } /** * Called by G_Ticker based on gameaction. */ -void G_DoLoadGame(void) +void G_DoLoadGame() { #if __JHEXEN__ - dd_bool mustCopyBaseToAutoSlot = (gaLoadGameSlot != AUTO_SLOT); + bool mustCopyBaseToAutoSlot = (gaLoadGameSlot != AUTO_SLOT); #endif G_SetGameAction(GA_NONE); @@ -2808,18 +3011,16 @@ void G_DoLoadGame(void) if(IS_NETGAME) return; // Copy the base slot to the autosave slot. - SaveSlots_CopySlot(saveSlots, BASE_SLOT, AUTO_SLOT); + SV_SaveSlots().copySlot(BASE_SLOT, AUTO_SLOT); #endif } -dd_bool G_IsSaveGamePossible(void) +dd_bool G_IsSaveGamePossible() { - player_t* player; - if(IS_CLIENT || Get(DD_PLAYBACK)) return false; if(GS_MAP != G_GameState()) return false; - player = &players[CONSOLEPLAYER]; + player_t *player = &players[CONSOLEPLAYER]; if(PST_DEAD == player->playerState) return false; return true; @@ -2827,27 +3028,27 @@ dd_bool G_IsSaveGamePossible(void) dd_bool G_SaveGame2(int slot, char const *name) { - if(0 > slot || slot >= SaveSlots_SlotCount(saveSlots)) return false; + if(0 > slot || slot >= SV_SaveSlots().slotCount()) return false; if(!G_IsSaveGamePossible()) return false; gaSaveGameSlot = slot; - if(!gaSaveGameName) + if(!gaSaveGameUserDescription) { - gaSaveGameName = Str_New(); + gaSaveGameUserDescription = Str_New(); } if(name && name[0]) { // A new name. - gaSaveGameGenerateName = false; - Str_Set(gaSaveGameName, name); + gaSaveGameGenerateDescription = false; + Str_Set(gaSaveGameUserDescription, name); } else { // Reusing the current name or generating a new one. - gaSaveGameGenerateName = (name && !name[0]); - Str_Clear(gaSaveGameName); + gaSaveGameGenerateDescription = (name && !name[0]); + Str_Clear(gaSaveGameUserDescription); } G_SetGameAction(GA_SAVEGAME); @@ -2859,20 +3060,16 @@ dd_bool G_SaveGame(int slot) return G_SaveGame2(slot, NULL); } -AutoStr *G_GenerateSaveGameName(void) +AutoStr *G_GenerateUserSaveDescription() { - AutoStr *str = AutoStr_New(); - int time = mapTime / TICRATE, hours, seconds, minutes; - char const *baseName, *mapTitle; - char baseNameBuf[256]; - AutoStr *mapPath; + int time = mapTime / TICRATE; - hours = time / 3600; time -= hours * 3600; - minutes = time / 60; time -= minutes * 60; - seconds = time; + int const hours = time / 3600; time -= hours * 3600; + int const minutes = time / 60; time -= minutes * 60; + int const seconds = time; - mapPath = Uri_Compose(gameMapUri); - mapTitle = P_MapTitle(0/*current map*/); + AutoStr *mapPath = Uri_Compose(gameMapUri); + char const *mapTitle = P_MapTitle(0/*current map*/); // Still no map title? Use the identifier. // Some tricksy modders provide us with an empty title... @@ -2882,15 +3079,17 @@ AutoStr *G_GenerateSaveGameName(void) mapTitle = Str_Text(mapPath); } - baseName = 0; + char baseNameBuf[256]; + char const *baseName = 0; if(P_MapIsCustom(Str_Text(mapPath))) { F_ExtractFileBase(baseNameBuf, Str_Text(P_MapSourceFile(Str_Text(mapPath))), 256); baseName = baseNameBuf; } + AutoStr *str = AutoStr_New(); Str_Appendf(str, "%s%s%s %02i:%02i:%02i", (baseName? baseName : ""), - (baseName? ":" : ""), mapTitle, hours, minutes, seconds); + (baseName? ":" : ""), mapTitle, hours, minutes, seconds); return str; } @@ -2898,39 +3097,38 @@ AutoStr *G_GenerateSaveGameName(void) /** * Called by G_Ticker based on gameaction. */ -void G_DoSaveGame(void) +void G_DoSaveGame() { - savestateworker_params_t p; - char const *name; - dd_bool didSave; - - if(gaSaveGameName && !Str_IsEmpty(gaSaveGameName)) + char const *description; + if(gaSaveGameUserDescription && !Str_IsEmpty(gaSaveGameUserDescription)) { - name = Str_Text(gaSaveGameName); + description = Str_Text(gaSaveGameUserDescription); } else { // No name specified. - SaveInfo *info = SaveSlots_SaveInfo(saveSlots, gaSaveGameSlot); - if(!gaSaveGameGenerateName && !Str_IsEmpty(SaveInfo_Description(info))) + SaveInfo &saveInfo = SV_SaveSlots()[gaSaveGameSlot].saveInfo(); + if(!gaSaveGameGenerateDescription && !saveInfo.userDescription().isEmpty()) { // Slot already in use; reuse the existing name. - name = Str_Text(SaveInfo_Description(info)); + description = Str_Text(AutoStr_FromTextStd(saveInfo.userDescription().toUtf8().constData())); } else { - name = Str_Text(G_GenerateSaveGameName()); + description = Str_Text(G_GenerateUserSaveDescription()); } } - /** + /* * Try to make a new game-save. */ - p.name = name; + savestateworker_params_t p; de::zap(p); + p.name = description; p.slot = gaSaveGameSlot; + /// @todo Use progress bar mode and update progress during the setup. - didSave = (BusyMode_RunNewTaskWithName(BUSYF_ACTIVITY | /*BUSYF_PROGRESS_BAR |*/ (verbose? BUSYF_CONSOLE_OUTPUT : 0), - G_SaveStateWorker, &p, "Saving game...") != 0); + bool didSave = (BusyMode_RunNewTaskWithName(BUSYF_ACTIVITY | /*BUSYF_PROGRESS_BAR |*/ (verbose? BUSYF_CONSOLE_OUTPUT : 0), + G_SaveStateWorker, &p, "Saving game...") != 0); if(didSave) { //Hu_MenuUpdateGameSaveWidgets(); @@ -3007,8 +3205,6 @@ static uint mapNumberFor(Uri const *mapUri) void G_NewGame(Uri const *mapUri, uint mapEntrance, GameRuleset const *rules) { - uint i; - DENG_ASSERT(mapUri != 0 && rules != 0); G_StopDemo(); @@ -3019,9 +3215,9 @@ void G_NewGame(Uri const *mapUri, uint mapEntrance, GameRuleset const *rules) // If there are any InFine scripts running, they must be stopped. FI_StackClear(); - for(i = 0; i < MAXPLAYERS; ++i) + for(uint i = 0; i < MAXPLAYERS; ++i) { - player_t *plr = players + i; + player_t *plr = players + i; ddplayer_t *ddplr = plr->plr; if(!ddplr->inGame) continue; @@ -3075,41 +3271,37 @@ void G_NewGame(Uri const *mapUri, uint mapEntrance, GameRuleset const *rules) NetSv_UpdateGameConfigDescription(); - { - loadmap_params_t p; - dd_bool showBrief; - ddfinale_t fin; - - p.mapUri = gameMapUri; - p.revisit = false; + loadmap_params_t p; de::zap(p); + p.mapUri = gameMapUri; + p.revisit = false; - showBrief = G_BriefingEnabled(p.mapUri, &fin); - if(!showBrief) - { - G_QueMapMusic(p.mapUri); - } + ddfinale_t fin; + bool showBrief = G_BriefingEnabled(p.mapUri, &fin); + if(!showBrief) + { + G_QueMapMusic(p.mapUri); + } - // If we're the server, let clients know the map will change. - NetSv_SendGameState(GSF_CHANGE_MAP, DDSP_ALL_PLAYERS); + // If we're the server, let clients know the map will change. + NetSv_SendGameState(GSF_CHANGE_MAP, DDSP_ALL_PLAYERS); - G_DoLoadMap(&p); + G_DoLoadMap(&p); - if(showBrief) - { - G_StartFinale(fin.script, 0, FIMODE_BEFORE, 0); - } - else - { - // No briefing; begin the map. - HU_WakeWidgets(-1 /* all players */); - G_BeginMap(); - } + if(showBrief) + { + G_StartFinale(fin.script, 0, FIMODE_BEFORE, 0); + } + else + { + // No briefing; begin the map. + HU_WakeWidgets(-1 /* all players */); + G_BeginMap(); } Z_CheckHeap(); } -int G_QuitGameResponse(msgresponse_t response, int userValue, void* userPointer) +int G_QuitGameResponse(msgresponse_t response, int /*userValue*/, void * /*userPointer*/) { if(response == MSG_YES) { @@ -3118,9 +3310,8 @@ int G_QuitGameResponse(msgresponse_t response, int userValue, void* userPointer) return true; } -void G_QuitGame(void) +void G_QuitGame() { - char const *endString; if(G_QuitInProgress()) return; if(Hu_IsMessageActiveWithCallback(G_QuitGameResponse)) @@ -3131,6 +3322,7 @@ void G_QuitGame(void) return; } + char const *endString; #if __JDOOM__ || __JDOOM64__ endString = endmsg[((int) GAMETIC % (NUM_QUITMESSAGES + 1))]; #else @@ -3141,7 +3333,82 @@ void G_QuitGame(void) Hu_MsgStart(MSG_YESNO, endString, G_QuitGameResponse, 0, NULL); } -char const *P_GetGameModeName(void) +AutoStr *G_IdentityKeyForLegacyGamemode(int gamemode, int saveVersion) +{ + static char const *identityKeys[] = { +#if __JDOOM__ + /*doom_shareware*/ "doom1-share", + /*doom*/ "doom1", + /*doom_ultimate*/ "doom1-ultimate", + /*doom_chex*/ "chex", + /*doom2*/ "doom2", + /*doom2_plut*/ "doom2-plut", + /*doom2_tnt*/ "doom2-tnt", + /*doom2_hacx*/ "hacx", +#elif __JHERETIC__ + /*heretic_shareware*/ "heretic-share", + /*heretic*/ "heretic", + /*heretic_extended*/ "heretic-ext", +#elif __JHEXEN__ + /*hexen_demo*/ "hexen-demo", + /*hexen*/ "hexen", + /*hexen_deathkings*/ "hexen-dk", + /*hexen_betademo*/ "hexen-betademo", + /*hexen_v10*/ "hexen-v10", +#endif + "" + }; +#if __JDOOM__ || __JHERETIC__ + static gamemode_t const oldGamemodes[] = { +# if __JDOOM__ + doom_shareware, + doom, + doom2, + doom_ultimate +# else // __JHERETIC__ + heretic_shareware, + heretic, + heretic_extended +# endif + }; +#endif + + // The gamemode may first need to be translated (if *really* old). +#if __JDOOM__ || __JHERETIC__ +# if __JDOOM__ + if(saveVersion < 9) +# elif __JHERETIC__ + if(saveVersion < 8) +# endif + { + DENG2_ASSERT(gamemode >= 0 && (unsigned)gamemode < sizeof(oldGamemodes) / sizeof(oldGamemodes[0])); + gamemode = oldGamemodes[(int)(gamemode)]; + +# if __JDOOM__ + /** + * @note Kludge: Older versions did not differentiate between versions + * of Doom2 (i.e., Plutonia and TNT are marked as Doom2). If we detect + * that this save is from some version of Doom2, replace the marked + * gamemode with the current gamemode. + */ + if(gamemode == doom2 && (gameModeBits & GM_ANY_DOOM2)) + { + GameInfo gameInfo; + DD_GameInfo(&gameInfo); + return gameInfo.identityKey; + } + /// kludge end. +# endif + } +#else + DENG2_UNUSED(saveVersion); +#endif + + DENG2_ASSERT(gamemode >= 0 && (unsigned)gamemode < sizeof(identityKeys) / sizeof(identityKeys[0])); + return AutoStr_FromTextStd(identityKeys[gamemode]); +} + +char const *P_GetGameModeName() { static char const *dm = "deathmatch"; static char const *coop = "cooperative"; @@ -3154,7 +3421,7 @@ char const *P_GetGameModeName(void) return sp; } -uint G_GenerateSessionId(void) +uint G_GenerateSessionId() { return Timer_RealMilliseconds() + (mapTime << 24); } @@ -3163,8 +3430,10 @@ uint G_LogicalMapNumber(uint episode, uint map) { #if __JHEXEN__ return P_TranslateMap(map); + DENG_UNUSED(episode); #elif __JDOOM64__ return map; + DENG_UNUSED(episode); #else # if __JDOOM__ if(gameModeBits & (GM_ANY_DOOM2|GM_DOOM_CHEX)) @@ -3177,7 +3446,7 @@ uint G_LogicalMapNumber(uint episode, uint map) #endif } -uint G_CurrentLogicalMapNumber(void) +uint G_CurrentLogicalMapNumber() { return G_LogicalMapNumber(gameEpisode, gameMap); } @@ -3187,6 +3456,7 @@ Uri *G_ComposeMapUri(uint episode, uint map) lumpname_t mapId; #if __JDOOM64__ dd_snprintf(mapId, LUMPNAME_T_MAXLEN, "MAP%02u", map+1); + DENG_UNUSED(episode); #elif __JDOOM__ if(gameModeBits & GM_ANY_DOOM2) dd_snprintf(mapId, LUMPNAME_T_MAXLEN, "MAP%02u", map+1); @@ -3196,15 +3466,14 @@ Uri *G_ComposeMapUri(uint episode, uint map) dd_snprintf(mapId, LUMPNAME_T_MAXLEN, "E%uM%u", episode+1, map+1); #else dd_snprintf(mapId, LUMPNAME_T_MAXLEN, "MAP%02u", map+1); + DENG_UNUSED(episode); #endif return Uri_NewWithPath2(mapId, RC_NULL); } dd_bool G_ValidateMap(uint *episode, uint *map) { - dd_bool ok = true; - AutoStr *path; - Uri *uri; + bool ok = true; #if __JDOOM__ if(gameModeBits & (GM_DOOM_SHAREWARE|GM_DOOM_CHEX)) @@ -3272,8 +3541,8 @@ dd_bool G_ValidateMap(uint *episode, uint *map) #endif // Check that the map truly exists. - uri = G_ComposeMapUri(*episode, *map); - path = Uri_Compose(uri); + Uri *uri = G_ComposeMapUri(*episode, *map); + AutoStr *path = Uri_Compose(uri); if(!P_MapExists(Str_Text(path))) { // (0,0) should exist always? @@ -3294,9 +3563,11 @@ uint G_GetNextMap(uint episode, uint map, dd_bool secretExit) Uri_Delete(mapUri); return nextMap; - DENG_UNUSED(secretExit); + DENG2_UNUSED(secretExit); #elif __JDOOM64__ + DENG2_UNUSED(episode); + if(secretExit) { switch(map) @@ -3406,12 +3677,12 @@ uint G_NextLogicalMapNumber(dd_bool secretExit) /** * Print a list of maps and the WAD files where they are from. */ -void G_PrintFormattedMapList(uint episode, char const** files, uint count) +void G_PrintFormattedMapList(uint episode, char const **files, uint count) { - char const* current = NULL; - uint i, k, rangeStart = 0, len; + char const *current = 0; + uint rangeStart = 0; - for(i = 0; i < count; ++i) + for(uint i = 0; i < count; ++i) { if(!current && files[i]) { @@ -3421,22 +3692,22 @@ void G_PrintFormattedMapList(uint episode, char const** files, uint count) else if(current && (!files[i] || stricmp(current, files[i]))) { // Print a range. - len = i - rangeStart; + uint len = i - rangeStart; LogBuffer_Printf(DE2_LOG_MAP, " "); // Indentation. if(len <= 2) { - for(k = rangeStart; k < i; ++k) + for(uint k = rangeStart; k < i; ++k) { - Uri* mapUri = G_ComposeMapUri(episode, k); - AutoStr* path = Uri_ToString(mapUri); + Uri *mapUri = G_ComposeMapUri(episode, k); + AutoStr *path = Uri_ToString(mapUri); LogBuffer_Printf(DE2_LOG_MAP, "%s%s", Str_Text(path), (k != i - 1) ? "," : ""); Uri_Delete(mapUri); } } else { - Uri* mapUri = G_ComposeMapUri(episode, rangeStart); - AutoStr* path = Uri_ToString(mapUri); + Uri *mapUri = G_ComposeMapUri(episode, rangeStart); + AutoStr *path = Uri_ToString(mapUri); LogBuffer_Printf(DE2_LOG_MAP, "%s-", Str_Text(path)); Uri_Delete(mapUri); @@ -3455,11 +3726,7 @@ void G_PrintFormattedMapList(uint episode, char const** files, uint count) } } -/** - * Print a list of all currently available maps and the location of the - * source file/directory which contains them. - */ -void G_PrintMapList(void) +void G_PrintMapList() { #if __JDOOM__ uint maxEpisodes = (gameModeBits & GM_ANY_DOOM)? 9 : 1; @@ -3472,17 +3739,16 @@ void G_PrintMapList(void) uint maxMapsPerEpisode = 99; #endif - uint episode, map; char const *sourceList[100]; - for(episode = 0; episode < maxEpisodes; ++episode) + for(uint episode = 0; episode < maxEpisodes; ++episode) { - memset((void *) sourceList, 0, sizeof(sourceList)); + de::zap(sourceList); // Find the name of each map (not all may exist). - for(map = 0; map < maxMapsPerEpisode; ++map) + for(uint map = 0; map < maxMapsPerEpisode; ++map) { - Uri *uri = G_ComposeMapUri(episode, map); + Uri *uri = G_ComposeMapUri(episode, map); AutoStr *path = P_MapSourceFile(Str_Text(Uri_Compose(uri))); if(!Str_IsEmpty(path)) { @@ -3530,7 +3796,9 @@ int G_DebriefingEnabled(Uri const *mapUri, ddfinale_t *fin) #endif if(G_GameState() == GS_INFINE || IS_CLIENT || Get(DD_PLAYBACK)) + { return false; + } // Is there such a finale definition? return Def_Get(DD_DEF_FINALE_AFTER, Str_Text(Uri_Compose(mapUri)), fin); @@ -3540,19 +3808,20 @@ int G_DebriefingEnabled(Uri const *mapUri, ddfinale_t *fin) * Stops both playback and a recording. Called at critical points like * starting a new game, or ending the game in the menu. */ -void G_StopDemo(void) +void G_StopDemo() { DD_Execute(true, "stopdemo"); } -int Hook_DemoStop(int hookType, int val, void* paramaters) +int Hook_DemoStop(int /*hookType*/, int val, void * /*context*/) { - dd_bool aborted = val != 0; + bool aborted = val != 0; G_ChangeGameState(GS_WAITING); if(!aborted && singledemo) - { // Playback ended normally. + { + // Playback ended normally. G_SetGameAction(GA_QUIT); return true; } @@ -3572,18 +3841,17 @@ int Hook_DemoStop(int hookType, int val, void* paramaters) #endif } - {int i; - for(i = 0; i < MAXPLAYERS; ++i) + for(int i = 0; i < MAXPLAYERS; ++i) { ST_AutomapOpen(i, false, true); #if __JHERETIC__ || __JHEXEN__ Hu_InventoryOpen(i, false); #endif - }} + } return true; } -void G_ScreenShot(void) +void G_ScreenShot() { G_SetGameAction(GA_SCREENSHOT); } @@ -3593,33 +3861,29 @@ void G_ScreenShot(void) * file name base. * @return Composed file name. */ -static AutoStr* composeScreenshotFileName(void) +static AutoStr *composeScreenshotFileName() { GameInfo gameInfo; - AutoStr* name; - int numPos; - if(!DD_GameInfo(&gameInfo)) { Con_Error("composeScreenshotFileName: Failed retrieving GameInfo."); return 0; // Unreachable. } - name = Str_Appendf(AutoStr_NewStd(), "%s-", Str_Text(gameInfo.identityKey)); - numPos = Str_Length(name); - { int i; - for(i = 0; i < 1e6; ++i) // Stop eventually... + AutoStr *name = Str_Appendf(AutoStr_NewStd(), "%s-", Str_Text(gameInfo.identityKey)); + int numPos = Str_Length(name); + for(int i = 0; i < 1e6; ++i) // Stop eventually... { Str_Appendf(name, "%03i.png", i); if(!F_FileExists(Str_Text(name))) break; Str_Truncate(name, numPos); - }} + } return name; } -void G_DoScreenShot(void) +void G_DoScreenShot() { - AutoStr* fileName = composeScreenshotFileName(); + AutoStr *fileName = composeScreenshotFileName(); if(fileName && M_ScreenShot(Str_Text(fileName), 24)) { /// @todo Do not use the console player's message log for this notification. @@ -3634,7 +3898,7 @@ void G_DoScreenShot(void) fileName? F_PrettyPath(Str_Text(fileName)) : "(null)"); } -static void openLoadMenu(void) +static void openLoadMenu() { Hu_MenuCommand(MCMD_OPEN); /// @todo This should be called automatically when opening the page @@ -3643,7 +3907,7 @@ static void openLoadMenu(void) Hu_MenuSetActivePage(Hu_MenuFindPageByName("LoadGame")); } -static void openSaveMenu(void) +static void openSaveMenu() { Hu_MenuCommand(MCMD_OPEN); /// @todo This should be called automatically when opening the page @@ -3654,6 +3918,8 @@ static void openSaveMenu(void) D_CMD(OpenLoadMenu) { + DENG_UNUSED(src); DENG_UNUSED(argc); DENG_UNUSED(argv); + if(!G_IsLoadGamePossible()) return false; openLoadMenu(); return true; @@ -3661,16 +3927,18 @@ D_CMD(OpenLoadMenu) D_CMD(OpenSaveMenu) { + DENG_UNUSED(src); DENG_UNUSED(argc); DENG_UNUSED(argv); + if(!G_IsSaveGamePossible()) return false; openSaveMenu(); return true; } -int loadGameConfirmResponse(msgresponse_t response, int userValue, void* userPointer) +static int loadGameConfirmResponse(msgresponse_t response, int userValue, void * /*userPointer*/) { if(response == MSG_YES) { - const int slot = userValue; + int const slot = userValue; G_LoadGame(slot); } return true; @@ -3678,8 +3946,9 @@ int loadGameConfirmResponse(msgresponse_t response, int userValue, void* userPoi D_CMD(LoadGame) { - dd_bool const confirm = (argc == 3 && !stricmp(argv[2], "confirm")); - int slot; + DENG_UNUSED(src); + + bool const confirm = (argc == 3 && !stricmp(argv[2], "confirm")); if(G_QuitInProgress()) return false; if(!G_IsLoadGamePossible()) return false; @@ -3692,28 +3961,25 @@ D_CMD(LoadGame) } // Ensure we have up-to-date info. - SaveSlots_UpdateAllSaveInfo(saveSlots); + SV_SaveSlots().updateAll(); - slot = SaveSlots_ParseSlotIdentifier(saveSlots, argv[1]); - if(SaveSlots_SlotInUse(saveSlots, slot)) + int const slotNumber = SV_SaveSlots().parseSlotIdentifier(argv[1]); + if(SV_SaveSlots()[slotNumber].isUsed()) { // A known used slot identifier. - SaveInfo *info; - AutoStr *msg; - if(confirm || !cfg.confirmQuickGameSave) { // Try to schedule a GA_LOADGAME action. S_LocalSound(SFX_MENU_ACCEPT, NULL); - return G_LoadGame(slot); + return G_LoadGame(slotNumber); } - info = SaveSlots_SaveInfo(saveSlots, slot); + SaveInfo &saveInfo = SV_SaveSlots()[slotNumber].saveInfo(); // Compose the confirmation message. - msg = Str_Appendf(AutoStr_NewStd(), QLPROMPT, Str_Text(SaveInfo_Description(info))); + AutoStr *msg = Str_Appendf(AutoStr_NewStd(), QLPROMPT, saveInfo.userDescription().toUtf8().constData()); S_LocalSound(SFX_QUICKLOAD_PROMPT, NULL); - Hu_MsgStart(MSG_YESNO, Str_Text(msg), loadGameConfirmResponse, slot, 0); + Hu_MsgStart(MSG_YESNO, Str_Text(msg), loadGameConfirmResponse, slotNumber, 0); return true; } else if(!stricmp(argv[1], "quick") || !stricmp(argv[1], "")) @@ -3743,16 +4009,18 @@ D_CMD(LoadGame) D_CMD(QuickLoadGame) { + DENG_UNUSED(src); DENG_UNUSED(argc); DENG_UNUSED(argv); + /// @todo Implement console command scripts? return DD_Execute(true, "loadgame quick"); } -int saveGameConfirmResponse(msgresponse_t response, int userValue, void* userPointer) +static int saveGameConfirmResponse(msgresponse_t response, int userValue, void *userPointer) { if(response == MSG_YES) { - const int slot = userValue; - ddstring_t* name = (ddstring_t*)userPointer; + int const slot = userValue; + ddstring_t *name = (ddstring_t *)userPointer; G_SaveGame2(slot, Str_Text(name)); // We're done with the name. Str_Delete(name); @@ -3762,9 +4030,10 @@ int saveGameConfirmResponse(msgresponse_t response, int userValue, void* userPoi D_CMD(SaveGame) { - const dd_bool confirm = (argc >= 3 && !stricmp(argv[argc-1], "confirm")); - player_t* player = &players[CONSOLEPLAYER]; - int slot; + DENG_UNUSED(src); + + bool const confirm = (argc >= 3 && !stricmp(argv[argc-1], "confirm")); + player_t *player = &players[CONSOLEPLAYER]; if(G_QuitInProgress()) return false; @@ -3789,33 +4058,32 @@ D_CMD(SaveGame) } // Ensure we have up-to-date info. - SaveSlots_UpdateAllSaveInfo(saveSlots); + SV_SaveSlots().updateAll(); - slot = SaveSlots_ParseSlotIdentifier(saveSlots, argv[1]); - if(SaveSlots_SlotIsUserWritable(saveSlots, slot)) + int const slotNumber = SV_SaveSlots().parseSlotIdentifier(argv[1]); + if(SV_SaveSlots().slotIsUserWritable(slotNumber)) { // A known slot identifier. - dd_bool const slotIsUsed = SaveSlots_SlotInUse(saveSlots, slot); - SaveInfo *info = SaveSlots_SaveInfo(saveSlots, slot); - ddstring_t localName, *name; - AutoStr *msg; + bool const slotIsUsed = SV_SaveSlots()[slotNumber].isUsed(); + ddstring_t localName; Str_InitStatic(&localName, (argc >= 3 && stricmp(argv[2], "confirm"))? argv[2] : ""); if(!slotIsUsed || confirm || !cfg.confirmQuickGameSave) { // Try to schedule a GA_LOADGAME action. S_LocalSound(SFX_MENU_ACCEPT, NULL); - return G_SaveGame2(slot, Str_Text(&localName)); + return G_SaveGame2(slotNumber, Str_Text(&localName)); } // Compose the confirmation message. - msg = Str_Appendf(AutoStr_NewStd(), QSPROMPT, Str_Text(SaveInfo_Description(info))); + SaveInfo &saveInfo = SV_SaveSlots()[slotNumber].saveInfo(); + AutoStr *msg = Str_Appendf(AutoStr_NewStd(), QSPROMPT, saveInfo.userDescription().toUtf8().constData()); // Make a copy of the name. - name = Str_Copy(Str_New(), &localName); + ddstring_t *name = Str_Copy(Str_New(), &localName); S_LocalSound(SFX_QUICKSAVE_PROMPT, NULL); - Hu_MsgStart(MSG_YESNO, Str_Text(msg), saveGameConfirmResponse, slot, (void*)name); + Hu_MsgStart(MSG_YESNO, Str_Text(msg), saveGameConfirmResponse, slotNumber, (void *)name); return true; } else if(!stricmp(argv[1], "quick") || !stricmp(argv[1], "")) @@ -3829,10 +4097,14 @@ D_CMD(SaveGame) } // Clearly the caller needs some assistance... - if(!SaveSlots_IsValidSlot(saveSlots, slot)) + if(!SV_SaveSlots().isKnownSlot(slotNumber)) + { App_Log(DE2_SCR_WARNING, "Failed to determine save slot from \"%s\"", argv[1]); + } else - App_Log(DE2_LOG_ERROR, "Save slot #%i is non-user-writable", slot); + { + App_Log(DE2_LOG_ERROR, "Save slot #%i is non-user-writable", slotNumber); + } // No action means the command failed. return false; @@ -3840,25 +4112,23 @@ D_CMD(SaveGame) D_CMD(QuickSaveGame) { + DENG_UNUSED(src); DENG_UNUSED(argc); DENG_UNUSED(argv); + /// @todo Implement console command scripts? return DD_Execute(true, "savegame quick"); } -dd_bool G_DeleteSaveGame(int slot) +dd_bool G_DeleteSaveGame(int slotNumber) { - SaveInfo *info; - - if(!SaveSlots_SlotIsUserWritable(saveSlots, slot)) return false; - if(!SaveSlots_SlotInUse(saveSlots, slot)) return false; + if(!SV_SaveSlots().slotIsUserWritable(slotNumber)) return false; + if(!SV_SaveSlots()[slotNumber].isUsed()) return false; // A known slot identifier. - info = SaveSlots_SaveInfo(saveSlots, slot); - DENG_ASSERT(info != 0); - SaveSlots_ClearSlot(saveSlots, slot); + SV_SaveSlots().clearSlot(slotNumber); if(Hu_MenuIsActive()) { - mn_page_t* activePage = Hu_MenuActivePage(); + mn_page_t *activePage = Hu_MenuActivePage(); if(activePage == Hu_MenuFindPageByName("LoadGame") || activePage == Hu_MenuFindPageByName("SaveGame")) { @@ -3867,15 +4137,15 @@ dd_bool G_DeleteSaveGame(int slot) Hu_MenuSetActivePage2(activePage, true); } } + return true; } -int deleteSaveGameConfirmResponse(msgresponse_t response, int userValue, void* userPointer) +static int deleteSaveGameConfirmResponse(msgresponse_t response, int userValue, void * /*context*/) { - DENG_UNUSED(userPointer); if(response == MSG_YES) { - const int slot = userValue; + int const slot = userValue; G_DeleteSaveGame(slot); } return true; @@ -3883,40 +4153,72 @@ int deleteSaveGameConfirmResponse(msgresponse_t response, int userValue, void* u D_CMD(DeleteGameSave) { - dd_bool const confirm = (argc >= 3 && !stricmp(argv[argc-1], "confirm")); - player_t *player = &players[CONSOLEPLAYER]; - int slot; + DENG2_UNUSED(src); + + bool const confirm = (argc >= 3 && !stricmp(argv[argc-1], "confirm")); if(G_QuitInProgress()) return false; // Ensure we have up-to-date info. - SaveSlots_UpdateAllSaveInfo(saveSlots); + SV_SaveSlots().updateAll(); - slot = SaveSlots_ParseSlotIdentifier(saveSlots, argv[1]); - if(SaveSlots_SlotIsUserWritable(saveSlots, slot) && - SaveSlots_SlotInUse(saveSlots, slot)) + int const slotNumber = SV_SaveSlots().parseSlotIdentifier(argv[1]); + if(SV_SaveSlots().slotIsUserWritable(slotNumber) && + SV_SaveSlots()[slotNumber].isUsed()) { // A known slot identifier. if(confirm) { - return G_DeleteSaveGame(slot); + return G_DeleteSaveGame(slotNumber); } else { // Compose the confirmation message. - SaveInfo *info = SaveSlots_SaveInfo(saveSlots, slot); - AutoStr *msg = Str_Appendf(AutoStr_NewStd(), DELETESAVEGAME_CONFIRM, Str_Text(SaveInfo_Description(info))); + SaveInfo &saveInfo = SV_SaveSlots()[slotNumber].saveInfo(); + AutoStr *msg = Str_Appendf(AutoStr_NewStd(), DELETESAVEGAME_CONFIRM, saveInfo.userDescription().toUtf8().constData()); S_LocalSound(SFX_DELETESAVEGAME_CONFIRM, NULL); - Hu_MsgStart(MSG_YESNO, Str_Text(msg), deleteSaveGameConfirmResponse, slot, 0); + Hu_MsgStart(MSG_YESNO, Str_Text(msg), deleteSaveGameConfirmResponse, slotNumber, 0); } return true; } // Clearly the caller needs some assistance... - if(!SaveSlots_IsValidSlot(saveSlots, slot)) + if(!SV_SaveSlots().isKnownSlot(slotNumber)) + { App_Log(DE2_SCR_WARNING, "Failed to determine save slot from \"%s\"", argv[1]); + } else - App_Log(DE2_LOG_ERROR, "Save slot #%i is non-user-writable", slot); + { + App_Log(DE2_LOG_ERROR, "Save slot #%i is non-user-writable", slotNumber); + } + + // No action means the command failed. + return false; +} + +D_CMD(InspectGameSave) +{ + DENG_UNUSED(src); DENG_UNUSED(argc); + + // Ensure we have up-to-date info. + SV_SaveSlots().updateAll(); + + int const slotNumber = SV_SaveSlots().parseSlotIdentifier(argv[1]); + if(SV_SaveSlots()[slotNumber].isUsed()) + { + SaveInfo &saveInfo = SV_SaveSlots()[slotNumber].saveInfo(); + App_Log(DE2_LOG_MESSAGE, "%s", saveInfo.description().toLatin1().constData()); + return true; + } + + if(!SV_SaveSlots().isKnownSlot(slotNumber)) + { + App_Log(DE2_SCR_WARNING, "Failed to determine save slot from \"%s\"", argv[1]); + } + else + { + App_Log(DE2_LOG_ERROR, "Save slot #%i is not in use", slotNumber); + } // No action means the command failed. return false; @@ -3924,24 +4226,32 @@ D_CMD(DeleteGameSave) D_CMD(HelpScreen) { + DENG_UNUSED(src); DENG_UNUSED(argc); DENG_UNUSED(argv); + G_StartHelp(); return true; } D_CMD(EndGame) { + DENG_UNUSED(src); DENG_UNUSED(argc); DENG_UNUSED(argv); + G_EndGame(); return true; } D_CMD(CycleTextureGamma) { + DENG_UNUSED(src); DENG_UNUSED(argc); DENG_UNUSED(argv); + R_CycleGammaLevel(); return true; } D_CMD(ListMaps) { + DENG_UNUSED(src); DENG_UNUSED(argc); DENG_UNUSED(argv); + App_Log(DE2_RES_MSG, "Available maps:"); G_PrintMapList(); return true; @@ -3967,14 +4277,16 @@ D_CMD(ListMaps) */ D_CMD(WarpMap) { - dd_bool const forceNewGameSession = IS_NETGAME != 0; - uint epsd, map, i; + bool const forceNewGameSession = IS_NETGAME != 0; // Only server operators can warp maps in network games. /// @todo Implement vote or similar mechanics. if(IS_NETGAME && !IS_NETWORK_SERVER) + { return false; + } + uint epsd, map; #if __JDOOM__ || __JDOOM64__ || __JHEXEN__ # if __JDOOM__ if(gameModeBits & GM_ANY_DOOM2) @@ -4021,21 +4333,24 @@ D_CMD(WarpMap) P_SetMessage(players + CONSOLEPLAYER, LMF_NO_HIDE, Str_Text(msg)); return false; } + Uri *newMapUri = G_ComposeMapUri(epsd, map); #if __JHEXEN__ // Hexen does not allow warping to the current map. - if(!forceNewGameSession && gameInProgress && map == gameMap) + if(!forceNewGameSession && gameInProgress && + Uri_Equality(gameMapUri, newMapUri)) { P_SetMessage(players + CONSOLEPLAYER, LMF_NO_HIDE, "Cannot warp to the current map."); + Uri_Delete(newMapUri); return false; } #endif // Close any left open UIs. /// @todo Still necessary here? - for(i = 0; i < MAXPLAYERS; ++i) + for(int i = 0; i < MAXPLAYERS; ++i) { - player_t *plr = players + i; + player_t *plr = players + i; ddplayer_t *ddplr = plr->plr; if(!ddplr->inGame) continue; @@ -4049,23 +4364,19 @@ D_CMD(WarpMap) // So be it. briefDisabled = true; + if(!forceNewGameSession && gameInProgress) { - Uri *newMapUri = G_ComposeMapUri(epsd, map); - if(!forceNewGameSession && gameInProgress) - { #if __JHEXEN__ - nextMap = map; - nextMapEntrance = 0; - G_SetGameAction(GA_LEAVEMAP); + nextMap = map; + nextMapEntrance = 0; + G_SetGameAction(GA_LEAVEMAP); #else - G_DeferredNewGame(newMapUri, 0/*default*/, &gameRules); + G_DeferredNewGame(newMapUri, 0/*default*/, &gameRules); #endif - } - else - { - G_DeferredNewGame(newMapUri, 0/*default*/, &gameRules); - } - Uri_Delete(newMapUri); + } + else + { + G_DeferredNewGame(newMapUri, 0/*default*/, &gameRules); } // If the command source was "us" the game library then it was probably in @@ -4088,5 +4399,6 @@ D_CMD(WarpMap) S_LocalSound(soundId, NULL); } + Uri_Delete(newMapUri); return true; } diff --git a/doomsday/plugins/common/src/gamestatereader.cpp b/doomsday/plugins/common/src/gamestatereader.cpp index 2d001cb456..3130b95270 100644 --- a/doomsday/plugins/common/src/gamestatereader.cpp +++ b/doomsday/plugins/common/src/gamestatereader.cpp @@ -21,20 +21,21 @@ #include "common.h" #include "gamestatereader.h" -#include "d_net.h" +#include "d_net.h" // NetSv_LoadGame #include "g_common.h" -#include "hu_log.h" #include "mapstatereader.h" #include "p_mapsetup.h" // P_SpawnAllMaterialOriginScrollers #include "p_saveio.h" #include "p_saveg.h" /// @todo remove me #include "p_tick.h" // mapTime #include "r_common.h" // R_UpdateConsoleView +#include "saveslots.h" +#include #include DENG2_PIMPL(GameStateReader) { - SaveInfo *saveInfo; ///< Info for the save state to be read. + SaveInfo *saveInfo; ///< Info for the save state to be read. Not owned. Reader *reader; dd_bool loaded[MAXPLAYERS]; dd_bool infile[MAXPLAYERS]; @@ -48,13 +49,51 @@ DENG2_PIMPL(GameStateReader) de::zap(infile); } + ~Instance() + { + Reader_Delete(reader); + } + /// Assumes the reader is currently positioned at the start of the stream. void seekToGameState() { // Read the header again. - SaveInfo *tmp = new SaveInfo; - SV_SaveInfo_Read(tmp, reader); - delete tmp; + /// @todo seek straight to the game state. + delete SaveInfo::fromReader(reader); + } + + void beginSegment(int segId) + { +#if __JHEXEN__ + if(segId == ASEG_END && SV_HxBytesLeft() < 4) + { + App_Log(DE2_LOG_WARNING, "Savegame lacks ASEG_END marker (unexpected end-of-file)"); + return; + } + if(Reader_ReadInt32(reader) != segId) + { + /// @throw ReadError Failed alignment check. + throw ReadError("GameStateReader", "Corrupt save game, segment #" + de::String::number(segId) + " failed alignment check"); + } +#else + DENG_UNUSED(segId); +#endif + } + + void endSegment() + { + beginSegment(ASEG_END); + } + + void readConsistencyBytes() + { +#if !__JHEXEN__ + if(Reader_ReadByte(reader) != CONSISTENCY) + { + /// @throw ReadError Failed alignment check. + throw ReadError("GameStateReader", "Corrupt save game, failed consistency check"); + } +#endif } void readWorldACScriptData() @@ -62,35 +101,34 @@ DENG2_PIMPL(GameStateReader) #if __JHEXEN__ if(saveInfo->version() >= 7) { - SV_AssertSegment(ASEG_WORLDSCRIPTDATA); + beginSegment(ASEG_WORLDSCRIPTDATA); } Game_ACScriptInterpreter().readWorldScriptData(reader, saveInfo->version()); #endif } - void readMap() + void readMap(int thingArchiveSize) { #if __JHEXEN__ // The map state is actually read from a separate file. // Close the game state file. - Z_Free(saveBuffer); - SV_CloseFile(); + SV_HxReleaseSaveBuffer(); // Open the map state file. - SV_OpenMapSaveFile(saveSlots->composeSavePathForSlot(BASE_SLOT, gameMap + 1)); + de::Path path = SV_SavePath() / saveInfo->fileNameForMap(gameMap); + if(!SV_OpenMapSaveFile(path)) + { + throw FileAccessError("GameStateReader", "Failed opening \"" + de::NativePath(path).pretty() + "\""); + } #endif - MapStateReader(saveInfo->version()).read(reader); + MapStateReader(saveInfo->version(), thingArchiveSize).read(reader); + readConsistencyBytes(); #if __JHEXEN__ - Z_Free(saveBuffer); -#endif - -#if !__JHEXEN__ - SV_ReadConsistencyBytes(); + SV_HxReleaseSaveBuffer(); +#else SV_CloseFile(); #endif - - SV_ClearThingArchive(); #if __JHEXEN__ SV_ClearTargetPlayers(); #endif @@ -102,6 +140,17 @@ DENG2_PIMPL(GameStateReader) // discarded. void readPlayers() { +#if __JHEXEN__ + if(saveInfo->version() >= 4) +#else + if(saveInfo->version() >= 5) +#endif + { + beginSegment(ASEG_PLAYER_HEADER); + } + playerheader_t plrHdr; + plrHdr.read(reader, saveInfo->version()); + // Setup the dummy. ddplayer_t dummyDDPlayer; player_t dummyPlayer; @@ -111,11 +160,11 @@ DENG2_PIMPL(GameStateReader) { loaded[i] = 0; #if !__JHEXEN__ - infile[i] = saveInfo->_players[i]; + infile[i] = saveInfo->players()[i]; #endif } - SV_AssertSegment(ASEG_PLAYERS); + beginSegment(ASEG_PLAYERS); { #if __JHEXEN__ for(int i = 0; i < MAXPLAYERS; ++i) @@ -145,7 +194,7 @@ DENG2_PIMPL(GameStateReader) loaded[k] = true; // Later references to the player number 'i' must be translated! saveToRealPlayerNum[i] = k; - App_Log(DE2_DEV_MAP_MSG, "readPlayers: saved %i is now %i\n", i, k); + App_Log(DE2_DEV_MAP_MSG, "readPlayers: saved %i is now %i", i, k); break; } } @@ -157,27 +206,29 @@ DENG2_PIMPL(GameStateReader) } // Read the data. - player->read(reader); + player->read(reader, plrHdr); } } - SV_AssertSegment(ASEG_END); + endSegment(); } void kickMissingPlayers() { for(int i = 0; i < MAXPLAYERS; ++i) { - dd_bool notLoaded = false; + bool notLoaded = false; #if __JHEXEN__ if(players[i].plr->inGame) { // Try to find a saved player that corresponds this one. - uint k; + int k; for(k = 0; k < MAXPLAYERS; ++k) { if(saveToRealPlayerNum[k] == i) + { break; + } } if(k < MAXPLAYERS) continue; // Found; don't bother this player. @@ -223,37 +274,28 @@ DENG2_PIMPL(GameStateReader) GameStateReader::GameStateReader() : d(new Instance(this)) {} -bool GameStateReader::recognize(SaveInfo *info, Str const *path) // static +GameStateReader::~GameStateReader() +{} + +bool GameStateReader::recognize(SaveInfo &info) // static { - DENG_ASSERT(info != 0 && path != 0); + de::Path const path = SV_SavePath() / info.fileName(); if(!SV_ExistingFile(path)) return false; - -#if __JHEXEN__ - /// @todo Do not buffer the whole file. - byte *saveBuffer; - size_t fileSize = M_ReadFile(Str_Text(path), (char **) &saveBuffer); - if(!fileSize) return false; - - // Set the save pointer. - SV_HxSavePtr()->b = saveBuffer; - SV_HxSetSaveEndPtr(saveBuffer + fileSize); -#else - if(!SV_OpenFile(path, "rp")) return false; -#endif + if(!SV_OpenGameSaveFile(path, false/*for reading*/)) return false; Reader *reader = SV_NewReader(); - SV_SaveInfo_Read(info, reader); + info.read(reader); Reader_Delete(reader); #if __JHEXEN__ - Z_Free(saveBuffer); + SV_HxReleaseSaveBuffer(); #else SV_CloseFile(); #endif // Magic must match. - if(info->magic() != MY_SAVE_MAGIC && info->magic() != MY_CLIENT_SAVE_MAGIC) + if(info.magic() != MY_SAVE_MAGIC && info.magic() != MY_CLIENT_SAVE_MAGIC) { return false; } @@ -261,7 +303,7 @@ bool GameStateReader::recognize(SaveInfo *info, Str const *path) // static /* * Check for unsupported versions. */ - if(info->version() > MY_SAVE_VERSION) // Future version? + if(info.version() > MY_SAVE_VERSION) // Future version? { return false; } @@ -269,7 +311,7 @@ bool GameStateReader::recognize(SaveInfo *info, Str const *path) // static #if __JHEXEN__ // We are incompatible with v3 saves due to an invalid test used to determine // present sides (ver3 format's sides contain chunks of junk data). - if(info->version() == 3) + if(info.version() == 3) { return false; } @@ -278,24 +320,26 @@ bool GameStateReader::recognize(SaveInfo *info, Str const *path) // static return true; } -void GameStateReader::read(SaveInfo *saveInfo, Str const *path) +IGameStateReader *GameStateReader::make() // static +{ + return new GameStateReader; +} + +void GameStateReader::read(SaveInfo &info) { - DENG_ASSERT(saveInfo != 0 && path != 0); - d->saveInfo = saveInfo; + de::Path const path = SV_SavePath() / info.fileName(); - playerHeaderOK = false; // Uninitialized. + d->saveInfo = &info; - if(!SV_OpenGameSaveFile(path, false)) + if(!SV_OpenGameSaveFile(path, false/*for reading*/)) { - throw FileAccessError("GameStateReader", "Failed opening \"" + de::String(Str_Text(path)) + "\""); + throw FileAccessError("GameStateReader", "Failed opening \"" + de::NativePath(path).pretty() + "\""); } d->reader = SV_NewReader(); d->seekToGameState(); - curInfo = d->saveInfo; - d->readWorldACScriptData(); /* @@ -309,14 +353,13 @@ void GameStateReader::read(SaveInfo *saveInfo, Str const *path) mapTime = d->saveInfo->mapTime(); #endif + int thingArchiveSize = 0; #if !__JHEXEN__ - SV_InitThingArchiveForLoad(d->saveInfo->version() >= 5? Reader_ReadInt32(d->reader) : 1024 /* num elements */); + thingArchiveSize = (d->saveInfo->version() >= 5? Reader_ReadInt32(d->reader) : 1024); #endif - SV_ReadPlayerHeader(d->reader, d->saveInfo->version()); - d->readPlayers(); - d->readMap(); + d->readMap(thingArchiveSize); // Notify the players that weren't in the savegame. d->kickMissingPlayers(); diff --git a/doomsday/plugins/common/src/gamestatewriter.cpp b/doomsday/plugins/common/src/gamestatewriter.cpp index cebb5f031e..09b43eed8b 100644 --- a/doomsday/plugins/common/src/gamestatewriter.cpp +++ b/doomsday/plugins/common/src/gamestatewriter.cpp @@ -21,22 +21,54 @@ #include "common.h" #include "gamestatewriter.h" +#include "d_net.h" // NetSv_SaveGame #include "mapstatewriter.h" #include "p_saveio.h" -#include "p_saveg.h" /// @todo remove me -#include +#include "p_saveg.h" /// playerheader_t @todo remove me +#include "thingarchive.h" +#include "saveslots.h" +#include DENG2_PIMPL(GameStateWriter) { - SaveInfo *saveInfo; ///< Info for the save state to be written. + SaveInfo *saveInfo; ///< Info for the save state to be written. Not owned. + ThingArchive *thingArchive; Writer *writer; Instance(Public *i) : Base(i) , saveInfo(0) + , thingArchive(0) , writer(0) {} + ~Instance() + { + Writer_Delete(writer); + delete thingArchive; + } + + void beginSegment(int segId) + { +#if __JHEXEN__ + Writer_WriteInt32(writer, segId); +#else + DENG_UNUSED(segId); +#endif + } + + void endSegment() + { + beginSegment(ASEG_END); + } + + void writeConsistencyBytes() + { +#if !__JHEXEN__ + Writer_WriteByte(writer, CONSISTENCY); +#endif + } + void writeSessionHeader() { saveInfo->write(writer); @@ -45,7 +77,7 @@ DENG2_PIMPL(GameStateWriter) void writeWorldACScriptData() { #if __JHEXEN__ - SV_BeginSegment(ASEG_WORLDSCRIPTDATA); + beginSegment(ASEG_WORLDSCRIPTDATA); Game_ACScriptInterpreter().writeWorldScriptData(writer); #endif } @@ -57,22 +89,22 @@ DENG2_PIMPL(GameStateWriter) SV_CloseFile(); // Open the map state file. - SV_OpenFile(saveSlots->composeSavePathForSlot(BASE_SLOT, gameMap + 1), "wp"); + SV_OpenFile(SV_SavePath() / saveInfo->fileNameForMap(gameMap), "wp"); #endif - MapStateWriter(thingArchiveExcludePlayers).write(writer); + MapStateWriter(*thingArchive).write(writer); - SV_WriteConsistencyBytes(); // To be absolutely sure... + writeConsistencyBytes(); // To be absolutely sure... SV_CloseFile(); - -#if !__JHEXEN___ - SV_ClearThingArchive(); -#endif } void writePlayers() { - SV_BeginSegment(ASEG_PLAYERS); + beginSegment(ASEG_PLAYER_HEADER); + playerheader_t plrHdr; + plrHdr.write(writer); + + beginSegment(ASEG_PLAYERS); { #if __JHEXEN__ for(int i = 0; i < MAXPLAYERS; ++i) @@ -88,26 +120,30 @@ DENG2_PIMPL(GameStateWriter) continue; Writer_WriteInt32(writer, Net_GetPlayerID(i)); - plr->write(writer); + plr->write(writer, plrHdr); } } - SV_EndSegment(); + endSegment(); } }; GameStateWriter::GameStateWriter() : d(new Instance(this)) {} -void GameStateWriter::write(SaveInfo *saveInfo, Str const *path) +void GameStateWriter::write(SaveInfo &info) { - DENG_ASSERT(saveInfo != 0 && path != 0); - d->saveInfo = saveInfo; + de::Path const path = SV_SavePath() / info.fileName(); - playerHeaderOK = false; // Uninitialized. + d->saveInfo = &info; - if(!SV_OpenGameSaveFile(path, true)) + // In networked games the server tells the clients to save their games. +#if !__JHEXEN__ + NetSv_SaveGame(d->saveInfo->sessionId()); +#endif + + if(!SV_OpenGameSaveFile(path, true/*for writing*/)) { - throw FileAccessError("GameStateWriter", "Failed opening \"" + de::String(Str_Text(path)) + "\""); + throw FileAccessError("GameStateWriter", "Failed opening \"" + de::NativePath(path).pretty() + "\""); } d->writer = SV_NewWriter(); @@ -116,15 +152,15 @@ void GameStateWriter::write(SaveInfo *saveInfo, Str const *path) d->writeWorldACScriptData(); // Set the mobj archive numbers. - SV_InitThingArchiveForSave(false/*do not exclude players*/); + d->thingArchive = new ThingArchive; + d->thingArchive->initForSave(false/*do not exclude players*/); #if !__JHEXEN__ - Writer_WriteInt32(d->writer, thingArchiveSize); + Writer_WriteInt32(d->writer, d->thingArchive->size()); #endif - SV_WritePlayerHeader(d->writer); - d->writePlayers(); d->writeMap(); + delete d->thingArchive; d->thingArchive = 0; Writer_Delete(d->writer); d->writer = 0; } diff --git a/doomsday/plugins/common/src/hu_automap.c b/doomsday/plugins/common/src/hu_automap.c index afd820c0b2..b19546b050 100644 --- a/doomsday/plugins/common/src/hu_automap.c +++ b/doomsday/plugins/common/src/hu_automap.c @@ -138,8 +138,8 @@ void UIAutomap_Register(void) static void rotate2D(coord_t* x, coord_t* y, float angle) { - coord_t tmpx = (coord_t) ((*x * cos(angle/180 * PI)) - (*y * sin(angle/180 * PI))); - *y = (coord_t) ((*x * sin(angle/180 * PI)) + (*y * cos(angle/180 * PI))); + coord_t tmpx = (coord_t) ((*x * cos(angle/180 * DD_PI)) - (*y * sin(angle/180 * DD_PI))); + *y = (coord_t) ((*x * sin(angle/180 * DD_PI)) + (*y * cos(angle/180 * DD_PI))); *x = tmpx; } @@ -1160,14 +1160,17 @@ static void drawMarkedPoints(uiwidget_t* obj, float scale) /** * Sets up the state for automap drawing. */ -static void setupGLStateForMap(uiwidget_t* obj) +static void setupGLStateForMap(uiwidget_t *obj) { - guidata_automap_t* am = (guidata_automap_t*)obj->typedata; - const float alpha = uiRendState->pageAlpha; + //guidata_automap_t *am = (guidata_automap_t *)obj->typedata; + float const alpha = uiRendState->pageAlpha; +#if __JDOOM64__ int player = UIWidget_Player(obj); +#endif float angle, bgColor[3]; coord_t plx, ply; RectRaw geometry; + assert(obj->type == GUI_AUTOMAP); UIAutomap_ParallaxLayerOrigin(obj, &plx, &ply); @@ -1679,7 +1682,7 @@ void UIAutomap_Ticker(uiwidget_t* obj, timespan_t ticLength) xy[VX] = panX[0] * panUnitsPerSecond * ticLength + panX[1]; xy[VY] = panY[0] * panUnitsPerSecond * ticLength + panY[1]; - V2d_Rotate(xy, am->angle / 360 * 2 * PI); + V2d_Rotate(xy, am->angle / 360 * 2 * DD_PI); if(xy[VX] || xy[VY]) { @@ -1783,7 +1786,7 @@ void UIAutomap_Ticker(uiwidget_t* obj, timespan_t ticLength) am->bottomRight[1] = am->bottomLeft[1] = -viewHeight/2; // Apply rotation. - rads = (float)(am->angle / 360 * 2 * PI); + rads = (float)(am->angle / 360 * 2 * DD_PI); V2d_Rotate(am->topLeft, rads); V2d_Rotate(am->bottomRight, rads); V2d_Rotate(am->bottomLeft, rads); diff --git a/doomsday/plugins/common/src/hu_lib.c b/doomsday/plugins/common/src/hu_lib.c index 19cc6bd14e..3173e4f4f1 100644 --- a/doomsday/plugins/common/src/hu_lib.c +++ b/doomsday/plugins/common/src/hu_lib.c @@ -953,19 +953,21 @@ static void applyPageLayout(mn_page_t *page) } } -static void composeSubpageString(mn_page_t* page, char* buf, size_t bufSize) +static void composeSubpageString(mn_page_t *page, char *buf, size_t bufSize) { - assert(page); + DENG_ASSERT(page != 0); if(!buf || 0 == bufSize) return; dd_snprintf(buf, bufSize, "Page %i/%i", 0, 0); } -static void drawPageNavigation(mn_page_t* page, int x, int y) +static void drawPageNavigation(mn_page_t *page, int x, int y) { - const int currentPage = 0;//(page->firstObject + page->numVisObjects/2) / page->numVisObjects + 1; - const int totalPages = 1;//(int)ceil((float)page->objectsCount/page->numVisObjects); + int const currentPage = 0;//(page->firstObject + page->numVisObjects/2) / page->numVisObjects + 1; + int const totalPages = 1;//(int)ceil((float)page->objectsCount/page->numVisObjects); #if __JDOOM__ || __JDOOM64__ char buf[1024]; + + DENG2_UNUSED(currentPage); #endif if(!page || totalPages <= 1) return; @@ -992,7 +994,7 @@ static void drawPageNavigation(mn_page_t* page, int x, int y) #endif } -static void drawPageHeading(mn_page_t* page, const Point2Raw* offset) +static void drawPageHeading(mn_page_t *page, Point2Raw const *offset) { Point2Raw origin; @@ -1677,25 +1679,26 @@ mn_object_t* MNRect_New(void) return ob; } -void MNRect_Delete(mn_object_t* ob) +void MNRect_Delete(mn_object_t *ob) { - assert(ob && ob->_type == MN_RECT); + DENG_ASSERT(ob != 0 && ob->_type == MN_RECT); Z_Free(ob->_typedata); Z_Free(ob); } -void MNRect_Ticker(mn_object_t* ob) +void MNRect_Ticker(mn_object_t *ob) { - mndata_rect_t* rect = (mndata_rect_t*) ob->_typedata; - assert(ob && ob->_type == MN_RECT); - + //mndata_rect_t *rect = (mndata_rect_t *) ob->_typedata; + DENG_ASSERT(ob != 0 && ob->_type == MN_RECT); + DENG_UNUSED(ob); // Stub. } -void MNRect_Drawer(mn_object_t* ob, const Point2Raw* origin) +void MNRect_Drawer(mn_object_t *ob, Point2Raw const *origin) { - mndata_rect_t* rect = (mndata_rect_t*)ob->_typedata; - assert(ob->_type == MN_RECT); + mndata_rect_t *rect = (mndata_rect_t *)ob->_typedata; + + DENG_ASSERT(ob->_type == MN_RECT); if(origin) { @@ -1748,50 +1751,49 @@ void MNRect_SetBackgroundPatch(mn_object_t* ob, patchid_t patch) rect->patch = patch; } -mn_object_t* MNText_New(void) +mn_object_t *MNText_New(void) { - mn_object_t* ob = Z_Calloc(sizeof(*ob), PU_GAMESTATIC, 0); - if(!ob) Con_Error("MNText::New: Failed on allocation of %lu bytes for new MNText.", (unsigned long) sizeof(*ob)); - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - if(!ob->_typedata) Con_Error("MNText::New: Failed on allocation of %lu bytes for mndata_text_t.", (unsigned long) sizeof(mndata_text_t)); + mn_object_t *ob = Z_Calloc(sizeof(*ob), PU_GAMESTATIC, 0); - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->drawer = MNText_Drawer; ob->updateGeometry = MNText_UpdateGeometry; return ob; } -void MNText_Delete(mn_object_t* ob) +void MNText_Delete(mn_object_t *ob) { - assert(ob && ob->_type == MN_TEXT); + DENG_ASSERT(ob != 0 && ob->_type == MN_TEXT); Z_Free(ob->_typedata); Z_Free(ob); } -void MNText_Ticker(mn_object_t* ob) +void MNText_Ticker(mn_object_t *ob) { - mndata_text_t* txt = (mndata_text_t*) ob->_typedata; - assert(ob && ob->_type == MN_TEXT); - + //mndata_text_t *txt = (mndata_text_t *) ob->_typedata; + DENG_ASSERT(ob != 0 && ob->_type == MN_TEXT); + DENG_UNUSED(ob); // Stub. } -void MNText_Drawer(mn_object_t* ob, const Point2Raw* origin) +void MNText_Drawer(mn_object_t *ob, Point2Raw const *origin) { - mndata_text_t* txt = (mndata_text_t*)ob->_typedata; + mndata_text_t *txt = (mndata_text_t *)ob->_typedata; fontid_t fontId = rs.textFonts[ob->_pageFontIdx]; float color[4], t = (ob->_flags & MNF_FOCUS)? 1 : 0; - assert(ob->_type == MN_TEXT); + + DENG_ASSERT(ob->_type == MN_TEXT); // Flash if focused? if((ob->_flags & MNF_FOCUS) && cfg.menuTextFlashSpeed > 0) { - const float speed = cfg.menuTextFlashSpeed / 2.f; - t = (1 + sin(MNPage_Timer(ob->_page) / (float)TICSPERSEC * speed * PI)) / 2; + float const speed = cfg.menuTextFlashSpeed / 2.f; + t = (1 + sin(MNPage_Timer(ob->_page) / (float)TICSPERSEC * speed * DD_PI)) / 2; } lerpColor(color, rs.textColors[ob->_pageColorIdx], cfg.menuTextFlashColor, t, false/*rgb mode*/); @@ -1868,7 +1870,7 @@ mn_object_t* MNEdit_New(void) ob->updateGeometry = MNEdit_UpdateGeometry; ob->cmdResponder = MNEdit_CommandResponder; ob->responder = MNEdit_Responder; - { mndata_edit_t* edit = (mndata_edit_t*) ob->_typedata; + { mndata_edit_t *edit = (mndata_edit_t *) ob->_typedata; Str_Init(&edit->text); Str_Init(&edit->oldtext); } @@ -1876,29 +1878,31 @@ mn_object_t* MNEdit_New(void) return ob; } -void MNEdit_Delete(mn_object_t* ob) +void MNEdit_Delete(mn_object_t *ob) { - mndata_edit_t* edit = (mndata_edit_t*) ob->_typedata; - assert(ob && ob->_type == MN_EDIT); + mndata_edit_t *edit = (mndata_edit_t *) ob->_typedata; + DENG_ASSERT(ob != 0 && ob->_type == MN_EDIT); Str_Free(&edit->text); Str_Free(&edit->oldtext); Z_Free(ob->_typedata); Z_Free(ob); } -void MNEdit_Ticker(mn_object_t* ob) +void MNEdit_Ticker(mn_object_t *ob) { - mndata_edit_t* edit = (mndata_edit_t*) ob->_typedata; - assert(ob && ob->_type == MN_EDIT); - + //mndata_edit_t *edit = (mndata_edit_t *) ob->_typedata; + DENG_ASSERT(ob != 0 && ob->_type == MN_EDIT); + DENG_UNUSED(ob); // Stub. } -static void drawEditBackground(const mn_object_t* obj, int x, int y, int width, float alpha) +static void drawEditBackground(mn_object_t const *ob, int x, int y, int width, float alpha) { patchinfo_t leftInfo, rightInfo, middleInfo; int leftOffset = 0, rightOffset = 0; + DENG_UNUSED(ob); + DGL_Color4f(1, 1, 1, alpha); if(R_GetPatchInfo(pEditLeft, &leftInfo)) @@ -1965,7 +1969,7 @@ void MNEdit_Drawer(mn_object_t* ob, const Point2Raw* _origin) if(!(ob->_flags & MNF_ACTIVE) && (ob->_flags & MNF_FOCUS) && cfg.menuTextFlashSpeed > 0) { const float speed = cfg.menuTextFlashSpeed / 2.f; - t = (1 + sin(MNPage_Timer(ob->_page) / (float)TICSPERSEC * speed * PI)) / 2; + t = (1 + sin(MNPage_Timer(ob->_page) / (float)TICSPERSEC * speed * DD_PI)) / 2; } lerpColor(color, cfg.menuTextColors[MNDATA_EDIT_TEXT_COLORIDX], cfg.menuTextFlashColor, t, false/*rgb mode*/); @@ -2172,34 +2176,35 @@ mn_object_t* MNList_New(void) return ob; } -void MNList_Delete(mn_object_t* ob) +void MNList_Delete(mn_object_t *ob) { - assert(ob && ob->_type == MN_LIST); + DENG_ASSERT(ob != 0 && ob->_type == MN_LIST); Z_Free(ob->_typedata); Z_Free(ob); } -void MNList_Ticker(mn_object_t* ob) +void MNList_Ticker(mn_object_t *ob) { - mndata_list_t* list = (mndata_list_t*) ob->_typedata; - assert(ob && ob->_type == MN_LIST); - + // mndata_list_t *list = (mndata_list_t *) ob->_typedata; + DENG_ASSERT(ob != 0 && ob->_type == MN_LIST); + DENG_UNUSED(ob); // Stub. } -void MNList_Drawer(mn_object_t* ob, const Point2Raw* _origin) +void MNList_Drawer(mn_object_t *ob, Point2Raw const *_origin) { - const mndata_list_t* list = (mndata_list_t*)ob->_typedata; - const dd_bool flashSelection = ((ob->_flags & MNF_ACTIVE) && MNList_SelectionIsVisible(ob)); - const float* color = rs.textColors[ob->_pageColorIdx]; + mndata_list_t const *list = (mndata_list_t *)ob->_typedata; + dd_bool const flashSelection = ((ob->_flags & MNF_ACTIVE) && MNList_SelectionIsVisible(ob)); + float const *color = rs.textColors[ob->_pageColorIdx]; float dimColor[4], flashColor[4], t = flashSelection? 1 : 0; Point2Raw origin; - assert(ob->_type == MN_LIST); + + DENG_ASSERT(ob->_type == MN_LIST); if(flashSelection && cfg.menuTextFlashSpeed > 0) { - const float speed = cfg.menuTextFlashSpeed / 2.f; - t = (1 + sin(MNPage_Timer(ob->_page) / (float)TICSPERSEC * speed * PI)) / 2; + float const speed = cfg.menuTextFlashSpeed / 2.f; + t = (1 + sin(MNPage_Timer(ob->_page) / (float)TICSPERSEC * speed * DD_PI)) / 2; } lerpColor(flashColor, rs.textColors[ob->_pageColorIdx], cfg.menuTextFlashColor, t, false/*rgb mode*/); @@ -2388,57 +2393,57 @@ dd_bool MNList_SelectItemByValue(mn_object_t* obj, int flags, int dataValue) return MNList_SelectItem(obj, flags, MNList_FindItem(obj, dataValue)); } -mn_object_t* MNListInline_New(void) +mn_object_t *MNListInline_New(void) { - mn_object_t* ob = Z_Calloc(sizeof(*ob), PU_GAMESTATIC, 0); - if(!ob) Con_Error("MNListInline::New: Failed on allocation of %lu bytes for new MNListInline.", (unsigned long) sizeof(*ob)); - ob->_typedata = Z_Calloc(sizeof(mndata_list_t), PU_GAMESTATIC, 0); - if(!ob->_typedata) Con_Error("MNListInline::New: Failed on allocation of %lu bytes for mndata_list_t.", (unsigned long) sizeof(mndata_list_t)); + mn_object_t *ob = Z_Calloc(sizeof(*ob), PU_GAMESTATIC, 0); - ob->_type = MN_LISTINLINE; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNListInline_Ticker; - ob->drawer = MNListInline_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_list_t), PU_GAMESTATIC, 0); + ob->_type = MN_LISTINLINE; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNListInline_Ticker; + ob->drawer = MNListInline_Drawer; ob->updateGeometry = MNListInline_UpdateGeometry; - ob->cmdResponder = MNListInline_CommandResponder; + ob->cmdResponder = MNListInline_CommandResponder; return ob; } -void MNListInline_Delete(mn_object_t* ob) +void MNListInline_Delete(mn_object_t *ob) { - assert(ob && ob->_type == MN_LISTINLINE); + DENG_ASSERT(ob != 0 && ob->_type == MN_LISTINLINE); Z_Free(ob->_typedata); Z_Free(ob); } -void MNListInline_Ticker(mn_object_t* ob) +void MNListInline_Ticker(mn_object_t *ob) { - mndata_list_t* rect = (mndata_list_t*) ob->_typedata; - assert(ob && ob->_type == MN_LISTINLINE); - + // mndata_list_t *rect = (mndata_list_t *) ob->_typedata; + DENG_ASSERT(ob != 0 && ob->_type == MN_LISTINLINE); + DENG_UNUSED(ob); // Stub. } -void MNListInline_Drawer(mn_object_t* obj, const Point2Raw* origin) +void MNListInline_Drawer(mn_object_t *ob, Point2Raw const *origin) { - const mndata_list_t* list = (mndata_list_t*)obj->_typedata; - const mndata_listitem_t* item = ((const mndata_listitem_t*)list->items) + list->selection; - assert(obj->_type == MN_LISTINLINE); + mndata_list_t const *list = (mndata_list_t *)ob->_typedata; + mndata_listitem_t const *item = ((mndata_listitem_t const *)list->items) + list->selection; + + DENG_ASSERT(ob->_type == MN_LISTINLINE); DGL_Enable(DGL_TEXTURE_2D); - FR_SetFont(rs.textFonts[obj->_pageFontIdx]); - FR_SetColorAndAlphav(rs.textColors[obj->_pageColorIdx]); + FR_SetFont(rs.textFonts[ob->_pageFontIdx]); + FR_SetColorAndAlphav(rs.textColors[ob->_pageColorIdx]); FR_DrawText3(item->text, origin, ALIGN_TOPLEFT, MN_MergeMenuEffectWithDrawTextFlags(0)); DGL_Disable(DGL_TEXTURE_2D); } -int MNListInline_CommandResponder(mn_object_t* obj, menucommand_e cmd) +int MNListInline_CommandResponder(mn_object_t *ob, menucommand_e cmd) { - mndata_list_t* list = (mndata_list_t*)obj->_typedata; - assert(obj->_type == MN_LISTINLINE); + mndata_list_t *list = (mndata_list_t *)ob->_typedata; + + DENG_ASSERT(ob->_type == MN_LISTINLINE); switch(cmd) { @@ -2468,9 +2473,9 @@ int MNListInline_CommandResponder(mn_object_t* obj, menucommand_e cmd) if(oldSelection != list->selection) { S_LocalSound(SFX_MENU_SLIDER_MOVE, NULL); - if(MNObject_HasAction(obj, MNA_MODIFIED)) + if(MNObject_HasAction(ob, MNA_MODIFIED)) { - MNObject_ExecAction(obj, MNA_MODIFIED, NULL); + MNObject_ExecAction(ob, MNA_MODIFIED, NULL); } } return true; @@ -2480,16 +2485,17 @@ int MNListInline_CommandResponder(mn_object_t* obj, menucommand_e cmd) } } -void MNList_UpdateGeometry(mn_object_t* obj, mn_page_t* page) +void MNList_UpdateGeometry(mn_object_t *ob, mn_page_t *page) { - mndata_list_t* list = (mndata_list_t*)obj->_typedata; + mndata_list_t *list = (mndata_list_t *)ob->_typedata; RectRaw itemGeometry = { 0, 0 }; int i; - assert(obj->_type == MN_LIST); - Rect_SetWidthHeight(obj->_geometry, 0, 0); + DENG_ASSERT(ob->_type == MN_LIST); - FR_SetFont(MNPage_PredefinedFont(page, obj->_pageFontIdx)); + Rect_SetWidthHeight(ob->_geometry, 0, 0); + + FR_SetFont(MNPage_PredefinedFont(page, ob->_pageFontIdx)); for(i = 0; i < list->count; ++i) { mndata_listitem_t* item = &((mndata_listitem_t*)list->items)[i]; @@ -2498,73 +2504,73 @@ void MNList_UpdateGeometry(mn_object_t* obj, mn_page_t* page) if(i != list->count-1) itemGeometry.size.height *= 1 + MNDATA_LIST_LEADING; - Rect_UniteRaw(obj->_geometry, &itemGeometry); + Rect_UniteRaw(ob->_geometry, &itemGeometry); itemGeometry.origin.y += itemGeometry.size.height; } } -void MNListInline_UpdateGeometry(mn_object_t* obj, mn_page_t* page) +void MNListInline_UpdateGeometry(mn_object_t *obj, mn_page_t *page) { - mndata_list_t* list = (mndata_list_t*)obj->_typedata; - mndata_listitem_t* item = ((mndata_listitem_t*) list->items) + list->selection; + mndata_list_t *list = (mndata_list_t *)obj->_typedata; + mndata_listitem_t *item = ((mndata_listitem_t *) list->items) + list->selection; Size2Raw size; - assert(obj->_type == MN_LISTINLINE); + + DENG_ASSERT(obj->_type == MN_LISTINLINE); FR_SetFont(MNPage_PredefinedFont(page, obj->_pageFontIdx)); FR_TextSize(&size, item->text); Rect_SetWidthHeight(obj->_geometry, size.width, size.height); } -mn_object_t* MNButton_New(void) +mn_object_t *MNButton_New(void) { - mn_object_t* ob = Z_Calloc(sizeof(*ob), PU_GAMESTATIC, 0); - if(!ob) Con_Error("MNButton::New: Failed on allocation of %lu bytes for new MNButton.", (unsigned long) sizeof(*ob)); - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - if(!ob->_typedata) Con_Error("MNButton:New: Failed on allocation of %lu bytes for mndata_button_t.", (unsigned long) sizeof(mndata_button_t)); + mn_object_t *ob = Z_Calloc(sizeof(*ob), PU_GAMESTATIC, 0); - ob->_type = MN_BUTTON; - ob->_pageFontIdx = MENU_FONT2; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->drawer = MNButton_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + ob->_type = MN_BUTTON; + ob->_pageFontIdx = MENU_FONT2; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->drawer = MNButton_Drawer; ob->updateGeometry = MNButton_UpdateGeometry; - ob->cmdResponder = MNButton_CommandResponder; + ob->cmdResponder = MNButton_CommandResponder; return ob; } -void MNButton_Delete(mn_object_t* ob) +void MNButton_Delete(mn_object_t *ob) { - assert(ob && ob->_type == MN_BUTTON); + DENG_ASSERT(ob != 0 && ob->_type == MN_BUTTON); Z_Free(ob->_typedata); Z_Free(ob); } -void MNButton_Ticker(mn_object_t* ob) +void MNButton_Ticker(mn_object_t *ob) { - mndata_button_t* btn = (mndata_button_t*) ob->_typedata; - assert(ob && ob->_type == MN_BUTTON); - + //mndata_button_t *btn = (mndata_button_t *) ob->_typedata; + DENG_ASSERT(ob != 0 && ob->_type == MN_BUTTON); + DENG_UNUSED(ob); // Stub. } -void MNButton_Drawer(mn_object_t* ob, const Point2Raw* origin) +void MNButton_Drawer(mn_object_t *ob, Point2Raw const *origin) { - mndata_button_t* btn = (mndata_button_t*)ob->_typedata; + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; //int dis = (obj->_flags & MNF_DISABLED) != 0; //int act = (obj->_flags & MNF_ACTIVE) != 0; //int click = (obj->_flags & MNF_CLICKED) != 0; //dd_bool down = act || click; - const fontid_t fontId = rs.textFonts[ob->_pageFontIdx]; + fontid_t const fontId = rs.textFonts[ob->_pageFontIdx]; float color[4], t = (ob->_flags & MNF_FOCUS)? 1 : 0; - assert(ob->_type == MN_BUTTON); + + DENG_ASSERT(ob->_type == MN_BUTTON); // Flash if focused? if((ob->_flags & MNF_FOCUS) && cfg.menuTextFlashSpeed > 0) { const float speed = cfg.menuTextFlashSpeed / 2.f; - t = (1 + sin(MNPage_Timer(ob->_page) / (float)TICSPERSEC * speed * PI)) / 2; + t = (1 + sin(MNPage_Timer(ob->_page) / (float)TICSPERSEC * speed * DD_PI)) / 2; } lerpColor(color, rs.textColors[ob->_pageColorIdx], cfg.menuTextFlashColor, t, false/*rgb mode*/); @@ -2727,28 +2733,29 @@ mn_object_t* MNColorBox_New(void) return ob; } -void MNColorBox_Delete(mn_object_t* ob) +void MNColorBox_Delete(mn_object_t *ob) { - assert(ob && ob->_type == MN_COLORBOX); + DENG_ASSERT(ob != 0 && ob->_type == MN_COLORBOX); Z_Free(ob->_typedata); Z_Free(ob); } -void MNColorBox_Ticker(mn_object_t* ob) +void MNColorBox_Ticker(mn_object_t *ob) { - mndata_colorbox_t* cbox = (mndata_colorbox_t*) ob->_typedata; - assert(ob && ob->_type == MN_COLORBOX); - + //mndata_colorbox_t *cbox = (mndata_colorbox_t*) ob->_typedata; + DENG_ASSERT(ob != 0 && ob->_type == MN_COLORBOX); + DENG_UNUSED(ob); // Stub. } -void MNColorBox_Drawer(mn_object_t* obj, const Point2Raw* offset) +void MNColorBox_Drawer(mn_object_t *ob, Point2Raw const *offset) { - const mndata_colorbox_t* cbox = (mndata_colorbox_t*)obj->_typedata; + mndata_colorbox_t const *cbox = (mndata_colorbox_t *)ob->_typedata; patchinfo_t t, b, l, r, tl, tr, br, bl; - const int up = 1; + int const up = 1; int x, y, w, h; - assert(obj->_type == MN_COLORBOX && offset); + + DENG_ASSERT(ob->_type == MN_COLORBOX && offset); R_GetPatchInfo(borderPatches[0], &t); R_GetPatchInfo(borderPatches[2], &b); @@ -3158,43 +3165,45 @@ void MNSlider_SetValue(mn_object_t* obj, int flags, float value) sldr->value = (int) (value + (value > 0? + .5f : -.5f)); } -int MNSlider_ThumbPos(const mn_object_t* obj) +int MNSlider_ThumbPos(mn_object_t const *ob) { #define WIDTH (middleInfo.geometry.size.width) - mndata_slider_t* data = (mndata_slider_t*)obj->_typedata; + mndata_slider_t *data = (mndata_slider_t*)ob->_typedata; float range = data->max - data->min, useVal; patchinfo_t middleInfo; - assert(obj->_type == MN_SLIDER); + + DENG_ASSERT(ob->_type == MN_SLIDER); if(!R_GetPatchInfo(pSliderMiddle, &middleInfo)) return 0; if(!range) range = 1; // Should never happen. - useVal = MNSlider_Value(obj) - data->min; + useVal = MNSlider_Value(ob) - data->min; //return obj->x + UI_BAR_BORDER + butw + useVal / range * (obj->w - UI_BAR_BORDER * 2 - butw * 3); return useVal / range * MNDATA_SLIDER_SLOTS * WIDTH; #undef WIDTH } -void MNSlider_Ticker(mn_object_t* ob) +void MNSlider_Ticker(mn_object_t *ob) { - mndata_slider_t* sld = (mndata_slider_t*) ob->_typedata; - assert(ob && ob->_type == MN_SLIDER); - + //mndata_slider_t *sld = (mndata_slider_t *) ob->_typedata; + DENG_ASSERT(ob != 0 && ob->_type == MN_SLIDER); + DENG_UNUSED(ob); // Stub. } -void MNSlider_Drawer(mn_object_t* obj, const Point2Raw* origin) +void MNSlider_Drawer(mn_object_t *obj, Point2Raw const *origin) { #define WIDTH (middleInfo.geometry.size.width) #define HEIGHT (middleInfo.geometry.size.height) - //const mndata_slider_t* sldr = (mndata_slider_t*)obj->_typedata; + //mndata_slider_t const *sldr = (mndata_slider_t *)obj->_typedata; float x, y;// float range = sldr->max - sldr->min; patchinfo_t middleInfo, leftInfo; - assert(obj->_type == MN_SLIDER && origin); + + DENG_ASSERT(obj->_type == MN_SLIDER && origin); if(!R_GetPatchInfo(pSliderMiddle, &middleInfo)) return; if(!R_GetPatchInfo(pSliderLeft, &leftInfo)) return; @@ -3462,26 +3471,27 @@ mn_object_t* MNMobjPreview_New(void) return ob; } -void MNMobjPreview_Delete(mn_object_t* ob) +void MNMobjPreview_Delete(mn_object_t *ob) { - assert(ob && ob->_type == MN_MOBJPREVIEW); + DENG_ASSERT(ob != 0 && ob->_type == MN_MOBJPREVIEW); Z_Free(ob->_typedata); Z_Free(ob); } -void MNMobjPreview_Ticker(mn_object_t* ob) +void MNMobjPreview_Ticker(mn_object_t *ob) { - mndata_mobjpreview_t* mop = (mndata_mobjpreview_t*) ob->_typedata; - assert(ob && ob->_type == MN_MOBJPREVIEW); - + //mndata_mobjpreview_t *mop = (mndata_mobjpreview_t *) ob->_typedata; + DENG_ASSERT(ob != 0 && ob->_type == MN_MOBJPREVIEW); + DENG_UNUSED(ob); // Stub. } -static void findSpriteForMobjType(int mobjType, spritetype_e* sprite, int* frame) +static void findSpriteForMobjType(int mobjType, spritetype_e *sprite, int *frame) { - mobjinfo_t* info; + mobjinfo_t *info; int stateNum; - assert(mobjType >= MT_FIRST && mobjType < NUMMOBJTYPES && sprite && frame); + + DENG_ASSERT(mobjType >= MT_FIRST && mobjType < NUMMOBJTYPES && sprite && frame); info = &MOBJINFO[mobjType]; stateNum = info->states[SN_SPAWN]; @@ -3489,42 +3499,42 @@ static void findSpriteForMobjType(int mobjType, spritetype_e* sprite, int* frame *frame = ((menuTime >> 3) & 3); } -void MNMobjPreview_SetMobjType(mn_object_t* obj, int mobjType) +void MNMobjPreview_SetMobjType(mn_object_t *ob, int mobjType) { - mndata_mobjpreview_t* mop = (mndata_mobjpreview_t*)obj->_typedata; - assert(obj->_type == MN_MOBJPREVIEW); + mndata_mobjpreview_t *mop = (mndata_mobjpreview_t *)ob->_typedata; + DENG_ASSERT(ob->_type == MN_MOBJPREVIEW); mop->mobjType = mobjType; } -void MNMobjPreview_SetPlayerClass(mn_object_t* obj, int plrClass) +void MNMobjPreview_SetPlayerClass(mn_object_t *ob, int plrClass) { - mndata_mobjpreview_t* mop = (mndata_mobjpreview_t*)obj->_typedata; - assert(obj->_type == MN_MOBJPREVIEW); + mndata_mobjpreview_t *mop = (mndata_mobjpreview_t *)ob->_typedata; + DENG_ASSERT(ob->_type == MN_MOBJPREVIEW); mop->plrClass = plrClass; } -void MNMobjPreview_SetTranslationClass(mn_object_t* obj, int tClass) +void MNMobjPreview_SetTranslationClass(mn_object_t *ob, int tClass) { - mndata_mobjpreview_t* mop = (mndata_mobjpreview_t*)obj->_typedata; - assert(obj->_type == MN_MOBJPREVIEW); + mndata_mobjpreview_t *mop = (mndata_mobjpreview_t *)ob->_typedata; + DENG_ASSERT(ob->_type == MN_MOBJPREVIEW); mop->tClass = tClass; } -void MNMobjPreview_SetTranslationMap(mn_object_t* obj, int tMap) +void MNMobjPreview_SetTranslationMap(mn_object_t *ob, int tMap) { - mndata_mobjpreview_t* mop = (mndata_mobjpreview_t*)obj->_typedata; - assert(obj->_type == MN_MOBJPREVIEW); + mndata_mobjpreview_t *mop = (mndata_mobjpreview_t *)ob->_typedata; + DENG_ASSERT(ob->_type == MN_MOBJPREVIEW); mop->tMap = tMap; } /// @todo We can do better - the engine should be able to render this visual for us. -void MNMobjPreview_Drawer(mn_object_t* ob, const Point2Raw* offset) +void MNMobjPreview_Drawer(mn_object_t *ob, Point2Raw const *offset) { - mndata_mobjpreview_t* mop = (mndata_mobjpreview_t*)ob->_typedata; + mndata_mobjpreview_t *mop = (mndata_mobjpreview_t *)ob->_typedata; int tClass, tMap, spriteFrame; spritetype_e sprite; spriteinfo_t info; @@ -3532,7 +3542,7 @@ void MNMobjPreview_Drawer(mn_object_t* ob, const Point2Raw* offset) Point2Raw origin; Size2Raw size; - assert(ob->_type == MN_MOBJPREVIEW); + DENG_ASSERT(ob->_type == MN_MOBJPREVIEW); if(MT_NONE == mop->mobjType) return; diff --git a/doomsday/plugins/common/src/hu_menu.c b/doomsday/plugins/common/src/hu_menu.c deleted file mode 100644 index dafe5b1f34..0000000000 --- a/doomsday/plugins/common/src/hu_menu.c +++ /dev/null @@ -1,6590 +0,0 @@ -/**\file hu_menu.c - *\section License - * License: GPL - * Online License Link: http://www.gnu.org/licenses/gpl.html - * - *\author Copyright © 2003-2013 Jaakko Keränen - *\author Copyright © 2005-2013 Daniel Swanson - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include - -#include "common.h" - -#include "m_argv.h" -#include "hu_chat.h" -#include "hu_log.h" -#include "hu_msg.h" -#include "hu_stuff.h" -#include "am_map.h" -#include "x_hair.h" -#include "player.h" -#include "g_controls.h" -#include "p_saveg.h" -#include "g_common.h" -#include "r_common.h" -#include "m_ctrl.h" - -#include "hu_menu.h" - -/// Original game line height for pages that employ the fixed layout (in 320x200 pixels). -#if __JDOOM__ -# define FIXED_LINE_HEIGHT (15+1) -#else -# define FIXED_LINE_HEIGHT (19+1) -#endif - -typedef struct cvarbutton_s { - char active; - const char* cvarname; - const char* yes; - const char* no; - int mask; -} cvarbutton_t; - -int Hu_MenuActionSetActivePage(mn_object_t* ob, mn_actionid_t action, void* parameters); -int Hu_MenuActionInitNewGame(mn_object_t* ob, mn_actionid_t action, void* parameters); - -int Hu_MenuSelectLoadGame(mn_object_t* ob, mn_actionid_t action, void* parameters); -int Hu_MenuSelectSaveGame(mn_object_t* ob, mn_actionid_t action, void* parameters); -#if __JHEXEN__ -int Hu_MenuSelectFiles(mn_object_t* ob, mn_actionid_t action, void* parameters); -#endif -int Hu_MenuSelectPlayerSetup(mn_object_t* ob, mn_actionid_t action, void* parameters); -int Hu_MenuSelectJoinGame(mn_object_t* ob, mn_actionid_t action, void* parameters); - -#if __JDOOM__ || __JHERETIC__ || __JHEXEN__ -int Hu_MenuSelectHelp(mn_object_t* ob, mn_actionid_t action, void* parameters); -#endif -int Hu_MenuSelectControlPanelLink(mn_object_t* ob, mn_actionid_t action, void* parameters); - -int Hu_MenuSelectSingleplayer(mn_object_t* ob, mn_actionid_t action, void* parameters); -int Hu_MenuSelectMultiplayer(mn_object_t* ob, mn_actionid_t action, void* parameters); -#if __JDOOM__ || __JHERETIC__ -int Hu_MenuFocusEpisode(mn_object_t* ob, mn_actionid_t action, void* parameters); -int Hu_MenuActivateNotSharewareEpisode(mn_object_t* ob, mn_actionid_t action, void* parameters); -#endif -#if __JHEXEN__ -int Hu_MenuFocusOnPlayerClass(mn_object_t* ob, mn_actionid_t action, void* parameters); -int Hu_MenuSelectPlayerClass(mn_object_t* ob, mn_actionid_t action, void* parameters); -#endif -int Hu_MenuFocusSkillMode(mn_object_t* ob, mn_actionid_t action, void* parameters); -int Hu_MenuSelectLoadSlot(mn_object_t* ob, mn_actionid_t action, void* parameters); -int Hu_MenuSelectQuitGame(mn_object_t* ob, mn_actionid_t action, void* parameters); -int Hu_MenuSelectEndGame(mn_object_t* ob, mn_actionid_t action, void* parameters); -int Hu_MenuSelectAcceptPlayerSetup(mn_object_t* ob, mn_actionid_t action, void* parameters); - -int Hu_MenuSelectSaveSlot(mn_object_t* ob, mn_actionid_t action, void* parameters); - -int Hu_MenuChangeWeaponPriority(mn_object_t* ob, mn_actionid_t action, void* parameters); -#if __JHEXEN__ -int Hu_MenuSelectPlayerSetupPlayerClass(mn_object_t* ob, mn_actionid_t action, void* parameters); -#endif -int Hu_MenuSelectPlayerColor(mn_object_t* ob, mn_actionid_t action, void* parameters); - -#if __JHEXEN__ -void Hu_MenuPlayerClassBackgroundTicker(mn_object_t* ob); -void Hu_MenuPlayerClassPreviewTicker(mn_object_t* ob); -#endif - -#if __JHERETIC__ || __JHEXEN__ -void Hu_MenuDrawMainPage(mn_page_t* page, const Point2Raw* origin); -#endif - -void Hu_MenuDrawGameTypePage(mn_page_t* page, const Point2Raw* origin); -void Hu_MenuDrawSkillPage(mn_page_t* page, const Point2Raw* origin); -#if __JHEXEN__ -void Hu_MenuDrawPlayerClassPage(mn_page_t* page, const Point2Raw* origin); -#endif -#if __JDOOM__ || __JHERETIC__ -void Hu_MenuDrawEpisodePage(mn_page_t* page, const Point2Raw* origin); -#endif -void Hu_MenuDrawOptionsPage(mn_page_t* page, const Point2Raw* origin); -void Hu_MenuDrawWeaponsPage(mn_page_t* page, const Point2Raw* origin); -void Hu_MenuDrawLoadGamePage(mn_page_t* page, const Point2Raw* origin); -void Hu_MenuDrawSaveGamePage(mn_page_t* page, const Point2Raw* origin); -void Hu_MenuDrawMultiplayerPage(mn_page_t* page, const Point2Raw* origin); -void Hu_MenuDrawPlayerSetupPage(mn_page_t* page, const Point2Raw* origin); - -int Hu_MenuColorWidgetCmdResponder(mn_page_t* page, menucommand_e cmd); - -static void initAllPages(void); -static void destroyAllPages(void); - -static void initAllObjectsOnAllPages(void); -static void updatePageObjects(mn_page_t* page); - -static void Hu_MenuUpdateCursorState(void); - -static dd_bool Hu_MenuHasCursorRotation(mn_object_t* obj); - -cvarbutton_t mnCVarButtons[] = { - { 0, "ctl-aim-noauto" }, -#if __JHERETIC__ || __JHEXEN__ - { 0, "ctl-inventory-mode", "Scroll", "Cursor" }, - { 0, "ctl-inventory-use-immediate" }, - { 0, "ctl-inventory-use-next" }, - { 0, "ctl-inventory-wrap" }, -#endif - { 0, "ctl-look-spring" }, - { 0, "ctl-run" }, -#if __JDOOM__ || __JDOOM64__ - { 0, "game-anybossdeath666" }, -#endif -#if __JDOOM__ || __JHERETIC__ || __JDOOM64__ - { 0, "game-corpse-sliding" }, -#endif -#if __JDOOM__ || __JDOOM64__ - { 0, "game-maxskulls" }, -#endif -#if __JDOOM__ || __JHERETIC__ || __JDOOM64__ - { 0, "game-monsters-stuckindoors" }, - { 0, "game-monsters-floatoverblocking" }, - { 0, "game-objects-clipping" }, - { 0, "game-objects-falloff" }, -#endif -#if __JDOOM__ || __JDOOM64__ - { 0, "game-objects-gibcrushednonbleeders" }, -#endif -#if __JDOOM__ || __JHERETIC__ || __JDOOM64__ - { 0, "game-objects-neverhangoverledges" }, - { 0, "game-player-wallrun-northonly" }, -#endif -#if __JDOOM__ - { 0, "game-raiseghosts" }, -#endif - { 0, "game-save-confirm" }, - { 0, "game-save-confirm-loadonreborn" }, -#if !__JHEXEN__ - { 0, "game-save-auto-loadonreborn" }, -#endif - { 0, "game-save-last-loadonreborn" }, -#if __JDOOM__ || __JDOOM64__ - { 0, "game-skullsinwalls" }, -#if __JDOOM__ - { 0, "game-vilechase-usevileradius" }, -#endif - { 0, "game-zombiescanexit" }, -#endif -#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ - { 0, "hud-ammo" }, - { 0, "hud-armor" }, -#endif -#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ - { 0, "hud-cheat-counter-show-mapopen" }, -#endif -#if __JHERETIC__ || __JHEXEN__ - { 0, "hud-currentitem" }, -#endif -#if __JDOOM__ - { 0, "hud-face" }, - { 0, "hud-face-ouchfix" }, -#endif - { 0, "hud-health" }, -#if __JHERETIC__ || __JHEXEN__ - { 0, "hud-inventory-slot-showempty" }, -#endif -#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ - { 0, "hud-keys" }, -#endif -#if __JDOOM__ - { 0, "hud-keys-combine" }, -#endif -#if __JHEXEN__ - { 0, "hud-mana" }, -#endif -#if __JDOOM64__ - { 0, "hud-power" }, -#endif -#if __JDOOM__ || __JDOOM64__ - { 0, "hud-status-weaponslots-ownedfix" }, -#endif - { 0, "hud-unhide-damage" }, - { 0, "hud-unhide-pickup-ammo" }, - { 0, "hud-unhide-pickup-armor" }, - { 0, "hud-unhide-pickup-health" }, -#if __JHERETIC__ || __JHEXEN__ - { 0, "hud-unhide-pickup-invitem" }, -#endif - { 0, "hud-unhide-pickup-powerup" }, - { 0, "hud-unhide-pickup-key" }, - { 0, "hud-unhide-pickup-weapon" }, - { 0, "map-door-colors" }, - { 0, "msg-show" }, -#if __JDOOM__ || __JDOOM64__ - { 0, "player-autoswitch-berserk" }, -#endif - { 0, "player-autoswitch-notfiring" }, -#if __JDOOM__ || __JHERETIC__ || __JDOOM64__ - { 0, "player-jump" }, -#endif - { 0, "player-weapon-cycle-sequential" }, - { 0, "player-weapon-nextmode" }, -#if __JDOOM64__ - { 0, "player-weapon-recoil" }, -#endif -#if __JDOOM__ || __JDOOM64__ - { 0, "server-game-bfg-freeaim" }, -#endif - { 0, "server-game-coop-nodamage" }, -#if __JDOOM__ || __JDOOM64__ - { 0, "server-game-coop-nothing" }, - { 0, "server-game-coop-noweapons" }, - { 0, "server-game-coop-respawn-items" }, -#endif -#if __JHEXEN__ - { 0, "server-game-deathmatch" }, -#endif -#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ - { 0, "server-game-jump" }, -#endif -#if __JDOOM__ || __JDOOM64__ - { 0, "server-game-nobfg" }, -#endif - { 0, "server-game-nomonsters" }, -#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ - { 0, "server-game-noteamdamage" }, -#endif - { 0, "server-game-radiusattack-nomaxz" }, -#if __JHEXEN__ - { 0, "server-game-randclass" }, -#endif -#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ - { 0, "server-game-respawn" }, -#endif - { 0, "view-cross-vitality" }, - { 0, 0 } -}; - -int menuTime = 0; -dd_bool menuNominatingQuickSaveSlot = false; - -static mn_page_t* menuActivePage = NULL; -static dd_bool menuActive = false; - -static float mnAlpha = 0; // Alpha level for the entire menu. -static float mnTargetAlpha = 0; // Target alpha for the entire UI. - -static skillmode_t mnSkillmode = SM_MEDIUM; -static int mnEpisode = 0; -#if __JHEXEN__ -static int mnPlrClass = PCLASS_FIGHTER; -#endif - -static int frame = 0; // Used by any graphic animations that need to be pumped. - -static dd_bool colorWidgetActive = false; - -// Present cursor state. -static dd_bool cursorHasRotation = false; -static float cursorAngle = 0; -static int cursorAnimCounter = 0; -static int cursorAnimFrame = 0; - -#if __JHERETIC__ -static char notDesignedForMessage[80]; -#endif - -static patchid_t pMainTitle; -#if __JDOOM__ || __JDOOM64__ -static patchid_t pNewGame; -static patchid_t pSkill; -static patchid_t pEpisode; -static patchid_t pNGame; -static patchid_t pOptions; -static patchid_t pLoadGame; -static patchid_t pSaveGame; -static patchid_t pReadThis; -static patchid_t pQuitGame; -static patchid_t pOptionsTitle; - -static patchid_t pSkillModeNames[NUM_SKILL_MODES]; -#endif -#if __JDOOM__ -static patchid_t pEpisodeNames[4]; -#endif - -#if __JHEXEN__ -static patchid_t pPlayerClassBG[3]; -static patchid_t pBullWithFire[8]; -#endif - -#if __JHERETIC__ -static patchid_t pRotatingSkull[18]; -#endif - -static patchid_t pCursors[MENU_CURSOR_FRAMECOUNT]; - -#if __JDOOM64__ -mndata_slider_t sld_hud_viewsize = { 3, 11, 0, 1, false, "view-size" }; -#else -mndata_slider_t sld_hud_viewsize = { 3, 13, 0, 1, false, "view-size" }; -#endif -mndata_slider_t sld_hud_uptime = { 0, 60, 0, 1.f, true, "hud-timer", "Disabled", NULL, " second", " seconds" }; -mndata_slider_t sld_hud_xhair_size = { 0, 1, 0, .1f, true, "view-cross-size" }; -mndata_slider_t sld_hud_xhair_angle = { 0, 1, 0, .0625f, true, "view-cross-angle" }; -mndata_slider_t sld_hud_xhair_opacity = { 0, 1, 0, .1f, true, "view-cross-a" }; -mndata_slider_t sld_hud_size = { 0, 1, 0, .1f, true, "hud-scale" }; -mndata_slider_t sld_hud_cntr_size = { 0, 1, 0, .1f, true, "hud-cheat-counter-scale" }; -mndata_slider_t sld_hud_sbar_size = { 0, 1, 0, .1f, true, "hud-status-size" }; -mndata_slider_t sld_hud_sbar_opacity = { 0, 1, 0, .1f, true, "hud-status-alpha" }; -mndata_slider_t sld_hud_msg_size = { 0, 1, 0, .1f, true, "msg-scale" }; -mndata_slider_t sld_hud_msg_uptime = { 0, 60, 0, 1.f, true, "msg-uptime", "Disabled", NULL, " second", " seconds" }; - -mndata_colorbox_t cbox_hud_color = { - 0, 0, 0, 0, 0, 0, true, - "hud-color-r", "hud-color-g", "hud-color-b", "hud-color-a" -}; - -mndata_colorbox_t cbox_hud_msg_color = { - 0, 0, 0, 0, 0, 0, false, - "msg-color-r", "msg-color-g", "msg-color-b" -}; - -mndata_listitem_t listit_hud_xhair_symbols[] = { - { "None", 0 }, - { "Cross", 1 }, - { "Twin Angles", 2 }, - { "Square", 3 }, - { "Open Square", 4 }, - { "Angle", 5 } -}; -mndata_list_t list_hud_xhair_symbol = { - listit_hud_xhair_symbols, NUMLISTITEMS(listit_hud_xhair_symbols), "view-cross-type" -}; - -mndata_colorbox_t cbox_hud_xhair_color = { - 0, 0, 0, 0, 0, 0, false, - "view-cross-r", "view-cross-g", "view-cross-b", -}; - -#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ -mndata_listitem_t listit_hud_killscounter_displaymethods[] = { - { "Hidden", 0 }, - { "Count", CCH_KILLS }, - { "Percent", CCH_KILLS_PRCNT }, - { "Count+Percent", CCH_KILLS | CCH_KILLS_PRCNT }, -}; -mndata_list_t list_hud_cntr_kills = { - listit_hud_killscounter_displaymethods, - NUMLISTITEMS(listit_hud_killscounter_displaymethods), - "hud-cheat-counter", CCH_KILLS | CCH_KILLS_PRCNT -}; - -mndata_listitem_t listit_hud_itemscounter_displaymethods[] = { - { "Hidden", 0 }, - { "Count", CCH_ITEMS }, - { "Percent", CCH_ITEMS_PRCNT }, - { "Count+Percent", CCH_ITEMS | CCH_ITEMS_PRCNT }, -}; -mndata_list_t list_hud_cntr_items = { - listit_hud_itemscounter_displaymethods, - NUMLISTITEMS(listit_hud_itemscounter_displaymethods), - "hud-cheat-counter", CCH_ITEMS | CCH_ITEMS_PRCNT -}; - -mndata_listitem_t listit_hud_secretscounter_displaymethods[] = { - { "Hidden", 0 }, - { "Count", CCH_SECRETS }, - { "Percent", CCH_SECRETS_PRCNT }, - { "Count+Percent", CCH_SECRETS | CCH_SECRETS_PRCNT }, -}; -mndata_list_t list_hud_cntr_secrets = { - listit_hud_secretscounter_displaymethods, - NUMLISTITEMS(listit_hud_secretscounter_displaymethods), - "hud-cheat-counter", CCH_SECRETS | CCH_SECRETS_PRCNT -}; -#endif - -mndata_text_t txt_hud_view_size = { "View Size" }; -#if __JDOOM__ -mndata_text_t txt_hud_single_key_display = { "Single Key Display" }; -#endif -mndata_text_t txt_hud_autohide = { "AutoHide" }; -mndata_text_t txt_hud_unhide_events = { "UnHide Events" }; -mndata_text_t txt_hud_unhide_receive_damage = { "Receive Damage" }; -mndata_text_t txt_hud_unhide_pickup_health = { "Pickup Health" }; -mndata_text_t txt_hud_unhide_pickup_armor = { "Pickup Armor" }; -mndata_text_t txt_hud_unhide_pickup_powerup = { "Pickup Powerup" }; -mndata_text_t txt_hud_unhide_pickup_weapon = { "Pickup Weapon" }; -#if __JHEXEN__ -mndata_text_t txt_hud_unhide_pickup_ammo = { "Pickup Mana" }; -#else -mndata_text_t txt_hud_unhide_pickup_ammo = { "Pickup Ammo" }; -#endif -mndata_text_t txt_hud_unhide_pickup_key = { "Pickup Key" }; -#if __JHERETIC__ || __JHEXEN__ -mndata_text_t txt_hud_unhide_pickup_item = { "Pickup Item" }; -#endif - -mndata_text_t txt_hud_messages = { "Messages" }; -mndata_text_t txt_hud_msg_shown = { "Shown" }; -mndata_text_t txt_hud_msg_size = { "Size" }; -mndata_text_t txt_hud_msg_color = { "Color" }; -mndata_text_t txt_hud_msg_uptime = { "Uptime" }; - -mndata_text_t txt_hud_crosshair = { "Crosshair" }; -mndata_text_t txt_hud_xhair_symbol = { "Symbol" }; -mndata_text_t txt_hud_xhair_size = { "Size" }; -mndata_text_t txt_hud_xhair_angle = { "Angle" }; -mndata_text_t txt_hud_xhair_opacity = { "Opacity" }; -mndata_text_t txt_hud_xhair_vitality_color = { "Vitality Color" }; -mndata_text_t txt_hud_xhair_color = { "Color" }; - -#if __JDOOM__ || __JHERETIC__ || __JHEXEN__ -mndata_text_t txt_hud_statusbar = { "Statusbar" }; -mndata_text_t txt_hud_sbar_size = { "Size" }; -mndata_text_t txt_hud_sbar_opacity = { "Opacity" }; -#endif -#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ -mndata_text_t txt_hud_counters = { "Counters" }; -mndata_text_t txt_hud_cntr_kills = { "Kills" }; -mndata_text_t txt_hud_cntr_items = { "Items" }; -mndata_text_t txt_hud_cntr_secrets = { "Secrets" }; -mndata_text_t txt_hud_cntr_size = { "Size" }; -#endif - -mndata_text_t txt_hud_fullscreen = { "Fullscreen" }; -mndata_text_t txt_hud_full_size = { "Size" }; -mndata_text_t txt_hud_full_text_color = { "Text Color" }; -#if __JHEXEN__ -mndata_text_t txt_hud_full_show_mana = { "Show Mana" }; -#endif -#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ -mndata_text_t txt_hud_full_show_ammo = { "Show Ammo" }; -mndata_text_t txt_hud_full_show_armor = { "Show Armor" }; -#endif -#if __JDOOM64__ -mndata_text_t txt_hud_full_show_powerkeys = { "Show PowerKeys" }; -#endif -#if __JDOOM__ -mndata_text_t txt_hud_full_show_status = { "Show Status" }; -#endif -mndata_text_t txt_hud_full_show_health = { "Show Health" }; -#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ -mndata_text_t txt_hud_full_show_keys = { "Show Keys" }; -#endif -#if __JHERETIC__ || __JHEXEN__ -mndata_text_t txt_hud_full_show_readyitem = { "Show Ready-Item" }; -#endif -#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ -mndata_text_t txt_hud_cntr_mapopen = { "Automap Only" }; -#endif - -mndata_button_t btn_hud_single_key_display = { true, "hud-keys-combine" }; -mndata_button_t btn_hud_unhide_receive_damage = { true, "hud-unhide-damage" }; -mndata_button_t btn_hud_unhide_pickup_health = { true, "hud-unhide-pickup-health" }; -mndata_button_t btn_hud_unhide_pickup_armor = { true, "hud-unhide-pickup-armor" }; -mndata_button_t btn_hud_unhide_pickup_powerup = { true, "hud-unhide-pickup-powerup" }; -mndata_button_t btn_hud_unhide_pickup_weapon = { true, "hud-unhide-pickup-weapon" }; -mndata_button_t btn_hud_unhide_pickup_ammo = { true, "hud-unhide-pickup-ammo" }; -mndata_button_t btn_hud_unhide_pickup_key = { true, "hud-unhide-pickup-key" }; -#if __JHERETIC__ || __JHEXEN__ -mndata_button_t btn_hud_unhide_pickup_item = { true, "hud-unhide-pickup-invitem" }; -#endif -mndata_button_t btn_hud_msg_shown = { true, "msg-show" }; -mndata_button_t btn_hud_xhair_vitality_color = { true, "view-cross-vitality" }; -#if __JHEXEN__ -mndata_button_t btn_hud_full_show_mana = { true, "hud-mana" }; -#endif -#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ -mndata_button_t btn_hud_full_show_ammo = { true, "hud-ammo" }; -mndata_button_t btn_hud_full_show_armor = { true, "hud-armor" }; -#endif -#if __JDOOM64__ -mndata_button_t btn_hud_full_show_powerkeys = { true, "hud-power" }; -#endif -#if __JDOOM__ -mndata_button_t btn_hud_full_show_face = { true, "hud-face" }; -#endif -mndata_button_t btn_hud_full_show_health = { true, "hud-health" }; -#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ -mndata_button_t btn_hud_full_show_keys = { true, "hud-keys" }; -#endif -#if __JHERETIC__ || __JHEXEN__ -mndata_button_t btn_hud_full_show_readyitem = { true, "hud-currentitem" }; -#endif -#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ -mndata_button_t btn_hud_cntr_mapopen = { true, "hud-cheat-counter-show-mapopen" }; -#endif - -static mn_object_t HudMenuObjects[] = { - { MN_TEXT, 0, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_view_size }, - { MN_SLIDER, 0, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNSlider_Ticker, MNSlider_UpdateGeometry, MNSlider_Drawer, { Hu_MenuCvarSlider, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_viewsize }, -#if __JDOOM__ - { MN_TEXT, 0, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_single_key_display }, - { MN_BUTTON, 0, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_single_key_display }, -#endif - { MN_TEXT, 0, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_autohide }, - { MN_SLIDER, 0, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR3, MNSlider_Ticker, MNSlider_TextualValueUpdateGeometry, MNSlider_TextualValueDrawer, { Hu_MenuCvarSlider, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_uptime }, - - { MN_TEXT, 1, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR2, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_unhide_events }, - { MN_TEXT, 1, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_unhide_receive_damage }, - { MN_BUTTON, 1, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_unhide_receive_damage }, - { MN_TEXT, 1, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_unhide_pickup_health }, - { MN_BUTTON, 1, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_unhide_pickup_health }, - { MN_TEXT, 1, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_unhide_pickup_armor }, - { MN_BUTTON, 1, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_unhide_pickup_armor }, - { MN_TEXT, 1, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_unhide_pickup_powerup }, - { MN_BUTTON, 1, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_unhide_pickup_powerup }, - { MN_TEXT, 1, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_unhide_pickup_weapon }, - { MN_BUTTON, 1, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_unhide_pickup_weapon }, - { MN_TEXT, 1, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_unhide_pickup_ammo }, - { MN_BUTTON, 1, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_unhide_pickup_ammo }, - { MN_TEXT, 1, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_unhide_pickup_key }, - { MN_BUTTON, 1, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_unhide_pickup_key }, -#if __JHERETIC__ || __JHEXEN__ - { MN_TEXT, 1, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_unhide_pickup_item }, - { MN_BUTTON, 1, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_unhide_pickup_item }, -#endif - - { MN_TEXT, 2, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR2, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_messages }, - { MN_TEXT, 2, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_msg_shown }, - { MN_BUTTON, 2, 0, { 0, 0 }, 'm',MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_msg_shown }, - { MN_TEXT, 2, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_msg_uptime }, - { MN_SLIDER, 2, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR3, MNSlider_Ticker, MNSlider_TextualValueUpdateGeometry, MNSlider_TextualValueDrawer, { Hu_MenuCvarSlider, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_msg_uptime }, - { MN_TEXT, 2, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_msg_size }, - { MN_SLIDER, 2, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNSlider_Ticker, MNSlider_UpdateGeometry, MNSlider_Drawer, { Hu_MenuCvarSlider, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_msg_size }, - { MN_TEXT, 2, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_msg_color }, - { MN_COLORBOX, 2, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNColorBox_Ticker, MNColorBox_UpdateGeometry, MNColorBox_Drawer, { Hu_MenuCvarColorBox, Hu_MenuCvarColorBox, Hu_MenuActivateColorWidget, NULL, NULL, Hu_MenuDefaultFocusAction }, MNColorBox_CommandResponder, NULL, NULL, &cbox_hud_msg_color }, - - { MN_TEXT, 3, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR2, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_crosshair }, - { MN_TEXT, 3, 0, { 0, 0 }, 'c',MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_xhair_symbol }, - { MN_LISTINLINE, 3, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR3, MNListInline_Ticker, MNListInline_UpdateGeometry, MNListInline_Drawer, { Hu_MenuCvarList, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNListInline_CommandResponder, NULL, NULL, &list_hud_xhair_symbol }, - { MN_TEXT, 3, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_xhair_size }, - { MN_SLIDER, 3, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNSlider_Ticker, MNSlider_UpdateGeometry, MNSlider_Drawer, { Hu_MenuCvarSlider, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_xhair_size }, - { MN_TEXT, 3, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_xhair_angle }, - { MN_SLIDER, 3, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNSlider_Ticker, MNSlider_UpdateGeometry, MNSlider_Drawer, { Hu_MenuCvarSlider, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_xhair_angle }, - { MN_TEXT, 3, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_xhair_opacity }, - { MN_SLIDER, 3, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNSlider_Ticker, MNSlider_UpdateGeometry, MNSlider_Drawer, { Hu_MenuCvarSlider, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_xhair_opacity }, - { MN_TEXT, 3, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_xhair_vitality_color }, - { MN_BUTTON, 3, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_xhair_vitality_color }, - { MN_TEXT, 3, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_xhair_color }, - { MN_COLORBOX, 3, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNColorBox_Ticker, MNColorBox_UpdateGeometry, MNColorBox_Drawer, { Hu_MenuCvarColorBox, Hu_MenuCvarColorBox, Hu_MenuActivateColorWidget, NULL, NULL, Hu_MenuDefaultFocusAction }, MNColorBox_CommandResponder, NULL, NULL, &cbox_hud_xhair_color }, - -#if __JDOOM__ || __JHERETIC__ || __JHEXEN__ - { MN_TEXT, 4, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR2, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_statusbar }, - { MN_TEXT, 4, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_sbar_size }, - { MN_SLIDER, 4, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNSlider_Ticker, MNSlider_UpdateGeometry, MNSlider_Drawer, { Hu_MenuCvarSlider, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_sbar_size }, - { MN_TEXT, 4, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_sbar_opacity }, - { MN_SLIDER, 4, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNSlider_Ticker, MNSlider_UpdateGeometry, MNSlider_Drawer, { Hu_MenuCvarSlider, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_sbar_opacity }, -#endif -#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ - { MN_TEXT, 5, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR2, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_counters }, - { MN_TEXT, 5, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_cntr_items }, - { MN_LISTINLINE, 5, 0, { 0, 0 }, 'i',MENU_FONT1, MENU_COLOR3, MNListInline_Ticker, MNListInline_UpdateGeometry, MNListInline_Drawer, { Hu_MenuCvarList, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNListInline_CommandResponder, NULL, NULL, &list_hud_cntr_items }, - { MN_TEXT, 5, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_cntr_kills }, - { MN_LISTINLINE, 5, 0, { 0, 0 }, 'k',MENU_FONT1, MENU_COLOR3, MNListInline_Ticker, MNListInline_UpdateGeometry, MNListInline_Drawer, { Hu_MenuCvarList, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNListInline_CommandResponder, NULL, NULL, &list_hud_cntr_kills }, - { MN_TEXT, 5, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_cntr_secrets }, - { MN_LISTINLINE, 5, 0, { 0, 0 }, 's',MENU_FONT1, MENU_COLOR3, MNListInline_Ticker, MNListInline_UpdateGeometry, MNListInline_Drawer, { Hu_MenuCvarList, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNListInline_CommandResponder, NULL, NULL, &list_hud_cntr_secrets }, - { MN_TEXT, 5, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_cntr_mapopen }, - { MN_BUTTON, 5, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_cntr_mapopen }, - { MN_TEXT, 5, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_cntr_size }, - { MN_SLIDER, 5, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNSlider_Ticker, MNSlider_UpdateGeometry, MNSlider_Drawer, { Hu_MenuCvarSlider, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_cntr_size }, -#endif - - { MN_TEXT, 6, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR2, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_fullscreen }, - { MN_TEXT, 6, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_full_size }, - { MN_SLIDER, 6, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNSlider_Ticker, MNSlider_UpdateGeometry, MNSlider_Drawer, { Hu_MenuCvarSlider, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_size }, - { MN_TEXT, 6, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_full_text_color }, - { MN_COLORBOX, 6, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNColorBox_Ticker, MNColorBox_UpdateGeometry, MNColorBox_Drawer, { Hu_MenuCvarColorBox, Hu_MenuCvarColorBox, Hu_MenuActivateColorWidget, NULL, NULL, Hu_MenuDefaultFocusAction }, MNColorBox_CommandResponder, NULL, NULL, &cbox_hud_color }, -#if __JHEXEN__ - { MN_TEXT, 6, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_full_show_mana }, - { MN_BUTTON, 6, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_full_show_mana }, -#endif -#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ - { MN_TEXT, 6, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_full_show_ammo }, - { MN_BUTTON, 6, 0, { 0, 0 }, 'a',MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_full_show_ammo }, - { MN_TEXT, 6, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_full_show_armor }, - { MN_BUTTON, 6, 0, { 0, 0 }, 'r',MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_full_show_armor }, -#endif -#if __JDOOM64__ - { MN_TEXT, 6, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_full_show_powerkeys }, - { MN_BUTTON, 6, 0, { 0, 0 }, 'p',MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_full_show_powerkeys }, -#endif -#if __JDOOM__ - { MN_TEXT, 6, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_full_show_status }, - { MN_BUTTON, 6, 0, { 0, 0 }, 'f',MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_full_show_face }, -#endif - { MN_TEXT, 6, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_full_show_health }, - { MN_BUTTON, 6, 0, { 0, 0 }, 'h',MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_full_show_health }, -#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ - { MN_TEXT, 6, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_full_show_keys }, - { MN_BUTTON, 6, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_full_show_keys }, -#endif -#if __JHERETIC__ || __JHEXEN__ - { MN_TEXT, 6, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { NULL }, NULL, NULL, NULL, &txt_hud_full_show_readyitem }, - { MN_BUTTON, 6, 0, { 0, 0 }, 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, NULL, NULL, NULL, NULL, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_full_show_readyitem }, -#endif - { MN_NONE } -}; - -static dd_bool inited = false; - -typedef struct { - mn_page_t* page; - ddstring_t name; // Symbolic name. -} pagerecord_t; - -static int pageCount; -static pagerecord_t* pages; - -// Cvars for the menu: -cvartemplate_t menuCVars[] = { - { "menu-scale", 0, CVT_FLOAT, &cfg.menuScale, .1f, 1 }, - { "menu-stretch", 0, CVT_BYTE, &cfg.menuScaleMode, SCALEMODE_FIRST, SCALEMODE_LAST }, - { "menu-flash-r", 0, CVT_FLOAT, &cfg.menuTextFlashColor[CR], 0, 1 }, - { "menu-flash-g", 0, CVT_FLOAT, &cfg.menuTextFlashColor[CG], 0, 1 }, - { "menu-flash-b", 0, CVT_FLOAT, &cfg.menuTextFlashColor[CB], 0, 1 }, - { "menu-flash-speed", 0, CVT_INT, &cfg.menuTextFlashSpeed, 0, 50 }, - { "menu-cursor-rotate", 0, CVT_BYTE, &cfg.menuCursorRotate, 0, 1 }, - { "menu-effect", 0, CVT_INT, &cfg.menuEffectFlags, 0, MEF_EVERYTHING }, - { "menu-color-r", 0, CVT_FLOAT, &cfg.menuTextColors[0][CR], 0, 1 }, - { "menu-color-g", 0, CVT_FLOAT, &cfg.menuTextColors[0][CG], 0, 1 }, - { "menu-color-b", 0, CVT_FLOAT, &cfg.menuTextColors[0][CB], 0, 1 }, - { "menu-colorb-r", 0, CVT_FLOAT, &cfg.menuTextColors[1][CR], 0, 1 }, - { "menu-colorb-g", 0, CVT_FLOAT, &cfg.menuTextColors[1][CG], 0, 1 }, - { "menu-colorb-b", 0, CVT_FLOAT, &cfg.menuTextColors[1][CB], 0, 1 }, - { "menu-colorc-r", 0, CVT_FLOAT, &cfg.menuTextColors[2][CR], 0, 1 }, - { "menu-colorc-g", 0, CVT_FLOAT, &cfg.menuTextColors[2][CG], 0, 1 }, - { "menu-colorc-b", 0, CVT_FLOAT, &cfg.menuTextColors[2][CB], 0, 1 }, - { "menu-colord-r", 0, CVT_FLOAT, &cfg.menuTextColors[3][CR], 0, 1 }, - { "menu-colord-g", 0, CVT_FLOAT, &cfg.menuTextColors[3][CG], 0, 1 }, - { "menu-colord-b", 0, CVT_FLOAT, &cfg.menuTextColors[3][CB], 0, 1 }, - { "menu-glitter", 0, CVT_FLOAT, &cfg.menuTextGlitter, 0, 1 }, - { "menu-fog", 0, CVT_INT, &cfg.hudFog, 0, 5 }, - { "menu-shadow", 0, CVT_FLOAT, &cfg.menuShadow, 0, 1 }, - { "menu-patch-replacement", 0, CVT_INT, &cfg.menuPatchReplaceMode, PRM_FIRST, PRM_LAST }, - { "menu-slam", 0, CVT_BYTE, &cfg.menuSlam, 0, 1 }, - { "menu-hotkeys", 0, CVT_BYTE, &cfg.menuShortcutsEnabled, 0, 1 }, -#if __JDOOM__ || __JDOOM64__ - { "menu-quitsound", 0, CVT_INT, &cfg.menuQuitSound, 0, 1 }, -#endif - { "menu-save-suggestname", 0, CVT_BYTE, &cfg.menuGameSaveSuggestName, 0, 1 }, - - // Aliases for obsolete cvars: - { "menu-turningskull", 0, CVT_BYTE, &cfg.menuCursorRotate, 0, 1 }, - { NULL } -}; - -// Console commands for the menu: -ccmdtemplate_t menuCCmds[] = { - { "menu", "s", CCmdMenuOpen }, - { "menu", "", CCmdMenuOpen }, - { "menuup", "", CCmdMenuCommand }, - { "menudown", "", CCmdMenuCommand }, - { "menupageup", "", CCmdMenuCommand }, - { "menupagedown", "", CCmdMenuCommand }, - { "menuleft", "", CCmdMenuCommand }, - { "menuright", "", CCmdMenuCommand }, - { "menuselect", "", CCmdMenuCommand }, - { "menudelete", "", CCmdMenuCommand }, - { "menuback", "", CCmdMenuCommand }, - { NULL } -}; - -void Hu_MenuRegister(void) -{ - int i; - for(i = 0; menuCVars[i].path; ++i) - Con_AddVariable(menuCVars + i); - for(i = 0; menuCCmds[i].name; ++i) - Con_AddCommand(menuCCmds + i); -} - -static dd_bool chooseCloseMethod(void) -{ - // If we aren't using a transition then we can close normally and allow our - // own menu fade-out animation to be used instead. - return Con_GetInteger("con-transition-tics") == 0? MCMD_CLOSE : MCMD_CLOSEFAST; -} - -mn_page_t* Hu_MenuFindPageByName(const char* name) -{ - if(name && name[0]) - { - int i; - for(i = 0; i < pageCount; ++i) - { - pagerecord_t* rec = pages + i; - if(!stricmp(name, Str_Text(&rec->name))) - { - return rec->page; - } - } - } - return NULL; -} - -/// @todo Make this state an object property flag. -/// @return @c true if the rotation of a cursor on this object should be animated. -static dd_bool Hu_MenuHasCursorRotation(mn_object_t* obj) -{ - assert(obj); - return (!(MNObject_Flags(obj) & MNF_DISABLED) && - (MNObject_Type(obj) == MN_LISTINLINE || MNObject_Type(obj) == MN_SLIDER)); -} - -/// To be called to re-evaluate the state of the cursor (e.g., when focus changes). -static void Hu_MenuUpdateCursorState(void) -{ - if(menuActive) - { - mn_page_t* page; - mn_object_t* obj; - - if(colorWidgetActive) - page = Hu_MenuFindPageByName("ColorWidget"); - else - page = Hu_MenuActivePage(); - - obj = MNPage_FocusObject(page); - if(obj) - { - cursorHasRotation = Hu_MenuHasCursorRotation(obj); - return; - } - } - cursorHasRotation = false; -} - -void Hu_MenuLoadResources(void) -{ - char buffer[9]; - int i; - -#if __JDOOM__ || __JDOOM64__ - pMainTitle = R_DeclarePatch("M_DOOM"); -#elif __JHERETIC__ || __JHEXEN__ - pMainTitle = R_DeclarePatch("M_HTIC"); -#endif - -#if __JDOOM__ || __JDOOM64__ - pNewGame = R_DeclarePatch("M_NEWG"); - pSkill = R_DeclarePatch("M_SKILL"); - pEpisode = R_DeclarePatch("M_EPISOD"); - pNGame = R_DeclarePatch("M_NGAME"); - pOptions = R_DeclarePatch("M_OPTION"); - pLoadGame = R_DeclarePatch("M_LOADG"); - pSaveGame = R_DeclarePatch("M_SAVEG"); - pReadThis = R_DeclarePatch("M_RDTHIS"); - pQuitGame = R_DeclarePatch("M_QUITG"); - pOptionsTitle = R_DeclarePatch("M_OPTTTL"); -#endif - -#if __JDOOM__ || __JDOOM64__ - pSkillModeNames[SM_BABY] = R_DeclarePatch("M_JKILL"); - pSkillModeNames[SM_EASY] = R_DeclarePatch("M_ROUGH"); - pSkillModeNames[SM_MEDIUM] = R_DeclarePatch("M_HURT"); - pSkillModeNames[SM_HARD] = R_DeclarePatch("M_ULTRA"); -# if __JDOOM__ - pSkillModeNames[SM_NIGHTMARE] = R_DeclarePatch("M_NMARE"); -# endif -#endif - -#if __JDOOM__ - if(gameModeBits & (GM_DOOM_SHAREWARE|GM_DOOM|GM_DOOM_ULTIMATE)) - { - pEpisodeNames[0] = R_DeclarePatch("M_EPI1"); - pEpisodeNames[1] = R_DeclarePatch("M_EPI2"); - pEpisodeNames[2] = R_DeclarePatch("M_EPI3"); - } - if(gameModeBits & GM_DOOM_ULTIMATE) - { - pEpisodeNames[3] = R_DeclarePatch("M_EPI4"); - } -#endif - -#if __JHERETIC__ - for(i = 0; i < 18; ++i) - { - dd_snprintf(buffer, 9, "M_SKL%02d", i); - pRotatingSkull[i] = R_DeclarePatch(buffer); - } -#endif - -#if __JHEXEN__ - for(i = 0; i < 7; ++i) - { - dd_snprintf(buffer, 9, "FBUL%c0", 'A'+i); - pBullWithFire[i] = R_DeclarePatch(buffer); - } - - pPlayerClassBG[0] = R_DeclarePatch("M_FBOX"); - pPlayerClassBG[1] = R_DeclarePatch("M_CBOX"); - pPlayerClassBG[2] = R_DeclarePatch("M_MBOX"); -#endif - - for(i = 0; i < MENU_CURSOR_FRAMECOUNT; ++i) - { -#if __JDOOM__ || __JDOOM64__ - dd_snprintf(buffer, 9, "M_SKULL%d", i+1); -#else - dd_snprintf(buffer, 9, "M_SLCTR%d", i+1); -#endif - pCursors[i] = R_DeclarePatch(buffer); - } -} - -void Hu_MenuInitColorWidgetPage(void) -{ -#if __JHERETIC__ || __JHEXEN__ - const Point2Raw origin = { 98, 60 }; -#else - const Point2Raw origin = { 124, 60 }; -#endif - mn_object_t* objects, *ob; - const uint numObjects = 10; - mn_page_t* page; - - page = Hu_MenuNewPage("ColorWidget", &origin, MPF_NEVER_SCROLL, Hu_MenuPageTicker, NULL, Hu_MenuColorWidgetCmdResponder, NULL); - MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); - - objects = Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); - if(!objects) Con_Error("Hu_MenuInitColorWidgetPage: Failed on allocation of %lu bytes for menu objects.", (unsigned long) (sizeof(*objects) * numObjects)); - - ob = objects; - - ob->_type = MN_COLORBOX; - ob->_flags = MNF_ID0|MNF_NO_FOCUS; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNColorBox_Ticker; - ob->updateGeometry = MNColorBox_UpdateGeometry; - ob->drawer = MNColorBox_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_colorbox_t), PU_GAMESTATIC, 0); - { mndata_colorbox_t* cbox = (mndata_colorbox_t*)ob->_typedata; - cbox->width = SCREENHEIGHT/7; - cbox->height = SCREENHEIGHT/7; - cbox->rgbaMode = true; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Red"; - } - ob++; - - ob->_type = MN_SLIDER; - ob->_flags = MNF_ID1; - ob->_shortcut = 'r'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNSlider_Ticker; - ob->updateGeometry = MNSlider_UpdateGeometry; - ob->drawer = MNSlider_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuUpdateColorWidgetColor; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNSlider_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); - ob->data2 = CR; - { mndata_slider_t* sld = (mndata_slider_t*)ob->_typedata; - sld->min = 0; - sld->max = 1; - sld->value = 0; - sld->step = .05f; - sld->floatMode = true; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Green"; - } - ob++; - - ob->_type = MN_SLIDER; - ob->_flags = MNF_ID2; - ob->_shortcut = 'g'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNSlider_Ticker; - ob->updateGeometry = MNSlider_UpdateGeometry; - ob->drawer = MNSlider_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuUpdateColorWidgetColor; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNSlider_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); - ob->data2 = CG; - { mndata_slider_t* sld = (mndata_slider_t*)ob->_typedata; - sld->min = 0; - sld->max = 1; - sld->value = 0; - sld->step = .05f; - sld->floatMode = true; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Blue"; - } - ob++; - - ob->_type = MN_SLIDER; - ob->_flags = MNF_ID3; - ob->_shortcut = 'b'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNSlider_Ticker; - ob->updateGeometry = MNSlider_UpdateGeometry; - ob->drawer = MNSlider_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuUpdateColorWidgetColor; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNSlider_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); - ob->data2 = CB; - { mndata_slider_t* sld = (mndata_slider_t*)ob->_typedata; - sld->min = 0; - sld->max = 1; - sld->value = 0; - sld->step = .05f; - sld->floatMode = true; - } - ob++; - - ob->_type = MN_TEXT; - ob->_flags = MNF_ID4; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Opacity"; - } - ob++; - - ob->_type = MN_SLIDER; - ob->_flags = MNF_ID5; - ob->_shortcut = 'o'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNSlider_Ticker; - ob->updateGeometry = MNSlider_UpdateGeometry; - ob->drawer = MNSlider_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuUpdateColorWidgetColor; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNSlider_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); - ob->data2 = CA; - { mndata_slider_t* sld = (mndata_slider_t*)ob->_typedata; - sld->min = 0; - sld->max = 1; - sld->value = 0; - sld->step = .05f; - sld->floatMode = true; - } - ob++; - - ob->_type = MN_NONE; - - page->objects = objects; -} - -void Hu_MenuInitMainPage(void) -{ -#if __JHEXEN__ || __JHERETIC__ - Point2Raw origin = { 110, 56 }; - uint numObjects = 6; -#else - Point2Raw origin = { 97, 64 }; -# if __JDOOM64__ - uint numObjects = 7; -# else - uint numObjects = 8; -# endif -#endif - mn_object_t* objects, *ob; - mn_page_t* page; - int y; - -#if __JDOOM__ - if(gameModeBits & GM_ANY_DOOM2) - origin.y += 8; -#endif - -#if __JDOOM__ || __JDOOM64__ - page = Hu_MenuNewPage("Main", &origin, MPF_LAYOUT_FIXED|MPF_NEVER_SCROLL, Hu_MenuPageTicker, NULL, NULL, NULL); -#else - page = Hu_MenuNewPage("Main", &origin, MPF_LAYOUT_FIXED|MPF_NEVER_SCROLL, Hu_MenuPageTicker, Hu_MenuDrawMainPage, NULL, NULL); -#endif - MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTB)); - - objects = Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); - if(!objects) Con_Error("Hu_MenuInitMainPage: Failed on allocation of %lu bytes for menu objects.", (unsigned long) (sizeof(*objects) * numObjects)); - - ob = objects; - y = 0; - -#if __JDOOM__ || __JDOOM64__ - ob->_type = MN_TEXT; - ob->_origin.x = -3; - ob->_origin.y = -70; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->patch = &pMainTitle; - } - ob++; -#endif - - ob->_type = MN_BUTTON; - ob->_origin.y = y; - ob->_shortcut = 'n'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->data1 = "GameType"; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; -#if defined(__JDOOM__) && !defined(__JDOOM64__) - btn->patch = &pNGame; -#else - btn->text = "New Game"; -#endif - } - ob++; y += FIXED_LINE_HEIGHT; - - ob->_type = MN_BUTTON; - ob->_origin.y = y; - ob->_shortcut = 'o'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->data1 = "Options"; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; -#if defined(__JDOOM__) && !defined(__JDOOM64__) - btn->patch = &pOptions; -#else - btn->text = "Options"; -#endif - } - ob++; y += FIXED_LINE_HEIGHT; - -#if __JDOOM__ || __JDOOM64__ - ob->_type = MN_BUTTON; - ob->_origin.y = y; - ob->_shortcut = 'l'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectLoadGame; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; -# if __JDOOM64__ - btn->text = "Load Game"; -# else - btn->patch = &pLoadGame; -# endif - } - ob++; y += FIXED_LINE_HEIGHT; - - ob->_type = MN_BUTTON; - ob->_origin.y = y; - ob->_shortcut = 's'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectSaveGame; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; -# if __JDOOM64__ - btn->text = "Save Game"; -# else - btn->patch = &pSaveGame; -# endif - } - ob++; y += FIXED_LINE_HEIGHT; -#else - ob->_type = MN_BUTTON; - ob->_origin.y = y; - ob->_shortcut = 'f'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->data1 = "Files"; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = "Game Files"; - } - ob++; y += FIXED_LINE_HEIGHT; -#endif - -#if !__JDOOM64__ - ob->_type = MN_BUTTON; - ob->_origin.y = y; -# if __JDOOM__ - ob->_flags = MNF_ID0; - ob->_shortcut = 'r'; -# else - ob->_shortcut = 'i'; -# endif - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectHelp; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; -# if defined(__JDOOM__) - btn->patch = &pReadThis; -# else - btn->text = "Info"; -# endif - } - ob++; y += FIXED_LINE_HEIGHT; -#endif - - ob->_type = MN_BUTTON; -#if __JDOOM__ - ob->_flags = MNF_ID1; -#endif - ob->_origin.y = y; - ob->_shortcut = 'q'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectQuitGame; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; -#if defined(__JDOOM__) && !defined(__JDOOM64__) - btn->patch = &pQuitGame; -#else - btn->text = "Quit Game"; -#endif - } - ob++; y += FIXED_LINE_HEIGHT; - - ob->_type = MN_NONE; - - page->objects = objects; -} - -void Hu_MenuInitGameTypePage(void) -{ -#if __JDOOM__ || __JDOOM64__ - Point2Raw origin = { 97, 65 }; -#else - Point2Raw origin = { 104, 65 }; -#endif - mn_object_t* objects, *ob; - const uint numObjects = 3; - mn_page_t* page; - int y; - - page = Hu_MenuNewPage("GameType", &origin, 0, Hu_MenuPageTicker, Hu_MenuDrawGameTypePage, NULL, NULL); - MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTB)); - MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Main")); - - objects = Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); - if(!objects) Con_Error("Hu_MenuInitGameTypeMenu: Failed on allocation of %lu bytes for menu objects.", (unsigned long) (sizeof(*objects) * numObjects)); - - ob = objects; - y = 0; - - ob->_type = MN_BUTTON; - ob->_origin.y = y; - ob->_shortcut = 's'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectSingleplayer; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = (const char*)TXT_SINGLEPLAYER; - } - ob++; y += FIXED_LINE_HEIGHT; - - ob->_type = MN_BUTTON; - ob->_origin.y = y; - ob->_shortcut = 'm'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectMultiplayer; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = (const char*)TXT_MULTIPLAYER; - } - ob++; y += FIXED_LINE_HEIGHT; - - ob->_type = MN_NONE; - - page->objects = objects; -} - -void Hu_MenuInitSkillPage(void) -{ -#if __JHEXEN__ - const Point2Raw origin = { 120, 44 }; -#elif __JHERETIC__ - const Point2Raw origin = { 38, 30 }; -#else - const Point2Raw origin = { 48, 63 }; -#endif - int skillButtonFlags[NUM_SKILL_MODES] = { - MNF_ID0, - MNF_ID1, - MNF_ID2 | MNF_DEFAULT, - MNF_ID3, -# if !__JDOOM64__ - MNF_ID4 -# endif - }; -#if !__JHEXEN__ - int skillButtonTexts[NUM_SKILL_MODES] = { - TXT_SKILL1, - TXT_SKILL2, - TXT_SKILL3, - TXT_SKILL4, -# if !__JDOOM64__ - TXT_SKILL5 -# endif - }; -#endif - mn_object_t* objects, *ob; - const uint numObjects = NUM_SKILL_MODES + 1; - mn_page_t* page; - int y; - - page = Hu_MenuNewPage("Skill", &origin, MPF_LAYOUT_FIXED|MPF_NEVER_SCROLL, Hu_MenuPageTicker, Hu_MenuDrawSkillPage, NULL, NULL); - MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTB)); -#if __JHEXEN__ - MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("PlayerClass")); -#elif __JHERETIC__ - MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Episode")); -#elif __JDOOM64__ - MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("GameType")); -#else // __JDOOM__ - if(gameModeBits & (GM_ANY_DOOM2|GM_DOOM_CHEX)) - { - MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("GameType")); - } - else - { - MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Episode")); - } -#endif - - objects = Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); - if(!objects) Con_Error("Hu_MenuInitSkillPage: Failed on allocation of %lu bytes for menu objects.", (unsigned long) (sizeof(*objects) * numObjects)); - - ob = objects; - y = 0; - - { uint i; - for(i = 0; i < NUM_SKILL_MODES; ++i, ob++, y += FIXED_LINE_HEIGHT) - { - ob->_type = MN_BUTTON; - ob->_flags = skillButtonFlags[i]; -#if !__JHEXEN__ - ob->_shortcut = GET_TXT(skillButtonTexts[i])[0]; -#endif - ob->_origin.y = y; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionInitNewGame; - ob->actions[MNA_FOCUS].callback = Hu_MenuFocusSkillMode; - ob->cmdResponder = MNButton_CommandResponder; - ob->data2 = (int)(SM_BABY + i); - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); -#if !__JHEXEN__ - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = INT2PTR(const char, skillButtonTexts[i]); -# if __JDOOM__ || __JDOOM64__ - btn->patch = &pSkillModeNames[i]; -# endif - } -#endif - }} - - ob->_type = MN_NONE; - - page->objects = objects; - -#if __JDOOM__ - if(gameMode != doom2_hacx && gameMode != doom_chex) - { - mn_object_t* ob = MN_MustFindObjectOnPage(page, 0, MNF_ID4); - MNButton_SetFlags(ob, FO_SET, MNBUTTON_NO_ALTTEXT); - } -#endif -} - -void Hu_MenuInitMultiplayerPage(void) -{ -#if __JHERETIC__ || __JHEXEN__ - const Point2Raw origin = { 97, 65 }; -#else - const Point2Raw origin = { 97, 65 }; -#endif - mn_object_t* objects, *ob; - const uint numObjects = 3; - mn_page_t* page; - - page = Hu_MenuNewPage("Multiplayer", &origin, 0, Hu_MenuPageTicker, Hu_MenuDrawMultiplayerPage, NULL, NULL); - MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTB)); - MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("GameType")); - - objects = Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); - if(!objects) Con_Error("Hu_MenuInitMultiplayerMenu: Failed on allocation of %lu bytes for menu objects.", (unsigned long) (sizeof(*objects) * numObjects)); - - ob = objects; - - ob->_type = MN_BUTTON; - ob->_flags = MNF_ID0; - ob->_shortcut = 'j'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectJoinGame; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = "Join Game"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_shortcut = 's'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectPlayerSetup; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = "Player Setup"; - } - ob++; - - ob->_type = MN_NONE; - - page->objects = objects; -} - -void Hu_MenuInitPlayerSetupPage(void) -{ -#if __JHERETIC__ || __JHEXEN__ - const Point2Raw origin = { 70, 44 }; -#else - const Point2Raw origin = { 70, 54 }; -#endif - mn_object_t* objects, *ob; - mn_page_t* page; - uint numObjects; - - page = Hu_MenuNewPage("PlayerSetup", &origin, 0, Hu_MenuPageTicker, Hu_MenuDrawPlayerSetupPage, NULL, NULL); - MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); - MNPage_SetPredefinedFont(page, MENU_FONT2, FID(GF_FONTB)); - MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Multiplayer")); - -#if __JHEXEN__ - numObjects = 8; -#else - numObjects = 6; -#endif - objects = Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); - if(!objects) Con_Error("Hu_MenuInitPlayerSetupMenu: Failed on allocation of %lu bytes for player setup menu objects.", (unsigned long) (sizeof(*objects) * numObjects)); - - ob = objects; - - ob->_type = MN_MOBJPREVIEW; - ob->_origin.x = SCREENWIDTH/2 - origin.x; - ob->_origin.y = 60; - ob->_flags = MNF_ID0 | MNF_POSITION_FIXED; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNMobjPreview_Ticker; - ob->updateGeometry = MNMobjPreview_UpdateGeometry; - ob->drawer = MNMobjPreview_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_mobjpreview_t), PU_GAMESTATIC, 0); - ob++; - - ob->_type = MN_EDIT; - ob->_flags = MNF_ID1 | MNF_LAYOUT_OFFSET; - ob->_origin.y = 75; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNEdit_Ticker; - ob->updateGeometry = MNEdit_UpdateGeometry; - ob->drawer = MNEdit_Drawer; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNEdit_CommandResponder; - ob->responder = MNEdit_Responder; - ob->_typedata = Z_Calloc(sizeof(mndata_edit_t), PU_GAMESTATIC, 0); - { mndata_edit_t* edit = (mndata_edit_t*)ob->_typedata; - Str_Init(&edit->text); - Str_Init(&edit->oldtext); - edit->data1 = "net-name"; - edit->maxLength = 24; - } - ob++; - -#if __JHEXEN__ - ob->_type = MN_TEXT; - ob->_flags = MNF_LAYOUT_OFFSET; - ob->_origin.y = 5; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Class"; - } - ob++; - - ob->_type = MN_LISTINLINE; - ob->_flags = MNF_ID2; - ob->_shortcut = 'c'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNListInline_Ticker; - ob->updateGeometry = MNListInline_UpdateGeometry; - ob->drawer = MNListInline_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuSelectPlayerSetupPlayerClass; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNListInline_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_list_t), PU_GAMESTATIC, 0); - { mndata_list_t* list = (mndata_list_t*)ob->_typedata; - list->count = 3; - list->items = (mndata_listitem_t*)Z_Calloc(sizeof(mndata_listitem_t) * list->count, PU_GAMESTATIC, 0); - - { mndata_listitem_t* item = list->items; - item->text = (const char*)TXT_PLAYERCLASS1; - item->data = PCLASS_FIGHTER; - item++; - - item->text = (const char*)TXT_PLAYERCLASS2; - item->data = PCLASS_CLERIC; - item++; - - item->text = (const char*)TXT_PLAYERCLASS3; - item->data = PCLASS_MAGE; - } - } - ob++; -#endif - - ob->_type = MN_TEXT; -#ifdef __JHERETIC__ - ob->_flags = MNF_LAYOUT_OFFSET; - ob->_origin.y = 5; -#endif - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Color"; - } - ob++; - - // Setup the player color selection list. - ob->_type = MN_LISTINLINE; - ob->_flags = MNF_ID3; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNListInline_Ticker; - ob->updateGeometry = MNListInline_UpdateGeometry; - ob->drawer = MNListInline_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuSelectPlayerColor; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNListInline_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_list_t), PU_GAMESTATIC, 0); - { mndata_list_t* list = (mndata_list_t*)ob->_typedata; -#if __JHEXEN__ - // Hexen v1.0 has only four player colors. - list->count = (gameMode == hexen_v10? 4 : NUMPLAYERCOLORS) + 1/*auto*/; -#else - list->count = NUMPLAYERCOLORS + 1/*auto*/; -#endif - list->items = (mndata_listitem_t*)Z_Calloc(sizeof(mndata_listitem_t) * list->count, PU_GAMESTATIC, 0); - - /// @todo Read these names from Text definitions. - { mndata_listitem_t* item = list->items; -#if __JHEXEN__ - item->text = "Red"; item->data = 0; - item++; - - item->text = "Blue"; item->data = 1; - item++; - - item->text = "Yellow"; item->data = 2; - item++; - - item->text = "Green"; item->data = 3; - item++; - - // Hexen v1.0 has only four player colors. - if(gameMode != hexen_v10) - { - item->text = "Jade"; item->data = 4; - item++; - - item->text = "White"; item->data = 5; - item++; - - item->text = "Hazel"; item->data = 6; - item++; - - item->text = "Purple"; item->data = 7; - item++; - } - - item->text = "Automatic"; item->data = 8; -#elif __JHERETIC__ - item->text = "Green"; item->data = 0; - item++; - - item->text = "Orange"; item->data = 1; - item++; - - item->text = "Red"; item->data = 2; - item++; - - item->text = "Blue"; item->data = 3; - item++; - - item->text = "Automatic"; item->data = 4; -#else - item->text = "Green"; item->data = 0; - item++; - - item->text = "Indigo"; item->data = 1; - item++; - - item->text = "Brown"; item->data = 2; - item++; - - item->text = "Red"; item->data = 3; - item++; - - item->text = "Automatic"; item->data = 4; -#endif - } - } - ob++; - - ob->_type = MN_BUTTON; - ob->_shortcut = 's'; - ob->_pageFontIdx = MENU_FONT2; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectAcceptPlayerSetup; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = "Save Changes"; - } - ob++; - - ob->_type = MN_NONE; - - page->objects = objects; -} - -void Hu_MenuInitSaveOptionsPage(void) -{ - const Point2Raw origin = { 60, 50 }; - mn_object_t* objects, *ob; -#if !__JHEXEN__ - const uint numObjects = 10; -#else - const uint numObjects = 8; -#endif - mn_page_t* page; - - page = Hu_MenuNewPage("SaveOptions", &origin, 0, Hu_MenuPageTicker, NULL, NULL, NULL); - MNPage_SetTitle(page, "Save Options"); - MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); - MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Options")); - - objects = Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); - if(!objects) Con_Error("Hu_MenuInitSaveOptionsPage: Failed on allocation of %lu bytes for menu objects.", (unsigned long) (sizeof(*objects) * numObjects)); - - ob = objects; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Confirm quick load/save"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_shortcut = 'q'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "game-save-confirm"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Confirm reborn load"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_shortcut = 'r'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "game-save-confirm-loadonreborn"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR2; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Reborn preferences"; - } - ob++; - -#if !__JHEXEN__ - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Load auto save"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 1; - ob->_shortcut = 'a'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "game-save-auto-loadonreborn"; - } - ob++; -#endif - - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Load last save"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 1; - ob->_shortcut = 'a'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "game-save-last-loadonreborn"; - } - ob++; - - ob->_type = MN_NONE; - - page->objects = objects; -} - -#if __JHERETIC__ || __JHEXEN__ -void Hu_MenuInitFilesPage(void) -{ - Point2Raw origin = { 110, 60 }; - mn_object_t* objects, *ob; - const uint numObjects = 3; - mn_page_t* page; - int y; - - page = Hu_MenuNewPage("Files", &origin, MPF_LAYOUT_FIXED|MPF_NEVER_SCROLL, Hu_MenuPageTicker, NULL, NULL, NULL); - MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTB)); - MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Main")); - - objects = Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); - if(!objects) Con_Error("Hu_MenuInitFilesMenu: Failed on allocation of %lu bytes for menu objects.", (unsigned long) (sizeof(*objects) * numObjects)); - - ob = objects; - y = 0; - - ob->_type = MN_BUTTON; - ob->_origin.y = y; - ob->_shortcut = 'l'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectLoadGame; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = "Load Game"; - } - ob++; y += FIXED_LINE_HEIGHT; - - ob->_type = MN_BUTTON; - ob->_origin.y = y; - ob->_shortcut = 's'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectSaveGame; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = "Save Game"; - } - ob++; y += FIXED_LINE_HEIGHT; - - ob->_type = MN_NONE; - - page->objects = objects; -} -#endif - -static void deleteGameSave(int slot) -{ - DD_Executef(true, "deletegamesave %i", slot); -} - -int Hu_MenuLoadSlotCommandResponder(mn_object_t* ob, menucommand_e cmd) -{ - DENG_ASSERT(ob && ob->_type == MN_EDIT); - if(MCMD_DELETE == cmd && - (ob->_flags & MNF_FOCUS) && !(ob->_flags & MNF_ACTIVE) && !(ob->_flags & MNF_DISABLED)) - { - mndata_edit_t* edit = (mndata_edit_t*)ob->_typedata; - deleteGameSave(edit->data2); - return true; - } - return MNObject_DefaultCommandResponder(ob, cmd); -} - -int Hu_MenuSaveSlotCommandResponder(mn_object_t* ob, menucommand_e cmd) -{ - assert(ob); - if(MCMD_DELETE == cmd && - (ob->_flags & MNF_FOCUS) && !(ob->_flags & MNF_ACTIVE) && !(ob->_flags & MNF_DISABLED)) - { - mndata_edit_t* edit = (mndata_edit_t*)ob->_typedata; - deleteGameSave(edit->data2); - return true; - } - return MNEdit_CommandResponder(ob, cmd); -} - -void Hu_MenuInitLoadGameAndSaveGamePages(void) -{ -#if __JDOOM__ || __JDOOM64__ - Point2Raw const origin = { 80, 54 }; -#else - Point2Raw const origin = { 70, 30 }; -#endif - mn_object_t *loadMenuObjects, *saveMenuObjects; - mndata_edit_t *saveSlots; - mn_page_t *page; - int const saveSlotObjectIds[NUMSAVESLOTS] = { - MNF_ID0, MNF_ID1, MNF_ID2, MNF_ID3, MNF_ID4, MNF_ID5, -#if !__JHEXEN__ - MNF_ID6, MNF_ID7 -#endif - }; - int i, y; - - saveSlots = Z_Calloc(sizeof(*saveSlots) * NUMSAVESLOTS, PU_GAMESTATIC, 0); - if(!saveSlots) Con_Error("Hu_MenuInitLoadGameAndSaveGamePages: Failed on allocation of %lu bytes for load/save menu edit fields.", (unsigned long) (sizeof(*saveSlots) * NUMSAVESLOTS)); - - for(i = 0; i < NUMSAVESLOTS; ++i) - { - mndata_edit_t* slot = saveSlots + i; - slot->emptyString = (const char*) TXT_EMPTYSTRING; - slot->data2 = i; - slot->maxLength = 24; - } - - loadMenuObjects = Z_Calloc(sizeof(*loadMenuObjects) * (NUMSAVESLOTS+1), PU_GAMESTATIC, 0); - if(!loadMenuObjects) Con_Error("Hu_MenuInitLoadGameAndSaveGamePages: Failed on allocation of %lu bytes for load menu objects.", (unsigned long) (sizeof(*loadMenuObjects) * (NUMSAVESLOTS+1))); - - y = 0; - for(i = 0; i < NUMSAVESLOTS; ++i, y += FIXED_LINE_HEIGHT) - { - mn_object_t* ob = loadMenuObjects + i; - mndata_edit_t* edit = saveSlots + i; - ob->_type = MN_EDIT; - ob->_origin.x = 0; - ob->_origin.y = y; - ob->_flags = saveSlotObjectIds[i] | MNF_DISABLED; - ob->_shortcut = '0' + i; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->updateGeometry = MNEdit_UpdateGeometry; - ob->drawer = MNEdit_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectLoadSlot; - ob->actions[MNA_FOCUSOUT].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = Hu_MenuLoadSlotCommandResponder; - ob->_typedata = edit; - ob->data2 = saveSlotObjectIds[i]; - Str_Init(&edit->text); - Str_Init(&edit->oldtext); - } - loadMenuObjects[i]._type = MN_NONE; - - saveMenuObjects = Z_Calloc(sizeof(*saveMenuObjects) * (NUMSAVESLOTS+1), PU_GAMESTATIC, 0); - if(!saveMenuObjects) Con_Error("initAllPages: Failed on allocation of %lu bytes for save menu objects.", (unsigned long) (sizeof(*saveMenuObjects) * (NUMSAVESLOTS+1))); - - y = 0; - for(i = 0; i < NUMSAVESLOTS; ++i, y += FIXED_LINE_HEIGHT) - { - mn_object_t* ob = saveMenuObjects + i; - mndata_edit_t* edit = saveSlots + i; - ob->_type = MN_EDIT; - ob->_origin.x = 0; - ob->_origin.y = y; - ob->_flags = saveSlotObjectIds[i]; - ob->_shortcut = '0' + i; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->updateGeometry = MNEdit_UpdateGeometry; - ob->drawer = MNEdit_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectSaveSlot; - ob->actions[MNA_ACTIVE].callback = Hu_MenuSaveSlotEdit; - ob->actions[MNA_FOCUSOUT].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = Hu_MenuSaveSlotCommandResponder; - ob->responder = MNEdit_Responder; - ob->_typedata = edit; - ob->data2 = saveSlotObjectIds[i]; - } - saveMenuObjects[i]._type = MN_NONE; - - page = Hu_MenuNewPage("LoadGame", &origin, MPF_LAYOUT_FIXED|MPF_NEVER_SCROLL, Hu_MenuPageTicker, Hu_MenuDrawLoadGamePage, NULL, NULL); - MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); - MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Main")); - page->objects = loadMenuObjects; - - page = Hu_MenuNewPage("SaveGame", &origin, MPF_LAYOUT_FIXED|MPF_NEVER_SCROLL, Hu_MenuPageTicker, Hu_MenuDrawSaveGamePage, NULL, NULL); - MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); - MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Main")); - page->objects = saveMenuObjects; -} - -void Hu_MenuInitOptionsPage(void) -{ -#if __JHERETIC__ || __JHEXEN__ - const Point2Raw origin = { 110, 63 }; -#else - const Point2Raw origin = { 110, 63 }; -#endif - mn_object_t* objects, *ob; -#if __JHERETIC__ || __JHEXEN__ - const uint numObjects = 13; -#else - const uint numObjects = 12; -#endif - mn_page_t* page; - - page = Hu_MenuNewPage("Options", &origin, 0, Hu_MenuPageTicker, Hu_MenuDrawOptionsPage, NULL, NULL); - MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); - MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Main")); - - objects = Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); - if(!objects) Con_Error("Hu_MenuInitOptionsPage: Failed on allocation of %lu bytes for menu objects.", (unsigned long) (sizeof(*objects) * numObjects)); - - ob = objects; - - ob->_type = MN_BUTTON; - ob->_shortcut = 'e'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectEndGame; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = "End Game"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_shortcut = 't'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectControlPanelLink; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = "Show Taskbar"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_shortcut = 'c'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->data1 = "ControlOptions"; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = "Controls"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_shortcut = 'g'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->data1 = "GameplayOptions"; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = "Gameplay"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_shortcut = 's'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->data1 = "SaveOptions"; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = "Game saves"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_shortcut = 'h'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->data1 = "HUDOptions"; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = "HUD"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_shortcut = 'a'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->data1 = "AutomapOptions"; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = "Automap"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_shortcut = 'w'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->data1 = "WeaponOptions"; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = "Weapons"; - } - ob++; - -#if __JHERETIC__ || __JHEXEN__ - ob->_type = MN_BUTTON; - ob->_shortcut = 'i'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->data1 = "InventoryOptions"; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = "Inventory"; - } - ob++; -#endif - - ob->_type = MN_BUTTON; - ob->_shortcut = 's'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->data1 = "SoundOptions"; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = "Sound"; - } - ob++; - - /* - ob->_type = MN_BUTTON; - ob->_shortcut = 'm'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectControlPanelLink; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->data2 = 2; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = "Mouse"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_shortcut = 'j'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectControlPanelLink; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->data2 = 2; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = "Joystick"; - } - ob++;*/ - - ob->_type = MN_NONE; - - page->objects = objects; -} - -void Hu_MenuInitGameplayOptionsPage(void) -{ -#if __JHEXEN__ - const Point2Raw origin = { 88, 25 }; -#elif __JHERETIC__ - const Point2Raw origin = { 30, 40 }; -#else - const Point2Raw origin = { 30, 40 }; -#endif - mn_object_t* objects, *ob; -#if __JDOOM64__ - const uint numObjects = 40; -#elif __JDOOM__ - const uint numObjects = 42; -#elif __JHERETIC__ - const uint numObjects = 24; -#elif __JHEXEN__ - const uint numObjects = 7; -#endif - mn_page_t* page; - - page = Hu_MenuNewPage("GameplayOptions", &origin, 0, Hu_MenuPageTicker, NULL, NULL, NULL); - MNPage_SetTitle(page, "Gameplay Options"); - MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); - MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Options")); - - objects = Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); - if(!objects) Con_Error("Hu_MenuInitGameplayOptionsPage: Failed on allocation of %lu bytes for menu objects.", (unsigned long) (sizeof(*objects) * numObjects)); - - ob = objects; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Always Run"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_shortcut = 'r'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "ctl-run"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Use LookSpring"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_shortcut = 'l'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "ctl-look-spring"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Disable AutoAim"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_shortcut = 'a'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "ctl-aim-noauto"; - } - ob++; - -#if __JDOOM__ || __JHERETIC__ || __JDOOM64__ - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Allow Jumping"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_shortcut = 'j'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "player-jump"; - } - ob++; -#endif - -#if __JDOOM64__ - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Weapon Recoil"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "player-weapon-recoil"; - } - ob++; -#endif - -#if __JDOOM__ || __JHERETIC__ || __JDOOM64__ - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR2; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Compatibility"; - } - ob++; - -# if __JDOOM__ || __JDOOM64__ - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Any Boss Trigger 666"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 1; - ob->_shortcut = 'b'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "game-anybossdeath666"; - } - ob++; - -# if !__JDOOM64__ - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Av Resurrects Ghosts"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 1; - ob->_shortcut = 'g'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "game-raiseghosts"; - } - ob++; - -# if __JDOOM__ - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "VileChase uses Av radius"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 1; - ob->_shortcut = 'g'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "game-vilechase-usevileradius"; - } - ob++; -# endif -# endif // !__JDOOM64__ - - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "PE Limited To 21 Lost Souls"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 1; - ob->_shortcut = 'p'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "game-maxskulls"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "LS Can Get Stuck Inside Walls"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "game-skullsinwalls"; - } - ob++; -# endif // __JDOOM__ || __JDOOM64__ - - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Monsters Fly Over Obstacles"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "game-monsters-floatoverblocking"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Monsters Can Get Stuck In Doors"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 1; - ob->_shortcut = 'd'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "game-monsters-stuckindoors"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Some Objects Never Hang Over Ledges"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 1; - ob->_shortcut = 'h'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "game-objects-neverhangoverledges"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Objects Fall Under Own Weight"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 1; - ob->_shortcut = 'f'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "game-objects-falloff"; - } - ob++; - -#if __JDOOM__ || __JDOOM64__ - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "All Crushed Objects Become A Pile Of Gibs"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 1; - ob->_shortcut = 'g'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "game-objects-gibcrushednonbleeders"; - } - ob++; -#endif - - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Corpses Slide Down Stairs"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 1; - ob->_shortcut = 's'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "game-corpse-sliding"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Use Exactly Doom's Clipping Code"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 1; - ob->_shortcut = 'c'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "game-objects-clipping"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = " ^If Not NorthOnly WallRunning"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 1; - ob->_shortcut = 'w'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "game-player-wallrun-northonly"; - } - ob++; - -# if __JDOOM__ || __JDOOM64__ - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Zombie Players Can Exit Maps"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 1; - ob->_shortcut = 'e'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "game-zombiescanexit"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Fix Ouch Face"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "hud-face-ouchfix"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Fix Weapon Slot Display"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "hud-status-weaponslots-ownedfix"; - } - ob++; -# endif // __JDOOM__ || __JDOOM64__ -#endif // __JDOOM__ || __JHERETIC__ || __JDOOM64__ - - ob->_type = MN_NONE; - - page->objects = objects; -} - -void Hu_MenuInitHUDOptionsPage(void) -{ -#if __JDOOM__ || __JDOOM64__ - const Point2Raw origin = { 97, 40 }; -#else - const Point2Raw origin = { 97, 28 }; -#endif - mn_page_t* page; - - page = Hu_MenuNewPage("HudOptions", &origin, 0, Hu_MenuPageTicker, NULL, NULL, NULL); - MNPage_SetTitle(page, "HUD Options"); - MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); - MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Options")); - page->objects = HudMenuObjects; -} - -void Hu_MenuInitAutomapOptionsPage(void) -{ -#if __JHERETIC__ || __JHEXEN__ - const Point2Raw origin = { 64, 28 }; -#else - const Point2Raw origin = { 70, 40 }; -#endif - mn_object_t* objects, *ob; -#if __JDOOM64__ - const uint numObjects = 26; -#else - const uint numObjects = 27; -#endif - mn_page_t* page; - - page = Hu_MenuNewPage("AutomapOptions", &origin, 0, Hu_MenuPageTicker, NULL, NULL, NULL); - MNPage_SetTitle(page, "Automap Options"); - MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); - MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Options")); - - objects = Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); - if(!objects) Con_Error("Hu_MenuInitAutomapOptionsPage: Failed on allocation of %lu bytes for menu objects.", (unsigned long) (sizeof(*objects) * numObjects)); - - ob = objects; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Background Opacity"; - } - ob++; - - ob->_type = MN_SLIDER; - ob->_shortcut = 'o'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNSlider_Ticker; - ob->updateGeometry = MNSlider_UpdateGeometry; - ob->drawer = MNSlider_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarSlider; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNSlider_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); - { mndata_slider_t* sld = (mndata_slider_t*)ob->_typedata; - sld->min = 0; - sld->max = 1; - sld->value = 0; - sld->step = 0.1f; - sld->floatMode = true; - sld->data1 = "map-opacity"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Line Opacity"; - } - ob++; - - ob->_type = MN_SLIDER; - ob->_shortcut = 'l'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNSlider_Ticker; - ob->updateGeometry = MNSlider_UpdateGeometry; - ob->drawer = MNSlider_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarSlider; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNSlider_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); - { mndata_slider_t* sld = (mndata_slider_t*)ob->_typedata; - sld->min = 0; - sld->max = 1; - sld->value = 0; - sld->step = 0.1f; - sld->floatMode = true; - sld->data1 = "map-line-opacity"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Line Width"; - } - ob++; - - ob->_type = MN_SLIDER; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNSlider_Ticker; - ob->updateGeometry = MNSlider_UpdateGeometry; - ob->drawer = MNSlider_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarSlider; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNSlider_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); - { mndata_slider_t* sld = (mndata_slider_t*)ob->_typedata; - sld->min = .1f; - sld->max = 2; - sld->value = 0; - sld->step = 0.1f; - sld->floatMode = true; - sld->data1 = "map-line-width"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "HUD Display"; - } - ob++; - -#if !__JDOOM64__ - ob->_type = MN_LISTINLINE; - ob->_shortcut = 'h'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNListInline_Ticker; - ob->updateGeometry = MNListInline_UpdateGeometry; - ob->drawer = MNListInline_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarList; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNListInline_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_list_t), PU_GAMESTATIC, 0); - { mndata_list_t* list = (mndata_list_t*)ob->_typedata; - mndata_listitem_t* item; - - list->data = "map-huddisplay"; - list->count = 3; - item = list->items = (mndata_listitem_t*)Z_Calloc(sizeof(mndata_listitem_t) * list->count, PU_GAMESTATIC, 0); - - item->text = "None"; - item->data = 0; - item++; - - item->text = "Current"; - item->data = 1; - item++; - - item->text = "Statusbar"; - item->data = 2; - } - ob++; -#endif - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Door Colors"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_shortcut = 'd'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "map-door-colors"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Door Glow"; - } - ob++; - - ob->_type = MN_SLIDER; - ob->_shortcut = 'g'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNSlider_Ticker; - ob->updateGeometry = MNSlider_UpdateGeometry; - ob->drawer = MNSlider_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarSlider; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNSlider_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); - { mndata_slider_t* sld = (mndata_slider_t*)ob->_typedata; - sld->min = 0; - sld->max = 200; - sld->value = 0; - sld->step = 5; - sld->floatMode = true; - sld->data1 = "map-door-glow"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Use Custom Colors"; - } - ob++; - - ob->_type = MN_LISTINLINE; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNListInline_Ticker; - ob->updateGeometry = MNListInline_UpdateGeometry; - ob->drawer = MNListInline_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarList; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNListInline_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_list_t), PU_GAMESTATIC, 0); - { mndata_list_t* list = (mndata_list_t*)ob->_typedata; - mndata_listitem_t* item; - - list->count = 3; - item = list->items = (mndata_listitem_t*)Z_Calloc(sizeof(mndata_listitem_t) * list->count, PU_GAMESTATIC, 0); - list->data = "map-customcolors"; - - item->text = "Never"; - item->data = 0; - item++; - - item->text = "Auto"; - item->data = 1; - item++; - - item->text = "Always"; - item->data = 2; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Wall"; - } - ob++; - - ob->_type = MN_COLORBOX; - ob->_shortcut = 'w'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNColorBox_Ticker; - ob->updateGeometry = MNColorBox_UpdateGeometry; - ob->drawer = MNColorBox_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarColorBox; - ob->actions[MNA_ACTIVE].callback = Hu_MenuActivateColorWidget; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNColorBox_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_colorbox_t), PU_GAMESTATIC, 0); - { mndata_colorbox_t* cbox = (mndata_colorbox_t*)ob->_typedata; - cbox->data1 = "map-wall-r"; - cbox->data2 = "map-wall-g"; - cbox->data3 = "map-wall-b"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Floor Height Change"; - } - ob++; - - ob->_type = MN_COLORBOX; - ob->_shortcut = 'f'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNColorBox_Ticker; - ob->updateGeometry = MNColorBox_UpdateGeometry; - ob->drawer = MNColorBox_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarColorBox; - ob->actions[MNA_ACTIVE].callback = Hu_MenuActivateColorWidget; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNColorBox_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_colorbox_t), PU_GAMESTATIC, 0); - { mndata_colorbox_t* cbox = (mndata_colorbox_t*)ob->_typedata; - cbox->data1 = "map-wall-floorchange-r"; - cbox->data2 = "map-wall-floorchange-g"; - cbox->data3 = "map-wall-floorchange-b"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Ceiling Height Change"; - } - ob++; - - ob->_type = MN_COLORBOX; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNColorBox_Ticker; - ob->updateGeometry = MNColorBox_UpdateGeometry; - ob->drawer = MNColorBox_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarColorBox; - ob->actions[MNA_ACTIVE].callback = Hu_MenuActivateColorWidget; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNColorBox_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_colorbox_t), PU_GAMESTATIC, 0); - { mndata_colorbox_t* cbox = (mndata_colorbox_t*)ob->_typedata; - cbox->data1 = "map-wall-ceilingchange-r"; - cbox->data2 = "map-wall-ceilingchange-g"; - cbox->data3 = "map-wall-ceilingchange-b"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Unseen"; - } - ob++; - - ob->_type = MN_COLORBOX; - ob->_shortcut = 'u'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNColorBox_Ticker; - ob->updateGeometry = MNColorBox_UpdateGeometry; - ob->drawer = MNColorBox_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarColorBox; - ob->actions[MNA_ACTIVE].callback = Hu_MenuActivateColorWidget; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNColorBox_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_colorbox_t), PU_GAMESTATIC, 0); - { mndata_colorbox_t* cbox = (mndata_colorbox_t*)ob->_typedata; - cbox->data1 = "map-wall-unseen-r"; - cbox->data2 = "map-wall-unseen-g"; - cbox->data3 = "map-wall-unseen-b"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Thing"; - } - ob++; - - ob->_type = MN_COLORBOX; - ob->_shortcut = 't'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNColorBox_Ticker; - ob->updateGeometry = MNColorBox_UpdateGeometry; - ob->drawer = MNColorBox_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarColorBox; - ob->actions[MNA_ACTIVE].callback = Hu_MenuActivateColorWidget; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNColorBox_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_colorbox_t), PU_GAMESTATIC, 0); - { mndata_colorbox_t* cbox = (mndata_colorbox_t*)ob->_typedata; - cbox->data1 = "map-mobj-r"; - cbox->data2 = "map-mobj-g"; - cbox->data3 = "map-mobj-b"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Background"; - } - ob++; - - ob->_type = MN_COLORBOX; - ob->_shortcut = 'b'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNColorBox_Ticker; - ob->updateGeometry = MNColorBox_UpdateGeometry; - ob->drawer = MNColorBox_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarColorBox; - ob->actions[MNA_ACTIVE].callback = Hu_MenuActivateColorWidget; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNColorBox_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_colorbox_t), PU_GAMESTATIC, 0); - { mndata_colorbox_t* cbox = (mndata_colorbox_t*)ob->_typedata; - cbox->data1 = "map-background-r"; - cbox->data2 = "map-background-g"; - cbox->data3 = "map-background-b"; - } - ob++; - - ob->_type = MN_NONE; - - page->objects = objects; -} - -static int compareWeaponPriority(const void* _a, const void* _b) -{ - const mndata_listitem_t* a = (const mndata_listitem_t*)_a; - const mndata_listitem_t* b = (const mndata_listitem_t*)_b; - int i = 0, aIndex = -1, bIndex = -1; - - do - { - if(cfg.weaponOrder[i] == a->data) - aIndex = i; - if(cfg.weaponOrder[i] == b->data) - bIndex = i; - } while(!(aIndex != -1 && bIndex != -1) && ++i < NUM_WEAPON_TYPES); - - if(aIndex > bIndex) return 1; - if(aIndex < bIndex) return -1; - return 0; // Should never happen. -} - -void Hu_MenuInitWeaponsPage(void) -{ -#if __JDOOM__ || __JDOOM64__ - const Point2Raw origin = { 78, 40 }; -#elif __JHERETIC__ - const Point2Raw origin = { 78, 26 }; -#elif __JHEXEN__ - const Point2Raw origin = { 78, 38 }; -#endif - mn_object_t* objects, *ob; -#if __JDOOM__ || __JDOOM64__ - const uint numObjects = 17; -#elif __JHERETIC__ || __JHEXEN__ - const uint numObjects = 15; -#endif - const struct { - const char* text; - int data; - } weaponOrder[NUM_WEAPON_TYPES+1] = { -#if __JDOOM__ || __JDOOM64__ - { (const char*)TXT_WEAPON1, WT_FIRST }, - { (const char*)TXT_WEAPON2, WT_SECOND }, - { (const char*)TXT_WEAPON3, WT_THIRD }, - { (const char*)TXT_WEAPON4, WT_FOURTH }, - { (const char*)TXT_WEAPON5, WT_FIFTH }, - { (const char*)TXT_WEAPON6, WT_SIXTH }, - { (const char*)TXT_WEAPON7, WT_SEVENTH }, - { (const char*)TXT_WEAPON8, WT_EIGHTH }, - { (const char*)TXT_WEAPON9, WT_NINETH }, -# if __JDOOM64__ - { (const char*)TXT_WEAPON10, WT_TENTH }, -# endif -#elif __JHERETIC__ - { (const char*)TXT_TXT_WPNSTAFF, WT_FIRST }, - { (const char*)TXT_TXT_WPNWAND, WT_SECOND }, - { (const char*)TXT_TXT_WPNCROSSBOW, WT_THIRD }, - { (const char*)TXT_TXT_WPNBLASTER, WT_FOURTH }, - { (const char*)TXT_TXT_WPNSKULLROD, WT_FIFTH }, - { (const char*)TXT_TXT_WPNPHOENIXROD, WT_SIXTH }, - { (const char*)TXT_TXT_WPNMACE, WT_SEVENTH }, - { (const char*)TXT_TXT_WPNGAUNTLETS, WT_EIGHTH }, -#elif __JHEXEN__ - /** - * @todo We should allow different weapon preferences per player-class. - */ - { "First", WT_FIRST }, - { "Second", WT_SECOND }, - { "Third", WT_THIRD }, - { "Fourth", WT_FOURTH }, -#endif - { 0 } - }; - mn_page_t* page; - - page = Hu_MenuNewPage("WeaponOptions", &origin, 0, Hu_MenuPageTicker, Hu_MenuDrawWeaponsPage, NULL, NULL); - MNPage_SetTitle(page, "Weapons Options"); - MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); - MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Options")); - - objects = Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); - if(!objects) Con_Error("Hu_MenuInitWeaponOptionsPage: Failed on allocation of %lu bytes for menu objects.", (unsigned long) (sizeof(*objects) * numObjects)); - - ob = objects; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR2; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Priority Order"; - } - ob++; - - ob->_type = MN_LIST; - ob->_flags = MNF_ID0; - ob->_shortcut = 'p'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNList_Ticker; - ob->updateGeometry = MNList_UpdateGeometry; - ob->drawer = MNList_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuChangeWeaponPriority; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNList_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_list_t), PU_GAMESTATIC, 0); - { mndata_list_t* list = (mndata_list_t*)ob->_typedata; - mndata_listitem_t* item; - uint i; - - list->count = NUM_WEAPON_TYPES; - item = list->items = (mndata_listitem_t*)Z_Calloc(sizeof(mndata_listitem_t) * list->count, PU_GAMESTATIC, 0); - - for(i = 0; weaponOrder[i].text; ++i, item++) - { - item->text = weaponOrder[i].text; - item->data = weaponOrder[i].data; - } - qsort(list->items, list->count, sizeof(mndata_listitem_t), compareWeaponPriority); - } - ob++; - - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR2; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Cycling"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Use Priority Order"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 1; - ob->_shortcut = 'o'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "player-weapon-nextmode"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Sequential"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 1; - ob->_shortcut = 's'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "player-weapon-cycle-sequential"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_group = 2; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR2; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Autoswitch"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_group = 2; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Pickup Weapon"; - } - ob++; - - ob->_type = MN_LISTINLINE; - ob->_group = 2; - ob->_shortcut = 'w'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNListInline_Ticker; - ob->updateGeometry = MNListInline_UpdateGeometry; - ob->drawer = MNListInline_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarList; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNListInline_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_list_t), PU_GAMESTATIC, 0); - { mndata_list_t* list = (mndata_list_t*)ob->_typedata; - mndata_listitem_t* item; - - list->count = 3; - item = list->items = (mndata_listitem_t*)Z_Calloc(sizeof(mndata_listitem_t) * list->count, PU_GAMESTATIC, 0); - list->data = "player-autoswitch"; - - item->text = "Never"; - item->data = 0; - item++; - - item->text = "If Better"; - item->data = 1; - item++; - - item->text = "Always"; - item->data = 2; - } - ob++; - - ob->_type = MN_TEXT; - ob->_group = 2; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = " If Not Firing"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 2; - ob->_shortcut = 'f'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "player-autoswitch-notfiring"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_group = 2; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Pickup Ammo"; - } - ob++; - - ob->_type = MN_LISTINLINE; - ob->_group = 2; - ob->_shortcut = 'a'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNListInline_Ticker; - ob->updateGeometry = MNListInline_UpdateGeometry; - ob->drawer = MNListInline_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarList; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNListInline_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_list_t), PU_GAMESTATIC, 0); - { mndata_list_t* list = (mndata_list_t*)ob->_typedata; - mndata_listitem_t* item; - - list->count = 3; - item = list->items = (mndata_listitem_t*)Z_Calloc(sizeof(mndata_listitem_t) * list->count, PU_GAMESTATIC, 0); - list->data = "player-autoswitch-ammo"; - - item->text = "Never"; - item->data = 0; - item++; - - item->text = "If Better"; - item->data = 1; - item++; - - item->text = "Always"; - item->data = 2; - } - ob++; - -#if __JDOOM__ || __JDOOM64__ - ob->_type = MN_TEXT; - ob->_group = 2; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Pickup Beserk"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 2; - ob->_shortcut = 'b'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "player-autoswitch-berserk"; - } - ob++; -#endif - - ob->_type = MN_NONE; - - page->objects = objects; -} - -#if __JHERETIC__ || __JHEXEN__ -void Hu_MenuInitInventoryOptionsPage(void) -{ - const Point2Raw origin = { 78, 48 }; - mn_object_t* objects, *ob; - const uint numObjects = 16; - mn_page_t* page; - - page = Hu_MenuNewPage("InventoryOptions", &origin, 0, Hu_MenuPageTicker, NULL, NULL, NULL); - MNPage_SetTitle(page, "Inventory Options"); - MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); - MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Options")); - - objects = Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); - - if(!objects) Con_Error("Hu_MenuInitInventoryPage: Failed on allocation of %lu bytes for menu objects.", (unsigned long) (sizeof(*objects) * numObjects)); - ob = objects; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Select Mode"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_shortcut = 's'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "ctl-inventory-mode"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Wrap Around"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_shortcut = 'w'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "ctl-inventory-wrap"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Choose And Use"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_shortcut = 'c'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "ctl-inventory-use-immediate"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Select Next If Use Failed"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_shortcut = 'n'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "ctl-inventory-use-next"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "AutoHide"; - } - ob++; - - ob->_type = MN_SLIDER; - ob->_shortcut = 'h'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNSlider_Ticker; - ob->updateGeometry = MNSlider_TextualValueUpdateGeometry; - ob->drawer = MNSlider_TextualValueDrawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarSlider; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNSlider_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); - { mndata_slider_t* sld = (mndata_slider_t*)ob->_typedata; - sld->min = 0; - sld->max = 30; - sld->value = 0; - sld->step = 1.f; - sld->floatMode = true; - sld->data1 = "hud-inventory-timer"; - sld->data2 = "Disabled"; - sld->data4 = " second"; - sld->data5 = " seconds"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR2; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Fullscreen HUD"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Max Visible Slots"; - } - ob++; - - ob->_type = MN_SLIDER; - ob->_group = 1; - ob->_shortcut = 'v'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNSlider_Ticker; - ob->updateGeometry = MNSlider_TextualValueUpdateGeometry; - ob->drawer = MNSlider_TextualValueDrawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarSlider; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNSlider_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); - { mndata_slider_t* sld = (mndata_slider_t*)ob->_typedata; - sld->min = 0; - sld->max = 16; - sld->value = 0; - sld->step = 1; - sld->floatMode = false; - sld->data1 = "hud-inventory-slot-max"; - sld->data2 = "Automatic"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_group = 1; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Show Empty Slots"; - } - ob++; - - ob->_type = MN_BUTTON; - ob->_group = 1; - ob->_shortcut = 'e'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR3; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->staydownMode = true; - btn->data = "hud-inventory-slot-showempty"; - } - ob++; - - ob->_type = MN_NONE; - - page->objects = objects; -} -#endif - -void Hu_MenuInitSoundOptionsPage(void) -{ -#if __JHEXEN__ - const Point2Raw origin = { 97, 25 }; -#elif __JHERETIC__ - const Point2Raw origin = { 97, 30 }; -#elif __JDOOM__ || __JDOOM64__ - const Point2Raw origin = { 97, 40 }; -#endif - mn_object_t* objects, *ob; - const uint numObjects = 6; - mn_page_t* page; - - page = Hu_MenuNewPage("SoundOptions", &origin, 0, Hu_MenuPageTicker, NULL, NULL, NULL); - MNPage_SetTitle(page, "Sound Options"); - MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); - MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Options")); - - objects = Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); - if(!objects) Con_Error("Hu_MenuInitFilesMenu: Failed on allocation of %lu bytes for menu objects.", (unsigned long) (sizeof(*objects) * numObjects)); - - ob = objects; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "SFX Volume"; - } - ob++; - - ob->_type = MN_SLIDER; - ob->_shortcut = 's'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNSlider_Ticker; - ob->updateGeometry = MNSlider_UpdateGeometry; - ob->drawer = MNSlider_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarSlider; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNSlider_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); - { mndata_slider_t* sld = (mndata_slider_t*)ob->_typedata; - sld->min = 0; - sld->max = 255; - sld->value = 0; - sld->step = 5; - sld->floatMode = false; - sld->data1 = "sound-volume"; - } - ob++; - - ob->_type = MN_TEXT; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNText_Ticker; - ob->updateGeometry = MNText_UpdateGeometry; - ob->drawer = MNText_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); - { mndata_text_t* text = (mndata_text_t*)ob->_typedata; - text->text = "Music Volume"; - } - ob++; - - ob->_type = MN_SLIDER; - ob->_shortcut = 'm'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNSlider_Ticker; - ob->updateGeometry = MNSlider_UpdateGeometry; - ob->drawer = MNSlider_Drawer; - ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarSlider; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNSlider_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); - { mndata_slider_t* sld = (mndata_slider_t*)ob->_typedata; - sld->min = 0; - sld->max = 255; - sld->value = 0; - sld->step = 5; - sld->floatMode = false; - sld->data1 = "music-volume"; - } - ob++; - - /* - ob->_type = MN_BUTTON; - ob->_shortcut = 'p'; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->ticker = MNButton_Ticker; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->drawer = MNButton_Drawer; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectControlPanelLink; - ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; - ob->cmdResponder = MNButton_CommandResponder; - ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); - ob->data2 = 1; - { mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - btn->text = "Open Audio Panel"; - } - ob++; - */ - - ob->_type = MN_NONE; - - page->objects = objects; -} - -#if __JDOOM__ || __JHERETIC__ -/** - * Construct the episode selection menu. - */ -void Hu_MenuInitEpisodePage(void) -{ -#if __JDOOM__ - const Point2Raw origin = { 48, 63 }; -#else - const Point2Raw origin = { 80, 50 }; -#endif - int i, y, numEpisodes; - mn_object_t* objects, *ob; - mndata_button_t* buttons, *btn; - mn_page_t* page; - -#if __JDOOM__ - if(gameModeBits & (GM_ANY_DOOM2|GM_DOOM_CHEX)) - numEpisodes = 0; - else if(gameMode == doom_ultimate) - numEpisodes = 4; - else - numEpisodes = 3; -#else // __JHERETIC__ - if(gameMode == heretic_extended) - numEpisodes = 6; - else - numEpisodes = 3; -#endif - - // Allocate the menu objects array. - objects = Z_Calloc(sizeof(mn_object_t) * (numEpisodes+1), PU_GAMESTATIC, 0); - buttons = Z_Calloc(sizeof(mndata_button_t) * (numEpisodes), PU_GAMESTATIC, 0); - - ob = objects; - btn = buttons; - y = 0; - for(i = 0; i < numEpisodes; ++i) - { - ob->_type = MN_BUTTON; - ob->_origin.x = 0; - ob->_origin.y = y; - btn->text = GET_TXT(TXT_EPISODE1 + i); - if(isalnum(btn->text[0])) - ob->_shortcut = tolower(btn->text[0]); -#if __JDOOM__ - btn->patch = &pEpisodeNames[i]; -#endif - ob->_typedata = btn; - ob->ticker = MNButton_Ticker; - ob->drawer = MNButton_Drawer; - ob->cmdResponder = MNButton_CommandResponder; - ob->updateGeometry = MNButton_UpdateGeometry; - - if(i != 0 -#if __JHERETIC__ - && gameMode == heretic_shareware -#else - && gameMode == doom_shareware -#endif - ) - { - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActivateNotSharewareEpisode; - } - else - { - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; - ob->data1 = "Skill"; -#if __JHERETIC__ - if(gameMode == heretic_extended && i == 5) - { - ob->_flags |= MNF_ID0; - } -#endif - } - - ob->actions[MNA_FOCUS].callback = Hu_MenuFocusEpisode; - ob->data2 = i; - ob->_pageFontIdx = MENU_FONT1; - ob++; - btn++; - y += FIXED_LINE_HEIGHT; - } - ob->_type = MN_NONE; - - page = Hu_MenuNewPage("Episode", &origin, MPF_LAYOUT_FIXED|MPF_NEVER_SCROLL, Hu_MenuPageTicker, Hu_MenuDrawEpisodePage, NULL, NULL); - MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTB)); - MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("GameType")); - - page->objects = objects; -} -#endif - -#if __JHEXEN__ -/** - * Construct the player class selection menu. - */ -void Hu_MenuInitPlayerClassPage(void) -{ - const Point2Raw pageOrigin = { 66, 66 }; - mndata_button_t* buttons, *btn; - uint i, n, count; - mn_object_t* objects, *ob; - mn_page_t* page; - int y; - - // First determine the number of selectable player classes. - count = 0; - for(i = 0; i < NUM_PLAYER_CLASSES; ++i) - { - classinfo_t* info = PCLASS_INFO(i); - if(info->userSelectable) - ++count; - } - - // Allocate the menu objects. - objects = Z_Calloc(sizeof(mn_object_t) * (count+4), PU_GAMESTATIC, 0); - buttons = Z_Calloc(sizeof(mndata_button_t) * (count+1), PU_GAMESTATIC, 0); - - // Add the selectable classes. - y = 0; - n = 0; - ob = objects; - btn = buttons; - while(n < count) - { - classinfo_t* info = PCLASS_INFO(n++); - - if(!info->userSelectable) continue; - - ob->_type = MN_BUTTON; - btn->text = info->niceName; - ob->_typedata = btn; - ob->_origin.x = 0; - ob->_origin.y = y; - ob->drawer = MNButton_Drawer; - ob->ticker = MNButton_Ticker; - ob->cmdResponder = MNButton_CommandResponder; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectPlayerClass; - ob->actions[MNA_FOCUS].callback = Hu_MenuFocusOnPlayerClass; - ob->data2 = (int)info->plrClass; - ob->_shortcut = tolower(btn->text[0]); - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob++; - btn++; - y += FIXED_LINE_HEIGHT; - } - - // Random class button. - ob->_type = MN_BUTTON; - btn->text = GET_TXT(TXT_RANDOMPLAYERCLASS); - ob->_typedata = btn; - ob->_origin.x = 0; - ob->_origin.y = y; - ob->drawer = MNButton_Drawer; - ob->ticker = MNButton_Ticker; - ob->cmdResponder = MNButton_CommandResponder; - ob->updateGeometry = MNButton_UpdateGeometry; - ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectPlayerClass; - ob->actions[MNA_FOCUS].callback = Hu_MenuFocusOnPlayerClass; - ob->data2 = (int)PCLASS_NONE; - ob->_shortcut = tolower(btn->text[0]); - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob++; - - // Mobj preview background. - ob->_type = MN_RECT; - ob->_flags = MNF_NO_FOCUS|MNF_ID1; - ob->_origin.x = 108; - ob->_origin.y = -58; - ob->drawer = MNRect_Drawer; - ob->ticker = Hu_MenuPlayerClassBackgroundTicker; - ob->updateGeometry = MNRect_UpdateGeometry; - ob->_pageFontIdx = MENU_FONT1; - ob->_pageColorIdx = MENU_COLOR1; - ob->_typedata = Z_Calloc(sizeof(mndata_rect_t), PU_GAMESTATIC, 0); - ob++; - - // Mobj preview. - ob->_type = MN_MOBJPREVIEW; - ob->_flags = MNF_ID0; - ob->_origin.x = 108 + 55; - ob->_origin.y = -58 + 76; - ob->ticker = Hu_MenuPlayerClassPreviewTicker; - ob->updateGeometry = MNMobjPreview_UpdateGeometry; - ob->drawer = MNMobjPreview_Drawer; - ob->_typedata = Z_Calloc(sizeof(mndata_mobjpreview_t), PU_GAMESTATIC, 0); - ob++; - - // Terminate. - ob->_type = MN_NONE; - - page = Hu_MenuNewPage("PlayerClass", &pageOrigin, MPF_LAYOUT_FIXED|MPF_NEVER_SCROLL, Hu_MenuPageTicker, Hu_MenuDrawPlayerClassPage, NULL, NULL); - MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTB)); - MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("GameType")); - - page->objects = objects; -} -#endif - -mn_page_t* MNPage_New(const Point2Raw* origin, int flags, - void (*ticker) (struct mn_page_s* page), - void (*drawer) (struct mn_page_s* page, const Point2Raw* origin), - int (*cmdResponder) (struct mn_page_s* page, menucommand_e cmd), - void* userData) -{ - mn_page_t* page = (mn_page_t*) malloc(sizeof(*page)); - fontid_t fontId; - int i; - - if(!page) Con_Error("MNPage::New: Failed on allocation of %lu bytes for new MenuPage.", (unsigned long) sizeof(*page)); - - page->origin.x = origin? origin->x : 0; - page->origin.y = origin? origin->y : 0; - - page->flags = flags; - - page->objects = NULL; - page->objectsCount = 0; - - page->ticker = ticker; - page->drawer = drawer; - page->cmdResponder = cmdResponder; - page->previous = NULL; - page->userData = userData; - - Str_Init(&page->title); - - fontId = FID(GF_FONTA); - for(i = 0; i < MENU_FONT_COUNT; ++i) - page->fonts[i] = fontId; - - memset(page->colors, 0, sizeof(page->colors)); - page->colors[0] = 0; - page->colors[1] = 1; - page->colors[2] = 2; - - page->focus = -1; /// @todo Make this a page flag. - page->geometry = Rect_New(); - - return page; -} - -static mn_page_t* addPageToCollection(mn_page_t* page, const char* name) -{ - if(page) - { - pagerecord_t* rec; - - pages = (pagerecord_t*)realloc(pages, sizeof(*pages) * ++pageCount); - if(!pages) Con_Error("addPageToCollection: Failed on (re)allocation of %lu bytes enlarging Pages collection.", (unsigned long) sizeof(*pages) * pageCount); - - rec = &pages[pageCount-1]; - rec->page = page; - Str_Init(&rec->name); Str_Set(&rec->name, name); - } - return page; -} - -mn_page_t* Hu_MenuNewPage(const char* name, const Point2Raw* origin, int flags, - void (*ticker) (struct mn_page_s* page), - void (*drawer) (struct mn_page_s* page, const Point2Raw* origin), - int (*cmdResponder) (struct mn_page_s* page, menucommand_e cmd), - void* userData) -{ - if(!name || !name[0]) - { - DENG_ASSERT(!"Hu_MenuNewPage: Attempt to create page with an invalid name"); - return NULL; - } - - return addPageToCollection(MNPage_New(origin, flags, ticker, drawer, cmdResponder, userData), name); -} - -void Hu_MenuInit(void) -{ - cvarbutton_t* cvb; - - if(inited) return; - - pageCount = 0; - pages = NULL; - - mnAlpha = mnTargetAlpha = 0; - menuActivePage = NULL; - menuActive = false; - cursorHasRotation = false; - cursorAngle = 0; - cursorAnimFrame = 0; - cursorAnimCounter = MENU_CURSOR_TICSPERFRAME; - - DD_Execute(true, "deactivatebcontext menu"); - - Hu_MenuLoadResources(); - - // Set default Yes/No strings. - for(cvb = mnCVarButtons; cvb->cvarname; cvb++) - { - if(!cvb->yes) cvb->yes = "Yes"; - if(!cvb->no) cvb->no = "No"; - } - - initAllPages(); - initAllObjectsOnAllPages(); - -#if __JDOOM__ - if(gameModeBits & GM_ANY_DOOM2) - { - mn_object_t* ob = MN_MustFindObjectOnPage(Hu_MenuFindPageByName("Main"), 0, MNF_ID0); // Read This! - MNObject_SetFlags(ob, FO_SET, MNF_DISABLED|MNF_HIDDEN|MNF_NO_FOCUS); - - ob = MN_MustFindObjectOnPage(Hu_MenuFindPageByName("Main"), 0, MNF_ID1); // Quit Game - MNObject_SetFixedY(ob, MNObject_FixedY(ob) - FIXED_LINE_HEIGHT); - } -#endif - - inited = true; -} - -void Hu_MenuShutdown(void) -{ - if(!inited) return; - - destroyAllPages(); - inited = false; -} - -dd_bool Hu_MenuIsActive(void) -{ - return menuActive; -} - -void Hu_MenuSetAlpha(float alpha) -{ - // The menu's alpha will start moving towards this target value. - mnTargetAlpha = alpha; -} - -float Hu_MenuAlpha(void) -{ - return mnAlpha; -} - -void Hu_MenuTicker(timespan_t ticLength) -{ -#define MENUALPHA_FADE_STEP (.07f) - - float diff = 0; - - // Move towards the target alpha level for the entire menu. - diff = mnTargetAlpha - mnAlpha; - if(fabs(diff) > MENUALPHA_FADE_STEP) - { - mnAlpha += (float)(MENUALPHA_FADE_STEP * ticLength * TICRATE * (diff > 0? 1 : -1)); - } - else - { - mnAlpha = mnTargetAlpha; - } - - if(!menuActive) return; - - // Animate cursor rotation? - if(cfg.menuCursorRotate) - { - if(cursorHasRotation) - { - cursorAngle += (float)(5 * ticLength * TICRATE); - } - else if(cursorAngle != 0) - { - float rewind = (float)(MENU_CURSOR_REWIND_SPEED * ticLength * TICRATE); - if(cursorAngle <= rewind || cursorAngle >= 360 - rewind) - cursorAngle = 0; - else if(cursorAngle < 180) - cursorAngle -= rewind; - else - cursorAngle += rewind; - } - - if(cursorAngle >= 360) - cursorAngle -= 360; - } - - // Time to think? Updates on 35Hz game ticks. - if(!DD_IsSharpTick()) return; - - // Advance menu time. - menuTime++; - - // Animate the cursor graphic? - if(--cursorAnimCounter <= 0) - { - cursorAnimFrame++; - cursorAnimCounter = MENU_CURSOR_TICSPERFRAME; - if(cursorAnimFrame > MENU_CURSOR_FRAMECOUNT-1) - cursorAnimFrame = 0; - } - - // Used for Heretic's rotating skulls. - frame = (menuTime / 3) % 18; - - // Call the active page's ticker. - menuActivePage->ticker(menuActivePage); - -#undef MENUALPHA_FADE_STEP -} - -mn_page_t* Hu_MenuActivePage(void) -{ - return menuActivePage; -} - -void Hu_MenuSetActivePage2(mn_page_t* page, dd_bool canReactivate) -{ - if(!menuActive) return; - if(!page) return; - - if(!(Get(DD_DEDICATED) || Get(DD_NOVIDEO))) - { - FR_ResetTypeinTimer(); - } - - cursorAngle = 0; // Stop cursor rotation animation dead (don't rewind). - menuNominatingQuickSaveSlot = false; - - if(menuActivePage == page) - { - if(!canReactivate) return; - MNPage_ClearFocusObject(page); - } - - updatePageObjects(page); - - // This is now the "active" page. - menuActivePage = page; - MNPage_Initialize(page); -} - -void Hu_MenuSetActivePage(mn_page_t* page) -{ - Hu_MenuSetActivePage2(page, false/*don't reactivate*/); -} - -dd_bool Hu_MenuIsVisible(void) -{ - return (menuActive || mnAlpha > .0001f); -} - -int Hu_MenuDefaultFocusAction(mn_object_t* ob, mn_actionid_t action, void* parameters) -{ - DENG_UNUSED(parameters); - if(MNA_FOCUS != action) return 1; - Hu_MenuUpdateCursorState(); - return 0; -} - -void Hu_MenuDrawFocusCursor(int x, int y, int focusObjectHeight, float alpha) -{ -#if __JDOOM__ || __JDOOM64__ -# define OFFSET_X (-22) -# define OFFSET_Y (-2) -#elif __JHERETIC__ || __JHEXEN__ -# define OFFSET_X (-16) -# define OFFSET_Y (3) -#endif - - const int cursorIdx = cursorAnimFrame; - const float angle = cursorAngle; - patchid_t pCursor = pCursors[cursorIdx % MENU_CURSOR_FRAMECOUNT]; - float scale, pos[2]; - patchinfo_t info; - - if(!R_GetPatchInfo(pCursor, &info)) - return; - - scale = MIN_OF((float) (focusObjectHeight * 1.267f) / info.geometry.size.height, 1); - pos[VX] = x + OFFSET_X * scale; - pos[VY] = y + OFFSET_Y * scale + focusObjectHeight/2; - - DGL_MatrixMode(DGL_MODELVIEW); - DGL_PushMatrix(); - - DGL_Translatef(pos[VX], pos[VY], 0); - DGL_Scalef(scale, scale, 1); - DGL_Rotatef(angle, 0, 0, 1); - - DGL_Enable(DGL_TEXTURE_2D); - DGL_Color4f(1, 1, 1, alpha); - - GL_DrawPatchXY3(pCursor, 0, 0, 0, DPF_NO_OFFSET); - - DGL_Disable(DGL_TEXTURE_2D); - - DGL_MatrixMode(DGL_MODELVIEW); - DGL_PopMatrix(); - -#undef OFFSET_Y -#undef OFFSET_X -} - -void Hu_MenuDrawPageTitle(const char* title, int x, int y) -{ - if(!title || !title[0]) return; - - DGL_Enable(DGL_TEXTURE_2D); - FR_SetFont(FID(GF_FONTB)); - FR_SetColorv(cfg.menuTextColors[0]); - FR_SetAlpha(mnRendState->pageAlpha); - - FR_DrawTextXY3(title, x, y, ALIGN_TOP, MN_MergeMenuEffectWithDrawTextFlags(0)); - - DGL_Disable(DGL_TEXTURE_2D); -} - -void Hu_MenuDrawPageHelp(const char* help, int x, int y) -{ - if(!help || !help[0]) return; - - DGL_Enable(DGL_TEXTURE_2D); - FR_SetFont(FID(GF_FONTA)); - FR_SetColorv(cfg.menuTextColors[1]); - FR_SetAlpha(mnRendState->pageAlpha); - - FR_DrawTextXY3(help, x, y, ALIGN_BOTTOM, MN_MergeMenuEffectWithDrawTextFlags(0)); - - DGL_Disable(DGL_TEXTURE_2D); -} - -static void drawOverlayBackground(float darken) -{ - DGL_SetNoMaterial(); - DGL_DrawRectf2Color(0, 0, SCREENWIDTH, SCREENHEIGHT, 0, 0, 0, darken); -} - -static void beginOverlayDraw(void) -{ -#define SMALL_SCALE .75f - - DGL_MatrixMode(DGL_MODELVIEW); - DGL_PushMatrix(); - - DGL_Translatef(SCREENWIDTH/2, SCREENHEIGHT/2, 0); - DGL_Scalef(SMALL_SCALE, SMALL_SCALE, 1); - DGL_Translatef(-(SCREENWIDTH/2), -(SCREENHEIGHT/2), 0); - -#undef SMALL_SCALE -} - -static void endOverlayDraw(void) -{ - DGL_MatrixMode(DGL_MODELVIEW); - DGL_PopMatrix(); -} - -void Hu_MenuDrawer(void) -{ -#define OVERLAY_DARKEN .7f - - dgl_borderedprojectionstate_t bp; - dd_bool showFocusCursor = true; - mn_object_t* focusObj; - - if(!Hu_MenuIsVisible()) return; - - GL_ConfigureBorderedProjection(&bp, 0, SCREENWIDTH, SCREENHEIGHT, - Get(DD_WINDOW_WIDTH), Get(DD_WINDOW_HEIGHT), cfg.menuScaleMode); - GL_BeginBorderedProjection(&bp); - - // First determine whether the focus cursor should be visible. - focusObj = MNPage_FocusObject(Hu_MenuActivePage()); - if(focusObj && (MNObject_Flags(focusObj) & MNF_ACTIVE)) - { - if(MNObject_Type(focusObj) == MN_COLORBOX || MNObject_Type(focusObj) == MN_BINDINGS) - { - showFocusCursor = false; - } - } - - DGL_MatrixMode(DGL_MODELVIEW); - DGL_PushMatrix(); - - DGL_Translatef(SCREENWIDTH/2, SCREENHEIGHT/2, 0); - DGL_Scalef(cfg.menuScale, cfg.menuScale, 1); - DGL_Translatef(-(SCREENWIDTH/2), -(SCREENHEIGHT/2), 0); - - MN_DrawPage(Hu_MenuActivePage(), mnAlpha, showFocusCursor); - - DGL_MatrixMode(DGL_MODELVIEW); - DGL_PopMatrix(); - - GL_EndBorderedProjection(&bp); - - // Drawing any overlays? - if(focusObj && (MNObject_Flags(focusObj) & MNF_ACTIVE)) - { - switch(MNObject_Type(focusObj)) - { - case MN_COLORBOX: - case MN_BINDINGS: - drawOverlayBackground(OVERLAY_DARKEN); - GL_BeginBorderedProjection(&bp); - - beginOverlayDraw(); - if(MNObject_Type(focusObj) == MN_BINDINGS) - { - Hu_MenuControlGrabDrawer(MNBindings_ControlName(focusObj), 1); - } - else - { - MN_DrawPage(Hu_MenuFindPageByName("ColorWidget"), 1, true); - } - endOverlayDraw(); - - GL_EndBorderedProjection(&bp); - break; - default: break; - } - } - -#undef OVERLAY_DARKEN -} - -void Hu_MenuPageTicker(mn_page_t* page) -{ - // Normal ticker actions first. - MNPage_Ticker(page); - - /// @todo Move game-menu specific page tick functionality here. -} - -void Hu_MenuNavigatePage(mn_page_t* page, int pageDelta) -{ -#if 0 - int index; - assert(page); - - oldIndex = index = MAX_OF(0, page->focus); - - if(pageDelta < 0) - { - index = MAX_OF(0, index - page->numVisObjects); - } - else - { - index = MIN_OF(page->objectsCount-1, index + page->numVisObjects); - } - - // Don't land on empty objects. - while((page->objects[index].flags & (MNF_DISABLED | MNF_NO_FOCUS)) && (index > 0)) - index--; - while((page->objects[index].flags & (MNF_DISABLED | MNF_NO_FOCUS)) && index < page->objectsCount) - index++; - - if(index != oldIndex) - { - S_LocalSound(SFX_MENU_NAV_RIGHT, NULL); - MNPage_SetFocus(page, page->objects + index); - } -#endif -} - -static void initPageObjects(mn_page_t* page) -{ - mn_object_t* ob; - assert(page); - - page->objectsCount = 0; - - for(ob = page->objects; MNObject_Type(ob) != MN_NONE; ob++) - { - page->objectsCount += 1; - - ob->_page = page; - ob->_geometry = Rect_New(); - - ob->timer = 0; - MNObject_SetFlags(ob, FO_CLEAR, MNF_FOCUS); - - if(0 != ob->_shortcut) - { - int shortcut = ob->_shortcut; - ob->_shortcut = 0; // Clear invalid defaults. - MNObject_SetShortcut(ob, shortcut); - } - - switch(MNObject_Type(ob)) - { - case MN_TEXT: { - mndata_text_t* txt = (mndata_text_t*)ob->_typedata; - MNObject_SetFlags(ob, FO_SET, MNF_NO_FOCUS); - - if(txt->text && (PTR2INT(txt->text) > 0 && PTR2INT(txt->text) < NUMTEXT)) - { - txt->text = GET_TXT(PTR2INT(txt->text)); - } - break; } - - case MN_BUTTON: { - const mn_actioninfo_t* action = MNObject_Action(ob, MNA_MODIFIED); - mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - - if(btn->text && (PTR2INT(btn->text) > 0 && PTR2INT(btn->text) < NUMTEXT)) - { - btn->text = GET_TXT(PTR2INT(btn->text)); - /// @todo Should not be done here. - MNObject_SetShortcut(ob, btn->text[0]); - } - break; } - - case MN_EDIT: { - mndata_edit_t* edit = (mndata_edit_t*) ob->_typedata; - - if(edit->emptyString && (PTR2INT(edit->emptyString) > 0 && PTR2INT(edit->emptyString) < NUMTEXT)) - { - edit->emptyString = GET_TXT(PTR2INT(edit->emptyString)); - } - break; } - - case MN_LIST: - case MN_LISTINLINE: { - mndata_list_t* list = (mndata_list_t*) ob->_typedata; - int i; - - for(i = 0; i < list->count; ++i) - { - mndata_listitem_t* item = &((mndata_listitem_t*)list->items)[i]; - if(item->text && (PTR2INT(item->text) > 0 && PTR2INT(item->text) < NUMTEXT)) - { - item->text = GET_TXT(PTR2INT(item->text)); - } - } - break; } - - case MN_COLORBOX: { - mndata_colorbox_t* cbox = (mndata_colorbox_t*) ob->_typedata; - - if(!cbox->rgbaMode) - cbox->a = 1.f; - if(0 >= cbox->width) - cbox->width = MNDATA_COLORBOX_WIDTH; - if(0 >= cbox->height) - cbox->height = MNDATA_COLORBOX_HEIGHT; - break; } - - case MN_MOBJPREVIEW: - MNObject_SetFlags(ob, FO_SET, MNF_NO_FOCUS); - break; - - default: break; - } - } -} - -/** - * Main task is to update objects linked to cvars. - */ -static void updatePageObjects(mn_page_t* page) -{ - mn_object_t* ob; - assert(page); - - for(ob = page->objects; MNObject_Type(ob) != MN_NONE; ob++) - { - switch(MNObject_Type(ob)) - { - case MN_TEXT: - case MN_MOBJPREVIEW: - MNObject_SetFlags(ob, FO_SET, MNF_NO_FOCUS); - break; - - case MN_BUTTON: { - const mn_actioninfo_t* action = MNObject_Action(ob, MNA_MODIFIED); - mndata_button_t* btn = (mndata_button_t*)ob->_typedata; - - if(action && action->callback == Hu_MenuCvarButton) - { - cvarbutton_t* cvb; - if(ob->data1) - { - // This button has already been initialized. - cvb = (cvarbutton_t*) ob->data1; - cvb->active = (Con_GetByte(cvb->cvarname) & (cvb->mask? cvb->mask : ~0)) != 0; - //strcpy(obj->text, cvb->active ? cvb->yes : cvb->no); - btn->text = cvb->active ? cvb->yes : cvb->no; - continue; - } - - // Find the cvarbutton representing this one. - for(cvb = mnCVarButtons; cvb->cvarname; cvb++) - { - if(!strcmp(btn->data, cvb->cvarname) && ob->data2 == cvb->mask) - { - cvb->active = (Con_GetByte(cvb->cvarname) & (cvb->mask? cvb->mask : ~0)) != 0; - ob->data1 = (void*) cvb; - btn->yes = cvb->yes; - btn->no = cvb->no; - btn->text = (cvb->active ? btn->yes : btn->no); - break; - } - } - cvb = NULL; - } - break; } - - case MN_LIST: - case MN_LISTINLINE: { - const mn_actioninfo_t* action = MNObject_Action(ob, MNA_MODIFIED); - mndata_list_t* list = (mndata_list_t*) ob->_typedata; - - if(action && action->callback == Hu_MenuCvarList) - { - MNList_SelectItemByValue(ob, MNLIST_SIF_NO_ACTION, Con_GetInteger(list->data)); - } - break; } - - case MN_EDIT: { - const mn_actioninfo_t* action = MNObject_Action(ob, MNA_MODIFIED); - mndata_edit_t* edit = (mndata_edit_t*) ob->_typedata; - - if(action && action->callback == Hu_MenuCvarEdit) - { - MNEdit_SetText(ob, MNEDIT_STF_NO_ACTION, Con_GetString(edit->data1)); - } - break; } - - case MN_SLIDER: { - const mn_actioninfo_t* action = MNObject_Action(ob, MNA_MODIFIED); - mndata_slider_t* sldr = (mndata_slider_t*) ob->_typedata; - if(action && action->callback == Hu_MenuCvarSlider) - { - float value; - if(sldr->floatMode) - value = Con_GetFloat(sldr->data1); - else - value = Con_GetInteger(sldr->data1); - MNSlider_SetValue(ob, MNSLIDER_SVF_NO_ACTION, value); - } - break; } - - case MN_COLORBOX: { - mndata_colorbox_t* cbox = (mndata_colorbox_t*) ob->_typedata; - const mn_actioninfo_t* action = MNObject_Action(ob, MNA_MODIFIED); - - if(action && action->callback == Hu_MenuCvarColorBox) - { - float rgba[4]; - rgba[CR] = Con_GetFloat(cbox->data1); - rgba[CG] = Con_GetFloat(cbox->data2); - rgba[CB] = Con_GetFloat(cbox->data3); - rgba[CA] = (cbox->rgbaMode? Con_GetFloat(cbox->data4) : 1.f); - MNColorBox_SetColor4fv(ob, MNCOLORBOX_SCF_NO_ACTION, rgba); - } - break; } - - default: break; - } - } -} - -static void destroyPageObjects(mn_page_t* page) -{ - mn_object_t* obj; - if(!page) return; - for(obj = page->objects; MNObject_Type(obj) != MN_NONE; obj++) - { - if(obj->_geometry) - { - Rect_Delete(obj->_geometry); - obj->_geometry = NULL; - } - } -} - -static void destroyPage(mn_page_t* page) -{ - if(!page) return; - - destroyPageObjects(page); - - Str_Free(&page->title); - - if(page->geometry) - { - Rect_Delete(page->geometry); - page->geometry = NULL; - } - - free(page); -} - -static void initAllPages(void) -{ - Hu_MenuInitColorWidgetPage(); - Hu_MenuInitMainPage(); - Hu_MenuInitGameTypePage(); -#if __JDOOM__ || __JHERETIC__ - Hu_MenuInitEpisodePage(); -#endif -#if __JHEXEN__ - Hu_MenuInitPlayerClassPage(); -#endif - Hu_MenuInitSkillPage(); - Hu_MenuInitMultiplayerPage(); - Hu_MenuInitPlayerSetupPage(); -#if __JHERETIC__ || __JHEXEN__ - Hu_MenuInitFilesPage(); -#endif - Hu_MenuInitLoadGameAndSaveGamePages(); - Hu_MenuInitOptionsPage(); - Hu_MenuInitGameplayOptionsPage(); - Hu_MenuInitSaveOptionsPage(); - Hu_MenuInitHUDOptionsPage(); - Hu_MenuInitAutomapOptionsPage(); - Hu_MenuInitWeaponsPage(); -#if __JHERETIC__ || __JHEXEN__ - Hu_MenuInitInventoryOptionsPage(); -#endif - Hu_MenuInitSoundOptionsPage(); - Hu_MenuInitControlsPage(); -} - -static void destroyAllPages(void) -{ - int i; - if(!pages) return; - for(i = 0; i < pageCount; ++i) - { - pagerecord_t* rec = pages + i; - destroyPage(rec->page); - Str_Free(&rec->name); - } - free(pages); -} - -static void initAllObjectsOnAllPages(void) -{ - int i; - for(i = 0; i < pageCount; ++i) - { - pagerecord_t* rec = pages + i; - initPageObjects(rec->page); - } -} - -int Hu_MenuColorWidgetCmdResponder(mn_page_t* page, menucommand_e cmd) -{ - assert(page); - switch(cmd) - { - case MCMD_NAV_OUT: { - mn_object_t* obj = (mn_object_t*)page->userData; - MNObject_SetFlags(obj, FO_CLEAR, MNF_ACTIVE); - S_LocalSound(SFX_MENU_CANCEL, NULL); - colorWidgetActive = false; - - /// @kludge We should re-focus on the object instead. - cursorAngle = 0; // Stop cursor rotation animation dead (don't rewind). - Hu_MenuUpdateCursorState(); - /// kludge end. - return true; - } - case MCMD_NAV_PAGEUP: - case MCMD_NAV_PAGEDOWN: - return true; // Eat these. - case MCMD_SELECT: { - mn_object_t* obj = (mn_object_t*)page->userData; - MNObject_SetFlags(obj, FO_CLEAR, MNF_ACTIVE); - S_LocalSound(SFX_MENU_ACCEPT, NULL); - colorWidgetActive = false; - MNColorBox_CopyColor(obj, 0, MN_MustFindObjectOnPage(page, 0, MNF_ID0)); - - /// @kludge We should re-focus on the object instead. - cursorAngle = 0; // Stop cursor rotation animation dead (don't rewind). - Hu_MenuUpdateCursorState(); - /// kludge end. - return true; - } - default: - break; - } - return false; -} - -static void fallbackCommandResponder(mn_page_t* page, menucommand_e cmd) -{ - assert(page); - switch(cmd) - { - case MCMD_NAV_PAGEUP: - case MCMD_NAV_PAGEDOWN: - S_LocalSound(cmd == MCMD_NAV_PAGEUP? SFX_MENU_NAV_UP : SFX_MENU_NAV_DOWN, NULL); - Hu_MenuNavigatePage(page, cmd == MCMD_NAV_PAGEUP? -1 : +1); - break; - - case MCMD_NAV_UP: - case MCMD_NAV_DOWN: { - mn_object_t* obj = MNPage_FocusObject(page); - // An object on this page must have focus in order to navigate. - if(obj) - { - int i = 0, giveFocus = page->focus; - do - { - giveFocus += (cmd == MCMD_NAV_UP? -1 : 1); - if(giveFocus < 0) - giveFocus = page->objectsCount - 1; - else if(giveFocus >= page->objectsCount) - giveFocus = 0; - } while(++i < page->objectsCount && (MNObject_Flags(page->objects + giveFocus) & (MNF_DISABLED | MNF_NO_FOCUS | MNF_HIDDEN))); - - if(giveFocus != page->focus) - { - S_LocalSound(cmd == MCMD_NAV_UP? SFX_MENU_NAV_UP : SFX_MENU_NAV_DOWN, NULL); - MNPage_SetFocus(page, page->objects + giveFocus); - } - } - break; - } - case MCMD_NAV_OUT: - if(!page->previous) - { - S_LocalSound(SFX_MENU_CLOSE, NULL); - Hu_MenuCommand(MCMD_CLOSE); - } - else - { - S_LocalSound(SFX_MENU_CANCEL, NULL); - Hu_MenuSetActivePage(page->previous); - } - break; - default: -// DEBUG_Message("Warning: fallbackCommandResponder: Command %i not processed, ignoring.\n", (int) cmd); - break; - } -} - -/// Depending on the current menu state some commands require translating. -static menucommand_e translateCommand(menucommand_e cmd) -{ - // If a close command is received while currently working with a selected - // "active" widget - interpret the command instead as "navigate out". - if(menuActive && (cmd == MCMD_CLOSE || cmd == MCMD_CLOSEFAST)) - { - mn_object_t* obj = MNPage_FocusObject(Hu_MenuActivePage()); - if(obj) - { - switch(MNObject_Type(obj)) - { - case MN_EDIT: - case MN_LIST: - case MN_COLORBOX: - if(MNObject_Flags(obj) & MNF_ACTIVE) - { - cmd = MCMD_NAV_OUT; - } - break; - default: - break; - } - } - } - return cmd; -} - -void Hu_MenuCommand(menucommand_e cmd) -{ - mn_page_t* page; - mn_object_t* obj; - - cmd = translateCommand(cmd); - - // Determine the page which will respond to this command. - if(colorWidgetActive) - page = Hu_MenuFindPageByName("ColorWidget"); - else - page = Hu_MenuActivePage(); - - if(cmd == MCMD_CLOSE || cmd == MCMD_CLOSEFAST) - { - if(menuActive) - { - //BusyMode_FreezeGameForBusyMode(); - - menuNominatingQuickSaveSlot = false; - - Hu_FogEffectSetAlphaTarget(0); - - if(cmd == MCMD_CLOSEFAST) - { // Hide the menu instantly. - mnAlpha = mnTargetAlpha = 0; - } - else - { - mnTargetAlpha = 0; - } - - if(cmd != MCMD_CLOSEFAST) - S_LocalSound(SFX_MENU_CLOSE, NULL); - - menuActive = false; - - // Disable the menu binding context. - DD_Execute(true, "deactivatebcontext menu"); - } - return; - } - - // No other commands are responded to once shutdown has begun. - if(G_QuitInProgress()) - { - return; - } - - if(!menuActive) - { - if(MCMD_OPEN == cmd) - { - // If anyone is currently chatting; the menu cannot be opened. - int i; - for(i = 0; i < MAXPLAYERS; ++i) - { - if(ST_ChatIsActive(i)) - return; - } - - S_LocalSound(SFX_MENU_OPEN, NULL); - - //Con_Open(false); - - Hu_FogEffectSetAlphaTarget(1); - Hu_MenuSetAlpha(1); - menuActive = true; - menuTime = 0; - - menuActivePage = NULL; // Always re-activate this page. - Hu_MenuSetActivePage(Hu_MenuFindPageByName("Main")); - - // Enable the menu binding class - DD_Execute(true, "activatebcontext menu"); - B_SetContextFallback("menu", Hu_MenuFallbackResponder); - } - return; - } - - // Try the current focus object. - obj = MNPage_FocusObject(page); - if(obj && obj->cmdResponder) - { - if(obj->cmdResponder(obj, cmd)) - return; - } - - // Try the page's cmd responder. - if(page->cmdResponder) - { - if(page->cmdResponder(page, cmd)) - return; - } - - fallbackCommandResponder(page, cmd); -} - -int Hu_MenuPrivilegedResponder(event_t* ev) -{ - if(Hu_MenuIsActive()) - { - mn_object_t* obj = MNPage_FocusObject(Hu_MenuActivePage()); - if(obj && !(MNObject_Flags(obj) & MNF_DISABLED)) - { - if(obj->privilegedResponder) - { - return obj->privilegedResponder(obj, ev); - } - } - } - return false; -} - -int Hu_MenuResponder(event_t* ev) -{ - if(Hu_MenuIsActive()) - { - mn_object_t* obj = MNPage_FocusObject(Hu_MenuActivePage()); - if(obj && !(MNObject_Flags(obj) & MNF_DISABLED)) - { - if(obj->responder) - { - return obj->responder(obj, ev); - } - } - } - return false; // Not eaten. -} - -int Hu_MenuFallbackResponder(event_t* ev) -{ - mn_page_t* page = Hu_MenuActivePage(); - - if(!Hu_MenuIsActive() || !page) return false; - - if(cfg.menuShortcutsEnabled) - { - if(ev->type == EV_KEY && (ev->state == EVS_DOWN || ev->state == EVS_REPEAT)) - { - int i; - for(i = 0; i < page->objectsCount; ++i) - { - mn_object_t* obj = &page->objects[i]; - if(MNObject_Flags(obj) & (MNF_DISABLED | MNF_NO_FOCUS | MNF_HIDDEN)) - continue; - - if(MNObject_Shortcut(obj) == ev->data1) - { - MNPage_SetFocus(page, obj); - return true; - } - } - } - } - return false; -} - -/** - * User wants to load this game - */ -int Hu_MenuSelectLoadSlot(mn_object_t* obj, mn_actionid_t action, void* parameters) -{ - mndata_edit_t* edit = (mndata_edit_t*)obj->_typedata; - const int saveSlot = edit->data2; - mn_page_t* saveGamePage; - - DENG_UNUSED(parameters); - - if(MNA_ACTIVEOUT != action) return 1; - - saveGamePage = Hu_MenuFindPageByName("SaveGame"); - MNPage_SetFocus(saveGamePage, MNPage_FindObject(saveGamePage, 0, obj->data2)); - - G_LoadGame(saveSlot); - Hu_MenuCommand(chooseCloseMethod()); - return 0; -} - -#if __JHERETIC__ || __JHEXEN__ -void Hu_MenuDrawMainPage(mn_page_t* page, const Point2Raw* origin) -{ -#define TITLEOFFSET_X (-22) -#define TITLEOFFSET_Y (-56) - -#if __JHEXEN__ - int frame = (menuTime / 5) % 7; -#endif - - DGL_Enable(DGL_TEXTURE_2D); - DGL_Color4f(1, 1, 1, mnRendState->pageAlpha); - FR_SetFont(FID(GF_FONTB)); - FR_SetColorAndAlpha(1, 1, 1, mnRendState->pageAlpha); - - WI_DrawPatchXY3(pMainTitle, Hu_ChoosePatchReplacement(cfg.menuPatchReplaceMode, pMainTitle), - origin->x + TITLEOFFSET_X, origin->y + TITLEOFFSET_Y, ALIGN_TOPLEFT, 0, MN_MergeMenuEffectWithDrawTextFlags(0)); -#if __JHEXEN__ - GL_DrawPatchXY(pBullWithFire[(frame + 2) % 7], origin->x - 73, origin->y + 24); - GL_DrawPatchXY(pBullWithFire[frame], origin->x + 168, origin->y + 24); -#elif __JHERETIC__ - GL_DrawPatchXY(pRotatingSkull[17 - frame], origin->x - 70, origin->y - 46); - GL_DrawPatchXY(pRotatingSkull[frame], origin->x + 122, origin->y - 46); -#endif - - DGL_Disable(DGL_TEXTURE_2D); - -#undef TITLEOFFSET_Y -#undef TITLEOFFSET_X -} -#endif - -void Hu_MenuDrawGameTypePage(mn_page_t* page, const Point2Raw* origin) -{ - Hu_MenuDrawPageTitle(GET_TXT(TXT_PICKGAMETYPE), SCREENWIDTH/2, origin->y - 28); -} - -#if __JHERETIC__ -static void composeNotDesignedForMessage(const char* str) -{ - char* buf = notDesignedForMessage, *in, tmp[2]; - - buf[0] = 0; - tmp[1] = 0; - - // Get the message template. - in = GET_TXT(TXT_NOTDESIGNEDFOR); - - for(; *in; in++) - { - if(in[0] == '%') - { - if(in[1] == '1') - { - strcat(buf, str); - in++; - continue; - } - - if(in[1] == '%') - in++; - } - tmp[0] = *in; - strcat(buf, tmp); - } -} -#endif - -#if __JHEXEN__ -/** - * A specialization of MNRect_Ticker() which implements the animation logic - * for the player class selection page's player visual background. - */ -void Hu_MenuPlayerClassBackgroundTicker(mn_object_t* ob) -{ - mn_object_t* mop; - assert(ob); - - // Determine our selection according to the current focus object. - /// @todo Do not search for the focus object, flag the "random" - /// state through a focus action. - mop = MNPage_FocusObject(MNObject_Page(ob)); - if(mop) - { - playerclass_t pClass = (playerclass_t) mop->data2; - if(pClass == PCLASS_NONE) - { - // Random class. - /// @todo Use this object's timer instead of menuTime. - pClass = (menuTime / 5); - } - - /// @todo Only change here if in the "random" state. - pClass %= 3; // Number of user-selectable classes. - - MNRect_SetBackgroundPatch(ob, pPlayerClassBG[pClass]); - } - - // Call MNRect's ticker now we've done our own processing. - MNRect_Ticker(ob); -} - -/** - * A specialization of MNMobjPreview_Ticker() which implements the animation - * logic for the player class selection page's player visual. - */ -void Hu_MenuPlayerClassPreviewTicker(mn_object_t* ob) -{ - mn_object_t* mop; - assert(ob); - - // Determine our selection according to the current focus object. - /// @todo Do not search for the focus object, flag the "random" - /// state through a focus action. - mop = MNPage_FocusObject(MNObject_Page(ob)); - if(mop) - { - playerclass_t pClass = (playerclass_t) mop->data2; - if(pClass == PCLASS_NONE) - { - // Random class. - /// @todo Use this object's timer instead of menuTime. - pClass = PCLASS_FIRST + (menuTime / 5); - pClass %= 3; // Number of user-selectable classes. - - MNMobjPreview_SetPlayerClass(ob, pClass); - MNMobjPreview_SetMobjType(ob, PCLASS_INFO(pClass)->mobjType); - } - - // Fighter is Yellow, others Red by default. - MNMobjPreview_SetTranslationClass(ob, pClass); - MNMobjPreview_SetTranslationMap(ob, pClass == PCLASS_FIGHTER? 2 : 0); - } - - // Call MNMobjPreview's ticker now we've done our own processing. - MNMobjPreview_Ticker(ob); -} - -void Hu_MenuDrawPlayerClassPage(mn_page_t* page, const Point2Raw* origin) -{ - DGL_Enable(DGL_TEXTURE_2D); - FR_SetFont(FID(GF_FONTB)); - FR_SetColorAndAlpha(cfg.menuTextColors[0][CR], cfg.menuTextColors[0][CG], cfg.menuTextColors[0][CB], mnRendState->pageAlpha); - - FR_DrawTextXY3("Choose class:", origin->x - 32, origin->y - 42, ALIGN_TOPLEFT, - MN_MergeMenuEffectWithDrawTextFlags(0)); - - DGL_Disable(DGL_TEXTURE_2D); -} -#endif - -#if __JDOOM__ || __JHERETIC__ -void Hu_MenuDrawEpisodePage(mn_page_t* page, const Point2Raw* origin) -{ -#if __JHERETIC__ - // Inform the user episode 6 is designed for deathmatch only. - mn_object_t* obj = MNPage_FindObject(page, 0, MNF_ID0); - if(obj && obj == MNPage_FocusObject(page)) - { - const char* str = notDesignedForMessage; - Point2Raw origin; - - composeNotDesignedForMessage(GET_TXT(TXT_SINGLEPLAYER)); - - origin.x = SCREENWIDTH/2; - origin.y = (SCREENHEIGHT/2) + ((SCREENHEIGHT/2-5)/cfg.menuScale); - - Hu_MenuDrawPageHelp(notDesignedForMessage, origin.x, origin.y); - } -#else // __JDOOM__ - DGL_Enable(DGL_TEXTURE_2D); - DGL_Color4f(1, 1, 1, mnRendState->pageAlpha); - - FR_SetFont(FID(GF_FONTB)); - FR_SetColorv(cfg.menuTextColors[0]); - FR_SetAlpha(mnRendState->pageAlpha); - - WI_DrawPatchXY3(pEpisode, Hu_ChoosePatchReplacement(cfg.menuPatchReplaceMode, pEpisode), - origin->x + 7, origin->y - 25, ALIGN_TOPLEFT, 0, MN_MergeMenuEffectWithDrawTextFlags(0)); - - DGL_Disable(DGL_TEXTURE_2D); -#endif -} -#endif - -void Hu_MenuDrawSkillPage(mn_page_t* page, const Point2Raw* origin) -{ -#if __JDOOM__ || __JDOOM64__ - DGL_Enable(DGL_TEXTURE_2D); - DGL_Color4f(1, 1, 1, mnRendState->pageAlpha); - FR_SetFont(FID(GF_FONTB)); - FR_SetColorAndAlpha(cfg.menuTextColors[0][CR], cfg.menuTextColors[0][CG], cfg.menuTextColors[0][CB], mnRendState->pageAlpha); - - WI_DrawPatchXY3(pNewGame, Hu_ChoosePatchReplacement(cfg.menuPatchReplaceMode, pNewGame), - origin->x + 48, origin->y - 49, ALIGN_TOPLEFT, 0, MN_MergeMenuEffectWithDrawTextFlags(0)); - WI_DrawPatchXY3(pSkill, Hu_ChoosePatchReplacement(cfg.menuPatchReplaceMode, pSkill), - origin->x + 6, origin->y - 25, ALIGN_TOPLEFT, 0, MN_MergeMenuEffectWithDrawTextFlags(0)); - - DGL_Disable(DGL_TEXTURE_2D); -#elif __JHEXEN__ - Hu_MenuDrawPageTitle("Choose Skill Level:", origin->x + 36, origin->y - 28); -#endif -} - -void Hu_MenuUpdateGameSaveWidgets(void) -{ - int const saveSlotObjectIds[NUMSAVESLOTS] = { - MNF_ID0, MNF_ID1, MNF_ID2, MNF_ID3, MNF_ID4, MNF_ID5, -#if !__JHEXEN__ - MNF_ID6, MNF_ID7 -#endif - }; - mn_page_t *page; - int i; - - if(!menuActive) return; - - // Prompt a refresh of the game-save info. We don't yet actively monitor - // the contents of the game-save paths, so instead we settle for manual - // updates whenever the save/load menu is opened. - SaveSlots_UpdateAllSaveInfo(saveSlots); - - // Update widgets. - page = Hu_MenuFindPageByName("LoadGame"); - for(i = 0; i < NUMSAVESLOTS; ++i) - { - mn_object_t *obj = MN_MustFindObjectOnPage(page, 0, saveSlotObjectIds[i]); - mndata_edit_t *edit = (mndata_edit_t *) obj->_typedata; - char const *text = ""; - - MNObject_SetFlags(obj, FO_SET, MNF_DISABLED); - if(SaveSlots_SlotInUse(saveSlots, edit->data2)) - { - SaveInfo *info = SaveSlots_SaveInfo(saveSlots, edit->data2); - text = Str_Text(SaveInfo_Description(info)); - MNObject_SetFlags(obj, FO_CLEAR, MNF_DISABLED); - } - MNEdit_SetText(obj, MNEDIT_STF_NO_ACTION, text); - } -} - -/** - * Called after the save name has been modified and to action the game-save. - */ -int Hu_MenuSelectSaveSlot(mn_object_t* ob, mn_actionid_t action, void* parameters) -{ - mndata_edit_t* edit = (mndata_edit_t*)ob->_typedata; - const int saveSlot = edit->data2; - mn_page_t* page; - - DENG_UNUSED(parameters); - - if(MNA_ACTIVEOUT != action) return 1; - - if(menuNominatingQuickSaveSlot) - { - Con_SetInteger("game-save-quick-slot", saveSlot); - menuNominatingQuickSaveSlot = false; - } - - if(!G_SaveGame2(saveSlot, Str_Text(MNEdit_Text(ob)))) return 0; - - page = Hu_MenuFindPageByName("SaveGame"); - MNPage_SetFocus(page, MN_MustFindObjectOnPage(page, 0, ob->data2)); - - page = Hu_MenuFindPageByName("LoadGame"); - MNPage_SetFocus(page, MN_MustFindObjectOnPage(page, 0, ob->data2)); - - Hu_MenuCommand(chooseCloseMethod()); - return 0; -} - -int Hu_MenuCvarButton(mn_object_t* obj, mn_actionid_t action, void* parameters) -{ - mndata_button_t* btn = (mndata_button_t*)obj->_typedata; - const cvarbutton_t* cb = obj->data1; - cvartype_t varType = Con_GetVariableType(cb->cvarname); - int value; - - DENG_UNUSED(parameters); - if(MNA_MODIFIED != action) return 1; - - //strcpy(btn->text, cb->active? cb->yes : cb->no); - btn->text = cb->active? cb->yes : cb->no; - - if(CVT_NULL == varType) return 0; - - if(cb->mask) - { - value = Con_GetInteger(cb->cvarname); - if(cb->active) - { - value |= cb->mask; - } - else - { - value &= ~cb->mask; - } - } - else - { - value = cb->active; - } - - Con_SetInteger2(cb->cvarname, value, SVF_WRITE_OVERRIDE); - return 0; -} - -int Hu_MenuCvarList(mn_object_t* obj, mn_actionid_t action, void* parameters) -{ - const mndata_list_t* list = (mndata_list_t*) obj->_typedata; - const mndata_listitem_t* item; - cvartype_t varType; - int value; - - DENG_UNUSED(parameters); - if(MNA_MODIFIED != action) return 1; - - if(MNList_Selection(obj) < 0) return 0; // Hmm? - - varType = Con_GetVariableType(list->data); - if(CVT_NULL == varType) return 0; - - item = &((mndata_listitem_t*) list->items)[list->selection]; - if(list->mask) - { - value = Con_GetInteger(list->data); - value = (value & ~list->mask) | (item->data & list->mask); - } - else - { - value = item->data; - } - - switch(varType) - { - case CVT_INT: - Con_SetInteger2(list->data, value, SVF_WRITE_OVERRIDE); - break; - case CVT_BYTE: - Con_SetInteger2(list->data, (byte) value, SVF_WRITE_OVERRIDE); - break; - default: - Con_Error("Hu_MenuCvarList: Unsupported variable type %i", (int)varType); - break; - } - return 0; -} - -int Hu_MenuSaveSlotEdit(mn_object_t* obj, mn_actionid_t action, void* parameters) -{ - DENG_UNUSED(parameters); - - if(MNA_ACTIVE != action) return 1; - - // Are we suggesting a new name? - if(cfg.menuGameSaveSuggestName) - { - AutoStr* suggestName = G_GenerateSaveGameName(); - MNEdit_SetText(obj, MNEDIT_STF_NO_ACTION, Str_Text(suggestName)); - } - return 0; -} - -int Hu_MenuCvarEdit(mn_object_t* ob, mn_actionid_t action, void* parameters) -{ - const mndata_edit_t* edit = (mndata_edit_t*)ob->_typedata; - cvartype_t varType = Con_GetVariableType(edit->data1); - - DENG_UNUSED(parameters); - - if(MNA_MODIFIED != action) return 1; - - switch(varType) - { - case CVT_CHARPTR: - Con_SetString2(edit->data1, Str_Text(MNEdit_Text(ob)), SVF_WRITE_OVERRIDE); - break; - case CVT_URIPTR: { - /// @todo Sanitize and validate against known schemas. - Uri* uri = Uri_NewWithPath2(Str_Text(MNEdit_Text(ob)), RC_NULL); - Con_SetUri2(edit->data1, uri, SVF_WRITE_OVERRIDE); - Uri_Delete(uri); - break; - } - default: break; - } - return 0; -} - -int Hu_MenuCvarSlider(mn_object_t* obj, mn_actionid_t action, void* parameters) -{ - const mndata_slider_t* sldr = obj->_typedata; - cvartype_t varType = Con_GetVariableType(sldr->data1); - float value = MNSlider_Value(obj); - - DENG_UNUSED(parameters); - if(MNA_MODIFIED != action) return 1; - - if(CVT_NULL == varType) return 0; - - switch(varType) - { - case CVT_FLOAT: - if(sldr->step >= .01f) - { - Con_SetFloat2(sldr->data1, (int) (100 * value) / 100.0f, SVF_WRITE_OVERRIDE); - } - else - { - Con_SetFloat2(sldr->data1, value, SVF_WRITE_OVERRIDE); - } - break; - case CVT_INT: - Con_SetInteger2(sldr->data1, (int) value, SVF_WRITE_OVERRIDE); - break; - case CVT_BYTE: - Con_SetInteger2(sldr->data1, (byte) value, SVF_WRITE_OVERRIDE); - break; - default: - break; - } - return 0; -} - -int Hu_MenuActivateColorWidget(mn_object_t* obj, mn_actionid_t action, void* parameters) -{ - mn_object_t* cboxMix, *sldrRed, *sldrGreen, *sldrBlue, *textAlpha, *sldrAlpha; - mn_page_t* colorWidgetPage = Hu_MenuFindPageByName("ColorWidget"); - - DENG_UNUSED(parameters); - if(action != MNA_ACTIVE) return 1; - - cboxMix = MN_MustFindObjectOnPage(colorWidgetPage, 0, MNF_ID0); - sldrRed = MN_MustFindObjectOnPage(colorWidgetPage, 0, MNF_ID1); - sldrGreen = MN_MustFindObjectOnPage(colorWidgetPage, 0, MNF_ID2); - sldrBlue = MN_MustFindObjectOnPage(colorWidgetPage, 0, MNF_ID3); - textAlpha = MN_MustFindObjectOnPage(colorWidgetPage, 0, MNF_ID4); - sldrAlpha = MN_MustFindObjectOnPage(colorWidgetPage, 0, MNF_ID5); - - colorWidgetActive = true; - - MNPage_Initialize(colorWidgetPage); - colorWidgetPage->userData = obj; - - MNColorBox_CopyColor(cboxMix, 0, obj); - MNSlider_SetValue(sldrRed, MNSLIDER_SVF_NO_ACTION, MNColorBox_Redf(obj)); - MNSlider_SetValue(sldrGreen, MNSLIDER_SVF_NO_ACTION, MNColorBox_Greenf(obj)); - MNSlider_SetValue(sldrBlue, MNSLIDER_SVF_NO_ACTION, MNColorBox_Bluef(obj)); - MNSlider_SetValue(sldrAlpha, MNSLIDER_SVF_NO_ACTION, MNColorBox_Alphaf(obj)); - - MNObject_SetFlags(textAlpha, (MNColorBox_RGBAMode(obj)? FO_CLEAR : FO_SET), MNF_DISABLED|MNF_HIDDEN); - MNObject_SetFlags(sldrAlpha, (MNColorBox_RGBAMode(obj)? FO_CLEAR : FO_SET), MNF_DISABLED|MNF_HIDDEN); - - return 0; -} - -int Hu_MenuCvarColorBox(mn_object_t* obj, mn_actionid_t action, void* parameters) -{ - mndata_colorbox_t* cbox = (mndata_colorbox_t*)obj->_typedata; - DENG_UNUSED(parameters); - if(action != MNA_MODIFIED) return 1; - // MNColorBox's current color has already been updated and we know - // that at least one of the color components have changed. - // So our job is to simply update the associated cvars. - Con_SetFloat2(cbox->data1, MNColorBox_Redf(obj), SVF_WRITE_OVERRIDE); - Con_SetFloat2(cbox->data2, MNColorBox_Greenf(obj), SVF_WRITE_OVERRIDE); - Con_SetFloat2(cbox->data3, MNColorBox_Bluef(obj), SVF_WRITE_OVERRIDE); - if(MNColorBox_RGBAMode(obj)) - Con_SetFloat2(cbox->data4, MNColorBox_Alphaf(obj), SVF_WRITE_OVERRIDE); - return 0; -} - -void Hu_MenuDrawLoadGamePage(mn_page_t* page, const Point2Raw* origin) -{ - DENG_UNUSED(page); - - DGL_Enable(DGL_TEXTURE_2D); - DGL_Color4f(1, 1, 1, mnRendState->pageAlpha); - FR_SetFont(FID(GF_FONTB)); - FR_SetColorAndAlpha(cfg.menuTextColors[0][CR], cfg.menuTextColors[0][CG], cfg.menuTextColors[0][CB], mnRendState->pageAlpha); - -#if __JHERETIC__ || __JHEXEN__ - FR_DrawTextXY3("Load Game", SCREENWIDTH/2, origin->y-20, ALIGN_TOP, MN_MergeMenuEffectWithDrawTextFlags(0)); -#else - WI_DrawPatchXY3(pLoadGame, Hu_ChoosePatchReplacement(cfg.menuPatchReplaceMode, pLoadGame), - origin->x - 8, origin->y - 26, ALIGN_TOPLEFT, 0, MN_MergeMenuEffectWithDrawTextFlags(0)); -#endif - DGL_Disable(DGL_TEXTURE_2D); - - { Point2Raw helpOrigin; - helpOrigin.x = SCREENWIDTH/2; - helpOrigin.y = (SCREENHEIGHT/2) + ((SCREENHEIGHT/2-5)/cfg.menuScale); - Hu_MenuDrawPageHelp("Select to load, [Del] to clear", helpOrigin.x, helpOrigin.y); - } -} - -void Hu_MenuDrawSaveGamePage(mn_page_t* page, const Point2Raw* origin) -{ - DENG_UNUSED(page); - -#if __JHERETIC__ || __JHEXEN__ - Hu_MenuDrawPageTitle("Save Game", SCREENWIDTH/2, origin->y - 20); -#else - DGL_Enable(DGL_TEXTURE_2D); - DGL_Color4f(1, 1, 1, mnRendState->pageAlpha); - FR_SetFont(FID(GF_FONTB)); - FR_SetColorAndAlpha(cfg.menuTextColors[0][CR], cfg.menuTextColors[0][CG], cfg.menuTextColors[0][CB], mnRendState->pageAlpha); - - WI_DrawPatchXY3(pSaveGame, Hu_ChoosePatchReplacement(cfg.menuPatchReplaceMode, pSaveGame), - origin->x - 8, origin->y - 26, ALIGN_TOPLEFT, 0, MN_MergeMenuEffectWithDrawTextFlags(0)); - - DGL_Disable(DGL_TEXTURE_2D); -#endif - - { Point2Raw helpOrigin; - helpOrigin.x = SCREENWIDTH/2; - helpOrigin.y = (SCREENHEIGHT/2) + ((SCREENHEIGHT/2-5)/cfg.menuScale); - Hu_MenuDrawPageHelp("Select to save, [Del] to clear", helpOrigin.x, helpOrigin.y); - } -} - -#if __JDOOM__ || __JHERETIC__ || __JHEXEN__ -int Hu_MenuSelectHelp(mn_object_t* obj, mn_actionid_t action, void* parameters) -{ - DENG_UNUSED(parameters); - if(MNA_ACTIVEOUT != action) return 1; - G_StartHelp(); - return 0; -} -#endif - -void Hu_MenuDrawOptionsPage(mn_page_t* page, const Point2Raw* origin) -{ -#if __JHERETIC__ || __JHEXEN__ - Hu_MenuDrawPageTitle("Options", origin->x + 42, origin->y - 38); -#else - DGL_Enable(DGL_TEXTURE_2D); - DGL_Color4f(1, 1, 1, mnRendState->pageAlpha); - FR_SetFont(FID(GF_FONTB)); - FR_SetColorAndAlpha(cfg.menuTextColors[0][CR], cfg.menuTextColors[0][CG], cfg.menuTextColors[0][CB], mnRendState->pageAlpha); - - WI_DrawPatchXY3(pOptionsTitle, Hu_ChoosePatchReplacement(cfg.menuPatchReplaceMode, pOptionsTitle), - origin->x + 42, origin->y - 20, ALIGN_TOP, 0, MN_MergeMenuEffectWithDrawTextFlags(0)); - - DGL_Disable(DGL_TEXTURE_2D); -#endif -} - -void Hu_MenuDrawWeaponsPage(mn_page_t* page, const Point2Raw* offset) -{ - // Inform the user how to change the order. - if(MNPage_FocusObject(page) == MN_MustFindObjectOnPage(page, 0, MNF_ID0)) - { - const char* helpText = "Use left/right to move weapon up/down"; - Point2Raw origin; - origin.x = SCREENWIDTH/2; - origin.y = (SCREENHEIGHT/2) + ((SCREENHEIGHT/2-5)/cfg.menuScale); - Hu_MenuDrawPageHelp(helpText, origin.x, origin.y); - } -} - -void Hu_MenuDrawMultiplayerPage(mn_page_t* page, const Point2Raw* origin) -{ - Hu_MenuDrawPageTitle(GET_TXT(TXT_MULTIPLAYER), SCREENWIDTH/2, origin->y - 28); -} - -void Hu_MenuDrawPlayerSetupPage(mn_page_t* page, const Point2Raw* origin) -{ - Hu_MenuDrawPageTitle(GET_TXT(TXT_PLAYERSETUP), SCREENWIDTH/2, origin->y - 28); -} - -int Hu_MenuActionSetActivePage(mn_object_t* obj, mn_actionid_t action, void* parameters) -{ - assert(obj); - DENG_UNUSED(parameters); - if(MNA_ACTIVEOUT != action) return 1; - Hu_MenuSetActivePage(Hu_MenuFindPageByName((char*)obj->data1)); - return 0; -} - -int Hu_MenuUpdateColorWidgetColor(mn_object_t* obj, mn_actionid_t action, void* parameters) -{ - float value = MNSlider_Value(obj); - mn_object_t* cboxMix = MN_MustFindObjectOnPage(Hu_MenuFindPageByName("ColorWidget"), 0, MNF_ID0); - - DENG_UNUSED(parameters); - if(MNA_MODIFIED != action) return 1; - - switch(obj->data2) - { - case CR: MNColorBox_SetRedf( cboxMix, MNCOLORBOX_SCF_NO_ACTION, value); break; - case CG: MNColorBox_SetGreenf(cboxMix, MNCOLORBOX_SCF_NO_ACTION, value); break; - case CB: MNColorBox_SetBluef( cboxMix, MNCOLORBOX_SCF_NO_ACTION, value); break; - case CA: MNColorBox_SetAlphaf(cboxMix, MNCOLORBOX_SCF_NO_ACTION, value); break; - default: - Con_Error("Hu_MenuUpdateColorWidgetColor: Invalid value (%i) for data2.", obj->data2); - } - return 0; -} - -int Hu_MenuChangeWeaponPriority(mn_object_t* obj, mn_actionid_t action, void* parameters) -{ - DENG_UNUSED(parameters); - if(MNA_MODIFIED != action) return 1; - /*int choice = option >> NUM_WEAPON_TYPES; - int temp; - - if(option & RIGHT_DIR) - { - if(choice < NUM_WEAPON_TYPES-1) - { - temp = cfg.weaponOrder[choice+1]; - cfg.weaponOrder[choice+1] = cfg.weaponOrder[choice]; - cfg.weaponOrder[choice] = temp; - } - } - else - { - if(choice > 0) - { - temp = cfg.weaponOrder[choice]; - cfg.weaponOrder[choice] = cfg.weaponOrder[choice-1]; - cfg.weaponOrder[choice-1] = temp; - } - }*/ - return 0; -} - -int Hu_MenuSelectSingleplayer(mn_object_t* ob, mn_actionid_t action, void* parameters) -{ - DENG_UNUSED(parameters); - - if(MNA_ACTIVEOUT != action) return 1; - - if(IS_NETGAME) - { - Hu_MsgStart(MSG_ANYKEY, NEWGAME, NULL, 0, NULL); - return 0; - } - -#if __JHEXEN__ - Hu_MenuSetActivePage(Hu_MenuFindPageByName("PlayerClass")); -#elif __JHERETIC__ - Hu_MenuSetActivePage(Hu_MenuFindPageByName("Episode")); -#elif __JDOOM64__ - Hu_MenuSetActivePage(Hu_MenuFindPageByName("Skill")); -#else // __JDOOM__ - if(gameModeBits & (GM_ANY_DOOM2|GM_DOOM_CHEX)) - Hu_MenuSetActivePage(Hu_MenuFindPageByName("Skill")); - else - Hu_MenuSetActivePage(Hu_MenuFindPageByName("Episode")); -#endif - return 0; -} - -int Hu_MenuSelectMultiplayer(mn_object_t* obj, mn_actionid_t action, void* parameters) -{ - mn_page_t* multiplayerPage = Hu_MenuFindPageByName("Multiplayer"); - mn_object_t* labelObj = MN_MustFindObjectOnPage(multiplayerPage, 0, MNF_ID0); - mndata_button_t* btn = (mndata_button_t*)labelObj->_typedata; - - DENG_UNUSED(parameters); - if(MNA_ACTIVEOUT != action) return 1; - - // Set the appropriate label. - if(IS_NETGAME) - { - btn->text = "Disconnect"; - } - else - { - btn->text = "Join Game"; - } - Hu_MenuSetActivePage(multiplayerPage); - return 0; -} - -int Hu_MenuSelectJoinGame(mn_object_t* ob, mn_actionid_t action, void* parameters) -{ - DENG_UNUSED(ob); - DENG_UNUSED(parameters); - - if(MNA_ACTIVEOUT != action) return 1; - if(IS_NETGAME) - { - DD_Execute(false, "net disconnect"); - Hu_MenuCommand(MCMD_CLOSE); - return 0; - } - - DD_Execute(false, "net setup client"); - return 0; -} - -int Hu_MenuSelectPlayerSetup(mn_object_t* ob, mn_actionid_t action, void* parameters) -{ - mn_page_t* playerSetupPage = Hu_MenuFindPageByName("PlayerSetup"); - mn_object_t* mop = MN_MustFindObjectOnPage(playerSetupPage, 0, MNF_ID0); - mn_object_t* name = MN_MustFindObjectOnPage(playerSetupPage, 0, MNF_ID1); - mn_object_t* color = MN_MustFindObjectOnPage(playerSetupPage, 0, MNF_ID3); -#if __JHEXEN__ - mn_object_t* class_; -#endif - - DENG_UNUSED(ob); - DENG_UNUSED(parameters); - if(MNA_ACTIVEOUT != action) return 1; - -#if __JHEXEN__ - MNMobjPreview_SetMobjType(mop, PCLASS_INFO(cfg.netClass)->mobjType); - MNMobjPreview_SetPlayerClass(mop, cfg.netClass); -#else - MNMobjPreview_SetMobjType(mop, MT_PLAYER); - MNMobjPreview_SetPlayerClass(mop, PCLASS_PLAYER); -#endif - MNMobjPreview_SetTranslationClass(mop, 0); - MNMobjPreview_SetTranslationMap(mop, cfg.netColor); - - MNList_SelectItemByValue(color, MNLIST_SIF_NO_ACTION, cfg.netColor); -#if __JHEXEN__ - class_ = MN_MustFindObjectOnPage(playerSetupPage, 0, MNF_ID2); - MNList_SelectItemByValue(class_, MNLIST_SIF_NO_ACTION, cfg.netClass); -#endif - - MNEdit_SetText(name, MNEDIT_STF_NO_ACTION|MNEDIT_STF_REPLACEOLD, Con_GetString("net-name")); - - Hu_MenuSetActivePage(playerSetupPage); - return 0; -} - -#if __JHEXEN__ -int Hu_MenuSelectPlayerSetupPlayerClass(mn_object_t* ob, mn_actionid_t action, void* parameters) -{ - int selection; - - DENG_UNUSED(parameters); - if(MNA_MODIFIED != action) return 1; - - selection = MNList_Selection(ob); - if(selection >= 0) - { - mn_object_t* mop = MN_MustFindObjectOnPage(MNObject_Page(ob), 0, MNF_ID0); - MNMobjPreview_SetPlayerClass(mop, selection); - MNMobjPreview_SetMobjType(mop, PCLASS_INFO(selection)->mobjType); - } - return 0; -} -#endif - -int Hu_MenuSelectPlayerColor(mn_object_t* ob, mn_actionid_t action, void* parameters) -{ - int selection; - - DENG_UNUSED(parameters); - if(MNA_MODIFIED != action) return 1; - - // The color translation map is stored in the list item data member. - selection = MNList_ItemData(ob, MNList_Selection(ob)); - if(selection >= 0) - { - mn_object_t* mop = MN_MustFindObjectOnPage(MNObject_Page(ob), 0, MNF_ID0); - MNMobjPreview_SetTranslationMap(mop, selection); - } - return 0; -} - -int Hu_MenuSelectAcceptPlayerSetup(mn_object_t* ob, mn_actionid_t action, void* parameters) -{ - mn_object_t* plrNameEdit = MN_MustFindObjectOnPage(MNObject_Page(ob), 0, MNF_ID1); -#if __JHEXEN__ - mn_object_t* plrClassList = MN_MustFindObjectOnPage(MNObject_Page(ob), 0, MNF_ID2); -#endif - mn_object_t* plrColorList = MN_MustFindObjectOnPage(MNObject_Page(ob), 0, MNF_ID3); - char buf[300]; - - DENG_UNUSED(parameters); - -#if __JHEXEN__ - cfg.netClass = MNList_Selection(plrClassList); -#endif - // The color translation map is stored in the list item data member. - cfg.netColor = MNList_ItemData(plrColorList, MNList_Selection(plrColorList)); - - if(MNA_ACTIVEOUT != action) return 1; - - strcpy(buf, "net-name "); - M_StrCatQuoted(buf, Str_Text(MNEdit_Text(plrNameEdit)), 300); - DD_Execute(false, buf); - - if(IS_NETGAME) - { - strcpy(buf, "setname "); - M_StrCatQuoted(buf, Str_Text(MNEdit_Text(plrNameEdit)), 300); - DD_Execute(false, buf); -#if __JHEXEN__ - // Must do 'setclass' first; the real class and color do not change - // until the server sends us a notification -- this means if we do - // 'setcolor' first, the 'setclass' after it will override the color - // change (or such would appear to be the case). - DD_Executef(false, "setclass %i", cfg.netClass); -#endif - DD_Executef(false, "setcolor %i", cfg.netColor); - } - - Hu_MenuSetActivePage(Hu_MenuFindPageByName("Multiplayer")); - return 0; -} - -int Hu_MenuSelectQuitGame(mn_object_t* ob, mn_actionid_t action, void* parameters) -{ - DENG_UNUSED(ob); - DENG_UNUSED(parameters); - if(MNA_ACTIVEOUT != action) return 1; - G_QuitGame(); - return 0; -} - -int Hu_MenuSelectEndGame(mn_object_t* ob, mn_actionid_t action, void* parameters) -{ - DENG_UNUSED(ob); - DENG_UNUSED(parameters); - if(MNA_ACTIVEOUT != action) return 1; - G_EndGame(); - return 0; -} - -int Hu_MenuSelectLoadGame(mn_object_t* ob, mn_actionid_t action, void* parameters) -{ - DENG_UNUSED(ob); - DENG_UNUSED(parameters); - - if(MNA_ACTIVEOUT != action) return 1; - - if(!Get(DD_DEDICATED)) - { - if(IS_CLIENT && !Get(DD_PLAYBACK)) - { - Hu_MsgStart(MSG_ANYKEY, LOADNET, NULL, 0, NULL); - return 0; - } - } - - Hu_MenuUpdateGameSaveWidgets(); - Hu_MenuSetActivePage(Hu_MenuFindPageByName("LoadGame")); - return 0; -} - -int Hu_MenuSelectSaveGame(mn_object_t* ob, mn_actionid_t action, void* parameters) -{ - player_t* player = &players[CONSOLEPLAYER]; - - DENG_UNUSED(ob); - DENG_UNUSED(parameters); - - if(MNA_ACTIVEOUT != action) return 1; - if(!Get(DD_DEDICATED)) - { - if(IS_CLIENT) - { -#if __JDOOM__ || __JDOOM64__ - Hu_MsgStart(MSG_ANYKEY, SAVENET, NULL, 0, NULL); -#endif - return 0; - } - - if(G_GameState() != GS_MAP) - { - Hu_MsgStart(MSG_ANYKEY, SAVEOUTMAP, NULL, 0, NULL); - return 0; - } - - if(player->playerState == PST_DEAD) - { - Hu_MsgStart(MSG_ANYKEY, SAVEDEAD, NULL, 0, NULL); - return 0; - } - } - - Hu_MenuCommand(MCMD_OPEN); - Hu_MenuUpdateGameSaveWidgets(); - Hu_MenuSetActivePage(Hu_MenuFindPageByName("SaveGame")); - return 0; -} - -#if __JHEXEN__ -int Hu_MenuSelectPlayerClass(mn_object_t* ob, mn_actionid_t action, void* parameters) -{ - mn_page_t* skillPage = Hu_MenuFindPageByName("Skill"); - int option = ob->data2; - mn_object_t* skillObj; - const char* text; - - DENG_UNUSED(parameters); - - if(MNA_ACTIVEOUT != action) return 1; - if(IS_NETGAME) - { - P_SetMessage(&players[CONSOLEPLAYER], LMF_NO_HIDE, "You can't start a new game from within a netgame!"); - return 0; - } - - if(option < 0) - { // Random class. - // Number of user-selectable classes. - mnPlrClass = (menuTime / 5) % 3; - } - else - { - mnPlrClass = option; - } - - skillObj = MN_MustFindObjectOnPage(skillPage, 0, MNF_ID0); - text = GET_TXT(PCLASS_INFO(mnPlrClass)->skillModeNames[SM_BABY]); - ((mndata_button_t*)skillObj->_typedata)->text = text; - MNObject_SetShortcut(skillObj, text[0]); - - skillObj = MN_MustFindObjectOnPage(skillPage, 0, MNF_ID1); - text = GET_TXT(PCLASS_INFO(mnPlrClass)->skillModeNames[SM_EASY]); - ((mndata_button_t*)skillObj->_typedata)->text = text; - MNObject_SetShortcut(skillObj, text[0]); - - skillObj = MN_MustFindObjectOnPage(skillPage, 0, MNF_ID2); - text = GET_TXT(PCLASS_INFO(mnPlrClass)->skillModeNames[SM_MEDIUM]); - ((mndata_button_t*)skillObj->_typedata)->text = text; - MNObject_SetShortcut(skillObj, text[0]); - - skillObj = MN_MustFindObjectOnPage(skillPage, 0, MNF_ID3); - text = GET_TXT(PCLASS_INFO(mnPlrClass)->skillModeNames[SM_HARD]); - ((mndata_button_t*)skillObj->_typedata)->text = text; - MNObject_SetShortcut(skillObj, text[0]); - - skillObj = MN_MustFindObjectOnPage(skillPage, 0, MNF_ID4); - text = GET_TXT(PCLASS_INFO(mnPlrClass)->skillModeNames[SM_NIGHTMARE]); - ((mndata_button_t*)skillObj->_typedata)->text = text; - MNObject_SetShortcut(skillObj, text[0]); - - switch(mnPlrClass) - { - case PCLASS_FIGHTER: MNPage_SetX(skillPage, 120); break; - case PCLASS_CLERIC: MNPage_SetX(skillPage, 116); break; - case PCLASS_MAGE: MNPage_SetX(skillPage, 112); break; - } - Hu_MenuSetActivePage(skillPage); - return 0; -} - -int Hu_MenuFocusOnPlayerClass(mn_object_t* ob, mn_actionid_t action, void* parameters) -{ - playerclass_t plrClass = (playerclass_t)ob->data2; - mn_object_t* mop; - - DENG_UNUSED(parameters); - if(MNA_FOCUS != action) return 1; - - mop = MN_MustFindObjectOnPage(MNObject_Page(ob), 0, MNF_ID0); - MNMobjPreview_SetPlayerClass(mop, plrClass); - MNMobjPreview_SetMobjType(mop, (PCLASS_NONE == plrClass? MT_NONE : PCLASS_INFO(plrClass)->mobjType)); - - Hu_MenuDefaultFocusAction(ob, action, parameters); - return 0; -} -#endif - -#if __JDOOM__ || __JHERETIC__ -int Hu_MenuFocusEpisode(mn_object_t* ob, mn_actionid_t action, void* parameters) -{ - DENG_UNUSED(parameters); - if(MNA_FOCUS != action) return 1; - mnEpisode = ob->data2; - Hu_MenuDefaultFocusAction(ob, action, parameters); - return 0; -} - -int Hu_MenuConfirmOrderCommericalVersion(msgresponse_t response, int userValue, void* userPointer) -{ - G_StartHelp(); - return true; -} - -int Hu_MenuActivateNotSharewareEpisode(mn_object_t* ob, mn_actionid_t action, void* parameters) -{ - DENG_UNUSED(ob); - DENG_UNUSED(parameters); - if(MNA_ACTIVEOUT != action) return 1; - Hu_MsgStart(MSG_ANYKEY, SWSTRING, Hu_MenuConfirmOrderCommericalVersion, 0, NULL); - return 0; -} -#endif - -int Hu_MenuFocusSkillMode(mn_object_t* ob, mn_actionid_t action, void* parameters) -{ - assert(ob); - DENG_UNUSED(parameters); - if(MNA_FOCUS != action) return 1; - mnSkillmode = (skillmode_t)ob->data2; - Hu_MenuDefaultFocusAction(ob, action, parameters); - return 0; -} - -#if __JDOOM__ -int Hu_MenuConfirmInitNewGame(msgresponse_t response, int userValue, void* userPointer) -{ - if(response == MSG_YES) - { - Hu_MenuInitNewGame(true); - } - return true; -} -#endif - -void Hu_MenuInitNewGame(dd_bool confirmed) -{ - GameRuleset newRules = gameRules; - Uri *newMapUri = 0; - -#if __JDOOM__ - if(!confirmed && SM_NIGHTMARE == mnSkillmode) - { - Hu_MsgStart(MSG_YESNO, NIGHTMARE, Hu_MenuConfirmInitNewGame, 0, NULL); - return; - } -#endif - - Hu_MenuCommand(chooseCloseMethod()); - -#if __JHEXEN__ - cfg.playerClass[CONSOLEPLAYER] = mnPlrClass; -#endif - - newRules.skill = mnSkillmode; -#if __JHEXEN__ - newMapUri = G_ComposeMapUri(mnEpisode, P_TranslateMap(0)); -#else - newMapUri = G_ComposeMapUri(mnEpisode, 0); -#endif - - G_DeferredNewGame(newMapUri, 0/*default*/, &newRules); - Uri_Delete(newMapUri); -} - -int Hu_MenuActionInitNewGame(mn_object_t* ob, mn_actionid_t action, void* parameters) -{ - DENG_UNUSED(ob); - DENG_UNUSED(parameters); - if(MNA_ACTIVEOUT != action) return 1; - Hu_MenuInitNewGame(false); - return 0; -} - -int Hu_MenuSelectControlPanelLink(mn_object_t* ob, mn_actionid_t action, void* parameters) -{ -#define NUM_PANEL_NAMES 1 - - static const char* panelNames[NUM_PANEL_NAMES] = { - "taskbar" //, - //"panel audio", - //"panel input" - }; - int idx = ob->data2; - - DENG_UNUSED(parameters); - if(MNA_ACTIVEOUT != action) return 1; - - if(idx < 0 || idx > NUM_PANEL_NAMES - 1) - idx = 0; - - DD_Execute(true, panelNames[idx]); - return 0; - -#undef NUM_PANEL_NAMES -} - -D_CMD(MenuOpen) -{ - if(argc > 1) - { - if(!stricmp(argv[1], "open")) - { - Hu_MenuCommand(MCMD_OPEN); - return true; - } - if(!stricmp(argv[1], "close")) - { - Hu_MenuCommand(MCMD_CLOSE); - return true; - } - - { - mn_page_t* page = Hu_MenuFindPageByName(argv[1]); - if(page) - { - Hu_MenuCommand(MCMD_OPEN); - Hu_MenuSetActivePage(page); - return true; - } - } - return false; - } - - Hu_MenuCommand(!menuActive? MCMD_OPEN : MCMD_CLOSE); - return true; -} - -/** - * Routes console commands for menu actions and navigation into the menu subsystem. - */ -D_CMD(MenuCommand) -{ - if(menuActive) - { - const char* cmd = argv[0] + 4; - if(!stricmp(cmd, "up")) - { - Hu_MenuCommand(MCMD_NAV_UP); - return true; - } - if(!stricmp(cmd, "down")) - { - Hu_MenuCommand(MCMD_NAV_DOWN); - return true; - } - if(!stricmp(cmd, "left")) - { - Hu_MenuCommand(MCMD_NAV_LEFT); - return true; - } - if(!stricmp(cmd, "right")) - { - Hu_MenuCommand(MCMD_NAV_RIGHT); - return true; - } - if(!stricmp(cmd, "back")) - { - Hu_MenuCommand(MCMD_NAV_OUT); - return true; - } - if(!stricmp(cmd, "delete")) - { - Hu_MenuCommand(MCMD_DELETE); - return true; - } - if(!stricmp(cmd, "select")) - { - Hu_MenuCommand(MCMD_SELECT); - return true; - } - if(!stricmp(cmd, "pagedown")) - { - Hu_MenuCommand(MCMD_NAV_PAGEDOWN); - return true; - } - if(!stricmp(cmd, "pageup")) - { - Hu_MenuCommand(MCMD_NAV_PAGEUP); - return true; - } - } - return false; -} diff --git a/doomsday/plugins/common/src/hu_menu.cpp b/doomsday/plugins/common/src/hu_menu.cpp new file mode 100644 index 0000000000..1b921db24b --- /dev/null +++ b/doomsday/plugins/common/src/hu_menu.cpp @@ -0,0 +1,6661 @@ +/** @file hu_menu.cpp Menu widget stuff, episode selection and such. + * + * @authors Copyright © 2003-2013 Jaakko Keränen + * @authors Copyright © 2005-2013 Daniel Swanson + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "common.h" +#include "hu_menu.h" + +#include "m_argv.h" +#include "hu_chat.h" +#include "hu_log.h" +#include "hu_msg.h" +#include "hu_stuff.h" +#include "am_map.h" +#include "x_hair.h" +#include "player.h" +#include "g_controls.h" +#include "p_saveg.h" +#include "g_common.h" +#include "r_common.h" +#include "m_ctrl.h" +#include "saveslots.h" +#include +#include +#include +#include +#include +#include + +/// Original game line height for pages that employ the fixed layout (in 320x200 pixels). +#if __JDOOM__ +# define FIXED_LINE_HEIGHT (15+1) +#else +# define FIXED_LINE_HEIGHT (19+1) +#endif + +struct cvarbutton_t +{ + char active; + char const *cvarname; + char const *yes; + char const *no; + int mask; + + cvarbutton_t(char active = 0, char const *cvarname = 0, char const *yes = 0, char const *no = 0, + int mask = 0) + : active(active) + , cvarname(cvarname) + , yes(yes) + , no(no) + , mask(mask) + {} +}; + +int Hu_MenuActionSetActivePage(mn_object_t *ob, mn_actionid_t action, void *parameters); +int Hu_MenuActionInitNewGame(mn_object_t *ob, mn_actionid_t action, void *parameters); + +int Hu_MenuSelectLoadGame(mn_object_t *ob, mn_actionid_t action, void *parameters); +int Hu_MenuSelectSaveGame(mn_object_t *ob, mn_actionid_t action, void *parameters); +#if __JHEXEN__ +int Hu_MenuSelectFiles(mn_object_t *ob, mn_actionid_t action, void *parameters); +#endif +int Hu_MenuSelectPlayerSetup(mn_object_t *ob, mn_actionid_t action, void *parameters); +int Hu_MenuSelectJoinGame(mn_object_t *ob, mn_actionid_t action, void *parameters); + +#if __JDOOM__ || __JHERETIC__ || __JHEXEN__ +int Hu_MenuSelectHelp(mn_object_t *ob, mn_actionid_t action, void *parameters); +#endif +int Hu_MenuSelectControlPanelLink(mn_object_t *ob, mn_actionid_t action, void *parameters); + +int Hu_MenuSelectSingleplayer(mn_object_t *ob, mn_actionid_t action, void *parameters); +int Hu_MenuSelectMultiplayer(mn_object_t *ob, mn_actionid_t action, void *parameters); +#if __JDOOM__ || __JHERETIC__ +int Hu_MenuFocusEpisode(mn_object_t *ob, mn_actionid_t action, void *parameters); +int Hu_MenuActivateNotSharewareEpisode(mn_object_t *ob, mn_actionid_t action, void *parameters); +#endif +#if __JHEXEN__ +int Hu_MenuFocusOnPlayerClass(mn_object_t *ob, mn_actionid_t action, void *parameters); +int Hu_MenuSelectPlayerClass(mn_object_t *ob, mn_actionid_t action, void *parameters); +#endif +int Hu_MenuFocusSkillMode(mn_object_t *ob, mn_actionid_t action, void *parameters); +int Hu_MenuSelectLoadSlot(mn_object_t *ob, mn_actionid_t action, void *parameters); +int Hu_MenuSelectQuitGame(mn_object_t *ob, mn_actionid_t action, void *parameters); +int Hu_MenuSelectEndGame(mn_object_t *ob, mn_actionid_t action, void *parameters); +int Hu_MenuSelectAcceptPlayerSetup(mn_object_t *ob, mn_actionid_t action, void *parameters); + +int Hu_MenuSelectSaveSlot(mn_object_t *ob, mn_actionid_t action, void *parameters); + +int Hu_MenuChangeWeaponPriority(mn_object_t *ob, mn_actionid_t action, void *parameters); +#if __JHEXEN__ +int Hu_MenuSelectPlayerSetupPlayerClass(mn_object_t *ob, mn_actionid_t action, void *parameters); +#endif +int Hu_MenuSelectPlayerColor(mn_object_t *ob, mn_actionid_t action, void *parameters); + +#if __JHEXEN__ +void Hu_MenuPlayerClassBackgroundTicker(mn_object_t *ob); +void Hu_MenuPlayerClassPreviewTicker(mn_object_t *ob); +#endif + +#if __JHERETIC__ || __JHEXEN__ +void Hu_MenuDrawMainPage(mn_page_t *page, Point2Raw const *origin); +#endif + +void Hu_MenuDrawGameTypePage(mn_page_t *page, Point2Raw const *origin); +void Hu_MenuDrawSkillPage(mn_page_t *page, Point2Raw const *origin); +#if __JHEXEN__ +void Hu_MenuDrawPlayerClassPage(mn_page_t *page, Point2Raw const *origin); +#endif +#if __JDOOM__ || __JHERETIC__ +void Hu_MenuDrawEpisodePage(mn_page_t *page, Point2Raw const *origin); +#endif +void Hu_MenuDrawOptionsPage(mn_page_t *page, Point2Raw const *origin); +void Hu_MenuDrawWeaponsPage(mn_page_t *page, Point2Raw const *origin); +void Hu_MenuDrawLoadGamePage(mn_page_t *page, Point2Raw const *origin); +void Hu_MenuDrawSaveGamePage(mn_page_t *page, Point2Raw const *origin); +void Hu_MenuDrawMultiplayerPage(mn_page_t *page, Point2Raw const *origin); +void Hu_MenuDrawPlayerSetupPage(mn_page_t *page, Point2Raw const *origin); + +int Hu_MenuColorWidgetCmdResponder(mn_page_t *page, menucommand_e cmd); + +static void initAllPages(); +static void destroyAllPages(); + +static void initAllObjectsOnAllPages(); +static void updatePageObjects(mn_page_t *page); + +static void Hu_MenuUpdateCursorState(); + +static dd_bool Hu_MenuHasCursorRotation(mn_object_t *obj); + +cvarbutton_t mnCVarButtons[] = { + cvarbutton_t(0, "ctl-aim-noauto"), +#if __JHERETIC__ || __JHEXEN__ + cvarbutton_t(0, "ctl-inventory-mode", "Scroll", "Cursor"), + cvarbutton_t(0, "ctl-inventory-use-immediate"), + cvarbutton_t(0, "ctl-inventory-use-next"), + cvarbutton_t(0, "ctl-inventory-wrap"), +#endif + cvarbutton_t(0, "ctl-look-spring"), + cvarbutton_t(0, "ctl-run"), +#if __JDOOM__ || __JDOOM64__ + cvarbutton_t(0, "game-anybossdeath666"), +#endif +#if __JDOOM__ || __JHERETIC__ || __JDOOM64__ + cvarbutton_t(0, "game-corpse-sliding"), +#endif +#if __JDOOM__ || __JDOOM64__ + cvarbutton_t(0, "game-maxskulls"), +#endif +#if __JDOOM__ || __JHERETIC__ || __JDOOM64__ + cvarbutton_t(0, "game-monsters-stuckindoors"), + cvarbutton_t(0, "game-monsters-floatoverblocking"), + cvarbutton_t(0, "game-objects-clipping"), + cvarbutton_t(0, "game-objects-falloff"), +#endif +#if __JDOOM__ || __JDOOM64__ + cvarbutton_t(0, "game-objects-gibcrushednonbleeders"), +#endif +#if __JDOOM__ || __JHERETIC__ || __JDOOM64__ + cvarbutton_t(0, "game-objects-neverhangoverledges"), + cvarbutton_t(0, "game-player-wallrun-northonly"), +#endif +#if __JDOOM__ + cvarbutton_t(0, "game-raiseghosts"), +#endif + cvarbutton_t(0, "game-save-confirm"), + cvarbutton_t(0, "game-save-confirm-loadonreborn"), +#if !__JHEXEN__ + cvarbutton_t(0, "game-save-auto-loadonreborn"), +#endif + cvarbutton_t(0, "game-save-last-loadonreborn"), +#if __JDOOM__ || __JDOOM64__ + cvarbutton_t(0, "game-skullsinwalls"), +#if __JDOOM__ + cvarbutton_t(0, "game-vilechase-usevileradius"), +#endif + cvarbutton_t(0, "game-zombiescanexit"), +#endif +#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ + cvarbutton_t(0, "hud-ammo"), + cvarbutton_t(0, "hud-armor"), +#endif +#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ + cvarbutton_t(0, "hud-cheat-counter-show-mapopen"), +#endif +#if __JHERETIC__ || __JHEXEN__ + cvarbutton_t(0, "hud-currentitem"), +#endif +#if __JDOOM__ + cvarbutton_t(0, "hud-face"), + cvarbutton_t(0, "hud-face-ouchfix"), +#endif + cvarbutton_t(0, "hud-health"), +#if __JHERETIC__ || __JHEXEN__ + cvarbutton_t(0, "hud-inventory-slot-showempty"), +#endif +#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ + cvarbutton_t(0, "hud-keys"), +#endif +#if __JDOOM__ + cvarbutton_t(0, "hud-keys-combine"), +#endif +#if __JHEXEN__ + cvarbutton_t(0, "hud-mana"), +#endif +#if __JDOOM64__ + cvarbutton_t(0, "hud-power"), +#endif +#if __JDOOM__ || __JDOOM64__ + cvarbutton_t(0, "hud-status-weaponslots-ownedfix"), +#endif + cvarbutton_t(0, "hud-unhide-damage"), + cvarbutton_t(0, "hud-unhide-pickup-ammo"), + cvarbutton_t(0, "hud-unhide-pickup-armor"), + cvarbutton_t(0, "hud-unhide-pickup-health"), +#if __JHERETIC__ || __JHEXEN__ + cvarbutton_t(0, "hud-unhide-pickup-invitem"), +#endif + cvarbutton_t(0, "hud-unhide-pickup-powerup"), + cvarbutton_t(0, "hud-unhide-pickup-key"), + cvarbutton_t(0, "hud-unhide-pickup-weapon"), + cvarbutton_t(0, "map-door-colors"), + cvarbutton_t(0, "msg-show"), +#if __JDOOM__ || __JDOOM64__ + cvarbutton_t(0, "player-autoswitch-berserk"), +#endif + cvarbutton_t(0, "player-autoswitch-notfiring"), +#if __JDOOM__ || __JHERETIC__ || __JDOOM64__ + cvarbutton_t(0, "player-jump"), +#endif + cvarbutton_t(0, "player-weapon-cycle-sequential"), + cvarbutton_t(0, "player-weapon-nextmode"), +#if __JDOOM64__ + cvarbutton_t(0, "player-weapon-recoil"), +#endif +#if __JDOOM__ || __JDOOM64__ + cvarbutton_t(0, "server-game-bfg-freeaim"), +#endif + cvarbutton_t(0, "server-game-coop-nodamage"), +#if __JDOOM__ || __JDOOM64__ + cvarbutton_t(0, "server-game-coop-nothing"), + cvarbutton_t(0, "server-game-coop-noweapons"), + cvarbutton_t(0, "server-game-coop-respawn-items"), +#endif +#if __JHEXEN__ + cvarbutton_t(0, "server-game-deathmatch"), +#endif +#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ + cvarbutton_t(0, "server-game-jump"), +#endif +#if __JDOOM__ || __JDOOM64__ + cvarbutton_t(0, "server-game-nobfg"), +#endif + cvarbutton_t(0, "server-game-nomonsters"), +#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ + cvarbutton_t(0, "server-game-noteamdamage"), +#endif + cvarbutton_t(0, "server-game-radiusattack-nomaxz"), +#if __JHEXEN__ + cvarbutton_t(0, "server-game-randclass"), +#endif +#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ + cvarbutton_t(0, "server-game-respawn"), +#endif + cvarbutton_t(0, "view-cross-vitality"), + cvarbutton_t() +}; + +int menuTime; +dd_bool menuNominatingQuickSaveSlot; + +static mn_page_t *menuActivePage; +static dd_bool menuActive; + +static float mnAlpha; // Alpha level for the entire menu. +static float mnTargetAlpha; // Target alpha for the entire UI. + +static skillmode_t mnSkillmode = SM_MEDIUM; +static int mnEpisode; +#if __JHEXEN__ +static int mnPlrClass = PCLASS_FIGHTER; +#endif + +static int frame; // Used by any graphic animations that need to be pumped. + +static dd_bool colorWidgetActive; + +// Present cursor state. +static dd_bool cursorHasRotation; +static float cursorAngle; +static int cursorAnimCounter; +static int cursorAnimFrame; + +#if __JHERETIC__ +static char notDesignedForMessage[80]; +#endif + +static patchid_t pMainTitle; +#if __JDOOM__ || __JDOOM64__ +static patchid_t pNewGame; +static patchid_t pSkill; +static patchid_t pEpisode; +static patchid_t pNGame; +static patchid_t pOptions; +static patchid_t pLoadGame; +static patchid_t pSaveGame; +static patchid_t pReadThis; +static patchid_t pQuitGame; +static patchid_t pOptionsTitle; + +static patchid_t pSkillModeNames[NUM_SKILL_MODES]; +#endif +#if __JDOOM__ +static patchid_t pEpisodeNames[4]; +#endif + +#if __JHEXEN__ +static patchid_t pPlayerClassBG[3]; +static patchid_t pBullWithFire[8]; +#endif + +#if __JHERETIC__ +static patchid_t pRotatingSkull[18]; +#endif + +static patchid_t pCursors[MENU_CURSOR_FRAMECOUNT]; + +#if __JDOOM64__ +mndata_slider_t sld_hud_viewsize = { 3, 11, 0, 1, false, (void *)"view-size", 0, 0, 0, 0 }; +#else +mndata_slider_t sld_hud_viewsize = { 3, 13, 0, 1, false, (void *)"view-size", 0, 0, 0, 0 }; +#endif +mndata_slider_t sld_hud_uptime = { 0, 60, 0, 1.f, true, (void *)"hud-timer", (void *)"Disabled", NULL, (void *)" second", (void *)" seconds" }; +mndata_slider_t sld_hud_xhair_size = { 0, 1, 0, .1f, true, (void *)"view-cross-size", 0, 0, 0, 0 }; +mndata_slider_t sld_hud_xhair_angle = { 0, 1, 0, .0625f, true, (void *)"view-cross-angle", 0, 0, 0, 0 }; +mndata_slider_t sld_hud_xhair_opacity = { 0, 1, 0, .1f, true, (void *)"view-cross-a", 0, 0, 0, 0 }; +mndata_slider_t sld_hud_size = { 0, 1, 0, .1f, true, (void *)"hud-scale", 0, 0, 0, 0 }; +mndata_slider_t sld_hud_cntr_size = { 0, 1, 0, .1f, true, (void *)"hud-cheat-counter-scale", 0, 0, 0, 0 }; +mndata_slider_t sld_hud_sbar_size = { 0, 1, 0, .1f, true, (void *)"hud-status-size", 0, 0, 0, 0 }; +mndata_slider_t sld_hud_sbar_opacity = { 0, 1, 0, .1f, true, (void *)"hud-status-alpha", 0, 0, 0, 0 }; +mndata_slider_t sld_hud_msg_size = { 0, 1, 0, .1f, true, (void *)"msg-scale", 0, 0, 0, 0 }; +mndata_slider_t sld_hud_msg_uptime = { 0, 60, 0, 1.f, true, (void *)"msg-uptime", (void *)"Disabled", NULL, (void *)" second", (void *)" seconds" }; + +mndata_colorbox_t cbox_hud_color = { + 0, 0, 0, 0, 0, 0, true, + (void *)"hud-color-r", (void *)"hud-color-g", (void *)"hud-color-b", (void *)"hud-color-a" +}; + +mndata_colorbox_t cbox_hud_msg_color = { + 0, 0, 0, 0, 0, 0, false, + (void *)"msg-color-r", (void *)"msg-color-g", (void *)"msg-color-b", 0 +}; + +mndata_listitem_t listit_hud_xhair_symbols[] = { + { "None", 0 }, + { "Cross", 1 }, + { "Twin Angles", 2 }, + { "Square", 3 }, + { "Open Square", 4 }, + { "Angle", 5 } +}; +mndata_list_t list_hud_xhair_symbol = { + listit_hud_xhair_symbols, NUMLISTITEMS(listit_hud_xhair_symbols), (void *)"view-cross-type", 0, 0, 0, 0 +}; + +mndata_colorbox_t cbox_hud_xhair_color = { + 0, 0, 0, 0, 0, 0, false, + (void *)"view-cross-r", (void *)"view-cross-g", (void *)"view-cross-b", 0 +}; + +#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ +mndata_listitem_t listit_hud_killscounter_displaymethods[] = { + { "Hidden", 0 }, + { "Count", CCH_KILLS }, + { "Percent", CCH_KILLS_PRCNT }, + { "Count+Percent", CCH_KILLS | CCH_KILLS_PRCNT }, +}; +mndata_list_t list_hud_cntr_kills = { + listit_hud_killscounter_displaymethods, + NUMLISTITEMS(listit_hud_killscounter_displaymethods), + (void *)"hud-cheat-counter", CCH_KILLS | CCH_KILLS_PRCNT, 0, 0, 0 +}; + +mndata_listitem_t listit_hud_itemscounter_displaymethods[] = { + { "Hidden", 0 }, + { "Count", CCH_ITEMS }, + { "Percent", CCH_ITEMS_PRCNT }, + { "Count+Percent", CCH_ITEMS | CCH_ITEMS_PRCNT }, +}; +mndata_list_t list_hud_cntr_items = { + listit_hud_itemscounter_displaymethods, + NUMLISTITEMS(listit_hud_itemscounter_displaymethods), + (void *)"hud-cheat-counter", CCH_ITEMS | CCH_ITEMS_PRCNT, 0, 0, 0 +}; + +mndata_listitem_t listit_hud_secretscounter_displaymethods[] = { + { "Hidden", 0 }, + { "Count", CCH_SECRETS }, + { "Percent", CCH_SECRETS_PRCNT }, + { "Count+Percent", CCH_SECRETS | CCH_SECRETS_PRCNT }, +}; +mndata_list_t list_hud_cntr_secrets = { + listit_hud_secretscounter_displaymethods, + NUMLISTITEMS(listit_hud_secretscounter_displaymethods), + (void *)"hud-cheat-counter", CCH_SECRETS | CCH_SECRETS_PRCNT, 0, 0, 0 +}; +#endif + +mndata_text_t txt_hud_view_size = { "View Size", 0, 0 }; +#if __JDOOM__ +mndata_text_t txt_hud_single_key_display = { "Single Key Display", 0, 0 }; +#endif +mndata_text_t txt_hud_autohide = { "AutoHide", 0, 0 }; +mndata_text_t txt_hud_unhide_events = { "UnHide Events", 0, 0 }; +mndata_text_t txt_hud_unhide_receive_damage = { "Receive Damage", 0, 0 }; +mndata_text_t txt_hud_unhide_pickup_health = { "Pickup Health", 0, 0 }; +mndata_text_t txt_hud_unhide_pickup_armor = { "Pickup Armor", 0, 0 }; +mndata_text_t txt_hud_unhide_pickup_powerup = { "Pickup Powerup", 0, 0 }; +mndata_text_t txt_hud_unhide_pickup_weapon = { "Pickup Weapon", 0, 0 }; +#if __JHEXEN__ +mndata_text_t txt_hud_unhide_pickup_ammo = { "Pickup Mana", 0, 0 }; +#else +mndata_text_t txt_hud_unhide_pickup_ammo = { "Pickup Ammo", 0, 0 }; +#endif +mndata_text_t txt_hud_unhide_pickup_key = { "Pickup Key", 0, 0 }; +#if __JHERETIC__ || __JHEXEN__ +mndata_text_t txt_hud_unhide_pickup_item = { "Pickup Item", 0, 0 }; +#endif + +mndata_text_t txt_hud_messages = { "Messages", 0, 0 }; +mndata_text_t txt_hud_msg_shown = { "Shown", 0, 0 }; +mndata_text_t txt_hud_msg_size = { "Size", 0, 0 }; +mndata_text_t txt_hud_msg_color = { "Color", 0, 0 }; +mndata_text_t txt_hud_msg_uptime = { "Uptime", 0, 0 }; + +mndata_text_t txt_hud_crosshair = { "Crosshair", 0, 0 }; +mndata_text_t txt_hud_xhair_symbol = { "Symbol", 0, 0 }; +mndata_text_t txt_hud_xhair_size = { "Size", 0, 0 }; +mndata_text_t txt_hud_xhair_angle = { "Angle", 0, 0 }; +mndata_text_t txt_hud_xhair_opacity = { "Opacity", 0, 0 }; +mndata_text_t txt_hud_xhair_vitality_color = { "Vitality Color", 0, 0 }; +mndata_text_t txt_hud_xhair_color = { "Color", 0, 0 }; + +#if __JDOOM__ || __JHERETIC__ || __JHEXEN__ +mndata_text_t txt_hud_statusbar = { "Statusbar", 0, 0 }; +mndata_text_t txt_hud_sbar_size = { "Size", 0, 0 }; +mndata_text_t txt_hud_sbar_opacity = { "Opacity", 0, 0 }; +#endif +#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ +mndata_text_t txt_hud_counters = { "Counters", 0, 0 }; +mndata_text_t txt_hud_cntr_kills = { "Kills", 0, 0 }; +mndata_text_t txt_hud_cntr_items = { "Items", 0, 0 }; +mndata_text_t txt_hud_cntr_secrets = { "Secrets", 0, 0 }; +mndata_text_t txt_hud_cntr_size = { "Size", 0, 0 }; +#endif + +mndata_text_t txt_hud_fullscreen = { "Fullscreen", 0, 0 }; +mndata_text_t txt_hud_full_size = { "Size", 0, 0 }; +mndata_text_t txt_hud_full_text_color = { "Text Color", 0, 0 }; +#if __JHEXEN__ +mndata_text_t txt_hud_full_show_mana = { "Show Mana", 0, 0 }; +#endif +#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ +mndata_text_t txt_hud_full_show_ammo = { "Show Ammo", 0, 0 }; +mndata_text_t txt_hud_full_show_armor = { "Show Armor", 0, 0 }; +#endif +#if __JDOOM64__ +mndata_text_t txt_hud_full_show_powerkeys = { "Show PowerKeys", 0, 0 }; +#endif +#if __JDOOM__ +mndata_text_t txt_hud_full_show_status = { "Show Status", 0, 0 }; +#endif +mndata_text_t txt_hud_full_show_health = { "Show Health", 0, 0 }; +#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ +mndata_text_t txt_hud_full_show_keys = { "Show Keys", 0, 0 }; +#endif +#if __JHERETIC__ || __JHEXEN__ +mndata_text_t txt_hud_full_show_readyitem = { "Show Ready-Item", 0, 0 }; +#endif +#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ +mndata_text_t txt_hud_cntr_mapopen = { "Automap Only", 0, 0 }; +#endif + +mndata_button_t btn_hud_single_key_display = { true, (void *)"hud-keys-combine", 0, 0, 0, 0, 0 }; +mndata_button_t btn_hud_unhide_receive_damage = { true, (void *)"hud-unhide-damage", 0, 0, 0, 0, 0 }; +mndata_button_t btn_hud_unhide_pickup_health = { true, (void *)"hud-unhide-pickup-health", 0, 0, 0, 0, 0 }; +mndata_button_t btn_hud_unhide_pickup_armor = { true, (void *)"hud-unhide-pickup-armor", 0, 0, 0, 0, 0 }; +mndata_button_t btn_hud_unhide_pickup_powerup = { true, (void *)"hud-unhide-pickup-powerup", 0, 0, 0, 0, 0 }; +mndata_button_t btn_hud_unhide_pickup_weapon = { true, (void *)"hud-unhide-pickup-weapon", 0, 0, 0, 0, 0 }; +mndata_button_t btn_hud_unhide_pickup_ammo = { true, (void *)"hud-unhide-pickup-ammo", 0, 0, 0, 0, 0 }; +mndata_button_t btn_hud_unhide_pickup_key = { true, (void *)"hud-unhide-pickup-key", 0, 0, 0, 0, 0 }; +#if __JHERETIC__ || __JHEXEN__ +mndata_button_t btn_hud_unhide_pickup_item = { true, (void *)"hud-unhide-pickup-invitem", 0, 0, 0, 0, 0 }; +#endif +mndata_button_t btn_hud_msg_shown = { true, (void *)"msg-show", 0, 0, 0, 0, 0 }; +mndata_button_t btn_hud_xhair_vitality_color = { true, (void *)"view-cross-vitality", 0, 0, 0, 0, 0 }; +#if __JHEXEN__ +mndata_button_t btn_hud_full_show_mana = { true, (void *)"hud-mana", 0, 0, 0, 0, 0 }; +#endif +#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ +mndata_button_t btn_hud_full_show_ammo = { true, (void *)"hud-ammo", 0, 0, 0, 0, 0 }; +mndata_button_t btn_hud_full_show_armor = { true, (void *)"hud-armor", 0, 0, 0, 0, 0 }; +#endif +#if __JDOOM64__ +mndata_button_t btn_hud_full_show_powerkeys = { true, (void *)"hud-power", 0, 0, 0, 0, 0 }; +#endif +#if __JDOOM__ +mndata_button_t btn_hud_full_show_face = { true, (void *)"hud-face", 0, 0, 0, 0, 0 }; +#endif +mndata_button_t btn_hud_full_show_health = { true, (void *)"hud-health", 0, 0, 0, 0, 0 }; +#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ +mndata_button_t btn_hud_full_show_keys = { true, (void *)"hud-keys", 0, 0, 0, 0, 0 }; +#endif +#if __JHERETIC__ || __JHEXEN__ +mndata_button_t btn_hud_full_show_readyitem = { true, (void *)"hud-currentitem", 0, 0, 0, 0, 0 }; +#endif +#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ +mndata_button_t btn_hud_cntr_mapopen = { true, (void *)"hud-cheat-counter-show-mapopen", 0, 0, 0, 0, 0 }; +#endif + +static mn_object_t HudMenuObjects[] = { + { MN_TEXT, 0, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_view_size, 0, 0, 0, 0, 0 }, + { MN_SLIDER, 0, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNSlider_Ticker, MNSlider_UpdateGeometry, MNSlider_Drawer, { Hu_MenuCvarSlider, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_viewsize, 0, 0, 0, 0, 0 }, +#if __JDOOM__ + { MN_TEXT, 0, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_single_key_display, 0, 0, 0, 0, 0 }, + { MN_BUTTON, 0, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_single_key_display, 0, 0, 0, 0, 0 }, +#endif + { MN_TEXT, 0, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_autohide, 0, 0, 0, 0, 0 }, + { MN_SLIDER, 0, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR3, MNSlider_Ticker, MNSlider_TextualValueUpdateGeometry, MNSlider_TextualValueDrawer, { Hu_MenuCvarSlider, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_uptime, 0, 0, 0, 0, 0 }, + + { MN_TEXT, 1, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR2, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_unhide_events, 0, 0, 0, 0, 0 }, + { MN_TEXT, 1, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_unhide_receive_damage, 0, 0, 0, 0, 0 }, + { MN_BUTTON, 1, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_unhide_receive_damage, 0, 0, 0, 0, 0 }, + { MN_TEXT, 1, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_unhide_pickup_health, 0, 0, 0, 0, 0 }, + { MN_BUTTON, 1, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_unhide_pickup_health, 0, 0, 0, 0, 0 }, + { MN_TEXT, 1, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_unhide_pickup_armor, 0, 0, 0, 0, 0 }, + { MN_BUTTON, 1, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_unhide_pickup_armor, 0, 0, 0, 0, 0 }, + { MN_TEXT, 1, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_unhide_pickup_powerup, 0, 0, 0, 0, 0 }, + { MN_BUTTON, 1, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_unhide_pickup_powerup, 0, 0, 0, 0, 0 }, + { MN_TEXT, 1, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_unhide_pickup_weapon, 0, 0, 0, 0, 0 }, + { MN_BUTTON, 1, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_unhide_pickup_weapon, 0, 0, 0, 0, 0 }, + { MN_TEXT, 1, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_unhide_pickup_ammo, 0, 0, 0, 0, 0 }, + { MN_BUTTON, 1, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_unhide_pickup_ammo, 0, 0, 0, 0, 0 }, + { MN_TEXT, 1, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_unhide_pickup_key, 0, 0, 0, 0, 0 }, + { MN_BUTTON, 1, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_unhide_pickup_key, 0, 0, 0, 0, 0 }, +#if __JHERETIC__ || __JHEXEN__ + { MN_TEXT, 1, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_unhide_pickup_item, 0, 0, 0, 0, 0 }, + { MN_BUTTON, 1, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_unhide_pickup_item, 0, 0, 0, 0, 0 }, +#endif + + { MN_TEXT, 2, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR2, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_messages, 0, 0, 0, 0, 0 }, + { MN_TEXT, 2, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_msg_shown, 0, 0, 0, 0, 0 }, + { MN_BUTTON, 2, 0, Point2Raw(), 'm',MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_msg_shown, 0, 0, 0, 0, 0 }, + { MN_TEXT, 2, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_msg_uptime, 0, 0, 0, 0, 0 }, + { MN_SLIDER, 2, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR3, MNSlider_Ticker, MNSlider_TextualValueUpdateGeometry, MNSlider_TextualValueDrawer, { Hu_MenuCvarSlider, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_msg_uptime, 0, 0, 0, 0, 0 }, + { MN_TEXT, 2, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_msg_size, 0, 0, 0, 0, 0 }, + { MN_SLIDER, 2, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNSlider_Ticker, MNSlider_UpdateGeometry, MNSlider_Drawer, { Hu_MenuCvarSlider, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_msg_size, 0, 0, 0, 0, 0 }, + { MN_TEXT, 2, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_msg_color, 0, 0, 0, 0, 0 }, + { MN_COLORBOX, 2, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNColorBox_Ticker, MNColorBox_UpdateGeometry, MNColorBox_Drawer, { Hu_MenuCvarColorBox, Hu_MenuCvarColorBox, Hu_MenuActivateColorWidget, 0, 0, Hu_MenuDefaultFocusAction }, MNColorBox_CommandResponder, NULL, NULL, &cbox_hud_msg_color, 0, 0, 0, 0, 0 }, + + { MN_TEXT, 3, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR2, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_crosshair, 0, 0, 0, 0, 0 }, + { MN_TEXT, 3, 0, Point2Raw(), 'c',MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_xhair_symbol, 0, 0, 0, 0, 0 }, + { MN_LISTINLINE, 3, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR3, MNListInline_Ticker, MNListInline_UpdateGeometry, MNListInline_Drawer, { Hu_MenuCvarList, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNListInline_CommandResponder, NULL, NULL, &list_hud_xhair_symbol, 0, 0, 0, 0, 0 }, + { MN_TEXT, 3, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_xhair_size, 0, 0, 0, 0, 0 }, + { MN_SLIDER, 3, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNSlider_Ticker, MNSlider_UpdateGeometry, MNSlider_Drawer, { Hu_MenuCvarSlider, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_xhair_size, 0, 0, 0, 0, 0 }, + { MN_TEXT, 3, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_xhair_angle, 0, 0, 0, 0, 0 }, + { MN_SLIDER, 3, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNSlider_Ticker, MNSlider_UpdateGeometry, MNSlider_Drawer, { Hu_MenuCvarSlider, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_xhair_angle, 0, 0, 0, 0, 0 }, + { MN_TEXT, 3, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_xhair_opacity, 0, 0, 0, 0, 0 }, + { MN_SLIDER, 3, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNSlider_Ticker, MNSlider_UpdateGeometry, MNSlider_Drawer, { Hu_MenuCvarSlider, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_xhair_opacity, 0, 0, 0, 0, 0 }, + { MN_TEXT, 3, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_xhair_vitality_color, 0, 0, 0, 0, 0 }, + { MN_BUTTON, 3, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_xhair_vitality_color, 0, 0, 0, 0, 0 }, + { MN_TEXT, 3, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_xhair_color, 0, 0, 0, 0, 0 }, + { MN_COLORBOX, 3, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNColorBox_Ticker, MNColorBox_UpdateGeometry, MNColorBox_Drawer, { Hu_MenuCvarColorBox, Hu_MenuCvarColorBox, Hu_MenuActivateColorWidget, 0, 0, Hu_MenuDefaultFocusAction }, MNColorBox_CommandResponder, NULL, NULL, &cbox_hud_xhair_color, 0, 0, 0, 0, 0 }, + +#if __JDOOM__ || __JHERETIC__ || __JHEXEN__ + { MN_TEXT, 4, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR2, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_statusbar, 0, 0, 0, 0, 0 }, + { MN_TEXT, 4, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_sbar_size, 0, 0, 0, 0, 0 }, + { MN_SLIDER, 4, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNSlider_Ticker, MNSlider_UpdateGeometry, MNSlider_Drawer, { Hu_MenuCvarSlider, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_sbar_size, 0, 0, 0, 0, 0 }, + { MN_TEXT, 4, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_sbar_opacity, 0, 0, 0, 0, 0 }, + { MN_SLIDER, 4, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNSlider_Ticker, MNSlider_UpdateGeometry, MNSlider_Drawer, { Hu_MenuCvarSlider, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_sbar_opacity, 0, 0, 0, 0, 0 }, +#endif +#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ + { MN_TEXT, 5, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR2, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_counters, 0, 0, 0, 0, 0 }, + { MN_TEXT, 5, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_cntr_items, 0, 0, 0, 0, 0 }, + { MN_LISTINLINE, 5, 0, Point2Raw(), 'i',MENU_FONT1, MENU_COLOR3, MNListInline_Ticker, MNListInline_UpdateGeometry, MNListInline_Drawer, { Hu_MenuCvarList, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNListInline_CommandResponder, NULL, NULL, &list_hud_cntr_items, 0, 0, 0, 0, 0 }, + { MN_TEXT, 5, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_cntr_kills, 0, 0, 0, 0, 0 }, + { MN_LISTINLINE, 5, 0, Point2Raw(), 'k',MENU_FONT1, MENU_COLOR3, MNListInline_Ticker, MNListInline_UpdateGeometry, MNListInline_Drawer, { Hu_MenuCvarList, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNListInline_CommandResponder, NULL, NULL, &list_hud_cntr_kills, 0, 0, 0, 0, 0 }, + { MN_TEXT, 5, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_cntr_secrets, 0, 0, 0, 0, 0 }, + { MN_LISTINLINE, 5, 0, Point2Raw(), 's',MENU_FONT1, MENU_COLOR3, MNListInline_Ticker, MNListInline_UpdateGeometry, MNListInline_Drawer, { Hu_MenuCvarList, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNListInline_CommandResponder, NULL, NULL, &list_hud_cntr_secrets, 0, 0, 0, 0, 0 }, + { MN_TEXT, 5, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_cntr_mapopen, 0, 0, 0, 0, 0 }, + { MN_BUTTON, 5, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_cntr_mapopen, 0, 0, 0, 0, 0 }, + { MN_TEXT, 5, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_cntr_size, 0, 0, 0, 0, 0 }, + { MN_SLIDER, 5, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNSlider_Ticker, MNSlider_UpdateGeometry, MNSlider_Drawer, { Hu_MenuCvarSlider, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_cntr_size, 0, 0, 0, 0, 0 }, +#endif + + { MN_TEXT, 6, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR2, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_fullscreen, 0, 0, 0, 0, 0 }, + { MN_TEXT, 6, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_full_size, 0, 0, 0, 0, 0 }, + { MN_SLIDER, 6, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNSlider_Ticker, MNSlider_UpdateGeometry, MNSlider_Drawer, { Hu_MenuCvarSlider, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNSlider_CommandResponder, NULL, NULL, &sld_hud_size, 0, 0, 0, 0, 0 }, + { MN_TEXT, 6, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_full_text_color, 0, 0, 0, 0, 0 }, + { MN_COLORBOX, 6, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNColorBox_Ticker, MNColorBox_UpdateGeometry, MNColorBox_Drawer, { Hu_MenuCvarColorBox, Hu_MenuCvarColorBox, Hu_MenuActivateColorWidget, 0, 0, Hu_MenuDefaultFocusAction }, MNColorBox_CommandResponder, NULL, NULL, &cbox_hud_color, 0, 0, 0, 0, 0 }, +#if __JHEXEN__ + { MN_TEXT, 6, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_full_show_mana, 0, 0, 0, 0, 0 }, + { MN_BUTTON, 6, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_full_show_mana, 0, 0, 0, 0, 0 }, +#endif +#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ + { MN_TEXT, 6, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_full_show_ammo, 0, 0, 0, 0, 0 }, + { MN_BUTTON, 6, 0, Point2Raw(), 'a',MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_full_show_ammo, 0, 0, 0, 0, 0 }, + { MN_TEXT, 6, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_full_show_armor, 0, 0, 0, 0, 0 }, + { MN_BUTTON, 6, 0, Point2Raw(), 'r',MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_full_show_armor, 0, 0, 0, 0, 0 }, +#endif +#if __JDOOM64__ + { MN_TEXT, 6, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_full_show_powerkeys, 0, 0, 0, 0, 0 }, + { MN_BUTTON, 6, 0, Point2Raw(), 'p',MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_full_show_powerkeys, 0, 0, 0, 0, 0 }, +#endif +#if __JDOOM__ + { MN_TEXT, 6, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_full_show_status, 0, 0, 0, 0, 0 }, + { MN_BUTTON, 6, 0, Point2Raw(), 'f',MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_full_show_face, 0, 0, 0, 0, 0 }, +#endif + { MN_TEXT, 6, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_full_show_health, 0, 0, 0, 0, 0 }, + { MN_BUTTON, 6, 0, Point2Raw(), 'h',MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_full_show_health, 0, 0, 0, 0, 0 }, +#if __JDOOM__ || __JDOOM64__ || __JHERETIC__ + { MN_TEXT, 6, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_full_show_keys, 0, 0, 0, 0, 0 }, + { MN_BUTTON, 6, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_full_show_keys, 0, 0, 0, 0, 0 }, +#endif +#if __JHERETIC__ || __JHEXEN__ + { MN_TEXT, 6, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, MNText_Ticker, MNText_UpdateGeometry, MNText_Drawer, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, &txt_hud_full_show_readyitem, 0, 0, 0, 0, 0 }, + { MN_BUTTON, 6, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR3, MNButton_Ticker, MNButton_UpdateGeometry, MNButton_Drawer, { Hu_MenuCvarButton, 0, 0, 0, 0, Hu_MenuDefaultFocusAction }, MNButton_CommandResponder, NULL, NULL, &btn_hud_full_show_readyitem, 0, 0, 0, 0, 0 }, +#endif + { MN_NONE, 0, 0, Point2Raw(), 0, MENU_FONT1, MENU_COLOR1, 0, 0, 0, { 0, 0, 0, 0, 0, 0 }, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0 } +}; + +static dd_bool inited; + +struct pagerecord_t +{ + mn_page_t *page; + ddstring_t name; // Symbolic name. +}; + +static int pageCount; +static pagerecord_t *pages; + +// Cvars for the menu: +cvartemplate_t menuCVars[] = { + { "menu-scale", 0, CVT_FLOAT, &cfg.menuScale, .1f, 1, 0 }, + { "menu-stretch", 0, CVT_BYTE, &cfg.menuScaleMode, SCALEMODE_FIRST, SCALEMODE_LAST, 0 }, + { "menu-flash-r", 0, CVT_FLOAT, &cfg.menuTextFlashColor[CR], 0, 1, 0 }, + { "menu-flash-g", 0, CVT_FLOAT, &cfg.menuTextFlashColor[CG], 0, 1, 0 }, + { "menu-flash-b", 0, CVT_FLOAT, &cfg.menuTextFlashColor[CB], 0, 1, 0 }, + { "menu-flash-speed", 0, CVT_INT, &cfg.menuTextFlashSpeed, 0, 50, 0 }, + { "menu-cursor-rotate", 0, CVT_BYTE, &cfg.menuCursorRotate, 0, 1, 0 }, + { "menu-effect", 0, CVT_INT, &cfg.menuEffectFlags, 0, MEF_EVERYTHING, 0 }, + { "menu-color-r", 0, CVT_FLOAT, &cfg.menuTextColors[0][CR], 0, 1, 0 }, + { "menu-color-g", 0, CVT_FLOAT, &cfg.menuTextColors[0][CG], 0, 1, 0 }, + { "menu-color-b", 0, CVT_FLOAT, &cfg.menuTextColors[0][CB], 0, 1, 0 }, + { "menu-colorb-r", 0, CVT_FLOAT, &cfg.menuTextColors[1][CR], 0, 1, 0 }, + { "menu-colorb-g", 0, CVT_FLOAT, &cfg.menuTextColors[1][CG], 0, 1, 0 }, + { "menu-colorb-b", 0, CVT_FLOAT, &cfg.menuTextColors[1][CB], 0, 1, 0 }, + { "menu-colorc-r", 0, CVT_FLOAT, &cfg.menuTextColors[2][CR], 0, 1, 0 }, + { "menu-colorc-g", 0, CVT_FLOAT, &cfg.menuTextColors[2][CG], 0, 1, 0 }, + { "menu-colorc-b", 0, CVT_FLOAT, &cfg.menuTextColors[2][CB], 0, 1, 0 }, + { "menu-colord-r", 0, CVT_FLOAT, &cfg.menuTextColors[3][CR], 0, 1, 0 }, + { "menu-colord-g", 0, CVT_FLOAT, &cfg.menuTextColors[3][CG], 0, 1, 0 }, + { "menu-colord-b", 0, CVT_FLOAT, &cfg.menuTextColors[3][CB], 0, 1, 0 }, + { "menu-glitter", 0, CVT_FLOAT, &cfg.menuTextGlitter, 0, 1, 0 }, + { "menu-fog", 0, CVT_INT, &cfg.hudFog, 0, 5, 0 }, + { "menu-shadow", 0, CVT_FLOAT, &cfg.menuShadow, 0, 1, 0 }, + { "menu-patch-replacement", 0, CVT_INT, &cfg.menuPatchReplaceMode, PRM_FIRST, PRM_LAST, 0 }, + { "menu-slam", 0, CVT_BYTE, &cfg.menuSlam, 0, 1, 0 }, + { "menu-hotkeys", 0, CVT_BYTE, &cfg.menuShortcutsEnabled, 0, 1, 0 }, +#if __JDOOM__ || __JDOOM64__ + { "menu-quitsound", 0, CVT_INT, &cfg.menuQuitSound, 0, 1, 0 }, +#endif + { "menu-save-suggestname", 0, CVT_BYTE, &cfg.menuGameSaveSuggestDescription, 0, 1, 0 }, + + // Aliases for obsolete cvars: + { "menu-turningskull", 0, CVT_BYTE, &cfg.menuCursorRotate, 0, 1, 0 }, + { "", 0, CVT_NULL, 0, 0, 0, 0 } +}; + +// Console commands for the menu: +ccmdtemplate_t menuCCmds[] = { + { "menu", "s", CCmdMenuOpen, 0 }, + { "menu", "", CCmdMenuOpen, 0 }, + { "menuup", "", CCmdMenuCommand, 0 }, + { "menudown", "", CCmdMenuCommand, 0 }, + { "menupageup", "", CCmdMenuCommand, 0 }, + { "menupagedown", "", CCmdMenuCommand, 0 }, + { "menuleft", "", CCmdMenuCommand, 0 }, + { "menuright", "", CCmdMenuCommand, 0 }, + { "menuselect", "", CCmdMenuCommand, 0 }, + { "menudelete", "", CCmdMenuCommand, 0 }, + { "menuback", "", CCmdMenuCommand, 0 }, + { "", "", 0, 0 } +}; + +void Hu_MenuRegister() +{ + for(int i = 0; menuCVars[i].path[0]; ++i) + { + Con_AddVariable(menuCVars + i); + } + + for(int i = 0; menuCCmds[i].name[0]; ++i) + { + Con_AddCommand(menuCCmds + i); + } +} + +static menucommand_e chooseCloseMethod() +{ + // If we aren't using a transition then we can close normally and allow our + // own menu fade-out animation to be used instead. + return Con_GetInteger("con-transition-tics") == 0? MCMD_CLOSE : MCMD_CLOSEFAST; +} + +mn_page_t* Hu_MenuFindPageByName(const char* name) +{ + if(name && name[0]) + { + int i; + for(i = 0; i < pageCount; ++i) + { + pagerecord_t* rec = pages + i; + if(!stricmp(name, Str_Text(&rec->name))) + { + return rec->page; + } + } + } + return NULL; +} + +/// @todo Make this state an object property flag. +/// @return @c true if the rotation of a cursor on this object should be animated. +static dd_bool Hu_MenuHasCursorRotation(mn_object_t *obj) +{ + assert(obj); + return (!(MNObject_Flags(obj) & MNF_DISABLED) && + (MNObject_Type(obj) == MN_LISTINLINE || MNObject_Type(obj) == MN_SLIDER)); +} + +/// To be called to re-evaluate the state of the cursor (e.g., when focus changes). +static void Hu_MenuUpdateCursorState() +{ + if(menuActive) + { + mn_page_t *page; + mn_object_t *obj; + + if(colorWidgetActive) + page = Hu_MenuFindPageByName("ColorWidget"); + else + page = Hu_MenuActivePage(); + + obj = MNPage_FocusObject(page); + if(obj) + { + cursorHasRotation = Hu_MenuHasCursorRotation(obj); + return; + } + } + cursorHasRotation = false; +} + +void Hu_MenuLoadResources() +{ + char buffer[9]; + int i; + +#if __JDOOM__ || __JDOOM64__ + pMainTitle = R_DeclarePatch("M_DOOM"); +#elif __JHERETIC__ || __JHEXEN__ + pMainTitle = R_DeclarePatch("M_HTIC"); +#endif + +#if __JDOOM__ || __JDOOM64__ + pNewGame = R_DeclarePatch("M_NEWG"); + pSkill = R_DeclarePatch("M_SKILL"); + pEpisode = R_DeclarePatch("M_EPISOD"); + pNGame = R_DeclarePatch("M_NGAME"); + pOptions = R_DeclarePatch("M_OPTION"); + pLoadGame = R_DeclarePatch("M_LOADG"); + pSaveGame = R_DeclarePatch("M_SAVEG"); + pReadThis = R_DeclarePatch("M_RDTHIS"); + pQuitGame = R_DeclarePatch("M_QUITG"); + pOptionsTitle = R_DeclarePatch("M_OPTTTL"); +#endif + +#if __JDOOM__ || __JDOOM64__ + pSkillModeNames[SM_BABY] = R_DeclarePatch("M_JKILL"); + pSkillModeNames[SM_EASY] = R_DeclarePatch("M_ROUGH"); + pSkillModeNames[SM_MEDIUM] = R_DeclarePatch("M_HURT"); + pSkillModeNames[SM_HARD] = R_DeclarePatch("M_ULTRA"); +# if __JDOOM__ + pSkillModeNames[SM_NIGHTMARE] = R_DeclarePatch("M_NMARE"); +# endif +#endif + +#if __JDOOM__ + if(gameModeBits & (GM_DOOM_SHAREWARE|GM_DOOM|GM_DOOM_ULTIMATE)) + { + pEpisodeNames[0] = R_DeclarePatch("M_EPI1"); + pEpisodeNames[1] = R_DeclarePatch("M_EPI2"); + pEpisodeNames[2] = R_DeclarePatch("M_EPI3"); + } + if(gameModeBits & GM_DOOM_ULTIMATE) + { + pEpisodeNames[3] = R_DeclarePatch("M_EPI4"); + } +#endif + +#if __JHERETIC__ + for(i = 0; i < 18; ++i) + { + dd_snprintf(buffer, 9, "M_SKL%02d", i); + pRotatingSkull[i] = R_DeclarePatch(buffer); + } +#endif + +#if __JHEXEN__ + for(i = 0; i < 7; ++i) + { + dd_snprintf(buffer, 9, "FBUL%c0", 'A'+i); + pBullWithFire[i] = R_DeclarePatch(buffer); + } + + pPlayerClassBG[0] = R_DeclarePatch("M_FBOX"); + pPlayerClassBG[1] = R_DeclarePatch("M_CBOX"); + pPlayerClassBG[2] = R_DeclarePatch("M_MBOX"); +#endif + + for(i = 0; i < MENU_CURSOR_FRAMECOUNT; ++i) + { +#if __JDOOM__ || __JDOOM64__ + dd_snprintf(buffer, 9, "M_SKULL%d", i+1); +#else + dd_snprintf(buffer, 9, "M_SLCTR%d", i+1); +#endif + pCursors[i] = R_DeclarePatch(buffer); + } +} + +void Hu_MenuInitColorWidgetPage() +{ +#if __JHERETIC__ || __JHEXEN__ + Point2Raw const origin(98, 60); +#else + Point2Raw const origin(124, 60); +#endif + uint const numObjects = 10; + + mn_page_t *page = Hu_MenuNewPage("ColorWidget", &origin, MPF_NEVER_SCROLL, Hu_MenuPageTicker, NULL, Hu_MenuColorWidgetCmdResponder, NULL); + MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); + + mn_object_t *objects = (mn_object_t *)Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); + mn_object_t *ob = objects; + + ob->_type = MN_COLORBOX; + ob->_flags = MNF_ID0 | MNF_NO_FOCUS; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNColorBox_Ticker; + ob->updateGeometry = MNColorBox_UpdateGeometry; + ob->drawer = MNColorBox_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_colorbox_t), PU_GAMESTATIC, 0); + { + mndata_colorbox_t *cbox = (mndata_colorbox_t *)ob->_typedata; + cbox->width = SCREENHEIGHT / 7; + cbox->height = SCREENHEIGHT / 7; + cbox->rgbaMode = true; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Red"; + } + ob++; + + ob->_type = MN_SLIDER; + ob->_flags = MNF_ID1; + ob->_shortcut = 'r'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNSlider_Ticker; + ob->updateGeometry = MNSlider_UpdateGeometry; + ob->drawer = MNSlider_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuUpdateColorWidgetColor; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNSlider_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); + ob->data2 = CR; + { + mndata_slider_t *sld = (mndata_slider_t *)ob->_typedata; + sld->min = 0; + sld->max = 1; + sld->value = 0; + sld->step = .05f; + sld->floatMode = true; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Green"; + } + ob++; + + ob->_type = MN_SLIDER; + ob->_flags = MNF_ID2; + ob->_shortcut = 'g'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNSlider_Ticker; + ob->updateGeometry = MNSlider_UpdateGeometry; + ob->drawer = MNSlider_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuUpdateColorWidgetColor; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNSlider_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); + ob->data2 = CG; + { + mndata_slider_t *sld = (mndata_slider_t *)ob->_typedata; + sld->min = 0; + sld->max = 1; + sld->value = 0; + sld->step = .05f; + sld->floatMode = true; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Blue"; + } + ob++; + + ob->_type = MN_SLIDER; + ob->_flags = MNF_ID3; + ob->_shortcut = 'b'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNSlider_Ticker; + ob->updateGeometry = MNSlider_UpdateGeometry; + ob->drawer = MNSlider_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuUpdateColorWidgetColor; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNSlider_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); + ob->data2 = CB; + { + mndata_slider_t *sld = (mndata_slider_t *)ob->_typedata; + sld->min = 0; + sld->max = 1; + sld->value = 0; + sld->step = .05f; + sld->floatMode = true; + } + ob++; + + ob->_type = MN_TEXT; + ob->_flags = MNF_ID4; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Opacity"; + } + ob++; + + ob->_type = MN_SLIDER; + ob->_flags = MNF_ID5; + ob->_shortcut = 'o'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNSlider_Ticker; + ob->updateGeometry = MNSlider_UpdateGeometry; + ob->drawer = MNSlider_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuUpdateColorWidgetColor; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNSlider_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); + ob->data2 = CA; + { + mndata_slider_t *sld = (mndata_slider_t *)ob->_typedata; + sld->min = 0; + sld->max = 1; + sld->value = 0; + sld->step = .05f; + sld->floatMode = true; + } + ob++; + + ob->_type = MN_NONE; + + page->objects = objects; +} + +void Hu_MenuInitMainPage() +{ +#if __JHEXEN__ || __JHERETIC__ + Point2Raw origin(110, 56); + uint numObjects = 6; +#else + Point2Raw origin(97, 64); +# if __JDOOM64__ + uint numObjects = 7; +# else + uint numObjects = 8; +# endif +#endif + +#if __JDOOM__ + if(gameModeBits & GM_ANY_DOOM2) + { + origin.y += 8; + } +#endif + +#if __JDOOM__ || __JDOOM64__ + mn_page_t *page = Hu_MenuNewPage("Main", &origin, MPF_LAYOUT_FIXED|MPF_NEVER_SCROLL, Hu_MenuPageTicker, NULL, NULL, NULL); +#else + mn_page_t *page = Hu_MenuNewPage("Main", &origin, MPF_LAYOUT_FIXED|MPF_NEVER_SCROLL, Hu_MenuPageTicker, Hu_MenuDrawMainPage, NULL, NULL); +#endif + MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTB)); + + mn_object_t *objects = (mn_object_t *)Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); + mn_object_t *ob = objects; + + int y = 0; + +#if __JDOOM__ || __JDOOM64__ + ob->_type = MN_TEXT; + ob->_origin.x = -3; + ob->_origin.y = -70; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->patch = &pMainTitle; + } + ob++; +#endif + + ob->_type = MN_BUTTON; + ob->_origin.y = y; + ob->_shortcut = 'n'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->data1 = (void *)"GameType"; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; +#if defined(__JDOOM__) && !defined(__JDOOM64__) + btn->patch = &pNGame; +#else + btn->text = "New Game"; +#endif + } + ob++; y += FIXED_LINE_HEIGHT; + + ob->_type = MN_BUTTON; + ob->_origin.y = y; + ob->_shortcut = 'o'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->data1 = (void *)"Options"; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; +#if defined(__JDOOM__) && !defined(__JDOOM64__) + btn->patch = &pOptions; +#else + btn->text = "Options"; +#endif + } + ob++; y += FIXED_LINE_HEIGHT; + +#if __JDOOM__ || __JDOOM64__ + ob->_type = MN_BUTTON; + ob->_origin.y = y; + ob->_shortcut = 'l'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectLoadGame; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; +# if __JDOOM64__ + btn->text = "Load Game"; +# else + btn->patch = &pLoadGame; +# endif + } + ob++; y += FIXED_LINE_HEIGHT; + + ob->_type = MN_BUTTON; + ob->_origin.y = y; + ob->_shortcut = 's'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectSaveGame; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; +# if __JDOOM64__ + btn->text = "Save Game"; +# else + btn->patch = &pSaveGame; +# endif + } + ob++; y += FIXED_LINE_HEIGHT; + +#else + ob->_type = MN_BUTTON; + ob->_origin.y = y; + ob->_shortcut = 'f'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->data1 = (void *)"Files"; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = "Game Files"; + } + ob++; y += FIXED_LINE_HEIGHT; +#endif + +#if !__JDOOM64__ + ob->_type = MN_BUTTON; + ob->_origin.y = y; +# if __JDOOM__ + ob->_flags = MNF_ID0; + ob->_shortcut = 'r'; +# else + ob->_shortcut = 'i'; +# endif + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectHelp; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; +# if defined(__JDOOM__) + btn->patch = &pReadThis; +# else + btn->text = "Info"; +# endif + } + ob++; y += FIXED_LINE_HEIGHT; +#endif + + ob->_type = MN_BUTTON; +#if __JDOOM__ + ob->_flags = MNF_ID1; +#endif + ob->_origin.y = y; + ob->_shortcut = 'q'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectQuitGame; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; +#if defined(__JDOOM__) && !defined(__JDOOM64__) + btn->patch = &pQuitGame; +#else + btn->text = "Quit Game"; +#endif + } + ob++; y += FIXED_LINE_HEIGHT; + + ob->_type = MN_NONE; + + page->objects = objects; +} + +void Hu_MenuInitGameTypePage() +{ +#if __JDOOM__ || __JDOOM64__ + Point2Raw origin(97, 65); +#else + Point2Raw origin(104, 65); +#endif + uint const numObjects = 3; + + mn_page_t *page = Hu_MenuNewPage("GameType", &origin, 0, Hu_MenuPageTicker, Hu_MenuDrawGameTypePage, NULL, NULL); + MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTB)); + MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Main")); + + mn_object_t *objects = (mn_object_t *)Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); + mn_object_t *ob = objects; + + int y = 0; + + ob->_type = MN_BUTTON; + ob->_origin.y = y; + ob->_shortcut = 's'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectSingleplayer; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = (char const *)TXT_SINGLEPLAYER; + } + ob++; y += FIXED_LINE_HEIGHT; + + ob->_type = MN_BUTTON; + ob->_origin.y = y; + ob->_shortcut = 'm'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectMultiplayer; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = (char const *)TXT_MULTIPLAYER; + } + ob++; y += FIXED_LINE_HEIGHT; + + ob->_type = MN_NONE; + + page->objects = objects; +} + +void Hu_MenuInitSkillPage() +{ +#if __JHEXEN__ + Point2Raw const origin(120, 44); +#elif __JHERETIC__ + Point2Raw const origin(38, 30); +#else + Point2Raw const origin(48, 63); +#endif + int skillButtonFlags[NUM_SKILL_MODES] = { + MNF_ID0, + MNF_ID1, + MNF_ID2 | MNF_DEFAULT, + MNF_ID3, +# if !__JDOOM64__ + MNF_ID4 +# endif + }; +#if !__JHEXEN__ + int skillButtonTexts[NUM_SKILL_MODES] = { + TXT_SKILL1, + TXT_SKILL2, + TXT_SKILL3, + TXT_SKILL4, +# if !__JDOOM64__ + TXT_SKILL5 +# endif + }; +#endif + uint const numObjects = NUM_SKILL_MODES + 1; + + mn_page_t *page = Hu_MenuNewPage("Skill", &origin, MPF_LAYOUT_FIXED|MPF_NEVER_SCROLL, Hu_MenuPageTicker, Hu_MenuDrawSkillPage, NULL, NULL); + MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTB)); +#if __JHEXEN__ + MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("PlayerClass")); +#elif __JHERETIC__ + MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Episode")); +#elif __JDOOM64__ + MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("GameType")); +#else // __JDOOM__ + if(gameModeBits & (GM_ANY_DOOM2|GM_DOOM_CHEX)) + { + MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("GameType")); + } + else + { + MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Episode")); + } +#endif + + mn_object_t *objects = (mn_object_t *)Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); + mn_object_t *ob = objects; + + int y = 0; + + for(uint i = 0; i < NUM_SKILL_MODES; ++i, ob++, y += FIXED_LINE_HEIGHT) + { + ob->_type = MN_BUTTON; + ob->_flags = skillButtonFlags[i]; +#if !__JHEXEN__ + ob->_shortcut = GET_TXT(skillButtonTexts[i])[0]; +#endif + ob->_origin.y = y; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionInitNewGame; + ob->actions[MNA_FOCUS ].callback = Hu_MenuFocusSkillMode; + ob->cmdResponder = MNButton_CommandResponder; + ob->data2 = (int)(SM_BABY + i); + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); +#if !__JHEXEN__ + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = INT2PTR(const char, skillButtonTexts[i]); +# if __JDOOM__ || __JDOOM64__ + btn->patch = &pSkillModeNames[i]; +# endif + } +#endif + } + + ob->_type = MN_NONE; + + page->objects = objects; + +#if __JDOOM__ + if(gameMode != doom2_hacx && gameMode != doom_chex) + { + mn_object_t *ob = MN_MustFindObjectOnPage(page, 0, MNF_ID4); + MNButton_SetFlags(ob, FO_SET, MNBUTTON_NO_ALTTEXT); + } +#endif +} + +void Hu_MenuInitMultiplayerPage() +{ +#if __JHERETIC__ || __JHEXEN__ + Point2Raw const origin(97, 65); +#else + Point2Raw const origin(97, 65); +#endif + uint const numObjects = 3; + + mn_page_t *page = Hu_MenuNewPage("Multiplayer", &origin, 0, Hu_MenuPageTicker, Hu_MenuDrawMultiplayerPage, NULL, NULL); + MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTB)); + MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("GameType")); + + mn_object_t *objects = (mn_object_t *)Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); + mn_object_t *ob = objects; + + ob->_type = MN_BUTTON; + ob->_flags = MNF_ID0; + ob->_shortcut = 'j'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectJoinGame; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = "Join Game"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_shortcut = 's'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectPlayerSetup; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = "Player Setup"; + } + ob++; + + ob->_type = MN_NONE; + + page->objects = objects; +} + +void Hu_MenuInitPlayerSetupPage() +{ +#if __JHERETIC__ || __JHEXEN__ + Point2Raw const origin(70, 44); +#else + Point2Raw const origin(70, 54); +#endif +#if __JHEXEN__ + uint numObjects = 8; +#else + uint numObjects = 6; +#endif + + mn_page_t *page = Hu_MenuNewPage("PlayerSetup", &origin, 0, Hu_MenuPageTicker, Hu_MenuDrawPlayerSetupPage, NULL, NULL); + MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); + MNPage_SetPredefinedFont(page, MENU_FONT2, FID(GF_FONTB)); + MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Multiplayer")); + + mn_object_t *objects = (mn_object_t *)Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); + mn_object_t *ob = objects; + + ob->_type = MN_MOBJPREVIEW; + ob->_origin.x = SCREENWIDTH/2 - origin.x; + ob->_origin.y = 60; + ob->_flags = MNF_ID0 | MNF_POSITION_FIXED; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNMobjPreview_Ticker; + ob->updateGeometry = MNMobjPreview_UpdateGeometry; + ob->drawer = MNMobjPreview_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_mobjpreview_t), PU_GAMESTATIC, 0); + ob++; + + ob->_type = MN_EDIT; + ob->_flags = MNF_ID1 | MNF_LAYOUT_OFFSET; + ob->_origin.y = 75; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNEdit_Ticker; + ob->updateGeometry = MNEdit_UpdateGeometry; + ob->drawer = MNEdit_Drawer; + ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNEdit_CommandResponder; + ob->responder = MNEdit_Responder; + ob->_typedata = Z_Calloc(sizeof(mndata_edit_t), PU_GAMESTATIC, 0); + { + mndata_edit_t *edit = (mndata_edit_t *)ob->_typedata; + Str_Init(&edit->text); + Str_Init(&edit->oldtext); + edit->data1 = (void *)"net-name"; + edit->maxLength = 24; + } + ob++; + +#if __JHEXEN__ + ob->_type = MN_TEXT; + ob->_flags = MNF_LAYOUT_OFFSET; + ob->_origin.y = 5; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Class"; + } + ob++; + + ob->_type = MN_LISTINLINE; + ob->_flags = MNF_ID2; + ob->_shortcut = 'c'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNListInline_Ticker; + ob->updateGeometry = MNListInline_UpdateGeometry; + ob->drawer = MNListInline_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuSelectPlayerSetupPlayerClass; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNListInline_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_list_t), PU_GAMESTATIC, 0); + { + mndata_list_t *list = (mndata_list_t *)ob->_typedata; + list->count = 3; + list->items = (mndata_listitem_t*)Z_Calloc(sizeof(mndata_listitem_t) * list->count, PU_GAMESTATIC, 0); + + { + mndata_listitem_t *item = (mndata_listitem_t *)list->items; + item->text = (char const *)TXT_PLAYERCLASS1; + item->data = PCLASS_FIGHTER; + item++; + + item->text = (char const *)TXT_PLAYERCLASS2; + item->data = PCLASS_CLERIC; + item++; + + item->text = (char const *)TXT_PLAYERCLASS3; + item->data = PCLASS_MAGE; + } + } + ob++; +#endif + + ob->_type = MN_TEXT; +#ifdef __JHERETIC__ + ob->_flags = MNF_LAYOUT_OFFSET; + ob->_origin.y = 5; +#endif + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t*)ob->_typedata; + text->text = "Color"; + } + ob++; + + // Setup the player color selection list. + ob->_type = MN_LISTINLINE; + ob->_flags = MNF_ID3; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNListInline_Ticker; + ob->updateGeometry = MNListInline_UpdateGeometry; + ob->drawer = MNListInline_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuSelectPlayerColor; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNListInline_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_list_t), PU_GAMESTATIC, 0); + { + mndata_list_t *list = (mndata_list_t *)ob->_typedata; +#if __JHEXEN__ + // Hexen v1.0 has only four player colors. + list->count = (gameMode == hexen_v10? 4 : NUMPLAYERCOLORS) + 1/*auto*/; +#else + list->count = NUMPLAYERCOLORS + 1/*auto*/; +#endif + list->items = (mndata_listitem_t *)Z_Calloc(sizeof(mndata_listitem_t) * list->count, PU_GAMESTATIC, 0); + + /// @todo Read these names from Text definitions. + { + mndata_listitem_t *item = (mndata_listitem_t *)list->items; +#if __JHEXEN__ + item->text = "Red"; item->data = 0; + item++; + + item->text = "Blue"; item->data = 1; + item++; + + item->text = "Yellow"; item->data = 2; + item++; + + item->text = "Green"; item->data = 3; + item++; + + // Hexen v1.0 has only four player colors. + if(gameMode != hexen_v10) + { + item->text = "Jade"; item->data = 4; + item++; + + item->text = "White"; item->data = 5; + item++; + + item->text = "Hazel"; item->data = 6; + item++; + + item->text = "Purple"; item->data = 7; + item++; + } + + item->text = "Automatic"; item->data = 8; +#elif __JHERETIC__ + item->text = "Green"; item->data = 0; + item++; + + item->text = "Orange"; item->data = 1; + item++; + + item->text = "Red"; item->data = 2; + item++; + + item->text = "Blue"; item->data = 3; + item++; + + item->text = "Automatic"; item->data = 4; +#else + item->text = "Green"; item->data = 0; + item++; + + item->text = "Indigo"; item->data = 1; + item++; + + item->text = "Brown"; item->data = 2; + item++; + + item->text = "Red"; item->data = 3; + item++; + + item->text = "Automatic"; item->data = 4; +#endif + } + } + ob++; + + ob->_type = MN_BUTTON; + ob->_shortcut = 's'; + ob->_pageFontIdx = MENU_FONT2; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectAcceptPlayerSetup; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = "Save Changes"; + } + ob++; + + ob->_type = MN_NONE; + + page->objects = objects; +} + +void Hu_MenuInitSaveOptionsPage() +{ + Point2Raw const origin(60, 50); +#if !__JHEXEN__ + uint const numObjects = 10; +#else + uint const numObjects = 8; +#endif + + mn_page_t *page = Hu_MenuNewPage("SaveOptions", &origin, 0, Hu_MenuPageTicker, NULL, NULL, NULL); + MNPage_SetTitle(page, "Save Options"); + MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); + MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Options")); + + mn_object_t *objects = (mn_object_t *)Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); + mn_object_t *ob = objects; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t*)ob->_typedata; + text->text = "Confirm quick load/save"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_shortcut = 'q'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"game-save-confirm"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Confirm reborn load"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_shortcut = 'r'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"game-save-confirm-loadonreborn"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR2; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Reborn preferences"; + } + ob++; + +#if !__JHEXEN__ + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Load auto save"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 1; + ob->_shortcut = 'a'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"game-save-auto-loadonreborn"; + } + ob++; +#endif + + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Load last save"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 1; + ob->_shortcut = 'a'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"game-save-last-loadonreborn"; + } + ob++; + + ob->_type = MN_NONE; + + page->objects = objects; +} + +#if __JHERETIC__ || __JHEXEN__ +void Hu_MenuInitFilesPage() +{ + Point2Raw origin(110, 60); + uint const numObjects = 3; + + mn_page_t *page = Hu_MenuNewPage("Files", &origin, MPF_LAYOUT_FIXED|MPF_NEVER_SCROLL, Hu_MenuPageTicker, NULL, NULL, NULL); + MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTB)); + MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Main")); + + mn_object_t *objects = (mn_object_t *)Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); + mn_object_t *ob = objects; + + int y = 0; + + ob->_type = MN_BUTTON; + ob->_origin.y = y; + ob->_shortcut = 'l'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectLoadGame; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = "Load Game"; + } + ob++; y += FIXED_LINE_HEIGHT; + + ob->_type = MN_BUTTON; + ob->_origin.y = y; + ob->_shortcut = 's'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectSaveGame; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = "Save Game"; + } + ob++; y += FIXED_LINE_HEIGHT; + + ob->_type = MN_NONE; + + page->objects = objects; +} +#endif + +static void deleteGameSave(int slot) +{ + DD_Executef(true, "deletegamesave %i", slot); +} + +int Hu_MenuLoadSlotCommandResponder(mn_object_t *ob, menucommand_e cmd) +{ + DENG_ASSERT(ob && ob->_type == MN_EDIT); + if(MCMD_DELETE == cmd && + (ob->_flags & MNF_FOCUS) && !(ob->_flags & MNF_ACTIVE) && !(ob->_flags & MNF_DISABLED)) + { + mndata_edit_t* edit = (mndata_edit_t*)ob->_typedata; + deleteGameSave(edit->data2); + return true; + } + return MNObject_DefaultCommandResponder(ob, cmd); +} + +int Hu_MenuSaveSlotCommandResponder(mn_object_t *ob, menucommand_e cmd) +{ + assert(ob); + if(MCMD_DELETE == cmd && + (ob->_flags & MNF_FOCUS) && !(ob->_flags & MNF_ACTIVE) && !(ob->_flags & MNF_DISABLED)) + { + mndata_edit_t* edit = (mndata_edit_t*)ob->_typedata; + deleteGameSave(edit->data2); + return true; + } + return MNEdit_CommandResponder(ob, cmd); +} + +void Hu_MenuInitLoadGameAndSaveGamePages() +{ +#if __JDOOM__ || __JDOOM64__ + Point2Raw const origin(80, 54); +#else + Point2Raw const origin(70, 30); +#endif + int const saveSlotObjectIds[NUMSAVESLOTS] = { + MNF_ID0, MNF_ID1, MNF_ID2, MNF_ID3, MNF_ID4, MNF_ID5, +#if !__JHEXEN__ + MNF_ID6, MNF_ID7 +#endif + }; + + mndata_edit_t *saveSlots = (mndata_edit_t *)Z_Calloc(sizeof(*saveSlots) * NUMSAVESLOTS, PU_GAMESTATIC, 0); + + for(int i = 0; i < NUMSAVESLOTS; ++i) + { + mndata_edit_t *slot = saveSlots + i; + slot->emptyString = (const char*) TXT_EMPTYSTRING; + slot->data2 = i; + slot->maxLength = 24; + } + + mn_object_t *loadMenuObjects = (mn_object_t *)Z_Calloc(sizeof(*loadMenuObjects) * (NUMSAVESLOTS+1), PU_GAMESTATIC, 0); + + int y = 0; + + int i = 0; + for(; i < NUMSAVESLOTS; ++i, y += FIXED_LINE_HEIGHT) + { + mn_object_t *ob = loadMenuObjects + i; + mndata_edit_t *edit = saveSlots + i; + + ob->_type = MN_EDIT; + ob->_origin.x = 0; + ob->_origin.y = y; + ob->_flags = saveSlotObjectIds[i] | MNF_DISABLED; + ob->_shortcut = '0' + i; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->updateGeometry = MNEdit_UpdateGeometry; + ob->drawer = MNEdit_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectLoadSlot; + ob->actions[MNA_FOCUSOUT ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = Hu_MenuLoadSlotCommandResponder; + ob->_typedata = edit; + ob->data2 = saveSlotObjectIds[i]; + Str_Init(&edit->text); + Str_Init(&edit->oldtext); + } + loadMenuObjects[i]._type = MN_NONE; + + mn_object_t *saveMenuObjects = (mn_object_t *)Z_Calloc(sizeof(*saveMenuObjects) * (NUMSAVESLOTS+1), PU_GAMESTATIC, 0); + + y = 0; + + i = 0; + for(; i < NUMSAVESLOTS; ++i, y += FIXED_LINE_HEIGHT) + { + mn_object_t *ob = saveMenuObjects + i; + mndata_edit_t *edit = saveSlots + i; + + ob->_type = MN_EDIT; + ob->_origin.x = 0; + ob->_origin.y = y; + ob->_flags = saveSlotObjectIds[i]; + ob->_shortcut = '0' + i; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->updateGeometry = MNEdit_UpdateGeometry; + ob->drawer = MNEdit_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectSaveSlot; + ob->actions[MNA_ACTIVE ].callback = Hu_MenuSaveSlotEdit; + ob->actions[MNA_FOCUSOUT ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = Hu_MenuSaveSlotCommandResponder; + ob->responder = MNEdit_Responder; + ob->_typedata = edit; + ob->data2 = saveSlotObjectIds[i]; + } + saveMenuObjects[i]._type = MN_NONE; + + mn_page_t *page = Hu_MenuNewPage("LoadGame", &origin, MPF_LAYOUT_FIXED|MPF_NEVER_SCROLL, Hu_MenuPageTicker, Hu_MenuDrawLoadGamePage, NULL, NULL); + MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); + MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Main")); + page->objects = loadMenuObjects; + + page = Hu_MenuNewPage("SaveGame", &origin, MPF_LAYOUT_FIXED|MPF_NEVER_SCROLL, Hu_MenuPageTicker, Hu_MenuDrawSaveGamePage, NULL, NULL); + MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); + MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Main")); + page->objects = saveMenuObjects; +} + +void Hu_MenuInitOptionsPage() +{ +#if __JHERETIC__ || __JHEXEN__ + Point2Raw const origin(110, 63); +#else + Point2Raw const origin(110, 63); +#endif +#if __JHERETIC__ || __JHEXEN__ + uint const numObjects = 13; +#else + uint const numObjects = 12; +#endif + + mn_page_t *page = Hu_MenuNewPage("Options", &origin, 0, Hu_MenuPageTicker, Hu_MenuDrawOptionsPage, NULL, NULL); + MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); + MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Main")); + + mn_object_t *objects = (mn_object_t *)Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); + mn_object_t *ob = objects; + + ob->_type = MN_BUTTON; + ob->_shortcut = 'e'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectEndGame; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = "End Game"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_shortcut = 't'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectControlPanelLink; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = "Show Taskbar"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_shortcut = 'c'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->data1 = (void *)"ControlOptions"; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = "Controls"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_shortcut = 'g'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->data1 = (void *)"GameplayOptions"; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = "Gameplay"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_shortcut = 's'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->data1 = (void *)"SaveOptions"; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = "Game saves"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_shortcut = 'h'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->data1 = (void *)"HUDOptions"; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = "HUD"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_shortcut = 'a'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->data1 = (void *)"AutomapOptions"; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = "Automap"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_shortcut = 'w'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->data1 = (void *)"WeaponOptions"; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = "Weapons"; + } + ob++; + +#if __JHERETIC__ || __JHEXEN__ + ob->_type = MN_BUTTON; + ob->_shortcut = 'i'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->data1 = (void *)"InventoryOptions"; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = "Inventory"; + } + ob++; +#endif + + ob->_type = MN_BUTTON; + ob->_shortcut = 's'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->data1 = (void *)"SoundOptions"; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = "Sound"; + } + ob++; + + /* + ob->_type = MN_BUTTON; + ob->_shortcut = 'm'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectControlPanelLink; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->data2 = 2; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = "Mouse"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_shortcut = 'j'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectControlPanelLink; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->data2 = 2; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = "Joystick"; + } + ob++;*/ + + ob->_type = MN_NONE; + + page->objects = objects; +} + +void Hu_MenuInitGameplayOptionsPage() +{ +#if __JHEXEN__ + Point2Raw const origin(88, 25); +#elif __JHERETIC__ + Point2Raw const origin(30, 40); +#else + Point2Raw const origin(30, 40); +#endif +#if __JDOOM64__ + uint const numObjects = 40; +#elif __JDOOM__ + uint const numObjects = 42; +#elif __JHERETIC__ + uint const numObjects = 24; +#elif __JHEXEN__ + uint const numObjects = 7; +#endif + + mn_page_t *page = Hu_MenuNewPage("GameplayOptions", &origin, 0, Hu_MenuPageTicker, NULL, NULL, NULL); + MNPage_SetTitle(page, "Gameplay Options"); + MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); + MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Options")); + + mn_object_t *objects = (mn_object_t *)Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); + mn_object_t *ob = objects; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Always Run"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_shortcut = 'r'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t*)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"ctl-run"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t*)ob->_typedata; + text->text = "Use LookSpring"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_shortcut = 'l'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t*)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"ctl-look-spring"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Disable AutoAim"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_shortcut = 'a'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"ctl-aim-noauto"; + } + ob++; + +#if __JDOOM__ || __JHERETIC__ || __JDOOM64__ + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Allow Jumping"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_shortcut = 'j'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"player-jump"; + } + ob++; +#endif + +#if __JDOOM64__ + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Weapon Recoil"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"player-weapon-recoil"; + } + ob++; +#endif + +#if __JDOOM__ || __JHERETIC__ || __JDOOM64__ + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR2; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Compatibility"; + } + ob++; + +# if __JDOOM__ || __JDOOM64__ + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Any Boss Trigger 666"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 1; + ob->_shortcut = 'b'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"game-anybossdeath666"; + } + ob++; + +# if !__JDOOM64__ + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Av Resurrects Ghosts"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 1; + ob->_shortcut = 'g'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"game-raiseghosts"; + } + ob++; + +# if __JDOOM__ + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "VileChase uses Av radius"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 1; + ob->_shortcut = 'g'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"game-vilechase-usevileradius"; + } + ob++; +# endif +# endif // !__JDOOM64__ + + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "PE Limited To 21 Lost Souls"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 1; + ob->_shortcut = 'p'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"game-maxskulls"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "LS Can Get Stuck Inside Walls"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"game-skullsinwalls"; + } + ob++; +# endif // __JDOOM__ || __JDOOM64__ + + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Monsters Fly Over Obstacles"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"game-monsters-floatoverblocking"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Monsters Can Get Stuck In Doors"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 1; + ob->_shortcut = 'd'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"game-monsters-stuckindoors"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Some Objects Never Hang Over Ledges"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 1; + ob->_shortcut = 'h'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"game-objects-neverhangoverledges"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Objects Fall Under Own Weight"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 1; + ob->_shortcut = 'f'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"game-objects-falloff"; + } + ob++; + +#if __JDOOM__ || __JDOOM64__ + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "All Crushed Objects Become A Pile Of Gibs"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 1; + ob->_shortcut = 'g'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"game-objects-gibcrushednonbleeders"; + } + ob++; +#endif + + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Corpses Slide Down Stairs"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 1; + ob->_shortcut = 's'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"game-corpse-sliding"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Use Exactly Doom's Clipping Code"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 1; + ob->_shortcut = 'c'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"game-objects-clipping"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = " ^If Not NorthOnly WallRunning"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 1; + ob->_shortcut = 'w'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"game-player-wallrun-northonly"; + } + ob++; + +# if __JDOOM__ || __JDOOM64__ + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Zombie Players Can Exit Maps"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 1; + ob->_shortcut = 'e'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"game-zombiescanexit"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Fix Ouch Face"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"hud-face-ouchfix"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Fix Weapon Slot Display"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"hud-status-weaponslots-ownedfix"; + } + ob++; +# endif // __JDOOM__ || __JDOOM64__ +#endif // __JDOOM__ || __JHERETIC__ || __JDOOM64__ + + ob->_type = MN_NONE; + + page->objects = objects; +} + +void Hu_MenuInitHUDOptionsPage() +{ +#if __JDOOM__ || __JDOOM64__ + Point2Raw const origin(97, 40); +#else + Point2Raw const origin(97, 28); +#endif + + mn_page_t *page = Hu_MenuNewPage("HudOptions", &origin, 0, Hu_MenuPageTicker, NULL, NULL, NULL); + MNPage_SetTitle(page, "HUD Options"); + MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); + MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Options")); + page->objects = HudMenuObjects; +} + +void Hu_MenuInitAutomapOptionsPage() +{ +#if __JHERETIC__ || __JHEXEN__ + Point2Raw const origin(64, 28); +#else + Point2Raw const origin(70, 40); +#endif +#if __JDOOM64__ + uint const numObjects = 26; +#else + uint const numObjects = 27; +#endif + + mn_page_t *page = Hu_MenuNewPage("AutomapOptions", &origin, 0, Hu_MenuPageTicker, NULL, NULL, NULL); + MNPage_SetTitle(page, "Automap Options"); + MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); + MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Options")); + + mn_object_t *objects = (mn_object_t *)Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); + mn_object_t *ob = objects; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Background Opacity"; + } + ob++; + + ob->_type = MN_SLIDER; + ob->_shortcut = 'o'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNSlider_Ticker; + ob->updateGeometry = MNSlider_UpdateGeometry; + ob->drawer = MNSlider_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarSlider; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNSlider_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); + { + mndata_slider_t *sld = (mndata_slider_t *)ob->_typedata; + sld->min = 0; + sld->max = 1; + sld->value = 0; + sld->step = 0.1f; + sld->floatMode = true; + sld->data1 = (void *)"map-opacity"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Line Opacity"; + } + ob++; + + ob->_type = MN_SLIDER; + ob->_shortcut = 'l'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNSlider_Ticker; + ob->updateGeometry = MNSlider_UpdateGeometry; + ob->drawer = MNSlider_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarSlider; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNSlider_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); + { + mndata_slider_t *sld = (mndata_slider_t *)ob->_typedata; + sld->min = 0; + sld->max = 1; + sld->value = 0; + sld->step = 0.1f; + sld->floatMode = true; + sld->data1 = (void *)"map-line-opacity"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Line Width"; + } + ob++; + + ob->_type = MN_SLIDER; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNSlider_Ticker; + ob->updateGeometry = MNSlider_UpdateGeometry; + ob->drawer = MNSlider_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarSlider; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNSlider_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); + { + mndata_slider_t *sld = (mndata_slider_t *)ob->_typedata; + sld->min = .1f; + sld->max = 2; + sld->value = 0; + sld->step = 0.1f; + sld->floatMode = true; + sld->data1 = (void *)"map-line-width"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "HUD Display"; + } + ob++; + +#if !__JDOOM64__ + ob->_type = MN_LISTINLINE; + ob->_shortcut = 'h'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNListInline_Ticker; + ob->updateGeometry = MNListInline_UpdateGeometry; + ob->drawer = MNListInline_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarList; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNListInline_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_list_t), PU_GAMESTATIC, 0); + { + mndata_list_t *list = (mndata_list_t *)ob->_typedata; + + list->data = (void *)"map-huddisplay"; + list->count = 3; + list->items = (mndata_listitem_t *)Z_Calloc(sizeof(mndata_listitem_t) * list->count, PU_GAMESTATIC, 0); + + mndata_listitem_t *item = (mndata_listitem_t *)list->items; + item->text = "None"; + item->data = 0; + item++; + + item->text = "Current"; + item->data = 1; + item++; + + item->text = "Statusbar"; + item->data = 2; + } + ob++; +#endif + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Door Colors"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_shortcut = 'd'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"map-door-colors"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Door Glow"; + } + ob++; + + ob->_type = MN_SLIDER; + ob->_shortcut = 'g'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNSlider_Ticker; + ob->updateGeometry = MNSlider_UpdateGeometry; + ob->drawer = MNSlider_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarSlider; + ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNSlider_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); + { + mndata_slider_t *sld = (mndata_slider_t *)ob->_typedata; + sld->min = 0; + sld->max = 200; + sld->value = 0; + sld->step = 5; + sld->floatMode = true; + sld->data1 = (void *)"map-door-glow"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Use Custom Colors"; + } + ob++; + + ob->_type = MN_LISTINLINE; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNListInline_Ticker; + ob->updateGeometry = MNListInline_UpdateGeometry; + ob->drawer = MNListInline_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarList; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNListInline_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_list_t), PU_GAMESTATIC, 0); + { + mndata_list_t *list = (mndata_list_t *)ob->_typedata; + + list->count = 3; + list->items = (mndata_listitem_t *)Z_Calloc(sizeof(mndata_listitem_t) * list->count, PU_GAMESTATIC, 0); + list->data = (void *)"map-customcolors"; + + mndata_listitem_t *item = (mndata_listitem_t *)list->items; + item->text = "Never"; + item->data = 0; + item++; + + item->text = "Auto"; + item->data = 1; + item++; + + item->text = "Always"; + item->data = 2; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Wall"; + } + ob++; + + ob->_type = MN_COLORBOX; + ob->_shortcut = 'w'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNColorBox_Ticker; + ob->updateGeometry = MNColorBox_UpdateGeometry; + ob->drawer = MNColorBox_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarColorBox; + ob->actions[MNA_ACTIVE ].callback = Hu_MenuActivateColorWidget; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNColorBox_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_colorbox_t), PU_GAMESTATIC, 0); + { + mndata_colorbox_t *cbox = (mndata_colorbox_t *)ob->_typedata; + cbox->data1 = (void *)"map-wall-r"; + cbox->data2 = (void *)"map-wall-g"; + cbox->data3 = (void *)"map-wall-b"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Floor Height Change"; + } + ob++; + + ob->_type = MN_COLORBOX; + ob->_shortcut = 'f'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNColorBox_Ticker; + ob->updateGeometry = MNColorBox_UpdateGeometry; + ob->drawer = MNColorBox_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarColorBox; + ob->actions[MNA_ACTIVE ].callback = Hu_MenuActivateColorWidget; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNColorBox_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_colorbox_t), PU_GAMESTATIC, 0); + { + mndata_colorbox_t *cbox = (mndata_colorbox_t *)ob->_typedata; + cbox->data1 = (void *)"map-wall-floorchange-r"; + cbox->data2 = (void *)"map-wall-floorchange-g"; + cbox->data3 = (void *)"map-wall-floorchange-b"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Ceiling Height Change"; + } + ob++; + + ob->_type = MN_COLORBOX; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNColorBox_Ticker; + ob->updateGeometry = MNColorBox_UpdateGeometry; + ob->drawer = MNColorBox_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarColorBox; + ob->actions[MNA_ACTIVE ].callback = Hu_MenuActivateColorWidget; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNColorBox_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_colorbox_t), PU_GAMESTATIC, 0); + { + mndata_colorbox_t *cbox = (mndata_colorbox_t *)ob->_typedata; + cbox->data1 = (void *)"map-wall-ceilingchange-r"; + cbox->data2 = (void *)"map-wall-ceilingchange-g"; + cbox->data3 = (void *)"map-wall-ceilingchange-b"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Unseen"; + } + ob++; + + ob->_type = MN_COLORBOX; + ob->_shortcut = 'u'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNColorBox_Ticker; + ob->updateGeometry = MNColorBox_UpdateGeometry; + ob->drawer = MNColorBox_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarColorBox; + ob->actions[MNA_ACTIVE ].callback = Hu_MenuActivateColorWidget; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNColorBox_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_colorbox_t), PU_GAMESTATIC, 0); + { + mndata_colorbox_t *cbox = (mndata_colorbox_t *)ob->_typedata; + cbox->data1 = (void *)"map-wall-unseen-r"; + cbox->data2 = (void *)"map-wall-unseen-g"; + cbox->data3 = (void *)"map-wall-unseen-b"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Thing"; + } + ob++; + + ob->_type = MN_COLORBOX; + ob->_shortcut = 't'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNColorBox_Ticker; + ob->updateGeometry = MNColorBox_UpdateGeometry; + ob->drawer = MNColorBox_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarColorBox; + ob->actions[MNA_ACTIVE ].callback = Hu_MenuActivateColorWidget; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNColorBox_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_colorbox_t), PU_GAMESTATIC, 0); + { + mndata_colorbox_t *cbox = (mndata_colorbox_t *)ob->_typedata; + cbox->data1 = (void *)"map-mobj-r"; + cbox->data2 = (void *)"map-mobj-g"; + cbox->data3 = (void *)"map-mobj-b"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Background"; + } + ob++; + + ob->_type = MN_COLORBOX; + ob->_shortcut = 'b'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNColorBox_Ticker; + ob->updateGeometry = MNColorBox_UpdateGeometry; + ob->drawer = MNColorBox_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarColorBox; + ob->actions[MNA_ACTIVE ].callback = Hu_MenuActivateColorWidget; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNColorBox_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_colorbox_t), PU_GAMESTATIC, 0); + { + mndata_colorbox_t *cbox = (mndata_colorbox_t *)ob->_typedata; + cbox->data1 = (void *)"map-background-r"; + cbox->data2 = (void *)"map-background-g"; + cbox->data3 = (void *)"map-background-b"; + } + ob++; + + ob->_type = MN_NONE; + + page->objects = objects; +} + +static int compareWeaponPriority(void const *_a, void const *_b) +{ + mndata_listitem_t const *a = (mndata_listitem_t const *)_a; + mndata_listitem_t const *b = (mndata_listitem_t const *)_b; + int i = 0, aIndex = -1, bIndex = -1; + + do + { + if(cfg.weaponOrder[i] == a->data) + { + aIndex = i; + } + if(cfg.weaponOrder[i] == b->data) + { + bIndex = i; + } + } while(!(aIndex != -1 && bIndex != -1) && ++i < NUM_WEAPON_TYPES); + + if(aIndex > bIndex) return 1; + if(aIndex < bIndex) return -1; + return 0; // Should never happen. +} + +void Hu_MenuInitWeaponsPage() +{ +#if __JDOOM__ || __JDOOM64__ + Point2Raw const origin(78, 40); +#elif __JHERETIC__ + Point2Raw const origin(78, 26); +#elif __JHEXEN__ + Point2Raw const origin(78, 38); +#endif +#if __JDOOM__ || __JDOOM64__ + uint const numObjects = 17; +#elif __JHERETIC__ || __JHEXEN__ + uint const numObjects = 15; +#endif + const struct { + char const *text; + weapontype_t data; + } weaponOrder[NUM_WEAPON_TYPES+1] = { +#if __JDOOM__ || __JDOOM64__ + { (char const *)TXT_WEAPON1, WT_FIRST }, + { (char const *)TXT_WEAPON2, WT_SECOND }, + { (char const *)TXT_WEAPON3, WT_THIRD }, + { (char const *)TXT_WEAPON4, WT_FOURTH }, + { (char const *)TXT_WEAPON5, WT_FIFTH }, + { (char const *)TXT_WEAPON6, WT_SIXTH }, + { (char const *)TXT_WEAPON7, WT_SEVENTH }, + { (char const *)TXT_WEAPON8, WT_EIGHTH }, + { (char const *)TXT_WEAPON9, WT_NINETH }, +# if __JDOOM64__ + { (char const *)TXT_WEAPON10, WT_TENTH }, +# endif +#elif __JHERETIC__ + { (char const *)TXT_TXT_WPNSTAFF, WT_FIRST }, + { (char const *)TXT_TXT_WPNWAND, WT_SECOND }, + { (char const *)TXT_TXT_WPNCROSSBOW, WT_THIRD }, + { (char const *)TXT_TXT_WPNBLASTER, WT_FOURTH }, + { (char const *)TXT_TXT_WPNSKULLROD, WT_FIFTH }, + { (char const *)TXT_TXT_WPNPHOENIXROD, WT_SIXTH }, + { (char const *)TXT_TXT_WPNMACE, WT_SEVENTH }, + { (char const *)TXT_TXT_WPNGAUNTLETS, WT_EIGHTH }, +#elif __JHEXEN__ + /// @todo We should allow different weapon preferences per player-class. + { "First", WT_FIRST }, + { "Second", WT_SECOND }, + { "Third", WT_THIRD }, + { "Fourth", WT_FOURTH }, +#endif + { "", WT_NOCHANGE} + }; + + mn_page_t *page = Hu_MenuNewPage("WeaponOptions", &origin, 0, Hu_MenuPageTicker, Hu_MenuDrawWeaponsPage, NULL, NULL); + MNPage_SetTitle(page, "Weapons Options"); + MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); + MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Options")); + + mn_object_t *objects = (mn_object_t *)Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); + mn_object_t *ob = objects; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR2; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Priority Order"; + } + ob++; + + ob->_type = MN_LIST; + ob->_flags = MNF_ID0; + ob->_shortcut = 'p'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNList_Ticker; + ob->updateGeometry = MNList_UpdateGeometry; + ob->drawer = MNList_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuChangeWeaponPriority; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNList_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_list_t), PU_GAMESTATIC, 0); + { + mndata_list_t *list = (mndata_list_t *)ob->_typedata; + + list->count = NUM_WEAPON_TYPES; + list->items = (mndata_listitem_t *)Z_Calloc(sizeof(mndata_listitem_t) * list->count, PU_GAMESTATIC, 0); + + mndata_listitem_t *item = (mndata_listitem_t *)list->items; + for(int i = 0; weaponOrder[i].data < NUM_WEAPON_TYPES; ++i, item++) + { + item->text = weaponOrder[i].text; + item->data = weaponOrder[i].data; + } + qsort(list->items, list->count, sizeof(mndata_listitem_t), compareWeaponPriority); + } + ob++; + + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR2; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Cycling"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Use Priority Order"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 1; + ob->_shortcut = 'o'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"player-weapon-nextmode"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Sequential"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 1; + ob->_shortcut = 's'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"player-weapon-cycle-sequential"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_group = 2; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR2; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Autoswitch"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_group = 2; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Pickup Weapon"; + } + ob++; + + ob->_type = MN_LISTINLINE; + ob->_group = 2; + ob->_shortcut = 'w'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNListInline_Ticker; + ob->updateGeometry = MNListInline_UpdateGeometry; + ob->drawer = MNListInline_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarList; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNListInline_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_list_t), PU_GAMESTATIC, 0); + { + mndata_list_t *list = (mndata_list_t *)ob->_typedata; + + list->count = 3; + list->items = (mndata_listitem_t *)Z_Calloc(sizeof(mndata_listitem_t) * list->count, PU_GAMESTATIC, 0); + list->data = (void *)"player-autoswitch"; + + mndata_listitem_t *item = (mndata_listitem_t *)list->items; + item->text = "Never"; + item->data = 0; + item++; + + item->text = "If Better"; + item->data = 1; + item++; + + item->text = "Always"; + item->data = 2; + } + ob++; + + ob->_type = MN_TEXT; + ob->_group = 2; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = " If Not Firing"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 2; + ob->_shortcut = 'f'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"player-autoswitch-notfiring"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_group = 2; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Pickup Ammo"; + } + ob++; + + ob->_type = MN_LISTINLINE; + ob->_group = 2; + ob->_shortcut = 'a'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNListInline_Ticker; + ob->updateGeometry = MNListInline_UpdateGeometry; + ob->drawer = MNListInline_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarList; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNListInline_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_list_t), PU_GAMESTATIC, 0); + { + mndata_list_t *list = (mndata_list_t *)ob->_typedata; + + list->count = 3; + list->items = (mndata_listitem_t *)Z_Calloc(sizeof(mndata_listitem_t) * list->count, PU_GAMESTATIC, 0); + list->data = (void *)"player-autoswitch-ammo"; + + mndata_listitem_t *item = (mndata_listitem_t *)list->items; + item->text = "Never"; + item->data = 0; + item++; + + item->text = "If Better"; + item->data = 1; + item++; + + item->text = "Always"; + item->data = 2; + } + ob++; + +#if __JDOOM__ || __JDOOM64__ + ob->_type = MN_TEXT; + ob->_group = 2; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Pickup Beserk"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 2; + ob->_shortcut = 'b'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"player-autoswitch-berserk"; + } + ob++; +#endif + + ob->_type = MN_NONE; + + page->objects = objects; +} + +#if __JHERETIC__ || __JHEXEN__ +void Hu_MenuInitInventoryOptionsPage() +{ + Point2Raw const origin(78, 48); + uint const numObjects = 16; + + mn_page_t *page = Hu_MenuNewPage("InventoryOptions", &origin, 0, Hu_MenuPageTicker, NULL, NULL, NULL); + MNPage_SetTitle(page, "Inventory Options"); + MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); + MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Options")); + + mn_object_t *objects = (mn_object_t *)Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); + mn_object_t *ob = objects; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Select Mode"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_shortcut = 's'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"ctl-inventory-mode"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Wrap Around"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_shortcut = 'w'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"ctl-inventory-wrap"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Choose And Use"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_shortcut = 'c'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"ctl-inventory-use-immediate"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Select Next If Use Failed"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_shortcut = 'n'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"ctl-inventory-use-next"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "AutoHide"; + } + ob++; + + ob->_type = MN_SLIDER; + ob->_shortcut = 'h'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNSlider_Ticker; + ob->updateGeometry = MNSlider_TextualValueUpdateGeometry; + ob->drawer = MNSlider_TextualValueDrawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarSlider; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNSlider_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); + { + mndata_slider_t *sld = (mndata_slider_t *)ob->_typedata; + sld->min = 0; + sld->max = 30; + sld->value = 0; + sld->step = 1.f; + sld->floatMode = true; + sld->data1 = (void *)"hud-inventory-timer"; + sld->data2 = (void *)"Disabled"; + sld->data4 = (void *)" second"; + sld->data5 = (void *)" seconds"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR2; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Fullscreen HUD"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Max Visible Slots"; + } + ob++; + + ob->_type = MN_SLIDER; + ob->_group = 1; + ob->_shortcut = 'v'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNSlider_Ticker; + ob->updateGeometry = MNSlider_TextualValueUpdateGeometry; + ob->drawer = MNSlider_TextualValueDrawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarSlider; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNSlider_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); + { + mndata_slider_t *sld = (mndata_slider_t *)ob->_typedata; + sld->min = 0; + sld->max = 16; + sld->value = 0; + sld->step = 1; + sld->floatMode = false; + sld->data1 = (void *)"hud-inventory-slot-max"; + sld->data2 = (void *)"Automatic"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_group = 1; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Show Empty Slots"; + } + ob++; + + ob->_type = MN_BUTTON; + ob->_group = 1; + ob->_shortcut = 'e'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR3; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarButton; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->staydownMode = true; + btn->data = (void *)"hud-inventory-slot-showempty"; + } + ob++; + + ob->_type = MN_NONE; + + page->objects = objects; +} +#endif + +void Hu_MenuInitSoundOptionsPage() +{ +#if __JHEXEN__ + Point2Raw const origin(97, 25); +#elif __JHERETIC__ + Point2Raw const origin(97, 30); +#elif __JDOOM__ || __JDOOM64__ + Point2Raw const origin(97, 40); +#endif + uint const numObjects = 6; + + mn_page_t *page = Hu_MenuNewPage("SoundOptions", &origin, 0, Hu_MenuPageTicker, NULL, NULL, NULL); + MNPage_SetTitle(page, "Sound Options"); + MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTA)); + MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("Options")); + + mn_object_t *objects = (mn_object_t *)Z_Calloc(sizeof(*objects) * numObjects, PU_GAMESTATIC, 0); + mn_object_t *ob = objects; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "SFX Volume"; + } + ob++; + + ob->_type = MN_SLIDER; + ob->_shortcut = 's'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNSlider_Ticker; + ob->updateGeometry = MNSlider_UpdateGeometry; + ob->drawer = MNSlider_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarSlider; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNSlider_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); + { + mndata_slider_t *sld = (mndata_slider_t *)ob->_typedata; + sld->min = 0; + sld->max = 255; + sld->value = 0; + sld->step = 5; + sld->floatMode = false; + sld->data1 = (void *)"sound-volume"; + } + ob++; + + ob->_type = MN_TEXT; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNText_Ticker; + ob->updateGeometry = MNText_UpdateGeometry; + ob->drawer = MNText_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_text_t), PU_GAMESTATIC, 0); + { + mndata_text_t *text = (mndata_text_t *)ob->_typedata; + text->text = "Music Volume"; + } + ob++; + + ob->_type = MN_SLIDER; + ob->_shortcut = 'm'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNSlider_Ticker; + ob->updateGeometry = MNSlider_UpdateGeometry; + ob->drawer = MNSlider_Drawer; + ob->actions[MNA_MODIFIED].callback = Hu_MenuCvarSlider; + ob->actions[MNA_FOCUS].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNSlider_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_slider_t), PU_GAMESTATIC, 0); + { + mndata_slider_t *sld = (mndata_slider_t *)ob->_typedata; + sld->min = 0; + sld->max = 255; + sld->value = 0; + sld->step = 5; + sld->floatMode = false; + sld->data1 = (void *)"music-volume"; + } + ob++; + + /* + ob->_type = MN_BUTTON; + ob->_shortcut = 'p'; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->ticker = MNButton_Ticker; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->drawer = MNButton_Drawer; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectControlPanelLink; + ob->actions[MNA_FOCUS ].callback = Hu_MenuDefaultFocusAction; + ob->cmdResponder = MNButton_CommandResponder; + ob->_typedata = Z_Calloc(sizeof(mndata_button_t), PU_GAMESTATIC, 0); + ob->data2 = 1; + { + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + btn->text = "Open Audio Panel"; + } + ob++; + */ + + ob->_type = MN_NONE; + + page->objects = objects; +} + +#if __JDOOM__ || __JHERETIC__ +/** + * Construct the episode selection menu. + */ +void Hu_MenuInitEpisodePage() +{ +#if __JDOOM__ + Point2Raw const origin(48, 63); +#else + Point2Raw const origin(80, 50); +#endif + + int numEpisodes; +#if __JDOOM__ + if(gameModeBits & (GM_ANY_DOOM2|GM_DOOM_CHEX)) + numEpisodes = 0; + else if(gameMode == doom_ultimate) + numEpisodes = 4; + else + numEpisodes = 3; +#else // __JHERETIC__ + if(gameMode == heretic_extended) + numEpisodes = 6; + else + numEpisodes = 3; +#endif + + // Allocate the menu objects array. + mn_object_t *objects = (mn_object_t *)Z_Calloc( sizeof(mn_object_t) * (numEpisodes + 1), PU_GAMESTATIC, 0); + mndata_button_t *buttons = (mndata_button_t *)Z_Calloc(sizeof(mndata_button_t) * (numEpisodes), PU_GAMESTATIC, 0); + + mn_object_t *ob = objects; + mndata_button_t *btn = buttons; + + int y = 0; + + for(int i = 0; i < numEpisodes; ++i) + { + ob->_type = MN_BUTTON; + ob->_origin.x = 0; + ob->_origin.y = y; + + btn->text = GET_TXT(TXT_EPISODE1 + i); + if(isalnum(btn->text[0])) + { + ob->_shortcut = tolower(btn->text[0]); + } +#if __JDOOM__ + btn->patch = &pEpisodeNames[i]; +#endif + + ob->_typedata = btn; + ob->ticker = MNButton_Ticker; + ob->drawer = MNButton_Drawer; + ob->cmdResponder = MNButton_CommandResponder; + ob->updateGeometry = MNButton_UpdateGeometry; + + if(i != 0 +#if __JHERETIC__ + && gameMode == heretic_shareware +#else + && gameMode == doom_shareware +#endif + ) + { + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActivateNotSharewareEpisode; + } + else + { + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuActionSetActivePage; + ob->data1 = (void *)"Skill"; +#if __JHERETIC__ + if(gameMode == heretic_extended && i == 5) + { + ob->_flags |= MNF_ID0; + } +#endif + } + + ob->actions[MNA_FOCUS].callback = Hu_MenuFocusEpisode; + ob->data2 = i; + ob->_pageFontIdx = MENU_FONT1; + ob++; + btn++; + y += FIXED_LINE_HEIGHT; + } + ob->_type = MN_NONE; + + mn_page_t *page = Hu_MenuNewPage("Episode", &origin, MPF_LAYOUT_FIXED|MPF_NEVER_SCROLL, Hu_MenuPageTicker, Hu_MenuDrawEpisodePage, NULL, NULL); + MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTB)); + MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("GameType")); + + page->objects = objects; +} +#endif + +#if __JHEXEN__ +/** + * Construct the player class selection menu. + */ +void Hu_MenuInitPlayerClassPage() +{ + Point2Raw const pageOrigin(66, 66); + + // First determine the number of selectable player classes. + int count = 0; + for(int i = 0; i < NUM_PLAYER_CLASSES; ++i) + { + classinfo_t *info = PCLASS_INFO(i); + if(info->userSelectable) + { + ++count; + } + } + + // Allocate the menu objects. + mn_object_t *objects = (mn_object_t *)Z_Calloc( sizeof(mn_object_t) * (count+4), PU_GAMESTATIC, 0); + mndata_button_t *buttons = (mndata_button_t *)Z_Calloc(sizeof(mndata_button_t) * (count+1), PU_GAMESTATIC, 0); + + uint y = 0; + + // Add the selectable classes. + mn_object_t *ob = objects; + mndata_button_t *btn = buttons; + + int n = 0; + while(n < count) + { + classinfo_t *info = PCLASS_INFO(n++); + + if(!info->userSelectable) continue; + + ob->_type = MN_BUTTON; + + btn->text = info->niceName; + + ob->_typedata = btn; + ob->_origin.x = 0; + ob->_origin.y = y; + ob->drawer = MNButton_Drawer; + ob->ticker = MNButton_Ticker; + ob->cmdResponder = MNButton_CommandResponder; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectPlayerClass; + ob->actions[MNA_FOCUS ].callback = Hu_MenuFocusOnPlayerClass; + ob->data2 = (int)info->plrClass; + ob->_shortcut = tolower(btn->text[0]); + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + + ob++; + btn++; + y += FIXED_LINE_HEIGHT; + } + + // Random class button. + ob->_type = MN_BUTTON; + + btn->text = GET_TXT(TXT_RANDOMPLAYERCLASS); + + ob->_typedata = btn; + ob->_origin.x = 0; + ob->_origin.y = y; + ob->drawer = MNButton_Drawer; + ob->ticker = MNButton_Ticker; + ob->cmdResponder = MNButton_CommandResponder; + ob->updateGeometry = MNButton_UpdateGeometry; + ob->actions[MNA_ACTIVEOUT].callback = Hu_MenuSelectPlayerClass; + ob->actions[MNA_FOCUS ].callback = Hu_MenuFocusOnPlayerClass; + ob->data2 = (int)PCLASS_NONE; + ob->_shortcut = tolower(btn->text[0]); + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob++; + + // Mobj preview background. + ob->_type = MN_RECT; + ob->_flags = MNF_NO_FOCUS|MNF_ID1; + ob->_origin.x = 108; + ob->_origin.y = -58; + ob->drawer = MNRect_Drawer; + ob->ticker = Hu_MenuPlayerClassBackgroundTicker; + ob->updateGeometry = MNRect_UpdateGeometry; + ob->_pageFontIdx = MENU_FONT1; + ob->_pageColorIdx = MENU_COLOR1; + ob->_typedata = Z_Calloc(sizeof(mndata_rect_t), PU_GAMESTATIC, 0); + ob++; + + // Mobj preview. + ob->_type = MN_MOBJPREVIEW; + ob->_flags = MNF_ID0; + ob->_origin.x = 108 + 55; + ob->_origin.y = -58 + 76; + ob->ticker = Hu_MenuPlayerClassPreviewTicker; + ob->updateGeometry = MNMobjPreview_UpdateGeometry; + ob->drawer = MNMobjPreview_Drawer; + ob->_typedata = Z_Calloc(sizeof(mndata_mobjpreview_t), PU_GAMESTATIC, 0); + ob++; + + // Terminate. + ob->_type = MN_NONE; + + mn_page_t *page = Hu_MenuNewPage("PlayerClass", &pageOrigin, MPF_LAYOUT_FIXED|MPF_NEVER_SCROLL, Hu_MenuPageTicker, Hu_MenuDrawPlayerClassPage, NULL, NULL); + MNPage_SetPredefinedFont(page, MENU_FONT1, FID(GF_FONTB)); + MNPage_SetPreviousPage(page, Hu_MenuFindPageByName("GameType")); + + page->objects = objects; +} +#endif + +mn_page_t *MNPage_New(Point2Raw const *origin, int flags, + void (*ticker) (struct mn_page_s *page), + void (*drawer) (struct mn_page_s *page, Point2Raw const *origin), + int (*cmdResponder) (struct mn_page_s *page, menucommand_e cmd), + void *userData) +{ + mn_page_t *page = (mn_page_t *) M_Malloc(sizeof(*page)); + + page->origin.x = origin? origin->x : 0; + page->origin.y = origin? origin->y : 0; + page->flags = flags; + page->objects = 0; + page->objectsCount = 0; + page->ticker = ticker; + page->drawer = drawer; + page->cmdResponder = cmdResponder; + page->previous = 0; + page->userData = userData; + Str_Init(&page->title); + + fontid_t fontId = FID(GF_FONTA); + for(int i = 0; i < MENU_FONT_COUNT; ++i) + { + page->fonts[i] = fontId; + } + + de::zap(page->colors); + page->colors[0] = 0; + page->colors[1] = 1; + page->colors[2] = 2; + page->focus = -1; /// @todo Make this a page flag. + page->geometry = Rect_New(); + + return page; +} + +static mn_page_t *addPageToCollection(mn_page_t *page, char const *name) +{ + if(page) + { + pages = (pagerecord_t *)M_Realloc(pages, sizeof(*pages) * ++pageCount); + + pagerecord_t *rec = &pages[pageCount-1]; + rec->page = page; + Str_Init(&rec->name); Str_Set(&rec->name, name); + } + return page; +} + +mn_page_t* Hu_MenuNewPage(const char* name, Point2Raw const *origin, int flags, + void (*ticker) (struct mn_page_s* page), + void (*drawer) (struct mn_page_s* page, Point2Raw const *origin), + int (*cmdResponder) (struct mn_page_s* page, menucommand_e cmd), + void* userData) +{ + if(!name || !name[0]) + { + DENG_ASSERT(!"Hu_MenuNewPage: Attempt to create page with an invalid name"); + return NULL; + } + + return addPageToCollection(MNPage_New(origin, flags, ticker, drawer, cmdResponder, userData), name); +} + +void Hu_MenuInit() +{ + cvarbutton_t* cvb; + + if(inited) return; + + pageCount = 0; + pages = NULL; + + mnAlpha = mnTargetAlpha = 0; + menuActivePage = NULL; + menuActive = false; + cursorHasRotation = false; + cursorAngle = 0; + cursorAnimFrame = 0; + cursorAnimCounter = MENU_CURSOR_TICSPERFRAME; + + DD_Execute(true, "deactivatebcontext menu"); + + Hu_MenuLoadResources(); + + // Set default Yes/No strings. + for(cvb = mnCVarButtons; cvb->cvarname; cvb++) + { + if(!cvb->yes) cvb->yes = "Yes"; + if(!cvb->no) cvb->no = "No"; + } + + initAllPages(); + initAllObjectsOnAllPages(); + +#if __JDOOM__ + if(gameModeBits & GM_ANY_DOOM2) + { + mn_object_t *ob = MN_MustFindObjectOnPage(Hu_MenuFindPageByName("Main"), 0, MNF_ID0); // Read This! + MNObject_SetFlags(ob, FO_SET, MNF_DISABLED|MNF_HIDDEN|MNF_NO_FOCUS); + + ob = MN_MustFindObjectOnPage(Hu_MenuFindPageByName("Main"), 0, MNF_ID1); // Quit Game + MNObject_SetFixedY(ob, MNObject_FixedY(ob) - FIXED_LINE_HEIGHT); + } +#endif + + inited = true; +} + +void Hu_MenuShutdown() +{ + if(!inited) return; + + destroyAllPages(); + inited = false; +} + +dd_bool Hu_MenuIsActive() +{ + return menuActive; +} + +void Hu_MenuSetAlpha(float alpha) +{ + // The menu's alpha will start moving towards this target value. + mnTargetAlpha = alpha; +} + +float Hu_MenuAlpha() +{ + return mnAlpha; +} + +void Hu_MenuTicker(timespan_t ticLength) +{ +#define MENUALPHA_FADE_STEP (.07f) + + float diff = 0; + + // Move towards the target alpha level for the entire menu. + diff = mnTargetAlpha - mnAlpha; + if(fabs(diff) > MENUALPHA_FADE_STEP) + { + mnAlpha += (float)(MENUALPHA_FADE_STEP * ticLength * TICRATE * (diff > 0? 1 : -1)); + } + else + { + mnAlpha = mnTargetAlpha; + } + + if(!menuActive) return; + + // Animate cursor rotation? + if(cfg.menuCursorRotate) + { + if(cursorHasRotation) + { + cursorAngle += (float)(5 * ticLength * TICRATE); + } + else if(cursorAngle != 0) + { + float rewind = (float)(MENU_CURSOR_REWIND_SPEED * ticLength * TICRATE); + if(cursorAngle <= rewind || cursorAngle >= 360 - rewind) + cursorAngle = 0; + else if(cursorAngle < 180) + cursorAngle -= rewind; + else + cursorAngle += rewind; + } + + if(cursorAngle >= 360) + cursorAngle -= 360; + } + + // Time to think? Updates on 35Hz game ticks. + if(!DD_IsSharpTick()) return; + + // Advance menu time. + menuTime++; + + // Animate the cursor graphic? + if(--cursorAnimCounter <= 0) + { + cursorAnimFrame++; + cursorAnimCounter = MENU_CURSOR_TICSPERFRAME; + if(cursorAnimFrame > MENU_CURSOR_FRAMECOUNT-1) + cursorAnimFrame = 0; + } + + // Used for Heretic's rotating skulls. + frame = (menuTime / 3) % 18; + + // Call the active page's ticker. + menuActivePage->ticker(menuActivePage); + +#undef MENUALPHA_FADE_STEP +} + +mn_page_t* Hu_MenuActivePage() +{ + return menuActivePage; +} + +void Hu_MenuSetActivePage2(mn_page_t *page, dd_bool canReactivate) +{ + if(!menuActive) return; + if(!page) return; + + if(!(Get(DD_DEDICATED) || Get(DD_NOVIDEO))) + { + FR_ResetTypeinTimer(); + } + + cursorAngle = 0; // Stop cursor rotation animation dead (don't rewind). + menuNominatingQuickSaveSlot = false; + + if(menuActivePage == page) + { + if(!canReactivate) return; + MNPage_ClearFocusObject(page); + } + + updatePageObjects(page); + + // This is now the "active" page. + menuActivePage = page; + MNPage_Initialize(page); +} + +void Hu_MenuSetActivePage(mn_page_t *page) +{ + Hu_MenuSetActivePage2(page, false/*don't reactivate*/); +} + +dd_bool Hu_MenuIsVisible() +{ + return (menuActive || mnAlpha > .0001f); +} + +int Hu_MenuDefaultFocusAction(mn_object_t * /*ob*/, mn_actionid_t action, void * /*context*/) +{ + if(MNA_FOCUS != action) return 1; + Hu_MenuUpdateCursorState(); + return 0; +} + +void Hu_MenuDrawFocusCursor(int x, int y, int focusObjectHeight, float alpha) +{ +#if __JDOOM__ || __JDOOM64__ +# define OFFSET_X (-22) +# define OFFSET_Y (-2) +#elif __JHERETIC__ || __JHEXEN__ +# define OFFSET_X (-16) +# define OFFSET_Y (3) +#endif + + const int cursorIdx = cursorAnimFrame; + const float angle = cursorAngle; + patchid_t pCursor = pCursors[cursorIdx % MENU_CURSOR_FRAMECOUNT]; + float scale, pos[2]; + patchinfo_t info; + + if(!R_GetPatchInfo(pCursor, &info)) + return; + + scale = MIN_OF((float) (focusObjectHeight * 1.267f) / info.geometry.size.height, 1); + pos[VX] = x + OFFSET_X * scale; + pos[VY] = y + OFFSET_Y * scale + focusObjectHeight/2; + + DGL_MatrixMode(DGL_MODELVIEW); + DGL_PushMatrix(); + + DGL_Translatef(pos[VX], pos[VY], 0); + DGL_Scalef(scale, scale, 1); + DGL_Rotatef(angle, 0, 0, 1); + + DGL_Enable(DGL_TEXTURE_2D); + DGL_Color4f(1, 1, 1, alpha); + + GL_DrawPatchXY3(pCursor, 0, 0, 0, DPF_NO_OFFSET); + + DGL_Disable(DGL_TEXTURE_2D); + + DGL_MatrixMode(DGL_MODELVIEW); + DGL_PopMatrix(); + +#undef OFFSET_Y +#undef OFFSET_X +} + +void Hu_MenuDrawPageTitle(const char* title, int x, int y) +{ + if(!title || !title[0]) return; + + DGL_Enable(DGL_TEXTURE_2D); + FR_SetFont(FID(GF_FONTB)); + FR_SetColorv(cfg.menuTextColors[0]); + FR_SetAlpha(mnRendState->pageAlpha); + + FR_DrawTextXY3(title, x, y, ALIGN_TOP, MN_MergeMenuEffectWithDrawTextFlags(0)); + + DGL_Disable(DGL_TEXTURE_2D); +} + +void Hu_MenuDrawPageHelp(const char* help, int x, int y) +{ + if(!help || !help[0]) return; + + DGL_Enable(DGL_TEXTURE_2D); + FR_SetFont(FID(GF_FONTA)); + FR_SetColorv(cfg.menuTextColors[1]); + FR_SetAlpha(mnRendState->pageAlpha); + + FR_DrawTextXY3(help, x, y, ALIGN_BOTTOM, MN_MergeMenuEffectWithDrawTextFlags(0)); + + DGL_Disable(DGL_TEXTURE_2D); +} + +static void drawOverlayBackground(float darken) +{ + DGL_SetNoMaterial(); + DGL_DrawRectf2Color(0, 0, SCREENWIDTH, SCREENHEIGHT, 0, 0, 0, darken); +} + +static void beginOverlayDraw() +{ +#define SMALL_SCALE .75f + + DGL_MatrixMode(DGL_MODELVIEW); + DGL_PushMatrix(); + + DGL_Translatef(SCREENWIDTH/2, SCREENHEIGHT/2, 0); + DGL_Scalef(SMALL_SCALE, SMALL_SCALE, 1); + DGL_Translatef(-(SCREENWIDTH/2), -(SCREENHEIGHT/2), 0); + +#undef SMALL_SCALE +} + +static void endOverlayDraw() +{ + DGL_MatrixMode(DGL_MODELVIEW); + DGL_PopMatrix(); +} + +void Hu_MenuDrawer() +{ +#define OVERLAY_DARKEN .7f + + dgl_borderedprojectionstate_t bp; + dd_bool showFocusCursor = true; + mn_object_t* focusObj; + + if(!Hu_MenuIsVisible()) return; + + GL_ConfigureBorderedProjection(&bp, 0, SCREENWIDTH, SCREENHEIGHT, + Get(DD_WINDOW_WIDTH), Get(DD_WINDOW_HEIGHT), scalemode_t(cfg.menuScaleMode)); + GL_BeginBorderedProjection(&bp); + + // First determine whether the focus cursor should be visible. + focusObj = MNPage_FocusObject(Hu_MenuActivePage()); + if(focusObj && (MNObject_Flags(focusObj) & MNF_ACTIVE)) + { + if(MNObject_Type(focusObj) == MN_COLORBOX || MNObject_Type(focusObj) == MN_BINDINGS) + { + showFocusCursor = false; + } + } + + DGL_MatrixMode(DGL_MODELVIEW); + DGL_PushMatrix(); + + DGL_Translatef(SCREENWIDTH/2, SCREENHEIGHT/2, 0); + DGL_Scalef(cfg.menuScale, cfg.menuScale, 1); + DGL_Translatef(-(SCREENWIDTH/2), -(SCREENHEIGHT/2), 0); + + MN_DrawPage(Hu_MenuActivePage(), mnAlpha, showFocusCursor); + + DGL_MatrixMode(DGL_MODELVIEW); + DGL_PopMatrix(); + + GL_EndBorderedProjection(&bp); + + // Drawing any overlays? + if(focusObj && (MNObject_Flags(focusObj) & MNF_ACTIVE)) + { + switch(MNObject_Type(focusObj)) + { + case MN_COLORBOX: + case MN_BINDINGS: + drawOverlayBackground(OVERLAY_DARKEN); + GL_BeginBorderedProjection(&bp); + + beginOverlayDraw(); + if(MNObject_Type(focusObj) == MN_BINDINGS) + { + Hu_MenuControlGrabDrawer(MNBindings_ControlName(focusObj), 1); + } + else + { + MN_DrawPage(Hu_MenuFindPageByName("ColorWidget"), 1, true); + } + endOverlayDraw(); + + GL_EndBorderedProjection(&bp); + break; + default: break; + } + } + +#undef OVERLAY_DARKEN +} + +void Hu_MenuPageTicker(mn_page_t *page) +{ + // Normal ticker actions first. + MNPage_Ticker(page); + + /// @todo Move game-menu specific page tick functionality here. +} + +void Hu_MenuNavigatePage(mn_page_t * /*page*/, int /*pageDelta*/) +{ +#if 0 + DENG2_ASSERT(page != 0); + + int index = MAX_OF(0, page->focus); + + oldIndex = index; + + if(pageDelta < 0) + { + index = MAX_OF(0, index - page->numVisObjects); + } + else + { + index = MIN_OF(page->objectsCount-1, index + page->numVisObjects); + } + + // Don't land on empty objects. + while((page->objects[index].flags & (MNF_DISABLED | MNF_NO_FOCUS)) && (index > 0)) + index--; + while((page->objects[index].flags & (MNF_DISABLED | MNF_NO_FOCUS)) && index < page->objectsCount) + index++; + + if(index != oldIndex) + { + S_LocalSound(SFX_MENU_NAV_RIGHT, NULL); + MNPage_SetFocus(page, page->objects + index); + } +#endif +} + +static void initPageObjects(mn_page_t *page) +{ + mn_object_t *ob; + assert(page); + + page->objectsCount = 0; + + for(ob = page->objects; MNObject_Type(ob) != MN_NONE; ob++) + { + page->objectsCount += 1; + + ob->_page = page; + ob->_geometry = Rect_New(); + + ob->timer = 0; + MNObject_SetFlags(ob, FO_CLEAR, MNF_FOCUS); + + if(0 != ob->_shortcut) + { + int shortcut = ob->_shortcut; + ob->_shortcut = 0; // Clear invalid defaults. + MNObject_SetShortcut(ob, shortcut); + } + + switch(MNObject_Type(ob)) + { + case MN_TEXT: { + mndata_text_t *txt = (mndata_text_t *)ob->_typedata; + MNObject_SetFlags(ob, FO_SET, MNF_NO_FOCUS); + + if(txt->text && (PTR2INT(txt->text) > 0 && PTR2INT(txt->text) < NUMTEXT)) + { + txt->text = GET_TXT(PTR2INT(txt->text)); + } + break; } + + case MN_BUTTON: { + /*mn_actioninfo_t const *action = MNObject_Action(ob, MNA_MODIFIED);*/ + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + + if(btn->text && (PTR2INT(btn->text) > 0 && PTR2INT(btn->text) < NUMTEXT)) + { + btn->text = GET_TXT(PTR2INT(btn->text)); + /// @todo Should not be done here. + MNObject_SetShortcut(ob, btn->text[0]); + } + break; } + + case MN_EDIT: { + mndata_edit_t *edit = (mndata_edit_t *) ob->_typedata; + + if(edit->emptyString && (PTR2INT(edit->emptyString) > 0 && PTR2INT(edit->emptyString) < NUMTEXT)) + { + edit->emptyString = GET_TXT(PTR2INT(edit->emptyString)); + } + break; } + + case MN_LIST: + case MN_LISTINLINE: { + mndata_list_t *list = (mndata_list_t *) ob->_typedata; + + for(int i = 0; i < list->count; ++i) + { + mndata_listitem_t *item = &((mndata_listitem_t *)list->items)[i]; + if(item->text && (PTR2INT(item->text) > 0 && PTR2INT(item->text) < NUMTEXT)) + { + item->text = GET_TXT(PTR2INT(item->text)); + } + } + break; } + + case MN_COLORBOX: { + mndata_colorbox_t *cbox = (mndata_colorbox_t *) ob->_typedata; + + if(!cbox->rgbaMode) + cbox->a = 1.f; + if(0 >= cbox->width) + cbox->width = MNDATA_COLORBOX_WIDTH; + if(0 >= cbox->height) + cbox->height = MNDATA_COLORBOX_HEIGHT; + break; } + + case MN_MOBJPREVIEW: + MNObject_SetFlags(ob, FO_SET, MNF_NO_FOCUS); + break; + + default: break; + } + } +} + +/** + * Main task is to update objects linked to cvars. + */ +static void updatePageObjects(mn_page_t *page) +{ + DENG_ASSERT(page != 0); + + mn_object_t *ob; + for(ob = page->objects; MNObject_Type(ob) != MN_NONE; ob++) + { + switch(MNObject_Type(ob)) + { + case MN_TEXT: + case MN_MOBJPREVIEW: + MNObject_SetFlags(ob, FO_SET, MNF_NO_FOCUS); + break; + + case MN_BUTTON: { + mn_actioninfo_t const *action = MNObject_Action(ob, MNA_MODIFIED); + mndata_button_t *btn = (mndata_button_t *)ob->_typedata; + + if(action && action->callback == Hu_MenuCvarButton) + { + cvarbutton_t *cvb; + if(ob->data1) + { + // This button has already been initialized. + cvb = (cvarbutton_t *) ob->data1; + cvb->active = (Con_GetByte(cvb->cvarname) & (cvb->mask? cvb->mask : ~0)) != 0; + //strcpy(obj->text, cvb->active ? cvb->yes : cvb->no); + btn->text = cvb->active ? cvb->yes : cvb->no; + continue; + } + + // Find the cvarbutton representing this one. + for(cvb = mnCVarButtons; cvb->cvarname; cvb++) + { + if(!strcmp((char const *)btn->data, cvb->cvarname) && ob->data2 == cvb->mask) + { + cvb->active = (Con_GetByte(cvb->cvarname) & (cvb->mask? cvb->mask : ~0)) != 0; + ob->data1 = (void*) cvb; + + btn->yes = cvb->yes; + btn->no = cvb->no; + btn->text = (cvb->active ? btn->yes : btn->no); + break; + } + } + cvb = 0; + } + break; } + + case MN_LIST: + case MN_LISTINLINE: { + mn_actioninfo_t const *action = MNObject_Action(ob, MNA_MODIFIED); + mndata_list_t *list = (mndata_list_t *) ob->_typedata; + + if(action && action->callback == Hu_MenuCvarList) + { + MNList_SelectItemByValue(ob, MNLIST_SIF_NO_ACTION, Con_GetInteger((char const *)list->data)); + } + break; } + + case MN_EDIT: { + mn_actioninfo_t const *action = MNObject_Action(ob, MNA_MODIFIED); + mndata_edit_t *edit = (mndata_edit_t *) ob->_typedata; + + if(action && action->callback == Hu_MenuCvarEdit) + { + MNEdit_SetText(ob, MNEDIT_STF_NO_ACTION, Con_GetString((char const *)edit->data1)); + } + break; } + + case MN_SLIDER: { + mn_actioninfo_t const *action = MNObject_Action(ob, MNA_MODIFIED); + mndata_slider_t *sldr = (mndata_slider_t *) ob->_typedata; + if(action && action->callback == Hu_MenuCvarSlider) + { + float value; + if(sldr->floatMode) + value = Con_GetFloat((char const *)sldr->data1); + else + value = Con_GetInteger((char const *)sldr->data1); + MNSlider_SetValue(ob, MNSLIDER_SVF_NO_ACTION, value); + } + break; } + + case MN_COLORBOX: { + mndata_colorbox_t *cbox = (mndata_colorbox_t *) ob->_typedata; + mn_actioninfo_t const *action = MNObject_Action(ob, MNA_MODIFIED); + + if(action && action->callback == Hu_MenuCvarColorBox) + { + float rgba[4]; + rgba[CR] = Con_GetFloat((char const *)cbox->data1); + rgba[CG] = Con_GetFloat((char const *)cbox->data2); + rgba[CB] = Con_GetFloat((char const *)cbox->data3); + rgba[CA] = (cbox->rgbaMode? Con_GetFloat((char const *)cbox->data4) : 1.f); + MNColorBox_SetColor4fv(ob, MNCOLORBOX_SCF_NO_ACTION, rgba); + } + break; } + + default: break; + } + } +} + +static void destroyPageObjects(mn_page_t *page) +{ + mn_object_t *obj; + if(!page) return; + for(obj = page->objects; MNObject_Type(obj) != MN_NONE; obj++) + { + if(obj->_geometry) + { + Rect_Delete(obj->_geometry); + obj->_geometry = NULL; + } + } +} + +static void destroyPage(mn_page_t *page) +{ + if(!page) return; + + destroyPageObjects(page); + + Str_Free(&page->title); + + if(page->geometry) + { + Rect_Delete(page->geometry); + page->geometry = NULL; + } + + free(page); +} + +static void initAllPages() +{ + Hu_MenuInitColorWidgetPage(); + Hu_MenuInitMainPage(); + Hu_MenuInitGameTypePage(); +#if __JDOOM__ || __JHERETIC__ + Hu_MenuInitEpisodePage(); +#endif +#if __JHEXEN__ + Hu_MenuInitPlayerClassPage(); +#endif + Hu_MenuInitSkillPage(); + Hu_MenuInitMultiplayerPage(); + Hu_MenuInitPlayerSetupPage(); +#if __JHERETIC__ || __JHEXEN__ + Hu_MenuInitFilesPage(); +#endif + Hu_MenuInitLoadGameAndSaveGamePages(); + Hu_MenuInitOptionsPage(); + Hu_MenuInitGameplayOptionsPage(); + Hu_MenuInitSaveOptionsPage(); + Hu_MenuInitHUDOptionsPage(); + Hu_MenuInitAutomapOptionsPage(); + Hu_MenuInitWeaponsPage(); +#if __JHERETIC__ || __JHEXEN__ + Hu_MenuInitInventoryOptionsPage(); +#endif + Hu_MenuInitSoundOptionsPage(); + Hu_MenuInitControlsPage(); +} + +static void destroyAllPages() +{ + int i; + if(!pages) return; + for(i = 0; i < pageCount; ++i) + { + pagerecord_t* rec = pages + i; + destroyPage(rec->page); + Str_Free(&rec->name); + } + free(pages); +} + +static void initAllObjectsOnAllPages() +{ + int i; + for(i = 0; i < pageCount; ++i) + { + pagerecord_t* rec = pages + i; + initPageObjects(rec->page); + } +} + +int Hu_MenuColorWidgetCmdResponder(mn_page_t *page, menucommand_e cmd) +{ + assert(page); + switch(cmd) + { + case MCMD_NAV_OUT: { + mn_object_t *obj = (mn_object_t*)page->userData; + MNObject_SetFlags(obj, FO_CLEAR, MNF_ACTIVE); + S_LocalSound(SFX_MENU_CANCEL, NULL); + colorWidgetActive = false; + + /// @kludge We should re-focus on the object instead. + cursorAngle = 0; // Stop cursor rotation animation dead (don't rewind). + Hu_MenuUpdateCursorState(); + /// kludge end. + return true; + } + case MCMD_NAV_PAGEUP: + case MCMD_NAV_PAGEDOWN: + return true; // Eat these. + case MCMD_SELECT: { + mn_object_t *obj = (mn_object_t*)page->userData; + MNObject_SetFlags(obj, FO_CLEAR, MNF_ACTIVE); + S_LocalSound(SFX_MENU_ACCEPT, NULL); + colorWidgetActive = false; + MNColorBox_CopyColor(obj, 0, MN_MustFindObjectOnPage(page, 0, MNF_ID0)); + + /// @kludge We should re-focus on the object instead. + cursorAngle = 0; // Stop cursor rotation animation dead (don't rewind). + Hu_MenuUpdateCursorState(); + /// kludge end. + return true; + } + default: + break; + } + return false; +} + +static void fallbackCommandResponder(mn_page_t *page, menucommand_e cmd) +{ + assert(page); + switch(cmd) + { + case MCMD_NAV_PAGEUP: + case MCMD_NAV_PAGEDOWN: + S_LocalSound(cmd == MCMD_NAV_PAGEUP? SFX_MENU_NAV_UP : SFX_MENU_NAV_DOWN, NULL); + Hu_MenuNavigatePage(page, cmd == MCMD_NAV_PAGEUP? -1 : +1); + break; + + case MCMD_NAV_UP: + case MCMD_NAV_DOWN: { + mn_object_t *obj = MNPage_FocusObject(page); + // An object on this page must have focus in order to navigate. + if(obj) + { + int i = 0, giveFocus = page->focus; + do + { + giveFocus += (cmd == MCMD_NAV_UP? -1 : 1); + if(giveFocus < 0) + giveFocus = page->objectsCount - 1; + else if(giveFocus >= page->objectsCount) + giveFocus = 0; + } while(++i < page->objectsCount && (MNObject_Flags(page->objects + giveFocus) & (MNF_DISABLED | MNF_NO_FOCUS | MNF_HIDDEN))); + + if(giveFocus != page->focus) + { + S_LocalSound(cmd == MCMD_NAV_UP? SFX_MENU_NAV_UP : SFX_MENU_NAV_DOWN, NULL); + MNPage_SetFocus(page, page->objects + giveFocus); + } + } + break; + } + case MCMD_NAV_OUT: + if(!page->previous) + { + S_LocalSound(SFX_MENU_CLOSE, NULL); + Hu_MenuCommand(MCMD_CLOSE); + } + else + { + S_LocalSound(SFX_MENU_CANCEL, NULL); + Hu_MenuSetActivePage(page->previous); + } + break; + default: +// DEBUG_Message("Warning: fallbackCommandResponder: Command %i not processed, ignoring.\n", (int) cmd); + break; + } +} + +/// Depending on the current menu state some commands require translating. +static menucommand_e translateCommand(menucommand_e cmd) +{ + // If a close command is received while currently working with a selected + // "active" widget - interpret the command instead as "navigate out". + if(menuActive && (cmd == MCMD_CLOSE || cmd == MCMD_CLOSEFAST)) + { + mn_object_t *obj = MNPage_FocusObject(Hu_MenuActivePage()); + if(obj) + { + switch(MNObject_Type(obj)) + { + case MN_EDIT: + case MN_LIST: + case MN_COLORBOX: + if(MNObject_Flags(obj) & MNF_ACTIVE) + { + cmd = MCMD_NAV_OUT; + } + break; + default: + break; + } + } + } + return cmd; +} + +void Hu_MenuCommand(menucommand_e cmd) +{ + mn_page_t *page; + mn_object_t *obj; + + cmd = translateCommand(cmd); + + // Determine the page which will respond to this command. + if(colorWidgetActive) + page = Hu_MenuFindPageByName("ColorWidget"); + else + page = Hu_MenuActivePage(); + + if(cmd == MCMD_CLOSE || cmd == MCMD_CLOSEFAST) + { + if(menuActive) + { + //BusyMode_FreezeGameForBusyMode(); + + menuNominatingQuickSaveSlot = false; + + Hu_FogEffectSetAlphaTarget(0); + + if(cmd == MCMD_CLOSEFAST) + { // Hide the menu instantly. + mnAlpha = mnTargetAlpha = 0; + } + else + { + mnTargetAlpha = 0; + } + + if(cmd != MCMD_CLOSEFAST) + S_LocalSound(SFX_MENU_CLOSE, NULL); + + menuActive = false; + + // Disable the menu binding context. + DD_Execute(true, "deactivatebcontext menu"); + } + return; + } + + // No other commands are responded to once shutdown has begun. + if(G_QuitInProgress()) + { + return; + } + + if(!menuActive) + { + if(MCMD_OPEN == cmd) + { + // If anyone is currently chatting; the menu cannot be opened. + int i; + for(i = 0; i < MAXPLAYERS; ++i) + { + if(ST_ChatIsActive(i)) + return; + } + + S_LocalSound(SFX_MENU_OPEN, NULL); + + //Con_Open(false); + + Hu_FogEffectSetAlphaTarget(1); + Hu_MenuSetAlpha(1); + menuActive = true; + menuTime = 0; + + menuActivePage = NULL; // Always re-activate this page. + Hu_MenuSetActivePage(Hu_MenuFindPageByName("Main")); + + // Enable the menu binding class + DD_Execute(true, "activatebcontext menu"); + B_SetContextFallback("menu", Hu_MenuFallbackResponder); + } + return; + } + + // Try the current focus object. + obj = MNPage_FocusObject(page); + if(obj && obj->cmdResponder) + { + if(obj->cmdResponder(obj, cmd)) + return; + } + + // Try the page's cmd responder. + if(page->cmdResponder) + { + if(page->cmdResponder(page, cmd)) + return; + } + + fallbackCommandResponder(page, cmd); +} + +int Hu_MenuPrivilegedResponder(event_t* ev) +{ + if(Hu_MenuIsActive()) + { + mn_object_t *obj = MNPage_FocusObject(Hu_MenuActivePage()); + if(obj && !(MNObject_Flags(obj) & MNF_DISABLED)) + { + if(obj->privilegedResponder) + { + return obj->privilegedResponder(obj, ev); + } + } + } + return false; +} + +int Hu_MenuResponder(event_t* ev) +{ + if(Hu_MenuIsActive()) + { + mn_object_t *obj = MNPage_FocusObject(Hu_MenuActivePage()); + if(obj && !(MNObject_Flags(obj) & MNF_DISABLED)) + { + if(obj->responder) + { + return obj->responder(obj, ev); + } + } + } + return false; // Not eaten. +} + +int Hu_MenuFallbackResponder(event_t* ev) +{ + mn_page_t *page = Hu_MenuActivePage(); + + if(!Hu_MenuIsActive() || !page) return false; + + if(cfg.menuShortcutsEnabled) + { + if(ev->type == EV_KEY && (ev->state == EVS_DOWN || ev->state == EVS_REPEAT)) + { + int i; + for(i = 0; i < page->objectsCount; ++i) + { + mn_object_t *obj = &page->objects[i]; + if(MNObject_Flags(obj) & (MNF_DISABLED | MNF_NO_FOCUS | MNF_HIDDEN)) + continue; + + if(MNObject_Shortcut(obj) == ev->data1) + { + MNPage_SetFocus(page, obj); + return true; + } + } + } + } + return false; +} + +/** + * User wants to load this game + */ +int Hu_MenuSelectLoadSlot(mn_object_t *obj, mn_actionid_t action, void * /*context*/) +{ + mndata_edit_t *edit = (mndata_edit_t *)obj->_typedata; + int const saveSlot = edit->data2; + mn_page_t *saveGamePage; + + if(MNA_ACTIVEOUT != action) return 1; + + saveGamePage = Hu_MenuFindPageByName("SaveGame"); + MNPage_SetFocus(saveGamePage, MNPage_FindObject(saveGamePage, 0, obj->data2)); + + G_LoadGame(saveSlot); + Hu_MenuCommand(chooseCloseMethod()); + return 0; +} + +#if __JHERETIC__ || __JHEXEN__ +void Hu_MenuDrawMainPage(mn_page_t * /*page*/, Point2Raw const *origin) +{ +#define TITLEOFFSET_X (-22) +#define TITLEOFFSET_Y (-56) + +#if __JHEXEN__ + int frame = (menuTime / 5) % 7; +#endif + + DGL_Enable(DGL_TEXTURE_2D); + DGL_Color4f(1, 1, 1, mnRendState->pageAlpha); + FR_SetFont(FID(GF_FONTB)); + FR_SetColorAndAlpha(1, 1, 1, mnRendState->pageAlpha); + + WI_DrawPatchXY3(pMainTitle, Hu_ChoosePatchReplacement(patchreplacemode_t(cfg.menuPatchReplaceMode), pMainTitle), + origin->x + TITLEOFFSET_X, origin->y + TITLEOFFSET_Y, ALIGN_TOPLEFT, 0, MN_MergeMenuEffectWithDrawTextFlags(0)); +#if __JHEXEN__ + GL_DrawPatchXY(pBullWithFire[(frame + 2) % 7], origin->x - 73, origin->y + 24); + GL_DrawPatchXY(pBullWithFire[frame], origin->x + 168, origin->y + 24); +#elif __JHERETIC__ + GL_DrawPatchXY(pRotatingSkull[17 - frame], origin->x - 70, origin->y - 46); + GL_DrawPatchXY(pRotatingSkull[frame], origin->x + 122, origin->y - 46); +#endif + + DGL_Disable(DGL_TEXTURE_2D); + +#undef TITLEOFFSET_Y +#undef TITLEOFFSET_X +} +#endif + +void Hu_MenuDrawGameTypePage(mn_page_t * /*page*/, Point2Raw const *origin) +{ + Hu_MenuDrawPageTitle(GET_TXT(TXT_PICKGAMETYPE), SCREENWIDTH/2, origin->y - 28); +} + +#if __JHERETIC__ +static void composeNotDesignedForMessage(char const *str) +{ + char *buf = notDesignedForMessage; + char tmp[2]; + + buf[0] = 0; + tmp[1] = 0; + + // Get the message template. + char const *in = GET_TXT(TXT_NOTDESIGNEDFOR); + + for(; *in; in++) + { + if(in[0] == '%') + { + if(in[1] == '1') + { + strcat(buf, str); + in++; + continue; + } + + if(in[1] == '%') + in++; + } + tmp[0] = *in; + strcat(buf, tmp); + } +} +#endif + +#if __JHEXEN__ +/** + * A specialization of MNRect_Ticker() which implements the animation logic + * for the player class selection page's player visual background. + */ +void Hu_MenuPlayerClassBackgroundTicker(mn_object_t *ob) +{ + DENG_ASSERT(ob != 0); + + // Determine our selection according to the current focus object. + /// @todo Do not search for the focus object, flag the "random" + /// state through a focus action. + if(mn_object_t *mop = MNPage_FocusObject(MNObject_Page(ob))) + { + playerclass_t pClass = (playerclass_t) mop->data2; + if(pClass == PCLASS_NONE) + { + // Random class. + /// @todo Use this object's timer instead of menuTime. + pClass = playerclass_t(menuTime / 5); + } + + /// @todo Only change here if in the "random" state. + pClass = playerclass_t(int(pClass) % 3); // Number of user-selectable classes. + + MNRect_SetBackgroundPatch(ob, pPlayerClassBG[pClass]); + } + + // Call MNRect's ticker now we've done our own processing. + MNRect_Ticker(ob); +} + +/** + * A specialization of MNMobjPreview_Ticker() which implements the animation + * logic for the player class selection page's player visual. + */ +void Hu_MenuPlayerClassPreviewTicker(mn_object_t *ob) +{ + DENG_ASSERT(ob != 0); + + // Determine our selection according to the current focus object. + /// @todo Do not search for the focus object, flag the "random" + /// state through a focus action. + if(mn_object_t *mop = MNPage_FocusObject(MNObject_Page(ob))) + { + playerclass_t pClass = (playerclass_t) mop->data2; + if(pClass == PCLASS_NONE) + { + // Random class. + /// @todo Use this object's timer instead of menuTime. + pClass = playerclass_t(PCLASS_FIRST + (menuTime / 5)); + pClass = playerclass_t(int(pClass) % 3); // Number of user-selectable classes. + + MNMobjPreview_SetPlayerClass(ob, pClass); + MNMobjPreview_SetMobjType(ob, PCLASS_INFO(pClass)->mobjType); + } + + // Fighter is Yellow, others Red by default. + MNMobjPreview_SetTranslationClass(ob, pClass); + MNMobjPreview_SetTranslationMap(ob, pClass == PCLASS_FIGHTER? 2 : 0); + } + + // Call MNMobjPreview's ticker now we've done our own processing. + MNMobjPreview_Ticker(ob); +} + +void Hu_MenuDrawPlayerClassPage(mn_page_t * /*page*/, Point2Raw const *origin) +{ + DGL_Enable(DGL_TEXTURE_2D); + FR_SetFont(FID(GF_FONTB)); + FR_SetColorAndAlpha(cfg.menuTextColors[0][CR], cfg.menuTextColors[0][CG], cfg.menuTextColors[0][CB], mnRendState->pageAlpha); + + FR_DrawTextXY3("Choose class:", origin->x - 32, origin->y - 42, ALIGN_TOPLEFT, + MN_MergeMenuEffectWithDrawTextFlags(0)); + + DGL_Disable(DGL_TEXTURE_2D); +} +#endif + +#if __JDOOM__ || __JHERETIC__ +void Hu_MenuDrawEpisodePage(mn_page_t *page, Point2Raw const *origin) +{ +#if __JHERETIC__ + DENG2_UNUSED(origin); + + // Inform the user episode 6 is designed for deathmatch only. + mn_object_t *obj = MNPage_FindObject(page, 0, MNF_ID0); + if(obj && obj == MNPage_FocusObject(page)) + { + Point2Raw origin; + + composeNotDesignedForMessage(GET_TXT(TXT_SINGLEPLAYER)); + + origin.x = SCREENWIDTH/2; + origin.y = (SCREENHEIGHT/2) + ((SCREENHEIGHT/2-5)/cfg.menuScale); + + Hu_MenuDrawPageHelp(notDesignedForMessage, origin.x, origin.y); + } +#else // __JDOOM__ + DENG2_UNUSED(page); + + DGL_Enable(DGL_TEXTURE_2D); + DGL_Color4f(1, 1, 1, mnRendState->pageAlpha); + + FR_SetFont(FID(GF_FONTB)); + FR_SetColorv(cfg.menuTextColors[0]); + FR_SetAlpha(mnRendState->pageAlpha); + + WI_DrawPatchXY3(pEpisode, Hu_ChoosePatchReplacement(patchreplacemode_t(cfg.menuPatchReplaceMode), pEpisode), + origin->x + 7, origin->y - 25, ALIGN_TOPLEFT, 0, MN_MergeMenuEffectWithDrawTextFlags(0)); + + DGL_Disable(DGL_TEXTURE_2D); +#endif +} +#endif + +void Hu_MenuDrawSkillPage(mn_page_t * /*page*/, Point2Raw const *origin) +{ +#if __JDOOM__ || __JDOOM64__ + DGL_Enable(DGL_TEXTURE_2D); + DGL_Color4f(1, 1, 1, mnRendState->pageAlpha); + FR_SetFont(FID(GF_FONTB)); + FR_SetColorAndAlpha(cfg.menuTextColors[0][CR], cfg.menuTextColors[0][CG], cfg.menuTextColors[0][CB], mnRendState->pageAlpha); + + WI_DrawPatchXY3(pNewGame, Hu_ChoosePatchReplacement(patchreplacemode_t(cfg.menuPatchReplaceMode), pNewGame), + origin->x + 48, origin->y - 49, ALIGN_TOPLEFT, 0, MN_MergeMenuEffectWithDrawTextFlags(0)); + WI_DrawPatchXY3(pSkill, Hu_ChoosePatchReplacement(patchreplacemode_t(cfg.menuPatchReplaceMode), pSkill), + origin->x + 6, origin->y - 25, ALIGN_TOPLEFT, 0, MN_MergeMenuEffectWithDrawTextFlags(0)); + + DGL_Disable(DGL_TEXTURE_2D); +#elif __JHEXEN__ + Hu_MenuDrawPageTitle("Choose Skill Level:", origin->x + 36, origin->y - 28); +#else + DENG2_UNUSED(origin); +#endif +} + +void Hu_MenuUpdateGameSaveWidgets() +{ + int const saveSlotObjectIds[NUMSAVESLOTS] = { + MNF_ID0, MNF_ID1, MNF_ID2, MNF_ID3, MNF_ID4, MNF_ID5, +#if !__JHEXEN__ + MNF_ID6, MNF_ID7 +#endif + }; + + if(!menuActive) return; + + // Prompt a refresh of the game-save info. We don't yet actively monitor + // the contents of the game-save paths, so instead we settle for manual + // updates whenever the save/load menu is opened. + SV_SaveSlots().updateAll(); + + // Update widgets. + mn_page_t *page = Hu_MenuFindPageByName("LoadGame"); + for(int i = 0; i < NUMSAVESLOTS; ++i) + { + mn_object_t *obj = MN_MustFindObjectOnPage(page, 0, saveSlotObjectIds[i]); + mndata_edit_t *edit = (mndata_edit_t *) obj->_typedata; + + MNObject_SetFlags(obj, FO_SET, MNF_DISABLED); + SaveSlot &sslot = SV_SaveSlots()[edit->data2]; + if(sslot.isUsed()) + { + MNEdit_SetText(obj, MNEDIT_STF_NO_ACTION, sslot.saveInfo().userDescription().toUtf8().constData()); + MNObject_SetFlags(obj, FO_CLEAR, MNF_DISABLED); + } + else + { + MNEdit_SetText(obj, MNEDIT_STF_NO_ACTION, ""); + } + } +} + +/** + * Called after the save name has been modified and to action the game-save. + */ +int Hu_MenuSelectSaveSlot(mn_object_t *ob, mn_actionid_t action, void *parameters) +{ + mndata_edit_t* edit = (mndata_edit_t*)ob->_typedata; + const int saveSlot = edit->data2; + mn_page_t *page; + + DENG_UNUSED(parameters); + + if(MNA_ACTIVEOUT != action) return 1; + + if(menuNominatingQuickSaveSlot) + { + Con_SetInteger("game-save-quick-slot", saveSlot); + menuNominatingQuickSaveSlot = false; + } + + if(!G_SaveGame2(saveSlot, Str_Text(MNEdit_Text(ob)))) return 0; + + page = Hu_MenuFindPageByName("SaveGame"); + MNPage_SetFocus(page, MN_MustFindObjectOnPage(page, 0, ob->data2)); + + page = Hu_MenuFindPageByName("LoadGame"); + MNPage_SetFocus(page, MN_MustFindObjectOnPage(page, 0, ob->data2)); + + Hu_MenuCommand(chooseCloseMethod()); + return 0; +} + +int Hu_MenuCvarButton(mn_object_t *obj, mn_actionid_t action, void *parameters) +{ + mndata_button_t *btn = (mndata_button_t *)obj->_typedata; + cvarbutton_t const *cb = (cvarbutton_t *)obj->data1; + cvartype_t varType = Con_GetVariableType(cb->cvarname); + int value; + + DENG_UNUSED(parameters); + if(MNA_MODIFIED != action) return 1; + + //strcpy(btn->text, cb->active? cb->yes : cb->no); + btn->text = cb->active? cb->yes : cb->no; + + if(CVT_NULL == varType) return 0; + + if(cb->mask) + { + value = Con_GetInteger(cb->cvarname); + if(cb->active) + { + value |= cb->mask; + } + else + { + value &= ~cb->mask; + } + } + else + { + value = cb->active; + } + + Con_SetInteger2(cb->cvarname, value, SVF_WRITE_OVERRIDE); + return 0; +} + +int Hu_MenuCvarList(mn_object_t *obj, mn_actionid_t action, void *parameters) +{ + mndata_list_t const *list = (mndata_list_t *) obj->_typedata; + mndata_listitem_t const *item; + cvartype_t varType; + int value; + + DENG_UNUSED(parameters); + if(MNA_MODIFIED != action) return 1; + + if(MNList_Selection(obj) < 0) return 0; // Hmm? + + varType = Con_GetVariableType((char const *)list->data); + if(CVT_NULL == varType) return 0; + + item = &((mndata_listitem_t *) list->items)[list->selection]; + if(list->mask) + { + value = Con_GetInteger((char const *)list->data); + value = (value & ~list->mask) | (item->data & list->mask); + } + else + { + value = item->data; + } + + switch(varType) + { + case CVT_INT: + Con_SetInteger2((char const *)list->data, value, SVF_WRITE_OVERRIDE); + break; + case CVT_BYTE: + Con_SetInteger2((char const *)list->data, (byte) value, SVF_WRITE_OVERRIDE); + break; + default: + Con_Error("Hu_MenuCvarList: Unsupported variable type %i", (int)varType); + break; + } + return 0; +} + +int Hu_MenuSaveSlotEdit(mn_object_t *obj, mn_actionid_t action, void * /*context*/) +{ + if(MNA_ACTIVE != action) return 1; + + // Are we suggesting a new name? + if(cfg.menuGameSaveSuggestDescription) + { + AutoStr *suggestion = G_GenerateUserSaveDescription(); + MNEdit_SetText(obj, MNEDIT_STF_NO_ACTION, Str_Text(suggestion)); + } + return 0; +} + +int Hu_MenuCvarEdit(mn_object_t *ob, mn_actionid_t action, void * /*context*/) +{ + mndata_edit_t const *edit = (mndata_edit_t *)ob->_typedata; + cvartype_t varType = Con_GetVariableType((char const *)edit->data1); + + if(MNA_MODIFIED != action) return 1; + + switch(varType) + { + case CVT_CHARPTR: + Con_SetString2((char const *)edit->data1, Str_Text(MNEdit_Text(ob)), SVF_WRITE_OVERRIDE); + break; + + case CVT_URIPTR: { + /// @todo Sanitize and validate against known schemas. + Uri *uri = Uri_NewWithPath2(Str_Text(MNEdit_Text(ob)), RC_NULL); + Con_SetUri2((char const *)edit->data1, uri, SVF_WRITE_OVERRIDE); + Uri_Delete(uri); + break; } + + default: break; + } + return 0; +} + +int Hu_MenuCvarSlider(mn_object_t *ob, mn_actionid_t action, void * /*context*/) +{ + mndata_slider_t const *sldr = (mndata_slider_t *)ob->_typedata; + cvartype_t varType = Con_GetVariableType((char const *)sldr->data1); + float value = MNSlider_Value(ob); + + if(MNA_MODIFIED != action) return 1; + + if(CVT_NULL == varType) return 0; + + switch(varType) + { + case CVT_FLOAT: + if(sldr->step >= .01f) + { + Con_SetFloat2((char const *)sldr->data1, (int) (100 * value) / 100.0f, SVF_WRITE_OVERRIDE); + } + else + { + Con_SetFloat2((char const *)sldr->data1, value, SVF_WRITE_OVERRIDE); + } + break; + case CVT_INT: + Con_SetInteger2((char const *)sldr->data1, (int) value, SVF_WRITE_OVERRIDE); + break; + case CVT_BYTE: + Con_SetInteger2((char const *)sldr->data1, (byte) value, SVF_WRITE_OVERRIDE); + break; + default: + break; + } + return 0; +} + +int Hu_MenuActivateColorWidget(mn_object_t *ob, mn_actionid_t action, void * /*context*/) +{ + mn_object_t *cboxMix, *sldrRed, *sldrGreen, *sldrBlue, *textAlpha, *sldrAlpha; + mn_page_t *colorWidgetPage = Hu_MenuFindPageByName("ColorWidget"); + + if(action != MNA_ACTIVE) return 1; + + cboxMix = MN_MustFindObjectOnPage(colorWidgetPage, 0, MNF_ID0); + sldrRed = MN_MustFindObjectOnPage(colorWidgetPage, 0, MNF_ID1); + sldrGreen = MN_MustFindObjectOnPage(colorWidgetPage, 0, MNF_ID2); + sldrBlue = MN_MustFindObjectOnPage(colorWidgetPage, 0, MNF_ID3); + textAlpha = MN_MustFindObjectOnPage(colorWidgetPage, 0, MNF_ID4); + sldrAlpha = MN_MustFindObjectOnPage(colorWidgetPage, 0, MNF_ID5); + + colorWidgetActive = true; + + MNPage_Initialize(colorWidgetPage); + colorWidgetPage->userData = ob; + + MNColorBox_CopyColor(cboxMix, 0, ob); + MNSlider_SetValue(sldrRed, MNSLIDER_SVF_NO_ACTION, MNColorBox_Redf(ob)); + MNSlider_SetValue(sldrGreen, MNSLIDER_SVF_NO_ACTION, MNColorBox_Greenf(ob)); + MNSlider_SetValue(sldrBlue, MNSLIDER_SVF_NO_ACTION, MNColorBox_Bluef(ob)); + MNSlider_SetValue(sldrAlpha, MNSLIDER_SVF_NO_ACTION, MNColorBox_Alphaf(ob)); + + MNObject_SetFlags(textAlpha, (MNColorBox_RGBAMode(ob)? FO_CLEAR : FO_SET), MNF_DISABLED|MNF_HIDDEN); + MNObject_SetFlags(sldrAlpha, (MNColorBox_RGBAMode(ob)? FO_CLEAR : FO_SET), MNF_DISABLED|MNF_HIDDEN); + + return 0; +} + +int Hu_MenuCvarColorBox(mn_object_t *ob, mn_actionid_t action, void * /*context*/) +{ + mndata_colorbox_t *cbox = (mndata_colorbox_t *)ob->_typedata; + + if(action != MNA_MODIFIED) return 1; + + // MNColorBox's current color has already been updated and we know + // that at least one of the color components have changed. + // So our job is to simply update the associated cvars. + Con_SetFloat2((char const *)cbox->data1, MNColorBox_Redf(ob), SVF_WRITE_OVERRIDE); + Con_SetFloat2((char const *)cbox->data2, MNColorBox_Greenf(ob), SVF_WRITE_OVERRIDE); + Con_SetFloat2((char const *)cbox->data3, MNColorBox_Bluef(ob), SVF_WRITE_OVERRIDE); + if(MNColorBox_RGBAMode(ob)) + { + Con_SetFloat2((char const *)cbox->data4, MNColorBox_Alphaf(ob), SVF_WRITE_OVERRIDE); + } + + return 0; +} + +void Hu_MenuDrawLoadGamePage(mn_page_t * /*page*/, Point2Raw const *origin) +{ + DGL_Enable(DGL_TEXTURE_2D); + DGL_Color4f(1, 1, 1, mnRendState->pageAlpha); + FR_SetFont(FID(GF_FONTB)); + FR_SetColorAndAlpha(cfg.menuTextColors[0][CR], cfg.menuTextColors[0][CG], cfg.menuTextColors[0][CB], mnRendState->pageAlpha); + +#if __JHERETIC__ || __JHEXEN__ + FR_DrawTextXY3("Load Game", SCREENWIDTH/2, origin->y-20, ALIGN_TOP, MN_MergeMenuEffectWithDrawTextFlags(0)); +#else + WI_DrawPatchXY3(pLoadGame, Hu_ChoosePatchReplacement(patchreplacemode_t(cfg.menuPatchReplaceMode), pLoadGame), + origin->x - 8, origin->y - 26, ALIGN_TOPLEFT, 0, MN_MergeMenuEffectWithDrawTextFlags(0)); +#endif + DGL_Disable(DGL_TEXTURE_2D); + + Point2Raw helpOrigin; + helpOrigin.x = SCREENWIDTH/2; + helpOrigin.y = (SCREENHEIGHT/2) + ((SCREENHEIGHT/2-5)/cfg.menuScale); + Hu_MenuDrawPageHelp("Select to load, [Del] to clear", helpOrigin.x, helpOrigin.y); +} + +void Hu_MenuDrawSaveGamePage(mn_page_t * /*page*/, Point2Raw const *origin) +{ +#if __JHERETIC__ || __JHEXEN__ + Hu_MenuDrawPageTitle("Save Game", SCREENWIDTH/2, origin->y - 20); +#else + DGL_Enable(DGL_TEXTURE_2D); + DGL_Color4f(1, 1, 1, mnRendState->pageAlpha); + FR_SetFont(FID(GF_FONTB)); + FR_SetColorAndAlpha(cfg.menuTextColors[0][CR], cfg.menuTextColors[0][CG], cfg.menuTextColors[0][CB], mnRendState->pageAlpha); + + WI_DrawPatchXY3(pSaveGame, Hu_ChoosePatchReplacement(patchreplacemode_t(cfg.menuPatchReplaceMode), pSaveGame), + origin->x - 8, origin->y - 26, ALIGN_TOPLEFT, 0, MN_MergeMenuEffectWithDrawTextFlags(0)); + + DGL_Disable(DGL_TEXTURE_2D); +#endif + + Point2Raw helpOrigin; + helpOrigin.x = SCREENWIDTH/2; + helpOrigin.y = (SCREENHEIGHT/2) + ((SCREENHEIGHT/2-5)/cfg.menuScale); + Hu_MenuDrawPageHelp("Select to save, [Del] to clear", helpOrigin.x, helpOrigin.y); +} + +#if __JDOOM__ || __JHERETIC__ || __JHEXEN__ +int Hu_MenuSelectHelp(mn_object_t * /*ob*/, mn_actionid_t action, void * /*context*/) +{ + if(MNA_ACTIVEOUT != action) return 1; + G_StartHelp(); + return 0; +} +#endif + +void Hu_MenuDrawOptionsPage(mn_page_t * /*page*/, Point2Raw const *origin) +{ +#if __JHERETIC__ || __JHEXEN__ + Hu_MenuDrawPageTitle("Options", origin->x + 42, origin->y - 38); +#else + DGL_Enable(DGL_TEXTURE_2D); + DGL_Color4f(1, 1, 1, mnRendState->pageAlpha); + FR_SetFont(FID(GF_FONTB)); + FR_SetColorAndAlpha(cfg.menuTextColors[0][CR], cfg.menuTextColors[0][CG], cfg.menuTextColors[0][CB], mnRendState->pageAlpha); + + WI_DrawPatchXY3(pOptionsTitle, Hu_ChoosePatchReplacement(patchreplacemode_t(cfg.menuPatchReplaceMode), pOptionsTitle), + origin->x + 42, origin->y - 20, ALIGN_TOP, 0, MN_MergeMenuEffectWithDrawTextFlags(0)); + + DGL_Disable(DGL_TEXTURE_2D); +#endif +} + +void Hu_MenuDrawWeaponsPage(mn_page_t *page, Point2Raw const * /*offset*/) +{ + // Inform the user how to change the order. + if(MNPage_FocusObject(page) == MN_MustFindObjectOnPage(page, 0, MNF_ID0)) + { + char const *helpText = "Use left/right to move weapon up/down"; + Point2Raw origin; + origin.x = SCREENWIDTH/2; + origin.y = (SCREENHEIGHT/2) + ((SCREENHEIGHT/2-5)/cfg.menuScale); + Hu_MenuDrawPageHelp(helpText, origin.x, origin.y); + } +} + +void Hu_MenuDrawMultiplayerPage(mn_page_t * /*page*/, Point2Raw const *origin) +{ + Hu_MenuDrawPageTitle(GET_TXT(TXT_MULTIPLAYER), SCREENWIDTH/2, origin->y - 28); +} + +void Hu_MenuDrawPlayerSetupPage(mn_page_t * /*page*/, Point2Raw const *origin) +{ + Hu_MenuDrawPageTitle(GET_TXT(TXT_PLAYERSETUP), SCREENWIDTH/2, origin->y - 28); +} + +int Hu_MenuActionSetActivePage(mn_object_t *ob, mn_actionid_t action, void * /*context*/) +{ + DENG2_ASSERT(ob != 0); + if(MNA_ACTIVEOUT != action) return 1; + Hu_MenuSetActivePage(Hu_MenuFindPageByName((char *)ob->data1)); + return 0; +} + +int Hu_MenuUpdateColorWidgetColor(mn_object_t *ob, mn_actionid_t action, void * /*context*/) +{ + float value = MNSlider_Value(ob); + mn_object_t *cboxMix = MN_MustFindObjectOnPage(Hu_MenuFindPageByName("ColorWidget"), 0, MNF_ID0); + + if(MNA_MODIFIED != action) return 1; + + switch(ob->data2) + { + case CR: MNColorBox_SetRedf( cboxMix, MNCOLORBOX_SCF_NO_ACTION, value); break; + case CG: MNColorBox_SetGreenf(cboxMix, MNCOLORBOX_SCF_NO_ACTION, value); break; + case CB: MNColorBox_SetBluef( cboxMix, MNCOLORBOX_SCF_NO_ACTION, value); break; + case CA: MNColorBox_SetAlphaf(cboxMix, MNCOLORBOX_SCF_NO_ACTION, value); break; + default: + Con_Error("Hu_MenuUpdateColorWidgetColor: Invalid value (%i) for data2.", ob->data2); + } + + return 0; +} + +int Hu_MenuChangeWeaponPriority(mn_object_t * /*ob*/, mn_actionid_t action, void * /*context*/) +{ + if(MNA_MODIFIED != action) return 1; + + /*int choice = option >> NUM_WEAPON_TYPES; + + if(option & RIGHT_DIR) + { + if(choice < NUM_WEAPON_TYPES-1) + { + int temp = cfg.weaponOrder[choice+1]; + cfg.weaponOrder[choice+1] = cfg.weaponOrder[choice]; + cfg.weaponOrder[choice] = temp; + } + } + else + { + if(choice > 0) + { + int temp = cfg.weaponOrder[choice]; + cfg.weaponOrder[choice] = cfg.weaponOrder[choice-1]; + cfg.weaponOrder[choice-1] = temp; + } + }*/ + return 0; +} + +int Hu_MenuSelectSingleplayer(mn_object_t * /*ob*/, mn_actionid_t action, void * /*context*/) +{ + if(MNA_ACTIVEOUT != action) return 1; + + if(IS_NETGAME) + { + Hu_MsgStart(MSG_ANYKEY, NEWGAME, NULL, 0, NULL); + return 0; + } + +#if __JHEXEN__ + Hu_MenuSetActivePage(Hu_MenuFindPageByName("PlayerClass")); +#elif __JHERETIC__ + Hu_MenuSetActivePage(Hu_MenuFindPageByName("Episode")); +#elif __JDOOM64__ + Hu_MenuSetActivePage(Hu_MenuFindPageByName("Skill")); +#else // __JDOOM__ + if(gameModeBits & (GM_ANY_DOOM2|GM_DOOM_CHEX)) + Hu_MenuSetActivePage(Hu_MenuFindPageByName("Skill")); + else + Hu_MenuSetActivePage(Hu_MenuFindPageByName("Episode")); +#endif + + return 0; +} + +int Hu_MenuSelectMultiplayer(mn_object_t * /*ob*/, mn_actionid_t action, void * /*context*/) +{ + mn_page_t *multiplayerPage = Hu_MenuFindPageByName("Multiplayer"); + mn_object_t *labelObj = MN_MustFindObjectOnPage(multiplayerPage, 0, MNF_ID0); + mndata_button_t *btn = (mndata_button_t *)labelObj->_typedata; + + if(MNA_ACTIVEOUT != action) return 1; + + // Set the appropriate label. + if(IS_NETGAME) + { + btn->text = "Disconnect"; + } + else + { + btn->text = "Join Game"; + } + Hu_MenuSetActivePage(multiplayerPage); + return 0; +} + +int Hu_MenuSelectJoinGame(mn_object_t * /*ob*/, mn_actionid_t action, void * /*parameters*/) +{ + if(MNA_ACTIVEOUT != action) return 1; + + if(IS_NETGAME) + { + DD_Execute(false, "net disconnect"); + Hu_MenuCommand(MCMD_CLOSE); + return 0; + } + + DD_Execute(false, "net setup client"); + return 0; +} + +int Hu_MenuSelectPlayerSetup(mn_object_t * /*ob*/, mn_actionid_t action, void * /*context*/) +{ + mn_page_t *playerSetupPage = Hu_MenuFindPageByName("PlayerSetup"); + mn_object_t *mop = MN_MustFindObjectOnPage(playerSetupPage, 0, MNF_ID0); + mn_object_t *name = MN_MustFindObjectOnPage(playerSetupPage, 0, MNF_ID1); + mn_object_t *color = MN_MustFindObjectOnPage(playerSetupPage, 0, MNF_ID3); +#if __JHEXEN__ + mn_object_t *class_; +#endif + + if(MNA_ACTIVEOUT != action) return 1; + +#if __JHEXEN__ + MNMobjPreview_SetMobjType(mop, PCLASS_INFO(cfg.netClass)->mobjType); + MNMobjPreview_SetPlayerClass(mop, cfg.netClass); +#else + MNMobjPreview_SetMobjType(mop, MT_PLAYER); + MNMobjPreview_SetPlayerClass(mop, PCLASS_PLAYER); +#endif + MNMobjPreview_SetTranslationClass(mop, 0); + MNMobjPreview_SetTranslationMap(mop, cfg.netColor); + + MNList_SelectItemByValue(color, MNLIST_SIF_NO_ACTION, cfg.netColor); +#if __JHEXEN__ + class_ = MN_MustFindObjectOnPage(playerSetupPage, 0, MNF_ID2); + MNList_SelectItemByValue(class_, MNLIST_SIF_NO_ACTION, cfg.netClass); +#endif + + MNEdit_SetText(name, MNEDIT_STF_NO_ACTION|MNEDIT_STF_REPLACEOLD, Con_GetString("net-name")); + + Hu_MenuSetActivePage(playerSetupPage); + return 0; +} + +#if __JHEXEN__ +int Hu_MenuSelectPlayerSetupPlayerClass(mn_object_t *ob, mn_actionid_t action, void * /*context*/) +{ + if(MNA_MODIFIED != action) return 1; + + int selection = MNList_Selection(ob); + if(selection >= 0) + { + mn_object_t *mop = MN_MustFindObjectOnPage(MNObject_Page(ob), 0, MNF_ID0); + MNMobjPreview_SetPlayerClass(mop, selection); + MNMobjPreview_SetMobjType(mop, PCLASS_INFO(selection)->mobjType); + } + return 0; +} +#endif + +int Hu_MenuSelectPlayerColor(mn_object_t *ob, mn_actionid_t action, void * /*context*/) +{ + if(MNA_MODIFIED != action) return 1; + + // The color translation map is stored in the list item data member. + int selection = MNList_ItemData(ob, MNList_Selection(ob)); + if(selection >= 0) + { + mn_object_t *mop = MN_MustFindObjectOnPage(MNObject_Page(ob), 0, MNF_ID0); + MNMobjPreview_SetTranslationMap(mop, selection); + } + return 0; +} + +int Hu_MenuSelectAcceptPlayerSetup(mn_object_t *ob, mn_actionid_t action, void * /*context*/) +{ + mn_object_t *plrNameEdit = MN_MustFindObjectOnPage(MNObject_Page(ob), 0, MNF_ID1); +#if __JHEXEN__ + mn_object_t *plrClassList = MN_MustFindObjectOnPage(MNObject_Page(ob), 0, MNF_ID2); +#endif + mn_object_t *plrColorList = MN_MustFindObjectOnPage(MNObject_Page(ob), 0, MNF_ID3); + +#if __JHEXEN__ + cfg.netClass = MNList_Selection(plrClassList); +#endif + // The color translation map is stored in the list item data member. + cfg.netColor = MNList_ItemData(plrColorList, MNList_Selection(plrColorList)); + + if(MNA_ACTIVEOUT != action) return 1; + + char buf[300]; + strcpy(buf, "net-name "); + M_StrCatQuoted(buf, Str_Text(MNEdit_Text(plrNameEdit)), 300); + DD_Execute(false, buf); + + if(IS_NETGAME) + { + strcpy(buf, "setname "); + M_StrCatQuoted(buf, Str_Text(MNEdit_Text(plrNameEdit)), 300); + DD_Execute(false, buf); +#if __JHEXEN__ + // Must do 'setclass' first; the real class and color do not change + // until the server sends us a notification -- this means if we do + // 'setcolor' first, the 'setclass' after it will override the color + // change (or such would appear to be the case). + DD_Executef(false, "setclass %i", cfg.netClass); +#endif + DD_Executef(false, "setcolor %i", cfg.netColor); + } + + Hu_MenuSetActivePage(Hu_MenuFindPageByName("Multiplayer")); + return 0; +} + +int Hu_MenuSelectQuitGame(mn_object_t * /*ob*/, mn_actionid_t action, void * /*parameters*/) +{ + if(MNA_ACTIVEOUT != action) return 1; + G_QuitGame(); + return 0; +} + +int Hu_MenuSelectEndGame(mn_object_t * /*ob*/, mn_actionid_t action, void * /*context*/) +{ + if(MNA_ACTIVEOUT != action) return 1; + G_EndGame(); + return 0; +} + +int Hu_MenuSelectLoadGame(mn_object_t * /*ob*/, mn_actionid_t action, void * /*context*/) +{ + if(MNA_ACTIVEOUT != action) return 1; + + if(!Get(DD_DEDICATED)) + { + if(IS_CLIENT && !Get(DD_PLAYBACK)) + { + Hu_MsgStart(MSG_ANYKEY, LOADNET, NULL, 0, NULL); + return 0; + } + } + + Hu_MenuUpdateGameSaveWidgets(); + Hu_MenuSetActivePage(Hu_MenuFindPageByName("LoadGame")); + return 0; +} + +int Hu_MenuSelectSaveGame(mn_object_t * /*ob*/, mn_actionid_t action, void * /*context*/) +{ + player_t *player = &players[CONSOLEPLAYER]; + + if(MNA_ACTIVEOUT != action) return 1; + + if(!Get(DD_DEDICATED)) + { + if(IS_CLIENT) + { +#if __JDOOM__ || __JDOOM64__ + Hu_MsgStart(MSG_ANYKEY, SAVENET, NULL, 0, NULL); +#endif + return 0; + } + + if(G_GameState() != GS_MAP) + { + Hu_MsgStart(MSG_ANYKEY, SAVEOUTMAP, NULL, 0, NULL); + return 0; + } + + if(player->playerState == PST_DEAD) + { + Hu_MsgStart(MSG_ANYKEY, SAVEDEAD, NULL, 0, NULL); + return 0; + } + } + + Hu_MenuCommand(MCMD_OPEN); + Hu_MenuUpdateGameSaveWidgets(); + Hu_MenuSetActivePage(Hu_MenuFindPageByName("SaveGame")); + return 0; +} + +#if __JHEXEN__ +int Hu_MenuSelectPlayerClass(mn_object_t *ob, mn_actionid_t action, void * /*context*/) +{ + mn_page_t *skillPage = Hu_MenuFindPageByName("Skill"); + int option = ob->data2; + mn_object_t *skillObj; + char const *text; + + if(MNA_ACTIVEOUT != action) return 1; + + if(IS_NETGAME) + { + P_SetMessage(&players[CONSOLEPLAYER], LMF_NO_HIDE, "You can't start a new game from within a netgame!"); + return 0; + } + + if(option < 0) + { // Random class. + // Number of user-selectable classes. + mnPlrClass = (menuTime / 5) % 3; + } + else + { + mnPlrClass = option; + } + + skillObj = MN_MustFindObjectOnPage(skillPage, 0, MNF_ID0); + text = GET_TXT(PCLASS_INFO(mnPlrClass)->skillModeNames[SM_BABY]); + ((mndata_button_t *)skillObj->_typedata)->text = text; + MNObject_SetShortcut(skillObj, text[0]); + + skillObj = MN_MustFindObjectOnPage(skillPage, 0, MNF_ID1); + text = GET_TXT(PCLASS_INFO(mnPlrClass)->skillModeNames[SM_EASY]); + ((mndata_button_t *)skillObj->_typedata)->text = text; + MNObject_SetShortcut(skillObj, text[0]); + + skillObj = MN_MustFindObjectOnPage(skillPage, 0, MNF_ID2); + text = GET_TXT(PCLASS_INFO(mnPlrClass)->skillModeNames[SM_MEDIUM]); + ((mndata_button_t *)skillObj->_typedata)->text = text; + MNObject_SetShortcut(skillObj, text[0]); + + skillObj = MN_MustFindObjectOnPage(skillPage, 0, MNF_ID3); + text = GET_TXT(PCLASS_INFO(mnPlrClass)->skillModeNames[SM_HARD]); + ((mndata_button_t *)skillObj->_typedata)->text = text; + MNObject_SetShortcut(skillObj, text[0]); + + skillObj = MN_MustFindObjectOnPage(skillPage, 0, MNF_ID4); + text = GET_TXT(PCLASS_INFO(mnPlrClass)->skillModeNames[SM_NIGHTMARE]); + ((mndata_button_t *)skillObj->_typedata)->text = text; + MNObject_SetShortcut(skillObj, text[0]); + + switch(mnPlrClass) + { + case PCLASS_FIGHTER: MNPage_SetX(skillPage, 120); break; + case PCLASS_CLERIC: MNPage_SetX(skillPage, 116); break; + case PCLASS_MAGE: MNPage_SetX(skillPage, 112); break; + } + Hu_MenuSetActivePage(skillPage); + return 0; +} + +int Hu_MenuFocusOnPlayerClass(mn_object_t *ob, mn_actionid_t action, void *context) +{ + playerclass_t plrClass = (playerclass_t)ob->data2; + + if(MNA_FOCUS != action) return 1; + + mn_object_t *mop = MN_MustFindObjectOnPage(MNObject_Page(ob), 0, MNF_ID0); + MNMobjPreview_SetPlayerClass(mop, plrClass); + MNMobjPreview_SetMobjType(mop, (PCLASS_NONE == plrClass? MT_NONE : PCLASS_INFO(plrClass)->mobjType)); + + Hu_MenuDefaultFocusAction(ob, action, context); + return 0; +} +#endif + +#if __JDOOM__ || __JHERETIC__ +int Hu_MenuFocusEpisode(mn_object_t *ob, mn_actionid_t action, void *context) +{ + if(MNA_FOCUS != action) return 1; + mnEpisode = ob->data2; + Hu_MenuDefaultFocusAction(ob, action, context); + return 0; +} + +int Hu_MenuConfirmOrderCommericalVersion(msgresponse_t /*response*/, int /*userValue*/, void * /*context*/) +{ + G_StartHelp(); + return true; +} + +int Hu_MenuActivateNotSharewareEpisode(mn_object_t * /*ob*/, mn_actionid_t action, void * /*context*/) +{ + if(MNA_ACTIVEOUT != action) return 1; + Hu_MsgStart(MSG_ANYKEY, SWSTRING, Hu_MenuConfirmOrderCommericalVersion, 0, NULL); + return 0; +} +#endif + +int Hu_MenuFocusSkillMode(mn_object_t *ob, mn_actionid_t action, void *context) +{ + DENG2_ASSERT(ob != 0); + + if(MNA_FOCUS != action) return 1; + mnSkillmode = (skillmode_t)ob->data2; + Hu_MenuDefaultFocusAction(ob, action, context); + return 0; +} + +#if __JDOOM__ +int Hu_MenuConfirmInitNewGame(msgresponse_t response, int /*userValue*/, void * /*context*/) +{ + if(response == MSG_YES) + { + Hu_MenuInitNewGame(true); + } + return true; +} +#endif + +void Hu_MenuInitNewGame(dd_bool confirmed) +{ +#if __JDOOM__ + if(!confirmed && SM_NIGHTMARE == mnSkillmode) + { + Hu_MsgStart(MSG_YESNO, NIGHTMARE, Hu_MenuConfirmInitNewGame, 0, NULL); + return; + } +#else + DENG2_UNUSED(confirmed); +#endif + + Hu_MenuCommand(chooseCloseMethod()); + +#if __JHEXEN__ + cfg.playerClass[CONSOLEPLAYER] = playerclass_t(mnPlrClass); +#endif + + GameRuleset newRules = gameRules; + newRules.skill = mnSkillmode; + +#if __JHEXEN__ + Uri *newMapUri = G_ComposeMapUri(mnEpisode, P_TranslateMap(0)); +#else + Uri *newMapUri = G_ComposeMapUri(mnEpisode, 0); +#endif + + G_DeferredNewGame(newMapUri, 0/*default*/, &newRules); + Uri_Delete(newMapUri); +} + +int Hu_MenuActionInitNewGame(mn_object_t * /*ob*/, mn_actionid_t action, void * /*context*/) +{ + if(MNA_ACTIVEOUT != action) return 1; + Hu_MenuInitNewGame(false); + return 0; +} + +int Hu_MenuSelectControlPanelLink(mn_object_t *ob, mn_actionid_t action, void * /*context*/) +{ +#define NUM_PANEL_NAMES 1 + + static char const *panelNames[NUM_PANEL_NAMES] = { + "taskbar" //, + //"panel audio", + //"panel input" + }; + + if(MNA_ACTIVEOUT != action) return 1; + + int idx = ob->data2; + if(idx < 0 || idx > NUM_PANEL_NAMES - 1) + { + idx = 0; + } + + DD_Execute(true, panelNames[idx]); + return 0; + +#undef NUM_PANEL_NAMES +} + +D_CMD(MenuOpen) +{ + DENG2_UNUSED(src); + + if(argc > 1) + { + if(!stricmp(argv[1], "open")) + { + Hu_MenuCommand(MCMD_OPEN); + return true; + } + if(!stricmp(argv[1], "close")) + { + Hu_MenuCommand(MCMD_CLOSE); + return true; + } + + if(mn_page_t *page = Hu_MenuFindPageByName(argv[1])) + { + Hu_MenuCommand(MCMD_OPEN); + Hu_MenuSetActivePage(page); + return true; + } + return false; + } + + Hu_MenuCommand(!menuActive? MCMD_OPEN : MCMD_CLOSE); + return true; +} + +/** + * Routes console commands for menu actions and navigation into the menu subsystem. + */ +D_CMD(MenuCommand) +{ + DENG2_UNUSED2(src, argc); + + if(menuActive) + { + char const *cmd = argv[0] + 4; + if(!stricmp(cmd, "up")) + { + Hu_MenuCommand(MCMD_NAV_UP); + return true; + } + if(!stricmp(cmd, "down")) + { + Hu_MenuCommand(MCMD_NAV_DOWN); + return true; + } + if(!stricmp(cmd, "left")) + { + Hu_MenuCommand(MCMD_NAV_LEFT); + return true; + } + if(!stricmp(cmd, "right")) + { + Hu_MenuCommand(MCMD_NAV_RIGHT); + return true; + } + if(!stricmp(cmd, "back")) + { + Hu_MenuCommand(MCMD_NAV_OUT); + return true; + } + if(!stricmp(cmd, "delete")) + { + Hu_MenuCommand(MCMD_DELETE); + return true; + } + if(!stricmp(cmd, "select")) + { + Hu_MenuCommand(MCMD_SELECT); + return true; + } + if(!stricmp(cmd, "pagedown")) + { + Hu_MenuCommand(MCMD_NAV_PAGEDOWN); + return true; + } + if(!stricmp(cmd, "pageup")) + { + Hu_MenuCommand(MCMD_NAV_PAGEUP); + return true; + } + } + return false; +} diff --git a/doomsday/plugins/common/src/hu_stuff.cpp b/doomsday/plugins/common/src/hu_stuff.cpp index b932a4237a..99058189b1 100644 --- a/doomsday/plugins/common/src/hu_stuff.cpp +++ b/doomsday/plugins/common/src/hu_stuff.cpp @@ -898,15 +898,15 @@ void Hu_FogEffectTicker(timespan_t ticLength) { fog->layers[i].texAngle += ((MENUFOGSPEED[i]/4) * ticLength * TICRATE); fog->layers[i].posAngle -= (MENUFOGSPEED[!i] * ticLength * TICRATE); - fog->layers[i].texOffset[VX] = 160 + 120 * cos(fog->layers[i].posAngle / 180 * PI); - fog->layers[i].texOffset[VY] = 100 + 100 * sin(fog->layers[i].posAngle / 180 * PI); + fog->layers[i].texOffset[VX] = 160 + 120 * cos(fog->layers[i].posAngle / 180 * DD_PI); + fog->layers[i].texOffset[VY] = 100 + 100 * sin(fog->layers[i].posAngle / 180 * DD_PI); } else { fog->layers[i].texAngle += ((MENUFOGSPEED[i]/4) * ticLength * TICRATE); fog->layers[i].posAngle -= ((MENUFOGSPEED[!i]*1.5f) * ticLength * TICRATE); - fog->layers[i].texOffset[VX] = 320 + 320 * cos(fog->layers[i].posAngle / 180 * PI); - fog->layers[i].texOffset[VY] = 240 + 240 * sin(fog->layers[i].posAngle / 180 * PI); + fog->layers[i].texOffset[VX] = 320 + 320 * cos(fog->layers[i].posAngle / 180 * DD_PI); + fog->layers[i].texOffset[VY] = 240 + 240 * sin(fog->layers[i].posAngle / 180 * DD_PI); } } diff --git a/doomsday/plugins/common/src/mapstatereader.cpp b/doomsday/plugins/common/src/mapstatereader.cpp index ea22816261..da94483959 100644 --- a/doomsday/plugins/common/src/mapstatereader.cpp +++ b/doomsday/plugins/common/src/mapstatereader.cpp @@ -22,18 +22,46 @@ #include "mapstatereader.h" #include "dmu_lib.h" +#include "dmu_archiveindex.h" #include "p_actor.h" -#include "p_saveg.h" +#include "p_saveg.h" // SV_ReadLine, SV_ReadSector #include "p_saveio.h" #include "polyobjs.h" #include "thinkerinfo.h" #include +namespace internal +{ + static bool useMaterialArchiveSegments() { +#if __JHEXEN__ + return true; +#else + return false; +#endif + } + + static int thingArchiveVersionFor(int mapVersion) { +#if __JHEXEN__ + return mapVersion >= 4? 1 : 0; +#else + return 0; + DENG_UNUSED(mapVersion); +#endif + } +} + +using namespace internal; + DENG2_PIMPL(MapStateReader) { - Reader *reader; + Reader *reader; // Not owned. int saveVersion; int mapVersion; + bool formatHasMapVersionNumber; + + int thingArchiveSize; + + ThingArchive *thingArchive; MaterialArchive *materialArchive; dmu_lib::SideArchive *sideArchive; @@ -42,11 +70,21 @@ DENG2_PIMPL(MapStateReader) , reader(0) , saveVersion(0) , mapVersion(0) + , formatHasMapVersionNumber(false) + , thingArchiveSize(0) + , thingArchive(0) , materialArchive(0) , sideArchive(0) {} - void checkSegment(int segId) + ~Instance() + { + delete thingArchive; + delete sideArchive; + MaterialArchive_Delete(materialArchive); + } + + void beginSegment(int segId) { #if __JHEXEN__ if(segId == ASEG_END && SV_HxBytesLeft() < 4) @@ -57,7 +95,7 @@ DENG2_PIMPL(MapStateReader) if(Reader_ReadInt32(reader) != segId) { /// @throw ReadError Failed alignment check. - throw ReadError("MapStateReader::checkSegment", "Corrupt save game, segment " + de::String::number(segId) + " failed alignment check"); + throw ReadError("MapStateReader", "Corrupt save game, segment #" + de::String::number(segId) + " failed alignment check"); } #else DENG_UNUSED(segId); @@ -65,63 +103,51 @@ DENG2_PIMPL(MapStateReader) } /// Special case check for the top-level map state segment. - void checkMapSegment() + void beginMapSegment() { #if __JHEXEN__ int segId = Reader_ReadInt32(reader); if(segId != ASEG_MAP_HEADER2 && segId != ASEG_MAP_HEADER) { /// @throw ReadError Failed alignment check. - throw ReadError("MapStateReader::checkSegment", "Corrupt save game, segment " + de::String::number(segId) + " failed alignment check"); + throw ReadError("MapStateReader", "Corrupt save game, segment #" + de::String::number(segId) + " failed alignment check"); } - - // Maps have their own version number, in Hexen. - mapVersion = (segId == ASEG_MAP_HEADER2? Reader_ReadByte(reader) : 2); + formatHasMapVersionNumber = (segId == ASEG_MAP_HEADER2); #else - checkSegment(ASEG_MAP_HEADER2); + beginSegment(ASEG_MAP_HEADER2); #endif } - void beginMapSegment() + void endSegment() { - checkMapSegment(); + beginSegment(ASEG_END); + } + void readMapHeader() + { #if __JHEXEN__ - thingArchiveVersion = mapVersion >= 4? 1 : 0; -#endif + // Maps have their own version number, in Hexen. + mapVersion = (formatHasMapVersionNumber? Reader_ReadByte(reader) : 2); -#if __JHEXEN__ // Read the map timer. mapTime = Reader_ReadInt32(reader); #endif + } - // Read the material archive for the map. -#ifdef __JHEXEN__ - materialArchive = MaterialArchive_NewEmpty(true /* segment checks */); -#else - materialArchive = MaterialArchive_NewEmpty(false); -#endif + void readMaterialArchive() + { + materialArchive = MaterialArchive_NewEmpty(useMaterialArchiveSegments()); #if !__JHEXEN__ if(mapVersion >= 4) #endif { MaterialArchive_Read(materialArchive, reader, mapVersion < 6? 0 : -1); } - - sideArchive = new dmu_lib::SideArchive; - } - - void endMapSegment() - { - checkSegment(ASEG_END); - - delete sideArchive; sideArchive = 0; - MaterialArchive_Delete(materialArchive); materialArchive = 0; } void readElements() { - checkSegment(ASEG_MAP_ELEMENTS); + beginSegment(ASEG_MAP_ELEMENTS); // Sectors. for(int i = 0; i < numsectors; ++i) @@ -134,12 +160,14 @@ DENG2_PIMPL(MapStateReader) { SV_ReadLine((Line *)P_ToPtr(DMU_LINE, i), thisPublic); } + + // endSegment(); } void readPolyobjs() { #if __JHEXEN__ - checkSegment(ASEG_POLYOBJS); + beginSegment(ASEG_POLYOBJS); int const writtenPolyobjCount = Reader_ReadInt32(reader); DENG_ASSERT(writtenPolyobjCount == numpolyobjs); @@ -151,10 +179,12 @@ DENG2_PIMPL(MapStateReader) DENG_ASSERT(po != 0); po->read(thisPublic); } + + // endSegment(); #endif } - static int removeThinkerWorker(thinker_t *th, void * /*context*/) + static int removeLoadSpawnedThinkerWorker(thinker_t *th, void * /*context*/) { if(th->function == (thinkfunc_t) P_MobjThinker) { @@ -174,7 +204,7 @@ DENG2_PIMPL(MapStateReader) if(!IS_SERVER) return; // Not for us. #endif - Thinker_Iterate(0 /*all thinkers*/, removeThinkerWorker, 0/*no parameters*/); + Thinker_Iterate(0 /*all thinkers*/, removeLoadSpawnedThinkerWorker, 0/*no params*/); Thinker_Init(); } @@ -242,14 +272,14 @@ DENG2_PIMPL(MapStateReader) static int restoreMobjLinksWorker(thinker_t *th, void *context) { - int const mapVersion = *static_cast(context); + MapStateReader *inst = static_cast(context); if(th->function != (thinkfunc_t) P_MobjThinker) return false; // Continue iteration. mobj_t *mo = (mobj_t *) th; - mo->target = SV_GetArchiveThing(PTR2INT(mo->target), &mo->target); - mo->onMobj = SV_GetArchiveThing(PTR2INT(mo->onMobj), &mo->onMobj); + mo->target = inst->mobj(PTR2INT(mo->target), &mo->target); + mo->onMobj = inst->mobj(PTR2INT(mo->onMobj), &mo->onMobj); #if __JHEXEN__ switch(mo->type) @@ -262,13 +292,13 @@ DENG2_PIMPL(MapStateReader) case MT_THRUSTFLOOR_DOWN: case MT_MINOTAUR: case MT_SORCFX1: - if(mapVersion >= 3) + if(inst->mapVersion() >= 3) { - mo->tracer = SV_GetArchiveThing(PTR2INT(mo->tracer), &mo->tracer); + mo->tracer = inst->mobj(PTR2INT(mo->tracer), &mo->tracer); } else { - mo->tracer = SV_GetArchiveThing(mo->special1, &mo->tracer); + mo->tracer = inst->mobj(mo->special1, &mo->tracer); mo->special1 = 0; } break; @@ -276,22 +306,22 @@ DENG2_PIMPL(MapStateReader) // Just special2 case MT_LIGHTNING_FLOOR: case MT_LIGHTNING_ZAP: - mo->special2 = PTR2INT(SV_GetArchiveThing(mo->special2, &mo->special2)); + mo->special2 = PTR2INT(inst->mobj(mo->special2, &mo->special2)); break; // Both tracer and special2 case MT_HOLY_TAIL: case MT_LIGHTNING_CEILING: - if(mapVersion >= 3) + if(inst->mapVersion() >= 3) { - mo->tracer = SV_GetArchiveThing(PTR2INT(mo->tracer), &mo->tracer); + mo->tracer = inst->mobj(PTR2INT(mo->tracer), &mo->tracer); } else { - mo->tracer = SV_GetArchiveThing(mo->special1, &mo->tracer); + mo->tracer = inst->mobj(mo->special1, &mo->tracer); mo->special1 = 0; } - mo->special2 = PTR2INT(SV_GetArchiveThing(mo->special2, &mo->special2)); + mo->special2 = PTR2INT(inst->mobj(mo->special2, &mo->special2)); break; default: @@ -299,43 +329,14 @@ DENG2_PIMPL(MapStateReader) } #else # if __JDOOM__ || __JDOOM64__ - mo->tracer = SV_GetArchiveThing(PTR2INT(mo->tracer), &mo->tracer); + mo->tracer = inst->mobj(PTR2INT(mo->tracer), &mo->tracer); # endif # if __JHERETIC__ - mo->generator = SV_GetArchiveThing(PTR2INT(mo->generator), &mo->generator); + mo->generator = inst->mobj(PTR2INT(mo->generator), &mo->generator); # endif #endif return false; // Continue iteration. - -#if !__JHEXEN__ - DENG_UNUSED(mapVersion); -#endif - } - - void relinkThinkers() - { -#if __JHEXEN__ - Thinker_Iterate((thinkfunc_t) P_MobjThinker, restoreMobjLinksWorker, &mapVersion); - - P_CreateTIDList(); - rebuildCorpseQueue(); - -#else - if(IS_SERVER) - { - Thinker_Iterate((thinkfunc_t) P_MobjThinker, restoreMobjLinksWorker, &mapVersion); - - for(int i = 0; i < numlines; ++i) - { - xline_t *xline = P_ToXLine((Line *)P_ToPtr(DMU_LINE, i)); - if(!xline->xg) continue; - - xline->xg->activator = SV_GetArchiveThing(PTR2INT(xline->xg->activator), - &xline->xg->activator); - } - } -#endif } void readThinkers() @@ -346,14 +347,14 @@ DENG2_PIMPL(MapStateReader) #if __JHEXEN__ if(mapVersion < 4) - checkSegment(ASEG_MOBJS); + beginSegment(ASEG_MOBJS); else #endif - checkSegment(ASEG_THINKERS); + beginSegment(ASEG_THINKERS); #if __JHEXEN__ SV_InitTargetPlayers(); - SV_InitThingArchiveForLoad(Reader_ReadInt32(reader) /* num elements */); + thingArchive->initForLoad(Reader_ReadInt32(reader) /* num elements */); #endif // Read in saved thinkers. @@ -365,12 +366,14 @@ DENG2_PIMPL(MapStateReader) #endif byte tClass = 0; - forever + for(;;) { #if __JHEXEN__ if(reachedSpecialsBlock) #endif + { tClass = Reader_ReadByte(reader); + } #if __JHEXEN__ if(mapVersion < 4) @@ -382,16 +385,18 @@ DENG2_PIMPL(MapStateReader) // are differrent, so we need to manipulate the thinker // class identifier value. if(tClass != TC_END) + { tClass += 2; + } } else { tClass = TC_MOBJ; } - if(tClass == TC_MOBJ && (uint)i == thingArchiveSize) + if(tClass == TC_MOBJ && (uint)i == thingArchive->size()) { - checkSegment(ASEG_THINKERS); + beginSegment(ASEG_THINKERS); // We have reached the begining of the "specials" block. reachedSpecialsBlock = true; continue; @@ -406,9 +411,13 @@ DENG2_PIMPL(MapStateReader) // the end of the specials data so we need to manipulate // the thinker class identifier value. if(tClass == PRE_VER5_END_SPECIALS) + { tClass = TC_END; + } else + { tClass += 3; + } } else if(tClass == TC_END) { @@ -418,6 +427,7 @@ DENG2_PIMPL(MapStateReader) } } #endif + if(tClass == TC_END) break; // End of the list. @@ -451,55 +461,74 @@ DENG2_PIMPL(MapStateReader) #if __JHEXEN__ if(tClass == TC_MOBJ) + { i++; + } #endif } // Update references between thinkers. - relinkThinkers(); +#if __JHEXEN__ + Thinker_Iterate((thinkfunc_t) P_MobjThinker, restoreMobjLinksWorker, thisPublic); + + P_CreateTIDList(); + rebuildCorpseQueue(); + +#else + if(IS_SERVER) + { + Thinker_Iterate((thinkfunc_t) P_MobjThinker, restoreMobjLinksWorker, thisPublic); + + for(int i = 0; i < numlines; ++i) + { + xline_t *xline = P_ToXLine((Line *)P_ToPtr(DMU_LINE, i)); + if(!xline->xg) continue; + + xline->xg->activator = thingArchive->mobj(PTR2INT(xline->xg->activator), + &xline->xg->activator); + } + } +#endif } void readACScriptData() { #if __JHEXEN__ - checkSegment(ASEG_SCRIPTS); + beginSegment(ASEG_SCRIPTS); Game_ACScriptInterpreter().readMapScriptData(thisPublic); + // endSegment(); #endif } void readSoundSequences() { #if __JHEXEN__ - checkSegment(ASEG_SOUNDS); + beginSegment(ASEG_SOUNDS); SN_ReadSequences(reader, mapVersion); + // endSegment(); #endif } void readMisc() { #if __JHEXEN__ - checkSegment(ASEG_MISC); + beginSegment(ASEG_MISC); for(int i = 0; i < MAXPLAYERS; ++i) { localQuakeHappening[i] = Reader_ReadInt32(reader); } #endif - } - - void readBrain() - { #if __JDOOM__ - DENG_ASSERT(bossBrain != 0); - bossBrain->read(thisPublic); + DENG_ASSERT(theBossBrain != 0); + theBossBrain->read(thisPublic); #endif } void readSoundTargets() { #if !__JHEXEN__ - // Not for us? - if(!IS_SERVER) return; + if(!IS_SERVER) return; // Not for us. // Sound target data was introduced in ver 5 if(mapVersion < 5) return; @@ -517,18 +546,19 @@ DENG2_PIMPL(MapStateReader) } xsec->soundTarget = INT2PTR(mobj_t, Reader_ReadInt16(reader)); - xsec->soundTarget = - SV_GetArchiveThing(PTR2INT(xsec->soundTarget), &xsec->soundTarget); + xsec->soundTarget = thingArchive->mobj(PTR2INT(xsec->soundTarget), &xsec->soundTarget); } #endif } }; -MapStateReader::MapStateReader(int saveVersion) +MapStateReader::MapStateReader(int saveVersion, int thingArchiveSize) : d(new Instance(this)) { - d->saveVersion = saveVersion; - d->mapVersion = saveVersion; // Default: mapVersion == saveVersion + d->saveVersion = saveVersion; + d->mapVersion = saveVersion; // Default: mapVersion == saveVersion + + d->thingArchiveSize = thingArchiveSize; } void MapStateReader::read(Reader *reader) @@ -536,24 +566,58 @@ void MapStateReader::read(Reader *reader) DENG_ASSERT(reader != 0); d->reader = reader; + // Prepare and populate the side archive. + d->sideArchive = new dmu_lib::SideArchive; + + // Deserialize the map. d->beginMapSegment(); { + d->readMapHeader(); + d->readMaterialArchive(); + + d->thingArchive = new ThingArchive(thingArchiveVersionFor(d->mapVersion)); +#if !__JHEXEN__ + d->thingArchive->initForLoad(d->thingArchiveSize); +#endif + d->readElements(); d->readPolyobjs(); d->readThinkers(); d->readACScriptData(); d->readSoundSequences(); d->readMisc(); - d->readBrain(); d->readSoundTargets(); } - d->endMapSegment(); + d->endSegment(); + + // Cleanup. + delete d->thingArchive; d->thingArchive = 0; + delete d->sideArchive; d->sideArchive = 0; + MaterialArchive_Delete(d->materialArchive); d->materialArchive = 0; } -Reader *MapStateReader::reader() +mobj_t *MapStateReader::mobj(ThingArchive::SerialId serialId, void *address) const { - DENG_ASSERT(d->reader != 0); - return d->reader; + DENG_ASSERT(d->thingArchive != 0); + return d->thingArchive->mobj(serialId, address); +} + +Material *MapStateReader::material(materialarchive_serialid_t serialId, int group) const +{ + DENG_ASSERT(d->materialArchive != 0); + return MaterialArchive_Find(d->materialArchive, serialId, group); +} + +Side *MapStateReader::side(int sideIndex) const +{ + DENG_ASSERT(d->sideArchive != 0); + return (Side *)d->sideArchive->at(sideIndex); +} + +player_t *MapStateReader::player(int serialId) const +{ + DENG_ASSERT(serialId > 0 && serialId <= MAXPLAYERS); + return players + saveToRealPlayerNum[serialId - 1]; } int MapStateReader::mapVersion() @@ -561,14 +625,14 @@ int MapStateReader::mapVersion() return d->mapVersion; } -Material *MapStateReader::material(materialarchive_serialid_t serialId, int group) +Reader *MapStateReader::reader() { - DENG_ASSERT(d->materialArchive != 0); - return MaterialArchive_Find(d->materialArchive, serialId, group); + DENG_ASSERT(d->reader != 0); + return d->reader; } -dmu_lib::SideArchive &MapStateReader::sideArchive() +void MapStateReader::addMobjToThingArchive(mobj_t *mobj, ThingArchive::SerialId serialId) { - DENG_ASSERT(d->sideArchive != 0); - return *d->sideArchive; + DENG_ASSERT(d->thingArchive != 0); + d->thingArchive->insert(mobj, serialId); } diff --git a/doomsday/plugins/common/src/mapstatewriter.cpp b/doomsday/plugins/common/src/mapstatewriter.cpp index c79fbd56b0..4d38a72106 100644 --- a/doomsday/plugins/common/src/mapstatewriter.cpp +++ b/doomsday/plugins/common/src/mapstatewriter.cpp @@ -22,58 +22,71 @@ #include "mapstatewriter.h" #include "dmu_lib.h" -#include "p_saveg.h" +#include "p_saveg.h" // SV_WriteSector, SV_WriteLine #include "p_saveio.h" #include "polyobjs.h" #include "thinkerinfo.h" +#include + +namespace internal +{ + static bool useMaterialArchiveSegments() { +#if __JHEXEN__ + return true; +#else + return false; +#endif + } +} + +using namespace internal; DENG2_PIMPL(MapStateWriter) { - bool excludePlayers; - Writer *writer; + ThingArchive *thingArchive; // Not owned. MaterialArchive *materialArchive; + Writer *writer; // Not owned. Instance(Public *i) : Base(i) - , excludePlayers(false) - , writer(0) + , thingArchive(0) , materialArchive(0) + , writer(0) {} - void beginSegment(int segmentId) + ~Instance() { - SV_BeginSegment(segmentId); + MaterialArchive_Delete(materialArchive); } - void endSegment() + void beginSegment(int segId) { - SV_EndSegment(); +#if __JHEXEN__ + Writer_WriteInt32(writer, segId); +#else + DENG_UNUSED(segId); +#endif } - void beginMapSegment() + void endSegment() { - beginSegment(ASEG_MAP_HEADER2); + beginSegment(ASEG_END); + } + void writeMapHeader() + { #if __JHEXEN__ - Writer_WriteByte(writer, MY_SAVE_VERSION); // Map version also. + // Maps have their own version number. + Writer_WriteByte(writer, MY_SAVE_VERSION); // Write the map timer Writer_WriteInt32(writer, mapTime); #endif - - // Create and populate the MaterialArchive. -#ifdef __JHEXEN__ - materialArchive = MaterialArchive_New(true /* segment check */); -#else - materialArchive = MaterialArchive_New(false); -#endif - MaterialArchive_Write(materialArchive, writer); } - void endMapSegment() + void writeMaterialArchive() { - endSegment(); - MaterialArchive_Delete(materialArchive); materialArchive = 0; + MaterialArchive_Write(materialArchive, writer); } void writeElements() @@ -89,6 +102,8 @@ DENG2_PIMPL(MapStateWriter) { SV_WriteLine((Line *)P_ToPtr(DMU_LINE, i), thisPublic); } + + // endSegment(); } void writePolyobjs() @@ -103,6 +118,8 @@ DENG2_PIMPL(MapStateWriter) DENG_ASSERT(po != 0); po->write(thisPublic); } + + // endSegment(); #endif } @@ -127,12 +144,16 @@ DENG2_PIMPL(MapStateWriter) if(p.excludePlayers) { if(th->function == (thinkfunc_t) P_MobjThinker && ((mobj_t *) th)->player) - return false; // Continue iteration. + { + return false; + } } // Only the server saves this class of thinker? if((thInfo->flags & TSF_SERVERONLY) && IS_CLIENT) + { return false; + } // Write the header block for this thinker. Writer_WriteByte(p.msw->writer(), thInfo->thinkclass); // Thinker type byte. @@ -141,14 +162,14 @@ DENG2_PIMPL(MapStateWriter) // Write the thinker data. thInfo->writeFunc(th, p.msw); - return false; // Continue iteration. + return false; } /** * Serializes thinkers for both client and server. * - * @note Clients do not save data for all thinkers. In some cases the server - * will send it anyway (so saving it would just bloat client save states). + * @note Clients do not save data for all thinkers. In some cases the server will send it + * anyway (so saving it would just bloat client save states). * * @note Some thinker classes are NEVER saved by clients. */ @@ -157,16 +178,17 @@ DENG2_PIMPL(MapStateWriter) beginSegment(ASEG_THINKERS); #if __JHEXEN__ - Writer_WriteInt32(writer, thingArchiveSize); // number of mobjs. + Writer_WriteInt32(writer, thingArchive->size()); // number of mobjs. #endif // Serialize qualifying thinkers. writethinkerworker_params_t parm; de::zap(parm); - parm.msw = thisPublic; - parm.excludePlayers = excludePlayers; + parm.msw = thisPublic; + parm.excludePlayers = thingArchive->excludePlayers(); Thinker_Iterate(0/*all thinkers*/, writeThinkerWorker, &parm); // Mark the end of the thinkers. + // endSegment(); Writer_WriteByte(writer, TC_END); } @@ -175,6 +197,7 @@ DENG2_PIMPL(MapStateWriter) #if __JHEXEN__ beginSegment(ASEG_SCRIPTS); Game_ACScriptInterpreter().writeMapScriptData(thisPublic); + // endSegment(); #endif } @@ -183,22 +206,29 @@ DENG2_PIMPL(MapStateWriter) #if __JHEXEN__ beginSegment(ASEG_SOUNDS); SN_WriteSequences(writer); + // endSegment(); #endif } - void writeBrain() + void writeMisc() { +#if __JHEXEN__ + beginSegment(ASEG_MISC); + for(int i = 0; i < MAXPLAYERS; ++i) + { + Writer_WriteInt32(writer, localQuakeHappening[i]); + } +#endif #if __JDOOM__ - DENG_ASSERT(bossBrain != 0); - bossBrain->write(thisPublic); + DENG_ASSERT(theBossBrain != 0); + theBossBrain->write(thisPublic); #endif } void writeSoundTargets() { #if !__JHEXEN__ - // Not for us? - if(!IS_SERVER) return; + if(!IS_SERVER) return; // Not for us. // Write the total number. int count = 0; @@ -210,39 +240,29 @@ DENG2_PIMPL(MapStateWriter) count += 1; } } + + // beginSegment(); Writer_WriteInt32(writer, count); // Write the mobj references using the mobj archive. for(int i = 0; i < numsectors; ++i) { xsector_t *xsec = P_ToXSector((Sector *)P_ToPtr(DMU_SECTOR, i)); - if(xsec->soundTarget) { Writer_WriteInt32(writer, i); - Writer_WriteInt16(writer, SV_ThingArchiveId(xsec->soundTarget)); + Writer_WriteInt16(writer, thingArchive->serialIdFor(xsec->soundTarget)); } } -#endif - } - - void writeMisc() - { -#if __JHEXEN__ - beginSegment(ASEG_MISC); - - for(int i = 0; i < MAXPLAYERS; ++i) - { - Writer_WriteInt32(writer, localQuakeHappening[i]); - } + // endSegment(); #endif } }; -MapStateWriter::MapStateWriter(bool excludePlayers) +MapStateWriter::MapStateWriter(ThingArchive &thingArchive) : d(new Instance(this)) { - d->excludePlayers = excludePlayers; + d->thingArchive = &thingArchive; } void MapStateWriter::write(Writer *writer) @@ -250,24 +270,33 @@ void MapStateWriter::write(Writer *writer) DENG_ASSERT(writer != 0); d->writer = writer; - d->beginMapSegment(); + // Prepare and populate the material archive. + d->materialArchive = MaterialArchive_New(useMaterialArchiveSegments()); + + // Serialize the map. + d->beginSegment(ASEG_MAP_HEADER2); { + d->writeMapHeader(); + d->writeMaterialArchive(); + d->writeElements(); d->writePolyobjs(); d->writeThinkers(); d->writeACScriptData(); d->writeSoundSequences(); d->writeMisc(); - d->writeBrain(); d->writeSoundTargets(); } - d->endMapSegment(); + d->endSegment(); + + // Cleanup. + MaterialArchive_Delete(d->materialArchive); d->materialArchive = 0; } -Writer *MapStateWriter::writer() +ThingArchive::SerialId MapStateWriter::serialIdFor(mobj_t *mobj) { - DENG_ASSERT(d->writer != 0); - return d->writer; + DENG_ASSERT(d->thingArchive != 0); + return d->thingArchive->serialIdFor(mobj); } materialarchive_serialid_t MapStateWriter::serialIdFor(Material *material) @@ -275,3 +304,9 @@ materialarchive_serialid_t MapStateWriter::serialIdFor(Material *material) DENG_ASSERT(d->materialArchive != 0); return MaterialArchive_FindUniqueSerialId(d->materialArchive, material); } + +Writer *MapStateWriter::writer() +{ + DENG_ASSERT(d->writer != 0); + return d->writer; +} diff --git a/doomsday/plugins/common/src/mobj.cpp b/doomsday/plugins/common/src/mobj.cpp index 65ab7ed154..01053db199 100644 --- a/doomsday/plugins/common/src/mobj.cpp +++ b/doomsday/plugins/common/src/mobj.cpp @@ -385,16 +385,16 @@ void mobj_s::write(MapStateWriter *msw) const #if !__JHEXEN__ // A version 2 features: archive number and target. - Writer_WriteInt16(writer, SV_ThingArchiveId((mobj_t*) original)); - Writer_WriteInt16(writer, SV_ThingArchiveId(mo->target)); + Writer_WriteInt16(writer, msw->serialIdFor((mobj_t*) original)); + Writer_WriteInt16(writer, msw->serialIdFor(mo->target)); # if __JDOOM__ || __JDOOM64__ // Ver 5 features: Save tracer (fixes Archvile, Revenant bug) - Writer_WriteInt16(writer, SV_ThingArchiveId(mo->tracer)); + Writer_WriteInt16(writer, msw->serialIdFor(mo->tracer)); # endif #endif - Writer_WriteInt16(writer, SV_ThingArchiveId(mo->onMobj)); + Writer_WriteInt16(writer, msw->serialIdFor(mo->onMobj)); // Info for drawing: position. Writer_WriteInt32(writer, FLT2FIX(mo->origin[VX])); @@ -451,7 +451,7 @@ void mobj_s::write(MapStateWriter *msw) const if(mo->flags & MF_CORPSE) Writer_WriteInt32(writer, 0); else - Writer_WriteInt32(writer, SV_ThingArchiveId(INT2PTR(mobj_t, mo->special2))); + Writer_WriteInt32(writer, msw->serialIdFor(INT2PTR(mobj_t, mo->special2))); break; default: @@ -469,7 +469,7 @@ void mobj_s::write(MapStateWriter *msw) const if(mo->flags & MF_CORPSE) Writer_WriteInt32(writer, 0); else - Writer_WriteInt32(writer, (int) SV_ThingArchiveId(mo->target)); + Writer_WriteInt32(writer, (int) msw->serialIdFor(mo->target)); #endif // Reaction time: if non 0, don't attack yet. @@ -512,7 +512,7 @@ void mobj_s::write(MapStateWriter *msw) const Writer_WriteInt32(writer, FLT2FIX(mo->floorClip)); #if __JHEXEN__ - Writer_WriteInt32(writer, SV_ThingArchiveId((mobj_t *) original)); + Writer_WriteInt32(writer, msw->serialIdFor((mobj_t *) original)); Writer_WriteInt32(writer, mo->tid); Writer_WriteInt32(writer, mo->special); Writer_Write(writer, mo->args, sizeof(mo->args)); @@ -534,7 +534,7 @@ void mobj_s::write(MapStateWriter *msw) const if(mo->flags & MF_CORPSE) Writer_WriteInt32(writer, 0); else - Writer_WriteInt32(writer, SV_ThingArchiveId(mo->tracer)); + Writer_WriteInt32(writer, msw->serialIdFor(mo->tracer)); break; default: @@ -545,76 +545,10 @@ void mobj_s::write(MapStateWriter *msw) const Writer_WriteInt32(writer, PTR2INT(mo->lastEnemy)); #elif __JHERETIC__ - Writer_WriteInt16(writer, SV_ThingArchiveId(mo->generator)); + Writer_WriteInt16(writer, msw->serialIdFor(mo->generator)); #endif } -static void RestoreMobj(mobj_t *mo, int ver) -{ -#if __JDOOM64__ - DENG_UNUSED(ver); -#endif - - mo->info = &MOBJINFO[mo->type]; - - Mobj_SetState(mo, PTR2INT(mo->state)); -#if __JHEXEN__ - if(mo->flags2 & MF2_DORMANT) - mo->tics = -1; -#endif - - if(mo->player) - { - // The player number translation table is used to find out the - // *current* (actual) player number of the referenced player. - int pNum = saveToRealPlayerNum[PTR2INT(mo->player) - 1]; - -#if __JHEXEN__ - if(pNum < 0) - { - // This saved player does not exist in the current game! - // Destroy this mobj. - Z_Free(mo); - - return; // Don't add this thinker. - } -#endif - - mo->player = &players[pNum]; - mo->dPlayer = mo->player->plr; - - mo->dPlayer->mo = mo; - //mo->dPlayer->clAngle = mo->angle; /* $unifiedangles */ - mo->dPlayer->lookDir = 0; /* $unifiedangles */ - } - - mo->visAngle = mo->angle >> 16; - -#if !__JHEXEN__ - if(mo->dPlayer && !mo->dPlayer->inGame) - { - mo->dPlayer->mo = 0; - Mobj_Destroy(mo); - - return; - } -#endif - -#if !__JDOOM64__ - // Do we need to update this mobj's flag values? - if(ver < MOBJ_SAVEVERSION) - { - SV_TranslateLegacyMobjFlags(mo, ver); - } -#endif - - P_MobjLink(mo); - mo->floorZ = P_GetDoublep(Mobj_Sector(mo), DMU_FLOOR_HEIGHT); - mo->ceilingZ = P_GetDoublep(Mobj_Sector(mo), DMU_CEILING_HEIGHT); - - return; -} - int mobj_s::read(MapStateReader *msr) { #define FF_FULLBRIGHT 0x8000 ///< Used to be a flag in thing->frame. @@ -627,7 +561,7 @@ int mobj_s::read(MapStateReader *msr) #if !__JHEXEN__ if(ver >= 2) // Version 2 has mobj archive numbers. { - SV_InsertThingInArchive(this, Reader_ReadInt16(reader)); + msr->addMobjToThingArchive(this, Reader_ReadInt16(reader)); } #endif @@ -756,7 +690,7 @@ int mobj_s::read(MapStateReader *msr) #if __JHEXEN__ floorClip = FIX2FLT(Reader_ReadInt32(reader)); - SV_InsertThingInArchive(this, Reader_ReadInt32(reader)); + msr->addMobjToThingArchive(this, Reader_ReadInt32(reader)); tid = Reader_ReadInt32(reader); #else // For nightmare respawn. @@ -873,8 +807,62 @@ int mobj_s::read(MapStateReader *msr) } #endif - // Restore! (unmangle) - RestoreMobj(this, ver); + /* + * Restore! (unmangle) + */ + info = &MOBJINFO[type]; + + Mobj_SetState(this, PTR2INT(state)); +#if __JHEXEN__ + if(flags2 & MF2_DORMANT) + { + tics = -1; + } +#endif + + if(player) + { + // The player number translation table is used to find out the + // *current* (actual) player number of the referenced player. + player = msr->player(PTR2INT(player)); +#if __JHEXEN__ + if(!player) + { + // This saved player does not exist in the current game! + // Destroy this mobj. + Mobj_Destroy(this); + return false; + } +#endif + dPlayer = player->plr; + + dPlayer->mo = this; + //dPlayer->clAngle = angle; /* $unifiedangles */ + dPlayer->lookDir = 0; /* $unifiedangles */ + } + + visAngle = angle >> 16; + +#if !__JHEXEN__ + if(dPlayer && !dPlayer->inGame) + { + dPlayer->mo = 0; + Mobj_Destroy(this); + return false; + } +#endif + +#if !__JDOOM64__ + // Do we need to update this mobj's flag values? + if(ver < MOBJ_SAVEVERSION) + { + SV_TranslateLegacyMobjFlags(this, ver); + } +#endif + + P_MobjLink(this); + floorZ = P_GetDoublep(Mobj_Sector(this), DMU_FLOOR_HEIGHT); + ceilingZ = P_GetDoublep(Mobj_Sector(this), DMU_CEILING_HEIGHT); return false; diff --git a/doomsday/plugins/common/src/p_ceiling.cpp b/doomsday/plugins/common/src/p_ceiling.cpp index 0af720c3c2..77df8767ec 100644 --- a/doomsday/plugins/common/src/p_ceiling.cpp +++ b/doomsday/plugins/common/src/p_ceiling.cpp @@ -300,8 +300,8 @@ int ceiling_s::read(MapStateReader *msr) { // Its in the old format which serialized ceiling_t // Padding at the start (an old thinker_t struct) - byte junk[16]; // sizeof thinker_t - Reader_Read(reader, junk, 16); + int32_t junk[4]; // sizeof thinker_t + Reader_Read(reader, (byte *)junk, 16); // Start of used data members. #if __JHEXEN__ @@ -329,8 +329,10 @@ int ceiling_s::read(MapStateReader *msr) thinker.function = T_MoveCeiling; #if !__JHEXEN__ - if(!((thinker_t *)junk)->function) + if(junk[2] == 0) // thinker_t::function + { Thinker_SetStasis(&thinker, true); + } #endif } diff --git a/doomsday/plugins/common/src/p_mapsetup.cpp b/doomsday/plugins/common/src/p_mapsetup.cpp index 17d7469c25..ddc2839e38 100644 --- a/doomsday/plugins/common/src/p_mapsetup.cpp +++ b/doomsday/plugins/common/src/p_mapsetup.cpp @@ -972,8 +972,8 @@ static void P_ResetWorldState() #endif #if __JDOOM__ - delete bossBrain; - bossBrain = new BossBrain; + delete theBossBrain; + theBossBrain = new BossBrain; #endif #if __JHEXEN__ diff --git a/doomsday/plugins/common/src/p_plat.cpp b/doomsday/plugins/common/src/p_plat.cpp index 96b714ca07..92365864fc 100644 --- a/doomsday/plugins/common/src/p_plat.cpp +++ b/doomsday/plugins/common/src/p_plat.cpp @@ -264,8 +264,8 @@ int plat_t::read(MapStateReader *msr) { // Its in the old format which serialized plat_t // Padding at the start (an old thinker_t struct) - byte junk[16]; // sizeof thinker_t - Reader_Read(reader, junk, 16); + int32_t junk[4]; // sizeof thinker_t + Reader_Read(reader, (byte *)junk, 16); // Start of used data members. // A 32bit pointer to sector, serialized. @@ -286,8 +286,10 @@ int plat_t::read(MapStateReader *msr) thinker.function = T_PlatRaise; #if !__JHEXEN__ - if(!((thinker_t *)junk)->function) + if(junk[2] == 0) + { Thinker_SetStasis(&thinker, true); + } #endif } diff --git a/doomsday/plugins/common/src/p_saveg.cpp b/doomsday/plugins/common/src/p_saveg.cpp index 9a32bd2967..b2c0ed1d5e 100644 --- a/doomsday/plugins/common/src/p_saveg.cpp +++ b/doomsday/plugins/common/src/p_saveg.cpp @@ -21,270 +21,66 @@ #include "common.h" #include "p_saveg.h" -#include "d_net.h" // NetSv_SaveGame #include "dmu_lib.h" -#include "g_common.h" #include "p_actor.h" -#include "p_map.h" // P_TelefragMobjsTouchingPlayers #include "p_inventory.h" -#include "p_tick.h" // mapTime +#include "p_tick.h" // mapTime #include "p_saveio.h" -#include "polyobjs.h" #include "gamestatewriter.h" #include "gamestatereader.h" #include "mapstatereader.h" #include "mapstatewriter.h" -#if __JDOOM__ -# include "p_oldsvg.h" -#endif -#if __JHERETIC__ -# include "p_oldsvg.h" -#endif +#include "saveslots.h" +#include #include #include -#include #include #include static bool inited = false; +static GameStateReaderFactory gameStateReaderFactory; static SaveSlots sslots(NUMSAVESLOTS); -SaveSlots *saveSlots = &sslots; - -SaveInfo const *curInfo; - -static playerheader_t playerHeader; -dd_bool playerHeaderOK; - -#if __JHEXEN__ -/// Symbolic identifier used to mark references to players in map states. -static ThingSerialId const TargetPlayerId = -2; -#endif - -static mobj_t **thingArchive; -int thingArchiveVersion; -uint thingArchiveSize; -dd_bool thingArchiveExcludePlayers; int saveToRealPlayerNum[MAXPLAYERS]; #if __JHEXEN__ -static targetplraddress_t *targetPlayerAddrs; -#endif - -#if __JHEXEN__ -byte *saveBuffer; +targetplraddress_t *targetPlayerAddrs; #endif #if !__JHEXEN__ /** - * Compose the (possibly relative) path to the game-save associated - * with @a sessionId. + * Compose the save game file name for the specified @a sessionId. * * @param sessionId Unique game-session identifier. * * @return File path to the reachable save directory. If the game-save path * is unreachable then a zero-length string is returned instead. */ -static AutoStr *composeGameSavePathForClientSessionId(uint sessionId) +static de::String saveNameForClientSessionId(uint sessionId) { - AutoStr *path = AutoStr_NewStd(); - // Do we have a valid path? - if(!F_MakePath(SV_ClientSavePath())) + if(!F_MakePath(de::NativePath(SV_ClientSavePath()).expand().toUtf8().constData())) { - return path; // return zero-length string. + return ""; } // Compose the full game-save path and filename. - Str_Appendf(path, "%s" CLIENTSAVEGAMENAME "%08X." SAVEGAMEEXTENSION, SV_ClientSavePath(), sessionId); - F_TranslatePath(path, path); - return path; -} -#endif - -dd_bool SV_OpenGameSaveFile(Str const *fileName, dd_bool write) -{ -#if __JHEXEN__ - if(!write) - { - bool result = M_ReadFile(Str_Text(fileName), (char **)&saveBuffer) > 0; - // Set the save pointer. - SV_HxSavePtr()->b = saveBuffer; - return result; - } - else -#endif - { - SV_OpenFile(fileName, write? "wp" : "rp"); - } - - return SV_File() != 0; -} - -#if __JHEXEN__ -void SV_OpenMapSaveFile(Str const *path) -{ - DENG_ASSERT(path != 0); - - App_Log(DE2_DEV_RES_MSG, "openMapSaveFile: Opening \"%s\"", Str_Text(path)); - - // Load the file - size_t bufferSize = M_ReadFile(Str_Text(path), (char **)&saveBuffer); - if(!bufferSize) - { - throw de::Error("openMapSaveFile", "Failed opening \"" + de::String(Str_Text(path)) + "\" for read"); - } - - SV_HxSavePtr()->b = saveBuffer; - SV_HxSetSaveEndPtr(saveBuffer + bufferSize); -} -#endif - -void SV_SaveInfo_Read(SaveInfo *info, Reader *reader) -{ -#if __JHEXEN__ - // Read the magic byte to determine the high-level format. - int magic = Reader_ReadInt32(reader); - SV_HxSavePtr()->b -= 4; // Rewind the stream. - - if((!IS_NETWORK_CLIENT && magic != MY_SAVE_MAGIC) || - ( IS_NETWORK_CLIENT && magic != MY_CLIENT_SAVE_MAGIC)) - { - // Perhaps the old v9 format? - info->read_Hx_v9(reader); - } - else -#endif - { - info->read(reader); - } -} - -#if __JHEXEN__ -dd_bool SV_HxHaveMapStateForSlot(int slot, uint map) -{ - DENG_ASSERT(inited); - AutoStr *path = saveSlots->composeSavePathForSlot(slot, (int)map + 1); - if(path && !Str_IsEmpty(path)) - { - return SV_ExistingFile(path); - } - return false; -} -#endif - -void SV_InitThingArchiveForLoad(uint size) -{ - thingArchiveSize = size; - thingArchive = reinterpret_cast(M_Calloc(thingArchiveSize * sizeof(*thingArchive))); -} - -struct countmobjthinkerstoarchive_params_t -{ - uint count; - bool excludePlayers; -}; - -static int countMobjThinkersToArchive(thinker_t *th, void *context) -{ - countmobjthinkerstoarchive_params_t *parms = (countmobjthinkerstoarchive_params_t *) context; - if(!(Mobj_IsPlayer((mobj_t *) th) && parms->excludePlayers)) - { - parms->count++; - } - return false; // Continue iteration. -} - -void SV_InitThingArchiveForSave(dd_bool excludePlayers) -{ - // Count the number of things we'll be writing. - countmobjthinkerstoarchive_params_t parm; de::zap(parm); - parm.count = 0; - parm.excludePlayers = excludePlayers; - Thinker_Iterate((thinkfunc_t) P_MobjThinker, countMobjThinkersToArchive, &parm); - - thingArchiveSize = parm.count; - thingArchive = reinterpret_cast(M_Calloc(thingArchiveSize * sizeof(*thingArchive))); - thingArchiveExcludePlayers = excludePlayers; + return de::String(CLIENTSAVEGAMENAME "%1") + .arg(sessionId, 8, 16, QChar('0')).toUpper(); } - -void SV_InsertThingInArchive(mobj_t const *mo, ThingSerialId thingId) -{ - DENG_ASSERT(mo != 0); - -#if __JHEXEN__ - if(thingArchiveVersion >= 1) #endif - { - thingId -= 1; - } #if __JHEXEN__ - // Only signed in Hexen. - DENG2_ASSERT(thingId >= 0); - if(thingId < 0) return; // Does this ever occur? -#endif - - DENG_ASSERT(thingArchive != 0); - DENG_ASSERT((unsigned)thingId < thingArchiveSize); - thingArchive[thingId] = const_cast(mo); -} - -void SV_ClearThingArchive() +dd_bool SV_HxHaveMapStateForSlot(int slotNumber, uint map) { - M_Free(thingArchive); thingArchive = 0; - thingArchiveSize = 0; -} - -ThingSerialId SV_ThingArchiveId(mobj_t const *mo) -{ - DENG_ASSERT(inited); - DENG_ASSERT(thingArchive != 0); - - if(!mo) return 0; - - // We only archive mobj thinkers. - if(((thinker_t *) mo)->function != (thinkfunc_t) P_MobjThinker) - { - return 0; - } - -#if __JHEXEN__ - if(mo->player && thingArchiveExcludePlayers) - { - return TargetPlayerId; - } -#endif - - uint firstUnused = 0; - bool found = false; - for(uint i = 0; i < thingArchiveSize; ++i) + DENG2_ASSERT(inited); + if(SV_SavePath().isEmpty() || !SV_SaveSlots().isKnownSlot(slotNumber)) { - if(!thingArchive[i] && !found) - { - firstUnused = i; - found = true; - continue; - } - - if(thingArchive[i] == mo) - { - return i + 1; - } - } - - if(!found) - { - Con_Error("SV_ThingArchiveId: Thing archive exhausted!"); - return 0; // No number available! + return false; } - - // Insert it in the archive. - thingArchive[firstUnused] = const_cast(mo); - return firstUnused + 1; + return SV_ExistingFile(SV_SavePath() / SV_SaveSlots()[slotNumber].saveInfo().fileNameForMap(map)); } -#if __JHEXEN__ void SV_InitTargetPlayers() { targetPlayerAddrs = 0; @@ -301,69 +97,15 @@ void SV_ClearTargetPlayers() } #endif // __JHEXEN__ -mobj_t *SV_GetArchiveThing(ThingSerialId thingId, void *address) +void SV_TranslateLegacyMobjFlags(mobj_t *mo, int ver) { - DENG_ASSERT(inited); -#if !__JHEXEN__ - DENG_UNUSED(address); -#endif - -#if __JHEXEN__ - if(thingId == TargetPlayerId) - { - targetplraddress_t *tpa = reinterpret_cast(M_Malloc(sizeof(targetplraddress_t))); - - tpa->address = (void **)address; - - tpa->next = targetPlayerAddrs; - targetPlayerAddrs = tpa; - - return 0; - } -#endif - - DENG_ASSERT(thingArchive != 0); - -#if __JHEXEN__ - if(thingArchiveVersion < 1) - { - // Old format (base 0). - - // A NULL reference? - if(thingId == -1) return 0; - - if(thingId < 0 || (unsigned) thingId > thingArchiveSize - 1) - return 0; - } - else +#if __JDOOM64__ + // Nothing to do. + DENG2_UNUSED(mo); + DENG2_UNUSED(ver); + return; #endif - { - // New format (base 1). - - // A NULL reference? - if(thingId == 0) return 0; - - if(thingId < 1 || (unsigned) thingId > thingArchiveSize) - { - App_Log(DE2_RES_WARNING, "SV_GetArchiveThing: Invalid thing Id %i", thingId); - return 0; - } - - thingId -= 1; - } - - return thingArchive[thingId]; -} - -playerheader_t *SV_GetPlayerHeader() -{ - DENG_ASSERT(playerHeaderOK); - return &playerHeader; -} -#if !__JDOOM64__ -void SV_TranslateLegacyMobjFlags(mobj_t *mo, int ver) -{ #if __JDOOM__ || __JHERETIC__ if(ver < 6) { @@ -418,51 +160,39 @@ void SV_TranslateLegacyMobjFlags(mobj_t *mo, int ver) mo->flags3 = mo->info->flags3; } } -#endif -/** - * Prepare and write the player header info. - */ -void SV_WritePlayerHeader(Writer *writer) +void playerheader_s::write(Writer *writer) { - playerheader_t *ph = &playerHeader; - - SV_BeginSegment(ASEG_PLAYER_HEADER); Writer_WriteByte(writer, 2); // version byte - ph->numPowers = NUM_POWER_TYPES; - ph->numKeys = NUM_KEY_TYPES; - ph->numFrags = MAXPLAYERS; - ph->numWeapons = NUM_WEAPON_TYPES; - ph->numAmmoTypes = NUM_AMMO_TYPES; - ph->numPSprites = NUMPSPRITES; + numPowers = NUM_POWER_TYPES; + numKeys = NUM_KEY_TYPES; + numFrags = MAXPLAYERS; + numWeapons = NUM_WEAPON_TYPES; + numAmmoTypes = NUM_AMMO_TYPES; + numPSprites = NUMPSPRITES; #if __JHERETIC__ || __JHEXEN__ || __JDOOM64__ - ph->numInvItemTypes = NUM_INVENTORYITEM_TYPES; + numInvItemTypes = NUM_INVENTORYITEM_TYPES; #endif #if __JHEXEN__ - ph->numArmorTypes = NUMARMOR; + numArmorTypes = NUMARMOR; #endif - Writer_WriteInt32(writer, ph->numPowers); - Writer_WriteInt32(writer, ph->numKeys); - Writer_WriteInt32(writer, ph->numFrags); - Writer_WriteInt32(writer, ph->numWeapons); - Writer_WriteInt32(writer, ph->numAmmoTypes); - Writer_WriteInt32(writer, ph->numPSprites); + Writer_WriteInt32(writer, numPowers); + Writer_WriteInt32(writer, numKeys); + Writer_WriteInt32(writer, numFrags); + Writer_WriteInt32(writer, numWeapons); + Writer_WriteInt32(writer, numAmmoTypes); + Writer_WriteInt32(writer, numPSprites); #if __JDOOM64__ || __JHERETIC__ || __JHEXEN__ - Writer_WriteInt32(writer, ph->numInvItemTypes); + Writer_WriteInt32(writer, numInvItemTypes); #endif #if __JHEXEN__ - Writer_WriteInt32(writer, ph->numArmorTypes); + Writer_WriteInt32(writer, numArmorTypes); #endif - - playerHeaderOK = true; } -/** - * Read player header info from the game state. - */ -void SV_ReadPlayerHeader(Reader *reader, int saveVersion) +void playerheader_s::read(Reader *reader, int saveVersion) { #if __JHEXEN__ if(saveVersion >= 4) @@ -470,63 +200,61 @@ void SV_ReadPlayerHeader(Reader *reader, int saveVersion) if(saveVersion >= 5) #endif { - SV_AssertSegment(ASEG_PLAYER_HEADER); int ver = Reader_ReadByte(reader); #if !__JHERETIC__ - DENG_UNUSED(ver); + DENG2_UNUSED(ver); #endif - playerHeader.numPowers = Reader_ReadInt32(reader); - playerHeader.numKeys = Reader_ReadInt32(reader); - playerHeader.numFrags = Reader_ReadInt32(reader); - playerHeader.numWeapons = Reader_ReadInt32(reader); - playerHeader.numAmmoTypes = Reader_ReadInt32(reader); - playerHeader.numPSprites = Reader_ReadInt32(reader); + numPowers = Reader_ReadInt32(reader); + numKeys = Reader_ReadInt32(reader); + numFrags = Reader_ReadInt32(reader); + numWeapons = Reader_ReadInt32(reader); + numAmmoTypes = Reader_ReadInt32(reader); + numPSprites = Reader_ReadInt32(reader); #if __JHERETIC__ if(ver >= 2) - playerHeader.numInvItemTypes = Reader_ReadInt32(reader); + numInvItemTypes = Reader_ReadInt32(reader); else - playerHeader.numInvItemTypes = NUM_INVENTORYITEM_TYPES; + numInvItemTypes = NUM_INVENTORYITEM_TYPES; #endif #if __JHEXEN__ || __JDOOM64__ - playerHeader.numInvItemTypes = Reader_ReadInt32(reader); + numInvItemTypes = Reader_ReadInt32(reader); #endif #if __JHEXEN__ - playerHeader.numArmorTypes = Reader_ReadInt32(reader); + numArmorTypes = Reader_ReadInt32(reader); #endif } else // The old format didn't save the counts. { #if __JHEXEN__ - playerHeader.numPowers = 9; - playerHeader.numKeys = 11; - playerHeader.numFrags = 8; - playerHeader.numWeapons = 4; - playerHeader.numAmmoTypes = 2; - playerHeader.numPSprites = 2; - playerHeader.numInvItemTypes = 33; - playerHeader.numArmorTypes = 4; + numPowers = 9; + numKeys = 11; + numFrags = 8; + numWeapons = 4; + numAmmoTypes = 2; + numPSprites = 2; + numInvItemTypes = 33; + numArmorTypes = 4; #elif __JDOOM__ || __JDOOM64__ - playerHeader.numPowers = 6; - playerHeader.numKeys = 6; - playerHeader.numFrags = 4; // Why was this only 4? - playerHeader.numWeapons = 9; - playerHeader.numAmmoTypes = 4; - playerHeader.numPSprites = 2; + numPowers = 6; + numKeys = 6; + numFrags = 4; // Why was this only 4? + numWeapons = 9; + numAmmoTypes = 4; + numPSprites = 2; # if __JDOOM64__ - playerHeader.numInvItemTypes = 3; + numInvItemTypes = 3; # endif #elif __JHERETIC__ - playerHeader.numPowers = 9; - playerHeader.numKeys = 3; - playerHeader.numFrags = 4; // ? - playerHeader.numWeapons = 8; - playerHeader.numInvItemTypes = 14; - playerHeader.numAmmoTypes = 6; - playerHeader.numPSprites = 2; + numPowers = 9; + numKeys = 3; + numFrags = 4; // ? + numWeapons = 8; + numInvItemTypes = 14; + numAmmoTypes = 6; + numPSprites = 2; #endif } - playerHeaderOK = true; } enum sectorclass_t @@ -858,7 +586,7 @@ void SV_WriteLine(Line *li, MapStateWriter *msw) // Extended General? if(xli->xg) { - SV_WriteXGLine(li, writer); + SV_WriteXGLine(li, msw); } #endif } @@ -873,7 +601,7 @@ void SV_ReadLine(Line *li, MapStateReader *msr) Reader *reader = msr->reader(); int mapVersion = msr->mapVersion(); - dd_bool xgDataFollows = false; + bool xgDataFollows = false; #if __JHEXEN__ if(mapVersion >= 4) #else @@ -883,7 +611,7 @@ void SV_ReadLine(Line *li, MapStateReader *msr) xgDataFollows = Reader_ReadByte(reader) == 1; } #ifdef __JHEXEN__ - DENG_UNUSED(xgDataFollows); + DENG2_UNUSED(xgDataFollows); #endif // A version byte? @@ -904,7 +632,9 @@ void SV_ReadLine(Line *li, MapStateReader *msr) int flags = Reader_ReadInt16(reader); if(xli->flags & ML_TWOSIDED) + { flags |= ML_TWOSIDED; + } if(ver < 4) { @@ -959,14 +689,14 @@ void SV_ReadLine(Line *li, MapStateReader *msr) #if __JHEXEN__ xli->special = Reader_ReadByte(reader); - xli->arg1 = Reader_ReadByte(reader); - xli->arg2 = Reader_ReadByte(reader); - xli->arg3 = Reader_ReadByte(reader); - xli->arg4 = Reader_ReadByte(reader); - xli->arg5 = Reader_ReadByte(reader); + xli->arg1 = Reader_ReadByte(reader); + xli->arg2 = Reader_ReadByte(reader); + xli->arg3 = Reader_ReadByte(reader); + xli->arg4 = Reader_ReadByte(reader); + xli->arg5 = Reader_ReadByte(reader); #else xli->special = Reader_ReadInt16(reader); - /*xli->tag =*/ Reader_ReadInt16(reader); + /*xli->tag =*/ Reader_ReadInt16(reader); #endif // For each side @@ -1061,184 +791,180 @@ void SV_ReadLine(Line *li, MapStateReader *msr) #if !__JHEXEN__ if(xgDataFollows) { - SV_ReadXGLine(li, reader, mapVersion); + SV_ReadXGLine(li, msr); } #endif } -#if __JHEXEN__ -void SV_WriteMovePoly(polyevent_t const *th, MapStateWriter *msw) +void SV_Initialize() { - Writer *writer = msw->writer(); - - Writer_WriteByte(writer, 1); // Write a version byte. + SV_InitIO(); - // Note we don't bother to save a byte to tell if the function - // is present as we ALWAYS add one when loading. + if(!inited) + { + // Declare the native game state reader. + gameStateReaderFactory.declareReader(&GameStateReader::recognize, &GameStateReader::make); + } - Writer_WriteInt32(writer, th->polyobj); - Writer_WriteInt32(writer, th->intSpeed); - Writer_WriteInt32(writer, th->dist); - Writer_WriteInt32(writer, th->fangle); - Writer_WriteInt32(writer, FLT2FIX(th->speed[VX])); - Writer_WriteInt32(writer, FLT2FIX(th->speed[VY])); + inited = true; } -int SV_ReadMovePoly(polyevent_t *th, MapStateReader *msr) +void SV_Shutdown() { - Reader *reader = msr->reader(); - int mapVersion = msr->mapVersion(); + if(!inited) return; - if(mapVersion >= 4) - { - // Note: the thinker class byte has already been read. - /*int ver =*/ Reader_ReadByte(reader); // version byte. - - // Start of used data members. - th->polyobj = Reader_ReadInt32(reader); - th->intSpeed = Reader_ReadInt32(reader); - th->dist = Reader_ReadInt32(reader); - th->fangle = Reader_ReadInt32(reader); - th->speed[VX] = FIX2FLT(Reader_ReadInt32(reader)); - th->speed[VY] = FIX2FLT(Reader_ReadInt32(reader)); - } - else - { - // Its in the old pre V4 format which serialized polyevent_t - // Padding at the start (an old thinker_t struct) - byte junk[16]; // sizeof thinker_t - Reader_Read(reader, junk, 16); - - // Start of used data members. - th->polyobj = Reader_ReadInt32(reader); - th->intSpeed = Reader_ReadInt32(reader); - th->dist = Reader_ReadInt32(reader); - th->fangle = Reader_ReadInt32(reader); - th->speed[VX] = FIX2FLT(Reader_ReadInt32(reader)); - th->speed[VY] = FIX2FLT(Reader_ReadInt32(reader)); - } + SV_ShutdownIO(); + sslots.clearAll(); - th->thinker.function = T_MovePoly; + inited = false; +} - return true; // Add this thinker. +SaveSlots &SV_SaveSlots() +{ + DENG2_ASSERT(inited); + return sslots; } -#endif // __JHEXEN__ -void SV_Initialize() +void SV_DeclareGameStateReader(GameStateRecognizeFunc recognizer, GameStateReaderMakeFunc maker) { - static bool firstInit = true; + DENG2_ASSERT(inited); + gameStateReaderFactory.declareReader(recognizer, maker); +} - SV_InitIO(); +bool SV_RecognizeGameState(SaveInfo &info) +{ + DENG2_ASSERT(inited); + return gameStateReaderFactory.recognize(info); +} - inited = true; - if(firstInit) +static std::auto_ptr gameStateReaderFor(SaveInfo &info) +{ + std::auto_ptr p(gameStateReaderFactory.recognizeAndMakeReader(info)); + if(!p.get()) { - firstInit = false; - playerHeaderOK = false; - thingArchive = 0; - thingArchiveSize = 0; + /// @throw Error The saved game state format was not recognized. + throw de::Error("gameStateReaderFor", "Unrecognized savegame format"); + } + return p; +} + +dd_bool SV_LoadGame(int slotNumber) +{ + DENG2_ASSERT(inited); + #if __JHEXEN__ - targetPlayerAddrs = 0; - saveBuffer = 0; + int const logicalSlot = BASE_SLOT; +#else + int const logicalSlot = slotNumber; #endif + + if(SV_SavePath().isEmpty()) + { + App_Log(DE2_RES_ERROR, "Game not loaded: path \"%s\" is unreachable", + de::NativePath(SV_SavePath()).pretty().toLatin1().constData()); + return false; } - // Reset last-used and quick-save slot tracking. - Con_SetInteger2("game-save-last-slot", -1, SVF_WRITE_OVERRIDE); - Con_SetInteger("game-save-quick-slot", -1); + try + { +#if __JHEXEN__ + // Copy all needed save files to the base slot. + if(slotNumber != BASE_SLOT) + { + SV_SaveSlots().copySlot(slotNumber, BASE_SLOT); + } +#endif - // (Re)Initialize the saved game paths, possibly creating them if they do not exist. - SV_ConfigureSavePaths(); -} + SaveInfo &saveInfo = SV_SaveSlots()[logicalSlot].saveInfo(); -void SV_Shutdown() -{ - if(!inited) return; + // Attempt to recognize and load the saved game state. + App_Log(DE2_LOG_VERBOSE, "Attempting load save game from \"%s\"", + de::NativePath(SV_SavePath() / saveInfo.fileName()).pretty().toLatin1().constData()); - SV_ShutdownIO(); - saveSlots->clearAllSaveInfo(); + gameStateReaderFor(saveInfo)->read(saveInfo); - inited = false; + // Make note of the last used save slot. + Con_SetInteger2("game-save-last-slot", slotNumber, SVF_WRITE_OVERRIDE); + + return true; + } + catch(de::Error const &er) + { + App_Log(DE2_RES_WARNING, "Error loading save slot #%i:\n%s", + slotNumber, er.asText().toLatin1().constData()); + } + + return false; } -dd_bool SV_LoadGame(int slot) +dd_bool SV_SaveGame(int slotNumber, char const *description) { - DENG_ASSERT(inited); + DENG2_ASSERT(inited); #if __JHEXEN__ int const logicalSlot = BASE_SLOT; #else - int const logicalSlot = slot; + int const logicalSlot = slotNumber; #endif - if(!saveSlots->isValidSlot(slot)) + if(!SV_SaveSlots().isKnownSlot(slotNumber)) { + DENG2_ASSERT(!"Invalid slot specified"); return false; } - App_Log(DE2_RES_VERBOSE, "Attempting load of save slot #%i...", slot); - - AutoStr *path = saveSlots->composeSavePathForSlot(slot); - if(Str_IsEmpty(path)) + if(!description || !description[0]) { - App_Log(DE2_RES_ERROR, "Game not loaded: path \"%s\" is unreachable", SV_SavePath()); + DENG2_ASSERT(!"Empty description specified for slot"); return false; } -#if __JHEXEN__ - // Copy all needed save files to the base slot. - /// @todo Why do this BEFORE loading?? (G_NewGame() does not load the serialized map state) - /// @todo Does any caller ever attempt to load the base slot?? (Doesn't seem logical) - if(slot != BASE_SLOT) + if(SV_SavePath().isEmpty()) { - saveSlots->copySlot(slot, BASE_SLOT); + App_Log(DE2_RES_WARNING, "Cannot save game: path \"%s\" is unreachable", + de::NativePath(SV_SavePath()).pretty().toLatin1().constData()); + return false; } -#endif + SaveInfo *saveInfo = SaveInfo::newWithCurrentSessionMetadata( + SV_SaveSlots()[logicalSlot].fileName(), + description); try { - SaveInfo &info = saveSlots->saveInfo(logicalSlot); + App_Log(DE2_LOG_VERBOSE, "Attempting save game to \"%s\"", + de::NativePath(SV_SavePath() / saveInfo->fileName()).pretty().toLatin1().constData()); - if(GameStateReader::recognize(&info, path)) - { - GameStateReader().read(&info, path); - } - // Perhaps an original game state? -#if __JDOOM__ - else if(DoomV9GameStateReader::recognize(&info, path)) - { - DoomV9GameStateReader().read(&info, path); - } -#endif -#if __JHERETIC__ - else if(HereticV13GameStateReader::recognize(&info, path)) - { - HereticV13GameStateReader().read(&info, path); - } + GameStateWriter().write(*saveInfo); + + // Swap the save info. + SV_SaveSlots()[logicalSlot].replaceSaveInfo(saveInfo); + +#if __JHEXEN__ + // Copy base slot to destination slot. + SV_SaveSlots().copySlot(logicalSlot, slotNumber); #endif - else - { - /// @throw Error The savegame was not recognized. - throw de::Error("loadGameState", "Unrecognized savegame format"); - } // Make note of the last used save slot. - Con_SetInteger2("game-save-last-slot", slot, SVF_WRITE_OVERRIDE); + Con_SetInteger2("game-save-last-slot", slotNumber, SVF_WRITE_OVERRIDE); return true; } catch(de::Error const &er) { - App_Log(DE2_RES_WARNING, "Error loading save slot #%i:\n%s", slot, er.asText().toLatin1().constData()); + App_Log(DE2_RES_WARNING, "Error writing to save slot #%i:\n%s", + slotNumber, er.asText().toLatin1().constData()); } + // Discard the useless save info. + delete saveInfo; + return false; } void SV_SaveGameClient(uint sessionId) { #if !__JHEXEN__ // unsupported in libhexen - DENG_ASSERT(inited); + DENG2_ASSERT(inited); player_t *pl = &players[CONSOLEPLAYER]; mobj_t *mo = pl->plr->mo; @@ -1246,19 +972,26 @@ void SV_SaveGameClient(uint sessionId) if(!IS_CLIENT || !mo) return; - playerHeaderOK = false; // Uninitialized. - - AutoStr *gameSavePath = composeGameSavePathForClientSessionId(sessionId); - if(!SV_OpenFile(gameSavePath, "wp")) + if(SV_ClientSavePath().isEmpty()) { - App_Log(DE2_RES_WARNING, "SV_SaveGameClient: Failed opening \"%s\" for writing", Str_Text(gameSavePath)); return; } // Prepare new save info. - SaveInfo *info = SaveInfo::newWithCurrentSessionMetadata(AutoStr_New()); + SaveInfo *info = SaveInfo::newWithCurrentSessionMetadata(saveNameForClientSessionId(sessionId)); info->setSessionId(sessionId); + de::Path path = SV_ClientSavePath() / info->fileName(); + if(!SV_OpenFile(path, "wp")) + { + App_Log(DE2_RES_WARNING, "SV_SaveGameClient: Failed opening \"%s\" for writing", + de::NativePath(path).pretty().toLatin1().constData()); + + // Discard the useless save info. + delete info; + return; + } + Writer *writer = SV_NewWriter(); info->write(writer); @@ -1273,24 +1006,28 @@ void SV_SaveGameClient(uint sessionId) Writer_WriteFloat(writer, pl->plr->lookDir); /* $unifiedangles */ - SV_WritePlayerHeader(writer); - players[CONSOLEPLAYER].write(writer); + SV_BeginSegment(ASEG_PLAYER_HEADER); + playerheader_t plrHdr; + plrHdr.write(writer); + + players[CONSOLEPLAYER].write(writer, plrHdr); - MapStateWriter(thingArchiveExcludePlayers).write(writer); + ThingArchive thingArchive; + MapStateWriter(thingArchive).write(writer); /// @todo No consistency bytes in client saves? SV_CloseFile(); Writer_Delete(writer); delete info; #else - DENG_UNUSED(sessionId); + DENG2_UNUSED(sessionId); #endif } void SV_LoadGameClient(uint sessionId) { #if !__JHEXEN__ // unsupported in libhexen - DENG_ASSERT(inited); + DENG2_ASSERT(inited); player_t *cpl = players + CONSOLEPLAYER; mobj_t *mo = cpl->plr->mo; @@ -1298,26 +1035,25 @@ void SV_LoadGameClient(uint sessionId) if(!IS_CLIENT || !mo) return; - playerHeaderOK = false; // Uninitialized. + Reader *reader = SV_NewReader(); + SaveInfo *info = SaveInfo::fromReader(reader); + info->setFileName(saveNameForClientSessionId(sessionId)); - AutoStr *gameSavePath = composeGameSavePathForClientSessionId(sessionId); - if(!SV_OpenFile(gameSavePath, "rp")) + de::Path path = SV_ClientSavePath() / info->fileName(); + if(!SV_OpenFile(path, "rp")) { - App_Log(DE2_RES_WARNING, "SV_LoadGameClient: Failed opening \"%s\" for reading", Str_Text(gameSavePath)); + Reader_Delete(reader); + delete info; + App_Log(DE2_RES_WARNING, "SV_LoadGameClient: Failed opening \"%s\" for reading", + de::NativePath(path).pretty().toLatin1().constData()); return; } - SaveInfo *info = new SaveInfo; - Reader *reader = SV_NewReader(); - SV_SaveInfo_Read(info, reader); - - curInfo = info; - if(info->magic() != MY_CLIENT_SAVE_MAGIC) { + SV_CloseFile(); Reader_Delete(reader); delete info; - SV_CloseFile(); App_Log(DE2_RES_ERROR, "Client save file format not recognized"); return; } @@ -1326,8 +1062,7 @@ void SV_LoadGameClient(uint sessionId) if(!Uri_Equality(gameMapUri, info->mapUri())) { G_NewGame(info->mapUri(), 0/*default*/, &info->gameRules()); - /// @todo Necessary? - G_SetGameAction(GA_NONE); + G_SetGameAction(GA_NONE); /// @todo Necessary? } else { @@ -1347,96 +1082,47 @@ void SV_LoadGameClient(uint sessionId) cpl->plr->lookDir = Reader_ReadFloat(reader); /* $unifiedangles */ - SV_ReadPlayerHeader(reader, info->version()); - cpl->read(reader); - - MapStateReader(info->version()).read(reader); - - SV_CloseFile(); - Reader_Delete(reader); - delete info; -#else - DENG_UNUSED(sessionId); -#endif -} - -dd_bool SV_SaveGame(int slot, char const *description) -{ - DENG_ASSERT(inited); - #if __JHEXEN__ - int const logicalSlot = BASE_SLOT; + if(info->version() >= 4) #else - int const logicalSlot = slot; + if(info->version() >= 5) #endif - - if(!saveSlots->isValidSlot(slot)) { - DENG_ASSERT(!"Invalid slot specified"); - return false; - } - if(!description || !description[0]) - { - DENG_ASSERT(!"Empty description specified for slot"); - return false; - } - - AutoStr *path = saveSlots->composeSavePathForSlot(logicalSlot); - if(Str_IsEmpty(path)) - { - App_Log(DE2_RES_WARNING, "Cannot save game: path \"%s\" is unreachable", SV_SavePath()); - return false; + SV_AssertSegment(ASEG_PLAYER_HEADER); } + playerheader_t plrHdr; + plrHdr.read(reader, info->version()); - App_Log(DE2_LOG_VERBOSE, "Attempting save game to \"%s\"", Str_Text(path)); - - SaveInfo *info = SaveInfo::newWithCurrentSessionMetadata(AutoStr_FromTextStd(description)); - try - { - // In networked games the server tells the clients to save their games. -#if !__JHEXEN__ - NetSv_SaveGame(info->sessionId()); -#endif - - GameStateWriter().write(info, path); - - // Swap the save info. - saveSlots->replaceSaveInfo(logicalSlot, info); - -#if __JHEXEN__ - // Copy base slot to destination slot. - saveSlots->copySlot(logicalSlot, slot); -#endif - - // Make note of the last used save slot. - Con_SetInteger2("game-save-last-slot", slot, SVF_WRITE_OVERRIDE); + cpl->read(reader, plrHdr); - return true; - } - catch(de::Error const &er) - { - App_Log(DE2_RES_WARNING, "Error writing to save slot #%i:\n%s", slot, er.asText().toLatin1().constData()); - } + MapStateReader(info->version()).read(reader); - // Discard the useless save info. + SV_CloseFile(); + Reader_Delete(reader); delete info; - - return false; +#else + DENG2_UNUSED(sessionId); +#endif } #if __JHEXEN__ void SV_HxSaveHubMap() { - playerHeaderOK = false; // Uninitialized. - - SV_OpenFile(saveSlots->composeSavePathForSlot(BASE_SLOT, gameMap + 1), "wp"); + SaveInfo &saveInfo = SV_SaveSlots()[BASE_SLOT].saveInfo(); + de::Path const path = SV_SavePath() / saveInfo.fileNameForMap(gameMap); - // Set the mobj archive numbers - SV_InitThingArchiveForSave(true /*exclude players*/); + if(!SV_OpenFile(path, "wp")) + { + throw de::Error("SV_HxSaveHubMap", "Failed opening \"" + de::NativePath(path).pretty() + "\" for write"); + } Writer *writer = SV_NewWriter(); - MapStateWriter(thingArchiveExcludePlayers).write(writer); + // Set the mobj archive numbers + ThingArchive thingArchive; + thingArchive.initForSave(true/*exclude players*/); + + MapStateWriter(thingArchive).write(writer); // Close the output file SV_CloseFile(); @@ -1446,196 +1132,33 @@ void SV_HxSaveHubMap() void SV_HxLoadHubMap() { - /// @todo fixme: do not assume this pointer is still valid! - DENG_ASSERT(curInfo != 0); - SaveInfo const *info = curInfo; - // Only readMap() uses targetPlayerAddrs, so it's NULLed here for the // following check (player mobj redirection). targetPlayerAddrs = 0; - playerHeaderOK = false; // Uninitialized. - Reader *reader = SV_NewReader(); - // Been here before, load the previous map state. try { - SV_OpenMapSaveFile(saveSlots->composeSavePathForSlot(BASE_SLOT, gameMap + 1)); - - MapStateReader(info->version()).read(reader); - -#if __JHEXEN__ - SV_ClearThingArchive(); - Z_Free(saveBuffer); -#endif - } - catch(de::Error const &er) - { - App_Log(DE2_RES_WARNING, "Error loading hub map save state:\n%s", er.asText().toLatin1().constData()); - } - - Reader_Delete(reader); -} - -void SV_HxBackupPlayersInHub(playerbackup_t playerBackup[MAXPLAYERS]) -{ - DENG_ASSERT(playerBackup != 0); - - for(int i = 0; i < MAXPLAYERS; ++i) - { - playerbackup_t *pb = playerBackup + i; - player_t *plr = players + i; - - std::memcpy(&pb->player, plr, sizeof(player_t)); + SaveInfo &saveInfo = SV_SaveSlots()[BASE_SLOT].saveInfo(); + de::Path const path = SV_SavePath() / saveInfo.fileNameForMap(gameMap); - // Make a copy of the inventory states also. - for(int k = 0; k < NUM_INVENTORYITEM_TYPES; ++k) + if(!SV_OpenMapSaveFile(path)) { - pb->numInventoryItems[k] = P_InventoryCount(i, inventoryitemtype_t(k)); + Reader_Delete(reader); + throw de::Error("SV_HxLoadHubMap", "Failed opening \"" + de::NativePath(path).pretty() + "\" for read"); } - pb->readyItem = P_InventoryReadyItem(i); - } -} - -void SV_HxRestorePlayersInHub(playerbackup_t playerBackup[MAXPLAYERS], uint mapEntrance) -{ - DENG_ASSERT(playerBackup != 0); - for(int i = 0; i < MAXPLAYERS; ++i) - { - playerbackup_t *pb = playerBackup + i; - player_t *plr = players + i; - ddplayer_t *ddplr = plr->plr; - - if(!ddplr->inGame) continue; + MapStateReader(saveInfo.version()).read(reader); - std::memcpy(plr, &pb->player, sizeof(player_t)); - for(int k = 0; k < NUM_INVENTORYITEM_TYPES; ++k) - { - // Don't give back the wings of wrath if reborn. - if(k == IIT_FLY && plr->playerState == PST_REBORN) - continue; - - for(uint l = 0; l < pb->numInventoryItems[k]; ++l) - { - P_InventoryGive(i, inventoryitemtype_t(k), true); - } - } - P_InventorySetReadyItem(i, pb->readyItem); - - ST_LogEmpty(i); - plr->attacker = 0; - plr->poisoner = 0; - - int oldKeys = 0, oldPieces = 0; - dd_bool oldWeaponOwned[NUM_WEAPON_TYPES]; - if(IS_NETGAME || gameRules.deathmatch) - { - // In a network game, force all players to be alive - if(plr->playerState == PST_DEAD) - { - plr->playerState = PST_REBORN; - } - - if(!gameRules.deathmatch) - { - // Cooperative net-play; retain keys and weapons. - oldKeys = plr->keys; - oldPieces = plr->pieces; - - for(int k = 0; k < NUM_WEAPON_TYPES; ++k) - { - oldWeaponOwned[k] = plr->weapons[k].owned; - } - } - } - - dd_bool wasReborn = (plr->playerState == PST_REBORN); - - if(gameRules.deathmatch) - { - de::zap(plr->frags); - ddplr->mo = 0; - G_DeathMatchSpawnPlayer(i); - } - else - { - if(playerstart_t const *start = P_GetPlayerStart(mapEntrance, i, false)) - { - mapspot_t const *spot = &mapSpots[start->spot]; - P_SpawnPlayer(i, cfg.playerClass[i], spot->origin[VX], - spot->origin[VY], spot->origin[VZ], spot->angle, - spot->flags, false, true); - } - else - { - P_SpawnPlayer(i, cfg.playerClass[i], 0, 0, 0, 0, - MSF_Z_FLOOR, true, true); - } - } - - if(wasReborn && IS_NETGAME && !gameRules.deathmatch) - { - // Restore keys and weapons when reborn in co-op. - plr->keys = oldKeys; - plr->pieces = oldPieces; - - int bestWeapon = 0; - for(int k = 0; k < NUM_WEAPON_TYPES; ++k) - { - if(oldWeaponOwned[k]) - { - bestWeapon = k; - plr->weapons[k].owned = true; - } - } - - plr->ammo[AT_BLUEMANA].owned = 25; /// @todo values.ded - plr->ammo[AT_GREENMANA].owned = 25; /// @todo values.ded - - // Bring up the best weapon. - if(bestWeapon) - { - plr->pendingWeapon = weapontype_t(bestWeapon); - } - } + SV_HxReleaseSaveBuffer(); } - - mobj_t *targetPlayerMobj = 0; - for(int i = 0; i < MAXPLAYERS; ++i) - { - player_t *plr = players + i; - ddplayer_t *ddplr = plr->plr; - - if(!ddplr->inGame) continue; - - if(!targetPlayerMobj) - { - targetPlayerMobj = ddplr->mo; - } - } - - /// @todo Redirect anything targeting a player mobj - /// FIXME! This only supports single player games!! - if(targetPlayerAddrs) + catch(de::Error const &er) { - for(targetplraddress_t *p = targetPlayerAddrs; p; p = p->next) - { - *(p->address) = targetPlayerMobj; - } - - SV_ClearTargetPlayers(); - - /* - * When XG is available in Hexen, call this after updating target player - * references (after a load) - ds - // The activator mobjs must be set. - XL_UpdateActivators(); - */ + App_Log(DE2_RES_WARNING, "Error loading hub map save state:\n%s", + er.asText().toLatin1().constData()); } - // Destroy all things touching players. - P_TelefragMobjsTouchingPlayers(); + Reader_Delete(reader); } #endif diff --git a/doomsday/plugins/common/src/p_saveio.cpp b/doomsday/plugins/common/src/p_saveio.cpp index f79765c540..9723a0fcb4 100644 --- a/doomsday/plugins/common/src/p_saveio.cpp +++ b/doomsday/plugins/common/src/p_saveio.cpp @@ -27,61 +27,26 @@ #include "p_savedef.h" #include "saveinfo.h" #include "api_materialarchive.h" +#include -#include -#include - -static dd_bool inited; +static bool inited; static LZFILE *savefile; -static ddstring_t savePath; // e.g., "savegame/" +static de::Path savePath; // e.g., "savegame" #if !__JHEXEN__ -static ddstring_t clientSavePath; // e.g., "savegame/client/" +static de::Path clientSavePath; // e.g., "savegame/client" #endif #if __JHEXEN__ +static byte *saveBuffer; static saveptr_t saveptr; static void *saveEndPtr; #endif -static void errorIfNotInited(char const *callerName) -{ - if(inited) return; - Con_Error("%s: Savegame I/O is not presently initialized.", callerName); - // Unreachable. Prevents static analysers from getting rather confused, poor things. - exit(1); -} - -/// @return Possibly relative saved game directory. Does not need to be free'd. -static AutoStr *composeSaveDir() -{ - AutoStr *dir = AutoStr_NewStd(); - - if(CommandLine_CheckWith("-savedir", 1)) - { - Str_Set(dir, CommandLine_Next()); - // Add a trailing backslash is necessary. - if(Str_RAt(dir, 0) != '/') - Str_AppendChar(dir, '/'); - return dir; - } - - // Use the default path. - GameInfo gameInfo; - if(DD_GameInfo(&gameInfo)) - { - Str_Appendf(dir, SAVEGAME_DEFAULT_DIR "/%s/", Str_Text(gameInfo.identityKey)); - return dir; - } - - Con_Error("composeSaveDir: Error, failed retrieving GameInfo."); - exit(1); // Unreachable. -} - void SV_InitIO() { - Str_Init(&savePath); + savePath.clear(); #if !__JHEXEN__ - Str_Init(&clientSavePath); + clientSavePath.clear(); #endif inited = true; savefile = 0; @@ -93,78 +58,60 @@ void SV_ShutdownIO() SV_CloseFile(); - Str_Free(&savePath); -#if !__JHEXEN__ - Str_Free(&clientSavePath); -#endif - inited = false; } -char const *SV_SavePath() +void SV_SetupSaveDirectory(de::Path newRootSaveDir) { - return Str_Text(&savePath); -} + DENG2_ASSERT(inited); + if(!newRootSaveDir.isEmpty()) + { + savePath = newRootSaveDir; #if !__JHEXEN__ -char const *SV_ClientSavePath() -{ - return Str_Text(&clientSavePath); -} + clientSavePath = newRootSaveDir / "client"; #endif -// Compose and create the saved game directories. -void SV_ConfigureSavePaths() -{ - DENG_ASSERT(inited); - - AutoStr *saveDir = composeSaveDir(); - dd_bool savePathExists; - - Str_Set(&savePath, Str_Text(saveDir)); + // Ensure that these paths exist. + bool savePathExists = F_MakePath(de::NativePath(savePath).expand().toUtf8().constData()); #if !__JHEXEN__ - Str_Clear(&clientSavePath); Str_Appendf(&clientSavePath, "%sclient/", Str_Text(saveDir)); + if(!F_MakePath(de::NativePath(clientSavePath).expand().toUtf8().constData())) + { + savePathExists = false; + } #endif - // Ensure that these paths exist. - savePathExists = F_MakePath(Str_Text(&savePath)); + if(savePathExists) + { + return; + } + } + + savePath.clear(); #if !__JHEXEN__ - if(!F_MakePath(Str_Text(&clientSavePath))) - savePathExists = false; + clientSavePath.clear(); #endif - if(!savePathExists) - { - App_Log(DE2_RES_ERROR, "SV_ConfigureSavePaths: Failed to locate \"%s\". Perhaps it could " - "not be created (insufficent permissions?). Saving will not be possible.", - Str_Text(&savePath)); - } -} - -LZFILE *SV_File() -{ - return savefile; + App_Log(DE2_RES_ERROR, "SV_SetupSaveDirectory: \"%s\" could not be accessed. Perhaps it could " + "not be created (insufficient permissions?). Saving will not be possible.", + de::NativePath(savePath).pretty().toLatin1().constData()); } -LZFILE *SV_OpenFile(Str const *filePath, char const *mode) +de::Path SV_SavePath() { - DENG_ASSERT(savefile == 0); - savefile = lzOpen(Str_Text(filePath), (char *)mode); - return savefile; + return savePath; } -void SV_CloseFile() +#if !__JHEXEN__ +de::Path SV_ClientSavePath() { - if(savefile) - { - lzClose(savefile); - savefile = 0; - } + return clientSavePath; } +#endif -dd_bool SV_ExistingFile(Str const *filePath) +bool SV_ExistingFile(de::Path filePath) { - if(FILE *fp = fopen(Str_Text(filePath), "rb")) + if(FILE *fp = fopen(de::NativePath(filePath).expand().toUtf8().constData(), "rb")) { fclose(fp); return true; @@ -172,27 +119,25 @@ dd_bool SV_ExistingFile(Str const *filePath) return false; } -int SV_RemoveFile(Str const *filePath) +int SV_RemoveFile(de::Path filePath) { - if(!filePath) return 1; - return remove(Str_Text(filePath)); + return remove(de::NativePath(filePath).expand().toUtf8().constData()); } -void SV_CopyFile(Str const *srcPath, Str const *destPath) +void SV_CopyFile(de::Path srcPath, de::Path destPath) { - if(!srcPath || !destPath) return; - if(!SV_ExistingFile(srcPath)) return; char *buffer; - size_t length = M_ReadFile(Str_Text(srcPath), &buffer); + size_t length = M_ReadFile(de::NativePath(srcPath).expand().toUtf8().constData(), &buffer); if(!length) { - App_Log(DE2_RES_ERROR, "SV_CopyFile: Failed opening \"%s\" for reading", Str_Text(srcPath)); + App_Log(DE2_RES_ERROR, "SV_CopyFile: Failed opening \"%s\" for reading", + de::NativePath(srcPath).pretty().toLatin1().constData()); return; } - if(LZFILE *outf = lzOpen((char*)Str_Text(destPath), "wp")) + if(LZFILE *outf = lzOpen(de::NativePath(destPath).expand().toUtf8().constData(), "wp")) { lzWrite(buffer, length, outf); lzClose(outf); @@ -201,7 +146,70 @@ void SV_CopyFile(Str const *srcPath, Str const *destPath) Z_Free(buffer); } -#ifdef __JHEXEN__ +LZFILE *SV_OpenFile(de::Path filePath, de::String mode) +{ + DENG2_ASSERT(savefile == 0); + savefile = lzOpen(de::NativePath(filePath).expand().toUtf8().constData(), mode.toUtf8().constData()); + return savefile; +} + +void SV_CloseFile() +{ + if(savefile) + { + lzClose(savefile); + savefile = 0; + } +} + +bool SV_OpenGameSaveFile(de::Path path, bool write) +{ + App_Log(DE2_DEV_RES_MSG, "SV_OpenGameSaveFile: Opening \"%s\"", + de::NativePath(path).pretty().toLatin1().constData()); + +#if __JHEXEN__ + if(!write) + { + size_t bufferSize = M_ReadFile(de::NativePath(path).expand().toUtf8().constData(), (char **)&saveBuffer); + if(!bufferSize) return false; + + SV_HxSavePtr()->b = saveBuffer; + SV_HxSetSaveEndPtr(saveBuffer + bufferSize); + + return true; + } + else +#endif + { + SV_OpenFile(path, write? "wp" : "rp"); + } + + return SV_File() != 0; +} + +#if __JHEXEN__ +bool SV_OpenMapSaveFile(de::Path path) +{ + App_Log(DE2_DEV_RES_MSG, "SV_OpenMapSaveFile: Opening \"%s\"", + de::NativePath(path).pretty().toLatin1().constData()); + + // Load the file + size_t bufferSize = M_ReadFile(de::NativePath(path).expand().toUtf8().constData(), (char **)&saveBuffer); + if(!bufferSize) return false; + + SV_HxSavePtr()->b = saveBuffer; + SV_HxSetSaveEndPtr(saveBuffer + bufferSize); + + return true; +} +#endif + +LZFILE *SV_File() +{ + return savefile; +} + +#if __JHEXEN__ saveptr_t *SV_HxSavePtr() { return &saveptr; @@ -212,15 +220,20 @@ void SV_HxSetSaveEndPtr(void *endPtr) saveEndPtr = endPtr; } -dd_bool SV_HxBytesLeft() +size_t SV_HxBytesLeft() { return (byte *) saveEndPtr - saveptr.b; } + +void SV_HxReleaseSaveBuffer() +{ + Z_Free(saveBuffer); +} #endif void SV_Seek(uint offset) { - errorIfNotInited("SV_SetPos"); + DENG2_ASSERT(inited); #if __JHEXEN__ saveptr.b += offset; #else @@ -230,13 +243,13 @@ void SV_Seek(uint offset) void SV_Write(void const *data, int len) { - errorIfNotInited("SV_Write"); + DENG2_ASSERT(inited); lzWrite((void *)data, len, savefile); } void SV_WriteByte(byte val) { - errorIfNotInited("SV_WriteByte"); + DENG2_ASSERT(inited); lzPutC(val, savefile); } @@ -246,7 +259,7 @@ void SV_WriteShort(unsigned short val) void SV_WriteShort(short val) #endif { - errorIfNotInited("SV_WriteShort"); + DENG2_ASSERT(inited); lzPutW(val, savefile); } @@ -256,23 +269,23 @@ void SV_WriteLong(unsigned int val) void SV_WriteLong(long val) #endif { - errorIfNotInited("SV_WriteLong"); + DENG2_ASSERT(inited); lzPutL(val, savefile); } void SV_WriteFloat(float val) { - DENG_ASSERT(sizeof(val) == 4); + DENG2_ASSERT(sizeof(val) == 4); + DENG2_ASSERT(inited); int32_t temp = 0; - errorIfNotInited("SV_WriteFloat"); std::memcpy(&temp, &val, 4); lzPutL(temp, savefile); } void SV_Read(void *data, int len) { - errorIfNotInited("SV_Read"); + DENG2_ASSERT(inited); #if __JHEXEN__ std::memcpy(data, saveptr.b, len); saveptr.b += len; @@ -283,9 +296,9 @@ void SV_Read(void *data, int len) byte SV_ReadByte() { - errorIfNotInited("SV_ReadByte"); + DENG2_ASSERT(inited); #if __JHEXEN__ - DENG_ASSERT((saveptr.b + 1) <= (byte *) saveEndPtr); + DENG2_ASSERT((saveptr.b + 1) <= (byte *) saveEndPtr); return (*saveptr.b++); #else return lzGetC(savefile); @@ -294,9 +307,9 @@ byte SV_ReadByte() short SV_ReadShort() { - errorIfNotInited("SV_ReadShort"); + DENG2_ASSERT(inited); #if __JHEXEN__ - DENG_ASSERT((saveptr.w + 1) <= (short *) saveEndPtr); + DENG2_ASSERT((saveptr.w + 1) <= (short *) saveEndPtr); return (SHORT(*saveptr.w++)); #else return lzGetW(savefile); @@ -305,9 +318,9 @@ short SV_ReadShort() long SV_ReadLong() { - errorIfNotInited("SV_ReadLong"); + DENG2_ASSERT(inited); #if __JHEXEN__ - DENG_ASSERT((saveptr.l + 1) <= (int *) saveEndPtr); + DENG2_ASSERT((saveptr.l + 1) <= (int *) saveEndPtr); return (LONG(*saveptr.l++)); #else return lzGetL(savefile); @@ -316,17 +329,13 @@ long SV_ReadLong() float SV_ReadFloat() { -#if !__JHEXEN__ - float returnValue = 0; - int32_t val; -#endif - errorIfNotInited("SV_ReadFloat"); + DENG2_ASSERT(inited); + DENG2_ASSERT(sizeof(float) == 4); #if __JHEXEN__ return (FLOAT(*saveptr.f++)); #else - val = lzGetL(savefile); - returnValue = 0; - DENG_ASSERT(sizeof(float) == 4); + int32_t val = lzGetL(savefile); + float returnValue = 0; std::memcpy(&returnValue, &val, 4); return returnValue; #endif @@ -334,11 +343,11 @@ float SV_ReadFloat() void SV_AssertSegment(int segmentId) { - errorIfNotInited("SV_AssertSegment"); + DENG2_ASSERT(inited); #if __JHEXEN__ if(segmentId == ASEG_END && SV_HxBytesLeft() < 4) { - App_Log(DE2_LOG_WARNING, "Savegame lacks ASEG_END marker (unexpected end-of-file)\n"); + App_Log(DE2_LOG_WARNING, "Savegame lacks ASEG_END marker (unexpected end-of-file)"); return; } if(SV_ReadLong() != segmentId) @@ -352,7 +361,7 @@ void SV_AssertSegment(int segmentId) void SV_BeginSegment(int segType) { - errorIfNotInited("SV_BeginSegment"); + DENG2_ASSERT(inited); #if __JHEXEN__ SV_WriteLong(segType); #else diff --git a/doomsday/plugins/common/src/p_scroll.cpp b/doomsday/plugins/common/src/p_scroll.cpp index 20a4473b3f..c78ff63520 100644 --- a/doomsday/plugins/common/src/p_scroll.cpp +++ b/doomsday/plugins/common/src/p_scroll.cpp @@ -101,7 +101,7 @@ int scroll_s::read(MapStateReader *msr) else { // Side index is actually a DMU_ARCHIVE_INDEX. - dmuObject = (Side *)msr->sideArchive().at(sideIndex); + dmuObject = msr->side(sideIndex); } DENG_ASSERT(dmuObject != 0); diff --git a/doomsday/plugins/common/src/p_start.cpp b/doomsday/plugins/common/src/p_start.cpp index d8eabb1d5d..19577bcb4e 100644 --- a/doomsday/plugins/common/src/p_start.cpp +++ b/doomsday/plugins/common/src/p_start.cpp @@ -247,7 +247,7 @@ void P_Shutdown() P_ShutdownTerrainTypes(); P_FreeWeaponSlots(); #if __JDOOM__ - delete bossBrain; bossBrain = 0; + delete theBossBrain; theBossBrain = 0; #endif } @@ -717,7 +717,7 @@ void P_RebornPlayerInMultiplayer(int plrNum) #if __JDOOM__ || __JHERETIC__ || __JDOOM64__ if(!foundSpot) { - App_Log(DE2_DEV_MAP_MSG, "- force spawning at %i\n", p->startSpot); + App_Log(DE2_DEV_MAP_MSG, "- force spawning at %i", p->startSpot); if(assigned) { diff --git a/doomsday/plugins/common/src/p_switch.cpp b/doomsday/plugins/common/src/p_switch.cpp index 79a75b3e7f..416b7bc983 100644 --- a/doomsday/plugins/common/src/p_switch.cpp +++ b/doomsday/plugins/common/src/p_switch.cpp @@ -336,7 +336,7 @@ int materialchanger_s::read(MapStateReader *msr) else { // Side index is actually a DMU_ARCHIVE_INDEX. - side = (Side *)msr->sideArchive().at(sideIndex); + side = msr->side(sideIndex); } DENG_ASSERT(side != 0); diff --git a/doomsday/plugins/common/src/p_xgsave.cpp b/doomsday/plugins/common/src/p_xgsave.cpp index 3b65ec5511..9d689ad96f 100644 --- a/doomsday/plugins/common/src/p_xgsave.cpp +++ b/doomsday/plugins/common/src/p_xgsave.cpp @@ -26,8 +26,9 @@ #include "p_saveg.h" #include "p_xg.h" -void SV_WriteXGLine(Line *li, Writer *writer) +void SV_WriteXGLine(Line *li, MapStateWriter *msw) { + Writer *writer = msw->writer(); xline_t *xline = P_ToXLine(li); // Version byte. @@ -50,15 +51,16 @@ void SV_WriteXGLine(Line *li, Writer *writer) Writer_WriteByte(writer, xg->disabled); Writer_WriteInt32(writer, xg->timer); Writer_WriteInt32(writer, xg->tickerTimer); - Writer_WriteInt16(writer, SV_ThingArchiveId((mobj_t *)xg->activator)); + Writer_WriteInt16(writer, msw->serialIdFor((mobj_t *)xg->activator)); Writer_WriteInt32(writer, xg->idata); Writer_WriteFloat(writer, xg->fdata); Writer_WriteInt32(writer, xg->chIdx); Writer_WriteFloat(writer, xg->chTimer); } -void SV_ReadXGLine(Line *li, Reader *reader, int /*mapVersion*/) +void SV_ReadXGLine(Line *li, MapStateReader *msr) { + Reader *reader = msr->reader(); xline_t *xline = P_ToXLine(li); // Read version. diff --git a/doomsday/plugins/common/src/p_xgsec.c b/doomsday/plugins/common/src/p_xgsec.c index 9ebe14c7d8..00048abb44 100644 --- a/doomsday/plugins/common/src/p_xgsec.c +++ b/doomsday/plugins/common/src/p_xgsec.c @@ -2757,7 +2757,7 @@ void P_ApplyWind(mobj_t *mo, Sector *sec) return; // Wind does not affect cameras. info = &(P_ToXSector(sec)->xg->info); - ang = PI * info->windAngle / 180; + ang = DD_PI * info->windAngle / 180; if(IS_CLIENT) { @@ -2925,7 +2925,7 @@ void XS_Thinker(xsthinker_t* xs) if(xg->info.materialMoveSpeed[0] != 0) { coord_t floorOffset[2]; - double ang = PI * xg->info.materialMoveAngle[0] / 180; + double ang = DD_PI * xg->info.materialMoveAngle[0] / 180; P_GetDoublepv(sector, DMU_FLOOR_MATERIAL_OFFSET_XY, floorOffset); floorOffset[VX] -= cos(ang) * xg->info.materialMoveSpeed[0]; @@ -2939,7 +2939,7 @@ void XS_Thinker(xsthinker_t* xs) if(xg->info.materialMoveSpeed[1] != 0) { coord_t ceilOffset[2]; - double ang = PI * xg->info.materialMoveAngle[1] / 180; + double ang = DD_PI * xg->info.materialMoveAngle[1] / 180; P_GetDoublepv(sector, DMU_CEILING_MATERIAL_OFFSET_XY, ceilOffset); ceilOffset[VX] -= cos(ang) * xg->info.materialMoveSpeed[1]; @@ -3046,17 +3046,17 @@ D_CMD(MovePlane) if(argc < 2) { - App_Log(DE2_SCR_NOTE, "Usage: %s (opts)\n", argv[0]); - App_Log(DE2_LOG_SCR, "Opts can be:\n"); - App_Log(DE2_LOG_SCR, " here [crush] [off] (z/units) [speed]\n"); - App_Log(DE2_LOG_SCR, " at (x) (y) [crush] [off] (z/units) [speed]\n"); - App_Log(DE2_LOG_SCR, " tag (sector-tag) [crush] [off] (z/units) [speed]\n"); + App_Log(DE2_SCR_NOTE, "Usage: %s (opts)", argv[0]); + App_Log(DE2_LOG_SCR, "Opts can be:"); + App_Log(DE2_LOG_SCR, " here [crush] [off] (z/units) [speed]"); + App_Log(DE2_LOG_SCR, " at (x) (y) [crush] [off] (z/units) [speed]"); + App_Log(DE2_LOG_SCR, " tag (sector-tag) [crush] [off] (z/units) [speed]"); return true; } if(IS_CLIENT) { - App_Log(DE2_SCR_ERROR, "Clients can't move planes\n"); + App_Log(DE2_SCR_ERROR, "Clients can't move planes"); return false; } @@ -3098,7 +3098,7 @@ D_CMD(MovePlane) } else { // Unknown mode. - App_Log(DE2_SCR_ERROR, "Unknown mode\n"); + App_Log(DE2_SCR_ERROR, "Unknown mode"); return false; } @@ -3108,7 +3108,7 @@ D_CMD(MovePlane) // No more arguments? if(argc == p) { - App_Log(DE2_LOG_MAP, "Ceiling = %g\nFloor = %g\n", ceilingheight, floorheight); + App_Log(DE2_LOG_MAP, "Ceiling = %g, Floor = %g", ceilingheight, floorheight); return true; } @@ -3133,7 +3133,7 @@ D_CMD(MovePlane) } else { - App_Log(DE2_SCR_ERROR, "You must specify Z-units\n"); + App_Log(DE2_SCR_ERROR, "You must specify Z-units"); return false; // Required parameter missing. } diff --git a/doomsday/plugins/common/src/player.cpp b/doomsday/plugins/common/src/player.cpp index 333c5d418d..46f588d572 100644 --- a/doomsday/plugins/common/src/player.cpp +++ b/doomsday/plugins/common/src/player.cpp @@ -698,7 +698,7 @@ void P_SetMessage(player_t *pl, int flags, char const *msg) if(pl == &players[CONSOLEPLAYER]) { - App_Log(DE2_LOG_MAP | (cfg.echoMsg? DE2_LOG_NOTE : DE2_LOG_VERBOSE), "%s\n", msg); + App_Log(DE2_LOG_MAP | (cfg.echoMsg? DE2_LOG_NOTE : DE2_LOG_VERBOSE), "%s", msg); } // Servers are responsible for sending these messages to the clients. @@ -724,7 +724,7 @@ void P_SetYellowMessage(player_t *pl, int flags, char const *msg) if(pl == &players[CONSOLEPLAYER]) { - App_Log(DE2_LOG_MAP | (cfg.echoMsg? DE2_LOG_NOTE : DE2_LOG_VERBOSE), "%s\n", msg); + App_Log(DE2_LOG_MAP | (cfg.echoMsg? DE2_LOG_NOTE : DE2_LOG_VERBOSE), "%s", msg); } // Servers are responsible for sending these messages to the clients. @@ -895,10 +895,12 @@ void P_PlayerThinkCamera(player_t *player) D_CMD(SetCamera) { + DENG_UNUSED(src); DENG_UNUSED(argc); + int p = atoi(argv[1]); if(p < 0 || p >= MAXPLAYERS) { - App_Log(DE2_LOG_SCR| DE2_LOG_ERROR, "Invalid console number %i\n", p); + App_Log(DE2_LOG_SCR| DE2_LOG_ERROR, "Invalid console number %i", p); return false; } @@ -980,6 +982,8 @@ void P_PlayerSetArmorType(player_t *plr, int type) D_CMD(SetViewMode) { + DENG_UNUSED(src); + if(argc > 2) return false; int pl = CONSOLEPLAYER; @@ -1004,6 +1008,8 @@ D_CMD(SetViewMode) D_CMD(SetViewLock) { + DENG_UNUSED(src); + int pl = CONSOLEPLAYER, lock; if(!stricmp(argv[0], "lockmode")) @@ -1040,23 +1046,25 @@ D_CMD(SetViewLock) D_CMD(MakeLocal) { + DENG_UNUSED(src); DENG_UNUSED(argc); + if(G_GameState() != GS_MAP) { - App_Log(DE2_LOG_ERROR | DE2_LOG_MAP, "You must be in a game to create a local player.\n"); + App_Log(DE2_LOG_ERROR | DE2_LOG_MAP, "You must be in a game to create a local player."); return false; } int p = atoi(argv[1]); if(p < 0 || p >= MAXPLAYERS) { - App_Log(DE2_SCR_ERROR, "Invalid console number %i.\n", p); + App_Log(DE2_SCR_ERROR, "Invalid console number %i.", p); return false; } player_t *plr = &players[p]; if(plr->plr->inGame) { - App_Log(DE2_LOG_ERROR | DE2_LOG_MAP, "Player %i is already in the game.\n", p); + App_Log(DE2_LOG_ERROR | DE2_LOG_MAP, "Player %i is already in the game.", p); return false; } @@ -1074,6 +1082,8 @@ D_CMD(MakeLocal) D_CMD(PrintPlayerCoords) { + DENG_UNUSED(src); DENG_UNUSED(argc); DENG_UNUSED(argv); + mobj_t *mo; if(G_GameState() != GS_MAP) @@ -1082,7 +1092,7 @@ D_CMD(PrintPlayerCoords) if(!(mo = players[CONSOLEPLAYER].plr->mo)) return false; - App_Log(DE2_LOG_MAP, "Console %i: X=%g Y=%g Z=%g\n", CONSOLEPLAYER, + App_Log(DE2_LOG_MAP, "Console %i: X=%g Y=%g Z=%g", CONSOLEPLAYER, mo->origin[VX], mo->origin[VY], mo->origin[VZ]); return true; @@ -1090,8 +1100,10 @@ D_CMD(PrintPlayerCoords) D_CMD(CycleSpy) { + DENG_UNUSED(src); DENG_UNUSED(argc); DENG_UNUSED(argv); + //// @todo The engine should do this. - App_Log(DE2_LOG_MAP | DE2_LOG_ERROR, "Spying not allowed.\n"); + App_Log(DE2_LOG_MAP | DE2_LOG_ERROR, "Spying not allowed."); #if 0 if(G_GameState() == GS_MAP && !deathmatch) { @@ -1112,18 +1124,20 @@ D_CMD(CycleSpy) D_CMD(SpawnMobj) { + DENG_UNUSED(src); + if(argc != 5 && argc != 6) { - App_Log(DE2_SCR_NOTE, "Usage: %s (type) (x) (y) (z) (angle)\n", argv[0]); - App_Log(DE2_LOG_SCR, "Type must be a defined Thing ID or Name.\n"); - App_Log(DE2_LOG_SCR, "Z is an offset from the floor, 'floor', 'ceil' or 'random'.\n"); - App_Log(DE2_LOG_SCR, "Angle (0..360) is optional.\n"); + App_Log(DE2_SCR_NOTE, "Usage: %s (type) (x) (y) (z) (angle)", argv[0]); + App_Log(DE2_LOG_SCR, "Type must be a defined Thing ID or Name."); + App_Log(DE2_LOG_SCR, "Z is an offset from the floor, 'floor', 'ceil' or 'random'."); + App_Log(DE2_LOG_SCR, "Angle (0..360) is optional."); return true; } if(IS_CLIENT) { - App_Log(DE2_SCR_ERROR, "%s can't be used by clients\n", argv[0]); + App_Log(DE2_SCR_ERROR, "%s can't be used by clients", argv[0]); return false; } @@ -1134,7 +1148,7 @@ D_CMD(SpawnMobj) // Try to find it by name instead. if((type = mobjtype_t(Def_Get(DD_DEF_MOBJ_BY_NAME, argv[1], 0))) < 0) { - App_Log(DE2_LOG_RES | DE2_LOG_ERROR, "Undefined thing type %s\n", argv[1]); + App_Log(DE2_LOG_RES | DE2_LOG_ERROR, "Undefined thing type %s", argv[1]); return false; } } @@ -1185,6 +1199,8 @@ D_CMD(SpawnMobj) mo->intFlags |= MIF_FADE; } // << d64tc +#else + DENG_UNUSED(mo); #endif } @@ -1210,14 +1226,11 @@ angle_t Player_ViewYawAngle(int playerNum) return ang; } -void player_s::write(Writer *writer) const +void player_s::write(Writer *writer, playerheader_t &plrHdr) const { #if __JDOOM64__ || __JHERETIC__ || __JHEXEN__ - int const plrnum = P_GetPlayerNum(this); + int const plrnum = P_GetPlayerNum(this); #endif - playerheader_t const &plrHdr = *SV_GetPlayerHeader(); - - int i, numPSprites = plrHdr.numPSprites; player_t temp, *p = &temp; ddplayer_t ddtemp, *dp = &ddtemp; @@ -1228,7 +1241,7 @@ void player_s::write(Writer *writer) const temp.plr = &ddtemp; // Convert the psprite states. - for(i = 0; i < numPSprites; ++i) + for(int i = 0; i < plrHdr.numPSprites; ++i) { pspdef_t *pspDef = &temp.pSprites[i]; @@ -1266,7 +1279,7 @@ void player_s::write(Writer *writer) const Writer_WriteInt32(writer, p->health); #if __JHEXEN__ - for(i = 0; i < plrHdr.numArmorTypes; ++i) + for(int i = 0; i < plrHdr.numArmorTypes; ++i) { Writer_WriteInt32(writer, p->armorPoints[i]); } @@ -1276,7 +1289,7 @@ void player_s::write(Writer *writer) const #endif #if __JDOOM64__ || __JHEXEN__ - for(i = 0; i < plrHdr.numInvItemTypes; ++i) + for(int i = 0; i < plrHdr.numInvItemTypes; ++i) { inventoryitemtype_t type = inventoryitemtype_t(IIT_FIRST + i); @@ -1286,7 +1299,7 @@ void player_s::write(Writer *writer) const Writer_WriteInt32(writer, P_InventoryReadyItem(plrnum)); #endif - for(i = 0; i < plrHdr.numPowers; ++i) + for(int i = 0; i < plrHdr.numPowers; ++i) { Writer_WriteInt32(writer, p->powers[i]); } @@ -1294,7 +1307,7 @@ void player_s::write(Writer *writer) const #if __JHEXEN__ Writer_WriteInt32(writer, p->keys); #else - for(i = 0; i < plrHdr.numKeys; ++i) + for(int i = 0; i < plrHdr.numKeys; ++i) { Writer_WriteInt32(writer, p->keys[i]); } @@ -1306,7 +1319,7 @@ void player_s::write(Writer *writer) const Writer_WriteInt32(writer, p->backpack); #endif - for(i = 0; i < plrHdr.numFrags; ++i) + for(int i = 0; i < plrHdr.numFrags; ++i) { Writer_WriteInt32(writer, p->frags[i]); } @@ -1314,12 +1327,12 @@ void player_s::write(Writer *writer) const Writer_WriteInt32(writer, p->readyWeapon); Writer_WriteInt32(writer, p->pendingWeapon); - for(i = 0; i < plrHdr.numWeapons; ++i) + for(int i = 0; i < plrHdr.numWeapons; ++i) { Writer_WriteInt32(writer, p->weapons[i].owned); } - for(i = 0; i < plrHdr.numAmmoTypes; ++i) + for(int i = 0; i < plrHdr.numAmmoTypes; ++i) { Writer_WriteInt32(writer, p->ammo[i].owned); #if !__JHEXEN__ @@ -1348,7 +1361,7 @@ void player_s::write(Writer *writer) const Writer_WriteInt32(writer, dp->fixedColorMap); Writer_WriteInt32(writer, p->colorMap); - for(i = 0; i < numPSprites; ++i) + for(int i = 0; i < plrHdr.numPSprites; ++i) { pspdef_t *psp = &p->pSprites[i]; @@ -1366,7 +1379,7 @@ void player_s::write(Writer *writer) const #endif #if __JHERETIC__ - for(i = 0; i < plrHdr.numInvItemTypes; ++i) + for(int i = 0; i < plrHdr.numInvItemTypes; ++i) { inventoryitemtype_t type = inventoryitemtype_t(IIT_FIRST + i); @@ -1394,7 +1407,7 @@ void player_s::write(Writer *writer) const #endif } -void player_s::read(Reader *reader) +void player_s::read(Reader *reader, playerheader_t &plrHdr) { int const plrnum = P_GetPlayerNum(this); @@ -1404,7 +1417,6 @@ void player_s::read(Reader *reader) cfg.playerClass[plrnum] = playerclass_t(Reader_ReadByte(reader)); #endif - playerheader_t const &plrHdr = *SV_GetPlayerHeader(); ddplayer_t *dp = plr; #if __JHEXEN__ diff --git a/doomsday/plugins/common/src/polyobjs.cpp b/doomsday/plugins/common/src/polyobjs.cpp index 9b153d45e0..e221e6cedd 100644 --- a/doomsday/plugins/common/src/polyobjs.cpp +++ b/doomsday/plugins/common/src/polyobjs.cpp @@ -331,6 +331,62 @@ int polyevent_s::read(MapStateReader *msr) return true; // Add this thinker. } +void SV_WriteMovePoly(polyevent_t const *th, MapStateWriter *msw) +{ + Writer *writer = msw->writer(); + + Writer_WriteByte(writer, 1); // Write a version byte. + + // Note we don't bother to save a byte to tell if the function + // is present as we ALWAYS add one when loading. + + Writer_WriteInt32(writer, th->polyobj); + Writer_WriteInt32(writer, th->intSpeed); + Writer_WriteInt32(writer, th->dist); + Writer_WriteInt32(writer, th->fangle); + Writer_WriteInt32(writer, FLT2FIX(th->speed[VX])); + Writer_WriteInt32(writer, FLT2FIX(th->speed[VY])); +} + +int SV_ReadMovePoly(polyevent_t *th, MapStateReader *msr) +{ + Reader *reader = msr->reader(); + int mapVersion = msr->mapVersion(); + + if(mapVersion >= 4) + { + // Note: the thinker class byte has already been read. + /*int ver =*/ Reader_ReadByte(reader); // version byte. + + // Start of used data members. + th->polyobj = Reader_ReadInt32(reader); + th->intSpeed = Reader_ReadInt32(reader); + th->dist = Reader_ReadInt32(reader); + th->fangle = Reader_ReadInt32(reader); + th->speed[VX] = FIX2FLT(Reader_ReadInt32(reader)); + th->speed[VY] = FIX2FLT(Reader_ReadInt32(reader)); + } + else + { + // Its in the old pre V4 format which serialized polyevent_t + // Padding at the start (an old thinker_t struct) + byte junk[16]; // sizeof thinker_t + Reader_Read(reader, junk, 16); + + // Start of used data members. + th->polyobj = Reader_ReadInt32(reader); + th->intSpeed = Reader_ReadInt32(reader); + th->dist = Reader_ReadInt32(reader); + th->fangle = Reader_ReadInt32(reader); + th->speed[VX] = FIX2FLT(Reader_ReadInt32(reader)); + th->speed[VY] = FIX2FLT(Reader_ReadInt32(reader)); + } + + th->thinker.function = T_MovePoly; + + return true; // Add this thinker. +} + dd_bool EV_MovePoly(Line *line, byte *args, dd_bool timesEight, dd_bool override) { DENG_UNUSED(line); diff --git a/doomsday/plugins/common/src/saveinfo.cpp b/doomsday/plugins/common/src/saveinfo.cpp index e1dfbf6f6c..7dcd4e9fdd 100644 --- a/doomsday/plugins/common/src/saveinfo.cpp +++ b/doomsday/plugins/common/src/saveinfo.cpp @@ -23,99 +23,125 @@ #include "g_common.h" #include "p_tick.h" -#include "p_saveg.h" +#include "p_savedef.h" +#include "p_saveg.h" /// SV_RecognizeGameState, @todo remove me #include "p_saveio.h" -#include -#include +#include #include -#if __JDOOM__ || __JHERETIC__ -static void translateLegacyGameMode(gamemode_t *mode, int saveVersion) -{ - DENG_ASSERT(mode != 0); - - static gamemode_t const oldGameModes[] = { -# if __JDOOM__ - doom_shareware, - doom, - doom2, - doom_ultimate -# else // __JHERETIC__ - heretic_shareware, - heretic, - heretic_extended -# endif - }; - - // Is translation unnecessary? -#if __JDOOM__ - if(saveVersion >= 9) return; -#elif __JHERETIC__ - if(saveVersion >= 8) return; -#endif - - *mode = oldGameModes[(int)(*mode)]; +DENG2_PIMPL_NOREF(SaveInfo) +{ + de::String fileName; ///< Name of the game state file. -# if __JDOOM__ - /** - * @note Kludge: Older versions did not differentiate between versions - * of Doom2 (i.e., Plutonia and TNT are marked as Doom2). If we detect - * that this save is from some version of Doom2, replace the marked - * gamemode with the current gamemode. - */ - if((*mode) == doom2 && (gameModeBits & GM_ANY_DOOM2)) - { - (*mode) = gameMode; - } - /// kludge end. -# endif -} + // Metadata (the session header): + de::String userDescription; + uint sessionId; + int magic; + int version; + de::String gameIdentityKey; + Uri *mapUri; +#if !__JHEXEN__ + int mapTime; + Players players; #endif + GameRuleset gameRules; -SaveInfo::SaveInfo() - : _sessionId(0) - , _magic (0) - , _version (0) - , _gameMode (NUM_GAME_MODES) - , _mapUri (Uri_New()) + Instance() + : sessionId(0) + , magic (0) + , version (0) + , mapUri (Uri_New()) #if !__JHEXEN__ - , _mapTime (0) + , mapTime (0) #endif -{ - Str_InitStd(&_description); + { #if !__JHEXEN__ - de::zap(_players); + de::zap(players); #endif - de::zap(_gameRules); -} + de::zap(gameRules); + } -SaveInfo::SaveInfo(SaveInfo const &other) - : _sessionId(other._sessionId) - , _magic (other._magic) - , _version (other._version) - , _gameMode (other._gameMode) + Instance(Instance const &other) + : IPrivate() + , fileName (other.fileName) + , userDescription(other.userDescription) + , sessionId (other.sessionId) + , magic (other.magic) + , version (other.version) + , gameIdentityKey(other.gameIdentityKey) + , mapUri (Uri_Dup(other.mapUri)) #if !__JHEXEN__ - , _mapTime (other._mapTime) + , mapTime (other.mapTime) #endif -{ - Str_Copy(Str_InitStd(&_description), &other._description); - Uri_Copy(_mapUri, other._mapUri); + { #if !__JHEXEN__ - std::memcpy(&_players, &other._players, sizeof(_players)); + std::memcpy(&players, &other.players, sizeof(players)); #endif - std::memcpy(&_gameRules, &other._gameRules, sizeof(_gameRules)); -} + std::memcpy(&gameRules, &other.gameRules, sizeof(gameRules)); + } + + ~Instance() + { + Uri_Delete(mapUri); + } + +#if __JHEXEN__ + /** + * Deserialize the legacy Hexen-specific v.9 info. + */ + void read_Hx_v9(Reader *reader) + { + char descBuf[24]; + Reader_Read(reader, descBuf, 24); + userDescription = de::String(descBuf, 24); + + magic = MY_SAVE_MAGIC; // Lets pretend... + + char verText[16 + 1]; // "HXS Ver " + Reader_Read(reader, &verText, 16); descBuf[16] = 0; + version = atoi(&verText[8]); + + /// @note Kludge: Assume the current game mode. + GameInfo gameInfo; + DD_GameInfo(&gameInfo); + gameIdentityKey = Str_Text(gameInfo.identityKey); + /// Kludge end. + + /*Skip junk*/ SV_Seek(4); + + uint episode = 0; + uint map = Reader_ReadByte(reader) - 1; + Uri_Copy(mapUri, G_ComposeMapUri(episode, map)); + + gameRules.skill = (skillmode_t) (Reader_ReadByte(reader) & 0x7f); + // Interpret skill modes outside the normal range as "spawn no things". + if(gameRules.skill < SM_BABY || gameRules.skill >= NUM_SKILL_MODES) + { + gameRules.skill = SM_NOTHINGS; + } + + gameRules.deathmatch = Reader_ReadByte(reader); + gameRules.noMonsters = Reader_ReadByte(reader); + gameRules.randomClasses = Reader_ReadByte(reader); + + sessionId = 0; // None. + } +#endif +}; -SaveInfo::~SaveInfo() +SaveInfo::SaveInfo(de::String const &fileName) : d(new Instance) { - Str_Free(&_description); - Uri_Delete(_mapUri); + d->fileName = fileName; } -SaveInfo *SaveInfo::newWithCurrentSessionMetadata(Str const *description) // static +SaveInfo::SaveInfo(SaveInfo const &other) : d(new Instance(*other.d)) +{} + +SaveInfo *SaveInfo::newWithCurrentSessionMetadata(de::String const &fileName, + de::String const &userDescription) // static { - SaveInfo *info = new SaveInfo; - info->setDescription(description); + SaveInfo *info = new SaveInfo(fileName); + info->setUserDescription(userDescription); info->applyCurrentSessionMetadata(); info->setSessionId(G_GenerateSessionId()); return info; @@ -123,152 +149,274 @@ SaveInfo *SaveInfo::newWithCurrentSessionMetadata(Str const *description) // sta SaveInfo &SaveInfo::operator = (SaveInfo const &other) { - Str_Copy(&_description, &other._description); - _sessionId = other._sessionId; - _magic = other._magic; - _version = other._version; - _gameMode = other._gameMode; - Uri_Copy(_mapUri, other._mapUri); -#if !__JHEXEN__ - _mapTime = other._mapTime; - std::memcpy(&_players, &other._players, sizeof(_players)); -#endif - std::memcpy(&_gameRules, &other._gameRules, sizeof(_gameRules)); + d.reset(new Instance(*other.d)); return *this; } +SaveInfo::SessionStatus SaveInfo::status() const +{ + if(!haveGameSession()) return Unused; + if(!gameSessionIsLoadable()) return Incompatible; + return Loadable; +} + +de::String SaveInfo::fileName() const +{ + return d->fileName + de::String("." SAVEGAMEEXTENSION); +} + +void SaveInfo::setFileName(de::String newName) +{ + d->fileName = newName; +} + +de::String SaveInfo::fileNameForMap(uint map) const +{ + // Compose the full game-save filename. + return d->fileName + de::String("%1." SAVEGAMEEXTENSION) + .arg(int(map + 1), 2, 10, QChar('0')); +} + +de::String const &SaveInfo::gameIdentityKey() const +{ + return d->gameIdentityKey; +} + +void SaveInfo::setGameIdentityKey(de::String newGameIdentityKey) +{ + d->gameIdentityKey = newGameIdentityKey; +} + int SaveInfo::version() const { - return _version; + return d->version; } -int SaveInfo::magic() const +void SaveInfo::setVersion(int newVersion) { - return _magic; + d->version = newVersion; } -Str const *SaveInfo::description() const +de::String const &SaveInfo::userDescription() const { - return &_description; + return d->userDescription; } -void SaveInfo::setDescription(Str const *newName) +void SaveInfo::setUserDescription(de::String newUserDescription) { - Str_CopyOrClear(&_description, newName); + d->userDescription = newUserDescription; } uint SaveInfo::sessionId() const { - return _sessionId; + return d->sessionId; } void SaveInfo::setSessionId(uint newSessionId) { - _sessionId = newSessionId; + d->sessionId = newSessionId; } Uri const *SaveInfo::mapUri() const { - return _mapUri; + return d->mapUri; +} + +void SaveInfo::setMapUri(Uri const *newMapUri) +{ + DENG_ASSERT(newMapUri != 0); + Uri_Copy(d->mapUri, newMapUri); } #if !__JHEXEN__ int SaveInfo::mapTime() const { - return _mapTime; + return d->mapTime; +} + +void SaveInfo::setMapTime(int newMapTime) +{ + d->mapTime = newMapTime; } -#endif + +SaveInfo::Players const &SaveInfo::players() const +{ + return d->players; +} + +void SaveInfo::setPlayers(Players const &newPlayers) +{ + std::memcpy(d->players, newPlayers, sizeof(d->players)); +} + +#endif // !__JHEXEN__ GameRuleset const &SaveInfo::gameRules() const { - return _gameRules; + return d->gameRules; +} + +void SaveInfo::setGameRules(GameRuleset const &newRules) +{ + d->gameRules = newRules; // make a copy } void SaveInfo::applyCurrentSessionMetadata() { - _magic = IS_NETWORK_CLIENT? MY_CLIENT_SAVE_MAGIC : MY_SAVE_MAGIC; - _version = MY_SAVE_VERSION; - _gameMode = gameMode; - Uri_Copy(_mapUri, gameMapUri); + d->magic = IS_NETWORK_CLIENT? MY_CLIENT_SAVE_MAGIC : MY_SAVE_MAGIC; + d->version = MY_SAVE_VERSION; + GameInfo gameInfo; + DD_GameInfo(&gameInfo); + d->gameIdentityKey = Str_Text(gameInfo.identityKey); + Uri_Copy(d->mapUri, gameMapUri); #if !__JHEXEN__ - _mapTime = ::mapTime; + d->mapTime = ::mapTime; #endif // Make a copy of the current game rules. - _gameRules = ::gameRules; + d->gameRules = ::gameRules; #if !__JHEXEN__ for(int i = 0; i < MAXPLAYERS; i++) { - _players[i] = players[i].plr->inGame; + d->players[i] = (::players[i]).plr->inGame; } #endif } -bool SaveInfo::isLoadable() +bool SaveInfo::haveGameSession() const { - // Game Mode missmatch? - if(_gameMode != gameMode) return false; + return SV_ExistingFile(SV_SavePath() / fileName()); +} + +bool SaveInfo::gameSessionIsLoadable() const +{ + if(!haveGameSession()) return false; + + // Game identity key missmatch? + GameInfo gameInfo; + DD_GameInfo(&gameInfo); + if(d->gameIdentityKey.compareWithoutCase(Str_Text(gameInfo.identityKey))) + { + return false; + } /// @todo Validate loaded add-ons and checksum the definition database. return true; // It's good! } +void SaveInfo::updateFromFile() +{ + if(SV_SavePath().isEmpty()) + { + // The save path cannot be accessed for some reason. Perhaps its a network path? + setUserDescription(""); + setSessionId(0); + return; + } + + // Is this a recognized game state? + if(!SV_RecognizeGameState(*this)) + { + // Clear the info. + setUserDescription(""); + setSessionId(0); + return; + } + + // Ensure we have a valid description. + if(d->userDescription.isEmpty()) + { + setUserDescription("UNNAMED"); + } +} + void SaveInfo::write(Writer *writer) const { - Writer_WriteInt32(writer, _magic); - Writer_WriteInt32(writer, _version); - Writer_WriteInt32(writer, _gameMode); - Str_Write(&_description, writer); + Writer_WriteInt32(writer, d->magic); + Writer_WriteInt32(writer, d->version); + + AutoStr *gameIdentityKeyStr = AutoStr_FromTextStd(d->gameIdentityKey.toUtf8().constData()); + Str_Write(gameIdentityKeyStr, writer); - Uri_Write(_mapUri, writer); + AutoStr *descriptionStr = AutoStr_FromTextStd(d->userDescription.toUtf8().constData()); + Str_Write(descriptionStr, writer); + + Uri_Write(d->mapUri, writer); #if !__JHEXEN__ - Writer_WriteInt32(writer, _mapTime); + Writer_WriteInt32(writer, d->mapTime); #endif - GameRuleset_Write(&_gameRules, writer); + GameRuleset_Write(&d->gameRules, writer); #if !__JHEXEN__ for(int i = 0; i < MAXPLAYERS; ++i) { - Writer_WriteByte(writer, _players[i]); + Writer_WriteByte(writer, d->players[i]); } #endif - Writer_WriteInt32(writer, _sessionId); + Writer_WriteInt32(writer, d->sessionId); } void SaveInfo::read(Reader *reader) { - _magic = Reader_ReadInt32(reader); - _version = Reader_ReadInt32(reader); - _gameMode = (gamemode_t)Reader_ReadInt32(reader); +#if __JHEXEN__ + // Read the magic byte to determine the high-level format. + int magic = Reader_ReadInt32(reader); + SV_HxSavePtr()->b -= 4; // Rewind the stream. - if(_version >= 10) + if((!IS_NETWORK_CLIENT && magic != MY_SAVE_MAGIC) || + ( IS_NETWORK_CLIENT && magic != MY_CLIENT_SAVE_MAGIC)) { - Str_Read(&_description, reader); + // Perhaps the old v9 format? + d->read_Hx_v9(reader); + return; + } +#endif + + d->magic = Reader_ReadInt32(reader); + d->version = Reader_ReadInt32(reader); + if(d->version >= 14) + { + AutoStr *tmp = AutoStr_NewStd(); + Str_Read(tmp, reader); + d->gameIdentityKey = Str_Text(tmp); + } + else + { + // Translate gamemode identifiers from older save versions. + int oldGamemode = Reader_ReadInt32(reader); + d->gameIdentityKey = Str_Text(G_IdentityKeyForLegacyGamemode(oldGamemode, d->version)); + } + + if(d->version >= 10) + { + AutoStr *tmp = AutoStr_NewStd(); + Str_Read(tmp, reader); + d->userDescription = Str_Text(tmp); } else { // Description is a fixed 24 characters in length. - char buf[24 + 1]; - Reader_Read(reader, buf, 24); buf[24] = 0; - Str_Set(&_description, buf); + char descBuf[24]; + Reader_Read(reader, descBuf, 24); + d->userDescription = de::String(descBuf, 24); } - if(_version >= 14) + if(d->version >= 14) { - Uri_Read(_mapUri, reader); + Uri_Read(d->mapUri, reader); #if !__JHEXEN__ - _mapTime = Reader_ReadInt32(reader); + d->mapTime = Reader_ReadInt32(reader); #endif - GameRuleset_Read(&_gameRules, reader); + GameRuleset_Read(&d->gameRules, reader); } else { #if !__JHEXEN__ - if(_version < 13) + if(d->version < 13) { // In DOOM the high bit of the skill mode byte is also used for the // "fast" game rule dd_bool. There is more confusion in that SM_NOTHINGS @@ -278,173 +426,106 @@ void SaveInfo::read(Reader *reader) // by default this means "spawn no things" and if so then the "fast" game // rule is meaningless so it is forced off. byte skillModePlusFastBit = Reader_ReadByte(reader); - _gameRules.skill = (skillmode_t) (skillModePlusFastBit & 0x7f); - if(_gameRules.skill < SM_BABY || _gameRules.skill >= NUM_SKILL_MODES) + d->gameRules.skill = (skillmode_t) (skillModePlusFastBit & 0x7f); + if(d->gameRules.skill < SM_BABY || d->gameRules.skill >= NUM_SKILL_MODES) { - _gameRules.skill = SM_NOTHINGS; - _gameRules.fast = 0; + d->gameRules.skill = SM_NOTHINGS; + d->gameRules.fast = 0; } else { - _gameRules.fast = (skillModePlusFastBit & 0x80) != 0; + d->gameRules.fast = (skillModePlusFastBit & 0x80) != 0; } } else #endif { - _gameRules.skill = skillmode_t( Reader_ReadByte(reader) & 0x7f ); - + d->gameRules.skill = skillmode_t( Reader_ReadByte(reader) & 0x7f ); // Interpret skill levels outside the normal range as "spawn no things". - if(_gameRules.skill < SM_BABY || _gameRules.skill >= NUM_SKILL_MODES) - _gameRules.skill = SM_NOTHINGS; + if(d->gameRules.skill < SM_BABY || d->gameRules.skill >= NUM_SKILL_MODES) + { + d->gameRules.skill = SM_NOTHINGS; + } } uint episode = Reader_ReadByte(reader) - 1; uint map = Reader_ReadByte(reader) - 1; - Uri_Copy(_mapUri, G_ComposeMapUri(episode, map)); + Uri_Copy(d->mapUri, G_ComposeMapUri(episode, map)); - _gameRules.deathmatch = Reader_ReadByte(reader); + d->gameRules.deathmatch = Reader_ReadByte(reader); #if !__JHEXEN__ - if(_version >= 13) - _gameRules.fast = Reader_ReadByte(reader); + if(d->version >= 13) + { + d->gameRules.fast = Reader_ReadByte(reader); + } #endif - _gameRules.noMonsters = Reader_ReadByte(reader); + d->gameRules.noMonsters = Reader_ReadByte(reader); #if __JHEXEN__ - _gameRules.randomClasses = Reader_ReadByte(reader); + d->gameRules.randomClasses = Reader_ReadByte(reader); #endif #if !__JHEXEN__ - _gameRules.respawnMonsters = Reader_ReadByte(reader); + d->gameRules.respawnMonsters = Reader_ReadByte(reader); #endif #if !__JHEXEN__ - // Older formats serialize the unpacked saveheader_t struct; skip the junk values (alignment). - if(_version < 10) SV_Seek(2); + /*skip old junk*/ if(d->version < 10) SV_Seek(2); - _mapTime = Reader_ReadInt32(reader); + d->mapTime = Reader_ReadInt32(reader); #endif } #if !__JHEXEN__ for(int i = 0; i < MAXPLAYERS; ++i) { - _players[i] = Reader_ReadByte(reader); - } -#endif - - _sessionId = Reader_ReadInt32(reader); - -#if __JDOOM__ || __JHERETIC__ - // Translate gameMode identifiers from older save versions. - translateLegacyGameMode(&_gameMode, _version); -#endif -} - -#if __JHEXEN__ -void SaveInfo::read_Hx_v9(Reader *reader) -{ - char descBuf[24 + 1]; - Reader_Read(reader, descBuf, 24); descBuf[24] = 0; - Str_Set(&_description, descBuf); - - _magic = MY_SAVE_MAGIC; // Lets pretend... - - char verText[16 + 1]; // "HXS Ver " - Reader_Read(reader, &verText, 16); descBuf[16] = 0; - _version = atoi(&verText[8]); - - _gameMode = gameMode; // Assume the current mode. - - /*Skip junk*/ SV_Seek(4); - - uint episode = 0; - uint map = Reader_ReadByte(reader) - 1; - Uri_Copy(_mapUri, G_ComposeMapUri(episode, map)); - - _gameRules.skill = (skillmode_t) (Reader_ReadByte(reader) & 0x7f); - // Interpret skill modes outside the normal range as "spawn no things". - if(_gameRules.skill < SM_BABY || _gameRules.skill >= NUM_SKILL_MODES) - { - _gameRules.skill = SM_NOTHINGS; + d->players[i] = Reader_ReadByte(reader); } - - _gameRules.deathmatch = Reader_ReadByte(reader); - _gameRules.noMonsters = Reader_ReadByte(reader); - _gameRules.randomClasses = Reader_ReadByte(reader); - - _sessionId = 0; // None. -} #endif -// C wrapper API --------------------------------------------------------------- - -SaveInfo *SaveInfo_New() -{ - return new SaveInfo; -} - -SaveInfo *SaveInfo_Dup(SaveInfo const *other) -{ - DENG_ASSERT(other != 0); - return new SaveInfo(*other); -} - -void SaveInfo_Delete(SaveInfo *info) -{ - if(info) delete info; -} - -SaveInfo *SaveInfo_Copy(SaveInfo *info, SaveInfo const *other) -{ - DENG_ASSERT(info != 0 && other != 0); - *info = *other; - return info; -} - -uint SaveInfo_SessionId(SaveInfo const *info) -{ - DENG_ASSERT(info != 0); - return info->sessionId(); + d->sessionId = Reader_ReadInt32(reader); } -void SaveInfo_SetSessionId(SaveInfo *info, uint newSessionId) +de::String SaveInfo::statusAsText() const { - DENG_ASSERT(info != 0); - info->setSessionId(newSessionId); + static de::String const statusTexts[] = { + "Loadable", + "Incompatible", + "Unused", + }; + return statusTexts[int(status())]; } -Str const *SaveInfo_Description(SaveInfo const *info) +de::String SaveInfo::description() const { - DENG_ASSERT(info != 0); - return info->description(); -} + AutoStr *currentMapUriAsText = Uri_ToString(mapUri()); -void SaveInfo_SetDescription(SaveInfo *info, Str const *newName) -{ - DENG_ASSERT(info != 0); - info->setDescription(newName); + return de::String(_E(b) "%1\n" _E(.) + _E(l) "IdentityKey: " _E(.)_E(i) "%2 " _E(.) + _E(l) "Current map: " _E(.)_E(i) "%3\n" _E(.) + _E(l) "Source file: " _E(.)_E(i) "%4\n" _E(.) + _E(l) "Version: " _E(.)_E(i) "%5 " _E(.) + _E(l) "Session id: " _E(.)_E(i) "%6\n" _E(.) + _E(D) "Status: " _E(.) "%7") + .arg(userDescription()) + .arg(gameIdentityKey()) + .arg(Str_Text(currentMapUriAsText)) + .arg(de::NativePath(SV_SavePath() / fileName()).pretty()) + .arg(version()) + .arg(sessionId()) + .arg(statusAsText()); } -dd_bool SaveInfo_IsLoadable(SaveInfo *info) +int SaveInfo::magic() const { - DENG_ASSERT(info != 0); - return info->isLoadable(); + return d->magic; } -void SaveInfo_Write(SaveInfo *info, Writer *writer) +void SaveInfo::setMagic(int newMagic) { - DENG_ASSERT(info != 0); - info->write(writer); + d->magic = newMagic; } -void SaveInfo_Read(SaveInfo *info, Reader *reader) +SaveInfo *SaveInfo::fromReader(Reader *reader) // static { - DENG_ASSERT(info != 0); + SaveInfo *info = new SaveInfo; info->read(reader); + return info; } - -#if __JHEXEN__ -void SaveInfo_Read_Hx_v9(SaveInfo *info, Reader *reader) -{ - DENG_ASSERT(info != 0); - info->read_Hx_v9(reader); -} -#endif diff --git a/doomsday/plugins/common/src/saveslots.cpp b/doomsday/plugins/common/src/saveslots.cpp index 6482a74c0e..828b2f0280 100644 --- a/doomsday/plugins/common/src/saveslots.cpp +++ b/doomsday/plugins/common/src/saveslots.cpp @@ -21,14 +21,10 @@ #include "common.h" #include "saveslots.h" -#include "gamestatereader.h" -#if __JDOOM__ -# include "p_oldsvg.h" -#endif -#if __JHERETIC__ -# include "p_oldsvg.h" -#endif +#include "p_savedef.h" #include "p_saveio.h" +#include "saveinfo.h" +#include #include #include @@ -37,216 +33,210 @@ static int cvarLastSlot = -1; ///< @c -1= Not yet loaded/saved in this game session. static int cvarQuickSlot = -1; ///< @c -1= Not yet chosen/determined. +DENG2_PIMPL_NOREF(SaveSlots::Slot) +{ + de::String saveName; + SaveInfo *info; + + Instance() : info(0) {} + ~Instance() { delete info; } +}; + +SaveSlots::Slot::Slot(de::String const &saveName) : d(new Instance) +{ + d->saveName = saveName; +} + +de::String SaveSlots::Slot::fileName() const +{ + return d->saveName; +} + +void SaveSlots::Slot::bindFileName(de::String newSaveName) +{ + if(d->saveName.compareWithoutCase(newSaveName)) + { + clearSaveInfo(); + } + d->saveName = newSaveName; +} + +bool SaveSlots::Slot::isUsed() const +{ + if(SV_SavePath().isEmpty()) return false; + if(!hasSaveInfo()) return false; + return saveInfo().gameSessionIsLoadable(); +} + +bool SaveSlots::Slot::hasSaveInfo() const +{ + return d->info != 0; +} + +void SaveSlots::Slot::clearSaveInfo() +{ + delete d->info; d->info = 0; +} + +void SaveSlots::Slot::replaceSaveInfo(SaveInfo *newInfo) +{ + clearSaveInfo(); + d->info = newInfo; +} + +SaveInfo &SaveSlots::Slot::saveInfo() const +{ + if(d->info) + { + return *d->info; + } + /// @throw MissingInfoError Attempted with no SaveInfo present. + throw MissingInfoError("SaveSlots::Slot::saveInfo", "No SaveInfo exists"); +} + +/// @todo We should look at all files on the save path and not just those which match +/// the default game-save file naming convention. DENG2_PIMPL(SaveSlots) { int slotCount; - typedef std::vector Infos; - Infos infos; - SaveInfo *autoInfo; + typedef std::vector Slots; + Slots sslots; + Slot autoSlot; #if __JHEXEN__ - SaveInfo *baseInfo; + Slot baseSlot; #endif Instance(Public *i, int slotCount) : Base(i) , slotCount(de::max(1, slotCount)) - , autoInfo(0) + , autoSlot(de::String(SAVEGAMENAME "%1").arg(AUTO_SLOT)) #if __JHEXEN__ - , baseInfo(0) + , baseSlot(de::String(SAVEGAMENAME "%1").arg(BASE_SLOT)) #endif - {} + { + for(int i = 0; i < slotCount; ++i) + { + sslots.push_back(new Slot(de::String(SAVEGAMENAME "%1").arg(i))); + } + } ~Instance() { - clearInfos(); + DENG2_FOR_EACH(Slots, i, sslots) { delete *i; } } - /// Determines whether to announce when the specified @a slot is cleared. - bool shouldAnnounceWhenClearing(int slot) + /// Determines whether to announce when the specified @a slotNumber is cleared. + bool shouldAnnounceWhenClearing(int slotNumber) { #ifdef DENG_DEBUG return true; // Always. #endif #if __JHEXEN__ - return (slot != AUTO_SLOT && slot != BASE_SLOT); + return (slotNumber != AUTO_SLOT && slotNumber != BASE_SLOT); #else - return (slot != AUTO_SLOT); -#endif - } - - void clearInfos() - { - DENG2_FOR_EACH(Infos, i, infos) { delete *i; } - infos.clear(); - - delete autoInfo; autoInfo = 0; -#if __JHEXEN__ - delete baseInfo; baseInfo = 0; + return (slotNumber != AUTO_SLOT); #endif } - SaveInfo **infoAdrForSlot(int slot) - { - buildInfosIfNeeded(); - - if(slot == AUTO_SLOT) return &autoInfo; -#if __JHEXEN__ - if(slot == BASE_SLOT) return &baseInfo; -#endif - return &infos[slot]; - } - - bool recognizeGameState(SaveInfo *info, Str const *path) - { - if(GameStateReader::recognize(info, path)) - return true; - - // Perhaps an original game state? -#if __JDOOM__ - if(DoomV9GameStateReader::recognize(info, path)) - return true; -#endif -#if __JHERETIC__ - if(HereticV13GameStateReader::recognize(info, path)) - return true; -#endif - return false; - } - - void updateInfo(Str const *path, SaveInfo &info) - { - if(!path || Str_IsEmpty(path)) - { - // The save path cannot be accessed for some reason. Perhaps its a network path? - // Clear the info for this slot. - info.setDescription(0); - info.setSessionId(0); - return; - } - - // Is this a recognized game state? - if(!recognizeGameState(&info, path)) - { - // Clear the info for this slot. - info.setDescription(0); - info.setSessionId(0); - return; - } - - // Ensure we have a valid description. - if(Str_IsEmpty(info.description())) - { - info.setDescription(AutoStr_FromText("UNNAMED")); - } - } - /// Re-build save info by re-scanning the save paths and populating the list. - void buildInfos() + void buildInfosIfNeeded(bool update = false) { - if(infos.empty()) + DENG2_FOR_EACH(Slots, i, sslots) { - // Not yet been here. We need to allocate and initialize the game-save info list. - for(int i = 0; i < slotCount; ++i) + SaveSlot &sslot = **i; + if(!sslot.hasSaveInfo()) { - infos.push_back(new SaveInfo); + sslot.replaceSaveInfo(new SaveInfo(sslot.fileName())); } - autoInfo = new SaveInfo; -#if __JHEXEN__ - baseInfo = new SaveInfo; -#endif + if(update) sslot.saveInfo().updateFromFile(); } - - // Scan the save paths and populate the list. - /// @todo We should look at all files on the save path and not just those which match - /// the default game-save file naming convention. - for(int i = 0; i < (signed)infos.size(); ++i) + if(!autoSlot.hasSaveInfo()) { - updateInfo(self.composeSavePathForSlot(i), *infos[i]); + autoSlot.replaceSaveInfo(new SaveInfo(autoSlot.fileName())); } - updateInfo(self.composeSavePathForSlot(AUTO_SLOT), *autoInfo); + if(update) autoSlot.saveInfo().updateFromFile(); #if __JHEXEN__ - updateInfo(self.composeSavePathForSlot(BASE_SLOT), *baseInfo); -#endif - } - - void buildInfosIfNeeded() - { - if(infos.empty()) + if(!baseSlot.hasSaveInfo()) { - buildInfos(); + baseSlot.replaceSaveInfo(new SaveInfo(baseSlot.fileName())); } + if(update) baseSlot.saveInfo().updateFromFile(); +#endif } }; SaveSlots::SaveSlots(int slotCount) : d(new Instance(this, slotCount)) {} -void SaveSlots::clearAllSaveInfo() +void SaveSlots::clearAll() { - d->clearInfos(); + DENG2_FOR_EACH(Instance::Slots, i, d->sslots) + { + (*i)->clearSaveInfo(); + } + d->autoSlot.clearSaveInfo(); +#if __JHEXEN__ + d->baseSlot.clearSaveInfo(); +#endif // Reset last-used and quick-save slot tracking. Con_SetInteger2("game-save-last-slot", -1, SVF_WRITE_OVERRIDE); Con_SetInteger("game-save-quick-slot", -1); } -void SaveSlots::updateAllSaveInfo() +void SaveSlots::updateAll() { - d->buildInfos(); + d->buildInfosIfNeeded(true/*update session headers from source files*/); } -AutoStr *SaveSlots::composeSlotIdentifier(int slot) const +de::String SaveSlots::slotIdentifier(int slot) const { - AutoStr *str = AutoStr_NewStd(); - if(slot < 0) return Str_Set(str, "(invalid slot)"); - if(slot == AUTO_SLOT) return Str_Set(str, ""); + if(slot < 0) return "(invalid slot)"; + if(slot == AUTO_SLOT) return ""; #if __JHEXEN__ - if(slot == BASE_SLOT) return Str_Set(str, ""); + if(slot == BASE_SLOT) return ""; #endif - return Str_Appendf(str, "%i", slot); + return de::String::number(slot); } -int SaveSlots::parseSlotIdentifier(char const *str) const +int SaveSlots::parseSlotIdentifier(de::String str) const { // Try game-save name match. - int slot = findSlotWithSaveDescription(str); + int slot = findSlotWithUserSaveDescription(str); if(slot >= 0) return slot; // Try keyword identifiers. - if(!stricmp(str, "last") || !stricmp(str, "")) + if(!str.compareWithoutCase("last") || !str.compareWithoutCase("")) { return Con_GetInteger("game-save-last-slot"); } - if(!stricmp(str, "quick") || !stricmp(str, "")) + if(!str.compareWithoutCase("quick") || !str.compareWithoutCase("")) { return Con_GetInteger("game-save-quick-slot"); } - if(!stricmp(str, "auto") || !stricmp(str, "")) + if(!str.compareWithoutCase("auto") || !str.compareWithoutCase("")) { return AUTO_SLOT; } // Try logical slot identifier. - if(M_IsStringValidInt(str)) - { - return atoi(str); - } + bool ok; + slot = str.toInt(&ok); + if(ok) return slot; // Unknown/not found. return -1; } -int SaveSlots::findSlotWithSaveDescription(char const *description) const +int SaveSlots::findSlotWithUserSaveDescription(de::String description) const { - if(description && description[0]) + if(!description.isEmpty()) { - // On first call - automatically build and populate game-save info. - d->buildInfosIfNeeded(); - - for(int i = 0; i < (signed)d->infos.size(); ++i) + for(int i = 0; i < (signed)d->sslots.size(); ++i) { - SaveInfo *info = d->infos[i]; - if(!Str_CompareIgnoreCase(info->description(), description)) + SaveSlot &sslot = *d->sslots[i]; + if(sslot.hasSaveInfo() && + !sslot.saveInfo().userDescription().compareWithoutCase(description)) { return i; } @@ -255,21 +245,12 @@ int SaveSlots::findSlotWithSaveDescription(char const *description) const return -1; // Not found. } -bool SaveSlots::slotInUse(int slot) const -{ - if(SV_ExistingFile(composeSavePathForSlot(slot))) - { - return saveInfo(slot).isLoadable(); - } - return false; -} - int SaveSlots::slotCount() const { return d->slotCount; } -bool SaveSlots::isValidSlot(int slot) const +bool SaveSlots::isKnownSlot(int slot) const { if(slot == AUTO_SLOT) return true; #if __JHEXEN__ @@ -284,228 +265,90 @@ bool SaveSlots::slotIsUserWritable(int slot) const #if __JHEXEN__ if(slot == BASE_SLOT) return false; #endif - return isValidSlot(slot); + return isKnownSlot(slot); } -SaveInfo &SaveSlots::saveInfo(int slot) const +SaveSlots::Slot &SaveSlots::slot(int slotNumber) const { - if(!isValidSlot(slot)) + if(!isKnownSlot(slotNumber)) { /// @throw InvalidSlotError An invalid slot was specified. - throw InvalidSlotError("SaveSlots::saveInfo", "Invalid slot " + de::String::number(slot)); + throw InvalidSlotError("SaveSlots::slot", "Invalid slot #" + de::String::number(slotNumber)); } - SaveInfo **info = d->infoAdrForSlot(slot); - DENG_ASSERT(*info != 0); - return **info; + + d->buildInfosIfNeeded(); + + if(slotNumber == AUTO_SLOT) return d->autoSlot; +#if __JHEXEN__ + if(slotNumber == BASE_SLOT) return d->baseSlot; +#endif + DENG2_ASSERT(slotNumber >= 0 && slotNumber < int( d->sslots.size() )); + return *d->sslots[slotNumber]; } -void SaveSlots::replaceSaveInfo(int slot, SaveInfo *newInfo) +void SaveSlots::clearSlot(int slotNumber) { - if(!isValidSlot(slot)) + if(SV_SavePath().isEmpty()) { - /// @throw InvalidSlotError An invalid slot was specified. - throw InvalidSlotError("SaveSlots::replaceSaveInfo", "Invalid slot " + de::String::number(slot)); + return; } - SaveInfo **infoAdr = d->infoAdrForSlot(slot); - if(*infoAdr) delete (*infoAdr); - *infoAdr = newInfo; -} -void SaveSlots::clearSlot(int slot) -{ - if(!isValidSlot(slot)) + Slot &sslot = slot(slotNumber); + if(!sslot.hasSaveInfo()) { - /// @throw InvalidSlotError An invalid slot was specified. - throw InvalidSlotError("SaveSlots::clearSlot", "Invalid slot " + de::String::number(slot)); + sslot.replaceSaveInfo(new SaveInfo(sslot.fileName())); } - if(d->shouldAnnounceWhenClearing(slot)) + SaveInfo &saveInfo = sslot.saveInfo(); + + if(d->shouldAnnounceWhenClearing(slotNumber)) { - AutoStr *ident = composeSlotIdentifier(slot); - App_Log(DE2_RES_MSG, "Clearing save slot %s", Str_Text(ident)); + App_Log(DE2_RES_MSG, "Clearing save slot %s", slotIdentifier(slotNumber).toLatin1().constData()); } for(int i = 0; i < MAX_HUB_MAPS; ++i) { - AutoStr *path = composeSavePathForSlot(slot, i); - SV_RemoveFile(path); + SV_RemoveFile(SV_SavePath() / saveInfo.fileNameForMap(i)); } - AutoStr *path = composeSavePathForSlot(slot); - SV_RemoveFile(path); + SV_RemoveFile(SV_SavePath() / saveInfo.fileName()); - d->updateInfo(path, saveInfo(slot)); + saveInfo.setUserDescription(""); + saveInfo.setSessionId(0); } -void SaveSlots::copySlot(int sourceSlot, int destSlot) +void SaveSlots::copySlot(int sourceSlotNumber, int destSlotNumber) { - if(!isValidSlot(sourceSlot)) - { - /// @throw InvalidSlotError An invalid slot was specified. - throw InvalidSlotError("SaveSlots::copySlot", "Invalid source slot " + de::String::number(sourceSlot)); - } + LOG_AS("SaveSlots::copySlot"); - if(!isValidSlot(destSlot)) + if(SV_SavePath().isEmpty()) { - /// @throw InvalidSlotError An invalid slot was specified. - throw InvalidSlotError("SaveSlots::saveInfo", "Invalid dest slot " + de::String::number(destSlot)); + return; } + Slot &sourceSlot = slot(sourceSlotNumber); + Slot &destSlot = slot(destSlotNumber); + // Clear all save files at destination slot. - clearSlot(destSlot); + clearSlot(destSlotNumber); - AutoStr *src, *dst; for(int i = 0; i < MAX_HUB_MAPS; ++i) { - src = composeSavePathForSlot(sourceSlot, i); - dst = composeSavePathForSlot(destSlot, i); - SV_CopyFile(src, dst); + SV_CopyFile(SV_SavePath() / sourceSlot.saveInfo().fileNameForMap(i), + SV_SavePath() / destSlot.saveInfo().fileNameForMap(i)); } - src = composeSavePathForSlot(sourceSlot); - dst = composeSavePathForSlot(destSlot); - SV_CopyFile(src, dst); + SV_CopyFile(SV_SavePath() / sourceSlot.saveInfo().fileName(), + SV_SavePath() / destSlot.saveInfo().fileName()); // Copy save info too. - replaceSaveInfo(destSlot, new SaveInfo(saveInfo(sourceSlot))); -} - -AutoStr *SaveSlots::composeSavePathForSlot(int slot, int map) const -{ - AutoStr *path = AutoStr_NewStd(); - - // A valid slot? - if(!isValidSlot(slot)) return path; - - // Do we have a valid path? - /// @todo Do not do alter the file system until necessary. - if(!F_MakePath(SV_SavePath())) return path; - - // Compose the full game-save path and filename. - if(map >= 0) - { - Str_Appendf(path, "%s" SAVEGAMENAME "%i%02i." SAVEGAMEEXTENSION, SV_SavePath(), slot, map); - } - else - { - Str_Appendf(path, "%s" SAVEGAMENAME "%i." SAVEGAMEEXTENSION, SV_SavePath(), slot); - } - F_TranslatePath(path, path); - return path; + destSlot.replaceSaveInfo(new SaveInfo(sourceSlot.saveInfo())); + // Update the file path associated with the copied save info. + destSlot.saveInfo().setFileName(destSlot.fileName()); } void SaveSlots::consoleRegister() // static { -#if !__JHEXEN__ - C_VAR_BYTE("game-save-auto-loadonreborn", &cfg.loadAutoSaveOnReborn, 0, 0, 1); -#endif - C_VAR_BYTE("game-save-confirm", &cfg.confirmQuickGameSave, 0, 0, 1); - C_VAR_BYTE("game-save-confirm-loadonreborn", &cfg.confirmRebornLoad, 0, 0, 1); - C_VAR_BYTE("game-save-last-loadonreborn", &cfg.loadLastSaveOnReborn, 0, 0, 1); - C_VAR_INT ("game-save-last-slot", &cvarLastSlot, CVF_NO_MIN|CVF_NO_MAX|CVF_NO_ARCHIVE|CVF_READ_ONLY, 0, 0); - C_VAR_INT ("game-save-quick-slot", &cvarQuickSlot, CVF_NO_MAX|CVF_NO_ARCHIVE, -1, 0); - - // Aliases for obsolete cvars: - C_VAR_BYTE("menu-quick-ask", &cfg.confirmQuickGameSave, 0, 0, 1); -} - -// C wrapper API --------------------------------------------------------------- - -SaveSlots *SaveSlots_New(int slotCount) -{ - return new SaveSlots(slotCount); -} - -void SaveSlots_Delete(SaveSlots *sslots) -{ - delete sslots; -} - -void SaveSlots_ClearAllSaveInfo(SaveSlots *sslots) -{ - DENG_ASSERT(sslots != 0); - sslots->clearAllSaveInfo(); -} - -void SaveSlots_UpdateAllSaveInfo(SaveSlots *sslots) -{ - DENG_ASSERT(sslots != 0); - sslots->updateAllSaveInfo(); -} - -int SaveSlots_SlotCount(SaveSlots const *sslots) -{ - DENG_ASSERT(sslots != 0); - return sslots->slotCount(); -} - -dd_bool SaveSlots_IsValidSlot(SaveSlots const *sslots, int slot) -{ - DENG_ASSERT(sslots != 0); - return sslots->isValidSlot(slot); -} - -AutoStr *SaveSlots_ComposeSlotIdentifier(SaveSlots const *sslots, int slot) -{ - DENG_ASSERT(sslots != 0); - return sslots->composeSlotIdentifier(slot); -} - -int SaveSlots_ParseSlotIdentifier(SaveSlots const *sslots, char const *str) -{ - DENG_ASSERT(sslots != 0); - return sslots->parseSlotIdentifier(str); -} - -int SaveSlots_FindSlotWithSaveDescription(SaveSlots const *sslots, char const *description) -{ - DENG_ASSERT(sslots != 0); - return sslots->findSlotWithSaveDescription(description); -} - -dd_bool SaveSlots_SlotInUse(SaveSlots const *sslots, int slot) -{ - DENG_ASSERT(sslots != 0); - return sslots->slotInUse(slot); -} - -dd_bool SaveSlots_SlotIsUserWritable(SaveSlots const *sslots, int slot) -{ - DENG_ASSERT(sslots != 0); - return sslots->slotIsUserWritable(slot); -} - -SaveInfo *SaveSlots_SaveInfo(SaveSlots *sslots, int slot) -{ - DENG_ASSERT(sslots != 0); - return sslots->saveInfoPtr(slot); -} - -void SaveSlots_ReplaceSaveInfo(SaveSlots *sslots, int slot, SaveInfo *newInfo) -{ - DENG_ASSERT(sslots != 0); - sslots->replaceSaveInfo(slot, newInfo); -} - -void SaveSlots_ClearSlot(SaveSlots *sslots, int slot) -{ - DENG_ASSERT(sslots != 0); - sslots->clearSlot(slot); -} - -void SaveSlots_CopySlot(SaveSlots *sslots, int sourceSlot, int destSlot) -{ - DENG_ASSERT(sslots != 0); - sslots->copySlot(sourceSlot, destSlot); -} - -AutoStr *SaveSlots_ComposeSavePathForSlot(SaveSlots const *sslots, int slot, int map) -{ - DENG_ASSERT(sslots != 0); - return sslots->composeSavePathForSlot(slot, map); -} - -void SaveSlots_ConsoleRegister() -{ - SaveSlots::consoleRegister(); + C_VAR_INT("game-save-last-slot", &cvarLastSlot, CVF_NO_MIN|CVF_NO_MAX|CVF_NO_ARCHIVE|CVF_READ_ONLY, 0, 0); + C_VAR_INT("game-save-quick-slot", &cvarQuickSlot, CVF_NO_MAX|CVF_NO_ARCHIVE, -1, 0); } diff --git a/doomsday/plugins/common/src/thingarchive.cpp b/doomsday/plugins/common/src/thingarchive.cpp new file mode 100644 index 0000000000..7dcd30d459 --- /dev/null +++ b/doomsday/plugins/common/src/thingarchive.cpp @@ -0,0 +1,235 @@ +/** @file thingarchive.cpp Map save state thing archive. + * + * @authors Copyright © 2003-2013 Jaakko Keränen + * @authors Copyright © 2005-2013 Daniel Swanson + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "common.h" +#include "thingarchive.h" + +#include "mobj.h" +#include "p_saveg.h" /// @todo remove me +#include + +#if __JHEXEN__ +/// Symbolic identifier used to mark references to players. +static ThingArchive::SerialId const TargetPlayerId = -2; +#endif + +DENG2_PIMPL(ThingArchive) +{ + int version; + uint size; + mobj_t **things; + bool excludePlayers; + + Instance(Public *i) + : Base(i) + , version(0) + , size(0) + , things(0) + , excludePlayers(false) + {} + + ~Instance() + { + self.clear(); + } + + struct countmobjthinkerstoarchive_params_t + { + uint count; + bool excludePlayers; + }; + + static int countMobjThinkersToArchive(thinker_t *th, void *context) + { + countmobjthinkerstoarchive_params_t &p = *(countmobjthinkerstoarchive_params_t *) context; + if(!(Mobj_IsPlayer((mobj_t *) th) && p.excludePlayers)) + { + p.count++; + } + return false; // Continue iteration. + } +}; + +ThingArchive::ThingArchive(int version) : d(new Instance(this)) +{ + d->version = version; +} + +int ThingArchive::version() const +{ + return d->version; +} + +bool ThingArchive::excludePlayers() const +{ + return d->excludePlayers; +} + +uint ThingArchive::size() const +{ + return d->size; +} + +void ThingArchive::clear() +{ + M_Free(d->things); d->things = 0; + d->size = 0; +} + +void ThingArchive::initForLoad(uint size) +{ + d->size = size; + d->things = (mobj_t **)M_Calloc(d->size * sizeof(*d->things)); +} + +void ThingArchive::initForSave(bool excludePlayers) +{ + // Count the number of things we'll be writing. + Instance::countmobjthinkerstoarchive_params_t parm; de::zap(parm); + parm.count = 0; + parm.excludePlayers = excludePlayers; + Thinker_Iterate((thinkfunc_t) P_MobjThinker, Instance::countMobjThinkersToArchive, &parm); + + d->size = parm.count; + d->things = (mobj_t **)M_Calloc(d->size * sizeof(*d->things)); + d->excludePlayers = excludePlayers; +} + +void ThingArchive::insert(mobj_t const *mo, SerialId serialId) +{ + DENG_ASSERT(mo != 0); + +#if __JHEXEN__ + if(d->version >= 1) +#endif + { + serialId -= 1; + } + +#if __JHEXEN__ + // Only signed in Hexen. + DENG2_ASSERT(serialId >= 0); + if(serialId < 0) return; // Does this ever occur? +#endif + + DENG_ASSERT(d->things != 0); + DENG_ASSERT((unsigned)serialId < d->size); + d->things[serialId] = const_cast(mo); +} + +ThingArchive::SerialId ThingArchive::serialIdFor(mobj_t const *mo) +{ + DENG_ASSERT(d->things != 0); + + if(!mo) return 0; + + // We only archive mobj thinkers. + if(((thinker_t *) mo)->function != (thinkfunc_t) P_MobjThinker) + { + return 0; + } + +#if __JHEXEN__ + if(mo->player && d->excludePlayers) + { + return TargetPlayerId; + } +#endif + + uint firstUnused = 0; + bool found = false; + for(uint i = 0; i < d->size; ++i) + { + if(!d->things[i] && !found) + { + firstUnused = i; + found = true; + continue; + } + + if(d->things[i] == mo) + { + return i + 1; + } + } + + if(!found) + { + Con_Error("ThingArchive::serialIdFor: Thing archive exhausted!"); + return 0; // No number available! + } + + // Insert it in the archive. + d->things[firstUnused] = const_cast(mo); + return firstUnused + 1; +} + +mobj_t *ThingArchive::mobj(SerialId serialId, void *address) +{ +#if !__JHEXEN__ + DENG_UNUSED(address); +#endif + +#if __JHEXEN__ + if(serialId == TargetPlayerId) + { + targetplraddress_t *tpa = (targetplraddress_t *)M_Malloc(sizeof(targetplraddress_t)); + + tpa->address = (void **)address; + + tpa->next = targetPlayerAddrs; + targetPlayerAddrs = tpa; + + return 0; + } +#endif + + DENG_ASSERT(d->things != 0); + +#if __JHEXEN__ + if(d->version < 1) + { + // Old format (base 0). + + // A NULL reference? + if(serialId == -1) return 0; + + if(serialId < 0 || (unsigned) serialId > d->size - 1) + return 0; + } + else +#endif + { + // New format (base 1). + + // A NULL reference? + if(serialId == 0) return 0; + + if(serialId < 1 || (unsigned) serialId > d->size) + { + App_Log(DE2_RES_WARNING, "ThingArchive::mobj: Invalid serialId %i", serialId); + return 0; + } + + serialId -= 1; + } + + return d->things[serialId]; +} diff --git a/doomsday/plugins/common/src/thinkerinfo.cpp b/doomsday/plugins/common/src/thinkerinfo.cpp index 2263586108..d3fb8d140d 100644 --- a/doomsday/plugins/common/src/thinkerinfo.cpp +++ b/doomsday/plugins/common/src/thinkerinfo.cpp @@ -22,7 +22,6 @@ #include "thinkerinfo.h" #include "mobj.h" -#include "p_saveg.h" #include "p_ceiling.h" #include "p_door.h" #include "p_floor.h" diff --git a/doomsday/plugins/directsound/src/driver_directsound.cpp b/doomsday/plugins/directsound/src/driver_directsound.cpp index 20b8c33c6c..278d82b4f4 100644 --- a/doomsday/plugins/directsound/src/driver_directsound.cpp +++ b/doomsday/plugins/directsound/src/driver_directsound.cpp @@ -402,15 +402,15 @@ int DS_Init(void) useEAX = false; App_Log(DE2_LOG_DEV | DE2_LOG_AUDIO | DE2_LOG_WARNING, - "dsDirectSound::DS_Init: Failed retrieving property set.\n"); + "dsDirectSound::DS_Init: Failed retrieving property set."); } } // Announce capabilites: - App_Log(DE2_LOG_AUDIO, "DirectSound configuration:\n"); - App_Log(DE2_LOG_AUDIO, " Primary Buffer: %s (%s)\n", (primaryBuffer3D? "3D" : "2D"), + App_Log(DE2_LOG_AUDIO, "DirectSound configuration:"); + App_Log(DE2_LOG_AUDIO, " Primary Buffer: %s (%s)", (primaryBuffer3D? "3D" : "2D"), (primaryBufferHW? "hardware" : "software")); - App_Log(DE2_LOG_AUDIO, " Hardware Buffers: %i\n", (primaryBuffer3D? NUMBUFFERS_HW_3D : NUMBUFFERS_HW_2D)); + App_Log(DE2_LOG_AUDIO, " Hardware Buffers: %i", (primaryBuffer3D? NUMBUFFERS_HW_3D : NUMBUFFERS_HW_2D)); LogBuffer_Printf(DE2_LOG_AUDIO, " DSP: %s", eaxAvailable? "EAX 2.0" : "None"); if(eaxAvailable) LogBuffer_Printf(DE2_LOG_AUDIO, " (%s)", useEAX? "enabled" : "disabled"); @@ -418,19 +418,19 @@ int DS_Init(void) if(eaxAvailable) { - App_Log(DE2_LOG_AUDIO, " EAX Listner Environment:\n"); + App_Log(DE2_LOG_AUDIO, " EAX Listner Environment:"); for(size_t i = 0; eaxProps[i].prop != DSPROPERTY_EAXLISTENER_NONE; ++i) { const eaxproperty_t* p = &eaxProps[i]; App_Log(DE2_LOG_AUDIO, " %s: %s", p->name, - queryEAXSupport(p->prop)? "Present\n" : "Not available\n"); + queryEAXSupport(p->prop)? "Present" : "Not available"); } } // Success! App_Log(DE2_LOG_AUDIO | DE2_LOG_VERBOSE | DE2_LOG_DEV, - "dsDirectSound::DS_Init: Initialization complete, OK.\n"); + "dsDirectSound::DS_Init: Initialization complete, OK."); return true; #undef NUMBUFFERS_HW_3D @@ -1251,7 +1251,7 @@ void DS_SFX_Listenerv(int prop, float* values) case SFXLP_ORIENTATION: if(!dsListener) return; - listenerOrientation(values[VX] / 180 * PI, values[VY] / 180 * PI); + listenerOrientation(values[VX] / 180 * DD_PI, values[VY] / 180 * DD_PI); break; case SFXLP_REVERB: diff --git a/doomsday/plugins/doom/data/conhelp.txt b/doomsday/plugins/doom/data/conhelp.txt index 7aaed47616..ff33df7b19 100644 --- a/doomsday/plugins/doom/data/conhelp.txt +++ b/doomsday/plugins/doom/data/conhelp.txt @@ -35,6 +35,10 @@ desc = End the game. [helpscreen] desc = Show the Help screens. +[inspectgamesave] +desc = Print detailed information about a saved game session to the console. +inf = Params: inspectgamesave (slotId)\nFor example, 'inspectgamesave 1'. + [kill] desc = Kill all the monsters on the map. diff --git a/doomsday/plugins/doom/doom.pro b/doomsday/plugins/doom/doom.pro index 91562e5e49..bb64ed6c4a 100644 --- a/doomsday/plugins/doom/doom.pro +++ b/doomsday/plugins/doom/doom.pro @@ -45,6 +45,7 @@ HEADERS += \ include/doomdata.h \ include/doomdef.h \ include/doomtype.h \ + include/doomv9gamestatereader.h \ include/dstrings.h \ include/g_game.h \ include/info.h \ @@ -58,7 +59,6 @@ HEADERS += \ include/p_local.h \ include/p_maputl.h \ include/p_mobj.h \ - include/p_oldsvg.h \ include/p_pspr.h \ include/p_setup.h \ include/p_spec.h \ @@ -75,8 +75,9 @@ SOURCES += \ src/d_api.c \ src/d_console.c \ src/d_items.c \ - src/d_main.c \ + src/d_main.cpp \ src/d_refresh.c \ + src/doomv9gamestatereader.cpp \ src/m_cheat.c \ src/m_random.c \ src/p_enemy.c \ @@ -84,7 +85,6 @@ SOURCES += \ src/p_lights.cpp \ src/p_maputl.c \ src/p_mobj.c \ - src/p_oldsvg.cpp \ src/p_pspr.c \ src/p_setup.c \ src/p_spec.c \ diff --git a/doomsday/plugins/doom/include/acfnlink.h b/doomsday/plugins/doom/include/acfnlink.h index b6e7d6f78e..96223ec20a 100644 --- a/doomsday/plugins/doom/include/acfnlink.h +++ b/doomsday/plugins/doom/include/acfnlink.h @@ -37,7 +37,7 @@ typedef struct { void (C_DECL *func)(); // Pointer to the function. } actionlink_t; -extern actionlink_t actionlinks[]; +DENG_EXTERN_C actionlink_t actionlinks[]; void C_DECL A_BabyMetal(); void C_DECL A_BFGsound(); diff --git a/doomsday/plugins/doom/include/bossbrain.h b/doomsday/plugins/doom/include/bossbrain.h index 3eff443678..3afd60916f 100644 --- a/doomsday/plugins/doom/include/bossbrain.h +++ b/doomsday/plugins/doom/include/bossbrain.h @@ -80,6 +80,6 @@ struct mobj_s *BossBrain_NextTarget(BossBrain *brain); #endif /// The One BossBrain instance. -DENG_EXTERN_C BossBrain *bossBrain; +DENG_EXTERN_C BossBrain *theBossBrain; #endif // LIBDOOM_PLAY_BOSSBRAIN_H diff --git a/doomsday/plugins/doom/include/d_config.h b/doomsday/plugins/doom/include/d_config.h index 63e67b41dc..122a350d11 100644 --- a/doomsday/plugins/doom/include/d_config.h +++ b/doomsday/plugins/doom/include/d_config.h @@ -105,7 +105,7 @@ typedef struct jdoom_config_s { byte menuShortcutsEnabled; byte menuScaleMode; int menuPatchReplaceMode; - byte menuGameSaveSuggestName; + byte menuGameSaveSuggestDescription; byte menuCursorRotate; float menuTextColors[MENU_COLOR_COUNT][3]; float menuTextFlashColor[3]; diff --git a/doomsday/plugins/doom/include/d_console.h b/doomsday/plugins/doom/include/d_console.h index c621e38a72..c0f32a1f69 100644 --- a/doomsday/plugins/doom/include/d_console.h +++ b/doomsday/plugins/doom/include/d_console.h @@ -33,6 +33,14 @@ # error "Using jDoom headers without __JDOOM__" #endif +#ifdef __cplusplus +extern "C" { +#endif + void G_ConsoleRegistration(void); +#ifdef __cplusplus +} // extern "C" +#endif + #endif /* LIBDOOM_DCONSOLE_H */ diff --git a/doomsday/plugins/doom/include/d_main.h b/doomsday/plugins/doom/include/d_main.h index 5671bbca74..34d1eda555 100644 --- a/doomsday/plugins/doom/include/d_main.h +++ b/doomsday/plugins/doom/include/d_main.h @@ -1,25 +1,21 @@ -/**\file d_main.h - *\section License - * License: GPL - * Online License Link: http://www.gnu.org/licenses/gpl.html +/** @file d_main.h Doom-specific game initialization * - *\author Copyright © 2003-2013 Jaakko Keränen - *\author Copyright © 2005-2013 Daniel Swanson + * @authors Copyright © 2003-2013 Jaakko Keränen + * @authors Copyright © 2005-2013 Daniel Swanson * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301 USA + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA */ #ifndef LIBJDOOM_MAIN_H @@ -31,40 +27,51 @@ #include "doomdef.h" -#ifdef __cplusplus -extern "C" { -#endif - -extern int verbose; +DENG_EXTERN_C int verbose; -//extern dd_bool noMonstersParm; // checkparm of -nomonsters -//extern dd_bool respawnParm; // checkparm of -respawn -//extern dd_bool turboParm; // checkparm of -turbo -//extern dd_bool randomClassParm; // checkparm of -randclass -//extern dd_bool devParm; // checkparm of -devparm -//extern dd_bool fastParm; // checkparm of -fast +DENG_EXTERN_C float turboMul; // Multiplier for turbo. -extern float turboMul; // Multiplier for turbo. +DENG_EXTERN_C gamemode_t gameMode; +DENG_EXTERN_C int gameModeBits; -extern gamemode_t gameMode; -extern int gameModeBits; +DENG_EXTERN_C char const *borderGraphics[]; -extern char* borderGraphics[]; +DENG_EXTERN_C float defFontRGB[]; +DENG_EXTERN_C float defFontRGB2[]; +DENG_EXTERN_C float defFontRGB3[]; -extern float defFontRGB[]; -extern float defFontRGB2[]; -extern float defFontRGB3[]; +DENG_EXTERN_C dd_bool monsterInfight; -extern dd_bool monsterInfight; +#ifdef __cplusplus +extern "C" { +#endif +/** + * Pre Game Initialization routine. + * All game-specific actions that should take place at this time go here. + */ void D_PreInit(void); + +/** + * Post Game Initialization routine. + * All game-specific actions that should take place at this time go here. + */ void D_PostInit(void); + void D_Shutdown(void); + +/** + * Get a 32-bit integer value. + */ int D_GetInteger(int id); -void* D_GetVariable(int id); + +/** + * Get a pointer to the value of a named variable/constant. + */ +void *D_GetVariable(int id); #ifdef __cplusplus } // extern "C" #endif -#endif /* LIBJDOOM_MAIN_H */ +#endif // LIBJDOOM_MAIN_H diff --git a/doomsday/plugins/doom/include/d_player.h b/doomsday/plugins/doom/include/d_player.h index 6a2ea4d6a5..28f67d0f83 100644 --- a/doomsday/plugins/doom/include/d_player.h +++ b/doomsday/plugins/doom/include/d_player.h @@ -117,8 +117,8 @@ typedef struct player_s { int lockFull; #ifdef __cplusplus - void write(Writer *writer) const; - void read(Reader *reader); + void write(Writer *writer, struct playerheader_s &plrHdr) const; + void read(Reader *reader, struct playerheader_s &plrHdr); #endif } player_t; diff --git a/doomsday/plugins/doom/include/p_oldsvg.h b/doomsday/plugins/doom/include/doomv9gamestatereader.h similarity index 63% rename from doomsday/plugins/doom/include/p_oldsvg.h rename to doomsday/plugins/doom/include/doomv9gamestatereader.h index 985a8519fb..dc7e6eea9e 100644 --- a/doomsday/plugins/doom/include/p_oldsvg.h +++ b/doomsday/plugins/doom/include/doomv9gamestatereader.h @@ -1,4 +1,4 @@ -/** @file p_oldsvg.h Doom ver 1.9 saved game state reader. +/** @file doomv9gamestatereader.h Doom ver 1.9 saved game state reader. * * @authors Copyright © 2003-2013 Jaakko Keränen * @authors Copyright © 2006-2013 Daniel Swanson @@ -26,33 +26,26 @@ # error "Using jDoom headers without __JDOOM__" #endif -#include "saveinfo.h" +#include "gamestatereader.h" /** * Doom ver 1.9 saved game state reader. * * @ingroup libdoom */ -class DoomV9GameStateReader +class DoomV9GameStateReader : public IGameStateReader { public: - /// An error occurred attempting to open the input file. @ingroup errors - DENG2_ERROR(FileAccessError); + DoomV9GameStateReader(); + ~DoomV9GameStateReader(); - /// Base class for read errors. @ingroup errors - DENG2_ERROR(ReadError); + static IGameStateReader *make(); + static bool recognize(SaveInfo &info); -public: - /** - * Determines whether the resource file on @a path is interpretable as a game state which can - * be loaded with a DoomV9GameStateReader. - * - * @param info SaveInfo to attempt to read game session header into. - * @param path Path to the resource file to be recognized. - */ - static bool recognize(SaveInfo *info, Str const *path); - - void read(SaveInfo *info, Str const *path); + void read(SaveInfo &info); + +private: + DENG2_PRIVATE(d) }; #endif // LIBDOOM_DOOMV9_GAMESTATEREADER diff --git a/doomsday/plugins/doom/include/g_game.h b/doomsday/plugins/doom/include/g_game.h index a54e4c1bfb..c78e780d48 100644 --- a/doomsday/plugins/doom/include/g_game.h +++ b/doomsday/plugins/doom/include/g_game.h @@ -68,15 +68,27 @@ extern "C" { #endif void G_Register(void); + void G_CommonPreInit(void); + +/** + * Common Post Game Initialization routine. + * Game-specific post init actions should be placed in eg D_PostInit() + * (for jDoom) and NOT here. + */ void G_CommonPostInit(void); + void G_CommonShutdown(void); void R_InitRefresh(void); +/** + * Print a list of all currently available maps and the location of the + * source file/directory which contains them. + */ void G_PrintMapList(void); -void G_DeferredPlayDemo(char* demo); +void G_DeferredPlayDemo(char *demo); void G_QuitGame(void); @@ -101,7 +113,7 @@ dd_bool G_IsSaveGamePossible(void); * If an empty string a new name will be generated automatically. * @return @c true iff @a slot is valid and saving is presently possible. */ -dd_bool G_SaveGame2(int slot, const char* name); +dd_bool G_SaveGame2(int slot, char const *name); dd_bool G_SaveGame(int slot); void G_StopDemo(void); @@ -129,19 +141,19 @@ void G_IntermissionDone(void); void G_Ticker(timespan_t ticLength); /// @return @c true if the input event @a ev was eaten. -int G_PrivilegedResponder(event_t* ev); +int G_PrivilegedResponder(event_t *ev); /// @return @c true if the input event @a ev was eaten. -int G_Responder(event_t* ev); +int G_Responder(event_t *ev); void G_ScreenShot(void); void G_PrepareWIData(void); -void G_QueueBody(mobj_t* body); +void G_QueueBody(mobj_t *body); #ifdef __cplusplus } // extern "C" #endif -#endif /* LIBJDOOM_G_GAME_H */ +#endif // LIBJDOOM_G_GAME_H diff --git a/doomsday/plugins/doom/include/m_cheat.h b/doomsday/plugins/doom/include/m_cheat.h index aa9c5a2f18..0a97e72915 100644 --- a/doomsday/plugins/doom/include/m_cheat.h +++ b/doomsday/plugins/doom/include/m_cheat.h @@ -27,9 +27,17 @@ # error "Using jDoom headers without __JDOOM__" #endif +#ifdef __cplusplus +extern "C" { +#endif + /** * Register event sequence callbacks for all cheats. */ void G_RegisterCheats(void); +#ifdef __cplusplus +} // extern "C" +#endif + #endif // LIBDOOM_M_CHEAT_H diff --git a/doomsday/plugins/doom/include/p_pspr.h b/doomsday/plugins/doom/include/p_pspr.h index 5905c21692..dd29848420 100644 --- a/doomsday/plugins/doom/include/p_pspr.h +++ b/doomsday/plugins/doom/include/p_pspr.h @@ -50,12 +50,20 @@ typedef enum { } psprnum_t; typedef struct { - state_t* state; // A NULL state means not active. - int tics; - float pos[2]; // [x, y]. + state_t *state; // A NULL state means not active. + int tics; + float pos[2]; // [x, y]. } pspdef_t; -void R_GetWeaponBob(int player, float* x, float* y); -void P_BringUpWeapon(struct player_s *player); +#ifdef __cplusplus +extern "C" { +#endif + +void R_GetWeaponBob(int player, float *x, float *y); +void P_BringUpWeapon(struct player_s *player); + +#ifdef __cplusplus +} // extern "C" +#endif #endif diff --git a/doomsday/plugins/doom/include/p_setup.h b/doomsday/plugins/doom/include/p_setup.h index 4dd703fc81..d3485ac0e9 100644 --- a/doomsday/plugins/doom/include/p_setup.h +++ b/doomsday/plugins/doom/include/p_setup.h @@ -50,8 +50,17 @@ enum { MO_TAG }; +#ifdef __cplusplus +extern "C" { +#endif + void P_RegisterMapObjs(void); int P_HandleMapDataPropertyValue(uint id, int dtype, int prop, valuetype_t type, void *data); int P_HandleMapObjectStatusReport(int code, uint id, int dtype, void *data); + +#ifdef __cplusplus +} // extern "C" +#endif + #endif diff --git a/doomsday/plugins/doom/include/wi_stuff.h b/doomsday/plugins/doom/include/wi_stuff.h index d00d582b68..5c2724fe9c 100644 --- a/doomsday/plugins/doom/include/wi_stuff.h +++ b/doomsday/plugins/doom/include/wi_stuff.h @@ -81,6 +81,10 @@ typedef enum { ILS_SHOW_NEXTMAP } interludestate_t; +#ifdef __cplusplus +extern "C" { +#endif + /// To be called to register the console commands and variables of this module. void WI_Register(void); @@ -116,4 +120,8 @@ void WI_End(void); */ void IN_SkipToNext(void); -#endif /* LIBDOOM_WI_STUFF_H */ +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // LIBDOOM_WI_STUFF_H diff --git a/doomsday/plugins/doom/src/bossbrain.cpp b/doomsday/plugins/doom/src/bossbrain.cpp index a351ad225f..834dac150a 100644 --- a/doomsday/plugins/doom/src/bossbrain.cpp +++ b/doomsday/plugins/doom/src/bossbrain.cpp @@ -26,7 +26,7 @@ #include "p_saveg.h" -BossBrain *bossBrain; // The One boss brain. +BossBrain *theBossBrain; // The One boss brain. DENG2_PIMPL_NOREF(BossBrain) { @@ -80,7 +80,7 @@ void BossBrain::write(MapStateWriter *msw) const // Write the mobj references using the mobj archive. for(int i = 0; i < d->numTargets; ++i) { - Writer_WriteInt16(writer, SV_ThingArchiveId(d->targets[i])); + Writer_WriteInt16(writer, msw->serialIdFor(d->targets[i])); } } @@ -116,7 +116,7 @@ void BossBrain::read(MapStateReader *msr) for(int i = 0; i < newTargetCount; ++i) { - addTarget(SV_GetArchiveThing((int) Reader_ReadInt16(reader), 0)); + addTarget(msr->mobj((int) Reader_ReadInt16(reader), 0)); } } diff --git a/doomsday/plugins/doom/src/d_main.c b/doomsday/plugins/doom/src/d_main.cpp similarity index 90% rename from doomsday/plugins/doom/src/d_main.c rename to doomsday/plugins/doom/src/d_main.cpp index e9ab63288b..7806dabf08 100644 --- a/doomsday/plugins/doom/src/d_main.c +++ b/doomsday/plugins/doom/src/d_main.cpp @@ -1,4 +1,4 @@ -/** @file d_main.c Doom-specific game initialization. +/** @file d_main.cpp Doom-specific game initialization. * * @authors Copyright © 2003-2013 Jaakko Keränen * @authors Copyright © 2005-2013 Daniel Swanson @@ -26,19 +26,13 @@ #include "m_argv.h" #include "p_map.h" #include "p_saveg.h" +#include "doomv9gamestatereader.h" #include "am_map.h" #include "g_defs.h" -#include +#include "saveslots.h" int verbose; -//dd_bool devParm; // checkparm of -devparm -//dd_bool noMonstersParm; // checkparm of -nomonsters -//dd_bool respawnParm; // checkparm of -respawn -//dd_bool fastParm; // checkparm of -fast -//dd_bool turboParm; // checkparm of -turbo -//dd_bool randomClassParm; // checkparm of -randclass - float turboMul; // Multiplier for turbo. gamemode_t gameMode; @@ -51,7 +45,7 @@ float defFontRGB3[3]; // The patches used in drawing the view border. // Percent-encoded. -char* borderGraphics[] = { +char const *borderGraphics[] = { "Flats:FLOOR7_2", // Background. "BRDR_T", // Top. "BRDR_R", // Right. @@ -63,40 +57,34 @@ char* borderGraphics[] = { "BRDR_BL" // Bottom left. }; -/** - * Get a 32-bit integer value. - */ int D_GetInteger(int id) { return Common_GetInteger(id); } -/** - * Get a pointer to the value of a named variable/constant. - */ -void* D_GetVariable(int id) +void *D_GetVariable(int id) { static float bob[2]; switch(id) { case DD_PLUGIN_NAME: - return PLUGIN_NAMETEXT; + return (void*)PLUGIN_NAMETEXT; case DD_PLUGIN_NICENAME: - return PLUGIN_NICENAME; + return (void*)PLUGIN_NICENAME; case DD_PLUGIN_VERSION_SHORT: - return PLUGIN_VERSION_TEXT; + return (void*)PLUGIN_VERSION_TEXT; case DD_PLUGIN_VERSION_LONG: - return PLUGIN_VERSION_TEXTLONG "\n" PLUGIN_DETAILS; + return (void*)(PLUGIN_VERSION_TEXTLONG "\n" PLUGIN_DETAILS); case DD_PLUGIN_HOMEURL: - return PLUGIN_HOMEURL; + return (void*)PLUGIN_HOMEURL; case DD_PLUGIN_DOCSURL: - return PLUGIN_DOCSURL; + return (void*)PLUGIN_DOCSURL; case DD_GAME_CONFIG: return gameConfigString; @@ -127,14 +115,8 @@ void* D_GetVariable(int id) return 0; } -/** - * Pre Game Initialization routine. - * All game-specific actions that should take place at this time go here. - */ -void D_PreInit(void) +void D_PreInit() { - int i; - // Configure default colors: switch(gameMode) { @@ -198,14 +180,16 @@ void D_PreInit(void) cfg.menuQuitSound = true; cfg.menuSlam = false; cfg.menuShortcutsEnabled = true; - cfg.menuGameSaveSuggestName = true; + cfg.menuGameSaveSuggestDescription = true; cfg.menuEffectFlags = MEF_TEXT_TYPEIN|MEF_TEXT_SHADOW|MEF_TEXT_GLITTER; cfg.menuTextFlashColor[0] = .7f; cfg.menuTextFlashColor[1] = .9f; cfg.menuTextFlashColor[2] = 1; cfg.menuTextFlashSpeed = 4; if(gameMode != doom_chex) + { cfg.menuCursorRotate = true; + } if(gameMode == doom2_hacx) { cfg.menuTextColors[0][CR] = cfg.menuTextColors[0][CG] = cfg.menuTextColors[0][CB] = 1; @@ -244,7 +228,7 @@ void D_PreInit(void) cfg.hudShown[HUD_FRAGS] = true; cfg.hudShown[HUD_FACE] = false; cfg.hudShown[HUD_LOG] = true; - for(i = 0; i < NUMHUDUNHIDEEVENTS; ++i) // when the hud/statusbar unhides. + for(int i = 0; i < NUMHUDUNHIDEEVENTS; ++i) // when the hud/statusbar unhides. { cfg.hudUnHide[i] = 1; } @@ -398,16 +382,10 @@ void D_PreInit(void) G_InitSpecialFilter(); } -/** - * Post Game Initialization routine. - * All game-specific actions that should take place at this time go here. - */ -void D_PostInit(void) +void D_PostInit() { dd_bool autoStart = false; Uri *startMapUri = 0; - AutoStr *path; - int p; /// @todo Kludge: Border background is different in DOOM2. /// @todo Do this properly! @@ -419,6 +397,9 @@ void D_PostInit(void) // Common post init routine G_CommonPostInit(); + // Declare the Doom V9 game state reader/interpreter. + SV_DeclareGameStateReader(&DoomV9GameStateReader::recognize, &DoomV9GameStateReader::make); + // Initialize ammo info. P_InitAmmoInfo(); @@ -441,7 +422,7 @@ void D_PostInit(void) gameRules.respawnMonsters = CommandLine_Check("-respawn")? true : false; gameRules.fast = CommandLine_Check("-fast")? true : false; - p = CommandLine_Check("-timer"); + int p = CommandLine_Check("-timer"); if(p && p < myargc - 1 && gameRules.deathmatch) { int time = atoi(CommandLine_At(p + 1)); @@ -470,8 +451,8 @@ void D_PostInit(void) p = CommandLine_Check("-loadgame"); if(p && p < myargc - 1) { - int const slotNumber = SaveSlots_ParseSlotIdentifier(saveSlots, CommandLine_At(p + 1)); - if(SaveSlots_SlotIsUserWritable(saveSlots, slotNumber) && G_LoadGame(slotNumber)) + int const slotNumber = SV_SaveSlots().parseSlotIdentifier(CommandLine_At(p + 1)); + if(SV_SaveSlots().slotIsUserWritable(slotNumber) && G_LoadGame(slotNumber)) { // No further initialization is to be done. return; @@ -530,7 +511,7 @@ void D_PostInit(void) } // Validate episode and map. - path = Uri_Compose(startMapUri); + AutoStr *path = Uri_Compose(startMapUri); if((autoStart || IS_NETGAME) && P_MapExists(Str_Text(path))) { G_DeferredNewGame(startMapUri, 0/*default*/, &gameRules); @@ -543,7 +524,7 @@ void D_PostInit(void) Uri_Delete(startMapUri); } -void D_Shutdown(void) +void D_Shutdown() { WI_Shutdown(); G_CommonShutdown(); diff --git a/doomsday/plugins/doom/src/doomv9gamestatereader.cpp b/doomsday/plugins/doom/src/doomv9gamestatereader.cpp new file mode 100644 index 0000000000..5ac887ae57 --- /dev/null +++ b/doomsday/plugins/doom/src/doomv9gamestatereader.cpp @@ -0,0 +1,1003 @@ +/** @file doomv9gamestatereader.cpp Doom ver 1.9 save game reader. + * + * @authors Copyright © 2003-2013 Jaakko Keränen + * @authors Copyright © 2005-2013 Daniel Swanson + * @authors Copyright © 1993-1996 id Software, Inc. + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "jdoom.h" +#include "doomv9gamestatereader.h" + +#include "am_map.h" +#include "dmu_lib.h" +#include "p_ceiling.h" +#include "p_door.h" +#include "p_floor.h" +#include "p_map.h" +#include "p_mapsetup.h" +#include "p_plat.h" +#include "p_saveio.h" +#include "p_saveg.h" +#include "p_tick.h" +#include "r_common.h" // R_UpdateConsoleView +#include + +#define PADSAVEP() savePtr += (4 - ((savePtr - saveBuffer) & 3)) & 3 + +#define SIZEOF_V19_THINKER_T 12 +#define V19_THINKER_T_FUNC_OFFSET 8 + +static byte *savePtr; +static byte *saveBuffer; + +static char sri8(Reader *r) +{ + if(!r) return 0; + savePtr++; + return *(char *) (savePtr - 1); +} + +static short sri16(Reader *r) +{ + if(!r) return 0; + savePtr += 2; + return *(int16_t *) (savePtr - 2); +} + +static int sri32(Reader *r) +{ + if(!r) return 0; + savePtr += 4; + return *(int32_t *) (savePtr - 4); +} + +static void srd(Reader *r, char *data, int len) +{ + if(!r) return; + if(data) + { + memcpy(data, savePtr, len); + } + savePtr += len; +} + +static Uri *readTextureUrn(Reader *reader, char const *schemeName) +{ + DENG_ASSERT(reader != 0 && schemeName != 0); + return Uri_NewWithPath2(Str_Text(Str_Appendf(AutoStr_NewStd(), "urn:%s:%i", schemeName, Reader_ReadInt16(reader))), RC_NULL); +} + +static void readPlayer(player_t *pl, Reader *reader) +{ + Reader_ReadInt32(reader); + + pl->playerState = playerstate_t(Reader_ReadInt32(reader)); + + Reader_Read(reader, NULL, 8); + + pl->viewZ = FIX2FLT(Reader_ReadInt32(reader)); + pl->viewHeight = FIX2FLT(Reader_ReadInt32(reader)); + pl->viewHeightDelta = FIX2FLT(Reader_ReadInt32(reader)); + pl->bob = FLT2FIX(Reader_ReadInt32(reader)); + pl->flyHeight = 0; + pl->health = Reader_ReadInt32(reader); + pl->armorPoints = Reader_ReadInt32(reader); + pl->armorType = Reader_ReadInt32(reader); + + de::zap(pl->powers); + pl->powers[PT_INVULNERABILITY] = Reader_ReadInt32(reader); + pl->powers[PT_STRENGTH] = Reader_ReadInt32(reader); + pl->powers[PT_INVISIBILITY] = Reader_ReadInt32(reader); + pl->powers[PT_IRONFEET] = Reader_ReadInt32(reader); + pl->powers[PT_ALLMAP] = Reader_ReadInt32(reader); + if(pl->powers[PT_ALLMAP]) + { + ST_RevealAutomap(pl - players, true); + } + pl->powers[PT_INFRARED] = Reader_ReadInt32(reader); + + de::zap(pl->keys); + pl->keys[KT_BLUECARD] = !!Reader_ReadInt32(reader); + pl->keys[KT_YELLOWCARD] = !!Reader_ReadInt32(reader); + pl->keys[KT_REDCARD] = !!Reader_ReadInt32(reader); + pl->keys[KT_BLUESKULL] = !!Reader_ReadInt32(reader); + pl->keys[KT_YELLOWSKULL] = !!Reader_ReadInt32(reader); + pl->keys[KT_REDSKULL] = !!Reader_ReadInt32(reader); + + pl->backpack = Reader_ReadInt32(reader); + + de::zap(pl->frags); + pl->frags[0] = Reader_ReadInt32(reader); + pl->frags[1] = Reader_ReadInt32(reader); + pl->frags[2] = Reader_ReadInt32(reader); + pl->frags[3] = Reader_ReadInt32(reader); + + pl->readyWeapon = weapontype_t(Reader_ReadInt32(reader)); + pl->pendingWeapon = weapontype_t(Reader_ReadInt32(reader)); + + de::zap(pl->weapons); + pl->weapons[WT_FIRST].owned = !!Reader_ReadInt32(reader); + pl->weapons[WT_SECOND].owned = !!Reader_ReadInt32(reader); + pl->weapons[WT_THIRD].owned = !!Reader_ReadInt32(reader); + pl->weapons[WT_FOURTH].owned = !!Reader_ReadInt32(reader); + pl->weapons[WT_FIFTH].owned = !!Reader_ReadInt32(reader); + pl->weapons[WT_SIXTH].owned = !!Reader_ReadInt32(reader); + pl->weapons[WT_SEVENTH].owned = !!Reader_ReadInt32(reader); + pl->weapons[WT_EIGHTH].owned = !!Reader_ReadInt32(reader); + pl->weapons[WT_NINETH].owned = !!Reader_ReadInt32(reader); + + de::zap(pl->ammo); + pl->ammo[AT_CLIP].owned = Reader_ReadInt32(reader); + pl->ammo[AT_SHELL].owned = Reader_ReadInt32(reader); + pl->ammo[AT_CELL].owned = Reader_ReadInt32(reader); + pl->ammo[AT_MISSILE].owned = Reader_ReadInt32(reader); + + pl->ammo[AT_CLIP].max = Reader_ReadInt32(reader); + pl->ammo[AT_SHELL].max = Reader_ReadInt32(reader); + pl->ammo[AT_CELL].max = Reader_ReadInt32(reader); + pl->ammo[AT_MISSILE].max = Reader_ReadInt32(reader); + + pl->attackDown = Reader_ReadInt32(reader); + pl->useDown = Reader_ReadInt32(reader); + + pl->cheats = Reader_ReadInt32(reader); + pl->refire = Reader_ReadInt32(reader); + + pl->killCount = Reader_ReadInt32(reader); + pl->itemCount = Reader_ReadInt32(reader); + pl->secretCount = Reader_ReadInt32(reader); + + Reader_ReadInt32(reader); + + pl->damageCount = Reader_ReadInt32(reader); + pl->bonusCount = Reader_ReadInt32(reader); + + Reader_ReadInt32(reader); + + pl->plr->extraLight = Reader_ReadInt32(reader); + pl->plr->fixedColorMap = Reader_ReadInt32(reader); + + pl->colorMap = Reader_ReadInt32(reader); + + for(int i = 0; i < 2; ++i) + { + pspdef_t *psp = &pl->pSprites[i]; + + psp->state = INT2PTR(state_t, Reader_ReadInt32(reader)); + psp->tics = Reader_ReadInt32(reader); + psp->pos[VX] = FIX2FLT(Reader_ReadInt32(reader)); + psp->pos[VY] = FIX2FLT(Reader_ReadInt32(reader)); + } + + pl->didSecret = !!Reader_ReadInt32(reader); +} + +static void readMobj(Reader *reader) +{ +#define FF_FULLBRIGHT 0x8000 ///< Used to be a flag in thing->frame. +#define FF_FRAMEMASK 0x7fff + + // List: thinker links. + Reader_ReadInt32(reader); + Reader_ReadInt32(reader); + Reader_ReadInt32(reader); + + // Info for drawing: position. + coord_t pos[3]; + pos[VX] = FIX2FLT(Reader_ReadInt32(reader)); + pos[VY] = FIX2FLT(Reader_ReadInt32(reader)); + pos[VZ] = FIX2FLT(Reader_ReadInt32(reader)); + + // More list: links in sector (if needed) + Reader_ReadInt32(reader); + Reader_ReadInt32(reader); + + //More drawing info: to determine current sprite. + angle_t angle = Reader_ReadInt32(reader); // orientation + spritenum_t sprite = Reader_ReadInt32(reader); // used to find patch_t and flip value + + int frame = Reader_ReadInt32(reader); // might be ORed with FF_FULLBRIGHT + if(frame & FF_FULLBRIGHT) + { + frame &= FF_FRAMEMASK; // not used anymore. + } + + // Interaction info, by BLOCKMAP. + // Links in blocks (if needed). + Reader_ReadInt32(reader); + Reader_ReadInt32(reader); + Reader_ReadInt32(reader); + + // The closest interval over all contacted Sectors. + coord_t floorz = FIX2FLT(Reader_ReadInt32(reader)); + coord_t ceilingz = FIX2FLT(Reader_ReadInt32(reader)); + + // For movement checking. + coord_t radius = FIX2FLT(Reader_ReadInt32(reader)); + coord_t height = FIX2FLT(Reader_ReadInt32(reader)); + + // Momentums, used to update position. + coord_t mom[3]; + mom[MX] = FIX2FLT(Reader_ReadInt32(reader)); + mom[MY] = FIX2FLT(Reader_ReadInt32(reader)); + mom[MZ] = FIX2FLT(Reader_ReadInt32(reader)); + + int valid = Reader_ReadInt32(reader); + int type = Reader_ReadInt32(reader); + mobjinfo_t *info = &MOBJINFO[type]; + + int ddflags = 0; + if(info->flags & MF_SOLID) ddflags |= DDMF_SOLID; + if(info->flags2 & MF2_DONTDRAW) ddflags |= DDMF_DONTDRAW; + + /* + * We now have all the information we need to create the mobj. + */ + mobj_t *mo = Mobj_CreateXYZ(P_MobjThinker, pos[VX], pos[VY], pos[VZ], angle, + radius, height, ddflags); + + mo->sprite = sprite; + mo->frame = frame; + mo->floorZ = floorz; + mo->ceilingZ = ceilingz; + mo->mom[MX] = mom[MX]; + mo->mom[MY] = mom[MY]; + mo->mom[MZ] = mom[MZ]; + mo->valid = valid; + mo->type = type; + mo->moveDir = DI_NODIR; + + Reader_ReadInt32(reader); // &mobjinfo[mo->type] + + mo->tics = Reader_ReadInt32(reader); // state tic counter + mo->state = INT2PTR(state_t, Reader_ReadInt32(reader)); + mo->damage = DDMAXINT; // Use damage set in mo->info->damage + mo->flags = Reader_ReadInt32(reader); + mo->health = Reader_ReadInt32(reader); + + // Movement direction, movement generation (zig-zagging). + mo->moveDir = Reader_ReadInt32(reader); // 0-7 + mo->moveCount = Reader_ReadInt32(reader); // when 0, select a new dir + + // Thing being chased/attacked (or NULL), + // also the originator for missiles. + Reader_ReadInt32(reader); + + // Reaction time: if non 0, don't attack yet. + // Used by player to freeze a bit after teleporting. + mo->reactionTime = Reader_ReadInt32(reader); + + // If >0, the target will be chased + // no matter what (even if shot) + mo->threshold = Reader_ReadInt32(reader); + + // Additional info record for player avatars only. + // Only valid if type == MT_PLAYER + mo->player = INT2PTR(player_t, Reader_ReadInt32(reader)); + + // Player number last looked for. + mo->lastLook = Reader_ReadInt32(reader); + + // For nightmare respawn. + mo->spawnSpot.origin[VX] = (float) Reader_ReadInt16(reader); + mo->spawnSpot.origin[VY] = (float) Reader_ReadInt16(reader); + mo->spawnSpot.origin[VZ] = 0; // Initialize with "something". + mo->spawnSpot.angle = (angle_t) (ANG45 * ((int)Reader_ReadInt16(reader) / 45)); + /* mo->spawnSpot.type = (int) */ Reader_ReadInt16(reader); + + int spawnFlags = ((int) Reader_ReadInt16(reader)) & ~MASK_UNKNOWN_MSF_FLAGS; + // Spawn on the floor by default unless the mobjtype flags override. + spawnFlags |= MSF_Z_FLOOR; + mo->spawnSpot.flags = spawnFlags; + + // Thing being chased/attacked for tracers. + Reader_ReadInt32(reader); + + mo->info = info; + SV_TranslateLegacyMobjFlags(mo, 0); + + mo->state = &STATES[PTR2INT(mo->state)]; + mo->target = 0; + if(mo->player) + { + int pnum = PTR2INT(mo->player) - 1; + + mo->player = &players[pnum]; + mo->dPlayer = mo->player->plr; + + mo->dPlayer->mo = mo; + //mo->dPlayer->clAngle = mo->angle; /* $unifiedangles */ + mo->dPlayer->lookDir = 0; /* $unifiedangles */ + } + P_MobjLink(mo); + mo->floorZ = P_GetDoublep(Mobj_Sector(mo), DMU_FLOOR_HEIGHT); + mo->ceilingZ = P_GetDoublep(Mobj_Sector(mo), DMU_CEILING_HEIGHT); + +#undef FF_FRAMEMASK +#undef FF_FULLBRIGHT +} + +static int removeThinker(thinker_t *th, void * /*context*/) +{ + if(th->function == (thinkfunc_t) P_MobjThinker) + { + P_MobjRemove((mobj_t *) th, true); + } + else + { + Z_Free(th); + } + + return false; // Continue iteration. +} + +static int readCeiling(ceiling_t *ceiling, Reader *reader) +{ +/* Original DOOM format: +typedef struct { + thinker_t thinker; // was 12 bytes + ceilingtype_e type; // was 32bit int + Sector *sector; + fixed_t bottomheight; + fixed_t topheight; + fixed_t speed; + dd_bool crush; + int direction; + int tag; + int olddirection; +} v19_ceiling_t; +*/ + byte temp[SIZEOF_V19_THINKER_T]; + + // Padding at the start (an old thinker_t struct). + Reader_Read(reader, &temp, SIZEOF_V19_THINKER_T); + + // Start of used data members. + ceiling->type = ceilingtype_e(Reader_ReadInt32(reader)); + + // A 32bit pointer to sector, serialized. + ceiling->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(reader)); + DENG_ASSERT(ceiling->sector != 0); + + ceiling->bottomHeight = FIX2FLT(Reader_ReadInt32(reader)); + ceiling->topHeight = FIX2FLT(Reader_ReadInt32(reader)); + ceiling->speed = FIX2FLT(Reader_ReadInt32(reader)); + ceiling->crush = Reader_ReadInt32(reader); + ceiling->state = (Reader_ReadInt32(reader) == -1? CS_DOWN : CS_UP); + ceiling->tag = Reader_ReadInt32(reader); + ceiling->oldState = (Reader_ReadInt32(reader) == -1? CS_DOWN : CS_UP); + + ceiling->thinker.function = T_MoveCeiling; + if(!(temp + V19_THINKER_T_FUNC_OFFSET)) + { + Thinker_SetStasis(&ceiling->thinker, true); + } + + P_ToXSector(ceiling->sector)->specialData = ceiling; + return true; // Add this thinker. +} + +static int readDoor(door_t *door, Reader *reader) +{ +/* Original DOOM format: +typedef struct { + thinker_t thinker; // was 12 bytes + doortype_e type; // was 32bit int + Sector *sector; + fixed_t topheight; + fixed_t speed; + int direction; + int topwait; + int topcountdown; +} v19_vldoor_t; +*/ + // Padding at the start (an old thinker_t struct) + Reader_Read(reader, NULL, SIZEOF_V19_THINKER_T); + + // Start of used data members. + door->type = doortype_e(Reader_ReadInt32(reader)); + + // A 32bit pointer to sector, serialized. + door->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(reader)); + DENG_ASSERT(door->sector != 0); + + door->topHeight = FIX2FLT(Reader_ReadInt32(reader)); + door->speed = FIX2FLT(Reader_ReadInt32(reader)); + door->state = doorstate_e(Reader_ReadInt32(reader)); + door->topWait = Reader_ReadInt32(reader); + door->topCountDown = Reader_ReadInt32(reader); + + door->thinker.function = T_Door; + + P_ToXSector(door->sector)->specialData = door; + return true; // Add this thinker. +} + +static int readFloor(floor_t *floor, Reader *reader) +{ +/* Original DOOM format: +typedef struct { + thinker_t thinker; // was 12 bytes + floortype_e type; // was 32bit int + dd_bool crush; + Sector *sector; + int direction; + int newspecial; + short texture; + fixed_t floordestheight; + fixed_t speed; +} v19_floormove_t; +*/ + + // Padding at the start (an old thinker_t struct) + Reader_Read(reader, NULL, SIZEOF_V19_THINKER_T); + + // Start of used data members. + floor->type = floortype_e(Reader_ReadInt32(reader)); + floor->crush = Reader_ReadInt32(reader); + + // A 32bit pointer to sector, serialized. + floor->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(reader)); + DENG_ASSERT(floor->sector != 0); + + floor->state = floorstate_e(Reader_ReadInt32(reader)); + floor->newSpecial = Reader_ReadInt32(reader); + + Uri *newTextureUrn = readTextureUrn(reader, "Flats"); + floor->material = DD_MaterialForTextureUri(newTextureUrn); + Uri_Delete(newTextureUrn); + + floor->floorDestHeight = FIX2FLT(Reader_ReadInt32(reader)); + floor->speed = FIX2FLT(Reader_ReadInt32(reader)); + + floor->thinker.function = T_MoveFloor; + + P_ToXSector(floor->sector)->specialData = floor; + return true; // Add this thinker. +} + +static int readPlat(plat_t *plat, Reader *reader) +{ +/* Original DOOM format: +typedef struct { + thinker_t thinker; // was 12 bytes + Sector *sector; + fixed_t speed; + fixed_t low; + fixed_t high; + int wait; + int count; + platstate_e status; // was 32bit int + platstate_e oldstatus; // was 32bit int + dd_bool crush; + int tag; + plattype_e type; // was 32bit int +} v19_plat_t; +*/ + byte temp[SIZEOF_V19_THINKER_T]; + + // Padding at the start (an old thinker_t struct) + Reader_Read(reader, temp, SIZEOF_V19_THINKER_T); + + // Start of used data members. + // A 32bit pointer to sector, serialized. + plat->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(reader)); + DENG_ASSERT(plat->sector != 0); + + plat->speed = FIX2FLT(Reader_ReadInt32(reader)); + plat->low = FIX2FLT(Reader_ReadInt32(reader)); + plat->high = FIX2FLT(Reader_ReadInt32(reader)); + plat->wait = Reader_ReadInt32(reader); + plat->count = Reader_ReadInt32(reader); + plat->state = platstate_e(Reader_ReadInt32(reader)); + plat->oldState = platstate_e(Reader_ReadInt32(reader)); + plat->crush = Reader_ReadInt32(reader); + plat->tag = Reader_ReadInt32(reader); + plat->type = plattype_e(Reader_ReadInt32(reader)); + + plat->thinker.function = T_PlatRaise; + if(!(temp + V19_THINKER_T_FUNC_OFFSET)) + { + Thinker_SetStasis(&plat->thinker, true); + } + + P_ToXSector(plat->sector)->specialData = plat; + return true; // Add this thinker. +} + +static int readFlash(lightflash_t *flash, Reader *reader) +{ +/* Original DOOM format: +typedef struct { + thinker_t thinker; // was 12 bytes + Sector *sector; + int count; + int maxlight; + int minlight; + int maxtime; + int mintime; +} v19_lightflash_t; +*/ + // Padding at the start (an old thinker_t struct) + Reader_Read(reader, NULL, SIZEOF_V19_THINKER_T); + + // Start of used data members. + // A 32bit pointer to sector, serialized. + flash->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(reader)); + DENG_ASSERT(flash->sector != 0); + + flash->count = Reader_ReadInt32(reader); + flash->maxLight = (float) Reader_ReadInt32(reader) / 255.0f; + flash->minLight = (float) Reader_ReadInt32(reader) / 255.0f; + flash->maxTime = Reader_ReadInt32(reader); + flash->minTime = Reader_ReadInt32(reader); + + flash->thinker.function = (thinkfunc_t) T_LightFlash; + return true; // Add this thinker. +} + +static int readStrobe(strobe_t *strobe, Reader *reader) +{ +/* Original DOOM format: +typedef struct { + thinker_t thinker; // was 12 bytes + Sector *sector; + int count; + int minlight; + int maxlight; + int darktime; + int brighttime; +} v19_strobe_t; +*/ + // Padding at the start (an old thinker_t struct) + Reader_Read(reader, NULL, SIZEOF_V19_THINKER_T); + + // Start of used data members. + // A 32bit pointer to sector, serialized. + strobe->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(reader)); + DENG_ASSERT(strobe->sector != 0); + + strobe->count = Reader_ReadInt32(reader); + strobe->minLight = (float) Reader_ReadInt32(reader) / 255.0f; + strobe->maxLight = (float) Reader_ReadInt32(reader) / 255.0f; + strobe->darkTime = Reader_ReadInt32(reader); + strobe->brightTime = Reader_ReadInt32(reader); + + strobe->thinker.function = (thinkfunc_t) T_StrobeFlash; + return true; // Add this thinker. +} + +static int readGlow(glow_t *glow, Reader *reader) +{ +/* Original DOOM format: +typedef struct { + thinker_t thinker; // was 12 bytes + Sector *sector; + int minlight; + int maxlight; + int direction; +} v19_glow_t; +*/ + // Padding at the start (an old thinker_t struct) + Reader_Read(reader, NULL, SIZEOF_V19_THINKER_T); + + // Start of used data members. + // A 32bit pointer to sector, serialized. + glow->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(reader)); + DENG_ASSERT(glow->sector != 0); + + glow->minLight = (float) Reader_ReadInt32(reader) / 255.0f; + glow->maxLight = (float) Reader_ReadInt32(reader) / 255.0f; + glow->direction = Reader_ReadInt32(reader); + + glow->thinker.function = (thinkfunc_t) T_Glow; + return true; // Add this thinker. +} + +static void SaveInfo_Read_Dm_v19(SaveInfo *info, Reader *reader) +{ + DENG_ASSERT(info != 0); + + char descBuf[24]; + Reader_Read(reader, descBuf, 24); + info->setUserDescription(de::String(descBuf, 24)); + + char vcheck[16 + 1]; + Reader_Read(reader, vcheck, 16); vcheck[16] = 0; + //DENG_ASSERT(!strncmp(vcheck, "version ", 8)); // Ensure save state format has been recognised by now. + info->setVersion(atoi(&vcheck[8])); + + GameRuleset rules; de::zap(rules); + rules.skill = (skillmode_t) Reader_ReadByte(reader); + // Interpret skill levels outside the normal range as "spawn no things". + if(rules.skill < SM_BABY || rules.skill >= NUM_SKILL_MODES) + { + rules.skill = SM_NOTHINGS; + } + info->setGameRules(rules); + + uint episode = Reader_ReadByte(reader) - 1; + uint map = Reader_ReadByte(reader) - 1; + info->setMapUri(G_ComposeMapUri(episode, map)); + + SaveInfo::Players players; de::zap(players); + for(int i = 0; i < 4; ++i) + { + players[i] = Reader_ReadByte(reader); + } + info->setPlayers(players); + + // Get the map time. + int a = Reader_ReadByte(reader); + int b = Reader_ReadByte(reader); + int c = Reader_ReadByte(reader); + info->setMapTime((a << 16) + (b << 8) + c); + + info->setMagic(0); // Initialize with *something*. + + /// @note Kludge: Assume the current game mode. + GameInfo gameInfo; + DD_GameInfo(&gameInfo); + info->setGameIdentityKey(Str_Text(gameInfo.identityKey)); + /// Kludge end. + + info->setSessionId(0); // None. +} + +static bool SV_OpenFile_Dm_v19(de::Path path) +{ + DENG_ASSERT(saveBuffer == 0); + if(!M_ReadFile(de::NativePath(path).expand().toUtf8().constData(), (char **)&saveBuffer)) + { + return false; + } + savePtr = saveBuffer; + return true; +} + +static void SV_CloseFile_Dm_v19() +{ + if(!saveBuffer) return; + Z_Free(saveBuffer); + saveBuffer = savePtr = 0; +} + +static Reader *SV_NewReader_Dm_v19() +{ + if(!saveBuffer) return 0; + return Reader_NewWithCallbacks(sri8, sri16, sri32, 0, srd); +} + +DENG2_PIMPL(DoomV9GameStateReader) +{ + Reader *reader; + + Instance(Public *i) + : Base(i) + , reader(0) + {} + + /// Assumes the reader is currently positioned at the start of the stream. + void seekToGameState() + { + // Read the header again. + /// @todo seek straight to the game state. + SaveInfo *tmp = new SaveInfo; + SaveInfo_Read_Dm_v19(tmp, reader); + delete tmp; + } + + void readPlayers() + { + for(int i = 0; i < 4; ++i) + { + if(!players[i].plr->inGame) continue; + + PADSAVEP(); + + readPlayer(players + i, reader); + + // will be set when unarc thinker + players[i].plr->mo = 0; + players[i].attacker = 0; + + for(int k = 0; k < NUMPSPRITES; ++k) + { + if(players[i].pSprites[k].state) + { + players[i].pSprites[k].state = + &STATES[PTR2INT(players[i].pSprites[k].state)]; + } + } + } + } + + void readMap() + { + // Do sectors. + for(int i = 0; i < numsectors; ++i) + { + Sector *sec = (Sector *)P_ToPtr(DMU_SECTOR, i); + xsector_t *xsec = P_ToXSector(sec); + + P_SetDoublep(sec, DMU_FLOOR_HEIGHT, (coord_t) Reader_ReadInt16(reader)); + P_SetDoublep(sec, DMU_CEILING_HEIGHT, (coord_t) Reader_ReadInt16(reader)); + + Uri *floorTextureUrn = readTextureUrn(reader, "Flats"); + P_SetPtrp (sec, DMU_FLOOR_MATERIAL, DD_MaterialForTextureUri(floorTextureUrn)); + Uri_Delete(floorTextureUrn); + + Uri *ceilingTextureUrn = readTextureUrn(reader, "Flats"); + P_SetPtrp (sec, DMU_CEILING_MATERIAL, DD_MaterialForTextureUri(ceilingTextureUrn)); + Uri_Delete(ceilingTextureUrn); + + P_SetFloatp(sec, DMU_LIGHT_LEVEL, (float) (Reader_ReadInt16(reader)) / 255.0f); + xsec->special = Reader_ReadInt16(reader); // needed? + /*xsec->tag = */Reader_ReadInt16(reader); // needed? + xsec->specialData = 0; + xsec->soundTarget = 0; + } + + // Do lines. + for(int i = 0; i < numlines; ++i) + { + Line *line = (Line *)P_ToPtr(DMU_LINE, i); + xline_t *xline = P_ToXLine(line); + + xline->flags = Reader_ReadInt16(reader); + xline->special = Reader_ReadInt16(reader); + /*xline->tag =*/Reader_ReadInt16(reader); + + for(int k = 0; k < 2; ++k) + { + Side *sdef = (Side *)P_GetPtrp(line, (k? DMU_BACK:DMU_FRONT)); + if(!sdef) continue; + + float matOffset[2]; + matOffset[VX] = (float) (Reader_ReadInt16(reader)); + matOffset[VY] = (float) (Reader_ReadInt16(reader)); + P_SetFloatpv(sdef, DMU_TOP_MATERIAL_OFFSET_XY, matOffset); + P_SetFloatpv(sdef, DMU_MIDDLE_MATERIAL_OFFSET_XY, matOffset); + P_SetFloatpv(sdef, DMU_BOTTOM_MATERIAL_OFFSET_XY, matOffset); + + Uri *topTextureUrn = readTextureUrn(reader, "Textures"); + P_SetPtrp(sdef, DMU_TOP_MATERIAL, DD_MaterialForTextureUri(topTextureUrn)); + Uri_Delete(topTextureUrn); + + Uri *bottomTextureUrn = readTextureUrn(reader, "Textures"); + P_SetPtrp(sdef, DMU_BOTTOM_MATERIAL, DD_MaterialForTextureUri(bottomTextureUrn)); + Uri_Delete(bottomTextureUrn); + + Uri *middleTextureUrn = readTextureUrn(reader, "Textures"); + P_SetPtrp(sdef, DMU_MIDDLE_MATERIAL, DD_MaterialForTextureUri(middleTextureUrn)); + Uri_Delete(middleTextureUrn); + } + } + + readThinkers(); + readSpecials(); + + if(Reader_ReadByte(reader) != 0x1d) + { + Reader_Delete(reader); reader = 0; + SV_CloseFile_Dm_v19(); + + throw ReadError("DoomV9GameStateReader", "Bad savegame (consistency test failed!)"); + } + } + + void readThinkers() + { + // Remove all the current thinkers. + Thinker_Iterate(NULL, removeThinker, NULL); + Thinker_Init(); + + // Read in saved thinkers. + byte tClass; + while((tClass = Reader_ReadByte(reader)) != 0 /*TC_END*/) + { + switch(tClass) + { + case 1: //TC_MOBJ + PADSAVEP(); + readMobj(reader); + break; + + default: + throw ReadError("DoomV9GameStateReader", "Unknown tclass #" + de::String::number(tClass) + "in savegame"); + } + } + } + + enum { + tc_ceiling, + tc_door, + tc_floor, + tc_plat, + tc_flash, + tc_strobe, + tc_glow, + tc_endspecials + }; + + /* + * Things to handle: + * + * T_MoveCeiling, (ceiling_t: Sector * swizzle), - active list + * T_Door, (door_t: Sector * swizzle), + * T_MoveFloor, (floor_t: Sector * swizzle), + * T_LightFlash, (lightflash_t: Sector * swizzle), + * T_StrobeFlash, (strobe_t: Sector *), + * T_Glow, (glow_t: Sector *), + * T_PlatRaise, (plat_t: Sector *), - active list + */ + void readSpecials() + { + byte tClass; + while((tClass = Reader_ReadByte(reader)) != tc_endspecials) + { + switch(tClass) + { + case tc_ceiling: { + PADSAVEP(); + ceiling_t *ceiling = (ceiling_t *)Z_Calloc(sizeof(*ceiling), PU_MAP, NULL); + + readCeiling(ceiling, reader); + + Thinker_Add(&ceiling->thinker); + break; } + + case tc_door: { + PADSAVEP(); + door_t *door = (door_t *)Z_Calloc(sizeof(*door), PU_MAP, NULL); + + readDoor(door, reader); + + Thinker_Add(&door->thinker); + break; } + + case tc_floor: { + PADSAVEP(); + floor_t *floor = (floor_t *)Z_Calloc(sizeof(*floor), PU_MAP, NULL); + + readFloor(floor, reader); + + Thinker_Add(&floor->thinker); + break; } + + case tc_plat: { + PADSAVEP(); + plat_t *plat = (plat_t *)Z_Calloc(sizeof(*plat), PU_MAP, NULL); + + readPlat(plat, reader); + + Thinker_Add(&plat->thinker); + break; } + + case tc_flash: { + PADSAVEP(); + lightflash_t *flash = (lightflash_t *)Z_Calloc(sizeof(*flash), PU_MAP, NULL); + + readFlash(flash, reader); + + Thinker_Add(&flash->thinker); + break; } + + case tc_strobe: { + PADSAVEP(); + strobe_t *strobe = (strobe_t *)Z_Calloc(sizeof(*strobe), PU_MAP, NULL); + + readStrobe(strobe, reader); + + Thinker_Add(&strobe->thinker); + break; } + + case tc_glow: { + PADSAVEP(); + glow_t *glow = (glow_t *)Z_Calloc(sizeof(*glow), PU_MAP, NULL); + + readGlow(glow, reader); + + Thinker_Add(&glow->thinker); + break; } + + default: + throw ReadError("DoomV9GameStateReader", "Unknown tclass #" + de::String::number(tClass) + "in savegame"); + } + } + } +}; + +DoomV9GameStateReader::DoomV9GameStateReader() : d(new Instance(this)) +{} + +DoomV9GameStateReader::~DoomV9GameStateReader() +{} + +bool DoomV9GameStateReader::recognize(SaveInfo &info) // static +{ + de::Path const path = SV_SavePath() / info.fileName(); + + if(!SV_ExistingFile(path)) return false; + if(!SV_OpenFile_Dm_v19(path)) return false; + + Reader *reader = SV_NewReader_Dm_v19(); + bool result = false; + + /// @todo Use the 'version' string as the "magic" identifier. + /*char vcheck[16]; + std::memset(vcheck, 0, sizeof(vcheck)); + Reader_Read(svReader, vcheck, sizeof(vcheck)); + + if(strncmp(vcheck, "version ", 8))*/ + { + SaveInfo_Read_Dm_v19(&info, reader); + result = (info.version() <= 500); + } + + Reader_Delete(reader); reader = 0; + SV_CloseFile_Dm_v19(); + + return result; +} + +IGameStateReader *DoomV9GameStateReader::make() +{ + return new DoomV9GameStateReader; +} + +void DoomV9GameStateReader::read(SaveInfo &info) +{ + de::Path const path = SV_SavePath() / info.fileName(); + + if(!SV_OpenFile_Dm_v19(path)) + { + throw FileAccessError("DoomV9GameStateReader", "Failed opening \"" + de::NativePath(path).pretty() + "\""); + } + + d->reader = SV_NewReader_Dm_v19(); + + d->seekToGameState(); + + /* + * Load the map and configure some game settings. + */ + briefDisabled = true; + G_NewGame(info.mapUri(), 0/*not saved??*/, &info.gameRules()); + G_SetGameAction(GA_NONE); /// @todo Necessary? + + // Recreate map state. + mapTime = info.mapTime(); + + d->readPlayers(); + d->readMap(); + + Reader_Delete(d->reader); d->reader = 0; + SV_CloseFile_Dm_v19(); + + // Material scrollers must be spawned. + P_SpawnAllMaterialOriginScrollers(); + + // Let the engine know where the local players are now. + for(int i = 0; i < MAXPLAYERS; ++i) + { + R_UpdateConsoleView(i); + } + + // Inform the engine that map setup must be performed once more. + R_SetupMap(0, 0); +} diff --git a/doomsday/plugins/doom/src/m_cheat.c b/doomsday/plugins/doom/src/m_cheat.c index 661276f315..380771ee2f 100644 --- a/doomsday/plugins/doom/src/m_cheat.c +++ b/doomsday/plugins/doom/src/m_cheat.c @@ -419,31 +419,30 @@ D_CMD(CheatGive) if(G_GameState() != GS_MAP) { - App_Log(DE2_SCR_ERROR, "Can only \"give\" when in a game!\n"); + App_Log(DE2_SCR_ERROR, "Can only \"give\" when in a game!"); return true; } if(argc != 2 && argc != 3) { - App_Log(DE2_SCR_NOTE, "Usage:\n give (stuff)\n"); - App_Log(DE2_LOG_SCR, " give (stuff) (plr)\n"); + App_Log(DE2_SCR_NOTE, "Usage:\n give (stuff)\n give (stuff) (plr)"); App_Log(DE2_LOG_SCR, "Stuff consists of one or more of (type:id). " - "If no id; give all of type:\n"); - App_Log(DE2_LOG_SCR, " a - ammo\n"); - App_Log(DE2_LOG_SCR, " b - berserk\n"); - App_Log(DE2_LOG_SCR, " f - the power of flight\n"); - App_Log(DE2_LOG_SCR, " g - light amplification visor\n"); - App_Log(DE2_LOG_SCR, " h - health\n"); - App_Log(DE2_LOG_SCR, " i - invulnerability\n"); - App_Log(DE2_LOG_SCR, " k - key cards/skulls\n"); - App_Log(DE2_LOG_SCR, " m - computer area map\n"); - App_Log(DE2_LOG_SCR, " p - backpack full of ammo\n"); - App_Log(DE2_LOG_SCR, " r - armor\n"); - App_Log(DE2_LOG_SCR, " s - radiation shielding suit\n"); - App_Log(DE2_LOG_SCR, " v - invisibility\n"); - App_Log(DE2_LOG_SCR, " w - weapons\n"); - App_Log(DE2_LOG_SCR, "Example: 'give arw' corresponds the cheat IDFA.\n"); - App_Log(DE2_LOG_SCR, "Example: 'give w2k1' gives weapon two and key one.\n"); + "If no id; give all of type:"); + App_Log(DE2_LOG_SCR, " a - ammo"); + App_Log(DE2_LOG_SCR, " b - berserk"); + App_Log(DE2_LOG_SCR, " f - the power of flight"); + App_Log(DE2_LOG_SCR, " g - light amplification visor"); + App_Log(DE2_LOG_SCR, " h - health"); + App_Log(DE2_LOG_SCR, " i - invulnerability"); + App_Log(DE2_LOG_SCR, " k - key cards/skulls"); + App_Log(DE2_LOG_SCR, " m - computer area map"); + App_Log(DE2_LOG_SCR, " p - backpack full of ammo"); + App_Log(DE2_LOG_SCR, " r - armor"); + App_Log(DE2_LOG_SCR, " s - radiation shielding suit"); + App_Log(DE2_LOG_SCR, " v - invisibility"); + App_Log(DE2_LOG_SCR, " w - weapons"); + App_Log(DE2_LOG_SCR, "Example: 'give arw' corresponds the cheat IDFA."); + App_Log(DE2_LOG_SCR, "Example: 'give w2k1' gives weapon two and key one."); return true; } @@ -492,7 +491,7 @@ D_CMD(CheatGive) i += end - &buf[i+1]; if(idx < AT_FIRST || idx >= NUM_AMMO_TYPES) { - App_Log(DE2_SCR_ERROR, "Unknown ammo #%d (valid range %d-%d)\n", + App_Log(DE2_SCR_ERROR, "Unknown ammo #%d (valid range %d-%d)", (int)idx, AT_FIRST, NUM_AMMO_TYPES-1); break; } @@ -539,7 +538,7 @@ D_CMD(CheatGive) i += end - &buf[i+1]; if(idx < KT_FIRST || idx >= NUM_KEY_TYPES) { - App_Log(DE2_SCR_ERROR, "Unknown key #%d (valid range %d-%d)\n", + App_Log(DE2_SCR_ERROR, "Unknown key #%d (valid range %d-%d)", (int)idx, KT_FIRST, NUM_KEY_TYPES-1); break; } @@ -576,7 +575,7 @@ D_CMD(CheatGive) i += end - &buf[i+1]; if(idx < 0 || idx >= 4) { - App_Log(DE2_SCR_ERROR, "Unknown armor type #%d (valid range %d-%d)\n", + App_Log(DE2_SCR_ERROR, "Unknown armor type #%d (valid range %d-%d)", (int)idx, 0, 4-1); break; } @@ -608,7 +607,7 @@ D_CMD(CheatGive) i += end - &buf[i+1]; if(idx < WT_FIRST || idx >= NUM_WEAPON_TYPES) { - App_Log(DE2_SCR_ERROR, "Unknown weapon #%d (valid range %d-%d)\n", + App_Log(DE2_SCR_ERROR, "Unknown weapon #%d (valid range %d-%d)", (int)idx, WT_FIRST, NUM_WEAPON_TYPES-1); break; } @@ -624,7 +623,7 @@ D_CMD(CheatGive) break; default: // Unrecognized. - App_Log(DE2_SCR_ERROR, "Cannot give '%c': unknown letter\n", buf[i]); + App_Log(DE2_SCR_ERROR, "Cannot give '%c': unknown letter", buf[i]); break; } } @@ -658,7 +657,7 @@ D_CMD(CheatMassacre) } else { - App_Log(DE2_LOG_MAP, "%i monsters killed\n", P_Massacre()); + App_Log(DE2_LOG_MAP, "%i monsters killed", P_Massacre()); } } return true; @@ -716,7 +715,7 @@ D_CMD(CheatLeaveMap) if(G_GameState() != GS_MAP) { S_LocalSound(SFX_OOF, NULL); - App_Log(DE2_LOG_MAP | DE2_LOG_ERROR, "Can only exit a map when in a game!\n"); + App_Log(DE2_LOG_MAP | DE2_LOG_ERROR, "Can only exit a map when in a game!"); return true; } diff --git a/doomsday/plugins/doom/src/p_enemy.c b/doomsday/plugins/doom/src/p_enemy.c index 5fec89c4cd..8d296885e5 100644 --- a/doomsday/plugins/doom/src/p_enemy.c +++ b/doomsday/plugins/doom/src/p_enemy.c @@ -1794,7 +1794,7 @@ void C_DECL A_BrainDie(mobj_t* mo) void C_DECL A_BrainSpit(mobj_t* mo) { - mobj_t *targ = BossBrain_NextTarget(bossBrain); + mobj_t *targ = BossBrain_NextTarget(theBossBrain); mobj_t *newmobj; if(!targ) return; diff --git a/doomsday/plugins/doom/src/p_mobj.c b/doomsday/plugins/doom/src/p_mobj.c index 6beee1ee87..eb24ada4eb 100644 --- a/doomsday/plugins/doom/src/p_mobj.c +++ b/doomsday/plugins/doom/src/p_mobj.c @@ -811,7 +811,7 @@ mobj_t* P_SpawnMobjXYZ(mobjtype_t type, coord_t x, coord_t y, coord_t z, angle_t if(type == MT_BOSSTARGET) { - BossBrain_AddTarget(bossBrain, mo); + BossBrain_AddTarget(theBossBrain, mo); } // Copy spawn attributes to the new mobj. diff --git a/doomsday/plugins/doom/src/p_oldsvg.cpp b/doomsday/plugins/doom/src/p_oldsvg.cpp deleted file mode 100644 index 002b3ccd69..0000000000 --- a/doomsday/plugins/doom/src/p_oldsvg.cpp +++ /dev/null @@ -1,999 +0,0 @@ -/** @file p_oldsvg.cpp Doom ver 1.9 save game reader. - * - * @authors Copyright © 2003-2013 Jaakko Keränen - * @authors Copyright © 2005-2013 Daniel Swanson - * @authors Copyright © 1993-1996 id Software, Inc. - * - * @par License - * GPL: http://www.gnu.org/licenses/gpl.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. This program is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General - * Public License for more details. You should have received a copy of the GNU - * General Public License along with this program; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include "jdoom.h" -#include "p_oldsvg.h" - -#include "am_map.h" -#include "dmu_lib.h" -#include "p_ceiling.h" -#include "p_door.h" -#include "p_floor.h" -#include "p_map.h" -#include "p_mapsetup.h" -#include "p_plat.h" -#include "p_saveio.h" -#include "p_saveg.h" -#include "p_tick.h" -#include "r_common.h" // R_UpdateConsoleView -#include - -#define PADSAVEP() savePtr += (4 - ((savePtr - saveBuffer) & 3)) & 3 - -// All the versions of DOOM have different savegame IDs, but 500 will be the -// savegame base from now on. -#define V19_SAVE_VERSION 500 ///< Version number associated with a recognised doom.exe game save state. -#define V19_SAVESTRINGSIZE 24 -#define VERSIONSIZE 16 - -#define FF_FULLBRIGHT 0x8000 ///< Used to be a flag in thing->frame. -#define FF_FRAMEMASK 0x7fff - -#define SIZEOF_V19_THINKER_T 12 -#define V19_THINKER_T_FUNC_OFFSET 8 - -static byte *savePtr; -static byte *saveBuffer; -static Reader *svReader; - -static dd_bool SV_OpenFile_Dm_v19(char const *filePath); -static void SV_CloseFile_Dm_v19(); -static Reader *SV_NewReader_Dm_v19(); - -static void SaveInfo_Read_Dm_v19(SaveInfo *info, Reader *reader); - -static char sri8(Reader *r) -{ - if(!r) return 0; - savePtr++; - return *(char *) (savePtr - 1); -} - -static short sri16(Reader *r) -{ - if(!r) return 0; - savePtr += 2; - return *(int16_t *) (savePtr - 2); -} - -static int sri32(Reader *r) -{ - if(!r) return 0; - savePtr += 4; - return *(int32_t *) (savePtr - 4); -} - -static void srd(Reader *r, char *data, int len) -{ - if(!r) return; - if(data) - { - memcpy(data, savePtr, len); - } - savePtr += len; -} - -static void SV_v19_ReadPlayer(player_t *pl) -{ - Reader_ReadInt32(svReader); - - pl->playerState = playerstate_t(Reader_ReadInt32(svReader)); - - Reader_Read(svReader, NULL, 8); - - pl->viewZ = FIX2FLT(Reader_ReadInt32(svReader)); - pl->viewHeight = FIX2FLT(Reader_ReadInt32(svReader)); - pl->viewHeightDelta = FIX2FLT(Reader_ReadInt32(svReader)); - pl->bob = FLT2FIX(Reader_ReadInt32(svReader)); - pl->flyHeight = 0; - pl->health = Reader_ReadInt32(svReader); - pl->armorPoints = Reader_ReadInt32(svReader); - pl->armorType = Reader_ReadInt32(svReader); - - memset(pl->powers, 0, sizeof(pl->powers)); - pl->powers[PT_INVULNERABILITY] = Reader_ReadInt32(svReader); - pl->powers[PT_STRENGTH] = Reader_ReadInt32(svReader); - pl->powers[PT_INVISIBILITY] = Reader_ReadInt32(svReader); - pl->powers[PT_IRONFEET] = Reader_ReadInt32(svReader); - pl->powers[PT_ALLMAP] = Reader_ReadInt32(svReader); - if(pl->powers[PT_ALLMAP]) - ST_RevealAutomap(pl - players, true); - pl->powers[PT_INFRARED] = Reader_ReadInt32(svReader); - - memset(pl->keys, 0, sizeof(pl->keys)); - pl->keys[KT_BLUECARD] = !!Reader_ReadInt32(svReader); - pl->keys[KT_YELLOWCARD] = !!Reader_ReadInt32(svReader); - pl->keys[KT_REDCARD] = !!Reader_ReadInt32(svReader); - pl->keys[KT_BLUESKULL] = !!Reader_ReadInt32(svReader); - pl->keys[KT_YELLOWSKULL] = !!Reader_ReadInt32(svReader); - pl->keys[KT_REDSKULL] = !!Reader_ReadInt32(svReader); - - pl->backpack = Reader_ReadInt32(svReader); - - memset(pl->frags, 0, sizeof(pl->frags)); - pl->frags[0] = Reader_ReadInt32(svReader); - pl->frags[1] = Reader_ReadInt32(svReader); - pl->frags[2] = Reader_ReadInt32(svReader); - pl->frags[3] = Reader_ReadInt32(svReader); - - pl->readyWeapon = weapontype_t(Reader_ReadInt32(svReader)); - pl->pendingWeapon = weapontype_t(Reader_ReadInt32(svReader)); - - memset(pl->weapons, 0, sizeof(pl->weapons)); - pl->weapons[WT_FIRST].owned = !!Reader_ReadInt32(svReader); - pl->weapons[WT_SECOND].owned = !!Reader_ReadInt32(svReader); - pl->weapons[WT_THIRD].owned = !!Reader_ReadInt32(svReader); - pl->weapons[WT_FOURTH].owned = !!Reader_ReadInt32(svReader); - pl->weapons[WT_FIFTH].owned = !!Reader_ReadInt32(svReader); - pl->weapons[WT_SIXTH].owned = !!Reader_ReadInt32(svReader); - pl->weapons[WT_SEVENTH].owned = !!Reader_ReadInt32(svReader); - pl->weapons[WT_EIGHTH].owned = !!Reader_ReadInt32(svReader); - pl->weapons[WT_NINETH].owned = !!Reader_ReadInt32(svReader); - - memset(pl->ammo, 0, sizeof(pl->ammo)); - pl->ammo[AT_CLIP].owned = Reader_ReadInt32(svReader); - pl->ammo[AT_SHELL].owned = Reader_ReadInt32(svReader); - pl->ammo[AT_CELL].owned = Reader_ReadInt32(svReader); - pl->ammo[AT_MISSILE].owned = Reader_ReadInt32(svReader); - - pl->ammo[AT_CLIP].max = Reader_ReadInt32(svReader); - pl->ammo[AT_SHELL].max = Reader_ReadInt32(svReader); - pl->ammo[AT_CELL].max = Reader_ReadInt32(svReader); - pl->ammo[AT_MISSILE].max = Reader_ReadInt32(svReader); - - pl->attackDown = Reader_ReadInt32(svReader); - pl->useDown = Reader_ReadInt32(svReader); - - pl->cheats = Reader_ReadInt32(svReader); - pl->refire = Reader_ReadInt32(svReader); - - pl->killCount = Reader_ReadInt32(svReader); - pl->itemCount = Reader_ReadInt32(svReader); - pl->secretCount = Reader_ReadInt32(svReader); - - Reader_ReadInt32(svReader); - - pl->damageCount = Reader_ReadInt32(svReader); - pl->bonusCount = Reader_ReadInt32(svReader); - - Reader_ReadInt32(svReader); - - pl->plr->extraLight = Reader_ReadInt32(svReader); - pl->plr->fixedColorMap = Reader_ReadInt32(svReader); - - pl->colorMap = Reader_ReadInt32(svReader); - - for(int i = 0; i < 2; ++i) - { - pspdef_t *psp = &pl->pSprites[i]; - - psp->state = INT2PTR(state_t, Reader_ReadInt32(svReader)); - psp->tics = Reader_ReadInt32(svReader); - psp->pos[VX] = FIX2FLT(Reader_ReadInt32(svReader)); - psp->pos[VY] = FIX2FLT(Reader_ReadInt32(svReader)); - } - - pl->didSecret = !!Reader_ReadInt32(svReader); -} - -static void SV_v19_ReadMobj() -{ - // List: thinker links. - Reader_ReadInt32(svReader); - Reader_ReadInt32(svReader); - Reader_ReadInt32(svReader); - - // Info for drawing: position. - coord_t pos[3]; - pos[VX] = FIX2FLT(Reader_ReadInt32(svReader)); - pos[VY] = FIX2FLT(Reader_ReadInt32(svReader)); - pos[VZ] = FIX2FLT(Reader_ReadInt32(svReader)); - - // More list: links in sector (if needed) - Reader_ReadInt32(svReader); - Reader_ReadInt32(svReader); - - //More drawing info: to determine current sprite. - angle_t angle = Reader_ReadInt32(svReader); // orientation - spritenum_t sprite = Reader_ReadInt32(svReader); // used to find patch_t and flip value - - int frame = Reader_ReadInt32(svReader); // might be ORed with FF_FULLBRIGHT - if(frame & FF_FULLBRIGHT) - frame &= FF_FRAMEMASK; // not used anymore. - - // Interaction info, by BLOCKMAP. - // Links in blocks (if needed). - Reader_ReadInt32(svReader); - Reader_ReadInt32(svReader); - Reader_ReadInt32(svReader); - - // The closest interval over all contacted Sectors. - coord_t floorz = FIX2FLT(Reader_ReadInt32(svReader)); - coord_t ceilingz = FIX2FLT(Reader_ReadInt32(svReader)); - - // For movement checking. - coord_t radius = FIX2FLT(Reader_ReadInt32(svReader)); - coord_t height = FIX2FLT(Reader_ReadInt32(svReader)); - - // Momentums, used to update position. - coord_t mom[3]; - mom[MX] = FIX2FLT(Reader_ReadInt32(svReader)); - mom[MY] = FIX2FLT(Reader_ReadInt32(svReader)); - mom[MZ] = FIX2FLT(Reader_ReadInt32(svReader)); - - int valid = Reader_ReadInt32(svReader); - int type = Reader_ReadInt32(svReader); - mobjinfo_t *info = &MOBJINFO[type]; - - int ddflags = 0; - if(info->flags & MF_SOLID) ddflags |= DDMF_SOLID; - if(info->flags2 & MF2_DONTDRAW) ddflags |= DDMF_DONTDRAW; - - /* - * We now have all the information we need to create the mobj. - */ - mobj_t *mo = Mobj_CreateXYZ(P_MobjThinker, pos[VX], pos[VY], pos[VZ], angle, - radius, height, ddflags); - - mo->sprite = sprite; - mo->frame = frame; - mo->floorZ = floorz; - mo->ceilingZ = ceilingz; - mo->mom[MX] = mom[MX]; - mo->mom[MY] = mom[MY]; - mo->mom[MZ] = mom[MZ]; - mo->valid = valid; - mo->type = type; - mo->moveDir = DI_NODIR; - - Reader_ReadInt32(svReader); // &mobjinfo[mo->type] - - mo->tics = Reader_ReadInt32(svReader); // state tic counter - mo->state = INT2PTR(state_t, Reader_ReadInt32(svReader)); - mo->damage = DDMAXINT; // Use damage set in mo->info->damage - mo->flags = Reader_ReadInt32(svReader); - mo->health = Reader_ReadInt32(svReader); - - // Movement direction, movement generation (zig-zagging). - mo->moveDir = Reader_ReadInt32(svReader); // 0-7 - mo->moveCount = Reader_ReadInt32(svReader); // when 0, select a new dir - - // Thing being chased/attacked (or NULL), - // also the originator for missiles. - Reader_ReadInt32(svReader); - - // Reaction time: if non 0, don't attack yet. - // Used by player to freeze a bit after teleporting. - mo->reactionTime = Reader_ReadInt32(svReader); - - // If >0, the target will be chased - // no matter what (even if shot) - mo->threshold = Reader_ReadInt32(svReader); - - // Additional info record for player avatars only. - // Only valid if type == MT_PLAYER - mo->player = INT2PTR(player_t, Reader_ReadInt32(svReader)); - - // Player number last looked for. - mo->lastLook = Reader_ReadInt32(svReader); - - // For nightmare respawn. - mo->spawnSpot.origin[VX] = (float) Reader_ReadInt16(svReader); - mo->spawnSpot.origin[VY] = (float) Reader_ReadInt16(svReader); - mo->spawnSpot.origin[VZ] = 0; // Initialize with "something". - mo->spawnSpot.angle = (angle_t) (ANG45 * ((int)Reader_ReadInt16(svReader) / 45)); - /* mo->spawnSpot.type = (int) */ Reader_ReadInt16(svReader); - - int spawnFlags = ((int) Reader_ReadInt16(svReader)) & ~MASK_UNKNOWN_MSF_FLAGS; - // Spawn on the floor by default unless the mobjtype flags override. - spawnFlags |= MSF_Z_FLOOR; - mo->spawnSpot.flags = spawnFlags; - - // Thing being chased/attacked for tracers. - Reader_ReadInt32(svReader); - - mo->info = info; - SV_TranslateLegacyMobjFlags(mo, 0); - - mo->state = &STATES[PTR2INT(mo->state)]; - mo->target = NULL; - if(mo->player) - { - int pnum = PTR2INT(mo->player) - 1; - - mo->player = &players[pnum]; - mo->dPlayer = mo->player->plr; - mo->dPlayer->mo = mo; - //mo->dPlayer->clAngle = mo->angle; /* $unifiedangles */ - mo->dPlayer->lookDir = 0; /* $unifiedangles */ - } - P_MobjLink(mo); - mo->floorZ = P_GetDoublep(Mobj_Sector(mo), DMU_FLOOR_HEIGHT); - mo->ceilingZ = P_GetDoublep(Mobj_Sector(mo), DMU_CEILING_HEIGHT); -} - -static void P_v19_UnArchivePlayers() -{ - for(int i = 0; i < 4; ++i) - { - if(!players[i].plr->inGame) continue; - - PADSAVEP(); - - SV_v19_ReadPlayer(players + i); - - // will be set when unarc thinker - players[i].plr->mo = NULL; - players[i].attacker = NULL; - - for(int k = 0; k < NUMPSPRITES; ++k) - { - if(players[i].pSprites[k].state) - { - players[i].pSprites[k].state = - &STATES[PTR2INT(players[i].pSprites[k].state)]; - } - } - } -} - -static Uri *readTextureUrn(Reader *reader, char const *schemeName) -{ - DENG_ASSERT(reader != 0 && schemeName != 0); - DENG_UNUSED(reader); - return Uri_NewWithPath2(Str_Text(Str_Appendf(AutoStr_NewStd(), "urn:%s:%i", schemeName, Reader_ReadInt16(svReader))), RC_NULL); -} - -static void P_v19_UnArchiveWorld() -{ - // Do sectors. - for(int i = 0; i < numsectors; ++i) - { - Sector *sec = (Sector *)P_ToPtr(DMU_SECTOR, i); - xsector_t *xsec = P_ToXSector(sec); - - P_SetDoublep(sec, DMU_FLOOR_HEIGHT, (coord_t) Reader_ReadInt16(svReader)); - P_SetDoublep(sec, DMU_CEILING_HEIGHT, (coord_t) Reader_ReadInt16(svReader)); - - Uri *floorTextureUrn = readTextureUrn(svReader, "Flats"); - P_SetPtrp (sec, DMU_FLOOR_MATERIAL, DD_MaterialForTextureUri(floorTextureUrn)); - Uri_Delete(floorTextureUrn); - - Uri *ceilingTextureUrn = readTextureUrn(svReader, "Flats"); - P_SetPtrp (sec, DMU_CEILING_MATERIAL, DD_MaterialForTextureUri(ceilingTextureUrn)); - Uri_Delete(ceilingTextureUrn); - - P_SetFloatp(sec, DMU_LIGHT_LEVEL, (float) (Reader_ReadInt16(svReader)) / 255.0f); - xsec->special = Reader_ReadInt16(svReader); // needed? - /*xsec->tag = */Reader_ReadInt16(svReader); // needed? - xsec->specialData = 0; - xsec->soundTarget = 0; - } - - // Do lines. - for(int i = 0; i < numlines; ++i) - { - Line *line = (Line *)P_ToPtr(DMU_LINE, i); - xline_t *xline = P_ToXLine(line); - - xline->flags = Reader_ReadInt16(svReader); - xline->special = Reader_ReadInt16(svReader); - /*xline->tag =*/Reader_ReadInt16(svReader); - - for(int k = 0; k < 2; ++k) - { - Side *sdef = (Side *)P_GetPtrp(line, (k? DMU_BACK:DMU_FRONT)); - if(!sdef) continue; - - float matOffset[2]; - matOffset[VX] = (float) (Reader_ReadInt16(svReader)); - matOffset[VY] = (float) (Reader_ReadInt16(svReader)); - P_SetFloatpv(sdef, DMU_TOP_MATERIAL_OFFSET_XY, matOffset); - P_SetFloatpv(sdef, DMU_MIDDLE_MATERIAL_OFFSET_XY, matOffset); - P_SetFloatpv(sdef, DMU_BOTTOM_MATERIAL_OFFSET_XY, matOffset); - - Uri *topTextureUrn = readTextureUrn(svReader, "Textures"); - P_SetPtrp(sdef, DMU_TOP_MATERIAL, DD_MaterialForTextureUri(topTextureUrn)); - Uri_Delete(topTextureUrn); - - Uri *bottomTextureUrn = readTextureUrn(svReader, "Textures"); - P_SetPtrp(sdef, DMU_BOTTOM_MATERIAL, DD_MaterialForTextureUri(bottomTextureUrn)); - Uri_Delete(bottomTextureUrn); - - Uri *middleTextureUrn = readTextureUrn(svReader, "Textures"); - P_SetPtrp(sdef, DMU_MIDDLE_MATERIAL, DD_MaterialForTextureUri(middleTextureUrn)); - Uri_Delete(middleTextureUrn); - } - } -} - -static int removeThinker(thinker_t *th, void * /*context*/) -{ - if(th->function == (thinkfunc_t) P_MobjThinker) - { - P_MobjRemove((mobj_t *) th, true); - } - else - { - Z_Free(th); - } - - return false; // Continue iteration. -} - -static void P_v19_UnArchiveThinkers() -{ - // Remove all the current thinkers. - Thinker_Iterate(NULL, removeThinker, NULL); - Thinker_Init(); - - // Read in saved thinkers. - byte tClass; - while((tClass = Reader_ReadByte(svReader)) != 0 /*TC_END*/) - { - switch(tClass) - { - case 1: //TC_MOBJ - PADSAVEP(); - SV_v19_ReadMobj(); - break; - - default: - Con_Error("Unknown tclass %i in savegame", tClass); - } - } -} - -static int SV_v19_ReadCeiling(ceiling_t *ceiling) -{ -/* Original DOOM format: -typedef struct { - thinker_t thinker; // was 12 bytes - ceilingtype_e type; // was 32bit int - Sector *sector; - fixed_t bottomheight; - fixed_t topheight; - fixed_t speed; - dd_bool crush; - int direction; - int tag; - int olddirection; -} v19_ceiling_t; -*/ - byte temp[SIZEOF_V19_THINKER_T]; - - // Padding at the start (an old thinker_t struct). - Reader_Read(svReader, &temp, SIZEOF_V19_THINKER_T); - - // Start of used data members. - ceiling->type = ceilingtype_e(Reader_ReadInt32(svReader)); - - // A 32bit pointer to sector, serialized. - ceiling->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(svReader)); - if(!ceiling->sector) - Con_Error("tc_ceiling: bad sector number\n"); - - ceiling->bottomHeight = FIX2FLT(Reader_ReadInt32(svReader)); - ceiling->topHeight = FIX2FLT(Reader_ReadInt32(svReader)); - ceiling->speed = FIX2FLT(Reader_ReadInt32(svReader)); - ceiling->crush = Reader_ReadInt32(svReader); - ceiling->state = (Reader_ReadInt32(svReader) == -1? CS_DOWN : CS_UP); - ceiling->tag = Reader_ReadInt32(svReader); - ceiling->oldState = (Reader_ReadInt32(svReader) == -1? CS_DOWN : CS_UP); - - ceiling->thinker.function = T_MoveCeiling; - if(!(temp + V19_THINKER_T_FUNC_OFFSET)) - { - Thinker_SetStasis(&ceiling->thinker, true); - } - - P_ToXSector(ceiling->sector)->specialData = ceiling; - return true; // Add this thinker. -} - -static int SV_v19_ReadDoor(door_t *door) -{ -/* Original DOOM format: -typedef struct { - thinker_t thinker; // was 12 bytes - doortype_e type; // was 32bit int - Sector *sector; - fixed_t topheight; - fixed_t speed; - int direction; - int topwait; - int topcountdown; -} v19_vldoor_t; -*/ - // Padding at the start (an old thinker_t struct) - Reader_Read(svReader, NULL, SIZEOF_V19_THINKER_T); - - // Start of used data members. - door->type = doortype_e(Reader_ReadInt32(svReader)); - - // A 32bit pointer to sector, serialized. - door->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(svReader)); - if(!door->sector) - Con_Error("tc_door: bad sector number\n"); - - door->topHeight = FIX2FLT(Reader_ReadInt32(svReader)); - door->speed = FIX2FLT(Reader_ReadInt32(svReader)); - door->state = doorstate_e(Reader_ReadInt32(svReader)); - door->topWait = Reader_ReadInt32(svReader); - door->topCountDown = Reader_ReadInt32(svReader); - - door->thinker.function = T_Door; - - P_ToXSector(door->sector)->specialData = door; - return true; // Add this thinker. -} - -static int SV_v19_ReadFloor(floor_t *floor) -{ -/* Original DOOM format: -typedef struct { - thinker_t thinker; // was 12 bytes - floortype_e type; // was 32bit int - dd_bool crush; - Sector *sector; - int direction; - int newspecial; - short texture; - fixed_t floordestheight; - fixed_t speed; -} v19_floormove_t; -*/ - - // Padding at the start (an old thinker_t struct) - Reader_Read(svReader, NULL, SIZEOF_V19_THINKER_T); - - // Start of used data members. - floor->type = floortype_e(Reader_ReadInt32(svReader)); - floor->crush = Reader_ReadInt32(svReader); - - // A 32bit pointer to sector, serialized. - floor->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(svReader)); - if(!floor->sector) - Con_Error("tc_floor: bad sector number\n"); - - floor->state = floorstate_e(Reader_ReadInt32(svReader)); - floor->newSpecial = Reader_ReadInt32(svReader); - - Uri *newTextureUrn = readTextureUrn(svReader, "Flats"); - floor->material = DD_MaterialForTextureUri(newTextureUrn); - Uri_Delete(newTextureUrn); - - floor->floorDestHeight = FIX2FLT(Reader_ReadInt32(svReader)); - floor->speed = FIX2FLT(Reader_ReadInt32(svReader)); - - floor->thinker.function = T_MoveFloor; - - P_ToXSector(floor->sector)->specialData = floor; - return true; // Add this thinker. -} - -static int SV_v19_ReadPlat(plat_t *plat) -{ -/* Original DOOM format: -typedef struct { - thinker_t thinker; // was 12 bytes - Sector *sector; - fixed_t speed; - fixed_t low; - fixed_t high; - int wait; - int count; - platstate_e status; // was 32bit int - platstate_e oldstatus; // was 32bit int - dd_bool crush; - int tag; - plattype_e type; // was 32bit int -} v19_plat_t; -*/ - byte temp[SIZEOF_V19_THINKER_T]; - - // Padding at the start (an old thinker_t struct) - Reader_Read(svReader, temp, SIZEOF_V19_THINKER_T); - - // Start of used data members. - // A 32bit pointer to sector, serialized. - plat->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(svReader)); - if(!plat->sector) - Con_Error("tc_plat: bad sector number\n"); - - plat->speed = FIX2FLT(Reader_ReadInt32(svReader)); - plat->low = FIX2FLT(Reader_ReadInt32(svReader)); - plat->high = FIX2FLT(Reader_ReadInt32(svReader)); - plat->wait = Reader_ReadInt32(svReader); - plat->count = Reader_ReadInt32(svReader); - plat->state = platstate_e(Reader_ReadInt32(svReader)); - plat->oldState = platstate_e(Reader_ReadInt32(svReader)); - plat->crush = Reader_ReadInt32(svReader); - plat->tag = Reader_ReadInt32(svReader); - plat->type = plattype_e(Reader_ReadInt32(svReader)); - - plat->thinker.function = T_PlatRaise; - if(!(temp + V19_THINKER_T_FUNC_OFFSET)) - { - Thinker_SetStasis(&plat->thinker, true); - } - - P_ToXSector(plat->sector)->specialData = plat; - return true; // Add this thinker. -} - -static int SV_v19_ReadFlash(lightflash_t *flash) -{ -/* Original DOOM format: -typedef struct { - thinker_t thinker; // was 12 bytes - Sector *sector; - int count; - int maxlight; - int minlight; - int maxtime; - int mintime; -} v19_lightflash_t; -*/ - // Padding at the start (an old thinker_t struct) - Reader_Read(svReader, NULL, SIZEOF_V19_THINKER_T); - - // Start of used data members. - // A 32bit pointer to sector, serialized. - flash->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(svReader)); - if(!flash->sector) - Con_Error("tc_flash: bad sector number\n"); - - flash->count = Reader_ReadInt32(svReader); - flash->maxLight = (float) Reader_ReadInt32(svReader) / 255.0f; - flash->minLight = (float) Reader_ReadInt32(svReader) / 255.0f; - flash->maxTime = Reader_ReadInt32(svReader); - flash->minTime = Reader_ReadInt32(svReader); - - flash->thinker.function = (thinkfunc_t) T_LightFlash; - return true; // Add this thinker. -} - -static int SV_v19_ReadStrobe(strobe_t *strobe) -{ -/* Original DOOM format: -typedef struct { - thinker_t thinker; // was 12 bytes - Sector *sector; - int count; - int minlight; - int maxlight; - int darktime; - int brighttime; -} v19_strobe_t; -*/ - // Padding at the start (an old thinker_t struct) - Reader_Read(svReader, NULL, SIZEOF_V19_THINKER_T); - - // Start of used data members. - // A 32bit pointer to sector, serialized. - strobe->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(svReader)); - if(!strobe->sector) - Con_Error("tc_strobe: bad sector number\n"); - - strobe->count = Reader_ReadInt32(svReader); - strobe->minLight = (float) Reader_ReadInt32(svReader) / 255.0f; - strobe->maxLight = (float) Reader_ReadInt32(svReader) / 255.0f; - strobe->darkTime = Reader_ReadInt32(svReader); - strobe->brightTime = Reader_ReadInt32(svReader); - - strobe->thinker.function = (thinkfunc_t) T_StrobeFlash; - return true; // Add this thinker. -} - -static int SV_v19_ReadGlow(glow_t *glow) -{ -/* Original DOOM format: -typedef struct { - thinker_t thinker; // was 12 bytes - Sector *sector; - int minlight; - int maxlight; - int direction; -} v19_glow_t; -*/ - // Padding at the start (an old thinker_t struct) - Reader_Read(svReader, NULL, SIZEOF_V19_THINKER_T); - - // Start of used data members. - // A 32bit pointer to sector, serialized. - glow->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(svReader)); - if(!glow->sector) - Con_Error("tc_glow: bad sector number\n"); - - glow->minLight = (float) Reader_ReadInt32(svReader) / 255.0f; - glow->maxLight = (float) Reader_ReadInt32(svReader) / 255.0f; - glow->direction = Reader_ReadInt32(svReader); - - glow->thinker.function = (thinkfunc_t) T_Glow; - return true; // Add this thinker. -} - -enum { - tc_ceiling, - tc_door, - tc_floor, - tc_plat, - tc_flash, - tc_strobe, - tc_glow, - tc_endspecials -}; - -/* - * Things to handle: - * - * T_MoveCeiling, (ceiling_t: Sector * swizzle), - active list - * T_Door, (door_t: Sector * swizzle), - * T_MoveFloor, (floor_t: Sector * swizzle), - * T_LightFlash, (lightflash_t: Sector * swizzle), - * T_StrobeFlash, (strobe_t: Sector *), - * T_Glow, (glow_t: Sector *), - * T_PlatRaise, (plat_t: Sector *), - active list - */ -static void P_v19_UnArchiveSpecials() -{ - byte tClass; - while((tClass = Reader_ReadByte(svReader)) != tc_endspecials) - { - switch(tClass) - { - case tc_ceiling: { - PADSAVEP(); - ceiling_t *ceiling = (ceiling_t *)Z_Calloc(sizeof(*ceiling), PU_MAP, NULL); - - SV_v19_ReadCeiling(ceiling); - - Thinker_Add(&ceiling->thinker); - break; } - - case tc_door: { - PADSAVEP(); - door_t *door = (door_t *)Z_Calloc(sizeof(*door), PU_MAP, NULL); - - SV_v19_ReadDoor(door); - - Thinker_Add(&door->thinker); - break; } - - case tc_floor: { - PADSAVEP(); - floor_t *floor = (floor_t *)Z_Calloc(sizeof(*floor), PU_MAP, NULL); - - SV_v19_ReadFloor(floor); - - Thinker_Add(&floor->thinker); - break; } - - case tc_plat: { - PADSAVEP(); - plat_t *plat = (plat_t *)Z_Calloc(sizeof(*plat), PU_MAP, NULL); - - SV_v19_ReadPlat(plat); - - Thinker_Add(&plat->thinker); - break; } - - case tc_flash: { - PADSAVEP(); - lightflash_t *flash = (lightflash_t *)Z_Calloc(sizeof(*flash), PU_MAP, NULL); - - SV_v19_ReadFlash(flash); - - Thinker_Add(&flash->thinker); - break; } - - case tc_strobe: { - PADSAVEP(); - strobe_t *strobe = (strobe_t *)Z_Calloc(sizeof(*strobe), PU_MAP, NULL); - - SV_v19_ReadStrobe(strobe); - - Thinker_Add(&strobe->thinker); - break; } - - case tc_glow: { - PADSAVEP(); - glow_t *glow = (glow_t *)Z_Calloc(sizeof(*glow), PU_MAP, NULL); - - SV_v19_ReadGlow(glow); - - Thinker_Add(&glow->thinker); - break; } - - default: - Con_Error("P_UnarchiveSpecials: Unknown tclass %i in savegame", tClass); - } - } -} - -static void SaveInfo_Read_Dm_v19(SaveInfo *info, Reader *reader) -{ - DENG_ASSERT(info != 0); - - char nameBuffer[V19_SAVESTRINGSIZE]; - Reader_Read(reader, nameBuffer, V19_SAVESTRINGSIZE); - nameBuffer[V19_SAVESTRINGSIZE - 1] = 0; - Str_Set(&info->_description, nameBuffer); - - char vcheck[VERSIONSIZE]; - Reader_Read(reader, vcheck, VERSIONSIZE); - //DENG_ASSERT(!strncmp(vcheck, "version ", 8)); // Ensure save state format has been recognised by now. - - info->_version = atoi(&vcheck[8]); - - info->_gameRules.skill = (skillmode_t) Reader_ReadByte(reader); - // Interpret skill levels outside the normal range as "spawn no things". - if(info->_gameRules.skill < SM_BABY || info->_gameRules.skill >= NUM_SKILL_MODES) - { - info->_gameRules.skill = SM_NOTHINGS; - } - - uint episode = Reader_ReadByte(reader) - 1; - uint map = Reader_ReadByte(reader) - 1; - Uri_Copy(info->_mapUri, G_ComposeMapUri(episode, map)); - - for(int i = 0; i < 4; ++i) - { - info->_players[i] = Reader_ReadByte(reader); - } - - memset(&info->_players[4], 0, sizeof(*info->_players) * (MAXPLAYERS-4)); - - // Get the map time. - int a = Reader_ReadByte(reader); - int b = Reader_ReadByte(reader); - int c = Reader_ReadByte(reader); - info->_mapTime = (a << 16) + (b << 8) + c; - - info->_magic = 0; // Initialize with *something*. - - /// @note Older formats do not contain all needed values: - info->_gameMode = gameMode; // Assume the current mode. - - info->_gameRules.deathmatch = 0; - info->_gameRules.noMonsters = 0; - info->_gameRules.respawnMonsters = 0; - - info->_sessionId = 0; // None. -} - -static dd_bool SV_OpenFile_Dm_v19(char const *filePath) -{ - dd_bool fileOpened; -#if _DEBUG - if(saveBuffer) - Con_Error("SV_OpenFile_Dm_v19: A save state file has already been opened!"); -#endif - fileOpened = 0 != M_ReadFile(filePath, (char **)&saveBuffer); - if(!fileOpened) return false; - savePtr = saveBuffer; - return true; -} - -static void SV_CloseFile_Dm_v19() -{ - if(!saveBuffer) return; - Z_Free(saveBuffer); - saveBuffer = savePtr = 0; -} - -static Reader *SV_NewReader_Dm_v19() -{ - if(!saveBuffer) return 0; - return Reader_NewWithCallbacks(sri8, sri16, sri32, NULL, srd); -} - -bool DoomV9GameStateReader::recognize(SaveInfo *info, Str const *path) // static -{ - DENG_ASSERT(info != 0 && path != 0); - - if(!SV_ExistingFile(path)) return false; - - if(SV_OpenFile_Dm_v19(Str_Text(path))) - { - Reader *svReader = SV_NewReader_Dm_v19(); - bool result = false; - - /// @todo Use the 'version' string as the "magic" identifier. - /*char vcheck[VERSIONSIZE]; - memset(vcheck, 0, sizeof(vcheck)); - Reader_Read(svReader, vcheck, sizeof(vcheck)); - - if(strncmp(vcheck, "version ", 8))*/ - { - SaveInfo_Read_Dm_v19(info, svReader); - result = (info->version() <= V19_SAVE_VERSION); - } - - Reader_Delete(svReader); svReader = 0; - SV_CloseFile_Dm_v19(); - - return result; - } - - return false; -} - -void DoomV9GameStateReader::read(SaveInfo *info, Str const *path) -{ - DENG_ASSERT(info != 0 && path != 0); - - if(!SV_OpenFile_Dm_v19(Str_Text(path))) - { - throw FileAccessError("DoomV9GameStateReader", "Failed opending " + de::String(Str_Text(path))); - } - - svReader = SV_NewReader_Dm_v19(); - - // Read the header again. - /// @todo Seek past the header straight to the game state. - { - SaveInfo *tmp = new SaveInfo; - SaveInfo_Read_Dm_v19(tmp, svReader); - delete tmp; - } - - // We don't want to see a briefing if we're loading a save game. - briefDisabled = true; - - // Load a base map. - G_NewGame(info->mapUri(), 0/*not saved??*/, &info->gameRules()); - - // Recreate map state. - mapTime = info->mapTime(); - - /// @todo Necessary? - G_SetGameAction(GA_NONE); - - P_v19_UnArchivePlayers(); - P_v19_UnArchiveWorld(); - P_v19_UnArchiveThinkers(); - P_v19_UnArchiveSpecials(); - - if(Reader_ReadByte(svReader) != 0x1d) - { - Reader_Delete(svReader); svReader = 0; - SV_CloseFile_Dm_v19(); - - throw ReadError("DoomV9GameStateReader", "Bad savegame (consistency test failed!)"); - } - - Reader_Delete(svReader); svReader = 0; - SV_CloseFile_Dm_v19(); - - // Material scrollers must be spawned. - P_SpawnAllMaterialOriginScrollers(); - - // Let the engine know where the local players are now. - for(int i = 0; i < MAXPLAYERS; ++i) - { - R_UpdateConsoleView(i); - } - - // Inform the engine that map setup must be performed once more. - R_SetupMap(0, 0); -} diff --git a/doomsday/plugins/doom/src/st_stuff.c b/doomsday/plugins/doom/src/st_stuff.c index 07cbbe5090..bf0cc78f97 100644 --- a/doomsday/plugins/doom/src/st_stuff.c +++ b/doomsday/plugins/doom/src/st_stuff.c @@ -3209,7 +3209,7 @@ void ST_ToggleAutomapMaxZoom(int player) if(!obj) return; if(UIAutomap_SetZoomMax(obj, !UIAutomap_ZoomMax(obj))) { - App_Log(0, "Maximum zoom %s in automap\n", UIAutomap_ZoomMax(obj)? "ON":"OFF"); + App_Log(0, "Maximum zoom %s in automap", UIAutomap_ZoomMax(obj)? "ON":"OFF"); } } @@ -3368,7 +3368,7 @@ D_CMD(ChatSendMacro) { App_Log(DE2_SCR_NOTE, "Usage: %s (team) (macro number)", argv[0]); App_Log(DE2_SCR_MSG, "Send a chat macro to other player(s). " - "If (team) is omitted, the message will be sent to all players."); + "If (team) is omitted, the message will be sent to all players."); return true; } diff --git a/doomsday/plugins/doom64/doom64.pro b/doomsday/plugins/doom64/doom64.pro index 2d2db4ba3b..f06f4b35f6 100644 --- a/doomsday/plugins/doom64/doom64.pro +++ b/doomsday/plugins/doom64/doom64.pro @@ -68,7 +68,7 @@ SOURCES += \ src/d_api.c \ src/d_console.c \ src/d_items.c \ - src/d_main.c \ + src/d_main.cpp \ src/d_refresh.c \ src/m_cheat.c \ src/m_random.c \ diff --git a/doomsday/plugins/doom64/include/acfnlink.h b/doomsday/plugins/doom64/include/acfnlink.h index 6d61318e1f..d30c80cd2c 100644 --- a/doomsday/plugins/doom64/include/acfnlink.h +++ b/doomsday/plugins/doom64/include/acfnlink.h @@ -42,7 +42,7 @@ typedef struct { void (C_DECL *func)(); // Pointer to the function. } actionlink_t; -extern actionlink_t actionlinks[]; +DENG_EXTERN_C actionlink_t actionlinks[]; void C_DECL A_BabyMetal(); void C_DECL A_BFGsound(); diff --git a/doomsday/plugins/doom64/include/d_config.h b/doomsday/plugins/doom64/include/d_config.h index 0df7303e16..fe7d28e7ff 100644 --- a/doomsday/plugins/doom64/include/d_config.h +++ b/doomsday/plugins/doom64/include/d_config.h @@ -107,7 +107,7 @@ typedef struct jdoom64_config_s { byte menuShortcutsEnabled; byte menuScaleMode; int menuPatchReplaceMode; - byte menuGameSaveSuggestName; + byte menuGameSaveSuggestDescription; byte menuCursorRotate; float menuTextColors[MENU_COLOR_COUNT][3]; float menuTextFlashColor[3]; diff --git a/doomsday/plugins/doom64/include/d_console.h b/doomsday/plugins/doom64/include/d_console.h index d042408345..cc6463b223 100644 --- a/doomsday/plugins/doom64/include/d_console.h +++ b/doomsday/plugins/doom64/include/d_console.h @@ -33,6 +33,14 @@ # error "Using jDoom64 headers without __JDOOM64__" #endif +#ifdef __cplusplus +extern "C" { +#endif + void G_ConsoleRegistration(void); +#ifdef __cplusplus +} // extern "C" +#endif + #endif /* LIBDOOM64_CONSOLE_H */ diff --git a/doomsday/plugins/doom64/include/d_main.h b/doomsday/plugins/doom64/include/d_main.h index f020629d3e..b4b314f507 100644 --- a/doomsday/plugins/doom64/include/d_main.h +++ b/doomsday/plugins/doom64/include/d_main.h @@ -1,25 +1,21 @@ -/**\file d_main.h - *\section License - * License: GPL - * Online License Link: http://www.gnu.org/licenses/gpl.html +/** @file d_main.h Doom64-specific game initialization. * - *\author Copyright © 2003-2013 Jaakko Keränen - *\author Copyright © 2005-2013 Daniel Swanson + * @authors Copyright © 2003-2013 Jaakko Keränen + * @authors Copyright © 2005-2013 Daniel Swanson * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301 USA + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA */ #ifndef LIBJDOOM64_MAIN_H @@ -31,43 +27,53 @@ #include "doomdef.h" -#ifdef __cplusplus -extern "C" { -#endif - -extern int verbose; +DENG_EXTERN_C int verbose; -//extern dd_bool noMonstersParm; // checkparm of -nomonsters -//extern dd_bool respawnParm; // checkparm of -respawn -//extern dd_bool turboParm; // checkparm of -turbo -//extern dd_bool randomClassParm; // checkparm of -randclass -//extern dd_bool devParm; // checkparm of -devparm -//extern dd_bool fastParm; // checkparm of -fast +DENG_EXTERN_C float turboMul; // Multiplier for turbo. -extern float turboMul; // Multiplier for turbo. +DENG_EXTERN_C gamemode_t gameMode; +DENG_EXTERN_C int gameModeBits; -extern gamemode_t gameMode; -extern int gameModeBits; +DENG_EXTERN_C char const *borderGraphics[]; -extern char* borderGraphics[]; +DENG_EXTERN_C float const defFontRGB[]; +DENG_EXTERN_C float const defFontRGB2[]; +DENG_EXTERN_C float const defFontRGB3[]; -extern const float defFontRGB[]; -extern const float defFontRGB2[]; -extern const float defFontRGB3[]; +DENG_EXTERN_C dd_bool monsterInfight; -extern dd_bool monsterInfight; +#ifdef __cplusplus +extern "C" { +#endif +/** + * Pre Game Initialization routine. + * All game-specific actions that should take place at this time go here. + */ void D_PreInit(void); + +/** + * Post Game Initialization routine. + * All game-specific actions that should take place at this time go here. + */ void D_PostInit(void); + void D_Shutdown(void); void D_EndFrame(void); +/** + * Get a 32-bit integer value. + */ int D_GetInteger(int id); -void* D_GetVariable(int id); + +/** + * Get a pointer to the value of a named variable/constant. + */ +void *D_GetVariable(int id); #ifdef __cplusplus } // extern "C" #endif -#endif /* LIBJDOOM64_MAIN_H */ +#endif // LIBJDOOM64_MAIN_H diff --git a/doomsday/plugins/doom64/include/d_player.h b/doomsday/plugins/doom64/include/d_player.h index 93c6d9a2a3..c365745424 100644 --- a/doomsday/plugins/doom64/include/d_player.h +++ b/doomsday/plugins/doom64/include/d_player.h @@ -142,8 +142,8 @@ typedef struct player_s { int flyHeight; #ifdef __cplusplus - void write(Writer *writer) const; - void read(Reader *reader); + void write(Writer *writer, struct playerheader_s &plrHdr) const; + void read(Reader *reader, struct playerheader_s &plrHdr); #endif } player_t; diff --git a/doomsday/plugins/doom64/include/p_setup.h b/doomsday/plugins/doom64/include/p_setup.h index 5dab7593bf..a8f364f253 100644 --- a/doomsday/plugins/doom64/include/p_setup.h +++ b/doomsday/plugins/doom64/include/p_setup.h @@ -66,10 +66,19 @@ enum { MO_XX2 }; +#ifdef __cplusplus +extern "C" { +#endif + void P_RegisterMapObjs(void); int P_HandleMapDataPropertyValue(uint id, int dtype, int prop, valuetype_t type, void* data); int P_HandleMapObjectStatusReport(int code, uint id, int dtype, void* data); + +#ifdef __cplusplus +} // extern "C" +#endif + #endif /* LIBDOOM64_SETUP_H */ diff --git a/doomsday/plugins/doom64/include/wi_stuff.h b/doomsday/plugins/doom64/include/wi_stuff.h index 281fcfb040..b4c8231eeb 100644 --- a/doomsday/plugins/doom64/include/wi_stuff.h +++ b/doomsday/plugins/doom64/include/wi_stuff.h @@ -83,6 +83,10 @@ typedef enum { /// relevant state progressions. } interludestate_t; +#ifdef __cplusplus +extern "C" { +#endif + /// To be called to register the console commands and variables of this module. void WI_Register(void); @@ -116,4 +120,9 @@ void WI_End(void); */ void IN_SkipToNext(void); -#endif /* LIBDOOM_WI_STUFF_H */ +#ifdef __cplusplus +} // extern "C" +#endif + + +#endif // LIBDOOM_WI_STUFF_H diff --git a/doomsday/plugins/doom64/src/d_main.c b/doomsday/plugins/doom64/src/d_main.cpp similarity index 89% rename from doomsday/plugins/doom64/src/d_main.c rename to doomsday/plugins/doom64/src/d_main.cpp index 9fd3fb4b4c..baf6f6e56e 100644 --- a/doomsday/plugins/doom64/src/d_main.c +++ b/doomsday/plugins/doom64/src/d_main.cpp @@ -1,4 +1,4 @@ -/** @file d_main.c Doom64-specific game initialization. +/** @file d_main.cpp Doom64-specific game initialization. * * @authors Copyright © 2003-2013 Jaakko Keränen * @authors Copyright © 2006-2013 Daniel Swanson @@ -21,9 +21,6 @@ * 02110-1301 USA */ -#include -#include - #include "jdoom64.h" #include "am_map.h" @@ -33,6 +30,8 @@ #include "p_inventory.h" #include "p_saveg.h" #include "p_map.h" +#include "saveslots.h" +#include int verbose; float turboMul; // Multiplier for turbo. @@ -46,7 +45,7 @@ float const defFontRGB2[] = { .85f, 0, 0 }; // The patches used in drawing the view border. // Percent-encoded. -char *borderGraphics[] = { +char const *borderGraphics[] = { "Flats:FTILEABC", // Background. "BRDR_T", // Top. "BRDR_R", // Right. @@ -58,40 +57,34 @@ char *borderGraphics[] = { "BRDR_BL" // Bottom left. }; -/** - * Get a 32-bit integer value. - */ int D_GetInteger(int id) { return Common_GetInteger(id); } -/** - * Get a pointer to the value of a named variable/constant. - */ -void* D_GetVariable(int id) +void *D_GetVariable(int id) { static float bob[2]; switch(id) { case DD_PLUGIN_NAME: - return PLUGIN_NAMETEXT; + return (void*)PLUGIN_NAMETEXT; case DD_PLUGIN_NICENAME: - return PLUGIN_NICENAME; + return (void*)PLUGIN_NICENAME; case DD_PLUGIN_VERSION_SHORT: - return PLUGIN_VERSION_TEXT; + return (void*)PLUGIN_VERSION_TEXT; case DD_PLUGIN_VERSION_LONG: - return PLUGIN_VERSION_TEXTLONG "\n" PLUGIN_DETAILS; + return (void*)(PLUGIN_VERSION_TEXTLONG "\n" PLUGIN_DETAILS); case DD_PLUGIN_HOMEURL: - return PLUGIN_HOMEURL; + return (void*)PLUGIN_HOMEURL; case DD_PLUGIN_DOCSURL: - return PLUGIN_DOCSURL; + return (void*)PLUGIN_DOCSURL; case DD_GAME_CONFIG: return gameConfigString; @@ -122,11 +115,7 @@ void* D_GetVariable(int id) return 0; } -/** - * Pre Game Initialization routine. - * All game-specific actions that should take place at this time go here. - */ -void D_PreInit(void) +void D_PreInit() { // Config defaults. The real settings are read from the .cfg files // but these will be used no such files are found. @@ -159,8 +148,8 @@ void D_PreInit(void) cfg.hudShown[HUD_FRAGS] = true; cfg.hudShown[HUD_INVENTORY] = false; // They will be visible when the automap is. cfg.hudShown[HUD_LOG] = true; - { int i; - for(i = 0; i < NUMHUDUNHIDEEVENTS; ++i) // When the hud/statusbar unhides. + for(int i = 0; i < NUMHUDUNHIDEEVENTS; ++i) // When the hud/statusbar unhides. + { cfg.hudUnHide[i] = 1; } cfg.hudScale = .6f; @@ -212,7 +201,7 @@ void D_PreInit(void) cfg.menuTextColors[3][CB] = 0; cfg.menuSlam = false; cfg.menuShortcutsEnabled = true; - cfg.menuGameSaveSuggestName = true; + cfg.menuGameSaveSuggestDescription = true; cfg.statusbarScale = 1; @@ -307,16 +296,10 @@ void D_PreInit(void) G_CommonPreInit(); } -/** - * Post Game Initialization routine. - * All game-specific actions that should take place at this time go here. - */ -void D_PostInit(void) +void D_PostInit() { dd_bool autoStart = false; Uri *startMapUri = 0; - AutoStr *path; - int p; // Common post init routine. G_CommonPostInit(); @@ -346,7 +329,7 @@ void D_PostInit(void) gameRules.respawnMonsters = CommandLine_Check("-respawn")? true : false; gameRules.fast = CommandLine_Check("-fast")? true : false; - p = CommandLine_Check("-timer"); + int p = CommandLine_Check("-timer"); if(p && p < myargc - 1 && gameRules.deathmatch) { int time = atoi(CommandLine_At(p + 1)); @@ -375,8 +358,8 @@ void D_PostInit(void) p = CommandLine_Check("-loadgame"); if(p && p < myargc - 1) { - int const slotNumber = SaveSlots_ParseSlotIdentifier(saveSlots, CommandLine_At(p + 1)); - if(SaveSlots_SlotIsUserWritable(saveSlots, slotNumber) && G_LoadGame(slotNumber)) + int const slotNumber = SV_SaveSlots().parseSlotIdentifier(CommandLine_At(p + 1)); + if(SV_SaveSlots().slotIsUserWritable(slotNumber) && G_LoadGame(slotNumber)) { // No further initialization is to be done. return; @@ -414,7 +397,7 @@ void D_PostInit(void) } // Validate episode and map. - path = Uri_Compose(startMapUri); + AutoStr *path = Uri_Compose(startMapUri); if((autoStart || IS_NETGAME) && P_MapExists(Str_Text(path))) { G_DeferredNewGame(startMapUri, 0/*default*/, &gameRules); @@ -427,7 +410,7 @@ void D_PostInit(void) Uri_Delete(startMapUri); } -void D_Shutdown(void) +void D_Shutdown() { P_ShutdownInventory(); G_CommonShutdown(); diff --git a/doomsday/plugins/doom64/src/d_refresh.c b/doomsday/plugins/doom64/src/d_refresh.c index e654efbe54..3532f44fb8 100644 --- a/doomsday/plugins/doom64/src/d_refresh.c +++ b/doomsday/plugins/doom64/src/d_refresh.c @@ -292,7 +292,7 @@ void D_DrawViewPort(int port, const RectRaw* portGeometry, } #endif -void D_DrawWindow(const Size2Raw* windowSize) +void D_DrawWindow(Size2Raw const *windowSize) { if(G_GameState() == GS_INTERMISSION) { @@ -308,7 +308,7 @@ void D_DrawWindow(const Size2Raw* windowSize) } } -void D_EndFrame(void) +void D_EndFrame() { int i; @@ -316,9 +316,10 @@ void D_EndFrame(void) for(i = 0; i < MAXPLAYERS; ++i) { - player_t* plr = players + i; + player_t *plr = players + i; - if(!plr->plr->inGame || !plr->plr->mo) continue; + if(!plr->plr->inGame) continue; + if(!plr->plr->mo) continue; // View angles are updated with fractional ticks, so we can just use the current values. R_SetViewAngle(i, Player_ViewYawAngle(i)); @@ -330,7 +331,7 @@ void D_EndFrame(void) * Updates the mobj flags used by Doomsday with the state of our local flags * for the given mobj. */ -void P_SetDoomsdayFlags(mobj_t* mo) +void P_SetDoomsdayFlags(mobj_t *mo) { // Client mobjs can't be set here. if(IS_CLIENT && mo->ddFlags & DDMF_REMOTE) diff --git a/doomsday/plugins/doom64/src/m_cheat.c b/doomsday/plugins/doom64/src/m_cheat.c index 863dbb335a..aeaa741d20 100644 --- a/doomsday/plugins/doom64/src/m_cheat.c +++ b/doomsday/plugins/doom64/src/m_cheat.c @@ -411,25 +411,24 @@ D_CMD(CheatGive) if(argc != 2 && argc != 3) { - App_Log(DE2_SCR_NOTE, "Usage:\n give (stuff)\n"); - App_Log(DE2_LOG_SCR, " give (stuff) (plr)\n"); + App_Log(DE2_SCR_NOTE, "Usage:\n give (stuff)\n give (stuff) (plr)\n"); App_Log(DE2_LOG_SCR, "Stuff consists of one or more of (type:id). " - "If no id; give all of type:\n"); - App_Log(DE2_LOG_SCR, " a - ammo\n"); - App_Log(DE2_LOG_SCR, " b - berserk\n"); - App_Log(DE2_LOG_SCR, " f - the power of flight\n"); - App_Log(DE2_LOG_SCR, " g - light amplification visor\n"); - App_Log(DE2_LOG_SCR, " h - health\n"); - App_Log(DE2_LOG_SCR, " i - invulnerability\n"); - App_Log(DE2_LOG_SCR, " k - key cards/skulls\n"); - App_Log(DE2_LOG_SCR, " m - computer area map\n"); - App_Log(DE2_LOG_SCR, " p - backpack full of ammo\n"); - App_Log(DE2_LOG_SCR, " r - armor\n"); - App_Log(DE2_LOG_SCR, " s - radiation shielding suit\n"); - App_Log(DE2_LOG_SCR, " v - invisibility\n"); - App_Log(DE2_LOG_SCR, " w - weapons\n"); - App_Log(DE2_LOG_SCR, "Example: 'give arw' corresponds the cheat IDFA.\n"); - App_Log(DE2_LOG_SCR, "Example: 'give w2k1' gives weapon two and key one.\n"); + "If no id; give all of type:"); + App_Log(DE2_LOG_SCR, " a - ammo"); + App_Log(DE2_LOG_SCR, " b - berserk"); + App_Log(DE2_LOG_SCR, " f - the power of flight"); + App_Log(DE2_LOG_SCR, " g - light amplification visor"); + App_Log(DE2_LOG_SCR, " h - health"); + App_Log(DE2_LOG_SCR, " i - invulnerability"); + App_Log(DE2_LOG_SCR, " k - key cards/skulls"); + App_Log(DE2_LOG_SCR, " m - computer area map"); + App_Log(DE2_LOG_SCR, " p - backpack full of ammo"); + App_Log(DE2_LOG_SCR, " r - armor"); + App_Log(DE2_LOG_SCR, " s - radiation shielding suit"); + App_Log(DE2_LOG_SCR, " v - invisibility"); + App_Log(DE2_LOG_SCR, " w - weapons"); + App_Log(DE2_LOG_SCR, "Example: 'give arw' corresponds the cheat IDFA."); + App_Log(DE2_LOG_SCR, "Example: 'give w2k1' gives weapon two and key one."); return true; } @@ -442,7 +441,7 @@ D_CMD(CheatGive) if(G_GameState() != GS_MAP) { - App_Log(DE2_SCR_ERROR, "Can only \"give\" when in a game!\n"); + App_Log(DE2_SCR_ERROR, "Can only \"give\" when in a game!"); return true; } @@ -469,7 +468,7 @@ D_CMD(CheatGive) i += end - &buf[i+1]; if(idx < AT_FIRST || idx >= NUM_AMMO_TYPES) { - App_Log(DE2_SCR_ERROR, "Unknown ammo #%d (valid range %d-%d)\n", + App_Log(DE2_SCR_ERROR, "Unknown ammo #%d (valid range %d-%d)", (int)idx, AT_FIRST, NUM_AMMO_TYPES-1); break; } @@ -525,7 +524,7 @@ D_CMD(CheatGive) i += end - &buf[i+1]; if(idx < KT_FIRST || idx >= NUM_KEY_TYPES) { - App_Log(DE2_SCR_ERROR, "Unknown key #%d (valid range %d-%d)\n", + App_Log(DE2_SCR_ERROR, "Unknown key #%d (valid range %d-%d)", (int)idx, KT_FIRST, NUM_KEY_TYPES-1); break; } @@ -579,7 +578,7 @@ D_CMD(CheatGive) i += end - &buf[i+1]; if(idx < WT_FIRST || idx >= NUM_WEAPON_TYPES) { - App_Log(DE2_SCR_ERROR, "Unknown weapon #%d (valid range %d-%d)\n", + App_Log(DE2_SCR_ERROR, "Unknown weapon #%d (valid range %d-%d)", (int)idx, WT_FIRST, NUM_WEAPON_TYPES-1); break; } @@ -595,7 +594,7 @@ D_CMD(CheatGive) break; default: // Unrecognized. - App_Log(DE2_SCR_ERROR, "Cannot give '%c': unknown letter\n", buf[i]); + App_Log(DE2_SCR_ERROR, "Cannot give '%c': unknown letter", buf[i]); break; } } @@ -605,7 +604,7 @@ D_CMD(CheatGive) D_CMD(CheatMassacre) { - App_Log(DE2_LOG_MAP, "%i monsters killed\n", P_Massacre()); + App_Log(DE2_LOG_MAP, "%i monsters killed", P_Massacre()); return true; } @@ -626,7 +625,7 @@ D_CMD(CheatLeaveMap) if(G_GameState() != GS_MAP) { S_LocalSound(SFX_OOF, NULL); - App_Log(DE2_LOG_ERROR | DE2_LOG_MAP, "Can only exit a map when in a game!\n"); + App_Log(DE2_LOG_ERROR | DE2_LOG_MAP, "Can only exit a map when in a game!"); return true; } diff --git a/doomsday/plugins/doom64/src/st_stuff.c b/doomsday/plugins/doom64/src/st_stuff.c index 721bd6440e..583ced06bb 100644 --- a/doomsday/plugins/doom64/src/st_stuff.c +++ b/doomsday/plugins/doom64/src/st_stuff.c @@ -968,7 +968,7 @@ void ST_ToggleAutomapMaxZoom(int player) if(!obj) return; if(UIAutomap_SetZoomMax(obj, !UIAutomap_ZoomMax(obj))) { - App_Log(0, "Maximum zoom %s in automap\n", UIAutomap_ZoomMax(obj)? "ON":"OFF"); + App_Log(0, "Maximum zoom %s in automap", UIAutomap_ZoomMax(obj)? "ON":"OFF"); } } diff --git a/doomsday/plugins/fluidsynth/src/fluidsynth_music.cpp b/doomsday/plugins/fluidsynth/src/fluidsynth_music.cpp index 715300a44b..844b8e184a 100644 --- a/doomsday/plugins/fluidsynth/src/fluidsynth_music.cpp +++ b/doomsday/plugins/fluidsynth/src/fluidsynth_music.cpp @@ -364,11 +364,11 @@ void DMFluid_SetSoundFont(const char* fileName) sfontId = fluid_synth_sfload(DMFluid_Synth(), fileName, true); if(sfontId >= 0) { - App_Log(DE2_LOG_VERBOSE, "FluidSynth: Loaded SF2 soundfont \"%s\" with id:%i\n", fileName, sfontId); + App_Log(DE2_LOG_VERBOSE, "FluidSynth: Loaded SF2 soundfont \"%s\" with id:%i", fileName, sfontId); } else { - App_Log(DE2_LOG_VERBOSE, "FluidSynth: Failed to load soundfont \"%s\" (not SF2 or not found)\n", fileName); + App_Log(DE2_LOG_VERBOSE, "FluidSynth: Failed to load soundfont \"%s\" (not SF2 or not found)", fileName); } } diff --git a/doomsday/plugins/heretic/data/conhelp.txt b/doomsday/plugins/heretic/data/conhelp.txt index d5c7507dc9..affb8ac804 100644 --- a/doomsday/plugins/heretic/data/conhelp.txt +++ b/doomsday/plugins/heretic/data/conhelp.txt @@ -35,6 +35,10 @@ desc = End the game. [helpscreen] desc = Show the Help screens. +[inspectgamesave] +desc = Print detailed information about a saved game session to the console. +inf = Params: inspectgamesave (slotId)\nFor example, 'inspectgamesave 1'. + [kill] desc = Kill all the monsters on the map. diff --git a/doomsday/plugins/heretic/heretic.pro b/doomsday/plugins/heretic/heretic.pro index 56c6f0265e..35582e757e 100644 --- a/doomsday/plugins/heretic/heretic.pro +++ b/doomsday/plugins/heretic/heretic.pro @@ -46,6 +46,7 @@ HEADERS += \ include/h_stat.h \ include/h_think.h \ include/h_type.h \ + include/hereticv13gamestatereader.h \ include/in_lude.h \ include/info.h \ include/jheretic.h \ @@ -57,7 +58,6 @@ HEADERS += \ include/p_local.h \ include/p_maputl.h \ include/p_mobj.h \ - include/p_oldsvg.h \ include/p_pspr.h \ include/p_setup.h \ include/p_spec.h \ @@ -74,8 +74,9 @@ SOURCES += \ src/acfnlink.c \ src/h_api.c \ src/h_console.c \ - src/h_main.c \ + src/h_main.cpp \ src/h_refresh.c \ + src/hereticv13gamestatereader.cpp \ src/in_lude.c \ src/m_cheat.c \ src/m_random.c \ @@ -84,7 +85,6 @@ SOURCES += \ src/p_lights.cpp \ src/p_maputl.c \ src/p_mobj.c \ - src/p_oldsvg.cpp \ src/p_pspr.c \ src/p_setup.c \ src/p_spec.c \ diff --git a/doomsday/plugins/heretic/include/acfnlink.h b/doomsday/plugins/heretic/include/acfnlink.h index 7055bd320c..4df249a39f 100644 --- a/doomsday/plugins/heretic/include/acfnlink.h +++ b/doomsday/plugins/heretic/include/acfnlink.h @@ -41,7 +41,7 @@ typedef struct { void (C_DECL *func) (); // Pointer to the function. } actionlink_t; -extern actionlink_t actionlinks[]; +DENG_EXTERN_C actionlink_t actionlinks[]; void C_DECL A_AccTeleGlitter(); void C_DECL A_AddPlayerCorpse(); diff --git a/doomsday/plugins/heretic/include/h_config.h b/doomsday/plugins/heretic/include/h_config.h index d54898ebc0..86895ff1e8 100644 --- a/doomsday/plugins/heretic/include/h_config.h +++ b/doomsday/plugins/heretic/include/h_config.h @@ -106,7 +106,7 @@ typedef struct jheretic_config_s { byte menuShortcutsEnabled; byte menuScaleMode; int menuPatchReplaceMode; - byte menuGameSaveSuggestName; + byte menuGameSaveSuggestDescription; byte menuCursorRotate; float menuTextColors[MENU_COLOR_COUNT][3]; float menuTextFlashColor[3]; diff --git a/doomsday/plugins/heretic/include/h_console.h b/doomsday/plugins/heretic/include/h_console.h index 167b3f332f..d89d3c6bdc 100644 --- a/doomsday/plugins/heretic/include/h_console.h +++ b/doomsday/plugins/heretic/include/h_console.h @@ -33,6 +33,14 @@ # error "Using jHeretic headers without __JHERETIC__" #endif +#ifdef __cplusplus +extern "C" { +#endif + void G_ConsoleRegistration(void); +#ifdef __cplusplus +} // extern "C" +#endif + #endif /* LIBHERETIC_CONSOLE_H */ diff --git a/doomsday/plugins/heretic/include/h_main.h b/doomsday/plugins/heretic/include/h_main.h index 433c4b9958..643429d977 100644 --- a/doomsday/plugins/heretic/include/h_main.h +++ b/doomsday/plugins/heretic/include/h_main.h @@ -1,25 +1,21 @@ -/**\file h_main.h - *\section License - * License: GPL - * Online License Link: http://www.gnu.org/licenses/gpl.html +/** @file h_main.h Heretic-specific game initialization. * - *\author Copyright © 2003-2013 Jaakko Keränen - *\author Copyright © 2006-2013 Daniel Swanson + * @authors Copyright © 2003-2013 Jaakko Keränen + * @authors Copyright © 2006-2013 Daniel Swanson * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301 USA + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA */ #ifndef LIBJHERETIC_MAIN_H @@ -29,40 +25,53 @@ # error "Using jHeretic headers without __JHERETIC__" #endif -#ifdef __cplusplus -extern "C" { -#endif +#include "doomdef.h" -extern int verbose; +DENG_EXTERN_C int verbose; -//extern dd_bool noMonstersParm; // checkparm of -nomonsters -//extern dd_bool respawnParm; // checkparm of -respawn -//extern dd_bool turboParm; // checkparm of -turbo -//extern dd_bool randomClassParm; // checkparm of -randclass -//extern dd_bool devParm; // checkparm of -devparm -//extern dd_bool fastParm; // checkparm of -fast +DENG_EXTERN_C float turboMul; // Multiplier for turbo. -extern float turboMul; // Multiplier for turbo. +DENG_EXTERN_C gamemode_t gameMode; +DENG_EXTERN_C int gameModeBits; -extern gamemode_t gameMode; -extern int gameModeBits; +DENG_EXTERN_C char const *borderGraphics[]; -extern char* borderGraphics[]; +DENG_EXTERN_C float const defFontRGB[]; +DENG_EXTERN_C float const defFontRGB2[]; +DENG_EXTERN_C float const defFontRGB3[]; -extern const float defFontRGB[]; -extern const float defFontRGB2[]; -extern const float defFontRGB3[]; +DENG_EXTERN_C dd_bool monsterInfight; -extern dd_bool monsterInfight; +#ifdef __cplusplus +extern "C" { +#endif +/** + * Pre Game Initialization routine. + * All game-specific actions that should take place at this time go here. + */ void H_PreInit(void); + +/** + * Post Game Initialization routine. + * All game-specific actions that should take place at this time go here. + */ void H_PostInit(void); + void H_Shutdown(void); + +/** + * Get a 32-bit integer value. + */ int H_GetInteger(int id); -void* H_GetVariable(int id); + +/** + * Get a pointer to the value of a variable. Added for 64-bit support. + */ +void *H_GetVariable(int id); #ifdef __cplusplus } // extern "C" #endif -#endif /* LIBJHERETIC_MAIN_H */ +#endif // LIBJHERETIC_MAIN_H diff --git a/doomsday/plugins/heretic/include/h_player.h b/doomsday/plugins/heretic/include/h_player.h index 59f64de33f..a252f4bf33 100644 --- a/doomsday/plugins/heretic/include/h_player.h +++ b/doomsday/plugins/heretic/include/h_player.h @@ -161,8 +161,8 @@ typedef struct player_s { mobj_t* rain2; // Active rain maker 2. #ifdef __cplusplus - void write(Writer *writer) const; - void read(Reader *reader); + void write(Writer *writer, struct playerheader_s &plrHdr) const; + void read(Reader *reader, struct playerheader_s &plrHdr); #endif } player_t; diff --git a/doomsday/plugins/heretic/include/p_oldsvg.h b/doomsday/plugins/heretic/include/hereticv13gamestatereader.h similarity index 64% rename from doomsday/plugins/heretic/include/p_oldsvg.h rename to doomsday/plugins/heretic/include/hereticv13gamestatereader.h index f651805b0b..143b8bb84b 100644 --- a/doomsday/plugins/heretic/include/p_oldsvg.h +++ b/doomsday/plugins/heretic/include/hereticv13gamestatereader.h @@ -1,4 +1,4 @@ -/** @file p_oldsvg.h Heretic ver 1.3 save game reader. +/** @file hereticv13gamestatereader.h Heretic ver 1.3 save game reader. * * @authors Copyright © 2003-2013 Jaakko Keränen * @authors Copyright © 2006-2013 Daniel Swanson @@ -26,33 +26,26 @@ # error "Using jHeretic headers without __JHERETIC__" #endif -#include "saveinfo.h" +#include "gamestatereader.h" /** * Heretic ver 1.3 saved game state reader. * * @ingroup libheretic */ -class HereticV13GameStateReader +class HereticV13GameStateReader : public IGameStateReader { public: - /// An error occurred attempting to open the input file. @ingroup errors - DENG2_ERROR(FileAccessError); + HereticV13GameStateReader(); + ~HereticV13GameStateReader(); - /// Base class for read errors. @ingroup errors - DENG2_ERROR(ReadError); + static IGameStateReader *make(); + static bool recognize(SaveInfo &info); -public: - /** - * Determines whether the resource file on @a path is interpretable as a game state which can - * be loaded with a HereticV13GameStateReader. - * - * @param info SaveInfo to attempt to read game session header into. - * @param path Path to the resource file to be recognized. - */ - static bool recognize(SaveInfo *info, Str const *path); - - void read(SaveInfo *info, Str const *path); + void read(SaveInfo &info); + +private: + DENG2_PRIVATE(d) }; #endif // LIBHERETIC_HERETICV13_GAMESTATEREADER diff --git a/doomsday/plugins/heretic/include/in_lude.h b/doomsday/plugins/heretic/include/in_lude.h index 6e68f71c7e..a20483dc16 100644 --- a/doomsday/plugins/heretic/include/in_lude.h +++ b/doomsday/plugins/heretic/include/in_lude.h @@ -34,9 +34,13 @@ #include "h_player.h" -extern dd_bool intermission; -extern int interState; -extern int interTime; +DENG_EXTERN_C dd_bool intermission; +DENG_EXTERN_C int interState; +DENG_EXTERN_C int interTime; + +#ifdef __cplusplus +extern "C" { +#endif /// To be called to register the console commands and variables of this module. void WI_Register(void); @@ -56,4 +60,8 @@ void IN_InitStats(void); void IN_InitDeathmatchStats(void); void IN_InitNetgameStats(void); +#ifdef __cplusplus +} // extern "C" +#endif + #endif diff --git a/doomsday/plugins/heretic/include/m_cheat.h b/doomsday/plugins/heretic/include/m_cheat.h index 18bc5c6e13..543c70ad61 100644 --- a/doomsday/plugins/heretic/include/m_cheat.h +++ b/doomsday/plugins/heretic/include/m_cheat.h @@ -27,9 +27,17 @@ # error "Using jHeretic headers without __JHERETIC__" #endif +#ifdef __cplusplus +extern "C" { +#endif + /** * Register event sequence callbacks for all cheats. */ void G_RegisterCheats(void); +#ifdef __cplusplus +} // extern "C" +#endif + #endif // LIBHERETIC_M_CHEAT_H diff --git a/doomsday/plugins/heretic/include/p_pspr.h b/doomsday/plugins/heretic/include/p_pspr.h index 4bd076c34c..6a8dc2de40 100644 --- a/doomsday/plugins/heretic/include/p_pspr.h +++ b/doomsday/plugins/heretic/include/p_pspr.h @@ -43,12 +43,20 @@ typedef enum { } psprnum_t; typedef struct pspdef_s { - state_t* state; // a NULL state means not active. - int tics; - float pos[2]; + state_t *state; // a NULL state means not active. + int tics; + float pos[2]; } pspdef_t; -void P_BringUpWeapon(struct player_s *player); -void R_GetWeaponBob(int player, float* x, float* y); +#ifdef __cplusplus +extern "C" { +#endif + +void R_GetWeaponBob(int player, float *x, float *y); +void P_BringUpWeapon(struct player_s *player); + +#ifdef __cplusplus +} // extern "C" +#endif #endif diff --git a/doomsday/plugins/heretic/include/p_setup.h b/doomsday/plugins/heretic/include/p_setup.h index f112801984..3364750bdb 100644 --- a/doomsday/plugins/heretic/include/p_setup.h +++ b/doomsday/plugins/heretic/include/p_setup.h @@ -50,8 +50,17 @@ enum { MO_TAG }; +#ifdef __cplusplus +extern "C" { +#endif + void P_RegisterMapObjs(void); int P_HandleMapDataPropertyValue(uint id, int dtype, int prop, valuetype_t type, void* data); int P_HandleMapObjectStatusReport(int code, uint id, int dtype, void* data); + +#ifdef __cplusplus +} // extern "C" +#endif + #endif diff --git a/doomsday/plugins/heretic/src/h_main.c b/doomsday/plugins/heretic/src/h_main.cpp similarity index 88% rename from doomsday/plugins/heretic/src/h_main.c rename to doomsday/plugins/heretic/src/h_main.cpp index 1840157b13..acdf7515be 100644 --- a/doomsday/plugins/heretic/src/h_main.c +++ b/doomsday/plugins/heretic/src/h_main.cpp @@ -1,4 +1,4 @@ -/** @file h_main.c Heretic-specific game initialization. +/** @file h_main.cpp Heretic-specific game initialization. * * @authors Copyright © 2003-2013 Jaakko Keränen * @authors Copyright © 2005-2013 Daniel Swanson @@ -26,21 +26,15 @@ #include "m_argv.h" #include "p_map.h" #include "p_saveg.h" +#include "hereticv13gamestatereader.h" #include "am_map.h" #include "g_defs.h" #include "p_inventory.h" -#include -#include +#include "saveslots.h" +#include int verbose; -//dd_bool devParm; // checkparm of -devparm -//dd_bool noMonstersParm; // checkparm of -nomonsters -//dd_bool respawnParm; // checkparm of -respawn -//dd_bool fastParm; // checkparm of -fast -//dd_bool turboParm; // checkparm of -turbo -//dd_bool randomClassParm; // checkparm of -randclass - float turboMul; // Multiplier for turbo. gamemode_t gameMode; @@ -53,7 +47,7 @@ float const defFontRGB3[] = { 1.0f, 1.0f, 1.0f }; // The patches used in drawing the view border. // Percent-encoded. -char* borderGraphics[] = { +char const *borderGraphics[] = { "Flats:FLAT513", // Background. "BORDT", // Top. "BORDR", // Right. @@ -65,40 +59,34 @@ char* borderGraphics[] = { "BORDBL" // Bottom left. }; -/** - * Get a 32-bit integer value. - */ int H_GetInteger(int id) { return Common_GetInteger(id); } -/** - * Get a pointer to the value of a variable. Added for 64-bit support. - */ -void* H_GetVariable(int id) +void *H_GetVariable(int id) { static float bob[2]; switch(id) { case DD_PLUGIN_NAME: - return PLUGIN_NAMETEXT; + return (void*)PLUGIN_NAMETEXT; case DD_PLUGIN_NICENAME: - return PLUGIN_NICENAME; + return (void*)PLUGIN_NICENAME; case DD_PLUGIN_VERSION_SHORT: - return PLUGIN_VERSION_TEXT; + return (void*)PLUGIN_VERSION_TEXT; case DD_PLUGIN_VERSION_LONG: - return PLUGIN_VERSION_TEXTLONG "\n" PLUGIN_DETAILS; + return (void*)(PLUGIN_VERSION_TEXTLONG "\n" PLUGIN_DETAILS); case DD_PLUGIN_HOMEURL: - return PLUGIN_HOMEURL; + return (void*)PLUGIN_HOMEURL; case DD_PLUGIN_DOCSURL: - return PLUGIN_DOCSURL; + return (void*)PLUGIN_DOCSURL; case DD_GAME_CONFIG: return gameConfigString; @@ -131,11 +119,7 @@ void* H_GetVariable(int id) return 0; } -/** - * Pre Game Initialization routine. - * All game-specific actions that should take place at this time go here. - */ -void H_PreInit(void) +void H_PreInit() { // Config defaults. The real settings are read from the .cfg files // but these will be used no such files are found. @@ -167,8 +151,8 @@ void H_PreInit(void) cfg.hudShown[HUD_HEALTH] = true; cfg.hudShown[HUD_READYITEM] = true; cfg.hudShown[HUD_LOG] = true; - { int i; - for(i = 0; i < NUMHUDUNHIDEEVENTS; ++i) // when the hud/statusbar unhides. + for(int i = 0; i < NUMHUDUNHIDEEVENTS; ++i) // when the hud/statusbar unhides. + { cfg.hudUnHide[i] = 1; } cfg.hudScale = .7f; @@ -222,7 +206,7 @@ void H_PreInit(void) cfg.menuTextColors[3][2] = defFontRGB3[2]; cfg.menuSlam = true; cfg.menuShortcutsEnabled = true; - cfg.menuGameSaveSuggestName = true; + cfg.menuGameSaveSuggestDescription = true; cfg.confirmQuickGameSave = true; cfg.confirmRebornLoad = true; @@ -334,16 +318,10 @@ void H_PreInit(void) G_CommonPreInit(); } -/** - * Post Game Initialization routine. - * All game-specific actions that should take place at this time go here. - */ -void H_PostInit(void) +void H_PostInit() { dd_bool autoStart = false; Uri *startMapUri = 0; - AutoStr *path; - int p; /// @todo Kludge: Shareware WAD has different border background. /// @todo Do this properly! @@ -355,6 +333,9 @@ void H_PostInit(void) // Common post init routine. G_CommonPostInit(); + // Declare the Heretic V13 game state reader/interpreter. + SV_DeclareGameStateReader(&HereticV13GameStateReader::recognize, &HereticV13GameStateReader::make); + // Initialize weapon info using definitions. P_InitWeaponInfo(); @@ -377,7 +358,7 @@ void H_PostInit(void) gameRules.respawnMonsters = CommandLine_Check("-respawn")? true : false; // turbo option. - p = CommandLine_Check("-turbo"); + int p = CommandLine_Check("-turbo"); turboMul = 1.0f; if(p) { @@ -398,8 +379,8 @@ void H_PostInit(void) p = CommandLine_Check("-loadgame"); if(p && p < myargc - 1) { - int const slotNumber = SaveSlots_ParseSlotIdentifier(saveSlots, CommandLine_At(p + 1)); - if(SaveSlots_SlotIsUserWritable(saveSlots, slotNumber) && G_LoadGame(slotNumber)) + int const slotNumber = SV_SaveSlots().parseSlotIdentifier(CommandLine_At(p + 1)); + if(SV_SaveSlots().slotIsUserWritable(slotNumber) && G_LoadGame(slotNumber)) { // No further initialization is to be done. return; @@ -448,7 +429,7 @@ void H_PostInit(void) } // Validate episode and map. - path = Uri_Compose(startMapUri); + AutoStr *path = Uri_Compose(startMapUri); if((autoStart || IS_NETGAME) && P_MapExists(Str_Text(path))) { G_DeferredNewGame(startMapUri, 0/*default*/, &gameRules); @@ -461,7 +442,7 @@ void H_PostInit(void) Uri_Delete(startMapUri); } -void H_Shutdown(void) +void H_Shutdown() { P_ShutdownInventory(); G_CommonShutdown(); diff --git a/doomsday/plugins/heretic/src/hereticv13gamestatereader.cpp b/doomsday/plugins/heretic/src/hereticv13gamestatereader.cpp new file mode 100644 index 0000000000..ae65a88d4d --- /dev/null +++ b/doomsday/plugins/heretic/src/hereticv13gamestatereader.cpp @@ -0,0 +1,1017 @@ +/** @file hereticv13gamestatereader.cpp Heretic ver 1.3 save game reader. + * + * @authors Copyright © 2003-2013 Jaakko Keränen + * @authors Copyright © 2006-2013 Daniel Swanson + * @authors Copyright © 1999 Activision + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "jheretic.h" +#include "hereticv13gamestatereader.h" + +#include "am_map.h" +#include "dmu_lib.h" +#include "hu_inventory.h" +#include "p_ceiling.h" +#include "p_door.h" +#include "p_floor.h" +#include "p_inventory.h" +#include "p_map.h" +#include "p_mapsetup.h" +#include "p_plat.h" +#include "p_saveio.h" +#include "p_saveg.h" +#include "p_tick.h" +#include "r_common.h" // R_UpdateConsoleView +#include +#include +#include + +#define SIZEOF_V13_THINKER_T 12 +#define V13_THINKER_T_FUNC_OFFSET 8 + +static byte *savePtr; +static byte *saveBuffer; + +static char sri8(Reader *r) +{ + if(!r) return 0; + savePtr++; + return *(char *) (savePtr - 1); +} + +static short sri16(Reader *r) +{ + if(!r) return 0; + savePtr += 2; + return *(int16_t *) (savePtr - 2); +} + +static int sri32(Reader *r) +{ + if(!r) return 0; + savePtr += 4; + return *(int32_t *) (savePtr - 4); +} + +static void srd(Reader *r, char *data, int len) +{ + if(!r) return; + if(data) + { + std::memcpy(data, savePtr, len); + } + savePtr += len; +} + +static Uri *readTextureUrn(Reader *reader, char const *schemeName) +{ + DENG_ASSERT(reader != 0 && schemeName != 0); + return Uri_NewWithPath2(Str_Text(Str_Appendf(AutoStr_NewStd(), "urn:%s:%i", schemeName, Reader_ReadInt16(reader))), RC_NULL); +} + +static void readPlayer(player_t *pl, Reader *reader) +{ + int plrnum = pl - players; + ddplayer_t *ddpl = pl->plr; + + Reader_ReadInt32(reader); // mo + + pl->playerState = playerstate_t(Reader_ReadInt32(reader)); + DENG_ASSERT(pl->playerState >= PST_LIVE && pl->playerState <= PST_REBORN); + + byte junk[12]; + Reader_Read(reader, junk, 10); // ticcmd_t + + pl->viewZ = FIX2FLT(Reader_ReadInt32(reader)); + pl->viewHeight = FIX2FLT(Reader_ReadInt32(reader)); + pl->viewHeightDelta = FIX2FLT(Reader_ReadInt32(reader)); + pl->bob = FIX2FLT(Reader_ReadInt32(reader)); + pl->flyHeight = Reader_ReadInt32(reader); + ddpl->lookDir = Reader_ReadInt32(reader); + pl->centering = Reader_ReadInt32(reader); + pl->health = Reader_ReadInt32(reader); + pl->armorPoints = Reader_ReadInt32(reader); + pl->armorType = Reader_ReadInt32(reader); + + P_InventoryEmpty(plrnum); + for(int i = 0; i < 14; ++i) + { + inventoryitemtype_t type = inventoryitemtype_t(Reader_ReadInt32(reader)); + DENG_ASSERT(type >= IIT_NONE && type < NUM_INVENTORYITEM_TYPES); + int count = Reader_ReadInt32(reader); + + for(int k = 0; k < count; ++k) + { + P_InventoryGive(plrnum, type, true); + } + } + + P_InventorySetReadyItem(plrnum, inventoryitemtype_t(Reader_ReadInt32(reader))); + Hu_InventorySelect(plrnum, P_InventoryReadyItem(plrnum)); + /*pl->artifactCount =*/ Reader_ReadInt32(reader); + /*pl->inventorySlotNum =*/ Reader_ReadInt32(reader); + + memset(pl->powers, 0, sizeof(pl->powers)); + /*pl->powers[pw_NONE] =*/ Reader_ReadInt32(reader); + pl->powers[PT_INVULNERABILITY] = Reader_ReadInt32(reader); + pl->powers[PT_INVISIBILITY] = Reader_ReadInt32(reader); + pl->powers[PT_ALLMAP] = Reader_ReadInt32(reader); + if(pl->powers[PT_ALLMAP]) + { + ST_RevealAutomap(pl - players, true); + } + pl->powers[PT_INFRARED] = Reader_ReadInt32(reader); + pl->powers[PT_WEAPONLEVEL2] = Reader_ReadInt32(reader); + pl->powers[PT_FLIGHT] = Reader_ReadInt32(reader); + pl->powers[PT_SHIELD] = Reader_ReadInt32(reader); + pl->powers[PT_HEALTH2] = Reader_ReadInt32(reader); + + memset(pl->keys, 0, sizeof(pl->keys)); + pl->keys[KT_YELLOW] = !!Reader_ReadInt32(reader); + pl->keys[KT_GREEN] = !!Reader_ReadInt32(reader); + pl->keys[KT_BLUE] = !!Reader_ReadInt32(reader); + + pl->backpack = Reader_ReadInt32(reader); + + memset(pl->frags, 0, sizeof(pl->frags)); + pl->frags[0] = Reader_ReadInt32(reader); + pl->frags[1] = Reader_ReadInt32(reader); + pl->frags[2] = Reader_ReadInt32(reader); + pl->frags[3] = Reader_ReadInt32(reader); + + int readyWeapon = Reader_ReadInt32(reader); + pl->readyWeapon = readyWeapon == 10? WT_NOCHANGE : weapontype_t(readyWeapon); + + int pendingWeapon = Reader_ReadInt32(reader); + pl->pendingWeapon = pendingWeapon == 10? WT_NOCHANGE : weapontype_t(pendingWeapon); + + // Owned weapons. + memset(pl->weapons, 0, sizeof(pl->weapons)); + pl->weapons[WT_FIRST ].owned = !!Reader_ReadInt32(reader); + pl->weapons[WT_SECOND ].owned = !!Reader_ReadInt32(reader); + pl->weapons[WT_THIRD ].owned = !!Reader_ReadInt32(reader); + pl->weapons[WT_FOURTH ].owned = !!Reader_ReadInt32(reader); + pl->weapons[WT_FIFTH ].owned = !!Reader_ReadInt32(reader); + pl->weapons[WT_SIXTH ].owned = !!Reader_ReadInt32(reader); + pl->weapons[WT_SEVENTH].owned = !!Reader_ReadInt32(reader); + pl->weapons[WT_EIGHTH ].owned = !!Reader_ReadInt32(reader); + /*pl->weapons[wp_beak ].owned = !!*/ Reader_ReadInt32(reader); + + memset(pl->ammo, 0, sizeof(pl->ammo)); + pl->ammo[AT_CRYSTAL].owned = Reader_ReadInt32(reader); + pl->ammo[AT_ARROW ].owned = Reader_ReadInt32(reader); + pl->ammo[AT_ORB ].owned = Reader_ReadInt32(reader); + pl->ammo[AT_RUNE ].owned = Reader_ReadInt32(reader); + pl->ammo[AT_FIREORB].owned = Reader_ReadInt32(reader); + pl->ammo[AT_MSPHERE].owned = Reader_ReadInt32(reader); + pl->ammo[AT_CRYSTAL].max = Reader_ReadInt32(reader); + pl->ammo[AT_ARROW ].max = Reader_ReadInt32(reader); + pl->ammo[AT_ORB ].max = Reader_ReadInt32(reader); + pl->ammo[AT_RUNE ].max = Reader_ReadInt32(reader); + pl->ammo[AT_FIREORB].max = Reader_ReadInt32(reader); + pl->ammo[AT_MSPHERE].max = Reader_ReadInt32(reader); + + pl->attackDown = Reader_ReadInt32(reader); + pl->useDown = Reader_ReadInt32(reader); + pl->cheats = Reader_ReadInt32(reader); + pl->refire = Reader_ReadInt32(reader); + pl->killCount = Reader_ReadInt32(reader); + pl->itemCount = Reader_ReadInt32(reader); + pl->secretCount = Reader_ReadInt32(reader); + /*pl->message =*/ Reader_ReadInt32(reader); // char* + /*pl->messageTics =*/ Reader_ReadInt32(reader); // int32 + pl->damageCount = Reader_ReadInt32(reader); + pl->bonusCount = Reader_ReadInt32(reader); + pl->flameCount = Reader_ReadInt32(reader); + /*pl->attacker =*/ Reader_ReadInt32(reader); // mobj_t* + + ddpl->extraLight = Reader_ReadInt32(reader); + ddpl->fixedColorMap = Reader_ReadInt32(reader); + + pl->colorMap = Reader_ReadInt32(reader); + + for(int i = 0; i < 2; ++i) + { + pspdef_t *psp = &pl->pSprites[i]; + + psp->state = INT2PTR(state_t, Reader_ReadInt32(reader)); + psp->tics = Reader_ReadInt32(reader); + psp->pos[VX] = FIX2FLT(Reader_ReadInt32(reader)); + psp->pos[VY] = FIX2FLT(Reader_ReadInt32(reader)); + } + + pl->didSecret = !!Reader_ReadInt32(reader); + pl->morphTics = Reader_ReadInt32(reader); + pl->chickenPeck = Reader_ReadInt32(reader); + /*pl->rain1 =*/ Reader_ReadInt32(reader); // mobj_t* + /*pl->rain2 =*/ Reader_ReadInt32(reader); // mobj_t* +} + +static void readMobj(Reader *reader) +{ +#define FF_FRAMEMASK 0x7fff + + // The thinker was 3 ints long. + Reader_ReadInt32(reader); + Reader_ReadInt32(reader); + Reader_ReadInt32(reader); + + coord_t pos[3]; + pos[VX] = FIX2FLT(Reader_ReadInt32(reader)); + pos[VY] = FIX2FLT(Reader_ReadInt32(reader)); + pos[VZ] = FIX2FLT(Reader_ReadInt32(reader)); + + // Sector links. + Reader_ReadInt32(reader); + Reader_ReadInt32(reader); + + angle_t angle = (angle_t) (ANG45 * (Reader_ReadInt32(reader) / 45)); + spritenum_t sprite = Reader_ReadInt32(reader); + + int frame = Reader_ReadInt32(reader); + frame &= ~FF_FRAMEMASK; // not used anymore. + + // Block links. + Reader_ReadInt32(reader); + Reader_ReadInt32(reader); + + // Subsector. + Reader_ReadInt32(reader); + + coord_t floorz = FIX2FLT(Reader_ReadInt32(reader)); + coord_t ceilingz = FIX2FLT(Reader_ReadInt32(reader)); + coord_t radius = FIX2FLT(Reader_ReadInt32(reader)); + coord_t height = FIX2FLT(Reader_ReadInt32(reader)); + + coord_t mom[3]; + mom[MX] = FIX2FLT(Reader_ReadInt32(reader)); + mom[MY] = FIX2FLT(Reader_ReadInt32(reader)); + mom[MZ] = FIX2FLT(Reader_ReadInt32(reader)); + + int valid = Reader_ReadInt32(reader); + int type = Reader_ReadInt32(reader); + + DENG_ASSERT(type >= 0 && type < Get(DD_NUMMOBJTYPES)); + mobjinfo_t *info = &MOBJINFO[type]; + + int ddflags = 0; + if(info->flags & MF_SOLID) ddflags |= DDMF_SOLID; + if(info->flags2 & MF2_DONTDRAW) ddflags |= DDMF_DONTDRAW; + + /* + * We now have all the information we need to create the mobj. + */ + mobj_t *mo = Mobj_CreateXYZ(P_MobjThinker, pos[VX], pos[VY], pos[VZ], angle, + radius, height, ddflags); + + mo->sprite = sprite; + mo->frame = frame; + mo->floorZ = floorz; + mo->ceilingZ = ceilingz; + mo->mom[MX] = mom[MX]; + mo->mom[MY] = mom[MY]; + mo->mom[MZ] = mom[MZ]; + mo->valid = valid; + mo->type = type; + mo->moveDir = DI_NODIR; + + /* + * Continue reading the mobj data. + */ + Reader_ReadInt32(reader); // info + + mo->tics = Reader_ReadInt32(reader); + mo->state = INT2PTR(state_t, Reader_ReadInt32(reader)); + mo->damage = Reader_ReadInt32(reader); + mo->flags = Reader_ReadInt32(reader); + mo->flags2 = Reader_ReadInt32(reader); + mo->special1 = Reader_ReadInt32(reader); + mo->special2 = Reader_ReadInt32(reader); + mo->health = Reader_ReadInt32(reader); + + // Fix a bunch of kludges in the original Heretic. + switch(mo->type) + { + case MT_MACEFX1: + case MT_MACEFX2: + case MT_MACEFX3: + case MT_HORNRODFX2: + case MT_HEADFX3: + case MT_WHIRLWIND: + case MT_TELEGLITTER: + case MT_TELEGLITTER2: + mo->special3 = mo->health; + mo->health = info->spawnHealth; + break; + + default: break; + } + + mo->moveDir = Reader_ReadInt32(reader); + mo->moveCount = Reader_ReadInt32(reader); + Reader_ReadInt32(reader); // target + mo->reactionTime = Reader_ReadInt32(reader); + mo->threshold = Reader_ReadInt32(reader); + mo->player = INT2PTR(player_t, Reader_ReadInt32(reader)); + mo->lastLook = Reader_ReadInt32(reader); + + mo->spawnSpot.origin[VX] = (coord_t) Reader_ReadInt16(reader); + mo->spawnSpot.origin[VY] = (coord_t) Reader_ReadInt16(reader); + mo->spawnSpot.origin[VZ] = 0; // Initialize with "something". + mo->spawnSpot.angle = (angle_t) (ANG45 * (Reader_ReadInt16(reader) / 45)); + /*mo->spawnSpot.type = (int)*/ Reader_ReadInt16(reader); + + int spawnFlags = ((int) Reader_ReadInt16(reader)) & ~MASK_UNKNOWN_MSF_FLAGS; + // Spawn on the floor by default unless the mobjtype flags override. + spawnFlags |= MSF_Z_FLOOR; + mo->spawnSpot.flags = spawnFlags; + + mo->info = info; + SV_TranslateLegacyMobjFlags(mo, 0); + + mo->state = &STATES[PTR2INT(mo->state)]; + mo->target = NULL; + if(mo->player) + { + mo->player = &players[PTR2INT(mo->player) - 1]; + mo->player->plr->mo = mo; + mo->player->plr->mo->dPlayer = mo->player->plr; + } + P_MobjLink(mo); + mo->floorZ = P_GetDoublep(Mobj_Sector(mo), DMU_FLOOR_HEIGHT); + mo->ceilingZ = P_GetDoublep(Mobj_Sector(mo), DMU_CEILING_HEIGHT); + +#undef FF_FRAMEMASK +} + +static int removeThinker(thinker_t *th, void * /*context*/) +{ + if(th->function == (thinkfunc_t) P_MobjThinker) + { + P_MobjRemove((mobj_t *) th, true); + } + else + { + Z_Free(th); + } + + return false; // Continue iteration. +} + +static int readCeiling(ceiling_t *ceiling, Reader *reader) +{ +/* Original Heretic format: +typedef struct { + thinker_t thinker; ///< 12 bytes + ceilingtype_e type; ///< 32bit int + Sector* sector; + fixed_t bottomheight, topheight; + fixed_t speed; + dd_bool crush; + int direction; /// 1= up, 0= waiting, -1= down + int tag; + int olddirection; +} v13_ceiling_t; +*/ + + // Padding at the start (an old thinker_t struct) + byte temp[SIZEOF_V13_THINKER_T]; + Reader_Read(reader, &temp, SIZEOF_V13_THINKER_T); + + // Start of used data members. + ceiling->type = ceilingtype_e(Reader_ReadInt32(reader)); + + // A 32bit pointer to sector, serialized. + ceiling->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(reader)); + DENG_ASSERT(ceiling->sector != 0); + + ceiling->bottomHeight = FIX2FLT(Reader_ReadInt32(reader)); + ceiling->topHeight = FIX2FLT(Reader_ReadInt32(reader)); + ceiling->speed = FIX2FLT(Reader_ReadInt32(reader)); + ceiling->crush = Reader_ReadInt32(reader); + ceiling->state = (Reader_ReadInt32(reader) == -1? CS_DOWN : CS_UP); + ceiling->tag = Reader_ReadInt32(reader); + ceiling->oldState = (Reader_ReadInt32(reader) == -1? CS_DOWN : CS_UP); + + ceiling->thinker.function = T_MoveCeiling; + if(!(temp + V13_THINKER_T_FUNC_OFFSET)) + Thinker_SetStasis(&ceiling->thinker, true); + + P_ToXSector(ceiling->sector)->specialData = ceiling; + return true; // Add this thinker. +} + +static int readDoor(door_t *door, Reader *reader) +{ +/* Original Heretic format: +typedef struct { + thinker_t thinker; // was 12 bytes + doortype_e type; // was 32bit int + Sector *sector; + fixed_t topheight; + fixed_t speed; + int direction; // 1 = up, 0 = waiting at top, -1 = down + int topwait; // tics to wait at the top + // (keep in case a door going down is reset) + int topcountdown; // when it reaches 0, start going down +} v13_vldoor_t; +*/ + // Padding at the start (an old thinker_t struct) + Reader_Read(reader, NULL, SIZEOF_V13_THINKER_T); + + // Start of used data members. + door->type = doortype_e(Reader_ReadInt32(reader)); + + // A 32bit pointer to sector, serialized. + door->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(reader)); + DENG_ASSERT(door->sector != 0); + + door->topHeight = FIX2FLT(Reader_ReadInt32(reader)); + door->speed = FIX2FLT(Reader_ReadInt32(reader)); + door->state = doorstate_e(Reader_ReadInt32(reader)); + door->topWait = Reader_ReadInt32(reader); + door->topCountDown = Reader_ReadInt32(reader); + + door->thinker.function = T_Door; + + P_ToXSector(door->sector)->specialData = door; + return true; // Add this thinker. +} + +static int readFloor(floor_t *floor, Reader *reader) +{ +/* Original Heretic format: +typedef struct { + thinker_t thinker; // was 12 bytes + floortype_e type; // was 32bit int + dd_bool crush; + Sector *sector; + int direction; + int newspecial; + short texture; + fixed_t floordestheight; + fixed_t speed; +} v13_floormove_t; +*/ + + // Padding at the start (an old thinker_t struct) + Reader_Read(reader, NULL, SIZEOF_V13_THINKER_T); + + // Start of used data members. + floor->type = floortype_e(Reader_ReadInt32(reader)); + floor->crush = Reader_ReadInt32(reader); + + // A 32bit pointer to sector, serialized. + floor->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(reader)); + DENG_ASSERT(floor->sector != 0); + + floor->state = floorstate_e(Reader_ReadInt32(reader)); + floor->newSpecial = Reader_ReadInt32(reader); + + Uri *newTextureUrn = readTextureUrn(reader, "Flats"); + floor->material = DD_MaterialForTextureUri(newTextureUrn); + Uri_Delete(newTextureUrn); + + floor->floorDestHeight = FIX2FLT(Reader_ReadInt32(reader)); + floor->speed = FIX2FLT(Reader_ReadInt32(reader)); + + floor->thinker.function = T_MoveFloor; + + P_ToXSector(floor->sector)->specialData = floor; + return true; // Add this thinker. +} + +static int readPlat(plat_t *plat, Reader *reader) +{ +/* Original Heretic format: +typedef struct { + thinker_t thinker; // was 12 bytes + Sector *sector; + fixed_t speed; + fixed_t low; + fixed_t high; + int wait; + int count; + platstate_e status; // was 32bit int + platstate_e oldStatus; // was 32bit int + dd_bool crush; + int tag; + plattype_e type; // was 32bit int +} v13_plat_t; +*/ + byte temp[SIZEOF_V13_THINKER_T]; + // Padding at the start (an old thinker_t struct) + Reader_Read(reader, &temp, SIZEOF_V13_THINKER_T); + + // Start of used data members. + // A 32bit pointer to sector, serialized. + plat->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(reader)); + DENG_ASSERT(plat->sector != 0); + + plat->speed = FIX2FLT(Reader_ReadInt32(reader)); + plat->low = FIX2FLT(Reader_ReadInt32(reader)); + plat->high = FIX2FLT(Reader_ReadInt32(reader)); + plat->wait = Reader_ReadInt32(reader); + plat->count = Reader_ReadInt32(reader); + plat->state = platstate_e(Reader_ReadInt32(reader)); + plat->oldState = platstate_e(Reader_ReadInt32(reader)); + plat->crush = Reader_ReadInt32(reader); + plat->tag = Reader_ReadInt32(reader); + plat->type = plattype_e(Reader_ReadInt32(reader)); + + plat->thinker.function = T_PlatRaise; + if(!(temp + V13_THINKER_T_FUNC_OFFSET)) + Thinker_SetStasis(&plat->thinker, true); + + P_ToXSector(plat->sector)->specialData = plat; + return true; // Add this thinker. +} + +static int readFlash(lightflash_t *flash, Reader *reader) +{ +/* Original Heretic format: +typedef struct { + thinker_t thinker; // was 12 bytes + Sector *sector; + int count; + int maxLight; + int minLight; + int maxTime; + int minTime; +} v13_lightflash_t; +*/ + // Padding at the start (an old thinker_t struct) + Reader_Read(reader, NULL, SIZEOF_V13_THINKER_T); + + // Start of used data members. + // A 32bit pointer to sector, serialized. + flash->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(reader)); + DENG_ASSERT(flash->sector != 0); + + flash->count = Reader_ReadInt32(reader); + flash->maxLight = (float) Reader_ReadInt32(reader) / 255.0f; + flash->minLight = (float) Reader_ReadInt32(reader) / 255.0f; + flash->maxTime = Reader_ReadInt32(reader); + flash->minTime = Reader_ReadInt32(reader); + + flash->thinker.function = (thinkfunc_t) T_LightFlash; + return true; // Add this thinker. +} + +static int readStrobe(strobe_t *strobe, Reader *reader) +{ +/* Original Heretic format: +typedef struct { + thinker_t thinker; // was 12 bytes + Sector *sector; + int count; + int minLight; + int maxLight; + int darkTime; + int brightTime; +} v13_strobe_t; +*/ + // Padding at the start (an old thinker_t struct) + Reader_Read(reader, NULL, SIZEOF_V13_THINKER_T); + + // Start of used data members. + // A 32bit pointer to sector, serialized. + strobe->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(reader)); + DENG_ASSERT(strobe->sector != 0); + + strobe->count = Reader_ReadInt32(reader); + strobe->minLight = (float) Reader_ReadInt32(reader) / 255.0f; + strobe->maxLight = (float) Reader_ReadInt32(reader) / 255.0f; + strobe->darkTime = Reader_ReadInt32(reader); + strobe->brightTime = Reader_ReadInt32(reader); + + strobe->thinker.function = (thinkfunc_t) T_StrobeFlash; + return true; // Add this thinker. +} + +static int readGlow(glow_t *glow, Reader *reader) +{ +/* Original Heretic format: +typedef struct { + thinker_t thinker; // was 12 bytes + Sector *sector; + int minLight; + int maxLight; + int direction; +} v13_glow_t; +*/ + // Padding at the start (an old thinker_t struct) + Reader_Read(reader, NULL, SIZEOF_V13_THINKER_T); + + // Start of used data members. + // A 32bit pointer to sector, serialized. + glow->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(reader)); + DENG_ASSERT(glow->sector != 0); + + glow->minLight = (float) Reader_ReadInt32(reader) / 255.0f; + glow->maxLight = (float) Reader_ReadInt32(reader) / 255.0f; + glow->direction = Reader_ReadInt32(reader); + + glow->thinker.function = (thinkfunc_t) T_Glow; + return true; // Add this thinker. +} + +static void SaveInfo_Read_Hr_v13(SaveInfo *info, Reader *reader) +{ + DENG_ASSERT(info != 0); + + char descBuf[24]; + Reader_Read(reader, descBuf, 24); + info->setUserDescription(de::String(descBuf, 24)); + + char vcheck[16 + 1]; + Reader_Read(reader, vcheck, 16); vcheck[16] = 0; + //DENG_ASSERT(!strncmp(vcheck, "version ", 8)); // Ensure save state format has been recognised by now. + info->setVersion(atoi(&vcheck[8])); + + GameRuleset rules; de::zap(rules); + rules.skill = (skillmode_t) Reader_ReadByte(reader); + // Interpret skill levels outside the normal range as "spawn no things". + if(rules.skill < SM_BABY || rules.skill >= NUM_SKILL_MODES) + { + rules.skill = SM_NOTHINGS; + } + info->setGameRules(rules); + + uint episode = Reader_ReadByte(reader) - 1; + uint map = Reader_ReadByte(reader) - 1; + info->setMapUri(G_ComposeMapUri(episode, map)); + + SaveInfo::Players players; de::zap(players); + for(int i = 0; i < 4; ++i) + { + players[i] = Reader_ReadByte(reader); + } + info->setPlayers(players); + + // Get the map time. + int a = Reader_ReadByte(reader); + int b = Reader_ReadByte(reader); + int c = Reader_ReadByte(reader); + info->setMapTime((a << 16) + (b << 8) + c); + + info->setMagic(0); // Initialize with *something*. + + /// @note Kludge: Assume the current game mode. + GameInfo gameInfo; + DD_GameInfo(&gameInfo); + info->setGameIdentityKey(Str_Text(gameInfo.identityKey)); + /// Kludge end. + + info->setSessionId(0); // None. +} + +static bool SV_OpenFile_Hr_v13(de::Path filePath) +{ + DENG_ASSERT(saveBuffer == 0); + if(!M_ReadFile(de::NativePath(filePath).expand().toUtf8().constData(), (char **)&saveBuffer)) + { + return false; + } + savePtr = saveBuffer; + return true; +} + +static void SV_CloseFile_Hr_v13() +{ + if(!saveBuffer) return; + Z_Free(saveBuffer); + saveBuffer = savePtr = 0; +} + +static Reader *SV_NewReader_Hr_v13() +{ + if(!saveBuffer) return 0; + return Reader_NewWithCallbacks(sri8, sri16, sri32, NULL, srd); +} + +DENG2_PIMPL(HereticV13GameStateReader) +{ + Reader *reader; + + Instance(Public *i) + : Base(i) + , reader(0) + {} + + /// Assumes the reader is currently positioned at the start of the stream. + void seekToGameState() + { + // Read the header again. + /// @todo seek straight to the game state. + SaveInfo *tmp = new SaveInfo; + SaveInfo_Read_Hr_v13(tmp, reader); + delete tmp; + } + + void readPlayers() + { + for(int i = 0; i < 4; ++i) + { + if(!players[i].plr->inGame) continue; + + readPlayer(players + i, reader); + players[i].plr->mo = 0; // Will be set when unarc thinker. + players[i].attacker = 0; + for(int k = 0; k < NUMPSPRITES; ++k) + { + player_t *plr = &players[i]; + + if(plr->pSprites[k].state) + { + plr->pSprites[k].state = &STATES[PTR2INT(plr->pSprites[k].state)]; + } + } + } + } + + void readMap() + { + // Do sectors. + for(int i = 0; i < numsectors; ++i) + { + Sector *sec = (Sector *)P_ToPtr(DMU_SECTOR, i); + xsector_t *xsec = P_ToXSector(sec); + + P_SetDoublep(sec, DMU_FLOOR_HEIGHT, (coord_t)Reader_ReadInt16(reader)); + P_SetDoublep(sec, DMU_CEILING_HEIGHT, (coord_t)Reader_ReadInt16(reader)); + + Uri *floorTextureUrn = readTextureUrn(reader, "Flats"); + P_SetPtrp(sec, DMU_FLOOR_MATERIAL, DD_MaterialForTextureUri(floorTextureUrn)); + Uri_Delete(floorTextureUrn); + + Uri *ceilingTextureUrn = readTextureUrn(reader, "Flats"); + P_SetPtrp(sec, DMU_CEILING_MATERIAL, DD_MaterialForTextureUri(ceilingTextureUrn)); + Uri_Delete(ceilingTextureUrn); + + P_SetFloatp(sec, DMU_LIGHT_LEVEL, (float) (Reader_ReadInt16(reader)) / 255.0f); + + xsec->special = Reader_ReadInt16(reader); // needed? + /*xsec->tag = **/Reader_ReadInt16(reader); // needed? + xsec->specialData = 0; + xsec->soundTarget = 0; + } + + // Do lines. + for(int i = 0; i < numlines; ++i) + { + Line *line = (Line *)P_ToPtr(DMU_LINE, i); + xline_t *xline = P_ToXLine(line); + + xline->flags = Reader_ReadInt16(reader); + xline->special = Reader_ReadInt16(reader); + /*xline->tag =*/Reader_ReadInt16(reader); + + for(int k = 0; k < 2; ++k) + { + Side *sdef = (Side *)P_GetPtrp(line, k == 0? DMU_FRONT : DMU_BACK); + if(!sdef) continue; + + fixed_t offx = Reader_ReadInt16(reader) << FRACBITS; + fixed_t offy = Reader_ReadInt16(reader) << FRACBITS; + P_SetFixedp(sdef, DMU_TOP_MATERIAL_OFFSET_X, offx); + P_SetFixedp(sdef, DMU_TOP_MATERIAL_OFFSET_Y, offy); + P_SetFixedp(sdef, DMU_MIDDLE_MATERIAL_OFFSET_X, offx); + P_SetFixedp(sdef, DMU_MIDDLE_MATERIAL_OFFSET_Y, offy); + P_SetFixedp(sdef, DMU_BOTTOM_MATERIAL_OFFSET_X, offx); + P_SetFixedp(sdef, DMU_BOTTOM_MATERIAL_OFFSET_Y, offy); + + Uri *topTextureUrn = readTextureUrn(reader, "Textures"); + P_SetPtrp(sdef, DMU_TOP_MATERIAL, DD_MaterialForTextureUri(topTextureUrn)); + Uri_Delete(topTextureUrn); + + Uri *bottomTextureUrn = readTextureUrn(reader, "Textures"); + P_SetPtrp(sdef, DMU_BOTTOM_MATERIAL, DD_MaterialForTextureUri(bottomTextureUrn)); + Uri_Delete(bottomTextureUrn); + + Uri *middleTextureUrn = readTextureUrn(reader, "Textures"); + P_SetPtrp(sdef, DMU_MIDDLE_MATERIAL, DD_MaterialForTextureUri(middleTextureUrn)); + Uri_Delete(middleTextureUrn); + } + } + + readThinkers(); + readSpecials(); + + if(Reader_ReadByte(reader) != 0x1d) + { + Reader_Delete(reader); reader = 0; + SV_CloseFile_Hr_v13(); + + throw ReadError("HereticV13GameStateReader", "Bad savegame (consistency test failed!)"); + } + } + + void readThinkers() + { + // Remove all the current thinkers. + Thinker_Iterate(NULL, removeThinker, NULL); + Thinker_Init(); + + // read in saved thinkers + byte tclass; + while((tclass = Reader_ReadByte(reader)) != 0/*TC_END*/) + { + switch(tclass) + { + case 1: // TC_MOBJ + readMobj(reader); + break; + + default: + throw ReadError("HereticV13GameStateReader", "Unknown tclass #" + de::String::number(tclass) + "in savegame"); + } + } + } + + enum { + tc_ceiling, + tc_door, + tc_floor, + tc_plat, + tc_flash, + tc_strobe, + tc_glow, + tc_endspecials + }; + + /** + * Things to handle: + * + * T_MoveCeiling, (ceiling_t: Sector * swizzle), - active list + * T_Door, (door_t: Sector * swizzle), + * T_MoveFloor, (floor_t: Sector * swizzle), + * T_LightFlash, (lightflash_t: Sector * swizzle), + * T_StrobeFlash, (strobe_t: Sector *), + * T_Glow, (glow_t: Sector *), + * T_PlatRaise, (plat_t: Sector *), - active list + */ + void readSpecials() + { + // Read in saved thinkers. + byte tclass; + while((tclass = Reader_ReadByte(reader)) != tc_endspecials) + { + switch(tclass) + { + case tc_ceiling: { + ceiling_t *ceiling = (ceiling_t *)Z_Calloc(sizeof(*ceiling), PU_MAP, NULL); + + readCeiling(ceiling, reader); + + Thinker_Add(&ceiling->thinker); + break; } + + case tc_door: { + door_t *door = (door_t *)Z_Calloc(sizeof(*door), PU_MAP, NULL); + + readDoor(door, reader); + + Thinker_Add(&door->thinker); + break; } + + case tc_floor: { + floor_t *floor = (floor_t *)Z_Calloc(sizeof(*floor), PU_MAP, NULL); + + readFloor(floor, reader); + + Thinker_Add(&floor->thinker); + break; } + + case tc_plat: { + plat_t *plat = (plat_t *)Z_Calloc(sizeof(*plat), PU_MAP, NULL); + + readPlat(plat, reader); + + Thinker_Add(&plat->thinker); + break; } + + case tc_flash: { + lightflash_t *flash = (lightflash_t *)Z_Calloc(sizeof(*flash), PU_MAP, NULL); + + readFlash(flash, reader); + + Thinker_Add(&flash->thinker); + break; } + + case tc_strobe: { + strobe_t *strobe = (strobe_t *)Z_Calloc(sizeof(*strobe), PU_MAP, NULL); + + readStrobe(strobe, reader); + + Thinker_Add(&strobe->thinker); + break; } + + case tc_glow: { + glow_t *glow = (glow_t *)Z_Calloc(sizeof(*glow), PU_MAP, NULL); + + readGlow(glow, reader); + + Thinker_Add(&glow->thinker); + break; } + + default: + throw ReadError("HereticV13GameStateReader", "Unknown tclass #" + de::String::number(tclass) + "in savegame"); + } + } + } +}; + +HereticV13GameStateReader::HereticV13GameStateReader() : d(new Instance(this)) +{} + +HereticV13GameStateReader::~HereticV13GameStateReader() +{} + +bool HereticV13GameStateReader::recognize(SaveInfo &info) // static +{ + de::Path const path = SV_SavePath() / info.fileName(); + + if(!SV_ExistingFile(path)) return false; + if(!SV_OpenFile_Hr_v13(path)) return false; + + Reader *reader = SV_NewReader_Hr_v13(); + bool result = false; + + /// @todo Use the 'version' string as the "magic" identifier. + /*char vcheck[VERSIONSIZE]; + std::memset(vcheck, 0, sizeof(vcheck)); + Reader_Read(svReader, vcheck, sizeof(vcheck)); + + if(strncmp(vcheck, "version ", 8))*/ + { + SaveInfo_Read_Hr_v13(&info, reader); + result = (info.version() == 130); + } + + Reader_Delete(reader); reader = 0; + SV_CloseFile_Hr_v13(); + + return result; +} + +IGameStateReader *HereticV13GameStateReader::make() +{ + return new HereticV13GameStateReader; +} + +void HereticV13GameStateReader::read(SaveInfo &info) +{ + de::Path const path = SV_SavePath() / info.fileName(); + + if(!SV_OpenFile_Hr_v13(path)) + { + throw FileAccessError("HereticV13GameStateReader", "Failed opening \"" + de::NativePath(path).pretty() + "\""); + } + + d->reader = SV_NewReader_Hr_v13(); + + d->seekToGameState(); + + // We don't want to see a briefing if we're loading a save game. + briefDisabled = true; + + // Load a base map. + G_NewGame(info.mapUri(), 0/*not saved??*/, &info.gameRules()); + G_SetGameAction(GA_NONE); /// @todo Necessary? + + // Recreate map state. + mapTime = info.mapTime(); + + d->readPlayers(); + d->readMap(); + + Reader_Delete(d->reader); d->reader = 0; + SV_CloseFile_Hr_v13(); + + // Material scrollers must be spawned. + P_SpawnAllMaterialOriginScrollers(); + + // Let the engine know where the local players are now. + for(int i = 0; i < MAXPLAYERS; ++i) + { + R_UpdateConsoleView(i); + } + + // Inform the engine that map setup must be performed once more. + R_SetupMap(0, 0); +} diff --git a/doomsday/plugins/heretic/src/m_cheat.c b/doomsday/plugins/heretic/src/m_cheat.c index debb5cb3da..ecb8738c28 100644 --- a/doomsday/plugins/heretic/src/m_cheat.c +++ b/doomsday/plugins/heretic/src/m_cheat.c @@ -391,26 +391,25 @@ D_CMD(CheatGive) if(G_GameState() != GS_MAP) { - App_Log(DE2_SCR_ERROR, "Can only \"give\" when in a game!\n"); + App_Log(DE2_SCR_ERROR, "Can only \"give\" when in a game!"); return true; } if(argc != 2 && argc != 3) { - App_Log(DE2_SCR_NOTE, "Usage:\n give (stuff)\n"); - App_Log(DE2_LOG_SCR, " give (stuff) (plr)\n"); + App_Log(DE2_SCR_NOTE, "Usage:\n give (stuff)\n give (stuff) (plr)"); App_Log(DE2_LOG_SCR, "Stuff consists of one or more of (type:id). " - "If no id; give all of type:\n"); - App_Log(DE2_LOG_SCR, " a - ammo\n"); - App_Log(DE2_LOG_SCR, " i - items\n"); - App_Log(DE2_LOG_SCR, " h - health\n"); - App_Log(DE2_LOG_SCR, " k - keys\n"); - App_Log(DE2_LOG_SCR, " p - backpack full of ammo\n"); - App_Log(DE2_LOG_SCR, " r - armor\n"); - App_Log(DE2_LOG_SCR, " t - tome of power\n"); - App_Log(DE2_LOG_SCR, " w - weapons\n"); - App_Log(DE2_LOG_SCR, "Example: 'give ikw' gives items, keys and weapons.\n"); - App_Log(DE2_LOG_SCR, "Example: 'give w2k1' gives weapon two and key one.\n"); + "If no id; give all of type:"); + App_Log(DE2_LOG_SCR, " a - ammo"); + App_Log(DE2_LOG_SCR, " i - items"); + App_Log(DE2_LOG_SCR, " h - health"); + App_Log(DE2_LOG_SCR, " k - keys"); + App_Log(DE2_LOG_SCR, " p - backpack full of ammo"); + App_Log(DE2_LOG_SCR, " r - armor"); + App_Log(DE2_LOG_SCR, " t - tome of power"); + App_Log(DE2_LOG_SCR, " w - weapons"); + App_Log(DE2_LOG_SCR, "Example: 'give ikw' gives items, keys and weapons."); + App_Log(DE2_LOG_SCR, "Example: 'give w2k1' gives weapon two and key one."); return true; } @@ -459,7 +458,7 @@ D_CMD(CheatGive) i += end - &buf[i+1]; if(idx < AT_FIRST || idx >= NUM_AMMO_TYPES) { - App_Log(DE2_SCR_ERROR, "Unknown ammo #%d (valid range %d-%d)\n", + App_Log(DE2_SCR_ERROR, "Unknown ammo #%d (valid range %d-%d)", (int)idx, AT_FIRST, NUM_AMMO_TYPES-1); break; } @@ -486,7 +485,7 @@ D_CMD(CheatGive) i += end - &buf[i+1]; if(idx < IIT_FIRST || idx >= NUM_INVENTORYITEM_TYPES) { - App_Log(DE2_SCR_ERROR, "Unknown item #%d (valid range %d-%d)\n", + App_Log(DE2_SCR_ERROR, "Unknown item #%d (valid range %d-%d)", (int)idx, IIT_FIRST, NUM_INVENTORYITEM_TYPES-1); break; } @@ -536,7 +535,7 @@ D_CMD(CheatGive) i += end - &buf[i+1]; if(idx < KT_FIRST || idx >= NUM_KEY_TYPES) { - App_Log(DE2_SCR_ERROR, "Unknown key #%d (valid range %d-%d)\n", + App_Log(DE2_SCR_ERROR, "Unknown key #%d (valid range %d-%d)", (int)idx, KT_FIRST, NUM_KEY_TYPES-1); break; } @@ -571,7 +570,7 @@ D_CMD(CheatGive) i += end - &buf[i+1]; if(idx < 0 || idx >= 3) { - App_Log(DE2_SCR_ERROR, "Unknown armor type #%d (valid range %d-%d)\n", + App_Log(DE2_SCR_ERROR, "Unknown armor type #%d (valid range %d-%d)", (int)idx, 0, 3-1); break; } @@ -610,7 +609,7 @@ D_CMD(CheatGive) i += end - &buf[i+1]; if(idx < WT_FIRST || idx >= NUM_WEAPON_TYPES) { - App_Log(DE2_SCR_ERROR, "Unknown weapon #%d (valid range %d-%d)\n", + App_Log(DE2_SCR_ERROR, "Unknown weapon #%d (valid range %d-%d)", (int)idx, WT_FIRST, NUM_WEAPON_TYPES-1); break; } @@ -626,7 +625,7 @@ D_CMD(CheatGive) break; default: // Unrecognized. - App_Log(DE2_SCR_ERROR, "Cannot give '%c': unknown letter\n", buf[i]); + App_Log(DE2_SCR_ERROR, "Cannot give '%c': unknown letter", buf[i]); break; } } @@ -717,7 +716,7 @@ D_CMD(CheatLeaveMap) if(G_GameState() != GS_MAP) { S_LocalSound(SFX_CHAT, NULL); - App_Log(DE2_LOG_MAP | DE2_LOG_ERROR, "Can only exit a map when in a game!\n"); + App_Log(DE2_LOG_MAP | DE2_LOG_ERROR, "Can only exit a map when in a game!"); return true; } diff --git a/doomsday/plugins/heretic/src/p_oldsvg.cpp b/doomsday/plugins/heretic/src/p_oldsvg.cpp deleted file mode 100644 index 93d96360ca..0000000000 --- a/doomsday/plugins/heretic/src/p_oldsvg.cpp +++ /dev/null @@ -1,1016 +0,0 @@ -/** @file p_oldsvg.cpp Heretic ver 1.3 save game reader. - * - * @authors Copyright © 2003-2013 Jaakko Keränen - * @authors Copyright © 2006-2013 Daniel Swanson - * @authors Copyright © 1999 Activision - * - * @par License - * GPL: http://www.gnu.org/licenses/gpl.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. This program is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General - * Public License for more details. You should have received a copy of the GNU - * General Public License along with this program; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include "jheretic.h" -#include "p_oldsvg.h" - -#include "am_map.h" -#include "dmu_lib.h" -#include "hu_inventory.h" -#include "p_ceiling.h" -#include "p_door.h" -#include "p_floor.h" -#include "p_inventory.h" -#include "p_map.h" -#include "p_mapsetup.h" -#include "p_plat.h" -#include "p_saveio.h" -#include "p_saveg.h" -#include "p_tick.h" -#include "r_common.h" // R_UpdateConsoleView -#include -#include -#include - -// Do NOT change this: -#define V13_SAVE_VERSION 130 ///< Version number associated with a recognised heretic.exe game save state. -#define V13_SAVESTRINGSIZE 24 -#define VERSIONSIZE 16 -#define SAVE_GAME_TERMINATOR 0x1d - -#define FF_FRAMEMASK 0x7fff - -#define SIZEOF_V13_THINKER_T 12 -#define V13_THINKER_T_FUNC_OFFSET 8 - -static byte *savePtr; -static byte *saveBuffer; -static Reader *svReader; - -static dd_bool SV_OpenFile_Hr_v13(char const *filePath); -static void SV_CloseFile_Hr_v13(); -static Reader *SV_NewReader_Hr_v13(); - -static void SaveInfo_Read_Hr_v13(SaveInfo *info, Reader *reader); - -static char sri8(Reader *r) -{ - if(!r) return 0; - savePtr++; - return *(char *) (savePtr - 1); -} - -static short sri16(Reader *r) -{ - if(!r) return 0; - savePtr += 2; - return *(int16_t *) (savePtr - 2); -} - -static int sri32(Reader *r) -{ - if(!r) return 0; - savePtr += 4; - return *(int32_t *) (savePtr - 4); -} - -static void srd(Reader *r, char *data, int len) -{ - if(!r) return; - if(data) - { - std::memcpy(data, savePtr, len); - } - savePtr += len; -} - -static void SV_v13_ReadPlayer(player_t *pl) -{ - int plrnum = pl - players; - ddplayer_t *ddpl = pl->plr; - - Reader_ReadInt32(svReader); // mo - - pl->playerState = playerstate_t(Reader_ReadInt32(svReader)); - DENG_ASSERT(pl->playerState >= PST_LIVE && pl->playerState <= PST_REBORN); - - byte junk[12]; - Reader_Read(svReader, junk, 10); // ticcmd_t - - pl->viewZ = FIX2FLT(Reader_ReadInt32(svReader)); - pl->viewHeight = FIX2FLT(Reader_ReadInt32(svReader)); - pl->viewHeightDelta = FIX2FLT(Reader_ReadInt32(svReader)); - pl->bob = FIX2FLT(Reader_ReadInt32(svReader)); - pl->flyHeight = Reader_ReadInt32(svReader); - ddpl->lookDir = Reader_ReadInt32(svReader); - pl->centering = Reader_ReadInt32(svReader); - pl->health = Reader_ReadInt32(svReader); - pl->armorPoints = Reader_ReadInt32(svReader); - pl->armorType = Reader_ReadInt32(svReader); - - P_InventoryEmpty(plrnum); - for(int i = 0; i < 14; ++i) - { - inventoryitemtype_t type = inventoryitemtype_t(Reader_ReadInt32(svReader)); - DENG_ASSERT(type >= IIT_NONE && type < NUM_INVENTORYITEM_TYPES); - int count = Reader_ReadInt32(svReader); - - for(int k = 0; k < count; ++k) - { - P_InventoryGive(plrnum, type, true); - } - } - - P_InventorySetReadyItem(plrnum, inventoryitemtype_t(Reader_ReadInt32(svReader))); - Hu_InventorySelect(plrnum, P_InventoryReadyItem(plrnum)); - /*pl->artifactCount =*/ Reader_ReadInt32(svReader); - /*pl->inventorySlotNum =*/ Reader_ReadInt32(svReader); - - memset(pl->powers, 0, sizeof(pl->powers)); - /*pl->powers[pw_NONE] =*/ Reader_ReadInt32(svReader); - pl->powers[PT_INVULNERABILITY] = Reader_ReadInt32(svReader); - pl->powers[PT_INVISIBILITY] = Reader_ReadInt32(svReader); - pl->powers[PT_ALLMAP] = Reader_ReadInt32(svReader); - if(pl->powers[PT_ALLMAP]) - { - ST_RevealAutomap(pl - players, true); - } - pl->powers[PT_INFRARED] = Reader_ReadInt32(svReader); - pl->powers[PT_WEAPONLEVEL2] = Reader_ReadInt32(svReader); - pl->powers[PT_FLIGHT] = Reader_ReadInt32(svReader); - pl->powers[PT_SHIELD] = Reader_ReadInt32(svReader); - pl->powers[PT_HEALTH2] = Reader_ReadInt32(svReader); - - memset(pl->keys, 0, sizeof(pl->keys)); - pl->keys[KT_YELLOW] = !!Reader_ReadInt32(svReader); - pl->keys[KT_GREEN] = !!Reader_ReadInt32(svReader); - pl->keys[KT_BLUE] = !!Reader_ReadInt32(svReader); - - pl->backpack = Reader_ReadInt32(svReader); - - memset(pl->frags, 0, sizeof(pl->frags)); - pl->frags[0] = Reader_ReadInt32(svReader); - pl->frags[1] = Reader_ReadInt32(svReader); - pl->frags[2] = Reader_ReadInt32(svReader); - pl->frags[3] = Reader_ReadInt32(svReader); - - int readyWeapon = Reader_ReadInt32(svReader); - pl->readyWeapon = readyWeapon == 10? WT_NOCHANGE : weapontype_t(readyWeapon); - - int pendingWeapon = Reader_ReadInt32(svReader); - pl->pendingWeapon = pendingWeapon == 10? WT_NOCHANGE : weapontype_t(pendingWeapon); - - // Owned weapons. - memset(pl->weapons, 0, sizeof(pl->weapons)); - pl->weapons[WT_FIRST ].owned = !!Reader_ReadInt32(svReader); - pl->weapons[WT_SECOND ].owned = !!Reader_ReadInt32(svReader); - pl->weapons[WT_THIRD ].owned = !!Reader_ReadInt32(svReader); - pl->weapons[WT_FOURTH ].owned = !!Reader_ReadInt32(svReader); - pl->weapons[WT_FIFTH ].owned = !!Reader_ReadInt32(svReader); - pl->weapons[WT_SIXTH ].owned = !!Reader_ReadInt32(svReader); - pl->weapons[WT_SEVENTH].owned = !!Reader_ReadInt32(svReader); - pl->weapons[WT_EIGHTH ].owned = !!Reader_ReadInt32(svReader); - /*pl->weapons[wp_beak ].owned = !!*/ Reader_ReadInt32(svReader); - - memset(pl->ammo, 0, sizeof(pl->ammo)); - pl->ammo[AT_CRYSTAL].owned = Reader_ReadInt32(svReader); - pl->ammo[AT_ARROW ].owned = Reader_ReadInt32(svReader); - pl->ammo[AT_ORB ].owned = Reader_ReadInt32(svReader); - pl->ammo[AT_RUNE ].owned = Reader_ReadInt32(svReader); - pl->ammo[AT_FIREORB].owned = Reader_ReadInt32(svReader); - pl->ammo[AT_MSPHERE].owned = Reader_ReadInt32(svReader); - pl->ammo[AT_CRYSTAL].max = Reader_ReadInt32(svReader); - pl->ammo[AT_ARROW ].max = Reader_ReadInt32(svReader); - pl->ammo[AT_ORB ].max = Reader_ReadInt32(svReader); - pl->ammo[AT_RUNE ].max = Reader_ReadInt32(svReader); - pl->ammo[AT_FIREORB].max = Reader_ReadInt32(svReader); - pl->ammo[AT_MSPHERE].max = Reader_ReadInt32(svReader); - - pl->attackDown = Reader_ReadInt32(svReader); - pl->useDown = Reader_ReadInt32(svReader); - pl->cheats = Reader_ReadInt32(svReader); - pl->refire = Reader_ReadInt32(svReader); - pl->killCount = Reader_ReadInt32(svReader); - pl->itemCount = Reader_ReadInt32(svReader); - pl->secretCount = Reader_ReadInt32(svReader); - /*pl->message =*/ Reader_ReadInt32(svReader); // char* - /*pl->messageTics =*/ Reader_ReadInt32(svReader); // int32 - pl->damageCount = Reader_ReadInt32(svReader); - pl->bonusCount = Reader_ReadInt32(svReader); - pl->flameCount = Reader_ReadInt32(svReader); - /*pl->attacker =*/ Reader_ReadInt32(svReader); // mobj_t* - - ddpl->extraLight = Reader_ReadInt32(svReader); - ddpl->fixedColorMap = Reader_ReadInt32(svReader); - - pl->colorMap = Reader_ReadInt32(svReader); - - for(int i = 0; i < 2; ++i) - { - pspdef_t *psp = &pl->pSprites[i]; - - psp->state = INT2PTR(state_t, Reader_ReadInt32(svReader)); - psp->tics = Reader_ReadInt32(svReader); - psp->pos[VX] = FIX2FLT(Reader_ReadInt32(svReader)); - psp->pos[VY] = FIX2FLT(Reader_ReadInt32(svReader)); - } - - pl->didSecret = !!Reader_ReadInt32(svReader); - pl->morphTics = Reader_ReadInt32(svReader); - pl->chickenPeck = Reader_ReadInt32(svReader); - /*pl->rain1 =*/ Reader_ReadInt32(svReader); // mobj_t* - /*pl->rain2 =*/ Reader_ReadInt32(svReader); // mobj_t* -} - -static void SV_v13_ReadMobj() -{ - // The thinker was 3 ints long. - Reader_ReadInt32(svReader); - Reader_ReadInt32(svReader); - Reader_ReadInt32(svReader); - - coord_t pos[3]; - pos[VX] = FIX2FLT(Reader_ReadInt32(svReader)); - pos[VY] = FIX2FLT(Reader_ReadInt32(svReader)); - pos[VZ] = FIX2FLT(Reader_ReadInt32(svReader)); - - // Sector links. - Reader_ReadInt32(svReader); - Reader_ReadInt32(svReader); - - angle_t angle = (angle_t) (ANG45 * (Reader_ReadInt32(svReader) / 45)); - spritenum_t sprite = Reader_ReadInt32(svReader); - - int frame = Reader_ReadInt32(svReader); - frame &= ~FF_FRAMEMASK; // not used anymore. - - // Block links. - Reader_ReadInt32(svReader); - Reader_ReadInt32(svReader); - - // Subsector. - Reader_ReadInt32(svReader); - - coord_t floorz = FIX2FLT(Reader_ReadInt32(svReader)); - coord_t ceilingz = FIX2FLT(Reader_ReadInt32(svReader)); - coord_t radius = FIX2FLT(Reader_ReadInt32(svReader)); - coord_t height = FIX2FLT(Reader_ReadInt32(svReader)); - - coord_t mom[3]; - mom[MX] = FIX2FLT(Reader_ReadInt32(svReader)); - mom[MY] = FIX2FLT(Reader_ReadInt32(svReader)); - mom[MZ] = FIX2FLT(Reader_ReadInt32(svReader)); - - int valid = Reader_ReadInt32(svReader); - int type = Reader_ReadInt32(svReader); - - DENG_ASSERT(type >= 0 && type < Get(DD_NUMMOBJTYPES)); - mobjinfo_t *info = &MOBJINFO[type]; - - int ddflags = 0; - if(info->flags & MF_SOLID) ddflags |= DDMF_SOLID; - if(info->flags2 & MF2_DONTDRAW) ddflags |= DDMF_DONTDRAW; - - /* - * We now have all the information we need to create the mobj. - */ - mobj_t *mo = Mobj_CreateXYZ(P_MobjThinker, pos[VX], pos[VY], pos[VZ], angle, - radius, height, ddflags); - - mo->sprite = sprite; - mo->frame = frame; - mo->floorZ = floorz; - mo->ceilingZ = ceilingz; - mo->mom[MX] = mom[MX]; - mo->mom[MY] = mom[MY]; - mo->mom[MZ] = mom[MZ]; - mo->valid = valid; - mo->type = type; - mo->moveDir = DI_NODIR; - - /* - * Continue reading the mobj data. - */ - Reader_ReadInt32(svReader); // info - - mo->tics = Reader_ReadInt32(svReader); - mo->state = INT2PTR(state_t, Reader_ReadInt32(svReader)); - mo->damage = Reader_ReadInt32(svReader); - mo->flags = Reader_ReadInt32(svReader); - mo->flags2 = Reader_ReadInt32(svReader); - mo->special1 = Reader_ReadInt32(svReader); - mo->special2 = Reader_ReadInt32(svReader); - mo->health = Reader_ReadInt32(svReader); - - // Fix a bunch of kludges in the original Heretic. - switch(mo->type) - { - case MT_MACEFX1: - case MT_MACEFX2: - case MT_MACEFX3: - case MT_HORNRODFX2: - case MT_HEADFX3: - case MT_WHIRLWIND: - case MT_TELEGLITTER: - case MT_TELEGLITTER2: - mo->special3 = mo->health; - mo->health = info->spawnHealth; - break; - - default: break; - } - - mo->moveDir = Reader_ReadInt32(svReader); - mo->moveCount = Reader_ReadInt32(svReader); - Reader_ReadInt32(svReader); // target - mo->reactionTime = Reader_ReadInt32(svReader); - mo->threshold = Reader_ReadInt32(svReader); - mo->player = INT2PTR(player_t, Reader_ReadInt32(svReader)); - mo->lastLook = Reader_ReadInt32(svReader); - - mo->spawnSpot.origin[VX] = (coord_t) Reader_ReadInt16(svReader); - mo->spawnSpot.origin[VY] = (coord_t) Reader_ReadInt16(svReader); - mo->spawnSpot.origin[VZ] = 0; // Initialize with "something". - mo->spawnSpot.angle = (angle_t) (ANG45 * (Reader_ReadInt16(svReader) / 45)); - /*mo->spawnSpot.type = (int)*/ Reader_ReadInt16(svReader); - - int spawnFlags = ((int) Reader_ReadInt16(svReader)) & ~MASK_UNKNOWN_MSF_FLAGS; - // Spawn on the floor by default unless the mobjtype flags override. - spawnFlags |= MSF_Z_FLOOR; - mo->spawnSpot.flags = spawnFlags; - - mo->info = info; - SV_TranslateLegacyMobjFlags(mo, 0); - - mo->state = &STATES[PTR2INT(mo->state)]; - mo->target = NULL; - if(mo->player) - { - mo->player = &players[PTR2INT(mo->player) - 1]; - mo->player->plr->mo = mo; - mo->player->plr->mo->dPlayer = mo->player->plr; - } - P_MobjLink(mo); - mo->floorZ = P_GetDoublep(Mobj_Sector(mo), DMU_FLOOR_HEIGHT); - mo->ceilingZ = P_GetDoublep(Mobj_Sector(mo), DMU_CEILING_HEIGHT); -} - -static void P_v13_UnArchivePlayers() -{ - for(int i = 0; i < 4; ++i) - { - if(!players[i].plr->inGame) continue; - - SV_v13_ReadPlayer(players + i); - players[i].plr->mo = NULL; // Will be set when unarc thinker. - players[i].attacker = NULL; - for(int k = 0; k < NUMPSPRITES; ++k) - { - player_t *plr = &players[i]; - - if(plr->pSprites[k].state) - { - plr->pSprites[k].state = &STATES[PTR2INT(plr->pSprites[k].state)]; - } - } - } -} - -static Uri *readTextureUrn(Reader *reader, char const *schemeName) -{ - DENG_ASSERT(reader != 0 && schemeName != 0); - DENG_UNUSED(reader); - return Uri_NewWithPath2(Str_Text(Str_Appendf(AutoStr_NewStd(), "urn:%s:%i", schemeName, Reader_ReadInt16(svReader))), RC_NULL); -} - -static void P_v13_UnArchiveWorld() -{ - // Do sectors. - for(int i = 0; i < numsectors; ++i) - { - Sector *sec = (Sector *)P_ToPtr(DMU_SECTOR, i); - xsector_t *xsec = P_ToXSector(sec); - - P_SetDoublep(sec, DMU_FLOOR_HEIGHT, (coord_t)Reader_ReadInt16(svReader)); - P_SetDoublep(sec, DMU_CEILING_HEIGHT, (coord_t)Reader_ReadInt16(svReader)); - - Uri *floorTextureUrn = readTextureUrn(svReader, "Flats"); - P_SetPtrp(sec, DMU_FLOOR_MATERIAL, DD_MaterialForTextureUri(floorTextureUrn)); - Uri_Delete(floorTextureUrn); - - Uri *ceilingTextureUrn = readTextureUrn(svReader, "Flats"); - P_SetPtrp(sec, DMU_CEILING_MATERIAL, DD_MaterialForTextureUri(ceilingTextureUrn)); - Uri_Delete(ceilingTextureUrn); - - P_SetFloatp(sec, DMU_LIGHT_LEVEL, (float) (Reader_ReadInt16(svReader)) / 255.0f); - - xsec->special = Reader_ReadInt16(svReader); // needed? - /*xsec->tag = **/Reader_ReadInt16(svReader); // needed? - xsec->specialData = 0; - xsec->soundTarget = 0; - } - - // Do lines. - for(int i = 0; i < numlines; ++i) - { - Line *line = (Line *)P_ToPtr(DMU_LINE, i); - xline_t *xline = P_ToXLine(line); - - xline->flags = Reader_ReadInt16(svReader); - xline->special = Reader_ReadInt16(svReader); - /*xline->tag =*/Reader_ReadInt16(svReader); - - for(int k = 0; k < 2; ++k) - { - Side *sdef = (Side *)P_GetPtrp(line, k == 0? DMU_FRONT : DMU_BACK); - if(!sdef) continue; - - fixed_t offx = Reader_ReadInt16(svReader) << FRACBITS; - fixed_t offy = Reader_ReadInt16(svReader) << FRACBITS; - P_SetFixedp(sdef, DMU_TOP_MATERIAL_OFFSET_X, offx); - P_SetFixedp(sdef, DMU_TOP_MATERIAL_OFFSET_Y, offy); - P_SetFixedp(sdef, DMU_MIDDLE_MATERIAL_OFFSET_X, offx); - P_SetFixedp(sdef, DMU_MIDDLE_MATERIAL_OFFSET_Y, offy); - P_SetFixedp(sdef, DMU_BOTTOM_MATERIAL_OFFSET_X, offx); - P_SetFixedp(sdef, DMU_BOTTOM_MATERIAL_OFFSET_Y, offy); - - Uri *topTextureUrn = readTextureUrn(svReader, "Textures"); - P_SetPtrp(sdef, DMU_TOP_MATERIAL, DD_MaterialForTextureUri(topTextureUrn)); - Uri_Delete(topTextureUrn); - - Uri *bottomTextureUrn = readTextureUrn(svReader, "Textures"); - P_SetPtrp(sdef, DMU_BOTTOM_MATERIAL, DD_MaterialForTextureUri(bottomTextureUrn)); - Uri_Delete(bottomTextureUrn); - - Uri *middleTextureUrn = readTextureUrn(svReader, "Textures"); - P_SetPtrp(sdef, DMU_MIDDLE_MATERIAL, DD_MaterialForTextureUri(middleTextureUrn)); - Uri_Delete(middleTextureUrn); - } - } -} - -static int removeThinker(thinker_t *th, void * /*context*/) -{ - if(th->function == (thinkfunc_t) P_MobjThinker) - { - P_MobjRemove((mobj_t *) th, true); - } - else - { - Z_Free(th); - } - - return false; // Continue iteration. -} - -static void P_v13_UnArchiveThinkers() -{ - // Remove all the current thinkers. - Thinker_Iterate(NULL, removeThinker, NULL); - Thinker_Init(); - - // read in saved thinkers - byte tclass; - while((tclass = Reader_ReadByte(svReader)) != 0/*TC_END*/) - { - switch(tclass) - { - case 1: // TC_MOBJ - SV_v13_ReadMobj(); - break; - - default: - Con_Error("Unknown tclass %i in savegame", tclass); - } - } -} - -static int SV_ReadCeiling(ceiling_t *ceiling) -{ -/* Original Heretic format: -typedef struct { - thinker_t thinker; ///< 12 bytes - ceilingtype_e type; ///< 32bit int - Sector* sector; - fixed_t bottomheight, topheight; - fixed_t speed; - dd_bool crush; - int direction; /// 1= up, 0= waiting, -1= down - int tag; - int olddirection; -} v13_ceiling_t; -*/ - - // Padding at the start (an old thinker_t struct) - byte temp[SIZEOF_V13_THINKER_T]; - Reader_Read(svReader, &temp, SIZEOF_V13_THINKER_T); - - // Start of used data members. - ceiling->type = ceilingtype_e(Reader_ReadInt32(svReader)); - - // A 32bit pointer to sector, serialized. - ceiling->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(svReader)); - if(!ceiling->sector) - Con_Error("tc_ceiling: bad sector number"); - - ceiling->bottomHeight = FIX2FLT(Reader_ReadInt32(svReader)); - ceiling->topHeight = FIX2FLT(Reader_ReadInt32(svReader)); - ceiling->speed = FIX2FLT(Reader_ReadInt32(svReader)); - ceiling->crush = Reader_ReadInt32(svReader); - ceiling->state = (Reader_ReadInt32(svReader) == -1? CS_DOWN : CS_UP); - ceiling->tag = Reader_ReadInt32(svReader); - ceiling->oldState = (Reader_ReadInt32(svReader) == -1? CS_DOWN : CS_UP); - - ceiling->thinker.function = T_MoveCeiling; - if(!(temp + V13_THINKER_T_FUNC_OFFSET)) - Thinker_SetStasis(&ceiling->thinker, true); - - P_ToXSector(ceiling->sector)->specialData = ceiling; - return true; // Add this thinker. -} - -static int SV_ReadDoor(door_t *door) -{ -/* Original Heretic format: -typedef struct { - thinker_t thinker; // was 12 bytes - doortype_e type; // was 32bit int - Sector *sector; - fixed_t topheight; - fixed_t speed; - int direction; // 1 = up, 0 = waiting at top, -1 = down - int topwait; // tics to wait at the top - // (keep in case a door going down is reset) - int topcountdown; // when it reaches 0, start going down -} v13_vldoor_t; -*/ - // Padding at the start (an old thinker_t struct) - Reader_Read(svReader, NULL, SIZEOF_V13_THINKER_T); - - // Start of used data members. - door->type = doortype_e(Reader_ReadInt32(svReader)); - - // A 32bit pointer to sector, serialized. - door->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(svReader)); - if(!door->sector) - Con_Error("tc_door: bad sector number"); - - door->topHeight = FIX2FLT(Reader_ReadInt32(svReader)); - door->speed = FIX2FLT(Reader_ReadInt32(svReader)); - door->state = doorstate_e(Reader_ReadInt32(svReader)); - door->topWait = Reader_ReadInt32(svReader); - door->topCountDown = Reader_ReadInt32(svReader); - - door->thinker.function = T_Door; - - P_ToXSector(door->sector)->specialData = door; - return true; // Add this thinker. -} - -static int SV_ReadFloor(floor_t *floor) -{ -/* Original Heretic format: -typedef struct { - thinker_t thinker; // was 12 bytes - floortype_e type; // was 32bit int - dd_bool crush; - Sector *sector; - int direction; - int newspecial; - short texture; - fixed_t floordestheight; - fixed_t speed; -} v13_floormove_t; -*/ - - // Padding at the start (an old thinker_t struct) - Reader_Read(svReader, NULL, SIZEOF_V13_THINKER_T); - - // Start of used data members. - floor->type = floortype_e(Reader_ReadInt32(svReader)); - floor->crush = Reader_ReadInt32(svReader); - - // A 32bit pointer to sector, serialized. - floor->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(svReader)); - if(!floor->sector) - Con_Error("tc_floor: bad sector number"); - - floor->state = floorstate_e(Reader_ReadInt32(svReader)); - floor->newSpecial = Reader_ReadInt32(svReader); - - Uri *newTextureUrn = readTextureUrn(svReader, "Flats"); - floor->material = DD_MaterialForTextureUri(newTextureUrn); - Uri_Delete(newTextureUrn); - - floor->floorDestHeight = FIX2FLT(Reader_ReadInt32(svReader)); - floor->speed = FIX2FLT(Reader_ReadInt32(svReader)); - - floor->thinker.function = T_MoveFloor; - - P_ToXSector(floor->sector)->specialData = floor; - return true; // Add this thinker. -} - -static int SV_ReadPlat(plat_t *plat) -{ -/* Original Heretic format: -typedef struct { - thinker_t thinker; // was 12 bytes - Sector *sector; - fixed_t speed; - fixed_t low; - fixed_t high; - int wait; - int count; - platstate_e status; // was 32bit int - platstate_e oldStatus; // was 32bit int - dd_bool crush; - int tag; - plattype_e type; // was 32bit int -} v13_plat_t; -*/ - byte temp[SIZEOF_V13_THINKER_T]; - // Padding at the start (an old thinker_t struct) - Reader_Read(svReader, &temp, SIZEOF_V13_THINKER_T); - - // Start of used data members. - // A 32bit pointer to sector, serialized. - plat->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(svReader)); - if(!plat->sector) - Con_Error("tc_plat: bad sector number"); - - plat->speed = FIX2FLT(Reader_ReadInt32(svReader)); - plat->low = FIX2FLT(Reader_ReadInt32(svReader)); - plat->high = FIX2FLT(Reader_ReadInt32(svReader)); - plat->wait = Reader_ReadInt32(svReader); - plat->count = Reader_ReadInt32(svReader); - plat->state = platstate_e(Reader_ReadInt32(svReader)); - plat->oldState = platstate_e(Reader_ReadInt32(svReader)); - plat->crush = Reader_ReadInt32(svReader); - plat->tag = Reader_ReadInt32(svReader); - plat->type = plattype_e(Reader_ReadInt32(svReader)); - - plat->thinker.function = T_PlatRaise; - if(!(temp + V13_THINKER_T_FUNC_OFFSET)) - Thinker_SetStasis(&plat->thinker, true); - - P_ToXSector(plat->sector)->specialData = plat; - return true; // Add this thinker. -} - -static int SV_ReadFlash(lightflash_t *flash) -{ -/* Original Heretic format: -typedef struct { - thinker_t thinker; // was 12 bytes - Sector *sector; - int count; - int maxLight; - int minLight; - int maxTime; - int minTime; -} v13_lightflash_t; -*/ - // Padding at the start (an old thinker_t struct) - Reader_Read(svReader, NULL, SIZEOF_V13_THINKER_T); - - // Start of used data members. - // A 32bit pointer to sector, serialized. - flash->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(svReader)); - if(!flash->sector) - Con_Error("tc_flash: bad sector number"); - - flash->count = Reader_ReadInt32(svReader); - flash->maxLight = (float) Reader_ReadInt32(svReader) / 255.0f; - flash->minLight = (float) Reader_ReadInt32(svReader) / 255.0f; - flash->maxTime = Reader_ReadInt32(svReader); - flash->minTime = Reader_ReadInt32(svReader); - - flash->thinker.function = (thinkfunc_t) T_LightFlash; - return true; // Add this thinker. -} - -static int SV_ReadStrobe(strobe_t *strobe) -{ -/* Original Heretic format: -typedef struct { - thinker_t thinker; // was 12 bytes - Sector *sector; - int count; - int minLight; - int maxLight; - int darkTime; - int brightTime; -} v13_strobe_t; -*/ - // Padding at the start (an old thinker_t struct) - Reader_Read(svReader, NULL, SIZEOF_V13_THINKER_T); - - // Start of used data members. - // A 32bit pointer to sector, serialized. - strobe->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(svReader)); - if(!strobe->sector) - Con_Error("tc_strobe: bad sector number"); - - strobe->count = Reader_ReadInt32(svReader); - strobe->minLight = (float) Reader_ReadInt32(svReader) / 255.0f; - strobe->maxLight = (float) Reader_ReadInt32(svReader) / 255.0f; - strobe->darkTime = Reader_ReadInt32(svReader); - strobe->brightTime = Reader_ReadInt32(svReader); - - strobe->thinker.function = (thinkfunc_t) T_StrobeFlash; - return true; // Add this thinker. -} - -static int SV_ReadGlow(glow_t *glow) -{ -/* Original Heretic format: -typedef struct { - thinker_t thinker; // was 12 bytes - Sector *sector; - int minLight; - int maxLight; - int direction; -} v13_glow_t; -*/ - // Padding at the start (an old thinker_t struct) - Reader_Read(svReader, NULL, SIZEOF_V13_THINKER_T); - - // Start of used data members. - // A 32bit pointer to sector, serialized. - glow->sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(svReader)); - if(!glow->sector) - Con_Error("tc_glow: bad sector number"); - - glow->minLight = (float) Reader_ReadInt32(svReader) / 255.0f; - glow->maxLight = (float) Reader_ReadInt32(svReader) / 255.0f; - glow->direction = Reader_ReadInt32(svReader); - - glow->thinker.function = (thinkfunc_t) T_Glow; - return true; // Add this thinker. -} - -enum { - tc_ceiling, - tc_door, - tc_floor, - tc_plat, - tc_flash, - tc_strobe, - tc_glow, - tc_endspecials -}; - -/** - * Things to handle: - * - * T_MoveCeiling, (ceiling_t: Sector * swizzle), - active list - * T_Door, (door_t: Sector * swizzle), - * T_MoveFloor, (floor_t: Sector * swizzle), - * T_LightFlash, (lightflash_t: Sector * swizzle), - * T_StrobeFlash, (strobe_t: Sector *), - * T_Glow, (glow_t: Sector *), - * T_PlatRaise, (plat_t: Sector *), - active list - */ -static void P_v13_UnArchiveSpecials() -{ - // Read in saved thinkers. - byte tclass; - while((tclass = Reader_ReadByte(svReader)) != tc_endspecials) - { - switch(tclass) - { - case tc_ceiling: { - ceiling_t *ceiling = (ceiling_t *)Z_Calloc(sizeof(*ceiling), PU_MAP, NULL); - - SV_ReadCeiling(ceiling); - - Thinker_Add(&ceiling->thinker); - break; } - - case tc_door: { - door_t *door = (door_t *)Z_Calloc(sizeof(*door), PU_MAP, NULL); - - SV_ReadDoor(door); - - Thinker_Add(&door->thinker); - break; } - - case tc_floor: { - floor_t *floor = (floor_t *)Z_Calloc(sizeof(*floor), PU_MAP, NULL); - - SV_ReadFloor(floor); - - Thinker_Add(&floor->thinker); - break; } - - case tc_plat: { - plat_t *plat = (plat_t *)Z_Calloc(sizeof(*plat), PU_MAP, NULL); - - SV_ReadPlat(plat); - - Thinker_Add(&plat->thinker); - break; } - - case tc_flash: { - lightflash_t *flash = (lightflash_t *)Z_Calloc(sizeof(*flash), PU_MAP, NULL); - - SV_ReadFlash(flash); - - Thinker_Add(&flash->thinker); - break; } - - case tc_strobe: { - strobe_t *strobe = (strobe_t *)Z_Calloc(sizeof(*strobe), PU_MAP, NULL); - - SV_ReadStrobe(strobe); - - Thinker_Add(&strobe->thinker); - break; } - - case tc_glow: { - glow_t *glow = (glow_t *)Z_Calloc(sizeof(*glow), PU_MAP, NULL); - - SV_ReadGlow(glow); - - Thinker_Add(&glow->thinker); - break; } - - default: - Con_Error("P_UnarchiveSpecials: Unknown tclass %i in savegame", tclass); - } - } -} - -static void SaveInfo_Read_Hr_v13(SaveInfo *info, Reader *reader) -{ - DENG_ASSERT(info != 0); - - char nameBuffer[V13_SAVESTRINGSIZE]; - Reader_Read(reader, nameBuffer, V13_SAVESTRINGSIZE); - nameBuffer[V13_SAVESTRINGSIZE - 1] = 0; - Str_Set(&info->_description, nameBuffer); - - char vcheck[VERSIONSIZE]; - Reader_Read(reader, vcheck, VERSIONSIZE); - //DENG_ASSERT(!strncmp(vcheck, "version ", 8)); // Ensure save state format has been recognised by now. - info->_version = atoi(&vcheck[8]); - - info->_gameRules.skill = (skillmode_t) Reader_ReadByte(reader); - // Interpret skill levels outside the normal range as "spawn no things". - if(info->_gameRules.skill < SM_BABY || info->_gameRules.skill >= NUM_SKILL_MODES) - { - info->_gameRules.skill = SM_NOTHINGS; - } - - uint episode = Reader_ReadByte(reader) - 1; - uint map = Reader_ReadByte(reader) - 1; - Uri_Copy(info->_mapUri, G_ComposeMapUri(episode, map)); - - for(int i = 0; i < 4; ++i) - { - info->_players[i] = Reader_ReadByte(reader); - } - memset(&info->_players[4], 0, sizeof(*info->_players) * (MAXPLAYERS-4)); - - // Get the map time. - int a = Reader_ReadByte(reader); - int b = Reader_ReadByte(reader); - int c = Reader_ReadByte(reader); - info->_mapTime = (a << 16) + (b << 8) + c; - - info->_magic = 0; // Initialize with *something*. - - /// @note Older formats do not contain all needed values: - info->_gameMode = gameMode; // Assume the current mode. - - info->_gameRules.deathmatch = 0; - info->_gameRules.noMonsters = 0; - info->_gameRules.respawnMonsters = 0; - - info->_sessionId = 0; // None. -} - -static dd_bool SV_OpenFile_Hr_v13(char const *filePath) -{ - dd_bool fileOpened; -#if _DEBUG - if(saveBuffer) - Con_Error("SV_OpenFile_Hr_v13: A save state file has already been opened!"); -#endif - fileOpened = 0 != M_ReadFile(filePath, (char **)&saveBuffer); - if(!fileOpened) return false; - savePtr = saveBuffer; - return true; -} - -static void SV_CloseFile_Hr_v13() -{ - if(!saveBuffer) return; - Z_Free(saveBuffer); - saveBuffer = savePtr = 0; -} - -static Reader *SV_NewReader_Hr_v13() -{ - if(!saveBuffer) return 0; - return Reader_NewWithCallbacks(sri8, sri16, sri32, NULL, srd); -} - -bool HereticV13GameStateReader::recognize(SaveInfo *info, Str const *path) // static -{ - DENG_ASSERT(info != 0 && path != 0); - - if(!SV_ExistingFile(path)) return false; - - if(SV_OpenFile_Hr_v13(Str_Text(path))) - { - Reader *svReader = SV_NewReader_Hr_v13(); - bool result = false; - - /// @todo Use the 'version' string as the "magic" identifier. - /*char vcheck[VERSIONSIZE]; - memset(vcheck, 0, sizeof(vcheck)); - Reader_Read(svReader, vcheck, sizeof(vcheck)); - - if(strncmp(vcheck, "version ", 8))*/ - { - SaveInfo_Read_Hr_v13(info, svReader); - result = (info->version() == V13_SAVE_VERSION); - } - - Reader_Delete(svReader); svReader = 0; - SV_CloseFile_Hr_v13(); - - return result; - } - return false; -} - -void HereticV13GameStateReader::read(SaveInfo *info, Str const *path) -{ - DENG_ASSERT(info != 0 && path != 0); - - if(!SV_OpenFile_Hr_v13(Str_Text(path))) - { - throw FileAccessError("HereticV13GameStateReader", "Failed opending " + de::String(Str_Text(path))); - } - - svReader = SV_NewReader_Hr_v13(); - - // Read the header again. - /// @todo Seek past the header straight to the game state. - { - SaveInfo *tmp = new SaveInfo; - SaveInfo_Read_Hr_v13(tmp, svReader); - delete tmp; - } - - // We don't want to see a briefing if we're loading a save game. - briefDisabled = true; - - // Load a base map. - G_NewGame(info->mapUri(), 0/*not saved??*/, &info->gameRules()); - - // Recreate map state. - mapTime = info->mapTime(); - - /// @todo Necessary? - G_SetGameAction(GA_NONE); - - P_v13_UnArchivePlayers(); - P_v13_UnArchiveWorld(); - P_v13_UnArchiveThinkers(); - P_v13_UnArchiveSpecials(); - - if(Reader_ReadByte(svReader) != SAVE_GAME_TERMINATOR) - { - Reader_Delete(svReader); svReader = 0; - SV_CloseFile_Hr_v13(); - - throw ReadError("HereticV13GameStateReader", "Bad savegame (consistency test failed!)"); - } - - Reader_Delete(svReader); svReader = 0; - SV_CloseFile_Hr_v13(); - - // Material scrollers must be spawned. - P_SpawnAllMaterialOriginScrollers(); - - // Let the engine know where the local players are now. - for(int i = 0; i < MAXPLAYERS; ++i) - { - R_UpdateConsoleView(i); - } - - // Inform the engine that map setup must be performed once more. - R_SetupMap(0, 0); -} diff --git a/doomsday/plugins/heretic/src/st_stuff.c b/doomsday/plugins/heretic/src/st_stuff.c index a210b292c2..23ce0ea2be 100644 --- a/doomsday/plugins/heretic/src/st_stuff.c +++ b/doomsday/plugins/heretic/src/st_stuff.c @@ -3034,7 +3034,7 @@ void ST_ToggleAutomapMaxZoom(int player) if(!obj) return; if(UIAutomap_SetZoomMax(obj, !UIAutomap_ZoomMax(obj))) { - App_Log(0, "Maximum zoom %s in automap\n", UIAutomap_ZoomMax(obj)? "ON":"OFF"); + App_Log(0, "Maximum zoom %s in automap", UIAutomap_ZoomMax(obj)? "ON":"OFF"); } } diff --git a/doomsday/plugins/hexen/data/conhelp.txt b/doomsday/plugins/hexen/data/conhelp.txt index 94d50de342..8ae44c8525 100644 --- a/doomsday/plugins/hexen/data/conhelp.txt +++ b/doomsday/plugins/hexen/data/conhelp.txt @@ -35,6 +35,10 @@ desc = End the game. [helpscreen] desc = Show the Help screens. +[inspectgamesave] +desc = Print detailed information about a saved game session to the console. +inf = Params: inspectgamesave (slotId)\nFor example, 'inspectgamesave 1'. + [kill] desc = Kill all the monsters on the map. diff --git a/doomsday/plugins/hexen/hexen.pro b/doomsday/plugins/hexen/hexen.pro index 7e2d2d80fd..7208dff252 100644 --- a/doomsday/plugins/hexen/hexen.pro +++ b/doomsday/plugins/hexen/hexen.pro @@ -77,7 +77,7 @@ SOURCES += \ src/a_action.c \ src/acfnlink.c \ src/acscript.cpp \ - src/h2_main.c \ + src/h2_main.cpp \ src/hconsole.c \ src/hrefresh.c \ src/in_lude.c \ diff --git a/doomsday/plugins/hexen/include/acfnlink.h b/doomsday/plugins/hexen/include/acfnlink.h index 866be7519c..eda93248d5 100644 --- a/doomsday/plugins/hexen/include/acfnlink.h +++ b/doomsday/plugins/hexen/include/acfnlink.h @@ -40,7 +40,7 @@ typedef struct { void (C_DECL * func) (); // Pointer to the function. } actionlink_t; -extern actionlink_t actionlinks[]; +DENG_EXTERN_C actionlink_t actionlinks[]; void C_DECL A_AddPlayerCorpse(); void C_DECL A_BatMove(); diff --git a/doomsday/plugins/hexen/include/h2def.h b/doomsday/plugins/hexen/include/h2def.h index f218d10e44..f26fbfd12c 100644 --- a/doomsday/plugins/hexen/include/h2def.h +++ b/doomsday/plugins/hexen/include/h2def.h @@ -1,26 +1,22 @@ -/**\file h2def.h - *\section License - * License: GPL - * Online License Link: http://www.gnu.org/licenses/gpl.html +/** @file h2def.h * - *\author Copyright © 2003-2013 Jaakko Keränen - *\author Copyright © 2005-2013 Daniel Swanson - *\author Copyright © 1999 Activision + * @authors Copyright © 2003-2013 Jaakko Keränen + * @authors Copyright © 2005-2013 Daniel Swanson + * @authors Copyright © 1999 Activision * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301 USA + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA */ #ifndef __H2DEF_H__ @@ -391,36 +387,11 @@ DENG_EXTERN_C int localQuakeHappening[MAXPLAYERS]; extern "C" { #endif -void H2_Main(void); - -byte P_Random(void); -void M_ResetRandom(void); - -void SC_Open(const char* name); -void SC_OpenLump(lumpnum_t lumpNum); -void SC_OpenFile(const char* name); -void SC_Close(void); -dd_bool SC_GetString(void); -void SC_MustGetString(void); -void SC_MustGetStringName(char* name); -dd_bool SC_GetNumber(void); -void SC_MustGetNumber(void); -void SC_UnGet(void); - -dd_bool SC_Compare(char* text); -int SC_MatchString(char** strings); -void SC_ScriptError(char* message); +byte P_Random(void); +void M_ResetRandom(void); #ifdef __cplusplus } // extern "C" #endif -DENG_EXTERN_C char* sc_String; -DENG_EXTERN_C int sc_Number; -DENG_EXTERN_C int sc_Line; -DENG_EXTERN_C dd_bool sc_End; -DENG_EXTERN_C dd_bool sc_Crossed; -DENG_EXTERN_C dd_bool sc_FileScripts; -DENG_EXTERN_C const char* sc_ScriptsDir; - #endif // __H2DEF_H__ diff --git a/doomsday/plugins/hexen/include/in_lude.h b/doomsday/plugins/hexen/include/in_lude.h index 5c2d1b56b0..1c6e1f4738 100644 --- a/doomsday/plugins/hexen/include/in_lude.h +++ b/doomsday/plugins/hexen/include/in_lude.h @@ -32,8 +32,14 @@ # error "Using jHexen headers without __JHEXEN__" #endif -extern dd_bool intermission; -extern int interState; +#include "h2def.h" + +DENG_EXTERN_C dd_bool intermission; +DENG_EXTERN_C int interState; + +#ifdef __cplusplus +extern "C" { +#endif /// To be called to register the console commands and variables of this module. void WI_Register(void); @@ -44,4 +50,8 @@ void IN_Ticker(void); void IN_Drawer(void); void IN_SkipToNext(void); +#ifdef __cplusplus +} // extern "C" +#endif + #endif diff --git a/doomsday/plugins/hexen/include/m_cheat.h b/doomsday/plugins/hexen/include/m_cheat.h index c905f68ae9..c68692fa88 100644 --- a/doomsday/plugins/hexen/include/m_cheat.h +++ b/doomsday/plugins/hexen/include/m_cheat.h @@ -27,9 +27,17 @@ # error "Using jHexen headers without __JHEXEN__" #endif +#ifdef __cplusplus +extern "C" { +#endif + /** * Register event sequence callbacks for all cheats. */ void G_RegisterCheats(void); +#ifdef __cplusplus +} // extern "C" +#endif + #endif // LIBHEXEN_M_CHEAT_H diff --git a/doomsday/plugins/hexen/include/p_pspr.h b/doomsday/plugins/hexen/include/p_pspr.h index 9a406818ec..a481ac5300 100644 --- a/doomsday/plugins/hexen/include/p_pspr.h +++ b/doomsday/plugins/hexen/include/p_pspr.h @@ -41,12 +41,20 @@ typedef enum { } psprnum_t; typedef struct { - state_t* state; // @c NULL means not active. - int tics; - float pos[2]; + state_t *state; // @c NULL means not active. + int tics; + float pos[2]; } pspdef_t; -void P_BringUpWeapon(struct player_s *player); -void R_GetWeaponBob(int player, float* x, float* y); +#ifdef __cplusplus +extern "C" { +#endif + +void R_GetWeaponBob(int player, float *x, float *y); +void P_BringUpWeapon(struct player_s *player); + +#ifdef __cplusplus +} // extern "C" +#endif #endif diff --git a/doomsday/plugins/hexen/include/p_setup.h b/doomsday/plugins/hexen/include/p_setup.h index bddbb86507..a131a12b72 100644 --- a/doomsday/plugins/hexen/include/p_setup.h +++ b/doomsday/plugins/hexen/include/p_setup.h @@ -57,8 +57,17 @@ enum { MO_SPECIAL }; +#ifdef __cplusplus +extern "C" { +#endif + void P_RegisterMapObjs(void); int P_HandleMapDataPropertyValue(uint id, int dtype, int prop, valuetype_t type, void *data); int P_HandleMapObjectStatusReport(int code, uint id, int dtype, void *data); + +#ifdef __cplusplus +} // extern "C" +#endif + #endif diff --git a/doomsday/plugins/hexen/include/x_config.h b/doomsday/plugins/hexen/include/x_config.h index 766ea977bc..05c1114da6 100644 --- a/doomsday/plugins/hexen/include/x_config.h +++ b/doomsday/plugins/hexen/include/x_config.h @@ -174,7 +174,7 @@ typedef struct { byte menuShortcutsEnabled; byte menuScaleMode; int menuPatchReplaceMode; - byte menuGameSaveSuggestName; + byte menuGameSaveSuggestDescription; byte menuCursorRotate; float menuTextColors[MENU_COLOR_COUNT][3]; float menuTextFlashColor[3]; diff --git a/doomsday/plugins/hexen/include/x_console.h b/doomsday/plugins/hexen/include/x_console.h index ce11fa4d1b..14c840a656 100644 --- a/doomsday/plugins/hexen/include/x_console.h +++ b/doomsday/plugins/hexen/include/x_console.h @@ -32,6 +32,14 @@ # error "Using jHexen headers without __JHEXEN__" #endif +#ifdef __cplusplus +extern "C" { +#endif + void G_ConsoleRegistration(void); +#ifdef __cplusplus +} // extern "C" +#endif + #endif /* LIBHEXEN_CONSOLE_H */ diff --git a/doomsday/plugins/hexen/include/x_main.h b/doomsday/plugins/hexen/include/x_main.h index 82c0c0dcf7..829aa8c5fc 100644 --- a/doomsday/plugins/hexen/include/x_main.h +++ b/doomsday/plugins/hexen/include/x_main.h @@ -1,25 +1,21 @@ -/**\file x_main.h - *\section License - * License: GPL - * Online License Link: http://www.gnu.org/licenses/gpl.html +/** @file x_main.h Hexen-specific game initialization * - *\author Copyright © 2003-2013 Jaakko Keränen - *\author Copyright © 2009-2013 Daniel Swanson + * @authors Copyright © 2003-2013 Jaakko Keränen + * @authors Copyright © 2009-2013 Daniel Swanson * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301 USA + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA */ #ifndef LIBJHEXEN_MAIN_H @@ -29,39 +25,52 @@ # error "Using jHexen headers without __JHEXEN__" #endif -#ifdef __cplusplus -extern "C" { -#endif - -extern int verbose; +#include "h2def.h" -//extern dd_bool noMonstersParm; // checkparm of -nomonsters -//extern dd_bool respawnParm; // checkparm of -respawn -//extern dd_bool turboParm; // checkparm of -turbo -//extern dd_bool randomClassParm; // checkparm of -randclass -//extern dd_bool devParm; // checkparm of -devparm -//extern dd_bool fastParm; // checkparm of -fast +DENG_EXTERN_C int verbose; -extern float turboMul; // Multiplier for turbo. +DENG_EXTERN_C float turboMul; // Multiplier for turbo. -extern gamemode_t gameMode; -extern int gameModeBits; +DENG_EXTERN_C gamemode_t gameMode; +DENG_EXTERN_C int gameModeBits; -extern char* borderGraphics[]; +DENG_EXTERN_C char const *borderGraphics[]; // Default font colors. -extern const float defFontRGB[]; -extern const float defFontRGB2[]; -extern const float defFontRGB3[]; +DENG_EXTERN_C float const defFontRGB[]; +DENG_EXTERN_C float const defFontRGB2[]; +DENG_EXTERN_C float const defFontRGB3[]; + +#ifdef __cplusplus +extern "C" { +#endif +/** + * Pre Game Initialization routine. + * All game-specific actions that should take place at this time go here. + */ void X_PreInit(void); + +/** + * Post Game Initialization routine. + * All game-specific actions that should take place at this time go here. + */ void X_PostInit(void); + void X_Shutdown(void); + +/** + * Get a 32-bit integer value. + */ int X_GetInteger(int id); -void* X_GetVariable(int id); + +/** + * Get a pointer to the value of a named variable/constant. + */ +void *X_GetVariable(int id); #ifdef __cplusplus } // extern "C" #endif -#endif /* LIBJHEXEN_MAIN_H */ +#endif // LIBJHEXEN_MAIN_H diff --git a/doomsday/plugins/hexen/include/x_player.h b/doomsday/plugins/hexen/include/x_player.h index e916e321cf..63c9d6adda 100644 --- a/doomsday/plugins/hexen/include/x_player.h +++ b/doomsday/plugins/hexen/include/x_player.h @@ -125,8 +125,8 @@ typedef struct player_s { int lockFull; #ifdef __cplusplus - void write(Writer *writer) const; - void read(Reader *reader); + void write(Writer *writer, struct playerheader_s &plrHdr) const; + void read(Reader *reader, struct playerheader_s &plrHdr); #endif } player_t; diff --git a/doomsday/plugins/hexen/src/acscript.cpp b/doomsday/plugins/hexen/src/acscript.cpp index 931b41fdbd..87ee38fc70 100644 --- a/doomsday/plugins/hexen/src/acscript.cpp +++ b/doomsday/plugins/hexen/src/acscript.cpp @@ -1723,7 +1723,7 @@ void ACScript::write(MapStateWriter *msw) const Writer_WriteByte(writer, 2); // Write a version byte. - Writer_WriteInt32(writer, SV_ThingArchiveId(activator)); + Writer_WriteInt32(writer, msw->serialIdFor(activator)); Writer_WriteInt32(writer, P_ToIndex(line)); Writer_WriteInt32(writer, side); Writer_WriteInt32(writer, info().scriptNumber); @@ -1751,7 +1751,7 @@ int ACScript::read(MapStateReader *msr) int ver = Reader_ReadByte(reader); // version byte. activator = INT2PTR(mobj_t, Reader_ReadInt32(reader)); - activator = SV_GetArchiveThing(PTR2INT(activator), &activator); + activator = msr->mobj(PTR2INT(activator), &activator); int temp = Reader_ReadInt32(reader); if(temp >= 0) @@ -1794,7 +1794,7 @@ int ACScript::read(MapStateReader *msr) // Start of used data members. activator = INT2PTR(mobj_t, Reader_ReadInt32(reader)); - activator = SV_GetArchiveThing(PTR2INT(activator), &activator); + activator = msr->mobj(PTR2INT(activator), &activator); int temp = Reader_ReadInt32(reader); if(temp >= 0) diff --git a/doomsday/plugins/hexen/src/h2_main.c b/doomsday/plugins/hexen/src/h2_main.cpp similarity index 90% rename from doomsday/plugins/hexen/src/h2_main.c rename to doomsday/plugins/hexen/src/h2_main.cpp index b6f5a5ee7b..a6367c8432 100644 --- a/doomsday/plugins/hexen/src/h2_main.c +++ b/doomsday/plugins/hexen/src/h2_main.cpp @@ -1,4 +1,4 @@ -/** @file h2_main.c Hexen specifc game initialization. +/** @file h2_main.cpp Hexen specifc game initialization. * * @authors Copyright © 2003-2013 Jaakko Keränen * @authors Copyright © 2006-2013 Daniel Swanson @@ -33,8 +33,8 @@ #include "p_saveg.h" #include "p_sound.h" -#include -#include +#include "saveslots.h" +#include int verbose; @@ -50,7 +50,7 @@ float const defFontRGB3[] = { .9f, .9f, .9f }; // The patches used in drawing the view border. // Percent-encoded. -char *borderGraphics[] = { +char const *borderGraphics[] = { "Flats:F_022", // Background. "BORDT", // Top. "BORDR", // Right. @@ -62,40 +62,34 @@ char *borderGraphics[] = { "BORDBL" // Bottom left. }; -/** - * Get a 32-bit integer value. - */ int X_GetInteger(int id) { return Common_GetInteger(id); } -/** - * Get a pointer to the value of a named variable/constant. - */ -void* X_GetVariable(int id) +void *X_GetVariable(int id) { static float bob[2]; switch(id) { case DD_PLUGIN_NAME: - return PLUGIN_NAMETEXT; + return (void*)PLUGIN_NAMETEXT; case DD_PLUGIN_NICENAME: - return PLUGIN_NICENAME; + return (void*)PLUGIN_NICENAME; case DD_PLUGIN_VERSION_SHORT: - return PLUGIN_VERSION_TEXT; + return (void*)PLUGIN_VERSION_TEXT; case DD_PLUGIN_VERSION_LONG: - return PLUGIN_VERSION_TEXTLONG "\n" PLUGIN_DETAILS; + return (void*)(PLUGIN_VERSION_TEXTLONG "\n" PLUGIN_DETAILS); case DD_PLUGIN_HOMEURL: - return PLUGIN_HOMEURL; + return (void*)PLUGIN_HOMEURL; case DD_PLUGIN_DOCSURL: - return PLUGIN_DOCSURL; + return (void*)PLUGIN_DOCSURL; case DD_GAME_CONFIG: return gameConfigString; @@ -126,17 +120,13 @@ void* X_GetVariable(int id) return 0; } -/** - * Pre Game Initialization routine. - * All game-specific actions that should take place at this time go here. - */ -void X_PreInit(void) +void X_PreInit() { // Config defaults. The real settings are read from the .cfg files // but these will be used no such files are found. memset(&cfg, 0, sizeof(cfg)); - { int i; - for(i = 0; i < MAXPLAYERS; ++i) + for(int i = 0; i < MAXPLAYERS; ++i) + { cfg.playerClass[i] = PCLASS_FIGHTER; } cfg.playerMoveSpeed = 1; @@ -146,8 +136,8 @@ void X_PreInit(void) cfg.hudShown[HUD_HEALTH] = true; cfg.hudShown[HUD_READYITEM] = true; cfg.hudShown[HUD_LOG] = true; - { int i; - for(i = 0; i < NUMHUDUNHIDEEVENTS; ++i) // When the hud/statusbar unhides. + for(int i = 0; i < NUMHUDUNHIDEEVENTS; ++i) // When the hud/statusbar unhides. + { cfg.hudUnHide[i] = 1; } cfg.lookSpeed = 3; @@ -202,7 +192,7 @@ void X_PreInit(void) cfg.hudFog = 5; cfg.menuSlam = true; - cfg.menuGameSaveSuggestName = true; + cfg.menuGameSaveSuggestDescription = true; cfg.menuTextFlashColor[0] = 1.0f; cfg.menuTextFlashColor[1] = .5f; cfg.menuTextFlashColor[2] = .5f; @@ -300,17 +290,11 @@ void X_PreInit(void) G_CommonPreInit(); } -/** - * Post Game Initialization routine. - * All game-specific actions that should take place at this time go here. - */ -void X_PostInit(void) +void X_PostInit() { dd_bool autoStart = false; Uri *startMapUri = 0; playerclass_t startPlayerClass = PCLASS_NONE; - AutoStr *path; - int p; // Do this early as other systems need to know. P_InitPlayerClassInfo(); @@ -337,7 +321,7 @@ void X_PostInit(void) gameRules.randomClasses = CommandLine_Exists("-randclass")? true : false; // Turbo movement option. - p = CommandLine_Check("-turbo"); + int p = CommandLine_Check("-turbo"); turboMul = 1.0f; if(p) { @@ -371,8 +355,8 @@ void X_PostInit(void) p = CommandLine_CheckWith("-loadgame", 1); if(p != 0) { - int const slotNumber = SaveSlots_ParseSlotIdentifier(saveSlots, CommandLine_At(p + 1)); - if(SaveSlots_SlotIsUserWritable(saveSlots, slotNumber) && G_LoadGame(slotNumber)) + int const slotNumber = SV_SaveSlots().parseSlotIdentifier(CommandLine_At(p + 1)); + if(SV_SaveSlots().slotIsUserWritable(slotNumber) && G_LoadGame(slotNumber)) { // No further initialization is to be done. return; @@ -434,7 +418,7 @@ void X_PostInit(void) } // Validate episode and map. - path = Uri_Compose(startMapUri); + AutoStr *path = Uri_Compose(startMapUri); if((autoStart || IS_NETGAME) && P_MapExists(Str_Text(path))) { G_DeferredNewGame(startMapUri, 0/*default*/, &gameRules); @@ -448,10 +432,9 @@ void X_PostInit(void) Uri_Delete(startMapUri); } -void X_Shutdown(void) +void X_Shutdown() { P_ShutdownInventory(); X_DestroyLUTs(); G_CommonShutdown(); } - diff --git a/doomsday/plugins/hexen/src/m_cheat.c b/doomsday/plugins/hexen/src/m_cheat.c index fccd7a9b2d..823ab46dbd 100644 --- a/doomsday/plugins/hexen/src/m_cheat.c +++ b/doomsday/plugins/hexen/src/m_cheat.c @@ -427,25 +427,24 @@ D_CMD(CheatGive) if(G_GameState() != GS_MAP) { - App_Log(DE2_SCR_ERROR, "Can only \"give\" when in a game!\n"); + App_Log(DE2_SCR_ERROR, "Can only \"give\" when in a game!"); return true; } if(argc != 2 && argc != 3) { - App_Log(DE2_SCR_NOTE, "Usage:\n give (stuff)\n"); - App_Log(DE2_LOG_SCR, " give (stuff) (plr)\n"); + App_Log(DE2_SCR_NOTE, "Usage:\n give (stuff)\n give (stuff) (plr)"); App_Log(DE2_LOG_SCR, "Stuff consists of one or more of (type:id). " - "If no id; give all of type:\n"); - App_Log(DE2_LOG_SCR, " a - ammo\n"); - App_Log(DE2_LOG_SCR, " h - health\n"); - App_Log(DE2_LOG_SCR, " i - items\n"); - App_Log(DE2_LOG_SCR, " k - keys\n"); - App_Log(DE2_LOG_SCR, " p - puzzle\n"); - App_Log(DE2_LOG_SCR, " r - armor\n"); - App_Log(DE2_LOG_SCR, " w - weapons\n"); - App_Log(DE2_LOG_SCR, "Example: 'give ikw' gives items, keys and weapons.\n"); - App_Log(DE2_LOG_SCR, "Example: 'give w2k1' gives weapon two and key one.\n"); + "If no id; give all of type:"); + App_Log(DE2_LOG_SCR, " a - ammo"); + App_Log(DE2_LOG_SCR, " h - health"); + App_Log(DE2_LOG_SCR, " i - items"); + App_Log(DE2_LOG_SCR, " k - keys"); + App_Log(DE2_LOG_SCR, " p - puzzle"); + App_Log(DE2_LOG_SCR, " r - armor"); + App_Log(DE2_LOG_SCR, " w - weapons"); + App_Log(DE2_LOG_SCR, "Example: 'give ikw' gives items, keys and weapons."); + App_Log(DE2_LOG_SCR, "Example: 'give w2k1' gives weapon two and key one."); return true; } @@ -494,7 +493,7 @@ D_CMD(CheatGive) i += end - &buf[i+1]; if(idx < AT_FIRST || idx >= NUM_AMMO_TYPES) { - App_Log(DE2_SCR_ERROR, "Unknown ammo #%d (valid range %d-%d)\n", + App_Log(DE2_SCR_ERROR, "Unknown ammo #%d (valid range %d-%d)", (int)idx, AT_FIRST, NUM_AMMO_TYPES-1); break; } @@ -540,7 +539,7 @@ D_CMD(CheatGive) i += end - &buf[i+1]; if(idx < KT_FIRST || idx >= NUM_KEY_TYPES) { - App_Log(DE2_SCR_ERROR, "Unknown key #%d (valid range %d-%d)\n", + App_Log(DE2_SCR_ERROR, "Unknown key #%d (valid range %d-%d)", (int)idx, KT_FIRST, NUM_KEY_TYPES-1); break; } @@ -581,7 +580,7 @@ D_CMD(CheatGive) i += end - &buf[i+1]; if(idx < ARMOR_FIRST || idx >= NUMARMOR) { - App_Log(DE2_SCR_ERROR, "Unknown armor #%d (valid range %d-%d)\n", + App_Log(DE2_SCR_ERROR, "Unknown armor #%d (valid range %d-%d)", (int)idx, ARMOR_FIRST, NUMARMOR-1); break; } @@ -609,7 +608,7 @@ D_CMD(CheatGive) i += end - &buf[i+1]; if(idx < WT_FIRST || idx >= NUM_WEAPON_TYPES) { - App_Log(DE2_SCR_ERROR, "Unknown weapon #%d (valid range %d-%d)\n", + App_Log(DE2_SCR_ERROR, "Unknown weapon #%d (valid range %d-%d)", (int)idx, WT_FIRST, NUM_WEAPON_TYPES-1); break; } @@ -625,7 +624,7 @@ D_CMD(CheatGive) break; default: // Unrecognized. - App_Log(DE2_SCR_ERROR, "Cannot give '%c': unknown letter\n", buf[i]); + App_Log(DE2_SCR_ERROR, "Cannot give '%c': unknown letter", buf[i]); break; } } diff --git a/doomsday/plugins/hexen/src/st_stuff.c b/doomsday/plugins/hexen/src/st_stuff.c index 3669727460..871a23e805 100644 --- a/doomsday/plugins/hexen/src/st_stuff.c +++ b/doomsday/plugins/hexen/src/st_stuff.c @@ -3300,7 +3300,7 @@ void ST_ToggleAutomapMaxZoom(int player) if(!obj) return; if(UIAutomap_SetZoomMax(obj, !UIAutomap_ZoomMax(obj))) { - App_Log(0, "Maximum zoom %s in automap\n", UIAutomap_ZoomMax(obj)? "ON":"OFF"); + App_Log(0, "Maximum zoom %s in automap", UIAutomap_ZoomMax(obj)? "ON":"OFF"); } } diff --git a/doomsday/plugins/openal/src/driver_openal.cpp b/doomsday/plugins/openal/src/driver_openal.cpp index 07abd4a258..dcfcc5c011 100644 --- a/doomsday/plugins/openal/src/driver_openal.cpp +++ b/doomsday/plugins/openal/src/driver_openal.cpp @@ -370,7 +370,7 @@ static void setPan(ALuint source, float pan) { float pos[3]; - vectors((float) (headYaw - pan * PI / 2), headPitch, pos, 0); + vectors((float) (headYaw - pan * DD_PI / 2), headPitch, pos, 0); alSourcefv(source, AL_POSITION, pos); } @@ -480,8 +480,8 @@ void DS_SFX_Listenerv(int prop, float* values) break; case SFXLP_ORIENTATION: - vectors(headYaw = (float) (values[VX] / 180 * PI), - headPitch = (float) (values[VY] / 180 * PI), + vectors(headYaw = (float) (values[VX] / 180 * DD_PI), + headPitch = (float) (values[VY] / 180 * DD_PI), ori, ori + 3); alListenerfv(AL_ORIENTATION, ori); break;