Merge remote-tracking branch 'gz/master' into gz_master2

This commit is contained in:
ZZYZX 2017-03-03 23:30:02 +02:00
commit 723f9770a4
211 changed files with 10506 additions and 9441 deletions

View file

@ -197,6 +197,7 @@ Note: All <bool> fields default to false unless mentioned otherwise.
desaturation = <float>; // Color desaturation factor. 0 = none, 1 = full, default = 0. desaturation = <float>; // Color desaturation factor. 0 = none, 1 = full, default = 0.
silent = <bool>; // Actors in this sector make no sound, silent = <bool>; // Actors in this sector make no sound,
nofallingdamage = <bool>; // Falling damage is disabled in this sector nofallingdamage = <bool>; // Falling damage is disabled in this sector
noattack = <bool>; // Blocks monster attacks in this sector.
dropactors = <bool>; // Actors drop with instantly moving floors (*) dropactors = <bool>; // Actors drop with instantly moving floors (*)
norespawn = <bool>; // Players can not respawn in this sector norespawn = <bool>; // Players can not respawn in this sector
soundsequence = <string>; // The sound sequence to play when this sector moves. Placing a soundsequence = <string>; // The sound sequence to play when this sector moves. Placing a
@ -262,7 +263,7 @@ Note: All <bool> fields default to false unless mentioned otherwise.
gravity = <float>; // Set per-actor gravity. Positive values are multiplied with the class's property, gravity = <float>; // Set per-actor gravity. Positive values are multiplied with the class's property,
// negative values are used as their absolute. Default = 1.0. // negative values are used as their absolute. Default = 1.0.
health = <int>; // Set per-actor health. Positive values are multiplied with the class's property, health = <float>; // Set per-actor health. Positive values are multiplied with the class's property,
// negative values are used as their absolute. Default = 1. // negative values are used as their absolute. Default = 1.
renderstyle = <string>; // Set per-actor render style, overriding the class default. Possible values can be "normal", renderstyle = <string>; // Set per-actor render style, overriding the class default. Possible values can be "normal",

View file

@ -38,6 +38,7 @@ conversation
page page
{ {
drop = <string>; drop = <string>;
userstring = <string>; New field which can be used to pass data to custom conversation menu classes.
ifitem ifitem
{ {
item = <string>; item = <string>;
@ -63,10 +64,6 @@ 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 outright abort on incompatible namespaces fail with a type mismatch error on
one of the specified propeties. one of the specified propeties.
In addition ZDoom defines one new field in the top level of a conversation block:
id = <integer>; Assigns a conversation ID for use in Thing_SetConversation or in UDMF's 'conversation' actor property.
ZDoom-format dialogues need to start with the line: ZDoom-format dialogues need to start with the line:
namespace = "ZDoom"; namespace = "ZDoom";
@ -86,6 +83,7 @@ conversation // Starts a dialog.
// the standard conversation ID ('actor' property) is used instead // the standard conversation ID ('actor' property) is used instead
// for this purpose but since 'ZDoom' namespace requires the actor // for this purpose but since 'ZDoom' namespace requires the actor
// to be a class name it needs a separate field for this. // to be a class name it needs a separate field for this.
class = <string>; //Override the default conversation menu class for this conversation.
page page
{ {
@ -94,6 +92,8 @@ conversation // Starts a dialog.
choice choice
{ {
specialname = <string>; // Allows specifying a special by name.
// The amount of an item needed for this option to become available. // The amount of an item needed for this option to become available.
// You can have as many as needed. All require blocks must be satisfied // You can have as many as needed. All require blocks must be satisfied
// to show this option. // to show this option.

View file

@ -103,61 +103,66 @@ if( WIN32 )
set( FMOD_SEARCH_PATHS set( FMOD_SEARCH_PATHS
"C:/Program Files/FMOD SoundSystem/FMOD Programmers API ${WIN_TYPE}/api" "C:/Program Files/FMOD SoundSystem/FMOD Programmers API ${WIN_TYPE}/api"
"C:/Program Files (x86)/FMOD SoundSystem/FMOD Programmers API ${WIN_TYPE}/api" "C:/Program Files (x86)/FMOD SoundSystem/FMOD Programmers API ${WIN_TYPE}/api"
# This next one is for Randy.
"E:/Software/Dev/FMOD/${WIN_TYPE}/api"
) )
set( FMOD_INC_PATH_SUFFIXES PATH_SUFFIXES inc ) set( FMOD_INC_PATH_SUFFIXES PATH_SUFFIXES inc )
set( FMOD_LIB_PATH_SUFFIXES PATH_SUFFIXES lib ) set( FMOD_LIB_PATH_SUFFIXES PATH_SUFFIXES lib )
find_path( D3D_INCLUDE_DIR d3d9.h if( ( MSVC14 AND NOT CMAKE_GENERATOR_TOOLSET STREQUAL "v140_xp" ) OR # For VS 2015.
PATHS ENV DXSDK_DIR ( MSVC15 AND NOT CMAKE_GENERATOR_TOOLSET STREQUAL "v150_xp" ) ) # For VS 2017.
PATH_SUFFIXES Include ) # for modern Windows SDKs the DirectX headers should be available by default.
if( NOT D3D_INCLUDE_DIR )
# Modern versions of the Windows SDK include d3d9.h. Unfortunately,
# CMake cannot find this file via find_path, so we check for it using
# CHECK_INCLUDE_FILE.
CHECK_INCLUDE_FILE( d3d9.h D3D9_H_FOUND )
if ( NOT D3D9_H_FOUND )
message( SEND_ERROR "Could not find DirectX 9 header files" )
endif()
else()
include_directories( ${D3D_INCLUDE_DIR} )
endif()
find_path( XINPUT_INCLUDE_DIR xinput.h
PATHS ENV DXSDK_DIR
PATH_SUFFIXES Include )
if( NOT XINPUT_INCLUDE_DIR )
# Modern versions of the Windows SDK include xinput.h. Unfortunately,
# CMake cannot find this file via find_path, so we check for it using
# CHECK_INCLUDE_FILES. windows.h must be included before xinput.h.
CHECK_INCLUDE_FILES( "windows.h;xinput.h" XINPUT_H_FOUND )
if( NOT XINPUT_H_FOUND )
message( WARNING "Could not find xinput.h. XInput will be disabled." )
add_definitions( -DNO_XINPUT )
endif()
else()
include_directories( ${XINPUT_INCLUDE_DIR} )
endif()
find_library( DX_dinput8_LIBRARY dinput8
PATHS ENV DXSDK_DIR
PATH_SUFFIXES Lib Lib/${XBITS} )
find_library( DX_dxguid_LIBRARY dxguid
PATHS ENV DXSDK_DIR
PATH_SUFFIXES Lib Lib/${XBITS} )
# Modern versions of the Windows SDK include dinput8.lib. Unfortunately,
# CMake cannot find these libraries via find_library.
if( NOT DX_dinput8_LIBRARY )
# If we got this far, assume dinput8.lib is in the system library path.
set( DX_dinput8_LIBRARY dinput8 ) set( DX_dinput8_LIBRARY dinput8 )
endif() else()
# Modern versions of the Windows SDK do NOT include dxguid.lib. Its contents find_path( D3D_INCLUDE_DIR d3d9.h
# were moved to dinput8.lib. PATHS ENV DXSDK_DIR
if( NOT DX_dxguid_LIBRARY ) PATH_SUFFIXES Include )
message( STATUS "Could not find dxguid.lib. Build may fail on old Windows SDKs.") if( NOT D3D_INCLUDE_DIR )
# Modern versions of the Windows SDK include d3d9.h. Unfortunately,
# CMake cannot find this file via find_path, so we check for it using
# CHECK_INCLUDE_FILE.
CHECK_INCLUDE_FILE( d3d9.h D3D9_H_FOUND )
if ( NOT D3D9_H_FOUND )
message( SEND_ERROR "Could not find DirectX 9 header files" )
endif()
else()
include_directories( ${D3D_INCLUDE_DIR} )
endif()
find_path( XINPUT_INCLUDE_DIR xinput.h
PATHS ENV DXSDK_DIR
PATH_SUFFIXES Include )
if( NOT XINPUT_INCLUDE_DIR )
# Modern versions of the Windows SDK include xinput.h. Unfortunately,
# CMake cannot find this file via find_path, so we check for it using
# CHECK_INCLUDE_FILES. windows.h must be included before xinput.h.
CHECK_INCLUDE_FILES( "windows.h;xinput.h" XINPUT_H_FOUND )
if( NOT XINPUT_H_FOUND )
message( WARNING "Could not find xinput.h. XInput will be disabled." )
add_definitions( -DNO_XINPUT )
endif()
else()
include_directories( ${XINPUT_INCLUDE_DIR} )
endif()
find_library( DX_dinput8_LIBRARY dinput8
PATHS ENV DXSDK_DIR
PATH_SUFFIXES Lib Lib/${XBITS} )
find_library( DX_dxguid_LIBRARY dxguid
PATHS ENV DXSDK_DIR
PATH_SUFFIXES Lib Lib/${XBITS} )
# Modern versions of the Windows SDK include dinput8.lib. Unfortunately,
# CMake cannot find these libraries via find_library.
if( NOT DX_dinput8_LIBRARY )
# If we got this far, assume dinput8.lib is in the system library path.
set( DX_dinput8_LIBRARY dinput8 )
endif()
# Modern versions of the Windows SDK do NOT include dxguid.lib. Its contents
# were moved to dinput8.lib.
if( NOT DX_dxguid_LIBRARY )
message( STATUS "Could not find dxguid.lib. Build may fail on old Windows SDKs.")
endif()
endif() endif()
set( ZDOOM_LIBS set( ZDOOM_LIBS
@ -392,22 +397,6 @@ if (NOT ZDOOM_USE_SSE2)
endif() endif()
endif() endif()
if( SSE_MATTERS )
if( WIN32 )
set( BACKPATCH 1 CACHE BOOL "Enable backpatching." )
else()
CHECK_FUNCTION_EXISTS(mprotect HAVE_MPROTECT)
if( HAVE_MPROTECT )
set( BACKPATCH 1 CACHE BOOL "Enable backpatching." )
else()
set( BACKPATCH 0 )
endif()
endif()
set( SSE 1 CACHE BOOL "Build SSE and SSE2 versions of key code." )
else()
set( BACKPATCH 0 )
endif()
if( X64 ) if( X64 )
set( HAVE_MMX 1 ) set( HAVE_MMX 1 )
else( X64 ) else( X64 )
@ -572,10 +561,6 @@ endif()
# Flags # Flags
if( BACKPATCH )
add_definitions( -DBACKPATCH )
endif()
# Update gitinfo.h # Update gitinfo.h
add_custom_target( revision_check ALL add_custom_target( revision_check ALL
@ -721,18 +706,6 @@ add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/sc_man_scanner.h
include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
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()
add_definitions( -DDISABLE_SSE )
endif()
else()
add_definitions( -DDISABLE_SSE )
set( X86_SOURCES )
endif()
if( SNDFILE_FOUND ) if( SNDFILE_FOUND )
add_definitions( -DHAVE_SNDFILE ) add_definitions( -DHAVE_SNDFILE )
endif() endif()
@ -840,15 +813,12 @@ set( FASTMATH_PCH_SOURCES
intermission/intermission.cpp intermission/intermission.cpp
intermission/intermission_parse.cpp intermission/intermission_parse.cpp
menu/joystickmenu.cpp menu/joystickmenu.cpp
menu/listmenu.cpp
menu/loadsavemenu.cpp menu/loadsavemenu.cpp
menu/menu.cpp menu/menu.cpp
menu/menudef.cpp menu/menudef.cpp
menu/menuinput.cpp
menu/messagebox.cpp menu/messagebox.cpp
menu/optionmenu.cpp menu/optionmenu.cpp
menu/playermenu.cpp menu/playermenu.cpp
menu/readthis.cpp
menu/videomenu.cpp menu/videomenu.cpp
oplsynth/fmopl.cpp oplsynth/fmopl.cpp
oplsynth/mlopl.cpp oplsynth/mlopl.cpp

View file

@ -15,7 +15,7 @@ DEFINE_SPECIAL(Door_LockedRaise, 13, 4, 5, 5)
DEFINE_SPECIAL(Door_Animated, 14, 4, 4, 4) DEFINE_SPECIAL(Door_Animated, 14, 4, 4, 4)
DEFINE_SPECIAL(Autosave, 15, 0, 0, 0) // [RH] Save the game *now* DEFINE_SPECIAL(Autosave, 15, 0, 0, 0) // [RH] Save the game *now*
DEFINE_SPECIAL(Transfer_WallLight, 16, -1, -1, 2) DEFINE_SPECIAL(Transfer_WallLight, 16, -1, -1, 2)
DEFINE_SPECIAL(Thing_Raise, 17, 1, 1, 1) DEFINE_SPECIAL(Thing_Raise, 17, 1, 2, 2)
DEFINE_SPECIAL(StartConversation, 18, 1, 2, 2) DEFINE_SPECIAL(StartConversation, 18, 1, 2, 2)
DEFINE_SPECIAL(Thing_Stop, 19, 1, 1, 1) DEFINE_SPECIAL(Thing_Stop, 19, 1, 1, 1)
DEFINE_SPECIAL(Floor_LowerByValue, 20, 3, 4, 4) DEFINE_SPECIAL(Floor_LowerByValue, 20, 3, 4, 4)
@ -47,7 +47,7 @@ DEFINE_SPECIAL(Ceiling_CrushRaiseAndStay, 45, 3, 4, 4)
DEFINE_SPECIAL(Floor_CrushStop, 46, 1, 1, 1) DEFINE_SPECIAL(Floor_CrushStop, 46, 1, 1, 1)
DEFINE_SPECIAL(Ceiling_MoveToValue, 47, 3, 5, 5) DEFINE_SPECIAL(Ceiling_MoveToValue, 47, 3, 5, 5)
DEFINE_SPECIAL(Sector_Attach3dMidtex, 48, -1, -1, 3) DEFINE_SPECIAL(Sector_Attach3dMidtex, 48, -1, -1, 3)
DEFINE_SPECIAL(GlassBreak, 49, 0, 1, 1) DEFINE_SPECIAL(GlassBreak, 49, 0, 1, 2)
DEFINE_SPECIAL(ExtraFloor_LightOnly, 50, -1, -1, 2) DEFINE_SPECIAL(ExtraFloor_LightOnly, 50, -1, -1, 2)
DEFINE_SPECIAL(Sector_SetLink, 51, 4, 4, 4) DEFINE_SPECIAL(Sector_SetLink, 51, 4, 4, 4)
DEFINE_SPECIAL(Scroll_Wall, 52, 5, 5, 5) DEFINE_SPECIAL(Scroll_Wall, 52, 5, 5, 5)
@ -259,5 +259,8 @@ DEFINE_SPECIAL(Stairs_BuildDownDoom, 270, 5, 5, 5)
DEFINE_SPECIAL(Stairs_BuildUpDoomSync, 271, 4, 4, 4) DEFINE_SPECIAL(Stairs_BuildUpDoomSync, 271, 4, 4, 4)
DEFINE_SPECIAL(Stairs_BuildDownDoomSync, 272, 4, 4, 4) DEFINE_SPECIAL(Stairs_BuildDownDoomSync, 272, 4, 4, 4)
DEFINE_SPECIAL(Stairs_BuildUpDoomCrush, 273, 5, 5, 5) DEFINE_SPECIAL(Stairs_BuildUpDoomCrush, 273, 5, 5, 5)
DEFINE_SPECIAL(Door_AnimatedClose, 274, 4, 4, 4)
DEFINE_SPECIAL(Floor_Stop, 275, 1, 1, 1)
DEFINE_SPECIAL(Ceiling_Stop, 276, 1, 1, 1)
#undef DEFINE_SPECIAL #undef DEFINE_SPECIAL

View file

@ -387,6 +387,9 @@ enum ActorFlag7
MF7_SPRITEANGLE = 0x02000000, // [MC] Utilize the SpriteAngle property and lock the rotation to the degrees specified. MF7_SPRITEANGLE = 0x02000000, // [MC] Utilize the SpriteAngle property and lock the rotation to the degrees specified.
MF7_SMASHABLE = 0x04000000, // dies if hitting the floor. MF7_SMASHABLE = 0x04000000, // dies if hitting the floor.
MF7_NOSHIELDREFLECT = 0x08000000, // will not be reflected by shields. MF7_NOSHIELDREFLECT = 0x08000000, // will not be reflected by shields.
MF7_FORCEZERORADIUSDMG = 0x10000000, // passes zero radius damage on to P_DamageMobj, this is necessary in some cases where DoSpecialDamage gets overrideen.
MF7_NOINFIGHTSPECIES = 0x20000000, // don't start infights with one's own species.
MF7_FORCEINFIGHTING = 0x40000000, // overrides a map setting of 'no infighting'.
}; };
// --- mobj.renderflags --- // --- mobj.renderflags ---
@ -427,6 +430,7 @@ enum ActorRenderFlag
RF_ABSMASKPITCH = 0x00800000, // [MC] The mask rotation does not offset by the actor's pitch. RF_ABSMASKPITCH = 0x00800000, // [MC] The mask rotation does not offset by the actor's pitch.
RF_INTERPOLATEANGLES = 0x01000000, // [MC] Allow interpolation of the actor's angle, pitch and roll. RF_INTERPOLATEANGLES = 0x01000000, // [MC] Allow interpolation of the actor's angle, pitch and roll.
RF_MAYBEINVISIBLE = 0x02000000, RF_MAYBEINVISIBLE = 0x02000000,
RF_DONTINTERPOLATE = 0x04000000, // no render interpolation ever!
}; };
// This translucency value produces the closest match to Heretic's TINTTAB. // This translucency value produces the closest match to Heretic's TINTTAB.
@ -464,6 +468,7 @@ enum ActorBounceFlag
BOUNCE_MBF = 1<<12, // This in itself is not a valid mode, but replaces MBF's MF_BOUNCE flag. BOUNCE_MBF = 1<<12, // This in itself is not a valid mode, but replaces MBF's MF_BOUNCE flag.
BOUNCE_AutoOffFloorOnly = 1<<13, // like BOUNCE_AutoOff, but only on floors BOUNCE_AutoOffFloorOnly = 1<<13, // like BOUNCE_AutoOff, but only on floors
BOUNCE_UseBounceState = 1<<14, // Use Bounce[.*] states BOUNCE_UseBounceState = 1<<14, // Use Bounce[.*] states
BOUNCE_NotOnShootables = 1<<15, // do not bounce off shootable actors if we are a projectile. Explode instead.
BOUNCE_TypeMask = BOUNCE_Walls | BOUNCE_Floors | BOUNCE_Ceilings | BOUNCE_Actors | BOUNCE_AutoOff | BOUNCE_HereticType | BOUNCE_MBF, BOUNCE_TypeMask = BOUNCE_Walls | BOUNCE_Floors | BOUNCE_Ceilings | BOUNCE_Actors | BOUNCE_AutoOff | BOUNCE_HereticType | BOUNCE_MBF,
@ -712,7 +717,7 @@ public:
virtual bool UseInventory (AInventory *item); virtual bool UseInventory (AInventory *item);
// Tosses an item out of the inventory. // Tosses an item out of the inventory.
AInventory *DropInventory (AInventory *item); AInventory *DropInventory (AInventory *item, int amt = -1);
// Removes all items from the inventory. // Removes all items from the inventory.
void ClearInventory(); void ClearInventory();
@ -802,42 +807,12 @@ public:
return (flags & MF_COUNTKILL) && !(flags & MF_FRIENDLY); return (flags & MF_COUNTKILL) && !(flags & MF_FRIENDLY);
} }
PalEntry GetBloodColor() const
{
return GetClass()->BloodColor;
}
// These also set CF_INTERPVIEW for players. // These also set CF_INTERPVIEW for players.
void SetPitch(DAngle p, bool interpolate, bool forceclamp = false); void SetPitch(DAngle p, bool interpolate, bool forceclamp = false);
void SetAngle(DAngle ang, bool interpolate); void SetAngle(DAngle ang, bool interpolate);
void SetRoll(DAngle roll, bool interpolate); void SetRoll(DAngle roll, bool interpolate);
PClassActor *GetBloodType(int type = 0) const PClassActor *GetBloodType(int type = 0) const;
{
PClassActor *bloodcls;
if (type == 0)
{
bloodcls = PClass::FindActor(GetClass()->BloodType);
}
else if (type == 1)
{
bloodcls = PClass::FindActor(GetClass()->BloodType2);
}
else if (type == 2)
{
bloodcls = PClass::FindActor(GetClass()->BloodType3);
}
else
{
return NULL;
}
if (bloodcls != NULL)
{
bloodcls = bloodcls->GetReplacement();
}
return bloodcls;
}
double Distance2DSquared(AActor *other, bool absolute = false) double Distance2DSquared(AActor *other, bool absolute = false)
{ {
@ -1003,29 +978,42 @@ public:
// NOTE: The first member variable *must* be snext. // NOTE: The first member variable *must* be snext.
AActor *snext, **sprev; // links in sector (if needed) AActor *snext, **sprev; // links in sector (if needed)
DVector3 __Pos; // double underscores so that it won't get used by accident. Access to this should be exclusively through the designated access functions. DVector3 __Pos; // double underscores so that it won't get used by accident. Access to this should be exclusively through the designated access functions.
DVector3 OldRenderPos;
DAngle SpriteAngle; DAngle SpriteAngle;
DAngle SpriteRotation; DAngle SpriteRotation;
DAngle VisibleStartAngle;
DAngle VisibleStartPitch;
DAngle VisibleEndAngle;
DAngle VisibleEndPitch;
DRotator Angles; DRotator Angles;
DVector3 Vel; DVector2 Scale; // Scaling values; 1 is normal size
double Speed; double Alpha; // Since P_CheckSight makes an alpha check this can't be a float. It has to be a double.
double FloatSpeed;
int sprite; // used to find patch_t and flip value int sprite; // used to find patch_t and flip value
uint8_t frame; // sprite frame to draw uint8_t frame; // sprite frame to draw
uint8_t effects; // [RH] see p_effect.h uint8_t effects; // [RH] see p_effect.h
uint8_t fountaincolor; // Split out of 'effect' to have easier access. uint8_t fountaincolor; // Split out of 'effect' to have easier access.
DVector2 Scale; // Scaling values; 1 is normal size
FRenderStyle RenderStyle; // Style to draw this actor with FRenderStyle RenderStyle; // Style to draw this actor with
ActorRenderFlags renderflags; // Different rendering flags
FTextureID picnum; // Draw this instead of sprite if valid FTextureID picnum; // Draw this instead of sprite if valid
double Alpha; // Since P_CheckSight makes an alpha check this can't be a float. It has to be a double.
DWORD fillcolor; // Color to draw when STYLE_Shaded DWORD fillcolor; // Color to draw when STYLE_Shaded
DWORD Translation;
ActorRenderFlags renderflags; // Different rendering flags
ActorFlags flags;
ActorFlags2 flags2; // Heretic flags
ActorFlags3 flags3; // [RH] Hexen/Heretic actor-dependant behavior made flaggable
ActorFlags4 flags4; // [RH] Even more flags!
ActorFlags5 flags5; // OMG! We need another one.
ActorFlags6 flags6; // Shit! Where did all the flags go?
ActorFlags7 flags7; // WHO WANTS TO BET ON 8!?
double Floorclip; // value to use for floor clipping
double radius, Height; // for movement checking
DAngle VisibleStartAngle;
DAngle VisibleStartPitch;
DAngle VisibleEndAngle;
DAngle VisibleEndPitch;
DVector3 OldRenderPos;
DVector3 Vel;
double Speed;
double FloatSpeed;
// interaction info // interaction info
FBlockNode *BlockNode; // links in blocks (if needed) FBlockNode *BlockNode; // links in blocks (if needed)
@ -1039,24 +1027,22 @@ public:
int floorterrain; int floorterrain;
struct sector_t *ceilingsector; struct sector_t *ceilingsector;
FTextureID ceilingpic; // contacted sec ceilingpic FTextureID ceilingpic; // contacted sec ceilingpic
double radius, Height; // for movement checking
double renderradius; double renderradius;
double projectilepassheight; // height for clipping projectile movement against this actor double projectilepassheight; // height for clipping projectile movement against this actor
double CameraHeight; // Height of camera when used as such
SDWORD tics; // state tic counter double RadiusDamageFactor; // Radius damage factor
double SelfDamageFactor;
double StealthAlpha; // Minmum alpha for MF_STEALTH.
int WoundHealth; // Health needed to enter wound state
int32_t tics; // state tic counter
FState *state; FState *state;
//VMFunction *Damage; // For missiles and monster railgun //VMFunction *Damage; // For missiles and monster railgun
int DamageVal; int DamageVal;
VMFunction *DamageFunc; VMFunction *DamageFunc;
int projectileKickback; int projectileKickback;
ActorFlags flags;
ActorFlags2 flags2; // Heretic flags
ActorFlags3 flags3; // [RH] Hexen/Heretic actor-dependant behavior made flaggable
ActorFlags4 flags4; // [RH] Even more flags!
ActorFlags5 flags5; // OMG! We need another one.
ActorFlags6 flags6; // Shit! Where did all the flags go?
ActorFlags7 flags7; // WHO WANTS TO BET ON 8!?
// [BB] If 0, everybody can see the actor, if > 0, only members of team (VisibleToTeam-1) can see it. // [BB] If 0, everybody can see the actor, if > 0, only members of team (VisibleToTeam-1) can see it.
DWORD VisibleToTeam; DWORD VisibleToTeam;
@ -1076,10 +1062,10 @@ public:
// also the originator for missiles // also the originator for missiles
TObjPtr<AActor> lastenemy; // Last known enemy -- killough 2/15/98 TObjPtr<AActor> lastenemy; // Last known enemy -- killough 2/15/98
TObjPtr<AActor> LastHeard; // [RH] Last actor this one heard TObjPtr<AActor> LastHeard; // [RH] Last actor this one heard
SDWORD reactiontime; // if non 0, don't attack yet; used by int32_t reactiontime; // if non 0, don't attack yet; used by
// player to freeze a bit after teleporting // player to freeze a bit after teleporting
SDWORD threshold; // if > 0, the target will be chased int32_t threshold; // if > 0, the target will be chased
SDWORD DefThreshold; // [MC] Default threshold which the actor will reset its threshold to after switching targets int32_t DefThreshold; // [MC] Default threshold which the actor will reset its threshold to after switching targets
// no matter what (even if shot) // no matter what (even if shot)
player_t *player; // only valid if type of APlayerPawn player_t *player; // only valid if type of APlayerPawn
TObjPtr<AActor> LastLookActor; // Actor last looked for (if TIDtoHate != 0) TObjPtr<AActor> LastLookActor; // Actor last looked for (if TIDtoHate != 0)
@ -1094,7 +1080,6 @@ public:
TObjPtr<AActor> alternative; // (Un)Morphed actors stored here. Those with the MF_UNMORPHED flag are the originals. TObjPtr<AActor> alternative; // (Un)Morphed actors stored here. Those with the MF_UNMORPHED flag are the originals.
TObjPtr<AActor> tracer; // Thing being chased/attacked for tracers TObjPtr<AActor> tracer; // Thing being chased/attacked for tracers
TObjPtr<AActor> master; // Thing which spawned this one (prevents mutual attacks) TObjPtr<AActor> master; // Thing which spawned this one (prevents mutual attacks)
double Floorclip; // value to use for floor clipping
int tid; // thing identifier int tid; // thing identifier
int special; // special int special; // special
@ -1157,7 +1142,8 @@ public:
BYTE smokecounter; BYTE smokecounter;
BYTE FloatBobPhase; BYTE FloatBobPhase;
BYTE FriendPlayer; // [RH] Player # + 1 this friendly monster works for (so 0 is no player, 1 is player 0, etc) BYTE FriendPlayer; // [RH] Player # + 1 this friendly monster works for (so 0 is no player, 1 is player 0, etc)
DWORD Translation; PalEntry BloodColor;
DWORD BloodTranslation;
// [RH] Stuff that used to be part of an Actor Info // [RH] Stuff that used to be part of an Actor Info
FSoundIDNoInit SeeSound; FSoundIDNoInit SeeSound;
@ -1173,7 +1159,7 @@ public:
double MaxDropOffHeight; double MaxDropOffHeight;
double MaxStepHeight; double MaxStepHeight;
SDWORD Mass; int32_t Mass;
SWORD PainChance; SWORD PainChance;
int PainThreshold; int PainThreshold;
FNameNoInit DamageType; FNameNoInit DamageType;
@ -1318,7 +1304,8 @@ public:
} }
DVector3 InterpolatedPosition(double ticFrac) const DVector3 InterpolatedPosition(double ticFrac) const
{ {
return Prev + (ticFrac * (Pos() - Prev)); if (renderflags & RF_DONTINTERPOLATE) return Pos();
else return Prev + (ticFrac * (Pos() - Prev));
} }
DRotator InterpolatedAngles(double ticFrac) const DRotator InterpolatedAngles(double ticFrac) const
{ {

View file

@ -90,7 +90,7 @@ CVAR (Bool, am_customcolors, true, CVAR_ARCHIVE);
CVAR (Int, am_map_secrets, 1, CVAR_ARCHIVE); CVAR (Int, am_map_secrets, 1, CVAR_ARCHIVE);
CVAR (Int, am_drawmapback, 1, CVAR_ARCHIVE); CVAR (Int, am_drawmapback, 1, CVAR_ARCHIVE);
CVAR (Bool, am_showkeys, true, CVAR_ARCHIVE); CVAR (Bool, am_showkeys, true, CVAR_ARCHIVE);
CVAR (Bool, am_showtriggerlines, false, CVAR_ARCHIVE); CVAR (Int, am_showtriggerlines, 0, CVAR_ARCHIVE);
CVAR (Int, am_showthingsprites, 0, CVAR_ARCHIVE); CVAR (Int, am_showthingsprites, 0, CVAR_ARCHIVE);
//============================================================================= //=============================================================================
@ -2281,58 +2281,47 @@ bool AM_checkSpecialBoundary (line_t &line, bool (*function)(int, int *), int *s
return (line.backsector && AM_checkSectorActions(line.backsector, function, specialptr, argsptr, false)); return (line.backsector && AM_checkSectorActions(line.backsector, function, specialptr, argsptr, false));
} }
bool AM_isTeleportSpecial (int special, int *)
{
return (special == Teleport ||
special == Teleport_NoFog ||
special == Teleport_ZombieChanger ||
special == Teleport_Line);
}
bool AM_isTeleportBoundary (line_t &line) bool AM_isTeleportBoundary (line_t &line)
{ {
return AM_checkSpecialBoundary(line, &AM_isTeleportSpecial); return AM_checkSpecialBoundary(line, [](int special, int *)
} {
return (special == Teleport ||
bool AM_isExitSpecial (int special, int *) special == Teleport_NoFog ||
{ special == Teleport_ZombieChanger ||
return (special == Teleport_NewMap || special == Teleport_Line);
special == Teleport_EndGame || });
special == Exit_Normal ||
special == Exit_Secret);
} }
bool AM_isExitBoundary (line_t& line) bool AM_isExitBoundary (line_t& line)
{ {
return AM_checkSpecialBoundary(line, &AM_isExitSpecial); return AM_checkSpecialBoundary(line, [](int special, int *)
} {
return (special == Teleport_NewMap ||
bool AM_isTriggerSpecial (int special, int *) special == Teleport_EndGame ||
{ special == Exit_Normal ||
FLineSpecial *spec = P_GetLineSpecialInfo(special); special == Exit_Secret);
return spec != NULL });
&& spec->max_args >= 0
&& special != Door_Open
&& special != Door_Close
&& special != Door_CloseWaitOpen
&& special != Door_Raise
&& special != Door_Animated
&& special != Generic_Door;
} }
bool AM_isTriggerBoundary (line_t &line) bool AM_isTriggerBoundary (line_t &line)
{ {
return AM_checkSpecialBoundary(line, &AM_isTriggerSpecial); return am_showtriggerlines == 1? AM_checkSpecialBoundary(line, [](int special, int *)
} {
FLineSpecial *spec = P_GetLineSpecialInfo(special);
bool AM_isLockSpecial (int special, int* args) return spec != NULL
{ && spec->max_args >= 0
return special == Door_LockedRaise && special != Door_Open
|| special == ACS_LockedExecute && special != Door_Close
|| special == ACS_LockedExecuteDoor && special != Door_CloseWaitOpen
|| (special == Door_Animated && args[3] != 0) && special != Door_Raise
|| (special == Generic_Door && args[4] != 0) && special != Door_Animated
|| (special == FS_Execute && args[2] != 0); && special != Generic_Door;
}) : AM_checkSpecialBoundary(line, [](int special, int *)
{
FLineSpecial *spec = P_GetLineSpecialInfo(special);
return spec != NULL
&& spec->max_args >= 0;
});
} }
bool AM_isLockBoundary (line_t &line, int *lockptr = NULL) bool AM_isLockBoundary (line_t &line, int *lockptr = NULL)
@ -2351,7 +2340,15 @@ bool AM_isLockBoundary (line_t &line, int *lockptr = NULL)
int special; int special;
int *args; int *args;
bool result = AM_checkSpecialBoundary(line, &AM_isLockSpecial, &special, &args); bool result = AM_checkSpecialBoundary(line, [](int special, int* args)
{
return special == Door_LockedRaise
|| special == ACS_LockedExecute
|| special == ACS_LockedExecuteDoor
|| (special == Door_Animated && args[3] != 0)
|| (special == Generic_Door && args[4] != 0)
|| (special == FS_Execute && args[2] != 0);
}, &special, &args);
if (result) if (result)
{ {

View file

@ -358,7 +358,7 @@ void DBot::WhatToGet (AActor *item)
} }
else if ((typeis (Megasphere) || typeis (Soulsphere) || typeis (HealthBonus)) && player->mo->health >= deh.MaxSoulsphere) else if ((typeis (Megasphere) || typeis (Soulsphere) || typeis (HealthBonus)) && player->mo->health >= deh.MaxSoulsphere)
return; return;
else if (item->IsKindOf (PClass::FindActor(NAME_Health)) && player->mo->health >= player->mo->GetMaxHealth() + player->mo->stamina) else if (item->IsKindOf (PClass::FindActor(NAME_Health)) && player->mo->health >= player->mo->GetMaxHealth(true))
return; return;
if ((dest == NULL || if ((dest == NULL ||

View file

@ -7,7 +7,6 @@ typedef int8_t SBYTE;
typedef uint8_t BYTE; typedef uint8_t BYTE;
typedef int16_t SWORD; typedef int16_t SWORD;
typedef uint16_t WORD; typedef uint16_t WORD;
typedef int32_t SDWORD;
typedef uint32_t uint32; typedef uint32_t uint32;
typedef int64_t SQWORD; typedef int64_t SQWORD;
typedef uint64_t QWORD; typedef uint64_t QWORD;
@ -63,7 +62,7 @@ union QWORD_UNION
#define FRACBITS 16 #define FRACBITS 16
#define FRACUNIT (1<<FRACBITS) #define FRACUNIT (1<<FRACBITS)
typedef SDWORD fixed_t; typedef int32_t fixed_t;
typedef DWORD dsfixed_t; // fixedpt used by span drawer typedef DWORD dsfixed_t; // fixedpt used by span drawer
#define FIXED_MAX (signed)(0x7fffffff) #define FIXED_MAX (signed)(0x7fffffff)

View file

@ -476,7 +476,7 @@ DEFINE_ACTION_FUNCTION(FKeyBindings, GetKeysForCommand)
{ {
PARAM_SELF_STRUCT_PROLOGUE(FKeyBindings); PARAM_SELF_STRUCT_PROLOGUE(FKeyBindings);
PARAM_STRING(cmd); PARAM_STRING(cmd);
int k1, k2, c; int k1, k2;
self->GetKeysForCommand(cmd.GetChars(), &k1, &k2); self->GetKeysForCommand(cmd.GetChars(), &k1, &k2);
if (numret > 0) ret[0].SetInt(k1); if (numret > 0) ret[0].SetInt(k1);
if (numret > 1) ret[1].SetInt(k2); if (numret > 1) ret[1].SetInt(k2);

View file

@ -732,34 +732,6 @@ CCMD (dir)
chdir (curdir); chdir (curdir);
} }
CCMD (fov)
{
player_t *player = who ? who->player : &players[consoleplayer];
if (argv.argc() != 2)
{
Printf ("fov is %g\n", player->DesiredFOV);
return;
}
else if (dmflags & DF_NO_FOV)
{
if (consoleplayer == Net_Arbitrator)
{
Net_WriteByte (DEM_FOV);
}
else
{
Printf ("A setting controller has disabled FOV changes.\n");
return;
}
}
else
{
Net_WriteByte (DEM_MYFOV);
}
Net_WriteByte (clamp (atoi (argv[1]), 5, 179));
}
//========================================================================== //==========================================================================
// //
// CCMD warp // CCMD warp
@ -880,7 +852,7 @@ CCMD(linetarget)
if (CheckCheatmode () || players[consoleplayer].mo == NULL) return; if (CheckCheatmode () || players[consoleplayer].mo == NULL) return;
C_AimLine(&t, false); C_AimLine(&t, false);
if (t.linetarget) if (t.linetarget)
C_PrintInfo(t.linetarget); C_PrintInfo(t.linetarget, argv.argc() > 1 && atoi(argv[1]) != 0);
else else
Printf("No target found\n"); Printf("No target found\n");
} }
@ -893,7 +865,7 @@ CCMD(info)
if (CheckCheatmode () || players[consoleplayer].mo == NULL) return; if (CheckCheatmode () || players[consoleplayer].mo == NULL) return;
C_AimLine(&t, true); C_AimLine(&t, true);
if (t.linetarget) if (t.linetarget)
C_PrintInfo(t.linetarget); C_PrintInfo(t.linetarget, !(argv.argc() > 1 && atoi(argv[1]) == 0));
else else
Printf("No target found. Info cannot find actors that have " Printf("No target found. Info cannot find actors that have "
"the NOBLOCKMAP flag or have height/radius of 0.\n"); "the NOBLOCKMAP flag or have height/radius of 0.\n");
@ -902,7 +874,7 @@ CCMD(info)
CCMD(myinfo) CCMD(myinfo)
{ {
if (CheckCheatmode () || players[consoleplayer].mo == NULL) return; if (CheckCheatmode () || players[consoleplayer].mo == NULL) return;
C_PrintInfo(players[consoleplayer].mo); C_PrintInfo(players[consoleplayer].mo, true);
} }
typedef bool (*ActorTypeChecker) (AActor *); typedef bool (*ActorTypeChecker) (AActor *);
@ -937,14 +909,20 @@ static void PrintFilteredActorList(const ActorTypeChecker IsActorType, const cha
AActor *mo; AActor *mo;
const PClass *FilterClass = NULL; const PClass *FilterClass = NULL;
int counter = 0; int counter = 0;
int tid = 0;
if (FilterName != NULL) if (FilterName != NULL)
{ {
FilterClass = PClass::FindActor(FilterName); FilterClass = PClass::FindActor(FilterName);
if (FilterClass == NULL) if (FilterClass == NULL)
{ {
Printf("%s is not an actor class.\n", FilterName); char *endp;
return; tid = (int)strtol(FilterName, &endp, 10);
if (*endp != 0)
{
Printf("%s is not an actor class.\n", FilterName);
return;
}
} }
} }
TThinkerIterator<AActor> it; TThinkerIterator<AActor> it;
@ -953,10 +931,13 @@ static void PrintFilteredActorList(const ActorTypeChecker IsActorType, const cha
{ {
if ((FilterClass == NULL || mo->IsA(FilterClass)) && IsActorType(mo)) if ((FilterClass == NULL || mo->IsA(FilterClass)) && IsActorType(mo))
{ {
counter++; if (tid == 0 || tid == mo->tid)
if (!countOnly) {
Printf ("%s at (%f,%f,%f)\n", counter++;
mo->GetClass()->TypeName.GetChars(), mo->X(), mo->Y(), mo->Z()); if (!countOnly)
Printf("%s at (%f,%f,%f)\n",
mo->GetClass()->TypeName.GetChars(), mo->X(), mo->Y(), mo->Z());
}
} }
} }
Printf("%i match(s) found.\n", counter); Printf("%i match(s) found.\n", counter);

View file

@ -206,6 +206,9 @@ struct FCommandBuffer
unsigned CursorPos; unsigned CursorPos;
unsigned StartPos; // First character to display unsigned StartPos; // First character to display
FString YankBuffer; // Deleted text buffer
bool AppendToYankBuffer; // Append consecutive deletes to buffer
FCommandBuffer() FCommandBuffer()
{ {
CursorPos = StartPos = 0; CursorPos = StartPos = 0;
@ -278,6 +281,30 @@ struct FCommandBuffer
StartPos = MAX(0, n); StartPos = MAX(0, n);
} }
unsigned WordBoundaryRight()
{
unsigned index = CursorPos;
while (index < Text.Len() && Text[index] == ' ') {
index++;
}
while (index < Text.Len() && Text[index] != ' ') {
index++;
}
return index;
}
unsigned WordBoundaryLeft()
{
int index = CursorPos - 1;
while (index > -1 && Text[index] == ' ') {
index--;
}
while (index > -1 && Text[index] != ' ') {
index--;
}
return (unsigned)index + 1;
}
void CursorStart() void CursorStart()
{ {
CursorPos = 0; CursorPos = 0;
@ -309,6 +336,18 @@ struct FCommandBuffer
} }
} }
void CursorWordLeft()
{
CursorPos = WordBoundaryLeft();
MakeStartPosGood();
}
void CursorWordRight()
{
CursorPos = WordBoundaryRight();
MakeStartPosGood();
}
void DeleteLeft() void DeleteLeft()
{ {
if (CursorPos > 0) if (CursorPos > 0)
@ -328,6 +367,50 @@ struct FCommandBuffer
} }
} }
void DeleteWordLeft()
{
if (CursorPos > 0)
{
unsigned index = WordBoundaryLeft();
if (AppendToYankBuffer) {
YankBuffer = FString(&Text[index], CursorPos - index) + YankBuffer;
} else {
YankBuffer = FString(&Text[index], CursorPos - index);
}
Text.Remove(index, CursorPos - index);
CursorPos = index;
MakeStartPosGood();
}
}
void DeleteLineLeft()
{
if (CursorPos > 0)
{
if (AppendToYankBuffer) {
YankBuffer = FString(&Text[0], CursorPos) + YankBuffer;
} else {
YankBuffer = FString(&Text[0], CursorPos);
}
Text.Remove(0, CursorPos);
CursorStart();
}
}
void DeleteLineRight()
{
if (CursorPos < Text.Len())
{
if (AppendToYankBuffer) {
YankBuffer += FString(&Text[CursorPos], Text.Len() - CursorPos);
} else {
YankBuffer = FString(&Text[CursorPos], Text.Len() - CursorPos);
}
Text.Truncate(CursorPos);
CursorEnd();
}
}
void AddChar(int character) void AddChar(int character)
{ {
///FIXME: Not Unicode-aware ///FIXME: Not Unicode-aware
@ -1180,7 +1263,7 @@ void C_DrawConsole (bool hw2d)
DTA_KeepRatio, true, TAG_DONE); DTA_KeepRatio, true, TAG_DONE);
// Draw the marker // Draw the marker
i = LEFTMARGIN+5+tickbegin*8 + Scale (TickerAt, (SDWORD)(tickend - tickbegin)*8, TickerMax); i = LEFTMARGIN+5+tickbegin*8 + Scale (TickerAt, (int32_t)(tickend - tickbegin)*8, TickerMax);
if (textScale == 1) if (textScale == 1)
screen->DrawChar (ConFont, CR_ORANGE, (int)i, tickerY, 0x13, TAG_DONE); screen->DrawChar (ConFont, CR_ORANGE, (int)i, tickerY, 0x13, TAG_DONE);
else else
@ -1345,6 +1428,7 @@ DEFINE_ACTION_FUNCTION(_Console, Printf)
static bool C_HandleKey (event_t *ev, FCommandBuffer &buffer) static bool C_HandleKey (event_t *ev, FCommandBuffer &buffer)
{ {
int data1 = ev->data1; int data1 = ev->data1;
bool keepappending = false;
switch (ev->subtype) switch (ev->subtype)
{ {
@ -1352,8 +1436,22 @@ static bool C_HandleKey (event_t *ev, FCommandBuffer &buffer)
return false; return false;
case EV_GUI_Char: case EV_GUI_Char:
if (ev->data2)
{
// Bash-style shortcuts
if (data1 == 'b')
{
buffer.CursorWordLeft();
break;
}
else if (data1 == 'f')
{
buffer.CursorWordRight();
break;
}
}
// Add keypress to command line // Add keypress to command line
buffer.AddChar(ev->data1); buffer.AddChar(data1);
HistPos = NULL; HistPos = NULL;
TabbedLast = false; TabbedLast = false;
TabbedList = false; TabbedList = false;
@ -1654,6 +1752,56 @@ static bool C_HandleKey (event_t *ev, FCommandBuffer &buffer)
break; break;
} }
break; break;
// Bash-style shortcuts
case 'A':
if (ev->data3 & GKM_CTRL)
{
buffer.CursorStart();
}
break;
case 'E':
if (ev->data3 & GKM_CTRL)
{
buffer.CursorEnd();
}
break;
case 'W':
if (ev->data3 & GKM_CTRL)
{
buffer.DeleteWordLeft();
keepappending = true;
TabbedLast = false;
TabbedList = false;
}
break;
case 'U':
if (ev->data3 & GKM_CTRL)
{
buffer.DeleteLineLeft();
keepappending = true;
TabbedLast = false;
TabbedList = false;
}
break;
case 'K':
if (ev->data3 & GKM_CTRL)
{
buffer.DeleteLineRight();
keepappending = true;
TabbedLast = false;
TabbedList = false;
}
break;
case 'Y':
if (ev->data3 & GKM_CTRL)
{
buffer.AddString(buffer.YankBuffer);
TabbedLast = false;
TabbedList = false;
HistPos = NULL;
}
break;
} }
break; break;
@ -1664,6 +1812,9 @@ static bool C_HandleKey (event_t *ev, FCommandBuffer &buffer)
break; break;
#endif #endif
} }
buffer.AppendToYankBuffer = keepappending;
// Ensure that the cursor is always visible while typing // Ensure that the cursor is always visible while typing
CursorTicker = C_BLINKRATE; CursorTicker = C_BLINKRATE;
cursoron = 1; cursoron = 1;

View file

@ -207,7 +207,7 @@ DEFINE_ACTION_FUNCTION(_CVar, SetInt)
{ {
// Only menus are allowed to change CVARs. // Only menus are allowed to change CVARs.
PARAM_SELF_STRUCT_PROLOGUE(FBaseCVar); PARAM_SELF_STRUCT_PROLOGUE(FBaseCVar);
if (!(self->GetFlags() & CVAR_MOD) && DMenu::CurrentMenu == nullptr) return 0; if (!(self->GetFlags() & CVAR_MOD) && CurrentMenu == nullptr) return 0;
PARAM_INT(val); PARAM_INT(val);
UCVarValue v; UCVarValue v;
v.Int = val; v.Int = val;
@ -219,7 +219,7 @@ DEFINE_ACTION_FUNCTION(_CVar, SetFloat)
{ {
// Only menus are allowed to change CVARs. // Only menus are allowed to change CVARs.
PARAM_SELF_STRUCT_PROLOGUE(FBaseCVar); PARAM_SELF_STRUCT_PROLOGUE(FBaseCVar);
if (!(self->GetFlags() & CVAR_MOD) && DMenu::CurrentMenu == nullptr) return 0; if (!(self->GetFlags() & CVAR_MOD) && CurrentMenu == nullptr) return 0;
PARAM_FLOAT(val); PARAM_FLOAT(val);
UCVarValue v; UCVarValue v;
v.Float = (float)val; v.Float = (float)val;
@ -231,7 +231,7 @@ DEFINE_ACTION_FUNCTION(_CVar, SetString)
{ {
// Only menus are allowed to change CVARs. // Only menus are allowed to change CVARs.
PARAM_SELF_STRUCT_PROLOGUE(FBaseCVar); PARAM_SELF_STRUCT_PROLOGUE(FBaseCVar);
if (!(self->GetFlags() & CVAR_MOD) && DMenu::CurrentMenu == nullptr) return 0; if (!(self->GetFlags() & CVAR_MOD) && CurrentMenu == nullptr) return 0;
PARAM_STRING(val); PARAM_STRING(val);
UCVarValue v; UCVarValue v;
v.String = val.GetChars(); v.String = val.GetChars();

View file

@ -666,7 +666,7 @@ void C_DoCommand (const char *cmd, int keynum)
// This is only accessible to the special menu item to run CCMDs. // This is only accessible to the special menu item to run CCMDs.
DEFINE_ACTION_FUNCTION(DOptionMenuItemCommand, DoCommand) DEFINE_ACTION_FUNCTION(DOptionMenuItemCommand, DoCommand)
{ {
if (DMenu::CurrentMenu == nullptr) return 0; if (CurrentMenu == nullptr) return 0;
PARAM_PROLOGUE; PARAM_PROLOGUE;
PARAM_STRING(cmd); PARAM_STRING(cmd);
C_DoCommand(cmd); C_DoCommand(cmd);

View file

@ -39,7 +39,7 @@
#include "c_functions.h" #include "c_functions.h"
void C_PrintInfo(AActor *target) void C_PrintInfo(AActor *target, bool verbose)
{ {
if (target->player) if (target->player)
Printf("Player=%s, ", target->player->userinfo.GetName()); Printf("Player=%s, ", target->player->userinfo.GetName());
@ -47,7 +47,7 @@ void C_PrintInfo(AActor *target)
target->GetClass()->TypeName.GetChars(), target->GetClass()->TypeName.GetChars(),
target->health, target->health,
target->SpawnHealth()); target->SpawnHealth());
PrintMiscActorInfo(target); if (verbose) PrintMiscActorInfo(target);
} }
void C_AimLine(FTranslatedLineTarget *t, bool nonshootable) void C_AimLine(FTranslatedLineTarget *t, bool nonshootable)

View file

@ -34,6 +34,6 @@
void C_PrintInv(AActor *target); void C_PrintInv(AActor *target);
void C_AimLine(FTranslatedLineTarget *t, bool nonshootable); void C_AimLine(FTranslatedLineTarget *t, bool nonshootable);
void C_PrintInfo(AActor *target); void C_PrintInfo(AActor *target, bool verbose);
struct FTranslatedLineTarget; struct FTranslatedLineTarget;

View file

@ -95,23 +95,6 @@ char *copystring (const char *s)
return b; 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 // ReplaceString

View file

@ -38,7 +38,6 @@ int ParseHex(const char *str, FScriptPosition *sc = nullptr);
bool IsNum (const char *str); // [RH] added bool IsNum (const char *str); // [RH] added
char *copystring(const char *s); char *copystring(const char *s);
char *ncopystring(const char *s);
void ReplaceString (char **ptr, const char *str); void ReplaceString (char **ptr, const char *str);
bool CheckWildcards (const char *pattern, const char *text); bool CheckWildcards (const char *pattern, const char *text);

View file

@ -86,6 +86,7 @@ enum
CP_SETTHINGZ, CP_SETTHINGZ,
CP_SETTAG, CP_SETTAG,
CP_SETTHINGFLAGS, CP_SETTHINGFLAGS,
CP_SETVERTEX,
}; };
// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
@ -345,6 +346,18 @@ void ParseCompatibility()
sc.MustGetNumber(); sc.MustGetNumber();
CompatParams.Push(sc.Number); CompatParams.Push(sc.Number);
} }
else if (sc.Compare("setvertex"))
{
if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
CompatParams.Push(CP_SETVERTEX);
sc.MustGetNumber();
CompatParams.Push(sc.Number);
sc.MustGetFloat();
CompatParams.Push(int(sc.Float * 256)); // do not use full fixed here so that it can eventually handle larger levels
sc.MustGetFloat();
CompatParams.Push(int(sc.Float * 256)); // do not use full fixed here so that it can eventually handle larger levels
flags.CompatFlags[SLOT_BCOMPAT] |= BCOMPATF_REBUILDNODES;
}
else else
{ {
sc.UnGet(); sc.UnGet();
@ -600,6 +613,16 @@ void SetCompatibilityParams()
i += 3; i += 3;
break; break;
} }
case CP_SETVERTEX:
{
if ((unsigned)CompatParams[i + 1] < level.vertexes.Size())
{
level.vertexes[CompatParams[i + 1]].p.X = CompatParams[i + 2] / 256.;
level.vertexes[CompatParams[i + 1]].p.Y = CompatParams[i + 3] / 256.;
}
i += 4;
break;
}
} }
} }
} }

View file

@ -1942,13 +1942,13 @@ static int PatchMisc (int dummy)
if (armor!=NULL) if (armor!=NULL)
{ {
armor->IntVar(NAME_SaveAmount) = 100 * deh.GreenAC; armor->IntVar(NAME_SaveAmount) = 100 * deh.GreenAC;
armor->FloatVar(NAME_SavePercent) = deh.GreenAC == 1 ? 0.33335 : 0.5; armor->FloatVar(NAME_SavePercent) = deh.GreenAC == 1 ? 33.335 : 50;
} }
armor = GetDefaultByName ("BlueArmor"); armor = GetDefaultByName ("BlueArmor");
if (armor!=NULL) if (armor!=NULL)
{ {
armor->IntVar(NAME_SaveAmount) = 100 * deh.BlueAC; armor->IntVar(NAME_SaveAmount) = 100 * deh.BlueAC;
armor->FloatVar(NAME_SavePercent) = deh.BlueAC == 1 ? 0.33335 : 0.5; armor->FloatVar(NAME_SavePercent) = deh.BlueAC == 1 ? 33.335 : 50;
} }
auto barmor = GetDefaultByName ("ArmorBonus"); auto barmor = GetDefaultByName ("ArmorBonus");

View file

@ -55,7 +55,7 @@ struct event_t
}; };
typedef enum enum gameaction_t : int
{ {
ga_nothing, ga_nothing,
ga_loadlevel, ga_loadlevel,
@ -75,7 +75,7 @@ typedef enum
ga_screenshot, ga_screenshot,
ga_togglemap, ga_togglemap,
ga_fullconsole, ga_fullconsole,
} gameaction_t; };

View file

@ -810,6 +810,10 @@ void D_Display ()
{ {
StatusBar->DrawBottomStuff (HUD_AltHud); StatusBar->DrawBottomStuff (HUD_AltHud);
if (DrawFSHUD || automapactive) DrawHUD(); if (DrawFSHUD || automapactive) DrawHUD();
if (players[consoleplayer].camera && players[consoleplayer].camera->player)
{
StatusBar->DrawCrosshair();
}
StatusBar->Draw (HUD_AltHud); StatusBar->Draw (HUD_AltHud);
StatusBar->DrawTopStuff (HUD_AltHud); StatusBar->DrawTopStuff (HUD_AltHud);
} }
@ -2688,6 +2692,7 @@ void D_DoomMain (void)
// These calls from inside V_Init2 are still necessary // These calls from inside V_Init2 are still necessary
C_NewModeAdjust(); C_NewModeAdjust();
M_InitVideoModesMenu(); M_InitVideoModesMenu();
Renderer->RemapVoxels();
D_StartTitle (); // start up intro loop D_StartTitle (); // start up intro loop
setmodeneeded = false; // This may be set to true here, but isn't needed for a restart setmodeneeded = false; // This may be set to true here, but isn't needed for a restart
} }
@ -2708,6 +2713,7 @@ void D_DoomMain (void)
// clean up game state // clean up game state
ST_Clear(); ST_Clear();
D_ErrorCleanup (); D_ErrorCleanup ();
DThinker::DestroyThinkersInList(STAT_STATIC);
P_FreeLevelData(); P_FreeLevelData();
P_FreeExtraLevelData(); P_FreeExtraLevelData();

View file

@ -73,7 +73,7 @@ EXTERN_CVAR (Int, autosavecount)
#define SIMULATEERRORS 0 #define SIMULATEERRORS 0
extern BYTE *demo_p; // [RH] Special "ticcmds" get recorded in demos extern BYTE *demo_p; // [RH] Special "ticcmds" get recorded in demos
extern char savedescription[SAVESTRINGSIZE]; extern FString savedescription;
extern FString savegamefile; extern FString savegamefile;
extern short consistancy[MAXPLAYERS][BACKUPTICS]; extern short consistancy[MAXPLAYERS][BACKUPTICS];
@ -2271,6 +2271,9 @@ void Net_DoCommand (int type, BYTE **stream, int player)
case DEM_INVDROP: case DEM_INVDROP:
{ {
DWORD which = ReadLong (stream); DWORD which = ReadLong (stream);
int amt = -1;
if (type == DEM_INVDROP) amt = ReadLong(stream);
if (gamestate == GS_LEVEL && !paused if (gamestate == GS_LEVEL && !paused
&& players[player].playerstate != PST_DEAD) && players[player].playerstate != PST_DEAD)
@ -2288,7 +2291,7 @@ void Net_DoCommand (int type, BYTE **stream, int player)
} }
else else
{ {
players[player].mo->DropInventory (item); players[player].mo->DropInventory (item, amt);
} }
} }
} }
@ -2373,25 +2376,13 @@ void Net_DoCommand (int type, BYTE **stream, int player)
break; break;
case DEM_SPRAY: case DEM_SPRAY:
{ s = ReadString(stream);
FTraceResults trace; SprayDecal(players[player].mo, s);
break;
DAngle ang = players[player].mo->Angles.Yaw; case DEM_MDK:
DAngle pitch = players[player].mo->Angles.Pitch; s = ReadString(stream);
double c = pitch.Cos(); cht_DoMDK(&players[player], s);
DVector3 vec(c * ang.Cos(), c * ang.Sin(), -pitch.Sin());
s = ReadString (stream);
if (Trace (players[player].mo->PosPlusZ(players[player].mo->Height/2), players[player].mo->Sector,
vec, 172., 0, ML_BLOCKEVERYTHING, players[player].mo, trace, TRACE_NoSky))
{
if (trace.HitType == TRACE_HitWall)
{
DImpactDecal::StaticCreate (s, trace.HitPos, trace.Line->sidedef[trace.Side], NULL);
}
}
}
break; break;
case DEM_PAUSE: case DEM_PAUSE:
@ -2418,8 +2409,7 @@ void Net_DoCommand (int type, BYTE **stream, int player)
savegamefile = s; savegamefile = s;
delete[] s; delete[] s;
s = ReadString (stream); s = ReadString (stream);
memset (savedescription, 0, sizeof(savedescription)); savedescription = s;
strncpy (savedescription, s, sizeof(savedescription));
if (player != consoleplayer) if (player != consoleplayer)
{ {
// Paths sent over the network will be valid for the system that sent // Paths sent over the network will be valid for the system that sent
@ -2681,12 +2671,12 @@ void Net_DoCommand (int type, BYTE **stream, int player)
case DEM_NETEVENT: case DEM_NETEVENT:
{ {
FString ename = ReadString(stream); s = ReadString(stream);
int argn = ReadByte(stream); int argn = ReadByte(stream);
int arg[3] = { 0, 0, 0 }; int arg[3] = { 0, 0, 0 };
for (int i = 0; i < argn; i++) for (int i = 0; i < 3; i++)
arg[i] = ReadLong(stream); arg[i] = ReadLong(stream);
E_Console(player, ename, arg[0], arg[1], arg[2]); E_Console(player, s, arg[0], arg[1], arg[2]);
} }
break; break;
@ -2736,6 +2726,10 @@ void Net_SkipCommand (int type, BYTE **stream)
skip = strlen ((char *)(*stream)) + 5; skip = strlen ((char *)(*stream)) + 5;
break; break;
case DEM_NETEVENT:
skip = strlen((char *)(*stream)) + 14;
break;
case DEM_SUMMON2: case DEM_SUMMON2:
case DEM_SUMMONFRIEND2: case DEM_SUMMONFRIEND2:
case DEM_SUMMONFOE2: case DEM_SUMMONFOE2:
@ -2757,6 +2751,7 @@ void Net_SkipCommand (int type, BYTE **stream)
case DEM_SPRAY: case DEM_SPRAY:
case DEM_MORPHEX: case DEM_MORPHEX:
case DEM_KILLCLASSCHEAT: case DEM_KILLCLASSCHEAT:
case DEM_MDK:
skip = strlen ((char *)(*stream)) + 1; skip = strlen ((char *)(*stream)) + 1;
break; break;
@ -2765,10 +2760,13 @@ void Net_SkipCommand (int type, BYTE **stream)
break; break;
case DEM_INVUSE: case DEM_INVUSE:
case DEM_INVDROP:
skip = 4; skip = 4;
break; break;
case DEM_INVDROP:
skip = 8;
break;
case DEM_GENERICCHEAT: case DEM_GENERICCHEAT:
case DEM_DROPPLAYER: case DEM_DROPPLAYER:
case DEM_FOV: case DEM_FOV:

View file

@ -729,7 +729,7 @@ void D_WriteUserInfoStrings (int pnum, BYTE **stream, bool compact)
break; break;
case NAME_Skin: case NAME_Skin:
*stream += sprintf(*((char **)stream), "\\%s", D_EscapeUserInfo(skins[info->GetSkin()].name).GetChars()); *stream += sprintf(*((char **)stream), "\\%s", D_EscapeUserInfo(Skins[info->GetSkin()].Name).GetChars());
break; break;
default: default:
@ -828,7 +828,7 @@ void D_ReadUserInfoStrings (int pnum, BYTE **stream, bool update)
players[pnum].mo->state->sprite == players[pnum].mo->state->sprite ==
GetDefaultByType (players[pnum].cls)->SpawnState->sprite) GetDefaultByType (players[pnum].cls)->SpawnState->sprite)
{ // Only change the sprite if the player is using a standard one { // Only change the sprite if the player is using a standard one
players[pnum].mo->sprite = skins[info->GetSkin()].sprite; players[pnum].mo->sprite = Skins[info->GetSkin()].sprite;
} }
} }
// Rebuild translation in case the new skin uses a different range // Rebuild translation in case the new skin uses a different range
@ -898,7 +898,7 @@ void WriteUserInfo(FSerializer &arc, userinfo_t &info)
switch (pair->Key.GetIndex()) switch (pair->Key.GetIndex())
{ {
case NAME_Skin: case NAME_Skin:
string = skins[info.GetSkin()].name; string = Skins[info.GetSkin()].Name;
break; break;
case NAME_PlayerClass: case NAME_PlayerClass:
@ -986,7 +986,7 @@ CCMD (playerinfo)
// Print special info // Print special info
Printf("%20s: %s\n", "Name", ui->GetName()); Printf("%20s: %s\n", "Name", ui->GetName());
Printf("%20s: %s (%d)\n", "Team", ui->GetTeam() == TEAM_NONE ? "None" : Teams[ui->GetTeam()].GetName(), ui->GetTeam()); Printf("%20s: %s (%d)\n", "Team", ui->GetTeam() == TEAM_NONE ? "None" : Teams[ui->GetTeam()].GetName(), ui->GetTeam());
Printf("%20s: %s (%d)\n", "Skin", skins[ui->GetSkin()].name, ui->GetSkin()); Printf("%20s: %s (%d)\n", "Skin", Skins[ui->GetSkin()].Name.GetChars(), ui->GetSkin());
Printf("%20s: %s (%d)\n", "Gender", GenderNames[ui->GetGender()], ui->GetGender()); Printf("%20s: %s (%d)\n", "Gender", GenderNames[ui->GetGender()], ui->GetGender());
Printf("%20s: %s (%d)\n", "PlayerClass", Printf("%20s: %s (%d)\n", "PlayerClass",
ui->GetPlayerClassNum() == -1 ? "Random" : ui->GetPlayerClassType()->DisplayName.GetChars(), ui->GetPlayerClassNum() == -1 ? "Random" : ui->GetPlayerClassType()->DisplayName.GetChars(),

View file

@ -93,7 +93,7 @@ public:
virtual bool UpdateWaterLevel (bool splash) override; virtual bool UpdateWaterLevel (bool splash) override;
bool ResetAirSupply (bool playgasp = true); bool ResetAirSupply (bool playgasp = true);
int GetMaxHealth() const; int GetMaxHealth(bool withupgrades = false) const;
void TweakSpeeds (double &forwardmove, double &sidemove); void TweakSpeeds (double &forwardmove, double &sidemove);
void MorphPlayerThink (); void MorphPlayerThink ();
void ActivateMorphWeapon (); void ActivateMorphWeapon ();
@ -125,6 +125,8 @@ public:
int crouchsprite; int crouchsprite;
int MaxHealth; int MaxHealth;
int BonusHealth;
int MugShotMaxHealth; int MugShotMaxHealth;
int RunHealth; int RunHealth;
int PlayerFlags; int PlayerFlags;
@ -159,8 +161,6 @@ public:
FNameNoInit Face; // Doom status bar face (when used) FNameNoInit Face; // Doom status bar face (when used)
FNameNoInit Portrait; FNameNoInit Portrait;
FNameNoInit Slot[10]; FNameNoInit Slot[10];
FNameNoInit InvulMode;
FNameNoInit HealingRadiusType;
double HexenArmor[5]; double HexenArmor[5];
BYTE ColorRangeStart; // Skin color range BYTE ColorRangeStart; // Skin color range
BYTE ColorRangeEnd; BYTE ColorRangeEnd;
@ -209,13 +209,7 @@ typedef enum
CF_TOTALLYFROZEN = 1 << 12, // [RH] All players can do is press +use CF_TOTALLYFROZEN = 1 << 12, // [RH] All players can do is press +use
CF_PREDICTING = 1 << 13, // [RH] Player movement is being predicted CF_PREDICTING = 1 << 13, // [RH] Player movement is being predicted
CF_INTERPVIEW = 1 << 14, // [RH] view was changed outside of input, so interpolate one frame CF_INTERPVIEW = 1 << 14, // [RH] view was changed outside of input, so interpolate one frame
CF_DRAIN = 1 << 16, // Player owns a drain powerup
CF_HIGHJUMP = 1 << 18, // more Skulltag flags. Implementation not guaranteed though. ;)
CF_REFLECTION = 1 << 19,
CF_PROSPERITY = 1 << 20,
CF_DOUBLEFIRINGSPEED= 1 << 21, // Player owns a double firing speed artifact
CF_EXTREMELYDEAD = 1 << 22, // [RH] Reliably let the status bar know about extreme deaths. CF_EXTREMELYDEAD = 1 << 22, // [RH] Reliably let the status bar know about extreme deaths.
CF_INFINITEAMMO = 1 << 23, // Player owns an infinite ammo artifact
CF_BUDDHA2 = 1 << 24, // [MC] Absolute buddha. No voodoo can kill it either. CF_BUDDHA2 = 1 << 24, // [MC] Absolute buddha. No voodoo can kill it either.
CF_GODMODE2 = 1 << 25, // [MC] Absolute godmode. No voodoo can kill it either. CF_GODMODE2 = 1 << 25, // [MC] Absolute godmode. No voodoo can kill it either.
CF_BUDDHA = 1 << 27, // [SP] Buddha mode - take damage, but don't die CF_BUDDHA = 1 << 27, // [SP] Buddha mode - take damage, but don't die
@ -297,6 +291,11 @@ struct userinfo_t : TMap<FName,FBaseCVar *>
return aim; return aim;
} }
} }
// Same but unfiltered.
double GetAutoaim() const
{
return *static_cast<FFloatCVar *>(*CheckKey(NAME_Autoaim));
}
const char *GetName() const const char *GetName() const
{ {
return *static_cast<FStringCVar *>(*CheckKey(NAME_Name)); return *static_cast<FStringCVar *>(*CheckKey(NAME_Name));
@ -530,6 +529,9 @@ public:
DPSprite *GetPSprite(PSPLayers layer); DPSprite *GetPSprite(PSPLayers layer);
bool GetPainFlash(FName type, PalEntry *color) const; bool GetPainFlash(FName type, PalEntry *color) const;
// [Nash] set player FOV
void SetFOV(float fov);
}; };
// Bookkeeping on players - state. // Bookkeeping on players - state.

View file

@ -159,7 +159,8 @@ enum EDemoCommand
DEM_SETSLOTPNUM, // 67 Byte: player number, the rest is the same as DEM_SETSLOT DEM_SETSLOTPNUM, // 67 Byte: player number, the rest is the same as DEM_SETSLOT
DEM_REMOVE, // 68 DEM_REMOVE, // 68
DEM_FINISHGAME, // 69 DEM_FINISHGAME, // 69
DEM_NETEVENT // 70 String: Event name, Byte: Arg count; each arg is a 4-byte int DEM_NETEVENT, // 70 String: Event name, Byte: Arg count; each arg is a 4-byte int
DEM_MDK // 71 String: Damage type
}; };
// The following are implemented by cht_DoCheat in m_cheat.cpp // The following are implemented by cht_DoCheat in m_cheat.cpp

View file

@ -623,12 +623,20 @@ DEFINE_ACTION_FUNCTION(DObject, MSTime)
void *DObject::ScriptVar(FName field, PType *type) void *DObject::ScriptVar(FName field, PType *type)
{ {
auto sym = dyn_cast<PField>(GetClass()->Symbols.FindSymbol(field, true)); auto cls = GetClass();
auto sym = dyn_cast<PField>(cls->Symbols.FindSymbol(field, true));
if (sym && (sym->Type == type || type == nullptr)) if (sym && (sym->Type == type || type == nullptr))
{ {
return (((char*)this) + sym->Offset); if (!(sym->Flags & VARF_Meta))
{
return (((char*)this) + sym->Offset);
}
else
{
return (cls->Meta + sym->Offset);
}
} }
// This is only for internal use so I_Error is fine. // This is only for internal use so I_Error is fine.
I_Error("Variable %s not found in %s\n", field.GetChars(), GetClass()->TypeName.GetChars()); I_Error("Variable %s not found in %s\n", field.GetChars(), cls->TypeName.GetChars());
return nullptr; return nullptr;
} }

View file

@ -41,6 +41,7 @@
class PClass; class PClass;
class PType; class PType;
class FSerializer; class FSerializer;
class FSoundID;
class DObject; class DObject;
/* /*
@ -165,10 +166,11 @@ protected: \
_X_CONSTRUCTOR_##isabstract(cls) \ _X_CONSTRUCTOR_##isabstract(cls) \
_IMP_PCLASS(cls, _X_POINTERS_##ptrs(cls), _X_ABSTRACT_##isabstract(cls)) _IMP_PCLASS(cls, _X_POINTERS_##ptrs(cls), _X_ABSTRACT_##isabstract(cls))
// Taking the address of a field in an object at address 1 instead of // Taking the address of a field in an object at address > 0 instead of
// address 0 keeps GCC from complaining about possible misuse of offsetof. // address 0 keeps GCC from complaining about possible misuse of offsetof.
// Using 8 to avoid unaligned pointer use.
#define IMPLEMENT_POINTERS_START(cls) const size_t cls::PointerOffsets[] = { #define IMPLEMENT_POINTERS_START(cls) const size_t cls::PointerOffsets[] = {
#define IMPLEMENT_POINTER(field) (size_t)&((ThisClass*)1)->field - 1, #define IMPLEMENT_POINTER(field) ((size_t)&((ThisClass*)8)->field) - 8,
#define IMPLEMENT_POINTERS_END ~(size_t)0 }; #define IMPLEMENT_POINTERS_END ~(size_t)0 };
// Possible arguments for the IMPLEMENT_CLASS macro // Possible arguments for the IMPLEMENT_CLASS macro
@ -484,9 +486,11 @@ public:
// Add other types as needed. // Add other types as needed.
bool &BoolVar(FName field); bool &BoolVar(FName field);
int &IntVar(FName field); int &IntVar(FName field);
FSoundID &SoundVar(FName field);
PalEntry &ColorVar(FName field); PalEntry &ColorVar(FName field);
FName &NameVar(FName field); FName &NameVar(FName field);
double &FloatVar(FName field); double &FloatVar(FName field);
FString &StringVar(FName field);
template<class T> T*& PointerVar(FName field); template<class T> T*& PointerVar(FName field);
// If you need to replace one object with another and want to // If you need to replace one object with another and want to

View file

@ -78,6 +78,7 @@
#include "menu/menu.h" #include "menu/menu.h"
#include "intermission/intermission.h" #include "intermission/intermission.h"
#include "g_levellocals.h" #include "g_levellocals.h"
#include "events.h"
// MACROS ------------------------------------------------------------------ // MACROS ------------------------------------------------------------------
@ -331,6 +332,8 @@ static void MarkRoot()
DThinker::MarkRoots(); DThinker::MarkRoots();
FCanvasTextureInfo::Mark(); FCanvasTextureInfo::Mark();
Mark(DACSThinker::ActiveThinker); Mark(DACSThinker::ActiveThinker);
Mark(E_FirstEventHandler);
Mark(E_LastEventHandler);
for (auto &s : level.sectorPortals) for (auto &s : level.sectorPortals)
{ {
Mark(s.mSkybox); Mark(s.mSkybox);
@ -544,7 +547,6 @@ void FullGC()
void Barrier(DObject *pointing, DObject *pointed) void Barrier(DObject *pointing, DObject *pointed)
{ {
assert(pointed->GetClass() < (void*)0x1000000000000000);
assert(pointing == NULL || (pointing->IsBlack() && !pointing->IsDead())); assert(pointing == NULL || (pointing->IsBlack() && !pointing->IsDead()));
assert(pointed->IsWhite() && !pointed->IsDead()); assert(pointed->IsWhite() && !pointed->IsDead());
assert(State != GCS_Finalize && State != GCS_Pause); assert(State != GCS_Finalize && State != GCS_Pause);

View file

@ -501,7 +501,7 @@ PInt::PInt(unsigned int size, bool unsign, bool compatible)
MemberOnly = (size < 4); MemberOnly = (size < 4);
if (!unsign) if (!unsign)
{ {
int maxval = (1 << ((8 * size) - 1)) - 1; int maxval = (1u << ((8 * size) - 1)) - 1; // compute as unsigned to prevent overflow before -1
int minval = -maxval - 1; int minval = -maxval - 1;
Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Min, this, minval)); Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Min, this, minval));
Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Max, this, maxval)); Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Max, this, maxval));
@ -509,7 +509,7 @@ PInt::PInt(unsigned int size, bool unsign, bool compatible)
else else
{ {
Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Min, this, 0u)); Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Min, this, 0u));
Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Max, this, (1u << (8 * size)) - 1)); Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Max, this, (1u << ((8 * size) - 1))));
} }
SetOps(); SetOps();
} }
@ -722,10 +722,6 @@ PBool::PBool()
{ {
mDescriptiveName = "Bool"; mDescriptiveName = "Bool";
MemberOnly = false; MemberOnly = false;
// Override the default max set by PInt's constructor
PSymbolConstNumeric *maxsym = static_cast<PSymbolConstNumeric *>(Symbols.FindSymbol(NAME_Max, false));
assert(maxsym != nullptr && maxsym->IsKindOf(RUNTIME_CLASS(PSymbolConstNumeric)));
maxsym->Value = 1;
} }
/* PFloat *****************************************************************/ /* PFloat *****************************************************************/
@ -1400,10 +1396,13 @@ void PPointer::WriteValue(FSerializer &ar, const char *key,const void *addr) con
ar(key, *(DObject **)addr); ar(key, *(DObject **)addr);
} }
} }
else if (writer != nullptr)
{
writer(ar, key, addr);
}
else else
{ {
assert(0 && "Pointer points to a type we don't handle"); I_Error("Attempt to save pointer to unhandled type %s", PointedType->DescriptiveName());
I_Error("Attempt to save pointer to unhandled type");
} }
} }
@ -1430,6 +1429,10 @@ bool PPointer::ReadValue(FSerializer &ar, const char *key, void *addr) const
} }
return res; return res;
} }
else if (reader != nullptr)
{
return reader(ar, key, addr);
}
return false; return false;
} }
@ -2311,7 +2314,7 @@ void PStruct::WriteFields(FSerializer &ar, const void *addr, const TArray<PField
{ {
const PField *field = fields[i]; const PField *field = fields[i];
// Skip fields without or with native serialization // Skip fields without or with native serialization
if (!(field->Flags & VARF_Transient)) if (!(field->Flags & (VARF_Transient|VARF_Meta)))
{ {
field->Type->WriteValue(ar, field->SymbolName.GetChars(), (const BYTE *)addr + field->Offset); field->Type->WriteValue(ar, field->SymbolName.GetChars(), (const BYTE *)addr + field->Offset);
} }
@ -2344,6 +2347,11 @@ bool PStruct::ReadFields(FSerializer &ar, void *addr) const
DPrintf(DMSG_ERROR, "Symbol %s in %s is not a field\n", DPrintf(DMSG_ERROR, "Symbol %s in %s is not a field\n",
label, TypeName.GetChars()); label, TypeName.GetChars());
} }
else if ((static_cast<const PField *>(sym)->Flags & (VARF_Transient | VARF_Meta)))
{
DPrintf(DMSG_ERROR, "Symbol %s in %s is not a serializable field\n",
label, TypeName.GetChars());
}
else else
{ {
readsomething |= static_cast<const PField *>(sym)->Type->ReadValue(ar, nullptr, readsomething |= static_cast<const PField *>(sym)->Type->ReadValue(ar, nullptr,
@ -2642,7 +2650,7 @@ static void RecurseWriteFields(const PClass *type, FSerializer &ar, const void *
// Don't write this part if it has no non-transient variables // Don't write this part if it has no non-transient variables
for (unsigned i = 0; i < type->Fields.Size(); ++i) for (unsigned i = 0; i < type->Fields.Size(); ++i)
{ {
if (!(type->Fields[i]->Flags & VARF_Transient)) if (!(type->Fields[i]->Flags & (VARF_Transient|VARF_Meta)))
{ {
// Tag this section with the class it came from in case // Tag this section with the class it came from in case
// a more-derived class has variables that shadow a less- // a more-derived class has variables that shadow a less-
@ -2892,6 +2900,7 @@ PClass::PClass()
bExported = false; bExported = false;
bDecorateClass = false; bDecorateClass = false;
ConstructNative = nullptr; ConstructNative = nullptr;
Meta = nullptr;
mDescriptiveName = "Class"; mDescriptiveName = "Class";
PClass::AllClasses.Push(this); PClass::AllClasses.Push(this);
@ -2910,6 +2919,11 @@ PClass::~PClass()
M_Free(Defaults); M_Free(Defaults);
Defaults = nullptr; Defaults = nullptr;
} }
if (Meta != nullptr)
{
M_Free(Meta);
Meta = nullptr;
}
} }
//========================================================================== //==========================================================================
@ -3047,7 +3061,7 @@ PClass *PClass::FindClass (FName zaname)
// //
//========================================================================== //==========================================================================
DObject *PClass::CreateNew() const DObject *PClass::CreateNew()
{ {
BYTE *mem = (BYTE *)M_Malloc (Size); BYTE *mem = (BYTE *)M_Malloc (Size);
assert (mem != nullptr); assert (mem != nullptr);
@ -3064,7 +3078,7 @@ DObject *PClass::CreateNew() const
} }
ConstructNative (mem); ConstructNative (mem);
((DObject *)mem)->SetClass (const_cast<PClass *>(this)); ((DObject *)mem)->SetClass (const_cast<PClass *>(this));
InitializeSpecials(mem, Defaults); InitializeSpecials(mem, Defaults, &PClass::SpecialInits);
return (DObject *)mem; return (DObject *)mem;
} }
@ -3076,17 +3090,16 @@ DObject *PClass::CreateNew() const
// //
//========================================================================== //==========================================================================
void PClass::InitializeSpecials(void *addr, void *defaults) const void PClass::InitializeSpecials(void *addr, void *defaults, TArray<FTypeAndOffset> PClass::*Inits)
{ {
// Once we reach a native class, we can stop going up the family tree, // Once we reach a native class, we can stop going up the family tree,
// since native classes handle initialization natively. // since native classes handle initialization natively.
if (!bRuntimeClass) if ((!bRuntimeClass && Inits == &PClass::SpecialInits) || ParentClass == nullptr)
{ {
return; return;
} }
assert(ParentClass != nullptr); ParentClass->InitializeSpecials(addr, defaults, Inits);
ParentClass->InitializeSpecials(addr, defaults); for (auto tao : (this->*Inits))
for (auto tao : SpecialInits)
{ {
tao.first->InitializeValue((char*)addr + tao.second, defaults == nullptr? nullptr : ((char*)defaults) + tao.second); tao.first->InitializeValue((char*)addr + tao.second, defaults == nullptr? nullptr : ((char*)defaults) + tao.second);
} }
@ -3101,7 +3114,7 @@ void PClass::InitializeSpecials(void *addr, void *defaults) const
// //
//========================================================================== //==========================================================================
void PClass::DestroySpecials(void *addr) const void PClass::DestroySpecials(void *addr)
{ {
// Once we reach a native class, we can stop going up the family tree, // Once we reach a native class, we can stop going up the family tree,
// since native classes handle deinitialization natively. // since native classes handle deinitialization natively.
@ -3160,7 +3173,6 @@ void PClass::InitializeDefaults()
optr->ObjNext = nullptr; optr->ObjNext = nullptr;
optr->SetClass(this); optr->SetClass(this);
// Copy the defaults from the parent but leave the DObject part alone because it contains important data. // Copy the defaults from the parent but leave the DObject part alone because it contains important data.
if (ParentClass->Defaults != nullptr) if (ParentClass->Defaults != nullptr)
{ {
@ -3174,21 +3186,53 @@ void PClass::InitializeDefaults()
{ {
memset(Defaults + sizeof(DObject), 0, Size - sizeof(DObject)); memset(Defaults + sizeof(DObject), 0, Size - sizeof(DObject));
} }
assert(MetaSize >= ParentClass->MetaSize);
if (MetaSize != 0)
{
Meta = (BYTE*)M_Malloc(MetaSize);
// Copy the defaults from the parent but leave the DObject part alone because it contains important data.
if (ParentClass->Meta != nullptr)
{
memcpy(Meta, ParentClass->Meta, ParentClass->MetaSize);
if (MetaSize > ParentClass->MetaSize)
{
memset(Meta + ParentClass->MetaSize, 0, MetaSize - ParentClass->MetaSize);
}
}
else
{
memset(Meta, 0, MetaSize);
}
if (MetaSize > 0) memcpy(Meta, ParentClass->Meta, ParentClass->MetaSize);
else memset(Meta, 0, MetaSize);
}
} }
if (bRuntimeClass) if (bRuntimeClass)
{ {
// Copy parent values from the parent defaults. // Copy parent values from the parent defaults.
assert(ParentClass != nullptr); assert(ParentClass != nullptr);
if (Defaults != nullptr) ParentClass->InitializeSpecials(Defaults, ParentClass->Defaults); if (Defaults != nullptr) ParentClass->InitializeSpecials(Defaults, ParentClass->Defaults, &PClass::SpecialInits);
if (Meta != nullptr) ParentClass->InitializeSpecials(Meta, ParentClass->Meta, &PClass::MetaInits);
for (const PField *field : Fields) for (const PField *field : Fields)
{ {
if (!(field->Flags & VARF_Native)) if (!(field->Flags & VARF_Native) && !(field->Flags & VARF_Meta))
{ {
field->Type->SetDefaultValue(Defaults, unsigned(field->Offset), &SpecialInits); field->Type->SetDefaultValue(Defaults, unsigned(field->Offset), &SpecialInits);
} }
} }
} }
if (Meta != nullptr) ParentClass->InitializeSpecials(Meta, ParentClass->Meta, &PClass::MetaInits);
for (const PField *field : Fields)
{
if (!(field->Flags & VARF_Native) && (field->Flags & VARF_Meta))
{
field->Type->SetDefaultValue(Meta, unsigned(field->Offset), &MetaInits);
}
}
} }
//========================================================================== //==========================================================================
@ -3219,16 +3263,24 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size)
const PClass *existclass = FindClass(name); const PClass *existclass = FindClass(name);
// This is a placeholder so fill it in if (existclass != nullptr)
if (existclass != NULL && existclass->Size == (unsigned)-1)
{ {
type = const_cast<PClass*>(existclass); // This is a placeholder so fill it in
if (!IsDescendantOf(type->ParentClass)) if (existclass->Size == TentativeClass)
{ {
I_Error("%s must inherit from %s but doesn't.", name.GetChars(), type->ParentClass->TypeName.GetChars()); type = const_cast<PClass*>(existclass);
if (!IsDescendantOf(type->ParentClass))
{
I_Error("%s must inherit from %s but doesn't.", name.GetChars(), type->ParentClass->TypeName.GetChars());
}
DPrintf(DMSG_SPAMMY, "Defining placeholder class %s\n", name.GetChars());
notnew = true;
}
else
{
// a different class with the same name already exists. Let the calling code deal with this.
return nullptr;
} }
DPrintf(DMSG_SPAMMY, "Defining placeholder class %s\n", name.GetChars());
notnew = true;
} }
else else
{ {
@ -3240,6 +3292,7 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size)
type->bRuntimeClass = true; type->bRuntimeClass = true;
Derive(type, name); Derive(type, name);
type->Size = size; type->Size = size;
type->MetaSize = MetaSize;
if (size != TentativeClass) if (size != TentativeClass)
{ {
type->InitializeDefaults(); type->InitializeDefaults();
@ -3256,6 +3309,39 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size)
return type; return type;
} }
//==========================================================================
//
// PStruct :: AddField
//
// Appends a new metadata field to the end of a struct. Returns either the new field
// or nullptr if a symbol by that name already exists.
//
//==========================================================================
PField *PClass::AddMetaField(FName name, PType *type, DWORD flags)
{
PField *field = new PField(name, type, flags);
// The new field is added to the end of this struct, alignment permitting.
field->Offset = (MetaSize + (type->Align - 1)) & ~(type->Align - 1);
// Enlarge this struct to enclose the new field.
MetaSize = unsigned(field->Offset + type->Size);
// This struct's alignment is the same as the largest alignment of any of
// its fields.
Align = MAX(Align, type->Align);
if (Symbols.AddSymbol(field) == nullptr)
{ // name is already in use
field->Destroy();
return nullptr;
}
Fields.Push(field);
return field;
}
//========================================================================== //==========================================================================
// //
// PClass :: AddField // PClass :: AddField
@ -3264,18 +3350,36 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size)
PField *PClass::AddField(FName name, PType *type, DWORD flags) PField *PClass::AddField(FName name, PType *type, DWORD flags)
{ {
unsigned oldsize = Size; if (!(flags & VARF_Meta))
PField *field = Super::AddField(name, type, flags);
// Only initialize the defaults if they have already been created.
// For ZScript this is not the case, it will first define all fields before
// setting up any defaults for any class.
if (field != nullptr && !(flags & VARF_Native) && Defaults != nullptr)
{ {
Defaults = (BYTE *)M_Realloc(Defaults, Size); unsigned oldsize = Size;
memset(Defaults + oldsize, 0, Size - oldsize); PField *field = Super::AddField(name, type, flags);
// Only initialize the defaults if they have already been created.
// For ZScript this is not the case, it will first define all fields before
// setting up any defaults for any class.
if (field != nullptr && !(flags & VARF_Native) && Defaults != nullptr)
{
Defaults = (BYTE *)M_Realloc(Defaults, Size);
memset(Defaults + oldsize, 0, Size - oldsize);
}
return field;
}
else
{
unsigned oldsize = MetaSize;
PField *field = AddMetaField(name, type, flags);
// Only initialize the defaults if they have already been created.
// For ZScript this is not the case, it will first define all fields before
// setting up any defaults for any class.
if (field != nullptr && !(flags & VARF_Native) && Meta != nullptr)
{
Meta = (BYTE *)M_Realloc(Meta, MetaSize);
memset(Meta + oldsize, 0, MetaSize - oldsize);
}
return field;
} }
return field;
} }
//========================================================================== //==========================================================================

View file

@ -365,13 +365,26 @@ public:
class PPointer : public PBasicType class PPointer : public PBasicType
{ {
DECLARE_CLASS(PPointer, PBasicType); DECLARE_CLASS(PPointer, PBasicType);
public: public:
typedef void(*WriteHandler)(FSerializer &ar, const char *key, const void *addr);
typedef bool(*ReadHandler)(FSerializer &ar, const char *key, void *addr);
PPointer(); PPointer();
PPointer(PType *pointsat, bool isconst = false); PPointer(PType *pointsat, bool isconst = false);
PType *PointedType; PType *PointedType;
bool IsConst; bool IsConst;
WriteHandler writer = nullptr;
ReadHandler reader = nullptr;
void InstallHandlers(WriteHandler w, ReadHandler r)
{
writer = w;
reader = r;
}
virtual bool IsMatch(intptr_t id1, intptr_t id2) const; virtual bool IsMatch(intptr_t id1, intptr_t id2) const;
virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const;
void SetPointer(void *base, unsigned offset, TArray<size_t> *special = NULL) const override; void SetPointer(void *base, unsigned offset, TArray<size_t> *special = NULL) const override;
@ -560,12 +573,13 @@ enum
class PClass : public PNativeStruct class PClass : public PNativeStruct
{ {
DECLARE_CLASS(PClass, PNativeStruct); DECLARE_CLASS(PClass, PNativeStruct);
protected:
// We unravel _WITH_META here just as we did for PType. // We unravel _WITH_META here just as we did for PType.
TArray<FTypeAndOffset> SpecialInits; protected:
TArray<FTypeAndOffset> MetaInits;
void Derive(PClass *newclass, FName name); void Derive(PClass *newclass, FName name);
void InitializeSpecials(void *addr, void *defaults) const; void InitializeSpecials(void *addr, void *defaults, TArray<FTypeAndOffset> PClass::*Inits);
void SetSuper(); void SetSuper();
PField *AddMetaField(FName name, PType *type, DWORD flags);
public: public:
void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; void WriteValue(FSerializer &ar, const char *key,const void *addr) const override;
void WriteAllFields(FSerializer &ar, const void *addr) const; void WriteAllFields(FSerializer &ar, const void *addr) const;
@ -580,11 +594,14 @@ public:
static void StaticBootstrap(); static void StaticBootstrap();
// Per-class information ------------------------------------- // Per-class information -------------------------------------
TArray<FTypeAndOffset> SpecialInits;
PClass *ParentClass; // the class this class derives from PClass *ParentClass; // the class this class derives from
const size_t *Pointers; // object pointers defined by this class *only* const size_t *Pointers; // object pointers defined by this class *only*
const size_t *FlatPointers; // object pointers defined by this class and all its superclasses; not initialized by default const size_t *FlatPointers; // object pointers defined by this class and all its superclasses; not initialized by default
const size_t *ArrayPointers; // dynamic arrays containing object pointers. const size_t *ArrayPointers; // dynamic arrays containing object pointers.
BYTE *Defaults; BYTE *Defaults;
BYTE *Meta; // Per-class static script data
unsigned MetaSize;
bool bRuntimeClass; // class was defined at run-time, not compile-time bool bRuntimeClass; // class was defined at run-time, not compile-time
bool bExported; // This type has been declared in a script bool bExported; // This type has been declared in a script
bool bDecorateClass; // may be subject to some idiosyncracies due to DECORATE backwards compatibility bool bDecorateClass; // may be subject to some idiosyncracies due to DECORATE backwards compatibility
@ -596,13 +613,14 @@ public:
PClass(); PClass();
~PClass(); ~PClass();
void InsertIntoHash(); void InsertIntoHash();
DObject *CreateNew() const; DObject *CreateNew();
PClass *CreateDerivedClass(FName name, unsigned int size); PClass *CreateDerivedClass(FName name, unsigned int size);
PField *AddField(FName name, PType *type, DWORD flags=0) override; PField *AddField(FName name, PType *type, DWORD flags=0) override;
void InitializeActorInfo(); void InitializeActorInfo();
void BuildFlatPointers(); void BuildFlatPointers();
void BuildArrayPointers(); void BuildArrayPointers();
void DestroySpecials(void *addr) const; void InitMeta();
void DestroySpecials(void *addr);
const PClass *NativeClass() const; const PClass *NativeClass() const;
// Returns true if this type is an ancestor of (or same as) the passed type. // Returns true if this type is an ancestor of (or same as) the passed type.
@ -726,6 +744,11 @@ inline int &DObject::IntVar(FName field)
return *(int*)ScriptVar(field, TypeSInt32); return *(int*)ScriptVar(field, TypeSInt32);
} }
inline FSoundID &DObject::SoundVar(FName field)
{
return *(FSoundID*)ScriptVar(field, TypeSound);
}
inline PalEntry &DObject::ColorVar(FName field) inline PalEntry &DObject::ColorVar(FName field)
{ {
return *(PalEntry*)ScriptVar(field, TypeColor); return *(PalEntry*)ScriptVar(field, TypeColor);
@ -741,6 +764,11 @@ inline double &DObject::FloatVar(FName field)
return *(double*)ScriptVar(field, TypeFloat64); return *(double*)ScriptVar(field, TypeFloat64);
} }
inline FString &DObject::StringVar(FName field)
{
return *(FString*)ScriptVar(field, TypeString);
}
template<class T> template<class T>
inline T *&DObject::PointerVar(FName field) inline T *&DObject::PointerVar(FName field)
{ {

View file

@ -262,8 +262,8 @@ struct mapseg_t
struct mapseg4_t struct mapseg4_t
{ {
SDWORD v1; int32_t v1;
SDWORD v2; int32_t v2;
SWORD angle; SWORD angle;
WORD linedef; WORD linedef;
SWORD side; SWORD side;
@ -359,7 +359,7 @@ struct FMapThing
double Alpha; double Alpha;
DWORD fillcolor; DWORD fillcolor;
DVector2 Scale; DVector2 Scale;
int health; double Health;
int score; int score;
short pitch; short pitch;
short roll; short roll;

View file

@ -64,7 +64,7 @@ typedef enum
// The current state of the game: whether we are // The current state of the game: whether we are
// playing, gazing at the intermission screen, // playing, gazing at the intermission screen,
// the game final animation, or a demo. // the game final animation, or a demo.
typedef enum enum gamestate_t : int
{ {
GS_LEVEL, GS_LEVEL,
GS_INTERMISSION, GS_INTERMISSION,
@ -79,7 +79,7 @@ typedef enum
GS_FORCEWIPEFADE = -2, GS_FORCEWIPEFADE = -2,
GS_FORCEWIPEBURN = -3, GS_FORCEWIPEBURN = -3,
GS_FORCEWIPEMELT = -4 GS_FORCEWIPEMELT = -4
} gamestate_t; };
extern gamestate_t gamestate; extern gamestate_t gamestate;

View file

@ -422,7 +422,7 @@ void DThinker::DestroyAllThinkers ()
for (i = 0; i <= MAX_STATNUM; i++) for (i = 0; i <= MAX_STATNUM; i++)
{ {
if (i != STAT_TRAVELLING) if (i != STAT_TRAVELLING && i != STAT_STATIC)
{ {
DestroyThinkersInList (Thinkers[i]); DestroyThinkersInList (Thinkers[i]);
DestroyThinkersInList (FreshThinkers[i]); DestroyThinkersInList (FreshThinkers[i]);

View file

@ -37,32 +37,42 @@ bool E_RegisterHandler(DStaticEventHandler* handler)
// 2. MyHandler3->2: // 2. MyHandler3->2:
// E_FirstEventHandler = MyHandler2, E_LastEventHandler = MyHandler3 // E_FirstEventHandler = MyHandler2, E_LastEventHandler = MyHandler3
// (Yes, all those write barriers here are really needed!)
if (before != nullptr) if (before != nullptr)
{ {
// if before is not null, link it before the existing handler. // if before is not null, link it before the existing handler.
// note that before can be first handler, check for this. // note that before can be first handler, check for this.
handler->next = before; handler->next = before;
GC::WriteBarrier(handler, before);
handler->prev = before->prev; handler->prev = before->prev;
GC::WriteBarrier(handler, before->prev);
before->prev = handler; before->prev = handler;
GC::WriteBarrier(before, handler);
if (before == E_FirstEventHandler) if (before == E_FirstEventHandler)
{
E_FirstEventHandler = handler; E_FirstEventHandler = handler;
GC::WriteBarrier(handler);
}
} }
else else
{ {
// so if before is null, it means add last. // so if before is null, it means add last.
// it can also mean that we have no handlers at all yet. // it can also mean that we have no handlers at all yet.
handler->prev = E_LastEventHandler; handler->prev = E_LastEventHandler;
GC::WriteBarrier(handler, E_LastEventHandler);
handler->next = nullptr; handler->next = nullptr;
if (E_FirstEventHandler == nullptr) if (E_FirstEventHandler == nullptr) E_FirstEventHandler = handler;
E_FirstEventHandler = handler;
E_LastEventHandler = handler; E_LastEventHandler = handler;
GC::WriteBarrier(handler);
if (handler->prev != nullptr) if (handler->prev != nullptr)
{
handler->prev->next = handler; handler->prev->next = handler;
GC::WriteBarrier(handler->prev, handler);
}
} }
if (handler->IsStatic()) if (handler->IsStatic())
{ {
handler->ObjectFlags |= OF_Fixed;
handler->ObjectFlags |= OF_Transient; handler->ObjectFlags |= OF_Transient;
} }
@ -80,16 +90,28 @@ bool E_UnregisterHandler(DStaticEventHandler* handler)
// link out of normal list // link out of normal list
if (handler->prev) if (handler->prev)
{
handler->prev->next = handler->next; handler->prev->next = handler->next;
GC::WriteBarrier(handler->prev, handler->next);
}
if (handler->next) if (handler->next)
{
handler->next->prev = handler->prev; handler->next->prev = handler->prev;
GC::WriteBarrier(handler->next, handler->prev);
}
if (handler == E_FirstEventHandler) if (handler == E_FirstEventHandler)
{
E_FirstEventHandler = handler->next; E_FirstEventHandler = handler->next;
GC::WriteBarrier(handler->next);
}
if (handler == E_LastEventHandler) if (handler == E_LastEventHandler)
{
E_LastEventHandler = handler->prev; E_LastEventHandler = handler->prev;
GC::WriteBarrier(handler->prev);
}
if (handler->IsStatic()) if (handler->IsStatic())
{ {
handler->ObjectFlags &= ~(OF_Fixed|OF_Transient); handler->ObjectFlags &= ~OF_Transient;
handler->Destroy(); handler->Destroy();
} }
return true; return true;
@ -427,7 +449,13 @@ DEFINE_EVENT_LOOPER(WorldLightning)
DEFINE_EVENT_LOOPER(WorldTick) DEFINE_EVENT_LOOPER(WorldTick)
// declarations // declarations
IMPLEMENT_CLASS(DStaticEventHandler, false, false); IMPLEMENT_CLASS(DStaticEventHandler, false, true);
IMPLEMENT_POINTERS_START(DStaticEventHandler)
IMPLEMENT_POINTER(next)
IMPLEMENT_POINTER(prev)
IMPLEMENT_POINTERS_END
IMPLEMENT_CLASS(DEventHandler, false, false); IMPLEMENT_CLASS(DEventHandler, false, false);
IMPLEMENT_CLASS(DBaseEvent, false, false) IMPLEMENT_CLASS(DBaseEvent, false, false)
IMPLEMENT_CLASS(DRenderEvent, false, false) IMPLEMENT_CLASS(DRenderEvent, false, false)
@ -1135,7 +1163,7 @@ CCMD(netevent)
Net_WriteByte(DEM_NETEVENT); Net_WriteByte(DEM_NETEVENT);
Net_WriteString(argv[1]); Net_WriteString(argv[1]);
Net_WriteByte(argn); Net_WriteByte(argn);
for (int i = 0; i < argn; i++) for (int i = 0; i < 3; i++)
Net_WriteLong(arg[i]); Net_WriteLong(arg[i]);
} }
} }

View file

@ -76,7 +76,8 @@ void E_SerializeEvents(FSerializer& arc);
class DStaticEventHandler : public DObject // make it a part of normal GC process class DStaticEventHandler : public DObject // make it a part of normal GC process
{ {
DECLARE_CLASS(DStaticEventHandler, DObject) DECLARE_CLASS(DStaticEventHandler, DObject);
HAS_OBJECT_POINTERS
public: public:
DStaticEventHandler() DStaticEventHandler()
{ {
@ -99,6 +100,7 @@ public:
void Serialize(FSerializer& arc) override void Serialize(FSerializer& arc) override
{ {
Super::Serialize(arc); Super::Serialize(arc);
/*
if (arc.isReading()) if (arc.isReading())
{ {
Printf("DStaticEventHandler::Serialize: reading object %s\n", GetClass()->TypeName.GetChars()); Printf("DStaticEventHandler::Serialize: reading object %s\n", GetClass()->TypeName.GetChars());
@ -107,6 +109,7 @@ public:
{ {
Printf("DStaticEventHandler::Serialize: store object %s\n", GetClass()->TypeName.GetChars()); Printf("DStaticEventHandler::Serialize: store object %s\n", GetClass()->TypeName.GetChars());
} }
*/
arc("Order", Order); arc("Order", Order);
arc("IsUiProcessor", IsUiProcessor); arc("IsUiProcessor", IsUiProcessor);
@ -155,6 +158,7 @@ public:
bool IsStatic() override { return false; } bool IsStatic() override { return false; }
}; };
extern DStaticEventHandler* E_FirstEventHandler; extern DStaticEventHandler* E_FirstEventHandler;
extern DStaticEventHandler* E_LastEventHandler;
// we cannot call this DEvent because in ZScript, 'event' is a keyword // we cannot call this DEvent because in ZScript, 'event' is a keyword
class DBaseEvent : public DObject class DBaseEvent : public DObject

View file

@ -479,7 +479,7 @@ DFsSection *FParser::looping_section()
int n; int n;
// check thru all the hashchains // check thru all the hashchains
SDWORD rover_index = Script->MakeIndex(Rover); int32_t rover_index = Script->MakeIndex(Rover);
for(n=0; n<SECTIONSLOTS; n++) for(n=0; n<SECTIONSLOTS; n++)
{ {

View file

@ -182,7 +182,7 @@ public:
union value_t union value_t
{ {
SDWORD i; int32_t i;
fsfix fixed; // haleyjd: fixed-point fsfix fixed; // haleyjd: fixed-point
// the following are only used in the global script so we don't need to bother with them // the following are only used in the global script so we don't need to bother with them

View file

@ -226,7 +226,7 @@ int mousex;
int mousey; int mousey;
FString savegamefile; FString savegamefile;
char savedescription[SAVESTRINGSIZE]; FString savedescription;
// [RH] Name of screenshot file to generate (usually NULL) // [RH] Name of screenshot file to generate (usually NULL)
FString shotfile; FString shotfile;
@ -241,6 +241,7 @@ FString BackupSaveName;
bool SendLand; bool SendLand;
const AInventory *SendItemUse, *SendItemDrop; const AInventory *SendItemUse, *SendItemDrop;
int SendItemDropAmount;
EXTERN_CVAR (Int, team) EXTERN_CVAR (Int, team)
@ -346,6 +347,10 @@ CCMD (weapnext)
StatusBar->AttachMessage(new DHUDMessageFadeOut(SmallFont, SendItemUse->GetTag(), 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' )); 1.5f, 0.90f, 0, 0, (EColorRange)*nametagcolor, 2.f, 0.35f), MAKE_ID( 'W', 'E', 'P', 'N' ));
} }
if (SendItemUse != players[consoleplayer].ReadyWeapon)
{
S_Sound(CHAN_AUTO, "misc/weaponchange", 1.0, ATTN_NONE);
}
} }
CCMD (weapprev) CCMD (weapprev)
@ -357,6 +362,10 @@ CCMD (weapprev)
StatusBar->AttachMessage(new DHUDMessageFadeOut(SmallFont, SendItemUse->GetTag(), 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' )); 1.5f, 0.90f, 0, 0, (EColorRange)*nametagcolor, 2.f, 0.35f), MAKE_ID( 'W', 'E', 'P', 'N' ));
} }
if (SendItemUse != players[consoleplayer].ReadyWeapon)
{
S_Sound(CHAN_AUTO, "misc/weaponchange", 1.0, ATTN_NONE);
}
} }
CCMD (invnext) CCMD (invnext)
@ -366,6 +375,7 @@ CCMD (invnext)
if (who == NULL) if (who == NULL)
return; return;
auto old = who->InvSel;
if (who->InvSel != NULL) if (who->InvSel != NULL)
{ {
if ((next = who->InvSel->NextInv()) != NULL) if ((next = who->InvSel->NextInv()) != NULL)
@ -389,6 +399,10 @@ CCMD (invnext)
1.5f, 0.80f, 0, 0, (EColorRange)*nametagcolor, 2.f, 0.35f), MAKE_ID('S','I','N','V')); 1.5f, 0.80f, 0, 0, (EColorRange)*nametagcolor, 2.f, 0.35f), MAKE_ID('S','I','N','V'));
} }
who->player->inventorytics = 5*TICRATE; who->player->inventorytics = 5*TICRATE;
if (old != who->InvSel)
{
S_Sound(CHAN_AUTO, "misc/invchange", 1.0, ATTN_NONE);
}
} }
CCMD (invprev) CCMD (invprev)
@ -398,6 +412,7 @@ CCMD (invprev)
if (who == NULL) if (who == NULL)
return; return;
auto old = who->InvSel;
if (who->InvSel != NULL) if (who->InvSel != NULL)
{ {
if ((item = who->InvSel->PrevInv()) != NULL) if ((item = who->InvSel->PrevInv()) != NULL)
@ -419,6 +434,10 @@ CCMD (invprev)
1.5f, 0.80f, 0, 0, (EColorRange)*nametagcolor, 2.f, 0.35f), MAKE_ID('S','I','N','V')); 1.5f, 0.80f, 0, 0, (EColorRange)*nametagcolor, 2.f, 0.35f), MAKE_ID('S','I','N','V'));
} }
who->player->inventorytics = 5*TICRATE; who->player->inventorytics = 5*TICRATE;
if (old != who->InvSel)
{
S_Sound(CHAN_AUTO, "misc/invchange", 1.0, ATTN_NONE);
}
} }
CCMD (invuseall) CCMD (invuseall)
@ -457,12 +476,14 @@ CCMD (invdrop)
if (players[consoleplayer].mo) if (players[consoleplayer].mo)
{ {
SendItemDrop = players[consoleplayer].mo->InvSel; SendItemDrop = players[consoleplayer].mo->InvSel;
SendItemDropAmount = -1;
} }
} }
CCMD (weapdrop) CCMD (weapdrop)
{ {
SendItemDrop = players[consoleplayer].ReadyWeapon; SendItemDrop = players[consoleplayer].ReadyWeapon;
SendItemDropAmount = -1;
} }
CCMD (drop) CCMD (drop)
@ -470,6 +491,7 @@ CCMD (drop)
if (argv.argc() > 1 && who != NULL) if (argv.argc() > 1 && who != NULL)
{ {
SendItemDrop = who->FindInventory(argv[1]); SendItemDrop = who->FindInventory(argv[1]);
SendItemDropAmount = argv.argc() > 2 ? atoi(argv[2]) : -1;
} }
} }
@ -762,6 +784,7 @@ void G_BuildTiccmd (ticcmd_t *cmd)
{ {
Net_WriteByte (DEM_INVDROP); Net_WriteByte (DEM_INVDROP);
Net_WriteLong (SendItemDrop->InventoryID); Net_WriteLong (SendItemDrop->InventoryID);
Net_WriteLong(SendItemDropAmount);
SendItemDrop = NULL; SendItemDrop = NULL;
} }
@ -1081,7 +1104,7 @@ void G_Ticker ()
G_DoSaveGame (true, savegamefile, savedescription); G_DoSaveGame (true, savegamefile, savedescription);
gameaction = ga_nothing; gameaction = ga_nothing;
savegamefile = ""; savegamefile = "";
savedescription[0] = '\0'; savedescription = "";
break; break;
case ga_autosave: case ga_autosave:
G_DoAutoSave (); G_DoAutoSave ();
@ -1320,12 +1343,24 @@ void G_PlayerFinishLevel (int player, EFinishLevelType mode, int flags)
if (mode == FINISH_NoHub && !(level.flags2 & LEVEL2_KEEPFULLINVENTORY)) if (mode == FINISH_NoHub && !(level.flags2 & LEVEL2_KEEPFULLINVENTORY))
{ // Reduce all owned (visible) inventory to defined maximum interhub amount { // Reduce all owned (visible) inventory to defined maximum interhub amount
TArray<AInventory*> todelete;
for (item = p->mo->Inventory; item != NULL; item = item->Inventory) for (item = p->mo->Inventory; item != NULL; item = item->Inventory)
{ {
// If the player is carrying more samples of an item than allowed, reduce amount accordingly // If the player is carrying more samples of an item than allowed, reduce amount accordingly
if (item->ItemFlags & IF_INVBAR && item->Amount > item->InterHubAmount) if (item->ItemFlags & IF_INVBAR && item->Amount > item->InterHubAmount)
{ {
item->Amount = item->InterHubAmount; item->Amount = item->InterHubAmount;
if ((level.flags3 & LEVEL3_RESETINVENTORY) && !(item->ItemFlags & IF_UNDROPPABLE))
{
todelete.Push(item);
}
}
}
for (auto it : todelete)
{
if (!(it->ObjectFlags & OF_EuthanizeMe))
{
it->DepleteOrDestroy();
} }
} }
} }
@ -1695,7 +1730,7 @@ static void G_QueueBody (AActor *body)
{ {
// Apply skin's scale to actor's scale, it will be lost otherwise // Apply skin's scale to actor's scale, it will be lost otherwise
const AActor *const defaultActor = body->GetDefault(); const AActor *const defaultActor = body->GetDefault();
const FPlayerSkin &skin = skins[skinidx]; const FPlayerSkin &skin = Skins[skinidx];
body->Scale.X *= skin.Scale.X / defaultActor->Scale.X; body->Scale.X *= skin.Scale.X / defaultActor->Scale.X;
body->Scale.Y *= skin.Scale.Y / defaultActor->Scale.Y; body->Scale.Y *= skin.Scale.Y / defaultActor->Scale.Y;
@ -2068,8 +2103,7 @@ void G_SaveGame (const char *filename, const char *description)
else else
{ {
savegamefile = filename; savegamefile = filename;
strncpy (savedescription, description, sizeof(savedescription)-1); savedescription = description;
savedescription[sizeof(savedescription)-1] = '\0';
sendsave = true; sendsave = true;
} }
} }
@ -2119,7 +2153,7 @@ extern void P_CalcHeight (player_t *);
void G_DoAutoSave () void G_DoAutoSave ()
{ {
char description[SAVESTRINGSIZE]; FString description;
FString file; FString file;
// Keep up to four autosaves at a time // Keep up to four autosaves at a time
UCVarValue num; UCVarValue num;
@ -2147,10 +2181,7 @@ void G_DoAutoSave ()
} }
readableTime = myasctime (); readableTime = myasctime ();
strcpy (description, "Autosave "); description.Format("Autosave %.12s", readableTime + 4);
strncpy (description+9, readableTime+4, 12);
description[9+12] = 0;
G_DoSaveGame (false, file, description); G_DoSaveGame (false, file, description);
} }
@ -2239,7 +2270,29 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio
I_FreezeTime(true); I_FreezeTime(true);
insave = true; insave = true;
G_SnapshotLevel (); try
{
G_SnapshotLevel();
}
catch(CRecoverableError &err)
{
// delete the snapshot. Since the save failed it is broken.
insave = false;
level.info->Snapshot.Clean();
Printf(PRINT_HIGH, "Save failed\n");
Printf(PRINT_HIGH, "%s\n", err.GetMessage());
// The time freeze must be reset if the save fails.
if (cl_waitforsave)
I_FreezeTime(false);
return;
}
catch (...)
{
insave = false;
if (cl_waitforsave)
I_FreezeTime(false);
throw;
}
BufferWriter savepic; BufferWriter savepic;
FSerializer savegameinfo; // this is for displayable info about the savegame FSerializer savegameinfo; // this is for displayable info about the savegame
@ -2310,7 +2363,7 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio
WriteZip(filename, savegame_filenames, savegame_content); WriteZip(filename, savegame_filenames, savegame_content);
savegameManager.NotifyNewSave (filename.GetChars(), description, okForQuicksave); savegameManager.NotifyNewSave (filename, description, okForQuicksave);
// delete the JSON buffers we created just above. Everything else will // delete the JSON buffers we created just above. Everything else will
// either still be needed or taken care of automatically. // either still be needed or taken care of automatically.

View file

@ -94,6 +94,7 @@ extern AActor *bodyque[BODYQUESIZE];
extern int bodyqueslot; extern int bodyqueslot;
class AInventory; class AInventory;
extern const AInventory *SendItemUse, *SendItemDrop; extern const AInventory *SendItemUse, *SendItemDrop;
extern int SendItemDropAmount;
#endif #endif

View file

@ -154,8 +154,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, wbplayerstruct_t &h, w
{ {
if (arc.BeginObject(key)) if (arc.BeginObject(key))
{ {
arc("in", h.in) arc("kills", h.skills)
("kills", h.skills)
("items", h.sitems) ("items", h.sitems)
("secrets", h.ssecret) ("secrets", h.ssecret)
("time", h.stime) ("time", h.stime)

View file

@ -50,8 +50,6 @@ DEFINE_FIELD(AInventory, DropTime)
DEFINE_FIELD(AInventory, SpawnPointClass) DEFINE_FIELD(AInventory, SpawnPointClass)
DEFINE_FIELD(AInventory, PickupFlash) DEFINE_FIELD(AInventory, PickupFlash)
DEFINE_FIELD(AInventory, PickupSound) DEFINE_FIELD(AInventory, PickupSound)
DEFINE_FIELD(AInventory, GiveQuest)
DEFINE_FIELD(PClassActor, PickupMsg)
//=========================================================================== //===========================================================================
// //
@ -115,8 +113,7 @@ void AInventory::Serialize(FSerializer &arc)
("icon", Icon, def->Icon) ("icon", Icon, def->Icon)
("pickupsound", PickupSound, def->PickupSound) ("pickupsound", PickupSound, def->PickupSound)
("spawnpointclass", SpawnPointClass, def->SpawnPointClass) ("spawnpointclass", SpawnPointClass, def->SpawnPointClass)
("droptime", DropTime, def->DropTime) ("droptime", DropTime, def->DropTime);
("givequest", GiveQuest, def->GiveQuest);
} }
//=========================================================================== //===========================================================================

View file

@ -17,7 +17,7 @@ struct visstyle_t;
// A pickup is anything the player can pickup (i.e. weapons, ammo, powerups, etc) // A pickup is anything the player can pickup (i.e. weapons, ammo, powerups, etc)
enum enum ItemFlag
{ {
IF_ACTIVATABLE = 1<<0, // can be activated IF_ACTIVATABLE = 1<<0, // can be activated
IF_ACTIVATED = 1<<1, // is currently activated IF_ACTIVATED = 1<<1, // is currently activated
@ -46,8 +46,14 @@ enum
IF_TRANSFER = 1<<24, // All inventory items that the inventory item contains is also transfered to the pickuper IF_TRANSFER = 1<<24, // All inventory items that the inventory item contains is also transfered to the pickuper
IF_NOTELEPORTFREEZE = 1<<25, // does not 'freeze' the player right after teleporting. IF_NOTELEPORTFREEZE = 1<<25, // does not 'freeze' the player right after teleporting.
IF_NOSCREENBLINK = 1<<26, // Does not blink the screen overlay when expiring. IF_NOSCREENBLINK = 1<<26, // Does not blink the screen overlay when expiring.
IF_ISHEALTH = 1<<27, // for the DM flag so that it can recognize items that are not obviously health givers.
IF_ISARMOR = 1<<28, // for the DM flag so that it can recognize items that are not obviously armor givers.
}; };
typedef TFlags<ItemFlag> InvFlags;
//typedef TFlags<ItemFlag2> ItemFlags2;
DEFINE_TFLAGS_OPERATORS(InvFlags)
//DEFINE_TFLAGS_OPERATORS(ItemFlags2)
class AInventory : public AActor class AInventory : public AActor
{ {
@ -87,10 +93,9 @@ public:
FTextureID Icon; // Icon to show on status bar or HUD FTextureID Icon; // Icon to show on status bar or HUD
int DropTime; // Countdown after dropping int DropTime; // Countdown after dropping
PClassActor *SpawnPointClass; // For respawning like Heretic's mace PClassActor *SpawnPointClass; // For respawning like Heretic's mace
int GiveQuest; // Optionally give one of the quest items.
FTextureID AltHUDIcon; FTextureID AltHUDIcon;
DWORD ItemFlags; InvFlags ItemFlags;
PClassActor *PickupFlash; // actor to spawn as pickup flash PClassActor *PickupFlash; // actor to spawn as pickup flash
FSoundIDNoInit PickupSound; FSoundIDNoInit PickupSound;

View file

@ -125,6 +125,7 @@ int starttime;
extern FString BackupSaveName; extern FString BackupSaveName;
bool savegamerestore; bool savegamerestore;
int finishstate;
extern int mousex, mousey; extern int mousex, mousey;
extern bool sendpause, sendsave, sendturn180, SendLand; extern bool sendpause, sendsave, sendturn180, SendLand;
@ -160,6 +161,7 @@ void G_DeferedInitNew (FGameStartup *gs)
d_skill = gs->Skill; d_skill = gs->Skill;
CheckWarpTransMap (d_mapname, true); CheckWarpTransMap (d_mapname, true);
gameaction = ga_newgame2; gameaction = ga_newgame2;
finishstate = FINISH_NoHub;
} }
//========================================================================== //==========================================================================
@ -425,6 +427,7 @@ void G_InitNew (const char *mapname, bool bTitleLevel)
UnlatchCVars (); UnlatchCVars ();
G_VerifySkill(); G_VerifySkill();
UnlatchCVars (); UnlatchCVars ();
DThinker::DestroyThinkersInList(STAT_STATIC);
if (paused) if (paused)
{ {
@ -776,7 +779,7 @@ void G_DoCompleted (void)
AM_Stop (); AM_Stop ();
wminfo.finished_ep = level.cluster - 1; wminfo.finished_ep = level.cluster - 1;
wminfo.LName0 = TexMan[TexMan.CheckForTexture(level.info->PName, FTexture::TEX_MiscPatch)]; wminfo.LName0 = TexMan.CheckForTexture(level.info->PName, FTexture::TEX_MiscPatch);
wminfo.current = level.MapName; wminfo.current = level.MapName;
if (deathmatch && if (deathmatch &&
@ -792,12 +795,12 @@ void G_DoCompleted (void)
if (nextinfo == NULL || strncmp (nextlevel, "enDSeQ", 6) == 0) if (nextinfo == NULL || strncmp (nextlevel, "enDSeQ", 6) == 0)
{ {
wminfo.next = nextlevel; wminfo.next = nextlevel;
wminfo.LName1 = NULL; wminfo.LName1.SetInvalid();
} }
else else
{ {
wminfo.next = nextinfo->MapName; wminfo.next = nextinfo->MapName;
wminfo.LName1 = TexMan[TexMan.CheckForTexture(nextinfo->PName, FTexture::TEX_MiscPatch)]; wminfo.LName1 = TexMan.CheckForTexture(nextinfo->PName, FTexture::TEX_MiscPatch);
} }
} }
@ -816,7 +819,6 @@ void G_DoCompleted (void)
for (i=0 ; i<MAXPLAYERS ; i++) for (i=0 ; i<MAXPLAYERS ; i++)
{ {
wminfo.plyr[i].in = playeringame[i];
wminfo.plyr[i].skills = players[i].killcount; wminfo.plyr[i].skills = players[i].killcount;
wminfo.plyr[i].sitems = players[i].itemcount; wminfo.plyr[i].sitems = players[i].itemcount;
wminfo.plyr[i].ssecret = players[i].secretcount; wminfo.plyr[i].ssecret = players[i].secretcount;
@ -898,6 +900,7 @@ void G_DoCompleted (void)
} }
gamestate = GS_INTERMISSION; gamestate = GS_INTERMISSION;
finishstate = mode;
viewactive = false; viewactive = false;
automapactive = false; automapactive = false;
@ -1069,11 +1072,24 @@ void G_DoLoadLevel (int position, bool autosave)
// For each player, if they are viewing through a player, make sure it is themselves. // For each player, if they are viewing through a player, make sure it is themselves.
for (int ii = 0; ii < MAXPLAYERS; ++ii) for (int ii = 0; ii < MAXPLAYERS; ++ii)
{ {
if (playeringame[ii] && (players[ii].camera == NULL || players[ii].camera->player != NULL)) if (playeringame[ii])
{ {
players[ii].camera = players[ii].mo; if (players[ii].camera == NULL || players[ii].camera->player != NULL)
{
players[ii].camera = players[ii].mo;
}
E_PlayerEntered(ii, finishstate == FINISH_SameHub);
// ENTER scripts are being handled when the player gets spawned, this cannot be changed due to its effect on voodoo dolls.
if (level.FromSnapshot) FBehavior::StaticStartTypedScripts(SCRIPT_Return, players[ii].mo, true);
} }
} }
if (level.FromSnapshot)
{
// [Nash] run REOPEN scripts upon map re-entry
FBehavior::StaticStartTypedScripts(SCRIPT_Reopen, NULL, false);
}
StatusBar->AttachToPlayer (&players[consoleplayer]); StatusBar->AttachToPlayer (&players[consoleplayer]);
// unsafe world load // unsafe world load
E_WorldLoadedUnsafe(); E_WorldLoadedUnsafe();
@ -1355,22 +1371,6 @@ void G_FinishTravel ()
pawns[pawnsnum++] = pawn; pawns[pawnsnum++] = pawn;
} }
// [ZZ] fire the reopen hook.
// if level is loaded from snapshot, and we don't have savegamerestore, this means we returned from a hub.
if (level.FromSnapshot)
{
// [Nash] run REOPEN scripts upon map re-entry
FBehavior::StaticStartTypedScripts(SCRIPT_Reopen, NULL, false);
for (int i = 0; i < pawnsnum; i++)
{
// [ZZ] fire the enter hook.
E_PlayerEntered(int(pawns[i]->player - players), true);
//
FBehavior::StaticStartTypedScripts(SCRIPT_Return, pawns[i], true);
}
}
bglobal.FinishTravel (); bglobal.FinishTravel ();
// make sure that, after travelling has completed, no travelling thinkers are left. // make sure that, after travelling has completed, no travelling thinkers are left.
@ -1461,6 +1461,7 @@ void G_InitLevelLocals ()
level.LevelName = level.info->LookupLevelName(); level.LevelName = level.info->LookupLevelName();
level.NextMap = info->NextMap; level.NextMap = info->NextMap;
level.NextSecretMap = info->NextSecretMap; level.NextSecretMap = info->NextSecretMap;
level.F1Pic = info->F1Pic;
compatflags.Callback(); compatflags.Callback();
compatflags2.Callback(); compatflags2.Callback();
@ -1623,6 +1624,7 @@ void G_UnSnapshotLevel (bool hubLoad)
} }
} }
} }
arc.Close();
} }
// No reason to keep the snapshot around once the level's been entered. // No reason to keep the snapshot around once the level's been entered.
level.info->Snapshot.Clean(); level.info->Snapshot.Clean();
@ -1909,6 +1911,7 @@ DEFINE_FIELD(FLevelLocals, LevelName)
DEFINE_FIELD(FLevelLocals, MapName) DEFINE_FIELD(FLevelLocals, MapName)
DEFINE_FIELD(FLevelLocals, NextMap) DEFINE_FIELD(FLevelLocals, NextMap)
DEFINE_FIELD(FLevelLocals, NextSecretMap) DEFINE_FIELD(FLevelLocals, NextSecretMap)
DEFINE_FIELD(FLevelLocals, F1Pic)
DEFINE_FIELD(FLevelLocals, maptype) DEFINE_FIELD(FLevelLocals, maptype)
DEFINE_FIELD(FLevelLocals, Music) DEFINE_FIELD(FLevelLocals, Music)
DEFINE_FIELD(FLevelLocals, musicorder) DEFINE_FIELD(FLevelLocals, musicorder)
@ -1934,6 +1937,7 @@ DEFINE_FIELD_BIT(FLevelLocals, flags2, polygrind, LEVEL2_POLYGRIND)
DEFINE_FIELD_BIT(FLevelLocals, flags2, nomonsters, LEVEL2_NOMONSTERS) DEFINE_FIELD_BIT(FLevelLocals, flags2, nomonsters, LEVEL2_NOMONSTERS)
DEFINE_FIELD_BIT(FLevelLocals, flags2, frozen, LEVEL2_FROZEN) DEFINE_FIELD_BIT(FLevelLocals, flags2, frozen, LEVEL2_FROZEN)
DEFINE_FIELD_BIT(FLevelLocals, flags2, infinite_flight, LEVEL2_INFINITE_FLIGHT) DEFINE_FIELD_BIT(FLevelLocals, flags2, infinite_flight, LEVEL2_INFINITE_FLIGHT)
DEFINE_FIELD_BIT(FLevelLocals, flags2, no_dlg_freeze, LEVEL2_CONV_SINGLE_UNFREEZE)
//========================================================================== //==========================================================================
// //

View file

@ -223,6 +223,7 @@ enum ELevelFlags : unsigned int
// More flags! // More flags!
LEVEL3_FORCEFAKECONTRAST = 0x00000001, // forces fake contrast even with fog enabled LEVEL3_FORCEFAKECONTRAST = 0x00000001, // forces fake contrast even with fog enabled
LEVEL3_RESETINVENTORY = 0x00000002, // kills all INVBAR items on map change.
}; };
@ -324,6 +325,7 @@ struct level_info_t
FString ExitPic; FString ExitPic;
FString InterMusic; FString InterMusic;
int intermusicorder; int intermusicorder;
TMap <FName, std::pair<FString, int> > MapInterMusic;
FString SoundInfo; FString SoundInfo;
FString SndSeq; FString SndSeq;
@ -495,6 +497,7 @@ enum EFSkillProperty // floating point properties
SKILLP_Aggressiveness, SKILLP_Aggressiveness,
SKILLP_MonsterHealth, SKILLP_MonsterHealth,
SKILLP_FriendlyHealth, SKILLP_FriendlyHealth,
SKILLP_KickbackFactor,
}; };
int G_SkillProperty(ESkillProperty prop); int G_SkillProperty(ESkillProperty prop);
@ -512,6 +515,7 @@ struct FSkillInfo
double DamageFactor; double DamageFactor;
double ArmorFactor; double ArmorFactor;
double HealthFactor; double HealthFactor;
double KickbackFactor;
bool FastMonsters; bool FastMonsters;
bool SlowMonsters; bool SlowMonsters;
@ -520,6 +524,7 @@ struct FSkillInfo
bool EasyBossBrain; bool EasyBossBrain;
bool EasyKey; bool EasyKey;
bool NoMenu;
int RespawnCounter; int RespawnCounter;
int RespawnLimit; int RespawnLimit;
double Aggressiveness; double Aggressiveness;

View file

@ -25,6 +25,7 @@ struct FLevelLocals
FString MapName; // the lump name (E1M1, MAP01, etc) FString MapName; // the lump name (E1M1, MAP01, etc)
FString NextMap; // go here when using the regular exit FString NextMap; // go here when using the regular exit
FString NextSecretMap; // map to go to when used secret exit FString NextSecretMap; // map to go to when used secret exit
FString F1Pic;
EMapType maptype; EMapType maptype;
TStaticArray<vertex_t> vertexes; TStaticArray<vertex_t> vertexes;

View file

@ -915,6 +915,18 @@ DEFINE_MAP_OPTION(intermusic, true)
parse.ParseMusic(info->InterMusic, info->intermusicorder); parse.ParseMusic(info->InterMusic, info->intermusicorder);
} }
DEFINE_MAP_OPTION(mapintermusic, true)
{
parse.ParseAssign();
parse.sc.MustGetString();
FString mapname = parse.sc.String;
FString music;
int order;
parse.ParseComma();
parse.ParseMusic(music, order);
info->MapInterMusic[FName(mapname)] = std::make_pair(music, order);
}
DEFINE_MAP_OPTION(fadetable, true) DEFINE_MAP_OPTION(fadetable, true)
{ {
parse.ParseAssign(); parse.ParseAssign();
@ -1265,6 +1277,7 @@ MapFlagHandlers[] =
{ "laxmonsteractivation", MITYPE_SETFLAG2, LEVEL2_LAXMONSTERACTIVATION, LEVEL2_LAXACTIVATIONMAPINFO }, { "laxmonsteractivation", MITYPE_SETFLAG2, LEVEL2_LAXMONSTERACTIVATION, LEVEL2_LAXACTIVATIONMAPINFO },
{ "additive_scrollers", MITYPE_COMPATFLAG, COMPATF_BOOMSCROLL, 0 }, { "additive_scrollers", MITYPE_COMPATFLAG, COMPATF_BOOMSCROLL, 0 },
{ "keepfullinventory", MITYPE_SETFLAG2, LEVEL2_KEEPFULLINVENTORY, 0 }, { "keepfullinventory", MITYPE_SETFLAG2, LEVEL2_KEEPFULLINVENTORY, 0 },
{ "resetitems", MITYPE_SETFLAG3, LEVEL2_RESETINVENTORY, 0 },
{ "monsterfallingdamage", MITYPE_SETFLAG2, LEVEL2_MONSTERFALLINGDAMAGE, 0 }, { "monsterfallingdamage", MITYPE_SETFLAG2, LEVEL2_MONSTERFALLINGDAMAGE, 0 },
{ "nomonsterfallingdamage", MITYPE_CLRFLAG2, LEVEL2_MONSTERFALLINGDAMAGE, 0 }, { "nomonsterfallingdamage", MITYPE_CLRFLAG2, LEVEL2_MONSTERFALLINGDAMAGE, 0 },
{ "clipmidtextures", MITYPE_SETFLAG2, LEVEL2_CLIPMIDTEX, 0 }, { "clipmidtextures", MITYPE_SETFLAG2, LEVEL2_CLIPMIDTEX, 0 },
@ -1970,6 +1983,7 @@ static void ClearMapinfo()
DefaultSkill = -1; DefaultSkill = -1;
DeinitIntermissions(); DeinitIntermissions();
level.info = NULL; level.info = NULL;
level.F1Pic = "";
} }
//========================================================================== //==========================================================================

View file

@ -704,6 +704,24 @@ CCMD (spray)
Net_WriteString (argv[1]); Net_WriteString (argv[1]);
} }
void SprayDecal(AActor *shooter, const char *name)
{
FTraceResults trace;
DAngle ang = shooter->Angles.Yaw;
DAngle pitch = shooter->Angles.Pitch;
double c = pitch.Cos();
DVector3 vec(c * ang.Cos(), c * ang.Sin(), -pitch.Sin());
if (Trace(shooter->PosPlusZ(shooter->Height / 2), shooter->Sector, vec, 172., 0, ML_BLOCKEVERYTHING, shooter, trace, TRACE_NoSky))
{
if (trace.HitType == TRACE_HitWall)
{
DImpactDecal::StaticCreate(name, trace.HitPos, trace.Line->sidedef[trace.Side], NULL);
}
}
}
DBaseDecal *ShootDecal(const FDecalTemplate *tpl, AActor *basisactor, sector_t *sec, double x, double y, double z, DAngle angle, double tracedist, bool permanent) DBaseDecal *ShootDecal(const FDecalTemplate *tpl, AActor *basisactor, sector_t *sec, double x, double y, double z, DAngle angle, double tracedist, bool permanent)
{ {
if (tpl == NULL || (tpl = tpl->GetDecal()) == NULL) if (tpl == NULL || (tpl = tpl->GetDecal()) == NULL)
@ -751,14 +769,23 @@ IMPLEMENT_CLASS(ADecal, false, false)
void ADecal::BeginPlay () void ADecal::BeginPlay ()
{ {
const FDecalTemplate *tpl; const FDecalTemplate *tpl = nullptr;
Super::BeginPlay (); Super::BeginPlay ();
int decalid = args[0] + (args[1] << 8); // [KS] High byte for decals. if (args[0] < 0)
{
FName name = ENamedName(-args[0]);
tpl = DecalLibrary.GetDecalByName(name.GetChars());
}
else
{
int decalid = args[0] + (args[1] << 8); // [KS] High byte for decals.
tpl = DecalLibrary.GetDecalByNum(decalid);
}
// If no decal is specified, don't try to create one. // If no decal is specified, don't try to create one.
if (decalid != 0 && (tpl = DecalLibrary.GetDecalByNum (decalid)) != 0) if (tpl != nullptr)
{ {
if (!tpl->PicNum.Exists()) if (!tpl->PicNum.Exists())
{ {

View file

@ -298,7 +298,7 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag,
// If a custom skin was in use, then reload it // If a custom skin was in use, then reload it
// or else the base skin for the player class. // or else the base skin for the player class.
if ((unsigned int)player->userinfo.GetSkin() >= PlayerClasses.Size () && if ((unsigned int)player->userinfo.GetSkin() >= PlayerClasses.Size () &&
(size_t)player->userinfo.GetSkin() < numskins) (unsigned)player->userinfo.GetSkin() < Skins.Size())
{ {
skinindex = player->userinfo.GetSkin(); skinindex = player->userinfo.GetSkin();
@ -701,3 +701,30 @@ void AMorphedMonster::Tick ()
Super::Tick (); Super::Tick ();
} }
} }
DEFINE_ACTION_FUNCTION(AActor, A_Morph)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_CLASS(type, AActor);
PARAM_INT_DEF(duration);
PARAM_INT_DEF(flags);
PARAM_CLASS_DEF(enter_flash, AActor);
PARAM_CLASS_DEF(exit_flash, AActor);
bool res = false;
if (self->player)
{
if (type->IsKindOf(RUNTIME_CLASS(APlayerPawn)))
{
res = P_MorphPlayer(self->player, self->player, type, duration, flags, enter_flash, exit_flash);
}
}
else
{
if (type->IsKindOf(RUNTIME_CLASS(AMorphedMonster)))
{
res = P_MorphMonster(self, type, duration, flags, enter_flash, exit_flash);
}
}
ACTION_RETURN_BOOL(res);
}

View file

@ -11,6 +11,7 @@ struct F3DFloor;
class DBaseDecal; class DBaseDecal;
class DBaseDecal *ShootDecal(const FDecalTemplate *tpl, AActor *basisactor, sector_t *sec, double x, double y, double z, DAngle angle, double tracedist, bool permanent); class DBaseDecal *ShootDecal(const FDecalTemplate *tpl, AActor *basisactor, sector_t *sec, double x, double y, double z, DAngle angle, double tracedist, bool permanent);
void SprayDecal(AActor *shooter, const char *name);
class DBaseDecal : public DThinker class DBaseDecal : public DThinker
{ {

View file

@ -1136,10 +1136,6 @@ void DrawHUD()
i=DrawAmmo(CPlayer, hudwidth-5, i); i=DrawAmmo(CPlayer, hudwidth-5, i);
if (hud_showweapons) DrawWeapons(CPlayer, hudwidth - 5, i); if (hud_showweapons) DrawWeapons(CPlayer, hudwidth - 5, i);
DrawInventory(CPlayer, 144, hudheight-28); DrawInventory(CPlayer, 144, hudheight-28);
if (CPlayer->camera && CPlayer->camera->player)
{
StatusBar->DrawCrosshair();
}
if (idmypos) DrawCoordinates(CPlayer); if (idmypos) DrawCoordinates(CPlayer);
DrawTime(); DrawTime();

View file

@ -60,12 +60,14 @@ void FMapInfoParser::ParseSkill ()
bool thisisdefault = false; bool thisisdefault = false;
bool acsreturnisset = false; bool acsreturnisset = false;
skill.NoMenu = false;
skill.AmmoFactor = 1.; skill.AmmoFactor = 1.;
skill.DoubleAmmoFactor = 2.; skill.DoubleAmmoFactor = 2.;
skill.DropAmmoFactor = -1.; skill.DropAmmoFactor = -1.;
skill.DamageFactor = 1.; skill.DamageFactor = 1.;
skill.ArmorFactor = 1.; skill.ArmorFactor = 1.;
skill.HealthFactor = 1.; skill.HealthFactor = 1.;
skill.KickbackFactor = 1.;
skill.FastMonsters = false; skill.FastMonsters = false;
skill.SlowMonsters = false; skill.SlowMonsters = false;
skill.DisableCheats = false; skill.DisableCheats = false;
@ -118,6 +120,12 @@ void FMapInfoParser::ParseSkill ()
sc.MustGetFloat (); sc.MustGetFloat ();
skill.DamageFactor = sc.Float; skill.DamageFactor = sc.Float;
} }
else if (sc.Compare("kickbackfactor"))
{
ParseAssign();
sc.MustGetFloat();
skill.KickbackFactor = sc.Float;
}
else if (sc.Compare ("fastmonsters")) else if (sc.Compare ("fastmonsters"))
{ {
skill.FastMonsters = true; skill.FastMonsters = true;
@ -142,6 +150,10 @@ void FMapInfoParser::ParseSkill ()
{ {
skill.AutoUseHealth = true; skill.AutoUseHealth = true;
} }
else if (sc.Compare("nomenu"))
{
skill.NoMenu = true;
}
else if (sc.Compare("respawntime")) else if (sc.Compare("respawntime"))
{ {
ParseAssign(); ParseAssign();
@ -436,6 +448,9 @@ double G_SkillProperty(EFSkillProperty prop)
case SKILLP_FriendlyHealth: case SKILLP_FriendlyHealth:
return AllSkills[gameskill].FriendlyHealth; return AllSkills[gameskill].FriendlyHealth;
case SKILLP_KickbackFactor:
return AllSkills[gameskill].KickbackFactor;
} }
} }
return 0; return 0;
@ -498,9 +513,11 @@ FSkillInfo &FSkillInfo::operator=(const FSkillInfo &other)
{ {
Name = other.Name; Name = other.Name;
AmmoFactor = other.AmmoFactor; AmmoFactor = other.AmmoFactor;
NoMenu = other.NoMenu;
DoubleAmmoFactor = other.DoubleAmmoFactor; DoubleAmmoFactor = other.DoubleAmmoFactor;
DropAmmoFactor = other.DropAmmoFactor; DropAmmoFactor = other.DropAmmoFactor;
DamageFactor = other.DamageFactor; DamageFactor = other.DamageFactor;
KickbackFactor = other.KickbackFactor;
FastMonsters = other.FastMonsters; FastMonsters = other.FastMonsters;
SlowMonsters = other.SlowMonsters; SlowMonsters = other.SlowMonsters;
DisableCheats = other.DisableCheats; DisableCheats = other.DisableCheats;

View file

@ -404,16 +404,6 @@ public:
double CrosshairSize; double CrosshairSize;
double Displacement; double Displacement;
enum
{
imgLAME = 0,
imgNEGATIVE = 1,
imgINumbers = 2,
imgBNEGATIVE = 12,
imgBNumbers = 13,
imgSmNumbers = 23,
NUM_BASESB_IMAGES = 33
};
FImageCollection Images; FImageCollection Images;
player_t *CPlayer; player_t *CPlayer;

View file

@ -489,7 +489,7 @@ FTexture *FMugShot::GetFace(player_t *player, const char *default_face, int accu
if (CurrentState != NULL) if (CurrentState != NULL)
{ {
int skin = player->userinfo.GetSkin(); int skin = player->userinfo.GetSkin();
const char *skin_face = (stateflags & FMugShot::CUSTOM) ? nullptr : (player->morphTics ? ((APlayerPawn*)GetDefaultByType(player->MorphedPlayerClass))->Face.GetChars() : skins[skin].face); const char *skin_face = (stateflags & FMugShot::CUSTOM) ? nullptr : (player->morphTics ? ((APlayerPawn*)GetDefaultByType(player->MorphedPlayerClass))->Face.GetChars() : Skins[skin].Face.GetChars());
return CurrentState->GetCurrentFrameTexture(default_face, skin_face, level, angle); return CurrentState->GetCurrentFrameTexture(default_face, skin_face, level, angle);
} }
return NULL; return NULL;

View file

@ -54,7 +54,6 @@
#include "v_palette.h" #include "v_palette.h"
#include "p_acs.h" #include "p_acs.h"
#include "gstrings.h" #include "gstrings.h"
#include "version.h"
#include "cmdlib.h" #include "cmdlib.h"
#include "g_levellocals.h" #include "g_levellocals.h"

View file

@ -2744,7 +2744,7 @@ class CommandDrawBar : public SBarInfoCommand
max = 0; max = 0;
} }
else //default to the class's health else //default to the class's health
max = statusBar->CPlayer->mo->GetMaxHealth() + statusBar->CPlayer->mo->stamina; max = statusBar->CPlayer->mo->GetMaxHealth(true);
break; break;
case ARMOR: case ARMOR:
value = statusBar->armor != NULL ? statusBar->armor->Amount : 0; value = statusBar->armor != NULL ? statusBar->armor->Amount : 0;
@ -3251,7 +3251,7 @@ class CommandDrawGem : public SBarInfoCommand
void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged)
{ {
goalValue = armor ? (statusBar->armor ? statusBar->armor->Amount : 0) : statusBar->CPlayer->mo->health; goalValue = armor ? (statusBar->armor ? statusBar->armor->Amount : 0) : statusBar->CPlayer->mo->health;
int max = armor ? 100 : statusBar->CPlayer->mo->GetMaxHealth() + statusBar->CPlayer->mo->stamina; int max = armor ? 100 : statusBar->CPlayer->mo->GetMaxHealth(true);
if(max != 0 && goalValue > 0) if(max != 0 && goalValue > 0)
{ {
goalValue = (goalValue*100)/max; goalValue = (goalValue*100)/max;

View file

@ -186,18 +186,6 @@ class DStrifeStatusBar : public DBaseStatusBar
public: public:
DStrifeStatusBar () : DBaseStatusBar (32) DStrifeStatusBar () : DBaseStatusBar (32)
{ {
static const char *sharedLumpNames[] =
{
NULL, NULL, "INVFONY0", "INVFONY1", "INVFONY2",
"INVFONY3", "INVFONY4", "INVFONY5", "INVFONY6", "INVFONY7",
"INVFONY8", "INVFONY9", NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, "INVFONG0", "INVFONG1",
"INVFONG2", "INVFONG3", "INVFONG4", "INVFONG5", "INVFONG6",
"INVFONG7", "INVFONG8", "INVFONG9"
};
DBaseStatusBar::Images.Init (sharedLumpNames, NUM_BASESB_IMAGES);
DoCommonInit (); DoCommonInit ();
} }
@ -301,10 +289,11 @@ private:
"INVFONY0", "INVFONY1", "INVFONY2", "INVFONY3", "INVFONY4", "INVFONY0", "INVFONY1", "INVFONY2", "INVFONY3", "INVFONY4",
"INVFONY5", "INVFONY6", "INVFONY7", "INVFONY8", "INVFONY9", "INVFONY5", "INVFONY6", "INVFONY7", "INVFONY8", "INVFONY9",
"INVFONY%", "INVFONY%",
"I_COMM", "I_MDKT", "I_ARM1", "I_ARM2" "I_COMM", "I_MDKT", "I_ARM1", "I_ARM2", nullptr
}; };
Images.Init (strifeLumpNames, NUM_STRIFESB_IMAGES); Images.Init (strifeLumpNames, countof(strifeLumpNames));
CursorImage = Images[imgINVCURS] != NULL ? imgINVCURS : imgCURSOR01; CursorImage = Images[imgINVCURS] != NULL ? imgINVCURS : imgCURSOR01;
@ -834,8 +823,8 @@ private:
imgMEDI, imgMEDI,
imgARM1, imgARM1,
imgARM2, imgARM2,
imgNEGATIVE,
NUM_STRIFESB_IMAGES imgINumbers = imgFONG0,
}; };
FImageCollection Images; FImageCollection Images;

View file

@ -50,6 +50,13 @@ DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, Armor2Percent)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, ArmorIcon1) DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, ArmorIcon1)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, ArmorIcon2) DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, ArmorIcon2)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, gametype) DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, gametype)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, norandomplayerclass)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, infoPages)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, mBackButton)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, mStatscreenMapNameFont)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, mStatscreenEnteringFont)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, mStatscreenFinishedFont)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, gibfactor)
const char *GameNames[17] = const char *GameNames[17] =
@ -360,6 +367,7 @@ void FMapInfoParser::ParseGameInfo()
GAMEINFOKEY_INT(TextScreenX, "textscreenx") GAMEINFOKEY_INT(TextScreenX, "textscreenx")
GAMEINFOKEY_INT(TextScreenY, "textscreeny") GAMEINFOKEY_INT(TextScreenY, "textscreeny")
GAMEINFOKEY_STRING(DefaultEndSequence, "defaultendsequence") GAMEINFOKEY_STRING(DefaultEndSequence, "defaultendsequence")
GAMEINFOKEY_STRING(DefaultConversationMenuClass, "defaultconversationmenuclass")
GAMEINFOKEY_FONT(mStatscreenMapNameFont, "statscreen_mapnamefont") GAMEINFOKEY_FONT(mStatscreenMapNameFont, "statscreen_mapnamefont")
GAMEINFOKEY_FONT(mStatscreenFinishedFont, "statscreen_finishedfont") GAMEINFOKEY_FONT(mStatscreenFinishedFont, "statscreen_finishedfont")
GAMEINFOKEY_FONT(mStatscreenEnteringFont, "statscreen_enteringfont") GAMEINFOKEY_FONT(mStatscreenEnteringFont, "statscreen_enteringfont")
@ -367,6 +375,7 @@ void FMapInfoParser::ParseGameInfo()
GAMEINFOKEY_PATCH(mStatscreenEnteringFont, "statscreen_enteringpatch") GAMEINFOKEY_PATCH(mStatscreenEnteringFont, "statscreen_enteringpatch")
GAMEINFOKEY_BOOL(norandomplayerclass, "norandomplayerclass") GAMEINFOKEY_BOOL(norandomplayerclass, "norandomplayerclass")
GAMEINFOKEY_BOOL(forcekillscripts, "forcekillscripts") // [JM] Force kill scripts on thing death. (MF7_NOKILLSCRIPTS overrides.) GAMEINFOKEY_BOOL(forcekillscripts, "forcekillscripts") // [JM] Force kill scripts on thing death. (MF7_NOKILLSCRIPTS overrides.)
GAMEINFOKEY_STRING(Dialogue, "dialogue")
else else
{ {

View file

@ -172,9 +172,11 @@ struct gameinfo_t
double gibfactor; double gibfactor;
int TextScreenX; int TextScreenX;
int TextScreenY; int TextScreenY;
FName DefaultConversationMenuClass;
FName DefaultEndSequence; FName DefaultEndSequence;
FString mMapArrow, mCheatMapArrow; FString mMapArrow, mCheatMapArrow;
FString mEasyKey, mCheatKey; FString mEasyKey, mCheatKey;
FString Dialogue;
FGIFont mStatscreenMapNameFont; FGIFont mStatscreenMapNameFont;
FGIFont mStatscreenFinishedFont; FGIFont mStatscreenFinishedFont;
FGIFont mStatscreenEnteringFont; FGIFont mStatscreenEnteringFont;

View file

@ -34,7 +34,6 @@
#include "doomtype.h" #include "doomtype.h"
#include "m_argv.h" #include "m_argv.h"
#include "zstring.h" #include "zstring.h"
#include "version.h"
#include "i_system.h" #include "i_system.h"
#include "v_text.h" #include "v_text.h"
#include "r_utility.h" #include "r_utility.h"

View file

@ -109,6 +109,7 @@ void AdjustSpriteOffsets()
{ {
char str[9]; char str[9];
Wads.GetLumpName(str, i); Wads.GetLumpName(str, i);
str[8] = 0;
FTextureID texid = TexMan.CheckForTexture(str, FTexture::TEX_Sprite, 0); FTextureID texid = TexMan.CheckForTexture(str, FTexture::TEX_Sprite, 0);
if (texid.isValid() && Wads.GetLumpFile(TexMan[texid]->SourceLump) > FWadCollection::IWAD_FILENUM) if (texid.isValid() && Wads.GetLumpFile(TexMan[texid]->SourceLump) > FWadCollection::IWAD_FILENUM)
{ {

View file

@ -204,7 +204,8 @@ void ADynamicLight::Activate(AActor *activator)
float pulseTime = specialf1 / TICRATE; float pulseTime = specialf1 / TICRATE;
m_lastUpdate = level.maptime; m_lastUpdate = level.maptime;
m_cycler.SetParams(float(args[LIGHT_SECONDARY_INTENSITY]), float(args[LIGHT_INTENSITY]), pulseTime); if (!swapped) m_cycler.SetParams(float(args[LIGHT_SECONDARY_INTENSITY]), float(args[LIGHT_INTENSITY]), pulseTime);
else m_cycler.SetParams(float(args[LIGHT_INTENSITY]), float(args[LIGHT_SECONDARY_INTENSITY]), pulseTime);
m_cycler.ShouldCycle(true); m_cycler.ShouldCycle(true);
m_cycler.SetCycleType(CYCLE_Sin); m_cycler.SetCycleType(CYCLE_Sin);
m_currentRadius = m_cycler.GetVal(); m_currentRadius = m_cycler.GetVal();

View file

@ -135,6 +135,16 @@ public:
void SetDontLightSelf(bool add) { m_dontlightself = add; } void SetDontLightSelf(bool add) { m_dontlightself = add; }
void SetAttenuate(bool on) { m_attenuate = on; } void SetAttenuate(bool on) { m_attenuate = on; }
void SetHalo(bool halo) { m_halo = halo; } void SetHalo(bool halo) { m_halo = halo; }
void OrderIntensities()
{
if (m_Args[LIGHT_INTENSITY] > m_Args[LIGHT_SECONDARY_INTENSITY])
{
std::swap(m_Args[LIGHT_INTENSITY], m_Args[LIGHT_SECONDARY_INTENSITY]);
m_swapped = true;
}
}
protected: protected:
FName m_Name; FName m_Name;
int m_Args[5]; int m_Args[5];
@ -143,6 +153,7 @@ protected:
ELightType m_type; ELightType m_type;
int8_t m_attenuate; int8_t m_attenuate;
bool m_subtractive, m_additive, m_halo, m_dontlightself; bool m_subtractive, m_additive, m_halo, m_dontlightself;
bool m_swapped = false;
}; };
TArray<FLightDefaults *> LightDefaults; TArray<FLightDefaults *> LightDefaults;
@ -178,8 +189,8 @@ void FLightDefaults::ApplyProperties(ADynamicLight * light) const
light->SetOffset(m_Pos); light->SetOffset(m_Pos);
light->halo = m_halo; light->halo = m_halo;
for (int a = 0; a < 3; a++) light->args[a] = clamp<int>((int)(m_Args[a]), 0, 255); for (int a = 0; a < 3; a++) light->args[a] = clamp<int>((int)(m_Args[a]), 0, 255);
light->args[LIGHT_INTENSITY] = int(m_Args[LIGHT_INTENSITY]); light->args[LIGHT_INTENSITY] = m_Args[LIGHT_INTENSITY];
light->args[LIGHT_SECONDARY_INTENSITY] = int(m_Args[LIGHT_SECONDARY_INTENSITY]); light->args[LIGHT_SECONDARY_INTENSITY] = m_Args[LIGHT_SECONDARY_INTENSITY];
light->flags4 &= ~(MF4_ADDITIVE | MF4_SUBTRACTIVE | MF4_DONTLIGHTSELF); light->flags4 &= ~(MF4_ADDITIVE | MF4_SUBTRACTIVE | MF4_DONTLIGHTSELF);
if (m_subtractive) light->flags4 |= MF4_SUBTRACTIVE; if (m_subtractive) light->flags4 |= MF4_SUBTRACTIVE;
if (m_additive) light->flags4 |= MF4_ADDITIVE; if (m_additive) light->flags4 |= MF4_ADDITIVE;
@ -190,11 +201,13 @@ void FLightDefaults::ApplyProperties(ADynamicLight * light) const
float pulseTime = float(m_Param / TICRATE); float pulseTime = float(m_Param / TICRATE);
light->m_lastUpdate = level.maptime; light->m_lastUpdate = level.maptime;
light->m_cycler.SetParams(float(light->args[LIGHT_SECONDARY_INTENSITY]), float(light->args[LIGHT_INTENSITY]), pulseTime, oldtype == PulseLight); if (m_swapped) light->m_cycler.SetParams(float(light->args[LIGHT_SECONDARY_INTENSITY]), float(light->args[LIGHT_INTENSITY]), pulseTime, oldtype == PulseLight);
else light->m_cycler.SetParams(float(light->args[LIGHT_INTENSITY]), float(light->args[LIGHT_SECONDARY_INTENSITY]), pulseTime, oldtype == PulseLight);
light->m_cycler.ShouldCycle(true); light->m_cycler.ShouldCycle(true);
light->m_cycler.SetCycleType(CYCLE_Sin); light->m_cycler.SetCycleType(CYCLE_Sin);
light->m_currentRadius = light->m_cycler.GetVal(); light->m_currentRadius = light->m_cycler.GetVal();
if (light->m_currentRadius <= 0) light->m_currentRadius = 1; if (light->m_currentRadius <= 0) light->m_currentRadius = 1;
light->swapped = m_swapped;
} }
switch (m_attenuate) switch (m_attenuate)
@ -474,13 +487,7 @@ void gl_ParsePulseLight(FScanner &sc)
sc.ScriptError("Unknown tag: %s\n", sc.String); sc.ScriptError("Unknown tag: %s\n", sc.String);
} }
} }
if (defaults->GetArg(LIGHT_INTENSITY) > defaults->GetArg(LIGHT_SECONDARY_INTENSITY)) defaults->OrderIntensities();
{
auto i = defaults->GetArg(LIGHT_INTENSITY);
auto j = defaults->GetArg(LIGHT_SECONDARY_INTENSITY);
defaults->SetArg(LIGHT_INTENSITY, j);
defaults->SetArg(LIGHT_SECONDARY_INTENSITY, i);
}
gl_AddLightDefaults(defaults); gl_AddLightDefaults(defaults);
} }
@ -564,6 +571,7 @@ void gl_ParseFlickerLight(FScanner &sc)
sc.ScriptError("Unknown tag: %s\n", sc.String); sc.ScriptError("Unknown tag: %s\n", sc.String);
} }
} }
defaults->OrderIntensities();
gl_AddLightDefaults(defaults); gl_AddLightDefaults(defaults);
} }
else else

View file

@ -141,6 +141,7 @@ public:
bool halo; bool halo;
BYTE color2[3]; BYTE color2[3];
bool visibletoplayer; bool visibletoplayer;
bool swapped;
int bufferindex; int bufferindex;

View file

@ -665,7 +665,7 @@ void GLSprite::Process(AActor* thing, sector_t * sector, int thruportal)
bool isPicnumOverride = thing->picnum.isValid(); bool isPicnumOverride = thing->picnum.isValid();
// Don't waste time projecting sprites that are definitely not visible. // Don't waste time projecting sprites that are definitely not visible.
if ((thing->sprite == 0 && !isPicnumOverride) || !thing->IsVisibleToPlayer() || !thing->IsInsideVisibleAngles()) if ((thing->sprite == 0 && !isPicnumOverride) || !thing->IsVisibleToPlayer() || ((thing->renderflags & RF_MASKROTATION) && !thing->IsInsideVisibleAngles()))
{ {
return; return;
} }

View file

@ -83,7 +83,7 @@ OpenGLFrameBuffer::OpenGLFrameBuffer(void *hMonitor, int width, int height, int
{ {
// SetVSync needs to be at the very top to workaround a bug in Nvidia's OpenGL driver. // SetVSync needs to be at the very top to workaround a bug in Nvidia's OpenGL driver.
// If wglSwapIntervalEXT is called after glBindFramebuffer in a frame the setting is not changed! // If wglSwapIntervalEXT is called after glBindFramebuffer in a frame the setting is not changed!
SetVSync(vid_vsync); Super::SetVSync(vid_vsync);
// Make sure all global variables tracking OpenGL context state are reset.. // Make sure all global variables tracking OpenGL context state are reset..
FHardwareTexture::InitGlobalState(); FHardwareTexture::InitGlobalState();
@ -230,6 +230,27 @@ void OpenGLFrameBuffer::Swap()
mDebug->Update(); mDebug->Update();
} }
//==========================================================================
//
// Enable/disable vertical sync
//
//==========================================================================
void OpenGLFrameBuffer::SetVSync(bool vsync)
{
// Switch to the default frame buffer because some drivers associate the vsync state with the bound FB object.
GLint oldDrawFramebufferBinding = 0, oldReadFramebufferBinding = 0;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldDrawFramebufferBinding);
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &oldReadFramebufferBinding);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
Super::SetVSync(vsync);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oldDrawFramebufferBinding);
glBindFramebuffer(GL_READ_FRAMEBUFFER, oldReadFramebufferBinding);
}
//=========================================================================== //===========================================================================
// //
// DoSetGamma // DoSetGamma

View file

@ -83,6 +83,8 @@ public:
bool Is8BitMode() { return false; } bool Is8BitMode() { return false; }
bool IsHWGammaActive() const { return HWGammaActive; } bool IsHWGammaActive() const { return HWGammaActive; }
void SetVSync(bool vsync);
private: private:
PalEntry Flash; PalEntry Flash;

View file

@ -49,14 +49,14 @@
#ifndef _WIN32 #ifndef _WIN32
struct POINT { struct POINT {
SDWORD x; int32_t x;
SDWORD y; int32_t y;
}; };
struct RECT { struct RECT {
SDWORD left; int32_t left;
SDWORD top; int32_t top;
SDWORD right; int32_t right;
SDWORD bottom; int32_t bottom;
}; };
#endif #endif

View file

@ -251,20 +251,7 @@ PClassActor::PClassActor()
DamageFactors = NULL; DamageFactors = NULL;
PainChances = NULL; PainChances = NULL;
DeathHeight = -1;
BurnHeight = -1;
GibHealth = INT_MIN;
WoundHealth = 6;
FastSpeed = -1.;
RDFactor = 1.;
CameraHeight = INT_MIN;
DropItems = NULL; DropItems = NULL;
DontHurtShooter = false;
ExplosionRadius = -1;
MeleeDamage = 0;
// Record this in the master list. // Record this in the master list.
AllActorClasses.Push(this); AllActorClasses.Push(this);
} }
@ -308,32 +295,10 @@ void PClassActor::DeriveData(PClass *newclass)
PClassActor *newa = static_cast<PClassActor *>(newclass); PClassActor *newa = static_cast<PClassActor *>(newclass);
newa->DefaultStateUsage = DefaultStateUsage; newa->DefaultStateUsage = DefaultStateUsage;
newa->Obituary = Obituary;
newa->HitObituary = HitObituary;
newa->DeathHeight = DeathHeight;
newa->BurnHeight = BurnHeight;
newa->BloodColor = BloodColor;
newa->GibHealth = GibHealth;
newa->WoundHealth = WoundHealth;
newa->FastSpeed = FastSpeed;
newa->RDFactor = RDFactor;
newa->CameraHeight = CameraHeight;
newa->HowlSound = HowlSound;
newa->BloodType = BloodType;
newa->BloodType2 = BloodType2;
newa->BloodType3 = BloodType3;
newa->distancecheck = distancecheck; newa->distancecheck = distancecheck;
newa->DropItems = DropItems; newa->DropItems = DropItems;
newa->DontHurtShooter = DontHurtShooter;
newa->ExplosionRadius = ExplosionRadius;
newa->ExplosionDamage = ExplosionDamage;
newa->MeleeDamage = MeleeDamage;
newa->MeleeSound = MeleeSound;
newa->MissileName = MissileName;
newa->MissileHeight = MissileHeight;
newa->VisibleToPlayerClass = VisibleToPlayerClass; newa->VisibleToPlayerClass = VisibleToPlayerClass;
if (DamageFactors != NULL) if (DamageFactors != NULL)
@ -350,7 +315,6 @@ void PClassActor::DeriveData(PClass *newclass)
} }
// Inventory stuff // Inventory stuff
newa->PickupMsg = PickupMsg;
newa->ForbiddenToPlayerClass = ForbiddenToPlayerClass; newa->ForbiddenToPlayerClass = ForbiddenToPlayerClass;
newa->RestrictedToPlayerClass = RestrictedToPlayerClass; newa->RestrictedToPlayerClass = RestrictedToPlayerClass;
@ -767,6 +731,13 @@ DEFINE_ACTION_FUNCTION(_DamageTypeDefinition, IgnoreArmor)
ACTION_RETURN_BOOL(DamageTypeDefinition::IgnoreArmor(type)); ACTION_RETURN_BOOL(DamageTypeDefinition::IgnoreArmor(type));
} }
FString DamageTypeDefinition::GetObituary(FName type)
{
DamageTypeDefinition *dtd = Get(type);
if (dtd) return dtd->Obituary;
return "";
}
//========================================================================== //==========================================================================
// //
@ -859,6 +830,12 @@ void FMapInfoParser::ParseDamageDefinition()
dtd.DefaultFactor = sc.Float; dtd.DefaultFactor = sc.Float;
if (dtd.DefaultFactor == 0) dtd.ReplaceFactor = true; if (dtd.DefaultFactor == 0) dtd.ReplaceFactor = true;
} }
else if (sc.Compare("OBITUARY"))
{
sc.MustGetStringName("=");
sc.MustGetString();
dtd.Obituary = sc.String;
}
else if (sc.Compare("REPLACEFACTOR")) else if (sc.Compare("REPLACEFACTOR"))
{ {
dtd.ReplaceFactor = true; dtd.ReplaceFactor = true;

View file

@ -214,6 +214,7 @@ struct DamageTypeDefinition
public: public:
DamageTypeDefinition() { Clear(); } DamageTypeDefinition() { Clear(); }
FString Obituary;
double DefaultFactor; double DefaultFactor;
bool ReplaceFactor; bool ReplaceFactor;
bool NoArmor; bool NoArmor;
@ -221,6 +222,7 @@ public:
void Apply(FName type); void Apply(FName type);
void Clear() void Clear()
{ {
Obituary = "";
DefaultFactor = 1.; DefaultFactor = 1.;
ReplaceFactor = false; ReplaceFactor = false;
NoArmor = false; NoArmor = false;
@ -228,6 +230,7 @@ public:
static bool IgnoreArmor(FName type); static bool IgnoreArmor(FName type);
static int ApplyMobjDamageFactor(int damage, FName type, DmgFactors const * const factors); static int ApplyMobjDamageFactor(int damage, FName type, DmgFactors const * const factors);
static FString GetObituary(FName type);
private: private:
static double GetMobjDamageFactor(FName type, DmgFactors const * const factors); static double GetMobjDamageFactor(FName type, DmgFactors const * const factors);
@ -287,36 +290,11 @@ public:
TArray<PClassActor *> VisibleToPlayerClass; TArray<PClassActor *> VisibleToPlayerClass;
FString Obituary; // Player was killed by this actor
FString HitObituary; // Player was killed by this actor in melee
double DeathHeight; // Height on normal death
double BurnHeight; // Height on burning death
PalEntry BloodColor; // Colorized blood
int GibHealth; // Negative health below which this monster dies an extreme death
int WoundHealth; // Health needed to enter wound state
double FastSpeed; // speed in fast mode
double RDFactor; // Radius damage factor
double CameraHeight; // Height of camera when used as such
FSoundID HowlSound; // Sound being played when electrocuted or poisoned
FName BloodType; // Blood replacement type
FName BloodType2; // Bloopsplatter replacement type
FName BloodType3; // AxeBlood replacement type
FDropItem *DropItems; FDropItem *DropItems;
FString SourceLumpName; FString SourceLumpName;
FIntCVar *distancecheck; FIntCVar *distancecheck;
// Old Decorate compatibility stuff
bool DontHurtShooter;
int ExplosionRadius;
int ExplosionDamage;
int MeleeDamage;
FSoundID MeleeSound;
FName MissileName;
double MissileHeight;
// These are only valid for inventory items. // These are only valid for inventory items.
FString PickupMsg;
TArray<PClassActor *> RestrictedToPlayerClass; TArray<PClassActor *> RestrictedToPlayerClass;
TArray<PClassActor *> ForbiddenToPlayerClass; TArray<PClassActor *> ForbiddenToPlayerClass;

View file

@ -68,6 +68,7 @@ IMPLEMENT_POINTERS_END
extern int NoWipe; extern int NoWipe;
CVAR(Bool, nointerscrollabort, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
//========================================================================== //==========================================================================
// //
// //
@ -593,7 +594,7 @@ void DIntermissionScreenCast::Drawer ()
if (!(mDefaults->flags4 & MF4_NOSKIN) && if (!(mDefaults->flags4 & MF4_NOSKIN) &&
mDefaults->SpawnState != NULL && caststate->sprite == mDefaults->SpawnState->sprite && mDefaults->SpawnState != NULL && caststate->sprite == mDefaults->SpawnState->sprite &&
mClass->IsDescendantOf(RUNTIME_CLASS(APlayerPawn)) && mClass->IsDescendantOf(RUNTIME_CLASS(APlayerPawn)) &&
skins != NULL) Skins.Size() > 0)
{ {
// Only use the skin sprite if this class has not been removed from the // Only use the skin sprite if this class has not been removed from the
// PlayerClasses list. // PlayerClasses list.
@ -601,7 +602,7 @@ void DIntermissionScreenCast::Drawer ()
{ {
if (PlayerClasses[i].Type == mClass) if (PlayerClasses[i].Type == mClass)
{ {
FPlayerSkin *skin = &skins[players[consoleplayer].userinfo.GetSkin()]; FPlayerSkin *skin = &Skins[players[consoleplayer].userinfo.GetSkin()];
castsprite = skin->sprite; castsprite = skin->sprite;
if (!(mDefaults->flags4 & MF4_NOSKIN)) if (!(mDefaults->flags4 & MF4_NOSKIN))
@ -647,7 +648,7 @@ void DIntermissionScreenScroller::Init(FIntermissionAction *desc, bool first)
int DIntermissionScreenScroller::Responder (event_t *ev) int DIntermissionScreenScroller::Responder (event_t *ev)
{ {
int res = Super::Responder(ev); int res = Super::Responder(ev);
if (res == -1) if (res == -1 && !nointerscrollabort)
{ {
mBackground = mSecondPic; mBackground = mSecondPic;
mTicker = mScrollDelay + mScrollTime; mTicker = mScrollDelay + mScrollTime;

View file

@ -58,15 +58,15 @@ enum EScrollDir
}; };
// actions that don't create objects // actions that don't create objects
#define WIPER_ID ((const PClass*)intptr_t(-1)) #define WIPER_ID ((PClass*)intptr_t(-1))
#define TITLE_ID ((const PClass*)intptr_t(-2)) #define TITLE_ID ((PClass*)intptr_t(-2))
//========================================================================== //==========================================================================
struct FIntermissionAction struct FIntermissionAction
{ {
int mSize; int mSize;
const PClass *mClass; PClass *mClass;
FString mMusic; FString mMusic;
int mMusicOrder; int mMusicOrder;
int mCdTrack; int mCdTrack;

View file

@ -55,6 +55,22 @@
// writes some bytes to the network data stream, and the network code // writes some bytes to the network data stream, and the network code
// later calls us. // later calls us.
void cht_DoMDK(player_t *player, const char *mod)
{
if (player->mo == NULL)
{
Printf("What do you want to kill outside of a game?\n");
}
else if (!deathmatch)
{
// Don't allow this in deathmatch even with cheats enabled, because it's
// a very very cheap kill.
P_LineAttack(player->mo, player->mo->Angles.Yaw, PLAYERMISSILERANGE,
P_AimLineAttack(player->mo, player->mo->Angles.Yaw, PLAYERMISSILERANGE), TELEFRAG_DAMAGE,
mod, NAME_BulletPuff);
}
}
void cht_DoCheat (player_t *player, int cheat) void cht_DoCheat (player_t *player, int cheat)
{ {
static const char * const BeholdPowers[9] = static const char * const BeholdPowers[9] =
@ -671,6 +687,7 @@ CCMD (mdk)
if (CheckCheatmode ()) if (CheckCheatmode ())
return; return;
Net_WriteByte (DEM_GENERICCHEAT); const char *name = argv.argc() > 1 ? argv[1] : "";
Net_WriteByte (CHT_MDK); Net_WriteByte (DEM_MDK);
Net_WriteString(name);
} }

View file

@ -31,6 +31,7 @@
class player_t; class player_t;
class PClassActor; class PClassActor;
void cht_DoMDK(player_t *player, const char *mod);
void cht_DoCheat (player_t *player, int cheat); void cht_DoCheat (player_t *player, int cheat);
void cht_Give (player_t *player, const char *item, int amount=1); void cht_Give (player_t *player, const char *item, int amount=1);
void cht_Take (player_t *player, const char *item, int amount=1); void cht_Take (player_t *player, const char *item, int amount=1);

View file

@ -138,9 +138,13 @@ DEFINE_ACTION_FUNCTION(IJoystickConfig, GetNumAxes)
void UpdateJoystickMenu(IJoystickConfig *selected) void UpdateJoystickMenu(IJoystickConfig *selected)
{ {
DMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_JoystickOptions); DMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_JoystickOptions);
DMenuDescriptor **ddesc = MenuDescriptors.CheckKey("JoystickOptionsDefaults");
if (ddesc == nullptr) return; // without any data the menu cannot be set up and must remain empty.
if (desc != NULL && (*desc)->IsKindOf(RUNTIME_CLASS(DOptionMenuDescriptor))) if (desc != NULL && (*desc)->IsKindOf(RUNTIME_CLASS(DOptionMenuDescriptor)))
{ {
DOptionMenuDescriptor *opt = (DOptionMenuDescriptor *)*desc; DOptionMenuDescriptor *opt = (DOptionMenuDescriptor *)*desc;
DOptionMenuDescriptor *dopt = (DOptionMenuDescriptor *)*ddesc;
if (dopt == nullptr) return;
DMenuItemBase *it; DMenuItemBase *it;
int i; int i;
@ -162,11 +166,7 @@ void UpdateJoystickMenu(IJoystickConfig *selected)
} }
} }
} }
#ifdef _WIN32 opt->mItems = dopt->mItems;
opt->mItems.Resize(8);
#else
opt->mItems.Resize(5);
#endif
it = opt->GetItem("ConfigureMessage"); it = opt->GetItem("ConfigureMessage");
if (it != nullptr) it->SetValue(0, !!Joysticks.Size()); if (it != nullptr) it->SetValue(0, !!Joysticks.Size());
@ -186,12 +186,12 @@ void UpdateJoystickMenu(IJoystickConfig *selected)
{ {
opt->mSelectedItem = opt->mItems.Size() - 1; opt->mSelectedItem = opt->mItems.Size() - 1;
} }
opt->CalcIndent(); //opt->CalcIndent();
// If the joystick config menu is open, close it if the device it's open for is gone. // If the joystick config menu is open, close it if the device it's open for is gone.
if (DMenu::CurrentMenu != nullptr && (DMenu::CurrentMenu->IsKindOf("JoystickConfigMenu"))) if (CurrentMenu != nullptr && (CurrentMenu->IsKindOf("JoystickConfigMenu")))
{ {
auto p = DMenu::CurrentMenu->PointerVar<IJoystickConfig>("mJoy"); auto p = CurrentMenu->PointerVar<IJoystickConfig>("mJoy");
if (p != nullptr) if (p != nullptr)
{ {
unsigned i; unsigned i;
@ -204,7 +204,7 @@ void UpdateJoystickMenu(IJoystickConfig *selected)
} }
if (i == Joysticks.Size()) if (i == Joysticks.Size())
{ {
DMenu::CurrentMenu->Close(); CurrentMenu->Close();
} }
} }
} }

View file

@ -1,281 +0,0 @@
/*
** 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, false, false)
IMPLEMENT_POINTERS_START(DListMenu)
IMPLEMENT_POINTER(mFocusControl)
IMPLEMENT_POINTERS_END
//=============================================================================
//
//
//
//=============================================================================
DListMenu::DListMenu(DMenu *parent, DListMenuDescriptor *desc)
: DMenu(parent)
{
mDesc = NULL;
if (desc != NULL) Init(parent, desc);
}
//=============================================================================
//
//
//
//=============================================================================
void DListMenu::Init(DMenu *parent, DListMenuDescriptor *desc)
{
mParentMenu = parent;
GC::WriteBarrier(this, parent);
mDesc = desc;
if (desc->mCenter)
{
int center = 160;
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
int xpos = mDesc->mItems[i]->GetX();
int width = mDesc->mItems[i]->GetWidth();
int curx = mDesc->mSelectOfsX;
if (width > 0 && mDesc->mItems[i]->Selectable())
{
int left = 160 - (width - curx) / 2 - curx;
if (left < center) center = left;
}
}
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
int width = mDesc->mItems[i]->GetWidth();
if (width > 0)
{
mDesc->mItems[i]->SetX(center);
}
}
}
}
//=============================================================================
//
//
//
//=============================================================================
DMenuItemBase *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 oldSelect = mDesc->mSelectedItem;
int startedAt = mDesc->mSelectedItem;
if (startedAt < 0) startedAt = 0;
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);
if (mDesc->mSelectedItem == startedAt) mDesc->mSelectedItem = oldSelect;
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);
if (mDesc->mSelectedItem == startedAt) mDesc->mSelectedItem = oldSelect;
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 ((int)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 == (int)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
//
//=============================================================================
IMPLEMENT_CLASS(DMenuItemBase, false, false)

File diff suppressed because it is too large Load diff

View file

@ -63,24 +63,24 @@
CVAR (Float, mouse_sensitivity, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Float, mouse_sensitivity, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, show_messages, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, show_messages, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, show_obituaries, true, CVAR_ARCHIVE) CVAR (Bool, show_obituaries, true, CVAR_ARCHIVE)
CVAR(Bool, m_showinputgrid, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Bool, m_blockcontrollers, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR (Float, snd_menuvolume, 0.6f, CVAR_ARCHIVE) CVAR (Float, snd_menuvolume, 0.6f, CVAR_ARCHIVE)
CVAR(Int, m_use_mouse, 2, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Int, m_use_mouse, 2, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR(Int, m_show_backbutton, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Int, m_show_backbutton, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
DMenu *DMenu::CurrentMenu;
DEFINE_ACTION_FUNCTION(DMenu, GetCurrentMenu) DEFINE_ACTION_FUNCTION(DMenu, GetCurrentMenu)
{ {
ACTION_RETURN_OBJECT(DMenu::CurrentMenu); ACTION_RETURN_OBJECT(CurrentMenu);
} }
int DMenu::MenuTime;
DEFINE_ACTION_FUNCTION(DMenu, MenuTime) DEFINE_ACTION_FUNCTION(DMenu, MenuTime)
{ {
ACTION_RETURN_INT(DMenu::MenuTime); ACTION_RETURN_INT(MenuTime);
} }
FGameStartup GameStartupInfo; FGameStartup GameStartupInfo;
@ -92,8 +92,12 @@ bool MenuButtonOrigin[NUM_MKEYS];
int BackbuttonTime; int BackbuttonTime;
float BackbuttonAlpha; float BackbuttonAlpha;
static bool MenuEnabled = true; static bool MenuEnabled = true;
DMenu *CurrentMenu;
int MenuTime;
void M_InitVideoModes(); void M_InitVideoModes();
extern PClass *DefaultListMenuClass;
extern PClass *DefaultOptionMenuClass;
#define KEY_REPEAT_DELAY (TICRATE*5/12) #define KEY_REPEAT_DELAY (TICRATE*5/12)
@ -138,7 +142,7 @@ void M_MarkMenus()
{ {
GC::Mark(pair->Value); GC::Mark(pair->Value);
} }
GC::Mark(DMenu::CurrentMenu); GC::Mark(CurrentMenu);
} }
//============================================================================ //============================================================================
// //
@ -157,56 +161,15 @@ DMenu::DMenu(DMenu *parent)
mParentMenu = parent; mParentMenu = parent;
mMouseCapture = false; mMouseCapture = false;
mBackbuttonSelected = false; mBackbuttonSelected = false;
DontDim = false;
GC::WriteBarrier(this, parent); GC::WriteBarrier(this, parent);
} }
bool DMenu::Responder (event_t *ev) //=============================================================================
{ //
bool res = false; //
if (ev->type == EV_GUI_Event) //
{ //=============================================================================
if (ev->subtype == EV_GUI_LButtonDown)
{
res = MouseEventBack(MOUSE_Click, ev->data1, ev->data2);
// make the menu's mouse handler believe that the current coordinate is outside the valid range
if (res) ev->data2 = -1;
res |= CallMouseEvent(MOUSE_Click, ev->data1, ev->data2);
if (res)
{
SetCapture();
}
}
else if (ev->subtype == EV_GUI_MouseMove)
{
BackbuttonTime = BACKBUTTON_TIME;
if (mMouseCapture || m_use_mouse == 1)
{
res = MouseEventBack(MOUSE_Move, ev->data1, ev->data2);
if (res) ev->data2 = -1;
res |= CallMouseEvent(MOUSE_Move, ev->data1, ev->data2);
}
}
else if (ev->subtype == EV_GUI_LButtonUp)
{
if (mMouseCapture)
{
ReleaseCapture();
res = MouseEventBack(MOUSE_Release, ev->data1, ev->data2);
if (res) ev->data2 = -1;
res |= CallMouseEvent(MOUSE_Release, ev->data1, ev->data2);
}
}
}
return false;
}
DEFINE_ACTION_FUNCTION(DMenu, Responder)
{
PARAM_SELF_PROLOGUE(DMenu);
PARAM_POINTER(ev, event_t);
ACTION_RETURN_BOOL(self->Responder(ev));
}
bool DMenu::CallResponder(event_t *ev) bool DMenu::CallResponder(event_t *ev)
{ {
@ -218,7 +181,7 @@ bool DMenu::CallResponder(event_t *ev)
GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr);
return !!retval; return !!retval;
} }
else return Responder(ev); else return false;
} }
//============================================================================= //=============================================================================
@ -227,29 +190,6 @@ bool DMenu::CallResponder(event_t *ev)
// //
//============================================================================= //=============================================================================
bool DMenu::MenuEvent (int mkey, bool fromcontroller)
{
switch (mkey)
{
case MKEY_Back:
{
Close();
S_Sound (CHAN_VOICE | CHAN_UI,
DMenu::CurrentMenu != nullptr? "menu/backup" : "menu/clear", snd_menuvolume, ATTN_NONE);
return true;
}
}
return false;
}
DEFINE_ACTION_FUNCTION(DMenu, MenuEvent)
{
PARAM_SELF_PROLOGUE(DMenu);
PARAM_INT(key);
PARAM_BOOL(fromcontroller);
ACTION_RETURN_BOOL(self->MenuEvent(key, fromcontroller));
}
bool DMenu::CallMenuEvent(int mkey, bool fromcontroller) bool DMenu::CallMenuEvent(int mkey, bool fromcontroller)
{ {
IFVIRTUAL(DMenu, MenuEvent) IFVIRTUAL(DMenu, MenuEvent)
@ -260,7 +200,7 @@ bool DMenu::CallMenuEvent(int mkey, bool fromcontroller)
GlobalVMStack.Call(func, params, 3, &ret, 1, nullptr); GlobalVMStack.Call(func, params, 3, &ret, 1, nullptr);
return !!retval; return !!retval;
} }
else return MenuEvent(mkey, fromcontroller); else return false;
} }
//============================================================================= //=============================================================================
// //
@ -268,14 +208,24 @@ bool DMenu::CallMenuEvent(int mkey, bool fromcontroller)
// //
//============================================================================= //=============================================================================
DEFINE_ACTION_FUNCTION(DMenu, SetMouseCapture)
{
PARAM_PROLOGUE;
PARAM_BOOL(on);
if (on) I_SetMouseCapture();
else I_ReleaseMouseCapture();
return 0;
}
void DMenu::Close () void DMenu::Close ()
{ {
assert(DMenu::CurrentMenu == this); if (CurrentMenu == nullptr) return; // double closing can happen in the save menu.
DMenu::CurrentMenu = mParentMenu; assert(CurrentMenu == this);
CurrentMenu = mParentMenu;
Destroy(); Destroy();
if (DMenu::CurrentMenu != nullptr) if (CurrentMenu != nullptr)
{ {
GC::WriteBarrier(DMenu::CurrentMenu); GC::WriteBarrier(CurrentMenu);
} }
else else
{ {
@ -283,157 +233,6 @@ void DMenu::Close ()
} }
} }
//=============================================================================
//
//
//
//=============================================================================
bool DMenu::MouseEvent(int type, int x, int y)
{
return true;
}
DEFINE_ACTION_FUNCTION(DMenu, MouseEvent)
{
PARAM_SELF_PROLOGUE(DMenu);
PARAM_INT(type);
PARAM_INT(x);
PARAM_INT(y);
ACTION_RETURN_BOOL(self->MouseEvent(type, x, y));
}
bool DMenu::CallMouseEvent(int type, int x, int y)
{
IFVIRTUAL(DMenu, MouseEvent)
{
VMValue params[] = { (DObject*)this, type, x, y };
int retval;
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, 4, &ret, 1, nullptr);
return !!retval;
}
else return MouseEvent (type, x, y);
}
//=============================================================================
//
//
//
//=============================================================================
bool DMenu::MouseEventBack(int type, int x, int y)
{
if (m_show_backbutton >= 0)
{
FTexture *tex = TexMan(gameinfo.mBackButton);
if (tex != nullptr)
{
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 >= 0 && y < tex->GetScaledHeight() * CleanYfac);
if (mBackbuttonSelected && type == MOUSE_Release)
{
if (m_use_mouse == 2) mBackbuttonSelected = false;
CallMenuEvent(MKEY_Back, true);
}
return mBackbuttonSelected;
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
void DMenu::SetCapture()
{
if (!mMouseCapture)
{
mMouseCapture = true;
I_SetMouseCapture();
}
}
void DMenu::ReleaseCapture()
{
if (mMouseCapture)
{
mMouseCapture = false;
I_ReleaseMouseCapture();
}
}
//=============================================================================
//
//
//
//=============================================================================
void DMenu::Ticker ()
{
}
DEFINE_ACTION_FUNCTION(DMenu, Ticker)
{
PARAM_SELF_PROLOGUE(DMenu);
self->Ticker();
return 0;
}
void DMenu::CallTicker()
{
IFVIRTUAL(DMenu, Ticker)
{
VMValue params[] = { (DObject*)this };
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
}
else 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);
}
}
}
DEFINE_ACTION_FUNCTION(DMenu, Drawer)
{
PARAM_SELF_PROLOGUE(DMenu);
self->Drawer();
return 0;
}
void DMenu::CallDrawer()
{
IFVIRTUAL(DMenu, Drawer)
{
VMValue params[] = { (DObject*)this };
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
}
else Drawer();
}
DEFINE_ACTION_FUNCTION(DMenu, Close) DEFINE_ACTION_FUNCTION(DMenu, Close)
{ {
PARAM_SELF_PROLOGUE(DMenu); PARAM_SELF_PROLOGUE(DMenu);
@ -441,24 +240,29 @@ DEFINE_ACTION_FUNCTION(DMenu, Close)
return 0; return 0;
} }
DEFINE_ACTION_FUNCTION(DMenu, GetItem) //=============================================================================
{ //
PARAM_SELF_PROLOGUE(DMenu); //
PARAM_NAME(name); //
ACTION_RETURN_OBJECT(self->GetItem(name)); //=============================================================================
}
DEFINE_ACTION_FUNCTION(DOptionMenuDescriptor, GetItem) void DMenu::CallTicker()
{ {
PARAM_SELF_PROLOGUE(DOptionMenuDescriptor); IFVIRTUAL(DMenu, Ticker)
PARAM_NAME(name); {
ACTION_RETURN_OBJECT(self->GetItem(name)); VMValue params[] = { (DObject*)this };
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
}
} }
bool DMenu::DimAllowed() void DMenu::CallDrawer()
{ {
return true; IFVIRTUAL(DMenu, Drawer)
{
VMValue params[] = { (DObject*)this };
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
}
} }
bool DMenu::TranslateKeyboardEvents() bool DMenu::TranslateKeyboardEvents()
@ -483,7 +287,7 @@ bool DMenu::TranslateKeyboardEvents()
void M_StartControlPanel (bool makeSound) void M_StartControlPanel (bool makeSound)
{ {
// intro might call this repeatedly // intro might call this repeatedly
if (DMenu::CurrentMenu != nullptr) if (CurrentMenu != nullptr)
return; return;
ResetButtonStates (); ResetButtonStates ();
@ -515,9 +319,13 @@ void M_StartControlPanel (bool makeSound)
void M_ActivateMenu(DMenu *menu) void M_ActivateMenu(DMenu *menu)
{ {
if (menuactive == MENU_Off) menuactive = MENU_On; if (menuactive == MENU_Off) menuactive = MENU_On;
if (DMenu::CurrentMenu != nullptr) DMenu::CurrentMenu->ReleaseCapture(); if (CurrentMenu != nullptr && CurrentMenu->mMouseCapture)
DMenu::CurrentMenu = menu; {
GC::WriteBarrier(DMenu::CurrentMenu); CurrentMenu->mMouseCapture = false;
I_ReleaseMouseCapture();
}
CurrentMenu = menu;
GC::WriteBarrier(CurrentMenu);
} }
DEFINE_ACTION_FUNCTION(DMenu, ActivateMenu) DEFINE_ACTION_FUNCTION(DMenu, ActivateMenu)
@ -599,6 +407,16 @@ void M_SetMenu(FName menu, int param)
M_InitVideoModes(); M_InitVideoModes();
break; break;
case NAME_Quitmenu:
// The separate menu class no longer exists but the name still needs support for existing mods.
C_DoCommand("menu_quit");
return;
case NAME_EndGameMenu:
// The separate menu class no longer exists but the name still needs support for existing mods.
void ActivateEndGameMenu();
ActivateEndGameMenu();
return;
} }
// End of special checks // End of special checks
@ -622,22 +440,30 @@ void M_SetMenu(FName menu, int param)
} }
else else
{ {
const PClass *cls = ld->mClass == nullptr? RUNTIME_CLASS(DListMenu) : ld->mClass; PClass *cls = ld->mClass;
if (cls == nullptr) cls = DefaultListMenuClass;
if (cls == nullptr) cls = PClass::FindClass("ListMenu");
DListMenu *newmenu = (DListMenu *)cls->CreateNew(); DMenu *newmenu = (DMenu *)cls->CreateNew();
newmenu->Init(DMenu::CurrentMenu, ld); IFVIRTUALPTRNAME(newmenu, "ListMenu", Init)
{
VMValue params[3] = { newmenu, CurrentMenu, ld };
GlobalVMStack.Call(func, params, 3, nullptr, 0);
}
M_ActivateMenu(newmenu); M_ActivateMenu(newmenu);
} }
} }
else if ((*desc)->IsKindOf(RUNTIME_CLASS(DOptionMenuDescriptor))) else if ((*desc)->IsKindOf(RUNTIME_CLASS(DOptionMenuDescriptor)))
{ {
DOptionMenuDescriptor *ld = static_cast<DOptionMenuDescriptor*>(*desc); DOptionMenuDescriptor *ld = static_cast<DOptionMenuDescriptor*>(*desc);
const PClass *cls = ld->mClass == nullptr? PClass::FindClass("OptionMenu") : ld->mClass; PClass *cls = ld->mClass;
if (cls == nullptr) cls = DefaultOptionMenuClass;
if (cls == nullptr) cls = PClass::FindClass("OptionMenu");
DMenu *newmenu = (DMenu*)cls->CreateNew(); DMenu *newmenu = (DMenu*)cls->CreateNew();
IFVIRTUALPTRNAME(newmenu, "OptionMenu", Init) IFVIRTUALPTRNAME(newmenu, "OptionMenu", Init)
{ {
VMValue params[3] = { newmenu, DMenu::CurrentMenu, ld }; VMValue params[3] = { newmenu, CurrentMenu, ld };
GlobalVMStack.Call(func, params, 3, nullptr, 0); GlobalVMStack.Call(func, params, 3, nullptr, 0);
} }
M_ActivateMenu(newmenu); M_ActivateMenu(newmenu);
@ -646,13 +472,18 @@ void M_SetMenu(FName menu, int param)
} }
else else
{ {
const PClass *menuclass = PClass::FindClass(menu); PClass *menuclass = PClass::FindClass(menu);
if (menuclass != nullptr) if (menuclass != nullptr)
{ {
if (menuclass->IsDescendantOf(RUNTIME_CLASS(DMenu))) if (menuclass->IsDescendantOf("GenericMenu"))
{ {
DMenu *newmenu = (DMenu*)menuclass->CreateNew(); DMenu *newmenu = (DMenu*)menuclass->CreateNew();
newmenu->mParentMenu = DMenu::CurrentMenu;
IFVIRTUALPTRNAME(newmenu, "GenericMenu", Init)
{
VMValue params[3] = { newmenu, CurrentMenu };
GlobalVMStack.Call(func, params, 2, nullptr, 0);
}
M_ActivateMenu(newmenu); M_ActivateMenu(newmenu);
return; return;
} }
@ -666,7 +497,7 @@ DEFINE_ACTION_FUNCTION(DMenu, SetMenu)
{ {
PARAM_PROLOGUE; PARAM_PROLOGUE;
PARAM_NAME(menu); PARAM_NAME(menu);
PARAM_INT(mparam); PARAM_INT_DEF(mparam);
M_SetMenu(menu, mparam); M_SetMenu(menu, mparam);
return 0; return 0;
} }
@ -688,7 +519,7 @@ bool M_Responder (event_t *ev)
return false; return false;
} }
if (DMenu::CurrentMenu != nullptr && menuactive != MENU_Off) if (CurrentMenu != nullptr && menuactive != MENU_Off)
{ {
// There are a few input sources we are interested in: // There are a few input sources we are interested in:
// //
@ -723,9 +554,9 @@ bool M_Responder (event_t *ev)
} }
// pass everything else on to the current menu // pass everything else on to the current menu
return DMenu::CurrentMenu->CallResponder(ev); return CurrentMenu->CallResponder(ev);
} }
else if (DMenu::CurrentMenu->TranslateKeyboardEvents()) else if (CurrentMenu->TranslateKeyboardEvents())
{ {
ch = ev->data1; ch = ev->data1;
keyup = ev->subtype == EV_GUI_KeyUp; keyup = ev->subtype == EV_GUI_KeyUp;
@ -744,7 +575,7 @@ bool M_Responder (event_t *ev)
default: default:
if (!keyup) if (!keyup)
{ {
return DMenu::CurrentMenu->CallResponder(ev); return CurrentMenu->CallResponder(ev);
} }
break; break;
} }
@ -752,6 +583,9 @@ bool M_Responder (event_t *ev)
} }
else if (menuactive != MENU_WaitKey && (ev->type == EV_KeyDown || ev->type == EV_KeyUp)) else if (menuactive != MENU_WaitKey && (ev->type == EV_KeyDown || ev->type == EV_KeyUp))
{ {
// eat blocked controller events without dispatching them.
if (ev->data1 >= KEY_FIRSTJOYBUTTON && m_blockcontrollers) return true;
keyup = ev->type == EV_KeyUp; keyup = ev->type == EV_KeyUp;
ch = ev->data1; ch = ev->data1;
@ -827,11 +661,11 @@ bool M_Responder (event_t *ev)
{ {
MenuButtonTickers[mkey] = KEY_REPEAT_DELAY; MenuButtonTickers[mkey] = KEY_REPEAT_DELAY;
} }
DMenu::CurrentMenu->CallMenuEvent(mkey, fromcontroller); CurrentMenu->CallMenuEvent(mkey, fromcontroller);
return true; return true;
} }
} }
return DMenu::CurrentMenu->CallResponder(ev) || !keyup; return CurrentMenu->CallResponder(ev) || !keyup;
} }
else if (MenuEnabled) else if (MenuEnabled)
{ {
@ -872,10 +706,10 @@ bool M_Responder (event_t *ev)
void M_Ticker (void) void M_Ticker (void)
{ {
DMenu::MenuTime++; MenuTime++;
if (DMenu::CurrentMenu != nullptr && menuactive != MENU_Off) if (CurrentMenu != nullptr && menuactive != MENU_Off)
{ {
DMenu::CurrentMenu->CallTicker(); CurrentMenu->CallTicker();
for (int i = 0; i < NUM_MKEYS; ++i) for (int i = 0; i < NUM_MKEYS; ++i)
{ {
@ -884,7 +718,7 @@ void M_Ticker (void)
if (MenuButtonTickers[i] > 0 && --MenuButtonTickers[i] <= 0) if (MenuButtonTickers[i] > 0 && --MenuButtonTickers[i] <= 0)
{ {
MenuButtonTickers[i] = KEY_REPEAT_RATE; MenuButtonTickers[i] = KEY_REPEAT_RATE;
DMenu::CurrentMenu->CallMenuEvent(i, MenuButtonOrigin[i]); CurrentMenu->CallMenuEvent(i, MenuButtonOrigin[i]);
} }
} }
} }
@ -924,14 +758,14 @@ void M_Drawer (void)
} }
if (DMenu::CurrentMenu != nullptr && menuactive != MENU_Off) if (CurrentMenu != nullptr && menuactive != MENU_Off)
{ {
if (DMenu::CurrentMenu->DimAllowed()) if (!CurrentMenu->DontDim)
{ {
screen->Dim(fade); screen->Dim(fade);
V_SetBorderNeedRefresh(); V_SetBorderNeedRefresh();
} }
DMenu::CurrentMenu->CallDrawer(); CurrentMenu->CallDrawer();
} }
} }
@ -941,13 +775,14 @@ void M_Drawer (void)
// //
//============================================================================= //=============================================================================
void M_ClearMenus () void M_ClearMenus()
{ {
M_DemoNoPlay = false; M_DemoNoPlay = false;
if (DMenu::CurrentMenu != nullptr) while (CurrentMenu != nullptr)
{ {
DMenu::CurrentMenu->Destroy(); DMenu* parent = CurrentMenu->mParentMenu;
DMenu::CurrentMenu = nullptr; CurrentMenu->Destroy();
CurrentMenu = parent;
} }
V_SetBorderNeedRefresh(); V_SetBorderNeedRefresh();
menuactive = MENU_Off; menuactive = MENU_Off;
@ -1185,11 +1020,11 @@ CCMD(reset2saved)
// This really should be in the script but we can't do scripted CCMDs yet. // This really should be in the script but we can't do scripted CCMDs yet.
CCMD(undocolorpic) CCMD(undocolorpic)
{ {
if (DMenu::CurrentMenu != NULL) if (CurrentMenu != NULL)
{ {
IFVIRTUALPTR(DMenu::CurrentMenu, DMenu, ResetColor) IFVIRTUALPTR(CurrentMenu, DMenu, ResetColor)
{ {
VMValue params[] = { (DObject*)DMenu::CurrentMenu }; VMValue params[] = { (DObject*)CurrentMenu };
GlobalVMStack.Call(func, params, countof(params), nullptr, 0, nullptr); GlobalVMStack.Call(func, params, countof(params), nullptr, 0, nullptr);
} }
} }
@ -1199,6 +1034,9 @@ CCMD(undocolorpic)
DEFINE_FIELD(DMenu, mParentMenu) DEFINE_FIELD(DMenu, mParentMenu)
DEFINE_FIELD(DMenu, mMouseCapture);
DEFINE_FIELD(DMenu, mBackbuttonSelected);
DEFINE_FIELD(DMenu, DontDim);
DEFINE_FIELD(DMenuDescriptor, mMenuName) DEFINE_FIELD(DMenuDescriptor, mMenuName)
DEFINE_FIELD(DMenuDescriptor, mNetgameMessage) DEFINE_FIELD(DMenuDescriptor, mNetgameMessage)
@ -1209,9 +1047,6 @@ DEFINE_FIELD(DMenuItemBase, mYpos)
DEFINE_FIELD(DMenuItemBase, mAction) DEFINE_FIELD(DMenuItemBase, mAction)
DEFINE_FIELD(DMenuItemBase, mEnabled) DEFINE_FIELD(DMenuItemBase, mEnabled)
DEFINE_FIELD(DListMenu, mDesc)
DEFINE_FIELD(DListMenu, mFocusControl)
DEFINE_FIELD(DListMenuDescriptor, mItems) DEFINE_FIELD(DListMenuDescriptor, mItems)
DEFINE_FIELD(DListMenuDescriptor, mSelectedItem) DEFINE_FIELD(DListMenuDescriptor, mSelectedItem)
DEFINE_FIELD(DListMenuDescriptor, mSelectOfsX) DEFINE_FIELD(DListMenuDescriptor, mSelectOfsX)
@ -1291,7 +1126,7 @@ DMenuItemBase * CreateOptionMenuItemControl(const char *label, FName cmd, FKeyBi
return (DMenuItemBase*)p; return (DMenuItemBase*)p;
} }
DMenuItemBase * CreateListMenuItemPatch(int x, int y, int height, int hotkey, FTextureID tex, FName command, int param) DMenuItemBase * CreateListMenuItemPatch(double x, double y, int height, int hotkey, FTextureID tex, FName command, int param)
{ {
auto c = PClass::FindClass("ListMenuItemPatchItem"); auto c = PClass::FindClass("ListMenuItemPatchItem");
auto p = c->CreateNew(); auto p = c->CreateNew();
@ -1301,7 +1136,7 @@ DMenuItemBase * CreateListMenuItemPatch(int x, int y, int height, int hotkey, FT
return (DMenuItemBase*)p; return (DMenuItemBase*)p;
} }
DMenuItemBase * CreateListMenuItemText(int x, int y, int height, int hotkey, const char *text, FFont *font, PalEntry color1, PalEntry color2, FName command, int param) DMenuItemBase * CreateListMenuItemText(double x, double y, int height, int hotkey, const char *text, FFont *font, PalEntry color1, PalEntry color2, FName command, int param)
{ {
auto c = PClass::FindClass("ListMenuItemTextItem"); auto c = PClass::FindClass("ListMenuItemTextItem");
auto p = c->CreateNew(); auto p = c->CreateNew();
@ -1311,50 +1146,6 @@ DMenuItemBase * CreateListMenuItemText(int x, int y, int height, int hotkey, con
return (DMenuItemBase*)p; return (DMenuItemBase*)p;
} }
bool DMenuItemBase::CheckCoordinate(int x, int y)
{
IFVIRTUAL(DMenuItemBase, CheckCoordinate)
{
VMValue params[] = { (DObject*)this, x, y };
int retval;
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr);
return !!retval;
}
return false;
}
void DMenuItemBase::Ticker()
{
IFVIRTUAL(DMenuItemBase, Ticker)
{
VMValue params[] = { (DObject*)this };
GlobalVMStack.Call(func, params, countof(params), nullptr, 0, nullptr);
}
}
void DMenuItemBase::Drawer(bool selected)
{
IFVIRTUAL(DMenuItemBase, Drawer)
{
VMValue params[] = { (DObject*)this, selected };
GlobalVMStack.Call(func, params, countof(params), nullptr, 0, nullptr);
}
}
bool DMenuItemBase::Selectable()
{
IFVIRTUAL(DMenuItemBase, Selectable)
{
VMValue params[] = { (DObject*)this };
int retval;
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr);
return !!retval;
}
return false;
}
bool DMenuItemBase::Activate() bool DMenuItemBase::Activate()
{ {
IFVIRTUAL(DMenuItemBase, Activate) IFVIRTUAL(DMenuItemBase, Activate)
@ -1367,18 +1158,6 @@ bool DMenuItemBase::Activate()
} }
return false; return false;
} }
FName DMenuItemBase::GetAction(int *pparam)
{
IFVIRTUAL(DMenuItemBase, GetAction)
{
VMValue params[] = { (DObject*)this };
int retval[2];
VMReturn ret[2]; ret[0].IntAt(&retval[0]); ret[1].IntAt(&retval[1]);
GlobalVMStack.Call(func, params, countof(params), ret, 2, nullptr);
return ENamedName(retval[0]);
}
return NAME_None;
}
bool DMenuItemBase::SetString(int i, const char *s) bool DMenuItemBase::SetString(int i, const char *s)
{ {
@ -1436,112 +1215,4 @@ bool DMenuItemBase::GetValue(int i, int *pvalue)
return false; return false;
} }
IMPLEMENT_CLASS(DMenuItemBase, false, false)
void DMenuItemBase::Enable(bool on)
{
IFVIRTUAL(DMenuItemBase, Enable)
{
VMValue params[] = { (DObject*)this, on };
GlobalVMStack.Call(func, params, countof(params), nullptr, 0, nullptr);
}
}
bool DMenuItemBase::MenuEvent(int mkey, bool fromcontroller)
{
IFVIRTUAL(DMenuItemBase, MenuEvent)
{
VMValue params[] = { (DObject*)this, mkey, fromcontroller };
int retval;
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr);
return !!retval;
}
return false;
}
bool DMenuItemBase::MouseEvent(int type, int x, int y)
{
IFVIRTUAL(DMenuItemBase, MouseEvent)
{
VMValue params[] = { (DObject*)this, type, x, y };
int retval;
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr);
return !!retval;
}
return false;
}
bool DMenuItemBase::CheckHotkey(int c)
{
IFVIRTUAL(DMenuItemBase, CheckHotkey)
{
VMValue params[] = { (DObject*)this, c };
int retval;
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr);
return !!retval;
}
return false;
}
int DMenuItemBase::GetWidth()
{
IFVIRTUAL(DMenuItemBase, GetWidth)
{
VMValue params[] = { (DObject*)this };
int retval;
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr);
return retval;
}
return false;
}
int DMenuItemBase::GetIndent()
{
IFVIRTUAL(DMenuItemBase, GetIndent)
{
VMValue params[] = { (DObject*)this };
int retval;
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr);
return retval;
}
return false;
}
int DMenuItemBase::Draw(DOptionMenuDescriptor *desc, int y, int indent, bool selected)
{
IFVIRTUAL(DMenuItemBase, Draw)
{
VMValue params[] = { (DObject*)this, desc, y, indent, selected };
int retval;
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr);
return retval;
}
return false;
}
void DMenuItemBase::DrawSelector(int xofs, int yofs, FTextureID tex)
{
if (tex.isNull())
{
if ((DMenu::MenuTime % 8) < 6)
{
screen->DrawText(ConFont, OptionSettings.mFontColorSelection,
(mXpos + xofs - 160) * CleanXfac + screen->GetWidth() / 2,
(mYpos + yofs - 100) * CleanYfac + screen->GetHeight() / 2,
"\xd",
DTA_CellX, 8 * CleanXfac,
DTA_CellY, 8 * CleanYfac,
TAG_DONE);
}
}
else
{
screen->DrawTexture(TexMan(tex), mXpos + xofs, mYpos + yofs, DTA_Clean, true, TAG_DONE);
}
}

View file

@ -9,7 +9,6 @@
#include "r_data/r_translate.h" #include "r_data/r_translate.h"
#include "c_cvars.h" #include "c_cvars.h"
#include "v_font.h" #include "v_font.h"
#include "version.h"
#include "textures/textures.h" #include "textures/textures.h"
EXTERN_CVAR(Float, snd_menuvolume) EXTERN_CVAR(Float, snd_menuvolume)
@ -58,46 +57,58 @@ extern FGameStartup GameStartupInfo;
struct FSaveGameNode struct FSaveGameNode
{ {
char Title[SAVESTRINGSIZE]; FString SaveTitle;
FString Filename; FString Filename;
bool bOldVersion; bool bOldVersion = false;
bool bMissingWads; bool bMissingWads = false;
bool bNoDelete; bool bNoDelete = false;
FSaveGameNode() { bNoDelete = false; }
}; };
struct SavegameManager struct FSavegameManager
{ {
private:
TArray<FSaveGameNode*> SaveGames; TArray<FSaveGameNode*> SaveGames;
FSaveGameNode NewSaveNode;
int LastSaved = -1; int LastSaved = -1;
int LastAccessed = -1; int LastAccessed = -1;
int WindowSize = 0;
FSaveGameNode *quickSaveSlot = nullptr;
FileReader *currentSavePic = nullptr; FileReader *currentSavePic = nullptr;
TArray<char> SavePicData; TArray<char> SavePicData;
FTexture *SavePic = nullptr; FTexture *SavePic = nullptr;
FBrokenLines *SaveComment = nullptr; FBrokenLines *SaveComment = nullptr;
void ClearSaveGames(); public:
int WindowSize = 0;
FSaveGameNode *quickSaveSlot = nullptr;
~FSavegameManager();
private:
int InsertSaveNode(FSaveGameNode *node); int InsertSaveNode(FSaveGameNode *node);
int RemoveSaveSlot(int index); public:
void NotifyNewSave(const FString &file, const FString &title, bool okForQuicksave);
void ClearSaveGames();
void ReadSaveStrings(); void ReadSaveStrings();
void NotifyNewSave(const char *file, const char *title, bool okForQuicksave); void UnloadSaveData();
int RemoveSaveSlot(int index);
void LoadSavegame(int Selected); void LoadSavegame(int Selected);
void DoSave(int Selected, const char *savegamestring); void DoSave(int Selected, const char *savegamestring);
void DeleteEntry(int Selected); unsigned ExtractSaveData(int index);
void ExtractSaveData(int index);
void UnloadSaveData();
void ClearSaveStuff(); void ClearSaveStuff();
bool DrawSavePic(int x, int y, int w, int h); bool DrawSavePic(int x, int y, int w, int h);
void DrawSaveComment(FFont *font, int cr, int x, int y, int scalefactor);
void SetFileInfo(int Selected); void SetFileInfo(int Selected);
unsigned SavegameCount();
FSaveGameNode *GetSavegame(int i);
void InsertNewSaveNode();
bool RemoveNewSaveNode();
}; };
extern SavegameManager savegameManager; extern FSavegameManager savegameManager;
class DMenu;
extern DMenu *CurrentMenu;
extern int MenuTime;
//============================================================================= //=============================================================================
// //
@ -112,7 +123,7 @@ class DMenuDescriptor : public DObject
public: public:
FName mMenuName; FName mMenuName;
FString mNetgameMessage; FString mNetgameMessage;
const PClass *mClass; PClass *mClass = nullptr;
virtual size_t PropagateMark() { return 0; } virtual size_t PropagateMark() { return 0; }
}; };
@ -126,11 +137,11 @@ class DListMenuDescriptor : public DMenuDescriptor
public: public:
TArray<DMenuItemBase *> mItems; TArray<DMenuItemBase *> mItems;
int mSelectedItem; int mSelectedItem;
int mSelectOfsX; double mSelectOfsX;
int mSelectOfsY; double mSelectOfsY;
FTextureID mSelector; FTextureID mSelector;
int mDisplayTop; int mDisplayTop;
int mXpos, mYpos; double mXpos, mYpos;
int mWLeft, mWRight; int mWLeft, mWRight;
int mLinespacing; // needs to be stored for dynamically created menus int mLinespacing; // needs to be stored for dynamically created menus
int mAutoselect; // this can only be set by internal menu creation functions int mAutoselect; // this can only be set by internal menu creation functions
@ -241,9 +252,7 @@ class DMenu : public DObject
DECLARE_CLASS (DMenu, DObject) DECLARE_CLASS (DMenu, DObject)
HAS_OBJECT_POINTERS HAS_OBJECT_POINTERS
protected:
bool mMouseCapture;
bool mBackbuttonSelected;
public: public:
enum enum
@ -253,45 +262,19 @@ public:
MOUSE_Release MOUSE_Release
}; };
enum
{
BACKBUTTON_TIME = 4*TICRATE
};
static DMenu *CurrentMenu;
static int MenuTime;
TObjPtr<DMenu> mParentMenu; TObjPtr<DMenu> mParentMenu;
bool mMouseCapture;
bool mBackbuttonSelected;
bool DontDim;
DMenu(DMenu *parent = NULL); 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 ();
bool TranslateKeyboardEvents(); bool TranslateKeyboardEvents();
virtual void Close(); virtual void Close();
virtual bool MouseEvent(int type, int x, int y);
virtual void SetFocus(DMenuItemBase *fc) {}
virtual bool CheckFocus(DMenuItemBase *fc) { return false; }
virtual void ReleaseFocus() {}
virtual DMenuItemBase *GetItem(FName name) { return nullptr; }
bool CallResponder(event_t *ev); bool CallResponder(event_t *ev);
bool CallMenuEvent(int mkey, bool fromcontroller); bool CallMenuEvent(int mkey, bool fromcontroller);
bool CallMouseEvent(int type, int x, int y);
void CallTicker(); void CallTicker();
void CallDrawer(); void CallDrawer();
bool MouseEventBack(int type, int x, int y);
void SetCapture();
void ReleaseCapture();
bool HasCapture()
{
return mMouseCapture;
}
}; };
//============================================================================= //=============================================================================
@ -304,74 +287,19 @@ class DMenuItemBase : public DObject
{ {
DECLARE_CLASS(DMenuItemBase, DObject) DECLARE_CLASS(DMenuItemBase, DObject)
public: public:
int mXpos, mYpos; double mXpos, mYpos;
FNameNoInit mAction; FNameNoInit mAction;
bool mEnabled; bool mEnabled;
bool CheckCoordinate(int x, int y);
void Ticker();
void Drawer(bool selected);
bool Selectable();
bool Activate(); bool Activate();
FName GetAction(int *pparam);
bool SetString(int i, const char *s); bool SetString(int i, const char *s);
bool GetString(int i, char *s, int len); bool GetString(int i, char *s, int len);
bool SetValue(int i, int value); bool SetValue(int i, int value);
bool GetValue(int i, int *pvalue); bool GetValue(int i, int *pvalue);
void Enable(bool on);
bool MenuEvent (int mkey, bool fromcontroller);
bool MouseEvent(int type, int x, int y);
bool CheckHotkey(int c);
int GetWidth();
int GetIndent();
int Draw(DOptionMenuDescriptor *desc, int y, int indent, bool selected);
void OffsetPositionY(int ydelta) { mYpos += ydelta; } void OffsetPositionY(int ydelta) { mYpos += ydelta; }
int GetY() { return mYpos; } double GetY() { return mYpos; }
int GetX() { return mXpos; }
void SetX(int x) { mXpos = x; }
void DrawSelector(int xofs, int yofs, FTextureID tex);
}; };
//=============================================================================
//
// list menu class runs a menu described by a DListMenuDescriptor
//
//=============================================================================
class DListMenu : public DMenu
{
DECLARE_CLASS(DListMenu, DMenu)
HAS_OBJECT_POINTERS;
public:
DListMenuDescriptor *mDesc;
DMenuItemBase *mFocusControl;
DListMenu(DMenu *parent = NULL, DListMenuDescriptor *desc = NULL);
virtual void Init(DMenu *parent = NULL, DListMenuDescriptor *desc = NULL);
DMenuItemBase *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(DMenuItemBase *fc)
{
mFocusControl = fc;
}
bool CheckFocus(DMenuItemBase *fc)
{
return mFocusControl == fc;
}
void ReleaseFocus()
{
mFocusControl = NULL;
}
};
//============================================================================= //=============================================================================
// //
// //
@ -396,41 +324,10 @@ extern FOptionMap OptionValues;
//============================================================================= //=============================================================================
// //
// Input some text //
// //
//============================================================================= //=============================================================================
class DTextEnterMenu : public DMenu
{
DECLARE_ABSTRACT_CLASS(DTextEnterMenu, DMenu)
public:
FString 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;
// [TP]
bool AllowColors;
// [TP] Added allowcolors
DTextEnterMenu(DMenu *parent, const char *textbuffer, int maxlen, int sizemode, bool showgrid, bool allowcolors = false);
void Drawer ();
bool MenuEvent (int mkey, bool fromcontroller);
bool Responder(event_t *ev);
bool MouseEvent(int type, int x, int y);
FString GetText();
};
struct event_t; struct event_t;
void M_EnableMenu (bool on) ; void M_EnableMenu (bool on) ;
bool M_Responder (event_t *ev); bool M_Responder (event_t *ev);
@ -442,7 +339,6 @@ void M_ActivateMenu(DMenu *menu);
void M_ClearMenus (); void M_ClearMenus ();
void M_ParseMenuDefs(); void M_ParseMenuDefs();
void M_StartupSkillMenu(FGameStartup *gs); void M_StartupSkillMenu(FGameStartup *gs);
int M_GetDefaultSkill();
void M_StartControlPanel (bool makeSound); void M_StartControlPanel (bool makeSound);
void M_SetMenu(FName menu, int param = -1); void M_SetMenu(FName menu, int param = -1);
void M_StartMessage(const char *message, int messagemode, FName action = NAME_None); void M_StartMessage(const char *message, int messagemode, FName action = NAME_None);
@ -457,7 +353,7 @@ DMenuItemBase * CreateOptionMenuItemStaticText(const char *name, bool v);
DMenuItemBase * CreateOptionMenuItemSubmenu(const char *label, FName cmd, int center); DMenuItemBase * CreateOptionMenuItemSubmenu(const char *label, FName cmd, int center);
DMenuItemBase * CreateOptionMenuItemControl(const char *label, FName cmd, FKeyBindings *bindings); DMenuItemBase * CreateOptionMenuItemControl(const char *label, FName cmd, FKeyBindings *bindings);
DMenuItemBase * CreateOptionMenuItemJoyConfigMenu(const char *label, IJoystickConfig *joy); DMenuItemBase * CreateOptionMenuItemJoyConfigMenu(const char *label, IJoystickConfig *joy);
DMenuItemBase * CreateListMenuItemPatch(int x, int y, int height, int hotkey, FTextureID tex, FName command, int param); DMenuItemBase * CreateListMenuItemPatch(double x, double y, int height, int hotkey, FTextureID tex, FName command, int param);
DMenuItemBase * CreateListMenuItemText(int x, int y, int height, int hotkey, const char *text, FFont *font, PalEntry color1, PalEntry color2, FName command, int param); DMenuItemBase * CreateListMenuItemText(double x, double y, int height, int hotkey, const char *text, FFont *font, PalEntry color1, PalEntry color2, FName command, int param);
#endif #endif

View file

@ -62,6 +62,8 @@ static DOptionMenuDescriptor *DefaultOptionMenuSettings; // contains common sett
FOptionMenuSettings OptionSettings; FOptionMenuSettings OptionSettings;
FOptionMap OptionValues; FOptionMap OptionValues;
bool mustPrintErrors; bool mustPrintErrors;
PClass *DefaultListMenuClass;
PClass *DefaultOptionMenuClass;
void I_BuildALDeviceList(FOptionValues *opt); void I_BuildALDeviceList(FOptionValues *opt);
@ -145,7 +147,7 @@ static void DeinitMenus()
} }
MenuDescriptors.Clear(); MenuDescriptors.Clear();
OptionValues.Clear(); OptionValues.Clear();
DMenu::CurrentMenu = nullptr; CurrentMenu = nullptr;
savegameManager.ClearSaveGames(); savegameManager.ClearSaveGames();
} }
@ -291,8 +293,8 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc)
else if (sc.Compare("Class")) else if (sc.Compare("Class"))
{ {
sc.MustGetString(); sc.MustGetString();
const PClass *cls = PClass::FindClass(sc.String); PClass *cls = PClass::FindClass(sc.String);
if (cls == nullptr || !cls->IsDescendantOf(RUNTIME_CLASS(DListMenu))) if (cls == nullptr || !cls->IsDescendantOf("ListMenu"))
{ {
sc.ScriptError("Unknown menu class '%s'", sc.String); sc.ScriptError("Unknown menu class '%s'", sc.String);
} }
@ -303,11 +305,11 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc)
sc.MustGetString(); sc.MustGetString();
desc->mSelector = GetMenuTexture(sc.String); desc->mSelector = GetMenuTexture(sc.String);
sc.MustGetStringName(","); sc.MustGetStringName(",");
sc.MustGetNumber(); sc.MustGetFloat();
desc->mSelectOfsX = sc.Number; desc->mSelectOfsX = sc.Float;
sc.MustGetStringName(","); sc.MustGetStringName(",");
sc.MustGetNumber(); sc.MustGetFloat();
desc->mSelectOfsY = sc.Number; desc->mSelectOfsY = sc.Float;
} }
else if (sc.Compare("Linespacing")) else if (sc.Compare("Linespacing"))
{ {
@ -316,11 +318,11 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc)
} }
else if (sc.Compare("Position")) else if (sc.Compare("Position"))
{ {
sc.MustGetNumber(); sc.MustGetFloat();
desc->mXpos = sc.Number; desc->mXpos = sc.Float;
sc.MustGetStringName(","); sc.MustGetStringName(",");
sc.MustGetNumber(); sc.MustGetFloat();
desc->mYpos = sc.Number; desc->mYpos = sc.Float;
} }
else if (sc.Compare("Centermenu")) else if (sc.Compare("Centermenu"))
{ {
@ -367,7 +369,7 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc)
PClass *cls = PClass::FindClass(buildname); PClass *cls = PClass::FindClass(buildname);
if (cls != nullptr && cls->IsDescendantOf("ListMenuItem")) if (cls != nullptr && cls->IsDescendantOf("ListMenuItem"))
{ {
auto func = dyn_cast<PFunction>(cls->Symbols.FindSymbol("Init", false)); auto func = dyn_cast<PFunction>(cls->Symbols.FindSymbol("Init", true));
if (func != nullptr && !(func->Variants[0].Flags & (VARF_Protected | VARF_Private))) // skip internal classes which have a protexted init method. if (func != nullptr && !(func->Variants[0].Flags & (VARF_Protected | VARF_Private))) // skip internal classes which have a protexted init method.
{ {
auto &args = func->Variants[0].Proto->ArgumentTypes; auto &args = func->Variants[0].Proto->ArgumentTypes;
@ -409,7 +411,7 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc)
else if (args[i] == TypeTextureID) else if (args[i] == TypeTextureID)
{ {
auto f = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch); auto f = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch);
if (!f.isValid()) if (!f.Exists())
{ {
sc.ScriptError("Unknown texture %s", sc.String); sc.ScriptError("Unknown texture %s", sc.String);
} }
@ -699,7 +701,7 @@ static void ParseOptionMenuBody(FScanner &sc, DOptionMenuDescriptor *desc)
else if (sc.Compare("Class")) else if (sc.Compare("Class"))
{ {
sc.MustGetString(); sc.MustGetString();
const PClass *cls = PClass::FindClass(sc.String); PClass *cls = PClass::FindClass(sc.String);
if (cls == nullptr || !cls->IsDescendantOf("OptionMenu")) if (cls == nullptr || !cls->IsDescendantOf("OptionMenu"))
{ {
sc.ScriptError("Unknown menu class '%s'", sc.String); sc.ScriptError("Unknown menu class '%s'", sc.String);
@ -739,7 +741,7 @@ static void ParseOptionMenuBody(FScanner &sc, DOptionMenuDescriptor *desc)
PClass *cls = PClass::FindClass(buildname); PClass *cls = PClass::FindClass(buildname);
if (cls != nullptr && cls->IsDescendantOf("OptionMenuItem")) if (cls != nullptr && cls->IsDescendantOf("OptionMenuItem"))
{ {
auto func = dyn_cast<PFunction>(cls->Symbols.FindSymbol("Init", false)); auto func = dyn_cast<PFunction>(cls->Symbols.FindSymbol("Init", true));
if (func != nullptr && !(func->Variants[0].Flags & (VARF_Protected | VARF_Private))) // skip internal classes which have a protexted init method. if (func != nullptr && !(func->Variants[0].Flags & (VARF_Protected | VARF_Private))) // skip internal classes which have a protexted init method.
{ {
auto &args = func->Variants[0].Proto->ArgumentTypes; auto &args = func->Variants[0].Proto->ArgumentTypes;
@ -866,7 +868,25 @@ static void ParseOptionMenu(FScanner &sc)
ParseOptionMenuBody(sc, desc); ParseOptionMenuBody(sc, desc);
ReplaceMenu(sc, desc); ReplaceMenu(sc, desc);
if (desc->mIndent == 0) desc->CalcIndent(); }
//=============================================================================
//
//
//
//=============================================================================
static void ParseAddOptionMenu(FScanner &sc)
{
sc.MustGetString();
DMenuDescriptor **pOld = MenuDescriptors.CheckKey(sc.String);
if (pOld == nullptr || *pOld == nullptr || !(*pOld)->IsKindOf(RUNTIME_CLASS(DOptionMenuDescriptor)))
{
sc.ScriptError("%s is not an option menu that can be extended", sc.String);
}
ParseOptionMenuBody(sc, (DOptionMenuDescriptor*)(*pOld));
} }
@ -934,6 +954,10 @@ void M_ParseMenuDefs()
{ {
ParseOptionMenu(sc); ParseOptionMenu(sc);
} }
else if (sc.Compare("ADDOPTIONMENU"))
{
ParseAddOptionMenu(sc);
}
else if (sc.Compare("DEFAULTOPTIONMENU")) else if (sc.Compare("DEFAULTOPTIONMENU"))
{ {
ParseOptionMenuBody(sc, DefaultOptionMenuSettings); ParseOptionMenuBody(sc, DefaultOptionMenuSettings);
@ -948,7 +972,9 @@ void M_ParseMenuDefs()
} }
} }
} }
DefaultListMenuClass = DefaultListMenuSettings->mClass;
DefaultListMenuSettings = nullptr; DefaultListMenuSettings = nullptr;
DefaultOptionMenuClass = DefaultOptionMenuSettings->mClass;
DefaultOptionMenuSettings = nullptr; DefaultOptionMenuSettings = nullptr;
} }
@ -970,13 +996,13 @@ static void BuildEpisodeMenu()
if ((*desc)->IsKindOf(RUNTIME_CLASS(DListMenuDescriptor))) if ((*desc)->IsKindOf(RUNTIME_CLASS(DListMenuDescriptor)))
{ {
DListMenuDescriptor *ld = static_cast<DListMenuDescriptor*>(*desc); DListMenuDescriptor *ld = static_cast<DListMenuDescriptor*>(*desc);
int posy = ld->mYpos; int posy = (int)ld->mYpos;
int topy = posy; int topy = posy;
// Get lowest y coordinate of any static item in the menu // Get lowest y coordinate of any static item in the menu
for(unsigned i = 0; i < ld->mItems.Size(); i++) for(unsigned i = 0; i < ld->mItems.Size(); i++)
{ {
int y = ld->mItems[i]->GetY(); int y = (int)ld->mItems[i]->GetY();
if (y < topy) topy = y; if (y < topy) topy = y;
} }
@ -1070,13 +1096,13 @@ static void BuildPlayerclassMenu()
// add player display // add player display
ld->mSelectedItem = ld->mItems.Size(); ld->mSelectedItem = ld->mItems.Size();
int posy = ld->mYpos; int posy = (int)ld->mYpos;
int topy = posy; int topy = posy;
// Get lowest y coordinate of any static item in the menu // Get lowest y coordinate of any static item in the menu
for(unsigned i = 0; i < ld->mItems.Size(); i++) for(unsigned i = 0; i < ld->mItems.Size(); i++)
{ {
int y = ld->mItems[i]->GetY(); int y = (int)ld->mItems[i]->GetY();
if (y < topy) topy = y; if (y < topy) topy = y;
} }
@ -1324,19 +1350,56 @@ void M_StartupSkillMenu(FGameStartup *gs)
{ {
static int done = -1; static int done = -1;
bool success = false; bool success = false;
TArray<FSkillInfo*> MenuSkills;
TArray<int> SkillIndices;
if (MenuSkills.Size() == 0)
{
for (unsigned ind = 0; ind < AllSkills.Size(); ind++)
{
if (!AllSkills[ind].NoMenu)
{
MenuSkills.Push(&AllSkills[ind]);
SkillIndices.Push(ind);
}
}
}
if (MenuSkills.Size() == 0) I_Error("No valid skills for menu found. At least one must be defined.");
int defskill = DefaultSkill;
if ((unsigned int)defskill >= MenuSkills.Size())
{
defskill = SkillIndices[(MenuSkills.Size() - 1) / 2];
}
if (AllSkills[defskill].NoMenu)
{
for (defskill = 0; defskill < (int)AllSkills.Size(); defskill++)
{
if (!AllSkills[defskill].NoMenu) break;
}
}
int defindex = 0;
for (unsigned i = 0; i < MenuSkills.Size(); i++)
{
if (MenuSkills[i] == &AllSkills[defskill])
{
defindex = i;
break;
}
}
DMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_Skillmenu); DMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_Skillmenu);
if (desc != nullptr) if (desc != nullptr)
{ {
if ((*desc)->IsKindOf(RUNTIME_CLASS(DListMenuDescriptor))) if ((*desc)->IsKindOf(RUNTIME_CLASS(DListMenuDescriptor)))
{ {
DListMenuDescriptor *ld = static_cast<DListMenuDescriptor*>(*desc); DListMenuDescriptor *ld = static_cast<DListMenuDescriptor*>(*desc);
int x = ld->mXpos; int x = (int)ld->mXpos;
int y = ld->mYpos; int y = (int)ld->mYpos;
// Delete previous contents // Delete previous contents
for(unsigned i=0; i<ld->mItems.Size(); i++) for(unsigned i=0; i<ld->mItems.Size(); i++)
{ {
FName n = ld->mItems[i]->GetAction(nullptr); FName n = ld->mItems[i]->mAction;
if (n == NAME_Startgame || n == NAME_StartgameConfirm) if (n == NAME_Startgame || n == NAME_StartgameConfirm)
{ {
ld->mItems.Resize(i); ld->mItems.Resize(i);
@ -1347,12 +1410,7 @@ void M_StartupSkillMenu(FGameStartup *gs)
if (done != restart) if (done != restart)
{ {
done = restart; done = restart;
int defskill = DefaultSkill; ld->mSelectedItem = ld->mItems.Size() + defindex;
if ((unsigned int)defskill >= AllSkills.Size())
{
defskill = (AllSkills.Size() - 1) / 2;
}
ld->mSelectedItem = ld->mItems.Size() + defskill;
int posy = y; int posy = y;
int topy = posy; int topy = posy;
@ -1360,14 +1418,14 @@ void M_StartupSkillMenu(FGameStartup *gs)
// Get lowest y coordinate of any static item in the menu // Get lowest y coordinate of any static item in the menu
for(unsigned i = 0; i < ld->mItems.Size(); i++) for(unsigned i = 0; i < ld->mItems.Size(); i++)
{ {
int y = ld->mItems[i]->GetY(); int y = (int)ld->mItems[i]->GetY();
if (y < topy) topy = y; if (y < topy) topy = y;
} }
// center the menu on the screen if the top space is larger than the bottom space // center the menu on the screen if the top space is larger than the bottom space
int totalheight = posy + AllSkills.Size() * ld->mLinespacing - topy; int totalheight = posy + MenuSkills.Size() * ld->mLinespacing - topy;
if (totalheight < 190 || AllSkills.Size() == 1) if (totalheight < 190 || MenuSkills.Size() == 1)
{ {
int newtop = (200 - totalheight + topy) / 2; int newtop = (200 - totalheight + topy) / 2;
int topdelta = newtop - topy; int topdelta = newtop - topy;
@ -1377,7 +1435,7 @@ void M_StartupSkillMenu(FGameStartup *gs)
{ {
ld->mItems[i]->OffsetPositionY(topdelta); ld->mItems[i]->OffsetPositionY(topdelta);
} }
y = ld->mYpos = posy - topdelta; ld->mYpos = y = posy - topdelta;
} }
} }
else else
@ -1390,9 +1448,9 @@ void M_StartupSkillMenu(FGameStartup *gs)
} }
unsigned firstitem = ld->mItems.Size(); unsigned firstitem = ld->mItems.Size();
for(unsigned int i = 0; i < AllSkills.Size(); i++) for(unsigned int i = 0; i < MenuSkills.Size(); i++)
{ {
FSkillInfo &skill = AllSkills[i]; FSkillInfo &skill = *MenuSkills[i];
DMenuItemBase *li; DMenuItemBase *li;
// Using a different name for skills that must be confirmed makes handling this easier. // Using a different name for skills that must be confirmed makes handling this easier.
FName action = (skill.MustConfirm && !AllEpisodes[gs->Episode].mNoSkill) ? FName action = (skill.MustConfirm && !AllEpisodes[gs->Episode].mNoSkill) ?
@ -1406,22 +1464,22 @@ void M_StartupSkillMenu(FGameStartup *gs)
if (skill.PicName.Len() != 0 && pItemText == nullptr) if (skill.PicName.Len() != 0 && pItemText == nullptr)
{ {
FTextureID tex = GetMenuTexture(skill.PicName); FTextureID tex = GetMenuTexture(skill.PicName);
li = CreateListMenuItemPatch(ld->mXpos, y, ld->mLinespacing, skill.Shortcut, tex, action, i); li = CreateListMenuItemPatch(ld->mXpos, y, ld->mLinespacing, skill.Shortcut, tex, action, SkillIndices[i]);
} }
else else
{ {
EColorRange color = (EColorRange)skill.GetTextColor(); EColorRange color = (EColorRange)skill.GetTextColor();
if (color == CR_UNTRANSLATED) color = ld->mFontColor; if (color == CR_UNTRANSLATED) color = ld->mFontColor;
li = CreateListMenuItemText(x, y, ld->mLinespacing, skill.Shortcut, li = CreateListMenuItemText(x, y, ld->mLinespacing, skill.Shortcut,
pItemText? *pItemText : skill.MenuName, ld->mFont, color,ld->mFontColor2, action, i); pItemText? *pItemText : skill.MenuName, ld->mFont, color,ld->mFontColor2, action, SkillIndices[i]);
} }
ld->mItems.Push(li); ld->mItems.Push(li);
GC::WriteBarrier(*desc, li); GC::WriteBarrier(*desc, li);
y += ld->mLinespacing; y += ld->mLinespacing;
} }
if (AllEpisodes[gs->Episode].mNoSkill || AllSkills.Size() == 1) if (AllEpisodes[gs->Episode].mNoSkill || MenuSkills.Size() == 1)
{ {
ld->mAutoselect = firstitem + M_GetDefaultSkill(); ld->mAutoselect = firstitem + defindex;
} }
else else
{ {
@ -1440,7 +1498,7 @@ fail:
MenuDescriptors[NAME_Skillmenu] = od; MenuDescriptors[NAME_Skillmenu] = od;
od->mMenuName = NAME_Skillmenu; od->mMenuName = NAME_Skillmenu;
od->mTitle = "$MNU_CHOOSESKILL"; od->mTitle = "$MNU_CHOOSESKILL";
od->mSelectedItem = 0; od->mSelectedItem = defindex;
od->mScrollPos = 0; od->mScrollPos = 0;
od->mClass = nullptr; od->mClass = nullptr;
od->mPosition = -15; od->mPosition = -15;
@ -1454,9 +1512,9 @@ fail:
od = static_cast<DOptionMenuDescriptor*>(*desc); od = static_cast<DOptionMenuDescriptor*>(*desc);
od->mItems.Clear(); od->mItems.Clear();
} }
for(unsigned int i = 0; i < AllSkills.Size(); i++) for(unsigned int i = 0; i < MenuSkills.Size(); i++)
{ {
FSkillInfo &skill = AllSkills[i]; FSkillInfo &skill = *MenuSkills[i];
DMenuItemBase *li; DMenuItemBase *li;
// Using a different name for skills that must be confirmed makes handling this easier. // Using a different name for skills that must be confirmed makes handling this easier.
const char *action = (skill.MustConfirm && !AllEpisodes[gs->Episode].mNoSkill) ? const char *action = (skill.MustConfirm && !AllEpisodes[gs->Episode].mNoSkill) ?
@ -1467,29 +1525,13 @@ fail:
{ {
pItemText = skill.MenuNamesForPlayerClass.CheckKey(gs->PlayerClass); pItemText = skill.MenuNamesForPlayerClass.CheckKey(gs->PlayerClass);
} }
li = CreateOptionMenuItemSubmenu(pItemText? *pItemText : skill.MenuName, action, i); li = CreateOptionMenuItemSubmenu(pItemText? *pItemText : skill.MenuName, action, SkillIndices[i]);
od->mItems.Push(li); od->mItems.Push(li);
GC::WriteBarrier(od, li); GC::WriteBarrier(od, li);
if (!done) if (!done)
{ {
done = true; done = true;
od->mSelectedItem = M_GetDefaultSkill(); od->mSelectedItem = defindex;
} }
} }
} }
//=============================================================================
//
// Returns the default skill level.
//
//=============================================================================
int M_GetDefaultSkill()
{
int defskill = DefaultSkill;
if ((unsigned int)defskill >= AllSkills.Size())
{
defskill = (AllSkills.Size() - 1) / 2;
}
return defskill;
}

View file

@ -1,396 +0,0 @@
/*
** 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"
#include "cmdlib.h"
// [TP] New #includes
#include "v_text.h"
IMPLEMENT_CLASS(DTextEnterMenu, true, false)
#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)
DEFINE_FIELD(DTextEnterMenu, mInputGridOkay)
//=============================================================================
//
//
//
//=============================================================================
// [TP] Added allowcolors
DTextEnterMenu::DTextEnterMenu(DMenu *parent, const char *textbuffer, int maxlen, int sizemode, bool showgrid, bool allowcolors)
: DMenu(parent)
{
mEnterString = textbuffer;
mEnterSize = maxlen < 0 ? UINT_MAX : unsigned(maxlen);
mSizeMode = sizemode;
mInputGridOkay = showgrid || m_showinputgrid;
if (mEnterString.IsNotEmpty())
{
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;
}
AllowColors = allowcolors; // [TP]
}
//=============================================================================
//
//
//
//=============================================================================
FString DTextEnterMenu::GetText()
{
return mEnterString;
}
//=============================================================================
//
//
//
//=============================================================================
//=============================================================================
//
//
//
//=============================================================================
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 (mEnterString.Len() < mEnterSize &&
(mSizeMode == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(mEnterString) < (mEnterSize-1)*8))
{
mEnterString.AppendFormat("%c", (char)ev->data1);
}
return true;
}
char ch = (char)ev->data1;
if ((ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_KeyRepeat) && ch == '\b')
{
if (mEnterString.IsNotEmpty())
{
mEnterString.Truncate(mEnterString.Len() - 1);
}
}
else if (ev->subtype == EV_GUI_KeyDown)
{
if (ch == GK_ESCAPE)
{
DMenu *parent = mParentMenu;
Close();
parent->CallMenuEvent(MKEY_Abort, false);
return true;
}
else if (ch == '\r')
{
if (mEnterString.IsNotEmpty())
{
// [TP] If we allow color codes, colorize the string now.
if (AllowColors)
mEnterString = strbin1(mEnterString);
DMenu *parent = mParentMenu;
parent->CallMenuEvent(MKEY_Input, false);
Close();
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 (CallMenuEvent(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->CallMenuEvent(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 (mEnterString.IsNotEmpty())
{
mEnterString.Truncate(mEnterString.Len() - 1);
}
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.IsNotEmpty())
{
DMenu *parent = mParentMenu;
Close();
parent->CallMenuEvent(MKEY_Input, false);
return true;
}
}
else if (ch == '\b') // bs
{
if (mEnterString.IsNotEmpty())
{
mEnterString.Truncate(mEnterString.Len() - 1);
}
}
else if (mEnterString.Len() < mEnterSize &&
(mSizeMode == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(mEnterString) < (mEnterSize-1)*8))
{
mEnterString += char(ch);
}
}
return true;
default:
break; // Keep GCC quiet
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
void DTextEnterMenu::Drawer ()
{
mParentMenu->CallDrawer();
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;
// The highlighted character is yellow; the rest are dark gray.
color = (x == InputGridX && y == InputGridY) ? CR_YELLOW : CR_DARKGRAY;
if (pic != NULL)
{
// Draw a normal character.
screen->DrawChar(SmallFont, color, xx + cell_width/2 - width*CleanXfac/2, yy + top_padding, ch, DTA_CleanNoMove, true, TAG_DONE);
}
else if (ch == ' ')
{
FRemapTable *remap = SmallFont->GetColorTranslation(color);
// 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();
}
DEFINE_ACTION_FUNCTION(DTextEnterMenu, Open)
{
PARAM_PROLOGUE;
PARAM_OBJECT(parent, DMenu);
PARAM_STRING(text);
PARAM_INT(maxlen);
PARAM_INT(sizemode);
PARAM_BOOL(fromcontroller);
auto m = new DTextEnterMenu(parent, text.GetChars(), maxlen, sizemode, fromcontroller, false);
M_ActivateMenu(m);
ACTION_RETURN_OBJECT(m);
}
DEFINE_ACTION_FUNCTION(DTextEnterMenu, GetText)
{
PARAM_SELF_PROLOGUE(DTextEnterMenu);
ACTION_RETURN_STRING(self->GetText());
}

View file

@ -46,52 +46,15 @@
#include "c_dispatch.h" #include "c_dispatch.h"
#include "g_game.h" #include "g_game.h"
EXTERN_CVAR (Bool, saveloadconfirmation) // [mxd] EXTERN_CVAR (Bool, saveloadconfirmation) // [mxd]
class DMessageBoxMenu : public DMenu typedef void(*hfunc)();
DEFINE_ACTION_FUNCTION(DMessageBoxMenu, CallHandler)
{ {
DECLARE_CLASS(DMessageBoxMenu, DMenu) PARAM_PROLOGUE;
PARAM_POINTERTYPE(Handler, hfunc);
FBrokenLines *mMessage; Handler();
int mMessageMode; return 0;
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 OnDestroy() override;
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, false, false)
//=============================================================================
//
//
//
//=============================================================================
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);
} }
//============================================================================= //=============================================================================
@ -100,325 +63,15 @@ DMessageBoxMenu::DMessageBoxMenu(DMenu *parent, const char *message, int message
// //
//============================================================================= //=============================================================================
void DMessageBoxMenu::Init(DMenu *parent, const char *message, int messagemode, bool playsound) DMenu *CreateMessageBoxMenu(DMenu *parent, const char *message, int messagemode, bool playsound, FName action = NAME_None, hfunc handler = nullptr)
{ {
mParentMenu = parent; auto c = PClass::FindClass("MessageBoxMenu");
if (message != NULL) auto p = c->CreateNew();
{ VMValue params[] = { p, parent, FString(message), messagemode, playsound, action.GetIndex(), reinterpret_cast<void*>(handler) };
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);
}
}
//============================================================================= auto f = dyn_cast<PFunction>(c->Symbols.FindSymbol("Init", false));
// GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0);
// return (DMenu*)p;
//
//=============================================================================
void DMessageBoxMenu::OnDestroy()
{
if (mMessage != NULL) V_FreeBrokenLines(mMessage);
mMessage = NULL;
Super::OnDestroy();
}
//=============================================================================
//
//
//
//=============================================================================
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->CallMenuEvent(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();
//V_SetBorderNeedRefresh();
//ST_SetNeedRefresh();
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 + fontheight/2 - 5) * 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, false, false)
//=============================================================================
//
//
//
//=============================================================================
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();
}
} }
//============================================================================= //=============================================================================
@ -430,75 +83,62 @@ void DQuitMenu::HandleResult(bool res)
CCMD (menu_quit) CCMD (menu_quit)
{ // F10 { // F10
M_StartControlPanel (true); M_StartControlPanel (true);
DMenu *newmenu = new DQuitMenu(false);
newmenu->mParentMenu = DMenu::CurrentMenu; 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];
DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, EndString, 0, false, NAME_None, []()
{
if (!netgame)
{
if (gameinfo.quitSound.IsNotEmpty())
{
S_Sound(CHAN_VOICE | CHAN_UI, gameinfo.quitSound, snd_menuvolume, ATTN_NONE);
I_WaitVBL(105);
}
}
ST_Endoom();
});
M_ActivateMenu(newmenu); M_ActivateMenu(newmenu);
} }
//=============================================================================
//
//
//
//=============================================================================
//============================================================================= //=============================================================================
// //
// //
// //
//============================================================================= //=============================================================================
class DEndGameMenu : public DMessageBoxMenu void ActivateEndGameMenu()
{ {
DECLARE_CLASS(DEndGameMenu, DMessageBoxMenu) FString tempstring = GStrings(netgame ? "NETEND" : "ENDGAME");
DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, false, NAME_None, []()
public:
DEndGameMenu(bool playsound = false);
virtual void HandleResult(bool res);
};
IMPLEMENT_CLASS(DEndGameMenu, false, false)
//=============================================================================
//
//
//
//=============================================================================
DEndGameMenu::DEndGameMenu(bool playsound)
{
Init(NULL, GStrings(netgame ? "NETEND" : "ENDGAME"), 0, playsound);
}
//=============================================================================
//
//
//
//=============================================================================
void DEndGameMenu::HandleResult(bool res)
{
if (res)
{ {
M_ClearMenus (); M_ClearMenus();
if (!netgame) if (!netgame)
{ {
D_StartTitle (); D_StartTitle();
} }
} });
else
{
Close();
CloseSound();
}
}
//============================================================================= M_ActivateMenu(newmenu);
// }
//
//
//=============================================================================
CCMD (menu_endgame) CCMD (menu_endgame)
{ // F7 { // F7
@ -510,67 +150,8 @@ CCMD (menu_endgame)
//M_StartControlPanel (true); //M_StartControlPanel (true);
S_Sound (CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE); S_Sound (CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE);
DMenu *newmenu = new DEndGameMenu(false);
newmenu->mParentMenu = DMenu::CurrentMenu;
M_ActivateMenu(newmenu);
}
//============================================================================= ActivateEndGameMenu();
//
//
//
//=============================================================================
//=============================================================================
//
//
//
//=============================================================================
class DQuickSaveMenu : public DMessageBoxMenu
{
DECLARE_CLASS(DQuickSaveMenu, DMessageBoxMenu)
public:
DQuickSaveMenu(bool playsound = false);
virtual void HandleResult(bool res);
};
IMPLEMENT_CLASS(DQuickSaveMenu, false, false)
//=============================================================================
//
//
//
//=============================================================================
DQuickSaveMenu::DQuickSaveMenu(bool playsound)
{
FString tempstring;
tempstring.Format(GStrings("QSPROMPT"), savegameManager.quickSaveSlot->Title);
Init(NULL, tempstring, 0, playsound);
}
//=============================================================================
//
//
//
//=============================================================================
void DQuickSaveMenu::HandleResult(bool res)
{
if (res)
{
G_SaveGame (savegameManager.quickSaveSlot->Filename.GetChars(), savegameManager.quickSaveSlot->Title);
S_Sound (CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE);
M_ClearMenus();
}
else
{
Close();
CloseSound();
}
} }
//============================================================================= //=============================================================================
@ -601,72 +182,23 @@ CCMD (quicksave)
// [mxd]. Just save the game, no questions asked. // [mxd]. Just save the game, no questions asked.
if (!saveloadconfirmation) if (!saveloadconfirmation)
{ {
G_SaveGame(savegameManager.quickSaveSlot->Filename.GetChars(), savegameManager.quickSaveSlot->Title); G_SaveGame(savegameManager.quickSaveSlot->Filename.GetChars(), savegameManager.quickSaveSlot->SaveTitle.GetChars());
return; return;
} }
S_Sound(CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE); S_Sound(CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE);
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, false, false)
//=============================================================================
//
//
//
//=============================================================================
DQuickLoadMenu::DQuickLoadMenu(bool playsound)
{
FString tempstring; FString tempstring;
tempstring.Format(GStrings("QSPROMPT"), savegameManager.quickSaveSlot->SaveTitle.GetChars());
tempstring.Format(GStrings("QLPROMPT"), savegameManager.quickSaveSlot->Title); DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, false, NAME_None, []()
Init(NULL, tempstring, 0, playsound);
}
//=============================================================================
//
//
//
//=============================================================================
void DQuickLoadMenu::HandleResult(bool res)
{
if (res)
{ {
G_LoadGame (savegameManager.quickSaveSlot->Filename.GetChars()); G_SaveGame(savegameManager.quickSaveSlot->Filename.GetChars(), savegameManager.quickSaveSlot->SaveTitle.GetChars());
S_Sound (CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE); S_Sound(CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE);
M_ClearMenus(); M_ClearMenus();
} });
else
{ M_ActivateMenu(newmenu);
Close();
CloseSound();
}
} }
//============================================================================= //=============================================================================
@ -699,10 +231,17 @@ CCMD (quickload)
G_LoadGame(savegameManager.quickSaveSlot->Filename.GetChars()); G_LoadGame(savegameManager.quickSaveSlot->Filename.GetChars());
return; return;
} }
FString tempstring;
tempstring.Format(GStrings("QLPROMPT"), savegameManager.quickSaveSlot->SaveTitle.GetChars());
M_StartControlPanel(true); M_StartControlPanel(true);
DMenu *newmenu = new DQuickLoadMenu(false);
newmenu->mParentMenu = DMenu::CurrentMenu; DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, false, NAME_None, []()
{
G_LoadGame(savegameManager.quickSaveSlot->Filename.GetChars());
S_Sound(CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE);
M_ClearMenus();
});
M_ActivateMenu(newmenu); M_ActivateMenu(newmenu);
} }
@ -714,13 +253,13 @@ CCMD (quickload)
void M_StartMessage(const char *message, int messagemode, FName action) void M_StartMessage(const char *message, int messagemode, FName action)
{ {
if (DMenu::CurrentMenu == NULL) if (CurrentMenu == NULL)
{ {
// only play a sound if no menu was active before // only play a sound if no menu was active before
M_StartControlPanel(menuactive == MENU_Off); M_StartControlPanel(menuactive == MENU_Off);
} }
DMenu *newmenu = new DMessageBoxMenu(DMenu::CurrentMenu, message, messagemode, false, action); DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, message, messagemode, false, action);
newmenu->mParentMenu = DMenu::CurrentMenu; newmenu->mParentMenu = CurrentMenu;
M_ActivateMenu(newmenu); M_ActivateMenu(newmenu);
} }
@ -728,7 +267,7 @@ DEFINE_ACTION_FUNCTION(DMenu, StartMessage)
{ {
PARAM_PROLOGUE; PARAM_PROLOGUE;
PARAM_STRING(msg); PARAM_STRING(msg);
PARAM_INT(mode); PARAM_INT_DEF(mode);
PARAM_NAME_DEF(action); PARAM_NAME_DEF(action);
M_StartMessage(msg, mode, action); M_StartMessage(msg, mode, action);
return 0; return 0;

View file

@ -49,32 +49,6 @@
#include "menu/menu.h" #include "menu/menu.h"
//=============================================================================
//
//
//
//=============================================================================
void DOptionMenuDescriptor::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;
}
DEFINE_ACTION_FUNCTION(DOptionMenuDescriptor, CalcIndent)
{
PARAM_SELF_PROLOGUE(DOptionMenuDescriptor);
self->CalcIndent();
return 0;
}
//============================================================================= //=============================================================================
// //
// //
@ -85,7 +59,7 @@ DMenuItemBase *DOptionMenuDescriptor::GetItem(FName name)
{ {
for(unsigned i=0;i<mItems.Size(); i++) for(unsigned i=0;i<mItems.Size(); i++)
{ {
FName nm = mItems[i]->GetAction(NULL); FName nm = mItems[i]->mAction;
if (nm == name) return mItems[i]; if (nm == name) return mItems[i];
} }
return NULL; return NULL;

View file

@ -48,12 +48,10 @@
#include "r_data/r_translate.h" #include "r_data/r_translate.h"
#include "v_text.h" #include "v_text.h"
EXTERN_CVAR (String, playerclass) EXTERN_CVAR(Int, team)
EXTERN_CVAR (String, name) EXTERN_CVAR(Float, autoaim)
EXTERN_CVAR (Int, team)
EXTERN_CVAR (Float, autoaim)
EXTERN_CVAR(Bool, neverswitchonpickup) EXTERN_CVAR(Bool, neverswitchonpickup)
EXTERN_CVAR (Bool, cl_run) EXTERN_CVAR(Bool, cl_run)
//============================================================================= //=============================================================================
// //
@ -61,365 +59,42 @@ EXTERN_CVAR (Bool, cl_run)
// //
//============================================================================= //=============================================================================
class DPlayerMenu : public DListMenu DEFINE_ACTION_FUNCTION(DPlayerMenu, ColorChanged)
{ {
DECLARE_CLASS(DPlayerMenu, DListMenu) PARAM_SELF_PROLOGUE(DMenu);
PARAM_INT(r);
int PlayerClassIndex; PARAM_INT(g);
FPlayerClass *PlayerClass; PARAM_INT(b);
TArray<int> PlayerColorSets; // only allow if the menu is active to prevent abuse.
TArray<int> PlayerSkins; if (self == CurrentMenu)
int mRotation;
void PickPlayerClass ();
void UpdateColorsets();
void UpdateSkins();
void UpdateTranslation();
void SendNewColor (int red, int green, int blue);
void PlayerNameChanged(DMenuItemBase *li);
void ColorSetChanged (DMenuItemBase *li);
void ClassChanged (DMenuItemBase *li);
void AutoaimChanged (DMenuItemBase *li);
void SkinChanged (DMenuItemBase *li);
public:
DPlayerMenu() {}
void Init(DMenu *parent, DListMenuDescriptor *desc);
bool Responder (event_t *ev);
bool MenuEvent (int mkey, bool fromcontroller);
bool MouseEvent(int type, int x, int y);
void Ticker ();
void Drawer ();
};
IMPLEMENT_CLASS(DPlayerMenu, false, false)
//=============================================================================
//
//
//
//=============================================================================
enum EPDFlags
{
ListMenuItemPlayerDisplay_PDF_ROTATION = 0x10001,
ListMenuItemPlayerDisplay_PDF_SKIN = 0x10002,
ListMenuItemPlayerDisplay_PDF_CLASS = 0x10003,
ListMenuItemPlayerDisplay_PDF_MODE = 0x10004,
ListMenuItemPlayerDisplay_PDF_TRANSLATE = 0x10005,
};
void DPlayerMenu::Init(DMenu *parent, DListMenuDescriptor *desc)
{
DMenuItemBase *li;
Super::Init(parent, desc);
PickPlayerClass();
mRotation = 0;
li = GetItem(NAME_Playerdisplay);
if (li != NULL)
{ {
li->SetValue(ListMenuItemPlayerDisplay_PDF_ROTATION, 0); char command[24];
li->SetValue(ListMenuItemPlayerDisplay_PDF_MODE, 1); players[consoleplayer].userinfo.ColorChanged(MAKERGB(r, g, b));
li->SetValue(ListMenuItemPlayerDisplay_PDF_TRANSLATE, 1); mysnprintf(command, countof(command), "color \"%02x %02x %02x\"", r, g, b);
li->SetValue(ListMenuItemPlayerDisplay_PDF_CLASS, players[consoleplayer].userinfo.GetPlayerClassNum()); C_DoCommand(command);
if (PlayerClass != NULL && !(GetDefaultByType (PlayerClass->Type)->flags4 & MF4_NOSKIN) &&
players[consoleplayer].userinfo.GetPlayerClassNum() != -1)
{
li->SetValue(ListMenuItemPlayerDisplay_PDF_SKIN, players[consoleplayer].userinfo.GetSkin());
}
} }
return 0;
li = GetItem(NAME_Playerbox);
if (li != NULL)
{
li->SetString(0, name);
}
li = GetItem(NAME_Team);
if (li != NULL)
{
li->SetString(0, "None");
for(unsigned i=0;i<Teams.Size(); i++)
{
li->SetString(i+1, Teams[i].GetName());
}
li->SetValue(0, team == TEAM_NONE? 0 : team + 1);
}
int mycolorset = players[consoleplayer].userinfo.GetColorSet();
int color = players[consoleplayer].userinfo.GetColor();
UpdateColorsets();
li = GetItem(NAME_Red);
if (li != NULL)
{
li->Enable(mycolorset == -1);
li->SetValue(0, RPART(color));
}
li = GetItem(NAME_Green);
if (li != NULL)
{
li->Enable(mycolorset == -1);
li->SetValue(0, GPART(color));
}
li = GetItem(NAME_Blue);
if (li != NULL)
{
li->Enable(mycolorset == -1);
li->SetValue(0, BPART(color));
}
li = GetItem(NAME_Class);
if (li != NULL)
{
if (PlayerClasses.Size() == 1)
{
li->SetString(0, GetPrintableDisplayName(PlayerClasses[0].Type));
li->SetValue(0, 0);
}
else
{
// [XA] Remove the "Random" option if the relevant gameinfo flag is set.
if(!gameinfo.norandomplayerclass)
li->SetString(0, "Random");
for(unsigned i=0; i< PlayerClasses.Size(); i++)
{
const char *cls = GetPrintableDisplayName(PlayerClasses[i].Type);
li->SetString(gameinfo.norandomplayerclass ? i : i+1, cls);
}
int pclass = players[consoleplayer].userinfo.GetPlayerClassNum();
li->SetValue(0, gameinfo.norandomplayerclass && pclass >= 0 ? pclass : pclass + 1);
}
}
UpdateSkins();
li = GetItem(NAME_Gender);
if (li != NULL)
{
li->SetValue(0, players[consoleplayer].userinfo.GetGender());
}
li = GetItem(NAME_Autoaim);
if (li != NULL)
{
li->SetValue(0, (int)autoaim);
}
li = GetItem(NAME_Switch);
if (li != NULL)
{
li->SetValue(0, neverswitchonpickup);
}
li = GetItem(NAME_AlwaysRun);
if (li != NULL)
{
li->SetValue(0, cl_run);
}
if (mDesc->mSelectedItem < 0) mDesc->mSelectedItem = 1;
} }
//============================================================================= //=============================================================================
// //
// // access to the player config is done natively, so that broader access
// functions do not need to be exported.
// //
//============================================================================= //=============================================================================
bool DPlayerMenu::Responder (event_t *ev) DEFINE_ACTION_FUNCTION(DPlayerMenu, PlayerNameChanged)
{ {
if (ev->type == EV_GUI_Event && ev->subtype == EV_GUI_Char && ev->data1 == ' ') PARAM_SELF_PROLOGUE(DMenu);
PARAM_STRING(s);
const char *pp = s;
FString command("name \"");
if (self == CurrentMenu)
{ {
// turn the player sprite around
mRotation = 8 - mRotation;
DMenuItemBase *li = GetItem(NAME_Playerdisplay);
if (li != NULL)
{
li->SetValue(ListMenuItemPlayerDisplay_PDF_ROTATION, mRotation);
}
return true;
}
return Super::Responder(ev);
}
//=============================================================================
//
//
//
//=============================================================================
void DPlayerMenu::UpdateTranslation()
{
int PlayerColor = players[consoleplayer].userinfo.GetColor();
int PlayerSkin = players[consoleplayer].userinfo.GetSkin();
int PlayerColorset = players[consoleplayer].userinfo.GetColorSet();
if (PlayerClass != NULL)
{
PlayerSkin = R_FindSkin (skins[PlayerSkin].name, int(PlayerClass - &PlayerClasses[0]));
R_GetPlayerTranslation(PlayerColor, GetColorSet(PlayerClass->Type, PlayerColorset),
&skins[PlayerSkin], translationtables[TRANSLATION_Players][MAXPLAYERS]);
}
}
//=============================================================================
//
//
//
//=============================================================================
void DPlayerMenu::PickPlayerClass()
{
/*
// What's the point of this? Aren't we supposed to edit the
// userinfo?
if (players[consoleplayer].mo != NULL)
{
PlayerClassIndex = players[consoleplayer].CurrentPlayerClass;
}
else
*/
{
int pclass = 0;
// [GRB] Pick a class from player class list
if (PlayerClasses.Size () > 1)
{
pclass = players[consoleplayer].userinfo.GetPlayerClassNum();
if (pclass < 0)
{
pclass = (MenuTime>>7) % PlayerClasses.Size ();
}
}
PlayerClassIndex = pclass;
}
PlayerClass = &PlayerClasses[PlayerClassIndex];
UpdateTranslation();
}
//=============================================================================
//
//
//
//=============================================================================
void DPlayerMenu::SendNewColor (int red, int green, int blue)
{
char command[24];
players[consoleplayer].userinfo.ColorChanged(MAKERGB(red,green,blue));
mysnprintf (command, countof(command), "color \"%02x %02x %02x\"", red, green, blue);
C_DoCommand (command);
UpdateTranslation();
}
//=============================================================================
//
//
//
//=============================================================================
void DPlayerMenu::UpdateColorsets()
{
DMenuItemBase *li = GetItem(NAME_Color);
if (li != NULL)
{
int sel = 0;
EnumColorSets(PlayerClass->Type, &PlayerColorSets);
li->SetString(0, "Custom");
for(unsigned i=0;i<PlayerColorSets.Size(); i++)
{
FPlayerColorSet *colorset = GetColorSet(PlayerClass->Type, PlayerColorSets[i]);
li->SetString(i+1, colorset->Name);
}
int mycolorset = players[consoleplayer].userinfo.GetColorSet();
if (mycolorset != -1)
{
for(unsigned i=0;i<PlayerColorSets.Size(); i++)
{
if (PlayerColorSets[i] == mycolorset)
{
sel = i+1;
}
}
}
li->SetValue(0, sel);
}
}
//=============================================================================
//
//
//
//=============================================================================
void DPlayerMenu::UpdateSkins()
{
int sel = 0;
int skin;
DMenuItemBase *li = GetItem(NAME_Skin);
if (li != NULL)
{
if (GetDefaultByType (PlayerClass->Type)->flags4 & MF4_NOSKIN ||
players[consoleplayer].userinfo.GetPlayerClassNum() == -1)
{
li->SetString(0, "Base");
li->SetValue(0, 0);
skin = 0;
}
else
{
PlayerSkins.Clear();
for(int i=0;i<(int)numskins; i++)
{
if (PlayerClass->CheckSkin(i))
{
int j = PlayerSkins.Push(i);
li->SetString(j, skins[i].name);
if (players[consoleplayer].userinfo.GetSkin() == i)
{
sel = j;
}
}
}
li->SetValue(0, sel);
skin = PlayerSkins[sel];
}
li = GetItem(NAME_Playerdisplay);
if (li != NULL)
{
li->SetValue(ListMenuItemPlayerDisplay_PDF_SKIN, skin);
}
}
UpdateTranslation();
}
//=============================================================================
//
//
//
//=============================================================================
void DPlayerMenu::PlayerNameChanged(DMenuItemBase *li)
{
char pp[MAXPLAYERNAME+1];
const char *p;
if (li->GetString(0, pp, MAXPLAYERNAME))
{
FString command("name \"");
// Escape any backslashes or quotation marks before sending the name to the console. // Escape any backslashes or quotation marks before sending the name to the console.
for (p = pp; *p != '\0'; ++p) for (auto p = pp; *p != '\0'; ++p)
{ {
if (*p == '"' || *p == '\\') if (*p == '"' || *p == '\\')
{ {
@ -428,41 +103,9 @@ void DPlayerMenu::PlayerNameChanged(DMenuItemBase *li)
command << *p; command << *p;
} }
command << '"'; command << '"';
C_DoCommand (command);
}
}
//=============================================================================
//
//
//
//=============================================================================
void DPlayerMenu::ColorSetChanged (DMenuItemBase *li)
{
int sel;
if (li->GetValue(0, &sel))
{
int mycolorset = -1;
if (sel > 0) mycolorset = PlayerColorSets[sel-1];
DMenuItemBase *red = GetItem(NAME_Red);
DMenuItemBase *green = GetItem(NAME_Green);
DMenuItemBase *blue = GetItem(NAME_Blue);
// disable the sliders if a valid colorset is selected
if (red != NULL) red->Enable(mycolorset == -1);
if (green != NULL) green->Enable(mycolorset == -1);
if (blue != NULL) blue->Enable(mycolorset == -1);
char command[24];
players[consoleplayer].userinfo.ColorSetChanged(mycolorset);
mysnprintf(command, countof(command), "colorset %d", mycolorset);
C_DoCommand(command); C_DoCommand(command);
UpdateTranslation();
} }
return 0;
} }
//============================================================================= //=============================================================================
@ -471,32 +114,18 @@ void DPlayerMenu::ColorSetChanged (DMenuItemBase *li)
// //
//============================================================================= //=============================================================================
void DPlayerMenu::ClassChanged (DMenuItemBase *li) DEFINE_ACTION_FUNCTION(DPlayerMenu, ColorSetChanged)
{ {
if (PlayerClasses.Size () == 1) PARAM_SELF_PROLOGUE(DMenu);
PARAM_INT(sel);
if (self == CurrentMenu)
{ {
return; players[consoleplayer].userinfo.ColorSetChanged(sel);
} char command[24];
mysnprintf(command, countof(command), "colorset %d", sel);
int sel; C_DoCommand(command);
if (li->GetValue(0, &sel))
{
players[consoleplayer].userinfo.PlayerClassNumChanged(gameinfo.norandomplayerclass ? sel : sel-1);
PickPlayerClass();
cvar_set ("playerclass", sel == 0 && !gameinfo.norandomplayerclass ? "Random" : GetPrintableDisplayName(PlayerClass->Type).GetChars());
UpdateSkins();
UpdateColorsets();
UpdateTranslation();
li = GetItem(NAME_Playerdisplay);
if (li != NULL)
{
li->SetValue(ListMenuItemPlayerDisplay_PDF_CLASS, players[consoleplayer].userinfo.GetPlayerClassNum());
}
} }
return 0;
} }
//============================================================================= //=============================================================================
@ -505,29 +134,36 @@ void DPlayerMenu::ClassChanged (DMenuItemBase *li)
// //
//============================================================================= //=============================================================================
void DPlayerMenu::SkinChanged (DMenuItemBase *li) DEFINE_ACTION_FUNCTION(DPlayerMenu, ClassChanged)
{ {
if (GetDefaultByType (PlayerClass->Type)->flags4 & MF4_NOSKIN || PARAM_SELF_PROLOGUE(DMenu);
players[consoleplayer].userinfo.GetPlayerClassNum() == -1) PARAM_INT(sel);
PARAM_POINTER(cls, FPlayerClass);
if (self == CurrentMenu)
{ {
return; players[consoleplayer].userinfo.PlayerClassNumChanged(gameinfo.norandomplayerclass ? sel : sel - 1);
cvar_set("playerclass", sel == 0 && !gameinfo.norandomplayerclass ? "Random" : GetPrintableDisplayName(cls->Type).GetChars());
} }
return 0;
}
int sel;
if (li->GetValue(0, &sel)) //=============================================================================
//
//
//
//=============================================================================
DEFINE_ACTION_FUNCTION(DPlayerMenu, SkinChanged)
{
PARAM_SELF_PROLOGUE(DMenu);
PARAM_INT(sel);
if (self == CurrentMenu)
{ {
sel = PlayerSkins[sel];
players[consoleplayer].userinfo.SkinNumChanged(sel); players[consoleplayer].userinfo.SkinNumChanged(sel);
UpdateTranslation(); cvar_set("skin", Skins[sel].Name);
cvar_set ("skin", skins[sel].name);
li = GetItem(NAME_Playerdisplay);
if (li != NULL)
{
li->SetValue(ListMenuItemPlayerDisplay_PDF_SKIN, sel);
}
} }
return 0;
} }
//============================================================================= //=============================================================================
@ -536,14 +172,16 @@ void DPlayerMenu::SkinChanged (DMenuItemBase *li)
// //
//============================================================================= //=============================================================================
void DPlayerMenu::AutoaimChanged (DMenuItemBase *li) DEFINE_ACTION_FUNCTION(DPlayerMenu, AutoaimChanged)
{ {
int sel; PARAM_SELF_PROLOGUE(DMenu);
PARAM_FLOAT(val);
if (li->GetValue(0, &sel)) // only allow if the menu is active to prevent abuse.
if (self == CurrentMenu)
{ {
autoaim = (float)sel; autoaim = float(val);
} }
return 0;
} }
//============================================================================= //=============================================================================
@ -552,145 +190,34 @@ void DPlayerMenu::AutoaimChanged (DMenuItemBase *li)
// //
//============================================================================= //=============================================================================
bool DPlayerMenu::MenuEvent (int mkey, bool fromcontroller) DEFINE_ACTION_FUNCTION(DPlayerMenu, TeamChanged)
{ {
int v; PARAM_SELF_PROLOGUE(DMenu);
if (mDesc->mSelectedItem >= 0) PARAM_INT(val);
// only allow if the menu is active to prevent abuse.
if (self == CurrentMenu)
{ {
DMenuItemBase *li = mDesc->mItems[mDesc->mSelectedItem]; team = val == 0 ? TEAM_NONE : val - 1;
if (li->MenuEvent(mkey, fromcontroller))
{
FName current = li->GetAction(NULL);
switch(current)
{
// item specific handling comes here
case NAME_Playerbox:
if (mkey == MKEY_Input)
{
PlayerNameChanged(li);
}
break;
case NAME_Team:
if (li->GetValue(0, &v))
{
team = v==0? TEAM_NONE : v-1;
}
break;
case NAME_Color:
ColorSetChanged(li);
break;
case NAME_Red:
if (li->GetValue(0, &v))
{
uint32 color = players[consoleplayer].userinfo.GetColor();
SendNewColor (v, GPART(color), BPART(color));
}
break;
case NAME_Green:
if (li->GetValue(0, &v))
{
uint32 color = players[consoleplayer].userinfo.GetColor();
SendNewColor (RPART(color), v, BPART(color));
}
break;
case NAME_Blue:
if (li->GetValue(0, &v))
{
uint32 color = players[consoleplayer].userinfo.GetColor();
SendNewColor (RPART(color), GPART(color), v);
}
break;
case NAME_Class:
ClassChanged(li);
break;
case NAME_Skin:
SkinChanged(li);
break;
case NAME_Gender:
if (li->GetValue(0, &v))
{
cvar_set ("gender", v==0? "male" : v==1? "female" : "other");
}
break;
case NAME_Autoaim:
AutoaimChanged(li);
break;
case NAME_Switch:
if (li->GetValue(0, &v))
{
neverswitchonpickup = !!v;
}
break;
case NAME_AlwaysRun:
if (li->GetValue(0, &v))
{
cl_run = !!v;
}
break;
default:
break;
}
return true;
}
} }
return Super::MenuEvent(mkey, fromcontroller); return 0;
} }
//=============================================================================
//
//
//
//=============================================================================
bool DPlayerMenu::MouseEvent(int type, int x, int y) DEFINE_ACTION_FUNCTION(DPlayerMenu, GenderChanged)
{ {
int v; PARAM_SELF_PROLOGUE(DMenu);
DMenuItemBase *li = mFocusControl; PARAM_INT(v);
bool res = Super::MouseEvent(type, x, y); // only allow if the menu is active to prevent abuse.
if (li == NULL) li = mFocusControl; if (self == CurrentMenu)
if (li != NULL)
{ {
// Check if the colors have changed cvar_set("gender", v == 0 ? "male" : v == 1 ? "female" : "other");
FName current = li->GetAction(NULL);
switch(current)
{
case NAME_Red:
if (li->GetValue(0, &v))
{
uint32 color = players[consoleplayer].userinfo.GetColor();
SendNewColor (v, GPART(color), BPART(color));
}
break;
case NAME_Green:
if (li->GetValue(0, &v))
{
uint32 color = players[consoleplayer].userinfo.GetColor();
SendNewColor (RPART(color), v, BPART(color));
}
break;
case NAME_Blue:
if (li->GetValue(0, &v))
{
uint32 color = players[consoleplayer].userinfo.GetColor();
SendNewColor (RPART(color), GPART(color), v);
}
break;
case NAME_Autoaim:
AutoaimChanged(li);
break;
}
} }
return res; return 0;
} }
//============================================================================= //=============================================================================
@ -699,10 +226,16 @@ bool DPlayerMenu::MouseEvent(int type, int x, int y)
// //
//============================================================================= //=============================================================================
void DPlayerMenu::Ticker () DEFINE_ACTION_FUNCTION(DPlayerMenu, SwitchOnPickupChanged)
{ {
PARAM_SELF_PROLOGUE(DMenu);
Super::Ticker(); PARAM_INT(v);
// only allow if the menu is active to prevent abuse.
if (self == CurrentMenu)
{
neverswitchonpickup = !!v;
}
return 0;
} }
//============================================================================= //=============================================================================
@ -711,20 +244,14 @@ void DPlayerMenu::Ticker ()
// //
//============================================================================= //=============================================================================
void DPlayerMenu::Drawer () DEFINE_ACTION_FUNCTION(DPlayerMenu, AlwaysRunChanged)
{ {
PARAM_SELF_PROLOGUE(DMenu);
Super::Drawer(); PARAM_INT(v);
// only allow if the menu is active to prevent abuse.
const char *str = "PRESS " TEXTCOLOR_WHITE "SPACE"; if (self == CurrentMenu)
screen->DrawText (SmallFont, CR_GOLD, 320 - 32 - 32 - {
SmallFont->StringWidth (str)/2, cl_run = !!v;
50 + 48 + 70, str, }
DTA_Clean, true, TAG_DONE); return 0;
str = mRotation ? "TO SEE FRONT" : "TO SEE BACK";
screen->DrawText (SmallFont, CR_GOLD, 320 - 32 - 32 -
SmallFont->StringWidth (str)/2,
50 + 48 + 70 + SmallFont->GetHeight (), str,
DTA_Clean, true, TAG_DONE);
} }

View file

@ -1,149 +0,0 @@
/*
** 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 "g_levellocals.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, false, false)
//=============================================================================
//
// Read This Menus
//
//=============================================================================
DReadThisMenu::DReadThisMenu(DMenu *parent)
: DMenu(parent)
{
mScreen = 1;
mInfoTic = gametic;
}
//=============================================================================
//
//
//
//=============================================================================
void DReadThisMenu::Drawer()
{
FTexture *tex = NULL, *prevpic = NULL;
double alpha;
// Did the mapper choose a custom help page via MAPINFO?
if ((level.info != NULL) && level.info->F1Pic.Len() != 0)
{
tex = TexMan.FindTexture(level.info->F1Pic);
mScreen = 1;
}
if (tex == NULL)
{
tex = TexMan[gameinfo.infoPages[mScreen-1].GetChars()];
}
if (mScreen > 1)
{
prevpic = TexMan[gameinfo.infoPages[mScreen-2].GetChars()];
}
screen->Dim(0, 1.0, 0,0, SCREENWIDTH, SCREENHEIGHT);
alpha = MIN((gametic - mInfoTic) * (3. / TICRATE), 1.);
if (alpha < 1. && prevpic != NULL)
{
screen->DrawTexture (prevpic, 0, 0, DTA_Fullscreen, true, TAG_DONE);
}
screen->DrawTexture (tex, 0, 0, DTA_Fullscreen, true, 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->F1Pic.Len() != 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;
}

View file

@ -197,6 +197,10 @@ xx(PowerWeaponLevel2)
xx(PowerFlight) xx(PowerFlight)
xx(PowerSpeed) xx(PowerSpeed)
xx(PowerTorch) xx(PowerTorch)
xx(PowerHighJump)
xx(PowerReflection)
xx(PowerDrain)
xx(Reflection)
xx(CustomInventory) xx(CustomInventory)
xx(Inventory) xx(Inventory)
xx(CallTryPickup) xx(CallTryPickup)
@ -356,6 +360,7 @@ xx(CeilingZ)
xx(FloorZ) xx(FloorZ)
xx(Health) xx(Health)
xx(Pitch) xx(Pitch)
xx(SpecialName)
xx(Special) xx(Special)
xx(TID) xx(TID)
xx(TIDtoHate) xx(TIDtoHate)
@ -386,12 +391,19 @@ xx(Radius)
xx(ReactionTime) xx(ReactionTime)
xx(MeleeRange) xx(MeleeRange)
xx(Speed) xx(Speed)
xx(FastSpeed)
xx(HowlSound)
xx(Clamp) xx(Clamp)
xx(VisibleStartAngle) xx(VisibleStartAngle)
xx(VisibleStartPitch) xx(VisibleStartPitch)
xx(VisibleEndAngle) xx(VisibleEndAngle)
xx(VisibleEndPitch) xx(VisibleEndPitch)
xx(Format) xx(Format)
xx(PickupMsg)
xx(Respawnable)
xx(ExplosionDamage)
xx(ExplosionRadius)
xx(DontHurtShooter)
// Various actor names which are used internally // Various actor names which are used internally
xx(MapSpot) xx(MapSpot)
@ -653,6 +665,8 @@ xx(Link)
xx(Goodbye) xx(Goodbye)
xx(Require) xx(Require)
xx(Exclude) xx(Exclude)
xx(Userstring)
xx(Sky)
// Special menus // Special menus
xx(Mainmenu) xx(Mainmenu)
@ -670,6 +684,7 @@ xx(Optionsmenu)
xx(Quitmenu) xx(Quitmenu)
xx(Savemenu) xx(Savemenu)
xx(Playermenu) xx(Playermenu)
xx(EndGameMenu)
xx(Playerbox) xx(Playerbox)
xx(Team) xx(Team)
@ -765,6 +780,7 @@ xx(BuiltinGetDefault)
xx(BuiltinClassCast) xx(BuiltinClassCast)
xx(BuiltinFormat) xx(BuiltinFormat)
xx(Damage) xx(Damage)
xx(Noattack)
// basic type names // basic type names
xx(Default) xx(Default)

View file

@ -818,41 +818,57 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou
newvert.y += fixed_t(frac * double(Vertices[seg->v2].y - newvert.y)); newvert.y += fixed_t(frac * double(Vertices[seg->v2].y - newvert.y));
vertnum = VertexMap->SelectVertexClose (newvert); vertnum = VertexMap->SelectVertexClose (newvert);
if (vertnum == (unsigned int)seg->v1 || vertnum == (unsigned int)seg->v2) if (vertnum != (unsigned int)seg->v1 && vertnum != (unsigned int)seg->v2)
{ {
Printf("SelectVertexClose selected endpoint of seg %u\n", set); 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, 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
// been split, and we would not be in this default case.
// Moreover, the back seg may not even be in the set being
// split, so we must not move its pieces into the out sets.
Segs[partner1].next = partner2;
Segs[partner2].partner = seg2;
Segs[seg2].partner = partner2;
}
if (GLNodes)
{
AddIntersection(node, vertnum);
}
} }
else
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; // all that matters here is to prevent a crash so we must make sure that we do not end up with all segs being sorted to the same side - even if this may not be correct.
int partner2 = SplitSeg (partner1, vertnum, sidev[1]); // But if we do not do that this code would not be able to move on. Just discarding the seg is also not an option because it won't guarantee that we achieve an actual split.
// The newly created seg stays in the same set as the if (_count0 == 0)
// back seg because it has not been considered for splitting {
// yet. If it had been, then the front seg would have already side = 0;
// been split, and we would not be in this default case. seg->next = outset0;
// Moreover, the back seg may not even be in the set being outset0 = set;
// split, so we must not move its pieces into the out sets. _count0++;
Segs[partner1].next = partner2; }
Segs[partner2].partner = seg2; else
Segs[seg2].partner = partner2; {
side = 1;
seg->next = outset1;
outset1 = set;
_count1++;
}
} }
if (GLNodes)
{
AddIntersection (node, vertnum);
}
break; break;
} }
if (side >= 0 && GLNodes) if (side >= 0 && GLNodes)
@ -1062,95 +1078,3 @@ void FNodeBuilder::PrintSet (int l, DWORD set)
} }
Printf (PRINT_LOG, "*\n"); 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);
#ifndef DISABLE_SSE
if (CPU.bSSE2)
{
func = ClassifyLineSSE2;
diff = int((char *)ClassifyLineSSE2 - (char *)calleroffset);
}
else
#endif
{
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

@ -53,22 +53,6 @@ struct FSimpleVert
fixed_t x, y; 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 class FNodeBuilder
{ {
struct FPrivSeg struct FPrivSeg
@ -282,7 +266,7 @@ private:
// 1 = seg is in back // 1 = seg is in back
// -1 = seg cuts the node // -1 = seg cuts the node
inline int ClassifyLine (node_t &node, const FPrivVert *v1, const FPrivVert *v2, int sidev[2]); int ClassifyLine (node_t &node, const FPrivVert *v1, const FPrivVert *v2, int sidev[2]);
void FixSplitSharers (const node_t &node); void FixSplitSharers (const node_t &node);
double AddIntersection (const node_t &node, int vertex); double AddIntersection (const node_t &node, int vertex);
@ -341,28 +325,3 @@ inline int FNodeBuilder::PointOnSide (int x, int y, int x1, int y1, int dx, int
} }
return s_num > 0.0 ? -1 : 1; return s_num > 0.0 ? -1 : 1;
} }
inline int FNodeBuilder::ClassifyLine (node_t &node, const FPrivVert *v1, const FPrivVert *v2, int sidev[2])
{
#ifdef DISABLE_SSE
return ClassifyLine2 (node, v1, v2, sidev);
#else
#if defined(__SSE2__) || defined(_M_X64)
// If compiling with SSE2 support everywhere, just use the SSE2 version.
return ClassifyLineSSE2 (node, v1, v2, sidev);
#elif defined(_MSC_VER) && _MSC_VER < 1300
// VC 6 does not support SSE optimizations.
return ClassifyLine2 (node, v1, v2, sidev);
#else
// Select the routine based on our flag.
#ifdef BACKPATCH
return ClassifyLineBackpatch (node, v1, v2, sidev);
#else
if (CPU.bSSE2)
return ClassifyLineSSE2 (node, v1, v2, sidev);
else
return ClassifyLine2 (node, v1, v2, sidev);
#endif
#endif
#endif
}

View file

@ -3,7 +3,7 @@
#define FAR_ENOUGH 17179869184.f // 4<<32 #define FAR_ENOUGH 17179869184.f // 4<<32
extern "C" int ClassifyLine2 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]) int FNodeBuilder::ClassifyLine(node_t &node, const FPrivVert *v1, const FPrivVert *v2, int sidev[2])
{ {
double d_x1 = double(node.x); double d_x1 = double(node.x);
double d_y1 = double(node.y); double d_y1 = double(node.y);

View file

@ -1,144 +0,0 @@
#ifndef DISABLE_SSE
#include "doomtype.h"
#include "nodebuild.h"
#define FAR_ENOUGH 17179869184.f // 4<<32
// 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.
extern "C" int ClassifyLineSSE2 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2])
{
double d_x1 = double(node.x);
double d_y1 = double(node.y);
double d_dx = double(node.dx);
double d_dy = double(node.dy);
double d_xv1 = double(v1->x);
double d_xv2 = double(v2->x);
double d_yv1 = double(v1->y);
double d_yv2 = double(v2->y);
double s_num1 = (d_y1 - d_yv1) * d_dx - (d_x1 - d_xv1) * d_dy;
double s_num2 = (d_y1 - d_yv2) * d_dx - (d_x1 - d_xv2) * d_dy;
int nears = 0;
if (s_num1 <= -FAR_ENOUGH)
{
if (s_num2 <= -FAR_ENOUGH)
{
sidev[0] = sidev[1] = 1;
return 1;
}
if (s_num2 >= FAR_ENOUGH)
{
sidev[0] = 1;
sidev[1] = -1;
return -1;
}
nears = 1;
}
else if (s_num1 >= FAR_ENOUGH)
{
if (s_num2 >= FAR_ENOUGH)
{
sidev[0] = sidev[1] = -1;
return 0;
}
if (s_num2 <= -FAR_ENOUGH)
{
sidev[0] = -1;
sidev[1] = 1;
return -1;
}
nears = 1;
}
else
{
nears = 2 | int(fabs(s_num2) < FAR_ENOUGH);
}
if (nears)
{
double l = 1.f / (d_dx*d_dx + d_dy*d_dy);
if (nears & 2)
{
double dist = s_num1 * s_num1 * l;
if (dist < SIDE_EPSILON*SIDE_EPSILON)
{
sidev[0] = 0;
}
else
{
sidev[0] = s_num1 > 0.0 ? -1 : 1;
}
}
else
{
sidev[0] = s_num1 > 0.0 ? -1 : 1;
}
if (nears & 1)
{
double dist = s_num2 * s_num2 * l;
if (dist < SIDE_EPSILON*SIDE_EPSILON)
{
sidev[1] = 0;
}
else
{
sidev[1] = s_num2 > 0.0 ? -1 : 1;
}
}
else
{
sidev[1] = s_num2 > 0.0 ? -1 : 1;
}
}
else
{
sidev[0] = s_num1 > 0.0 ? -1 : 1;
sidev[1] = s_num2 > 0.0 ? -1 : 1;
}
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.
if (node.dx != 0)
{
if ((node.dx > 0 && v2->x > v1->x) || (node.dx < 0 && v2->x < v1->x))
{
return 0;
}
else
{
return 1;
}
}
else
{
if ((node.dy > 0 && v2->y > v1->y) || (node.dy < 0 && v2->y < v1->y))
{
return 0;
}
else
{
return 1;
}
}
}
else if (sidev[0] <= 0 && sidev[1] <= 0)
{
return 0;
}
else if (sidev[0] >= 0 && sidev[1] >= 0)
{
return 1;
}
return -1;
}
#endif

View file

@ -35,7 +35,7 @@ static FRandom pr_opl;
typedef uintptr_t Bitu; typedef uintptr_t Bitu;
typedef intptr_t Bits; typedef intptr_t Bits;
typedef DWORD Bit32u; typedef DWORD Bit32u;
typedef SDWORD Bit32s; typedef int32_t Bit32s;
typedef WORD Bit16u; typedef WORD Bit16u;
typedef SWORD Bit16s; typedef SWORD Bit16s;
typedef BYTE Bit8u; typedef BYTE Bit8u;

View file

@ -35,7 +35,7 @@
typedef uintptr_t Bitu; typedef uintptr_t Bitu;
typedef intptr_t Bits; typedef intptr_t Bits;
typedef DWORD Bit32u; typedef DWORD Bit32u;
typedef SDWORD Bit32s; typedef int32_t Bit32s;
typedef WORD Bit16u; typedef WORD Bit16u;
typedef SWORD Bit16s; typedef SWORD Bit16s;
typedef BYTE Bit8u; typedef BYTE Bit8u;

View file

@ -180,7 +180,7 @@ inline int PitchToACS(DAngle ang)
struct CallReturn struct CallReturn
{ {
CallReturn(int pc, ScriptFunction *func, FBehavior *module, SDWORD *locals, ACSLocalArrays *arrays, bool discard, unsigned int runaway) CallReturn(int pc, ScriptFunction *func, FBehavior *module, int32_t *locals, ACSLocalArrays *arrays, bool discard, unsigned int runaway)
: ReturnFunction(func), : ReturnFunction(func),
ReturnModule(module), ReturnModule(module),
ReturnLocals(locals), ReturnLocals(locals),
@ -192,7 +192,7 @@ struct CallReturn
ScriptFunction *ReturnFunction; ScriptFunction *ReturnFunction;
FBehavior *ReturnModule; FBehavior *ReturnModule;
SDWORD *ReturnLocals; int32_t *ReturnLocals;
ACSLocalArrays *ReturnArrays; ACSLocalArrays *ReturnArrays;
int ReturnAddress; int ReturnAddress;
int bDiscardResult; int bDiscardResult;
@ -206,7 +206,7 @@ static DLevelScript *P_GetScriptGoing (AActor *who, line_t *where, int num, cons
struct FBehavior::ArrayInfo struct FBehavior::ArrayInfo
{ {
DWORD ArraySize; DWORD ArraySize;
SDWORD *Elements; int32_t *Elements;
}; };
TArray<FBehavior *> FBehavior::StaticModules; TArray<FBehavior *> FBehavior::StaticModules;
@ -243,11 +243,11 @@ inline int uallong(const int &foo)
//============================================================================ //============================================================================
// ACS variables with world scope // ACS variables with world scope
SDWORD ACS_WorldVars[NUM_WORLDVARS]; int32_t ACS_WorldVars[NUM_WORLDVARS];
FWorldGlobalArray ACS_WorldArrays[NUM_WORLDVARS]; FWorldGlobalArray ACS_WorldArrays[NUM_WORLDVARS];
// ACS variables with global scope // ACS variables with global scope
SDWORD ACS_GlobalVars[NUM_GLOBALVARS]; int32_t ACS_GlobalVars[NUM_GLOBALVARS];
FWorldGlobalArray ACS_GlobalArrays[NUM_GLOBALVARS]; FWorldGlobalArray ACS_GlobalArrays[NUM_GLOBALVARS];
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -261,7 +261,7 @@ FWorldGlobalArray ACS_GlobalArrays[NUM_GLOBALVARS];
struct FACSStack struct FACSStack
{ {
SDWORD buffer[STACK_SIZE]; int32_t buffer[STACK_SIZE];
int sp; int sp;
FACSStack *next; FACSStack *next;
FACSStack *prev; FACSStack *prev;
@ -979,7 +979,7 @@ void P_ClearACSVars(bool alsoglobal)
// //
//============================================================================ //============================================================================
static void WriteVars (FSerializer &file, SDWORD *vars, size_t count, const char *key) static void WriteVars (FSerializer &file, int32_t *vars, size_t count, const char *key)
{ {
size_t i, j; size_t i, j;
@ -1007,7 +1007,7 @@ static void WriteVars (FSerializer &file, SDWORD *vars, size_t count, const char
// //
//============================================================================ //============================================================================
static void ReadVars (FSerializer &arc, SDWORD *vars, size_t count, const char *key) static void ReadVars (FSerializer &arc, int32_t *vars, size_t count, const char *key)
{ {
memset(&vars[0], 0, count * 4); memset(&vars[0], 0, count * 4);
arc.Array(key, vars, (int)count); arc.Array(key, vars, (int)count);
@ -1330,7 +1330,7 @@ static int CheckInventory (AActor *activator, const char *type, bool max)
if (max) if (max)
{ {
if (activator->IsKindOf (RUNTIME_CLASS (APlayerPawn))) if (activator->IsKindOf (RUNTIME_CLASS (APlayerPawn)))
return static_cast<APlayerPawn *>(activator)->MaxHealth; return static_cast<APlayerPawn *>(activator)->GetMaxHealth();
else else
return activator->SpawnHealth(); return activator->SpawnHealth();
} }
@ -1684,10 +1684,10 @@ void FBehavior::SerializeVars (FSerializer &arc)
} }
} }
void FBehavior::SerializeVarSet (FSerializer &arc, SDWORD *vars, int max) void FBehavior::SerializeVarSet (FSerializer &arc, int32_t *vars, int max)
{ {
SDWORD count; int32_t count;
SDWORD first, last; int32_t first, last;
if (arc.BeginObject(nullptr)) if (arc.BeginObject(nullptr))
{ {
@ -1997,7 +1997,7 @@ bool FBehavior::Init(int lumpnum, FileReader * fr, int len)
{ {
MapVarStore[LittleLong(chunk[2+i*2])] = i; MapVarStore[LittleLong(chunk[2+i*2])] = i;
ArrayStore[i].ArraySize = LittleLong(chunk[3+i*2]); ArrayStore[i].ArraySize = LittleLong(chunk[3+i*2]);
ArrayStore[i].Elements = new SDWORD[ArrayStore[i].ArraySize]; ArrayStore[i].Elements = new int32_t[ArrayStore[i].ArraySize];
memset(ArrayStore[i].Elements, 0, ArrayStore[i].ArraySize*sizeof(DWORD)); memset(ArrayStore[i].Elements, 0, ArrayStore[i].ArraySize*sizeof(DWORD));
} }
} }
@ -2013,7 +2013,7 @@ bool FBehavior::Init(int lumpnum, FileReader * fr, int len)
// optimizer. Might be some undefined behavior in this code, // optimizer. Might be some undefined behavior in this code,
// but I don't know what it is. // but I don't know what it is.
unsigned int initsize = MIN<unsigned int> (ArrayStore[arraynum].ArraySize, (LittleLong(chunk[1])-4)/4); unsigned int initsize = MIN<unsigned int> (ArrayStore[arraynum].ArraySize, (LittleLong(chunk[1])-4)/4);
SDWORD *elems = ArrayStore[arraynum].Elements; int32_t *elems = ArrayStore[arraynum].Elements;
for (unsigned int j = 0; j < initsize; ++j) for (unsigned int j = 0; j < initsize; ++j)
{ {
elems[j] = LittleLong(chunk[3+j]); elems[j] = LittleLong(chunk[3+j]);
@ -2062,7 +2062,7 @@ bool FBehavior::Init(int lumpnum, FileReader * fr, int len)
int arraynum = MapVarStore[LittleLong(chunk[i+2])]; int arraynum = MapVarStore[LittleLong(chunk[i+2])];
if ((unsigned)arraynum < (unsigned)NumArrays) if ((unsigned)arraynum < (unsigned)NumArrays)
{ {
SDWORD *elems = ArrayStore[arraynum].Elements; int32_t *elems = ArrayStore[arraynum].Elements;
for (int j = ArrayStore[arraynum].ArraySize; j > 0; --j, ++elems) for (int j = ArrayStore[arraynum].ArraySize; j > 0; --j, ++elems)
{ {
// *elems |= LibraryID; // *elems |= LibraryID;
@ -2088,7 +2088,7 @@ bool FBehavior::Init(int lumpnum, FileReader * fr, int len)
chunkData += 4; chunkData += 4;
if ((unsigned)arraynum < (unsigned)NumArrays) if ((unsigned)arraynum < (unsigned)NumArrays)
{ {
SDWORD *elems = ArrayStore[arraynum].Elements; int32_t *elems = ArrayStore[arraynum].Elements;
// Ending zeros may be left out. // Ending zeros may be left out.
for (int j = MIN(LittleLong(chunk[1])-5, ArrayStore[arraynum].ArraySize); j > 0; --j, ++elems, ++chunkData) for (int j = MIN(LittleLong(chunk[1])-5, ArrayStore[arraynum].ArraySize); j > 0; --j, ++elems, ++chunkData)
{ {
@ -2821,6 +2821,8 @@ void FBehavior::StaticStartTypedScripts (WORD type, AActor *activator, bool alwa
"Unloading", "Unloading",
"Disconnect", "Disconnect",
"Return", "Return",
"Event",
"Kill",
"Reopen" "Reopen"
}; };
DPrintf(DMSG_NOTIFY, "Starting all scripts of type %d (%s)\n", type, DPrintf(DMSG_NOTIFY, "Starting all scripts of type %d (%s)\n", type,
@ -3937,7 +3939,7 @@ int DLevelScript::GetActorProperty (int tid, int property)
case APROP_Dormant: return !!(actor->flags2 & MF2_DORMANT); case APROP_Dormant: return !!(actor->flags2 & MF2_DORMANT);
case APROP_SpawnHealth: if (actor->IsKindOf (RUNTIME_CLASS (APlayerPawn))) case APROP_SpawnHealth: if (actor->IsKindOf (RUNTIME_CLASS (APlayerPawn)))
{ {
return static_cast<APlayerPawn *>(actor)->MaxHealth; return static_cast<APlayerPawn *>(actor)->GetMaxHealth();
} }
else else
{ {
@ -4230,7 +4232,7 @@ enum
SOUND_Howl, SOUND_Howl,
}; };
static FSoundID GetActorSound(const AActor *actor, int soundtype) static FSoundID GetActorSound(AActor *actor, int soundtype)
{ {
switch (soundtype) switch (soundtype)
{ {
@ -4243,7 +4245,7 @@ static FSoundID GetActorSound(const AActor *actor, int soundtype)
case SOUND_Bounce: return actor->BounceSound; case SOUND_Bounce: return actor->BounceSound;
case SOUND_WallBounce: return actor->WallBounceSound; case SOUND_WallBounce: return actor->WallBounceSound;
case SOUND_CrushPain: return actor->CrushPainSound; case SOUND_CrushPain: return actor->CrushPainSound;
case SOUND_Howl: return actor->GetClass()->HowlSound; case SOUND_Howl: return actor->SoundVar(NAME_HowlSound);
default: return 0; default: return 0;
} }
} }
@ -4365,6 +4367,11 @@ enum EACSFunctions
ACSF_SetTranslation, ACSF_SetTranslation,
ACSF_GetActorFloorTexture, ACSF_GetActorFloorTexture,
ACSF_GetActorFloorTerrain, ACSF_GetActorFloorTerrain,
ACSF_StrArg,
ACSF_Floor,
ACSF_Round,
ACSF_Ceil,
ACSF_ScriptCall,
// OpenGL stuff // OpenGL stuff
@ -4747,9 +4754,121 @@ static int SwapActorTeleFog(AActor *activator, int tid)
return count; return count;
} }
static int ScriptCall(unsigned argc, int32_t *args)
{
int retval = 0;
if (argc >= 2)
{
auto clsname = FBehavior::StaticLookupString(args[0]);
auto funcname = FBehavior::StaticLookupString(args[1]);
auto cls = PClass::FindClass(clsname);
if (!cls)
{
I_Error("ACS call to unknown class in script function %s.%s", cls, funcname);
}
auto funcsym = dyn_cast<PFunction>(cls->Symbols.FindSymbol(funcname, true));
if (funcsym == nullptr)
{
I_Error("ACS call to unknown script function %s.%s", cls, funcname);
}
auto func = funcsym->Variants[0].Implementation;
if (func->ImplicitArgs > 0)
{
I_Error("ACS call to non-static script function %s.%s", cls, funcname);
}
TArray<VMValue> params;
for (unsigned i = 2; i < argc; i++)
{
if (func->Proto->ArgumentTypes.Size() < i - 1)
{
I_Error("Too many parameters in call to %s.%s", cls, funcname);
}
auto argtype = func->Proto->ArgumentTypes[i - 2];
// The only types allowed are int, bool, double, Name, Sound, Color and String
if (argtype == TypeSInt32 || argtype == TypeColor)
{
params.Push(args[i]);
}
else if (argtype == TypeBool)
{
params.Push(!!args[i]);
}
else if (argtype == TypeFloat64)
{
params.Push(ACSToDouble(args[i]));
}
else if (argtype == TypeName)
{
params.Push(FName(FBehavior::StaticLookupString(args[i])).GetIndex());
}
else if (argtype == TypeString)
{
params.Push(FBehavior::StaticLookupString(args[i]));
}
else if (argtype == TypeSound)
{
params.Push(int(FSoundID(FBehavior::StaticLookupString(args[i]))));
}
else
{
I_Error("Invalid type %s in call to %s.%s", argtype->DescriptiveName(), cls, funcname);
}
}
if (func->Proto->ArgumentTypes.Size() > params.Size())
{
// Check if we got enough parameters. That means we either cover the full argument list of the function or the next argument is optional.
if (!(funcsym->Variants[0].ArgFlags[params.Size()] & VARF_Optional))
{
I_Error("Insufficient parameters in call to %s.%s", cls, funcname);
}
}
// The return value can be the same types as the parameter types, plus void
if (func->Proto->ReturnTypes.Size() == 0)
{
GlobalVMStack.Call(func, &params[0], params.Size(), nullptr, 0);
}
else
{
auto rettype = func->Proto->ReturnTypes[0];
if (rettype == TypeSInt32 || rettype == TypeBool || rettype == TypeColor || rettype == TypeName || rettype == TypeSound)
{
VMReturn ret(&retval);
GlobalVMStack.Call(func, &params[0], params.Size(), &ret, 1);
if (rettype == TypeName)
{
retval = GlobalACSStrings.AddString(FName(ENamedName(retval)));
}
else if (rettype == TypeSound)
{
retval = GlobalACSStrings.AddString(FSoundID(retval));
}
}
else if (rettype == TypeFloat64)
{
double d;
VMReturn ret(&d);
GlobalVMStack.Call(func, &params[0], params.Size(), &ret, 1);
retval = DoubleToACS(d);
}
else if (rettype == TypeString)
{
FString d;
VMReturn ret(&d);
GlobalVMStack.Call(func, &params[0], params.Size(), &ret, 1);
retval = GlobalACSStrings.AddString(d);
}
else
{
// All other return values can not be handled so ignore them.
GlobalVMStack.Call(func, &params[0], params.Size(), nullptr, 0);
}
}
}
return retval;
}
int DLevelScript::CallFunction(int argCount, int funcIndex, SDWORD *args) int DLevelScript::CallFunction(int argCount, int funcIndex, int32_t *args)
{ {
AActor *actor; AActor *actor;
switch(funcIndex) switch(funcIndex)
@ -6085,7 +6204,20 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
break; break;
} }
case ACSF_StrArg:
return -FName(FBehavior::StaticLookupString(args[0]));
case ACSF_Floor:
return args[0] & ~0xffff;
case ACSF_Ceil:
return (args[0] & ~0xffff) + 0x10000;
case ACSF_Round:
return (args[0] + 32768) & ~0xffff;
case ACSF_ScriptCall:
return ScriptCall(argCount, args);
default: default:
break; break;
@ -6176,7 +6308,7 @@ static void SetMarineSprite(AActor *marine, PClassActor *source)
int DLevelScript::RunScript () int DLevelScript::RunScript ()
{ {
DACSThinker *controller = DACSThinker::ActiveThinker; DACSThinker *controller = DACSThinker::ActiveThinker;
SDWORD *locals = &Localvars[0]; int32_t *locals = &Localvars[0];
ACSLocalArrays noarrays; ACSLocalArrays noarrays;
ACSLocalArrays *localarrays = &noarrays; ACSLocalArrays *localarrays = &noarrays;
ScriptFunction *activeFunction = NULL; ScriptFunction *activeFunction = NULL;
@ -6250,7 +6382,7 @@ int DLevelScript::RunScript ()
} }
FACSStack stackobj; FACSStack stackobj;
SDWORD *Stack = stackobj.buffer; int32_t *Stack = stackobj.buffer;
int &sp = stackobj.sp; int &sp = stackobj.sp;
int *pc = this->pc; int *pc = this->pc;
@ -6544,7 +6676,7 @@ int DLevelScript::RunScript ()
int i; int i;
ScriptFunction *func; ScriptFunction *func;
FBehavior *module; FBehavior *module;
SDWORD *mylocals; int32_t *mylocals;
if(pcd == PCD_CALLSTACK) if(pcd == PCD_CALLSTACK)
{ {
@ -6599,7 +6731,7 @@ int DLevelScript::RunScript ()
int value; int value;
union union
{ {
SDWORD *retsp; int32_t *retsp;
CallReturn *ret; CallReturn *ret;
}; };
@ -7754,7 +7886,7 @@ scriptwait:
while (min <= max) while (min <= max)
{ {
int mid = (min + max) / 2; int mid = (min + max) / 2;
SDWORD caseval = LittleLong(pc[mid*2]); int32_t caseval = LittleLong(pc[mid*2]);
if (caseval == STACK(1)) if (caseval == STACK(1))
{ {
pc = activeBehavior->Ofs2PC (LittleLong(pc[mid*2+1])); pc = activeBehavior->Ofs2PC (LittleLong(pc[mid*2+1]));
@ -9169,7 +9301,7 @@ scriptwait:
const char *str = FBehavior::StaticLookupString(STACK(1)); const char *str = FBehavior::StaticLookupString(STACK(1));
if (str != NULL) if (str != NULL)
{ {
STACK(1) = SDWORD(strlen(str)); STACK(1) = int32_t(strlen(str));
break; break;
} }
@ -9339,7 +9471,7 @@ scriptwait:
switch (STACK(1)) switch (STACK(1))
{ {
case PLAYERINFO_TEAM: STACK(2) = userinfo->GetTeam(); break; case PLAYERINFO_TEAM: STACK(2) = userinfo->GetTeam(); break;
case PLAYERINFO_AIMDIST: STACK(2) = (SDWORD)(userinfo->GetAimDist() * (0x40000000/90.)); break; // Yes, this has been returning a BAM since its creation. case PLAYERINFO_AIMDIST: STACK(2) = (int32_t)(userinfo->GetAimDist() * (0x40000000/90.)); break; // Yes, this has been returning a BAM since its creation.
case PLAYERINFO_COLOR: STACK(2) = userinfo->GetColor(); break; case PLAYERINFO_COLOR: STACK(2) = userinfo->GetColor(); break;
case PLAYERINFO_GENDER: STACK(2) = userinfo->GetGender(); break; case PLAYERINFO_GENDER: STACK(2) = userinfo->GetGender(); break;
case PLAYERINFO_NEVERSWITCH: STACK(2) = userinfo->GetNeverSwitch(); break; case PLAYERINFO_NEVERSWITCH: STACK(2) = userinfo->GetNeverSwitch(); break;
@ -9421,7 +9553,10 @@ scriptwait:
} }
case PCD_SETMUGSHOTSTATE: case PCD_SETMUGSHOTSTATE:
StatusBar->SetMugShotState(FBehavior::StaticLookupString(STACK(1))); if (!multiplayer || (activator != nullptr && activator->CheckLocalView(consoleplayer)))
{
StatusBar->SetMugShotState(FBehavior::StaticLookupString(STACK(1)));
}
sp--; sp--;
break; break;
@ -9743,7 +9878,7 @@ DLevelScript::DLevelScript (AActor *who, line_t *where, int num, const ScriptPtr
script = num; script = num;
assert(code->VarCount >= code->ArgCount); assert(code->VarCount >= code->ArgCount);
Localvars.Resize(code->VarCount); Localvars.Resize(code->VarCount);
memset(&Localvars[0], 0, code->VarCount * sizeof(SDWORD)); memset(&Localvars[0], 0, code->VarCount * sizeof(int32_t));
for (int i = 0; i < MIN<int>(argcount, code->ArgCount); ++i) for (int i = 0; i < MIN<int>(argcount, code->ArgCount); ++i)
{ {
Localvars[i] = args[i]; Localvars[i] = args[i];

View file

@ -60,14 +60,14 @@ struct InitIntToZero
v = 0; v = 0;
} }
}; };
typedef TMap<SDWORD, SDWORD, THashTraits<SDWORD>, InitIntToZero> FWorldGlobalArray; typedef TMap<int32_t, int32_t, THashTraits<int32_t>, InitIntToZero> FWorldGlobalArray;
// ACS variables with world scope // ACS variables with world scope
extern SDWORD ACS_WorldVars[NUM_WORLDVARS]; extern int32_t ACS_WorldVars[NUM_WORLDVARS];
extern FWorldGlobalArray ACS_WorldArrays[NUM_WORLDVARS]; extern FWorldGlobalArray ACS_WorldArrays[NUM_WORLDVARS];
// ACS variables with global scope // ACS variables with global scope
extern SDWORD ACS_GlobalVars[NUM_GLOBALVARS]; extern int32_t ACS_GlobalVars[NUM_GLOBALVARS];
extern FWorldGlobalArray ACS_GlobalArrays[NUM_GLOBALVARS]; extern FWorldGlobalArray ACS_GlobalArrays[NUM_GLOBALVARS];
#define LIBRARYID_MASK 0xFFF00000 #define LIBRARYID_MASK 0xFFF00000
@ -323,7 +323,7 @@ public:
ACSProfileInfo *GetFunctionProfileData(ScriptFunction *func) { return GetFunctionProfileData((int)(func - (ScriptFunction *)Functions)); } ACSProfileInfo *GetFunctionProfileData(ScriptFunction *func) { return GetFunctionProfileData((int)(func - (ScriptFunction *)Functions)); }
const char *LookupString (DWORD index) const; const char *LookupString (DWORD index) const;
SDWORD *MapVars[NUM_MAPVARS]; int32_t *MapVars[NUM_MAPVARS];
static FBehavior *StaticLoadModule (int lumpnum, FileReader * fr=NULL, int len=0); static FBehavior *StaticLoadModule (int lumpnum, FileReader * fr=NULL, int len=0);
static void StaticLoadDefaultModules (); static void StaticLoadDefaultModules ();
@ -359,7 +359,7 @@ private:
ArrayInfo **Arrays; ArrayInfo **Arrays;
int NumTotalArrays; int NumTotalArrays;
DWORD StringTable; DWORD StringTable;
SDWORD MapVarStore[NUM_MAPVARS]; int32_t MapVarStore[NUM_MAPVARS];
TArray<FBehavior *> Imports; TArray<FBehavior *> Imports;
DWORD LibraryID; DWORD LibraryID;
char ModuleName[9]; char ModuleName[9];
@ -375,7 +375,7 @@ private:
int FindStringInChunk (DWORD *chunk, const char *varname) const; int FindStringInChunk (DWORD *chunk, const char *varname) const;
void SerializeVars (FSerializer &arc); void SerializeVars (FSerializer &arc);
void SerializeVarSet (FSerializer &arc, SDWORD *vars, int max); void SerializeVarSet (FSerializer &arc, int32_t *vars, int max);
void MarkMapVarStrings() const; void MarkMapVarStrings() const;
void LockMapVarStrings() const; void LockMapVarStrings() const;
@ -919,7 +919,7 @@ protected:
int DoSpawnSpot (int type, int spot, int tid, int angle, bool forced); int DoSpawnSpot (int type, int spot, int tid, int angle, bool forced);
int DoSpawnSpotFacing (int type, int spot, int tid, bool forced); int DoSpawnSpotFacing (int type, int spot, int tid, bool forced);
int DoClassifyActor (int tid); int DoClassifyActor (int tid);
int CallFunction(int argCount, int funcIndex, SDWORD *args); int CallFunction(int argCount, int funcIndex, int32_t *args);
void DoFadeTo (int r, int g, int b, int a, int time); void DoFadeTo (int r, int g, int b, int a, int time);
void DoFadeRange (int r1, int g1, int b1, int a1, void DoFadeRange (int r1, int g1, int b1, int a1,

View file

@ -80,12 +80,12 @@
#include "math/cmath.h" #include "math/cmath.h"
#include "g_levellocals.h" #include "g_levellocals.h"
#include "r_utility.h" #include "r_utility.h"
#include "sbar.h"
AActor *SingleActorFromTID(int tid, AActor *defactor); AActor *SingleActorFromTID(int tid, AActor *defactor);
static FRandom pr_camissile ("CustomActorfire"); static FRandom pr_camissile ("CustomActorfire");
static FRandom pr_camelee ("CustomMelee");
static FRandom pr_cabullet ("CustomBullet"); static FRandom pr_cabullet ("CustomBullet");
static FRandom pr_cajump ("CustomJump"); static FRandom pr_cajump ("CustomJump");
static FRandom pr_cwbullet ("CustomWpBullet"); static FRandom pr_cwbullet ("CustomWpBullet");
@ -438,22 +438,6 @@ DEFINE_ACTION_FUNCTION(AActor, GetSpawnHealth)
return 0; return 0;
} }
//==========================================================================
//
// GetGibHealth
//
//==========================================================================
DEFINE_ACTION_FUNCTION(AActor, GetGibHealth)
{
if (numret > 0)
{
PARAM_SELF_PROLOGUE(AActor);
ret->SetInt(self->GetGibHealth());
return 1;
}
return 0;
}
//========================================================================== //==========================================================================
// //
// GetSpriteAngle // GetSpriteAngle
@ -923,86 +907,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_CopyFriendliness)
return 0; return 0;
} }
//==========================================================================
//
// Customizable attack functions which use actor parameters.
//
//==========================================================================
static void DoAttack (AActor *self, bool domelee, bool domissile,
int MeleeDamage, FSoundID MeleeSound, PClassActor *MissileType,double MissileHeight)
{
if (self->target == NULL) return;
A_FaceTarget (self);
if (domelee && MeleeDamage>0 && self->CheckMeleeRange ())
{
int damage = pr_camelee.HitDice(MeleeDamage);
if (MeleeSound) S_Sound (self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
}
else if (domissile && MissileType != NULL)
{
// This seemingly senseless code is needed for proper aiming.
double add = MissileHeight + self->GetBobOffset() - 32;
self->AddZ(add);
AActor *missile = P_SpawnMissileXYZ (self->PosPlusZ(32.), self, self->target, MissileType, false);
self->AddZ(-add);
if (missile)
{
// automatic handling of seeker missiles
if (missile->flags2&MF2_SEEKERMISSILE)
{
missile->tracer=self->target;
}
P_CheckMissileSpawn(missile, self->radius);
}
}
}
DEFINE_ACTION_FUNCTION(AActor, A_MeleeAttack)
{
PARAM_SELF_PROLOGUE(AActor);
int MeleeDamage = self->GetClass()->MeleeDamage;
FSoundID MeleeSound = self->GetClass()->MeleeSound;
DoAttack(self, true, false, MeleeDamage, MeleeSound, NULL, 0);
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_MissileAttack)
{
PARAM_SELF_PROLOGUE(AActor);
PClassActor *MissileType = PClass::FindActor(self->GetClass()->MissileName);
DoAttack(self, false, true, 0, 0, MissileType, self->GetClass()->MissileHeight);
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_ComboAttack)
{
PARAM_SELF_PROLOGUE(AActor);
int MeleeDamage = self->GetClass()->MeleeDamage;
FSoundID MeleeSound = self->GetClass()->MeleeSound;
PClassActor *MissileType = PClass::FindActor(self->GetClass()->MissileName);
DoAttack(self, true, true, MeleeDamage, MeleeSound, MissileType, self->GetClass()->MissileHeight);
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_BasicAttack)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_INT (melee_damage);
PARAM_SOUND (melee_sound);
PARAM_CLASS (missile_type, AActor);
PARAM_FLOAT (missile_height);
if (missile_type != NULL)
{
DoAttack(self, true, true, melee_damage, melee_sound, missile_type, missile_height);
}
return 0;
}
//========================================================================== //==========================================================================
// //
// Custom sound functions. // Custom sound functions.
@ -1124,7 +1028,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_SeekerMissile)
PARAM_INT_DEF(chance); PARAM_INT_DEF(chance);
PARAM_INT_DEF(distance); PARAM_INT_DEF(distance);
if ((flags & SMF_LOOK) && (self->tracer == 0) && (pr_seekermissile()<chance)) if ((flags & SMF_LOOK) && (self->tracer == nullptr) && (pr_seekermissile()<chance))
{ {
self->tracer = P_RoughMonsterSearch (self, distance, true); self->tracer = P_RoughMonsterSearch (self, distance, true);
} }
@ -1260,9 +1164,9 @@ DEFINE_ACTION_FUNCTION(AActor, A_Explode)
if (damage < 0) // get parameters from metadata if (damage < 0) // get parameters from metadata
{ {
damage = self->GetClass()->ExplosionDamage; damage = self->IntVar(NAME_ExplosionDamage);
distance = self->GetClass()->ExplosionRadius; distance = self->IntVar(NAME_ExplosionRadius);
flags = !self->GetClass()->DontHurtShooter; flags = !self->BoolVar(NAME_DontHurtShooter);
alert = false; alert = false;
} }
if (distance <= 0) distance = damage; if (distance <= 0) distance = damage;
@ -1410,26 +1314,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_RadiusDamageSelf)
return 0; return 0;
} }
//==========================================================================
//
// Execute a line special / script
//
//==========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CallSpecial)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_INT (special);
PARAM_INT_DEF (arg1);
PARAM_INT_DEF (arg2);
PARAM_INT_DEF (arg3);
PARAM_INT_DEF (arg4);
PARAM_INT_DEF (arg5);
bool res = !!P_ExecuteSpecial(special, NULL, self, false, arg1, arg2, arg3, arg4, arg5);
ACTION_RETURN_BOOL(res);
}
//========================================================================== //==========================================================================
// //
// The ultimate code pointer: Fully customizable missiles! // The ultimate code pointer: Fully customizable missiles!
@ -2696,8 +2580,7 @@ static bool InitSpawnedItem(AActor *self, AActor *mo, int flags)
else if (flags & SIXF_USEBLOODCOLOR) else if (flags & SIXF_USEBLOODCOLOR)
{ {
// [XA] Use the spawning actor's BloodColor to translate the newly-spawned object. // [XA] Use the spawning actor's BloodColor to translate the newly-spawned object.
PalEntry bloodcolor = self->GetBloodColor(); mo->Translation = self->BloodTranslation;
mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
} }
} }
if (flags & SIXF_TRANSFERPOINTERS) if (flags & SIXF_TRANSFERPOINTERS)
@ -3683,13 +3566,14 @@ DEFINE_ACTION_FUNCTION(AActor, A_DropInventory)
{ {
PARAM_SELF_PROLOGUE(AActor); PARAM_SELF_PROLOGUE(AActor);
PARAM_CLASS(drop, AInventory); PARAM_CLASS(drop, AInventory);
PARAM_INT_DEF(amount);
if (drop) if (drop)
{ {
AInventory *inv = self->FindInventory(drop); AInventory *inv = self->FindInventory(drop);
if (inv) if (inv)
{ {
self->DropInventory(inv); self->DropInventory(inv, amount);
} }
} }
return 0; return 0;
@ -3939,7 +3823,7 @@ DEFINE_ACTION_FUNCTION(AActor, PlayerSkinCheck)
PARAM_SELF_PROLOGUE(AActor); PARAM_SELF_PROLOGUE(AActor);
ACTION_RETURN_BOOL(self->player != NULL && ACTION_RETURN_BOOL(self->player != NULL &&
skins[self->player->userinfo.GetSkin()].othergame); Skins[self->player->userinfo.GetSkin()].othergame);
} }
// [KS] *** Start of my modifications *** // [KS] *** Start of my modifications ***
@ -4621,6 +4505,13 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeCountFlags)
return 0; return 0;
} }
enum ERaise
{
RF_TRANSFERFRIENDLINESS = 1,
RF_NOCHECKPOSITION = 2
};
//=========================================================================== //===========================================================================
// //
// A_RaiseMaster // A_RaiseMaster
@ -4629,11 +4520,12 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeCountFlags)
DEFINE_ACTION_FUNCTION(AActor, A_RaiseMaster) DEFINE_ACTION_FUNCTION(AActor, A_RaiseMaster)
{ {
PARAM_SELF_PROLOGUE(AActor); PARAM_SELF_PROLOGUE(AActor);
PARAM_BOOL_DEF(copy); PARAM_INT_DEF(flags);
bool copy = !!(flags & RF_TRANSFERFRIENDLINESS);
if (self->master != NULL) if (self->master != NULL)
{ {
P_Thing_Raise(self->master, copy ? self : NULL); P_Thing_Raise(self->master, copy ? self : NULL, (flags & RF_NOCHECKPOSITION));
} }
return 0; return 0;
} }
@ -4646,16 +4538,17 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseMaster)
DEFINE_ACTION_FUNCTION(AActor, A_RaiseChildren) DEFINE_ACTION_FUNCTION(AActor, A_RaiseChildren)
{ {
PARAM_SELF_PROLOGUE(AActor); PARAM_SELF_PROLOGUE(AActor);
PARAM_BOOL_DEF(copy); PARAM_INT_DEF(flags);
TThinkerIterator<AActor> it; TThinkerIterator<AActor> it;
AActor *mo; AActor *mo;
bool copy = !!(flags & RF_TRANSFERFRIENDLINESS);
while ((mo = it.Next()) != NULL) while ((mo = it.Next()) != NULL)
{ {
if (mo->master == self) if (mo->master == self)
{ {
P_Thing_Raise(mo, copy ? self : NULL); P_Thing_Raise(mo, copy ? self : NULL, (flags & RF_NOCHECKPOSITION));
} }
} }
return 0; return 0;
@ -4669,18 +4562,19 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseChildren)
DEFINE_ACTION_FUNCTION(AActor, A_RaiseSiblings) DEFINE_ACTION_FUNCTION(AActor, A_RaiseSiblings)
{ {
PARAM_SELF_PROLOGUE(AActor); PARAM_SELF_PROLOGUE(AActor);
PARAM_BOOL_DEF(copy); PARAM_INT_DEF(flags);
TThinkerIterator<AActor> it; TThinkerIterator<AActor> it;
AActor *mo; AActor *mo;
bool copy = !!(flags & RF_TRANSFERFRIENDLINESS);
if (self->master != NULL) if (self->master != NULL)
{ {
while ((mo = it.Next()) != NULL) while ((mo = it.Next()) != NULL)
{ {
if (mo->master == self->master && mo != self) if (mo->master == self->master && mo != self)
{ {
P_Thing_Raise(mo, copy ? self : NULL); P_Thing_Raise(mo, copy ? self : NULL, (flags & RF_NOCHECKPOSITION));
} }
} }
} }
@ -6941,3 +6835,20 @@ DEFINE_ACTION_FUNCTION(AActor, SetCamera)
} }
return 0; return 0;
} }
DEFINE_ACTION_FUNCTION(AActor, A_SprayDecal)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_STRING(name);
SprayDecal(self, name);
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_SetMugshotState)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_STRING(name);
if (self->CheckLocalView(consoleplayer))
StatusBar->SetMugShotState(name);
return 0;
}

View file

@ -46,7 +46,7 @@
struct sectortype struct sectortype
{ {
SWORD wallptr, wallnum; SWORD wallptr, wallnum;
SDWORD ceilingZ, floorZ; int32_t ceilingZ, floorZ;
SWORD ceilingstat, floorstat; SWORD ceilingstat, floorstat;
SWORD ceilingpicnum, ceilingheinum; SWORD ceilingpicnum, ceilingheinum;
SBYTE ceilingshade; SBYTE ceilingshade;
@ -74,7 +74,7 @@ struct sectortype
//32 bytes //32 bytes
struct walltype struct walltype
{ {
SDWORD x, y; int32_t x, y;
SWORD point2, nextwall, nextsector, cstat; SWORD point2, nextwall, nextsector, cstat;
SWORD picnum, overpicnum; SWORD picnum, overpicnum;
SBYTE shade; SBYTE shade;
@ -100,7 +100,7 @@ struct walltype
//44 bytes //44 bytes
struct spritetype struct spritetype
{ {
SDWORD x, y, z; int32_t x, y, z;
SWORD cstat, picnum; SWORD cstat, picnum;
SBYTE shade; SBYTE shade;
BYTE pal, clipdist, filler; BYTE pal, clipdist, filler;
@ -146,8 +146,8 @@ static bool P_LoadBloodMap (BYTE *data, size_t len, FMapThing **sprites, int *nu
static void LoadSectors (sectortype *bsectors, int count); static void LoadSectors (sectortype *bsectors, int count);
static void LoadWalls (walltype *walls, int numwalls, sectortype *bsectors); static void LoadWalls (walltype *walls, int numwalls, sectortype *bsectors);
static int LoadSprites (spritetype *sprites, Xsprite *xsprites, int numsprites, sectortype *bsectors, FMapThing *mapthings); static int LoadSprites (spritetype *sprites, Xsprite *xsprites, int numsprites, sectortype *bsectors, FMapThing *mapthings);
static vertex_t *FindVertex (SDWORD x, SDWORD y); static vertex_t *FindVertex (int32_t x, int32_t y);
static void CreateStartSpot (SDWORD *pos, FMapThing *start); static void CreateStartSpot (int32_t *pos, FMapThing *start);
static void CalcPlane (SlopeWork &slope, secplane_t &plane); static void CalcPlane (SlopeWork &slope, secplane_t &plane);
static void Decrypt (void *to, const void *from, int len, int key); static void Decrypt (void *to, const void *from, int len, int key);
@ -232,7 +232,7 @@ bool P_LoadBuildMap (BYTE *data, size_t len, FMapThing **sprites, int *numspr)
numsprites = *(WORD *)(data + 24 + numsec*sizeof(sectortype) + numwalls*sizeof(walltype)); numsprites = *(WORD *)(data + 24 + numsec*sizeof(sectortype) + numwalls*sizeof(walltype));
*sprites = new FMapThing[numsprites + 1]; *sprites = new FMapThing[numsprites + 1];
CreateStartSpot ((SDWORD *)(data + 4), *sprites); CreateStartSpot ((int32_t *)(data + 4), *sprites);
*numspr = 1 + LoadSprites ((spritetype *)(data + 26 + numsec*sizeof(sectortype) + numwalls*sizeof(walltype)), *numspr = 1 + LoadSprites ((spritetype *)(data + 26 + numsec*sizeof(sectortype) + numwalls*sizeof(walltype)),
NULL, numsprites, (sectortype *)(data + 22), *sprites + 1); NULL, numsprites, (sectortype *)(data + 22), *sprites + 1);
@ -755,7 +755,7 @@ static int LoadSprites (spritetype *sprites, Xsprite *xsprites, int numsprites,
// //
//========================================================================== //==========================================================================
vertex_t *FindVertex (SDWORD xx, SDWORD yy) vertex_t *FindVertex (int32_t xx, int32_t yy)
{ {
int i; int i;
@ -780,7 +780,7 @@ vertex_t *FindVertex (SDWORD xx, SDWORD yy)
// //
//========================================================================== //==========================================================================
static void CreateStartSpot (SDWORD *pos, FMapThing *start) static void CreateStartSpot (int32_t *pos, FMapThing *start)
{ {
short angle = LittleShort(*(WORD *)(&pos[3])); short angle = LittleShort(*(WORD *)(&pos[3]));
FMapThing mt = { 0, }; FMapThing mt = { 0, };

View file

@ -582,3 +582,18 @@ bool EV_CeilingCrushStop (int tag, bool remove)
return rtn; return rtn;
} }
bool EV_StopCeiling(int tag)
{
FSectorTagIterator it(tag);
while (int sec = it.Next())
{
if (level.sectors[sec].ceilingdata)
{
SN_StopSequence(&level.sectors[sec], CHAN_CEILING);
level.sectors[sec].ceilingdata->Destroy();
level.sectors[sec].ceilingdata = nullptr;
}
}
return true;
}

View file

@ -61,26 +61,27 @@
#include "p_local.h" #include "p_local.h"
#include "menu/menu.h" #include "menu/menu.h"
#include "g_levellocals.h" #include "g_levellocals.h"
#include "virtual.h"
// The conversations as they exist inside a SCRIPTxx lump. // The conversations as they exist inside a SCRIPTxx lump.
struct Response struct Response
{ {
SDWORD GiveType; int32_t GiveType;
SDWORD Item[3]; int32_t Item[3];
SDWORD Count[3]; int32_t Count[3];
char Reply[32]; char Reply[32];
char Yes[80]; char Yes[80];
SDWORD Link; int32_t Link;
DWORD Log; uint32_t Log;
char No[80]; char No[80];
}; };
struct Speech struct Speech
{ {
DWORD SpeakerType; uint32_t SpeakerType;
SDWORD DropType; int32_t DropType;
SDWORD ItemCheck[3]; int32_t ItemCheck[3];
SDWORD Link; int32_t Link;
char Name[16]; char Name[16];
char Sound[8]; char Sound[8];
char Backdrop[8]; char Backdrop[8];
@ -91,9 +92,9 @@ struct Speech
// The Teaser version of the game uses an older version of the structure // The Teaser version of the game uses an older version of the structure
struct TeaserSpeech struct TeaserSpeech
{ {
DWORD SpeakerType; uint32_t SpeakerType;
SDWORD DropType; int32_t DropType;
DWORD VoiceNumber; uint32_t VoiceNumber;
char Name[16]; char Name[16];
char Dialogue[320]; char Dialogue[320];
Response Responses[5]; Response Responses[5];
@ -112,11 +113,11 @@ static FDialogueMap ClassRoots;
static int ConversationMenuY; static int ConversationMenuY;
static int ConversationPauseTic; static int ConversationPauseTic;
static bool ShowGold; static int StaticLastReply;
static bool LoadScriptFile(int lumpnum, FileReader *lump, int numnodes, bool include, int type); static bool LoadScriptFile(int lumpnum, FileReader *lump, int numnodes, bool include, int type);
static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeakerType); static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, uint32_t &prevSpeakerType);
static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, DWORD &prevSpeakerType); static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, uint32_t &prevSpeakerType);
static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses); static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses);
static bool DrawConversationMenu (); static bool DrawConversationMenu ();
static void PickConversationReply (int replyindex); static void PickConversationReply (int replyindex);
@ -124,9 +125,6 @@ static void TerminalResponse (const char *str);
static FStrifeDialogueNode *PrevNode; static FStrifeDialogueNode *PrevNode;
#define NUM_RANDOM_LINES 10
#define NUM_RANDOM_GOODBYES 3
//============================================================================ //============================================================================
// //
// GetStrifeType // GetStrifeType
@ -207,6 +205,11 @@ void P_LoadStrifeConversations (MapData *map, const char *mapname)
{ {
if (!LoadScriptFile (scriptname_b, false, 1)) if (!LoadScriptFile (scriptname_b, false, 1))
{ {
if (gameinfo.Dialogue.IsNotEmpty())
{
if (LoadScriptFile(gameinfo.Dialogue, false, 0)) return;
}
LoadScriptFile ("SCRIPT00", false, 1); LoadScriptFile ("SCRIPT00", false, 1);
} }
} }
@ -240,7 +243,7 @@ bool LoadScriptFile (const char *name, bool include, int type)
static bool LoadScriptFile(int lumpnum, FileReader *lump, int numnodes, bool include, int type) static bool LoadScriptFile(int lumpnum, FileReader *lump, int numnodes, bool include, int type)
{ {
int i; int i;
DWORD prevSpeakerType; uint32_t prevSpeakerType;
FStrifeDialogueNode *node; FStrifeDialogueNode *node;
char buffer[4]; char buffer[4];
@ -314,7 +317,7 @@ static bool LoadScriptFile(int lumpnum, FileReader *lump, int numnodes, bool inc
// //
//============================================================================ //============================================================================
static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeakerType) static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, uint32_t &prevSpeakerType)
{ {
FStrifeDialogueNode *node; FStrifeDialogueNode *node;
Speech speech; Speech speech;
@ -347,11 +350,11 @@ static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeaker
} }
// Convert the rest of the data to our own internal format. // Convert the rest of the data to our own internal format.
node->Dialogue = ncopystring (speech.Dialogue); node->Dialogue = speech.Dialogue;
// The speaker's portrait, if any. // The speaker's portrait, if any.
speech.Dialogue[0] = 0; //speech.Backdrop[8] = 0; speech.Dialogue[0] = 0; //speech.Backdrop[8] = 0;
node->Backdrop = TexMan.CheckForTexture (speech.Backdrop, FTexture::TEX_MiscPatch); node->Backdrop = speech.Backdrop;
// The speaker's voice for this node, if any. // The speaker's voice for this node, if any.
speech.Backdrop[0] = 0; //speech.Sound[8] = 0; speech.Backdrop[0] = 0; //speech.Sound[8] = 0;
@ -360,7 +363,7 @@ static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeaker
// The speaker's name, if any. // The speaker's name, if any.
speech.Sound[0] = 0; //speech.Name[16] = 0; speech.Sound[0] = 0; //speech.Name[16] = 0;
node->SpeakerName = ncopystring(speech.Name); node->SpeakerName = speech.Name;
// The item the speaker should drop when killed. // The item the speaker should drop when killed.
node->DropType = dyn_cast<PClassActor>(GetStrifeType(speech.DropType)); node->DropType = dyn_cast<PClassActor>(GetStrifeType(speech.DropType));
@ -390,7 +393,7 @@ static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeaker
// //
//============================================================================ //============================================================================
static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, DWORD &prevSpeakerType) static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, uint32_t &prevSpeakerType)
{ {
FStrifeDialogueNode *node; FStrifeDialogueNode *node;
TeaserSpeech speech; TeaserSpeech speech;
@ -422,10 +425,10 @@ static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, DWORD &prevSpeaker
} }
// Convert the rest of the data to our own internal format. // Convert the rest of the data to our own internal format.
node->Dialogue = ncopystring (speech.Dialogue); node->Dialogue = speech.Dialogue;
// The Teaser version doesn't have portraits. // The Teaser version doesn't have portraits.
node->Backdrop.SetInvalid(); node->Backdrop = "";
// The speaker's voice for this node, if any. // The speaker's voice for this node, if any.
if (speech.VoiceNumber != 0) if (speech.VoiceNumber != 0)
@ -440,7 +443,7 @@ static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, DWORD &prevSpeaker
// The speaker's name, if any. // The speaker's name, if any.
speech.Dialogue[0] = 0; //speech.Name[16] = 0; speech.Dialogue[0] = 0; //speech.Name[16] = 0;
node->SpeakerName = ncopystring (speech.Name); node->SpeakerName = speech.Name;
// The item the speaker should drop when killed. // The item the speaker should drop when killed.
node->DropType = dyn_cast<PClassActor>(GetStrifeType (speech.DropType)); node->DropType = dyn_cast<PClassActor>(GetStrifeType (speech.DropType));
@ -505,7 +508,7 @@ static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses)
// The message to record in the log for this reply. // The message to record in the log for this reply.
reply->LogNumber = rsp->Log; reply->LogNumber = rsp->Log;
reply->LogString = NULL; reply->LogString = "";
// The item to receive when this reply is used. // The item to receive when this reply is used.
reply->GiveType = dyn_cast<PClassActor>(GetStrifeType (rsp->GiveType)); reply->GiveType = dyn_cast<PClassActor>(GetStrifeType (rsp->GiveType));
@ -520,31 +523,32 @@ static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses)
reply->ItemCheck[k].Item = inv; reply->ItemCheck[k].Item = inv;
reply->ItemCheck[k].Amount = rsp->Count[k]; reply->ItemCheck[k].Amount = rsp->Count[k];
} }
reply->PrintAmount = reply->ItemCheck[0].Amount;
reply->ItemCheckRequire.Clear(); reply->ItemCheckRequire.Clear();
reply->ItemCheckExclude.Clear(); reply->ItemCheckExclude.Clear();
// If the first item check has a positive amount required, then // If the first item check has a positive amount required, then
// add that to the reply string. Otherwise, use the reply as-is. // add that to the reply string. Otherwise, use the reply as-is.
reply->Reply = copystring (rsp->Reply); reply->Reply = rsp->Reply;
reply->NeedsGold = (rsp->Count[0] > 0); reply->NeedsGold = (rsp->Count[0] > 0);
// QuickYes messages are shown when you meet the item checks. // QuickYes messages are shown when you meet the item checks.
// QuickNo messages are shown when you don't. // QuickNo messages are shown when you don't.
if (rsp->Yes[0] == '_' && rsp->Yes[1] == 0) if (rsp->Yes[0] == '_' && rsp->Yes[1] == 0)
{ {
reply->QuickYes = NULL; reply->QuickYes = "";
} }
else else
{ {
reply->QuickYes = ncopystring (rsp->Yes); reply->QuickYes = rsp->Yes;
} }
if (reply->ItemCheck[0].Item != 0) if (reply->ItemCheck[0].Item != 0)
{ {
reply->QuickNo = ncopystring (rsp->No); reply->QuickNo = rsp->No;
} }
else else
{ {
reply->QuickNo = NULL; reply->QuickNo = "";
} }
reply->Next = *replyptr; reply->Next = *replyptr;
*replyptr = reply; *replyptr = reply;
@ -560,9 +564,6 @@ static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses)
FStrifeDialogueNode::~FStrifeDialogueNode () FStrifeDialogueNode::~FStrifeDialogueNode ()
{ {
if (SpeakerName != NULL) delete[] SpeakerName;
if (Dialogue != NULL) delete[] Dialogue;
if (Goodbye != nullptr) delete[] Goodbye;
FStrifeDialogueReply *tokill = Children; FStrifeDialogueReply *tokill = Children;
while (tokill != NULL) while (tokill != NULL)
{ {
@ -572,19 +573,6 @@ FStrifeDialogueNode::~FStrifeDialogueNode ()
} }
} }
//============================================================================
//
// FStrifeDialogueReply :: ~FStrifeDialogueReply
//
//============================================================================
FStrifeDialogueReply::~FStrifeDialogueReply ()
{
if (Reply != NULL) delete[] Reply;
if (QuickYes != NULL) delete[] QuickYes;
if (QuickNo != NULL) delete[] QuickNo;
}
//============================================================================ //============================================================================
// //
// FindNode // FindNode
@ -672,7 +660,7 @@ CUSTOM_CVAR(Float, dlg_musicvolume, 1.0f, CVAR_ARCHIVE)
static bool ShouldSkipReply(FStrifeDialogueReply *reply, player_t *player) static bool ShouldSkipReply(FStrifeDialogueReply *reply, player_t *player)
{ {
if (reply->Reply == nullptr) if (reply->Reply.IsEmpty())
return true; return true;
int i; int i;
@ -694,428 +682,60 @@ static bool ShouldSkipReply(FStrifeDialogueReply *reply, player_t *player)
return false; return false;
} }
//============================================================================ DEFINE_ACTION_FUNCTION(FStrifeDialogueReply, ShouldSkipReply)
//
// The conversation menu
//
//============================================================================
class DConversationMenu : public DMenu
{ {
DECLARE_CLASS(DConversationMenu, DMenu) PARAM_SELF_STRUCT_PROLOGUE(FStrifeDialogueReply);
PARAM_POINTER(player, player_t);
ACTION_RETURN_BOOL(ShouldSkipReply(self, player));
}
FString mSpeaker; DEFINE_ACTION_FUNCTION(DConversationMenu, SendConversationReply)
FBrokenLines *mDialogueLines; {
TArray<FString> mResponseLines; PARAM_PROLOGUE;
TArray<unsigned int> mResponses; PARAM_INT(node);
bool mShowGold; PARAM_INT(reply);
FStrifeDialogueNode *mCurNode; switch (node)
int mYpos; {
player_t *mPlayer; case -1:
Net_WriteByte(DEM_CONVNULL);
break;
case -2:
Net_WriteByte(DEM_CONVCLOSE);
break;
default:
Net_WriteByte(DEM_CONVREPLY);
Net_WriteWord(node);
Net_WriteByte(reply);
break;
}
StaticLastReply = reply;
return 0;
}
// Needed for the conversion process.
class DBrokenLines : public DObject
{
DECLARE_ABSTRACT_CLASS(DBrokenLines, DObject)
public: public:
static int mSelection; FBrokenLines *mBroken;
unsigned int mCount;
//============================================================================= DBrokenLines(FBrokenLines *broken, unsigned int count)
//
//
//
//=============================================================================
DConversationMenu(FStrifeDialogueNode *CurNode, player_t *player)
{ {
mCurNode = CurNode; mBroken = broken;
mPlayer = player; mCount = count;
mDialogueLines = NULL;
mShowGold = false;
// Format the speaker's message.
const char * toSay = CurNode->Dialogue;
if (strncmp (toSay, "RANDOM_", 7) == 0)
{
FString dlgtext;
dlgtext.Format("TXT_%s_%02d", toSay, 1+(pr_randomspeech() % NUM_RANDOM_LINES));
toSay = GStrings[dlgtext];
if (toSay == NULL)
{
toSay = GStrings["TXT_GOAWAY"]; // Ok, it's lame - but it doesn't look like an error to the player. ;)
}
}
else
{
// handle string table replacement
if (toSay[0] == '$')
{
toSay = GStrings(toSay + 1);
}
}
if (toSay == NULL)
{
toSay = ".";
}
mDialogueLines = V_BreakLines (SmallFont, screen->GetWidth()/CleanXfac - 24*2, toSay);
FStrifeDialogueReply *reply;
int i,j;
for (reply = CurNode->Children, i = 1; reply != NULL; reply = reply->Next)
{
if (ShouldSkipReply(reply, mPlayer))
{
continue;
}
mShowGold |= reply->NeedsGold;
const char *ReplyText = reply->Reply;
if (ReplyText[0] == '$')
{
ReplyText = GStrings(ReplyText + 1);
}
FString ReplyString = ReplyText;
if (reply->NeedsGold) ReplyString.AppendFormat(" for %u", reply->ItemCheck[0].Amount);
FBrokenLines *ReplyLines = V_BreakLines (SmallFont, 320-50-10, ReplyString);
mResponses.Push(mResponseLines.Size());
for (j = 0; ReplyLines[j].Width >= 0; ++j)
{
mResponseLines.Push(ReplyLines[j].Text);
}
++i;
V_FreeBrokenLines (ReplyLines);
}
const char *goodbyestr = CurNode->Goodbye;
if (goodbyestr == nullptr)
{
char goodbye[25];
mysnprintf(goodbye, countof(goodbye), "TXT_RANDOMGOODBYE_%d", 1 + (pr_randomspeech() % NUM_RANDOM_GOODBYES));
goodbyestr = GStrings[goodbye];
}
else if (strncmp(goodbyestr, "RANDOM_", 7) == 0)
{
FString byetext;
byetext.Format("TXT_%s_%02d", goodbyestr, 1 + (pr_randomspeech() % NUM_RANDOM_LINES));
goodbyestr = GStrings[byetext];
}
else if (goodbyestr[0] == '$')
{
goodbyestr = GStrings(goodbyestr + 1);
}
if (goodbyestr == nullptr) goodbyestr = "Bye.";
mResponses.Push(mResponseLines.Size());
mResponseLines.Push(FString(goodbyestr));
// Determine where the top of the reply list should be positioned.
i = OptionSettings.mLinespacing;
mYpos = MIN<int> (140, 192 - mResponseLines.Size() * i);
for (i = 0; mDialogueLines[i].Width >= 0; ++i)
{ }
i = 44 + i * 10;
if (mYpos - 100 < i - screen->GetHeight() / CleanYfac / 2)
{
mYpos = i - screen->GetHeight() / CleanYfac / 2 + 100;
}
ConversationMenuY = mYpos;
//ConversationMenu.indent = 50;
// Because replies can be selectively hidden mResponses.Size() won't be consistent.
// So make sure mSelection doesn't exceed mResponses.Size(). [FishyClockwork]
if (mSelection >= (int)mResponses.Size())
{
mSelection = mResponses.Size() - 1;
}
} }
//=============================================================================
//
//
//
//=============================================================================
void OnDestroy() override void OnDestroy() override
{ {
V_FreeBrokenLines(mDialogueLines); V_FreeBrokenLines(mBroken);
mDialogueLines = NULL;
I_SetMusicVolume (1.f);
Super::OnDestroy();
} }
bool DimAllowed()
{
return false;
}
//=============================================================================
//
//
//
//=============================================================================
bool MenuEvent(int mkey, bool fromcontroller)
{
if (demoplayback)
{ // During demo playback, don't let the user do anything besides close this menu.
if (mkey == MKEY_Back)
{
Close();
return true;
}
return false;
}
if (mkey == MKEY_Up)
{
if (--mSelection < 0) mSelection = mResponses.Size() - 1;
return true;
}
else if (mkey == MKEY_Down)
{
if (++mSelection >= (int)mResponses.Size()) mSelection = 0;
return true;
}
else if (mkey == MKEY_Back)
{
Net_WriteByte (DEM_CONVNULL);
Close();
return true;
}
else if (mkey == MKEY_Enter)
{
if ((unsigned)mSelection >= mResponses.Size())
{
Net_WriteByte(DEM_CONVCLOSE);
}
else
{
assert((unsigned)mCurNode->ThisNodeNum < StrifeDialogues.Size());
assert(StrifeDialogues[mCurNode->ThisNodeNum] == mCurNode);
// This is needed because mSelection represents the replies currently being displayed which will
// not match up with what's supposed to be selected if there are any hidden/skipped replies. [FishyClockwork]
FStrifeDialogueReply *reply = mCurNode->Children;
int replynum = mSelection;
for (int i = 0; i <= mSelection && reply != nullptr; reply = reply->Next)
{
if (ShouldSkipReply(reply, mPlayer))
replynum++;
else
i++;
}
// Send dialogue and reply numbers across the wire.
Net_WriteByte(DEM_CONVREPLY);
Net_WriteWord(mCurNode->ThisNodeNum);
Net_WriteByte(replynum);
}
Close();
return true;
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
bool MouseEvent(int type, int x, int y)
{
int sel = -1;
int fh = OptionSettings.mLinespacing;
// 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 >= 24 && x <= 320-24 && y >= mYpos && y < mYpos + fh * (int)mResponseLines.Size())
{
sel = (y - mYpos) / fh;
for(unsigned i=0;i<mResponses.Size(); i++)
{
if ((int)mResponses[i] > sel)
{
sel = i-1;
break;
}
}
}
if (sel != -1 && sel != mSelection)
{
//S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
}
mSelection = sel;
if (type == MOUSE_Release)
{
return MenuEvent(MKEY_Enter, true);
}
return true;
}
//=============================================================================
//
//
//
//=============================================================================
bool Responder(event_t *ev)
{
if (demoplayback)
{ // No interaction during demo playback
return false;
}
if (ev->type == EV_GUI_Event && ev->subtype == EV_GUI_Char && ev->data1 >= '0' && ev->data1 <= '9')
{ // Activate an item of type numberedmore (dialogue only)
mSelection = ev->data1 == '0' ? 9 : ev->data1 - '1';
return MenuEvent(MKEY_Enter, false);
}
return Super::Responder(ev);
}
//============================================================================
//
// DrawConversationMenu
//
//============================================================================
void Drawer()
{
const char *speakerName;
int x, y, linesize;
int width, fontheight;
player_t *cp = &players[consoleplayer];
assert (mDialogueLines != NULL);
assert (mCurNode != NULL);
FStrifeDialogueNode *CurNode = mCurNode;
if (CurNode == NULL)
{
Close ();
return;
}
// [CW] Freeze the game depending on MAPINFO options.
if (ConversationPauseTic < gametic && !multiplayer && !(level.flags2 & LEVEL2_CONV_SINGLE_UNFREEZE))
{
menuactive = MENU_On;
}
if (CurNode->Backdrop.isValid())
{
screen->DrawTexture (TexMan(CurNode->Backdrop), 0, 0, DTA_320x200, true, TAG_DONE);
}
x = 16 * screen->GetWidth() / 320;
y = 16 * screen->GetHeight() / 200;
linesize = 10 * CleanYfac;
// Who is talking to you?
if (CurNode->SpeakerName != NULL)
{
speakerName = CurNode->SpeakerName;
if (speakerName[0] == '$') speakerName = GStrings(speakerName+1);
}
else
{
speakerName = cp->ConversationNPC->GetTag("Person");
}
// Dim the screen behind the dialogue (but only if there is no backdrop).
if (!CurNode->Backdrop.isValid())
{
int i;
for (i = 0; mDialogueLines[i].Width >= 0; ++i)
{ }
screen->Dim (0, 0.45f, 14 * screen->GetWidth() / 320, 13 * screen->GetHeight() / 200,
308 * screen->GetWidth() / 320 - 14 * screen->GetWidth () / 320,
speakerName == NULL ? linesize * i + 6 * CleanYfac
: linesize * i + 6 * CleanYfac + linesize * 3/2);
}
// Dim the screen behind the PC's choices.
screen->Dim (0, 0.45f, (24-160) * CleanXfac + screen->GetWidth()/2,
(mYpos - 2 - 100) * CleanYfac + screen->GetHeight()/2,
272 * CleanXfac,
MIN<int>(mResponseLines.Size() * OptionSettings.mLinespacing + 4, 200 - mYpos) * CleanYfac);
if (speakerName != NULL)
{
screen->DrawText (SmallFont, CR_WHITE, x, y, speakerName,
DTA_CleanNoMove, true, TAG_DONE);
y += linesize * 3 / 2;
}
x = 24 * screen->GetWidth() / 320;
for (int i = 0; mDialogueLines[i].Width >= 0; ++i)
{
screen->DrawText (SmallFont, CR_UNTRANSLATED, x, y, mDialogueLines[i].Text,
DTA_CleanNoMove, true, TAG_DONE);
y += linesize;
}
if (ShowGold)
{
auto cointype = PClass::FindActor("Coin");
if (cointype)
{
AInventory *coin = cp->ConversationPC->FindInventory(cointype);
char goldstr[32];
mysnprintf(goldstr, countof(goldstr), "%d", coin != NULL ? coin->Amount : 0);
screen->DrawText(SmallFont, CR_GRAY, 21, 191, goldstr, DTA_320x200, true,
DTA_FillColor, 0, DTA_Alpha, HR_SHADOW, TAG_DONE);
screen->DrawTexture(TexMan(((AInventory *)GetDefaultByType(cointype))->Icon),
3, 190, DTA_320x200, true,
DTA_FillColor, 0, DTA_Alpha, HR_SHADOW, TAG_DONE);
screen->DrawText(SmallFont, CR_GRAY, 20, 190, goldstr, DTA_320x200, true, TAG_DONE);
screen->DrawTexture(TexMan(((AInventory *)GetDefaultByType(cointype))->Icon),
2, 189, DTA_320x200, true, TAG_DONE);
}
}
y = mYpos;
fontheight = OptionSettings.mLinespacing;
int response = 0;
for (unsigned i = 0; i < mResponseLines.Size(); i++, y += fontheight)
{
width = SmallFont->StringWidth(mResponseLines[i]);
x = 64;
screen->DrawText (SmallFont, CR_GREEN, x, y, mResponseLines[i], DTA_Clean, true, TAG_DONE);
if (i == mResponses[response])
{
char tbuf[16];
response++;
mysnprintf (tbuf, countof(tbuf), "%d.", response);
x = 50 - SmallFont->StringWidth (tbuf);
screen->DrawText (SmallFont, CR_GREY, x, y, tbuf, DTA_Clean, true, TAG_DONE);
if (response == mSelection+1)
{
int color = ((DMenu::MenuTime%8) < 4) || DMenu::CurrentMenu != this ? CR_RED:CR_GREY;
x = (50 + 3 - 160) * CleanXfac + screen->GetWidth() / 2;
int yy = (y + fontheight/2 - 5 - 100) * CleanYfac + screen->GetHeight() / 2;
screen->DrawText (ConFont, color, x, yy, "\xd",
DTA_CellX, 8 * CleanXfac,
DTA_CellY, 8 * CleanYfac,
TAG_DONE);
}
}
}
}
}; };
IMPLEMENT_CLASS(DConversationMenu, true, false)
int DConversationMenu::mSelection; // needs to be preserved if the same dialogue is restarted
//============================================================================ //============================================================================
// //
// P_FreeStrifeConversations // P_FreeStrifeConversations
@ -1135,9 +755,9 @@ void P_FreeStrifeConversations ()
ClassRoots.Clear(); ClassRoots.Clear();
PrevNode = NULL; PrevNode = NULL;
if (DMenu::CurrentMenu != NULL && DMenu::CurrentMenu->IsKindOf(RUNTIME_CLASS(DConversationMenu))) if (CurrentMenu != NULL && CurrentMenu->IsKindOf("ConversationMenu"))
{ {
DMenu::CurrentMenu->Close(); CurrentMenu->Close();
} }
} }
@ -1233,19 +853,31 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang
S_Sound (npc, CHAN_VOICE|CHAN_NOPAUSE, CurNode->SpeakerVoice, 1, ATTN_NORM); S_Sound (npc, CHAN_VOICE|CHAN_NOPAUSE, CurNode->SpeakerVoice, 1, ATTN_NORM);
} }
DConversationMenu *cmenu = new DConversationMenu(CurNode, pc->player); // Create the menu. This may be a user-defined class so check if it is good to use.
FName cls = CurNode->MenuClassName;
if (cls == NAME_None) cls = gameinfo.DefaultConversationMenuClass;
if (cls == NAME_None) cls = "ConversationMenu";
auto mcls = PClass::FindClass(cls);
if (mcls == nullptr || !mcls->IsDescendantOf("ConversationMenu")) mcls = PClass::FindClass("ConversationMenu");
assert(mcls);
auto cmenu = mcls->CreateNew();
IFVIRTUALPTRNAME(cmenu, "ConversationMenu", Init)
{
VMValue params[] = { cmenu, CurNode, pc->player, StaticLastReply };
VMReturn ret(&ConversationMenuY);
GlobalVMStack.Call(func, params, countof(params), &ret, 1);
}
if (CurNode != PrevNode) if (CurNode != PrevNode)
{ // Only reset the selection if showing a different menu. { // Only reset the selection if showing a different menu.
DConversationMenu::mSelection = 0; StaticLastReply = 0;
PrevNode = CurNode; PrevNode = CurNode;
} }
// And open the menu // And open the menu
M_StartControlPanel (false); M_StartControlPanel (false);
M_ActivateMenu(cmenu); M_ActivateMenu((DMenu*)cmenu);
ConversationPauseTic = gametic + 20;
menuactive = MENU_OnNoPause; menuactive = MENU_OnNoPause;
} }
} }
@ -1315,7 +947,7 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply
if (!CheckStrifeItem(player, reply->ItemCheck[i].Item, reply->ItemCheck[i].Amount)) if (!CheckStrifeItem(player, reply->ItemCheck[i].Item, reply->ItemCheck[i].Amount))
{ {
// No, you don't. Say so and let the NPC animate negatively. // No, you don't. Say so and let the NPC animate negatively.
if (reply->QuickNo && isconsole) if (reply->QuickNo.IsNotEmpty() && isconsole)
{ {
TerminalResponse(reply->QuickNo); TerminalResponse(reply->QuickNo);
} }
@ -1396,7 +1028,7 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply
} }
// Update the quest log, if needed. // Update the quest log, if needed.
if (reply->LogString != NULL) if (reply->LogString.IsNotEmpty())
{ {
const char *log = reply->LogString; const char *log = reply->LogString;
if (log[0] == '$') if (log[0] == '$')
@ -1482,10 +1114,9 @@ void P_ConversationCommand (int netcode, int pnum, BYTE **stream)
// The conversation menus are normally closed by the menu code, but that // The conversation menus are normally closed by the menu code, but that
// doesn't happen during demo playback, so we need to do it here. // doesn't happen during demo playback, so we need to do it here.
if (demoplayback && DMenu::CurrentMenu != NULL && if (demoplayback && CurrentMenu != NULL && CurrentMenu->IsKindOf("ConversationMenu"))
DMenu::CurrentMenu->IsKindOf(RUNTIME_CLASS(DConversationMenu)))
{ {
DMenu::CurrentMenu->Close(); CurrentMenu->Close();
} }
if (netcode == DEM_CONVREPLY) if (netcode == DEM_CONVREPLY)
{ {
@ -1549,3 +1180,28 @@ static void TerminalResponse (const char *str)
} }
} }
DEFINE_FIELD(FStrifeDialogueNode, DropType);
DEFINE_FIELD(FStrifeDialogueNode, ThisNodeNum);
DEFINE_FIELD(FStrifeDialogueNode, ItemCheckNode);
DEFINE_FIELD(FStrifeDialogueNode, SpeakerType);
DEFINE_FIELD(FStrifeDialogueNode, SpeakerName);
DEFINE_FIELD(FStrifeDialogueNode, SpeakerVoice);
DEFINE_FIELD(FStrifeDialogueNode, Backdrop);
DEFINE_FIELD(FStrifeDialogueNode, Dialogue);
DEFINE_FIELD(FStrifeDialogueNode, Goodbye);
DEFINE_FIELD(FStrifeDialogueNode, Children);
DEFINE_FIELD(FStrifeDialogueNode, MenuClassName);
DEFINE_FIELD(FStrifeDialogueNode, UserData);
DEFINE_FIELD(FStrifeDialogueReply, Next);
DEFINE_FIELD(FStrifeDialogueReply, GiveType);
DEFINE_FIELD(FStrifeDialogueReply, ActionSpecial);
DEFINE_FIELD(FStrifeDialogueReply, Args);
DEFINE_FIELD(FStrifeDialogueReply, PrintAmount);
DEFINE_FIELD(FStrifeDialogueReply, Reply);
DEFINE_FIELD(FStrifeDialogueReply, QuickYes);
DEFINE_FIELD(FStrifeDialogueReply, QuickNo);
DEFINE_FIELD(FStrifeDialogueReply, LogString);
DEFINE_FIELD(FStrifeDialogueReply, NextNode);
DEFINE_FIELD(FStrifeDialogueReply, LogNumber);
DEFINE_FIELD(FStrifeDialogueReply, NeedsGold);

View file

@ -20,40 +20,41 @@ struct FStrifeDialogueItemCheck
struct FStrifeDialogueNode struct FStrifeDialogueNode
{ {
~FStrifeDialogueNode (); ~FStrifeDialogueNode ();
PClassActor *DropType; PClassActor *DropType = nullptr;
TArray<FStrifeDialogueItemCheck> ItemCheck; TArray<FStrifeDialogueItemCheck> ItemCheck;
int ThisNodeNum; // location of this node in StrifeDialogues int ThisNodeNum = 0; // location of this node in StrifeDialogues
int ItemCheckNode; // index into StrifeDialogues int ItemCheckNode = 0; // index into StrifeDialogues
PClassActor *SpeakerType; PClassActor *SpeakerType = nullptr;
char *SpeakerName; FString SpeakerName;
FSoundID SpeakerVoice; FSoundID SpeakerVoice;
FTextureID Backdrop; FString Backdrop;
char *Dialogue; FString Dialogue;
char *Goodbye = nullptr; // must init to null for binary scripts to work as intended FString Goodbye; // must init to null for binary scripts to work as intended
FStrifeDialogueReply *Children; FStrifeDialogueReply *Children = nullptr;
FName MenuClassName;
FString UserData;
}; };
// FStrifeDialogueReply holds responses the player can give to the NPC // FStrifeDialogueReply holds responses the player can give to the NPC
struct FStrifeDialogueReply struct FStrifeDialogueReply
{ {
~FStrifeDialogueReply (); FStrifeDialogueReply *Next = nullptr;
PClassActor *GiveType = nullptr;
FStrifeDialogueReply *Next; int ActionSpecial = 0;
PClassActor *GiveType; int Args[5] = {};
int ActionSpecial; int PrintAmount = 0;
int Args[5];
TArray<FStrifeDialogueItemCheck> ItemCheck; TArray<FStrifeDialogueItemCheck> ItemCheck;
TArray<FStrifeDialogueItemCheck> ItemCheckRequire; TArray<FStrifeDialogueItemCheck> ItemCheckRequire;
TArray<FStrifeDialogueItemCheck> ItemCheckExclude; TArray<FStrifeDialogueItemCheck> ItemCheckExclude;
char *Reply; FString Reply;
char *QuickYes; FString QuickYes;
int NextNode; // index into StrifeDialogues FString QuickNo;
int LogNumber; FString LogString;
char *LogString; int NextNode = 0; // index into StrifeDialogues
char *QuickNo; int LogNumber = 0;
bool NeedsGold; bool NeedsGold = false;
}; };
extern TArray<FStrifeDialogueNode *> StrifeDialogues; extern TArray<FStrifeDialogueNode *> StrifeDialogues;

View file

@ -538,7 +538,8 @@ void DAnimatedDoor::Serialize(FSerializer &arc)
("delay", m_Delay) ("delay", m_Delay)
("dooranim", m_DoorAnim) ("dooranim", m_DoorAnim)
("setblock1", m_SetBlocking1) ("setblock1", m_SetBlocking1)
("setblock2", m_SetBlocking2); ("setblock2", m_SetBlocking2)
("type", m_Type);
} }
//============================================================================ //============================================================================
@ -614,7 +615,6 @@ void DAnimatedDoor::Tick ()
} }
m_Timer = m_Delay; m_Timer = m_Delay;
m_Status = Waiting;
} }
else else
{ {
@ -631,7 +631,7 @@ void DAnimatedDoor::Tick ()
case Waiting: case Waiting:
// IF DOOR IS DONE WAITING... // IF DOOR IS DONE WAITING...
if (!m_Timer--) if (m_Type == adClose || !m_Timer--)
{ {
if (!StartClosing()) if (!StartClosing())
{ {
@ -683,7 +683,7 @@ void DAnimatedDoor::Tick ()
// //
//============================================================================ //============================================================================
DAnimatedDoor::DAnimatedDoor (sector_t *sec, line_t *line, int speed, int delay, FDoorAnimation *anim) DAnimatedDoor::DAnimatedDoor (sector_t *sec, line_t *line, int speed, int delay, FDoorAnimation *anim, DAnimatedDoor::EADType type)
: DMovingCeiling (sec, false) : DMovingCeiling (sec, false)
{ {
double topdist; double topdist;
@ -717,7 +717,8 @@ DAnimatedDoor::DAnimatedDoor (sector_t *sec, line_t *line, int speed, int delay,
topdist = m_Sector->ceilingplane.fD() - topdist * m_Sector->ceilingplane.fC(); topdist = m_Sector->ceilingplane.fD() - topdist * m_Sector->ceilingplane.fC();
m_Status = Opening; m_Type = type;
m_Status = type == adClose? Waiting : Opening;
m_Speed = speed; m_Speed = speed;
m_Delay = delay; m_Delay = delay;
m_Timer = m_Speed; m_Timer = m_Speed;
@ -728,9 +729,12 @@ DAnimatedDoor::DAnimatedDoor (sector_t *sec, line_t *line, int speed, int delay,
m_Line2->flags |= ML_BLOCKING; m_Line2->flags |= ML_BLOCKING;
m_BotDist = m_Sector->ceilingplane.fD(); m_BotDist = m_Sector->ceilingplane.fD();
m_Sector->MoveCeiling (2048., topdist, 1); m_Sector->MoveCeiling (2048., topdist, 1);
if (m_DoorAnim->OpenSound != NAME_None) if (type == adOpenClose)
{ {
SN_StartSequence (m_Sector, CHAN_INTERIOR, m_DoorAnim->OpenSound, 1); if (m_DoorAnim->OpenSound != NAME_None)
{
SN_StartSequence(m_Sector, CHAN_INTERIOR, m_DoorAnim->OpenSound, 1);
}
} }
} }
@ -741,7 +745,7 @@ DAnimatedDoor::DAnimatedDoor (sector_t *sec, line_t *line, int speed, int delay,
// //
//============================================================================ //============================================================================
bool EV_SlidingDoor (line_t *line, AActor *actor, int tag, int speed, int delay) bool EV_SlidingDoor (line_t *line, AActor *actor, int tag, int speed, int delay, DAnimatedDoor::EADType type)
{ {
sector_t *sec; sector_t *sec;
int secnum; int secnum;
@ -756,7 +760,7 @@ bool EV_SlidingDoor (line_t *line, AActor *actor, int tag, int speed, int delay)
sec = line->backsector; sec = line->backsector;
// Make sure door isn't already being animated // Make sure door isn't already being animated
if (sec->ceilingdata != NULL) if (sec->ceilingdata != NULL )
{ {
if (actor->player == NULL) if (actor->player == NULL)
return false; return false;
@ -774,7 +778,7 @@ bool EV_SlidingDoor (line_t *line, AActor *actor, int tag, int speed, int delay)
FDoorAnimation *anim = TexMan.FindAnimatedDoor (line->sidedef[0]->GetTexture(side_t::top)); FDoorAnimation *anim = TexMan.FindAnimatedDoor (line->sidedef[0]->GetTexture(side_t::top));
if (anim != NULL) if (anim != NULL)
{ {
new DAnimatedDoor (sec, line, speed, delay, anim); new DAnimatedDoor (sec, line, speed, delay, anim, type);
return true; return true;
} }
return false; return false;
@ -799,7 +803,7 @@ bool EV_SlidingDoor (line_t *line, AActor *actor, int tag, int speed, int delay)
if (anim != NULL) if (anim != NULL)
{ {
rtn = true; rtn = true;
new DAnimatedDoor (sec, line, speed, delay, anim); new DAnimatedDoor (sec, line, speed, delay, anim, type);
break; break;
} }
} }

View file

@ -349,7 +349,7 @@ bool AActor::CheckMeleeRange ()
double dist; double dist;
if (!pl) if (!pl || (Sector->Flags & SECF_NOATTACK))
return false; return false;
dist = Distance2D (pl); dist = Distance2D (pl);
@ -398,7 +398,7 @@ bool P_CheckMeleeRange2 (AActor *actor)
double dist; double dist;
if (!actor->target) if (!actor->target || (actor->Sector->Flags & SECF_NOATTACK))
{ {
return false; return false;
} }
@ -445,6 +445,8 @@ bool P_CheckMissileRange (AActor *actor)
{ {
double dist; double dist;
if ((actor->Sector->Flags & SECF_NOATTACK)) return false;
if (!P_CheckSight (actor, actor->target, SF_SEEPASTBLOCKEVERYTHING)) if (!P_CheckSight (actor, actor->target, SF_SEEPASTBLOCKEVERYTHING))
return false; return false;
@ -3215,13 +3217,13 @@ DEFINE_ACTION_FUNCTION(AActor, A_ActiveSound)
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void ModifyDropAmount(AInventory *inv, int dropamount) void ModifyDropAmount(AInventory *inv, int dropamount)
{ {
int flagmask = IF_IGNORESKILL; auto flagmask = IF_IGNORESKILL;
double dropammofactor = G_SkillProperty(SKILLP_DropAmmoFactor); double dropammofactor = G_SkillProperty(SKILLP_DropAmmoFactor);
// Default drop amount is half of regular amount * regular ammo multiplication // Default drop amount is half of regular amount * regular ammo multiplication
if (dropammofactor == -1) if (dropammofactor == -1)
{ {
dropammofactor = 0.5; dropammofactor = 0.5;
flagmask = 0; flagmask = ItemFlag(0);
} }
if (dropamount > 0) if (dropamount > 0)

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