diff --git a/libraries/ZWidget/src/widgets/lineedit/lineedit.cpp b/libraries/ZWidget/src/widgets/lineedit/lineedit.cpp index 7faa25d90..23bd073c6 100644 --- a/libraries/ZWidget/src/widgets/lineedit/lineedit.cpp +++ b/libraries/ZWidget/src/widgets/lineedit/lineedit.cpp @@ -1,4 +1,4 @@ - +#include #include "widgets/lineedit/lineedit.h" #include "core/utf8reader.h" #include "core/colorf.h" diff --git a/src/am_map.cpp b/src/am_map.cpp index 1b610a52e..bf1aab1db 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -2886,7 +2886,8 @@ void DAutomap::drawPlayers () if (p->mo != nullptr) { - DVector3 pos = p->mo->PosRelative(MapPortalGroup); + DVector2 pos = p->mo->InterpolatedPosition(r_viewpoint.TicFrac).XY(); + pos += Level->Displacements.getOffset(Level->PointInSector(pos)->PortalGroup, MapPortalGroup); pt.x = pos.X; pt.y = pos.Y; diff --git a/src/common/filesystem/source/resourcefile.cpp b/src/common/filesystem/source/resourcefile.cpp index 8ba320f7f..359eb1e8c 100644 --- a/src/common/filesystem/source/resourcefile.cpp +++ b/src/common/filesystem/source/resourcefile.cpp @@ -34,6 +34,7 @@ ** */ +#include #include #include "resourcefile.h" #include "md5.hpp" @@ -46,7 +47,7 @@ #include namespace FileSys { - + // this is for restricting shared file readers to the main thread. thread_local bool mainThread; void SetMainThread() diff --git a/src/common/platform/win32/i_main.cpp b/src/common/platform/win32/i_main.cpp index c05e1aaba..12af5e563 100644 --- a/src/common/platform/win32/i_main.cpp +++ b/src/common/platform/win32/i_main.cpp @@ -127,7 +127,7 @@ int DoMain (HINSTANCE hInstance) Args->AppendArg(FString(wargv[i])); } - if (Args->CheckParm("-stdout")) + if (Args->CheckParm("-stdout") || Args->CheckParm("-norun")) { // As a GUI application, we don't normally get a console when we start. // If we were run from the shell and are on XP+, we can attach to its diff --git a/src/console/c_cmds.cpp b/src/console/c_cmds.cpp index 1ea9fe771..fc849eaa9 100644 --- a/src/console/c_cmds.cpp +++ b/src/console/c_cmds.cpp @@ -412,7 +412,7 @@ CCMD (changeskill) { if (!players[consoleplayer].mo || !usergame) { - Printf ("Use the skill command when not in a game.\n"); + Printf ("Cannot change skills while not in a game.\n"); return; } @@ -431,7 +431,8 @@ CCMD (changeskill) } else { - NextSkill = skill; + Net_WriteInt8(DEM_CHANGESKILL); + Net_WriteInt32(skill); Printf ("Skill %d will take effect on the next map.\n", skill); } } diff --git a/src/d_main.cpp b/src/d_main.cpp index df6770fe3..4c0f33749 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -715,6 +715,8 @@ CVAR (Flag, compat_railing, compatflags2, COMPATF2_RAILING); CVAR (Flag, compat_avoidhazard, compatflags2, COMPATF2_AVOID_HAZARDS); CVAR (Flag, compat_stayonlift, compatflags2, COMPATF2_STAYONLIFT); CVAR (Flag, compat_nombf21, compatflags2, COMPATF2_NOMBF21); +CVAR (Flag, compat_voodoozombies, compatflags2, COMPATF2_VOODOO_ZOMBIES); +CVAR (Flag, compat_fdteleport, compatflags2, COMPATF2_FDTELEPORT); CVAR(Bool, vid_activeinbackground, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) diff --git a/src/d_net.cpp b/src/d_net.cpp index 656b07e0b..a4d8dab28 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -2753,6 +2753,10 @@ void Net_DoCommand (int type, uint8_t **stream, int player) primaryLevel->localEventManager->NetCommand(netCmd); } break; + + case DEM_CHANGESKILL: + NextSkill = ReadInt32(stream); + break; default: I_Error ("Unknown net command: %d", type); @@ -2848,6 +2852,7 @@ void Net_SkipCommand (int type, uint8_t **stream) case DEM_INVUSE: case DEM_FOV: case DEM_MYFOV: + case DEM_CHANGESKILL: skip = 4; break; diff --git a/src/d_protocol.h b/src/d_protocol.h index 33fa10966..f726a6ef2 100644 --- a/src/d_protocol.h +++ b/src/d_protocol.h @@ -164,6 +164,7 @@ enum EDemoCommand DEM_SETINV, // 72 SetInventory DEM_ENDSCREENJOB, DEM_ZSC_CMD, // 74 String: Command, Word: Byte size of command + DEM_CHANGESKILL, // 75 Int: Skill }; // The following are implemented by cht_DoCheat in m_cheat.cpp diff --git a/src/doomdef.h b/src/doomdef.h index 98fa88d93..811b4fa5e 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -241,6 +241,7 @@ enum : unsigned int COMPATF2_STAYONLIFT = 1 << 13, // yet another MBF thing. COMPATF2_NOMBF21 = 1 << 14, // disable MBF21 features that may clash with certain maps COMPATF2_VOODOO_ZOMBIES = 1 << 15, // [RL0] allow playerinfo, playerpawn, and voodoo health to all be different, and skip killing the player's mobj if a voodoo doll dies to allow voodoo zombies + COMPATF2_FDTELEPORT = 1 << 16, // Emulate Final Doom's teleporter z glitch. }; diff --git a/src/g_level.cpp b/src/g_level.cpp index b7101a7b9..d3d76b2bf 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1478,7 +1478,7 @@ void FLevelLocals::DoLoadLevel(const FString &nextmapname, int position, bool au for (int i = 0; imo != nullptr) - P_PlayerStartStomp(Players[i]->mo); + P_PlayerStartStomp(Players[i]->mo, !deathmatch); } } diff --git a/src/launcher/playgamepage.cpp b/src/launcher/playgamepage.cpp index 55d777b73..411d2fb9f 100644 --- a/src/launcher/playgamepage.cpp +++ b/src/launcher/playgamepage.cpp @@ -98,7 +98,7 @@ void PlayGamePage::OnGeometryChanged() y = GetHeight() - 10.0; -#if defined(EXTRAARGS) +#if 0 // NYI: Additional Parameters double editHeight = 24.0; y -= editHeight; ParametersEdit->SetFrameGeometry(0.0, y, GetWidth(), editHeight); diff --git a/src/maploader/compatibility.cpp b/src/maploader/compatibility.cpp index 802ec3465..7caddc90a 100644 --- a/src/maploader/compatibility.cpp +++ b/src/maploader/compatibility.cpp @@ -173,6 +173,7 @@ static FCompatOption Options[] = { "scriptwait", COMPATF2_SCRIPTWAIT, SLOT_COMPAT2 }, { "nombf21", COMPATF2_NOMBF21, SLOT_COMPAT2 }, { "voodoozombies", COMPATF2_VOODOO_ZOMBIES, SLOT_COMPAT2 }, + { "fdteleport", COMPATF2_FDTELEPORT, SLOT_COMPAT2 }, { NULL, 0, 0 } }; diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 53cbfce62..945e3609d 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -506,6 +506,18 @@ void P_SetupLevel(FLevelLocals *Level, int position, bool newGame) } } } + else if (newGame) + { + for (i = 0; i < MAXPLAYERS; ++i) + { + // Didn't have a player spawn available so spawn it now. + if (Level->PlayerInGame(i) && Level->Players[i]->playerstate == PST_ENTER && Level->Players[i]->mo == nullptr) + { + FPlayerStart* mthing = Level->PickPlayerStart(i); + Level->SpawnPlayer(mthing, i, (Level->flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0); + } + } + } // [SP] move unfriendly players around // horribly hacky - yes, this needs rewritten. diff --git a/src/playsim/actor.h b/src/playsim/actor.h index ea0bd0538..b7fc48e6b 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -1362,14 +1362,18 @@ public: DVector3 Prev; DRotator PrevAngles; DAngle PrevFOV; - int PrevPortalGroup; TArray AttachedLights; TDeletingArray UserLights; + int PrevPortalGroup; // When was this actor spawned? int SpawnTime; uint32_t SpawnOrder; + int UnmorphTime; + int MorphFlags; + int PremorphProperties; + PClassActor* MorphExitFlash; // landing speed from a jump with normal gravity (squats the player's view) // (note: this is put into AActor instead of the PlayerPawn because non-players also use the value) double LandingSpeed; diff --git a/src/playsim/p_lnspec.cpp b/src/playsim/p_lnspec.cpp index 73666d0c3..00318dfbb 100644 --- a/src/playsim/p_lnspec.cpp +++ b/src/playsim/p_lnspec.cpp @@ -1120,7 +1120,7 @@ FUNC(LS_Teleport_NewMap) FUNC(LS_Teleport) // Teleport (tid, sectortag, bNoSourceFog) { - int flags = TELF_DESTFOG; + int flags = TELF_DESTFOG | TELF_FDCOMPAT; if (!arg2) { flags |= TELF_SOURCEFOG; diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index 09d4dfc9c..5c274eeeb 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -393,7 +393,13 @@ void AActor::Serialize(FSerializer &arc) A("userlights", UserLights) A("WorldOffset", WorldOffset) ("modelData", modelData) - A("LandingSpeed", LandingSpeed); + A("LandingSpeed", LandingSpeed) + + ("unmorphtime", UnmorphTime) + ("morphflags", MorphFlags) + ("premorphproperties", PremorphProperties) + ("morphexitflash", MorphExitFlash); + SerializeTerrain(arc, "floorterrain", floorterrain, &def->floorterrain); SerializeArgs(arc, "args", args, def->args, special); @@ -3656,10 +3662,6 @@ void AActor::SetViewAngle(DAngle ang, int fflags) double AActor::GetFOV(double ticFrac) { - // [B] Disable interpolation when playing online, otherwise it gets vomit inducing - if (netgame) - return player ? player->FOV : CameraFOV; - double fov; if (player) { diff --git a/src/playsim/p_spec.h b/src/playsim/p_spec.h index 5d602eb1d..cc7d990a9 100644 --- a/src/playsim/p_spec.h +++ b/src/playsim/p_spec.h @@ -141,6 +141,7 @@ enum TELF_KEEPHEIGHT = 16, TELF_ROTATEBOOM = 32, TELF_ROTATEBOOMINVERSE = 64, + TELF_FDCOMPAT = 128, }; //Spawns teleport fog. Pass the actor to pluck TeleFogFromType and TeleFogToType. 'from' determines if this is the fog to spawn at the old position (true) or new (false). diff --git a/src/playsim/p_teleport.cpp b/src/playsim/p_teleport.cpp index 1643b1b76..a69d3654c 100644 --- a/src/playsim/p_teleport.cpp +++ b/src/playsim/p_teleport.cpp @@ -128,7 +128,15 @@ bool P_Teleport (AActor *thing, DVector3 pos, DAngle angle, int flags) } else { - pos.Z = floorheight; + if (!(thing->Level->i_compatflags2 & COMPATF2_FDTELEPORT) || !(flags & TELF_FDCOMPAT) || floorheight > thing->Z()) + { + pos.Z = floorheight; + } + else + { + pos.Z = thing->Z(); + } + if (!(flags & TELF_KEEPORIENTATION)) { resetpitch = false; @@ -145,7 +153,16 @@ bool P_Teleport (AActor *thing, DVector3 pos, DAngle angle, int flags) } else { - pos.Z = floorheight; + // emulation of Final Doom's teleport glitch. + // For walking monsters we still have to force them to the ground because the handling of off-ground monsters is different from vanilla. + if (!(thing->Level->i_compatflags2 & COMPATF2_FDTELEPORT) || !(flags & TELF_FDCOMPAT) || !(thing->flags & MF_NOGRAVITY) || floorheight > pos.Z) + { + pos.Z = floorheight; + } + else + { + pos.Z = thing->Z(); + } } } // [MK] notify thing of incoming teleport, check for an early cancel @@ -245,7 +262,7 @@ DEFINE_ACTION_FUNCTION(AActor, Teleport) PARAM_FLOAT(z); PARAM_ANGLE(an); PARAM_INT(flags); - ACTION_RETURN_BOOL(P_Teleport(self, DVector3(x, y, z), an, flags)); + ACTION_RETURN_BOOL(P_Teleport(self, DVector3(x, y, z), an, flags & ~TELF_FDCOMPAT)); } //----------------------------------------------------------------------------- diff --git a/src/playsim/p_user.cpp b/src/playsim/p_user.cpp index 9751fc268..d00f7571b 100644 --- a/src/playsim/p_user.cpp +++ b/src/playsim/p_user.cpp @@ -103,16 +103,26 @@ CVAR(Bool, sv_singleplayerrespawn, false, CVAR_SERVERINFO | CVAR_CHEAT) // Variables for prediction CVAR (Bool, cl_noprediction, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Bool, cl_predict_specials, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +// Deprecated +CVAR(Float, cl_predict_lerpscale, 0.05f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Float, cl_predict_lerpthreshold, 2.00f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -CUSTOM_CVAR(Float, cl_predict_lerpscale, 0.05f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CUSTOM_CVAR(Float, cl_rubberband_scale, 0.3f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { - P_PredictionLerpReset(); + if (self < 0.0f) + self = 0.0f; + else if (self > 1.0f) + self = 1.0f; } -CUSTOM_CVAR(Float, cl_predict_lerpthreshold, 2.00f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CUSTOM_CVAR(Float, cl_rubberband_threshold, 20.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (self < 0.1f) + self = 0.1f; +} +CUSTOM_CVAR(Float, cl_rubberband_minmove, 20.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { if (self < 0.1f) self = 0.1f; - P_PredictionLerpReset(); } ColorSetList ColorSets; @@ -125,13 +135,9 @@ CUSTOM_CVAR(Float, fov, 90.f, CVAR_ARCHIVE | CVAR_USERINFO | CVAR_NOINITCALL) p->SetFOV(fov); } -struct PredictPos -{ - int gametic; - DVector3 pos; - DRotator angles; -} static PredictionLerpFrom, PredictionLerpResult, PredictionLast; -static int PredictionLerptics; +static DVector3 LastPredictedPosition; +static int LastPredictedPortalGroup; +static int LastPredictedTic; static player_t PredictionPlayerBackup; static AActor *PredictionActor; @@ -1290,23 +1296,25 @@ void P_PlayerThink (player_t *player) void P_PredictionLerpReset() { - PredictionLerptics = PredictionLast.gametic = PredictionLerpFrom.gametic = PredictionLerpResult.gametic = 0; + LastPredictedPosition = DVector3{}; + LastPredictedPortalGroup = 0; + LastPredictedTic = -1; } -bool P_LerpCalculate(AActor *pmo, PredictPos from, PredictPos to, PredictPos &result, float scale) +void P_LerpCalculate(AActor* pmo, const DVector3& from, DVector3 &result, float scale, float threshold, float minMove) { - DVector3 vecFrom = from.pos; - DVector3 vecTo = to.pos; - DVector3 vecResult; - vecResult = vecTo - vecFrom; - vecResult *= scale; - vecResult = vecResult + vecFrom; - DVector3 delta = vecResult - vecTo; + DVector3 diff = pmo->Pos() - from; + diff.XY() += pmo->Level->Displacements.getOffset(pmo->Sector->PortalGroup, pmo->Level->PointInSector(from.XY())->PortalGroup); + double dist = diff.Length(); + if (dist <= max(threshold, minMove)) + { + result = pmo->Pos(); + return; + } - result.pos = pmo->Vec3Offset(vecResult - to.pos); - - // As a fail safe, assume extrapolation is the threshold. - return (delta.LengthSquared() > cl_predict_lerpthreshold && scale <= 1.00f); + diff /= dist; + diff *= min(dist * (1.0f - scale), dist - minMove); + result = pmo->Vec3Offset(-diff.X, -diff.Y, -diff.Z); } template @@ -1437,7 +1445,6 @@ void P_PredictPlayer (player_t *player) act->flags &= ~MF_PICKUP; act->flags2 &= ~MF2_PUSHWALL; - act->renderflags &= ~RF_NOINTERPOLATEVIEW; player->cheats |= CF_PREDICTING; BackupNodeList(act, act->touching_sectorlist, §or_t::touching_thinglist, PredictionTouchingSectors_sprev_Backup, PredictionTouchingSectorsBackup); @@ -1473,64 +1480,66 @@ void P_PredictPlayer (player_t *player) } act->BlockNode = NULL; - // Values too small to be usable for lerping can be considered "off". - bool CanLerp = (!(cl_predict_lerpscale < 0.01f) && (ticdup == 1)), DoLerp = false, NoInterpolateOld = R_GetViewInterpolationStatus(); + // This essentially acts like a mini P_Ticker where only the stuff relevant to the client is actually + // called. Call order is preserved. + bool rubberband = false; + DVector3 rubberbandPos = {}; + const bool canRubberband = LastPredictedTic >= 0 && cl_rubberband_scale > 0.0f && cl_rubberband_scale < 1.0f; + const double rubberbandThreshold = max(cl_rubberband_minmove, cl_rubberband_threshold); for (int i = gametic; i < maxtic; ++i) { - if (!NoInterpolateOld) - R_RebuildViewInterpolation(player); + // Make sure any portal paths have been cleared from the previous movement. + R_ClearInterpolationPath(); + r_NoInterpolate = false; + // Because we're always predicting, this will get set by teleporters and then can never unset itself in the renderer properly. + player->mo->renderflags &= ~RF_NOINTERPOLATEVIEW; + + // Got snagged on something. Start correcting towards the player's final predicted position. We're + // being intentionally generous here by not really caring how the player got to that position, only + // that they ended up in the same spot on the same tick. + if (canRubberband && LastPredictedTic == i) + { + DVector3 diff = player->mo->Pos() - LastPredictedPosition; + diff += player->mo->Level->Displacements.getOffset(player->mo->Sector->PortalGroup, LastPredictedPortalGroup); + double dist = diff.LengthSquared(); + if (dist >= EQUAL_EPSILON * EQUAL_EPSILON && dist > rubberbandThreshold * rubberbandThreshold) + { + rubberband = true; + rubberbandPos = player->mo->Pos(); + } + } player->cmd = localcmds[i % LOCALCMDTICS]; + player->mo->ClearInterpolation(); + player->mo->ClearFOVInterpolation(); P_PlayerThink (player); player->mo->Tick (); - - if (CanLerp && PredictionLast.gametic > 0 && i == PredictionLast.gametic && !NoInterpolateOld) - { - // Z is not compared as lifts will alter this with no apparent change - // Make lerping less picky by only testing whole units - DoLerp = (int)PredictionLast.pos.X != (int)player->mo->X() || (int)PredictionLast.pos.Y != (int)player->mo->Y(); - - // Aditional Debug information - if (developer >= DMSG_NOTIFY && DoLerp) - { - DPrintf(DMSG_NOTIFY, "Lerp! Ltic (%d) && Ptic (%d) | Lx (%f) && Px (%f) | Ly (%f) && Py (%f)\n", - PredictionLast.gametic, i, - (PredictionLast.pos.X), (player->mo->X()), - (PredictionLast.pos.Y), (player->mo->Y())); - } - } } - if (CanLerp) + if (rubberband) { - if (NoInterpolateOld) - P_PredictionLerpReset(); + R_ClearInterpolationPath(); + player->mo->renderflags &= ~RF_NOINTERPOLATEVIEW; - else if (DoLerp) - { - // If lerping is already in effect, use the previous camera postion so the view doesn't suddenly snap - PredictionLerpFrom = (PredictionLerptics == 0) ? PredictionLast : PredictionLerpResult; - PredictionLerptics = 1; - } + DPrintf(DMSG_NOTIFY, "Prediction mismatch at (%.3f, %.3f, %.3f)\nExpected: (%.3f, %.3f, %.3f)\nCorrecting to (%.3f, %.3f, %.3f)\n", + LastPredictedPosition.X, LastPredictedPosition.Y, LastPredictedPosition.Z, + rubberbandPos.X, rubberbandPos.Y, rubberbandPos.Z, + player->mo->X(), player->mo->Y(), player->mo->Z()); - PredictionLast.gametic = maxtic - 1; - PredictionLast.pos = player->mo->Pos(); - //PredictionLast.portalgroup = player->mo->Sector->PortalGroup; - - if (PredictionLerptics > 0) - { - if (PredictionLerpFrom.gametic > 0 && - P_LerpCalculate(player->mo, PredictionLerpFrom, PredictionLast, PredictionLerpResult, (float)PredictionLerptics * cl_predict_lerpscale)) - { - PredictionLerptics++; - player->mo->SetXYZ(PredictionLerpResult.pos); - } - else - { - PredictionLerptics = 0; - } - } + DVector3 snapPos = {}; + P_LerpCalculate(player->mo, LastPredictedPosition, snapPos, cl_rubberband_scale, cl_rubberband_threshold, cl_rubberband_minmove); + player->mo->PrevPortalGroup = LastPredictedPortalGroup; + player->mo->Prev = LastPredictedPosition; + const double zOfs = player->viewz - player->mo->Z(); + player->mo->SetXYZ(snapPos); + player->viewz = snapPos.Z + zOfs; } + + // This is intentionally done after rubberbanding starts since it'll automatically smooth itself towards + // the right spot until it reaches it. + LastPredictedTic = maxtic; + LastPredictedPosition = player->mo->Pos(); + LastPredictedPortalGroup = player->mo->Level->PointInSector(LastPredictedPosition)->PortalGroup; } void P_UnPredictPlayer () diff --git a/src/rendering/hwrenderer/scene/hw_sky.cpp b/src/rendering/hwrenderer/scene/hw_sky.cpp index ac2486ad0..2a65a9f01 100644 --- a/src/rendering/hwrenderer/scene/hw_sky.cpp +++ b/src/rendering/hwrenderer/scene/hw_sky.cpp @@ -137,9 +137,9 @@ void HWWall::SkyPlane(HWWallDispatcher *di, FRenderState& state, sector_t *secto // Either a regular sky or a skybox with skyboxes disabled if ((sportal == nullptr && sector->GetTexture(plane) == skyflatnum) || (gl_noskyboxes && sportal != nullptr && sportal->mType == PORTS_SKYVIEWPOINT)) { + HWSkyInfo skyinfo; if (di->di) { - HWSkyInfo skyinfo; skyinfo.init(di->di, sector, plane, sector->skytransfer, Colormap.FadeColor); sky = &skyinfo; } diff --git a/src/rendering/hwrenderer/scene/hw_sprites.cpp b/src/rendering/hwrenderer/scene/hw_sprites.cpp index 9d67e4df9..46f2d1ecf 100644 --- a/src/rendering/hwrenderer/scene/hw_sprites.cpp +++ b/src/rendering/hwrenderer/scene/hw_sprites.cpp @@ -406,6 +406,12 @@ void HandleSpriteOffsets(Matrix3x4 *mat, const FRotator *HW, FVector2 *offset, b bool HWSprite::CalculateVertices(HWDrawInfo* di, FVector3* v, DVector3* vp) { + float pixelstretch = 1.2; + if (actor && actor->Level) + pixelstretch = actor->Level->pixelstretch; + else if (particle && particle->subsector && particle->subsector->sector && particle->subsector->sector->Level) + pixelstretch = particle->subsector->sector->Level->pixelstretch; + FVector3 center = FVector3((x1 + x2) * 0.5, (y1 + y2) * 0.5, (z1 + z2) * 0.5); const auto& HWAngles = di->Viewpoint.HWAngles; Matrix3x4 mat; @@ -472,12 +478,6 @@ bool HWSprite::CalculateVertices(HWDrawInfo* di, FVector3* v, DVector3* vp) // [Nash] check for special sprite drawing modes if (drawWithXYBillboard || isWallSprite) { - float pixelstretch = 1.2; - if (actor && actor->Level) - pixelstretch = actor->Level->pixelstretch; - else if (particle && particle->subsector && particle->subsector->sector && particle->subsector->sector->Level) - pixelstretch = particle->subsector->sector->Level->pixelstretch; - mat.MakeIdentity(); mat.Translate(center.X, center.Z, center.Y); // move to sprite center mat.Scale(1.0, 1.0/pixelstretch, 1.0); // unstretch sprite by level aspect ratio @@ -560,9 +560,11 @@ bool HWSprite::CalculateVertices(HWDrawInfo* di, FVector3* v, DVector3* vp) float rollDegrees = Angles.Roll.Degrees(); mat.Translate(center.X, center.Z, center.Y); + mat.Scale(1.0, 1.0/pixelstretch, 1.0); // unstretch sprite by level aspect ratio if (useOffsets) mat.Translate(xx, zz, yy); mat.Rotate(cos(angleRad), 0, sin(angleRad), rollDegrees); if (useOffsets) mat.Translate(-xx, -zz, -yy); + mat.Scale(1.0, pixelstretch, 1.0); // stretch sprite by level aspect ratio mat.Translate(-center.X, -center.Z, -center.Y); } diff --git a/src/scripting/vmthunks_actors.cpp b/src/scripting/vmthunks_actors.cpp index 7094212e0..acf253807 100644 --- a/src/scripting/vmthunks_actors.cpp +++ b/src/scripting/vmthunks_actors.cpp @@ -2126,6 +2126,10 @@ DEFINE_FIELD(AActor, ShadowAimFactor) DEFINE_FIELD(AActor, ShadowPenaltyFactor) DEFINE_FIELD(AActor, AutomapOffsets) DEFINE_FIELD(AActor, LandingSpeed) +DEFINE_FIELD(AActor, UnmorphTime) +DEFINE_FIELD(AActor, MorphFlags) +DEFINE_FIELD(AActor, PremorphProperties) +DEFINE_FIELD(AActor, MorphExitFlash) DEFINE_FIELD_X(FCheckPosition, FCheckPosition, thing); DEFINE_FIELD_X(FCheckPosition, FCheckPosition, pos); diff --git a/wadsrc/static/compatibility.txt b/wadsrc/static/compatibility.txt index 2440ef55f..f8c3fe453 100644 --- a/wadsrc/static/compatibility.txt +++ b/wadsrc/static/compatibility.txt @@ -332,3 +332,8 @@ F1EB6927F53047F219A54997DAD9DC81 // mm2.wad map20 rebuildnodes trace } + +BA2247ED1465C8107192AC4215B2A5F3 // saturnia.wad map10 +{ + fdteleport +} diff --git a/wadsrc/static/language.csv b/wadsrc/static/language.csv index 080a605b2..b588c7c66 100644 --- a/wadsrc/static/language.csv +++ b/wadsrc/static/language.csv @@ -3384,8 +3384,11 @@ Disallow override of camera facing mode,GLPREFMNU_SPRBILLFACECAMERAFORCE,,,,,Til Swap health and armor,ALTHUDMNU_SWAPHEALTHARMOR,,,,Prohodit zdraví a brnění,Byt om på helbred og rustning,Anzeige für Gesundheit und Panzerung tauschen,,Permuti sanon kaj kirason,,,,Échanger la santé et l'armure,,Scambio di salute e armatura,体力とアーマーをスワップ,,Verwissel gezondheid en pantser,Bytt helse og rustning,Zamień zdrowie i pancerz,Trocar saúde e armadura,,,Поменять отображение здоровья и брони местами,,Byt hälsa och rustning,Sağlık ve zırhı değiştir, Use classic HUD scaling,SCALEMNU_HUD_OLDSCALE,hud_oldscale,,,Pro HUD použít klasické škálování,Brug klassisk HUD-skalering,Klassische HUD-Skalierung,,Uzi klasikan skaladon de HUD,,,,Utiliser l'échelle classique du HUD,,Utilizzare la scalatura classica dell'HUD,旧式HUDスケーリングを使う,,Gebruik klassieke HUD-schaling,Bruk klassisk HUD-skalering,,Usar escala clássica do HUD,,,Классическое масштабирование интерфейса,,Använd klassisk HUD-skalning,Klasik HUD ölçeklendirmesini kullanın, HUD scale factor,SCALEMNU_HUD_SCALEFACTOR,hud_scalefactor,,,Faktor HUD škálování,HUD-skaleringsfaktor,HUD-Skalierungsfaktor,,Skalfaktoro de HUD,,,,Facteur d'échelle du HUD,,Fattore di scala dell'HUD,HUDスケール倍率,,HUD-schaalfactor,HUD-skaleringsfaktor,,Fator de escala do HUD,,,Значение масштабирования интерфейса,,Skalningsfaktor för HUD,HUD ölçek faktörü, -Spawn co-op only things in multiplayer,GMPLYMNU_COOPTHINGS,Any Actor that only appears in co-op is disabled,,,Co-op věci pouze v multiplayeru,,,,Ekaperigi nurajn kooperajn aĵojn en plurludanta reĝimo,,,,,,,マルチで協力モード限定thingsを出す,,,,,,,,Появление вещей из совместной игры в сетевой игре,,,, -Spawn co-op only items in multiplayer,GMPLYMNU_COOPITEMS,Items that only appear in co-op are disabled,,,Co-op předměty pouze v multiplayeru,,,,Ekaperigi nurajn kooperajn objektojn en plurludanta reĝimo,,,,,,,マルチで協力モード限定アイテムを出す,,,,,,,,Появление предметов из совместной игры в сетевой игре,,,, -Players pick up their own copy of items in multiplayer,GMPLYMNU_LOCALITEMS,Items are picked up client-side rather than fully taken by the client who picked it up,,,V multiplayeru hráči sbírají své vlastní kopie předmětů,,,,Ludantoj prenas sian propran kopion de objektoj en plurludanta reĝimo,,,,,,,マルチでプレイヤー達は複製アイテムを拾う,,,,,,,,Игроки подбирают собственные копии предметов в сетевой игре,,,, -Disable client-side pick ups on dropped items,GMPLYMNU_NOLOCALDROP,Drops from Actors aren't picked up locally,,,Zakázat sbírání odhozených předmětů na straně klientů,,,,Malvalidigi klientflankajn prenadojn de faligitaj objektoj,,,,,,,クライアント側で落としたアイテムを拾わせない,,,,,,,,Отключить подбирание выпавших предметов на стороне клиента,,,, -Restart on Death,MISCMNU_RESTARTONDEATH,Disables autoloading save on death,,,,,,,,,,,,,,,,,,,,,,Перезапуск после смерти,,,, \ No newline at end of file +Spawn co-op only things in multiplayer,GMPLYMNU_COOPTHINGS,Any Actor that only appears in co-op is disabled,,,Co-op věci pouze v multiplayeru,Skab kun co-op-objekter i multiplayer,Erzeuge Nur-Co-op Dinge in Multiplayer,,Ekaperigi nurajn kooperajn aĵojn en plurludanta reĝimo,,,,Faire apparaitre les acteurs Co-Op Seulement en Multijoueur,,Creare oggetti solo per la cooperativa in multigiocatore,マルチで協力モード限定thingsを出す,,Alleen co-op-voorwerpen maken in multiplayer,Opprett co-op-objekter i flerspillermodus,,,,,Появление вещей из совместной игры в сетевой игре,,Skapa co-op-objekt i flerspelarläget,Çok oyunculu modda ortak nesneler oluşturun, +Spawn co-op only pickup items in multiplayer,GMPLYMNU_COOPITEMS,Items that only appear in co-op are disabled,,,Co-op předměty pouze v multiplayeru,Skab kun co-op-genstande i multiplayer,Erzeuge Nur-Co-op Gegenstände in Multiplayer,,Ekaperigi nurajn kooperajn objektojn en plurludanta reĝimo,,,,Faire apparaitre les objets Co-Op Seulement en Multijoueur,,Creare pickup solo per la cooperativa in multigiocatore,マルチで協力モード限定アイテムを出す,,Alleen co-op pickups maken in multiplayer,Opprett co-op-pickuper i flerspillermodus,,,,,Появление предметов из совместной игры в сетевой игре,,Skapa co-op pickups i flerspelarläget,Çok oyunculu modda ortak pikaplar oluşturun, +Players pick up their own copy of items in multiplayer,GMPLYMNU_LOCALITEMS,Items are picked up client-side rather than fully taken by the client who picked it up,,,V multiplayeru hráči sbírají své vlastní kopie předmětů,Spillere samler deres egen kopi af genstande op i multiplayer,Spieler nehmen ihre eigene Kopie von Gegenständen im Mehrspielermodus auf,,Ludantoj prenas sian propran kopion de objektoj en plurludanta reĝimo,,,,Chaque joueur obtient sa copie de chaque objet en Multijoueur,,I giocatori raccolgono la propria copia di oggetti in multigiocatore,マルチでプレイヤー達は複製アイテムを拾う,,Spelers pikken hun eigen exemplaar van voorwerpen op in multiplayer,Spillere plukker opp sin egen kopi av gjenstander i flerspiller,,,,,Игроки подбирают собственные копии предметов в сетевой игре,,Spelare plockar upp sin egen kopia av föremål i flerspelarläget,Oyuncular çok oyunculu modda eşyaların kendi kopyalarını alırlar, +Disable client-side pick ups on dropped items,GMPLYMNU_NOLOCALDROP,Drops from Actors aren't picked up locally,,,Zakázat sbírání odhozených předmětů na straně klientů,Deaktiver opsamling på klientsiden af tabte genstande,Client-seitiges Aufnehmen von fallengelassenen Gegenständen deaktivieren,,Malvalidigi klientflankajn prenadojn de faligitaj objektoj,,,,Désactiver le ramassage des objets lâchés en clientside,,Disabilita il ritiro degli oggetti caduti dal lato client,クライアント側で落としたアイテムを拾わせない,,Client-side pick ups van gedropte items uitschakelen,Deaktiver opphenting på klientsiden av gjenstander som droppes,,,,,Отключить подбирание выпавших предметов на стороне клиента,,Inaktivera upphämtning av tappade föremål på klientsidan,Düşen öğeleri istemci tarafında almayı devre dışı bırakma, +Restart level on Death,MISCMNU_RESTARTONDEATH,Disables autoloading save on death,,,Restart po umření,Genstart niveau ved død,Level bei Tod neu starten,,,,,,Recharger après mort,,Riavvio del livello in caso di morte,,,Level herstarten bij dood,Start nivået på nytt ved død,,,,,Перезапуск уровня после смерти,,Starta om nivån vid död,Ölüm halinde seviyeyi yeniden başlat, +Pistol Start,GMPLYMNU_PISTOLSTART,Resets inventory on every map,,,Začít jen s pistolí,Start med pistol,Pistolenstart,,,,,,Démarrage du pistolet,,Avvio pistola,,,Start met pistool,Start med pistol,,,,,Пистолет в начале,,Starta med pistol,Tabanca ile başlayın, +Allow creation of zombie players,CMPTMNU_VOODOOZOMBIES,,,,,,Erlaube Zombieplayer,,,,,,,,,,,,,,,,,,,,, +ignore floor z when teleporting,CMPTMNU_FDTELEPORT,,,,,,Ignoriere Fußbodenhöhe bem Teleportieren,,,,,,,,,,,,,,,,,,,,, \ No newline at end of file diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 270aff20c..8499ed42a 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1886,6 +1886,7 @@ OptionMenu "CompatActorMenu" protected Option "$CMPTMNU_INVISIBILITY", "compat_INVISIBILITY", "YesNo" Option "$CMPTMNU_MINOTAUR", "compat_MINOTAUR", "YesNo" Option "$CMPTMNU_NOTOSSDROPS", "compat_NOTOSSDROPS", "YesNo" + Option "$CMPTMNU_VOODOOZOMBIES", "compat_voodoozombies", "YesNo" Class "CompatibilityMenu" } @@ -1912,6 +1913,7 @@ OptionMenu "CompatMapMenu" protected Option "$CMPTMNU_POINTONLINE", "compat_pointonline", "YesNo" Option "$CMPTMNU_MULTIEXIT", "compat_multiexit", "YesNo" Option "$CMPTMNU_TELEPORT", "compat_teleport", "YesNo" + Option "$CMPTMNU_FDTELEPORT", "compat_fdteleport", "YesNo" Option "$CMPTMNU_PUSHWINDOW", "compat_pushwindow", "YesNo" Option "$CMPTMNU_CHECKSWITCHRANGE", "compat_checkswitchrange", "YesNo" Option "$CMPTMNU_RAILINGHACK", "compat_railing", "YesNo" diff --git a/wadsrc/static/zscript/actors/morph.zs b/wadsrc/static/zscript/actors/morph.zs index b9fdbb101..9cd21287a 100644 --- a/wadsrc/static/zscript/actors/morph.zs +++ b/wadsrc/static/zscript/actors/morph.zs @@ -35,10 +35,10 @@ extend class Actor MPROP_INVIS = 1 << 6, } - int UnmorphTime; - EMorphFlags MorphFlags; - class MorphExitFlash; - EPremorphProperty PremorphProperties; + native int UnmorphTime; + native int MorphFlags; + native class MorphExitFlash; + native int PremorphProperties; // Players still track these separately for legacy reasons. void SetMorphStyle(EMorphFlags flags)