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.
silent = <bool>; // Actors in this sector make no sound,
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 (*)
norespawn = <bool>; // Players can not respawn in this sector
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,
// 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.
renderstyle = <string>; // Set per-actor render style, overriding the class default. Possible values can be "normal",

View file

@ -38,6 +38,7 @@ conversation
page
{
drop = <string>;
userstring = <string>; New field which can be used to pass data to custom conversation menu classes.
ifitem
{
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
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:
namespace = "ZDoom";
@ -86,6 +83,7 @@ conversation // Starts a dialog.
// the standard conversation ID ('actor' property) is used instead
// for this purpose but since 'ZDoom' namespace requires the actor
// to be a class name it needs a separate field for this.
class = <string>; //Override the default conversation menu class for this conversation.
page
{
@ -94,6 +92,8 @@ conversation // Starts a dialog.
choice
{
specialname = <string>; // Allows specifying a special by name.
// 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
// to show this option.

View file

@ -103,61 +103,66 @@ if( WIN32 )
set( FMOD_SEARCH_PATHS
"C:/Program Files/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_LIB_PATH_SUFFIXES PATH_SUFFIXES lib )
find_path( D3D_INCLUDE_DIR d3d9.h
PATHS ENV DXSDK_DIR
PATH_SUFFIXES Include )
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.
if( ( MSVC14 AND NOT CMAKE_GENERATOR_TOOLSET STREQUAL "v140_xp" ) OR # For VS 2015.
( MSVC15 AND NOT CMAKE_GENERATOR_TOOLSET STREQUAL "v150_xp" ) ) # For VS 2017.
# for modern Windows SDKs the DirectX headers should be available by default.
set( DX_dinput8_LIBRARY dinput8 )
endif()
else()
find_path( D3D_INCLUDE_DIR d3d9.h
PATHS ENV DXSDK_DIR
PATH_SUFFIXES Include )
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()
# 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.")
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()
set( ZDOOM_LIBS
@ -392,22 +397,6 @@ if (NOT ZDOOM_USE_SSE2)
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 )
set( HAVE_MMX 1 )
else( X64 )
@ -572,10 +561,6 @@ endif()
# Flags
if( BACKPATCH )
add_definitions( -DBACKPATCH )
endif()
# Update gitinfo.h
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} )
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 )
add_definitions( -DHAVE_SNDFILE )
endif()
@ -840,15 +813,12 @@ set( FASTMATH_PCH_SOURCES
intermission/intermission.cpp
intermission/intermission_parse.cpp
menu/joystickmenu.cpp
menu/listmenu.cpp
menu/loadsavemenu.cpp
menu/menu.cpp
menu/menudef.cpp
menu/menuinput.cpp
menu/messagebox.cpp
menu/optionmenu.cpp
menu/playermenu.cpp
menu/readthis.cpp
menu/videomenu.cpp
oplsynth/fmopl.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(Autosave, 15, 0, 0, 0) // [RH] Save the game *now*
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(Thing_Stop, 19, 1, 1, 1)
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(Ceiling_MoveToValue, 47, 3, 5, 5)
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(Sector_SetLink, 51, 4, 4, 4)
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_BuildDownDoomSync, 272, 4, 4, 4)
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

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_SMASHABLE = 0x04000000, // dies if hitting the floor.
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 ---
@ -427,6 +430,7 @@ enum ActorRenderFlag
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_MAYBEINVISIBLE = 0x02000000,
RF_DONTINTERPOLATE = 0x04000000, // no render interpolation ever!
};
// 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_AutoOffFloorOnly = 1<<13, // like BOUNCE_AutoOff, but only on floors
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,
@ -712,7 +717,7 @@ public:
virtual bool UseInventory (AInventory *item);
// Tosses an item out of the inventory.
AInventory *DropInventory (AInventory *item);
AInventory *DropInventory (AInventory *item, int amt = -1);
// Removes all items from the inventory.
void ClearInventory();
@ -802,42 +807,12 @@ public:
return (flags & MF_COUNTKILL) && !(flags & MF_FRIENDLY);
}
PalEntry GetBloodColor() const
{
return GetClass()->BloodColor;
}
// These also set CF_INTERPVIEW for players.
void SetPitch(DAngle p, bool interpolate, bool forceclamp = false);
void SetAngle(DAngle ang, bool interpolate);
void SetRoll(DAngle roll, bool interpolate);
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;
}
PClassActor *GetBloodType(int type = 0) const;
double Distance2DSquared(AActor *other, bool absolute = false)
{
@ -1003,29 +978,42 @@ public:
// NOTE: The first member variable *must* be snext.
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 OldRenderPos;
DAngle SpriteAngle;
DAngle SpriteRotation;
DAngle VisibleStartAngle;
DAngle VisibleStartPitch;
DAngle VisibleEndAngle;
DAngle VisibleEndPitch;
DRotator Angles;
DVector3 Vel;
double Speed;
double FloatSpeed;
DVector2 Scale; // Scaling values; 1 is normal size
double Alpha; // Since P_CheckSight makes an alpha check this can't be a float. It has to be a double.
int sprite; // used to find patch_t and flip value
uint8_t frame; // sprite frame to draw
uint8_t effects; // [RH] see p_effect.h
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
ActorRenderFlags renderflags; // Different rendering flags
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 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
FBlockNode *BlockNode; // links in blocks (if needed)
@ -1039,24 +1027,22 @@ public:
int floorterrain;
struct sector_t *ceilingsector;
FTextureID ceilingpic; // contacted sec ceilingpic
double radius, Height; // for movement checking
double renderradius;
double projectilepassheight; // height for clipping projectile movement against this actor
SDWORD tics; // state tic counter
double CameraHeight; // Height of camera when used as such
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;
//VMFunction *Damage; // For missiles and monster railgun
int DamageVal;
VMFunction *DamageFunc;
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.
DWORD VisibleToTeam;
@ -1076,10 +1062,10 @@ public:
// also the originator for missiles
TObjPtr<AActor> lastenemy; // Last known enemy -- killough 2/15/98
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
SDWORD 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 threshold; // if > 0, the target will be chased
int32_t DefThreshold; // [MC] Default threshold which the actor will reset its threshold to after switching targets
// no matter what (even if shot)
player_t *player; // only valid if type of APlayerPawn
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> tracer; // Thing being chased/attacked for tracers
TObjPtr<AActor> master; // Thing which spawned this one (prevents mutual attacks)
double Floorclip; // value to use for floor clipping
int tid; // thing identifier
int special; // special
@ -1157,7 +1142,8 @@ public:
BYTE smokecounter;
BYTE FloatBobPhase;
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
FSoundIDNoInit SeeSound;
@ -1173,7 +1159,7 @@ public:
double MaxDropOffHeight;
double MaxStepHeight;
SDWORD Mass;
int32_t Mass;
SWORD PainChance;
int PainThreshold;
FNameNoInit DamageType;
@ -1318,7 +1304,8 @@ public:
}
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
{

View file

@ -90,7 +90,7 @@ CVAR (Bool, am_customcolors, true, CVAR_ARCHIVE);
CVAR (Int, am_map_secrets, 1, CVAR_ARCHIVE);
CVAR (Int, am_drawmapback, 1, 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);
//=============================================================================
@ -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));
}
bool AM_isTeleportSpecial (int special, int *)
{
return (special == Teleport ||
special == Teleport_NoFog ||
special == Teleport_ZombieChanger ||
special == Teleport_Line);
}
bool AM_isTeleportBoundary (line_t &line)
{
return AM_checkSpecialBoundary(line, &AM_isTeleportSpecial);
}
bool AM_isExitSpecial (int special, int *)
{
return (special == Teleport_NewMap ||
special == Teleport_EndGame ||
special == Exit_Normal ||
special == Exit_Secret);
return AM_checkSpecialBoundary(line, [](int special, int *)
{
return (special == Teleport ||
special == Teleport_NoFog ||
special == Teleport_ZombieChanger ||
special == Teleport_Line);
});
}
bool AM_isExitBoundary (line_t& line)
{
return AM_checkSpecialBoundary(line, &AM_isExitSpecial);
}
bool AM_isTriggerSpecial (int special, int *)
{
FLineSpecial *spec = P_GetLineSpecialInfo(special);
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;
return AM_checkSpecialBoundary(line, [](int special, int *)
{
return (special == Teleport_NewMap ||
special == Teleport_EndGame ||
special == Exit_Normal ||
special == Exit_Secret);
});
}
bool AM_isTriggerBoundary (line_t &line)
{
return AM_checkSpecialBoundary(line, &AM_isTriggerSpecial);
}
bool AM_isLockSpecial (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);
return am_showtriggerlines == 1? AM_checkSpecialBoundary(line, [](int special, int *)
{
FLineSpecial *spec = P_GetLineSpecialInfo(special);
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;
}) : 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)
@ -2351,7 +2340,15 @@ bool AM_isLockBoundary (line_t &line, int *lockptr = NULL)
int special;
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)
{

View file

@ -358,7 +358,7 @@ void DBot::WhatToGet (AActor *item)
}
else if ((typeis (Megasphere) || typeis (Soulsphere) || typeis (HealthBonus)) && player->mo->health >= deh.MaxSoulsphere)
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;
if ((dest == NULL ||

View file

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

View file

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

View file

@ -732,34 +732,6 @@ CCMD (dir)
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
@ -880,7 +852,7 @@ CCMD(linetarget)
if (CheckCheatmode () || players[consoleplayer].mo == NULL) return;
C_AimLine(&t, false);
if (t.linetarget)
C_PrintInfo(t.linetarget);
C_PrintInfo(t.linetarget, argv.argc() > 1 && atoi(argv[1]) != 0);
else
Printf("No target found\n");
}
@ -893,7 +865,7 @@ CCMD(info)
if (CheckCheatmode () || players[consoleplayer].mo == NULL) return;
C_AimLine(&t, true);
if (t.linetarget)
C_PrintInfo(t.linetarget);
C_PrintInfo(t.linetarget, !(argv.argc() > 1 && atoi(argv[1]) == 0));
else
Printf("No target found. Info cannot find actors that have "
"the NOBLOCKMAP flag or have height/radius of 0.\n");
@ -902,7 +874,7 @@ CCMD(info)
CCMD(myinfo)
{
if (CheckCheatmode () || players[consoleplayer].mo == NULL) return;
C_PrintInfo(players[consoleplayer].mo);
C_PrintInfo(players[consoleplayer].mo, true);
}
typedef bool (*ActorTypeChecker) (AActor *);
@ -937,14 +909,20 @@ static void PrintFilteredActorList(const ActorTypeChecker IsActorType, const cha
AActor *mo;
const PClass *FilterClass = NULL;
int counter = 0;
int tid = 0;
if (FilterName != NULL)
{
FilterClass = PClass::FindActor(FilterName);
if (FilterClass == NULL)
{
Printf("%s is not an actor class.\n", FilterName);
return;
char *endp;
tid = (int)strtol(FilterName, &endp, 10);
if (*endp != 0)
{
Printf("%s is not an actor class.\n", FilterName);
return;
}
}
}
TThinkerIterator<AActor> it;
@ -953,10 +931,13 @@ static void PrintFilteredActorList(const ActorTypeChecker IsActorType, const cha
{
if ((FilterClass == NULL || mo->IsA(FilterClass)) && IsActorType(mo))
{
counter++;
if (!countOnly)
Printf ("%s at (%f,%f,%f)\n",
mo->GetClass()->TypeName.GetChars(), mo->X(), mo->Y(), mo->Z());
if (tid == 0 || tid == mo->tid)
{
counter++;
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);

View file

@ -206,6 +206,9 @@ struct FCommandBuffer
unsigned CursorPos;
unsigned StartPos; // First character to display
FString YankBuffer; // Deleted text buffer
bool AppendToYankBuffer; // Append consecutive deletes to buffer
FCommandBuffer()
{
CursorPos = StartPos = 0;
@ -278,6 +281,30 @@ struct FCommandBuffer
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()
{
CursorPos = 0;
@ -309,6 +336,18 @@ struct FCommandBuffer
}
}
void CursorWordLeft()
{
CursorPos = WordBoundaryLeft();
MakeStartPosGood();
}
void CursorWordRight()
{
CursorPos = WordBoundaryRight();
MakeStartPosGood();
}
void DeleteLeft()
{
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)
{
///FIXME: Not Unicode-aware
@ -1180,7 +1263,7 @@ void C_DrawConsole (bool hw2d)
DTA_KeepRatio, true, TAG_DONE);
// 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)
screen->DrawChar (ConFont, CR_ORANGE, (int)i, tickerY, 0x13, TAG_DONE);
else
@ -1345,6 +1428,7 @@ DEFINE_ACTION_FUNCTION(_Console, Printf)
static bool C_HandleKey (event_t *ev, FCommandBuffer &buffer)
{
int data1 = ev->data1;
bool keepappending = false;
switch (ev->subtype)
{
@ -1352,8 +1436,22 @@ static bool C_HandleKey (event_t *ev, FCommandBuffer &buffer)
return false;
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
buffer.AddChar(ev->data1);
buffer.AddChar(data1);
HistPos = NULL;
TabbedLast = false;
TabbedList = false;
@ -1654,6 +1752,56 @@ static bool C_HandleKey (event_t *ev, FCommandBuffer &buffer)
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;
@ -1664,6 +1812,9 @@ static bool C_HandleKey (event_t *ev, FCommandBuffer &buffer)
break;
#endif
}
buffer.AppendToYankBuffer = keepappending;
// Ensure that the cursor is always visible while typing
CursorTicker = C_BLINKRATE;
cursoron = 1;

View file

@ -207,7 +207,7 @@ DEFINE_ACTION_FUNCTION(_CVar, SetInt)
{
// Only menus are allowed to change CVARs.
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);
UCVarValue v;
v.Int = val;
@ -219,7 +219,7 @@ DEFINE_ACTION_FUNCTION(_CVar, SetFloat)
{
// Only menus are allowed to change CVARs.
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);
UCVarValue v;
v.Float = (float)val;
@ -231,7 +231,7 @@ DEFINE_ACTION_FUNCTION(_CVar, SetString)
{
// Only menus are allowed to change CVARs.
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);
UCVarValue v;
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.
DEFINE_ACTION_FUNCTION(DOptionMenuItemCommand, DoCommand)
{
if (DMenu::CurrentMenu == nullptr) return 0;
if (CurrentMenu == nullptr) return 0;
PARAM_PROLOGUE;
PARAM_STRING(cmd);
C_DoCommand(cmd);

View file

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

View file

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

View file

@ -95,23 +95,6 @@ char *copystring (const char *s)
return b;
}
//============================================================================
//
// ncopystring
//
// If the string has no content, returns NULL. Otherwise, returns a copy.
//
//============================================================================
char *ncopystring (const char *string)
{
if (string == NULL || string[0] == 0)
{
return NULL;
}
return copystring (string);
}
//==========================================================================
//
// ReplaceString

View file

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

View file

@ -86,6 +86,7 @@ enum
CP_SETTHINGZ,
CP_SETTAG,
CP_SETTHINGFLAGS,
CP_SETVERTEX,
};
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
@ -345,7 +346,19 @@ void ParseCompatibility()
sc.MustGetNumber();
CompatParams.Push(sc.Number);
}
else
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
{
sc.UnGet();
break;
@ -600,6 +613,16 @@ void SetCompatibilityParams()
i += 3;
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)
{
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");
if (armor!=NULL)
{
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");

View file

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

View file

@ -810,6 +810,10 @@ void D_Display ()
{
StatusBar->DrawBottomStuff (HUD_AltHud);
if (DrawFSHUD || automapactive) DrawHUD();
if (players[consoleplayer].camera && players[consoleplayer].camera->player)
{
StatusBar->DrawCrosshair();
}
StatusBar->Draw (HUD_AltHud);
StatusBar->DrawTopStuff (HUD_AltHud);
}
@ -2688,6 +2692,7 @@ void D_DoomMain (void)
// These calls from inside V_Init2 are still necessary
C_NewModeAdjust();
M_InitVideoModesMenu();
Renderer->RemapVoxels();
D_StartTitle (); // start up intro loop
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
ST_Clear();
D_ErrorCleanup ();
DThinker::DestroyThinkersInList(STAT_STATIC);
P_FreeLevelData();
P_FreeExtraLevelData();

View file

@ -73,7 +73,7 @@ EXTERN_CVAR (Int, autosavecount)
#define SIMULATEERRORS 0
extern BYTE *demo_p; // [RH] Special "ticcmds" get recorded in demos
extern char savedescription[SAVESTRINGSIZE];
extern FString savedescription;
extern FString savegamefile;
extern short consistancy[MAXPLAYERS][BACKUPTICS];
@ -2271,6 +2271,9 @@ void Net_DoCommand (int type, BYTE **stream, int player)
case DEM_INVDROP:
{
DWORD which = ReadLong (stream);
int amt = -1;
if (type == DEM_INVDROP) amt = ReadLong(stream);
if (gamestate == GS_LEVEL && !paused
&& players[player].playerstate != PST_DEAD)
@ -2288,7 +2291,7 @@ void Net_DoCommand (int type, BYTE **stream, int player)
}
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;
case DEM_SPRAY:
{
FTraceResults trace;
s = ReadString(stream);
SprayDecal(players[player].mo, s);
break;
DAngle ang = players[player].mo->Angles.Yaw;
DAngle pitch = players[player].mo->Angles.Pitch;
double c = pitch.Cos();
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);
}
}
}
case DEM_MDK:
s = ReadString(stream);
cht_DoMDK(&players[player], s);
break;
case DEM_PAUSE:
@ -2418,8 +2409,7 @@ void Net_DoCommand (int type, BYTE **stream, int player)
savegamefile = s;
delete[] s;
s = ReadString (stream);
memset (savedescription, 0, sizeof(savedescription));
strncpy (savedescription, s, sizeof(savedescription));
savedescription = s;
if (player != consoleplayer)
{
// 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:
{
FString ename = ReadString(stream);
s = ReadString(stream);
int argn = ReadByte(stream);
int arg[3] = { 0, 0, 0 };
for (int i = 0; i < argn; i++)
for (int i = 0; i < 3; i++)
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;
@ -2736,6 +2726,10 @@ void Net_SkipCommand (int type, BYTE **stream)
skip = strlen ((char *)(*stream)) + 5;
break;
case DEM_NETEVENT:
skip = strlen((char *)(*stream)) + 14;
break;
case DEM_SUMMON2:
case DEM_SUMMONFRIEND2:
case DEM_SUMMONFOE2:
@ -2757,6 +2751,7 @@ void Net_SkipCommand (int type, BYTE **stream)
case DEM_SPRAY:
case DEM_MORPHEX:
case DEM_KILLCLASSCHEAT:
case DEM_MDK:
skip = strlen ((char *)(*stream)) + 1;
break;
@ -2765,10 +2760,13 @@ void Net_SkipCommand (int type, BYTE **stream)
break;
case DEM_INVUSE:
case DEM_INVDROP:
skip = 4;
break;
case DEM_INVDROP:
skip = 8;
break;
case DEM_GENERICCHEAT:
case DEM_DROPPLAYER:
case DEM_FOV:

View file

@ -729,7 +729,7 @@ void D_WriteUserInfoStrings (int pnum, BYTE **stream, bool compact)
break;
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;
default:
@ -828,7 +828,7 @@ void D_ReadUserInfoStrings (int pnum, BYTE **stream, bool update)
players[pnum].mo->state->sprite ==
GetDefaultByType (players[pnum].cls)->SpawnState->sprite)
{ // 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
@ -898,7 +898,7 @@ void WriteUserInfo(FSerializer &arc, userinfo_t &info)
switch (pair->Key.GetIndex())
{
case NAME_Skin:
string = skins[info.GetSkin()].name;
string = Skins[info.GetSkin()].Name;
break;
case NAME_PlayerClass:
@ -986,7 +986,7 @@ CCMD (playerinfo)
// Print special info
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", "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", "PlayerClass",
ui->GetPlayerClassNum() == -1 ? "Random" : ui->GetPlayerClassType()->DisplayName.GetChars(),

View file

@ -93,7 +93,7 @@ public:
virtual bool UpdateWaterLevel (bool splash) override;
bool ResetAirSupply (bool playgasp = true);
int GetMaxHealth() const;
int GetMaxHealth(bool withupgrades = false) const;
void TweakSpeeds (double &forwardmove, double &sidemove);
void MorphPlayerThink ();
void ActivateMorphWeapon ();
@ -125,6 +125,8 @@ public:
int crouchsprite;
int MaxHealth;
int BonusHealth;
int MugShotMaxHealth;
int RunHealth;
int PlayerFlags;
@ -159,8 +161,6 @@ public:
FNameNoInit Face; // Doom status bar face (when used)
FNameNoInit Portrait;
FNameNoInit Slot[10];
FNameNoInit InvulMode;
FNameNoInit HealingRadiusType;
double HexenArmor[5];
BYTE ColorRangeStart; // Skin color range
BYTE ColorRangeEnd;
@ -209,13 +209,7 @@ typedef enum
CF_TOTALLYFROZEN = 1 << 12, // [RH] All players can do is press +use
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_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_INFINITEAMMO = 1 << 23, // Player owns an infinite ammo artifact
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_BUDDHA = 1 << 27, // [SP] Buddha mode - take damage, but don't die
@ -297,6 +291,11 @@ struct userinfo_t : TMap<FName,FBaseCVar *>
return aim;
}
}
// Same but unfiltered.
double GetAutoaim() const
{
return *static_cast<FFloatCVar *>(*CheckKey(NAME_Autoaim));
}
const char *GetName() const
{
return *static_cast<FStringCVar *>(*CheckKey(NAME_Name));
@ -530,6 +529,9 @@ public:
DPSprite *GetPSprite(PSPLayers layer);
bool GetPainFlash(FName type, PalEntry *color) const;
// [Nash] set player FOV
void SetFOV(float fov);
};
// 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_REMOVE, // 68
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

View file

@ -623,12 +623,20 @@ DEFINE_ACTION_FUNCTION(DObject, MSTime)
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))
{
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.
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;
}

View file

@ -41,6 +41,7 @@
class PClass;
class PType;
class FSerializer;
class FSoundID;
class DObject;
/*
@ -165,10 +166,11 @@ protected: \
_X_CONSTRUCTOR_##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.
// Using 8 to avoid unaligned pointer use.
#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 };
// Possible arguments for the IMPLEMENT_CLASS macro
@ -484,9 +486,11 @@ public:
// Add other types as needed.
bool &BoolVar(FName field);
int &IntVar(FName field);
FSoundID &SoundVar(FName field);
PalEntry &ColorVar(FName field);
FName &NameVar(FName field);
double &FloatVar(FName field);
FString &StringVar(FName field);
template<class T> T*& PointerVar(FName field);
// If you need to replace one object with another and want to

View file

@ -78,6 +78,7 @@
#include "menu/menu.h"
#include "intermission/intermission.h"
#include "g_levellocals.h"
#include "events.h"
// MACROS ------------------------------------------------------------------
@ -331,6 +332,8 @@ static void MarkRoot()
DThinker::MarkRoots();
FCanvasTextureInfo::Mark();
Mark(DACSThinker::ActiveThinker);
Mark(E_FirstEventHandler);
Mark(E_LastEventHandler);
for (auto &s : level.sectorPortals)
{
Mark(s.mSkybox);
@ -544,7 +547,6 @@ void FullGC()
void Barrier(DObject *pointing, DObject *pointed)
{
assert(pointed->GetClass() < (void*)0x1000000000000000);
assert(pointing == NULL || (pointing->IsBlack() && !pointing->IsDead()));
assert(pointed->IsWhite() && !pointed->IsDead());
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);
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;
Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Min, this, minval));
Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Max, this, maxval));
@ -509,7 +509,7 @@ PInt::PInt(unsigned int size, bool unsign, bool compatible)
else
{
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();
}
@ -722,10 +722,6 @@ PBool::PBool()
{
mDescriptiveName = "Bool";
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 *****************************************************************/
@ -1400,10 +1396,13 @@ void PPointer::WriteValue(FSerializer &ar, const char *key,const void *addr) con
ar(key, *(DObject **)addr);
}
}
else if (writer != nullptr)
{
writer(ar, key, addr);
}
else
{
assert(0 && "Pointer points to a type we don't handle");
I_Error("Attempt to save pointer to unhandled type");
I_Error("Attempt to save pointer to unhandled type %s", PointedType->DescriptiveName());
}
}
@ -1430,6 +1429,10 @@ bool PPointer::ReadValue(FSerializer &ar, const char *key, void *addr) const
}
return res;
}
else if (reader != nullptr)
{
return reader(ar, key, addr);
}
return false;
}
@ -2311,7 +2314,7 @@ void PStruct::WriteFields(FSerializer &ar, const void *addr, const TArray<PField
{
const PField *field = fields[i];
// 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);
}
@ -2344,6 +2347,11 @@ bool PStruct::ReadFields(FSerializer &ar, void *addr) const
DPrintf(DMSG_ERROR, "Symbol %s in %s is not a field\n",
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
{
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
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
// a more-derived class has variables that shadow a less-
@ -2892,6 +2900,7 @@ PClass::PClass()
bExported = false;
bDecorateClass = false;
ConstructNative = nullptr;
Meta = nullptr;
mDescriptiveName = "Class";
PClass::AllClasses.Push(this);
@ -2910,6 +2919,11 @@ PClass::~PClass()
M_Free(Defaults);
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);
assert (mem != nullptr);
@ -3064,7 +3078,7 @@ DObject *PClass::CreateNew() const
}
ConstructNative (mem);
((DObject *)mem)->SetClass (const_cast<PClass *>(this));
InitializeSpecials(mem, Defaults);
InitializeSpecials(mem, Defaults, &PClass::SpecialInits);
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,
// since native classes handle initialization natively.
if (!bRuntimeClass)
if ((!bRuntimeClass && Inits == &PClass::SpecialInits) || ParentClass == nullptr)
{
return;
}
assert(ParentClass != nullptr);
ParentClass->InitializeSpecials(addr, defaults);
for (auto tao : SpecialInits)
ParentClass->InitializeSpecials(addr, defaults, Inits);
for (auto tao : (this->*Inits))
{
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,
// since native classes handle deinitialization natively.
@ -3160,7 +3173,6 @@ void PClass::InitializeDefaults()
optr->ObjNext = nullptr;
optr->SetClass(this);
// Copy the defaults from the parent but leave the DObject part alone because it contains important data.
if (ParentClass->Defaults != nullptr)
{
@ -3174,21 +3186,53 @@ void PClass::InitializeDefaults()
{
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)
{
// Copy parent values from the parent defaults.
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)
{
if (!(field->Flags & VARF_Native))
if (!(field->Flags & VARF_Native) && !(field->Flags & VARF_Meta))
{
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);
// This is a placeholder so fill it in
if (existclass != NULL && existclass->Size == (unsigned)-1)
if (existclass != nullptr)
{
type = const_cast<PClass*>(existclass);
if (!IsDescendantOf(type->ParentClass))
// This is a placeholder so fill it in
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
{
@ -3240,6 +3292,7 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size)
type->bRuntimeClass = true;
Derive(type, name);
type->Size = size;
type->MetaSize = MetaSize;
if (size != TentativeClass)
{
type->InitializeDefaults();
@ -3256,6 +3309,39 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size)
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
@ -3264,18 +3350,36 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size)
PField *PClass::AddField(FName name, PType *type, DWORD flags)
{
unsigned oldsize = Size;
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)
if (!(flags & VARF_Meta))
{
Defaults = (BYTE *)M_Realloc(Defaults, Size);
memset(Defaults + oldsize, 0, Size - oldsize);
unsigned oldsize = Size;
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
{
DECLARE_CLASS(PPointer, PBasicType);
public:
typedef void(*WriteHandler)(FSerializer &ar, const char *key, const void *addr);
typedef bool(*ReadHandler)(FSerializer &ar, const char *key, void *addr);
PPointer();
PPointer(PType *pointsat, bool isconst = false);
PType *PointedType;
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 void GetTypeIDs(intptr_t &id1, intptr_t &id2) const;
void SetPointer(void *base, unsigned offset, TArray<size_t> *special = NULL) const override;
@ -560,12 +573,13 @@ enum
class PClass : public PNativeStruct
{
DECLARE_CLASS(PClass, PNativeStruct);
protected:
// 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 InitializeSpecials(void *addr, void *defaults) const;
void InitializeSpecials(void *addr, void *defaults, TArray<FTypeAndOffset> PClass::*Inits);
void SetSuper();
PField *AddMetaField(FName name, PType *type, DWORD flags);
public:
void WriteValue(FSerializer &ar, const char *key,const void *addr) const override;
void WriteAllFields(FSerializer &ar, const void *addr) const;
@ -580,11 +594,14 @@ public:
static void StaticBootstrap();
// Per-class information -------------------------------------
TArray<FTypeAndOffset> SpecialInits;
PClass *ParentClass; // the class this class derives from
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 *ArrayPointers; // dynamic arrays containing object pointers.
BYTE *Defaults;
BYTE *Meta; // Per-class static script data
unsigned MetaSize;
bool bRuntimeClass; // class was defined at run-time, not compile-time
bool bExported; // This type has been declared in a script
bool bDecorateClass; // may be subject to some idiosyncracies due to DECORATE backwards compatibility
@ -596,13 +613,14 @@ public:
PClass();
~PClass();
void InsertIntoHash();
DObject *CreateNew() const;
DObject *CreateNew();
PClass *CreateDerivedClass(FName name, unsigned int size);
PField *AddField(FName name, PType *type, DWORD flags=0) override;
void InitializeActorInfo();
void BuildFlatPointers();
void BuildArrayPointers();
void DestroySpecials(void *addr) const;
void InitMeta();
void DestroySpecials(void *addr);
const PClass *NativeClass() const;
// 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);
}
inline FSoundID &DObject::SoundVar(FName field)
{
return *(FSoundID*)ScriptVar(field, TypeSound);
}
inline PalEntry &DObject::ColorVar(FName field)
{
return *(PalEntry*)ScriptVar(field, TypeColor);
@ -741,6 +764,11 @@ inline double &DObject::FloatVar(FName field)
return *(double*)ScriptVar(field, TypeFloat64);
}
inline FString &DObject::StringVar(FName field)
{
return *(FString*)ScriptVar(field, TypeString);
}
template<class T>
inline T *&DObject::PointerVar(FName field)
{

View file

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

View file

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

View file

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

View file

@ -37,32 +37,42 @@ bool E_RegisterHandler(DStaticEventHandler* handler)
// 2. MyHandler3->2:
// E_FirstEventHandler = MyHandler2, E_LastEventHandler = MyHandler3
// (Yes, all those write barriers here are really needed!)
if (before != nullptr)
{
// if before is not null, link it before the existing handler.
// note that before can be first handler, check for this.
handler->next = before;
GC::WriteBarrier(handler, before);
handler->prev = before->prev;
GC::WriteBarrier(handler, before->prev);
before->prev = handler;
GC::WriteBarrier(before, handler);
if (before == E_FirstEventHandler)
{
E_FirstEventHandler = handler;
GC::WriteBarrier(handler);
}
}
else
{
// so if before is null, it means add last.
// it can also mean that we have no handlers at all yet.
handler->prev = E_LastEventHandler;
GC::WriteBarrier(handler, E_LastEventHandler);
handler->next = nullptr;
if (E_FirstEventHandler == nullptr)
E_FirstEventHandler = handler;
if (E_FirstEventHandler == nullptr) E_FirstEventHandler = handler;
E_LastEventHandler = handler;
GC::WriteBarrier(handler);
if (handler->prev != nullptr)
{
handler->prev->next = handler;
GC::WriteBarrier(handler->prev, handler);
}
}
if (handler->IsStatic())
{
handler->ObjectFlags |= OF_Fixed;
handler->ObjectFlags |= OF_Transient;
}
@ -80,16 +90,28 @@ bool E_UnregisterHandler(DStaticEventHandler* handler)
// link out of normal list
if (handler->prev)
{
handler->prev->next = handler->next;
GC::WriteBarrier(handler->prev, handler->next);
}
if (handler->next)
{
handler->next->prev = handler->prev;
GC::WriteBarrier(handler->next, handler->prev);
}
if (handler == E_FirstEventHandler)
{
E_FirstEventHandler = handler->next;
GC::WriteBarrier(handler->next);
}
if (handler == E_LastEventHandler)
{
E_LastEventHandler = handler->prev;
GC::WriteBarrier(handler->prev);
}
if (handler->IsStatic())
{
handler->ObjectFlags &= ~(OF_Fixed|OF_Transient);
handler->ObjectFlags &= ~OF_Transient;
handler->Destroy();
}
return true;
@ -427,7 +449,13 @@ DEFINE_EVENT_LOOPER(WorldLightning)
DEFINE_EVENT_LOOPER(WorldTick)
// 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(DBaseEvent, false, false)
IMPLEMENT_CLASS(DRenderEvent, false, false)
@ -1135,7 +1163,7 @@ CCMD(netevent)
Net_WriteByte(DEM_NETEVENT);
Net_WriteString(argv[1]);
Net_WriteByte(argn);
for (int i = 0; i < argn; i++)
for (int i = 0; i < 3; 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
{
DECLARE_CLASS(DStaticEventHandler, DObject)
DECLARE_CLASS(DStaticEventHandler, DObject);
HAS_OBJECT_POINTERS
public:
DStaticEventHandler()
{
@ -99,6 +100,7 @@ public:
void Serialize(FSerializer& arc) override
{
Super::Serialize(arc);
/*
if (arc.isReading())
{
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());
}
*/
arc("Order", Order);
arc("IsUiProcessor", IsUiProcessor);
@ -155,6 +158,7 @@ public:
bool IsStatic() override { return false; }
};
extern DStaticEventHandler* E_FirstEventHandler;
extern DStaticEventHandler* E_LastEventHandler;
// we cannot call this DEvent because in ZScript, 'event' is a keyword
class DBaseEvent : public DObject

View file

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

View file

@ -182,7 +182,7 @@ public:
union value_t
{
SDWORD i;
int32_t i;
fsfix fixed; // haleyjd: fixed-point
// 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;
FString savegamefile;
char savedescription[SAVESTRINGSIZE];
FString savedescription;
// [RH] Name of screenshot file to generate (usually NULL)
FString shotfile;
@ -241,6 +241,7 @@ FString BackupSaveName;
bool SendLand;
const AInventory *SendItemUse, *SendItemDrop;
int SendItemDropAmount;
EXTERN_CVAR (Int, team)
@ -346,6 +347,10 @@ CCMD (weapnext)
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' ));
}
if (SendItemUse != players[consoleplayer].ReadyWeapon)
{
S_Sound(CHAN_AUTO, "misc/weaponchange", 1.0, ATTN_NONE);
}
}
CCMD (weapprev)
@ -357,6 +362,10 @@ CCMD (weapprev)
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' ));
}
if (SendItemUse != players[consoleplayer].ReadyWeapon)
{
S_Sound(CHAN_AUTO, "misc/weaponchange", 1.0, ATTN_NONE);
}
}
CCMD (invnext)
@ -366,6 +375,7 @@ CCMD (invnext)
if (who == NULL)
return;
auto old = who->InvSel;
if (who->InvSel != 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'));
}
who->player->inventorytics = 5*TICRATE;
if (old != who->InvSel)
{
S_Sound(CHAN_AUTO, "misc/invchange", 1.0, ATTN_NONE);
}
}
CCMD (invprev)
@ -398,6 +412,7 @@ CCMD (invprev)
if (who == NULL)
return;
auto old = who->InvSel;
if (who->InvSel != 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'));
}
who->player->inventorytics = 5*TICRATE;
if (old != who->InvSel)
{
S_Sound(CHAN_AUTO, "misc/invchange", 1.0, ATTN_NONE);
}
}
CCMD (invuseall)
@ -457,12 +476,14 @@ CCMD (invdrop)
if (players[consoleplayer].mo)
{
SendItemDrop = players[consoleplayer].mo->InvSel;
SendItemDropAmount = -1;
}
}
CCMD (weapdrop)
{
SendItemDrop = players[consoleplayer].ReadyWeapon;
SendItemDropAmount = -1;
}
CCMD (drop)
@ -470,6 +491,7 @@ CCMD (drop)
if (argv.argc() > 1 && who != NULL)
{
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_WriteLong (SendItemDrop->InventoryID);
Net_WriteLong(SendItemDropAmount);
SendItemDrop = NULL;
}
@ -1081,7 +1104,7 @@ void G_Ticker ()
G_DoSaveGame (true, savegamefile, savedescription);
gameaction = ga_nothing;
savegamefile = "";
savedescription[0] = '\0';
savedescription = "";
break;
case ga_autosave:
G_DoAutoSave ();
@ -1320,12 +1343,24 @@ void G_PlayerFinishLevel (int player, EFinishLevelType mode, int flags)
if (mode == FINISH_NoHub && !(level.flags2 & LEVEL2_KEEPFULLINVENTORY))
{ // Reduce all owned (visible) inventory to defined maximum interhub amount
TArray<AInventory*> todelete;
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 (item->ItemFlags & IF_INVBAR && 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
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.Y *= skin.Scale.Y / defaultActor->Scale.Y;
@ -2068,8 +2103,7 @@ void G_SaveGame (const char *filename, const char *description)
else
{
savegamefile = filename;
strncpy (savedescription, description, sizeof(savedescription)-1);
savedescription[sizeof(savedescription)-1] = '\0';
savedescription = description;
sendsave = true;
}
}
@ -2119,7 +2153,7 @@ extern void P_CalcHeight (player_t *);
void G_DoAutoSave ()
{
char description[SAVESTRINGSIZE];
FString description;
FString file;
// Keep up to four autosaves at a time
UCVarValue num;
@ -2147,10 +2181,7 @@ void G_DoAutoSave ()
}
readableTime = myasctime ();
strcpy (description, "Autosave ");
strncpy (description+9, readableTime+4, 12);
description[9+12] = 0;
description.Format("Autosave %.12s", readableTime + 4);
G_DoSaveGame (false, file, description);
}
@ -2239,7 +2270,29 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio
I_FreezeTime(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;
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);
savegameManager.NotifyNewSave (filename.GetChars(), description, okForQuicksave);
savegameManager.NotifyNewSave (filename, description, okForQuicksave);
// delete the JSON buffers we created just above. Everything else will
// either still be needed or taken care of automatically.

View file

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

View file

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

View file

@ -50,8 +50,6 @@ DEFINE_FIELD(AInventory, DropTime)
DEFINE_FIELD(AInventory, SpawnPointClass)
DEFINE_FIELD(AInventory, PickupFlash)
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)
("pickupsound", PickupSound, def->PickupSound)
("spawnpointclass", SpawnPointClass, def->SpawnPointClass)
("droptime", DropTime, def->DropTime)
("givequest", GiveQuest, def->GiveQuest);
("droptime", DropTime, def->DropTime);
}
//===========================================================================

View file

@ -17,7 +17,7 @@ struct visstyle_t;
// 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_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_NOTELEPORTFREEZE = 1<<25, // does not 'freeze' the player right after teleporting.
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
{
@ -87,10 +93,9 @@ public:
FTextureID Icon; // Icon to show on status bar or HUD
int DropTime; // Countdown after dropping
PClassActor *SpawnPointClass; // For respawning like Heretic's mace
int GiveQuest; // Optionally give one of the quest items.
FTextureID AltHUDIcon;
DWORD ItemFlags;
InvFlags ItemFlags;
PClassActor *PickupFlash; // actor to spawn as pickup flash
FSoundIDNoInit PickupSound;

View file

@ -125,6 +125,7 @@ int starttime;
extern FString BackupSaveName;
bool savegamerestore;
int finishstate;
extern int mousex, mousey;
extern bool sendpause, sendsave, sendturn180, SendLand;
@ -160,6 +161,7 @@ void G_DeferedInitNew (FGameStartup *gs)
d_skill = gs->Skill;
CheckWarpTransMap (d_mapname, true);
gameaction = ga_newgame2;
finishstate = FINISH_NoHub;
}
//==========================================================================
@ -425,6 +427,7 @@ void G_InitNew (const char *mapname, bool bTitleLevel)
UnlatchCVars ();
G_VerifySkill();
UnlatchCVars ();
DThinker::DestroyThinkersInList(STAT_STATIC);
if (paused)
{
@ -776,7 +779,7 @@ void G_DoCompleted (void)
AM_Stop ();
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;
if (deathmatch &&
@ -792,12 +795,12 @@ void G_DoCompleted (void)
if (nextinfo == NULL || strncmp (nextlevel, "enDSeQ", 6) == 0)
{
wminfo.next = nextlevel;
wminfo.LName1 = NULL;
wminfo.LName1.SetInvalid();
}
else
{
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++)
{
wminfo.plyr[i].in = playeringame[i];
wminfo.plyr[i].skills = players[i].killcount;
wminfo.plyr[i].sitems = players[i].itemcount;
wminfo.plyr[i].ssecret = players[i].secretcount;
@ -898,6 +900,7 @@ void G_DoCompleted (void)
}
gamestate = GS_INTERMISSION;
finishstate = mode;
viewactive = 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 (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]);
// unsafe world load
E_WorldLoadedUnsafe();
@ -1355,22 +1371,6 @@ void G_FinishTravel ()
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 ();
// 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.NextMap = info->NextMap;
level.NextSecretMap = info->NextSecretMap;
level.F1Pic = info->F1Pic;
compatflags.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.
level.info->Snapshot.Clean();
@ -1909,6 +1911,7 @@ DEFINE_FIELD(FLevelLocals, LevelName)
DEFINE_FIELD(FLevelLocals, MapName)
DEFINE_FIELD(FLevelLocals, NextMap)
DEFINE_FIELD(FLevelLocals, NextSecretMap)
DEFINE_FIELD(FLevelLocals, F1Pic)
DEFINE_FIELD(FLevelLocals, maptype)
DEFINE_FIELD(FLevelLocals, Music)
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, frozen, LEVEL2_FROZEN)
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!
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 InterMusic;
int intermusicorder;
TMap <FName, std::pair<FString, int> > MapInterMusic;
FString SoundInfo;
FString SndSeq;
@ -495,6 +497,7 @@ enum EFSkillProperty // floating point properties
SKILLP_Aggressiveness,
SKILLP_MonsterHealth,
SKILLP_FriendlyHealth,
SKILLP_KickbackFactor,
};
int G_SkillProperty(ESkillProperty prop);
@ -512,6 +515,7 @@ struct FSkillInfo
double DamageFactor;
double ArmorFactor;
double HealthFactor;
double KickbackFactor;
bool FastMonsters;
bool SlowMonsters;
@ -520,6 +524,7 @@ struct FSkillInfo
bool EasyBossBrain;
bool EasyKey;
bool NoMenu;
int RespawnCounter;
int RespawnLimit;
double Aggressiveness;

View file

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

View file

@ -915,6 +915,18 @@ DEFINE_MAP_OPTION(intermusic, true)
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)
{
parse.ParseAssign();
@ -1265,6 +1277,7 @@ MapFlagHandlers[] =
{ "laxmonsteractivation", MITYPE_SETFLAG2, LEVEL2_LAXMONSTERACTIVATION, LEVEL2_LAXACTIVATIONMAPINFO },
{ "additive_scrollers", MITYPE_COMPATFLAG, COMPATF_BOOMSCROLL, 0 },
{ "keepfullinventory", MITYPE_SETFLAG2, LEVEL2_KEEPFULLINVENTORY, 0 },
{ "resetitems", MITYPE_SETFLAG3, LEVEL2_RESETINVENTORY, 0 },
{ "monsterfallingdamage", MITYPE_SETFLAG2, LEVEL2_MONSTERFALLINGDAMAGE, 0 },
{ "nomonsterfallingdamage", MITYPE_CLRFLAG2, LEVEL2_MONSTERFALLINGDAMAGE, 0 },
{ "clipmidtextures", MITYPE_SETFLAG2, LEVEL2_CLIPMIDTEX, 0 },
@ -1970,6 +1983,7 @@ static void ClearMapinfo()
DefaultSkill = -1;
DeinitIntermissions();
level.info = NULL;
level.F1Pic = "";
}
//==========================================================================

View file

@ -704,6 +704,24 @@ CCMD (spray)
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)
{
if (tpl == NULL || (tpl = tpl->GetDecal()) == NULL)
@ -751,14 +769,23 @@ IMPLEMENT_CLASS(ADecal, false, false)
void ADecal::BeginPlay ()
{
const FDecalTemplate *tpl;
const FDecalTemplate *tpl = nullptr;
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 (decalid != 0 && (tpl = DecalLibrary.GetDecalByNum (decalid)) != 0)
if (tpl != nullptr)
{
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
// or else the base skin for the player class.
if ((unsigned int)player->userinfo.GetSkin() >= PlayerClasses.Size () &&
(size_t)player->userinfo.GetSkin() < numskins)
(unsigned)player->userinfo.GetSkin() < Skins.Size())
{
skinindex = player->userinfo.GetSkin();
@ -701,3 +701,30 @@ void AMorphedMonster::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 *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
{

View file

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

View file

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

View file

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

View file

@ -489,7 +489,7 @@ FTexture *FMugShot::GetFace(player_t *player, const char *default_face, int accu
if (CurrentState != NULL)
{
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 NULL;

View file

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

View file

@ -2744,7 +2744,7 @@ class CommandDrawBar : public SBarInfoCommand
max = 0;
}
else //default to the class's health
max = statusBar->CPlayer->mo->GetMaxHealth() + statusBar->CPlayer->mo->stamina;
max = statusBar->CPlayer->mo->GetMaxHealth(true);
break;
case ARMOR:
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)
{
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)
{
goalValue = (goalValue*100)/max;

View file

@ -186,18 +186,6 @@ class DStrifeStatusBar : public DBaseStatusBar
public:
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 ();
}
@ -301,10 +289,11 @@ private:
"INVFONY0", "INVFONY1", "INVFONY2", "INVFONY3", "INVFONY4",
"INVFONY5", "INVFONY6", "INVFONY7", "INVFONY8", "INVFONY9",
"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;
@ -834,8 +823,8 @@ private:
imgMEDI,
imgARM1,
imgARM2,
NUM_STRIFESB_IMAGES
imgNEGATIVE,
imgINumbers = imgFONG0,
};
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, ArmorIcon2)
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] =
@ -360,6 +367,7 @@ void FMapInfoParser::ParseGameInfo()
GAMEINFOKEY_INT(TextScreenX, "textscreenx")
GAMEINFOKEY_INT(TextScreenY, "textscreeny")
GAMEINFOKEY_STRING(DefaultEndSequence, "defaultendsequence")
GAMEINFOKEY_STRING(DefaultConversationMenuClass, "defaultconversationmenuclass")
GAMEINFOKEY_FONT(mStatscreenMapNameFont, "statscreen_mapnamefont")
GAMEINFOKEY_FONT(mStatscreenFinishedFont, "statscreen_finishedfont")
GAMEINFOKEY_FONT(mStatscreenEnteringFont, "statscreen_enteringfont")
@ -367,6 +375,7 @@ void FMapInfoParser::ParseGameInfo()
GAMEINFOKEY_PATCH(mStatscreenEnteringFont, "statscreen_enteringpatch")
GAMEINFOKEY_BOOL(norandomplayerclass, "norandomplayerclass")
GAMEINFOKEY_BOOL(forcekillscripts, "forcekillscripts") // [JM] Force kill scripts on thing death. (MF7_NOKILLSCRIPTS overrides.)
GAMEINFOKEY_STRING(Dialogue, "dialogue")
else
{

View file

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

View file

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

View file

@ -109,6 +109,7 @@ void AdjustSpriteOffsets()
{
char str[9];
Wads.GetLumpName(str, i);
str[8] = 0;
FTextureID texid = TexMan.CheckForTexture(str, FTexture::TEX_Sprite, 0);
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;
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.SetCycleType(CYCLE_Sin);
m_currentRadius = m_cycler.GetVal();

View file

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

View file

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

View file

@ -665,7 +665,7 @@ void GLSprite::Process(AActor* thing, sector_t * sector, int thruportal)
bool isPicnumOverride = thing->picnum.isValid();
// 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;
}

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.
// 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..
FHardwareTexture::InitGlobalState();
@ -230,6 +230,27 @@ void OpenGLFrameBuffer::Swap()
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

View file

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

View file

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

View file

@ -251,20 +251,7 @@ PClassActor::PClassActor()
DamageFactors = NULL;
PainChances = NULL;
DeathHeight = -1;
BurnHeight = -1;
GibHealth = INT_MIN;
WoundHealth = 6;
FastSpeed = -1.;
RDFactor = 1.;
CameraHeight = INT_MIN;
DropItems = NULL;
DontHurtShooter = false;
ExplosionRadius = -1;
MeleeDamage = 0;
// Record this in the master list.
AllActorClasses.Push(this);
}
@ -308,32 +295,10 @@ void PClassActor::DeriveData(PClass *newclass)
PClassActor *newa = static_cast<PClassActor *>(newclass);
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->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;
if (DamageFactors != NULL)
@ -350,7 +315,6 @@ void PClassActor::DeriveData(PClass *newclass)
}
// Inventory stuff
newa->PickupMsg = PickupMsg;
newa->ForbiddenToPlayerClass = ForbiddenToPlayerClass;
newa->RestrictedToPlayerClass = RestrictedToPlayerClass;
@ -767,6 +731,13 @@ DEFINE_ACTION_FUNCTION(_DamageTypeDefinition, IgnoreArmor)
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;
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"))
{
dtd.ReplaceFactor = true;

View file

@ -214,6 +214,7 @@ struct DamageTypeDefinition
public:
DamageTypeDefinition() { Clear(); }
FString Obituary;
double DefaultFactor;
bool ReplaceFactor;
bool NoArmor;
@ -221,6 +222,7 @@ public:
void Apply(FName type);
void Clear()
{
Obituary = "";
DefaultFactor = 1.;
ReplaceFactor = false;
NoArmor = false;
@ -228,6 +230,7 @@ public:
static bool IgnoreArmor(FName type);
static int ApplyMobjDamageFactor(int damage, FName type, DmgFactors const * const factors);
static FString GetObituary(FName type);
private:
static double GetMobjDamageFactor(FName type, DmgFactors const * const factors);
@ -287,36 +290,11 @@ public:
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;
FString SourceLumpName;
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.
FString PickupMsg;
TArray<PClassActor *> RestrictedToPlayerClass;
TArray<PClassActor *> ForbiddenToPlayerClass;

View file

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

View file

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

View file

@ -55,6 +55,22 @@
// writes some bytes to the network data stream, and the network code
// 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)
{
static const char * const BeholdPowers[9] =
@ -671,6 +687,7 @@ CCMD (mdk)
if (CheckCheatmode ())
return;
Net_WriteByte (DEM_GENERICCHEAT);
Net_WriteByte (CHT_MDK);
const char *name = argv.argc() > 1 ? argv[1] : "";
Net_WriteByte (DEM_MDK);
Net_WriteString(name);
}

View file

@ -31,6 +31,7 @@
class player_t;
class PClassActor;
void cht_DoMDK(player_t *player, const char *mod);
void cht_DoCheat (player_t *player, int cheat);
void cht_Give (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)
{
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)))
{
DOptionMenuDescriptor *opt = (DOptionMenuDescriptor *)*desc;
DOptionMenuDescriptor *dopt = (DOptionMenuDescriptor *)*ddesc;
if (dopt == nullptr) return;
DMenuItemBase *it;
int i;
@ -162,11 +166,7 @@ void UpdateJoystickMenu(IJoystickConfig *selected)
}
}
}
#ifdef _WIN32
opt->mItems.Resize(8);
#else
opt->mItems.Resize(5);
#endif
opt->mItems = dopt->mItems;
it = opt->GetItem("ConfigureMessage");
if (it != nullptr) it->SetValue(0, !!Joysticks.Size());
@ -186,12 +186,12 @@ void UpdateJoystickMenu(IJoystickConfig *selected)
{
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 (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)
{
unsigned i;
@ -204,7 +204,7 @@ void UpdateJoystickMenu(IJoystickConfig *selected)
}
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 (Bool, show_messages, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
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(Int, m_use_mouse, 2, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR(Int, m_show_backbutton, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
DMenu *DMenu::CurrentMenu;
DEFINE_ACTION_FUNCTION(DMenu, GetCurrentMenu)
{
ACTION_RETURN_OBJECT(DMenu::CurrentMenu);
ACTION_RETURN_OBJECT(CurrentMenu);
}
int DMenu::MenuTime;
DEFINE_ACTION_FUNCTION(DMenu, MenuTime)
{
ACTION_RETURN_INT(DMenu::MenuTime);
ACTION_RETURN_INT(MenuTime);
}
FGameStartup GameStartupInfo;
@ -92,8 +92,12 @@ bool MenuButtonOrigin[NUM_MKEYS];
int BackbuttonTime;
float BackbuttonAlpha;
static bool MenuEnabled = true;
DMenu *CurrentMenu;
int MenuTime;
void M_InitVideoModes();
extern PClass *DefaultListMenuClass;
extern PClass *DefaultOptionMenuClass;
#define KEY_REPEAT_DELAY (TICRATE*5/12)
@ -138,7 +142,7 @@ void M_MarkMenus()
{
GC::Mark(pair->Value);
}
GC::Mark(DMenu::CurrentMenu);
GC::Mark(CurrentMenu);
}
//============================================================================
//
@ -157,56 +161,15 @@ DMenu::DMenu(DMenu *parent)
mParentMenu = parent;
mMouseCapture = false;
mBackbuttonSelected = false;
DontDim = false;
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)
{
@ -218,7 +181,7 @@ bool DMenu::CallResponder(event_t *ev)
GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr);
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)
{
IFVIRTUAL(DMenu, MenuEvent)
@ -260,7 +200,7 @@ bool DMenu::CallMenuEvent(int mkey, bool fromcontroller)
GlobalVMStack.Call(func, params, 3, &ret, 1, nullptr);
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 ()
{
assert(DMenu::CurrentMenu == this);
DMenu::CurrentMenu = mParentMenu;
if (CurrentMenu == nullptr) return; // double closing can happen in the save menu.
assert(CurrentMenu == this);
CurrentMenu = mParentMenu;
Destroy();
if (DMenu::CurrentMenu != nullptr)
if (CurrentMenu != nullptr)
{
GC::WriteBarrier(DMenu::CurrentMenu);
GC::WriteBarrier(CurrentMenu);
}
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)
{
PARAM_SELF_PROLOGUE(DMenu);
@ -441,24 +240,29 @@ DEFINE_ACTION_FUNCTION(DMenu, Close)
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);
PARAM_NAME(name);
ACTION_RETURN_OBJECT(self->GetItem(name));
IFVIRTUAL(DMenu, Ticker)
{
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()
@ -483,7 +287,7 @@ bool DMenu::TranslateKeyboardEvents()
void M_StartControlPanel (bool makeSound)
{
// intro might call this repeatedly
if (DMenu::CurrentMenu != nullptr)
if (CurrentMenu != nullptr)
return;
ResetButtonStates ();
@ -515,9 +319,13 @@ void M_StartControlPanel (bool makeSound)
void M_ActivateMenu(DMenu *menu)
{
if (menuactive == MENU_Off) menuactive = MENU_On;
if (DMenu::CurrentMenu != nullptr) DMenu::CurrentMenu->ReleaseCapture();
DMenu::CurrentMenu = menu;
GC::WriteBarrier(DMenu::CurrentMenu);
if (CurrentMenu != nullptr && CurrentMenu->mMouseCapture)
{
CurrentMenu->mMouseCapture = false;
I_ReleaseMouseCapture();
}
CurrentMenu = menu;
GC::WriteBarrier(CurrentMenu);
}
DEFINE_ACTION_FUNCTION(DMenu, ActivateMenu)
@ -599,6 +407,16 @@ void M_SetMenu(FName menu, int param)
M_InitVideoModes();
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
@ -622,22 +440,30 @@ void M_SetMenu(FName menu, int param)
}
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();
newmenu->Init(DMenu::CurrentMenu, ld);
DMenu *newmenu = (DMenu *)cls->CreateNew();
IFVIRTUALPTRNAME(newmenu, "ListMenu", Init)
{
VMValue params[3] = { newmenu, CurrentMenu, ld };
GlobalVMStack.Call(func, params, 3, nullptr, 0);
}
M_ActivateMenu(newmenu);
}
}
else if ((*desc)->IsKindOf(RUNTIME_CLASS(DOptionMenuDescriptor)))
{
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();
IFVIRTUALPTRNAME(newmenu, "OptionMenu", Init)
{
VMValue params[3] = { newmenu, DMenu::CurrentMenu, ld };
VMValue params[3] = { newmenu, CurrentMenu, ld };
GlobalVMStack.Call(func, params, 3, nullptr, 0);
}
M_ActivateMenu(newmenu);
@ -646,13 +472,18 @@ void M_SetMenu(FName menu, int param)
}
else
{
const PClass *menuclass = PClass::FindClass(menu);
PClass *menuclass = PClass::FindClass(menu);
if (menuclass != nullptr)
{
if (menuclass->IsDescendantOf(RUNTIME_CLASS(DMenu)))
if (menuclass->IsDescendantOf("GenericMenu"))
{
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);
return;
}
@ -666,7 +497,7 @@ DEFINE_ACTION_FUNCTION(DMenu, SetMenu)
{
PARAM_PROLOGUE;
PARAM_NAME(menu);
PARAM_INT(mparam);
PARAM_INT_DEF(mparam);
M_SetMenu(menu, mparam);
return 0;
}
@ -688,7 +519,7 @@ bool M_Responder (event_t *ev)
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:
//
@ -723,9 +554,9 @@ bool M_Responder (event_t *ev)
}
// 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;
keyup = ev->subtype == EV_GUI_KeyUp;
@ -744,7 +575,7 @@ bool M_Responder (event_t *ev)
default:
if (!keyup)
{
return DMenu::CurrentMenu->CallResponder(ev);
return CurrentMenu->CallResponder(ev);
}
break;
}
@ -752,6 +583,9 @@ bool M_Responder (event_t *ev)
}
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;
ch = ev->data1;
@ -827,11 +661,11 @@ bool M_Responder (event_t *ev)
{
MenuButtonTickers[mkey] = KEY_REPEAT_DELAY;
}
DMenu::CurrentMenu->CallMenuEvent(mkey, fromcontroller);
CurrentMenu->CallMenuEvent(mkey, fromcontroller);
return true;
}
}
return DMenu::CurrentMenu->CallResponder(ev) || !keyup;
return CurrentMenu->CallResponder(ev) || !keyup;
}
else if (MenuEnabled)
{
@ -872,10 +706,10 @@ bool M_Responder (event_t *ev)
void M_Ticker (void)
{
DMenu::MenuTime++;
if (DMenu::CurrentMenu != nullptr && menuactive != MENU_Off)
MenuTime++;
if (CurrentMenu != nullptr && menuactive != MENU_Off)
{
DMenu::CurrentMenu->CallTicker();
CurrentMenu->CallTicker();
for (int i = 0; i < NUM_MKEYS; ++i)
{
@ -884,7 +718,7 @@ void M_Ticker (void)
if (MenuButtonTickers[i] > 0 && --MenuButtonTickers[i] <= 0)
{
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);
V_SetBorderNeedRefresh();
}
DMenu::CurrentMenu->CallDrawer();
CurrentMenu->CallDrawer();
}
}
@ -941,13 +775,14 @@ void M_Drawer (void)
//
//=============================================================================
void M_ClearMenus ()
void M_ClearMenus()
{
M_DemoNoPlay = false;
if (DMenu::CurrentMenu != nullptr)
while (CurrentMenu != nullptr)
{
DMenu::CurrentMenu->Destroy();
DMenu::CurrentMenu = nullptr;
DMenu* parent = CurrentMenu->mParentMenu;
CurrentMenu->Destroy();
CurrentMenu = parent;
}
V_SetBorderNeedRefresh();
menuactive = MENU_Off;
@ -1185,11 +1020,11 @@ CCMD(reset2saved)
// This really should be in the script but we can't do scripted CCMDs yet.
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);
}
}
@ -1199,6 +1034,9 @@ CCMD(undocolorpic)
DEFINE_FIELD(DMenu, mParentMenu)
DEFINE_FIELD(DMenu, mMouseCapture);
DEFINE_FIELD(DMenu, mBackbuttonSelected);
DEFINE_FIELD(DMenu, DontDim);
DEFINE_FIELD(DMenuDescriptor, mMenuName)
DEFINE_FIELD(DMenuDescriptor, mNetgameMessage)
@ -1209,9 +1047,6 @@ DEFINE_FIELD(DMenuItemBase, mYpos)
DEFINE_FIELD(DMenuItemBase, mAction)
DEFINE_FIELD(DMenuItemBase, mEnabled)
DEFINE_FIELD(DListMenu, mDesc)
DEFINE_FIELD(DListMenu, mFocusControl)
DEFINE_FIELD(DListMenuDescriptor, mItems)
DEFINE_FIELD(DListMenuDescriptor, mSelectedItem)
DEFINE_FIELD(DListMenuDescriptor, mSelectOfsX)
@ -1291,7 +1126,7 @@ DMenuItemBase * CreateOptionMenuItemControl(const char *label, FName cmd, FKeyBi
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 p = c->CreateNew();
@ -1301,7 +1136,7 @@ DMenuItemBase * CreateListMenuItemPatch(int x, int y, int height, int hotkey, FT
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 p = c->CreateNew();
@ -1311,50 +1146,6 @@ DMenuItemBase * CreateListMenuItemText(int x, int y, int height, int hotkey, con
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()
{
IFVIRTUAL(DMenuItemBase, Activate)
@ -1367,18 +1158,6 @@ bool DMenuItemBase::Activate()
}
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)
{
@ -1436,112 +1215,4 @@ bool DMenuItemBase::GetValue(int i, int *pvalue)
return 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);
}
}
IMPLEMENT_CLASS(DMenuItemBase, false, false)

View file

@ -9,7 +9,6 @@
#include "r_data/r_translate.h"
#include "c_cvars.h"
#include "v_font.h"
#include "version.h"
#include "textures/textures.h"
EXTERN_CVAR(Float, snd_menuvolume)
@ -58,46 +57,58 @@ extern FGameStartup GameStartupInfo;
struct FSaveGameNode
{
char Title[SAVESTRINGSIZE];
FString SaveTitle;
FString Filename;
bool bOldVersion;
bool bMissingWads;
bool bNoDelete;
FSaveGameNode() { bNoDelete = false; }
bool bOldVersion = false;
bool bMissingWads = false;
bool bNoDelete = false;
};
struct SavegameManager
struct FSavegameManager
{
private:
TArray<FSaveGameNode*> SaveGames;
FSaveGameNode NewSaveNode;
int LastSaved = -1;
int LastAccessed = -1;
int WindowSize = 0;
FSaveGameNode *quickSaveSlot = nullptr;
FileReader *currentSavePic = nullptr;
TArray<char> SavePicData;
FTexture *SavePic = nullptr;
FBrokenLines *SaveComment = nullptr;
void ClearSaveGames();
public:
int WindowSize = 0;
FSaveGameNode *quickSaveSlot = nullptr;
~FSavegameManager();
private:
int InsertSaveNode(FSaveGameNode *node);
int RemoveSaveSlot(int index);
public:
void NotifyNewSave(const FString &file, const FString &title, bool okForQuicksave);
void ClearSaveGames();
void ReadSaveStrings();
void NotifyNewSave(const char *file, const char *title, bool okForQuicksave);
void UnloadSaveData();
int RemoveSaveSlot(int index);
void LoadSavegame(int Selected);
void DoSave(int Selected, const char *savegamestring);
void DeleteEntry(int Selected);
void ExtractSaveData(int index);
void UnloadSaveData();
unsigned ExtractSaveData(int index);
void ClearSaveStuff();
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);
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:
FName mMenuName;
FString mNetgameMessage;
const PClass *mClass;
PClass *mClass = nullptr;
virtual size_t PropagateMark() { return 0; }
};
@ -126,11 +137,11 @@ class DListMenuDescriptor : public DMenuDescriptor
public:
TArray<DMenuItemBase *> mItems;
int mSelectedItem;
int mSelectOfsX;
int mSelectOfsY;
double mSelectOfsX;
double mSelectOfsY;
FTextureID mSelector;
int mDisplayTop;
int mXpos, mYpos;
double mXpos, mYpos;
int mWLeft, mWRight;
int mLinespacing; // needs to be stored for dynamically created menus
int mAutoselect; // this can only be set by internal menu creation functions
@ -241,9 +252,7 @@ class DMenu : public DObject
DECLARE_CLASS (DMenu, DObject)
HAS_OBJECT_POINTERS
protected:
bool mMouseCapture;
bool mBackbuttonSelected;
public:
enum
@ -253,45 +262,19 @@ public:
MOUSE_Release
};
enum
{
BACKBUTTON_TIME = 4*TICRATE
};
static DMenu *CurrentMenu;
static int MenuTime;
TObjPtr<DMenu> mParentMenu;
bool mMouseCapture;
bool mBackbuttonSelected;
bool DontDim;
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();
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 CallMenuEvent(int mkey, bool fromcontroller);
bool CallMouseEvent(int type, int x, int y);
void CallTicker();
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)
public:
int mXpos, mYpos;
double mXpos, mYpos;
FNameNoInit mAction;
bool mEnabled;
bool CheckCoordinate(int x, int y);
void Ticker();
void Drawer(bool selected);
bool Selectable();
bool Activate();
FName GetAction(int *pparam);
bool SetString(int i, const char *s);
bool GetString(int i, char *s, int len);
bool SetValue(int i, int value);
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; }
int GetY() { return mYpos; }
int GetX() { return mXpos; }
void SetX(int x) { mXpos = x; }
void DrawSelector(int xofs, int yofs, FTextureID tex);
double GetY() { return mYpos; }
};
//=============================================================================
//
// 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;
void M_EnableMenu (bool on) ;
bool M_Responder (event_t *ev);
@ -442,7 +339,6 @@ void M_ActivateMenu(DMenu *menu);
void M_ClearMenus ();
void M_ParseMenuDefs();
void M_StartupSkillMenu(FGameStartup *gs);
int M_GetDefaultSkill();
void M_StartControlPanel (bool makeSound);
void M_SetMenu(FName menu, int param = -1);
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 * CreateOptionMenuItemControl(const char *label, FName cmd, FKeyBindings *bindings);
DMenuItemBase * CreateOptionMenuItemJoyConfigMenu(const char *label, IJoystickConfig *joy);
DMenuItemBase * CreateListMenuItemPatch(int x, int 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 * CreateListMenuItemPatch(double x, double y, int height, int hotkey, FTextureID tex, 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

View file

@ -62,6 +62,8 @@ static DOptionMenuDescriptor *DefaultOptionMenuSettings; // contains common sett
FOptionMenuSettings OptionSettings;
FOptionMap OptionValues;
bool mustPrintErrors;
PClass *DefaultListMenuClass;
PClass *DefaultOptionMenuClass;
void I_BuildALDeviceList(FOptionValues *opt);
@ -145,7 +147,7 @@ static void DeinitMenus()
}
MenuDescriptors.Clear();
OptionValues.Clear();
DMenu::CurrentMenu = nullptr;
CurrentMenu = nullptr;
savegameManager.ClearSaveGames();
}
@ -291,8 +293,8 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc)
else if (sc.Compare("Class"))
{
sc.MustGetString();
const PClass *cls = PClass::FindClass(sc.String);
if (cls == nullptr || !cls->IsDescendantOf(RUNTIME_CLASS(DListMenu)))
PClass *cls = PClass::FindClass(sc.String);
if (cls == nullptr || !cls->IsDescendantOf("ListMenu"))
{
sc.ScriptError("Unknown menu class '%s'", sc.String);
}
@ -303,11 +305,11 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc)
sc.MustGetString();
desc->mSelector = GetMenuTexture(sc.String);
sc.MustGetStringName(",");
sc.MustGetNumber();
desc->mSelectOfsX = sc.Number;
sc.MustGetFloat();
desc->mSelectOfsX = sc.Float;
sc.MustGetStringName(",");
sc.MustGetNumber();
desc->mSelectOfsY = sc.Number;
sc.MustGetFloat();
desc->mSelectOfsY = sc.Float;
}
else if (sc.Compare("Linespacing"))
{
@ -316,11 +318,11 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc)
}
else if (sc.Compare("Position"))
{
sc.MustGetNumber();
desc->mXpos = sc.Number;
sc.MustGetFloat();
desc->mXpos = sc.Float;
sc.MustGetStringName(",");
sc.MustGetNumber();
desc->mYpos = sc.Number;
sc.MustGetFloat();
desc->mYpos = sc.Float;
}
else if (sc.Compare("Centermenu"))
{
@ -367,7 +369,7 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc)
PClass *cls = PClass::FindClass(buildname);
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.
{
auto &args = func->Variants[0].Proto->ArgumentTypes;
@ -409,7 +411,7 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc)
else if (args[i] == TypeTextureID)
{
auto f = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch);
if (!f.isValid())
if (!f.Exists())
{
sc.ScriptError("Unknown texture %s", sc.String);
}
@ -699,7 +701,7 @@ static void ParseOptionMenuBody(FScanner &sc, DOptionMenuDescriptor *desc)
else if (sc.Compare("Class"))
{
sc.MustGetString();
const PClass *cls = PClass::FindClass(sc.String);
PClass *cls = PClass::FindClass(sc.String);
if (cls == nullptr || !cls->IsDescendantOf("OptionMenu"))
{
sc.ScriptError("Unknown menu class '%s'", sc.String);
@ -739,7 +741,7 @@ static void ParseOptionMenuBody(FScanner &sc, DOptionMenuDescriptor *desc)
PClass *cls = PClass::FindClass(buildname);
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.
{
auto &args = func->Variants[0].Proto->ArgumentTypes;
@ -866,7 +868,25 @@ static void ParseOptionMenu(FScanner &sc)
ParseOptionMenuBody(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);
}
else if (sc.Compare("ADDOPTIONMENU"))
{
ParseAddOptionMenu(sc);
}
else if (sc.Compare("DEFAULTOPTIONMENU"))
{
ParseOptionMenuBody(sc, DefaultOptionMenuSettings);
@ -948,7 +972,9 @@ void M_ParseMenuDefs()
}
}
}
DefaultListMenuClass = DefaultListMenuSettings->mClass;
DefaultListMenuSettings = nullptr;
DefaultOptionMenuClass = DefaultOptionMenuSettings->mClass;
DefaultOptionMenuSettings = nullptr;
}
@ -970,13 +996,13 @@ static void BuildEpisodeMenu()
if ((*desc)->IsKindOf(RUNTIME_CLASS(DListMenuDescriptor)))
{
DListMenuDescriptor *ld = static_cast<DListMenuDescriptor*>(*desc);
int posy = ld->mYpos;
int posy = (int)ld->mYpos;
int topy = posy;
// Get lowest y coordinate of any static item in the menu
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;
}
@ -1070,13 +1096,13 @@ static void BuildPlayerclassMenu()
// add player display
ld->mSelectedItem = ld->mItems.Size();
int posy = ld->mYpos;
int posy = (int)ld->mYpos;
int topy = posy;
// Get lowest y coordinate of any static item in the menu
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;
}
@ -1324,19 +1350,56 @@ void M_StartupSkillMenu(FGameStartup *gs)
{
static int done = -1;
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);
if (desc != nullptr)
{
if ((*desc)->IsKindOf(RUNTIME_CLASS(DListMenuDescriptor)))
{
DListMenuDescriptor *ld = static_cast<DListMenuDescriptor*>(*desc);
int x = ld->mXpos;
int y = ld->mYpos;
int x = (int)ld->mXpos;
int y = (int)ld->mYpos;
// Delete previous contents
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)
{
ld->mItems.Resize(i);
@ -1347,12 +1410,7 @@ void M_StartupSkillMenu(FGameStartup *gs)
if (done != restart)
{
done = restart;
int defskill = DefaultSkill;
if ((unsigned int)defskill >= AllSkills.Size())
{
defskill = (AllSkills.Size() - 1) / 2;
}
ld->mSelectedItem = ld->mItems.Size() + defskill;
ld->mSelectedItem = ld->mItems.Size() + defindex;
int posy = y;
int topy = posy;
@ -1360,14 +1418,14 @@ void M_StartupSkillMenu(FGameStartup *gs)
// Get lowest y coordinate of any static item in the menu
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;
}
// 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 topdelta = newtop - topy;
@ -1377,7 +1435,7 @@ void M_StartupSkillMenu(FGameStartup *gs)
{
ld->mItems[i]->OffsetPositionY(topdelta);
}
y = ld->mYpos = posy - topdelta;
ld->mYpos = y = posy - topdelta;
}
}
else
@ -1390,9 +1448,9 @@ void M_StartupSkillMenu(FGameStartup *gs)
}
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;
// Using a different name for skills that must be confirmed makes handling this easier.
FName action = (skill.MustConfirm && !AllEpisodes[gs->Episode].mNoSkill) ?
@ -1406,22 +1464,22 @@ void M_StartupSkillMenu(FGameStartup *gs)
if (skill.PicName.Len() != 0 && pItemText == nullptr)
{
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
{
EColorRange color = (EColorRange)skill.GetTextColor();
if (color == CR_UNTRANSLATED) color = ld->mFontColor;
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);
GC::WriteBarrier(*desc, li);
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
{
@ -1440,7 +1498,7 @@ fail:
MenuDescriptors[NAME_Skillmenu] = od;
od->mMenuName = NAME_Skillmenu;
od->mTitle = "$MNU_CHOOSESKILL";
od->mSelectedItem = 0;
od->mSelectedItem = defindex;
od->mScrollPos = 0;
od->mClass = nullptr;
od->mPosition = -15;
@ -1454,9 +1512,9 @@ fail:
od = static_cast<DOptionMenuDescriptor*>(*desc);
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;
// Using a different name for skills that must be confirmed makes handling this easier.
const char *action = (skill.MustConfirm && !AllEpisodes[gs->Episode].mNoSkill) ?
@ -1467,29 +1525,13 @@ fail:
{
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);
GC::WriteBarrier(od, li);
if (!done)
{
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 "g_game.h"
EXTERN_CVAR (Bool, saveloadconfirmation) // [mxd]
class DMessageBoxMenu : public DMenu
typedef void(*hfunc)();
DEFINE_ACTION_FUNCTION(DMessageBoxMenu, CallHandler)
{
DECLARE_CLASS(DMessageBoxMenu, DMenu)
FBrokenLines *mMessage;
int mMessageMode;
int messageSelection;
int mMouseLeft, mMouseRight, mMouseY;
FName mAction;
public:
DMessageBoxMenu(DMenu *parent = NULL, const char *message = NULL, int messagemode = 0, bool playsound = false, FName action = NAME_None);
void 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);
PARAM_PROLOGUE;
PARAM_POINTERTYPE(Handler, hfunc);
Handler();
return 0;
}
//=============================================================================
@ -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;
if (message != NULL)
{
if (*message == '$') message = GStrings(message+1);
mMessage = V_BreakLines(SmallFont, 300, message);
}
else mMessage = NULL;
mMessageMode = messagemode;
if (playsound)
{
S_StopSound (CHAN_VOICE);
S_Sound (CHAN_VOICE | CHAN_UI, "menu/prompt", snd_menuvolume, ATTN_NONE);
}
}
auto c = PClass::FindClass("MessageBoxMenu");
auto p = c->CreateNew();
VMValue params[] = { p, parent, FString(message), messagemode, playsound, action.GetIndex(), reinterpret_cast<void*>(handler) };
//=============================================================================
//
//
//
//=============================================================================
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();
}
auto f = dyn_cast<PFunction>(c->Symbols.FindSymbol("Init", false));
GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0);
return (DMenu*)p;
}
//=============================================================================
@ -430,75 +83,62 @@ void DQuitMenu::HandleResult(bool res)
CCMD (menu_quit)
{ // F10
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);
}
//=============================================================================
//
//
//
//=============================================================================
//=============================================================================
//
//
//
//=============================================================================
class DEndGameMenu : public DMessageBoxMenu
void ActivateEndGameMenu()
{
DECLARE_CLASS(DEndGameMenu, DMessageBoxMenu)
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)
FString tempstring = GStrings(netgame ? "NETEND" : "ENDGAME");
DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, false, NAME_None, []()
{
M_ClearMenus ();
M_ClearMenus();
if (!netgame)
{
D_StartTitle ();
D_StartTitle();
}
}
else
{
Close();
CloseSound();
}
}
});
//=============================================================================
//
//
//
//=============================================================================
M_ActivateMenu(newmenu);
}
CCMD (menu_endgame)
{ // F7
@ -510,67 +150,8 @@ CCMD (menu_endgame)
//M_StartControlPanel (true);
S_Sound (CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE);
DMenu *newmenu = new DEndGameMenu(false);
newmenu->mParentMenu = DMenu::CurrentMenu;
M_ActivateMenu(newmenu);
}
//=============================================================================
//
//
//
//=============================================================================
//=============================================================================
//
//
//
//=============================================================================
class DQuickSaveMenu : public DMessageBoxMenu
{
DECLARE_CLASS(DQuickSaveMenu, DMessageBoxMenu)
public:
DQuickSaveMenu(bool playsound = false);
virtual void HandleResult(bool res);
};
IMPLEMENT_CLASS(DQuickSaveMenu, 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();
}
ActivateEndGameMenu();
}
//=============================================================================
@ -601,72 +182,23 @@ CCMD (quicksave)
// [mxd]. Just save the game, no questions asked.
if (!saveloadconfirmation)
{
G_SaveGame(savegameManager.quickSaveSlot->Filename.GetChars(), savegameManager.quickSaveSlot->Title);
G_SaveGame(savegameManager.quickSaveSlot->Filename.GetChars(), savegameManager.quickSaveSlot->SaveTitle.GetChars());
return;
}
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;
tempstring.Format(GStrings("QSPROMPT"), savegameManager.quickSaveSlot->SaveTitle.GetChars());
tempstring.Format(GStrings("QLPROMPT"), savegameManager.quickSaveSlot->Title);
Init(NULL, tempstring, 0, playsound);
}
//=============================================================================
//
//
//
//=============================================================================
void DQuickLoadMenu::HandleResult(bool res)
{
if (res)
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);
G_SaveGame(savegameManager.quickSaveSlot->Filename.GetChars(), savegameManager.quickSaveSlot->SaveTitle.GetChars());
S_Sound(CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE);
M_ClearMenus();
}
else
{
Close();
CloseSound();
}
});
M_ActivateMenu(newmenu);
}
//=============================================================================
@ -699,10 +231,17 @@ CCMD (quickload)
G_LoadGame(savegameManager.quickSaveSlot->Filename.GetChars());
return;
}
FString tempstring;
tempstring.Format(GStrings("QLPROMPT"), savegameManager.quickSaveSlot->SaveTitle.GetChars());
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);
}
@ -714,13 +253,13 @@ CCMD (quickload)
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
M_StartControlPanel(menuactive == MENU_Off);
}
DMenu *newmenu = new DMessageBoxMenu(DMenu::CurrentMenu, message, messagemode, false, action);
newmenu->mParentMenu = DMenu::CurrentMenu;
DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, message, messagemode, false, action);
newmenu->mParentMenu = CurrentMenu;
M_ActivateMenu(newmenu);
}
@ -728,8 +267,8 @@ DEFINE_ACTION_FUNCTION(DMenu, StartMessage)
{
PARAM_PROLOGUE;
PARAM_STRING(msg);
PARAM_INT(mode);
PARAM_INT_DEF(mode);
PARAM_NAME_DEF(action);
M_StartMessage(msg, mode, action);
return 0;
}
}

View file

@ -49,32 +49,6 @@
#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++)
{
FName nm = mItems[i]->GetAction(NULL);
FName nm = mItems[i]->mAction;
if (nm == name) return mItems[i];
}
return NULL;

View file

@ -48,12 +48,10 @@
#include "r_data/r_translate.h"
#include "v_text.h"
EXTERN_CVAR (String, playerclass)
EXTERN_CVAR (String, name)
EXTERN_CVAR (Int, team)
EXTERN_CVAR (Float, autoaim)
EXTERN_CVAR(Int, team)
EXTERN_CVAR(Float, autoaim)
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)
int PlayerClassIndex;
FPlayerClass *PlayerClass;
TArray<int> PlayerColorSets;
TArray<int> PlayerSkins;
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)
PARAM_SELF_PROLOGUE(DMenu);
PARAM_INT(r);
PARAM_INT(g);
PARAM_INT(b);
// only allow if the menu is active to prevent abuse.
if (self == CurrentMenu)
{
li->SetValue(ListMenuItemPlayerDisplay_PDF_ROTATION, 0);
li->SetValue(ListMenuItemPlayerDisplay_PDF_MODE, 1);
li->SetValue(ListMenuItemPlayerDisplay_PDF_TRANSLATE, 1);
li->SetValue(ListMenuItemPlayerDisplay_PDF_CLASS, players[consoleplayer].userinfo.GetPlayerClassNum());
if (PlayerClass != NULL && !(GetDefaultByType (PlayerClass->Type)->flags4 & MF4_NOSKIN) &&
players[consoleplayer].userinfo.GetPlayerClassNum() != -1)
{
li->SetValue(ListMenuItemPlayerDisplay_PDF_SKIN, players[consoleplayer].userinfo.GetSkin());
}
char command[24];
players[consoleplayer].userinfo.ColorChanged(MAKERGB(r, g, b));
mysnprintf(command, countof(command), "color \"%02x %02x %02x\"", r, g, b);
C_DoCommand(command);
}
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;
return 0;
}
//=============================================================================
//
//
// 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.
for (p = pp; *p != '\0'; ++p)
for (auto p = pp; *p != '\0'; ++p)
{
if (*p == '"' || *p == '\\')
{
@ -428,41 +103,9 @@ void DPlayerMenu::PlayerNameChanged(DMenuItemBase *li)
command << *p;
}
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);
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;
}
int sel;
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());
}
players[consoleplayer].userinfo.ColorSetChanged(sel);
char command[24];
mysnprintf(command, countof(command), "colorset %d", sel);
C_DoCommand(command);
}
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 ||
players[consoleplayer].userinfo.GetPlayerClassNum() == -1)
PARAM_SELF_PROLOGUE(DMenu);
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);
UpdateTranslation();
cvar_set ("skin", skins[sel].name);
li = GetItem(NAME_Playerdisplay);
if (li != NULL)
{
li->SetValue(ListMenuItemPlayerDisplay_PDF_SKIN, sel);
}
cvar_set("skin", Skins[sel].Name);
}
return 0;
}
//=============================================================================
@ -536,14 +172,16 @@ void DPlayerMenu::SkinChanged (DMenuItemBase *li)
//
//=============================================================================
void DPlayerMenu::AutoaimChanged (DMenuItemBase *li)
DEFINE_ACTION_FUNCTION(DPlayerMenu, AutoaimChanged)
{
int sel;
if (li->GetValue(0, &sel))
PARAM_SELF_PROLOGUE(DMenu);
PARAM_FLOAT(val);
// 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;
if (mDesc->mSelectedItem >= 0)
PARAM_SELF_PROLOGUE(DMenu);
PARAM_INT(val);
// only allow if the menu is active to prevent abuse.
if (self == CurrentMenu)
{
DMenuItemBase *li = mDesc->mItems[mDesc->mSelectedItem];
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;
}
team = val == 0 ? TEAM_NONE : val - 1;
}
return Super::MenuEvent(mkey, fromcontroller);
return 0;
}
//=============================================================================
//
//
//
//=============================================================================
bool DPlayerMenu::MouseEvent(int type, int x, int y)
DEFINE_ACTION_FUNCTION(DPlayerMenu, GenderChanged)
{
int v;
DMenuItemBase *li = mFocusControl;
bool res = Super::MouseEvent(type, x, y);
if (li == NULL) li = mFocusControl;
if (li != NULL)
PARAM_SELF_PROLOGUE(DMenu);
PARAM_INT(v);
// only allow if the menu is active to prevent abuse.
if (self == CurrentMenu)
{
// Check if the colors have changed
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;
}
cvar_set("gender", v == 0 ? "male" : v == 1 ? "female" : "other");
}
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)
{
Super::Ticker();
PARAM_SELF_PROLOGUE(DMenu);
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)
{
Super::Drawer();
const char *str = "PRESS " TEXTCOLOR_WHITE "SPACE";
screen->DrawText (SmallFont, CR_GOLD, 320 - 32 - 32 -
SmallFont->StringWidth (str)/2,
50 + 48 + 70, str,
DTA_Clean, true, TAG_DONE);
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);
PARAM_SELF_PROLOGUE(DMenu);
PARAM_INT(v);
// only allow if the menu is active to prevent abuse.
if (self == CurrentMenu)
{
cl_run = !!v;
}
return 0;
}

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(PowerSpeed)
xx(PowerTorch)
xx(PowerHighJump)
xx(PowerReflection)
xx(PowerDrain)
xx(Reflection)
xx(CustomInventory)
xx(Inventory)
xx(CallTryPickup)
@ -356,6 +360,7 @@ xx(CeilingZ)
xx(FloorZ)
xx(Health)
xx(Pitch)
xx(SpecialName)
xx(Special)
xx(TID)
xx(TIDtoHate)
@ -386,12 +391,19 @@ xx(Radius)
xx(ReactionTime)
xx(MeleeRange)
xx(Speed)
xx(FastSpeed)
xx(HowlSound)
xx(Clamp)
xx(VisibleStartAngle)
xx(VisibleStartPitch)
xx(VisibleEndAngle)
xx(VisibleEndPitch)
xx(Format)
xx(PickupMsg)
xx(Respawnable)
xx(ExplosionDamage)
xx(ExplosionRadius)
xx(DontHurtShooter)
// Various actor names which are used internally
xx(MapSpot)
@ -653,6 +665,8 @@ xx(Link)
xx(Goodbye)
xx(Require)
xx(Exclude)
xx(Userstring)
xx(Sky)
// Special menus
xx(Mainmenu)
@ -670,6 +684,7 @@ xx(Optionsmenu)
xx(Quitmenu)
xx(Savemenu)
xx(Playermenu)
xx(EndGameMenu)
xx(Playerbox)
xx(Team)
@ -765,6 +780,7 @@ xx(BuiltinGetDefault)
xx(BuiltinClassCast)
xx(BuiltinFormat)
xx(Damage)
xx(Noattack)
// basic type names
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));
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);
}
}
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)
else
{
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;
// 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.
// 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.
if (_count0 == 0)
{
side = 0;
seg->next = outset0;
outset0 = set;
_count0++;
}
else
{
side = 1;
seg->next = outset1;
outset1 = set;
_count1++;
}
}
if (GLNodes)
{
AddIntersection (node, vertnum);
}
break;
}
if (side >= 0 && GLNodes)
@ -1062,95 +1078,3 @@ void FNodeBuilder::PrintSet (int l, DWORD set)
}
Printf (PRINT_LOG, "*\n");
}
#ifdef BACKPATCH
#ifdef _WIN32
extern "C" {
__declspec(dllimport) int __stdcall VirtualProtect(void *, unsigned long, unsigned long, unsigned long *);
}
#define PAGE_EXECUTE_READWRITE 64
#else
#include <sys/mman.h>
#include <limits.h>
#include <unistd.h>
#endif
#ifdef __GNUC__
extern "C" int ClassifyLineBackpatch (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2])
#else
static int *CallerOffset;
int ClassifyLineBackpatchC (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2])
#endif
{
// Select the routine based on SSE2 availability and patch the caller so that
// they call that routine directly next time instead of going through here.
int *calleroffset;
int diff;
int (*func)(node_t &, const FSimpleVert *, const FSimpleVert *, int[2]);
#ifdef __GNUC__
calleroffset = (int *)__builtin_return_address(0);
#else
calleroffset = CallerOffset;
#endif
// printf ("Patching for SSE %d @ %p %d\n", SSELevel, calleroffset, *calleroffset);
#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;
};
extern "C"
{
int ClassifyLine2 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]);
#ifndef DISABLE_SSE
int ClassifyLineSSE1 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]);
int ClassifyLineSSE2 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]);
#ifdef BACKPATCH
#ifdef __GNUC__
int ClassifyLineBackpatch (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]) __attribute__((noinline));
#else
int __declspec(noinline) ClassifyLineBackpatch (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]);
#endif
#endif
#endif
}
class FNodeBuilder
{
struct FPrivSeg
@ -282,7 +266,7 @@ private:
// 1 = seg is in back
// -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);
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;
}
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
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_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 intptr_t Bits;
typedef DWORD Bit32u;
typedef SDWORD Bit32s;
typedef int32_t Bit32s;
typedef WORD Bit16u;
typedef SWORD Bit16s;
typedef BYTE Bit8u;

View file

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

View file

@ -180,7 +180,7 @@ inline int PitchToACS(DAngle ang)
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),
ReturnModule(module),
ReturnLocals(locals),
@ -192,7 +192,7 @@ struct CallReturn
ScriptFunction *ReturnFunction;
FBehavior *ReturnModule;
SDWORD *ReturnLocals;
int32_t *ReturnLocals;
ACSLocalArrays *ReturnArrays;
int ReturnAddress;
int bDiscardResult;
@ -206,7 +206,7 @@ static DLevelScript *P_GetScriptGoing (AActor *who, line_t *where, int num, cons
struct FBehavior::ArrayInfo
{
DWORD ArraySize;
SDWORD *Elements;
int32_t *Elements;
};
TArray<FBehavior *> FBehavior::StaticModules;
@ -243,11 +243,11 @@ inline int uallong(const int &foo)
//============================================================================
// ACS variables with world scope
SDWORD ACS_WorldVars[NUM_WORLDVARS];
int32_t ACS_WorldVars[NUM_WORLDVARS];
FWorldGlobalArray ACS_WorldArrays[NUM_WORLDVARS];
// ACS variables with global scope
SDWORD ACS_GlobalVars[NUM_GLOBALVARS];
int32_t ACS_GlobalVars[NUM_GLOBALVARS];
FWorldGlobalArray ACS_GlobalArrays[NUM_GLOBALVARS];
//----------------------------------------------------------------------------
@ -261,7 +261,7 @@ FWorldGlobalArray ACS_GlobalArrays[NUM_GLOBALVARS];
struct FACSStack
{
SDWORD buffer[STACK_SIZE];
int32_t buffer[STACK_SIZE];
int sp;
FACSStack *next;
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;
@ -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);
arc.Array(key, vars, (int)count);
@ -1330,7 +1330,7 @@ static int CheckInventory (AActor *activator, const char *type, bool max)
if (max)
{
if (activator->IsKindOf (RUNTIME_CLASS (APlayerPawn)))
return static_cast<APlayerPawn *>(activator)->MaxHealth;
return static_cast<APlayerPawn *>(activator)->GetMaxHealth();
else
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;
SDWORD first, last;
int32_t count;
int32_t first, last;
if (arc.BeginObject(nullptr))
{
@ -1997,7 +1997,7 @@ bool FBehavior::Init(int lumpnum, FileReader * fr, int len)
{
MapVarStore[LittleLong(chunk[2+i*2])] = i;
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));
}
}
@ -2013,7 +2013,7 @@ bool FBehavior::Init(int lumpnum, FileReader * fr, int len)
// optimizer. Might be some undefined behavior in this code,
// but I don't know what it is.
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)
{
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])];
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)
{
// *elems |= LibraryID;
@ -2088,7 +2088,7 @@ bool FBehavior::Init(int lumpnum, FileReader * fr, int len)
chunkData += 4;
if ((unsigned)arraynum < (unsigned)NumArrays)
{
SDWORD *elems = ArrayStore[arraynum].Elements;
int32_t *elems = ArrayStore[arraynum].Elements;
// Ending zeros may be left out.
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",
"Disconnect",
"Return",
"Event",
"Kill",
"Reopen"
};
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_SpawnHealth: if (actor->IsKindOf (RUNTIME_CLASS (APlayerPawn)))
{
return static_cast<APlayerPawn *>(actor)->MaxHealth;
return static_cast<APlayerPawn *>(actor)->GetMaxHealth();
}
else
{
@ -4230,7 +4232,7 @@ enum
SOUND_Howl,
};
static FSoundID GetActorSound(const AActor *actor, int soundtype)
static FSoundID GetActorSound(AActor *actor, int soundtype)
{
switch (soundtype)
{
@ -4243,7 +4245,7 @@ static FSoundID GetActorSound(const AActor *actor, int soundtype)
case SOUND_Bounce: return actor->BounceSound;
case SOUND_WallBounce: return actor->WallBounceSound;
case SOUND_CrushPain: return actor->CrushPainSound;
case SOUND_Howl: return actor->GetClass()->HowlSound;
case SOUND_Howl: return actor->SoundVar(NAME_HowlSound);
default: return 0;
}
}
@ -4365,6 +4367,11 @@ enum EACSFunctions
ACSF_SetTranslation,
ACSF_GetActorFloorTexture,
ACSF_GetActorFloorTerrain,
ACSF_StrArg,
ACSF_Floor,
ACSF_Round,
ACSF_Ceil,
ACSF_ScriptCall,
// OpenGL stuff
@ -4747,9 +4754,121 @@ static int SwapActorTeleFog(AActor *activator, int tid)
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;
switch(funcIndex)
@ -6085,7 +6204,20 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound)
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:
break;
@ -6176,7 +6308,7 @@ static void SetMarineSprite(AActor *marine, PClassActor *source)
int DLevelScript::RunScript ()
{
DACSThinker *controller = DACSThinker::ActiveThinker;
SDWORD *locals = &Localvars[0];
int32_t *locals = &Localvars[0];
ACSLocalArrays noarrays;
ACSLocalArrays *localarrays = &noarrays;
ScriptFunction *activeFunction = NULL;
@ -6250,7 +6382,7 @@ int DLevelScript::RunScript ()
}
FACSStack stackobj;
SDWORD *Stack = stackobj.buffer;
int32_t *Stack = stackobj.buffer;
int &sp = stackobj.sp;
int *pc = this->pc;
@ -6544,7 +6676,7 @@ int DLevelScript::RunScript ()
int i;
ScriptFunction *func;
FBehavior *module;
SDWORD *mylocals;
int32_t *mylocals;
if(pcd == PCD_CALLSTACK)
{
@ -6599,7 +6731,7 @@ int DLevelScript::RunScript ()
int value;
union
{
SDWORD *retsp;
int32_t *retsp;
CallReturn *ret;
};
@ -7754,7 +7886,7 @@ scriptwait:
while (min <= max)
{
int mid = (min + max) / 2;
SDWORD caseval = LittleLong(pc[mid*2]);
int32_t caseval = LittleLong(pc[mid*2]);
if (caseval == STACK(1))
{
pc = activeBehavior->Ofs2PC (LittleLong(pc[mid*2+1]));
@ -9169,7 +9301,7 @@ scriptwait:
const char *str = FBehavior::StaticLookupString(STACK(1));
if (str != NULL)
{
STACK(1) = SDWORD(strlen(str));
STACK(1) = int32_t(strlen(str));
break;
}
@ -9339,7 +9471,7 @@ scriptwait:
switch (STACK(1))
{
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_GENDER: STACK(2) = userinfo->GetGender(); break;
case PLAYERINFO_NEVERSWITCH: STACK(2) = userinfo->GetNeverSwitch(); break;
@ -9421,7 +9553,10 @@ scriptwait:
}
case PCD_SETMUGSHOTSTATE:
StatusBar->SetMugShotState(FBehavior::StaticLookupString(STACK(1)));
if (!multiplayer || (activator != nullptr && activator->CheckLocalView(consoleplayer)))
{
StatusBar->SetMugShotState(FBehavior::StaticLookupString(STACK(1)));
}
sp--;
break;
@ -9743,7 +9878,7 @@ DLevelScript::DLevelScript (AActor *who, line_t *where, int num, const ScriptPtr
script = num;
assert(code->VarCount >= code->ArgCount);
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)
{
Localvars[i] = args[i];

View file

@ -60,14 +60,14 @@ struct InitIntToZero
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
extern SDWORD ACS_WorldVars[NUM_WORLDVARS];
extern int32_t ACS_WorldVars[NUM_WORLDVARS];
extern FWorldGlobalArray ACS_WorldArrays[NUM_WORLDVARS];
// ACS variables with global scope
extern SDWORD ACS_GlobalVars[NUM_GLOBALVARS];
extern int32_t ACS_GlobalVars[NUM_GLOBALVARS];
extern FWorldGlobalArray ACS_GlobalArrays[NUM_GLOBALVARS];
#define LIBRARYID_MASK 0xFFF00000
@ -323,7 +323,7 @@ public:
ACSProfileInfo *GetFunctionProfileData(ScriptFunction *func) { return GetFunctionProfileData((int)(func - (ScriptFunction *)Functions)); }
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 void StaticLoadDefaultModules ();
@ -359,7 +359,7 @@ private:
ArrayInfo **Arrays;
int NumTotalArrays;
DWORD StringTable;
SDWORD MapVarStore[NUM_MAPVARS];
int32_t MapVarStore[NUM_MAPVARS];
TArray<FBehavior *> Imports;
DWORD LibraryID;
char ModuleName[9];
@ -375,7 +375,7 @@ private:
int FindStringInChunk (DWORD *chunk, const char *varname) const;
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 LockMapVarStrings() const;
@ -919,7 +919,7 @@ protected:
int DoSpawnSpot (int type, int spot, int tid, int angle, bool forced);
int DoSpawnSpotFacing (int type, int spot, int tid, bool forced);
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 DoFadeRange (int r1, int g1, int b1, int a1,

View file

@ -80,12 +80,12 @@
#include "math/cmath.h"
#include "g_levellocals.h"
#include "r_utility.h"
#include "sbar.h"
AActor *SingleActorFromTID(int tid, AActor *defactor);
static FRandom pr_camissile ("CustomActorfire");
static FRandom pr_camelee ("CustomMelee");
static FRandom pr_cabullet ("CustomBullet");
static FRandom pr_cajump ("CustomJump");
static FRandom pr_cwbullet ("CustomWpBullet");
@ -438,22 +438,6 @@ DEFINE_ACTION_FUNCTION(AActor, GetSpawnHealth)
return 0;
}
//==========================================================================
//
// GetGibHealth
//
//==========================================================================
DEFINE_ACTION_FUNCTION(AActor, GetGibHealth)
{
if (numret > 0)
{
PARAM_SELF_PROLOGUE(AActor);
ret->SetInt(self->GetGibHealth());
return 1;
}
return 0;
}
//==========================================================================
//
// GetSpriteAngle
@ -923,86 +907,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_CopyFriendliness)
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.
@ -1124,7 +1028,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_SeekerMissile)
PARAM_INT_DEF(chance);
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);
}
@ -1260,9 +1164,9 @@ DEFINE_ACTION_FUNCTION(AActor, A_Explode)
if (damage < 0) // get parameters from metadata
{
damage = self->GetClass()->ExplosionDamage;
distance = self->GetClass()->ExplosionRadius;
flags = !self->GetClass()->DontHurtShooter;
damage = self->IntVar(NAME_ExplosionDamage);
distance = self->IntVar(NAME_ExplosionRadius);
flags = !self->BoolVar(NAME_DontHurtShooter);
alert = false;
}
if (distance <= 0) distance = damage;
@ -1410,26 +1314,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_RadiusDamageSelf)
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!
@ -2696,8 +2580,7 @@ static bool InitSpawnedItem(AActor *self, AActor *mo, int flags)
else if (flags & SIXF_USEBLOODCOLOR)
{
// [XA] Use the spawning actor's BloodColor to translate the newly-spawned object.
PalEntry bloodcolor = self->GetBloodColor();
mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a);
mo->Translation = self->BloodTranslation;
}
}
if (flags & SIXF_TRANSFERPOINTERS)
@ -3683,13 +3566,14 @@ DEFINE_ACTION_FUNCTION(AActor, A_DropInventory)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_CLASS(drop, AInventory);
PARAM_INT_DEF(amount);
if (drop)
{
AInventory *inv = self->FindInventory(drop);
if (inv)
{
self->DropInventory(inv);
self->DropInventory(inv, amount);
}
}
return 0;
@ -3939,7 +3823,7 @@ DEFINE_ACTION_FUNCTION(AActor, PlayerSkinCheck)
PARAM_SELF_PROLOGUE(AActor);
ACTION_RETURN_BOOL(self->player != NULL &&
skins[self->player->userinfo.GetSkin()].othergame);
Skins[self->player->userinfo.GetSkin()].othergame);
}
// [KS] *** Start of my modifications ***
@ -4621,6 +4505,13 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeCountFlags)
return 0;
}
enum ERaise
{
RF_TRANSFERFRIENDLINESS = 1,
RF_NOCHECKPOSITION = 2
};
//===========================================================================
//
// A_RaiseMaster
@ -4629,11 +4520,12 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeCountFlags)
DEFINE_ACTION_FUNCTION(AActor, A_RaiseMaster)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_BOOL_DEF(copy);
PARAM_INT_DEF(flags);
bool copy = !!(flags & RF_TRANSFERFRIENDLINESS);
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;
}
@ -4646,16 +4538,17 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseMaster)
DEFINE_ACTION_FUNCTION(AActor, A_RaiseChildren)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_BOOL_DEF(copy);
PARAM_INT_DEF(flags);
TThinkerIterator<AActor> it;
AActor *mo;
bool copy = !!(flags & RF_TRANSFERFRIENDLINESS);
while ((mo = it.Next()) != NULL)
{
if (mo->master == self)
{
P_Thing_Raise(mo, copy ? self : NULL);
P_Thing_Raise(mo, copy ? self : NULL, (flags & RF_NOCHECKPOSITION));
}
}
return 0;
@ -4669,18 +4562,19 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseChildren)
DEFINE_ACTION_FUNCTION(AActor, A_RaiseSiblings)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_BOOL_DEF(copy);
PARAM_INT_DEF(flags);
TThinkerIterator<AActor> it;
AActor *mo;
bool copy = !!(flags & RF_TRANSFERFRIENDLINESS);
if (self->master != NULL)
{
while ((mo = it.Next()) != NULL)
{
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;
}
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
{
SWORD wallptr, wallnum;
SDWORD ceilingZ, floorZ;
int32_t ceilingZ, floorZ;
SWORD ceilingstat, floorstat;
SWORD ceilingpicnum, ceilingheinum;
SBYTE ceilingshade;
@ -74,7 +74,7 @@ struct sectortype
//32 bytes
struct walltype
{
SDWORD x, y;
int32_t x, y;
SWORD point2, nextwall, nextsector, cstat;
SWORD picnum, overpicnum;
SBYTE shade;
@ -100,7 +100,7 @@ struct walltype
//44 bytes
struct spritetype
{
SDWORD x, y, z;
int32_t x, y, z;
SWORD cstat, picnum;
SBYTE shade;
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 LoadWalls (walltype *walls, int numwalls, sectortype *bsectors);
static int LoadSprites (spritetype *sprites, Xsprite *xsprites, int numsprites, sectortype *bsectors, FMapThing *mapthings);
static vertex_t *FindVertex (SDWORD x, SDWORD y);
static void CreateStartSpot (SDWORD *pos, FMapThing *start);
static vertex_t *FindVertex (int32_t x, int32_t y);
static void CreateStartSpot (int32_t *pos, FMapThing *start);
static void CalcPlane (SlopeWork &slope, secplane_t &plane);
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));
*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)),
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;
@ -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]));
FMapThing mt = { 0, };

View file

@ -582,3 +582,18 @@ bool EV_CeilingCrushStop (int tag, bool remove)
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 "menu/menu.h"
#include "g_levellocals.h"
#include "virtual.h"
// The conversations as they exist inside a SCRIPTxx lump.
struct Response
{
SDWORD GiveType;
SDWORD Item[3];
SDWORD Count[3];
int32_t GiveType;
int32_t Item[3];
int32_t Count[3];
char Reply[32];
char Yes[80];
SDWORD Link;
DWORD Log;
int32_t Link;
uint32_t Log;
char No[80];
};
struct Speech
{
DWORD SpeakerType;
SDWORD DropType;
SDWORD ItemCheck[3];
SDWORD Link;
uint32_t SpeakerType;
int32_t DropType;
int32_t ItemCheck[3];
int32_t Link;
char Name[16];
char Sound[8];
char Backdrop[8];
@ -91,9 +92,9 @@ struct Speech
// The Teaser version of the game uses an older version of the structure
struct TeaserSpeech
{
DWORD SpeakerType;
SDWORD DropType;
DWORD VoiceNumber;
uint32_t SpeakerType;
int32_t DropType;
uint32_t VoiceNumber;
char Name[16];
char Dialogue[320];
Response Responses[5];
@ -112,11 +113,11 @@ static FDialogueMap ClassRoots;
static int ConversationMenuY;
static int ConversationPauseTic;
static bool ShowGold;
static int StaticLastReply;
static bool LoadScriptFile(int lumpnum, FileReader *lump, int numnodes, bool include, int type);
static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeakerType);
static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, DWORD &prevSpeakerType);
static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, uint32_t &prevSpeakerType);
static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, uint32_t &prevSpeakerType);
static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses);
static bool DrawConversationMenu ();
static void PickConversationReply (int replyindex);
@ -124,9 +125,6 @@ static void TerminalResponse (const char *str);
static FStrifeDialogueNode *PrevNode;
#define NUM_RANDOM_LINES 10
#define NUM_RANDOM_GOODBYES 3
//============================================================================
//
// GetStrifeType
@ -207,6 +205,11 @@ void P_LoadStrifeConversations (MapData *map, const char *mapname)
{
if (!LoadScriptFile (scriptname_b, false, 1))
{
if (gameinfo.Dialogue.IsNotEmpty())
{
if (LoadScriptFile(gameinfo.Dialogue, false, 0)) return;
}
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)
{
int i;
DWORD prevSpeakerType;
uint32_t prevSpeakerType;
FStrifeDialogueNode *node;
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;
Speech speech;
@ -347,11 +350,11 @@ static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeaker
}
// 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.
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.
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.
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.
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;
TeaserSpeech speech;
@ -422,10 +425,10 @@ static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, DWORD &prevSpeaker
}
// 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.
node->Backdrop.SetInvalid();
node->Backdrop = "";
// The speaker's voice for this node, if any.
if (speech.VoiceNumber != 0)
@ -440,7 +443,7 @@ static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, DWORD &prevSpeaker
// The speaker's name, if any.
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.
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.
reply->LogNumber = rsp->Log;
reply->LogString = NULL;
reply->LogString = "";
// The item to receive when this reply is used.
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].Amount = rsp->Count[k];
}
reply->PrintAmount = reply->ItemCheck[0].Amount;
reply->ItemCheckRequire.Clear();
reply->ItemCheckExclude.Clear();
// If the first item check has a positive amount required, then
// 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);
// QuickYes messages are shown when you meet the item checks.
// QuickNo messages are shown when you don't.
if (rsp->Yes[0] == '_' && rsp->Yes[1] == 0)
{
reply->QuickYes = NULL;
reply->QuickYes = "";
}
else
{
reply->QuickYes = ncopystring (rsp->Yes);
reply->QuickYes = rsp->Yes;
}
if (reply->ItemCheck[0].Item != 0)
{
reply->QuickNo = ncopystring (rsp->No);
reply->QuickNo = rsp->No;
}
else
{
reply->QuickNo = NULL;
reply->QuickNo = "";
}
reply->Next = *replyptr;
*replyptr = reply;
@ -560,9 +564,6 @@ static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses)
FStrifeDialogueNode::~FStrifeDialogueNode ()
{
if (SpeakerName != NULL) delete[] SpeakerName;
if (Dialogue != NULL) delete[] Dialogue;
if (Goodbye != nullptr) delete[] Goodbye;
FStrifeDialogueReply *tokill = Children;
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
@ -672,7 +660,7 @@ CUSTOM_CVAR(Float, dlg_musicvolume, 1.0f, CVAR_ARCHIVE)
static bool ShouldSkipReply(FStrifeDialogueReply *reply, player_t *player)
{
if (reply->Reply == nullptr)
if (reply->Reply.IsEmpty())
return true;
int i;
@ -694,428 +682,60 @@ static bool ShouldSkipReply(FStrifeDialogueReply *reply, player_t *player)
return false;
}
//============================================================================
//
// The conversation menu
//
//============================================================================
class DConversationMenu : public DMenu
DEFINE_ACTION_FUNCTION(FStrifeDialogueReply, ShouldSkipReply)
{
DECLARE_CLASS(DConversationMenu, DMenu)
PARAM_SELF_STRUCT_PROLOGUE(FStrifeDialogueReply);
PARAM_POINTER(player, player_t);
ACTION_RETURN_BOOL(ShouldSkipReply(self, player));
}
FString mSpeaker;
FBrokenLines *mDialogueLines;
TArray<FString> mResponseLines;
TArray<unsigned int> mResponses;
bool mShowGold;
FStrifeDialogueNode *mCurNode;
int mYpos;
player_t *mPlayer;
DEFINE_ACTION_FUNCTION(DConversationMenu, SendConversationReply)
{
PARAM_PROLOGUE;
PARAM_INT(node);
PARAM_INT(reply);
switch (node)
{
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:
static int mSelection;
FBrokenLines *mBroken;
unsigned int mCount;
//=============================================================================
//
//
//
//=============================================================================
DConversationMenu(FStrifeDialogueNode *CurNode, player_t *player)
DBrokenLines(FBrokenLines *broken, unsigned int count)
{
mCurNode = CurNode;
mPlayer = player;
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;
}
mBroken = broken;
mCount = count;
}
//=============================================================================
//
//
//
//=============================================================================
void OnDestroy() override
{
V_FreeBrokenLines(mDialogueLines);
mDialogueLines = NULL;
I_SetMusicVolume (1.f);
Super::OnDestroy();
V_FreeBrokenLines(mBroken);
}
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
@ -1135,9 +755,9 @@ void P_FreeStrifeConversations ()
ClassRoots.Clear();
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);
}
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)
{ // Only reset the selection if showing a different menu.
DConversationMenu::mSelection = 0;
StaticLastReply = 0;
PrevNode = CurNode;
}
// And open the menu
M_StartControlPanel (false);
M_ActivateMenu(cmenu);
ConversationPauseTic = gametic + 20;
M_ActivateMenu((DMenu*)cmenu);
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))
{
// No, you don't. Say so and let the NPC animate negatively.
if (reply->QuickNo && isconsole)
if (reply->QuickNo.IsNotEmpty() && isconsole)
{
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.
if (reply->LogString != NULL)
if (reply->LogString.IsNotEmpty())
{
const char *log = reply->LogString;
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
// doesn't happen during demo playback, so we need to do it here.
if (demoplayback && DMenu::CurrentMenu != NULL &&
DMenu::CurrentMenu->IsKindOf(RUNTIME_CLASS(DConversationMenu)))
if (demoplayback && CurrentMenu != NULL && CurrentMenu->IsKindOf("ConversationMenu"))
{
DMenu::CurrentMenu->Close();
CurrentMenu->Close();
}
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
{
~FStrifeDialogueNode ();
PClassActor *DropType;
PClassActor *DropType = nullptr;
TArray<FStrifeDialogueItemCheck> ItemCheck;
int ThisNodeNum; // location of this node in StrifeDialogues
int ItemCheckNode; // index into StrifeDialogues
int ThisNodeNum = 0; // location of this node in StrifeDialogues
int ItemCheckNode = 0; // index into StrifeDialogues
PClassActor *SpeakerType;
char *SpeakerName;
PClassActor *SpeakerType = nullptr;
FString SpeakerName;
FSoundID SpeakerVoice;
FTextureID Backdrop;
char *Dialogue;
char *Goodbye = nullptr; // must init to null for binary scripts to work as intended
FString Backdrop;
FString Dialogue;
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
struct FStrifeDialogueReply
{
~FStrifeDialogueReply ();
FStrifeDialogueReply *Next;
PClassActor *GiveType;
int ActionSpecial;
int Args[5];
FStrifeDialogueReply *Next = nullptr;
PClassActor *GiveType = nullptr;
int ActionSpecial = 0;
int Args[5] = {};
int PrintAmount = 0;
TArray<FStrifeDialogueItemCheck> ItemCheck;
TArray<FStrifeDialogueItemCheck> ItemCheckRequire;
TArray<FStrifeDialogueItemCheck> ItemCheckExclude;
char *Reply;
char *QuickYes;
int NextNode; // index into StrifeDialogues
int LogNumber;
char *LogString;
char *QuickNo;
bool NeedsGold;
FString Reply;
FString QuickYes;
FString QuickNo;
FString LogString;
int NextNode = 0; // index into StrifeDialogues
int LogNumber = 0;
bool NeedsGold = false;
};
extern TArray<FStrifeDialogueNode *> StrifeDialogues;

View file

@ -538,7 +538,8 @@ void DAnimatedDoor::Serialize(FSerializer &arc)
("delay", m_Delay)
("dooranim", m_DoorAnim)
("setblock1", m_SetBlocking1)
("setblock2", m_SetBlocking2);
("setblock2", m_SetBlocking2)
("type", m_Type);
}
//============================================================================
@ -614,7 +615,6 @@ void DAnimatedDoor::Tick ()
}
m_Timer = m_Delay;
m_Status = Waiting;
}
else
{
@ -631,7 +631,7 @@ void DAnimatedDoor::Tick ()
case Waiting:
// IF DOOR IS DONE WAITING...
if (!m_Timer--)
if (m_Type == adClose || !m_Timer--)
{
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)
{
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();
m_Status = Opening;
m_Type = type;
m_Status = type == adClose? Waiting : Opening;
m_Speed = speed;
m_Delay = delay;
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_BotDist = m_Sector->ceilingplane.fD();
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;
int secnum;
@ -756,7 +760,7 @@ bool EV_SlidingDoor (line_t *line, AActor *actor, int tag, int speed, int delay)
sec = line->backsector;
// Make sure door isn't already being animated
if (sec->ceilingdata != NULL)
if (sec->ceilingdata != NULL )
{
if (actor->player == NULL)
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));
if (anim != NULL)
{
new DAnimatedDoor (sec, line, speed, delay, anim);
new DAnimatedDoor (sec, line, speed, delay, anim, type);
return true;
}
return false;
@ -799,7 +803,7 @@ bool EV_SlidingDoor (line_t *line, AActor *actor, int tag, int speed, int delay)
if (anim != NULL)
{
rtn = true;
new DAnimatedDoor (sec, line, speed, delay, anim);
new DAnimatedDoor (sec, line, speed, delay, anim, type);
break;
}
}

View file

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

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