Skip to content

Commit cc0e182

Browse files
committed
Added crouch rotation
1 parent 7bfdfb4 commit cc0e182

File tree

5 files changed

+57
-24
lines changed

5 files changed

+57
-24
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
8686

8787
- 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.
8888

89+
- 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).
90+
8991
- 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).
9092

9193
- 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).

Entities/AHuman.cpp

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ void AHuman::Clear()
6464
m_ProneState = NOTPRONE;
6565
m_ProneTimer.Reset();
6666
m_MaxWalkPathCrouchShift = 6.0F;
67+
m_MaxCrouchRotation = c_QuarterPI * 1.25F
6768
for (int i = 0; i < MOVEMENTSTATECOUNT; ++i) {
6869
m_Paths[FGROUND][i].Reset();
6970
m_Paths[BGROUND][i].Reset();
@@ -86,7 +87,7 @@ void AHuman::Clear()
8687
m_BGArmFlailScalar = 0.7F;
8788
m_EquipHUDTimer.Reset();
8889
m_WalkAngle.fill(Matrix());
89-
m_WalkPathYOffset = 0.0F;
90+
m_WalkPathOffset.Reset();
9091
m_ArmSwingRate = 1.0F;
9192
m_DeviceArmSwayRate = 0.5F;
9293

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

212213
m_MaxWalkPathCrouchShift = reference.m_MaxWalkPathCrouchShift;
214+
m_MaxCrouchRotation = reference.m_MaxCrouchRotation;
213215

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

@@ -287,9 +289,8 @@ int AHuman::ReadProperty(const std::string_view &propName, Reader &reader) {
287289
m_BackupBGFootGroup = new AtomGroup(*m_pBGFootGroup);
288290
m_BackupBGFootGroup->RemoveAllAtoms();
289291
});
290-
MatchProperty("MaxWalkPathCrouchShift", {
291-
reader >> m_MaxWalkPathCrouchShift;
292-
});
292+
MatchProperty("MaxWalkPathCrouchShift", { reader >> m_MaxWalkPathCrouchShift; });
293+
MatchProperty("MaxCrouchRotation", { reader >> m_MaxCrouchRotation; });
293294
MatchProperty("StrideSound", {
294295
m_StrideSound = new SoundContainer;
295296
reader >> m_StrideSound;
@@ -355,6 +356,8 @@ int AHuman::Save(Writer &writer) const
355356
writer << m_pBGFootGroup;
356357
writer.NewProperty("MaxWalkPathCrouchShift");
357358
writer << m_MaxWalkPathCrouchShift;
359+
writer.NewProperty("MaxCrouchRotation");
360+
writer << m_MaxCrouchRotation;
358361
writer.NewProperty("StrideSound");
359362
writer << m_StrideSound;
360363

@@ -1725,9 +1728,14 @@ void AHuman::UpdateWalkAngle(AHuman::Layer whichLayer) {
17251728
g_SceneMan.CastStrengthRay(hitPosRight, Vector(0.0F, -desiredCrouchHeadRoom), 10.0F, hitPosRight, 0, g_MaterialGrass);
17261729
float headroom = m_pHead->GetPos().m_Y - std::max(hitPosLeft.m_Y, hitPosRight.m_Y);
17271730
float adjust = desiredCrouchHeadRoom - headroom;
1728-
m_WalkPathYOffset = std::clamp(LERP(0.0F, 1.0F, m_WalkPathYOffset, adjust, 0.3F), 0.0F, m_MaxWalkPathCrouchShift);
1731+
float walkPathYOffset = std::clamp(LERP(0.0F, 1.0F, -m_WalkPathOffset.m_Y, adjust, 0.3F), 0.0F, m_MaxWalkPathCrouchShift);
1732+
m_WalkPathOffset.m_Y = -walkPathYOffset;
1733+
1734+
// Adjust our X offset to try to keep our legs under our centre-of-mass
1735+
float predictedPosition = ((m_pHead->GetPos().m_X - m_Pos.m_X) * 0.15F) + m_Vel.m_X;
1736+
m_WalkPathOffset.m_X = predictedPosition;
17291737
} else {
1730-
m_WalkPathYOffset = 0.0f;
1738+
m_WalkPathOffset.Reset();
17311739
}
17321740
}
17331741
}
@@ -2240,7 +2248,7 @@ void AHuman::PreControllerUpdate()
22402248
// Reset the stride timer if the path is about to restart.
22412249
if (m_Paths[FGROUND][WALK].PathEnded() || m_Paths[FGROUND][WALK].PathIsAtStart()) { m_StrideTimer.Reset(); }
22422250
Vector jointPos = m_Pos + RotateOffset(m_pFGLeg->GetParentOffset());
2243-
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));
2251+
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);
22442252
} else {
22452253
m_ArmClimbing[BGROUND] = false;
22462254
}
@@ -2249,7 +2257,7 @@ void AHuman::PreControllerUpdate()
22492257
// Reset the stride timer if the path is about to restart.
22502258
if (m_Paths[BGROUND][WALK].PathEnded() || m_Paths[BGROUND][WALK].PathIsAtStart()) { m_StrideTimer.Reset(); }
22512259
Vector jointPos = m_Pos + RotateOffset(m_pBGLeg->GetParentOffset());
2252-
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));
2260+
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);
22532261
} else {
22542262
if (m_pBGLeg) { m_pBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pBGLeg->GetParentOffset()), m_pBGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pBGLeg->GetMass(), deltaTime); }
22552263
m_ArmClimbing[FGROUND] = false;
@@ -2389,12 +2397,12 @@ void AHuman::PreControllerUpdate()
23892397

23902398
if (m_pFGLeg) {
23912399
Vector jointPos = m_Pos.GetFloored() + m_pFGLeg->GetParentOffset().GetXFlipped(m_HFlipped);
2392-
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));
2400+
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);
23932401
}
23942402

23952403
if (m_pBGLeg) {
23962404
Vector jointPos = m_Pos.GetFloored() + m_pBGLeg->GetParentOffset().GetXFlipped(m_HFlipped);
2397-
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));
2405+
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);
23982406
}
23992407
}
24002408
}
@@ -2646,7 +2654,13 @@ void AHuman::Update()
26462654
}
26472655
} else {
26482656
// Upright body posture
2649-
float rotDiff = rot - (GetRotAngleTarget(m_MoveState) * (m_AimAngle > 0 ? 1.0F - (m_AimAngle / c_HalfPI) : 1.0F) * GetFlipFactor());
2657+
float rotTarget = (GetRotAngleTarget(m_MoveState) * (m_AimAngle > 0 ? 1.0F - (m_AimAngle / c_HalfPI) : 1.0F) * GetFlipFactor());
2658+
2659+
// Lean forwards when crouching
2660+
float crouchAngleAdjust = m_HFlipped ? m_MaxCrouchRotation : -m_MaxCrouchRotation;
2661+
rotTarget += LERP(0.0F, m_MaxWalkPathCrouchShift, 0.0F, crouchAngleAdjust, m_WalkPathOffset.m_Y * -1.0F);
2662+
2663+
float rotDiff = rot - rotTarget;
26502664
m_AngularVel = m_AngularVel * (0.98F - 0.06F * (m_Health / m_MaxHealth)) - (rotDiff * 0.5F);
26512665
}
26522666
}
@@ -2755,10 +2769,10 @@ void AHuman::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mode,
27552769
}
27562770

27572771
if (mode == g_DrawColor && !onlyPhysical && g_SettingsMan.DrawLimbPathVisualizations()) {
2758-
m_Paths[m_HFlipped][WALK].Draw(pTargetBitmap, targetPos, 122);
2759-
m_Paths[m_HFlipped][CRAWL].Draw(pTargetBitmap, targetPos, 122);
2760-
m_Paths[m_HFlipped][ARMCRAWL].Draw(pTargetBitmap, targetPos, 13);
2761-
m_Paths[m_HFlipped][CLIMB].Draw(pTargetBitmap, targetPos, 165);
2772+
m_Paths[m_HFlipped][WALK].Draw(pTargetBitmap, targetPos, 122);
2773+
m_Paths[m_HFlipped][CRAWL].Draw(pTargetBitmap, targetPos, 122);
2774+
m_Paths[m_HFlipped][ARMCRAWL].Draw(pTargetBitmap, targetPos, 13);
2775+
m_Paths[m_HFlipped][CLIMB].Draw(pTargetBitmap, targetPos, 165);
27622776
}
27632777
}
27642778

Entities/AHuman.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,18 @@ DefaultPieMenuNameGetter("Default Human Pie Menu");
875875
/// <param name="newValue">The new value for this AHuman's max walkpath adjustment.</param>
876876
void SetMaxWalkPathCrouchShift(float newValue) { m_MaxWalkPathCrouchShift = newValue; }
877877

878+
/// <summary>
879+
/// Gets this AHuman's max crouch rotation to duck below low ceilings.
880+
/// </summary>
881+
/// <returns>This AHuman's max crouch rotation adjustment.</returns>
882+
float GetMaxCrouchRotation() const { return m_MaxCrouchRotation; }
883+
884+
/// <summary>
885+
/// Sets this AHuman's max crouch rotation to duck below low ceilings.
886+
/// </summary>
887+
/// <param name="newValue">The new value for this AHuman's max crouch rotation adjustment.</param>
888+
void SetMaxCrouchRotation(float newValue) { m_MaxCrouchRotation = newValue; }
889+
878890
/// <summary>
879891
/// Gets this AHuman's stride sound. Ownership is NOT transferred!
880892
/// </summary>
@@ -949,6 +961,8 @@ DefaultPieMenuNameGetter("Default Human Pie Menu");
949961
Timer m_ProneTimer;
950962
// The maximum amount our walkpath can be shifted upwards to crouch and avoid ceilings above us
951963
float m_MaxWalkPathCrouchShift;
964+
// The maximum amount we will duck our head down to avoid obstacles above us.
965+
float m_MaxCrouchRotation;
952966
// Limb paths for different movement states.
953967
// [0] is for the foreground limbs, and [1] is for BG.
954968
LimbPath m_Paths[2][MOVEMENTSTATECOUNT];
@@ -972,7 +986,7 @@ DefaultPieMenuNameGetter("Default Human Pie Menu");
972986
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.
973987
Timer m_EquipHUDTimer; //!< Timer for showing the name of any newly equipped Device.
974988
std::array<Matrix, 2> m_WalkAngle; //!< An array of rot angle targets for different movement states.
975-
float m_WalkPathYOffset;
989+
Vector m_WalkPathOffset;
976990
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).
977991
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.
978992

Entities/LimbPath.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// Inclusions of header files
1313

1414
#include "LimbPath.h"
15+
1516
#include "PresetMan.h"
1617
#include "SLTerrain.h"
1718

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

190191

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

196197
//////////////////////////////////////////////////////////////////////////////////////////
@@ -247,7 +248,7 @@ void LimbPath::Destroy(bool notInherited)
247248

248249
Vector LimbPath::GetProgressPos()
249250
{
250-
Vector returnVec(m_Start + m_PositionOffset);
251+
Vector returnVec(m_Start);
251252
if (IsStaticPoint()) {
252253
return m_JointPos + RotatePoint(returnVec);
253254
}
@@ -274,7 +275,7 @@ Vector LimbPath::GetProgressPos()
274275

275276
Vector LimbPath::GetCurrentSegTarget()
276277
{
277-
Vector returnVec(m_Start + m_PositionOffset);
278+
Vector returnVec(m_Start);
278279
if (IsStaticPoint()) {
279280
return m_JointPos + RotatePoint(returnVec);
280281
}
@@ -566,7 +567,7 @@ bool LimbPath::RestartFree(Vector &limbPos, MOID MOIDToIgnore, int ignoreTeam)
566567
if (IsStaticPoint())
567568
{
568569
Vector notUsed;
569-
Vector targetPos = m_JointPos + RotatePoint(m_Start + m_PositionOffset);
570+
Vector targetPos = m_JointPos + RotatePoint(m_Start);
570571
Vector beginPos = targetPos;
571572
// TODO: don't hardcode the beginpos
572573
beginPos.m_Y -= 24;
@@ -588,14 +589,15 @@ bool LimbPath::RestartFree(Vector &limbPos, MOID MOIDToIgnore, int ignoreTeam)
588589
int i = 0;
589590
for (; i < m_StartSegCount; ++i)
590591
{
591-
result = g_SceneMan.CastObstacleRay(GetProgressPos(), RotatePoint(*m_CurrentSegment + m_PositionOffset), notUsed, limbPos, MOIDToIgnore, ignoreTeam, g_MaterialGrass);
592+
Vector offsetSegment = (*m_CurrentSegment);
593+
result = g_SceneMan.CastObstacleRay(GetProgressPos(), RotatePoint(offsetSegment), notUsed, limbPos, MOIDToIgnore, ignoreTeam, g_MaterialGrass);
592594

593595
// If we found an obstacle after the first pixel, report the current segment as the starting one and that there is free space here
594596
if (result > 0)
595597
{
596598
// Set accurate segment progress
597599
// 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
598-
m_SegProgress = g_SceneMan.ShortestDistance(GetProgressPos(), limbPos).GetMagnitude() / (*m_CurrentSegment + m_PositionOffset).GetMagnitude();
600+
m_SegProgress = g_SceneMan.ShortestDistance(GetProgressPos(), limbPos).GetMagnitude() / offsetSegment.GetMagnitude();
599601
limbPos = GetProgressPos();
600602
// m_SegProgress = 0;
601603
m_Ended = false;
@@ -685,7 +687,7 @@ void LimbPath::Draw(BITMAP *pTargetBitmap,
685687
const Vector &targetPos,
686688
unsigned char color) const
687689
{
688-
Vector prevPoint = m_Start + m_PositionOffset;
690+
Vector prevPoint = m_Start;
689691
Vector nextPoint = prevPoint;
690692
for (std::deque<Vector>::const_iterator itr = m_Segments.begin(); itr != m_Segments.end(); ++itr)
691693
{

Lua/LuaBindingsEntities.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,7 @@ namespace RTE {
461461
.property("FGFoot", &AHuman::GetFGFoot, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetFGFoot)
462462
.property("BGFoot", &AHuman::GetBGFoot, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetBGFoot)
463463
.property("MaxWalkPathCrouchShift", &AHuman::GetMaxWalkPathCrouchShift, &AHuman::SetMaxWalkPathCrouchShift)
464+
.property("MaxCrouchRotation", &AHuman::GetMaxCrouchRotation, &AHuman::SetMaxCrouchRotation)
464465
.property("StrideSound", &AHuman::GetStrideSound, &LuaAdaptersPropertyOwnershipSafetyFaker::AHumanSetStrideSound)
465466
.property("UpperBodyState", &AHuman::GetUpperBodyState, &AHuman::SetUpperBodyState)
466467
.property("MovementState", &AHuman::GetMovementState, &AHuman::SetMovementState)

0 commit comments

Comments
 (0)