diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 6a33bf595..5c6198d46 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -6052,3 +6052,115 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckBlock) ACTION_JUMP(block); } } + +//=========================================================================== +// +// A_FaceVelocity(angle offset, bool pitch, ptr) +// +// Sets the actor('s pointer) to face the direction of travel. +//=========================================================================== +enum FVFlags +{ + FVF_NOPITCH = 1 << 0, + FVF_INTERPOLATE = 1 << 1, + FVF_NOANGLE = 1 << 2, +}; +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FaceVelocity) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_ANGLE(offset, 0); + ACTION_PARAM_ANGLE(anglelimit, 1); + ACTION_PARAM_ANGLE(pitchlimit, 2); + ACTION_PARAM_INT(flags, 3); + ACTION_PARAM_INT(ptr, 4); + + AActor *mobj = COPY_AAPTR(self, ptr); + + //Need an actor. + if (!mobj || ((flags & FVF_NOPITCH) && (flags & FVF_NOANGLE))) + { + ACTION_SET_RESULT(false); + return; + } + + //Don't bother calculating this if we don't have any horizontal movement. + if (!(flags & FVF_NOANGLE) && (mobj->velx != 0 || mobj->vely != 0)) + { + angle_t current = mobj->angle; + const angle_t angle = R_PointToAngle2(0, 0, mobj->velx, mobj->vely); + //Done because using anglelimit directly causes a signed/unsigned mismatch. + const angle_t limit = anglelimit; + + //Code borrowed from A_Face*. + if (limit > 0 && (absangle(current - angle) > limit)) + { + if (current < angle) + { + // [MC] This may appear backwards, but I assure any who + // reads this, it works. + if (current - angle > ANGLE_180) + current += limit + offset; + else + current -= limit + offset; + mobj->SetAngle(current, !!(flags & FVF_INTERPOLATE)); + } + else if (current > angle) + { + if (angle - current > ANGLE_180) + current -= limit + offset; + else + current += limit + offset; + mobj->SetAngle(current, !!(flags & FVF_INTERPOLATE)); + } + else + mobj->SetAngle(angle + ANGLE_180 + offset, !!(flags & FVF_INTERPOLATE)); + + } + else + mobj->SetAngle(angle + offset, !!(flags & FVF_INTERPOLATE)); + } + + if (!(flags & FVF_NOPITCH)) + { + fixed_t current = mobj->pitch; + const FVector2 velocity(mobj->velx, mobj->vely); + const fixed_t pitch = R_PointToAngle2(0, 0, (fixed_t)velocity.Length(), -mobj->velz); + fixedvec2 test; + if (pitchlimit > 0) + { + // [MC] angle_t for pitchlimit was required because otherwise + // we would wind up with less than desirable turn rates that didn't + // match that of A_SetPitch. We want consistency. Also, I didn't know + // of a better way to convert from angle_t to fixed_t properly so I + // used this instead. + fixed_t plimit = fixed_t(pitchlimit); + + if (abs(current - pitch) > plimit) + { + fixed_t max = 0; + + if (current > pitch) + { + max = MIN(plimit, (current - pitch)); + current -= max; + } + else //if (current > pitch) + { + max = MIN(plimit, (pitch - current)); + current += max; + } + mobj->SetPitch(current, !!(flags & FVF_INTERPOLATE)); + } + else + { + mobj->SetPitch(pitch, !!(flags & FVF_INTERPOLATE)); + } + + } + else + { + mobj->SetPitch(pitch, !!(flags & FVF_INTERPOLATE)); + } + } +} + diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 5158aa7b5..eca2a06c6 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -341,6 +341,7 @@ ACTOR Actor native //: Thinker action native A_CheckBlock(state block, int flags = 0, int ptr = AAPTR_DEFAULT); action native A_CheckSightOrRange(float distance, state label, bool two_dimension = false); action native A_CheckRange(float distance, state label, bool two_dimension = false); + action native A_FaceVelocity(float offset = 0, float anglelimit = 0, float pitchlimit = 0, int flags = 0, int ptr = AAPTR_DEFAULT); action native A_RearrangePointers(int newtarget, int newmaster = AAPTR_DEFAULT, int newtracer = AAPTR_DEFAULT, int flags=0); action native A_TransferPointer(int ptr_source, int ptr_recepient, int sourcefield, int recepientfield=AAPTR_DEFAULT, int flags=0); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 213e2109d..539b30267 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -510,6 +510,14 @@ enum CBF_SETONPTR = 1 << 4, //Sets the pointer change on the actor doing the checking instead of self. }; +//Flags for A_FaceVelocity +enum +{ + FVF_NOPITCH = 1 << 0, + FVF_INTERPOLATE = 1 << 1, + FVF_NOANGLE = 1 << 2, +}; + // This is only here to provide one global variable for testing. native int testglobalvar;