summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE339
-rw-r--r--README.md16
-rw-r--r--patch.py52
-rw-r--r--patch/GNUmakefile1004
-rw-r--r--patch/config.cpp929
-rw-r--r--patch/fluidsynth_inc.h6
-rw-r--r--patch/game.h568
-rw-r--r--patch/menus.cpp6639
-rw-r--r--patch/osdcmds.cpp1773
-rw-r--r--patch/sdlmusic.cpp180
10 files changed, 11506 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..727f2d0
--- /dev/null
+++ b/README.md
@@ -0,0 +1,16 @@
+# Duke on Fluidsynth
+
+This is a patchset I wrote for eduke32 that replaces the SDL2_Mixer backend,
+since its MIDI playback didn't work on my machine.
+
+I should probably implement this as some sort of SVN patch, since right now the
+patching script merely copies my source files into the tree.
+
+Also, this was only tested on the latest SVN checkout (r6576). It _might_ work
+on the stable releases, but I don't know for sure.
+
+# Installation
+
+```
+$ python patch.py /path/to/eduke32/source/tree
+```
diff --git a/patch.py b/patch.py
new file mode 100644
index 0000000..57534c6
--- /dev/null
+++ b/patch.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python
+
+import os
+import shutil
+import sys
+
+PATCHDIR = "./patch/"
+
+ADDITIONS = [
+ ("fluidsynth_inc.h", "source/build/include/fluidsynth_inc.h"),
+]
+
+REPLACEMENTS = [
+ ("GNUmakefile", "GNUmakefile"),
+ ("config.cpp", "source/duke3d/src/config.cpp"),
+ ("game.h", "source/duke3d/src/game.h"),
+ ("menus.cpp", "source/duke3d/src/menus.cpp"),
+ ("osdcmds.cpp", "source/duke3d/src/osdcmds.cpp"),
+ ("sdlmusic.cpp", "source/duke3d/src/sdlmusic.cpp"),
+]
+
+out = sys.stdout.write
+err = sys.stderr.write
+
+
+if __name__ == "__main__":
+ if len(sys.argv) != 2:
+ err("usage: {} PATH_TO_SOURCE\n".format(sys.argv[0]))
+ sys.exit(1)
+
+ source = sys.argv[1]
+
+ if not os.path.exists(source) or not os.path.isdir(source):
+ err("'{}' does not exist\n".format(source))
+ sys.exit(1)
+
+ if not os.path.exists(PATCHDIR) or not os.path.isdir(PATCHDIR):
+ err("patch directory does not exist\n".format(source))
+ sys.exit(1)
+
+ for patch, dest in ADDITIONS:
+ out("Adding {} to {}...\n".format(patch, dest))
+ shutil.copyfile(os.path.join(PATCHDIR, patch), os.path.join(source, dest))
+
+ for patch, dest in REPLACEMENTS:
+ out("Patching {}...\n".format(dest))
+ shutil.copyfile(os.path.join(PATCHDIR, patch), os.path.join(source, dest))
+
+ err("\n")
+ err("IF EDUKE32 CRASHES ON STARTUP:\n")
+ err("1) Open ~/.eduke32/settings.cfg\n")
+ err('2) Change snd_fluidsynthbackend to "4"\n')
diff --git a/patch/GNUmakefile b/patch/GNUmakefile
new file mode 100644
index 0000000..1541d75
--- /dev/null
+++ b/patch/GNUmakefile
@@ -0,0 +1,1004 @@
+#
+# EDuke32 Makefile for GNU Make
+#
+
+include Common.mak
+
+### File Extensions
+asm := nasm
+o := o
+
+### Directories
+source := source
+obj := obj
+
+### Functions
+define parent
+$(word 1,$(subst _, ,$1))
+endef
+define expandobjs
+$$(addprefix $$($$(call parent,$1)_obj)/,$$(addsuffix .$$o,$$(basename $$($1_objs) $$($1_rsrc_objs) $$($1_gen_objs))))
+endef
+define expandsrcs
+$(addprefix $($(call parent,$1)_src)/,$($1_objs)) $(addprefix $($(call parent,$1)_rsrc)/,$($1_rsrc_objs)) $(addprefix $($(call parent,$1)_obj)/,$($1_gen_objs))
+endef
+define expanddeps
+$(strip $1 $(foreach j,$1,$(call $0,$($j_deps))))
+endef
+define getdeps
+$(call expanddeps,$1_$2 $(common_$2_deps) engine)
+endef
+
+
+##### External Library Definitions
+
+#### libxmp-lite
+
+libxmplite := libxmp-lite
+
+libxmplite_objs := \
+ control.c \
+ dataio.c \
+ effects.c \
+ filter.c \
+ format.c \
+ hio.c \
+ lfo.c \
+ load.c \
+ load_helpers.c \
+ memio.c \
+ mixer.c \
+ mix_all.c \
+ period.c \
+ player.c \
+ read_event.c \
+ scan.c \
+ smix.c \
+ virtual.c \
+ common.c \
+ itsex.c \
+ it_load.c \
+ mod_load.c \
+ mtm_load.c \
+ s3m_load.c \
+ sample.c \
+ xm_load.c \
+
+libxmplite_root := $(source)/$(libxmplite)
+libxmplite_src := $(libxmplite_root)/src
+libxmplite_inc := $(libxmplite_root)/include
+libxmplite_obj := $(obj)/$(libxmplite)
+
+libxmplite_cflags := -DHAVE_ROUND -DLIBXMP_CORE_PLAYER -DBUILDING_STATIC -I$(libxmplite_inc)/libxmp-lite -Wno-unused-parameter -Wno-sign-compare
+
+
+#### LPeg
+
+lpeg := lpeg
+
+lpeg_objs := \
+ lpcap.c \
+ lpcode.c \
+ lpprint.c \
+ lptree.c \
+ lpvm.c \
+
+lpeg_root := $(source)/$(lpeg)
+lpeg_src := $(lpeg_root)/src
+lpeg_inc := $(lpeg_root)/include
+lpeg_obj := $(obj)/$(lpeg)
+
+
+#### ENet
+
+enet := enet
+
+enet_objs := \
+ callbacks.c \
+ host.c \
+ list.c \
+ packet.c \
+ peer.c \
+ protocol.c \
+ compress.c \
+
+enet_root := $(source)/$(enet)
+enet_src := $(enet_root)/src
+enet_inc := $(enet_root)/include
+enet_obj := $(obj)/$(enet)
+
+enet_cflags :=
+
+ifeq ($(PLATFORM),WINDOWS)
+ enet_objs += win32.c
+else
+ enet_objs += unix.c
+ enet_cflags += -DHAS_SOCKLEN_T
+endif
+
+
+##### Component Definitions
+
+#### EBacktrace
+
+ifndef ebacktrace_dll
+ ebacktrace_dll := ebacktrace1.dll
+ ifeq ($(findstring x86_64,$(COMPILERTARGET)),x86_64)
+ ebacktrace_dll := ebacktrace1-64.dll
+ endif
+endif
+
+
+#### BUILD Engine
+
+engine := build
+
+engine_root := $(source)/$(engine)
+engine_src := $(engine_root)/src
+engine_inc := $(engine_root)/include
+engine_obj := $(obj)/$(engine)
+
+engine_cflags := -I$(engine_src)
+
+engine_objs := \
+ rev.cpp \
+ baselayer.cpp \
+ cache1d.cpp \
+ common.cpp \
+ compat.cpp \
+ crc32.cpp \
+ defs.cpp \
+ engine.cpp \
+ tiles.cpp \
+ clip.cpp \
+ 2d.cpp \
+ hash.cpp \
+ palette.cpp \
+ polymost.cpp \
+ texcache.cpp \
+ dxtfilter.cpp \
+ hightile.cpp \
+ textfont.cpp \
+ smalltextfont.cpp \
+ kplib.cpp \
+ lz4.c \
+ osd.cpp \
+ pragmas.cpp \
+ scriptfile.cpp \
+ mmulti_null.cpp \
+ mutex.cpp \
+ xxhash.c \
+ md4.cpp \
+ colmatch.cpp \
+ screenshot.cpp \
+ mhk.cpp \
+ pngwrite.cpp \
+ miniz.c \
+
+engine_editor_objs := \
+ build.cpp \
+ config.cpp \
+ defs.cpp \
+
+engine_tools_objs := \
+ compat.cpp \
+ pragmas.cpp \
+ kplib.cpp \
+ cache1d.cpp \
+ crc32.cpp \
+ colmatch.cpp \
+
+ifeq (0,$(NOASM))
+ engine_objs += a.nasm
+else
+ engine_objs += a-c.cpp
+ ifneq (0,$(USE_ASM64))
+ engine_objs += a64.yasm
+ endif
+endif
+ifeq (1,$(USE_OPENGL))
+ engine_objs += glbuild.cpp voxmodel.cpp mdsprite.cpp
+ ifeq (1,$(POLYMER))
+ engine_objs += polymer.cpp
+ endif
+endif
+ifneq (0,$(LUNATIC))
+ engine_objs += lunatic.cpp
+endif
+ifeq ($(PLATFORM),DARWIN)
+ engine_objs += osxbits.mm
+ engine_tools_objs += osxbits.mm
+ ifeq ($(STARTUP_WINDOW),1)
+ engine_editor_objs += startosx.editor.mm
+ endif
+ ifeq ($(SDL_TARGET),1)
+ ifneq ($(SDL_FRAMEWORK),0)
+ engine_objs += SDLMain.mm
+ endif
+ endif
+endif
+ifeq ($(PLATFORM),WINDOWS)
+ engine_objs += winbits.cpp
+ ifeq ($(STARTUP_WINDOW),1)
+ engine_editor_objs += startwin.editor.cpp
+ endif
+endif
+ifeq ($(PLATFORM),WII)
+ engine_objs += wiibits.cpp
+ LINKERFLAGS += -Wl,-wrap,c_default_exceptionhandler
+endif
+ifeq ($(RENDERTYPE),SDL)
+ engine_objs += sdlayer.cpp
+
+ ifeq (1,$(HAVE_GTK2))
+ engine_objs += gtkbits.cpp dynamicgtk.cpp
+ ifeq ($(STARTUP_WINDOW),1)
+ engine_editor_objs += startgtk.editor.cpp
+ endif
+ endif
+endif
+ifeq ($(RENDERTYPE),WIN)
+ engine_objs += winlayer.cpp rawinput.cpp
+endif
+
+ifneq ($(USE_LIBVPX),0)
+ engine_objs += animvpx.cpp
+endif
+
+
+#### mact
+
+mact := mact
+
+mact_root := $(source)/$(mact)
+mact_src := $(mact_root)/src
+mact_inc := $(mact_root)/include
+mact_obj := $(obj)/$(mact)
+
+mact_objs := \
+ file_lib.cpp \
+ control.cpp \
+ keyboard.cpp \
+ joystick.cpp \
+ scriplib.cpp \
+ animlib.cpp \
+
+
+#### AudioLib
+
+audiolib := audiolib
+
+audiolib_objs := \
+ drivers.cpp \
+ fx_man.cpp \
+ multivoc.cpp \
+ mix.cpp \
+ mixst.cpp \
+ pitch.cpp \
+ formats.cpp \
+ vorbis.cpp \
+ flac.cpp \
+ xa.cpp \
+ xmp.cpp \
+ driver_nosound.cpp \
+
+audiolib_root := $(source)/$(audiolib)
+audiolib_src := $(audiolib_root)/src
+audiolib_inc := $(audiolib_root)/include
+audiolib_obj := $(obj)/$(audiolib)
+
+audiolib_cflags :=
+
+audiolib_deps :=
+
+ifeq ($(PLATFORM),WINDOWS)
+ ifeq ($(MIXERTYPE),WIN)
+ audiolib_objs += driver_directsound.cpp
+ endif
+endif
+
+ifeq ($(MIXERTYPE),SDL)
+ ifeq (,$(filter $(PLATFORM),DARWIN WINDOWS WII))
+ audiolib_cflags += `$(PKG_CONFIG) --cflags vorbis`
+ endif
+ audiolib_objs += driver_sdl.cpp
+endif
+
+ifneq (0,$(HAVE_XMP))
+ audiolib_cflags += -I$(libxmplite_inc)
+ audiolib_deps += libxmplite
+endif
+
+
+#### Tools
+
+tools := tools
+
+tools_objs := \
+ compat_tools.cpp \
+
+tools_root := $(source)/$(tools)
+tools_src := $(tools_root)/src
+tools_obj := $(obj)/$(tools)
+
+tools_cflags := $(engine_cflags)
+
+tools_deps := engine_tools
+
+tools_targets := \
+ kextract \
+ kgroup \
+ transpal \
+ wad2art \
+ wad2map \
+ kmd2tool \
+ md2tool \
+ generateicon \
+ cacheinfo \
+ arttool \
+ givedepth \
+ mkpalette \
+ unpackssi \
+ bsuite \
+ ivfrate \
+ map2stl \
+
+ifeq ($(PLATFORM),WINDOWS)
+ tools_targets += enumdisplay getdxdidf
+endif
+ifeq ($(RENDERTYPE),SDL)
+ tools_targets += makesdlkeytrans
+endif
+
+
+#### KenBuild (Test Game)
+
+kenbuild := kenbuild
+
+kenbuild_root := $(source)/$(kenbuild)
+kenbuild_src := $(kenbuild_root)/src
+kenbuild_rsrc := $(kenbuild_root)/rsrc
+kenbuild_obj := $(obj)/$(kenbuild)
+
+kenbuild_cflags := -I$(kenbuild_src)
+
+kenbuild_game := ekenbuild
+kenbuild_editor := ekenbuild-editor
+
+kenbuild_game_proper := EKenBuild
+kenbuild_editor_proper := EKenBuild Editor
+
+kenbuild_game_objs := \
+ game.cpp \
+ sound_stub.cpp \
+ common.cpp \
+ config.cpp \
+
+kenbuild_editor_objs := \
+ bstub.cpp \
+ common.cpp \
+
+kenbuild_game_rsrc_objs :=
+kenbuild_editor_rsrc_objs :=
+kenbuild_game_gen_objs :=
+kenbuild_editor_rsrc_objs :=
+
+ifeq (1,$(HAVE_GTK2))
+ kenbuild_game_objs += startgtk.game.cpp
+ kenbuild_game_gen_objs += game_banner.c
+ kenbuild_editor_gen_objs += build_banner.c
+endif
+ifeq ($(RENDERTYPE),SDL)
+ kenbuild_game_rsrc_objs += game_icon.c
+ kenbuild_editor_rsrc_objs += build_icon.c
+endif
+ifeq ($(PLATFORM),WINDOWS)
+ kenbuild_game_objs += startwin.game.cpp
+ kenbuild_game_rsrc_objs += gameres.rc
+ kenbuild_editor_rsrc_objs += buildres.rc
+endif
+
+ifeq ($(PLATFORM),DARWIN)
+ ifeq ($(STARTUP_WINDOW),1)
+ kenbuild_game_objs += StartupWinController.game.mm
+ endif
+endif
+
+
+#### Duke Nukem 3D
+
+duke3d := duke3d
+
+duke3d_game_ldflags :=
+duke3d_editor_ldflags :=
+
+duke3d_game_stripflags :=
+duke3d_editor_stripflags :=
+
+duke3d_root := $(source)/$(duke3d)
+duke3d_src := $(duke3d_root)/src
+duke3d_rsrc := $(duke3d_root)/rsrc
+duke3d_obj := $(obj)/$(duke3d)
+
+duke3d_cflags := -I$(duke3d_src)
+
+common_editor_deps := duke3d_common_editor engine_editor
+
+duke3d_game_deps := duke3d_common_midi audiolib mact
+duke3d_editor_deps := audiolib
+
+ifneq (0,$(NETCODE))
+ duke3d_game_deps += enet
+endif
+
+ifneq (0,$(LUNATIC))
+ duke3d_game_deps += lunatic lunatic_game lpeg
+ duke3d_editor_deps += lunatic lunatic_editor lpeg
+endif
+
+duke3d_game := eduke32
+duke3d_editor := mapster32
+
+ifneq (,$(APPBASENAME))
+ duke3d_game := $(APPBASENAME)
+endif
+
+duke3d_game_proper := EDuke32
+duke3d_editor_proper := Mapster32
+
+duke3d_common_editor_objs := \
+ m32common.cpp \
+ m32def.cpp \
+ m32exec.cpp \
+ m32vars.cpp \
+
+duke3d_game_objs := \
+ game.cpp \
+ global.cpp \
+ actors.cpp \
+ gamedef.cpp \
+ gameexec.cpp \
+ gamevars.cpp \
+ player.cpp \
+ premap.cpp \
+ sector.cpp \
+ anim.cpp \
+ common.cpp \
+ config.cpp \
+ demo.cpp \
+ input.cpp \
+ menus.cpp \
+ namesdyn.cpp \
+ net.cpp \
+ savegame.cpp \
+ rts.cpp \
+ osdfuncs.cpp \
+ osdcmds.cpp \
+ grpscan.cpp \
+ sounds.cpp \
+ soundsdyn.cpp \
+ cheats.cpp \
+ sbar.cpp \
+ screentext.cpp \
+ screens.cpp \
+ cmdline.cpp \
+
+duke3d_editor_objs := \
+ astub.cpp \
+ common.cpp \
+ grpscan.cpp \
+ sounds_mapster32.cpp \
+
+duke3d_game_rsrc_objs :=
+duke3d_editor_rsrc_objs :=
+duke3d_game_gen_objs :=
+duke3d_editor_gen_objs :=
+
+duke3d_game_miscdeps :=
+duke3d_editor_miscdeps :=
+duke3d_game_orderonlydeps :=
+duke3d_editor_orderonlydeps :=
+
+## Lunatic devel
+lunatic := lunatic
+lunatic_src := $(duke3d_src)/$(lunatic)
+lunatic_obj := $(duke3d_obj)
+
+ifneq (0,$(LUNATIC))
+ COMPILERFLAGS += -I$(lunatic_src) -DLUNATIC
+
+ # Determine size of _defs*.lua bytecode once.
+ ifndef DEFS_BC_SIZE
+ DEFS_BC_SIZE := $(shell $(LUAJIT) -bg -t h $(lunatic_src)/_defs_game.lua -)
+ DEFS_BC_SIZE := $(word 3, $(DEFS_BC_SIZE))
+ endif
+ ifndef DEFS_M32_BC_SIZE
+ DEFS_M32_BC_SIZE := $(shell $(LUAJIT) -bg -t h $(lunatic_src)/_defs_editor.lua -)
+ DEFS_M32_BC_SIZE := $(word 3, $(DEFS_M32_BC_SIZE))
+ endif
+ duke3d_cflags += -DLUNATIC_DEFS_BC_SIZE=$(DEFS_BC_SIZE) -DLUNATIC_DEFS_M32_BC_SIZE=$(DEFS_M32_BC_SIZE)
+
+ # Lunatic object base names. These are not used in targets directly.
+ lunatic_objs := \
+ defs_common.lua \
+ engine_maptext.lua \
+ engine.lua \
+ bcarray.lua \
+ bcheck.lua \
+ bitar.lua \
+ xmath.lua \
+ v.lua \
+ dump.lua \
+ dis_x86.lua \
+ dis_x64.lua \
+
+ lunatic_game_objs := \
+ lunatic_game.cpp \
+ _defs_game.lua \
+ con_lang.lua \
+ lunacon.lua \
+ randgen.lua \
+ stat.lua \
+ control.lua \
+ lunasave.lua \
+ fs.lua \
+
+ lunatic_editor_objs := \
+ lunatic_editor.cpp \
+ _defs_editor.lua \
+
+ # TODO: remove debugging modules from release build
+
+ # now, take care of having the necessary symbols (sector, wall, etc.) in the
+ # executable no matter what the debugging level
+
+ ifeq ($(PLATFORM),DARWIN)
+ # strip on OSX says: removing global symbols from a final linked no longer supported.
+ # Use -exported_symbols_list at link time when building
+ # But, following _their_ directions does not give us the symbols! wtf?
+ # Instead of using -alias_list and -exported_symbols_list, prevent stripping them.
+ duke3d_game_stripflags += -s $(duke3d_obj)/lunatic_dynsymlist_game_osx
+ duke3d_editor_stripflags += -s $(duke3d_obj)/lunatic_dynsymlist_editor_osx
+
+ duke3d_game_orderonlydeps += $(duke3d_obj)/lunatic_dynsymlist_game_osx
+ duke3d_editor_orderonlydeps += $(duke3d_obj)/lunatic_dynsymlist_editor_osx
+ LINKERFLAGS += -pagezero_size 10000 -image_base 100000000
+ endif
+ ifeq ($(PLATFORM),WINDOWS)
+ override STRIP :=
+ duke3d_game_miscdeps += $(duke3d_obj)/lunatic_dynsymlist_game.def
+ duke3d_editor_miscdeps += $(duke3d_obj)/lunatic_dynsymlist_editor.def
+ endif
+ ifeq ($(SUBPLATFORM),LINUX)
+ override STRIP :=
+ duke3d_game_ldflags += -Wl,--dynamic-list=$(lunatic_src)/dynsymlist_game.lds
+ duke3d_editor_ldflags += -Wl,--dynamic-list=$(lunatic_src)/dynsymlist_editor.lds
+ endif
+endif
+
+ifeq ($(SUBPLATFORM),LINUX)
+ LIBS += -lFLAC -lvorbisfile -lvorbis -logg
+endif
+
+ifeq ($(PLATFORM),BSD)
+ LIBS += -lFLAC -lvorbisfile -lvorbis -logg -lexecinfo
+endif
+
+ifeq ($(PLATFORM),DARWIN)
+ LIBS += -lFLAC -lvorbisfile -lvorbis -logg -lm \
+ -Wl,-framework,Cocoa -Wl,-framework,Carbon -Wl,-framework,OpenGL \
+ -Wl,-framework,CoreMIDI -Wl,-framework,AudioUnit \
+ -Wl,-framework,AudioToolbox -Wl,-framework,IOKit -Wl,-framework,AGL
+ ifneq (00,$(DARWIN9)$(DARWIN10))
+ LIBS += -Wl,-framework,QuickTime -lm
+ endif
+
+ ifeq ($(STARTUP_WINDOW),1)
+ duke3d_game_objs += GrpFile.game.mm GameListSource.game.mm startosx.game.mm
+ endif
+endif
+
+ifeq ($(PLATFORM),WINDOWS)
+ LIBS += -lFLAC -lvorbisfile -lvorbis -logg
+ duke3d_game_objs += winbits.cpp
+ duke3d_game_rsrc_objs += gameres.rc
+ duke3d_editor_rsrc_objs += buildres.rc
+ ifeq ($(STARTUP_WINDOW),1)
+ duke3d_game_objs += startwin.game.cpp
+ endif
+ ifeq ($(MIXERTYPE),WIN)
+ LIBS += -ldsound
+ duke3d_common_midi_objs := music.cpp midi.cpp mpu401.cpp
+ endif
+endif
+
+ifeq ($(PLATFORM),WII)
+ LIBS += -lvorbisidec
+endif
+
+ifeq (11,$(HAVE_GTK2)$(STARTUP_WINDOW))
+ duke3d_game_objs += startgtk.game.cpp
+ duke3d_game_gen_objs += game_banner.c
+ duke3d_editor_gen_objs += build_banner.c
+endif
+ifeq ($(RENDERTYPE),SDL)
+ duke3d_game_rsrc_objs += game_icon.c
+ duke3d_editor_rsrc_objs += build_icon.c
+endif
+ifeq ($(MIXERTYPE),SDL)
+ LIBS += -lfluidsynth
+ duke3d_common_midi_objs := sdlmusic.cpp
+endif
+
+
+#### Shadow Warrior
+
+sw := sw
+
+sw_root := $(source)/$(sw)
+sw_src := $(sw_root)/src
+sw_rsrc := $(sw_root)/rsrc
+sw_obj := $(obj)/$(sw)
+
+sw_cflags := -I$(sw_src)
+
+sw_game_deps := duke3d_common_midi audiolib mact
+sw_editor_deps := audiolib
+
+sw_game := voidsw
+sw_editor := voidsw-editor
+
+sw_game_proper := VoidSW
+sw_editor_proper := VoidSW Editor
+
+sw_game_objs := \
+ actor.cpp \
+ ai.cpp \
+ anim.cpp \
+ border.cpp \
+ break.cpp \
+ bunny.cpp \
+ cache.cpp \
+ cheats.cpp \
+ colormap.cpp \
+ common.cpp \
+ config.cpp \
+ console.cpp \
+ coolg.cpp \
+ coolie.cpp \
+ copysect.cpp \
+ demo.cpp \
+ draw.cpp \
+ eel.cpp \
+ game.cpp \
+ girlninj.cpp \
+ goro.cpp \
+ grpscan.cpp \
+ hornet.cpp \
+ interp.cpp \
+ interpsh.cpp \
+ inv.cpp \
+ jplayer.cpp \
+ jsector.cpp \
+ jweapon.cpp \
+ lava.cpp \
+ light.cpp \
+ mclip.cpp \
+ mdastr.cpp \
+ menus.cpp \
+ miscactr.cpp \
+ morph.cpp \
+ net.cpp \
+ ninja.cpp \
+ panel.cpp \
+ player.cpp \
+ predict.cpp \
+ quake.cpp \
+ ripper.cpp \
+ ripper2.cpp \
+ rooms.cpp \
+ rotator.cpp \
+ rts.cpp \
+ save.cpp \
+ scrip2.cpp \
+ sector.cpp \
+ serp.cpp \
+ setup.cpp \
+ skel.cpp \
+ skull.cpp \
+ slidor.cpp \
+ sounds.cpp \
+ spike.cpp \
+ sprite.cpp \
+ sumo.cpp \
+ swconfig.cpp \
+ sync.cpp \
+ text.cpp \
+ track.cpp \
+ vator.cpp \
+ vis.cpp \
+ wallmove.cpp \
+ warp.cpp \
+ weapon.cpp \
+ zilla.cpp \
+ zombie.cpp \
+ saveable.cpp \
+
+sw_editor_objs := \
+ jnstub.cpp \
+ brooms.cpp \
+ bldscript.cpp \
+ jbhlp.cpp \
+ colormap.cpp \
+ grpscan.cpp \
+ common.cpp \
+
+sw_game_rsrc_objs :=
+sw_editor_rsrc_objs :=
+sw_game_gen_objs :=
+sw_editor_gen_objs :=
+
+ifeq (1,$(HAVE_GTK2))
+ sw_game_objs += startgtk.game.cpp
+ sw_game_gen_objs += game_banner.c
+ sw_editor_gen_objs += build_banner.c
+endif
+ifeq ($(RENDERTYPE),SDL)
+ sw_game_rsrc_objs += game_icon.c
+ sw_editor_rsrc_objs += game_icon.c
+endif
+ifeq ($(PLATFORM),WINDOWS)
+ sw_game_objs += startwin.game.cpp
+ sw_game_rsrc_objs += gameres.rc
+ sw_editor_rsrc_objs += buildres.rc
+endif
+
+
+#### Final setup
+
+COMPILERFLAGS += -I$(engine_inc) -I$(mact_inc) -I$(audiolib_inc) -I$(enet_inc)
+
+
+##### Recipes
+
+games := \
+ kenbuild \
+ duke3d \
+ sw \
+
+libraries := \
+ engine \
+ audiolib \
+ mact \
+ enet \
+ libxmplite \
+ lpeg \
+
+components := \
+ $(games) \
+ $(libraries) \
+ tools \
+
+roles := \
+ game \
+ editor \
+
+
+ifeq ($(PRETTY_OUTPUT),1)
+.SILENT:
+endif
+.PHONY: \
+ all \
+ start \
+ $(addprefix clean,$(games) test utils tools) \
+ veryclean \
+ clean \
+ printutils \
+ printtools \
+ rev \
+ $(engine_obj)/rev.$o \
+ clang-tools \
+
+.SUFFIXES:
+.SECONDEXPANSION:
+
+
+#### Targets
+
+all: duke3d
+
+start:
+ $(BUILD_STARTED)
+
+tools: $(addsuffix $(EXESUFFIX),$(tools_targets)) | start
+ @$(call LL,$^)
+
+$(games): $$(foreach i,$(roles),$$($$@_$$i)$(EXESUFFIX)) | start
+ @$(call LL,$^)
+
+ebacktrace: $(ebacktrace_dll) | start
+ @$(call LL,$^)
+
+ifeq ($(PLATFORM),WII)
+ifneq ($(ELF2DOL),)
+%$(DOLSUFFIX): %$(EXESUFFIX)
+endif
+endif
+
+
+define BUILDRULE
+
+$$($1_$2)$$(EXESUFFIX): $$(foreach i,$(call getdeps,$1,$2),$$(call expandobjs,$$i)) $$($1_$2_miscdeps) | $$($1_$2_orderonlydeps)
+ $$(LINK_STATUS)
+ $$(RECIPE_IF) $$(LINKER) -o $$@ $$^ $$(GUI_LIBS) $$($1_$2_ldflags) $$(LIBDIRS) $$(LIBS) $$(RECIPE_RESULT_LINK)
+ifeq ($$(PLATFORM),WII)
+ifneq ($$(ELF2DOL),)
+ $$(ELF2DOL) $$@ $$($1_$2)$$(DOLSUFFIX)
+endif
+endif
+ifneq ($$(STRIP),)
+ $$(STRIP) $$@ $$($1_$2_stripflags)
+endif
+ifeq ($$(PLATFORM),DARWIN)
+ cp -RPf "platform/Apple/bundles/$$($1_$2_proper).app" "./"
+ $(call MKDIR,"$$($1_$2_proper).app/Contents/MacOS")
+ cp -f "$$($1_$2)$$(EXESUFFIX)" "$$($1_$2_proper).app/Contents/MacOS/"
+endif
+
+endef
+
+$(foreach i,$(games),$(foreach j,$(roles),$(eval $(call BUILDRULE,$i,$j))))
+
+
+include $(lpeg_root)/Dependencies.mak
+include $(engine_root)/Dependencies.mak
+include $(duke3d_root)/Dependencies.mak
+include $(sw_root)/Dependencies.mak
+
+
+#### Rules
+
+$(ebacktrace_dll): platform/Windows/src/backtrace.c
+ $(COMPILE_STATUS)
+ $(RECIPE_IF) $(CC) $(CONLYFLAGS) -O2 -ggdb -shared -Wall -Wextra -static-libgcc -I$(engine_inc) -o $@ $^ -lbfd -liberty -limagehlp $(RECIPE_RESULT_COMPILE)
+
+libcache1d$(DLLSUFFIX): $(engine_src)/cache1d.cpp
+ $(COMPILE_STATUS)
+ $(RECIPE_IF) $(COMPILER_C) -DCACHE1D_COMPRESS_ONLY -shared -fPIC $< -o $@ $(RECIPE_RESULT_COMPILE)
+
+# to debug the tools link phase, make a copy of this rule explicitly replacing % with the name of a tool, such as kextract
+%$(EXESUFFIX): $(tools_obj)/%.$o $(foreach i,tools $(tools_deps),$(call expandobjs,$i))
+ $(LINK_STATUS)
+ $(RECIPE_IF) $(LINKER) -o $@ $^ $(LIBDIRS) $(LIBS) $(RECIPE_RESULT_LINK)
+
+enumdisplay$(EXESUFFIX): $(tools_obj)/enumdisplay.$o
+ $(LINK_STATUS)
+ $(RECIPE_IF) $(LINKER) -o $@ $^ $(LIBDIRS) $(LIBS) -lgdi32 $(RECIPE_RESULT_LINK)
+getdxdidf$(EXESUFFIX): $(tools_obj)/getdxdidf.$o
+ $(LINK_STATUS)
+ $(RECIPE_IF) $(LINKER) -o $@ $^ $(LIBDIRS) $(LIBS) -ldinput $(RECIPE_RESULT_LINK)
+
+
+### Lunatic
+
+# Create object files directly with luajit
+$(duke3d_obj)/%.$o: $(lunatic_src)/%.lua | $(duke3d_obj)
+ $(COMPILE_STATUS)
+ $(RECIPE_IF) $(LUAJIT) -bg $(LUAJIT_BCOPTS) $< $@ $(RECIPE_RESULT_COMPILE)
+
+$(duke3d_obj)/%.$o: $(lunatic_src)/%.cpp | $(duke3d_obj)
+ $(COMPILE_STATUS)
+ $(RECIPE_IF) $(COMPILER_CXX) $(duke3d_cflags) -c $< -o $@ $(RECIPE_RESULT_COMPILE)
+
+# List of exported symbols, OS X
+$(duke3d_obj)/lunatic_%_osx: $(lunatic_src)/%.lds | $(duke3d_obj)
+ sed 's/[{};]//g;s/[A-Za-z_][A-Za-z_0-9]*/_&/g' $< > $@
+
+# List of exported symbols, Windows
+$(duke3d_obj)/lunatic_%.def: $(lunatic_src)/%.lds | $(duke3d_obj)
+ echo EXPORTS > $@
+ sed 's/[{};]//g' $< >> $@
+
+
+### Main Rules
+
+define OBJECTRULES
+
+$$($1_obj)/%.$$o: $$($1_src)/%.nasm | $$($1_obj)
+ $$(COMPILE_STATUS)
+ $$(RECIPE_IF) $$(AS) $$(ASFLAGS) $$< -o $$@ $$(RECIPE_RESULT_COMPILE)
+
+$$($1_obj)/%.$$o: $$($1_src)/%.yasm | $$($1_obj)
+ $$(COMPILE_STATUS)
+ $$(RECIPE_IF) $$(AS) $$(ASFLAGS) $$< -o $$@ $$(RECIPE_RESULT_COMPILE)
+
+$$($1_obj)/%.$$o: $$($1_src)/%.c | $$($1_obj)
+ $$(COMPILE_STATUS)
+ $$(RECIPE_IF) $$(COMPILER_C) $$($1_cflags) -c $$< -o $$@ $$(RECIPE_RESULT_COMPILE)
+
+$$($1_obj)/%.$$o: $$($1_src)/%.cpp | $$($1_obj)
+ $$(COMPILE_STATUS)
+ $$(RECIPE_IF) $$(COMPILER_CXX) $$($1_cflags) -c $$< -o $$@ $$(RECIPE_RESULT_COMPILE)
+
+$$($1_obj)/%.$$o: $$($1_src)/%.m | $$($1_obj)
+ $$(COMPILE_STATUS)
+ $$(RECIPE_IF) $$(COMPILER_OBJC) $$($1_cflags) -c $$< -o $$@ $$(RECIPE_RESULT_COMPILE)
+
+$$($1_obj)/%.$$o: $$($1_src)/%.mm | $$($1_obj)
+ $$(COMPILE_STATUS)
+ $$(RECIPE_IF) $$(COMPILER_OBJCXX) $$($1_cflags) -c $$< -o $$@ $$(RECIPE_RESULT_COMPILE)
+
+$$($1_obj)/%.$$o: $$($1_obj)/%.c
+ $$(COMPILE_STATUS)
+ $$(RECIPE_IF) $$(COMPILER_C) $$($1_cflags) -c $$< -o $$@ $$(RECIPE_RESULT_COMPILE)
+
+## Cosmetic stuff
+
+$$($1_obj)/%.$$o: $$($1_rsrc)/%.rc | $$($1_obj)
+ $$(COMPILE_STATUS)
+ $$(RECIPE_IF) $$(RC) -i $$< -o $$@ --include-dir=$$(engine_inc) --include-dir=$$($1_src) --include-dir=$$($1_rsrc) -DPOLYMER=$$(POLYMER) $$(RECIPE_RESULT_COMPILE)
+
+$$($1_obj)/%.$$o: $$($1_rsrc)/%.c | $$($1_obj)
+ $$(COMPILE_STATUS)
+ $$(RECIPE_IF) $$(COMPILER_C) $$($1_cflags) -c $$< -o $$@ $$(RECIPE_RESULT_COMPILE)
+
+$$($1_obj)/%_banner.c: $$($1_rsrc)/%.bmp | $$($1_obj)
+ echo "#include \"gtkpixdata_shim.h\"" > $$@
+ gdk-pixbuf-csource --extern --struct --raw --name=startbanner_pixdata $$^ | sed 's/load_inc//' >> $$@
+
+endef
+
+$(foreach i,$(components),$(eval $(call OBJECTRULES,$i)))
+
+
+### Other special cases
+
+# Comment out the following rule to debug a-c.o
+$(engine_obj)/a-c.$o: $(engine_src)/a-c.cpp | $(engine_obj)
+ $(COMPILE_STATUS)
+ $(RECIPE_IF) $(subst -O$(OPTLEVEL),-O2,$(subst $(ASAN_FLAGS),,$(COMPILER_CXX))) $(engine_cflags) -c $< -o $@ $(RECIPE_RESULT_COMPILE)
+
+$(engine_obj)/rev.$o: $(engine_src)/rev.cpp | $(engine_obj)
+ $(COMPILE_STATUS)
+ $(RECIPE_IF) $(COMPILER_CXX) $(engine_cflags) $(REVFLAG) -c $< -o $@ $(RECIPE_RESULT_COMPILE)
+
+
+### Directories
+
+$(foreach i,$(components),$($i_obj)):
+ -$(call MKDIR,$@)
+
+### Phonies
+
+clang-tools: $(filter %.c %.cpp,$(foreach i,$(call getdeps,duke3d,game),$(call expandsrcs,$i)))
+ echo $^ -- -x c++ $(CXXONLYFLAGS) $(COMPILERFLAGS) $(foreach i,$(components),$($i_cflags)) $(CWARNS)
+
+$(addprefix clean,$(games)):
+ -$(call RM,$(foreach i,$(roles),$($(subst clean,,$@)_$i)$(EXESUFFIX)))
+ -$(call RMDIR,$($(subst clean,,$@)_obj))
+ifeq ($(PLATFORM),DARWIN)
+ -$(call RMDIR,$(foreach i,$(roles),"$($(subst clean,,$@)_$i_proper).app"))
+endif
+
+cleantools:
+ -$(call RM,$(addsuffix $(EXESUFFIX),$($(subst clean,,$@)_targets)))
+ -$(call RMDIR,$($(subst clean,,$@)_obj))
+
+clean: cleanduke3d cleantools
+ -$(call RMDIR,$(obj))
+ -$(call RM,$(ebacktrace_dll))
+
+printtools:
+ echo "$(addsuffix $(EXESUFFIX),$(tools_targets))"
+
+rev: $(engine_obj)/rev.$o
+
+
+### Compatibility
+
+test: kenbuild
+utils: tools
+printutils: printtools
+veryclean: clean
+cleanutils: cleantools
+cleantest: cleankenbuild
diff --git a/patch/config.cpp b/patch/config.cpp
new file mode 100644
index 0000000..6081e36
--- /dev/null
+++ b/patch/config.cpp
@@ -0,0 +1,929 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 2016 EDuke32 developers and contributors
+
+This file is part of EDuke32.
+
+EDuke32 is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License version 2
+as published by the Free Software Foundation.
+
+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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+//-------------------------------------------------------------------------
+
+#include "duke3d.h"
+#include "scriplib.h"
+#include "osdcmds.h"
+#include "renderlayer.h"
+#include "cmdline.h"
+
+#ifdef __ANDROID__
+#include "android.h"
+#endif
+
+// we load this in to get default button and key assignments
+// as well as setting up function mappings
+
+#define __SETUP__ // JBF 20031211
+#include "_functio.h"
+
+hashtable_t h_gamefuncs = { NUMGAMEFUNCTIONS<<1, NULL };
+
+int32_t CONFIG_FunctionNameToNum(const char *func)
+{
+ int32_t i;
+
+ if (!func)
+ return -1;
+
+ i = hash_find(&h_gamefuncs,func);
+
+ if (i < 0)
+ {
+ char *str = Bstrtolower(Xstrdup(func));
+ i = hash_find(&h_gamefuncs,str);
+ Bfree(str);
+
+ return i;
+ }
+
+ return i;
+}
+
+
+char *CONFIG_FunctionNumToName(int32_t func)
+{
+ if ((unsigned)func >= (unsigned)NUMGAMEFUNCTIONS)
+ return NULL;
+ return gamefunctions[func];
+}
+
+
+int32_t CONFIG_AnalogNameToNum(const char *func)
+{
+ if (!func)
+ return -1;
+
+ if (!Bstrcasecmp(func,"analog_turning"))
+ {
+ return analog_turning;
+ }
+ if (!Bstrcasecmp(func,"analog_strafing"))
+ {
+ return analog_strafing;
+ }
+ if (!Bstrcasecmp(func,"analog_moving"))
+ {
+ return analog_moving;
+ }
+ if (!Bstrcasecmp(func,"analog_lookingupanddown"))
+ {
+ return analog_lookingupanddown;
+ }
+
+ return -1;
+}
+
+
+const char *CONFIG_AnalogNumToName(int32_t func)
+{
+ switch (func)
+ {
+ case analog_turning:
+ return "analog_turning";
+ case analog_strafing:
+ return "analog_strafing";
+ case analog_moving:
+ return "analog_moving";
+ case analog_lookingupanddown:
+ return "analog_lookingupanddown";
+ }
+
+ return NULL;
+}
+
+
+void CONFIG_SetDefaultKeys(const char (*keyptr)[MAXGAMEFUNCLEN])
+{
+ Bmemset(ud.config.KeyboardKeys, 0xff, sizeof(ud.config.KeyboardKeys));
+
+ CONTROL_ClearAllBinds();
+
+ for (size_t i=0; i < ARRAY_SIZE(gamefunctions); ++i)
+ {
+ if (gamefunctions[i][0] == '\0')
+ continue;
+
+ ud.config.KeyboardKeys[i][0] = KB_StringToScanCode(keyptr[i<<1]);
+ ud.config.KeyboardKeys[i][1] = KB_StringToScanCode(keyptr[(i<<1)+1]);
+
+ if (i == gamefunc_Show_Console)
+ OSD_CaptureKey(ud.config.KeyboardKeys[i][0]);
+ else
+ CONFIG_MapKey(i, ud.config.KeyboardKeys[i][0], 0, ud.config.KeyboardKeys[i][1], 0);
+ }
+}
+
+void CONFIG_SetDefaults(void)
+{
+ // JBF 20031211
+ int32_t i;
+
+ ud.config.scripthandle = -1;
+#ifdef __ANDROID__
+ droidinput.forward_sens = 5.f;
+ droidinput.strafe_sens = 5.f;
+ droidinput.pitch_sens = 5.f;
+ droidinput.yaw_sens = 5.f;
+ droidinput.hideStick = 0;
+ droidinput.gameControlsAlpha = 0.5;
+ droidinput.toggleCrouch = 1;
+ droidinput.quickSelectWeapon = 1;
+
+ ud.config.ScreenWidth = droidinfo.screen_width;
+ ud.config.ScreenHeight = droidinfo.screen_height;
+#else
+ ud.config.ScreenWidth = 1024;
+ ud.config.ScreenHeight = 768;
+#endif
+
+ ud.config.ScreenMode = 0;
+
+#ifdef USE_OPENGL
+ ud.config.ScreenBPP = 32;
+#else
+ ud.config.ScreenBPP = 8;
+#endif
+ ud.config.useprecache = 1;
+ ud.config.ForceSetup = 1;
+ ud.config.NoAutoLoad = 1;
+ ud.config.AmbienceToggle = 1;
+ ud.config.AutoAim = 1;
+ ud.config.MasterVolume = 255;
+ ud.config.FXVolume = 225;
+#if defined(_WIN32)
+ ud.config.MixRate = 44100;
+#elif defined __ANDROID__
+ ud.config.MixRate = droidinfo.audio_sample_rate;
+#else
+ ud.config.MixRate = 48000;
+#endif
+ ud.config.MouseBias = 0;
+ ud.config.MouseDeadZone = 0;
+ ud.config.MusicToggle = 1;
+ ud.config.MusicVolume = 195;
+ g_myAimMode = g_player[0].ps->aim_mode = 1;
+ ud.config.NumBits = 16;
+ ud.config.NumChannels = 2;
+#if defined GEKKO || defined __OPENDINGUX__
+ ud.config.NumVoices = 32;
+#else
+ ud.config.NumVoices = 256;
+#endif
+ ud.config.FluidsynthBackend = 0;
+ ud.config.ReverseStereo = 0;
+ ud.auto_run = 1;
+ ud.config.ShowOpponentWeapons = 0;
+ ud.config.SmoothInput = 1;
+ ud.config.SoundToggle = 1;
+ ud.althud = 1;
+ ud.automsg = 0;
+ ud.autovote = 0;
+ ud.brightness = 8;
+ ud.camerasprite = -1;
+
+#if defined GEKKO || defined __OPENDINGUX__
+ ud.camera_time = 11;
+#elif defined(__ANDROID__)
+ ud.camera_time = 7;
+#else
+ ud.camera_time = 4;
+#endif
+
+ ud.color = 0;
+ ud.crosshair = 1;
+ ud.crosshairscale = 50;
+ ud.obituaries = 1;
+ ud.democams = 1;
+ ud.detail = 0;
+ ud.drawweapon = 1;
+ ud.idplayers = 1;
+ ud.levelstats = 0;
+ ud.lockout = 0;
+ ud.m_ffire = 1;
+ ud.m_marker = 1;
+ ud.mouseaiming = 0;
+ ud.mouseflip = 1;
+ ud.msgdisptime = 120;
+ ud.pwlockout[0] = '\0';
+ ud.runkey_mode = 0;
+ ud.screen_size = 4;
+ ud.screen_tilting = 1;
+ ud.shadows = 1;
+ ud.statusbarmode = 1;
+ ud.statusbarscale = 100;
+ ud.team = 0;
+ ud.viewbob = 1;
+ ud.weaponsway = 1;
+ ud.weaponswitch = 3; // new+empty
+ ud.angleinterpolation = 0;
+#ifdef GEKKO
+ ud.config.UseJoystick = 1;
+#else
+ ud.config.UseJoystick = 0;
+#endif
+ ud.config.UseMouse = 1;
+ ud.config.VoiceToggle = 5; // bitfield, 1 = local, 2 = dummy, 4 = other players in DM
+ ud.display_bonus_screen = 1;
+ ud.show_level_text = 1;
+ ud.configversion = 0;
+ ud.weaponscale = 100;
+ ud.textscale = 200;
+ ud.screenfade = 1;
+
+ ud.config.CheckForUpdates = 1;
+
+ if (g_rtsNamePtr == NULL)
+ Bstrcpy(ud.rtsname, G_DefaultRtsFile());
+
+ Bstrcpy(szPlayerName, "Player");
+
+#ifndef EDUKE32_STANDALONE
+ Bstrcpy(ud.ridecule[0], "An inspiration for birth control.");
+ Bstrcpy(ud.ridecule[1], "You're gonna die for that!");
+ Bstrcpy(ud.ridecule[2], "It hurts to be you.");
+ Bstrcpy(ud.ridecule[3], "Lucky son of a bitch.");
+ Bstrcpy(ud.ridecule[4], "Hmmm... payback time.");
+ Bstrcpy(ud.ridecule[5], "You bottom dwelling scum sucker.");
+ Bstrcpy(ud.ridecule[6], "Damn, you're ugly.");
+ Bstrcpy(ud.ridecule[7], "Ha ha ha... wasted!");
+ Bstrcpy(ud.ridecule[8], "You suck!");
+ Bstrcpy(ud.ridecule[9], "AARRRGHHHHH!!!");
+#endif
+
+ // JBF 20031211
+
+ CONFIG_SetDefaultKeys(keydefaults);
+
+ memset(ud.config.MouseFunctions, -1, sizeof(ud.config.MouseFunctions));
+ for (i=0; i<MAXMOUSEBUTTONS; i++)
+ {
+ ud.config.MouseFunctions[i][0] = CONFIG_FunctionNameToNum(mousedefaults[i]);
+ CONTROL_MapButton(ud.config.MouseFunctions[i][0], i, 0, controldevice_mouse);
+ if (i>=4) continue;
+ ud.config.MouseFunctions[i][1] = CONFIG_FunctionNameToNum(mouseclickeddefaults[i]);
+ CONTROL_MapButton(ud.config.MouseFunctions[i][1], i, 1, controldevice_mouse);
+ }
+
+ memset(ud.config.MouseDigitalFunctions, -1, sizeof(ud.config.MouseDigitalFunctions));
+ for (i=0; i<MAXMOUSEAXES; i++)
+ {
+ ud.config.MouseAnalogueScale[i] = DEFAULTMOUSEANALOGUESCALE;
+ CONTROL_SetAnalogAxisScale(i, ud.config.MouseAnalogueScale[i], controldevice_mouse);
+
+ ud.config.MouseDigitalFunctions[i][0] = CONFIG_FunctionNameToNum(mousedigitaldefaults[i*2]);
+ ud.config.MouseDigitalFunctions[i][1] = CONFIG_FunctionNameToNum(mousedigitaldefaults[i*2+1]);
+ CONTROL_MapDigitalAxis(i, ud.config.MouseDigitalFunctions[i][0], 0, controldevice_mouse);
+ CONTROL_MapDigitalAxis(i, ud.config.MouseDigitalFunctions[i][1], 1, controldevice_mouse);
+
+ ud.config.MouseAnalogueAxes[i] = CONFIG_AnalogNameToNum(mouseanalogdefaults[i]);
+ CONTROL_MapAnalogAxis(i, ud.config.MouseAnalogueAxes[i], controldevice_mouse);
+ }
+ CONTROL_MouseSensitivity = DEFAULTMOUSESENSITIVITY;
+
+ memset(ud.config.JoystickFunctions, -1, sizeof(ud.config.JoystickFunctions));
+ for (i=0; i<MAXJOYBUTTONSANDHATS; i++)
+ {
+ ud.config.JoystickFunctions[i][0] = CONFIG_FunctionNameToNum(joystickdefaults[i]);
+ ud.config.JoystickFunctions[i][1] = CONFIG_FunctionNameToNum(joystickclickeddefaults[i]);
+ CONTROL_MapButton(ud.config.JoystickFunctions[i][0], i, 0, controldevice_joystick);
+ CONTROL_MapButton(ud.config.JoystickFunctions[i][1], i, 1, controldevice_joystick);
+ }
+
+ memset(ud.config.JoystickDigitalFunctions, -1, sizeof(ud.config.JoystickDigitalFunctions));
+ for (i=0; i<MAXJOYAXES; i++)
+ {
+ ud.config.JoystickAnalogueScale[i] = DEFAULTJOYSTICKANALOGUESCALE;
+ ud.config.JoystickAnalogueDead[i] = DEFAULTJOYSTICKANALOGUEDEAD;
+ ud.config.JoystickAnalogueSaturate[i] = DEFAULTJOYSTICKANALOGUESATURATE;
+ CONTROL_SetAnalogAxisScale(i, ud.config.JoystickAnalogueScale[i], controldevice_joystick);
+
+ ud.config.JoystickDigitalFunctions[i][0] = CONFIG_FunctionNameToNum(joystickdigitaldefaults[i*2]);
+ ud.config.JoystickDigitalFunctions[i][1] = CONFIG_FunctionNameToNum(joystickdigitaldefaults[i*2+1]);
+ CONTROL_MapDigitalAxis(i, ud.config.JoystickDigitalFunctions[i][0], 0, controldevice_joystick);
+ CONTROL_MapDigitalAxis(i, ud.config.JoystickDigitalFunctions[i][1], 1, controldevice_joystick);
+
+ ud.config.JoystickAnalogueAxes[i] = CONFIG_AnalogNameToNum(joystickanalogdefaults[i]);
+ CONTROL_MapAnalogAxis(i, ud.config.JoystickAnalogueAxes[i], controldevice_joystick);
+ }
+
+ VM_OnEvent(EVENT_SETDEFAULTS, g_player[myconnectindex].ps->i, myconnectindex);
+}
+
+
+// wrapper for CONTROL_MapKey(), generates key bindings to reflect changes to keyboard setup
+void CONFIG_MapKey(int32_t which, kb_scancode key1, kb_scancode oldkey1, kb_scancode key2, kb_scancode oldkey2)
+{
+ int32_t i, j, k;
+ int32_t ii[] = { key1, key2, oldkey1, oldkey2 };
+ char buf[2*MAXGAMEFUNCLEN];
+
+ UNREFERENCED_PARAMETER(which);
+// CONTROL_MapKey(which, key1, key2);
+
+ if (which == gamefunc_Show_Console)
+ OSD_CaptureKey(key1);
+
+ for (k = 0; (unsigned)k < ARRAY_SIZE(ii); k++)
+ {
+ if (ii[k] == 0xff || !ii[k])
+ continue;
+
+ for (j=0; ConsoleKeys[j].name; j++)
+ if (ii[k] == ConsoleKeys[j].id)
+ break;
+
+ tempbuf[0] = 0;
+
+ for (i=NUMGAMEFUNCTIONS-1; i>=0; i--)
+ {
+ if (ud.config.KeyboardKeys[i][0] == ii[k] || ud.config.KeyboardKeys[i][1] == ii[k])
+ {
+ Bsprintf(buf,"gamefunc_%s; ",CONFIG_FunctionNumToName(i));
+ Bstrcat(tempbuf,buf);
+ }
+ }
+
+ i = Bstrlen(tempbuf);
+ if (i >= 2)
+ {
+ tempbuf[i-2] = 0; // cut off the trailing "; "
+ CONTROL_BindKey(ii[k], tempbuf, 1, ConsoleKeys[j].name ? ConsoleKeys[j].name : "<?>");
+ }
+ else
+ {
+ CONTROL_FreeKeyBind(ii[k]);
+ }
+ }
+}
+
+
+void CONFIG_SetupMouse(void)
+{
+ int32_t i;
+ char str[80];
+ char temp[80];
+ int32_t scale;
+
+ if (ud.config.scripthandle < 0) return;
+
+ for (i=0; i<MAXMOUSEBUTTONS; i++)
+ {
+ Bsprintf(str,"MouseButton%d",i);
+ temp[0] = 0;
+ if (!SCRIPT_GetString(ud.config.scripthandle,"Controls", str,temp))
+ ud.config.MouseFunctions[i][0] = CONFIG_FunctionNameToNum(temp);
+
+ Bsprintf(str,"MouseButtonClicked%d",i);
+ temp[0] = 0;
+ if (!SCRIPT_GetString(ud.config.scripthandle,"Controls", str,temp))
+ ud.config.MouseFunctions[i][1] = CONFIG_FunctionNameToNum(temp);
+ }
+
+ // map over the axes
+ for (i=0; i<MAXMOUSEAXES; i++)
+ {
+ Bsprintf(str,"MouseAnalogAxes%d",i);
+ temp[0] = 0;
+ if (!SCRIPT_GetString(ud.config.scripthandle, "Controls", str,temp))
+ if (CONFIG_AnalogNameToNum(temp) != -1 || (!temp[0] && CONFIG_FunctionNameToNum(temp) != -1))
+ ud.config.MouseAnalogueAxes[i] = CONFIG_AnalogNameToNum(temp);
+
+ Bsprintf(str,"MouseDigitalAxes%d_0",i);
+ temp[0] = 0;
+ if (!SCRIPT_GetString(ud.config.scripthandle, "Controls", str,temp))
+ if (CONFIG_FunctionNameToNum(temp) != -1 || (!temp[0] && CONFIG_FunctionNameToNum(temp) != -1))
+ ud.config.MouseDigitalFunctions[i][0] = CONFIG_FunctionNameToNum(temp);
+
+ Bsprintf(str,"MouseDigitalAxes%d_1",i);
+ temp[0] = 0;
+ if (!SCRIPT_GetString(ud.config.scripthandle, "Controls", str,temp))
+ if (CONFIG_FunctionNameToNum(temp) != -1 || (!temp[0] && CONFIG_FunctionNameToNum(temp) != -1))
+ ud.config.MouseDigitalFunctions[i][1] = CONFIG_FunctionNameToNum(temp);
+
+ Bsprintf(str,"MouseAnalogScale%d",i);
+ scale = ud.config.MouseAnalogueScale[i];
+ SCRIPT_GetNumber(ud.config.scripthandle, "Controls", str,&scale);
+ ud.config.MouseAnalogueScale[i] = scale;
+ }
+
+ {
+ tempbuf[0] = 0;
+ SCRIPT_GetString(ud.config.scripthandle, "Controls","Mouse_Sensitivity",&tempbuf[0]);
+ if (tempbuf[0]) CONTROL_MouseSensitivity = atof(tempbuf);
+ }
+
+ for (i=0; i<MAXMOUSEBUTTONS; i++)
+ {
+ CONTROL_MapButton(ud.config.MouseFunctions[i][0], i, 0, controldevice_mouse);
+ CONTROL_MapButton(ud.config.MouseFunctions[i][1], i, 1, controldevice_mouse);
+ }
+ for (i=0; i<MAXMOUSEAXES; i++)
+ {
+ CONTROL_MapAnalogAxis(i, ud.config.MouseAnalogueAxes[i], controldevice_mouse);
+ CONTROL_MapDigitalAxis(i, ud.config.MouseDigitalFunctions[i][0], 0,controldevice_mouse);
+ CONTROL_MapDigitalAxis(i, ud.config.MouseDigitalFunctions[i][1], 1,controldevice_mouse);
+ CONTROL_SetAnalogAxisScale(i, ud.config.MouseAnalogueScale[i], controldevice_mouse);
+ }
+}
+
+
+void CONFIG_SetupJoystick(void)
+{
+ int32_t i;
+ char str[80];
+ char temp[80];
+ int32_t scale;
+
+ if (ud.config.scripthandle < 0) return;
+
+ for (i=0; i<MAXJOYBUTTONSANDHATS; i++)
+ {
+ Bsprintf(str,"JoystickButton%d",i);
+ temp[0] = 0;
+ if (!SCRIPT_GetString(ud.config.scripthandle,"Controls", str,temp))
+ ud.config.JoystickFunctions[i][0] = CONFIG_FunctionNameToNum(temp);
+
+ Bsprintf(str,"JoystickButtonClicked%d",i);
+ temp[0] = 0;
+ if (!SCRIPT_GetString(ud.config.scripthandle,"Controls", str,temp))
+ ud.config.JoystickFunctions[i][1] = CONFIG_FunctionNameToNum(temp);
+ }
+
+ // map over the axes
+ for (i=0; i<MAXJOYAXES; i++)
+ {
+ Bsprintf(str,"JoystickAnalogAxes%d",i);
+ temp[0] = 0;
+ if (!SCRIPT_GetString(ud.config.scripthandle, "Controls", str,temp))
+ if (CONFIG_AnalogNameToNum(temp) != -1 || (!temp[0] && CONFIG_FunctionNameToNum(temp) != -1))
+ ud.config.JoystickAnalogueAxes[i] = CONFIG_AnalogNameToNum(temp);
+
+ Bsprintf(str,"JoystickDigitalAxes%d_0",i);
+ temp[0] = 0;
+ if (!SCRIPT_GetString(ud.config.scripthandle, "Controls", str,temp))
+ if (CONFIG_FunctionNameToNum(temp) != -1 || (!temp[0] && CONFIG_FunctionNameToNum(temp) != -1))
+ ud.config.JoystickDigitalFunctions[i][0] = CONFIG_FunctionNameToNum(temp);
+
+ Bsprintf(str,"JoystickDigitalAxes%d_1",i);
+ temp[0] = 0;
+ if (!SCRIPT_GetString(ud.config.scripthandle, "Controls", str,temp))
+ if (CONFIG_FunctionNameToNum(temp) != -1 || (!temp[0] && CONFIG_FunctionNameToNum(temp) != -1))
+ ud.config.JoystickDigitalFunctions[i][1] = CONFIG_FunctionNameToNum(temp);
+
+ Bsprintf(str,"JoystickAnalogScale%d",i);
+ scale = ud.config.JoystickAnalogueScale[i];
+ SCRIPT_GetNumber(ud.config.scripthandle, "Controls", str,&scale);
+ ud.config.JoystickAnalogueScale[i] = scale;
+
+ Bsprintf(str,"JoystickAnalogDead%d",i);
+ scale = ud.config.JoystickAnalogueDead[i];
+ SCRIPT_GetNumber(ud.config.scripthandle, "Controls", str,&scale);
+ ud.config.JoystickAnalogueDead[i] = scale;
+
+ Bsprintf(str,"JoystickAnalogSaturate%d",i);
+ scale = ud.config.JoystickAnalogueSaturate[i];
+ SCRIPT_GetNumber(ud.config.scripthandle, "Controls", str,&scale);
+ ud.config.JoystickAnalogueSaturate[i] = scale;
+ }
+
+ for (i=0; i<MAXJOYBUTTONSANDHATS; i++)
+ {
+ CONTROL_MapButton(ud.config.JoystickFunctions[i][0], i, 0, controldevice_joystick);
+ CONTROL_MapButton(ud.config.JoystickFunctions[i][1], i, 1, controldevice_joystick);
+ }
+ for (i=0; i<MAXJOYAXES; i++)
+ {
+ CONTROL_MapAnalogAxis(i, ud.config.JoystickAnalogueAxes[i], controldevice_joystick);
+ CONTROL_MapDigitalAxis(i, ud.config.JoystickDigitalFunctions[i][0], 0, controldevice_joystick);
+ CONTROL_MapDigitalAxis(i, ud.config.JoystickDigitalFunctions[i][1], 1, controldevice_joystick);
+ CONTROL_SetAnalogAxisScale(i, ud.config.JoystickAnalogueScale[i], controldevice_joystick);
+ }
+}
+
+
+int32_t CONFIG_ReadSetup(void)
+{
+ int32_t dummy;
+ char commmacro[] = "CommbatMacro# ";
+ char tempbuf[1024];
+
+ CONTROL_ClearAssignments();
+ CONFIG_SetDefaults();
+
+ ud.config.setupread = 1;
+ pathsearchmode = 1;
+
+ if (ud.config.scripthandle < 0)
+ {
+ if (SafeFileExists(g_setupFileName)) // JBF 20031211
+ ud.config.scripthandle = SCRIPT_Load(g_setupFileName);
+#if !defined(EDUKE32_TOUCH_DEVICES) && !defined(EDUKE32_STANDALONE)
+ else if (SafeFileExists(SETUPFILENAME))
+ {
+ int32_t i;
+ i=wm_ynbox("Import Configuration Settings", "The configuration file \"%s\" was not found. "
+ "Import configuration data from \"%s\"?", g_setupFileName, SETUPFILENAME);
+ if (i) ud.config.scripthandle = SCRIPT_Load(SETUPFILENAME);
+ }
+ else if (SafeFileExists("duke3d.cfg"))
+ {
+ int32_t i;
+ i=wm_ynbox("Import Configuration Settings", "The configuration file \"%s\" was not found. "
+ "Import configuration data from \"duke3d.cfg\"?", g_setupFileName);
+ if (i) ud.config.scripthandle = SCRIPT_Load("duke3d.cfg");
+ }
+#endif
+ }
+
+ pathsearchmode = 0;
+
+ if (ud.config.scripthandle < 0)
+ return -1;
+
+ for (dummy = 0; dummy < MAXRIDECULE; dummy++)
+ {
+ commmacro[13] = dummy+'0';
+ SCRIPT_GetString(ud.config.scripthandle, "Comm Setup",commmacro,&ud.ridecule[dummy][0]);
+ }
+
+ Bmemset(tempbuf, 0, sizeof(tempbuf));
+ SCRIPT_GetString(ud.config.scripthandle, "Comm Setup","PlayerName",&tempbuf[0]);
+
+ char nameBuf[64];
+
+ while (Bstrlen(OSD_StripColors(nameBuf, tempbuf)) > 10)
+ tempbuf[Bstrlen(tempbuf) - 1] = '\0';
+
+ Bstrncpyz(szPlayerName, tempbuf, sizeof(szPlayerName));
+
+ if (g_rtsNamePtr == NULL)
+ SCRIPT_GetString(ud.config.scripthandle, "Comm Setup","RTSName",&ud.rtsname[0]);
+
+ SCRIPT_GetNumber(ud.config.scripthandle, "Setup", "ConfigVersion", &ud.configversion);
+ SCRIPT_GetNumber(ud.config.scripthandle, "Setup", "ForceSetup", &ud.config.ForceSetup);
+ SCRIPT_GetNumber(ud.config.scripthandle, "Setup", "NoAutoLoad", &ud.config.NoAutoLoad);
+
+ if (g_noSetup == 0 && g_modDir[0] == '/')
+ {
+ struct Bstat st;
+ SCRIPT_GetString(ud.config.scripthandle, "Setup","ModDir",&g_modDir[0]);
+
+ if (Bstat(g_modDir, &st))
+ {
+ if ((st.st_mode & S_IFDIR) != S_IFDIR)
+ {
+ initprintf("Invalid mod dir in cfg!\n");
+ Bsprintf(g_modDir,"/");
+ }
+ }
+ }
+
+ if (g_grpNamePtr == NULL && g_addonNum == 0)
+ {
+ SCRIPT_GetStringPtr(ud.config.scripthandle, "Setup","SelectedGRP",&g_grpNamePtr);
+ if (g_grpNamePtr && !Bstrlen(g_grpNamePtr))
+ g_grpNamePtr = dup_filename(G_DefaultGrpFile());
+ }
+
+ if (!NAM_WW2GI)
+ {
+ SCRIPT_GetNumber(ud.config.scripthandle, "Screen Setup", "Out",&ud.lockout);
+ SCRIPT_GetString(ud.config.scripthandle, "Screen Setup","Password",&ud.pwlockout[0]);
+ }
+
+ SCRIPT_GetNumber(ud.config.scripthandle, "Screen Setup", "ScreenHeight",&ud.config.ScreenHeight);
+ SCRIPT_GetNumber(ud.config.scripthandle, "Screen Setup", "ScreenMode",&ud.config.ScreenMode);
+ SCRIPT_GetNumber(ud.config.scripthandle, "Screen Setup", "ScreenWidth",&ud.config.ScreenWidth);
+
+ SCRIPT_GetNumber(ud.config.scripthandle, "Screen Setup", "WindowPositioning", (int32_t *)&windowpos);
+
+ windowx = -1;
+ windowy = -1;
+ SCRIPT_GetNumber(ud.config.scripthandle, "Screen Setup", "WindowPosX", (int32_t *)&windowx);
+ SCRIPT_GetNumber(ud.config.scripthandle, "Screen Setup", "WindowPosY", (int32_t *)&windowy);
+
+ SCRIPT_GetNumber(ud.config.scripthandle, "Screen Setup", "MaxRefreshFreq", (int32_t *)&maxrefreshfreq);
+ SCRIPT_GetNumber(ud.config.scripthandle, "Screen Setup", "ScreenBPP", &ud.config.ScreenBPP);
+
+ if (ud.config.ScreenBPP < 8) ud.config.ScreenBPP = 32;
+
+#ifdef POLYMER
+ SCRIPT_GetNumber(ud.config.scripthandle, "Screen Setup", "Polymer", &dummy);
+ if (dummy > 0 && ud.config.ScreenBPP >= 16) glrendmode = REND_POLYMER;
+ else glrendmode = REND_POLYMOST;
+#endif
+
+ SCRIPT_GetNumber(ud.config.scripthandle, "Misc", "Executions",&ud.executions);
+
+#ifdef _WIN32
+ SCRIPT_GetNumber(ud.config.scripthandle, "Updates", "CheckForUpdates", &ud.config.CheckForUpdates);
+ SCRIPT_GetNumber(ud.config.scripthandle, "Updates", "LastUpdateCheck", &ud.config.LastUpdateCheck);
+#endif
+
+ ud.config.setupread = 1;
+ return 0;
+}
+
+
+void CONFIG_WriteSettings(void) // save binds and aliases to <cfgname>_settings.cfg
+{
+ int32_t i;
+ BFILE *fp;
+ char *ptr = Xstrdup(g_setupFileName);
+ char tempbuf[128];
+
+ if (!Bstrcmp(g_setupFileName, SETUPFILENAME))
+ Bsprintf(tempbuf, "settings.cfg");
+ else Bsprintf(tempbuf, "%s_settings.cfg", strtok(ptr, "."));
+
+ fp = Bfopen(tempbuf, "wt");
+
+ if (fp)
+ {
+ Bfprintf(fp,"// this file is automatically generated by %s\n", AppProperName);
+ Bfprintf(fp,"unbindall\n");
+
+ for (i=0; i<MAXBOUNDKEYS+MAXMOUSEBUTTONS; i++)
+ if (CONTROL_KeyIsBound(i))
+ Bfprintf(fp,"bind \"%s\"%s \"%s\"\n",CONTROL_KeyBinds[i].key,
+ CONTROL_KeyBinds[i].repeat?"":" norepeat",CONTROL_KeyBinds[i].cmdstr);
+
+ OSD_WriteAliases(fp);
+
+ if (g_crosshairSum != -1 && g_crosshairSum != DefaultCrosshairColors.r+(DefaultCrosshairColors.g<<8)+(DefaultCrosshairColors.b<<16))
+ Bfprintf(fp, "crosshaircolor %d %d %d\n", CrosshairColors.r, CrosshairColors.g, CrosshairColors.b);
+
+ OSD_WriteCvars(fp);
+
+ Bfclose(fp);
+
+ if (!Bstrcmp(g_setupFileName, SETUPFILENAME))
+ OSD_Printf("Wrote settings.cfg\n");
+ else OSD_Printf("Wrote %s_settings.cfg\n",ptr);
+
+ Bfree(ptr);
+ return;
+ }
+
+ if (!Bstrcmp(g_setupFileName, SETUPFILENAME))
+ OSD_Printf("Error writing settings.cfg: %s\n", strerror(errno));
+ else OSD_Printf("Error writing %s_settings.cfg: %s\n",ptr,strerror(errno));
+
+ Bfree(ptr);
+}
+
+void CONFIG_WriteSetup(uint32_t flags)
+{
+ int32_t dummy;
+
+ if (!ud.config.setupread) return;
+
+ if (ud.config.scripthandle < 0)
+ ud.config.scripthandle = SCRIPT_Init(g_setupFileName);
+
+ SCRIPT_PutNumber(ud.config.scripthandle, "Misc", "Executions",++ud.executions,FALSE,FALSE);
+
+ SCRIPT_PutNumber(ud.config.scripthandle, "Setup","ConfigVersion",BYTEVERSION_EDUKE32,FALSE,FALSE);
+ SCRIPT_PutNumber(ud.config.scripthandle, "Setup", "ForceSetup",ud.config.ForceSetup,FALSE,FALSE);
+ SCRIPT_PutNumber(ud.config.scripthandle, "Setup", "NoAutoLoad",ud.config.NoAutoLoad,FALSE,FALSE);
+
+#ifdef POLYMER
+ SCRIPT_PutNumber(ud.config.scripthandle, "Screen Setup", "Polymer",glrendmode == REND_POLYMER,FALSE,FALSE);
+#endif
+
+ SCRIPT_PutNumber(ud.config.scripthandle, "Screen Setup", "ScreenBPP",ud.config.ScreenBPP,FALSE,FALSE); // JBF 20040523
+ SCRIPT_PutNumber(ud.config.scripthandle, "Screen Setup", "ScreenHeight",ud.config.ScreenHeight,FALSE,FALSE); // JBF 20031206
+ SCRIPT_PutNumber(ud.config.scripthandle, "Screen Setup", "ScreenMode",ud.config.ScreenMode,FALSE,FALSE); // JBF 20031206
+ SCRIPT_PutNumber(ud.config.scripthandle, "Screen Setup", "ScreenWidth",ud.config.ScreenWidth,FALSE,FALSE); // JBF 20031206
+
+ if (g_grpNamePtr && !g_addonNum)
+ SCRIPT_PutString(ud.config.scripthandle, "Setup","SelectedGRP",g_grpNamePtr);
+
+#ifdef STARTUP_SETUP_WINDOW
+ if (g_noSetup == 0)
+ SCRIPT_PutString(ud.config.scripthandle, "Setup","ModDir",&g_modDir[0]);
+#endif
+ // exit early after only updating the values that can be changed from the startup window
+ if (flags & 1)
+ {
+ SCRIPT_Save(ud.config.scripthandle, g_setupFileName);
+ SCRIPT_Free(ud.config.scripthandle);
+ return;
+ }
+
+ SCRIPT_PutNumber(ud.config.scripthandle, "Screen Setup", "WindowPositioning", windowpos, FALSE, FALSE);
+ SCRIPT_PutNumber(ud.config.scripthandle, "Screen Setup", "WindowPosX", windowx, FALSE, FALSE);
+ SCRIPT_PutNumber(ud.config.scripthandle, "Screen Setup", "WindowPosY", windowy, FALSE, FALSE);
+ SCRIPT_PutNumber(ud.config.scripthandle, "Screen Setup", "MaxRefreshFreq", maxrefreshfreq, FALSE, FALSE);
+
+ if (!NAM_WW2GI)
+ {
+ SCRIPT_PutNumber(ud.config.scripthandle, "Screen Setup", "Out",ud.lockout,FALSE,FALSE);
+ SCRIPT_PutString(ud.config.scripthandle, "Screen Setup", "Password",ud.pwlockout);
+ }
+
+#ifdef _WIN32
+ SCRIPT_PutNumber(ud.config.scripthandle, "Updates", "CheckForUpdates", ud.config.CheckForUpdates, FALSE, FALSE);
+ SCRIPT_PutNumber(ud.config.scripthandle, "Updates", "LastUpdateCheck", ud.config.LastUpdateCheck, FALSE, FALSE);
+#endif
+
+ if (ud.config.UseMouse)
+ {
+ for (dummy=0; dummy<MAXMOUSEBUTTONS; dummy++)
+ {
+ if (CONFIG_FunctionNumToName(ud.config.MouseFunctions[dummy][0]))
+ {
+ Bsprintf(buf, "MouseButton%d", dummy);
+ SCRIPT_PutString(ud.config.scripthandle, "Controls", buf, CONFIG_FunctionNumToName(ud.config.MouseFunctions[dummy][0]));
+ }
+
+ if (dummy >= (MAXMOUSEBUTTONS-2)) continue;
+
+ if (CONFIG_FunctionNumToName(ud.config.MouseFunctions[dummy][1]))
+ {
+ Bsprintf(buf, "MouseButtonClicked%d", dummy);
+ SCRIPT_PutString(ud.config.scripthandle, "Controls", buf, CONFIG_FunctionNumToName(ud.config.MouseFunctions[dummy][1]));
+ }
+ }
+
+ for (dummy=0; dummy<MAXMOUSEAXES; dummy++)
+ {
+ if (CONFIG_AnalogNumToName(ud.config.MouseAnalogueAxes[dummy]))
+ {
+ Bsprintf(buf, "MouseAnalogAxes%d", dummy);
+ SCRIPT_PutString(ud.config.scripthandle, "Controls", buf, CONFIG_AnalogNumToName(ud.config.MouseAnalogueAxes[dummy]));
+ }
+
+ if (CONFIG_FunctionNumToName(ud.config.MouseDigitalFunctions[dummy][0]))
+ {
+ Bsprintf(buf, "MouseDigitalAxes%d_0", dummy);
+ SCRIPT_PutString(ud.config.scripthandle, "Controls", buf, CONFIG_FunctionNumToName(ud.config.MouseDigitalFunctions[dummy][0]));
+ }
+
+ if (CONFIG_FunctionNumToName(ud.config.MouseDigitalFunctions[dummy][1]))
+ {
+ Bsprintf(buf, "MouseDigitalAxes%d_1", dummy);
+ SCRIPT_PutString(ud.config.scripthandle, "Controls", buf, CONFIG_FunctionNumToName(ud.config.MouseDigitalFunctions[dummy][1]));
+ }
+
+ if (ud.config.MouseAnalogueScale[dummy] != DEFAULTMOUSEANALOGUESCALE)
+ {
+ Bsprintf(buf, "MouseAnalogScale%d", dummy);
+ SCRIPT_PutNumber(ud.config.scripthandle, "Controls", buf, ud.config.MouseAnalogueScale[dummy], FALSE, FALSE);
+ }
+ }
+ }
+
+ if (ud.config.UseJoystick)
+ {
+ for (dummy=0; dummy<MAXJOYBUTTONSANDHATS; dummy++)
+ {
+ if (CONFIG_FunctionNumToName(ud.config.JoystickFunctions[dummy][0]))
+ {
+ Bsprintf(buf, "JoystickButton%d", dummy);
+ SCRIPT_PutString(ud.config.scripthandle, "Controls", buf, CONFIG_FunctionNumToName(ud.config.JoystickFunctions[dummy][0]));
+ }
+
+ if (CONFIG_FunctionNumToName(ud.config.JoystickFunctions[dummy][1]))
+ {
+ Bsprintf(buf, "JoystickButtonClicked%d", dummy);
+ SCRIPT_PutString(ud.config.scripthandle, "Controls", buf, CONFIG_FunctionNumToName(ud.config.JoystickFunctions[dummy][1]));
+ }
+ }
+ for (dummy=0; dummy<MAXJOYAXES; dummy++)
+ {
+ if (CONFIG_AnalogNumToName(ud.config.JoystickAnalogueAxes[dummy]))
+ {
+ Bsprintf(buf, "JoystickAnalogAxes%d", dummy);
+ SCRIPT_PutString(ud.config.scripthandle, "Controls", buf, CONFIG_AnalogNumToName(ud.config.JoystickAnalogueAxes[dummy]));
+ }
+
+ if (CONFIG_FunctionNumToName(ud.config.JoystickDigitalFunctions[dummy][0]))
+ {
+ Bsprintf(buf, "JoystickDigitalAxes%d_0", dummy);
+ SCRIPT_PutString(ud.config.scripthandle, "Controls", buf, CONFIG_FunctionNumToName(ud.config.JoystickDigitalFunctions[dummy][0]));
+ }
+
+ if (CONFIG_FunctionNumToName(ud.config.JoystickDigitalFunctions[dummy][1]))
+ {
+ Bsprintf(buf, "JoystickDigitalAxes%d_1", dummy);
+ SCRIPT_PutString(ud.config.scripthandle, "Controls", buf, CONFIG_FunctionNumToName(ud.config.JoystickDigitalFunctions[dummy][1]));
+ }
+
+ if (ud.config.JoystickAnalogueScale[dummy] != DEFAULTJOYSTICKANALOGUESCALE)
+ {
+ Bsprintf(buf, "JoystickAnalogScale%d", dummy);
+ SCRIPT_PutNumber(ud.config.scripthandle, "Controls", buf, ud.config.JoystickAnalogueScale[dummy], FALSE, FALSE);
+ }
+
+ if (ud.config.JoystickAnalogueDead[dummy] != DEFAULTJOYSTICKANALOGUEDEAD)
+ {
+ Bsprintf(buf, "JoystickAnalogDead%d", dummy);
+ SCRIPT_PutNumber(ud.config.scripthandle, "Controls", buf, ud.config.JoystickAnalogueDead[dummy], FALSE, FALSE);
+ }
+
+ if (ud.config.JoystickAnalogueSaturate[dummy] != DEFAULTJOYSTICKANALOGUESATURATE)
+ {
+ Bsprintf(buf, "JoystickAnalogSaturate%d", dummy);
+ SCRIPT_PutNumber(ud.config.scripthandle, "Controls", buf, ud.config.JoystickAnalogueSaturate[dummy], FALSE, FALSE);
+ }
+ }
+ }
+
+ SCRIPT_PutString(ud.config.scripthandle, "Comm Setup","PlayerName",&szPlayerName[0]);
+
+ if (g_rtsNamePtr == NULL)
+ SCRIPT_PutString(ud.config.scripthandle, "Comm Setup","RTSName",&ud.rtsname[0]);
+
+ char commmacro[] = "CommbatMacro# ";
+
+ for (dummy = 0; dummy < MAXRIDECULE; dummy++)
+ {
+ commmacro[13] = dummy+'0';
+ SCRIPT_PutString(ud.config.scripthandle, "Comm Setup",commmacro,&ud.ridecule[dummy][0]);
+ }
+
+ SCRIPT_Save(ud.config.scripthandle, g_setupFileName);
+
+ if ((flags & 2) == 0)
+ SCRIPT_Free(ud.config.scripthandle);
+
+ OSD_Printf("Wrote %s\n",g_setupFileName);
+ CONFIG_WriteSettings();
+ Bfflush(NULL);
+}
+
+static const char *CONFIG_GetMapEntryName(char m[], char const * const mapname)
+{
+ strcpy(m, mapname);
+
+ char *p = strrchr(m, '/');
+ if (!p) p = strrchr(m, '\\');
+ if (p) Bmemmove(m, p, Bstrlen(p)+1);
+ for (p=m; *p; p++) *p = tolower(*p);
+
+ // cheap hack because SCRIPT_GetNumber doesn't like the slashes
+ p = m;
+ while (*p == '/') p++;
+
+ return p;
+}
+
+static void CONFIG_GetMD4EntryName(char m[], uint8_t const * const md4)
+{
+ sprintf(m, "MD4_%08x%08x%08x%08x",
+ B_BIG32(B_UNBUF32(&md4[0])), B_BIG32(B_UNBUF32(&md4[4])),
+ B_BIG32(B_UNBUF32(&md4[8])), B_BIG32(B_UNBUF32(&md4[12])));
+}
+
+int32_t CONFIG_GetMapBestTime(char const * const mapname, uint8_t const * const mapmd4)
+{
+ if (!ud.config.setupread || ud.config.scripthandle < 0)
+ return -1;
+
+ char m[37];
+ CONFIG_GetMD4EntryName(m, mapmd4);
+
+ int32_t t = -1;
+ if (SCRIPT_GetNumber(ud.config.scripthandle, "MapTimes", m, &t))
+ {
+ // fall back to map filenames
+ char m2[BMAX_PATH];
+ char const * const p = CONFIG_GetMapEntryName(m2, mapname);
+ SCRIPT_GetNumber(ud.config.scripthandle, "MapTimes", p, &t);
+ }
+ return t;
+}
+
+int32_t CONFIG_SetMapBestTime(uint8_t const * const mapmd4, int32_t const tm)
+{
+ if (ud.config.scripthandle < 0) ud.config.scripthandle = SCRIPT_Init(g_setupFileName);
+ if (ud.config.scripthandle < 0) return -1;
+
+ char m[37];
+ CONFIG_GetMD4EntryName(m, mapmd4);
+
+ SCRIPT_PutNumber(ud.config.scripthandle, "MapTimes", m, tm, FALSE, FALSE);
+ return 0;
+}
+
diff --git a/patch/fluidsynth_inc.h b/patch/fluidsynth_inc.h
new file mode 100644
index 0000000..957ae9e
--- /dev/null
+++ b/patch/fluidsynth_inc.h
@@ -0,0 +1,6 @@
+#ifndef FLUIDSYNTH_INC_H_
+#define FLUIDSYNTH_INC_H_
+
+#include <fluidsynth.h>
+
+#endif /* FLUIDSYNTH_INC_H_ */
diff --git a/patch/game.h b/patch/game.h
new file mode 100644
index 0000000..93d3e22
--- /dev/null
+++ b/patch/game.h
@@ -0,0 +1,568 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 2016 EDuke32 developers and contributors
+
+This file is part of EDuke32.
+
+EDuke32 is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License version 2
+as published by the Free Software Foundation.
+
+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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+//-------------------------------------------------------------------------
+
+#pragma once
+
+#ifndef game_h_
+#define game_h_
+
+#ifndef ONLY_USERDEFS
+#include "premap.h" // XXX
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef ONLY_USERDEFS
+
+// Compile game-side legacy Room over Room code?
+#define LEGACY_ROR 1
+
+#define USERQUOTE_LEFTOFFSET 5
+#define USERQUOTE_RIGHTOFFSET 14
+
+#if defined(GEKKO) || defined(__OPENDINGUX__)
+# define VIEWSCREENFACTOR 0
+#elif defined(__ANDROID__)
+# define VIEWSCREENFACTOR 1
+#else
+# define VIEWSCREENFACTOR 2
+#endif
+
+enum GametypeFlags_t {
+ GAMETYPE_COOP = 0x00000001,
+ GAMETYPE_WEAPSTAY = 0x00000002,
+ GAMETYPE_FRAGBAR = 0x00000004,
+ GAMETYPE_SCORESHEET = 0x00000008,
+ GAMETYPE_DMSWITCHES = 0x00000010,
+ GAMETYPE_COOPSPAWN = 0x00000020,
+ GAMETYPE_ACCESSCARDSPRITES = 0x00000040,
+ GAMETYPE_COOPVIEW = 0x00000080,
+ GAMETYPE_COOPSOUND = 0x00000100,
+ GAMETYPE_OTHERPLAYERSINMAP = 0x00000200,
+ GAMETYPE_ITEMRESPAWN = 0x00000400,
+ GAMETYPE_MARKEROPTION = 0x00000800,
+ GAMETYPE_PLAYERSFRIENDLY = 0x00001000,
+ GAMETYPE_FIXEDRESPAWN = 0x00002000,
+ GAMETYPE_ACCESSATSTART = 0x00004000,
+ GAMETYPE_PRESERVEINVENTORYDEATH = 0x00008000,
+ GAMETYPE_TDM = 0x00010000,
+ GAMETYPE_TDMSPAWN = 0x00020000
+};
+
+// logo control
+enum LogoFlags_t {
+ LOGO_ENABLED = 0x00000001,
+ LOGO_PLAYANIM = 0x00000002,
+ LOGO_PLAYMUSIC = 0x00000004,
+ LOGO_3DRSCREEN = 0x00000008,
+ LOGO_TITLESCREEN = 0x00000010,
+ LOGO_DUKENUKEM = 0x00000020,
+ LOGO_THREEDEE = 0x00000040,
+ LOGO_PLUTOPAKSPRITE = 0x00000080,
+ LOGO_SHAREWARESCREENS = 0x00000100,
+ LOGO_TENSCREEN = 0x00000200,
+ LOGO_STOPANIMSOUNDS = 0x00000400,
+ LOGO_NOE4CUTSCENE = 0x00000800,
+ LOGO_NOE1BONUSSCENE = 0x00001000,
+ LOGO_NOE2BONUSSCENE = 0x00002000,
+ LOGO_NOE3BONUSSCENE = 0x00004000,
+ LOGO_NOE4BONUSSCENE = 0x00008000,
+ LOGO_NOE1ENDSCREEN = 0x00010000,
+ LOGO_NOE2ENDSCREEN = 0x00020000,
+ LOGO_NOE3RADLOGO = 0x00040000,
+ LOGO_NODUKETEAMTEXT = 0x00080000,
+ LOGO_NODUKETEAMPIC = 0x00100000,
+ LOGO_STOPMISCSOUNDS = 0x00200000,
+ LOGO_NOGAMETITLE = 0x00400000,
+ LOGO_NOTITLEBAR = 0x00800000,
+};
+
+void A_DeleteSprite(int spriteNum);
+
+static inline int32_t G_GetLogoFlags(void)
+{
+#if !defined LUNATIC
+ return Gv_GetVarByLabel("LOGO_FLAGS",255, -1, -1);
+#else
+ extern int32_t g_logoFlags;
+ return g_logoFlags;
+#endif
+}
+
+#ifdef LUNATIC
+typedef struct {
+ vec3_t pos;
+ int32_t dist, clock;
+ int16_t ang, horiz, sect;
+} camera_t;
+
+extern camera_t g_camera;
+
+# define CAMERA(Membname) (g_camera.Membname)
+# define CAMERADIST (g_camera.dist)
+# define CAMERACLOCK (g_camera.clock)
+#else
+# define CAMERA(Membname) (ud.camera ## Membname)
+# define CAMERADIST g_cameraDistance
+# define CAMERACLOCK g_cameraClock
+#endif
+
+#endif
+
+#define MAXRIDECULE 10
+#define MAXRIDECULELENGTH 40
+#define MAXSAVEGAMENAMESTRUCT 32
+#define MAXSAVEGAMENAME (MAXSAVEGAMENAMESTRUCT-1)
+#define MAXPWLOCKOUT 128
+#define MAXRTSNAME 128
+
+// KEEPINSYNC lunatic/_defs_game.lua
+typedef struct {
+#if !defined LUNATIC
+ vec3_t camerapos;
+#endif
+ int32_t const_visibility,uw_framerate;
+ int32_t camera_time,folfvel,folavel,folx,foly,fola;
+ int32_t reccnt,crosshairscale;
+
+ int32_t runkey_mode,statusbarscale,mouseaiming,weaponswitch,drawweapon; // JBF 20031125
+ int32_t democams,color,msgdisptime,statusbarmode;
+ int32_t m_noexits,noexits,autovote,automsg,idplayers;
+ int32_t team, viewbob, weaponsway, althud, weaponscale, textscale;
+ int32_t screenarea_x1, screenarea_y1, screenarea_x2, screenarea_y2;
+
+ int32_t entered_name,screen_tilting,shadows,fta_on,executions,auto_run;
+ int32_t coords,showfps,levelstats,m_coop,coop,screen_size,lockout,crosshair;
+ int32_t playerai,angleinterpolation,obituaries;
+
+ int32_t respawn_monsters,respawn_items,respawn_inventory,recstat,monsters_off,brightness;
+ int32_t m_respawn_items,m_respawn_monsters,m_respawn_inventory,m_recstat,m_monsters_off,detail;
+ int32_t m_ffire,ffire,m_player_skill,m_level_number,m_volume_number,multimode;
+ int32_t player_skill,level_number,volume_number,m_marker,marker,mouseflip;
+ int32_t music_episode, music_level;
+
+ vec2_t m_origin;
+ int32_t playerbest;
+
+ int32_t configversion, bgstretch;
+#if !defined LUNATIC
+ int16_t cameraang, camerasect, camerahoriz;
+#endif
+ int16_t pause_on,from_bonus;
+ int16_t camerasprite,last_camsprite;
+ int16_t last_level,secretlevel;
+
+ struct {
+ int32_t UseJoystick;
+ int32_t UseMouse;
+ int32_t AutoAim;
+ int32_t ShowOpponentWeapons;
+ int32_t MouseDeadZone,MouseBias;
+ int32_t SmoothInput;
+
+ // JBF 20031211: Store the input settings because
+ // (currently) mact can't regurgitate them
+ int32_t MouseFunctions[MAXMOUSEBUTTONS][2];
+ int32_t MouseDigitalFunctions[MAXMOUSEAXES][2];
+ int32_t MouseAnalogueAxes[MAXMOUSEAXES];
+ int32_t MouseAnalogueScale[MAXMOUSEAXES];
+ int32_t JoystickFunctions[MAXJOYBUTTONSANDHATS][2];
+ int32_t JoystickDigitalFunctions[MAXJOYAXES][2];
+ int32_t JoystickAnalogueAxes[MAXJOYAXES];
+ int32_t JoystickAnalogueScale[MAXJOYAXES];
+ int32_t JoystickAnalogueDead[MAXJOYAXES];
+ int32_t JoystickAnalogueSaturate[MAXJOYAXES];
+ uint8_t KeyboardKeys[NUMGAMEFUNCTIONS][2];
+
+ //
+ // Sound variables
+ //
+ int32_t MasterVolume;
+ int32_t FXVolume;
+ int32_t MusicVolume;
+ int32_t SoundToggle;
+ int32_t MusicToggle;
+ int32_t VoiceToggle;
+ int32_t AmbienceToggle;
+
+ int32_t NumVoices;
+ int32_t NumChannels;
+ int32_t NumBits;
+ int32_t MixRate;
+
+ int32_t ReverseStereo;
+
+ int32_t FluidsynthBackend;
+
+ //
+ // Screen variables
+ //
+
+ int32_t ScreenMode;
+
+ int32_t ScreenWidth;
+ int32_t ScreenHeight;
+ int32_t ScreenBPP;
+
+ int32_t ForceSetup;
+ int32_t NoAutoLoad;
+
+ int32_t scripthandle;
+ int32_t setupread;
+
+ int32_t CheckForUpdates;
+ int32_t LastUpdateCheck;
+ int32_t useprecache;
+ } config;
+
+ char overhead_on,last_overhead,showweapons;
+ char god,warp_on,cashman,eog,showallmap;
+ char show_help,scrollmode,noclip;
+ char ridecule[MAXRIDECULE][MAXRIDECULELENGTH];
+ char pwlockout[MAXPWLOCKOUT],rtsname[MAXRTSNAME];
+ char display_bonus_screen;
+ char show_level_text;
+ char wchoice[MAX_WEAPONS];
+
+ uint8_t user_map;
+ uint8_t screenfade;
+} user_defs;
+
+extern user_defs ud;
+
+#ifndef ONLY_USERDEFS
+
+// this is checked against http://eduke32.com/VERSION
+extern const char *s_buildDate;
+
+extern const char *g_rtsNamePtr;
+
+extern char boardfilename[BMAX_PATH], currentboardfilename[BMAX_PATH];
+extern char boardfilename[BMAX_PATH];
+
+extern const char *defaultrtsfilename[GAMECOUNT];
+extern const char *G_DefaultRtsFile(void);
+
+#ifdef LEGACY_ROR
+extern char ror_protectedsectors[MAXSECTORS];
+#endif
+
+extern float r_ambientlight;
+
+extern int32_t g_Debug;
+extern int32_t g_Shareware;
+#if !defined LUNATIC
+extern int32_t g_cameraClock;
+extern int32_t g_cameraDistance;
+#endif
+extern int32_t g_crosshairSum;
+extern int32_t g_doQuickSave;
+extern int32_t g_forceWeaponChoice;
+extern int32_t g_fakeMultiMode;
+extern int32_t g_levelTextTime;
+extern int32_t g_quitDeadline;
+extern int32_t g_restorePalette;
+extern int32_t hud_glowingquotes;
+extern int32_t hud_showmapname;
+extern int32_t r_maxfps;
+extern int32_t tempwallptr;
+extern int32_t ticrandomseed;
+extern int32_t vote_map;
+extern int32_t voting;
+
+extern int32_t MAXCACHE1DSIZE;
+//extern int8_t cheatbuf[MAXCHEATLEN],cheatbuflen;
+
+#define CROSSHAIR_PAL (MAXPALOOKUPS-RESERVEDPALS-1)
+
+extern palette_t CrosshairColors;
+extern palette_t DefaultCrosshairColors;
+
+extern uint64_t g_frameDelay;
+
+int32_t A_CheckInventorySprite(spritetype *s);
+int32_t A_InsertSprite(int16_t whatsect, int32_t s_x, int32_t s_y, int32_t s_z, int16_t s_pn, int8_t s_s, uint8_t s_xr,
+ uint8_t s_yr, int16_t s_a, int16_t s_ve, int16_t s_zv, int16_t s_ow, int16_t s_ss);
+int A_Spawn(int j,int pn);
+int G_DoMoveThings(void);
+//int32_t G_EndOfLevel(void);
+
+#ifdef YAX_ENABLE
+void Yax_SetBunchZs(int32_t sectnum, int32_t cf, int32_t daz);
+#else
+#define Yax_SetBunchZs(sectnum, cf, daz)
+#endif
+
+#ifdef LUNATIC
+void El_CreateGameState(void);
+#endif
+void G_PostCreateGameState(void);
+
+void A_SpawnCeilingGlass(int spriteNum,int sectNum,int glassCnt);
+void A_SpawnGlass(int spriteNum,int glassCnt);
+void A_SpawnRandomGlass(int spriteNum,int wallNum,int glassCnt);
+void A_SpawnWallGlass(int i,int wallnum,int n);
+void G_AddUserQuote(const char *daquote);
+void G_BackToMenu(void);
+void G_DumpDebugInfo(void);
+
+const char* G_PrintYourTime(void);
+const char* G_PrintParTime(void);
+const char* G_PrintDesignerTime(void);
+const char* G_PrintBestTime(void);
+void G_BonusScreen(int32_t bonusonly);
+//void G_CheatGetInv(void);
+void G_DisplayRest(int32_t smoothratio);
+void G_DoSpriteAnimations(int32_t ourx, int32_t oury, int32_t oura, int32_t smoothratio);
+void G_DrawBackground(void);
+void G_DrawFrags(void);
+void G_HandleMirror(int32_t x, int32_t y, int32_t z, int32_t a, int32_t horiz, int32_t smoothratio);
+void G_DrawRooms(int32_t snum,int32_t smoothratio);
+void G_DrawTXDigiNumZ(int32_t starttile,int32_t x,int32_t y,int32_t n,int32_t s,int32_t pal,int32_t cs,int32_t x1,int32_t y1,int32_t x2,int32_t y2,int32_t z);
+int G_FPSLimit(void);
+void G_GameExit(const char *t) ATTRIBUTE((noreturn));
+void G_GameQuit(void);
+void G_GetCrosshairColor(void);
+void G_HandleLocalKeys(void);
+void G_HandleSpecialKeys(void);
+void G_UpdateAppTitle(void);
+void G_PrintGameQuotes(int32_t snum);
+//void G_SE40(int32_t smoothratio);
+void G_SetCrosshairColor(int32_t r,int32_t g,int32_t b);
+void G_Shutdown(void);
+void G_UpdatePlayerFromMenu(void);
+void M32RunScript(const char *s);
+void P_DoQuote(int32_t q,DukePlayer_t *p);
+void P_SetGamePalette(DukePlayer_t *player, uint32_t palid, int32_t set);
+
+#define NEG_ALPHA_TO_BLEND(alpha, blend, orientation) do { \
+ if (alpha < 0) { blend = -alpha; alpha = 0; orientation |= RS_TRANS1; } \
+} while (0)
+
+// Cstat protection mask for (currently) spawned MASKWALL* sprites.
+// TODO: look at more cases of cstat=(cstat&PROTECTED)|ADDED in A_Spawn()?
+// 2048+(32+16)+8+4
+#define SPAWN_PROTECT_CSTAT_MASK (CSTAT_SPRITE_NOSHADE|CSTAT_SPRITE_SLAB|CSTAT_SPRITE_XFLIP|CSTAT_SPRITE_YFLIP);
+
+void fadepal(int32_t r,int32_t g,int32_t b,int32_t start,int32_t end,int32_t step);
+//void fadepaltile(int32_t r,int32_t g,int32_t b,int32_t start,int32_t end,int32_t step,int32_t tile);
+void G_InitTimer(int32_t ticpersec);
+
+static inline int32_t G_GetTeamPalette(int32_t team)
+{
+ int8_t pal[] = { 3, 10, 11, 12 };
+
+ if ((unsigned)team >= ARRAY_SIZE(pal))
+ return 0;
+
+ return pal[team];
+}
+
+#define A_CheckSpriteFlags(spriteNum, iType) (((g_tile[sprite[spriteNum].picnum].flags^actor[spriteNum].flags) & iType) != 0)
+// (unsigned)iPicnum check: AMC TC Rusty Nails, bayonet MG alt. fire, iPicnum == -1 (via aplWeaponShoots)
+#define A_CheckSpriteTileFlags(iPicnum, iType) (((unsigned)iPicnum < MAXTILES) && (g_tile[iPicnum].flags & iType) != 0)
+#define S_StopSound(num) S_StopEnvSound(num, -1)
+
+extern int G_StartRTS(int lumpNum, int localPlayer);
+
+extern void G_MaybeAllocPlayer(int32_t pnum);
+
+static inline void G_HandleAsync(void)
+{
+ handleevents();
+ Net_GetPackets();
+}
+
+static inline int32_t calc_smoothratio(int32_t totalclk, int32_t ototalclk)
+{
+ return clamp((totalclk-ototalclk)*(65536/TICSPERFRAME), 0, 65536);
+}
+
+// sector effector lotags
+enum
+{
+ SE_0_ROTATING_SECTOR = 0,
+ SE_1_PIVOT = 1,
+ SE_2_EARTHQUAKE = 2,
+ SE_3_RANDOM_LIGHTS_AFTER_SHOT_OUT = 3,
+ SE_4_RANDOM_LIGHTS = 4,
+ SE_5 = 5,
+ SE_6_SUBWAY = 6,
+ // ^^ potentially incomplete substitution in code
+ // vv almost surely complete substitution
+ SE_7_TELEPORT = 7,
+ SE_8_UP_OPEN_DOOR_LIGHTS = 8,
+ SE_9_DOWN_OPEN_DOOR_LIGHTS = 9,
+ SE_10_DOOR_AUTO_CLOSE = 10,
+ SE_11_SWINGING_DOOR = 11,
+ SE_12_LIGHT_SWITCH = 12,
+ SE_13_EXPLOSIVE = 13,
+ SE_14_SUBWAY_CAR = 14,
+ SE_15_SLIDING_DOOR = 15,
+ SE_16_REACTOR = 16,
+ SE_17_WARP_ELEVATOR = 17,
+ SE_18_INCREMENTAL_SECTOR_RISE_FALL = 18,
+ SE_19_EXPLOSION_LOWERS_CEILING = 19,
+ SE_20_STRETCH_BRIDGE = 20,
+ SE_21_DROP_FLOOR = 21,
+ SE_22_TEETH_DOOR = 22,
+ SE_23_ONE_WAY_TELEPORT = 23,
+ SE_24_CONVEYOR = 24,
+ SE_25_PISTON = 25,
+ SE_26 = 26,
+ SE_27_DEMO_CAM = 27,
+ SE_28_LIGHTNING = 28,
+ SE_29_WAVES = 29,
+ SE_30_TWO_WAY_TRAIN = 30,
+ SE_31_FLOOR_RISE_FALL = 31,
+ SE_32_CEILING_RISE_FALL = 32,
+ SE_33_QUAKE_DEBRIS = 33,
+ SE_34 = 34, // XXX
+ SE_35 = 35, // XXX
+ SE_36_PROJ_SHOOTER = 36,
+ SE_49_POINT_LIGHT = 49,
+ SE_50_SPOT_LIGHT = 50,
+ SE_130 = 130,
+ SE_131 = 131,
+};
+
+// sector lotags
+enum
+{
+ ST_0_NO_EFFECT = 0,
+ ST_1_ABOVE_WATER = 1,
+ ST_2_UNDERWATER = 2,
+ ST_3 = 3,
+ // ^^^ maybe not complete substitution in code
+ ST_9_SLIDING_ST_DOOR = 9,
+ ST_15_WARP_ELEVATOR = 15,
+ ST_16_PLATFORM_DOWN = 16,
+ ST_17_PLATFORM_UP = 17,
+ ST_18_ELEVATOR_DOWN = 18,
+ ST_19_ELEVATOR_UP = 19,
+ ST_20_CEILING_DOOR = 20,
+ ST_21_FLOOR_DOOR = 21,
+ ST_22_SPLITTING_DOOR = 22,
+ ST_23_SWINGING_DOOR = 23,
+ ST_25_SLIDING_DOOR = 25,
+ ST_26_SPLITTING_ST_DOOR = 26,
+ ST_27_STRETCH_BRIDGE = 27,
+ ST_28_DROP_FLOOR = 28,
+ ST_29_TEETH_DOOR = 29,
+ ST_30_ROTATE_RISE_BRIDGE = 30,
+ ST_31_TWO_WAY_TRAIN = 31,
+ // left: ST 32767, 65534, 65535
+};
+
+
+#define G_ModDirSnprintf(buf, size, basename, ...) \
+ \
+(((g_modDir[0] != '/') ? Bsnprintf(buf, size, "%s/" basename, g_modDir, ##__VA_ARGS__) \
+ : Bsnprintf(buf, size, basename, ##__VA_ARGS__)) >= ((int32_t)size) - 1\
+)
+#define G_ModDirSnprintfLite(buf, size, basename) \
+ \
+((g_modDir[0] != '/') ? Bsnprintf(buf, size, "%s/%s", g_modDir, basename) \
+ : Bsnprintf(buf, size, basename))
+
+static inline void G_NewGame_EnterLevel(void)
+{
+ G_NewGame(ud.m_volume_number, ud.m_level_number, ud.m_player_skill);
+
+ if (G_EnterLevel(MODE_GAME))
+ G_BackToMenu();
+}
+
+static inline int G_GetMusicIdx(const char *str)
+{
+ int32_t lev, ep;
+ char b1, b2;
+
+ int numMatches = sscanf(str, "%c%d%c%d", &b1,&ep, &b2,&lev);
+
+ if (numMatches != 4 || Btoupper(b1) != 'E' || Btoupper(b2) != 'L')
+ return -1;
+
+ if ((unsigned)--lev >= MAXLEVELS || (unsigned)--ep >= MAXVOLUMES)
+ return -2;
+
+ return (ep * MAXLEVELS) + lev;
+}
+
+static inline int G_GetViewscreenSizeShift(const uspritetype *tspr)
+{
+#if VIEWSCREENFACTOR == 0
+ UNREFERENCED_PARAMETER(tspr);
+ return VIEWSCREENFACTOR;
+#else
+ static const int mask = (1<<VIEWSCREENFACTOR)-1;
+ const int rem = (tspr->xrepeat & mask) | (tspr->yrepeat & mask);
+
+ for (bssize_t i=0; i < VIEWSCREENFACTOR; i++)
+ if (rem & (1<<i))
+ return i;
+
+ return VIEWSCREENFACTOR;
+#endif
+}
+
+extern void G_StartMusic(void);
+
+#ifdef LUNATIC
+void El_SetCON(const char *conluacode);
+#endif
+
+EXTERN_INLINE_HEADER void G_SetStatusBarScale(int32_t sc);
+
+EXTERN_INLINE_HEADER void SetIfGreater(int32_t *variable, int32_t potentialValue);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifndef ONLY_USERDEFS
+
+#if defined game_c_ || !defined DISABLE_INLINING
+
+EXTERN_INLINE void G_SetStatusBarScale(int32_t sc)
+{
+ ud.statusbarscale = clamp(sc, 36, 100);
+ G_UpdateScreenArea();
+}
+
+// the point of this is to prevent re-running a function or calculation passed to potentialValue
+// without making a new variable under each individual circumstance
+EXTERN_INLINE void SetIfGreater(int32_t *variable, int32_t potentialValue)
+{
+ if (potentialValue > *variable)
+ *variable = potentialValue;
+}
+
+#endif
+
+#endif
+
+#endif
diff --git a/patch/menus.cpp b/patch/menus.cpp
new file mode 100644
index 0000000..e13ee57
--- /dev/null
+++ b/patch/menus.cpp
@@ -0,0 +1,6639 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 2016 EDuke32 developers and contributors
+
+This file is part of EDuke32.
+
+EDuke32 is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License version 2
+as published by the Free Software Foundation.
+
+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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+//-------------------------------------------------------------------------
+
+#include "compat.h"
+#include "duke3d.h"
+#include "osdcmds.h"
+#include "savegame.h"
+#include "demo.h"
+#include "xxhash.h"
+#include "input.h"
+#include "menus.h"
+#include "cheats.h"
+
+#include "in_android.h"
+#ifndef __ANDROID__
+droidinput_t droidinput;
+#endif
+
+// common positions
+#define MENU_MARGIN_REGULAR 40
+#define MENU_MARGIN_WIDE 32
+#define MENU_MARGIN_CENTER 160
+#define MENU_HEIGHT_CENTER 100
+
+int32_t g_skillSoundVoice = -1;
+
+extern int32_t voting;
+
+#define USERMAPENTRYLENGTH 25
+
+static FORCE_INLINE void WithSDL2_StartTextInput()
+{
+#if defined EDUKE32_TOUCH_DEVICES && defined SDL_MAJOR_VERSION && SDL_MAJOR_VERSION > 1
+# if defined __ANDROID__
+ AndroidShowKeyboard(1);
+# else
+ SDL_StartTextInput();
+# endif
+#endif
+}
+
+static FORCE_INLINE void WithSDL2_StopTextInput()
+{
+#if defined EDUKE32_TOUCH_DEVICES && defined SDL_MAJOR_VERSION && SDL_MAJOR_VERSION > 1
+# if defined __ANDROID__
+ AndroidShowKeyboard(0);
+# else
+ SDL_StopTextInput();
+# endif
+#endif
+}
+
+static FORCE_INLINE void rotatesprite_ybounds(int32_t sx, int32_t sy, int32_t z, int16_t a, int16_t picnum, int8_t dashade, char dapalnum, int32_t dastat, int32_t ydim_upper, int32_t ydim_lower)
+{
+ rotatesprite_(sx, sy, z, a, picnum, dashade, dapalnum, dastat, 0, 0, 0, ydim_upper, xdim-1, ydim_lower);
+}
+
+#ifndef EDUKE32_STANDALONE
+static void mgametext(int32_t x, int32_t y, char const * t)
+{
+ G_ScreenText(MF_Bluefont.tilenum, x, y, MF_Bluefont.zoom, 0, 0, t, 0, MF_Bluefont.pal, 2|8|16|ROTATESPRITE_FULL16, 0, MF_Bluefont.emptychar.x, MF_Bluefont.emptychar.y, MF_Bluefont.between.x, MF_Bluefont.between.y, MF_Bluefont.textflags, 0, 0, xdim-1, ydim-1);
+}
+#endif
+static vec2_t mgametextcenter(int32_t x, int32_t y, char const * t, int32_t f = 0)
+{
+ return G_ScreenText(MF_Bluefont.tilenum, (MENU_MARGIN_CENTER<<16) + x, y, MF_Bluefont.zoom, 0, 0, t, 0, MF_Bluefont.pal, 2|8|16|ROTATESPRITE_FULL16, 0, MF_Bluefont.emptychar.x, MF_Bluefont.emptychar.y, MF_Bluefont.between.x, MF_Bluefont.between.y, MF_Bluefont.textflags|f|TEXT_XCENTER, 0, 0, xdim-1, ydim-1);
+}
+
+#define mminitext(x,y,t,p) minitext_(x, y, t, 0, p, 2|8|16|ROTATESPRITE_FULL16)
+#define mmenutext menutext
+
+#ifndef EDUKE32_STANDALONE
+static void shadowminitext(int32_t x, int32_t y, const char *t, int32_t p)
+{
+ int32_t f = 0;
+
+ if (!minitext_lowercase)
+ f |= TEXT_UPPERCASE;
+
+ G_ScreenTextShadow(1, 1, MINIFONT, x, y, 65536, 0, 0, t, 0, p, 2|8|16|ROTATESPRITE_FULL16, 0, 4<<16, 8<<16, 1<<16, 0, f, 0, 0, xdim-1, ydim-1);
+}
+#endif
+static void creditsminitext(int32_t x, int32_t y, const char *t, int32_t p)
+{
+ int32_t f = TEXT_XCENTER;
+
+ if (!minitext_lowercase)
+ f |= TEXT_UPPERCASE;
+
+ G_ScreenTextShadow(1, 1, MINIFONT, x, y, 65536, 0, 0, t, 0, p, 2|8|16|ROTATESPRITE_FULL16, 0, 4<<16, 8<<16, 1<<16, 0, f, 0, 0, xdim-1, ydim-1);
+}
+
+#pragma pack(push,1)
+static savehead_t savehead;
+#pragma pack(pop)
+
+static void Menu_DrawBackground(const vec2_t origin)
+{
+ rotatesprite_fs(origin.x + (MENU_MARGIN_CENTER<<16), origin.y + (100<<16), 65536L,0,MENUSCREEN,16,0,10+64);
+}
+
+static void Menu_DrawTopBar(const vec2_t origin)
+{
+ if ((G_GetLogoFlags() & LOGO_NOTITLEBAR) == 0)
+ rotatesprite_fs(origin.x + (MENU_MARGIN_CENTER<<16), origin.y + (19<<16), MF_Redfont.cursorScale, 0,MENUBAR,16,0,10);
+}
+
+static void Menu_DrawTopBarCaption(const char *caption, const vec2_t origin)
+{
+ char *s = Bstrdup(caption), p = Bstrlen(caption)-1;
+ if (s[p] == ':') s[p] = 0;
+ menutext_(origin.x + (MENU_MARGIN_CENTER<<16), origin.y + (24<<16) + (15<<15), 0, s, 10|16, TEXT_XCENTER|TEXT_YCENTER);
+ Bfree(s);
+}
+
+static FORCE_INLINE int32_t Menu_CursorShade(void)
+{
+ return 4-(sintable[(totalclock<<4)&2047]>>11);
+}
+static void Menu_DrawCursorCommon(int32_t x, int32_t y, int32_t z, int32_t picnum, int32_t ydim_upper = 0, int32_t ydim_lower = ydim-1)
+{
+ rotatesprite_(x, y, z, 0, picnum, Menu_CursorShade(), 0, 2|8, 0, 0, 0, ydim_upper, xdim-1, ydim_lower);
+}
+static void Menu_DrawCursorLeft(int32_t x, int32_t y, int32_t z)
+{
+ if (KXDWN) return;
+ Menu_DrawCursorCommon(x, y, z, SPINNINGNUKEICON+((totalclock>>3)%7));
+}
+static void Menu_DrawCursorRight(int32_t x, int32_t y, int32_t z)
+{
+ if (KXDWN) return;
+ Menu_DrawCursorCommon(x, y, z, SPINNINGNUKEICON+6-((6+(totalclock>>3))%7));
+}
+static void Menu_DrawCursorTextTile(int32_t x, int32_t y, int32_t h, int32_t picnum, vec2s_t const & siz, int32_t ydim_upper = 0, int32_t ydim_lower = ydim-1)
+{
+ vec2_t const adjsiz = { siz.x<<15, siz.y<<16 };
+ Menu_DrawCursorCommon(x + scale(adjsiz.x, h, adjsiz.y), y, divscale16(h, adjsiz.y), picnum, ydim_upper, ydim_lower);
+}
+static void Menu_DrawCursorText(int32_t x, int32_t y, int32_t h, int32_t ydim_upper = 0, int32_t ydim_lower = ydim-1)
+{
+ vec2s_t const & siz = tilesiz[SPINNINGNUKEICON];
+
+ if (KXDWN || siz.x == 0)
+ {
+ Menu_DrawCursorTextTile(x, y, h, SMALLFNTCURSOR, tilesiz[SMALLFNTCURSOR], ydim_upper, ydim_lower);
+ return;
+ }
+
+ Menu_DrawCursorTextTile(x, y, h, SPINNINGNUKEICON+((totalclock>>3)%7), siz, ydim_upper, ydim_lower);
+}
+
+extern int32_t g_quitDeadline;
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*
+All MAKE_* macros are generally for the purpose of keeping state initialization
+separate from actual data. Alternatively, they can serve to factor out repetitive
+stuff and keep the important bits from getting lost to our eyes.
+
+They serve as a stand-in for C++ default value constructors, since we're using C89.
+
+Note that I prefer to include a space on the inside of the macro parentheses, since
+they effectively stand in for curly braces as struct initializers.
+*/
+
+
+// common font types
+// tilenums are set after namesdyn runs
+MenuFont_t MF_Redfont = { { 5<<16, 15<<16 }, { 0, 0 }, 65536, 20<<16, 110<<16, 65536, TEXT_BIGALPHANUM | TEXT_UPPERCASE, -1, 10, 0, 1 };
+MenuFont_t MF_Bluefont = { { 5<<16, 7<<16 }, { -(1<<16), 2<<16 }, 65536, 10<<16, 110<<16, 32768, 0, -1, 10, 0, 16 };
+MenuFont_t MF_BluefontRed = { { 5<<16, 7<<16 }, { -(1<<16), 2<<16 }, 65536, 10<<16, 110<<16, 32768, 0, -1, 10, 10, 16 };
+MenuFont_t MF_BluefontGame = { { 5<<16, 7<<16 }, { 0, 0 }, 65536, 10<<16, 110<<16, 32768, 0, -1, 10, 0, 16 };
+static MenuFont_t MF_Minifont = { { 4<<16, 5<<16 }, { 1<<16, 1<<16 }, 65536, 10<<16, 110<<16, 32768, 0, -1, 10, 0, 16 };
+static MenuFont_t MF_MinifontRed = { { 4<<16, 5<<16 }, { 1<<16, 1<<16 }, 65536, 10<<16, 110<<16, 32768, 0, -1, 16, 21, 16 };
+MenuFont_t MF_MinifontSave = { { 4<<16, 5<<16 }, { 1<<16, 1<<16 }, 65536, 10<<16, 110<<16, 32768, 0, -1, 10, 0, 13 };
+
+
+static MenuMenuFormat_t MMF_Top_Main = { { MENU_MARGIN_CENTER<<16, 55<<16, }, -(170<<16) };
+static MenuMenuFormat_t MMF_Top_Episode = { { MENU_MARGIN_CENTER<<16, 48<<16, }, -(190<<16) };
+static MenuMenuFormat_t MMF_Top_Skill = { { MENU_MARGIN_CENTER<<16, 58<<16, }, -(190<<16) };
+static MenuMenuFormat_t MMF_Top_Options = { { MENU_MARGIN_CENTER<<16, 38<<16, }, -(190<<16) };
+static MenuMenuFormat_t MMF_Top_Joystick_Network = { { MENU_MARGIN_CENTER<<16, 70<<16, }, -(190<<16) };
+static MenuMenuFormat_t MMF_BigOptions = { { MENU_MARGIN_WIDE<<16, 38<<16, }, -(190<<16) };
+static MenuMenuFormat_t MMF_SmallOptions = { { MENU_MARGIN_WIDE<<16, 37<<16, }, 170<<16 };
+static MenuMenuFormat_t MMF_Macros = { { 26<<16, 40<<16, }, 160<<16 };
+static MenuMenuFormat_t MMF_SmallOptionsNarrow = { { MENU_MARGIN_REGULAR<<16, 38<<16, }, -(190<<16) };
+static MenuMenuFormat_t MMF_KeyboardSetupFuncs = { { 70<<16, 34<<16, }, 151<<16 };
+static MenuMenuFormat_t MMF_MouseJoySetupBtns = { { 76<<16, 34<<16, }, 143<<16 };
+static MenuMenuFormat_t MMF_FuncList = { { 100<<16, 51<<16, }, 152<<16 };
+static MenuMenuFormat_t MMF_ColorCorrect = { { MENU_MARGIN_REGULAR<<16, 86<<16, }, 190<<16 };
+static MenuMenuFormat_t MMF_BigSliders = { { MENU_MARGIN_WIDE<<16, 37<<16, }, 190<<16 };
+static MenuMenuFormat_t MMF_LoadSave = { { 200<<16, 49<<16, }, 145<<16 };
+static MenuMenuFormat_t MMF_NetSetup = { { 36<<16, 38<<16, }, 190<<16 };
+static MenuMenuFormat_t MMF_FileSelectLeft = { { 40<<16, 45<<16, }, 162<<16 };
+static MenuMenuFormat_t MMF_FileSelectRight = { { 164<<16, 45<<16, }, 162<<16 };
+
+static MenuEntryFormat_t MEF_Null = { 0, 0, 0 };
+static MenuEntryFormat_t MEF_MainMenu = { 4<<16, 0, 0 };
+static MenuEntryFormat_t MEF_OptionsMenu = { 7<<16, 0, 0 };
+static MenuEntryFormat_t MEF_CenterMenu = { 7<<16, 0, 0 };
+static MenuEntryFormat_t MEF_BigOptions_Apply = { 4<<16, 16<<16, -(244<<16) };
+static MenuEntryFormat_t MEF_BigOptionsRt = { 4<<16, 0, -(260<<16) };
+#if defined USE_OPENGL || !defined EDUKE32_ANDROID_MENU
+static MenuEntryFormat_t MEF_SmallOptions = { 1<<16, 0, 216<<16 };
+#endif
+static MenuEntryFormat_t MEF_BigCheats = { 3<<16, 0, -(260<<16) };
+static MenuEntryFormat_t MEF_Cheats = { 2<<16, 0, -(260<<16) };
+static MenuEntryFormat_t MEF_PlayerNarrow = { 1<<16, 0, 90<<16 };
+static MenuEntryFormat_t MEF_Macros = { 2<<16, -1, 268<<16 };
+static MenuEntryFormat_t MEF_VideoSetup = { 4<<16, 0, 168<<16 };
+static MenuEntryFormat_t MEF_VideoSetup_Apply = { 4<<16, 16<<16, 168<<16 };
+static MenuEntryFormat_t MEF_FuncList = { 3<<16, 0, 100<<16 };
+static MenuEntryFormat_t MEF_ColorCorrect = { 2<<16, 0, -(240<<16) };
+static MenuEntryFormat_t MEF_BigSliders = { 2<<16, 0, 170<<16 };
+static MenuEntryFormat_t MEF_LoadSave = { 2<<16, -1, 78<<16 };
+static MenuEntryFormat_t MEF_NetSetup = { 4<<16, 0, 112<<16 };
+static MenuEntryFormat_t MEF_NetSetup_Confirm = { 4<<16, 16<<16, 112<<16 };
+
+// common menu option sets
+#define MAKE_MENUOPTIONSET(optionNames, optionValues, features) { optionNames, optionValues, &MMF_FuncList, &MEF_FuncList, &MF_Minifont, ARRAY_SIZE(optionNames), -1, 0, features }
+#define MAKE_MENUOPTIONSETDYN(optionNames, optionValues, numOptions, features) { optionNames, optionValues, &MMF_FuncList, &MEF_FuncList, &MF_Minifont, numOptions, -1, 0, features }
+#define MAKE_MENUOPTIONSETNULL { NULL, NULL, &MMF_FuncList, &MEF_FuncList, &MF_Minifont, 0, -1, 0, 0 }
+
+static char const *MEOSN_OffOn[] = { "Off", "On", };
+static MenuOptionSet_t MEOS_OffOn = MAKE_MENUOPTIONSET( MEOSN_OffOn, NULL, 0x3 );
+static char const *MEOSN_OnOff[] = { "On", "Off", };
+static MenuOptionSet_t MEOS_OnOff = MAKE_MENUOPTIONSET( MEOSN_OnOff, NULL, 0x3 );
+static char const *MEOSN_NoYes[] = { "No", "Yes", };
+static MenuOptionSet_t MEOS_NoYes = MAKE_MENUOPTIONSET( MEOSN_NoYes, NULL, 0x3 );
+static char const *MEOSN_YesNo[] = { "Yes", "No", };
+static MenuOptionSet_t MEOS_YesNo = MAKE_MENUOPTIONSET( MEOSN_YesNo, NULL, 0x3 );
+
+
+static char MenuGameFuncs[NUMGAMEFUNCTIONS][MAXGAMEFUNCLEN];
+static char const *MenuGameFuncNone = " -None-";
+static char const *MEOSN_Gamefuncs[NUMGAMEFUNCTIONS+1];
+static int32_t MEOSV_Gamefuncs[NUMGAMEFUNCTIONS+1];
+static MenuOptionSet_t MEOS_Gamefuncs = MAKE_MENUOPTIONSET( MEOSN_Gamefuncs, MEOSV_Gamefuncs, 0x1 );
+
+
+
+/*
+MenuEntry_t is passed in arrays of pointers so that the callback function
+that is called when an entry is modified or activated can test equality of the current
+entry pointer directly against the known ones, instead of relying on an ID number.
+
+That way, individual menu entries can be ifdef'd out painlessly.
+*/
+
+static MenuLink_t MEO_NULL = { MENU_NULL, MA_None, };
+static const char* MenuCustom = "Custom";
+
+#define MAKE_MENUSTRING(...) { NULL, __VA_ARGS__, }
+#define MAKE_MENUOPTION(...) { __VA_ARGS__, -1, }
+#define MAKE_MENURANGE(...) { __VA_ARGS__, }
+#define MAKE_MENUENTRY(...) { __VA_ARGS__, 0, 0, 0, }
+
+
+#define MAKE_SPACER( EntryName, Height ) \
+static MenuSpacer_t MEO_ ## EntryName = { Height };
+
+MAKE_SPACER( Space2, 2<<16 ); // bigoptions
+MAKE_SPACER( Space4, 4<<16 ); // usermap, smalloptions, anything else non-top
+MAKE_SPACER( Space6, 6<<16 ); // videosetup
+MAKE_SPACER( Space8, 8<<16 ); // colcorr, redslide
+
+static MenuEntry_t ME_Space2_Redfont = MAKE_MENUENTRY( NULL, &MF_Redfont, &MEF_Null, &MEO_Space2, Spacer );
+static MenuEntry_t ME_Space4_Bluefont = MAKE_MENUENTRY( NULL, &MF_Bluefont, &MEF_Null, &MEO_Space4, Spacer );
+#ifndef EDUKE32_SIMPLE_MENU
+static MenuEntry_t ME_Space4_Redfont = MAKE_MENUENTRY( NULL, &MF_Redfont, &MEF_Null, &MEO_Space4, Spacer );
+static MenuEntry_t ME_Space8_Bluefont = MAKE_MENUENTRY( NULL, &MF_Bluefont, &MEF_Null, &MEO_Space8, Spacer );
+#endif
+static MenuEntry_t ME_Space6_Redfont = MAKE_MENUENTRY( NULL, &MF_Redfont, &MEF_Null, &MEO_Space6, Spacer );
+static MenuEntry_t ME_Space8_Redfont = MAKE_MENUENTRY( NULL, &MF_Redfont, &MEF_Null, &MEO_Space8, Spacer );
+
+#define MAKE_MENU_TOP_ENTRYLINK( Title, Format, EntryName, LinkID ) \
+static MenuLink_t MEO_ ## EntryName = { LinkID, MA_Advance, };\
+static MenuEntry_t ME_ ## EntryName = MAKE_MENUENTRY( Title, &MF_Redfont, &Format, &MEO_ ## EntryName, Link )
+
+static char const s_NewGame[] = "New Game";
+static char const s_SaveGame[] = "Save Game";
+static char const s_LoadGame[] = "Load Game";
+static char const s_Continue[] = "Continue";
+static char const s_Options[] = "Options";
+static char const s_Credits[] = "Credits";
+
+MAKE_MENU_TOP_ENTRYLINK( s_NewGame, MEF_MainMenu, MAIN_NEWGAME, MENU_EPISODE );
+#ifdef EDUKE32_SIMPLE_MENU
+MAKE_MENU_TOP_ENTRYLINK( "Resume Game", MEF_MainMenu, MAIN_RESUMEGAME, MENU_CLOSE );
+#endif
+MAKE_MENU_TOP_ENTRYLINK( s_NewGame, MEF_MainMenu, MAIN_NEWGAME_INGAME, MENU_NEWVERIFY );
+static MenuLink_t MEO_MAIN_NEWGAME_NETWORK = { MENU_NETWORK, MA_Advance, };
+MAKE_MENU_TOP_ENTRYLINK( s_SaveGame, MEF_MainMenu, MAIN_SAVEGAME, MENU_SAVE );
+MAKE_MENU_TOP_ENTRYLINK( s_LoadGame, MEF_MainMenu, MAIN_LOADGAME, MENU_LOAD );
+MAKE_MENU_TOP_ENTRYLINK( s_Options, MEF_MainMenu, MAIN_OPTIONS, MENU_OPTIONS );
+MAKE_MENU_TOP_ENTRYLINK( "Help", MEF_MainMenu, MAIN_HELP, MENU_STORY );
+#ifndef EDUKE32_SIMPLE_MENU
+MAKE_MENU_TOP_ENTRYLINK( s_Credits, MEF_MainMenu, MAIN_CREDITS, MENU_CREDITS );
+#endif
+MAKE_MENU_TOP_ENTRYLINK( "End Game", MEF_MainMenu, MAIN_QUITTOTITLE, MENU_QUITTOTITLE );
+MAKE_MENU_TOP_ENTRYLINK( "Quit", MEF_MainMenu, MAIN_QUIT, MENU_QUIT );
+#ifndef EDUKE32_SIMPLE_MENU
+MAKE_MENU_TOP_ENTRYLINK( "Quit Game", MEF_MainMenu, MAIN_QUITGAME, MENU_QUIT );
+#endif
+
+static MenuEntry_t *MEL_MAIN[] = {
+ &ME_MAIN_NEWGAME,
+ &ME_MAIN_LOADGAME,
+ &ME_MAIN_OPTIONS,
+ &ME_MAIN_HELP,
+#ifndef EDUKE32_SIMPLE_MENU
+ &ME_MAIN_CREDITS,
+#endif
+ &ME_MAIN_QUIT,
+};
+
+static MenuEntry_t *MEL_MAIN_INGAME[] = {
+#ifdef EDUKE32_SIMPLE_MENU
+ &ME_MAIN_RESUMEGAME,
+#else
+ &ME_MAIN_NEWGAME_INGAME,
+#endif
+ &ME_MAIN_SAVEGAME,
+ &ME_MAIN_LOADGAME,
+ &ME_MAIN_OPTIONS,
+ &ME_MAIN_HELP,
+ &ME_MAIN_QUITTOTITLE,
+#ifndef EDUKE32_SIMPLE_MENU
+ &ME_MAIN_QUITGAME,
+#endif
+};
+
+// Episode and Skill will be dynamically generated after CONs are parsed
+static MenuLink_t MEO_EPISODE = { MENU_SKILL, MA_Advance, };
+static MenuLink_t MEO_EPISODE_SHAREWARE = { MENU_BUYDUKE, MA_Advance, };
+static MenuEntry_t ME_EPISODE_TEMPLATE = MAKE_MENUENTRY( NULL, &MF_Redfont, &MEF_CenterMenu, &MEO_EPISODE, Link );
+static MenuEntry_t ME_EPISODE[MAXVOLUMES];
+static MenuLink_t MEO_EPISODE_USERMAP = { MENU_USERMAP, MA_Advance, };
+static MenuEntry_t ME_EPISODE_USERMAP = MAKE_MENUENTRY( "User Map", &MF_Redfont, &MEF_CenterMenu, &MEO_EPISODE_USERMAP, Link );
+static MenuEntry_t *MEL_EPISODE[MAXVOLUMES+2]; // +2 for spacer and User Map
+
+static MenuEntry_t ME_SKILL_TEMPLATE = MAKE_MENUENTRY( NULL, &MF_Redfont, &MEF_CenterMenu, &MEO_NULL, Link );
+static MenuEntry_t ME_SKILL[MAXSKILLS];
+static MenuEntry_t *MEL_SKILL[MAXSKILLS];
+
+#if defined STARTUP_SETUP_WINDOW && !defined EDUKE32_SIMPLE_MENU
+static MenuOption_t MEO_GAMESETUP_STARTWIN = MAKE_MENUOPTION( &MF_Redfont, &MEOS_OffOn, &ud.config.ForceSetup );
+static MenuEntry_t ME_GAMESETUP_STARTWIN = MAKE_MENUENTRY( "Startup window:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_GAMESETUP_STARTWIN, Option );
+#endif
+
+static char const *MEOSN_GAMESETUP_AIM_AUTO[] = { "Never", "Always", "Hitscan only",
+#ifdef EDUKE32_ANDROID_MENU
+"Extra wide"
+#endif
+};
+static int32_t MEOSV_GAMESETUP_AIM_AUTO[] = { 0, 1, 2,
+#ifdef EDUKE32_ANDROID_MENU
+3,
+#endif
+};
+
+static MenuOptionSet_t MEOS_GAMESETUP_AIM_AUTO = MAKE_MENUOPTIONSET( MEOSN_GAMESETUP_AIM_AUTO, MEOSV_GAMESETUP_AIM_AUTO, 0x2 );
+static MenuOption_t MEO_GAMESETUP_AIM_AUTO = MAKE_MENUOPTION( &MF_Redfont, &MEOS_GAMESETUP_AIM_AUTO, &ud.config.AutoAim );
+static MenuEntry_t ME_GAMESETUP_AIM_AUTO = MAKE_MENUENTRY( "Auto aim:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_GAMESETUP_AIM_AUTO, Option );
+
+static char const *MEOSN_GAMESETUP_WEAPSWITCH_PICKUP[] = { "Never", "If new", "By rating", };
+static MenuOptionSet_t MEOS_GAMESETUP_WEAPSWITCH_PICKUP = MAKE_MENUOPTIONSET( MEOSN_GAMESETUP_WEAPSWITCH_PICKUP, NULL, 0x2 );
+static MenuOption_t MEO_GAMESETUP_WEAPSWITCH_PICKUP = MAKE_MENUOPTION( &MF_Redfont, &MEOS_GAMESETUP_WEAPSWITCH_PICKUP, NULL );
+static MenuEntry_t ME_GAMESETUP_WEAPSWITCH_PICKUP = MAKE_MENUENTRY( "Equip pickups:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_GAMESETUP_WEAPSWITCH_PICKUP, Option );
+
+static char const *MEOSN_DemoRec[] = { "Off", "Running", };
+static MenuOptionSet_t MEOS_DemoRec = MAKE_MENUOPTIONSET( MEOSN_DemoRec, NULL, 0x3 );
+static MenuOption_t MEO_GAMESETUP_DEMOREC = MAKE_MENUOPTION( &MF_Redfont, &MEOS_OffOn, &ud.m_recstat );
+static MenuEntry_t ME_GAMESETUP_DEMOREC = MAKE_MENUENTRY( "Record demo:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_GAMESETUP_DEMOREC, Option );
+
+#ifdef _WIN32
+static MenuOption_t MEO_GAMESETUP_UPDATES = MAKE_MENUOPTION( &MF_Redfont, &MEOS_NoYes, &ud.config.CheckForUpdates );
+static MenuEntry_t ME_GAMESETUP_UPDATES = MAKE_MENUENTRY( "Online updates:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_GAMESETUP_UPDATES, Option );
+#endif
+
+static MenuOption_t MEO_ADULTMODE = MAKE_MENUOPTION(&MF_Redfont, &MEOS_OffOn, &ud.lockout);
+static MenuEntry_t ME_ADULTMODE = MAKE_MENUENTRY( "Parental lock:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_ADULTMODE, Option );
+// static MenuLink_t MEO_ADULTMODE_PASSWORD = { MENU_ADULTPASSWORD, MA_None, };
+// static MenuEntry_t ME_ADULTMODE_PASSWORD = MAKE_MENUENTRY( "Enter Password", &MF_Redfont, &, &MEO_ADULTMODE_PASSWORD, Link );
+
+#ifdef EDUKE32_ANDROID_MENU
+static MenuOption_t MEO_GAMESETUP_CROUCHLOCK = MAKE_MENUOPTION(&MF_Redfont, &MEOS_OffOn, &droidinput.toggleCrouch);
+static MenuEntry_t ME_GAMESETUP_CROUCHLOCK = MAKE_MENUENTRY("Crouch lock:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_GAMESETUP_CROUCHLOCK, Option);
+
+static MenuOption_t MEO_GAMESETUP_QUICKSWITCH = MAKE_MENUOPTION(&MF_Redfont, &MEOS_OffOn, &droidinput.quickSelectWeapon);
+static MenuEntry_t ME_GAMESETUP_QUICKSWITCH = MAKE_MENUENTRY("Quick weapon switch:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_GAMESETUP_QUICKSWITCH, Option);
+#endif
+
+#if defined(EDUKE32_ANDROID_MENU) || !defined(EDUKE32_SIMPLE_MENU)
+static MenuLink_t MEO_GAMESETUP_CHEATS = { MENU_CHEATS, MA_Advance, };
+static MenuEntry_t ME_GAMESETUP_CHEATS = MAKE_MENUENTRY( "Cheats", &MF_Redfont, &MEF_BigOptionsRt, &MEO_GAMESETUP_CHEATS, Link );
+
+static MenuEntry_t *MEL_GAMESETUP[] = {
+ &ME_ADULTMODE,
+#if defined STARTUP_SETUP_WINDOW && !defined EDUKE32_SIMPLE_MENU
+ &ME_GAMESETUP_STARTWIN,
+#endif
+ &ME_GAMESETUP_AIM_AUTO,
+ &ME_GAMESETUP_WEAPSWITCH_PICKUP,
+#ifdef EDUKE32_ANDROID_MENU
+ &ME_GAMESETUP_QUICKSWITCH,
+ &ME_GAMESETUP_CROUCHLOCK,
+#else
+ &ME_GAMESETUP_DEMOREC,
+#ifdef _WIN32
+ &ME_GAMESETUP_UPDATES,
+#endif
+#endif
+ &ME_GAMESETUP_CHEATS,
+};
+#endif
+
+#ifndef EDUKE32_SIMPLE_MENU
+MAKE_MENU_TOP_ENTRYLINK( "Game Setup", MEF_OptionsMenu, OPTIONS_GAMESETUP, MENU_GAMESETUP );
+#endif
+MAKE_MENU_TOP_ENTRYLINK( "Sound Setup", MEF_OptionsMenu, OPTIONS_SOUNDSETUP, MENU_SOUND );
+MAKE_MENU_TOP_ENTRYLINK( "Display Setup", MEF_OptionsMenu, OPTIONS_DISPLAYSETUP, MENU_DISPLAYSETUP );
+MAKE_MENU_TOP_ENTRYLINK( "Player Setup", MEF_OptionsMenu, OPTIONS_PLAYERSETUP, MENU_PLAYER );
+#ifndef EDUKE32_ANDROID_MENU
+MAKE_MENU_TOP_ENTRYLINK( "Control Setup", MEF_OptionsMenu, OPTIONS_CONTROLS, MENU_CONTROLS );
+
+MAKE_MENU_TOP_ENTRYLINK( "Keyboard Setup", MEF_CenterMenu, OPTIONS_KEYBOARDSETUP, MENU_KEYBOARDSETUP );
+MAKE_MENU_TOP_ENTRYLINK( "Mouse Setup", MEF_CenterMenu, OPTIONS_MOUSESETUP, MENU_MOUSESETUP );
+#endif
+MAKE_MENU_TOP_ENTRYLINK( "Joystick Setup", MEF_CenterMenu, OPTIONS_JOYSTICKSETUP, MENU_JOYSTICKSETUP );
+#ifdef EDUKE32_ANDROID_MENU
+MAKE_MENU_TOP_ENTRYLINK( "Touch Setup", MEF_CenterMenu, OPTIONS_TOUCHSETUP, MENU_TOUCHSETUP );
+#endif
+#ifdef EDUKE32_SIMPLE_MENU
+MAKE_MENU_TOP_ENTRYLINK("Cheats", MEF_OptionsMenu, OPTIONS_CHEATS, MENU_CHEATS);
+#endif
+
+static int32_t newresolution, newrendermode, newfullscreen, newvsync;
+
+enum resflags_t {
+ RES_FS = 0x1,
+ RES_WIN = 0x2,
+};
+
+#define MAXRESOLUTIONSTRINGLENGTH 16
+
+typedef struct resolution_t {
+ int32_t xdim, ydim;
+ int32_t flags;
+ int32_t bppmax;
+ char name[MAXRESOLUTIONSTRINGLENGTH];
+} resolution_t;
+
+resolution_t resolution[MAXVALIDMODES];
+
+static char const *MEOSN_VIDEOSETUP_RESOLUTION[MAXVALIDMODES];
+static MenuOptionSet_t MEOS_VIDEOSETUP_RESOLUTION = MAKE_MENUOPTIONSETDYN( MEOSN_VIDEOSETUP_RESOLUTION, NULL, 0, 0x0 );
+static MenuOption_t MEO_VIDEOSETUP_RESOLUTION = MAKE_MENUOPTION( &MF_Redfont, &MEOS_VIDEOSETUP_RESOLUTION, &newresolution );
+static MenuEntry_t ME_VIDEOSETUP_RESOLUTION = MAKE_MENUENTRY( "Resolution:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_VIDEOSETUP_RESOLUTION, Option );
+
+#ifdef USE_OPENGL
+#ifdef POLYMER
+static char const *MEOSN_VIDEOSETUP_RENDERER[] = { "Classic", "Polymost", "Polymer", };
+static int32_t MEOSV_VIDEOSETUP_RENDERER[] = { REND_CLASSIC, REND_POLYMOST, REND_POLYMER, };
+#else
+static char const *MEOSN_VIDEOSETUP_RENDERER[] = { "Classic", "OpenGL", };
+static int32_t MEOSV_VIDEOSETUP_RENDERER[] = { REND_CLASSIC, REND_POLYMOST, };
+#endif
+
+static MenuOptionSet_t MEOS_VIDEOSETUP_RENDERER = MAKE_MENUOPTIONSET( MEOSN_VIDEOSETUP_RENDERER, MEOSV_VIDEOSETUP_RENDERER, 0x2 );
+
+static MenuOption_t MEO_VIDEOSETUP_RENDERER = MAKE_MENUOPTION( &MF_Redfont, &MEOS_VIDEOSETUP_RENDERER, &newrendermode );
+static MenuEntry_t ME_VIDEOSETUP_RENDERER = MAKE_MENUENTRY( "Renderer:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_VIDEOSETUP_RENDERER, Option );
+#endif
+
+static MenuOption_t MEO_VIDEOSETUP_FULLSCREEN = MAKE_MENUOPTION( &MF_Redfont, &MEOS_NoYes, &newfullscreen );
+static MenuEntry_t ME_VIDEOSETUP_FULLSCREEN = MAKE_MENUENTRY( "Fullscreen:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_VIDEOSETUP_FULLSCREEN, Option );
+
+
+static char const *MEOSN_VIDEOSETUP_VSYNC [] = { "Adaptive", "Off", "On", };
+static int32_t MEOSV_VIDEOSETUP_VSYNC [] = { -1, 0, 1, };
+static MenuOptionSet_t MEOS_VIDEOSETUP_VSYNC = MAKE_MENUOPTIONSET(MEOSN_VIDEOSETUP_VSYNC, MEOSV_VIDEOSETUP_VSYNC, 0x2);
+static MenuOption_t MEO_VIDEOSETUP_VSYNC = MAKE_MENUOPTION(&MF_Redfont, &MEOS_VIDEOSETUP_VSYNC, &newvsync);
+static MenuEntry_t ME_VIDEOSETUP_VSYNC = MAKE_MENUENTRY("VSync:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_VIDEOSETUP_VSYNC, Option);
+
+static char const *MEOSN_VIDEOSETUP_FRAMELIMIT [] = { "None", "30 fps", "60 fps", "120 fps", "144 fps", };
+static int32_t MEOSV_VIDEOSETUP_FRAMELIMIT [] = { 0, 30, 60, 120, 144 };
+static MenuOptionSet_t MEOS_VIDEOSETUP_FRAMELIMIT = MAKE_MENUOPTIONSET(MEOSN_VIDEOSETUP_FRAMELIMIT, MEOSV_VIDEOSETUP_FRAMELIMIT, 0x2);
+static MenuOption_t MEO_VIDEOSETUP_FRAMELIMIT= MAKE_MENUOPTION(&MF_Redfont, &MEOS_VIDEOSETUP_FRAMELIMIT, &r_maxfps);
+static MenuEntry_t ME_VIDEOSETUP_FRAMELIMIT = MAKE_MENUENTRY("Framerate limit:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_VIDEOSETUP_FRAMELIMIT, Option);
+
+
+static MenuEntry_t ME_VIDEOSETUP_APPLY = MAKE_MENUENTRY( "Apply Changes", &MF_Redfont, &MEF_BigOptions_Apply, &MEO_NULL, Link );
+
+
+static MenuLink_t MEO_DISPLAYSETUP_COLORCORR = { MENU_COLCORR, MA_Advance, };
+static MenuEntry_t ME_DISPLAYSETUP_COLORCORR = MAKE_MENUENTRY( "Color Correction", &MF_Redfont, &MEF_BigOptionsRt, &MEO_DISPLAYSETUP_COLORCORR, Link );
+
+
+static MenuOption_t MEO_DISPLAYSETUP_PIXELDOUBLING = MAKE_MENUOPTION( &MF_Redfont, &MEOS_OffOn, &ud.detail );
+static MenuEntry_t ME_DISPLAYSETUP_PIXELDOUBLING = MAKE_MENUENTRY( "Pixel Doubling:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_DISPLAYSETUP_PIXELDOUBLING, Option );
+
+
+#ifndef EDUKE32_ANDROID_MENU
+static MenuOption_t MEO_DISPLAYSETUP_ASPECTRATIO = MAKE_MENUOPTION(&MF_Redfont, &MEOS_OffOn, &r_usenewaspect);
+static MenuEntry_t ME_DISPLAYSETUP_ASPECTRATIO = MAKE_MENUENTRY( "Widescreen:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_DISPLAYSETUP_ASPECTRATIO, Option );
+#endif
+
+
+#ifdef USE_OPENGL
+#ifdef EDUKE32_SIMPLE_MENU
+static MenuOption_t MEO_DISPLAYSETUP_PALETTEEMULATION = MAKE_MENUOPTION(&MF_Redfont, &MEOS_OffOn, &r_usetileshades);
+static MenuEntry_t ME_DISPLAYSETUP_PALETTEEMULATION = MAKE_MENUENTRY("Palette emulation:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_DISPLAYSETUP_PALETTEEMULATION, Option);
+#endif
+
+static char const *MEOSN_DISPLAYSETUP_TEXFILTER[] = { "Classic", "Filtered" };
+static int32_t MEOSV_DISPLAYSETUP_TEXFILTER[] = { TEXFILTER_OFF, TEXFILTER_ON };
+static MenuOptionSet_t MEOS_DISPLAYSETUP_TEXFILTER = MAKE_MENUOPTIONSET( MEOSN_DISPLAYSETUP_TEXFILTER, MEOSV_DISPLAYSETUP_TEXFILTER, 0x2 );
+static MenuOption_t MEO_DISPLAYSETUP_TEXFILTER = MAKE_MENUOPTION( &MF_Redfont, &MEOS_DISPLAYSETUP_TEXFILTER, &gltexfiltermode );
+static MenuEntry_t ME_DISPLAYSETUP_TEXFILTER = MAKE_MENUENTRY( "Texture Mode:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_DISPLAYSETUP_TEXFILTER, Option );
+
+static char const *MEOSN_DISPLAYSETUP_ANISOTROPY[] = { "Max", "None", "2x", "4x", "8x", "16x", };
+static int32_t MEOSV_DISPLAYSETUP_ANISOTROPY[] = { 0, 1, 2, 4, 8, 16, };
+static MenuOptionSet_t MEOS_DISPLAYSETUP_ANISOTROPY = MAKE_MENUOPTIONSET( MEOSN_DISPLAYSETUP_ANISOTROPY, MEOSV_DISPLAYSETUP_ANISOTROPY, 0x0 );
+static MenuOption_t MEO_DISPLAYSETUP_ANISOTROPY = MAKE_MENUOPTION(&MF_Redfont, &MEOS_DISPLAYSETUP_ANISOTROPY, &glanisotropy);
+static MenuEntry_t ME_DISPLAYSETUP_ANISOTROPY = MAKE_MENUENTRY( "Anisotropy:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_DISPLAYSETUP_ANISOTROPY, Option );
+
+#ifdef EDUKE32_ANDROID_MENU
+static MenuOption_t MEO_DISPLAYSETUP_HIDEDPAD = MAKE_MENUOPTION(&MF_Redfont, &MEOS_NoYes, &droidinput.hideStick);
+static MenuEntry_t ME_DISPLAYSETUP_HIDEDPAD = MAKE_MENUENTRY("Hide touch D-pad:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_DISPLAYSETUP_HIDEDPAD, Option);
+
+static MenuRangeFloat_t MEO_DISPLAYSETUP_TOUCHALPHA = MAKE_MENURANGE(&droidinput.gameControlsAlpha, &MF_Redfont, 0, 1, 0, 16, 2);
+static MenuEntry_t ME_DISPLAYSETUP_TOUCHALPHA = MAKE_MENUENTRY("UI opacity:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_DISPLAYSETUP_TOUCHALPHA, RangeFloat);
+#endif
+
+#endif
+
+static char const s_Scale[] = "Scale:";
+
+static MenuOption_t MEO_SCREENSETUP_CROSSHAIR = MAKE_MENUOPTION(&MF_Redfont, &MEOS_OffOn, &ud.crosshair);
+static MenuEntry_t ME_SCREENSETUP_CROSSHAIR = MAKE_MENUENTRY( "Crosshair:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_SCREENSETUP_CROSSHAIR, Option );
+static MenuRangeInt32_t MEO_SCREENSETUP_CROSSHAIRSIZE = MAKE_MENURANGE( &ud.crosshairscale, &MF_Redfont, 25, 100, 0, 16, 2 );
+static MenuEntry_t ME_SCREENSETUP_CROSSHAIRSIZE = MAKE_MENUENTRY( s_Scale, &MF_Redfont, &MEF_BigOptions_Apply, &MEO_SCREENSETUP_CROSSHAIRSIZE, RangeInt32 );
+
+static int32_t vpsize;
+static MenuRangeInt32_t MEO_SCREENSETUP_SCREENSIZE = MAKE_MENURANGE( &vpsize, &MF_Redfont, 16, 0, 0, 5, EnforceIntervals );
+static MenuEntry_t ME_SCREENSETUP_SCREENSIZE = MAKE_MENUENTRY( "Screen size:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_SCREENSETUP_SCREENSIZE, RangeInt32 );
+static MenuRangeInt32_t MEO_SCREENSETUP_TEXTSIZE = MAKE_MENURANGE( &ud.textscale, &MF_Redfont, 100, 400, 0, 16, 2 );
+static MenuEntry_t ME_SCREENSETUP_TEXTSIZE = MAKE_MENUENTRY( s_Scale, &MF_Redfont, &MEF_BigOptions_Apply, &MEO_SCREENSETUP_TEXTSIZE, RangeInt32 );
+static MenuOption_t MEO_SCREENSETUP_LEVELSTATS = MAKE_MENUOPTION(&MF_Redfont, &MEOS_OffOn, &ud.levelstats);
+static MenuEntry_t ME_SCREENSETUP_LEVELSTATS = MAKE_MENUENTRY( "Level stats:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_SCREENSETUP_LEVELSTATS, Option );
+
+
+static MenuOption_t MEO_SCREENSETUP_SHOWPICKUPMESSAGES = MAKE_MENUOPTION(&MF_Redfont, &MEOS_OffOn, &ud.fta_on);
+static MenuEntry_t ME_SCREENSETUP_SHOWPICKUPMESSAGES = MAKE_MENUENTRY( "Game messages:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_SCREENSETUP_SHOWPICKUPMESSAGES, Option );
+
+
+
+static MenuRangeInt32_t MEO_SCREENSETUP_SBARSIZE = MAKE_MENURANGE( &ud.statusbarscale, &MF_Redfont, 36, 100, 0, 17, 2 );
+static MenuEntry_t ME_SCREENSETUP_SBARSIZE = MAKE_MENUENTRY( s_Scale, &MF_Redfont, &MEF_BigOptions_Apply, &MEO_SCREENSETUP_SBARSIZE, RangeInt32 );
+
+
+static MenuLink_t MEO_DISPLAYSETUP_SCREENSETUP = { MENU_SCREENSETUP, MA_Advance, };
+static MenuEntry_t ME_DISPLAYSETUP_SCREENSETUP = MAKE_MENUENTRY( "HUD setup", &MF_Redfont, &MEF_BigOptionsRt, &MEO_DISPLAYSETUP_SCREENSETUP, Link );
+
+
+#ifndef EDUKE32_SIMPLE_MENU
+#ifdef USE_OPENGL
+static MenuLink_t MEO_DISPLAYSETUP_ADVANCED_GL_POLYMOST = { MENU_POLYMOST, MA_Advance, };
+static MenuEntry_t ME_DISPLAYSETUP_ADVANCED_GL_POLYMOST = MAKE_MENUENTRY( "Polymost setup", &MF_Redfont, &MEF_BigOptionsRt, &MEO_DISPLAYSETUP_ADVANCED_GL_POLYMOST, Link );
+
+#ifdef POLYMER
+static MenuLink_t MEO_DISPLAYSETUP_ADVANCED_GL_POLYMER = { MENU_POLYMER, MA_Advance, };
+static MenuEntry_t ME_DISPLAYSETUP_ADVANCED_GL_POLYMER = MAKE_MENUENTRY("Polymer setup", &MF_Redfont, &MEF_BigOptionsRt, &MEO_DISPLAYSETUP_ADVANCED_GL_POLYMER, Link);
+#endif
+#endif
+#endif
+
+#ifndef EDUKE32_ANDROID_MENU
+static MenuLink_t MEO_DISPLAYSETUP_VIDEOSETUP = { MENU_VIDEOSETUP, MA_Advance, };
+static MenuEntry_t ME_DISPLAYSETUP_VIDEOSETUP = MAKE_MENUENTRY( "Video mode", &MF_Redfont, &MEF_BigOptionsRt, &MEO_DISPLAYSETUP_VIDEOSETUP, Link );
+#endif
+
+
+static MenuLink_t MEO_ENTERCHEAT = { MENU_CHEATENTRY, MA_None, };
+static MenuEntry_t ME_ENTERCHEAT = MAKE_MENUENTRY( "Enter Cheat Code", &MF_Redfont, &MEF_BigCheats, &MEO_ENTERCHEAT, Link );
+
+static MenuLink_t MEO_CHEAT_WARP = { MENU_CHEAT_WARP, MA_None, };
+static MenuLink_t MEO_CHEAT_SKILL = { MENU_CHEAT_SKILL, MA_None, };
+// KEEPINSYNC game.h: enum CheatCodeFunctions
+// KEEPINSYNC game.c: uint8_t CheatFunctionIDs[]
+#define MAKE_MENUCHEAT( Name ) MAKE_MENUENTRY( Name, &MF_Bluefont, &MEF_Cheats, &MEO_NULL, Link )
+static MenuEntry_t ME_CheatCodes[] = {
+ MAKE_MENUCHEAT( "Toggle Cashman" ),
+ MAKE_MENUCHEAT( "Toggle God Mode" ),
+ MAKE_MENUCHEAT( "Give Everything" ),
+ MAKE_MENUCHEAT( "Give Weapons" ),
+ MAKE_MENUCHEAT( "Give All Items" ),
+ MAKE_MENUCHEAT( "Give Inventory" ),
+ MAKE_MENUCHEAT( "Give Keys" ),
+ MAKE_MENUCHEAT( "Toggle Hyper" ),
+ MAKE_MENUCHEAT( "Toggle 3rd-Person View" ),
+ MAKE_MENUCHEAT( "Toggle Show All Map" ),
+ MAKE_MENUCHEAT( "Toggle All Locks" ),
+ MAKE_MENUCHEAT( "Toggle Clipping" ),
+ MAKE_MENUENTRY( "Level Warp", &MF_Bluefont, &MEF_Cheats, &MEO_CHEAT_WARP, Link ),
+ MAKE_MENUENTRY( "Change Skill", &MF_Bluefont, &MEF_Cheats, &MEO_CHEAT_SKILL, Link ),
+ MAKE_MENUCHEAT( "Toggle Monsters" ),
+ MAKE_MENUCHEAT( "Toggle Framerate Display" ),
+ MAKE_MENUCHEAT( NULL ),
+ MAKE_MENUCHEAT( NULL ),
+ MAKE_MENUCHEAT( NULL ),
+ MAKE_MENUCHEAT( "Toggle Coordinate Display" ),
+ MAKE_MENUCHEAT( "Toggle Debug Data Dump" ),
+};
+
+static MenuEntry_t *MEL_OPTIONS[] = {
+#ifndef EDUKE32_SIMPLE_MENU
+ &ME_OPTIONS_GAMESETUP,
+#endif
+ &ME_OPTIONS_DISPLAYSETUP,
+ &ME_OPTIONS_SOUNDSETUP,
+#ifndef EDUKE32_ANDROID_MENU
+#ifndef EDUKE32_SIMPLE_MENU
+ &ME_OPTIONS_PLAYERSETUP,
+#endif
+ &ME_OPTIONS_CONTROLS,
+#else
+ &ME_OPTIONS_TOUCHSETUP,
+#endif
+#ifdef EDUKE32_SIMPLE_MENU
+ &ME_OPTIONS_CHEATS
+#endif
+};
+
+static MenuEntry_t *MEL_CONTROLS[] = {
+#ifndef EDUKE32_ANDROID_MENU
+ &ME_OPTIONS_KEYBOARDSETUP,
+ &ME_OPTIONS_MOUSESETUP,
+ &ME_OPTIONS_JOYSTICKSETUP,
+#else
+ &ME_OPTIONS_TOUCHSETUP
+#endif
+};
+
+static MenuEntry_t *MEL_CHEATS[ARRAY_SIZE(ME_CheatCodes)+1] = {
+ &ME_ENTERCHEAT,
+};
+
+static MenuEntry_t *MEL_VIDEOSETUP[] = {
+ &ME_VIDEOSETUP_RESOLUTION,
+#ifdef USE_OPENGL
+ &ME_VIDEOSETUP_RENDERER,
+#endif
+ &ME_VIDEOSETUP_FULLSCREEN,
+ &ME_VIDEOSETUP_VSYNC,
+ &ME_VIDEOSETUP_FRAMELIMIT,
+ &ME_Space6_Redfont,
+ &ME_VIDEOSETUP_APPLY,
+};
+static MenuEntry_t *MEL_DISPLAYSETUP[] = {
+ &ME_DISPLAYSETUP_SCREENSETUP,
+ &ME_DISPLAYSETUP_COLORCORR,
+#ifndef EDUKE32_ANDROID_MENU
+ &ME_DISPLAYSETUP_VIDEOSETUP,
+ &ME_DISPLAYSETUP_ASPECTRATIO,
+#endif
+ &ME_DISPLAYSETUP_PIXELDOUBLING,
+};
+
+#ifdef USE_OPENGL
+static MenuEntry_t *MEL_DISPLAYSETUP_GL[] = {
+ &ME_DISPLAYSETUP_SCREENSETUP,
+ &ME_DISPLAYSETUP_COLORCORR,
+#ifndef EDUKE32_ANDROID_MENU
+ &ME_DISPLAYSETUP_VIDEOSETUP,
+ &ME_DISPLAYSETUP_ASPECTRATIO,
+#endif
+ &ME_DISPLAYSETUP_TEXFILTER,
+#ifdef EDUKE32_ANDROID_MENU
+ &ME_DISPLAYSETUP_HIDEDPAD,
+ &ME_DISPLAYSETUP_TOUCHALPHA,
+#else
+ &ME_DISPLAYSETUP_ANISOTROPY,
+#ifdef EDUKE32_SIMPLE_MENU
+ &ME_DISPLAYSETUP_PALETTEEMULATION,
+#else
+ &ME_DISPLAYSETUP_ADVANCED_GL_POLYMOST,
+#endif
+#endif
+};
+
+#ifdef POLYMER
+static MenuEntry_t *MEL_DISPLAYSETUP_GL_POLYMER[] = {
+ &ME_DISPLAYSETUP_SCREENSETUP,
+ &ME_DISPLAYSETUP_COLORCORR,
+#ifndef EDUKE32_ANDROID_MENU
+ &ME_DISPLAYSETUP_VIDEOSETUP,
+#endif
+ &ME_DISPLAYSETUP_TEXFILTER,
+ &ME_DISPLAYSETUP_ANISOTROPY,
+#ifndef EDUKE32_SIMPLE_MENU
+ &ME_DISPLAYSETUP_ADVANCED_GL_POLYMER,
+#endif
+};
+
+#endif
+#endif
+
+
+
+static char const *MenuKeyNone = " -";
+static char const *MEOSN_Keys[NUMKEYS];
+
+static MenuCustom2Col_t MEO_KEYBOARDSETUPFUNCS_TEMPLATE = { { NULL, NULL, }, MEOSN_Keys, &MF_MinifontRed, NUMKEYS, 54<<16, 0 };
+static MenuCustom2Col_t MEO_KEYBOARDSETUPFUNCS[NUMGAMEFUNCTIONS];
+static MenuEntry_t ME_KEYBOARDSETUPFUNCS_TEMPLATE = MAKE_MENUENTRY( NULL, &MF_Minifont, &MEF_FuncList, &MEO_KEYBOARDSETUPFUNCS_TEMPLATE, Custom2Col );
+static MenuEntry_t ME_KEYBOARDSETUPFUNCS[NUMGAMEFUNCTIONS];
+static MenuEntry_t *MEL_KEYBOARDSETUPFUNCS[NUMGAMEFUNCTIONS];
+
+static MenuLink_t MEO_KEYBOARDSETUP_KEYS = { MENU_KEYBOARDKEYS, MA_Advance, };
+static MenuEntry_t ME_KEYBOARDSETUP_KEYS = MAKE_MENUENTRY( "Configure Keys", &MF_Redfont, &MEF_CenterMenu, &MEO_KEYBOARDSETUP_KEYS, Link );
+static MenuEntry_t ME_KEYBOARDSETUP_RESET = MAKE_MENUENTRY( "Reset To Defaults", &MF_Redfont, &MEF_CenterMenu, &MEO_NULL, Link );
+static MenuEntry_t ME_KEYBOARDSETUP_RESETCLASSIC = MAKE_MENUENTRY( "Reset To Classic", &MF_Redfont, &MEF_CenterMenu, &MEO_NULL, Link );
+
+static MenuEntry_t *MEL_KEYBOARDSETUP[] = {
+ &ME_KEYBOARDSETUP_KEYS,
+ &ME_KEYBOARDSETUP_RESET,
+ &ME_KEYBOARDSETUP_RESETCLASSIC,
+};
+
+
+// There is no better way to do this than manually.
+
+#define MENUMOUSEFUNCTIONS 12
+
+static char const *MenuMouseNames[MENUMOUSEFUNCTIONS] = {
+ "Button 1",
+ "Double Button 1",
+ "Button 2",
+ "Double Button 2",
+ "Button 3",
+ "Double Button 3",
+
+ "Wheel Up",
+ "Wheel Down",
+
+ "Button 4",
+ "Double Button 4",
+ "Button 5",
+ "Double Button 5",
+};
+static int32_t MenuMouseDataIndex[MENUMOUSEFUNCTIONS][2] = {
+ { 0, 0, },
+ { 0, 1, },
+ { 1, 0, },
+ { 1, 1, },
+ { 2, 0, },
+ { 2, 1, },
+
+ // note the mouse wheel
+ { 4, 0, },
+ { 5, 0, },
+
+ { 3, 0, },
+ { 3, 1, },
+ { 6, 0, },
+ { 6, 1, },
+};
+
+static MenuOption_t MEO_MOUSEJOYSETUPBTNS_TEMPLATE = MAKE_MENUOPTION( &MF_Minifont, &MEOS_Gamefuncs, NULL );
+static MenuOption_t MEO_MOUSESETUPBTNS[MENUMOUSEFUNCTIONS];
+static MenuEntry_t ME_MOUSEJOYSETUPBTNS_TEMPLATE = MAKE_MENUENTRY( NULL, &MF_Minifont, &MEF_FuncList, NULL, Option );
+static MenuEntry_t ME_MOUSESETUPBTNS[MENUMOUSEFUNCTIONS];
+static MenuEntry_t *MEL_MOUSESETUPBTNS[MENUMOUSEFUNCTIONS];
+
+static MenuLink_t MEO_MOUSESETUP_BTNS = { MENU_MOUSEBTNS, MA_Advance, };
+static MenuEntry_t ME_MOUSESETUP_BTNS = MAKE_MENUENTRY( "Button assignment", &MF_Redfont, &MEF_BigOptionsRt, &MEO_MOUSESETUP_BTNS, Link );
+static MenuRangeFloat_t MEO_MOUSESETUP_SENSITIVITY = MAKE_MENURANGE( &CONTROL_MouseSensitivity, &MF_Redfont, .5f, 16.f, 0.f, 32, 1 );
+static MenuEntry_t ME_MOUSESETUP_SENSITIVITY = MAKE_MENUENTRY( "Sensitivity:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_MOUSESETUP_SENSITIVITY, RangeFloat );
+
+#ifndef EDUKE32_SIMPLE_MENU
+static char const *MEOSN_MOUSESETUP_AIM_TYPE [] = { "Toggle", "Hold" };
+static MenuOptionSet_t MEOS_MOUSESETUP_AIM_TYPE = MAKE_MENUOPTIONSET(MEOSN_MOUSESETUP_AIM_TYPE, NULL, 0x2);
+static MenuOption_t MEO_MOUSESETUP_MOUSEAIMINGTYPE = MAKE_MENUOPTION(&MF_Redfont, &MEOS_MOUSESETUP_AIM_TYPE, &ud.mouseaiming);
+static MenuEntry_t ME_MOUSESETUP_MOUSEAIMINGTYPE = MAKE_MENUENTRY("Aiming type:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_MOUSESETUP_MOUSEAIMINGTYPE, Option);
+static MenuOption_t MEO_MOUSESETUP_MOUSEAIMING = MAKE_MENUOPTION( &MF_Redfont, &MEOS_NoYes, &g_myAimMode );
+static MenuEntry_t ME_MOUSESETUP_MOUSEAIMING = MAKE_MENUENTRY( "Vertical aiming:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_MOUSESETUP_MOUSEAIMING, Option );
+#endif
+static MenuOption_t MEO_MOUSESETUP_INVERT = MAKE_MENUOPTION( &MF_Redfont, &MEOS_YesNo, &ud.mouseflip );
+static MenuEntry_t ME_MOUSESETUP_INVERT = MAKE_MENUENTRY( "Invert aiming:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_MOUSESETUP_INVERT, Option );
+static MenuOption_t MEO_MOUSESETUP_SMOOTH = MAKE_MENUOPTION( &MF_Redfont, &MEOS_NoYes, &ud.config.SmoothInput );
+static MenuEntry_t ME_MOUSESETUP_SMOOTH = MAKE_MENUENTRY( "Filter input:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_MOUSESETUP_SMOOTH, Option );
+#ifndef EDUKE32_SIMPLE_MENU
+static MenuLink_t MEO_MOUSESETUP_ADVANCED = { MENU_MOUSEADVANCED, MA_Advance, };
+static MenuEntry_t ME_MOUSESETUP_ADVANCED = MAKE_MENUENTRY( "Advanced setup", &MF_Redfont, &MEF_BigOptionsRt, &MEO_MOUSESETUP_ADVANCED, Link );
+#endif
+static MenuRangeInt32_t MEO_MOUSEADVANCED_SCALEX = MAKE_MENURANGE(&ud.config.MouseAnalogueScale[0], &MF_Redfont, 512, 65536, 65536, 63, 3);
+static MenuEntry_t ME_MOUSEADVANCED_SCALEX = MAKE_MENUENTRY("X-Axis Scale:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_MOUSEADVANCED_SCALEX, RangeInt32);
+static MenuRangeInt32_t MEO_MOUSEADVANCED_SCALEY = MAKE_MENURANGE(&ud.config.MouseAnalogueScale[1], &MF_Redfont, 512, 65536, 65536, 63, 3);
+static MenuEntry_t ME_MOUSEADVANCED_SCALEY = MAKE_MENUENTRY("Y-Axis Scale:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_MOUSEADVANCED_SCALEY, RangeInt32);
+
+static MenuEntry_t *MEL_MOUSESETUP[] = {
+ &ME_MOUSESETUP_BTNS,
+ &ME_MOUSESETUP_SENSITIVITY,
+#ifdef EDUKE32_SIMPLE_MENU
+ &ME_MOUSEADVANCED_SCALEX,
+ &ME_MOUSEADVANCED_SCALEY,
+#endif
+ &ME_Space2_Redfont,
+#ifdef EDUKE32_SIMPLE_MENU
+ &ME_GAMESETUP_AIM_AUTO,
+#endif
+ &ME_MOUSESETUP_INVERT,
+ &ME_MOUSESETUP_SMOOTH,
+#ifndef EDUKE32_SIMPLE_MENU
+ &ME_MOUSESETUP_MOUSEAIMINGTYPE,
+ &ME_MOUSESETUP_MOUSEAIMING,
+ &ME_MOUSESETUP_ADVANCED,
+#endif
+};
+
+#ifdef EDUKE32_ANDROID_MENU
+static MenuRangeFloat_t MEO_TOUCHSETUP_SENSITIVITY_MOVE = MAKE_MENURANGE(&droidinput.forward_sens, &MF_Redfont, 1.f, 9.f, 0.f, 17, 1 + EnforceIntervals);
+static MenuEntry_t ME_TOUCHSETUP_SENSITIVITY_MOVE = MAKE_MENUENTRY("Running:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_TOUCHSETUP_SENSITIVITY_MOVE, RangeFloat);
+
+static MenuRangeFloat_t MEO_TOUCHSETUP_SENSITIVITY_STRAFE = MAKE_MENURANGE(&droidinput.strafe_sens, &MF_Redfont, 1.f, 9.f, 0.f, 17, 1 + EnforceIntervals);
+static MenuEntry_t ME_TOUCHSETUP_SENSITIVITY_STRAFE = MAKE_MENUENTRY("Strafing:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_TOUCHSETUP_SENSITIVITY_STRAFE, RangeFloat);
+
+static MenuRangeFloat_t MEO_TOUCHSETUP_SENSITIVITY_LOOK = MAKE_MENURANGE(&droidinput.pitch_sens, &MF_Redfont, 1.f, 9.f, 0.f, 17, 1 + EnforceIntervals);
+static MenuEntry_t ME_TOUCHSETUP_SENSITIVITY_LOOK = MAKE_MENUENTRY("Looking:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_TOUCHSETUP_SENSITIVITY_LOOK, RangeFloat);
+
+static MenuRangeFloat_t MEO_TOUCHSETUP_SENSITIVITY_TURN = MAKE_MENURANGE(&droidinput.yaw_sens, &MF_Redfont, 1.f, 9.f, 0.f, 17, 1 + EnforceIntervals);
+static MenuEntry_t ME_TOUCHSETUP_SENSITIVITY_TURN = MAKE_MENUENTRY("Turning:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_TOUCHSETUP_SENSITIVITY_TURN, RangeFloat);
+
+static MenuOption_t MEO_TOUCHSETUP_INVERT = MAKE_MENUOPTION(&MF_Redfont, &MEOS_NoYes, &droidinput.invertLook);
+static MenuEntry_t ME_TOUCHSETUP_INVERT = MAKE_MENUENTRY("Invert look:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_TOUCHSETUP_INVERT, Option);
+
+MAKE_MENU_TOP_ENTRYLINK("Sensitivity", MEF_CenterMenu, TOUCHSENS, MENU_TOUCHSENS);
+MAKE_MENU_TOP_ENTRYLINK("Button Setup", MEF_CenterMenu, TOUCHBUTTONS, MENU_TOUCHBUTTONS);
+
+static MenuEntry_t *MEL_TOUCHSETUP [] = {
+ &ME_TOUCHSENS,
+ &ME_TOUCHBUTTONS,
+};
+
+static MenuEntry_t *MEL_TOUCHSENS [] = {
+ &ME_TOUCHSETUP_SENSITIVITY_MOVE,
+ &ME_TOUCHSETUP_SENSITIVITY_STRAFE,
+ &ME_TOUCHSETUP_SENSITIVITY_LOOK,
+ &ME_TOUCHSETUP_SENSITIVITY_TURN,
+ &ME_Space2_Redfont,
+ &ME_TOUCHSETUP_INVERT,
+};
+#endif
+
+MAKE_MENU_TOP_ENTRYLINK( "Edit Buttons", MEF_CenterMenu, JOYSTICK_EDITBUTTONS, MENU_JOYSTICKBTNS );
+MAKE_MENU_TOP_ENTRYLINK( "Edit Axes", MEF_CenterMenu, JOYSTICK_EDITAXES, MENU_JOYSTICKAXES );
+
+static MenuEntry_t *MEL_JOYSTICKSETUP[] = {
+ &ME_JOYSTICK_EDITBUTTONS,
+ &ME_JOYSTICK_EDITAXES,
+};
+
+#define MAXJOYBUTTONSTRINGLENGTH 32
+
+static char MenuJoystickNames[MAXJOYBUTTONSANDHATS*2][MAXJOYBUTTONSTRINGLENGTH];
+
+static MenuOption_t MEO_JOYSTICKBTNS[MAXJOYBUTTONSANDHATS*2];
+static MenuEntry_t ME_JOYSTICKBTNS[MAXJOYBUTTONSANDHATS*2];
+static MenuEntry_t *MEL_JOYSTICKBTNS[MAXJOYBUTTONSANDHATS*2];
+
+static MenuLink_t MEO_JOYSTICKAXES = { MENU_JOYSTICKAXIS, MA_Advance, };
+static MenuEntry_t ME_JOYSTICKAXES_TEMPLATE = MAKE_MENUENTRY( NULL, &MF_Redfont, &MEF_BigSliders, &MEO_JOYSTICKAXES, Link );
+static MenuEntry_t ME_JOYSTICKAXES[MAXJOYAXES];
+static char MenuJoystickAxes[MAXJOYAXES][MAXJOYBUTTONSTRINGLENGTH];
+
+static MenuEntry_t *MEL_JOYSTICKAXES[MAXJOYAXES];
+
+static MenuOption_t MEO_MOUSEADVANCED_DAXES_UP = MAKE_MENUOPTION( &MF_BluefontRed, &MEOS_Gamefuncs, &ud.config.MouseDigitalFunctions[1][0] );
+static MenuEntry_t ME_MOUSEADVANCED_DAXES_UP = MAKE_MENUENTRY( "Digital Up", &MF_Redfont, &MEF_BigSliders, &MEO_MOUSEADVANCED_DAXES_UP, Option );
+static MenuOption_t MEO_MOUSEADVANCED_DAXES_DOWN = MAKE_MENUOPTION( &MF_BluefontRed, &MEOS_Gamefuncs, &ud.config.MouseDigitalFunctions[1][1] );
+static MenuEntry_t ME_MOUSEADVANCED_DAXES_DOWN = MAKE_MENUENTRY( "Digital Down", &MF_Redfont, &MEF_BigSliders, &MEO_MOUSEADVANCED_DAXES_DOWN, Option );
+static MenuOption_t MEO_MOUSEADVANCED_DAXES_LEFT = MAKE_MENUOPTION( &MF_BluefontRed, &MEOS_Gamefuncs, &ud.config.MouseDigitalFunctions[0][0] );
+static MenuEntry_t ME_MOUSEADVANCED_DAXES_LEFT = MAKE_MENUENTRY( "Digital Left", &MF_Redfont, &MEF_BigSliders, &MEO_MOUSEADVANCED_DAXES_LEFT, Option );
+static MenuOption_t MEO_MOUSEADVANCED_DAXES_RIGHT = MAKE_MENUOPTION( &MF_BluefontRed, &MEOS_Gamefuncs, &ud.config.MouseDigitalFunctions[0][1] );
+static MenuEntry_t ME_MOUSEADVANCED_DAXES_RIGHT = MAKE_MENUENTRY( "Digital Right", &MF_Redfont, &MEF_BigSliders, &MEO_MOUSEADVANCED_DAXES_RIGHT, Option );
+
+static MenuEntry_t *MEL_MOUSEADVANCED[] = {
+ &ME_MOUSEADVANCED_SCALEX,
+ &ME_MOUSEADVANCED_SCALEY,
+ &ME_Space8_Redfont,
+ &ME_MOUSEADVANCED_DAXES_UP,
+ &ME_MOUSEADVANCED_DAXES_DOWN,
+ &ME_MOUSEADVANCED_DAXES_LEFT,
+ &ME_MOUSEADVANCED_DAXES_RIGHT,
+};
+
+static MenuEntry_t *MEL_INTERNAL_MOUSEADVANCED_DAXES[] = {
+ &ME_MOUSEADVANCED_DAXES_UP,
+ &ME_MOUSEADVANCED_DAXES_DOWN,
+ &ME_MOUSEADVANCED_DAXES_LEFT,
+ &ME_MOUSEADVANCED_DAXES_RIGHT,
+};
+
+static const char *MenuJoystickHatDirections[] = { "Up", "Right", "Down", "Left", };
+
+static char const *MEOSN_JOYSTICKAXIS_ANALOG[] = { " -None-", "Turning", "Strafing", "Looking", "Moving", };
+static int32_t MEOSV_JOYSTICKAXIS_ANALOG[] = { -1, analog_turning, analog_strafing, analog_lookingupanddown, analog_moving, };
+static MenuOptionSet_t MEOS_JOYSTICKAXIS_ANALOG = MAKE_MENUOPTIONSET( MEOSN_JOYSTICKAXIS_ANALOG, MEOSV_JOYSTICKAXIS_ANALOG, 0x0 );
+static MenuOption_t MEO_JOYSTICKAXIS_ANALOG = MAKE_MENUOPTION( &MF_Bluefont, &MEOS_JOYSTICKAXIS_ANALOG, NULL );
+static MenuEntry_t ME_JOYSTICKAXIS_ANALOG = MAKE_MENUENTRY( "Analog", &MF_Redfont, &MEF_BigSliders, &MEO_JOYSTICKAXIS_ANALOG, Option );
+static MenuRangeInt32_t MEO_JOYSTICKAXIS_SCALE = MAKE_MENURANGE( NULL, &MF_Bluefont, -262144, 262144, 65536, 65, 3 );
+static MenuEntry_t ME_JOYSTICKAXIS_SCALE = MAKE_MENUENTRY( "Scale", &MF_Redfont, &MEF_BigSliders, &MEO_JOYSTICKAXIS_SCALE, RangeInt32 );
+static MenuRangeInt32_t MEO_JOYSTICKAXIS_DEAD = MAKE_MENURANGE( NULL, &MF_Bluefont, 0, 1000000, 0, 33, 2 );
+static MenuEntry_t ME_JOYSTICKAXIS_DEAD = MAKE_MENUENTRY( "Dead Zone", &MF_Redfont, &MEF_BigSliders, &MEO_JOYSTICKAXIS_DEAD, RangeInt32 );
+static MenuRangeInt32_t MEO_JOYSTICKAXIS_SATU = MAKE_MENURANGE( NULL, &MF_Bluefont, 0, 1000000, 0, 33, 2 );
+static MenuEntry_t ME_JOYSTICKAXIS_SATU = MAKE_MENUENTRY( "Saturation", &MF_Redfont, &MEF_BigSliders, &MEO_JOYSTICKAXIS_SATU, RangeInt32 );
+
+static MenuOption_t MEO_JOYSTICKAXIS_DIGITALNEGATIVE = MAKE_MENUOPTION( &MF_Minifont, &MEOS_Gamefuncs, NULL );
+static MenuEntry_t ME_JOYSTICKAXIS_DIGITALNEGATIVE = MAKE_MENUENTRY( "Digital -", &MF_BluefontRed, &MEF_BigSliders, &MEO_JOYSTICKAXIS_DIGITALNEGATIVE, Option );
+static MenuOption_t MEO_JOYSTICKAXIS_DIGITALPOSITIVE = MAKE_MENUOPTION( &MF_Minifont, &MEOS_Gamefuncs, NULL );
+static MenuEntry_t ME_JOYSTICKAXIS_DIGITALPOSITIVE = MAKE_MENUENTRY( "Digital +", &MF_BluefontRed, &MEF_BigSliders, &MEO_JOYSTICKAXIS_DIGITALPOSITIVE, Option );
+
+static MenuEntry_t *MEL_JOYSTICKAXIS[] = {
+ &ME_JOYSTICKAXIS_ANALOG,
+ &ME_JOYSTICKAXIS_SCALE,
+ &ME_JOYSTICKAXIS_DEAD,
+ &ME_JOYSTICKAXIS_SATU,
+ &ME_Space8_Redfont,
+ &ME_JOYSTICKAXIS_DIGITALNEGATIVE,
+ &ME_JOYSTICKAXIS_DIGITALPOSITIVE,
+};
+
+static MenuEntry_t *MEL_INTERNAL_JOYSTICKAXIS_DIGITAL[] = {
+ &ME_JOYSTICKAXIS_DIGITALNEGATIVE,
+ &ME_JOYSTICKAXIS_DIGITALPOSITIVE,
+};
+
+#ifdef USE_OPENGL
+static MenuOption_t MEO_RENDERERSETUP_HIGHTILE = MAKE_MENUOPTION( &MF_Bluefont, &MEOS_NoYes, &usehightile );
+static MenuEntry_t ME_RENDERERSETUP_HIGHTILE = MAKE_MENUENTRY( "True color textures:", &MF_BluefontRed, &MEF_SmallOptions, &MEO_RENDERERSETUP_HIGHTILE, Option );
+
+static char const *MEOSN_RENDERERSETUP_TEXQUALITY [] = { "Full", "Half", "Barf", };
+static MenuOptionSet_t MEOS_RENDERERSETUP_TEXQUALITY = MAKE_MENUOPTIONSET(MEOSN_RENDERERSETUP_TEXQUALITY, NULL, 0x2);
+static MenuOption_t MEO_RENDERERSETUP_TEXQUALITY = MAKE_MENUOPTION(&MF_Bluefont, &MEOS_RENDERERSETUP_TEXQUALITY, &r_downsize);
+static MenuEntry_t ME_RENDERERSETUP_TEXQUALITY = MAKE_MENUENTRY("GL texture quality:", &MF_BluefontRed, &MEF_SmallOptions, &MEO_RENDERERSETUP_TEXQUALITY, Option);
+
+
+static MenuOption_t MEO_RENDERERSETUP_PRECACHE = MAKE_MENUOPTION( &MF_Bluefont, &MEOS_OffOn, &ud.config.useprecache );
+static MenuEntry_t ME_RENDERERSETUP_PRECACHE = MAKE_MENUENTRY( "Pre-load map textures:", &MF_BluefontRed, &MEF_SmallOptions, &MEO_RENDERERSETUP_PRECACHE, Option );
+# ifndef EDUKE32_GLES
+static char const *MEOSN_RENDERERSETUP_TEXCACHE[] = { "Off", "On", "Compr.", };
+static MenuOptionSet_t MEOS_RENDERERSETUP_TEXCACHE = MAKE_MENUOPTIONSET( MEOSN_RENDERERSETUP_TEXCACHE, NULL, 0x2 );
+static MenuOption_t MEO_RENDERERSETUP_TEXCACHE = MAKE_MENUOPTION( &MF_Bluefont, &MEOS_RENDERERSETUP_TEXCACHE, &glusetexcache );
+static MenuEntry_t ME_RENDERERSETUP_TEXCACHE = MAKE_MENUENTRY( "On-disk texture cache:", &MF_BluefontRed, &MEF_SmallOptions, &MEO_RENDERERSETUP_TEXCACHE, Option );
+# endif
+# ifdef USE_GLEXT
+static MenuOption_t MEO_RENDERERSETUP_DETAILTEX = MAKE_MENUOPTION( &MF_Bluefont, &MEOS_NoYes, &r_detailmapping );
+static MenuEntry_t ME_RENDERERSETUP_DETAILTEX = MAKE_MENUENTRY( "Detail textures:", &MF_BluefontRed, &MEF_SmallOptions, &MEO_RENDERERSETUP_DETAILTEX, Option );
+static MenuOption_t MEO_RENDERERSETUP_GLOWTEX = MAKE_MENUOPTION(&MF_Bluefont, &MEOS_NoYes, &r_glowmapping);
+static MenuEntry_t ME_RENDERERSETUP_GLOWTEX = MAKE_MENUENTRY("Glow textures:", &MF_BluefontRed, &MEF_SmallOptions, &MEO_RENDERERSETUP_GLOWTEX, Option);
+# endif
+static MenuOption_t MEO_RENDERERSETUP_MODELS = MAKE_MENUOPTION( &MF_Bluefont, &MEOS_NoYes, &usemodels );
+static MenuEntry_t ME_RENDERERSETUP_MODELS = MAKE_MENUENTRY( "3D models:", &MF_BluefontRed, &MEF_SmallOptions, &MEO_RENDERERSETUP_MODELS, Option );
+static MenuOption_t MEO_RENDERERSETUP_PALETTEEMULATION = MAKE_MENUOPTION(&MF_Bluefont, &MEOS_NoYes, &r_usetileshades);
+static MenuEntry_t ME_RENDERERSETUP_PALETTEEMULATION = MAKE_MENUENTRY("Palette emulation:", &MF_BluefontRed, &MEF_SmallOptions, &MEO_RENDERERSETUP_PALETTEEMULATION, Option);
+#endif
+
+#ifdef POLYMER
+static char const *MEOSN_POLYMER_LIGHTS [] = { "Off", "Full", "Map only", };
+static MenuOptionSet_t MEOS_POLYMER_LIGHTS = MAKE_MENUOPTIONSET(MEOSN_POLYMER_LIGHTS, NULL, 0x2);
+static MenuOption_t MEO_POLYMER_LIGHTS = MAKE_MENUOPTION(&MF_Bluefont, &MEOS_POLYMER_LIGHTS, &pr_lighting);
+static MenuEntry_t ME_POLYMER_LIGHTS = MAKE_MENUENTRY("Dynamic lights:", &MF_BluefontRed, &MEF_SmallOptions, &MEO_POLYMER_LIGHTS, Option);
+
+static MenuRangeInt32_t MEO_POLYMER_LIGHTPASSES = MAKE_MENURANGE(&r_pr_maxlightpasses, &MF_Bluefont, 1, 10, 1, 10, 1);
+static MenuEntry_t ME_POLYMER_LIGHTPASSES = MAKE_MENUENTRY("Lights per surface:", &MF_BluefontRed, &MEF_SmallOptions, &MEO_POLYMER_LIGHTPASSES, RangeInt32);
+
+static MenuOption_t MEO_POLYMER_SHADOWS = MAKE_MENUOPTION(&MF_Bluefont, &MEOS_OffOn, &pr_shadows);
+static MenuEntry_t ME_POLYMER_SHADOWS = MAKE_MENUENTRY("Dynamic shadows:", &MF_BluefontRed, &MEF_SmallOptions, &MEO_POLYMER_SHADOWS, Option);
+
+static MenuRangeInt32_t MEO_POLYMER_SHADOWCOUNT = MAKE_MENURANGE(&pr_shadowcount, &MF_Bluefont, 1, 10, 1, 10, 1);
+static MenuEntry_t ME_POLYMER_SHADOWCOUNT = MAKE_MENUENTRY("Shadows per surface:", &MF_BluefontRed, &MEF_SmallOptions, &MEO_POLYMER_SHADOWCOUNT, RangeInt32);
+
+static MenuOption_t MEO_POLYMER_PALETTEEMULATION = MAKE_MENUOPTION(&MF_Bluefont, &MEOS_NoYes, &pr_artmapping);
+static MenuEntry_t ME_POLYMER_PALETTEEMULATION = MAKE_MENUENTRY("Palette emulation:", &MF_BluefontRed, &MEF_SmallOptions, &MEO_POLYMER_PALETTEEMULATION, Option);
+
+#endif
+
+#ifdef USE_OPENGL
+static MenuEntry_t *MEL_RENDERERSETUP_POLYMOST[] = {
+ &ME_RENDERERSETUP_HIGHTILE,
+ &ME_RENDERERSETUP_TEXQUALITY,
+ &ME_RENDERERSETUP_PRECACHE,
+# ifndef EDUKE32_GLES
+ &ME_RENDERERSETUP_TEXCACHE,
+# endif
+# ifdef USE_GLEXT
+ &ME_RENDERERSETUP_DETAILTEX,
+ &ME_RENDERERSETUP_GLOWTEX,
+# endif
+ &ME_Space4_Bluefont,
+ &ME_RENDERERSETUP_MODELS,
+ &ME_RENDERERSETUP_PALETTEEMULATION,
+};
+
+#ifdef POLYMER
+static MenuEntry_t *MEL_RENDERERSETUP_POLYMER [] = {
+ &ME_RENDERERSETUP_HIGHTILE,
+ &ME_RENDERERSETUP_TEXQUALITY,
+ &ME_RENDERERSETUP_PRECACHE,
+# ifndef EDUKE32_GLES
+ &ME_RENDERERSETUP_TEXCACHE,
+# endif
+# ifdef USE_GLEXT
+ &ME_RENDERERSETUP_DETAILTEX,
+ &ME_RENDERERSETUP_GLOWTEX,
+ &ME_POLYMER_PALETTEEMULATION,
+# endif
+ &ME_Space4_Bluefont,
+ &ME_RENDERERSETUP_MODELS,
+ &ME_Space4_Bluefont,
+ &ME_POLYMER_LIGHTS,
+ &ME_POLYMER_LIGHTPASSES,
+ &ME_POLYMER_SHADOWS,
+ &ME_POLYMER_SHADOWCOUNT,
+};
+#endif
+#endif
+
+#ifdef EDUKE32_ANDROID_MENU
+static MenuRangeFloat_t MEO_COLCORR_GAMMA = MAKE_MENURANGE( &vid_gamma, &MF_Bluefont, 1.f, 2.5f, 0.f, 39, 1 );
+#else
+static MenuRangeFloat_t MEO_COLCORR_GAMMA = MAKE_MENURANGE( &vid_gamma, &MF_Bluefont, 0.3f, 4.f, 0.f, 38, 1 );
+#endif
+static MenuEntry_t ME_COLCORR_GAMMA = MAKE_MENUENTRY( "Gamma:", &MF_Redfont, &MEF_ColorCorrect, &MEO_COLCORR_GAMMA, RangeFloat );
+static MenuRangeFloat_t MEO_COLCORR_CONTRAST = MAKE_MENURANGE( &vid_contrast, &MF_Bluefont, 0.1f, 2.7f, 0.f, 53, 1 );
+static MenuEntry_t ME_COLCORR_CONTRAST = MAKE_MENUENTRY( "Contrast:", &MF_Redfont, &MEF_ColorCorrect, &MEO_COLCORR_CONTRAST, RangeFloat );
+static MenuRangeFloat_t MEO_COLCORR_BRIGHTNESS = MAKE_MENURANGE( &vid_brightness, &MF_Bluefont, -0.8f, 0.8f, 0.f, 33, 1 );
+static MenuEntry_t ME_COLCORR_BRIGHTNESS = MAKE_MENUENTRY( "Brightness:", &MF_Redfont, &MEF_ColorCorrect, &MEO_COLCORR_BRIGHTNESS, RangeFloat );
+static MenuEntry_t ME_COLCORR_RESET = MAKE_MENUENTRY( "Reset To Defaults", &MF_Redfont, &MEF_ColorCorrect, &MEO_NULL, Link );
+#ifdef EDUKE32_ANDROID_MENU
+#define MINVIS 1.f
+#else
+#define MINVIS 0.125f
+#endif
+#ifndef EDUKE32_SIMPLE_MENU
+static MenuRangeFloat_t MEO_COLCORR_AMBIENT = MAKE_MENURANGE( &r_ambientlight, &MF_Bluefont, MINVIS, 4.f, 0.f, 32, 1 );
+static MenuEntry_t ME_COLCORR_AMBIENT = MAKE_MENUENTRY( "Visibility:", &MF_Redfont, &MEF_ColorCorrect, &MEO_COLCORR_AMBIENT, RangeFloat );
+#endif
+static MenuEntry_t *MEL_COLCORR[] = {
+ &ME_COLCORR_GAMMA,
+#ifndef EDUKE32_ANDROID_MENU
+ &ME_COLCORR_CONTRAST,
+ &ME_COLCORR_BRIGHTNESS,
+#endif
+#ifndef EDUKE32_SIMPLE_MENU
+ &ME_COLCORR_AMBIENT,
+#endif
+ &ME_Space8_Redfont,
+ &ME_COLCORR_RESET,
+};
+
+static MenuEntry_t *MEL_SCREENSETUP[] = {
+#ifndef EDUKE32_SIMPLE_MENU
+ &ME_SCREENSETUP_SCREENSIZE,
+#endif
+
+ &ME_SCREENSETUP_SBARSIZE,
+
+ &ME_SCREENSETUP_CROSSHAIR,
+ &ME_SCREENSETUP_CROSSHAIRSIZE,
+
+ &ME_SCREENSETUP_LEVELSTATS,
+ &ME_SCREENSETUP_TEXTSIZE,
+
+ &ME_SCREENSETUP_SHOWPICKUPMESSAGES,
+};
+
+// Save and load will be filled in before every viewing of the save/load screen.
+static MenuLink_t MEO_LOAD = { MENU_LOADVERIFY, MA_None, };
+static MenuEntry_t ME_LOAD_TEMPLATE = MAKE_MENUENTRY( NULL, &MF_MinifontSave, &MEF_LoadSave, &MEO_LOAD, Link );
+static MenuEntry_t ME_LOAD_EMPTY = MAKE_MENUENTRY( NULL, &MF_MinifontSave, &MEF_LoadSave, nullptr, Dummy );
+static MenuEntry_t *ME_LOAD;
+static MenuEntry_t **MEL_LOAD;
+
+static char const s_NewSaveGame[] = "(New Save Game)";
+static MenuString_t MEO_SAVE_TEMPLATE = MAKE_MENUSTRING( NULL, &MF_MinifontSave, MAXSAVEGAMENAME, 0 );
+static MenuString_t MEO_SAVE_NEW = MAKE_MENUSTRING( NULL, &MF_MinifontSave, MAXSAVEGAMENAME, 0 );
+static MenuString_t *MEO_SAVE;
+static MenuEntry_t ME_SAVE_TEMPLATE = MAKE_MENUENTRY( NULL, &MF_MinifontSave, &MEF_LoadSave, &MEO_SAVE_TEMPLATE, String );
+static MenuEntry_t ME_SAVE_NEW = MAKE_MENUENTRY( s_NewSaveGame, &MF_MinifontSave, &MEF_LoadSave, &MEO_SAVE_NEW, String );
+static MenuEntry_t *ME_SAVE;
+static MenuEntry_t **MEL_SAVE;
+
+static int32_t soundrate, soundvoices, musicbackend;
+static MenuOption_t MEO_SOUND = MAKE_MENUOPTION( &MF_Redfont, &MEOS_OffOn, &ud.config.SoundToggle );
+static MenuEntry_t ME_SOUND = MAKE_MENUENTRY( "Sound:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_SOUND, Option );
+
+static MenuOption_t MEO_SOUND_MUSIC = MAKE_MENUOPTION( &MF_Redfont, &MEOS_OffOn, &ud.config.MusicToggle );
+static MenuEntry_t ME_SOUND_MUSIC = MAKE_MENUENTRY( "Music:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_SOUND_MUSIC, Option );
+
+static MenuRangeInt32_t MEO_SOUND_VOLUME_MASTER = MAKE_MENURANGE( &ud.config.MasterVolume, &MF_Redfont, 0, 255, 0, 33, 2 );
+static MenuEntry_t ME_SOUND_VOLUME_MASTER = MAKE_MENUENTRY( "Volume:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_SOUND_VOLUME_MASTER, RangeInt32 );
+
+static MenuRangeInt32_t MEO_SOUND_VOLUME_EFFECTS = MAKE_MENURANGE( &ud.config.FXVolume, &MF_Redfont, 0, 255, 0, 33, 2 );
+static MenuEntry_t ME_SOUND_VOLUME_EFFECTS = MAKE_MENUENTRY( "Effects:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_SOUND_VOLUME_EFFECTS, RangeInt32 );
+
+static MenuRangeInt32_t MEO_SOUND_VOLUME_MUSIC = MAKE_MENURANGE( &ud.config.MusicVolume, &MF_Redfont, 0, 255, 0, 33, 2 );
+static MenuEntry_t ME_SOUND_VOLUME_MUSIC = MAKE_MENUENTRY( "Music:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_SOUND_VOLUME_MUSIC, RangeInt32 );
+
+static MenuOption_t MEO_SOUND_DUKETALK = MAKE_MENUOPTION(&MF_Redfont, &MEOS_NoYes, NULL);
+#ifndef EDUKE32_STANDALONE
+static MenuEntry_t ME_SOUND_DUKETALK = MAKE_MENUENTRY( "Duke talk:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_SOUND_DUKETALK, Option );
+#else
+static MenuEntry_t ME_SOUND_DUKETALK = MAKE_MENUENTRY("Player speech:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_SOUND_DUKETALK, Option);
+#endif
+
+static char const *MEOSN_SOUND_SAMPLINGRATE[] = { "22050Hz", "44100Hz", "48000Hz", };
+static int32_t MEOSV_SOUND_SAMPLINGRATE[] = { 22050, 44100, 48000, };
+static MenuOptionSet_t MEOS_SOUND_SAMPLINGRATE = MAKE_MENUOPTIONSET( MEOSN_SOUND_SAMPLINGRATE, MEOSV_SOUND_SAMPLINGRATE, 0x3 );
+static MenuOption_t MEO_SOUND_SAMPLINGRATE = MAKE_MENUOPTION( &MF_Redfont, &MEOS_SOUND_SAMPLINGRATE, &soundrate );
+static MenuEntry_t ME_SOUND_SAMPLINGRATE = MAKE_MENUENTRY( "Sample rate:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_SOUND_SAMPLINGRATE, Option );
+
+#ifndef EDUKE32_SIMPLE_MENU
+static MenuRangeInt32_t MEO_SOUND_NUMVOICES = MAKE_MENURANGE( &soundvoices, &MF_Redfont, 16, 256, 0, 16, 1 );
+static MenuEntry_t ME_SOUND_NUMVOICES = MAKE_MENUENTRY( "Voices:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_SOUND_NUMVOICES, RangeInt32 );
+#endif
+
+static char const *MEOSN_SOUND_FLUIDSYNTH_BACKEND[] ={ "default", "jack", "alsa", "oss", "pulseaudio", "coreaudio", "dsound", "portaudio", "sndman", "dart" };
+static int32_t MEOSV_SOUND_FLUIDSYNTH_BACKEND[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+static MenuOptionSet_t MEOS_SOUND_FLUIDSYNTH_BACKEND = MAKE_MENUOPTIONSET( MEOSN_SOUND_FLUIDSYNTH_BACKEND, MEOSV_SOUND_FLUIDSYNTH_BACKEND, 0x3 );
+static MenuOption_t MEO_SOUND_FLUIDSYNTH_BACKEND = MAKE_MENUOPTION( &MF_Redfont, &MEOS_SOUND_FLUIDSYNTH_BACKEND, &musicbackend );
+static MenuEntry_t ME_SOUND_FLUIDSYNTH_BACKEND = MAKE_MENUENTRY( "Backend:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_SOUND_FLUIDSYNTH_BACKEND, Option );
+
+static MenuEntry_t ME_SOUND_RESTART = MAKE_MENUENTRY( "Apply Changes", &MF_Redfont, &MEF_BigOptions_Apply, &MEO_NULL, Link );
+
+#ifndef EDUKE32_SIMPLE_MENU
+static MenuLink_t MEO_ADVSOUND = { MENU_ADVSOUND, MA_Advance, };
+static MenuEntry_t ME_SOUND_ADVSOUND = MAKE_MENUENTRY( "Advanced", &MF_Redfont, &MEF_BigOptionsRt, &MEO_ADVSOUND, Link );
+#endif
+
+static MenuEntry_t *MEL_SOUND[] = {
+ &ME_SOUND,
+ &ME_SOUND_MUSIC,
+ &ME_SOUND_VOLUME_MASTER,
+ &ME_SOUND_VOLUME_EFFECTS,
+ &ME_SOUND_VOLUME_MUSIC,
+ &ME_SOUND_DUKETALK,
+#ifndef EDUKE32_SIMPLE_MENU
+ &ME_SOUND_ADVSOUND,
+#endif
+};
+
+static MenuEntry_t *MEL_ADVSOUND[] = {
+ &ME_SOUND_SAMPLINGRATE,
+ &ME_Space2_Redfont,
+#ifndef EDUKE32_SIMPLE_MENU
+ &ME_SOUND_NUMVOICES,
+ &ME_Space2_Redfont,
+#endif
+ &ME_SOUND_FLUIDSYNTH_BACKEND,
+ &ME_SOUND_RESTART,
+};
+
+MAKE_MENU_TOP_ENTRYLINK( "Player Setup", MEF_CenterMenu, NETWORK_PLAYERSETUP, MENU_PLAYER );
+MAKE_MENU_TOP_ENTRYLINK( "Join Game", MEF_CenterMenu, NETWORK_JOINGAME, MENU_NETJOIN );
+MAKE_MENU_TOP_ENTRYLINK( "Host Game", MEF_CenterMenu, NETWORK_HOSTGAME, MENU_NETHOST );
+
+static MenuEntry_t *MEL_NETWORK[] = {
+ &ME_NETWORK_PLAYERSETUP,
+ &ME_NETWORK_JOINGAME,
+ &ME_NETWORK_HOSTGAME,
+};
+
+static MenuString_t MEO_PLAYER_NAME = MAKE_MENUSTRING( szPlayerName, &MF_Bluefont, MAXPLAYERNAME, 0 );
+static MenuEntry_t ME_PLAYER_NAME = MAKE_MENUENTRY( "Name", &MF_BluefontRed, &MEF_PlayerNarrow, &MEO_PLAYER_NAME, String );
+static char const *MEOSN_PLAYER_COLOR[] = { "Auto", "Blue", "Red", "Green", "Gray", "Dark gray", "Dark green", "Brown", "Dark blue", "Bright red", "Yellow", };
+static int32_t MEOSV_PLAYER_COLOR[] = { 0, 9, 10, 11, 12, 13, 14, 15, 16, 21, 23, };
+static MenuOptionSet_t MEOS_PLAYER_COLOR = MAKE_MENUOPTIONSET( MEOSN_PLAYER_COLOR, MEOSV_PLAYER_COLOR, 0x2 );
+static MenuOption_t MEO_PLAYER_COLOR = MAKE_MENUOPTION( &MF_Bluefont, &MEOS_PLAYER_COLOR, &ud.color );
+static MenuEntry_t ME_PLAYER_COLOR = MAKE_MENUENTRY( "Color", &MF_BluefontRed, &MEF_PlayerNarrow, &MEO_PLAYER_COLOR, Option );
+static char const *MEOSN_PLAYER_TEAM[] = { "Blue", "Red", "Green", "Gray", };
+static MenuOptionSet_t MEOS_PLAYER_TEAM = MAKE_MENUOPTIONSET( MEOSN_PLAYER_TEAM, NULL, 0x2 );
+static MenuOption_t MEO_PLAYER_TEAM = MAKE_MENUOPTION( &MF_Bluefont, &MEOS_PLAYER_TEAM, &ud.team );
+static MenuEntry_t ME_PLAYER_TEAM = MAKE_MENUENTRY( "Team", &MF_BluefontRed, &MEF_PlayerNarrow, &MEO_PLAYER_TEAM, Option );
+#ifndef EDUKE32_SIMPLE_MENU
+static MenuLink_t MEO_PLAYER_MACROS = { MENU_MACROS, MA_Advance, };
+static MenuEntry_t ME_PLAYER_MACROS = MAKE_MENUENTRY( "Multiplayer macros", &MF_BluefontRed, &MEF_SmallOptions, &MEO_PLAYER_MACROS, Link );
+#endif
+
+static MenuEntry_t *MEL_PLAYER[] = {
+ &ME_PLAYER_NAME,
+ &ME_Space4_Bluefont,
+ &ME_PLAYER_COLOR,
+ &ME_Space4_Bluefont,
+ &ME_PLAYER_TEAM,
+#ifndef EDUKE32_SIMPLE_MENU
+ &ME_Space8_Bluefont,
+ &ME_PLAYER_MACROS,
+#endif
+};
+
+static MenuString_t MEO_MACROS_TEMPLATE = MAKE_MENUSTRING( NULL, &MF_Bluefont, MAXRIDECULELENGTH, 0 );
+static MenuString_t MEO_MACROS[10];
+static MenuEntry_t ME_MACROS_TEMPLATE = MAKE_MENUENTRY( NULL, &MF_Bluefont, &MEF_Macros, &MEO_MACROS_TEMPLATE, String );
+static MenuEntry_t ME_MACROS[MAXRIDECULE];
+static MenuEntry_t *MEL_MACROS[MAXRIDECULE];
+
+#ifndef EDUKE32_SIMPLE_MENU
+static char const *MenuUserMap = "User Map";
+#endif
+static char const *MenuSkillNone = "None";
+
+static char const *MEOSN_NetGametypes[MAXGAMETYPES];
+static char const *MEOSN_NetEpisodes[MAXVOLUMES+1];
+static int32_t MEOSV_NetEpisodes[MAXVOLUMES+1];
+static char const *MEOSN_NetLevels[MAXVOLUMES][MAXLEVELS];
+static char const *MEOSN_NetSkills[MAXSKILLS+1];
+
+static MenuLink_t MEO_NETHOST_OPTIONS = { MENU_NETOPTIONS, MA_Advance, };
+static MenuEntry_t ME_NETHOST_OPTIONS = MAKE_MENUENTRY( "Game Options", &MF_Redfont, &MEF_VideoSetup, &MEO_NETHOST_OPTIONS, Link );
+static MenuEntry_t ME_NETHOST_LAUNCH = MAKE_MENUENTRY( "Launch Game", &MF_Redfont, &MEF_VideoSetup, &MEO_NULL, Link );
+
+static MenuEntry_t *MEL_NETHOST[] = {
+ &ME_NETHOST_OPTIONS,
+ &ME_NETHOST_LAUNCH,
+};
+
+static MenuOptionSet_t MEOS_NETOPTIONS_GAMETYPE = MAKE_MENUOPTIONSET( MEOSN_NetGametypes, NULL, 0x0 );
+static MenuOption_t MEO_NETOPTIONS_GAMETYPE = MAKE_MENUOPTION( &MF_Bluefont, &MEOS_NETOPTIONS_GAMETYPE, &ud.m_coop );
+static MenuEntry_t ME_NETOPTIONS_GAMETYPE = MAKE_MENUENTRY( "Game Type", &MF_Redfont, &MEF_NetSetup, &MEO_NETOPTIONS_GAMETYPE, Option );
+static MenuOptionSet_t MEOS_NETOPTIONS_EPISODE = MAKE_MENUOPTIONSET( MEOSN_NetEpisodes, MEOSV_NetEpisodes, 0x0 );
+static int32_t NetEpisode;
+static MenuOption_t MEO_NETOPTIONS_EPISODE = MAKE_MENUOPTION( &MF_Bluefont, &MEOS_NETOPTIONS_EPISODE, &NetEpisode );
+static MenuEntry_t ME_NETOPTIONS_EPISODE = MAKE_MENUENTRY( "Episode", &MF_Redfont, &MEF_NetSetup, &MEO_NETOPTIONS_EPISODE, Option );
+static MenuOptionSet_t MEOS_NETOPTIONS_LEVEL_TEMPLATE = MAKE_MENUOPTIONSETNULL;
+static MenuOptionSet_t MEOS_NETOPTIONS_LEVEL[MAXVOLUMES];
+static MenuOption_t MEO_NETOPTIONS_LEVEL = MAKE_MENUOPTION( &MF_Bluefont, NULL, &ud.m_level_number );
+static MenuEntry_t ME_NETOPTIONS_LEVEL = MAKE_MENUENTRY( "Level", &MF_Redfont, &MEF_NetSetup, &MEO_NETOPTIONS_LEVEL, Option );
+static MenuLink_t MEO_NETOPTIONS_USERMAP = { MENU_NETUSERMAP, MA_Advance, };
+static MenuEntry_t ME_NETOPTIONS_USERMAP = MAKE_MENUENTRY( "User Map", &MF_Redfont, &MEF_NetSetup, &MEO_NETOPTIONS_USERMAP, Link );
+static MenuOptionSet_t MEOS_NETOPTIONS_MONSTERS = MAKE_MENUOPTIONSET( MEOSN_NetSkills, NULL, 0x0 );
+static MenuOption_t MEO_NETOPTIONS_MONSTERS = MAKE_MENUOPTION( &MF_Bluefont, &MEOS_NETOPTIONS_MONSTERS, NULL );
+static MenuEntry_t ME_NETOPTIONS_MONSTERS = MAKE_MENUENTRY( "Monsters", &MF_Redfont, &MEF_NetSetup, &MEO_NETOPTIONS_MONSTERS, Option );
+static MenuOption_t MEO_NETOPTIONS_MARKERS = MAKE_MENUOPTION( &MF_Bluefont, &MEOS_OffOn, &ud.m_marker );
+static MenuEntry_t ME_NETOPTIONS_MARKERS = MAKE_MENUENTRY( "Markers", &MF_Redfont, &MEF_NetSetup, &MEO_NETOPTIONS_MARKERS, Option );
+static MenuOption_t MEO_NETOPTIONS_MAPEXITS = MAKE_MENUOPTION( &MF_Bluefont, &MEOS_OnOff, &ud.m_noexits );
+static MenuEntry_t ME_NETOPTIONS_MAPEXITS = MAKE_MENUENTRY( "Map Exits", &MF_Redfont, &MEF_NetSetup, &MEO_NETOPTIONS_MAPEXITS, Option );
+static MenuOption_t MEO_NETOPTIONS_FRFIRE = MAKE_MENUOPTION( &MF_Bluefont, &MEOS_OffOn, &ud.m_ffire );
+static MenuEntry_t ME_NETOPTIONS_FRFIRE = MAKE_MENUENTRY( "Fr. Fire", &MF_Redfont, &MEF_NetSetup, &MEO_NETOPTIONS_FRFIRE, Option );
+static MenuEntry_t ME_NETOPTIONS_ACCEPT = MAKE_MENUENTRY( "Accept", &MF_Redfont, &MEF_NetSetup_Confirm, &MEO_NULL, Link );
+
+static MenuEntry_t *MEL_NETOPTIONS[] = {
+ &ME_NETOPTIONS_GAMETYPE,
+ &ME_NETOPTIONS_EPISODE,
+ &ME_NETOPTIONS_LEVEL,
+ &ME_NETOPTIONS_MONSTERS,
+ &ME_NETOPTIONS_MARKERS,
+ &ME_NETOPTIONS_MAPEXITS,
+ &ME_NETOPTIONS_ACCEPT,
+};
+
+static char MenuServer[BMAX_PATH] = "localhost";
+static MenuString_t MEO_NETJOIN_SERVER = MAKE_MENUSTRING( MenuServer, &MF_Bluefont, BMAX_PATH, 0 );
+static MenuEntry_t ME_NETJOIN_SERVER = MAKE_MENUENTRY( "Server", &MF_Redfont, &MEF_VideoSetup, &MEO_NETJOIN_SERVER, String );
+#define MAXPORTSTRINGLENGTH 6 // unsigned 16-bit integer
+static char MenuPort[MAXPORTSTRINGLENGTH] = "19014";
+static MenuString_t MEO_NETJOIN_PORT = MAKE_MENUSTRING( MenuPort, &MF_Bluefont, MAXPORTSTRINGLENGTH, INPUT_NUMERIC );
+static MenuEntry_t ME_NETJOIN_PORT = MAKE_MENUENTRY( "Port", &MF_Redfont, &MEF_VideoSetup, &MEO_NETJOIN_PORT, String );
+static MenuEntry_t ME_NETJOIN_CONNECT = MAKE_MENUENTRY( "Connect", &MF_Redfont, &MEF_VideoSetup_Apply, &MEO_NULL, Link );
+
+static MenuEntry_t *MEL_NETJOIN[] = {
+ &ME_NETJOIN_SERVER,
+ &ME_NETJOIN_PORT,
+ &ME_NETJOIN_CONNECT,
+};
+
+
+#define NoTitle NULL
+
+#define MAKE_MENUMENU(Title, Format, Entries) { Title, Format, Entries, ARRAY_SIZE(Entries), 0, 0, 0 }
+#define MAKE_MENUMENU_CUSTOMSIZE(Title, Format, Entries) { Title, Format, Entries, 0, 0, 0, 0 }
+
+static MenuMenu_t M_MAIN = MAKE_MENUMENU( NoTitle, &MMF_Top_Main, MEL_MAIN );
+static MenuMenu_t M_MAIN_INGAME = MAKE_MENUMENU( NoTitle, &MMF_Top_Main, MEL_MAIN_INGAME );
+static MenuMenu_t M_EPISODE = MAKE_MENUMENU( "Select An Episode", &MMF_Top_Episode, MEL_EPISODE );
+static MenuMenu_t M_SKILL = MAKE_MENUMENU( "Select Skill", &MMF_Top_Skill, MEL_SKILL );
+#ifndef EDUKE32_SIMPLE_MENU
+static MenuMenu_t M_GAMESETUP = MAKE_MENUMENU( "Game Setup", &MMF_BigOptions, MEL_GAMESETUP );
+#endif
+static MenuMenu_t M_OPTIONS = MAKE_MENUMENU( s_Options, &MMF_Top_Options, MEL_OPTIONS );
+static MenuMenu_t M_VIDEOSETUP = MAKE_MENUMENU( "Video Mode", &MMF_BigOptions, MEL_VIDEOSETUP );
+static MenuMenu_t M_KEYBOARDSETUP = MAKE_MENUMENU( "Keyboard Setup", &MMF_Top_Options, MEL_KEYBOARDSETUP );
+static MenuMenu_t M_CONTROLS = MAKE_MENUMENU( "Control Setup", &MMF_Top_Options, MEL_CONTROLS );
+static MenuMenu_t M_CHEATS = MAKE_MENUMENU( "Cheats", &MMF_SmallOptions, MEL_CHEATS );
+static MenuMenu_t M_MOUSESETUP = MAKE_MENUMENU( "Mouse Setup", &MMF_BigOptions, MEL_MOUSESETUP );
+#ifdef EDUKE32_ANDROID_MENU
+static MenuMenu_t M_TOUCHSETUP = MAKE_MENUMENU( "Touch Setup", &MMF_Top_Options, MEL_TOUCHSETUP );
+static MenuMenu_t M_TOUCHSENS = MAKE_MENUMENU( "Sensitivity", &MMF_BigOptions, MEL_TOUCHSENS);
+static MenuPanel_t M_TOUCHBUTTONS = { "Button Setup", MENU_TOUCHSETUP, MA_Return, MENU_TOUCHSETUP, MA_Advance, };
+#endif
+static MenuMenu_t M_JOYSTICKSETUP = MAKE_MENUMENU( "Joystick Setup", &MMF_Top_Joystick_Network, MEL_JOYSTICKSETUP );
+static MenuMenu_t M_JOYSTICKBTNS = MAKE_MENUMENU( "Joystick Buttons", &MMF_MouseJoySetupBtns, MEL_JOYSTICKBTNS );
+static MenuMenu_t M_JOYSTICKAXES = MAKE_MENUMENU( "Joystick Axes", &MMF_BigSliders, MEL_JOYSTICKAXES );
+static MenuMenu_t M_KEYBOARDKEYS = MAKE_MENUMENU( "Key Configuration", &MMF_KeyboardSetupFuncs, MEL_KEYBOARDSETUPFUNCS );
+static MenuMenu_t M_MOUSEBTNS = MAKE_MENUMENU( "Mouse Buttons", &MMF_MouseJoySetupBtns, MEL_MOUSESETUPBTNS );
+static MenuMenu_t M_MOUSEADVANCED = MAKE_MENUMENU( "Advanced Mouse", &MMF_BigSliders, MEL_MOUSEADVANCED );
+static MenuMenu_t M_JOYSTICKAXIS = MAKE_MENUMENU( NULL, &MMF_BigSliders, MEL_JOYSTICKAXIS );
+#ifdef USE_OPENGL
+static MenuMenu_t M_RENDERERSETUP_POLYMOST = MAKE_MENUMENU( "Polymost Setup", &MMF_SmallOptions, MEL_RENDERERSETUP_POLYMOST );
+# ifdef POLYMER
+static MenuMenu_t M_RENDERERSETUP_POLYMER = MAKE_MENUMENU("Polymer Setup", &MMF_SmallOptions, MEL_RENDERERSETUP_POLYMER );
+# endif
+#endif
+static MenuMenu_t M_COLCORR = MAKE_MENUMENU( "Color Correction", &MMF_ColorCorrect, MEL_COLCORR );
+static MenuMenu_t M_SCREENSETUP = MAKE_MENUMENU( "HUD Setup", &MMF_BigOptions, MEL_SCREENSETUP );
+static MenuMenu_t M_DISPLAYSETUP = MAKE_MENUMENU( "Display Setup", &MMF_BigOptions, MEL_DISPLAYSETUP );
+static MenuMenu_t M_LOAD = MAKE_MENUMENU_CUSTOMSIZE( s_LoadGame, &MMF_LoadSave, MEL_LOAD );
+static MenuMenu_t M_SAVE = MAKE_MENUMENU_CUSTOMSIZE( s_SaveGame, &MMF_LoadSave, MEL_SAVE );
+static MenuMenu_t M_SOUND = MAKE_MENUMENU( "Sound Setup", &MMF_BigOptions, MEL_SOUND );
+static MenuMenu_t M_ADVSOUND = MAKE_MENUMENU( "Advanced Sound", &MMF_BigOptions, MEL_ADVSOUND );
+static MenuMenu_t M_NETWORK = MAKE_MENUMENU( "Network Game", &MMF_Top_Joystick_Network, MEL_NETWORK );
+static MenuMenu_t M_PLAYER = MAKE_MENUMENU( "Player Setup", &MMF_SmallOptions, MEL_PLAYER );
+static MenuMenu_t M_MACROS = MAKE_MENUMENU( "Multiplayer Macros", &MMF_Macros, MEL_MACROS );
+static MenuMenu_t M_NETHOST = MAKE_MENUMENU( "Host Network Game", &MMF_SmallOptionsNarrow, MEL_NETHOST );
+static MenuMenu_t M_NETOPTIONS = MAKE_MENUMENU( "Net Game Options", &MMF_NetSetup, MEL_NETOPTIONS );
+static MenuMenu_t M_NETJOIN = MAKE_MENUMENU( "Join Network Game", &MMF_SmallOptionsNarrow, MEL_NETJOIN );
+
+#ifdef EDUKE32_SIMPLE_MENU
+static MenuPanel_t M_STORY = { NoTitle, MENU_STORY, MA_Return, MENU_STORY, MA_Advance, };
+#else
+static MenuPanel_t M_STORY = { NoTitle, MENU_F1HELP, MA_Return, MENU_F1HELP, MA_Advance, };
+#endif
+
+static MenuPanel_t M_F1HELP = { NoTitle, MENU_STORY, MA_Return, MENU_STORY, MA_Advance, };
+static MenuPanel_t M_CREDITS = { NoTitle, MENU_CREDITS5, MA_Return, MENU_CREDITS2, MA_Advance, };
+static MenuPanel_t M_CREDITS2 = { NoTitle, MENU_CREDITS, MA_Return, MENU_CREDITS3, MA_Advance, };
+static MenuPanel_t M_CREDITS3 = { NoTitle, MENU_CREDITS2, MA_Return, MENU_CREDITS4, MA_Advance, };
+static MenuPanel_t M_CREDITS4 = { "About " APPNAME, MENU_CREDITS3, MA_Return, MENU_CREDITS5, MA_Advance, };
+static MenuPanel_t M_CREDITS5 = { "About " APPNAME, MENU_CREDITS4, MA_Return, MENU_CREDITS, MA_Advance, };
+
+#define CURSOR_CENTER_2LINE { MENU_MARGIN_CENTER<<16, 120<<16, }
+#define CURSOR_CENTER_3LINE { MENU_MARGIN_CENTER<<16, 129<<16, }
+#define CURSOR_BOTTOMRIGHT { 304<<16, 186<<16, }
+
+static MenuVerify_t M_QUIT = { CURSOR_CENTER_2LINE, MENU_CLOSE, MA_None, };
+static MenuVerify_t M_QUITTOTITLE = { CURSOR_CENTER_2LINE, MENU_CLOSE, MA_None, };
+static MenuVerify_t M_LOADVERIFY = { CURSOR_CENTER_3LINE, MENU_CLOSE, MA_None, };
+static MenuVerify_t M_NEWVERIFY = { CURSOR_CENTER_2LINE, MENU_EPISODE, MA_Advance, };
+static MenuVerify_t M_SAVEVERIFY = { CURSOR_CENTER_2LINE, MENU_SAVE, MA_None, };
+static MenuVerify_t M_RESETPLAYER = { CURSOR_CENTER_3LINE, MENU_CLOSE, MA_None, };
+
+static MenuMessage_t M_NETWAITMASTER = { CURSOR_BOTTOMRIGHT, MENU_NULL, MA_None, };
+static MenuMessage_t M_NETWAITVOTES = { CURSOR_BOTTOMRIGHT, MENU_NULL, MA_None, };
+static MenuMessage_t M_BUYDUKE = { CURSOR_BOTTOMRIGHT, MENU_EPISODE, MA_Return, };
+
+static MenuTextForm_t M_ADULTPASSWORD = { NULL, "Enter Password:", MAXPWLOCKOUT, MTF_Password };
+static MenuTextForm_t M_CHEATENTRY = { NULL, "Enter Cheat Code:", MAXCHEATLEN, 0 };
+static MenuTextForm_t M_CHEAT_WARP = { NULL, "Enter Warp #:", 3, 0 };
+static MenuTextForm_t M_CHEAT_SKILL = { NULL, "Enter Skill #:", 1, 0 };
+
+#define MAKE_MENUFILESELECT(a, b, c) { a, { &MMF_FileSelectLeft, &MMF_FileSelectRight }, { &MF_Minifont, &MF_MinifontRed }, b, c, { NULL, NULL }, { 0, 0 }, { 3<<16, 3<<16 }, FNLIST_INITIALIZER, 0 }
+
+static MenuFileSelect_t M_USERMAP = MAKE_MENUFILESELECT( "Select A User Map", "*.map", boardfilename );
+
+// MUST be in ascending order of MenuID enum values due to binary search
+static Menu_t Menus[] = {
+ { &M_MAIN, MENU_MAIN, MENU_CLOSE, MA_None, Menu },
+ { &M_MAIN_INGAME, MENU_MAIN_INGAME, MENU_CLOSE, MA_None, Menu },
+ { &M_EPISODE, MENU_EPISODE, MENU_MAIN, MA_Return, Menu },
+ { &M_USERMAP, MENU_USERMAP, MENU_EPISODE, MA_Return, FileSelect },
+ { &M_SKILL, MENU_SKILL, MENU_EPISODE, MA_Return, Menu },
+#ifndef EDUKE32_SIMPLE_MENU
+ { &M_GAMESETUP, MENU_GAMESETUP, MENU_OPTIONS, MA_Return, Menu },
+#endif
+ { &M_OPTIONS, MENU_OPTIONS, MENU_MAIN, MA_Return, Menu },
+ { &M_VIDEOSETUP, MENU_VIDEOSETUP, MENU_DISPLAYSETUP, MA_Return, Menu },
+ { &M_KEYBOARDSETUP, MENU_KEYBOARDSETUP, MENU_CONTROLS, MA_Return, Menu },
+ { &M_MOUSESETUP, MENU_MOUSESETUP, MENU_CONTROLS, MA_Return, Menu },
+ { &M_JOYSTICKSETUP, MENU_JOYSTICKSETUP, MENU_CONTROLS, MA_Return, Menu },
+ { &M_JOYSTICKBTNS, MENU_JOYSTICKBTNS, MENU_JOYSTICKSETUP, MA_Return, Menu },
+ { &M_JOYSTICKAXES, MENU_JOYSTICKAXES, MENU_JOYSTICKSETUP, MA_Return, Menu },
+ { &M_KEYBOARDKEYS, MENU_KEYBOARDKEYS, MENU_KEYBOARDSETUP, MA_Return, Menu },
+ { &M_MOUSEBTNS, MENU_MOUSEBTNS, MENU_MOUSESETUP, MA_Return, Menu },
+ { &M_MOUSEADVANCED, MENU_MOUSEADVANCED, MENU_MOUSESETUP, MA_Return, Menu },
+ { &M_JOYSTICKAXIS, MENU_JOYSTICKAXIS, MENU_JOYSTICKAXES, MA_Return, Menu },
+#ifdef EDUKE32_ANDROID_MENU
+ { &M_TOUCHSETUP, MENU_TOUCHSETUP, MENU_OPTIONS, MA_Return, Menu },
+ { &M_TOUCHSENS, MENU_TOUCHSENS, MENU_TOUCHSETUP, MA_Return, Menu },
+ { &M_TOUCHBUTTONS, MENU_TOUCHBUTTONS, MENU_TOUCHSETUP, MA_Return, Panel },
+#endif
+ { &M_CONTROLS, MENU_CONTROLS, MENU_OPTIONS, MA_Return, Menu },
+#ifdef USE_OPENGL
+ { &M_RENDERERSETUP_POLYMOST, MENU_POLYMOST, MENU_DISPLAYSETUP, MA_Return, Menu },
+#endif
+ { &M_COLCORR, MENU_COLCORR, MENU_DISPLAYSETUP, MA_Return, Menu },
+ { &M_COLCORR, MENU_COLCORR_INGAME, MENU_CLOSE, MA_Return, Menu },
+ { &M_SCREENSETUP, MENU_SCREENSETUP, MENU_DISPLAYSETUP, MA_Return, Menu },
+ { &M_DISPLAYSETUP, MENU_DISPLAYSETUP, MENU_OPTIONS, MA_Return, Menu },
+#ifdef POLYMER
+ { &M_RENDERERSETUP_POLYMER, MENU_POLYMER, MENU_DISPLAYSETUP, MA_Return, Menu },
+#endif
+ { &M_LOAD, MENU_LOAD, MENU_MAIN, MA_Return, Menu },
+ { &M_SAVE, MENU_SAVE, MENU_MAIN, MA_Return, Menu },
+ { &M_STORY, MENU_STORY, MENU_MAIN, MA_Return, Panel },
+ { &M_F1HELP, MENU_F1HELP, MENU_MAIN, MA_Return, Panel },
+ { &M_QUIT, MENU_QUIT, MENU_PREVIOUS, MA_Return, Verify },
+ { &M_QUITTOTITLE, MENU_QUITTOTITLE, MENU_PREVIOUS, MA_Return, Verify },
+ { &M_QUIT, MENU_QUIT_INGAME, MENU_CLOSE, MA_None, Verify },
+ { &M_NETHOST, MENU_NETSETUP, MENU_MAIN, MA_Return, Menu },
+ { &M_NETWAITMASTER, MENU_NETWAITMASTER, MENU_MAIN, MA_Return, Message },
+ { &M_NETWAITVOTES, MENU_NETWAITVOTES, MENU_MAIN, MA_Return, Message },
+ { &M_SOUND, MENU_SOUND, MENU_OPTIONS, MA_Return, Menu },
+ { &M_SOUND, MENU_SOUND_INGAME, MENU_CLOSE, MA_Return, Menu },
+ { &M_ADVSOUND, MENU_ADVSOUND, MENU_SOUND, MA_Return, Menu },
+#ifdef EDUKE32_SIMPLE_MENU
+ { &M_CHEATS, MENU_CHEATS, MENU_OPTIONS, MA_Return, Menu },
+#else
+ { &M_CHEATS, MENU_CHEATS, MENU_GAMESETUP, MA_Return, Menu },
+#endif
+ { &M_CHEATENTRY, MENU_CHEATENTRY, MENU_CHEATS, MA_None, TextForm },
+ { &M_CHEAT_WARP, MENU_CHEAT_WARP, MENU_CHEATS, MA_None, TextForm },
+ { &M_CHEAT_SKILL, MENU_CHEAT_SKILL, MENU_CHEATS, MA_None, TextForm },
+ { &M_CREDITS, MENU_CREDITS, MENU_MAIN, MA_Return, Panel },
+ { &M_CREDITS2, MENU_CREDITS2, MENU_MAIN, MA_Return, Panel },
+ { &M_CREDITS3, MENU_CREDITS3, MENU_MAIN, MA_Return, Panel },
+ { &M_CREDITS4, MENU_CREDITS4, MENU_MAIN, MA_Return, Panel },
+ { &M_CREDITS5, MENU_CREDITS5, MENU_MAIN, MA_Return, Panel },
+ { &M_LOADVERIFY, MENU_LOADVERIFY, MENU_LOAD, MA_None, Verify },
+ { &M_NEWVERIFY, MENU_NEWVERIFY, MENU_MAIN, MA_Return, Verify },
+ { &M_SAVEVERIFY, MENU_SAVEVERIFY, MENU_SAVE, MA_None, Verify },
+ { &M_ADULTPASSWORD, MENU_ADULTPASSWORD, MENU_GAMESETUP, MA_None, TextForm },
+ { &M_RESETPLAYER, MENU_RESETPLAYER, MENU_CLOSE, MA_None, Verify },
+ { &M_BUYDUKE, MENU_BUYDUKE, MENU_EPISODE, MA_Return, Message },
+ { &M_NETWORK, MENU_NETWORK, MENU_MAIN, MA_Return, Menu },
+ { &M_PLAYER, MENU_PLAYER, MENU_OPTIONS, MA_Return, Menu },
+ { &M_MACROS, MENU_MACROS, MENU_PLAYER, MA_Return, Menu },
+ { &M_NETHOST, MENU_NETHOST, MENU_NETWORK, MA_Return, Menu },
+ { &M_NETOPTIONS, MENU_NETOPTIONS, MENU_NETWORK, MA_Return, Menu },
+ { &M_USERMAP, MENU_NETUSERMAP, MENU_NETOPTIONS, MA_Return, FileSelect },
+ { &M_NETJOIN, MENU_NETJOIN, MENU_NETWORK, MA_Return, Menu },
+};
+
+static CONSTEXPR const size_t numMenus = ARRAY_SIZE(Menus);
+
+Menu_t *m_currentMenu = &Menus[0];
+static Menu_t *m_previousMenu = &Menus[0];
+static Menu_t * m_parentMenu;
+
+MenuID_t g_currentMenu;
+static MenuID_t g_previousMenu;
+
+#define MenuMenu_ChangeEntryList(m, el)\
+ do {\
+ m.entrylist = el;\
+ m.numEntries = ARRAY_SIZE(el);\
+ } while (0)
+
+static void MenuEntry_DisableOnCondition(MenuEntry_t * const entry, const int32_t condition)
+{
+ if (condition)
+ entry->flags |= MEF_Disabled;
+ else
+ entry->flags &= ~MEF_Disabled;
+}
+static void MenuEntry_LookDisabledOnCondition(MenuEntry_t * const entry, const int32_t condition)
+{
+ if (condition)
+ entry->flags |= MEF_LookDisabled;
+ else
+ entry->flags &= ~MEF_LookDisabled;
+}
+
+static int32_t M_RunMenu_Menu(Menu_t *cm, MenuMenu_t *menu, MenuEntry_t *currentry, int32_t state, const vec2_t origin, bool actually_draw = true);
+static void Menu_EntryFocus(/*MenuEntry_t *entry*/);
+
+static MenuEntry_t *Menu_AdjustForCurrentEntryAssignment(MenuMenu_t *menu)
+{
+ MenuEntry_t *currentry = menu->entrylist[menu->currentEntry];
+
+ Menu_EntryFocus(/*currentry*/);
+
+ if (currentry->ybottom - menu->scrollPos > klabs(menu->format->bottomcutoff))
+ menu->scrollPos = currentry->ybottom - klabs(menu->format->bottomcutoff);
+ else if (currentry->ytop - menu->scrollPos < menu->format->pos.y)
+ menu->scrollPos = currentry->ytop - menu->format->pos.y;
+
+ return currentry;
+}
+
+static MenuEntry_t *Menu_AdjustForCurrentEntryAssignmentBlind(MenuMenu_t *menu)
+{
+ M_RunMenu_Menu(nullptr, menu, nullptr, 0, { 0, 0 }, false);
+ return Menu_AdjustForCurrentEntryAssignment(menu);
+}
+
+/*
+This function prepares data after ART and CON have been processed.
+It also initializes some data in loops rather than statically at compile time.
+*/
+void Menu_Init(void)
+{
+ int32_t i, j, k;
+
+ // prepare menu fonts
+ MF_Redfont.tilenum = BIGALPHANUM;
+ MF_Bluefont.tilenum = MF_BluefontRed.tilenum = MF_BluefontGame.tilenum = STARTALPHANUM;
+ MF_Minifont.tilenum = MF_MinifontRed.tilenum = MF_MinifontSave.tilenum = MINIFONT;
+ MF_Redfont.emptychar.y = tilesiz[BIGALPHANUM].y<<16;
+ MF_Bluefont.emptychar.y = MF_BluefontRed.emptychar.y = MF_BluefontGame.emptychar.y = tilesiz[STARTALPHANUM].y<<16;
+ MF_Minifont.emptychar.y = MF_MinifontRed.emptychar.y = MF_MinifontSave.emptychar.y = tilesiz[MINIFONT].y<<16;
+ if (NAM_WW2GI)
+ MF_Bluefont.between.x = MF_BluefontRed.between.x = 0;
+ if (!minitext_lowercase)
+ {
+ MF_Minifont.textflags |= TEXT_UPPERCASE;
+ MF_MinifontRed.textflags |= TEXT_UPPERCASE;
+ MF_MinifontSave.textflags |= TEXT_UPPERCASE;
+ }
+
+ // prepare gamefuncs and keys
+ MEOSN_Gamefuncs[0] = MenuGameFuncNone;
+ MEOSV_Gamefuncs[0] = -1;
+ k = 1;
+ for (i = 0; i < NUMGAMEFUNCTIONS; ++i)
+ {
+ Bstrcpy(MenuGameFuncs[i], gamefunctions[i]);
+
+ for (j = 0; j < MAXGAMEFUNCLEN; ++j)
+ if (MenuGameFuncs[i][j] == '_')
+ MenuGameFuncs[i][j] = ' ';
+
+ if (gamefunctions[i][0] != '\0')
+ {
+ MEOSN_Gamefuncs[k] = MenuGameFuncs[i];
+ MEOSV_Gamefuncs[k] = i;
+ ++k;
+ }
+ }
+ MEOS_Gamefuncs.numOptions = k;
+
+ for (i = 0; i < NUMKEYS; ++i)
+ MEOSN_Keys[i] = key_names[i];
+ MEOSN_Keys[NUMKEYS-1] = MenuKeyNone;
+
+
+ // prepare episodes
+ k = 0;
+ for (i = 0; i < g_volumeCnt; ++i)
+ {
+ if (g_volumeNames[i][0])
+ {
+ if (!(g_volumeFlags[i] & EF_HIDEFROMSP))
+ {
+ MEL_EPISODE[i] = &ME_EPISODE[i];
+ ME_EPISODE[i] = ME_EPISODE_TEMPLATE;
+ ME_EPISODE[i].name = g_volumeNames[i];
+ }
+
+ // if (!(EpisodeFlags[i] & EF_HIDEFROMMP))
+ {
+ MEOSN_NetEpisodes[k] = g_volumeNames[i];
+ MEOSV_NetEpisodes[k] = i;
+
+ k++;
+ }
+ }
+
+ // prepare levels
+ MEOS_NETOPTIONS_LEVEL[i] = MEOS_NETOPTIONS_LEVEL_TEMPLATE;
+ for (j = 0; j < MAXLEVELS; ++j)
+ {
+ MEOSN_NetLevels[i][j] = g_mapInfo[MAXLEVELS*i+j].name;
+ if (g_mapInfo[i*MAXLEVELS+j].filename != NULL)
+ MEOS_NETOPTIONS_LEVEL[i].numOptions = j+1;
+ }
+ MEOS_NETOPTIONS_LEVEL[i].optionNames = MEOSN_NetLevels[i];
+ }
+ M_EPISODE.numEntries = g_volumeCnt+2;
+#ifndef EDUKE32_SIMPLE_MENU
+ MEL_EPISODE[g_volumeCnt] = &ME_Space4_Redfont;
+ MEL_EPISODE[g_volumeCnt+1] = &ME_EPISODE_USERMAP;
+ MEOSN_NetEpisodes[k] = MenuUserMap;
+ MEOSV_NetEpisodes[k] = MAXVOLUMES;
+#else
+ M_EPISODE.numEntries = g_volumeCnt;
+ k--;
+#endif
+ MEOS_NETOPTIONS_EPISODE.numOptions = k + 1;
+ NetEpisode = MEOSV_NetEpisodes[0];
+ MMF_Top_Episode.pos.y = (58 + (3-k)*6)<<16;
+ if (g_skillCnt == 0)
+ MEO_EPISODE.linkID = MENU_NULL;
+
+ // prepare skills
+ k = -1;
+ for (i = 0; i < g_skillCnt && g_skillNames[i][0]; ++i)
+ {
+ MEL_SKILL[i] = &ME_SKILL[i];
+ ME_SKILL[i] = ME_SKILL_TEMPLATE;
+ ME_SKILL[i].name = g_skillNames[i];
+
+ MEOSN_NetSkills[i] = g_skillNames[i];
+
+ k = i;
+ }
+ ++k;
+ M_SKILL.numEntries = g_skillCnt; // k;
+ MEOS_NETOPTIONS_MONSTERS.numOptions = g_skillCnt + 1; // k+1;
+ MEOSN_NetSkills[g_skillCnt] = MenuSkillNone;
+ MMF_Top_Skill.pos.y = (58 + (4-g_skillCnt)*6)<<16;
+ M_SKILL.currentEntry = 1;
+ Menu_AdjustForCurrentEntryAssignmentBlind(&M_SKILL);
+
+ // prepare multiplayer gametypes
+ k = -1;
+ for (i = 0; i < MAXGAMETYPES; ++i)
+ if (g_gametypeNames[i][0])
+ {
+ MEOSN_NetGametypes[i] = g_gametypeNames[i];
+ k = i;
+ }
+ ++k;
+ MEOS_NETOPTIONS_GAMETYPE.numOptions = k;
+ if (NAM_WW2GI)
+ ME_NETOPTIONS_MONSTERS.name = "Enemies";
+
+ // prepare text chat macros
+ for (i = 0; i < MAXRIDECULE; ++i)
+ {
+ MEL_MACROS[i] = &ME_MACROS[i];
+ ME_MACROS[i] = ME_MACROS_TEMPLATE;
+ ME_MACROS[i].entry = &MEO_MACROS[i];
+ MEO_MACROS[i] = MEO_MACROS_TEMPLATE;
+
+ MEO_MACROS[i].variable = ud.ridecule[i];
+ }
+
+ // prepare input
+ for (i = 0; i < NUMGAMEFUNCTIONS; ++i)
+ {
+ if (MenuGameFuncs[i][0] == '\0')
+ {
+ MEL_KEYBOARDSETUPFUNCS[i] = NULL;
+ continue;
+ }
+
+ MEL_KEYBOARDSETUPFUNCS[i] = &ME_KEYBOARDSETUPFUNCS[i];
+ ME_KEYBOARDSETUPFUNCS[i] = ME_KEYBOARDSETUPFUNCS_TEMPLATE;
+ ME_KEYBOARDSETUPFUNCS[i].name = MenuGameFuncs[i];
+ ME_KEYBOARDSETUPFUNCS[i].entry = &MEO_KEYBOARDSETUPFUNCS[i];
+ MEO_KEYBOARDSETUPFUNCS[i] = MEO_KEYBOARDSETUPFUNCS_TEMPLATE;
+ MEO_KEYBOARDSETUPFUNCS[i].column[0] = &ud.config.KeyboardKeys[i][0];
+ MEO_KEYBOARDSETUPFUNCS[i].column[1] = &ud.config.KeyboardKeys[i][1];
+ }
+ M_KEYBOARDKEYS.numEntries = NUMGAMEFUNCTIONS;
+ for (i = 0; i < MENUMOUSEFUNCTIONS; ++i)
+ {
+ MEL_MOUSESETUPBTNS[i] = &ME_MOUSESETUPBTNS[i];
+ ME_MOUSESETUPBTNS[i] = ME_MOUSEJOYSETUPBTNS_TEMPLATE;
+ ME_MOUSESETUPBTNS[i].name = MenuMouseNames[i];
+ ME_MOUSESETUPBTNS[i].entry = &MEO_MOUSESETUPBTNS[i];
+ MEO_MOUSESETUPBTNS[i] = MEO_MOUSEJOYSETUPBTNS_TEMPLATE;
+ MEO_MOUSESETUPBTNS[i].data = &ud.config.MouseFunctions[MenuMouseDataIndex[i][0]][MenuMouseDataIndex[i][1]];
+ }
+ for (i = 0; i < 2*joynumbuttons + 8*joynumhats; ++i)
+ {
+ if (i < 2*joynumbuttons)
+ {
+ if (i & 1)
+ Bsnprintf(MenuJoystickNames[i], MAXJOYBUTTONSTRINGLENGTH, "Double %s", getjoyname(1, i>>1));
+ else
+ Bstrncpy(MenuJoystickNames[i], getjoyname(1, i>>1), MAXJOYBUTTONSTRINGLENGTH);
+ }
+ else
+ {
+ Bsnprintf(MenuJoystickNames[i], MAXJOYBUTTONSTRINGLENGTH, (i & 1) ? "Double Hat %d %s" : "Hat %d %s", ((i - 2*joynumbuttons)>>3), MenuJoystickHatDirections[((i - 2*joynumbuttons)>>1) % 4]);
+ }
+
+ MEL_JOYSTICKBTNS[i] = &ME_JOYSTICKBTNS[i];
+ ME_JOYSTICKBTNS[i] = ME_MOUSEJOYSETUPBTNS_TEMPLATE;
+ ME_JOYSTICKBTNS[i].name = MenuJoystickNames[i];
+ ME_JOYSTICKBTNS[i].entry = &MEO_JOYSTICKBTNS[i];
+ MEO_JOYSTICKBTNS[i] = MEO_MOUSEJOYSETUPBTNS_TEMPLATE;
+ MEO_JOYSTICKBTNS[i].data = &ud.config.JoystickFunctions[i>>1][i&1];
+ }
+ M_JOYSTICKBTNS.numEntries = 2*joynumbuttons + 8*joynumhats;
+ for (i = 0; i < joynumaxes; ++i)
+ {
+ ME_JOYSTICKAXES[i] = ME_JOYSTICKAXES_TEMPLATE;
+ Bstrncpy(MenuJoystickAxes[i], getjoyname(0, i), MAXJOYBUTTONSTRINGLENGTH);
+ ME_JOYSTICKAXES[i].name = MenuJoystickAxes[i];
+ MEL_JOYSTICKAXES[i] = &ME_JOYSTICKAXES[i];
+ }
+ M_JOYSTICKAXES.numEntries = joynumaxes;
+
+ // prepare video setup
+ for (i = 0; i < validmodecnt; ++i)
+ {
+ int32_t *const r = &MEOS_VIDEOSETUP_RESOLUTION.numOptions;
+
+ for (j = 0; j < *r; ++j)
+ {
+ if (validmode[i].xdim == resolution[j].xdim && validmode[i].ydim == resolution[j].ydim)
+ {
+ resolution[j].flags |= validmode[i].fs ? RES_FS : RES_WIN;
+ if (validmode[i].bpp > resolution[j].bppmax)
+ resolution[j].bppmax = validmode[i].bpp;
+ break;
+ }
+ }
+
+ if (j == *r) // no match found
+ {
+ resolution[j].xdim = validmode[i].xdim;
+ resolution[j].ydim = validmode[i].ydim;
+ resolution[j].bppmax = validmode[i].bpp;
+ resolution[j].flags = validmode[i].fs ? RES_FS : RES_WIN;
+ Bsnprintf(resolution[j].name, MAXRESOLUTIONSTRINGLENGTH, "%d x %d", resolution[j].xdim, resolution[j].ydim);
+ MEOSN_VIDEOSETUP_RESOLUTION[j] = resolution[j].name;
+ ++*r;
+ }
+ }
+
+ // prepare sound setup
+#ifndef EDUKE32_STANDALONE
+ if (WW2GI)
+ ME_SOUND_DUKETALK.name = "GI talk:";
+ else if (NAM)
+ ME_SOUND_DUKETALK.name = "Grunt talk:";
+#endif
+
+ if (KXDWN)
+ {
+ MF_Redfont.between.x = 2<<16;
+ MF_Redfont.cursorScale = 32768;
+ MF_Redfont.zoom = 16384;
+ MF_Bluefont.between.x = MF_BluefontRed.between.x = 0;
+ MF_Bluefont.zoom = MF_BluefontRed.zoom = MF_BluefontGame.zoom = 16384;
+
+ // hack; should swap out pointers
+ MF_Minifont = MF_Bluefont;
+ MF_MinifontRed = MF_BluefontRed;
+ MF_MinifontSave.zoom = 32768;
+
+ MMF_Top_Main.pos.x = 40<<16;
+ MMF_Top_Main.pos.y = 130<<16;
+ MMF_Top_Main.bottomcutoff = 190<<16;
+ M_OPTIONS.format = &MMF_Top_Main;
+
+ MEF_MainMenu.width = MEF_OptionsMenu.width = -(160<<16);
+ MEF_MainMenu.marginBottom = 7<<16;
+
+ M_OPTIONS.title = NoTitle;
+ }
+
+ // prepare shareware
+ if (VOLUMEONE)
+ {
+ // blue out episodes beyond the first
+ for (i = 1; i < g_volumeCnt; ++i)
+ {
+ if (MEL_EPISODE[i])
+ {
+ ME_EPISODE[i].entry = &MEO_EPISODE_SHAREWARE;
+ ME_EPISODE[i].flags |= MEF_LookDisabled;
+ }
+ }
+ M_EPISODE.numEntries = g_volumeCnt; // remove User Map (and spacer)
+ MEOS_NETOPTIONS_EPISODE.numOptions = 1;
+ MenuEntry_DisableOnCondition(&ME_NETOPTIONS_EPISODE, 1);
+ }
+
+ // prepare pre-Atomic
+ if (!VOLUMEALL || !PLUTOPAK)
+ {
+ // prepare credits
+ M_CREDITS.title = M_CREDITS2.title = M_CREDITS3.title = s_Credits;
+ }
+}
+
+static void Menu_Run(Menu_t *cm, vec2_t origin);
+
+
+static void Menu_BlackRectangle(int32_t x, int32_t y, int32_t width, int32_t height, int32_t orientation);
+
+/*
+At present, no true difference is planned between Menu_Pre() and Menu_PreDraw().
+They are separate for purposes of organization.
+*/
+
+static void Menu_Pre(MenuID_t cm)
+{
+ int32_t i;
+ DukePlayer_t *ps = g_player[myconnectindex].ps;
+
+ switch (cm)
+ {
+ case MENU_MAIN_INGAME:
+ MenuEntry_DisableOnCondition(&ME_MAIN_SAVEGAME, ud.recstat == 2);
+ MenuEntry_DisableOnCondition(&ME_MAIN_QUITTOTITLE, g_netServer || numplayers > 1);
+ fallthrough__;
+ case MENU_MAIN:
+ if ((g_netServer || ud.multimode > 1) && ud.recstat != 2)
+ {
+ ME_MAIN_NEWGAME.entry = &MEO_MAIN_NEWGAME_NETWORK;
+ ME_MAIN_NEWGAME_INGAME.entry = &MEO_MAIN_NEWGAME_NETWORK;
+ }
+ else
+ {
+ ME_MAIN_NEWGAME.entry = &MEO_MAIN_NEWGAME;
+ ME_MAIN_NEWGAME_INGAME.entry = &MEO_MAIN_NEWGAME_INGAME;
+ }
+ break;
+
+ case MENU_GAMESETUP:
+ MEO_GAMESETUP_DEMOREC.options = (ps->gm&MODE_GAME) ? &MEOS_DemoRec : &MEOS_OffOn;
+ MenuEntry_DisableOnCondition(&ME_GAMESETUP_DEMOREC, (ps->gm&MODE_GAME) && ud.m_recstat != 1);
+ break;
+
+#ifdef USE_OPENGL
+ case MENU_DISPLAYSETUP:
+ if (getrendermode() == REND_CLASSIC)
+ MenuMenu_ChangeEntryList(M_DISPLAYSETUP, MEL_DISPLAYSETUP);
+#ifdef POLYMER
+ else if (getrendermode() == REND_POLYMER)
+ MenuMenu_ChangeEntryList(M_DISPLAYSETUP, MEL_DISPLAYSETUP_GL_POLYMER);
+#endif
+ else
+ MenuMenu_ChangeEntryList(M_DISPLAYSETUP, MEL_DISPLAYSETUP_GL);
+
+ vpsize = ud.screen_size + 4*(ud.screen_size>=8 && ud.statusbarmode==0) + 4*(ud.screen_size>=4 && ud.althud==0);
+
+ if (getrendermode() != REND_CLASSIC)
+ {
+ for (i = (int32_t) ARRAY_SIZE(MEOSV_DISPLAYSETUP_ANISOTROPY) - 1; i >= 0; --i)
+ {
+ if (MEOSV_DISPLAYSETUP_ANISOTROPY[i] <= glinfo.maxanisotropy)
+ {
+ MEOS_DISPLAYSETUP_ANISOTROPY.numOptions = i + 1;
+ break;
+ }
+ }
+ }
+ break;
+
+ case MENU_POLYMER:
+ case MENU_POLYMOST:
+ MenuEntry_DisableOnCondition(&ME_RENDERERSETUP_TEXQUALITY, !usehightile);
+ MenuEntry_DisableOnCondition(&ME_RENDERERSETUP_PRECACHE, !usehightile);
+# ifndef EDUKE32_GLES
+ MenuEntry_DisableOnCondition(&ME_RENDERERSETUP_TEXCACHE, !(glusetexcompr && usehightile));
+# endif
+# ifdef USE_GLEXT
+ MenuEntry_DisableOnCondition(&ME_RENDERERSETUP_DETAILTEX, !usehightile);
+ MenuEntry_DisableOnCondition(&ME_RENDERERSETUP_GLOWTEX, !usehightile);
+# endif
+ break;
+#endif
+
+ case MENU_VIDEOSETUP:
+ {
+ const int32_t nr = newresolution;
+
+ MenuEntry_DisableOnCondition(&ME_VIDEOSETUP_APPLY,
+ (xdim == resolution[nr].xdim && ydim == resolution[nr].ydim &&
+ getrendermode() == newrendermode && fullscreen == newfullscreen
+ && vsync == newvsync
+ )
+ || (newfullscreen ? !(resolution[nr].flags & RES_FS) : !(resolution[nr].flags & RES_WIN))
+ || (newrendermode != REND_CLASSIC && resolution[nr].bppmax <= 8));
+ break;
+ }
+
+ case MENU_SOUND:
+ case MENU_SOUND_INGAME:
+ case MENU_ADVSOUND:
+ MenuEntry_DisableOnCondition(&ME_SOUND_VOLUME_MASTER, !ud.config.SoundToggle && !ud.config.MusicToggle);
+ MenuEntry_DisableOnCondition(&ME_SOUND_VOLUME_EFFECTS, !ud.config.SoundToggle);
+ MenuEntry_DisableOnCondition(&ME_SOUND_VOLUME_MUSIC, !ud.config.MusicToggle);
+ MenuEntry_DisableOnCondition(&ME_SOUND_DUKETALK, !ud.config.SoundToggle);
+ MenuEntry_DisableOnCondition(&ME_SOUND_SAMPLINGRATE, !ud.config.SoundToggle && !ud.config.MusicToggle);
+#ifndef EDUKE32_SIMPLE_MENU
+ MenuEntry_DisableOnCondition(&ME_SOUND_NUMVOICES, !ud.config.SoundToggle);
+#endif
+ MenuEntry_DisableOnCondition(&ME_SOUND_FLUIDSYNTH_BACKEND, !ud.config.SoundToggle && !ud.config.MusicToggle);
+ MenuEntry_DisableOnCondition(&ME_SOUND_RESTART, soundrate == ud.config.MixRate &&
+ soundvoices == ud.config.NumVoices &&
+ musicbackend == ud.config.FluidsynthBackend);
+ break;
+
+#ifndef EDUKE32_SIMPLE_MENU
+ case MENU_MOUSESETUP:
+ MenuEntry_DisableOnCondition(&ME_MOUSESETUP_MOUSEAIMING, ud.mouseaiming);
+ break;
+#endif
+ case MENU_NETOPTIONS:
+ if (MEOSV_NetEpisodes[MEO_NETOPTIONS_EPISODE.currentOption] == MAXVOLUMES)
+ MEL_NETOPTIONS[2] = &ME_NETOPTIONS_USERMAP;
+ else
+ {
+ MEL_NETOPTIONS[2] = &ME_NETOPTIONS_LEVEL;
+ MEO_NETOPTIONS_LEVEL.options = &MEOS_NETOPTIONS_LEVEL[MEOSV_NetEpisodes[MEO_NETOPTIONS_EPISODE.currentOption]];
+ }
+ if (!(g_gametypeFlags[ud.m_coop] & GAMETYPE_MARKEROPTION))
+ {
+ ME_NETOPTIONS_MARKERS.type = Dummy;
+ ME_NETOPTIONS_MARKERS.flags |= MEF_Disabled;
+ }
+ else
+ {
+ ME_NETOPTIONS_MARKERS.type = Option;
+ ME_NETOPTIONS_MARKERS.flags &= ~MEF_Disabled;
+ }
+ MEL_NETOPTIONS[5] = (g_gametypeFlags[ud.m_coop] & (GAMETYPE_PLAYERSFRIENDLY|GAMETYPE_TDM)) ? &ME_NETOPTIONS_FRFIRE : &ME_NETOPTIONS_MAPEXITS;
+ break;
+
+ case MENU_OPTIONS:
+ MenuEntry_DisableOnCondition(&ME_OPTIONS_PLAYERSETUP, ud.recstat == 1);
+ MenuEntry_DisableOnCondition(&ME_OPTIONS_JOYSTICKSETUP, CONTROL_JoyPresent == 0);
+ break;
+
+ case MENU_COLCORR:
+ case MENU_COLCORR_INGAME:
+ MenuEntry_DisableOnCondition(&ME_COLCORR_CONTRAST, !gammabrightness);
+ MenuEntry_DisableOnCondition(&ME_COLCORR_BRIGHTNESS, !gammabrightness);
+ break;
+
+ case MENU_CHEATS:
+ case MENU_CHEATENTRY:
+ case MENU_CHEAT_WARP:
+ case MENU_CHEAT_SKILL:
+ {
+ const int32_t menucheatsdisabled = numplayers != 1 || !(g_player[myconnectindex].ps->gm & MODE_GAME);
+
+ for (i = 0; i < NUMCHEATFUNCS; i++)
+ {
+ uint32_t cheatmask = cl_cheatmask & (1<<i);
+
+ // KEEPINSYNC: NAM_WW2GI_CHEATS
+ if (NAM_WW2GI)
+ {
+ switch (i)
+ {
+ case CHEATFUNC_CASHMAN:
+ case CHEATFUNC_GIVEALLITEMS:
+ case CHEATFUNC_QUOTEBETA:
+ case CHEATFUNC_MONSTERS:
+ case CHEATFUNC_QUOTEALLEN:
+ case CHEATFUNC_GIVEKEYS:
+ cheatmask = 0;
+ break;
+ }
+ }
+ if (WW2GI)
+ {
+ switch (i)
+ {
+ case CHEATFUNC_HYPER:
+ cheatmask = 0;
+ break;
+ }
+ }
+
+ // only show cheats that have been typed in before
+ MEL_CHEATS[i+1] = cheatmask ? &ME_CheatCodes[i] : NULL;
+
+ // disable outside of a single-player game
+ MenuEntry_DisableOnCondition(&ME_CheatCodes[i], menucheatsdisabled);
+ }
+
+ // refresh display names of quote cheats
+ if (!DUKEBETA)
+ {
+ ME_CheatCodes[CHEATFUNC_QUOTEBETA].name = apStrings[QUOTE_CHEAT_BETA];
+ ME_CheatCodes[CHEATFUNC_QUOTETODD].name = NAM ? g_NAMMattCheatQuote : apStrings[QUOTE_CHEAT_TODD];
+ ME_CheatCodes[CHEATFUNC_QUOTEALLEN].name = apStrings[QUOTE_CHEAT_ALLEN];
+ }
+
+ MenuEntry_DisableOnCondition(&ME_ENTERCHEAT, (cl_cheatmask == UINT32_MAX));
+
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+
+static void Menu_PreDrawBackground(MenuID_t cm, const vec2_t origin)
+{
+ switch (cm)
+ {
+ case MENU_CREDITS:
+ case MENU_CREDITS2:
+ case MENU_CREDITS3:
+ if (!VOLUMEALL || !PLUTOPAK)
+ Menu_DrawBackground(origin);
+ else
+ rotatesprite_fs(origin.x + (MENU_MARGIN_CENTER<<16), origin.y + (100<<16), 65536L,0,2504+cm-MENU_CREDITS,0,0,10+64);
+ break;
+
+ case MENU_LOAD:
+ case MENU_SAVE:
+ case MENU_CREDITS4:
+ case MENU_CREDITS5:
+ Menu_DrawBackground(origin);
+ break;
+
+ case MENU_STORY:
+ rotatesprite_fs(origin.x + (MENU_MARGIN_CENTER<<16), origin.y + (100<<16), 65536L,0,TEXTSTORY,0,0,10+64);
+ break;
+
+ case MENU_F1HELP:
+ rotatesprite_fs(origin.x + (MENU_MARGIN_CENTER<<16), origin.y + (100<<16), 65536L,0,F1HELP,0,0,10+64);
+ break;
+ }
+}
+
+
+static void Menu_PreDraw(MenuID_t cm, MenuEntry_t *entry, const vec2_t origin)
+{
+ int32_t i, j, l = 0, m;
+
+ switch (cm)
+ {
+ case MENU_MAIN_INGAME:
+ l += 4;
+ fallthrough__;
+ case MENU_MAIN:
+ if ((G_GetLogoFlags() & LOGO_NOGAMETITLE) == 0)
+ {
+ rotatesprite_fs(origin.x + (MENU_MARGIN_CENTER<<16), origin.y + ((28+l)<<16), 65536L,0,INGAMEDUKETHREEDEE,0,0,10);
+ if (PLUTOPAK) // JBF 20030804
+ rotatesprite_fs(origin.x + ((MENU_MARGIN_CENTER+100)<<16), origin.y + (36<<16), 65536L,0,PLUTOPAKSPRITE+2,(sintable[(totalclock<<4)&2047]>>11),0,2+8);
+ }
+ break;
+
+ case MENU_PLAYER:
+ rotatesprite_fs(origin.x + (260<<16), origin.y + ((24+(tilesiz[APLAYER].y>>1))<<16), 49152L,0,1441-((((4-(totalclock>>4)))&3)*5),0,entry == &ME_PLAYER_TEAM ? G_GetTeamPalette(ud.team) : ud.color,10);
+ break;
+
+ case MENU_MACROS:
+ mgametextcenter(origin.x, origin.y + (144<<16), "Activate in-game with Shift-F#");
+ break;
+
+ case MENU_COLCORR:
+ case MENU_COLCORR_INGAME:
+ // center panel
+ rotatesprite_fs(origin.x + (120<<16), origin.y + (32<<16), 16384, 0, 3290, 0, 0, 2|8|16);
+ rotatesprite_fs(origin.x + (160<<16) - (tilesiz[BOTTOMSTATUSBAR].x<<13), origin.y + (82<<16) - (tilesiz[BOTTOMSTATUSBAR].y<<14), 16384, 0, BOTTOMSTATUSBAR, 0, 0, 2|8|16);
+
+ // left panel
+ rotatesprite_fs(origin.x + (40<<16), origin.y + (32<<16), 16384, 0, BONUSSCREEN, 0, 0, 2|8|16);
+
+ // right panel
+ rotatesprite_fs(origin.x + (200<<16), origin.y + (32<<16), 16384, 0, LOADSCREEN, 0, 0, 2|8|16);
+ break;
+
+ case MENU_NETSETUP:
+ case MENU_NETHOST:
+ mminitext(origin.x + (90<<16), origin.y + (90<<16), "Game Type", 2);
+ mminitext(origin.x + (90<<16), origin.y + ((90+8)<<16), "Episode", 2);
+ mminitext(origin.x + (90<<16), origin.y + ((90+8+8)<<16), "Level", 2);
+ mminitext(origin.x + (90<<16), origin.y + ((90+8+8+8)<<16), ME_NETOPTIONS_MONSTERS.name, 2);
+ if (ud.m_coop == 0)
+ mminitext(origin.x + (90<<16), origin.y + ((90+8+8+8+8)<<16), "Markers", 2);
+ else if (ud.m_coop == 1)
+ mminitext(origin.x + (90<<16), origin.y + ((90+8+8+8+8)<<16), "Friendly Fire", 2);
+ mminitext(origin.x + (90<<16), origin.y + ((90+8+8+8+8+8)<<16), "User Map", 2);
+
+ mminitext(origin.x + ((90+60)<<16), origin.y + (90<<16), g_gametypeNames[ud.m_coop], 0);
+
+ mminitext(origin.x + ((90+60)<<16), origin.y + ((90+8)<<16), g_volumeNames[ud.m_volume_number], 0);
+ mminitext(origin.x + ((90+60)<<16), origin.y + ((90+8+8)<<16), g_mapInfo[MAXLEVELS*ud.m_volume_number+ud.m_level_number].name, 0);
+ if (ud.m_monsters_off == 0 || ud.m_player_skill > 0)
+ mminitext(origin.x + ((90+60)<<16), origin.y + ((90+8+8+8)<<16), g_skillNames[ud.m_player_skill], 0);
+ else mminitext(origin.x + ((90+60)<<16), origin.y + ((90+8+8+8)<<16), "None", 0);
+ if (ud.m_coop == 0)
+ {
+ if (ud.m_marker) mminitext(origin.x + ((90+60)<<16), origin.y + ((90+8+8+8+8)<<16), "On", 0);
+ else mminitext(origin.x + ((90+60)<<16), origin.y + ((90+8+8+8+8)<<16), "Off", 0);
+ }
+ else if (ud.m_coop == 1)
+ {
+ if (ud.m_ffire) mminitext(origin.x + ((90+60)<<16), origin.y + ((90+8+8+8+8)<<16), "On", 0);
+ else mminitext(origin.x + ((90+60)<<16), origin.y + ((90+8+8+8+8)<<16), "Off", 0);
+ }
+ break;
+
+ case MENU_MOUSEADVANCED:
+ {
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(MEL_INTERNAL_MOUSEADVANCED_DAXES); i++)
+ if (entry == MEL_INTERNAL_MOUSEADVANCED_DAXES[i])
+ {
+ mgametextcenter(origin.x, origin.y + (162<<16), "Digital axes are not for mouse look\n"
+ "or for aiming up and down");
+ }
+ }
+ break;
+
+ case MENU_RESETPLAYER:
+ fade_screen_black(1);
+ Bsprintf(tempbuf, "Load last game:\n\"%s\""
+#ifndef EDUKE32_ANDROID_MENU
+ "\n(Y/N)"
+#endif
+ , g_quickload->name);
+ mgametextcenter(origin.x, origin.y + (90<<16), tempbuf);
+ break;
+
+ case MENU_LOAD:
+ {
+#if 0
+ for (i = 0; i <= 108; i += 12)
+ rotatesprite_fs(origin.x + ((160+64+91-64)<<16), origin.y + ((i+56)<<16), 65536L,0,TEXTBOX,24,0,10);
+#endif
+ Menu_BlackRectangle(origin.x + (198<<16), origin.y + (47<<16), 102<<16, 100<<16, 1|32);
+
+ rotatesprite_fs(origin.x + (22<<16), origin.y + (97<<16), 65536L,0,WINDOWBORDER2,24,0,10);
+ rotatesprite_fs(origin.x + (180<<16), origin.y + (97<<16), 65536L,1024,WINDOWBORDER2,24,0,10);
+ rotatesprite_fs(origin.x + (99<<16), origin.y + (50<<16), 65536L,512,WINDOWBORDER1,24,0,10);
+ rotatesprite_fs(origin.x + (103<<16), origin.y + (144<<16), 65536L,1024+512,WINDOWBORDER1,24,0,10);
+
+ if (M_LOAD.currentEntry >= (int32_t)g_nummenusaves)
+ {
+ mmenutext(origin.x + (72<<16), origin.y + (100<<16), "Empty");
+ break;
+ }
+
+ menusave_t & msv = g_menusaves[M_LOAD.currentEntry];
+
+ if (msv.brief.isValid())
+ {
+ rotatesprite_fs(origin.x + (101<<16), origin.y + (97<<16), 65536>>1,512,TILE_LOADSHOT,-32,0,4+10+64);
+
+ if (msv.isOldVer)
+ {
+ mmenutext(origin.x + (53<<16), origin.y + (70<<16), "Previous");
+ mmenutext(origin.x + (58<<16), origin.y + (90<<16), "Version");
+
+#ifndef EDUKE32_SIMPLE_MENU
+ Bsprintf(tempbuf,"Saved: %d.%d.%d %d-bit", savehead.majorver, savehead.minorver,
+ savehead.bytever, 8*savehead.ptrsize);
+ mgametext(origin.x + (31<<16), origin.y + (104<<16), tempbuf);
+ Bsprintf(tempbuf,"Our: %d.%d.%d %d-bit", SV_MAJOR_VER, SV_MINOR_VER, BYTEVERSION,
+ (int32_t)(8*sizeof(intptr_t)));
+ mgametext(origin.x + ((31+16)<<16), origin.y + (114<<16), tempbuf);
+#endif
+ }
+
+ if (savehead.numplayers > 1)
+ {
+ Bsprintf(tempbuf, "Players: %-2d ", savehead.numplayers);
+ mgametextcenter(origin.x, origin.y + (156<<16), tempbuf);
+ }
+
+ {
+ const char *name = g_mapInfo[(savehead.volnum*MAXLEVELS) + savehead.levnum].name;
+ Bsprintf(tempbuf, "%s / %s", name ? name : "^10unnamed^0", g_skillNames[savehead.skill-1]);
+ }
+
+ mgametextcenter(origin.x, origin.y + (168<<16), tempbuf);
+ if (savehead.volnum == 0 && savehead.levnum == 7)
+ mgametextcenter(origin.x, origin.y + (180<<16), savehead.boardfn);
+ }
+ break;
+ }
+
+ case MENU_SAVE:
+ {
+#if 0
+ for (i = 0; i <= 108; i += 12)
+ rotatesprite_fs(origin.x + ((160+64+91-64)<<16), origin.y + ((i+56)<<16), 65536L,0,TEXTBOX,24,0,10);
+#endif
+ Menu_BlackRectangle(origin.x + (198<<16), origin.y + (47<<16), 102<<16, 100<<16, 1|32);
+
+ rotatesprite_fs(origin.x + (22<<16), origin.y + (97<<16), 65536L,0,WINDOWBORDER2,24,0,10);
+ rotatesprite_fs(origin.x + (180<<16), origin.y + (97<<16), 65536L,1024,WINDOWBORDER2,24,0,10);
+ rotatesprite_fs(origin.x + (99<<16), origin.y + (50<<16), 65536L,512,WINDOWBORDER1,24,0,10);
+ rotatesprite_fs(origin.x + (103<<16), origin.y + (144<<16), 65536L,1024+512,WINDOWBORDER1,24,0,10);
+
+ j = 0;
+ for (size_t k = 0; k < g_nummenusaves+1; ++k)
+ if (((MenuString_t*)M_SAVE.entrylist[k]->entry)->editfield)
+ j |= 1;
+
+ if (j)
+ rotatesprite_fs(origin.x + (101<<16), origin.y + (97<<16), 65536L>>1,512,TILE_SAVESHOT,-32,0,4+10+64);
+ else if (0 < M_SAVE.currentEntry && M_SAVE.currentEntry <= (int32_t)g_nummenusaves)
+ {
+ if (g_menusaves[M_SAVE.currentEntry-1].brief.isValid())
+ {
+ rotatesprite_fs(origin.x + (101<<16), origin.y + (97<<16), 65536L>>1,512,TILE_LOADSHOT,-32,0,4+10+64);
+
+ if (g_menusaves[M_SAVE.currentEntry-1].isOldVer)
+ {
+ mmenutext(origin.x + (53<<16), origin.y + (70<<16), "Previous");
+ mmenutext(origin.x + (58<<16), origin.y + (90<<16), "Version");
+
+#ifndef EDUKE32_SIMPLE_MENU
+ Bsprintf(tempbuf,"Saved: %d.%d.%d %d-bit", savehead.majorver, savehead.minorver,
+ savehead.bytever, 8*savehead.ptrsize);
+ mgametext(origin.x + (31<<16), origin.y + (104<<16), tempbuf);
+ Bsprintf(tempbuf,"Our: %d.%d.%d %d-bit", SV_MAJOR_VER, SV_MINOR_VER, BYTEVERSION,
+ (int32_t)(8*sizeof(intptr_t)));
+ mgametext(origin.x + ((31+16)<<16), origin.y + (114<<16), tempbuf);
+#endif
+ }
+ }
+ }
+ else
+ mmenutext(origin.x + (82<<16), origin.y + (100<<16), "New");
+
+ if (ud.multimode > 1)
+ {
+ Bsprintf(tempbuf, "Players: %-2d ", ud.multimode);
+ mgametextcenter(origin.x, origin.y + (156<<16), tempbuf);
+ }
+
+ Bsprintf(tempbuf,"%s / %s",g_mapInfo[(ud.volume_number*MAXLEVELS) + ud.level_number].name, g_skillNames[ud.player_skill-1]);
+ mgametextcenter(origin.x, origin.y + (168<<16), tempbuf);
+ if (ud.volume_number == 0 && ud.level_number == 7)
+ mgametextcenter(origin.x, origin.y + (180<<16), currentboardfilename);
+ break;
+ }
+
+#ifdef EDUKE32_ANDROID_MENU
+ case MENU_SKILL:
+ {
+ static const char *s[] = { "EASY - Few enemies, and lots of stuff.", "MEDIUM - Normal difficulty.", "HARD - For experienced players.", "EXPERTS - Lots of enemies, plus they respawn!" };
+ if ((size_t)M_SKILL.currentEntry < ARRAY_SIZE(s))
+ mgametextcenter(origin.x, origin.y + (168<<16), s[M_SKILL.currentEntry]);
+ }
+ break;
+#endif
+
+ case MENU_LOADVERIFY:
+ {
+ fade_screen_black(1);
+ menusave_t & msv = g_menusaves[M_LOAD.currentEntry];
+ if (msv.isOldVer)
+ {
+ Bsprintf(tempbuf, "Start new game:\n%s / %s"
+#ifndef EDUKE32_ANDROID_MENU
+ "\n(Y/N)"
+#endif
+ , g_mapInfo[(ud.volume_number*MAXLEVELS) + ud.level_number].name, g_skillNames[ud.player_skill-1]);
+ mgametextcenter(origin.x, origin.y + (90<<16), tempbuf);
+ }
+ else
+ {
+ Bsprintf(tempbuf, "Load game:\n\"%s\""
+#ifndef EDUKE32_ANDROID_MENU
+ "\n(Y/N)"
+#endif
+ , msv.brief.name);
+ mgametextcenter(origin.x, origin.y + (90<<16), tempbuf);
+ }
+ break;
+ }
+
+ case MENU_SAVEVERIFY:
+ fade_screen_black(1);
+ mgametextcenter(origin.x, origin.y + (90<<16), "Overwrite previous saved game?"
+#ifndef EDUKE32_ANDROID_MENU
+ "\n(Y/N)"
+#endif
+ );
+ break;
+
+ case MENU_NEWVERIFY:
+ fade_screen_black(1);
+ mgametextcenter(origin.x, origin.y + (90<<16), "Abort this game?"
+#ifndef EDUKE32_ANDROID_MENU
+ "\n(Y/N)"
+#endif
+ );
+ break;
+
+ case MENU_QUIT:
+ case MENU_QUIT_INGAME:
+ fade_screen_black(1);
+ mgametextcenter(origin.x, origin.y + (90<<16), "Are you sure you want to quit?"
+#ifndef EDUKE32_ANDROID_MENU
+ "\n(Y/N)"
+#endif
+ );
+ break;
+
+ case MENU_QUITTOTITLE:
+ fade_screen_black(1);
+ mgametextcenter(origin.x, origin.y + (90<<16), "End game and return to title screen?"
+#ifndef EDUKE32_ANDROID_MENU
+ "\n(Y/N)"
+#endif
+ );
+ break;
+
+ case MENU_NETWAITMASTER:
+ G_DrawFrags();
+ mgametextcenter(origin.x, origin.y + (50<<16), "Waiting for master\n"
+ "to select level");
+ break;
+
+ case MENU_NETWAITVOTES:
+ G_DrawFrags();
+ mgametextcenter(origin.x, origin.y + (90<<16), "Waiting for votes");
+ break;
+
+#ifndef EDUKE32_STANDALONE
+ case MENU_BUYDUKE:
+ mgametextcenter(origin.x, origin.y + (33<<16), "You are playing the shareware\n"
+ "version of Duke Nukem 3D. While\n"
+ "this version is really cool, you\n"
+ "are missing over 75% of the total\n"
+ "game, along with other great extras\n"
+ "which you'll get when you order\n"
+ "the complete version and get\n"
+ "the final three episodes.");
+
+ mgametextcenter(origin.x, origin.y + ((148+16)<<16), "Press any key or button...");
+ break;
+#endif
+ case MENU_CREDITS:
+ case MENU_CREDITS2:
+ case MENU_CREDITS3:
+#ifndef EDUKE32_STANDALONE
+ if (!VOLUMEALL || !PLUTOPAK)
+ {
+ switch (cm)
+ {
+ case MENU_CREDITS:
+ m = origin.x + (20<<16);
+ l = origin.y + (33<<16);
+
+ shadowminitext(m, l, "Original Concept", 12); l += 7<<16;
+ shadowminitext(m, l, "Todd Replogle and Allen H. Blum III", 12); l += 7<<16;
+ l += 3<<16;
+ shadowminitext(m, l, "Produced & Directed By", 12); l += 7<<16;
+ shadowminitext(m, l, "Greg Malone", 12); l += 7<<16;
+ l += 3<<16;
+ shadowminitext(m, l, "Executive Producer", 12); l += 7<<16;
+ shadowminitext(m, l, "George Broussard", 12); l += 7<<16;
+ l += 3<<16;
+ shadowminitext(m, l, "BUILD Engine", 12); l += 7<<16;
+ shadowminitext(m, l, "Ken Silverman", 12); l += 7<<16;
+ l += 3<<16;
+ shadowminitext(m, l, "Game Programming", 12); l += 7<<16;
+ shadowminitext(m, l, "Todd Replogle", 12); l += 7<<16;
+ l += 3<<16;
+ shadowminitext(m, l, "3D Engine/Tools/Net", 12); l += 7<<16;
+ shadowminitext(m, l, "Ken Silverman", 12); l += 7<<16;
+ l += 3<<16;
+ shadowminitext(m, l, "Network Layer/Setup Program", 12); l += 7<<16;
+ shadowminitext(m, l, "Mark Dochtermann", 12); l += 7<<16;
+ l += 3<<16;
+ shadowminitext(m, l, "Map Design", 12); l += 7<<16;
+ shadowminitext(m, l, "Allen H. Blum III", 12); l += 7<<16;
+ shadowminitext(m, l, "Richard Gray", 12); l += 7<<16;
+
+ m = origin.x + (180<<16);
+ l = origin.y + (33<<16);
+
+ shadowminitext(m, l, "3D Modeling", 12); l += 7<<16;
+ shadowminitext(m, l, "Chuck Jones", 12); l += 7<<16;
+ shadowminitext(m, l, "Sapphire Corporation", 12); l += 7<<16;
+ l += 3<<16;
+ shadowminitext(m, l, "Artwork", 12); l += 7<<16;
+ shadowminitext(m, l, "Dirk Jones, Stephen Hornback", 12); l += 7<<16;
+ shadowminitext(m, l, "James Storey, David Demaret", 12); l += 7<<16;
+ shadowminitext(m, l, "Douglas R. Wood", 12); l += 7<<16;
+ l += 3<<16;
+ shadowminitext(m, l, "Sound Engine", 12); l += 7<<16;
+ shadowminitext(m, l, "Jim Dose", 12); l += 7<<16;
+ l += 3<<16;
+ shadowminitext(m, l, "Sound & Music Development", 12); l += 7<<16;
+ shadowminitext(m, l, "Robert Prince", 12); l += 7<<16;
+ shadowminitext(m, l, "Lee Jackson", 12); l += 7<<16;
+ l += 3<<16;
+ shadowminitext(m, l, "Voice Talent", 12); l += 7<<16;
+ shadowminitext(m, l, "Lani Minella - Voice Producer", 12); l += 7<<16;
+ shadowminitext(m, l, "Jon St. John as \"Duke Nukem\"", 12); l += 7<<16;
+ l += 3<<16;
+ shadowminitext(m, l, "Graphic Design", 12); l += 7<<16;
+ shadowminitext(m, l, "Packaging, Manual, Ads", 12); l += 7<<16;
+ shadowminitext(m, l, "Robert M. Atkins", 12); l += 7<<16;
+ shadowminitext(m, l, "Michael Hadwin", 12); l += 7<<16;
+ break;
+
+ case MENU_CREDITS2:
+ m = origin.x + (20<<16);
+ l = origin.y + (33<<16);
+
+ shadowminitext(m, l, "Special Thanks To", 12); l += 7<<16;
+ shadowminitext(m, l, "Steven Blackburn, Tom Hall", 12); l += 7<<16;
+ shadowminitext(m, l, "Scott Miller, Joe Siegler", 12); l += 7<<16;
+ shadowminitext(m, l, "Terry Nagy, Colleen Compton", 12); l += 7<<16;
+ shadowminitext(m, l, "HASH, Inc., FormGen, Inc.", 12); l += 7<<16;
+ l += 3<<16;
+ shadowminitext(m, l, "The 3D Realms Beta Testers", 12); l += 7<<16;
+ l += 3<<16;
+ shadowminitext(m, l, "Nathan Anderson, Wayne Benner", 12); l += 7<<16;
+ shadowminitext(m, l, "Glenn Brensinger, Rob Brown", 12); l += 7<<16;
+ shadowminitext(m, l, "Erik Harris, Ken Heckbert", 12); l += 7<<16;
+ shadowminitext(m, l, "Terry Herrin, Greg Hively", 12); l += 7<<16;
+ shadowminitext(m, l, "Hank Leukart, Eric Baker", 12); l += 7<<16;
+ shadowminitext(m, l, "Jeff Rausch, Kelly Rogers", 12); l += 7<<16;
+ shadowminitext(m, l, "Mike Duncan, Doug Howell", 12); l += 7<<16;
+ shadowminitext(m, l, "Bill Blair", 12); l += 7<<16;
+
+ m = origin.x + (160<<16);
+ l = origin.y + (33<<16);
+
+ shadowminitext(m, l, "Company Product Support", 12); l += 7<<16;
+ l += 3<<16;
+ shadowminitext(m, l, "The following companies were cool", 12); l += 7<<16;
+ shadowminitext(m, l, "enough to give us lots of stuff", 12); l += 7<<16;
+ shadowminitext(m, l, "during the making of Duke Nukem 3D.", 12); l += 7<<16;
+ l += 3<<16;
+ shadowminitext(m, l, "Altec Lansing Multimedia", 12); l += 7<<16;
+ shadowminitext(m, l, "for tons of speakers and the", 12); l += 7<<16;
+ shadowminitext(m, l, "THX-licensed sound system.", 12); l += 7<<16;
+ shadowminitext(m, l, "For info call 1-800-548-0620", 12); l += 7<<16;
+ l += 3<<16;
+ shadowminitext(m, l, "Creative Labs, Inc.", 12); l += 7<<16;
+ l += 3<<16;
+ shadowminitext(m, l, "Thanks for the hardware, guys.", 12); l += 7<<16;
+ break;
+
+ case MENU_CREDITS3:
+ mgametextcenter(origin.x, origin.y + (50<<16), "Duke Nukem 3D is a trademark of\n"
+ "3D Realms Entertainment"
+ "\n"
+ "Duke Nukem 3D\n"
+ "(C) 1996 3D Realms Entertainment");
+
+#if !defined(EDUKE32_ANDROID_MENU) && !defined(EDUKE32_STANDALONE)
+ if (VOLUMEONE)
+ {
+ mgametextcenter(origin.x, origin.y + (106<<16), "Please read LICENSE.DOC for shareware\n"
+ "distribution grants and restrictions.");
+ }
+#endif
+ mgametextcenter(origin.x, origin.y + ((VOLUMEONE?134:115)<<16), "Made in Dallas, Texas USA");
+ break;
+ }
+ }
+ break;
+#endif
+ case MENU_CREDITS4: // JBF 20031220
+ l = 7;
+
+ mgametextcenter(origin.x, origin.y + ((50-l)<<16), "Developers");
+ creditsminitext(origin.x + (160<<16), origin.y + ((50+10-l)<<16), "Richard \"TerminX\" Gobeille", 8);
+ creditsminitext(origin.x + (160<<16), origin.y + ((50+7+10-l)<<16), "Evan \"Hendricks266\" Ramos", 8);
+
+ mgametextcenter(origin.x, origin.y + ((80-l)<<16), "Retired developers");
+ creditsminitext(origin.x + (160<<16), origin.y + ((80+10-l)<<16), "Pierre-Loup \"Plagman\" Griffais", 8);
+ creditsminitext(origin.x + (160<<16), origin.y + ((80+7+10-l)<<16), "Philipp \"Helixhorned\" Kutin", 8);
+
+ mgametextcenter(origin.x, origin.y + ((130+7-l)<<16), "Special thanks to");
+ creditsminitext(origin.x + (160<<16), origin.y + ((130+7+10-l)<<16), "Jonathon \"JonoF\" Fowler", 8);
+
+ mgametextcenter(origin.x, origin.y + ((150+7-l)<<16), "Uses BUILD Engine technology by");
+ creditsminitext(origin.x + (160<<16), origin.y + ((150+7+10-l)<<16), "Ken \"Awesoken\" Silverman", 8);
+
+
+ break;
+
+ case MENU_CREDITS5:
+ l = 7;
+
+ mgametextcenter(origin.x, origin.y + ((38-l)<<16), "License and Other Contributors");
+ {
+ const char *header[] =
+ {
+ "This program is distributed under the terms of the",
+ "GNU General Public License version 2 as published by the",
+ "Free Software Foundation. See gpl-2.0.txt for details.",
+ " ",
+ "The EDuke32 team thanks the following people for their contributions:",
+ " ",
+ };
+ const char *body[] =
+ {
+ "Bioman", // GTK work, APT repository and package upkeep
+ "Brandon Bergren", // "Bdragon" - tiles.cfg
+ "Charlie Honig", // "CONAN" - showview command
+ "Dan Gaskill", // "DeeperThought" - testing
+ "David Koenig", // "Bargle" - Merged a couple of things from duke3d_w32
+ "Ed Coolidge", // Mapster32 improvements
+ "Emile Belanger", // original Android work
+ "Hunter_rus", // tons of stuff
+ "James Bentler", // Mapster32 improvements
+ "Jasper Foreman", // netcode contributions
+ "Javier Martinez", // "Malone3D" - EDuke 2.1.1 components
+ "Jeff Hart", // website graphics
+ "Jonathan Strander", // "Mblackwell" - testing and feature speccing
+ "Jose del Castillo", // "Renegado" - EDuke 2.1.1 components
+ "Lachlan McDonald", // official EDuke32 icon
+ "LSDNinja", // OS X help and testing
+ "Marcus Herbert", // "rhoenie" - OS X compatibility work
+ "Matthew Palmer", // "Usurper" - testing and eduke32.com domain
+ "Matt Saettler", // original DOS EDuke/WW2GI enhancements
+ "Ozkan Sezer", // SDL/GTK version checking improvements
+ "Peter Green", // "Plugwash" - dynamic remapping, custom gametypes
+ "Peter Veenstra", // "Qbix" - port to 64-bit
+ "Robin Green", // CON array support
+ "Ryan Gordon", // "icculus" - icculus.org Duke3D port sound code
+ "Stephen Anthony", // early 64-bit porting work
+ "tueidj", // Wii port
+ " ",
+ };
+ const char *footer[] =
+ {
+ " ",
+ "BUILD engine technology available under license. See buildlic.txt.",
+ };
+
+ const int32_t header_numlines = sizeof(header)/sizeof(char *);
+ const int32_t body_numlines = sizeof(body)/sizeof(char *);
+ const int32_t footer_numlines = sizeof(footer)/sizeof(char *);
+
+ i = 0;
+ for (m=0; m<header_numlines; m++)
+ creditsminitext(origin.x + (160<<16), origin.y + ((17+10+10+8+4+(m*7)-l)<<16), header[m], 8);
+ i += m;
+#define CCOLUMNS 3
+#define CCOLXBUF 20
+ for (m=0; m<body_numlines; m++)
+ creditsminitext(origin.x + ((CCOLXBUF+((320-CCOLXBUF*2)/(CCOLUMNS*2)) +((320-CCOLXBUF*2)/CCOLUMNS)*(m/(body_numlines/CCOLUMNS)))<<16), origin.y + ((17+10+10+8+4+((m%(body_numlines/CCOLUMNS))*7)+(i*7)-l)<<16), body[m], 8);
+ i += m/CCOLUMNS;
+ for (m=0; m<footer_numlines; m++)
+ creditsminitext(origin.x + (160<<16), origin.y + ((17+10+10+8+4+(m*7)+(i*7)-l)<<16), footer[m], 8);
+
+ creditsminitext(origin.x + (160<<16), origin.y + ((138+10+10+10+10+4-l)<<16), "Visit www.eduke32.com for news and updates", 8);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+
+static void Menu_PreInput(MenuEntry_t *entry)
+{
+ switch (g_currentMenu)
+ {
+
+ case MENU_KEYBOARDKEYS:
+ if (KB_KeyPressed(sc_Delete))
+ {
+ MenuCustom2Col_t *column = (MenuCustom2Col_t*)entry->entry;
+ char key[2];
+ key[0] = ud.config.KeyboardKeys[M_KEYBOARDKEYS.currentEntry][0];
+ key[1] = ud.config.KeyboardKeys[M_KEYBOARDKEYS.currentEntry][1];
+ *column->column[M_KEYBOARDKEYS.currentColumn] = 0xff;
+ CONFIG_MapKey(M_KEYBOARDKEYS.currentEntry, ud.config.KeyboardKeys[M_KEYBOARDKEYS.currentEntry][0], key[0], ud.config.KeyboardKeys[M_KEYBOARDKEYS.currentEntry][1], key[1]);
+ S_PlaySound(KICK_HIT);
+ KB_ClearKeyDown(sc_Delete);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void Menu_PreOptionListDraw(MenuEntry_t *entry, const vec2_t origin)
+{
+ switch (g_currentMenu)
+ {
+ case MENU_MOUSEBTNS:
+ case MENU_MOUSEADVANCED:
+ case MENU_JOYSTICKBTNS:
+ case MENU_JOYSTICKAXIS:
+ mgametextcenter(origin.x, origin.y + (31<<16), "Select a function to assign");
+
+ Bsprintf(tempbuf, "to %s", entry->name);
+
+ mgametextcenter(origin.x, origin.y + ((31+9)<<16), tempbuf);
+
+ mgametextcenter(origin.x, origin.y + (161<<16), "Press \"Escape\" To Cancel");
+ break;
+ }
+}
+
+static int32_t Menu_PreCustom2ColScreen(MenuEntry_t *entry)
+{
+ if (g_currentMenu == MENU_KEYBOARDKEYS)
+ {
+ MenuCustom2Col_t *column = (MenuCustom2Col_t*)entry->entry;
+
+ int32_t sc = KB_GetLastScanCode();
+ if (sc != sc_None)
+ {
+ char key[2];
+ key[0] = ud.config.KeyboardKeys[M_KEYBOARDKEYS.currentEntry][0];
+ key[1] = ud.config.KeyboardKeys[M_KEYBOARDKEYS.currentEntry][1];
+
+ S_PlaySound(PISTOL_BODYHIT);
+
+ *column->column[M_KEYBOARDKEYS.currentColumn] = KB_GetLastScanCode();
+
+ CONFIG_MapKey(M_KEYBOARDKEYS.currentEntry, ud.config.KeyboardKeys[M_KEYBOARDKEYS.currentEntry][0], key[0], ud.config.KeyboardKeys[M_KEYBOARDKEYS.currentEntry][1], key[1]);
+
+ KB_ClearKeyDown(sc);
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void Menu_PreCustom2ColScreenDraw(MenuEntry_t *entry, const vec2_t origin)
+{
+ if (g_currentMenu == MENU_KEYBOARDKEYS)
+ {
+ Bsprintf(tempbuf, "Press the key to assign as\n"
+ "%s for \"%s\"\n"
+ "\n"
+ "Press \"Escape\" To Cancel"
+ , M_KEYBOARDKEYS.currentColumn?"secondary":"primary", entry->name);
+ mgametextcenter(origin.x, origin.y + (90<<16), tempbuf);
+ }
+}
+
+static void Menu_EntryFocus(/*MenuEntry_t *entry*/)
+{
+ switch (g_currentMenu)
+ {
+ case MENU_LOAD:
+ if (M_LOAD.currentEntry < (int32_t)g_nummenusaves)
+ {
+ savebrief_t & sv = g_menusaves[M_LOAD.currentEntry].brief;
+ if (sv.isValid())
+ G_LoadSaveHeaderNew(sv.path, &savehead);
+ }
+ break;
+
+ case MENU_SAVE:
+ if (0 < M_SAVE.currentEntry && M_SAVE.currentEntry <= (int32_t)g_nummenusaves)
+ {
+ savebrief_t & sv = g_menusaves[M_SAVE.currentEntry-1].brief;
+ if (sv.isValid())
+ G_LoadSaveHeaderNew(sv.path, &savehead);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void Menu_StartGameWithoutSkill(void)
+{
+ ud.m_player_skill = M_SKILL.currentEntry+1;
+
+ g_skillSoundVoice = S_PlaySound(PISTOL_BODYHIT);
+
+ ud.m_respawn_monsters = 0;
+
+ ud.m_monsters_off = ud.monsters_off = 0;
+
+ ud.m_respawn_items = 0;
+ ud.m_respawn_inventory = 0;
+
+ ud.multimode = 1;
+
+ G_NewGame_EnterLevel();
+}
+
+static void Menu_DoCheat(int32_t cheatID)
+{
+ if (numplayers != 1 || !(g_player[myconnectindex].ps->gm & MODE_GAME))
+ return;
+
+ osdcmd_cheatsinfo_stat.cheatnum = cheatID;
+}
+
+static int32_t Menu_Cheat_Warp(char const * const numbers)
+{
+ if (numplayers != 1 || !(g_player[myconnectindex].ps->gm & MODE_GAME))
+ return 0;
+
+ if (numbers == NULL || !numbers[0] || !numbers[1] || (VOLUMEALL && !numbers[2]))
+ return 1;
+
+ if (VOLUMEALL)
+ {
+ osdcmd_cheatsinfo_stat.volume = numbers[0] - '0';
+ osdcmd_cheatsinfo_stat.level = (numbers[1] - '0')*10+(numbers[2]-'0');
+ }
+ else
+ {
+ osdcmd_cheatsinfo_stat.volume = numbers[0] - '0';
+ osdcmd_cheatsinfo_stat.level = numbers[1] - '0';
+ }
+
+ osdcmd_cheatsinfo_stat.volume--;
+ osdcmd_cheatsinfo_stat.level--;
+
+ if ((VOLUMEONE && osdcmd_cheatsinfo_stat.volume > 0) || osdcmd_cheatsinfo_stat.volume > g_volumeCnt-1 ||
+ osdcmd_cheatsinfo_stat.level >= MAXLEVELS || g_mapInfo[osdcmd_cheatsinfo_stat.volume *MAXLEVELS+osdcmd_cheatsinfo_stat.level].filename == NULL)
+ return 1;
+
+ osdcmd_cheatsinfo_stat.cheatnum = CHEAT_SCOTTY;
+
+ return 0;
+}
+
+static int32_t Menu_Cheat_Skill(char const * const number)
+{
+ if (numplayers != 1 || !(g_player[myconnectindex].ps->gm & MODE_GAME))
+ return 0;
+
+ if (number == NULL || !number[0])
+ return 1;
+
+ osdcmd_cheatsinfo_stat.volume = number[0] - '1';
+
+ osdcmd_cheatsinfo_stat.cheatnum = CHEAT_SKILL;
+
+ return 0;
+}
+
+/*
+Functions where a "newValue" or similar is passed are run *before* the linked variable is actually changed.
+That way you can compare the new and old values and potentially block the change.
+*/
+static void Menu_EntryLinkActivate(MenuEntry_t *entry)
+{
+ switch (g_currentMenu)
+ {
+ case MENU_EPISODE:
+ if (entry != &ME_EPISODE_USERMAP)
+ {
+ ud.m_volume_number = M_EPISODE.currentEntry;
+ ud.m_level_number = 0;
+
+ if (g_skillCnt == 0)
+ Menu_StartGameWithoutSkill();
+ }
+ break;
+
+ case MENU_SKILL:
+ {
+ int32_t skillsound = PISTOL_BODYHIT;
+
+ switch (M_SKILL.currentEntry)
+ {
+ case 0:
+ skillsound = JIBBED_ACTOR6;
+ break;
+ case 1:
+ skillsound = BONUS_SPEECH1;
+ break;
+ case 2:
+ skillsound = DUKE_GETWEAPON2;
+ break;
+ case 3:
+ skillsound = JIBBED_ACTOR5;
+ break;
+ }
+
+ ud.m_player_skill = M_SKILL.currentEntry+1;
+
+ g_skillSoundVoice = S_PlaySound(skillsound);
+
+ if (M_SKILL.currentEntry == 3) ud.m_respawn_monsters = 1;
+ else ud.m_respawn_monsters = 0;
+
+ ud.m_monsters_off = ud.monsters_off = 0;
+
+ ud.m_respawn_items = 0;
+ ud.m_respawn_inventory = 0;
+
+ ud.multimode = 1;
+
+ G_NewGame_EnterLevel();
+ break;
+ }
+
+ case MENU_JOYSTICKAXES:
+ M_JOYSTICKAXIS.title = getjoyname(0, M_JOYSTICKAXES.currentEntry);
+ MEO_JOYSTICKAXIS_ANALOG.data = &ud.config.JoystickAnalogueAxes[M_JOYSTICKAXES.currentEntry];
+ MEO_JOYSTICKAXIS_SCALE.variable = &ud.config.JoystickAnalogueScale[M_JOYSTICKAXES.currentEntry];
+ MEO_JOYSTICKAXIS_DEAD.variable = &ud.config.JoystickAnalogueDead[M_JOYSTICKAXES.currentEntry];
+ MEO_JOYSTICKAXIS_SATU.variable = &ud.config.JoystickAnalogueSaturate[M_JOYSTICKAXES.currentEntry];
+ MEO_JOYSTICKAXIS_DIGITALNEGATIVE.data = &ud.config.JoystickDigitalFunctions[M_JOYSTICKAXES.currentEntry][0];
+ MEO_JOYSTICKAXIS_DIGITALPOSITIVE.data = &ud.config.JoystickDigitalFunctions[M_JOYSTICKAXES.currentEntry][1];
+ break;
+
+ case MENU_CHEATS:
+ {
+ const int32_t cheatFuncID = M_CHEATS.currentEntry - 1;
+ switch (cheatFuncID)
+ {
+ case -1:
+ case CHEATFUNC_WARP:
+ case CHEATFUNC_SKILL:
+ break;
+ default:
+ Menu_DoCheat(CheatFunctionIDs[cheatFuncID]);
+ break;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (entry == &ME_VIDEOSETUP_APPLY)
+ {
+ resolution_t p = { xdim, ydim, fullscreen, bpp, 0 };
+ int32_t prend = getrendermode();
+ int32_t pvsync = vsync;
+
+ resolution_t n = { resolution[newresolution].xdim, resolution[newresolution].ydim, newfullscreen,
+ (newrendermode == REND_CLASSIC) ? 8 : resolution[newresolution].bppmax, 0 };
+ int32_t nrend = newrendermode;
+ int32_t nvsync = newvsync;
+
+ if (setgamemode(n.flags, n.xdim, n.ydim, n.bppmax) < 0)
+ {
+ if (setgamemode(p.flags, p.xdim, p.ydim, p.bppmax) < 0)
+ {
+ setrendermode(prend);
+ G_GameExit("Failed restoring old video mode.");
+ }
+ else
+ {
+ onvideomodechange(p.bppmax > 8);
+ vsync = setvsync(pvsync);
+ }
+ }
+ else onvideomodechange(n.bppmax > 8);
+
+ g_restorePalette = -1;
+ G_UpdateScreenArea();
+ setrendermode(nrend);
+ vsync = setvsync(nvsync);
+ ud.config.ScreenMode = fullscreen;
+ ud.config.ScreenWidth = xdim;
+ ud.config.ScreenHeight = ydim;
+ ud.config.ScreenBPP = bpp;
+ }
+ else if (entry == &ME_SOUND_RESTART)
+ {
+ ud.config.MixRate = soundrate;
+ ud.config.NumVoices = soundvoices;
+ ud.config.FluidsynthBackend = musicbackend;
+
+ S_SoundShutdown();
+ S_MusicShutdown();
+
+ S_MusicStartup();
+ S_SoundStartup();
+
+ FX_StopAllSounds();
+ S_ClearSoundLocks();
+
+ if (ud.config.MusicToggle)
+ S_RestartMusic();
+ }
+ else if (entry == &ME_COLCORR_RESET)
+ {
+ vid_gamma = DEFAULT_GAMMA;
+ vid_contrast = DEFAULT_CONTRAST;
+ vid_brightness = DEFAULT_BRIGHTNESS;
+ ud.brightness = 0;
+ r_ambientlight = r_ambientlightrecip = 1.f;
+ setbrightness(ud.brightness>>2,g_player[myconnectindex].ps->palette,0);
+ }
+ else if (entry == &ME_KEYBOARDSETUP_RESET)
+ CONFIG_SetDefaultKeys(keydefaults);
+ else if (entry == &ME_KEYBOARDSETUP_RESETCLASSIC)
+ CONFIG_SetDefaultKeys(oldkeydefaults);
+ else if (entry == &ME_NETHOST_LAUNCH)
+ {
+ // master does whatever it wants
+ if (g_netServer)
+ {
+ Net_FillNewGame(&pendingnewgame, 1);
+ Net_StartNewGame();
+ Net_SendNewGame(1, NULL);
+ }
+ else if (voting == -1)
+ {
+ Net_SendMapVoteInitiate();
+ Menu_Change(MENU_NETWAITVOTES);
+ }
+ }
+}
+
+static int32_t Menu_EntryOptionModify(MenuEntry_t *entry, int32_t newOption)
+{
+ int32_t x;
+ DukePlayer_t *ps = g_player[myconnectindex].ps;
+
+ if (entry == &ME_GAMESETUP_DEMOREC)
+ {
+ if ((ps->gm&MODE_GAME))
+ G_CloseDemoWrite();
+ }
+#ifdef _WIN32
+ else if (entry == &ME_GAMESETUP_UPDATES)
+ ud.config.LastUpdateCheck = 0;
+#endif
+ else if (entry == &ME_GAMESETUP_WEAPSWITCH_PICKUP)
+ {
+ ud.weaponswitch &= ~(1|4);
+ switch (newOption)
+ {
+ case 2:
+ ud.weaponswitch |= 4;
+ fallthrough__;
+ case 1:
+ ud.weaponswitch |= 1;
+ break;
+ default:
+ break;
+ }
+ }
+ else if (entry == &ME_SOUND)
+ {
+ if (newOption == 0)
+ {
+ FX_StopAllSounds();
+ S_ClearSoundLocks();
+ }
+ }
+ else if (entry == &ME_SOUND_MUSIC)
+ {
+ ud.config.MusicToggle = newOption;
+
+ if (newOption == 0)
+ S_PauseMusic(1);
+ else
+ {
+ S_RestartMusic();
+ S_PauseMusic(0);
+ }
+ }
+ else if (entry == &ME_SOUND_DUKETALK)
+ ud.config.VoiceToggle = (ud.config.VoiceToggle&~1) | newOption;
+ else if (entry == &ME_MOUSESETUP_SMOOTH)
+ CONTROL_SmoothMouse = ud.config.SmoothInput;
+ else if (entry == &ME_JOYSTICKAXIS_ANALOG)
+ CONTROL_MapAnalogAxis(M_JOYSTICKAXES.currentEntry, newOption, controldevice_joystick);
+ else if (entry == &ME_NETOPTIONS_EPISODE)
+ {
+ if (newOption < g_volumeCnt)
+ ud.m_volume_number = newOption;
+ }
+ else if (entry == &ME_NETOPTIONS_MONSTERS)
+ {
+ ud.m_monsters_off = (newOption == g_skillCnt);
+ if (newOption < g_skillCnt)
+ ud.m_player_skill = newOption;
+ }
+ else if (entry == &ME_ADULTMODE)
+ {
+ if (newOption)
+ {
+ for (x=0; x<g_animWallCnt; x++)
+ switch (DYNAMICTILEMAP(wall[animwall[x].wallnum].picnum))
+ {
+ case FEMPIC1__STATIC:
+ wall[animwall[x].wallnum].picnum = BLANKSCREEN;
+ break;
+ case FEMPIC2__STATIC:
+ case FEMPIC3__STATIC:
+ wall[animwall[x].wallnum].picnum = SCREENBREAK6;
+ break;
+ }
+
+ ud.pwlockout[0] = 0;
+ Menu_Change(MENU_ADULTPASSWORD);
+// return -1;
+ }
+ else
+ {
+ if (ud.pwlockout[0] == 0)
+ {
+ ud.lockout = 0;
+#if 0
+ for (x=0; x<g_numAnimWalls; x++)
+ if (wall[animwall[x].wallnum].picnum != W_SCREENBREAK &&
+ wall[animwall[x].wallnum].picnum != W_SCREENBREAK+1 &&
+ wall[animwall[x].wallnum].picnum != W_SCREENBREAK+2)
+ if (wall[animwall[x].wallnum].extra >= 0)
+ wall[animwall[x].wallnum].picnum = wall[animwall[x].wallnum].extra;
+#endif
+ }
+ else
+ {
+ Menu_Change(MENU_ADULTPASSWORD);
+ return -1;
+ }
+ }
+ }
+ else if (entry == &ME_VIDEOSETUP_FRAMELIMIT)
+ {
+ g_frameDelay = newOption ? (getu64tickspersec()/newOption) : 0;
+ }
+
+ switch (g_currentMenu)
+ {
+ case MENU_MOUSEBTNS:
+ CONTROL_MapButton(newOption, MenuMouseDataIndex[M_MOUSEBTNS.currentEntry][0], MenuMouseDataIndex[M_MOUSEBTNS.currentEntry][1], controldevice_mouse);
+ CONTROL_FreeMouseBind(MenuMouseDataIndex[M_MOUSEBTNS.currentEntry][0]);
+ break;
+ case MENU_MOUSEADVANCED:
+ {
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(MEL_INTERNAL_MOUSEADVANCED_DAXES); i++)
+ if (entry == MEL_INTERNAL_MOUSEADVANCED_DAXES[i])
+ CONTROL_MapDigitalAxis(i>>1, newOption, i&1, controldevice_mouse);
+ }
+ break;
+ case MENU_JOYSTICKBTNS:
+ CONTROL_MapButton(newOption, M_JOYSTICKBTNS.currentEntry>>1, M_JOYSTICKBTNS.currentEntry&1, controldevice_joystick);
+ break;
+ case MENU_JOYSTICKAXIS:
+ {
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(MEL_INTERNAL_JOYSTICKAXIS_DIGITAL); i++)
+ if (entry == MEL_INTERNAL_JOYSTICKAXIS_DIGITAL[i])
+ CONTROL_MapDigitalAxis(i>>1, newOption, i&1, controldevice_joystick);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static void Menu_EntryOptionDidModify(MenuEntry_t *entry)
+{
+#ifdef USE_OPENGL
+ int domodechange = 0;
+#endif
+
+ if (entry == &ME_GAMESETUP_AIM_AUTO ||
+ entry == &ME_GAMESETUP_WEAPSWITCH_PICKUP ||
+ entry == &ME_PLAYER_NAME ||
+ entry == &ME_PLAYER_COLOR ||
+ entry == &ME_PLAYER_TEAM)
+ G_UpdatePlayerFromMenu();
+#ifdef USE_OPENGL
+ else if (entry == &ME_DISPLAYSETUP_ANISOTROPY || entry == &ME_DISPLAYSETUP_TEXFILTER)
+ gltexapplyprops();
+ else if (entry == &ME_RENDERERSETUP_TEXQUALITY)
+ {
+ texcache_invalidate();
+ r_downsizevar = r_downsize;
+ domodechange = 1;
+ }
+#ifdef POLYMER
+ else if (entry == &ME_POLYMER_LIGHTS ||
+ entry == &ME_POLYMER_LIGHTPASSES ||
+ entry == &ME_POLYMER_SHADOWCOUNT)
+ domodechange = 1;
+#endif
+
+ if (domodechange)
+ {
+ resetvideomode();
+ if (setgamemode(fullscreen, xdim, ydim, bpp))
+ OSD_Printf("restartvid: Reset failed...\n");
+ onvideomodechange(ud.config.ScreenBPP>8);
+ G_RefreshLights();
+ }
+#endif
+}
+
+static void Menu_Custom2ColScreen(/*MenuEntry_t *entry*/)
+{
+ if (g_currentMenu == MENU_KEYBOARDKEYS)
+ {
+ KB_FlushKeyboardQueue();
+ KB_ClearLastScanCode();
+ }
+}
+
+static int32_t Menu_EntryRangeInt32Modify(MenuEntry_t *entry, int32_t newValue)
+{
+ if (entry == &ME_SCREENSETUP_SCREENSIZE)
+ G_SetViewportShrink(newValue - vpsize);
+ else if (entry == &ME_SCREENSETUP_SBARSIZE)
+ G_SetStatusBarScale(newValue);
+ else if (entry == &ME_SOUND_VOLUME_MASTER)
+ {
+ FX_SetVolume(newValue);
+ S_MusicVolume(MASTER_VOLUME(ud.config.MusicVolume));
+ }
+ else if (entry == &ME_SOUND_VOLUME_MUSIC)
+ S_MusicVolume(MASTER_VOLUME(newValue));
+ else if (entry == &ME_MOUSEADVANCED_SCALEX)
+ CONTROL_SetAnalogAxisScale(0, newValue, controldevice_mouse);
+ else if (entry == &ME_MOUSEADVANCED_SCALEY)
+ CONTROL_SetAnalogAxisScale(1, newValue, controldevice_mouse);
+ else if (entry == &ME_JOYSTICKAXIS_SCALE)
+ CONTROL_SetAnalogAxisScale(M_JOYSTICKAXES.currentEntry, newValue, controldevice_joystick);
+ else if (entry == &ME_JOYSTICKAXIS_DEAD)
+ setjoydeadzone(M_JOYSTICKAXES.currentEntry, newValue, *MEO_JOYSTICKAXIS_SATU.variable);
+ else if (entry == &ME_JOYSTICKAXIS_SATU)
+ setjoydeadzone(M_JOYSTICKAXES.currentEntry, *MEO_JOYSTICKAXIS_DEAD.variable, newValue);
+
+ return 0;
+}
+
+static int32_t Menu_EntryRangeFloatModify(MenuEntry_t *entry, float newValue)
+{
+#ifndef EDUKE32_SIMPLE_MENU
+ if (entry == &ME_COLCORR_AMBIENT)
+ r_ambientlightrecip = 1.f/newValue;
+#else
+ UNREFERENCED_PARAMETER(entry);
+ UNREFERENCED_PARAMETER(newValue);
+#endif
+
+ return 0;
+}
+
+static int32_t Menu_EntryRangeFloatDidModify(MenuEntry_t *entry)
+{
+ if (entry == &ME_COLCORR_GAMMA)
+ {
+ ud.brightness = GAMMA_CALC<<2;
+ setbrightness(ud.brightness>>2, g_player[myconnectindex].ps->palette, 0);
+ }
+ else if (entry == &ME_COLCORR_CONTRAST || entry == &ME_COLCORR_BRIGHTNESS)
+ {
+ setbrightness(ud.brightness>>2, g_player[myconnectindex].ps->palette, 0);
+ }
+
+ return 0;
+}
+
+#ifdef MENU_ENABLE_RANGEDOUBLE
+static int32_t Menu_EntryRangeDoubleModify(void /*MenuEntry_t *entry, double newValue*/)
+{
+
+ return 0;
+}
+#endif
+
+static uint32_t save_xxh = 0;
+
+static void Menu_EntryStringActivate(/*MenuEntry_t *entry*/)
+{
+ switch (g_currentMenu)
+ {
+ case MENU_SAVE:
+ if (M_SAVE.currentEntry > 0)
+ {
+ savebrief_t & sv = g_menusaves[M_SAVE.currentEntry-1].brief;
+ if (!save_xxh)
+ save_xxh = XXH32((uint8_t *)sv.name, MAXSAVEGAMENAME, 0xDEADBEEF);
+ if (sv.isValid())
+ Menu_Change(MENU_SAVEVERIFY);
+ }
+ else
+ {
+ ME_SAVE_NEW.name = nullptr;
+ save_xxh = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int32_t Menu_EntryStringSubmit(/*MenuEntry_t *entry, */char *input)
+{
+ int32_t returnvar = 0;
+
+ switch (g_currentMenu)
+ {
+ case MENU_SAVE:
+ {
+ savebrief_t & sv = g_lastusersave = M_SAVE.currentEntry == 0 ? savebrief_t{input} : g_menusaves[M_SAVE.currentEntry-1].brief;
+
+ // dirty hack... char 127 in last position indicates an auto-filled name
+#ifdef __ANDROID__
+ if (1)
+#else
+ if (input[0] == 0 || (sv.name[MAXSAVEGAMENAME] == 127 &&
+ strncmp(sv.name, input, MAXSAVEGAMENAME) == 0 &&
+ save_xxh == XXH32((uint8_t *)sv.name, MAXSAVEGAMENAME, 0xDEADBEEF)))
+#endif
+ {
+ strncpy(sv.name, g_mapInfo[ud.volume_number * MAXLEVELS + ud.level_number].name, MAXSAVEGAMENAME);
+ sv.name[MAXSAVEGAMENAME] = 127;
+ returnvar = -1;
+ }
+ else
+ {
+ strncpy(sv.name, input, MAXSAVEGAMENAME);
+ sv.name[MAXSAVEGAMENAME] = 0;
+ }
+
+ G_SavePlayerMaybeMulti(sv);
+
+ g_quickload = &sv;
+ g_player[myconnectindex].ps->gm = MODE_GAME;
+
+ Menu_Change(MENU_CLOSE);
+ save_xxh = 0;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return returnvar;
+}
+
+static void Menu_EntryStringCancel(/*MenuEntry_t *entry*/)
+{
+ switch (g_currentMenu)
+ {
+ case MENU_SAVE:
+ save_xxh = 0;
+ ME_SAVE_NEW.name = s_NewSaveGame;
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+This is polled when the menu code is populating the screen but for some reason doesn't have the data.
+*/
+static int32_t Menu_EntryOptionSource(MenuEntry_t *entry, int32_t currentValue)
+{
+ if (entry == &ME_GAMESETUP_WEAPSWITCH_PICKUP)
+ return (ud.weaponswitch & 1) ? ((ud.weaponswitch & 4) ? 2 : 1) : 0;
+ else if (entry == &ME_SOUND_DUKETALK)
+ return ud.config.VoiceToggle & 1;
+ else if (entry == &ME_NETOPTIONS_MONSTERS)
+ return (ud.m_monsters_off ? g_skillCnt : ud.m_player_skill);
+
+ return currentValue;
+}
+
+static void Menu_Verify(int32_t input)
+{
+ switch (g_currentMenu)
+ {
+ case MENU_RESETPLAYER:
+ if (input)
+ {
+ KB_FlushKeyboardQueue();
+ KB_ClearKeysDown();
+ FX_StopAllSounds();
+ S_ClearSoundLocks();
+
+ G_LoadPlayerMaybeMulti(*g_quickload);
+ }
+ else
+ {
+ if (sprite[g_player[myconnectindex].ps->i].extra <= 0)
+ {
+ if (G_EnterLevel(MODE_GAME)) G_BackToMenu();
+ return;
+ }
+
+ Menu_Change(MENU_CLOSE);
+ }
+ break;
+
+ case MENU_LOADVERIFY:
+ if (input)
+ {
+ savebrief_t & sv = g_menusaves[M_LOAD.currentEntry].brief;
+
+ if (strcmp(sv.path, g_lastusersave.path) != 0)
+ {
+ g_freshload = sv;
+ g_lastusersave.reset();
+ g_lastautosave.reset();
+ g_quickload = &g_freshload;
+ }
+ else
+ {
+ g_quickload = &g_lastusersave;
+ }
+
+ KB_FlushKeyboardQueue();
+ KB_ClearKeysDown();
+
+ Menu_Change(MENU_CLOSE);
+
+ G_LoadPlayerMaybeMulti(sv);
+ }
+ break;
+
+ case MENU_SAVEVERIFY:
+ if (!input)
+ {
+ save_xxh = 0;
+
+ ((MenuString_t*)M_SAVE.entrylist[M_SAVE.currentEntry]->entry)->editfield = NULL;
+ }
+ break;
+
+ case MENU_QUIT:
+ case MENU_QUIT_INGAME:
+ if (input)
+ G_GameQuit();
+ else
+ g_quitDeadline = 0;
+ break;
+
+ case MENU_QUITTOTITLE:
+ if (input)
+ {
+ g_player[myconnectindex].ps->gm = MODE_DEMO;
+ if (ud.recstat == 1)
+ G_CloseDemoWrite();
+ E_MapArt_Clear();
+ }
+ break;
+
+ case MENU_NETWAITVOTES:
+ if (!input)
+ Net_SendMapVoteCancel(0);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int Menu_CheatStringMatch(char const * input, char const * cheat)
+{
+ while (*cheat || *input)
+ {
+ if (*cheat != *input)
+ {
+ if (!(*cheat == '#' && Bisdigit(*input)))
+ return 0;
+ }
+
+ ++cheat;
+ ++input;
+ }
+
+ return 1;
+}
+
+static void Menu_TextFormSubmit(char *input)
+{
+ switch (g_currentMenu)
+ {
+ case MENU_ADULTPASSWORD:
+ if (Bstrlen(input) && (ud.pwlockout[0] == 0 || ud.lockout == 0))
+ Bstrcpy(&ud.pwlockout[0], input);
+ else if (Bstrcmp(input, &ud.pwlockout[0]) == 0)
+ {
+ for (bssize_t x=0; x<g_animWallCnt; x++)
+ if ((unsigned) animwall[x].wallnum < (unsigned)numwalls && wall[animwall[x].wallnum].picnum != W_SCREENBREAK &&
+ wall[animwall[x].wallnum].picnum != W_SCREENBREAK+1 &&
+ wall[animwall[x].wallnum].picnum != W_SCREENBREAK+2)
+ if (wall[animwall[x].wallnum].extra >= 0)
+ wall[animwall[x].wallnum].picnum = wall[animwall[x].wallnum].extra;
+ ud.lockout = 0;
+ }
+
+ S_PlaySound(PISTOL_BODYHIT);
+ Menu_Change(MENU_GAMESETUP);
+ break;
+
+ case MENU_CHEATENTRY:
+ {
+ const size_t inputlength = Bstrlen(input);
+ Bstrcpy(tempbuf, input);
+ for (size_t i = 0; i < inputlength; i++)
+ tempbuf[i] = Btolower(tempbuf[i]);
+
+ int8_t cheatID = -1;
+
+ if (inputlength > 2 && tempbuf[0] == scantoasc[CheatKeys[0]] && tempbuf[1] == scantoasc[CheatKeys[1]])
+ {
+ for (size_t i = 0; i < NUMCHEATS; i++)
+ if (Menu_CheatStringMatch(tempbuf+2, CheatStrings[i]))
+ {
+ cheatID = i;
+ break;
+ }
+ }
+
+ switch (cheatID)
+ {
+ case -1:
+ S_PlaySound(KICK_HIT);
+ break;
+ case CHEAT_SCOTTY:
+ {
+ char const * const numberpos = Bstrchr(CheatStrings[CHEAT_SCOTTY], '#');
+ if (numberpos == NULL)
+ {
+ S_PlaySound(KICK_HIT);
+ break;
+ }
+
+ Menu_Cheat_Warp(input + (numberpos - CheatStrings[CHEAT_SCOTTY]) + 2);
+ if (g_player[myconnectindex].ps->gm&MODE_MENU)
+ S_PlaySound(DUKE_GET);
+ break;
+ }
+ case CHEAT_SKILL:
+ {
+ char const * const numberpos = Bstrchr(CheatStrings[CHEAT_SKILL], '#');
+ if (numberpos == NULL)
+ {
+ S_PlaySound(KICK_HIT);
+ break;
+ }
+
+ Menu_Cheat_Skill(input + (numberpos - CheatStrings[CHEAT_SKILL]) + 2);
+ if (g_player[myconnectindex].ps->gm&MODE_MENU)
+ S_PlaySound(DUKE_GET);
+ break;
+ }
+ default:
+ Menu_DoCheat(cheatID);
+ S_PlaySound(DUKE_GET);
+ break;
+ }
+
+ if (cheatID >= 0)
+ cl_cheatmask |= CheatFunctionFlags[cheatID];
+
+ if ((NAM_WW2GI && (cl_cheatmask & (1<<CHEATFUNC_QUOTETODD))) ||
+ ((cl_cheatmask & (1<<CHEATFUNC_QUOTEBETA)) && (cl_cheatmask & (1<<CHEATFUNC_QUOTETODD)) && (cl_cheatmask & (1<<CHEATFUNC_QUOTEALLEN))))
+ {
+ S_PlaySound(DUKE_GETWEAPON6);
+ cl_cheatmask = ~0;
+ }
+
+ Menu_Change(MENU_CHEATS);
+ break;
+ }
+
+ case MENU_CHEAT_WARP:
+ if (Menu_Cheat_Warp(input))
+ S_PlaySound(KICK_HIT);
+ Menu_Change(MENU_CHEATS);
+ break;
+
+ case MENU_CHEAT_SKILL:
+ if (Menu_Cheat_Skill(input))
+ S_PlaySound(KICK_HIT);
+ Menu_Change(MENU_CHEATS);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void klistbookends(CACHE1D_FIND_REC *start)
+{
+ CACHE1D_FIND_REC *end = start, *n;
+ size_t i = 0;
+
+ if (!start)
+ return;
+
+ while (start->prev)
+ start = start->prev;
+
+ while (end->next)
+ end = end->next;
+
+ for (n = start; n; n = n->next)
+ {
+ n->type = i; // overload this...
+ n->usera = start;
+ n->userb = end;
+ i++;
+ }
+}
+
+static void Menu_FileSelectInit(MenuFileSelect_t *object)
+{
+ size_t i;
+
+ fnlist_clearnames(&object->fnlist);
+
+ if (object->destination[0] == 0)
+ Bstrcpy(object->destination, "./");
+ Bcorrectfilename(object->destination, 1);
+
+ fnlist_getnames(&object->fnlist, object->destination, object->pattern, 0, 0);
+ object->findhigh[0] = object->fnlist.finddirs;
+ object->findhigh[1] = object->fnlist.findfiles;
+
+ for (i = 0; i < 2; ++i)
+ {
+ object->scrollPos[i] = 0;
+ klistbookends(object->findhigh[i]);
+ }
+
+ object->currentList = 0;
+ if (object->findhigh[1])
+ object->currentList = 1;
+
+ KB_FlushKeyboardQueue();
+}
+
+static void Menu_FileSelect(int32_t input)
+{
+ switch (g_currentMenu)
+ {
+ case MENU_NETUSERMAP:
+ if ((g_netServer || ud.multimode > 1))
+ Net_SendUserMapName();
+ fallthrough__;
+ case MENU_USERMAP:
+ if (input)
+ {
+ ud.m_volume_number = 0;
+ ud.m_level_number = 7;
+
+ if (g_skillCnt > 0)
+ Menu_AnimateChange(MENU_SKILL, MA_Advance);
+ else
+ Menu_StartGameWithoutSkill();
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+
+
+
+static Menu_t* Menu_BinarySearch(MenuID_t query, size_t searchstart, size_t searchend)
+{
+ const size_t thissearch = (searchstart + searchend) / 2;
+ const MenuID_t difference = query - Menus[thissearch].menuID;
+
+ if (difference == 0)
+ return &Menus[thissearch];
+ else if (searchstart == searchend)
+ return NULL;
+ else if (difference > 0)
+ {
+ if (thissearch == searchend)
+ return NULL;
+ searchstart = thissearch + 1;
+ }
+ else if (difference < 0)
+ {
+ if (thissearch == searchstart)
+ return NULL;
+ searchend = thissearch - 1;
+ }
+
+ return Menu_BinarySearch(query, searchstart, searchend);
+}
+
+static Menu_t* Menu_Find(MenuID_t query)
+{
+ if ((unsigned) query > (unsigned) Menus[numMenus-1].menuID)
+ return NULL;
+
+ return Menu_BinarySearch(query, 0, numMenus-1);
+}
+
+static Menu_t* Menu_FindFiltered(MenuID_t query)
+{
+ if ((g_player[myconnectindex].ps->gm&MODE_GAME) && query == MENU_MAIN)
+ query = MENU_MAIN_INGAME;
+
+ return Menu_Find(query);
+}
+
+MenuAnimation_t m_animation;
+
+int32_t Menu_Anim_SinOutRight(MenuAnimation_t *animdata)
+{
+ return sintable[divscale10(totalclock - animdata->start, animdata->length) + 512] - 16384;
+}
+int32_t Menu_Anim_SinInRight(MenuAnimation_t *animdata)
+{
+ return sintable[divscale10(totalclock - animdata->start, animdata->length) + 512] + 16384;
+}
+int32_t Menu_Anim_SinOutLeft(MenuAnimation_t *animdata)
+{
+ return -sintable[divscale10(totalclock - animdata->start, animdata->length) + 512] + 16384;
+}
+int32_t Menu_Anim_SinInLeft(MenuAnimation_t *animdata)
+{
+ return -sintable[divscale10(totalclock - animdata->start, animdata->length) + 512] - 16384;
+}
+
+void Menu_AnimateChange(int32_t cm, MenuAnimationType_t animtype)
+{
+ if (KXDWN)
+ {
+ m_animation.start = 0;
+ m_animation.length = 0;
+ Menu_Change(cm);
+ return;
+ }
+
+ switch (animtype)
+ {
+ case MA_Advance:
+ {
+ Menu_t * const previousMenu = m_currentMenu;
+
+ if (!Menu_Change(cm))
+ {
+ m_animation.out = Menu_Anim_SinOutRight;
+ m_animation.in = Menu_Anim_SinInRight;
+ m_animation.start = totalclock;
+ m_animation.length = 30;
+
+ m_animation.previous = previousMenu;
+ m_animation.current = m_currentMenu;
+ }
+
+ break;
+ }
+ case MA_Return:
+ {
+ Menu_t * const previousMenu = m_currentMenu;
+
+ if (!Menu_Change(cm))
+ {
+ m_animation.out = Menu_Anim_SinOutLeft;
+ m_animation.in = Menu_Anim_SinInLeft;
+ m_animation.start = totalclock;
+ m_animation.length = 30;
+
+ m_animation.previous = previousMenu;
+ m_animation.current = m_currentMenu;
+ }
+
+ break;
+ }
+ default:
+ m_animation.start = 0;
+ m_animation.length = 0;
+ Menu_Change(cm);
+ break;
+ }
+}
+
+static void Menu_MaybeSetSelectionToChild(Menu_t * m, MenuID_t id)
+{
+ if (m->type == Menu)
+ {
+ MenuMenu_t * menu = (MenuMenu_t *)m->object;
+
+ for (size_t i = 0, i_end = menu->numEntries; i < i_end; ++i)
+ {
+ MenuEntry_t const * entry = menu->entrylist[i];
+ if (entry != NULL && entry->type == Link)
+ {
+ MenuLink_t const * link = (MenuLink_t const *)entry->entry;
+ if (link->linkID == id)
+ {
+ menu->currentEntry = i;
+ Menu_AdjustForCurrentEntryAssignmentBlind(menu);
+ break;
+ }
+ }
+ }
+ }
+}
+
+static void Menu_ReadSaveGameHeaders()
+{
+ ReadSaveGameHeaders();
+
+ size_t const numloaditems = max(g_nummenusaves, 1), numsaveitems = g_nummenusaves+1;
+ ME_LOAD = (MenuEntry_t *)realloc(ME_LOAD, g_nummenusaves * sizeof(MenuEntry_t));
+ MEL_LOAD = (MenuEntry_t **)realloc(MEL_LOAD, numloaditems * sizeof(MenuEntry_t *));
+ MEO_SAVE = (MenuString_t *)realloc(MEO_SAVE, g_nummenusaves * sizeof(MenuString_t));
+ ME_SAVE = (MenuEntry_t *)realloc(ME_SAVE, g_nummenusaves * sizeof(MenuEntry_t));
+ MEL_SAVE = (MenuEntry_t **)realloc(MEL_SAVE, numsaveitems * sizeof(MenuEntry_t *));
+
+ MEL_SAVE[0] = &ME_SAVE_NEW;
+ ME_SAVE_NEW.name = s_NewSaveGame;
+ for (size_t i = 0; i < g_nummenusaves; ++i)
+ {
+ MEL_LOAD[i] = &ME_LOAD[i];
+ MEL_SAVE[i+1] = &ME_SAVE[i];
+ ME_LOAD[i] = ME_LOAD_TEMPLATE;
+ ME_SAVE[i] = ME_SAVE_TEMPLATE;
+ ME_SAVE[i].entry = &MEO_SAVE[i];
+ MEO_SAVE[i] = MEO_SAVE_TEMPLATE;
+
+ ME_LOAD[i].name = g_menusaves[i].brief.name;
+ MEO_SAVE[i].variable = g_menusaves[i].brief.name;
+ }
+
+ if (g_nummenusaves == 0)
+ MEL_LOAD[0] = &ME_LOAD_EMPTY;
+
+ M_LOAD.entrylist = MEL_LOAD;
+ M_LOAD.numEntries = numloaditems;
+ M_SAVE.entrylist = MEL_SAVE;
+ M_SAVE.numEntries = numsaveitems;
+
+ // lexicographical sorting?
+}
+
+static void Menu_AboutToStartDisplaying(Menu_t * m)
+{
+ switch (m->menuID)
+ {
+ case MENU_MAIN:
+ if (KXDWN)
+ ME_MAIN_LOADGAME.name = s_Continue;
+ break;
+
+ case MENU_MAIN_INGAME:
+ if (KXDWN)
+ ME_MAIN_LOADGAME.name = s_LoadGame;
+ break;
+
+ case MENU_LOAD:
+ if (KXDWN)
+ M_LOAD.title = (g_player[myconnectindex].ps->gm & MODE_GAME) ? s_LoadGame : s_Continue;
+
+ Menu_ReadSaveGameHeaders();
+
+ for (size_t i = 0; i < g_nummenusaves; ++i)
+ MenuEntry_LookDisabledOnCondition(&ME_LOAD[i], g_menusaves[i].isOldVer);
+
+ if (g_quickload && g_quickload->isValid())
+ {
+ for (size_t i = 0; i < g_nummenusaves; ++i)
+ {
+ if (strcmp(g_menusaves[i].brief.path, g_quickload->path) == 0)
+ {
+ M_LOAD.currentEntry = i;
+ Menu_AdjustForCurrentEntryAssignmentBlind(&M_LOAD);
+ break;
+ }
+ }
+ }
+ break;
+
+ case MENU_SAVE:
+ if (g_previousMenu == MENU_SAVEVERIFY)
+ break;
+
+ Menu_ReadSaveGameHeaders();
+
+ if (g_lastusersave.isValid())
+ {
+ for (size_t i = 0; i < g_nummenusaves; ++i)
+ {
+ if (strcmp(g_menusaves[i].brief.path, g_lastusersave.path) == 0)
+ {
+ M_SAVE.currentEntry = i+1;
+ Menu_AdjustForCurrentEntryAssignmentBlind(&M_SAVE);
+ break;
+ }
+ }
+ }
+
+ for (size_t i = 0; i < g_nummenusaves; ++i)
+ MenuEntry_LookDisabledOnCondition(&ME_SAVE[i], g_menusaves[i].isOldVer);
+
+ if (g_player[myconnectindex].ps->gm&MODE_GAME)
+ {
+ g_screenCapture = 1;
+ G_DrawRooms(myconnectindex,65536);
+ g_screenCapture = 0;
+ }
+ break;
+
+ case MENU_VIDEOSETUP:
+ newresolution = 0;
+ for (size_t i = 0; i < MAXVALIDMODES; ++i)
+ {
+ if (resolution[i].xdim == xdim && resolution[i].ydim == ydim)
+ {
+ newresolution = i;
+ break;
+ }
+ }
+ newrendermode = getrendermode();
+ newfullscreen = fullscreen;
+ newvsync = vsync;
+ break;
+
+ case MENU_ADVSOUND:
+ soundrate = ud.config.MixRate;
+ soundvoices = ud.config.NumVoices;
+ musicbackend = ud.config.FluidsynthBackend;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (m->type)
+ {
+ case TextForm:
+ typebuf[0] = 0;
+ ((MenuTextForm_t*)m->object)->input = typebuf;
+ break;
+ case FileSelect:
+ Menu_FileSelectInit((MenuFileSelect_t*)m->object);
+ break;
+ case Menu:
+ {
+ MenuMenu_t *menu = (MenuMenu_t*)m->object;
+ // MenuEntry_t* currentry = menu->entrylist[menu->currentEntry];
+
+ // need this for MENU_SKILL
+ if (menu->currentEntry >= menu->numEntries)
+ menu->currentEntry = 0;
+
+ int32_t i = menu->currentEntry;
+ while (!menu->entrylist[menu->currentEntry] || ((MenuEntry_t*)menu->entrylist[menu->currentEntry])->type == Spacer)
+ {
+ menu->currentEntry++;
+ if (menu->currentEntry >= menu->numEntries)
+ menu->currentEntry = 0;
+ if (menu->currentEntry == i)
+ G_GameExit("Menu_Change: Attempted to show a menu with no entries.");
+ }
+
+ Menu_EntryFocus(/*currentry*/);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void Menu_ChangingTo(Menu_t * m)
+{
+ switch (m->menuID)
+ {
+#ifdef __ANDROID__
+ case MENU_TOUCHBUTTONS:
+ AndroidToggleButtonEditor();
+ break;
+#endif
+ default:
+ break;
+ }
+
+ switch (m->type)
+ {
+ case TextForm:
+ WithSDL2_StartTextInput();
+ break;
+ default:
+ break;
+ }
+}
+
+int Menu_Change(MenuID_t cm)
+{
+ Menu_t * beginMenu = m_currentMenu;
+
+ cm = VM_OnEventWithReturn(EVENT_CHANGEMENU, g_player[screenpeek].ps->i, screenpeek, cm);
+
+ if (cm == MENU_PREVIOUS)
+ {
+ m_currentMenu = m_previousMenu;
+ g_currentMenu = g_previousMenu;
+ }
+ else if (cm == MENU_CLOSE)
+ Menu_Close(myconnectindex);
+ else if (cm >= 0)
+ {
+ Menu_t * search = Menu_FindFiltered(cm);
+
+ if (search == NULL)
+ return 0; // intentional, so that users don't use any random value as "don't change"
+
+ m_previousMenu = m_currentMenu;
+ g_previousMenu = g_currentMenu;
+ m_currentMenu = search;
+ g_currentMenu = search->menuID;
+ }
+ else
+ return 1;
+
+ if (KXDWN)
+ {
+ Menu_t * parent = m_currentMenu, * result = NULL;
+
+ while (parent != NULL && parent->menuID != MENU_OPTIONS && parent->menuID != MENU_MAIN && parent->menuID != MENU_MAIN_INGAME)
+ {
+ result = parent = Menu_FindFiltered(parent->parentID);
+ }
+
+ m_parentMenu = result;
+
+ if (result)
+ {
+ Menu_MaybeSetSelectionToChild(result, m_currentMenu->menuID);
+ Menu_AboutToStartDisplaying(result);
+ }
+ }
+
+ Menu_MaybeSetSelectionToChild(m_currentMenu, beginMenu->menuID);
+ Menu_AboutToStartDisplaying(m_currentMenu);
+ Menu_ChangingTo(m_currentMenu);
+
+#if !defined EDUKE32_TOUCH_DEVICES
+ m_menuchange_watchpoint = 1;
+#endif
+
+ return 0;
+}
+
+
+
+
+
+
+
+
+
+
+void G_CheckPlayerColor(int32_t *color, int32_t prev_color)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(MEOSV_PLAYER_COLOR); ++i)
+ if (*color == MEOSV_PLAYER_COLOR[i])
+ return;
+
+ *color = prev_color;
+}
+
+
+int32_t Menu_DetermineSpecialState(MenuEntry_t *entry)
+{
+ if (entry == NULL)
+ return 0;
+
+ if (entry->type == String)
+ {
+ if (((MenuString_t*)entry->entry)->editfield)
+ return 1;
+ }
+ else if (entry->type == Option)
+ {
+ if (((MenuOption_t*)entry->entry)->options->currentEntry >= 0)
+ return 2;
+ }
+ else if (entry->type == Custom2Col)
+ {
+ if (((MenuCustom2Col_t*)entry->entry)->screenOpen)
+ return 2;
+ }
+
+ return 0;
+}
+
+int32_t Menu_IsTextInput(Menu_t *cm)
+{
+ switch (m_currentMenu->type)
+ {
+ case Verify:
+ case TextForm:
+ case FileSelect:
+ case Message:
+ return 1;
+ break;
+ case Panel:
+ return 0;
+ break;
+ case Menu:
+ {
+ MenuMenu_t *menu = (MenuMenu_t*)cm->object;
+ MenuEntry_t *entry = menu->entrylist[menu->currentEntry];
+ return Menu_DetermineSpecialState(entry);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static inline int32_t Menu_BlackTranslucentBackgroundOK(MenuID_t cm)
+{
+ switch (cm)
+ {
+ case MENU_COLCORR:
+ case MENU_COLCORR_INGAME:
+ return 0;
+ break;
+ default:
+ return 1;
+ break;
+ }
+
+ return 1;
+}
+
+static inline int32_t Menu_UpdateScreenOK(MenuID_t cm)
+{
+ switch (cm)
+ {
+ case MENU_LOAD:
+ case MENU_SAVE:
+ case MENU_LOADVERIFY:
+ case MENU_SAVEVERIFY:
+ return 0;
+ break;
+ default:
+ return 1;
+ break;
+ }
+
+ return 1;
+}
+
+
+/*
+ Code below this point is entirely general,
+ so if you want to change or add a menu,
+ chances are you should scroll up.
+*/
+
+int32_t m_mouselastactivity;
+#if !defined EDUKE32_TOUCH_DEVICES
+int32_t m_mousewake_watchpoint, m_menuchange_watchpoint;
+#endif
+int32_t m_mousecaught;
+static vec2_t m_prevmousepos, m_mousepos, m_mousedownpos;
+
+void Menu_Open(size_t playerID)
+{
+ g_player[playerID].ps->gm |= MODE_MENU;
+
+ readmouseabsxy(&m_prevmousepos, &mouseabs);
+ m_mouselastactivity = -M_MOUSETIMEOUT;
+
+#if !defined EDUKE32_TOUCH_DEVICES
+ m_mousewake_watchpoint = 0;
+#endif
+
+ AppGrabMouse(0);
+}
+
+void Menu_Close(size_t playerID)
+{
+ if (g_player[playerID].ps->gm & MODE_GAME)
+ {
+ // The following lines are here so that you cannot close the menu when no game is running.
+ g_player[playerID].ps->gm &= ~MODE_MENU;
+ AppGrabMouse(1);
+
+ if ((!g_netServer && ud.multimode < 2) && ud.recstat != 2)
+ {
+ ready2send = 1;
+ totalclock = ototalclock;
+ CAMERACLOCK = totalclock;
+ CAMERADIST = 65536;
+ m_animation.start = 0;
+ m_animation.length = 0;
+
+ // Reset next-viewscreen-redraw counter.
+ // XXX: are there any other cases like that in need of handling?
+ if (g_curViewscreen >= 0)
+ actor[g_curViewscreen].t_data[0] = totalclock;
+ }
+
+ walock[TILE_SAVESHOT] = 199;
+ G_UpdateScreenArea();
+ }
+}
+
+static int32_t x_widescreen_left(void)
+{
+ return (320<<15) - scale(240<<15, xdim, ydim);
+}
+
+static int32_t xdim_from_320_16(int32_t x)
+{
+ const int32_t screenwidth = scale(240<<16, xdim, ydim);
+ return scale(x + (screenwidth>>1) - (160<<16), xdim, screenwidth);
+}
+static int32_t ydim_from_200_16(int32_t y)
+{
+ return scale(y, ydim, 200<<16);
+}
+
+static void Menu_BlackRectangle(int32_t x, int32_t y, int32_t width, int32_t height, int32_t orientation)
+{
+ const int32_t xscale = divscale16(width, tilesiz[0].x<<16), yscale = divscale16(height, tilesiz[0].y<<16);
+
+ rotatesprite_(x, y, max(xscale, yscale), 0, 0, 127, 4, (orientation&(1|32))|2|8|16, 0, 0, xdim_from_320_16(x), ydim_from_200_16(y), xdim_from_320_16(x + width), ydim_from_200_16(y + height));
+}
+
+enum MenuTextFlags_t
+{
+ MT_Selected = 1<<0,
+ MT_Disabled = 1<<1,
+ MT_XCenter = 1<<2,
+ MT_XRight = 1<<3,
+ MT_YCenter = 1<<4,
+ MT_Literal = 1<<5,
+};
+
+static void Menu_GetFmt(const MenuFont_t *font, uint8_t const status, int32_t *s, int32_t *p, int32_t *z)
+{
+ if (KXDWN) *z = (status & MT_Selected) ? *z + (*z >> 4) : *z;
+ *s = (status & MT_Selected) ? (sintable[(totalclock<<5)&2047]>>12) : font->shade_deselected;
+ *p = (status & MT_Disabled) ? font->pal_disabled : font->pal;
+}
+
+static vec2_t Menu_Text(int32_t x, int32_t y, const MenuFont_t *font, const char *t, uint8_t status, int32_t ydim_upper, int32_t ydim_lower)
+{
+ int32_t s, p, ybetween = font->between.y;
+ int32_t f = font->textflags;
+ if (status & MT_XCenter)
+ f |= TEXT_XCENTER;
+ if (status & MT_XRight)
+ f |= TEXT_XRIGHT;
+ if (status & MT_YCenter)
+ {
+ f |= TEXT_YCENTER | TEXT_YOFFSETZERO;
+ ybetween = font->emptychar.y; // <^ the battle against 'Q'
+ }
+ if (status & MT_Literal)
+ f |= TEXT_LITERALESCAPE;
+
+ int32_t z = font->zoom;
+
+ Menu_GetFmt(font, status, &s, &p, &z);
+
+ return G_ScreenText(font->tilenum, x, y, z, 0, 0, t, s, p, 2|8|16|ROTATESPRITE_FULL16, 0, font->emptychar.x, font->emptychar.y, font->between.x, ybetween, f, 0, ydim_upper, xdim-1, ydim_lower);
+}
+
+#if 0
+static vec2_t Menu_TextSize(int32_t x, int32_t y, const MenuFont_t *font, const char *t, uint8_t status)
+{
+ int32_t f = font->textflags;
+ if (status & MT_Literal)
+ f |= TEXT_LITERALESCAPE;
+
+ return G_ScreenTextSize(font->tilenum, x, y, font->zoom, 0, t, 2|8|16|ROTATESPRITE_FULL16, font->emptychar.x, font->emptychar.y, font->between.x, font->between.y, f, 0, 0, xdim-1, ydim-1);
+}
+#endif
+
+static int32_t Menu_FindOptionBinarySearch(MenuOption_t *object, const int32_t query, size_t searchstart, size_t searchend)
+{
+ const size_t thissearch = (searchstart + searchend) / 2;
+ const bool isIdentityMap = object->options->optionValues == NULL;
+
+ const int32_t destination = isIdentityMap ? (int32_t) thissearch : object->options->optionValues[thissearch];
+
+ const int32_t difference = query - destination;
+
+ Bassert(!isIdentityMap || query >= 0);
+
+ if (difference == 0)
+ return thissearch;
+ else if (searchstart == searchend)
+ return -1;
+ else if (difference > 0)
+ {
+ if (thissearch == searchend)
+ return -1;
+ searchstart = thissearch + 1;
+ }
+ else if (difference < 0)
+ {
+ if (thissearch == searchstart)
+ return -1;
+ searchend = thissearch - 1;
+ }
+
+ return Menu_FindOptionBinarySearch(object, query, searchstart, searchend);
+}
+
+static int32_t Menu_MouseOutsideBounds(vec2_t const * const pos, const int32_t x, const int32_t y, const int32_t width, const int32_t height)
+{
+ return pos->x < x || pos->x >= x + width || pos->y < y || pos->y >= y + height;
+}
+
+static void Menu_RunScrollbar(Menu_t *cm, MenuMenuFormat_t const * const format, const int32_t totalextent, int32_t * const scrollPos, const int32_t rightedge, const vec2_t origin)
+{
+ if (totalextent > klabs(format->bottomcutoff))
+ {
+ const int32_t scrollx = origin.x + rightedge - (tilesiz[SELECTDIR].x<<16), scrolly = origin.y + format->pos.y;
+ const int32_t scrollwidth = tilesiz[SELECTDIR].x<<16;
+ const int32_t scrollheight = klabs(format->bottomcutoff) - format->pos.y;
+ const int32_t scrollregionheight = scrollheight - (tilesiz[SELECTDIR].y<<16);
+ const int32_t scrollPosMax = totalextent - klabs(format->bottomcutoff);
+
+ Menu_BlackRectangle(scrollx, scrolly, scrollwidth, scrollheight, 1|32);
+
+ rotatesprite_fs(scrollx, scrolly + scale(scrollregionheight, *scrollPos, scrollPosMax), 65536, 0, SELECTDIR, 0, 0, 26);
+
+ if (cm == m_currentMenu && !m_mousecaught && MOUSEACTIVECONDITIONAL(mousepressstate == Mouse_Pressed || mousepressstate == Mouse_Held))
+ {
+ const int32_t scrolltilehalfheight = tilesiz[SELECTDIR].y<<15;
+ const int32_t scrollregiony = scrolly + scrolltilehalfheight;
+
+ // region between the y-midline of the arrow at the extremes scrolls proportionally
+ if (!Menu_MouseOutsideBounds(&m_mousepos, scrollx, scrollregiony, scrollwidth, scrollregionheight))
+ {
+ *scrollPos = scale(m_mousepos.y - scrollregiony, scrollPosMax, scrollregionheight);
+
+ m_mousecaught = 1;
+ }
+ // region outside the y-midlines clamps to the extremes
+ else if (!Menu_MouseOutsideBounds(&m_mousepos, scrollx, scrolly, scrollwidth, scrollheight))
+ {
+ if (m_mousepos.y > scrolly + scrollheight/2)
+ *scrollPos = scrollPosMax;
+ else
+ *scrollPos = 0;
+
+ m_mousecaught = 1;
+ }
+ }
+ }
+}
+
+typedef enum MenuMovement_t
+{
+ MM_Up = 1,
+ MM_End = 3,
+ MM_Down = 4,
+ MM_Home = 12,
+ MM_Left = 16,
+ MM_AllTheWayLeft = 48,
+ MM_Right = 64,
+ MM_AllTheWayRight = 192,
+ MM_Swap = 80,
+} MenuMovement_t;
+
+static MenuEntry_t *Menu_RunInput_Menu_MovementVerify(MenuMenu_t *menu);
+static MenuEntry_t *Menu_RunInput_Menu_Movement(MenuMenu_t *menu, MenuMovement_t direction);
+static void Menu_RunInput_EntryLink_Activate(MenuEntry_t *entry);
+static void Menu_RunInput_EntryOptionList_MovementVerify(MenuOption_t *object);
+static void Menu_RunInput_EntryOptionList_Movement(MenuOption_t *object, MenuMovement_t direction);
+static int32_t Menu_RunInput_EntryOption_Modify(MenuEntry_t *entry, MenuOption_t *object, int32_t newValueIndex);
+static int32_t Menu_RunInput_EntryOption_Movement(MenuEntry_t *entry, MenuOption_t *object, MenuMovement_t direction);
+static int32_t Menu_RunInput_EntryOption_Activate(MenuEntry_t *entry, MenuOption_t *object);
+static int32_t Menu_RunInput_EntryOptionList_Activate(MenuEntry_t *entry, MenuOption_t *object);
+static void Menu_RunInput_EntryCustom2Col_Activate(MenuEntry_t *entry);
+static void Menu_RunInput_EntryRangeInt32_MovementVerify(MenuEntry_t *entry, MenuRangeInt32_t *object, int32_t newValue);
+static void Menu_RunInput_EntryRangeInt32_MovementArbitrary(MenuEntry_t *entry, MenuRangeInt32_t *object, int32_t newValue);
+static void Menu_RunInput_EntryRangeInt32_Movement(MenuEntry_t *entry, MenuRangeInt32_t *object, MenuMovement_t direction);
+static void Menu_RunInput_EntryRangeFloat_MovementVerify(MenuEntry_t *entry, MenuRangeFloat_t *object, float newValue);
+static void Menu_RunInput_EntryRangeFloat_MovementArbitrary(MenuEntry_t *entry, MenuRangeFloat_t *object, float newValue);
+static void Menu_RunInput_EntryRangeFloat_Movement(MenuEntry_t *entry, MenuRangeFloat_t *object, MenuMovement_t direction);
+#ifdef MENU_ENABLE_RANGEDOUBLE
+static void Menu_RunInput_EntryRangeDouble_MovementVerify(/*MenuEntry_t *entry, */MenuRangeDouble_t *object, double newValue);
+static void Menu_RunInput_EntryRangeDouble_MovementArbitrary(/*MenuEntry_t *entry, */MenuRangeDouble_t *object, double newValue);
+static void Menu_RunInput_EntryRangeDouble_Movement(/*MenuEntry_t *entry, */MenuRangeDouble_t *object, MenuMovement_t direction);
+#endif
+static void Menu_RunInput_EntryString_Activate(MenuEntry_t *entry);
+static void Menu_RunInput_EntryString_Submit(/*MenuEntry_t *entry, */MenuString_t *object);
+static void Menu_RunInput_EntryString_Cancel(/*MenuEntry_t *entry, */MenuString_t *object);
+static void Menu_RunInput_FileSelect_MovementVerify(MenuFileSelect_t *object);
+static void Menu_RunInput_FileSelect_Movement(MenuFileSelect_t *object, MenuMovement_t direction);
+static void Menu_RunInput_FileSelect_Select(MenuFileSelect_t *object);
+
+static int32_t M_RunMenu_Menu(Menu_t *cm, MenuMenu_t *menu, MenuEntry_t *currentry, int32_t state, const vec2_t origin, bool actually_draw)
+{
+ int32_t totalHeight = 0;
+
+ // RIP MenuGroup_t b. 2014-03-?? d. 2014-11-29
+ {
+ int32_t e;
+ const int32_t y_upper = menu->format->pos.y;
+ const int32_t y_lower = klabs(menu->format->bottomcutoff);
+ int32_t y = 0;
+ int32_t calculatedentryspacing = 0;
+
+ if (menu->format->bottomcutoff < 0)
+ {
+ int32_t totalheight = 0, numvalidentries = 0;
+
+ for (e = 0; e < menu->numEntries; ++e)
+ {
+ MenuEntry_t *entry = menu->entrylist[e];
+
+ if (entry == NULL)
+ continue;
+
+ ++numvalidentries;
+
+ // assumes height == font->get_yline()!
+ totalheight += entry->getHeight();
+ }
+
+ calculatedentryspacing = (klabs(menu->format->bottomcutoff) - menu->format->pos.y - totalheight) / (numvalidentries > 1 ? numvalidentries - 1 : 1);
+ }
+
+ // totalHeight calculating pass
+ for (e = 0; e < menu->numEntries; ++e)
+ {
+ MenuEntry_t *entry = menu->entrylist[e];
+
+ if (entry == NULL)
+ continue;
+
+ int32_t const height = entry->getHeight();
+
+ y += height;
+ totalHeight = y;
+ y += (!calculatedentryspacing || calculatedentryspacing > entry->getMarginBottom()) ? entry->getMarginBottom() : calculatedentryspacing;
+ }
+ y = 0;
+
+ int32_t ydim_upper, ydim_lower;
+ if (y_upper + totalHeight > y_lower)
+ {
+ ydim_upper = ydim_from_200_16(origin.y + y_upper);
+ ydim_lower = ydim_from_200_16(origin.y + y_lower);
+ }
+ else
+ {
+ ydim_upper = 0;
+ ydim_lower = ydim-1;
+ }
+
+ for (e = 0; e < menu->numEntries; ++e)
+ {
+ MenuEntry_t *entry = menu->entrylist[e];
+
+ if (entry == NULL)
+ continue;
+
+ int32_t x = menu->format->pos.x + entry->getIndent();
+
+ uint8_t status = 0;
+ if (e == menu->currentEntry)
+ status |= MT_Selected;
+ if (entry->flags & (MEF_Disabled|MEF_LookDisabled))
+ status |= MT_Disabled;
+ if (entry->format->width == 0)
+ status |= MT_XCenter;
+
+ bool const dodraw = entry->type != Spacer && actually_draw &&
+ 0 <= y - menu->scrollPos + entry->font->get_yline() &&
+ y - menu->scrollPos <= klabs(menu->format->bottomcutoff) - menu->format->pos.y;
+
+ int32_t const height = entry->getHeight(); // max(textsize.y, entry->font->get_yline()); // bluefont Q ruins this
+ status |= MT_YCenter;
+ int32_t const y_internal = origin.y + y_upper + y + (height>>1) - menu->scrollPos;
+
+ vec2_t textsize;
+ if (dodraw)
+ textsize = Menu_Text(origin.x + x, y_internal, entry->font, entry->name, status, ydim_upper, ydim_lower);
+
+ if (entry->format->width < 0)
+ status |= MT_XRight;
+
+ if (dodraw && (status & MT_Selected) && state != 1)
+ {
+ if (status & MT_XCenter)
+ {
+ Menu_DrawCursorLeft(origin.x + (MENU_MARGIN_CENTER<<16) + entry->font->cursorCenterPosition, y_internal, entry->font->cursorScale);
+ Menu_DrawCursorRight(origin.x + (MENU_MARGIN_CENTER<<16) - entry->font->cursorCenterPosition, y_internal, entry->font->cursorScale);
+ }
+ else
+ Menu_DrawCursorLeft(origin.x + x - entry->font->cursorLeftPosition, y_internal, entry->font->cursorScale);
+ }
+
+ // need this up here to avoid race conditions
+ entry->ybottom = (entry->ytop = y_upper + y) + height;
+
+ if (dodraw)
+ {
+ const int32_t mousex = origin.x + entry->format->width == 0 ? x - textsize.x/2 : x;
+ const int32_t mousey = origin.y + y_upper + y - menu->scrollPos;
+ int32_t mousewidth = entry->format->width == 0 ? textsize.x : klabs(entry->format->width);
+
+ if (entry->name)
+ x += klabs(entry->format->width);
+
+ switch (entry->type)
+ {
+ case Spacer:
+ break;
+ case Dummy:
+ if (MOUSEACTIVECONDITIONAL(state != 1 && cm == m_currentMenu && !Menu_MouseOutsideBounds(&m_mousepos, mousex, mousey, mousewidth, height)))
+ {
+ if (MOUSEWATCHPOINTCONDITIONAL(Menu_MouseOutsideBounds(&m_prevmousepos, mousex, mousey, mousewidth, height)))
+ {
+ menu->currentEntry = e;
+ Menu_RunInput_Menu_MovementVerify(menu);
+ }
+ }
+ break;
+ case Link:
+ if (MOUSEACTIVECONDITIONAL(state != 1 && cm == m_currentMenu && !Menu_MouseOutsideBounds(&m_mousepos, mousex, mousey, mousewidth, height)))
+ {
+ if (MOUSEWATCHPOINTCONDITIONAL(Menu_MouseOutsideBounds(&m_prevmousepos, mousex, mousey, mousewidth, height)))
+ {
+ menu->currentEntry = e;
+ Menu_RunInput_Menu_MovementVerify(menu);
+ }
+
+ if (!m_mousecaught && mousepressstate == Mouse_Released && !Menu_MouseOutsideBounds(&m_mousedownpos, mousex, mousey, mousewidth, entry->font->get_yline()))
+ {
+ menu->currentEntry = e;
+ Menu_RunInput_Menu_MovementVerify(menu);
+
+ if (entry->flags & MEF_Disabled)
+ break;
+
+ Menu_RunInput_EntryLink_Activate(entry);
+
+ if (g_player[myconnectindex].ps->gm&MODE_MENU) // for skill selection
+ S_PlaySound(PISTOL_BODYHIT);
+
+ m_mousecaught = 1;
+ }
+ }
+ break;
+ case Option:
+ {
+ MenuOption_t *object = (MenuOption_t*)entry->entry;
+ int32_t currentOption = Menu_FindOptionBinarySearch(object, object->data == NULL ? Menu_EntryOptionSource(entry, object->currentOption) : *object->data, 0, object->options->numOptions);
+
+ if (currentOption >= 0)
+ object->currentOption = currentOption;
+
+ int32_t optiontextx = origin.x + x;
+ const int32_t optiontexty = origin.y + y_upper + y - menu->scrollPos;
+
+ const vec2_t optiontextsize = Menu_Text(optiontextx, optiontexty + (height>>1), object->font,
+ currentOption < 0 ? MenuCustom : currentOption < object->options->numOptions ? object->options->optionNames[currentOption] : NULL,
+ status, ydim_upper, ydim_lower);
+
+ if (entry->format->width > 0)
+ mousewidth += optiontextsize.x;
+ else
+ optiontextx -= optiontextsize.x;
+
+ if (MOUSEACTIVECONDITIONAL(state != 1 && cm == m_currentMenu && !Menu_MouseOutsideBounds(&m_mousepos, mousex, mousey, mousewidth, height)))
+ {
+ if (MOUSEWATCHPOINTCONDITIONAL(Menu_MouseOutsideBounds(&m_prevmousepos, mousex, mousey, mousewidth, height)))
+ {
+ menu->currentEntry = e;
+ Menu_RunInput_Menu_MovementVerify(menu);
+ }
+
+ if (!m_mousecaught && mousepressstate == Mouse_Released && !Menu_MouseOutsideBounds(&m_mousedownpos, mousex, mousey, mousewidth, entry->font->get_yline()))
+ {
+ menu->currentEntry = e;
+ Menu_RunInput_Menu_MovementVerify(menu);
+
+ if (entry->flags & MEF_Disabled)
+ break;
+
+ Menu_RunInput_EntryOption_Activate(entry, object);
+
+ S_PlaySound(PISTOL_BODYHIT);
+
+ m_mousecaught = 1;
+ }
+ }
+
+ break;
+ }
+ case Custom2Col:
+ {
+ MenuCustom2Col_t *object = (MenuCustom2Col_t*)entry->entry;
+ int32_t columnx[2] = { origin.x + x - ((status & MT_XRight) ? object->columnWidth : 0), origin.x + x + ((status & MT_XRight) ? 0 : object->columnWidth) };
+ const int32_t columny = origin.y + y_upper + y - menu->scrollPos;
+
+ const vec2_t column0textsize = Menu_Text(columnx[0], columny + (height>>1), object->font, object->key[*object->column[0]], menu->currentColumn == 0 ? status : (status & ~MT_Selected), ydim_upper, ydim_lower);
+ const vec2_t column1textsize = Menu_Text(columnx[1], columny + (height>>1), object->font, object->key[*object->column[1]], menu->currentColumn == 1 ? status : (status & ~MT_Selected), ydim_upper, ydim_lower);
+
+ if (entry->format->width > 0)
+ mousewidth += object->columnWidth + column1textsize.x;
+ else
+ {
+ columnx[0] -= column0textsize.x;
+ columnx[1] -= column0textsize.x;
+ }
+
+ if (MOUSEACTIVECONDITIONAL(state != 1 && cm == m_currentMenu && !Menu_MouseOutsideBounds(&m_mousepos, mousex, mousey, mousewidth, height)))
+ {
+ if (MOUSEWATCHPOINTCONDITIONAL(Menu_MouseOutsideBounds(&m_prevmousepos, mousex, mousey, mousewidth, height)))
+ {
+ menu->currentEntry = e;
+ Menu_RunInput_Menu_MovementVerify(menu);
+ }
+
+ if (!Menu_MouseOutsideBounds(&m_mousepos, columnx[1], mousey, column1textsize.x, object->font->get_yline()))
+ {
+ if (MOUSEWATCHPOINTCONDITIONAL(Menu_MouseOutsideBounds(&m_prevmousepos, columnx[1], mousey, column1textsize.x, object->font->get_yline())))
+ {
+ menu->currentColumn = 1;
+ }
+
+ if (!m_mousecaught && mousepressstate == Mouse_Released && !Menu_MouseOutsideBounds(&m_mousedownpos, columnx[1], mousey, column1textsize.x, object->font->get_yline()))
+ {
+ menu->currentEntry = e;
+ Menu_RunInput_Menu_MovementVerify(menu);
+ menu->currentColumn = 1;
+
+ if (entry->flags & MEF_Disabled)
+ break;
+
+ Menu_RunInput_EntryCustom2Col_Activate(entry);
+
+ S_PlaySound(PISTOL_BODYHIT);
+
+ m_mousecaught = 1;
+ }
+ }
+ else if (!Menu_MouseOutsideBounds(&m_mousepos, columnx[0], mousey, column0textsize.x, object->font->get_yline()))
+ {
+ if (MOUSEWATCHPOINTCONDITIONAL(Menu_MouseOutsideBounds(&m_prevmousepos, columnx[0], mousey, column0textsize.x, object->font->get_yline())))
+ {
+ menu->currentColumn = 0;
+ }
+
+ if (!m_mousecaught && mousepressstate == Mouse_Released && !Menu_MouseOutsideBounds(&m_mousedownpos, columnx[0], mousey, column0textsize.x, object->font->get_yline()))
+ {
+ menu->currentEntry = e;
+ Menu_RunInput_Menu_MovementVerify(menu);
+ menu->currentColumn = 0;
+
+ if (entry->flags & MEF_Disabled)
+ break;
+
+ Menu_RunInput_EntryCustom2Col_Activate(entry);
+
+ S_PlaySound(PISTOL_BODYHIT);
+
+ m_mousecaught = 1;
+ }
+ }
+ }
+ break;
+ }
+ case RangeInt32:
+ {
+ MenuRangeInt32_t *object = (MenuRangeInt32_t*)entry->entry;
+
+ int32_t s, p;
+ int32_t z = entry->font->cursorScale;
+ Menu_GetFmt(object->font, status, &s, &p, &z);
+
+ const int32_t slidebarwidth = mulscale16(tilesiz[SLIDEBAR].x<<16, z);
+ const int32_t slidebarheight = mulscale16(tilesiz[SLIDEBAR].y<<16, z);
+
+ if (status & MT_XRight)
+ x -= slidebarwidth;
+ else
+ mousewidth += slidebarwidth;
+
+ const int32_t slidebarx = origin.x + x;
+ const int32_t slidebary = origin.y + y_upper + y + ((height - slidebarheight)>>1) - menu->scrollPos;
+
+ rotatesprite_ybounds(slidebarx, slidebary, z, 0, SLIDEBAR, s, (entry->flags & (MEF_Disabled|MEF_LookDisabled)) ? 1 : 0, 2|8|16|ROTATESPRITE_FULL16, ydim_upper, ydim_lower);
+
+ const int32_t slideregionwidth = mulscale16((tilesiz[SLIDEBAR].x-2-tilesiz[SLIDEBAR+1].x)<<16, z);
+ const int32_t slidepointx = slidebarx + (1<<16) + scale(slideregionwidth, *object->variable - object->min, object->max - object->min);
+ const int32_t slidepointy = slidebary + mulscale16((tilesiz[SLIDEBAR].y-tilesiz[SLIDEBAR+1].y)<<15, z);
+
+ rotatesprite_ybounds(slidepointx, slidepointy, z, 0, SLIDEBAR+1, s, (entry->flags & (MEF_Disabled|MEF_LookDisabled)) ? 1 : 0, 2|8|16|ROTATESPRITE_FULL16, ydim_upper, ydim_lower);
+
+ if (object->flags & DisplayTypeMask)
+ {
+ status |= MT_XRight;
+
+ int32_t onehundredpercent = object->onehundredpercent;
+ if (onehundredpercent == 0)
+ onehundredpercent = object->max;
+
+ switch (object->flags & DisplayTypeMask)
+ {
+ case DisplayTypeInteger:
+ Bsprintf(tempbuf, "%d", *object->variable);
+ break;
+ case DisplayTypePercent:
+ Bsprintf(tempbuf, "%d%%", roundscale(*object->variable, 100, onehundredpercent));
+ break;
+ case DisplayTypeNormalizedDecimal:
+ Bsprintf(tempbuf, "%.2f", (double) *object->variable / (double) onehundredpercent);
+ break;
+ }
+
+ Menu_Text(origin.x + x - (4<<16), origin.y + y_upper + y + (height>>1) - menu->scrollPos, object->font, tempbuf, status, ydim_upper, ydim_lower);
+ }
+
+ if (MOUSEACTIVECONDITIONAL(state != 1 && cm == m_currentMenu && !Menu_MouseOutsideBounds(&m_mousepos, mousex, mousey, mousewidth, height)))
+ {
+ if (MOUSEWATCHPOINTCONDITIONAL(Menu_MouseOutsideBounds(&m_prevmousepos, mousex, mousey, mousewidth, height)))
+ {
+ menu->currentEntry = e;
+ Menu_RunInput_Menu_MovementVerify(menu);
+ }
+
+ if (!m_mousecaught && (mousepressstate == Mouse_Pressed || mousepressstate == Mouse_Held))
+ {
+ const int32_t slidepointhalfwidth = mulscale16((2+tilesiz[SLIDEBAR+1].x)<<15, z);
+ const int32_t slideregionx = slidebarx + slidepointhalfwidth;
+
+ menu->currentEntry = e;
+ Menu_RunInput_Menu_MovementVerify(menu);
+
+ if (entry->flags & MEF_Disabled)
+ break;
+
+ // region between the x-midline of the slidepoint at the extremes slides proportionally
+ if (!Menu_MouseOutsideBounds(&m_mousepos, slideregionx, mousey, slideregionwidth, height))
+ {
+ Menu_RunInput_EntryRangeInt32_MovementArbitrary(entry, object, roundscale(object->max - object->min, m_mousepos.x - slideregionx, slideregionwidth) + object->min);
+
+ m_mousecaught = 1;
+ }
+ // region outside the x-midlines clamps to the extremes
+ else if (!Menu_MouseOutsideBounds(&m_mousepos, slidebarx, mousey, slidebarwidth, height))
+ {
+ if (m_mousepos.x > slideregionx + slideregionwidth/2)
+ Menu_RunInput_EntryRangeInt32_MovementVerify(entry, object, object->max);
+ else
+ Menu_RunInput_EntryRangeInt32_MovementVerify(entry, object, object->min);
+
+ m_mousecaught = 1;
+ }
+ }
+ }
+
+ break;
+ }
+ case RangeFloat:
+ {
+ MenuRangeFloat_t *object = (MenuRangeFloat_t*)entry->entry;
+
+ int32_t s, p;
+ int32_t z = entry->font->cursorScale;
+ Menu_GetFmt(object->font, status, &s, &p, &z);
+
+ const int32_t slidebarwidth = mulscale16(tilesiz[SLIDEBAR].x<<16, z);
+ const int32_t slidebarheight = mulscale16(tilesiz[SLIDEBAR].y<<16, z);
+
+ if (status & MT_XRight)
+ x -= slidebarwidth;
+ else
+ mousewidth += slidebarwidth;
+
+ const int32_t slidebarx = origin.x + x;
+ const int32_t slidebary = origin.y + y_upper + y + ((height - slidebarheight)>>1) - menu->scrollPos;
+
+ rotatesprite_ybounds(slidebarx, slidebary, z, 0, SLIDEBAR, s, (entry->flags & (MEF_Disabled|MEF_LookDisabled)) ? 1 : 0, 2|8|16|ROTATESPRITE_FULL16, ydim_upper, ydim_lower);
+
+ const int32_t slideregionwidth = mulscale16((tilesiz[SLIDEBAR].x-2-tilesiz[SLIDEBAR+1].x)<<16, z);
+ const int32_t slidepointx = slidebarx + (1<<16) + Blrintf((float) slideregionwidth * (*object->variable - object->min) / (object->max - object->min));
+ const int32_t slidepointy = slidebary + mulscale16((tilesiz[SLIDEBAR].y-tilesiz[SLIDEBAR+1].y)<<15, z);
+
+ rotatesprite_ybounds(slidepointx, slidepointy, z, 0, SLIDEBAR+1, s, (entry->flags & (MEF_Disabled|MEF_LookDisabled)) ? 1 : 0, 2|8|16|ROTATESPRITE_FULL16, ydim_upper, ydim_lower);
+
+ if (object->flags & DisplayTypeMask)
+ {
+ status |= MT_XRight;
+
+ float onehundredpercent = object->onehundredpercent;
+ if (onehundredpercent == 0.f)
+ onehundredpercent = 1.f;
+
+ switch (object->flags & DisplayTypeMask)
+ {
+ case DisplayTypeInteger:
+ Bsprintf(tempbuf, "%.2f", *object->variable);
+ break;
+ case DisplayTypePercent:
+ Bsprintf(tempbuf, "%ld%%", lrintf(*object->variable * 100.f / onehundredpercent));
+ break;
+ case DisplayTypeNormalizedDecimal:
+ Bsprintf(tempbuf, "%.2f", *object->variable / onehundredpercent);
+ break;
+ }
+
+ Menu_Text(origin.x + x - (4<<16), origin.y + y_upper + y + (height>>1) - menu->scrollPos, object->font, tempbuf, status, ydim_upper, ydim_lower);
+ }
+
+ if (MOUSEACTIVECONDITIONAL(state != 1 && cm == m_currentMenu && !Menu_MouseOutsideBounds(&m_mousepos, mousex, mousey, mousewidth, height)))
+ {
+ if (MOUSEWATCHPOINTCONDITIONAL(Menu_MouseOutsideBounds(&m_prevmousepos, mousex, mousey, mousewidth, height)))
+ {
+ menu->currentEntry = e;
+ Menu_RunInput_Menu_MovementVerify(menu);
+ }
+
+ if (!m_mousecaught && (mousepressstate == Mouse_Pressed || mousepressstate == Mouse_Held))
+ {
+ const int32_t slidepointhalfwidth = mulscale16((2+tilesiz[SLIDEBAR+1].x)<<15, z);
+ const int32_t slideregionx = slidebarx + slidepointhalfwidth;
+
+ menu->currentEntry = e;
+ Menu_RunInput_Menu_MovementVerify(menu);
+
+ if (entry->flags & MEF_Disabled)
+ break;
+
+ // region between the x-midline of the slidepoint at the extremes slides proportionally
+ if (!Menu_MouseOutsideBounds(&m_mousepos, slideregionx, mousey, slideregionwidth, height))
+ {
+ Menu_RunInput_EntryRangeFloat_MovementArbitrary(entry, object, (object->max - object->min) * (m_mousepos.x - slideregionx) / slideregionwidth + object->min);
+
+ m_mousecaught = 1;
+ }
+ // region outside the x-midlines clamps to the extremes
+ else if (!Menu_MouseOutsideBounds(&m_mousepos, slidebarx, mousey, slidebarwidth, height))
+ {
+ if (m_mousepos.x > slideregionx + slideregionwidth/2)
+ Menu_RunInput_EntryRangeFloat_MovementVerify(entry, object, object->max);
+ else
+ Menu_RunInput_EntryRangeFloat_MovementVerify(entry, object, object->min);
+
+ m_mousecaught = 1;
+ }
+ }
+ }
+
+ break;
+ }
+#ifdef MENU_ENABLE_RANGEDOUBLE
+ case RangeDouble:
+ {
+ MenuRangeDouble_t *object = (MenuRangeDouble_t*)entry->entry;
+
+ int32_t s, p;
+ int32_t z = entry->font->cursorScale;
+ Menu_GetFmt(object->font, status, &s, &p, &z);
+
+ const int32_t slidebarwidth = mulscale16(tilesiz[SLIDEBAR].x<<16, z);
+ const int32_t slidebarheight = mulscale16(tilesiz[SLIDEBAR].y<<16, z);
+
+ if (status & MT_XRight)
+ x -= slidebarwidth;
+ else
+ mousewidth += slidebarwidth;
+
+ const int32_t slidebarx = origin.x + x;
+ const int32_t slidebary = origin.y + y_upper + y + ((height - slidebarheight)>>1) - menu->scrollPos;
+
+ rotatesprite_ybounds(slidebarx, slidebary, z, 0, SLIDEBAR, s, (entry->flags & (MEF_Disabled|MEF_LookDisabled)) ? 1 : 0, 2|8|16|ROTATESPRITE_FULL16, ydim_upper, ydim_lower);
+
+ const int32_t slideregionwidth = mulscale16((tilesiz[SLIDEBAR].x-2-tilesiz[SLIDEBAR+1].x)<<16, z);
+ const int32_t slidepointx = slidebarx + (1<<16) + lrint((double) slideregionwidth * (*object->variable - object->min) / (object->max - object->min));
+ const int32_t slidepointy = slidebary + mulscale16((tilesiz[SLIDEBAR].y-tilesiz[SLIDEBAR+1].y)<<15, z);
+
+ rotatesprite_ybounds(slidepointx, slidepointy, z, 0, SLIDEBAR+1, s, (entry->flags & (MEF_Disabled|MEF_LookDisabled)) ? 1 : 0, 2|8|16|ROTATESPRITE_FULL16, ydim_upper, ydim_lower);
+
+ if (object->flags & DisplayTypeMask)
+ {
+ status |= MT_XRight;
+
+ double onehundredpercent = object->onehundredpercent;
+ if (onehundredpercent == 0.)
+ onehundredpercent = 1.;
+
+ switch (object->flags & DisplayTypeMask)
+ {
+ case DisplayTypeInteger:
+ Bsprintf(tempbuf, "%.2f", *object->variable);
+ break;
+ case DisplayTypePercent:
+ Bsprintf(tempbuf, "%ld%%", lrint(*object->variable * 100. / onehundredpercent));
+ break;
+ case DisplayTypeNormalizedDecimal:
+ Bsprintf(tempbuf, "%.2f", *object->variable / onehundredpercent);
+ break;
+ }
+
+ Menu_Text(origin.x + x - (4<<16), origin.y + y_upper + y + (height>>1) - menu->scrollPos, object->font, tempbuf, status, ydim_upper, ydim_lower);
+ }
+
+ if (MOUSEACTIVECONDITIONAL(state != 1 && cm == m_currentMenu && !Menu_MouseOutsideBounds(&m_mousepos, mousex, mousey, mousewidth, height)))
+ {
+ if (MOUSEWATCHPOINTCONDITIONAL(Menu_MouseOutsideBounds(&m_prevmousepos, mousex, mousey, mousewidth, height)))
+ {
+ menu->currentEntry = e;
+ Menu_RunInput_Menu_MovementVerify(menu);
+ }
+
+ if (!m_mousecaught && (mousepressstate == Mouse_Pressed || mousepressstate == Mouse_Held))
+ {
+ const int32_t slidepointhalfwidth = mulscale16((2+tilesiz[SLIDEBAR+1].x)<<15, z);
+ const int32_t slideregionx = slidebarx + slidepointhalfwidth;
+
+ menu->currentEntry = e;
+ Menu_RunInput_Menu_MovementVerify(menu);
+
+ if (entry->flags & MEF_Disabled)
+ break;
+
+ // region between the x-midline of the slidepoint at the extremes slides proportionally
+ if (!Menu_MouseOutsideBounds(&m_mousepos, slideregionx, mousey, slideregionwidth, height))
+ {
+ Menu_RunInput_EntryRangeDouble_MovementArbitrary(/*entry, */object, (object->max - object->min) * (m_mousepos.x - slideregionx) / slideregionwidth + object->min);
+
+ m_mousecaught = 1;
+ }
+ // region outside the x-midlines clamps to the extremes
+ else if (!Menu_MouseOutsideBounds(&m_mousepos, slidebarx, mousey, slidebarwidth, height))
+ {
+ if (m_mousepos.x > slideregionx + slideregionwidth/2)
+ Menu_RunInput_EntryRangeDouble_MovementVerify(/*entry, */object, object->max);
+ else
+ Menu_RunInput_EntryRangeDouble_MovementVerify(/*entry, */object, object->min);
+
+ m_mousecaught = 1;
+ }
+ }
+ }
+
+ break;
+ }
+#endif
+ case String:
+ {
+ MenuString_t *object = (MenuString_t*)entry->entry;
+
+ vec2_t dim;
+ int32_t stringx = x;
+ const int32_t stringy = origin.y + y_upper + y + (height>>1) - menu->scrollPos;
+ int32_t h;
+
+ if (entry == currentry && object->editfield != NULL)
+ {
+ dim = Menu_Text(origin.x + stringx, stringy, object->font, object->editfield, (status & ~MT_Disabled) | MT_Literal, ydim_upper, ydim_lower);
+ h = max(dim.y, entry->font->get_yline());
+
+ Menu_DrawCursorText(origin.x + x + dim.x + (1<<16), stringy, h, ydim_upper, ydim_lower);
+ }
+ else
+ {
+ dim = Menu_Text(origin.x + stringx, stringy, object->font, object->variable, status, ydim_upper, ydim_lower);
+ h = max(dim.y, entry->font->get_yline());
+ }
+
+ if (entry->format->width > 0)
+ {
+ if (entry->name)
+ mousewidth += dim.x;
+ }
+ else
+ stringx -= dim.x;
+
+ if (MOUSEACTIVECONDITIONAL(cm == m_currentMenu && !Menu_MouseOutsideBounds(&m_mousepos, mousex, mousey, mousewidth, h)))
+ {
+ if (state != 1 && Menu_MouseOutsideBounds(&m_prevmousepos, mousex, mousey, mousewidth, h))
+ {
+ menu->currentEntry = e;
+ Menu_RunInput_Menu_MovementVerify(menu);
+ }
+
+ #ifndef EDUKE32_TOUCH_DEVICES
+ if (!m_mousecaught && mousepressstate == Mouse_Released && !Menu_MouseOutsideBounds(&m_mousepos, mousex, mousey, mousewidth, h) && !Menu_MouseOutsideBounds(&m_mousedownpos, mousex, mousey, mousewidth, h))
+ #endif
+ {
+ if (entry == currentry && object->editfield != NULL)
+ {
+ Menu_RunInput_EntryString_Submit(/*entry, */object);
+
+ S_PlaySound(PISTOL_BODYHIT);
+
+ m_mousecaught = 1;
+ }
+ else if (state != 1)
+ {
+ menu->currentEntry = e;
+ Menu_RunInput_Menu_MovementVerify(menu);
+
+ if (entry->flags & MEF_Disabled)
+ break;
+
+ Menu_RunInput_EntryString_Activate(entry);
+
+ S_PlaySound(PISTOL_BODYHIT);
+
+ m_mousecaught = 1;
+ }
+ }
+ }
+
+ break;
+ }
+ }
+ }
+
+ // prepare for the next line
+ y += height;
+ y += (!calculatedentryspacing || calculatedentryspacing > entry->getMarginBottom()) ? entry->getMarginBottom() : calculatedentryspacing;
+ }
+
+ // draw indicators if applicable
+ if (actually_draw)
+ Menu_RunScrollbar(cm, menu->format, y_upper + totalHeight, &menu->scrollPos, 320<<16, origin);
+ }
+
+ return totalHeight;
+}
+
+static void Menu_RunOptionList(Menu_t *cm, MenuEntry_t *entry, MenuOption_t *object, const vec2_t origin)
+{
+ int32_t e, y = 0;
+ const int32_t y_upper = object->options->menuFormat->pos.y;
+ const int32_t y_lower = object->options->menuFormat->bottomcutoff;
+ int32_t calculatedentryspacing = object->options->getMarginBottom();
+
+ // assumes height == font->get_yline()!
+ if (calculatedentryspacing < 0)
+ calculatedentryspacing = (-calculatedentryspacing - object->options->font->get_yline()) / (object->options->numOptions - 1) - object->options->font->get_yline();
+
+ int32_t totalHeight = 0;
+ for (e = 0; e < object->options->numOptions; ++e)
+ {
+ int32_t const height = object->options->font->get_yline();
+
+ y += height;
+ totalHeight = y;
+ y += calculatedentryspacing;
+ }
+ y = 0;
+
+ int32_t ydim_upper, ydim_lower;
+ if (y_upper + totalHeight > y_lower)
+ {
+ ydim_upper = ydim_from_200_16(origin.y + y_upper);
+ ydim_lower = ydim_from_200_16(origin.y + y_lower);
+ }
+ else
+ {
+ ydim_upper = 0;
+ ydim_lower = ydim-1;
+ }
+
+ for (e = 0; e < object->options->numOptions; ++e)
+ {
+ int32_t const x = object->options->menuFormat->pos.x;
+
+ uint8_t status = 0;
+ if (e == object->options->currentEntry)
+ status |= MT_Selected;
+ if (object->options->entryFormat->width == 0)
+ status |= MT_XCenter;
+
+ bool const dodraw = 0 <= y - object->options->scrollPos + object->options->font->get_yline() &&
+ y - object->options->scrollPos <= object->options->menuFormat->bottomcutoff - object->options->menuFormat->pos.y;
+
+ int32_t const height = object->options->font->get_yline(); // max(textsize.y, object->options->font->get_yline());
+ status |= MT_YCenter;
+ int32_t const y_internal = origin.y + y_upper + y + (height>>1) - object->options->scrollPos;
+
+ vec2_t textsize;
+ if (dodraw)
+ textsize = Menu_Text(origin.x + x, y_internal, object->options->font, object->options->optionNames[e], status, ydim_upper, ydim_lower);
+
+ if (object->options->entryFormat->width < 0)
+ status |= MT_XRight;
+
+ if (dodraw && (status & MT_Selected))
+ {
+ if (status & MT_XCenter)
+ {
+ Menu_DrawCursorLeft(origin.x + (MENU_MARGIN_CENTER<<16) + object->options->font->cursorCenterPosition, y_internal, object->options->font->cursorScale);
+ Menu_DrawCursorRight(origin.x + (MENU_MARGIN_CENTER<<16) - object->options->font->cursorCenterPosition, y_internal, object->options->font->cursorScale);
+ }
+ else
+ Menu_DrawCursorLeft(origin.x + x - object->options->font->cursorLeftPosition, y_internal, object->options->font->cursorScale);
+ }
+
+ if (dodraw)
+ {
+ const int32_t mousex = origin.x + object->options->entryFormat->width == 0 ? x - textsize.x/2 : x;
+ const int32_t mousey = origin.y + y_upper + y - object->options->scrollPos;
+ const int32_t mousewidth = object->options->entryFormat->width == 0 ? textsize.x : klabs(object->options->entryFormat->width);
+
+ if (MOUSEACTIVECONDITIONAL(cm == m_currentMenu && !Menu_MouseOutsideBounds(&m_mousepos, mousex, mousey, mousewidth, object->options->font->get_yline())))
+ {
+ if (MOUSEWATCHPOINTCONDITIONAL(Menu_MouseOutsideBounds(&m_prevmousepos, mousex, mousey, mousewidth, object->options->font->get_yline())))
+ {
+ object->options->currentEntry = e;
+ }
+
+ if (!m_mousecaught && mousepressstate == Mouse_Released && !Menu_MouseOutsideBounds(&m_mousedownpos, mousex, mousey, mousewidth, object->options->font->get_yline()))
+ {
+ object->options->currentEntry = e;
+
+ if (!Menu_RunInput_EntryOptionList_Activate(entry, object))
+ S_PlaySound(PISTOL_BODYHIT);
+
+ m_mousecaught = 1;
+ }
+ }
+ }
+
+ // prepare for the next line
+ y += height;
+ y += calculatedentryspacing;
+ }
+
+ // draw indicators if applicable
+ Menu_RunScrollbar(cm, object->options->menuFormat, y_upper + totalHeight, &object->options->scrollPos, 320<<16, origin);
+}
+
+static int32_t Menu_RunInput_MouseAdvance(void)
+{
+ return MOUSEACTIVECONDITIONAL(!m_mousecaught && mousepressstate == Mouse_Released);
+}
+
+static int32_t Menu_RunInput_MouseReturn_status;
+
+#if !defined EDUKE32_TOUCH_DEVICES
+static void Menu_Run_MouseReturn(Menu_t *cm, const vec2_t origin)
+{
+ if (!MOUSEACTIVECONDITION)
+ return;
+
+ if (cm->menuID == MENU_MAIN)
+ return;
+
+ rotatesprite_(origin.x + (tilesiz[SELECTDIR].y << 16), 0, 65536, 512, SELECTDIR,
+ Menu_RunInput_MouseReturn_status ? 4 - (sintable[(totalclock << 4) & 2047] >> 11) : 6, 0,
+ 2 | 8 | 16 | RS_ALIGN_L, MOUSEALPHA, 0, xdim_from_320_16(origin.x + x_widescreen_left()), 0,
+ xdim_from_320_16(origin.x + x_widescreen_left() + (tilesiz[SELECTDIR].y << 15)), ydim - 1);
+}
+#endif
+
+static int32_t Menu_RunInput_MouseReturn(void)
+{
+#if !defined EDUKE32_TOUCH_DEVICES
+ if (!MOUSEACTIVECONDITION)
+ {
+ Menu_RunInput_MouseReturn_status = 0;
+ return 0;
+ }
+#endif
+
+ if (g_currentMenu == MENU_MAIN)
+ return 0;
+
+ const int32_t MouseReturnRegionX = x_widescreen_left();
+
+ if (!Menu_MouseOutsideBounds(&m_mousepos, MouseReturnRegionX, 0, tilesiz[SELECTDIR].y<<15, tilesiz[SELECTDIR].x<<16))
+ {
+#if !defined EDUKE32_TOUCH_DEVICES
+ Menu_RunInput_MouseReturn_status = 1;
+#else
+ Menu_RunInput_MouseReturn_status = (mousepressstate == Mouse_Pressed || mousepressstate == Mouse_Held);
+#endif
+
+ return !m_mousecaught && mousepressstate == Mouse_Released && !Menu_MouseOutsideBounds(&m_mousedownpos, MouseReturnRegionX, 0, tilesiz[SELECTDIR].y<<15, tilesiz[SELECTDIR].x<<16);
+ }
+
+ Menu_RunInput_MouseReturn_status = 0;
+
+ return 0;
+}
+
+static void Menu_Run_AbbreviateNameIntoBuffer(const char* name, int32_t entrylength)
+{
+ int32_t len = Bstrlen(name);
+ Bstrncpy(tempbuf, name, len);
+ if (len > entrylength)
+ {
+ len = entrylength-3;
+ tempbuf[len] = 0;
+ while (len < entrylength)
+ tempbuf[len++] = '.';
+ }
+ tempbuf[len] = 0;
+}
+
+static void Menu_Recurse(MenuID_t cm, const vec2_t origin)
+{
+ switch (cm)
+ {
+ case MENU_LOADVERIFY:
+ case MENU_SAVEVERIFY:
+ case MENU_ADULTPASSWORD:
+ case MENU_CHEATENTRY:
+ case MENU_CHEAT_WARP:
+ case MENU_CHEAT_SKILL:
+ Menu_Run(m_previousMenu, origin);
+ break;
+ default:
+ break;
+ }
+}
+
+static void Menu_Run(Menu_t *cm, const vec2_t origin)
+{
+ Menu_Recurse(cm->menuID, origin);
+
+ switch (cm->type)
+ {
+ case Verify:
+ {
+ MenuVerify_t *object = (MenuVerify_t*)cm->object;
+
+ Menu_Pre(cm->menuID);
+
+ Menu_PreDrawBackground(cm->menuID, origin);
+
+ Menu_PreDraw(cm->menuID, NULL, origin);
+
+ Menu_DrawCursorLeft(origin.x + object->cursorpos.x, origin.y + object->cursorpos.y, 65536);
+
+ break;
+ }
+
+ case Message:
+ {
+ MenuMessage_t *object = (MenuMessage_t*)cm->object;
+
+ Menu_Pre(cm->menuID);
+
+ Menu_PreDrawBackground(cm->menuID, origin);
+
+ Menu_PreDraw(cm->menuID, NULL, origin);
+
+ Menu_DrawCursorLeft(origin.x + object->cursorpos.x, origin.y + object->cursorpos.y, 65536);
+
+ break;
+ }
+
+ case TextForm:
+ {
+ MenuTextForm_t *object = (MenuTextForm_t*)cm->object;
+
+ Menu_Pre(cm->menuID);
+
+ Menu_PreDrawBackground(cm->menuID, origin);
+
+ Menu_BlackRectangle(origin.x + (60<<16), origin.y + (86<<16), 200<<16, 28<<16, 0);
+
+ mgametextcenter(origin.x, origin.y + (98<<16), object->instructions, TEXT_YBOTTOM);
+
+ const char *displaytext = object->input;
+
+ if (object->flags & MTF_Password)
+ {
+ size_t x;
+ for (x = 0; x < Bstrlen(object->input); ++x)
+ tempbuf[x] = '*';
+ tempbuf[x] = 0;
+
+ displaytext = tempbuf;
+ }
+
+ const vec2_t textreturn = mgametextcenter(origin.x, origin.y + (102<<16), displaytext);
+
+ Menu_PreDraw(cm->menuID, NULL, origin);
+
+ int32_t const h = MF_Bluefont.get_yline();
+
+ Menu_DrawCursorText(origin.x + (MENU_MARGIN_CENTER<<16) + (textreturn.x>>1) + (1<<16), origin.y + (102<<16) + (h>>1), h);
+
+ break;
+ }
+
+ case FileSelect:
+ {
+ MenuFileSelect_t *object = (MenuFileSelect_t*)cm->object;
+ const int32_t MenuFileSelect_scrollbar_rightedge[2] = { 160<<16, 284<<16 };
+ int32_t i, selected = 0;
+
+ Menu_Pre(cm->menuID);
+
+ Menu_PreDrawBackground(cm->menuID, origin);
+
+ if (object->title != NoTitle)
+ Menu_DrawTopBar(origin);
+
+
+ // black translucent background underneath file lists
+ Menu_BlackRectangle(origin.x + (36<<16), origin.y + (42<<16), 248<<16, 123<<16, 1|32);
+
+ // path
+ Bsnprintf(tempbuf, sizeof(tempbuf), "Path: %s", object->destination);
+ Menu_Text(origin.x + object->format[0]->pos.x, origin.y + (32<<16) + (MF_Bluefont.get_yline()>>1), &MF_Bluefont, tempbuf, MT_YCenter, 0, ydim-1);
+
+ for (i = 0; i < 2; ++i)
+ {
+ if (object->findhigh[i])
+ {
+ CACHE1D_FIND_REC *dir;
+ int32_t y = 0;
+ const int32_t y_upper = object->format[i]->pos.y;
+ const int32_t y_lower = klabs(object->format[i]->bottomcutoff);
+
+ int32_t totalHeight = 0;
+ for (dir = object->findhigh[i]->usera; dir; dir = dir->next)
+ {
+ y += object->font[i]->get_yline();
+ totalHeight = y;
+ y += object->getMarginBottom(i);
+ }
+ y = 0;
+
+ int32_t ydim_upper, ydim_lower;
+ if (y_upper + totalHeight > y_lower)
+ {
+ ydim_upper = ydim_from_200_16(origin.y + y_upper);
+ ydim_lower = ydim_from_200_16(origin.y + y_lower);
+ }
+ else
+ {
+ ydim_upper = 0;
+ ydim_lower = ydim-1;
+ }
+
+ for (dir = object->findhigh[i]->usera; dir; dir = dir->next)
+ {
+ uint8_t status = (dir == object->findhigh[i] && object->currentList == i) ? MT_Selected : 0;
+
+ // pal = dir->source==CACHE1D_SOURCE_ZIP ? 8 : 2
+
+ Menu_Run_AbbreviateNameIntoBuffer(dir->name, USERMAPENTRYLENGTH);
+
+ const int32_t thisx = object->format[i]->pos.x;
+ const int32_t thisy = y - object->scrollPos[i];
+
+ int32_t const height = object->font[i]->get_yline();
+
+ if (0 <= thisy + height && thisy <= klabs(object->format[i]->bottomcutoff) - object->format[i]->pos.y)
+ {
+ status |= MT_YCenter;
+
+ const int32_t mousex = origin.x + thisx;
+ const int32_t mousey = origin.y + y_upper + thisy + (height>>1);
+
+ vec2_t textdim = Menu_Text(mousex, mousey, object->font[i], tempbuf, status, ydim_upper, ydim_lower);
+
+ if (MOUSEACTIVECONDITIONAL(cm == m_currentMenu && !Menu_MouseOutsideBounds(&m_mousepos, mousex, mousey, textdim.x, object->font[i]->get_yline())))
+ {
+ if (MOUSEWATCHPOINTCONDITIONAL(Menu_MouseOutsideBounds(&m_prevmousepos, mousex, mousey, textdim.x, object->font[i]->get_yline())))
+ {
+ object->findhigh[i] = dir;
+ object->currentList = i;
+
+ Menu_RunInput_FileSelect_MovementVerify(object);
+ }
+
+ if (!m_mousecaught && mousepressstate == Mouse_Released && !Menu_MouseOutsideBounds(&m_mousedownpos, mousex, mousey, textdim.x, object->font[i]->get_yline()))
+ {
+ object->findhigh[i] = dir;
+ object->currentList = i;
+
+ Menu_RunInput_FileSelect_MovementVerify(object);
+
+ m_mousecaught = 1;
+ selected = 1;
+ }
+ }
+ }
+
+ y += object->font[i]->get_yline() + object->getMarginBottom(i);
+ }
+
+ Menu_RunScrollbar(cm, object->format[i], y_upper + totalHeight, &object->scrollPos[i], MenuFileSelect_scrollbar_rightedge[i], origin);
+ }
+ }
+
+ Menu_PreDraw(cm->menuID, NULL, origin);
+
+ if (object->title != NoTitle)
+ Menu_DrawTopBarCaption(object->title, origin);
+
+ if (selected)
+ {
+ Menu_RunInput_FileSelect_Select(object);
+
+ S_PlaySound(PISTOL_BODYHIT);
+
+ m_mousecaught = 1;
+ }
+
+ break;
+ }
+
+ case Panel:
+ {
+ MenuPanel_t *object = (MenuPanel_t*)cm->object;
+
+ Menu_Pre(cm->menuID);
+
+ Menu_PreDrawBackground(cm->menuID, origin);
+
+ if (object->title != NoTitle)
+ Menu_DrawTopBar(origin);
+
+ Menu_PreDraw(cm->menuID, NULL, origin);
+
+ if (object->title != NoTitle)
+ Menu_DrawTopBarCaption(object->title, origin);
+
+ break;
+ }
+
+ case Menu:
+ {
+ int32_t state;
+
+ MenuMenu_t *menu = (MenuMenu_t*)cm->object;
+ MenuEntry_t *currentry = menu->entrylist[menu->currentEntry];
+
+ state = Menu_DetermineSpecialState(currentry);
+
+ if (state != 2)
+ {
+ Menu_Pre(cm->menuID);
+
+ Menu_PreDrawBackground(cm->menuID, origin);
+
+ if (menu->title != NoTitle)
+ Menu_DrawTopBar(origin);
+
+ Menu_PreDraw(cm->menuID, currentry, origin);
+
+ M_RunMenu_Menu(cm, menu, currentry, state, origin);
+ }
+ else
+ {
+ Menu_PreDrawBackground(cm->menuID, origin);
+
+ if (menu->title != NoTitle)
+ Menu_DrawTopBar(origin);
+
+ if (currentry->type == Option)
+ {
+ if (currentry->name)
+ Menu_DrawTopBarCaption(currentry->name, origin);
+
+ Menu_PreOptionListDraw(currentry, origin);
+
+ Menu_RunOptionList(cm, currentry, (MenuOption_t*)currentry->entry, origin);
+ }
+ else if (currentry->type == Custom2Col)
+ {
+ Menu_PreCustom2ColScreenDraw(currentry, origin);
+ }
+ }
+
+ if ((currentry->type != Option || state != 2) && menu->title != NoTitle)
+ Menu_DrawTopBarCaption(menu->title, origin);
+
+ break;
+ }
+ }
+
+#if !defined EDUKE32_TOUCH_DEVICES
+ Menu_Run_MouseReturn(cm, origin);
+#endif
+}
+
+/*
+Note: When menus are exposed to scripting, care will need to be taken so that
+a user cannot define an empty MenuEntryList, or one containing only spacers,
+or else this function will recurse infinitely.
+*/
+static MenuEntry_t *Menu_RunInput_Menu_MovementVerify(MenuMenu_t *menu)
+{
+ return Menu_AdjustForCurrentEntryAssignment(menu);
+}
+
+static MenuEntry_t *Menu_RunInput_Menu_Movement(MenuMenu_t *menu, MenuMovement_t direction)
+{
+ if (menu->numEntries == 1)
+ return menu->entrylist[menu->currentEntry];
+
+ switch (direction)
+ {
+ case MM_End:
+ menu->currentEntry = menu->numEntries;
+ fallthrough__;
+ case MM_Up:
+ do
+ {
+ --menu->currentEntry;
+ if (menu->currentEntry < 0)
+ return Menu_RunInput_Menu_Movement(menu, MM_End);
+ }
+ while (!menu->entrylist[menu->currentEntry] || menu->entrylist[menu->currentEntry]->type == Spacer);
+ break;
+
+ case MM_Home:
+ menu->currentEntry = -1;
+ fallthrough__;
+ case MM_Down:
+ do
+ {
+ ++menu->currentEntry;
+ if (menu->currentEntry >= menu->numEntries)
+ return Menu_RunInput_Menu_Movement(menu, MM_Home);
+ }
+ while (!menu->entrylist[menu->currentEntry] || menu->entrylist[menu->currentEntry]->type == Spacer);
+ break;
+
+ case MM_Swap:
+ menu->currentColumn = !menu->currentColumn;
+ break;
+
+ default:
+ break;
+ }
+
+ return Menu_RunInput_Menu_MovementVerify(menu);
+}
+
+static void Menu_RunInput_EntryLink_Activate(MenuEntry_t *entry)
+{
+ MenuLink_t *link = (MenuLink_t*)entry->entry;
+
+ Menu_EntryLinkActivate(entry);
+
+ Menu_AnimateChange(link->linkID, link->animation);
+}
+
+static void Menu_RunInput_EntryOptionList_MovementVerify(MenuOption_t *object)
+{
+ const int32_t listytop = object->options->menuFormat->pos.y;
+ // assumes height == font->get_yline()!
+ const int32_t unitheight = object->options->getMarginBottom() < 0 ? (-object->options->getMarginBottom() - object->options->font->get_yline()) / object->options->numOptions : (object->options->font->get_yline() + object->options->getMarginBottom());
+ const int32_t ytop = listytop + object->options->currentEntry * unitheight;
+ const int32_t ybottom = ytop + object->options->font->get_yline();
+
+ if (ybottom - object->options->scrollPos > object->options->menuFormat->bottomcutoff)
+ object->options->scrollPos = ybottom - object->options->menuFormat->bottomcutoff;
+ else if (ytop - object->options->scrollPos < listytop)
+ object->options->scrollPos = ytop - listytop;
+}
+
+static void Menu_RunInput_EntryOptionList_Movement(MenuOption_t *object, MenuMovement_t direction)
+{
+ switch (direction)
+ {
+ case MM_Up:
+ --object->options->currentEntry;
+ if (object->options->currentEntry >= 0)
+ break;
+ fallthrough__;
+ case MM_End:
+ object->options->currentEntry = object->options->numOptions-1;
+ break;
+
+ case MM_Down:
+ ++object->options->currentEntry;
+ if (object->options->currentEntry < object->options->numOptions)
+ break;
+ fallthrough__;
+ case MM_Home:
+ object->options->currentEntry = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ Menu_RunInput_EntryOptionList_MovementVerify(object);
+}
+
+static int32_t Menu_RunInput_EntryOption_Modify(MenuEntry_t *entry, MenuOption_t *object, int32_t newValueIndex)
+{
+ int32_t newValue = (object->options->optionValues == NULL) ? newValueIndex : object->options->optionValues[newValueIndex];
+ if (!Menu_EntryOptionModify(entry, newValue))
+ {
+ object->currentOption = newValueIndex;
+
+ if ((int32_t*)object->data != NULL) // NULL implies the functions will handle it
+ *((int32_t*)object->data) = newValue;
+
+ Menu_EntryOptionDidModify(entry);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+static int32_t Menu_RunInput_EntryOption_Movement(MenuEntry_t *entry, MenuOption_t *object, MenuMovement_t direction)
+{
+ int32_t newValueIndex = object->currentOption;
+
+ switch (direction)
+ {
+ case MM_Left:
+ --newValueIndex;
+ if (newValueIndex >= 0)
+ break;
+ fallthrough__;
+ case MM_AllTheWayRight:
+ newValueIndex = object->options->numOptions-1;
+ break;
+
+ case MM_Right:
+ ++newValueIndex;
+ if (newValueIndex < object->options->numOptions)
+ break;
+ fallthrough__;
+ case MM_AllTheWayLeft:
+ newValueIndex = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ return Menu_RunInput_EntryOption_Modify(entry, object, newValueIndex);
+}
+
+static int32_t Menu_RunInput_EntryOption_Activate(MenuEntry_t *entry, MenuOption_t *object)
+{
+ if (object->options->features & 2)
+ return Menu_RunInput_EntryOption_Movement(entry, object, MM_Right);
+ else
+ {
+ object->options->currentEntry = object->currentOption >= 0 ? object->currentOption : 0;
+ Menu_RunInput_EntryOptionList_MovementVerify(object);
+
+ return 0;
+ }
+}
+
+static int32_t Menu_RunInput_EntryOptionList_Activate(MenuEntry_t *entry, MenuOption_t *object)
+{
+ if (!Menu_RunInput_EntryOption_Modify(entry, object, object->options->currentEntry))
+ {
+ object->options->currentEntry = -1;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+static void Menu_RunInput_EntryCustom2Col_Activate(MenuEntry_t *entry)
+{
+ MenuCustom2Col_t *object = (MenuCustom2Col_t*)entry->entry;
+
+ Menu_Custom2ColScreen(/*entry*/);
+
+ object->screenOpen = 1;
+}
+
+static void Menu_RunInput_EntryRangeInt32_MovementVerify(MenuEntry_t *entry, MenuRangeInt32_t *object, int32_t newValue)
+{
+ if (!Menu_EntryRangeInt32Modify(entry, newValue))
+ *object->variable = newValue;
+}
+
+static void Menu_RunInput_EntryRangeInt32_MovementArbitrary(MenuEntry_t *entry, MenuRangeInt32_t *object, int32_t newValue)
+{
+ if (object->flags & EnforceIntervals)
+ {
+ int32_t const range = object->max - object->min;
+ int32_t const maxInterval = object->steps - 1;
+ int32_t const newValueIndex = roundscale(newValue - object->min, maxInterval, range);
+ newValue = newValueIndex * range / maxInterval + object->min;
+ }
+
+ Menu_RunInput_EntryRangeInt32_MovementVerify(entry, object, newValue);
+}
+
+static void Menu_RunInput_EntryRangeInt32_Movement(MenuEntry_t *entry, MenuRangeInt32_t *object, MenuMovement_t direction)
+{
+ int32_t const oldValue = *object->variable;
+ int32_t const range = object->max - object->min;
+ int32_t const maxInterval = object->steps - 1;
+ int32_t newValueIndex = roundscale(oldValue - object->min, maxInterval, range);
+ int32_t const newValueProjected = newValueIndex * range / maxInterval + object->min;
+
+ switch (direction)
+ {
+ case MM_Left:
+ if (newValueProjected >= oldValue)
+ --newValueIndex;
+ if (newValueIndex >= 0)
+ break;
+ fallthrough__;
+ case MM_AllTheWayLeft:
+ newValueIndex = 0;
+ break;
+
+ case MM_Right:
+ if (newValueProjected <= oldValue)
+ ++newValueIndex;
+ if (newValueIndex <= maxInterval)
+ break;
+ fallthrough__;
+ case MM_AllTheWayRight:
+ newValueIndex = maxInterval;
+ break;
+
+ default:
+ break;
+ }
+
+ int32_t const newValue = newValueIndex * range / maxInterval + object->min;
+ Menu_RunInput_EntryRangeInt32_MovementVerify(entry, object, newValue);
+}
+
+static void Menu_RunInput_EntryRangeFloat_MovementVerify(MenuEntry_t *entry, MenuRangeFloat_t *object, float newValue)
+{
+ if (!Menu_EntryRangeFloatModify(entry, newValue))
+ {
+ *object->variable = newValue;
+ Menu_EntryRangeFloatDidModify(entry);
+ }
+}
+
+static void Menu_RunInput_EntryRangeFloat_MovementArbitrary(MenuEntry_t *entry, MenuRangeFloat_t *object, float newValue)
+{
+ if (object->flags & EnforceIntervals)
+ {
+ float const range = object->max - object->min;
+ float const maxInterval = (float)(object->steps - 1);
+ float const newValueIndex = rintf((newValue - object->min) * maxInterval / range);
+ newValue = newValueIndex * range / maxInterval + object->min;
+ }
+
+ Menu_RunInput_EntryRangeFloat_MovementVerify(entry, object, newValue);
+}
+
+static void Menu_RunInput_EntryRangeFloat_Movement(MenuEntry_t *entry, MenuRangeFloat_t *object, MenuMovement_t direction)
+{
+ float const oldValue = *object->variable;
+ float const range = object->max - object->min;
+ float const maxInterval = (float)(object->steps - 1);
+ float newValueIndex = rintf((oldValue - object->min) * maxInterval / range);
+ float const newValueProjected = newValueIndex * range / maxInterval + object->min;
+
+ switch (direction)
+ {
+ case MM_Left:
+ if (newValueProjected >= oldValue)
+ newValueIndex -= 1.f;
+ if (newValueIndex >= 0.f)
+ break;
+ fallthrough__;
+ case MM_AllTheWayLeft:
+ newValueIndex = 0.f;
+ break;
+
+ case MM_Right:
+ if (newValueProjected <= oldValue)
+ newValueIndex += 1.f;
+ if (newValueIndex <= maxInterval)
+ break;
+ fallthrough__;
+ case MM_AllTheWayRight:
+ newValueIndex = maxInterval;
+ break;
+
+ default:
+ break;
+ }
+
+ float const newValue = newValueIndex * range / maxInterval + object->min;
+ Menu_RunInput_EntryRangeFloat_MovementVerify(entry, object, newValue);
+}
+
+#ifdef MENU_ENABLE_RANGEDOUBLE
+static void Menu_RunInput_EntryRangeDouble_MovementVerify(/*MenuEntry_t *entry, */MenuRangeDouble_t *object, double newValue)
+{
+ if (!Menu_EntryRangeDoubleModify(/*entry, newValue*/))
+ *object->variable = newValue;
+}
+
+static void Menu_RunInput_EntryRangeDouble_MovementArbitrary(/*MenuEntry_t *entry, */MenuRangeDouble_t *object, double newValue)
+{
+ if (object->flags & EnforceIntervals)
+ {
+ double const range = object->max - object->min;
+ double const maxInterval = object->steps - 1;
+ double const newValueIndex = rint((newValue - object->min) * maxInterval / range);
+ newValue = newValueIndex * range / maxInterval + object->min;
+ }
+
+ Menu_RunInput_EntryRangeDouble_MovementVerify(/*entry, */object, newValue);
+}
+
+static void Menu_RunInput_EntryRangeDouble_Movement(/*MenuEntry_t *entry, */MenuRangeDouble_t *object, MenuMovement_t direction)
+{
+ double const oldValue = *object->variable;
+ double const range = object->max - object->min;
+ double const maxInterval = object->steps - 1;
+ double newValueIndex = rint((oldValue - object->min) * maxInterval / range);
+ double const newValueProjected = newValueIndex * range / maxInterval + object->min;
+
+ switch (direction)
+ {
+ case MM_Left:
+ if (newValueProjected >= oldValue)
+ newValueIndex -= 1.;
+ if (newValueIndex >= 0.)
+ break;
+ fallthrough__;
+ case MM_AllTheWayLeft:
+ newValueIndex = 0.;
+ break;
+
+ case MM_Right:
+ if (newValueProjected <= oldValue)
+ newValueIndex += 1.;
+ if (newValueIndex <= maxInterval)
+ break;
+ fallthrough__;
+ case MM_AllTheWayRight:
+ newValueIndex = maxInterval;
+ break;
+
+ default:
+ break;
+ }
+
+ double const newValue = newValueIndex * range / maxInterval + object->min;
+ Menu_RunInput_EntryRangeDouble_MovementVerify(/*entry, */object, newValue);
+}
+#endif
+
+static void Menu_RunInput_EntryString_Activate(MenuEntry_t *entry)
+{
+ MenuString_t *object = (MenuString_t*)entry->entry;
+
+ if (object->variable)
+ strncpy(typebuf, object->variable, TYPEBUFSIZE);
+ else
+ typebuf[0] = '\0';
+ object->editfield = typebuf;
+
+ // this limitation is an arbitrary implementation detail
+ if (object->bufsize > TYPEBUFSIZE)
+ object->bufsize = TYPEBUFSIZE;
+
+ Menu_EntryStringActivate(/*entry*/);
+ WithSDL2_StartTextInput();
+}
+
+static void Menu_RunInput_EntryString_Submit(/*MenuEntry_t *entry, */MenuString_t *object)
+{
+ if (!Menu_EntryStringSubmit(/*entry, */object->editfield))
+ {
+ if (object->variable)
+ strncpy(object->variable, object->editfield, object->bufsize);
+ }
+
+ object->editfield = NULL;
+ WithSDL2_StopTextInput();
+}
+
+static void Menu_RunInput_EntryString_Cancel(/*MenuEntry_t *entry, */MenuString_t *object)
+{
+ Menu_EntryStringCancel(/*entry*/);
+
+ object->editfield = NULL;
+ WithSDL2_StopTextInput();
+}
+
+static void Menu_RunInput_FileSelect_MovementVerify(MenuFileSelect_t *object)
+{
+ const int32_t listytop = object->format[object->currentList]->pos.y;
+ const int32_t listybottom = klabs(object->format[object->currentList]->bottomcutoff);
+ const int32_t ytop = listytop + object->findhigh[object->currentList]->type * (object->font[object->currentList]->get_yline() + object->getMarginBottom(object->currentList));
+ const int32_t ybottom = ytop + object->font[object->currentList]->get_yline();
+
+ if (ybottom - object->scrollPos[object->currentList] > listybottom)
+ object->scrollPos[object->currentList] = ybottom - listybottom;
+ else if (ytop - object->scrollPos[object->currentList] < listytop)
+ object->scrollPos[object->currentList] = ytop - listytop;
+}
+
+static void Menu_RunInput_FileSelect_Movement(MenuFileSelect_t *object, MenuMovement_t direction)
+{
+ switch (direction)
+ {
+ case MM_Up:
+ if (!object->findhigh[object->currentList])
+ break;
+ if (object->findhigh[object->currentList]->prev)
+ {
+ object->findhigh[object->currentList] = object->findhigh[object->currentList]->prev;
+ break;
+ }
+ fallthrough__;
+ case MM_End:
+ object->findhigh[object->currentList] = object->findhigh[object->currentList]->userb;
+ break;
+
+ case MM_Down:
+ if (!object->findhigh[object->currentList])
+ break;
+ if (object->findhigh[object->currentList]->next)
+ {
+ object->findhigh[object->currentList] = object->findhigh[object->currentList]->next;
+ break;
+ }
+ fallthrough__;
+ case MM_Home:
+ object->findhigh[object->currentList] = object->findhigh[object->currentList]->usera;
+ break;
+
+ case MM_Swap:
+ object->currentList = !object->currentList;
+ break;
+
+ default:
+ break;
+ }
+
+ Menu_RunInput_FileSelect_MovementVerify(object);
+}
+
+static void Menu_RunInput_FileSelect_Select(MenuFileSelect_t *object)
+{
+ if (!object->findhigh[object->currentList])
+ return;
+
+ Bstrcat(object->destination, object->findhigh[object->currentList]->name);
+
+ if (object->currentList == 0)
+ {
+ Bstrcat(object->destination, "/");
+ Bcorrectfilename(object->destination, 1);
+
+ Menu_FileSelectInit(object);
+ }
+ else
+ {
+ Menu_FileSelect(1);
+ }
+}
+
+static void Menu_RunInput(Menu_t *cm)
+{
+ switch (cm->type)
+ {
+ case Panel:
+ {
+ MenuPanel_t *panel = (MenuPanel_t*)cm->object;
+
+ if (I_ReturnTrigger() || Menu_RunInput_MouseReturn())
+ {
+ I_ReturnTriggerClear();
+ m_mousecaught = 1;
+
+ S_PlaySound(EXITMENUSOUND);
+
+ Menu_AnimateChange(cm->parentID, cm->parentAnimation);
+ }
+ else if (I_PanelUp())
+ {
+ I_PanelUpClear();
+
+ S_PlaySound(KICK_HIT);
+ Menu_AnimateChange(panel->previousID, panel->previousAnimation);
+ }
+ else if (I_PanelDown() || Menu_RunInput_MouseAdvance())
+ {
+ I_PanelDownClear();
+ m_mousecaught = 1;
+
+ S_PlaySound(KICK_HIT);
+ Menu_AnimateChange(panel->nextID, panel->nextAnimation);
+ }
+ break;
+ }
+
+ case TextForm:
+ {
+ MenuTextForm_t *object = (MenuTextForm_t*)cm->object;
+ int32_t hitstate = I_EnterText(object->input, object->bufsize-1, 0);
+
+ if (hitstate == -1 || Menu_RunInput_MouseReturn())
+ {
+ m_mousecaught = 1;
+
+ S_PlaySound(EXITMENUSOUND);
+
+ object->input = NULL;
+
+ Menu_AnimateChange(cm->parentID, cm->parentAnimation);
+ WithSDL2_StopTextInput();
+ }
+ else if (hitstate == 1 || Menu_RunInput_MouseAdvance())
+ {
+ m_mousecaught = 1;
+
+ Menu_TextFormSubmit(object->input);
+
+ object->input = NULL;
+ WithSDL2_StopTextInput();
+ }
+ break;
+ }
+
+ case FileSelect:
+ {
+ MenuFileSelect_t *object = (MenuFileSelect_t*)cm->object;
+
+ if (I_ReturnTrigger() || Menu_RunInput_MouseReturn())
+ {
+ I_ReturnTriggerClear();
+ m_mousecaught = 1;
+
+ S_PlaySound(EXITMENUSOUND);
+
+ object->destination[0] = 0;
+
+ Menu_FileSelect(0);
+
+ Menu_AnimateChange(cm->parentID, MA_Return);
+ }
+ else if (I_AdvanceTrigger())
+ {
+ I_AdvanceTriggerClear();
+
+ Menu_RunInput_FileSelect_Select(object);
+
+ S_PlaySound(PISTOL_BODYHIT);
+ }
+ else if (KB_KeyPressed(sc_Home))
+ {
+ KB_ClearKeyDown(sc_Home);
+
+ Menu_RunInput_FileSelect_Movement(object, MM_Home);
+
+ S_PlaySound(KICK_HIT);
+ }
+ else if (KB_KeyPressed(sc_End))
+ {
+ KB_ClearKeyDown(sc_End);
+
+ Menu_RunInput_FileSelect_Movement(object, MM_End);
+
+ S_PlaySound(KICK_HIT);
+ }
+ else if (KB_KeyPressed(sc_PgUp))
+ {
+ int32_t i;
+
+ CACHE1D_FIND_REC *seeker = object->findhigh[object->currentList];
+
+ KB_ClearKeyDown(sc_PgUp);
+
+ for (i = 0; i < 6; ++i)
+ {
+ if (seeker && seeker->prev)
+ seeker = seeker->prev;
+ }
+
+ if (seeker)
+ {
+ object->findhigh[object->currentList] = seeker;
+
+ Menu_RunInput_FileSelect_MovementVerify(object);
+
+ S_PlaySound(KICK_HIT);
+ }
+ }
+ else if (KB_KeyPressed(sc_PgDn))
+ {
+ int32_t i;
+
+ CACHE1D_FIND_REC *seeker = object->findhigh[object->currentList];
+
+ KB_ClearKeyDown(sc_PgDn);
+
+ for (i = 0; i < 6; ++i)
+ {
+ if (seeker && seeker->next)
+ seeker = seeker->next;
+ }
+
+ if (seeker)
+ {
+ object->findhigh[object->currentList] = seeker;
+
+ Menu_RunInput_FileSelect_MovementVerify(object);
+
+ S_PlaySound(KICK_HIT);
+ }
+ }
+ else if (I_MenuLeft() || I_MenuRight())
+ {
+ I_MenuLeftClear();
+ I_MenuRightClear();
+
+ if ((object->currentList ? object->fnlist.numdirs : object->fnlist.numfiles) > 0)
+ {
+ Menu_RunInput_FileSelect_Movement(object, MM_Swap);
+
+ S_PlaySound(KICK_HIT);
+ }
+ }
+ else if (I_MenuUp())
+ {
+ I_MenuUpClear();
+
+ Menu_RunInput_FileSelect_Movement(object, MM_Up);
+
+ S_PlaySound(KICK_HIT);
+ }
+ else if (I_MenuDown())
+ {
+ I_MenuDownClear();
+
+ Menu_RunInput_FileSelect_Movement(object, MM_Down);
+
+ S_PlaySound(KICK_HIT);
+ }
+ else
+ {
+ // JBF 20040208: seek to first name matching pressed character
+ char ch2, ch;
+ ch = KB_GetCh();
+ if (ch > 0 && ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')))
+ {
+ CACHE1D_FIND_REC *seeker = object->findhigh[object->currentList]->usera;
+ if (ch >= 'a')
+ ch -= ('a'-'A');
+ while (seeker)
+ {
+ ch2 = seeker->name[0];
+ if (ch2 >= 'a' && ch2 <= 'z')
+ ch2 -= ('a'-'A');
+ if (ch2 == ch)
+ break;
+ seeker = seeker->next;
+ }
+ if (seeker)
+ {
+ object->findhigh[object->currentList] = seeker;
+
+ Menu_RunInput_FileSelect_MovementVerify(object);
+
+ S_PlaySound(KICK_HIT);
+ }
+ }
+ }
+
+ Menu_PreInput(NULL);
+ break;
+ }
+
+ case Message:
+ if (I_ReturnTrigger() || Menu_RunInput_MouseReturn())
+ {
+ I_ReturnTriggerClear();
+ m_mousecaught = 1;
+
+ S_PlaySound(EXITMENUSOUND);
+
+ Menu_AnimateChange(cm->parentID, cm->parentAnimation);
+ }
+
+ if (I_CheckAllInput())
+ {
+ MenuMessage_t *message = (MenuMessage_t*)cm->object;
+
+ I_ClearAllInput();
+
+ S_PlaySound(EXITMENUSOUND);
+
+ Menu_AnimateChange(message->linkID, message->animation);
+ }
+
+ Menu_PreInput(NULL);
+ break;
+
+ case Verify:
+ if (I_ReturnTrigger() || KB_KeyPressed(sc_N) || Menu_RunInput_MouseReturn())
+ {
+ I_ReturnTriggerClear();
+ KB_ClearKeyDown(sc_N);
+ m_mousecaught = 1;
+
+ Menu_Verify(0);
+
+ Menu_AnimateChange(cm->parentID, cm->parentAnimation);
+
+ S_PlaySound(EXITMENUSOUND);
+ }
+
+ if (I_AdvanceTrigger() || KB_KeyPressed(sc_Y) || Menu_RunInput_MouseAdvance())
+ {
+ MenuVerify_t *verify = (MenuVerify_t*)cm->object;
+
+ I_AdvanceTriggerClear();
+ KB_ClearKeyDown(sc_Y);
+ m_mousecaught = 1;
+
+ Menu_Verify(1);
+
+ Menu_AnimateChange(verify->linkID, verify->animation);
+
+ S_PlaySound(PISTOL_BODYHIT);
+ }
+
+ Menu_PreInput(NULL);
+ break;
+
+ case Menu:
+ {
+ int32_t state;
+
+ MenuMenu_t *menu = (MenuMenu_t*)cm->object;
+ MenuEntry_t *currentry = menu->entrylist[menu->currentEntry];
+
+ state = Menu_DetermineSpecialState(currentry);
+
+ if (state == 0)
+ {
+ if (currentry != NULL)
+ switch (currentry->type)
+ {
+ case Dummy:
+ case Spacer:
+ break;
+ case Link:
+ if (currentry->flags & MEF_Disabled)
+ break;
+ if (I_AdvanceTrigger())
+ {
+ I_AdvanceTriggerClear();
+
+ Menu_RunInput_EntryLink_Activate(currentry);
+
+ if (g_player[myconnectindex].ps->gm&MODE_MENU) // for skill selection
+ S_PlaySound(PISTOL_BODYHIT);
+ }
+ break;
+ case Option:
+ {
+ MenuOption_t *object = (MenuOption_t*)currentry->entry;
+
+ if (currentry->flags & MEF_Disabled)
+ break;
+
+ if (I_AdvanceTrigger())
+ {
+ I_AdvanceTriggerClear();
+
+ Menu_RunInput_EntryOption_Activate(currentry, object);
+
+ S_PlaySound(PISTOL_BODYHIT);
+ }
+ else if (I_MenuRight())
+ {
+ I_MenuRightClear();
+
+ Menu_RunInput_EntryOption_Movement(currentry, object, MM_Right);
+
+ S_PlaySound(PISTOL_BODYHIT);
+ }
+ else if (I_MenuLeft())
+ {
+ I_MenuLeftClear();
+
+ Menu_RunInput_EntryOption_Movement(currentry, object, MM_Left);
+
+ S_PlaySound(PISTOL_BODYHIT);
+ }
+ }
+ break;
+ case Custom2Col:
+ if (I_MenuLeft() || I_MenuRight())
+ {
+ I_MenuLeftClear();
+ I_MenuRightClear();
+
+ Menu_RunInput_Menu_Movement(menu, MM_Swap);
+
+ S_PlaySound(KICK_HIT);
+ }
+
+ if (currentry->flags & MEF_Disabled)
+ break;
+
+ if (I_AdvanceTrigger())
+ {
+ I_AdvanceTriggerClear();
+
+ Menu_RunInput_EntryCustom2Col_Activate(currentry);
+
+ S_PlaySound(PISTOL_BODYHIT);
+ }
+ break;
+ case RangeInt32:
+ {
+ MenuRangeInt32_t *object = (MenuRangeInt32_t*)currentry->entry;
+
+ if (currentry->flags & MEF_Disabled)
+ break;
+
+ if (I_SliderLeft())
+ {
+ I_SliderLeftClear();
+
+ Menu_RunInput_EntryRangeInt32_Movement(currentry, object, MM_Left);
+
+ S_PlaySound(KICK_HIT);
+ }
+ else if (I_SliderRight())
+ {
+ I_SliderRightClear();
+
+ Menu_RunInput_EntryRangeInt32_Movement(currentry, object, MM_Right);
+
+ S_PlaySound(KICK_HIT);
+ }
+ break;
+ }
+ case RangeFloat:
+ {
+ MenuRangeFloat_t *object = (MenuRangeFloat_t*)currentry->entry;
+
+ if (currentry->flags & MEF_Disabled)
+ break;
+
+ if (I_SliderLeft())
+ {
+ I_SliderLeftClear();
+
+ Menu_RunInput_EntryRangeFloat_Movement(currentry, object, MM_Left);
+
+ S_PlaySound(KICK_HIT);
+ }
+ else if (I_SliderRight())
+ {
+ I_SliderRightClear();
+
+ Menu_RunInput_EntryRangeFloat_Movement(currentry, object, MM_Right);
+
+ S_PlaySound(KICK_HIT);
+ }
+ break;
+ }
+#ifdef MENU_ENABLE_RANGEDOUBLE
+ case RangeDouble:
+ {
+ MenuRangeDouble_t *object = (MenuRangeDouble_t*)currentry->entry;
+
+ if (currentry->flags & MEF_Disabled)
+ break;
+
+ if (I_SliderLeft())
+ {
+ I_SliderLeftClear();
+
+ Menu_RunInput_EntryRangeDouble_Movement(/*currentry, */object, MM_Left);
+
+ S_PlaySound(KICK_HIT);
+ }
+ else if (I_SliderRight())
+ {
+ I_SliderRightClear();
+
+ Menu_RunInput_EntryRangeDouble_Movement(/*currentry, */object, MM_Right);
+
+ S_PlaySound(KICK_HIT);
+ }
+ break;
+ }
+#endif
+
+ case String:
+ {
+ if (currentry->flags & MEF_Disabled)
+ break;
+
+ if (I_AdvanceTrigger())
+ {
+ I_AdvanceTriggerClear();
+
+ Menu_RunInput_EntryString_Activate(currentry);
+
+ S_PlaySound(PISTOL_BODYHIT);
+ }
+
+ break;
+ }
+ }
+
+ if (I_ReturnTrigger() || I_EscapeTrigger() || Menu_RunInput_MouseReturn())
+ {
+ I_ReturnTriggerClear();
+ I_EscapeTriggerClear();
+ m_mousecaught = 1;
+
+ if (cm->parentID != MENU_CLOSE || (g_player[myconnectindex].ps->gm & MODE_GAME))
+ S_PlaySound(EXITMENUSOUND);
+
+ Menu_AnimateChange(cm->parentID, cm->parentAnimation);
+ }
+ else if (KB_KeyPressed(sc_Home))
+ {
+ KB_ClearKeyDown(sc_Home);
+
+ S_PlaySound(KICK_HIT);
+
+ currentry = Menu_RunInput_Menu_Movement(menu, MM_Home);
+ }
+ else if (KB_KeyPressed(sc_End))
+ {
+ KB_ClearKeyDown(sc_End);
+
+ S_PlaySound(KICK_HIT);
+
+ currentry = Menu_RunInput_Menu_Movement(menu, MM_End);
+ }
+ else if (I_MenuUp())
+ {
+ I_MenuUpClear();
+
+ S_PlaySound(KICK_HIT);
+
+ currentry = Menu_RunInput_Menu_Movement(menu, MM_Up);
+ }
+ else if (I_MenuDown())
+ {
+ I_MenuDownClear();
+
+ S_PlaySound(KICK_HIT);
+
+ currentry = Menu_RunInput_Menu_Movement(menu, MM_Down);
+ }
+
+ if (currentry != NULL && !(currentry->flags & MEF_Disabled))
+ Menu_PreInput(currentry);
+ }
+ else if (state == 1)
+ {
+ if (currentry->type == String)
+ {
+ MenuString_t *object = (MenuString_t*)currentry->entry;
+
+ int32_t hitstate = I_EnterText(object->editfield, object->bufsize-1, object->flags);
+
+ if (hitstate == -1 || Menu_RunInput_MouseReturn())
+ {
+ m_mousecaught = 1;
+
+ Menu_RunInput_EntryString_Cancel(/*currentry, */object);
+
+ S_PlaySound(EXITMENUSOUND);
+ }
+ else if (hitstate == 1)
+ {
+ Menu_RunInput_EntryString_Submit(/*currentry, */object);
+
+ S_PlaySound(PISTOL_BODYHIT);
+ }
+ }
+ }
+ else if (state == 2)
+ {
+ if (currentry->type == Option)
+ {
+ MenuOption_t *object = (MenuOption_t*)currentry->entry;
+
+ if (I_ReturnTrigger() || Menu_RunInput_MouseReturn())
+ {
+ I_ReturnTriggerClear();
+ m_mousecaught = 1;
+
+ S_PlaySound(EXITMENUSOUND);
+
+ object->options->currentEntry = -1;
+ }
+ else if (I_AdvanceTrigger())
+ {
+ I_AdvanceTriggerClear();
+
+ if (!Menu_RunInput_EntryOptionList_Activate(currentry, object))
+ S_PlaySound(PISTOL_BODYHIT);
+ }
+ else if (KB_KeyPressed(sc_Home))
+ {
+ KB_ClearKeyDown(sc_Home);
+
+ S_PlaySound(KICK_HIT);
+
+ Menu_RunInput_EntryOptionList_Movement(object, MM_Home);
+ }
+ else if (KB_KeyPressed(sc_End))
+ {
+ KB_ClearKeyDown(sc_End);
+
+ S_PlaySound(KICK_HIT);
+
+ Menu_RunInput_EntryOptionList_Movement(object, MM_End);
+ }
+ else if (I_MenuUp())
+ {
+ I_MenuUpClear();
+
+ S_PlaySound(KICK_HIT);
+
+ Menu_RunInput_EntryOptionList_Movement(object, MM_Up);
+ }
+ else if (I_MenuDown())
+ {
+ I_MenuDownClear();
+
+ S_PlaySound(KICK_HIT);
+
+ Menu_RunInput_EntryOptionList_Movement(object, MM_Down);
+ }
+ }
+ else if (currentry->type == Custom2Col)
+ {
+ if (I_EscapeTrigger() || Menu_RunInput_MouseReturn())
+ {
+ I_EscapeTriggerClear();
+ m_mousecaught = 1;
+
+ S_PlaySound(EXITMENUSOUND);
+
+ ((MenuCustom2Col_t*)currentry->entry)->screenOpen = 0;
+ }
+ else if (Menu_PreCustom2ColScreen(currentry))
+ ((MenuCustom2Col_t*)currentry->entry)->screenOpen = 0;
+ }
+ }
+
+ break;
+ }
+ }
+}
+
+// This function MUST NOT RECURSE. That is why Menu_Run is separate.
+void M_DisplayMenus(void)
+{
+ vec2_t origin = { 0, 0 }, previousOrigin = { 0, 0 };
+
+ Net_GetPackets();
+
+ if ((g_player[myconnectindex].ps->gm&MODE_MENU) == 0)
+ {
+ walock[TILE_LOADSHOT] = 1;
+ return;
+ }
+
+ if (!Menu_IsTextInput(m_currentMenu) && KB_KeyPressed(sc_Q))
+ Menu_AnimateChange(MENU_QUIT, MA_Advance);
+
+ int32_t mousestatus = readmouseabsxy(&m_mousepos, &mouseabs);
+ if (mousestatus && mousepressstate == Mouse_Pressed)
+ m_mousedownpos = m_mousepos;
+
+ Menu_RunInput(m_currentMenu);
+
+ g_player[myconnectindex].ps->gm &= (0xff-MODE_TYPE);
+ // g_player[myconnectindex].ps->fta = 0;
+
+ int32_t const backgroundOK = Menu_BlackTranslucentBackgroundOK(g_currentMenu);
+
+ // need EVENT_DISPLAYMENUBACKGROUND here
+
+ if (!KXDWN && ((g_player[myconnectindex].ps->gm&MODE_GAME) || ud.recstat==2) && backgroundOK)
+ fade_screen_black(1);
+
+ if (Menu_UpdateScreenOK(g_currentMenu))
+ G_UpdateScreenArea();
+
+#if !defined EDUKE32_TOUCH_DEVICES
+ if (m_menuchange_watchpoint > 0)
+ m_menuchange_watchpoint++;
+#endif
+
+ if (m_parentMenu)
+ {
+ ud.m_origin = origin;
+ VM_OnEventWithReturn(EVENT_DISPLAYINACTIVEMENU, g_player[screenpeek].ps->i, screenpeek, m_parentMenu->menuID);
+ origin = ud.m_origin;
+ }
+
+ // Determine animation values.
+ if (totalclock < m_animation.start + m_animation.length)
+ {
+ const int32_t screenwidth = scale(240<<16, xdim, ydim);
+
+ origin.x = mulscale15(screenwidth, m_animation.in(&m_animation));
+ previousOrigin.x = mulscale15(screenwidth, m_animation.out(&m_animation));
+
+ ud.m_origin = previousOrigin;
+ VM_OnEventWithReturn(EVENT_DISPLAYINACTIVEMENU, g_player[screenpeek].ps->i, screenpeek, m_animation.previous->menuID);
+ previousOrigin = ud.m_origin;
+ }
+
+ ud.m_origin = origin;
+ VM_OnEventWithReturn(EVENT_DISPLAYMENU, g_player[screenpeek].ps->i, screenpeek, g_currentMenu);
+ origin = ud.m_origin;
+
+ if (m_parentMenu && backgroundOK)
+ {
+ Menu_Run(m_parentMenu, origin);
+ }
+
+ // hack; need EVENT_DISPLAYMENUBACKGROUND above
+ if (KXDWN && ((g_player[myconnectindex].ps->gm&MODE_GAME) || ud.recstat==2 || m_parentMenu != NULL) && backgroundOK)
+ fade_screen_black(1);
+
+ // Display the menu, with a transition animation if applicable.
+ if (totalclock < m_animation.start + m_animation.length)
+ {
+ Menu_Run(m_animation.previous, previousOrigin);
+ Menu_Run(m_animation.current, origin);
+ }
+ else
+ Menu_Run(m_currentMenu, origin);
+
+#if !defined EDUKE32_TOUCH_DEVICES
+ if (m_menuchange_watchpoint >= 3)
+ m_menuchange_watchpoint = 0;
+#endif
+
+ if (m_parentMenu)
+ {
+ ud.m_origin = origin;
+ VM_OnEventWithReturn(EVENT_DISPLAYINACTIVEMENUREST, g_player[screenpeek].ps->i, screenpeek, m_parentMenu->menuID);
+ }
+
+ if (totalclock < m_animation.start + m_animation.length)
+ {
+ ud.m_origin = previousOrigin;
+ VM_OnEventWithReturn(EVENT_DISPLAYINACTIVEMENUREST, g_player[screenpeek].ps->i, screenpeek, m_animation.previous->menuID);
+ }
+
+ ud.m_origin = origin;
+ VM_OnEventWithReturn(EVENT_DISPLAYMENUREST, g_player[screenpeek].ps->i, screenpeek, g_currentMenu);
+
+#if !defined EDUKE32_TOUCH_DEVICES
+ if (tilesiz[CROSSHAIR].x > 0 && mousestatus)
+#else
+ if (mousestatus)
+#endif
+ {
+#if !defined EDUKE32_TOUCH_DEVICES
+ if (!MOUSEACTIVECONDITION)
+ m_mousewake_watchpoint = 1;
+#endif
+
+ if (MOUSEACTIVECONDITIONAL(mousepressstateadvance()) || m_mousepos.x != m_prevmousepos.x || m_mousepos.y != m_prevmousepos.y)
+ {
+ m_prevmousepos = m_mousepos;
+ m_mouselastactivity = totalclock;
+ }
+#if !defined EDUKE32_TOUCH_DEVICES
+ else
+ m_mousewake_watchpoint = 0;
+#endif
+
+ m_mousecaught = 0;
+ }
+ else
+ {
+ m_mouselastactivity = -M_MOUSETIMEOUT;
+
+#if !defined EDUKE32_TOUCH_DEVICES
+ m_mousewake_watchpoint = 0;
+#endif
+ }
+
+#ifndef EDUKE32_TOUCH_DEVICES
+ // Display the mouse cursor, except on touch devices.
+ if (MOUSEACTIVECONDITION)
+ {
+ int32_t a = VM_OnEventWithReturn(EVENT_DISPLAYCURSOR, g_player[screenpeek].ps->i, screenpeek, CROSSHAIR);
+
+ if ((unsigned) a < MAXTILES)
+ rotatesprite_fs_alpha(m_mousepos.x, m_mousepos.y, 65536, 0, a, 0, CROSSHAIR_PAL, 2|1, MOUSEALPHA);
+ }
+ else
+ mousepressstate = Mouse_Idle;
+#endif
+
+ if ((g_player[myconnectindex].ps->gm&MODE_MENU) != MODE_MENU)
+ {
+ G_UpdateScreenArea();
+ CAMERACLOCK = totalclock;
+ CAMERADIST = 65536;
+ }
+}
diff --git a/patch/osdcmds.cpp b/patch/osdcmds.cpp
new file mode 100644
index 0000000..34cde37
--- /dev/null
+++ b/patch/osdcmds.cpp
@@ -0,0 +1,1773 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 2010 EDuke32 developers and contributors
+
+This file is part of EDuke32.
+
+EDuke32 is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License version 2
+as published by the Free Software Foundation.
+
+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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+//-------------------------------------------------------------------------
+
+#include "duke3d.h"
+#include "osdcmds.h"
+#include "menus.h"
+#include "osdfuncs.h"
+#include "demo.h" // g_firstDemoFile[]
+#include "cheats.h"
+#include "sbar.h"
+
+#ifdef LUNATIC
+# include "lunatic_game.h"
+#endif
+
+#ifdef EDUKE32_TOUCH_DEVICES
+#include "in_android.h"
+#endif
+
+struct osdcmd_cheatsinfo osdcmd_cheatsinfo_stat;
+float r_ambientlight = 1.0, r_ambientlightrecip = 1.0;
+
+uint32_t cl_cheatmask;
+
+static inline int32_t osdcmd_quit(osdfuncparm_t const * const UNUSED(parm))
+{
+ UNREFERENCED_CONST_PARAMETER(parm);
+ OSD_ShowDisplay(0);
+ G_GameQuit();
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_changelevel(osdfuncparm_t const * const parm)
+{
+ int32_t volume=0,level;
+ char *p;
+
+ if (!VOLUMEONE)
+ {
+ if (parm->numparms != 2) return OSDCMD_SHOWHELP;
+
+ volume = strtol(parm->parms[0], &p, 10) - 1;
+ if (p[0]) return OSDCMD_SHOWHELP;
+ level = strtol(parm->parms[1], &p, 10) - 1;
+ if (p[0]) return OSDCMD_SHOWHELP;
+ }
+ else
+ {
+ if (parm->numparms != 1) return OSDCMD_SHOWHELP;
+
+ level = strtol(parm->parms[0], &p, 10) - 1;
+ if (p[0]) return OSDCMD_SHOWHELP;
+ }
+
+ if (volume < 0) return OSDCMD_SHOWHELP;
+ if (level < 0) return OSDCMD_SHOWHELP;
+
+ if (!VOLUMEONE)
+ {
+ if (volume > g_volumeCnt)
+ {
+ OSD_Printf("changelevel: invalid volume number (range 1-%d)\n",g_volumeCnt);
+ return OSDCMD_OK;
+ }
+ }
+
+ if (level > MAXLEVELS || g_mapInfo[volume *MAXLEVELS+level].filename == NULL)
+ {
+ OSD_Printf("changelevel: invalid level number\n");
+ return OSDCMD_SHOWHELP;
+ }
+
+ if (numplayers > 1)
+ {
+ /*
+ if (g_netServer)
+ Net_NewGame(volume,level);
+ else if (voting == -1)
+ {
+ ud.m_volume_number = volume;
+ ud.m_level_number = level;
+
+ if (g_player[myconnectindex].ps->i)
+ {
+ int32_t i;
+
+ for (i=0; i<MAXPLAYERS; i++)
+ {
+ g_player[i].vote = 0;
+ g_player[i].gotvote = 0;
+ }
+
+ g_player[myconnectindex].vote = g_player[myconnectindex].gotvote = 1;
+
+ voting = myconnectindex;
+
+ tempbuf[0] = PACKET_MAP_VOTE_INITIATE;
+ tempbuf[1] = myconnectindex;
+ tempbuf[2] = ud.m_volume_number;
+ tempbuf[3] = ud.m_level_number;
+
+ enet_peer_send(g_netClientPeer, CHAN_GAMESTATE, enet_packet_create(tempbuf, 4, ENET_PACKET_FLAG_RELIABLE));
+ }
+ if ((g_gametypeFlags[ud.m_coop] & GAMETYPE_PLAYERSFRIENDLY) && !(g_gametypeFlags[ud.m_coop] & GAMETYPE_TDM))
+ ud.m_noexits = 0;
+
+ M_OpenMenu(myconnectindex);
+ Menu_Change(MENU_NETWAITVOTES);
+ }
+ */
+ return OSDCMD_OK;
+ }
+ if (g_player[myconnectindex].ps->gm & MODE_GAME)
+ {
+ // in-game behave like a cheat
+ osdcmd_cheatsinfo_stat.cheatnum = CHEAT_SCOTTY;
+ osdcmd_cheatsinfo_stat.volume = volume;
+ osdcmd_cheatsinfo_stat.level = level;
+ }
+ else
+ {
+ // out-of-game behave like a menu command
+ osdcmd_cheatsinfo_stat.cheatnum = -1;
+
+ ud.m_volume_number = volume;
+ ud.m_level_number = level;
+
+ ud.m_monsters_off = 0;
+ ud.monsters_off = 0;
+
+ ud.m_respawn_items = 0;
+ ud.m_respawn_inventory = 0;
+
+ ud.multimode = 1;
+
+ G_NewGame_EnterLevel();
+ }
+
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_map(osdfuncparm_t const * const parm)
+{
+ int32_t i;
+ char filename[BMAX_PATH];
+
+ const int32_t wildcardp = parm->numparms==1 &&
+ (Bstrchr(parm->parms[0], '*') != NULL);
+
+ if (parm->numparms != 1 || wildcardp)
+ {
+ CACHE1D_FIND_REC *r;
+ fnlist_t fnlist = FNLIST_INITIALIZER;
+ int32_t maxwidth = 0;
+
+ if (wildcardp)
+ maybe_append_ext(filename, sizeof(filename), parm->parms[0], ".map");
+ else
+ Bstrcpy(filename, "*.MAP");
+
+ fnlist_getnames(&fnlist, "/", filename, -1, 0);
+
+ for (r=fnlist.findfiles; r; r=r->next)
+ maxwidth = max((unsigned)maxwidth, Bstrlen(r->name));
+
+ if (maxwidth > 0)
+ {
+ int32_t x = 0, count = 0;
+ maxwidth += 3;
+ OSD_Printf(OSDTEXT_RED "Map listing:\n");
+ for (r=fnlist.findfiles; r; r=r->next)
+ {
+ OSD_Printf("%-*s",maxwidth,r->name);
+ x += maxwidth;
+ count++;
+ if (x > OSD_GetCols() - maxwidth)
+ {
+ x = 0;
+ OSD_Printf("\n");
+ }
+ }
+ if (x) OSD_Printf("\n");
+ OSD_Printf(OSDTEXT_RED "Found %d maps\n", fnlist.numfiles);
+ }
+
+ fnlist_clearnames(&fnlist);
+
+ return OSDCMD_SHOWHELP;
+ }
+
+ maybe_append_ext(filename, sizeof(filename), parm->parms[0], ".map");
+
+ if ((i = kopen4loadfrommod(filename,0)) < 0)
+ {
+ OSD_Printf(OSD_ERROR "map: file \"%s\" not found.\n", filename);
+ return OSDCMD_OK;
+ }
+ kclose(i);
+
+ boardfilename[0] = '/';
+ boardfilename[1] = 0;
+ strcat(boardfilename, filename);
+
+ if (numplayers > 1)
+ {
+ /*
+ if (g_netServer)
+ {
+ Net_SendUserMapName();
+ ud.m_volume_number = 0;
+ ud.m_level_number = 7;
+ Net_NewGame(ud.m_volume_number, ud.m_level_number);
+ }
+ else if (voting == -1)
+ {
+ Net_SendUserMapName();
+
+ ud.m_volume_number = 0;
+ ud.m_level_number = 7;
+
+ if (g_player[myconnectindex].ps->i)
+ {
+ int32_t i;
+
+ for (i=0; i<MAXPLAYERS; i++)
+ {
+ g_player[i].vote = 0;
+ g_player[i].gotvote = 0;
+ }
+
+ g_player[myconnectindex].vote = g_player[myconnectindex].gotvote = 1;
+ voting = myconnectindex;
+
+ tempbuf[0] = PACKET_MAP_VOTE_INITIATE;
+ tempbuf[1] = myconnectindex;
+ tempbuf[2] = ud.m_volume_number;
+ tempbuf[3] = ud.m_level_number;
+
+ enet_peer_send(g_netClientPeer, CHAN_GAMESTATE, enet_packet_create(tempbuf, 4, ENET_PACKET_FLAG_RELIABLE));
+ }
+ if ((g_gametypeFlags[ud.m_coop] & GAMETYPE_PLAYERSFRIENDLY) && !(g_gametypeFlags[ud.m_coop] & GAMETYPE_TDM))
+ ud.m_noexits = 0;
+
+ M_OpenMenu(myconnectindex);
+ Menu_Change(MENU_NETWAITVOTES);
+ }
+ */
+ return OSDCMD_OK;
+ }
+
+ osdcmd_cheatsinfo_stat.cheatnum = -1;
+ ud.m_volume_number = 0;
+ ud.m_level_number = 7;
+
+ ud.m_monsters_off = ud.monsters_off = 0;
+
+ ud.m_respawn_items = 0;
+ ud.m_respawn_inventory = 0;
+
+ ud.multimode = 1;
+
+ G_NewGame_EnterLevel();
+
+ return OSDCMD_OK;
+}
+
+// demo <demonum or demofn> [<prof>]
+//
+// To profile a demo ("timedemo mode"), <prof> can be given in the range 0-8,
+// which will start to replay it as fast as possible, rendering <prof> frames
+// for each gametic.
+//
+// Notes:
+// * The demos should be recorded with demorec_diffs set to 0, so that the
+// game state updates are actually computed.
+// * Currently, the profiling can only be aborted on SDL 1.2 builds by
+// pressing any key.
+// * With <prof> greater than 1, interpolation should be calculated properly,
+// though this has not been verified by looking at the frames.
+// * When testing whether a change in the source has an effect on performance,
+// the variance of the run times MUST be taken into account (that is, the
+// replaying must be performed multiple times for the old and new versions,
+// etc.)
+static int32_t osdcmd_demo(osdfuncparm_t const * const parm)
+{
+ if (numplayers > 1)
+ {
+ OSD_Printf("Command not allowed in multiplayer\n");
+ return OSDCMD_OK;
+ }
+
+ if (g_player[myconnectindex].ps->gm & MODE_GAME)
+ {
+ OSD_Printf("demo: Must not be in a game.\n");
+ return OSDCMD_OK;
+ }
+
+ if (parm->numparms != 1 && parm->numparms != 2)
+ return OSDCMD_SHOWHELP;
+
+ {
+ int32_t prof = parm->numparms==2 ? Batoi(parm->parms[1]) : -1;
+
+ Demo_SetFirst(parm->parms[0]);
+ Demo_PlayFirst(clamp(prof, -1, 8)+1, 0);
+ }
+
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_activatecheat(osdfuncparm_t const * const parm)
+{
+ if (parm->numparms != 1)
+ return OSDCMD_SHOWHELP;
+
+ if (numplayers == 1 && g_player[myconnectindex].ps->gm & MODE_GAME)
+ osdcmd_cheatsinfo_stat.cheatnum = Batoi(parm->parms[0]);
+ else
+ OSD_Printf("activatecheat: Not in a single-player game.\n");
+
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_god(osdfuncparm_t const * const UNUSED(parm))
+{
+ UNREFERENCED_CONST_PARAMETER(parm);
+ if (numplayers == 1 && g_player[myconnectindex].ps->gm & MODE_GAME)
+ osdcmd_cheatsinfo_stat.cheatnum = CHEAT_CORNHOLIO;
+ else
+ OSD_Printf("god: Not in a single-player game.\n");
+
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_noclip(osdfuncparm_t const * const UNUSED(parm))
+{
+ UNREFERENCED_CONST_PARAMETER(parm);
+
+ if (numplayers == 1 && g_player[myconnectindex].ps->gm & MODE_GAME)
+ {
+ osdcmd_cheatsinfo_stat.cheatnum = CHEAT_CLIP;
+ }
+ else
+ {
+ OSD_Printf("noclip: Not in a single-player game.\n");
+ }
+
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_restartsound(osdfuncparm_t const * const UNUSED(parm))
+{
+ UNREFERENCED_CONST_PARAMETER(parm);
+ S_SoundShutdown();
+ S_MusicShutdown();
+
+ S_MusicStartup();
+ S_SoundStartup();
+
+ FX_StopAllSounds();
+ S_ClearSoundLocks();
+
+ if (ud.config.MusicToggle)
+ S_RestartMusic();
+
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_music(osdfuncparm_t const * const parm)
+{
+ if (parm->numparms == 1)
+ {
+ int32_t sel = G_GetMusicIdx(parm->parms[0]);
+
+ if (sel == -1)
+ return OSDCMD_SHOWHELP;
+
+ if (sel == -2)
+ {
+ OSD_Printf("%s is not a valid episode/level number pair\n", parm->parms[0]);
+ return OSDCMD_OK;
+ }
+
+ if (g_mapInfo[sel].musicfn != NULL)
+ {
+ g_musicIndex = sel;
+ G_StartMusic();
+ }
+ else
+ {
+ OSD_Printf("No music defined for %s\n", parm->parms[0]);
+ }
+
+ return OSDCMD_OK;
+ }
+
+ return OSDCMD_SHOWHELP;
+}
+
+int32_t osdcmd_restartvid(osdfuncparm_t const * const UNUSED(parm))
+{
+ UNREFERENCED_CONST_PARAMETER(parm);
+ resetvideomode();
+ if (setgamemode(ud.config.ScreenMode,ud.config.ScreenWidth,ud.config.ScreenHeight,ud.config.ScreenBPP))
+ G_GameExit("restartvid: Reset failed...\n");
+ onvideomodechange(ud.config.ScreenBPP>8);
+ G_UpdateScreenArea();
+
+ return OSDCMD_OK;
+}
+
+int32_t osdcmd_restartmap(osdfuncparm_t const * const UNUSED(parm))
+{
+ UNREFERENCED_CONST_PARAMETER(parm);
+
+ if (g_player[myconnectindex].ps->gm & MODE_GAME && ud.multimode == 1)
+ g_player[myconnectindex].ps->gm = MODE_RESTART;
+
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_vidmode(osdfuncparm_t const * const parm)
+{
+ int32_t newbpp = ud.config.ScreenBPP, newwidth = ud.config.ScreenWidth,
+ newheight = ud.config.ScreenHeight, newfs = ud.config.ScreenMode;
+ int32_t tmp;
+
+ if (parm->numparms < 1 || parm->numparms > 4) return OSDCMD_SHOWHELP;
+
+ switch (parm->numparms)
+ {
+ case 1: // bpp switch
+ tmp = Batol(parm->parms[0]);
+ if (!(tmp==8 || tmp==16 || tmp==32))
+ return OSDCMD_SHOWHELP;
+ newbpp = tmp;
+ break;
+ case 2: // res switch
+ newwidth = Batol(parm->parms[0]);
+ newheight = Batol(parm->parms[1]);
+ break;
+ case 3: // res & bpp switch
+ case 4:
+ newwidth = Batol(parm->parms[0]);
+ newheight = Batol(parm->parms[1]);
+ tmp = Batol(parm->parms[2]);
+ if (!(tmp==8 || tmp==16 || tmp==32))
+ return OSDCMD_SHOWHELP;
+ newbpp = tmp;
+ if (parm->numparms == 4)
+ newfs = (Batol(parm->parms[3]) != 0);
+ break;
+ }
+
+ if (setgamemode(newfs,newwidth,newheight,newbpp))
+ {
+ initprintf("vidmode: Mode change failed!\n");
+ if (setgamemode(ud.config.ScreenMode, ud.config.ScreenWidth, ud.config.ScreenHeight, ud.config.ScreenBPP))
+ G_GameExit("vidmode: Reset failed!\n");
+ }
+ ud.config.ScreenBPP = newbpp;
+ ud.config.ScreenWidth = newwidth;
+ ud.config.ScreenHeight = newheight;
+ ud.config.ScreenMode = newfs;
+ onvideomodechange(ud.config.ScreenBPP>8);
+ G_UpdateScreenArea();
+ return OSDCMD_OK;
+}
+
+#ifdef LUNATIC
+// Returns: INT32_MIN if no such CON label, its value else.
+LUNATIC_CB int32_t (*El_GetLabelValue)(const char *label);
+#endif
+
+static int32_t osdcmd_spawn(osdfuncparm_t const * const parm)
+{
+ int32_t picnum = 0;
+ uint16_t cstat=0;
+ char pal=0;
+ int16_t ang=0;
+ int16_t set=0, idx;
+ vec3_t vect;
+
+ if (numplayers > 1 || !(g_player[myconnectindex].ps->gm & MODE_GAME))
+ {
+ OSD_Printf("spawn: Can't spawn sprites in multiplayer games or demos\n");
+ return OSDCMD_OK;
+ }
+
+ switch (parm->numparms)
+ {
+ case 7: // x,y,z
+ vect.x = Batol(parm->parms[4]);
+ vect.y = Batol(parm->parms[5]);
+ vect.z = Batol(parm->parms[6]);
+ set |= 8;
+ fallthrough__;
+ case 4: // ang
+ ang = Batol(parm->parms[3]) & 2047;
+ set |= 4;
+ fallthrough__;
+ case 3: // cstat
+ cstat = (uint16_t)Batol(parm->parms[2]);
+ set |= 2;
+ fallthrough__;
+ case 2: // pal
+ pal = (uint8_t)Batol(parm->parms[1]);
+ set |= 1;
+ fallthrough__;
+ case 1: // tile number
+ if (isdigit(parm->parms[0][0]))
+ {
+ picnum = Batol(parm->parms[0]);
+ }
+ else
+ {
+ int32_t i;
+#ifdef LUNATIC
+ i = g_labelCnt;
+ picnum = El_GetLabelValue(parm->parms[0]);
+ if (picnum != INT32_MIN)
+ i = !i;
+#else
+ int32_t j;
+
+ for (j=0; j<2; j++)
+ {
+ for (i=0; i<g_labelCnt; i++)
+ {
+ if ((j == 0 && !Bstrcmp(label+(i<<6), parm->parms[0])) ||
+ (j == 1 && !Bstrcasecmp(label+(i<<6), parm->parms[0])))
+ {
+ picnum = labelcode[i];
+ break;
+ }
+ }
+
+ if (i < g_labelCnt)
+ break;
+ }
+#endif
+ if (i==g_labelCnt)
+ {
+ OSD_Printf("spawn: Invalid tile label given\n");
+ return OSDCMD_OK;
+ }
+ }
+
+ if ((uint32_t)picnum >= MAXUSERTILES)
+ {
+ OSD_Printf("spawn: Invalid tile number\n");
+ return OSDCMD_OK;
+ }
+ break;
+
+ default:
+ return OSDCMD_SHOWHELP;
+ }
+
+ idx = A_Spawn(g_player[myconnectindex].ps->i, picnum);
+ if (set & 1) sprite[idx].pal = (uint8_t)pal;
+ if (set & 2) sprite[idx].cstat = (int16_t)cstat;
+ if (set & 4) sprite[idx].ang = ang;
+ if (set & 8)
+ {
+ if (setsprite(idx, &vect) < 0)
+ {
+ OSD_Printf("spawn: Sprite can't be spawned into null space\n");
+ A_DeleteSprite(idx);
+ }
+ }
+
+ return OSDCMD_OK;
+}
+
+#if !defined LUNATIC
+static int32_t osdcmd_setvar(osdfuncparm_t const * const parm)
+{
+ int32_t i, varval;
+ char varname[256];
+
+ if (parm->numparms != 2) return OSDCMD_SHOWHELP;
+
+ if (numplayers > 1)
+ {
+ OSD_Printf("Command not allowed in multiplayer\n");
+ return OSDCMD_OK;
+ }
+
+ strcpy(varname,parm->parms[1]);
+ varval = Batol(varname);
+ i = hash_find(&h_gamevars,varname);
+ if (i >= 0)
+ varval=Gv_GetVar(i, g_player[screenpeek].ps->i, screenpeek);
+
+ strcpy(varname,parm->parms[0]);
+ i = hash_find(&h_gamevars,varname);
+ if (i >= 0)
+ Gv_SetVar(i, varval, g_player[screenpeek].ps->i, screenpeek);
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_addlogvar(osdfuncparm_t const * const parm)
+{
+ int32_t i;
+ char varname[256];
+
+ if (parm->numparms != 1) return OSDCMD_SHOWHELP;
+
+ if (numplayers > 1)
+ {
+ OSD_Printf("Command not allowed in multiplayer\n");
+ return OSDCMD_OK;
+ }
+
+ strcpy(varname,parm->parms[0]);
+ i = hash_find(&h_gamevars,varname);
+ if (i >= 0)
+ OSD_Printf("%s = %d\n", varname, Gv_GetVar(i, g_player[screenpeek].ps->i, screenpeek));
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_setactorvar(osdfuncparm_t const * const parm)
+{
+ int32_t i, varval, ID;
+ char varname[256];
+
+ if (parm->numparms != 3) return OSDCMD_SHOWHELP;
+
+ if (numplayers > 1)
+ {
+ OSD_Printf("Command not allowed in multiplayer\n");
+ return OSDCMD_OK;
+ }
+
+ ID=Batol(parm->parms[0]);
+ if (ID>=MAXSPRITES)
+ {
+ OSD_Printf("Invalid sprite ID\n");
+ return OSDCMD_OK;
+ }
+
+ varval = Batol(parm->parms[2]);
+ strcpy(varname,parm->parms[2]);
+ varval = Batol(varname);
+ i = hash_find(&h_gamevars,varname);
+ if (i >= 0)
+ varval=Gv_GetVar(i, g_player[screenpeek].ps->i, screenpeek);
+
+ strcpy(varname,parm->parms[1]);
+ i = hash_find(&h_gamevars,varname);
+ if (i >= 0)
+ Gv_SetVar(i, varval, ID, -1);
+ return OSDCMD_OK;
+}
+#else
+static int32_t osdcmd_lua(osdfuncparm_t const * const parm)
+{
+ // Should be used like
+ // lua "lua code..."
+ // (the quotes making the whole string passed as one argument)
+
+ int32_t ret;
+
+ if (parm->numparms != 1)
+ return OSDCMD_SHOWHELP;
+
+ if (!L_IsInitialized(&g_ElState))
+ {
+ OSD_Printf("Lua state is not initialized.\n");
+ return OSDCMD_OK;
+ }
+
+ // TODO: "=<expr>" as shorthand for "print(<expr>)", like in the
+ // stand-alone Lua interpreter?
+ // TODO: reserve some table to explicitly store stuff on the top level, for
+ // debugging convenience?
+
+ // For the 'lua' OSD command, don't make errors appear on-screen:
+ el_addNewErrors = 0;
+ ret = L_RunString(&g_ElState, parm->parms[0], -1, "console");
+ el_addNewErrors = 1;
+
+ if (ret != 0)
+ OSD_Printf("Error running the Lua code (error code %d)\n", ret);
+
+ return OSDCMD_OK;
+}
+#endif
+
+static int32_t osdcmd_addpath(osdfuncparm_t const * const parm)
+{
+ char pathname[BMAX_PATH];
+
+ if (parm->numparms != 1) return OSDCMD_SHOWHELP;
+
+ strcpy(pathname,parm->parms[0]);
+ addsearchpath(pathname);
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_initgroupfile(osdfuncparm_t const * const parm)
+{
+ char file[BMAX_PATH];
+
+ if (parm->numparms != 1) return OSDCMD_SHOWHELP;
+
+ strcpy(file,parm->parms[0]);
+ initgroupfile(file);
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_cmenu(osdfuncparm_t const * const parm)
+{
+ if (parm->numparms != 1) return OSDCMD_SHOWHELP;
+ if (numplayers > 1)
+ {
+ OSD_Printf("cmenu: disallowed in multiplayer\n");
+ return OSDCMD_OK;
+ }
+ else
+ {
+ Menu_Change(Batol(parm->parms[0]));
+ }
+
+ return OSDCMD_OK;
+}
+
+extern void G_SetCrosshairColor(int32_t r, int32_t g, int32_t b);
+extern palette_t CrosshairColors;
+
+static int32_t osdcmd_crosshaircolor(osdfuncparm_t const * const parm)
+{
+ int32_t r, g, b;
+
+ if (parm->numparms != 3)
+ {
+ OSD_Printf("crosshaircolor: r:%d g:%d b:%d\n",CrosshairColors.r,CrosshairColors.g,CrosshairColors.b);
+ return OSDCMD_SHOWHELP;
+ }
+ r = Batol(parm->parms[0]);
+ g = Batol(parm->parms[1]);
+ b = Batol(parm->parms[2]);
+ G_SetCrosshairColor(r,g,b);
+
+ if (!OSD_ParsingScript())
+ OSD_Printf("%s\n", parm->raw);
+
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_give(osdfuncparm_t const * const parm)
+{
+ int32_t i;
+
+ if (numplayers != 1 || (g_player[myconnectindex].ps->gm & MODE_GAME) == 0 ||
+ g_player[myconnectindex].ps->dead_flag != 0)
+ {
+ OSD_Printf("give: Cannot give while dead or not in a single-player game.\n");
+ return OSDCMD_OK;
+ }
+
+ if (parm->numparms != 1) return OSDCMD_SHOWHELP;
+
+ if (!Bstrcasecmp(parm->parms[0], "all"))
+ {
+ osdcmd_cheatsinfo_stat.cheatnum = CHEAT_STUFF;
+ return OSDCMD_OK;
+ }
+ else if (!Bstrcasecmp(parm->parms[0], "health"))
+ {
+ sprite[g_player[myconnectindex].ps->i].extra = g_player[myconnectindex].ps->max_player_health<<1;
+ return OSDCMD_OK;
+ }
+ else if (!Bstrcasecmp(parm->parms[0], "weapons"))
+ {
+ osdcmd_cheatsinfo_stat.cheatnum = CHEAT_WEAPONS;
+ return OSDCMD_OK;
+ }
+ else if (!Bstrcasecmp(parm->parms[0], "ammo"))
+ {
+ for (i=MAX_WEAPONS-(VOLUMEONE?6:1)-1; i>=PISTOL_WEAPON; i--)
+ P_AddAmmo(g_player[myconnectindex].ps,i,g_player[myconnectindex].ps->max_ammo_amount[i]);
+ return OSDCMD_OK;
+ }
+ else if (!Bstrcasecmp(parm->parms[0], "armor"))
+ {
+ g_player[myconnectindex].ps->inv_amount[GET_SHIELD] = 100;
+ return OSDCMD_OK;
+ }
+ else if (!Bstrcasecmp(parm->parms[0], "keys"))
+ {
+ osdcmd_cheatsinfo_stat.cheatnum = CHEAT_KEYS;
+ return OSDCMD_OK;
+ }
+ else if (!Bstrcasecmp(parm->parms[0], "inventory"))
+ {
+ osdcmd_cheatsinfo_stat.cheatnum = CHEAT_INVENTORY;
+ return OSDCMD_OK;
+ }
+ return OSDCMD_SHOWHELP;
+}
+
+void onvideomodechange(int32_t newmode)
+{
+ uint8_t palid;
+
+ // XXX?
+ if (!newmode || g_player[screenpeek].ps->palette < BASEPALCOUNT)
+ palid = g_player[screenpeek].ps->palette;
+ else
+ palid = BASEPAL;
+
+#ifdef POLYMER
+ if (getrendermode() == REND_POLYMER)
+ {
+ int32_t i = 0;
+
+ while (i < MAXSPRITES)
+ {
+ if (actor[i].lightptr)
+ {
+ polymer_deletelight(actor[i].lightId);
+ actor[i].lightptr = NULL;
+ actor[i].lightId = -1;
+ }
+ i++;
+ }
+ }
+#endif
+
+ setbrightness(ud.brightness>>2, palid, 0);
+ g_restorePalette = -1;
+ g_crosshairSum = -1;
+}
+
+static int32_t osdcmd_name(osdfuncparm_t const * const parm)
+{
+ char namebuf[32];
+
+ if (parm->numparms != 1)
+ {
+ OSD_Printf("\"name\" is \"%s\"\n",szPlayerName);
+ return OSDCMD_SHOWHELP;
+ }
+
+ Bstrcpy(tempbuf,parm->parms[0]);
+
+ while (Bstrlen(OSD_StripColors(namebuf,tempbuf)) > 10)
+ tempbuf[Bstrlen(tempbuf)-1] = '\0';
+
+ Bstrncpy(szPlayerName,tempbuf,sizeof(szPlayerName)-1);
+ szPlayerName[sizeof(szPlayerName)-1] = '\0';
+
+ OSD_Printf("name %s\n",szPlayerName);
+
+ Net_SendClientInfo();
+
+ return OSDCMD_OK;
+}
+
+
+static int32_t osdcmd_button(osdfuncparm_t const * const parm)
+{
+ char const *p = parm->name+9; // skip "gamefunc_"
+// if (g_player[myconnectindex].ps->gm == MODE_GAME) // only trigger these if in game
+ CONTROL_OSDInput[CONFIG_FunctionNameToNum(p)] = 1; // FIXME
+ return OSDCMD_OK;
+}
+
+const keydef_t ConsoleKeys[]=
+{
+ { "Escape", 0x1 },
+ { "1", 0x2 },
+ { "2", 0x3 },
+ { "3", 0x4 },
+ { "4", 0x5 },
+ { "5", 0x6 },
+ { "6", 0x7 },
+ { "7", 0x8 },
+ { "8", 0x9 },
+ { "9", 0xa },
+ { "0", 0xb },
+ { "-", 0xc },
+ { "=", 0xd },
+ { "BakSpc", 0xe },
+ { "Tab", 0xf },
+ { "Q", 0x10 },
+ { "W", 0x11 },
+ { "E", 0x12 },
+ { "R", 0x13 },
+ { "T", 0x14 },
+ { "Y", 0x15 },
+ { "U", 0x16 },
+ { "I", 0x17 },
+ { "O", 0x18 },
+ { "P", 0x19 },
+ { "[", 0x1a },
+ { "]", 0x1b },
+ { "Enter", 0x1c },
+ { "LCtrl", 0x1d },
+ { "A", 0x1e },
+ { "S", 0x1f },
+ { "D", 0x20 },
+ { "F", 0x21 },
+ { "G", 0x22 },
+ { "H", 0x23 },
+ { "J", 0x24 },
+ { "K", 0x25 },
+ { "L", 0x26 },
+ { "SemiColon", 0x27 },
+ { "'", 0x28 },
+ { "Tilde", 0x29 },
+ { "LShift", 0x2a },
+ { "Backslash", 0x2b },
+ { "Z", 0x2c },
+ { "X", 0x2d },
+ { "C", 0x2e },
+ { "V", 0x2f },
+ { "B", 0x30 },
+ { "N", 0x31 },
+ { "M", 0x32 },
+ { ",", 0x33 },
+ { ".", 0x34 },
+ { "/", 0x35 },
+ { "RShift", 0x36 },
+ { "Kpad*", 0x37 },
+ { "LAlt", 0x38 },
+ { "Space", 0x39 },
+ { "CapLck", 0x3a },
+ { "F1", 0x3b },
+ { "F2", 0x3c },
+ { "F3", 0x3d },
+ { "F4", 0x3e },
+ { "F5", 0x3f },
+ { "F6", 0x40 },
+ { "F7", 0x41 },
+ { "F8", 0x42 },
+ { "F9", 0x43 },
+ { "F10", 0x44 },
+ { "NumLck", 0x45 },
+ { "ScrLck", 0x46 },
+ { "Kpad7", 0x47 },
+ { "Kpad8", 0x48 },
+ { "Kpad9", 0x49 },
+ { "Kpad-", 0x4a },
+ { "Kpad4", 0x4b },
+ { "Kpad5", 0x4c },
+ { "Kpad6", 0x4d },
+ { "Kpad+", 0x4e },
+ { "Kpad1", 0x4f },
+ { "Kpad2", 0x50 },
+ { "Kpad3", 0x51 },
+ { "Kpad0", 0x52 },
+ { "Kpad.", 0x53 },
+ { "F11", 0x57 },
+ { "F12", 0x58 },
+ { "KpdEnt", 0x9c },
+ { "RCtrl", 0x9d },
+ { "Kpad/", 0xb5 },
+ { "RAlt", 0xb8 },
+ { "PrtScn", 0xb7 },
+ { "Pause", 0xc5 },
+ { "Home", 0xc7 },
+ { "Up", 0xc8 },
+ { "PgUp", 0xc9 },
+ { "Left", 0xcb },
+ { "Right", 0xcd },
+ { "End", 0xcf },
+ { "Down", 0xd0 },
+ { "PgDn", 0xd1 },
+ { "Insert", 0xd2 },
+ { "Delete", 0xd3 },
+
+ {0,0}
+};
+
+const char *const ConsoleButtons[] =
+{
+ "mouse1", "mouse2", "mouse3", "mouse4", "mwheelup",
+ "mwheeldn", "mouse5", "mouse6", "mouse7", "mouse8"
+};
+
+static int32_t osdcmd_bind(osdfuncparm_t const * const parm)
+{
+ int32_t i, j, repeat;
+
+ if (parm->numparms==1 && !Bstrcasecmp(parm->parms[0],"showkeys"))
+ {
+ for (i=0; ConsoleKeys[i].name; i++)
+ OSD_Printf("%s\n",ConsoleKeys[i].name);
+ for (i=0; i<MAXMOUSEBUTTONS; i++)
+ OSD_Printf("%s\n",ConsoleButtons[i]);
+ return OSDCMD_OK;
+ }
+
+ if (parm->numparms==0)
+ {
+ int32_t j=0;
+
+ OSD_Printf("Current key bindings:\n");
+
+ for (i=0; i<MAXBOUNDKEYS+MAXMOUSEBUTTONS; i++)
+ if (CONTROL_KeyIsBound(i))
+ {
+ j++;
+ OSD_Printf("%-9s %s\"%s\"\n", CONTROL_KeyBinds[i].key, CONTROL_KeyBinds[i].repeat?"":"norepeat ",
+ CONTROL_KeyBinds[i].cmdstr);
+ }
+
+ if (j == 0)
+ OSD_Printf("No binds found.\n");
+
+ return OSDCMD_OK;
+ }
+
+ for (i=0; ConsoleKeys[i].name; i++)
+ if (!Bstrcasecmp(parm->parms[0],ConsoleKeys[i].name))
+ break;
+
+ if (!ConsoleKeys[i].name)
+ {
+ for (i=0; i<MAXMOUSEBUTTONS; i++)
+ if (!Bstrcasecmp(parm->parms[0],ConsoleButtons[i]))
+ break;
+ if (i >= MAXMOUSEBUTTONS)
+ return OSDCMD_SHOWHELP;
+
+ if (parm->numparms < 2)
+ {
+ if (CONTROL_KeyBinds[MAXBOUNDKEYS + i].cmdstr && CONTROL_KeyBinds[MAXBOUNDKEYS + i ].key)
+ OSD_Printf("%-9s %s\"%s\"\n", ConsoleButtons[i], CONTROL_KeyBinds[MAXBOUNDKEYS + i].repeat?"":"norepeat ",
+ CONTROL_KeyBinds[MAXBOUNDKEYS + i].cmdstr);
+ else OSD_Printf("%s is unbound\n", ConsoleButtons[i]);
+ return OSDCMD_OK;
+ }
+
+ j = 1;
+
+ repeat = 1;
+ if (parm->numparms >= 2 && !Bstrcasecmp(parm->parms[j],"norepeat"))
+ {
+ repeat = 0;
+ j++;
+ }
+
+ Bstrcpy(tempbuf,parm->parms[j++]);
+ for (; j<parm->numparms; j++)
+ {
+ Bstrcat(tempbuf," ");
+ Bstrcat(tempbuf,parm->parms[j++]);
+ }
+
+ CONTROL_BindMouse(i, tempbuf, repeat, ConsoleButtons[i]);
+
+ if (!OSD_ParsingScript())
+ OSD_Printf("%s\n",parm->raw);
+ return OSDCMD_OK;
+ }
+
+ if (parm->numparms < 2)
+ {
+ if (CONTROL_KeyIsBound(ConsoleKeys[i].id))
+ OSD_Printf("%-9s %s\"%s\"\n", ConsoleKeys[i].name, CONTROL_KeyBinds[ConsoleKeys[i].id].repeat?"":"norepeat ",
+ CONTROL_KeyBinds[ConsoleKeys[i].id].cmdstr);
+ else OSD_Printf("%s is unbound\n", ConsoleKeys[i].name);
+
+ return OSDCMD_OK;
+ }
+
+ j = 1;
+
+ repeat = 1;
+ if (parm->numparms >= 2 && !Bstrcasecmp(parm->parms[j],"norepeat"))
+ {
+ repeat = 0;
+ j++;
+ }
+
+ Bstrcpy(tempbuf,parm->parms[j++]);
+ for (; j<parm->numparms; j++)
+ {
+ Bstrcat(tempbuf," ");
+ Bstrcat(tempbuf,parm->parms[j++]);
+ }
+
+ CONTROL_BindKey(ConsoleKeys[i].id, tempbuf, repeat, ConsoleKeys[i].name);
+
+ {
+ char *cp = tempbuf;
+
+ // Populate the keyboard config menu based on the bind.
+ // Take care of processing one-to-many bindings properly, too.
+ while ((cp = Bstrstr(cp, "gamefunc_")))
+ {
+ char *semi;
+
+ cp += 9; // skip the "gamefunc_"
+
+ semi = Bstrchr(cp, ';');
+ if (semi)
+ *semi = 0;
+
+ j = CONFIG_FunctionNameToNum(cp);
+
+ if (semi)
+ cp = semi+1;
+
+ if (j != -1)
+ {
+ ud.config.KeyboardKeys[j][1] = ud.config.KeyboardKeys[j][0];
+ ud.config.KeyboardKeys[j][0] = ConsoleKeys[i].id;
+// CONTROL_MapKey(j, ConsoleKeys[i].id, ud.config.KeyboardKeys[j][0]);
+
+ if (j == gamefunc_Show_Console)
+ OSD_CaptureKey(ConsoleKeys[i].id);
+ }
+ }
+ }
+
+ if (!OSD_ParsingScript())
+ OSD_Printf("%s\n",parm->raw);
+
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_unbindall(osdfuncparm_t const * const UNUSED(parm))
+{
+ int32_t i;
+
+ UNREFERENCED_CONST_PARAMETER(parm);
+
+ for (i=0; i<MAXBOUNDKEYS; i++)
+ CONTROL_FreeKeyBind(i);
+
+ for (i=0; i<MAXMOUSEBUTTONS; i++)
+ CONTROL_FreeMouseBind(i);
+
+ for (i=0; i<NUMGAMEFUNCTIONS; i++)
+ {
+ ud.config.KeyboardKeys[i][0] = ud.config.KeyboardKeys[i][1] = 0xff;
+// CONTROL_MapKey(i, ud.config.KeyboardKeys[i][0], ud.config.KeyboardKeys[i][1]);
+ }
+
+ if (!OSD_ParsingScript())
+ OSD_Printf("unbound all controls\n");
+
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_unbind(osdfuncparm_t const * const parm)
+{
+ int32_t i;
+
+ if (parm->numparms < 1) return OSDCMD_SHOWHELP;
+
+ for (i=0; ConsoleKeys[i].name; i++)
+ if (!Bstrcasecmp(parm->parms[0],ConsoleKeys[i].name))
+ break;
+
+ if (!ConsoleKeys[i].name)
+ {
+ for (i=0; i<MAXMOUSEBUTTONS; i++)
+ if (!Bstrcasecmp(parm->parms[0],ConsoleButtons[i]))
+ break;
+
+ if (i >= MAXMOUSEBUTTONS)
+ return OSDCMD_SHOWHELP;
+
+ CONTROL_FreeMouseBind(i);
+
+ OSD_Printf("unbound %s\n",ConsoleButtons[i]);
+
+ return OSDCMD_OK;
+ }
+
+ CONTROL_FreeKeyBind(ConsoleKeys[i].id);
+
+ OSD_Printf("unbound key %s\n",ConsoleKeys[i].name);
+
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_quicksave(osdfuncparm_t const * const UNUSED(parm))
+{
+ UNREFERENCED_CONST_PARAMETER(parm);
+ if (!(g_player[myconnectindex].ps->gm & MODE_GAME))
+ OSD_Printf("quicksave: not in a game.\n");
+ else g_doQuickSave = 1;
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_quickload(osdfuncparm_t const * const UNUSED(parm))
+{
+ UNREFERENCED_CONST_PARAMETER(parm);
+ if (!(g_player[myconnectindex].ps->gm & MODE_GAME))
+ OSD_Printf("quickload: not in a game.\n");
+ else g_doQuickSave = 2;
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_screenshot(osdfuncparm_t const * const parm)
+{
+// KB_ClearKeysDown();
+#ifndef EDUKE32_STANDALONE
+ static const char *fn = "duke0000.png";
+#else
+ static const char *fn = "capt0000.png";
+#endif
+
+ if (parm->numparms == 1 && !Bstrcasecmp(parm->parms[0], "tga"))
+ screencapture_tga(fn, 0);
+ else screencapture(fn, 0);
+
+ return OSDCMD_OK;
+}
+
+#if 0
+static int32_t osdcmd_savestate(osdfuncparm_t const * const UNUSED(parm))
+{
+ UNREFERENCED_PARAMETER(parm);
+ G_SaveMapState();
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_restorestate(osdfuncparm_t const * const UNUSED(parm))
+{
+ UNREFERENCED_PARAMETER(parm);
+ G_RestoreMapState();
+ return OSDCMD_OK;
+}
+#endif
+
+#ifdef DEBUGGINGAIDS
+static int32_t osdcmd_inittimer(osdfuncparm_t const * const parm)
+{
+ if (parm->numparms != 1)
+ {
+ OSD_Printf("%dHz timer\n",g_timerTicsPerSecond);
+ return OSDCMD_SHOWHELP;
+ }
+
+ G_InitTimer(Batol(parm->parms[0]));
+
+ OSD_Printf("%s\n",parm->raw);
+ return OSDCMD_OK;
+}
+#endif
+
+static int32_t osdcmd_disconnect(osdfuncparm_t const * const UNUSED(parm))
+{
+ UNREFERENCED_CONST_PARAMETER(parm);
+ g_netDisconnect = 1;
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_connect(osdfuncparm_t const * const parm)
+{
+ if (parm->numparms != 1)
+ return OSDCMD_SHOWHELP;
+
+ Net_Connect(parm->parms[0]);
+ G_BackToMenu();
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_password(osdfuncparm_t const * const parm)
+{
+ if (parm->numparms < 1)
+ {
+ Bmemset(g_netPassword, 0, sizeof(g_netPassword));
+ return OSDCMD_OK;
+ }
+ Bstrncpy(g_netPassword, (parm->raw) + 9, sizeof(g_netPassword)-1);
+
+ return OSDCMD_OK;
+}
+
+#if !defined NETCODE_DISABLE
+static int32_t osdcmd_listplayers(osdfuncparm_t const * const parm)
+{
+ ENetPeer *currentPeer;
+ char ipaddr[32];
+
+ if (parm && parm->numparms != 0)
+ return OSDCMD_SHOWHELP;
+
+ if (!g_netServer)
+ {
+ initprintf("You are not the server.\n");
+ return OSDCMD_OK;
+ }
+
+ initprintf("Connected clients:\n");
+
+ for (currentPeer = g_netServer -> peers;
+ currentPeer < & g_netServer -> peers [g_netServer -> peerCount];
+ ++ currentPeer)
+ {
+ if (currentPeer -> state != ENET_PEER_STATE_CONNECTED)
+ continue;
+
+ enet_address_get_host_ip(&currentPeer->address, ipaddr, sizeof(ipaddr));
+ initprintf("%x %s %s\n", currentPeer->address.host, ipaddr,
+ g_player[(intptr_t)currentPeer->data].user_name);
+ }
+
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_kick(osdfuncparm_t const * const parm)
+{
+ ENetPeer *currentPeer;
+ uint32_t hexaddr;
+
+ if (parm->numparms != 1)
+ return OSDCMD_SHOWHELP;
+
+ if (!g_netServer)
+ {
+ initprintf("You are not the server.\n");
+ return OSDCMD_OK;
+ }
+
+ for (currentPeer = g_netServer -> peers;
+ currentPeer < & g_netServer -> peers [g_netServer -> peerCount];
+ ++ currentPeer)
+ {
+ if (currentPeer -> state != ENET_PEER_STATE_CONNECTED)
+ continue;
+
+ sscanf(parm->parms[0],"%" SCNx32 "", &hexaddr);
+
+ if (currentPeer->address.host == hexaddr)
+ {
+ initprintf("Kicking %x (%s)\n", currentPeer->address.host,
+ g_player[(intptr_t)currentPeer->data].user_name);
+ enet_peer_disconnect(currentPeer, DISC_KICKED);
+ return OSDCMD_OK;
+ }
+ }
+
+ initprintf("Player %s not found!\n", parm->parms[0]);
+ osdcmd_listplayers(NULL);
+
+ return OSDCMD_OK;
+}
+
+static int32_t osdcmd_kickban(osdfuncparm_t const * const parm)
+{
+ ENetPeer *currentPeer;
+ uint32_t hexaddr;
+
+ if (parm->numparms != 1)
+ return OSDCMD_SHOWHELP;
+
+ if (!g_netServer)
+ {
+ initprintf("You are not the server.\n");
+ return OSDCMD_OK;
+ }
+
+ for (currentPeer = g_netServer -> peers;
+ currentPeer < & g_netServer -> peers [g_netServer -> peerCount];
+ ++ currentPeer)
+ {
+ if (currentPeer -> state != ENET_PEER_STATE_CONNECTED)
+ continue;
+
+ sscanf(parm->parms[0],"%" SCNx32 "", &hexaddr);
+
+ // TODO: implement banning logic
+
+ if (currentPeer->address.host == hexaddr)
+ {
+ char ipaddr[32];
+
+ enet_address_get_host_ip(&currentPeer->address, ipaddr, sizeof(ipaddr));
+ initprintf("Host %s is now banned.\n", ipaddr);
+ initprintf("Kicking %x (%s)\n", currentPeer->address.host,
+ g_player[(intptr_t)currentPeer->data].user_name);
+ enet_peer_disconnect(currentPeer, DISC_BANNED);
+ return OSDCMD_OK;
+ }
+ }
+
+ initprintf("Player %s not found!\n", parm->parms[0]);
+ osdcmd_listplayers(NULL);
+
+ return OSDCMD_OK;
+}
+#endif
+
+static int32_t osdcmd_cvar_set_game(osdfuncparm_t const * const parm)
+{
+ int32_t r = osdcmd_cvar_set(parm);
+
+ if (r != OSDCMD_OK) return r;
+
+ if (!Bstrcasecmp(parm->name, "r_size"))
+ {
+ ud.statusbarmode = (ud.screen_size < 8);
+ G_UpdateScreenArea();
+ }
+ else if (!Bstrcasecmp(parm->name, "r_maxfps"))
+ {
+ if (r_maxfps != 0) r_maxfps = clamp(r_maxfps, 30, 1000);
+ g_frameDelay = r_maxfps ? (getu64tickspersec()/r_maxfps) : 0;
+ }
+ else if (!Bstrcasecmp(parm->name, "r_ambientlight"))
+ {
+ if (r_ambientlight == 0)
+ r_ambientlightrecip = 256.f;
+ else r_ambientlightrecip = 1.f/r_ambientlight;
+ }
+ else if (!Bstrcasecmp(parm->name, "in_mouse"))
+ {
+ CONTROL_MouseEnabled = (ud.config.UseMouse && CONTROL_MousePresent);
+ }
+ else if (!Bstrcasecmp(parm->name, "in_joystick"))
+ {
+ CONTROL_JoystickEnabled = (ud.config.UseJoystick && CONTROL_JoyPresent);
+ }
+ else if (!Bstrcasecmp(parm->name, "vid_gamma"))
+ {
+ ud.brightness = GAMMA_CALC;
+ ud.brightness <<= 2;
+ setbrightness(ud.brightness>>2,g_player[myconnectindex].ps->palette,0);
+ }
+ else if (!Bstrcasecmp(parm->name, "vid_brightness") || !Bstrcasecmp(parm->name, "vid_contrast"))
+ {
+ setbrightness(ud.brightness>>2,g_player[myconnectindex].ps->palette,0);
+ }
+ else if (!Bstrcasecmp(parm->name, "hud_scale")
+ || !Bstrcasecmp(parm->name, "hud_statusbarmode")
+ || !Bstrcasecmp(parm->name, "r_rotatespritenowidescreen"))
+ {
+ G_UpdateScreenArea();
+ }
+ else if (!Bstrcasecmp(parm->name, "skill"))
+ {
+ if (numplayers > 1)
+ return r;
+
+ ud.player_skill = ud.m_player_skill;
+ }
+ else if (!Bstrcasecmp(parm->name, "color"))
+ {
+ G_CheckPlayerColor((int32_t *)&ud.color,-1);
+ g_player[0].ps->palookup = g_player[0].pcolor = ud.color;
+ }
+ else if (!Bstrcasecmp(parm->name, "osdscale"))
+ {
+ osdrscale = 1.f/osdscale;
+
+ if (xdim && ydim)
+ OSD_ResizeDisplay(xdim, ydim);
+ }
+ else if (!Bstrcasecmp(parm->name, "wchoice"))
+ {
+ if (parm->numparms == 1)
+ {
+ if (g_forceWeaponChoice) // rewrite ud.wchoice because osdcmd_cvar_set already changed it
+ {
+ int j = 0;
+
+ while (j < 10)
+ {
+ ud.wchoice[j] = g_player[myconnectindex].wchoice[j] + '0';
+ j++;
+ }
+
+ ud.wchoice[j] = 0;
+ }
+ else
+ {
+ char const *c = parm->parms[0];
+
+ if (*c)
+ {
+ int j = 0;
+
+ while (*c && j < 10)
+ {
+ g_player[myconnectindex].wchoice[j] = *c - '0';
+ c++;
+ j++;
+ }
+
+ while (j < 10)
+ {
+ if (j == 9)
+ g_player[myconnectindex].wchoice[9] = 1;
+ else
+ g_player[myconnectindex].wchoice[j] = 2;
+
+ j++;
+ }
+ }
+ }
+
+ g_forceWeaponChoice = 0;
+ }
+
+ /* Net_SendClientInfo();*/
+ }
+
+ return r;
+}
+
+static int32_t osdcmd_cvar_set_multi(osdfuncparm_t const * const parm)
+{
+ int32_t r = osdcmd_cvar_set_game(parm);
+
+ if (r != OSDCMD_OK) return r;
+
+ G_UpdatePlayerFromMenu();
+
+ return r;
+}
+
+int32_t registerosdcommands(void)
+{
+ uint32_t i;
+
+ static osdcvardata_t cvars_game[] =
+ {
+ { "crosshair", "enable/disable crosshair", (void *)&ud.crosshair, CVAR_BOOL, 0, 1 },
+
+ { "cl_autoaim", "enable/disable weapon autoaim", (void *)&ud.config.AutoAim, CVAR_INT|CVAR_MULTI, 0, 3 },
+ { "cl_automsg", "enable/disable automatically sending messages to all players", (void *)&ud.automsg, CVAR_BOOL, 0, 1 },
+ { "cl_autorun", "enable/disable autorun", (void *)&ud.auto_run, CVAR_BOOL, 0, 1 },
+ { "cl_autovote", "enable/disable automatic voting", (void *)&ud.autovote, CVAR_INT, 0, 2 },
+
+ { "cl_cheatmask", "configure what cheats show in the cheats menu", (void *)&cl_cheatmask, CVAR_UINT, 0, ~0 },
+
+ { "cl_obituaries", "enable/disable multiplayer death messages", (void *)&ud.obituaries, CVAR_BOOL, 0, 1 },
+ { "cl_democams", "enable/disable demo playback cameras", (void *)&ud.democams, CVAR_BOOL, 0, 1 },
+
+ { "cl_idplayers", "enable/disable name display when aiming at opponents", (void *)&ud.idplayers, CVAR_BOOL, 0, 1 },
+
+ { "cl_runmode", "enable/disable modernized run key operation", (void *)&ud.runkey_mode, CVAR_BOOL, 0, 1 },
+
+ { "cl_showcoords", "show your position in the game world", (void *)&ud.coords, CVAR_INT, 0,
+#ifdef USE_OPENGL
+ 2
+#else
+ 1
+#endif
+ },
+
+ { "cl_viewbob", "enable/disable player head bobbing", (void *)&ud.viewbob, CVAR_BOOL, 0, 1 },
+
+ { "cl_weaponsway", "enable/disable player weapon swaying", (void *)&ud.weaponsway, CVAR_BOOL, 0, 1 },
+ { "cl_weaponswitch", "enable/disable auto weapon switching", (void *)&ud.weaponswitch, CVAR_INT|CVAR_MULTI, 0, 7 },
+
+ { "color", "changes player palette", (void *)&ud.color, CVAR_INT|CVAR_MULTI, 0, MAXPALOOKUPS-1 },
+
+ { "crosshairscale","changes the size of the crosshair", (void *)&ud.crosshairscale, CVAR_INT, 10, 100 },
+
+ { "demorec_diffs","enable/disable diff recording in demos",(void *)&demorec_diffs_cvar, CVAR_BOOL, 0, 1 },
+ { "demorec_force","enable/disable forced demo recording",(void *)&demorec_force_cvar, CVAR_BOOL|CVAR_NOSAVE, 0, 1 },
+ {
+ "demorec_difftics","sets game tic interval after which a diff is recorded",
+ (void *)&demorec_difftics_cvar, CVAR_INT, 2, 60*REALGAMETICSPERSEC
+ },
+ { "demorec_diffcompress","Compression method for diffs. (0: none, 1: KSLZW)",(void *)&demorec_diffcompress_cvar, CVAR_INT, 0, 1 },
+ { "demorec_synccompress","Compression method for input. (0: none, 1: KSLZW)",(void *)&demorec_synccompress_cvar, CVAR_INT, 0, 1 },
+ { "demorec_seeds","enable/disable recording of random seed for later sync checking",(void *)&demorec_seeds_cvar, CVAR_BOOL, 0, 1 },
+ { "demoplay_diffs","enable/disable application of diffs in demo playback",(void *)&demoplay_diffs, CVAR_BOOL, 0, 1 },
+ { "demoplay_showsync","enable/disable display of sync status",(void *)&demoplay_showsync, CVAR_BOOL, 0, 1 },
+
+ { "hud_althud", "enable/disable alternate mini-hud", (void *)&ud.althud, CVAR_INT, 0, 2 },
+ { "hud_bgstretch", "enable/disable background image stretching in wide resolutions", (void *)&ud.bgstretch, CVAR_BOOL, 0, 1 },
+ { "hud_messagetime", "length of time to display multiplayer chat messages", (void *)&ud.msgdisptime, CVAR_INT, 0, 3600 },
+ { "hud_numbertile", "first tile in alt hud number set", (void *)&althud_numbertile, CVAR_INT, 0, MAXUSERTILES-10 },
+ { "hud_numberpal", "pal for alt hud numbers", (void *)&althud_numberpal, CVAR_INT, 0, MAXPALOOKUPS-1 },
+ { "hud_shadows", "enable/disable althud shadows", (void *)&althud_shadows, CVAR_BOOL, 0, 1 },
+ { "hud_flashing", "enable/disable althud flashing", (void *)&althud_flashing, CVAR_BOOL, 0, 1 },
+ { "hud_glowingquotes", "enable/disable \"glowing\" quote text", (void *)&hud_glowingquotes, CVAR_BOOL, 0, 1 },
+ { "hud_scale","changes the hud scale", (void *)&ud.statusbarscale, CVAR_INT|CVAR_FUNCPTR, 36, 100 },
+ { "hud_showmapname", "enable/disable map name display on load", (void *)&hud_showmapname, CVAR_BOOL, 0, 1 },
+ { "hud_stats", "enable/disable level statistics display", (void *)&ud.levelstats, CVAR_BOOL, 0, 1 },
+ { "hud_textscale", "sets multiplayer chat message size", (void *)&ud.textscale, CVAR_INT, 100, 400 },
+ { "hud_weaponscale","changes the weapon scale", (void *)&ud.weaponscale, CVAR_INT, 10, 100 },
+ { "hud_statusbarmode", "change overlay mode of status bar", (void *)&ud.statusbarmode, CVAR_BOOL|CVAR_FUNCPTR, 0, 1 },
+
+#ifdef EDUKE32_TOUCH_DEVICES
+ { "hud_hidestick", "hide the touch input stick", (void *)&droidinput.hideStick, CVAR_BOOL, 0, 1 },
+#endif
+
+ { "in_joystick","enables input from the joystick if it is present",(void *)&ud.config.UseJoystick, CVAR_BOOL|CVAR_FUNCPTR, 0, 1 },
+ { "in_mouse","enables input from the mouse if it is present",(void *)&ud.config.UseMouse, CVAR_BOOL|CVAR_FUNCPTR, 0, 1 },
+
+ { "in_aimmode", "0:toggle, 1:hold to aim", (void *)&ud.mouseaiming, CVAR_BOOL, 0, 1 },
+ {
+ "in_mousebias", "emulates the original mouse code's weighting of input towards whichever axis is moving the most at any given time",
+ (void *)&ud.config.MouseBias, CVAR_INT, 0, 32
+ },
+ { "in_mousedeadzone", "amount of mouse movement to filter out", (void *)&ud.config.MouseDeadZone, CVAR_INT, 0, 512 },
+ { "in_mouseflip", "invert vertical mouse movement", (void *)&ud.mouseflip, CVAR_BOOL, 0, 1 },
+ { "in_mousemode", "toggles vertical mouse view", (void *)&g_myAimMode, CVAR_BOOL, 0, 1 },
+ { "in_mousesmoothing", "enable/disable mouse input smoothing", (void *)&ud.config.SmoothInput, CVAR_BOOL, 0, 1 },
+
+ { "mus_enabled", "enables/disables music", (void *)&ud.config.MusicToggle, CVAR_BOOL, 0, 1 },
+ { "mus_volume", "controls music volume", (void *)&ud.config.MusicVolume, CVAR_INT, 0, 255 },
+
+ { "osdhightile", "enable/disable hires art replacements for console text", (void *)&osdhightile, CVAR_BOOL, 0, 1 },
+ { "osdscale", "adjust console text size", (void *)&osdscale, CVAR_FLOAT|CVAR_FUNCPTR, 1, 4 },
+
+ { "r_camrefreshdelay", "minimum delay between security camera sprite updates, 120 = 1 second", (void *)&ud.camera_time, CVAR_INT, 1, 240 },
+ { "r_drawweapon", "enable/disable weapon drawing", (void *)&ud.drawweapon, CVAR_INT, 0, 2 },
+ { "r_showfps", "show the frame rate counter", (void *)&ud.showfps, CVAR_INT, 0, 2 },
+ { "r_shadows", "enable/disable sprite and model shadows", (void *)&ud.shadows, CVAR_BOOL, 0, 1 },
+ { "r_size", "change size of viewable area", (void *)&ud.screen_size, CVAR_INT|CVAR_FUNCPTR, 0, 64 },
+ { "r_rotatespritenowidescreen", "pass bit 1024 to all CON rotatesprite calls", (void *)&g_rotatespriteNoWidescreen, CVAR_BOOL|CVAR_FUNCPTR, 0, 1 },
+ { "r_pixeldoubling", "enable/disable pixel doubling in the software renderer", (void *) &ud.detail, CVAR_BOOL, 0, 1 },
+ { "r_precache", "enable/disable the pre-level caching routine", (void *)&ud.config.useprecache, CVAR_BOOL, 0, 1 },
+
+ { "r_ambientlight", "sets the global map light level",(void *)&r_ambientlight, CVAR_FLOAT|CVAR_FUNCPTR, 0, 10 },
+ { "r_maxfps", "limit the frame rate",(void *)&r_maxfps, CVAR_INT|CVAR_FUNCPTR, 0, 1000 },
+
+ { "sensitivity","changes the mouse sensitivity", (void *)&CONTROL_MouseSensitivity, CVAR_FLOAT|CVAR_FUNCPTR, 0, 25 },
+
+ { "skill","changes the game skill setting", (void *)&ud.m_player_skill, CVAR_INT|CVAR_FUNCPTR/*|CVAR_NOMULTI*/, 0, 5 },
+
+ { "snd_ambience", "enables/disables ambient sounds", (void *)&ud.config.AmbienceToggle, CVAR_BOOL, 0, 1 },
+ { "snd_enabled", "enables/disables sound effects", (void *)&ud.config.SoundToggle, CVAR_BOOL, 0, 1 },
+ { "snd_mastervolume", "master volume for sound system", (void *)&ud.config.MasterVolume, CVAR_INT, 0, 255 },
+ { "snd_fxvolume", "volume of sound effects", (void *)&ud.config.FXVolume, CVAR_INT, 1, 255 },
+ { "snd_mixrate", "sound mixing rate", (void *)&ud.config.MixRate, CVAR_INT, 0, 48000 },
+ { "snd_numchannels", "the number of sound channels", (void *)&ud.config.NumChannels, CVAR_INT, 0, 2 },
+ { "snd_numvoices", "the number of concurrent sounds", (void *)&ud.config.NumVoices, CVAR_INT, 0, 256 },
+ { "snd_fluidsynthbackend", "the backend to use with fluidsynth", (void *)&ud.config.FluidsynthBackend, CVAR_INT, 0, 9 },
+ { "snd_reversestereo", "reverses the stereo channels", (void *)&ud.config.ReverseStereo, CVAR_BOOL, 0, 1 },
+ { "snd_speech", "enables/disables player speech", (void *)&ud.config.VoiceToggle, CVAR_INT, 0, 5 },
+
+ { "team","change team in multiplayer", (void *)&ud.team, CVAR_INT|CVAR_MULTI, 0, 3 },
+
+#ifdef EDUKE32_TOUCH_DEVICES
+ { "touch_sens_move_x","touch input sensitivity for moving forward/back", (void *)&droidinput.forward_sens, CVAR_FLOAT, 1, 9 },
+ { "touch_sens_move_y","touch input sensitivity for strafing", (void *)&droidinput.strafe_sens, CVAR_FLOAT, 1, 9 },
+ { "touch_sens_look_x", "touch input sensitivity for turning left/right", (void *) &droidinput.yaw_sens, CVAR_FLOAT, 1, 9 },
+ { "touch_sens_look_y", "touch input sensitivity for looking up/down", (void *) &droidinput.pitch_sens, CVAR_FLOAT, 1, 9 },
+ { "touch_invert", "invert look up/down touch input", (void *) &droidinput.invertLook, CVAR_INT, 0, 1 },
+#endif
+
+ { "vid_gamma","adjusts gamma component of gamma ramp",(void *)&vid_gamma, CVAR_FLOAT|CVAR_FUNCPTR, 0, 10 },
+ { "vid_contrast","adjusts contrast component of gamma ramp",(void *)&vid_contrast, CVAR_FLOAT|CVAR_FUNCPTR, 0, 10 },
+ { "vid_brightness","adjusts brightness component of gamma ramp",(void *)&vid_brightness, CVAR_FLOAT|CVAR_FUNCPTR, 0, 10 },
+ { "wchoice","sets weapon autoselection order", (void *)ud.wchoice, CVAR_STRING|CVAR_FUNCPTR, 0, MAX_WEAPONS },
+ };
+
+ osdcmd_cheatsinfo_stat.cheatnum = -1;
+
+ for (i=0; i<ARRAY_SIZE(cvars_game); i++)
+ {
+ switch (cvars_game[i].flags & (CVAR_FUNCPTR|CVAR_MULTI))
+ {
+ case CVAR_FUNCPTR:
+ OSD_RegisterCvar(&cvars_game[i], osdcmd_cvar_set_game); break;
+ case CVAR_MULTI:
+ case CVAR_FUNCPTR|CVAR_MULTI:
+ OSD_RegisterCvar(&cvars_game[i], osdcmd_cvar_set_multi); break;
+ default:
+ OSD_RegisterCvar(&cvars_game[i], osdcmd_cvar_set); break;
+ }
+ }
+
+ if (VOLUMEONE)
+ OSD_RegisterFunction("changelevel","changelevel <level>: warps to the given level", osdcmd_changelevel);
+ else
+ {
+ OSD_RegisterFunction("changelevel","changelevel <volume> <level>: warps to the given level", osdcmd_changelevel);
+ OSD_RegisterFunction("map","map <mapfile>: loads the given user map", osdcmd_map);
+ OSD_RegisterFunction("demo","demo <demofile or demonum>: starts the given demo", osdcmd_demo);
+ }
+
+ OSD_RegisterFunction("addpath","addpath <path>: adds path to game filesystem", osdcmd_addpath);
+ OSD_RegisterFunction("bind","bind <key> <string>: associates a keypress with a string of console input. Type \"bind showkeys\" for a list of keys and \"listsymbols\" for a list of valid console commands.", osdcmd_bind);
+ OSD_RegisterFunction("cmenu","cmenu <#>: jumps to menu", osdcmd_cmenu);
+ OSD_RegisterFunction("crosshaircolor","crosshaircolor: changes the crosshair color", osdcmd_crosshaircolor);
+
+ OSD_RegisterFunction("connect","connect: connects to a multiplayer game", osdcmd_connect);
+ OSD_RegisterFunction("disconnect","disconnect: disconnects from the local multiplayer game", osdcmd_disconnect);
+
+ for (i=0; i<NUMGAMEFUNCTIONS; i++)
+ {
+ char *t;
+ int32_t j;
+
+ if (gamefunctions[i][0] == '\0')
+ continue;
+
+// if (!Bstrcmp(gamefunctions[i],"Show_Console")) continue;
+
+ Bsprintf(tempbuf,"gamefunc_%s",gamefunctions[i]);
+ t = Xstrdup(tempbuf);
+ for (j=Bstrlen(t); j>=0; j--)
+ t[j] = Btolower(t[j]);
+ Bstrcat(tempbuf,": game button");
+ OSD_RegisterFunction(t, Xstrdup(tempbuf), osdcmd_button);
+ }
+
+ OSD_RegisterFunction("give","give <all|health|weapons|ammo|armor|keys|inventory>: gives requested item", osdcmd_give);
+ OSD_RegisterFunction("god","god: toggles god mode", osdcmd_god);
+ OSD_RegisterFunction("activatecheat","activatecheat <id>: activates a cheat code", osdcmd_activatecheat);
+
+ OSD_RegisterFunction("initgroupfile","initgroupfile <path>: adds a grp file into the game filesystem", osdcmd_initgroupfile);
+#ifdef DEBUGGINGAIDS
+ OSD_RegisterFunction("inittimer","debug", osdcmd_inittimer);
+#endif
+#if !defined NETCODE_DISABLE
+ OSD_RegisterFunction("kick","kick <id>: kicks a multiplayer client. See listplayers.", osdcmd_kick);
+ OSD_RegisterFunction("kickban","kickban <id>: kicks a multiplayer client and prevents them from reconnecting. See listplayers.", osdcmd_kickban);
+
+ OSD_RegisterFunction("listplayers","listplayers: lists currently connected multiplayer clients", osdcmd_listplayers);
+#endif
+ OSD_RegisterFunction("music","music E<ep>L<lev>: change music", osdcmd_music);
+ OSD_RegisterFunction("name","name: change your multiplayer nickname", osdcmd_name);
+ OSD_RegisterFunction("noclip","noclip: toggles clipping mode", osdcmd_noclip);
+
+ OSD_RegisterFunction("password","password: sets multiplayer game password", osdcmd_password);
+
+ OSD_RegisterFunction("quicksave","quicksave: performs a quick save", osdcmd_quicksave);
+ OSD_RegisterFunction("quickload","quickload: performs a quick load", osdcmd_quickload);
+ OSD_RegisterFunction("quit","quit: exits the game immediately", osdcmd_quit);
+ OSD_RegisterFunction("exit","exit: exits the game immediately", osdcmd_quit);
+
+ OSD_RegisterFunction("restartmap", "restartmap: restarts the current map", osdcmd_restartmap);
+ OSD_RegisterFunction("restartsound","restartsound: reinitializes the sound system",osdcmd_restartsound);
+ OSD_RegisterFunction("restartvid","restartvid: reinitializes the video mode",osdcmd_restartvid);
+#if !defined LUNATIC
+ OSD_RegisterFunction("addlogvar","addlogvar <gamevar>: prints the value of a gamevar", osdcmd_addlogvar);
+ OSD_RegisterFunction("setvar","setvar <gamevar> <value>: sets the value of a gamevar", osdcmd_setvar);
+ OSD_RegisterFunction("setvarvar","setvarvar <gamevar1> <gamevar2>: sets the value of <gamevar1> to <gamevar2>", osdcmd_setvar);
+ OSD_RegisterFunction("setactorvar","setactorvar <actor#> <gamevar> <value>: sets the value of <actor#>'s <gamevar> to <value>", osdcmd_setactorvar);
+#else
+ OSD_RegisterFunction("lua", "lua \"Lua code...\": runs Lunatic code", osdcmd_lua);
+#endif
+ OSD_RegisterFunction("screenshot","screenshot [format]: takes a screenshot.", osdcmd_screenshot);
+
+ OSD_RegisterFunction("spawn","spawn <picnum> [palnum] [cstat] [ang] [x y z]: spawns a sprite with the given properties",osdcmd_spawn);
+
+ OSD_RegisterFunction("unbind","unbind <key>: unbinds a key", osdcmd_unbind);
+ OSD_RegisterFunction("unbindall","unbindall: unbinds all keys", osdcmd_unbindall);
+
+ OSD_RegisterFunction("vidmode","vidmode <xdim> <ydim> <bpp> <fullscreen>: change the video mode",osdcmd_vidmode);
+#ifdef USE_OPENGL
+ baselayer_osdcmd_vidmode_func = osdcmd_vidmode;
+#endif
+ return 0;
+}
+
diff --git a/patch/sdlmusic.cpp b/patch/sdlmusic.cpp
new file mode 100644
index 0000000..62af485
--- /dev/null
+++ b/patch/sdlmusic.cpp
@@ -0,0 +1,180 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 2018 Jakob L. Kreuze
+
+This file is part of EDuke32.
+
+EDuke32 is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License version 2
+as published by the Free Software Foundation.
+
+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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+//-------------------------------------------------------------------------
+
+/*
+ * Replacement for the SDL2_Mixer reimplementation of Jim Dose's FX_MAN
+ * routines, for anyone troubled by the shitty internal version of TiMidity.
+ */
+
+#include "fluidsynth_inc.h"
+
+#include "duke3d.h"
+#include "game.h"
+#include "music.h"
+#include "midi.h"
+
+static bool music_initialized = false;
+
+static fluid_synth_t *synth;
+static fluid_settings_t *settings;
+static fluid_player_t *player;
+static fluid_audio_driver_t *driver;
+
+static const char *backend_names[10] = {
+ "unknown",
+ "jack",
+ "alsa",
+ "oss",
+ "pulseaudio",
+ "coreaudio",
+ "dsound",
+ "portaudio",
+ "sndman",
+ "dart",
+};
+
+
+const char *MUSIC_ErrorString(int32_t ErrorNumber)
+{
+ switch (ErrorNumber)
+ {
+ default:
+ return "Unknown error.";
+ }
+
+ return NULL;
+}
+
+int32_t MUSIC_Init(int32_t SoundCard, int32_t Address)
+{
+ int32_t backend = ud.config.FluidsynthBackend;
+
+ if (getenv("EDUKE32_MUSIC_CMD")) {
+ initprintf("External MIDI player not supported by Fluidsynth backend");
+ }
+
+ if (music_initialized) {
+ // Music system is already initialized
+ // return MUSIC_Error;
+ return MUSIC_Ok;
+ }
+
+ settings = new_fluid_settings();
+
+ MUSIC_SetVolume(ud.config.MusicVolume);
+
+ if (backend) {
+ fluid_settings_setstr(settings, "audio.driver", backend_names[backend]);
+ }
+
+ synth = new_fluid_synth(settings);
+ player = new_fluid_player(synth);
+ driver = new_fluid_audio_driver(settings, synth);
+
+ fluid_synth_sfload(synth, "/usr/share/soundfonts/FluidR3_GM.sf2", 1);
+
+ return MIDI_Ok;
+}
+
+int32_t MUSIC_Shutdown(void)
+{
+ delete_fluid_player(player);
+ delete_fluid_audio_driver(driver);
+ delete_fluid_synth(synth);
+ delete_fluid_settings(settings);
+
+ music_initialized = false;
+ return MUSIC_Ok;
+}
+
+void MUSIC_SetVolume(int32_t volume)
+{
+ double val = ((double) volume) * 0.00390625;
+ fluid_settings_setnum(settings, "synth.gain", val);
+}
+
+int32_t MUSIC_GetVolume(void)
+{
+ double val;
+ fluid_settings_getnum(settings, "synth.gain", &val);
+ return (int32_t) (val / 0.00390625);
+}
+
+void MUSIC_SetLoopFlag(int32_t loopflag)
+{
+ fluid_player_set_loop(player, loopflag == MUSIC_LoopSong ? -1 : 1);
+}
+
+void MUSIC_Continue(void)
+{
+ fluid_player_play(player);
+}
+
+void MUSIC_Pause(void)
+{
+ fluid_player_stop(player);
+}
+
+int32_t MUSIC_StopSong(void)
+{
+ fluid_player_stop(player);
+ MUSIC_Shutdown();
+ MUSIC_Init(0, 0);
+ return MUSIC_Ok;
+}
+
+// Fun fact to any onlookers: The part that parses the MIDI file and
+// figures out how long it is worked on the first try and it's a format
+// I've never worked with before. I celebrated.
+int32_t MUSIC_PlaySong(char *song, int32_t loopflag)
+{
+ char *tracks;
+ size_t file_size;
+ uint16_t num_tracks;
+
+ tracks = song + 0x14;
+ num_tracks = *((uint16_t *) (song + 0x10));
+ file_size = 0x14; // Size of the header.
+
+ while (num_tracks--) {
+ uint16_t track_size;
+
+ if (!memcmp(tracks, "MTrk", 4)) {
+ break;
+ }
+
+ track_size = *((uint16_t *) (tracks + 0x04));
+ file_size += track_size + 0x08;
+ tracks += track_size + 0x08;
+ }
+
+ fluid_player_add_mem(player, song, file_size);
+ fluid_player_play(player);
+ return MUSIC_Ok;
+}
+
+int32_t MUSIC_InitMidi(int32_t card, midifuncs *Funcs, int32_t Address)
+{
+ return MIDI_Ok;
+}
+
+void MUSIC_Update(void) {}