- Sync 3dfloors branch with trunk.

SVN r2773 (3dfloors)
This commit is contained in:
Braden Obrzut 2010-09-14 19:32:11 +00:00
parent 5a364ed252
commit 6322239caa
294 changed files with 26747 additions and 13274 deletions

View file

@ -64,42 +64,39 @@ set( CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} ${REL_C_FLAGS}" )
set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${REL_C_FLAGS}" )
set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${DEB_C_FLAGS} -D_DEBUG" )
if( CMAKE_COMPILER_IS_GNUCXX AND PROFILE )
set( CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -pg" )
set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg" )
set( CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -pg" )
set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -pg" )
endif( CMAKE_COMPILER_IS_GNUCXX AND PROFILE )
if( ZLIB_FOUND )
option(FORCE_INTERNAL_ZLIB "Use internal zlib")
option(FORCE_INTERNAL_JPEG "Use internal jpeg")
option(FORCE_INTERNAL_BZIP2 "Use internal bzip2")
if( ZLIB_FOUND AND NOT FORCE_INTERNAL_ZLIB )
message( STATUS "Using system zlib" )
else( ZLIB_FOUND )
else( ZLIB_FOUND AND NOT FORCE_INTERNAL_ZLIB )
message( STATUS "Using internal zlib" )
add_subdirectory( zlib )
set( ZLIB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/zlib )
set( ZLIB_LIBRARIES z )
set( ZLIB_LIBRARY z )
endif( ZLIB_FOUND )
endif( ZLIB_FOUND AND NOT FORCE_INTERNAL_ZLIB )
if( JPEG_FOUND )
if( JPEG_FOUND AND NOT FORCE_INTERNAL_JPEG )
message( STATUS "Using system jpeg library" )
else( JPEG_FOUND )
else( JPEG_FOUND AND NOT FORCE_INTERNAL_JPEG )
message( STATUS "Using internal jpeg library" )
add_subdirectory( jpeg-6b )
set( JPEG_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/jpeg-6b )
set( JPEG_LIBRARIES jpeg )
set( JPEG_LIBRARY jpeg )
endif( JPEG_FOUND )
endif( JPEG_FOUND AND NOT FORCE_INTERNAL_JPEG )
if( BZIP2_FOUND )
if( BZIP2_FOUND AND NOT FORCE_INTERNAL_BZIP2 )
message( STATUS "Using system bzip2 library" )
else( BZIP2_FOUND )
else( BZIP2_FOUND AND NOT FORCE_INTERNAL_BZIP2 )
message( STATUS "Using internal bzip2 library" )
add_subdirectory( bzip2 )
set( BZIP2_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/bzip2" )
set( BZIP2_LIBRARIES bz2 )
set( BZIP2_LIBRARY bz2 )
endif( BZIP2_FOUND)
endif( BZIP2_FOUND AND NOT FORCE_INTERNAL_BZIP2 )
set( LZMA_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/lzma/C" )

23
FindFluidSynth.cmake Normal file
View file

@ -0,0 +1,23 @@
# - Find fluidsynth
# Find the native fluidsynth includes and library
#
# FLUIDSYNTH_INCLUDE_DIR - where to find fluidsynth.h
# FLUIDSYNTH_LIBRARIES - List of libraries when using fluidsynth.
# FLUIDSYNTH_FOUND - True if fluidsynth found.
IF (FLUIDSYNTH_INCLUDE_DIR AND FLUIDSYNTH_LIBRARIES)
# Already in cache, be silent
SET(FluidSynth_FIND_QUIETLY TRUE)
ENDIF (FLUIDSYNTH_INCLUDE_DIR AND FLUIDSYNTH_LIBRARIES)
FIND_PATH(FLUIDSYNTH_INCLUDE_DIR fluidsynth.h)
FIND_LIBRARY(FLUIDSYNTH_LIBRARIES NAMES fluidsynth )
MARK_AS_ADVANCED( FLUIDSYNTH_LIBRARIES FLUIDSYNTH_INCLUDE_DIR )
# handle the QUIETLY and REQUIRED arguments and set FLUIDSYNTH_FOUND to TRUE if
# all listed variables are TRUE
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(FluidSynth DEFAULT_MSG FLUIDSYNTH_LIBRARIES FLUIDSYNTH_INCLUDE_DIR)

View file

@ -1 +1,3 @@
This version of ZDoom must be compiled with any version between 4.22 and 4.28 inclusive.
Use of the latest 4.26 is recommended though due to technical issues with 4.28.

View file

@ -161,6 +161,9 @@ Note: All <bool> fields default to false unless mentioned otherwise.
nofallingdamage = <bool>; // Falling damage is disabled in this sector
dropactors = <bool>; // Actors drop with instantly moving floors (*)
norespawn = <bool>; // Players can not respawn in this sector
soundsequence = <string>; // The sound sequence to play when this sector moves. Placing a
// sound sequence thing in the sector will override this property.
hidden = <bool>; // if true this sector will not be drawn on the textured automap.
* Note about dropactors
@ -178,6 +181,8 @@ Note: All <bool> fields default to false unless mentioned otherwise.
class# = <bool> // Unlike the base spec, # can range from 1-8.
// 8 is the maximum amount of classes the class
// menu can display.
conversation = <int> // Assigns a conversation dialogue to this thing.
// Parameter is the conversation ID, 0 meaning none.
}
@ -263,6 +268,15 @@ Changed node specifications to deprecate compression of node lump.
1.10 25.04.2010
Added 'playeruseback' line trigger flag.
1.11 07.08.2010
Added 'soundsequnce' sector property.
1.12 22.08.2010
Added 'conversation' thing property.
1.13 29.08.2010
Added 'hidden' sector property.
===============================================================================
EOF
===============================================================================

158
specs/usdf.txt Normal file
View file

@ -0,0 +1,158 @@
===============================================================================
Universal Strife Dialog Format Specification v2.0 - 08/20/10
Written by Braden "Blzut3" Obrzut - admin@maniacsvault.net
Defined with input from:
CodeImp
Gez
Graf Zahl
Quasar
et al.
Copyright (c) 2010 Braden Obrzut.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.2
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
===============================================================================
=======================================
I. Grammar / Syntax
=======================================
The grammar and syntax is similar to that of UDMF. A compliant UDMF parser
should be applyable to the USDF. However, it will need to be capable of
handling sub-blocks. Unknown sub-blocks should be skipped.
=======================================
II. Implementation Semantics
=======================================
------------------------------------
II.A : Storage and Retrieval of Data
------------------------------------
This is the same as in UDMF.
-----------------------------------
II.B : Storage Within Archive Files
-----------------------------------
There are two options for the USDF lump placement. This can either be a part
of the UDMF lump list or standalone. If used stand alone the lump name
DIALOGXY is used corresponding with MAPXY. For UDMF the lump shall be called
"DIALOGUE".
--------------------------------
II.C : Implementation Dependence
--------------------------------
USDF also implements the namespace statement. This has all the same
requirements as UDMF.
=======================================
III. Standardized Fields
=======================================
The following are required for all USDF complient implementations. Like UDMF,
any unknown field/function should be ignored and not treated as an error.
NOTE: "mobj" refers to Strife's conversationIDs and not doom editor numbers or
Hexen's spawnids. A valid mobj value is any positive integer greater
than or equal to 1.
---------------------
III.A : Conversations
---------------------
Conversations are groups of pages that can be assigned to a particular object.
Implementors should preserve the IDs to allow for dynamic reassignment through
scripting although this is not a requirement.
conversation // Starts a dialog.
{
actor = <integer>; // mobj for this conversation's actor. If previously
// used, this will override the previous conversation.
page // Starts a new page. Pages are automatically numbered starting at 0.
{
name = <string>; // Name that goes in the upper left hand corner
panel = <string>; // Name of lump to render as the background.
voice = <string>; // Narration sound lump.
dialog = <string>; // Dialog of the page.
drop = <integer>; // mobj for the object to drop if the actor is
// killed.
link = <integer>; // Page to jump to if all ifitem conditions are
// satisified.
// jumps to the specified page if the player has the specified amount
// or more of item in their inventory. This can be repeated as many
// times as the author wants, all conditions must be met for the
// jump to occur.
ifitem
{
item = <integer>; // mobj of item to check.
amount = <integer>; // amount required to be in inventory.
}
// Choices shall be automatically numbered.
choice
{
text = <string>; // Name of the choice.
// The amount of an item needed to successfully pick this option.
// This can be repeated, but only the first will be shown (provided
// diaplaycost is true). All costs must be satisfied for success.
cost
{
item = <integer>; // Item that is required for this option.
amount = <integer>; // Minimum amount of the item needed.
}
displaycost = <bool>; // Weather the cost should be
// displayed with the option.
// If no cost is specified this should
// be ignored.
yesmessage = <string>; // Text to add to console when choice
// is accepted.
nomessage = <string>; // Text to add to console when choice
// is denied.
log = <string>; // LOG entry to use on success.
giveitem = <integer>; // Gives the specified item upon
// success.
// The following are the same as the special for linedefs in UDMF.
// They are executed on success.
special = <integer>;
arg0 = <integer>;
arg1 = <integer>;
arg2 = <integer>;
arg3 = <integer>;
arg4 = <integer>;
nextpage = <integer>; // Sets the next page.
closedialog = <bool>; // Should the dialog be closed upon
// selecting this choice?
// Default: false
}
}
}
-------------------------------
III.B : Including Other Dialogs
-------------------------------
Unlike the original Strife dialog format. The lump "SCRIPT00" should not be
included automatically. Instead the user must specify this behavior by using
the include function, which takes the name of a lump to include. Include only
needs to be available in the global scope and for compatibility reasons, must
include the result of the script and not act like a preprocessor statement.
include = <string>;
===============================================================================
EOF
===============================================================================

89
specs/usdf_zdoom.txt Normal file
View file

@ -0,0 +1,89 @@
===============================================================================
ZDoom Strife Dialog Format ZDoom v1.1 - 23.08.2010
based on Universal Strife Dialog Format v2.0
Copyright (c) 2010 Christoph Oelckers.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.2
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
===============================================================================
=======================================
I. Grammar / Syntax
=======================================
No changes.
=======================================
II. Implementation Semantics
=======================================
No changes.
=======================================
III. Changes to USDF spec
=======================================
ZDoom Strife Dialogue format implements the USDF base specification as described with one important change:
To take advantage of named actor classes any field specifying an actor type
by a conversationID takes a class name instead.
The following fields are affected by this change:
conversation
{
actor = <string>;
page
{
drop = <string>;
ifitem
{
item = <string>;
}
choice
{
cost
{
item = <string>;
}
giveitem = <string>;
}
}
}
It should be noted that this change creates an incompatibility with USDF
so technically speaking the ZDoom format is no longer 'real' USDF.
To accomodate what is needed here this is unavoidable, unfortunately.
Any proper USDF implementation not supporting named actor classes should
either refuse loading dialogues with the 'ZDoom' namespace or if it does not
outright abort on incompatible namespaces fail with a type mismatch error on
one of the specified propeties.
ZDoom-format dialogues need to start with the line:
namespace = "ZDoom";
---------------------
III.A : Conversations
---------------------
This block only lists the newly added fields. Currently ZDoom only adds one
field to the specification:
conversation // Starts a dialog.
{
id = <int>; // assigns an ID to a dialogue. IDs are used to dynamically assign
// dialogues to actors. For 'Strife' namespace or binary dialogues
// the standard conversation ID ('actor' property) is used instead
// for this purpose but since 'ZDoom' namespace requires the actor
// to be a class name it needs a separate field for this.
}
===============================================================================
EOF
===============================================================================

View file

@ -6,13 +6,21 @@ endif( COMMAND cmake_policy )
include( CheckCXXSourceCompiles )
include( CheckFunctionExists )
include( CheckCXXCompilerFlag )
include( FindPkgConfig )
option( NO_ASM "Disable assembly code" )
if( CMAKE_COMPILER_IS_GNUCXX )
option( NO_STRIP "Do not strip Release or MinSizeRel builds" )
# At least some versions of Xcode fail if you strip with the linker
# instead of the separate strip utility.
if( APPLE )
set( NO_STRIP ON )
endif( APPLE )
endif( CMAKE_COMPILER_IS_GNUCXX )
option( DYN_FLUIDSYNTH "Dynamically load fluidsynth" )
if( CMAKE_SIZEOF_VOID_P MATCHES "8" )
set( X64 64 )
endif( CMAKE_SIZEOF_VOID_P MATCHES "8" )
@ -25,6 +33,10 @@ endif( CMAKE_SIZEOF_VOID_P MATCHES "8" )
# fmodapi<version>linux[64] -or simply- fmod
# jpeg-6b
# ...
# The recommended method is to put it in the zdoom tree, since its
# headers are unversioned. Especially now that we can't work properly
# with anything newer than 4.26.xx, you probably don't want to use
# a system-wide version.
# Construct version numbers for searching for the FMOD library on Linux.
set( MINOR_VERSIONS "50" "49" "48" "47" "46" "45" "44" "43" "42" "41"
@ -32,7 +44,7 @@ set( MINOR_VERSIONS "50" "49" "48" "47" "46" "45" "44" "43" "42" "41"
"27" "26" "25" "24" "23" "22" "21" "20" "21" "19" "18" "17" "16"
"15" "14" "13" "12" "11" "10" "09" "08" "07" "06" "05" "04" "03"
"02" "01" "00" )
set( MAJOR_VERSIONS "26" "24" "22" "20" )
set( MAJOR_VERSIONS "30" "28" "26" "24" "22" "20" )
set( FMOD_DIR_VERSIONS ${FMOD_DIR_VERSIONS} "../fmod" )
foreach( majver ${MAJOR_VERSIONS} )
foreach( minver ${MINOR_VERSIONS} )
@ -230,14 +242,11 @@ else( FMOD_LIBRARY )
endif( FMOD_LIBRARY )
# Search for NASM
# Search for FluidSynth
if( CMAKE_SYSTEM_PROCESSOR MATCHES powerpc )
if( NOT NO_ASM )
message( STATUS "Disabling assembly code for PowerPC." )
set( NO_ASM ON )
endif( NOT NO_ASM )
endif( CMAKE_SYSTEM_PROCESSOR MATCHES powerpc )
include( ../FindFluidSynth.cmake )
# Search for NASM
if( NOT NO_ASM )
if( UNIX AND X64 )
@ -297,7 +306,12 @@ if( NOT NO_ASM )
set( ASM_FLAGS )
set( ASM_SOURCE_EXTENSION .s )
else( X64 )
set( ASM_FLAGS -f elf -DM_TARGET_LINUX -i${CMAKE_CURRENT_SOURCE_DIR}/ )
if( APPLE )
set( ASM_FLAGS -fmacho -DM_TARGET_MACHO )
else( APPLE )
set( ASM_FLAGS -felf -DM_TARGET_LINUX )
endif( APPLE )
set( ASM_FLAGS "${ASM_FLAGS}" -i${CMAKE_CURRENT_SOURCE_DIR}/ )
set( ASM_SOURCE_EXTENSION .asm )
endif( X64 )
else( UNIX )
@ -327,9 +341,52 @@ if( NOT NO_ASM )
ENDMACRO( ADD_ASM_FILE )
endif( NOT NO_ASM )
# Decide on SSE setup
set( SSE_MATTERS NO )
# SSE only matters on 32-bit targets. We check compiler flags to know if we can do it.
if( CMAKE_SIZEOF_VOID_P MATCHES "4" )
CHECK_CXX_COMPILER_FLAG( "-msse2 -mfpmath=sse" CAN_DO_MFPMATH )
CHECK_CXX_COMPILER_FLAG( -arch:SSE2 CAN_DO_ARCHSSE2 )
if( CAN_DO_MFPMATH )
set( SSE1_ENABLE "-msse -mfpmath=sse" )
set( SSE2_ENABLE "-msse2 -mfpmath=sse" )
set( SSE_MATTERS YES )
elseif( CAN_DO_ARCHSSE2 )
set( SSE1_ENABLE -arch:SSE )
set( SSE2_ENABLE -arch:SSE2 )
set( SSE_MATTERS YES )
endif( CAN_DO_MFPMATH )
endif( CMAKE_SIZEOF_VOID_P MATCHES "4" )
if( SSE_MATTERS )
if( WIN32 )
set( BACKPATCH 1 CACHE BOOL "Enable backpatching." )
else( WIN32 )
CHECK_FUNCTION_EXISTS(mprotect HAVE_MPROTECT)
if( HAVE_MPROTECT )
set( BACKPATCH 1 CACHE BOOL "Enable backpatching." )
else( HAVE_MPROTECT )
set( BACKPATCH 0 )
endif( HAVE_MPROTECT )
endif( WIN32 )
set( SSE 1 CACHE BOOL "Build SSE and SSE2 versions of key code." )
else( SSE_MATTERS )
set( BACKPATCH 0 )
endif( SSE_MATTERS )
# Set up flags for GCC
if( CMAKE_COMPILER_IS_GNUCXX )
if( PROFILE )
set( CMAKE_C_FLinclude( FindFluidSynth.cmake )
AGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -pg" )
set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg" )
set( CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -pg" )
set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -pg" )
endif( PROFILE )
set( REL_CXX_FLAGS "-fno-rtti" )
if( NOT PROFILE )
set( REL_CXX_FLAGS "${REL_CXX_FLAGS} -fomit-frame-pointer" )
@ -415,6 +472,12 @@ if( NOT HAS_VA_COPY )
endif( HAS___VA_COPY )
endif( NOT HAS_VA_COPY )
# Flags
if( BACKPATCH )
add_definitions( -DBACKPATCH )
endif( BACKPATCH )
# Update svnrevision.h
add_custom_target( revision_check ALL
@ -424,8 +487,16 @@ add_custom_target( revision_check ALL
# Libraries ZDoom needs
message( STATUS "Fluid synth libs: ${FLUIDSYNTH_LIBRARIES}" )
set( ZDOOM_LIBS ${ZDOOM_LIBS} "${ZLIB_LIBRARIES}" "${JPEG_LIBRARIES}" "${BZIP2_LIBRARIES}" "${FMOD_LIBRARY}" )
include_directories( "${ZLIB_INCLUDE_DIR}" "${JPEG_INCLUDE_DIR}" "${FMOD_INCLUDE_DIR}" "${BZIP2_INCLUDE_DIR}" "${LZMA_INCLUDE_DIR}" )
include_directories( "${ZLIB_INCLUDE_DIR}" "${FMOD_INCLUDE_DIR}" "${BZIP2_INCLUDE_DIR}" "${LZMA_INCLUDE_DIR}" "${JPEG_INCLUDE_DIR}" )
if( FLUIDSYNTH_FOUND )
if( NOT DYN_FLUIDSYNTH)
set( ZDOOM_LIBS ${ZDOOM_LIBS} "${FLUIDSYNTH_LIBRARIES}" )
include_directories( "${FLUIDSYNTH_INCLUDE_DIR}" )
endif( NOT DYN_FLUIDSYNTH )
endif( FLUIDSYNTH_FOUND )
# Start defining source files for ZDoom
@ -473,7 +544,7 @@ else( WIN32 )
sdl/sdlvideo.cpp
sdl/st_start.cpp )
if( APPLE )
set( SYSTEM_SOURCES ${SYSTEM_SOURCES} sdl/SDLMain.m )
set( SYSTEM_SOURCES ${SYSTEM_SOURCES} sdl/SDLMain.m sdl/iwadpicker_cocoa.mm )
endif( APPLE )
endif( WIN32 )
@ -508,12 +579,23 @@ add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/sc_man_scanner.h
include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
if( CMAKE_SYSTEM_PROCESSOR MATCHES powerpc )
if( SSE_MATTERS )
if( SSE )
set( X86_SOURCES nodebuild_classify_sse2.cpp )
set_source_files_properties( nodebuild_classify_sse2.cpp PROPERTIES COMPILE_FLAGS "${SSE2_ENABLE}" )
else( SSE )
add_definitions( -DDISABLE_SSE )
endif( SSE )
else( SSE_MATTERS )
add_definitions( -DDISABLE_SSE )
set( X86_SOURCES )
set( NOT_X86 ON )
else( CMAKE_SYSTEM_PROCESSOR MATCHES powerpc )
set( X86_SOURCES nodebuild_classify_sse2.cpp )
endif( CMAKE_SYSTEM_PROCESSOR MATCHES powerpc )
endif( SSE_MATTERS )
if( DYN_FLUIDSYNTH )
add_definitions( -DHAVE_FLUIDSYNTH -DDYN_FLUIDSYNTH )
elseif( FLUIDSYNTH_FOUND )
add_definitions( -DHAVE_FLUIDSYNTH )
endif( DYN_FLUIDSYNTH )
add_executable( zdoom WIN32
autostart.cpp
@ -567,19 +649,17 @@ add_executable( zdoom WIN32
hu_scores.cpp
i_net.cpp
info.cpp
keysections.cpp
lumpconfigfile.cpp
m_alloc.cpp
m_argv.cpp
m_bbox.cpp
m_cheat.cpp
m_joy.cpp
m_menu.cpp
m_misc.cpp
m_options.cpp
m_png.cpp
m_random.cpp
md5.cpp
mus2midi.cpp
name.cpp
nodebuild.cpp
nodebuild_classify_nosse2.cpp
@ -597,6 +677,7 @@ add_executable( zdoom WIN32
p_effect.cpp
p_enemy.cpp
p_floor.cpp
p_glnodes.cpp
p_interaction.cpp
p_lights.cpp
p_linkedsectors.cpp
@ -621,6 +702,7 @@ add_executable( zdoom WIN32
p_tick.cpp
p_trace.cpp
p_udmf.cpp
p_usdf.cpp
p_user.cpp
p_writemap.cpp
p_xlat.cpp
@ -705,6 +787,19 @@ add_executable( zdoom WIN32
g_shared/sbar_mugshot.cpp
g_shared/shared_hud.cpp
g_shared/shared_sbar.cpp
menu/colorpickermenu.cpp
menu/joystickmenu.cpp
menu/listmenu.cpp
menu/loadsavemenu.cpp
menu/menu.cpp
menu/menudef.cpp
menu/menuinput.cpp
menu/messagebox.cpp
menu/optionmenu.cpp
menu/playerdisplay.cpp
menu/playermenu.cpp
menu/readthis.cpp
menu/videomenu.cpp
oplsynth/fmopl.cpp
oplsynth/mlopl.cpp
oplsynth/mlopl_io.cpp
@ -728,13 +823,16 @@ add_executable( zdoom WIN32
sound/music_cd.cpp
sound/music_dumb.cpp
sound/music_gme.cpp
sound/music_mus_midiout.cpp
sound/music_smf_midiout.cpp
sound/music_hmi_midiout.cpp
sound/music_midistream.cpp
sound/music_midi_base.cpp
sound/music_midi_midiout.cpp
sound/music_midi_timidity.cpp
sound/music_mus_midiout.cpp
sound/music_mus_opl.cpp
sound/music_stream.cpp
sound/music_fluidsynth_mididevice.cpp
sound/music_softsynth_mididevice.cpp
sound/music_timidity_mididevice.cpp
sound/music_win_mididevice.cpp
textures/automaptexture.cpp
@ -817,21 +915,12 @@ if( NOT WIN32 )
COMMAND chmod +x ${CMAKE_CURRENT_BINARY_DIR}/link-make
COMMAND /bin/sh -c ${CMAKE_CURRENT_BINARY_DIR}/link-make )
endif( NOT WIN32 )
if( CMAKE_COMPILER_IS_GNUCXX )
# GCC misoptimizes this file
set_source_files_properties( oplsynth/fmopl.cpp PROPERTIES COMPILE_FLAGS "-fno-tree-dominator-opts -fno-tree-fre" )
# Compile this one file with SSE2 support.
set_source_files_properties( nodebuild_classify_sse2.cpp PROPERTIES COMPILE_FLAGS "-msse2 -mfpmath=sse" )
# Need to enable intrinsics for this file.
if( NOT NOT_X86 )
if( SSE_MATTERS )
set_source_files_properties( x86.cpp PROPERTIES COMPILE_FLAGS "-msse2 -mmmx" )
endif( NOT NOT_X86 )
endif( SSE_MATTERS )
endif( CMAKE_COMPILER_IS_GNUCXX )
if( MSVC )
# Compile this one file with SSE2 support.
set_source_files_properties( nodebuild_classify_sse2.cpp PROPERTIES COMPILE_FLAGS "/arch:SSE2" )
endif( MSVC )

View file

@ -57,7 +57,7 @@ DEFINE_SPECIAL(Line_SetBlocking, 55, 3, 3, 3)
DEFINE_SPECIAL(Line_SetTextureScale, 56, 5, 5, 5)
DEFINE_SPECIAL(Sector_SetPortal, 57, -1, -1, 5)
DEFINE_SPECIAL(Sector_CopyScroller, 58, -1, -1, 2)
DEFINE_SPECIAL(Polyobj_OR_MoveToSpot, 59, 3, 3, 3)
DEFINE_SPECIAL(Plat_PerpetualRaise, 60, 3, 3, 3)
DEFINE_SPECIAL(Plat_Stop, 61, 1, 1, 1)
DEFINE_SPECIAL(Plat_DownWaitUpStay, 62, 3, 3, 3)
@ -77,14 +77,17 @@ DEFINE_SPECIAL(Teleport_EndGame, 75, 0, 0, 0)
DEFINE_SPECIAL(TeleportOther, 76, 3, 3, 3)
DEFINE_SPECIAL(TeleportGroup, 77, 5, 5, 5)
DEFINE_SPECIAL(TeleportInSector, 78, 4, 5, 5)
DEFINE_SPECIAL(Thing_SetConversation, 79, 2, 2, 2)
DEFINE_SPECIAL(ACS_Execute, 80, 1, 5, 5)
DEFINE_SPECIAL(ACS_Suspend, 81, 2, 2, 2)
DEFINE_SPECIAL(ACS_Terminate, 82, 2, 2, 2)
DEFINE_SPECIAL(ACS_LockedExecute, 83, 5, 5, 5)
DEFINE_SPECIAL(ACS_ExecuteWithResult, 84, 1, 4, 4)
DEFINE_SPECIAL(ACS_LockedExecuteDoor, 85, 5, 5, 5)
DEFINE_SPECIAL(Polyobj_MoveToSpot, 86, 3, 3, 3)
DEFINE_SPECIAL(Polyobj_Stop, 87, 1, 1, 1)
DEFINE_SPECIAL(Polyobj_MoveTo, 88, 4, 4, 4)
DEFINE_SPECIAL(Polyobj_OR_MoveTo, 89, 4, 4, 4)
DEFINE_SPECIAL(Polyobj_OR_RotateLeft, 90, 3, 3, 3)
DEFINE_SPECIAL(Polyobj_OR_RotateRight, 91, 3, 3, 3)
DEFINE_SPECIAL(Polyobj_OR_Move, 92, 4, 4, 4)
@ -111,7 +114,9 @@ DEFINE_SPECIAL(Plane_Copy, 118, -1, -1, 5)
DEFINE_SPECIAL(Thing_Damage, 119, 2, 3, 3)
DEFINE_SPECIAL(Radius_Quake, 120, 5, 5, 5) // Earthquake
DEFINE_SPECIAL(Line_SetIdentification, 121, -1, -1, 5)
DEFINE_SPECIAL(Thing_Move, 125, 2, 3, 3)
DEFINE_SPECIAL(Thing_SetSpecial, 127, 5, 5, 5)
DEFINE_SPECIAL(ThrustThingZ, 128, 4, 4, 4)
DEFINE_SPECIAL(UsePuzzleItem, 129, 2, 5, 5)

View file

@ -40,6 +40,7 @@
#include "r_blend.h"
#include "s_sound.h"
struct subsector_t;
//
// NOTES: AActor
//
@ -320,6 +321,8 @@ enum
MF6_SHATTERING = 0x00020000, // marks an ice corpse for forced shattering
MF6_KILLED = 0x00040000, // Something that was killed (but not necessarily a corpse)
MF6_BLOCKEDBYSOLIDACTORS = 0x00080000, // Blocked by solid actors, even if not solid itself
MF6_ADDITIVEPOISONDAMAGE = 0x00100000,
MF6_ADDITIVEPOISONDURATION = 0x00200000,
// --- mobj.renderflags ---
@ -505,7 +508,6 @@ enum
AMETA_BloodColor, // colorized blood
AMETA_GibHealth, // negative health below which this monster dies an extreme death
AMETA_WoundHealth, // health needed to enter wound state
AMETA_PoisonDamage, // Amount of poison damage
AMETA_FastSpeed, // Speed in fast mode
AMETA_RDFactor, // Radius damage factor
AMETA_CameraHeight, // Height of camera when used as such
@ -721,19 +723,26 @@ public:
const PClass *GetBloodType(int type = 0) const
{
const PClass *bloodcls;
if (type == 0)
{
return PClass::FindClass((ENamedName)GetClass()->Meta.GetMetaInt(AMETA_BloodType, NAME_Blood));
bloodcls = PClass::FindClass((ENamedName)GetClass()->Meta.GetMetaInt(AMETA_BloodType, NAME_Blood));
}
else if (type == 1)
{
return PClass::FindClass((ENamedName)GetClass()->Meta.GetMetaInt(AMETA_BloodType2, NAME_BloodSplatter));
bloodcls = PClass::FindClass((ENamedName)GetClass()->Meta.GetMetaInt(AMETA_BloodType2, NAME_BloodSplatter));
}
else if (type == 2)
{
return PClass::FindClass((ENamedName)GetClass()->Meta.GetMetaInt(AMETA_BloodType3, NAME_AxeBlood));
bloodcls = PClass::FindClass((ENamedName)GetClass()->Meta.GetMetaInt(AMETA_BloodType3, NAME_AxeBlood));
}
else return NULL;
if (bloodcls != NULL)
{
bloodcls = bloodcls->GetReplacement();
}
return bloodcls;
}
// Calculate amount of missile damage
@ -765,6 +774,7 @@ public:
fixed_t pitch, roll;
FBlockNode *BlockNode; // links in blocks (if needed)
struct sector_t *Sector;
subsector_t * subsector;
fixed_t floorz, ceilingz; // closest together of contacted secs
fixed_t dropoffz; // killough 11/98: the lowest floor over all contacted Sectors.
@ -843,6 +853,15 @@ public:
AActor *BlockingMobj; // Actor that blocked the last move
line_t *BlockingLine; // Line that blocked the last move
int PoisonDamage; // Damage received per tic from poison.
int PoisonDuration; // Duration left for receiving poison damage.
int PoisonPeriod; // How often poison damage is applied. (Every X tics.)
int PoisonDamageReceived; // Damage received per tic from poison.
int PoisonDurationReceived; // Duration left for receiving poison damage.
int PoisonPeriodReceived; // How often poison damage is applied. (Every X tics.)
TObjPtr<AActor> Poisoner; // Last source of received poison damage.
// a linked list of sectors where this object appears
struct msecnode_t *touching_sectorlist; // phares 3/14/98
@ -882,8 +901,9 @@ public:
FState *MeleeState;
FState *MissileState;
// [RH] The dialogue to show when this actor is "used."
FStrifeDialogueNode *Conversation;
int ConversationRoot; // THe root of the current dialogue
FStrifeDialogueNode *Conversation; // [RH] The dialogue to show when this actor is "used."
// [RH] Decal(s) this weapon/projectile generates on impact.
FDecalBase *DecalGenerator;

View file

@ -36,6 +36,9 @@
#include "r_translate.h"
#include "d_event.h"
#include "gi.h"
#include "r_bsp.h"
#include "p_setup.h"
#include "c_bind.h"
#include "m_cheat.h"
#include "i_system.h"
@ -58,6 +61,8 @@
#include "am_map.h"
#include "a_artifacts.h"
#include "po_man.h"
#include "a_keys.h"
struct AMColor
{
@ -79,7 +84,7 @@ struct AMColor
static AMColor Background, YourColor, WallColor, TSWallColor,
FDWallColor, CDWallColor, ThingColor,
ThingColor_Item, ThingColor_Monster, ThingColor_Friend,
ThingColor_Item, ThingColor_CountItem, ThingColor_Monster, ThingColor_Friend,
SecretWallColor, GridColor, XHairColor,
NotSeenColor,
LockedColor,
@ -170,12 +175,27 @@ CVAR (Color, am_secretsectorcolor, 0xff00ff, CVAR_ARCHIVE);
CVAR (Color, am_ovsecretsectorcolor,0x00ffff, CVAR_ARCHIVE);
CVAR (Int, am_map_secrets, 1, CVAR_ARCHIVE);
CVAR (Bool, am_drawmapback, true, CVAR_ARCHIVE);
CVAR (Bool, am_showkeys, true, CVAR_ARCHIVE);
CVAR (Color, am_thingcolor_friend, 0xfcfcfc, CVAR_ARCHIVE);
CVAR (Color, am_thingcolor_monster, 0xfcfcfc, CVAR_ARCHIVE);
CVAR (Color, am_thingcolor_item, 0xfcfcfc, CVAR_ARCHIVE);
CVAR (Color, am_thingcolor_citem, 0xfcfcfc, CVAR_ARCHIVE);
CVAR (Color, am_ovthingcolor_friend, 0xe88800, CVAR_ARCHIVE);
CVAR (Color, am_ovthingcolor_monster, 0xe88800, CVAR_ARCHIVE);
CVAR (Color, am_ovthingcolor_item, 0xe88800, CVAR_ARCHIVE);
CVAR (Color, am_ovthingcolor_citem, 0xe88800, CVAR_ARCHIVE);
static int bigstate = 0;
static bool textured = 1; // internal toggle for texture mode
CUSTOM_CVAR(Bool, am_textured, false, CVAR_ARCHIVE)
{
textured |= self;
}
CVAR(Int, am_showsubsector, -1, 0);
// Disable the ML_DONTDRAW line flag if x% of all lines in a map are flagged with it
// (To counter annoying mappers who think they are smart by making the automap unusable)
@ -216,21 +236,6 @@ CUSTOM_CVAR (Int, am_showalllines, -1, 0) // This is a cheat so don't save it.
}
// drawing stuff
#define AM_PANDOWNKEY KEY_DOWNARROW
#define AM_PANUPKEY KEY_UPARROW
#define AM_PANRIGHTKEY KEY_RIGHTARROW
#define AM_PANLEFTKEY KEY_LEFTARROW
#define AM_ZOOMINKEY KEY_EQUALS
#define AM_ZOOMINKEY2 0x4e // DIK_ADD
#define AM_ZOOMOUTKEY KEY_MINUS
#define AM_ZOOMOUTKEY2 0x4a // DIK_SUBTRACT
#define AM_GOBIGKEY 0x0b // DIK_0
#define AM_FOLLOWKEY 'f'
#define AM_GRIDKEY 'g'
#define AM_MARKKEY 'm'
#define AM_CLEARMARKKEY 'c'
#define AM_NUMMARKPOINTS 10
// player radius for automap checking
@ -241,10 +246,10 @@ CUSTOM_CVAR (Int, am_showalllines, -1, 0) // This is a cheat so don't save it.
#define F_PANINC (140/TICRATE)
// how much zoom-in per tic
// goes to 2x in 1 second
#define M_ZOOMIN ((int) (1.02*MAPUNIT))
#define M_ZOOMIN (1.02*MAPUNIT)
// how much zoom-out per tic
// pulls out to 0.5x in 1 second
#define M_ZOOMOUT ((int) (MAPUNIT/1.02))
#define M_ZOOMOUT (MAPUNIT/1.02)
// translates between frame-buffer and map coordinates
#define CXMTOF(x) (MTOF((x)-m_x)/* - f_x*/)
@ -292,25 +297,22 @@ mline_t player_arrow[] = {
{ { -R+3*R/8, 0 }, { -R+R/8, R/4 } }, // >>--->
{ { -R+3*R/8, 0 }, { -R+R/8, -R/4 } }
};
#define NUMPLYRLINES (sizeof(player_arrow)/sizeof(mline_t))
mline_t player_arrow_raven[] = {
{ { -R+R/4, 0 }, { 0, 0} }, // center line.
{ { -R+R/4, R/8 }, { R, 0} }, // blade
{ { -R+R/4, -R/8 }, { R, 0 } },
{ { -R+R/4, -R/4 }, { -R+R/4, R/4 } }, // crosspiece
{ { -R+R/8, -R/4 }, { -R+R/8, R/4 } },
{ { -R+R/8, -R/4 }, { -R+R/4, -R/4} }, //crosspiece connectors
{ { -R+R/8, R/4 }, { -R+R/4, R/4} },
{ { -R-R/4, R/8 }, { -R-R/4, -R/8 } }, //pommel
{ { -R-R/4, R/8 }, { -R+R/8, R/8 } },
{ { -R-R/4, -R/8}, { -R+R/8, -R/8 } }
};
#undef R
#define NUMPLYRLINES (sizeof(player_arrow)/sizeof(mline_t))
{ { -R+R/4, 0 }, { 0, 0} }, // center line.
{ { -R+R/4, R/8 }, { R, 0} }, // blade
{ { -R+R/4, -R/8 }, { R, 0 } },
{ { -R+R/4, -R/4 }, { -R+R/4, R/4 } }, // crosspiece
{ { -R+R/8, -R/4 }, { -R+R/8, R/4 } },
{ { -R+R/8, -R/4 }, { -R+R/4, -R/4} }, //crosspiece connectors
{ { -R+R/8, R/4 }, { -R+R/4, R/4} },
{ { -R-R/4, R/8 }, { -R-R/4, -R/8 } }, //pommel
{ { -R-R/4, R/8 }, { -R+R/8, R/8 } },
{ { -R-R/4, -R/8}, { -R+R/8, -R/8 } }
};
#define NUMPLYRLINES_RAVEN (sizeof(player_arrow_raven)/sizeof(mline_t))
#define R ((8*PLAYERRADIUS)/7)
mline_t cheat_player_arrow[] = {
{ { -R+R/8, 0 }, { R, 0 } }, // -----
{ { R, 0 }, { R-R/2, R/6 } }, // ----->
@ -329,9 +331,9 @@ mline_t cheat_player_arrow[] = {
{ { R/6, -R/7 }, { R/6+R/32, -R/7-R/32 } },
{ { R/6+R/32, -R/7-R/32 }, { R/6+R/10, -R/7 } }
};
#define NUMCHEATPLYRLINES (sizeof(cheat_player_arrow)/sizeof(mline_t))
#undef R
#define NUMCHEATPLYRLINES (sizeof(cheat_player_arrow)/sizeof(mline_t))
#define R (MAPUNIT)
// [RH] Avoid lots of warnings without compiler-specific #pragmas
@ -348,9 +350,37 @@ mline_t thintriangle_guy[] = {
L (1,0, -.5,.7),
L (-.5,.7, -.5,-.7)
};
#define NUMTHINTRIANGLEGUYLINES (sizeof(thintriangle_guy)/sizeof(mline_t))
mline_t square_guy[] = {
L (0,1,1,0),
L (1,0,0,-1),
L (0,-1,-1,0),
L (-1,0,0,1)
};
#define NUMSQUAREGUYLINES (sizeof(square_guy)/sizeof(mline_t))
#undef R
#define R (MAPUNIT)
mline_t key_guy[] = {
L (-2, 0, -1.7, -0.5),
L (-1.7, -0.5, -1.5, -0.7),
L (-1.5, -0.7, -0.8, -0.5),
L (-0.8, -0.5, -0.6, 0),
L (-0.6, 0, -0.8, 0.5),
L (-1.5, 0.7, -0.8, 0.5),
L (-1.7, 0.5, -1.5, 0.7),
L (-2, 0, -1.7, 0.5),
L (-0.6, 0, 2, 0),
L (1.7, 0, 1.7, -1),
L (1.5, 0, 1.5, -1),
L (1.3, 0, 1.3, -1)
};
#define NUMKEYGUYLINES (sizeof(key_guy)/sizeof(mline_t))
#undef L
#undef R
#define NUMTHINTRIANGLEGUYLINES (sizeof(thintriangle_guy)/sizeof(mline_t))
@ -383,7 +413,7 @@ static int amclock;
static mpoint_t m_paninc; // how far the window pans each tic (map coords)
static fixed_t mtof_zoommul; // how far the window zooms in each tic (map coords)
static fixed_t ftom_zoommul; // how far the window zooms in each tic (fb coords)
static float am_zoomdir;
static fixed_t m_x, m_y; // LL x,y where the window is on the map (map coords)
static fixed_t m_x2, m_y2; // UR x,y where the window is on the map (map coords)
@ -432,7 +462,69 @@ static void AM_calcMinMaxMtoF();
void AM_rotatePoint (fixed_t *x, fixed_t *y);
void AM_rotate (fixed_t *x, fixed_t *y, angle_t an);
void AM_doFollowPlayer ();
static void AM_ToggleFollowPlayer();
//=============================================================================
//
// map functions
//
//=============================================================================
bool AM_addMark ();
bool AM_clearMarks ();
void AM_saveScaleAndLoc ();
void AM_restoreScaleAndLoc ();
void AM_minOutWindowScale ();
CCMD(am_togglefollow)
{
followplayer = !followplayer;
f_oldloc.x = FIXED_MAX;
Printf ("%s\n", GStrings(followplayer ? "AMSTR_FOLLOWON" : "AMSTR_FOLLOWOFF"));
}
CCMD(am_togglegrid)
{
grid = !grid;
Printf ("%s\n", GStrings(grid ? "AMSTR_GRIDON" : "AMSTR_GRIDOFF"));
}
CCMD(am_toggletexture)
{
if (am_textured && hasglnodes)
{
textured = !textured;
Printf ("%s\n", GStrings(textured ? "AMSTR_TEXON" : "AMSTR_TEXOFF"));
}
}
CCMD(am_setmark)
{
if (AM_addMark())
{
Printf ("%s %d\n", GStrings("AMSTR_MARKEDSPOT"), markpointnum);
}
}
CCMD(am_clearmarks)
{
if (AM_clearMarks())
{
Printf ("%s\n", GStrings("AMSTR_MARKSCLEARED"));
}
}
CCMD(am_gobig)
{
bigstate = !bigstate;
if (bigstate)
{
AM_saveScaleAndLoc();
AM_minOutWindowScale();
}
else
AM_restoreScaleAndLoc();
}
// Calculates the slope and slope according to the x-axis of a line
// segment in map coordinates (with the upright y-axis n' all) so
@ -741,11 +833,19 @@ void AM_initVariables ()
automapactive = true;
// Reset AM buttons
Button_AM_PanLeft.Reset();
Button_AM_PanRight.Reset();
Button_AM_PanUp.Reset();
Button_AM_PanDown.Reset();
Button_AM_ZoomIn.Reset();
Button_AM_ZoomOut.Reset();
f_oldloc.x = FIXED_MAX;
amclock = 0;
m_paninc.x = m_paninc.y = 0;
ftom_zoommul = MAPUNIT;
mtof_zoommul = MAPUNIT;
m_w = FTOM(SCREENWIDTH);
@ -813,6 +913,7 @@ static void AM_initColors (bool overlayed)
SecretWallColor = WallColor;
SecretSectorColor.FromCVar (am_ovsecretsectorcolor);
ThingColor_Item.FromCVar (am_ovthingcolor_item);
ThingColor_CountItem.FromCVar (am_ovthingcolor_citem);
ThingColor_Friend.FromCVar (am_ovthingcolor_friend);
ThingColor_Monster.FromCVar (am_ovthingcolor_monster);
ThingColor.FromCVar (am_ovthingcolor);
@ -836,6 +937,7 @@ static void AM_initColors (bool overlayed)
FDWallColor.FromCVar (am_fdwallcolor);
CDWallColor.FromCVar (am_cdwallcolor);
ThingColor_Item.FromCVar (am_thingcolor_item);
ThingColor_CountItem.FromCVar (am_thingcolor_citem);
ThingColor_Friend.FromCVar (am_thingcolor_friend);
ThingColor_Monster.FromCVar (am_thingcolor_monster);
ThingColor.FromCVar (am_thingcolor);
@ -1122,127 +1224,28 @@ void AM_ToggleMap ()
//
//=============================================================================
bool AM_Responder (event_t *ev)
bool AM_Responder (event_t *ev, bool last)
{
bool rc;
static int cheatstate = 0;
static int bigstate = 0;
rc = false;
if (automapactive && ev->type == EV_KeyDown)
if (automapactive && (ev->type == EV_KeyDown || ev->type == EV_KeyUp))
{
rc = true;
switch (ev->data1)
if (followplayer)
{
case AM_PANRIGHTKEY: // pan right
if (!followplayer)
m_paninc.x = FTOM(F_PANINC);
else
rc = false;
break;
case AM_PANLEFTKEY: // pan left
if (!followplayer)
m_paninc.x = -FTOM(F_PANINC);
else
rc = false;
break;
case AM_PANUPKEY: // pan up
if (!followplayer)
m_paninc.y = FTOM(F_PANINC);
else
rc = false;
break;
case AM_PANDOWNKEY: // pan down
if (!followplayer)
m_paninc.y = -FTOM(F_PANINC);
else
rc = false;
break;
case AM_ZOOMOUTKEY: // zoom out
case AM_ZOOMOUTKEY2:
mtof_zoommul = M_ZOOMOUT;
ftom_zoommul = M_ZOOMIN;
break;
case AM_ZOOMINKEY: // zoom in
case AM_ZOOMINKEY2:
mtof_zoommul = M_ZOOMIN;
ftom_zoommul = M_ZOOMOUT;
break;
case AM_GOBIGKEY:
bigstate = !bigstate;
if (bigstate)
{
AM_saveScaleAndLoc();
AM_minOutWindowScale();
}
else
AM_restoreScaleAndLoc();
break;
default:
switch (ev->data2)
{
case AM_FOLLOWKEY:
AM_ToggleFollowPlayer();
break;
case AM_GRIDKEY:
grid = !grid;
Printf ("%s\n", GStrings(grid ? "AMSTR_GRIDON" : "AMSTR_GRIDOFF"));
break;
case AM_MARKKEY:
if (AM_addMark())
{
Printf ("%s %d\n", GStrings("AMSTR_MARKEDSPOT"), markpointnum);
}
else
{
rc = false;
}
break;
case AM_CLEARMARKKEY:
if (AM_clearMarks())
{
Printf ("%s\n", GStrings("AMSTR_MARKSCLEARED"));
}
else
{
rc = false;
}
break;
default:
cheatstate = 0;
rc = false;
}
// check for am_pan* and ignore in follow mode
const char *defbind = AutomapBindings.GetBind(ev->data1);
if (!strnicmp(defbind, "+am_pan", 7)) return false;
}
}
else if (ev->type == EV_KeyUp)
{
rc = false;
switch (ev->data1)
{
case AM_PANRIGHTKEY:
if (!followplayer) m_paninc.x = 0;
break;
case AM_PANLEFTKEY:
if (!followplayer) m_paninc.x = 0;
break;
case AM_PANUPKEY:
if (!followplayer) m_paninc.y = 0;
break;
case AM_PANDOWNKEY:
if (!followplayer) m_paninc.y = 0;
break;
case AM_ZOOMOUTKEY:
case AM_ZOOMOUTKEY2:
case AM_ZOOMINKEY:
case AM_ZOOMINKEY2:
mtof_zoommul = MAPUNIT;
ftom_zoommul = MAPUNIT;
break;
}
}
return rc;
bool res = C_DoKey(ev, &AutomapBindings, NULL);
if (res && ev->type == EV_KeyUp && !last)
{
// If this is a release event we also need to check if it released a button in the main Bindings
// so that that button does not get stuck.
const char *defbind = Bindings.GetBind(ev->data1);
return (defbind[0] != '+'); // Let G_Responder handle button releases
}
return res;
}
return false;
}
@ -1254,6 +1257,26 @@ bool AM_Responder (event_t *ev)
void AM_changeWindowScale ()
{
int mtof_zoommul;
if (am_zoomdir > 0)
{
mtof_zoommul = int(M_ZOOMIN * am_zoomdir);
}
else if (am_zoomdir < 0)
{
mtof_zoommul = int(M_ZOOMOUT / -am_zoomdir);
}
else if (Button_AM_ZoomIn.bDown)
{
mtof_zoommul = int(M_ZOOMIN);
}
else if (Button_AM_ZoomOut.bDown)
{
mtof_zoommul = int(M_ZOOMOUT);
}
am_zoomdir = 0;
// Change the scaling multipliers
scale_mtof = MapMul(scale_mtof, mtof_zoommul);
scale_ftom = MapDiv(MAPUNIT, scale_mtof);
@ -1264,6 +1287,13 @@ void AM_changeWindowScale ()
AM_maxOutWindowScale();
}
CCMD(am_zoom)
{
if (argv.argc() >= 2)
{
am_zoomdir = (float)atof(argv[1]);
}
}
//=============================================================================
//
@ -1298,19 +1328,6 @@ void AM_doFollowPlayer ()
}
}
//=============================================================================
//
//
//
//=============================================================================
static void AM_ToggleFollowPlayer()
{
followplayer = !followplayer;
f_oldloc.x = FIXED_MAX;
Printf ("%s\n", GStrings(followplayer ? "AMSTR_FOLLOWON" : "AMSTR_FOLLOWOFF"));
}
//=============================================================================
//
// Updates on Game Tick
@ -1325,10 +1342,20 @@ void AM_Ticker ()
amclock++;
if (followplayer)
{
AM_doFollowPlayer();
}
else
{
m_paninc.x = m_paninc.y = 0;
if (Button_AM_PanLeft.bDown) m_paninc.x -= FTOM(F_PANINC);
if (Button_AM_PanRight.bDown) m_paninc.x += FTOM(F_PANINC);
if (Button_AM_PanUp.bDown) m_paninc.y += FTOM(F_PANINC);
if (Button_AM_PanDown.bDown) m_paninc.y -= FTOM(F_PANINC);
}
// Change the zoom if necessary
if (ftom_zoommul != MAPUNIT)
if (Button_AM_ZoomIn.bDown || Button_AM_ZoomOut.bDown || am_zoomdir != 0)
AM_changeWindowScale();
// Change x,y location
@ -1586,6 +1613,97 @@ void AM_drawGrid (const AMColor &color)
}
}
//=============================================================================
//
// AM_drawSubsectors
//
//=============================================================================
void AM_drawSubsectors()
{
static TArray<FVector2> points;
float scale = float(scale_mtof);
angle_t rotation;
sector_t tempsec;
int floorlight, ceilinglight;
double originx, originy;
FDynamicColormap *colormap;
for (int i = 0; i < numsubsectors; ++i)
{
if (subsectors[i].flags & SSECF_POLYORG)
{
continue;
}
if ((!(subsectors[i].flags & SSECF_DRAWN) || (subsectors[i].render_sector->MoreFlags & SECF_HIDDEN)) && am_cheat == 0)
{
continue;
}
// Fill the points array from the subsector.
points.Resize(subsectors[i].numlines);
for (DWORD j = 0; j < subsectors[i].numlines; ++j)
{
mpoint_t pt = { subsectors[i].firstline[j].v1->x >> FRACTOMAPBITS,
subsectors[i].firstline[j].v1->y >> FRACTOMAPBITS };
if (am_rotate == 1 || (am_rotate == 2 && viewactive))
{
AM_rotatePoint(&pt.x, &pt.y);
}
points[j].X = f_x + ((pt.x - m_x) * scale / float(1 << 24));
points[j].Y = f_y + (f_h - (pt.y - m_y) * scale / float(1 << 24));
}
// For lighting and texture determination
sector_t *sec = R_FakeFlat (subsectors[i].render_sector, &tempsec, &floorlight,
&ceilinglight, false);
// Find texture origin.
mpoint_t originpt = { -sec->GetXOffset(sector_t::floor) >> FRACTOMAPBITS,
sec->GetYOffset(sector_t::floor) >> FRACTOMAPBITS };
rotation = 0 - sec->GetAngle(sector_t::floor);
// Apply the floor's rotation to the texture origin.
if (rotation != 0)
{
AM_rotate(&originpt.x, &originpt.y, rotation);
}
// Apply the automap's rotation to the texture origin.
if (am_rotate == 1 || (am_rotate == 2 && viewactive))
{
rotation += ANG90 - players[consoleplayer].camera->angle;
AM_rotatePoint(&originpt.x, &originpt.y);
}
originx = f_x + ((originpt.x - m_x) * scale / float(1 << 24));
originy = f_y + (f_h - (originpt.y - m_y) * scale / float(1 << 24));
// Coloring for the polygon
colormap = sec->ColorMap;
// If this subsector has not actually been seen yet (because you are cheating
// to see it on the map), tint and desaturate it.
if (!(subsectors[i].flags & SSECF_DRAWN))
{
colormap = GetSpecialLights(
MAKERGB(
(colormap->Color.r + 255) / 2,
(colormap->Color.g + 200) / 2,
(colormap->Color.b + 160) / 2),
colormap->Fade,
255 - (255 - colormap->Desaturate) / 4);
floorlight = (floorlight + 200*15) / 16;
}
// Draw the polygon.
screen->FillSimplePoly(
TexMan(sec->GetTexture(sector_t::floor)),
&points[0], points.Size(),
originx, originy,
scale / (FIXED2FLOAT(sec->GetXScale(sector_t::floor)) * float(1 << MAPBITS)),
scale / (FIXED2FLOAT(sec->GetYScale(sector_t::floor)) * float(1 << MAPBITS)),
rotation,
colormap,
floorlight
);
}
}
//=============================================================================
//
//
@ -1614,6 +1732,80 @@ static bool AM_CheckSecret(line_t *line)
}
//=============================================================================
//
// Polyobject debug stuff
//
//=============================================================================
void AM_drawSeg(seg_t *seg, const AMColor &color)
{
mline_t l;
l.a.x = seg->v1->x >> FRACTOMAPBITS;
l.a.y = seg->v1->y >> FRACTOMAPBITS;
l.b.x = seg->v2->x >> FRACTOMAPBITS;
l.b.y = seg->v2->y >> FRACTOMAPBITS;
if (am_rotate == 1 || (am_rotate == 2 && viewactive))
{
AM_rotatePoint (&l.a.x, &l.a.y);
AM_rotatePoint (&l.b.x, &l.b.y);
}
AM_drawMline(&l, color);
}
void AM_drawPolySeg(FPolySeg *seg, const AMColor &color)
{
mline_t l;
l.a.x = seg->v1.x >> FRACTOMAPBITS;
l.a.y = seg->v1.y >> FRACTOMAPBITS;
l.b.x = seg->v2.x >> FRACTOMAPBITS;
l.b.y = seg->v2.y >> FRACTOMAPBITS;
if (am_rotate == 1 || (am_rotate == 2 && viewactive))
{
AM_rotatePoint (&l.a.x, &l.a.y);
AM_rotatePoint (&l.b.x, &l.b.y);
}
AM_drawMline(&l, color);
}
void AM_showSS()
{
if (am_showsubsector >= 0 && am_showsubsector < numsubsectors)
{
AMColor yellow;
yellow.FromRGB(255,255,0);
AMColor red;
red.FromRGB(255,0,0);
subsector_t *sub = &subsectors[am_showsubsector];
for (unsigned int i = 0; i < sub->numlines; i++)
{
AM_drawSeg(sub->firstline + i, yellow);
}
PO_LinkToSubsectors();
for (int i = 0; i <po_NumPolyobjs; i++)
{
FPolyObj *po = &polyobjs[i];
FPolyNode *pnode = po->subsectorlinks;
while (pnode != NULL)
{
if (pnode->subsector == sub)
{
for (unsigned j = 0; j < pnode->segs.Size(); j++)
{
AM_drawPolySeg(&pnode->segs[j], red);
}
}
pnode = pnode->snext;
}
}
}
}
//=============================================================================
//
// Determines visible lines, draws them.
@ -1746,14 +1938,27 @@ void AM_drawWalls (bool allmap)
//
//=============================================================================
void AM_rotate (fixed_t *x, fixed_t *y, angle_t a)
void AM_rotate(fixed_t *xp, fixed_t *yp, angle_t a)
{
fixed_t tmpx;
static angle_t angle_saved = 0;
static double sinrot = 0;
static double cosrot = 1;
a >>= ANGLETOFINESHIFT;
tmpx = DMulScale16 (*x,finecosine[a],*y,-finesine[a]);
*y = DMulScale16 (*x,finesine[a],*y,finecosine[a]);
*x = tmpx;
if (angle_saved != a)
{
angle_saved = a;
double rot = (double)a / (double)(1u << 31) * (double)M_PI;
sinrot = sin(rot);
cosrot = cos(rot);
}
double x = FIXED2FLOAT(*xp);
double y = FIXED2FLOAT(*yp);
double tmpx = (x * cosrot) - (y * sinrot);
y = (x * sinrot) + (y * cosrot);
x = tmpx;
*xp = FLOAT2FIXED(x);
*yp = FLOAT2FIXED(y);
}
//=============================================================================
@ -1964,11 +2169,40 @@ void AM_drawThings ()
if (t->flags & MF_FRIENDLY || !(t->flags & MF_COUNTKILL)) color = ThingColor_Friend;
else color = ThingColor_Monster;
}
else if (t->flags&MF_SPECIAL) color = ThingColor_Item;
else if (t->flags&MF_SPECIAL)
{
// Find the key's own color.
// Only works correctly if single-key locks have lower numbers than any-key locks.
// That is the case for all default keys, however.
if (t->IsKindOf(RUNTIME_CLASS(AKey)))
{
if (am_showkeys)
{
int P_GetMapColorForKey (AInventory * key);
int c = P_GetMapColorForKey(static_cast<AKey *>(t));
AM_drawLineCharacter
(thintriangle_guy, NUMTHINTRIANGLEGUYLINES,
16<<MAPBITS, angle, color, p.x, p.y);
if (c >= 0) color.FromRGB(RPART(c), GPART(c), BPART(c));
else color = ThingColor_CountItem;
AM_drawLineCharacter(key_guy, NUMKEYGUYLINES, 16<<MAPBITS, 0, color, p.x, p.y);
color.Index = -1;
}
else
{
color = ThingColor_Item;
}
}
else if (t->flags&MF_COUNTITEM)
color = ThingColor_CountItem;
else
color = ThingColor_Item;
}
if (color.Index != -1)
{
AM_drawLineCharacter
(thintriangle_guy, NUMTHINTRIANGLEGUYLINES,
16<<MAPBITS, angle, color, p.x, p.y);
}
if (am_cheat >= 3)
{
@ -2093,7 +2327,11 @@ void AM_drawAuthorMarkers ()
while (marked != NULL)
{
if (mark->args[1] == 0 || (mark->args[1] == 1 && marked->Sector->MoreFlags & SECF_DRAWN))
// Use more correct info if we have GL nodes available
if (mark->args[1] == 0 ||
(mark->args[1] == 1 && (hasglnodes ?
marked->subsector->flags & SSECF_DRAWN :
marked->Sector->MoreFlags & SECF_DRAWN)))
{
DrawMarker (tex, marked->x >> FRACTOMAPBITS, marked->y >> FRACTOMAPBITS, 0,
flip, mark->scaleX, mark->scaleY, mark->Translation,
@ -2152,6 +2390,9 @@ void AM_Drawer ()
}
AM_activateNewScale();
if (am_textured && hasglnodes && textured && !viewactive)
AM_drawSubsectors();
if (grid)
AM_drawGrid(GridColor);
@ -2166,6 +2407,8 @@ void AM_Drawer ()
AM_drawCrosshair(XHairColor);
AM_drawMarks();
AM_showSS();
}
//=============================================================================

View file

@ -26,7 +26,7 @@ struct event_t;
class FArchive;
// Called by main loop.
bool AM_Responder (event_t* ev);
bool AM_Responder (event_t* ev, bool last);
// Called by main loop.
void AM_Ticker (void);

View file

@ -93,7 +93,16 @@ setupvlineasm:
selfmod premach3a, machvsh8+6
ret
%ifdef M_TARGET_MACHO
SECTION .text align=64
%else
SECTION .rtext progbits alloc exec write align=64
%endif
%ifdef M_TARGET_MACHO
GLOBAL _rtext_a_start
_rtext_a_start:
%endif
;eax = xscale
;ebx = palookupoffse
@ -325,6 +334,7 @@ setupmvlineasm:
mov ecx, dword [esp+4]
mov byte [maskmach3a+2], cl
mov byte [machmv13+2], cl
mov byte [machmv14+2], cl
mov byte [machmv15+2], cl
mov byte [machmv16+2], cl
@ -538,3 +548,8 @@ ALIGN 16
mvcase0: jmp beginmvlineasm4
align 16
%ifdef M_TARGET_MACHO
GLOBAL _rtext_a_end
_rtext_a_end:
%endif

View file

@ -285,7 +285,16 @@ R_SetSpanSize_ASM:
aret: ret
%ifdef M_TARGET_MACHO
SECTION .text align=64
%else
SECTION .rtext progbits alloc exec write align=64
%endif
%ifdef M_TARGET_MACHO
GLOBAL _rtext_tmap_start
_rtext_tmap_start:
%endif
rtext_start:
@ -300,6 +309,8 @@ GLOBAL R_DrawSpanP_ASM
; edi: dest
; ebp: scratch
; esi: count
; [esp]: xstep
; [esp+4]: ystep
align 16
@ -315,6 +326,7 @@ R_DrawSpanP_ASM:
push edi
push ebp
push esi
sub esp, 8
mov edi,ecx
add edi,[dc_destorg]
@ -326,13 +338,13 @@ dsy1: shl edx,6
dsy3: shr ebp,26
xor ebx,ebx
lea esi,[eax+1]
mov [ds_xstep],edx
mov [esp],edx
mov edx,[ds_ystep]
mov ecx,[ds_xfrac]
dsy4: shr ecx,26
dsm8: and edx,0xffffffc0
or ebp,edx
mov [ds_ystep],ebp
mov [esp+4],ebp
mov ebp,[ds_yfrac]
mov edx,[ds_xfrac]
dsy2: shl edx,6
@ -346,8 +358,8 @@ dsm9: and ebp,0xffffffc0
mov ebp,ecx
dsx1: rol ebp,6
dsm1: and ebp,0xfff
add edx,[ds_xstep]
adc ecx,[ds_ystep]
add edx,[esp]
adc ecx,[esp+4]
spreada mov bl,[ebp+SPACEFILLER4]
spmapa mov bl,[ebx+SPACEFILLER4]
mov [edi],bl
@ -358,13 +370,13 @@ dseven1 shr esi,1
; do two more pixels
mov ebp,ecx
add edx,[ds_xstep]
adc ecx,[ds_ystep]
add edx,[esp]
adc ecx,[esp+4]
dsm2: and ebp,0xfc00003f
dsx2: rol ebp,6
mov eax,ecx
add edx,[ds_xstep]
adc ecx,[ds_ystep]
add edx,[esp]
adc ecx,[esp+4]
spreadb mov bl,[ebp+SPACEFILLER4] ;read texel1
dsx3: rol eax,6
dsm6: and eax,0xfff
@ -383,13 +395,13 @@ dsrest test esi,esi
align 16
dsloop mov ebp,ecx
spstep1d add edx,[ds_xstep]
spstep2d adc ecx,[ds_ystep]
spstep1d add edx,[esp]
spstep2d adc ecx,[esp+4]
dsm3: and ebp,0xfc00003f
dsx4: rol ebp,6
mov eax,ecx
spstep1e add edx,[ds_xstep]
spstep2e adc ecx,[ds_ystep]
spstep1e add edx,[esp]
spstep2e adc ecx,[esp+4]
spreadd mov bl,[ebp+SPACEFILLER4] ;read texel1
dsx5: rol eax,6
dsm5: and eax,0xfff
@ -397,8 +409,8 @@ spmapd mov bl,[ebx+SPACEFILLER4] ;map texel1
mov [edi],bl ;store texel1
mov ebp,ecx
spreade mov bl,[eax+SPACEFILLER4] ;read texel2
spstep1f add edx,[ds_xstep]
spstep2f adc ecx,[ds_ystep]
spstep1f add edx,[esp]
spstep2f adc ecx,[esp+4]
dsm4: and ebp,0xfc00003f
dsx6: rol ebp,6
spmape mov bl,[ebx+SPACEFILLER4] ;map texel2
@ -411,14 +423,15 @@ dsx7: rol eax,6
dsm7: and eax,0xfff
mov [edi-2],bl ;store texel3
spreadg mov bl,[eax+SPACEFILLER4] ;read texel4
spstep1g add edx,[ds_xstep]
spstep2g adc ecx,[ds_ystep]
spstep1g add edx,[esp]
spstep2g adc ecx,[esp+4]
spmapg mov bl,[ebx+SPACEFILLER4] ;map texel4
dec esi
mov [edi-1],bl ;store texel4
jnz near dsloop
dsdone pop esi
dsdone add esp,8
pop esi
pop ebp
pop edi
pop ebx
@ -439,6 +452,8 @@ GLOBAL R_DrawSpanMaskedP_ASM
; edi: dest
; ebp: scratch
; esi: count
; [esp]: xstep
; [esp+4]: ystep
align 16
@ -454,6 +469,7 @@ R_DrawSpanMaskedP_ASM:
push edi
push ebp
push esi
sub esp,8
mov edi,ecx
add edi,[dc_destorg]
@ -465,13 +481,13 @@ dmsy1: shl edx,6
dmsy3: shr ebp,26
xor ebx,ebx
lea esi,[eax+1]
mov [ds_xstep],edx
mov [esp],edx
mov edx,[ds_ystep]
mov ecx,[ds_xfrac]
dmsy4: shr ecx,26
dmsm8: and edx,0xffffffc0
or ebp,edx
mov [ds_ystep],ebp
mov [esp+4],ebp
mov ebp,[ds_yfrac]
mov edx,[ds_xfrac]
dmsy2: shl edx,6
@ -485,8 +501,8 @@ dmsm9: and ebp,0xffffffc0
mov ebp,ecx
dmsx1: rol ebp,6
dmsm1: and ebp,0xfff
add edx,[ds_xstep]
adc ecx,[ds_ystep]
add edx,[esp]
adc ecx,[esp+4]
mspreada mov bl,[ebp+SPACEFILLER4]
cmp bl,0
je mspskipa
@ -499,13 +515,13 @@ dmseven1 shr esi,1
; do two more pixels
mov ebp,ecx
add edx,[ds_xstep]
adc ecx,[ds_ystep]
add edx,[esp]
adc ecx,[esp+4]
dmsm2: and ebp,0xfc00003f
dmsx2: rol ebp,6
mov eax,ecx
add edx,[ds_xstep]
adc ecx,[ds_ystep]
add edx,[esp]
adc ecx,[esp+4]
mspreadb mov bl,[ebp+SPACEFILLER4] ;read texel1
dmsx3: rol eax,6
dmsm6: and eax,0xfff
@ -528,13 +544,13 @@ dmsrest test esi,esi
align 16
dmsloop mov ebp,ecx
mspstep1d add edx,[ds_xstep]
mspstep2d adc ecx,[ds_ystep]
mspstep1d add edx,[esp]
mspstep2d adc ecx,[esp+4]
dmsm3: and ebp,0xfc00003f
dmsx4: rol ebp,6
mov eax,ecx
mspstep1e add edx,[ds_xstep]
mspstep2e adc ecx,[ds_ystep]
mspstep1e add edx,[esp]
mspstep2e adc ecx,[esp+4]
mspreadd mov bl,[ebp+SPACEFILLER4] ;read texel1
dmsx5: rol eax,6
dmsm5: and eax,0xfff
@ -544,8 +560,8 @@ dmsm5: and eax,0xfff
mspmapd mov bl,[ebx+SPACEFILLER4] ;map texel1
mov [edi],bl ;store texel1
mspreade mov bl,[eax+SPACEFILLER4] ;read texel2
mspstep1f add edx,[ds_xstep]
mspstep2f adc ecx,[ds_ystep]
mspstep1f add edx,[esp]
mspstep2f adc ecx,[esp+4]
dmsm4: and ebp,0xfc00003f
dmsx6: rol ebp,6
cmp bl,0
@ -562,8 +578,8 @@ dmsm7: and eax,0xfff
mspmapf mov bl,[ebx+SPACEFILLER4] ;map texel3
mov [edi-2],bl ;store texel3
mspreadg mov bl,[eax+SPACEFILLER4] ;read texel4
mspstep1g add edx,[ds_xstep]
mspstep2g adc ecx,[ds_ystep]
mspstep1g add edx,[esp]
mspstep2g adc ecx,[esp+4]
cmp bl,0
je mspskipg
mspmapg mov bl,[ebx+SPACEFILLER4] ;map texel4
@ -571,7 +587,8 @@ mspmapg mov bl,[ebx+SPACEFILLER4] ;map texel4
mspskipg dec esi
jnz near dmsloop
dmsdone pop esi
dmsdone add esp,8
pop esi
pop ebp
pop edi
pop ebx
@ -1738,6 +1755,10 @@ ac4nil: pop edi
ret
rtext_end:
%ifdef M_TARGET_MACHO
GLOBAL _rtext_tmap_end
_rtext_tmap_end:
%endif
align 16
;************************

View file

@ -216,7 +216,13 @@ SetTiltedSpanSize:
ret
%ifndef M_TARGET_MACHO
SECTION .rtext progbits alloc exec write align=64
%else
SECTION .text align=64
GLOBAL _rtext_tmap2_start
_rtext_tmap2_start:
%endif
rtext_start:
@ -628,3 +634,7 @@ fetch10 mov al,[ebp+esi+SPACEFILLER4]
ret
rtext_end:
%ifdef M_TARGET_MACHO
GLOBAL _rtext_tmap2_end
_rtext_tmap2_end:
%endif

View file

@ -80,7 +80,13 @@ setupvlinetallasm:
selfmod shifter1, shift12+6
ret
%ifdef M_TARGET_MACHO
SECTION .text align=64
GLOBAL _rtext_tmap3_start
_rtext_tmap3_start:
%else
SECTION .rtext progbits alloc exec write align=64
%endif
ALIGN 16
@ -331,3 +337,8 @@ shift12: shr ecx,16
pop ebx
pop ebp
ret
%ifdef M_TARGET_MACHO
GLOBAL _rtext_tmap3_end
_rtext_tmap3_end:
%endif

View file

@ -1,28 +1,28 @@
#%include "valgrind.inc"
#%include "valgrind.inc"
.section .text
.section .text
.globl ASM_PatchPitch
ASM_PatchPitch:
.globl ASM_PatchPitch
ASM_PatchPitch:
movl dc_pitch(%rip), %ecx
movl %ecx, pm+3(%rip)
movl %ecx, vltpitch+3(%rip)
# selfmod pm, vltpitch+6
ret
.align 16
movl %ecx, vltpitch+3(%rip)
# selfmod pm, vltpitch+6
ret
.align 16
.globl setupvlinetallasm
.globl setupvlinetallasm
setupvlinetallasm:
movb %dil, shifter1+2(%rip)
movb %dil, shifter2+2(%rip)
movb %dil, shifter3+2(%rip)
movb %dil, shifter4+2(%rip)
# selfmod shifter1, shifter4+3
movb %dil, shifter4+2(%rip)
# selfmod shifter1, shifter4+3
ret
.align 16
.section .rtext,"awx"
.align 16
.section .rtext,"awx"
.globl vlinetallasm4
.type vlinetallasm4,@function
vlinetallasm4:
@ -38,18 +38,18 @@ vlinetallasm4:
subq $8, %rsp # Does the stack need to be 16-byte aligned for Linux?
.cfi_adjust_cfa_offset 8
# rax = bufplce base address
# rbx =
# rcx = offset from rdi/count (negative)
# edx/rdx = scratch
# rdi = bottom of columns to write to
# r8d-r11d = column offsets
# r12-r15 = palookupoffse[0] - palookupoffse[4]
# rax = bufplce base address
# rbx =
# rcx = offset from rdi/count (negative)
# edx/rdx = scratch
# rdi = bottom of columns to write to
# r8d-r11d = column offsets
# r12-r15 = palookupoffse[0] - palookupoffse[4]
movl dc_count(%rip), %ecx
movq dc_dest(%rip), %rdi
testl %ecx, %ecx
jle vltepilog # count must be positive
jle vltepilog # count must be positive
movq bufplce(%rip), %rax
movq bufplce+8(%rip), %r8
@ -60,14 +60,14 @@ vlinetallasm4:
subq %rax, %r10
movl %r8d, source2+4(%rip)
movl %r9d, source3+4(%rip)
movl %r10d, source4+4(%rip)
movl %r10d, source4+4(%rip)
pm: imulq $320, %rcx
pm: imulq $320, %rcx
movq palookupoffse(%rip), %r12
movq palookupoffse+8(%rip), %r13
movq palookupoffse+16(%rip), %r14
movq palookupoffse+24(%rip), %r15
movq palookupoffse+24(%rip), %r15
movl vince(%rip), %r8d
movl vince+4(%rip), %r9d
@ -76,53 +76,53 @@ pm: imulq $320, %rcx
movl %r8d, step1+3(%rip)
movl %r9d, step2+3(%rip)
movl %r10d, step3+3(%rip)
movl %r11d, step4+3(%rip)
movl %r11d, step4+3(%rip)
addq %rcx, %rdi
negq %rcx
negq %rcx
movl vplce(%rip), %r8d
movl vplce+4(%rip), %r9d
movl vplce+8(%rip), %r10d
movl vplce+12(%rip), %r11d
# selfmod loopit, vltepilog
jmp loopit
jmp loopit
.align 16
.align 16
loopit:
movl %r8d, %edx
shifter1: shrl $24, %edx
step1: addl $0x88888888, %r8d
step1: addl $0x44444444, %r8d
movzbl (%rax,%rdx), %edx
movl %r9d, %ebx
movb (%r12,%rdx), %dl
shifter2: shrl $24, %ebx
step2: addl $0x88888888, %r9d
source2: movzbl 0x88888888(%rax,%rbx), %ebx
step2: addl $0x44444444, %r9d
source2: movzbl 0x44444444(%rax,%rbx), %ebx
movl %r10d, %ebp
movb (%r13,%rbx), %bl
shifter3: shr $24, %ebp
step3: addl $0x88888888, %r10d
source3: movzbl 0x88888888(%rax,%rbp), %ebp
step3: addl $0x44444444, %r10d
source3: movzbl 0x44444444(%rax,%rbp), %ebp
movl %r11d, %esi
movb (%r14,%rbp), %bpl
shifter4: shr $24, %esi
step4: add $0x88888888, %r11d
source4: movzbl 0x88888888(%rax,%rsi), %esi
step4: add $0x44444444, %r11d
source4: movzbl 0x44444444(%rax,%rsi), %esi
movb %dl, (%rdi,%rcx)
movb %bl, 1(%rdi,%rcx)
movb (%r15,%rsi), %sil
movb %bpl, 2(%rdi,%rcx)
movb %sil, 3(%rdi,%rcx)
vltpitch: addq $320, %rcx
jl loopit
vltpitch: addq $320, %rcx
jl loopit
movl %r8d, vplce(%rip)
movl %r9d, vplce+4(%rip)
movl %r10d, vplce+8(%rip)
movl %r11d, vplce+12(%rip)
movl %r11d, vplce+12(%rip)
vltepilog:
addq $8, %rsp
.cfi_adjust_cfa_offset -8
@ -137,5 +137,5 @@ vltepilog:
ret
.cfi_endproc
.align 16

View file

@ -11,8 +11,8 @@
#pragma once
#endif
#ifndef _MSC_VER
#define __forceinline inline
#if defined(__GNUC__) && !defined(__forceinline)
#define __forceinline __inline__ __attribute__((always_inline))
#endif
static __forceinline SDWORD Scale (SDWORD a, SDWORD b, SDWORD c)

View file

@ -47,12 +47,6 @@
#include <math.h>
#include <stdlib.h>
struct FBinding
{
const char *Key;
const char *Bind;
};
/* Default keybindings for Doom (and all other games)
*/
static const FBinding DefBindings[] =
@ -178,6 +172,29 @@ static const FBinding DefStrifeBindings[] =
// h - use health
};
static const FBinding DefAutomapBindings[] =
{
{ "f", "am_togglefollow" },
{ "g", "am_togglegrid" },
{ "p", "am_toggletexture" },
{ "m", "am_setmark" },
{ "c", "am_clearmarks" },
{ "0", "am_gobig" },
{ "rightarrow", "+am_panright" },
{ "leftarrow", "+am_panleft" },
{ "uparrow", "+am_panup" },
{ "downarrow", "+am_pandown" },
{ "-", "+am_zoomout" },
{ "=", "+am_zoomin" },
{ "kp-", "+am_zoomout" },
{ "kp+", "+am_zoomin" },
{ "mwheelup", "am_zoom 1.2" },
{ "mwheeldown", "am_zoom -1.2" },
{ NULL }
};
const char *KeyNames[NUM_KEYS] =
{
// This array is dependant on the particular keyboard input
@ -278,11 +295,19 @@ const char *KeyNames[NUM_KEYS] =
"pad_a", "pad_b", "pad_x", "pad_y"
};
static FString Bindings[NUM_KEYS];
static FString DoubleBindings[NUM_KEYS];
FKeyBindings Bindings;
FKeyBindings DoubleBindings;
FKeyBindings AutomapBindings;
static unsigned int DClickTime[NUM_KEYS];
static BYTE DClicked[(NUM_KEYS+7)/8];
//=============================================================================
//
//
//
//=============================================================================
static int GetKeyFromName (const char *name)
{
int i;
@ -302,380 +327,15 @@ static int GetKeyFromName (const char *name)
return 0;
}
static const char *KeyName (int key)
{
static char name[5];
if (KeyNames[key])
return KeyNames[key];
mysnprintf (name, countof(name), "#%d", key);
return name;
}
void C_UnbindAll ()
{
for (int i = 0; i < NUM_KEYS; ++i)
{
Bindings[i] = "";
DoubleBindings[i] = "";
}
}
CCMD (unbindall)
{
C_UnbindAll ();
}
CCMD (unbind)
{
int i;
if (argv.argc() > 1)
{
if ( (i = GetKeyFromName (argv[1])) )
{
Bindings[i] = "";
}
else
{
Printf ("Unknown key \"%s\"\n", argv[1]);
return;
}
}
}
CCMD (bind)
{
int i;
if (argv.argc() > 1)
{
i = GetKeyFromName (argv[1]);
if (!i)
{
Printf ("Unknown key \"%s\"\n", argv[1]);
return;
}
if (argv.argc() == 2)
{
Printf ("\"%s\" = \"%s\"\n", argv[1], Bindings[i].GetChars());
}
else
{
Bindings[i] = argv[2];
}
}
else
{
Printf ("Current key bindings:\n");
for (i = 0; i < NUM_KEYS; i++)
{
if (!Bindings[i].IsEmpty())
Printf ("%s \"%s\"\n", KeyName (i), Bindings[i].GetChars());
}
}
}
//==========================================================================
//=============================================================================
//
// CCMD defaultbind
//
// Binds a command to a key if that key is not already bound and if
// that command is not already bound to another key.
//
//==========================================================================
//=============================================================================
CCMD (defaultbind)
static int GetConfigKeyFromName (const char *key)
{
if (argv.argc() < 3)
{
Printf ("Usage: defaultbind <key> <command>\n");
}
else
{
int key = GetKeyFromName (argv[1]);
if (key == 0)
{
Printf ("Unknown key \"%s\"\n", argv[1]);
return;
}
if (!Bindings[key].IsEmpty())
{ // This key is already bound.
return;
}
for (int i = 0; i < NUM_KEYS; ++i)
{
if (!Bindings[i].IsEmpty() && stricmp (Bindings[i], argv[2]) == 0)
{ // This command is already bound to a key.
return;
}
}
// It is safe to do the bind, so do it.
Bindings[key] = argv[2];
}
}
CCMD (undoublebind)
{
int i;
if (argv.argc() > 1)
{
if ( (i = GetKeyFromName (argv[1])) )
{
DoubleBindings[i] = "";
}
else
{
Printf ("Unknown key \"%s\"\n", argv[1]);
return;
}
}
}
CCMD (doublebind)
{
int i;
if (argv.argc() > 1)
{
i = GetKeyFromName (argv[1]);
if (!i)
{
Printf ("Unknown key \"%s\"\n", argv[1]);
return;
}
if (argv.argc() == 2)
{
Printf ("\"%s\" = \"%s\"\n", argv[1], DoubleBindings[i].GetChars());
}
else
{
DoubleBindings[i] = argv[2];
}
}
else
{
Printf ("Current key doublebindings:\n");
for (i = 0; i < NUM_KEYS; i++)
{
if (!DoubleBindings[i].IsEmpty())
Printf ("%s \"%s\"\n", KeyName (i), DoubleBindings[i].GetChars());
}
}
}
CCMD (rebind)
{
FString *bindings;
if (key == 0)
{
Printf ("Rebind cannot be used from the console\n");
return;
}
if (key & KEY_DBLCLICKED)
{
bindings = DoubleBindings;
key &= KEY_DBLCLICKED-1;
}
else
{
bindings = Bindings;
}
if (argv.argc() > 1)
{
bindings[key] = argv[1];
}
}
static void SetBinds (const FBinding *array)
{
while (array->Key)
{
C_DoBind (array->Key, array->Bind, false);
array++;
}
}
void C_BindDefaults ()
{
SetBinds (DefBindings);
if (gameinfo.gametype & (GAME_Raven|GAME_Strife))
{
SetBinds (DefRavenBindings);
}
if (gameinfo.gametype == GAME_Heretic)
{
SetBinds (DefHereticBindings);
}
if (gameinfo.gametype == GAME_Hexen)
{
SetBinds (DefHexenBindings);
}
if (gameinfo.gametype == GAME_Strife)
{
SetBinds (DefStrifeBindings);
}
}
CCMD(binddefaults)
{
C_BindDefaults ();
}
void C_SetDefaultBindings ()
{
C_UnbindAll ();
C_BindDefaults ();
}
bool C_DoKey (event_t *ev)
{
FString binding;
bool dclick;
int dclickspot;
BYTE dclickmask;
if (ev->type != EV_KeyDown && ev->type != EV_KeyUp)
return false;
if ((unsigned int)ev->data1 >= NUM_KEYS)
return false;
dclickspot = ev->data1 >> 3;
dclickmask = 1 << (ev->data1 & 7);
dclick = false;
// This used level.time which didn't work outside a level.
if (DClickTime[ev->data1] > I_MSTime() && ev->type == EV_KeyDown)
{
// Key pressed for a double click
binding = DoubleBindings[ev->data1];
DClicked[dclickspot] |= dclickmask;
dclick = true;
}
else
{
if (ev->type == EV_KeyDown)
{ // Key pressed for a normal press
binding = Bindings[ev->data1];
DClickTime[ev->data1] = I_MSTime() + 571;
}
else if (DClicked[dclickspot] & dclickmask)
{ // Key released from a double click
binding = DoubleBindings[ev->data1];
DClicked[dclickspot] &= ~dclickmask;
DClickTime[ev->data1] = 0;
dclick = true;
}
else
{ // Key released from a normal press
binding = Bindings[ev->data1];
}
}
if (binding.IsEmpty())
{
binding = Bindings[ev->data1];
dclick = false;
}
if (!binding.IsEmpty() && (chatmodeon == 0 || ev->data1 < 256))
{
if (ev->type == EV_KeyUp && binding[0] != '+')
{
return false;
}
char *copy = binding.LockBuffer();
if (ev->type == EV_KeyUp)
{
copy[0] = '-';
}
AddCommandString (copy, dclick ? ev->data1 | KEY_DBLCLICKED : ev->data1);
return true;
}
return false;
}
const char *C_ConfigKeyName(int keynum)
{
const char *name = KeyName(keynum);
if (name[1] == 0) // Make sure given name is config-safe
{
if (name[0] == '[')
return "LeftBracket";
else if (name[0] == ']')
return "RightBracket";
else if (name[0] == '=')
return "Equals";
else if (strcmp (name, "kp=") == 0)
return "KP-Equals";
}
return name;
}
// This function is first called for functions in custom key sections.
// In this case, matchcmd is non-NULL, and only keys bound to that command
// are stored. If a match is found, its binding is set to "\1".
// After all custom key sections are saved, it is called one more for the
// normal Bindings and DoubleBindings sections for this game. In this case
// matchcmd is NULL and all keys will be stored. The config section was not
// previously cleared, so all old bindings are still in place. If the binding
// for a key is empty, the corresponding key in the config is removed as well.
// If a binding is "\1", then the binding itself is cleared, but nothing
// happens to the entry in the config.
void C_ArchiveBindings (FConfigFile *f, bool dodouble, const char *matchcmd)
{
FString *bindings;
int i;
bindings = dodouble ? DoubleBindings : Bindings;
for (i = 0; i < NUM_KEYS; i++)
{
if (bindings[i].IsEmpty())
{
if (matchcmd == NULL)
{
f->ClearKey(C_ConfigKeyName(i));
}
}
else if (matchcmd == NULL || stricmp(bindings[i], matchcmd) == 0)
{
if (bindings[i][0] == '\1')
{
bindings[i] = "";
continue;
}
f->SetValueForKey(C_ConfigKeyName(i), bindings[i]);
if (matchcmd != NULL)
{ // If saving a specific command, set a marker so that
// it does not get saved in the general binding list.
bindings[i] = "\1";
}
}
}
}
void C_DoBind (const char *key, const char *bind, bool dodouble)
{
int keynum = GetKeyFromName (key);
int keynum = GetKeyFromName(key);
if (keynum == 0)
{
if (stricmp (key, "LeftBracket") == 0)
@ -695,32 +355,55 @@ void C_DoBind (const char *key, const char *bind, bool dodouble)
keynum = GetKeyFromName ("kp=");
}
}
if (keynum != 0)
{
(dodouble ? DoubleBindings : Bindings)[keynum] = bind;
}
return keynum;
}
int C_GetKeysForCommand (char *cmd, int *first, int *second)
//=============================================================================
//
//
//
//=============================================================================
static const char *KeyName (int key)
{
int c, i;
static char name[5];
*first = *second = c = i = 0;
if (KeyNames[key])
return KeyNames[key];
while (i < NUM_KEYS && c < 2)
{
if (stricmp (cmd, Bindings[i]) == 0)
{
if (c++ == 0)
*first = i;
else
*second = i;
}
i++;
}
return c;
mysnprintf (name, countof(name), "#%d", key);
return name;
}
//=============================================================================
//
//
//
//=============================================================================
static const char *ConfigKeyName(int keynum)
{
const char *name = KeyName(keynum);
if (name[1] == 0) // Make sure given name is config-safe
{
if (name[0] == '[')
return "LeftBracket";
else if (name[0] == ']')
return "RightBracket";
else if (name[0] == '=')
return "Equals";
else if (strcmp (name, "kp=") == 0)
return "KP-Equals";
}
return name;
}
//=============================================================================
//
//
//
//=============================================================================
void C_NameKeys (char *str, int first, int second)
{
int c = 0;
@ -744,28 +427,471 @@ void C_NameKeys (char *str, int first, int second)
*str = '\0';
}
void C_UnbindACommand (char *str)
//=============================================================================
//
//
//
//=============================================================================
void FKeyBindings::DoBind (const char *key, const char *bind)
{
int keynum = GetConfigKeyFromName (key);
if (keynum != 0)
{
Binds[keynum] = bind;
}
}
//=============================================================================
//
//
//
//=============================================================================
void FKeyBindings::SetBinds(const FBinding *binds)
{
while (binds->Key)
{
DoBind (binds->Key, binds->Bind);
binds++;
}
}
//=============================================================================
//
//
//
//=============================================================================
void FKeyBindings::UnbindAll ()
{
for (int i = 0; i < NUM_KEYS; ++i)
{
Binds[i] = "";
}
}
//=============================================================================
//
//
//
//=============================================================================
void FKeyBindings::UnbindKey(const char *key)
{
int i;
if ( (i = GetKeyFromName (key)) )
{
Binds[i] = "";
}
else
{
Printf ("Unknown key \"%s\"\n", key);
return;
}
}
//=============================================================================
//
//
//
//=============================================================================
void FKeyBindings::PerformBind(FCommandLine &argv, const char *msg)
{
int i;
if (argv.argc() > 1)
{
i = GetKeyFromName (argv[1]);
if (!i)
{
Printf ("Unknown key \"%s\"\n", argv[1]);
return;
}
if (argv.argc() == 2)
{
Printf ("\"%s\" = \"%s\"\n", argv[1], Binds[i].GetChars());
}
else
{
Binds[i] = argv[2];
}
}
else
{
Printf ("%s:\n", msg);
for (i = 0; i < NUM_KEYS; i++)
{
if (!Binds[i].IsEmpty())
Printf ("%s \"%s\"\n", KeyName (i), Binds[i].GetChars());
}
}
}
//=============================================================================
//
// This function is first called for functions in custom key sections.
// In this case, matchcmd is non-NULL, and only keys bound to that command
// are stored. If a match is found, its binding is set to "\1".
// After all custom key sections are saved, it is called one more for the
// normal Bindings and DoubleBindings sections for this game. In this case
// matchcmd is NULL and all keys will be stored. The config section was not
// previously cleared, so all old bindings are still in place. If the binding
// for a key is empty, the corresponding key in the config is removed as well.
// If a binding is "\1", then the binding itself is cleared, but nothing
// happens to the entry in the config.
//
//=============================================================================
void FKeyBindings::ArchiveBindings(FConfigFile *f, const char *matchcmd)
{
int i;
for (i = 0; i < NUM_KEYS; i++)
{
if (!stricmp (str, Bindings[i]))
if (Binds[i].IsEmpty())
{
Bindings[i] = "";
if (matchcmd == NULL)
{
f->ClearKey(ConfigKeyName(i));
}
}
else if (matchcmd == NULL || stricmp(Binds[i], matchcmd) == 0)
{
if (Binds[i][0] == '\1')
{
Binds[i] = "";
continue;
}
f->SetValueForKey(ConfigKeyName(i), Binds[i]);
if (matchcmd != NULL)
{ // If saving a specific command, set a marker so that
// it does not get saved in the general binding list.
Binds[i] = "\1";
}
}
}
}
void C_ChangeBinding (const char *str, int newone)
//=============================================================================
//
//
//
//=============================================================================
int FKeyBindings::GetKeysForCommand (const char *cmd, int *first, int *second)
{
if ((unsigned int)newone < NUM_KEYS)
int c, i;
*first = *second = c = i = 0;
while (i < NUM_KEYS && c < 2)
{
Bindings[newone] = str;
if (stricmp (cmd, Binds[i]) == 0)
{
if (c++ == 0)
*first = i;
else
*second = i;
}
i++;
}
return c;
}
//=============================================================================
//
//
//
//=============================================================================
void FKeyBindings::UnbindACommand (const char *str)
{
int i;
for (i = 0; i < NUM_KEYS; i++)
{
if (!stricmp (str, Binds[i]))
{
Binds[i] = "";
}
}
}
const char *C_GetBinding (int key)
//=============================================================================
//
//
//
//=============================================================================
void FKeyBindings::DefaultBind(const char *keyname, const char *cmd)
{
return (unsigned int)key < NUM_KEYS ? Bindings[key].GetChars() : NULL;
int key = GetKeyFromName (keyname);
if (key == 0)
{
Printf ("Unknown key \"%s\"\n", keyname);
return;
}
if (!Binds[key].IsEmpty())
{ // This key is already bound.
return;
}
for (int i = 0; i < NUM_KEYS; ++i)
{
if (!Binds[i].IsEmpty() && stricmp (Binds[i], cmd) == 0)
{ // This command is already bound to a key.
return;
}
}
// It is safe to do the bind, so do it.
Binds[key] = cmd;
}
//=============================================================================
//
//
//
//=============================================================================
void C_UnbindAll ()
{
Bindings.UnbindAll();
DoubleBindings.UnbindAll();
AutomapBindings.UnbindAll();
}
CCMD (unbindall)
{
C_UnbindAll ();
}
//=============================================================================
//
//
//
//=============================================================================
CCMD (unbind)
{
if (argv.argc() > 1)
{
Bindings.UnbindKey(argv[1]);
}
}
CCMD (undoublebind)
{
if (argv.argc() > 1)
{
DoubleBindings.UnbindKey(argv[1]);
}
}
CCMD (unmapbind)
{
if (argv.argc() > 1)
{
AutomapBindings.UnbindKey(argv[1]);
}
}
//=============================================================================
//
//
//
//=============================================================================
CCMD (bind)
{
Bindings.PerformBind(argv, "Current key bindings");
}
CCMD (doublebind)
{
DoubleBindings.PerformBind(argv, "Current key doublebindings");
}
CCMD (mapbind)
{
AutomapBindings.PerformBind(argv, "Current automap key bindings");
}
//==========================================================================
//
// CCMD defaultbind
//
// Binds a command to a key if that key is not already bound and if
// that command is not already bound to another key.
//
//==========================================================================
CCMD (defaultbind)
{
if (argv.argc() < 3)
{
Printf ("Usage: defaultbind <key> <command>\n");
}
else
{
Bindings.DefaultBind(argv[1], argv[2]);
}
}
//=============================================================================
//
//
//
//=============================================================================
CCMD (rebind)
{
FKeyBindings *bindings;
if (key == 0)
{
Printf ("Rebind cannot be used from the console\n");
return;
}
if (key & KEY_DBLCLICKED)
{
bindings = &DoubleBindings;
key &= KEY_DBLCLICKED-1;
}
else
{
bindings = &Bindings;
}
if (argv.argc() > 1)
{
bindings->SetBind(key, argv[1]);
}
}
//=============================================================================
//
//
//
//=============================================================================
void C_BindDefaults ()
{
Bindings.SetBinds (DefBindings);
if (gameinfo.gametype & (GAME_Raven|GAME_Strife))
{
Bindings.SetBinds (DefRavenBindings);
}
if (gameinfo.gametype == GAME_Heretic)
{
Bindings.SetBinds (DefHereticBindings);
}
if (gameinfo.gametype == GAME_Hexen)
{
Bindings.SetBinds (DefHexenBindings);
}
if (gameinfo.gametype == GAME_Strife)
{
Bindings.SetBinds (DefStrifeBindings);
}
AutomapBindings.SetBinds(DefAutomapBindings);
}
CCMD(binddefaults)
{
C_BindDefaults ();
}
void C_SetDefaultBindings ()
{
C_UnbindAll ();
C_BindDefaults ();
}
//=============================================================================
//
//
//
//=============================================================================
bool C_DoKey (event_t *ev, FKeyBindings *binds, FKeyBindings *doublebinds)
{
FString binding;
bool dclick;
int dclickspot;
BYTE dclickmask;
if (ev->type != EV_KeyDown && ev->type != EV_KeyUp)
return false;
if ((unsigned int)ev->data1 >= NUM_KEYS)
return false;
dclickspot = ev->data1 >> 3;
dclickmask = 1 << (ev->data1 & 7);
dclick = false;
// This used level.time which didn't work outside a level.
if (DClickTime[ev->data1] > I_MSTime() && ev->type == EV_KeyDown)
{
// Key pressed for a double click
if (doublebinds != NULL) binding = doublebinds->GetBinding(ev->data1);
DClicked[dclickspot] |= dclickmask;
dclick = true;
}
else
{
if (ev->type == EV_KeyDown)
{ // Key pressed for a normal press
binding = binds->GetBinding(ev->data1);
DClickTime[ev->data1] = I_MSTime() + 571;
}
else if (DClicked[dclickspot] & dclickmask)
{ // Key released from a double click
if (doublebinds != NULL) binding = doublebinds->GetBinding(ev->data1);
DClicked[dclickspot] &= ~dclickmask;
DClickTime[ev->data1] = 0;
dclick = true;
}
else
{ // Key released from a normal press
binding = binds->GetBinding(ev->data1);
}
}
if (binding.IsEmpty())
{
binding = binds->GetBinding(ev->data1);
dclick = false;
}
if (!binding.IsEmpty() && (chatmodeon == 0 || ev->data1 < 256))
{
if (ev->type == EV_KeyUp && binding[0] != '+')
{
return false;
}
char *copy = binding.LockBuffer();
if (ev->type == EV_KeyUp)
{
copy[0] = '-';
}
AddCommandString (copy, dclick ? ev->data1 | KEY_DBLCLICKED : ev->data1);
return true;
}
return false;
}

View file

@ -34,25 +34,80 @@
#ifndef __C_BINDINGS_H__
#define __C_BINDINGS_H__
#include "doomdef.h"
struct event_t;
class FConfigFile;
class FCommandLine;
bool C_DoKey (event_t *ev);
void C_ArchiveBindings (FConfigFile *f, bool dodouble, const char *matchcmd=NULL);
void C_NameKeys (char *str, int first, int second);
struct FBinding
{
const char *Key;
const char *Bind;
};
class FKeyBindings
{
FString Binds[NUM_KEYS];
public:
void PerformBind(FCommandLine &argv, const char *msg);
void SetBinds(const FBinding *binds);
bool DoKey(event_t *ev);
void ArchiveBindings(FConfigFile *F, const char *matchcmd = NULL);
int GetKeysForCommand (const char *cmd, int *first, int *second);
void UnbindACommand (const char *str);
void UnbindAll ();
void UnbindKey(const char *key);
void DoBind (const char *key, const char *bind);
void DefaultBind(const char *keyname, const char *cmd);
void SetBind(unsigned int key, const char *bind)
{
if (key < NUM_KEYS) Binds[key] = bind;
}
const FString &GetBinding(unsigned int index) const
{
return Binds[index];
}
const char *GetBind(unsigned int index) const
{
if (index < NUM_KEYS) return Binds[index];
else return NULL;
}
};
extern FKeyBindings Bindings;
extern FKeyBindings DoubleBindings;
extern FKeyBindings AutomapBindings;
extern FKeyBindings MenuBindings;
bool C_DoKey (event_t *ev, FKeyBindings *binds, FKeyBindings *doublebinds);
// Stuff used by the customize controls menu
int C_GetKeysForCommand (char *cmd, int *first, int *second);
void C_NameKeys (char *str, int first, int second);
void C_UnbindACommand (char *str);
void C_ChangeBinding (const char *str, int newone);
void C_DoBind (const char *key, const char *bind, bool doublebind);
void C_SetDefaultBindings ();
void C_UnbindAll ();
// Returns string bound to given key (NULL if none)
const char *C_GetBinding (int key);
extern const char *KeyNames[];
struct FKeyAction
{
FString mTitle;
FString mAction;
};
struct FKeySection
{
FString mTitle;
FString mSection;
TArray<FKeyAction> mActions;
};
extern TArray<FKeySection> KeySections;
#endif //__C_BINDINGS_H__

View file

@ -936,3 +936,4 @@ CCMD(currentpos)
FIXED2FLOAT(mo->x), FIXED2FLOAT(mo->y), FIXED2FLOAT(mo->z), mo->angle/float(ANGLE_1), FIXED2FLOAT(mo->floorz), mo->Sector->sectornum, mo->Sector->lightlevel);
}

View file

@ -1119,7 +1119,7 @@ void FFlagCVar::DoSet (UCVarValue value, ECVarType type)
// exec scripts because all flags will base their changes off of the value of
// the "master" cvar at the time the script was run, overriding any changes
// another flag might have made to the same cvar earlier in the script.
if ((ValueVar.Flags & CVAR_SERVERINFO) && gamestate != GS_STARTUP && !demoplayback)
if ((ValueVar.GetFlags() & CVAR_SERVERINFO) && gamestate != GS_STARTUP && !demoplayback)
{
if (netgame && !players[consoleplayer].settings_controller)
{
@ -1139,6 +1139,114 @@ void FFlagCVar::DoSet (UCVarValue value, ECVarType type)
}
}
//
// Mask cvar implementation
//
// Similar to FFlagCVar but can have multiple bits
//
FMaskCVar::FMaskCVar (const char *name, FIntCVar &realvar, DWORD bitval)
: FBaseCVar (name, 0, NULL),
ValueVar (realvar),
BitVal (bitval)
{
int bit;
Flags &= ~CVAR_ISDEFAULT;
assert (bitval != 0);
bit = 0;
while ((bitval & 1) == 0)
{
++bit;
bitval >>= 1;
}
BitNum = bit;
}
ECVarType FMaskCVar::GetRealType () const
{
return CVAR_Dummy;
}
UCVarValue FMaskCVar::GetGenericRep (ECVarType type) const
{
return FromInt ((ValueVar & BitVal) >> BitNum, type);
}
UCVarValue FMaskCVar::GetFavoriteRep (ECVarType *type) const
{
UCVarValue ret;
*type = CVAR_Int;
ret.Int = (ValueVar & BitVal) >> BitNum;
return ret;
}
UCVarValue FMaskCVar::GetGenericRepDefault (ECVarType type) const
{
ECVarType dummy;
UCVarValue def;
def = ValueVar.GetFavoriteRepDefault (&dummy);
return FromInt ((def.Int & BitVal) >> BitNum, type);
}
UCVarValue FMaskCVar::GetFavoriteRepDefault (ECVarType *type) const
{
ECVarType dummy;
UCVarValue def;
def = ValueVar.GetFavoriteRepDefault (&dummy);
def.Int = (def.Int & BitVal) >> BitNum;
*type = CVAR_Int;
return def;
}
void FMaskCVar::SetGenericRepDefault (UCVarValue value, ECVarType type)
{
int val = ToInt(value, type) << BitNum;
ECVarType dummy;
UCVarValue def;
def = ValueVar.GetFavoriteRepDefault (&dummy);
def.Int &= ~BitVal;
def.Int |= val;
ValueVar.SetGenericRepDefault (def, CVAR_Int);
}
void FMaskCVar::DoSet (UCVarValue value, ECVarType type)
{
int val = ToInt(value, type) << BitNum;
// Server cvars that get changed by this need to use a special message, because
// changes are not processed until the next net update. This is a problem with
// exec scripts because all flags will base their changes off of the value of
// the "master" cvar at the time the script was run, overriding any changes
// another flag might have made to the same cvar earlier in the script.
if ((ValueVar.GetFlags() & CVAR_SERVERINFO) && gamestate != GS_STARTUP && !demoplayback)
{
if (netgame && !players[consoleplayer].settings_controller)
{
Printf ("Only setting controllers can change %s\n", Name);
return;
}
// Ugh...
for(int i = 0; i < 32; i++)
{
if (BitVal & (1<<i))
{
D_SendServerFlagChange (&ValueVar, i, !!(val & (1<<i)));
}
}
}
else
{
int vval = *ValueVar;
vval &= ~BitVal;
vval |= val;
ValueVar = vval;
}
}
////////////////////////////////////////////////////////////////////////
static int STACK_ARGS sortcvars (const void *a, const void *b)
{

View file

@ -344,6 +344,30 @@ protected:
int BitNum;
};
class FMaskCVar : public FBaseCVar
{
public:
FMaskCVar (const char *name, FIntCVar &realvar, uint32 bitval);
virtual ECVarType GetRealType () const;
virtual UCVarValue GetGenericRep (ECVarType type) const;
virtual UCVarValue GetFavoriteRep (ECVarType *type) const;
virtual UCVarValue GetGenericRepDefault (ECVarType type) const;
virtual UCVarValue GetFavoriteRepDefault (ECVarType *type) const;
virtual void SetGenericRepDefault (UCVarValue value, ECVarType type);
inline operator int () const { return (ValueVar & BitVal) >> BitNum; }
inline int operator *() const { return (ValueVar & BitVal) >> BitNum; }
protected:
virtual void DoSet (UCVarValue value, ECVarType type);
FIntCVar &ValueVar;
uint32 BitVal;
int BitNum;
};
class FGUIDCVar : public FBaseCVar
{
public:

View file

@ -119,7 +119,9 @@ FButtonStatus Button_Mlook, Button_Klook, Button_Use, Button_AltAttack,
Button_Forward, Button_Right, Button_Left, Button_MoveDown,
Button_MoveUp, Button_Jump, Button_ShowScores, Button_Crouch,
Button_Zoom, Button_Reload,
Button_User1, Button_User2, Button_User3, Button_User4;
Button_User1, Button_User2, Button_User3, Button_User4,
Button_AM_PanLeft, Button_AM_PanRight, Button_AM_PanDown, Button_AM_PanUp,
Button_AM_ZoomIn, Button_AM_ZoomOut;
bool ParsingKeyConf;
@ -131,13 +133,16 @@ bool ParsingKeyConf;
FActionMap ActionMaps[] =
{
{ 0x0d52d67b, &Button_AM_PanLeft, "am_panleft"},
{ 0x125f5226, &Button_User2, "user2" },
{ 0x1eefa611, &Button_Jump, "jump" },
{ 0x201f1c55, &Button_Right, "right" },
{ 0x20ccc4d5, &Button_Zoom, "zoom" },
{ 0x23a99cd7, &Button_Back, "back" },
{ 0x41df90c2, &Button_AM_ZoomIn, "am_zoomin"},
{ 0x426b69e7, &Button_Reload, "reload" },
{ 0x4463f43a, &Button_LookDown, "lookdown" },
{ 0x51f7a334, &Button_AM_ZoomOut, "am_zoomout"},
{ 0x534c30ee, &Button_User4, "user4" },
{ 0x5622bf42, &Button_Attack, "attack" },
{ 0x577712d0, &Button_User1, "user1" },
@ -147,12 +152,15 @@ FActionMap ActionMaps[] =
{ 0x676885b8, &Button_AltAttack, "altattack" },
{ 0x6fa41b84, &Button_MoveLeft, "moveleft" },
{ 0x818f08e6, &Button_MoveRight, "moveright" },
{ 0x8197097b, &Button_AM_PanRight, "am_panright"},
{ 0x8d89955e, &Button_AM_PanUp, "am_panup"} ,
{ 0xa2b62d8b, &Button_Mlook, "mlook" },
{ 0xab2c3e71, &Button_Crouch, "crouch" },
{ 0xb000b483, &Button_Left, "left" },
{ 0xb62b1e49, &Button_LookUp, "lookup" },
{ 0xb6f8fe92, &Button_User3, "user3" },
{ 0xb7e6a54b, &Button_Strafe, "strafe" },
{ 0xce301c81, &Button_AM_PanDown, "am_pandown"},
{ 0xd5897c73, &Button_ShowScores, "showscores" },
{ 0xe0ccb317, &Button_Speed, "speed" },
{ 0xe0cfc260, &Button_Use, "use" },
@ -160,6 +168,7 @@ FActionMap ActionMaps[] =
};
#define NUM_ACTIONS countof(ActionMaps)
// PRIVATE DATA DEFINITIONS ------------------------------------------------
static const char *KeyConfCommands[] =

View file

@ -146,6 +146,7 @@ struct FButtonStatus
bool PressKey (int keynum); // Returns true if this key caused the button to be pressed.
bool ReleaseKey (int keynum); // Returns true if this key is no longer pressed.
void ResetTriggers () { bWentDown = bWentUp = false; }
void Reset () { bDown = bWentDown = bWentUp = false; }
};
extern FButtonStatus Button_Mlook, Button_Klook, Button_Use, Button_AltAttack,
@ -154,7 +155,9 @@ extern FButtonStatus Button_Mlook, Button_Klook, Button_Use, Button_AltAttack,
Button_Forward, Button_Right, Button_Left, Button_MoveDown,
Button_MoveUp, Button_Jump, Button_ShowScores, Button_Crouch,
Button_Zoom, Button_Reload,
Button_User1, Button_User2, Button_User3, Button_User4;
Button_User1, Button_User2, Button_User3, Button_User4,
Button_AM_PanLeft, Button_AM_PanRight, Button_AM_PanDown, Button_AM_PanUp,
Button_AM_ZoomIn, Button_AM_ZoomOut;
extern bool ParsingKeyConf;
void ResetButtonTriggers (); // Call ResetTriggers for all buttons

View file

@ -2,10 +2,14 @@
#ifdef _WIN32
#include <direct.h>
#include <io.h>
#else
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#if !defined(__sun)
#include <fts.h>
#endif
#endif
#include "doomtype.h"
#include "cmdlib.h"
@ -90,6 +94,23 @@ char *copystring (const char *s)
return b;
}
//============================================================================
//
// ncopystring
//
// If the string has no content, returns NULL. Otherwise, returns a copy.
//
//============================================================================
char *ncopystring (const char *string)
{
if (string == NULL || string[0] == 0)
{
return NULL;
}
return copystring (string);
}
//==========================================================================
//
// ReplaceString
@ -868,3 +889,153 @@ FString NicePath(const char *path)
return where;
#endif
}
#ifdef _WIN32
//==========================================================================
//
// ScanDirectory
//
//==========================================================================
void ScanDirectory(TArray<FFileList> &list, const char *dirpath)
{
struct _finddata_t fileinfo;
intptr_t handle;
FString dirmatch;
dirmatch << dirpath << "*";
if ((handle = _findfirst(dirmatch, &fileinfo)) == -1)
{
I_Error("Could not scan '%s': %s\n", dirpath, strerror(errno));
}
else
{
do
{
if (fileinfo.attrib & _A_HIDDEN)
{
// Skip hidden files and directories. (Prevents SVN bookkeeping
// info from being included.)
continue;
}
if (fileinfo.attrib & _A_SUBDIR)
{
if (fileinfo.name[0] == '.' &&
(fileinfo.name[1] == '\0' ||
(fileinfo.name[1] == '.' && fileinfo.name[2] == '\0')))
{
// Do not record . and .. directories.
continue;
}
FFileList *fl = &list[list.Reserve(1)];
fl->Filename << dirpath << fileinfo.name;
fl->isDirectory = true;
FString newdir = fl->Filename;
newdir << "/";
ScanDirectory(list, newdir);
}
else
{
FFileList *fl = &list[list.Reserve(1)];
fl->Filename << dirpath << fileinfo.name;
fl->isDirectory = false;
}
}
while (_findnext(handle, &fileinfo) == 0);
_findclose(handle);
}
}
#elif defined(__sun) || defined(linux)
//==========================================================================
//
// ScanDirectory
// Solaris version
//
// Given NULL-terminated array of directory paths, create trees for them.
//
//==========================================================================
void ScanDirectory(TArray<FFileList> &list, const char *dirpath)
{
DIR *directory = opendir(dirpath);
if(directory == NULL)
return;
struct dirent *file;
while((file = readdir(directory)) != NULL)
{
if(file->d_name[0] == '.') //File is hidden or ./.. directory so ignore it.
continue;
FFileList *fl = &list[list.Reserve(1)];
fl->Filename << dirpath << file->d_name;
struct stat fileStat;
stat(fl->Filename, &fileStat);
fl->isDirectory = S_ISDIR(fileStat.st_mode);
if(fl->isDirectory)
{
FString newdir = fl->Filename;
newdir += "/";
ScanDirectory(list, newdir);
continue;
}
}
closedir(directory);
}
#else
//==========================================================================
//
// ScanDirectory
// 4.4BSD version
//
//==========================================================================
void ScanDirectory(TArray<FFileList> &list, const char *dirpath)
{
const char **argv[] = {dirpath, NULL };
FTS *fts;
FTSENT *ent;
fts = fts_open(argv, FTS_LOGICAL, NULL);
if (fts == NULL)
{
I_Error("Failed to start directory traversal: %s\n", strerror(errno));
return;
}
while ((ent = fts_read(fts)) != NULL)
{
if (ent->fts_info == FTS_D && ent->fts_name[0] == '.')
{
// Skip hidden directories. (Prevents SVN bookkeeping
// info from being included.)
fts_set(fts, ent, FTS_SKIP);
}
if (ent->fts_info == FTS_D && ent->fts_level == 0)
{
FFileList *fl = &list[list.Reserve(1)];
fl->Filename = ent->fts_path;
fl->isDirectory = true;
}
if (ent->fts_info == FTS_F)
{
// We're only interested in remembering files.
FFileList *fl = &list[list.Reserve(1)];
fl->Filename = ent->fts_path;
fl->isDirectory = false;
}
}
fts_close(fts);
}
#endif

View file

@ -36,6 +36,7 @@ int ParseNum (const char *str);
bool IsNum (const char *str); // [RH] added
char *copystring(const char *s);
char *ncopystring(const char *s);
void ReplaceString (char **ptr, const char *str);
bool CheckWildcards (const char *pattern, const char *text);
@ -53,4 +54,12 @@ void CreatePath(const char * fn);
FString ExpandEnvVars(const char *searchpathstring);
FString NicePath(const char *path);
struct FFileList
{
FString Filename;
bool isDirectory;
};
void ScanDirectory(TArray<FFileList> &list, const char *dirpath);
#endif

View file

@ -118,6 +118,7 @@ static FCompatOption Options[] =
{ "spritesort", COMPATF_SPRITESORT, 0 },
{ "hitscan", COMPATF_HITSCAN, 0 },
{ "lightlevel", COMPATF_LIGHT, 0 },
{ "polyobj", COMPATF_POLYOBJ, 0 },
{ NULL, 0, 0 }
};
@ -185,7 +186,7 @@ void ParseCompatibility()
} while (!sc.Compare("{"));
flags.CompatFlags = 0;
flags.BCompatFlags = 0;
flags.ExtCommandIndex = -1;
flags.ExtCommandIndex = ~0u;
while (sc.GetString())
{
if ((i = sc.MatchString(&Options[0].Name, sizeof(*Options))) >= 0)
@ -195,7 +196,7 @@ void ParseCompatibility()
}
else if (sc.Compare("clearlineflags"))
{
if (flags.ExtCommandIndex == -1) flags.ExtCommandIndex = CompatParams.Size();
if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
CompatParams.Push(CP_CLEARFLAGS);
sc.MustGetNumber();
CompatParams.Push(sc.Number);
@ -204,7 +205,7 @@ void ParseCompatibility()
}
else if (sc.Compare("setlineflags"))
{
if (flags.ExtCommandIndex == -1) flags.ExtCommandIndex = CompatParams.Size();
if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
CompatParams.Push(CP_SETFLAGS);
sc.MustGetNumber();
CompatParams.Push(sc.Number);
@ -213,7 +214,7 @@ void ParseCompatibility()
}
else if (sc.Compare("setlinespecial"))
{
if (flags.ExtCommandIndex == -1) flags.ExtCommandIndex = CompatParams.Size();
if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
CompatParams.Push(CP_SETSPECIAL);
sc.MustGetNumber();
CompatParams.Push(sc.Number);
@ -232,7 +233,7 @@ void ParseCompatibility()
break;
}
}
if (flags.ExtCommandIndex != -1)
if (flags.ExtCommandIndex != ~0u)
{
CompatParams.Push(CP_END);
}
@ -266,6 +267,19 @@ void CheckCompatibility(MapData *map)
ib_compatflags = 0;
ii_compatparams = -1;
}
else if (Wads.GetLumpFile(map->lumpnum) == 1 && (gameinfo.flags & GI_COMPATPOLY1) && Wads.CheckLumpName(map->lumpnum, "MAP36"))
{
ii_compatflags = COMPATF_POLYOBJ;
ib_compatflags = 0;
ii_compatparams = -1;
}
else if (Wads.GetLumpFile(map->lumpnum) == 2 && (gameinfo.flags & GI_COMPATPOLY2) && Wads.CheckLumpName(map->lumpnum, "MAP47"))
{
ii_compatflags = COMPATF_POLYOBJ;
ib_compatflags = 0;
ii_compatparams = -1;
}
else
{
map->GetChecksum(md5.Bytes);

View file

@ -952,7 +952,7 @@ static int PatchThing (int thingy)
// compatibility, the upper bits are freed, but we have conflicts between the ZDoom bits
// and the MBF bits. The only such flag exposed to DEHSUPP, though, is STEALTH -- the others
// are not available through mnemonics, and aren't available either through their bit value.
// So if we find the STEALTH keyword, it's a ZDoom mod, otherwise assume assume FRIEND.
// So if we find the STEALTH keyword, it's a ZDoom mod, otherwise assume FRIEND.
bool zdoomflags = false;
char *strval;
@ -2109,13 +2109,13 @@ static int PatchText (int oldSize)
// This must be done because the map is scanned using a binary search.
while (i > 0 && strncmp (DehSpriteMappings[i-1].Sprite, newStr, 4) > 0)
{
swap (DehSpriteMappings[i-1], DehSpriteMappings[i]);
swapvalues (DehSpriteMappings[i-1], DehSpriteMappings[i]);
--i;
}
while ((size_t)i < countof(DehSpriteMappings)-1 &&
strncmp (DehSpriteMappings[i+1].Sprite, newStr, 4) < 0)
{
swap (DehSpriteMappings[i+1], DehSpriteMappings[i]);
swapvalues (DehSpriteMappings[i+1], DehSpriteMappings[i]);
++i;
}
break;

View file

@ -43,27 +43,34 @@ enum EGUIEvent
EV_GUI_KeyRepeat, // same
EV_GUI_KeyUp, // same
EV_GUI_Char, // data1: translated character (for user text input), data2: alt down?
EV_GUI_MouseMove,
EV_GUI_LButtonDown,
EV_GUI_LButtonUp,
EV_GUI_LButtonDblClick,
EV_GUI_MButtonDown,
EV_GUI_MButtonUp,
EV_GUI_MButtonDblClick,
EV_GUI_RButtonDown,
EV_GUI_RButtonUp,
EV_GUI_RButtonDblClick,
EV_GUI_WheelUp, // data3: shift/ctrl/alt
EV_GUI_WheelDown, // "
EV_GUI_WheelRight, // "
EV_GUI_WheelLeft, // "
EV_GUI_FirstMouseEvent,
EV_GUI_MouseMove,
EV_GUI_LButtonDown,
EV_GUI_LButtonUp,
EV_GUI_LButtonDblClick,
EV_GUI_MButtonDown,
EV_GUI_MButtonUp,
EV_GUI_MButtonDblClick,
EV_GUI_RButtonDown,
EV_GUI_RButtonUp,
EV_GUI_RButtonDblClick,
EV_GUI_WheelUp, // data3: shift/ctrl/alt
EV_GUI_WheelDown, // "
EV_GUI_WheelRight, // "
EV_GUI_WheelLeft, // "
EV_GUI_BackButtonDown,
EV_GUI_BackButtonUp,
EV_GUI_FwdButtonDown,
EV_GUI_FwdButtonUp,
EV_GUI_LastMouseEvent,
};
enum GUIKeyModifiers
{
GKM_SHIFT = 1,
GKM_CTRL = 2,
GKM_ALT = 4
GKM_ALT = 4,
GKM_LBUTTON = 8
};
// Special codes for some GUI keys, including a few real ASCII codes.
@ -100,7 +107,7 @@ enum ESpecialGUIKeys
GK_ESCAPE = 27, // ASCII
GK_FREE1 = 28,
GK_FREE2 = 29,
GK_FREE3 = 30,
GK_BACK = 30, // browser back key
GK_CESCAPE = 31 // color escape
};

View file

@ -60,8 +60,8 @@ const IWADInfo IWADInfos[NUM_IWAD_TYPES] =
// banner text, autoname, fg color, bg color
{ "Final Doom: TNT - Evilution", "TNT", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/tnt.txt", GI_MAPxx | GI_COMPATSHORTTEX | GI_COMPATSTAIRS },
{ "Final Doom: Plutonia Experiment", "Plutonia", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/plutonia.txt", GI_MAPxx | GI_COMPATSHORTTEX },
{ "Hexen: Beyond Heretic", NULL, MAKERGB(240,240,240), MAKERGB(107,44,24), GAME_Hexen, "mapinfo/hexen.txt", GI_MAPxx },
{ "Hexen: Deathkings of the Dark Citadel", "HexenDK", MAKERGB(240,240,240), MAKERGB(139,68,9), GAME_Hexen, "mapinfo/hexen.txt", GI_MAPxx },
{ "Hexen: Beyond Heretic", NULL, MAKERGB(240,240,240), MAKERGB(107,44,24), GAME_Hexen, "mapinfo/hexen.txt", GI_MAPxx | GI_COMPATPOLY1 },
{ "Hexen: Deathkings of the Dark Citadel", "HexenDK", MAKERGB(240,240,240), MAKERGB(139,68,9), GAME_Hexen, "mapinfo/hexen.txt", GI_MAPxx | GI_COMPATPOLY1 | GI_COMPATPOLY2 },
{ "Hexen: Demo Version", "HexenDemo",MAKERGB(240,240,240), MAKERGB(107,44,24), GAME_Hexen, "mapinfo/hexen.txt", GI_MAPxx | GI_SHAREWARE },
{ "DOOM 2: Hell on Earth", "Doom2", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx | GI_COMPATSHORTTEX },
{ "Heretic Shareware", NULL, MAKERGB(252,252,0), MAKERGB(168,0,0), GAME_Heretic, "mapinfo/hereticsw.txt",GI_SHAREWARE },
@ -79,7 +79,7 @@ const IWADInfo IWADInfos[NUM_IWAD_TYPES] =
{ "FreeDM", "FreeDM", MAKERGB(50,84,67), MAKERGB(198,220,209), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx },
{ "Blasphemer", "Blasphemer",MAKERGB(115,0,0), MAKERGB(0,0,0), GAME_Heretic, "mapinfo/heretic.txt" },
{ "Chex(R) Quest", "Chex1", MAKERGB(255,255,0), MAKERGB(0,192,0), GAME_Chex, "mapinfo/chex.txt" },
{ "Chex(R) Quest 3", "Chex3", MAKERGB(255,255,0), MAKERGB(0,192,0), GAME_Chex, "mapinfo/chex3.txt" },
{ "Chex(R) Quest 3", "Chex3", MAKERGB(255,255,0), MAKERGB(0,192,0), GAME_Chex, "mapinfo/chex3.txt", GI_NOTEXTCOLOR },
{ "Action Doom 2: Urban Brawl", "UrbanBrawl",MAKERGB(168,168,0), MAKERGB(168,0,0), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx },
{ "Harmony", "Harmony", MAKERGB(110,180,230), MAKERGB(69,79,126), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx },
//{ "ZDoom Engine", NULL, MAKERGB(168,0,0), MAKERGB(168,168,168) },

View file

@ -38,7 +38,7 @@
#endif
#include <float.h>
#ifdef unix
#if defined(unix) || defined(__APPLE__)
#include <unistd.h>
#endif
@ -61,7 +61,7 @@
#include "f_wipe.h"
#include "m_argv.h"
#include "m_misc.h"
#include "m_menu.h"
#include "menu/menu.h"
#include "c_console.h"
#include "c_dispatch.h"
#include "i_system.h"
@ -104,6 +104,7 @@
#include "compatibility.h"
#include "m_joy.h"
#include "sc_man.h"
#include "po_man.h"
#include "resourcefiles/resourcefile.h"
EXTERN_CVAR(Bool, hud_althud)
@ -200,6 +201,7 @@ gamestate_t wipegamestate = GS_DEMOSCREEN; // can be -1 to force a wipe
bool PageBlank;
FTexture *Page;
FTexture *Advisory;
bool nospriterename;
cycle_t FrameCycles;
@ -390,6 +392,18 @@ CVAR (Flag, sv_nofov, dmflags, DF_NO_FOV);
CVAR (Flag, sv_noweaponspawn, dmflags, DF_NO_COOP_WEAPON_SPAWN);
CVAR (Flag, sv_nocrouch, dmflags, DF_NO_CROUCH);
CVAR (Flag, sv_allowcrouch, dmflags, DF_YES_CROUCH);
CVAR (Flag, sv_cooploseinventory, dmflags, DF_COOP_LOSE_INVENTORY);
CVAR (Flag, sv_cooplosekeys, dmflags, DF_COOP_LOSE_KEYS);
CVAR (Flag, sv_cooploseweapons, dmflags, DF_COOP_LOSE_WEAPONS);
CVAR (Flag, sv_cooplosearmor, dmflags, DF_COOP_LOSE_ARMOR);
CVAR (Flag, sv_cooplosepowerups, dmflags, DF_COOP_LOSE_POWERUPS);
CVAR (Flag, sv_cooploseammo, dmflags, DF_COOP_LOSE_AMMO);
CVAR (Flag, sv_coophalveammo, dmflags, DF_COOP_HALVE_AMMO);
// Some (hopefully cleaner) interface to these settings.
CVAR (Mask, sv_crouch, dmflags, DF_NO_CROUCH|DF_YES_CROUCH);
CVAR (Mask, sv_jump, dmflags, DF_NO_JUMP|DF_YES_JUMP);
CVAR (Mask, sv_fallingdamage, dmflags, DF_FORCE_FALLINGHX|DF_FORCE_FALLINGZD);
//==========================================================================
//
@ -460,7 +474,6 @@ CVAR (Flag, sv_disallowsuicide, dmflags2, DF2_NOSUICIDE);
CVAR (Flag, sv_noautoaim, dmflags2, DF2_NOAUTOAIM);
CVAR (Flag, sv_dontcheckammo, dmflags2, DF2_DONTCHECKAMMO);
CVAR (Flag, sv_killbossmonst, dmflags2, DF2_KILLBOSSMONST);
//==========================================================================
//
// CVAR compatflags
@ -480,7 +493,12 @@ static int GetCompatibility(int mask)
CUSTOM_CVAR (Int, compatflags, 0, CVAR_ARCHIVE|CVAR_SERVERINFO)
{
int old = i_compatflags;
i_compatflags = GetCompatibility(self) | ii_compatflags;
if ((old ^i_compatflags) & COMPATF_POLYOBJ)
{
FPolyObj::ClearAllSubsectorLinks();
}
}
CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_NOINITCALL)
@ -559,6 +577,7 @@ CVAR (Flag, compat_noblockfriends,compatflags,COMPATF_NOBLOCKFRIENDS);
CVAR (Flag, compat_spritesort, compatflags,COMPATF_SPRITESORT);
CVAR (Flag, compat_hitscan, compatflags,COMPATF_HITSCAN);
CVAR (Flag, compat_light, compatflags,COMPATF_LIGHT);
CVAR (Flag, compat_polyobj, compatflags,COMPATF_POLYOBJ);
//==========================================================================
//
@ -880,6 +899,8 @@ void D_DoomLoop ()
// Clamp the timer to TICRATE until the playloop has been entered.
r_NoInterpolate = true;
I_SetCursor(TexMan["cursor"]);
for (;;)
{
try
@ -1671,6 +1692,10 @@ static FString ParseGameInfo(TArray<FString> &pwads, const char *fn, const char
}
while (sc.CheckToken(','));
}
else if (!nextKey.CompareNoCase("NOSPRITERENAME"))
{
nospriterename = true;
}
}
return iwad;
}
@ -2065,6 +2090,7 @@ void D_DoomMain (void)
// [GRB] Initialize player class list
SetupPlayerClasses ();
// [RH] Load custom key and weapon settings from WADs
D_LoadWadSettings ();
@ -2134,7 +2160,7 @@ void D_DoomMain (void)
bglobal.spawn_tries = 0;
bglobal.wanted_botnum = bglobal.getspawned.Size();
Printf ("M_Init: Init miscellaneous info.\n");
Printf ("M_Init: Init menus.\n");
M_Init ();
Printf ("P_Init: Init Playloop state.\n");
@ -2287,6 +2313,14 @@ void FStartupScreen::AppendStatusLine(const char *status)
//
//==========================================================================
//==========================================================================
//
// STAT fps
//
// Displays statistics about rendering times
//
//==========================================================================
ADD_STAT (fps)
{
FString out;
@ -2295,6 +2329,24 @@ ADD_STAT (fps)
return out;
}
static double f_acc, w_acc,p_acc,m_acc;
static int acc_c;
ADD_STAT (fps_accumulated)
{
f_acc += FrameCycles.TimeMS();
w_acc += WallCycles.TimeMS();
p_acc += PlaneCycles.TimeMS();
m_acc += MaskedCycles.TimeMS();
acc_c++;
FString out;
out.Format("frame=%04.1f ms walls=%04.1f ms planes=%04.1f ms masked=%04.1f ms %d counts",
f_acc/acc_c, w_acc/acc_c, p_acc/acc_c, m_acc/acc_c, acc_c);
Printf(PRINT_LOG, "%s\n", out.GetChars());
return out;
}
//==========================================================================
//
// STAT wallcycles

View file

@ -25,7 +25,7 @@
#include <stddef.h>
#include "version.h"
#include "m_menu.h"
#include "menu/menu.h"
#include "m_random.h"
#include "i_system.h"
#include "i_video.h"
@ -1917,6 +1917,22 @@ BYTE *FDynamicBuffer::GetData (int *len)
}
static int KillAll(const PClass *cls)
{
AActor *actor;
int killcount = 0;
TThinkerIterator<AActor> iterator(cls);
while ( (actor = iterator.Next ()) )
{
if (actor->IsA(cls))
{
if (!(actor->flags2 & MF2_DORMANT) && (actor->flags3 & MF3_ISMONSTER))
killcount += actor->Massacre ();
}
}
return killcount;
}
// [RH] Execute a special "ticcmd". The type byte should
// have already been read, and the stream is positioned
// at the beginning of the command's actual data.
@ -2348,22 +2364,25 @@ void Net_DoCommand (int type, BYTE **stream, int player)
case DEM_KILLCLASSCHEAT:
{
AActor *actor;
TThinkerIterator<AActor> iterator;
char *classname = ReadString (stream);
int killcount = 0;
const PClass *cls = PClass::FindClass(classname);
while ( (actor = iterator.Next ()) )
if (cls != NULL && cls->ActorInfo != NULL)
{
if (!stricmp (actor->GetClass ()->TypeName.GetChars (), classname))
killcount = KillAll(cls);
const PClass *cls_rep = cls->GetReplacement();
if (cls != cls_rep)
{
if (!(actor->flags2 & MF2_DORMANT) && (actor->flags3 & MF3_ISMONSTER))
killcount += actor->Massacre ();
killcount += KillAll(cls_rep);
}
Printf ("Killed %d monsters of type %s.\n",killcount, classname);
}
else
{
Printf ("%s is not an actor class.\n", classname);
}
Printf ("Killed %d monsters of type %s.\n",killcount, classname);
}
break;

View file

@ -54,6 +54,7 @@ enum
APMETA_ColorRange, // skin color range
APMETA_InvulMode,
APMETA_HealingRadius,
APMETA_Portrait,
APMETA_Hexenarmor0,
APMETA_Hexenarmor1,
APMETA_Hexenarmor2,

View file

@ -188,6 +188,33 @@ const char *FMetaTable::GetMetaString (DWORD id) const
return meta != NULL ? meta->Value.String : NULL;
}
CCMD (dumpactors)
{
const char *const filters[32] =
{
"0:All", "1:Doom", "2:Heretic", "3:DoomHeretic", "4:Hexen", "5:DoomHexen", "6:Raven", "7:IdRaven",
"8:Strife", "9:DoomStrife", "10:HereticStrife", "11:DoomHereticStrife", "12:HexenStrife",
"13:DoomHexenStrife", "14:RavenStrife", "15:NotChex", "16:Chex", "17:DoomChex", "18:HereticChex",
"19:DoomHereticChex", "20:HexenChex", "21:DoomHexenChex", "22:RavenChex", "23:NotStrife", "24:StrifeChex",
"25:DoomStrifeChex", "26:HereticStrifeChex", "27:NotHexen", "28:HexenStrifeChex", "29:NotHeretic",
"30:NotDoom", "31:All",
};
Printf("%i object class types total\nActor\tEd Num\tSpawnID\tFilter\tSource\n", PClass::m_Types.Size());
for (unsigned int i = 0; i < PClass::m_Types.Size(); i++)
{
PClass *cls = PClass::m_Types[i];
if (cls != NULL && cls->ActorInfo != NULL)
Printf("%s\t%i\t%i\t%s\t%s\n",
cls->TypeName.GetChars(), cls->ActorInfo->DoomEdNum,
cls->ActorInfo->SpawnID, filters[cls->ActorInfo->GameFilter & 31],
cls->Meta.GetMetaString (ACMETA_Lump));
else if (cls != NULL)
Printf("%s\tn/a\tn/a\tn/a\tEngine (not an actor type)\n", cls->TypeName.GetChars());
else
Printf("Type %i is not an object class\n", i);
}
}
CCMD (dumpclasses)
{
// This is by no means speed-optimized. But it's an informational console

View file

@ -71,6 +71,8 @@
#include "r_interpolate.h"
#include "doomstat.h"
#include "m_argv.h"
#include "po_man.h"
#include "menu/menu.h"
// MACROS ------------------------------------------------------------------
@ -297,6 +299,7 @@ static void MarkRoot()
Mark(Args);
Mark(screen);
Mark(StatusBar);
Mark(DMenu::CurrentMenu);
DThinker::MarkRoots();
FCanvasTextureInfo::Mark();
Mark(DACSThinker::ActiveThinker);

View file

@ -68,7 +68,7 @@ void PClass::StaticInit ()
// MinGW's linker is linking the object files backwards for me now...
if (head > tail)
{
swap (head, tail);
swapvalues (head, tail);
}
qsort (head + 1, tail - head - 1, sizeof(REGINFO), cregcmp);
@ -486,6 +486,11 @@ const PClass *PClass::NativeClass() const
return cls;
}
PClass *PClass::GetReplacement() const
{
return ActorInfo->GetReplacement()->Class;
}
// Symbol tables ------------------------------------------------------------
PSymbol::~PSymbol()

View file

@ -174,6 +174,7 @@ struct PClass
static const PClass *FindClass (ENamedName name) { return FindClass (FName (name)); }
static const PClass *FindClass (FName name);
const PClass *FindClassTentative (FName name); // not static!
PClass *GetReplacement() const;
static TArray<PClass *> m_Types;
static TArray<PClass *> m_RuntimeActors;

View file

@ -340,6 +340,7 @@ struct FMapThing
DWORD flags;
int special;
int args[5];
int Conversation;
void Serialize (FArchive &);
};

View file

@ -329,6 +329,7 @@ enum
COMPATF_SPRITESORT = 1 << 27, // Invert sprite sorting order for sprites of equal distance
COMPATF_HITSCAN = 1 << 28, // Hitscans use original blockmap anf hit check code.
COMPATF_LIGHT = 1 << 29, // Find neighboring light level like Doom
COMPATF_POLYOBJ = 1 << 30, // Draw polyobjects the old fashioned way
};
// Emulate old bugs for select maps. These are not exposed by a cvar

View file

@ -44,12 +44,6 @@
// Since this file is included by everything, it seems an appropriate place
// to check the NOASM/USEASM macros.
#if defined(__APPLE__)
// The assembly code needs to be tweaked for Mach-O before enabled on Macs.
#ifndef NOASM
#define NOASM
#endif
#endif
// There are three assembly-related macros:
//

View file

@ -723,7 +723,7 @@ bool F_CastResponder (event_t* ev)
if (ev->type != EV_KeyDown)
return false;
const char *cmd = C_GetBinding (ev->data1);
const char *cmd = Bindings.GetBind (ev->data1);
if (cmd != NULL && !stricmp (cmd, "toggleconsole"))
return false;

View file

@ -290,6 +290,10 @@ template<> inline FArchive &operator<< <FFont> (FArchive &arc, FFont* &font)
return SerializeFFontPtr (arc, font);
}
struct FStrifeDialogueNode;
template<> FArchive &operator<< (FArchive &arc, FStrifeDialogueNode *&node);
template<class T,class TT>
inline FArchive &operator<< (FArchive &arc, TArray<T,TT> &self)

View file

@ -132,6 +132,8 @@ long FileReader::Seek (long offset, int origin)
long FileReader::Read (void *buffer, long len)
{
assert(len >= 0);
if (len <= 0) return 0;
if (FilePos + len > StartPos + Length)
{
len = Length - FilePos + StartPos;

View file

@ -100,52 +100,88 @@ DEFINE_ACTION_FUNCTION(AActor, A_FirePistol)
//
// A_Saw
//
enum SAW_Flags
{
SF_NORANDOM = 1,
SF_RANDOMLIGHTMISS = 2,
SF_RANDOMLIGHTHIT = 4,
SF_NOUSEAMMOMISS = 8,
SF_NOUSEAMMO = 16,
};
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Saw)
{
angle_t angle;
angle_t angle;
angle_t slope;
player_t *player;
AActor *linetarget;
ACTION_PARAM_START(4);
ACTION_PARAM_START(9);
ACTION_PARAM_SOUND(fullsound, 0);
ACTION_PARAM_SOUND(hitsound, 1);
ACTION_PARAM_INT(damage, 2);
ACTION_PARAM_CLASS(pufftype, 3);
ACTION_PARAM_FIXED(Range, 4)
ACTION_PARAM_FIXED(LifeSteal, 5);
ACTION_PARAM_INT(Flags, 4);
ACTION_PARAM_FIXED(Range, 5);
ACTION_PARAM_ANGLE(Spread_XY, 6);
ACTION_PARAM_ANGLE(Spread_Z, 7);
ACTION_PARAM_FIXED(LifeSteal, 8);
if (NULL == (player = self->player))
{
return;
}
if (pufftype == NULL) pufftype = PClass::FindClass(NAME_BulletPuff);
if (damage == 0) damage = 2;
if (!(Flags & SF_NORANDOM))
damage *= (pr_saw()%10+1);
// use meleerange + 1 so the puff doesn't skip the flash (i.e. plays all states)
if (Range == 0) Range = MELEERANGE+1;
angle = self->angle + (pr_saw.Random2() * (Spread_XY / 255));
slope = P_AimLineAttack (self, angle, Range, &linetarget) + (pr_saw.Random2() * (Spread_Z / 255));
P_LineAttack (self, angle, Range,
slope, damage,
NAME_None, pufftype);
AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL)
if ((weapon != NULL) && !(Flags & SF_NOUSEAMMO) && !(!linetarget && (Flags & SF_NOUSEAMMOMISS)))
{
if (!weapon->DepleteAmmo (weapon->bAltFire))
return;
}
if (pufftype == NULL) pufftype = PClass::FindClass(NAME_BulletPuff);
if (damage == 0) damage = 2;
damage *= (pr_saw()%10+1);
angle = self->angle;
angle += pr_saw.Random2() << 18;
// use meleerange + 1 so the puff doesn't skip the flash (i.e. plays all states)
if (Range == 0) Range = MELEERANGE+1;
P_LineAttack (self, angle, Range,
P_AimLineAttack (self, angle, Range, &linetarget), damage,
NAME_None, pufftype);
if (!linetarget)
{
if ((Flags & SF_RANDOMLIGHTMISS) && (pr_saw() > 64))
{
player->extralight = !player->extralight;
}
S_Sound (self, CHAN_WEAPON, fullsound, 1, ATTN_NORM);
return;
}
if (Flags & SF_RANDOMLIGHTHIT)
{
int randVal = pr_saw();
if (randVal < 64)
{
player->extralight = 0;
}
else if (randVal < 160)
{
player->extralight = 1;
}
else
{
player->extralight = 2;
}
}
if (LifeSteal)
P_GiveBody (self, (damage * LifeSteal) >> FRACBITS);

View file

@ -40,7 +40,7 @@
#include "f_finale.h"
#include "m_argv.h"
#include "m_misc.h"
#include "m_menu.h"
#include "menu/menu.h"
#include "m_random.h"
#include "m_crc32.h"
#include "i_system.h"
@ -116,18 +116,20 @@ EXTERN_CVAR (Float, con_midtime);
//
// CVAR displaynametags
//
// Selects whether to display name tags or not when changing weapons
// Selects whether to display name tags or not when changing weapons/items
//
//==========================================================================
CUSTOM_CVAR (Bool, displaynametags, 0, CVAR_ARCHIVE)
CUSTOM_CVAR (Int, displaynametags, 0, CVAR_ARCHIVE)
{
if (self != 0 && self != 1)
if (self < 0 || self > 3)
{
self = 0;
}
}
CVAR(Int, nametagcolor, CR_GOLD, CVAR_ARCHIVE)
gameaction_t gameaction;
gamestate_t gamestate = GS_STARTUP;
@ -322,11 +324,23 @@ CCMD (turn180)
CCMD (weapnext)
{
SendItemUse = players[consoleplayer].weapons.PickNextWeapon (&players[consoleplayer]);
// [BC] Option to display the name of the weapon being cycled to.
if ((displaynametags & 2) && StatusBar && SmallFont && SendItemUse)
{
StatusBar->AttachMessage(new DHUDMessageFadeOut(SmallFont, SendItemUse->GetTag(),
1.5f, 0.90f, 0, 0, (EColorRange)*nametagcolor, 2.f, 0.35f), MAKE_ID( 'W', 'E', 'P', 'N' ));
}
}
CCMD (weapprev)
{
SendItemUse = players[consoleplayer].weapons.PickPrevWeapon (&players[consoleplayer]);
// [BC] Option to display the name of the weapon being cycled to.
if ((displaynametags & 2) && StatusBar && SmallFont && SendItemUse)
{
StatusBar->AttachMessage(new DHUDMessageFadeOut(SmallFont, SendItemUse->GetTag(),
1.5f, 0.90f, 0, 0, (EColorRange)*nametagcolor, 2.f, 0.35f), MAKE_ID( 'W', 'E', 'P', 'N' ));
}
}
CCMD (invnext)
@ -354,10 +368,9 @@ CCMD (invnext)
who->InvSel = who->Inventory;
}
}
if (displaynametags && StatusBar && SmallFont
&& gamestate == GS_LEVEL && level.time > con_midtime && who->InvSel)
StatusBar->AttachMessage (new DHUDMessage (SmallFont, who->InvSel->GetTag(),
2.5f, 0.375f, 0, 0, CR_YELLOW, con_midtime), MAKE_ID('S','I','N','V'));
if ((displaynametags & 1) && StatusBar && SmallFont && who->InvSel)
StatusBar->AttachMessage (new DHUDMessageFadeOut (SmallFont, who->InvSel->GetTag(),
1.5f, 0.80f, 0, 0, (EColorRange)*nametagcolor, 2.f, 0.35f), MAKE_ID('S','I','N','V'));
}
who->player->inventorytics = 5*TICRATE;
}
@ -385,10 +398,9 @@ CCMD (invprev)
}
who->InvSel = item;
}
if (displaynametags && StatusBar && SmallFont
&& gamestate == GS_LEVEL && level.time > con_midtime && who->InvSel)
StatusBar->AttachMessage (new DHUDMessage (SmallFont, who->InvSel->GetTag(),
2.5f, 0.375f, 0, 0, CR_YELLOW, con_midtime), MAKE_ID('S','I','N','V'));
if ((displaynametags & 1) && StatusBar && SmallFont && who->InvSel)
StatusBar->AttachMessage (new DHUDMessageFadeOut (SmallFont, who->InvSel->GetTag(),
1.5f, 0.80f, 0, 0, (EColorRange)*nametagcolor, 2.f, 0.35f), MAKE_ID('S','I','N','V'));
}
who->player->inventorytics = 5*TICRATE;
}
@ -869,7 +881,7 @@ bool G_Responder (event_t *ev)
if (gameaction == ga_nothing &&
(demoplayback || gamestate == GS_DEMOSCREEN || gamestate == GS_TITLELEVEL))
{
const char *cmd = C_GetBinding (ev->data1);
const char *cmd = Bindings.GetBind (ev->data1);
if (ev->type == EV_KeyDown)
{
@ -887,16 +899,17 @@ bool G_Responder (event_t *ev)
stricmp (cmd, "bumpgamma") &&
stricmp (cmd, "screenshot")))
{
M_StartControlPanel (true, true);
M_StartControlPanel(true);
M_SetMenu(NAME_Mainmenu, -1);
return true;
}
else
{
return C_DoKey (ev);
return C_DoKey (ev, &Bindings, &DoubleBindings);
}
}
if (cmd && cmd[0] == '+')
return C_DoKey (ev);
return C_DoKey (ev, &Bindings, &DoubleBindings);
return false;
}
@ -908,7 +921,7 @@ bool G_Responder (event_t *ev)
{
if (ST_Responder (ev))
return true; // status window ate it
if (!viewactive && AM_Responder (ev))
if (!viewactive && AM_Responder (ev, false))
return true; // automap ate it
}
else if (gamestate == GS_FINALE)
@ -920,12 +933,12 @@ bool G_Responder (event_t *ev)
switch (ev->type)
{
case EV_KeyDown:
if (C_DoKey (ev))
if (C_DoKey (ev, &Bindings, &DoubleBindings))
return true;
break;
case EV_KeyUp:
C_DoKey (ev);
C_DoKey (ev, &Bindings, &DoubleBindings);
break;
// [RH] mouse buttons are sent as key up/down events
@ -939,7 +952,7 @@ bool G_Responder (event_t *ev)
// the events *last* so that any bound keys get precedence.
if (gamestate == GS_LEVEL && viewactive)
return AM_Responder (ev);
return AM_Responder (ev, true);
return (ev->type == EV_KeyDown ||
ev->type == EV_Mouse);
@ -2050,11 +2063,11 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio
M_AppendPNGChunk (stdfile, MAKE_ID('s','n','X','t'), &next, 1);
}
M_NotifyNewSave (filename.GetChars(), description, okForQuicksave);
M_FinishPNG (stdfile);
fclose (stdfile);
M_NotifyNewSave (filename.GetChars(), description, okForQuicksave);
// Check whether the file is ok.
bool success = false;
stdfile = fopen (filename.GetChars(), "rb");

View file

@ -139,7 +139,7 @@ void P_DSparilTeleport (AActor *actor)
DSpotState *state = DSpotState::GetSpotState();
if (state == NULL) return;
spot = state->GetSpotWithMinDistance(PClass::FindClass("BossSpot"), actor->x, actor->y, 128*FRACUNIT);
spot = state->GetSpotWithMinMaxDistance(PClass::FindClass("BossSpot"), actor->x, actor->y, 128*FRACUNIT, 0);
if (spot == NULL) return;
prevX = actor->x;

View file

@ -68,7 +68,6 @@
#include "m_png.h"
#include "m_random.h"
#include "version.h"
#include "m_menu.h"
#include "statnums.h"
#include "sbarinfo.h"
#include "r_translate.h"
@ -78,6 +77,7 @@
#include "d_net.h"
#include "d_netinf.h"
#include "v_palette.h"
#include "menu/menu.h"
#include "gi.h"
@ -99,6 +99,7 @@ void STAT_WRITE(FILE *f);
EXTERN_CVAR (Float, sv_gravity)
EXTERN_CVAR (Float, sv_aircontrol)
EXTERN_CVAR (Int, disableautosave)
EXTERN_CVAR (String, playerclass)
#define SNAP_ID MAKE_ID('s','n','A','p')
#define DSNP_ID MAKE_ID('d','s','N','p')
@ -230,6 +231,15 @@ void G_DeferedInitNew (const char *mapname, int newskill)
gameaction = ga_newgame2;
}
void G_DeferedInitNew (FGameStartup *gs)
{
playerclass = gs->PlayerClass;
d_mapname = AllEpisodes[gs->Episode].mEpisodeMap;
d_skill = gs->Skill;
CheckWarpTransMap (d_mapname, true);
gameaction = ga_newgame2;
}
//==========================================================================
//
//
@ -1464,8 +1474,8 @@ void G_SerializeLevel (FArchive &arc, bool hubLoad)
P_SerializeThinkers (arc, hubLoad);
P_SerializeWorld (arc);
P_SerializePolyobjs (arc);
P_SerializeSubsectors(arc);
StatusBar->Serialize (arc);
//SerializeInterpolations (arc);
arc << level.total_monsters << level.total_items << level.total_secrets;

View file

@ -486,6 +486,8 @@ void G_InitNew (const char *mapname, bool bTitleLevel);
// A normal game starts at map 1,
// but a warp test can start elsewhere
void G_DeferedInitNew (const char *mapname, int skill = -1);
struct FGameStartup;
void G_DeferedInitNew (FGameStartup *gs);
void G_ExitLevel (int position, bool keepFacing);
void G_SecretExitLevel (int position);
@ -548,7 +550,8 @@ enum ESkillProperty
SKILLP_ACSReturn,
SKILLP_MonsterHealth,
SKILLP_FriendlyHealth,
SKILLP_NoPain
SKILLP_NoPain,
SKILLP_ArmorFactor
};
int G_SkillProperty(ESkillProperty prop);
const char * G_SkillName();
@ -583,6 +586,7 @@ struct FSkillInfo
fixed_t MonsterHealth;
fixed_t FriendlyHealth;
bool NoPain;
fixed_t ArmorFactor;
FSkillInfo() {}
FSkillInfo(const FSkillInfo &other)
@ -601,6 +605,16 @@ struct FSkillInfo
extern TArray<FSkillInfo> AllSkills;
extern int DefaultSkill;
struct FEpisode
{
FString mEpisodeName;
FString mEpisodeMap;
FString mPicName;
char mShortcut;
bool mNoSkill;
};
extern TArray<FEpisode> AllEpisodes;
#endif //__G_LEVEL_H__

View file

@ -38,7 +38,6 @@
#include "g_level.h"
#include "sc_man.h"
#include "w_wad.h"
#include "m_menu.h"
#include "cmdlib.h"
#include "v_video.h"
#include "p_lnspec.h"
@ -63,6 +62,8 @@ TArray<level_info_t> wadlevelinfos;
level_info_t TheDefaultLevelInfo;
static cluster_info_t TheDefaultClusterInfo;
TArray<FEpisode> AllEpisodes;
//==========================================================================
//
//
@ -1404,6 +1405,7 @@ MapFlagHandlers[] =
{ "compat_noblockfriends", MITYPE_COMPATFLAG, COMPATF_NOBLOCKFRIENDS},
{ "compat_spritesort", MITYPE_COMPATFLAG, COMPATF_SPRITESORT},
{ "compat_light", MITYPE_COMPATFLAG, COMPATF_LIGHT},
{ "compat_polyobj", MITYPE_COMPATFLAG, COMPATF_POLYOBJ},
{ "cd_start_track", MITYPE_EATNEXT, 0, 0 },
{ "cd_end1_track", MITYPE_EATNEXT, 0, 0 },
{ "cd_end2_track", MITYPE_EATNEXT, 0, 0 },
@ -1654,10 +1656,10 @@ level_info_t *FMapInfoParser::ParseMapHeader(level_info_t &defaultinfo)
void FMapInfoParser::ParseEpisodeInfo ()
{
int i;
unsigned int i;
char map[9];
char *pic = NULL;
bool picisgfx = false; // Shut up, GCC!!!!
FString pic;
FString name;
bool remove = false;
char key = 0;
bool noskill = false;
@ -1696,15 +1698,13 @@ void FMapInfoParser::ParseEpisodeInfo ()
{
ParseAssign();
sc.MustGetString ();
ReplaceString (&pic, sc.String);
picisgfx = false;
name = sc.String;
}
else if (sc.Compare ("picname"))
{
ParseAssign();
sc.MustGetString ();
ReplaceString (&pic, sc.String);
picisgfx = true;
pic = sc.String;
}
else if (sc.Compare ("remove"))
{
@ -1750,9 +1750,9 @@ void FMapInfoParser::ParseEpisodeInfo ()
}
for (i = 0; i < EpiDef.numitems; ++i)
for (i = 0; i < AllEpisodes.Size(); i++)
{
if (strncmp (EpisodeMaps[i], map, 8) == 0)
if (AllEpisodes[i].mEpisodeMap.CompareNoCase(map) == 0)
{
break;
}
@ -1761,50 +1761,17 @@ void FMapInfoParser::ParseEpisodeInfo ()
if (remove)
{
// If the remove property is given for an episode, remove it.
if (i < EpiDef.numitems)
{
if (i+1 < EpiDef.numitems)
{
memmove (&EpisodeMaps[i], &EpisodeMaps[i+1],
sizeof(EpisodeMaps[0])*(EpiDef.numitems - i - 1));
memmove (&EpisodeMenu[i], &EpisodeMenu[i+1],
sizeof(EpisodeMenu[0])*(EpiDef.numitems - i - 1));
memmove (&EpisodeNoSkill[i], &EpisodeNoSkill[i+1],
sizeof(EpisodeNoSkill[0])*(EpiDef.numitems - i - 1));
}
EpiDef.numitems--;
}
AllEpisodes.Delete(i);
}
else
{
if (pic == NULL)
{
pic = copystring (map);
picisgfx = false;
}
FEpisode *epi = &AllEpisodes[AllEpisodes.Reserve(1)];
if (i == EpiDef.numitems)
{
if (EpiDef.numitems == MAX_EPISODES)
{
i = EpiDef.numitems - 1;
}
else
{
i = EpiDef.numitems++;
}
}
else
{
delete[] const_cast<char *>(EpisodeMenu[i].name);
}
EpisodeMenu[i].name = pic;
EpisodeMenu[i].alphaKey = tolower(key);
EpisodeMenu[i].fulltext = !picisgfx;
EpisodeNoSkill[i] = noskill;
strncpy (EpisodeMaps[i], map, 8);
EpisodeMaps[i][8] = 0;
epi->mEpisodeMap = map;
epi->mEpisodeName = name;
epi->mPicName = pic;
epi->mShortcut = tolower(key);
epi->mNoSkill = noskill;
}
}
@ -1817,12 +1784,7 @@ void FMapInfoParser::ParseEpisodeInfo ()
void ClearEpisodes()
{
for (int i = 0; i < EpiDef.numitems; ++i)
{
delete[] const_cast<char *>(EpisodeMenu[i].name);
EpisodeMenu[i].name = NULL;
}
EpiDef.numitems = 0;
AllEpisodes.Clear();
}
//==========================================================================
@ -2003,7 +1965,7 @@ void G_ParseMapInfo (const char *basemapinfo)
}
EndSequences.ShrinkToFit ();
if (EpiDef.numitems == 0)
if (AllEpisodes.Size() == 0)
{
I_FatalError ("You cannot use clearepisodes in a MAPINFO if you do not define any new episodes after it.");
}

View file

@ -448,9 +448,9 @@ DEFINE_ACTION_FUNCTION(AActor, A_MinotaurRoam)
{
// Turn
if (pr_minotaurroam() & 1)
self->movedir = (++self->movedir)%8;
self->movedir = (self->movedir + 1) % 8;
else
self->movedir = (self->movedir+7)%8;
self->movedir = (self->movedir + 7) % 8;
FaceMovementDirection (self);
}
}

View file

@ -68,7 +68,7 @@ void A_Unblock(AActor *self, bool drop)
self->flags &= ~MF_SOLID;
// If the self has a conversation that sets an item to drop, drop that.
// If the actor has a conversation that sets an item to drop, drop that.
if (self->Conversation != NULL && self->Conversation->DropType != NULL)
{
P_DropItem (self, self->Conversation->DropType, -1, 256);

View file

@ -5,6 +5,7 @@
#include "r_data.h"
#include "a_pickups.h"
#include "templates.h"
#include "g_level.h"
IMPLEMENT_CLASS (AArmor)
@ -84,6 +85,18 @@ bool ABasicArmor::HandlePickup (AInventory *item)
// You shouldn't be picking up BasicArmor anyway.
return true;
}
if (item->IsKindOf(RUNTIME_CLASS(ABasicArmorBonus)) && !(item->ItemFlags & IF_IGNORESKILL))
{
ABasicArmorBonus *armor = static_cast<ABasicArmorBonus*>(item);
armor->SaveAmount = FixedMul(armor->SaveAmount, G_SkillProperty(SKILLP_ArmorFactor));
}
else if (item->IsKindOf(RUNTIME_CLASS(ABasicArmorPickup)) && !(item->ItemFlags & IF_IGNORESKILL))
{
ABasicArmorPickup *armor = static_cast<ABasicArmorPickup*>(item);
armor->SaveAmount = FixedMul(armor->SaveAmount, G_SkillProperty(SKILLP_ArmorFactor));
}
if (Inventory != NULL)
{
return Inventory->HandlePickup (item);
@ -198,10 +211,17 @@ void ABasicArmorPickup::Serialize (FArchive &arc)
AInventory *ABasicArmorPickup::CreateCopy (AActor *other)
{
ABasicArmorPickup *copy = static_cast<ABasicArmorPickup *> (Super::CreateCopy (other));
if (!(ItemFlags & IF_IGNORESKILL))
{
SaveAmount = FixedMul(SaveAmount, G_SkillProperty(SKILLP_ArmorFactor));
}
copy->SavePercent = SavePercent;
copy->SaveAmount = SaveAmount;
copy->MaxAbsorb = MaxAbsorb;
copy->MaxFullAbsorb = MaxFullAbsorb;
return copy;
}
@ -272,6 +292,12 @@ void ABasicArmorBonus::Serialize (FArchive &arc)
AInventory *ABasicArmorBonus::CreateCopy (AActor *other)
{
ABasicArmorBonus *copy = static_cast<ABasicArmorBonus *> (Super::CreateCopy (other));
if (!(ItemFlags & IF_IGNORESKILL))
{
SaveAmount = FixedMul(SaveAmount, G_SkillProperty(SKILLP_ArmorFactor));
}
copy->SavePercent = SavePercent;
copy->SaveAmount = SaveAmount;
copy->MaxSaveAmount = MaxSaveAmount;
@ -279,6 +305,7 @@ AInventory *ABasicArmorBonus::CreateCopy (AActor *other)
copy->BonusMax = BonusMax;
copy->MaxAbsorb = MaxAbsorb;
copy->MaxFullAbsorb = MaxFullAbsorb;
return copy;
}

View file

@ -1004,6 +1004,12 @@ void APowerFlight::EndEffect ()
bool APowerFlight::DrawPowerup (int x, int y)
{
// If this item got a valid icon use that instead of the default spinning wings.
if (Icon.isValid())
{
return Super::DrawPowerup(x, y);
}
if (EffectTics > BLINKTHRESHOLD || !(EffectTics & 16))
{
FTextureID picnum = TexMan.CheckForTexture ("SPFLY0", FTexture::TEX_MiscPatch);

View file

@ -72,7 +72,7 @@ void AFastProjectile::Tick ()
tm.LastRipped = NULL; // [RH] Do rip damage each step, like Hexen
}
if (!P_TryMove (this, x + xfrac,y + yfrac, true, false, tm))
if (!P_TryMove (this, x + xfrac,y + yfrac, true, NULL, tm))
{ // Blocked move
if (!(flags3 & MF3_SKYEXPLODE))
{
@ -158,10 +158,13 @@ void AFastProjectile::Effect()
if (name != NAME_None)
{
fixed_t hitz = z-8*FRACUNIT;
if (hitz < floorz)
{
hitz = floorz;
}
// Do not clip this offset to the floor.
hitz += GetClass()->Meta.GetMetaFixed (ACMETA_MissileHeight);
const PClass *trail = PClass::FindClass(name);
if (trail != NULL)

View file

@ -19,7 +19,12 @@ struct OneKey
bool check(AActor * owner)
{
return !!owner->FindInventory(key);
// P_GetMapColorForKey() checks the key directly
if (owner->IsKindOf (RUNTIME_CLASS(AKey)))
return owner->IsA(key);
// Other calls check an actor that may have a key in its inventory.
else
return !!owner->FindInventory(key);
}
};

View file

@ -356,6 +356,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_RestoreSpecialPosition)
self->z += FloatBobOffsets[(self->FloatBobPhase + level.maptime) & 63];
}
}
self->SetOrigin (self->x, self->y, self->z);
}
int AInventory::StaticLastMessageTic;
@ -889,7 +890,7 @@ void AInventory::Touch (AActor *toucher)
{
const char * message = PickupMessage ();
if (toucher->CheckLocalView (consoleplayer)
if (message != NULL && *message != 0 && toucher->CheckLocalView (consoleplayer)
&& (StaticLastMessageTic != gametic || StaticLastMessage != message))
{
StaticLastMessageTic = gametic;
@ -959,9 +960,7 @@ void AInventory::DoPickupSpecial (AActor *toucher)
const char *AInventory::PickupMessage ()
{
const char *message = GetClass()->Meta.GetMetaString (AIMETA_PickupMessage);
return message != NULL? message : "You got a pickup";
return GetClass()->Meta.GetMetaString (AIMETA_PickupMessage);
}
//===========================================================================

View file

@ -71,7 +71,7 @@ class ARandomSpawner : public AActor
cls = PClass::FindClass(di->Name);
if (cls != NULL)
{
const PClass *rep = cls->ActorInfo->GetReplacement()->Class;
const PClass *rep = cls->GetReplacement();
if (rep != NULL)
{
cls = rep;

View file

@ -147,14 +147,20 @@ struct FSpotList
//
//----------------------------------------------------------------------------
ASpecialSpot *GetSpotWithMinDistance(fixed_t x, fixed_t y, fixed_t distance)
ASpecialSpot *GetSpotWithMinMaxDistance(fixed_t x, fixed_t y, fixed_t mindist, fixed_t maxdist)
{
if (Spots.Size() == 0) return NULL;
int i = pr_spot() % Spots.Size();
int initial = i;
while (P_AproxDistance(Spots[i]->x - x, Spots[i]->y - y) < distance)
fixed_t distance;
while (true)
{
distance = P_AproxDistance(Spots[i]->x - x, Spots[i]->y - y);
if ((distance >= mindist) && ((maxdist == 0) || (distance <= maxdist))) break;
i = (i+1) % Spots.Size();
if (i == initial) return NULL;
}
@ -329,10 +335,10 @@ ASpecialSpot *DSpotState::GetNextInList(const PClass *type, int skipcounter)
//
//----------------------------------------------------------------------------
ASpecialSpot *DSpotState::GetSpotWithMinDistance(const PClass *type, fixed_t x, fixed_t y, fixed_t distance)
ASpecialSpot *DSpotState::GetSpotWithMinMaxDistance(const PClass *type, fixed_t x, fixed_t y, fixed_t mindist, fixed_t maxdist)
{
FSpotList *list = FindSpotList(type);
if (list != NULL) return list->GetSpotWithMinDistance(x, y, distance);
if (list != NULL) return list->GetSpotWithMinMaxDistance(x, y, mindist, maxdist);
return NULL;
}

View file

@ -36,7 +36,7 @@ public:
bool RemoveSpot(ASpecialSpot *spot);
void Serialize(FArchive &arc);
ASpecialSpot *GetNextInList(const PClass *type, int skipcounter);
ASpecialSpot *GetSpotWithMinDistance(const PClass *type, fixed_t x, fixed_t y, fixed_t distance);
ASpecialSpot *GetSpotWithMinMaxDistance(const PClass *type, fixed_t x, fixed_t y, fixed_t mindist, fixed_t maxdist);
ASpecialSpot *GetRandomSpot(const PClass *type, bool onlyonce = false);
};

View file

@ -933,16 +933,16 @@ void Popup::close()
////////////////////////////////////////////////////////////////////////////////
inline void adjustRelCenter(const SBarInfoCoordinate &x, const SBarInfoCoordinate &y, double &outX, double &outY, const double &xScale, const double &yScale)
inline void adjustRelCenter(bool relX, bool relY, const double &x, const double &y, double &outX, double &outY, const double &xScale, const double &yScale)
{
if(x.RelCenter())
outX = *x + (SCREENWIDTH/(hud_scale ? xScale*2 : 2));
if(relX)
outX = x + (SCREENWIDTH/(hud_scale ? xScale*2 : 2));
else
outX = *x;
if(y.RelCenter())
outY = *y + (SCREENHEIGHT/(hud_scale ? yScale*2 : 2));
outX = x;
if(relY)
outY = y + (SCREENHEIGHT/(hud_scale ? yScale*2 : 2));
else
outY = *y;
outY = y;
}
class DSBarInfo : public DBaseStatusBar
@ -1227,7 +1227,7 @@ public:
double xScale = !hud_scale ? 1.0 : (double) CleanXfac*320.0/(double) script->resW;//(double) SCREENWIDTH/(double) script->resW;
double yScale = !hud_scale ? 1.0 : (double) CleanYfac*200.0/(double) script->resH;//(double) SCREENHEIGHT/(double) script->resH;
adjustRelCenter(x, y, rx, ry, xScale, yScale);
adjustRelCenter(x.RelCenter(), y.RelCenter(), dx, dy, rx, ry, xScale, yScale);
// We can't use DTA_HUDRules since it forces a width and height.
// Translation: No high res.
@ -1288,7 +1288,7 @@ public:
}
if(clearDontDraw)
screen->Clear(static_cast<int>(rcx), static_cast<int>(rcy), static_cast<int>(MIN<double>(rcr, w)), static_cast<int>(MIN<double>(rcb, h)), GPalette.BlackIndex, 0);
screen->Clear(static_cast<int>(rcx), static_cast<int>(rcy), static_cast<int>(MIN<double>(rcr, rcx+w)), static_cast<int>(MIN<double>(rcb, rcy+h)), GPalette.BlackIndex, 0);
else
{
if(alphaMap)
@ -1343,13 +1343,16 @@ public:
xScale = (double) CleanXfac*320.0/(double) script->resW;//(double) SCREENWIDTH/(double) script->resW;
yScale = (double) CleanYfac*200.0/(double) script->resH;//(double) SCREENWIDTH/(double) script->resW;
}
adjustRelCenter(x, y, ax, ay, xScale, yScale);
adjustRelCenter(x.RelCenter(), y.RelCenter(), *x, *y, ax, ay, xScale, yScale);
}
while(*str != '\0')
{
if(*str == ' ')
{
ax += font->GetSpaceWidth();
if(script->spacingCharacter == '\0')
ax += font->GetSpaceWidth();
else
ax += font->GetCharWidth((int) script->spacingCharacter);
str++;
continue;
}

View file

@ -93,7 +93,10 @@ class CommandDrawImage : public SBarInfoCommand
else if(sc.Compare("amulet"))
type = HEXENARMOR_AMULET;
else
sc.ScriptError("Unkown armor type: '%s'", sc.String);
{
sc.ScriptMessage("Unkown armor type: '%s'", sc.String);
type = HEXENARMOR_ARMOR;
}
sc.MustGetToken(',');
getImage = true;
}
@ -108,9 +111,12 @@ class CommandDrawImage : public SBarInfoCommand
const PClass* item = PClass::FindClass(sc.String);
if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of Inventory
{
sc.ScriptError("'%s' is not a type of inventory item.", sc.String);
sc.ScriptMessage("'%s' is not a type of inventory item.", sc.String);
}
else
{
sprite = ((AInventory *)GetDefaultByType(item))->Icon;
}
sprite = ((AInventory *)GetDefaultByType(item))->Icon;
image = -1;
}
}
@ -324,7 +330,7 @@ class CommandDrawSwitchableImage : public CommandDrawImage
const PClass* item = PClass::FindClass(sc.String);
if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of Inventory
{
sc.ScriptError("'%s' is not a type of inventory item.", sc.String);
sc.ScriptMessage("'%s' is not a type of inventory item.", sc.String);
}
GetOperation(sc, conditionalOperator[0], conditionalValue[0]);
}
@ -349,7 +355,7 @@ class CommandDrawSwitchableImage : public CommandDrawImage
const PClass* item = PClass::FindClass(sc.String);
if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of Inventory
{
sc.ScriptError("'%s' is not a type of inventory item.", sc.String);
sc.ScriptMessage("'%s' is not a type of inventory item.", sc.String);
}
GetOperation(sc, conditionalOperator[1], conditionalValue[1]);
}
@ -516,7 +522,7 @@ class CommandDrawString : public SBarInfoCommand
CommandDrawString(SBarInfo *script) : SBarInfoCommand(script),
shadow(false), shadowX(2), shadowY(2), spacing(0), font(NULL),
translation(CR_UNTRANSLATED), cache(-1), strValue(CONSTANT),
valueArgument(0)
valueArgument(0), alignment (ALIGN_RIGHT)
{
}
@ -529,7 +535,10 @@ class CommandDrawString : public SBarInfoCommand
sc.MustGetToken(TK_Identifier);
font = V_GetFont(sc.String);
if(font == NULL)
sc.ScriptError("Unknown font '%s'.", sc.String);
{
sc.ScriptMessage("Unknown font '%s'.", sc.String);
font = SmallFont;
}
sc.MustGetToken(',');
translation = GetTranslation(sc);
sc.MustGetToken(',');
@ -587,6 +596,42 @@ class CommandDrawString : public SBarInfoCommand
{
sc.MustGetToken(TK_IntConst);
spacing = sc.Number;
if(sc.CheckToken(',')) //[KS] flags? flags! SIX FLAGS!
{
while(sc.CheckToken(TK_Identifier))
{
if(sc.Compare("alignment"))
{
sc.MustGetToken('(');
sc.MustGetToken(TK_Identifier);
if(sc.Compare("right"))
alignment = ALIGN_RIGHT;
else if(sc.Compare("left"))
alignment = ALIGN_LEFT;
else if(sc.Compare("center"))
alignment = ALIGN_CENTER;
else
sc.ScriptError("Unknown alignment '%s'.", sc.String);
sc.MustGetToken(')');
}
else if(sc.Compare("drawshadow"))
{
if(sc.CheckToken('('))
{
sc.MustGetToken(TK_IntConst);
shadowX = sc.Number;
sc.MustGetToken(',');
sc.MustGetToken(TK_IntConst);
shadowY = sc.Number;
sc.MustGetToken(')');
}
shadow = true;
}
else
sc.ScriptError("Unknown flag '%s'.", sc.String);
if(!sc.CheckToken('|') && !sc.CheckToken(',')) break;
}
}
}
sc.MustGetToken(';');
@ -682,13 +727,33 @@ class CommandDrawString : public SBarInfoCommand
}
}
protected:
enum StringAlignment
{
ALIGN_RIGHT,
ALIGN_LEFT,
ALIGN_CENTER,
};
void RealignString()
{
x = startX;
if(script->spacingCharacter == '\0')
x -= static_cast<int> (font->StringWidth(str)+(spacing * str.Len()));
else //monospaced, so just multiplay the character size
x -= static_cast<int> ((font->GetCharWidth((int) script->spacingCharacter) + spacing) * str.Len());
switch (alignment)
{
case ALIGN_LEFT:
break;
case ALIGN_RIGHT:
if(script->spacingCharacter == '\0')
x -= static_cast<int> (font->StringWidth(str)+(spacing * str.Len()));
else //monospaced, so just multiplay the character size
x -= static_cast<int> ((font->GetCharWidth((int) script->spacingCharacter) + spacing) * str.Len());
break;
case ALIGN_CENTER:
if(script->spacingCharacter == '\0')
x -= static_cast<int> (font->StringWidth(str)+(spacing * str.Len()) / 2);
else
x -= static_cast<int> ((font->GetCharWidth((int) script->spacingCharacter) + spacing) * str.Len() / 2);
break;
}
}
enum StringValueType
@ -721,6 +786,7 @@ class CommandDrawString : public SBarInfoCommand
StringValueType strValue;
int valueArgument;
FString str;
StringAlignment alignment;
private:
void SetStringToTag(AActor *actor)
@ -763,7 +829,10 @@ class CommandDrawNumber : public CommandDrawString
sc.MustGetToken(TK_Identifier);
font = V_GetFont(sc.String);
if(font == NULL)
sc.ScriptError("Unknown font '%s'.", sc.String);
{
sc.ScriptMessage("Unknown font '%s'.", sc.String);
font = SmallFont;
}
sc.MustGetToken(',');
normalTranslation = GetTranslation(sc);
sc.MustGetToken(',');
@ -793,7 +862,8 @@ class CommandDrawNumber : public CommandDrawString
inventoryItem = PClass::FindClass(sc.String);
if(inventoryItem == NULL || !RUNTIME_CLASS(AAmmo)->IsAncestorOf(inventoryItem)) //must be a kind of ammo
{
sc.ScriptError("'%s' is not a type of ammo.", sc.String);
sc.ScriptMessage("'%s' is not a type of ammo.", sc.String);
inventoryItem = RUNTIME_CLASS(AAmmo);
}
}
else if(sc.Compare("ammocapacity"))
@ -803,7 +873,8 @@ class CommandDrawNumber : public CommandDrawString
inventoryItem = PClass::FindClass(sc.String);
if(inventoryItem == NULL || !RUNTIME_CLASS(AAmmo)->IsAncestorOf(inventoryItem)) //must be a kind of ammo
{
sc.ScriptError("'%s' is not a type of ammo.", sc.String);
sc.ScriptMessage("'%s' is not a type of ammo.", sc.String);
inventoryItem = RUNTIME_CLASS(AAmmo);
}
}
else if(sc.Compare("frags"))
@ -845,18 +916,20 @@ class CommandDrawNumber : public CommandDrawString
value = POWERUPTIME;
sc.MustGetToken(TK_Identifier);
inventoryItem = PClass::FindClass(sc.String);
if(inventoryItem == NULL || !PClass::FindClass("PowerupGiver")->IsAncestorOf(inventoryItem))
if(inventoryItem == NULL || !RUNTIME_CLASS(APowerupGiver)->IsAncestorOf(inventoryItem))
{
sc.ScriptError("'%s' is not a type of PowerupGiver.", sc.String);
sc.ScriptMessage("'%s' is not a type of PowerupGiver.", sc.String);
inventoryItem = RUNTIME_CLASS(APowerupGiver);
}
}
else
{
value = INVENTORY;
inventoryItem = PClass::FindClass(sc.String);
if(inventoryItem == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(inventoryItem)) //must be a kind of ammo
if(inventoryItem == NULL || !RUNTIME_CLASS(AInventory)->IsAncestorOf(inventoryItem)) //must be a kind of ammo
{
sc.ScriptError("'%s' is not a type of inventory item.", sc.String);
sc.ScriptMessage("'%s' is not a type of inventory item.", sc.String);
inventoryItem = RUNTIME_CLASS(AInventory);
}
}
sc.MustGetToken(',');
@ -887,6 +960,20 @@ class CommandDrawNumber : public CommandDrawString
interpolationSpeed = sc.Number;
sc.MustGetToken(')');
}
else if(sc.Compare("alignment"))
{
sc.MustGetToken('(');
sc.MustGetToken(TK_Identifier);
if(sc.Compare("right"))
alignment = ALIGN_RIGHT;
else if(sc.Compare("left"))
alignment = ALIGN_LEFT;
else if(sc.Compare("center"))
alignment = ALIGN_CENTER;
else
sc.ScriptError("Unknown alignment '%s'.", sc.String);
sc.MustGetToken(')');
}
else
sc.ScriptError("Unknown flag '%s'.", sc.String);
if(!sc.CheckToken('|'))
@ -1275,7 +1362,10 @@ class CommandDrawSelectedInventory : public SBarInfoCommandFlowControl, private
{
font = V_GetFont(sc.String);
if(font == NULL)
sc.ScriptError("Unknown font '%s'.", sc.String);
{
sc.ScriptMessage("Unknown font '%s'.", sc.String);
font = SmallFont;
}
sc.MustGetToken(',');
break;
}
@ -1757,7 +1847,10 @@ class CommandDrawInventoryBar : public SBarInfoCommand
sc.MustGetToken(TK_Identifier);
font = V_GetFont(sc.String);
if(font == NULL)
{
sc.ScriptError("Unknown font '%s'.", sc.String);
font = SmallFont;
}
sc.MustGetToken(',');
GetCoordinates(sc, fullScreenOffsets, x, y);
@ -1991,9 +2084,12 @@ class CommandDrawBar : public SBarInfoCommand
FTexture *fg = statusBar->Images[foreground];
FTexture *bg = (background != -1) ? statusBar->Images[background] : NULL;
fixed_t value = drawValue;
if(border != 0)
{
value = FRACUNIT - value; //invert since the new drawing method requires drawing the bg on the fg.
//Draw the whole foreground
statusBar->DrawGraphic(fg, this->x, this->y, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets());
}
@ -2010,7 +2106,7 @@ class CommandDrawBar : public SBarInfoCommand
fixed_t clip[4] = {0, 0, 0, 0};
fixed_t sizeOfImage = (horizontal ? fg->GetScaledWidth()-border*2 : fg->GetScaledHeight()-border*2)<<FRACBITS;
clip[(!horizontal)|((horizontal ? !reverse : reverse)<<1)] = sizeOfImage - FixedMul(sizeOfImage, drawValue);
clip[(!horizontal)|((horizontal ? !reverse : reverse)<<1)] = sizeOfImage - FixedMul(sizeOfImage, value);
// Draw background
if(border != 0)
{
@ -2040,8 +2136,11 @@ class CommandDrawBar : public SBarInfoCommand
if(sc.CheckToken(TK_Identifier)) //comparing reference
{
inventoryItem = PClass::FindClass(sc.String);
if(inventoryItem == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(inventoryItem)) //must be a kind of inventory
sc.ScriptError("'%s' is not a type of inventory item.", sc.String);
if(inventoryItem == NULL || !RUNTIME_CLASS(AInventory)->IsAncestorOf(inventoryItem)) //must be a kind of inventory
{
sc.ScriptMessage("'%s' is not a type of inventory item.", sc.String);
inventoryItem = RUNTIME_CLASS(AInventory);
}
}
}
else if(sc.Compare("armor"))
@ -2050,8 +2149,11 @@ class CommandDrawBar : public SBarInfoCommand
if(sc.CheckToken(TK_Identifier))
{
inventoryItem = PClass::FindClass(sc.String);
if(inventoryItem == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(inventoryItem)) //must be a kind of inventory
sc.ScriptError("'%s' is not a type of inventory item.", sc.String);
if(inventoryItem == NULL || !RUNTIME_CLASS(AInventory)->IsAncestorOf(inventoryItem)) //must be a kind of inventory
{
sc.ScriptMessage("'%s' is not a type of inventory item.", sc.String);
inventoryItem = RUNTIME_CLASS(AInventory);
}
}
}
else if(sc.Compare("ammo1"))
@ -2065,7 +2167,8 @@ class CommandDrawBar : public SBarInfoCommand
inventoryItem = PClass::FindClass(sc.String);
if(inventoryItem == NULL || !RUNTIME_CLASS(AAmmo)->IsAncestorOf(inventoryItem)) //must be a kind of ammo
{
sc.ScriptError("'%s' is not a type of ammo.", sc.String);
sc.ScriptMessage("'%s' is not a type of ammo.", sc.String);
inventoryItem = RUNTIME_CLASS(AAmmo);
}
}
else if(sc.Compare("frags"))
@ -2083,9 +2186,10 @@ class CommandDrawBar : public SBarInfoCommand
type = POWERUPTIME;
sc.MustGetToken(TK_Identifier);
inventoryItem = PClass::FindClass(sc.String);
if(inventoryItem == NULL || !PClass::FindClass("PowerupGiver")->IsAncestorOf(inventoryItem))
if(inventoryItem == NULL || !RUNTIME_CLASS(APowerupGiver)->IsAncestorOf(inventoryItem))
{
sc.ScriptError("'%s' is not a type of PowerupGiver.", sc.String);
sc.ScriptMessage("'%s' is not a type of PowerupGiver.", sc.String);
inventoryItem = RUNTIME_CLASS(APowerupGiver);
}
}
else
@ -2094,7 +2198,8 @@ class CommandDrawBar : public SBarInfoCommand
inventoryItem = PClass::FindClass(sc.String);
if(inventoryItem == NULL || !RUNTIME_CLASS(AInventory)->IsAncestorOf(inventoryItem))
{
sc.ScriptError("'%s' is not a type of inventory item.", sc.String);
sc.ScriptMessage("'%s' is not a type of inventory item.", sc.String);
inventoryItem = RUNTIME_CLASS(AInventory);
}
}
sc.MustGetToken(',');
@ -2258,17 +2363,13 @@ class CommandDrawBar : public SBarInfoCommand
}
default: return;
}
if(border != 0)
value = max - value; //invert since the new drawing method requires drawing the bg on the fg.
if(max != 0 && value > 0)
{
value = (value << FRACBITS) / max;
if(value > FRACUNIT)
value = FRACUNIT;
}
else if(border != 0 && max == 0 && value <= 0)
value = FRACUNIT;
else
value = 0;
if(interpolationSpeed != 0 && (!hudChanged || level.time == 1))
@ -2343,7 +2444,10 @@ class CommandIsSelected : public SBarInfoCommandFlowControl
{
weapon[i] = PClass::FindClass(sc.String);
if(weapon[i] == NULL || !RUNTIME_CLASS(AWeapon)->IsAncestorOf(weapon[i]))
sc.ScriptError("'%s' is not a type of weapon.", sc.String);
{
sc.ScriptMessage("'%s' is not a type of weapon.", sc.String);
weapon[i] = RUNTIME_CLASS(AWeapon);
}
if(sc.CheckToken(','))
{
@ -2445,7 +2549,10 @@ class CommandHasWeaponPiece : public SBarInfoCommandFlowControl
sc.MustGetToken(TK_Identifier);
weapon = PClass::FindClass(sc.String);
if(weapon == NULL || !RUNTIME_CLASS(AWeapon)->IsAncestorOf(weapon)) //must be a weapon
sc.ScriptError("%s is not a kind of weapon.", sc.String);
{
sc.ScriptMessage("%s is not a kind of weapon.", sc.String);
weapon = RUNTIME_CLASS(AWeapon);
}
sc.MustGetToken(',');
sc.MustGetToken(TK_IntConst);
if(sc.Number < 1)
@ -2634,7 +2741,10 @@ class CommandWeaponAmmo : public SBarInfoCommandFlowControl
{
ammo[i] = PClass::FindClass(sc.String);
if(ammo[i] == NULL || !RUNTIME_CLASS(AAmmo)->IsAncestorOf(ammo[i])) //must be a kind of ammo
sc.ScriptError("'%s' is not a type of ammo.", sc.String);
{
sc.ScriptMessage("'%s' is not a type of ammo.", sc.String);
ammo[i] = RUNTIME_CLASS(AAmmo);
}
if(sc.CheckToken(TK_OrOr))
{
@ -2734,7 +2844,10 @@ class CommandInInventory : public SBarInfoCommandFlowControl
{
item[i] = PClass::FindClass(sc.String);
if(item[i] == NULL || !RUNTIME_CLASS(AInventory)->IsAncestorOf(item[i]))
sc.ScriptError("'%s' is not a type of inventory item.", sc.String);
{
sc.ScriptMessage("'%s' is not a type of inventory item.", sc.String);
item[i] = RUNTIME_CLASS(AInventory);
}
if (sc.CheckToken(','))
{

View file

@ -1504,18 +1504,23 @@ void DBaseStatusBar::BlendView (float blend[4])
if (CPlayer->bonuscount)
{
cnt = CPlayer->bonuscount << 3;
AddBlend (0.8431f, 0.7333f, 0.2706f, cnt > 128 ? 0.5f : cnt / 255.f, blend);
AddBlend (RPART(gameinfo.pickupcolor)/255.f, GPART(gameinfo.pickupcolor)/255.f,
BPART(gameinfo.pickupcolor)/255.f, cnt > 128 ? 0.5f : cnt / 255.f, blend);
}
cnt = DamageToAlpha[MIN (113, CPlayer->damagecount)];
if (cnt)
if (CPlayer->mo->DamageFade.a != 0)
{
if (cnt > 228)
cnt = 228;
cnt = DamageToAlpha[MIN (113, CPlayer->damagecount * CPlayer->mo->DamageFade.a / 255)];
if (cnt)
{
if (cnt > 228)
cnt = 228;
APlayerPawn *mo = players[consoleplayer].mo;
AddBlend (mo->DamageFade.r / 255.f, mo->DamageFade.g / 255.f, mo->DamageFade.b / 255.f, cnt / 255.f, blend);
APlayerPawn *mo = CPlayer->mo;
AddBlend (mo->DamageFade.r / 255.f, mo->DamageFade.g / 255.f, mo->DamageFade.b / 255.f, cnt / 255.f, blend);
}
}
// Unlike Doom, I did not have any utility source to look at to find the

View file

@ -80,6 +80,7 @@ void FMapInfoParser::ParseSkill ()
skill.MonsterHealth = FRACUNIT;
skill.FriendlyHealth = FRACUNIT;
skill.NoPain = false;
skill.ArmorFactor = FRACUNIT;
sc.MustGetString();
skill.Name = sc.String;
@ -249,6 +250,12 @@ void FMapInfoParser::ParseSkill ()
{
skill.NoPain = true;
}
else if (sc.Compare("ArmorFactor"))
{
ParseAssign();
sc.MustGetFloat();
skill.ArmorFactor = FLOAT2FIXED(sc.Float);
}
else if (sc.Compare("DefaultSkill"))
{
if (DefaultSkill >= 0)
@ -357,7 +364,10 @@ int G_SkillProperty(ESkillProperty prop)
return AllSkills[gameskill].FriendlyHealth;
case SKILLP_NoPain:
return AllSkills[gameskill].NoPain;
return AllSkills[gameskill].NoPain;
case SKILLP_ArmorFactor:
return AllSkills[gameskill].ArmorFactor;
}
}
return 0;
@ -435,6 +445,7 @@ FSkillInfo &FSkillInfo::operator=(const FSkillInfo &other)
MonsterHealth = other.MonsterHealth;
FriendlyHealth = other.FriendlyHealth;
NoPain = other.NoPain;
ArmorFactor = other.ArmorFactor;
return *this;
}

View file

@ -399,29 +399,38 @@ void FGameConfigFile::DoGameSetup (const char *gamename)
ReadCVars (0);
}
strncpy (subsection, "Bindings", sublen);
if (!SetSection (section))
{ // Config has no bindings for the given game
if (!bMigrating)
{
C_SetDefaultBindings ();
}
}
else
if (!bMigrating)
{
C_UnbindAll ();
C_SetDefaultBindings ();
}
strncpy (subsection, "Bindings", sublen);
if (SetSection (section))
{
Bindings.UnbindAll();
while (NextInSection (key, value))
{
C_DoBind (key, value, false);
Bindings.DoBind (key, value);
}
}
strncpy (subsection, "DoubleBindings", sublen);
if (SetSection (section))
{
DoubleBindings.UnbindAll();
while (NextInSection (key, value))
{
C_DoBind (key, value, true);
DoubleBindings.DoBind (key, value);
}
}
strncpy (subsection, "AutomapBindings", sublen);
if (SetSection (section))
{
AutomapBindings.UnbindAll();
while (NextInSection (key, value))
{
AutomapBindings.DoBind (key, value);
}
}
@ -512,11 +521,15 @@ void FGameConfigFile::ArchiveGameData (const char *gamename)
strcpy (subsection, "Bindings");
SetSection (section, true);
C_ArchiveBindings (this, false);
Bindings.ArchiveBindings (this);
strncpy (subsection, "DoubleBindings", sublen);
SetSection (section, true);
C_ArchiveBindings (this, true);
DoubleBindings.ArchiveBindings (this);
strncpy (subsection, "AutomapBindings", sublen);
SetSection (section, true);
AutomapBindings.ArchiveBindings (this);
}
void FGameConfigFile::ArchiveGlobalData ()

View file

@ -103,12 +103,11 @@ const char* GameInfoBoarders[] =
do \
{ \
sc.MustGetToken(TK_StringConst); \
if(strlen(sc.String) > length) \
if(length > 0 && strlen(sc.String) > length) \
{ \
sc.ScriptError("Value for '%s' can not be longer than %d characters.", #key, length); \
} \
FName val = sc.String; \
gameinfo.key.Push(val); \
gameinfo.key[gameinfo.key.Reserve(1)] = sc.String; \
} \
while (sc.CheckToken(',')); \
}
@ -266,6 +265,7 @@ void FMapInfoParser::ParseGameInfo()
GAMEINFOKEY_INT(defKickback, "defKickback")
GAMEINFOKEY_CSTRING(SkyFlatName, "SkyFlatName", 8)
GAMEINFOKEY_STRING(translator, "translator")
GAMEINFOKEY_COLOR(pickupcolor, "pickupcolor")
GAMEINFOKEY_COLOR(defaultbloodcolor, "defaultbloodcolor")
GAMEINFOKEY_COLOR(defaultbloodparticlecolor, "defaultbloodparticlecolor")
GAMEINFOKEY_STRING(backpacktype, "backpacktype")
@ -282,6 +282,16 @@ void FMapInfoParser::ParseGameInfo()
GAMEINFOKEY_INT(defaultdropstyle, "defaultdropstyle")
GAMEINFOKEY_CSTRING(Endoom, "endoom", 8)
GAMEINFOKEY_INT(player5start, "player5start")
GAMEINFOKEY_STRINGARRAY(quitmessages, "quitmessages", 0)
GAMEINFOKEY_STRING(mTitleColor, "menufontcolor_title")
GAMEINFOKEY_STRING(mFontColor, "menufontcolor_label")
GAMEINFOKEY_STRING(mFontColorValue, "menufontcolor_value")
GAMEINFOKEY_STRING(mFontColorMore, "menufontcolor_action")
GAMEINFOKEY_STRING(mFontColorHeader, "menufontcolor_header")
GAMEINFOKEY_STRING(mFontColorHighlight, "menufontcolor_highlight")
GAMEINFOKEY_STRING(mFontColorSelection, "menufontcolor_selection")
GAMEINFOKEY_CSTRING(mBackButton, "menubackbutton", 8)
else
{
// ignore unkown keys.

View file

@ -43,7 +43,10 @@
#define GI_MENUHACK_EXTENDED 0x00000004 // (Heretic)
#define GI_TEASER2 0x00000008 // Alternate version of the Strife Teaser
#define GI_COMPATSHORTTEX 0x00000010 // always force COMPAT_SHORTTEX for IWAD maps.
#define GI_COMPATSTAIRS 0x00000010 // same for stairbuilding
#define GI_COMPATSTAIRS 0x00000020 // same for stairbuilding
#define GI_COMPATPOLY1 0x00000040 // Hexen's MAP36 needs old polyobject drawing
#define GI_COMPATPOLY2 0x00000080 // so does HEXDD's MAP47
#define GI_NOTEXTCOLOR 0x00000100
#include "gametype.h"
@ -106,6 +109,16 @@ struct gameinfo_t
int defaultrespawntime;
int defaultdropstyle;
int player5start;
DWORD pickupcolor;
TArray<FString> quitmessages;
FName mTitleColor;
FName mFontColor;
FName mFontColorValue;
FName mFontColorMore;
FName mFontColorHeader;
FName mFontColorHighlight;
FName mFontColorSelection;
char mBackButton[9];
const char *GetFinalePage(unsigned int num) const;
};

146
src/keysections.cpp Normal file
View file

@ -0,0 +1,146 @@
/*
** keysections.cpp
** Custom key bindings
**
**---------------------------------------------------------------------------
** Copyright 1998-2009 Randy Heit
** Copyright 2010 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "menu/menu.h"
#include "g_level.h"
#include "d_player.h"
#include "gi.h"
#include "c_bind.h"
#include "c_dispatch.h"
#include "gameconfigfile.h"
TArray<FKeySection> KeySections;
static void LoadKeys (const char *modname, bool dbl)
{
char section[64];
if (GameNames[gameinfo.gametype] == NULL)
return;
mysnprintf (section, countof(section), "%s.%s%sBindings", GameNames[gameinfo.gametype], modname,
dbl ? ".Double" : ".");
FKeyBindings *bindings = dbl? &DoubleBindings : &Bindings;
if (GameConfig->SetSection (section))
{
const char *key, *value;
while (GameConfig->NextInSection (key, value))
{
bindings->DoBind (key, value);
}
}
}
static void DoSaveKeys (FConfigFile *config, const char *section, FKeySection *keysection, bool dbl)
{
config->SetSection (section, true);
config->ClearCurrentSection ();
FKeyBindings *bindings = dbl? &DoubleBindings : &Bindings;
for (unsigned i = 0; i < keysection->mActions.Size(); ++i)
{
bindings->ArchiveBindings (config, keysection->mActions[i].mAction);
}
}
void M_SaveCustomKeys (FConfigFile *config, char *section, char *subsection, size_t sublen)
{
for (unsigned i=0; i<KeySections.Size(); i++)
{
mysnprintf (subsection, sublen, "%s.Bindings", KeySections[i].mSection);
DoSaveKeys (config, section, &KeySections[i], false);
mysnprintf (subsection, sublen, "%s.DoubleBindings", KeySections[i].mSection);
DoSaveKeys (config, section, &KeySections[i], true);
}
}
static int CurrentKeySection = -1;
CCMD (addkeysection)
{
if (ParsingKeyConf)
{
if (argv.argc() != 3)
{
Printf ("Usage: addkeysection <menu section name> <ini name>\n");
return;
}
// Limit the ini name to 32 chars
if (strlen (argv[2]) > 32)
argv[2][32] = 0;
for (unsigned i = 0; i < KeySections.Size(); i++)
{
if (KeySections[i].mTitle.CompareNoCase(argv[2] == 0))
{
CurrentKeySection = i;
return;
}
}
CurrentKeySection = KeySections.Reserve(1);
KeySections[CurrentKeySection].mTitle = argv[1];
KeySections[CurrentKeySection].mSection = argv[2];
// Load bindings for this section from the ini
LoadKeys (argv[2], 0);
LoadKeys (argv[2], 1);
}
}
CCMD (addmenukey)
{
if (ParsingKeyConf)
{
if (argv.argc() != 3)
{
Printf ("Usage: addmenukey <description> <command>\n");
return;
}
if (CurrentKeySection == -1 || CurrentKeySection >= (int)KeySections.Size())
{
Printf ("You must use addkeysection first.\n");
return;
}
FKeySection *sect = &KeySections[CurrentKeySection];
FKeyAction *act = &sect->mActions[sect->mActions.Reserve(1)];
act->mTitle = argv[1];
act->mAction = argv[2];
}
}

View file

@ -91,8 +91,3 @@ int FBoundingBox::BoxOnLineSide (const line_t *ld) const
return (p1 == p2) ? p1 : -1;
}

View file

@ -25,6 +25,7 @@
#include "doomtype.h"
struct line_t;
struct node_t;
class FBoundingBox
{

View file

@ -754,8 +754,8 @@ void cht_Give (player_t *player, const char *name, int amount)
// Don't give replaced weapons unless the replacement was done by Dehacked.
if (type != RUNTIME_CLASS(AWeapon) &&
type->IsDescendantOf (RUNTIME_CLASS(AWeapon)) &&
(type->ActorInfo->GetReplacement() == type->ActorInfo ||
type->ActorInfo->GetReplacement()->Class->IsDescendantOf(RUNTIME_CLASS(ADehackedPickup))))
(type->GetReplacement() == type ||
type->GetReplacement()->IsDescendantOf(RUNTIME_CLASS(ADehackedPickup))))
{
// Give the weapon only if it belongs to the current game or

View file

@ -60,5 +60,6 @@ double Joy_RemoveDeadZone(double axisval, double deadzone, BYTE *buttons);
void I_GetAxes(float axes[NUM_JOYAXIS]);
void I_GetJoysticks(TArray<IJoystickConfig *> &sticks);
IJoystickConfig *I_UpdateDeviceList();
extern void UpdateJoystickMenu(IJoystickConfig *);
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,279 +0,0 @@
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id:$
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This source is available for distribution and/or modification
// only under the terms of the DOOM Source Code License as
// published by id Software. All rights reserved.
//
// The source is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
// for more details.
//
// DESCRIPTION:
// Menu widget stuff, episode selection and such.
//
//-----------------------------------------------------------------------------
#ifndef __M_MENU_H__
#define __M_MENU_H__
#include "c_cvars.h"
struct event_t;
struct menu_t;
//
// MENUS
//
// Called by main loop,
// saves config file and calls I_Quit when user exits.
// Even when the menu is not displayed,
// this can resize the view and change game parameters.
// Does all the real work of the menu interaction.
bool M_Responder (event_t *ev);
// Called by main loop,
// only used for menu (skull cursor) animation.
void M_Ticker (void);
// Called by main loop,
// draws the menus directly into the screen buffer.
void M_Drawer (void);
// Called by D_DoomMain, loads the config file.
void M_Init (void);
void M_Deinit ();
// Called by intro code to force menu up upon a keypress,
// does nothing if menu is already up.
void M_StartControlPanel (bool makeSound, bool wantTop=false);
// Turns off the menu
void M_ClearMenus ();
// [RH] Setup options menu
bool M_StartOptionsMenu (void);
// [RH] Handle keys for options menu
void M_OptResponder (event_t *ev);
// [RH] Draw options menu
void M_OptDrawer (void);
// [RH] Initialize options menu
void M_OptInit (void);
// [RH] Initialize the video modes menu
void M_InitVideoModesMenu (void);
void M_SwitchMenu (struct menu_t *menu);
void M_PopMenuStack (void);
// [RH] Called whenever the display mode changes
void M_RefreshModesList ();
void M_ActivateMenuInput ();
void M_DeactivateMenuInput ();
void M_NotifyNewSave (const char *file, const char *title, bool okForQuicksave);
//
// MENU TYPEDEFS
//
typedef enum {
whitetext,
redtext,
more,
rightmore,
safemore,
rsafemore,
joymore,
slider,
absslider,
inverter,
discrete,
discretes,
cdiscrete,
ediscrete,
control,
screenres,
bitflag,
bitmask,
listelement,
nochoice,
numberedmore,
colorpicker,
intslider,
palettegrid,
joy_sens,
joy_slider,
joy_map,
joy_inverter,
} itemtype;
struct IJoystickConfig;
void UpdateJoystickMenu(IJoystickConfig *selected);
// Yeargh! It's a monster!
struct menuitem_t
{
itemtype type;
const char *label;
union {
FBaseCVar *cvar;
FIntCVar *intcvar;
FGUIDCVar *guidcvar;
FColorCVar *colorcvar;
int selmode;
float fval;
int joyselection;
} a;
union {
float min; /* aka numvalues aka invflag */
float numvalues;
float invflag;
int key1;
char *res1;
int position;
} b;
union {
float max;
int key2;
char *res2;
void *extra;
float discretecenter; // 1 to center or 2 to disable repeat (do I even use centered discretes?)
} c;
union {
float step;
char *res3;
FBoolCVar *graycheck; // for drawing discrete items
} d;
union {
struct value_t *values;
struct valuestring_t *valuestrings;
struct valueenum_t *enumvalues;
char *command;
void (*cfunc)(FBaseCVar *cvar, float newval);
void (*mfunc)(void);
void (*lfunc)(int);
int highlight;
int flagmask;
int joyslidernum;
} e;
};
struct menu_t {
const char *texttitle;
int lastOn;
int numitems;
int indent;
menuitem_t *items;
int scrolltop;
int scrollpos;
int y;
bool (*PreDraw)(void);
bool DontDim;
void (*EscapeHandler)(void);
};
struct value_t {
float value;
const char *name;
};
struct valuestring_t {
float value;
FString name;
};
struct valueenum_t {
const char *value; // Value of cvar
const char *name; // Name on menu
};
struct oldmenuitem_t
{
// -1 = no cursor here, 1 = ok, 2 = arrows ok
SBYTE status;
BYTE fulltext; // [RH] Menu name is text, not a graphic
// hotkey in menu
char alphaKey;
const char *name;
// choice = menu item #.
// if status = 2,
// choice=0:leftarrow,1:rightarrow
void (*routine)(int choice);
int textcolor;
};
struct oldmenu_t
{
short numitems; // # of menu items
oldmenuitem_t *menuitems; // menu items
void (*routine)(void); // draw routine
short x;
short y; // x,y of menu
short lastOn; // last item user was on in menu
};
struct menustack_t
{
union {
menu_t *newmenu;
oldmenu_t *old;
} menu;
bool isNewStyle;
bool drawSkull;
};
enum EMenuKey
{
MKEY_Up,
MKEY_Down,
MKEY_Left,
MKEY_Right,
MKEY_PageUp,
MKEY_PageDown,
//----------------- Keys past here do not repeat.
MKEY_Enter,
MKEY_Back, // Back to previous menu
MKEY_Clear, // Clear keybinding/flip player sprite preview
NUM_MKEYS
};
void M_ButtonHandler(EMenuKey key, bool repeat);
void M_OptButtonHandler(EMenuKey key, bool repeat);
void M_DrawConText (int color, int x, int y, const char *str);
extern value_t YesNo[2];
extern value_t NoYes[2];
extern value_t OnOff[2];
extern menustack_t MenuStack[16];
extern int MenuStackDepth;
extern bool OptionsActive;
extern int skullAnimCounter;
extern menu_t *CurrentMenu;
extern int CurrentItem;
#define MAX_EPISODES 8
extern oldmenuitem_t EpisodeMenu[MAX_EPISODES];
extern bool EpisodeNoSkill[MAX_EPISODES];
extern char EpisodeMaps[MAX_EPISODES][9];
extern oldmenu_t EpiDef;
#endif

View file

@ -85,8 +85,6 @@ CVAR(String, screenshot_type, "png", CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
CVAR(String, screenshot_dir, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
EXTERN_CVAR(Bool, longsavemessages);
extern void FreeKeySections();
static long ParseCommandLine (const char *args, int *argc, char **argv);
//
@ -167,7 +165,7 @@ void M_FindResponseFile (void)
int argc = 0;
FILE *handle;
int size;
long argsize;
long argsize = 0;
int index;
// Any more response files after the limit will be removed from the
@ -420,7 +418,6 @@ void M_LoadDefaults ()
{
GameConfig = new FGameConfigFile;
GameConfig->DoGlobalSetup ();
atterm (FreeKeySections);
atterm (M_SaveDefaultsFinal);
}

View file

@ -43,6 +43,7 @@ void M_LoadDefaults ();
bool M_SaveDefaults (const char *filename);
void M_SaveCustomKeys (FConfigFile *config, char *section, char *subsection, size_t sublen);
// Prepends ~/.zdoom to path
FString GetUserFile (const char *path);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,357 @@
/*
** colorpickermenu.cpp
** The color picker menu
**
**---------------------------------------------------------------------------
** Copyright 2010 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include <float.h>
#include "menu/menu.h"
#include "c_dispatch.h"
#include "w_wad.h"
#include "sc_man.h"
#include "v_font.h"
#include "g_level.h"
#include "d_player.h"
#include "v_video.h"
#include "gi.h"
#include "i_system.h"
#include "c_bind.h"
#include "v_palette.h"
#include "d_event.h"
#include "d_gui.h"
#define NO_IMP
#include "menu/optionmenuitems.h"
class DColorPickerMenu : public DOptionMenu
{
DECLARE_CLASS(DColorPickerMenu, DOptionMenu)
float mRed;
float mGreen;
float mBlue;
int mGridPosX;
int mGridPosY;
int mStartItem;
FColorCVar *mCVar;
public:
DColorPickerMenu(DMenu *parent, const char *name, FOptionMenuDescriptor *desc, FColorCVar *cvar)
{
mStartItem = desc->mItems.Size();
mRed = (float)RPART(DWORD(*cvar));
mGreen = (float)GPART(DWORD(*cvar));
mBlue = (float)BPART(DWORD(*cvar));
mGridPosX = 0;
mGridPosY = 0;
mCVar = cvar;
// This menu uses some featurs that are hard to implement in an external control lump
// so it creates its own list of menu items.
desc->mItems.Resize(mStartItem+8);
desc->mItems[mStartItem+0] = new FOptionMenuItemStaticText(name, false);
desc->mItems[mStartItem+1] = new FOptionMenuItemStaticText(" ", false);
desc->mItems[mStartItem+2] = new FOptionMenuSliderVar("Red", &mRed, 0, 255, 15, 0);
desc->mItems[mStartItem+3] = new FOptionMenuSliderVar("Green", &mGreen, 0, 255, 15, 0);
desc->mItems[mStartItem+4] = new FOptionMenuSliderVar("Blue", &mBlue, 0, 255, 15, 0);
desc->mItems[mStartItem+5] = new FOptionMenuItemStaticText(" ", false);
desc->mItems[mStartItem+6] = new FOptionMenuItemCommand("Undo changes", "undocolorpic");
desc->mItems[mStartItem+7] = new FOptionMenuItemStaticText(" ", false);
desc->mSelectedItem = mStartItem + 2;
Init(parent, desc);
desc->mIndent = 0;
desc->CalcIndent();
}
void Destroy()
{
if (mStartItem >= 0)
{
for(unsigned i=0;i<8;i++)
{
delete mDesc->mItems[mStartItem+i];
mDesc->mItems.Resize(mStartItem);
}
UCVarValue val;
val.Int = MAKERGB(int(mRed), int(mGreen), int(mBlue));
if (mCVar != NULL) mCVar->SetGenericRep (val, CVAR_Int);
mStartItem = -1;
}
}
void Reset()
{
mRed = (float)RPART(DWORD(*mCVar));
mGreen = (float)GPART(DWORD(*mCVar));
mBlue = (float)BPART(DWORD(*mCVar));
}
//=============================================================================
//
//
//
//=============================================================================
bool MenuEvent (int mkey, bool fromcontroller)
{
int &mSelectedItem = mDesc->mSelectedItem;
switch (mkey)
{
case MKEY_Down:
if (mSelectedItem == mStartItem+6) // last valid item
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
mGridPosY = 0;
// let it point to the last static item so that the super class code still has a valid item
mSelectedItem = mStartItem+7;
return true;
}
else if (mSelectedItem == mStartItem+7)
{
if (mGridPosY < 15)
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
mGridPosY++;
}
return true;
}
break;
case MKEY_Up:
if (mSelectedItem == mStartItem+7)
{
if (mGridPosY > 0)
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
mGridPosY--;
}
else
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
mSelectedItem = mStartItem+6;
}
return true;
}
break;
case MKEY_Left:
if (mSelectedItem == mStartItem+7)
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
if (--mGridPosX < 0) mGridPosX = 15;
return true;
}
break;
case MKEY_Right:
if (mSelectedItem == mStartItem+7)
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
if (++mGridPosX > 15) mGridPosX = 0;
return true;
}
break;
case MKEY_Enter:
if (mSelectedItem == mStartItem+7)
{
// Choose selected palette entry
int index = mGridPosX + mGridPosY * 16;
mRed = GPalette.BaseColors[index].r;
mGreen = GPalette.BaseColors[index].g;
mBlue = GPalette.BaseColors[index].b;
S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
return true;
}
break;
}
if (mSelectedItem >= 0 && mSelectedItem < mStartItem+7)
{
if (mDesc->mItems[mDesc->mSelectedItem]->MenuEvent(mkey, fromcontroller)) return true;
}
return Super::MenuEvent(mkey, fromcontroller);
}
//=============================================================================
//
//
//
//=============================================================================
bool MouseEvent(int type, int mx, int my)
{
int olditem = mDesc->mSelectedItem;
bool res = Super::MouseEvent(type, mx, my);
if (mDesc->mSelectedItem == -1 || mDesc->mSelectedItem == mStartItem+7)
{
int y = (-mDesc->mPosition + BigFont->GetHeight() + mDesc->mItems.Size() * OptionSettings.mLinespacing) * CleanYfac_1;
int h = (screen->GetHeight() - y) / 16;
int fh = OptionSettings.mLinespacing * CleanYfac_1;
int w = fh;
int yy = y + 2 * CleanYfac_1;
int indent = (screen->GetWidth() / 2);
if (h > fh) h = fh;
else if (h < 4) return res; // no space to draw it.
int box_y = y - 2 * CleanYfac_1;
int box_x = indent - 16*w;
if (mx >= box_x && mx < box_x + 16*w && my >= box_y && my < box_y + 16*h)
{
int cell_x = (mx - box_x) / w;
int cell_y = (my - box_y) / h;
if (olditem != mStartItem+7 || cell_x != mGridPosX || cell_y != mGridPosY)
{
mGridPosX = cell_x;
mGridPosY = cell_y;
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
}
mDesc->mSelectedItem = mStartItem+7;
if (type == MOUSE_Release)
{
MenuEvent(MKEY_Enter, true);
if (m_use_mouse == 2) mDesc->mSelectedItem = -1;
}
res = true;
}
}
return res;
}
//=============================================================================
//
//
//
//=============================================================================
void Drawer()
{
Super::Drawer();
if (mCVar == NULL) return;
int y = (-mDesc->mPosition + BigFont->GetHeight() + mDesc->mItems.Size() * OptionSettings.mLinespacing) * CleanYfac_1;
int h = (screen->GetHeight() - y) / 16;
int fh = OptionSettings.mLinespacing * CleanYfac_1;
int w = fh;
int yy = y;
if (h > fh) h = fh;
else if (h < 4) return; // no space to draw it.
int indent = (screen->GetWidth() / 2);
int p = 0;
for(int i = 0; i < 16; i++, y += h)
{
int box_x, box_y;
int x1;
box_y = y - 2 * CleanYfac_1;
box_x = indent - 16*w;
for (x1 = 0; x1 < 16; ++x1, p++)
{
screen->Clear (box_x, box_y, box_x + w, box_y + h, p, 0);
if ((mDesc->mSelectedItem == mStartItem+7) &&
(/*p == CurrColorIndex ||*/ (i == mGridPosY && x1 == mGridPosX)))
{
int r, g, b;
DWORD col;
double blinky;
if (i == mGridPosY && x1 == mGridPosX)
{
r = 255, g = 128, b = 0;
}
else
{
r = 200, g = 200, b = 255;
}
// Make sure the cursors stand out against similar colors
// by pulsing them.
blinky = fabs(sin(I_MSTime()/1000.0)) * 0.5 + 0.5;
col = MAKEARGB(255,int(r*blinky),int(g*blinky),int(b*blinky));
screen->Clear (box_x, box_y, box_x + w, box_y + 1, -1, col);
screen->Clear (box_x, box_y + h-1, box_x + w, box_y + h, -1, col);
screen->Clear (box_x, box_y, box_x + 1, box_y + h, -1, col);
screen->Clear (box_x + w - 1, box_y, box_x + w, box_y + h, -1, col);
}
box_x += w;
}
}
y = yy;
DWORD newColor = MAKEARGB(255, int(mRed), int(mGreen), int(mBlue));
DWORD oldColor = DWORD(*mCVar) | 0xFF000000;
int x = screen->GetWidth()*2/3;
screen->Clear (x, y, x + 48*CleanXfac_1, y + 48*CleanYfac_1, -1, oldColor);
screen->Clear (x + 48*CleanXfac_1, y, x + 48*2*CleanXfac_1, y + 48*CleanYfac_1, -1, newColor);
y += 49*CleanYfac_1;
screen->DrawText (SmallFont, CR_GRAY, x+(24-SmallFont->StringWidth("Old")/2)*CleanXfac_1, y,
"Old", DTA_CleanNoMove_1, true, TAG_DONE);
screen->DrawText (SmallFont, CR_WHITE, x+(48+24-SmallFont->StringWidth("New")/2)*CleanXfac_1, y,
"New", DTA_CleanNoMove_1, true, TAG_DONE);
}
};
IMPLEMENT_ABSTRACT_CLASS(DColorPickerMenu)
CCMD(undocolorpic)
{
if (DMenu::CurrentMenu != NULL && DMenu::CurrentMenu->IsKindOf(RUNTIME_CLASS(DColorPickerMenu)))
{
static_cast<DColorPickerMenu*>(DMenu::CurrentMenu)->Reset();
}
}
DMenu *StartPickerMenu(DMenu *parent, const char *name, FColorCVar *cvar)
{
FMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_Colorpickermenu);
if (desc != NULL && (*desc)->mType == MDESC_OptionsMenu)
{
return new DColorPickerMenu(parent, name, (FOptionMenuDescriptor*)(*desc), cvar);
}
else
{
return NULL;
}
}

420
src/menu/joystickmenu.cpp Normal file
View file

@ -0,0 +1,420 @@
/*
** joystickmenu.cpp
** The joystick configuration menus
**
**---------------------------------------------------------------------------
** Copyright 2010 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include <float.h>
#include "menu/menu.h"
#include "c_dispatch.h"
#include "w_wad.h"
#include "sc_man.h"
#include "v_font.h"
#include "g_level.h"
#include "d_player.h"
#include "v_video.h"
#include "gi.h"
#include "i_system.h"
#include "c_bind.h"
#include "v_palette.h"
#include "d_event.h"
#include "d_gui.h"
#include "i_music.h"
#include "m_joy.h"
#define NO_IMP
#include "optionmenuitems.h"
static TArray<IJoystickConfig *> Joysticks;
IJoystickConfig *SELECTED_JOYSTICK;
FOptionMenuDescriptor *UpdateJoystickConfigMenu(IJoystickConfig *joy);
//=============================================================================
//
//
//
//=============================================================================
class FOptionMenuSliderJoySensitivity : public FOptionMenuSliderBase
{
public:
FOptionMenuSliderJoySensitivity(const char *label, double min, double max, double step, int showval)
: FOptionMenuSliderBase(label, min, max, step, showval)
{
}
double GetValue()
{
return SELECTED_JOYSTICK->GetSensitivity();
}
void SetValue(double val)
{
SELECTED_JOYSTICK->SetSensitivity(float(val));
}
};
//=============================================================================
//
//
//
//=============================================================================
class FOptionMenuSliderJoyScale : public FOptionMenuSliderBase
{
int mAxis;
int mNeg;
public:
FOptionMenuSliderJoyScale(const char *label, int axis, double min, double max, double step, int showval)
: FOptionMenuSliderBase(label, min, max, step, showval)
{
mAxis = axis;
mNeg = 1;
}
double GetValue()
{
double d = SELECTED_JOYSTICK->GetAxisScale(mAxis);
mNeg = d < 0? -1:1;
return d;
}
void SetValue(double val)
{
SELECTED_JOYSTICK->SetAxisScale(mAxis, float(val * mNeg));
}
};
//=============================================================================
//
//
//
//=============================================================================
class FOptionMenuSliderJoyDeadZone : public FOptionMenuSliderBase
{
int mAxis;
int mNeg;
public:
FOptionMenuSliderJoyDeadZone(const char *label, int axis, double min, double max, double step, int showval)
: FOptionMenuSliderBase(label, min, max, step, showval)
{
mAxis = axis;
mNeg = 1;
}
double GetValue()
{
double d = SELECTED_JOYSTICK->GetAxisDeadZone(mAxis);
mNeg = d < 0? -1:1;
return d;
}
void SetValue(double val)
{
SELECTED_JOYSTICK->SetAxisDeadZone(mAxis, float(val * mNeg));
}
};
//=============================================================================
//
//
//
//=============================================================================
class FOptionMenuItemJoyMap : public FOptionMenuItemOptionBase
{
int mAxis;
public:
FOptionMenuItemJoyMap(const char *label, int axis, const char *values, int center)
: FOptionMenuItemOptionBase(label, "none", values, NULL, center)
{
mAxis = axis;
}
int GetSelection()
{
float f = (float)(int)SELECTED_JOYSTICK->GetAxisMap(mAxis);
for(unsigned i=0;i<mValues->mValues.Size(); i++)
{
if (fabs(f - mValues->mValues[i].Value) < FLT_EPSILON)
{
return i;
}
}
return -1;
}
void SetSelection(int Selection)
{
SELECTED_JOYSTICK->SetAxisMap(mAxis, (EJoyAxis)Selection);
}
};
//=============================================================================
//
//
//
//=============================================================================
class FOptionMenuItemInverter : public FOptionMenuItemOptionBase
{
int mAxis;
public:
FOptionMenuItemInverter(const char *label, int axis, int center)
: FOptionMenuItemOptionBase(label, "none", "YesNo", NULL, center)
{
mAxis = axis;
}
int GetSelection()
{
float f = SELECTED_JOYSTICK->GetAxisScale(mAxis);
return f > 0? 0:1;
}
void SetSelection(int Selection)
{
float f = fabs(SELECTED_JOYSTICK->GetAxisScale(mAxis));
if (Selection) f*=-1;
SELECTED_JOYSTICK->SetAxisScale(mAxis, f);
}
};
class DJoystickConfigMenu : public DOptionMenu
{
DECLARE_CLASS(DJoystickConfigMenu, DOptionMenu)
};
IMPLEMENT_CLASS(DJoystickConfigMenu)
//=============================================================================
//
// Executes a CCMD, action is a CCMD name
//
//=============================================================================
class FOptionMenuItemJoyConfigMenu : public FOptionMenuItemSubmenu
{
IJoystickConfig *mJoy;
public:
FOptionMenuItemJoyConfigMenu(const char *label, IJoystickConfig *joy)
: FOptionMenuItemSubmenu(label, "JoystickConfigMenu")
{
mJoy = joy;
}
bool Activate()
{
UpdateJoystickConfigMenu(mJoy);
return FOptionMenuItemSubmenu::Activate();
}
};
/*=======================================
*
* Joystick Menu
*
*=======================================*/
FOptionMenuDescriptor *UpdateJoystickConfigMenu(IJoystickConfig *joy)
{
FMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_JoystickConfigMenu);
if (desc != NULL && (*desc)->mType == MDESC_OptionsMenu)
{
FOptionMenuDescriptor *opt = (FOptionMenuDescriptor *)*desc;
FOptionMenuItem *it;
for(unsigned i=0;i<opt->mItems.Size();i++)
{
delete opt->mItems[i];
opt->mItems.Clear();
}
opt->mTitle.Format("Configure %s", joy->GetName().GetChars());
if (joy == NULL)
{
it = new FOptionMenuItemStaticText("Invalid controller specified for menu", false);
opt->mItems.Push(it);
}
else
{
SELECTED_JOYSTICK = joy;
it = new FOptionMenuSliderJoySensitivity("Overall sensitivity", 0, 2, 0.1, 3);
opt->mItems.Push(it);
it = new FOptionMenuItemStaticText(" ", false);
opt->mItems.Push(it);
if (joy->GetNumAxes() > 0)
{
it = new FOptionMenuItemStaticText("Axis Configuration", true);
opt->mItems.Push(it);
for (int i = 0; i < joy->GetNumAxes(); ++i)
{
it = new FOptionMenuItemStaticText(" ", false);
opt->mItems.Push(it);
it = new FOptionMenuItemJoyMap(joy->GetAxisName(i), i, "JoyAxisMapNames", false);
opt->mItems.Push(it);
it = new FOptionMenuSliderJoyScale("Overall sensitivity", i, 0, 4, 0.1, 3);
opt->mItems.Push(it);
it = new FOptionMenuItemInverter("Invert", i, false);
opt->mItems.Push(it);
it = new FOptionMenuSliderJoyDeadZone("Dead Zone", i, 0, 0.9, 0.05, 3);
opt->mItems.Push(it);
}
}
else
{
it = new FOptionMenuItemStaticText("No configurable axes", false);
opt->mItems.Push(it);
}
}
opt->mScrollPos = 0;
opt->mSelectedItem = -1;
opt->mIndent = 0;
opt->mPosition = -25;
opt->CalcIndent();
return opt;
}
return NULL;
}
void UpdateJoystickMenu(IJoystickConfig *selected)
{
FMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_JoystickOptions);
if (desc != NULL && (*desc)->mType == MDESC_OptionsMenu)
{
FOptionMenuDescriptor *opt = (FOptionMenuDescriptor *)*desc;
FOptionMenuItem *it;
for(unsigned i=0;i<opt->mItems.Size();i++)
{
delete opt->mItems[i];
opt->mItems.Clear();
}
int i;
int itemnum = -1;
I_GetJoysticks(Joysticks);
if ((unsigned)itemnum >= Joysticks.Size())
{
itemnum = Joysticks.Size() - 1;
}
if (selected != NULL)
{
for (i = 0; (unsigned)i < Joysticks.Size(); ++i)
{
if (Joysticks[i] == selected)
{
itemnum = i;
break;
}
}
}
// Todo: Block joystick for changing this one.
it = new FOptionMenuItemOption("Enable controller support", "use_joystick", "YesNo", NULL, false);
opt->mItems.Push(it);
#ifdef _WIN32
it = new FOptionMenuItemOption("Enable DirectInput controllers", "joy_dinput", "YesNo", NULL, false);
opt->mItems.Push(it);
it = new FOptionMenuItemOption("Enable XInput controllers", "joy_xinput", "YesNo", NULL, false);
opt->mItems.Push(it);
it = new FOptionMenuItemOption("Enable raw PlayStation 2 adapters", "joy_ps2raw", "YesNo", NULL, false);
opt->mItems.Push(it);
#endif
it = new FOptionMenuItemStaticText(" ", false);
opt->mItems.Push(it);
if (Joysticks.Size() == 0)
{
it = new FOptionMenuItemStaticText("No controllers detected", false);
opt->mItems.Push(it);
if (!use_joystick)
{
it = new FOptionMenuItemStaticText("Controller support must be", false);
opt->mItems.Push(it);
it = new FOptionMenuItemStaticText("enabled to detect any", false);
opt->mItems.Push(it);
}
}
else
{
it = new FOptionMenuItemStaticText("Configure controllers:", false);
opt->mItems.Push(it);
for (int i = 0; i < (int)Joysticks.Size(); ++i)
{
it = new FOptionMenuItemJoyConfigMenu(Joysticks[i]->GetName(), Joysticks[i]);
opt->mItems.Push(it);
if (i == itemnum) opt->mSelectedItem = opt->mItems.Size();
}
}
if (opt->mSelectedItem >= (int)opt->mItems.Size())
{
opt->mSelectedItem = opt->mItems.Size() - 1;
}
opt->CalcIndent();
// If the joystick config menu is open, close it if the device it's
// open for is gone.
for (i = 0; (unsigned)i < Joysticks.Size(); ++i)
{
if (Joysticks[i] == SELECTED_JOYSTICK)
{
break;
}
}
if (i == (int)Joysticks.Size())
{
SELECTED_JOYSTICK = NULL;
if (DMenu::CurrentMenu != NULL && DMenu::CurrentMenu->IsKindOf(RUNTIME_CLASS(DJoystickConfigMenu)))
{
DMenu::CurrentMenu->Close();
}
}
}
}

511
src/menu/listmenu.cpp Normal file
View file

@ -0,0 +1,511 @@
/*
** listmenu.cpp
** A simple menu consisting of a list of items
**
**---------------------------------------------------------------------------
** Copyright 2010 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "v_video.h"
#include "v_font.h"
#include "cmdlib.h"
#include "gstrings.h"
#include "g_level.h"
#include "gi.h"
#include "d_gui.h"
#include "d_event.h"
#include "menu/menu.h"
IMPLEMENT_CLASS(DListMenu)
//=============================================================================
//
//
//
//=============================================================================
DListMenu::DListMenu(DMenu *parent, FListMenuDescriptor *desc)
: DMenu(parent)
{
mDesc = desc;
mFocusControl = NULL;
}
//=============================================================================
//
//
//
//=============================================================================
void DListMenu::Init(DMenu *parent, FListMenuDescriptor *desc)
{
mParentMenu = parent;
GC::WriteBarrier(this, parent);
mDesc = desc;
}
//=============================================================================
//
//
//
//=============================================================================
FListMenuItem *DListMenu::GetItem(FName name)
{
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
FName nm = mDesc->mItems[i]->GetAction(NULL);
if (nm == name) return mDesc->mItems[i];
}
return NULL;
}
//=============================================================================
//
//
//
//=============================================================================
bool DListMenu::Responder (event_t *ev)
{
if (ev->type == EV_GUI_Event)
{
if (ev->subtype == EV_GUI_KeyDown)
{
int ch = tolower (ev->data1);
for(unsigned i = mDesc->mSelectedItem + 1; i < mDesc->mItems.Size(); i++)
{
if (mDesc->mItems[i]->CheckHotkey(ch))
{
mDesc->mSelectedItem = i;
S_Sound(CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
return true;
}
}
for(int i = 0; i < mDesc->mSelectedItem; i++)
{
if (mDesc->mItems[i]->CheckHotkey(ch))
{
mDesc->mSelectedItem = i;
S_Sound(CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
return true;
}
}
}
}
return Super::Responder(ev);
}
//=============================================================================
//
//
//
//=============================================================================
bool DListMenu::MenuEvent (int mkey, bool fromcontroller)
{
int startedAt = mDesc->mSelectedItem;
switch (mkey)
{
case MKEY_Up:
do
{
if (--mDesc->mSelectedItem < 0) mDesc->mSelectedItem = mDesc->mItems.Size()-1;
}
while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable() && mDesc->mSelectedItem != startedAt);
S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
return true;
case MKEY_Down:
do
{
if (++mDesc->mSelectedItem >= (int)mDesc->mItems.Size()) mDesc->mSelectedItem = 0;
}
while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable() && mDesc->mSelectedItem != startedAt);
S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
return true;
case MKEY_Enter:
if (mDesc->mSelectedItem >= 0 && mDesc->mItems[mDesc->mSelectedItem]->Activate())
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
}
return true;
default:
return Super::MenuEvent(mkey, fromcontroller);
}
}
//=============================================================================
//
//
//
//=============================================================================
bool DListMenu::MouseEvent(int type, int x, int y)
{
int sel = -1;
// convert x/y from screen to virtual coordinates, according to CleanX/Yfac use in DrawTexture
x = ((x - (screen->GetWidth() / 2)) / CleanXfac) + 160;
y = ((y - (screen->GetHeight() / 2)) / CleanYfac) + 100;
if (mFocusControl != NULL)
{
mFocusControl->MouseEvent(type, x, y);
return true;
}
else
{
if ((mDesc->mWLeft <= 0 || x > mDesc->mWLeft) &&
(mDesc->mWRight <= 0 || x < mDesc->mWRight))
{
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
if (mDesc->mItems[i]->CheckCoordinate(x, y))
{
if (i != mDesc->mSelectedItem)
{
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
}
mDesc->mSelectedItem = i;
mDesc->mItems[i]->MouseEvent(type, x, y);
return true;
}
}
}
}
mDesc->mSelectedItem = -1;
return Super::MouseEvent(type, x, y);
}
//=============================================================================
//
//
//
//=============================================================================
void DListMenu::Ticker ()
{
Super::Ticker();
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
mDesc->mItems[i]->Ticker();
}
}
//=============================================================================
//
//
//
//=============================================================================
void DListMenu::Drawer ()
{
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
if (mDesc->mItems[i]->mEnabled) mDesc->mItems[i]->Drawer(mDesc->mSelectedItem == i);
}
if (mDesc->mSelectedItem >= 0 && mDesc->mSelectedItem < (int)mDesc->mItems.Size())
mDesc->mItems[mDesc->mSelectedItem]->DrawSelector(mDesc->mSelectOfsX, mDesc->mSelectOfsY, mDesc->mSelector);
Super::Drawer();
}
//=============================================================================
//
// base class for menu items
//
//=============================================================================
FListMenuItem::~FListMenuItem()
{
}
bool FListMenuItem::CheckCoordinate(int x, int y)
{
return false;
}
void FListMenuItem::Ticker()
{
}
void FListMenuItem::Drawer(bool selected)
{
}
bool FListMenuItem::Selectable()
{
return false;
}
void FListMenuItem::DrawSelector(int xofs, int yofs, FTextureID tex)
{
if (tex.isNull())
{
if ((DMenu::MenuTime%8) < 6)
{
screen->DrawText(ConFont, OptionSettings.mFontColorSelection,
mXpos + xofs, mYpos + yofs, "\xd", DTA_Clean, true, TAG_DONE);
}
}
else
{
screen->DrawTexture (TexMan(tex), mXpos + xofs, mYpos + yofs, DTA_Clean, true, TAG_DONE);
}
}
bool FListMenuItem::Activate()
{
return false; // cannot be activated
}
FName FListMenuItem::GetAction(int *pparam)
{
return mAction;
}
bool FListMenuItem::SetString(int i, const char *s)
{
return false;
}
bool FListMenuItem::GetString(int i, char *s, int len)
{
return false;
}
bool FListMenuItem::SetValue(int i, int value)
{
return false;
}
bool FListMenuItem::GetValue(int i, int *pvalue)
{
return false;
}
void FListMenuItem::Enable(bool on)
{
mEnabled = on;
}
bool FListMenuItem::MenuEvent(int mkey, bool fromcontroller)
{
return false;
}
bool FListMenuItem::MouseEvent(int type, int x, int y)
{
return false;
}
bool FListMenuItem::CheckHotkey(int c)
{
return false;
}
//=============================================================================
//
// static patch
//
//=============================================================================
FListMenuItemStaticPatch::FListMenuItemStaticPatch(int x, int y, FTextureID patch, bool centered)
: FListMenuItem(x, y)
{
mTexture = patch;
mCentered = centered;
}
void FListMenuItemStaticPatch::Drawer(bool selected)
{
int x = mXpos;
FTexture *tex = TexMan(mTexture);
if (mYpos >= 0)
{
if (mCentered) x -= tex->GetScaledWidth()/2;
screen->DrawTexture (tex, x, mYpos, DTA_Clean, true, TAG_DONE);
}
else
{
int x = (mXpos - 160) * CleanXfac + (SCREENWIDTH>>1);
if (mCentered) x -= (tex->GetScaledWidth()*CleanXfac)/2;
screen->DrawTexture (tex, x, -mYpos*CleanYfac, DTA_CleanNoMove, true, TAG_DONE);
}
}
//=============================================================================
//
// static text
//
//=============================================================================
FListMenuItemStaticText::FListMenuItemStaticText(int x, int y, const char *text, FFont *font, EColorRange color, bool centered)
: FListMenuItem(x, y)
{
mText = ncopystring(text);
mFont = font;
mColor = color;
mCentered = centered;
}
void FListMenuItemStaticText::Drawer(bool selected)
{
const char *text = mText;
if (text != NULL)
{
if (*text == '$') text = GStrings(text+1);
if (mYpos >= 0)
{
int x = mXpos;
if (mCentered) x -= mFont->StringWidth(text)/2;
screen->DrawText(mFont, mColor, x, mYpos, text, DTA_Clean, true, TAG_DONE);
}
else
{
int x = (mXpos - 160) * CleanXfac + (SCREENWIDTH>>1);
if (mCentered) x -= (mFont->StringWidth(text)*CleanXfac)/2;
screen->DrawText (mFont, mColor, x, -mYpos*CleanYfac, text, DTA_CleanNoMove, true, TAG_DONE);
}
}
}
FListMenuItemStaticText::~FListMenuItemStaticText()
{
if (mText != NULL) delete [] mText;
}
//=============================================================================
//
// base class for selectable items
//
//=============================================================================
FListMenuItemSelectable::FListMenuItemSelectable(int x, int y, int height, FName action, int param)
: FListMenuItem(x, y, action)
{
mHeight = height;
mParam = param;
mHotkey = 0;
}
bool FListMenuItemSelectable::CheckCoordinate(int x, int y)
{
return mEnabled && y >= mYpos && y < mYpos + mHeight; // no x check here
}
bool FListMenuItemSelectable::Selectable()
{
return mEnabled;
}
bool FListMenuItemSelectable::Activate()
{
M_SetMenu(mAction, mParam);
return true;
}
FName FListMenuItemSelectable::GetAction(int *pparam)
{
if (pparam != NULL) *pparam = mParam;
return mAction;
}
bool FListMenuItemSelectable::CheckHotkey(int c)
{
return c == tolower(mHotkey);
}
bool FListMenuItemSelectable::MouseEvent(int type, int x, int y)
{
if (type == DMenu::MOUSE_Release)
{
if (DMenu::CurrentMenu->MenuEvent(MKEY_Enter, true))
{
return true;
}
}
return false;
}
//=============================================================================
//
// text item
//
//=============================================================================
FListMenuItemText::FListMenuItemText(int x, int y, int height, int hotkey, const char *text, FFont *font, EColorRange color, FName child, int param)
: FListMenuItemSelectable(x, y, height, child, param)
{
mText = ncopystring(text);
mFont = font;
mColor = color;
mHotkey = hotkey;
}
FListMenuItemText::~FListMenuItemText()
{
if (mText != NULL)
{
delete [] mText;
}
}
void FListMenuItemText::Drawer(bool selected)
{
const char *text = mText;
if (text != NULL)
{
if (*text == '$') text = GStrings(text+1);
screen->DrawText(mFont, mColor, mXpos, mYpos, text, DTA_Clean, true, TAG_DONE);
}
}
//=============================================================================
//
// patch item
//
//=============================================================================
FListMenuItemPatch::FListMenuItemPatch(int x, int y, int height, int hotkey, FTextureID patch, FName child, int param)
: FListMenuItemSelectable(x, y, height, child, param)
{
mHotkey = hotkey;
mTexture = patch;
}
void FListMenuItemPatch::Drawer(bool selected)
{
screen->DrawTexture (TexMan(mTexture), mXpos, mYpos, DTA_Clean, true, TAG_DONE);
}

1165
src/menu/loadsavemenu.cpp Normal file

File diff suppressed because it is too large Load diff

949
src/menu/menu.cpp Normal file
View file

@ -0,0 +1,949 @@
/*
** menu.cpp
** Menu base class and global interface
**
**---------------------------------------------------------------------------
** Copyright 2010 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "doomdef.h"
#include "doomstat.h"
#include "c_dispatch.h"
#include "d_gui.h"
#include "d_player.h"
#include "g_level.h"
#include "c_console.h"
#include "c_bind.h"
#include "s_sound.h"
#include "p_tick.h"
#include "g_game.h"
#include "c_cvars.h"
#include "d_event.h"
#include "v_video.h"
#include "hu_stuff.h"
#include "gi.h"
#include "i_input.h"
#include "gameconfigfile.h"
#include "gstrings.h"
#include "r_main.h"
#include "menu/menu.h"
#include "textures/textures.h"
//
// Todo: Move these elsewhere
//
CVAR (Float, mouse_sensitivity, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, show_messages, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, show_obituaries, true, CVAR_ARCHIVE)
CVAR (Float, snd_menuvolume, 0.6f, CVAR_ARCHIVE)
CVAR(Int, m_use_mouse, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR(Int, m_show_backbutton, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
DMenu *DMenu::CurrentMenu;
int DMenu::MenuTime;
FListMenuDescriptor *MainMenu;
FGameStartup GameStartupInfo;
EMenuState menuactive;
bool M_DemoNoPlay;
FButtonStatus MenuButtons[NUM_MKEYS];
int MenuButtonTickers[NUM_MKEYS];
bool MenuButtonOrigin[NUM_MKEYS];
int BackbuttonTime;
fixed_t BackbuttonAlpha;
#define KEY_REPEAT_DELAY (TICRATE*5/12)
#define KEY_REPEAT_RATE (3)
//============================================================================
//
// DMenu base class
//
//============================================================================
IMPLEMENT_POINTY_CLASS (DMenu)
DECLARE_POINTER(mParentMenu)
END_POINTERS
DMenu::DMenu(DMenu *parent)
{
mParentMenu = parent;
mMouseCapture = false;
mBackbuttonSelected = false;
GC::WriteBarrier(this, parent);
}
bool DMenu::Responder (event_t *ev)
{
if (ev->type == EV_GUI_Event)
{
if (ev->subtype == EV_GUI_LButtonDown)
{
MouseEventBack(MOUSE_Click, ev->data1, ev->data2);
if (MouseEvent(MOUSE_Click, ev->data1, ev->data2))
{
SetCapture();
}
}
else if (ev->subtype == EV_GUI_MouseMove)
{
BackbuttonTime = BACKBUTTON_TIME;
if (mMouseCapture || m_use_mouse == 1)
{
MouseEventBack(MOUSE_Move, ev->data1, ev->data2);
return MouseEvent(MOUSE_Move, ev->data1, ev->data2);
}
}
else if (ev->subtype == EV_GUI_LButtonUp)
{
if (mMouseCapture)
{
ReleaseCapture();
MouseEventBack(MOUSE_Release, ev->data1, ev->data2);
return MouseEvent(MOUSE_Release, ev->data1, ev->data2);
}
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
bool DMenu::MenuEvent (int mkey, bool fromcontroller)
{
switch (mkey)
{
case MKEY_Back:
{
Close();
S_Sound (CHAN_VOICE | CHAN_UI,
DMenu::CurrentMenu != NULL? "menu/backup" : "menu/clear", snd_menuvolume, ATTN_NONE);
return true;
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
void DMenu::Close ()
{
assert(DMenu::CurrentMenu == this);
DMenu::CurrentMenu = mParentMenu;
Destroy();
if (DMenu::CurrentMenu != NULL)
{
GC::WriteBarrier(DMenu::CurrentMenu);
}
else
{
M_ClearMenus ();
}
}
//=============================================================================
//
//
//
//=============================================================================
bool DMenu::MouseEvent(int type, int x, int y)
{
return true;
}
//=============================================================================
//
//
//
//=============================================================================
bool DMenu::MouseEventBack(int type, int x, int y)
{
if (m_show_backbutton >= 0)
{
FTexture *tex = TexMan[gameinfo.mBackButton];
if (tex != NULL)
{
if (m_show_backbutton&1) x -= screen->GetWidth() - tex->GetScaledWidth() * CleanXfac;
if (m_show_backbutton&2) y -= screen->GetHeight() - tex->GetScaledHeight() * CleanYfac;
mBackbuttonSelected = (x >= 0 && x < tex->GetScaledWidth() * CleanXfac && y < tex->GetScaledHeight() * CleanYfac);
if (mBackbuttonSelected && type == MOUSE_Release)
{
if (m_use_mouse == 2) mBackbuttonSelected = false;
MenuEvent(MKEY_Back, true);
}
return true;
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
void DMenu::SetCapture()
{
if (!mMouseCapture)
{
mMouseCapture = true;
I_SetMouseCapture();
}
}
void DMenu::ReleaseCapture()
{
if (mMouseCapture)
{
mMouseCapture = false;
I_ReleaseMouseCapture();
}
}
//=============================================================================
//
//
//
//=============================================================================
void DMenu::Ticker ()
{
}
void DMenu::Drawer ()
{
if (this == DMenu::CurrentMenu && BackbuttonAlpha > 0 && m_show_backbutton >= 0 && m_use_mouse)
{
FTexture *tex = TexMan[gameinfo.mBackButton];
int w = tex->GetScaledWidth() * CleanXfac;
int h = tex->GetScaledHeight() * CleanYfac;
int x = (!(m_show_backbutton&1))? 0:screen->GetWidth() - w;
int y = (!(m_show_backbutton&2))? 0:screen->GetHeight() - h;
if (mBackbuttonSelected && (mMouseCapture || m_use_mouse == 1))
{
screen->DrawTexture(tex, x, y, DTA_CleanNoMove, true, DTA_ColorOverlay, MAKEARGB(40, 255,255,255), TAG_DONE);
}
else
{
screen->DrawTexture(tex, x, y, DTA_CleanNoMove, true, DTA_Alpha, BackbuttonAlpha, TAG_DONE);
}
}
}
bool DMenu::DimAllowed()
{
return true;
}
bool DMenu::TranslateKeyboardEvents()
{
return true;
}
//=============================================================================
//
//
//
//=============================================================================
void M_StartControlPanel (bool makeSound)
{
// intro might call this repeatedly
if (DMenu::CurrentMenu != NULL)
return;
ResetButtonStates ();
for (int i = 0; i < NUM_MKEYS; ++i)
{
MenuButtons[i].ReleaseKey(0);
}
C_HideConsole (); // [RH] Make sure console goes bye bye.
menuactive = MENU_On;
// Pause sound effects before we play the menu switch sound.
// That way, it won't be paused.
P_CheckTickerPaused ();
if (makeSound)
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE);
}
BackbuttonTime = 0;
BackbuttonAlpha = 0;
}
//=============================================================================
//
//
//
//=============================================================================
void M_ActivateMenu(DMenu *menu)
{
if (menuactive == MENU_Off) menuactive = MENU_On;
if (DMenu::CurrentMenu != NULL) DMenu::CurrentMenu->ReleaseCapture();
DMenu::CurrentMenu = menu;
GC::WriteBarrier(DMenu::CurrentMenu);
}
//=============================================================================
//
//
//
//=============================================================================
void M_SetMenu(FName menu, int param)
{
// some menus need some special treatment
switch (menu)
{
case NAME_Episodemenu:
// sent from the player class menu
GameStartupInfo.Skill = -1;
GameStartupInfo.Episode = -1;
GameStartupInfo.PlayerClass =
param == -1? "Random" : PlayerClasses[param].Type->Meta.GetMetaString (APMETA_DisplayName);
break;
case NAME_Skillmenu:
// sent from the episode menu
if ((gameinfo.flags & GI_SHAREWARE) && param > 0)
{
// Only Doom and Heretic have multi-episode shareware versions.
if (gameinfo.gametype == GAME_Doom)
{
M_StartMessage(GStrings("SWSTRING"), 1);
}
else
{
M_StartMessage(GStrings("MNU_ONLYREGISTERED"), 1);
}
return;
}
GameStartupInfo.Episode = param;
M_StartupSkillMenu(&GameStartupInfo); // needs player class name from class menu (later)
break;
case NAME_StartgameConfirm:
{
// sent from the skill menu for a skill that needs to be confirmed
GameStartupInfo.Skill = param;
const char *msg = AllSkills[param].MustConfirmText;
if (*msg==0) msg = GStrings("NIGHTMARE");
M_StartMessage (msg, 0, NAME_Startgame);
return;
}
case NAME_Startgame:
// sent either from skill menu or confirmation screen. Skill gets only set if sent from skill menu
// Now we can finally start the game. Ugh...
if (GameStartupInfo.Skill == -1) GameStartupInfo.Skill = param;
G_DeferedInitNew (&GameStartupInfo);
if (gamestate == GS_FULLCONSOLE)
{
gamestate = GS_HIDECONSOLE;
gameaction = ga_newgame;
}
M_ClearMenus ();
return;
case NAME_Savegamemenu:
if (!usergame || (players[consoleplayer].health <= 0 && !multiplayer))
{
// cannot save outside the game.
M_StartMessage (GStrings("SAVEDEAD"), 1);
return;
}
}
// End of special checks
FMenuDescriptor **desc = MenuDescriptors.CheckKey(menu);
if (desc != NULL)
{
if ((*desc)->mNetgameMessage.IsNotEmpty() && netgame)
{
M_StartMessage((*desc)->mNetgameMessage, 1);
return;
}
if ((*desc)->mType == MDESC_ListMenu)
{
FListMenuDescriptor *ld = static_cast<FListMenuDescriptor*>(*desc);
if (ld->mAutoselect >= 0 && ld->mAutoselect < (int)ld->mItems.Size())
{
// recursively activate the autoselected item without ever creating this menu.
ld->mItems[ld->mAutoselect]->Activate();
}
else
{
const PClass *cls = ld->mClass == NULL? RUNTIME_CLASS(DListMenu) : ld->mClass;
DListMenu *newmenu = (DListMenu *)cls->CreateNew();
newmenu->Init(DMenu::CurrentMenu, ld);
M_ActivateMenu(newmenu);
}
}
else if ((*desc)->mType == MDESC_OptionsMenu)
{
FOptionMenuDescriptor *ld = static_cast<FOptionMenuDescriptor*>(*desc);
const PClass *cls = ld->mClass == NULL? RUNTIME_CLASS(DOptionMenu) : ld->mClass;
DOptionMenu *newmenu = (DOptionMenu *)cls->CreateNew();
newmenu->Init(DMenu::CurrentMenu, ld);
M_ActivateMenu(newmenu);
}
return;
}
else
{
const PClass *menuclass = PClass::FindClass(menu);
if (menuclass != NULL)
{
if (menuclass->IsDescendantOf(RUNTIME_CLASS(DMenu)))
{
DMenu *newmenu = (DMenu*)menuclass->CreateNew();
newmenu->mParentMenu = DMenu::CurrentMenu;
M_ActivateMenu(newmenu);
return;
}
}
}
Printf("Attempting to open menu of unknown type '%s'\n", menu.GetChars());
}
//=============================================================================
//
//
//
//=============================================================================
bool M_Responder (event_t *ev)
{
int ch = 0;
bool keyup = false;
int mkey = NUM_MKEYS;
bool fromcontroller = true;
if (chatmodeon)
{
return false;
}
if (DMenu::CurrentMenu != NULL && menuactive != MENU_Off)
{
// There are a few input sources we are interested in:
//
// EV_KeyDown / EV_KeyUp : joysticks/gamepads/controllers
// EV_GUI_KeyDown / EV_GUI_KeyUp : the keyboard
// EV_GUI_Char : printable characters, which we want in string input mode
//
// This code previously listened for EV_GUI_KeyRepeat to handle repeating
// in the menus, but that doesn't work with gamepads, so now we combine
// the multiple inputs into buttons and handle the repetition manually.
if (ev->type == EV_GUI_Event)
{
fromcontroller = false;
if (ev->subtype == EV_GUI_KeyRepeat)
{
// We do our own key repeat handling but still want to eat the
// OS's repeated keys.
return true;
}
else if (ev->subtype == EV_GUI_BackButtonDown || ev->subtype == EV_GUI_BackButtonUp)
{
mkey = MKEY_Back;
keyup = ev->subtype == EV_GUI_BackButtonUp;
}
else if (ev->subtype != EV_GUI_KeyDown && ev->subtype != EV_GUI_KeyUp)
{
// do we want mouse input?
if (ev->subtype >= EV_GUI_FirstMouseEvent && ev->subtype <= EV_GUI_LastMouseEvent)
{
// FIXME: Mouse events in SDL code are mostly useless so mouse is
// disabled until that code is fixed
#ifdef _WIN32
if (!m_use_mouse)
#endif
return true;
}
// pass everything else on to the current menu
return DMenu::CurrentMenu->Responder(ev);
}
else if (DMenu::CurrentMenu->TranslateKeyboardEvents())
{
ch = ev->data1;
keyup = ev->subtype == EV_GUI_KeyUp;
switch (ch)
{
case GK_BACK: mkey = MKEY_Back; break;
case GK_ESCAPE: mkey = MKEY_Back; break;
case GK_RETURN: mkey = MKEY_Enter; break;
case GK_UP: mkey = MKEY_Up; break;
case GK_DOWN: mkey = MKEY_Down; break;
case GK_LEFT: mkey = MKEY_Left; break;
case GK_RIGHT: mkey = MKEY_Right; break;
case GK_BACKSPACE: mkey = MKEY_Clear; break;
case GK_PGUP: mkey = MKEY_PageUp; break;
case GK_PGDN: mkey = MKEY_PageDown; break;
default:
if (!keyup)
{
return DMenu::CurrentMenu->Responder(ev);
}
break;
}
}
}
else if (ev->type == EV_KeyDown || ev->type == EV_KeyUp)
{
keyup = ev->type == EV_KeyUp;
ch = ev->data1;
switch (ch)
{
case KEY_JOY1:
case KEY_PAD_A:
mkey = MKEY_Enter;
break;
case KEY_JOY2:
case KEY_PAD_B:
mkey = MKEY_Back;
break;
case KEY_JOY3:
case KEY_PAD_X:
mkey = MKEY_Clear;
break;
case KEY_JOY5:
case KEY_PAD_LSHOULDER:
mkey = MKEY_PageUp;
break;
case KEY_JOY6:
case KEY_PAD_RSHOULDER:
mkey = MKEY_PageDown;
break;
case KEY_PAD_DPAD_UP:
case KEY_PAD_LTHUMB_UP:
case KEY_JOYAXIS1MINUS:
case KEY_JOYPOV1_UP:
mkey = MKEY_Up;
break;
case KEY_PAD_DPAD_DOWN:
case KEY_PAD_LTHUMB_DOWN:
case KEY_JOYAXIS1PLUS:
case KEY_JOYPOV1_DOWN:
mkey = MKEY_Down;
break;
case KEY_PAD_DPAD_LEFT:
case KEY_PAD_LTHUMB_LEFT:
case KEY_JOYAXIS2MINUS:
case KEY_JOYPOV1_LEFT:
mkey = MKEY_Left;
break;
case KEY_PAD_DPAD_RIGHT:
case KEY_PAD_LTHUMB_RIGHT:
case KEY_JOYAXIS2PLUS:
case KEY_JOYPOV1_RIGHT:
mkey = MKEY_Right;
break;
}
}
if (mkey != NUM_MKEYS)
{
if (keyup)
{
MenuButtons[mkey].ReleaseKey(ch);
return false;
}
else
{
MenuButtons[mkey].PressKey(ch);
MenuButtonOrigin[mkey] = fromcontroller;
if (mkey <= MKEY_PageDown)
{
MenuButtonTickers[mkey] = KEY_REPEAT_DELAY;
}
DMenu::CurrentMenu->MenuEvent(mkey, fromcontroller);
return true;
}
}
return DMenu::CurrentMenu->Responder(ev) || !keyup;
}
else
{
if (ev->type == EV_KeyDown)
{
// Pop-up menu?
if (ev->data1 == KEY_ESCAPE)
{
M_StartControlPanel(true);
M_SetMenu(NAME_Mainmenu, -1);
return true;
}
// If devparm is set, pressing F1 always takes a screenshot no matter
// what it's bound to. (for those who don't bother to read the docs)
if (devparm && ev->data1 == KEY_F1)
{
G_ScreenShot(NULL);
return true;
}
return false;
}
else if (ev->type == EV_GUI_Event && ev->subtype == EV_GUI_LButtonDown &&
ConsoleState != c_down && m_use_mouse)
{
M_StartControlPanel(true);
M_SetMenu(NAME_Mainmenu, -1);
return true;
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
void M_Ticker (void)
{
DMenu::MenuTime++;
if (DMenu::CurrentMenu != NULL && menuactive != MENU_Off)
DMenu::CurrentMenu->Ticker();
for (int i = 0; i < NUM_MKEYS; ++i)
{
if (MenuButtons[i].bDown)
{
if (MenuButtonTickers[i] > 0 && --MenuButtonTickers[i] <= 0)
{
MenuButtonTickers[i] = KEY_REPEAT_RATE;
DMenu::CurrentMenu->MenuEvent(i, MenuButtonOrigin[i]);
}
}
}
if (BackbuttonTime > 0)
{
if (BackbuttonAlpha < FRACUNIT) BackbuttonAlpha += FRACUNIT/10;
BackbuttonTime--;
}
else
{
if (BackbuttonAlpha > 0) BackbuttonAlpha -= FRACUNIT/10;
if (BackbuttonAlpha < 0) BackbuttonAlpha = 0;
}
}
//=============================================================================
//
//
//
//=============================================================================
void M_Drawer (void)
{
player_t *player = &players[consoleplayer];
AActor *camera = player->camera;
PalEntry fade = 0;
if (!screen->Accel2D && camera != NULL && (gamestate == GS_LEVEL || gamestate == GS_TITLELEVEL))
{
if (camera->player != NULL)
{
player = camera->player;
}
fade = PalEntry (BYTE(player->BlendA*255), BYTE(player->BlendR*255), BYTE(player->BlendG*255), BYTE(player->BlendB*255));
}
if (DMenu::CurrentMenu != NULL && menuactive != MENU_Off)
{
if (DMenu::CurrentMenu->DimAllowed()) screen->Dim(fade);
DMenu::CurrentMenu->Drawer();
}
}
//=============================================================================
//
//
//
//=============================================================================
void M_ClearMenus ()
{
M_DemoNoPlay = false;
if (DMenu::CurrentMenu != NULL)
{
DMenu::CurrentMenu->Destroy();
DMenu::CurrentMenu = NULL;
}
BorderNeedRefresh = screen->GetPageCount ();
menuactive = MENU_Off;
}
//=============================================================================
//
//
//
//=============================================================================
void M_Init (void)
{
M_ParseMenuDefs();
M_CreateMenus();
}
//=============================================================================
//
// [RH] Most menus can now be accessed directly
// through console commands.
//
//=============================================================================
CCMD (menu_main)
{
M_StartControlPanel(true);
M_SetMenu(NAME_Mainmenu, -1);
}
CCMD (menu_load)
{ // F3
M_StartControlPanel (true);
M_SetMenu(NAME_Loadgamemenu, -1);
}
CCMD (menu_save)
{ // F2
M_StartControlPanel (true);
M_SetMenu(NAME_Savegamemenu, -1);
}
CCMD (menu_help)
{ // F1
M_StartControlPanel (true);
M_SetMenu(NAME_Readthismenu, -1);
}
CCMD (menu_game)
{
M_StartControlPanel (true);
M_SetMenu(NAME_Playerclassmenu, -1); // The playerclass menu is the first in the 'start game' chain
}
CCMD (menu_options)
{
M_StartControlPanel (true);
M_SetMenu(NAME_Optionsmenu, -1);
}
CCMD (menu_player)
{
M_StartControlPanel (true);
M_SetMenu(NAME_Playermenu, -1);
}
CCMD (menu_messages)
{
M_StartControlPanel (true);
M_SetMenu(NAME_MessageOptions, -1);
}
CCMD (menu_automap)
{
M_StartControlPanel (true);
M_SetMenu(NAME_AutomapOptions, -1);
}
CCMD (menu_scoreboard)
{
M_StartControlPanel (true);
M_SetMenu(NAME_ScoreboardOptions, -1);
}
CCMD (menu_mapcolors)
{
M_StartControlPanel (true);
M_SetMenu(NAME_MapColorMenu, -1);
}
CCMD (menu_keys)
{
M_StartControlPanel (true);
M_SetMenu(NAME_CustomizeControls, -1);
}
CCMD (menu_gameplay)
{
M_StartControlPanel (true);
M_SetMenu(NAME_GameplayOptions, -1);
}
CCMD (menu_compatibility)
{
M_StartControlPanel (true);
M_SetMenu(NAME_CompatibilityOptions, -1);
}
CCMD (menu_mouse)
{
M_StartControlPanel (true);
M_SetMenu(NAME_MouseOptions, -1);
}
CCMD (menu_joystick)
{
M_StartControlPanel (true);
M_SetMenu(NAME_JoystickOptions, -1);
}
CCMD (menu_sound)
{
M_StartControlPanel (true);
M_SetMenu(NAME_SoundOptions, -1);
}
CCMD (menu_advsound)
{
M_StartControlPanel (true);
M_SetMenu(NAME_AdvSoundOptions, -1);
}
CCMD (menu_modreplayer)
{
M_StartControlPanel(true);
M_SetMenu(NAME_ModReplayerOptions, -1);
}
CCMD (menu_display)
{
M_StartControlPanel (true);
M_SetMenu(NAME_VideoOptions, -1);
}
CCMD (menu_video)
{
M_StartControlPanel (true);
M_SetMenu(NAME_VideoModeMenu, -1);
}
CCMD (openmenu)
{
if (argv.argc() < 2)
{
Printf("Usage: openmenu \"menu_name\"");
return;
}
M_StartControlPanel (true);
M_SetMenu(argv[1], -1);
}
//
// Toggle messages on/off
//
CCMD (togglemessages)
{
if (show_messages)
{
Printf (128, "%s\n", GStrings("MSGOFF"));
show_messages = false;
}
else
{
Printf (128, "%s\n", GStrings("MSGON"));
show_messages = true;
}
}
EXTERN_CVAR (Int, screenblocks)
CCMD (sizedown)
{
screenblocks = screenblocks - 1;
S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE);
}
CCMD (sizeup)
{
screenblocks = screenblocks + 1;
S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE);
}
CCMD(menuconsole)
{
M_ClearMenus();
C_ToggleConsole();
}
CCMD(reset2defaults)
{
C_SetDefaultBindings ();
C_SetCVarsToDefaults ();
R_SetViewSize (screenblocks);
}
CCMD(reset2saved)
{
GameConfig->DoGlobalSetup ();
GameConfig->DoGameSetup (GameNames[gameinfo.gametype]);
R_SetViewSize (screenblocks);
}

652
src/menu/menu.h Normal file
View file

@ -0,0 +1,652 @@
#ifndef __M_MENU_MENU_H__
#define __M_MENU_MENU_H__
#include "dobject.h"
#include "lists.h"
#include "d_player.h"
#include "r_translate.h"
#include "c_cvars.h"
#include "v_font.h"
#include "version.h"
#include "textures/textures.h"
EXTERN_CVAR(Float, snd_menuvolume)
EXTERN_CVAR(Int, m_use_mouse);
struct event_t;
class FTexture;
class FFont;
enum EColorRange;
class FPlayerClass;
class FKeyBindings;
enum EMenuKey
{
MKEY_Up,
MKEY_Down,
MKEY_Left,
MKEY_Right,
MKEY_PageUp,
MKEY_PageDown,
//----------------- Keys past here do not repeat.
MKEY_Enter,
MKEY_Back, // Back to previous menu
MKEY_Clear, // Clear keybinding/flip player sprite preview
NUM_MKEYS,
// These are not buttons but events sent from other menus
MKEY_Input, // Sent when input is confirmed
MKEY_Abort, // Input aborted
MKEY_MBYes,
MKEY_MBNo,
};
struct FGameStartup
{
const char *PlayerClass;
int Episode;
int Skill;
};
extern FGameStartup GameStartupInfo;
struct FSaveGameNode : public Node
{
char Title[SAVESTRINGSIZE];
FString Filename;
bool bOldVersion;
bool bMissingWads;
bool bNoDelete;
FSaveGameNode() { bNoDelete = false; }
};
//=============================================================================
//
// menu descriptor. This is created from the menu definition lump
// Items must be inserted in the order they are cycled through with the cursor
//
//=============================================================================
enum EMenuDescriptorType
{
MDESC_ListMenu,
MDESC_OptionsMenu,
};
struct FMenuDescriptor
{
FName mMenuName;
FString mNetgameMessage;
int mType;
virtual ~FMenuDescriptor() {}
};
class FListMenuItem;
class FOptionMenuItem;
struct FListMenuDescriptor : public FMenuDescriptor
{
TDeletingArray<FListMenuItem *> mItems;
int mSelectedItem;
int mSelectOfsX;
int mSelectOfsY;
FTextureID mSelector;
int mDisplayTop;
int mXpos, mYpos;
int mWLeft, mWRight;
int mLinespacing; // needs to be stored for dynamically created menus
int mAutoselect; // this can only be set by internal menu creation functions
FFont *mFont;
EColorRange mFontColor;
EColorRange mFontColor2;
const PClass *mClass;
FMenuDescriptor *mRedirect; // used to redirect overlong skill and episode menus to option menu based alternatives
};
struct FOptionMenuSettings
{
EColorRange mTitleColor;
EColorRange mFontColor;
EColorRange mFontColorValue;
EColorRange mFontColorMore;
EColorRange mFontColorHeader;
EColorRange mFontColorHighlight;
EColorRange mFontColorSelection;
int mLinespacing;
int mLabelOffset;
};
struct FOptionMenuDescriptor : public FMenuDescriptor
{
TDeletingArray<FOptionMenuItem *> mItems;
FString mTitle;
int mSelectedItem;
int mDrawTop;
int mScrollTop;
int mScrollPos;
int mIndent;
int mPosition;
bool mDontDim;
const PClass *mClass;
void CalcIndent();
FOptionMenuItem *GetItem(FName name);
};
typedef TMap<FName, FMenuDescriptor *> MenuDescriptorList;
extern FOptionMenuSettings OptionSettings;
extern MenuDescriptorList MenuDescriptors;
#define CURSORSPACE (14 * CleanXfac_1)
//=============================================================================
//
//
//
//=============================================================================
struct FMenuRect
{
int x, y;
int width, height;
void set(int _x, int _y, int _w, int _h)
{
x = _x;
y = _y;
width = _w;
height = _h;
}
bool inside(int _x, int _y)
{
return _x >= x && _x < x+width && _y >= y && _y < y+height;
}
};
class DMenu : public DObject
{
DECLARE_CLASS (DMenu, DObject)
HAS_OBJECT_POINTERS
protected:
bool mMouseCapture;
bool mBackbuttonSelected;
public:
enum
{
MOUSE_Click,
MOUSE_Move,
MOUSE_Release
};
enum
{
BACKBUTTON_TIME = 4*TICRATE
};
static DMenu *CurrentMenu;
static int MenuTime;
TObjPtr<DMenu> mParentMenu;
DMenu(DMenu *parent = NULL);
virtual bool Responder (event_t *ev);
virtual bool MenuEvent (int mkey, bool fromcontroller);
virtual void Ticker ();
virtual void Drawer ();
virtual bool DimAllowed ();
virtual bool TranslateKeyboardEvents();
virtual void Close();
virtual bool MouseEvent(int type, int x, int y);
bool MouseEventBack(int type, int x, int y);
void SetCapture();
void ReleaseCapture();
bool HasCapture()
{
return mMouseCapture;
}
};
//=============================================================================
//
// base class for menu items
//
//=============================================================================
class FListMenuItem
{
protected:
int mXpos, mYpos;
FName mAction;
public:
bool mEnabled;
FListMenuItem(int xpos = 0, int ypos = 0, FName action = NAME_None)
{
mXpos = xpos;
mYpos = ypos;
mAction = action;
mEnabled = true;
}
virtual ~FListMenuItem();
virtual bool CheckCoordinate(int x, int y);
virtual void Ticker();
virtual void Drawer(bool selected);
virtual bool Selectable();
virtual bool Activate();
virtual FName GetAction(int *pparam);
virtual bool SetString(int i, const char *s);
virtual bool GetString(int i, char *s, int len);
virtual bool SetValue(int i, int value);
virtual bool GetValue(int i, int *pvalue);
virtual void Enable(bool on);
virtual bool MenuEvent (int mkey, bool fromcontroller);
virtual bool MouseEvent(int type, int x, int y);
virtual bool CheckHotkey(int c);
void DrawSelector(int xofs, int yofs, FTextureID tex);
void OffsetPositionY(int ydelta) { mYpos += ydelta; }
int GetY() { return mYpos; }
};
class FListMenuItemStaticPatch : public FListMenuItem
{
protected:
FTextureID mTexture;
bool mCentered;
public:
FListMenuItemStaticPatch(int x, int y, FTextureID patch, bool centered);
void Drawer(bool selected);
};
class FListMenuItemStaticText : public FListMenuItem
{
protected:
const char *mText;
FFont *mFont;
EColorRange mColor;
bool mCentered;
public:
FListMenuItemStaticText(int x, int y, const char *text, FFont *font, EColorRange color, bool centered);
~FListMenuItemStaticText();
void Drawer(bool selected);
};
//=============================================================================
//
// the player sprite window
//
//=============================================================================
class FListMenuItemPlayerDisplay : public FListMenuItem
{
FListMenuDescriptor *mOwner;
FTexture *mBackdrop;
FRemapTable mRemap;
FPlayerClass *mPlayerClass;
FState *mPlayerState;
int mPlayerTics;
bool mNoportrait;
BYTE mRotation;
BYTE mMode; // 0: automatic (used by class selection), 1: manual (used by player setup)
BYTE mTranslate;
int mSkin;
int mRandomClass;
int mRandomTimer;
int mClassNum;
void SetPlayerClass(int classnum, bool force = false);
bool UpdatePlayerClass();
void UpdateRandomClass();
public:
enum
{
PDF_ROTATION = 0x10001,
PDF_SKIN = 0x10002,
PDF_CLASS = 0x10003,
PDF_MODE = 0x10004,
PDF_TRANSLATE = 0x10005,
};
FListMenuItemPlayerDisplay(FListMenuDescriptor *menu, int x, int y, PalEntry c1, PalEntry c2, bool np, FName action);
~FListMenuItemPlayerDisplay();
virtual void Ticker();
virtual void Drawer(bool selected);
bool SetValue(int i, int value);
};
//=============================================================================
//
// selectable items
//
//=============================================================================
class FListMenuItemSelectable : public FListMenuItem
{
protected:
int mHotkey;
int mHeight;
int mParam;
public:
FListMenuItemSelectable(int x, int y, int height, FName childmenu, int mParam = -1);
bool CheckCoordinate(int x, int y);
bool Selectable();
bool CheckHotkey(int c);
bool Activate();
bool MouseEvent(int type, int x, int y);
FName GetAction(int *pparam);
};
class FListMenuItemText : public FListMenuItemSelectable
{
const char *mText;
FFont *mFont;
EColorRange mColor;
public:
FListMenuItemText(int x, int y, int height, int hotkey, const char *text, FFont *font, EColorRange color, FName child, int param = 0);
~FListMenuItemText();
void Drawer(bool selected);
};
class FListMenuItemPatch : public FListMenuItemSelectable
{
FTextureID mTexture;
public:
FListMenuItemPatch(int x, int y, int height, int hotkey, FTextureID patch, FName child, int param = 0);
void Drawer(bool selected);
};
//=============================================================================
//
// items for the player menu
//
//=============================================================================
class FPlayerNameBox : public FListMenuItemSelectable
{
const char *mText;
FFont *mFont;
EColorRange mFontColor;
int mFrameSize;
char mPlayerName[MAXPLAYERNAME+1];
char mEditName[MAXPLAYERNAME+2];
bool mEntering;
void DrawBorder (int x, int y, int len);
public:
FPlayerNameBox(int x, int y, int height, int frameofs, const char *text, FFont *font, EColorRange color, FName action);
~FPlayerNameBox();
bool SetString(int i, const char *s);
bool GetString(int i, char *s, int len);
void Drawer(bool selected);
bool MenuEvent (int mkey, bool fromcontroller);
};
//=============================================================================
//
// items for the player menu
//
//=============================================================================
class FValueTextItem : public FListMenuItemSelectable
{
TArray<FString> mSelections;
const char *mText;
int mSelection;
FFont *mFont;
EColorRange mFontColor;
EColorRange mFontColor2;
public:
FValueTextItem(int x, int y, int height, const char *text, FFont *font, EColorRange color, EColorRange valuecolor, FName action, FName values);
~FValueTextItem();
bool SetString(int i, const char *s);
bool SetValue(int i, int value);
bool GetValue(int i, int *pvalue);
bool MenuEvent (int mkey, bool fromcontroller);
void Drawer(bool selected);
};
//=============================================================================
//
// items for the player menu
//
//=============================================================================
class FSliderItem : public FListMenuItemSelectable
{
const char *mText;
FFont *mFont;
EColorRange mFontColor;
int mMinrange, mMaxrange;
int mStep;
int mSelection;
void DrawSlider (int x, int y);
public:
FSliderItem(int x, int y, int height, const char *text, FFont *font, EColorRange color, FName action, int min, int max, int step);
~FSliderItem();
bool SetValue(int i, int value);
bool GetValue(int i, int *pvalue);
bool MenuEvent (int mkey, bool fromcontroller);
void Drawer(bool selected);
bool MouseEvent(int type, int x, int y);
};
//=============================================================================
//
// list menu class runs a menu described by a FListMenuDescriptor
//
//=============================================================================
class DListMenu : public DMenu
{
DECLARE_CLASS(DListMenu, DMenu)
protected:
FListMenuDescriptor *mDesc;
FListMenuItem *mFocusControl;
public:
DListMenu(DMenu *parent = NULL, FListMenuDescriptor *desc = NULL);
virtual void Init(DMenu *parent = NULL, FListMenuDescriptor *desc = NULL);
FListMenuItem *GetItem(FName name);
bool Responder (event_t *ev);
bool MenuEvent (int mkey, bool fromcontroller);
bool MouseEvent(int type, int x, int y);
void Ticker ();
void Drawer ();
void SetFocus(FListMenuItem *fc)
{
mFocusControl = fc;
}
bool CheckFocus(FListMenuItem *fc)
{
return mFocusControl == fc;
}
void ReleaseFocus()
{
mFocusControl = NULL;
}
};
//=============================================================================
//
// base class for menu items
//
//=============================================================================
class FOptionMenuItem : public FListMenuItem
{
protected:
char *mLabel;
bool mCentered;
void drawLabel(int indent, int y, EColorRange color, bool grayed = false);
public:
FOptionMenuItem(const char *text, FName action = NAME_None, bool center = false)
: FListMenuItem(0, 0, action)
{
mLabel = copystring(text);
mCentered = center;
}
~FOptionMenuItem();
virtual bool CheckCoordinate(FOptionMenuDescriptor *desc, int x, int y);
virtual int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected);
virtual bool Selectable();
virtual int GetIndent();
virtual bool MouseEvent(int type, int x, int y);
};
//=============================================================================
//
//
//
//=============================================================================
struct FOptionValues
{
struct Pair
{
double Value;
FString TextValue;
FString Text;
};
TArray<Pair> mValues;
};
typedef TMap< FName, FOptionValues* > FOptionMap;
extern FOptionMap OptionValues;
//=============================================================================
//
// Option menu class runs a menu described by a FOptionMenuDescriptor
//
//=============================================================================
class DOptionMenu : public DMenu
{
DECLARE_CLASS(DOptionMenu, DMenu)
bool CanScrollUp;
bool CanScrollDown;
int VisBottom;
FOptionMenuItem *mFocusControl;
protected:
FOptionMenuDescriptor *mDesc;
public:
FOptionMenuItem *GetItem(FName name);
DOptionMenu(DMenu *parent = NULL, FOptionMenuDescriptor *desc = NULL);
virtual void Init(DMenu *parent = NULL, FOptionMenuDescriptor *desc = NULL);
bool Responder (event_t *ev);
bool MenuEvent (int mkey, bool fromcontroller);
bool MouseEvent(int type, int x, int y);
void Ticker ();
void Drawer ();
const FOptionMenuDescriptor *GetDescriptor() const { return mDesc; }
void SetFocus(FOptionMenuItem *fc)
{
mFocusControl = fc;
}
bool CheckFocus(FOptionMenuItem *fc)
{
return mFocusControl == fc;
}
void ReleaseFocus()
{
mFocusControl = NULL;
}
};
//=============================================================================
//
// Input some text
//
//=============================================================================
class DTextEnterMenu : public DMenu
{
DECLARE_ABSTRACT_CLASS(DTextEnterMenu, DMenu)
char *mEnterString;
unsigned int mEnterSize;
unsigned int mEnterPos;
int mSizeMode; // 1: size is length in chars. 2: also check string width
bool mInputGridOkay;
int InputGridX;
int InputGridY;
public:
DTextEnterMenu(DMenu *parent, char *textbuffer, int maxlen, int sizemode, bool showgrid);
void Drawer ();
bool MenuEvent (int mkey, bool fromcontroller);
bool Responder(event_t *ev);
bool TranslateKeyboardEvents();
bool MouseEvent(int type, int x, int y);
};
struct event_t;
bool M_Responder (event_t *ev);
void M_Ticker (void);
void M_Drawer (void);
void M_Init (void);
void M_CreateMenus();
void M_ActivateMenu(DMenu *menu);
void M_ClearMenus ();
void M_ParseMenuDefs();
void M_StartupSkillMenu(FGameStartup *gs);
void M_StartControlPanel (bool makeSound);
void M_SetMenu(FName menu, int param = -1);
void M_NotifyNewSave (const char *file, const char *title, bool okForQuicksave);
void M_StartMessage(const char *message, int messagemode, FName action = NAME_None);
DMenu *StartPickerMenu(DMenu *parent, const char *name, FColorCVar *cvar);
void M_RefreshModesList ();
void M_InitVideoModesMenu ();
#endif

1389
src/menu/menudef.cpp Normal file

File diff suppressed because it is too large Load diff

366
src/menu/menuinput.cpp Normal file
View file

@ -0,0 +1,366 @@
/*
** menuinput.cpp
** The string input code
**
**---------------------------------------------------------------------------
** Copyright 2001-2010 Randy Heit
** Copyright 2010 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "menu/menu.h"
#include "v_video.h"
#include "c_cvars.h"
#include "d_event.h"
#include "d_gui.h"
#include "v_font.h"
#include "v_palette.h"
IMPLEMENT_ABSTRACT_CLASS(DTextEnterMenu)
#define INPUTGRID_WIDTH 13
#define INPUTGRID_HEIGHT 5
// Heretic and Hexen do not, by default, come with glyphs for all of these
// characters. Oh well. Doom and Strife do.
static const char InputGridChars[INPUTGRID_WIDTH * INPUTGRID_HEIGHT] =
"ABCDEFGHIJKLM"
"NOPQRSTUVWXYZ"
"0123456789+-="
".,!?@'\":;[]()"
"<>^#$%&*/_ \b";
CVAR(Bool, m_showinputgrid, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
//=============================================================================
//
//
//
//=============================================================================
DTextEnterMenu::DTextEnterMenu(DMenu *parent, char *textbuffer, int maxlen, int sizemode, bool showgrid)
: DMenu(parent)
{
mEnterString = textbuffer;
mEnterSize = maxlen;
mEnterPos = (unsigned)strlen(textbuffer);
mSizeMode = sizemode;
mInputGridOkay = showgrid || m_showinputgrid;
if (mEnterPos > 0)
{
InputGridX = INPUTGRID_WIDTH - 1;
InputGridY = INPUTGRID_HEIGHT - 1;
}
else
{
// If we are naming a new save, don't start the cursor on "end".
InputGridX = 0;
InputGridY = 0;
}
}
//=============================================================================
//
//
//
//=============================================================================
bool DTextEnterMenu::TranslateKeyboardEvents()
{
return mInputGridOkay;
}
//=============================================================================
//
//
//
//=============================================================================
bool DTextEnterMenu::Responder(event_t *ev)
{
if (ev->type == EV_GUI_Event)
{
// Save game and player name string input
if (ev->subtype == EV_GUI_Char)
{
mInputGridOkay = false;
if (mEnterPos < mEnterSize &&
(mSizeMode == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(mEnterString) < (mEnterSize-1)*8))
{
mEnterString[mEnterPos] = (char)ev->data1;
mEnterString[++mEnterPos] = 0;
}
return true;
}
char ch = (char)ev->data1;
if ((ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_KeyRepeat) && ch == '\b')
{
if (mEnterPos > 0)
{
mEnterPos--;
mEnterString[mEnterPos] = 0;
}
}
else if (ev->subtype == EV_GUI_KeyDown)
{
if (ch == GK_ESCAPE)
{
DMenu *parent = mParentMenu;
Close();
parent->MenuEvent(MKEY_Abort, false);
return true;
}
else if (ch == '\r')
{
if (mEnterString[0])
{
DMenu *parent = mParentMenu;
Close();
parent->MenuEvent(MKEY_Input, false);
return true;
}
}
}
if (ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_KeyRepeat)
{
return true;
}
}
return Super::Responder(ev);
}
//=============================================================================
//
//
//
//=============================================================================
bool DTextEnterMenu::MouseEvent(int type, int x, int y)
{
const int cell_width = 18 * CleanXfac;
const int cell_height = 12 * CleanYfac;
const int screen_y = screen->GetHeight() - INPUTGRID_HEIGHT * cell_height;
const int screen_x = (screen->GetWidth() - INPUTGRID_WIDTH * cell_width) / 2;
if (x >= screen_x && x < screen_x + INPUTGRID_WIDTH * cell_width && y >= screen_y)
{
InputGridX = (x - screen_x) / cell_width;
InputGridY = (y - screen_y) / cell_height;
if (type == DMenu::MOUSE_Release)
{
if (MenuEvent(MKEY_Enter, true))
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
if (m_use_mouse == 2) InputGridX = InputGridY = -1;
return true;
}
}
}
else
{
InputGridX = InputGridY = -1;
}
return Super::MouseEvent(type, x, y);
}
//=============================================================================
//
//
//
//=============================================================================
bool DTextEnterMenu::MenuEvent (int key, bool fromcontroller)
{
if (key == MKEY_Back)
{
mParentMenu->MenuEvent(MKEY_Abort, false);
return Super::MenuEvent(key, fromcontroller);
}
if (fromcontroller)
{
mInputGridOkay = true;
}
if (mInputGridOkay)
{
int ch;
if (InputGridX == -1 || InputGridY == -1)
{
InputGridX = InputGridY = 0;
}
switch (key)
{
case MKEY_Down:
InputGridY = (InputGridY + 1) % INPUTGRID_HEIGHT;
return true;
case MKEY_Up:
InputGridY = (InputGridY + INPUTGRID_HEIGHT - 1) % INPUTGRID_HEIGHT;
return true;
case MKEY_Right:
InputGridX = (InputGridX + 1) % INPUTGRID_WIDTH;
return true;
case MKEY_Left:
InputGridX = (InputGridX + INPUTGRID_WIDTH - 1) % INPUTGRID_WIDTH;
return true;
case MKEY_Clear:
if (mEnterPos > 0)
{
mEnterString[--mEnterPos] = 0;
}
return true;
case MKEY_Enter:
assert(unsigned(InputGridX) < INPUTGRID_WIDTH && unsigned(InputGridY) < INPUTGRID_HEIGHT);
if (mInputGridOkay)
{
ch = InputGridChars[InputGridX + InputGridY * INPUTGRID_WIDTH];
if (ch == 0) // end
{
if (mEnterString[0] != '\0')
{
DMenu *parent = mParentMenu;
Close();
parent->MenuEvent(MKEY_Input, false);
return true;
}
}
else if (ch == '\b') // bs
{
if (mEnterPos > 0)
{
mEnterString[--mEnterPos] = 0;
}
}
else if (mEnterPos < mEnterSize &&
(mSizeMode == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(mEnterString) < (mEnterSize-1)*8))
{
mEnterString[mEnterPos] = ch;
mEnterString[++mEnterPos] = 0;
}
}
return true;
default:
break; // Keep GCC quiet
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
void DTextEnterMenu::Drawer ()
{
mParentMenu->Drawer();
if (mInputGridOkay)
{
const int cell_width = 18 * CleanXfac;
const int cell_height = 12 * CleanYfac;
const int top_padding = cell_height / 2 - SmallFont->GetHeight() * CleanYfac / 2;
// Darken the background behind the character grid.
// Unless we frame it with a border, I think it looks better to extend the
// background across the full width of the screen.
screen->Dim(0, 0.8f,
0 /*screen->GetWidth()/2 - 13 * cell_width / 2*/,
screen->GetHeight() - INPUTGRID_HEIGHT * cell_height,
screen->GetWidth() /*13 * cell_width*/,
INPUTGRID_HEIGHT * cell_height);
if (InputGridX >= 0 && InputGridY >= 0)
{
// Highlight the background behind the selected character.
screen->Dim(MAKERGB(255,248,220), 0.6f,
InputGridX * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen->GetWidth() / 2,
InputGridY * cell_height - INPUTGRID_HEIGHT * cell_height + screen->GetHeight(),
cell_width, cell_height);
}
for (int y = 0; y < INPUTGRID_HEIGHT; ++y)
{
const int yy = y * cell_height - INPUTGRID_HEIGHT * cell_height + screen->GetHeight();
for (int x = 0; x < INPUTGRID_WIDTH; ++x)
{
int width;
const int xx = x * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen->GetWidth() / 2;
const int ch = InputGridChars[y * INPUTGRID_WIDTH + x];
FTexture *pic = SmallFont->GetChar(ch, &width);
EColorRange color;
FRemapTable *remap;
// The highlighted character is yellow; the rest are dark gray.
color = (x == InputGridX && y == InputGridY) ? CR_YELLOW : CR_DARKGRAY;
remap = SmallFont->GetColorTranslation(color);
if (pic != NULL)
{
// Draw a normal character.
screen->DrawTexture(pic, xx + cell_width/2 - width*CleanXfac/2, yy + top_padding,
DTA_Translation, remap,
DTA_CleanNoMove, true,
TAG_DONE);
}
else if (ch == ' ')
{
// Draw the space as a box outline. We also draw it 50% wider than it really is.
const int x1 = xx + cell_width/2 - width * CleanXfac * 3 / 4;
const int x2 = x1 + width * 3 * CleanXfac / 2;
const int y1 = yy + top_padding;
const int y2 = y1 + SmallFont->GetHeight() * CleanYfac;
const int palentry = remap->Remap[remap->NumEntries*2/3];
const uint32 palcolor = remap->Palette[remap->NumEntries*2/3];
screen->Clear(x1, y1, x2, y1+CleanYfac, palentry, palcolor); // top
screen->Clear(x1, y2, x2, y2+CleanYfac, palentry, palcolor); // bottom
screen->Clear(x1, y1+CleanYfac, x1+CleanXfac, y2, palentry, palcolor); // left
screen->Clear(x2-CleanXfac, y1+CleanYfac, x2, y2, palentry, palcolor); // right
}
else if (ch == '\b' || ch == 0)
{
// Draw the backspace and end "characters".
const char *const str = ch == '\b' ? "BS" : "ED";
screen->DrawText(SmallFont, color,
xx + cell_width/2 - SmallFont->StringWidth(str)*CleanXfac/2,
yy + top_padding, str, DTA_CleanNoMove, true, TAG_DONE);
}
}
}
}
Super::Drawer();
}

733
src/menu/messagebox.cpp Normal file
View file

@ -0,0 +1,733 @@
/*
** messagebox.cpp
** Confirmation, notification screns
**
**---------------------------------------------------------------------------
** Copyright 2010 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "menu/menu.h"
#include "d_event.h"
#include "d_gui.h"
#include "v_video.h"
#include "v_text.h"
#include "d_main.h"
#include "gstrings.h"
#include "gi.h"
#include "i_video.h"
#include "st_start.h"
#include "c_dispatch.h"
#include "g_game.h"
extern FSaveGameNode *quickSaveSlot;
class DMessageBoxMenu : public DMenu
{
DECLARE_CLASS(DMessageBoxMenu, DMenu)
FBrokenLines *mMessage;
int mMessageMode;
int messageSelection;
int mMouseLeft, mMouseRight, mMouseY;
FName mAction;
public:
DMessageBoxMenu(DMenu *parent = NULL, const char *message = NULL, int messagemode = 0, bool playsound = false, FName action = NAME_None);
void Destroy();
void Init(DMenu *parent, const char *message, int messagemode, bool playsound = false);
void Drawer();
bool Responder(event_t *ev);
bool MenuEvent(int mkey, bool fromcontroller);
bool MouseEvent(int type, int x, int y);
void CloseSound();
virtual void HandleResult(bool res);
};
IMPLEMENT_CLASS(DMessageBoxMenu)
//=============================================================================
//
//
//
//=============================================================================
DMessageBoxMenu::DMessageBoxMenu(DMenu *parent, const char *message, int messagemode, bool playsound, FName action)
: DMenu(parent)
{
mAction = action;
messageSelection = 0;
mMouseLeft = 140;
mMouseY = INT_MIN;
int mr1 = 170 + SmallFont->StringWidth(GStrings["TXT_YES"]);
int mr2 = 170 + SmallFont->StringWidth(GStrings["TXT_NO"]);
mMouseRight = MAX(mr1, mr2);
Init(parent, message, messagemode, playsound);
}
//=============================================================================
//
//
//
//=============================================================================
void DMessageBoxMenu::Init(DMenu *parent, const char *message, int messagemode, bool playsound)
{
mParentMenu = parent;
if (message != NULL)
{
if (*message == '$') message = GStrings(message+1);
mMessage = V_BreakLines(SmallFont, 300, message);
}
else mMessage = NULL;
mMessageMode = messagemode;
if (playsound)
{
S_StopSound (CHAN_VOICE);
S_Sound (CHAN_VOICE | CHAN_UI, "menu/prompt", snd_menuvolume, ATTN_NONE);
}
}
//=============================================================================
//
//
//
//=============================================================================
void DMessageBoxMenu::Destroy()
{
if (mMessage != NULL) V_FreeBrokenLines(mMessage);
mMessage = NULL;
}
//=============================================================================
//
//
//
//=============================================================================
void DMessageBoxMenu::CloseSound()
{
S_Sound (CHAN_VOICE | CHAN_UI,
DMenu::CurrentMenu != NULL? "menu/backup" : "menu/dismiss", snd_menuvolume, ATTN_NONE);
}
//=============================================================================
//
//
//
//=============================================================================
void DMessageBoxMenu::HandleResult(bool res)
{
if (mParentMenu != NULL)
{
if (mMessageMode == 0)
{
if (mAction == NAME_None)
{
mParentMenu->MenuEvent(res? MKEY_MBYes : MKEY_MBNo, false);
Close();
}
else
{
Close();
if (res) M_SetMenu(mAction, -1);
}
CloseSound();
}
}
}
//=============================================================================
//
//
//
//=============================================================================
void DMessageBoxMenu::Drawer ()
{
int i, y;
PalEntry fade = 0;
int fontheight = SmallFont->GetHeight();
//BorderNeedRefresh = screen->GetPageCount ();
//SB_state = screen->GetPageCount ();
y = 100;
if (mMessage != NULL)
{
for (i = 0; mMessage[i].Width >= 0; i++)
y -= SmallFont->GetHeight () / 2;
for (i = 0; mMessage[i].Width >= 0; i++)
{
screen->DrawText (SmallFont, CR_UNTRANSLATED, 160 - mMessage[i].Width/2, y, mMessage[i].Text,
DTA_Clean, true, TAG_DONE);
y += fontheight;
}
}
if (mMessageMode == 0)
{
y += fontheight;
mMouseY = y;
screen->DrawText(SmallFont,
messageSelection == 0? OptionSettings.mFontColorSelection : OptionSettings.mFontColor,
160, y, GStrings["TXT_YES"], DTA_Clean, true, TAG_DONE);
screen->DrawText(SmallFont,
messageSelection == 1? OptionSettings.mFontColorSelection : OptionSettings.mFontColor,
160, y + fontheight + 1, GStrings["TXT_NO"], DTA_Clean, true, TAG_DONE);
if (messageSelection >= 0)
{
if ((DMenu::MenuTime%8) < 6)
{
screen->DrawText(ConFont, OptionSettings.mFontColorSelection,
(150 - 160) * CleanXfac + screen->GetWidth() / 2,
(y + (fontheight + 1) * messageSelection - 100) * CleanYfac + screen->GetHeight() / 2,
"\xd",
DTA_CellX, 8 * CleanXfac,
DTA_CellY, 8 * CleanYfac,
TAG_DONE);
}
}
}
}
//=============================================================================
//
//
//
//=============================================================================
bool DMessageBoxMenu::Responder(event_t *ev)
{
if (ev->type == EV_GUI_Event && ev->subtype == EV_GUI_KeyDown)
{
if (mMessageMode == 0)
{
int ch = tolower(ev->data1);
if (ch == 'n' || ch == ' ')
{
HandleResult(false);
return true;
}
else if (ch == 'y')
{
HandleResult(true);
return true;
}
}
else
{
Close();
return true;
}
return false;
}
else if (ev->type == EV_KeyDown)
{
Close();
return true;
}
return Super::Responder(ev);
}
//=============================================================================
//
//
//
//=============================================================================
bool DMessageBoxMenu::MenuEvent(int mkey, bool fromcontroller)
{
if (mMessageMode == 0)
{
if (mkey == MKEY_Up || mkey == MKEY_Down)
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
messageSelection = !messageSelection;
return true;
}
else if (mkey == MKEY_Enter)
{
// 0 is yes, 1 is no
HandleResult(!messageSelection);
return true;
}
else if (mkey == MKEY_Back)
{
HandleResult(false);
return true;
}
return false;
}
else
{
Close();
CloseSound();
return true;
}
}
//=============================================================================
//
//
//
//=============================================================================
bool DMessageBoxMenu::MouseEvent(int type, int x, int y)
{
if (mMessageMode == 1)
{
if (type == MOUSE_Click)
{
return MenuEvent(MKEY_Enter, true);
}
return false;
}
else
{
int sel = -1;
int fh = SmallFont->GetHeight() + 1;
// convert x/y from screen to virtual coordinates, according to CleanX/Yfac use in DrawTexture
x = ((x - (screen->GetWidth() / 2)) / CleanXfac) + 160;
y = ((y - (screen->GetHeight() / 2)) / CleanYfac) + 100;
if (x >= mMouseLeft && x <= mMouseRight && y >= mMouseY && y < mMouseY + 2 * fh)
{
sel = y >= mMouseY + fh;
}
if (sel != -1 && sel != messageSelection)
{
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
}
messageSelection = sel;
if (type == MOUSE_Release)
{
return MenuEvent(MKEY_Enter, true);
}
return true;
}
}
//=============================================================================
//
//
//
//=============================================================================
//=============================================================================
//
//
//
//=============================================================================
class DQuitMenu : public DMessageBoxMenu
{
DECLARE_CLASS(DQuitMenu, DMessageBoxMenu)
public:
DQuitMenu(bool playsound = false);
virtual void HandleResult(bool res);
};
IMPLEMENT_CLASS(DQuitMenu)
//=============================================================================
//
//
//
//=============================================================================
DQuitMenu::DQuitMenu(bool playsound)
{
int messageindex = gametic % gameinfo.quitmessages.Size();
FString EndString;
const char *msg = gameinfo.quitmessages[messageindex];
if (msg[0] == '$')
{
if (msg[1] == '*')
{
EndString = GStrings(msg+2);
}
else
{
EndString.Format("%s\n\n%s", GStrings(msg+1), GStrings("DOSY"));
}
}
else EndString = gameinfo.quitmessages[messageindex];
Init(NULL, EndString, 0, playsound);
}
//=============================================================================
//
//
//
//=============================================================================
void DQuitMenu::HandleResult(bool res)
{
if (res)
{
if (!netgame)
{
if (gameinfo.quitSound.IsNotEmpty())
{
S_Sound (CHAN_VOICE | CHAN_UI, gameinfo.quitSound, snd_menuvolume, ATTN_NONE);
I_WaitVBL (105);
}
}
ST_Endoom();
}
else
{
Close();
CloseSound();
}
}
//=============================================================================
//
//
//
//=============================================================================
CCMD (menu_quit)
{ // F10
M_StartControlPanel (true);
DMenu *newmenu = new DQuitMenu(false);
newmenu->mParentMenu = DMenu::CurrentMenu;
M_ActivateMenu(newmenu);
}
//=============================================================================
//
//
//
//=============================================================================
//=============================================================================
//
//
//
//=============================================================================
class DEndGameMenu : public DMessageBoxMenu
{
DECLARE_CLASS(DEndGameMenu, DMessageBoxMenu)
public:
DEndGameMenu(bool playsound = false);
virtual void HandleResult(bool res);
};
IMPLEMENT_CLASS(DEndGameMenu)
//=============================================================================
//
//
//
//=============================================================================
DEndGameMenu::DEndGameMenu(bool playsound)
{
int messageindex = gametic % gameinfo.quitmessages.Size();
FString EndString = gameinfo.quitmessages[messageindex];
if (netgame)
{
if(gameinfo.gametype == GAME_Chex)
EndString = GStrings("CNETEND");
else
EndString = GStrings("NETEND");
return;
}
if(gameinfo.gametype == GAME_Chex)
EndString = GStrings("CENDGAME");
else
EndString = GStrings("ENDGAME");
Init(NULL, EndString, 0, playsound);
}
//=============================================================================
//
//
//
//=============================================================================
void DEndGameMenu::HandleResult(bool res)
{
if (res)
{
M_ClearMenus ();
D_StartTitle ();
}
else
{
Close();
CloseSound();
}
}
//=============================================================================
//
//
//
//=============================================================================
CCMD (menu_endgame)
{ // F7
if (!usergame)
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/invalid", snd_menuvolume, ATTN_NONE);
return;
}
//M_StartControlPanel (true);
S_Sound (CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE);
DMenu *newmenu = new DEndGameMenu(false);
newmenu->mParentMenu = DMenu::CurrentMenu;
M_ActivateMenu(newmenu);
}
//=============================================================================
//
//
//
//=============================================================================
//=============================================================================
//
//
//
//=============================================================================
class DQuickSaveMenu : public DMessageBoxMenu
{
DECLARE_CLASS(DQuickSaveMenu, DMessageBoxMenu)
public:
DQuickSaveMenu(bool playsound = false);
virtual void HandleResult(bool res);
};
IMPLEMENT_CLASS(DQuickSaveMenu)
//=============================================================================
//
//
//
//=============================================================================
DQuickSaveMenu::DQuickSaveMenu(bool playsound)
{
FString tempstring;
if(gameinfo.gametype == GAME_Chex)
tempstring.Format(GStrings("CQSPROMPT"), quickSaveSlot->Title);
else
tempstring.Format(GStrings("QSPROMPT"), quickSaveSlot->Title);
Init(NULL, tempstring, 0, playsound);
}
//=============================================================================
//
//
//
//=============================================================================
void DQuickSaveMenu::HandleResult(bool res)
{
if (res)
{
G_SaveGame (quickSaveSlot->Filename.GetChars(), quickSaveSlot->Title);
S_Sound (CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE);
M_ClearMenus();
}
else
{
Close();
CloseSound();
}
}
//=============================================================================
//
//
//
//=============================================================================
CCMD (quicksave)
{ // F6
if (!usergame || (players[consoleplayer].health <= 0 && !multiplayer))
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/invalid", snd_menuvolume, ATTN_NONE);
return;
}
if (gamestate != GS_LEVEL)
return;
S_Sound (CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE);
if (quickSaveSlot == NULL)
{
M_StartControlPanel(false);
M_SetMenu(NAME_Savegamemenu);
return;
}
DMenu *newmenu = new DQuickSaveMenu(false);
newmenu->mParentMenu = DMenu::CurrentMenu;
M_ActivateMenu(newmenu);
}
//=============================================================================
//
//
//
//=============================================================================
//=============================================================================
//
//
//
//=============================================================================
class DQuickLoadMenu : public DMessageBoxMenu
{
DECLARE_CLASS(DQuickLoadMenu, DMessageBoxMenu)
public:
DQuickLoadMenu(bool playsound = false);
virtual void HandleResult(bool res);
};
IMPLEMENT_CLASS(DQuickLoadMenu)
//=============================================================================
//
//
//
//=============================================================================
DQuickLoadMenu::DQuickLoadMenu(bool playsound)
{
FString tempstring;
if(gameinfo.gametype == GAME_Chex)
tempstring.Format(GStrings("CQLPROMPT"), quickSaveSlot->Title);
else
tempstring.Format(GStrings("QLPROMPT"), quickSaveSlot->Title);
Init(NULL, tempstring, 0, playsound);
}
//=============================================================================
//
//
//
//=============================================================================
void DQuickLoadMenu::HandleResult(bool res)
{
if (res)
{
G_LoadGame (quickSaveSlot->Filename.GetChars());
S_Sound (CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE);
M_ClearMenus();
}
else
{
Close();
CloseSound();
}
}
//=============================================================================
//
//
//
//=============================================================================
CCMD (quickload)
{ // F9
M_StartControlPanel (true);
if (netgame)
{
if(gameinfo.gametype == GAME_Chex)
M_StartMessage (GStrings("CQLOADNET"), NULL);
else
M_StartMessage (GStrings("QLOADNET"), NULL);
return;
}
if (quickSaveSlot == NULL)
{
M_StartControlPanel(false);
// signal that whatever gets loaded should be the new quicksave
quickSaveSlot = (FSaveGameNode *)1;
M_SetMenu(NAME_Loadgamemenu);
return;
}
DMenu *newmenu = new DQuickLoadMenu(false);
newmenu->mParentMenu = DMenu::CurrentMenu;
M_ActivateMenu(newmenu);
}
//=============================================================================
//
//
//
//=============================================================================
void M_StartMessage(const char *message, int messagemode, FName action)
{
if (DMenu::CurrentMenu == NULL)
{
// only play a sound if no menu was active before
M_StartControlPanel(menuactive == MENU_Off);
}
DMenu *newmenu = new DMessageBoxMenu(DMenu::CurrentMenu, message, messagemode, false, action);
newmenu->mParentMenu = DMenu::CurrentMenu;
M_ActivateMenu(newmenu);
}

591
src/menu/optionmenu.cpp Normal file
View file

@ -0,0 +1,591 @@
/*
** optionmenu.cpp
** Handler class for the option menus and associated items
**
**---------------------------------------------------------------------------
** Copyright 2010 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "v_video.h"
#include "v_font.h"
#include "cmdlib.h"
#include "gstrings.h"
#include "g_level.h"
#include "gi.h"
#include "v_palette.h"
#include "d_gui.h"
#include "d_event.h"
#include "c_dispatch.h"
#include "c_console.h"
#include "c_cvars.h"
#include "c_bind.h"
#include "gameconfigfile.h"
#include "menu/menu.h"
//=============================================================================
//
// Draws a string in the console font, scaled to the 8x8 cells
// used by the default console font.
//
//=============================================================================
void M_DrawConText (int color, int x, int y, const char *str)
{
int len = (int)strlen(str);
screen->DrawText (ConFont, color, x, y, str,
DTA_CellX, 8 * CleanXfac_1,
DTA_CellY, 8 * CleanYfac_1,
TAG_DONE);
}
//=============================================================================
//
// Draw a slider. Set fracdigits negative to not display the current value numerically.
//
//=============================================================================
void M_DrawSlider (int x, int y, double min, double max, double cur,int fracdigits)
{
double range;
range = max - min;
double ccur = clamp(cur, min, max) - min;
if (CleanXfac > CleanXfac_1 || CleanXfac_1 * 320 < screen->GetWidth())
{
M_DrawConText(CR_WHITE, x, y, "\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12");
M_DrawConText(CR_ORANGE, x + int((5 + ((ccur * 78) / range)) * CleanXfac_1), y, "\x13");
if (fracdigits >= 0)
{
char textbuf[16];
mysnprintf(textbuf, countof(textbuf), "%.*f", fracdigits, cur);
screen->DrawText(SmallFont, CR_DARKGRAY, x + (12*8 + 4) * CleanXfac_1, y, textbuf, DTA_CleanNoMove_1, true, TAG_DONE);
}
}
else
{
// On 320x200 we need a shorter slider
M_DrawConText(CR_WHITE, x, y, "\x10\x11\x11\x11\x11\x11\x12");
M_DrawConText(CR_ORANGE, x + int((5 + ((ccur * 38) / range)) * CleanXfac_1), y, "\x13");
if (fracdigits >= 0)
{
char textbuf[16];
mysnprintf(textbuf, countof(textbuf), "%.*f", fracdigits, cur);
screen->DrawText(SmallFont, CR_DARKGRAY, x + (7*8 + 4) * CleanXfac_1, y, textbuf, DTA_CleanNoMove_1, true, TAG_DONE);
}
}
}
IMPLEMENT_CLASS(DOptionMenu)
//=============================================================================
//
//
//
//=============================================================================
DOptionMenu::DOptionMenu(DMenu *parent, FOptionMenuDescriptor *desc)
: DMenu(parent)
{
CanScrollUp = false;
CanScrollDown = false;
VisBottom = 0;
mFocusControl = NULL;
Init(parent, desc);
}
//=============================================================================
//
//
//
//=============================================================================
void DOptionMenu::Init(DMenu *parent, FOptionMenuDescriptor *desc)
{
mParentMenu = parent;
GC::WriteBarrier(this, parent);
mDesc = desc;
if (mDesc != NULL && mDesc->mSelectedItem < 0)
{
// Go down to the first selectable item
int i = -1;
mDesc->mSelectedItem = -1;
do
{
i++;
}
while (!mDesc->mItems[i]->Selectable() && i < (int)mDesc->mItems.Size());
if (i>=0) mDesc->mSelectedItem = i;
}
}
//=============================================================================
//
//
//
//=============================================================================
FOptionMenuItem *DOptionMenu::GetItem(FName name)
{
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
FName nm = mDesc->mItems[i]->GetAction(NULL);
if (nm == name) return mDesc->mItems[i];
}
return NULL;
}
//=============================================================================
//
//
//
//=============================================================================
bool DOptionMenu::Responder (event_t *ev)
{
if (ev->type == EV_GUI_Event)
{
if (ev->subtype == EV_GUI_WheelUp)
{
if (mDesc->mScrollPos > 0)
{
mDesc->mScrollPos--;
}
return true;
}
else if (ev->subtype == EV_GUI_WheelDown)
{
if (CanScrollDown)
{
mDesc->mScrollPos++;
VisBottom++;
}
return true;
}
}
return Super::Responder(ev);
}
//=============================================================================
//
//
//
//=============================================================================
bool DOptionMenu::MenuEvent (int mkey, bool fromcontroller)
{
int startedAt = mDesc->mSelectedItem;
switch (mkey)
{
case MKEY_Up:
do
{
--mDesc->mSelectedItem;
if (mDesc->mScrollPos > 0 &&
mDesc->mSelectedItem == mDesc->mScrollTop + mDesc->mScrollPos)
{
mDesc->mScrollPos--;
}
if (mDesc->mSelectedItem < 0)
{
// Figure out how many lines of text fit on the menu
int y = mDesc->mPosition;
if (y <= 0)
{
if (BigFont && mDesc->mTitle.IsNotEmpty())
{
y = -y + BigFont->GetHeight();
}
else
{
y = -y;
}
}
y *= CleanYfac_1;
int rowheight = OptionSettings.mLinespacing * CleanYfac_1;
int maxitems = (screen->GetHeight() - rowheight - y) / rowheight + 1;
mDesc->mScrollPos = MAX (0, (int)mDesc->mItems.Size() - maxitems + mDesc->mScrollTop);
mDesc->mSelectedItem = mDesc->mItems.Size()-1;
}
}
while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable() && mDesc->mSelectedItem != startedAt);
break;
case MKEY_Down:
do
{
++mDesc->mSelectedItem;
if (CanScrollDown && mDesc->mSelectedItem == VisBottom)
{
mDesc->mScrollPos++;
VisBottom++;
}
if (mDesc->mSelectedItem >= (int)mDesc->mItems.Size())
{
mDesc->mSelectedItem = 0;
mDesc->mScrollPos = 0;
}
}
while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable() && mDesc->mSelectedItem != startedAt);
break;
case MKEY_PageUp:
if (mDesc->mScrollPos > 0)
{
mDesc->mScrollPos -= VisBottom - mDesc->mScrollPos - mDesc->mScrollTop;
if (mDesc->mScrollPos < 0)
{
mDesc->mScrollPos = 0;
}
mDesc->mSelectedItem = mDesc->mScrollTop + mDesc->mScrollPos + 1;
while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable())
{
++mDesc->mSelectedItem;
}
}
break;
case MKEY_PageDown:
if (CanScrollDown)
{
int pagesize = VisBottom - mDesc->mScrollPos - mDesc->mScrollTop;
mDesc->mScrollPos += pagesize;
if (mDesc->mScrollPos + mDesc->mScrollTop + pagesize > (int)mDesc->mItems.Size())
{
mDesc->mScrollPos = mDesc->mItems.Size() - mDesc->mScrollTop - pagesize;
}
mDesc->mSelectedItem = mDesc->mScrollTop + mDesc->mScrollPos;
while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable())
{
++mDesc->mSelectedItem;
}
}
break;
case MKEY_Enter:
if (mDesc->mSelectedItem >= 0 && mDesc->mItems[mDesc->mSelectedItem]->Activate())
{
return true;
}
// fall through to default
default:
if (mDesc->mSelectedItem >= 0 &&
mDesc->mItems[mDesc->mSelectedItem]->MenuEvent(mkey, fromcontroller)) return true;
return Super::MenuEvent(mkey, fromcontroller);
}
if (mDesc->mSelectedItem != startedAt)
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
}
return true;
}
//=============================================================================
//
//
//
//=============================================================================
bool DOptionMenu::MouseEvent(int type, int x, int y)
{
y = (y / CleanYfac_1) - mDesc->mDrawTop;
if (mFocusControl)
{
mFocusControl->MouseEvent(type, x, y);
return true;
}
else
{
int yline = (y / OptionSettings.mLinespacing);
if (yline >= mDesc->mScrollTop)
{
yline += mDesc->mScrollPos;
}
if ((unsigned)yline < mDesc->mItems.Size() && mDesc->mItems[yline]->Selectable())
{
if (yline != mDesc->mSelectedItem)
{
mDesc->mSelectedItem = yline;
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
}
mDesc->mItems[yline]->MouseEvent(type, x, y);
return true;
}
}
mDesc->mSelectedItem = -1;
return Super::MouseEvent(type, x, y);
}
//=============================================================================
//
//
//
//=============================================================================
void DOptionMenu::Ticker ()
{
Super::Ticker();
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
mDesc->mItems[i]->Ticker();
}
}
//=============================================================================
//
//
//
//=============================================================================
void DOptionMenu::Drawer ()
{
int y = mDesc->mPosition;
if (y <= 0)
{
if (BigFont && mDesc->mTitle.IsNotEmpty())
{
const char *tt = mDesc->mTitle;
if (*tt == '$') tt = GStrings(tt+1);
screen->DrawText (BigFont, OptionSettings.mTitleColor,
(screen->GetWidth() - BigFont->StringWidth(tt) * CleanXfac_1) / 2, 10*CleanYfac_1,
tt, DTA_CleanNoMove_1, true, TAG_DONE);
y = -y + BigFont->GetHeight();
}
else
{
y = -y;
}
}
mDesc->mDrawTop = y;
//int labelofs = OptionSettings.mLabelOffset * CleanXfac_1;
//int cursorspace = 14 * CleanXfac_1;
int fontheight = OptionSettings.mLinespacing * CleanYfac_1;
y *= CleanYfac_1;
int indent = mDesc->mIndent;
if (indent > 280)
{ // kludge for the compatibility options with their extremely long labels
if (indent + 40 <= CleanWidth_1)
{
indent = (screen->GetWidth() - ((indent + 40) * CleanXfac_1)) / 2 + indent * CleanXfac_1;
}
else
{
indent = screen->GetWidth() - 40 * CleanXfac_1;
}
}
else
{
indent = (indent - 160) * CleanXfac_1 + screen->GetWidth() / 2;
}
int ytop = y + mDesc->mScrollTop * 8 * CleanYfac_1;
int lastrow = screen->GetHeight() - SmallFont->GetHeight() * CleanYfac_1;
unsigned i;
for (i = 0; i < mDesc->mItems.Size() && y <= lastrow; i++, y += fontheight)
{
// Don't scroll the uppermost items
if (i == mDesc->mScrollTop)
{
i += mDesc->mScrollPos;
if (i >= mDesc->mItems.Size()) break; // skipped beyond end of menu
}
int cur_indent = mDesc->mItems[i]->Draw(mDesc, y, indent, mDesc->mSelectedItem == i);
if (cur_indent >= 0 && mDesc->mSelectedItem == i && mDesc->mItems[i]->Selectable())
{
if (((DMenu::MenuTime%8) < 6) || DMenu::CurrentMenu != this)
{
M_DrawConText(OptionSettings.mFontColorSelection, cur_indent + 3 * CleanXfac_1, y-CleanYfac_1+OptionSettings.mLabelOffset, "\xd");
}
}
}
CanScrollUp = (mDesc->mScrollPos > 0);
CanScrollDown = (i < mDesc->mItems.Size());
VisBottom = i - 1;
if (CanScrollUp)
{
M_DrawConText(CR_ORANGE, 3 * CleanXfac_1, ytop + OptionSettings.mLabelOffset, "\x1a");
}
if (CanScrollDown)
{
M_DrawConText(CR_ORANGE, 3 * CleanXfac_1, y - 8*CleanYfac_1 + OptionSettings.mLabelOffset, "\x1b");
}
Super::Drawer();
}
//=============================================================================
//
// base class for menu items
//
//=============================================================================
FOptionMenuItem::~FOptionMenuItem()
{
if (mLabel != NULL) delete [] mLabel;
}
bool FOptionMenuItem::CheckCoordinate(FOptionMenuDescriptor *desc, int x, int y)
{
return false;
}
int FOptionMenuItem::Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
{
return indent;
}
bool FOptionMenuItem::Selectable()
{
return true;
}
bool FOptionMenuItem::MouseEvent(int type, int x, int y)
{
if (Selectable() && type == DMenu::MOUSE_Release)
{
return DMenu::CurrentMenu->MenuEvent(MKEY_Enter, true);
}
return false;
}
int FOptionMenuItem::GetIndent()
{
return mCentered? 0 : SmallFont->StringWidth(mLabel);
}
void FOptionMenuItem::drawLabel(int indent, int y, EColorRange color, bool grayed)
{
const char *label = mLabel;
if (*label == '$') label = GStrings(label+1);
int overlay = grayed? MAKEARGB(96,48,0,0) : 0;
int x;
int w = SmallFont->StringWidth(label) * CleanXfac_1;
if (!mCentered) x = indent - w;
else x = (screen->GetWidth() - w) / 2;
screen->DrawText (SmallFont, color, x, y, label, DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay, TAG_DONE);
}
void FOptionMenuDescriptor::CalcIndent()
{
// calculate the menu indent
int widest = 0, thiswidth;
for (unsigned i = 0; i < mItems.Size(); i++)
{
thiswidth = mItems[i]->GetIndent();
if (thiswidth > widest) widest = thiswidth;
}
mIndent = widest + 4;
}
//=============================================================================
//
//
//
//=============================================================================
FOptionMenuItem *FOptionMenuDescriptor::GetItem(FName name)
{
for(unsigned i=0;i<mItems.Size(); i++)
{
FName nm = mItems[i]->GetAction(NULL);
if (nm == name) return mItems[i];
}
return NULL;
}
class DGameplayMenu : public DOptionMenu
{
DECLARE_CLASS(DGameplayMenu, DOptionMenu)
public:
DGameplayMenu()
{}
void Drawer ()
{
Super::Drawer();
char text[64];
mysnprintf(text, 64, "dmflags = %d dmflags2 = %d", *dmflags, *dmflags2);
screen->DrawText (SmallFont, OptionSettings.mFontColorValue,
(screen->GetWidth() - SmallFont->StringWidth (text) * CleanXfac_1) / 2, 0, text,
DTA_CleanNoMove_1, true, TAG_DONE);
}
};
IMPLEMENT_CLASS(DGameplayMenu)
class DCompatibilityMenu : public DOptionMenu
{
DECLARE_CLASS(DCompatibilityMenu, DOptionMenu)
public:
DCompatibilityMenu()
{}
void Drawer ()
{
Super::Drawer();
char text[64];
mysnprintf(text, 64, "compatflags = %d", *compatflags);
screen->DrawText (SmallFont, OptionSettings.mFontColorValue,
(screen->GetWidth() - SmallFont->StringWidth (text) * CleanXfac_1) / 2, 0, text,
DTA_CleanNoMove_1, true, TAG_DONE);
}
};
IMPLEMENT_CLASS(DCompatibilityMenu)

916
src/menu/optionmenuitems.h Normal file
View file

@ -0,0 +1,916 @@
/*
** optionmenuitems.h
** Control items for option menus
**
**---------------------------------------------------------------------------
** Copyright 2010 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
void M_DrawConText (int color, int x, int y, const char *str);
void M_DrawSlider (int x, int y, double min, double max, double cur,int fracdigits);
void M_SetVideoMode();
//=============================================================================
//
// opens a submenu, action is a submenu name
//
//=============================================================================
class FOptionMenuItemSubmenu : public FOptionMenuItem
{
int mParam;
public:
FOptionMenuItemSubmenu(const char *label, const char *menu, int param = 0)
: FOptionMenuItem(label, menu)
{
mParam = param;
}
int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
{
drawLabel(indent, y, selected? OptionSettings.mFontColorSelection : OptionSettings.mFontColorMore);
return indent;
}
bool Activate()
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
M_SetMenu(mAction, mParam);
return true;
}
};
//=============================================================================
//
// Executes a CCMD, action is a CCMD name
//
//=============================================================================
class FOptionMenuItemCommand : public FOptionMenuItemSubmenu
{
public:
FOptionMenuItemCommand(const char *label, const char *menu)
: FOptionMenuItemSubmenu(label, menu)
{
}
bool Activate()
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
C_DoCommand(mAction);
return true;
}
};
//=============================================================================
//
// Executes a CCMD after confirmation, action is a CCMD name
//
//=============================================================================
class FOptionMenuItemSafeCommand : public FOptionMenuItemCommand
{
// action is a CCMD
public:
FOptionMenuItemSafeCommand(const char *label, const char *menu)
: FOptionMenuItemCommand(label, menu)
{
}
bool MenuEvent (int mkey, bool fromcontroller)
{
if (mkey == MKEY_MBYes)
{
C_DoCommand(mAction);
return true;
}
return FOptionMenuItemCommand::MenuEvent(mkey, fromcontroller);
}
bool Activate()
{
M_StartMessage("Do you really want to do this?", 0);
return true;
}
};
//=============================================================================
//
// Base class for option lists
//
//=============================================================================
class FOptionMenuItemOptionBase : public FOptionMenuItem
{
protected:
// action is a CVAR
FOptionValues *mValues;
FBaseCVar *mGrayCheck;
int mCenter;
public:
enum
{
OP_VALUES = 0x11001
};
FOptionMenuItemOptionBase(const char *label, const char *menu, const char *values, const char *graycheck, int center)
: FOptionMenuItem(label, menu)
{
FOptionValues **opt = OptionValues.CheckKey(values);
if (opt != NULL)
{
mValues = *opt;
}
else
{
mValues = NULL;
}
mGrayCheck = (FBoolCVar*)FindCVar(graycheck, NULL);
mCenter = center;
}
bool SetString(int i, const char *newtext)
{
if (i == OP_VALUES)
{
FOptionValues **opt = OptionValues.CheckKey(newtext);
if (opt != NULL)
{
mValues = *opt;
int s = GetSelection();
if (s >= (int)mValues->mValues.Size()) s = 0;
SetSelection(s); // readjust the CVAR if its value is outside the range now
return true;
}
}
return false;
}
//=============================================================================
virtual int GetSelection() = 0;
virtual void SetSelection(int Selection) = 0;
//=============================================================================
int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
{
bool grayed = mGrayCheck != NULL && !(mGrayCheck->GetGenericRep(CVAR_Bool).Bool);
if (mCenter)
{
indent = (screen->GetWidth() / 2);
}
drawLabel(indent, y, selected? OptionSettings.mFontColorSelection : OptionSettings.mFontColor, grayed);
int overlay = grayed? MAKEARGB(96,48,0,0) : 0;
const char *text;
int Selection = GetSelection();
if (Selection < 0)
{
text = "Unknown";
}
else
{
text = mValues->mValues[Selection].Text;
}
screen->DrawText (SmallFont, OptionSettings.mFontColorValue, indent + CURSORSPACE, y,
text, DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay, TAG_DONE);
return indent;
}
//=============================================================================
bool MenuEvent (int mkey, bool fromcontroller)
{
if (mValues->mValues.Size() > 0)
{
int Selection = GetSelection();
if (mkey == MKEY_Left)
{
if (Selection == -1) Selection = 0;
else if (--Selection < 0) Selection = mValues->mValues.Size()-1;
}
else if (mkey == MKEY_Right || mkey == MKEY_Enter)
{
if (++Selection >= (int)mValues->mValues.Size()) Selection = 0;
}
else
{
return FOptionMenuItem::MenuEvent(mkey, fromcontroller);
}
SetSelection(Selection);
S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE);
}
return true;
}
bool Selectable()
{
return !(mGrayCheck != NULL && !(mGrayCheck->GetGenericRep(CVAR_Bool).Bool));
}
};
//=============================================================================
//
// Change a CVAR, action is the CVAR name
//
//=============================================================================
class FOptionMenuItemOption : public FOptionMenuItemOptionBase
{
// action is a CVAR
FBaseCVar *mCVar;
public:
FOptionMenuItemOption(const char *label, const char *menu, const char *values, const char *graycheck, int center)
: FOptionMenuItemOptionBase(label, menu, values, graycheck, center)
{
mCVar = FindCVar(mAction, NULL);
}
//=============================================================================
int GetSelection()
{
int Selection = -1;
if (mValues != NULL && mCVar != NULL && mValues->mValues.Size() > 0)
{
if (mValues->mValues[0].TextValue.IsEmpty())
{
UCVarValue cv = mCVar->GetGenericRep(CVAR_Float);
for(unsigned i=0;i<mValues->mValues.Size(); i++)
{
if (fabs(cv.Float - mValues->mValues[i].Value) < FLT_EPSILON)
{
Selection = i;
break;
}
}
}
else
{
UCVarValue cv = mCVar->GetGenericRep(CVAR_String);
for(unsigned i=0;i<mValues->mValues.Size(); i++)
{
if (mValues->mValues[i].TextValue.CompareNoCase(cv.String) == 0)
{
Selection = i;
break;
}
}
}
}
return Selection;
}
void SetSelection(int Selection)
{
UCVarValue value;
if (mValues != NULL && mCVar != NULL && mValues->mValues.Size() > 0)
{
if (mValues->mValues[0].TextValue.IsEmpty())
{
value.Float = (float)mValues->mValues[Selection].Value;
mCVar->SetGenericRep (value, CVAR_Float);
}
else
{
value.String = mValues->mValues[Selection].TextValue.LockBuffer();
mCVar->SetGenericRep (value, CVAR_String);
mValues->mValues[Selection].TextValue.UnlockBuffer();
}
}
}
};
//=============================================================================
//
// This class is used to capture the key to be used as the new key binding
// for a control item
//
//=============================================================================
class DEnterKey : public DMenu
{
DECLARE_CLASS(DEnterKey, DMenu)
int *pKey;
public:
DEnterKey(DMenu *parent, int *keyptr)
: DMenu(parent)
{
pKey = keyptr;
SetMenuMessage(1);
menuactive = MENU_WaitKey; // There should be a better way to disable GUI capture...
}
bool TranslateKeyboardEvents()
{
return false;
}
void SetMenuMessage(int which)
{
if (mParentMenu->IsKindOf(RUNTIME_CLASS(DOptionMenu)))
{
DOptionMenu *m = barrier_cast<DOptionMenu*>(mParentMenu);
FListMenuItem *it = m->GetItem(NAME_Controlmessage);
if (it != NULL)
{
it->SetValue(0, which);
}
}
}
bool Responder(event_t *ev)
{
if (ev->type == EV_KeyDown)
{
*pKey = ev->data1;
menuactive = MENU_On;
SetMenuMessage(0);
Close();
mParentMenu->MenuEvent((ev->data1 == KEY_ESCAPE)? MKEY_Abort : MKEY_Input, 0);
return true;
}
return false;
}
void Drawer()
{
mParentMenu->Drawer();
}
};
#ifndef NO_IMP
IMPLEMENT_ABSTRACT_CLASS(DEnterKey)
#endif
//=============================================================================
//
// // Edit a key binding, Action is the CCMD to bind
//
//=============================================================================
class FOptionMenuItemControl : public FOptionMenuItem
{
FKeyBindings *mBindings;
int mInput;
bool mWaiting;
public:
FOptionMenuItemControl(const char *label, const char *menu, FKeyBindings *bindings)
: FOptionMenuItem(label, menu)
{
mBindings = bindings;
mWaiting = false;
}
//=============================================================================
int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
{
drawLabel(indent, y, mWaiting? OptionSettings.mFontColorHighlight:
(selected? OptionSettings.mFontColorSelection : OptionSettings.mFontColor));
char description[64];
int Key1, Key2;
mBindings->GetKeysForCommand(mAction, &Key1, &Key2);
C_NameKeys (description, Key1, Key2);
if (description[0])
{
M_DrawConText(CR_WHITE, indent + CURSORSPACE, y-1+OptionSettings.mLabelOffset, description);
}
else
{
screen->DrawText(SmallFont, CR_BLACK, indent + CURSORSPACE, y + OptionSettings.mLabelOffset, "---",
DTA_CleanNoMove_1, true, TAG_DONE);
}
return indent;
}
//=============================================================================
bool MenuEvent(int mkey, bool fromcontroller)
{
if (mkey == MKEY_Input)
{
mWaiting = false;
mBindings->SetBind(mInput, mAction);
return true;
}
else if (mkey == MKEY_Clear)
{
mBindings->UnbindACommand(mAction);
return true;
}
else if (mkey == MKEY_Abort)
{
mWaiting = false;
return true;
}
return false;
}
bool Activate()
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
mWaiting = true;
DMenu *input = new DEnterKey(DMenu::CurrentMenu, &mInput);
M_ActivateMenu(input);
return true;
}
};
//=============================================================================
//
//
//
//=============================================================================
class FOptionMenuItemStaticText : public FOptionMenuItem
{
EColorRange mColor;
public:
FOptionMenuItemStaticText(const char *label, bool header)
: FOptionMenuItem(label, NAME_None, true)
{
mColor = header? OptionSettings.mFontColorHeader : OptionSettings.mFontColor;
}
int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
{
drawLabel(indent, y, mColor);
return -1;
}
bool Selectable()
{
return false;
}
};
//=============================================================================
//
//
//
//=============================================================================
class FOptionMenuItemStaticTextSwitchable : public FOptionMenuItem
{
EColorRange mColor;
FString mAltText;
int mCurrent;
public:
FOptionMenuItemStaticTextSwitchable(const char *label, const char *label2, FName action, bool header)
: FOptionMenuItem(label, action, true)
{
mColor = header? OptionSettings.mFontColorHeader : OptionSettings.mFontColor;
mAltText = label2;
mCurrent = 0;
}
int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
{
const char *txt = mCurrent? (const char*)mAltText : mLabel;
int w = SmallFont->StringWidth(txt) * CleanXfac_1;
int x = (screen->GetWidth() - w) / 2;
screen->DrawText (SmallFont, mColor, x, y, txt, DTA_CleanNoMove_1, true, TAG_DONE);
return -1;
}
bool SetValue(int i, int val)
{
if (i == 0)
{
mCurrent = val;
return true;
}
return false;
}
bool SetString(int i, const char *newtext)
{
if (i == 0)
{
mAltText = newtext;
return true;
}
return false;
}
bool Selectable()
{
return false;
}
};
//=============================================================================
//
//
//
//=============================================================================
class FOptionMenuSliderBase : public FOptionMenuItem
{
// action is a CVAR
double mMin, mMax, mStep;
int mShowValue;
int mDrawX;
public:
FOptionMenuSliderBase(const char *label, double min, double max, double step, int showval)
: FOptionMenuItem(label, NAME_None)
{
mMin = min;
mMax = max;
mStep = step;
mShowValue = showval;
mDrawX = 0;
}
virtual double GetValue() = 0;
virtual void SetValue(double val) = 0;
//=============================================================================
int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
{
drawLabel(indent, y, selected? OptionSettings.mFontColorSelection : OptionSettings.mFontColor);
mDrawX = indent + CURSORSPACE;
M_DrawSlider (mDrawX, y + OptionSettings.mLabelOffset, mMin, mMax, GetValue(), mShowValue);
return indent;
}
//=============================================================================
bool MenuEvent (int mkey, bool fromcontroller)
{
double value = GetValue();
if (mkey == MKEY_Left)
{
value -= mStep;
}
else if (mkey == MKEY_Right)
{
value += mStep;
}
else
{
return FOptionMenuItem::MenuEvent(mkey, fromcontroller);
}
SetValue(clamp(value, mMin, mMax));
S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE);
return true;
}
bool MouseEvent(int type, int x, int y)
{
DOptionMenu *lm = static_cast<DOptionMenu*>(DMenu::CurrentMenu);
if (type != DMenu::MOUSE_Click)
{
if (!lm->CheckFocus(this)) return false;
}
if (type == DMenu::MOUSE_Release)
{
lm->ReleaseFocus();
}
int slide_left = mDrawX+8*CleanXfac_1;
int slide_right = slide_left + 10*8*CleanXfac_1; // 12 char cells with 8 pixels each.
if (type == DMenu::MOUSE_Click)
{
if (x < slide_left || x >= slide_right) return true;
}
x = clamp(x, slide_left, slide_right);
double v = mMin + ((x - slide_left) * (mMax - mMin)) / (slide_right - slide_left);
if (v != GetValue())
{
SetValue(v);
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE);
}
if (type == DMenu::MOUSE_Click)
{
lm->SetFocus(this);
}
return true;
}
};
//=============================================================================
//
//
//
//=============================================================================
class FOptionMenuSliderCVar : public FOptionMenuSliderBase
{
FBaseCVar *mCVar;
public:
FOptionMenuSliderCVar(const char *label, const char *menu, double min, double max, double step, int showval)
: FOptionMenuSliderBase(label, min, max, step, showval)
{
mCVar = FindCVar(menu, NULL);
}
double GetValue()
{
if (mCVar != NULL)
{
return mCVar->GetGenericRep(CVAR_Float).Float;
}
else
{
return 0;
}
}
void SetValue(double val)
{
if (mCVar != NULL)
{
UCVarValue value;
value.Float = (float)val;
mCVar->SetGenericRep(value, CVAR_Float);
}
}
};
//=============================================================================
//
//
//
//=============================================================================
class FOptionMenuSliderVar : public FOptionMenuSliderBase
{
float *mPVal;
public:
FOptionMenuSliderVar(const char *label, float *pVal, double min, double max, double step, int showval)
: FOptionMenuSliderBase(label, min, max, step, showval)
{
mPVal = pVal;
}
double GetValue()
{
return *mPVal;
}
void SetValue(double val)
{
*mPVal = (float)val;
}
};
//=============================================================================
//
// // Edit a key binding, Action is the CCMD to bind
//
//=============================================================================
class FOptionMenuItemColorPicker : public FOptionMenuItem
{
FColorCVar *mCVar;
public:
enum
{
CPF_RESET = 0x20001,
};
FOptionMenuItemColorPicker(const char *label, const char *menu)
: FOptionMenuItem(label, menu)
{
FBaseCVar *cv = FindCVar(menu, NULL);
if (cv->GetRealType() == CVAR_Color)
{
mCVar = (FColorCVar*)cv;
}
else mCVar = NULL;
}
//=============================================================================
int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
{
drawLabel(indent, y, selected? OptionSettings.mFontColorSelection : OptionSettings.mFontColor);
if (mCVar != NULL)
{
int box_x = indent + CURSORSPACE;
int box_y = y + OptionSettings.mLabelOffset * CleanYfac_1 / 2;
screen->Clear (box_x, box_y, box_x + 32*CleanXfac_1, box_y + (SmallFont->GetHeight() - 1) * CleanYfac_1,
-1, (uint32)*mCVar | 0xff000000);
}
return indent;
}
bool SetValue(int i, int v)
{
if (i == CPF_RESET && mCVar != NULL)
{
mCVar->ResetToDefault();
return true;
}
return false;
}
bool Activate()
{
if (mCVar != NULL)
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
DMenu *picker = StartPickerMenu(DMenu::CurrentMenu, mLabel, mCVar);
if (picker != NULL)
{
M_ActivateMenu(picker);
return true;
}
}
return false;
}
};
class FOptionMenuScreenResolutionLine : public FOptionMenuItem
{
FString mResTexts[3];
int mSelection;
int mHighlight;
int mMaxValid;
public:
enum
{
SRL_INDEX = 0x30000,
SRL_SELECTION = 0x30003,
SRL_HIGHLIGHT = 0x30004,
};
FOptionMenuScreenResolutionLine(const char *action)
: FOptionMenuItem("", action)
{
mSelection = 0;
mHighlight = -1;
}
bool SetValue(int i, int v)
{
if (i == SRL_SELECTION)
{
mSelection = v;
return true;
}
else if (i == SRL_HIGHLIGHT)
{
mHighlight = v;
return true;
}
return false;
}
bool GetValue(int i, int *v)
{
if (i == SRL_SELECTION)
{
*v = mSelection;
return true;
}
return false;
}
bool SetString(int i, const char *newtext)
{
if (i >= SRL_INDEX && i <= SRL_INDEX+2)
{
mResTexts[i-SRL_INDEX] = newtext;
if (mResTexts[0].IsEmpty()) mMaxValid = -1;
else if (mResTexts[1].IsEmpty()) mMaxValid = 0;
else if (mResTexts[2].IsEmpty()) mMaxValid = 1;
else mMaxValid = 2;
return true;
}
return false;
}
bool GetString(int i, char *s, int len)
{
if (i >= SRL_INDEX && i <= SRL_INDEX+2)
{
strncpy(s, mResTexts[i-SRL_INDEX], len-1);
s[len-1] = 0;
return true;
}
return false;
}
bool MenuEvent (int mkey, bool fromcontroller)
{
if (mkey == MKEY_Left)
{
if (--mSelection < 0) mSelection = mMaxValid;
S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE);
return true;
}
else if (mkey == MKEY_Right)
{
if (++mSelection > mMaxValid) mSelection = 0;
S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE);
return true;
}
else
{
return FOptionMenuItem::MenuEvent(mkey, fromcontroller);
}
return false;
}
bool MouseEvent(int type, int x, int y)
{
int colwidth = screen->GetWidth() / 3;
mSelection = x / colwidth;
return FOptionMenuItem::MouseEvent(type, x, y);
}
bool Activate()
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
M_SetVideoMode();
return true;
}
int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
{
int colwidth = screen->GetWidth() / 3;
EColorRange color;
for (int x = 0; x < 3; x++)
{
if (selected && mSelection == x)
color = OptionSettings.mFontColorSelection;
else if (x == mHighlight)
color = OptionSettings.mFontColorHighlight;
else
color = OptionSettings.mFontColorValue;
screen->DrawText (SmallFont, color, colwidth * x + 20 * CleanXfac_1, y, mResTexts[x], DTA_CleanNoMove_1, true, TAG_DONE);
}
return colwidth * mSelection + 20 * CleanXfac_1 - CURSORSPACE;
}
bool Selectable()
{
return mMaxValid >= 0;
}
};
#ifndef NO_IMP
CCMD(am_restorecolors)
{
if (DMenu::CurrentMenu != NULL && DMenu::CurrentMenu->IsKindOf(RUNTIME_CLASS(DOptionMenu)))
{
DOptionMenu *m = (DOptionMenu*)DMenu::CurrentMenu;
const FOptionMenuDescriptor *desc = m->GetDescriptor();
// Find the color cvars by scanning the MapColors menu.
for (unsigned i = 0; i < desc->mItems.Size(); ++i)
{
desc->mItems[i]->SetValue(FOptionMenuItemColorPicker::CPF_RESET, 0);
}
}
}
#endif

561
src/menu/playerdisplay.cpp Normal file
View file

@ -0,0 +1,561 @@
/*
** playerdisplay.cpp
** The player display for the player setup and class selection screen
**
**---------------------------------------------------------------------------
** Copyright 2010 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "doomtype.h"
#include "doomstat.h"
#include "d_player.h"
#include "tables.h"
#include "m_fixed.h"
#include "templates.h"
#include "menu/menu.h"
#include "colormatcher.h"
#include "textures/textures.h"
#include "w_wad.h"
#include "v_font.h"
#include "v_video.h"
#include "g_level.h"
#include "gi.h"
#include "r_defs.h"
#include "r_state.h"
//=============================================================================
//
// Used by the player display
//
//=============================================================================
struct FBackdropTexture : public FTexture
{
public:
FBackdropTexture();
const BYTE *GetColumn(unsigned int column, const Span **spans_out);
const BYTE *GetPixels();
void Unload();
bool CheckModified();
protected:
BYTE Pixels[144*160];
static const Span DummySpan[2];
int LastRenderTic;
angle_t time1, time2, time3, time4;
angle_t t1ang, t2ang, z1ang, z2ang;
void Render();
};
// A 32x32 cloud rendered with Photoshop, plus some other filters
static BYTE pattern1[1024] =
{
5, 9, 7,10, 9,15, 9, 7, 8,10, 5, 3, 5, 7, 9, 8,14, 8, 4, 7, 8, 9, 5, 7,14, 7, 0, 7,13,13, 9, 6,
2, 7, 9, 7, 7,10, 8, 8,11,10, 6, 7,10, 7, 5, 6, 6, 4, 7,13,15,16,11,15,11, 8, 0, 4,13,22,17,11,
5, 9, 9, 7, 9,10, 4, 3, 6, 7, 8, 6, 5, 4, 2, 2, 1, 4, 6,11,15,15,14,13,17, 9, 5, 9,11,12,17,20,
9,16, 9, 8,12,13, 7, 3, 7, 9, 5, 4, 2, 5, 5, 5, 7,11, 6, 7, 6,13,17,10,10, 9,12,17,14,12,16,15,
15,13, 5, 3, 9,10, 4,10,12,12, 7, 9, 8, 8, 8,10, 7, 6, 5, 5, 5, 6,11, 9, 3,13,16,18,21,16,23,18,
23,13, 0, 0, 0, 0, 0,12,18,14,15,16,13, 7, 7, 5, 9, 6, 6, 8, 4, 0, 0, 0, 0,14,19,17,14,20,21,25,
19,20,14,13, 7, 5,13,19,14,13,17,15,14, 7, 3, 5, 6,11, 7, 7, 8, 8,10, 9, 9,18,17,15,14,15,18,16,
16,29,24,23,18, 9,17,20,11, 5,12,15,15,12, 6, 3, 4, 6, 7,10,13,18,18,19,16,12,17,19,23,16,14,14,
9,18,20,26,19, 5,18,18,10, 5,12,15,14,17,11, 6,11, 9,10,13,10,20,24,20,21,20,14,18,15,22,20,19,
0, 6,16,18, 8, 7,15,18,10,13,17,17,13,11,15,11,19,12,13,10, 4,15,19,21,21,24,14, 9,17,20,24,17,
18,17, 7, 7,16,21,22,15, 5,14,20,14,13,21,13, 8,12,14, 7, 8,11,15,13,11,16,17, 7, 5,12,17,19,14,
25,23,17,16,23,18,15, 7, 0, 6,11, 6,11,15,11, 7,12, 7, 4,10,16,13, 7, 7,15,13, 9,15,21,14, 5, 0,
18,22,21,21,21,22,12, 6,14,20,15, 6,10,19,13, 8, 7, 3, 7,12,14,16, 9,12,22,15,12,18,24,19,17, 9,
0,15,18,21,17,25,14,13,19,21,21,11, 6,13,16,16,12,10,12,11,13,20,14,13,18,13, 9,15,16,25,31,20,
5,20,24,16, 7,14,14,11,18,19,19, 6, 0, 5,11,14,17,16,19,14,15,21,19,15,14,14, 8, 0, 7,24,18,16,
9,17,15, 7, 6,14,12, 7,14,16,11, 4, 7, 6,13,16,15,13,12,20,21,20,21,17,18,26,14, 0,13,23,21,11,
9,12,18,11,15,21,13, 8,13,13,10, 7,13, 8, 8,19,13, 7, 4,15,19,18,14,12,14,15, 8, 6,16,22,22,15,
9,17,14,19,15,14,15, 9,11, 9, 6, 8,14,13,13,12, 5, 0, 0, 6,12,13, 7, 7, 9, 7, 0,12,21,16,15,18,
15,16,18,11, 6, 8,15, 9, 2, 0, 5,10,10,16, 9, 0, 4,12,15, 9,12, 9, 7, 7,12, 7, 0, 6,12, 6, 9,13,
12,19,15,14,11, 7, 8, 9,12,10, 5, 5, 7,12,12,10,14,16,16,11, 8,12,10,12,10, 8,10,10,14,12,16,16,
16,17,20,22,12,15,12,14,19,11, 6, 5,10,13,17,17,21,19,15, 9, 6, 9,15,18,10,10,18,14,20,15,16,17,
11,19,19,18,19,14,17,13,12,12, 7,11,18,17,16,15,19,19,10, 2, 0, 8,15,12, 8,11,12,10,19,20,19,19,
6,14,18,13,13,16,16,12, 5, 8,10,12,10,13,18,12, 9,10, 7, 6, 5,11, 8, 6, 7,13,16,13,10,15,20,14,
0, 5,12,12, 4, 0, 9,16, 9,10,12, 8, 0, 9,13, 9, 0, 2, 4, 7,10, 6, 7, 3, 4,11,16,18,10,11,21,21,
16,13,11,15, 8, 0, 5, 9, 8, 7, 6, 3, 0, 9,17, 9, 0, 0, 0, 3, 5, 4, 3, 5, 7,15,16,16,17,14,22,22,
24,14,15,12, 9, 0, 5,10, 8, 4, 7,12,10,11,12, 7, 6, 8, 6, 5, 7, 8, 8,11,13,10,15,14,12,18,20,16,
16,17,17,18,12, 9,12,16,10, 5, 6,20,13,15, 8, 4, 8, 9, 8, 7, 9,11,12,17,16,16,11,10, 9,10, 5, 0,
0,14,18,18,15,16,14, 9,10, 9, 9,15,14,10, 4, 6,10, 8, 8, 7,10, 9,10,16,18,10, 0, 0, 7,12,10, 8,
0,14,19,14, 9,11,11, 8, 8,10,15, 9,10, 7, 4,10,13, 9, 7, 5, 5, 7, 7, 7,13,13, 5, 5,14,22,18,16,
0,10,14,10, 3, 6, 5, 6, 8, 9, 8, 9, 5, 9, 8, 9, 6, 8, 8, 8, 1, 0, 0, 0, 9,17,12,12,17,19,20,13,
6,11,17,11, 5, 5, 8,10, 6, 5, 6, 6, 3, 7, 9, 7, 6, 8,12,10, 4, 8, 6, 6,11,16,16,15,16,17,17,16,
11, 9,10,10, 5, 6,12,10, 5, 1, 6,10, 5, 3, 3, 5, 4, 7,15,10, 7,13, 7, 8,15,11,15,15,15, 8,11,15,
};
// Just a 32x32 cloud rendered with the standard Photoshop filter
static BYTE pattern2[1024] =
{
9, 9, 8, 8, 8, 8, 6, 6,13,13,11,21,19,21,23,18,23,24,19,19,24,17,18,12, 9,14, 8,12,12, 5, 8, 6,
11,10, 6, 7, 8, 8, 9,13,10,11,17,15,23,22,23,22,20,26,27,26,17,21,20,14,12, 8,11, 8,11, 7, 8, 7,
6, 9,13,13,10, 9,13, 7,12,13,16,19,16,20,22,25,22,25,27,22,21,23,15,10,14,14,15,13,12, 8,12, 6,
6, 7,12,12,12,16, 9,12,12,15,16,11,21,24,19,24,23,26,28,27,26,21,14,15, 7, 7,10,15,12,11,10, 9,
7,14,11,16,12,18,16,14,16,14,11,14,15,21,23,17,20,18,26,24,27,18,20,11,11,14,10,17,17,10, 6,10,
13, 9,14,10,13,11,14,15,18,15,15,12,19,19,20,18,22,20,19,22,19,19,19,20,17,15,15,11,16,14,10, 8,
13,16,12,16,17,19,17,18,15,19,14,18,15,14,15,17,21,19,23,18,23,22,18,18,17,15,15,16,12,12,15,10,
10,12,14,10,16,11,18,15,21,20,20,17,18,19,16,19,14,20,19,14,19,25,22,21,22,24,18,12, 9, 9, 8, 6,
10,10,13, 9,15,13,20,19,22,18,18,17,17,21,21,13,13,12,19,18,16,17,27,26,22,23,20,17,12,11, 8, 9,
7,13,14,15,11,13,18,22,19,23,23,20,22,24,21,14,12,16,17,19,18,18,22,18,24,23,19,17,16,14, 8, 7,
12,12, 8, 8,16,20,26,25,28,28,22,29,23,22,21,18,13,16,15,15,20,17,25,24,19,17,17,17,15,10, 8, 9,
7,12,15,11,17,20,25,25,25,29,30,31,28,26,18,16,17,18,20,21,22,20,23,19,18,19,10,16,16,11,11, 8,
5, 6, 8,14,14,17,17,21,27,23,27,31,27,22,23,21,19,19,21,19,20,19,17,22,13,17,12,15,10,10,12, 6,
8, 9, 8,14,15,16,15,18,27,26,23,25,23,22,18,21,20,17,19,20,20,16,20,14,15,13,12, 8, 8, 7,11,13,
7, 6,11,11,11,13,15,22,25,24,26,22,24,26,23,18,24,24,20,18,20,16,17,12,12,12,10, 8,11, 9, 6, 8,
9,10, 9, 6, 5,14,16,19,17,21,26,20,23,19,19,17,20,21,26,25,23,21,17,13,12, 5,13,11, 7,12,10,12,
6, 5, 4,10,11, 9,10,13,17,20,20,18,23,26,27,20,21,24,20,19,24,20,18,10,11, 3, 6,13, 9, 6, 8, 8,
1, 2, 2,11,13,13,11,16,16,16,19,21,20,23,22,28,21,20,19,18,23,16,18, 7, 5, 9, 7, 6, 5,10, 8, 8,
0, 0, 6, 9,11,15,12,12,19,18,19,26,22,24,26,30,23,22,22,16,20,19,12,12, 3, 4, 6, 5, 4, 7, 2, 4,
2, 0, 0, 7,11, 8,14,13,15,21,26,28,25,24,27,26,23,24,22,22,15,17,12, 8,10, 7, 7, 4, 0, 5, 0, 1,
1, 2, 0, 1, 9,14,13,10,19,24,22,29,30,28,30,30,31,23,24,19,17,14,13, 8, 8, 8, 1, 4, 0, 0, 0, 3,
5, 2, 4, 2, 9, 8, 8, 8,18,23,20,27,30,27,31,25,28,30,28,24,24,15,11,14,10, 3, 4, 3, 0, 0, 1, 3,
9, 3, 4, 3, 5, 6, 8,13,14,23,21,27,28,27,28,27,27,29,30,24,22,23,13,15, 8, 6, 2, 0, 4, 3, 4, 1,
6, 5, 5, 3, 9, 3, 6,14,13,16,23,26,28,23,30,31,28,29,26,27,21,20,15,15,13, 9, 1, 0, 2, 0, 5, 8,
8, 4, 3, 7, 2, 0,10, 7,10,14,21,21,29,28,25,27,30,28,25,24,27,22,19,13,10, 5, 0, 0, 0, 0, 0, 7,
7, 6, 7, 0, 2, 2, 5, 6,15,11,19,24,22,29,27,31,30,30,31,28,23,18,14,14, 7, 5, 0, 0, 1, 0, 1, 0,
5, 5, 5, 0, 0, 4, 5,11, 7,10,13,20,21,21,28,31,28,30,26,28,25,21, 9,12, 3, 3, 0, 2, 2, 2, 0, 1,
3, 3, 0, 2, 0, 3, 5, 3,11,11,16,19,19,27,26,26,30,27,28,26,23,22,16, 6, 2, 2, 3, 2, 0, 2, 4, 0,
0, 0, 0, 3, 3, 1, 0, 4, 5, 9,11,16,24,20,28,26,28,24,28,25,22,21,16, 5, 7, 5, 7, 3, 2, 3, 3, 6,
0, 0, 2, 0, 2, 0, 4, 3, 8,12, 9,17,16,23,23,27,27,22,26,22,21,21,13,14, 5, 3, 7, 3, 2, 4, 6, 1,
2, 5, 6, 4, 0, 1, 5, 8, 7, 6,15,17,22,20,24,28,23,25,20,21,18,16,13,15,13,10, 8, 5, 5, 9, 3, 7,
7, 7, 0, 5, 1, 6, 7, 9,12, 9,12,21,22,25,24,22,23,25,24,18,24,22,17,13,10, 9,10, 9, 6,11, 6, 5,
};
const FTexture::Span FBackdropTexture::DummySpan[2] = { { 0, 160 }, { 0, 0 } };
//=============================================================================
//
//
//
//=============================================================================
FBackdropTexture::FBackdropTexture()
{
Width = 144;
Height = 160;
WidthBits = 8;
HeightBits = 8;
WidthMask = 255;
LastRenderTic = 0;
time1 = ANGLE_1*180;
time2 = ANGLE_1*56;
time3 = ANGLE_1*99;
time4 = ANGLE_1*1;
t1ang = ANGLE_90;
t2ang = 0;
z1ang = 0;
z2ang = ANGLE_90/2;
}
//=============================================================================
//
//
//
//=============================================================================
bool FBackdropTexture::CheckModified()
{
return LastRenderTic != gametic;
}
void FBackdropTexture::Unload()
{
}
//=============================================================================
//
//
//
//=============================================================================
const BYTE *FBackdropTexture::GetColumn(unsigned int column, const Span **spans_out)
{
if (LastRenderTic != gametic)
{
Render();
}
column = clamp(column, 0u, 143u);
if (spans_out != NULL)
{
*spans_out = DummySpan;
}
return Pixels + column*160;
}
//=============================================================================
//
//
//
//=============================================================================
const BYTE *FBackdropTexture::GetPixels()
{
if (LastRenderTic != gametic)
{
Render();
}
return Pixels;
}
//=============================================================================
//
// This is one plasma and two rotozoomers. I think it turned out quite awesome.
//
//=============================================================================
void FBackdropTexture::Render()
{
BYTE *from;
int width, height, pitch;
width = 160;
height = 144;
pitch = width;
int x, y;
const angle_t a1add = ANGLE_1/2;
const angle_t a2add = ANGLE_MAX-ANGLE_1;
const angle_t a3add = ANGLE_1*5/7;
const angle_t a4add = ANGLE_MAX-ANGLE_1*4/3;
const angle_t t1add = ANGLE_MAX-ANGLE_1*2;
const angle_t t2add = ANGLE_MAX-ANGLE_1*3+ANGLE_1/6;
const angle_t t3add = ANGLE_1*16/7;
const angle_t t4add = ANGLE_MAX-ANGLE_1*2/3;
const angle_t x1add = 5<<ANGLETOFINESHIFT;
const angle_t x2add = ANGLE_MAX-(13<<ANGLETOFINESHIFT);
const angle_t z1add = 3<<ANGLETOFINESHIFT;
const angle_t z2add = 4<<ANGLETOFINESHIFT;
angle_t a1, a2, a3, a4;
fixed_t c1, c2, c3, c4;
DWORD tx, ty, tc, ts;
DWORD ux, uy, uc, us;
DWORD ltx, lty, lux, luy;
from = Pixels;
a3 = time3;
a4 = time4;
fixed_t z1 = (finecosine[z2ang>>ANGLETOFINESHIFT]>>2)+FRACUNIT/2;
fixed_t z2 = (finecosine[z1ang>>ANGLETOFINESHIFT]>>2)+FRACUNIT*3/4;
tc = MulScale5 (finecosine[t1ang>>ANGLETOFINESHIFT], z1);
ts = MulScale5 (finesine[t1ang>>ANGLETOFINESHIFT], z1);
uc = MulScale5 (finecosine[t2ang>>ANGLETOFINESHIFT], z2);
us = MulScale5 (finesine[t2ang>>ANGLETOFINESHIFT], z2);
ltx = -width/2*tc;
lty = -width/2*ts;
lux = -width/2*uc;
luy = -width/2*us;
for (y = 0; y < height; ++y)
{
a1 = time1;
a2 = time2;
c3 = finecosine[a3>>ANGLETOFINESHIFT];
c4 = finecosine[a4>>ANGLETOFINESHIFT];
tx = ltx - (y-height/2)*ts;
ty = lty + (y-height/2)*tc;
ux = lux - (y-height/2)*us;
uy = luy + (y-height/2)*uc;
for (x = 0; x < width; ++x)
{
c1 = finecosine[a1>>ANGLETOFINESHIFT];
c2 = finecosine[a2>>ANGLETOFINESHIFT];
from[x] = ((c1 + c2 + c3 + c4) >> (FRACBITS+3-7)) + 128 // plasma
+ pattern1[(tx>>27)+((ty>>22)&992)] // rotozoomer 1
+ pattern2[(ux>>27)+((uy>>22)&992)]; // rotozoomer 2
tx += tc;
ty += ts;
ux += uc;
uy += us;
a1 += a1add;
a2 += a2add;
}
a3 += a3add;
a4 += a4add;
from += pitch;
}
time1 += t1add;
time2 += t2add;
time3 += t3add;
time4 += t4add;
t1ang += x1add;
t2ang += x2add;
z1ang += z1add;
z2ang += z2add;
LastRenderTic = gametic;
}
//=============================================================================
//
//
//
//=============================================================================
FListMenuItemPlayerDisplay::FListMenuItemPlayerDisplay(FListMenuDescriptor *menu, int x, int y, PalEntry c1, PalEntry c2, bool np, FName action)
: FListMenuItem(x, y, action)
{
mOwner = menu;
for (int i = 0; i < 256; i++)
{
int r = c1.r + c2.r * i / 255;
int g = c1.g + c2.g * i / 255;
int b = c1.b + c2.b * i / 255;
mRemap.Remap[i] = ColorMatcher.Pick (r, g, b);
mRemap.Palette[i] = PalEntry(255, r, g, b);
}
mBackdrop = new FBackdropTexture;
mPlayerClass = NULL;
mPlayerState = NULL;
mNoportrait = np;
mMode = 0;
mRotation = 0;
mTranslate = false;
mSkin = 0;
mRandomClass = 0;
mRandomTimer = 0;
mClassNum = -1;
}
//=============================================================================
//
//
//
//=============================================================================
FListMenuItemPlayerDisplay::~FListMenuItemPlayerDisplay()
{
delete mBackdrop;
}
//=============================================================================
//
//
//
//=============================================================================
void FListMenuItemPlayerDisplay::UpdateRandomClass()
{
if (--mRandomTimer < 0)
{
if (++mRandomClass >= (int)PlayerClasses.Size ()) mRandomClass = 0;
mPlayerClass = &PlayerClasses[mRandomClass];
mPlayerState = GetDefaultByType (mPlayerClass->Type)->SeeState;
mPlayerTics = mPlayerState->GetTics();
mRandomTimer = 6;
}
}
//=============================================================================
//
//
//
//=============================================================================
void FListMenuItemPlayerDisplay::SetPlayerClass(int classnum, bool force)
{
if (classnum < 0 || classnum >= (int)PlayerClasses.Size ())
{
if (mClassNum != -1)
{
mClassNum = -1;
mRandomTimer = 0;
UpdateRandomClass();
}
}
else if (mPlayerClass != &PlayerClasses[classnum] || force)
{
mPlayerClass = &PlayerClasses[classnum];
mPlayerState = GetDefaultByType (mPlayerClass->Type)->SeeState;
mPlayerTics = mPlayerState->GetTics();
mClassNum = classnum;
}
}
//=============================================================================
//
//
//
//=============================================================================
bool FListMenuItemPlayerDisplay::UpdatePlayerClass()
{
int classnum;
FName seltype = mOwner->mItems[mOwner->mSelectedItem]->GetAction(&classnum);
if (seltype != NAME_Episodemenu) return false;
if (PlayerClasses.Size() == 0) return false;
SetPlayerClass(classnum);
return true;
}
//=============================================================================
//
//
//
//=============================================================================
bool FListMenuItemPlayerDisplay::SetValue(int i, int value)
{
switch (i)
{
case PDF_MODE:
mMode = value;
return true;
case PDF_ROTATION:
mRotation = value;
return true;
case PDF_TRANSLATE:
mTranslate = value;
case PDF_CLASS:
SetPlayerClass(value, true);
break;
case PDF_SKIN:
mSkin = value;
break;
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
void FListMenuItemPlayerDisplay::Ticker()
{
if (mClassNum < 0) UpdateRandomClass();
if (mPlayerState != NULL && mPlayerState->GetTics () != -1 && mPlayerState->GetNextState () != NULL)
{
if (--mPlayerTics <= 0)
{
mPlayerState = mPlayerState->GetNextState();
mPlayerTics = mPlayerState->GetTics();
}
}
}
//=============================================================================
//
//
//
//=============================================================================
void FListMenuItemPlayerDisplay::Drawer(bool selected)
{
if (mMode == 0 && !UpdatePlayerClass())
{
return;
}
const char *portrait = mPlayerClass->Type->Meta.GetMetaString(APMETA_Portrait);
if (portrait != NULL && !mNoportrait)
{
FTextureID texid = TexMan.CheckForTexture(portrait, FTexture::TEX_MiscPatch);
if (texid.isValid())
{
FTexture *tex = TexMan(texid);
if (tex != NULL)
{
screen->DrawTexture (tex, mXpos, mYpos, DTA_Clean, true, TAG_DONE);
return;
}
}
}
int x = (mXpos - 160) * CleanXfac + (SCREENWIDTH>>1);
int y = (mYpos - 100) * CleanYfac + (SCREENHEIGHT>>1);
screen->DrawTexture (mBackdrop, x, y - 1,
DTA_DestWidth, 72 * CleanXfac,
DTA_DestHeight, 80 * CleanYfac,
DTA_Translation, &mRemap,
DTA_Masked, true,
TAG_DONE);
V_DrawFrame (x, y, 72*CleanXfac, 80*CleanYfac-1);
spriteframe_t *sprframe;
fixed_t scaleX, scaleY;
if (mSkin == 0)
{
sprframe = &SpriteFrames[sprites[mPlayerState->sprite].spriteframes + mPlayerState->GetFrame()];
scaleX = GetDefaultByType(mPlayerClass->Type)->scaleX;
scaleY = GetDefaultByType(mPlayerClass->Type)->scaleY;
}
else
{
sprframe = &SpriteFrames[sprites[skins[mSkin].sprite].spriteframes + mPlayerState->GetFrame()];
scaleX = skins[mSkin].ScaleX;
scaleY = skins[mSkin].ScaleY;
}
if (sprframe != NULL)
{
FTexture *tex = TexMan(sprframe->Texture[mRotation]);
if (tex != NULL && tex->UseType != FTexture::TEX_Null)
{
FRemapTable *trans = NULL;
if (mTranslate) trans = translationtables[TRANSLATION_Players](MAXPLAYERS);
screen->DrawTexture (tex,
x + 36*CleanXfac, y + 71*CleanYfac,
DTA_DestWidth, MulScale16 (tex->GetWidth() * CleanXfac, scaleX),
DTA_DestHeight, MulScale16 (tex->GetHeight() * CleanYfac, scaleY),
DTA_Translation, trans,
TAG_DONE);
}
}
}

1142
src/menu/playermenu.cpp Normal file

File diff suppressed because it is too large Load diff

154
src/menu/readthis.cpp Normal file
View file

@ -0,0 +1,154 @@
/*
** readthis.cpp
** Help screens
**
**---------------------------------------------------------------------------
** Copyright 2001-2010 Randy Heit
** Copyright 2010 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "menu/menu.h"
#include "v_video.h"
#include "g_level.h"
#include "gi.h"
#include "textures/textures.h"
class DReadThisMenu : public DMenu
{
DECLARE_CLASS(DReadThisMenu, DMenu)
int mScreen;
int mInfoTic;
public:
DReadThisMenu(DMenu *parent = NULL);
void Drawer();
bool MenuEvent(int mkey, bool fromcontroller);
bool DimAllowed () { return false; }
bool MouseEvent(int type, int x, int y);
};
IMPLEMENT_CLASS(DReadThisMenu)
//=============================================================================
//
// Read This Menus
//
//=============================================================================
DReadThisMenu::DReadThisMenu(DMenu *parent)
: DMenu(parent)
{
mScreen = 1;
mInfoTic = gametic;
}
//=============================================================================
//
//
//
//=============================================================================
void DReadThisMenu::Drawer()
{
FTexture *tex = NULL, *prevpic = NULL;
fixed_t alpha;
// Did the mapper choose a custom help page via MAPINFO?
if ((level.info != NULL) && level.info->f1[0] != 0)
{
tex = TexMan.FindTexture(level.info->f1);
mScreen = 1;
}
if (tex == NULL)
{
tex = TexMan[gameinfo.infoPages[mScreen-1].GetChars()];
}
if (mScreen > 1)
{
prevpic = TexMan[gameinfo.infoPages[mScreen-2].GetChars()];
}
alpha = MIN<fixed_t> (Scale (gametic - mInfoTic, OPAQUE, TICRATE/3), OPAQUE);
if (alpha < OPAQUE && prevpic != NULL)
{
screen->DrawTexture (prevpic, 0, 0,
DTA_DestWidth, screen->GetWidth(),
DTA_DestHeight, screen->GetHeight(),
TAG_DONE);
}
screen->DrawTexture (tex, 0, 0,
DTA_DestWidth, screen->GetWidth(),
DTA_DestHeight, screen->GetHeight(),
DTA_Alpha, alpha,
TAG_DONE);
}
//=============================================================================
//
//
//
//=============================================================================
bool DReadThisMenu::MenuEvent(int mkey, bool fromcontroller)
{
if (mkey == MKEY_Enter)
{
S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
mScreen++;
mInfoTic = gametic;
if ((level.info != NULL && level.info->f1[0] != 0) || mScreen > int(gameinfo.infoPages.Size()))
{
Close();
}
return true;
}
else return Super::MenuEvent(mkey, fromcontroller);
}
//=============================================================================
//
//
//
//=============================================================================
bool DReadThisMenu::MouseEvent(int type, int x, int y)
{
if (type == MOUSE_Click)
{
return MenuEvent(MKEY_Enter, true);
}
return false;
}

442
src/menu/videomenu.cpp Normal file
View file

@ -0,0 +1,442 @@
/*
** videomenu.cpp
** The video modes menu
**
**---------------------------------------------------------------------------
** Copyright 2001-2010 Randy Heit
** Copyright 2010 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include <float.h>
#include "menu/menu.h"
#include "c_dispatch.h"
#include "w_wad.h"
#include "sc_man.h"
#include "v_font.h"
#include "g_level.h"
#include "d_player.h"
#include "v_video.h"
#include "gi.h"
#include "i_system.h"
#include "c_bind.h"
#include "v_palette.h"
#include "d_event.h"
#include "d_gui.h"
#include "i_music.h"
#include "m_joy.h"
#include "sbar.h"
#include "hardware.h"
#define NO_IMP
#include "optionmenuitems.h"
/*=======================================
*
* Video Modes Menu
*
*=======================================*/
static void BuildModesList (int hiwidth, int hiheight, int hi_id);
static bool GetSelectedSize (int *width, int *height);
static void SetModesMenu (int w, int h, int bits);
FOptionMenuDescriptor *GetVideoModeMenu();
extern bool setmodeneeded;
extern int NewWidth, NewHeight, NewBits;
extern int DisplayBits;
EXTERN_CVAR (Int, vid_defwidth)
EXTERN_CVAR (Int, vid_defheight)
EXTERN_CVAR (Int, vid_defbits)
EXTERN_CVAR (Bool, fullscreen)
EXTERN_CVAR (Bool, vid_tft) // Defined below
int testingmode; // Holds time to revert to old mode
int OldWidth, OldHeight, OldBits;
static FIntCVar DummyDepthCvar (NULL, 0, 0);
static BYTE BitTranslate[32];
CUSTOM_CVAR (Int, menu_screenratios, 0, CVAR_ARCHIVE)
{
if (self < 0 || self > 4)
{
self = 3;
}
else if (self == 4 && !vid_tft)
{
self = 3;
}
else
{
BuildModesList (SCREENWIDTH, SCREENHEIGHT, DisplayBits);
}
}
CUSTOM_CVAR (Bool, vid_tft, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
{
FOptionMenuDescriptor *opt = GetVideoModeMenu();
if (opt != NULL)
{
FOptionMenuItem *it = opt->GetItem("menu_screenratios");
if (it != NULL)
{
if (self)
{
it->SetString(FOptionMenuItemOptionBase::OP_VALUES, "RatiosTFT");
}
else
{
it->SetString(FOptionMenuItemOptionBase::OP_VALUES, "Ratios");
}
}
}
setsizeneeded = true;
if (StatusBar != NULL)
{
StatusBar->ScreenSizeChanged();
}
}
//=============================================================================
//
//
//
//=============================================================================
class DVideoModeMenu : public DOptionMenu
{
DECLARE_CLASS(DVideoModeMenu, DOptionMenu)
public:
DVideoModeMenu()
{
SetModesMenu (SCREENWIDTH, SCREENHEIGHT, DisplayBits);
}
bool MenuEvent(int mkey, bool fromcontroller)
{
if ((mkey == MKEY_Up || mkey == MKEY_Down) && mDesc->mSelectedItem >= 0 &&
mDesc->mSelectedItem < (int)mDesc->mItems.Size())
{
int sel;
bool selected = mDesc->mItems[mDesc->mSelectedItem]->GetValue(FOptionMenuScreenResolutionLine::SRL_SELECTION, &sel);
bool res = Super::MenuEvent(mkey, fromcontroller);
if (selected) mDesc->mItems[mDesc->mSelectedItem]->SetValue(FOptionMenuScreenResolutionLine::SRL_SELECTION, sel);
return res;
}
return Super::MenuEvent(mkey, fromcontroller);
}
bool Responder(event_t *ev)
{
if (ev->type == EV_GUI_Event && ev->subtype == EV_GUI_KeyDown &&
(ev->data1 == 't' || ev->data1 == 'T'))
{
if (!GetSelectedSize (&NewWidth, &NewHeight))
{
NewWidth = SCREENWIDTH;
NewHeight = SCREENHEIGHT;
}
OldWidth = SCREENWIDTH;
OldHeight = SCREENHEIGHT;
OldBits = DisplayBits;
NewBits = BitTranslate[DummyDepthCvar];
setmodeneeded = true;
testingmode = I_GetTime(false) + 5 * TICRATE;
S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
SetModesMenu (NewWidth, NewHeight, NewBits);
return true;
}
return Super::Responder(ev);
}
};
IMPLEMENT_CLASS(DVideoModeMenu)
//=============================================================================
//
//
//
//=============================================================================
FOptionMenuDescriptor *GetVideoModeMenu()
{
FMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_VideoModeMenu);
if (desc != NULL && (*desc)->mType == MDESC_OptionsMenu)
{
return (FOptionMenuDescriptor *)*desc;
}
return NULL;
}
//=============================================================================
//
// Set some stuff up for the video modes menu
//
//=============================================================================
static void BuildModesList (int hiwidth, int hiheight, int hi_bits)
{
char strtemp[32];
int i, c;
int width, height, showbits;
bool letterbox=false;
int ratiomatch;
if (menu_screenratios >= 0 && menu_screenratios <= 4 && menu_screenratios != 3)
{
ratiomatch = menu_screenratios;
}
else
{
ratiomatch = -1;
}
showbits = BitTranslate[DummyDepthCvar];
if (Video != NULL)
{
Video->StartModeIterator (showbits, screen->IsFullscreen());
}
FOptionMenuDescriptor *opt = GetVideoModeMenu();
if (opt != NULL)
{
for (i = NAME_res_0; i<= NAME_res_9; i++)
{
FOptionMenuItem *it = opt->GetItem((ENamedName)i);
if (it != NULL)
{
it->SetValue(FOptionMenuScreenResolutionLine::SRL_HIGHLIGHT, -1);
for (c = 0; c < 3; c++)
{
bool haveMode = false;
if (Video != NULL)
{
while ((haveMode = Video->NextMode (&width, &height, &letterbox)) &&
(ratiomatch >= 0 && CheckRatio (width, height) != ratiomatch))
{
}
}
if (haveMode)
{
if (width == hiwidth && height == hiheight)
{
it->SetValue(FOptionMenuScreenResolutionLine::SRL_SELECTION, c);
it->SetValue(FOptionMenuScreenResolutionLine::SRL_HIGHLIGHT, c);
}
mysnprintf (strtemp, countof(strtemp), "%dx%d%s", width, height, letterbox?TEXTCOLOR_BROWN" LB":"");
it->SetString(FOptionMenuScreenResolutionLine::SRL_INDEX+c, strtemp);
}
else
{
it->SetString(FOptionMenuScreenResolutionLine::SRL_INDEX+c, "");
}
}
}
}
}
}
//=============================================================================
//
//
//
//=============================================================================
void M_RestoreMode ()
{
NewWidth = OldWidth;
NewHeight = OldHeight;
NewBits = OldBits;
setmodeneeded = true;
testingmode = 0;
SetModesMenu (OldWidth, OldHeight, OldBits);
}
void M_SetDefaultMode ()
{
// Make current resolution the default
vid_defwidth = SCREENWIDTH;
vid_defheight = SCREENHEIGHT;
vid_defbits = DisplayBits;
testingmode = 0;
SetModesMenu (SCREENWIDTH, SCREENHEIGHT, DisplayBits);
}
//=============================================================================
//
//
//
//=============================================================================
void M_RefreshModesList ()
{
BuildModesList (SCREENWIDTH, SCREENHEIGHT, DisplayBits);
}
void M_InitVideoModesMenu ()
{
int dummy1, dummy2;
size_t currval = 0;
M_RefreshModesList();
for (unsigned int i = 1; i <= 32 && currval < countof(BitTranslate); i++)
{
Video->StartModeIterator (i, screen->IsFullscreen());
if (Video->NextMode (&dummy1, &dummy2, NULL))
{
BitTranslate[currval++] = i;
}
}
/* It doesn't look like this can be anything but DISPLAY_Both, regardless of any other settings.
switch (Video->GetDisplayType ())
{
case DISPLAY_FullscreenOnly:
case DISPLAY_WindowOnly:
// todo: gray out fullscreen option
default:
break;
}
*/
}
//=============================================================================
//
//
//
//=============================================================================
static bool GetSelectedSize (int *width, int *height)
{
FOptionMenuDescriptor *opt = GetVideoModeMenu();
if (opt != NULL)
{
int line = opt->mSelectedItem;
int hsel;
FOptionMenuItem *it = opt->mItems[line];
if (it->GetValue(FOptionMenuScreenResolutionLine::SRL_SELECTION, &hsel))
{
char buffer[32];
char *breakpt;
if (it->GetString(FOptionMenuScreenResolutionLine::SRL_INDEX+hsel, buffer, sizeof(buffer)))
{
*width = strtol (buffer, &breakpt, 10);
*height = strtol (breakpt+1, NULL, 10);
return true;
}
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
void M_SetVideoMode()
{
if (!GetSelectedSize (&NewWidth, &NewHeight))
{
NewWidth = SCREENWIDTH;
NewHeight = SCREENHEIGHT;
}
else
{
testingmode = 1;
setmodeneeded = true;
NewBits = BitTranslate[DummyDepthCvar];
}
SetModesMenu (NewWidth, NewHeight, NewBits);
}
//=============================================================================
//
//
//
//=============================================================================
static int FindBits (int bits)
{
int i;
for (i = 0; i < 22; i++)
{
if (BitTranslate[i] == bits)
return i;
}
return 0;
}
static void SetModesMenu (int w, int h, int bits)
{
DummyDepthCvar = FindBits (bits);
FOptionMenuDescriptor *opt = GetVideoModeMenu();
if (opt != NULL)
{
FOptionMenuItem *it;
if (testingmode <= 1)
{
it = opt->GetItem(NAME_VMEnterText);
if (it != NULL) it->SetValue(0, 0);
it = opt->GetItem(NAME_VMTestText);
if (it != NULL) it->SetValue(0, 0);
}
else
{
it = opt->GetItem(NAME_VMTestText);
if (it != NULL) it->SetValue(0, 1);
it = opt->GetItem(NAME_VMEnterText);
if (it != NULL)
{
char strtemp[64];
mysnprintf (strtemp, countof(strtemp), "TESTING %dx%dx%d", w, h, bits);
it->SetValue(0, 1);
it->SetString(0, strtemp);
}
}
}
BuildModesList (w, h, bits);
}

View file

@ -195,9 +195,9 @@ bool ProduceMIDI (const BYTE *musBuf, int len, TArray<BYTE> &outFile)
switch (event & 0x70)
{
case MUS_NOTEOFF:
midStatus |= MIDI_NOTEOFF;
midStatus |= MIDI_NOTEON;
mid1 = t & 127;
mid2 = 64;
mid2 = 0;
break;
case MUS_NOTEON:

View file

@ -75,7 +75,4 @@ typedef struct
// WORD UsedInstruments[NumInstruments];
} MUSHeader;
bool ProduceMIDI (const BYTE *musBuf, int len, TArray<BYTE> &outFile);
bool ProduceMIDI (const BYTE *musBuf, int len, FILE *outFile);
#endif //__MUS2MIDI_H__

View file

@ -383,6 +383,7 @@ xx(Firstsideonly)
xx(Transparent)
xx(Passuse)
xx(Repeatspecial)
xx(Conversation)
xx(Playercross)
xx(Playeruse)
@ -417,6 +418,7 @@ xx(Gravity)
xx(Lightcolor)
xx(Fadecolor)
xx(Desaturation)
xx(SoundSequence)
xx(Silent)
xx(Nofallingdamage)
xx(Dropactors)
@ -440,5 +442,87 @@ xx(nofakecontrast)
xx(smoothlighting)
xx(blockprojectiles)
xx(blockuse)
xx(hidden)
xx(Renderstyle)
// USDF keywords
xx(Amount)
xx(Text)
xx(Displaycost)
xx(Yesmessage)
xx(Nomessage)
xx(Log)
xx(Giveitem)
xx(Nextpage)
xx(Closedialog)
xx(Cost)
xx(Page)
xx(Count)
xx(Name)
xx(Panel)
xx(Dialog)
xx(Ifitem)
xx(Choice)
xx(Link)
// Special menus
xx(Mainmenu)
xx(Episodemenu)
xx(Playerclassmenu)
xx(HexenDefaultPlayerclassmenu)
xx(Skillmenu)
xx(Startgame)
xx(StartgameConfirm)
xx(Loadgamemenu)
xx(Savegamemenu)
xx(Readthismenu)
xx(Optionsmenu)
xx(Quitmenu)
xx(Savemenu)
xx(Playermenu)
xx(Playerbox)
xx(Team)
xx(Color)
xx(Red)
xx(Green)
xx(Blue)
xx(Class)
xx(Skin)
xx(Gender)
xx(Autoaim)
xx(Switch)
xx(Playerdisplay)
xx(Controlmessage)
xx(Crosshairs)
xx(Colorpickermenu)
xx(Mididevices)
xx(CustomizeControls)
xx(MessageOptions)
xx(AutomapOptions)
xx(ScoreboardOptions)
xx(MapColorMenu)
xx(GameplayOptions)
xx(CompatibilityOptions)
xx(MouseOptions)
xx(JoystickOptions)
xx(SoundOptions)
xx(AdvSoundOptions)
xx(ModReplayerOptions)
xx(VideoOptions)
xx(JoystickConfigMenu)
xx(VMEnterText)
xx(VMTestText)
xx(VideoModeMenu)
xx(res_0)
xx(res_1)
xx(res_2)
xx(res_3)
xx(res_4)
xx(res_5)
xx(res_6)
xx(res_7)
xx(res_8)
xx(res_9)
xx(AlwaysRun)

View file

@ -62,10 +62,17 @@ const int AAPreference = 16;
#define D(x) do{}while(0)
#endif
FNodeBuilder::FNodeBuilder(FLevel &level)
: Level(level), GLNodes(false), SegsStuffed(0)
{
VertexMap = NULL;
}
FNodeBuilder::FNodeBuilder (FLevel &level,
TArray<FPolyStart> &polyspots, TArray<FPolyStart> &anchors,
bool makeGLNodes, bool enableSSE2)
: Level(level), GLNodes(makeGLNodes), EnableSSE2(enableSSE2), SegsStuffed(0)
bool makeGLNodes)
: Level(level), GLNodes(makeGLNodes), SegsStuffed(0)
{
VertexMap = new FVertexMap (*this, Level.MinX, Level.MinY, Level.MaxX, Level.MaxY);
FindUsedVertices (Level.Vertices, Level.NumVertices);
@ -83,6 +90,33 @@ FNodeBuilder::~FNodeBuilder()
}
}
void FNodeBuilder::BuildMini(bool makeGLNodes)
{
GLNodes = makeGLNodes;
GroupSegPlanesSimple();
BuildTree();
}
void FNodeBuilder::Clear()
{
SegsStuffed = 0;
Nodes.Clear();
Subsectors.Clear();
SubsectorSets.Clear();
Segs.Clear();
Vertices.Clear();
SegList.Clear();
PlaneChecked.Clear();
Planes.Clear();
Touched.Clear();
Colinear.Clear();
SplitSharers.Clear();
if (VertexMap == NULL)
{
VertexMap = new FVertexMapSimple(*this);
}
}
void FNodeBuilder::BuildTree ()
{
fixed_t bbox[4];
@ -90,35 +124,38 @@ void FNodeBuilder::BuildTree ()
C_InitTicker ("Building BSP", FRACUNIT);
HackSeg = DWORD_MAX;
HackMate = DWORD_MAX;
CreateNode (0, bbox);
CreateNode (0, Segs.Size(), bbox);
CreateSubsectorsForReal ();
C_InitTicker (NULL, 0);
}
int FNodeBuilder::CreateNode (DWORD set, fixed_t bbox[4])
int FNodeBuilder::CreateNode (DWORD set, unsigned int count, fixed_t bbox[4])
{
node_t node;
int skip, count, selstat;
int skip, selstat;
DWORD splitseg;
count = CountSegs (set);
skip = count / MaxSegs;
skip = int(count / MaxSegs);
// When building GL nodes, count may not be an exact count of the number of segs
// in the set. That's okay, because we just use it to get a skip count, so an
// estimate is fine.
if ((selstat = SelectSplitter (set, node, splitseg, skip, true)) > 0 ||
(skip > 0 && (selstat = SelectSplitter (set, node, splitseg, 1, true)) > 0) ||
(selstat < 0 && (SelectSplitter (set, node, splitseg, skip, false) > 0 ||
(skip > 0 && SelectSplitter (set, node, splitseg, 1, false)))) ||
CheckSubsector (set, node, splitseg, count))
CheckSubsector (set, node, splitseg))
{
// Create a normal node
DWORD set1, set2;
unsigned int count1, count2;
SplitSegs (set, node, splitseg, set1, set2);
SplitSegs (set, node, splitseg, set1, set2, count1, count2);
D(PrintSet (1, set1));
D(Printf (PRINT_LOG, "(%d,%d) delta (%d,%d) from seg %d\n", node.x>>16, node.y>>16, node.dx>>16, node.dy>>16, splitseg));
D(PrintSet (2, set2));
node.intchildren[0] = CreateNode (set1, node.bbox[0]);
node.intchildren[1] = CreateNode (set2, node.bbox[1]);
node.intchildren[0] = CreateNode (set1, count1, node.bbox[0]);
node.intchildren[1] = CreateNode (set2, count2, node.bbox[1]);
bbox[BOXTOP] = MAX (node.bbox[0][BOXTOP], node.bbox[1][BOXTOP]);
bbox[BOXBOTTOM] = MIN (node.bbox[0][BOXBOTTOM], node.bbox[1][BOXBOTTOM]);
bbox[BOXLEFT] = MIN (node.bbox[0][BOXLEFT], node.bbox[1][BOXLEFT]);
@ -173,17 +210,15 @@ void FNodeBuilder::CreateSubsectorsForReal ()
subsector_t sub;
unsigned int i;
sub.poly = NULL;
sub.validcount = 0;
sub.CenterX = 0; // Code in p_setup.cpp will set these for us later.
sub.CenterY = 0;
sub.sector = NULL;
sub.polys = NULL;
sub.BSP = NULL;
for (i = 0; i < SubsectorSets.Size(); ++i)
{
DWORD set = SubsectorSets[i];
DWORD firstline = (DWORD)SegList.Size();
sub.firstline = (DWORD)SegList.Size();
while (set != DWORD_MAX)
{
USegPtr ptr;
@ -192,14 +227,15 @@ void FNodeBuilder::CreateSubsectorsForReal ()
SegList.Push (ptr);
set = ptr.SegPtr->next;
}
sub.numlines = (DWORD)(SegList.Size() - sub.firstline);
sub.numlines = (DWORD)(SegList.Size() - firstline);
sub.firstline = (seg_t *)(size_t)firstline;
// Sort segs by linedef for special effects
qsort (&SegList[sub.firstline], sub.numlines, sizeof(USegPtr), SortSegs);
qsort (&SegList[firstline], sub.numlines, sizeof(USegPtr), SortSegs);
// Convert seg pointers into indices
D(Printf (PRINT_LOG, "Output subsector %d:\n", Subsectors.Size()));
for (unsigned int i = sub.firstline; i < SegList.Size(); ++i)
for (unsigned int i = firstline; i < SegList.Size(); ++i)
{
D(Printf (PRINT_LOG, " Seg %5d%c(%5d,%5d)-(%5d,%5d)\n", SegList[i].SegPtr - &Segs[0],
SegList[i].SegPtr->linedef == -1 ? '+' : ' ',
@ -273,24 +309,12 @@ int STACK_ARGS FNodeBuilder::SortSegs (const void *a, const void *b)
}
}
int FNodeBuilder::CountSegs (DWORD set) const
{
int count = 0;
while (set != DWORD_MAX)
{
count++;
set = Segs[set].next;
}
return count;
}
// Given a set of segs, checks to make sure they all belong to a single
// sector. If so, false is returned, and they become a subsector. If not,
// a splitter is synthesized, and true is returned to continue processing
// down this branch of the tree.
bool FNodeBuilder::CheckSubsector (DWORD set, node_t &node, DWORD &splitseg, int setsize)
bool FNodeBuilder::CheckSubsector (DWORD set, node_t &node, DWORD &splitseg)
{
sector_t *sec;
DWORD seg;
@ -371,7 +395,7 @@ bool FNodeBuilder::CheckSubsectorOverlappingSegs (DWORD set, node_t &node, DWORD
{
if (Segs[seg2].linedef == -1)
{ // Do not put minisegs into a new subsector.
swap (seg1, seg2);
swapvalues (seg1, seg2);
}
D(Printf(PRINT_LOG, "Need to synthesize a splitter for set %d on seg %d (ov)\n", set, seg2));
splitseg = DWORD_MAX;
@ -506,7 +530,7 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit)
int realSegs[2] = { 0, 0 };
int specialSegs[2] = { 0, 0 };
DWORD i = set;
int sidev1, sidev2;
int sidev[2];
int side;
bool splitter = false;
unsigned int max, m2, p, q;
@ -525,7 +549,7 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit)
}
else
{
side = ClassifyLine (node, test, sidev1, sidev2);
side = ClassifyLine (node, &Vertices[test->v1], &Vertices[test->v2], sidev);
}
switch (side)
{
@ -535,9 +559,9 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit)
// The "right" thing to do in this case is to only reject it if there is
// another nosplit seg from the same sector at this vertex. Note that a line
// that lies exactly on top of the splitter is okay.
if (test->loopnum && honorNoSplit && (sidev1 == 0 || sidev2 == 0))
if (test->loopnum && honorNoSplit && (sidev[0] == 0 || sidev[1] == 0))
{
if ((sidev1 | sidev2) != 0)
if ((sidev[0] | sidev[1]) != 0)
{
max = Touched.Size();
for (p = 0; p < max; ++p)
@ -735,8 +759,10 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit)
return score;
}
void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &outset0, DWORD &outset1)
void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &outset0, DWORD &outset1, unsigned int &count0, unsigned int &count1)
{
unsigned int _count0 = 0;
unsigned int _count1 = 0;
outset0 = DWORD_MAX;
outset1 = DWORD_MAX;
@ -749,18 +775,18 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou
FPrivSeg *seg = &Segs[set];
int next = seg->next;
int sidev1, sidev2, side;
int sidev[2], side;
if (HackSeg == set)
{
HackSeg = DWORD_MAX;
side = 1;
sidev1 = sidev2 = 0;
sidev[0] = sidev[1] = 0;
hack = true;
}
else
{
side = ClassifyLine (node, seg, sidev1, sidev2);
side = ClassifyLine (node, &Vertices[seg->v1], &Vertices[seg->v2], sidev);
hack = false;
}
@ -769,11 +795,13 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou
case 0: // seg is entirely in front
seg->next = outset0;
outset0 = set;
_count0++;
break;
case 1: // seg is entirely in back
seg->next = outset1;
outset1 = set;
_count1++;
break;
default: // seg needs to be split
@ -803,18 +831,20 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou
Printf("SelectVertexClose selected endpoint of seg %u\n", set);
}
seg2 = SplitSeg (set, vertnum, sidev1);
seg2 = SplitSeg (set, vertnum, sidev[0]);
Segs[seg2].next = outset0;
outset0 = seg2;
Segs[set].next = outset1;
outset1 = set;
_count0++;
_count1++;
// Also split the seg on the back side
if (Segs[set].partner != DWORD_MAX)
{
int partner1 = Segs[set].partner;
int partner2 = SplitSeg (partner1, vertnum, sidev2);
int partner2 = SplitSeg (partner1, vertnum, sidev[1]);
// The newly created seg stays in the same set as the
// back seg because it has not been considered for splitting
// yet. If it had been, then the front seg would have already
@ -835,17 +865,17 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou
}
if (side >= 0 && GLNodes)
{
if (sidev1 == 0)
if (sidev[0] == 0)
{
double dist1 = AddIntersection (node, seg->v1);
if (sidev2 == 0)
if (sidev[1] == 0)
{
double dist2 = AddIntersection (node, seg->v2);
FSplitSharer share = { dist1, set, dist2 > dist1 };
SplitSharers.Push (share);
}
}
else if (sidev2 == 0)
else if (sidev[1] == 0)
{
AddIntersection (node, seg->v2);
}
@ -881,6 +911,8 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou
{
AddMinisegs (node, splitseg, outset0, outset1);
}
count0 = _count0;
count1 = _count1;
}
void FNodeBuilder::SetNodeFromSeg (node_t &node, const FPrivSeg *pseg) const
@ -1038,3 +1070,93 @@ void FNodeBuilder::PrintSet (int l, DWORD set)
}
Printf (PRINT_LOG, "*\n");
}
#ifdef BACKPATCH
#ifdef _WIN32
extern "C" {
__declspec(dllimport) int __stdcall VirtualProtect(void *, unsigned long, unsigned long, unsigned long *);
}
#define PAGE_EXECUTE_READWRITE 64
#else
#include <sys/mman.h>
#include <limits.h>
#include <unistd.h>
#endif
#ifdef __GNUC__
extern "C" int ClassifyLineBackpatch (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2])
#else
static int *CallerOffset;
int ClassifyLineBackpatchC (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2])
#endif
{
// Select the routine based on SSE2 availability and patch the caller so that
// they call that routine directly next time instead of going through here.
int *calleroffset;
int diff;
int (*func)(node_t &, const FSimpleVert *, const FSimpleVert *, int[2]);
#ifdef __GNUC__
calleroffset = (int *)__builtin_return_address(0);
#else
calleroffset = CallerOffset;
#endif
// printf ("Patching for SSE %d @ %p %d\n", SSELevel, calleroffset, *calleroffset);
if (CPU.bSSE2)
{
func = ClassifyLineSSE2;
diff = int((char *)ClassifyLineSSE2 - (char *)calleroffset);
}
else
{
func = ClassifyLine2;
diff = int((char *)ClassifyLine2 - (char *)calleroffset);
}
calleroffset--;
// Patch the caller.
#ifdef _WIN32
unsigned long oldprotect;
if (VirtualProtect (calleroffset, 4, PAGE_EXECUTE_READWRITE, &oldprotect))
#else
// must make this page-aligned for mprotect
long pagesize = sysconf(_SC_PAGESIZE);
char *callerpage = (char *)((intptr_t)calleroffset & ~(pagesize - 1));
size_t protectlen = (intptr_t)calleroffset + sizeof(void*) - (intptr_t)callerpage;
int ptect;
if (!(ptect = mprotect(callerpage, protectlen, PROT_READ|PROT_WRITE|PROT_EXEC)))
#endif
{
*calleroffset = diff;
#ifdef _WIN32
VirtualProtect (calleroffset, sizeof(void*), oldprotect, &oldprotect);
#else
mprotect(callerpage, protectlen, PROT_READ|PROT_EXEC);
#endif
}
// And return by calling the real function.
return func (node, v1, v2, sidev);
}
#ifndef __GNUC__
// The ClassifyLineBackpatch() function here is a stub that uses inline assembly and nakedness
// to retrieve the return address of the stack before sending control to the real
// ClassifyLineBackpatchC() function. Since BACKPATCH shouldn't be defined on 64-bit builds,
// we're okay that VC++ can't do inline assembly on that target.
extern "C" __declspec(noinline) __declspec(naked) int ClassifyLineBackpatch (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2])
{
// We store the return address in a global, so as not to need to mess with the parameter list.
__asm
{
mov eax, [esp]
mov CallerOffset, eax
jmp ClassifyLineBackpatchC
}
}
#endif
#endif

View file

@ -1,6 +1,10 @@
#include "doomdata.h"
#include "tarray.h"
#include "r_defs.h"
#include "x86.h"
struct FPolySeg;
struct FMiniBSP;
struct FEventInfo
{
@ -40,6 +44,27 @@ private:
FEvent *Predecessor (FEvent *event) const;
};
struct FSimpleVert
{
fixed_t x, y;
};
extern "C"
{
int ClassifyLine2 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]);
#ifndef DISABLE_SSE
int ClassifyLineSSE1 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]);
int ClassifyLineSSE2 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]);
#ifdef BACKPATCH
#ifdef __GNUC__
int ClassifyLineBackpatch (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]) __attribute__((noinline));
#else
int __declspec(noinline) ClassifyLineBackpatch (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]);
#endif
#endif
#endif
}
class FNodeBuilder
{
struct FPrivSeg
@ -60,9 +85,8 @@ class FNodeBuilder
bool planefront;
FPrivSeg *hashnext;
};
struct FPrivVert
struct FPrivVert : FSimpleVert
{
fixed_t x, y;
DWORD segs; // segs that use this vertex as v1
DWORD segs2; // segs that use this vertex as v2
@ -87,8 +111,24 @@ class FNodeBuilder
bool Forward;
};
struct glseg_t : public seg_t
{
DWORD Partner;
};
// Like a blockmap, but for vertices instead of lines
class FVertexMap
class IVertexMap
{
public:
virtual ~IVertexMap();
virtual int SelectVertexExact(FPrivVert &vert) = 0;
virtual int SelectVertexClose(FPrivVert &vert) = 0;
private:
IVertexMap &operator=(const IVertexMap &);
};
class FVertexMap : public IVertexMap
{
public:
FVertexMap (FNodeBuilder &builder, fixed_t minx, fixed_t miny, fixed_t maxx, fixed_t maxy);
@ -116,12 +156,23 @@ class FNodeBuilder
assert (y <= MaxY);
return (unsigned(x - MinX) >> BLOCK_SHIFT) + (unsigned(y - MinY) >> BLOCK_SHIFT) * BlocksWide;
}
};
FVertexMap &operator= (const FVertexMap &) { return *this; }
class FVertexMapSimple : public IVertexMap
{
public:
FVertexMapSimple(FNodeBuilder &builder);
int SelectVertexExact(FPrivVert &vert);
int SelectVertexClose(FPrivVert &vert);
private:
int InsertVertex(FPrivVert &vert);
FNodeBuilder &MyBuilder;
};
friend class FVertexMap;
friend class FVertexMapSimple;
public:
struct FLevel
@ -132,7 +183,14 @@ public:
fixed_t MinX, MinY, MaxX, MaxY;
void FindMapBounds ();
void FindMapBounds();
void ResetMapBounds()
{
MinX = FIXED_MAX;
MinY = FIXED_MAX;
MaxX = FIXED_MIN;
MaxY = FIXED_MIN;
}
};
struct FPolyStart
@ -141,16 +199,24 @@ public:
fixed_t x, y;
};
FNodeBuilder (FLevel &level);
FNodeBuilder (FLevel &level,
TArray<FPolyStart> &polyspots, TArray<FPolyStart> &anchors,
bool makeGLNodes, bool enableSSE2);
bool makeGLNodes);
~FNodeBuilder ();
void Extract (node_t *&nodes, int &nodeCount,
seg_t *&segs, int &segCount,
seg_t *&segs, glsegextra_t *&glsegextras, int &segCount,
subsector_t *&ssecs, int &subCount,
vertex_t *&verts, int &vertCount);
// These are used for building sub-BSP trees for polyobjects.
void Clear();
void AddPolySegs(FPolySeg *segs, int numsegs);
void AddSegs(seg_t *segs, int numsegs);
void BuildMini(bool makeGLNodes);
void ExtractMini(FMiniBSP *bsp);
static angle_t PointToAngle (fixed_t dx, fixed_t dy);
// < 0 : in front of line
@ -160,7 +226,7 @@ public:
static inline int PointOnSide (int x, int y, int x1, int y1, int dx, int dy);
private:
FVertexMap *VertexMap;
IVertexMap *VertexMap;
TArray<node_t> Nodes;
TArray<subsector_t> Subsectors;
@ -181,7 +247,6 @@ private:
DWORD HackMate; // Seg to use in front of hack seg
FLevel &Level;
bool GLNodes; // Add minisegs to make GL nodes?
bool EnableSSE2;
// Progress meter stuff
int SegsStuffed;
@ -191,29 +256,27 @@ private:
void MakeSegsFromSides ();
int CreateSeg (int linenum, int sidenum);
void GroupSegPlanes ();
void GroupSegPlanesSimple ();
void FindPolyContainers (TArray<FPolyStart> &spots, TArray<FPolyStart> &anchors);
bool GetPolyExtents (int polynum, fixed_t bbox[4]);
int MarkLoop (DWORD firstseg, int loopnum);
void AddSegToBBox (fixed_t bbox[4], const FPrivSeg *seg);
int CreateNode (DWORD set, fixed_t bbox[4]);
int CreateNode (DWORD set, unsigned int count, fixed_t bbox[4]);
int CreateSubsector (DWORD set, fixed_t bbox[4]);
void CreateSubsectorsForReal ();
bool CheckSubsector (DWORD set, node_t &node, DWORD &splitseg, int setsize);
bool CheckSubsector (DWORD set, node_t &node, DWORD &splitseg);
bool CheckSubsectorOverlappingSegs (DWORD set, node_t &node, DWORD &splitseg);
bool ShoveSegBehind (DWORD set, node_t &node, DWORD seg, DWORD mate); int SelectSplitter (DWORD set, node_t &node, DWORD &splitseg, int step, bool nosplit);
void SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &outset0, DWORD &outset1);
void SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &outset0, DWORD &outset1, unsigned int &count0, unsigned int &count1);
DWORD SplitSeg (DWORD segnum, int splitvert, int v1InFront);
int Heuristic (node_t &node, DWORD set, bool honorNoSplit);
int CountSegs (DWORD set) const;
// Returns:
// 0 = seg is in front
// 1 = seg is in back
// -1 = seg cuts the node
inline int ClassifyLine (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2);
int ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2);
int ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2);
inline int ClassifyLine (node_t &node, const FPrivVert *v1, const FPrivVert *v2, int sidev[2]);
void FixSplitSharers (const node_t &node);
double AddIntersection (const node_t &node, int vertex);
@ -225,10 +288,10 @@ private:
DWORD AddMiniseg (int v1, int v2, DWORD partner, DWORD seg1, DWORD splitseg);
void SetNodeFromSeg (node_t &node, const FPrivSeg *pseg) const;
int CloseSubsector (TArray<seg_t> &segs, int subsector, vertex_t *outVerts);
DWORD PushGLSeg (TArray<seg_t> &segs, const FPrivSeg *seg, vertex_t *outVerts);
void PushConnectingGLSeg (int subsector, TArray<seg_t> &segs, vertex_t *v1, vertex_t *v2);
int OutputDegenerateSubsector (TArray<seg_t> &segs, int subsector, bool bForward, double lastdot, FPrivSeg *&prev, vertex_t *outVerts);
int CloseSubsector (TArray<glseg_t> &segs, int subsector, vertex_t *outVerts);
DWORD PushGLSeg (TArray<glseg_t> &segs, const FPrivSeg *seg, vertex_t *outVerts);
void PushConnectingGLSeg (int subsector, TArray<glseg_t> &segs, vertex_t *v1, vertex_t *v2);
int OutputDegenerateSubsector (TArray<glseg_t> &segs, int subsector, bool bForward, double lastdot, FPrivSeg *&prev, vertex_t *outVerts);
static int STACK_ARGS SortSegs (const void *a, const void *b);
@ -273,21 +336,27 @@ inline int FNodeBuilder::PointOnSide (int x, int y, int x1, int y1, int dx, int
return s_num > 0.0 ? -1 : 1;
}
inline int FNodeBuilder::ClassifyLine (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2)
inline int FNodeBuilder::ClassifyLine (node_t &node, const FPrivVert *v1, const FPrivVert *v2, int sidev[2])
{
#if !defined(_M_IX86) && !defined(_M_X64) && !defined(__i386__) && !defined(__amd64__)
return ClassifyLine2 (node, seg, sidev1, sidev2);
#elif defined(__SSE2__)
#ifdef DISABLE_SSE
return ClassifyLine2 (node, v1, v2, sidev);
#else
#if defined(__SSE2__) || defined(_M_IX64)
// If compiling with SSE2 support everywhere, just use the SSE2 version.
return ClassifyLineSSE2 (node, seg, sidev1, sidev2);
return ClassifyLineSSE2 (node, v1, v2, sidev);
#elif defined(_MSC_VER) && _MSC_VER < 1300
// VC 6 does not support SSE2 optimizations.
return ClassifyLine2 (node, seg, sidev1, sidev2);
// VC 6 does not support SSE optimizations.
return ClassifyLine2 (node, v1, v2, sidev);
#else
// Select the routine based on our flag.
if (EnableSSE2)
return ClassifyLineSSE2 (node, seg, sidev1, sidev2);
#ifdef BACKPATCH
return ClassifyLineBackpatch (node, v1, v2, sidev);
#else
if (CPU.bSSE2)
return ClassifyLineSSE2 (node, v1, v2, sidev);
else
return ClassifyLine2 (node, seg, sidev1, sidev2);
return ClassifyLine2 (node, v1, v2, sidev);
#endif
#endif
#endif
}

View file

@ -3,11 +3,8 @@
#define FAR_ENOUGH 17179869184.f // 4<<32
int FNodeBuilder::ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2)
extern "C" int ClassifyLine2 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2])
{
const FPrivVert *v1 = &Vertices[seg->v1];
const FPrivVert *v2 = &Vertices[seg->v2];
double d_x1 = double(node.x);
double d_y1 = double(node.y);
double d_dx = double(node.dx);
@ -26,13 +23,13 @@ int FNodeBuilder::ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1,
{
if (s_num2 <= -FAR_ENOUGH)
{
sidev1 = sidev2 = 1;
sidev[0] = sidev[1] = 1;
return 1;
}
if (s_num2 >= FAR_ENOUGH)
{
sidev1 = 1;
sidev2 = -1;
sidev[0] = 1;
sidev[1] = -1;
return -1;
}
nears = 1;
@ -41,13 +38,13 @@ int FNodeBuilder::ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1,
{
if (s_num2 >= FAR_ENOUGH)
{
sidev1 = sidev2 = -1;
sidev[0] = sidev[1] = -1;
return 0;
}
if (s_num2 <= -FAR_ENOUGH)
{
sidev1 = -1;
sidev2 = 1;
sidev[0] = -1;
sidev[1] = 1;
return -1;
}
nears = 1;
@ -65,41 +62,41 @@ int FNodeBuilder::ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1,
double dist = s_num1 * s_num1 * l;
if (dist < SIDE_EPSILON*SIDE_EPSILON)
{
sidev1 = 0;
sidev[0] = 0;
}
else
{
sidev1 = s_num1 > 0.0 ? -1 : 1;
sidev[0] = s_num1 > 0.0 ? -1 : 1;
}
}
else
{
sidev1 = s_num1 > 0.0 ? -1 : 1;
sidev[0] = s_num1 > 0.0 ? -1 : 1;
}
if (nears & 1)
{
double dist = s_num2 * s_num2 * l;
if (dist < SIDE_EPSILON*SIDE_EPSILON)
{
sidev2 = 0;
sidev[1] = 0;
}
else
{
sidev2 = s_num2 > 0.0 ? -1 : 1;
sidev[1] = s_num2 > 0.0 ? -1 : 1;
}
}
else
{
sidev2 = s_num2 > 0.0 ? -1 : 1;
sidev[1] = s_num2 > 0.0 ? -1 : 1;
}
}
else
{
sidev1 = s_num1 > 0.0 ? -1 : 1;
sidev2 = s_num2 > 0.0 ? -1 : 1;
sidev[0] = s_num1 > 0.0 ? -1 : 1;
sidev[1] = s_num2 > 0.0 ? -1 : 1;
}
if ((sidev1 | sidev2) == 0)
if ((sidev[0] | sidev[1]) == 0)
{ // seg is coplanar with the splitter, so use its orientation to determine
// which child it ends up in. If it faces the same direction as the splitter,
// it goes in front. Otherwise, it goes in back.
@ -127,11 +124,11 @@ int FNodeBuilder::ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1,
}
}
}
else if (sidev1 <= 0 && sidev2 <= 0)
else if (sidev[0] <= 0 && sidev[1] <= 0)
{
return 0;
}
else if (sidev1 >= 0 && sidev2 >= 0)
else if (sidev[0] >= 0 && sidev[1] >= 0)
{
return 1;
}

View file

@ -1,18 +1,16 @@
#ifndef DISABLE_SSE
#include "doomtype.h"
#include "nodebuild.h"
#define FAR_ENOUGH 17179869184.f // 4<<32
// This function is identical to the ClassifyLine2 version. So how does it use SSE2?
// Easy! By explicitly enabling SSE2 in the configuration properties for this one
// file, we can build it with SSE2 enabled without forcing SSE2 on the rest of the
// project.
// You may notice that this function is identical to ClassifyLine2.
// The reason it is SSE2 is because this file is explicitly compiled
// with SSE2 math enabled, but the other files are not.
int FNodeBuilder::ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2)
extern "C" int ClassifyLineSSE2 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2])
{
const FPrivVert *v1 = &Vertices[seg->v1];
const FPrivVert *v2 = &Vertices[seg->v2];
double d_x1 = double(node.x);
double d_y1 = double(node.y);
double d_dx = double(node.dx);
@ -31,13 +29,13 @@ int FNodeBuilder::ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &side
{
if (s_num2 <= -FAR_ENOUGH)
{
sidev1 = sidev2 = 1;
sidev[0] = sidev[1] = 1;
return 1;
}
if (s_num2 >= FAR_ENOUGH)
{
sidev1 = 1;
sidev2 = -1;
sidev[0] = 1;
sidev[1] = -1;
return -1;
}
nears = 1;
@ -46,13 +44,13 @@ int FNodeBuilder::ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &side
{
if (s_num2 >= FAR_ENOUGH)
{
sidev1 = sidev2 = -1;
sidev[0] = sidev[1] = -1;
return 0;
}
if (s_num2 <= -FAR_ENOUGH)
{
sidev1 = -1;
sidev2 = 1;
sidev[0] = -1;
sidev[1] = 1;
return -1;
}
nears = 1;
@ -70,41 +68,41 @@ int FNodeBuilder::ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &side
double dist = s_num1 * s_num1 * l;
if (dist < SIDE_EPSILON*SIDE_EPSILON)
{
sidev1 = 0;
sidev[0] = 0;
}
else
{
sidev1 = s_num1 > 0.0 ? -1 : 1;
sidev[0] = s_num1 > 0.0 ? -1 : 1;
}
}
else
{
sidev1 = s_num1 > 0.0 ? -1 : 1;
sidev[0] = s_num1 > 0.0 ? -1 : 1;
}
if (nears & 1)
{
double dist = s_num2 * s_num2 * l;
if (dist < SIDE_EPSILON*SIDE_EPSILON)
{
sidev2 = 0;
sidev[1] = 0;
}
else
{
sidev2 = s_num2 > 0.0 ? -1 : 1;
sidev[1] = s_num2 > 0.0 ? -1 : 1;
}
}
else
{
sidev2 = s_num2 > 0.0 ? -1 : 1;
sidev[1] = s_num2 > 0.0 ? -1 : 1;
}
}
else
{
sidev1 = s_num1 > 0.0 ? -1 : 1;
sidev2 = s_num2 > 0.0 ? -1 : 1;
sidev[0] = s_num1 > 0.0 ? -1 : 1;
sidev[1] = s_num2 > 0.0 ? -1 : 1;
}
if ((sidev1 | sidev2) == 0)
if ((sidev[0] | sidev[1]) == 0)
{ // seg is coplanar with the splitter, so use its orientation to determine
// which child it ends up in. If it faces the same direction as the splitter,
// it goes in front. Otherwise, it goes in back.
@ -132,13 +130,15 @@ int FNodeBuilder::ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &side
}
}
}
else if (sidev1 <= 0 && sidev2 <= 0)
else if (sidev[0] <= 0 && sidev[1] <= 0)
{
return 0;
}
else if (sidev1 >= 0 && sidev2 >= 0)
else if (sidev[0] >= 0 && sidev[1] >= 0)
{
return 1;
}
return -1;
}
#endif

View file

@ -54,7 +54,7 @@
#endif
void FNodeBuilder::Extract (node_t *&outNodes, int &nodeCount,
seg_t *&outSegs, int &segCount,
seg_t *&outSegs, glsegextra_t *&outSegExtras, int &segCount,
subsector_t *&outSubs, int &subCount,
vertex_t *&outVerts, int &vertCount)
{
@ -99,25 +99,30 @@ void FNodeBuilder::Extract (node_t *&outNodes, int &nodeCount,
if (GLNodes)
{
TArray<seg_t> segs (Segs.Size()*5/4);
TArray<glseg_t> segs (Segs.Size()*5/4);
for (i = 0; i < subCount; ++i)
{
DWORD numsegs = CloseSubsector (segs, i, outVerts);
outSubs[i].numlines = numsegs;
outSubs[i].firstline = segs.Size() - numsegs;
outSubs[i].poly = NULL;
outSubs[i].firstline = (seg_t *)(size_t)(segs.Size() - numsegs);
}
segCount = segs.Size ();
outSegs = new seg_t[segCount];
memcpy (outSegs, &segs[0], segCount*sizeof(seg_t));
outSegExtras = new glsegextra_t[segCount];
for (i = 0; i < segCount; ++i)
{
if (outSegs[i].PartnerSeg != NULL)
outSegs[i] = *(seg_t *)&segs[i];
if (segs[i].Partner != DWORD_MAX)
{
outSegs[i].PartnerSeg = &outSegs[Segs[(unsigned int)(size_t)outSegs[i].PartnerSeg-1].storedseg];
outSegExtras[i].PartnerSeg = Segs[segs[i].Partner].storedseg;
}
else
{
outSegExtras[i].PartnerSeg = DWORD_MAX;
}
}
}
@ -126,6 +131,7 @@ void FNodeBuilder::Extract (node_t *&outNodes, int &nodeCount,
memcpy (outSubs, &Subsectors[0], subCount*sizeof(subsector_t));
segCount = Segs.Size ();
outSegs = new seg_t[segCount];
outSegExtras = NULL;
for (i = 0; i < segCount; ++i)
{
const FPrivSeg *org = &Segs[SegList[i].SegNum];
@ -139,10 +145,12 @@ void FNodeBuilder::Extract (node_t *&outNodes, int &nodeCount,
out->frontsector = org->frontsector;
out->linedef = Level.Lines + org->linedef;
out->sidedef = Level.Sides + org->sidedef;
out->PartnerSeg = NULL;
out->bPolySeg = false;
}
}
for (i = 0; i < subCount; ++i)
{
outSubs[i].firstline = &outSegs[(size_t)outSubs[i].firstline];
}
D(Printf("%i segs, %i nodes, %i subsectors\n", segCount, nodeCount, subCount));
@ -153,7 +161,91 @@ void FNodeBuilder::Extract (node_t *&outNodes, int &nodeCount,
}
}
int FNodeBuilder::CloseSubsector (TArray<seg_t> &segs, int subsector, vertex_t *outVerts)
void FNodeBuilder::ExtractMini (FMiniBSP *bsp)
{
unsigned int i;
bsp->Verts.Resize(Vertices.Size());
for (i = 0; i < Vertices.Size(); ++i)
{
bsp->Verts[i].x = Vertices[i].x;
bsp->Verts[i].y = Vertices[i].y;
}
bsp->Subsectors.Resize(Subsectors.Size());
memset(&bsp->Subsectors[0], 0, Subsectors.Size() * sizeof(subsector_t));
bsp->Nodes.Resize(Nodes.Size());
memcpy(&bsp->Nodes[0], &Nodes[0], Nodes.Size()*sizeof(node_t));
for (i = 0; i < Nodes.Size(); ++i)
{
D(Printf(PRINT_LOG, "Node %d:\n", i));
// Go backwards because on 64-bit systems, both of the intchildren are
// inside the first in-game child.
for (int j = 1; j >= 0; --j)
{
if (bsp->Nodes[i].intchildren[j] & 0x80000000)
{
D(Printf(PRINT_LOG, " subsector %d\n", bsp->Nodes[i].intchildren[j] & 0x7FFFFFFF));
bsp->Nodes[i].children[j] = (BYTE *)&bsp->Subsectors[bsp->Nodes[i].intchildren[j] & 0x7fffffff] + 1;
}
else
{
D(Printf(PRINT_LOG, " node %d\n", bsp->Nodes[i].intchildren[j]));
bsp->Nodes[i].children[j] = &bsp->Nodes[bsp->Nodes[i].intchildren[j]];
}
}
}
if (GLNodes)
{
TArray<glseg_t> glsegs;
for (i = 0; i < Subsectors.Size(); ++i)
{
DWORD numsegs = CloseSubsector (glsegs, i, &bsp->Verts[0]);
bsp->Subsectors[i].numlines = numsegs;
bsp->Subsectors[i].firstline = &bsp->Segs[bsp->Segs.Size() - numsegs];
}
bsp->Segs.Resize(glsegs.Size());
for (i = 0; i < glsegs.Size(); ++i)
{
bsp->Segs[i] = *(seg_t *)&glsegs[i];
}
}
else
{
memcpy(&bsp->Subsectors[0], &Subsectors[0], Subsectors.Size()*sizeof(subsector_t));
bsp->Segs.Resize(Segs.Size());
for (i = 0; i < Segs.Size(); ++i)
{
const FPrivSeg *org = &Segs[SegList[i].SegNum];
seg_t *out = &bsp->Segs[i];
D(Printf(PRINT_LOG, "Seg %d: v1(%d) -> v2(%d)\n", i, org->v1, org->v2));
out->v1 = &bsp->Verts[org->v1];
out->v2 = &bsp->Verts[org->v2];
out->backsector = org->backsector;
out->frontsector = org->frontsector;
if (org->sidedef != int(NO_SIDE))
{
out->linedef = Level.Lines + org->linedef;
out->sidedef = Level.Sides + org->sidedef;
}
else // part of a miniseg
{
out->linedef = NULL;
out->sidedef = NULL;
}
}
for (i = 0; i < bsp->Subsectors.Size(); ++i)
{
bsp->Subsectors[i].firstline = &bsp->Segs[(size_t)bsp->Subsectors[i].firstline];
}
}
}
int FNodeBuilder::CloseSubsector (TArray<glseg_t> &segs, int subsector, vertex_t *outVerts)
{
FPrivSeg *seg, *prev;
angle_t prevAngle;
@ -164,7 +256,7 @@ int FNodeBuilder::CloseSubsector (TArray<seg_t> &segs, int subsector, vertex_t *
bool diffplanes;
int firstplane;
first = Subsectors[subsector].firstline;
first = (DWORD)(size_t)Subsectors[subsector].firstline;
max = first + Subsectors[subsector].numlines;
count = 0;
@ -315,7 +407,7 @@ int FNodeBuilder::CloseSubsector (TArray<seg_t> &segs, int subsector, vertex_t *
return count;
}
int FNodeBuilder::OutputDegenerateSubsector (TArray<seg_t> &segs, int subsector, bool bForward, double lastdot, FPrivSeg *&prev, vertex_t *outVerts)
int FNodeBuilder::OutputDegenerateSubsector (TArray<glseg_t> &segs, int subsector, bool bForward, double lastdot, FPrivSeg *&prev, vertex_t *outVerts)
{
static const double bestinit[2] = { -DBL_MAX, DBL_MAX };
FPrivSeg *seg;
@ -323,7 +415,7 @@ int FNodeBuilder::OutputDegenerateSubsector (TArray<seg_t> &segs, int subsector,
double dot, x1, y1, dx, dy, dx2, dy2;
bool wantside;
first = Subsectors[subsector].firstline;
first = (DWORD)(size_t)Subsectors[subsector].firstline;
max = first + Subsectors[subsector].numlines;
count = 0;
@ -382,9 +474,9 @@ int FNodeBuilder::OutputDegenerateSubsector (TArray<seg_t> &segs, int subsector,
return count;
}
DWORD FNodeBuilder::PushGLSeg (TArray<seg_t> &segs, const FPrivSeg *seg, vertex_t *outVerts)
DWORD FNodeBuilder::PushGLSeg (TArray<glseg_t> &segs, const FPrivSeg *seg, vertex_t *outVerts)
{
seg_t newseg;
glseg_t newseg;
newseg.v1 = outVerts + seg->v1;
newseg.v2 = outVerts + seg->v2;
@ -400,15 +492,13 @@ DWORD FNodeBuilder::PushGLSeg (TArray<seg_t> &segs, const FPrivSeg *seg, vertex_
newseg.linedef = NULL;
newseg.sidedef = NULL;
}
newseg.PartnerSeg = (seg_t *)(seg->partner == DWORD_MAX ? 0 : (size_t)seg->partner + 1);
newseg.bPolySeg = false;
newseg.Subsector = NULL;
newseg.Partner = seg->partner;
return (DWORD)segs.Push (newseg);
}
void FNodeBuilder::PushConnectingGLSeg (int subsector, TArray<seg_t> &segs, vertex_t *v1, vertex_t *v2)
void FNodeBuilder::PushConnectingGLSeg (int subsector, TArray<glseg_t> &segs, vertex_t *v1, vertex_t *v2)
{
seg_t newseg;
glseg_t newseg;
newseg.v1 = v1;
newseg.v2 = v2;
@ -416,8 +506,6 @@ void FNodeBuilder::PushConnectingGLSeg (int subsector, TArray<seg_t> &segs, vert
newseg.frontsector = NULL;
newseg.linedef = NULL;
newseg.sidedef = NULL;
newseg.PartnerSeg = NULL;
newseg.bPolySeg = false;
newseg.Subsector = NULL;
newseg.Partner = DWORD_MAX;
segs.Push (newseg);
}

View file

@ -49,6 +49,7 @@
#include "m_bbox.h"
#include "r_main.h"
#include "i_system.h"
#include "po_man.h"
static const int PO_LINE_START = 1;
static const int PO_LINE_EXPLICIT = 5;
@ -177,6 +178,86 @@ int FNodeBuilder::CreateSeg (int linenum, int sidenum)
return segnum;
}
// For every seg, create FPrivSegs and FPrivVerts.
void FNodeBuilder::AddSegs(seg_t *segs, int numsegs)
{
assert(numsegs > 0);
for (int i = 0; i < numsegs; ++i)
{
FPrivSeg seg;
FPrivVert vert;
int segnum;
seg.next = DWORD_MAX;
seg.loopnum = 0;
seg.partner = DWORD_MAX;
seg.hashnext = NULL;
seg.planefront = false;
seg.planenum = DWORD_MAX;
seg.storedseg = DWORD_MAX;
seg.frontsector = segs[i].frontsector;
seg.backsector = segs[i].backsector;
vert.x = segs[i].v1->x;
vert.y = segs[i].v1->y;
seg.v1 = VertexMap->SelectVertexExact(vert);
vert.x = segs[i].v2->x;
vert.y = segs[i].v2->y;
seg.v2 = VertexMap->SelectVertexExact(vert);
seg.linedef = int(segs[i].linedef - Level.Lines);
seg.sidedef = segs[i].sidedef != NULL ? int(segs[i].sidedef - Level.Sides) : int(NO_SIDE);
seg.nextforvert = Vertices[seg.v1].segs;
seg.nextforvert2 = Vertices[seg.v2].segs2;
segnum = (int)Segs.Push(seg);
Vertices[seg.v1].segs = segnum;
Vertices[seg.v2].segs2 = segnum;
}
}
void FNodeBuilder::AddPolySegs(FPolySeg *segs, int numsegs)
{
assert(numsegs > 0);
for (int i = 0; i < numsegs; ++i)
{
FPrivSeg seg;
FPrivVert vert;
int segnum;
seg.next = DWORD_MAX;
seg.loopnum = 0;
seg.partner = DWORD_MAX;
seg.hashnext = NULL;
seg.planefront = false;
seg.planenum = DWORD_MAX;
seg.storedseg = DWORD_MAX;
side_t *side = segs[i].wall;
assert(side != NULL);
seg.frontsector = side->sector;
seg.backsector = side->linedef->frontsector == side->sector ? side->linedef->backsector : side->linedef->frontsector;
vert.x = segs[i].v1.x;
vert.y = segs[i].v1.y;
seg.v1 = VertexMap->SelectVertexExact(vert);
vert.x = segs[i].v2.x;
vert.y = segs[i].v2.y;
seg.v2 = VertexMap->SelectVertexExact(vert);
seg.linedef = int(side->linedef - Level.Lines);
seg.sidedef = int(side - Level.Sides);
seg.nextforvert = Vertices[seg.v1].segs;
seg.nextforvert2 = Vertices[seg.v2].segs2;
segnum = (int)Segs.Push(seg);
Vertices[seg.v1].segs = segnum;
Vertices[seg.v2].segs2 = segnum;
}
}
// Group colinear segs together so that only one seg per line needs to be checked
// by SelectSplitter().
@ -269,6 +350,27 @@ void FNodeBuilder::GroupSegPlanes ()
PlaneChecked.Reserve ((planenum + 7) / 8);
}
// Just create one plane per seg. Should be good enough for mini BSPs.
void FNodeBuilder::GroupSegPlanesSimple()
{
Planes.Resize(Segs.Size());
for (int i = 0; i < (int)Segs.Size(); ++i)
{
FPrivSeg *seg = &Segs[i];
FSimpleLine *pline = &Planes[i];
seg->next = i+1;
seg->hashnext = NULL;
seg->planenum = i;
seg->planefront = true;
pline->x = Vertices[seg->v1].x;
pline->y = Vertices[seg->v1].y;
pline->dx = Vertices[seg->v2].x - Vertices[seg->v1].x;
pline->dy = Vertices[seg->v2].y - Vertices[seg->v1].y;
}
Segs.Last().next = DWORD_MAX;
PlaneChecked.Reserve((Segs.Size() + 7) / 8);
}
// Find "loops" of segs surrounding polyobject's origin. Note that a polyobject's origin
// is not solely defined by the polyobject's anchor, but also by the polyobject itself.
// For the split avoidance to work properly, you must have a convex, complete loop of
@ -507,6 +609,10 @@ void FNodeBuilder::FLevel::FindMapBounds ()
MaxY = maxy;
}
FNodeBuilder::IVertexMap::~IVertexMap()
{
}
FNodeBuilder::FVertexMap::FVertexMap (FNodeBuilder &builder,
fixed_t minx, fixed_t miny, fixed_t maxx, fixed_t maxy)
: MyBuilder(builder)
@ -606,3 +712,52 @@ int FNodeBuilder::FVertexMap::InsertVertex (FNodeBuilder::FPrivVert &vert)
return vertnum;
}
FNodeBuilder::FVertexMapSimple::FVertexMapSimple(FNodeBuilder &builder)
: MyBuilder(builder)
{
}
int FNodeBuilder::FVertexMapSimple::SelectVertexExact(FNodeBuilder::FPrivVert &vert)
{
FPrivVert *verts = &MyBuilder.Vertices[0];
unsigned int stop = MyBuilder.Vertices.Size();
for (unsigned int i = 0; i < stop; ++i)
{
if (verts[i].x == vert.x && verts[i].y == vert.y)
{
return i;
}
}
// Not present: add it!
return InsertVertex(vert);
}
int FNodeBuilder::FVertexMapSimple::SelectVertexClose(FNodeBuilder::FPrivVert &vert)
{
FPrivVert *verts = &MyBuilder.Vertices[0];
unsigned int stop = MyBuilder.Vertices.Size();
for (unsigned int i = 0; i < stop; ++i)
{
#if VERTEX_EPSILON <= 1
if (verts[i].x == vert.x && verts[i].y == y)
#else
if (abs(verts[i].x - vert.x) < VERTEX_EPSILON &&
abs(verts[i].y - vert.y) < VERTEX_EPSILON)
#endif
{
return i;
}
}
// Not present: add it!
return InsertVertex (vert);
}
int FNodeBuilder::FVertexMapSimple::InsertVertex (FNodeBuilder::FPrivVert &vert)
{
vert.segs = DWORD_MAX;
vert.segs2 = DWORD_MAX;
return (int)MyBuilder.Vertices.Push (vert);
}

Some files were not shown because too many files have changed in this diff Show more