logo
Main

Forums

Downloads

Unreal-Netiquette

Donate for Oldunreal:
Donate

borderline

Links to our wiki:
Wiki

Walkthrough

Links

Tutorials

Unreal Reference

Usermaps

borderline

Contact us:
Submit News
Page Index Toggle Pages: 1 [2]  Send TopicPrint
Hot Topic (More than 10 Replies) Real crouching bug: telefragging when getting up (Read 1061 times)
Masterkent
Developer Team
Offline



Posts: 983
Location: Russia
Joined: Apr 5th, 2013
Gender: Male
Re: Real crouching bug: telefragging when getting up
Reply #15 - Jan 4th, 2018 at 5:22pm
Print Post  
Here's the preliminary implementation (can be compiled and tested):
PlayerPawn (without defaultproperties)
RealCrouchController.uc (new auxiliary class)

It does not resolve issue 6; other issues are addressed.

I'll comment the changes later.
  
Back to top
 
IP Logged
 
Masterkent
Developer Team
Offline



Posts: 983
Location: Russia
Joined: Apr 5th, 2013
Gender: Male
Re: Real crouching bug: telefragging when getting up
Reply #16 - Jan 6th, 2018 at 6:08pm
Print Post  
A few commentaries to the code. The first part is about changes in class PlayerPawn.

Code
Select All
function ClientUpdatePosition()
{
	local SavedMove CurrentMove;
	local int realbRun, realbDuck;
-	local bool bRealJump,bRealCrouch;
+	local bool bRealJump;

	realbRun = bRun;
	realbDuck = bDuck;
	bRealJump = bPressedJump;
-	bRealCrouch = bIsReducedCrouch;
	CurrentMove = SavedMoves;
	while ( CurrentMove != None )
	{
		if ( CurrentMove.TimeStamp <= CurrentTimeStamp )
		{
			SavedMoves = CurrentMove.NextMove;
			CurrentMove.NextMove = FreeMoves;
			FreeMoves = CurrentMove;
			FreeMoves.Clear();
			CurrentMove = SavedMoves;
		}
		else
		{
			CrouchCheckTime = -1;
-			SetCrouch(CurrentMove.bIsReducedCrouch);
			MoveAutonomous(CurrentMove.Delta, CurrentMove.bRun, CurrentMove.bDuck, CurrentMove.bPressedJump, CurrentMove.DodgeMove, CurrentMove.Acceleration, rot(0,0,0));
			CurrentMove = CurrentMove.NextMove;
		}
	}
	bDuck = realbDuck;
	bRun = realbRun;
	bPressedJump = bRealJump;
	CrouchCheckTime = -1;
-	SetCrouch(bRealCrouch);
	bUpdatePosition = false;
	//log("Client adjusted "$self$" stamp "$CurrentTimeStamp$" location "$Location$" dodge "$DodgeDir);
} 


I don't see reasons to call SetCrouch from ClientUpdatePosition; moreover, when I logged the value of CurrentMove.bIsReducedCrouch, it was always equal to false.

Code
Select All
event UpdateEyeHeight(float DeltaTime)
{
	local float smooth, bound;

+	if (bIsReducedCrouch && IsInState('PlayerWalking'))
+		BaseEyeHeight = default.BaseEyeHeight + CollisionHeight - default.CollisionHeight;
+
	smooth = FMin(1.0, 10.0 * DeltaTime/Level.TimeDilation);
	// smooth up/down stairs
	If( (IsInState('PlayerSwimming') || Physics==PHYS_Walking) && !bJustLanded )
	{
		EyeHeight = (EyeHeight - Location.Z + OldLocation.Z) * (1 - smooth) + ( ShakeVert + BaseEyeHeight) * smooth;
		bound = -0.5 * CollisionHeight;
		if (EyeHeight < bound)
			EyeHeight = bound;
		else
		{
-			bound = CollisionHeight + FMin(FMax(0.0,(OldLocation.Z - Location.Z)), MaxStepHeight);
+			bound = FMin(FMax(0.0,(OldLocation.Z - Location.Z)), MaxStepHeight);
+			if (bIsReducedCrouch)
+				bound += default.CollisionHeight;
+			else
+				bound += CollisionHeight;
			if ( EyeHeight > bound )
				EyeHeight = bound; 


This change is necessary for smooth movement of player's camera when the player begins to crouch.

Code
Select All
simulated function bool AdjustHitLocation(out vector HitLocation, vector TraceDir)
{
	local float adjZ, maxZ;

	TraceDir = Normal(TraceDir);
	HitLocation = HitLocation + 0.5 * CollisionRadius * TraceDir;
-	if ( BaseEyeHeight == Default.BaseEyeHeight )
+	if (BaseEyeHeight == default.BaseEyeHeight || bIsCrouching && bIsReducedCrouch)
		return true;
 


When the player is crouching and the collision height is reduced, there is no need in cutting the top part of the collision cylinder.

Code
Select All
event PlayerCalcView(out actor ViewActor, out vector CameraLocation, out rotator CameraRotation )
{
	local Pawn PTarget;

	if ( ViewTarget != None )
	{
		ViewActor = ViewTarget;
		CameraLocation = ViewTarget.Location;
		CameraRotation = ViewTarget.Rotation;
		PTarget = Pawn(ViewTarget);
		if ( PTarget != None )
		{
			if ( Level.NetMode == NM_Client )
			{
				if ( PTarget.bIsPlayer )
					PTarget.ViewRotation = TargetViewRotation;
				PTarget.EyeHeight = TargetEyeHeight;
				if ( PTarget.Weapon != None )
					PTarget.Weapon.PlayerViewOffset = TargetWeaponViewOffset;
			}
			if ( PTarget.bIsPlayer )
				CameraRotation = PTarget.ViewRotation;
			if ( !bBehindView )
				CameraLocation.Z += PTarget.EyeHeight;
+			else
+				CameraLocation.Z += PTarget.PrePivot.Z - PTarget.default.PrePivot.Z;
		}
		if ( bBehindView )
			CalcBehindView(CameraLocation, CameraRotation, 180);
		return;
	}

	ViewActor = Self;
	CameraLocation = Location;

	if ( bBehindView ) //up and behind
+	{
+		CameraLocation.Z += PrePivot.Z - default.PrePivot.Z;
		CalcBehindView(CameraLocation, CameraRotation, 150);
+	}
	else 


This change prevents the camera from jumping up and down when crouching and standing up while using 3rd person view.

In state PlayerWalking:
Code
Select All
	function AnimEnd()
	{
		local name MyAnimGroup;

		bAnimTransition = false;
		if ( Physics == PHYS_Spider )
		{
			if ( VSize(Velocity)<10 )
				PlayDuck();
			else PlayCrawling();
		}
		else if (Physics == PHYS_Walking)
		{
....
		}
+		else if (bIsReducedCrouch)
+			PlayDuck();
		else
			PlayInAir();
	} 


PlayInAir animation doesn't look appropriate when the player is crouching in air.

In state PlayerWalking:
Code
Select All
	function ProcessMove(float DeltaTime, vector NewAccel, eDodgeDir DodgeMove, rotator DeltaRot)
	{
		local vector OldAccel;

		OldAccel = Acceleration;
		Acceleration = NewAccel;
		bIsTurning = ( Abs(DeltaRot.Yaw/DeltaTime) > 5000 );
		if ( (DodgeMove == DODGE_Active) && (Physics == PHYS_Falling) )
			DodgeDir = DODGE_Active;
		else if ( (DodgeMove != DODGE_None) && (DodgeMove < DODGE_Active) )
			Dodge(DodgeMove);

		if ( bPressedJump )
			DoJump();
		if ( (Physics == PHYS_Walking) && (GetAnimGroup(AnimSequence) != 'Dodge') )
		{
			if ( !bIsCrouching )
			{
-				if ( bDuck != 0 && TryToDuck(true) )
+				if ( bDuck != 0 && TryToDuck(true) && IsInState('PlayerWalking') ) // Note: TryToDuck may change the state (e.g. to PlayerSwimming)
				{
					bIsCrouching = true;
					PlayDuck();
				}
			}
			else if ( bDuck == 0 && TryToDuck(false) )
			{
				OldAccel = vect(0,0,0);
				bIsCrouching = false;
			} 


TryToDuck may change player's state to PlayerSwimming (because new player location may reside a water zone), then changing the value of bIsCrouching to true produces weird effects.

In state PlayerWalking:
Code
Select All
	function BeginState()
	{
		WalkBob = vect(0,0,0);
		DodgeDir = DODGE_None;
-		bIsCrouching = false;
+		bIsCrouching = bIsReducedCrouch;
		bIsTurning = false;
		bPressedJump = false;
		if (Physics != PHYS_Falling)
			SetPhysics(PHYS_Walking);
		if ( !IsAnimating() )
-			PlayWaiting();
+		{
+			if (bIsCrouching)
+				PlayDuck();
+			else
+				PlayWaiting();
+		}
	} 


A crouching player may enter a water zone and leave it while holding the crouch key. A correct and smooth transition from PlayerSwimming to PlayerWalking needs setting the value of bIsCrouching to the value that would correspond to bIsReducedCrouch which reflects whether the height of player's collision cylinder is reduced.

In state PlayerWalking:
Code
Select All
	function EndState()
	{
		WalkBob = vect(0,0,0);
		bIsCrouching = false;
-		SetCrouch(false);
	} 


Restoring the original collision height and location when leaving the PlayerWalking state may cause undesirable effects, because the state can be changed due to location change implied by real crouching. Uncrouching may imply changing the location back to its previous value which in turn may imply the need in changing the state back to PlayerWalking. This way we can end up in having a series of state changes like PlayerWalking -> PlayerSwimming -> PlayerWalking -> PlayerSwimming -> PlayerWalking -> ...

In state FeigningDeath:
Code
Select All
	function Rise()
	{
		if ( !bRising )
		{
			Enable('AnimEnd');
-			BaseEyeHeight = Default.BaseEyeHeight;
			bRising = true;
-			PlayRising();
+			if (TryToDuck(false))
+			{
+				BaseEyeHeight = default.BaseEyeHeight;
+				PlayRising();
+			}
+			else
+			{
+				PlayDuck();
+				BaseEyeHeight = default.BaseEyeHeight + CollisionHeight - default.CollisionHeight;
+			}
		}
	} 


If the player cannot stand up due to insufficient space, rising should be performed as a transition from feigning death to crouching.

In state FeigningDeath:
Code
Select All
+	simulated function bool ShouldHaveReducedHeight()
+	{
+		return !bRising;
+	} 


This function is supposed to override the corresponding global function (see below). True result indicates that the collision cylinder should have a reduced height, false result indicates that an attempt to restore the original CollisionHeight should be done (CollisionHeight is changed as a part of standing up; standing up is possible only if there is enough space).

In state FeigningDeath:
Code
Select All
	function BeginState()
	{
		local rotator NewRot;
		if ( carriedDecoration != None )
			DropDecoration();
		NewRot = Rotation;
		NewRot.Pitch = 0;
		SetRotation(NewRot);
		BaseEyeHeight = -0.5 * CollisionHeight;
		bIsCrouching = false;
		bPressedJump = false;
		bRising = false;
		Disable('AnimEnd');
		PlayFeignDeath();
		PlayerReplicationInfo.bFeigningDeath = true;
+		TryToDuck(true);
	} 


If crouching implies that CollisionHeight should be reduced, it would be logical to reduce CollisionHeight when the player is feigning death too. Since players cannot move when feigning death, I do not suggest to choose a smaller value of CollisionHeight than the one used for crouching. Maintaining the same CollisionHeight as would be used for crouching does not allow to block the player in a way so that he would not be able to quit feigning death and either stand up or begin to crouch at least.

In state PlayerSwimming:
Code
Select All
	event UpdateEyeHeight(float DeltaTime)
	{
		local float smooth, bound;

		// smooth up/down stairs
		if ( !bJustLanded )
		{
			smooth = FMin(1.0, 10.0 * DeltaTime/Level.TimeDilation);
			EyeHeight = (EyeHeight - Location.Z + OldLocation.Z) * (1 - smooth) + ( ShakeVert + BaseEyeHeight) * smooth;
			bound = -0.5 * CollisionHeight;
			if (EyeHeight < bound)
				EyeHeight = bound;
			else
			{
				bound = CollisionHeight + FClamp((OldLocation.Z - Location.Z), 0.0, MaxStepHeight);
+				if (bIsReducedCrouch)
+					bound += default.CollisionHeight - CollisionHeight;
				if ( EyeHeight > bound )
					EyeHeight = bound;
 


This change is necessary for smooth movement of player's camera when the player begins to crouch.

In state PlayerSwimming:
Code
Select All
	function BeginState()
	{
		Disable('Timer');
		if (!IsAnimating())
			TweenToWaiting(0.3);
+		else if (bIsReducedCrouch)
+			TweenToSwimming(0.2);
		//log("player swimming");
	} 


This change is needed for smooth animation transition.

In function SetCrouch:
Code
Select All
	bIsReducedCrouch = bCrouching;
+	class'RealCrouchController'.static.GetRealCrouchController(self);
 


This function creates new RC controller if the player has no such a controller yet. The RC controller serves two purposes. Firstly, it checks whether the player's collision cylinder definitely must have a reduced CollisionHeight or full CollisionHeight and if the current value does not match the required one, then the controller changes CollisionHeight correspondingly. This check is done on every tick. Since PlayerPawn.PlayerTick is widely overridden in various states and may be overridden in subclasses, using the Tick event of a separate actor appears to be more simple and reliable solution.

Secondly, RC controller implements speculative move operations, it lets us determine whether the player can be moved from "here" to "there". Basically, it imitates the player's collision cylinder.

In function SetCrouch:
Code
Select All
	if (bCrouching)
	{
		// Update collision size, prepivot height and location.
-		OldHeight = CollisionHeight;
-		SetCollisionSize(CollisionRadius,CollisionHeight*CrouchHeightPct);
-		OldHeight = OldHeight-CollisionHeight;
-		PrePivot.Z+=OldHeight;
-		EyeHeight+=OldHeight;
-		Move(vect(0,0,-1)*OldHeight);
+		NewCollisionHeight = default.CollisionHeight * CrouchHeightPct;
+		MoveDistance = CollisionHeight - NewCollisionHeight;
+		if (MoveDistance <= 0)
+			return;
+
+		VerticalOffset = default.CollisionHeight - NewCollisionHeight;
+		SetCollisionSize(CollisionRadius, NewCollisionHeight);
+		PrePivot.Z = default.PrePivot.Z + VerticalOffset;
+		EyeHeight += MoveDistance;
+		Move(vect(0, 0, -1) * MoveDistance);
	} 


default.CollisionHeight is a reliable basis, while CollisionHeight may have an outdated value assigned through server-to-client replication. It's worth noting though that using default.CollisionHeight limits the freedom of changing CollisionHeight by third party mods.

In function SetCrouch:
Code
Select All
	else
	{
		// Reset collision size, prepivot height and location.
-		OldHeight = CollisionHeight;
-		NewHeight = CollisionHeight/CrouchHeightPct;
-		OldHeight = NewHeight-OldHeight;
-		PrePivot.Z-=OldHeight;
-		OldHeight*=0.5f;
-		EyeHeight-=OldHeight;
-		Move(vect(0,0,2)*OldHeight);
-		SetLocation(Location);
-		SetCollisionSize(CollisionRadius,NewHeight);
+		NewCollisionHeight = default.CollisionHeight;
+		MoveDistance = NewCollisionHeight - CollisionHeight;
+
+		// Use the cached result when indirectly calling from TryToDuck or a newly calculated result otherwise
+		if (MoveDistance <= 0 || !class'RealCrouchController'.static.CanStandUp(self, NewCollisionHeight))
+			return;
+
+		PrePivot.Z = default.PrePivot.Z;
+		EyeHeight -= MoveDistance;
+
+		bOldBlockActors = bBlockActors;
+		bOldBlockPlayers = bBlockPlayers;
+		bOldCollideWorld = bCollideWorld;
+
+		SetCollision(bCollideActors, false, false);
+		bCollideWorld = false;
+		SetCollisionSize(CollisionRadius, NewCollisionHeight);
+		Move(vect(0, 0, 1) * MoveDistance);
+		SetLocation(Location);
+		if (CarriedDecoration != none)
+		{
+			CarriedDecoration.SetPhysics(PHYS_None);
+			CarriedDecoration.SetBase(self);
+		}
+		SetCollision(bCollideActors, bOldBlockActors, bOldBlockPlayers);
+		bCollideWorld = bOldCollideWorld;
	} 


Generally, SetCrouch is supposed to be an internal function in the new implementation, mods should not call it directly. In case if someone calls it nevertheless, SetCrouch(false) performs an extra check if the player can stand up (such a check is supposed to be done by TryToDuck).

Once the player is known to be able to stand up, the player is moved to the new location. During the move operation the player cannot be blocked by level geometry or other actors and cannot encroach on other actors (the cause of telefragging). SetLocation(Location) ensures that the player touches all actors that might be touched when moving the top bound of the player in a usual way. The call to Move moves the player and the CarriedDecoration (if any); it's possible to use only SetLocation, but then CarriedDecoration has to be moved separately in order to resolve issue 6.1. I don't see an easy resolution for issue 6 in general, having only the currently available set of C++ functions.

Changing CollisionHeight and Location could be done in a much more clean way if we had a function that could perform these two operations as a single transaction:

Code
Select All
// Effects: the function moves the actor by (ZOffset * vect(0, 0, 1)) and changes its CollisionHeight
//     to (CollisionHeight + ZOffset) - hence the collision top is moved by (ZOffset * vect(0, 0, 2)).
//     Other actors can be touched as if Move(ZOffset * vect(0, 0, 2)) was called instead, but the bottom
//     of the collision cylinder does not change its relative position to the level and the actor cannot be
//     blocked by the level geometry or other actors during the move operation. A possible overlapping with
//     other actors does not cause calls to EncroachingOn or EncroachedBy. Moving the actor out of world is
//     allowed.
// Precondition: ZOffset shall not be less than -CollisionRadius
//
native final function bool MoveCollisionTop(float ZOffset); 



Code
Select All
simulated final function bool TryToDuck(bool bCrouching)
{
-	local vector Dummy,Offset,Start;

	if (!Level.bSupportsRealCrouching || bIsReducedCrouch == bCrouching)
		return true;
	if (bCrouching)
	{
		SetCrouch(true);
		return true;
	}
	// Make sure there is space to stand up
-	Start = Location;
-	Start.Z = Location.Z+CollisionHeight-0.01f;
-	Offset.Z = ((CollisionHeight/CrouchHeightPct)-CollisionHeight);
-	if( Trace(Dummy,Dummy,Start+Offset,Start,true,vect(1.f,1.f,0.f)*CollisionRadius)!=None )
-		return false; // Wasnt enough space to uncrouch.
+	if (!class'RealCrouchController'.static.CanStandUp(self, default.CollisionHeight))
+		return false; // Not enough space to uncrouch
	SetCrouch(false);
	return true;
} 


The method based on the Trace function may give false-positive and false-negative results. CanStandUp uses a more reliable method based on a speculative move operation (see below).

Code
Select All
simulated function bool ShouldHaveReducedHeight()
{
	return bIsCrouching || bIsReducedCrouch && bDuck != 0;
} 


The result of this function is used by RealCrouchController.Tick to determine if the player should be forced to crouch or try to uncrouch (see below).

Code
Select All
+simulated function bool IsHeadShot(vector HitLocation, vector TraceDir)
+{
+	return !bIsReducedCrouch && super.IsHeadShot(HitLocation, TraceDir);
+} 


Crouching is known as a trick that prevents headshots. Although it's more likely a dev's oversight than a planned behavior, I think that people have widely used this trick for years, and disallowing it in case of real crouching wouldn't be appreciated by gamers. Tweaking AdjustHitLocation as shown above would allow headshots for crouching players, and this definition of IsHeadShot is supposed to negate such an effect, so that old trick would still work.


Now about new class RealCrouchController.

Code
Select All
static function RealCrouchController GetRealCrouchController(PlayerPawn Player)
{
	local RealCrouchController CrouchController;
	foreach Player.ChildActors(class'RealCrouchController', CrouchController)
		break;
	if (CrouchController == none || CrouchController.bDeleteMe)
		return Player.Spawn(class'RealCrouchController', Player);
	return CrouchController;
} 


Finding the existing controller can be optimized by adding a new property in class PlayerPawn:

Code
Select All
var RealCrouhController RealCrouchController; 


I used ChildActors for testing purposes, because adding new variables in PlayerPawn by means of UnrealEd doesn't work well.

In function CanStandUp:
Code
Select All
	if (Player.Level.TimeSeconds == CrouchController.TimeStamp)
		return !CrouchController.bIsBlocked;

	if (Player.CollisionHeight >= TargetCollisionHeight)
		return true;
	if (!Player.bCollideActors && !Player.bCollideWorld)
		return true;
 


A speculative move operation is relatively slow, so it makes sense to avoid unnecessary calculations, when the result can be evaluated using more simple methods. Firstly, CollisionHeight most likely won't be changed many times during the same tick, so we can cache the result of CanStandUp and use it within the same tick later. Secondly, if all collision between the player and other objects is disabled (unusual case), the move operation would succeed anyway.

Code
Select All
	CrouchController.SetCollision(false, false, false);
	CrouchController.SetCollisionSize(Player.CollisionRadius, Player.CollisionHeight);
	CrouchController.bCollideWorld = false;
	CrouchController.Move(Player.Location - CrouchController.Location);
	if (Player.bBlockActors)
		CrouchController.SetCollision(true, false, false);
	CrouchController.bCollideWorld = Player.bCollideWorld;

	CrouchController.bIsBlocked = false;
	CrouchController.BumpedActor = none;

	DstLocation = Player.Location + vect(0, 0, 2) * (TargetCollisionHeight - Player.CollisionHeight);

	// Checking if the player would hit non-Brush actors, Brush actors, or the level geometry
	CrouchController.Move(DstLocation - CrouchController.Location);
	if (CrouchController.bIsBlocked)
		return CrouchController.CanStandUp_Cache(false);

	while (
		CrouchController.Location != DstLocation &&
		CrouchController.BumpedActor != none &&
		CrouchController.BumpedActor.bCollideActors &&
		!CrouchController.BumpedActor.bBlockPlayers &&
		i < 16)
	{
		CrouchController.BumpedActor.SetCollision(false, CrouchController.BumpedActor.bBlockActors, false);
		CrouchController.Move(DstLocation - CrouchController.Location);
		CrouchController.BumpedActor.SetCollision(true, CrouchController.BumpedActor.bBlockActors, false);

		if (CrouchController.bIsBlocked)
			return CrouchController.CanStandUp_Cache(false);

		CrouchController.BumpedActor = none;
		++i;
	}

	return CrouchController.CanStandUp_Cache(CrouchController.Location == DstLocation);
} 


All this complicated stuff is supposed to calculate what entities would block the player if the player tried to stand up. Blocking PlayerPawns by other actors is implemented in a special way, so simulating its movement with an aux actor is a rather tricky thing. Such tricks would be unnecessary if we had a function like this:

Code
Select All
// Effects: the function determines what would happen if Move(Delta) was called instead:
//     - the return value is the same as would be returned by Move(Delta);
//     - if FromLocation is not specified, the initial location is assumed to be the current Location;
//       otherwise, the initial location before the move operation is assumed to be FromLocation;
//     - ResultingLocation is equal to the value that the actor's Location would have after calling Move(Delta);
//     - if during the move operation the actor would be blocked by the level geometry or other actor,
//       then BlockedByActor is set to Level or that blocking actor correspondingly;
//       otherwise, BlockedByActor is set to none.
// Note: the function has no side effects; in particular, it does not invoke events Touch/UnTouch, ZoneChange, etc.
//
native final function bool SpeculativeMove(vector Delta, optional vector FromLocation, optional out vector ResultingLocation, optional out Actor BlockedByActor);
 


This is how CanStandUp could be implemented then:

Code
Select All
static final function bool CanStandUp(PlayerPawn Player, float TargetCollisionHeight)
{
	local RealCrouchController CrouchController;
	local vector TopOffset;
	local Actor BlockedBy;

	CrouchController = GetRealCrouchController(Player);
	if (CrouchController == none) // should be always false
		return true;

	if (Player.Level.TimeSeconds == CrouchController.TimeStamp)
		return !CrouchController.bIsBlocked;

	if (Player.CollisionHeight >= TargetCollisionHeight)
		return true;
	if (!Player.bCollideActors && !Player.bCollideWorld)
		return true;

	CrouchController.bIsBlocked = false;
	TopOffset = vect(0, 0, 2) * (TargetCollisionHeight - Player.CollisionHeight);

	return CrouchController.CanStandUp_Cache(SpeculativeMove(TopOffset,,, BlockedBy) && BlockedBy == none);
} 


Code
Select All
event Tick(float DeltaTime)
{
	local PlayerPawn Player;

	Player = PlayerPawn(Owner);
	if (Player == none || Player.bDeleteMe)
	{
		Destroy();
		return;
	}

	if (Player.bIsReducedCrouch != Player.ShouldHaveReducedHeight())
		Player.TryToDuck(!Player.bIsReducedCrouch);
} 


This function periodically checks if the player should be forced to crouch or should try to stand up and adjust the player correspondingly. It's useful mostly in states that differ from PlayerWalking.
  
Back to top
 
IP Logged
 
Smirftsch
Forum Administrator
*****
Offline



Posts: 7619
Location: at home
Joined: Apr 30th, 1998
Gender: Male
Re: Real crouching bug: telefragging when getting up
Reply #17 - Jan 7th, 2018 at 7:35am
Print Post  
thank you very much for the work you put into this!!!

I'll integrate it asap Smiley

Edited:
Added current version but will have a look yet for the native function you suggested.


Edited:
It seems that MoveActor is having already an almost suitable flag: bTest. If I pass this through as optional bool in move() it may suffice already for the most part.
« Last Edit: Jan 7th, 2018 at 11:30am by Smirftsch »  

Sometimes you have to lose a fight to win the war.
Back to top
WWWICQ  
IP Logged
 
Page Index Toggle Pages: 1 [2] 
Send TopicPrint
Bookmarks: del.icio.us Digg Facebook Google Google+ Linked in reddit StumbleUpon Twitter Yahoo