Skip to content
This repository has been archived by the owner on Jan 5, 2024. It is now read-only.

Commit

Permalink
Added crouch rotation
Browse files Browse the repository at this point in the history
  • Loading branch information
Causeless committed Dec 27, 2023
1 parent 7bfdfb4 commit cc0e182
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 24 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

- New `AHuman` INI and Lua (R/W) property `MaxWalkPathCrouchShift`, which determines how much the actor will automatically duck down to avoid low ceilings above them. This can be set to 0 to never duck. Defaults to 6.

- New `AHuman` INI and Lua (R/W) property `MaxCrouchRotation`, which determines how much the actor will rotate when ducking to avoid low ceilings above them. This can be set to 0 to never duck. Defaults to a quarter of Pi * 1.25 (roughly 56 degrees).

- New `MOPixel` INI and Lua (R/W) property `Staininess`, which defines how likely a pixel is to stain a surface when it collides with it. Staining a surface changes that surface's `Color` to that of this `MOPixel`, without changing the underlying material. Value can be between 0 and 1. Defaults to 0 (never stain).

- New `Activity` INI and Lua (R/W) property `AllowsUserSaving`, which can be used to enable/disable manual user saving/loading. This defaults to true for all `GAScripted` with an `OnSave()` function, but false otherwise. Lua `ActivityMan::SaveGame()` function now forces a save even if `AllowsUserSaving` is disabled. This allows mods and scripted gamemodes to handle saving in their own way (for example, only allowing saving at set points).
Expand Down
44 changes: 29 additions & 15 deletions Entities/AHuman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ void AHuman::Clear()
m_ProneState = NOTPRONE;
m_ProneTimer.Reset();
m_MaxWalkPathCrouchShift = 6.0F;
m_MaxCrouchRotation = c_QuarterPI * 1.25F
for (int i = 0; i < MOVEMENTSTATECOUNT; ++i) {
m_Paths[FGROUND][i].Reset();
m_Paths[BGROUND][i].Reset();
Expand All @@ -86,7 +87,7 @@ void AHuman::Clear()
m_BGArmFlailScalar = 0.7F;
m_EquipHUDTimer.Reset();
m_WalkAngle.fill(Matrix());
m_WalkPathYOffset = 0.0F;
m_WalkPathOffset.Reset();
m_ArmSwingRate = 1.0F;
m_DeviceArmSwayRate = 0.5F;

Expand Down Expand Up @@ -210,6 +211,7 @@ int AHuman::Create(const AHuman &reference) {
m_BackupBGFootGroup->SetLimbPos(atomGroupToUseAsFootGroupBG->GetLimbPos());

m_MaxWalkPathCrouchShift = reference.m_MaxWalkPathCrouchShift;
m_MaxCrouchRotation = reference.m_MaxCrouchRotation;

if (reference.m_StrideSound) { m_StrideSound = dynamic_cast<SoundContainer*>(reference.m_StrideSound->Clone()); }

Expand Down Expand Up @@ -287,9 +289,8 @@ int AHuman::ReadProperty(const std::string_view &propName, Reader &reader) {
m_BackupBGFootGroup = new AtomGroup(*m_pBGFootGroup);
m_BackupBGFootGroup->RemoveAllAtoms();
});
MatchProperty("MaxWalkPathCrouchShift", {
reader >> m_MaxWalkPathCrouchShift;
});
MatchProperty("MaxWalkPathCrouchShift", { reader >> m_MaxWalkPathCrouchShift; });
MatchProperty("MaxCrouchRotation", { reader >> m_MaxCrouchRotation; });
MatchProperty("StrideSound", {
m_StrideSound = new SoundContainer;
reader >> m_StrideSound;
Expand Down Expand Up @@ -355,6 +356,8 @@ int AHuman::Save(Writer &writer) const
writer << m_pBGFootGroup;
writer.NewProperty("MaxWalkPathCrouchShift");
writer << m_MaxWalkPathCrouchShift;
writer.NewProperty("MaxCrouchRotation");
writer << m_MaxCrouchRotation;
writer.NewProperty("StrideSound");
writer << m_StrideSound;

Expand Down Expand Up @@ -1725,9 +1728,14 @@ void AHuman::UpdateWalkAngle(AHuman::Layer whichLayer) {
g_SceneMan.CastStrengthRay(hitPosRight, Vector(0.0F, -desiredCrouchHeadRoom), 10.0F, hitPosRight, 0, g_MaterialGrass);
float headroom = m_pHead->GetPos().m_Y - std::max(hitPosLeft.m_Y, hitPosRight.m_Y);
float adjust = desiredCrouchHeadRoom - headroom;
m_WalkPathYOffset = std::clamp(LERP(0.0F, 1.0F, m_WalkPathYOffset, adjust, 0.3F), 0.0F, m_MaxWalkPathCrouchShift);
float walkPathYOffset = std::clamp(LERP(0.0F, 1.0F, -m_WalkPathOffset.m_Y, adjust, 0.3F), 0.0F, m_MaxWalkPathCrouchShift);
m_WalkPathOffset.m_Y = -walkPathYOffset;

// Adjust our X offset to try to keep our legs under our centre-of-mass
float predictedPosition = ((m_pHead->GetPos().m_X - m_Pos.m_X) * 0.15F) + m_Vel.m_X;
m_WalkPathOffset.m_X = predictedPosition;
} else {
m_WalkPathYOffset = 0.0f;
m_WalkPathOffset.Reset();
}
}
}
Expand Down Expand Up @@ -2240,7 +2248,7 @@ void AHuman::PreControllerUpdate()
// Reset the stride timer if the path is about to restart.
if (m_Paths[FGROUND][WALK].PathEnded() || m_Paths[FGROUND][WALK].PathIsAtStart()) { m_StrideTimer.Reset(); }
Vector jointPos = m_Pos + RotateOffset(m_pFGLeg->GetParentOffset());
m_ArmClimbing[BGROUND] = !m_pFGFootGroup->PushAsLimb(jointPos, m_Vel, m_WalkAngle[FGROUND], m_Paths[FGROUND][WALK], deltaTime, &restarted, false, Vector(0.0F, m_Paths[FGROUND][WALK].GetLowestY()), Vector(0.0F, -m_WalkPathYOffset));
m_ArmClimbing[BGROUND] = !m_pFGFootGroup->PushAsLimb(jointPos, m_Vel, m_WalkAngle[FGROUND], m_Paths[FGROUND][WALK], deltaTime, &restarted, false, Vector(0.0F, m_Paths[FGROUND][WALK].GetLowestY()), m_WalkPathOffset);
} else {
m_ArmClimbing[BGROUND] = false;
}
Expand All @@ -2249,7 +2257,7 @@ void AHuman::PreControllerUpdate()
// Reset the stride timer if the path is about to restart.
if (m_Paths[BGROUND][WALK].PathEnded() || m_Paths[BGROUND][WALK].PathIsAtStart()) { m_StrideTimer.Reset(); }
Vector jointPos = m_Pos + RotateOffset(m_pBGLeg->GetParentOffset());
m_ArmClimbing[FGROUND] = !m_pBGFootGroup->PushAsLimb(jointPos, m_Vel, m_WalkAngle[BGROUND], m_Paths[BGROUND][WALK], deltaTime, &restarted, false, Vector(0.0F, m_Paths[BGROUND][WALK].GetLowestY()), Vector(0.0F, -m_WalkPathYOffset));
m_ArmClimbing[FGROUND] = !m_pBGFootGroup->PushAsLimb(jointPos, m_Vel, m_WalkAngle[BGROUND], m_Paths[BGROUND][WALK], deltaTime, &restarted, false, Vector(0.0F, m_Paths[BGROUND][WALK].GetLowestY()), m_WalkPathOffset);
} else {
if (m_pBGLeg) { m_pBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pBGLeg->GetParentOffset()), m_pBGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pBGLeg->GetMass(), deltaTime); }
m_ArmClimbing[FGROUND] = false;
Expand Down Expand Up @@ -2389,12 +2397,12 @@ void AHuman::PreControllerUpdate()

if (m_pFGLeg) {
Vector jointPos = m_Pos.GetFloored() + m_pFGLeg->GetParentOffset().GetXFlipped(m_HFlipped);
m_pFGFootGroup->PushAsLimb(jointPos, m_Vel, m_WalkAngle[FGROUND], m_Paths[FGROUND][STAND], deltaTime, nullptr, !m_pBGLeg, Vector(0.0F, m_Paths[FGROUND][STAND].GetLowestY()), Vector(0.0F, -m_WalkPathYOffset));
m_pFGFootGroup->PushAsLimb(jointPos, m_Vel, m_WalkAngle[FGROUND], m_Paths[FGROUND][STAND], deltaTime, nullptr, !m_pBGLeg, Vector(0.0F, m_Paths[FGROUND][STAND].GetLowestY()), m_WalkPathOffset);
}

if (m_pBGLeg) {
Vector jointPos = m_Pos.GetFloored() + m_pBGLeg->GetParentOffset().GetXFlipped(m_HFlipped);
m_pBGFootGroup->PushAsLimb(jointPos, m_Vel, m_WalkAngle[BGROUND], m_Paths[BGROUND][STAND], deltaTime, nullptr, !m_pFGLeg, Vector(0.0F, m_Paths[FGROUND][STAND].GetLowestY()), Vector(0.0F, -m_WalkPathYOffset));
m_pBGFootGroup->PushAsLimb(jointPos, m_Vel, m_WalkAngle[BGROUND], m_Paths[BGROUND][STAND], deltaTime, nullptr, !m_pFGLeg, Vector(0.0F, m_Paths[FGROUND][STAND].GetLowestY()), m_WalkPathOffset);
}
}
}
Expand Down Expand Up @@ -2646,7 +2654,13 @@ void AHuman::Update()
}
} else {
// Upright body posture
float rotDiff = rot - (GetRotAngleTarget(m_MoveState) * (m_AimAngle > 0 ? 1.0F - (m_AimAngle / c_HalfPI) : 1.0F) * GetFlipFactor());
float rotTarget = (GetRotAngleTarget(m_MoveState) * (m_AimAngle > 0 ? 1.0F - (m_AimAngle / c_HalfPI) : 1.0F) * GetFlipFactor());

// Lean forwards when crouching
float crouchAngleAdjust = m_HFlipped ? m_MaxCrouchRotation : -m_MaxCrouchRotation;
rotTarget += LERP(0.0F, m_MaxWalkPathCrouchShift, 0.0F, crouchAngleAdjust, m_WalkPathOffset.m_Y * -1.0F);

float rotDiff = rot - rotTarget;
m_AngularVel = m_AngularVel * (0.98F - 0.06F * (m_Health / m_MaxHealth)) - (rotDiff * 0.5F);
}
}
Expand Down Expand Up @@ -2755,10 +2769,10 @@ void AHuman::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mode,
}

if (mode == g_DrawColor && !onlyPhysical && g_SettingsMan.DrawLimbPathVisualizations()) {
m_Paths[m_HFlipped][WALK].Draw(pTargetBitmap, targetPos, 122);
m_Paths[m_HFlipped][CRAWL].Draw(pTargetBitmap, targetPos, 122);
m_Paths[m_HFlipped][ARMCRAWL].Draw(pTargetBitmap, targetPos, 13);
m_Paths[m_HFlipped][CLIMB].Draw(pTargetBitmap, targetPos, 165);
m_Paths[m_HFlipped][WALK].Draw(pTargetBitmap, targetPos, 122);
m_Paths[m_HFlipped][CRAWL].Draw(pTargetBitmap, targetPos, 122);
m_Paths[m_HFlipped][ARMCRAWL].Draw(pTargetBitmap, targetPos, 13);
m_Paths[m_HFlipped][CLIMB].Draw(pTargetBitmap, targetPos, 165);
}
}

Expand Down
16 changes: 15 additions & 1 deletion Entities/AHuman.h
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,18 @@ DefaultPieMenuNameGetter("Default Human Pie Menu");
/// <param name="newValue">The new value for this AHuman's max walkpath adjustment.</param>
void SetMaxWalkPathCrouchShift(float newValue) { m_MaxWalkPathCrouchShift = newValue; }

/// <summary>
/// Gets this AHuman's max crouch rotation to duck below low ceilings.
/// </summary>
/// <returns>This AHuman's max crouch rotation adjustment.</returns>
float GetMaxCrouchRotation() const { return m_MaxCrouchRotation; }

/// <summary>
/// Sets this AHuman's max crouch rotation to duck below low ceilings.
/// </summary>
/// <param name="newValue">The new value for this AHuman's max crouch rotation adjustment.</param>
void SetMaxCrouchRotation(float newValue) { m_MaxCrouchRotation = newValue; }

/// <summary>
/// Gets this AHuman's stride sound. Ownership is NOT transferred!
/// </summary>
Expand Down Expand Up @@ -949,6 +961,8 @@ DefaultPieMenuNameGetter("Default Human Pie Menu");
Timer m_ProneTimer;
// The maximum amount our walkpath can be shifted upwards to crouch and avoid ceilings above us
float m_MaxWalkPathCrouchShift;
// The maximum amount we will duck our head down to avoid obstacles above us.
float m_MaxCrouchRotation;
// Limb paths for different movement states.
// [0] is for the foreground limbs, and [1] is for BG.
LimbPath m_Paths[2][MOVEMENTSTATECOUNT];
Expand All @@ -972,7 +986,7 @@ DefaultPieMenuNameGetter("Default Human Pie Menu");
float m_BGArmFlailScalar; //!< The rate at which this AHuman's BG Arm follows the the bodily rotation. Set to a negative value for a "counterweight" effect.
Timer m_EquipHUDTimer; //!< Timer for showing the name of any newly equipped Device.
std::array<Matrix, 2> m_WalkAngle; //!< An array of rot angle targets for different movement states.
float m_WalkPathYOffset;
Vector m_WalkPathOffset;
float m_ArmSwingRate; //!< Controls the rate at which this AHuman's Arms follow the movement of its Legs while they're not holding device(s).
float m_DeviceArmSwayRate; //!< Controls the rate at which this AHuman's Arms follow the movement of its Legs while they're holding device(s). One-handed devices sway half as much as two-handed ones. Defaults to three quarters of Arm swing rate.

Expand Down
18 changes: 10 additions & 8 deletions Entities/LimbPath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// Inclusions of header files

#include "LimbPath.h"

#include "PresetMan.h"
#include "SLTerrain.h"

Expand Down Expand Up @@ -189,8 +190,8 @@ int LimbPath::ReadProperty(const std::string_view &propName, Reader &reader)


Vector LimbPath::RotatePoint(const Vector &point) const {
Vector offset = (m_RotationOffset + m_PositionOffset).GetXFlipped(m_HFlipped);
return ((point - offset) * m_Rotation) + offset;
Vector offset = (m_RotationOffset).GetXFlipped(m_HFlipped);
return (((point - offset) * m_Rotation) + offset) + m_PositionOffset;
}

//////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -247,7 +248,7 @@ void LimbPath::Destroy(bool notInherited)

Vector LimbPath::GetProgressPos()
{
Vector returnVec(m_Start + m_PositionOffset);
Vector returnVec(m_Start);
if (IsStaticPoint()) {
return m_JointPos + RotatePoint(returnVec);
}
Expand All @@ -274,7 +275,7 @@ Vector LimbPath::GetProgressPos()

Vector LimbPath::GetCurrentSegTarget()
{
Vector returnVec(m_Start + m_PositionOffset);
Vector returnVec(m_Start);
if (IsStaticPoint()) {
return m_JointPos + RotatePoint(returnVec);
}
Expand Down Expand Up @@ -566,7 +567,7 @@ bool LimbPath::RestartFree(Vector &limbPos, MOID MOIDToIgnore, int ignoreTeam)
if (IsStaticPoint())
{
Vector notUsed;
Vector targetPos = m_JointPos + RotatePoint(m_Start + m_PositionOffset);
Vector targetPos = m_JointPos + RotatePoint(m_Start);
Vector beginPos = targetPos;
// TODO: don't hardcode the beginpos
beginPos.m_Y -= 24;
Expand All @@ -588,14 +589,15 @@ bool LimbPath::RestartFree(Vector &limbPos, MOID MOIDToIgnore, int ignoreTeam)
int i = 0;
for (; i < m_StartSegCount; ++i)
{
result = g_SceneMan.CastObstacleRay(GetProgressPos(), RotatePoint(*m_CurrentSegment + m_PositionOffset), notUsed, limbPos, MOIDToIgnore, ignoreTeam, g_MaterialGrass);
Vector offsetSegment = (*m_CurrentSegment);
result = g_SceneMan.CastObstacleRay(GetProgressPos(), RotatePoint(offsetSegment), notUsed, limbPos, MOIDToIgnore, ignoreTeam, g_MaterialGrass);

// If we found an obstacle after the first pixel, report the current segment as the starting one and that there is free space here
if (result > 0)
{
// Set accurate segment progress
// TODO: See if this is a good idea, or if we should just set it to 0 and set limbPos to the start of current segment
m_SegProgress = g_SceneMan.ShortestDistance(GetProgressPos(), limbPos).GetMagnitude() / (*m_CurrentSegment + m_PositionOffset).GetMagnitude();
m_SegProgress = g_SceneMan.ShortestDistance(GetProgressPos(), limbPos).GetMagnitude() / offsetSegment.GetMagnitude();
limbPos = GetProgressPos();
// m_SegProgress = 0;
m_Ended = false;
Expand Down Expand Up @@ -685,7 +687,7 @@ void LimbPath::Draw(BITMAP *pTargetBitmap,
const Vector &targetPos,
unsigned char color) const
{
Vector prevPoint = m_Start + m_PositionOffset;
Vector prevPoint = m_Start;
Vector nextPoint = prevPoint;
for (std::deque<Vector>::const_iterator itr = m_Segments.begin(); itr != m_Segments.end(); ++itr)
{
Expand Down
1 change: 1 addition & 0 deletions Lua/LuaBindingsEntities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ namespace RTE {
.property("FGFoot", &AHuman::GetFGFoot, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetFGFoot)
.property("BGFoot", &AHuman::GetBGFoot, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetBGFoot)
.property("MaxWalkPathCrouchShift", &AHuman::GetMaxWalkPathCrouchShift, &AHuman::SetMaxWalkPathCrouchShift)
.property("MaxCrouchRotation", &AHuman::GetMaxCrouchRotation, &AHuman::SetMaxCrouchRotation)
.property("StrideSound", &AHuman::GetStrideSound, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetStrideSound)
.property("UpperBodyState", &AHuman::GetUpperBodyState, &AHuman::SetUpperBodyState)
.property("MovementState", &AHuman::GetMovementState, &AHuman::SetMovementState)
Expand Down

0 comments on commit cc0e182

Please sign in to comment.