diff --git a/src/common/engine/namedef.h b/src/common/engine/namedef.h index 9e20b35c23..538242e548 100644 --- a/src/common/engine/namedef.h +++ b/src/common/engine/namedef.h @@ -1039,6 +1039,9 @@ xx(AttackZOffset) xx(SpawnMask) xx(ScoreIcon) xx(ViewHeight) +xx(ViewAngle) +xx(ViewPitch) +xx(ViewRoll) xx(FallingScreamMinSpeed) xx(FallingScreamMaxSpeed) xx(GruntSpeed) diff --git a/src/playsim/actor.h b/src/playsim/actor.h index 07fd9720ea..f5d3d14bbe 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -412,6 +412,7 @@ enum ActorFlag8 MF8_RETARGETAFTERSLAM = 0x00000080, // Forces jumping to the idle state after slamming into something MF8_RECREATELIGHTS = 0x00000100, // Internal flag that signifies that the light attachments need to be recreated at the MF8_STOPRAILS = 0x00000200, // [MC] Prevent rails from going further if an actor has this flag. + MF8_ABSVIEWANGLES = 0x00000400, // [MC] By default view angle/pitch/roll is an offset. This will make it absolute instead. }; // --- mobj.renderflags --- @@ -845,9 +846,13 @@ public: } // 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); + DAngle ClampPitch(DAngle p); + void SetPitch(DAngle p, int fflags); + void SetAngle(DAngle ang, int fflags); + void SetRoll(DAngle roll, int fflags); + void SetViewPitch(DAngle p, int fflags); + void SetViewAngle(DAngle ang, int fflags); + void SetViewRoll(DAngle roll, int fflags); PClassActor *GetBloodType(int type = 0) const; @@ -961,6 +966,7 @@ public: DAngle SpriteAngle; DAngle SpriteRotation; DRotator Angles; + DRotator ViewAngles; // Offsets for cameras 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. diff --git a/src/playsim/d_player.h b/src/playsim/d_player.h index 20f2d2f3b9..102b2e01a8 100644 --- a/src/playsim/d_player.h +++ b/src/playsim/d_player.h @@ -419,7 +419,6 @@ public: return mo->FloatVar(NAME_ViewHeight); } - void Uncrouch() { if (crouchfactor != 1) diff --git a/src/playsim/p_actionfunctions.cpp b/src/playsim/p_actionfunctions.cpp index 992787f718..dc1db61e26 100644 --- a/src/playsim/p_actionfunctions.cpp +++ b/src/playsim/p_actionfunctions.cpp @@ -2793,12 +2793,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_MonsterRefire) // Set actor's angle (in degrees). // //=========================================================================== -enum -{ - SPF_FORCECLAMP = 1, // players always clamp - SPF_INTERPOLATE = 2, -}; - DEFINE_ACTION_FUNCTION(AActor, A_SetAngle) { @@ -2810,7 +2804,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_SetAngle) AActor *ref = COPY_AAPTR(self, ptr); if (ref != NULL) { - ref->SetAngle(angle, !!(flags & SPF_INTERPOLATE)); + ref->SetAngle(angle, flags); } return 0; } @@ -2834,7 +2828,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_SetPitch) if (ref != NULL) { - ref->SetPitch(pitch, !!(flags & SPF_INTERPOLATE), !!(flags & SPF_FORCECLAMP)); + ref->SetPitch(pitch, flags); } return 0; } @@ -2857,11 +2851,98 @@ DEFINE_ACTION_FUNCTION(AActor, A_SetRoll) if (ref != NULL) { - ref->SetRoll(roll, !!(flags & SPF_INTERPOLATE)); + ref->SetRoll(roll, flags); } return 0; } +//=========================================================================== +// +// A_SetViewAngle +// +// Set actor's viewangle (in degrees). +// +//=========================================================================== + +static void SetViewAngleNative(AActor* self, double angle, int flags, int ptr) +{ + AActor *ref = COPY_AAPTR(self, ptr); + if (ref != nullptr) + { + ref->SetViewAngle(angle, flags); + } +} + +DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_SetViewAngle, SetViewAngleNative) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_FLOAT(angle); + PARAM_INT(flags); + PARAM_INT(ptr); + + SetViewAngleNative(self, angle, flags, ptr); + + return 0; +} + +//=========================================================================== +// +// A_SetViewPitch +// +// Set actor's viewpitch (in degrees). +// +//=========================================================================== + +static void SetViewPitchNative(AActor* self, double pitch, int flags, int ptr) +{ + AActor *ref = COPY_AAPTR(self, ptr); + if (ref != nullptr) + { + ref->SetViewPitch(pitch, flags); + } +} + +DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_SetViewPitch, SetViewPitchNative) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_FLOAT(pitch); + PARAM_INT(flags); + PARAM_INT(ptr); + + SetViewPitchNative(self, pitch, flags, ptr); + + return 0; +} + +//=========================================================================== +// +// [MC] A_SetViewRoll +// +// Set actor's viewroll (in degrees). +// +//=========================================================================== + +static void SetViewRollNative(AActor* self, double roll, int flags, int ptr) +{ + AActor *ref = COPY_AAPTR(self, ptr); + if (ref != nullptr) + { + ref->SetViewRoll(roll, flags); + } +} + +DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_SetViewRoll, SetViewRollNative) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_FLOAT(roll); + PARAM_INT(flags); + PARAM_INT(ptr); + + SetViewRollNative(self, roll, flags, ptr); + + return 0; +} + //=========================================================================== // // A_SetUserVar diff --git a/src/playsim/p_local.h b/src/playsim/p_local.h index 2a031b6895..20e941213d 100644 --- a/src/playsim/p_local.h +++ b/src/playsim/p_local.h @@ -217,6 +217,12 @@ enum WARPF WARPF_COPYPITCH = 0x8000, }; +enum SPF +{ + SPF_FORCECLAMP = 1, // players always clamp + SPF_INTERPOLATE = 2, +}; + enum PCM { PCM_DROPOFF = 1, diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index 455866084f..0783d4e90f 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -362,6 +362,7 @@ void AActor::Serialize(FSerializer &arc) A("renderhidden", RenderHidden) A("renderrequired", RenderRequired) A("friendlyseeblocks", friendlyseeblocks) + A("viewangles", ViewAngles) A("spawntime", SpawnTime) A("spawnorder", SpawnOrder) A("friction", Friction) @@ -3352,52 +3353,106 @@ DEFINE_ACTION_FUNCTION(AActor, SetShade) return 0; } -void AActor::SetPitch(DAngle p, bool interpolate, bool forceclamp) -{ - if (player != NULL || forceclamp) - { // clamp the pitch we set - DAngle min, max; +// [MC] Helper function for Set(View)Pitch. +DAngle AActor::ClampPitch(DAngle p) +{ + // clamp the pitch we set + DAngle min, max; - if (player != NULL) - { - min = player->MinPitch; - max = player->MaxPitch; - } - else - { - min = -89.; - max = 89.; - } - p = clamp(p, min, max); + if (player != nullptr) + { + min = player->MinPitch; + max = player->MaxPitch; } + else + { + min = -89.; + max = 89.; + } + p = clamp(p, min, max); + return p; +} + +void AActor::SetPitch(DAngle p, int fflags) +{ + if (player != nullptr || (fflags & SPF_FORCECLAMP)) + { + p = ClampPitch(p); + } + if (p != Angles.Pitch) { Angles.Pitch = p; - if (player != NULL && interpolate) + if (player != nullptr && (fflags & SPF_INTERPOLATE)) { player->cheats |= CF_INTERPVIEW; } } + } -void AActor::SetAngle(DAngle ang, bool interpolate) +void AActor::SetAngle(DAngle ang, int fflags) { if (ang != Angles.Yaw) { Angles.Yaw = ang; - if (player != NULL && interpolate) + if (player != nullptr && (fflags & SPF_INTERPOLATE)) + { + player->cheats |= CF_INTERPVIEW; + } + } + +} + +void AActor::SetRoll(DAngle r, int fflags) +{ + if (r != Angles.Roll) + { + Angles.Roll = r; + if (player != nullptr && (fflags & SPF_INTERPOLATE)) { player->cheats |= CF_INTERPVIEW; } } } -void AActor::SetRoll(DAngle r, bool interpolate) +void AActor::SetViewPitch(DAngle p, int fflags) { - if (r != Angles.Roll) + if (player != NULL || (fflags & SPF_FORCECLAMP)) { - Angles.Roll = r; - if (player != NULL && interpolate) + p = ClampPitch(p); + } + + if (p != ViewAngles.Pitch) + { + ViewAngles.Pitch = p; + if (player != nullptr && (fflags & SPF_INTERPOLATE)) + { + player->cheats |= CF_INTERPVIEW; + } + } + +} + +void AActor::SetViewAngle(DAngle ang, int fflags) +{ + if (ang != ViewAngles.Yaw) + { + ViewAngles.Yaw = ang; + if (player != nullptr && (fflags & SPF_INTERPOLATE)) + { + player->cheats |= CF_INTERPVIEW; + } + } + +} + +void AActor::SetViewRoll(DAngle r, int fflags) +{ + if (r != ViewAngles.Roll) + { + ViewAngles.Roll = r; + if (player != nullptr && (fflags & SPF_INTERPOLATE)) { player->cheats |= CF_INTERPVIEW; } diff --git a/src/rendering/r_utility.cpp b/src/rendering/r_utility.cpp index 76ecc5e683..157bef7bc3 100644 --- a/src/rendering/r_utility.cpp +++ b/src/rendering/r_utility.cpp @@ -804,7 +804,17 @@ void R_SetupFrame (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, AActor viewpoint.sector = viewpoint.camera->Sector; viewpoint.showviewer = false; } - iview->New.Angles = viewpoint.camera->Angles; + + // [MC] Apply the view angles first, which is the offsets. If the absolute isn't desired, + // add the standard angles on top of it. + viewpoint.Angles = viewpoint.camera->ViewAngles; + + if (!(viewpoint.camera->flags8 & MF8_ABSVIEWANGLES)) + { + viewpoint.Angles += viewpoint.camera->Angles; + } + + iview->New.Angles = viewpoint.Angles; if (viewpoint.camera->player != 0) { player = viewpoint.camera->player; diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 80470c2113..571efff641 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -325,6 +325,7 @@ static FFlagDef ActorFlagDefs[]= DEFINE_FLAG(MF8, NOFRICTIONBOUNCE, AActor, flags8), DEFINE_FLAG(MF8, RETARGETAFTERSLAM, AActor, flags8), DEFINE_FLAG(MF8, STOPRAILS, AActor, flags8), + DEFINE_FLAG(MF8, ABSVIEWANGLES, AActor, flags8), // Effect flags DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects), diff --git a/src/scripting/vmthunks_actors.cpp b/src/scripting/vmthunks_actors.cpp index 63fdcbbd86..a99554ab8b 100644 --- a/src/scripting/vmthunks_actors.cpp +++ b/src/scripting/vmthunks_actors.cpp @@ -1943,6 +1943,9 @@ DEFINE_FIELD(AActor, RenderRequired) DEFINE_FIELD(AActor, friendlyseeblocks) DEFINE_FIELD(AActor, SpawnTime) DEFINE_FIELD(AActor, InventoryID) +DEFINE_FIELD_NAMED(AActor, ViewAngles.Yaw, viewangle) +DEFINE_FIELD_NAMED(AActor, ViewAngles.Pitch, viewpitch) +DEFINE_FIELD_NAMED(AActor, ViewAngles.Roll, viewroll) DEFINE_FIELD_X(FCheckPosition, FCheckPosition, thing); DEFINE_FIELD_X(FCheckPosition, FCheckPosition, pos); diff --git a/wadsrc/static/zscript/actors/actor.zs b/wadsrc/static/zscript/actors/actor.zs index a719376e3d..c79a47a777 100644 --- a/wadsrc/static/zscript/actors/actor.zs +++ b/wadsrc/static/zscript/actors/actor.zs @@ -232,6 +232,7 @@ class Actor : Thinker native native uint8 fountaincolor; native double CameraHeight; // Height of camera when used as such native double CameraFOV; + native double ViewAngle, ViewPitch, ViewRoll; native double RadiusDamageFactor; // Radius damage factor native double SelfDamageFactor; native double StealthAlpha; @@ -1122,6 +1123,9 @@ class Actor : Thinker native native void A_SetAngle(double angle = 0, int flags = 0, int ptr = AAPTR_DEFAULT); native void A_SetPitch(double pitch, int flags = 0, int ptr = AAPTR_DEFAULT); native void A_SetRoll(double roll, int flags = 0, int ptr = AAPTR_DEFAULT); + native void A_SetViewAngle(double angle = 0, int flags = 0, int ptr = AAPTR_DEFAULT); + native void A_SetViewPitch(double pitch, int flags = 0, int ptr = AAPTR_DEFAULT); + native void A_SetViewRoll(double roll, int flags = 0, int ptr = AAPTR_DEFAULT); deprecated("2.3", "User variables are deprecated in ZScript. Actor variables are directly accessible") native void A_SetUserVar(name varname, int value); deprecated("2.3", "User variables are deprecated in ZScript. Actor variables are directly accessible") native void A_SetUserArray(name varname, int index, int value); deprecated("2.3", "User variables are deprecated in ZScript. Actor variables are directly accessible") native void A_SetUserVarFloat(name varname, double value);