diff --git a/specs/udmf_zdoom.txt b/specs/udmf_zdoom.txt index 797d092efa..eeb3c2a560 100644 --- a/specs/udmf_zdoom.txt +++ b/specs/udmf_zdoom.txt @@ -197,6 +197,7 @@ Note: All fields default to false unless mentioned otherwise. desaturation = ; // Color desaturation factor. 0 = none, 1 = full, default = 0. silent = ; // Actors in this sector make no sound, nofallingdamage = ; // Falling damage is disabled in this sector + noattack = ; // Blocks monster attacks in this sector. dropactors = ; // Actors drop with instantly moving floors (*) norespawn = ; // Players can not respawn in this sector soundsequence = ; // The sound sequence to play when this sector moves. Placing a @@ -262,7 +263,7 @@ Note: All fields default to false unless mentioned otherwise. gravity = ; // Set per-actor gravity. Positive values are multiplied with the class's property, // negative values are used as their absolute. Default = 1.0. - health = ; // Set per-actor health. Positive values are multiplied with the class's property, + health = ; // Set per-actor health. Positive values are multiplied with the class's property, // negative values are used as their absolute. Default = 1. renderstyle = ; // Set per-actor render style, overriding the class default. Possible values can be "normal", diff --git a/specs/usdf_zdoom.txt b/specs/usdf_zdoom.txt index f800b5ae79..2025b06ba6 100644 --- a/specs/usdf_zdoom.txt +++ b/specs/usdf_zdoom.txt @@ -38,6 +38,7 @@ conversation page { drop = ; + userstring = ; New field which can be used to pass data to custom conversation menu classes. ifitem { item = ; @@ -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 = ; 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 = ; //Override the default conversation menu class for this conversation. page { @@ -94,6 +92,8 @@ conversation // Starts a dialog. choice { + specialname = ; // 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. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 84e1f576d7..4ff73e32c8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/actionspecials.h b/src/actionspecials.h index 3edb1671d3..173a07b2d3 100644 --- a/src/actionspecials.h +++ b/src/actionspecials.h @@ -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 diff --git a/src/actor.h b/src/actor.h index a973c101a6..e435a1fb72 100644 --- a/src/actor.h +++ b/src/actor.h @@ -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 lastenemy; // Last known enemy -- killough 2/15/98 TObjPtr 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 LastLookActor; // Actor last looked for (if TIDtoHate != 0) @@ -1094,7 +1080,6 @@ public: TObjPtr alternative; // (Un)Morphed actors stored here. Those with the MF_UNMORPHED flag are the originals. TObjPtr tracer; // Thing being chased/attacked for tracers TObjPtr 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 { diff --git a/src/am_map.cpp b/src/am_map.cpp index 9968f4ac43..8df0741828 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -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) { diff --git a/src/b_think.cpp b/src/b_think.cpp index da3fb61246..f286dd30f5 100644 --- a/src/b_think.cpp +++ b/src/b_think.cpp @@ -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 || diff --git a/src/basictypes.h b/src/basictypes.h index ff2cd972e6..acb499c0d4 100644 --- a/src/basictypes.h +++ b/src/basictypes.h @@ -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<GetKeysForCommand(cmd.GetChars(), &k1, &k2); if (numret > 0) ret[0].SetInt(k1); if (numret > 1) ret[1].SetInt(k2); diff --git a/src/c_cmds.cpp b/src/c_cmds.cpp index e8f9509e56..4bdca624a2 100644 --- a/src/c_cmds.cpp +++ b/src/c_cmds.cpp @@ -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 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); diff --git a/src/c_console.cpp b/src/c_console.cpp index 0b1e582874..83e61d3feb 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -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; diff --git a/src/c_cvars.cpp b/src/c_cvars.cpp index cd48e304ec..6430d95ad5 100644 --- a/src/c_cvars.cpp +++ b/src/c_cvars.cpp @@ -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(); diff --git a/src/c_dispatch.cpp b/src/c_dispatch.cpp index 3439034958..184f33adb1 100644 --- a/src/c_dispatch.cpp +++ b/src/c_dispatch.cpp @@ -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); diff --git a/src/c_functions.cpp b/src/c_functions.cpp index 1d1b35a454..ba3eb11755 100644 --- a/src/c_functions.cpp +++ b/src/c_functions.cpp @@ -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) diff --git a/src/c_functions.h b/src/c_functions.h index 3d8ab6fc81..4989469a66 100644 --- a/src/c_functions.h +++ b/src/c_functions.h @@ -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; \ No newline at end of file diff --git a/src/cmdlib.cpp b/src/cmdlib.cpp index f7fb94510a..471c02cc8a 100644 --- a/src/cmdlib.cpp +++ b/src/cmdlib.cpp @@ -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 diff --git a/src/cmdlib.h b/src/cmdlib.h index 6e9fcd622f..7af4e630b5 100644 --- a/src/cmdlib.h +++ b/src/cmdlib.h @@ -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); diff --git a/src/compatibility.cpp b/src/compatibility.cpp index 0b27a1efa0..37531d40fa 100644 --- a/src/compatibility.cpp +++ b/src/compatibility.cpp @@ -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; + } } } } diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 9cfd745203..7e2b22e277 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -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"); diff --git a/src/d_event.h b/src/d_event.h index 9e90054a33..962286b8f2 100644 --- a/src/d_event.h +++ b/src/d_event.h @@ -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; +}; diff --git a/src/d_main.cpp b/src/d_main.cpp index c7a001dfe1..77e0edda04 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -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(); diff --git a/src/d_net.cpp b/src/d_net.cpp index 0985cf41c9..6ba77bc2ae 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -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: diff --git a/src/d_netinfo.cpp b/src/d_netinfo.cpp index 62beb54601..8a856b44b2 100644 --- a/src/d_netinfo.cpp +++ b/src/d_netinfo.cpp @@ -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(), diff --git a/src/d_player.h b/src/d_player.h index 7fa0d45b26..379265cab1 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -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 return aim; } } + // Same but unfiltered. + double GetAutoaim() const + { + return *static_cast(*CheckKey(NAME_Autoaim)); + } const char *GetName() const { return *static_cast(*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. diff --git a/src/d_protocol.h b/src/d_protocol.h index 27af1dae26..f95e3fc816 100644 --- a/src/d_protocol.h +++ b/src/d_protocol.h @@ -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 diff --git a/src/dobject.cpp b/src/dobject.cpp index 747c1f9bfa..b8097f5498 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -623,12 +623,20 @@ DEFINE_ACTION_FUNCTION(DObject, MSTime) void *DObject::ScriptVar(FName field, PType *type) { - auto sym = dyn_cast(GetClass()->Symbols.FindSymbol(field, true)); + auto cls = GetClass(); + auto sym = dyn_cast(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; } diff --git a/src/dobject.h b/src/dobject.h index 5bcb81c504..cc6672cdfd 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -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 T*& PointerVar(FName field); // If you need to replace one object with another and want to diff --git a/src/dobjgc.cpp b/src/dobjgc.cpp index 8ac991aa82..4f0aa20db4 100644 --- a/src/dobjgc.cpp +++ b/src/dobjgc.cpp @@ -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); diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 211c73e824..1a582d3662 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -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(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 TArrayFlags & 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(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(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(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 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(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(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; } //========================================================================== diff --git a/src/dobjtype.h b/src/dobjtype.h index 776d66ce3b..de3b9c9162 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -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 *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 SpecialInits; +protected: + TArray MetaInits; void Derive(PClass *newclass, FName name); - void InitializeSpecials(void *addr, void *defaults) const; + void InitializeSpecials(void *addr, void *defaults, TArray 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 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 inline T *&DObject::PointerVar(FName field) { diff --git a/src/doomdata.h b/src/doomdata.h index eca7dcd4aa..0c1fb4a970 100644 --- a/src/doomdata.h +++ b/src/doomdata.h @@ -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; diff --git a/src/doomdef.h b/src/doomdef.h index 5a1d1f95f4..a07985bb65 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -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; diff --git a/src/dthinker.cpp b/src/dthinker.cpp index da68d83886..fddc283177 100644 --- a/src/dthinker.cpp +++ b/src/dthinker.cpp @@ -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]); diff --git a/src/events.cpp b/src/events.cpp index bb7b34f668..75dd8cde8f 100755 --- a/src/events.cpp +++ b/src/events.cpp @@ -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]); } } diff --git a/src/events.h b/src/events.h index 71c8a27f67..fa63e73258 100755 --- a/src/events.h +++ b/src/events.h @@ -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 diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index 68d9407046..1a979824f7 100644 --- a/src/fragglescript/t_func.cpp +++ b/src/fragglescript/t_func.cpp @@ -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; nAttachMessage(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 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. diff --git a/src/g_game.h b/src/g_game.h index 0a11527fd7..f3f7740398 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -94,6 +94,7 @@ extern AActor *bodyque[BODYQUESIZE]; extern int bodyqueslot; class AInventory; extern const AInventory *SendItemUse, *SendItemDrop; +extern int SendItemDropAmount; #endif diff --git a/src/g_hub.cpp b/src/g_hub.cpp index e87dcff7d9..ecf3106ce4 100644 --- a/src/g_hub.cpp +++ b/src/g_hub.cpp @@ -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) diff --git a/src/g_inventory/a_pickups.cpp b/src/g_inventory/a_pickups.cpp index 53e7d84ed7..df6679d41a 100644 --- a/src/g_inventory/a_pickups.cpp +++ b/src/g_inventory/a_pickups.cpp @@ -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); } //=========================================================================== diff --git a/src/g_inventory/a_pickups.h b/src/g_inventory/a_pickups.h index 817fcf1769..9b2fc1cc1a 100644 --- a/src/g_inventory/a_pickups.h +++ b/src/g_inventory/a_pickups.h @@ -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 InvFlags; +//typedef TFlags 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; diff --git a/src/g_level.cpp b/src/g_level.cpp index 0842f3ec84..b0de51b43e 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -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 ; iplayer != 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) //========================================================================== // diff --git a/src/g_level.h b/src/g_level.h index d59a4fc09e..06a91259a5 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -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 > 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; diff --git a/src/g_levellocals.h b/src/g_levellocals.h index 357e2e1545..1b3d1dba2d 100644 --- a/src/g_levellocals.h +++ b/src/g_levellocals.h @@ -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 vertexes; diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 6f26363371..7a7960115e 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -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 = ""; } //========================================================================== diff --git a/src/g_shared/a_decals.cpp b/src/g_shared/a_decals.cpp index dfa66795ca..21f116b072 100644 --- a/src/g_shared/a_decals.cpp +++ b/src/g_shared/a_decals.cpp @@ -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()) { diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index 359e8a1742..9259561b09 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -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); +} diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h index c9d0d86cef..ea367dd3a4 100644 --- a/src/g_shared/a_sharedglobal.h +++ b/src/g_shared/a_sharedglobal.h @@ -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 { diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index d39c16d88e..7fea7b014d 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -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(); diff --git a/src/g_skill.cpp b/src/g_skill.cpp index d158fa4cdd..25108c0dfd 100644 --- a/src/g_skill.cpp +++ b/src/g_skill.cpp @@ -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; diff --git a/src/g_statusbar/sbar.h b/src/g_statusbar/sbar.h index 3c911e6932..1e8af15144 100644 --- a/src/g_statusbar/sbar.h +++ b/src/g_statusbar/sbar.h @@ -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; diff --git a/src/g_statusbar/sbar_mugshot.cpp b/src/g_statusbar/sbar_mugshot.cpp index d24298b949..18e0fec42a 100644 --- a/src/g_statusbar/sbar_mugshot.cpp +++ b/src/g_statusbar/sbar_mugshot.cpp @@ -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; diff --git a/src/g_statusbar/sbarinfo.cpp b/src/g_statusbar/sbarinfo.cpp index 2fd21737f2..5096ee8134 100644 --- a/src/g_statusbar/sbarinfo.cpp +++ b/src/g_statusbar/sbarinfo.cpp @@ -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" diff --git a/src/g_statusbar/sbarinfo_commands.cpp b/src/g_statusbar/sbarinfo_commands.cpp index ae6ba5b000..ad2da961d4 100644 --- a/src/g_statusbar/sbarinfo_commands.cpp +++ b/src/g_statusbar/sbarinfo_commands.cpp @@ -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; diff --git a/src/g_statusbar/strife_sbar.cpp b/src/g_statusbar/strife_sbar.cpp index 177117eb2b..9726574827 100644 --- a/src/g_statusbar/strife_sbar.cpp +++ b/src/g_statusbar/strife_sbar.cpp @@ -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; diff --git a/src/gi.cpp b/src/gi.cpp index 9f6827ab45..ad5c5a0eea 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -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 { diff --git a/src/gi.h b/src/gi.h index 1bab4e9a7a..2c614e3c70 100644 --- a/src/gi.h +++ b/src/gi.h @@ -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; diff --git a/src/gl/compatibility/gl_20.cpp b/src/gl/compatibility/gl_20.cpp index eeb5572c18..7fc8b783bd 100644 --- a/src/gl/compatibility/gl_20.cpp +++ b/src/gl/compatibility/gl_20.cpp @@ -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" diff --git a/src/gl/data/gl_data.cpp b/src/gl/data/gl_data.cpp index 93e4d5697a..36ae3ad8f4 100644 --- a/src/gl/data/gl_data.cpp +++ b/src/gl/data/gl_data.cpp @@ -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) { diff --git a/src/gl/dynlights/a_dynlight.cpp b/src/gl/dynlights/a_dynlight.cpp index 1255401ae9..bd25752165 100644 --- a/src/gl/dynlights/a_dynlight.cpp +++ b/src/gl/dynlights/a_dynlight.cpp @@ -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(); diff --git a/src/gl/dynlights/gl_dynlight.cpp b/src/gl/dynlights/gl_dynlight.cpp index 5ce79fd09a..4b0eac499d 100644 --- a/src/gl/dynlights/gl_dynlight.cpp +++ b/src/gl/dynlights/gl_dynlight.cpp @@ -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 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)(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 diff --git a/src/gl/dynlights/gl_dynlight.h b/src/gl/dynlights/gl_dynlight.h index ee581e74a7..4733d853f4 100644 --- a/src/gl/dynlights/gl_dynlight.h +++ b/src/gl/dynlights/gl_dynlight.h @@ -141,6 +141,7 @@ public: bool halo; BYTE color2[3]; bool visibletoplayer; + bool swapped; int bufferindex; diff --git a/src/gl/scene/gl_sprite.cpp b/src/gl/scene/gl_sprite.cpp index 5e8bdb560e..3a6abb3197 100644 --- a/src/gl/scene/gl_sprite.cpp +++ b/src/gl/scene/gl_sprite.cpp @@ -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; } diff --git a/src/gl/system/gl_framebuffer.cpp b/src/gl/system/gl_framebuffer.cpp index 1a4582d801..864d796a81 100644 --- a/src/gl/system/gl_framebuffer.cpp +++ b/src/gl/system/gl_framebuffer.cpp @@ -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 diff --git a/src/gl/system/gl_framebuffer.h b/src/gl/system/gl_framebuffer.h index 0eb289ffe1..d6306fcdf8 100644 --- a/src/gl/system/gl_framebuffer.h +++ b/src/gl/system/gl_framebuffer.h @@ -83,6 +83,8 @@ public: bool Is8BitMode() { return false; } bool IsHWGammaActive() const { return HWGammaActive; } + void SetVSync(bool vsync); + private: PalEntry Flash; diff --git a/src/gl/system/gl_wipe.cpp b/src/gl/system/gl_wipe.cpp index 642f20fb10..51d3f90421 100644 --- a/src/gl/system/gl_wipe.cpp +++ b/src/gl/system/gl_wipe.cpp @@ -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 diff --git a/src/info.cpp b/src/info.cpp index 90d847fcc8..fdda8633e2 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -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(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; diff --git a/src/info.h b/src/info.h index 05785f90b6..6caa273254 100644 --- a/src/info.h +++ b/src/info.h @@ -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 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 RestrictedToPlayerClass; TArray ForbiddenToPlayerClass; diff --git a/src/intermission/intermission.cpp b/src/intermission/intermission.cpp index 8310914bef..5dddef1051 100644 --- a/src/intermission/intermission.cpp +++ b/src/intermission/intermission.cpp @@ -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; diff --git a/src/intermission/intermission.h b/src/intermission/intermission.h index cdc100d888..f0b423150c 100644 --- a/src/intermission/intermission.h +++ b/src/intermission/intermission.h @@ -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; diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index c78ab5dde1..652385df28 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -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); } diff --git a/src/m_cheat.h b/src/m_cheat.h index baab5a4513..729c3bff66 100644 --- a/src/m_cheat.h +++ b/src/m_cheat.h @@ -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); diff --git a/src/menu/joystickmenu.cpp b/src/menu/joystickmenu.cpp index f1015b79c1..59b3ad9903 100644 --- a/src/menu/joystickmenu.cpp +++ b/src/menu/joystickmenu.cpp @@ -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("mJoy"); + auto p = CurrentMenu->PointerVar("mJoy"); if (p != nullptr) { unsigned i; @@ -204,7 +204,7 @@ void UpdateJoystickMenu(IJoystickConfig *selected) } if (i == Joysticks.Size()) { - DMenu::CurrentMenu->Close(); + CurrentMenu->Close(); } } } diff --git a/src/menu/listmenu.cpp b/src/menu/listmenu.cpp deleted file mode 100644 index 18c627359d..0000000000 --- a/src/menu/listmenu.cpp +++ /dev/null @@ -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;imItems.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;imItems.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;imItems.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;imItems.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;imItems.Size(); i++) - { - mDesc->mItems[i]->Ticker(); - } -} - -//============================================================================= -// -// -// -//============================================================================= - -void DListMenu::Drawer () -{ - for(unsigned i=0;imItems.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) diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index ec92a591f8..fdca9abc81 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -49,18 +49,8 @@ #include "serializer.h" #include "resourcefiles/resourcefile.h" - -//============================================================================= -// -// This should remain native because exposing 'remove' would allow -// the creation of actual malware mods! -// -//============================================================================= - -void SavegameManager::DeleteEntry(int Selected) -{ - remove(SaveGames[Selected]->Filename.GetChars()); -} +// Save name length limit for old binary formats. +#define OLDSAVESTRINGSIZE 24 //============================================================================= // @@ -68,7 +58,7 @@ void SavegameManager::DeleteEntry(int Selected) // //============================================================================= -void SavegameManager::ClearSaveGames() +void FSavegameManager::ClearSaveGames() { for (unsigned i = 0; ibNoDelete ? index - 1 : index; + if (listindex < 0) return index; + + remove(SaveGames[index]->Filename.GetChars()); + UnloadSaveData(); + FSaveGameNode *file = SaveGames[index]; if (quickSaveSlot == SaveGames[index]) @@ -93,18 +94,33 @@ int SavegameManager::RemoveSaveSlot(int index) quickSaveSlot = nullptr; } if (!file->bNoDelete) delete file; + + if (LastSaved == listindex) LastSaved = -1; + else if (LastSaved > listindex) LastSaved--; + if (LastAccessed == listindex) LastAccessed = -1; + else if (LastAccessed > listindex) LastAccessed--; + SaveGames.Delete(index); if ((unsigned)index >= SaveGames.Size()) index--; + ExtractSaveData(index); return index; } +DEFINE_ACTION_FUNCTION(FSavegameManager, RemoveSaveSlot) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + PARAM_INT(sel); + ACTION_RETURN_INT(self->RemoveSaveSlot(sel)); +} + + //============================================================================= // // // //============================================================================= -int SavegameManager::InsertSaveNode(FSaveGameNode *node) +int FSavegameManager::InsertSaveNode(FSaveGameNode *node) { if (SaveGames.Size() == 0) { @@ -120,8 +136,7 @@ int SavegameManager::InsertSaveNode(FSaveGameNode *node) unsigned int i; for (i = 0; i < SaveGames.Size(); i++) { - if (SaveGames[i]->bOldVersion || - stricmp(node->Title, SaveGames[i]->Title) <= 0) + if (SaveGames[i]->bOldVersion || node->SaveTitle.CompareNoCase(SaveGames[i]->SaveTitle) <= 0) { break; } @@ -139,7 +154,7 @@ int SavegameManager::InsertSaveNode(FSaveGameNode *node) // //============================================================================= -void SavegameManager::ReadSaveStrings() +void FSavegameManager::ReadSaveStrings() { if (SaveGames.Size() == 0) { @@ -212,7 +227,7 @@ void SavegameManager::ReadSaveStrings() node->Filename = filepath; node->bOldVersion = oldVer; node->bMissingWads = missing; - strncpy(node->Title, title.GetChars(), SAVESTRINGSIZE); + node->SaveTitle = title; InsertSaveNode(node); delete savegame; } @@ -225,7 +240,7 @@ void SavegameManager::ReadSaveStrings() { PNGHandle *png; char sig[16]; - char title[SAVESTRINGSIZE + 1]; + char title[OLDSAVESTRINGSIZE + 1]; bool oldVer = true; bool addIt = false; bool missing = false; @@ -237,7 +252,7 @@ void SavegameManager::ReadSaveStrings() // Old savegame versions are always added to the menu so // the user can easily delete them if desired. - title[SAVESTRINGSIZE] = 0; + title[OLDSAVESTRINGSIZE] = 0; if (nullptr != (png = M_VerifyPNG(file))) @@ -246,9 +261,9 @@ void SavegameManager::ReadSaveStrings() if (ver != nullptr) { // An old version - if (!M_GetPNGText(png, "Title", title, SAVESTRINGSIZE)) + if (!M_GetPNGText(png, "Title", title, OLDSAVESTRINGSIZE)) { - strncpy(title, I_FindName(&c_file), SAVESTRINGSIZE); + strncpy(title, I_FindName(&c_file), OLDSAVESTRINGSIZE); } addIt = true; delete[] ver; @@ -263,7 +278,7 @@ void SavegameManager::ReadSaveStrings() if (strncmp(sig, "ZDOOMSAVE", 9) == 0) { - if (fread(title, 1, SAVESTRINGSIZE, file) == SAVESTRINGSIZE) + if (fread(title, 1, OLDSAVESTRINGSIZE, file) == OLDSAVESTRINGSIZE) { addIt = true; } @@ -271,7 +286,7 @@ void SavegameManager::ReadSaveStrings() else { memcpy(title, sig, 16); - if (fread(title + 16, 1, SAVESTRINGSIZE - 16, file) == SAVESTRINGSIZE - 16 && + if (fread(title + 16, 1, OLDSAVESTRINGSIZE - 16, file) == OLDSAVESTRINGSIZE - 16 && fread(sig, 1, 16, file) == 16 && strncmp(sig, "ZDOOMSAVE", 9) == 0) { @@ -287,7 +302,7 @@ void SavegameManager::ReadSaveStrings() node->Filename = filepath; node->bOldVersion = true; node->bMissingWads = false; - memcpy(node->Title, title, SAVESTRINGSIZE); + node->SaveTitle = title; InsertSaveNode(node); } fclose(file); @@ -299,17 +314,25 @@ void SavegameManager::ReadSaveStrings() } } +DEFINE_ACTION_FUNCTION(FSavegameManager, ReadSaveStrings) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + self->ReadSaveStrings(); + return 0; +} + + //============================================================================= // // // //============================================================================= -void SavegameManager::NotifyNewSave(const char *file, const char *title, bool okForQuicksave) +void FSavegameManager::NotifyNewSave(const FString &file, const FString &title, bool okForQuicksave) { FSaveGameNode *node; - if (file == nullptr) + if (file.IsEmpty()) return; ReadSaveStrings(); @@ -324,7 +347,7 @@ void SavegameManager::NotifyNewSave(const char *file, const char *title, bool ok if (node->Filename.CompareNoCase(file) == 0) #endif { - strcpy(node->Title, title); + node->SaveTitle = title; node->bOldVersion = false; node->bMissingWads = false; if (okForQuicksave) @@ -337,7 +360,7 @@ void SavegameManager::NotifyNewSave(const char *file, const char *title, bool ok } node = new FSaveGameNode; - strcpy(node->Title, title); + node->SaveTitle = title; node->Filename = file; node->bOldVersion = false; node->bMissingWads = false; @@ -356,7 +379,7 @@ void SavegameManager::NotifyNewSave(const char *file, const char *title, bool ok // //============================================================================= -void SavegameManager::LoadSavegame(int Selected) +void FSavegameManager::LoadSavegame(int Selected) { G_LoadGame(SaveGames[Selected]->Filename.GetChars(), true); if (quickSaveSlot == (FSaveGameNode*)1) @@ -368,13 +391,21 @@ void SavegameManager::LoadSavegame(int Selected) LastAccessed = Selected; } +DEFINE_ACTION_FUNCTION(FSavegameManager, LoadSavegame) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + PARAM_INT(sel); + self->LoadSavegame(sel); + return 0; +} + //============================================================================= // // // //============================================================================= -void SavegameManager::DoSave(int Selected, const char *savegamestring) +void FSavegameManager::DoSave(int Selected, const char *savegamestring) { if (Selected != 0) { @@ -404,17 +435,38 @@ void SavegameManager::DoSave(int Selected, const char *savegamestring) V_SetBorderNeedRefresh(); } +DEFINE_ACTION_FUNCTION(FSavegameManager, DoSave) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + PARAM_INT(sel); + PARAM_STRING(name); + self->DoSave(sel, name); + return 0; +} + //============================================================================= // // // //============================================================================= -void SavegameManager::ExtractSaveData(int index) +unsigned FSavegameManager::ExtractSaveData(int index) { FResourceFile *resf; FSaveGameNode *node; + if (index == -1) + { + if (SaveGames.Size() > 0 && SaveGames[0]->bNoDelete) + { + index = LastSaved + 1; + } + else + { + index = LastAccessed < 0? 0 : LastAccessed; + } + } + UnloadSaveData(); if ((unsigned)index < SaveGames.Size() && @@ -427,7 +479,7 @@ void SavegameManager::ExtractSaveData(int index) if (info == nullptr) { // this should not happen because the file has already been verified. - return; + return index; } void *data = info->CacheLump(); FSerializer arc; @@ -477,6 +529,14 @@ void SavegameManager::ExtractSaveData(int index) } delete resf; } + return index; +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, ExtractSaveData) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + PARAM_INT(sel); + ACTION_RETURN_INT(self->ExtractSaveData(sel)); } //============================================================================= @@ -485,7 +545,7 @@ void SavegameManager::ExtractSaveData(int index) // //============================================================================= -void SavegameManager::UnloadSaveData() +void FSavegameManager::UnloadSaveData() { if (SavePic != nullptr) { @@ -506,13 +566,20 @@ void SavegameManager::UnloadSaveData() SavePicData.Clear(); } +DEFINE_ACTION_FUNCTION(FSavegameManager, UnloadSaveData) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + self->UnloadSaveData(); + return 0; +} + //============================================================================= // // // //============================================================================= -void SavegameManager::ClearSaveStuff() +void FSavegameManager::ClearSaveStuff() { UnloadSaveData(); if (quickSaveSlot == (FSaveGameNode*)1) @@ -521,18 +588,11 @@ void SavegameManager::ClearSaveStuff() } } - -//============================================================================= -// -// -// -//============================================================================= - -bool SavegameManager::DrawSavePic(int x, int y, int w, int h) +DEFINE_ACTION_FUNCTION(FSavegameManager, ClearSaveStuff) { - if (SavePic == nullptr) return false; - screen->DrawTexture(SavePic, x, y, DTA_DestWidth, w, DTA_DestHeight, h, DTA_Masked, false, TAG_DONE); - return true; + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + self->ClearSaveStuff(); + return 0; } //============================================================================= @@ -541,7 +601,68 @@ bool SavegameManager::DrawSavePic(int x, int y, int w, int h) // //============================================================================= -void SavegameManager::SetFileInfo(int Selected) +bool FSavegameManager::DrawSavePic(int x, int y, int w, int h) +{ + if (SavePic == nullptr) return false; + screen->DrawTexture(SavePic, x, y, DTA_DestWidth, w, DTA_DestHeight, h, DTA_Masked, false, TAG_DONE); + return true; +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, DrawSavePic) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + PARAM_INT(x); + PARAM_INT(y); + PARAM_INT(w); + PARAM_INT(h); + ACTION_RETURN_BOOL(self->DrawSavePic(x, y, w, h)); +} + +//============================================================================= +// +// +// +//============================================================================= + +void FSavegameManager::DrawSaveComment(FFont *font, int cr, int x, int y, int scalefactor) +{ + int sx = CleanXfac; + int sy = CleanYfac; + + CleanXfac = CleanYfac = scalefactor; + + // I'm not sure why SaveComment would go nullptr in this loop, but I got + // a crash report where it was nullptr when i reached 1, so now I check + // for that. + for (int i = 0; SaveComment != nullptr && SaveComment[i].Width >= 0 && i < 6; ++i) + { + screen->DrawText(font, cr, x, y + font->GetHeight() * i * scalefactor, SaveComment[i].Text, DTA_CleanNoMove, true, TAG_DONE); + } + + CleanXfac = sx; + CleanYfac = sy; +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, DrawSaveComment) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + PARAM_POINTER(fnt, FFont); + PARAM_INT(cr); + PARAM_INT(x); + PARAM_INT(y); + PARAM_INT(fac); + self->DrawSaveComment(fnt, cr, x, y, fac); + return 0; +} + + +//============================================================================= +// +// +// +//============================================================================= + +void FSavegameManager::SetFileInfo(int Selected) { if (!SaveGames[Selected]->Filename.IsEmpty()) { @@ -556,405 +677,12 @@ void SavegameManager::SetFileInfo(int Selected) } } -SavegameManager savegameManager; - - - - -class DLoadSaveMenu : public DListMenu +DEFINE_ACTION_FUNCTION(FSavegameManager, SetFileInfo) { - DECLARE_CLASS(DLoadSaveMenu, DListMenu) - -protected: - - SavegameManager *manager; - - int Selected; - int TopItem; - - - int savepicLeft; - int savepicTop; - int savepicWidth; - int savepicHeight; - - int rowHeight; - int listboxLeft; - int listboxTop; - int listboxWidth; - - int listboxRows; - int listboxHeight; - int listboxRight; - int listboxBottom; - - int commentLeft; - int commentTop; - int commentWidth; - int commentHeight; - int commentRight; - int commentBottom; - - // this needs to be kept in memory so that the texture can access it when it needs to. - bool mEntering; - char savegamestring[SAVESTRINGSIZE]; - DTextEnterMenu *mInput = nullptr; - - DLoadSaveMenu(DMenu *parent = nullptr, DListMenuDescriptor *desc = nullptr); - void OnDestroy() override; - - void Drawer (); - bool MenuEvent (int mkey, bool fromcontroller); - bool MouseEvent(int type, int x, int y); - bool Responder(event_t *ev); -}; - -IMPLEMENT_CLASS(DLoadSaveMenu, false, false) - - - -//============================================================================= -// -// End of static savegame maintenance code -// -//============================================================================= - -DLoadSaveMenu::DLoadSaveMenu(DMenu *parent, DListMenuDescriptor *desc) -: DListMenu(parent, desc) -{ - manager = &savegameManager; - manager->ReadSaveStrings(); - - savepicLeft = 10; - savepicTop = 54*CleanYfac; - savepicWidth = 216*screen->GetWidth()/640; - savepicHeight = 135*screen->GetHeight()/400; - manager->WindowSize = savepicWidth; - - rowHeight = (SmallFont->GetHeight() + 1) * CleanYfac; - listboxLeft = savepicLeft + savepicWidth + 14; - listboxTop = savepicTop; - listboxWidth = screen->GetWidth() - listboxLeft - 10; - int listboxHeight1 = screen->GetHeight() - listboxTop - 10; - listboxRows = (listboxHeight1 - 1) / rowHeight; - listboxHeight = listboxRows * rowHeight + 1; - listboxRight = listboxLeft + listboxWidth; - listboxBottom = listboxTop + listboxHeight; - - commentLeft = savepicLeft; - commentTop = savepicTop + savepicHeight + 16; - commentWidth = savepicWidth; - commentHeight = (51+(screen->GetHeight()>200?10:0))*CleanYfac; - commentRight = commentLeft + commentWidth; - commentBottom = commentTop + commentHeight; -} - -void DLoadSaveMenu::OnDestroy() -{ - manager->ClearSaveStuff (); - Super::OnDestroy(); -} - -//============================================================================= -// -// -// -//============================================================================= - -void DLoadSaveMenu::Drawer () -{ - Super::Drawer(); - - FSaveGameNode *node; - int i; - unsigned j; - bool didSeeSelected = false; - - // Draw picture area - if (gameaction == ga_loadgame || gameaction == ga_loadgamehidecon || gameaction == ga_savegame) - { - return; - } - - V_DrawFrame (savepicLeft, savepicTop, savepicWidth, savepicHeight); - if (!manager->DrawSavePic(savepicLeft, savepicTop, savepicWidth, savepicHeight)) - { - screen->Clear (savepicLeft, savepicTop, - savepicLeft+savepicWidth, savepicTop+savepicHeight, 0, 0); - - if (manager->SaveGames.Size() > 0) - { - const char *text = - (Selected == -1 || !manager->SaveGames[Selected]->bOldVersion) - ? GStrings("MNU_NOPICTURE") : GStrings("MNU_DIFFVERSION"); - const int textlen = SmallFont->StringWidth (text)*CleanXfac; - - screen->DrawText (SmallFont, CR_GOLD, savepicLeft+(savepicWidth-textlen)/2, - savepicTop+(savepicHeight-rowHeight)/2, text, - DTA_CleanNoMove, true, TAG_DONE); - } - } - - // Draw comment area - V_DrawFrame (commentLeft, commentTop, commentWidth, commentHeight); - screen->Clear (commentLeft, commentTop, commentRight, commentBottom, 0, 0); - if (manager->SaveComment != nullptr) - { - // I'm not sure why SaveComment would go nullptr in this loop, but I got - // a crash report where it was nullptr when i reached 1, so now I check - // for that. - for (i = 0; manager->SaveComment != nullptr && manager->SaveComment[i].Width >= 0 && i < 6; ++i) - { - screen->DrawText (SmallFont, CR_GOLD, commentLeft, commentTop - + SmallFont->GetHeight()*i*CleanYfac, manager->SaveComment[i].Text, - DTA_CleanNoMove, true, TAG_DONE); - } - } - - // Draw file area - V_DrawFrame (listboxLeft, listboxTop, listboxWidth, listboxHeight); - screen->Clear (listboxLeft, listboxTop, listboxRight, listboxBottom, 0, 0); - - if (manager->SaveGames.Size() == 0) - { - const char * text = GStrings("MNU_NOFILES"); - const int textlen = SmallFont->StringWidth (text)*CleanXfac; - - screen->DrawText (SmallFont, CR_GOLD, listboxLeft+(listboxWidth-textlen)/2, - listboxTop+(listboxHeight-rowHeight)/2, text, - DTA_CleanNoMove, true, TAG_DONE); - return; - } - - for (i = 0, j = TopItem; i < listboxRows && j < manager->SaveGames.Size(); i++,j++) - { - int color; - node = manager->SaveGames[j]; - if (node->bOldVersion) - { - color = CR_BLUE; - } - else if (node->bMissingWads) - { - color = CR_ORANGE; - } - else if ((int)j == Selected) - { - color = CR_WHITE; - } - else - { - color = CR_TAN; - } - - if ((int)j == Selected) - { - screen->Clear (listboxLeft, listboxTop+rowHeight*i, - listboxRight, listboxTop+rowHeight*(i+1), -1, - mEntering ? MAKEARGB(255,255,0,0) : MAKEARGB(255,0,0,255)); - didSeeSelected = true; - if (!mEntering) - { - screen->DrawText (SmallFont, color, - listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, node->Title, - DTA_CleanNoMove, true, TAG_DONE); - } - else - { - FString s = mInput->GetText() + SmallFont->GetCursor(); - screen->DrawText (SmallFont, CR_WHITE, - listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, s, - DTA_CleanNoMove, true, TAG_DONE); - } - } - else - { - screen->DrawText (SmallFont, color, - listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, node->Title, - DTA_CleanNoMove, true, TAG_DONE); - } - } -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DLoadSaveMenu::MenuEvent (int mkey, bool fromcontroller) -{ - switch (mkey) - { - case MKEY_Up: - if (manager->SaveGames.Size() > 1) - { - if (Selected == -1) Selected = TopItem; - else - { - if (--Selected < 0) Selected = manager->SaveGames.Size()-1; - if (Selected < TopItem) TopItem = Selected; - else if (Selected >= TopItem + listboxRows) TopItem = MAX(0, Selected - listboxRows + 1); - } - manager->UnloadSaveData (); - manager->ExtractSaveData (Selected); - } - return true; - - case MKEY_Down: - if (manager->SaveGames.Size() > 1) - { - if (Selected == -1) Selected = TopItem; - else - { - if (unsigned(++Selected) >= manager->SaveGames.Size()) Selected = 0; - if (Selected < TopItem) TopItem = Selected; - else if (Selected >= TopItem + listboxRows) TopItem = MAX(0, Selected - listboxRows + 1); - } - manager->UnloadSaveData (); - manager->ExtractSaveData (Selected); - } - return true; - - case MKEY_PageDown: - if (manager->SaveGames.Size() > 1) - { - if (TopItem >= (int)manager->SaveGames.Size() - listboxRows) - { - TopItem = 0; - if (Selected != -1) Selected = 0; - } - else - { - TopItem = MIN(TopItem + listboxRows, manager->SaveGames.Size() - listboxRows); - if (TopItem > Selected && Selected != -1) Selected = TopItem; - } - manager->UnloadSaveData (); - manager->ExtractSaveData (Selected); - } - return true; - - case MKEY_PageUp: - if (manager->SaveGames.Size() > 1) - { - if (TopItem == 0) - { - TopItem = manager->SaveGames.Size() - listboxRows; - if (Selected != -1) Selected = TopItem; - } - else - { - TopItem = MAX(TopItem - listboxRows, 0); - if (Selected >= TopItem + listboxRows) Selected = TopItem; - } - manager->UnloadSaveData (); - manager->ExtractSaveData (Selected); - } - return true; - - case MKEY_Enter: - return false; // This event will be handled by the subclasses - - case MKEY_MBYes: - { - if ((unsigned)Selected < manager->SaveGames.Size()) - { - int listindex = manager->SaveGames[0]->bNoDelete? Selected-1 : Selected; - manager->DeleteEntry(Selected); - manager->UnloadSaveData (); - Selected = manager->RemoveSaveSlot (Selected); - manager->ExtractSaveData (Selected); - - if (manager->LastSaved == listindex) manager->LastSaved = -1; - else if (manager->LastSaved > listindex) manager->LastSaved--; - if (manager->LastAccessed == listindex) manager->LastAccessed = -1; - else if (manager->LastAccessed > listindex) manager->LastAccessed--; - } - return true; - } - - default: - return Super::MenuEvent(mkey, fromcontroller); - } -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DLoadSaveMenu::MouseEvent(int type, int x, int y) -{ - if (x >= listboxLeft && x < listboxLeft + listboxWidth && - y >= listboxTop && y < listboxTop + listboxHeight) - { - int lineno = (y - listboxTop) / rowHeight; - - if (TopItem + lineno < (int)manager->SaveGames.Size()) - { - Selected = TopItem + lineno; - manager->UnloadSaveData (); - manager->ExtractSaveData (Selected); - if (type == MOUSE_Release) - { - if (MenuEvent(MKEY_Enter, true)) - { - return true; - } - } - } - else Selected = -1; - } - else Selected = -1; - - return Super::MouseEvent(type, x, y); -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DLoadSaveMenu::Responder (event_t *ev) -{ - if (ev->type == EV_GUI_Event) - { - if (ev->subtype == EV_GUI_KeyDown) - { - if ((unsigned)Selected < manager->SaveGames.Size()) - { - switch (ev->data1) - { - case GK_F1: - manager->SetFileInfo(Selected); - return true; - - case GK_DEL: - case '\b': - { - FString EndString; - EndString.Format("%s" TEXTCOLOR_WHITE "%s" TEXTCOLOR_NORMAL "?\n\n%s", - GStrings("MNU_DELETESG"), manager->SaveGames[Selected]->Title, GStrings("PRESSYN")); - M_StartMessage (EndString, 0); - } - return true; - } - } - } - else if (ev->subtype == EV_GUI_WheelUp) - { - if (TopItem > 0) TopItem--; - return true; - } - else if (ev->subtype == EV_GUI_WheelDown) - { - if (TopItem < (int)manager->SaveGames.Size() - listboxRows) TopItem++; - return true; - } - } - return Super::Responder(ev); + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + PARAM_INT(i); + self->SetFileInfo(i); + return 0; } @@ -964,23 +692,16 @@ bool DLoadSaveMenu::Responder (event_t *ev) // //============================================================================= -class DSaveMenu : public DLoadSaveMenu +unsigned FSavegameManager::SavegameCount() { - DECLARE_CLASS(DSaveMenu, DLoadSaveMenu) - - FSaveGameNode NewSaveNode; - -public: - - DSaveMenu(DMenu *parent = nullptr, DListMenuDescriptor *desc = nullptr); - void OnDestroy() override; - bool Responder (event_t *ev); - bool MenuEvent (int mkey, bool fromcontroller); - -}; - -IMPLEMENT_CLASS(DSaveMenu, false, false) + return SaveGames.Size(); +} +DEFINE_ACTION_FUNCTION(FSavegameManager, SavegameCount) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + ACTION_RETURN_INT(self->SavegameCount()); +} //============================================================================= // @@ -988,22 +709,36 @@ IMPLEMENT_CLASS(DSaveMenu, false, false) // //============================================================================= -DSaveMenu::DSaveMenu(DMenu *parent, DListMenuDescriptor *desc) -: DLoadSaveMenu(parent, desc) +FSaveGameNode *FSavegameManager::GetSavegame(int i) { - strcpy (NewSaveNode.Title, GStrings["NEWSAVE"]); + return SaveGames[i]; +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, GetSavegame) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + PARAM_INT(i); + ACTION_RETURN_POINTER(self->GetSavegame(i)); +} + +//============================================================================= +// +// +// +//============================================================================= + +void FSavegameManager::InsertNewSaveNode() +{ + NewSaveNode.SaveTitle = GStrings["NEWSAVE"]; NewSaveNode.bNoDelete = true; - manager->SaveGames.Insert(0, &NewSaveNode); - TopItem = 0; - if (manager->LastSaved == -1) - { - Selected = 0; - } - else - { - Selected = manager->LastSaved + 1; - } - manager->ExtractSaveData (Selected); + SaveGames.Insert(0, &NewSaveNode); +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, InsertNewSaveNode) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + self->InsertNewSaveNode(); + return 0; } //============================================================================= @@ -1012,152 +747,39 @@ DSaveMenu::DSaveMenu(DMenu *parent, DListMenuDescriptor *desc) // //============================================================================= -void DSaveMenu::OnDestroy() +bool FSavegameManager::RemoveNewSaveNode() { - if (manager->SaveGames[0] == &NewSaveNode) + if (SaveGames[0] == &NewSaveNode) { - manager->SaveGames.Delete(0); - if (Selected == 0) Selected = -1; - else Selected--; - } - Super::OnDestroy(); -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DSaveMenu::MenuEvent (int mkey, bool fromcontroller) -{ - if (Super::MenuEvent(mkey, fromcontroller)) - { - return true; - } - if (Selected == -1) - { - return false; - } - - if (mkey == MKEY_Enter) - { - if (Selected != 0) - { - strcpy (savegamestring, manager->SaveGames[Selected]->Title); - } - else - { - savegamestring[0] = 0; - } - mInput = new DTextEnterMenu(this, savegamestring, SAVESTRINGSIZE, 1, fromcontroller); - M_ActivateMenu(mInput); - mEntering = true; - } - else if (mkey == MKEY_Input) - { - mEntering = false; - manager->DoSave(Selected, mInput->GetText()); - mInput = nullptr; - } - else if (mkey == MKEY_Abort) - { - mEntering = false; - mInput = nullptr; - } - return false; -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DSaveMenu::Responder (event_t *ev) -{ - if (ev->subtype == EV_GUI_KeyDown) - { - if (Selected != -1) - { - switch (ev->data1) - { - case GK_DEL: - case '\b': - // cannot delete 'new save game' item - if (Selected == 0) return true; - break; - - case 'N': - Selected = TopItem = 0; - manager->UnloadSaveData (); - return true; - } - } - } - return Super::Responder(ev); -} - -//============================================================================= -// -// -// -//============================================================================= - -class DLoadMenu : public DLoadSaveMenu -{ - DECLARE_CLASS(DLoadMenu, DLoadSaveMenu) - -public: - - DLoadMenu(DMenu *parent = nullptr, DListMenuDescriptor *desc = nullptr); - - bool MenuEvent (int mkey, bool fromcontroller); -}; - -IMPLEMENT_CLASS(DLoadMenu, false, false) - - -//============================================================================= -// -// -// -//============================================================================= - -DLoadMenu::DLoadMenu(DMenu *parent, DListMenuDescriptor *desc) -: DLoadSaveMenu(parent, desc) -{ - TopItem = 0; - if (manager->LastAccessed != -1) - { - Selected = manager->LastAccessed; - } - manager->ExtractSaveData (Selected); - -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DLoadMenu::MenuEvent (int mkey, bool fromcontroller) -{ - if (Super::MenuEvent(mkey, fromcontroller)) - { - return true; - } - if (Selected == -1 || manager->SaveGames.Size() == 0) - { - return false; - } - - if (mkey == MKEY_Enter) - { - manager->LoadSavegame(Selected); + SaveGames.Delete(0); return true; } return false; } +DEFINE_ACTION_FUNCTION(FSavegameManager, RemoveNewSaveNode) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + ACTION_RETURN_INT(self->RemoveNewSaveNode()); +} + + +FSavegameManager savegameManager; + +DEFINE_ACTION_FUNCTION(FSavegameManager, GetManager) +{ + PARAM_PROLOGUE; + ACTION_RETURN_POINTER(&savegameManager); +} + + + +DEFINE_FIELD(FSaveGameNode, SaveTitle); +DEFINE_FIELD(FSaveGameNode, Filename); +DEFINE_FIELD(FSaveGameNode, bOldVersion); +DEFINE_FIELD(FSaveGameNode, bMissingWads); +DEFINE_FIELD(FSaveGameNode, bNoDelete); + +DEFINE_FIELD(FSavegameManager, WindowSize); +DEFINE_FIELD(FSavegameManager, quickSaveSlot); + diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index 676057321e..1c8e0b39bb 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -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(*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) diff --git a/src/menu/menu.h b/src/menu/menu.h index ffc15e8808..0b6b55ea26 100644 --- a/src/menu/menu.h +++ b/src/menu/menu.h @@ -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 SaveGames; + FSaveGameNode NewSaveNode; int LastSaved = -1; int LastAccessed = -1; - int WindowSize = 0; - FSaveGameNode *quickSaveSlot = nullptr; - FileReader *currentSavePic = nullptr; TArray 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 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 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 diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index 057f544c5e..239366dcfe 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -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(cls->Symbols.FindSymbol("Init", false)); + auto func = dyn_cast(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(cls->Symbols.FindSymbol("Init", false)); + auto func = dyn_cast(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(*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 MenuSkills; + TArray 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(*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; imItems.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(*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; -} diff --git a/src/menu/menuinput.cpp b/src/menu/menuinput.cpp deleted file mode 100644 index 9a0b717b51..0000000000 --- a/src/menu/menuinput.cpp +++ /dev/null @@ -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()); -} diff --git a/src/menu/messagebox.cpp b/src/menu/messagebox.cpp index e15940acbb..a1a0fda769 100644 --- a/src/menu/messagebox.cpp +++ b/src/menu/messagebox.cpp @@ -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(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(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; -} \ No newline at end of file +} diff --git a/src/menu/optionmenu.cpp b/src/menu/optionmenu.cpp index acbb3c58e9..87f0adcf12 100644 --- a/src/menu/optionmenu.cpp +++ b/src/menu/optionmenu.cpp @@ -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;iGetAction(NULL); + FName nm = mItems[i]->mAction; if (nm == name) return mItems[i]; } return NULL; diff --git a/src/menu/playermenu.cpp b/src/menu/playermenu.cpp index 3a13760529..c730f2d813 100644 --- a/src/menu/playermenu.cpp +++ b/src/menu/playermenu.cpp @@ -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 PlayerColorSets; - TArray 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;iSetString(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;iType, PlayerColorSets[i]); - li->SetString(i+1, colorset->Name); - } - int mycolorset = players[consoleplayer].userinfo.GetColorSet(); - if (mycolorset != -1) - { - for(unsigned i=0;iSetValue(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; } diff --git a/src/menu/readthis.cpp b/src/menu/readthis.cpp deleted file mode 100644 index 06d4620da1..0000000000 --- a/src/menu/readthis.cpp +++ /dev/null @@ -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; -} - diff --git a/src/namedef.h b/src/namedef.h index ebd9f3e8ee..91fd28626e 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -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) diff --git a/src/nodebuild.cpp b/src/nodebuild.cpp index 19dc16b615..30c44b69c0 100644 --- a/src/nodebuild.cpp +++ b/src/nodebuild.cpp @@ -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 -#include -#include -#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 diff --git a/src/nodebuild.h b/src/nodebuild.h index 9599bc0d2e..6d730644e2 100644 --- a/src/nodebuild.h +++ b/src/nodebuild.h @@ -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 -} diff --git a/src/nodebuild_classify_nosse2.cpp b/src/nodebuild_classify_nosse2.cpp index 2d2c8edea4..44f7e709f4 100644 --- a/src/nodebuild_classify_nosse2.cpp +++ b/src/nodebuild_classify_nosse2.cpp @@ -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); diff --git a/src/nodebuild_classify_sse2.cpp b/src/nodebuild_classify_sse2.cpp deleted file mode 100644 index 52429ecb81..0000000000 --- a/src/nodebuild_classify_sse2.cpp +++ /dev/null @@ -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 diff --git a/src/oplsynth/dosbox/opl.cpp b/src/oplsynth/dosbox/opl.cpp index 0130dd7978..64b77ce8c9 100644 --- a/src/oplsynth/dosbox/opl.cpp +++ b/src/oplsynth/dosbox/opl.cpp @@ -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; diff --git a/src/oplsynth/nukedopl3.h b/src/oplsynth/nukedopl3.h index 849646f5f3..5b1392542d 100644 --- a/src/oplsynth/nukedopl3.h +++ b/src/oplsynth/nukedopl3.h @@ -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; diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 00b1d7be85..027b614ea1 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -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::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(activator)->MaxHealth; + return static_cast(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 (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(actor)->MaxHealth; + return static_cast(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(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 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, ¶ms[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, ¶ms[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, ¶ms[0], params.Size(), &ret, 1); + retval = DoubleToACS(d); + } + else if (rettype == TypeString) + { + FString d; + VMReturn ret(&d); + GlobalVMStack.Call(func, ¶ms[0], params.Size(), &ret, 1); + retval = GlobalACSStrings.AddString(d); + } + else + { + // All other return values can not be handled so ignore them. + GlobalVMStack.Call(func, ¶ms[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(argcount, code->ArgCount); ++i) { Localvars[i] = args[i]; diff --git a/src/p_acs.h b/src/p_acs.h index 8265e5b37a..09d2ca6868 100644 --- a/src/p_acs.h +++ b/src/p_acs.h @@ -60,14 +60,14 @@ struct InitIntToZero v = 0; } }; -typedef TMap, InitIntToZero> FWorldGlobalArray; +typedef TMap, 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 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, diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 40629bfcb2..c32372b4e3 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -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()tracer == nullptr) && (pr_seekermissile()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 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 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; +} \ No newline at end of file diff --git a/src/p_buildmap.cpp b/src/p_buildmap.cpp index 3d1e77d6da..1be648bc2f 100644 --- a/src/p_buildmap.cpp +++ b/src/p_buildmap.cpp @@ -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, }; diff --git a/src/p_ceiling.cpp b/src/p_ceiling.cpp index 7bf1f9099e..b978fd0d32 100644 --- a/src/p_ceiling.cpp +++ b/src/p_ceiling.cpp @@ -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; +} diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index 54be79e3ca..0ac80171ca 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -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(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(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(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 mResponseLines; - TArray 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 (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 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(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); diff --git a/src/p_conversation.h b/src/p_conversation.h index 8771d95aef..7ee24093b6 100644 --- a/src/p_conversation.h +++ b/src/p_conversation.h @@ -20,40 +20,41 @@ struct FStrifeDialogueItemCheck struct FStrifeDialogueNode { ~FStrifeDialogueNode (); - PClassActor *DropType; + PClassActor *DropType = nullptr; TArray 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 ItemCheck; TArray ItemCheckRequire; TArray 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 StrifeDialogues; diff --git a/src/p_doors.cpp b/src/p_doors.cpp index 3101be1c77..05c93a8989 100644 --- a/src/p_doors.cpp +++ b/src/p_doors.cpp @@ -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; } } diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index 2fb040a933..dde4fce29c 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -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) diff --git a/src/p_floor.cpp b/src/p_floor.cpp index 2fd205a9d3..8f7d935ee5 100644 --- a/src/p_floor.cpp +++ b/src/p_floor.cpp @@ -561,6 +561,21 @@ bool EV_FloorCrushStop (int tag) return true; } +// same as above but stops any floor mover that was active on the given sector. +bool EV_StopFloor(int tag) +{ + FSectorTagIterator it(tag); + while (int sec = it.Next()) + { + if (level.sectors[sec].floordata) + { + SN_StopSequence(&level.sectors[sec], CHAN_FLOOR); + level.sectors[sec].floordata->Destroy(); + level.sectors[sec].floordata = nullptr; + } + } + return true; +} //========================================================================== // // BUILD A STAIRCASE! @@ -625,6 +640,7 @@ bool EV_BuildStairs (int tag, DFloor::EStair type, line_t *line, floor->m_Delay = delay; floor->m_PauseTime = 0; floor->m_StepTime = floor->m_PerStepTime = persteptime; + floor->m_Instant = false; floor->m_Crush = (usespecials & DFloor::stairCrush) ? 10 : -1; //jff 2/27/98 fix uninitialized crush field floor->m_Hexencrush = false; @@ -740,6 +756,7 @@ bool EV_BuildStairs (int tag, DFloor::EStair type, line_t *line, //jff 2/27/98 fix uninitialized crush field floor->m_Crush = (!(usespecials & DFloor::stairUseSpecials) && speed == 4) ? 10 : -1; //jff 2/27/98 fix uninitialized crush field floor->m_Hexencrush = false; + floor->m_Instant = false; floor->m_ResetCount = reset; // [RH] Tics until reset (0 if never) floor->m_OrgDist = sec->floorplane.fD(); // [RH] Height to reset to } @@ -802,6 +819,7 @@ bool EV_DoDonut (int tag, line_t *line, double pillarspeed, double slimespeed) floor->m_Direction = 1; floor->m_Sector = s2; floor->m_Speed = slimespeed; + floor->m_Instant = false; floor->m_Texture = s3->GetTexture(sector_t::floor); floor->m_NewSpecial.Clear(); height = s3->FindHighestFloorPoint (&spot); @@ -816,6 +834,7 @@ bool EV_DoDonut (int tag, line_t *line, double pillarspeed, double slimespeed) floor->m_Direction = -1; floor->m_Sector = s1; floor->m_Speed = pillarspeed; + floor->m_Instant = false; height = s3->FindHighestFloorPoint (&spot); floor->m_FloorDestDist = s1->floorplane.PointToDist (spot, height); floor->StartFloorSound (); diff --git a/src/p_glnodes.cpp b/src/p_glnodes.cpp index 43ed096e0c..d30cb812ee 100644 --- a/src/p_glnodes.cpp +++ b/src/p_glnodes.cpp @@ -82,13 +82,13 @@ static void CreateCachedNodes(MapData *map); // fixed 32 bit gl_vert format v2.0+ (glBsp 1.91) struct mapglvertex_t { - SDWORD x,y; + int32_t x,y; }; struct gl3_mapsubsector_t { - SDWORD numsegs; - SDWORD firstseg; // Index of first one; segs are stored sequentially. + int32_t numsegs; + int32_t firstseg; // Index of first one; segs are stored sequentially. }; struct glseg_t @@ -102,11 +102,11 @@ struct glseg_t struct glseg3_t { - SDWORD v1; - SDWORD v2; + int32_t v1; + int32_t v2; WORD linedef; WORD side; - SDWORD partner; + int32_t partner; }; struct gl5_mapnode_t @@ -621,7 +621,7 @@ static bool LoadNodes (FileReader * lump) no->dy = LittleShort(mn->dy)<children[j]); + int32_t child = LittleLong(mn->children[j]); if (child & GL5_NF_SUBSECTOR) { child &= ~GL5_NF_SUBSECTOR; diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 28df6e761f..9629ccfe84 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -62,7 +62,6 @@ #include "g_levellocals.h" #include "events.h" -static FRandom pr_obituary ("Obituary"); static FRandom pr_botrespawn ("BotRespawn"); static FRandom pr_killmobj ("ActorDie"); FRandom pr_damagemobj ("ActorTakeDamage"); @@ -186,14 +185,11 @@ void ClientObituary (AActor *self, AActor *inflictor, AActor *attacker, int dmgf const char *message; const char *messagename; char gendermessage[1024]; - int gender; // No obituaries for non-players, voodoo dolls or when not wanted if (self->player == NULL || self->player->mo != self || !show_obituaries) return; - gender = self->player->userinfo.GetGender(); - // Treat voodoo dolls as unknown deaths if (inflictor && inflictor->player && inflictor->player->mo != inflictor) MeansOfDeath = NAME_None; @@ -216,89 +212,49 @@ void ClientObituary (AActor *self, AActor *inflictor, AActor *attacker, int dmgf } } - switch (mod) + FString obit = DamageTypeDefinition::GetObituary(mod); + if (attacker == nullptr) messagename = obit; + else { - case NAME_Suicide: messagename = "OB_SUICIDE"; break; - case NAME_Falling: messagename = "OB_FALLING"; break; - case NAME_Crush: messagename = "OB_CRUSH"; break; - case NAME_Exit: messagename = "OB_EXIT"; break; - case NAME_Drowning: messagename = "OB_WATER"; break; - case NAME_Slime: messagename = "OB_SLIME"; break; - case NAME_Fire: if (attacker == NULL) messagename = "OB_LAVA"; break; + switch (mod) + { + case NAME_Suicide: message = "$OB_SUICIDE"; break; + case NAME_Falling: message = "$OB_FALLING"; break; + case NAME_Crush: message = "$OB_CRUSH"; break; + case NAME_Exit: message = "$OB_EXIT"; break; + case NAME_Drowning: message = "$OB_WATER"; break; + case NAME_Slime: message = "$OB_SLIME"; break; + case NAME_Fire: messagename = "$OB_LAVA"; break; + } } // Check for being killed by a voodoo doll. if (inflictor && inflictor->player && inflictor->player->mo != inflictor) { - messagename = "OB_VOODOO"; + messagename = "$OB_VOODOO"; } - if (messagename != NULL) - message = GStrings(messagename); - if (attacker != NULL && message == NULL) { if (attacker == self) { - message = GStrings("OB_KILLEDSELF"); + message = "$OB_KILLEDSELF"; } - else if (attacker->player == NULL) + else { - if (mod == NAME_Telefrag) + IFVIRTUALPTR(attacker, AActor, GetObituary) { - message = GStrings("OB_MONTELEFRAG"); - } - else if (mod == NAME_Melee && attacker->GetClass()->HitObituary.IsNotEmpty()) - { - message = attacker->GetClass()->HitObituary; - } - else if (attacker->GetClass()->Obituary.IsNotEmpty()) - { - message = attacker->GetClass()->Obituary; + VMValue params[] = { attacker, self, inflictor, mod.GetIndex(), !!(dmgflags & DMG_PLAYERATTACK) }; + FString ret; + VMReturn rett(&ret); + GlobalVMStack.Call(func, params, countof(params), &rett, 1); + if (ret.IsNotEmpty()) message = ret; } } } - - if (message == NULL && attacker != NULL && attacker->player != NULL) - { - if (self->player != attacker->player && self->IsTeammate(attacker)) - { - self = attacker; - gender = self->player->userinfo.GetGender(); - mysnprintf (gendermessage, countof(gendermessage), "OB_FRIENDLY%c", '1' + (pr_obituary() & 3)); - message = GStrings(gendermessage); - } - else - { - if (mod == NAME_Telefrag) message = GStrings("OB_MPTELEFRAG"); - if (message == NULL) - { - if (inflictor != NULL && inflictor->GetClass()->Obituary.IsNotEmpty()) - { - message = inflictor->GetClass()->Obituary; - } - if (message == NULL && (dmgflags & DMG_PLAYERATTACK) && attacker->player->ReadyWeapon != NULL) - { - message = attacker->player->ReadyWeapon->GetClass()->Obituary; - } - if (message == NULL) - { - switch (mod) - { - case NAME_BFGSplash: messagename = "OB_MPBFG_SPLASH"; break; - case NAME_Railgun: messagename = "OB_RAILGUN"; break; - } - if (messagename != NULL) - message = GStrings(messagename); - } - if (message == NULL) - { - message = attacker->GetClass()->Obituary; - } - } - } - } - else attacker = self; // for the message creation + if (message == nullptr) message = messagename; // fallback to defaults if possible. + if (attacker == nullptr) attacker = self; // world + if (attacker->player == nullptr) attacker = self; // for the message creation if (message != NULL && message[0] == '$') { @@ -314,7 +270,7 @@ void ClientObituary (AActor *self, AActor *inflictor, AActor *attacker, int dmgf if (message == NULL || strlen(message) <= 0) return; - SexMessage (message, gendermessage, gender, + SexMessage (message, gendermessage, self->player->userinfo.GetGender(), self->player->userinfo.GetName(), attacker->player->userinfo.GetName()); Printf (PRINT_MEDIUM, "%s\n", gendermessage); } @@ -406,23 +362,11 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags) } flags6 |= MF6_KILLED; - // [RH] Allow the death height to be overridden using metadata. - double metaheight = -1; - if (DamageType == NAME_Fire) + IFVIRTUAL(AActor, GetDeathHeight) { - metaheight = GetClass()->BurnHeight; - } - if (metaheight < 0) - { - metaheight = GetClass()->DeathHeight; - } - if (metaheight < 0) - { - Height *= 0.25; - } - else - { - Height = MAX (metaheight, 0); + VMValue params[] = { (DObject*)this }; + VMReturn ret(&Height); + GlobalVMStack.Call(func, params, 1, &ret, 1); } // [RH] If the thing has a special, execute and remove it @@ -1015,6 +959,11 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da } return 0; } + if (target == source && damage < TELEFRAG_DAMAGE) + { + damage = int(damage * target->SelfDamageFactor); + } + // [MC] Changed it to check rawdamage here for consistency, even though that doesn't actually do anything // different here. At any rate, invulnerable is being checked before type factoring, which is then being // checked by player cheats/invul/buddha followed by monster buddha. This is inconsistent. Don't let the @@ -1162,6 +1111,38 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da // any negative value means that something in the above chain has cancelled out all damage and all damage effects, including pain. return 0; } + + + //[RC] Backported from the Zandronum source.. Mostly. + if( target->player && + damage > 0 && + source && + mod != NAME_Reflection && + target != source) + + { + int reflectdamage = 0; + for (auto p = target->player->mo->Inventory; p != nullptr; p = p->Inventory) + { + // This picks the reflection item with the maximum efficiency for the given damage type. + if (p->IsKindOf(NAME_PowerReflection)) + { + int mydamage = p->ApplyDamageFactor(mod, damage); + if (mydamage > reflectdamage) reflectdamage = mydamage; + } + } + + if (reflectdamage > 0) + { + // use the reflect item's damage factors to get the final value here. + P_DamageMobj(source, nullptr, target, reflectdamage, NAME_Reflection ); + + // Reset means of death flag. + MeansOfDeath = mod; + } + } + + // Push the target unless the source's weapon's kickback is 0. // (i.e. Gauntlets/Chainsaw) if (!plrDontThrust && inflictor && inflictor != target // [RH] Not if hurting own self @@ -1180,6 +1161,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da else kickback = source->player->ReadyWeapon->Kickback; + kickback = int(kickback * G_SkillProperty(SKILLP_KickbackFactor)); if (kickback) { AActor *origin = (source && (flags & DMG_INFLICTOR_IS_PUFF))? source : inflictor; @@ -1417,13 +1399,24 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da // If the damaging player has the power of drain, give the player 50% of the damage // done in health. - if ( source && source->player && source->player->cheats & CF_DRAIN && !(target->flags5 & MF5_DONTDRAIN)) + if ( source && source->player && !(target->flags5 & MF5_DONTDRAIN)) { if (!target->player || target->player != source->player) { - if ( P_GiveBody( source, damage / 2 )) + double draindamage = 0; + for (auto p = source->player->mo->Inventory; p != nullptr; p = p->Inventory) { - S_Sound( source, CHAN_ITEM, "*drainhealth", 1, ATTN_NORM ); + // This picks the item with the maximum efficiency. + if (p->IsKindOf(NAME_PowerDrain)) + { + double mydamage = p->FloatVar(NAME_Strength); + if (mydamage > draindamage) draindamage = mydamage; + } + } + + if ( P_GiveBody( source, int(draindamage * damage))) + { + S_Sound(source, CHAN_ITEM, "*drainhealth", 1, ATTN_NORM ); } } } @@ -1482,7 +1475,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da woundstate = target->FindState(NAME_Wound, mod); if (woundstate != NULL) { - int woundhealth = target->GetClass()->WoundHealth; + int woundhealth = target->WoundHealth; if (target->health <= woundhealth) { @@ -1714,6 +1707,9 @@ bool AActor::OkayToSwitchTarget (AActor *other) } } + if ((flags7 & MF7_NOINFIGHTSPECIES) && GetSpecies() == target->GetSpecies()) + return false; // Don't fight own species. + if ((other->flags3 & MF3_NOTARGET) && (other->tid != TIDtoHate || TIDtoHate == 0) && !IsHostile (other)) @@ -1726,7 +1722,8 @@ bool AActor::OkayToSwitchTarget (AActor *other) } int infight; - if (flags5 & MF5_NOINFIGHTING) infight=-1; + if (flags7 & MF7_FORCEINFIGHTING) infight = 1; + else if (flags5 & MF5_NOINFIGHTING) infight = -1; else infight = G_SkillProperty(SKILLP_Infight); if (infight < 0 && other->player == NULL && !IsHostile (other)) diff --git a/src/p_linkedsectors.cpp b/src/p_linkedsectors.cpp index 0dd29c10e2..7ac614f2ef 100644 --- a/src/p_linkedsectors.cpp +++ b/src/p_linkedsectors.cpp @@ -329,11 +329,13 @@ bool P_AddSectorLinks(sector_t *control, int tag, INTBOOL ceiling, int movetype) FSectorTagIterator itr(tag); while ((sec = itr.Next()) >= 0) { - // Don't attach to self! - if (control != &level.sectors[sec]) + // Don't attach to self (but allow attaching to this sector's oposite plane. + if (control == &level.sectors[sec]) { - AddSingleSector(scrollplane, &level.sectors[sec], movetype); + if (ceiling == sector_t::floor && movetype & LINK_FLOOR) continue; + if (ceiling == sector_t::ceiling && movetype & LINK_CEILING) continue; } + AddSingleSector(scrollplane, &level.sectors[sec], movetype); } } else diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index e71cf2f794..ff72b3baa3 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -297,7 +297,13 @@ FUNC(LS_Door_Animated) if (arg3 != 0 && !P_CheckKeys (it, arg3, arg0 != 0)) return false; - return EV_SlidingDoor (ln, it, arg0, arg1, arg2); + return EV_SlidingDoor (ln, it, arg0, arg1, arg2, DAnimatedDoor::adOpenClose); +} + +FUNC(LS_Door_AnimatedClose) +// Door_AnimatedClose (tag, speed) +{ + return EV_SlidingDoor(ln, it, arg0, arg1, -1, DAnimatedDoor::adClose); } FUNC(LS_Generic_Door) @@ -557,6 +563,13 @@ FUNC(LS_Generic_Floor) } +FUNC(LS_Floor_Stop) +// Floor_Stop (tag) +{ + return EV_StopFloor(arg0); +} + + FUNC(LS_Stairs_BuildDown) // Stair_BuildDown (tag, speed, height, delay, reset) { @@ -854,6 +867,13 @@ FUNC(LS_Ceiling_LowerByTexture) return EV_DoCeiling (DCeiling::ceilLowerByTexture, ln, arg0, SPEED(arg1), 0, 0, CRUSH(arg3), 0, CHANGE(arg4)); } +FUNC(LS_Ceiling_Stop) +// Ceiling_Stop (tag) +{ + return EV_StopCeiling(arg0); +} + + FUNC(LS_Generic_Ceiling) // Generic_Ceiling (tag, speed, height, target, change/model/direct/crush) { @@ -1731,14 +1751,14 @@ FUNC(LS_Thing_SpawnFacing) } FUNC(LS_Thing_Raise) -// Thing_Raise(tid) +// Thing_Raise(tid, nocheck) { AActor * target; bool ok = false; if (arg0==0) { - ok = P_Thing_Raise (it,NULL); + ok = P_Thing_Raise (it,NULL, arg1); } else { @@ -1746,7 +1766,7 @@ FUNC(LS_Thing_Raise) while ( (target = iterator.Next ()) ) { - ok |= P_Thing_Raise(target,NULL); + ok |= P_Thing_Raise(target,NULL, arg1); } } return ok; @@ -3171,7 +3191,7 @@ FUNC(LS_ClearForceField) } FUNC(LS_GlassBreak) -// GlassBreak (bNoJunk) +// GlassBreak (bNoJunk, junkID) { bool switched; bool quest1, quest2; @@ -3191,7 +3211,6 @@ FUNC(LS_GlassBreak) { if (!arg0) { // Break some glass - AActor *glass; DVector2 linemid((ln->v1->fX() + ln->v2->fX()) / 2, (ln->v1->fY() + ln->v2->fY()) / 2); @@ -3203,18 +3222,32 @@ FUNC(LS_GlassBreak) y += (ln->frontsector->centerspot.y - y) / 5; */ + auto type = SpawnableThings.CheckKey(arg1); for (int i = 0; i < 7; ++i) { - glass = Spawn("GlassJunk", DVector3(linemid, ONFLOORZ), ALLOW_REPLACE); - - glass->AddZ(24.); - glass->SetState (glass->SpawnState + (pr_glass() % glass->health)); - - glass->Angles.Yaw = pr_glass() * (360 / 256.); - glass->VelFromAngle(pr_glass() & 3); - glass->Vel.Z = (pr_glass() & 7); - // [RH] Let the shards stick around longer than they did in Strife. - glass->tics += pr_glass(); + AActor *glass = nullptr; + if (arg1 > 0) + { + if (type != nullptr) + { + glass = Spawn(*type, DVector3(linemid, ONFLOORZ), ALLOW_REPLACE); + glass->AddZ(24.); + } + } + else + { + glass = Spawn("GlassJunk", DVector3(linemid, ONFLOORZ), ALLOW_REPLACE); + glass->AddZ(24.); + glass->SetState(glass->SpawnState + (pr_glass() % glass->health)); + } + if (glass != nullptr) + { + glass->Angles.Yaw = pr_glass() * (360 / 256.); + glass->VelFromAngle(pr_glass() & 3); + glass->Vel.Z = (pr_glass() & 7); + // [RH] Let the shards stick around longer than they did in Strife. + glass->tics += pr_glass(); + } } } if (quest1 || quest2) @@ -3594,6 +3627,9 @@ static lnSpecFunc LineSpecials[] = /* 271 */ LS_Stairs_BuildUpDoomSync, /* 272 */ LS_Stairs_BuildDownDoomSync, /* 273 */ LS_Stairs_BuildUpDoomCrush, + /* 274 */ LS_Door_AnimatedClose, + /* 275 */ LS_Floor_Stop, + /* 276 */ LS_Ceiling_Stop, }; @@ -3717,3 +3753,27 @@ int P_ExecuteSpecial(int num, } return 0; } + +//========================================================================== +// +// Execute a line special / script +// +//========================================================================== +DEFINE_ACTION_FUNCTION(FLevelLocals, ExecuteSpecial) +{ + PARAM_SELF_STRUCT_PROLOGUE(FLevelLocals); + PARAM_INT(special); + PARAM_OBJECT(activator, AActor); + PARAM_POINTER(linedef, line_t); + PARAM_BOOL(lineside); + 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, linedef, activator, lineside, arg1, arg2, arg3, arg4, arg5); + + ACTION_RETURN_BOOL(res); +} + diff --git a/src/p_local.h b/src/p_local.h index 89346328e8..9ce290be76 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -111,7 +111,7 @@ void P_BloodSplatter (const DVector3 &pos, AActor *originator, DAngle hitangle); void P_BloodSplatter2 (const DVector3 &pos, AActor *originator, DAngle hitangle); void P_RipperBlood (AActor *mo, AActor *bleeder); int P_GetThingFloorType (AActor *thing); -void P_ExplodeMissile (AActor *missile, line_t *explodeline, AActor *target); +void P_ExplodeMissile (AActor *missile, line_t *explodeline, AActor *target, bool onsky = false); AActor *P_OldSpawnMissile(AActor *source, AActor *owner, AActor *dest, PClassActor *type); AActor *P_SpawnMissile (AActor* source, AActor* dest, PClassActor *type, AActor* owner = NULL); @@ -151,7 +151,7 @@ bool P_Thing_Move (int tid, AActor *source, int mapspot, bool fog); int P_Thing_Damage (int tid, AActor *whofor0, int amount, FName type); void P_Thing_SetVelocity(AActor *actor, const DVector3 &vec, bool add, bool setbob); void P_RemoveThing(AActor * actor); -bool P_Thing_Raise(AActor *thing, AActor *raiser); +bool P_Thing_Raise(AActor *thing, AActor *raiser, int nocheck = false); bool P_Thing_CanRaise(AActor *thing); PClassActor *P_GetSpawnableType(int spawnnum); void InitSpawnablesFromMapinfo(); diff --git a/src/p_map.cpp b/src/p_map.cpp index da84855fde..4d47b89c19 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -1177,6 +1177,7 @@ static bool CanAttackHurt(AActor *victim, AActor *shooter) if (!victim->player && !shooter->player) { int infight = G_SkillProperty(SKILLP_Infight); + if (infight < 0 && (victim->flags7 & MF7_FORCEINFIGHTING)) infight = 0; // This must override the 'no infight' setting to take effect. if (infight < 0) { @@ -2280,9 +2281,9 @@ bool P_TryMove(AActor *thing, const DVector2 &pos, } #endif } - if (!(thing->flags & MF_TELEPORT) && !(thing->flags3 & MF3_FLOORHUGGER)) + if (!(thing->flags & MF_TELEPORT) && (!(thing->flags3 & MF3_FLOORHUGGER) || thing->flags5 & MF5_NODROPOFF)) { - if ((thing->flags & MF_MISSILE) && !(thing->flags6 & MF6_STEPMISSILE) && tm.floorz > thing->Z()) + if ((thing->flags & MF_MISSILE) && !(thing->flags6 & MF6_STEPMISSILE) && tm.floorz > thing->Z() && !(thing->flags3 & MF3_FLOORHUGGER)) { // [RH] Don't let normal missiles climb steps goto pushline; } @@ -2771,13 +2772,13 @@ bool P_CheckMove(AActor *thing, const DVector2 &pos, int flags) if (thing->Top() > tm.ceilingz) return false; } - if (!(thing->flags & MF_TELEPORT) && !(thing->flags3 & MF3_FLOORHUGGER)) + if (!(thing->flags & MF_TELEPORT) && (!(thing->flags3 & MF3_FLOORHUGGER) || thing->flags5 & MF5_NODROPOFF)) { if (tm.floorz - newz > thing->MaxStepHeight) { // too big a step up return false; } - else if ((thing->flags & MF_MISSILE) && !(thing->flags6 & MF6_STEPMISSILE) && tm.floorz > newz) + else if ((thing->flags & MF_MISSILE) && !(thing->flags6 & MF6_STEPMISSILE) && tm.floorz > newz && !(thing->flags3 & MF3_FLOORHUGGER)) { // [RH] Don't let normal missiles climb steps return false; } @@ -3536,6 +3537,11 @@ bool P_BounceActor(AActor *mo, AActor *BlockingMobj, bool ontop) if ((mo->flags & MF_MISSILE) && (mo->flags2 & MF2_RIP) && BlockingMobj->flags & MF_SHOOTABLE) return true; + if (BlockingMobj->flags & MF_SHOOTABLE && mo->BounceFlags & BOUNCE_NotOnShootables) + { + mo->bouncecount = 1; // let it explode now. + } + if (mo->bouncecount>0 && --mo->bouncecount == 0) { if (mo->flags & MF_MISSILE) @@ -4797,7 +4803,7 @@ void P_TraceBleed(int damage, const DVector3 &pos, AActor *actor, DAngle angle, { if (bleedtrace.HitType == TRACE_HitWall) { - PalEntry bloodcolor = actor->GetBloodColor(); + PalEntry bloodcolor = actor->BloodColor; if (bloodcolor != 0) { bloodcolor.r >>= 1; // the full color is too bright for blood decals @@ -5276,20 +5282,22 @@ bool P_UseTraverse(AActor *usething, const DVector2 &start, const DVector2 &end, // [RH] Check for things to talk with or use a puzzle item on if (!in->isaline) { - if (usething == in->d.thing) + AActor * const mobj = in->d.thing; + + if (mobj == usething) continue; // Check thing // Check for puzzle item use or USESPECIAL flag // Extended to use the same activationtype mechanism as BUMPSPECIAL does - if (in->d.thing->flags5 & MF5_USESPECIAL || in->d.thing->special == UsePuzzleItem) + if (mobj->flags5 & MF5_USESPECIAL || mobj->special == UsePuzzleItem) { - if (P_ActivateThingSpecial(in->d.thing, usething)) + if (P_ActivateThingSpecial(mobj, usething)) return true; } - IFVIRTUALPTR(usething, AActor, Used) + IFVIRTUALPTR(mobj, AActor, Used) { - VMValue params[] = { usething, in->d.thing }; + VMValue params[] = { mobj, usething }; int ret; VMReturn vret(&ret); GlobalVMStack.Call(func, params, 2, &vret, 1); @@ -5672,10 +5680,11 @@ int P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bom { points = points * splashfactor; } - points *= thing->GetClass()->RDFactor; + points *= thing->RadiusDamageFactor; + double check = int(points) * bombdamage; // points and bombdamage should be the same sign (the double cast of 'points' is needed to prevent overflows and incorrect values slipping through.) - if ((((double)int(points) * bombdamage) > 0) && P_CheckSight(thing, bombspot, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY)) + if ((check > 0 || (check == 0 && bombspot->flags7 & MF7_FORCEZERORADIUSDMG)) && P_CheckSight(thing, bombspot, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY)) { // OK to damage; target is in direct path double vz; double thrust; @@ -5750,9 +5759,9 @@ int P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bom dist = clamp(dist - fulldamagedistance, 0, dist); int damage = Scale(bombdamage, bombdistance - int(dist), bombdistance); - double factor = splashfactor * thing->GetClass()->RDFactor; + double factor = splashfactor * thing->RadiusDamageFactor; damage = int(damage * factor); - if (damage > 0) + if (damage > 0 || (bombspot->flags7 & MF7_FORCEZERORADIUSDMG)) { //[MC] Don't count actors saved by buddha if already at 1 health. int prehealth = thing->health; @@ -5985,7 +5994,6 @@ void P_DoCrunch(AActor *thing, FChangePosition *cpos) { if (!(thing->flags&MF_NOBLOOD)) { - PalEntry bloodcolor = thing->GetBloodColor(); PClassActor *bloodcls = thing->GetBloodType(); P_TraceBleed (newdam > 0 ? newdam : cpos->crushchange, thing); @@ -5997,9 +6005,9 @@ void P_DoCrunch(AActor *thing, FChangePosition *cpos) mo->Vel.X = pr_crunch.Random2() / 16.; mo->Vel.Y = pr_crunch.Random2() / 16.; - if (bloodcolor != 0 && !(mo->flags2 & MF2_DONTTRANSLATE)) + if (thing->BloodTranslation != 0 && !(mo->flags2 & MF2_DONTTRANSLATE)) { - mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a); + mo->Translation = thing->BloodTranslation; } if (!(cl_bloodtype <= 1)) mo->renderflags |= RF_INVISIBLE; @@ -6008,7 +6016,7 @@ void P_DoCrunch(AActor *thing, FChangePosition *cpos) DAngle an = (M_Random() - 128) * (360./256); if (cl_bloodtype >= 1) { - P_DrawSplash2(32, thing->PosPlusZ(thing->Height/2), an, 2, bloodcolor); + P_DrawSplash2(32, thing->PosPlusZ(thing->Height/2), an, 2, thing->BloodColor); } } if (thing->CrushPainSound != 0 && !S_GetSoundPlayingInfo(thing, thing->CrushPainSound)) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 5bf6118004..d7de2a18cc 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -311,28 +311,13 @@ DEFINE_FIELD(AActor, ConversationRoot) DEFINE_FIELD(AActor, Conversation) DEFINE_FIELD(AActor, DecalGenerator) DEFINE_FIELD(AActor, fountaincolor) - -DEFINE_FIELD(PClassActor, Obituary) -DEFINE_FIELD(PClassActor, HitObituary) -DEFINE_FIELD(PClassActor, DeathHeight) -DEFINE_FIELD(PClassActor, BurnHeight) -DEFINE_FIELD(PClassActor, BloodColor) -DEFINE_FIELD(PClassActor, GibHealth) -DEFINE_FIELD(PClassActor, WoundHealth) -DEFINE_FIELD(PClassActor, FastSpeed) -DEFINE_FIELD(PClassActor, RDFactor) -DEFINE_FIELD(PClassActor, CameraHeight) -DEFINE_FIELD(PClassActor, HowlSound) -DEFINE_FIELD(PClassActor, BloodType) -DEFINE_FIELD(PClassActor, BloodType2) -DEFINE_FIELD(PClassActor, BloodType3) -DEFINE_FIELD(PClassActor, DontHurtShooter) -DEFINE_FIELD(PClassActor, ExplosionRadius) -DEFINE_FIELD(PClassActor, ExplosionDamage) -DEFINE_FIELD(PClassActor, MeleeDamage) -DEFINE_FIELD(PClassActor, MeleeSound) -DEFINE_FIELD(PClassActor, MissileName) -DEFINE_FIELD(PClassActor, MissileHeight) +DEFINE_FIELD(AActor, CameraHeight) +DEFINE_FIELD(AActor, RadiusDamageFactor) +DEFINE_FIELD(AActor, SelfDamageFactor) +DEFINE_FIELD(AActor, StealthAlpha) +DEFINE_FIELD(AActor, WoundHealth) +DEFINE_FIELD(AActor, BloodColor) +DEFINE_FIELD(AActor, BloodTranslation) //========================================================================== // @@ -424,6 +409,8 @@ void AActor::Serialize(FSerializer &arc) A("inventoryid", InventoryID) A("floatbobphase", FloatBobPhase) A("translation", Translation) + A("bloodcolor", BloodColor) + A("bloodtranslation", BloodTranslation) A("seesound", SeeSound) A("attacksound", AttackSound) A("paimsound", PainSound) @@ -493,11 +480,17 @@ void AActor::Serialize(FSerializer &arc) A("spriteangle", SpriteAngle) A("spriterotation", SpriteRotation) ("alternative", alternative) + A("cameraheight", CameraHeight) A("tag", Tag) A("visiblestartangle",VisibleStartAngle) A("visibleendangle",VisibleEndAngle) A("visiblestartpitch",VisibleStartPitch) - A("visibleendpitch",VisibleEndPitch); + A("visibleendpitch",VisibleEndPitch) + A("woundhealth", WoundHealth) + A("rdfactor", RadiusDamageFactor) + A("selfdamagefactor", SelfDamageFactor) + A("stealthalpha", StealthAlpha); + } #undef A @@ -522,7 +515,7 @@ void AActor::PostSerialize() !(flags4 & MF4_NOSKIN) && state->sprite == GetDefaultByType(player->cls)->SpawnState->sprite) { // Give player back the skin - sprite = skins[player->userinfo.GetSkin()].sprite; + sprite = Skins[player->userinfo.GetSkin()].sprite; } if (Speed == 0) { @@ -666,9 +659,9 @@ bool AActor::SetState (FState *newstate, bool nofunction) // for Dehacked, I would move sprite changing out of the states // altogether, since actors rarely change their sprites after // spawning. - if (player != NULL && skins != NULL) + if (player != NULL && Skins.Size() > 0) { - sprite = skins[player->userinfo.GetSkin()].sprite; + sprite = Skins[player->userinfo.GetSkin()].sprite; } else if (newsprite != prevsprite) { @@ -902,8 +895,7 @@ bool AActor::TakeInventory(PClassActor *itemclass, int amount, bool fromdecorate // Do not take ammo if the "no take infinite/take as ammo depletion" flag is set // and infinite ammo is on if (notakeinfinite && - ((dmflags & DF_INFINITE_AMMO) || (player && player->cheats & CF_INFINITEAMMO)) && - item->IsKindOf(NAME_Ammo)) + ((dmflags & DF_INFINITE_AMMO) || (player && FindInventory(NAME_PowerInfiniteAmmo, true))) && item->IsKindOf(NAME_Ammo)) { // Nothing to do here, except maybe res = false;? Would it make sense? result = false; @@ -1035,14 +1027,14 @@ DEFINE_ACTION_FUNCTION(AActor, UseInventory) // //=========================================================================== -AInventory *AActor::DropInventory (AInventory *item) +AInventory *AActor::DropInventory (AInventory *item, int amt) { AInventory *drop = nullptr; IFVIRTUALPTR(item, AInventory, CreateTossable) { - VMValue params[1] = { (DObject*)item }; + VMValue params[] = { (DObject*)item, amt }; VMReturn ret((void**)&drop); - GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); + GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr); } if (drop == nullptr) return NULL; drop->SetOrigin(PosPlusZ(10.), false); @@ -1059,7 +1051,8 @@ DEFINE_ACTION_FUNCTION(AActor, DropInventory) { PARAM_SELF_PROLOGUE(AActor); PARAM_OBJECT_NOT_NULL(item, AInventory); - ACTION_RETURN_OBJECT(self->DropInventory(item)); + PARAM_INT_DEF(amt); + ACTION_RETURN_OBJECT(self->DropInventory(item, amt)); } //============================================================================ @@ -1339,7 +1332,7 @@ bool P_GiveBody(AActor *actor, int num, int max) // calls while supporting health pickups. if (max <= 0) { - max = static_cast(actor)->GetMaxHealth() + player->mo->stamina; + max = static_cast(actor)->GetMaxHealth(true); // [MH] First step in predictable generic morph effects if (player->morphTics) { @@ -1347,7 +1340,7 @@ bool P_GiveBody(AActor *actor, int num, int max) { if (!(player->MorphStyle & MORPH_ADDSTAMINA)) { - max -= player->mo->stamina; + max -= player->mo->stamina + player->mo->BonusHealth; } } else // old health behaviour @@ -1355,7 +1348,7 @@ bool P_GiveBody(AActor *actor, int num, int max) max = MAXMORPHHEALTH; if (player->MorphStyle & MORPH_ADDSTAMINA) { - max += player->mo->stamina; + max += player->mo->stamina + player->mo->BonusHealth; } } } @@ -1674,8 +1667,7 @@ bool AActor::Grind(bool items) if (isgeneric) // Not a custom crush state, so colorize it appropriately. { S_Sound (this, CHAN_BODY, "misc/fallingsplat", 1, ATTN_IDLE); - PalEntry bloodcolor = GetBloodColor(); - if (bloodcolor!=0) Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a); + Translation = BloodTranslation; } return false; } @@ -1716,10 +1708,7 @@ bool AActor::Grind(bool items) gib->Alpha = Alpha; gib->Height = 0; gib->radius = 0; - - PalEntry bloodcolor = GetBloodColor(); - if (bloodcolor != 0) - gib->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a); + gib->Translation = BloodTranslation; } S_Sound (this, CHAN_BODY, "misc/fallingsplat", 1, ATTN_IDLE); } @@ -1803,7 +1792,7 @@ bool AActor::Massacre () // //---------------------------------------------------------------------------- -void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target) +void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target, bool onsky) { if (mo->flags3 & MF3_EXPLOCOUNT) { @@ -1831,11 +1820,15 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target) } if (nextstate == NULL) nextstate = mo->FindState(NAME_Death); - if (line != NULL && line->special == Line_Horizon && !(mo->flags3 & MF3_SKYEXPLODE)) + if (onsky || (line != NULL && line->special == Line_Horizon)) { - // [RH] Don't explode missiles on horizon lines. - mo->Destroy (); - return; + if (!(mo->flags3 & MF3_SKYEXPLODE)) + { + // [RH] Don't explode missiles on horizon lines. + mo->Destroy(); + return; + } + nextstate = mo->FindState(NAME_Death, NAME_Sky); } if (line != NULL && cl_missiledecals) @@ -2556,26 +2549,32 @@ double P_XYMovement (AActor *mo, DVector2 scroll) } explode: // explode a missile - if (!(mo->flags3 & MF3_SKYEXPLODE)) - { + bool onsky = false; if (tm.ceilingline && tm.ceilingline->backsector && tm.ceilingline->backsector->GetTexture(sector_t::ceiling) == skyflatnum && mo->Z() >= tm.ceilingline->backsector->ceilingplane.ZatPoint(mo->PosRelative(tm.ceilingline))) { - // Hack to prevent missiles exploding against the sky. - // Does not handle sky floors. - mo->Destroy (); - return Oldfloorz; + if (!(mo->flags3 & MF3_SKYEXPLODE)) + { + // Hack to prevent missiles exploding against the sky. + // Does not handle sky floors. + mo->Destroy(); + return Oldfloorz; + } + else onsky = true; } // [RH] Don't explode on horizon lines. if (mo->BlockingLine != NULL && mo->BlockingLine->special == Line_Horizon) { - mo->Destroy (); - return Oldfloorz; + if (!(mo->flags3 & MF3_SKYEXPLODE)) + { + mo->Destroy(); + return Oldfloorz; + } + else onsky = true; } - } - P_ExplodeMissile (mo, mo->BlockingLine, BlockingMobj); + P_ExplodeMissile (mo, mo->BlockingLine, BlockingMobj, onsky); return Oldfloorz; } else @@ -2948,21 +2947,26 @@ void P_ZMovement (AActor *mo, double oldfloorz) mo->Vel.Z = 0; return; } - else if (mo->flags3 & MF3_FLOORHUGGER) + else if ((mo->flags3 & MF3_FLOORHUGGER) && !(mo->flags5 & MF5_NODROPOFF)) { // Floor huggers can go up steps return; } else { - if (mo->floorpic == skyflatnum && !(mo->flags3 & MF3_SKYEXPLODE)) + bool onsky = false; + if (mo->floorpic == skyflatnum) { - // [RH] Just remove the missile without exploding it - // if this is a sky floor. - mo->Destroy (); - return; + if (!(mo->flags3 & MF3_SKYEXPLODE)) + { + // [RH] Just remove the missile without exploding it + // if this is a sky floor. + mo->Destroy(); + return; + } + else onsky = true; } P_HitFloor (mo); - P_ExplodeMissile (mo, NULL, NULL); + P_ExplodeMissile (mo, NULL, NULL, onsky); return; } } @@ -3055,12 +3059,17 @@ void P_ZMovement (AActor *mo, double oldfloorz) { return; } - if (mo->ceilingpic == skyflatnum && !(mo->flags3 & MF3_SKYEXPLODE)) + bool onsky = false; + if (mo->ceilingpic == skyflatnum) { - mo->Destroy (); - return; + if (!(mo->flags3 & MF3_SKYEXPLODE)) + { + mo->Destroy(); + return; + } + else onsky = true; } - P_ExplodeMissile (mo, NULL, NULL); + P_ExplodeMissile (mo, NULL, NULL, onsky); return; } } @@ -3499,7 +3508,7 @@ int AActor::GetMissileDamage (int mask, int add) void AActor::Howl () { - FSoundID howl = GetClass()->HowlSound; + FSoundID howl = SoundVar(NAME_HowlSound); if (!S_IsActorPlayingSomething(this, CHAN_BODY, howl)) { S_Sound (this, CHAN_BODY, howl, 1, ATTN_NORM); @@ -3801,6 +3810,19 @@ void AActor::SetRoll(DAngle r, bool interpolate) } } +PClassActor *AActor::GetBloodType(int type) const +{ + IFVIRTUAL(AActor, GetBloodType) + { + VMValue params[] = { (DObject*)this, type }; + PClassActor *res; + VMReturn ret((void**)&res); + GlobalVMStack.Call(func, params, countof(params), &ret, 1); + return res; + } + return nullptr; +} + DVector3 AActor::GetPortalTransition(double byoffset, sector_t **pSec) { @@ -3848,7 +3870,7 @@ void AActor::CheckPortalTransition(bool islinked) DVector3 oldpos = Pos(); if (islinked && !moved) UnlinkFromWorld(&ctx); SetXYZ(PosRelative(Sector->GetOppositePortalGroup(sector_t::ceiling))); - Prev = Pos() - oldpos; + Prev += Pos() - oldpos; Sector = P_PointInSector(Pos()); PrevPortalGroup = Sector->PortalGroup; moved = true; @@ -3865,7 +3887,7 @@ void AActor::CheckPortalTransition(bool islinked) DVector3 oldpos = Pos(); if (islinked && !moved) UnlinkFromWorld(&ctx); SetXYZ(PosRelative(Sector->GetOppositePortalGroup(sector_t::floor))); - Prev = Pos() - oldpos; + Prev += Pos() - oldpos; Sector = P_PointInSector(Pos()); PrevPortalGroup = Sector->PortalGroup; moved = true; @@ -4079,9 +4101,9 @@ void AActor::Tick () else if (visdir < 0) { Alpha -= 1.5/TICRATE; - if (Alpha < 0) + if (Alpha < StealthAlpha) { - Alpha = 0; + Alpha = StealthAlpha; visdir = 0; } } @@ -4417,7 +4439,8 @@ void AActor::Tick () return; // freed itself } } - else + + if (tics == -1 || state->GetCanRaise()) { int respawn_monsters = G_SkillProperty(SKILLP_Respawn); // check for nightmare respawn @@ -4801,8 +4824,11 @@ AActor *AActor::StaticSpawn (PClassActor *type, const DVector3 &pos, replace_t a actor->renderflags = (actor->renderflags & ~RF_FULLBRIGHT) | ActorRenderFlags::FromInt (st->GetFullbright()); actor->touching_sectorlist = nullptr; // NULL head of sector list // phares 3/13/98 actor->touching_rendersectors = nullptr; - if (G_SkillProperty(SKILLP_FastMonsters) && actor->GetClass()->FastSpeed >= 0) - actor->Speed = actor->GetClass()->FastSpeed; + if (G_SkillProperty(SKILLP_FastMonsters)) + { + double f = actor->FloatVar(NAME_FastSpeed); + if (f >= 0) actor->Speed = f; + } // set subsector and/or block links actor->LinkToWorld (nullptr, SpawningMapThing); @@ -5282,6 +5308,7 @@ DEFINE_ACTION_FUNCTION(AActor, AdjustFloorClip) // EXTERN_CVAR (Bool, chasedemo) EXTERN_CVAR(Bool, sv_singleplayerrespawn) +EXTERN_CVAR(Float, fov) extern bool demonew; @@ -5398,7 +5425,7 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags) } // [GRB] Reset skin - p->userinfo.SkinNumChanged(R_FindSkin (skins[p->userinfo.GetSkin()].name, p->CurrentPlayerClass)); + p->userinfo.SkinNumChanged(R_FindSkin (Skins[p->userinfo.GetSkin()].Name, p->CurrentPlayerClass)); if (!(mobj->flags2 & MF2_DONTTRANSLATE)) { @@ -5416,10 +5443,10 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags) // [RH] Set player sprite based on skin if (!(mobj->flags4 & MF4_NOSKIN)) { - mobj->sprite = skins[p->userinfo.GetSkin()].sprite; + mobj->sprite = Skins[p->userinfo.GetSkin()].sprite; } - p->DesiredFOV = p->FOV = 90.f; + p->DesiredFOV = p->FOV = fov; p->camera = p->mo; p->playerstate = PST_LIVE; p->refire = 0; @@ -5510,9 +5537,6 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags) { if (state == PST_ENTER || (state == PST_LIVE && !savegamerestore)) { - // [ZZ] fire non-hub ENTER event - // level.time is a hack to make sure that we don't call it on dummy player initialization during hub return. - if (!level.time) E_PlayerEntered(int(p - players), false); FBehavior::StaticStartTypedScripts (SCRIPT_Enter, p->mo, true); } else if (state == PST_REBORN) @@ -5539,6 +5563,7 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags) for (int ii=0; ii < BODYQUESIZE; ++ii) if (bodyque[ii] == p->mo) bodyque[ii] = oldactor; + E_PlayerRespawned(int(p - players)); FBehavior::StaticStartTypedScripts (SCRIPT_Respawn, p->mo, true); } } @@ -5795,27 +5820,23 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) // [RH] Other things that shouldn't be spawned depending on dmflags if (deathmatch || alwaysapplydmflags) { - // Fixme: This needs to be done differently, it's quite broken. - if (dmflags & DF_NO_HEALTH) + if (i->IsDescendantOf(RUNTIME_CLASS(AInventory))) { - if (i->IsDescendantOf (PClass::FindActor(NAME_Health))) - return NULL; - if (i->TypeName == NAME_Berserk) - return NULL; - if (i->TypeName == NAME_Megasphere) - return NULL; - } - if (dmflags & DF_NO_ITEMS) - { -// if (i->IsDescendantOf (RUNTIME_CLASS(AArtifact))) -// return; - } - if (dmflags & DF_NO_ARMOR) - { - if (i->IsDescendantOf (PClass::FindActor(NAME_Armor))) - return NULL; - if (i->TypeName == NAME_Megasphere) - return NULL; + auto it = static_cast(GetDefaultByType(i)); + + if (dmflags & DF_NO_HEALTH) + { + if (it->ItemFlags & IF_ISHEALTH) return nullptr; + } + if (dmflags & DF_NO_ITEMS) + { + // if (i->IsDescendantOf (RUNTIME_CLASS(AArtifact))) + // return; + } + if (dmflags & DF_NO_ARMOR) + { + if (it->ItemFlags & IF_ISARMOR) return nullptr; + } } } @@ -5910,13 +5931,13 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) mobj->LevelSpawned (); } - if (mthing->health > 0) - mobj->health *= mthing->health; + if (mthing->Health > 0) + mobj->health = int(mobj->health * mthing->Health); else - mobj->health = -mthing->health; - if (mthing->health == 0) + mobj->health = -int(mthing->Health); + if (mthing->Health == 0) mobj->CallDie(NULL, NULL); - else if (mthing->health != 1) + else if (mthing->Health != 1) mobj->StartHealth = mobj->health; return mobj; @@ -5989,7 +6010,7 @@ AActor *P_SpawnPuff (AActor *source, PClassActor *pufftype, const DVector3 &pos1 if (cl_pufftype && updown != 3 && (puff->flags4 & MF4_ALLOWPARTICLES)) { P_DrawSplash2 (32, pos, particledir, updown, 1); - puff->renderflags |= RF_INVISIBLE; + if (cl_pufftype == 1) puff->renderflags |= RF_INVISIBLE; } if ((flags & PF_HITTHING) && puff->SeeSound) @@ -6029,7 +6050,6 @@ DEFINE_ACTION_FUNCTION(AActor, SpawnPuff) void P_SpawnBlood (const DVector3 &pos1, DAngle dir, int damage, AActor *originator) { AActor *th; - PalEntry bloodcolor = originator->GetBloodColor(); PClassActor *bloodcls = originator->GetBloodType(); DVector3 pos = pos1; pos.Z += pr_spawnblood.Random2() / 64.; @@ -6054,9 +6074,9 @@ void P_SpawnBlood (const DVector3 &pos1, DAngle dir, int damage, AActor *origina th->tics = 1; } // colorize the blood - if (bloodcolor != 0 && !(th->flags2 & MF2_DONTTRANSLATE)) + if (!(th->flags2 & MF2_DONTTRANSLATE)) { - th->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a); + th->Translation = originator->BloodTranslation; } // Moved out of the blood actor so that replacing blood is easier @@ -6113,7 +6133,7 @@ void P_SpawnBlood (const DVector3 &pos1, DAngle dir, int damage, AActor *origina } if (bloodtype >= 1) - P_DrawSplash2 (40, pos, dir, 2, bloodcolor); + P_DrawSplash2 (40, pos, dir, 2, originator->BloodColor); } DEFINE_ACTION_FUNCTION(AActor, SpawnBlood) @@ -6137,7 +6157,6 @@ DEFINE_ACTION_FUNCTION(AActor, SpawnBlood) void P_BloodSplatter (const DVector3 &pos, AActor *originator, DAngle hitangle) { - PalEntry bloodcolor = originator->GetBloodColor(); PClassActor *bloodcls = originator->GetBloodType(1); int bloodtype = cl_bloodtype; @@ -6156,16 +6175,16 @@ void P_BloodSplatter (const DVector3 &pos, AActor *originator, DAngle hitangle) mo->Vel.Z = 3; // colorize the blood! - if (bloodcolor!=0 && !(mo->flags2 & MF2_DONTTRANSLATE)) + if (!(mo->flags2 & MF2_DONTTRANSLATE)) { - mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a); + mo->Translation = originator->BloodTranslation; } if (!(bloodtype <= 1)) mo->renderflags |= RF_INVISIBLE; } if (bloodtype >= 1) { - P_DrawSplash2 (40, pos, hitangle-180., 2, bloodcolor); + P_DrawSplash2 (40, pos, hitangle-180., 2, originator->BloodColor); } } @@ -6177,7 +6196,6 @@ void P_BloodSplatter (const DVector3 &pos, AActor *originator, DAngle hitangle) void P_BloodSplatter2 (const DVector3 &pos, AActor *originator, DAngle hitangle) { - PalEntry bloodcolor = originator->GetBloodColor(); PClassActor *bloodcls = originator->GetBloodType(2); int bloodtype = cl_bloodtype; @@ -6198,16 +6216,16 @@ void P_BloodSplatter2 (const DVector3 &pos, AActor *originator, DAngle hitangle) mo->target = originator; // colorize the blood! - if (bloodcolor != 0 && !(mo->flags2 & MF2_DONTTRANSLATE)) + if (!(mo->flags2 & MF2_DONTTRANSLATE)) { - mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a); + mo->Translation = originator->BloodTranslation; } if (!(bloodtype <= 1)) mo->renderflags |= RF_INVISIBLE; } if (bloodtype >= 1) { - P_DrawSplash2(40, pos + add, hitangle - 180., 2, bloodcolor); + P_DrawSplash2(40, pos + add, hitangle - 180., 2, originator->BloodColor); } } @@ -6233,7 +6251,6 @@ DEFINE_ACTION_FUNCTION(AActor, BloodSplatter) void P_RipperBlood (AActor *mo, AActor *bleeder) { - PalEntry bloodcolor = bleeder->GetBloodColor(); PClassActor *bloodcls = bleeder->GetBloodType(); double xo = pr_ripperblood.Random2() / 16.; @@ -6259,16 +6276,16 @@ void P_RipperBlood (AActor *mo, AActor *bleeder) th->tics += pr_ripperblood () & 3; // colorize the blood! - if (bloodcolor!=0 && !(th->flags2 & MF2_DONTTRANSLATE)) + if (!(th->flags2 & MF2_DONTTRANSLATE)) { - th->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a); + th->Translation = bleeder->BloodTranslation; } if (!(bloodtype <= 1)) th->renderflags |= RF_INVISIBLE; } if (bloodtype >= 1) { - P_DrawSplash2(28, pos, bleeder->AngleTo(mo) + 180., 0, bloodcolor); + P_DrawSplash2(28, pos, bleeder->AngleTo(mo) + 180., 0, bleeder->BloodColor); } } @@ -6677,10 +6694,14 @@ static double GetDefaultSpeed(PClassActor *type) { if (type == NULL) return 0; - else if (G_SkillProperty(SKILLP_FastMonsters) && type->FastSpeed >= 0) - return type->FastSpeed; - else - return GetDefaultByType(type)->Speed; + + auto def = GetDefaultByType(type); + if (G_SkillProperty(SKILLP_FastMonsters)) + { + double f = def->FloatVar(NAME_FastSpeed); + if (f >= 0) return f; + } + return def->Speed; } DEFINE_ACTION_FUNCTION(AActor, GetDefaultSpeed) @@ -7446,9 +7467,10 @@ void AActor::Crash() { FState *crashstate = NULL; + int gibh = GetGibHealth(); if (DamageType != NAME_None) { - if (health < GetGibHealth()) + if (health < gibh) { // Extreme death FName labels[] = { NAME_Crash, NAME_Extreme, DamageType }; crashstate = FindState (3, labels, true); @@ -7460,7 +7482,7 @@ void AActor::Crash() } if (crashstate == NULL) { - if (health < GetGibHealth()) + if (health < gibh) { // Extreme death crashstate = FindState(NAME_Crash, NAME_Extreme); } @@ -7566,21 +7588,20 @@ void AActor::Revive() int AActor::GetGibHealth() const { - int gibhealth = GetClass()->GibHealth; - - if (gibhealth != INT_MIN) + IFVIRTUAL(AActor, GetGibHealth) { - return -abs(gibhealth); - } - else - { - return -int(SpawnHealth() * gameinfo.gibfactor); + VMValue params[] = { (DObject*)this }; + int h; + VMReturn ret(&h); + GlobalVMStack.Call(func, params, 1, &ret, 1); + return h; } + return -SpawnHealth(); } double AActor::GetCameraHeight() const { - return GetClass()->CameraHeight == INT_MIN ? Height / 2 : GetClass()->CameraHeight; + return CameraHeight == INT_MIN ? Height / 2 : CameraHeight; } DEFINE_ACTION_FUNCTION(AActor, GetCameraHeight) @@ -8259,9 +8280,9 @@ void PrintMiscActorInfo(AActor *query) query->args[0], query->args[1], query->args[2], query->args[3], query->args[4], query->special1, query->special2); Printf("\nTID: %d", query->tid); - Printf("\nCoord= x: %f, y: %f, z:%f, floor:%f, ceiling:%f.", + Printf("\nCoord= x: %f, y: %f, z:%f, floor:%f, ceiling:%f, height= %f", query->X(), query->Y(), query->Z(), - query->floorz, query->ceilingz); + query->floorz, query->ceilingz, query->Height); Printf("\nSpeed= %f, velocity= x:%f, y:%f, z:%f, combined:%f.\n", query->Speed, query->Vel.X, query->Vel.Y, query->Vel.Z, query->Vel.Length()); Printf("Scale: x:%f, y:%f\n", query->Scale.X, query->Scale.Y); diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 8b8e93f6ad..47675699a2 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -536,6 +536,7 @@ void P_FireWeapon (player_t *player, FState *state) return; } + player->WeaponState &= ~WF_WEAPONBOBBING; player->mo->PlayAttacking (); weapon->bAltFire = false; if (state == nullptr) @@ -572,6 +573,7 @@ void P_FireWeaponAlt (player_t *player, FState *state) return; } + player->WeaponState &= ~WF_WEAPONBOBBING; player->mo->PlayAttacking (); weapon->bAltFire = true; diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index a3208598d2..8f06b78098 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -837,7 +837,7 @@ void CopyPlayer(player_t *dst, player_t *src, const char *name) } // Validate the skin - dst->userinfo.SkinNumChanged(R_FindSkin(skins[dst->userinfo.GetSkin()].name, dst->CurrentPlayerClass)); + dst->userinfo.SkinNumChanged(R_FindSkin(Skins[dst->userinfo.GetSkin()].Name, dst->CurrentPlayerClass)); // Make sure the player pawn points to the proper player struct. if (dst->mo != nullptr) diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 875d3fbc07..76e8b6b340 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -1741,7 +1741,7 @@ void P_LoadThings (MapData * map) mti[i].ClassFilter = 0xffff; // Doom map format doesn't have class flags so spawn for all player classes mti[i].RenderStyle = STYLE_Count; mti[i].Alpha = -1; - mti[i].health = 1; + mti[i].Health = 1; mti[i].FloatbobPhase = -1; mti[i].pos.X = LittleShort(mt->x); @@ -1837,7 +1837,7 @@ void P_LoadThings2 (MapData * map) mti[i].Gravity = 1; mti[i].RenderStyle = STYLE_Count; mti[i].Alpha = -1; - mti[i].health = 1; + mti[i].Health = 1; mti[i].FloatbobPhase = -1; } delete[] mtp; @@ -4202,8 +4202,8 @@ void P_Init () static void P_Shutdown () { // [ZZ] delete global event handlers + DThinker::DestroyThinkersInList(STAT_STATIC); E_Shutdown(false); - R_DeinitSpriteData (); P_DeinitKeyMessages (); P_FreeLevelData (); P_FreeExtraLevelData (); diff --git a/src/p_sight.cpp b/src/p_sight.cpp index 74b9fe1658..68bf2ed3d0 100644 --- a/src/p_sight.cpp +++ b/src/p_sight.cpp @@ -107,6 +107,7 @@ class SightCheck bool P_SightCheckLine (line_t *ld); int P_SightBlockLinesIterator (int x, int y); bool P_SightTraverseIntercepts (); + bool LineBlocksSight(line_t *ld); public: bool P_SightPathTraverse (); @@ -211,7 +212,14 @@ bool SightCheck::PTR_SightTraverse (intercept_t *in) double trX = Trace.x + Trace.dx * in->frac; double trY = Trace.y + Trace.dy * in->frac; - P_SightOpening (open, li, trX, trY); + + P_SightOpening(open, li, trX, trY); + if (LineBlocksSight(in->d.line)) + { + // This may not skip P_SightOpening, but only reduce the open range to 0. + open.range = 0; + open.bottom = open.top; + } FLinePortal *lport = li->getPortal(); @@ -362,6 +370,42 @@ bool SightCheck::PTR_SightTraverse (intercept_t *in) } +// performs trivial visibility checks. +bool SightCheck::LineBlocksSight(line_t *ld) +{ + // try to early out the check + if (!ld->backsector || !(ld->flags & ML_TWOSIDED) || (ld->flags & ML_BLOCKSIGHT)) + return true; // stop checking + + // [RH] don't see past block everything lines + if (ld->flags & ML_BLOCKEVERYTHING) + { + if (!(Flags & SF_SEEPASTBLOCKEVERYTHING)) + { + return true; + } + // Pretend the other side is invisible if this is not an impact line + // that runs a script on the current map. Used to prevent monsters + // from trying to attack through a block everything line unless + // there's a chance their attack will make it nonblocking. + if (!(Flags & SF_SEEPASTSHOOTABLELINES)) + { + if (!(ld->activation & SPAC_Impact)) + { + return true; + } + if (ld->special != ACS_Execute && ld->special != ACS_ExecuteAlways) + { + return true; + } + if (ld->args[1] != 0 && ld->args[1] != level.levelnum) + { + return true; + } + } + } + return false; +} /* ================== @@ -392,36 +436,9 @@ bool SightCheck::P_SightCheckLine (line_t *ld) return true; // line isn't crossed } - // try to early out the check - if (!ld->backsector || !(ld->flags & ML_TWOSIDED) || (ld->flags & ML_BLOCKSIGHT)) - return false; // stop checking - - // [RH] don't see past block everything lines - if (ld->flags & ML_BLOCKEVERYTHING) + if (!portalfound) // when portals come into play, the quick-outs here may not be performed { - if (!(Flags & SF_SEEPASTBLOCKEVERYTHING)) - { - return false; - } - // Pretend the other side is invisible if this is not an impact line - // that runs a script on the current map. Used to prevent monsters - // from trying to attack through a block everything line unless - // there's a chance their attack will make it nonblocking. - if (!(Flags & SF_SEEPASTSHOOTABLELINES)) - { - if (!(ld->activation & SPAC_Impact)) - { - return false; - } - if (ld->special != ACS_Execute && ld->special != ACS_ExecuteAlways) - { - return false; - } - if (ld->args[1] != 0 && ld->args[1] != level.levelnum) - { - return false; - } - } + if (LineBlocksSight(ld)) return false; } sightcounts[3]++; diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 0eb6197234..b3ce7e3e31 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -466,7 +466,8 @@ static void DoSectorDamage(AActor *actor, sector_t *sec, int amount, FName type, return; } - P_DamageMobj (actor, NULL, NULL, amount, type); + int dflags = (flags & DAMAGE_NO_ARMOR) ? DMG_NO_ARMOR : 0; + P_DamageMobj (actor, NULL, NULL, amount, type, dflags); } void P_SectorDamage(int tag, int amount, FName type, PClassActor *protectClass, int flags) diff --git a/src/p_spec.h b/src/p_spec.h index c5e7b2f5c9..930ae51434 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -76,6 +76,7 @@ const double CARRYFACTOR = 3 / 32.; #define DAMAGE_NONPLAYERS 2 #define DAMAGE_IN_AIR 4 #define DAMAGE_SUBCLASSES_PROTECT 8 +#define DAMAGE_NO_ARMOR 16 // [RH] If a deathmatch game, checks to see if noexit is enabled. @@ -322,8 +323,15 @@ class DAnimatedDoor : public DMovingCeiling { DECLARE_CLASS (DAnimatedDoor, DMovingCeiling) public: + + enum EADType + { + adOpenClose, + adClose + }; + DAnimatedDoor (sector_t *sector); - DAnimatedDoor (sector_t *sec, line_t *line, int speed, int delay, FDoorAnimation *anim); + DAnimatedDoor (sector_t *sec, line_t *line, int speed, int delay, FDoorAnimation *anim, EADType type); void Serialize(FSerializer &arc); void Tick (); @@ -336,6 +344,7 @@ protected: int m_Timer; double m_BotDist; int m_Status; + int m_Type; enum { Opening, @@ -347,12 +356,12 @@ protected: int m_Delay; bool m_SetBlocking1, m_SetBlocking2; - friend bool EV_SlidingDoor (line_t *line, AActor *thing, int tag, int speed, int delay); + friend bool EV_SlidingDoor (line_t *line, AActor *thing, int tag, int speed, int delay, EADType type); private: DAnimatedDoor (); }; -bool EV_SlidingDoor (line_t *line, AActor *thing, int tag, int speed, int delay); +bool EV_SlidingDoor (line_t *line, AActor *thing, int tag, int speed, int delay, DAnimatedDoor::EADType type); // // P_CEILNG @@ -442,6 +451,7 @@ bool P_CreateCeiling(sector_t *sec, DCeiling::ECeiling type, line_t *line, int t bool EV_DoCeiling (DCeiling::ECeiling type, line_t *line, int tag, double speed, double speed2, double height, int crush, int silent, int change, DCeiling::ECrushMode hexencrush = DCeiling::ECrushMode::crushDoom); bool EV_CeilingCrushStop (int tag, bool remove); +bool EV_StopCeiling(int tag); void P_ActivateInStasisCeiling (int tag); @@ -555,6 +565,7 @@ bool EV_DoFloor(DFloor::EFloor floortype, line_t *line, int tag, double speed, double height, int crush, int change, bool hexencrush, bool hereticlower = false); bool EV_FloorCrushStop (int tag); +bool EV_StopFloor(int tag); bool EV_DoDonut (int tag, line_t *line, double pillarspeed, double slimespeed); class DElevator : public DMover diff --git a/src/p_states.cpp b/src/p_states.cpp index 3602cfc81e..d6f43aabe2 100644 --- a/src/p_states.cpp +++ b/src/p_states.cpp @@ -74,9 +74,9 @@ DEFINE_ACTION_FUNCTION(FState, GetSpriteTexture) } else { - sprframe = &SpriteFrames[sprites[skins[skin].sprite].spriteframes + self->GetFrame()]; - scalex = skins[skin].Scale.X; - scaley = skins[skin].Scale.Y; + sprframe = &SpriteFrames[sprites[Skins[skin].sprite].spriteframes + self->GetFrame()]; + scalex = Skins[skin].Scale.X; + scaley = Skins[skin].Scale.Y; } if (numret > 0) ret[0].SetInt(sprframe->Texture[rotation].GetIndex()); if (numret > 1) ret[1].SetInt(!!(sprframe->Flip & (1 << rotation))); diff --git a/src/p_switch.cpp b/src/p_switch.cpp index ae7cf21432..3e956f691b 100644 --- a/src/p_switch.cpp +++ b/src/p_switch.cpp @@ -69,7 +69,7 @@ public: bool bFlippable; bool bReturning; FSwitchDef *m_SwitchDef; - SDWORD m_Frame; + int32_t m_Frame; DWORD m_Timer; DVector2 m_Pos; diff --git a/src/p_things.cpp b/src/p_things.cpp index f6c06a1e5e..d4298f934b 100644 --- a/src/p_things.cpp +++ b/src/p_things.cpp @@ -406,7 +406,7 @@ void P_RemoveThing(AActor * actor) } -bool P_Thing_Raise(AActor *thing, AActor *raiser) +bool P_Thing_Raise(AActor *thing, AActor *raiser, int nocheck) { FState * RaiseState = thing->GetRaiseState(); if (RaiseState == NULL) @@ -426,7 +426,7 @@ bool P_Thing_Raise(AActor *thing, AActor *raiser) thing->flags |= MF_SOLID; thing->Height = info->Height; // [RH] Use real height thing->radius = info->radius; // [RH] Use real radius - if (!P_CheckPosition (thing, thing->Pos())) + if (!nocheck && !P_CheckPosition (thing, thing->Pos())) { thing->flags = oldflags; thing->radius = oldradius; diff --git a/src/p_trace.cpp b/src/p_trace.cpp index 3597535e55..818372e745 100644 --- a/src/p_trace.cpp +++ b/src/p_trace.cpp @@ -41,6 +41,7 @@ #include "r_defs.h" #include "p_spec.h" #include "g_levellocals.h" +#include "p_terrain.h" //========================================================================== // @@ -135,6 +136,13 @@ static void GetPortalTransition(DVector3 &pos, sector_t *&sec) } } +static bool isLiquid(F3DFloor *ff) +{ + if (ff->flags & FF_SWIMMABLE) return true; + auto terrain = ff->model->GetTerrain(ff->flags & FF_INVERTPLANES ? sector_t::floor : sector_t::ceiling); + return Terrains[terrain].IsLiquid && Terrains[terrain].Splash != -1; +} + //========================================================================== // // Trace entry point @@ -300,9 +308,9 @@ void FTraceInfo::Setup3DFloors() if (!(rover->flags&FF_EXISTS)) continue; - if (rover->flags&FF_SWIMMABLE && Results->Crossed3DWater == NULL) + if (Results->Crossed3DWater == NULL) { - if (Check3DFloorPlane(rover, false)) + if (Check3DFloorPlane(rover, false) && isLiquid(rover)) { // only consider if the plane is above the actual floor. if (rover->top.plane->ZatPoint(Results->HitPos) > bf) @@ -767,7 +775,7 @@ bool FTraceInfo::TraceTraverse (int ptflags) { for (auto rover : CurSector->e->XFloor.ffloors) { - if ((rover->flags & FF_EXISTS) && (rover->flags&FF_SWIMMABLE)) + if ((rover->flags & FF_EXISTS) && isLiquid(rover)) { if (Check3DFloorPlane(rover, false)) { diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index fc71160ae9..4d5e00e48a 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -518,7 +518,7 @@ public: th->Gravity = 1; th->RenderStyle = STYLE_Count; th->Alpha = -1; - th->health = 1; + th->Health = 1; th->FloatbobPhase = -1; sc.MustGetToken('{'); while (!sc.CheckToken('}')) @@ -746,7 +746,7 @@ public: break; case NAME_Health: - th->health = CheckInt(key); + th->Health = CheckFloat(key); break; case NAME_Score: @@ -1393,7 +1393,7 @@ public: if (isTranslated) sec->special = P_TranslateSectorSpecial(sec->special); else if (namespc == NAME_Hexen) { - if (sec->special < 0 || sec->special > 255 || !HexenSectorSpecialOk[sec->special]) + if (sec->special < 0 || sec->special > 140 || !HexenSectorSpecialOk[sec->special]) sec->special = 0; // NULL all unknown specials } continue; @@ -1658,6 +1658,10 @@ public: sec->planes[sector_t::ceiling].GlowHeight = (float)CheckFloat(key); break; + case NAME_Noattack: + Flag(sec->Flags, SECF_NOATTACK, key); + break; + case NAME_MoreIds: // delay parsing of the tag string until parsing of the sector is complete // This ensures that the ID is always the first tag in the list. diff --git a/src/p_usdf.cpp b/src/p_usdf.cpp index f4ebe5ebd3..3d2c2e010f 100644 --- a/src/p_usdf.cpp +++ b/src/p_usdf.cpp @@ -121,7 +121,6 @@ class USDFParser : public UDMFParserBase bool ParseChoice(FStrifeDialogueReply **&replyptr) { FStrifeDialogueReply *reply = new FStrifeDialogueReply; - memset(reply, 0, sizeof(*reply)); reply->Next = *replyptr; *replyptr = reply; @@ -193,10 +192,15 @@ class USDFParser : public UDMFParserBase case NAME_Special: reply->ActionSpecial = CheckInt(key); - if (reply->ActionSpecial < 0 || reply->ActionSpecial > 255) + if (reply->ActionSpecial < 0) reply->ActionSpecial = 0; break; + case NAME_SpecialName: + if (namespace_bits == Zd) + reply->ActionSpecial = P_FindLineSpecial(CheckString(key)); + break; + case NAME_Arg0: case NAME_Arg1: case NAME_Arg2: @@ -232,20 +236,21 @@ class USDFParser : public UDMFParserBase // Todo: Finalize if (reply->ItemCheck.Size() > 0) { - if (reply->ItemCheck[0].Amount <= 0) reply->NeedsGold = false; + reply->PrintAmount = reply->ItemCheck[0].Amount; + if (reply->PrintAmount <= 0) reply->NeedsGold = false; } - reply->Reply = ncopystring(ReplyString); - reply->QuickYes = ncopystring(QuickYes); + reply->Reply = ReplyString; + reply->QuickYes = QuickYes; if (reply->ItemCheck.Size() > 0 && reply->ItemCheck[0].Item != NULL) { - reply->QuickNo = ncopystring(QuickNo); + reply->QuickNo = QuickNo; } else { - reply->QuickNo = NULL; + reply->QuickNo = ""; } - reply->LogString = ncopystring(LogString); + reply->LogString = LogString; if(!closeDialog) reply->NextNode *= -1; return true; } @@ -292,8 +297,6 @@ class USDFParser : public UDMFParserBase { FStrifeDialogueNode *node = new FStrifeDialogueNode; FStrifeDialogueReply **replyptr = &node->Children; - memset(node, 0, sizeof(*node)); - //node->ItemCheckCount[0] = node->ItemCheckCount[1] = node->ItemCheckCount[2] = -1; node->ThisNodeNum = StrifeDialogues.Push(node); node->ItemCheckNode = -1; @@ -315,7 +318,14 @@ class USDFParser : public UDMFParserBase break; case NAME_Panel: - node->Backdrop = TexMan.CheckForTexture (CheckString(key), FTexture::TEX_MiscPatch); + node->Backdrop = CheckString(key); + break; + + case NAME_Userstring: + if (namespace_bits == Zd) + { + node->UserData = CheckString(key); + } break; case NAME_Voice: @@ -373,9 +383,9 @@ class USDFParser : public UDMFParserBase } } } - node->SpeakerName = ncopystring(SpeakerName); - node->Dialogue = ncopystring(Dialogue); - node->Goodbye = ncopystring(Goodbye); + node->SpeakerName = SpeakerName; + node->Dialogue = Dialogue; + node->Goodbye = Goodbye; return true; } @@ -390,6 +400,7 @@ class USDFParser : public UDMFParserBase { PClassActor *type = NULL; int dlgid = -1; + FName clsid; unsigned int startpos = StrifeDialogues.Size(); while (!sc.CheckToken('}')) @@ -414,6 +425,13 @@ class USDFParser : public UDMFParserBase dlgid = CheckInt(key); } break; + + case NAME_Class: + if (namespace_bits == Zd) + { + clsid = CheckString(key); + } + break; } } else @@ -439,6 +457,7 @@ class USDFParser : public UDMFParserBase for(;startpos < StrifeDialogues.Size(); startpos++) { StrifeDialogues[startpos]->SpeakerType = type; + StrifeDialogues[startpos]->MenuClassName = clsid; } return true; } diff --git a/src/p_user.cpp b/src/p_user.cpp index eea26cc4b1..9b65b559f8 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -88,6 +88,13 @@ CUSTOM_CVAR(Float, cl_predict_lerpthreshold, 2.00f, CVAR_ARCHIVE | CVAR_GLOBALCO ColorSetList ColorSets; PainFlashList PainFlashes; +// [Nash] FOV cvar setting +CUSTOM_CVAR(Float, fov, 90.f, CVAR_ARCHIVE | CVAR_USERINFO | CVAR_NOINITCALL) +{ + player_t *p = &players[consoleplayer]; + p->SetFOV(fov); +} + struct PredictPos { int gametic; @@ -163,6 +170,13 @@ FString GetPrintableDisplayName(PClassActor *cls) return cls->DisplayName; } +DEFINE_ACTION_FUNCTION(APlayerPawn, GetPrintableDisplayName) +{ + PARAM_PROLOGUE; + PARAM_CLASS(type, AActor); + ACTION_RETURN_STRING(type->DisplayName); +} + bool ValidatePlayerClass(PClassActor *ti, const char *name) { if (ti == NULL) @@ -543,6 +557,40 @@ int player_t::GetSpawnClass() return static_cast(GetDefaultByType(type))->SpawnMask; } +// [Nash] Set FOV +void player_t::SetFOV(float fov) +{ + player_t *p = &players[consoleplayer]; + if (p != nullptr && p->mo != nullptr) + { + if (dmflags & DF_NO_FOV) + { + if (consoleplayer == Net_Arbitrator) + { + Net_WriteByte(DEM_MYFOV); + } + else + { + Printf("A setting controller has disabled FOV changes.\n"); + return; + } + } + else + { + Net_WriteByte(DEM_MYFOV); + } + Net_WriteByte((BYTE)clamp(fov, 5.f, 179.f)); + } +} + +DEFINE_ACTION_FUNCTION(_PlayerInfo, SetFOV) +{ + PARAM_SELF_STRUCT_PROLOGUE(player_t); + PARAM_FLOAT(fov); + self->SetFOV((float)fov); + return 0; +} + //=========================================================================== // // EnumColorsets @@ -577,6 +625,14 @@ void EnumColorSets(PClassActor *cls, TArray *out) qsort(&(*out)[0], out->Size(), sizeof(int), intcmp); } +DEFINE_ACTION_FUNCTION(FPlayerClass, EnumColorSets) +{ + PARAM_SELF_STRUCT_PROLOGUE(FPlayerClass); + PARAM_POINTER(out, TArray); + EnumColorSets(self->Type, out); + return 0; +} + //========================================================================== // // @@ -596,6 +652,14 @@ FPlayerColorSet *GetColorSet(PClassActor *cls, int setnum) return nullptr; } +DEFINE_ACTION_FUNCTION(FPlayerClass, GetColorSetName) +{ + PARAM_SELF_STRUCT_PROLOGUE(FPlayerClass); + PARAM_INT(setnum); + auto p = GetColorSet(self->Type, setnum); + ACTION_RETURN_INT(p ? p->Name.GetIndex() : 0); +} + //========================================================================== // // @@ -654,6 +718,49 @@ DEFINE_ACTION_FUNCTION(_PlayerInfo, GetNeverSwitch) ACTION_RETURN_BOOL(self->userinfo.GetNeverSwitch()); } +DEFINE_ACTION_FUNCTION(_PlayerInfo, GetColor) +{ + PARAM_SELF_STRUCT_PROLOGUE(player_t); + ACTION_RETURN_INT(self->userinfo.GetColor()); +} + +DEFINE_ACTION_FUNCTION(_PlayerInfo, GetColorSet) +{ + PARAM_SELF_STRUCT_PROLOGUE(player_t); + ACTION_RETURN_INT(self->userinfo.GetColorSet()); +} + +DEFINE_ACTION_FUNCTION(_PlayerInfo, GetPlayerClassNum) +{ + PARAM_SELF_STRUCT_PROLOGUE(player_t); + ACTION_RETURN_INT(self->userinfo.GetPlayerClassNum()); +} + +DEFINE_ACTION_FUNCTION(_PlayerInfo, GetSkin) +{ + PARAM_SELF_STRUCT_PROLOGUE(player_t); + ACTION_RETURN_INT(self->userinfo.GetSkin()); +} + +DEFINE_ACTION_FUNCTION(_PlayerInfo, GetGender) +{ + PARAM_SELF_STRUCT_PROLOGUE(player_t); + ACTION_RETURN_INT(self->userinfo.GetGender()); +} + +DEFINE_ACTION_FUNCTION(_PlayerInfo, GetAutoaim) +{ + PARAM_SELF_STRUCT_PROLOGUE(player_t); + ACTION_RETURN_FLOAT(self->userinfo.GetAutoaim()); +} + +DEFINE_ACTION_FUNCTION(_PlayerInfo, GetTeam) +{ + PARAM_SELF_STRUCT_PROLOGUE(player_t); + ACTION_RETURN_INT(self->userinfo.GetTeam()); +} + + //=========================================================================== // // APlayerPawn @@ -674,6 +781,7 @@ void APlayerPawn::Serialize(FSerializer &arc) arc("jumpz", JumpZ, def->JumpZ) ("maxhealth", MaxHealth, def->MaxHealth) + ("bonushealth", BonusHealth, def->BonusHealth) ("runhealth", RunHealth, def->RunHealth) ("spawnmask", SpawnMask, def->SpawnMask) ("forwardmove1", ForwardMove1, def->ForwardMove1) @@ -1227,9 +1335,9 @@ const char *APlayerPawn::GetSoundClass() const if (player != NULL && (player->mo == NULL || !(player->mo->flags4 &MF4_NOSKIN)) && (unsigned int)player->userinfo.GetSkin() >= PlayerClasses.Size () && - (size_t)player->userinfo.GetSkin() < numskins) + (unsigned)player->userinfo.GetSkin() < Skins.Size()) { - return skins[player->userinfo.GetSkin()].name; + return Skins[player->userinfo.GetSkin()].Name.GetChars(); } return SoundClass != NAME_None? SoundClass.GetChars() : "player"; @@ -1243,15 +1351,18 @@ const char *APlayerPawn::GetSoundClass() const // //=========================================================================== -int APlayerPawn::GetMaxHealth() const +int APlayerPawn::GetMaxHealth(bool withupgrades) const { - return MaxHealth > 0? MaxHealth : ((i_compatflags&COMPATF_DEHHEALTH)? 100 : deh.MaxHealth); + int ret = MaxHealth > 0? MaxHealth : ((i_compatflags&COMPATF_DEHHEALTH)? 100 : deh.MaxHealth); + if (withupgrades) ret += stamina + BonusHealth; + return ret; } DEFINE_ACTION_FUNCTION(APlayerPawn, GetMaxHealth) { PARAM_SELF_PROLOGUE(APlayerPawn); - ACTION_RETURN_INT(self->GetMaxHealth()); + PARAM_BOOL_DEF(withupgrades); + ACTION_RETURN_INT(self->GetMaxHealth(withupgrades)); } //=========================================================================== @@ -1781,8 +1892,8 @@ void P_CheckPlayerSprite(AActor *actor, int &spritenum, DVector2 &scale) { // Convert from default scale to skin scale. DVector2 defscale = actor->GetDefault()->Scale; - scale.X *= skins[player->userinfo.GetSkin()].Scale.X / defscale.X; - scale.Y *= skins[player->userinfo.GetSkin()].Scale.Y / defscale.Y; + scale.X *= Skins[player->userinfo.GetSkin()].Scale.X / defscale.X; + scale.Y *= Skins[player->userinfo.GetSkin()].Scale.Y / defscale.Y; } // Set the crouch sprite? @@ -1793,10 +1904,10 @@ void P_CheckPlayerSprite(AActor *actor, int &spritenum, DVector2 &scale) crouchspriteno = player->mo->crouchsprite; } else if (!(actor->flags4 & MF4_NOSKIN) && - (spritenum == skins[player->userinfo.GetSkin()].sprite || - spritenum == skins[player->userinfo.GetSkin()].crouchsprite)) + (spritenum == Skins[player->userinfo.GetSkin()].sprite || + spritenum == Skins[player->userinfo.GetSkin()].crouchsprite)) { - crouchspriteno = skins[player->userinfo.GetSkin()].crouchsprite; + crouchspriteno = Skins[player->userinfo.GetSkin()].crouchsprite; } else { // no sprite -> squash the existing one @@ -2623,9 +2734,19 @@ void P_PlayerThink (player_t *player) else if (level.IsJumpingAllowed() && player->onground && player->jumpTics == 0) { double jumpvelz = player->mo->JumpZ * 35 / TICRATE; + double jumpfac = 0; // [BC] If the player has the high jump power, double his jump velocity. - if ( player->cheats & CF_HIGHJUMP ) jumpvelz *= 2; + // (actually, pick the best factors from all active items.) + for (auto p = player->mo->Inventory; p != nullptr; p = p->Inventory) + { + if (p->IsKindOf(NAME_PowerHighJump)) + { + double f = p->FloatVar(NAME_Strength); + if (f > jumpfac) jumpfac = f; + } + } + if (jumpfac > 0) jumpvelz *= jumpfac; player->mo->Vel.Z += jumpvelz; player->mo->flags2 &= ~MF2_ONMOBJ; @@ -2752,7 +2873,7 @@ void P_PlayerThink (player_t *player) // Apply degeneration. if (dmflags2 & DF2_YES_DEGENERATION) { - int maxhealth = player->mo->GetMaxHealth() + player->mo->stamina; + int maxhealth = player->mo->GetMaxHealth(true); if ((level.time % TICRATE) == 0 && player->health > maxhealth) { if (player->health - 5 < maxhealth) @@ -3225,6 +3346,7 @@ bool P_IsPlayerTotallyFrozen(const player_t *player) DEFINE_FIELD(APlayerPawn, crouchsprite) DEFINE_FIELD(APlayerPawn, MaxHealth) +DEFINE_FIELD(APlayerPawn, BonusHealth) DEFINE_FIELD(APlayerPawn, MugShotMaxHealth) DEFINE_FIELD(APlayerPawn, RunHealth) DEFINE_FIELD(APlayerPawn, PlayerFlags) @@ -3249,19 +3371,14 @@ DEFINE_FIELD(APlayerPawn, FlechetteType) DEFINE_FIELD(APlayerPawn, DamageFade) DEFINE_FIELD(APlayerPawn, ViewBob) DEFINE_FIELD(APlayerPawn, FullHeight) - -DEFINE_FIELD(APlayerPawn, HealingRadiusType) DEFINE_FIELD(APlayerPawn, SoundClass) DEFINE_FIELD(APlayerPawn, Face) DEFINE_FIELD(APlayerPawn, Portrait) DEFINE_FIELD(APlayerPawn, Slot) -DEFINE_FIELD(APlayerPawn, InvulMode) DEFINE_FIELD(APlayerPawn, HexenArmor) DEFINE_FIELD(APlayerPawn, ColorRangeStart) DEFINE_FIELD(APlayerPawn, ColorRangeEnd) -DEFINE_FIELD(PClassActor, DisplayName) - DEFINE_FIELD_X(PlayerInfo, player_t, mo) DEFINE_FIELD_X(PlayerInfo, player_t, playerstate) DEFINE_FIELD_X(PlayerInfo, player_t, original_oldbuttons) diff --git a/src/po_man.cpp b/src/po_man.cpp index c2c8f5a7da..ebe39cccae 100644 --- a/src/po_man.cpp +++ b/src/po_man.cpp @@ -147,7 +147,7 @@ polyspawns_t *polyspawns; // [RH] Let P_SpawnMapThings() find our thingies for u // PRIVATE DATA DEFINITIONS ------------------------------------------------ -static TArray KnownPolySides; +static TArray KnownPolySides; static FPolyNode *FreePolyNodes; // CODE -------------------------------------------------------------------- diff --git a/src/posix/sdl/i_input.cpp b/src/posix/sdl/i_input.cpp index 759da062cd..57ea03d928 100644 --- a/src/posix/sdl/i_input.cpp +++ b/src/posix/sdl/i_input.cpp @@ -395,9 +395,10 @@ void MessagePump (const SDL_Event &sev) { event.type = EV_GUI_Event; event.subtype = sev.type == SDL_KEYDOWN ? EV_GUI_KeyDown : EV_GUI_KeyUp; - event.data3 = ((sev.key.keysym.mod & KMOD_SHIFT) ? GKM_SHIFT : 0) | - ((sev.key.keysym.mod & KMOD_CTRL) ? GKM_CTRL : 0) | - ((sev.key.keysym.mod & KMOD_ALT) ? GKM_ALT : 0); + SDL_Keymod kmod = SDL_GetModState(); + event.data3 = ((kmod & KMOD_SHIFT) ? GKM_SHIFT : 0) | + ((kmod & KMOD_CTRL) ? GKM_CTRL : 0) | + ((kmod & KMOD_ALT) ? GKM_ALT : 0); if (event.subtype == EV_GUI_KeyDown) { @@ -458,6 +459,7 @@ void MessagePump (const SDL_Event &sev) event.type = EV_GUI_Event; event.subtype = EV_GUI_Char; event.data1 = sev.text.text[0]; + event.data2 = !!(SDL_GetModState() & KMOD_ALT); D_PostEvent (&event); } break; diff --git a/src/r_data/r_translate.cpp b/src/r_data/r_translate.cpp index 0f4d58e6de..fd1bf5d215 100644 --- a/src/r_data/r_translate.cpp +++ b/src/r_data/r_translate.cpp @@ -1171,7 +1171,7 @@ void R_BuildPlayerTranslation (int player) D_GetPlayerColor (player, &h, &s, &v, &colorset); R_CreatePlayerTranslation (h, s, v, colorset, - &skins[players[player].userinfo.GetSkin()], + &Skins[players[player].userinfo.GetSkin()], translationtables[TRANSLATION_Players][player], translationtables[TRANSLATION_PlayersExtra][player], translationtables[TRANSLATION_RainPillar][player] @@ -1218,9 +1218,9 @@ DEFINE_ACTION_FUNCTION(_Translation, SetPlayerTranslation) if (cls != nullptr) { - PlayerSkin = R_FindSkin(skins[PlayerSkin].name, int(cls - &PlayerClasses[0])); + PlayerSkin = R_FindSkin(Skins[PlayerSkin].Name, int(cls - &PlayerClasses[0])); R_GetPlayerTranslation(PlayerColor, GetColorSet(cls->Type, PlayerColorset), - &skins[PlayerSkin], translationtables[tgroup][tnum]); + &Skins[PlayerSkin], translationtables[tgroup][tnum]); } ACTION_RETURN_BOOL(true); } diff --git a/src/r_data/sprites.cpp b/src/r_data/sprites.cpp index c3f73760db..d6d6aa4e11 100644 --- a/src/r_data/sprites.cpp +++ b/src/r_data/sprites.cpp @@ -30,8 +30,7 @@ struct spriteframewithrotate : public spriteframe_t // [RH] skin globals -FPlayerSkin *skins; -size_t numskins; +TArray Skins; BYTE OtherGameSkinRemap[256]; PalEntry OtherGameSkinPalette[256]; @@ -512,7 +511,7 @@ void R_InitSkins (void) int sndlumps[NUMSKINSOUNDS]; char key[65]; DWORD intname, crouchname; - size_t i; + unsigned i; int j, k, base; int lastlump; int aliasid; @@ -538,7 +537,7 @@ void R_InitSkins (void) i++; for (j = 0; j < NUMSKINSOUNDS; j++) sndlumps[j] = -1; - skins[i].namespc = Wads.GetLumpNamespace (base); + Skins[i].namespc = Wads.GetLumpNamespace (base); FScanner sc(base); intname = 0; @@ -560,14 +559,13 @@ void R_InitSkins (void) sc.GetString (); if (0 == stricmp (key, "name")) { - strncpy (skins[i].name, sc.String, 16); - for (j = 0; (size_t)j < i; j++) + Skins[i].Name = sc.String; + for (j = 0; (unsigned)j < i; j++) { - if (stricmp (skins[i].name, skins[j].name) == 0) + if (Skins[i].Name.CompareNoCase(Skins[j].Name) == 0) { - mysnprintf (skins[i].name, countof(skins[i].name), "skin%d", (int)i); - Printf (PRINT_BOLD, "Skin %s duplicated as %s\n", - skins[j].name, skins[i].name); + Skins[i].Name.Format("skin%u", i); + Printf (PRINT_BOLD, "Skin %s duplicated as %s\n", Skins[j].Name.GetChars(), Skins[i].Name.GetChars()); break; } } @@ -586,18 +584,16 @@ void R_InitSkins (void) } else if (0 == stricmp (key, "face")) { - for (j = 2; j >= 0; j--) - skins[i].face[j] = toupper (sc.String[j]); - skins[i].face[3] = '\0'; + Skins[i].Face = FString(sc.String, 3); } else if (0 == stricmp (key, "gender")) { - skins[i].gender = D_GenderToInt (sc.String); + Skins[i].gender = D_GenderToInt (sc.String); } else if (0 == stricmp (key, "scale")) { - skins[i].Scale.X = clamp(atof (sc.String), 1./65536, 256.); - skins[i].Scale.Y = skins[i].Scale.X; + Skins[i].Scale.X = clamp(atof (sc.String), 1./65536, 256.); + Skins[i].Scale.Y = Skins[i].Scale.X; } else if (0 == stricmp (key, "game")) { @@ -615,7 +611,7 @@ void R_InitSkins (void) if (gameinfo.gametype & GAME_DoomChex) { transtype = PClass::FindActor(NAME_HereticPlayer); - skins[i].othergame = true; + Skins[i].othergame = true; } else if (gameinfo.gametype != GAME_Heretic) { @@ -634,7 +630,7 @@ void R_InitSkins (void) if (gameinfo.gametype == GAME_Heretic) { transtype = PClass::FindActor(NAME_DoomPlayer); - skins[i].othergame = true; + Skins[i].othergame = true; } else if (!(gameinfo.gametype & GAME_DoomChex)) { @@ -659,7 +655,7 @@ void R_InitSkins (void) } else if (key[0] == '*') { // Player sound replacment (ZDoom extension) - int lump = Wads.CheckNumForName (sc.String, skins[i].namespc); + int lump = Wads.CheckNumForName (sc.String, Skins[i].namespc); if (lump == -1) { lump = Wads.CheckNumForFullName (sc.String, true, ns_sounds); @@ -668,11 +664,11 @@ void R_InitSkins (void) { if (stricmp (key, "*pain") == 0) { // Replace all pain sounds in one go - aliasid = S_AddPlayerSound (skins[i].name, skins[i].gender, + aliasid = S_AddPlayerSound (Skins[i].Name, Skins[i].gender, playersoundrefs[0], lump, true); for (int l = 3; l > 0; --l) { - S_AddPlayerSoundExisting (skins[i].name, skins[i].gender, + S_AddPlayerSoundExisting (Skins[i].Name, Skins[i].gender, playersoundrefs[l], aliasid, true); } } @@ -681,7 +677,7 @@ void R_InitSkins (void) int sndref = S_FindSoundNoHash (key); if (sndref != 0) { - S_AddPlayerSound (skins[i].name, skins[i].gender, sndref, lump, true); + S_AddPlayerSound (Skins[i].Name, Skins[i].gender, sndref, lump, true); } } } @@ -692,7 +688,7 @@ void R_InitSkins (void) { if (stricmp (key, skinsoundnames[j][0]) == 0) { - sndlumps[j] = Wads.CheckNumForName (sc.String, skins[i].namespc); + sndlumps[j] = Wads.CheckNumForName (sc.String, Skins[i].namespc); if (sndlumps[j] == -1) { // Replacement not found, try finding it in the global namespace sndlumps[j] = Wads.CheckNumForFullName (sc.String, true, ns_sounds); @@ -715,7 +711,7 @@ void R_InitSkins (void) { basetype = PClass::FindActor(NAME_HereticPlayer); transtype = PClass::FindActor(NAME_DoomPlayer); - skins[i].othergame = true; + Skins[i].othergame = true; } else { @@ -728,8 +724,8 @@ void R_InitSkins (void) auto transdef = ((APlayerPawn*)GetDefaultByType(transtype)); auto basedef = ((APlayerPawn*)GetDefaultByType(basetype)); - skins[i].range0start = transdef->ColorRangeStart; - skins[i].range0end = transdef->ColorRangeEnd; + Skins[i].range0start = transdef->ColorRangeStart; + Skins[i].range0end = transdef->ColorRangeEnd; remove = true; for (j = 0; j < (int)PlayerClasses.Size (); j++) @@ -750,8 +746,8 @@ void R_InitSkins (void) if (!remove) { - if (skins[i].name[0] == 0) - mysnprintf (skins[i].name, countof(skins[i].name), "skin%d", (int)i); + if (Skins[i].Name.IsEmpty()) + Skins[i].Name.Format("skin%u", i); // Now collect the sprite frames for this skin. If the sprite name was not // specified, use whatever immediately follows the specifier lump. @@ -783,7 +779,7 @@ void R_InitSkins (void) } else { - skins[i].crouchsprite = -1; + Skins[i].crouchsprite = -1; break; } } @@ -806,7 +802,7 @@ void R_InitSkins (void) if (spr == 0 && maxframe <= 0) { - Printf (PRINT_BOLD, "Skin %s (#%d) has no frames. Removing.\n", skins[i].name, (int)i); + Printf (PRINT_BOLD, "Skin %s (#%u) has no frames. Removing.\n", Skins[i].Name.GetChars(), i); remove = true; break; } @@ -814,16 +810,18 @@ void R_InitSkins (void) Wads.GetLumpName (temp.name, base+1); temp.name[4] = 0; int sprno = (int)sprites.Push (temp); - if (spr==0) skins[i].sprite = sprno; - else skins[i].crouchsprite = sprno; + if (spr==0) Skins[i].sprite = sprno; + else Skins[i].crouchsprite = sprno; R_InstallSprite (sprno, sprtemp, maxframe); } } if (remove) { - if (i < numskins-1) - memmove (&skins[i], &skins[i+1], sizeof(skins[0])*(numskins-i-1)); + if (i < Skins.Size() - 1) + { + Skins.Delete(i); + } i--; continue; } @@ -836,25 +834,25 @@ void R_InitSkins (void) { if (j == 0 || sndlumps[j] != sndlumps[j-1]) { - aliasid = S_AddPlayerSound (skins[i].name, skins[i].gender, + aliasid = S_AddPlayerSound (Skins[i].Name, Skins[i].gender, playersoundrefs[j], sndlumps[j], true); } else { - S_AddPlayerSoundExisting (skins[i].name, skins[i].gender, + S_AddPlayerSoundExisting (Skins[i].Name, Skins[i].gender, playersoundrefs[j], aliasid, true); } } } // Make sure face prefix is a full 3 chars - if (skins[i].face[1] == 0 || skins[i].face[2] == 0) + if (Skins[i].Face.Len() < 3) { - skins[i].face[0] = 0; + Skins[i].Face = ""; } } - if (numskins > PlayerClasses.Size ()) + if (Skins.Size() > PlayerClasses.Size ()) { // The sound table may have changed, so rehash it. S_HashSounds (); S_ShrinkPlayerSoundLists (); @@ -869,9 +867,9 @@ int R_FindSkin (const char *name, int pclass) return pclass; } - for (unsigned i = PlayerClasses.Size(); i < numskins; i++) + for (unsigned i = PlayerClasses.Size(); i < Skins.Size(); i++) { - if (strnicmp (skins[i].name, name, 16) == 0) + if (Skins[i].Name.CompareNoCase(name) == 0) { if (PlayerClasses[pclass].CheckSkin (i)) return i; @@ -887,8 +885,8 @@ CCMD (skins) { int i; - for (i = PlayerClasses.Size ()-1; i < (int)numskins; i++) - Printf ("% 3d %s\n", i-PlayerClasses.Size ()+1, skins[i].name); + for (i = PlayerClasses.Size() - 1; i < (int)Skins.Size(); i++) + Printf("% 3d %s\n", i - PlayerClasses.Size() + 1, Skins[i].Name.GetChars()); } @@ -914,6 +912,7 @@ void R_InitSprites () { int lump, lastlump; unsigned int i, j; + unsigned numskins; // [RH] Create a standard translation to map skins between Heretic and Doom if (gameinfo.gametype == GAME_DoomChex) @@ -934,15 +933,15 @@ void R_InitSprites () } // [RH] Do some preliminary setup - if (skins != NULL) delete [] skins; - skins = new FPlayerSkin[numskins]; - memset (skins, 0, sizeof(*skins) * numskins); + Skins.Clear(); + Skins.Resize(numskins); + for (i = 0; i < numskins; i++) { // Assume Doom skin by default auto type = ((APlayerPawn*)GetDefaultByType(PlayerClasses[0].Type)); - skins[i].range0start = type->ColorRangeStart; - skins[i].range0end = type->ColorRangeEnd; - skins[i].Scale = type->Scale; + Skins[i].range0start = type->ColorRangeStart; + Skins[i].range0end = type->ColorRangeEnd; + Skins[i].Scale = type->Scale; } R_InitSpriteDefs (); @@ -956,33 +955,30 @@ void R_InitSprites () { auto basetype = ((APlayerPawn*)GetDefaultByType(PlayerClasses[i].Type)); - strcpy (skins[i].name, "Base"); + Skins[i].Name = "Base"; if (basetype->Face == NAME_None) { - skins[i].face[0] = 'S'; - skins[i].face[1] = 'T'; - skins[i].face[2] = 'F'; - skins[i].face[3] = '\0'; + Skins[i].Face = "STF"; } else { - strcpy(skins[i].face, basetype->Face); + Skins[i].Face = basetype->Face; } - skins[i].range0start = basetype->ColorRangeStart; - skins[i].range0end = basetype->ColorRangeEnd; - skins[i].Scale = basetype->Scale; - skins[i].sprite = basetype->SpawnState->sprite; - skins[i].namespc = ns_global; + Skins[i].range0start = basetype->ColorRangeStart; + Skins[i].range0end = basetype->ColorRangeEnd; + Skins[i].Scale = basetype->Scale; + Skins[i].sprite = basetype->SpawnState->sprite; + Skins[i].namespc = ns_global; PlayerClasses[i].Skins.Push (i); - if (memcmp (sprites[skins[i].sprite].name, "PLAY", 4) == 0) + if (memcmp (sprites[Skins[i].sprite].name, "PLAY", 4) == 0) { for (j = 0; j < sprites.Size (); j++) { if (memcmp (sprites[j].name, deh.PlayerSprite, 4) == 0) { - skins[i].sprite = (int)j; + Skins[i].sprite = (int)j; break; } } @@ -995,12 +991,14 @@ void R_InitSprites () gl_InitModels(); } -void R_DeinitSpriteData() -{ - // Free skins - if (skins != NULL) - { - delete[] skins; - skins = NULL; - } -} + +DEFINE_FIELD_NAMED(FPlayerSkin, Name, SkinName); +DEFINE_FIELD(FPlayerSkin, Face); +DEFINE_FIELD(FPlayerSkin, gender); +DEFINE_FIELD(FPlayerSkin, range0start); +DEFINE_FIELD(FPlayerSkin, range0end); +DEFINE_FIELD(FPlayerSkin, othergame); +DEFINE_FIELD(FPlayerSkin, Scale); +DEFINE_FIELD(FPlayerSkin, sprite); +DEFINE_FIELD(FPlayerSkin, crouchsprite); +DEFINE_FIELD(FPlayerSkin, namespc); diff --git a/src/r_data/sprites.h b/src/r_data/sprites.h index d882ee9810..9e7d9392ec 100644 --- a/src/r_data/sprites.h +++ b/src/r_data/sprites.h @@ -47,25 +47,23 @@ extern TArray SpriteFrames; class FPlayerSkin { public: - char name[17]; // 16 chars + NULL - char face[4]; // 3 chars ([MH] + NULL so can use as a C string) - BYTE gender; // This skin's gender (not really used) - BYTE range0start; - BYTE range0end; - bool othergame; // [GRB] - DVector2 Scale; - int sprite; - int crouchsprite; - int namespc; // namespace for this skin + FString Name; + FString Face; + BYTE gender = 0; // This skin's gender (not really used) + BYTE range0start = 0; + BYTE range0end = 0; + bool othergame = 0; // [GRB] + DVector2 Scale = { 1, 1 }; + int sprite = 0; + int crouchsprite = 0; + int namespc = 0; // namespace for this skin }; -extern size_t numskins; // [RH] -extern FPlayerSkin * skins; // [RH] +extern TArray Skins; extern BYTE OtherGameSkinRemap[256]; extern PalEntry OtherGameSkinPalette[256]; void R_InitSprites (); -void R_DeinitSpriteData (); #endif diff --git a/src/r_defs.h b/src/r_defs.h index 08c27bfd39..abf43fdf15 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -494,6 +494,7 @@ enum SECF_ENDGODMODE = 256, // getting damaged by this sector ends god mode SECF_ENDLEVEL = 512, // ends level when health goes below 10 SECF_HAZARD = 1024, // Change to Strife's delayed damage handling. + SECF_NOATTACK = 2048, // monsters cannot start attacks in this sector. SECF_WASSECRET = 1 << 30, // a secret that was discovered SECF_SECRET = 1 << 31, // a secret sector diff --git a/src/resourcefiles/file_zip.cpp b/src/resourcefiles/file_zip.cpp index 3ae7088230..a48a17ca25 100644 --- a/src/resourcefiles/file_zip.cpp +++ b/src/resourcefiles/file_zip.cpp @@ -151,7 +151,7 @@ static DWORD Zip_FindCentralDir(FileReader * fin) if (fin->Seek(uReadPos, SEEK_SET) != 0) break; - if (fin->Read(buf, (SDWORD)uReadSize) != (SDWORD)uReadSize) break; + if (fin->Read(buf, (int32_t)uReadSize) != (int32_t)uReadSize) break; for (i = (int)uReadSize - 3; (i--) > 0;) { @@ -217,12 +217,88 @@ bool FZipFile::Open(bool quiet) char *dirptr = (char*)directory; FZipLump *lump_p = Lumps; + + FString name0; + bool foundspeciallump = false; + + // Check if all files have the same prefix so that this can be stripped out. + // This will only be done if there is either a MAPINFO, ZMAPINFO or GAMEINFO lump in the subdirectory, denoting a ZDoom mod. + if (NumLumps > 1) for (DWORD i = 0; i < NumLumps; i++) + { + FZipCentralDirectoryInfo *zip_fh = (FZipCentralDirectoryInfo *)dirptr; + + int len = LittleShort(zip_fh->NameLength); + FString name(dirptr + sizeof(FZipCentralDirectoryInfo), len); + + dirptr += sizeof(FZipCentralDirectoryInfo) + + LittleShort(zip_fh->NameLength) + + LittleShort(zip_fh->ExtraLength) + + LittleShort(zip_fh->CommentLength); + + if (dirptr > ((char*)directory) + dirsize) // This directory entry goes beyond the end of the file. + { + free(directory); + if (!quiet) Printf(TEXTCOLOR_RED "\n%s: Central directory corrupted.", Filename); + return false; + } + + name.ToLower(); + if (i == 0) + { + // check for special names, if one of these gets found this must be treated as a normal zip. + bool isspecial = !name.Compare("flats/") || + name.IndexOf("/") < 0 || + !name.Compare("textures/") || + !name.Compare("hires/") || + !name.Compare("sprites/") || + !name.Compare("voxels/") || + !name.Compare("colormaps/") || + !name.Compare("acs/") || + !name.Compare("maps/") || + !name.Compare("voices/") || + !name.Compare("patches/") || + !name.Compare("graphics/") || + !name.Compare("sounds/") || + !name.Compare("music/"); + if (isspecial) break; + name0 = name; + } + else + { + if (name.IndexOf(name0) != 0) + { + name0 = ""; + break; + } + else if (!foundspeciallump) + { + // at least one of the more common definition lumps must be present. + if (name.IndexOf(name0 + "mapinfo") == 0) foundspeciallump = true; + else if (name.IndexOf(name0 + "zmapinfo") == 0) foundspeciallump = true; + else if (name.IndexOf(name0 + "gameinfo") == 0) foundspeciallump = true; + else if (name.IndexOf(name0 + "sndinfo") == 0) foundspeciallump = true; + else if (name.IndexOf(name0 + "sbarinfo") == 0) foundspeciallump = true; + else if (name.IndexOf(name0 + "menudef") == 0) foundspeciallump = true; + else if (name.IndexOf(name0 + "gldefs") == 0) foundspeciallump = true; + else if (name.IndexOf(name0 + "animdefs") == 0) foundspeciallump = true; + else if (name.IndexOf(name0 + "decorate.") == 0) foundspeciallump = true; // DECORATE is a common subdirectory name, so the check needs to be a bit different. + else if (name.Compare(name0 + "decorate") == 0) foundspeciallump = true; + else if (name.IndexOf(name0 + "zscript.") == 0) foundspeciallump = true; // same here. + else if (name.Compare(name0 + "zscript") == 0) foundspeciallump = true; + else if (name.Compare(name0 + "maps/") == 0) foundspeciallump = true; + } + } + } + + dirptr = (char*)directory; + lump_p = Lumps; for (DWORD i = 0; i < NumLumps; i++) { FZipCentralDirectoryInfo *zip_fh = (FZipCentralDirectoryInfo *)dirptr; int len = LittleShort(zip_fh->NameLength); FString name(dirptr + sizeof(FZipCentralDirectoryInfo), len); + if (name0.IsNotEmpty()) name = name.Mid(name0.Len()); dirptr += sizeof(FZipCentralDirectoryInfo) + LittleShort(zip_fh->NameLength) + LittleShort(zip_fh->ExtraLength) + diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index 952a40d09f..687a27a433 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -167,7 +167,7 @@ struct FBloodSFX int Pitch; // pitch change int PitchRange; // range of random pitch DWORD Format; // format of audio 1=11025 5=22050 - SDWORD LoopStart; // loop position (-1 means no looping) + int32_t LoopStart; // loop position (-1 means no looping) char RawName[9]; // name of RAW resource }; diff --git a/src/s_sndseq.cpp b/src/s_sndseq.cpp index 6506f303bc..af3f3617b3 100644 --- a/src/s_sndseq.cpp +++ b/src/s_sndseq.cpp @@ -34,8 +34,8 @@ // MACROS ------------------------------------------------------------------ #define GetCommand(a) ((a) & 255) -#define GetData(a) (SDWORD(a) >> 8 ) -#define GetFloatData(a) float((SDWORD(a) >> 8 )/65536.f) +#define GetData(a) (int32_t(a) >> 8 ) +#define GetFloatData(a) float((int32_t(a) >> 8 )/65536.f) #define MakeCommand(a,b) ((a) | ((b) << 8)) #define HexenPlatSeq(a) (a) #define HexenDoorSeq(a) ((a) | 0x40) @@ -1390,7 +1390,7 @@ void SN_StopAllSequences (void) // //========================================================================== -ptrdiff_t SN_GetSequenceOffset (int sequence, SDWORD *sequencePtr) +ptrdiff_t SN_GetSequenceOffset (int sequence, int32_t *sequencePtr) { return sequencePtr - Sequences[sequence]->Script; } diff --git a/src/s_sndseq.h b/src/s_sndseq.h index a35fab026d..e77df22719 100644 --- a/src/s_sndseq.h +++ b/src/s_sndseq.h @@ -44,7 +44,7 @@ protected: DSeqNode (); DSeqNode (int sequence, int modenum); - SDWORD *m_SequencePtr; + int32_t *m_SequencePtr; int m_Sequence; FSoundID m_CurrentSoundID; @@ -74,7 +74,7 @@ struct FSoundSequence FName SeqName; FName Slot; FSoundID StopSound; - SDWORD Script[1]; // + more until end of sequence script + int32_t Script[1]; // + more until end of sequence script }; void S_ParseSndSeq (int levellump); @@ -93,7 +93,7 @@ void SN_StopSequence (FPolyObj *poly); bool SN_AreModesSame(int sequence, seqtype_t type, int mode1, int mode2); bool SN_AreModesSame(FName name, int mode1, int mode2); void SN_UpdateActiveSequences (void); -ptrdiff_t SN_GetSequenceOffset (int sequence, SDWORD *sequencePtr); +ptrdiff_t SN_GetSequenceOffset (int sequence, int32_t *sequencePtr); void SN_DoStop (void *); void SN_ChangeNodeData (int nodeNum, int seqOffset, int delayTics, float volume, int currentSoundID); diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 5c0ae69e27..89fc13365a 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -1256,7 +1256,7 @@ DEFINE_ACTION_FUNCTION(DObject, S_Sound) PARAM_INT(channel); PARAM_FLOAT_DEF(volume); PARAM_FLOAT_DEF(attn); - S_Sound(channel, id, volume, attn); + S_Sound(channel, id, static_cast(volume), static_cast(attn)); return 0; } @@ -1400,7 +1400,7 @@ sfxinfo_t *S_LoadSound(sfxinfo_t *sfx) FWadLump wlump = Wads.OpenLumpNum(sfx->lumpnum); BYTE *sfxdata = new BYTE[size]; wlump.Read(sfxdata, size); - SDWORD dmxlen = LittleLong(((SDWORD *)sfxdata)[1]); + int32_t dmxlen = LittleLong(((int32_t *)sfxdata)[1]); std::pair snd; // If the sound is voc, use the custom loader. @@ -1460,7 +1460,7 @@ static void S_LoadSound3D(sfxinfo_t *sfx) FWadLump wlump = Wads.OpenLumpNum(sfx->lumpnum); BYTE *sfxdata = new BYTE[size]; wlump.Read(sfxdata, size); - SDWORD dmxlen = LittleLong(((SDWORD *)sfxdata)[1]); + int32_t dmxlen = LittleLong(((int32_t *)sfxdata)[1]); std::pair snd; // If the sound is voc, use the custom loader. diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index dca4fdbf16..1359df59a3 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -1562,7 +1562,7 @@ FxExpression *FxTypeCast::Resolve(FCompileContext &ctx) SAFE_RESOLVE(basex, ctx); // first deal with the simple types - if (ValueType == TypeError || basex->ValueType == TypeError) + if (ValueType == TypeError || basex->ValueType == TypeError || basex->ValueType == nullptr) { ScriptPosition.Message(MSG_ERROR, "Trying to cast to invalid type."); delete this; @@ -6142,10 +6142,81 @@ FxExpression *FxMemberIdentifier::Resolve(FCompileContext& ctx) if (Object->ExprType == EFX_Identifier) { + auto id = static_cast(Object)->Identifier; // If the left side is a class name for a static member function call it needs to be resolved manually // because the resulting value type would cause problems in nearly every other place where identifiers are being used. - ccls = FindStructType(static_cast(Object)->Identifier, ctx); - if (ccls != nullptr) static_cast(Object)->noglobal = true; + ccls = FindStructType(id, ctx); + if (ccls != nullptr) + { + static_cast(Object)->noglobal = true; + } + else + { + PType *type; + // Another special case to deal with here is constants assigned to non-struct types. The code below cannot deal with them so it needs to be done here explicitly. + // Thanks to the messed up search logic of the type system, which doesn't allow any search by type name for the basic types at all, + // we have to do this manually, though and check for all types that may have values attached explicitly. + // (What's the point of attached fields to types if you cannot even search for the types...???) + switch (id) + { + default: + type = nullptr; + break; + + case NAME_Byte: + case NAME_uint8: + type = TypeUInt8; + break; + + case NAME_sByte: + case NAME_int8: + type = TypeSInt8; + break; + + case NAME_uShort: + case NAME_uint16: + type = TypeUInt16; + break; + + case NAME_Short: + case NAME_int16: + type = TypeSInt16; + break; + + case NAME_Int: + type = TypeSInt32; + break; + + case NAME_uInt: + type = TypeUInt32; + break; + + case NAME_Float: + type = TypeFloat32; + break; + + case NAME_Double: + type = TypeFloat64; + break; + } + if (type != nullptr) + { + auto sym = type->Symbols.FindSymbol(Identifier, true); + if (sym != nullptr) + { + // non-struct symbols must be constant numbers and can only be defined internally. + assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolConstNumeric))); + auto sn = static_cast(sym); + + VMValue vmv; + if (sn->ValueType->IsKindOf(RUNTIME_CLASS(PInt))) vmv = sn->Value; + else vmv = sn->Float; + auto x = new FxConstant(sn->ValueType, vmv, ScriptPosition); + delete this; + return x->Resolve(ctx); + } + } + } } SAFE_RESOLVE(Object, ctx); @@ -6420,7 +6491,7 @@ ExpEmit FxClassDefaults::Emit(VMFunctionBuilder *build) ExpEmit ob = obj->Emit(build); ob.Free(build); ExpEmit meta(build, REGT_POINTER); - build->Emit(OP_META, meta.RegNum, ob.RegNum); + build->Emit(OP_CLSS, meta.RegNum, ob.RegNum); build->Emit(OP_LOS, meta.RegNum, meta.RegNum, build->GetConstantInt(myoffsetof(PClass, Defaults))); return meta; @@ -6571,7 +6642,7 @@ ExpEmit FxCVar::Emit(VMFunctionBuilder *build) case CVAR_String: build->Emit(OP_LKP, addr.RegNum, build->GetConstantAddress(&static_cast(CVar)->Value, ATAG_GENERIC)); - build->Emit(OP_LS, dest.RegNum, addr.RegNum, nul); + build->Emit(OP_LCS, dest.RegNum, addr.RegNum, nul); break; case CVAR_DummyBool: @@ -9013,7 +9084,7 @@ ExpEmit FxGetClass::Emit(VMFunctionBuilder *build) ExpEmit op = Self->Emit(build); op.Free(build); ExpEmit to(build, REGT_POINTER); - build->Emit(OP_META, to.RegNum, op.RegNum); + build->Emit(OP_CLSS, to.RegNum, op.RegNum); return to; } @@ -9054,7 +9125,7 @@ ExpEmit FxGetParentClass::Emit(VMFunctionBuilder *build) if (Self->IsObject()) { ExpEmit to(build, REGT_POINTER); - build->Emit(OP_META, to.RegNum, op.RegNum); + build->Emit(OP_CLSS, to.RegNum, op.RegNum); op = to; op.Free(build); } diff --git a/src/scripting/backend/vmbuilder.cpp b/src/scripting/backend/vmbuilder.cpp index 74f845248c..3be1000c59 100644 --- a/src/scripting/backend/vmbuilder.cpp +++ b/src/scripting/backend/vmbuilder.cpp @@ -846,6 +846,7 @@ void FFunctionBuildList::Build() { int errorcount = 0; int codesize = 0; + int datasize = 0; FILE *dump = nullptr; if (Args->CheckParm("-dumpdisasm")) dump = fopen("disasm.txt", "w"); @@ -927,6 +928,8 @@ void FFunctionBuildList::Build() { DumpFunction(dump, sfunc, item.PrintableName.GetChars(), (int)item.PrintableName.Len()); codesize += sfunc->CodeSize; + datasize += sfunc->LineInfoCount * sizeof(FStatementInfo) + sfunc->ExtraSpace + sfunc->NumKonstD * sizeof(int) + + sfunc->NumKonstA * sizeof(void*) + sfunc->NumKonstF * sizeof(double) + sfunc->NumKonstS * sizeof(FString); } sfunc->Unsafe = ctx.Unsafe; } @@ -944,10 +947,11 @@ void FFunctionBuildList::Build() } if (dump != nullptr) { - fprintf(dump, "\n*************************************************************************\n%i code bytes\n", codesize * 4); + fprintf(dump, "\n*************************************************************************\n%i code bytes\n%i data bytes", codesize * 4, datasize); fclose(dump); } FScriptPosition::StrictErrors = false; mItems.Clear(); + mItems.ShrinkToFit(); FxAlloc.FreeAllBlocks(); } \ No newline at end of file diff --git a/src/scripting/decorate/olddecorations.cpp b/src/scripting/decorate/olddecorations.cpp index 50e8f9c6a8..e656445fc0 100644 --- a/src/scripting/decorate/olddecorations.cpp +++ b/src/scripting/decorate/olddecorations.cpp @@ -223,7 +223,7 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def, PNamespace *ns) { extra.DeathHeight = ((AActor*)(type->Defaults))->Height; } - type->DeathHeight = extra.DeathHeight; + ((AActor*)(type->Defaults))->FloatVar("DeathHeight") = extra.DeathHeight; } bag.statedef.SetStateLabel("Death", &type->OwnedStates[extra.DeathStart]); } @@ -262,7 +262,7 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def, PNamespace *ns) } if (extra.BurnHeight == 0) extra.BurnHeight = ((AActor*)(type->Defaults))->Height; - type->BurnHeight = extra.BurnHeight; + ((AActor*)(type->Defaults))->FloatVar("BurnHeight") = extra.BurnHeight; bag.statedef.SetStateLabel("Burn", &type->OwnedStates[extra.FireDeathStart]); } @@ -445,18 +445,18 @@ static void ParseInsideDecoration (Baggage &bag, AActor *defaults, else if (def == DEF_Projectile && sc.Compare ("ExplosionRadius")) { sc.MustGetNumber (); - bag.Info->ExplosionRadius = sc.Number; + defaults->IntVar(NAME_ExplosionRadius) = sc.Number; extra.bExplosive = true; } else if (def == DEF_Projectile && sc.Compare ("ExplosionDamage")) { sc.MustGetNumber (); - bag.Info->ExplosionDamage = sc.Number; + defaults->IntVar(NAME_ExplosionDamage) = sc.Number; extra.bExplosive = true; } else if (def == DEF_Projectile && sc.Compare ("DoNotHurtShooter")) { - bag.Info->DontHurtShooter = true; + defaults->BoolVar(NAME_DontHurtShooter) = true; } else if (def == DEF_Projectile && sc.Compare ("Damage")) { @@ -483,7 +483,7 @@ static void ParseInsideDecoration (Baggage &bag, AActor *defaults, else if (sc.Compare ("Mass")) { sc.MustGetFloat (); - defaults->Mass = SDWORD(sc.Float); + defaults->Mass = int32_t(sc.Float); } else if (sc.Compare ("Translation1")) { @@ -541,11 +541,11 @@ static void ParseInsideDecoration (Baggage &bag, AActor *defaults, else if (def == DEF_Pickup && sc.Compare ("PickupMessage")) { sc.MustGetString (); - bag.Info->PickupMsg = sc.String; + inv->StringVar(NAME_PickupMsg) = sc.String; } else if (def == DEF_Pickup && sc.Compare ("Respawns")) { - inv->BoolVar("Respawnable") = true; + inv->BoolVar(NAME_Respawnable) = true; } else if (def == DEF_BreakableDecoration && sc.Compare ("SolidOnDeath")) { diff --git a/src/scripting/decorate/thingdef_parse.cpp b/src/scripting/decorate/thingdef_parse.cpp index 991577f68e..72cac7cde7 100644 --- a/src/scripting/decorate/thingdef_parse.cpp +++ b/src/scripting/decorate/thingdef_parse.cpp @@ -52,7 +52,6 @@ #include "backend/codegen.h" #include "w_wad.h" #include "v_video.h" -#include "version.h" #include "v_text.h" #include "m_argv.h" @@ -828,7 +827,7 @@ static void DispatchScriptProperty(FScanner &sc, PProperty *prop, AActor *defaul if (i > 0) sc.MustGetStringName(","); if (f->Flags & VARF_Meta) { - addr = ((char*)bag.Info) + f->Offset; + addr = ((char*)bag.Info->Meta) + f->Offset; } else { @@ -868,7 +867,7 @@ static void DispatchScriptProperty(FScanner &sc, PProperty *prop, AActor *defaul else if (f->Type->IsKindOf(RUNTIME_CLASS(PString))) { sc.MustGetString(); - *(FString*)addr = sc.String; + *(FString*)addr = strbin1(sc.String); } else if (f->Type->IsKindOf(RUNTIME_CLASS(PClassPointer))) { diff --git a/src/scripting/decorate/thingdef_states.cpp b/src/scripting/decorate/thingdef_states.cpp index 63269eeeb5..b3d4c904a2 100644 --- a/src/scripting/decorate/thingdef_states.cpp +++ b/src/scripting/decorate/thingdef_states.cpp @@ -54,7 +54,6 @@ #include "i_system.h" #include "colormatcher.h" #include "backend/codegen.h" -#include "version.h" #include "templates.h" #include "backend/vmbuilder.h" @@ -316,9 +315,7 @@ do_stop: do { sc.MustGetString(); - #ifdef DYNLIGHT - AddStateLight(&state, sc.String); - #endif + AddStateLight(&state, sc.String); } while (sc.CheckString(",")); sc.MustGetStringName(")"); diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index f257c7c2d6..c50b7f65bf 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -57,10 +57,16 @@ #include "v_video.h" #include "c_bind.h" #include "menu/menu.h" +#include "teaminfo.h" +#include "r_data/sprites.h" +#include "serializer.h" static TArray properties; static TArray AFTable; static TArray FieldTable; +extern int BackbuttonTime; +extern float BackbuttonAlpha; +static AWeapon *wpnochg; //========================================================================== // @@ -311,6 +317,9 @@ static FFlagDef ActorFlagDefs[]= DEFINE_FLAG(MF7, SPRITEANGLE, AActor, flags7), DEFINE_FLAG(MF7, SMASHABLE, AActor, flags7), DEFINE_FLAG(MF7, NOSHIELDREFLECT, AActor, flags7), + DEFINE_FLAG(MF7, FORCEZERORADIUSDMG, AActor, flags7), + DEFINE_FLAG(MF7, NOINFIGHTSPECIES, AActor, flags7), + DEFINE_FLAG(MF7, FORCEINFIGHTING, AActor, flags7), // Effect flags DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects), @@ -331,6 +340,7 @@ static FFlagDef ActorFlagDefs[]= DEFINE_FLAG(RF, XFLIP, AActor, renderflags), DEFINE_FLAG(RF, YFLIP, AActor, renderflags), DEFINE_FLAG(RF, INTERPOLATEANGLES, AActor, renderflags), + DEFINE_FLAG(RF, DONTINTERPOLATE, AActor, renderflags), // Bounce flags DEFINE_FLAG2(BOUNCE_Walls, BOUNCEONWALLS, AActor, BounceFlags), @@ -347,6 +357,7 @@ static FFlagDef ActorFlagDefs[]= DEFINE_FLAG2(BOUNCE_MBF, MBFBOUNCER, AActor, BounceFlags), DEFINE_FLAG2(BOUNCE_AutoOffFloorOnly, BOUNCEAUTOOFFFLOORONLY, AActor, BounceFlags), DEFINE_FLAG2(BOUNCE_UseBounceState, USEBOUNCESTATE, AActor, BounceFlags), + DEFINE_FLAG2(BOUNCE_NotOnShootables, DONTBOUNCEONSHOOTABLES, AActor, BounceFlags), }; // These won't be accessible through bitfield variables @@ -420,6 +431,8 @@ static FFlagDef InventoryFlagDefs[] = DEFINE_FLAG(IF, TRANSFER, AInventory, ItemFlags), DEFINE_FLAG(IF, NOTELEPORTFREEZE, AInventory, ItemFlags), DEFINE_FLAG(IF, NOSCREENBLINK, AInventory, ItemFlags), + DEFINE_FLAG(IF, ISARMOR, AInventory, ItemFlags), + DEFINE_FLAG(IF, ISHEALTH, AInventory, ItemFlags), DEFINE_DUMMY_FLAG(FORCERESPAWNINSURVIVAL, false), @@ -729,7 +742,7 @@ static int fieldcmp(const void * a, const void * b) void InitThingdef() { // Create all global variables here because this cannot be done on the script side and really isn't worth adding support for. - // Also create all special fields here that cannot be declared by script syntax. + // Also create all special fields here that cannot be declared by script syntax plus the pointer serializers. Doing all these with class overrides would be a bit messy. auto secplanestruct = NewNativeStruct("Secplane", nullptr); secplanestruct->Size = sizeof(secplane_t); @@ -738,18 +751,62 @@ void InitThingdef() auto sectorstruct = NewNativeStruct("Sector", nullptr); sectorstruct->Size = sizeof(sector_t); sectorstruct->Align = alignof(sector_t); + NewPointer(sectorstruct, false)->InstallHandlers( + [](FSerializer &ar, const char *key, const void *addr) + { + ar(key, *(sector_t **)addr); + }, + [](FSerializer &ar, const char *key, void *addr) + { + Serialize(ar, key, *(sector_t **)addr, nullptr); + return true; + } + ); auto linestruct = NewNativeStruct("Line", nullptr); linestruct->Size = sizeof(line_t); linestruct->Align = alignof(line_t); + NewPointer(linestruct, false)->InstallHandlers( + [](FSerializer &ar, const char *key, const void *addr) + { + ar(key, *(line_t **)addr); + }, + [](FSerializer &ar, const char *key, void *addr) + { + Serialize(ar, key, *(line_t **)addr, nullptr); + return true; + } + ); auto sidestruct = NewNativeStruct("Side", nullptr); sidestruct->Size = sizeof(side_t); sidestruct->Align = alignof(side_t); + NewPointer(sidestruct, false)->InstallHandlers( + [](FSerializer &ar, const char *key, const void *addr) + { + ar(key, *(side_t **)addr); + }, + [](FSerializer &ar, const char *key, void *addr) + { + Serialize(ar, key, *(side_t **)addr, nullptr); + return true; + } + ); auto vertstruct = NewNativeStruct("Vertex", nullptr); vertstruct->Size = sizeof(vertex_t); vertstruct->Align = alignof(vertex_t); + NewPointer(vertstruct, false)->InstallHandlers( + [](FSerializer &ar, const char *key, const void *addr) + { + ar(key, *(vertex_t **)addr); + }, + [](FSerializer &ar, const char *key, void *addr) + { + Serialize(ar, key, *(vertex_t **)addr, nullptr); + return true; + } + ); auto sectorportalstruct = NewNativeStruct("SectorPortal", nullptr); sectorportalstruct->Size = sizeof(FSectorPortal); @@ -759,6 +816,44 @@ void InitThingdef() playerclassstruct->Size = sizeof(FPlayerClass); playerclassstruct->Align = alignof(FPlayerClass); + auto playerskinstruct = NewNativeStruct("PlayerSkin", nullptr); + playerskinstruct->Size = sizeof(FPlayerSkin); + playerskinstruct->Align = alignof(FPlayerSkin); + + auto teamstruct = NewNativeStruct("Team", nullptr); + teamstruct->Size = sizeof(FTeam); + teamstruct->Align = alignof(FTeam); + + PStruct *pstruct = NewNativeStruct("PlayerInfo", nullptr); + pstruct->Size = sizeof(player_t); + pstruct->Align = alignof(player_t); + NewPointer(pstruct, false)->InstallHandlers( + [](FSerializer &ar, const char *key, const void *addr) + { + ar(key, *(player_t **)addr); + }, + [](FSerializer &ar, const char *key, void *addr) + { + Serialize(ar, key, *(player_t **)addr, nullptr); + return true; + } + ); + + auto fontstruct = NewNativeStruct("FFont", nullptr); + fontstruct->Size = sizeof(FFont); + fontstruct->Align = alignof(FFont); + NewPointer(fontstruct, false)->InstallHandlers( + [](FSerializer &ar, const char *key, const void *addr) + { + ar(key, *(FFont **)addr); + }, + [](FSerializer &ar, const char *key, void *addr) + { + Serialize(ar, key, *(FFont **)addr, nullptr); + return true; + } + ); + // set up the lines array in the sector struct. This is a bit messy because the type system is not prepared to handle a pointer to an array of pointers to a native struct even remotely well... // As a result, the size has to be set to something large and arbritrary because it can change between maps. This will need some serious improvement when things get cleaned up. sectorstruct->AddNativeField("lines", NewPointer(NewResizableArray(NewPointer(linestruct, false)), false), myoffsetof(sector_t, Lines), VARF_Native); @@ -798,6 +893,14 @@ void InitThingdef() PField *plrclsf = new PField("PlayerClasses", plrcls, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&PlayerClasses); Namespaces.GlobalNamespace->Symbols.AddSymbol(plrclsf); + auto plrskn = NewPointer(NewResizableArray(playerskinstruct), false); + PField *plrsknf = new PField("PlayerSkins", plrskn, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&Skins); + Namespaces.GlobalNamespace->Symbols.AddSymbol(plrsknf); + + auto teamst = NewPointer(NewResizableArray(teamstruct), false); + PField *teamf = new PField("Teams", teamst, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&Teams); + Namespaces.GlobalNamespace->Symbols.AddSymbol(teamf); + auto bindcls = NewNativeStruct("KeyBindings", nullptr); PField *binding = new PField("Bindings", bindcls, VARF_Native | VARF_Static, (intptr_t)&Bindings); Namespaces.GlobalNamespace->Symbols.AddSymbol(binding); @@ -815,9 +918,6 @@ void InitThingdef() Namespaces.GlobalNamespace->Symbols.AddSymbol(gi); // set up a variable for the global players array. - PStruct *pstruct = NewNativeStruct("PlayerInfo", nullptr); - pstruct->Size = sizeof(player_t); - pstruct->Align = alignof(player_t); PArray *parray = NewArray(pstruct, MAXPLAYERS); PField *fieldptr = new PField("players", parray, VARF_Native | VARF_Static, (intptr_t)&players); Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr); @@ -829,7 +929,10 @@ void InitThingdef() fieldptr = new PField("playeringame", parray, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&playeringame); Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr); - fieldptr = new PField("gameaction", TypeUInt8, VARF_Native | VARF_Static, (intptr_t)&gameaction); + fieldptr = new PField("gameaction", TypeUInt32, VARF_Native | VARF_Static, (intptr_t)&gameaction); + Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr); + + fieldptr = new PField("gamestate", TypeSInt32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&gamestate); Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr); fieldptr = new PField("skyflatnum", TypeTextureID, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&skyflatnum); @@ -888,11 +991,23 @@ void InitThingdef() fieldptr = new PField("OptionMenuSettings", NewStruct("FOptionMenuSettings", nullptr), VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&OptionSettings); Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr); - + fieldptr = new PField("gametic", TypeSInt32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&gametic); + Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr); + + fieldptr = new PField("demoplayback", TypeBool, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&demoplayback); + Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr); + + fieldptr = new PField("BackbuttonTime", TypeSInt32, VARF_Native | VARF_Static, (intptr_t)&BackbuttonTime); + Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr); + + fieldptr = new PField("BackbuttonAlpha", TypeFloat32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&BackbuttonAlpha); + Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr); + + // Argh. It sucks when bad hacks need to be supported. WP_NOCHANGE is just a bogus pointer but it used everywhere as a special flag. // It cannot be defined as constant because constants can either be numbers or strings but nothing else, so the only 'solution' // is to create a static variable from it and reference that in the script. Yuck!!! - static AWeapon *wpnochg = WP_NOCHANGE; + wpnochg = WP_NOCHANGE; fieldptr = new PField("WP_NOCHANGE", NewPointer(RUNTIME_CLASS(AWeapon), false), VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&wpnochg); Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr); @@ -1195,10 +1310,20 @@ DEFINE_ACTION_FUNCTION(FStringStruct, Mid) ACTION_RETURN_STRING(s); } -DEFINE_ACTION_FUNCTION(FStringStruct, Len) +DEFINE_ACTION_FUNCTION(FStringStruct, Left) { PARAM_SELF_STRUCT_PROLOGUE(FString); - ACTION_RETURN_INT((int)self->Len()); + PARAM_UINT(len); + FString s = self->Left(len); + ACTION_RETURN_STRING(s); +} + +DEFINE_ACTION_FUNCTION(FStringStruct, Truncate) +{ + PARAM_SELF_STRUCT_PROLOGUE(FString); + PARAM_UINT(len); + self->Truncate(len); + return 0; } // CharAt and CharCodeAt is how JS does it, and JS is similar here in that it doesn't have char type as int. @@ -1221,3 +1346,10 @@ DEFINE_ACTION_FUNCTION(FStringStruct, CharCodeAt) ACTION_RETURN_INT(0); ACTION_RETURN_INT((*self)[pos]); } + +DEFINE_ACTION_FUNCTION(FStringStruct, Filter) +{ + PARAM_SELF_STRUCT_PROLOGUE(FString); + ACTION_RETURN_STRING(strbin1(*self)); +} + diff --git a/src/scripting/thingdef_properties.cpp b/src/scripting/thingdef_properties.cpp index 50b81310ca..6212f0ff1d 100644 --- a/src/scripting/thingdef_properties.cpp +++ b/src/scripting/thingdef_properties.cpp @@ -592,24 +592,13 @@ DEFINE_PROPERTY(health, I, Actor) defaults->health=id; } -//========================================================================== -// -//========================================================================== -DEFINE_PROPERTY(gibhealth, I, Actor) -{ - PROP_INT_PARM(id, 0); - assert(info->IsKindOf(RUNTIME_CLASS(PClassActor))); - static_cast(info)->GibHealth = id; -} - //========================================================================== // //========================================================================== DEFINE_PROPERTY(woundhealth, I, Actor) { PROP_INT_PARM(id, 0); - assert(info->IsKindOf(RUNTIME_CLASS(PClassActor))); - static_cast(info)->WoundHealth = id; + defaults->WoundHealth = id; } //========================================================================== @@ -888,16 +877,6 @@ DEFINE_PROPERTY(activesound, S, Actor) defaults->ActiveSound = str; } -//========================================================================== -// -//========================================================================== -DEFINE_PROPERTY(howlsound, S, Actor) -{ - PROP_STRING_PARM(str, 0); - assert(info->IsKindOf(RUNTIME_CLASS(PClassActor))); - static_cast(info)->HowlSound = str; -} - //========================================================================== // //========================================================================== @@ -981,75 +960,6 @@ DEFINE_PROPERTY(alpha, F, Actor) defaults->Alpha = id; } -//========================================================================== -// -//========================================================================== -DEFINE_PROPERTY(obituary, S, Actor) -{ - PROP_STRING_PARM(str, 0); - assert(info->IsKindOf(RUNTIME_CLASS(PClassActor))); - static_cast(info)->Obituary = str; -} - -//========================================================================== -// -//========================================================================== -DEFINE_PROPERTY(hitobituary, S, Actor) -{ - PROP_STRING_PARM(str, 0); - assert(info->IsKindOf(RUNTIME_CLASS(PClassActor))); - static_cast(info)->HitObituary = str; -} - -//========================================================================== -// -//========================================================================== -DEFINE_PROPERTY(donthurtshooter, 0, Actor) -{ - assert(info->IsKindOf(RUNTIME_CLASS(PClassActor))); - static_cast(info)->DontHurtShooter = true; -} - -//========================================================================== -// -//========================================================================== -DEFINE_PROPERTY(explosionradius, I, Actor) -{ - PROP_INT_PARM(id, 0); - assert(info->IsKindOf(RUNTIME_CLASS(PClassActor))); - static_cast(info)->ExplosionRadius = id; -} - -//========================================================================== -// -//========================================================================== -DEFINE_PROPERTY(explosiondamage, I, Actor) -{ - PROP_INT_PARM(id, 0); - assert(info->IsKindOf(RUNTIME_CLASS(PClassActor))); - static_cast(info)->ExplosionDamage = id; -} - -//========================================================================== -// -//========================================================================== -DEFINE_PROPERTY(deathheight, F, Actor) -{ - PROP_DOUBLE_PARM(h, 0); - assert(info->IsKindOf(RUNTIME_CLASS(PClassActor))); - static_cast(info)->DeathHeight = MAX(0., h); -} - -//========================================================================== -// -//========================================================================== -DEFINE_PROPERTY(burnheight, F, Actor) -{ - PROP_DOUBLE_PARM(h, 0); - assert(info->IsKindOf(RUNTIME_CLASS(PClassActor))); - static_cast(info)->BurnHeight = MAX(0., h); -} - //========================================================================== // //========================================================================== @@ -1068,16 +978,6 @@ DEFINE_PROPERTY(meleethreshold, F, Actor) defaults->meleethreshold = id; } -//========================================================================== -// -//========================================================================== -DEFINE_PROPERTY(meleedamage, I, Actor) -{ - PROP_INT_PARM(id, 0); - assert(info->IsKindOf(RUNTIME_CLASS(PClassActor))); - static_cast(info)->MeleeDamage = id; -} - //========================================================================== // //========================================================================== @@ -1087,36 +987,6 @@ DEFINE_PROPERTY(meleerange, F, Actor) defaults->meleerange = id; } -//========================================================================== -// -//========================================================================== -DEFINE_PROPERTY(meleesound, S, Actor) -{ - PROP_STRING_PARM(str, 0); - assert(info->IsKindOf(RUNTIME_CLASS(PClassActor))); - static_cast(info)->MeleeSound = str; -} - -//========================================================================== -// -//========================================================================== -DEFINE_PROPERTY(missiletype, S, Actor) -{ - PROP_STRING_PARM(str, 0); - assert(info->IsKindOf(RUNTIME_CLASS(PClassActor))); - static_cast(info)->MissileName = str; -} - -//========================================================================== -// -//========================================================================== -DEFINE_PROPERTY(missileheight, F, Actor) -{ - PROP_DOUBLE_PARM(id, 0); - assert(info->IsKindOf(RUNTIME_CLASS(PClassActor))); - static_cast(info)->MissileHeight = id; -} - //========================================================================== // //========================================================================== @@ -1183,13 +1053,11 @@ DEFINE_PROPERTY(bloodcolor, C, Actor) { PROP_COLOR_PARM(color, 0); - PalEntry pe = color; - pe.a = CreateBloodTranslation(pe); - assert(info->IsKindOf(RUNTIME_CLASS(PClassActor))); - static_cast(info)->BloodColor = pe; + defaults->BloodColor = color; + defaults->BloodColor.a = 255; // a should not be 0. + defaults->BloodTranslation = TRANSLATION(TRANSLATION_Blood, CreateBloodTranslation(color)); } - //========================================================================== // //========================================================================== @@ -1199,26 +1067,23 @@ DEFINE_PROPERTY(bloodtype, Sss, Actor) PROP_STRING_PARM(str1, 1) PROP_STRING_PARM(str2, 2) - assert(info->IsKindOf(RUNTIME_CLASS(PClassActor))); - PClassActor *ainfo = static_cast(info); - FName blood = str; // normal blood - ainfo->BloodType = blood; + defaults->NameVar("BloodType") = blood; if (PROP_PARM_COUNT > 1) { blood = str1; } // blood splatter - ainfo->BloodType2 = blood; + defaults->NameVar("BloodType2") = blood; if (PROP_PARM_COUNT > 2) { blood = str2; } // axe blood - ainfo->BloodType3 = blood; + defaults->NameVar("BloodType3") = blood; } //========================================================================== @@ -1413,21 +1278,28 @@ DEFINE_PROPERTY(poisondamagetype, S, Actor) //========================================================================== // //========================================================================== -DEFINE_PROPERTY(fastspeed, F, Actor) +DEFINE_PROPERTY(radiusdamagefactor, F, Actor) { PROP_DOUBLE_PARM(i, 0); - assert(info->IsKindOf(RUNTIME_CLASS(PClassActor))); - static_cast(info)->FastSpeed = i; + defaults->RadiusDamageFactor = i; } //========================================================================== // //========================================================================== -DEFINE_PROPERTY(radiusdamagefactor, F, Actor) +DEFINE_PROPERTY(selfdamagefactor, F, Actor) { PROP_DOUBLE_PARM(i, 0); - assert(info->IsKindOf(RUNTIME_CLASS(PClassActor))); - static_cast(info)->RDFactor = i; + defaults->SelfDamageFactor = i; +} + +//========================================================================== +// +//========================================================================== +DEFINE_PROPERTY(stealthalpha, F, Actor) +{ + PROP_DOUBLE_PARM(i, 0); + defaults->StealthAlpha = i; } //========================================================================== @@ -1436,8 +1308,7 @@ DEFINE_PROPERTY(radiusdamagefactor, F, Actor) DEFINE_PROPERTY(cameraheight, F, Actor) { PROP_DOUBLE_PARM(i, 0); - assert(info->IsKindOf(RUNTIME_CLASS(PClassActor))); - static_cast(info)->CameraHeight = i; + defaults->CameraHeight = i; } //========================================================================== @@ -1811,16 +1682,6 @@ DEFINE_CLASS_PROPERTY(pickupflash, S, Inventory) defaults->PickupFlash = FindClassTentative(str, RUNTIME_CLASS(AActor)); } -//========================================================================== -// -//========================================================================== -DEFINE_CLASS_PROPERTY(pickupmessage, T, Inventory) -{ - PROP_STRING_PARM(str, 0); - assert(info->IsKindOf(RUNTIME_CLASS(PClassActor))); - static_cast(info)->PickupMsg = str; -} - //========================================================================== // //========================================================================== @@ -1855,15 +1716,6 @@ DEFINE_CLASS_PROPERTY(usesound, S, Inventory) defaults->UseSound = str; } -//========================================================================== -// -//========================================================================== -DEFINE_CLASS_PROPERTY(givequest, I, Inventory) -{ - PROP_INT_PARM(i, 0); - defaults->GiveQuest = i; -} - //========================================================================== // //========================================================================== @@ -2698,24 +2550,6 @@ DEFINE_CLASS_PROPERTY_PREFIX(player, startitem, S_i, PlayerPawn) bag.DropItemList = di; } -//========================================================================== -// -//========================================================================== -DEFINE_CLASS_PROPERTY_PREFIX(player, invulnerabilitymode, S, PlayerPawn) -{ - PROP_STRING_PARM(str, 0); - defaults->InvulMode = str; -} - -//========================================================================== -// -//========================================================================== -DEFINE_CLASS_PROPERTY_PREFIX(player, healradiustype, S, PlayerPawn) -{ - PROP_STRING_PARM(str, 0); - defaults->HealingRadiusType = str; -} - //========================================================================== // //========================================================================== diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index b13f17777e..06de1e2c10 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -12,6 +12,7 @@ // these are used in vmexec.h void FScopeBarrier_ValidateNew(PClass* cls, PFunction* callingfunc); void FScopeBarrier_ValidateCall(PFunction* calledfunc, PFunction* callingfunc, PClass* selftype); +class DObject; extern FMemArena ClassDataAllocator; @@ -1035,6 +1036,7 @@ void NullParam(const char *varname); #define PARAM_STATE_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); FState *x = (FState *)StateLabels.GetState(param[p].i, self->GetClass()); #define PARAM_STATE_ACTION_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); FState *x = (FState *)StateLabels.GetState(param[p].i, stateowner->GetClass()); #define PARAM_POINTER_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER); type *x = (type *)param[p].a; +#define PARAM_POINTERTYPE_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER); type x = (type )param[p].a; #define PARAM_OBJECT_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_OBJECT || param[p].a == NULL)); type *x = (type *)param[p].a; assert(x == NULL || x->IsKindOf(RUNTIME_CLASS(type))); #define PARAM_CLASS_AT(p,x,base) assert((p) < numparam); assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_OBJECT || param[p].a == NULL)); base::MetaClass *x = (base::MetaClass *)param[p].a; assert(x == NULL || x->IsDescendantOf(RUNTIME_CLASS(base))); #define PARAM_POINTER_NOT_NULL_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER); type *x = (type *)PARAM_NULLCHECK(param[p].a, #x); @@ -1079,6 +1081,7 @@ void NullParam(const char *varname); #define PARAM_STATE(x) ++paramnum; PARAM_STATE_AT(paramnum,x) #define PARAM_STATE_ACTION(x) ++paramnum; PARAM_STATE_ACTION_AT(paramnum,x) #define PARAM_POINTER(x,type) ++paramnum; PARAM_POINTER_AT(paramnum,x,type) +#define PARAM_POINTERTYPE(x,type) ++paramnum; PARAM_POINTERTYPE_AT(paramnum,x,type) #define PARAM_OBJECT(x,type) ++paramnum; PARAM_OBJECT_AT(paramnum,x,type) #define PARAM_CLASS(x,base) ++paramnum; PARAM_CLASS_AT(paramnum,x,base) #define PARAM_POINTER_NOT_NULL(x,type) ++paramnum; PARAM_POINTER_NOT_NULL_AT(paramnum,x,type) diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index 64ea0b7c39..b3d1b24260 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -109,12 +109,18 @@ begin: reg.atag[a] = ATAG_GENERIC; // using ATAG_FRAMEPOINTER will cause endless asserts. NEXTOP; - OP(META): + OP(CLSS): ASSERTA(a); ASSERTO(B); reg.a[a] = ((DObject*)reg.a[B])->GetClass(); // I wish this could be done without a special opcode but there's really no good way to guarantee initialization of the Class pointer... reg.atag[a] = ATAG_OBJECT; NEXTOP; + OP(META): + ASSERTA(a); ASSERTO(B); + reg.a[a] = ((DObject*)reg.a[B])->GetClass()->Meta; // I wish this could be done without a special opcode but there's really no good way to guarantee initialization of the Class pointer... + reg.atag[a] = ATAG_OBJECT; + NEXTOP; + OP(LB): ASSERTD(a); ASSERTA(B); ASSERTKD(C); GETADDR(PB,KC,X_READ_NIL); @@ -197,6 +203,16 @@ begin: GETADDR(PB,RC,X_READ_NIL); reg.s[a] = *(FString *)ptr; NEXTOP; + OP(LCS): + ASSERTS(a); ASSERTA(B); ASSERTKD(C); + GETADDR(PB,KC,X_READ_NIL); + reg.s[a] = *(const char **)ptr; + NEXTOP; + OP(LCS_R): + ASSERTS(a); ASSERTA(B); ASSERTD(C); + GETADDR(PB,RC,X_READ_NIL); + reg.s[a] = *(const char **)ptr; + NEXTOP; OP(LO): ASSERTA(a); ASSERTA(B); ASSERTKD(C); GETADDR(PB,KC,X_READ_NIL); diff --git a/src/scripting/vm/vmframe.cpp b/src/scripting/vm/vmframe.cpp index d5eaed5bd8..10fed9abb6 100644 --- a/src/scripting/vm/vmframe.cpp +++ b/src/scripting/vm/vmframe.cpp @@ -446,7 +446,8 @@ int VMFrameStack::Call(VMFunction *func, VMValue *params, int numparams, VMRetur { auto code = static_cast(func)->Code; // handle empty functions consisting of a single return explicitly so that empty virtual callbacks do not need to set up an entire VM frame. - if (code->word == 0x0080804e) + // code cann be null here in case of some non-fatal DECORATE errors. + if (code == nullptr || code->word == 0x0080804e) { return 0; } diff --git a/src/scripting/vm/vmops.h b/src/scripting/vm/vmops.h index 6f3931531b..3d503932aa 100644 --- a/src/scripting/vm/vmops.h +++ b/src/scripting/vm/vmops.h @@ -23,7 +23,8 @@ xx(LKF_R, lk, RFRII8, NOP, 0, 0), // load float constant indexed xx(LKS_R, lk, RSRII8, NOP, 0, 0), // load string constant indexed xx(LKP_R, lk, RPRII8, NOP, 0, 0), // load pointer constant indexed xx(LFP, lf, LFP, NOP, 0, 0), // load frame pointer -xx(META, meta, RPRP, NOP, 0, 0), // load a class's meta class address +xx(META, meta, RPRP, NOP, 0, 0), // load a class's meta data address +xx(CLSS, clss, RPRP, NOP, 0, 0), // load a class's descriptor address // Load from memory. rA = *(rB + rkC) xx(LB, lb, RIRPKI, LB_R, 4, REGT_INT), // load byte @@ -52,6 +53,8 @@ xx(LV2, lv2, RVRPKI, LV2_R, 4, REGT_INT), // load vector2 xx(LV2_R, lv2, RVRPRI, NOP, 0, 0), xx(LV3, lv3, RVRPKI, LV3_R, 4, REGT_INT), // load vector3 xx(LV3_R, lv3, RVRPRI, NOP, 0, 0), +xx(LCS, lcs, RSRPKI, LCS_R, 4, REGT_INT), // load string from char ptr. +xx(LCS_R, lcs, RSRPRI, NOP, 0, 0), xx(LBIT, lbit, RIRPI8, NOP, 0, 0), // rA = !!(*rB & C) -- *rB is a byte diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 923b52cfdb..e28f5d2715 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -49,7 +49,6 @@ #include "i_system.h" #include "gdtoa.h" #include "backend/vmbuilder.h" -#include "version.h" static int GetIntConst(FxExpression *ex, FCompileContext &ctx) { @@ -1040,6 +1039,11 @@ void ZCCCompiler::CompileAllFields() type->Size = Classes[i]->Type()->ParentClass->Size; } } + if (Classes[i]->Type()->ParentClass) + type->MetaSize = Classes[i]->Type()->ParentClass->MetaSize; + else + type->MetaSize = 0; + if (CompileFields(type, Classes[i]->Fields, nullptr, &Classes[i]->TreeNodes, false, !!HasNativeChildren.CheckKey(type))) { // Remove from the list if all fields got compiled. @@ -1130,11 +1134,6 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fiel if (field->Flags & ZCC_Meta) { varflags |= VARF_Meta | VARF_Static | VARF_ReadOnly; // metadata implies readonly - if (!(field->Flags & ZCC_Native)) - { - // Non-native meta data is not implemented yet and requires some groundwork in the class copy code. - Error(field, "Metadata member %s must be native", FName(field->Names->Name).GetChars()); - } } if (field->Type->ArraySize != nullptr) @@ -1155,22 +1154,28 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fiel if (varflags & VARF_Native) { - auto querytype = (varflags & VARF_Meta) ? type->GetClass() : type; - fd = FindField(querytype, FName(name->Name).GetChars()); - if (fd == nullptr) + if (varflags & VARF_Meta) { - Error(field, "The member variable '%s.%s' has not been exported from the executable", type->TypeName.GetChars(), FName(name->Name).GetChars()); + Error(field, "Native meta variable %s not allowed", FName(name->Name).GetChars()); } - else if (thisfieldtype->Size != fd->FieldSize && fd->BitValue == 0) - { - Error(field, "The member variable '%s.%s' has mismatching sizes in internal and external declaration (Internal = %d, External = %d)", type->TypeName.GetChars(), FName(name->Name).GetChars(), fd->FieldSize, thisfieldtype->Size); - } - // Q: Should we check alignment, too? A mismatch may be an indicator for bad assumptions. else { - // for bit fields the type must point to the source variable. - if (fd->BitValue != 0) thisfieldtype = fd->FieldSize == 1 ? TypeUInt8 : fd->FieldSize == 2 ? TypeUInt16 : TypeUInt32; - type->AddNativeField(name->Name, thisfieldtype, fd->FieldOffset, varflags, fd->BitValue); + fd = FindField(type, FName(name->Name).GetChars()); + if (fd == nullptr) + { + Error(field, "The member variable '%s.%s' has not been exported from the executable.", type->TypeName.GetChars(), FName(name->Name).GetChars()); + } + else if (thisfieldtype->Size != fd->FieldSize && fd->BitValue == 0) + { + Error(field, "The member variable '%s.%s' has mismatching sizes in internal and external declaration. (Internal = %d, External = %d)", type->TypeName.GetChars(), FName(name->Name).GetChars(), fd->FieldSize, thisfieldtype->Size); + } + // Q: Should we check alignment, too? A mismatch may be an indicator for bad assumptions. + else + { + // for bit fields the type must point to the source variable. + if (fd->BitValue != 0) thisfieldtype = fd->FieldSize == 1 ? TypeUInt8 : fd->FieldSize == 2 ? TypeUInt16 : TypeUInt32; + type->AddNativeField(name->Name, thisfieldtype, fd->FieldOffset, varflags, fd->BitValue); + } } } else if (hasnativechildren) @@ -1248,8 +1253,10 @@ bool ZCCCompiler::CompileProperties(PClass *type, TArray &Proper FString qualifiedname; // Store the full qualified name and prepend some 'garbage' to the name so that no conflicts with other symbol types can happen. // All these will be removed from the symbol table after the compiler finishes to free up the allocated space. - if (prefix == NAME_None) qualifiedname.Format("@property@%s", FName(p->NodeName).GetChars()); - else qualifiedname.Format("@property@%s.%s", prefix.GetChars(), FName(p->NodeName).GetChars()); + FName name = FName(p->NodeName); + if (prefix == NAME_None) qualifiedname.Format("@property@%s", name.GetChars()); + else qualifiedname.Format("@property@%s.%s", prefix.GetChars(), name.GetChars()); + fields.ShrinkToFit(); if (!type->Symbols.AddSymbol(new PProperty(qualifiedname, fields))) { @@ -1420,22 +1427,19 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n break; case AST_DynArrayType: - if (allowarraytypes) + { + auto atype = static_cast(ztype); + auto ftype = DetermineType(outertype, field, name, atype->ElementType, false, true); + if (ftype->GetRegType() == REGT_NIL || ftype->GetRegCount() > 1) { - auto atype = static_cast(ztype); - auto ftype = DetermineType(outertype, field, name, atype->ElementType, false, true); - if (ftype->GetRegType() == REGT_NIL || ftype->GetRegCount() > 1) - { - Error(field, "%s: Base type for dynamic array types nust be integral, but got %s", name.GetChars(), ftype->DescriptiveName()); - } - else - { - retval = NewDynArray(ftype); - } - break; + Error(field, "%s: Base type for dynamic array types nust be integral, but got %s", name.GetChars(), ftype->DescriptiveName()); + } + else + { + retval = NewDynArray(ftype); } break; - + } case AST_ClassType: { auto ctype = static_cast(ztype); @@ -1755,7 +1759,7 @@ void ZCCCompiler::DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *prop if (f->Flags & VARF_Meta) { - addr = ((char*)bag.Info) + f->Offset; + addr = ((char*)bag.Info->Meta) + f->Offset; } else { @@ -2250,7 +2254,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool { auto type = DetermineType(c->Type(), p, f->Name, p->Type, false, false); int flags = 0; - if (type->IsA(RUNTIME_CLASS(PStruct)) && type != TypeVector2 && type != TypeVector3) + if ((type->IsA(RUNTIME_CLASS(PStruct)) && type != TypeVector2 && type != TypeVector3) || type->IsA(RUNTIME_CLASS(PDynArray))) { // Structs are being passed by pointer, but unless marked 'out' that pointer must be readonly. type = NewPointer(type /*, !(p->Flags & ZCC_Out)*/); @@ -2711,7 +2715,7 @@ void ZCCCompiler::CompileStates() state.Misc1 = IntConstFromNode(sl->Offset, c->Type()); state.Misc2 = IntConstFromNode(static_cast(sl->Offset->SiblingNext), c->Type()); } -#ifdef DYNLIGHT + if (sl->Lights != nullptr) { auto l = sl->Lights; @@ -2721,7 +2725,6 @@ void ZCCCompiler::CompileStates() l = static_cast(l->SiblingNext); } while (l != sl->Lights); } -#endif if (sl->Action != nullptr) { diff --git a/src/scripting/zscript/zcc_parser.cpp b/src/scripting/zscript/zcc_parser.cpp index e48033bccd..761af95415 100644 --- a/src/scripting/zscript/zcc_parser.cpp +++ b/src/scripting/zscript/zcc_parser.cpp @@ -285,6 +285,7 @@ static void ParseSingleFile(const char *filename, int lump, void *parser, ZCCPar tokentype = ZCC_FLOATCONST; break; + case TK_None: // 'NONE' is a token for SBARINFO but not here. case TK_Identifier: value.Int = FName(sc.String); tokentype = ZCC_IDENTIFIER; diff --git a/src/serializer.cpp b/src/serializer.cpp index a577056aff..fd9af832c0 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -486,6 +486,8 @@ bool FSerializer::OpenReader(FCompressedBuffer *input) void FSerializer::Close() { + if (w == nullptr && r == nullptr) return; // double close? This should skip the I_Error at the bottom. + if (w != nullptr) { delete w; @@ -590,7 +592,7 @@ bool FSerializer::BeginObject(const char *name) } else { - Printf(TEXTCOLOR_RED "Object expected for '%s'", name); + Printf(TEXTCOLOR_RED "Object expected for '%s'\n", name); mErrors++; return false; } @@ -656,7 +658,7 @@ bool FSerializer::BeginArray(const char *name) } else { - Printf(TEXTCOLOR_RED "Array expected for '%s'", name); + Printf(TEXTCOLOR_RED "Array expected for '%s'\n", name); mErrors++; return false; } @@ -748,7 +750,7 @@ FSerializer &FSerializer::Args(const char *key, int *args, int *defargs, int spe else { assert(false && "Integer expected"); - Printf(TEXTCOLOR_RED "Integer expected for '%s[%d]'", key, i); + Printf(TEXTCOLOR_RED "Integer expected for '%s[%d]'\n", key, i); mErrors++; } } @@ -756,7 +758,7 @@ FSerializer &FSerializer::Args(const char *key, int *args, int *defargs, int spe else { assert(false && "array expected"); - Printf(TEXTCOLOR_RED "array expected for '%s'", key); + Printf(TEXTCOLOR_RED "array expected for '%s'\n", key); mErrors++; } } @@ -800,7 +802,7 @@ FSerializer &FSerializer::ScriptNum(const char *key, int &num) else { assert(false && "Integer expected"); - Printf(TEXTCOLOR_RED "Integer expected for '%s'", key); + Printf(TEXTCOLOR_RED "Integer expected for '%s'\n", key); mErrors++; } } @@ -1005,7 +1007,7 @@ void FSerializer::ReadObjects(bool hubtravel) PClass *cls = PClass::FindClass(clsname); if (cls == nullptr) { - Printf("Unknown object class '%s' in savegame", clsname.GetChars()); + Printf(TEXTCOLOR_RED "Unknown object class '%s' in savegame\n", clsname.GetChars()); founderrors = true; r->mDObjects[i] = RUNTIME_CLASS(AActor)->CreateNew(); // make sure we got at least a valid pointer for the duration of the loading process. r->mDObjects[i]->Destroy(); // but we do not want to keep this around, so destroy it right away. @@ -1041,7 +1043,7 @@ void FSerializer::ReadObjects(bool hubtravel) catch (CRecoverableError &err) { // In case something in here throws an error, let's continue and deal with it later. - Printf(TEXTCOLOR_RED "'%s'\n while restoring %s", err.GetMessage(), obj ? obj->GetClass()->TypeName.GetChars() : "invalid object"); + Printf(TEXTCOLOR_RED "'%s'\n while restoring %s\n", err.GetMessage(), obj ? obj->GetClass()->TypeName.GetChars() : "invalid object"); mErrors++; } } @@ -1055,7 +1057,7 @@ void FSerializer::ReadObjects(bool hubtravel) assert(!founderrors); if (founderrors) { - Printf(TEXTCOLOR_RED "Failed to restore all objects in savegame"); + Printf(TEXTCOLOR_RED "Failed to restore all objects in savegame\n"); mErrors++; } } @@ -1064,7 +1066,7 @@ void FSerializer::ReadObjects(bool hubtravel) // nuke all objects we created here. for (auto obj : r->mDObjects) { - obj->Destroy(); + if (!(obj->ObjectFlags & OF_EuthanizeMe)) obj->Destroy(); } r->mDObjects.Clear(); @@ -1182,7 +1184,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, bool &value, bool *def } else { - Printf(TEXTCOLOR_RED "boolean type expected for '%s'", key); + Printf(TEXTCOLOR_RED "boolean type expected for '%s'\n", key); arc.mErrors++; } } @@ -1218,7 +1220,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, int64_t &value, int64_ } else { - Printf(TEXTCOLOR_RED "integer type expected for '%s'", key); + Printf(TEXTCOLOR_RED "integer type expected for '%s'\n", key); arc.mErrors++; } } @@ -1254,7 +1256,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, uint64_t &value, uint6 } else { - Printf(TEXTCOLOR_RED "integer type expected for '%s'", key); + Printf(TEXTCOLOR_RED "integer type expected for '%s'\n", key); arc.mErrors++; } } @@ -1291,7 +1293,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, int32_t &value, int32_ } else { - Printf(TEXTCOLOR_RED "integer type expected for '%s'", key); + Printf(TEXTCOLOR_RED "integer type expected for '%s'\n", key); arc.mErrors++; } } @@ -1327,7 +1329,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, uint32_t &value, uint3 } else { - Printf(TEXTCOLOR_RED "integer type expected for '%s'", key); + Printf(TEXTCOLOR_RED "integer type expected for '%s'\n", key); arc.mErrors++; } } @@ -1405,7 +1407,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, double &value, double } else { - Printf(TEXTCOLOR_RED "float type expected for '%s'", key); + Printf(TEXTCOLOR_RED "float type expected for '%s'\n", key); arc.mErrors++; } } @@ -1548,7 +1550,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTe } else { - Printf(TEXTCOLOR_RED "object does not represent a texture for '%s'", key); + Printf(TEXTCOLOR_RED "object does not represent a texture for '%s'\n", key); value.SetNull(); arc.mErrors++; } @@ -1564,7 +1566,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTe else { assert(false && "not a texture"); - Printf(TEXTCOLOR_RED "object does not represent a texture for '%s'", key); + Printf(TEXTCOLOR_RED "object does not represent a texture for '%s'\n", key); value.SetNull(); arc.mErrors++; } @@ -1649,7 +1651,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObje else { assert(false && "invalid object reference"); - Printf(TEXTCOLOR_RED "Invalid object reference for '%s'", key); + Printf(TEXTCOLOR_RED "Invalid object reference for '%s'\n", key); value = nullptr; arc.mErrors++; if (retcode) *retcode = false; @@ -1698,7 +1700,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FName &value, FName *d } else { - Printf(TEXTCOLOR_RED "String expected for '%s'", key); + Printf(TEXTCOLOR_RED "String expected for '%s'\n", key); arc.mErrors++; value = NAME_None; } @@ -1746,7 +1748,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FDynamicCol } } assert(false && "not a colormap"); - Printf(TEXTCOLOR_RED "object does not represent a colormap for '%s'", key); + Printf(TEXTCOLOR_RED "object does not represent a colormap for '%s'\n", key); cm = &NormalLight; } } @@ -1787,7 +1789,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundI } else { - Printf(TEXTCOLOR_RED "string type expected for '%s'", key); + Printf(TEXTCOLOR_RED "string type expected for '%s'\n", key); sid = 0; arc.mErrors++; } @@ -1836,7 +1838,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClassActor } else { - Printf(TEXTCOLOR_RED "string type expected for '%s'", key); + Printf(TEXTCOLOR_RED "string type expected for '%s'\n", key); clst = nullptr; arc.mErrors++; } @@ -1884,7 +1886,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClass *&cl } else { - Printf(TEXTCOLOR_RED "string type expected for '%s'", key); + Printf(TEXTCOLOR_RED "string type expected for '%s'\n", key); clst = nullptr; arc.mErrors++; } @@ -1960,20 +1962,20 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState { // this can actually happen by changing the DECORATE so treat it as a warning, not an error. state = nullptr; - Printf(TEXTCOLOR_ORANGE "Invalid state '%s+%d' for '%s'", cls.GetString(), ndx.GetInt(), key); + Printf(TEXTCOLOR_ORANGE "Invalid state '%s+%d' for '%s'\n", cls.GetString(), ndx.GetInt(), key); } } else { assert(false && "not a state"); - Printf(TEXTCOLOR_RED "data does not represent a state for '%s'", key); + Printf(TEXTCOLOR_RED "data does not represent a state for '%s'\n", key); arc.mErrors++; } } else if (!retcode) { assert(false && "not an array"); - Printf(TEXTCOLOR_RED "array type expected for '%s'", key); + Printf(TEXTCOLOR_RED "array type expected for '%s'\n", key); arc.mErrors++; } } @@ -2028,7 +2030,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDial } else { - Printf(TEXTCOLOR_RED "integer expected for '%s'", key); + Printf(TEXTCOLOR_RED "integer expected for '%s'\n", key); arc.mErrors++; node = nullptr; } @@ -2077,7 +2079,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FString *&p } else { - Printf(TEXTCOLOR_RED "string expected for '%s'", key); + Printf(TEXTCOLOR_RED "string expected for '%s'\n", key); pstr = nullptr; arc.mErrors++; } @@ -2119,7 +2121,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FString &pstr, FString } else { - Printf(TEXTCOLOR_RED "string expected for '%s'", key); + Printf(TEXTCOLOR_RED "string expected for '%s'\n", key); pstr = ""; arc.mErrors++; } @@ -2168,7 +2170,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, char *&pstr } else { - Printf(TEXTCOLOR_RED "string expected for '%s'", key); + Printf(TEXTCOLOR_RED "string expected for '%s'\n", key); pstr = nullptr; arc.mErrors++; } diff --git a/src/serializer.h b/src/serializer.h index 4f0d90cecd..7527eeb3c3 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -64,6 +64,7 @@ public: ~FSerializer() { + mErrors = 0; // The destructor may not throw an exception so silence the error checker. Close(); } bool OpenWriter(bool pretty = true); diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index 1cbca0863d..a896aeee73 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -279,6 +279,10 @@ void MusInfo::TimidityVolumeChanged() { } +void MusInfo::GMEDepthChanged(float val) +{ +} + void MusInfo::FluidSettingInt(const char *, int) { } diff --git a/src/sound/i_music.h b/src/sound/i_music.h index aaa00b020a..069d2f1427 100644 --- a/src/sound/i_music.h +++ b/src/sound/i_music.h @@ -84,6 +84,7 @@ public: virtual void FluidSettingNum(const char *setting, double value); // " virtual void FluidSettingStr(const char *setting, const char *value); // " virtual void WildMidiSetOption(int opt, int set); + virtual void GMEDepthChanged(float val); void Start(bool loop, float rel_vol = -1.f, int subsong = 0); diff --git a/src/sound/music_gme.cpp b/src/sound/music_gme.cpp index 6038f23496..d00728ec69 100644 --- a/src/sound/music_gme.cpp +++ b/src/sound/music_gme.cpp @@ -43,6 +43,7 @@ #include #include "v_text.h" #include "files.h" +#include "templates.h" // MACROS ------------------------------------------------------------------ @@ -67,6 +68,7 @@ protected: bool StartTrack(int track, bool getcritsec=true); bool GetTrackInfo(); int CalcSongLength(); + void GMEDepthChanged(float val); static bool Read(SoundStream *stream, void *buff, int len, void *userdata); }; @@ -84,6 +86,12 @@ protected: // Currently not used. CVAR (Float, spc_amp, 1.875f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CUSTOM_CVAR(Float, gme_stereodepth, 0.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (currSong != nullptr) + currSong->GMEDepthChanged(self); +} + // PRIVATE DATA DEFINITIONS ------------------------------------------------ // CODE -------------------------------------------------------------------- @@ -146,6 +154,7 @@ MusInfo *GME_OpenSong(FileReader &reader, const char *fmt) reader.Seek(fpos, SEEK_SET); return NULL; } + gme_set_stereo_depth(emu, clamp(*gme_stereodepth, 0.f, 1.f)); return new GMESong(emu, sample_rate); } @@ -189,6 +198,19 @@ GMESong::~GMESong() } +//========================================================================== +// +// GMESong :: GMEDepthChanged +// +//========================================================================== + +void GMESong::GMEDepthChanged(float val) +{ + if (Emu != nullptr) + gme_set_stereo_depth(Emu, clamp(val, 0.f, 1.f)); +} + + //========================================================================== // // GMESong :: Play diff --git a/src/sound/oalsound.cpp b/src/sound/oalsound.cpp index 1b34583f94..601ec72002 100644 --- a/src/sound/oalsound.cpp +++ b/src/sound/oalsound.cpp @@ -99,9 +99,9 @@ bool IsOpenALPresent() void I_BuildALDeviceList(FOptionValues *opt) { - opt->mValues.Resize(1); - opt->mValues[0].TextValue = "Default"; - opt->mValues[0].Text = "Default"; + opt->mValues.Resize(1); + opt->mValues[0].TextValue = "Default"; + opt->mValues[0].Text = "Default"; #ifndef NO_OPENAL if (IsOpenALPresent()) @@ -138,31 +138,31 @@ EXTERN_CVAR (Bool, snd_pitched) static ALenum checkALError(const char *fn, unsigned int ln) { - ALenum err = alGetError(); - if(err != AL_NO_ERROR) - { - if(strchr(fn, '/')) - fn = strrchr(fn, '/')+1; - else if(strchr(fn, '\\')) - fn = strrchr(fn, '\\')+1; - Printf(">>>>>>>>>>>> Received AL error %s (%#x), %s:%u\n", alGetString(err), err, fn, ln); - } - return err; + ALenum err = alGetError(); + if(err != AL_NO_ERROR) + { + if(strchr(fn, '/')) + fn = strrchr(fn, '/')+1; + else if(strchr(fn, '\\')) + fn = strrchr(fn, '\\')+1; + Printf(">>>>>>>>>>>> Received AL error %s (%#x), %s:%u\n", alGetString(err), err, fn, ln); + } + return err; } #define getALError() checkALError(__FILE__, __LINE__) static ALCenum checkALCError(ALCdevice *device, const char *fn, unsigned int ln) { - ALCenum err = alcGetError(device); - if(err != ALC_NO_ERROR) - { - if(strchr(fn, '/')) - fn = strrchr(fn, '/')+1; - else if(strchr(fn, '\\')) - fn = strrchr(fn, '\\')+1; - Printf(">>>>>>>>>>>> Received ALC error %s (%#x), %s:%u\n", alcGetString(device, err), err, fn, ln); - } - return err; + ALCenum err = alcGetError(device); + if(err != ALC_NO_ERROR) + { + if(strchr(fn, '/')) + fn = strrchr(fn, '/')+1; + else if(strchr(fn, '\\')) + fn = strrchr(fn, '\\')+1; + Printf(">>>>>>>>>>>> Received ALC error %s (%#x), %s:%u\n", alcGetString(device, err), err, fn, ln); + } + return err; } #define getALCError(d) checkALCError((d), __FILE__, __LINE__) @@ -172,272 +172,272 @@ static ALCenum checkALCError(ALCdevice *device, const char *fn, unsigned int ln) // where they act as appropriate fallbacks. static ALvoid AL_APIENTRY _wrap_DeferUpdatesSOFT(void) { - alcSuspendContext(alcGetCurrentContext()); + alcSuspendContext(alcGetCurrentContext()); } static ALvoid AL_APIENTRY _wrap_ProcessUpdatesSOFT(void) { - alcProcessContext(alcGetCurrentContext()); + alcProcessContext(alcGetCurrentContext()); } class OpenALSoundStream : public SoundStream { - OpenALSoundRenderer *Renderer; + OpenALSoundRenderer *Renderer; - SoundStreamCallback Callback; - void *UserData; + SoundStreamCallback Callback; + void *UserData; - TArray Data; + TArray Data; - ALsizei SampleRate; - ALenum Format; - ALsizei FrameSize; + ALsizei SampleRate; + ALenum Format; + ALsizei FrameSize; - static const int BufferCount = 4; - ALuint Buffers[BufferCount]; - ALuint Source; + static const int BufferCount = 4; + ALuint Buffers[BufferCount]; + ALuint Source; - std::atomic Playing; - bool Looping; - ALfloat Volume; + std::atomic Playing; + bool Looping; + ALfloat Volume; - FileReader *Reader; - SoundDecoder *Decoder; - static bool DecoderCallback(SoundStream *_sstream, void *ptr, int length, void *user) - { - OpenALSoundStream *self = static_cast(_sstream); - if(length < 0) return false; + FileReader *Reader; + SoundDecoder *Decoder; + static bool DecoderCallback(SoundStream *_sstream, void *ptr, int length, void *user) + { + OpenALSoundStream *self = static_cast(_sstream); + if(length < 0) return false; - size_t got = self->Decoder->read((char*)ptr, length); - if(got < (unsigned int)length) - { - if(!self->Looping || !self->Decoder->seek(0)) - return false; - got += self->Decoder->read((char*)ptr+got, length-got); - } + size_t got = self->Decoder->read((char*)ptr, length); + if(got < (unsigned int)length) + { + if(!self->Looping || !self->Decoder->seek(0)) + return false; + got += self->Decoder->read((char*)ptr+got, length-got); + } - return (got == (unsigned int)length); - } + return (got == (unsigned int)length); + } - bool SetupSource() - { - /* Get a source, killing the farthest, lowest-priority sound if needed */ - if(Renderer->FreeSfx.Size() == 0) - { - FSoundChan *lowest = Renderer->FindLowestChannel(); - if(lowest) Renderer->StopChannel(lowest); + bool SetupSource() + { + /* Get a source, killing the farthest, lowest-priority sound if needed */ + if(Renderer->FreeSfx.Size() == 0) + { + FSoundChan *lowest = Renderer->FindLowestChannel(); + if(lowest) Renderer->StopChannel(lowest); - if(Renderer->FreeSfx.Size() == 0) - return false; - } - Renderer->FreeSfx.Pop(Source); + if(Renderer->FreeSfx.Size() == 0) + return false; + } + Renderer->FreeSfx.Pop(Source); - /* Set the default properties for localized playback */ - alSource3f(Source, AL_DIRECTION, 0.f, 0.f, 0.f); - alSource3f(Source, AL_VELOCITY, 0.f, 0.f, 0.f); - alSource3f(Source, AL_POSITION, 0.f, 0.f, 0.f); - alSourcef(Source, AL_MAX_GAIN, 1.f); - alSourcef(Source, AL_GAIN, 1.f); - alSourcef(Source, AL_PITCH, 1.f); - alSourcef(Source, AL_ROLLOFF_FACTOR, 0.f); - alSourcef(Source, AL_SEC_OFFSET, 0.f); - alSourcei(Source, AL_SOURCE_RELATIVE, AL_TRUE); - alSourcei(Source, AL_LOOPING, AL_FALSE); - if(Renderer->EnvSlot) - { - alSourcef(Source, AL_ROOM_ROLLOFF_FACTOR, 0.f); - alSourcef(Source, AL_AIR_ABSORPTION_FACTOR, 0.f); - alSourcei(Source, AL_DIRECT_FILTER, AL_FILTER_NULL); - alSource3i(Source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); - } - if(Renderer->AL.EXT_SOURCE_RADIUS) - alSourcef(Source, AL_SOURCE_RADIUS, 0.f); + /* Set the default properties for localized playback */ + alSource3f(Source, AL_DIRECTION, 0.f, 0.f, 0.f); + alSource3f(Source, AL_VELOCITY, 0.f, 0.f, 0.f); + alSource3f(Source, AL_POSITION, 0.f, 0.f, 0.f); + alSourcef(Source, AL_MAX_GAIN, 1.f); + alSourcef(Source, AL_GAIN, 1.f); + alSourcef(Source, AL_PITCH, 1.f); + alSourcef(Source, AL_ROLLOFF_FACTOR, 0.f); + alSourcef(Source, AL_SEC_OFFSET, 0.f); + alSourcei(Source, AL_SOURCE_RELATIVE, AL_TRUE); + alSourcei(Source, AL_LOOPING, AL_FALSE); + if(Renderer->EnvSlot) + { + alSourcef(Source, AL_ROOM_ROLLOFF_FACTOR, 0.f); + alSourcef(Source, AL_AIR_ABSORPTION_FACTOR, 0.f); + alSourcei(Source, AL_DIRECT_FILTER, AL_FILTER_NULL); + alSource3i(Source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); + } + if(Renderer->AL.EXT_SOURCE_RADIUS) + alSourcef(Source, AL_SOURCE_RADIUS, 0.f); - alGenBuffers(BufferCount, Buffers); - return (getALError() == AL_NO_ERROR); - } + alGenBuffers(BufferCount, Buffers); + return (getALError() == AL_NO_ERROR); + } public: - OpenALSoundStream(OpenALSoundRenderer *renderer) - : Renderer(renderer), Source(0), Playing(false), Looping(false), Volume(1.0f), Reader(NULL), Decoder(NULL) - { - memset(Buffers, 0, sizeof(Buffers)); - Renderer->AddStream(this); - } + OpenALSoundStream(OpenALSoundRenderer *renderer) + : Renderer(renderer), Source(0), Playing(false), Looping(false), Volume(1.0f), Reader(NULL), Decoder(NULL) + { + memset(Buffers, 0, sizeof(Buffers)); + Renderer->AddStream(this); + } - virtual ~OpenALSoundStream() - { - Renderer->RemoveStream(this); + virtual ~OpenALSoundStream() + { + Renderer->RemoveStream(this); - if(Source) - { - alSourceRewind(Source); - alSourcei(Source, AL_BUFFER, 0); + if(Source) + { + alSourceRewind(Source); + alSourcei(Source, AL_BUFFER, 0); - Renderer->FreeSfx.Push(Source); - Source = 0; - } + Renderer->FreeSfx.Push(Source); + Source = 0; + } - if(Buffers[0]) - { - alDeleteBuffers(BufferCount, &Buffers[0]); - memset(Buffers, 0, sizeof(Buffers)); - } - getALError(); + if(Buffers[0]) + { + alDeleteBuffers(BufferCount, &Buffers[0]); + memset(Buffers, 0, sizeof(Buffers)); + } + getALError(); - delete Decoder; - delete Reader; - } + delete Decoder; + delete Reader; + } - virtual bool Play(bool loop, float vol) - { - SetVolume(vol); + virtual bool Play(bool loop, float vol) + { + SetVolume(vol); - if(Playing.load()) - return true; + if(Playing.load()) + return true; - /* Clear the buffer queue, then fill and queue each buffer */ - alSourcei(Source, AL_BUFFER, 0); - for(int i = 0;i < BufferCount;i++) - { - if(!Callback(this, &Data[0], Data.Size(), UserData)) - { - if(i == 0) - return false; - break; - } + /* Clear the buffer queue, then fill and queue each buffer */ + alSourcei(Source, AL_BUFFER, 0); + for(int i = 0;i < BufferCount;i++) + { + if(!Callback(this, &Data[0], Data.Size(), UserData)) + { + if(i == 0) + return false; + break; + } - alBufferData(Buffers[i], Format, &Data[0], Data.Size(), SampleRate); - alSourceQueueBuffers(Source, 1, &Buffers[i]); - } - if(getALError() != AL_NO_ERROR) - return false; + alBufferData(Buffers[i], Format, &Data[0], Data.Size(), SampleRate); + alSourceQueueBuffers(Source, 1, &Buffers[i]); + } + if(getALError() != AL_NO_ERROR) + return false; - alSourcePlay(Source); - if(getALError() != AL_NO_ERROR) - return false; + alSourcePlay(Source); + if(getALError() != AL_NO_ERROR) + return false; - Playing.store(true); - return true; - } + Playing.store(true); + return true; + } - virtual void Stop() - { - if(!Playing.load()) - return; + virtual void Stop() + { + if(!Playing.load()) + return; - std::unique_lock lock(Renderer->StreamLock); - alSourceStop(Source); - alSourcei(Source, AL_BUFFER, 0); - getALError(); + std::unique_lock lock(Renderer->StreamLock); + alSourceStop(Source); + alSourcei(Source, AL_BUFFER, 0); + getALError(); - Playing.store(false); - } + Playing.store(false); + } - virtual void SetVolume(float vol) - { - Volume = vol; - UpdateVolume(); - } + virtual void SetVolume(float vol) + { + Volume = vol; + UpdateVolume(); + } - void UpdateVolume() - { - alSourcef(Source, AL_GAIN, Renderer->MusicVolume*Volume); - getALError(); - } + void UpdateVolume() + { + alSourcef(Source, AL_GAIN, Renderer->MusicVolume*Volume); + getALError(); + } - virtual bool SetPaused(bool pause) - { - if(pause) - alSourcePause(Source); - else - alSourcePlay(Source); - return (getALError()==AL_NO_ERROR); - } + virtual bool SetPaused(bool pause) + { + if(pause) + alSourcePause(Source); + else + alSourcePlay(Source); + return (getALError()==AL_NO_ERROR); + } - virtual bool SetPosition(unsigned int ms_pos) - { - std::unique_lock lock(Renderer->StreamLock); - if(!Decoder->seek(ms_pos)) - return false; + virtual bool SetPosition(unsigned int ms_pos) + { + std::unique_lock lock(Renderer->StreamLock); + if(!Decoder->seek(ms_pos)) + return false; - if(!Playing.load()) - return true; - // Stop the source so that all buffers become processed, which will - // allow the next update to restart the source queue with the new - // position. - alSourceStop(Source); - getALError(); - lock.unlock(); - Renderer->StreamWake.notify_all(); - return true; - } + if(!Playing.load()) + return true; + // Stop the source so that all buffers become processed, which will + // allow the next update to restart the source queue with the new + // position. + alSourceStop(Source); + getALError(); + lock.unlock(); + Renderer->StreamWake.notify_all(); + return true; + } - virtual unsigned int GetPosition() - { - std::unique_lock lock(Renderer->StreamLock); - ALint offset, queued, state; - alGetSourcei(Source, AL_SAMPLE_OFFSET, &offset); - alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); - alGetSourcei(Source, AL_SOURCE_STATE, &state); - if(getALError() != AL_NO_ERROR) - return 0; + virtual unsigned int GetPosition() + { + std::unique_lock lock(Renderer->StreamLock); + ALint offset, queued, state; + alGetSourcei(Source, AL_SAMPLE_OFFSET, &offset); + alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); + alGetSourcei(Source, AL_SOURCE_STATE, &state); + if(getALError() != AL_NO_ERROR) + return 0; - size_t pos = Decoder->getSampleOffset(); - lock.unlock(); + size_t pos = Decoder->getSampleOffset(); + lock.unlock(); - if(state != AL_STOPPED) - { - size_t rem = queued*(Data.Size()/FrameSize) - offset; - if(pos > rem) pos -= rem; - else pos = 0; - } - return (unsigned int)(pos * 1000.0 / SampleRate); - } + if(state != AL_STOPPED) + { + size_t rem = queued*(Data.Size()/FrameSize) - offset; + if(pos > rem) pos -= rem; + else pos = 0; + } + return (unsigned int)(pos * 1000.0 / SampleRate); + } - virtual bool IsEnded() - { - return !Playing.load(); - } + virtual bool IsEnded() + { + return !Playing.load(); + } - virtual FString GetStats() - { - FString stats; - size_t pos = 0, len = 0; - ALfloat volume; - ALint offset; - ALint processed; - ALint queued; - ALint state; - ALenum err; + virtual FString GetStats() + { + FString stats; + size_t pos = 0, len = 0; + ALfloat volume; + ALint offset; + ALint processed; + ALint queued; + ALint state; + ALenum err; - std::unique_lock lock(Renderer->StreamLock); - alGetSourcef(Source, AL_GAIN, &volume); - alGetSourcei(Source, AL_SAMPLE_OFFSET, &offset); - alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed); - alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); - alGetSourcei(Source, AL_SOURCE_STATE, &state); - if((err=alGetError()) != AL_NO_ERROR) - { - lock.unlock(); - stats = "Error getting stats: "; - stats += alGetString(err); - return stats; - } + std::unique_lock lock(Renderer->StreamLock); + alGetSourcef(Source, AL_GAIN, &volume); + alGetSourcei(Source, AL_SAMPLE_OFFSET, &offset); + alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed); + alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); + alGetSourcei(Source, AL_SOURCE_STATE, &state); + if((err=alGetError()) != AL_NO_ERROR) + { + lock.unlock(); + stats = "Error getting stats: "; + stats += alGetString(err); + return stats; + } if (Decoder != nullptr) { pos = Decoder->getSampleOffset(); len = Decoder->getSampleLength(); } - lock.unlock(); + lock.unlock(); - stats = (state == AL_INITIAL) ? "Buffering" : (state == AL_STOPPED) ? "Underrun" : - (state == AL_PLAYING || state == AL_PAUSED) ? "Ready" : "Unknown state"; + stats = (state == AL_INITIAL) ? "Buffering" : (state == AL_STOPPED) ? "Underrun" : + (state == AL_PLAYING || state == AL_PAUSED) ? "Ready" : "Unknown state"; if (Decoder != nullptr) { @@ -457,177 +457,177 @@ public: if (len > 0) stats.AppendFormat(" / %zu.%03zu", len / 1000, len % 1000); } - if(state == AL_PAUSED) - stats += ", paused"; - if(state == AL_PLAYING) - stats += ", playing"; - stats.AppendFormat(", %uHz", SampleRate); - if(!Playing) - stats += " XX"; - return stats; - } + if(state == AL_PAUSED) + stats += ", paused"; + if(state == AL_PLAYING) + stats += ", playing"; + stats.AppendFormat(", %uHz", SampleRate); + if(!Playing) + stats += " XX"; + return stats; + } - bool Process() - { - if(!Playing.load()) - return false; + bool Process() + { + if(!Playing.load()) + return false; - ALint state, processed; - alGetSourcei(Source, AL_SOURCE_STATE, &state); - alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed); - if(getALError() != AL_NO_ERROR) - { - Playing.store(false); - return false; - } + ALint state, processed; + alGetSourcei(Source, AL_SOURCE_STATE, &state); + alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed); + if(getALError() != AL_NO_ERROR) + { + Playing.store(false); + return false; + } - // For each processed buffer in the queue... - while(processed > 0) - { - ALuint bufid; + // For each processed buffer in the queue... + while(processed > 0) + { + ALuint bufid; - // Unqueue the oldest buffer, fill it with more data, and queue it - // on the end - alSourceUnqueueBuffers(Source, 1, &bufid); - processed--; + // Unqueue the oldest buffer, fill it with more data, and queue it + // on the end + alSourceUnqueueBuffers(Source, 1, &bufid); + processed--; - if(Callback(this, &Data[0], Data.Size(), UserData)) - { - alBufferData(bufid, Format, &Data[0], Data.Size(), SampleRate); - alSourceQueueBuffers(Source, 1, &bufid); - } - } + if(Callback(this, &Data[0], Data.Size(), UserData)) + { + alBufferData(bufid, Format, &Data[0], Data.Size(), SampleRate); + alSourceQueueBuffers(Source, 1, &bufid); + } + } - // If the source is not playing or paused, and there are buffers queued, - // then there was an underrun. Restart the source. - bool ok = (getALError()==AL_NO_ERROR); - if(ok && state != AL_PLAYING && state != AL_PAUSED) - { - ALint queued = 0; - alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); + // If the source is not playing or paused, and there are buffers queued, + // then there was an underrun. Restart the source. + bool ok = (getALError()==AL_NO_ERROR); + if(ok && state != AL_PLAYING && state != AL_PAUSED) + { + ALint queued = 0; + alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); - ok = (getALError() == AL_NO_ERROR) && (queued > 0); - if(ok) - { - alSourcePlay(Source); - ok = (getALError()==AL_NO_ERROR); - } - } + ok = (getALError() == AL_NO_ERROR) && (queued > 0); + if(ok) + { + alSourcePlay(Source); + ok = (getALError()==AL_NO_ERROR); + } + } - Playing.store(ok); - return ok; - } + Playing.store(ok); + return ok; + } - bool Init(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) - { - if(!SetupSource()) - return false; + bool Init(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) + { + if(!SetupSource()) + return false; - Callback = callback; - UserData = userdata; - SampleRate = samplerate; + Callback = callback; + UserData = userdata; + SampleRate = samplerate; - Format = AL_NONE; - if((flags&Bits8)) /* Signed or unsigned? We assume unsigned 8-bit... */ - { - if((flags&Mono)) Format = AL_FORMAT_MONO8; - else Format = AL_FORMAT_STEREO8; - } - else if((flags&Float)) - { - if(alIsExtensionPresent("AL_EXT_FLOAT32")) - { - if((flags&Mono)) Format = AL_FORMAT_MONO_FLOAT32; - else Format = AL_FORMAT_STEREO_FLOAT32; - } - } - else if((flags&Bits32)) - { - } - else - { - if((flags&Mono)) Format = AL_FORMAT_MONO16; - else Format = AL_FORMAT_STEREO16; - } + Format = AL_NONE; + if((flags&Bits8)) /* Signed or unsigned? We assume unsigned 8-bit... */ + { + if((flags&Mono)) Format = AL_FORMAT_MONO8; + else Format = AL_FORMAT_STEREO8; + } + else if((flags&Float)) + { + if(alIsExtensionPresent("AL_EXT_FLOAT32")) + { + if((flags&Mono)) Format = AL_FORMAT_MONO_FLOAT32; + else Format = AL_FORMAT_STEREO_FLOAT32; + } + } + else if((flags&Bits32)) + { + } + else + { + if((flags&Mono)) Format = AL_FORMAT_MONO16; + else Format = AL_FORMAT_STEREO16; + } - if(Format == AL_NONE) - { - Printf("Unsupported format: 0x%x\n", flags); - return false; - } + if(Format == AL_NONE) + { + Printf("Unsupported format: 0x%x\n", flags); + return false; + } - FrameSize = 1; - if((flags&Bits8)) - FrameSize *= 1; - else if((flags&(Bits32|Float))) - FrameSize *= 4; - else - FrameSize *= 2; + FrameSize = 1; + if((flags&Bits8)) + FrameSize *= 1; + else if((flags&(Bits32|Float))) + FrameSize *= 4; + else + FrameSize *= 2; - if((flags&Mono)) - FrameSize *= 1; - else - FrameSize *= 2; + if((flags&Mono)) + FrameSize *= 1; + else + FrameSize *= 2; - buffbytes += FrameSize-1; - buffbytes -= buffbytes%FrameSize; - Data.Resize(buffbytes); + buffbytes += FrameSize-1; + buffbytes -= buffbytes%FrameSize; + Data.Resize(buffbytes); - return true; - } + return true; + } - bool Init(FileReader *reader, bool loop) - { - if(!SetupSource()) - { - delete reader; - return false; - } + bool Init(FileReader *reader, bool loop) + { + if(!SetupSource()) + { + delete reader; + return false; + } - if(Decoder) delete Decoder; - if(Reader) delete Reader; - Reader = reader; - Decoder = Renderer->CreateDecoder(Reader); - if(!Decoder) return false; + if(Decoder) delete Decoder; + if(Reader) delete Reader; + Reader = reader; + Decoder = Renderer->CreateDecoder(Reader); + if(!Decoder) return false; - Callback = DecoderCallback; - UserData = NULL; - Format = AL_NONE; - FrameSize = 1; + Callback = DecoderCallback; + UserData = NULL; + Format = AL_NONE; + FrameSize = 1; - ChannelConfig chans; - SampleType type; - int srate; + ChannelConfig chans; + SampleType type; + int srate; - Decoder->getInfo(&srate, &chans, &type); - if(chans == ChannelConfig_Mono) - { - if(type == SampleType_UInt8) Format = AL_FORMAT_MONO8; - if(type == SampleType_Int16) Format = AL_FORMAT_MONO16; - FrameSize *= 1; - } - if(chans == ChannelConfig_Stereo) - { - if(type == SampleType_UInt8) Format = AL_FORMAT_STEREO8; - if(type == SampleType_Int16) Format = AL_FORMAT_STEREO16; - FrameSize *= 2; - } - if(type == SampleType_UInt8) FrameSize *= 1; - if(type == SampleType_Int16) FrameSize *= 2; + Decoder->getInfo(&srate, &chans, &type); + if(chans == ChannelConfig_Mono) + { + if(type == SampleType_UInt8) Format = AL_FORMAT_MONO8; + if(type == SampleType_Int16) Format = AL_FORMAT_MONO16; + FrameSize *= 1; + } + if(chans == ChannelConfig_Stereo) + { + if(type == SampleType_UInt8) Format = AL_FORMAT_STEREO8; + if(type == SampleType_Int16) Format = AL_FORMAT_STEREO16; + FrameSize *= 2; + } + if(type == SampleType_UInt8) FrameSize *= 1; + if(type == SampleType_Int16) FrameSize *= 2; - if(Format == AL_NONE) - { - Printf("Unsupported audio format: %s, %s\n", GetChannelConfigName(chans), - GetSampleTypeName(type)); - return false; - } - SampleRate = srate; - Looping = loop; + if(Format == AL_NONE) + { + Printf("Unsupported audio format: %s, %s\n", GetChannelConfigName(chans), + GetSampleTypeName(type)); + return false; + } + SampleRate = srate; + Looping = loop; - Data.Resize((SampleRate / 5) * FrameSize); + Data.Resize((SampleRate / 5) * FrameSize); - return true; - } + return true; + } }; @@ -641,32 +641,32 @@ extern ReverbContainer *ForcedEnvironment; static size_t GetChannelCount(ChannelConfig chans) { - switch(chans) - { - case ChannelConfig_Mono: return 1; - case ChannelConfig_Stereo: return 2; - } - return 0; + switch(chans) + { + case ChannelConfig_Mono: return 1; + case ChannelConfig_Stereo: return 2; + } + return 0; } static float GetRolloff(const FRolloffInfo *rolloff, float distance) { - if(distance <= rolloff->MinDistance) - return 1.f; - // Logarithmic rolloff has no max distance where it goes silent. - if(rolloff->RolloffType == ROLLOFF_Log) - return rolloff->MinDistance / - (rolloff->MinDistance + rolloff->RolloffFactor*(distance-rolloff->MinDistance)); - if(distance >= rolloff->MaxDistance) - return 0.f; + if(distance <= rolloff->MinDistance) + return 1.f; + // Logarithmic rolloff has no max distance where it goes silent. + if(rolloff->RolloffType == ROLLOFF_Log) + return rolloff->MinDistance / + (rolloff->MinDistance + rolloff->RolloffFactor*(distance-rolloff->MinDistance)); + if(distance >= rolloff->MaxDistance) + return 0.f; - float volume = (rolloff->MaxDistance - distance) / (rolloff->MaxDistance - rolloff->MinDistance); - if(rolloff->RolloffType == ROLLOFF_Linear) - return volume; + float volume = (rolloff->MaxDistance - distance) / (rolloff->MaxDistance - rolloff->MinDistance); + if(rolloff->RolloffType == ROLLOFF_Linear) + return volume; - if(rolloff->RolloffType == ROLLOFF_Custom && S_SoundCurve != NULL) - return S_SoundCurve[int(S_SoundCurveSize * (1.f - volume))] / 127.f; - return (powf(10.f, volume) - 1.f) / 9.f; + if(rolloff->RolloffType == ROLLOFF_Custom && S_SoundCurve != NULL) + return S_SoundCurve[int(S_SoundCurveSize * (1.f - volume))] / 127.f; + return (powf(10.f, volume) - 1.f) / 9.f; } ALCdevice *OpenALSoundRenderer::InitDevice() @@ -700,608 +700,612 @@ ALCdevice *OpenALSoundRenderer::InitDevice() template static void LoadALFunc(const char *name, T *x) -{ *x = reinterpret_cast(alGetProcAddress(name)); } +{ + *x = reinterpret_cast(alGetProcAddress(name)); +} template static void LoadALCFunc(ALCdevice *device, const char *name, T *x) -{ *x = reinterpret_cast(alcGetProcAddress(device, name)); } +{ + *x = reinterpret_cast(alcGetProcAddress(device, name)); +} #define LOAD_FUNC(x) (LoadALFunc(#x, &x)) #define LOAD_DEV_FUNC(d, x) (LoadALCFunc(d, #x, &x)) OpenALSoundRenderer::OpenALSoundRenderer() - : QuitThread(false), Device(NULL), Context(NULL), SFXPaused(0), PrevEnvironment(NULL), EnvSlot(0) + : QuitThread(false), Device(NULL), Context(NULL), SFXPaused(0), PrevEnvironment(NULL), EnvSlot(0) { - EnvFilters[0] = EnvFilters[1] = 0; + EnvFilters[0] = EnvFilters[1] = 0; - Printf("I_InitSound: Initializing OpenAL\n"); + Printf("I_InitSound: Initializing OpenAL\n"); Device = InitDevice(); if (Device == NULL) return; - const ALCchar *current = NULL; - if(alcIsExtensionPresent(Device, "ALC_ENUMERATE_ALL_EXT")) - current = alcGetString(Device, ALC_ALL_DEVICES_SPECIFIER); - if(alcGetError(Device) != ALC_NO_ERROR || !current) - current = alcGetString(Device, ALC_DEVICE_SPECIFIER); - Printf(" Opened device " TEXTCOLOR_ORANGE"%s\n", current); + const ALCchar *current = NULL; + if(alcIsExtensionPresent(Device, "ALC_ENUMERATE_ALL_EXT")) + current = alcGetString(Device, ALC_ALL_DEVICES_SPECIFIER); + if(alcGetError(Device) != ALC_NO_ERROR || !current) + current = alcGetString(Device, ALC_DEVICE_SPECIFIER); + Printf(" Opened device " TEXTCOLOR_ORANGE"%s\n", current); - ALCint major=0, minor=0; - alcGetIntegerv(Device, ALC_MAJOR_VERSION, 1, &major); - alcGetIntegerv(Device, ALC_MINOR_VERSION, 1, &minor); - DPrintf(DMSG_SPAMMY, " ALC Version: " TEXTCOLOR_BLUE"%d.%d\n", major, minor); - DPrintf(DMSG_SPAMMY, " ALC Extensions: " TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_EXTENSIONS)); + ALCint major=0, minor=0; + alcGetIntegerv(Device, ALC_MAJOR_VERSION, 1, &major); + alcGetIntegerv(Device, ALC_MINOR_VERSION, 1, &minor); + DPrintf(DMSG_SPAMMY, " ALC Version: " TEXTCOLOR_BLUE"%d.%d\n", major, minor); + DPrintf(DMSG_SPAMMY, " ALC Extensions: " TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_EXTENSIONS)); - TArray attribs; - if(*snd_samplerate > 0) - { - attribs.Push(ALC_FREQUENCY); - attribs.Push(*snd_samplerate); - } - // Make sure one source is capable of stereo output with the rest doing - // mono, without running out of voices - attribs.Push(ALC_MONO_SOURCES); - attribs.Push(MAX(*snd_channels, 2) - 1); - attribs.Push(ALC_STEREO_SOURCES); - attribs.Push(1); - // Other attribs..? - attribs.Push(0); + TArray attribs; + if(*snd_samplerate > 0) + { + attribs.Push(ALC_FREQUENCY); + attribs.Push(*snd_samplerate); + } + // Make sure one source is capable of stereo output with the rest doing + // mono, without running out of voices + attribs.Push(ALC_MONO_SOURCES); + attribs.Push(MAX(*snd_channels, 2) - 1); + attribs.Push(ALC_STEREO_SOURCES); + attribs.Push(1); + // Other attribs..? + attribs.Push(0); - Context = alcCreateContext(Device, &attribs[0]); - if(!Context || alcMakeContextCurrent(Context) == ALC_FALSE) - { - Printf(TEXTCOLOR_RED" Failed to setup context: %s\n", alcGetString(Device, alcGetError(Device))); - if(Context) - alcDestroyContext(Context); - Context = NULL; - alcCloseDevice(Device); - Device = NULL; - return; - } - attribs.Clear(); + Context = alcCreateContext(Device, &attribs[0]); + if(!Context || alcMakeContextCurrent(Context) == ALC_FALSE) + { + Printf(TEXTCOLOR_RED" Failed to setup context: %s\n", alcGetString(Device, alcGetError(Device))); + if(Context) + alcDestroyContext(Context); + Context = NULL; + alcCloseDevice(Device); + Device = NULL; + return; + } + attribs.Clear(); - DPrintf(DMSG_SPAMMY, " Vendor: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VENDOR)); - DPrintf(DMSG_SPAMMY, " Renderer: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_RENDERER)); - DPrintf(DMSG_SPAMMY, " Version: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VERSION)); - DPrintf(DMSG_SPAMMY, " Extensions: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_EXTENSIONS)); + DPrintf(DMSG_SPAMMY, " Vendor: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VENDOR)); + DPrintf(DMSG_SPAMMY, " Renderer: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_RENDERER)); + DPrintf(DMSG_SPAMMY, " Version: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VERSION)); + DPrintf(DMSG_SPAMMY, " Extensions: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_EXTENSIONS)); - ALC.EXT_EFX = !!alcIsExtensionPresent(Device, "ALC_EXT_EFX"); - ALC.EXT_disconnect = !!alcIsExtensionPresent(Device, "ALC_EXT_disconnect"); - ALC.SOFT_pause_device = !!alcIsExtensionPresent(Device, "ALC_SOFT_pause_device"); - AL.EXT_source_distance_model = !!alIsExtensionPresent("AL_EXT_source_distance_model"); - AL.EXT_SOURCE_RADIUS = !!alIsExtensionPresent("AL_EXT_SOURCE_RADIUS"); - AL.SOFT_deferred_updates = !!alIsExtensionPresent("AL_SOFT_deferred_updates"); - AL.SOFT_loop_points = !!alIsExtensionPresent("AL_SOFT_loop_points"); + ALC.EXT_EFX = !!alcIsExtensionPresent(Device, "ALC_EXT_EFX"); + ALC.EXT_disconnect = !!alcIsExtensionPresent(Device, "ALC_EXT_disconnect"); + ALC.SOFT_pause_device = !!alcIsExtensionPresent(Device, "ALC_SOFT_pause_device"); + AL.EXT_source_distance_model = !!alIsExtensionPresent("AL_EXT_source_distance_model"); + AL.EXT_SOURCE_RADIUS = !!alIsExtensionPresent("AL_EXT_SOURCE_RADIUS"); + AL.SOFT_deferred_updates = !!alIsExtensionPresent("AL_SOFT_deferred_updates"); + AL.SOFT_loop_points = !!alIsExtensionPresent("AL_SOFT_loop_points"); - alDopplerFactor(0.5f); - alSpeedOfSound(343.3f * 96.0f); - alDistanceModel(AL_INVERSE_DISTANCE); - if(AL.EXT_source_distance_model) - alEnable(AL_SOURCE_DISTANCE_MODEL); + alDopplerFactor(0.5f); + alSpeedOfSound(343.3f * 96.0f); + alDistanceModel(AL_INVERSE_DISTANCE); + if(AL.EXT_source_distance_model) + alEnable(AL_SOURCE_DISTANCE_MODEL); - if(AL.SOFT_deferred_updates) - { - LOAD_FUNC(alDeferUpdatesSOFT); - LOAD_FUNC(alProcessUpdatesSOFT); - } - else - { - alDeferUpdatesSOFT = _wrap_DeferUpdatesSOFT; - alProcessUpdatesSOFT = _wrap_ProcessUpdatesSOFT; - } + if(AL.SOFT_deferred_updates) + { + LOAD_FUNC(alDeferUpdatesSOFT); + LOAD_FUNC(alProcessUpdatesSOFT); + } + else + { + alDeferUpdatesSOFT = _wrap_DeferUpdatesSOFT; + alProcessUpdatesSOFT = _wrap_ProcessUpdatesSOFT; + } - if(ALC.SOFT_pause_device) - { - LOAD_DEV_FUNC(Device, alcDevicePauseSOFT); - LOAD_DEV_FUNC(Device, alcDeviceResumeSOFT); - } + if(ALC.SOFT_pause_device) + { + LOAD_DEV_FUNC(Device, alcDevicePauseSOFT); + LOAD_DEV_FUNC(Device, alcDeviceResumeSOFT); + } - ALenum err = getALError(); - if(err != AL_NO_ERROR) - { - alcMakeContextCurrent(NULL); - alcDestroyContext(Context); - Context = NULL; - alcCloseDevice(Device); - Device = NULL; - return; - } + ALenum err = getALError(); + if(err != AL_NO_ERROR) + { + alcMakeContextCurrent(NULL); + alcDestroyContext(Context); + Context = NULL; + alcCloseDevice(Device); + Device = NULL; + return; + } - ALCint numMono=0, numStereo=0; - alcGetIntegerv(Device, ALC_MONO_SOURCES, 1, &numMono); - alcGetIntegerv(Device, ALC_STEREO_SOURCES, 1, &numStereo); + ALCint numMono=0, numStereo=0; + alcGetIntegerv(Device, ALC_MONO_SOURCES, 1, &numMono); + alcGetIntegerv(Device, ALC_STEREO_SOURCES, 1, &numStereo); - // OpenAL specification doesn't require alcGetIntegerv() to return - // meaningful values for ALC_MONO_SOURCES and ALC_MONO_SOURCES. - // At least Apple's OpenAL implementation returns zeroes, - // although it can generate reasonable number of sources. + // OpenAL specification doesn't require alcGetIntegerv() to return + // meaningful values for ALC_MONO_SOURCES and ALC_MONO_SOURCES. + // At least Apple's OpenAL implementation returns zeroes, + // although it can generate reasonable number of sources. - const int numChannels = MAX(*snd_channels, 2); - int numSources = numMono + numStereo; + const int numChannels = MAX(*snd_channels, 2); + int numSources = numMono + numStereo; - if (0 == numSources) - { - numSources = numChannels; - } + if (0 == numSources) + { + numSources = numChannels; + } - Sources.Resize(MIN(numChannels, numSources)); - for(unsigned i = 0;i < Sources.Size();i++) - { - alGenSources(1, &Sources[i]); - if(getALError() != AL_NO_ERROR) - { - Sources.Resize(i); - Sources.ShrinkToFit(); - break; - } - } - if(Sources.Size() == 0) - { - Printf(TEXTCOLOR_RED" Error: could not generate any sound sources!\n"); - alcMakeContextCurrent(NULL); - alcDestroyContext(Context); - Context = NULL; - alcCloseDevice(Device); - Device = NULL; - return; - } - FreeSfx = Sources; - DPrintf(DMSG_NOTIFY, " Allocated " TEXTCOLOR_BLUE"%u" TEXTCOLOR_NORMAL" sources\n", Sources.Size()); + Sources.Resize(MIN(numChannels, numSources)); + for(unsigned i = 0;i < Sources.Size();i++) + { + alGenSources(1, &Sources[i]); + if(getALError() != AL_NO_ERROR) + { + Sources.Resize(i); + Sources.ShrinkToFit(); + break; + } + } + if(Sources.Size() == 0) + { + Printf(TEXTCOLOR_RED" Error: could not generate any sound sources!\n"); + alcMakeContextCurrent(NULL); + alcDestroyContext(Context); + Context = NULL; + alcCloseDevice(Device); + Device = NULL; + return; + } + FreeSfx = Sources; + DPrintf(DMSG_NOTIFY, " Allocated " TEXTCOLOR_BLUE"%u" TEXTCOLOR_NORMAL" sources\n", Sources.Size()); - WasInWater = false; - if(*snd_efx && ALC.EXT_EFX) - { - // EFX function pointers - LOAD_FUNC(alGenEffects); - LOAD_FUNC(alDeleteEffects); - LOAD_FUNC(alIsEffect); - LOAD_FUNC(alEffecti); - LOAD_FUNC(alEffectiv); - LOAD_FUNC(alEffectf); - LOAD_FUNC(alEffectfv); - LOAD_FUNC(alGetEffecti); - LOAD_FUNC(alGetEffectiv); - LOAD_FUNC(alGetEffectf); - LOAD_FUNC(alGetEffectfv); + WasInWater = false; + if(*snd_efx && ALC.EXT_EFX) + { + // EFX function pointers + LOAD_FUNC(alGenEffects); + LOAD_FUNC(alDeleteEffects); + LOAD_FUNC(alIsEffect); + LOAD_FUNC(alEffecti); + LOAD_FUNC(alEffectiv); + LOAD_FUNC(alEffectf); + LOAD_FUNC(alEffectfv); + LOAD_FUNC(alGetEffecti); + LOAD_FUNC(alGetEffectiv); + LOAD_FUNC(alGetEffectf); + LOAD_FUNC(alGetEffectfv); - LOAD_FUNC(alGenFilters); - LOAD_FUNC(alDeleteFilters); - LOAD_FUNC(alIsFilter); - LOAD_FUNC(alFilteri); - LOAD_FUNC(alFilteriv); - LOAD_FUNC(alFilterf); - LOAD_FUNC(alFilterfv); - LOAD_FUNC(alGetFilteri); - LOAD_FUNC(alGetFilteriv); - LOAD_FUNC(alGetFilterf); - LOAD_FUNC(alGetFilterfv); + LOAD_FUNC(alGenFilters); + LOAD_FUNC(alDeleteFilters); + LOAD_FUNC(alIsFilter); + LOAD_FUNC(alFilteri); + LOAD_FUNC(alFilteriv); + LOAD_FUNC(alFilterf); + LOAD_FUNC(alFilterfv); + LOAD_FUNC(alGetFilteri); + LOAD_FUNC(alGetFilteriv); + LOAD_FUNC(alGetFilterf); + LOAD_FUNC(alGetFilterfv); - LOAD_FUNC(alGenAuxiliaryEffectSlots); - LOAD_FUNC(alDeleteAuxiliaryEffectSlots); - LOAD_FUNC(alIsAuxiliaryEffectSlot); - LOAD_FUNC(alAuxiliaryEffectSloti); - LOAD_FUNC(alAuxiliaryEffectSlotiv); - LOAD_FUNC(alAuxiliaryEffectSlotf); - LOAD_FUNC(alAuxiliaryEffectSlotfv); - LOAD_FUNC(alGetAuxiliaryEffectSloti); - LOAD_FUNC(alGetAuxiliaryEffectSlotiv); - LOAD_FUNC(alGetAuxiliaryEffectSlotf); - LOAD_FUNC(alGetAuxiliaryEffectSlotfv); - if(getALError() == AL_NO_ERROR) - { - ALuint envReverb; - alGenEffects(1, &envReverb); - if(getALError() == AL_NO_ERROR) - { - alEffecti(envReverb, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); - if(alGetError() == AL_NO_ERROR) - DPrintf(DMSG_SPAMMY, " EAX Reverb found\n"); - alEffecti(envReverb, AL_EFFECT_TYPE, AL_EFFECT_REVERB); - if(alGetError() == AL_NO_ERROR) - DPrintf(DMSG_SPAMMY, " Standard Reverb found\n"); + LOAD_FUNC(alGenAuxiliaryEffectSlots); + LOAD_FUNC(alDeleteAuxiliaryEffectSlots); + LOAD_FUNC(alIsAuxiliaryEffectSlot); + LOAD_FUNC(alAuxiliaryEffectSloti); + LOAD_FUNC(alAuxiliaryEffectSlotiv); + LOAD_FUNC(alAuxiliaryEffectSlotf); + LOAD_FUNC(alAuxiliaryEffectSlotfv); + LOAD_FUNC(alGetAuxiliaryEffectSloti); + LOAD_FUNC(alGetAuxiliaryEffectSlotiv); + LOAD_FUNC(alGetAuxiliaryEffectSlotf); + LOAD_FUNC(alGetAuxiliaryEffectSlotfv); + if(getALError() == AL_NO_ERROR) + { + ALuint envReverb; + alGenEffects(1, &envReverb); + if(getALError() == AL_NO_ERROR) + { + alEffecti(envReverb, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); + if(alGetError() == AL_NO_ERROR) + DPrintf(DMSG_SPAMMY, " EAX Reverb found\n"); + alEffecti(envReverb, AL_EFFECT_TYPE, AL_EFFECT_REVERB); + if(alGetError() == AL_NO_ERROR) + DPrintf(DMSG_SPAMMY, " Standard Reverb found\n"); - alDeleteEffects(1, &envReverb); - getALError(); - } + alDeleteEffects(1, &envReverb); + getALError(); + } - alGenAuxiliaryEffectSlots(1, &EnvSlot); - alGenFilters(2, EnvFilters); - if(getALError() == AL_NO_ERROR) - { - alFilteri(EnvFilters[0], AL_FILTER_TYPE, AL_FILTER_LOWPASS); - alFilteri(EnvFilters[1], AL_FILTER_TYPE, AL_FILTER_LOWPASS); - if(getALError() == AL_NO_ERROR) - DPrintf(DMSG_SPAMMY, " Lowpass found\n"); - else - { - alDeleteFilters(2, EnvFilters); - EnvFilters[0] = EnvFilters[1] = 0; - alDeleteAuxiliaryEffectSlots(1, &EnvSlot); - EnvSlot = 0; - getALError(); - } - } - else - { - alDeleteFilters(2, EnvFilters); - alDeleteAuxiliaryEffectSlots(1, &EnvSlot); - EnvFilters[0] = EnvFilters[1] = 0; - EnvSlot = 0; - getALError(); - } - } - } + alGenAuxiliaryEffectSlots(1, &EnvSlot); + alGenFilters(2, EnvFilters); + if(getALError() == AL_NO_ERROR) + { + alFilteri(EnvFilters[0], AL_FILTER_TYPE, AL_FILTER_LOWPASS); + alFilteri(EnvFilters[1], AL_FILTER_TYPE, AL_FILTER_LOWPASS); + if(getALError() == AL_NO_ERROR) + DPrintf(DMSG_SPAMMY, " Lowpass found\n"); + else + { + alDeleteFilters(2, EnvFilters); + EnvFilters[0] = EnvFilters[1] = 0; + alDeleteAuxiliaryEffectSlots(1, &EnvSlot); + EnvSlot = 0; + getALError(); + } + } + else + { + alDeleteFilters(2, EnvFilters); + alDeleteAuxiliaryEffectSlots(1, &EnvSlot); + EnvFilters[0] = EnvFilters[1] = 0; + EnvSlot = 0; + getALError(); + } + } + } - if(EnvSlot) - Printf(" EFX enabled\n"); + if(EnvSlot) + Printf(" EFX enabled\n"); } #undef LOAD_DEV_FUNC #undef LOAD_FUNC OpenALSoundRenderer::~OpenALSoundRenderer() { - if(!Device) - return; + if(!Device) + return; - if(StreamThread.joinable()) - { - std::unique_lock lock(StreamLock); - QuitThread.store(true); - lock.unlock(); - StreamWake.notify_all(); - StreamThread.join(); - } + if(StreamThread.joinable()) + { + std::unique_lock lock(StreamLock); + QuitThread.store(true); + lock.unlock(); + StreamWake.notify_all(); + StreamThread.join(); + } - while(Streams.Size() > 0) - delete Streams[0]; + while(Streams.Size() > 0) + delete Streams[0]; - alDeleteSources(Sources.Size(), &Sources[0]); - Sources.Clear(); - FreeSfx.Clear(); - SfxGroup.Clear(); - PausableSfx.Clear(); - ReverbSfx.Clear(); + alDeleteSources(Sources.Size(), &Sources[0]); + Sources.Clear(); + FreeSfx.Clear(); + SfxGroup.Clear(); + PausableSfx.Clear(); + ReverbSfx.Clear(); - if(EnvEffects.CountUsed() > 0) - { - EffectMapIter iter(EnvEffects); - EffectMap::Pair *pair; - while(iter.NextPair(pair)) - alDeleteEffects(1, &(pair->Value)); - } - EnvEffects.Clear(); + if(EnvEffects.CountUsed() > 0) + { + EffectMapIter iter(EnvEffects); + EffectMap::Pair *pair; + while(iter.NextPair(pair)) + alDeleteEffects(1, &(pair->Value)); + } + EnvEffects.Clear(); - if(EnvSlot) - { - alDeleteAuxiliaryEffectSlots(1, &EnvSlot); - alDeleteFilters(2, EnvFilters); - } - EnvSlot = 0; - EnvFilters[0] = EnvFilters[1] = 0; + if(EnvSlot) + { + alDeleteAuxiliaryEffectSlots(1, &EnvSlot); + alDeleteFilters(2, EnvFilters); + } + EnvSlot = 0; + EnvFilters[0] = EnvFilters[1] = 0; - alcMakeContextCurrent(NULL); - alcDestroyContext(Context); - Context = NULL; - alcCloseDevice(Device); - Device = NULL; + alcMakeContextCurrent(NULL); + alcDestroyContext(Context); + Context = NULL; + alcCloseDevice(Device); + Device = NULL; } void OpenALSoundRenderer::BackgroundProc() { - std::unique_lock lock(StreamLock); - while(!QuitThread.load()) - { - if(Streams.Size() == 0) - { - // If there's nothing to play, wait indefinitely. - StreamWake.wait(lock); - } - else - { - // Else, process all active streams and sleep for 100ms - for(size_t i = 0;i < Streams.Size();i++) - Streams[i]->Process(); - StreamWake.wait_for(lock, std::chrono::milliseconds(100)); - } - } + std::unique_lock lock(StreamLock); + while(!QuitThread.load()) + { + if(Streams.Size() == 0) + { + // If there's nothing to play, wait indefinitely. + StreamWake.wait(lock); + } + else + { + // Else, process all active streams and sleep for 100ms + for(size_t i = 0;i < Streams.Size();i++) + Streams[i]->Process(); + StreamWake.wait_for(lock, std::chrono::milliseconds(100)); + } + } } void OpenALSoundRenderer::AddStream(OpenALSoundStream *stream) { - std::unique_lock lock(StreamLock); - Streams.Push(stream); - lock.unlock(); - // There's a stream to play, make sure the background thread is aware - StreamWake.notify_all(); + std::unique_lock lock(StreamLock); + Streams.Push(stream); + lock.unlock(); + // There's a stream to play, make sure the background thread is aware + StreamWake.notify_all(); } void OpenALSoundRenderer::RemoveStream(OpenALSoundStream *stream) { - std::unique_lock lock(StreamLock); - unsigned int idx = Streams.Find(stream); - if(idx < Streams.Size()) - Streams.Delete(idx); + std::unique_lock lock(StreamLock); + unsigned int idx = Streams.Find(stream); + if(idx < Streams.Size()) + Streams.Delete(idx); } void OpenALSoundRenderer::SetSfxVolume(float volume) { - SfxVolume = volume; + SfxVolume = volume; - FSoundChan *schan = Channels; - while(schan) - { - if(schan->SysChannel != NULL) - { - ALuint source = GET_PTRID(schan->SysChannel); - volume = SfxVolume; + FSoundChan *schan = Channels; + while(schan) + { + if(schan->SysChannel != NULL) + { + ALuint source = GET_PTRID(schan->SysChannel); + volume = SfxVolume; - alDeferUpdatesSOFT(); - alSourcef(source, AL_MAX_GAIN, volume); - alSourcef(source, AL_GAIN, volume * schan->Volume); - } - schan = schan->NextChan; - } + alDeferUpdatesSOFT(); + alSourcef(source, AL_MAX_GAIN, volume); + alSourcef(source, AL_GAIN, volume * schan->Volume); + } + schan = schan->NextChan; + } - alProcessUpdatesSOFT(); + alProcessUpdatesSOFT(); - getALError(); + getALError(); } void OpenALSoundRenderer::SetMusicVolume(float volume) { - MusicVolume = volume; - for(uint32 i = 0;i < Streams.Size();++i) - Streams[i]->UpdateVolume(); + MusicVolume = volume; + for(uint32 i = 0;i < Streams.Size();++i) + Streams[i]->UpdateVolume(); } unsigned int OpenALSoundRenderer::GetMSLength(SoundHandle sfx) { - if(sfx.data) - { - ALuint buffer = GET_PTRID(sfx.data); - if(alIsBuffer(buffer)) - { - ALint bits, channels, freq, size; - alGetBufferi(buffer, AL_BITS, &bits); - alGetBufferi(buffer, AL_CHANNELS, &channels); - alGetBufferi(buffer, AL_FREQUENCY, &freq); - alGetBufferi(buffer, AL_SIZE, &size); - if(getALError() == AL_NO_ERROR) - return (unsigned int)(size / (channels*bits/8) * 1000. / freq); - } - } - return 0; + if(sfx.data) + { + ALuint buffer = GET_PTRID(sfx.data); + if(alIsBuffer(buffer)) + { + ALint bits, channels, freq, size; + alGetBufferi(buffer, AL_BITS, &bits); + alGetBufferi(buffer, AL_CHANNELS, &channels); + alGetBufferi(buffer, AL_FREQUENCY, &freq); + alGetBufferi(buffer, AL_SIZE, &size); + if(getALError() == AL_NO_ERROR) + return (unsigned int)(size / (channels*bits/8) * 1000. / freq); + } + } + return 0; } unsigned int OpenALSoundRenderer::GetSampleLength(SoundHandle sfx) { - if(sfx.data) - { - ALuint buffer = GET_PTRID(sfx.data); - ALint bits, channels, size; - alGetBufferi(buffer, AL_BITS, &bits); - alGetBufferi(buffer, AL_CHANNELS, &channels); - alGetBufferi(buffer, AL_SIZE, &size); - if(getALError() == AL_NO_ERROR) - return (ALsizei)(size / (channels * bits / 8)); - } - return 0; + if(sfx.data) + { + ALuint buffer = GET_PTRID(sfx.data); + ALint bits, channels, size; + alGetBufferi(buffer, AL_BITS, &bits); + alGetBufferi(buffer, AL_CHANNELS, &channels); + alGetBufferi(buffer, AL_SIZE, &size); + if(getALError() == AL_NO_ERROR) + return (ALsizei)(size / (channels * bits / 8)); + } + return 0; } float OpenALSoundRenderer::GetOutputRate() { - ALCint rate = 44100; // Default, just in case - alcGetIntegerv(Device, ALC_FREQUENCY, 1, &rate); - return (float)rate; + ALCint rate = 44100; // Default, just in case + alcGetIntegerv(Device, ALC_FREQUENCY, 1, &rate); + return (float)rate; } std::pair OpenALSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend, bool monoize) { - SoundHandle retval = { NULL }; + SoundHandle retval = { NULL }; - if(length == 0) return std::make_pair(retval, true); + if(length == 0) return std::make_pair(retval, true); - if(bits == -8) - { - // Simple signed->unsigned conversion - for(int i = 0;i < length;i++) - sfxdata[i] ^= 0x80; - bits = -bits; - } + if(bits == -8) + { + // Simple signed->unsigned conversion + for(int i = 0;i < length;i++) + sfxdata[i] ^= 0x80; + bits = -bits; + } - if(channels > 1 && monoize) - { - size_t frames = length / channels * 8 / bits; - if(bits == 16) - { - for(size_t i = 0;i < frames;i++) - { - int sum = 0; - for(int c = 0;c < channels;c++) - sum += ((short*)sfxdata)[i*channels + c]; - ((short*)sfxdata)[i] = sum / channels; - } - } - else if(bits == 8) - { - for(size_t i = 0;i < frames;i++) - { - int sum = 0; - for(int c = 0;c < channels;c++) - sum += sfxdata[i*channels + c] - 128; - sfxdata[i] = (sum / channels) + 128; - } - } - length /= channels; - channels = 1; - } + if(channels > 1 && monoize) + { + size_t frames = length / channels * 8 / bits; + if(bits == 16) + { + for(size_t i = 0;i < frames;i++) + { + int sum = 0; + for(int c = 0;c < channels;c++) + sum += ((short*)sfxdata)[i*channels + c]; + ((short*)sfxdata)[i] = sum / channels; + } + } + else if(bits == 8) + { + for(size_t i = 0;i < frames;i++) + { + int sum = 0; + for(int c = 0;c < channels;c++) + sum += sfxdata[i*channels + c] - 128; + sfxdata[i] = (sum / channels) + 128; + } + } + length /= channels; + channels = 1; + } - ALenum format = AL_NONE; - if(bits == 16) - { - if(channels == 1) format = AL_FORMAT_MONO16; - if(channels == 2) format = AL_FORMAT_STEREO16; - } - else if(bits == 8) - { - if(channels == 1) format = AL_FORMAT_MONO8; - if(channels == 2) format = AL_FORMAT_STEREO8; - } + ALenum format = AL_NONE; + if(bits == 16) + { + if(channels == 1) format = AL_FORMAT_MONO16; + if(channels == 2) format = AL_FORMAT_STEREO16; + } + else if(bits == 8) + { + if(channels == 1) format = AL_FORMAT_MONO8; + if(channels == 2) format = AL_FORMAT_STEREO8; + } - if(format == AL_NONE || frequency <= 0) - { - Printf("Unhandled format: %d bit, %d channel, %d hz\n", bits, channels, frequency); - return std::make_pair(retval, true); - } - length -= length%(channels*bits/8); + if(format == AL_NONE || frequency <= 0) + { + Printf("Unhandled format: %d bit, %d channel, %d hz\n", bits, channels, frequency); + return std::make_pair(retval, true); + } + length -= length%(channels*bits/8); - ALenum err; - ALuint buffer = 0; - alGenBuffers(1, &buffer); - alBufferData(buffer, format, sfxdata, length, frequency); - if((err=getALError()) != AL_NO_ERROR) - { - Printf("Failed to buffer data: %s\n", alGetString(err)); - alDeleteBuffers(1, &buffer); - getALError(); - return std::make_pair(retval, true); - } + ALenum err; + ALuint buffer = 0; + alGenBuffers(1, &buffer); + alBufferData(buffer, format, sfxdata, length, frequency); + if((err=getALError()) != AL_NO_ERROR) + { + Printf("Failed to buffer data: %s\n", alGetString(err)); + alDeleteBuffers(1, &buffer); + getALError(); + return std::make_pair(retval, true); + } - if((loopstart > 0 || loopend > 0) && AL.SOFT_loop_points) - { - if(loopstart < 0) - loopstart = 0; - if(loopend < loopstart) - loopend = length / (channels*bits/8); + if((loopstart > 0 || loopend > 0) && AL.SOFT_loop_points) + { + if(loopstart < 0) + loopstart = 0; + if(loopend < loopstart) + loopend = length / (channels*bits/8); - ALint loops[2] = { loopstart, loopend }; - DPrintf(DMSG_NOTIFY, "Setting loop points %d -> %d\n", loops[0], loops[1]); - alBufferiv(buffer, AL_LOOP_POINTS_SOFT, loops); - getALError(); - } - else if(loopstart > 0 || loopend > 0) - { - static bool warned = false; - if(!warned) - Printf(DMSG_WARNING, "Loop points not supported!\n"); - warned = true; - } + ALint loops[2] = { loopstart, loopend }; + DPrintf(DMSG_NOTIFY, "Setting loop points %d -> %d\n", loops[0], loops[1]); + alBufferiv(buffer, AL_LOOP_POINTS_SOFT, loops); + getALError(); + } + else if(loopstart > 0 || loopend > 0) + { + static bool warned = false; + if(!warned) + Printf(DMSG_WARNING, "Loop points not supported!\n"); + warned = true; + } - retval.data = MAKE_PTRID(buffer); - return std::make_pair(retval, channels==1); + retval.data = MAKE_PTRID(buffer); + return std::make_pair(retval, channels==1); } std::pair OpenALSoundRenderer::LoadSound(BYTE *sfxdata, int length, bool monoize) { - SoundHandle retval = { NULL }; - MemoryReader reader((const char*)sfxdata, length); - ALenum format = AL_NONE; - ChannelConfig chans; - SampleType type; - int srate; + SoundHandle retval = { NULL }; + MemoryReader reader((const char*)sfxdata, length); + ALenum format = AL_NONE; + ChannelConfig chans; + SampleType type; + int srate; - std::unique_ptr decoder(CreateDecoder(&reader)); - if(!decoder) return std::make_pair(retval, true); + std::unique_ptr decoder(CreateDecoder(&reader)); + if(!decoder) return std::make_pair(retval, true); - decoder->getInfo(&srate, &chans, &type); - if(chans == ChannelConfig_Mono || monoize) - { - if(type == SampleType_UInt8) format = AL_FORMAT_MONO8; - if(type == SampleType_Int16) format = AL_FORMAT_MONO16; - } - else if(chans == ChannelConfig_Stereo) - { - if(type == SampleType_UInt8) format = AL_FORMAT_STEREO8; - if(type == SampleType_Int16) format = AL_FORMAT_STEREO16; - } + decoder->getInfo(&srate, &chans, &type); + if(chans == ChannelConfig_Mono || monoize) + { + if(type == SampleType_UInt8) format = AL_FORMAT_MONO8; + if(type == SampleType_Int16) format = AL_FORMAT_MONO16; + } + else if(chans == ChannelConfig_Stereo) + { + if(type == SampleType_UInt8) format = AL_FORMAT_STEREO8; + if(type == SampleType_Int16) format = AL_FORMAT_STEREO16; + } - if(format == AL_NONE) - { - Printf("Unsupported audio format: %s, %s\n", GetChannelConfigName(chans), - GetSampleTypeName(type)); - return std::make_pair(retval, true); - } + if(format == AL_NONE) + { + Printf("Unsupported audio format: %s, %s\n", GetChannelConfigName(chans), + GetSampleTypeName(type)); + return std::make_pair(retval, true); + } - TArray data = decoder->readAll(); - if(chans != ChannelConfig_Mono && monoize) - { - size_t chancount = GetChannelCount(chans); - size_t frames = data.Size() / chancount / - (type == SampleType_Int16 ? 2 : 1); - if(type == SampleType_Int16) - { - short *sfxdata = (short*)&data[0]; - for(size_t i = 0;i < frames;i++) - { - int sum = 0; - for(size_t c = 0;c < chancount;c++) - sum += sfxdata[i*chancount + c]; - sfxdata[i] = short(sum / chancount); - } - } - else if(type == SampleType_UInt8) - { - BYTE *sfxdata = (BYTE*)&data[0]; - for(size_t i = 0;i < frames;i++) - { - int sum = 0; - for(size_t c = 0;c < chancount;c++) - sum += sfxdata[i*chancount + c] - 128; - sfxdata[i] = BYTE((sum / chancount) + 128); - } - } - data.Resize(unsigned(data.Size()/chancount)); - } + TArray data = decoder->readAll(); + if(chans != ChannelConfig_Mono && monoize) + { + size_t chancount = GetChannelCount(chans); + size_t frames = data.Size() / chancount / + (type == SampleType_Int16 ? 2 : 1); + if(type == SampleType_Int16) + { + short *sfxdata = (short*)&data[0]; + for(size_t i = 0;i < frames;i++) + { + int sum = 0; + for(size_t c = 0;c < chancount;c++) + sum += sfxdata[i*chancount + c]; + sfxdata[i] = short(sum / chancount); + } + } + else if(type == SampleType_UInt8) + { + BYTE *sfxdata = (BYTE*)&data[0]; + for(size_t i = 0;i < frames;i++) + { + int sum = 0; + for(size_t c = 0;c < chancount;c++) + sum += sfxdata[i*chancount + c] - 128; + sfxdata[i] = BYTE((sum / chancount) + 128); + } + } + data.Resize(unsigned(data.Size()/chancount)); + } - ALenum err; - ALuint buffer = 0; - alGenBuffers(1, &buffer); - alBufferData(buffer, format, &data[0], data.Size(), srate); - if((err=getALError()) != AL_NO_ERROR) - { - Printf("Failed to buffer data: %s\n", alGetString(err)); - alDeleteBuffers(1, &buffer); - getALError(); - return std::make_pair(retval, true); - } + ALenum err; + ALuint buffer = 0; + alGenBuffers(1, &buffer); + alBufferData(buffer, format, &data[0], data.Size(), srate); + if((err=getALError()) != AL_NO_ERROR) + { + Printf("Failed to buffer data: %s\n", alGetString(err)); + alDeleteBuffers(1, &buffer); + getALError(); + return std::make_pair(retval, true); + } - retval.data = MAKE_PTRID(buffer); - return std::make_pair(retval, (chans == ChannelConfig_Mono || monoize)); + retval.data = MAKE_PTRID(buffer); + return std::make_pair(retval, (chans == ChannelConfig_Mono || monoize)); } void OpenALSoundRenderer::UnloadSound(SoundHandle sfx) { - if(!sfx.data) - return; + if(!sfx.data) + return; - ALuint buffer = GET_PTRID(sfx.data); - FSoundChan *schan = Channels; - while(schan) - { - if(schan->SysChannel) - { - ALint bufID = 0; - alGetSourcei(GET_PTRID(schan->SysChannel), AL_BUFFER, &bufID); - if((ALuint)bufID == buffer) - { - FSoundChan *next = schan->NextChan; - StopChannel(schan); - schan = next; - continue; - } - } - schan = schan->NextChan; - } + ALuint buffer = GET_PTRID(sfx.data); + FSoundChan *schan = Channels; + while(schan) + { + if(schan->SysChannel) + { + ALint bufID = 0; + alGetSourcei(GET_PTRID(schan->SysChannel), AL_BUFFER, &bufID); + if((ALuint)bufID == buffer) + { + FSoundChan *next = schan->NextChan; + StopChannel(schan); + schan = next; + continue; + } + } + schan = schan->NextChan; + } - alDeleteBuffers(1, &buffer); - getALError(); + alDeleteBuffers(1, &buffer); + getALError(); } SoundStream *OpenALSoundRenderer::CreateStream(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) { - if(StreamThread.get_id() == std::thread::id()) - StreamThread = std::thread(std::mem_fn(&OpenALSoundRenderer::BackgroundProc), this); + if(StreamThread.get_id() == std::thread::id()) + StreamThread = std::thread(std::mem_fn(&OpenALSoundRenderer::BackgroundProc), this); OpenALSoundStream *stream = new OpenALSoundStream(this); if (!stream->Init(callback, buffbytes, flags, samplerate, userdata)) { @@ -1313,8 +1317,8 @@ SoundStream *OpenALSoundRenderer::CreateStream(SoundStreamCallback callback, int SoundStream *OpenALSoundRenderer::OpenStream(FileReader *reader, int flags) { - if(StreamThread.get_id() == std::thread::id()) - StreamThread = std::thread(std::mem_fn(&OpenALSoundRenderer::BackgroundProc), this); + if(StreamThread.get_id() == std::thread::id()) + StreamThread = std::thread(std::mem_fn(&OpenALSoundRenderer::BackgroundProc), this); OpenALSoundStream *stream = new OpenALSoundStream(this); if (!stream->Init(reader, !!(flags&SoundStream::Loop))) { @@ -1326,875 +1330,875 @@ SoundStream *OpenALSoundRenderer::OpenStream(FileReader *reader, int flags) FISoundChannel *OpenALSoundRenderer::StartSound(SoundHandle sfx, float vol, int pitch, int chanflags, FISoundChannel *reuse_chan) { - if(FreeSfx.Size() == 0) - { - FSoundChan *lowest = FindLowestChannel(); - if(lowest) StopChannel(lowest); + if(FreeSfx.Size() == 0) + { + FSoundChan *lowest = FindLowestChannel(); + if(lowest) StopChannel(lowest); - if(FreeSfx.Size() == 0) - return NULL; - } + if(FreeSfx.Size() == 0) + return NULL; + } - ALuint buffer = GET_PTRID(sfx.data); - ALuint source = FreeSfx.Last(); - alSource3f(source, AL_POSITION, 0.f, 0.f, 0.f); - alSource3f(source, AL_VELOCITY, 0.f, 0.f, 0.f); - alSource3f(source, AL_DIRECTION, 0.f, 0.f, 0.f); - alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE); + ALuint buffer = GET_PTRID(sfx.data); + ALuint source = FreeSfx.Last(); + alSource3f(source, AL_POSITION, 0.f, 0.f, 0.f); + alSource3f(source, AL_VELOCITY, 0.f, 0.f, 0.f); + alSource3f(source, AL_DIRECTION, 0.f, 0.f, 0.f); + alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE); - alSourcei(source, AL_LOOPING, (chanflags&SNDF_LOOP) ? AL_TRUE : AL_FALSE); + alSourcei(source, AL_LOOPING, (chanflags&SNDF_LOOP) ? AL_TRUE : AL_FALSE); - alSourcef(source, AL_REFERENCE_DISTANCE, 1.f); - alSourcef(source, AL_MAX_DISTANCE, 1000.f); - alSourcef(source, AL_ROLLOFF_FACTOR, 0.f); - alSourcef(source, AL_MAX_GAIN, SfxVolume); - alSourcef(source, AL_GAIN, SfxVolume*vol); - if(AL.EXT_SOURCE_RADIUS) - alSourcef(source, AL_SOURCE_RADIUS, 0.f); + alSourcef(source, AL_REFERENCE_DISTANCE, 1.f); + alSourcef(source, AL_MAX_DISTANCE, 1000.f); + alSourcef(source, AL_ROLLOFF_FACTOR, 0.f); + alSourcef(source, AL_MAX_GAIN, SfxVolume); + alSourcef(source, AL_GAIN, SfxVolume*vol); + if(AL.EXT_SOURCE_RADIUS) + alSourcef(source, AL_SOURCE_RADIUS, 0.f); - if(EnvSlot) - { - if(!(chanflags&SNDF_NOREVERB)) - { - alSourcei(source, AL_DIRECT_FILTER, EnvFilters[0]); - alSource3i(source, AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); - } - else - { - alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL); - alSource3i(source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); - } - alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, 0.f); - } - if(WasInWater && !(chanflags&SNDF_NOREVERB)) - alSourcef(source, AL_PITCH, PITCH(pitch)*PITCH_MULT); - else - alSourcef(source, AL_PITCH, PITCH(pitch)); + if(EnvSlot) + { + if(!(chanflags&SNDF_NOREVERB)) + { + alSourcei(source, AL_DIRECT_FILTER, EnvFilters[0]); + alSource3i(source, AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); + } + else + { + alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL); + alSource3i(source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); + } + alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, 0.f); + } + if(WasInWater && !(chanflags&SNDF_NOREVERB)) + alSourcef(source, AL_PITCH, PITCH(pitch)*PITCH_MULT); + else + alSourcef(source, AL_PITCH, PITCH(pitch)); - if(!reuse_chan || reuse_chan->StartTime.AsOne == 0) - alSourcef(source, AL_SEC_OFFSET, 0.f); - else - { - if((chanflags&SNDF_ABSTIME)) - alSourcef(source, AL_SEC_OFFSET, reuse_chan->StartTime.Lo/1000.f); - else - { - float offset = std::chrono::duration_cast>( - std::chrono::steady_clock::now().time_since_epoch() - - std::chrono::steady_clock::time_point::duration(reuse_chan->StartTime.AsOne) - ).count(); - if(offset > 0.f) alSourcef(source, AL_SEC_OFFSET, offset); - } - } - if(getALError() != AL_NO_ERROR) - return NULL; + if(!reuse_chan || reuse_chan->StartTime.AsOne == 0) + alSourcef(source, AL_SEC_OFFSET, 0.f); + else + { + if((chanflags&SNDF_ABSTIME)) + alSourcef(source, AL_SEC_OFFSET, reuse_chan->StartTime.Lo/1000.f); + else + { + float offset = std::chrono::duration_cast>( + std::chrono::steady_clock::now().time_since_epoch() - + std::chrono::steady_clock::time_point::duration(reuse_chan->StartTime.AsOne) + ).count(); + if(offset > 0.f) alSourcef(source, AL_SEC_OFFSET, offset); + } + } + if(getALError() != AL_NO_ERROR) + return NULL; - alSourcei(source, AL_BUFFER, buffer); - if((chanflags&SNDF_NOPAUSE) || !SFXPaused) - alSourcePlay(source); - if(getALError() != AL_NO_ERROR) - { - alSourcei(source, AL_BUFFER, 0); - getALError(); - return NULL; - } + alSourcei(source, AL_BUFFER, buffer); + if((chanflags&SNDF_NOPAUSE) || !SFXPaused) + alSourcePlay(source); + if(getALError() != AL_NO_ERROR) + { + alSourcei(source, AL_BUFFER, 0); + getALError(); + return NULL; + } - if(!(chanflags&SNDF_NOREVERB)) - ReverbSfx.Push(source); - if(!(chanflags&SNDF_NOPAUSE)) - PausableSfx.Push(source); - SfxGroup.Push(source); - FreeSfx.Pop(); + if(!(chanflags&SNDF_NOREVERB)) + ReverbSfx.Push(source); + if(!(chanflags&SNDF_NOPAUSE)) + PausableSfx.Push(source); + SfxGroup.Push(source); + FreeSfx.Pop(); - FISoundChannel *chan = reuse_chan; - if(!chan) chan = S_GetChannel(MAKE_PTRID(source)); - else chan->SysChannel = MAKE_PTRID(source); + FISoundChannel *chan = reuse_chan; + if(!chan) chan = S_GetChannel(MAKE_PTRID(source)); + else chan->SysChannel = MAKE_PTRID(source); - chan->Rolloff.RolloffType = ROLLOFF_Log; - chan->Rolloff.RolloffFactor = 0.f; - chan->Rolloff.MinDistance = 1.f; - chan->DistanceSqr = 0.f; - chan->ManualRolloff = false; + chan->Rolloff.RolloffType = ROLLOFF_Log; + chan->Rolloff.RolloffFactor = 0.f; + chan->Rolloff.MinDistance = 1.f; + chan->DistanceSqr = 0.f; + chan->ManualRolloff = false; - return chan; + return chan; } FISoundChannel *OpenALSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener *listener, float vol, - FRolloffInfo *rolloff, float distscale, int pitch, int priority, const FVector3 &pos, const FVector3 &vel, - int channum, int chanflags, FISoundChannel *reuse_chan) + FRolloffInfo *rolloff, float distscale, int pitch, int priority, const FVector3 &pos, const FVector3 &vel, + int channum, int chanflags, FISoundChannel *reuse_chan) { - float dist_sqr = (float)(pos - listener->position).LengthSquared(); + float dist_sqr = (float)(pos - listener->position).LengthSquared(); - if(FreeSfx.Size() == 0) - { - FSoundChan *lowest = FindLowestChannel(); - if(lowest) - { - if(lowest->Priority < priority || (lowest->Priority == priority && - lowest->DistanceSqr > dist_sqr)) - StopChannel(lowest); - } - if(FreeSfx.Size() == 0) - return NULL; - } + if(FreeSfx.Size() == 0) + { + FSoundChan *lowest = FindLowestChannel(); + if(lowest) + { + if(lowest->Priority < priority || (lowest->Priority == priority && + lowest->DistanceSqr > dist_sqr)) + StopChannel(lowest); + } + if(FreeSfx.Size() == 0) + return NULL; + } - bool manualRolloff = true; - ALuint buffer = GET_PTRID(sfx.data); - ALuint source = FreeSfx.Last(); - if(rolloff->RolloffType == ROLLOFF_Log) - { - if(AL.EXT_source_distance_model) - alSourcei(source, AL_DISTANCE_MODEL, AL_INVERSE_DISTANCE); - alSourcef(source, AL_REFERENCE_DISTANCE, rolloff->MinDistance/distscale); - alSourcef(source, AL_MAX_DISTANCE, (1000.f+rolloff->MinDistance)/distscale); - alSourcef(source, AL_ROLLOFF_FACTOR, rolloff->RolloffFactor); - manualRolloff = false; - } - else if(rolloff->RolloffType == ROLLOFF_Linear && AL.EXT_source_distance_model) - { - alSourcei(source, AL_DISTANCE_MODEL, AL_LINEAR_DISTANCE); - alSourcef(source, AL_REFERENCE_DISTANCE, rolloff->MinDistance/distscale); - alSourcef(source, AL_MAX_DISTANCE, rolloff->MaxDistance/distscale); - alSourcef(source, AL_ROLLOFF_FACTOR, 1.f); - manualRolloff = false; - } - if(manualRolloff) - { - // How manual rolloff works: - // - // If a sound is using Custom or Doom style rolloff, or Linear style - // when AL_EXT_source_distance_model is not supported, we have to play - // around a bit to get appropriate distance attenation. What we do is - // calculate the attenuation that should be applied, then given an - // Inverse Distance rolloff model with OpenAL, reverse the calculation - // to get the distance needed for that much attenuation. The Inverse - // Distance calculation is: - // - // Gain = MinDist / (MinDist + RolloffFactor*(Distance - MinDist)) - // - // Thus, the reverse is: - // - // Distance = (MinDist/Gain - MinDist)/RolloffFactor + MinDist - // - // This can be simplified by using a MinDist and RolloffFactor of 1, - // which makes it: - // - // Distance = 1.0f/Gain; - // - // The source position is then set that many units away from the - // listener position, and OpenAL takes care of the rest. - if(AL.EXT_source_distance_model) - alSourcei(source, AL_DISTANCE_MODEL, AL_INVERSE_DISTANCE); - alSourcef(source, AL_REFERENCE_DISTANCE, 1.f); - alSourcef(source, AL_MAX_DISTANCE, 100000.f); - alSourcef(source, AL_ROLLOFF_FACTOR, 1.f); + bool manualRolloff = true; + ALuint buffer = GET_PTRID(sfx.data); + ALuint source = FreeSfx.Last(); + if(rolloff->RolloffType == ROLLOFF_Log) + { + if(AL.EXT_source_distance_model) + alSourcei(source, AL_DISTANCE_MODEL, AL_INVERSE_DISTANCE); + alSourcef(source, AL_REFERENCE_DISTANCE, rolloff->MinDistance/distscale); + alSourcef(source, AL_MAX_DISTANCE, (1000.f+rolloff->MinDistance)/distscale); + alSourcef(source, AL_ROLLOFF_FACTOR, rolloff->RolloffFactor); + manualRolloff = false; + } + else if(rolloff->RolloffType == ROLLOFF_Linear && AL.EXT_source_distance_model) + { + alSourcei(source, AL_DISTANCE_MODEL, AL_LINEAR_DISTANCE); + alSourcef(source, AL_REFERENCE_DISTANCE, rolloff->MinDistance/distscale); + alSourcef(source, AL_MAX_DISTANCE, rolloff->MaxDistance/distscale); + alSourcef(source, AL_ROLLOFF_FACTOR, 1.f); + manualRolloff = false; + } + if(manualRolloff) + { + // How manual rolloff works: + // + // If a sound is using Custom or Doom style rolloff, or Linear style + // when AL_EXT_source_distance_model is not supported, we have to play + // around a bit to get appropriate distance attenation. What we do is + // calculate the attenuation that should be applied, then given an + // Inverse Distance rolloff model with OpenAL, reverse the calculation + // to get the distance needed for that much attenuation. The Inverse + // Distance calculation is: + // + // Gain = MinDist / (MinDist + RolloffFactor*(Distance - MinDist)) + // + // Thus, the reverse is: + // + // Distance = (MinDist/Gain - MinDist)/RolloffFactor + MinDist + // + // This can be simplified by using a MinDist and RolloffFactor of 1, + // which makes it: + // + // Distance = 1.0f/Gain; + // + // The source position is then set that many units away from the + // listener position, and OpenAL takes care of the rest. + if(AL.EXT_source_distance_model) + alSourcei(source, AL_DISTANCE_MODEL, AL_INVERSE_DISTANCE); + alSourcef(source, AL_REFERENCE_DISTANCE, 1.f); + alSourcef(source, AL_MAX_DISTANCE, 100000.f); + alSourcef(source, AL_ROLLOFF_FACTOR, 1.f); - FVector3 dir = pos - listener->position; - if(dir.DoesNotApproximatelyEqual(FVector3(0.f, 0.f, 0.f))) - { - float gain = GetRolloff(rolloff, sqrtf(dist_sqr) * distscale); - dir.MakeResize((gain > 0.00001f) ? 1.f/gain : 100000.f); - } - if(AL.EXT_SOURCE_RADIUS) - { - /* Since the OpenAL distance is decoupled from the sound's distance, get the OpenAL - * distance that corresponds to the area radius. */ - alSourcef(source, AL_SOURCE_RADIUS, (chanflags&SNDF_AREA) ? - // Clamp in case the max distance is <= the area radius - 1.f/MAX(GetRolloff(rolloff, AREA_SOUND_RADIUS), 0.00001f) : 0.f - ); - } - else if((chanflags&SNDF_AREA) && dist_sqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) - { - FVector3 amb(0.f, !(dir.Y>=0.f) ? -1.f : 1.f, 0.f); - float a = sqrtf(dist_sqr) / AREA_SOUND_RADIUS; - dir = amb + (dir-amb)*a; - } - dir += listener->position; + FVector3 dir = pos - listener->position; + if(dir.DoesNotApproximatelyEqual(FVector3(0.f, 0.f, 0.f))) + { + float gain = GetRolloff(rolloff, sqrtf(dist_sqr) * distscale); + dir.MakeResize((gain > 0.00001f) ? 1.f/gain : 100000.f); + } + if(AL.EXT_SOURCE_RADIUS) + { + /* Since the OpenAL distance is decoupled from the sound's distance, get the OpenAL + * distance that corresponds to the area radius. */ + alSourcef(source, AL_SOURCE_RADIUS, (chanflags&SNDF_AREA) ? + // Clamp in case the max distance is <= the area radius + 1.f/MAX(GetRolloff(rolloff, AREA_SOUND_RADIUS), 0.00001f) : 0.f + ); + } + else if((chanflags&SNDF_AREA) && dist_sqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) + { + FVector3 amb(0.f, !(dir.Y>=0.f) ? -1.f : 1.f, 0.f); + float a = sqrtf(dist_sqr) / AREA_SOUND_RADIUS; + dir = amb + (dir-amb)*a; + } + dir += listener->position; - if(dist_sqr < (0.0004f*0.0004f)) - { - // Head relative - alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE); - alSource3f(source, AL_POSITION, 0.f, 0.f, 0.f); - } - else - { - alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE); - alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]); - } - } - else - { - FVector3 dir = pos; - if(AL.EXT_SOURCE_RADIUS) - alSourcef(source, AL_SOURCE_RADIUS, (chanflags&SNDF_AREA) ? AREA_SOUND_RADIUS : 0.f); - else if((chanflags&SNDF_AREA) && dist_sqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) - { - dir -= listener->position; + if(dist_sqr < (0.0004f*0.0004f)) + { + // Head relative + alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE); + alSource3f(source, AL_POSITION, 0.f, 0.f, 0.f); + } + else + { + alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE); + alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]); + } + } + else + { + FVector3 dir = pos; + if(AL.EXT_SOURCE_RADIUS) + alSourcef(source, AL_SOURCE_RADIUS, (chanflags&SNDF_AREA) ? AREA_SOUND_RADIUS : 0.f); + else if((chanflags&SNDF_AREA) && dist_sqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) + { + dir -= listener->position; + + float mindist = rolloff->MinDistance/distscale; + FVector3 amb(0.f, !(dir.Y>=0.f) ? -mindist : mindist, 0.f); + float a = sqrtf(dist_sqr) / AREA_SOUND_RADIUS; + dir = amb + (dir-amb)*a; + + dir += listener->position; + } + if(dist_sqr < (0.0004f*0.0004f)) + { + // Head relative + alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE); + alSource3f(source, AL_POSITION, 0.f, 0.f, 0.f); + } + else + { + alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE); + alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]); + } + } + alSource3f(source, AL_VELOCITY, vel[0], vel[1], -vel[2]); + alSource3f(source, AL_DIRECTION, 0.f, 0.f, 0.f); - float mindist = rolloff->MinDistance/distscale; - FVector3 amb(0.f, !(dir.Y>=0.f) ? -mindist : mindist, 0.f); - float a = sqrtf(dist_sqr) / AREA_SOUND_RADIUS; - dir = amb + (dir-amb)*a; + alSourcei(source, AL_LOOPING, (chanflags&SNDF_LOOP) ? AL_TRUE : AL_FALSE); - dir += listener->position; - } - if(dist_sqr < (0.0004f*0.0004f)) - { - // Head relative - alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE); - alSource3f(source, AL_POSITION, 0.f, 0.f, 0.f); - } - else - { - alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE); - alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]); - } - } - alSource3f(source, AL_VELOCITY, vel[0], vel[1], -vel[2]); - alSource3f(source, AL_DIRECTION, 0.f, 0.f, 0.f); + alSourcef(source, AL_MAX_GAIN, SfxVolume); + alSourcef(source, AL_GAIN, SfxVolume*vol); - alSourcei(source, AL_LOOPING, (chanflags&SNDF_LOOP) ? AL_TRUE : AL_FALSE); + if(EnvSlot) + { + if(!(chanflags&SNDF_NOREVERB)) + { + alSourcei(source, AL_DIRECT_FILTER, EnvFilters[0]); + alSource3i(source, AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); + } + else + { + alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL); + alSource3i(source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); + } + alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, 0.f); + } + if(WasInWater && !(chanflags&SNDF_NOREVERB)) + alSourcef(source, AL_PITCH, PITCH(pitch)*PITCH_MULT); + else + alSourcef(source, AL_PITCH, PITCH(pitch)); - alSourcef(source, AL_MAX_GAIN, SfxVolume); - alSourcef(source, AL_GAIN, SfxVolume*vol); + if(!reuse_chan || reuse_chan->StartTime.AsOne == 0) + alSourcef(source, AL_SEC_OFFSET, 0.f); + else + { + if((chanflags&SNDF_ABSTIME)) + alSourcef(source, AL_SEC_OFFSET, reuse_chan->StartTime.Lo/1000.f); + else + { + float offset = std::chrono::duration_cast>( + std::chrono::steady_clock::now().time_since_epoch() - + std::chrono::steady_clock::time_point::duration(reuse_chan->StartTime.AsOne) + ).count(); + if(offset > 0.f) alSourcef(source, AL_SEC_OFFSET, offset); + } + } + if(getALError() != AL_NO_ERROR) + return NULL; - if(EnvSlot) - { - if(!(chanflags&SNDF_NOREVERB)) - { - alSourcei(source, AL_DIRECT_FILTER, EnvFilters[0]); - alSource3i(source, AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); - } - else - { - alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL); - alSource3i(source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); - } - alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, 0.f); - } - if(WasInWater && !(chanflags&SNDF_NOREVERB)) - alSourcef(source, AL_PITCH, PITCH(pitch)*PITCH_MULT); - else - alSourcef(source, AL_PITCH, PITCH(pitch)); + alSourcei(source, AL_BUFFER, buffer); + if((chanflags&SNDF_NOPAUSE) || !SFXPaused) + alSourcePlay(source); + if(getALError() != AL_NO_ERROR) + { + alSourcei(source, AL_BUFFER, 0); + getALError(); + return NULL; + } - if(!reuse_chan || reuse_chan->StartTime.AsOne == 0) - alSourcef(source, AL_SEC_OFFSET, 0.f); - else - { - if((chanflags&SNDF_ABSTIME)) - alSourcef(source, AL_SEC_OFFSET, reuse_chan->StartTime.Lo/1000.f); - else - { - float offset = std::chrono::duration_cast>( - std::chrono::steady_clock::now().time_since_epoch() - - std::chrono::steady_clock::time_point::duration(reuse_chan->StartTime.AsOne) - ).count(); - if(offset > 0.f) alSourcef(source, AL_SEC_OFFSET, offset); - } - } - if(getALError() != AL_NO_ERROR) - return NULL; + if(!(chanflags&SNDF_NOREVERB)) + ReverbSfx.Push(source); + if(!(chanflags&SNDF_NOPAUSE)) + PausableSfx.Push(source); + SfxGroup.Push(source); + FreeSfx.Pop(); - alSourcei(source, AL_BUFFER, buffer); - if((chanflags&SNDF_NOPAUSE) || !SFXPaused) - alSourcePlay(source); - if(getALError() != AL_NO_ERROR) - { - alSourcei(source, AL_BUFFER, 0); - getALError(); - return NULL; - } + FISoundChannel *chan = reuse_chan; + if(!chan) chan = S_GetChannel(MAKE_PTRID(source)); + else chan->SysChannel = MAKE_PTRID(source); - if(!(chanflags&SNDF_NOREVERB)) - ReverbSfx.Push(source); - if(!(chanflags&SNDF_NOPAUSE)) - PausableSfx.Push(source); - SfxGroup.Push(source); - FreeSfx.Pop(); + chan->Rolloff = *rolloff; + chan->DistanceSqr = dist_sqr; + chan->ManualRolloff = manualRolloff; - FISoundChannel *chan = reuse_chan; - if(!chan) chan = S_GetChannel(MAKE_PTRID(source)); - else chan->SysChannel = MAKE_PTRID(source); - - chan->Rolloff = *rolloff; - chan->DistanceSqr = dist_sqr; - chan->ManualRolloff = manualRolloff; - - return chan; + return chan; } void OpenALSoundRenderer::ChannelVolume(FISoundChannel *chan, float volume) { - if(chan == NULL || chan->SysChannel == NULL) - return; + if(chan == NULL || chan->SysChannel == NULL) + return; - alDeferUpdatesSOFT(); + alDeferUpdatesSOFT(); - ALuint source = GET_PTRID(chan->SysChannel); - alSourcef(source, AL_GAIN, SfxVolume * volume); + ALuint source = GET_PTRID(chan->SysChannel); + alSourcef(source, AL_GAIN, SfxVolume * volume); } void OpenALSoundRenderer::StopChannel(FISoundChannel *chan) { - if(chan == NULL || chan->SysChannel == NULL) - return; + if(chan == NULL || chan->SysChannel == NULL) + return; - ALuint source = GET_PTRID(chan->SysChannel); - // Release first, so it can be properly marked as evicted if it's being - // forcefully killed - S_ChannelEnded(chan); + ALuint source = GET_PTRID(chan->SysChannel); + // Release first, so it can be properly marked as evicted if it's being + // forcefully killed + S_ChannelEnded(chan); - alSourceRewind(source); - alSourcei(source, AL_BUFFER, 0); - getALError(); + alSourceRewind(source); + alSourcei(source, AL_BUFFER, 0); + getALError(); - uint32 i; - if((i=PausableSfx.Find(source)) < PausableSfx.Size()) - PausableSfx.Delete(i); - if((i=ReverbSfx.Find(source)) < ReverbSfx.Size()) - ReverbSfx.Delete(i); + uint32 i; + if((i=PausableSfx.Find(source)) < PausableSfx.Size()) + PausableSfx.Delete(i); + if((i=ReverbSfx.Find(source)) < ReverbSfx.Size()) + ReverbSfx.Delete(i); - SfxGroup.Delete(SfxGroup.Find(source)); - FreeSfx.Push(source); + SfxGroup.Delete(SfxGroup.Find(source)); + FreeSfx.Push(source); } unsigned int OpenALSoundRenderer::GetPosition(FISoundChannel *chan) { - if(chan == NULL || chan->SysChannel == NULL) - return 0; + if(chan == NULL || chan->SysChannel == NULL) + return 0; - ALint pos; - alGetSourcei(GET_PTRID(chan->SysChannel), AL_SAMPLE_OFFSET, &pos); - if(getALError() == AL_NO_ERROR) - return pos; - return 0; + ALint pos; + alGetSourcei(GET_PTRID(chan->SysChannel), AL_SAMPLE_OFFSET, &pos); + if(getALError() == AL_NO_ERROR) + return pos; + return 0; } void OpenALSoundRenderer::SetSfxPaused(bool paused, int slot) { - int oldslots = SFXPaused; + int oldslots = SFXPaused; - if(paused) - { - SFXPaused |= 1 << slot; - if(oldslots == 0 && PausableSfx.Size() > 0) - { - alSourcePausev(PausableSfx.Size(), &PausableSfx[0]); - getALError(); - PurgeStoppedSources(); - } - } - else - { - SFXPaused &= ~(1 << slot); - if(SFXPaused == 0 && oldslots != 0 && PausableSfx.Size() > 0) - { - alSourcePlayv(PausableSfx.Size(), &PausableSfx[0]); - getALError(); - } - } + if(paused) + { + SFXPaused |= 1 << slot; + if(oldslots == 0 && PausableSfx.Size() > 0) + { + alSourcePausev(PausableSfx.Size(), &PausableSfx[0]); + getALError(); + PurgeStoppedSources(); + } + } + else + { + SFXPaused &= ~(1 << slot); + if(SFXPaused == 0 && oldslots != 0 && PausableSfx.Size() > 0) + { + alSourcePlayv(PausableSfx.Size(), &PausableSfx[0]); + getALError(); + } + } } void OpenALSoundRenderer::SetInactive(SoundRenderer::EInactiveState state) { - switch(state) - { - case SoundRenderer::INACTIVE_Active: - alListenerf(AL_GAIN, 1.0f); - if(ALC.SOFT_pause_device) - { - alcDeviceResumeSOFT(Device); - getALCError(Device); - } - break; + switch(state) + { + case SoundRenderer::INACTIVE_Active: + alListenerf(AL_GAIN, 1.0f); + if(ALC.SOFT_pause_device) + { + alcDeviceResumeSOFT(Device); + getALCError(Device); + } + break; - case SoundRenderer::INACTIVE_Complete: - if(ALC.SOFT_pause_device) - { - alcDevicePauseSOFT(Device); - getALCError(Device); - } - /* fall-through */ - case SoundRenderer::INACTIVE_Mute: - alListenerf(AL_GAIN, 0.0f); - break; - } + case SoundRenderer::INACTIVE_Complete: + if(ALC.SOFT_pause_device) + { + alcDevicePauseSOFT(Device); + getALCError(Device); + } + /* fall-through */ + case SoundRenderer::INACTIVE_Mute: + alListenerf(AL_GAIN, 0.0f); + break; + } } void OpenALSoundRenderer::Sync(bool sync) { - if(sync) - { - if(SfxGroup.Size() > 0) - { - alSourcePausev(SfxGroup.Size(), &SfxGroup[0]); - getALError(); - PurgeStoppedSources(); - } - } - else - { - // Might already be something to handle this; basically, get a vector - // of all values in SfxGroup that are not also in PausableSfx (when - // SFXPaused is non-0). - TArray toplay = SfxGroup; - if(SFXPaused) - { - uint32 i = 0; - while(i < toplay.Size()) - { - uint32 p = PausableSfx.Find(toplay[i]); - if(p < PausableSfx.Size()) - toplay.Delete(i); - else - i++; - } - } - if(toplay.Size() > 0) - { - alSourcePlayv(toplay.Size(), &toplay[0]); - getALError(); - } - } + if(sync) + { + if(SfxGroup.Size() > 0) + { + alSourcePausev(SfxGroup.Size(), &SfxGroup[0]); + getALError(); + PurgeStoppedSources(); + } + } + else + { + // Might already be something to handle this; basically, get a vector + // of all values in SfxGroup that are not also in PausableSfx (when + // SFXPaused is non-0). + TArray toplay = SfxGroup; + if(SFXPaused) + { + uint32 i = 0; + while(i < toplay.Size()) + { + uint32 p = PausableSfx.Find(toplay[i]); + if(p < PausableSfx.Size()) + toplay.Delete(i); + else + i++; + } + } + if(toplay.Size() > 0) + { + alSourcePlayv(toplay.Size(), &toplay[0]); + getALError(); + } + } } void OpenALSoundRenderer::UpdateSoundParams3D(SoundListener *listener, FISoundChannel *chan, bool areasound, const FVector3 &pos, const FVector3 &vel) { - if(chan == NULL || chan->SysChannel == NULL) - return; + if(chan == NULL || chan->SysChannel == NULL) + return; - FVector3 dir = pos - listener->position; - chan->DistanceSqr = (float)dir.LengthSquared(); + FVector3 dir = pos - listener->position; + chan->DistanceSqr = (float)dir.LengthSquared(); - if(chan->ManualRolloff) - { - if(!AL.EXT_SOURCE_RADIUS && areasound && - chan->DistanceSqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) - { - FVector3 amb(0.f, !(dir.Y>=0.f) ? -1.f : 1.f, 0.f); - float a = sqrtf(chan->DistanceSqr) / AREA_SOUND_RADIUS; - dir = amb + (dir-amb)*a; - } - if(dir.DoesNotApproximatelyEqual(FVector3(0.f, 0.f, 0.f))) - { - float gain = GetRolloff(&chan->Rolloff, sqrtf(chan->DistanceSqr)*chan->DistanceScale); - dir.MakeResize((gain > 0.00001f) ? 1.f/gain : 100000.f); - } - } - else if(!AL.EXT_SOURCE_RADIUS && areasound && - chan->DistanceSqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) - { - float mindist = chan->Rolloff.MinDistance / chan->DistanceScale; - FVector3 amb(0.f, !(dir.Y>=0.f) ? -mindist : mindist, 0.f); - float a = sqrtf(chan->DistanceSqr) / AREA_SOUND_RADIUS; - dir = amb + (dir-amb)*a; - } - dir += listener->position; + if(chan->ManualRolloff) + { + if(!AL.EXT_SOURCE_RADIUS && areasound && + chan->DistanceSqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) + { + FVector3 amb(0.f, !(dir.Y>=0.f) ? -1.f : 1.f, 0.f); + float a = sqrtf(chan->DistanceSqr) / AREA_SOUND_RADIUS; + dir = amb + (dir-amb)*a; + } + if(dir.DoesNotApproximatelyEqual(FVector3(0.f, 0.f, 0.f))) + { + float gain = GetRolloff(&chan->Rolloff, sqrtf(chan->DistanceSqr)*chan->DistanceScale); + dir.MakeResize((gain > 0.00001f) ? 1.f/gain : 100000.f); + } + } + else if(!AL.EXT_SOURCE_RADIUS && areasound && + chan->DistanceSqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) + { + float mindist = chan->Rolloff.MinDistance / chan->DistanceScale; + FVector3 amb(0.f, !(dir.Y>=0.f) ? -mindist : mindist, 0.f); + float a = sqrtf(chan->DistanceSqr) / AREA_SOUND_RADIUS; + dir = amb + (dir-amb)*a; + } + dir += listener->position; - alDeferUpdatesSOFT(); - ALuint source = GET_PTRID(chan->SysChannel); + alDeferUpdatesSOFT(); + ALuint source = GET_PTRID(chan->SysChannel); - if(chan->DistanceSqr < (0.0004f*0.0004f)) - { - alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE); - alSource3f(source, AL_POSITION, 0.f, 0.f, 0.f); - } - else - { - alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE); - alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]); - } - alSource3f(source, AL_VELOCITY, vel[0], vel[1], -vel[2]); - getALError(); + if(chan->DistanceSqr < (0.0004f*0.0004f)) + { + alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE); + alSource3f(source, AL_POSITION, 0.f, 0.f, 0.f); + } + else + { + alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE); + alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]); + } + alSource3f(source, AL_VELOCITY, vel[0], vel[1], -vel[2]); + getALError(); } void OpenALSoundRenderer::UpdateListener(SoundListener *listener) { - if(!listener->valid) - return; + if(!listener->valid) + return; - alDeferUpdatesSOFT(); + alDeferUpdatesSOFT(); - float angle = listener->angle; - ALfloat orient[6]; - // forward - orient[0] = cosf(angle); - orient[1] = 0.f; - orient[2] = -sinf(angle); - // up - orient[3] = 0.f; - orient[4] = 1.f; - orient[5] = 0.f; + float angle = listener->angle; + ALfloat orient[6]; + // forward + orient[0] = cosf(angle); + orient[1] = 0.f; + orient[2] = -sinf(angle); + // up + orient[3] = 0.f; + orient[4] = 1.f; + orient[5] = 0.f; - alListenerfv(AL_ORIENTATION, orient); - alListener3f(AL_POSITION, listener->position.X, - listener->position.Y, - -listener->position.Z); - alListener3f(AL_VELOCITY, listener->velocity.X, - listener->velocity.Y, - -listener->velocity.Z); - getALError(); + alListenerfv(AL_ORIENTATION, orient); + alListener3f(AL_POSITION, listener->position.X, + listener->position.Y, + -listener->position.Z); + alListener3f(AL_VELOCITY, listener->velocity.X, + listener->velocity.Y, + -listener->velocity.Z); + getALError(); - const ReverbContainer *env = ForcedEnvironment; - if(!env) - { - env = listener->Environment; - if(!env) - env = DefaultEnvironments[0]; - } - if(env != PrevEnvironment || env->Modified) - { - PrevEnvironment = env; - DPrintf(DMSG_NOTIFY, "Reverb Environment %s\n", env->Name); + const ReverbContainer *env = ForcedEnvironment; + if(!env) + { + env = listener->Environment; + if(!env) + env = DefaultEnvironments[0]; + } + if(env != PrevEnvironment || env->Modified) + { + PrevEnvironment = env; + DPrintf(DMSG_NOTIFY, "Reverb Environment %s\n", env->Name); - if(EnvSlot != 0) - LoadReverb(env); + if(EnvSlot != 0) + LoadReverb(env); - const_cast(env)->Modified = false; - } + const_cast(env)->Modified = false; + } - // NOTE: Moving into and out of water will undo pitch variations on sounds. - if(listener->underwater || env->SoftwareWater) - { - if(!WasInWater) - { - WasInWater = true; + // NOTE: Moving into and out of water will undo pitch variations on sounds. + if(listener->underwater || env->SoftwareWater) + { + if(!WasInWater) + { + WasInWater = true; - if(EnvSlot != 0 && *snd_waterreverb) - { - // Find the "Underwater" reverb environment - env = S_FindEnvironment(0x1600); - LoadReverb(env ? env : DefaultEnvironments[0]); + if(EnvSlot != 0 && *snd_waterreverb) + { + // Find the "Underwater" reverb environment + env = S_FindEnvironment(0x1600); + LoadReverb(env ? env : DefaultEnvironments[0]); - alFilterf(EnvFilters[0], AL_LOWPASS_GAIN, 1.f); - alFilterf(EnvFilters[0], AL_LOWPASS_GAINHF, 0.125f); - alFilterf(EnvFilters[1], AL_LOWPASS_GAIN, 1.f); - alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); + alFilterf(EnvFilters[0], AL_LOWPASS_GAIN, 1.f); + alFilterf(EnvFilters[0], AL_LOWPASS_GAINHF, 0.125f); + alFilterf(EnvFilters[1], AL_LOWPASS_GAIN, 1.f); + alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); - // Apply the updated filters on the sources - for(uint32 i = 0;i < ReverbSfx.Size();++i) - { - alSourcei(ReverbSfx[i], AL_DIRECT_FILTER, EnvFilters[0]); - alSource3i(ReverbSfx[i], AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); - } - } + // Apply the updated filters on the sources + for(uint32 i = 0;i < ReverbSfx.Size();++i) + { + alSourcei(ReverbSfx[i], AL_DIRECT_FILTER, EnvFilters[0]); + alSource3i(ReverbSfx[i], AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); + } + } - for(uint32 i = 0;i < ReverbSfx.Size();++i) - alSourcef(ReverbSfx[i], AL_PITCH, PITCH_MULT); - getALError(); - } - } - else if(WasInWater) - { - WasInWater = false; + for(uint32 i = 0;i < ReverbSfx.Size();++i) + alSourcef(ReverbSfx[i], AL_PITCH, PITCH_MULT); + getALError(); + } + } + else if(WasInWater) + { + WasInWater = false; - if(EnvSlot != 0) - { - LoadReverb(env); + if(EnvSlot != 0) + { + LoadReverb(env); - alFilterf(EnvFilters[0], AL_LOWPASS_GAIN, 1.f); - alFilterf(EnvFilters[0], AL_LOWPASS_GAINHF, 1.f); - alFilterf(EnvFilters[1], AL_LOWPASS_GAIN, 1.f); - alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); - for(uint32 i = 0;i < ReverbSfx.Size();++i) - { - alSourcei(ReverbSfx[i], AL_DIRECT_FILTER, EnvFilters[0]); - alSource3i(ReverbSfx[i], AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); - } - } + alFilterf(EnvFilters[0], AL_LOWPASS_GAIN, 1.f); + alFilterf(EnvFilters[0], AL_LOWPASS_GAINHF, 1.f); + alFilterf(EnvFilters[1], AL_LOWPASS_GAIN, 1.f); + alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); + for(uint32 i = 0;i < ReverbSfx.Size();++i) + { + alSourcei(ReverbSfx[i], AL_DIRECT_FILTER, EnvFilters[0]); + alSource3i(ReverbSfx[i], AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); + } + } - for(uint32 i = 0;i < ReverbSfx.Size();++i) - alSourcef(ReverbSfx[i], AL_PITCH, 1.f); - getALError(); - } + for(uint32 i = 0;i < ReverbSfx.Size();++i) + alSourcef(ReverbSfx[i], AL_PITCH, 1.f); + getALError(); + } } void OpenALSoundRenderer::UpdateSounds() { - alProcessUpdatesSOFT(); + alProcessUpdatesSOFT(); - if(ALC.EXT_disconnect) - { - ALCint connected = ALC_TRUE; - alcGetIntegerv(Device, ALC_CONNECTED, 1, &connected); - if(connected == ALC_FALSE) - { - Printf("Sound device disconnected; restarting...\n"); - static char snd_reset[] = "snd_reset"; - AddCommandString(snd_reset); - return; - } - } + if(ALC.EXT_disconnect) + { + ALCint connected = ALC_TRUE; + alcGetIntegerv(Device, ALC_CONNECTED, 1, &connected); + if(connected == ALC_FALSE) + { + Printf("Sound device disconnected; restarting...\n"); + static char snd_reset[] = "snd_reset"; + AddCommandString(snd_reset); + return; + } + } - PurgeStoppedSources(); + PurgeStoppedSources(); } bool OpenALSoundRenderer::IsValid() { - return Device != NULL; + return Device != NULL; } void OpenALSoundRenderer::MarkStartTime(FISoundChannel *chan) { - // FIXME: Get current time (preferably from the audio clock, but the system - // time will have to do) - chan->StartTime.AsOne = std::chrono::steady_clock::now().time_since_epoch().count(); + // FIXME: Get current time (preferably from the audio clock, but the system + // time will have to do) + chan->StartTime.AsOne = std::chrono::steady_clock::now().time_since_epoch().count(); } float OpenALSoundRenderer::GetAudibility(FISoundChannel *chan) { - if(chan == NULL || chan->SysChannel == NULL) - return 0.f; + if(chan == NULL || chan->SysChannel == NULL) + return 0.f; - ALuint source = GET_PTRID(chan->SysChannel); - ALfloat volume = 0.f; + ALuint source = GET_PTRID(chan->SysChannel); + ALfloat volume = 0.f; - alGetSourcef(source, AL_GAIN, &volume); - getALError(); + alGetSourcef(source, AL_GAIN, &volume); + getALError(); - volume *= GetRolloff(&chan->Rolloff, sqrtf(chan->DistanceSqr) * chan->DistanceScale); - return volume; + volume *= GetRolloff(&chan->Rolloff, sqrtf(chan->DistanceSqr) * chan->DistanceScale); + return volume; } void OpenALSoundRenderer::PrintStatus() { - Printf("Output device: " TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_DEVICE_SPECIFIER)); - getALCError(Device); + Printf("Output device: " TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_DEVICE_SPECIFIER)); + getALCError(Device); - ALCint frequency, major, minor, mono, stereo; - alcGetIntegerv(Device, ALC_FREQUENCY, 1, &frequency); - alcGetIntegerv(Device, ALC_MAJOR_VERSION, 1, &major); - alcGetIntegerv(Device, ALC_MINOR_VERSION, 1, &minor); - alcGetIntegerv(Device, ALC_MONO_SOURCES, 1, &mono); - alcGetIntegerv(Device, ALC_STEREO_SOURCES, 1, &stereo); - if(getALCError(Device) == AL_NO_ERROR) - { - Printf("Device sample rate: " TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL"hz\n", frequency); - Printf("ALC Version: " TEXTCOLOR_BLUE"%d.%d\n", major, minor); - Printf("ALC Extensions: " TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_EXTENSIONS)); - Printf("Available sources: " TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL" (" TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL" mono, " TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL" stereo)\n", mono+stereo, mono, stereo); - } - if(!alcIsExtensionPresent(Device, "ALC_EXT_EFX")) - Printf("EFX not found\n"); - else - { - ALCint sends; - alcGetIntegerv(Device, ALC_EFX_MAJOR_VERSION, 1, &major); - alcGetIntegerv(Device, ALC_EFX_MINOR_VERSION, 1, &minor); - alcGetIntegerv(Device, ALC_MAX_AUXILIARY_SENDS, 1, &sends); - if(getALCError(Device) == AL_NO_ERROR) - { - Printf("EFX Version: " TEXTCOLOR_BLUE"%d.%d\n", major, minor); - Printf("Auxiliary sends: " TEXTCOLOR_BLUE"%d\n", sends); - } - } - Printf("Vendor: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VENDOR)); - Printf("Renderer: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_RENDERER)); - Printf("Version: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VERSION)); - Printf("Extensions: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_EXTENSIONS)); - getALError(); + ALCint frequency, major, minor, mono, stereo; + alcGetIntegerv(Device, ALC_FREQUENCY, 1, &frequency); + alcGetIntegerv(Device, ALC_MAJOR_VERSION, 1, &major); + alcGetIntegerv(Device, ALC_MINOR_VERSION, 1, &minor); + alcGetIntegerv(Device, ALC_MONO_SOURCES, 1, &mono); + alcGetIntegerv(Device, ALC_STEREO_SOURCES, 1, &stereo); + if(getALCError(Device) == AL_NO_ERROR) + { + Printf("Device sample rate: " TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL"hz\n", frequency); + Printf("ALC Version: " TEXTCOLOR_BLUE"%d.%d\n", major, minor); + Printf("ALC Extensions: " TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_EXTENSIONS)); + Printf("Available sources: " TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL" (" TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL" mono, " TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL" stereo)\n", mono+stereo, mono, stereo); + } + if(!alcIsExtensionPresent(Device, "ALC_EXT_EFX")) + Printf("EFX not found\n"); + else + { + ALCint sends; + alcGetIntegerv(Device, ALC_EFX_MAJOR_VERSION, 1, &major); + alcGetIntegerv(Device, ALC_EFX_MINOR_VERSION, 1, &minor); + alcGetIntegerv(Device, ALC_MAX_AUXILIARY_SENDS, 1, &sends); + if(getALCError(Device) == AL_NO_ERROR) + { + Printf("EFX Version: " TEXTCOLOR_BLUE"%d.%d\n", major, minor); + Printf("Auxiliary sends: " TEXTCOLOR_BLUE"%d\n", sends); + } + } + Printf("Vendor: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VENDOR)); + Printf("Renderer: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_RENDERER)); + Printf("Version: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VERSION)); + Printf("Extensions: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_EXTENSIONS)); + getALError(); } FString OpenALSoundRenderer::GatherStats() { - ALCint updates = 1; - alcGetIntegerv(Device, ALC_REFRESH, 1, &updates); - getALCError(Device); + ALCint updates = 1; + alcGetIntegerv(Device, ALC_REFRESH, 1, &updates); + getALCError(Device); - uint32 total = Sources.Size(); - uint32 used = SfxGroup.Size()+Streams.Size(); - uint32 unused = FreeSfx.Size(); + uint32 total = Sources.Size(); + uint32 used = SfxGroup.Size()+Streams.Size(); + uint32 unused = FreeSfx.Size(); - FString out; - out.Format("%u sources (" TEXTCOLOR_YELLOW"%u" TEXTCOLOR_NORMAL" active, " TEXTCOLOR_YELLOW"%u" TEXTCOLOR_NORMAL" free), Update interval: " TEXTCOLOR_YELLOW"%d" TEXTCOLOR_NORMAL"ms", - total, used, unused, 1000/updates); - return out; + FString out; + out.Format("%u sources (" TEXTCOLOR_YELLOW"%u" TEXTCOLOR_NORMAL" active, " TEXTCOLOR_YELLOW"%u" TEXTCOLOR_NORMAL" free), Update interval: " TEXTCOLOR_YELLOW"%d" TEXTCOLOR_NORMAL"ms", + total, used, unused, 1000/updates); + return out; } void OpenALSoundRenderer::PrintDriversList() { - const ALCchar *drivers = (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") ? - alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER) : - alcGetString(NULL, ALC_DEVICE_SPECIFIER)); - if(drivers == NULL) - { - Printf(TEXTCOLOR_YELLOW"Failed to retrieve device list: %s\n", alcGetString(NULL, alcGetError(NULL))); - return; - } + const ALCchar *drivers = (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") ? + alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER) : + alcGetString(NULL, ALC_DEVICE_SPECIFIER)); + if(drivers == NULL) + { + Printf(TEXTCOLOR_YELLOW"Failed to retrieve device list: %s\n", alcGetString(NULL, alcGetError(NULL))); + return; + } - const ALCchar *current = NULL; - if(alcIsExtensionPresent(Device, "ALC_ENUMERATE_ALL_EXT")) - current = alcGetString(Device, ALC_ALL_DEVICES_SPECIFIER); - if(alcGetError(Device) != ALC_NO_ERROR || !current) - current = alcGetString(Device, ALC_DEVICE_SPECIFIER); - if(current == NULL) - { - Printf(TEXTCOLOR_YELLOW"Failed to retrieve device name: %s\n", alcGetString(Device, alcGetError(Device))); - return; - } + const ALCchar *current = NULL; + if(alcIsExtensionPresent(Device, "ALC_ENUMERATE_ALL_EXT")) + current = alcGetString(Device, ALC_ALL_DEVICES_SPECIFIER); + if(alcGetError(Device) != ALC_NO_ERROR || !current) + current = alcGetString(Device, ALC_DEVICE_SPECIFIER); + if(current == NULL) + { + Printf(TEXTCOLOR_YELLOW"Failed to retrieve device name: %s\n", alcGetString(Device, alcGetError(Device))); + return; + } - Printf("%c%s%2d. %s\n", ' ', ((strcmp(snd_aldevice, "Default") == 0) ? TEXTCOLOR_BOLD : ""), 0, - "Default"); - for(int i = 1;*drivers;i++) - { - Printf("%c%s%2d. %s\n", ((strcmp(current, drivers)==0) ? '*' : ' '), - ((strcmp(*snd_aldevice, drivers)==0) ? TEXTCOLOR_BOLD : ""), i, - drivers); - drivers += strlen(drivers)+1; - } + Printf("%c%s%2d. %s\n", ' ', ((strcmp(snd_aldevice, "Default") == 0) ? TEXTCOLOR_BOLD : ""), 0, + "Default"); + for(int i = 1;*drivers;i++) + { + Printf("%c%s%2d. %s\n", ((strcmp(current, drivers)==0) ? '*' : ' '), + ((strcmp(*snd_aldevice, drivers)==0) ? TEXTCOLOR_BOLD : ""), i, + drivers); + drivers += strlen(drivers)+1; + } } void OpenALSoundRenderer::PurgeStoppedSources() { - // Release channels that are stopped - for(uint32 i = 0;i < SfxGroup.Size();++i) - { - ALuint src = SfxGroup[i]; - ALint state = AL_INITIAL; - alGetSourcei(src, AL_SOURCE_STATE, &state); - if(state == AL_INITIAL || state == AL_PLAYING || state == AL_PAUSED) - continue; + // Release channels that are stopped + for(uint32 i = 0;i < SfxGroup.Size();++i) + { + ALuint src = SfxGroup[i]; + ALint state = AL_INITIAL; + alGetSourcei(src, AL_SOURCE_STATE, &state); + if(state == AL_INITIAL || state == AL_PLAYING || state == AL_PAUSED) + continue; - FSoundChan *schan = Channels; - while(schan) - { - if(schan->SysChannel != NULL && src == GET_PTRID(schan->SysChannel)) - { - StopChannel(schan); - break; - } - schan = schan->NextChan; - } - } - getALError(); + FSoundChan *schan = Channels; + while(schan) + { + if(schan->SysChannel != NULL && src == GET_PTRID(schan->SysChannel)) + { + StopChannel(schan); + break; + } + schan = schan->NextChan; + } + } + getALError(); } void OpenALSoundRenderer::LoadReverb(const ReverbContainer *env) { - ALuint *envReverb = EnvEffects.CheckKey(env->ID); - bool doLoad = (env->Modified || !envReverb); + ALuint *envReverb = EnvEffects.CheckKey(env->ID); + bool doLoad = (env->Modified || !envReverb); - if(!envReverb) - { - bool ok = false; + if(!envReverb) + { + bool ok = false; - envReverb = &EnvEffects.Insert(env->ID, 0); - alGenEffects(1, envReverb); - if(getALError() == AL_NO_ERROR) - { - alEffecti(*envReverb, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); - ok = (alGetError() == AL_NO_ERROR); - if(!ok) - { - alEffecti(*envReverb, AL_EFFECT_TYPE, AL_EFFECT_REVERB); - ok = (alGetError() == AL_NO_ERROR); - } - if(!ok) - { - alEffecti(*envReverb, AL_EFFECT_TYPE, AL_EFFECT_NULL); - ok = (alGetError() == AL_NO_ERROR); - } - if(!ok) - { - alDeleteEffects(1, envReverb); - getALError(); - } - } - if(!ok) - { - *envReverb = 0; - doLoad = false; - } - } + envReverb = &EnvEffects.Insert(env->ID, 0); + alGenEffects(1, envReverb); + if(getALError() == AL_NO_ERROR) + { + alEffecti(*envReverb, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); + ok = (alGetError() == AL_NO_ERROR); + if(!ok) + { + alEffecti(*envReverb, AL_EFFECT_TYPE, AL_EFFECT_REVERB); + ok = (alGetError() == AL_NO_ERROR); + } + if(!ok) + { + alEffecti(*envReverb, AL_EFFECT_TYPE, AL_EFFECT_NULL); + ok = (alGetError() == AL_NO_ERROR); + } + if(!ok) + { + alDeleteEffects(1, envReverb); + getALError(); + } + } + if(!ok) + { + *envReverb = 0; + doLoad = false; + } + } - if(doLoad) - { - const REVERB_PROPERTIES &props = env->Properties; - ALint type = AL_EFFECT_NULL; + if(doLoad) + { + const REVERB_PROPERTIES &props = env->Properties; + ALint type = AL_EFFECT_NULL; - alGetEffecti(*envReverb, AL_EFFECT_TYPE, &type); + alGetEffecti(*envReverb, AL_EFFECT_TYPE, &type); #define mB2Gain(x) ((float)pow(10., (x)/2000.)) - if(type == AL_EFFECT_EAXREVERB) - { - ALfloat reflectpan[3] = { props.ReflectionsPan0, - props.ReflectionsPan1, - props.ReflectionsPan2 }; - ALfloat latepan[3] = { props.ReverbPan0, props.ReverbPan1, - props.ReverbPan2 }; + if(type == AL_EFFECT_EAXREVERB) + { + ALfloat reflectpan[3] = { props.ReflectionsPan0, + props.ReflectionsPan1, + props.ReflectionsPan2 }; + ALfloat latepan[3] = { props.ReverbPan0, props.ReverbPan1, + props.ReverbPan2 }; #undef SETPARAM #define SETPARAM(e,t,v) alEffectf((e), AL_EAXREVERB_##t, clamp((v), AL_EAXREVERB_MIN_##t, AL_EAXREVERB_MAX_##t)) - SETPARAM(*envReverb, DIFFUSION, props.EnvDiffusion); - SETPARAM(*envReverb, DENSITY, powf(props.EnvSize, 3.0f) * 0.0625f); - SETPARAM(*envReverb, GAIN, mB2Gain(props.Room)); - SETPARAM(*envReverb, GAINHF, mB2Gain(props.RoomHF)); - SETPARAM(*envReverb, GAINLF, mB2Gain(props.RoomLF)); - SETPARAM(*envReverb, DECAY_TIME, props.DecayTime); - SETPARAM(*envReverb, DECAY_HFRATIO, props.DecayHFRatio); - SETPARAM(*envReverb, DECAY_LFRATIO, props.DecayLFRatio); - SETPARAM(*envReverb, REFLECTIONS_GAIN, mB2Gain(props.Reflections)); - SETPARAM(*envReverb, REFLECTIONS_DELAY, props.ReflectionsDelay); - alEffectfv(*envReverb, AL_EAXREVERB_REFLECTIONS_PAN, reflectpan); - SETPARAM(*envReverb, LATE_REVERB_GAIN, mB2Gain(props.Reverb)); - SETPARAM(*envReverb, LATE_REVERB_DELAY, props.ReverbDelay); - alEffectfv(*envReverb, AL_EAXREVERB_LATE_REVERB_PAN, latepan); - SETPARAM(*envReverb, ECHO_TIME, props.EchoTime); - SETPARAM(*envReverb, ECHO_DEPTH, props.EchoDepth); - SETPARAM(*envReverb, MODULATION_TIME, props.ModulationTime); - SETPARAM(*envReverb, MODULATION_DEPTH, props.ModulationDepth); - SETPARAM(*envReverb, AIR_ABSORPTION_GAINHF, mB2Gain(props.AirAbsorptionHF)); - SETPARAM(*envReverb, HFREFERENCE, props.HFReference); - SETPARAM(*envReverb, LFREFERENCE, props.LFReference); - SETPARAM(*envReverb, ROOM_ROLLOFF_FACTOR, props.RoomRolloffFactor); - alEffecti(*envReverb, AL_EAXREVERB_DECAY_HFLIMIT, - (props.Flags&REVERB_FLAGS_DECAYHFLIMIT)?AL_TRUE:AL_FALSE); + SETPARAM(*envReverb, DIFFUSION, props.EnvDiffusion); + SETPARAM(*envReverb, DENSITY, powf(props.EnvSize, 3.0f) * 0.0625f); + SETPARAM(*envReverb, GAIN, mB2Gain(props.Room)); + SETPARAM(*envReverb, GAINHF, mB2Gain(props.RoomHF)); + SETPARAM(*envReverb, GAINLF, mB2Gain(props.RoomLF)); + SETPARAM(*envReverb, DECAY_TIME, props.DecayTime); + SETPARAM(*envReverb, DECAY_HFRATIO, props.DecayHFRatio); + SETPARAM(*envReverb, DECAY_LFRATIO, props.DecayLFRatio); + SETPARAM(*envReverb, REFLECTIONS_GAIN, mB2Gain(props.Reflections)); + SETPARAM(*envReverb, REFLECTIONS_DELAY, props.ReflectionsDelay); + alEffectfv(*envReverb, AL_EAXREVERB_REFLECTIONS_PAN, reflectpan); + SETPARAM(*envReverb, LATE_REVERB_GAIN, mB2Gain(props.Reverb)); + SETPARAM(*envReverb, LATE_REVERB_DELAY, props.ReverbDelay); + alEffectfv(*envReverb, AL_EAXREVERB_LATE_REVERB_PAN, latepan); + SETPARAM(*envReverb, ECHO_TIME, props.EchoTime); + SETPARAM(*envReverb, ECHO_DEPTH, props.EchoDepth); + SETPARAM(*envReverb, MODULATION_TIME, props.ModulationTime); + SETPARAM(*envReverb, MODULATION_DEPTH, props.ModulationDepth); + SETPARAM(*envReverb, AIR_ABSORPTION_GAINHF, mB2Gain(props.AirAbsorptionHF)); + SETPARAM(*envReverb, HFREFERENCE, props.HFReference); + SETPARAM(*envReverb, LFREFERENCE, props.LFReference); + SETPARAM(*envReverb, ROOM_ROLLOFF_FACTOR, props.RoomRolloffFactor); + alEffecti(*envReverb, AL_EAXREVERB_DECAY_HFLIMIT, + (props.Flags&REVERB_FLAGS_DECAYHFLIMIT)?AL_TRUE:AL_FALSE); #undef SETPARAM - } - else if(type == AL_EFFECT_REVERB) - { + } + else if(type == AL_EFFECT_REVERB) + { #define SETPARAM(e,t,v) alEffectf((e), AL_REVERB_##t, clamp((v), AL_REVERB_MIN_##t, AL_REVERB_MAX_##t)) - SETPARAM(*envReverb, DIFFUSION, props.EnvDiffusion); - SETPARAM(*envReverb, DENSITY, powf(props.EnvSize, 3.0f) * 0.0625f); - SETPARAM(*envReverb, GAIN, mB2Gain(props.Room)); - SETPARAM(*envReverb, GAINHF, mB2Gain(props.RoomHF)); - SETPARAM(*envReverb, DECAY_TIME, props.DecayTime); - SETPARAM(*envReverb, DECAY_HFRATIO, props.DecayHFRatio); - SETPARAM(*envReverb, REFLECTIONS_GAIN, mB2Gain(props.Reflections)); - SETPARAM(*envReverb, REFLECTIONS_DELAY, props.ReflectionsDelay); - SETPARAM(*envReverb, LATE_REVERB_GAIN, mB2Gain(props.Reverb)); - SETPARAM(*envReverb, LATE_REVERB_DELAY, props.ReverbDelay); - SETPARAM(*envReverb, AIR_ABSORPTION_GAINHF, mB2Gain(props.AirAbsorptionHF)); - SETPARAM(*envReverb, ROOM_ROLLOFF_FACTOR, props.RoomRolloffFactor); - alEffecti(*envReverb, AL_REVERB_DECAY_HFLIMIT, - (props.Flags&REVERB_FLAGS_DECAYHFLIMIT)?AL_TRUE:AL_FALSE); + SETPARAM(*envReverb, DIFFUSION, props.EnvDiffusion); + SETPARAM(*envReverb, DENSITY, powf(props.EnvSize, 3.0f) * 0.0625f); + SETPARAM(*envReverb, GAIN, mB2Gain(props.Room)); + SETPARAM(*envReverb, GAINHF, mB2Gain(props.RoomHF)); + SETPARAM(*envReverb, DECAY_TIME, props.DecayTime); + SETPARAM(*envReverb, DECAY_HFRATIO, props.DecayHFRatio); + SETPARAM(*envReverb, REFLECTIONS_GAIN, mB2Gain(props.Reflections)); + SETPARAM(*envReverb, REFLECTIONS_DELAY, props.ReflectionsDelay); + SETPARAM(*envReverb, LATE_REVERB_GAIN, mB2Gain(props.Reverb)); + SETPARAM(*envReverb, LATE_REVERB_DELAY, props.ReverbDelay); + SETPARAM(*envReverb, AIR_ABSORPTION_GAINHF, mB2Gain(props.AirAbsorptionHF)); + SETPARAM(*envReverb, ROOM_ROLLOFF_FACTOR, props.RoomRolloffFactor); + alEffecti(*envReverb, AL_REVERB_DECAY_HFLIMIT, + (props.Flags&REVERB_FLAGS_DECAYHFLIMIT)?AL_TRUE:AL_FALSE); #undef SETPARAM - } + } #undef mB2Gain - } + } - alAuxiliaryEffectSloti(EnvSlot, AL_EFFECTSLOT_EFFECT, *envReverb); - getALError(); + alAuxiliaryEffectSloti(EnvSlot, AL_EFFECTSLOT_EFFECT, *envReverb); + getALError(); } FSoundChan *OpenALSoundRenderer::FindLowestChannel() { - FSoundChan *schan = Channels; - FSoundChan *lowest = NULL; - while(schan) - { - if(schan->SysChannel != NULL) - { - if(!lowest || schan->Priority < lowest->Priority || - (schan->Priority == lowest->Priority && - schan->DistanceSqr > lowest->DistanceSqr)) - lowest = schan; - } - schan = schan->NextChan; - } - return lowest; + FSoundChan *schan = Channels; + FSoundChan *lowest = NULL; + while(schan) + { + if(schan->SysChannel != NULL) + { + if(!lowest || schan->Priority < lowest->Priority || + (schan->Priority == lowest->Priority && + schan->DistanceSqr > lowest->DistanceSqr)) + lowest = schan; + } + schan = schan->NextChan; + } + return lowest; } #endif // NO_OPENAL diff --git a/src/statnums.h b/src/statnums.h index 7f691e232a..dd868bbe13 100644 --- a/src/statnums.h +++ b/src/statnums.h @@ -45,6 +45,7 @@ enum STAT_AUTODECAL, // A decal that can be automatically deleted STAT_CORPSEPOINTER, // An entry in Hexen's corpse queue STAT_TRAVELLING, // An actor temporarily travelling to a new map + STAT_STATIC, // persistent across maps. // Thinkers that do think STAT_FIRST_THINKING=32, diff --git a/src/teaminfo.cpp b/src/teaminfo.cpp index 38ba56f4cd..18a2580e2d 100644 --- a/src/teaminfo.cpp +++ b/src/teaminfo.cpp @@ -333,3 +333,6 @@ CCMD (teamlist) Printf ("End of team list.\n"); } + + +DEFINE_FIELD_NAMED(FTeam, m_Name, mName) \ No newline at end of file diff --git a/src/teaminfo.h b/src/teaminfo.h index 41408b546a..1c84d9b9b8 100644 --- a/src/teaminfo.h +++ b/src/teaminfo.h @@ -63,7 +63,9 @@ private: void ParseTeamDefinition (FScanner &Scan); void ClearTeams (); +public: // needed for script access. FString m_Name; +private: int m_iPlayerColor; FString m_TextColor; FString m_Logo; diff --git a/src/textures/backdroptexture.cpp b/src/textures/backdroptexture.cpp index b4b2793dc2..0f18fe6d6c 100644 --- a/src/textures/backdroptexture.cpp +++ b/src/textures/backdroptexture.cpp @@ -289,7 +289,7 @@ void FBackdropTexture::Render() DWORD a1, a2, a3, a4; - SDWORD c1, c2, c3, c4; + int32_t c1, c2, c3, c4; DWORD tx, ty, tc, ts; DWORD ux, uy, uc, us; DWORD ltx, lty, lux, luy; @@ -302,10 +302,10 @@ void FBackdropTexture::Render() double z1 = (cos(TORAD(z2ang)) / 4 + 0.5) * (0x8000000); double z2 = (cos(TORAD(z1ang)) / 4 + 0.75) * (0x8000000); - tc = SDWORD(cos(TORAD(t1ang)) * z1); - ts = SDWORD(sin(TORAD(t1ang)) * z1); - uc = SDWORD(cos(TORAD(t2ang)) * z2); - us = SDWORD(sin(TORAD(t2ang)) * z2); + tc = int32_t(cos(TORAD(t1ang)) * z1); + ts = int32_t(sin(TORAD(t1ang)) * z1); + uc = int32_t(cos(TORAD(t2ang)) * z2); + us = int32_t(sin(TORAD(t2ang)) * z2); ltx = -width / 2 * tc; lty = -width / 2 * ts; @@ -316,8 +316,8 @@ void FBackdropTexture::Render() { a1 = time1; a2 = time2; - c3 = SDWORD(cos(TORAD(a3)) * 65536.0); - c4 = SDWORD(cos(TORAD(a4)) * 65536.0); + c3 = int32_t(cos(TORAD(a3)) * 65536.0); + c4 = int32_t(cos(TORAD(a4)) * 65536.0); tx = ltx - (y - height / 2)*ts; ty = lty + (y - height / 2)*tc; ux = lux - (y - height / 2)*us; diff --git a/src/textures/ddstexture.cpp b/src/textures/ddstexture.cpp index a2c69b38b2..c55cf11bb3 100644 --- a/src/textures/ddstexture.cpp +++ b/src/textures/ddstexture.cpp @@ -126,7 +126,7 @@ struct DDSURFACEDESC2 DWORD Width; union { - SDWORD Pitch; + int32_t Pitch; DWORD LinearSize; }; DWORD Depth; @@ -172,7 +172,7 @@ protected: BYTE RShiftL, GShiftL, BShiftL, AShiftL; BYTE RShiftR, GShiftR, BShiftR, AShiftR; - SDWORD Pitch; + int32_t Pitch; DWORD LinearSize; static void CalcBitShift (DWORD mask, BYTE *lshift, BYTE *rshift); diff --git a/src/timidity/instrum_dls.cpp b/src/timidity/instrum_dls.cpp index fefac19e0c..c882cd84e2 100644 --- a/src/timidity/instrum_dls.cpp +++ b/src/timidity/instrum_dls.cpp @@ -253,7 +253,7 @@ http://www.midi.org/about-midi/dls/dlsspec.shtml #define FAR typedef SWORD SHORT; typedef WORD USHORT; -typedef SDWORD LONG; +typedef int32_t LONG; typedef DWORD ULONG; #define mmioFOURCC MAKE_ID #define DEFINE_GUID(A, B, C, E, F, G, H, I, J, K, L, M) @@ -1075,15 +1075,15 @@ static double to_normalized_percent(int decipercent) } /* convert from 8bit value to fractional offset (15.15) */ -static SDWORD to_offset(int offset) +static int32_t to_offset(int offset) { - return (SDWORD)offset << (7+15); + return (int32_t)offset << (7+15); } /* calculate ramp rate in fractional unit; * diff = 8bit, time = msec */ -static SDWORD calc_rate(Renderer *song, int diff, int sample_rate, double msec) +static int32_t calc_rate(Renderer *song, int diff, int sample_rate, double msec) { double rate; @@ -1093,7 +1093,7 @@ static SDWORD calc_rate(Renderer *song, int diff, int sample_rate, double msec) diff = 255; diff <<= (7+15); rate = ((double)diff / song->rate) * song->control_ratio * 1000.0 / msec; - return (SDWORD)rate; + return (int32_t)rate; } static int load_connection(ULONG cConnections, CONNECTION *artList, USHORT destination) diff --git a/src/timidity/instrum_sf2.cpp b/src/timidity/instrum_sf2.cpp index cae7ae57f6..2e9d7d0c00 100644 --- a/src/timidity/instrum_sf2.cpp +++ b/src/timidity/instrum_sf2.cpp @@ -199,12 +199,12 @@ static double timecent_to_sec(SWORD timecent) return pow(2.0, timecent / 1200.0); } -static SDWORD to_offset(int offset) +static int32_t to_offset(int offset) { - return (SDWORD)offset << (7+15); + return (int32_t)offset << (7+15); } -static SDWORD calc_rate(Renderer *song, int diff, double sec) +static int32_t calc_rate(Renderer *song, int diff, double sec) { double rate; @@ -214,7 +214,7 @@ static SDWORD calc_rate(Renderer *song, int diff, double sec) diff = 255; diff <<= (7+15); rate = ((double)diff / song->rate) * song->control_ratio / sec; - return (SDWORD)rate; + return (int32_t)rate; } @@ -1524,7 +1524,7 @@ void SFFile::LoadSample(SFSample *sample) { BYTE samp; *fp >> samp; - sample->InMemoryData[i] = ((((SDWORD(sample->InMemoryData[i] * 32768) << 8) | samp) << 8) >> 8) / 8388608.f; + sample->InMemoryData[i] = ((((int32_t(sample->InMemoryData[i] * 32768) << 8) | samp) << 8) >> 8) / 8388608.f; } } // Final 0 byte is for interpolation. diff --git a/src/timidity/mix.cpp b/src/timidity/mix.cpp index cd1ede89d8..037226c2c2 100644 --- a/src/timidity/mix.cpp +++ b/src/timidity/mix.cpp @@ -426,7 +426,7 @@ static int update_signal(Voice *v) return 0; } -static void mix_mystery_signal(SDWORD control_ratio, const sample_t *sp, float *lp, Voice *v, int count) +static void mix_mystery_signal(int32_t control_ratio, const sample_t *sp, float *lp, Voice *v, int count) { final_volume_t left = v->left_mix, @@ -477,7 +477,7 @@ static void mix_mystery_signal(SDWORD control_ratio, const sample_t *sp, float * } } -static void mix_single_signal(SDWORD control_ratio, const sample_t *sp, float *lp, Voice *v, float *ampat, int count) +static void mix_single_signal(int32_t control_ratio, const sample_t *sp, float *lp, Voice *v, float *ampat, int count) { final_volume_t amp; int cc; @@ -518,17 +518,17 @@ static void mix_single_signal(SDWORD control_ratio, const sample_t *sp, float *l } } -static void mix_single_left_signal(SDWORD control_ratio, const sample_t *sp, float *lp, Voice *v, int count) +static void mix_single_left_signal(int32_t control_ratio, const sample_t *sp, float *lp, Voice *v, int count) { mix_single_signal(control_ratio, sp, lp, v, &v->left_mix, count); } -static void mix_single_right_signal(SDWORD control_ratio, const sample_t *sp, float *lp, Voice *v, int count) +static void mix_single_right_signal(int32_t control_ratio, const sample_t *sp, float *lp, Voice *v, int count) { mix_single_signal(control_ratio, sp, lp + 1, v, &v->right_mix, count); } -static void mix_mono_signal(SDWORD control_ratio, const sample_t *sp, float *lp, Voice *v, int count) +static void mix_mono_signal(int32_t control_ratio, const sample_t *sp, float *lp, Voice *v, int count) { final_volume_t left = v->left_mix; @@ -568,7 +568,7 @@ static void mix_mono_signal(SDWORD control_ratio, const sample_t *sp, float *lp, } } -static void mix_mystery(SDWORD control_ratio, const sample_t *sp, float *lp, Voice *v, int count) +static void mix_mystery(int32_t control_ratio, const sample_t *sp, float *lp, Voice *v, int count) { final_volume_t left = v->left_mix, diff --git a/src/timidity/timidity.h b/src/timidity/timidity.h index 2ca64a59a6..b3a1e2c809 100644 --- a/src/timidity/timidity.h +++ b/src/timidity/timidity.h @@ -211,7 +211,7 @@ enum struct Sample { - SDWORD + int32_t loop_start, loop_end, data_length, sample_rate; float @@ -233,7 +233,7 @@ struct Sample } sf2; } envelope; sample_t *data; - SDWORD + int32_t tremolo_sweep_increment, tremolo_phase_increment, vibrato_sweep_increment, vibrato_control_ratio; BYTE diff --git a/src/v_draw.cpp b/src/v_draw.cpp index ca5e1fa372..0a9b75b19f 100644 --- a/src/v_draw.cpp +++ b/src/v_draw.cpp @@ -572,7 +572,7 @@ bool DCanvas::ParseDrawTextureTags(FTexture *img, double x, double y, DWORD tag, parms->colorOverlay = 0; parms->alphaChannel = false; parms->flipX = false; - parms->shadowAlpha = 0; + //parms->shadowAlpha = 0; parms->shadowColor = 0; parms->virtWidth = this->GetWidth(); parms->virtHeight = this->GetHeight(); @@ -845,7 +845,7 @@ bool DCanvas::ParseDrawTextureTags(FTexture *img, double x, double y, DWORD tag, break; case DTA_ShadowAlpha: - parms->shadowAlpha = (float)MIN(1., ListGetDouble(tags)); + //parms->shadowAlpha = (float)MIN(1., ListGetDouble(tags)); break; case DTA_ShadowColor: @@ -856,12 +856,12 @@ bool DCanvas::ParseDrawTextureTags(FTexture *img, double x, double y, DWORD tag, boolval = ListGetInt(tags); if (boolval) { - parms->shadowAlpha = 0.5; + //parms->shadowAlpha = 0.5; parms->shadowColor = 0; } else { - parms->shadowAlpha = 0; + //parms->shadowAlpha = 0; } break; diff --git a/src/v_font.cpp b/src/v_font.cpp index a82bd16c17..d668de94ca 100644 --- a/src/v_font.cpp +++ b/src/v_font.cpp @@ -163,7 +163,7 @@ protected: class FSpecialFont : public FFont { public: - FSpecialFont (const char *name, int first, int count, FTexture **lumplist, const bool *notranslate, int lump); + FSpecialFont (const char *name, int first, int count, FTexture **lumplist, const bool *notranslate, int lump, bool donttranslate); void LoadTranslations(); @@ -357,7 +357,7 @@ DEFINE_ACTION_FUNCTION(FFont, GetFont) // //========================================================================== -FFont::FFont (const char *name, const char *nametemplate, int first, int count, int start, int fdlump, int spacewidth) +FFont::FFont (const char *name, const char *nametemplate, int first, int count, int start, int fdlump, int spacewidth, bool notranslate) { int i; FTextureID lump; @@ -367,6 +367,7 @@ FFont::FFont (const char *name, const char *nametemplate, int first, int count, bool doomtemplate = gameinfo.gametype & GAME_DoomChex ? strncmp (nametemplate, "STCFN", 5) == 0 : false; bool stcfn121 = false; + noTranslate = notranslate; Lump = fdlump; Chars = new CharData[count]; charlumps = new FTexture *[count]; @@ -430,7 +431,8 @@ FFont::FFont (const char *name, const char *nametemplate, int first, int count, if (charlumps[i] != NULL) { - Chars[i].Pic = new FFontChar1 (charlumps[i]); + if (!noTranslate) Chars[i].Pic = new FFontChar1 (charlumps[i]); + else Chars[i].Pic = charlumps[i]; Chars[i].XMove = Chars[i].Pic->GetScaledWidth(); } else @@ -455,7 +457,7 @@ FFont::FFont (const char *name, const char *nametemplate, int first, int count, FixXMoves(); - LoadTranslations(); + if (!noTranslate) LoadTranslations(); delete[] charlumps; } @@ -472,11 +474,15 @@ FFont::~FFont () { int count = LastChar - FirstChar + 1; - for (int i = 0; i < count; ++i) + // A noTranslate font directly references the original textures. + if (!noTranslate) { - if (Chars[i].Pic != NULL && Chars[i].Pic->Name[0] == 0) + for (int i = 0; i < count; ++i) { - delete Chars[i].Pic; + if (Chars[i].Pic != NULL && Chars[i].Pic->Name[0] == 0) + { + delete Chars[i].Pic; + } } } delete[] Chars; @@ -752,7 +758,7 @@ void FFont::BuildTranslations (const double *luminosity, const BYTE *identity, FRemapTable *FFont::GetColorTranslation (EColorRange range) const { - if (ActiveColors == 0) + if (ActiveColors == 0 || noTranslate) return NULL; else if (range >= NumTextColors) range = CR_UNTRANSLATED; @@ -1005,6 +1011,7 @@ FFont::FFont (int lump) PatchRemap = NULL; FontName = NAME_None; Cursor = '_'; + noTranslate = false; } //========================================================================== @@ -1962,7 +1969,7 @@ void FFontChar2::MakeTexture () // //========================================================================== -FSpecialFont::FSpecialFont (const char *name, int first, int count, FTexture **lumplist, const bool *notranslate, int lump) : FFont(lump) +FSpecialFont::FSpecialFont (const char *name, int first, int count, FTexture **lumplist, const bool *notranslate, int lump, bool donttranslate) : FFont(lump) { int i; FTexture **charlumps; @@ -1971,6 +1978,7 @@ FSpecialFont::FSpecialFont (const char *name, int first, int count, FTexture **l memcpy(this->notranslate, notranslate, 256*sizeof(bool)); + noTranslate = donttranslate; FontName = name; Chars = new CharData[count]; charlumps = new FTexture*[count]; @@ -2005,7 +2013,8 @@ FSpecialFont::FSpecialFont (const char *name, int first, int count, FTexture **l if (charlumps[i] != NULL) { - Chars[i].Pic = new FFontChar1 (charlumps[i]); + if (!noTranslate) Chars[i].Pic = new FFontChar1 (charlumps[i]); + else Chars[i].Pic = charlumps[i]; Chars[i].XMove = Chars[i].Pic->GetScaledWidth(); } else @@ -2027,7 +2036,7 @@ FSpecialFont::FSpecialFont (const char *name, int first, int count, FTexture **l FixXMoves(); - LoadTranslations(); + if (!noTranslate) LoadTranslations(); delete[] charlumps; } @@ -2158,6 +2167,7 @@ void V_InitCustomFonts() FScanner sc; FTexture *lumplist[256]; bool notranslate[256]; + bool donttranslate; FString namebuffer, templatebuf; int i; int llump,lastlump=0; @@ -2175,6 +2185,7 @@ void V_InitCustomFonts() { memset (lumplist, 0, sizeof(lumplist)); memset (notranslate, 0, sizeof(notranslate)); + donttranslate = false; namebuffer = sc.String; format = 0; start = 33; @@ -2226,6 +2237,10 @@ void V_InitCustomFonts() spacewidth = sc.Number; format = 1; } + else if (sc.Compare("DONTTRANSLATE")) + { + donttranslate = true; + } else if (sc.Compare ("NOTRANSLATION")) { if (format == 1) goto wrong; @@ -2256,7 +2271,7 @@ void V_InitCustomFonts() } if (format == 1) { - FFont *fnt = new FFont (namebuffer, templatebuf, first, count, start, llump, spacewidth); + FFont *fnt = new FFont (namebuffer, templatebuf, first, count, start, llump, spacewidth, donttranslate); fnt->SetCursor(cursor); } else if (format == 2) @@ -2279,7 +2294,7 @@ void V_InitCustomFonts() } if (count > 0) { - FFont *fnt = new FSpecialFont (namebuffer, first, count, &lumplist[first], notranslate, llump); + FFont *fnt = new FSpecialFont (namebuffer, first, count, &lumplist[first], notranslate, llump, donttranslate); fnt->SetCursor(cursor); } } diff --git a/src/v_font.h b/src/v_font.h index 21d773d9d4..ec85f76221 100644 --- a/src/v_font.h +++ b/src/v_font.h @@ -75,7 +75,7 @@ extern int NumTextColors; class FFont { public: - FFont (const char *fontname, const char *nametemplate, int first, int count, int base, int fdlump, int spacewidth=-1); + FFont (const char *fontname, const char *nametemplate, int first, int count, int base, int fdlump, int spacewidth=-1, bool notranslate = false); virtual ~FFont (); virtual FTexture *GetChar (int code, int *const width) const; @@ -100,6 +100,7 @@ public: int GetCharCode(int code, bool needpic) const; char GetCursor() const { return Cursor; } void SetCursor(char c) { Cursor = c; } + bool NoTranslate() const { return noTranslate; } protected: FFont (int lump); @@ -116,6 +117,7 @@ protected: int FontHeight; int GlobalKerning; char Cursor; + bool noTranslate; struct CharData { FTexture *Pic; diff --git a/src/v_video.cpp b/src/v_video.cpp index 6d5913ce14..d993204a27 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -72,6 +72,7 @@ FRenderer *Renderer; IMPLEMENT_CLASS(DCanvas, true, false) IMPLEMENT_CLASS(DFrameBuffer, true, false) +EXTERN_CVAR (Bool, fullscreen) #if defined(_DEBUG) && defined(_M_IX86) && !defined(__MINGW32__) #define DBGBREAK { __asm int 3 } @@ -398,6 +399,20 @@ void DCanvas::Dim (PalEntry color, float damount, int x1, int y1, int w, int h) } } +DEFINE_ACTION_FUNCTION(_Screen, Dim) +{ + PARAM_PROLOGUE; + PARAM_INT(color); + PARAM_FLOAT(amount); + PARAM_INT(x1); + PARAM_INT(y1); + PARAM_INT(w); + PARAM_INT(h); + screen->Dim(color, float(amount), x1, y1, w, h); + return 0; +} + + //========================================================================== // // DCanvas :: GetScreenshotBuffer @@ -1475,6 +1490,9 @@ CCMD (vid_setmode) goodmode = true; } + if (!fullscreen) + goodmode = true; + if (goodmode) { // The actual change of resolution will take place diff --git a/src/v_video.h b/src/v_video.h index 72474ee288..8618f95ba7 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -162,7 +162,7 @@ struct DrawParms uint32 colorOverlay; INTBOOL alphaChannel; INTBOOL flipX; - float shadowAlpha; + //float shadowAlpha; int shadowColor; INTBOOL keepratio; INTBOOL masked; diff --git a/src/version.h b/src/version.h index e50bdfd8d8..d0910f8b40 100644 --- a/src/version.h +++ b/src/version.h @@ -31,6 +31,8 @@ ** */ +#include "gitinfo.h" + #ifndef __VERSION_H__ #define __VERSION_H__ @@ -41,17 +43,21 @@ const char *GetVersionString(); /** Lots of different version numbers **/ +#ifdef GIT_DESCRIPTION +#define VERSIONSTR GIT_DESCRIPTION +#else #define VERSIONSTR "2.3pre" +#endif // The version as seen in the Windows resource #define RC_FILEVERSION 2,3,9999,0 #define RC_PRODUCTVERSION 2,3,9999,0 -#define RC_PRODUCTVERSION2 "2.3pre" +#define RC_PRODUCTVERSION2 VERSIONSTR // Version identifier for network games. // Bump it every time you do a release unless you're certain you // didn't change anything that will affect sync. -#define NETGAMEVERSION 232 +#define NETGAMEVERSION 233 // Version stored in the ini's [LastRun] section. // Bump it if you made some configuration change that you want to @@ -81,8 +87,6 @@ const char *GetVersionString(); // SVN revision ever got. #define SAVEVER 4550 -#define DYNLIGHT - // This is so that derivates can use the same savegame versions without worrying about engine compatibility #define GAMESIG "GZDOOM" #define BASEWAD "gzdoom.pk3" @@ -100,7 +104,4 @@ const char *GetVersionString(); #endif -// The maximum length of one save game description for the menus. -#define SAVESTRINGSIZE 24 - #endif //__VERSION_H__ diff --git a/src/wi_stuff.cpp b/src/wi_stuff.cpp index 73a5b9742e..5542130e6c 100644 --- a/src/wi_stuff.cpp +++ b/src/wi_stuff.cpp @@ -54,237 +54,21 @@ #include "cmdlib.h" #include "g_levellocals.h" +CVAR(Bool, wi_percents, true, CVAR_ARCHIVE) +CVAR(Bool, wi_showtotaltime, true, CVAR_ARCHIVE) +CVAR(Bool, wi_noautostartmap, false, CVAR_USERINFO | CVAR_ARCHIVE) +CVAR(Int, wi_autoadvance, 0, CVAR_SERVERINFO) + // States for the intermission -typedef enum +enum EState { NoState = -1, StatCount, ShowNextLoc, LeavingIntermission -} stateenum_t; - -CVAR (Bool, wi_percents, true, CVAR_ARCHIVE) -CVAR (Bool, wi_showtotaltime, true, CVAR_ARCHIVE) -CVAR (Bool, wi_noautostartmap, false, CVAR_USERINFO|CVAR_ARCHIVE) -CVAR (Int, wi_autoadvance, 0, CVAR_SERVERINFO) - - -void WI_loadData (); -void WI_unloadData (); - -// GLOBAL LOCATIONS -#define WI_TITLEY 2 -#define WI_SPACINGY 33 - -// SINGPLE-PLAYER STUFF -#define SP_STATSX 50 -#define SP_STATSY 50 - -#define SP_TIMEX 8 -#define SP_TIMEY (200-32) - - -// NET GAME STUFF -#define NG_STATSY 50 -#define NG_STATSX (32 + star->GetScaledWidth()/2 + 32*!dofrags) - -#define NG_SPACINGX 64 - - -// DEATHMATCH STUFF -#define DM_MATRIXX 42 -#define DM_MATRIXY 68 - -#define DM_SPACINGX 40 - -#define DM_TOTALSX 269 - -#define DM_KILLERSX 10 -#define DM_KILLERSY 100 -#define DM_VICTIMSX 5 -#define DM_VICTIMSY 50 - -// These animation variables, structures, etc. are used for the -// DOOM/Ultimate DOOM intermission screen animations. This is -// totally different from any sprite or texture/flat animations -typedef enum -{ - ANIM_ALWAYS, // determined by patch entry - ANIM_PIC, // continuous - - // condition bitflags - ANIM_IFVISITED=8, - ANIM_IFNOTVISITED=16, - ANIM_IFENTERING=32, - ANIM_IFNOTENTERING=64, - ANIM_IFLEAVING=128, - ANIM_IFNOTLEAVING=256, - ANIM_IFTRAVELLING=512, - ANIM_IFNOTTRAVELLING=1024, - - ANIM_TYPE=7, - ANIM_CONDITION=~7, - -} animenum_t; - -struct yahpt_t -{ - int x, y; }; -struct lnode_t -{ - int x; // x/y coordinate pair structure - int y; - char level[9]; -} ; - - -#define FACEBACKOFS 4 - - -// -// Animation. -// There is another anim_t used in p_spec. -// (which is why I have renamed this one!) -// - -#define MAX_ANIMATION_FRAMES 20 -struct in_anim_t -{ - int type; // Made an int so I can use '|' - int period; // period in tics between animations - int nanims; // number of animation frames - yahpt_t loc; // location of animation - int data; // ALWAYS: n/a, RANDOM: period deviation (<256) - FTexture * p[MAX_ANIMATION_FRAMES]; // actual graphics for frames of animations - - // following must be initialized to zero before use! - int nexttic; // next value of bcnt (used in conjunction with period) - int ctr; // next frame number to animate - int state; // used by RANDOM and LEVEL when animating - - char levelname[9]; - char levelname2[9]; -}; - -static TArray lnodes; -static TArray anims; - - -// -// GENERAL DATA -// - -// -// Locally used stuff. -// - - -// States for single-player -#define SP_KILLS 0 -#define SP_ITEMS 2 -#define SP_SECRET 4 -#define SP_FRAGS 6 -#define SP_TIME 8 -#define SP_PAR ST_TIME - -#define SP_PAUSE 1 - -#define SHOWNEXTLOCDELAY 4 // in seconds - -static int acceleratestage; // used to accelerate or skip a stage -static bool playerready[MAXPLAYERS]; -static int me; // wbs->pnum -static stateenum_t state; // specifies current state -static wbstartstruct_t *wbs; // contains information passed into intermission -static wbplayerstruct_t*plrs; // wbs->plyr[] -static int cnt; // used for general timing -static int bcnt; // used for timing of background animation -static int cnt_kills[MAXPLAYERS]; -static int cnt_items[MAXPLAYERS]; -static int cnt_secret[MAXPLAYERS]; -static int cnt_frags[MAXPLAYERS]; -static int cnt_deaths[MAXPLAYERS]; -static int cnt_time; -static int cnt_total_time; -static int cnt_par; -static int cnt_pause; -static int total_frags; -static int total_deaths; -static bool noautostartmap; -static int dofrags; -static int ng_state; - -// -// GRAPHICS -// - -struct FPatchInfo -{ - FFont *mFont; - FTexture *mPatch; - EColorRange mColor; - - void Init(FGIFont &gifont) - { - if (gifont.color == NAME_Null) - { - mPatch = TexMan[gifont.fontname]; // "entering" - mColor = mPatch == NULL? CR_UNTRANSLATED : CR_UNDEFINED; - mFont = NULL; - } - else - { - mFont = V_GetFont(gifont.fontname); - mColor = V_FindFontColor(gifont.color); - mPatch = NULL; - } - if (mFont == NULL) - { - mFont = BigFont; - } - } -}; - -static FPatchInfo mapname; -static FPatchInfo finished; -static FPatchInfo entering; - -static TArray yah; // You Are Here graphic -static FTexture* splat; // splat -static FTexture* sp_secret; // "secret" -static FTexture* kills; // "Kills", "Scrt", "Items", "Frags" -static FTexture* secret; -static FTexture* items; -static FTexture* frags; -static FTexture* timepic; // Time sucks. -static FTexture* par; -static FTexture* sucks; -static FTexture* killers; // "killers", "victims" -static FTexture* victims; -static FTexture* total; // "Total", your face, your dead face -//static FTexture* star; -//static FTexture* bstar; -static FTexture* p; // Player graphic -static FTexture* lnames[2]; // Name graphics of each level (centered) - -// [RH] Info to dynamically generate the level name graphics -static FString lnametexts[2]; - -static FTexture *background; - -// -// CODE -// - -// ==================================================================== -// -// Background script commands -// -// ==================================================================== - -static const char *WI_Cmd[]={ +static const char *WI_Cmd[] = { "Background", "Splat", "Pointer", @@ -307,1880 +91,2128 @@ static const char *WI_Cmd[]={ NULL }; -//==================================================================== -// -// Loads the background - either from a single texture -// or an intermission lump. -// Unfortunately the texture manager is incapable of recognizing text -// files so if you use a script you have to prefix its name by '$' in -// MAPINFO. -// -//==================================================================== -static bool IsExMy(const char * name) +struct FInterBackground { - // Only check for the first 3 episodes. They are the only ones with default intermission scripts. - // Level names can be upper- and lower case so use tolower to check! - return (tolower(name[0])=='e' && name[1]>='1' && name[1]<='3' && tolower(name[2])=='m'); -} - -void WI_LoadBackground(bool isenterpic) -{ - const char *lumpname = NULL; - char buffer[10]; - in_anim_t an; - lnode_t pt; - FTextureID texture; - - bcnt=0; - - texture.SetInvalid(); - if (isenterpic) + // These animation variables, structures, etc. are used for the + // DOOM/Ultimate DOOM intermission screen animations. This is + // totally different from any sprite or texture/flat animations + enum EAnim { - level_info_t * li = FindLevelInfo(wbs->next); - if (li != NULL) lumpname = li->EnterPic; - } - else - { - lumpname = level.info->ExitPic; - } + ANIM_ALWAYS, // determined by patch entry + ANIM_PIC, // continuous - // Try to get a default if nothing specified - if (lumpname == NULL || lumpname[0]==0) + // condition bitflags + ANIM_IFVISITED = 8, + ANIM_IFNOTVISITED = 16, + ANIM_IFENTERING = 32, + ANIM_IFNOTENTERING = 64, + ANIM_IFLEAVING = 128, + ANIM_IFNOTLEAVING = 256, + ANIM_IFTRAVELLING = 512, + ANIM_IFNOTTRAVELLING = 1024, + + ANIM_TYPE = 7, + ANIM_CONDITION = ~7, + + }; + + struct yahpt_t { - lumpname = NULL; - switch(gameinfo.gametype) + int x, y; + }; + + struct lnode_t + { + int x; // x/y coordinate pair structure + int y; + FString Level; + }; + + struct in_anim_t + { + int type; // Made an int so I can use '|' + int period; // period in tics between animations + yahpt_t loc; // location of animation + int data; // ALWAYS: n/a, RANDOM: period deviation (<256) + TArray frames; // actual graphics for frames of animations + + // following must be initialized to zero before use! + int nexttic; // next value of bcnt (used in conjunction with period) + int ctr; // next frame number to animate + int state; // used by RANDOM and LEVEL when animating + + FString LevelName; + FString LevelName2; + + void Reset() { - case GAME_Chex: - case GAME_Doom: - if (!(gameinfo.flags & GI_MAPxx)) - { - const char *level = isenterpic ? wbs->next : wbs->current; - if (IsExMy(level)) - { - mysnprintf(buffer, countof(buffer), "$IN_EPI%c", level[1]); - lumpname = buffer; - } - } - if (!lumpname) - { - if (isenterpic) - { - // One special case needs to be handled here! - // If going from E1-E3 to E4 the default should be used, not the exit pic. - - // Not if the exit pic is user defined! - if (level.info->ExitPic.IsNotEmpty()) return; - - // E1-E3 need special treatment when playing Doom 1. - if (!(gameinfo.flags & GI_MAPxx)) - { - // not if the last level is not from the first 3 episodes - if (!IsExMy(wbs->current)) return; - - // not if the next level is one of the first 3 episodes - if (IsExMy(wbs->next)) return; - } - } - lumpname = "INTERPIC"; - } - break; - - case GAME_Heretic: - if (isenterpic) - { - if (IsExMy(wbs->next)) - { - mysnprintf(buffer, countof(buffer), "$IN_HTC%c", wbs->next[1]); - lumpname = buffer; - } - } - if (!lumpname) - { - if (isenterpic) return; - lumpname = "FLOOR16"; - } - break; - - case GAME_Hexen: - if (isenterpic) return; - lumpname = "INTERPIC"; - break; - - case GAME_Strife: - default: - // Strife doesn't have an intermission pic so choose something neutral. - if (isenterpic) return; - lumpname = gameinfo.BorderFlat; - break; + type = period = loc.x = loc.y = data = nexttic = ctr = state = 0; + LevelName = ""; + LevelName2 = ""; + frames.Clear(); } - } - if (lumpname == NULL) + }; + +private: + TArray lnodes; + TArray anims; + int bcnt = 0; // used for timing of background animation + TArray yah; // You Are Here graphic + FTexture* splat = nullptr; // splat + FTexture *background = nullptr; + wbstartstruct_t *wbs; +public: + + FInterBackground(wbstartstruct_t *wbst) { - // shouldn't happen! - background = NULL; - return; + wbs = wbst; + + }; + //==================================================================== + // + // Loads the background - either from a single texture + // or an intermission lump. + // Unfortunately the texture manager is incapable of recognizing text + // files so if you use a script you have to prefix its name by '$' in + // MAPINFO. + // + //==================================================================== + bool IsExMy(const char * name) + { + // Only check for the first 3 episodes. They are the only ones with default intermission scripts. + // Level names can be upper- and lower case so use tolower to check! + return (tolower(name[0]) == 'e' && name[1] >= '1' && name[1] <= '3' && tolower(name[2]) == 'm'); } - lnodes.Clear(); - anims.Clear(); - yah.Clear(); - splat = NULL; + bool LoadBackground(bool isenterpic) + { + const char *lumpname = NULL; + char buffer[10]; + in_anim_t an; + lnode_t pt; + FTextureID texture; + bool noautostartmap = false; - // a name with a starting '$' indicates an intermission script - if (*lumpname!='$') - { - texture = TexMan.CheckForTexture(lumpname, FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny); - } - else - { - int lumpnum=Wads.CheckNumForFullName(lumpname+1, true); - if (lumpnum>=0) + bcnt = 0; + + texture.SetInvalid(); + if (isenterpic) { - FScanner sc(lumpnum); - while (sc.GetString()) + level_info_t * li = FindLevelInfo(wbs->next); + if (li != NULL) lumpname = li->EnterPic; + } + else + { + lumpname = level.info->ExitPic; + } + + // Try to get a default if nothing specified + if (lumpname == NULL || lumpname[0] == 0) + { + lumpname = NULL; + switch (gameinfo.gametype) { - memset(&an,0,sizeof(an)); - int caseval = sc.MustMatchString(WI_Cmd); - switch(caseval) + case GAME_Chex: + case GAME_Doom: + if (!(gameinfo.flags & GI_MAPxx)) { - case 0: // Background - sc.MustGetString(); - texture = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny); - break; - - case 1: // Splat - sc.MustGetString(); - splat = TexMan[sc.String]; - break; - - case 2: // Pointers - while (sc.GetString() && !sc.Crossed) + const char *level = isenterpic ? wbs->next : wbs->current; + if (IsExMy(level)) { - yah.Push(TexMan[sc.String]); + mysnprintf(buffer, countof(buffer), "$IN_EPI%c", level[1]); + lumpname = buffer; } - if (sc.Crossed) - sc.UnGet(); - break; - - case 3: // Spots - sc.MustGetStringName("{"); - while (!sc.CheckString("}")) + } + if (!lumpname) + { + if (isenterpic) { - sc.MustGetString(); - strncpy(pt.level, sc.String,8); - pt.level[8] = 0; - sc.MustGetNumber(); - pt.x = sc.Number; - sc.MustGetNumber(); - pt.y = sc.Number; - lnodes.Push(pt); + // One special case needs to be handled here! + // If going from E1-E3 to E4 the default should be used, not the exit pic. + + // Not if the exit pic is user defined! + if (level.info->ExitPic.IsNotEmpty()) return false; + + // E1-E3 need special treatment when playing Doom 1. + if (!(gameinfo.flags & GI_MAPxx)) + { + // not if the last level is not from the first 3 episodes + if (!IsExMy(wbs->current)) return false; + + // not if the next level is one of the first 3 episodes + if (IsExMy(wbs->next)) return false; + } } - break; + lumpname = "INTERPIC"; + } + break; - case 4: // IfEntering - an.type = ANIM_IFENTERING; - goto readanimation; + case GAME_Heretic: + if (isenterpic) + { + if (IsExMy(wbs->next)) + { + mysnprintf(buffer, countof(buffer), "$IN_HTC%c", wbs->next[1]); + lumpname = buffer; + } + } + if (!lumpname) + { + if (isenterpic) return false; + lumpname = "FLOOR16"; + } + break; - case 5: // IfEntering - an.type = ANIM_IFNOTENTERING; - goto readanimation; + case GAME_Hexen: + if (isenterpic) return false; + lumpname = "INTERPIC"; + break; - case 6: // IfVisited - an.type = ANIM_IFVISITED; - goto readanimation; + case GAME_Strife: + default: + // Strife doesn't have an intermission pic so choose something neutral. + if (isenterpic) return false; + lumpname = gameinfo.BorderFlat; + break; + } + } + if (lumpname == NULL) + { + // shouldn't happen! + background = NULL; + return false; + } - case 7: // IfNotVisited - an.type = ANIM_IFNOTVISITED; - goto readanimation; + lnodes.Clear(); + anims.Clear(); + yah.Clear(); + splat = NULL; - case 8: // IfLeaving - an.type = ANIM_IFLEAVING; - goto readanimation; - - case 9: // IfNotLeaving - an.type = ANIM_IFNOTLEAVING; - goto readanimation; - - case 10: // IfTravelling - an.type = ANIM_IFTRAVELLING; - sc.MustGetString(); - strncpy(an.levelname2, sc.String, 8); - an.levelname2[8] = 0; - goto readanimation; - - case 11: // IfNotTravelling - an.type = ANIM_IFTRAVELLING; - sc.MustGetString(); - strncpy(an.levelname2, sc.String, 8); - an.levelname2[8] = 0; - goto readanimation; - - case 14: // NoAutostartMap - noautostartmap = true; - break; - - readanimation: - sc.MustGetString(); - strncpy(an.levelname, sc.String, 8); - an.levelname[8] = 0; - sc.MustGetString(); - caseval=sc.MustMatchString(WI_Cmd); - - default: + // a name with a starting '$' indicates an intermission script + if (*lumpname != '$') + { + texture = TexMan.CheckForTexture(lumpname, FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny); + } + else + { + int lumpnum = Wads.CheckNumForFullName(lumpname + 1, true); + if (lumpnum >= 0) + { + FScanner sc(lumpnum); + while (sc.GetString()) + { + an.Reset(); + int caseval = sc.MustMatchString(WI_Cmd); switch (caseval) { - case 12: // Animation - an.type |= ANIM_ALWAYS; - sc.MustGetNumber(); - an.loc.x = sc.Number; - sc.MustGetNumber(); - an.loc.y = sc.Number; - sc.MustGetNumber(); - an.period = sc.Number; - an.nexttic = 1 + (M_Random() % an.period); - if (sc.GetString()) + case 0: // Background + sc.MustGetString(); + texture = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny); + break; + + case 1: // Splat + sc.MustGetString(); + splat = TexMan[sc.String]; + break; + + case 2: // Pointers + while (sc.GetString() && !sc.Crossed) { - if (sc.Compare("ONCE")) + yah.Push(TexMan[sc.String]); + } + if (sc.Crossed) + sc.UnGet(); + break; + + case 3: // Spots + sc.MustGetStringName("{"); + while (!sc.CheckString("}")) + { + sc.MustGetString(); + pt.Level = sc.String; + sc.MustGetNumber(); + pt.x = sc.Number; + sc.MustGetNumber(); + pt.y = sc.Number; + lnodes.Push(pt); + } + break; + + case 4: // IfEntering + an.type = ANIM_IFENTERING; + goto readanimation; + + case 5: // IfEntering + an.type = ANIM_IFNOTENTERING; + goto readanimation; + + case 6: // IfVisited + an.type = ANIM_IFVISITED; + goto readanimation; + + case 7: // IfNotVisited + an.type = ANIM_IFNOTVISITED; + goto readanimation; + + case 8: // IfLeaving + an.type = ANIM_IFLEAVING; + goto readanimation; + + case 9: // IfNotLeaving + an.type = ANIM_IFNOTLEAVING; + goto readanimation; + + case 10: // IfTravelling + an.type = ANIM_IFTRAVELLING; + sc.MustGetString(); + an.LevelName2 = sc.String; + goto readanimation; + + case 11: // IfNotTravelling + an.type = ANIM_IFTRAVELLING; + sc.MustGetString(); + an.LevelName2 = sc.String; + goto readanimation; + + case 14: // NoAutostartMap + noautostartmap = true; + break; + + readanimation: + sc.MustGetString(); + an.LevelName = sc.String; + sc.MustGetString(); + caseval = sc.MustMatchString(WI_Cmd); + + default: + switch (caseval) + { + case 12: // Animation + an.type |= ANIM_ALWAYS; + sc.MustGetNumber(); + an.loc.x = sc.Number; + sc.MustGetNumber(); + an.loc.y = sc.Number; + sc.MustGetNumber(); + an.period = sc.Number; + an.nexttic = 1 + (M_Random() % an.period); + if (sc.GetString()) { - an.data = 1; + if (sc.Compare("ONCE")) + { + an.data = 1; + } + else + { + sc.UnGet(); + } + } + if (!sc.CheckString("{")) + { + sc.MustGetString(); + an.frames.Push(TexMan[sc.String]); } else { - sc.UnGet(); + while (!sc.CheckString("}")) + { + sc.MustGetString(); + an.frames.Push(TexMan[sc.String]); + } } - } - if (!sc.CheckString("{")) - { + an.ctr = -1; + anims.Push(an); + break; + + case 13: // Pic + an.type |= ANIM_PIC; + sc.MustGetNumber(); + an.loc.x = sc.Number; + sc.MustGetNumber(); + an.loc.y = sc.Number; sc.MustGetString(); - an.p[an.nanims++] = TexMan[sc.String]; - } - else - { - while (!sc.CheckString("}")) - { - sc.MustGetString(); - if (an.nanimstype & ANIM_TYPE) - { - case ANIM_ALWAYS: - if (bcnt >= a->nexttic) + else { - if (++a->ctr >= a->nanims) + Printf("Intermission script %s not found!\n", lumpname + 1); + texture = TexMan.GetTexture("INTERPIC", FTexture::TEX_MiscPatch); + } + } + background = TexMan[texture]; + return noautostartmap; + } + + //==================================================================== + // + // made this more generic and configurable through a script + // Removed all the ugly special case handling for different game modes + // + //==================================================================== + + void updateAnimatedBack() + { + unsigned int i; + + bcnt++; + for (i = 0; itype & ANIM_TYPE) + { + case ANIM_ALWAYS: + if (bcnt >= a->nexttic) { - if (a->data==0) a->ctr = 0; - else a->ctr--; + if (++a->ctr >= (int)a->frames.Size()) + { + if (a->data == 0) a->ctr = 0; + else a->ctr--; + } + a->nexttic = bcnt + a->period; } - a->nexttic = bcnt + a->period; + break; + + case ANIM_PIC: + a->ctr = 0; + break; + } - break; - - case ANIM_PIC: - a->ctr = 0; - break; - } } -} -//==================================================================== -// -// Draws the background including all animations -// -//==================================================================== + //==================================================================== + // + // Draws the background including all animations + // + //==================================================================== -void WI_drawBackground() -{ - unsigned int i; - double animwidth=320; // For a flat fill or clear background scale animations to 320x200 - double animheight=200; - - if (background) + void drawBackground(int state, bool drawsplat, bool snl_pointeron) { - // background - if (background->UseType == FTexture::TEX_MiscPatch) + unsigned int i; + double animwidth = 320; // For a flat fill or clear background scale animations to 320x200 + double animheight = 200; + + if (background) { - // scale all animations below to fit the size of the base pic - // The base pic is always scaled to fit the screen so this allows - // placing the animations precisely where they belong on the base pic - animwidth = background->GetScaledWidthDouble(); - animheight = background->GetScaledHeightDouble(); - screen->FillBorder (NULL); - screen->DrawTexture(background, 0, 0, DTA_Fullscreen, true, TAG_DONE); + // background + if (background->UseType == FTexture::TEX_MiscPatch) + { + // scale all animations below to fit the size of the base pic + // The base pic is always scaled to fit the screen so this allows + // placing the animations precisely where they belong on the base pic + animwidth = background->GetScaledWidthDouble(); + animheight = background->GetScaledHeightDouble(); + screen->FillBorder(NULL); + screen->DrawTexture(background, 0, 0, DTA_Fullscreen, true, TAG_DONE); + } + else + { + screen->FlatFill(0, 0, SCREENWIDTH, SCREENHEIGHT, background); + } + } + else + { + screen->Clear(0, 0, SCREENWIDTH, SCREENHEIGHT, 0, 0); + } + + for (i = 0; itype & ANIM_CONDITION) + { + case ANIM_IFVISITED: + li = FindLevelInfo(a->LevelName); + if (li == NULL || !(li->flags & LEVEL_VISITED)) continue; + break; + + case ANIM_IFNOTVISITED: + li = FindLevelInfo(a->LevelName); + if (li == NULL || (li->flags & LEVEL_VISITED)) continue; + break; + + // StatCount means 'leaving' - everything else means 'entering'! + case ANIM_IFENTERING: + if (state == StatCount || strnicmp(a->LevelName, wbs->next, 8)) continue; + break; + + case ANIM_IFNOTENTERING: + if (state != StatCount && !strnicmp(a->LevelName, wbs->next, 8)) continue; + break; + + case ANIM_IFLEAVING: + if (state != StatCount || strnicmp(a->LevelName, wbs->current, 8)) continue; + break; + + case ANIM_IFNOTLEAVING: + if (state == StatCount && !strnicmp(a->LevelName, wbs->current, 8)) continue; + break; + + case ANIM_IFTRAVELLING: + if (strnicmp(a->LevelName2, wbs->current, 8) || strnicmp(a->LevelName, wbs->next, 8)) continue; + break; + + case ANIM_IFNOTTRAVELLING: + if (!strnicmp(a->LevelName2, wbs->current, 8) && !strnicmp(a->LevelName, wbs->next, 8)) continue; + break; + } + if (a->ctr >= 0) + screen->DrawTexture(a->frames[a->ctr], a->loc.x, a->loc.y, + DTA_VirtualWidthF, animwidth, DTA_VirtualHeightF, animheight, TAG_DONE); + } + + if (drawsplat) + { + for (i = 0; iflags & LEVEL_VISITED) drawOnLnode(i, &splat, 1); // draw a splat on taken cities. + } + } + + // draw flashing ptr + if (snl_pointeron && yah.Size()) + { + unsigned int v = MapToIndex(wbs->next); + // Draw only if it points to a valid level on the current screen! + if (vGetScaledWidth(); + bottom = c[i]->GetScaledHeight(); + left = lnodes[n].x - c[i]->GetScaledLeftOffset(); + top = lnodes[n].y - c[i]->GetScaledTopOffset(); + right += left; + bottom += top; + + if (left >= 0 && right < 320 && top >= 0 && bottom < 200) + { + screen->DrawTexture(c[i], lnodes[n].x, lnodes[n].y, DTA_320x200, true, TAG_DONE); + break; + } + } + } + + +}; + +struct FPatchInfo +{ + FFont *mFont; + FTexture *mPatch; + EColorRange mColor; + + void Init(FGIFont &gifont) + { + if (gifont.color == NAME_Null) + { + mPatch = TexMan[gifont.fontname]; // "entering" + mColor = mPatch == NULL ? CR_UNTRANSLATED : CR_UNDEFINED; + mFont = NULL; + } + else + { + mFont = V_GetFont(gifont.fontname); + mColor = V_FindFontColor(gifont.color); + mPatch = NULL; + } + if (mFont == NULL) + { + mFont = BigFont; + } + } +}; + + + +class FIntermissionScreen +{ +public: + + enum EValues + { + // GLOBAL LOCATIONS + WI_TITLEY = 2, + + // SINGPLE-PLAYER STUFF + SP_STATSX = 50, + SP_STATSY = 50, + + SP_TIMEX = 8, + SP_TIMEY = (200 - 32), + + // NET GAME STUFF + NG_STATSY = 50, + }; + + + + // States for single-player + enum ESPState + { + SP_KILLS = 0, + SP_ITEMS = 2, + SP_SECRET = 4, + SP_FRAGS = 6, + SP_TIME = 8, + }; + + static const int SHOWNEXTLOCDELAY = 4; // in seconds + + // + // Animation. + // There is another anim_t used in p_spec. + // (which is why I have renamed this one!) + // + + + FInterBackground *bg; + int acceleratestage; // used to accelerate or skip a stage + bool playerready[MAXPLAYERS]; + int me; // wbs->pnum + int bcnt; + EState state; // specifies current state + wbstartstruct_t *wbs; // contains information passed into intermission + wbplayerstruct_t* Plrs[MAXPLAYERS]; // wbs->plyr[] + int cnt; // used for general timing + int cnt_kills[MAXPLAYERS]; + int cnt_items[MAXPLAYERS]; + int cnt_secret[MAXPLAYERS]; + int cnt_frags[MAXPLAYERS]; + int cnt_deaths[MAXPLAYERS]; + int cnt_time; + int cnt_total_time; + int cnt_par; + int cnt_pause; + int total_frags; + int total_deaths; + bool noautostartmap; + int dofrags; + int ng_state; + float shadowalpha; + + // + // GRAPHICS + // + + FPatchInfo mapname; + FPatchInfo finished; + FPatchInfo entering; + + FTexture* sp_secret; // "secret" + FTexture* kills; // "Kills", "Scrt", "Items", "Frags" + FTexture* secret; + FTexture* items; + FTexture* frags; + FTexture* timepic; // Time sucks. + FTexture* par; + FTexture* sucks; + FTexture* killers; // "killers", "victims" + FTexture* victims; + FTexture* total; // "Total", your face, your dead face + FTexture* p; // Player graphic + FTexture* lnames[2]; // Name graphics of each level (centered) + + // [RH] Info to dynamically generate the level name graphics + FString lnametexts[2]; + + + bool snl_pointeron = false; + + int player_deaths[MAXPLAYERS]; + int sp_state; + + // + // CODE + // + + + + + //==================================================================== + // + // CheckRealHeight + // + // Checks the posts in a texture and returns the lowest row (plus one) + // of the texture that is actually used. + // + //==================================================================== + + int CheckRealHeight(FTexture *tex) + { + const FTexture::Span *span; + int maxy = 0, miny = tex->GetHeight(); + + for (int i = 0; i < tex->GetWidth(); ++i) + { + tex->GetColumn(i, &span); + while (span->Length != 0) + { + if (span->TopOffset < miny) + { + miny = span->TopOffset; + } + if (span->TopOffset + span->Length > maxy) + { + maxy = span->TopOffset + span->Length; + } + span++; + } + } + // Scale maxy before returning it + maxy = int((maxy *2) / tex->Scale.Y); + maxy = (maxy >> 1) + (maxy & 1); + return maxy; + } + + //==================================================================== + // + // Draws a single character with a shadow + // + //==================================================================== + + int WI_DrawCharPatch(FFont *font, int charcode, int x, int y, EColorRange translation = CR_UNTRANSLATED, bool nomove = false) + { + int width; + font->GetChar(charcode, &width); + screen->DrawChar(font, translation, x, y, charcode, nomove ? DTA_CleanNoMove : DTA_Clean, true, TAG_DONE); + return x - width; + } + + //==================================================================== + // + // Draws a level name with the big font + // + // x is no longer passed as a parameter because the text is now broken into several lines + // if it is too long + // + //==================================================================== + + int WI_DrawName(int y, FTexture *tex, const char *levelname) + { + // draw + if (tex) + { + screen->DrawTexture(tex, (screen->GetWidth() - tex->GetScaledWidth()*CleanXfac) /2, y, DTA_CleanNoMove, true, TAG_DONE); + int h = tex->GetScaledHeight(); + if (h > 50) + { // Fix for Deus Vult II and similar wads that decide to make these hugely tall + // patches with vast amounts of empty space at the bottom. + h = CheckRealHeight(tex); + } + return y + (h + BigFont->GetHeight()/4) * CleanYfac; } else { - screen->FlatFill(0, 0, SCREENWIDTH, SCREENHEIGHT, background); - } - } - else - { - screen->Clear(0,0, SCREENWIDTH, SCREENHEIGHT, 0, 0); - } + int i; + size_t l; + const char *p; + int h = 0; + int lumph; - for(i=0;iGetHeight() * CleanYfac; - switch (a->type & ANIM_CONDITION) - { - case ANIM_IFVISITED: - li = FindLevelInfo(a->levelname); - if (li == NULL || !(li->flags & LEVEL_VISITED)) continue; - break; + p = levelname; + if (!p) return 0; + l = strlen(p); + if (!l) return 0; - case ANIM_IFNOTVISITED: - li = FindLevelInfo(a->levelname); - if (li == NULL || (li->flags & LEVEL_VISITED)) continue; - break; + FBrokenLines *lines = V_BreakLines(mapname.mFont, screen->GetWidth() / CleanXfac, p); - // StatCount means 'leaving' - everything else means 'entering'! - case ANIM_IFENTERING: - if (state == StatCount || strnicmp(a->levelname, wbs->next, 8)) continue; - break; - - case ANIM_IFNOTENTERING: - if (state != StatCount && !strnicmp(a->levelname, wbs->next, 8)) continue; - break; - - case ANIM_IFLEAVING: - if (state != StatCount || strnicmp(a->levelname, wbs->current, 8)) continue; - break; - - case ANIM_IFNOTLEAVING: - if (state == StatCount && !strnicmp(a->levelname, wbs->current, 8)) continue; - break; - - case ANIM_IFTRAVELLING: - if (strnicmp(a->levelname2, wbs->current, 8) || strnicmp(a->levelname, wbs->next, 8)) continue; - break; - - case ANIM_IFNOTTRAVELLING: - if (!strnicmp(a->levelname2, wbs->current, 8) && !strnicmp(a->levelname, wbs->next, 8)) continue; - break; - } - if (a->ctr >= 0) - screen->DrawTexture(a->p[a->ctr], a->loc.x, a->loc.y, - DTA_VirtualWidthF, animwidth, DTA_VirtualHeightF, animheight, TAG_DONE); - } -} - - -//==================================================================== -// -// Draws a single character with a shadow -// -//==================================================================== - -static int WI_DrawCharPatch (FFont *font, int charcode, int x, int y, EColorRange translation=CR_UNTRANSLATED, bool nomove=false) -{ - int width; - font->GetChar(charcode, &width); - screen->DrawChar(font, translation, x, y, charcode, - nomove ? DTA_CleanNoMove : DTA_Clean, true, - DTA_ShadowAlpha, (gameinfo.gametype & GAME_DoomChex) ? 0 : 0.5, - TAG_DONE); - return x - width; -} - -//==================================================================== -// -// CheckRealHeight -// -// Checks the posts in a texture and returns the lowest row (plus one) -// of the texture that is actually used. -// -//==================================================================== - -int CheckRealHeight(FTexture *tex) -{ - const FTexture::Span *span; - int maxy = 0, miny = tex->GetHeight(); - - for (int i = 0; i < tex->GetWidth(); ++i) - { - tex->GetColumn(i, &span); - while (span->Length != 0) - { - if (span->TopOffset < miny) + if (lines) { - miny = span->TopOffset; + for (i = 0; lines[i].Width >= 0; i++) + { + screen->DrawText(mapname.mFont, mapname.mColor, (SCREENWIDTH - lines[i].Width * CleanXfac) / 2, y + h, + lines[i].Text, DTA_CleanNoMove, true, TAG_DONE); + h += lumph; + } + V_FreeBrokenLines(lines); } - if (span->TopOffset + span->Length > maxy) - { - maxy = span->TopOffset + span->Length; - } - span++; + return y + h + lumph/4; } } - // Scale maxy before returning it - maxy = int((maxy *2) / tex->Scale.Y); - maxy = (maxy >> 1) + (maxy & 1); - return maxy; -} -//==================================================================== -// -// Draws a level name with the big font -// -// x is no longer passed as a parameter because the text is now broken into several lines -// if it is too long -// -//==================================================================== + //==================================================================== + // + // Draws a text, either as patch or as string from the string table + // + //==================================================================== -int WI_DrawName(int y, FTexture *tex, const char *levelname) -{ - // draw - if (tex) + int WI_DrawPatchText(int y, FPatchInfo *pinfo, const char *stringname) { - screen->DrawTexture(tex, (screen->GetWidth() - tex->GetScaledWidth()*CleanXfac) /2, y, DTA_CleanNoMove, true, TAG_DONE); - int h = tex->GetScaledHeight(); - if (h > 50) - { // Fix for Deus Vult II and similar wads that decide to make these hugely tall - // patches with vast amounts of empty space at the bottom. - h = CheckRealHeight(tex); + const char *string = GStrings(stringname); + int midx = screen->GetWidth() / 2; + + if (pinfo->mPatch != NULL) + { + screen->DrawTexture(pinfo->mPatch, midx - pinfo->mPatch->GetScaledWidth()*CleanXfac/2, y, DTA_CleanNoMove, true, TAG_DONE); + return y + (pinfo->mPatch->GetScaledHeight() * CleanYfac); + } + else + { + screen->DrawText(pinfo->mFont, pinfo->mColor, midx - pinfo->mFont->StringWidth(string)*CleanXfac/2, + y, string, DTA_CleanNoMove, true, TAG_DONE); + return y + pinfo->mFont->GetHeight() * CleanYfac; } - return y + (h + BigFont->GetHeight()/4) * CleanYfac; } - else + + + //==================================================================== + // + // Draws " Finished!" + // + // Either uses the specified patch or the big font + // A level name patch can be specified for all games now, not just Doom. + // + //==================================================================== + + int WI_drawLF () + { + int y = WI_TITLEY * CleanYfac; + + y = WI_DrawName(y, TexMan(wbs->LName0), lnametexts[0]); + + // Adjustment for different font sizes for map name and 'finished'. + y -= ((mapname.mFont->GetHeight() - finished.mFont->GetHeight()) * CleanYfac) / 4; + + // draw "Finished!" + if (y < (NG_STATSY - finished.mFont->GetHeight()*3/4) * CleanYfac) + { + // don't draw 'finished' if the level name is too tall + y = WI_DrawPatchText(y, &finished, "WI_FINISHED"); + } + return y; + } + + + //==================================================================== + // + // Draws "Entering " + // + // Either uses the specified patch or the big font + // A level name patch can be specified for all games now, not just Doom. + // + //==================================================================== + + void WI_drawEL () + { + int y = WI_TITLEY * CleanYfac; + + y = WI_DrawPatchText(y, &entering, "WI_ENTERING"); + y += entering.mFont->GetHeight() * CleanYfac / 4; + WI_DrawName(y, TexMan(wbs->LName1), lnametexts[1]); + } + + + //==================================================================== + // + // Draws a number. + // If digits > 0, then use that many digits minimum, + // otherwise only use as many as necessary. + // x is the right edge of the number. + // Returns new x position, that is, the left edge of the number. + // + //==================================================================== + int WI_drawNum (FFont *font, int x, int y, int n, int digits, bool leadingzeros=true, EColorRange translation=CR_UNTRANSLATED) + { + int fontwidth = font->GetCharWidth('3'); + char text[8]; + int len; + char *text_p; + bool nomove = font != IntermissionFont; + + if (nomove) + { + fontwidth *= CleanXfac; + } + if (leadingzeros) + { + len = mysnprintf (text, countof(text), "%0*d", digits, n); + } + else + { + len = mysnprintf (text, countof(text), "%d", n); + } + text_p = text + MIN(len, countof(text)-1); + + while (--text_p >= text) + { + // Digits are centered in a box the width of the '3' character. + // Other characters (specifically, '-') are right-aligned in their cell. + if (*text_p >= '0' && *text_p <= '9') + { + x -= fontwidth; + WI_DrawCharPatch(font, *text_p, x + (fontwidth - font->GetCharWidth(*text_p)) / 2, y, translation, nomove); + } + else + { + WI_DrawCharPatch(font, *text_p, x - font->GetCharWidth(*text_p), y, translation, nomove); + x -= fontwidth; + } + } + if (len < digits) + { + x -= fontwidth * (digits - len); + } + return x; + } + + //==================================================================== + // + // + // + //==================================================================== + + void WI_drawPercent (FFont *font, int x, int y, int p, int b, bool show_total=true, EColorRange color=CR_UNTRANSLATED) + { + if (p < 0) + return; + + if (wi_percents) + { + if (font != IntermissionFont) + { + x -= font->GetCharWidth('%') * CleanXfac; + } + else + { + x -= font->GetCharWidth('%'); + } + screen->DrawText(font, color, x, y, "%", font != IntermissionFont ? DTA_CleanNoMove : DTA_Clean, true, TAG_DONE); + if (font != IntermissionFont) + { + x -= 2*CleanXfac; + } + WI_drawNum(font, x, y, b == 0 ? 100 : p * 100 / b, -1, false, color); + } + else + { + if (show_total) + { + x = WI_drawNum(font, x, y, b, 2, false); + x -= font->GetCharWidth('/'); + screen->DrawText (IntermissionFont, color, x, y, "/", + DTA_Clean, true, TAG_DONE); + } + WI_drawNum (font, x, y, p, -1, false, color); + } + } + + //==================================================================== + // + // Display level completion time and par, or "sucks" message if overflow. + // + //==================================================================== + void WI_drawTime (int x, int y, int t, bool no_sucks=false) + { + bool sucky; + + if (t<0) + return; + + sucky = !no_sucks && t >= wbs->sucktime * 60 * 60 && wbs->sucktime > 0; + + if (sucky) + { // "sucks" + if (sucks != NULL) + { + screen->DrawTexture (sucks, x - sucks->GetScaledWidth(), y - IntermissionFont->GetHeight() - 2, + DTA_Clean, true, TAG_DONE); + } + else + { + screen->DrawText (BigFont, CR_UNTRANSLATED, x - BigFont->StringWidth("SUCKS"), y - IntermissionFont->GetHeight() - 2, + "SUCKS", DTA_Clean, true, TAG_DONE); + } + } + + int hours = t / 3600; + t -= hours * 3600; + int minutes = t / 60; + t -= minutes * 60; + int seconds = t; + + // Why were these offsets hard coded? Half the WADs with custom patches + // I tested screwed up miserably in this function! + int num_spacing = IntermissionFont->GetCharWidth('3'); + int colon_spacing = IntermissionFont->GetCharWidth(':'); + + x = WI_drawNum (IntermissionFont, x, y, seconds, 2) - 1; + WI_DrawCharPatch (IntermissionFont, ':', x -= colon_spacing, y); + x = WI_drawNum (IntermissionFont, x, y, minutes, 2, hours!=0); + if (hours) + { + WI_DrawCharPatch (IntermissionFont, ':', x -= colon_spacing, y); + WI_drawNum (IntermissionFont, x, y, hours, 2); + } + } + + void WI_End () + { + state = LeavingIntermission; + WI_unloadData (); + + //Added by mc + if (deathmatch) + { + bglobal.RemoveAllBots (consoleplayer != Net_Arbitrator); + } + } + + bool WI_autoSkip() + { + return wi_autoadvance > 0 && bcnt > (wi_autoadvance * TICRATE); + } + + void WI_initNoState () + { + state = NoState; + acceleratestage = 0; + cnt = 10; + } + + void WI_updateNoState () + { + if (acceleratestage) + { + cnt = 0; + } + else + { + bool noauto = noautostartmap; + bool autoskip = WI_autoSkip(); + + for (int i = 0; !noauto && i < MAXPLAYERS; ++i) + { + if (playeringame[i]) + { + noauto |= players[i].userinfo.GetNoAutostartMap(); + } + } + if (!noauto || autoskip) + { + cnt--; + } + } + + if (cnt == 0) + { + WI_End(); + G_WorldDone(); + } + } + + + void WI_initShowNextLoc () + { + auto info = FindLevelInfo(wbs->next, false); + if (info == nullptr) + { + // Last map in episode - there is no next location! + WI_End(); + G_WorldDone(); + return; + } + + state = ShowNextLoc; + acceleratestage = 0; + cnt = SHOWNEXTLOCDELAY * TICRATE; + bg->LoadBackground(true); + } + + void WI_updateShowNextLoc () + { + if (!--cnt || acceleratestage) + WI_initNoState(); + else + snl_pointeron = (cnt & 31) < 20; + } + + void WI_drawShowNextLoc(void) + { + bg->drawBackground(state, true, snl_pointeron); + + // draws which level you are entering.. + WI_drawEL (); + + } + + void WI_drawNoState () + { + snl_pointeron = true; + WI_drawShowNextLoc(); + } + + int WI_fragSum (int playernum) { int i; - size_t l; - const char *p; - int h = 0; - int lumph; - - lumph = mapname.mFont->GetHeight() * CleanYfac; - - p = levelname; - if (!p) return 0; - l = strlen(p); - if (!l) return 0; - - FBrokenLines *lines = V_BreakLines(mapname.mFont, screen->GetWidth() / CleanXfac, p); - - if (lines) - { - for (i = 0; lines[i].Width >= 0; i++) - { - screen->DrawText(mapname.mFont, mapname.mColor, (SCREENWIDTH - lines[i].Width * CleanXfac) / 2, y + h, - lines[i].Text, DTA_CleanNoMove, true, TAG_DONE); - h += lumph; - } - V_FreeBrokenLines(lines); - } - return y + h + lumph/4; - } -} - -//==================================================================== -// -// Draws a text, either as patch or as string from the string table -// -//==================================================================== - -int WI_DrawPatchText(int y, FPatchInfo *pinfo, const char *stringname) -{ - const char *string = GStrings(stringname); - int midx = screen->GetWidth() / 2; - - if (pinfo->mPatch != NULL) - { - screen->DrawTexture(pinfo->mPatch, midx - pinfo->mPatch->GetScaledWidth()*CleanXfac/2, y, DTA_CleanNoMove, true, TAG_DONE); - return y + (pinfo->mPatch->GetScaledHeight() * CleanYfac); - } - else - { - screen->DrawText(pinfo->mFont, pinfo->mColor, midx - pinfo->mFont->StringWidth(string)*CleanXfac/2, - y, string, DTA_CleanNoMove, true, TAG_DONE); - return y + pinfo->mFont->GetHeight() * CleanYfac; - } -} - - -//==================================================================== -// -// Draws " Finished!" -// -// Either uses the specified patch or the big font -// A level name patch can be specified for all games now, not just Doom. -// -//==================================================================== - -int WI_drawLF () -{ - int y = WI_TITLEY * CleanYfac; - - y = WI_DrawName(y, wbs->LName0, lnametexts[0]); + int frags = 0; - // Adjustment for different font sizes for map name and 'finished'. - y -= ((mapname.mFont->GetHeight() - finished.mFont->GetHeight()) * CleanYfac) / 4; - - // draw "Finished!" - if (y < (NG_STATSY - finished.mFont->GetHeight()*3/4) * CleanYfac) - { - // don't draw 'finished' if the level name is too tall - y = WI_DrawPatchText(y, &finished, "WI_FINISHED"); - } - return y; -} - - -//==================================================================== -// -// Draws "Entering " -// -// Either uses the specified patch or the big font -// A level name patch can be specified for all games now, not just Doom. -// -//==================================================================== - -void WI_drawEL () -{ - int y = WI_TITLEY * CleanYfac; - - y = WI_DrawPatchText(y, &entering, "WI_ENTERING"); - y += entering.mFont->GetHeight() * CleanYfac / 4; - WI_DrawName(y, wbs->LName1, lnametexts[1]); -} - - -//==================================================================== -// -// Draws the splats and the 'You are here' arrows -// -//==================================================================== - -int WI_MapToIndex (const char *map) -{ - unsigned int i; - - for (i = 0; i < lnodes.Size(); i++) - { - if (!strnicmp (lnodes[i].level, map, 8)) - break; - } - return i; -} - - -//==================================================================== -// -// Draws the splats and the 'You are here' arrows -// -//==================================================================== - -void WI_drawOnLnode( int n, FTexture * c[] ,int numc) -{ - int i; - for(i=0;iGetScaledWidth(); - bottom = c[i]->GetScaledHeight(); - left = lnodes[n].x - c[i]->GetScaledLeftOffset(); - top = lnodes[n].y - c[i]->GetScaledTopOffset(); - right += left; - bottom += top; + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] + && i!=playernum) + { + frags += Plrs[playernum]->frags[i]; + } + } - if (left >= 0 && right < 320 && top >= 0 && bottom < 200) - { - screen->DrawTexture (c[i], lnodes[n].x, lnodes[n].y, DTA_320x200, true, TAG_DONE); - break; - } - } -} + // JDC hack - negative frags. + frags -= Plrs[playernum]->frags[playernum]; -//==================================================================== -// -// Draws a number. -// If digits > 0, then use that many digits minimum, -// otherwise only use as many as necessary. -// x is the right edge of the number. -// Returns new x position, that is, the left edge of the number. -// -//==================================================================== -int WI_drawNum (FFont *font, int x, int y, int n, int digits, bool leadingzeros=true, EColorRange translation=CR_UNTRANSLATED) -{ - int fontwidth = font->GetCharWidth('3'); - char text[8]; - int len; - char *text_p; - bool nomove = font != IntermissionFont; - - if (nomove) - { - fontwidth *= CleanXfac; - } - if (leadingzeros) - { - len = mysnprintf (text, countof(text), "%0*d", digits, n); - } - else - { - len = mysnprintf (text, countof(text), "%d", n); - } - text_p = text + MIN(len, countof(text)-1); - - while (--text_p >= text) - { - // Digits are centered in a box the width of the '3' character. - // Other characters (specifically, '-') are right-aligned in their cell. - if (*text_p >= '0' && *text_p <= '9') - { - x -= fontwidth; - WI_DrawCharPatch(font, *text_p, x + (fontwidth - font->GetCharWidth(*text_p)) / 2, y, translation, nomove); - } - else - { - WI_DrawCharPatch(font, *text_p, x - font->GetCharWidth(*text_p), y, translation, nomove); - x -= fontwidth; - } - } - if (len < digits) - { - x -= fontwidth * (digits - len); - } - return x; -} - -//==================================================================== -// -// -// -//==================================================================== - -void WI_drawPercent (FFont *font, int x, int y, int p, int b, bool show_total=true, EColorRange color=CR_UNTRANSLATED) -{ - if (p < 0) - return; - - if (wi_percents) - { - if (font != IntermissionFont) - { - x -= font->GetCharWidth('%') * CleanXfac; - } - else - { - x -= font->GetCharWidth('%'); - } - screen->DrawText(font, color, x, y, "%", font != IntermissionFont ? DTA_CleanNoMove : DTA_Clean, true, TAG_DONE); - if (font != IntermissionFont) - { - x -= 2*CleanXfac; - } - WI_drawNum(font, x, y, b == 0 ? 100 : p * 100 / b, -1, false, color); - } - else - { - if (show_total) - { - x = WI_drawNum(font, x, y, b, 2, false); - x -= font->GetCharWidth('/'); - screen->DrawText (IntermissionFont, color, x, y, "/", - DTA_Clean, true, TAG_DONE); - } - WI_drawNum (font, x, y, p, -1, false, color); - } -} - -//==================================================================== -// -// Display level completion time and par, or "sucks" message if overflow. -// -//==================================================================== -void WI_drawTime (int x, int y, int t, bool no_sucks=false) -{ - bool sucky; - - if (t<0) - return; - - sucky = !no_sucks && t >= wbs->sucktime * 60 * 60 && wbs->sucktime > 0; - - if (sucky) - { // "sucks" - if (sucks != NULL) - { - screen->DrawTexture (sucks, x - sucks->GetScaledWidth(), y - IntermissionFont->GetHeight() - 2, - DTA_Clean, true, TAG_DONE); - } - else - { - screen->DrawText (BigFont, CR_UNTRANSLATED, x - BigFont->StringWidth("SUCKS"), y - IntermissionFont->GetHeight() - 2, - "SUCKS", DTA_Clean, true, TAG_DONE); - } + return frags; } - int hours = t / 3600; - t -= hours * 3600; - int minutes = t / 60; - t -= minutes * 60; - int seconds = t; - // Why were these offsets hard coded? Half the WADs with custom patches - // I tested screwed up miserably in this function! - int num_spacing = IntermissionFont->GetCharWidth('3'); - int colon_spacing = IntermissionFont->GetCharWidth(':'); - - x = WI_drawNum (IntermissionFont, x, y, seconds, 2) - 1; - WI_DrawCharPatch (IntermissionFont, ':', x -= colon_spacing, y); - x = WI_drawNum (IntermissionFont, x, y, minutes, 2, hours!=0); - if (hours) + void WI_initDeathmatchStats (void) { - WI_DrawCharPatch (IntermissionFont, ':', x -= colon_spacing, y); - WI_drawNum (IntermissionFont, x, y, hours, 2); - } -} + int i, j; -void WI_End () -{ - state = LeavingIntermission; - WI_unloadData (); + state = StatCount; + acceleratestage = 0; + memset(playerready, 0, sizeof(playerready)); + memset(cnt_frags, 0, sizeof(cnt_frags)); + memset(cnt_deaths, 0, sizeof(cnt_deaths)); + memset(player_deaths, 0, sizeof(player_deaths)); + total_frags = 0; + total_deaths = 0; - //Added by mc - if (deathmatch) - { - bglobal.RemoveAllBots (consoleplayer != Net_Arbitrator); - } -} + ng_state = 1; + cnt_pause = TICRATE; -bool WI_autoSkip() -{ - return wi_autoadvance > 0 && bcnt > (wi_autoadvance * TICRATE); -} - -void WI_initNoState () -{ - state = NoState; - acceleratestage = 0; - cnt = 10; -} - -void WI_updateNoState () -{ - WI_updateAnimatedBack(); - - if (acceleratestage) - { - cnt = 0; - } - else - { - bool noauto = noautostartmap; - bool autoskip = WI_autoSkip(); - - for (int i = 0; !noauto && i < MAXPLAYERS; ++i) + for (i=0 ; ifrags[i]; + total_deaths += player_deaths[i]; + total_frags += Plrs[i]->fragcount; } } - if (!noauto || autoskip) + } + + void WI_updateDeathmatchStats () + { + + int i; + bool stillticking; + bool autoskip = WI_autoSkip(); + + if ((acceleratestage || autoskip) && ng_state != 6) { - cnt--; - } - } + acceleratestage = 0; - if (cnt == 0) - { - WI_End(); - G_WorldDone(); - } -} + for (i = 0; inext_ep == -1) - { - // Last map in episode - there is no next location! - WI_End(); - G_WorldDone(); - return; - } - - state = ShowNextLoc; - acceleratestage = 0; - cnt = SHOWNEXTLOCDELAY * TICRATE; - WI_LoadBackground(true); -} - -void WI_updateShowNextLoc () -{ - WI_updateAnimatedBack(); - - if (!--cnt || acceleratestage) - WI_initNoState(); - else - snl_pointeron = (cnt & 31) < 20; -} - -void WI_drawShowNextLoc(void) -{ - unsigned int i; - - WI_drawBackground(); - - if (splat) - { - for (i=0 ; iflags & LEVEL_VISITED) WI_drawOnLnode(i, &splat,1); // draw a splat on taken cities. - } - } - - // draw flashing ptr - if (snl_pointeron && yah.Size()) - { - unsigned int v = WI_MapToIndex (wbs->next); - // Draw only if it points to a valid level on the current screen! - if (v plrs[i].fragcount) - cnt_frags[i] = plrs[i].fragcount; - else - stillticking = true; - } - - if (!stillticking) - { - S_Sound(CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); - ng_state++; - } - } - else if (ng_state == 4) - { - if (!(bcnt & 3)) - S_Sound(CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE); - - stillticking = false; - - for (i = 0; i player_deaths[i]) + cnt_frags[i] = Plrs[i]->fragcount; cnt_deaths[i] = player_deaths[i]; - else - stillticking = true; - } - if (!stillticking) - { + } S_Sound(CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); - ng_state++; + ng_state = 6; + } + + if (ng_state == 2) + { + if (!(bcnt & 3)) + S_Sound(CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE); + + stillticking = false; + + for (i = 0; i Plrs[i]->fragcount) + cnt_frags[i] = Plrs[i]->fragcount; + else + stillticking = true; + } + + if (!stillticking) + { + S_Sound(CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); + ng_state++; + } + } + else if (ng_state == 4) + { + if (!(bcnt & 3)) + S_Sound(CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE); + + stillticking = false; + + for (i = 0; i player_deaths[i]) + cnt_deaths[i] = player_deaths[i]; + else + stillticking = true; + } + if (!stillticking) + { + S_Sound(CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); + ng_state++; + } + } + else if (ng_state == 6) + { + int i; + for (i = 0; i < MAXPLAYERS; i++) + { + // If the player is in the game and not ready, stop checking + if (playeringame[i] && players[i].Bot == NULL && !playerready[i]) + break; + } + + // All players are ready; proceed. + if ((i == MAXPLAYERS && acceleratestage) || autoskip) + { + S_Sound(CHAN_VOICE | CHAN_UI, "intermission/pastdmstats", 1, ATTN_NONE); + WI_initShowNextLoc(); + } + } + else if (ng_state & 1) + { + if (!--cnt_pause) + { + ng_state++; + cnt_pause = TICRATE; + } } } - else if (ng_state == 6) + + + + void WI_drawDeathmatchStats () { - int i; + int i, pnum, x, y, ypadding, height, lineheight; + int maxnamewidth, maxscorewidth, maxiconheight; + int pwidth = IntermissionFont->GetCharWidth('%'); + int icon_x, name_x, frags_x, deaths_x; + int deaths_len; + float h, s, v, r, g, b; + EColorRange color; + const char *text_deaths, *text_frags; + FTexture *readyico = TexMan.FindTexture("READYICO"); + player_t *sortedplayers[MAXPLAYERS]; + + // draw animated background + bg->drawBackground(state, false, false); + + y = WI_drawLF(); + + HU_GetPlayerWidths(maxnamewidth, maxscorewidth, maxiconheight); + // Use the readyico height if it's bigger. + height = readyico->GetScaledHeight() - readyico->GetScaledTopOffset(); + maxiconheight = MAX(height, maxiconheight); + height = SmallFont->GetHeight() * CleanYfac; + lineheight = MAX(height, maxiconheight * CleanYfac); + ypadding = (lineheight - height + 1) / 2; + y += CleanYfac; + + text_deaths = GStrings("SCORE_DEATHS"); + //text_color = GStrings("SCORE_COLOR"); + text_frags = GStrings("SCORE_FRAGS"); + + icon_x = 8 * CleanXfac; + name_x = icon_x + maxscorewidth * CleanXfac; + frags_x = name_x + (maxnamewidth + MAX(SmallFont->StringWidth("XXXXX"), SmallFont->StringWidth(text_frags)) + 8) * CleanXfac; + deaths_x = frags_x + ((deaths_len = SmallFont->StringWidth(text_deaths)) + 8) * CleanXfac; + + x = (SCREENWIDTH - deaths_x) >> 1; + icon_x += x; + name_x += x; + frags_x += x; + deaths_x += x; + + color = (gameinfo.gametype & GAME_Raven) ? CR_GREEN : CR_UNTRANSLATED; + + screen->DrawText(SmallFont, color, name_x, y, GStrings("SCORE_NAME"), DTA_CleanNoMove, true, TAG_DONE); + screen->DrawText(SmallFont, color, frags_x - SmallFont->StringWidth(text_frags)*CleanXfac, y, text_frags, DTA_CleanNoMove, true, TAG_DONE); + screen->DrawText(SmallFont, color, deaths_x - deaths_len*CleanXfac, y, text_deaths, DTA_CleanNoMove, true, TAG_DONE); + y += height + 6 * CleanYfac; + + // Sort all players for (i = 0; i < MAXPLAYERS; i++) { - // If the player is in the game and not ready, stop checking - if (playeringame[i] && players[i].Bot == NULL && !playerready[i]) - break; + sortedplayers[i] = &players[i]; } - // All players are ready; proceed. - if ((i == MAXPLAYERS && acceleratestage) || autoskip) - { - S_Sound(CHAN_VOICE | CHAN_UI, "intermission/pastdmstats", 1, ATTN_NONE); - WI_initShowNextLoc(); - } - } - else if (ng_state & 1) - { - if (!--cnt_pause) - { - ng_state++; - cnt_pause = TICRATE; - } - } -} + if (teamplay) + qsort(sortedplayers, MAXPLAYERS, sizeof(player_t *), compareteams); + else + qsort(sortedplayers, MAXPLAYERS, sizeof(player_t *), comparepoints); - - -void WI_drawDeathmatchStats () -{ - int i, pnum, x, y, ypadding, height, lineheight; - int maxnamewidth, maxscorewidth, maxiconheight; - int pwidth = IntermissionFont->GetCharWidth('%'); - int icon_x, name_x, frags_x, deaths_x; - int deaths_len; - float h, s, v, r, g, b; - EColorRange color; - const char *text_deaths, *text_frags; - FTexture *readyico = TexMan.FindTexture("READYICO"); - player_t *sortedplayers[MAXPLAYERS]; - - // draw animated background - WI_drawBackground(); - - y = WI_drawLF(); - - HU_GetPlayerWidths(maxnamewidth, maxscorewidth, maxiconheight); - // Use the readyico height if it's bigger. - height = readyico->GetScaledHeight() - readyico->GetScaledTopOffset(); - maxiconheight = MAX(height, maxiconheight); - height = SmallFont->GetHeight() * CleanYfac; - lineheight = MAX(height, maxiconheight * CleanYfac); - ypadding = (lineheight - height + 1) / 2; - y += CleanYfac; - - text_deaths = GStrings("SCORE_DEATHS"); - //text_color = GStrings("SCORE_COLOR"); - text_frags = GStrings("SCORE_FRAGS"); - - icon_x = 8 * CleanXfac; - name_x = icon_x + maxscorewidth * CleanXfac; - frags_x = name_x + (maxnamewidth + MAX(SmallFont->StringWidth("XXXXX"), SmallFont->StringWidth(text_frags)) + 8) * CleanXfac; - deaths_x = frags_x + ((deaths_len = SmallFont->StringWidth(text_deaths)) + 8) * CleanXfac; - - x = (SCREENWIDTH - deaths_x) >> 1; - icon_x += x; - name_x += x; - frags_x += x; - deaths_x += x; - - color = (gameinfo.gametype & GAME_Raven) ? CR_GREEN : CR_UNTRANSLATED; - - screen->DrawText(SmallFont, color, name_x, y, GStrings("SCORE_NAME"), DTA_CleanNoMove, true, TAG_DONE); - screen->DrawText(SmallFont, color, frags_x - SmallFont->StringWidth(text_frags)*CleanXfac, y, text_frags, DTA_CleanNoMove, true, TAG_DONE); - screen->DrawText(SmallFont, color, deaths_x - deaths_len*CleanXfac, y, text_deaths, DTA_CleanNoMove, true, TAG_DONE); - y += height + 6 * CleanYfac; - - // Sort all players - for (i = 0; i < MAXPLAYERS; i++) - { - sortedplayers[i] = &players[i]; - } - - if (teamplay) - qsort(sortedplayers, MAXPLAYERS, sizeof(player_t *), compareteams); - else - qsort(sortedplayers, MAXPLAYERS, sizeof(player_t *), comparepoints); - - // Draw lines for each player - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *player = sortedplayers[i]; - pnum = int(player - players); - - if (!playeringame[pnum]) - continue; - - D_GetPlayerColor(pnum, &h, &s, &v, NULL); - HSVtoRGB(&r, &g, &b, h, s, v); - - screen->Dim(MAKERGB(clamp(int(r*255.f), 0, 255), - clamp(int(g*255.f), 0, 255), - clamp(int(b*255.f), 0, 255)), 0.8f, x, y - ypadding, (deaths_x - x) + (8 * CleanXfac), lineheight); - - if (playerready[pnum] || player->Bot != NULL) // Bots are automatically assumed ready, to prevent confusion - screen->DrawTexture(readyico, x - (readyico->GetWidth() * CleanXfac), y, DTA_CleanNoMove, true, TAG_DONE); - - color = (EColorRange)HU_GetRowColor(player, pnum == consoleplayer); - if (player->mo->ScoreIcon.isValid()) - { - FTexture *pic = TexMan[player->mo->ScoreIcon]; - screen->DrawTexture(pic, icon_x, y, DTA_CleanNoMove, true, TAG_DONE); - } - screen->DrawText(SmallFont, color, name_x, y + ypadding, player->userinfo.GetName(), DTA_CleanNoMove, true, TAG_DONE); - WI_drawNum(SmallFont, frags_x, y + ypadding, cnt_frags[pnum], 0, false, color); - if (ng_state >= 2) - { - WI_drawNum(SmallFont, deaths_x, y + ypadding, cnt_deaths[pnum], 0, false, color); - } - y += lineheight + CleanYfac; - } - - // Draw "TOTAL" line - y += height + 3 * CleanYfac; - color = (gameinfo.gametype & GAME_Raven) ? CR_GREEN : CR_UNTRANSLATED; - screen->DrawText(SmallFont, color, name_x, y, GStrings("SCORE_TOTAL"), DTA_CleanNoMove, true, TAG_DONE); - WI_drawNum(SmallFont, frags_x, y, total_frags, 0, false, color); - if (ng_state >= 4) - { - WI_drawNum(SmallFont, deaths_x, y, total_deaths, 0, false, color); - } - - // Draw game time - y += height + CleanYfac; - - int seconds = Tics2Seconds(plrs[me].stime); - int hours = seconds / 3600; - int minutes = (seconds % 3600) / 60; - seconds = seconds % 60; - - FString leveltime = GStrings("SCORE_LVLTIME"); - leveltime += ": "; - - char timer[sizeof "HH:MM:SS"]; - mysnprintf(timer, sizeof(timer), "%02i:%02i:%02i", hours, minutes, seconds); - leveltime += timer; - - screen->DrawText(SmallFont, color, x, y, leveltime, DTA_CleanNoMove, true, TAG_DONE); -} - -void WI_initNetgameStats () -{ - - int i; - - state = StatCount; - acceleratestage = 0; - memset(playerready, 0, sizeof(playerready)); - ng_state = 1; - - cnt_pause = TICRATE; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - - cnt_kills[i] = cnt_items[i] = cnt_secret[i] = cnt_frags[i] = 0; - - dofrags += WI_fragSum (i); - } - - dofrags = !!dofrags; -} - -void WI_updateNetgameStats () -{ - - int i; - int fsum; - bool stillticking; - bool autoskip = WI_autoSkip(); - - WI_updateAnimatedBack (); - - if ((acceleratestage || autoskip) && ng_state != 10) - { - acceleratestage = 0; - - for (i=0 ; i plrs[i].skills) - cnt_kills[i] = plrs[i].skills; - else - stillticking = true; - } - - if (!stillticking) - { - S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); - ng_state++; - } - } - else if (ng_state == 4) - { - if (!(bcnt&3)) - S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE); - - stillticking = false; - - for (i=0 ; i plrs[i].sitems) - cnt_items[i] = plrs[i].sitems; - else - stillticking = true; - } - if (!stillticking) - { - S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); - ng_state++; - } - } - else if (ng_state == 6) - { - if (!(bcnt&3)) - S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE); - - stillticking = false; - - for (i=0 ; i plrs[i].ssecret) - cnt_secret[i] = plrs[i].ssecret; - else - stillticking = true; - } - - if (!stillticking) - { - S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); - ng_state += 1 + 2*!dofrags; - } - } - else if (ng_state == 8) - { - if (!(bcnt&3)) - S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE); - - stillticking = false; - - for (i=0 ; i= (fsum = WI_fragSum(i))) - cnt_frags[i] = fsum; - else - stillticking = true; - } - - if (!stillticking) - { - S_Sound (CHAN_VOICE | CHAN_UI, "intermission/cooptotal", 1, ATTN_NONE); - ng_state++; - } - } - else if (ng_state == 10) - { - int i; + // Draw lines for each player for (i = 0; i < MAXPLAYERS; i++) { - // If the player is in the game and not ready, stop checking - if (playeringame[i] && players[i].Bot == NULL && !playerready[i]) - break; + player_t *player = sortedplayers[i]; + pnum = int(player - players); + + if (!playeringame[pnum]) + continue; + + D_GetPlayerColor(pnum, &h, &s, &v, NULL); + HSVtoRGB(&r, &g, &b, h, s, v); + + screen->Dim(MAKERGB(clamp(int(r*255.f), 0, 255), + clamp(int(g*255.f), 0, 255), + clamp(int(b*255.f), 0, 255)), 0.8f, x, y - ypadding, (deaths_x - x) + (8 * CleanXfac), lineheight); + + if (playerready[pnum] || player->Bot != NULL) // Bots are automatically assumed ready, to prevent confusion + screen->DrawTexture(readyico, x - (readyico->GetWidth() * CleanXfac), y, DTA_CleanNoMove, true, TAG_DONE); + + color = (EColorRange)HU_GetRowColor(player, pnum == consoleplayer); + if (player->mo->ScoreIcon.isValid()) + { + FTexture *pic = TexMan[player->mo->ScoreIcon]; + screen->DrawTexture(pic, icon_x, y, DTA_CleanNoMove, true, TAG_DONE); + } + screen->DrawText(SmallFont, color, name_x, y + ypadding, player->userinfo.GetName(), DTA_CleanNoMove, true, TAG_DONE); + WI_drawNum(SmallFont, frags_x, y + ypadding, cnt_frags[pnum], 0, false, color); + if (ng_state >= 2) + { + WI_drawNum(SmallFont, deaths_x, y + ypadding, cnt_deaths[pnum], 0, false, color); + } + y += lineheight + CleanYfac; } - // All players are ready; proceed. - if ((i == MAXPLAYERS && acceleratestage) || autoskip) - { - S_Sound (CHAN_VOICE | CHAN_UI, "intermission/pastcoopstats", 1, ATTN_NONE); - WI_initShowNextLoc(); - } - } - else if (ng_state & 1) - { - if (!--cnt_pause) - { - ng_state++; - cnt_pause = TICRATE; - } - } -} - -void WI_drawNetgameStats () -{ - int i, x, y, ypadding, height, lineheight; - int maxnamewidth, maxscorewidth, maxiconheight; - int pwidth = IntermissionFont->GetCharWidth('%'); - int icon_x, name_x, kills_x, bonus_x, secret_x; - int bonus_len, secret_len; - int missed_kills, missed_items, missed_secrets; - float h, s, v, r, g, b; - EColorRange color; - const char *text_bonus, *text_secret, *text_kills; - FTexture *readyico = TexMan.FindTexture("READYICO"); - - // draw animated background - WI_drawBackground(); - - y = WI_drawLF(); - - HU_GetPlayerWidths(maxnamewidth, maxscorewidth, maxiconheight); - // Use the readyico height if it's bigger. - height = readyico->GetScaledHeight() - readyico->GetScaledTopOffset(); - if (height > maxiconheight) - { - maxiconheight = height; - } - height = SmallFont->GetHeight() * CleanYfac; - lineheight = MAX(height, maxiconheight * CleanYfac); - ypadding = (lineheight - height + 1) / 2; - y += CleanYfac; - - text_bonus = GStrings((gameinfo.gametype & GAME_Raven) ? "SCORE_BONUS" : "SCORE_ITEMS"); - text_secret = GStrings("SCORE_SECRET"); - text_kills = GStrings("SCORE_KILLS"); - - icon_x = 8 * CleanXfac; - name_x = icon_x + maxscorewidth * CleanXfac; - kills_x = name_x + (maxnamewidth + MAX(SmallFont->StringWidth("XXXXX"), SmallFont->StringWidth(text_kills)) + 8) * CleanXfac; - bonus_x = kills_x + ((bonus_len = SmallFont->StringWidth(text_bonus)) + 8) * CleanXfac; - secret_x = bonus_x + ((secret_len = SmallFont->StringWidth(text_secret)) + 8) * CleanXfac; - - x = (SCREENWIDTH - secret_x) >> 1; - icon_x += x; - name_x += x; - kills_x += x; - bonus_x += x; - secret_x += x; - - color = (gameinfo.gametype & GAME_Raven) ? CR_GREEN : CR_UNTRANSLATED; - - screen->DrawText(SmallFont, color, name_x, y, GStrings("SCORE_NAME"), DTA_CleanNoMove, true, TAG_DONE); - screen->DrawText(SmallFont, color, kills_x - SmallFont->StringWidth(text_kills)*CleanXfac, y, text_kills, DTA_CleanNoMove, true, TAG_DONE); - screen->DrawText(SmallFont, color, bonus_x - bonus_len*CleanXfac, y, text_bonus, DTA_CleanNoMove, true, TAG_DONE); - screen->DrawText(SmallFont, color, secret_x - secret_len*CleanXfac, y, text_secret, DTA_CleanNoMove, true, TAG_DONE); - y += height + 6 * CleanYfac; - - missed_kills = wbs->maxkills; - missed_items = wbs->maxitems; - missed_secrets = wbs->maxsecret; - - // Draw lines for each player - for (i = 0; i < MAXPLAYERS; ++i) - { - player_t *player; - - if (!playeringame[i]) - continue; - - player = &players[i]; - - D_GetPlayerColor(i, &h, &s, &v, NULL); - HSVtoRGB(&r, &g, &b, h, s, v); - - screen->Dim(MAKERGB(clamp(int(r*255.f), 0, 255), - clamp(int(g*255.f), 0, 255), - clamp(int(b*255.f), 0, 255)), 0.8f, x, y - ypadding, (secret_x - x) + (8 * CleanXfac), lineheight); - - if (playerready[i] || player->Bot != NULL) // Bots are automatically assumed ready, to prevent confusion - screen->DrawTexture(readyico, x - (readyico->GetWidth() * CleanXfac), y, DTA_CleanNoMove, true, TAG_DONE); - - color = (EColorRange)HU_GetRowColor(player, i == consoleplayer); - if (player->mo->ScoreIcon.isValid()) - { - FTexture *pic = TexMan[player->mo->ScoreIcon]; - screen->DrawTexture(pic, icon_x, y, DTA_CleanNoMove, true, TAG_DONE); - } - screen->DrawText(SmallFont, color, name_x, y + ypadding, player->userinfo.GetName(), DTA_CleanNoMove, true, TAG_DONE); - WI_drawPercent(SmallFont, kills_x, y + ypadding, cnt_kills[i], wbs->maxkills, false, color); - missed_kills -= cnt_kills[i]; + // Draw "TOTAL" line + y += height + 3 * CleanYfac; + color = (gameinfo.gametype & GAME_Raven) ? CR_GREEN : CR_UNTRANSLATED; + screen->DrawText(SmallFont, color, name_x, y, GStrings("SCORE_TOTAL"), DTA_CleanNoMove, true, TAG_DONE); + WI_drawNum(SmallFont, frags_x, y, total_frags, 0, false, color); if (ng_state >= 4) { - WI_drawPercent(SmallFont, bonus_x, y + ypadding, cnt_items[i], wbs->maxitems, false, color); - missed_items -= cnt_items[i]; - if (ng_state >= 6) + WI_drawNum(SmallFont, deaths_x, y, total_deaths, 0, false, color); + } + + // Draw game time + y += height + CleanYfac; + + int seconds = Tics2Seconds(Plrs[me]->stime); + int hours = seconds / 3600; + int minutes = (seconds % 3600) / 60; + seconds = seconds % 60; + + FString leveltime = GStrings("SCORE_LVLTIME"); + leveltime += ": "; + + char timer[sizeof "HH:MM:SS"]; + mysnprintf(timer, sizeof(timer), "%02i:%02i:%02i", hours, minutes, seconds); + leveltime += timer; + + screen->DrawText(SmallFont, color, x, y, leveltime, DTA_CleanNoMove, true, TAG_DONE); + } + + void WI_initNetgameStats () + { + + int i; + + state = StatCount; + acceleratestage = 0; + memset(playerready, 0, sizeof(playerready)); + ng_state = 1; + + cnt_pause = TICRATE; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + cnt_kills[i] = cnt_items[i] = cnt_secret[i] = cnt_frags[i] = 0; + + dofrags += WI_fragSum (i); + } + + dofrags = !!dofrags; + } + + void WI_updateNetgameStats () + { + + int i; + int fsum; + bool stillticking; + bool autoskip = WI_autoSkip(); + + if ((acceleratestage || autoskip) && ng_state != 10) + { + acceleratestage = 0; + + for (i=0 ; imaxsecret, false, color); - missed_secrets -= cnt_secret[i]; + if (!playeringame[i]) + continue; + + cnt_kills[i] = Plrs[i]->skills; + cnt_items[i] = Plrs[i]->sitems; + cnt_secret[i] = Plrs[i]->ssecret; + + if (dofrags) + cnt_frags[i] = WI_fragSum (i); + } + S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); + ng_state = 10; + } + + if (ng_state == 2) + { + if (!(bcnt&3)) + S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE); + + stillticking = false; + + for (i=0 ; i Plrs[i]->skills) + cnt_kills[i] = Plrs[i]->skills; + else + stillticking = true; + } + + if (!stillticking) + { + S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); + ng_state++; } } - y += lineheight + CleanYfac; - } - - // Draw "MISSED" line - y += 3 * CleanYfac; - screen->DrawText(SmallFont, CR_DARKGRAY, name_x, y, GStrings("SCORE_MISSED"), DTA_CleanNoMove, true, TAG_DONE); - WI_drawPercent(SmallFont, kills_x, y, missed_kills, wbs->maxkills, false, CR_DARKGRAY); - if (ng_state >= 4) - { - WI_drawPercent(SmallFont, bonus_x, y, missed_items, wbs->maxitems, false, CR_DARKGRAY); - if (ng_state >= 6) - { - WI_drawPercent(SmallFont, secret_x, y, missed_secrets, wbs->maxsecret, false, CR_DARKGRAY); - } - } - - // Draw "TOTAL" line - y += height + 3 * CleanYfac; - color = (gameinfo.gametype & GAME_Raven) ? CR_GREEN : CR_UNTRANSLATED; - screen->DrawText(SmallFont, color, name_x, y, GStrings("SCORE_TOTAL"), DTA_CleanNoMove, true, TAG_DONE); - WI_drawNum(SmallFont, kills_x, y, wbs->maxkills, 0, false, color); - if (ng_state >= 4) - { - WI_drawNum(SmallFont, bonus_x, y, wbs->maxitems, 0, false, color); - if (ng_state >= 6) - { - WI_drawNum(SmallFont, secret_x, y, wbs->maxsecret, 0, false, color); - } - } -} - -static int sp_state; - -void WI_initStats () -{ - state = StatCount; - acceleratestage = 0; - sp_state = 1; - cnt_kills[0] = cnt_items[0] = cnt_secret[0] = -1; - cnt_time = cnt_par = -1; - cnt_pause = TICRATE; - - cnt_total_time = -1; -} - -void WI_updateStats () -{ - WI_updateAnimatedBack (); - - if (acceleratestage && sp_state != 10) - { - acceleratestage = 0; - sp_state = 10; - S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); - - cnt_kills[0] = plrs[me].skills; - cnt_items[0] = plrs[me].sitems; - cnt_secret[0] = plrs[me].ssecret; - cnt_time = Tics2Seconds(plrs[me].stime); - cnt_par = wbs->partime / TICRATE; - cnt_total_time = Tics2Seconds(wbs->totaltime); - } - - if (sp_state == 2) - { - if (gameinfo.intermissioncounter) - { - cnt_kills[0] += 2; - - if (!(bcnt&3)) - S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE); - } - if (!gameinfo.intermissioncounter || cnt_kills[0] >= plrs[me].skills) - { - cnt_kills[0] = plrs[me].skills; - S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); - sp_state++; - } - } - else if (sp_state == 4) - { - if (gameinfo.intermissioncounter) - { - cnt_items[0] += 2; - - if (!(bcnt&3)) - S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE); - } - if (!gameinfo.intermissioncounter || cnt_items[0] >= plrs[me].sitems) - { - cnt_items[0] = plrs[me].sitems; - S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); - sp_state++; - } - } - else if (sp_state == 6) - { - if (gameinfo.intermissioncounter) - { - cnt_secret[0] += 2; - - if (!(bcnt&3)) - S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE); - } - if (!gameinfo.intermissioncounter || cnt_secret[0] >= plrs[me].ssecret) - { - cnt_secret[0] = plrs[me].ssecret; - S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); - sp_state++; - } - } - else if (sp_state == 8) - { - if (gameinfo.intermissioncounter) + else if (ng_state == 4) { if (!(bcnt&3)) S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE); - cnt_time += 3; - cnt_par += 3; - cnt_total_time += 3; - } + stillticking = false; - int sec = Tics2Seconds(plrs[me].stime); - if (!gameinfo.intermissioncounter || cnt_time >= sec) - cnt_time = sec; - - int tsec = Tics2Seconds(wbs->totaltime); - if (!gameinfo.intermissioncounter || cnt_total_time >= tsec) - cnt_total_time = tsec; - - if (!gameinfo.intermissioncounter || cnt_par >= wbs->partime / TICRATE) - { - cnt_par = wbs->partime / TICRATE; - - if (cnt_time >= sec) + for (i=0 ; i Plrs[i]->sitems) + cnt_items[i] = Plrs[i]->sitems; + else + stillticking = true; + } + if (!stillticking) + { + S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); + ng_state++; + } + } + else if (ng_state == 6) + { + if (!(bcnt&3)) + S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE); + + stillticking = false; + + for (i=0 ; i Plrs[i]->ssecret) + cnt_secret[i] = Plrs[i]->ssecret; + else + stillticking = true; + } + + if (!stillticking) + { + S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); + ng_state += 1 + 2*!dofrags; + } + } + else if (ng_state == 8) + { + if (!(bcnt&3)) + S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE); + + stillticking = false; + + for (i=0 ; i= (fsum = WI_fragSum(i))) + cnt_frags[i] = fsum; + else + stillticking = true; + } + + if (!stillticking) + { + S_Sound (CHAN_VOICE | CHAN_UI, "intermission/cooptotal", 1, ATTN_NONE); + ng_state++; + } + } + else if (ng_state == 10) + { + int i; + for (i = 0; i < MAXPLAYERS; i++) + { + // If the player is in the game and not ready, stop checking + if (playeringame[i] && players[i].Bot == NULL && !playerready[i]) + break; + } + + // All players are ready; proceed. + if ((i == MAXPLAYERS && acceleratestage) || autoskip) + { + S_Sound (CHAN_VOICE | CHAN_UI, "intermission/pastcoopstats", 1, ATTN_NONE); + WI_initShowNextLoc(); + } + } + else if (ng_state & 1) + { + if (!--cnt_pause) + { + ng_state++; + cnt_pause = TICRATE; + } + } + } + + void WI_drawNetgameStats () + { + int i, x, y, ypadding, height, lineheight; + int maxnamewidth, maxscorewidth, maxiconheight; + int pwidth = IntermissionFont->GetCharWidth('%'); + int icon_x, name_x, kills_x, bonus_x, secret_x; + int bonus_len, secret_len; + int missed_kills, missed_items, missed_secrets; + float h, s, v, r, g, b; + EColorRange color; + const char *text_bonus, *text_secret, *text_kills; + FTexture *readyico = TexMan.FindTexture("READYICO"); + + // draw animated background + bg->drawBackground(state, false, false); + + y = WI_drawLF(); + + HU_GetPlayerWidths(maxnamewidth, maxscorewidth, maxiconheight); + // Use the readyico height if it's bigger. + height = readyico->GetScaledHeight() - readyico->GetScaledTopOffset(); + if (height > maxiconheight) + { + maxiconheight = height; + } + height = SmallFont->GetHeight() * CleanYfac; + lineheight = MAX(height, maxiconheight * CleanYfac); + ypadding = (lineheight - height + 1) / 2; + y += CleanYfac; + + text_bonus = GStrings((gameinfo.gametype & GAME_Raven) ? "SCORE_BONUS" : "SCORE_ITEMS"); + text_secret = GStrings("SCORE_SECRET"); + text_kills = GStrings("SCORE_KILLS"); + + icon_x = 8 * CleanXfac; + name_x = icon_x + maxscorewidth * CleanXfac; + kills_x = name_x + (maxnamewidth + MAX(SmallFont->StringWidth("XXXXX"), SmallFont->StringWidth(text_kills)) + 8) * CleanXfac; + bonus_x = kills_x + ((bonus_len = SmallFont->StringWidth(text_bonus)) + 8) * CleanXfac; + secret_x = bonus_x + ((secret_len = SmallFont->StringWidth(text_secret)) + 8) * CleanXfac; + + x = (SCREENWIDTH - secret_x) >> 1; + icon_x += x; + name_x += x; + kills_x += x; + bonus_x += x; + secret_x += x; + + color = (gameinfo.gametype & GAME_Raven) ? CR_GREEN : CR_UNTRANSLATED; + + screen->DrawText(SmallFont, color, name_x, y, GStrings("SCORE_NAME"), DTA_CleanNoMove, true, TAG_DONE); + screen->DrawText(SmallFont, color, kills_x - SmallFont->StringWidth(text_kills)*CleanXfac, y, text_kills, DTA_CleanNoMove, true, TAG_DONE); + screen->DrawText(SmallFont, color, bonus_x - bonus_len*CleanXfac, y, text_bonus, DTA_CleanNoMove, true, TAG_DONE); + screen->DrawText(SmallFont, color, secret_x - secret_len*CleanXfac, y, text_secret, DTA_CleanNoMove, true, TAG_DONE); + y += height + 6 * CleanYfac; + + missed_kills = wbs->maxkills; + missed_items = wbs->maxitems; + missed_secrets = wbs->maxsecret; + + // Draw lines for each player + for (i = 0; i < MAXPLAYERS; ++i) + { + player_t *player; + + if (!playeringame[i]) + continue; + + player = &players[i]; + + D_GetPlayerColor(i, &h, &s, &v, NULL); + HSVtoRGB(&r, &g, &b, h, s, v); + + screen->Dim(MAKERGB(clamp(int(r*255.f), 0, 255), + clamp(int(g*255.f), 0, 255), + clamp(int(b*255.f), 0, 255)), 0.8f, x, y - ypadding, (secret_x - x) + (8 * CleanXfac), lineheight); + + if (playerready[i] || player->Bot != NULL) // Bots are automatically assumed ready, to prevent confusion + screen->DrawTexture(readyico, x - (readyico->GetWidth() * CleanXfac), y, DTA_CleanNoMove, true, TAG_DONE); + + color = (EColorRange)HU_GetRowColor(player, i == consoleplayer); + if (player->mo->ScoreIcon.isValid()) + { + FTexture *pic = TexMan[player->mo->ScoreIcon]; + screen->DrawTexture(pic, icon_x, y, DTA_CleanNoMove, true, TAG_DONE); + } + screen->DrawText(SmallFont, color, name_x, y + ypadding, player->userinfo.GetName(), DTA_CleanNoMove, true, TAG_DONE); + WI_drawPercent(SmallFont, kills_x, y + ypadding, cnt_kills[i], wbs->maxkills, false, color); + missed_kills -= cnt_kills[i]; + if (ng_state >= 4) + { + WI_drawPercent(SmallFont, bonus_x, y + ypadding, cnt_items[i], wbs->maxitems, false, color); + missed_items -= cnt_items[i]; + if (ng_state >= 6) + { + WI_drawPercent(SmallFont, secret_x, y + ypadding, cnt_secret[i], wbs->maxsecret, false, color); + missed_secrets -= cnt_secret[i]; + } + } + y += lineheight + CleanYfac; + } + + // Draw "MISSED" line + y += 3 * CleanYfac; + screen->DrawText(SmallFont, CR_DARKGRAY, name_x, y, GStrings("SCORE_MISSED"), DTA_CleanNoMove, true, TAG_DONE); + WI_drawPercent(SmallFont, kills_x, y, missed_kills, wbs->maxkills, false, CR_DARKGRAY); + if (ng_state >= 4) + { + WI_drawPercent(SmallFont, bonus_x, y, missed_items, wbs->maxitems, false, CR_DARKGRAY); + if (ng_state >= 6) + { + WI_drawPercent(SmallFont, secret_x, y, missed_secrets, wbs->maxsecret, false, CR_DARKGRAY); + } + } + + // Draw "TOTAL" line + y += height + 3 * CleanYfac; + color = (gameinfo.gametype & GAME_Raven) ? CR_GREEN : CR_UNTRANSLATED; + screen->DrawText(SmallFont, color, name_x, y, GStrings("SCORE_TOTAL"), DTA_CleanNoMove, true, TAG_DONE); + WI_drawNum(SmallFont, kills_x, y, wbs->maxkills, 0, false, color); + if (ng_state >= 4) + { + WI_drawNum(SmallFont, bonus_x, y, wbs->maxitems, 0, false, color); + if (ng_state >= 6) + { + WI_drawNum(SmallFont, secret_x, y, wbs->maxsecret, 0, false, color); + } + } + } + + + void WI_initStats () + { + state = StatCount; + acceleratestage = 0; + sp_state = 1; + cnt_kills[0] = cnt_items[0] = cnt_secret[0] = -1; + cnt_time = cnt_par = -1; + cnt_pause = TICRATE; + + cnt_total_time = -1; + } + + void WI_updateStats () + { + if (acceleratestage && sp_state != 10) + { + acceleratestage = 0; + sp_state = 10; + S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); + + cnt_kills[0] = Plrs[me]->skills; + cnt_items[0] = Plrs[me]->sitems; + cnt_secret[0] = Plrs[me]->ssecret; + cnt_time = Tics2Seconds(Plrs[me]->stime); + cnt_par = wbs->partime / TICRATE; + cnt_total_time = Tics2Seconds(wbs->totaltime); + } + + if (sp_state == 2) + { + if (gameinfo.intermissioncounter) + { + cnt_kills[0] += 2; + + if (!(bcnt&3)) + S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE); + } + if (!gameinfo.intermissioncounter || cnt_kills[0] >= Plrs[me]->skills) + { + cnt_kills[0] = Plrs[me]->skills; S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); sp_state++; } } - } - else if (sp_state == 10) - { - if (acceleratestage) + else if (sp_state == 4) { - S_Sound (CHAN_VOICE | CHAN_UI, "intermission/paststats", 1, ATTN_NONE); - WI_initShowNextLoc(); + if (gameinfo.intermissioncounter) + { + cnt_items[0] += 2; + + if (!(bcnt&3)) + S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE); + } + if (!gameinfo.intermissioncounter || cnt_items[0] >= Plrs[me]->sitems) + { + cnt_items[0] = Plrs[me]->sitems; + S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); + sp_state++; + } + } + else if (sp_state == 6) + { + if (gameinfo.intermissioncounter) + { + cnt_secret[0] += 2; + + if (!(bcnt&3)) + S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE); + } + if (!gameinfo.intermissioncounter || cnt_secret[0] >= Plrs[me]->ssecret) + { + cnt_secret[0] = Plrs[me]->ssecret; + S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); + sp_state++; + } + } + else if (sp_state == 8) + { + if (gameinfo.intermissioncounter) + { + if (!(bcnt&3)) + S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE); + + cnt_time += 3; + cnt_par += 3; + cnt_total_time += 3; + } + + int sec = Tics2Seconds(Plrs[me]->stime); + if (!gameinfo.intermissioncounter || cnt_time >= sec) + cnt_time = sec; + + int tsec = Tics2Seconds(wbs->totaltime); + if (!gameinfo.intermissioncounter || cnt_total_time >= tsec) + cnt_total_time = tsec; + + if (!gameinfo.intermissioncounter || cnt_par >= wbs->partime / TICRATE) + { + cnt_par = wbs->partime / TICRATE; + + if (cnt_time >= sec) + { + cnt_total_time = tsec; + S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); + sp_state++; + } + } + } + else if (sp_state == 10) + { + if (acceleratestage) + { + S_Sound (CHAN_VOICE | CHAN_UI, "intermission/paststats", 1, ATTN_NONE); + WI_initShowNextLoc(); + } + } + else if (sp_state & 1) + { + if (!--cnt_pause) + { + sp_state++; + cnt_pause = TICRATE; + } } } - else if (sp_state & 1) + + void WI_drawStats (void) { - if (!--cnt_pause) - { - sp_state++; - cnt_pause = TICRATE; - } - } -} + // line height + int lh; -void WI_drawStats (void) -{ - // line height - int lh; + lh = IntermissionFont->GetHeight() * 3 / 2; - lh = IntermissionFont->GetHeight() * 3 / 2; + // draw animated background + bg->drawBackground(state, false, false); - // draw animated background - WI_drawBackground(); + WI_drawLF(); - WI_drawLF(); - - if (gameinfo.gametype & GAME_DoomChex) - { - screen->DrawTexture (kills, SP_STATSX, SP_STATSY, DTA_Clean, true, TAG_DONE); - WI_drawPercent (IntermissionFont, 320 - SP_STATSX, SP_STATSY, cnt_kills[0], wbs->maxkills); - - screen->DrawTexture (items, SP_STATSX, SP_STATSY+lh, DTA_Clean, true, TAG_DONE); - WI_drawPercent (IntermissionFont, 320 - SP_STATSX, SP_STATSY+lh, cnt_items[0], wbs->maxitems); - - screen->DrawTexture (sp_secret, SP_STATSX, SP_STATSY+2*lh, DTA_Clean, true, TAG_DONE); - WI_drawPercent (IntermissionFont, 320 - SP_STATSX, SP_STATSY+2*lh, cnt_secret[0], wbs->maxsecret); - - screen->DrawTexture (timepic, SP_TIMEX, SP_TIMEY, DTA_Clean, true, TAG_DONE); - WI_drawTime (160 - SP_TIMEX, SP_TIMEY, cnt_time); - if (wi_showtotaltime) + if (gameinfo.gametype & GAME_DoomChex) { - WI_drawTime (160 - SP_TIMEX, SP_TIMEY + lh, cnt_total_time, true); // no 'sucks' for total time ever! - } + screen->DrawTexture (kills, SP_STATSX, SP_STATSY, DTA_Clean, true, TAG_DONE); + WI_drawPercent (IntermissionFont, 320 - SP_STATSX, SP_STATSY, cnt_kills[0], wbs->maxkills); - if (wbs->partime) - { - screen->DrawTexture (par, 160 + SP_TIMEX, SP_TIMEY, DTA_Clean, true, TAG_DONE); - WI_drawTime (320 - SP_TIMEX, SP_TIMEY, cnt_par); - } + screen->DrawTexture (items, SP_STATSX, SP_STATSY+lh, DTA_Clean, true, TAG_DONE); + WI_drawPercent (IntermissionFont, 320 - SP_STATSX, SP_STATSY+lh, cnt_items[0], wbs->maxitems); - } - else - { - screen->DrawText (BigFont, CR_UNTRANSLATED, 50, 65, GStrings("TXT_IMKILLS"), DTA_Clean, true, DTA_Shadow, true, TAG_DONE); - screen->DrawText (BigFont, CR_UNTRANSLATED, 50, 90, GStrings("TXT_IMITEMS"), DTA_Clean, true, DTA_Shadow, true, TAG_DONE); - screen->DrawText (BigFont, CR_UNTRANSLATED, 50, 115, GStrings("TXT_IMSECRETS"), DTA_Clean, true, DTA_Shadow, true, TAG_DONE); + screen->DrawTexture (sp_secret, SP_STATSX, SP_STATSY+2*lh, DTA_Clean, true, TAG_DONE); + WI_drawPercent (IntermissionFont, 320 - SP_STATSX, SP_STATSY+2*lh, cnt_secret[0], wbs->maxsecret); - int countpos = gameinfo.gametype==GAME_Strife? 285:270; - if (sp_state >= 2) - { - WI_drawPercent (IntermissionFont, countpos, 65, cnt_kills[0], wbs->maxkills); - } - if (sp_state >= 4) - { - WI_drawPercent (IntermissionFont, countpos, 90, cnt_items[0], wbs->maxitems); - } - if (sp_state >= 6) - { - WI_drawPercent (IntermissionFont, countpos, 115, cnt_secret[0], wbs->maxsecret); - } - if (sp_state >= 8) - { - screen->DrawText (BigFont, CR_UNTRANSLATED, 85, 160, GStrings("TXT_IMTIME"), - DTA_Clean, true, DTA_Shadow, true, TAG_DONE); - WI_drawTime (249, 160, cnt_time); + screen->DrawTexture (timepic, SP_TIMEX, SP_TIMEY, DTA_Clean, true, TAG_DONE); + WI_drawTime (160 - SP_TIMEX, SP_TIMEY, cnt_time); if (wi_showtotaltime) { - WI_drawTime (249, 180, cnt_total_time); + WI_drawTime (160 - SP_TIMEX, SP_TIMEY + lh, cnt_total_time, true); // no 'sucks' for total time ever! } - } - } -} -// ==================================================================== -// WI_checkForAccelerate -// Purpose: See if the player has hit either the attack or use key -// or mouse button. If so we set acceleratestage to 1 and -// all those display routines above jump right to the end. -// Args: none -// Returns: void -// -// ==================================================================== -void WI_checkForAccelerate(void) -{ - int i; - player_t *player; - - // check for button presses to skip delays - for (i = 0, player = players; i < MAXPLAYERS; i++, player++) - { - if (playeringame[i]) - { - if ((player->cmd.ucmd.buttons ^ player->oldbuttons) && - ((players[i].cmd.ucmd.buttons & players[i].oldbuttons) - == players[i].oldbuttons) && player->Bot == NULL) + if (wbs->partime) { - acceleratestage = 1; - playerready[i] = true; + screen->DrawTexture (par, 160 + SP_TIMEX, SP_TIMEY, DTA_Clean, true, TAG_DONE); + WI_drawTime (320 - SP_TIMEX, SP_TIMEY, cnt_par); } - player->oldbuttons = player->cmd.ucmd.buttons; + } - } -} - -// ==================================================================== -// WI_Ticker -// Purpose: Do various updates every gametic, for stats, animation, -// checking that intermission music is running, etc. -// Args: none -// Returns: void -// -// ==================================================================== -void WI_Ticker(void) -{ - // counter for general background animation - bcnt++; - - if (bcnt == 1) - { - // intermission music - use the defaults if none specified - if (level.info->InterMusic.IsNotEmpty()) - S_ChangeMusic(level.info->InterMusic, level.info->intermusicorder); else - S_ChangeMusic (gameinfo.intermissionMusic.GetChars(), gameinfo.intermissionOrder); - - } - - WI_checkForAccelerate(); - - switch (state) - { - case StatCount: - if (deathmatch) WI_updateDeathmatchStats(); - else if (multiplayer) WI_updateNetgameStats(); - else WI_updateStats(); - break; - - case ShowNextLoc: - WI_updateShowNextLoc(); - break; - - case NoState: - WI_updateNoState(); - break; - - case LeavingIntermission: - // Hush, GCC. - break; - } -} - - -void WI_loadData(void) -{ - entering.Init(gameinfo.mStatscreenEnteringFont); - finished.Init(gameinfo.mStatscreenFinishedFont); - mapname.Init(gameinfo.mStatscreenMapNameFont); - - if (gameinfo.gametype & GAME_DoomChex) - { - kills = TexMan["WIOSTK"]; // "kills" - secret = TexMan["WIOSTS"]; // "scrt" - sp_secret = TexMan["WISCRT2"]; // "secret" - items = TexMan["WIOSTI"]; // "items" - frags = TexMan["WIFRGS"]; // "frgs" - timepic = TexMan["WITIME"]; // "time" - sucks = TexMan["WISUCKS"]; // "sucks" - par = TexMan["WIPAR"]; // "par" - killers = TexMan["WIKILRS"]; // "killers" (vertical] - victims = TexMan["WIVCTMS"]; // "victims" (horiz] - total = TexMan["WIMSTT"]; // "total" -// star = TexMan["STFST01"]; // your face -// bstar = TexMan["STFDEAD0"]; // dead face - p = TexMan["STPBANY"]; - } -#if 0 - else if (gameinfo.gametype & GAME_Raven) - { - if (gameinfo.gametype == GAME_Heretic) { - star = TexMan["FACEA0"]; - bstar = TexMan["FACEB0"]; + screen->DrawText (BigFont, CR_UNTRANSLATED, 50, 65, GStrings("TXT_IMKILLS"), DTA_Clean, true, DTA_Shadow, true, TAG_DONE); + screen->DrawText (BigFont, CR_UNTRANSLATED, 50, 90, GStrings("TXT_IMITEMS"), DTA_Clean, true, DTA_Shadow, true, TAG_DONE); + screen->DrawText (BigFont, CR_UNTRANSLATED, 50, 115, GStrings("TXT_IMSECRETS"), DTA_Clean, true, DTA_Shadow, true, TAG_DONE); + + int countpos = gameinfo.gametype==GAME_Strife? 285:270; + if (sp_state >= 2) + { + WI_drawPercent (IntermissionFont, countpos, 65, cnt_kills[0], wbs->maxkills); + } + if (sp_state >= 4) + { + WI_drawPercent (IntermissionFont, countpos, 90, cnt_items[0], wbs->maxitems); + } + if (sp_state >= 6) + { + WI_drawPercent (IntermissionFont, countpos, 115, cnt_secret[0], wbs->maxsecret); + } + if (sp_state >= 8) + { + screen->DrawText (BigFont, CR_UNTRANSLATED, 85, 160, GStrings("TXT_IMTIME"), + DTA_Clean, true, DTA_Shadow, true, TAG_DONE); + WI_drawTime (249, 160, cnt_time); + if (wi_showtotaltime) + { + WI_drawTime (249, 180, cnt_total_time); + } + } } - else + } + + // ==================================================================== + // WI_checkForAccelerate + // Purpose: See if the player has hit either the attack or use key + // or mouse button. If so we set acceleratestage to 1 and + // all those display routines above jump right to the end. + // Args: none + // Returns: void + // + // ==================================================================== + void WI_checkForAccelerate(void) + { + int i; + player_t *player; + + // check for button presses to skip delays + for (i = 0, player = players; i < MAXPLAYERS; i++, player++) + { + if (playeringame[i]) + { + if ((player->cmd.ucmd.buttons ^ player->oldbuttons) && + ((players[i].cmd.ucmd.buttons & players[i].oldbuttons) + == players[i].oldbuttons) && player->Bot == NULL) + { + acceleratestage = 1; + playerready[i] = true; + } + player->oldbuttons = player->cmd.ucmd.buttons; + } + } + } + + // ==================================================================== + // WI_Ticker + // Purpose: Do various updates every gametic, for stats, animation, + // checking that intermission music is running, etc. + // Args: none + // Returns: void + // + // ==================================================================== + void WI_Ticker(void) + { + // counter for general background animation + bcnt++; + + if (bcnt == 1) + { + // intermission music - use the defaults if none specified + auto mus = level.info->MapInterMusic.CheckKey(wbs->next); + if (mus != nullptr) + S_ChangeMusic(mus->first, mus->second); + else if (level.info->InterMusic.IsNotEmpty()) + S_ChangeMusic(level.info->InterMusic, level.info->intermusicorder); + else + S_ChangeMusic (gameinfo.intermissionMusic.GetChars(), gameinfo.intermissionOrder); + + } + + WI_checkForAccelerate(); + + switch (state) + { + case StatCount: + if (deathmatch) WI_updateDeathmatchStats(); + else if (multiplayer) WI_updateNetgameStats(); + else WI_updateStats(); + break; + + case ShowNextLoc: + WI_updateShowNextLoc(); + break; + + case NoState: + WI_updateNoState(); + break; + + case LeavingIntermission: + // Hush, GCC. + break; + } + } + + + void WI_loadData(void) + { + entering.Init(gameinfo.mStatscreenEnteringFont); + finished.Init(gameinfo.mStatscreenFinishedFont); + mapname.Init(gameinfo.mStatscreenMapNameFont); + + if (gameinfo.gametype & GAME_DoomChex) + { + kills = TexMan["WIOSTK"]; // "kills" + secret = TexMan["WIOSTS"]; // "scrt" + sp_secret = TexMan["WISCRT2"]; // "secret" + items = TexMan["WIOSTI"]; // "items" + frags = TexMan["WIFRGS"]; // "frgs" + timepic = TexMan["WITIME"]; // "time" + sucks = TexMan["WISUCKS"]; // "sucks" + par = TexMan["WIPAR"]; // "par" + killers = TexMan["WIKILRS"]; // "killers" (vertical] + victims = TexMan["WIVCTMS"]; // "victims" (horiz] + total = TexMan["WIMSTT"]; // "total" + // star = TexMan["STFST01"]; // your face + // bstar = TexMan["STFDEAD0"]; // dead face + p = TexMan["STPBANY"]; + } + #if 0 + else if (gameinfo.gametype & GAME_Raven) + { + if (gameinfo.gametype == GAME_Heretic) + { + star = TexMan["FACEA0"]; + bstar = TexMan["FACEB0"]; + } + else + { + star = BigFont->GetChar('*', NULL); + bstar = star; + } + } + else // Strife needs some handling, too! { star = BigFont->GetChar('*', NULL); bstar = star; } + #endif + + // Use the local level structure which can be overridden by hubs + lnametexts[0] = level.LevelName; + + level_info_t *li = FindLevelInfo(wbs->next); + if (li) lnametexts[1] = li->LookupLevelName(); + else lnametexts[1] = ""; + + bg = new FInterBackground(wbs); + noautostartmap = bg->LoadBackground(false); } - else // Strife needs some handling, too! + + void WI_unloadData () { - star = BigFont->GetChar('*', NULL); - bstar = star; + // [RH] The texture data gets unloaded at pre-map time, so there's nothing to do here + if (bg != nullptr) delete bg; + bg = nullptr; + return; } -#endif - // Use the local level structure which can be overridden by hubs - lnametexts[0] = level.LevelName; - - level_info_t *li = FindLevelInfo(wbs->next); - if (li) lnametexts[1] = li->LookupLevelName(); - else lnametexts[1] = ""; - - WI_LoadBackground(false); -} - -void WI_unloadData () -{ - // [RH] The texture data gets unloaded at pre-map time, so there's nothing to do here - return; -} - -void WI_Drawer (void) -{ - switch (state) + void WI_Drawer (void) { - case StatCount: + switch (state) + { + case StatCount: + if (deathmatch) + WI_drawDeathmatchStats(); + else if (multiplayer) + WI_drawNetgameStats(); + else + WI_drawStats(); + break; + + case ShowNextLoc: + WI_drawShowNextLoc(); + break; + + case LeavingIntermission: + break; + + default: + WI_drawNoState(); + break; + } + } + + + void WI_initVariables (wbstartstruct_t *wbstartstruct) + { + wbs = wbstartstruct; + acceleratestage = 0; + cnt = bcnt = 0; + me = wbs->pnum; + for (int i = 0; i < 8; i++) Plrs[i] = &wbs->plyr[i]; + } + + void WI_Start (wbstartstruct_t *wbstartstruct) + { + noautostartmap = false; + V_SetBlend (0,0,0,0); + WI_initVariables (wbstartstruct); + WI_loadData (); if (deathmatch) - WI_drawDeathmatchStats(); + WI_initDeathmatchStats(); else if (multiplayer) - WI_drawNetgameStats(); + WI_initNetgameStats(); else - WI_drawStats(); - break; - - case ShowNextLoc: - WI_drawShowNextLoc(); - break; - - case LeavingIntermission: - break; - - default: - WI_drawNoState(); - break; + WI_initStats(); + S_StopAllChannels (); + SN_StopAllSequences (); } -} +}; +static FIntermissionScreen WI_Screen; -void WI_initVariables (wbstartstruct_t *wbstartstruct) +void WI_Ticker() { - wbs = wbstartstruct; - acceleratestage = 0; - cnt = bcnt = 0; - me = wbs->pnum; - plrs = wbs->plyr; + WI_Screen.bg->updateAnimatedBack(); + WI_Screen.WI_Ticker(); } -void WI_Start (wbstartstruct_t *wbstartstruct) +// Called by main loop, +// draws the intermission directly into the screen buffer. +void WI_Drawer() { - noautostartmap = false; - V_SetBlend (0,0,0,0); - WI_initVariables (wbstartstruct); - WI_loadData (); - if (deathmatch) - WI_initDeathmatchStats(); - else if (multiplayer) - WI_initNetgameStats(); - else - WI_initStats(); - S_StopAllChannels (); - SN_StopAllSequences (); + WI_Screen.WI_Drawer(); } + +// Setup for an intermission screen. +void WI_Start(wbstartstruct_t *wbstartstruct) +{ + WI_Screen.WI_Start(wbstartstruct); +} + + +DEFINE_FIELD_X(WBPlayerStruct, wbplayerstruct_t, skills); +DEFINE_FIELD_X(WBPlayerStruct, wbplayerstruct_t, sitems); +DEFINE_FIELD_X(WBPlayerStruct, wbplayerstruct_t, ssecret); +DEFINE_FIELD_X(WBPlayerStruct, wbplayerstruct_t, stime); +DEFINE_FIELD_X(WBPlayerStruct, wbplayerstruct_t, frags); +DEFINE_FIELD_X(WBPlayerStruct, wbplayerstruct_t, fragcount); + +DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, finished_ep); +DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, next_ep); +DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, current); +DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, next); +DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, LName0); +DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, LName1); +DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, maxkills); +DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, maxitems); +DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, maxsecret); +DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, maxfrags); +DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, partime); +DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, sucktime); +DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, totaltime); +DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, pnum); +DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, plyr); diff --git a/src/wi_stuff.h b/src/wi_stuff.h index 7eefb9ee5f..04acb1231e 100644 --- a/src/wi_stuff.h +++ b/src/wi_stuff.h @@ -32,8 +32,6 @@ class FTexture; // struct wbplayerstruct_t { - bool in; // whether the player is in game - // Player stats, kills, collected items etc. int skills; int sitems; @@ -51,8 +49,8 @@ struct wbstartstruct_t FString current; // [RH] Name of map just finished FString next; // next level, [RH] actual map name - FTexture *LName0; - FTexture *LName1; + FTextureID LName0; + FTextureID LName1; int maxkills; int maxitems; diff --git a/src/zstring.cpp b/src/zstring.cpp index 83a2a20d81..a74edd01b5 100644 --- a/src/zstring.cpp +++ b/src/zstring.cpp @@ -393,7 +393,6 @@ void FString::Remove(size_t index, size_t remlen) } else { - remlen = Len() - remlen < remlen ? Len() - remlen : remlen; if (Data()->RefCount == 1) { // Can do this in place memmove(Chars + index, Chars + index + remlen, Len() - index - remlen); diff --git a/wadsrc/static/compatibility.txt b/wadsrc/static/compatibility.txt index 020d8477d6..9f7564b95f 100644 --- a/wadsrc/static/compatibility.txt +++ b/wadsrc/static/compatibility.txt @@ -497,6 +497,12 @@ ABC4EB5A1535ECCD0061AD14F3547908 // Plutonia Experiment, map26 setsectorspecial 156 0 } +B68EB7CFB4CC481796E2919B9C16DFBD // Moc11.wad e1m6 +{ + setvertex 1650 -3072 2671 + setvertex 1642 -2944 2671 +} + 712BB4CFBD0753178CA0C6814BE4C288 // map12 BTSX_E1 - patch some rendering glitches that are problematic to detect { setsectortag 545 32000 diff --git a/wadsrc/static/filter/doom.doom1/sprofs.txt b/wadsrc/static/filter/doom.doom1/sprofs.txt index eab3dc4ef0..1485959efd 100644 --- a/wadsrc/static/filter/doom.doom1/sprofs.txt +++ b/wadsrc/static/filter/doom.doom1/sprofs.txt @@ -111,8 +111,6 @@ SPIDR0, 110, 111, iwad SPIDS0, 98, 35, iwad SPOSH0, 14, 60, iwad SPOSL0, 24, 15, iwad -SSWVG0, 17, 55, iwad -SSWVH0, 17, 52, iwad SSWVI0, 18, 54, iwad SSWVJ0, 15, 44, iwad SSWVK0, 15, 40, iwad diff --git a/wadsrc/static/filter/doom.doom2/sprofs.txt b/wadsrc/static/filter/doom.doom2/sprofs.txt index eab3dc4ef0..1485959efd 100644 --- a/wadsrc/static/filter/doom.doom2/sprofs.txt +++ b/wadsrc/static/filter/doom.doom2/sprofs.txt @@ -111,8 +111,6 @@ SPIDR0, 110, 111, iwad SPIDS0, 98, 35, iwad SPOSH0, 14, 60, iwad SPOSL0, 24, 15, iwad -SSWVG0, 17, 55, iwad -SSWVH0, 17, 52, iwad SSWVI0, 18, 54, iwad SSWVJ0, 15, 44, iwad SSWVK0, 15, 40, iwad diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index b1a8172d71..55477ee2e2 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -1566,6 +1566,23 @@ TXT_COMM0 = "Incoming Message"; TXT_COMM1 = "Incoming Message from BlackBird"; +AMMO_CLIP = "Bullets"; +AMMO_SHELLS = "Shotgun Shells"; +AMMO_ROCKETS = "Rockets"; +AMMO_CELLS = "Energy Cells"; +AMMO_GOLDWAND = "Wand Crystals"; +AMMO_CROSSBOW = "Ethereal Arrows"; +AMMO_BLASTER = "Claw Orbs"; +AMMO_MACE = "Mace Spheres"; +AMMO_SKULLROD = "Hellstaff Runes"; +AMMO_PHOENIXROD = "Flame Orbs"; +AMMO_MANA1 = "Blue Mana"; +AMMO_MANA2 = "Green Mana"; +$ifgame(chex) AMMO_CLIP = "Mini Zorch Charge"; +$ifgame(chex) AMMO_SHELLS = "Large Zorcher Charge"; +$ifgame(chex) AMMO_ROCKETS = "Propulsor Charge"; +$ifgame(chex) AMMO_CELLS = "Phasing Zorcher Charge"; + // Menu Strings // Main Menu @@ -1759,6 +1776,7 @@ MOUSEMNU_LOOKSTRAFE = "Lookstrafe"; JOYMNU_CONFIG = "CONFIGURE CONTROLLER"; JOYMNU_OPTIONS = "CONTROLLER OPTIONS"; +JOYMNU_NOMENU = "Block controller input in menu"; // Player Setup Menu MNU_PLAYERSETUP = "PLAYER SETUP"; @@ -1878,7 +1896,7 @@ MISCMNU_DEHLOAD = "Load *.deh/*.bex lumps"; MISCMNU_CACHENODES = "Cache nodes"; MISCMNU_CACHETIME = "Time threshold for node caching"; MISCMNU_CLEARNODECACHE = "Clear node cache"; - +MISCMNU_INTERSCROLL = "Allow skipping of intermission scrollers"; // Automap Options AUTOMAPMNU_TITLE = "AUTOMAP OPTIONS"; AUTOMAPMNU_COLORSET = "Map color set"; @@ -2268,6 +2286,7 @@ OPTVAL_FRONT = "Front"; OPTVAL_ANIMATED = "Animated"; OPTVAL_ROTATED = "Rotated"; OPTVAL_MAPDEFINEDCOLORSONLY = "Map defined colors only"; +OPTVAL_NODOORS = "All except doors"; OPTVAL_DOUBLE = "Double"; OPTVAL_TRIPLE = "Triple"; OPTVAL_QUADRUPLE = "Quadruple"; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 3fba5fd6a0..2dd3e5e559 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -558,10 +558,11 @@ OptionMenu "MouseOptions" // //------------------------------------------------------------------------------------------- -OptionMenu "JoystickOptions" +OptionMenu "JoystickOptionsDefaults" { Title "$JOYMNU_OPTIONS" Option "$JOYMNU_ENABLE", "use_joystick", "YesNo" + Option "$JOYMNU_NOMENU", "m_blockcontrollers", "YesNo" IfOption(Windows) { Option "$JOYMNU_DINPUT", "joy_dinput", "YesNo" @@ -576,6 +577,11 @@ OptionMenu "JoystickOptions" // The rest will be filled in by joystick code if devices get connected or disconnected } +OptionMenu "JoystickOptions" +{ + Title "$JOYMNU_OPTIONS" +} + OptionValue "JoyAxisMapNames" { -1, "$OPTVAL_NONE" @@ -632,6 +638,7 @@ OptionValue PuffTypes { 0.0, "$OPTVAL_SPRITES" 1.0, "$OPTVAL_PARTICLES" + 2.0, "$OPTVAL_SPRITESPARTICLES" } OptionValue Wipes @@ -940,6 +947,7 @@ OptionMenu "MiscOptions" Option "$MISCMNU_SAVELOADCONFIRMATION", "saveloadconfirmation", "OnOff" Slider "$MISCMNU_AUTOSAVECOUNT", "autosavecount", 1, 20, 1, 0 Option "$MISCMNU_DEHLOAD", "dehload", "dehopt" + Option "$MISCMNU_INTERSCROLL", "nointerscrollabort", "OffOn" StaticText " " Option "$MISCMNU_CACHENODES", "gl_cachenodes", "OnOff" Slider "$MISCMNU_CACHETIME", "gl_cachetime", 0.0, 2.0, 0.1 @@ -1003,6 +1011,13 @@ OptionValue MapBackTypes 2, "$OPTVAL_MAPDEFINEDCOLORSONLY" } +OptionValue MapTriggers +{ + 0, "$OPTVAL_OFF" + 1, "$OPTVAL_NODOORS" + 2, "$OPTVAL_ON" +} + OptionMenu AutomapOptions { Title "$AUTOMAPMNU_TITLE" @@ -1026,7 +1041,7 @@ OptionMenu AutomapOptions Option "$AUTOMAPMNU_SHOWMAPLABEL", "am_showmaplabel", "MaplabelTypes" Option "$AUTOMAPMNU_DRAWMAPBACK", "am_drawmapback", "MapBackTypes" Option "$AUTOMAPMNU_SHOWKEYS", "am_showkeys", "OnOff" - Option "$AUTOMAPMNU_SHOWTRIGGERLINES", "am_showtriggerlines", "OnOff" + Option "$AUTOMAPMNU_SHOWTRIGGERLINES", "am_showtriggerlines", "MapTriggers" Option "$AUTOMAPMNU_SHOWTHINGSPRITES", "am_showthingsprites", "STSTypes" } diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index d755c85b97..bf7f75f188 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -9,16 +9,23 @@ #include "zscript/menu/menuitembase.txt" #include "zscript/menu/menu.txt" +#include "zscript/menu/messagebox.txt" #include "zscript/menu/listmenu.txt" #include "zscript/menu/listmenuitems.txt" #include "zscript/menu/optionmenu.txt" #include "zscript/menu/optionmenuitems.txt" #include "zscript/menu/colorpickermenu.txt" #include "zscript/menu/joystickmenu.txt" +#include "zscript/menu/loadsavemenu.txt" +#include "zscript/menu/playermenu.txt" #include "zscript/menu/playerdisplay.txt" #include "zscript/menu/playercontrols.txt" #include "zscript/menu/textentermenu.txt" #include "zscript/menu/videomenu.txt" +#include "zscript/menu/readthis.txt" +#include "zscript/menu/conversationmenu.txt" + +#include "zscript/statscreen/types.txt" #include "zscript/inventory/inventory.txt" #include "zscript/inventory/inv_misc.txt" diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 5e2454ed25..148fd4bd5e 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -189,30 +189,49 @@ class Actor : Thinker native native State MissileState; native voidptr /*DecalBase*/ DecalGenerator; native uint8 fountaincolor; - - native meta String Obituary; // Player was killed by this actor - native meta String HitObituary; // Player was killed by this actor in melee - native meta double DeathHeight; // Height on normal death - native meta double BurnHeight; // Height on burning death - native meta color BloodColor; // Colorized blood - native meta int GibHealth; // Negative health below which this monster dies an extreme death - native meta int WoundHealth; // Health needed to enter wound state - native meta double FastSpeed; // speed in fast mode - native meta double RDFactor; // Radius damage factor - native meta double CameraHeight; // Height of camera when used as such - native meta Sound HowlSound; // Sound being played when electrocuted or poisoned - native meta Name BloodType; // Blood replacement type - native meta Name BloodType2; // Bloopsplatter replacement type - native meta Name BloodType3; // AxeBlood replacement type - native meta bool DontHurtShooter; - native meta int ExplosionRadius; - native meta int ExplosionDamage; - native meta int MeleeDamage; - native meta Sound MeleeSound; - native meta Name MissileName; - native meta double MissileHeight; - + native double CameraHeight; // Height of camera when used as such + native double RadiusDamageFactor; // Radius damage factor + native double SelfDamageFactor; + native double StealthAlpha; + native int WoundHealth; // Health needed to enter wound state + native readonly color BloodColor; + native readonly int BloodTranslation; + meta String Obituary; // Player was killed by this actor + meta String HitObituary; // Player was killed by this actor in melee + meta double DeathHeight; // Height on normal death + meta double BurnHeight; // Height on burning death + meta int GibHealth; // Negative health below which this monster dies an extreme death + meta Sound HowlSound; // Sound being played when electrocuted or poisoned + meta Name BloodType; // Blood replacement type + meta Name BloodType2; // Bloopsplatter replacement type + meta Name BloodType3; // AxeBlood replacement type + meta bool DontHurtShooter; + meta int ExplosionRadius; + meta int ExplosionDamage; + meta int MeleeDamage; + meta Sound MeleeSound; + meta double MissileHeight; + meta Name MissileName; + meta double FastSpeed; // speed in fast mode + + Property prefix: none; + Property Obituary: Obituary; + Property HitObituary: HitObituary; + Property MeleeDamage: MeleeDamage; + Property MeleeSound: MeleeSound; + Property MissileHeight: MissileHeight; + Property MissileType: MissileName; + Property DontHurtShooter: DontHurtShooter; + Property ExplosionRadius: ExplosionRadius; + Property ExplosionDamage: ExplosionDamage; + //Property BloodType: BloodType, BloodType2, BloodType3; + Property FastSpeed: FastSpeed; + Property HowlSound: HowlSound; + Property GibHealth: GibHealth; + Property DeathHeight: DeathHeight; + Property BurnHeight: BurnHeight; + // need some definition work first //FRenderStyle RenderStyle; //int ConversationRoot; // THe root of the current dialogue @@ -230,7 +249,6 @@ class Actor : Thinker native native deprecated double ScaleX; native deprecated double ScaleY; - //int ConversationRoot; // THe root of the current dialogue; //FStrifeDialogueNode *Conversation; // [RH] The dialogue to show when this actor is used.; @@ -240,7 +258,7 @@ class Actor : Thinker native Health DEFAULT_HEALTH; Reactiontime 8; Radius 20; - RenderRadius 0; + RenderRadius 0; Height 16; Mass 100; RenderStyle 'Normal'; @@ -272,6 +290,7 @@ class Actor : Thinker native DefThreshold 100; BloodType "Blood", "BloodSplatter", "AxeBlood"; ExplosionDamage 128; + ExplosionRadius -1; // i.e. use ExplosionDamage value MissileHeight 32; SpriteAngle 0; SpriteRotation 0; @@ -279,6 +298,15 @@ class Actor : Thinker native VisibleAngles 0, 0; VisiblePitch 0, 0; DefaultStateUsage SUF_ACTOR|SUF_OVERLAY; + CameraHeight int.min; + FastSpeed -1; + RadiusDamageFactor 1; + SelfDamageFactor 1; + StealthAlpha 0; + WoundHealth 6; + GibHealth int.min; + DeathHeight -1; + BurnHeight -1; } // Functions @@ -311,7 +339,7 @@ class Actor : Thinker native virtual native void Touch(Actor toucher); virtual native void MarkPrecacheSounds(); - // Called by PIT_CheckThing to check if two actos actually can collide. + // Called by PIT_CheckThing to check if two actors actually can collide. virtual bool CanCollideWith(Actor other, bool passive) { return true; @@ -335,6 +363,81 @@ class Actor : Thinker native { return false; } + + virtual class GetBloodType(int type) + { + Class bloodcls; + if (type == 0) + { + bloodcls = BloodType; + } + else if (type == 1) + { + bloodcls = BloodType2; + } + else if (type == 2) + { + bloodcls = BloodType3; + } + else + { + return NULL; + } + + if (bloodcls != NULL) + { + bloodcls = GetReplacement(bloodcls); + } + return bloodcls; + } + + virtual int GetGibHealth() + { + if (GibHealth != int.min) + { + return -abs(GibHealth); + } + else + { + return -int(GetSpawnHealth() * gameinfo.gibfactor); + } + } + + virtual double GetDeathHeight() + { + // [RH] Allow the death height to be overridden using metadata. + double metaheight = -1; + if (DamageType == 'Fire') + { + metaheight = BurnHeight; + } + if (metaheight < 0) + { + metaheight = DeathHeight; + } + if (metaheight < 0) + { + return Height * 0.25; + } + else + { + return MAX(metaheight, 0); + } + } + + virtual String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack) + { + if (mod == 'Telefrag') + { + return "$OB_MONTELEFRAG"; + } + else if (mod == 'Melee' && HitObituary.Length() > 0) + { + return HitObituary; + } + return Obituary; + } + native static class GetReplacement(class cls); native static class GetReplacee(class cls); @@ -368,7 +471,7 @@ class Actor : Thinker native native Vector3 PosRelative(sector sec); native void HandleSpawnFlags(); - native void ExplodeMissile(line lin = null, Actor target = null); + native void ExplodeMissile(line lin = null, Actor target = null, bool onsky = false); native void RestoreDamage(); native int SpawnHealth(); native void SetDamage(int dmg); @@ -496,7 +599,7 @@ class Actor : Thinker native native bool TakeInventory(class itemclass, int amount, bool fromdecorate = false, bool notakeinfinite = false); native Inventory FindInventory(class itemtype, bool subclass = false); native Inventory GiveInventoryType(class itemtype); - native Inventory DropInventory (Inventory item); + native Inventory DropInventory (Inventory item, int amt = -1); native bool UseInventory(Inventory item); native void ObtainInventory(Actor other); native bool GiveAmmo (Class type, int amount); @@ -511,7 +614,6 @@ class Actor : Thinker native native double GetAngle(int flags, int ptr = AAPTR_TARGET); native double GetZAt(double px = 0, double py = 0, double angle = 0, int flags = 0, int pick_pointer = AAPTR_DEFAULT); native int GetSpawnHealth(); - native int GetGibHealth(); native double GetCrouchFactor(int ptr = AAPTR_PLAYER1); native double GetCVar(string cvar); native int GetPlayerInput(int inputnum, int ptr = AAPTR_DEFAULT); @@ -725,6 +827,68 @@ class Actor : Thinker native // Meh, MBF redundant functions. Only for DeHackEd support. native bool A_LineEffect(int boomspecial = 0, int tag = 0); // End of MBF redundant functions. + + + //========================================================================== + // + // old customizable attack functions which use actor parameters. + // + //========================================================================== + + private void DoAttack (bool domelee, bool domissile, int MeleeDamage, Sound MeleeSound, Class MissileType,double MissileHeight) + { + if (target == NULL) return; + + A_FaceTarget (); + if (domelee && MeleeDamage>0 && CheckMeleeRange ()) + { + int damage = random[CustomMelee](1, 8) * MeleeDamage; + if (MeleeSound) A_PlaySound (MeleeSound, CHAN_WEAPON); + int newdam = target.DamageMobj (self, self, damage, 'Melee'); + target.TraceBleed (newdam > 0 ? newdam : damage, self); + } + else if (domissile && MissileType != NULL) + { + // This seemingly senseless code is needed for proper aiming. + double add = MissileHeight + GetBobOffset() - 32; + AddZ(add); + Actor missile = SpawnMissileXYZ (Pos + (0, 0, 32), target, MissileType, false); + AddZ(-add); + + if (missile) + { + // automatic handling of seeker missiles + if (missile.bSeekerMissile) + { + missile.tracer = target; + } + missile.CheckMissileSpawn(radius); + } + } + } + + deprecated void A_MeleeAttack() + { + DoAttack(true, false, MeleeDamage, MeleeSound, NULL, 0); + } + + deprecated void A_MissileAttack() + { + Class MissileType = MissileName; + DoAttack(false, true, 0, 0, MissileType, MissileHeight); + } + + deprecated void A_ComboAttack() + { + Class MissileType = MissileName; + DoAttack(true, true, MeleeDamage, MeleeSound, MissileType, MissileHeight); + } + + void A_BasicAttack(int melee_damage, sound melee_sound, class missile_type, double missile_height) + { + DoAttack(true, true, melee_damage, melee_sound, missile_type, missile_height); + } + native void A_MonsterRail(); native void A_Pain(); @@ -737,7 +901,11 @@ class Actor : Thinker native native void A_VileChase(); native void A_BossDeath(); native void A_Detonate(); - native bool A_CallSpecial(int special, int arg1=0, int arg2=0, int arg3=0, int arg4=0, int arg5=0); + bool A_CallSpecial(int special, int arg1=0, int arg2=0, int arg3=0, int arg4=0, int arg5=0) + { + return Level.ExecuteSpecial(special, self, null, false, arg1, arg2, arg3, arg4, arg5); + } + native void A_ActiveSound(); @@ -750,9 +918,6 @@ class Actor : Thinker native native void A_Wander(int flags = 0); native void A_Look2(); - deprecated native void A_MissileAttack(); - deprecated native void A_MeleeAttack(); - deprecated native void A_ComboAttack(); deprecated native void A_BulletAttack(); native void A_WolfAttack(int flags = 0, sound whattoplay = "weapons/pistol", double snipe = 1.0, int maxdamage = 64, int blocksize = 128, int pointblank = 2, int longrange = 4, double runspeed = 160.0, class pufftype = "BulletPuff"); native void A_PlaySound(sound whattoplay = "weapons/pistol", int slot = CHAN_BODY, double volume = 1.0, bool looping = false, double attenuation = ATTN_NORM, bool local = false); @@ -783,16 +948,16 @@ class Actor : Thinker native native void A_SpawnDebris(class spawntype, bool transfer_translation = false, double mult_h = 1, double mult_v = 1); native void A_SpawnParticle(color color1, int flags = 0, int lifetime = 35, double size = 1, double angle = 0, double xoff = 0, double yoff = 0, double zoff = 0, double velx = 0, double vely = 0, double velz = 0, double accelx = 0, double accely = 0, double accelz = 0, double startalphaf = 1, double fadestepf = -1, double sizestep = 0); native void A_ExtChase(bool usemelee, bool usemissile, bool playactive = true, bool nightmarefast = false); - native void A_DropInventory(class itemtype); + native void A_DropInventory(class itemtype, int amount = -1); native void A_SetBlend(color color1, double alpha, int tics, color color2 = 0); deprecated native void A_ChangeFlag(string flagname, bool value); native void A_ChangeCountFlags(int kill = FLAG_NO_CHANGE, int item = FLAG_NO_CHANGE, int secret = FLAG_NO_CHANGE); - native void A_RaiseMaster(bool copy = 0); - native void A_RaiseChildren(bool copy = 0); - native void A_RaiseSiblings(bool copy = 0); - deprecated native void A_BasicAttack(int meleedamage, sound meleesound, class missiletype, double missileheight); + native void A_RaiseMaster(int flags = 0); + native void A_RaiseChildren(int flags = 0); + native void A_RaiseSiblings(int flags = 0); action native bool, Actor A_ThrowGrenade(class itemtype, double zheight = 0, double xyvel = 0, double zvel = 0, bool useammo = true); native void A_Weave(int xspeed, int yspeed, double xdist, double ydist); + native bool A_Morph(class type, int duration = 0, int flags = 0, class enter_flash = null, class exit_flash = null); action native state, bool A_Teleport(statelabel teleportstate = null, class targettype = "BossSpot", class fogtype = "TeleportFog", int flags = 0, double mindist = 128, double maxdist = 0, int ptr = AAPTR_DEFAULT); @@ -865,6 +1030,8 @@ class Actor : Thinker native native bool A_SetVisibleRotation(double anglestart = 0, double angleend = 0, double pitchstart = 0, double pitchend = 0, int flags = 0, int ptr = AAPTR_DEFAULT); native void A_SetTranslation(name transname); native bool A_SetSize(double newradius, double newheight = -1, bool testpos = false); + native void A_SprayDecal(String name); + native void A_SetMugshotState(String name); native void A_RearrangePointers(int newtarget, int newmaster = AAPTR_DEFAULT, int newtracer = AAPTR_DEFAULT, int flags=0); native void A_TransferPointer(int ptr_source, int ptr_recepient, int sourcefield, int recepientfield=AAPTR_DEFAULT, int flags=0); diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 14aec9b1d7..2c1fb6661d 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -158,15 +158,11 @@ enum DrawTextureTags struct Screen native { - int CleanWidth, CleanHeight; - int CleanXFac, CleanYFac; - int CleanWidth_1, CleanHeight_1; - int CleanXFac_1, CleanYFac_1; - native static Color PaletteColor(int index); native static int GetWidth(); native static int GetHeight(); native static void Clear(int left, int top, int right, int bottom, Color color, int palcolor = -1); + native static void Dim(Color col, double amount, int x, int y, int w, int h); native static void DrawHUDTexture(TextureID tex, double x, double y); native static vararg void DrawTexture(TextureID tex, bool animate, double x, double y, ...); @@ -175,13 +171,6 @@ struct Screen native native static void DrawFrame(int x, int y, int w, int h); } -class BrokenLines : Object native -{ - native int Count(); - native int StringWidth(int line); - native String StringAt(int line); -} - struct Font native { enum EColorRange @@ -252,7 +241,7 @@ struct Font native native static int FindFontColor(Name color); native static Font FindFont(Name fontname); native static Font GetFont(Name fontname); - native static BrokenLines BreakLines(String text, int maxlen); + native BrokenLines BreakLines(String text, int maxlen); } struct Translation @@ -301,6 +290,12 @@ struct CVar native native int ResetToDefault(); } +struct GIFont +{ + Name fontname; + Name color; +}; + struct GameInfoStruct native { // will be extended as needed. @@ -309,6 +304,13 @@ struct GameInfoStruct native native String ArmorIcon1; native String ArmorIcon2; native int gametype; + native bool norandomplayerclass; + native Array infoPages; + native String mBackButton; + native GIFont mStatscreenMapNameFont; + native GIFont mStatscreenEnteringFont; + native GIFont mStatscreenFinishedFont; + native double gibfactor; } class Object native @@ -335,7 +337,14 @@ class Object native virtual virtualscope void OnDestroy() {} } -class Thinker : Object play native +class BrokenLines : Object native +{ + native int Count(); + native int StringWidth(int line); + native String StringAt(int line); +} + +class Thinker : Object native play { enum EStatnums { @@ -345,6 +354,7 @@ class Thinker : Object play native STAT_AUTODECAL, // A decal that can be automatically deleted STAT_CORPSEPOINTER, // An entry in Hexen's corpse queue STAT_TRAVELLING, // An actor temporarily travelling to a new map + STAT_STATIC, // Thinkers that do think STAT_FIRST_THINKING=32, @@ -439,6 +449,7 @@ struct LevelLocals native native readonly String MapName; native String NextMap; native String NextSecretMap; + native String F1Pic; native readonly int maptype; native readonly String Music; native readonly int musicorder; @@ -464,11 +475,13 @@ struct LevelLocals native native bool nomonsters; native bool frozen; native bool infinite_flight; + native bool no_dlg_freeze; // level_info_t *info cannot be done yet. native String GetUDMFString(int type, int index, Name key); native int GetUDMFInt(int type, int index, Name key); native double GetUDMFFloat(int type, int index, Name key); + native bool ExecuteSpecial(int special, Actor activator, line linedef, bool lineside, int arg1 = 0, int arg2 = 0, int arg3 = 0, int arg4 = 0, int arg5 = 0); } @@ -579,10 +592,12 @@ struct StringStruct native native vararg void AppendFormat(String fmt, ...); native void Replace(String pattern, String replacement); - native String Mid(int pos = 0, int len = 2147483647) const; - native int Len() const; - native String CharAt(int pos) const; - native int CharCodeAt(int pos) const; + native String Left(int len); + native String Mid(int pos = 0, int len = 2147483647); + native void Truncate(int newlen); + native String CharAt(int pos); + native int CharCodeAt(int pos); + native String Filter(); } class Floor : Thinker native diff --git a/wadsrc/static/zscript/constants.txt b/wadsrc/static/zscript/constants.txt index 6996da6cc8..1982c44727 100644 --- a/wadsrc/static/zscript/constants.txt +++ b/wadsrc/static/zscript/constants.txt @@ -214,20 +214,23 @@ enum ESelectWeaponFlags // Morph constants enum EMorphFlags { - MRF_ADDSTAMINA = 1, - MRF_FULLHEALTH = 2, - MRF_UNDOBYTOMEOFPOWER = 4, - MRF_UNDOBYCHAOSDEVICE = 8, - MRF_FAILNOTELEFRAG = 16, - MRF_FAILNOLAUGH = 32, - MRF_WHENINVULNERABLE = 64, - MRF_LOSEACTUALWEAPON = 128, - MRF_NEWTIDBEHAVIOUR = 256, - MRF_UNDOBYDEATH = 512, - MRF_UNDOBYDEATHFORCED = 1024, - MRF_UNDOBYDEATHSAVES = 2048, - MRF_UNDOALWAYS = 4096, - MRF_TRANSFERTRANSLATION = 8192, + MRF_OLDEFFECTS = 0x00000000, + MRF_ADDSTAMINA = 0x00000001, + MRF_FULLHEALTH = 0x00000002, + MRF_UNDOBYTOMEOFPOWER = 0x00000004, + MRF_UNDOBYCHAOSDEVICE = 0x00000008, + MRF_FAILNOTELEFRAG = 0x00000010, + MRF_FAILNOLAUGH = 0x00000020, + MRF_WHENINVULNERABLE = 0x00000040, + MRF_LOSEACTUALWEAPON = 0x00000080, + MRF_NEWTIDBEHAVIOUR = 0x00000100, + MRF_UNDOBYDEATH = 0x00000200, + MRF_UNDOBYDEATHFORCED = 0x00000400, + MRF_UNDOBYDEATHSAVES = 0x00000800, + MRF_UNDOBYTIMEOUT = 0x00001000, + MRF_UNDOALWAYS = 0x00002000, + MRF_TRANSFERTRANSLATION = 0x00004000, + MRF_STANDARDUNDOING = MRF_UNDOBYTOMEOFPOWER | MRF_UNDOBYCHAOSDEVICE | MRF_UNDOBYTIMEOUT, }; // Flags for A_RailAttack and A_CustomRailgun @@ -996,6 +999,19 @@ enum EFindFloorCeiling FFCF_NODROPOFF = 256, // Caller does not need a dropoff (saves some time when checking portals) }; +enum ERaise +{ + RF_TRANSFERFRIENDLINESS = 1, + RF_NOCHECKPOSITION = 2 +} + +enum eFogParm +{ + FOGP_DENSITY = 0, + FOGP_OUTSIDEDENSITY = 1, + FOGP_SKYFOG = 2, +} + enum ETeleport { TELF_DESTFOG = 1, @@ -1029,6 +1045,18 @@ enum PaletteFlashFlags PF_HAZARD = 8, }; +enum EGameState +{ + GS_LEVEL, + GS_INTERMISSION, + GS_FINALE, + GS_DEMOSCREEN, + GS_FULLCONSOLE, + GS_HIDECONSOLE, + GS_STARTUP, + GS_TITLELEVEL, +} + enum EGameAction { ga_nothing, @@ -1076,17 +1104,21 @@ enum EPlayerCheats 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 CF_NOCLIP2 = 1 << 30, // [RH] More Quake-like noclip + + // These flags no longer exist, but keep the names for some stray mod that might have used them. + CF_DRAIN = 0, + CF_HIGHJUMP = 0, + CF_REFLECTION = 0, + CF_PROSPERITY = 0, + CF_DOUBLEFIRINGSPEED= 0, + CF_INFINITEAMMO = 0, }; const TEXTCOLOR_BRICK = "\034A"; diff --git a/wadsrc/static/zscript/doom/doomammo.txt b/wadsrc/static/zscript/doom/doomammo.txt index 394f13e94f..950fd2122a 100644 --- a/wadsrc/static/zscript/doom/doomammo.txt +++ b/wadsrc/static/zscript/doom/doomammo.txt @@ -10,6 +10,7 @@ class Clip : Ammo Ammo.BackpackAmount 10; Ammo.BackpackMaxAmount 400; Inventory.Icon "CLIPA0"; + Tag "$AMMO_CLIP"; } States { @@ -48,6 +49,7 @@ class RocketAmmo : Ammo Ammo.BackpackAmount 1; Ammo.BackpackMaxAmount 100; Inventory.Icon "ROCKA0"; + Tag "$AMMO_ROCKETS"; } States { @@ -86,6 +88,7 @@ class Cell : Ammo Ammo.BackpackAmount 20; Ammo.BackpackMaxAmount 600; Inventory.Icon "CELLA0"; + Tag "$AMMO_CELLS"; } States { @@ -124,6 +127,7 @@ class Shell : Ammo Ammo.BackpackAmount 4; Ammo.BackpackMaxAmount 100; Inventory.Icon "SHELA0"; + Tag "$AMMO_SHELLS"; } States { diff --git a/wadsrc/static/zscript/doom/doomartifacts.txt b/wadsrc/static/zscript/doom/doomartifacts.txt index ad5e304c66..a6996cb886 100644 --- a/wadsrc/static/zscript/doom/doomartifacts.txt +++ b/wadsrc/static/zscript/doom/doomartifacts.txt @@ -72,6 +72,8 @@ class Megasphere : CustomInventory { +COUNTITEM +INVENTORY.ALWAYSPICKUP + +INVENTORY.ISHEALTH + +INVENTORY.ISARMOR Inventory.PickupMessage "$GOTMSPHERE"; Inventory.PickupSound "misc/p_pkup"; } @@ -183,6 +185,7 @@ class Berserk : CustomInventory { +COUNTITEM +INVENTORY.ALWAYSPICKUP + +INVENTORY.ISHEALTH Inventory.PickupMessage "$GOTBERSERK"; Inventory.PickupSound "misc/p_pkup"; } diff --git a/wadsrc/static/zscript/heretic/hereticammo.txt b/wadsrc/static/zscript/heretic/hereticammo.txt index cf1e50a3d7..c5ea831aaf 100644 --- a/wadsrc/static/zscript/heretic/hereticammo.txt +++ b/wadsrc/static/zscript/heretic/hereticammo.txt @@ -11,6 +11,7 @@ Class GoldWandAmmo : Ammo Ammo.BackpackAmount 10; Ammo.BackpackMaxAmount 200; Inventory.Icon "INAMGLD"; + Tag "$AMMO_GOLDWAND"; } States { @@ -48,6 +49,7 @@ Class CrossbowAmmo : Ammo Ammo.BackpackAmount 5; Ammo.BackpackMaxAmount 100; Inventory.Icon "INAMBOW"; + Tag "$AMMO_CROSSBOW"; } States { @@ -85,6 +87,7 @@ Class MaceAmmo : Ammo Ammo.BackpackAmount 20; Ammo.BackpackMaxAmount 300; Inventory.Icon "INAMLOB"; + Tag "$AMMO_MACE"; } States { @@ -123,6 +126,7 @@ Class BlasterAmmo : Ammo Ammo.BackpackAmount 10; Ammo.BackpackMaxAmount 400; Inventory.Icon "INAMBST"; + Tag "$AMMO_BLASTER"; } States { @@ -161,6 +165,7 @@ Class SkullRodAmmo : Ammo Ammo.BackpackAmount 20; Ammo.BackpackMaxAmount 400; Inventory.Icon "INAMRAM"; + Tag "$AMMO_SKULLROD"; } States { @@ -199,6 +204,7 @@ Class PhoenixRodAmmo : Ammo Ammo.BackpackAmount 1; Ammo.BackpackMaxAmount 40; Inventory.Icon "INAMPNX"; + Tag "$AMMO_PHOENIXROD"; } States { diff --git a/wadsrc/static/zscript/hexen/flechette.txt b/wadsrc/static/zscript/hexen/flechette.txt index 3440312c9e..1708b843b5 100644 --- a/wadsrc/static/zscript/hexen/flechette.txt +++ b/wadsrc/static/zscript/hexen/flechette.txt @@ -437,7 +437,7 @@ class PoisonCloud : Actor Mass 0x7fffffff; +NOBLOCKMAP +NOGRAVITY +DROPOFF +NODAMAGETHRUST - +DONTSPLASH +FOILINVUL +CANBLAST +BLOODLESSIMPACT +BLOCKEDBYSOLIDACTORS + +DONTSPLASH +FOILINVUL +CANBLAST +BLOODLESSIMPACT +BLOCKEDBYSOLIDACTORS +FORCEZERORADIUSDMG RenderStyle "Translucent"; Alpha 0.6; DeathSound "PoisonShroomDeath"; diff --git a/wadsrc/static/zscript/hexen/mana.txt b/wadsrc/static/zscript/hexen/mana.txt index 0a43a21ddd..ad3d6d66be 100644 --- a/wadsrc/static/zscript/hexen/mana.txt +++ b/wadsrc/static/zscript/hexen/mana.txt @@ -13,6 +13,7 @@ class Mana1 : Ammo +FLOATBOB Inventory.Icon "MAN1I0"; Inventory.PickupMessage "$TXT_MANA_1"; + Tag "$AMMO_MANA1"; } States { @@ -37,6 +38,7 @@ class Mana2 : Ammo +FLOATBOB Inventory.Icon "MAN2G0"; Inventory.PickupMessage "$TXT_MANA_2"; + Tag "$AMMO_MANA2"; } States { diff --git a/wadsrc/static/zscript/inventory/ammo.txt b/wadsrc/static/zscript/inventory/ammo.txt index ba087a8942..092a93846b 100644 --- a/wadsrc/static/zscript/inventory/ammo.txt +++ b/wadsrc/static/zscript/inventory/ammo.txt @@ -37,7 +37,7 @@ class Ammo : Inventory { int BackpackAmount; int BackpackMaxAmount; - /*meta*/ int DropAmount; + meta int DropAmount; property BackpackAmount: BackpackAmount; property BackpackMaxAmount: BackpackMaxAmount; @@ -173,9 +173,9 @@ class Ammo : Inventory // //=========================================================================== - override Inventory CreateTossable() + override Inventory CreateTossable(int amt) { - Inventory copy = Super.CreateTossable(); + Inventory copy = Super.CreateTossable(amt); if (copy != null) { // Do not increase ammo by dropping it and picking it back up at // certain skill levels. @@ -306,9 +306,9 @@ class BackpackItem : Inventory // //=========================================================================== - override Inventory CreateTossable () + override Inventory CreateTossable (int amount) { - let pack = BackpackItem(Super.CreateTossable()); + let pack = BackpackItem(Super.CreateTossable(-1)); if (pack != NULL) { pack.bDepleted = true; diff --git a/wadsrc/static/zscript/inventory/armor.txt b/wadsrc/static/zscript/inventory/armor.txt index 50161e8af2..600c4e5265 100644 --- a/wadsrc/static/zscript/inventory/armor.txt +++ b/wadsrc/static/zscript/inventory/armor.txt @@ -38,6 +38,7 @@ class Armor : Inventory Default { Inventory.PickupSound "misc/armor_pkup"; + +INVENTORY.ISARMOR } } @@ -489,7 +490,7 @@ class HexenArmor : Armor // //=========================================================================== - override Inventory CreateTossable () + override Inventory CreateTossable (int amount) { return NULL; } diff --git a/wadsrc/static/zscript/inventory/health.txt b/wadsrc/static/zscript/inventory/health.txt index 88bf6b133c..e69d85814d 100644 --- a/wadsrc/static/zscript/inventory/health.txt +++ b/wadsrc/static/zscript/inventory/health.txt @@ -36,13 +36,14 @@ class Health : Inventory { transient int PrevHealth; - /*meta*/ int LowHealth; - /*meta*/ String LowHealthMessage; + meta int LowHealth; + meta String LowHealthMessage; property LowMessage: LowHealth, LowHealthMessage; Default { + +INVENTORY.ISHEALTH Inventory.Amount 1; Inventory.MaxAmount 0; Inventory.PickupSound "misc/health_pkup"; @@ -85,8 +86,36 @@ class Health : Inventory } return false; } +} - +class MaxHealth : Health +{ + //=========================================================================== + // + // TryPickup + // + //=========================================================================== + + override bool TryPickup (in out Actor other) + { + bool success = false; + int savedAmount = MaxAmount; + let player = PlayerPawn(other); + MaxAmount = Health; + if (player) + { + if (player.BonusHealth < savedAmount) + { + player.BonusHealth = min(player.BonusHealth + Amount, savedAmount); + success = true; + } + MaxAmount += player.BonusHealth; + } + success |= Super.TryPickup(other); + MaxAmount = savedAmount; + if (success) GoAwayAndDie(); + return success; + } } class HealthPickup : Inventory @@ -99,6 +128,7 @@ class HealthPickup : Inventory { Inventory.DefMaxAmount; +INVENTORY.INVBAR + +INVENTORY.ISHEALTH } //=========================================================================== @@ -120,9 +150,9 @@ class HealthPickup : Inventory // //=========================================================================== - override Inventory CreateTossable () + override Inventory CreateTossable (int amount) { - Inventory copy = Super.CreateTossable (); + Inventory copy = Super.CreateTossable (-1); if (copy != NULL) { copy.health = health; diff --git a/wadsrc/static/zscript/inventory/inv_misc.txt b/wadsrc/static/zscript/inventory/inv_misc.txt index 441566786c..ce4bf805b4 100644 --- a/wadsrc/static/zscript/inventory/inv_misc.txt +++ b/wadsrc/static/zscript/inventory/inv_misc.txt @@ -87,8 +87,8 @@ class MapRevealer : Inventory class PuzzleItem : Inventory { - /*meta*/ int PuzzleItemNumber; - /*meta*/ String PuzzFailMessage; + meta int PuzzleItemNumber; + meta String PuzzFailMessage; property Number: PuzzleItemNumber; property FailMessage: PuzzFailMessage; diff --git a/wadsrc/static/zscript/inventory/inventory.txt b/wadsrc/static/zscript/inventory/inventory.txt index 8b142c9a5b..16a83c7a50 100644 --- a/wadsrc/static/zscript/inventory/inventory.txt +++ b/wadsrc/static/zscript/inventory/inventory.txt @@ -23,8 +23,11 @@ class Inventory : Actor native native bool bPickupGood; native bool bCreateCopyMoved; native bool bInitEffectFailed; - native meta String PickupMsg; - native /*meta*/ int GiveQuest; + meta String PickupMsg; + meta int GiveQuest; + + Property PickupMessage: PickupMsg; + Property GiveQuest: GiveQuest; Default { @@ -440,7 +443,7 @@ class Inventory : Actor native // //=========================================================================== - virtual Inventory CreateTossable () + virtual Inventory CreateTossable (int amt = -1) { // If self actor lacks a SpawnState, don't drop it. (e.g. A base weapon // like the fist can't be dropped because you'll never see it.) @@ -448,7 +451,7 @@ class Inventory : Actor native { return NULL; } - if (bUndroppable || bUntossable || Owner == NULL || Amount <= 0) + if (bUndroppable || bUntossable || Owner == NULL || Amount <= 0 || amt == 0) { return NULL; } @@ -462,11 +465,13 @@ class Inventory : Actor native let copy = Inventory(Spawn (GetClass(), Owner.Pos, NO_REPLACE)); if (copy != NULL) { + amt = clamp(amt, 1, Amount); + copy.MaxAmount = MaxAmount; - copy.Amount = 1; + copy.Amount = amt; copy.DropTime = 30; copy.bSpecial = copy.bSolid = false; - Amount--; + Amount -= amt; } return copy; } diff --git a/wadsrc/static/zscript/inventory/powerups.txt b/wadsrc/static/zscript/inventory/powerups.txt index 5885100e61..d4d78d1192 100644 --- a/wadsrc/static/zscript/inventory/powerups.txt +++ b/wadsrc/static/zscript/inventory/powerups.txt @@ -174,7 +174,7 @@ class Powerup : Inventory // //=========================================================================== - override Inventory CreateTossable () + override Inventory CreateTossable (int amount) { return NULL; } @@ -1767,7 +1767,7 @@ class PowerProtection : Powerup { if (passive && damage > 0) { - newdamage = max(1, ApplyDamageFactors(GetClass(), damageType, damage, damage / 4)); + newdamage = max(0, ApplyDamageFactors(GetClass(), damageType, damage, damage / 4)); if (Owner != null && newdamage < damage) Owner.A_PlaySound(ActiveSound, CHAN_AUTO, 1.0, false, ATTN_NONE); } } @@ -1783,32 +1783,9 @@ class PowerDrain : Powerup { Default { + Powerup.Strength 0.5; Powerup.Duration -60; } - - override void InitEffect() - { - Super.InitEffect(); - - if (Owner!= null && Owner.player != null) - { - // Give the player the power to drain life from opponents when he damages them. - Owner.player.cheats |= CF_DRAIN; - } - } - - override void EndEffect() - { - Super.EndEffect(); - - // Nothing to do if there's no owner. - if (Owner!= null && Owner.player != null) - { - // Take away the drain power. - Owner.player.cheats &= ~CF_DRAIN; - } - } - } //=========================================================================== @@ -1846,27 +1823,9 @@ class PowerRegeneration : Powerup class PowerHighJump : Powerup { - override void InitEffect() + Default { - Super.InitEffect(); - - if (Owner!= null && Owner.player != null) - { - // Give the player the power to jump much higher. - Owner.player.cheats |= CF_HIGHJUMP; - } - } - - override void EndEffect() - { - Super.EndEffect(); - - // Nothing to do if there's no owner. - if (Owner!= null && Owner.player != null) - { - // Take away the high jump power. - Owner.player.cheats &= ~CF_HIGHJUMP; - } + Powerup.Strength 2; } } @@ -1878,27 +1837,9 @@ class PowerHighJump : Powerup class PowerDoubleFiringSpeed : Powerup { - override void InitEffect() + Default { - Super.InitEffect(); - - if (Owner!= null && Owner.player != null) - { - // Give the player the power to shoot twice as fast. - Owner.player.cheats |= CF_DOUBLEFIRINGSPEED; - } - } - - override void EndEffect() - { - Super.EndEffect(); - - // Nothing to do if there's no owner. - if (Owner!= null && Owner.player != null) - { - // Take away the shooting twice as fast power. - Owner.player.cheats &= ~CF_DOUBLEFIRINGSPEED; - } + Powerup.Duration -40; } } @@ -1914,28 +1855,21 @@ class PowerInfiniteAmmo : Powerup { Powerup.Duration -30; } - - override void InitEffect() +} + + +//=========================================================================== +// +// InfiniteAmmo +// +//=========================================================================== + +class PowerReflection : Powerup +{ + Default { - Super.InitEffect(); - - if (Owner!= null && Owner.player != null) - { - // Give the player infinite ammo - Owner.player.cheats |= CF_INFINITEAMMO; - } - } - - override void EndEffect() - { - Super.EndEffect(); - - // Nothing to do if there's no owner. - if (Owner!= null && Owner.player != null) - { - // Take away the limitless ammo - Owner.player.cheats &= ~CF_INFINITEAMMO; - } + Powerup.Duration -60; + DamageFactor 0.5; } } diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index c1fbf43846..860d387e27 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -33,7 +33,7 @@ class Weapon : StateProvider native native float FOVScale; native int Crosshair; // 0 to use player's crosshair native bool GivenAsMorphWeapon; - native bool bAltFire; // Set when self weapon's alternate fire is used. + native bool bAltFire; // Set when this weapon's alternate fire is used. native readonly bool bDehAmmo; Default @@ -89,6 +89,12 @@ class Weapon : StateProvider native return s; } + override String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack) + { + // Weapons may never return HitObituary by default. Override this if it is needed. + return Obituary; + } + action void A_GunFlash(statelabel flashlabel = null, int flags = 0) { let player = player; @@ -435,7 +441,7 @@ class Weapon : StateProvider native // //=========================================================================== - override Inventory CreateTossable () + override Inventory CreateTossable (int amt) { // Only drop the weapon that is meant to be placed in a level. That is, // only drop the weapon that normally gives you ammo. @@ -443,9 +449,9 @@ class Weapon : StateProvider native Default.AmmoGive1 == 0 && Default.AmmoGive2 == 0 && (SisterWeapon.Default.AmmoGive1 > 0 || SisterWeapon.Default.AmmoGive2 > 0)) { - return SisterWeapon.CreateTossable (); + return SisterWeapon.CreateTossable (amt); } - let copy = Weapon(Super.CreateTossable ()); + let copy = Weapon(Super.CreateTossable (-1)); if (copy != NULL) { diff --git a/wadsrc/static/zscript/menu/conversationmenu.txt b/wadsrc/static/zscript/menu/conversationmenu.txt new file mode 100644 index 0000000000..dba162ad7c --- /dev/null +++ b/wadsrc/static/zscript/menu/conversationmenu.txt @@ -0,0 +1,516 @@ +/* +** conversationmenu.txt +** The Strife dialogue display +** +**--------------------------------------------------------------------------- +** Copyright 2010-2017 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. +**--------------------------------------------------------------------------- +** +*/ + +struct StrifeDialogueNode native +{ + native Class DropType; + native int ThisNodeNum; + native int ItemCheckNode; + + native Class SpeakerType; + native String SpeakerName; + native Sound SpeakerVoice; + native String Backdrop; + native String Dialogue; + native String Goodbye; + + native StrifeDialogueReply Children; +} + +// FStrifeDialogueReply holds responses the player can give to the NPC +struct StrifeDialogueReply native +{ + native StrifeDialogueReply Next; + native Class GiveType; + native int ActionSpecial; + native int Args[5]; + native int PrintAmount; + native String Reply; + native String QuickYes; + native String QuickNo; + native String LogString; + native int NextNode; // index into StrifeDialogues + native int LogNumber; + native bool NeedsGold; + + native bool ShouldSkipReply(PlayerInfo player); +} + + +class ConversationMenu : Menu +{ + String mSpeaker; + BrokenLines mDialogueLines; + Array mResponseLines; + Array mResponses; + bool mShowGold; + StrifeDialogueNode mCurNode; + int mYpos; + PlayerInfo mPlayer; + int mSelection; + int ConversationPauseTic; + + int SpeechWidth; + int ReplyWidth; + + native static void SendConversationReply(int node, int reply); + + const NUM_RANDOM_LINES = 10; + const NUM_RANDOM_GOODBYES = 3; + + //============================================================================= + // + // returns the y position of the replies boy for positioning the terminal response. + // + //============================================================================= + + virtual int Init(StrifeDialogueNode CurNode, PlayerInfo player, int activereply) + { + mCurNode = CurNode; + mPlayer = player; + mShowGold = false; + ConversationPauseTic = gametic + 20; + DontDim = true; + + ReplyWidth = 320-50-10; + SpeechWidth = screen.GetWidth()/CleanXfac - 24*2; + + FormatSpeakerMessage(); + return FormatReplies(activereply); + } + + //============================================================================= + // + // + // + //============================================================================= + + virtual int FormatReplies(int activereply) + { + mSelection = -1; + + StrifeDialogueReply reply; + int r = -1; + int i = 1,j; + for (reply = mCurNode.Children; reply != NULL; reply = reply.Next) + { + r++; + if (reply.ShouldSkipReply(mPlayer)) + { + continue; + } + if (activereply == r) mSelection = i - 1; + + mShowGold |= reply.NeedsGold; + + let ReplyText = Stringtable.Localize(reply.Reply); + if (reply.NeedsGold) ReplyText.AppendFormat(" for %u", reply.PrintAmount); + + let ReplyLines = SmallFont.BreakLines (ReplyText, ReplyWidth); + + mResponses.Push(mResponseLines.Size()); + for (j = 0; j < ReplyLines.Count(); ++j) + { + mResponseLines.Push(ReplyLines.StringAt(j)); + } + + ++i; + ReplyLines.Destroy(); + } + if (mSelection == -1) + { + mSelection = r < activereply ? r + 1 : 0; + } + let goodbyestr = mCurNode.Goodbye; + if (goodbyestr.Length() == 0) + { + goodbyestr = String.Format("$TXT_RANDOMGOODBYE_%d", Random[RandomSpeech](1, NUM_RANDOM_GOODBYES)); + } + else if (goodbyestr.Left(7) == "RANDOM_") + { + goodbyestr = String.Format("$TXT_%s_%02d", goodbyestr, Random[RandomSpeech](1, NUM_RANDOM_LINES)); + } + goodbyestr = Stringtable.Localize(goodbyestr); + if (goodbyestr.Length() == 0 || goodbyestr.CharAt(0) == "$") goodbyestr = "Bye."; + mResponses.Push(mResponseLines.Size()); + mResponseLines.Push(goodbyestr); + + // Determine where the top of the reply list should be positioned. + mYpos = MIN (140, 192 - mResponseLines.Size() * OptionMenuSettings.mLinespacing); + i = 44 + mResponseLines.Size() * OptionMenuSettings.mLinespacing; + if (mYpos - 100 < i - screen.GetHeight() / CleanYfac / 2) + { + mYpos = i - screen.GetHeight() / CleanYfac / 2 + 100; + } + + if (mSelection >= mResponses.Size()) + { + mSelection = mResponses.Size() - 1; + } + return mYpos; + } + + //============================================================================= + // + // + // + //============================================================================= + + virtual void FormatSpeakerMessage() + { + // Format the speaker's message. + String toSay = mCurNode.Dialogue; + if (toSay.Left(7) == "RANDOM_") + { + let dlgtext = String.Format("$TXT_%s_%02d", toSay, random[RandomSpeech](1, NUM_RANDOM_LINES)); + toSay = Stringtable.Localize(dlgtext); + if (toSay.CharAt(0) == "$") toSay = Stringtable.Localize("$TXT_GOAWAY"); + } + else + { + // handle string table replacement + toSay = Stringtable.Localize(toSay); + } + if (toSay.Length() == 0) + { + toSay = "."; + } + mDialogueLines = SmallFont.BreakLines(toSay, SpeechWidth); + } + + //============================================================================= + // + // + // + //============================================================================= + + override void OnDestroy() + { + mDialogueLines.Destroy(); + SetMusicVolume (1); + Super.OnDestroy(); + } + + protected int GetReplyNum() + { + // 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] + let reply = mCurNode.Children; + int replynum = mSelection; + for (int i = 0; i <= mSelection && reply != null; reply = reply.Next) + { + if (reply.ShouldSkipReply(mPlayer)) + replynum++; + else + i++; + } + return replynum; + } + + //============================================================================= + // + // + // + //============================================================================= + + override 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 >= mResponses.Size()) mSelection = 0; + return true; + } + else if (mkey == MKEY_Back) + { + SendConversationReply(-1, GetReplyNum()); + Close(); + return true; + } + else if (mkey == MKEY_Enter) + { + int replynum = GetReplyNum(); + if (mSelection >= mResponses.Size()) + { + SendConversationReply(-2, replynum); + } + else + { + // Send dialogue and reply numbers across the wire. + SendConversationReply(mCurNode.ThisNodeNum, replynum); + } + Close(); + return true; + } + return false; + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MouseEvent(int type, int x, int y) + { + int sel = -1; + int fh = OptionMenuSettings.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 * mResponseLines.Size()) + { + sel = (y - mYpos) / fh; + for(int i = 0; i < mResponses.Size(); i++) + { + if (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; + } + + + //============================================================================= + // + // + // + //============================================================================= + + override bool Responder(InputEventData ev) + { + if (demoplayback) + { // No interaction during demo playback + return false; + } + if (ev.type == InputEventData.GUI_Event && ev.subtype == InputEventData.GUI_Char && ev.data1 >= 48 && ev.data1 <= 57) + { // Activate an item of type numberedmore (dialogue only) + mSelection = ev.data1 == 48 ? 9 : ev.data1 - 49; + return MenuEvent(MKEY_Enter, false); + } + return Super.Responder(ev); + } + + //============================================================================ + // + // Draw the backdrop, returns true if the text background should be dimmed + // + //============================================================================ + + virtual bool DrawBackdrop() + { + let tex = TexMan.CheckForTexture (mCurNode.Backdrop, TexMan.Type_MiscPatch); + if (tex.isValid()) + { + screen.DrawTexture(tex, false, 0, 0, DTA_320x200, true); + return false; + } + return true; + } + + //============================================================================ + // + // Draw the speaker text + // + //============================================================================ + + virtual void DrawSpeakerText(bool dimbg) + { + String speakerName; + int linesize = OptionMenuSettings.mLinespacing * CleanYfac; + int cnt = mDialogueLines.Count(); + + // Who is talking to you? + if (mCurNode.SpeakerName.Length() > 0) + { + speakerName = Stringtable.Localize(mCurNode.SpeakerName); + } + else + { + speakerName = players[consoleplayer].ConversationNPC.GetTag("Person"); + } + + + // Dim the screen behind the dialogue (but only if there is no backdrop). + if (dimbg) + { + int x = 14 * screen.GetWidth() / 320; + int y = 13 * screen.GetHeight() / 200; + int w = 294 * screen.GetWidth() / 320; + int h = linesize * cnt + 6 * CleanYfac; + if (speakerName.Length() > 0) h += linesize * 3 / 2; + screen.Dim(0, 0.45f, x, y, w, h); + } + + int x = 16 * screen.GetWidth() / 320; + int y = 16 * screen.GetHeight() / 200; + + if (speakerName.Length() > 0) + { + screen.DrawText(SmallFont, Font.CR_WHITE, x, y, speakerName, DTA_CleanNoMove, true); + y += linesize * 3 / 2; + } + x = 24 * screen.GetWidth() / 320; + for (int i = 0; i < cnt; ++i) + { + screen.DrawText(SmallFont, Font.CR_UNTRANSLATED, x, y, mDialogueLines.StringAt(i), DTA_CleanNoMove, true); + y += linesize; + } + } + + + //============================================================================ + // + // Draw the replies + // + //============================================================================ + + virtual void DrawReplies() + { + // Dim the screen behind the PC's choices. + screen.Dim(0, 0.45, (24 - 160) * CleanXfac + screen.GetWidth() / 2, (mYpos - 2 - 100) * CleanYfac + screen.GetHeight() / 2, + 272 * CleanXfac, MIN(mResponseLines.Size() * OptionMenuSettings.mLinespacing + 4, 200 - mYpos) * CleanYfac); + + int y = mYpos; + int fontheight = OptionMenuSettings.mLinespacing; + + int response = 0; + for (int i = 0; i < mResponseLines.Size(); i++) + { + int width = SmallFont.StringWidth(mResponseLines[i]); + int x = 64; + + screen.DrawText(SmallFont, Font.CR_GREEN, x, y, mResponseLines[i], DTA_Clean, true); + + if (i == mResponses[response]) + { + String tbuf; + + response++; + tbuf = String.Format("%d.", response); + x = 50 - SmallFont.StringWidth(tbuf); + screen.DrawText(SmallFont, Font.CR_GREY, x, y, tbuf, DTA_Clean, true); + + if (response == mSelection + 1) + { + int colr = ((MenuTime() % 8) < 4) || GetCurrentMenu() != self ? Font.CR_RED : Font.CR_GREY; + + x = (50 + 3 - 160) * CleanXfac + screen.GetWidth() / 2; + int yy = (y + fontheight / 2 - 5 - 100) * CleanYfac + screen.GetHeight() / 2; + screen.DrawText(ConFont, colr, x, yy, "\xd", DTA_CellX, 8 * CleanXfac, DTA_CellY, 8 * CleanYfac); + } + } + y += fontheight; + } + } + + virtual void DrawGold() + { + if (mShowGold) + { + let coin = players[consoleplayer].ConversationPC.FindInventory("Coin"); + let icon = GetDefaultByType("Coin").Icon; + let goldstr = String.Format("%d", coin != NULL ? coin.Amount : 0); + screen.DrawText(SmallFont, Font.CR_GRAY, 21, 191, goldstr, DTA_320x200, true, DTA_FillColor, 0, DTA_Alpha, HR_SHADOW); + screen.DrawTexture(icon, false, 3, 190, DTA_320x200, true, DTA_FillColor, 0, DTA_Alpha, HR_SHADOW); + screen.DrawText(SmallFont, Font.CR_GRAY, 20, 190, goldstr, DTA_320x200, true); + screen.DrawTexture(icon, false, 2, 189, DTA_320x200, true); + } + + } + + //============================================================================ + // + // DrawConversationMenu + // + //============================================================================ + + override void Drawer() + { + if (mCurNode == NULL) + { + Close (); + return; + } + + bool dimbg = DrawBackdrop(); + DrawSpeakerText(dimbg); + DrawReplies(); + DrawGold(); + } + + + //============================================================================ + // + // + // + //============================================================================ + + override void Ticker() + { + // [CW] Freeze the game depending on MAPINFO options. + if (ConversationPauseTic < gametic && !multiplayer && !level.no_dlg_freeze) + { + menuactive = Menu.On; + } + } + +} diff --git a/wadsrc/static/zscript/menu/listmenu.txt b/wadsrc/static/zscript/menu/listmenu.txt index d920dc26c4..a7a9f71ae5 100644 --- a/wadsrc/static/zscript/menu/listmenu.txt +++ b/wadsrc/static/zscript/menu/listmenu.txt @@ -4,11 +4,11 @@ class ListMenuDescriptor : MenuDescriptor native { native Array mItems; native int mSelectedItem; - native int mSelectOfsX; - native int mSelectOfsY; + native double mSelectOfsX; + native double mSelectOfsY; native TextureID mSelector; native int mDisplayTop; - native int mXpos, mYpos; + native double mXpos, mYpos; native int mWLeft, mWRight; native int mLinespacing; // needs to be stored for dynamically created menus native int mAutoselect; // this can only be set by internal menu creation functions @@ -40,27 +40,27 @@ class ListMenuDescriptor : MenuDescriptor native // //============================================================================= -class ListMenu : Menu native +class ListMenu : Menu { - native ListMenuDescriptor mDesc; - native MenuItemBase mFocusControl; + ListMenuDescriptor mDesc; + MenuItemBase mFocusControl; virtual void Init(Menu parent = NULL, ListMenuDescriptor desc = NULL) { - mParentMenu = parent; + Super.Init(parent); mDesc = desc; if (desc.mCenter) { - int center = 160; + double center = 160; for(int i=0; i < mDesc.mItems.Size(); i++) { - int xpos = mDesc.mItems[i].GetX(); + double xpos = mDesc.mItems[i].GetX(); int width = mDesc.mItems[i].GetWidth(); - int curx = mDesc.mSelectOfsX; + double curx = mDesc.mSelectOfsX; if (width > 0 && mDesc.mItems[i].Selectable()) { - int left = 160 - (width - curx) / 2 - curx; + double left = 160 - (width - curx) / 2 - curx; if (left < center) center = left; } } @@ -76,7 +76,13 @@ class ListMenu : Menu native } } - MenuItemBase GetItem(Name name) + //============================================================================= + // + // + // + //============================================================================= + + ListMenuItem GetItem(Name name) { for(int i = 0; i < mDesc.mItems.Size(); i++) { @@ -86,12 +92,173 @@ class ListMenu : Menu native return NULL; } - //bool Responder (InputEventData ev); - //bool MenuEvent (int mkey, bool fromcontroller); - //bool MouseEvent(int type, int x, int y); - //void Ticker (); - //void Drawer (); + + //============================================================================= + // + // + // + //============================================================================= + + override bool Responder (InputEventData ev) + { + if (ev.type == InputEventData.GUI_Event) + { + if (ev.subtype == InputEventData.GUI_KeyDown) + { + // tolower + int ch = ev.data1; + ch = ch >= 65 && ch <91? ch + 32 : ch; + + for(int i = mDesc.mSelectedItem + 1; i < mDesc.mItems.Size(); i++) + { + if (mDesc.mItems[i].CheckHotkey(ch)) + { + mDesc.mSelectedItem = i; + MenuSound("menu/cursor"); + return true; + } + } + for(int i = 0; i < mDesc.mSelectedItem; i++) + { + if (mDesc.mItems[i].CheckHotkey(ch)) + { + mDesc.mSelectedItem = i; + MenuSound("menu/cursor"); + return true; + } + } + } + } + return Super.Responder(ev); + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool 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; + MenuSound("menu/cursor"); + return true; + + case MKEY_Down: + do + { + if (++mDesc.mSelectedItem >= mDesc.mItems.Size()) mDesc.mSelectedItem = 0; + } + while (!mDesc.mItems[mDesc.mSelectedItem].Selectable() && mDesc.mSelectedItem != startedAt); + if (mDesc.mSelectedItem == startedAt) mDesc.mSelectedItem = oldSelect; + MenuSound("menu/cursor"); + return true; + + case MKEY_Enter: + if (mDesc.mSelectedItem >= 0 && mDesc.mItems[mDesc.mSelectedItem].Activate()) + { + MenuSound("menu/choose"); + } + return true; + + default: + return Super.MenuEvent(mkey, fromcontroller); + } + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool 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(int i=0;i= 0 && mDesc.mSelectedItem < mDesc.mItems.Size()) + mDesc.mItems[mDesc.mSelectedItem].DrawSelector(mDesc.mSelectOfsX, mDesc.mSelectOfsY, mDesc.mSelector); + Super.Drawer(); + } + //============================================================================= + // + // + // + //============================================================================= + override void SetFocus(MenuItemBase fc) { mFocusControl = fc; @@ -106,3 +273,4 @@ class ListMenu : Menu native } } + diff --git a/wadsrc/static/zscript/menu/listmenuitems.txt b/wadsrc/static/zscript/menu/listmenuitems.txt index 8920e617bd..c1bafeaf94 100644 --- a/wadsrc/static/zscript/menu/listmenuitems.txt +++ b/wadsrc/static/zscript/menu/listmenuitems.txt @@ -35,7 +35,7 @@ class ListMenuItem : MenuItemBase { - void DrawSelector(int xofs, int yofs, TextureID tex) + void DrawSelector(double xofs, double yofs, TextureID tex) { if (tex.isNull()) { @@ -52,7 +52,7 @@ class ListMenuItem : MenuItemBase } else { - screen.DrawTexture (tex, mXpos + xofs, mYpos + yofs, DTA_Clean, true); + screen.DrawTexture (tex, true, mXpos + xofs, mYpos + yofs, DTA_Clean, true); } } } @@ -68,7 +68,7 @@ class ListMenuItemStaticPatch : ListMenuItem TextureID mTexture; bool mCentered; - void Init(int x, int y, TextureID patch, bool centered = false) + void Init(double x, double y, TextureID patch, bool centered = false) { Super.Init(x, y); mTexture = patch; @@ -82,17 +82,17 @@ class ListMenuItemStaticPatch : ListMenuItem return; } - int x = mXpos; + double x = mXpos; Vector2 vec = TexMan.GetScaledSize(mTexture); if (mYpos >= 0) { - if (mCentered) x -= int(vec.X) / 2; + if (mCentered) x -= vec.X / 2; screen.DrawTexture (mTexture, true, x, mYpos, DTA_Clean, true); } else { - int x = (mXpos - 160) * CleanXfac + (Screen.GetWidth()>>1); - if (mCentered) x -= (int(vec.X) * CleanXfac)/2; + x = (mXpos - 160) * CleanXfac + (Screen.GetWidth()>>1); + if (mCentered) x -= (vec.X * CleanXfac)/2; screen.DrawTexture (mTexture, true, x, -mYpos*CleanYfac, DTA_CleanNoMove, true); } } @@ -100,7 +100,7 @@ class ListMenuItemStaticPatch : ListMenuItem class ListMenuItemStaticPatchCentered : ListMenuItemStaticPatch { - void Init(int x, int y, TextureID patch) + void Init(double x, double y, TextureID patch) { Super.Init(x, y, patch, true); } @@ -119,7 +119,7 @@ class ListMenuItemStaticText : ListMenuItem int mColor; bool mCentered; - void Init(ListMenuDescriptor desc, int x, int y, String text, int color = Font.CR_UNTRANSLATED) + void Init(ListMenuDescriptor desc, double x, double y, String text, int color = Font.CR_UNTRANSLATED) { Super.Init(x, y); mText = text; @@ -128,7 +128,7 @@ class ListMenuItemStaticText : ListMenuItem mCentered = false; } - void InitDirect(int x, int y, String text, Font font, int color = Font.CR_UNTRANSLATED, bool centered = false) + void InitDirect(double x, double y, String text, Font font, int color = Font.CR_UNTRANSLATED, bool centered = false) { Super.Init(x, y); mText = text; @@ -144,13 +144,13 @@ class ListMenuItemStaticText : ListMenuItem String text = Stringtable.Localize(mText); if (mYpos >= 0) { - int x = mXpos; + double x = mXpos; if (mCentered) x -= mFont.StringWidth(text)/2; screen.DrawText(mFont, mColor, x, mYpos, text, DTA_Clean, true); } else { - int x = (mXpos - 160) * CleanXfac + (Screen.GetWidth() >> 1); + double x = (mXpos - 160) * CleanXfac + (Screen.GetWidth() >> 1); if (mCentered) x -= (mFont.StringWidth(text) * CleanXfac)/2; screen.DrawText (mFont, mColor, x, -mYpos*CleanYfac, text, DTA_CleanNoMove, true); } @@ -160,7 +160,7 @@ class ListMenuItemStaticText : ListMenuItem class ListMenuItemStaticTextCentered : ListMenuItemStaticText { - void Init(ListMenuDescriptor desc, int x, int y, String text, int color = -1) + void Init(ListMenuDescriptor desc, double x, double y, String text, int color = -1) { Super.Init(desc, x, y, text, color); mCentered = true; @@ -179,7 +179,7 @@ class ListMenuItemSelectable : ListMenuItem int mHeight; int mParam; - protected void Init(int x, int y, int height, Name childmenu, int param = -1) + protected void Init(double x, double y, int height, Name childmenu, int param = -1) { Super.Init(x, y, childmenu); mHeight = height; @@ -250,7 +250,7 @@ class ListMenuItemTextItem : ListMenuItemSelectable mHotkey = hotkey.CharCodeAt(0); } - void InitDirect(int x, int y, int height, String hotkey, String text, Font font, int color, int color2, Name child, int param = 0) + void InitDirect(double x, double y, int height, String hotkey, String text, Font font, int color, int color2, Name child, int param = 0) { Super.Init(x, y, height, child, param); mText = text; @@ -288,7 +288,7 @@ class ListMenuItemPatchItem : ListMenuItemSelectable mTexture = patch; } - void InitDirect(int x, int y, int height, TextureID patch, String hotkey, Name child, int param = 0) + void InitDirect(double x, double y, int height, TextureID patch, String hotkey, Name child, int param = 0) { Super.Init(x, y, height, child, param); mHotkey = hotkey.CharCodeAt(0); diff --git a/wadsrc/static/zscript/menu/loadsavemenu.txt b/wadsrc/static/zscript/menu/loadsavemenu.txt new file mode 100644 index 0000000000..05638d393b --- /dev/null +++ b/wadsrc/static/zscript/menu/loadsavemenu.txt @@ -0,0 +1,587 @@ +/* +** loacpp +** The load game and save game menus +** +**--------------------------------------------------------------------------- +** Copyright 2001-2010 Randy Heit +** Copyright 2010-2017 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. +**--------------------------------------------------------------------------- +** +*/ + + +struct SaveGameNode native +{ + native String SaveTitle; + native String Filename; + native bool bOldVersion; + native bool bMissingWads; + native bool bNoDelete; +} + +struct SavegameManager native +{ + native int WindowSize; + native SaveGameNode quickSaveSlot; + + native static SavegameManager GetManager(); + native void ReadSaveStrings(); + native void UnloadSaveData(); + + native int RemoveSaveSlot(int index); + native void LoadSavegame(int Selected); + native void DoSave(int Selected, String savegamestring); + native int ExtractSaveData(int index); + native void ClearSaveStuff(); + native bool DrawSavePic(int x, int y, int w, int h); + native void DrawSaveComment(Font font, int cr, int x, int y, int scalefactor); + native void SetFileInfo(int Selected); + native int SavegameCount(); + native SaveGameNode GetSavegame(int i); + native void InsertNewSaveNode(); + native bool RemoveNewSaveNode(); + +} + + + +class LoadSaveMenu : ListMenu +{ + SavegameManager manager; + int TopItem; + int Selected; + + int savepicLeft; + int savepicTop; + int savepicWidth; + int savepicHeight; + int rowHeight; + int listboxLeft; + int listboxTop; + int listboxWidth; + + int listboxRows; + int listboxHeight; + int listboxRight; + int listboxBottom; + + int commentLeft; + int commentTop; + int commentWidth; + int commentHeight; + int commentRight; + int commentBottom; + + bool mEntering; + TextEnterMenu mInput; + + + + //============================================================================= + // + // + // + //============================================================================= + + override void Init(Menu parent, ListMenuDescriptor desc) + { + Super.Init(parent, desc); + manager = SavegameManager.GetManager(); + manager.ReadSaveStrings(); + + savepicLeft = 10; + savepicTop = 54*CleanYfac; + savepicWidth = 216*screen.GetWidth()/640; + savepicHeight = 135*screen.GetHeight()/400; + manager.WindowSize = savepicWidth / CleanXfac; + + rowHeight = (SmallFont.GetHeight() + 1) * CleanYfac; + listboxLeft = savepicLeft + savepicWidth + 14; + listboxTop = savepicTop; + listboxWidth = screen.GetWidth() - listboxLeft - 10; + int listboxHeight1 = screen.GetHeight() - listboxTop - 10; + listboxRows = (listboxHeight1 - 1) / rowHeight; + listboxHeight = listboxRows * rowHeight + 1; + listboxRight = listboxLeft + listboxWidth; + listboxBottom = listboxTop + listboxHeight; + + commentLeft = savepicLeft; + commentTop = savepicTop + savepicHeight + 16; + commentWidth = savepicWidth; + commentHeight = (51+(screen.GetHeight()>200?10:0))*CleanYfac; + commentRight = commentLeft + commentWidth; + commentBottom = commentTop + commentHeight; + } + + //============================================================================= + // + // + // + //============================================================================= + + override void OnDestroy() + { + manager.ClearSaveStuff (); + Super.OnDestroy(); + } + + //============================================================================= + // + // + // + //============================================================================= + + override void Drawer () + { + Super.Drawer(); + + SaveGameNode node; + int i; + int j; + bool didSeeSelected = false; + + // Draw picture area + if (gameaction == ga_loadgame || gameaction == ga_loadgamehidecon || gameaction == ga_savegame) + { + return; + } + + Screen.DrawFrame (savepicLeft, savepicTop, savepicWidth, savepicHeight); + if (!manager.DrawSavePic(savepicLeft, savepicTop, savepicWidth, savepicHeight)) + { + screen.Clear (savepicLeft, savepicTop, savepicLeft+savepicWidth, savepicTop+savepicHeight, 0, 0); + + if (manager.SavegameCount() > 0) + { + String text = (Selected == -1 || !manager.GetSavegame(Selected).bOldVersion)? Stringtable.Localize("$MNU_NOPICTURE") : Stringtable.Localize("$MNU_DIFFVERSION"); + int textlen = SmallFont.StringWidth(text) * CleanXfac; + + screen.DrawText (SmallFont, Font.CR_GOLD, savepicLeft+(savepicWidth-textlen)/2, + savepicTop+(savepicHeight-rowHeight)/2, text, DTA_CleanNoMove, true); + } + } + + // Draw comment area + Screen.DrawFrame (commentLeft, commentTop, commentWidth, commentHeight); + screen.Clear (commentLeft, commentTop, commentRight, commentBottom, 0, 0); + + manager.DrawSaveComment(SmallFont, Font.CR_GOLD, commentLeft, commentTop, CleanYfac); + + // Draw file area + Screen.DrawFrame (listboxLeft, listboxTop, listboxWidth, listboxHeight); + screen.Clear (listboxLeft, listboxTop, listboxRight, listboxBottom, 0, 0); + + if (manager.SavegameCount() == 0) + { + String text = Stringtable.Localize("$MNU_NOFILES"); + int textlen = SmallFont.StringWidth(text) * CleanXfac; + + screen.DrawText (SmallFont, Font.CR_GOLD, listboxLeft+(listboxWidth-textlen)/2, listboxTop+(listboxHeight-rowHeight)/2, text, DTA_CleanNoMove, true); + return; + } + + j = TopItem; + for (i = 0; i < listboxRows && j < manager.SavegameCount(); i++) + { + int colr; + node = manager.GetSavegame(j); + if (node.bOldVersion) + { + colr = Font.CR_BLUE; + } + else if (node.bMissingWads) + { + colr = Font.CR_ORANGE; + } + else if (j == Selected) + { + colr = Font.CR_WHITE; + } + else + { + colr = Font.CR_TAN; + } + + if (j == Selected) + { + screen.Clear (listboxLeft, listboxTop+rowHeight*i, listboxRight, listboxTop+rowHeight*(i+1), mEntering ? Color(255,255,0,0) : Color(255,0,0,255)); + didSeeSelected = true; + if (!mEntering) + { + screen.DrawText (SmallFont, colr, listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, node.SaveTitle, DTA_CleanNoMove, true); + } + else + { + String s = mInput.GetText() .. SmallFont.GetCursor(); + screen.DrawText (SmallFont, Font.CR_WHITE, listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, s, DTA_CleanNoMove, true); + } + } + else + { + screen.DrawText (SmallFont, colr, listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, node.SaveTitle, DTA_CleanNoMove, true); + } + j++; + } + } + + + + //============================================================================= + // + // + // + //============================================================================= + + override bool MenuEvent (int mkey, bool fromcontroller) + { + switch (mkey) + { + case MKEY_Up: + if (manager.SavegameCount() > 1) + { + if (Selected == -1) Selected = TopItem; + else + { + if (--Selected < 0) Selected = manager.SavegameCount()-1; + if (Selected < TopItem) TopItem = Selected; + else if (Selected >= TopItem + listboxRows) TopItem = MAX(0, Selected - listboxRows + 1); + } + manager.UnloadSaveData (); + manager.ExtractSaveData (Selected); + } + return true; + + case MKEY_Down: + if (manager.SavegameCount() > 1) + { + if (Selected == -1) Selected = TopItem; + else + { + if (++Selected >= manager.SavegameCount()) Selected = 0; + if (Selected < TopItem) TopItem = Selected; + else if (Selected >= TopItem + listboxRows) TopItem = MAX(0, Selected - listboxRows + 1); + } + manager.UnloadSaveData (); + manager.ExtractSaveData (Selected); + } + return true; + + case MKEY_PageDown: + if (manager.SavegameCount() > 1) + { + if (TopItem >= manager.SavegameCount() - listboxRows) + { + TopItem = 0; + if (Selected != -1) Selected = 0; + } + else + { + TopItem = MIN(TopItem + listboxRows, manager.SavegameCount() - listboxRows); + if (TopItem > Selected && Selected != -1) Selected = TopItem; + } + manager.UnloadSaveData (); + manager.ExtractSaveData (Selected); + } + return true; + + case MKEY_PageUp: + if (manager.SavegameCount() > 1) + { + if (TopItem == 0) + { + TopItem = MAX(0, manager.SavegameCount() - listboxRows); + if (Selected != -1) Selected = TopItem; + } + else + { + TopItem = MAX(TopItem - listboxRows, 0); + if (Selected >= TopItem + listboxRows) Selected = TopItem; + } + manager.UnloadSaveData (); + manager.ExtractSaveData (Selected); + } + return true; + + case MKEY_Enter: + return false; // This event will be handled by the subclasses + + case MKEY_MBYes: + { + if (Selected < manager.SavegameCount()) + { + Selected = manager.RemoveSaveSlot (Selected); + } + return true; + } + + default: + return Super.MenuEvent(mkey, fromcontroller); + } + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MouseEvent(int type, int x, int y) + { + if (x >= listboxLeft && x < listboxLeft + listboxWidth && + y >= listboxTop && y < listboxTop + listboxHeight) + { + int lineno = (y - listboxTop) / rowHeight; + + if (TopItem + lineno < manager.SavegameCount()) + { + Selected = TopItem + lineno; + manager.UnloadSaveData (); + manager.ExtractSaveData (Selected); + if (type == MOUSE_Release) + { + if (MenuEvent(MKEY_Enter, true)) + { + return true; + } + } + } + else Selected = -1; + } + else Selected = -1; + + return Super.MouseEvent(type, x, y); + } + + + //============================================================================= + // + // + // + //============================================================================= + + override bool Responder (InputEventData ev) + { + if (ev.type == InputEventData.GUI_Event) + { + if (ev.subtype == InputEventData.GUI_KeyDown) + { + if (Selected < manager.SavegameCount()) + { + switch (ev.data1) + { + case UIEvent.Key_F1: + manager.SetFileInfo(Selected); + return true; + + case UIEvent.Key_DEL: + { + String EndString; + EndString = String.Format("%s%s%s%s?\n\n%s", Stringtable.Localize("$MNU_DELETESG"), TEXTCOLOR_WHITE, manager.GetSavegame(Selected).SaveTitle, TEXTCOLOR_NORMAL, Stringtable.Localize("$PRESSYN")); + StartMessage (EndString, 0); + } + return true; + } + } + } + else if (ev.subtype == InputEventData.GUI_WheelUp) + { + if (TopItem > 0) TopItem--; + return true; + } + else if (ev.subtype == InputEventData.GUI_WheelDown) + { + if (TopItem < manager.SavegameCount() - listboxRows) TopItem++; + return true; + } + } + return Super.Responder(ev); + } + + +} + +class SaveMenu : LoadSaveMenu +{ + String mSaveName; + + //============================================================================= + // + // + // + //============================================================================= + + override void Init(Menu parent, ListMenuDescriptor desc) + { + Super.Init(parent, desc); + manager.InsertNewSaveNode(); + TopItem = 0; + Selected = manager.ExtractSaveData (-1); + } + + //============================================================================= + // + // + // + //============================================================================= + + override void OnDestroy() + { + if (manager.RemoveNewSaveNode()) + { + Selected--; + } + Super.OnDestroy(); + } + + //============================================================================= + // + // + // + //============================================================================= + const SAVESTRINGSIZE = 32; + + override bool MenuEvent (int mkey, bool fromcontroller) + { + if (Super.MenuEvent(mkey, fromcontroller)) + { + return true; + } + if (Selected == -1) + { + return false; + } + + if (mkey == MKEY_Enter) + { + String SavegameString = (Selected != 0)? manager.GetSavegame(Selected).SaveTitle : ""; + mInput = TextEnterMenu.Open(self, SavegameString, SAVESTRINGSIZE, 1, fromcontroller); + mInput.ActivateMenu(); + mEntering = true; + } + else if (mkey == MKEY_Input) + { + // Do not start the save here, it would cause some serious execution ordering problems. + mEntering = false; + mSaveName = mInput.GetText(); + mInput = null; + } + else if (mkey == MKEY_Abort) + { + mEntering = false; + mInput = null; + } + return false; + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool Responder (InputEventData ev) + { + if (ev.subtype == InputEventData.GUI_KeyDown) + { + if (Selected != -1) + { + switch (ev.data1) + { + case UIEvent.Key_DEL: + // cannot delete 'new save game' item + if (Selected == 0) return true; + break; + + case 78://'N': + Selected = TopItem = 0; + manager.UnloadSaveData (); + return true; + } + } + } + return Super.Responder(ev); + } + + + override void Ticker() + { + if (mSaveName.Length() > 0) + { + manager.DoSave(Selected, mSaveName); + mSaveName = ""; + } + } + +} + +//============================================================================= +// +// +// +//============================================================================= + +class LoadMenu : LoadSaveMenu +{ + //============================================================================= + // + // + // + //============================================================================= + + override void Init(Menu parent, ListMenuDescriptor desc) + { + Super.Init(parent, desc); + TopItem = 0; + Selected = manager.ExtractSaveData (-1); + + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MenuEvent (int mkey, bool fromcontroller) + { + if (Super.MenuEvent(mkey, fromcontroller)) + { + return true; + } + if (Selected == -1 || manager.SavegameCount() == 0) + { + return false; + } + + if (mkey == MKEY_Enter) + { + manager.LoadSavegame(Selected); + return true; + } + return false; + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/menu/menu.txt b/wadsrc/static/zscript/menu/menu.txt index db105fa327..b4e5c1ac87 100644 --- a/wadsrc/static/zscript/menu/menu.txt +++ b/wadsrc/static/zscript/menu/menu.txt @@ -87,33 +87,194 @@ class Menu : Object native ui }; native Menu mParentMenu; + native bool mMouseCapture; + native bool mBackbuttonSelected; + native bool DontDim; - void Init(Menu parent) - { - mParentMenu = parent; - } - native static int MenuTime(); native static void SetVideoMode(); native static Menu GetCurrentMenu(); native static void SetMenu(Name mnu, int param = 0); native static void StartMessage(String msg, int mode = 0, Name command = 'none'); + native static void SetMouseCapture(bool on); + native void Close(); + native void ActivateMenu(); + //============================================================================= + // + // + // + //============================================================================= + + void Init(Menu parent) + { + mParentMenu = parent; + mMouseCapture = false; + mBackbuttonSelected = false; + DontDim = false; + } + + //============================================================================= + // + // + // + //============================================================================= + + virtual bool MenuEvent (int mkey, bool fromcontroller) + { + switch (mkey) + { + case MKEY_Back: + Close(); + MenuSound (GetCurrentMenu() != null? "menu/backup" : "menu/clear"); + return true; + } + return false; + } + + + //============================================================================= + // + // + // + //============================================================================= + + protected bool MouseEventBack(int type, int x, int y) + { + if (m_show_backbutton >= 0) + { + let tex = TexMan.CheckForTexture(gameinfo.mBackButton, TexMan.Type_MiscPatch); + if (tex.IsValid()) + { + Vector2 v = TexMan.GetScaledSize(tex); + int w = int(v.X + 0.5) * CleanXfac; + int h = int(v.Y + 0.5) * CleanYfac; + if (m_show_backbutton&1) x -= screen.GetWidth() - w; + if (m_show_backbutton&2) y -= screen.GetHeight() - h; + mBackbuttonSelected = ( x >= 0 && x < w && y >= 0 && y < h); + if (mBackbuttonSelected && type == MOUSE_Release) + { + if (m_use_mouse == 2) mBackbuttonSelected = false; + MenuEvent(MKEY_Back, true); + } + return mBackbuttonSelected; + } + } + return false; + } + + //============================================================================= + // + // + // + //============================================================================= + + virtual bool Responder(InputEventData ev) + { + bool res = false; + if (ev.type == InputEventData.GUI_Event) + { + if (ev.subtype == InputEventData.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 |= MouseEvent(MOUSE_Click, ev.data1, ev.data2); + if (res) + { + SetCapture(true); + } + + } + else if (ev.subtype == InputEventData.GUI_MouseMove) + { + BackbuttonTime = 4*Thinker.TICRATE; + if (mMouseCapture || m_use_mouse == 1) + { + res = MouseEventBack(MOUSE_Move, ev.data1, ev.data2); + if (res) ev.data2 = -1; + res |= MouseEvent(MOUSE_Move, ev.data1, ev.data2); + } + } + else if (ev.subtype == InputEventData.GUI_LButtonUp) + { + if (mMouseCapture) + { + SetCapture(false); + res = MouseEventBack(MOUSE_Release, ev.data1, ev.data2); + if (res) ev.data2 = -1; + res |= MouseEvent(MOUSE_Release, ev.data1, ev.data2); + } + } + } + return false; + } + + //============================================================================= + // + // + // + //============================================================================= + + virtual void Drawer () + { + if (self == GetCurrentMenu() && BackbuttonAlpha > 0 && m_show_backbutton >= 0 && m_use_mouse) + { + let tex = TexMan.CheckForTexture(gameinfo.mBackButton, TexMan.Type_MiscPatch); + if (tex.IsValid()) + { + Vector2 v = TexMan.GetScaledSize(tex); + int w = int(v.X + 0.5) * CleanXfac; + int h = int(v.Y + 0.5) * 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, true, x, y, DTA_CleanNoMove, true, DTA_ColorOverlay, Color(40, 255,255,255)); + } + else + { + screen.DrawTexture(tex, true, x, y, DTA_CleanNoMove, true, DTA_Alpha, BackbuttonAlpha); + } + } + } + } + + //============================================================================= + // + // + // + //============================================================================= + + void SetCapture(bool on) + { + if (mMouseCapture != on) + { + mMouseCapture = on; + SetMouseCapture(on); + } + } + + //============================================================================= + // + // + // + //============================================================================= + virtual bool TranslateKeyboardEvents() { return true; } virtual void SetFocus(MenuItemBase fc) {} virtual bool CheckFocus(MenuItemBase fc) { return false; } virtual void ReleaseFocus() {} virtual void ResetColor() {} + virtual bool MouseEvent(int type, int mx, int my) { return true; } + virtual void Ticker() {} + + //============================================================================= + // + // + // + //============================================================================= - native virtual bool Responder(InputEventData ev); - native virtual bool MenuEvent (int mkey, bool fromcontroller); - native virtual bool MouseEvent(int type, int mx, int my); - native virtual void Ticker(); - native virtual void Drawer(); - native void Close(); - native MenuItemBase GetItem(Name n); - native void ActivateMenu(); - static void MenuSound(Sound snd) { S_Sound (snd, CHAN_VOICE | CHAN_UI, snd_menuvolume, ATTN_NONE); @@ -135,3 +296,11 @@ class MenuDescriptor : Object native native static MenuDescriptor GetDescriptor(Name n); } +// This class is only needed to give it a virtual Init method that doesn't belong to Menu itself +class GenericMenu : Menu +{ + virtual void Init(Menu parent) + { + Super.Init(parent); + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/menu/menuitembase.txt b/wadsrc/static/zscript/menu/menuitembase.txt index 901de9d4f1..9b758c1172 100644 --- a/wadsrc/static/zscript/menu/menuitembase.txt +++ b/wadsrc/static/zscript/menu/menuitembase.txt @@ -6,11 +6,11 @@ class MenuItemBase : Object native ui { - protected native int mXpos, mYpos; + protected native double mXpos, mYpos; protected native Name mAction; native bool mEnabled; - void Init(int xpos = 0, int ypos = 0, Name actionname = 'None') + void Init(double xpos = 0, double ypos = 0, Name actionname = 'None') { mXpos = xpos; mYpos = ypos; @@ -36,10 +36,10 @@ class MenuItemBase : Object native ui virtual int GetIndent() { return 0; } virtual int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected) { return indent; } - void OffsetPositionY(int ydelta) { mYpos += ydelta; } - int GetY() { return mYpos; } - int GetX() { return mXpos; } - void SetX(int x) { mXpos = x; } + void OffsetPositionY(double ydelta) { mYpos += ydelta; } + double GetY() { return mYpos; } + double GetX() { return mXpos; } + void SetX(double x) { mXpos = x; } } // this is only used to parse font color ranges in MENUDEF diff --git a/wadsrc/static/zscript/menu/messagebox.txt b/wadsrc/static/zscript/menu/messagebox.txt new file mode 100644 index 0000000000..f5a60d9d40 --- /dev/null +++ b/wadsrc/static/zscript/menu/messagebox.txt @@ -0,0 +1,293 @@ +/* +** messagebox.cpp +** Confirmation, notification screns +** +**--------------------------------------------------------------------------- +** Copyright 2010-2017 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. +**--------------------------------------------------------------------------- +** +*/ + +class MessageBoxMenu : Menu +{ + BrokenLines mMessage; + voidptr Handler; + int mMessageMode; + int messageSelection; + int mMouseLeft, mMouseRight, mMouseY; + Name mAction; + + native static void CallHandler(voidptr hnd); + + + //============================================================================= + // + // + // + //============================================================================= + + void Init(Menu parent, String message, int messagemode, bool playsound = false, Name cmd = 'None', voidptr native_handler = null) + { + Super.Init(parent); + mAction = cmd; + messageSelection = 0; + mMouseLeft = 140; + mMouseY = 0x80000000; + int mr1 = 170 + SmallFont.StringWidth(Stringtable.Localize("$TXT_YES")); + int mr2 = 170 + SmallFont.StringWidth(Stringtable.Localize("$TXT_NO")); + mMouseRight = MAX(mr1, mr2); + mParentMenu = parent; + mMessage = SmallFont.BreakLines(Stringtable.Localize(message), 300); + mMessageMode = messagemode; + if (playsound) + { + MenuSound ("menu/prompt"); + } + Handler = native_handler; + } + + //============================================================================= + // + // + // + //============================================================================= + + override void Drawer () + { + int i, y; + + int fontheight = SmallFont.GetHeight(); + + y = 100; + + int c = mMessage.Count(); + for (i = 0; i < c; i++) + y -= SmallFont.GetHeight () / 2; + + for (i = 0; i < c; i++) + { + screen.DrawText (SmallFont, Font.CR_UNTRANSLATED, 160 - mMessage.StringWidth(i)/2, y, mMessage.StringAt(i), DTA_Clean, true); + y += fontheight; + } + + if (mMessageMode == 0) + { + y += fontheight; + mMouseY = y; + screen.DrawText(SmallFont, messageSelection == 0? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor, 160, y, Stringtable.Localize("$TXT_YES"), DTA_Clean, true); + screen.DrawText(SmallFont, messageSelection == 1? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor, 160, y + fontheight + 1, Stringtable.Localize("$TXT_NO"), DTA_Clean, true); + + if (messageSelection >= 0) + { + if ((MenuTime() % 8) < 6) + { + screen.DrawText(ConFont, OptionMenuSettings.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); + } + } + } + } + + + //============================================================================= + // + // + // + //============================================================================= + + protected void CloseSound() + { + MenuSound (GetCurrentMenu() != NULL? "menu/backup" : "menu/dismiss"); + } + + //============================================================================= + // + // + // + //============================================================================= + + virtual void HandleResult(bool res) + { + if (Handler != null) + { + if (res) + { + CallHandler(Handler); + } + else + { + Close(); + CloseSound(); + } + } + else if (mParentMenu != NULL) + { + if (mMessageMode == 0) + { + if (mAction == 'None') + { + mParentMenu.MenuEvent(res? MKEY_MBYes : MKEY_MBNo, false); + Close(); + } + else + { + Close(); + if (res) SetMenu(mAction, -1); + } + CloseSound(); + } + } + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool Responder(InputEventData ev) + { + if (ev.type == InputEventData.GUI_Event && ev.subtype == InputEventData.GUI_KeyDown) + { + if (mMessageMode == 0) + { + // tolower + int ch = ev.data1; + ch = ch >= 65 && ch <91? ch + 32 : ch; + + if (ch == 110 /*'n'*/ || ch == 32) + { + HandleResult(false); + return true; + } + else if (ch == 121 /*'y'*/) + { + HandleResult(true); + return true; + } + } + else + { + Close(); + return true; + } + return false; + } + else if (ev.type == InputEventData.KeyDown) + { + Close(); + return true; + } + return Super.Responder(ev); + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MenuEvent(int mkey, bool fromcontroller) + { + if (mMessageMode == 0) + { + if (mkey == MKEY_Up || mkey == MKEY_Down) + { + MenuSound("menu/cursor"); + 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; + } + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool 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; + } + } + + +} + + + diff --git a/wadsrc/static/zscript/menu/optionmenu.txt b/wadsrc/static/zscript/menu/optionmenu.txt index 5b06f30206..ed6189335c 100644 --- a/wadsrc/static/zscript/menu/optionmenu.txt +++ b/wadsrc/static/zscript/menu/optionmenu.txt @@ -56,8 +56,6 @@ class OptionMenuDescriptor : MenuDescriptor native native int mPosition; native bool mDontDim; - native void CalcIndent(); - native OptionMenuItem GetItem(Name iname); void Reset() { // Reset the default settings (ignore all other values in the struct) @@ -66,6 +64,25 @@ class OptionMenuDescriptor : MenuDescriptor native mIndent = 0; mDontDim = 0; } + + //============================================================================= + // + // + // + //============================================================================= + + void CalcIndent() + { + // calculate the menu indent + int widest = 0, thiswidth; + + for (int i = 0; i < mItems.Size(); i++) + { + thiswidth = mItems[i].GetIndent(); + if (thiswidth > widest) widest = thiswidth; + } + mIndent = widest + 4; + } } @@ -87,11 +104,29 @@ class OptionMenu : Menu { mParentMenu = parent; mDesc = desc; + DontDim = desc.mDontDim; if (mDesc != NULL && mDesc.mSelectedItem == -1) mDesc.mSelectedItem = FirstSelectable(); - + mDesc.CalcIndent(); } + //============================================================================= + // + // + // + //============================================================================= + + OptionMenuItem GetItem(Name name) + { + for(int i = 0; i < mDesc.mItems.Size(); i++) + { + Name nm = mDesc.mItems[i].GetAction(); + if (nm == name) return mDesc.mItems[i]; + } + return NULL; + } + + //============================================================================= // // diff --git a/wadsrc/static/zscript/menu/optionmenuitems.txt b/wadsrc/static/zscript/menu/optionmenuitems.txt index 8efe0cfb91..404ce5b36f 100644 --- a/wadsrc/static/zscript/menu/optionmenuitems.txt +++ b/wadsrc/static/zscript/menu/optionmenuitems.txt @@ -136,7 +136,7 @@ class OptionMenuItemCommand : OptionMenuItemSubmenu // don't execute if no menu is active if (m == null) return false; // don't execute if this item cannot be found in the current menu. - if (m.mDesc.GetItem(mAction) != self) return false; + if (m.GetItem(mAction) != self) return false; Menu.MenuSound("menu/choose"); DoCommand(mAction); return true; @@ -1129,6 +1129,7 @@ class OptionMenuItemTextField : OptionMenuFieldBase { Menu.MenuSound("menu/choose"); mEnter = TextEnterMenu.Open(Menu.GetCurrentMenu(), GetCVarString(), -1, 2, fromcontroller); + mEnter.ActivateMenu(); return true; } else if (mkey == Menu.MKEY_Input) diff --git a/wadsrc/static/zscript/menu/playercontrols.txt b/wadsrc/static/zscript/menu/playercontrols.txt index 389c34043e..b0f97cdbc2 100644 --- a/wadsrc/static/zscript/menu/playercontrols.txt +++ b/wadsrc/static/zscript/menu/playercontrols.txt @@ -71,7 +71,7 @@ class ListMenuItemPlayerNameBox : ListMenuItemSelectable // //============================================================================= - void InitDirect(int x, int y, int height, int frameofs, String text, Font font, int color, Name command) + void InitDirect(double x, double y, int height, int frameofs, String text, Font font, int color, Name command) { Super.Init(x, y, height, command); mText = text; @@ -113,7 +113,7 @@ class ListMenuItemPlayerNameBox : ListMenuItemSelectable // //============================================================================= - protected void DrawBorder (int x, int y, int len) + protected void DrawBorder (double x, double y, int len) { let left = TexMan.CheckForTexture("M_LSLEFT", TexMan.Type_MiscPatch); let mid = TexMan.CheckForTexture("M_LSCNTR", TexMan.Type_MiscPatch); @@ -141,7 +141,9 @@ class ListMenuItemPlayerNameBox : ListMenuItemSelectable } else { - screen.Clear(x, y, x + len, y + SmallFont.GetHeight() * 3/2, 0); + int xx = int(x - 160) * CleanXfac + screen.GetWidth()/2; + int yy = int(y - 100) * CleanXfac + screen.GetHeight()/2; + screen.Clear(xx, yy, xx + len*CleanXfac, yy + SmallFont.GetHeight() * CleanYfac * 3/2, 0); } } } @@ -161,7 +163,7 @@ class ListMenuItemPlayerNameBox : ListMenuItemSelectable } // Draw player name box - int x = mXpos + mFont.StringWidth(text) + 16 + mFrameSize; + double x = mXpos + mFont.StringWidth(text) + 16 + mFrameSize; DrawBorder (x, mYpos - mFrameSize, MAXPLAYERNAME+1); if (!mEnter) { @@ -186,6 +188,7 @@ class ListMenuItemPlayerNameBox : ListMenuItemSelectable { Menu.MenuSound ("menu/choose"); mEnter = TextEnterMenu.Open(Menu.GetCurrentMenu(), mPlayerName, MAXPLAYERNAME, 2, fromcontroller); + mEnter.ActivateMenu(); return true; } else if (mkey == Menu.MKEY_Input) @@ -246,7 +249,7 @@ class ListMenuItemValueText : ListMenuItemSelectable // //============================================================================= - void InitDirect(int x, int y, int height, String text, Font font, int color, int valuecolor, Name command, Name values) + void InitDirect(double x, double y, int height, String text, Font font, int color, int valuecolor, Name command, Name values) { Super.Init(x, y, height, command); mText = text; @@ -337,7 +340,7 @@ class ListMenuItemValueText : ListMenuItemSelectable String text = Stringtable.Localize(mText); screen.DrawText(mFont, selected? OptionMenuSettings.mFontColorSelection : mFontColor, mXpos, mYpos, text, DTA_Clean, true); - int x = mXpos + mFont.StringWidth(text) + 8; + double x = mXpos + mFont.StringWidth(text) + 8; if (mSelections.Size() > 0) { screen.DrawText(mFont, mFontColor2, x, mYpos, mSelections[mSelection], DTA_Clean, true); @@ -385,7 +388,7 @@ class ListMenuItemSlider : ListMenuItemSelectable // //============================================================================= - void InitDirect(int x, int y, int height, String text, Font font, int color, Name command, int min, int max, int step) + void InitDirect(double x, double y, int height, String text, Font font, int color, Name command, int min, int max, int step) { Super.Init(x, y, height, command); mText = text; @@ -463,7 +466,7 @@ class ListMenuItemSlider : ListMenuItemSelectable lm.ReleaseFocus(); } - int slide_left = SmallFont.StringWidth ("Green") + 8 + mXpos; + int slide_left = SmallFont.StringWidth ("Green") + 8 + int(mXpos); int slide_right = slide_left + 12*8; // 12 char cells with 8 pixels each. if (type == Menu.MOUSE_Click) @@ -491,7 +494,7 @@ class ListMenuItemSlider : ListMenuItemSelectable // //============================================================================= - protected void DrawSlider (int x, int y) + protected void DrawSlider (double x, double y) { int range = mMaxrange - mMinrange; int cur = mSelection - mMinrange; @@ -515,8 +518,8 @@ class ListMenuItemSlider : ListMenuItemSelectable screen.DrawText(mFont, selected? OptionMenuSettings.mFontColorSelection : mFontColor, mXpos, mYpos, text, DTA_Clean, true); - int x = SmallFont.StringWidth ("Green") + 8 + mXpos; - int x2 = SmallFont.StringWidth (text) + 8 + mXpos; + double x = SmallFont.StringWidth ("Green") + 8 + mXpos; + double x2 = SmallFont.StringWidth (text) + 8 + mXpos; DrawSlider (MAX(x2, x), mYpos); } } \ No newline at end of file diff --git a/wadsrc/static/zscript/menu/playerdisplay.txt b/wadsrc/static/zscript/menu/playerdisplay.txt index 492ba978c3..da64559c84 100644 --- a/wadsrc/static/zscript/menu/playerdisplay.txt +++ b/wadsrc/static/zscript/menu/playerdisplay.txt @@ -252,8 +252,8 @@ class ListMenuItemPlayerDisplay : ListMenuItem } else { - int x = (mXpos - 160) * CleanXfac + (screen.GetWidth() >> 1); - int y = (mYpos - 100) * CleanYfac + (screen.GetHeight() >> 1); + int x = int(mXpos - 160) * CleanXfac + (screen.GetWidth() >> 1); + int y = int(mYpos - 100) * CleanYfac + (screen.GetHeight() >> 1); screen.DrawTexture(mBackdrop, false, x, y - 1, DTA_DestWidth, 72 * CleanXfac, diff --git a/wadsrc/static/zscript/menu/playermenu.txt b/wadsrc/static/zscript/menu/playermenu.txt new file mode 100644 index 0000000000..4502cc9fa8 --- /dev/null +++ b/wadsrc/static/zscript/menu/playermenu.txt @@ -0,0 +1,595 @@ +/* +** playermenu.cpp +** The player setup menu +** +**--------------------------------------------------------------------------- +** Copyright 2001-2010 Randy Heit +** Copyright 2010-2017 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. +**--------------------------------------------------------------------------- +** +*/ + +class PlayerMenu : ListMenu +{ + int mRotation; + int PlayerClassIndex; + PlayerClass mPlayerClass; + Array PlayerColorSets; + Array mPlayerSkins; + + // All write function for the player config are native to prevent abuse. + protected native void AutoaimChanged(float val); + protected native void TeamChanged(int val); + protected native void AlwaysRunChanged(int val); + protected native void GenderChanged(int val); + protected native void SwitchOnPickupChanged(int val); + protected native void ColorChanged(int red, int green, int blue); + protected native void ColorSetChanged(int red); + protected native void PlayerNameChanged(String name); + protected native void SkinChanged (int val); + protected native void ClassChanged(int sel, PlayerClass cls); + + //============================================================================= + // + // + // + //============================================================================= + + protected void UpdateTranslation() + { + Translation.SetPlayerTranslation(TRANSLATION_Players, MAXPLAYERS, consoleplayer, mPlayerClass); + } + + protected void SendNewColor (int red, int green, int blue) + { + ColorChanged(red, green, blue); + UpdateTranslation(); + } + + //============================================================================= + // + // + // + //============================================================================= + + override void Init(Menu parent, ListMenuDescriptor desc) + { + MenuItemBase li; + PlayerInfo p = players[consoleplayer]; + + Super.Init(parent, desc); + PickPlayerClass(); + mRotation = 0; + + li = GetItem('Playerdisplay'); + if (li != NULL) + { + li.SetValue(ListMenuItemPlayerDisplay.PDF_ROTATION, 0); + li.SetValue(ListMenuItemPlayerDisplay.PDF_MODE, 1); + li.SetValue(ListMenuItemPlayerDisplay.PDF_TRANSLATE, 1); + li.SetValue(ListMenuItemPlayerDisplay.PDF_CLASS, p.GetPlayerClassNum()); + if (mPlayerClass != NULL && !(GetDefaultByType (mPlayerClass.Type).bNoSkin) && + p.GetPlayerClassNum() != -1) + { + li.SetValue(ListMenuItemPlayerDisplay.PDF_SKIN, p.GetSkin()); + } + } + + li = GetItem('Playerbox'); + if (li != NULL) + { + li.SetString(0, p.GetUserName()); + } + + li = GetItem('Team'); + if (li != NULL) + { + li.SetString(0, "None"); + for(int i=0;i= 0 ? pclass : pclass + 1); + } + } + + UpdateSkins(); + + li = GetItem('Gender'); + if (li != NULL) + { + li.SetValue(0, p.GetGender()); + } + + li = GetItem('Autoaim'); + if (li != NULL) + { + li.SetValue(0, int(p.GetAutoaim())); + } + + li = GetItem('Switch'); + if (li != NULL) + { + li.SetValue(0, p.GetNeverSwitch()); + } + + li = GetItem('AlwaysRun'); + if (li != NULL) + { + li.SetValue(0, cl_run); + } + + if (mDesc.mSelectedItem < 0) mDesc.mSelectedItem = 1; + + } + + + //============================================================================= + // + // + // + //============================================================================= + + protected void PickPlayerClass(int pick = -100) + { + int pclass = 0; + // [GRB] Pick a class from player class list + if (PlayerClasses.Size () > 1) + { + pclass = pick == -100? players[consoleplayer].GetPlayerClassNum() : pick; + + if (pclass < 0) + { + pclass = (MenuTime() >> 7) % PlayerClasses.Size (); + } + } + PlayerClassIndex = pclass; + mPlayerClass = PlayerClasses[PlayerClassIndex]; + UpdateTranslation(); + } + + //============================================================================= + // + // + // + //============================================================================= + + protected void UpdateColorsets() + { + let li = GetItem('Color'); + if (li != NULL) + { + int sel = 0; + mPlayerClass.EnumColorSets(PlayerColorSets); + li.SetString(0, "Custom"); + for(int i = 0; i < PlayerColorSets.Size(); i++) + { + let cname = mPlayerClass.GetColorSetName(PlayerColorSets[i]); + li.SetString(i+1, cname); + } + int mycolorset = players[consoleplayer].GetColorSet(); + if (mycolorset != -1) + { + for(int i = 0; i < PlayerColorSets.Size(); i++) + { + if (PlayerColorSets[i] == mycolorset) + { + sel = i + 1; + } + } + } + li.SetValue(0, sel); + } + } + + //============================================================================= + // + // + // + //============================================================================= + + protected void UpdateSkins() + { + int sel = 0; + int skin; + let li = GetItem('Skin'); + if (li != NULL) + { + if (GetDefaultByType (mPlayerClass.Type).bNoSkin || players[consoleplayer].GetPlayerClassNum() == -1) + { + li.SetString(0, "Base"); + li.SetValue(0, 0); + skin = 0; + } + else + { + mPlayerSkins.Clear(); + for (int i = 0; i < PlayerSkins.Size(); i++) + { + if (mPlayerClass.CheckSkin(i)) + { + int j = mPlayerSkins.Push(i); + li.SetString(j, PlayerSkins[i].SkinName); + if (players[consoleplayer].GetSkin() == i) + { + sel = j; + } + } + } + li.SetValue(0, sel); + skin = mPlayerSkins[sel]; + } + li = GetItem('Playerdisplay'); + if (li != NULL) + { + li.SetValue(ListMenuItemPlayerDisplay.PDF_SKIN, skin); + } + } + UpdateTranslation(); + } + + //============================================================================= + // + // + // + //============================================================================= + + void ChangeClass (MenuItemBase li) + { + if (PlayerClasses.Size () == 1) + { + return; + } + + bool res; + int sel; + + [res, sel] = li.GetValue(0); + if (res) + { + PickPlayerClass(gameinfo.norandomplayerclass ? sel : sel-1); + ClassChanged(sel, mPlayerClass); + UpdateSkins(); + UpdateColorsets(); + UpdateTranslation(); + + li = GetItem('Playerdisplay'); + if (li != NULL) + { + li.SetValue(ListMenuItemPlayerDisplay.PDF_CLASS, players[consoleplayer].GetPlayerClassNum()); + } + } + } + + //============================================================================= + // + // + // + //============================================================================= + + protected void ChangeSkin (MenuItemBase li) + { + if (GetDefaultByType (mPlayerClass.Type).bNoSkin || players[consoleplayer].GetPlayerClassNum() == -1) + { + return; + } + + bool res; + int sel; + + [res, sel] = li.GetValue(0); + if (res) + { + sel = mPlayerSkins[sel]; + SkinChanged(sel); + UpdateTranslation(); + + li = GetItem('Playerdisplay'); + if (li != NULL) + { + li.SetValue(ListMenuItemPlayerDisplay.PDF_SKIN, sel); + } + } + } + + + //============================================================================= + // + // + // + //============================================================================= + + override bool Responder (InputEventData ev) + { + if (ev.type == InputEventData.GUI_Event && ev.subtype == InputEventData.GUI_Char && ev.data1 == 32) + { + // turn the player sprite around + mRotation = 8 - mRotation; + MenuItemBase li = GetItem('Playerdisplay'); + if (li != NULL) + { + li.SetValue(ListMenuItemPlayerDisplay.PDF_ROTATION, mRotation); + } + return true; + } + return Super.Responder(ev); + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MenuEvent (int mkey, bool fromcontroller) + { + int v; + bool res; + String s; + if (mDesc.mSelectedItem >= 0) + { + let li = mDesc.mItems[mDesc.mSelectedItem]; + if (li.MenuEvent(mkey, fromcontroller)) + { + Name ctrl = li.GetAction(); + switch(ctrl) + { + // item specific handling comes here + + case 'Playerbox': + if (mkey == MKEY_Input) + { + [res, s] = li.GetString(0); + if (res) PlayerNameChanged(s); + } + break; + + case 'Team': + [res, v] = li.GetValue(0); + if (res) TeamChanged(v); + break; + + case 'Color': + [res, v] = li.GetValue(0); + if (res) + { + int mycolorset = -1; + + if (v > 0) mycolorset = PlayerColorSets[v - 1]; + + let red = GetItem('Red'); + let green = GetItem('Green'); + let blue = GetItem('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); + + ColorSetChanged(v - 1); + UpdateTranslation(); + } + break; + + case 'Red': + [res, v] = li.GetValue(0); + if (res) + { + Color colr = players[consoleplayer].GetColor(); + SendNewColor (v, colr.g, colr.b); + } + break; + + case 'Green': + [res, v] = li.GetValue(0); + if (res) + { + Color colr = players[consoleplayer].GetColor(); + SendNewColor (colr.r, v, colr.b); + } + break; + + case 'Blue': + [res, v] = li.GetValue(0); + if (res) + { + Color colr = players[consoleplayer].GetColor(); + SendNewColor (colr.r, colr.g, v); + } + break; + + case 'Class': + [res, v] = li.GetValue(0); + if (res) + { + ChangeClass(li); + } + break; + + case 'Skin': + ChangeSkin(li); + break; + + case 'Gender': + [res, v] = li.GetValue(0); + if (res) + { + GenderChanged(v); + } + break; + + case 'Autoaim': + [res, v] = li.GetValue(0); + if (res) + { + AutoaimChanged(v); + } + break; + + case 'Switch': + [res, v] = li.GetValue(0); + if (res) + { + SwitchOnPickupChanged(v); + } + break; + + case 'AlwaysRun': + [res, v] = li.GetValue(0); + if (res) + { + AlwaysRunChanged(v); + } + break; + + default: + break; + } + return true; + } + } + return Super.MenuEvent(mkey, fromcontroller); + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MouseEvent(int type, int x, int y) + { + let li = mFocusControl; + bool res = Super.MouseEvent(type, x, y); + if (li == NULL) li = mFocusControl; + if (li != NULL) + { + // Check if the colors have changed + Name ctrl = li.GetAction(); + bool resv; + int v; + [resv, v]= li.GetValue(0); + switch(ctrl) + { + case 'Red': + if (resv) + { + Color colr = players[consoleplayer].GetColor(); + SendNewColor (v, colr.g, colr.b); + } + break; + + case 'Green': + if (resv) + { + Color colr = players[consoleplayer].GetColor(); + SendNewColor (colr.r, v, colr.b); + } + break; + + case 'Blue': + if (resv) + { + Color colr = players[consoleplayer].GetColor(); + SendNewColor (colr.r, colr.g, v); + } + break; + case 'Autoaim': + AutoaimChanged(v); + break; + } + } + return res; + } + + + //============================================================================= + // + // + // + //============================================================================= + + override void Drawer () + { + Super.Drawer(); + String str = "PRESS " .. TEXTCOLOR_WHITE .. "SPACE"; + screen.DrawText (SmallFont, Font.CR_GOLD, 320 - 32 - 32 - SmallFont.StringWidth (str)/2, 50 + 48 + 70, str, DTA_Clean, true); + str = mRotation ? "TO SEE FRONT" : "TO SEE BACK"; + screen.DrawText (SmallFont, Font.CR_GOLD, 320 - 32 - 32 - SmallFont.StringWidth (str)/2, 50 + 48 + 70 + SmallFont.GetHeight (), str, DTA_Clean, true); + + } + +} \ No newline at end of file diff --git a/wadsrc/static/zscript/menu/readthis.txt b/wadsrc/static/zscript/menu/readthis.txt new file mode 100644 index 0000000000..604fb489e0 --- /dev/null +++ b/wadsrc/static/zscript/menu/readthis.txt @@ -0,0 +1,125 @@ +/* +** 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. +**--------------------------------------------------------------------------- +** +*/ + +class ReadThisMenu : GenericMenu +{ + int mScreen; + int mInfoTic; + + //============================================================================= + // + // + // + //============================================================================= + + override void Init(Menu parent) + { + Super.Init(parent); + mScreen = 1; + mInfoTic = gametic; + } + + override void Drawer() + { + double alpha; + TextureID tex, prevpic; + + // Did the mapper choose a custom help page via MAPINFO? + if (level.F1Pic.Length() != 0) + { + tex = TexMan.CheckForTexture(level.F1Pic, TexMan.Type_MiscPatch); + mScreen = 1; + } + + if (!tex.IsValid()) + { + tex = TexMan.CheckForTexture(gameinfo.infoPages[mScreen-1], TexMan.Type_MiscPatch); + } + + if (mScreen > 1) + { + prevpic = TexMan.CheckForTexture(gameinfo.infoPages[mScreen-2], TexMan.Type_MiscPatch); + } + + screen.Dim(0, 1.0, 0,0, screen.GetWidth(), screen.GetHeight()); + alpha = MIN((gametic - mInfoTic) * (3. / Thinker.TICRATE), 1.); + if (alpha < 1. && prevpic.IsValid()) + { + screen.DrawTexture (prevpic, false, 0, 0, DTA_Fullscreen, true); + } + else alpha = 1; + screen.DrawTexture (tex, false, 0, 0, DTA_Fullscreen, true, DTA_Alpha, alpha); + + } + + + //============================================================================= + // + // + // + //============================================================================= + + override bool MenuEvent(int mkey, bool fromcontroller) + { + if (mkey == MKEY_Enter) + { + MenuSound("menu/choose"); + mScreen++; + mInfoTic = gametic; + if (level.F1Pic.Length() != 0 || mScreen > gameinfo.infoPages.Size()) + { + Close(); + } + return true; + } + else return Super.MenuEvent(mkey, fromcontroller); + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MouseEvent(int type, int x, int y) + { + if (type == MOUSE_Click) + { + return MenuEvent(MKEY_Enter, true); + } + return false; + } + +} \ No newline at end of file diff --git a/wadsrc/static/zscript/menu/textentermenu.txt b/wadsrc/static/zscript/menu/textentermenu.txt index 7974a62bd0..b92932aaf3 100644 --- a/wadsrc/static/zscript/menu/textentermenu.txt +++ b/wadsrc/static/zscript/menu/textentermenu.txt @@ -1,15 +1,362 @@ +/* +** menuinput.cpp +** The string input code +** +**--------------------------------------------------------------------------- +** Copyright 2001-2010 Randy Heit +** Copyright 2010-2017 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. +**--------------------------------------------------------------------------- +** +*/ -// This is only the parts that are needed to make the menu fully work right now. More to come later. -class TextEnterMenu : Menu native + +class TextEnterMenu : Menu { - native bool mInputGridOkay; + const INPUTGRID_WIDTH = 13; + const INPUTGRID_HEIGHT = 5; - native static TextEnterMenu Open(Menu parent, String text, int maxlen, int sizemode, bool fromcontroller); - native String GetText(); + const Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-=.,!?@'\":;[]()<>^#$%&*/_ \b"; + + String mEnterString; + int mEnterSize; + int mEnterPos; + int mSizeMode; // 1: size is length in chars. 2: also check string width + bool mInputGridOkay; + int InputGridX; + int InputGridY; + bool AllowColors; + //============================================================================= + // + // + // + //============================================================================= + + // [TP] Added allowcolors + private void Init(Menu parent, String textbuffer, int maxlen, int sizemode, bool showgrid, bool allowcolors) + { + Super.init(parent); + mEnterString = textbuffer; + mEnterSize = maxlen < 0 ? 0x7fffffff : maxlen; + mSizeMode = sizemode; + mInputGridOkay = showgrid || m_showinputgrid; + if (mEnterString.Length() > 0) + { + InputGridX = INPUTGRID_WIDTH - 1; + InputGridY = INPUTGRID_HEIGHT - 1; + } + else + { + // If we are naming a new save, don't start the cursor on "end". + InputGridX = 0; + InputGridY = 0; + } + AllowColors = allowcolors; // [TP] + } + + static TextEnterMenu Open(Menu parent, String textbuffer, int maxlen, int sizemode, bool showgrid = false, bool allowcolors = false) + { + let me = new("TextEnterMenu"); + me.Init(parent, textbuffer, maxlen, sizemode, showgrid, allowcolors); + return me; + } + + //============================================================================= + // + // + // + //============================================================================= + + String GetText() + { + return mEnterString; + } override bool TranslateKeyboardEvents() { return mInputGridOkay; } + + + //============================================================================= + // + // + // + //============================================================================= + + override bool Responder(InputEventData ev) + { + if (ev.type == InputEventData.GUI_Event) + { + // Save game and player name string input + if (ev.subtype == InputEventData.GUI_Char) + { + mInputGridOkay = false; + if (mEnterString.Length() < mEnterSize && + (mSizeMode == 2/*entering player name*/ || SmallFont.StringWidth(mEnterString) < (mEnterSize-1)*8)) + { + mEnterString.AppendFormat("%c", ev.data1); + } + return true; + } + int ch = ev.data1; + if ((ev.subtype == InputEventData.GUI_KeyDown || ev.subtype == InputEventData.GUI_KeyRepeat) && ch == 8) + { + if (mEnterString.Length() > 0) + { + mEnterString.Truncate(mEnterString.Length() - 1); + } + } + else if (ev.subtype == InputEventData.GUI_KeyDown) + { + if (ch == UIEvent.Key_ESCAPE) + { + Menu parent = mParentMenu; + Close(); + parent.MenuEvent(MKEY_Abort, false); + return true; + } + else if (ch == 13) + { + if (mEnterString.Length() > 0) + { + // [TP] If we allow color codes, colorize the string now. + if (AllowColors) + mEnterString = mEnterString.Filter(); + + Menu parent = mParentMenu; + parent.MenuEvent(MKEY_Input, false); + Close(); + return true; + } + } + } + if (ev.subtype == InputEventData.GUI_KeyDown || ev.subtype == InputEventData.GUI_KeyRepeat) + { + return true; + } + } + return Super.Responder(ev); + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MouseEvent(int type, int x, int y) + { + int cell_width = 18 * CleanXfac; + int cell_height = 12 * CleanYfac; + int screen_y = screen.GetHeight() - INPUTGRID_HEIGHT * cell_height; + 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 == MOUSE_Release) + { + if (MenuEvent(MKEY_Enter, true)) + { + MenuSound("menu/choose"); + if (m_use_mouse == 2) InputGridX = InputGridY = -1; + } + } + return true; + } + else + { + InputGridX = InputGridY = -1; + } + return Super.MouseEvent(type, x, y); + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MenuEvent (int key, bool fromcontroller) + { + String InputGridChars = Chars; + if (key == MKEY_Back) + { + mParentMenu.MenuEvent(MKEY_Abort, false); + return Super.MenuEvent(key, fromcontroller); + } + if (fromcontroller) + { + mInputGridOkay = true; + } + + if (mInputGridOkay) + { + int ch; + + if (InputGridX == -1 || InputGridY == -1) + { + InputGridX = InputGridY = 0; + } + switch (key) + { + case MKEY_Down: + InputGridY = (InputGridY + 1) % INPUTGRID_HEIGHT; + return true; + + case MKEY_Up: + InputGridY = (InputGridY + INPUTGRID_HEIGHT - 1) % INPUTGRID_HEIGHT; + return true; + + case MKEY_Right: + InputGridX = (InputGridX + 1) % INPUTGRID_WIDTH; + return true; + + case MKEY_Left: + InputGridX = (InputGridX + INPUTGRID_WIDTH - 1) % INPUTGRID_WIDTH; + return true; + + case MKEY_Clear: + if (mEnterString.Length() > 0) + { + mEnterString.Truncate(mEnterString.Length() - 1); + } + return true; + + case MKEY_Enter: + if (mInputGridOkay) + { + String c = InputGridChars.CharAt(InputGridX + InputGridY * INPUTGRID_WIDTH); + int ch = c.CharCodeAt(0); + if (ch == 0) // end + { + if (mEnterString.Length() > 0) + { + Menu parent = mParentMenu; + parent.MenuEvent(MKEY_Input, false); + Close(); + return true; + } + } + else if (ch == 8) // bs + { + if (mEnterString.Length() > 0) + { + mEnterString.Truncate(mEnterString.Length() - 1); + } + } + else if (mEnterString.Length() < mEnterSize && + (mSizeMode == 2/*entering player name*/ || SmallFont.StringWidth(mEnterString) < (mEnterSize-1)*8)) + { + mEnterString = mEnterString .. c; + } + } + return true; + + default: + break; // Keep GCC quiet + } + } + return false; + } + + + //============================================================================= + // + // + // + //============================================================================= + + override void Drawer () + { + mParentMenu.Drawer(); + if (mInputGridOkay) + { + String InputGridChars = Chars; + int cell_width = 18 * CleanXfac; + int cell_height = 12 * CleanYfac; + int top_padding = cell_height / 2 - SmallFont.GetHeight() * CleanYfac / 2; + + // Darken the background behind the character grid. + screen.Dim(0, 0.8, 0, screen.GetHeight() - INPUTGRID_HEIGHT * cell_height, screen.GetWidth(), INPUTGRID_HEIGHT * cell_height); + + if (InputGridX >= 0 && InputGridY >= 0) + { + // Highlight the background behind the selected character. + screen.Dim(Color(255,248,220), 0.6, + 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) + { + int yy = y * cell_height - INPUTGRID_HEIGHT * cell_height + screen.GetHeight(); + for (int x = 0; x < INPUTGRID_WIDTH; ++x) + { + int xx = x * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen.GetWidth() / 2; + int ch = InputGridChars.CharCodeAt(y * INPUTGRID_WIDTH + x); + int width = SmallFont.GetCharWidth(ch); + + // The highlighted character is yellow; the rest are dark gray. + int colr = (x == InputGridX && y == InputGridY) ? Font.CR_YELLOW : Font.CR_DARKGRAY; + Color palcolor = (x == InputGridX && y == InputGridY) ? Color(160, 120, 0) : Color(120, 120, 120); + + if (ch > 32) + { + // Draw a normal character. + screen.DrawChar(SmallFont, colr, xx + cell_width/2 - width*CleanXfac/2, yy + top_padding, ch, DTA_CleanNoMove, true); + } + else if (ch == 32) + { + // Draw the space as a box outline. We also draw it 50% wider than it really is. + int x1 = xx + cell_width/2 - width * CleanXfac * 3 / 4; + int x2 = x1 + width * 3 * CleanXfac / 2; + int y1 = yy + top_padding; + int y2 = y1 + SmallFont.GetHeight() * CleanYfac; + screen.Clear(x1, y1, x2, y1+CleanYfac, palcolor); // top + screen.Clear(x1, y2, x2, y2+CleanYfac, palcolor); // bottom + screen.Clear(x1, y1+CleanYfac, x1+CleanXfac, y2, palcolor); // left + screen.Clear(x2-CleanXfac, y1+CleanYfac, x2, y2, palcolor); // right + } + else if (ch == 8 || ch == 0) + { + // Draw the backspace and end "characters". + String str = ch == 8 ? "BS" : "ED"; + screen.DrawText(SmallFont, colr, + xx + cell_width/2 - SmallFont.StringWidth(str)*CleanXfac/2, + yy + top_padding, str, DTA_CleanNoMove, true); + } + } + } + } + Super.Drawer(); + } + } \ No newline at end of file diff --git a/wadsrc/static/zscript/shared/movingcamera.txt b/wadsrc/static/zscript/shared/movingcamera.txt index 15fdd08368..c6af13b42f 100644 --- a/wadsrc/static/zscript/shared/movingcamera.txt +++ b/wadsrc/static/zscript/shared/movingcamera.txt @@ -311,7 +311,7 @@ class PathFollower : Actor while ( (spec = InterpolationSpecial(iterator.Next ())) ) { - A_CallSpecial(spec.special, spec.args[0], spec.args[1], spec.args[2], spec.args[3], spec.args[4]); + Level.ExecuteSpecial(spec.special, null, null, false, spec.args[0], spec.args[1], spec.args[2], spec.args[3], spec.args[4]); } } diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 9039b37b6b..a802107141 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -3,23 +3,20 @@ class PlayerPawn : Actor native native int crouchsprite; native int MaxHealth; + native int BonusHealth; native int MugShotMaxHealth; native int RunHealth; native int PlayerFlags; native Inventory InvFirst; // first inventory item displayed on inventory bar native Inventory InvSel; // selected inventory item - native meta String DisplayName; // Display name (used in menus, etc.) + native Name SoundClass; // Sound class + native Name Face; // Doom status bar face (when used) + native Name Portrait; + native Name Slot[10]; + native double HexenArmor[5]; + native uint8 ColorRangeStart; // Skin color range + native uint8 ColorRangeEnd; - native /*meta*/ Name SoundClass; // Sound class - native /*meta*/ Name Face; // Doom status bar face (when used) - native /*meta*/ Name Portrait; - native /*meta*/ Name Slot[10]; - native /*meta*/ Name InvulMode; - native /*meta*/ Name HealingRadiusType; - native /*meta*/ double HexenArmor[5]; - native /*meta*/ uint8 ColorRangeStart; // Skin color range - native /*meta*/ uint8 ColorRangeEnd; - // [GRB] Player class properties native double JumpZ; native double GruntSpeed; @@ -37,6 +34,13 @@ class PlayerPawn : Actor native native color DamageFade; // [CW] Fades for when you are being damaged. native double ViewBob; // [SP] ViewBob Multiplier native double FullHeight; + + meta Name HealingRadiusType; + meta Name InvulMode; + + Property prefix: Player; + Property HealRadiusType: HealingradiusType; + Property InvulnerabilityMode: InvulMode; Default { @@ -115,6 +119,36 @@ class PlayerPawn : Actor native } } + override String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack) + { + if (victim.player != player && victim.IsTeammate(self)) + { + victim = self; + return String.Format("$OB_FRIENDLY%c", random[Obituary](49, 53)); + } + else + { + if (mod == 'Telefrag') return "$OB_MPTELEFRAG"; + + String message; + if (inflictor != NULL) + { + message = inflictor.GetObituary(victim, inflictor, mod, playerattack); + } + if (message.Length() == 0 && playerattack && player.ReadyWeapon != NULL) + { + message = player.ReadyWeapon.GetObituary(victim, inflictor, mod, playerattack); + } + if (message.Length() == 0) + { + if (mod == 'BFGSplash') return "$OB_MPBFG_SPLASH"; + if (mod == 'Railgun') return "$OB_RAILGUN"; + message = Obituary; + } + return message; + } + } + // This is for SBARINFO. int, int GetEffectTicsForItem(class item) { @@ -133,9 +167,11 @@ class PlayerPawn : Actor native return -1, -1; } - native int GetMaxHealth(); + native int GetMaxHealth(bool withupgrades = false); native bool ResetAirSupply (bool playgasp = false); native void CheckWeaponSwitch(class item); + native static String GetPrintableDisplayName(Class cls); + } class PlayerChunk : PlayerPawn @@ -293,7 +329,6 @@ struct PlayerInfo native // this is what internally is known as player_t /* these are not doable yet ticcmd_t cmd; usercmd_t original_cmd; -userinfo_t userinfo; */ @@ -307,11 +342,19 @@ userinfo_t userinfo; native PSprite FindPSprite(int id); native void SetLogNumber (int text); native void SetLogText (String text); - native String GetUserName(); - native bool GetNeverSwitch(); native void DropWeapon(); native void BringUpWeapon(); + native String GetUserName(); + native Color GetColor(); + native int GetColorSet(); + native int GetPlayerClassNum(); + native int GetSkin(); + native bool GetNeverSwitch(); + native int GetGender(); + native int GetTeam(); + native float GetAutoaim(); + native void SetFOV(float fov); } struct PlayerClass native @@ -321,4 +364,27 @@ struct PlayerClass native native Array Skins; native bool CheckSkin(int skin); + native void EnumColorsets(out Array data); + native Name GetColorsetName(int setnum); +} + +struct PlayerSkin native +{ + native readonly String SkinName; + native readonly String Face; + native readonly uint8 gender; + native readonly uint8 range0start; + native readonly uint8 range0end; + native readonly bool othergame; + native readonly Vector2 Scale; + native readonly int sprite; + native readonly int crouchsprite; + native readonly int namespc; +}; + +struct Team native +{ + const NoTeam = 255; + const Max = 16; + native String mName; } diff --git a/wadsrc/static/zscript/shared/player_cheat.txt b/wadsrc/static/zscript/shared/player_cheat.txt index 2aaa01ba87..15bad043cf 100644 --- a/wadsrc/static/zscript/shared/player_cheat.txt +++ b/wadsrc/static/zscript/shared/player_cheat.txt @@ -76,7 +76,7 @@ extend class PlayerPawn } else { - player.health = health = GetMaxHealth(); + player.health = health = GetMaxHealth(true); } } diff --git a/wadsrc/static/zscript/statscreen/types.txt b/wadsrc/static/zscript/statscreen/types.txt new file mode 100644 index 0000000000..57080643e4 --- /dev/null +++ b/wadsrc/static/zscript/statscreen/types.txt @@ -0,0 +1,43 @@ + +// +// INTERMISSION +// Structure passed e.g. to WI_Start(wb) +// +struct WBPlayerStruct native +{ + // Player stats, kills, collected items etc. + native int skills; + native int sitems; + native int ssecret; + native int stime; + native int frags[MAXPLAYERS]; + native int fragcount; // [RH] Cumulative frags for this player +} + +struct WBStartStruct native +{ + native int finished_ep; + native int next_ep; + + native String current; // [RH] Name of map just finished + native String next; // next level, [RH] actual map name + + native TextureID LName0; + native TextureID LName1; + + native int maxkills; + native int maxitems; + native int maxsecret; + native int maxfrags; + + // the par time and sucktime + native int partime; // in tics + native int sucktime; // in minutes + + // total time for the entire current game + native int totaltime; + + // index of this player in game + native int pnum; +} + diff --git a/wadsrc/static/zscript/strife/coin.txt b/wadsrc/static/zscript/strife/coin.txt index b6a83ecf2a..6f7815cac2 100644 --- a/wadsrc/static/zscript/strife/coin.txt +++ b/wadsrc/static/zscript/strife/coin.txt @@ -79,7 +79,7 @@ class Coin : Inventory // //=========================================================================== - override Inventory CreateTossable () + override Inventory CreateTossable (int amt) { Coin tossed; @@ -87,17 +87,20 @@ class Coin : Inventory { return NULL; } - if (Amount >= 50) + if (amt == -1) amt = Amount >= 50? 50 : Amount >= 25? 25 : Amount >= 10? 10 : 1; + else if (amt > Amount) amt = Amount; + if (amt > 25) { - Amount -= 50; + Amount -= amt; tossed = Coin(Spawn("Gold50")); + tossed.Amount = amt; } - else if (Amount >= 25) + else if (amt > 10) { Amount -= 25; tossed = Coin(Spawn("Gold25")); } - else if (Amount >= 10) + else if (amt > 1) { Amount -= 10; tossed = Coin(Spawn("Gold10")); diff --git a/wadsrc/static/zscript/strife/programmer.txt b/wadsrc/static/zscript/strife/programmer.txt index 3fa34b6595..90cdd53997 100644 --- a/wadsrc/static/zscript/strife/programmer.txt +++ b/wadsrc/static/zscript/strife/programmer.txt @@ -95,7 +95,7 @@ class Programmer : Actor A_PlaySound("programmer/clank", CHAN_WEAPON); int damage = ((random[Programmer]() % 10) + 1) * 6; - int newdam = DamageMobj (self, self, damage, 'Melee'); + int newdam = target.DamageMobj (self, self, damage, 'Melee'); target.TraceBleed (newdam > 0 ? newdam : damage, self); } diff --git a/wadsrc_lights/static/filter/strife/gldefs.txt b/wadsrc_lights/static/filter/strife/gldefs.txt index 8a2bdcc5dc..7474486ba0 100644 --- a/wadsrc_lights/static/filter/strife/gldefs.txt +++ b/wadsrc_lights/static/filter/strife/gldefs.txt @@ -2651,16 +2651,6 @@ flickerlight LGNTAIL chance 0.8 } -object StrifeZap1 -{ - frame ZAP1A { light ARROWZAP1 } - frame ZAP1B { light ARROWZAP2 } - frame ZAP1C { light ARROWZAP3 } - frame ZAP1D { light ARROWZAP4 } - frame ZAP1E { light ARROWZAP5 } - frame ZAP1F { light ARROWZAP6 } -} - object SpectralLightningBase { frame ZAP1A { light ARROWZAP1 } @@ -2871,4 +2861,4 @@ object TeleportFog frame TFOGD { light TFOG4 } frame TFOGE { light TFOG5 } frame TFOGF { light TFOG6 } -} \ No newline at end of file +}