From 7aebffe5d98636ff03bda7873a6b82261d5e92a1 Mon Sep 17 00:00:00 2001 From: Gareth YR Date: Mon, 5 Jul 2021 01:10:03 -0300 Subject: [PATCH 01/72] Unbroke all the mods :P Added flag for whether or not either throttle was redefined and, if it wasn't, defaulted it to 0 for AHumans' and ACrabs' jetpacks. Added setter for throttle values, and renamed the getters to be more consistent (though the throttle variables and getters and setters all need to be renamed at some point, there's an issue in for that) Added lua bindings for min and max throttle range. They've got garbage names because I don't want them bound til we get a better name for this stuff --- Entities/ACrab.cpp | 9 +++++++-- Entities/AEmitter.cpp | 4 ++++ Entities/AEmitter.h | 23 +++++++++++++++++++++-- Entities/AHuman.cpp | 9 +++++++-- Managers/LuaMan.cpp | 2 ++ 5 files changed, 41 insertions(+), 6 deletions(-) diff --git a/Entities/ACrab.cpp b/Entities/ACrab.cpp index 7c74e2bf7..dee2bcb4d 100644 --- a/Entities/ACrab.cpp +++ b/Entities/ACrab.cpp @@ -505,6 +505,11 @@ void ACrab::SetJetpack(AEmitter *newJetpack) { if (m_pJetpack->HasNoSetDamageMultiplier()) { m_pJetpack->SetDamageMultiplier(0.0F); } m_pJetpack->SetApplyTransferredForcesAtOffset(false); m_pJetpack->SetDeleteWhenRemovedFromParent(true); + + if (!m_pJetpack->GetThrottleRangeRedefined()) { + m_pJetpack->SetMinThrottleRange(0); + m_pJetpack->SetMaxThrottleRange(0); + } } } @@ -2156,8 +2161,8 @@ void ACrab::Update() // Jetpack throttle depletes relative to jet time, but only if throttle range values have been defined float jetTimeRatio = std::max(m_JetTimeLeft / m_JetTimeTotal, 0.0F); m_pJetpack->SetThrottle(jetTimeRatio * 2.0F - 1.0F); - float minScale = 1.0F - m_pJetpack->GetMinThrottle(); - m_pJetpack->SetFlashScale(minScale + (1.0F + m_pJetpack->GetMaxThrottle() - minScale) * jetTimeRatio); + float minScale = 1.0F - m_pJetpack->GetMinThrottleRange(); + m_pJetpack->SetFlashScale(minScale + (1.0F + m_pJetpack->GetMaxThrottleRange() - minScale) * jetTimeRatio); } // Start Jetpack burn if (m_Controller.IsState(BODY_JUMPSTART) && m_JetTimeLeft > 0 && m_Status != INACTIVE) diff --git a/Entities/AEmitter.cpp b/Entities/AEmitter.cpp index 3e0a661d5..6af479e6b 100644 --- a/Entities/AEmitter.cpp +++ b/Entities/AEmitter.cpp @@ -40,6 +40,7 @@ void AEmitter::Clear() m_EmitCountLimit = 0; m_MinThrottleRange = 1.0F; m_MaxThrottleRange = 1.0F; + m_ThrottleRangeRedefined = false; m_Throttle = 0; m_EmissionsIgnoreThis = false; m_BurstScale = 1.0F; @@ -87,6 +88,7 @@ int AEmitter::Create(const AEmitter &reference) { m_EmitCountLimit = reference.m_EmitCountLimit; m_MinThrottleRange = reference.m_MinThrottleRange; m_MaxThrottleRange = reference.m_MaxThrottleRange; + m_ThrottleRangeRedefined = reference.m_ThrottleRangeRedefined; m_Throttle = reference.m_Throttle; m_EmissionsIgnoreThis = reference.m_EmissionsIgnoreThis; m_BurstScale = reference.m_BurstScale; @@ -140,8 +142,10 @@ int AEmitter::ReadProperty(const std::string_view &propName, Reader &reader) { for (Emission *emission : m_EmissionList) { emission->m_PPM = ppm / static_cast(m_EmissionList.size()); } } else if (propName == "MinThrottleRange") { reader >> m_MinThrottleRange; + m_ThrottleRangeRedefined = true; } else if (propName == "MaxThrottleRange") { reader >> m_MaxThrottleRange; + m_ThrottleRangeRedefined = true; } else if (propName == "Throttle") { reader >> m_Throttle; } else if (propName == "EmissionsIgnoreThis") { diff --git a/Entities/AEmitter.h b/Entities/AEmitter.h index 9676fc423..a333273a4 100644 --- a/Entities/AEmitter.h +++ b/Entities/AEmitter.h @@ -266,13 +266,31 @@ ClassInfoGetters /// Gets the minimum throttle range of this AEmitter. /// /// The minimum throttle range of this AEmitter. - float GetMinThrottle() const { return m_MinThrottleRange; } + float GetMinThrottleRange() const { return m_MinThrottleRange; } + + /// + /// Sets the minimum throttle range for this AEmitter. + /// + /// The new minimum throttle range for this AEmitter. + void SetMinThrottleRange(float minThrottleRange) { m_MinThrottleRange = minThrottleRange; m_ThrottleRangeRedefined = true; } /// /// Gets the maximum throttle range of this AEmitter. /// /// The maximum throttle range of this AEmitter. - float GetMaxThrottle() const { return m_MaxThrottleRange; } + float GetMaxThrottleRange() const { return m_MaxThrottleRange; } + + /// + /// Sets the maximum throttle range for this AEmitter. + /// + /// The new maximum throttle range for this AEmitter. + void SetMaxThrottleRange(float maxThrottleRange) { m_MaxThrottleRange = maxThrottleRange; m_ThrottleRangeRedefined = true; } + + /// + /// Gets whether or not the MinThrottleRange or MaxThrottleRange of this AEmitter have been redefined in INI. + /// + /// Whether or not the MinThrottleRange or MaxThrottleRange have been redefined in INI. + bool GetThrottleRangeRedefined() const { return m_ThrottleRangeRedefined; } /* ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetEmitRate @@ -634,6 +652,7 @@ ClassInfoGetters long m_EmitCountLimit; float m_MinThrottleRange; //!< The range negative throttle has on emission rate. 1.0 means the rate can be throttled down to 0%, 0 means negative throttle has no effect float m_MaxThrottleRange; //!< The range positive throttle has on emission rate. 1.0 means the rate can be throttled up to 200%, 0 means positive throttle has no effect + bool m_ThrottleRangeRedefined; //!< Whether or not the MinThrottleRange or MaxThrottleRange has been redefined in INI. float m_Throttle; //!< The normalized throttle which controls the MSPE between 1.0 * m_MSPERange and -1.0 * m_MSPERange. 0 means emit the regular m_PPM amount. // Whether or not this' emissions ignore hits with itself, even if they are set to hit other MOs. bool m_EmissionsIgnoreThis; diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index fceaca0b7..c91492a18 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -553,6 +553,11 @@ void AHuman::SetJetpack(AEmitter *newJetpack) { if (m_pJetpack->HasNoSetDamageMultiplier()) { m_pJetpack->SetDamageMultiplier(0.0F); } m_pJetpack->SetApplyTransferredForcesAtOffset(false); + + if (!m_pJetpack->GetThrottleRangeRedefined()) { + m_pJetpack->SetMinThrottleRange(0); + m_pJetpack->SetMaxThrottleRange(0); + } } } @@ -3166,8 +3171,8 @@ void AHuman::Update() // Jetpack throttle depletes relative to jet time, but only if throttle range values have been defined float jetTimeRatio = std::max(m_JetTimeLeft / m_JetTimeTotal, 0.0F); m_pJetpack->SetThrottle(jetTimeRatio * 2.0F - 1.0F); - float minScale = 1.0F - m_pJetpack->GetMinThrottle(); - m_pJetpack->SetFlashScale(minScale + (1.0F + m_pJetpack->GetMaxThrottle() - minScale) * jetTimeRatio); + float minScale = 1.0F - m_pJetpack->GetMinThrottleRange(); + m_pJetpack->SetFlashScale(minScale + (1.0F + m_pJetpack->GetMaxThrottleRange() - minScale) * jetTimeRatio); } // Start Jetpack burn if (m_Controller.IsState(BODY_JUMPSTART) && m_JetTimeLeft > 0 && m_Status != INACTIVE) diff --git a/Managers/LuaMan.cpp b/Managers/LuaMan.cpp index 8b5d5ddb9..d25cb3be9 100644 --- a/Managers/LuaMan.cpp +++ b/Managers/LuaMan.cpp @@ -1020,6 +1020,8 @@ int LuaMan::Initialize() { .property("EmitAngle", &AEmitter::GetEmitAngle, &AEmitter::SetEmitAngle) .property("GetThrottle", &AEmitter::GetThrottle, &AEmitter::SetThrottle) .property("Throttle", &AEmitter::GetThrottle, &AEmitter::SetThrottle) + .property("MinThrottleRangeTODORENAME", &AEmitter::GetMinThrottleRange, &AEmitter::SetMinThrottleRange) + .property("MaxThrottleRangeTODORENAME", &AEmitter::GetMaxThrottleRange, &AEmitter::SetMaxThrottleRange) .property("BurstSpacing", &AEmitter::GetBurstSpacing, &AEmitter::SetBurstSpacing) .property("BurstDamage", &AEmitter::GetBurstDamage, &AEmitter::SetBurstDamage) .property("EmitterDamageMultiplier", &AEmitter::GetEmitterDamageMultiplier, &AEmitter::SetEmitterDamageMultiplier) From 1883cb236f0a644ffb421cda2336737c2fce1609 Mon Sep 17 00:00:00 2001 From: Gareth YR Date: Mon, 5 Jul 2021 01:16:15 -0300 Subject: [PATCH 02/72] Garbage changelog entry so it's not forgotten about --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6fe017e0..d98ace530 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -153,6 +153,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Added `MovableObject` Lua function `EnableOrDisableAllScripts` that allows you to enable or disable all scripts on a `MovableObject` based on the passed in value. +WARNING FIX THIS LINE BEFORE MERGING PR!!!!!!!!!!!! +- Added `AEmitter` Lua properties `MinThrottleRangeTODOFIXNAMEHERE` and `MaxThrottleRangeTODOFIXNAMEHERE` that allow you to 4zK PLEASE FILL IN + ### Changed - `AHuman` can now manually reload BG devices. From e54696e791212e2bad656281070bfcbc595842b1 Mon Sep 17 00:00:00 2001 From: fourZK Date: Mon, 16 Aug 2021 17:36:26 +0300 Subject: [PATCH 03/72] Fix up cpp patrol AI and allow it to detect ledges in terrain --- Entities/ACrab.cpp | 15 +++++++-------- Entities/AHuman.cpp | 15 +++++++-------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/Entities/ACrab.cpp b/Entities/ACrab.cpp index a20d10bc0..1c6277d09 100644 --- a/Entities/ACrab.cpp +++ b/Entities/ACrab.cpp @@ -1112,14 +1112,13 @@ void ACrab::UpdateAI() } else { - // Stop and then turn around after a period of time, or if bumped into another actor (like a rocket) - if (m_PatrolTimer.IsPastSimMS(8000) || - /*g_SceneMan.CastNotMaterialRay(m_Pos, Vector(m_CharHeight / 4, 0), g_MaterialAir, Vector(), 4, false)*/ - g_SceneMan.CastMORay(m_Pos, Vector((m_LateralMoveState == LAT_RIGHT ? m_CharHeight : -m_CharHeight) / 3, 0), m_MOID, IgnoresWhichTeam(), g_MaterialGrass, false, 4) != g_NoMOID) - { - m_PatrolTimer.Reset(); - m_LateralMoveState = LAT_STILL; - } + Vector hitPos; + Vector trace((m_LateralMoveState == LAT_RIGHT ? GetRadius() : -GetRadius()) * 0.5F, 0); + // Stop and turn around after a period of time, or if bumped into another actor (like a rocket), or if walking off a ledge. + if (m_PatrolTimer.IsPastSimMS(8000) || g_SceneMan.CastMORay(m_Pos, trace, m_MOID, IgnoresWhichTeam(), g_MaterialGrass, false, 5) != g_NoMOID || !g_SceneMan.CastStrengthRay(m_Pos + trace, Vector(0, GetRadius()), 5.0F, hitPos, 5, g_MaterialGrass)) { + m_PatrolTimer.Reset(); + m_LateralMoveState = LAT_STILL; + } } } // Going to a goal, potentially through a set of waypoints diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index 333059116..c21e40110 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -1901,14 +1901,13 @@ void AHuman::UpdateAI() } else { - // Stop and then turn around after a period of time, or if bumped into another actor (like a rocket) - if (m_PatrolTimer.IsPastSimMS(8000) || - /*g_SceneMan.CastNotMaterialRay(m_Pos, Vector(m_CharHeight / 4, 0), g_MaterialAir, Vector(), 4, false)*/ - g_SceneMan.CastMORay(m_Pos, Vector((m_LateralMoveState == LAT_RIGHT ? m_CharHeight : -m_CharHeight) / 3, 0), m_MOID, IgnoresWhichTeam(), g_MaterialGrass, false, 4) != g_NoMOID) - { - m_PatrolTimer.Reset(); - m_LateralMoveState = LAT_STILL; - } + Vector hitPos; + Vector trace((m_LateralMoveState == LAT_RIGHT ? GetRadius() : -GetRadius()) * 0.5F, 0); + // Stop and turn around after a period of time, or if bumped into another actor (like a rocket), or if walking off a ledge. + if (m_PatrolTimer.IsPastSimMS(8000) || g_SceneMan.CastMORay(m_Pos, trace, m_MOID, IgnoresWhichTeam(), g_MaterialGrass, false, 5) != g_NoMOID || !g_SceneMan.CastStrengthRay(m_Pos + trace, Vector(0, GetRadius()), 5.0F, hitPos, 5, g_MaterialGrass)) { + m_PatrolTimer.Reset(); + m_LateralMoveState = LAT_STILL; + } } } // Going to a goal, potentially through a set of waypoints From 4aede17a19301b220b1dc07de7946c77e890b8b8 Mon Sep 17 00:00:00 2001 From: fourZK Date: Mon, 16 Aug 2021 21:12:39 +0300 Subject: [PATCH 04/72] Allowed keyboard-only controls to strafe by sharp aiming and fixed a bug where actors would turn around for one frame before starting to strafe --- Entities/ACrab.cpp | 38 +++------- Entities/AHuman.cpp | 157 ++++++++++++++++-------------------------- System/Controller.cpp | 2 +- 3 files changed, 70 insertions(+), 127 deletions(-) diff --git a/Entities/ACrab.cpp b/Entities/ACrab.cpp index 1c6277d09..a98e12172 100644 --- a/Entities/ACrab.cpp +++ b/Entities/ACrab.cpp @@ -2109,6 +2109,7 @@ void ACrab::Update() { float deltaTime = g_TimerMan.GetDeltaTimeSecs(); float mass = GetMass(); + Vector analogAim = m_Controller.GetAnalogAim(); // Set Default direction of all the paths! for (int side = 0; side < SIDECOUNT; ++side) @@ -2204,22 +2205,14 @@ void ACrab::Update() } } - // Walk backwards if the aiming is done in the opposite direction of travel - if (fabs(m_Controller.GetAnalogAim().m_X) > 0.1) - { - // Walk backwards if necessary - for (int side = 0; side < SIDECOUNT; ++side) - { + // Walk backwards if the aiming is already focused in the opposite direction of travel. + if (std::abs(analogAim.m_X) > 0 || m_Controller.IsState(AIM_SHARP)) { + for (int side = 0; side < SIDECOUNT; ++side) { m_Paths[side][FGROUND][m_MoveState].SetHFlip(m_Controller.IsState(MOVE_LEFT)); m_Paths[side][BGROUND][m_MoveState].SetHFlip(m_Controller.IsState(MOVE_LEFT)); } - } - // Flip if we're moving in the opposite direction - else if ((m_Controller.IsState(MOVE_RIGHT) && m_HFlipped) || (m_Controller.IsState(MOVE_LEFT) && !m_HFlipped)) - { + } else if ((m_Controller.IsState(MOVE_RIGHT) && m_HFlipped) || (m_Controller.IsState(MOVE_LEFT) && !m_HFlipped)) { m_HFlipped = !m_HFlipped; -// // Instead of simply carving out a silhouette of the now flipped actor, isntead disable any atoms which are embedded int eh terrain until they emerge again -// m_ForceDeepCheck = true; m_CheckTerrIntersection = true; MoveOutOfTerrain(g_MaterialGrass); for (int side = 0; side < SIDECOUNT; ++side) @@ -2260,34 +2253,25 @@ void ACrab::Update() float adjustedAimRangeUpperLimit = (m_HFlipped) ? m_AimRangeUpperLimit - rotAngle : m_AimRangeUpperLimit + rotAngle; float adjustedAimRangeLowerLimit = (m_HFlipped) ? -m_AimRangeLowerLimit - rotAngle : -m_AimRangeLowerLimit + rotAngle; - if (m_Controller.IsState(AIM_UP)) - { + if (m_Controller.IsState(AIM_UP) && m_Status != INACTIVE) { // Set the timer to some base number so we don't get a sluggish feeling at start of aim if (m_AimState != AIMUP) m_AimTmr.SetElapsedSimTimeMS(150); m_AimState = AIMUP; m_AimAngle += m_Controller.IsState(AIM_SHARP) ? MIN(m_AimTmr.GetElapsedSimTimeMS() * 0.00005, 0.05) : MIN(m_AimTmr.GetElapsedSimTimeMS() * 0.00015, 0.1); - } - else if (m_Controller.IsState(AIM_DOWN)) - { + } else if (m_Controller.IsState(AIM_DOWN) && m_Status != INACTIVE) { // Set the timer to some base number so we don't get a sluggish feeling at start of aim if (m_AimState != AIMDOWN) m_AimTmr.SetElapsedSimTimeMS(150); m_AimState = AIMDOWN; m_AimAngle -= m_Controller.IsState(AIM_SHARP) ? MIN(m_AimTmr.GetElapsedSimTimeMS() * 0.00005, 0.05) : MIN(m_AimTmr.GetElapsedSimTimeMS() * 0.00015, 0.1); - } - // Analog aim - else if (m_Controller.GetAnalogAim().GetMagnitude() > 0.1) - { - Vector aim = m_Controller.GetAnalogAim(); + } else if (analogAim.GetMagnitude() > 0.1F && m_Status != INACTIVE) { // Hack to avoid the GetAbsRadAngle to mangle an aim angle straight down - if (aim.m_X == 0) - aim.m_X += m_HFlipped ? -0.01 : 0.01; - m_AimAngle = aim.GetAbsRadAngle(); + if (analogAim.m_X == 0) { analogAim.m_X += 0.01F * GetFlipFactor(); } + m_AimAngle = analogAim.GetAbsRadAngle(); // Check for flip change - if ((aim.m_X > 0 && m_HFlipped) || (aim.m_X < 0 && !m_HFlipped)) - { + if ((analogAim.m_X > 0 && m_HFlipped) || (analogAim.m_X < 0 && !m_HFlipped)) { m_HFlipped = !m_HFlipped; // Instead of simply carving out a silhouette of the now flipped actor, isntead disable any atoms which are embedded int eh terrain until they emerge again //m_ForceDeepCheck = true; diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index c21e40110..2e152a9e0 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -3103,22 +3103,11 @@ int AHuman::OnPieMenu(Actor *pieMenuActor) { void AHuman::Update() { float deltaTime = g_TimerMan.GetDeltaTimeSecs(); - // Get the rotation in radians. float rot = m_Rotation.GetRadAngle(); + Vector analogAim = m_Controller.GetAnalogAim(); - // Set Default direction of all the paths! - m_Paths[FGROUND][WALK].SetHFlip(m_HFlipped); - m_Paths[BGROUND][WALK].SetHFlip(m_HFlipped); - m_Paths[FGROUND][CROUCH].SetHFlip(m_HFlipped); - m_Paths[BGROUND][CROUCH].SetHFlip(m_HFlipped); - m_Paths[FGROUND][CRAWL].SetHFlip(m_HFlipped); - m_Paths[BGROUND][CRAWL].SetHFlip(m_HFlipped); - m_Paths[FGROUND][ARMCRAWL].SetHFlip(m_HFlipped); - m_Paths[BGROUND][ARMCRAWL].SetHFlip(m_HFlipped); - m_Paths[FGROUND][CLIMB].SetHFlip(m_HFlipped); - m_Paths[BGROUND][CLIMB].SetHFlip(m_HFlipped); - m_Paths[FGROUND][STAND].SetHFlip(m_HFlipped); - m_Paths[BGROUND][STAND].SetHFlip(m_HFlipped); + m_Paths[FGROUND][m_MoveState].SetHFlip(m_HFlipped); + m_Paths[BGROUND][m_MoveState].SetHFlip(m_HFlipped); //////////////////////////////////// // Jetpack activation and blast direction @@ -3184,85 +3173,63 @@ void AHuman::Update() //////////////////////////////////// // Movement direction - // If the pie menu is on, try to preserve whatever move state we had before it going into effect - if (m_Controller.IsState(PIE_MENU_ACTIVE)) - { - // Just keep the previous movestate, don't stand up or stop walking or stop jumping - } - else if (m_Controller.IsState(MOVE_RIGHT) || m_Controller.IsState(MOVE_LEFT) || m_MoveState == JUMP && m_Status != INACTIVE) - { - // Only if not jumping, OR if jumping, and apparently stuck on something - then help out with the limbs - if (m_MoveState != JUMP || (m_MoveState == JUMP && m_Vel.GetLargest() < 0.1)) - { - // Restart the stride if we're just starting to walk or crawl - if ((m_MoveState != WALK && !m_Controller.IsState(BODY_CROUCH)) || - (m_MoveState != CRAWL && m_Controller.IsState(BODY_CROUCH))) - { - m_StrideStart = true; - MoveOutOfTerrain(g_MaterialGrass); + // If the pie menu is on, try to preserve whatever move state we had before it going into effect. + if (!m_Controller.IsState(PIE_MENU_ACTIVE)) { + if (m_Controller.IsState(MOVE_RIGHT) || m_Controller.IsState(MOVE_LEFT) || m_MoveState == JUMP && m_Status != INACTIVE) { + for (int i = WALK; i < MOVEMENTSTATECOUNT; ++i) { + m_Paths[FGROUND][i].SetHFlip(m_HFlipped); + m_Paths[BGROUND][i].SetHFlip(m_HFlipped); } + // Only if not jumping, OR if jumping, and apparently stuck on something - then help out with the limbs. + if (m_MoveState != JUMP || m_Vel.GetLargest() < 0.1F) { + // Restart the stride if we're just starting to walk or crawl. + if ((m_MoveState != WALK && !m_Controller.IsState(BODY_CROUCH)) || (m_MoveState != CRAWL && m_Controller.IsState(BODY_CROUCH))) { + m_StrideStart = true; + MoveOutOfTerrain(g_MaterialGrass); + } - // Crawling or walking? - m_MoveState = m_Controller.IsState(BODY_CROUCH) ? CRAWL : WALK; + m_MoveState = m_Controller.IsState(BODY_CROUCH) ? CRAWL : WALK; - // Engage prone state, this makes the body's rotational spring pull it horizontal instead of upright - if (m_MoveState == CRAWL && m_ProneState == NOTPRONE) - { - m_ProneState = GOPRONE; - m_ProneTimer.Reset(); - } + // Engage prone state, this makes the body's rotational spring pull it horizontal instead of upright. + if (m_MoveState == CRAWL && m_ProneState == NOTPRONE) { + m_ProneState = GOPRONE; + m_ProneTimer.Reset(); + } - m_Paths[FGROUND][m_MoveState].SetSpeed(m_Controller.IsState(MOVE_FAST) ? FAST : NORMAL); - m_Paths[BGROUND][m_MoveState].SetSpeed(m_Controller.IsState(MOVE_FAST) ? FAST : NORMAL); - } + m_Paths[FGROUND][m_MoveState].SetSpeed(m_Controller.IsState(MOVE_FAST) ? FAST : NORMAL); + m_Paths[BGROUND][m_MoveState].SetSpeed(m_Controller.IsState(MOVE_FAST) ? FAST : NORMAL); + } - // Walk backwards if the aiming is done in the opposite direction of travel - if (std::abs(m_Controller.GetAnalogAim().m_X) > 0.1F) - { - // Walk backwards if necessary - m_Paths[FGROUND][m_MoveState].SetHFlip(m_Controller.IsState(MOVE_LEFT)); - m_Paths[BGROUND][m_MoveState].SetHFlip(m_Controller.IsState(MOVE_LEFT)); - } - // Flip if we're moving in the opposite direction - else if ((m_Controller.IsState(MOVE_RIGHT) && m_HFlipped) || (m_Controller.IsState(MOVE_LEFT) && !m_HFlipped)) - { - m_HFlipped = !m_HFlipped; - // // Instead of simply carving out a silhouette of the now flipped actor, isntead disable any atoms which are embedded int eh terrain until they emerge again - // m_ForceDeepCheck = true; - m_CheckTerrIntersection = true; - if (m_ProneState == NOTPRONE) { MoveOutOfTerrain(g_MaterialGrass); } - m_Paths[FGROUND][m_MoveState].SetHFlip(m_HFlipped); - m_Paths[BGROUND][m_MoveState].SetHFlip(m_HFlipped); - - m_Paths[FGROUND][WALK].Terminate(); - m_Paths[BGROUND][WALK].Terminate(); - m_Paths[FGROUND][CROUCH].Terminate(); - m_Paths[BGROUND][CROUCH].Terminate(); - m_Paths[FGROUND][CLIMB].Terminate(); - m_Paths[BGROUND][CLIMB].Terminate(); - m_Paths[FGROUND][CRAWL].Terminate(); - m_Paths[BGROUND][CRAWL].Terminate(); - m_Paths[FGROUND][ARMCRAWL].Terminate(); - m_Paths[BGROUND][ARMCRAWL].Terminate(); - m_Paths[FGROUND][STAND].Terminate(); - m_Paths[BGROUND][STAND].Terminate(); - m_StrideStart = true; - // Stop the going prone spring - if (m_ProneState == GOPRONE) { m_ProneState = PRONE; } + // Walk backwards if the aiming is already focused in the opposite direction of travel. + if (std::abs(analogAim.m_X) > 0 || m_Controller.IsState(AIM_SHARP)) { + m_Paths[FGROUND][m_MoveState].SetHFlip(m_Controller.IsState(MOVE_LEFT)); + m_Paths[BGROUND][m_MoveState].SetHFlip(m_Controller.IsState(MOVE_LEFT)); + } else if ((m_Controller.IsState(MOVE_RIGHT) && m_HFlipped) || (m_Controller.IsState(MOVE_LEFT) && !m_HFlipped)) { + m_HFlipped = !m_HFlipped; + m_CheckTerrIntersection = true; + if (m_ProneState == NOTPRONE) { MoveOutOfTerrain(g_MaterialGrass); } + + for (int i = WALK; i < MOVEMENTSTATECOUNT; ++i) { + m_Paths[FGROUND][i].SetHFlip(m_HFlipped); + m_Paths[BGROUND][i].SetHFlip(m_HFlipped); + m_Paths[FGROUND][i].Terminate(); + m_Paths[BGROUND][i].Terminate(); + } + m_StrideStart = true; + // Stop the going prone spring. + if (m_ProneState == GOPRONE) { m_ProneState = PRONE; } + } + // Not moving, so check if we need to be crouched or not. + } else if (m_Controller.IsState(BODY_CROUCH)) { + // Don't go back to crouching if we're already prone, the player has to let go of the crouch button first. If already laying down, just stay put. + m_MoveState = m_ProneState == NOTPRONE ? CROUCH : NOMOVE; + } else { + m_MoveState = STAND; } - // Not moving, so check if we need to be crouched or not - } else if (m_Controller.IsState(BODY_CROUCH)) { - // Don't go back to crouching if we're already prone, player has to let go of the crouch button first - // If already laying down, just don't do anything and keep laying there - m_MoveState = m_ProneState == NOTPRONE ? CROUCH : NOMOVE; - } else { - m_MoveState = STAND; + // Disengage the prone state as soon as crouch is released. + if (!m_Controller.IsState(BODY_CROUCH)) { m_ProneState = NOTPRONE; } } - // Disengage the prone state as soon as the crouch is released when the pie menu isn't active - if (!m_Controller.IsState(BODY_CROUCH) && !m_Controller.IsState(PIE_MENU_ACTIVE)) - m_ProneState = NOTPRONE; - //////////////////////////////////// // Change and reload held MovableObjects @@ -3345,24 +3312,16 @@ void AHuman::Update() m_AimAngle -= m_Controller.IsState(AIM_SHARP) ? std::min(m_AimTmr.GetElapsedSimTimeMS() * 0.00005, 0.05) : std::min(m_AimTmr.GetElapsedSimTimeMS() * 0.00015, 0.1); - if (m_AimAngle < -m_AimRange) - m_AimAngle = -m_AimRange; - } - // Analog aim - else if (m_Controller.GetAnalogAim().GetMagnitude() > 0.1 && m_Status != INACTIVE) - { - Vector aim = m_Controller.GetAnalogAim(); + if (m_AimAngle < -m_AimRange) { m_AimAngle = -m_AimRange; } + + } else if (analogAim.GetMagnitude() > 0.1F && m_Status != INACTIVE) { // Hack to avoid the GetAbsRadAngle to mangle an aim angle straight down - if (aim.m_X == 0) - aim.m_X += m_HFlipped ? -0.01 : 0.01; - m_AimAngle = aim.GetAbsRadAngle(); + if (analogAim.m_X == 0) { analogAim.m_X += 0.01F * GetFlipFactor(); } + m_AimAngle = analogAim.GetAbsRadAngle(); // Check for flip change - if ((aim.m_X > 0 && m_HFlipped) || (aim.m_X < 0 && !m_HFlipped)) - { + if ((analogAim.m_X > 0 && m_HFlipped) || (analogAim.m_X < 0 && !m_HFlipped)) { m_HFlipped = !m_HFlipped; -// // Instead of simply carving out a silhouette of the now flipped actor, isntead disable any atoms which are embedded int eh terrain until they emerge again -// m_ForceDeepCheck = true; m_CheckTerrIntersection = true; if (m_ProneState == NOTPRONE) MoveOutOfTerrain(g_MaterialGrass); diff --git a/System/Controller.cpp b/System/Controller.cpp index 2ac77148c..c4464d8de 100644 --- a/System/Controller.cpp +++ b/System/Controller.cpp @@ -331,7 +331,7 @@ namespace RTE { if (m_AnalogAim.GetMagnitude() > 0.1F && !m_ControlStates.at(PIE_MENU_ACTIVE)) { m_ControlStates.at(AIM_SHARP) = true; } // Disable sharp aim while moving - this also helps with keyboard vs mouse fighting when moving and aiming in opposite directions - if (m_ControlStates.at(PRESS_RIGHT) || m_ControlStates.at(PRESS_LEFT) || m_ControlStates.at(BODY_JUMPSTART) || (m_ControlStates.at(PIE_MENU_ACTIVE) && !m_ControlStates.at(SECONDARY_ACTION))) { + if (m_ControlStates.at(BODY_JUMP) || (m_ControlStates.at(PIE_MENU_ACTIVE) && !m_ControlStates.at(SECONDARY_ACTION))) { if (IsMouseControlled()) { g_UInputMan.SetMouseValueMagnitude(0.05F); } m_ControlStates.at(AIM_SHARP) = false; } From 2bfb13fc880cd5b4ffeca16401213de4babb0957 Mon Sep 17 00:00:00 2001 From: fourZK Date: Mon, 16 Aug 2021 21:29:52 +0300 Subject: [PATCH 05/72] Add inactive status checks to make it work as intended + some local refactor --- Entities/ACrab.cpp | 12 ++++------- Entities/AHuman.cpp | 50 ++++++++++++++------------------------------- 2 files changed, 19 insertions(+), 43 deletions(-) diff --git a/Entities/ACrab.cpp b/Entities/ACrab.cpp index a98e12172..dcc6739a1 100644 --- a/Entities/ACrab.cpp +++ b/Entities/ACrab.cpp @@ -2142,10 +2142,7 @@ void ACrab::Update() m_pJetpack->EnableEmission(true); // Quadruple this for the burst m_JetTimeLeft = std::max(m_JetTimeLeft - g_TimerMan.GetDeltaTimeMS() * 10.0F, 0.0F); - } - // Jetpack is burning - else if (m_Controller.IsState(BODY_JUMP) && m_JetTimeLeft > 0) - { + } else if (m_Controller.IsState(BODY_JUMP) && m_JetTimeLeft > 0 && m_Status != INACTIVE) { m_pJetpack->EnableEmission(true); // Jetpacks are noisy! m_pJetpack->AlarmOnEmit(m_Team); @@ -2184,8 +2181,7 @@ void ACrab::Update() //////////////////////////////////// // Movement direction - if (m_Controller.IsState(MOVE_RIGHT) || m_Controller.IsState(MOVE_LEFT) || m_MoveState == JUMP) - { + if (m_Controller.IsState(MOVE_RIGHT) || m_Controller.IsState(MOVE_LEFT) || m_MoveState == JUMP && m_Status != INACTIVE) { if (m_MoveState != JUMP) { // Restart the stride if we're just starting to walk or crawl @@ -2233,7 +2229,7 @@ void ACrab::Update() //////////////////////////////////// // Reload held MO, if applicable - if (m_Controller.IsState(WEAPON_RELOAD) && FirearmNeedsReload()) { + if (m_Controller.IsState(WEAPON_RELOAD) && FirearmNeedsReload() && m_Status != INACTIVE) { ReloadFirearms(); if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } @@ -2342,7 +2338,7 @@ void ACrab::Update() //////////////////////////////////// // Fire/Activate held devices - if (m_pTurret && m_pTurret->IsAttached()) { + if (m_pTurret && m_pTurret->IsAttached() && m_Status != INACTIVE) { for (HeldDevice *mountedDevice : m_pTurret->GetMountedDevices()) { mountedDevice->SetSharpAim(m_SharpAimProgress); if (m_Controller.IsState(WEAPON_FIRE)) { diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index 2e152a9e0..59255d237 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -3130,10 +3130,8 @@ void AHuman::Update() m_pJetpack->EnableEmission(true); // Quadruple this for the burst m_JetTimeLeft = std::max(m_JetTimeLeft - g_TimerMan.GetDeltaTimeMS() * 10.0F, 0.0F); - } // Jetpack is ordered to be burning, or the pie menu is on and was burning before it went on - else if ((m_Controller.IsState(BODY_JUMP) || (m_MoveState == JUMP && m_Controller.IsState(PIE_MENU_ACTIVE))) && m_JetTimeLeft > 0) - { + } else if ((m_Controller.IsState(BODY_JUMP) || (m_MoveState == JUMP && m_Controller.IsState(PIE_MENU_ACTIVE))) && m_JetTimeLeft > 0 && m_Status != INACTIVE) { m_pJetpack->EnableEmission(true); // Jetpacks are noisy! m_pJetpack->AlarmOnEmit(m_Team); @@ -3353,20 +3351,8 @@ void AHuman::Update() // TODO: make the delay data driven by both the actor and the device! // - if (m_Controller.IsState(AIM_SHARP) && (m_MoveState == STAND || m_MoveState == CROUCH || m_MoveState == NOMOVE || m_MoveState == WALK) && m_Vel.GetMagnitude() < 5.0F) { -/* - float halfDelay = m_SharpAimDelay / 2; - // Accelerate for first half - if (!m_SharpAimTimer.IsPastSimMS(halfDelay)) - m_SharpAimProgress = (float)m_SharpAimTimer.GetElapsedSimTimeMS() / (float)m_SharpAimDelay; - // Decelerate for second half - else if (!m_SharpAimTimer.IsPastSimMS(m_SharpAimDelay) - m_SharpAimProgress - // At max - else - m_SharpAimProgress = 1.0; -*/ - float aimMag = m_Controller.GetAnalogAim().GetMagnitude(); + if (m_Controller.IsState(AIM_SHARP) && m_Status == STABLE && (m_MoveState == STAND || m_MoveState == CROUCH || m_MoveState == NOMOVE || m_MoveState == WALK) && m_Vel.GetMagnitude() < 5.0F && GetEquippedItem()) { + float aimMag = analogAim.GetMagnitude(); // If aim sharp is being done digitally, then translate to full analog aim mag if (aimMag < 0.1F) { aimMag = 1.0F; } @@ -3397,8 +3383,8 @@ void AHuman::Update() // Fire/Activate held devices ThrownDevice *pThrown = nullptr; - if (m_pFGArm && m_pFGArm->IsAttached()) { - // DOn't reach toward anything + if (m_pFGArm && m_Status != INACTIVE) { + // Force arm to idle by reaching toward a virtually inaccessible point. m_pFGArm->ReachToward(Vector()); // Activate held device, if it's not a thrown device. @@ -3474,7 +3460,7 @@ void AHuman::Update() m_ArmsState = WEAPON_READY; } - if (m_pBGArm && m_pBGArm->IsAttached() && m_pBGArm->HoldsHeldDevice()) { + if (m_pBGArm && m_pBGArm->HoldsHeldDevice() && m_Status != INACTIVE) { HeldDevice *pDevice = m_pBGArm->GetHeldDevice(); if (pDevice->IsReloading()) { @@ -3584,21 +3570,15 @@ void AHuman::Update() m_PieNeedsUpdate = true; } - // Pick up the designated item - if (m_pItemInReach && m_pFGArm && m_Controller.IsState(WEAPON_PICKUP) && m_Status != INACTIVE) - { - // Remove the item from the scene, it's gong into the hands of this - if (g_MovableMan.RemoveMO(m_pItemInReach)) - { - // Replace whatever's in the FG arm (if anything) with what we are picking up - MovableObject *pMO = m_pFGArm->ReleaseHeldMO(); - if (pMO) { m_Inventory.push_back(pMO); } - m_pFGArm->SetHeldMO(m_pItemInReach); - m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); + if (m_pItemInReach && m_pFGArm && m_Controller.IsState(WEAPON_PICKUP) && m_Status != INACTIVE && g_MovableMan.RemoveMO(m_pItemInReach)) { + MovableObject *pMO = m_pFGArm->ReleaseHeldMO(); + if (pMO) { m_Inventory.push_back(pMO); } + m_pFGArm->SetHeldMO(m_pItemInReach); + m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); - m_PieNeedsUpdate = true; - if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } - } + m_SharpAimProgress = 0; + m_PieNeedsUpdate = true; + if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } } /////////////////////////////////////////////////// @@ -4494,7 +4474,7 @@ void AHuman::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichSc } // Weight and jetpack energy - if (m_pJetpack && m_pJetpack->IsAttached() && m_Controller.IsState(BODY_JUMP)) + if (m_pJetpack && m_Controller.IsState(BODY_JUMP) && m_Status != INACTIVE) { // Draw empty fuel indicator if (m_JetTimeLeft < 100) From e9e3290f20a386f4531cfaf2ac7cf12d3a1deb96 Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 17 Aug 2021 13:30:29 +0300 Subject: [PATCH 06/72] Implemented `HDFirearm` property `Reloadable` and functions `GetRoundInMagCapacity` and `GetReloadProgress` which are used in actor HUD stuff and also open to Lua as properties --- Entities/HDFirearm.cpp | 21 +++++++++++++++------ Entities/HDFirearm.h | 24 ++++++++++++++++++++++++ Lua/LuaBindingsEntities.cpp | 3 +++ 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/Entities/HDFirearm.cpp b/Entities/HDFirearm.cpp index a6be2c722..1b0bc8608 100644 --- a/Entities/HDFirearm.cpp +++ b/Entities/HDFirearm.cpp @@ -51,6 +51,7 @@ void HDFirearm::Clear() m_ReloadTime = 0; m_FullAuto = false; m_FireIgnoresThis = true; + m_Reloadable = true; m_ShakeRange = 0; m_SharpShakeRange = 0; m_NoSupportFactor = 0; @@ -125,6 +126,7 @@ int HDFirearm::Create(const HDFirearm &reference) { m_ReloadTime = reference.m_ReloadTime; m_FullAuto = reference.m_FullAuto; m_FireIgnoresThis = reference.m_FireIgnoresThis; + m_Reloadable = reference.m_Reloadable; m_ShakeRange = reference.m_ShakeRange; m_SharpShakeRange = reference.m_SharpShakeRange; m_NoSupportFactor = reference.m_NoSupportFactor; @@ -193,6 +195,8 @@ int HDFirearm::ReadProperty(const std::string_view &propName, Reader &reader) { reader >> m_FullAuto; } else if (propName == "FireIgnoresThis") { reader >> m_FireIgnoresThis; + } else if (propName == "Reloadable") { + reader >> m_Reloadable; } else if (propName == "RecoilTransmission") { reader >> m_JointStiffness; } else if (propName == "IsAnimatedManually") { @@ -270,6 +274,8 @@ int HDFirearm::Save(Writer &writer) const writer << m_FullAuto; writer.NewProperty("FireIgnoresThis"); writer << m_FireIgnoresThis; + writer.NewProperty("Reloadable"); + writer << m_Reloadable; writer.NewProperty("RecoilTransmission"); writer << m_JointStiffness; writer.NewProperty("IsAnimatedManually"); @@ -415,6 +421,12 @@ int HDFirearm::GetRoundInMagCount() const return m_pMagazine ? m_pMagazine->GetRoundCount() : 0; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int HDFirearm::GetRoundInMagCapacity() const { + return m_pMagazine ? m_pMagazine->GetCapacity() : (m_pMagazineReference ? m_pMagazineReference->GetCapacity() : 0); +} + ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetAIFireVel @@ -645,8 +657,7 @@ void HDFirearm::StopActivationSound() void HDFirearm::Reload() { - if (!m_Reloading) - { + if (!m_Reloading && m_Reloadable) { if (m_pMagazine) { Vector constrainedMagazineOffset = g_SceneMan.ShortestDistance(m_Pos, m_pMagazine->GetPos(), g_SceneMan.SceneWrapsX()).SetMagnitude(2.0F); @@ -678,8 +689,7 @@ void HDFirearm::Reload() bool HDFirearm::NeedsReloading() const { - if (!m_Reloading) - { + if (!m_Reloading && m_Reloadable) { if (m_pMagazine) { // If we've used over half the rounds, we can profitably go ahead and reload @@ -700,8 +710,7 @@ bool HDFirearm::NeedsReloading() const bool HDFirearm::IsFull() const { - if (!m_Reloading) - { + if (!m_Reloading && m_Reloadable) { if (m_pMagazine) { // If we've used over half the rounds, we can profitably go ahead and reload diff --git a/Entities/HDFirearm.h b/Entities/HDFirearm.h index 6dab89df9..07b5d40bc 100644 --- a/Entities/HDFirearm.h +++ b/Entities/HDFirearm.h @@ -178,6 +178,12 @@ ClassInfoGetters int GetRoundInMagCount() const; + /// + /// Gets the maximum number of rounds the magazine or magazine reference of this HDFirearm can hold. + /// + /// An int with the maximum number of rounds the magazine or magazine reference of this HDFirearm can hold. + int GetRoundInMagCapacity() const; + ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetActivationDelay @@ -534,6 +540,11 @@ ClassInfoGetters void ResetAllTimers() override { HeldDevice::ResetAllTimers(); m_LastFireTmr.Reset(); m_ReloadTmr.Reset(); } + /// + /// Gets this HDFirearm's reload progress as a scalar from 0 to 1. + /// + /// The reload progress as a scalar from 0 to 1. + float GetReloadProgress() const { return IsReloading() && m_ReloadTime > 0 ? std::min(static_cast(m_ReloadTmr.GetElapsedSimTimeMS() / m_ReloadTime), 1.0F) : 1.0F; } ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: RestDetection @@ -640,6 +651,18 @@ ClassInfoGetters bool IsFullAuto() const { return m_FullAuto; } + /// + /// Gets whether this HDFirearm is set to be reloadable or not. + /// + /// Whether this HDFirearm is reloadable. + bool IsReloadable() const { return m_Reloadable; } + + /// + /// Sets whether this HDFirearm is reloadable or not. + /// + /// Whether this HDFirearm is reloadable. + void SetReloadable(bool isReloadable) { m_Reloadable = isReloadable; } + ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetFullAuto @@ -803,6 +826,7 @@ ClassInfoGetters // Whether particles fired from this HDFirearm will ignore hits with itself, // and the root parent of this HDFirearm, regardless if they are set to hit MOs. bool m_FireIgnoresThis; + bool m_Reloadable; //!< Whether this HDFirearm is reloadable by normal means. // Timer for timing how long ago the last round was fired. Timer m_LastFireTmr; diff --git a/Lua/LuaBindingsEntities.cpp b/Lua/LuaBindingsEntities.cpp index 9cb6c0d84..f60c30d83 100644 --- a/Lua/LuaBindingsEntities.cpp +++ b/Lua/LuaBindingsEntities.cpp @@ -588,7 +588,9 @@ namespace RTE { .property("RateOfFire", &HDFirearm::GetRateOfFire, &HDFirearm::SetRateOfFire) .property("FullAuto", &HDFirearm::IsFullAuto, &HDFirearm::SetFullAuto) + .property("Reloadable", &HDFirearm::IsReloadable, &HDFirearm::SetReloadable) .property("RoundInMagCount", &HDFirearm::GetRoundInMagCount) + .property("RoundInMagCapacity", &HDFirearm::GetRoundInMagCapacity) .property("Magazine", &HDFirearm::GetMagazine, &HDFirearmSetMagazine) .property("Flash", &HDFirearm::GetFlash, &HDFirearmSetFlash) .property("PreFireSound", &HDFirearm::GetPreFireSound, &HDFirearmSetPreFireSound) @@ -602,6 +604,7 @@ namespace RTE { .property("ActivationDelay", &HDFirearm::GetActivationDelay, &HDFirearm::SetActivationDelay) .property("DeactivationDelay", &HDFirearm::GetDeactivationDelay, &HDFirearm::SetDeactivationDelay) .property("ReloadTime", &HDFirearm::GetReloadTime, &HDFirearm::SetReloadTime) + .property("ReloadProgress", &HDFirearm::GetReloadProgress) .property("ShakeRange", &HDFirearm::GetShakeRange, &HDFirearm::SetShakeRange) .property("SharpShakeRange", &HDFirearm::GetSharpShakeRange, &HDFirearm::SetSharpShakeRange) .property("NoSupportFactor", &HDFirearm::GetNoSupportFactor, &HDFirearm::SetNoSupportFactor) From f740eb549e9875b7d2756e141a58b2e7fc9f0fc6 Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 17 Aug 2021 14:57:05 +0300 Subject: [PATCH 07/72] Added `AHuman` HUD element that shows the name of a newly equipped device; Added reload bar HUD elements for `AHuman` and `ACrab`; Added `AHuman` function `UnequipFGArm` and moved the ability to unequip both arms from Lua to cpp; Changed the conditions under which BG devices are forcefully equipped - GACYR pls follow up with potential fixes for big inventory!; + refactor where applicable --- Entities/ACrab.cpp | 15 ++-- Entities/AHuman.cpp | 176 ++++++++++++++++++++++++++------------------ Entities/AHuman.h | 16 ++-- 3 files changed, 122 insertions(+), 85 deletions(-) diff --git a/Entities/ACrab.cpp b/Entities/ACrab.cpp index dcc6739a1..ece332c5b 100644 --- a/Entities/ACrab.cpp +++ b/Entities/ACrab.cpp @@ -2894,13 +2894,10 @@ void ACrab::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichScr str[1] = 0; pSymbolFont->DrawAligned(&allegroBitmap, drawPos.m_X - 11, drawPos.m_Y + m_HUDStack, str, GUIFont::Centre); - float jetTimeRatio = m_JetTimeLeft / m_JetTimeTotal; -// TODO: Don't hardcode this shit - char gaugeColor = jetTimeRatio > 0.6 ? 149 : (jetTimeRatio > 0.3 ? 77 : 13); - rectfill(pTargetBitmap, drawPos.m_X, drawPos.m_Y + m_HUDStack + 6, drawPos.m_X + (16 * jetTimeRatio), drawPos.m_Y + m_HUDStack + 7, gaugeColor); -// rect(pTargetBitmap, drawPos.m_X, drawPos.m_Y + m_HUDStack - 2, drawPos.m_X + 24, drawPos.m_Y + m_HUDStack - 4, 238); -// std::snprintf(str, sizeof(str), "%.0f Kg", mass); -// pSmallFont->DrawAligned(&allegroBitmap, drawPos.m_X - 0, drawPos.m_Y + m_HUDStack + 3, str, GUIFont::Left); + float jetTimeRatio = m_JetTimeLeft / m_JetTimeTotal; + int gaugeColor = jetTimeRatio > 0.6F ? 149 : (jetTimeRatio > 0.3F ? 77 : 13); + rectfill(pTargetBitmap, drawPos.GetFloorIntX() + 1, drawPos.GetFloorIntY() + m_HUDStack + 7, drawPos.GetFloorIntX() + 16, drawPos.GetFloorIntY() + m_HUDStack + 8, 245); + rectfill(pTargetBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 6, drawPos.GetFloorIntX() + static_cast(15.0F * jetTimeRatio), drawPos.GetFloorIntY() + m_HUDStack + 7, gaugeColor); m_HUDStack += -10; } @@ -2912,8 +2909,10 @@ void ACrab::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichScr if (!textString.empty()) { textString += " | "; } if (mountedFirearm->IsReloading()) { textString += "Reloading"; + rectfill(pTargetBitmap, drawPos.GetFloorIntX() + 1, drawPos.GetFloorIntY() + m_HUDStack + 13, drawPos.GetFloorIntX() + 29, drawPos.GetFloorIntY() + m_HUDStack + 14, 245); + rectfill(pTargetBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 12, drawPos.GetFloorIntX() + static_cast(28.0F * mountedFirearm->GetReloadProgress() + 0.5F), drawPos.GetFloorIntY() + m_HUDStack + 13, 77); } else { - textString += mountedFirearm->GetRoundInMagCount() > 0 ? std::to_string(mountedFirearm->GetRoundInMagCount()) : "Infinite"; + textString += mountedFirearm->GetRoundInMagCount() < 0 ? "Infinite" : std::to_string(mountedFirearm->GetRoundInMagCount()); } } } diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index 59255d237..696761bd7 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -80,6 +80,7 @@ void AHuman::Clear() m_SharpAimRevertTimer.Reset(); m_FGArmFlailScalar = 0.0F; m_BGArmFlailScalar = 0.7F; + m_DeviceEquipTimer.Reset(); m_DeviceState = SCANNING; m_SweepState = NOSWEEP; @@ -1411,6 +1412,19 @@ bool AHuman::EquipShieldInBGArm() return false; } +////////////////////////////////////////////////////////////////////////////////////////// + +bool AHuman::UnequipFGArm() { + if (!m_pFGArm || !m_pFGArm->IsAttached()) { + return false; + } + if (m_pFGArm->HoldsSomething()) { + m_pFGArm->GetHeldDevice()->Deactivate(); + m_Inventory.push_back(m_pFGArm->ReleaseHeldMO()); + return true; + } + return false; +} ////////////////////////////////////////////////////////////////////////////////////////// // Virtual Method: UnequipBGArm @@ -3234,28 +3248,39 @@ void AHuman::Update() bool reloadFG = false; if (m_pFGArm && m_Status != INACTIVE) { HDFirearm * pFireArm = dynamic_cast(m_pFGArm->GetHeldMO()); - if (m_Controller.IsState(WEAPON_CHANGE_NEXT)) { - if (!m_Inventory.empty() || GetEquippedBGItem()) { - if (pFireArm) { pFireArm->StopActivationSound(); } - if (m_Inventory.empty()) { UnequipBGArm(); } - - m_pFGArm->SetHeldMO(SwapNextInventory(m_pFGArm->ReleaseHeldMO())); - m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); - m_PieNeedsUpdate = true; - - EquipShieldInBGArm(); + if (m_Controller.IsState(WEAPON_CHANGE_NEXT) && m_Controller.IsState(WEAPON_CHANGE_PREV)) { + UnequipFGArm(); + UnequipBGArm(); + m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); + if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } + } else { + if (m_Controller.IsState(WEAPON_CHANGE_NEXT)) { + m_DeviceEquipTimer.Reset(); + if (!m_Inventory.empty() || GetEquippedBGItem()) { + if (pFireArm) { pFireArm->StopActivationSound(); } + if (m_Inventory.empty()) { UnequipBGArm(); } + + m_pFGArm->SetHeldMO(SwapNextInventory(m_pFGArm->ReleaseHeldMO())); + m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); + m_PieNeedsUpdate = true; + + EquipShieldInBGArm(); + m_SharpAimProgress = 0; + } } - } - if (m_Controller.IsState(WEAPON_CHANGE_PREV)) { - if (!m_Inventory.empty() || GetEquippedBGItem()) { - if (pFireArm) { pFireArm->StopActivationSound(); } - if (m_Inventory.empty()) { UnequipBGArm(); } - - m_pFGArm->SetHeldMO(SwapPrevInventory(m_pFGArm->ReleaseHeldMO())); - m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); - m_PieNeedsUpdate = true; - - EquipShieldInBGArm(); + if (m_Controller.IsState(WEAPON_CHANGE_PREV)) { + m_DeviceEquipTimer.Reset(); + if (!m_Inventory.empty() || GetEquippedBGItem()) { + if (pFireArm) { pFireArm->StopActivationSound(); } + if (m_Inventory.empty()) { UnequipBGArm(); } + + m_pFGArm->SetHeldMO(SwapPrevInventory(m_pFGArm->ReleaseHeldMO())); + m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); + m_PieNeedsUpdate = true; + + EquipShieldInBGArm(); + m_SharpAimProgress = 0; + } } } @@ -3540,7 +3565,9 @@ void AHuman::Update() m_pBGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); } } + EquipShieldInBGArm(); m_PieNeedsUpdate = true; + m_DeviceEquipTimer.Reset(); } //////////////////////////////////////// @@ -3576,9 +3603,12 @@ void AHuman::Update() m_pFGArm->SetHeldMO(m_pItemInReach); m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); + EquipShieldInBGArm(); m_SharpAimProgress = 0; m_PieNeedsUpdate = true; if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } + + m_DeviceEquipTimer.Reset(); } /////////////////////////////////////////////////// @@ -4040,19 +4070,17 @@ void AHuman::Update() m_pBGArm->ReachToward(m_pBGHandGroup->GetLimbPos(m_HFlipped)); } else { - // Re-equip shield in BG arm after climbing - EquipShieldInBGArm(); if (m_pFGArm && m_pFGArm->HoldsHeldDevice() && !m_pBGArm->HoldsHeldDevice()) { - m_pBGArm->Reach(m_pFGArm->GetHeldDevice()->GetSupportPos()); - - // BGArm does reach to support the device held by FGArm. - if (m_pBGArm->DidReach()) { - m_pFGArm->GetHeldDevice()->SetSupported(true); - m_pBGArm->SetRecoil(m_pFGArm->GetHeldDevice()->GetRecoilForce(), m_pFGArm->GetHeldDevice()->GetRecoilOffset(), m_pFGArm->GetHeldDevice()->IsRecoiled()); - } else { - // BGArm did not reach to support the device. Count device as supported anyway, if crouching - m_pFGArm->GetHeldDevice()->SetSupported(m_MoveState == CROUCH || m_ProneState == PRONE); - m_pBGArm->SetRecoil(Vector(), Vector(), false); + if (!EquipShieldInBGArm()) { + m_pBGArm->Reach(m_pFGArm->GetHeldDevice()->GetSupportPos()); + if (m_pBGArm->DidReach()) { + m_pFGArm->GetHeldDevice()->SetSupported(true); + m_pBGArm->SetRecoil(m_pFGArm->GetHeldDevice()->GetRecoilForce(), m_pFGArm->GetHeldDevice()->GetRecoilOffset(), m_pFGArm->GetHeldDevice()->IsRecoiled()); + } else { + // BGArm did not reach to support the device. Count device as supported anyway, if crouching. + m_pFGArm->GetHeldDevice()->SetSupported(m_MoveState == CROUCH || m_ProneState == PRONE); + m_pBGArm->SetRecoil(Vector(), Vector(), false); + } } } else { // Use an unreachable position to force this arm to idle, so it wont bug out where the AtomGroup was left off @@ -4493,21 +4521,25 @@ void AHuman::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichSc } // null-terminate str[1] = 0; - pSymbolFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() - 11, drawPos.GetFloorIntY() + m_HUDStack, str, GUIFont::Centre); + pSymbolFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() - 9, drawPos.GetFloorIntY() + m_HUDStack, str, GUIFont::Centre); float jetTimeRatio = m_JetTimeLeft / m_JetTimeTotal; -// TODO: Don't hardcode this shit - int gaugeColor = jetTimeRatio > 0.6F ? 149 : (jetTimeRatio > 0.3F ? 77 : 13); - rectfill(pTargetBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 6, drawPos.GetFloorIntX() + (16 * jetTimeRatio), drawPos.GetFloorIntY() + m_HUDStack + 7, gaugeColor); -// rect(pTargetBitmap, drawPos.m_X, drawPos.m_Y + m_HUDStack - 2, drawPos.m_X + 24, drawPos.m_Y + m_HUDStack - 4, 238); -// std::snprintf(str, sizeof(str), "%.0f Kg", mass); -// pSmallFont->DrawAligned(&allegroBitmap, drawPos.m_X - 0, drawPos.m_Y + m_HUDStack + 3, str, GUIFont::Left); - - m_HUDStack += -10; - } + int gaugeColor = jetTimeRatio > 0.6F ? 149 : (jetTimeRatio > 0.3F ? 77 : 13); + rectfill(pTargetBitmap, drawPos.GetFloorIntX() + 1, drawPos.GetFloorIntY() + m_HUDStack + 7, drawPos.GetFloorIntX() + 16, drawPos.GetFloorIntY() + m_HUDStack + 8, 245); + rectfill(pTargetBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 6, drawPos.GetFloorIntX() + static_cast(15.0F * jetTimeRatio), drawPos.GetFloorIntY() + m_HUDStack + 7, gaugeColor); + + m_HUDStack += -10; + if (m_pFGArm && !m_DeviceEquipTimer.IsPastRealMS(500)) { + if (m_pFGArm->HoldsSomething()) { + pSmallFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() + 1, drawPos.GetFloorIntY() + m_HUDStack + 3, m_pFGArm->GetHeldMO()->GetPresetName().c_str(), GUIFont::Centre); + } else { + pSmallFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() + 1, drawPos.GetFloorIntY() + m_HUDStack + 3, "EMPTY", GUIFont::Centre); + } + m_HUDStack += -9; + } + } // Held-related GUI stuff - else if (m_pFGArm && m_pFGArm->IsAttached()) - { + else if (m_pFGArm) { HDFirearm *pHeldFirearm = dynamic_cast(m_pFGArm->GetHeldDevice()); // Ammo @@ -4518,24 +4550,37 @@ void AHuman::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichSc str[0] = -56; str[1] = 0; pSymbolFont->DrawAligned(&allegroBitmap, drawPos.m_X - 10, drawPos.m_Y + m_HUDStack, str, GUIFont::Left); - std::string fgWeaponString = pHeldFirearm->GetRoundInMagCount() < 0 ? "Infinite" : std::to_string(pHeldFirearm->GetRoundInMagCount()); - fgWeaponString = pHeldFirearm->IsReloading() ? "Reloading" : fgWeaponString; + std::string fgWeaponString; + + if (pHeldFirearm->IsReloading()) { + fgWeaponString = "Reloading"; + rectfill(pTargetBitmap, drawPos.GetFloorIntX() + 1, drawPos.GetFloorIntY() + m_HUDStack + 13, drawPos.GetFloorIntX() + 29, drawPos.GetFloorIntY() + m_HUDStack + 14, 245); + rectfill(pTargetBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 12, drawPos.GetFloorIntX() + static_cast(28.0F * pHeldFirearm->GetReloadProgress() + 0.5F), drawPos.GetFloorIntY() + m_HUDStack + 13, 77); + } else { + fgWeaponString = pHeldFirearm->GetRoundInMagCount() < 0 ? "Infinite" : std::to_string(pHeldFirearm->GetRoundInMagCount()); + } if (bgHeldItem && bgHeldFirearm) { - std::string bgWeaponString = bgHeldFirearm->GetRoundInMagCount() < 0 ? "Infinite" : std::to_string(bgHeldFirearm->GetRoundInMagCount()); - bgWeaponString = bgHeldFirearm->IsReloading() ? "Reloading" : bgWeaponString; - std::snprintf(str, sizeof(str), "%s | %s", fgWeaponString.c_str(), bgWeaponString.c_str()); + std::string bgWeaponString; + + if (bgHeldFirearm->IsReloading()) { + bgWeaponString = "Reloading"; + int textWidth = pSmallFont->CalculateWidth(fgWeaponString) + 6; + rectfill(pTargetBitmap, drawPos.GetFloorIntX() + 1 + textWidth, drawPos.GetFloorIntY() + m_HUDStack + 13, drawPos.GetFloorIntX() + 29 + textWidth, drawPos.GetFloorIntY() + m_HUDStack + 14, 245); + rectfill(pTargetBitmap, drawPos.GetFloorIntX() + textWidth, drawPos.GetFloorIntY() + m_HUDStack + 12, drawPos.GetFloorIntX() + static_cast(28.0F * bgHeldFirearm->GetReloadProgress() + 0.5F) + textWidth, drawPos.GetFloorIntY() + m_HUDStack + 13, 77); + } else { + bgWeaponString = bgHeldFirearm->GetRoundInMagCount() < 0 ? "Infinite" : std::to_string(bgHeldFirearm->GetRoundInMagCount()); + } + std::snprintf(str, sizeof(str), "%s | %s", fgWeaponString.c_str(), bgWeaponString.c_str()); } else { std::snprintf(str, sizeof(str), "%s", fgWeaponString.c_str()); } - pSmallFont->DrawAligned(&allegroBitmap, drawPos.m_X - 0, drawPos.m_Y + m_HUDStack + 3, str, GUIFont::Left); + pSmallFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 3, str, GUIFont::Left); m_HUDStack += -10; } - // Device changing GUI - if (m_Controller.IsState(PIE_MENU_ACTIVE)) - { + if (m_Controller.IsState(PIE_MENU_ACTIVE) || !m_DeviceEquipTimer.IsPastRealMS(700)) { /* // Display Gold tally if gold chunk is in hand if (m_pFGArm->HoldsSomething() && m_pFGArm->GetHeldMO()->IsGold() && GetGoldCarried() > 0) @@ -4548,23 +4593,12 @@ void AHuman::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichSc m_HUDStack += -11; } */ - if (m_pFGArm->HoldsSomething()) - { -/* - std::snprintf(str, sizeof(str), " Œ Drop"); - pSmallFont->DrawAligned(&allegroBitmap, drawPos.m_X - 12, drawPos.m_Y + m_HUDStack + 3, str, GUIFont::Left); - m_HUDStack += -9; -*/ -// std::snprintf(str, sizeof(str), " %s", m_pFGArm->GetHeldMO()->GetPresetName().c_str()); - pSmallFont->DrawAligned(&allegroBitmap, drawPos.m_X, drawPos.m_Y + m_HUDStack + 3, m_pFGArm->GetHeldMO()->GetPresetName().c_str(), GUIFont::Centre); - m_HUDStack += -9; - } - else - { -// std::snprintf(str, sizeof(str), "æ EMPTY ø"); - pSmallFont->DrawAligned(&allegroBitmap, drawPos.m_X, drawPos.m_Y + m_HUDStack + 3, "EMPTY", GUIFont::Centre); - m_HUDStack += -9; - } + if (m_pFGArm->HoldsSomething()) { + pSmallFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() + 1, drawPos.GetFloorIntY() + m_HUDStack + 3, m_pFGArm->GetHeldMO()->GetPresetName().c_str(), GUIFont::Centre); + } else { + pSmallFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() + 1, drawPos.GetFloorIntY() + m_HUDStack + 3, "EMPTY", GUIFont::Centre); + } + m_HUDStack += -9; /* // Reload GUI, only show when there's nothing to pick up if (!m_pItemInReach && m_pFGArm->HoldsSomething() && pHeldFirearm && !pHeldFirearm->IsFull()) diff --git a/Entities/AHuman.h b/Entities/AHuman.h index a2e9d85f6..28b90dbf8 100644 --- a/Entities/AHuman.h +++ b/Entities/AHuman.h @@ -563,6 +563,12 @@ ClassInfoGetters // bool EquipDualWieldableInBGArm(); + /// + /// Unequips whatever is in the FG arm and puts it into the inventory. + /// + /// Whether there was anything to unequip. + bool UnequipFGArm(); + ////////////////////////////////////////////////////////////////////////////////////////// // Virtual Method: UnequipBGArm @@ -989,12 +995,10 @@ ClassInfoGetters Timer m_ThrowTmr; // The duration it takes this AHuman to fully charge a throw. long m_ThrowPrepTime; - // For timing the transition from sharp aim back to regular aim - Timer m_SharpAimRevertTimer; - // The rate at which this AHuman's FG Arm follows the the bodily rotation. Best to keep this at 0 so it doesn't complicate aiming. - float m_FGArmFlailScalar; - // The rate at which this AHuman's BG Arm follows the the bodily rotation. Set to a negative value for a "counterweight" effect. - float m_BGArmFlailScalar; + Timer m_SharpAimRevertTimer; //!< For timing the transition from sharp aim back to regular aim. + float m_FGArmFlailScalar; //!< The rate at which this AHuman's FG Arm follows the the bodily rotation. Best to keep this at 0 so it doesn't complicate aiming. + 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_DeviceEquipTimer; //!< Timer for showing the name of the Device we had just equipped. //////////////// // AI States From 1654a0e33a8ffab2b347769af73d11849dcabfb0 Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 17 Aug 2021 15:12:26 +0300 Subject: [PATCH 08/72] Toggle device pickup HUD when a viewing player is nearby (MAX: pls add a gameplay setting slider for this) --- Entities/HeldDevice.cpp | 49 +++++++++++++++++++++++------------------ Entities/HeldDevice.h | 1 + 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/Entities/HeldDevice.cpp b/Entities/HeldDevice.cpp index 40027b264..6bc337e30 100644 --- a/Entities/HeldDevice.cpp +++ b/Entities/HeldDevice.cpp @@ -45,6 +45,9 @@ void HeldDevice::Clear() m_MaxSharpLength = 0; m_Supported = false; m_SupportOffset.Reset(); + for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { + m_SeenByPlayer[player] = false; + } m_IsUnPickupable = false; m_PickupableByPresetNames.clear(); m_GripStrengthMultiplier = 1.0F; @@ -542,7 +545,8 @@ void HeldDevice::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whi if (!m_Parent && !IsUnPickupable()) { // Only draw if the team viewing this has seen the space where this is located - int viewingTeam = g_ActivityMan.GetActivity()->GetTeamOfPlayer(g_ActivityMan.GetActivity()->PlayerOfScreen(whichScreen)); + int viewingPlayer = g_ActivityMan.GetActivity()->PlayerOfScreen(whichScreen); + int viewingTeam = g_ActivityMan.GetActivity()->GetTeamOfPlayer(viewingPlayer); if (viewingTeam != Activity::NoTeam) { if (g_SceneMan.IsUnseen(m_Pos.m_X, m_Pos.m_Y, viewingTeam)) @@ -578,28 +582,31 @@ void HeldDevice::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whi GUIFont *pSymbolFont = g_FrameMan.GetLargeFont(); GUIFont *pTextFont = g_FrameMan.GetSmallFont(); if (pSymbolFont && pTextFont) { - AllegroBitmap pBitmapInt(pTargetBitmap); - if (m_BlinkTimer.GetElapsedSimTimeMS() < 300) { - str[0] = -42; - str[1] = 0; - } - else if (m_BlinkTimer.GetElapsedSimTimeMS() < 600) { - str[0] = -41; + if (m_BlinkTimer.GetElapsedSimTimeMS() < 250) { + str[0] = 0; + } else if (m_BlinkTimer.GetElapsedSimTimeMS() < 500) { + str[0] = -42; str[1] = 0; - } - else if (m_BlinkTimer.GetElapsedSimTimeMS() < 900) { - str[0] = -40; + } else if (m_BlinkTimer.GetElapsedSimTimeMS() < 750) { + str[0] = -41; str[1] = 0; - } - else if (m_BlinkTimer.GetElapsedSimTimeMS() < 1200) { - str[0] = 0; - } - else - m_BlinkTimer.Reset(); - - pSymbolFont->DrawAligned(&pBitmapInt, drawPos.m_X - 1, drawPos.m_Y - 20, str, GUIFont::Centre); - std::snprintf(str, sizeof(str), "%s", m_PresetName.c_str()); - pTextFont->DrawAligned(&pBitmapInt, drawPos.m_X + 0, drawPos.m_Y - 29, str, GUIFont::Centre); + } else if (m_BlinkTimer.GetElapsedSimTimeMS() < 1000) { + str[0] = -40; + str[1] = 0; + } else { + m_BlinkTimer.Reset(); + // Check for nearby actors that will toggle this pickup HUD + float dist; + // TODO: @MaximDude replace radius with settings property! + float radius = 100.0F; //static_cast(g_SettingsMan.GetPickupHUDRadius()); + m_SeenByPlayer[viewingPlayer] = radius < 0 || g_SceneMan.ShortestDistance(m_Pos, g_SceneMan.GetScrollTarget(whichScreen), g_SceneMan.SceneWrapsX()).GetMagnitude() < radius; + } + if (m_SeenByPlayer[viewingPlayer]) { + AllegroBitmap pBitmapInt(pTargetBitmap); + pSymbolFont->DrawAligned(&pBitmapInt, drawPos.GetFloorIntX() - 1, drawPos.GetFloorIntY() - 20, str, GUIFont::Centre); + std::snprintf(str, sizeof(str), "%s", m_PresetName.c_str()); + pTextFont->DrawAligned(&pBitmapInt, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() - 29, str, GUIFont::Centre); + } } } } diff --git a/Entities/HeldDevice.h b/Entities/HeldDevice.h index d99afd4c5..043b8fecf 100644 --- a/Entities/HeldDevice.h +++ b/Entities/HeldDevice.h @@ -635,6 +635,7 @@ ClassInfoGetters // If this HeldDevice is currently being supported by a second hand. bool m_Supported; bool m_IsUnPickupable; //!< Whether or not this HeldDevice should be able to be picked up at all. + std::array m_SeenByPlayer; //!< An array of players that can currently see the pickup HUD of this HeldDevice. std::unordered_set m_PickupableByPresetNames; //!< The unordered set of PresetNames that can pick up this HeldDevice if it's dropped. An empty set means there are no PresetName limitations. float m_GripStrengthMultiplier; //!< The multiplier for how well this HeldDevice can be gripped by Arms. // Blink timer for the icon From 1159013ce07ad199165802a547fb555f86e0c71b Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 17 Aug 2021 15:23:29 +0300 Subject: [PATCH 09/72] Refactored ugly HDFirearm HUD code; Implemented `GetThrowProgress` function to access the throw chargeup, in Lua also; Mild refactor around those areas --- Entities/AHuman.cpp | 26 ++++----- Entities/AHuman.h | 8 ++- Entities/HDFirearm.cpp | 110 +++++++++--------------------------- Lua/LuaBindingsEntities.cpp | 1 + 4 files changed, 45 insertions(+), 100 deletions(-) diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index 696761bd7..0b3209ec1 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -3441,7 +3441,6 @@ void AHuman::Update() if (pMO) { pMO->SetPos(m_Pos + (m_pFGArm->GetParentOffset() + Vector(m_pFGArm->GetMaxLength(), -m_pFGArm->GetMaxLength() * 0.5F)).GetXFlipped(m_HFlipped).RadRotate(m_AimAngle * GetFlipFactor()) * m_Rotation); - float throwScalar = static_cast(std::min(m_ThrowTmr.GetElapsedSimTimeMS(), static_cast(m_ThrowPrepTime)) / m_ThrowPrepTime); float maxThrowVel = pThrown->GetMaxThrowVel(); float minThrowVel = pThrown->GetMinThrowVel(); if (maxThrowVel == 0) { @@ -3449,7 +3448,7 @@ void AHuman::Update() maxThrowVel = (m_pFGArm->GetThrowStrength() + std::abs(m_AngularVel * 0.5F)) / std::sqrt(std::abs(pMO->GetMass()) + 1.0F); minThrowVel = maxThrowVel * 0.2F; } - Vector tossVec(minThrowVel + (maxThrowVel - minThrowVel) * throwScalar, 0.5F * RandomNormalNum()); + Vector tossVec(minThrowVel + (maxThrowVel - minThrowVel) * GetThrowProgress(), 0.5F * RandomNormalNum()); tossVec.RadRotate(m_AimAngle); pMO->SetVel(tossVec.GetXFlipped(m_HFlipped) * m_Rotation); pMO->SetAngularVel(m_AngularVel + RandomNum(-5.0F, 2.5F) * GetFlipFactor()); @@ -4321,8 +4320,7 @@ void AHuman::Update() ////////////////////////////////////////////////////////////////////////////////////////// // Description: Draws an aiming aid in front of this HeldDevice for throwing. -void AHuman::DrawThrowingReticule(BITMAP *pTargetBitmap, const Vector &targetPos, double amount) const -{ +void AHuman::DrawThrowingReticule(BITMAP *pTargetBitmap, const Vector &targetPos, float progressScalar) const { const int pointCount = 9; Vector points[pointCount]; //Color colors[pointCount]; @@ -4336,12 +4334,12 @@ void AHuman::DrawThrowingReticule(BITMAP *pTargetBitmap, const Vector &targetPos acquire_bitmap(pTargetBitmap); - for (int i = 0; i < pointCount * amount; ++i) { + for (int i = 0; i < pointCount * progressScalar; ++i) { points[i].FlipX(m_HFlipped); points[i] += outOffset; points[i].RadRotate((m_AimAngle * GetFlipFactor()) + m_Rotation.GetRadAngle()); points[i] += m_Pos; - if (m_pFGArm && m_pFGArm->IsAttached()) + if (m_pFGArm) points[i] += m_pFGArm->GetParentOffset(); // Put the flickering glows on the reticule dots, in absolute scene coordinates @@ -4448,15 +4446,13 @@ void AHuman::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichSc // Player AI drawing - // Device aiming reticule - if (m_Controller.IsState(AIM_SHARP) && m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->HoldsHeldDevice()) { - m_pFGArm->GetHeldDevice()->DrawHUD(pTargetBitmap, targetPos, whichScreen, m_Controller.IsPlayerControlled()); - } - - - // Throwing reticule - if (m_ArmsState == THROWING_PREP) { - DrawThrowingReticule(pTargetBitmap, targetPos, std::min(m_ThrowTmr.GetElapsedSimTimeMS() / m_ThrowPrepTime, 1.0)); + if (m_pFGArm && m_pFGArm->HoldsHeldDevice()) { + // Draw the aiming dots for the currently held device. + if (m_ArmsState == THROWING_PREP) { + DrawThrowingReticule(pTargetBitmap, targetPos, GetThrowProgress()); + } else if (m_Controller.IsState(AIM_SHARP) || (m_Controller.IsPlayerControlled() && !m_Controller.IsState(PIE_MENU_ACTIVE))) { + m_pFGArm->GetHeldDevice()->DrawHUD(pTargetBitmap, targetPos, whichScreen, m_Controller.IsState(AIM_SHARP) && m_Controller.IsPlayerControlled()); + } } ////////////////////////////////////// diff --git a/Entities/AHuman.h b/Entities/AHuman.h index 28b90dbf8..bda216190 100644 --- a/Entities/AHuman.h +++ b/Entities/AHuman.h @@ -563,6 +563,12 @@ ClassInfoGetters // bool EquipDualWieldableInBGArm(); + /// + /// Gets the throw chargeup progress of this AHuman. + /// + /// The throw chargeup progress, as a scalar from 0 to 1. + float GetThrowProgress() const { return m_ThrowPrepTime > 0 ? static_cast(std::min(m_ThrowTmr.GetElapsedSimTimeMS() / static_cast(m_ThrowPrepTime), 1.0)) : 1.0F; } + /// /// Unequips whatever is in the FG arm and puts it into the inventory. /// @@ -931,7 +937,7 @@ ClassInfoGetters // reticule should be drawn, to indicate force in the throw. // Return value: None. - void DrawThrowingReticule(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), double amount = 1.0) const; + void DrawThrowingReticule(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), float progressScalar = 1.0F) const; // Member variables diff --git a/Entities/HDFirearm.cpp b/Entities/HDFirearm.cpp index 1b0bc8608..fc63c3fdf 100644 --- a/Entities/HDFirearm.cpp +++ b/Entities/HDFirearm.cpp @@ -1114,90 +1114,32 @@ void HDFirearm::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whic HeldDevice::DrawHUD(pTargetBitmap, targetPos, whichScreen); - // Don't bother if the aim distance is really short, or not held - if (!m_Parent || m_SharpAim < 0.15) - return; - - float sharpLength = m_MaxSharpLength * m_SharpAim; - - if (playerControlled) - { - Vector aimPoint1(sharpLength - 9, 0); - Vector aimPoint2(sharpLength - 3, 0); - Vector aimPoint3(sharpLength + 3, 0); - Vector aimPoint4(sharpLength + 9, 0); - aimPoint1 += m_MuzzleOff; - aimPoint2 += m_MuzzleOff; - aimPoint3 += m_MuzzleOff; - aimPoint4 += m_MuzzleOff; - Matrix aimMatrix(m_Rotation); - aimMatrix.SetXFlipped(m_HFlipped); - aimPoint1 *= aimMatrix; - aimPoint2 *= aimMatrix; - aimPoint3 *= aimMatrix; - aimPoint4 *= aimMatrix; - aimPoint1 += m_Pos; - aimPoint2 += m_Pos; - aimPoint3 += m_Pos; - aimPoint4 += m_Pos; - - // Put the flickering glows on the reticule dots, in absolute scene coordinates - int glow = (155 + RandomNum(0, 100)); - g_PostProcessMan.RegisterGlowDotEffect(aimPoint1, YellowDot, glow); - g_PostProcessMan.RegisterGlowDotEffect(aimPoint2, YellowDot, glow); - g_PostProcessMan.RegisterGlowDotEffect(aimPoint3, YellowDot, glow); - g_PostProcessMan.RegisterGlowDotEffect(aimPoint4, YellowDot, glow); - - // Make into target frame coordinates - aimPoint1 -= targetPos; - aimPoint2 -= targetPos; - aimPoint3 -= targetPos; - aimPoint4 -= targetPos; - - // Wrap the points - g_SceneMan.WrapPosition(aimPoint1); - g_SceneMan.WrapPosition(aimPoint2); - g_SceneMan.WrapPosition(aimPoint3); - g_SceneMan.WrapPosition(aimPoint4); - - acquire_bitmap(pTargetBitmap); - putpixel(pTargetBitmap, aimPoint1.m_X, aimPoint1.m_Y, g_YellowGlowColor); - putpixel(pTargetBitmap, aimPoint2.m_X, aimPoint2.m_Y, g_YellowGlowColor); - putpixel(pTargetBitmap, aimPoint3.m_X, aimPoint3.m_Y, g_YellowGlowColor); - putpixel(pTargetBitmap, aimPoint4.m_X, aimPoint4.m_Y, g_YellowGlowColor); - release_bitmap(pTargetBitmap); - } - else - { - Vector aimPoint2(sharpLength - 3, 0); - Vector aimPoint3(sharpLength + 3, 0); - aimPoint2 += m_MuzzleOff; - aimPoint3 += m_MuzzleOff; - Matrix aimMatrix(m_Rotation); - aimMatrix.SetXFlipped(m_HFlipped); - aimPoint2 *= aimMatrix; - aimPoint3 *= aimMatrix; - aimPoint2 += m_Pos; - aimPoint3 += m_Pos; - - // Put the flickering glows on the reticule dots, in absolute scene coordinates - int glow = (55 + RandomNum(0, 100)); - g_PostProcessMan.RegisterGlowDotEffect(aimPoint2, YellowDot, glow); - g_PostProcessMan.RegisterGlowDotEffect(aimPoint3, YellowDot, glow); - - // Make into target frame coordinates - aimPoint2 -= targetPos; - aimPoint3 -= targetPos; - - // Wrap the points - g_SceneMan.WrapPosition(aimPoint2); - g_SceneMan.WrapPosition(aimPoint3); - - acquire_bitmap(pTargetBitmap); - putpixel(pTargetBitmap, aimPoint2.m_X, aimPoint2.m_Y, g_YellowGlowColor); - putpixel(pTargetBitmap, aimPoint3.m_X, aimPoint3.m_Y, g_YellowGlowColor); - release_bitmap(pTargetBitmap); - } + if (!m_Parent || IsReloading() || m_MaxSharpLength == 0) { return; } + + float sharpLength = std::max(m_MaxSharpLength * m_SharpAim, 20.0F); + int glow, pointCount; + if (playerControlled && sharpLength > 20.0F) { + pointCount = m_SharpAim > 0.5F ? 4 : 3; + glow = RandomNum(127, 255); + } else { + pointCount = 2; + glow = RandomNum(63, 127); + } + int pointSpacing = 10 - pointCount; + sharpLength -= static_cast(pointSpacing * pointCount) * 0.5F; + Vector muzzleOffset(std::max(m_MuzzleOff.m_X, m_SpriteRadius), m_MuzzleOff.m_Y); + + acquire_bitmap(pTargetBitmap); + for (int i = 0; i < pointCount; ++i) { + Vector aimPoint(sharpLength + static_cast(pointSpacing * i), 0); + aimPoint = RotateOffset(aimPoint + muzzleOffset) + m_Pos; + + g_PostProcessMan.RegisterGlowDotEffect(aimPoint, YellowDot, glow); + aimPoint -= targetPos; + g_SceneMan.WrapPosition(aimPoint); + putpixel(pTargetBitmap, aimPoint.GetFloorIntX(), aimPoint.GetFloorIntY(), g_YellowGlowColor); + } + release_bitmap(pTargetBitmap); } } // namespace RTE diff --git a/Lua/LuaBindingsEntities.cpp b/Lua/LuaBindingsEntities.cpp index f60c30d83..44af6d339 100644 --- a/Lua/LuaBindingsEntities.cpp +++ b/Lua/LuaBindingsEntities.cpp @@ -404,6 +404,7 @@ namespace RTE { .property("JetTimeLeft", &AHuman::GetJetTimeLeft, &AHuman::SetJetTimeLeft) .property("JetAngleRange", &AHuman::GetJetAngleRange, &AHuman::SetJetAngleRange) .property("ThrowPrepTime", &AHuman::GetThrowPrepTime, &AHuman::SetThrowPrepTime) + .property("ThrowProgress", &AHuman::GetThrowProgress) .property("EquippedItem", &AHuman::GetEquippedItem) .property("EquippedBGItem", &AHuman::GetEquippedBGItem) .property("FirearmIsReady", &AHuman::FirearmIsReady) From 4bd2e42c0467b1dc17e9db7e972dcce903186eb7 Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 17 Aug 2021 15:34:04 +0300 Subject: [PATCH 10/72] Misc `AHuman` and `ACrab` refactor and fixing --- Entities/ACrab.cpp | 56 ++++------- Entities/AHuman.cpp | 227 ++++++++++++++++++-------------------------- 2 files changed, 112 insertions(+), 171 deletions(-) diff --git a/Entities/ACrab.cpp b/Entities/ACrab.cpp index ece332c5b..507ed1294 100644 --- a/Entities/ACrab.cpp +++ b/Entities/ACrab.cpp @@ -59,7 +59,7 @@ void ACrab::Clear() m_pJetpack = 0; m_JetTimeTotal = 0.0; m_JetTimeLeft = 0.0; - m_JetAngleRange = 0.25; + m_JetAngleRange = 0.25F; m_MoveState = STAND; for (int side = 0; side < SIDECOUNT; ++side) { @@ -2296,44 +2296,26 @@ void ACrab::Update() ////////////////////////////// // Sharp aim calculation -// TODO: make the delay data driven by both the actor and the device! - // - if (m_Controller.IsState(AIM_SHARP) && m_MoveState == STAND && m_Vel.GetMagnitude() < 5.0) - { -/* - float halfDelay = m_SharpAimDelay / 2; - // Accelerate for first half - if (!m_SharpAimTimer.IsPastSimMS(halfDelay)) - m_SharpAimProgress = (float)m_SharpAimTimer.GetElapsedSimTimeMS() / (float)m_SharpAimDelay; - // Decelerate for second half - else if (!m_SharpAimTimer.IsPastSimMS(m_SharpAimDelay) - m_SharpAimProgress - // At max - else - m_SharpAimProgress = 1.0; -*/ - float aimMag = m_Controller.GetAnalogAim().GetMagnitude(); + if (m_Controller.IsState(AIM_SHARP) && m_Status == STABLE && m_Vel.GetMagnitude() < 5.0F) { + float aimMag = analogAim.GetMagnitude(); - // If aim sharp is being done digitally, then translate to full analog aim mag - if (aimMag < 0.1) - aimMag = 1.0; + // If aim sharp is being done digitally, then translate to full magnitude. + if (aimMag < 0.1F) { aimMag = 1.0F; } + if (m_MoveState == WALK) { aimMag *= 0.3F; } - if (m_SharpAimTimer.IsPastSimMS(m_SharpAimDelay)) - { - // Only go slower outward - if (m_SharpAimProgress < aimMag) - m_SharpAimProgress += (aimMag - m_SharpAimProgress) * 0.035; - else - m_SharpAimProgress = aimMag; - } - else - m_SharpAimProgress = 0; - } - else - { - m_SharpAimProgress = 0; - m_SharpAimTimer.Reset(); - } + if (m_SharpAimTimer.IsPastSimMS(m_SharpAimDelay)) { + // Only go slower outward. + if (m_SharpAimProgress < aimMag) { + m_SharpAimProgress += (aimMag - m_SharpAimProgress) * 0.035F; + } else { + m_SharpAimProgress = aimMag; + } + } else { + m_SharpAimProgress *= 0.95F; + } + } else { + m_SharpAimProgress = std::max(m_SharpAimProgress * 0.95F - 0.1F, 0.0F); + } //////////////////////////////////// // Fire/Activate held devices diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index 0b3209ec1..202a72a07 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -514,7 +514,7 @@ void AHuman::SetHead(Attachable *newHead) { dynamic_cast(parent)->SetHead(attachable); }}); - if (m_pHead->HasNoSetDamageMultiplier()) { m_pHead->SetDamageMultiplier(5.0F); } + if (m_pHead->HasNoSetDamageMultiplier()) { m_pHead->SetDamageMultiplier(4.0F); } if (m_pHead->IsDrawnAfterParent()) { m_pHead->SetDrawnNormallyByParent(false); } m_pHead->SetInheritsRotAngle(false); } @@ -756,7 +756,6 @@ bool AHuman::AddPieMenuSlices(PieMenuGUI *pPieMenu) PieSlice reloadSlice(GetEquippedItem() || GetEquippedBGItem() ? "Reload" : (m_pFGArm ? "Not holding anything!" : "NO ARM!"), PieSlice::PieSliceIndex::PSI_RELOAD, PieSlice::SliceDirection::UP, ((m_pFGArm && m_pFGArm->GetHeldDevice() && !m_pFGArm->GetHeldDevice()->IsFull()) || (m_pBGArm && m_pBGArm->GetHeldDevice() && !m_pBGArm->GetHeldDevice()->IsFull())) && m_Status != INACTIVE); pPieMenu->AddSlice(reloadSlice); } - //To-do: don't oneline this? PieSlice dropSlice(m_pFGArm && m_pFGArm->GetHeldMO() ? "Drop " + m_pFGArm->GetHeldMO()->GetPresetName() : m_pBGArm ? (m_pBGArm->GetHeldMO() ? "Drop " + m_pBGArm->GetHeldMO()->GetPresetName() : (!m_Inventory.empty() ? "Drop Inventory" : "Not holding anything!")) : (m_pFGArm ? "Not holding anything!" : "NO ARM!"), PieSlice::PieSliceIndex::PSI_DROP, PieSlice::SliceDirection::DOWN, ((m_pFGArm && m_pFGArm->GetHeldMO()) || (m_pBGArm && (m_pBGArm->GetHeldMO() || !m_Inventory.empty()))) && m_Status != INACTIVE); pPieMenu->AddSlice(dropSlice); @@ -1399,11 +1398,7 @@ bool AHuman::EquipShieldInBGArm() // Move the hand to a poisition so it looks like the new device was drawn from inventory m_pBGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); - // Play the device switching sound only if activity is running - if (g_ActivityMan.ActivityRunning()) - { - if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } - } + if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } return true; } @@ -3431,10 +3426,10 @@ void AHuman::Update() if (!pThrown->ActivatesWhenReleased()) { pThrown->Activate(); } } m_ArmsState = THROWING_PREP; - m_pFGArm->ReachToward(m_Pos + (m_pFGArm->GetParentOffset() + pThrown->GetStartThrowOffset().RadRotate(m_AimAngle + m_AngularVel * g_TimerMan.GetDeltaTimeSecs())).GetXFlipped(m_HFlipped) * m_Rotation); + m_pFGArm->ReachToward(m_Pos + (m_pFGArm->GetParentOffset() + pThrown->GetStartThrowOffset().RadRotate(m_AimAngle + m_AngularVel * deltaTime)).GetXFlipped(m_HFlipped) * m_Rotation); } else if (m_ArmsState == THROWING_PREP) { m_ArmsState = THROWING_RELEASE; - //TODO: figure out how to properly use EndThrowOffset, since it doesn't play much a role for just one frame + // TODO: figure out how to properly use EndThrowOffset, since it doesn't play much a role for just one frame! m_pFGArm->SetHandPos(m_Pos + (m_pFGArm->GetParentOffset() + pThrown->GetEndThrowOffset().RadRotate(m_AimAngle * GetFlipFactor())).GetXFlipped(m_HFlipped) * m_Rotation); MovableObject *pMO = m_pFGArm->ReleaseHeldMO(); @@ -3486,18 +3481,18 @@ void AHuman::Update() if (m_pBGArm && m_pBGArm->HoldsHeldDevice() && m_Status != INACTIVE) { HeldDevice *pDevice = m_pBGArm->GetHeldDevice(); - if (pDevice->IsReloading()) { m_SharpAimTimer.Reset(); m_SharpAimProgress = 0; - if (m_pFGArm && m_pFGArm->IsAttached() && !GetEquippedItem()) { m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); } + if (m_pFGArm && !m_pFGArm->HoldsSomething()) { m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); } } - if (reloadFG == false && !pDevice->IsFull() && m_Controller.IsState(WEAPON_RELOAD) && !m_pItemInReach) { - if (m_pFGArm && m_pFGArm->IsAttached() && !GetEquippedItem()) { m_pFGArm->SetHandPos(pDevice->GetMagazinePos()); } + if (reloadFG == false && !pDevice->IsFull() && m_Controller.IsState(WEAPON_RELOAD)) { + if (m_pFGArm && !m_pFGArm->HoldsSomething()) { m_pFGArm->SetHandPos(pDevice->GetMagazinePos()); } pDevice->Reload(); if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } } - if (pDevice->DoneReloading() && m_pFGArm && m_pFGArm->IsAttached() && !GetEquippedItem()) { m_pFGArm->SetHandPos(pDevice->GetMagazinePos()); } + if (pDevice->DoneReloading() && m_pFGArm && !m_pFGArm->HoldsSomething()) { m_pFGArm->SetHandPos(pDevice->GetMagazinePos()); } + pDevice->SetSharpAim(m_SharpAimProgress); if (m_Controller.IsState(WEAPON_FIRE)) { pDevice->Activate(); @@ -3559,12 +3554,13 @@ void AHuman::Update() } g_MovableMan.AddParticle(pMO); } - } else { + } else if (!m_Inventory.empty()) { DropAllInventory(); m_pBGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); } } EquipShieldInBGArm(); + m_SharpAimProgress = 0; m_PieNeedsUpdate = true; m_DeviceEquipTimer.Reset(); } @@ -3578,21 +3574,21 @@ void AHuman::Update() // Try to detect a new item if (m_pFGArm && m_Status == STABLE) { reach += m_pFGArm->GetMaxLength(); - reachPoint = m_pFGArm->GetPos(); + reachPoint = m_pFGArm->GetPos() + m_pFGArm->GetJointOffset().GetXFlipped(m_HFlipped).RadRotate(m_pFGArm->GetRotAngle()); if (!m_pItemInReach) { - MOID itemMOID = g_SceneMan.CastMORay(reachPoint, Vector((m_HFlipped ? -reach : reach) * RandomNum(), RandomNum(0.0F, reach)), m_MOID, Activity::NoTeam, g_MaterialGrass, true, 2); + MOID itemMOID = g_SceneMan.CastMORay(reachPoint, Vector(reach * RandomNum(), 0).RadRotate(GetAimAngle(true) + RandomNum(-c_HalfPI, 0.0F) * GetFlipFactor()), m_MOID, Activity::NoTeam, g_MaterialGrass, true, 2); MovableObject *pItem = g_MovableMan.GetMOFromID(itemMOID); if (pItem) { - m_pItemInReach = pItem ? dynamic_cast(pItem->GetRootParent()) : 0; + m_pItemInReach = pItem ? dynamic_cast(pItem->GetRootParent()) : nullptr; if (m_pItemInReach) { m_PieNeedsUpdate = true; } } } } // Item currently set to be within reach has expired or is now out of range - if (m_pItemInReach && (m_pItemInReach->IsUnPickupable() || (m_pItemInReach->HasPickupLimitations() && !m_pItemInReach->IsPickupableBy(this)) || !g_MovableMan.IsDevice(m_pItemInReach) || g_SceneMan.ShortestDistance(reachPoint, m_pItemInReach->GetPos(), g_SceneMan.SceneWrapsX()).GetMagnitude() > reach + m_pItemInReach->GetRadius())) { - m_pItemInReach = 0; + if (m_pItemInReach && (!m_pFGArm || m_pItemInReach->IsUnPickupable() || (m_pItemInReach->HasPickupLimitations() && !m_pItemInReach->IsPickupableBy(this)) || !g_MovableMan.IsDevice(m_pItemInReach) || g_SceneMan.ShortestDistance(reachPoint, m_pItemInReach->GetPos(), g_SceneMan.SceneWrapsX()).GetMagnitude() > reach + m_pItemInReach->GetRadius())) { + m_pItemInReach = nullptr; m_PieNeedsUpdate = true; } @@ -4004,34 +4000,25 @@ void AHuman::Update() ///////////////////////////////// // Manage Attachable:s - if (m_pHead && m_pHead->IsAttached()) { + if (m_pHead) { float toRotate = 0; // Only rotate the head to match the aim angle if body is stable and upright if (m_Status == STABLE && std::abs(rot) < (c_HalfPI + c_QuarterPI)) { toRotate = m_pHead->GetRotMatrix().GetRadAngleTo((m_AimAngle * GetFlipFactor()) * m_LookToAimRatio + rot * (0.9F - m_LookToAimRatio)) * 0.15F; - } - // If dying upright, make head slump forward or back depending on body lean -// TODO: Doesn't work too well, but probably could -// else if ((m_Status == DEAD || m_Status == DYING) && fabs(m_Rotation.GetRadAngle()) < c_QuarterPI) -// { -// toRotate = m_pHead->GetRotMatrix().GetRadAngleTo(m_Rotation.GetRadAngle() + ((m_HFlipped && m_Rotation.GetRadAngle() > 0) || (!m_HFlipped && m_Rotation.GetRadAngle() > 0) ? c_PI : -c_PI) * 0.6); -// toRotate *= 0.10; -// } - // Make head just keep rotating loosely with the body if unstable or upside down - else { - toRotate = m_pHead->GetRotMatrix().GetRadAngleTo(m_Rotation.GetRadAngle()); - toRotate *= 0.10F; + } else { + // Rotate the head loosely along with the body if upside down, unstable or dying. + toRotate = m_pHead->GetRotMatrix().GetRadAngleTo(rot) * m_pHead->GetJointStiffness() * (std::abs(toRotate) + c_QuarterPI); } // Now actually rotate by the amount calculated above m_pHead->SetRotAngle(m_pHead->GetRotAngle() + toRotate); } - if (m_pFGLeg && m_pFGLeg->IsAttached()) { + if (m_pFGLeg) { m_pFGLeg->EnableIdle(m_ProneState == NOTPRONE && m_Status != UNSTABLE); m_pFGLeg->SetTargetPosition(m_pFGFootGroup->GetLimbPos(m_HFlipped)); } - if (m_pBGLeg && m_pBGLeg->IsAttached()) { + if (m_pBGLeg) { m_pBGLeg->EnableIdle(m_ProneState == NOTPRONE && m_Status != UNSTABLE); m_pBGLeg->SetTargetPosition(m_pBGFootGroup->GetLimbPos(m_HFlipped)); } @@ -4109,9 +4096,9 @@ void AHuman::Update() // Reset this each frame m_SharpAimMaxedOut = false; - if (m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->HoldsHeldDevice()) - { - float maxLength = m_pFGArm->GetHeldDevice()->GetSharpLength(); + if (m_pFGArm && m_pFGArm->HoldsHeldDevice()) { + HeldDevice *heldDevice = m_pFGArm->GetHeldDevice(); + float maxLength = heldDevice->GetSharpLength(); if (maxLength == 0) { m_SharpAimProgress = 0; m_SharpAimMaxedOut = true; @@ -4120,17 +4107,27 @@ void AHuman::Update() } // Use a non-terrain check ray to cap the magnitude, so we can't see into objects etc if (m_SharpAimProgress > 0) { - if (m_pFGArm->GetHeldDevice()->IsRecoiled()) { - float totalGripStrength = m_pFGArm->GetGripStrength() + (m_pBGArm && m_pBGArm->IsAttached() && m_pBGArm->HoldsHeldDevice() ? m_pBGArm->GetGripStrength() : 0); - m_SharpAimProgress *= 1 - std::min(m_pFGArm->GetHeldDevice()->GetRecoilForce().GetMagnitude() / std::max(totalGripStrength * m_pFGArm->GetHeldDevice()->GetGripStrengthMultiplier(), 1.0F), 1.0F); + // TODO: make an uniform function to get the total GripStrength of an AHuman? + float totalGripStrength = m_pFGArm->GetGripStrength(); + if (m_pBGArm) { + if (m_pBGArm->HoldsHeldDevice()) { + HeldDevice *heldBGDevice = m_pBGArm->GetHeldDevice(); + if (heldBGDevice->IsRecoiled()) { + m_SharpAimProgress *= 1.0F - std::min(heldBGDevice->GetRecoilForce().GetMagnitude() / std::max(m_pBGArm->GetGripStrength() * heldBGDevice->GetGripStrengthMultiplier(), 1.0F), 1.0F); + } + } else if (heldDevice->GetSupported()) { + totalGripStrength += m_pBGArm->GetGripStrength(); + } + } + if (heldDevice->IsRecoiled()) { + m_SharpAimProgress *= 1.0F - std::min(heldDevice->GetRecoilForce().GetMagnitude() / std::max(totalGripStrength * heldDevice->GetGripStrengthMultiplier(), 1.0F), 1.0F); } Vector notUsed; Vector sharpAimVector(maxLength, 0); sharpAimVector *= aimMatrix; // See how far along the sharp aim vector there is opaque air -// float result = g_SceneMan.CastNotMaterialRay(m_pFGArm->GetHeldDevice()->GetMuzzlePos(), sharpAimVector, g_MaterialAir, 5); - float result = g_SceneMan.CastObstacleRay(m_pFGArm->GetHeldDevice()->GetMuzzlePos(), sharpAimVector, notUsed, notUsed, GetRootID(), IgnoresWhichTeam(), g_MaterialAir, 5); + float result = g_SceneMan.CastObstacleRay(heldDevice->GetMuzzlePos(), sharpAimVector, notUsed, notUsed, GetRootID(), IgnoresWhichTeam(), g_MaterialAir, 5); // If we didn't find anything but air before the sharpdistance, then don't alter the sharp distance if (result >= 0 && result < (maxLength * m_SharpAimProgress)) { @@ -4138,11 +4135,7 @@ void AHuman::Update() m_SharpAimMaxedOut = true; } } - // Indicate maxed outedness if we really are, too - if (m_SharpAimProgress > 0.9) - m_SharpAimMaxedOut = true; - -// sharpDistance *= m_Controller.GetAnalogAim().GetMagnitude(); + if (m_SharpAimProgress > 0.9F) { m_SharpAimMaxedOut = true; } aimSight.m_X += maxLength * m_SharpAimProgress; } @@ -4193,16 +4186,14 @@ void AHuman::Update() // Balance stuff // Eliminate full rotations - while (fabs(rot) > c_TwoPI) { + while (std::abs(rot) > c_TwoPI) { rot -= rot > 0 ? c_TwoPI : -c_TwoPI; } // Eliminate rotations over half a turn - if (fabs(rot) > c_PI) - { + if (std::abs(rot) > c_PI) { rot = (rot > 0 ? -c_PI : c_PI) + (rot - (rot > 0 ? c_PI : -c_PI)); // If we're upside down, we're unstable damnit - if (m_Status != DYING && m_Status != DEAD) - m_Status = UNSTABLE; + if (m_Status != DYING && m_Status != DEAD) { m_Status = UNSTABLE; } m_StableRecoverTimer.Reset(); } @@ -4215,80 +4206,57 @@ void AHuman::Update() float rotTarget = m_HFlipped ? c_HalfPI : -c_HalfPI; float rotDiff = rotTarget - rot; - if (m_ProneState == GOPRONE) - { - if (!m_ProneTimer.IsPastSimMS(333)) - { - if (fabs(rotDiff) > 0.1 && fabs(rotDiff) < c_PI) - { - m_AngularVel += rotDiff * 0.45;// * fabs(rotDiff); - m_Vel.m_X += (m_HFlipped ? -fabs(rotDiff) : fabs(rotDiff)) * 0.25; - } - } - // Done going down, now stay down without spring - else - { - m_AngularVel *= 0.5; - m_ProneState = PRONE; + if (m_ProneState == GOPRONE) { + if (!m_ProneTimer.IsPastSimMS(333)) { + if (std::abs(rotDiff) > 0.1F && std::abs(rotDiff) < c_PI) { + m_AngularVel += rotDiff * 0.4F; + m_Vel.m_X += (m_HFlipped ? -std::abs(rotDiff) : std::abs(rotDiff)) / std::max(m_Vel.GetMagnitude(), 4.0F); + } + } else { + // Done going down, now stay down without spring. + m_AngularVel *= 0.5F; + m_ProneState = PRONE; } -/* - // Break the spring if close to target angle. - if (-c_HalfPI + fabs(rot) > 0.1) - m_AngularVel -= rot * fabs(rot); - else if (fabs(m_AngularVel) > 0.1) - m_AngularVel *= 0.5; -*/ - } - // If down, try to keep flat against the ground - else if (m_ProneState == PRONE) - { - if (fabs(rotDiff) > c_SixteenthPI && fabs(rotDiff) < c_HalfPI) - m_AngularVel += rotDiff * 0.65;// * fabs(rotDiff); - else if (fabs(m_AngularVel) > 0.3) - m_AngularVel *= 0.85; - } + } else if (m_ProneState == PRONE) { + // If down, try to keep flat against the ground. + if (std::abs(rotDiff) > c_SixteenthPI && std::abs(rotDiff) < c_HalfPI) { + m_AngularVel += rotDiff * 0.65F; + } else if (std::abs(m_AngularVel) > 0.3F) { + m_AngularVel *= 0.85F; + } + } } else { // Upright body posture float rotDiff = rot - (GetRotAngleTarget(m_MoveState) * (m_AimAngle > 0 ? 1.0F - (m_AimAngle / c_HalfPI) : 1.0F) * GetFlipFactor()); m_AngularVel = m_AngularVel * (0.98F - 0.06F * (m_Health / m_MaxHealth)) - (rotDiff * 0.5F); } } - // Keel over - else if (m_Status == UNSTABLE && fabs(m_AngularVel) < 5) - { - float rotTarget = 0; - // If traveling at speed, then always start falling forward - if (fabs(m_Vel.m_X) > 1.0) - rotTarget = m_HFlipped ? c_HalfPI : -c_HalfPI; - // Go whichever way we're already rotated - else - rotTarget = rot > 0 ? c_HalfPI : -c_HalfPI; - - float rotDiff = rotTarget - rot; - if (fabs(rotDiff) > 0.1 && fabs(rotDiff) < c_PI) - { - m_AngularVel += rotDiff * 0.05; -// m_Vel.m_X += (rotTarget > 0 ? -fabs(rotDiff) : fabs(rotDiff)) * 0.35; - } - } - // While dying, pull body quickly toward down toward horizontal - else if (m_Status == DYING) - { - float rotTarget = rot > 0 ? c_HalfPI : -c_HalfPI; -// float rotTarget = m_HFlipped ? c_HalfPI : -c_HalfPI; - float rotDiff = rotTarget - rot; - if (!m_DeathTmr.IsPastSimMS(125) && fabs(rotDiff) > 0.1 && fabs(rotDiff) < c_PI) - { - m_AngularVel += rotDiff * 0.5;//fabs(rotDiff); -// m_Vel.m_X += (m_HFlipped ? -fabs(rotDiff) : fabs(rotDiff)) * 0.35; - m_Vel.m_X += (rotTarget > 0 ? -fabs(rotDiff) : fabs(rotDiff)) * 0.35; - } - else - m_Status = DEAD; + else if (m_Status == UNSTABLE) { + float rotTarget = 0; + // If traveling at speed, always start falling forward. + if (std::abs(m_Vel.m_X) > 1.0F) { + rotTarget = m_HFlipped ? c_HalfPI : -c_HalfPI; + } else { + // Otherwise, go whichever way we're already rotated. + rotTarget = rot > 0 ? c_HalfPI : -c_HalfPI; + } -// else if (fabs(m_AngularVel) > 0.1) -// m_AngularVel *= 0.5; - } + float rotDiff = rotTarget - rot; + if (std::abs(rotDiff) > 0.1F && std::abs(rotDiff) < c_PI) { + m_AngularVel += rotDiff * 0.05F; + } + } else if (m_Status == DYING) { + float rotTarget = m_Vel.m_X - (rot + m_AngularVel) > 0 ? -c_HalfPI : c_HalfPI; + float rotDiff = rotTarget - rot; + if (!m_DeathTmr.IsPastSimMS(125) && std::abs(rotDiff) > 0.1F && std::abs(rotDiff) < c_PI) { + // TODO: finetune this for situations like low gravity! + float velScalar = 0.5F; //* (g_SceneMan.GetGlobalAcc().GetY * m_GlobalAccScalar) / GetPPM(); + m_AngularVel += rotDiff * velScalar; + m_Vel.m_X += (rotTarget > 0 ? -std::abs(rotDiff) : std::abs(rotDiff)) * velScalar * 0.5F; + } else { + m_Status = DEAD; + } + } m_Rotation.SetRadAngle(rot); /////////////////////////////////////////////////// @@ -4300,12 +4268,9 @@ void AHuman::Update() m_Health -= 0.1F; } - if (m_Status == DYING) - { - if (m_pFGArm && m_pFGArm->IsAttached()) - m_pFGArm->DropEverything(); - if (m_pBGArm && m_pBGArm->IsAttached()) - m_pBGArm->DropEverything(); + if (m_Status == DYING) { + if (m_pFGArm) { m_pFGArm->DropEverything(); } + if (m_pBGArm) { m_pBGArm->DropEverything(); } } ///////////////////////////////////////// @@ -4613,15 +4578,9 @@ void AHuman::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichSc } // Pickup GUI - if (!m_Controller.IsState(PIE_MENU_ACTIVE)) - { - if (m_pItemInReach && g_MovableMan.IsDevice(m_pItemInReach) && m_pFGArm && m_pFGArm->IsAttached()) - { - std::snprintf(str, sizeof(str), " %c %s", -49, m_pItemInReach->GetPresetName().c_str()); - pSmallFont->DrawAligned(&allegroBitmap, drawPos.m_X - 12, drawPos.m_Y + m_HUDStack + 3, str, GUIFont::Left); - } - else - m_pItemInReach = 0; + if (!m_Controller.IsState(PIE_MENU_ACTIVE) && m_pItemInReach) { + std::snprintf(str, sizeof(str), " %c %s", -49, m_pItemInReach->GetPresetName().c_str()); + pSmallFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() - 12, drawPos.GetFloorIntY() + m_HUDStack + 3, str, GUIFont::Left); } /* // AI Mode select GUI HUD From 600b5fa0ab44bb5881a86f982b6efa41715d7ce1 Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 17 Aug 2021 15:37:46 +0300 Subject: [PATCH 11/72] Trying to put some sense into the `FlailAsLimb` functions while refactoring, although the limb path section of AHuman needs more looking into in the future; Added "artificial G-forces" into limb paths, meaning they now adapt better to different velocities --- Entities/AHuman.cpp | 213 +++++++++++------------------------------- Entities/LimbPath.cpp | 5 +- Entities/LimbPath.h | 9 -- 3 files changed, 55 insertions(+), 172 deletions(-) diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index 202a72a07..7879be02b 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -3831,172 +3831,63 @@ void AHuman::Update() m_Paths[FGROUND][CRAWL].Terminate(); m_Paths[BGROUND][CRAWL].Terminate(); } - } - // JUMPING - else if ((m_pFGLeg || m_pBGLeg) && m_MoveState == JUMP) { - //TODO 4zK Uncomment this section to keep the limb held static - /* - if (m_pFGLeg) { - m_pFGFootGroup->SetLimbPos(m_Pos + RotateOffset(m_Paths[FGROUND][STAND].GetStartOffset())); - } - if (m_pBGLeg) { - m_pBGFootGroup->SetLimbPos(m_Pos + RotateOffset(m_Paths[BGROUND][STAND].GetStartOffset())); - } - */ - - //TODO 4zK Uncomment this section to make the limb follow its jump path. I believe this was data's original intention but - // 1. The existing standard jump limbpath is awful, the actor spends all its time squatting - // 2. I'm not sure of the details, but this push as limb doesn't seem to be advancing the jump limbpath, so it doesn't work very well, even with my efforts to properly reset it - /* - if (m_pFGLeg && (!m_Paths[FGROUND][m_MoveState].PathEnded() || m_JetTimeLeft == m_JetTimeTotal)) { - m_pFGFootGroup->PushAsLimb( - m_Pos + m_pFGLeg->GetParentOffset().GetXFlipped(m_HFlipped), - m_Vel, - Matrix(), - m_Paths[FGROUND][m_MoveState], - deltaTime); - } - if (m_pBGLeg && (!m_Paths[BGROUND][m_MoveState].PathEnded() || m_JetTimeLeft == m_JetTimeTotal)) { - m_pBGFootGroup->PushAsLimb( - m_Pos + m_pBGLeg->GetParentOffset().GetXFlipped(m_HFlipped), - m_Vel, - Matrix(), - m_Paths[BGROUND][m_MoveState], - deltaTime); - } - */ - if (m_pFGLeg && (!m_Paths[FGROUND][m_MoveState].PathEnded() || m_JetTimeLeft == m_JetTimeTotal)) { - m_pFGFootGroup->FlailAsLimb( - m_Pos, - m_pFGLeg->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation, - m_pFGLeg->GetMaxLength(), - g_SceneMan.GetGlobalAcc() * g_TimerMan.GetDeltaTimeSecs(), - m_AngularVel, - m_pFGLeg->GetMass(), - g_TimerMan.GetDeltaTimeSecs()); - } - if (m_pBGLeg && (!m_Paths[BGROUND][m_MoveState].PathEnded() || m_JetTimeLeft == m_JetTimeTotal)) { - m_pBGFootGroup->FlailAsLimb( - m_Pos, - m_pBGLeg->GetParentOffset().GetXFlipped(m_HFlipped) *m_Rotation, - m_pBGLeg->GetMaxLength(), - g_SceneMan.GetGlobalAcc() *g_TimerMan.GetDeltaTimeSecs(), - m_AngularVel, - m_pBGLeg->GetMass(), - g_TimerMan.GetDeltaTimeSecs()); - } + } else if (m_pFGLeg || m_pBGLeg) { + if (m_MoveState == JUMP) { + // TODO: Utilize jump paths in an intuitive way! + if (m_pFGLeg && (!m_Paths[FGROUND][m_MoveState].PathEnded() || m_JetTimeLeft == m_JetTimeTotal)) { + m_pFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pFGLeg->GetParentOffset()), m_pFGLeg->GetMaxLength(), g_SceneMan.GetGlobalAcc() * deltaTime, m_AngularVel, m_pFGLeg->GetMass(), deltaTime); + } + if (m_pBGLeg && (!m_Paths[BGROUND][m_MoveState].PathEnded() || m_JetTimeLeft == m_JetTimeTotal)) { + m_pBGFootGroup->FlailAsLimb( m_Pos, RotateOffset(m_pBGLeg->GetParentOffset()), m_pBGLeg->GetMaxLength(), g_SceneMan.GetGlobalAcc() * deltaTime, m_AngularVel, m_pBGLeg->GetMass(), deltaTime); + } + if (m_JetTimeLeft <= 0) { + m_MoveState = STAND; + m_Paths[FGROUND][JUMP].Terminate(); + m_Paths[BGROUND][JUMP].Terminate(); + m_Paths[FGROUND][STAND].Terminate(); + m_Paths[BGROUND][STAND].Terminate(); + m_Paths[FGROUND][WALK].Terminate(); + m_Paths[BGROUND][WALK].Terminate(); + } + } else if (m_MoveState == CROUCH) { + m_Paths[FGROUND][WALK].Terminate(); + m_Paths[BGROUND][WALK].Terminate(); + m_Paths[FGROUND][CRAWL].Terminate(); + m_Paths[BGROUND][CRAWL].Terminate(); - if (m_JetTimeLeft <= 0) { - m_MoveState = STAND; - m_Paths[FGROUND][JUMP].Terminate(); - m_Paths[BGROUND][JUMP].Terminate(); - m_Paths[FGROUND][STAND].Terminate(); - m_Paths[BGROUND][STAND].Terminate(); - m_Paths[FGROUND][WALK].Terminate(); - m_Paths[BGROUND][WALK].Terminate(); - } - } - // CROUCHING - else if ((m_pFGLeg || m_pBGLeg) && m_MoveState == CROUCH) - { - m_Paths[FGROUND][WALK].Terminate(); - m_Paths[BGROUND][WALK].Terminate(); - m_Paths[FGROUND][CRAWL].Terminate(); - m_Paths[BGROUND][CRAWL].Terminate(); + if (m_pFGLeg) { m_pFGFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pFGLeg->GetParentOffset().GetXFlipped(m_HFlipped), m_Vel, Matrix(), m_Paths[FGROUND][CROUCH], deltaTime); } - if (m_pFGLeg) - m_pFGFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pFGLeg->GetParentOffset().GetXFlipped(m_HFlipped), - m_Vel, - Matrix(), - m_Paths[FGROUND][CROUCH], - deltaTime); + if (m_pBGLeg) { m_pBGFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pBGLeg->GetParentOffset().GetXFlipped(m_HFlipped), m_Vel, Matrix(), m_Paths[BGROUND][CROUCH], deltaTime); } - if (m_pBGLeg) - m_pBGFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pBGLeg->GetParentOffset().GetXFlipped(m_HFlipped), - m_Vel, - Matrix(), - m_Paths[BGROUND][CROUCH], - deltaTime); - } - // STANDING - else if (m_pFGLeg || m_pBGLeg) - { - m_Paths[FGROUND][WALK].Terminate(); - m_Paths[BGROUND][WALK].Terminate(); - m_Paths[FGROUND][CRAWL].Terminate(); - m_Paths[BGROUND][CRAWL].Terminate(); + } else { + m_Paths[FGROUND][WALK].Terminate(); + m_Paths[BGROUND][WALK].Terminate(); + m_Paths[FGROUND][CRAWL].Terminate(); + m_Paths[BGROUND][CRAWL].Terminate(); + m_Paths[FGROUND][ARMCRAWL].Terminate(); + m_Paths[BGROUND][ARMCRAWL].Terminate(); - if (m_pFGLeg) - m_pFGFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pFGLeg->GetParentOffset().GetXFlipped(m_HFlipped), - m_Vel, - Matrix(), - m_Paths[FGROUND][STAND], - deltaTime, - 0, - false); - - if (m_pBGLeg) - m_pBGFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pBGLeg->GetParentOffset().GetXFlipped(m_HFlipped), - m_Vel, - Matrix(), - m_Paths[BGROUND][STAND], - deltaTime, - 0, - false); - } - } - // Not stable/standing, so make sure the end of limbs are moving around limply in a ragdoll fashion - else - { + if (m_pFGLeg) { m_pFGFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pFGLeg->GetParentOffset().GetXFlipped(m_HFlipped), m_Vel, Matrix(), m_Paths[FGROUND][STAND], deltaTime, 0, false); } -// TODO: Make the limb atom groups fly around and react to terrain, without getting stuck etc - bool wrapped = false; - Vector limbPos; - if (m_pFGArm) - { -// m_pFGHandGroup->SetLimbPos(m_pFGArm->GetHandPos(), m_HFlipped); - m_pFGHandGroup->FlailAsLimb(m_Pos, - m_pFGArm->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation, - m_pFGArm->GetMaxLength(), - g_SceneMan.GetGlobalAcc() * g_TimerMan.GetDeltaTimeSecs(), - m_AngularVel, - m_pFGArm->GetMass(), - g_TimerMan.GetDeltaTimeSecs()); - } - if (m_pBGArm) - { -// m_pBGHandGroup->SetLimbPos(m_pBGArm->GetHandPos(), m_HFlipped); - m_pBGHandGroup->FlailAsLimb(m_Pos, - m_pBGArm->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation, - m_pBGArm->GetMaxLength(), - g_SceneMan.GetGlobalAcc() * g_TimerMan.GetDeltaTimeSecs(), - m_AngularVel, - m_pBGArm->GetMass(), - g_TimerMan.GetDeltaTimeSecs()); - } - if (m_pFGLeg) - { -// m_pFGFootGroup->SetLimbPos(m_pFGLeg->GetAnklePos(), m_HFlipped); - m_pFGFootGroup->FlailAsLimb(m_Pos, - m_pFGLeg->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation, - m_pFGLeg->GetMaxLength(), - g_SceneMan.GetGlobalAcc() * g_TimerMan.GetDeltaTimeSecs(), - m_AngularVel, - m_pFGLeg->GetMass(), - g_TimerMan.GetDeltaTimeSecs()); - } - if (m_pBGLeg) - { -// m_pBGFootGroup->SetLimbPos(m_pBGLeg->GetAnklePos(), m_HFlipped); - m_pBGFootGroup->FlailAsLimb(m_Pos, - m_pBGLeg->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation, - m_pBGLeg->GetMaxLength(), - g_SceneMan.GetGlobalAcc() * g_TimerMan.GetDeltaTimeSecs(), - m_AngularVel, - m_pBGLeg->GetMass(), - g_TimerMan.GetDeltaTimeSecs()); - } - } + if (m_pBGLeg) { m_pBGFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pBGLeg->GetParentOffset().GetXFlipped(m_HFlipped), m_Vel, Matrix(), m_Paths[BGROUND][STAND], deltaTime, 0, false); } + } + } + } else { + // Not stable/standing, so make sure the end of limbs are moving around limply in a ragdoll fashion. + // TODO: Make the limb atom groups fly around and react to terrain, without getting stuck etc. + if (m_pFGArm) { + m_pFGHandGroup->FlailAsLimb(m_Pos, RotateOffset(m_pFGArm->GetParentOffset()), m_pFGArm->GetMaxLength(), (g_SceneMan.GetGlobalAcc() * deltaTime).RadRotate(m_AngularVel * deltaTime), m_AngularVel, m_pFGArm->GetMass(), deltaTime); + } + if (m_pBGArm) { + m_pBGHandGroup->FlailAsLimb(m_Pos, RotateOffset(m_pBGArm->GetParentOffset()), m_pBGArm->GetMaxLength(), (g_SceneMan.GetGlobalAcc() * deltaTime).RadRotate(m_AngularVel * deltaTime), m_AngularVel, m_pBGArm->GetMass(), deltaTime); + } + if (m_pFGLeg) { + m_pFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pFGLeg->GetParentOffset()), m_pFGLeg->GetMaxLength(), (g_SceneMan.GetGlobalAcc() * deltaTime).RadRotate(m_AngularVel * deltaTime), m_AngularVel, m_pFGLeg->GetMass(), deltaTime); + } + if (m_pBGLeg) { + m_pBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pBGLeg->GetParentOffset()), m_pBGLeg->GetMaxLength(), (g_SceneMan.GetGlobalAcc() * deltaTime).RadRotate(m_AngularVel * deltaTime), m_AngularVel, m_pBGLeg->GetMass(), deltaTime); + } + } ///////////////////////////////// // Manage Attachable:s diff --git a/Entities/LimbPath.cpp b/Entities/LimbPath.cpp index 018d49680..06e57591f 100644 --- a/Entities/LimbPath.cpp +++ b/Entities/LimbPath.cpp @@ -300,11 +300,12 @@ Vector LimbPath::GetCurrentVel(const Vector &limbPos) { Vector returnVel; Vector distVect = g_SceneMan.ShortestDistance(limbPos, GetCurrentSegTarget()); + float adjustedTravelSpeed = m_TravelSpeed[m_WhichSpeed] / (1.0F + m_JointVel.GetMagnitude() * 0.1F); if (IsStaticPoint()) { returnVel = distVect * c_MPP / 0.020/* + m_JointVel*/; - returnVel.CapMagnitude(m_TravelSpeed[m_WhichSpeed]); + returnVel.CapMagnitude(adjustedTravelSpeed); returnVel += m_JointVel; // if (distVect.GetMagnitude() < 0.5) @@ -312,7 +313,7 @@ Vector LimbPath::GetCurrentVel(const Vector &limbPos) } else { - returnVel.SetXY(m_TravelSpeed[m_WhichSpeed], 0); + returnVel.SetXY(adjustedTravelSpeed, 0); if (!distVect.IsZero()) returnVel.AbsRotateTo(distVect); diff --git a/Entities/LimbPath.h b/Entities/LimbPath.h index 90f46eeaa..901390dd9 100644 --- a/Entities/LimbPath.h +++ b/Entities/LimbPath.h @@ -449,15 +449,6 @@ ClassInfoGetters void SetHFlip(bool hflipped) { m_HFlipped = hflipped; -/* - if (m_HFlipped != hflipped) { - // Gotta flip before restart. - m_HFlipped = hflipped; - Restart(); - } - else - m_HFlipped = hflipped; -*/ m_Rotation.SetXFlipped(m_HFlipped); } From 714ff1cbe491a0ea96de45465bebeea24cb010b2 Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 17 Aug 2021 15:41:21 +0300 Subject: [PATCH 12/72] Added stance offset manipulation per actual recoil. Can be disabled with `RecoilTransmission`, the same way as normal recoil --- Entities/Arm.cpp | 14 +++++++++++--- Entities/HDFirearm.cpp | 10 +++++----- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/Entities/Arm.cpp b/Entities/Arm.cpp index b1ee9e7fb..2d664d874 100644 --- a/Entities/Arm.cpp +++ b/Entities/Arm.cpp @@ -389,9 +389,17 @@ void Arm::UpdateCurrentHandOffset() { Vector targetOffset; if (m_pHeldMO && !dynamic_cast(m_pHeldMO)) { m_DidReach = false; - const HeldDevice *heldDevice = dynamic_cast(m_pHeldMO); - targetOffset = heldDevice->GetStanceOffset() * m_Rotation; - + HeldDevice *heldDevice = dynamic_cast(m_pHeldMO); + // TODO: calculate total grip strength from both arms? (also: fine-tune this shit) + float totalGripStrength = (m_GripStrength || heldDevice->GetJointStrength()) * (heldDevice->GetSupported() ? 2.0F : 1.0F); + targetOffset = heldDevice->GetStanceOffset(); + // Diminish recoil effect when body is horizontal so that the device doesn't get pushed into terrain when prone. + float rotAngleScalar = std::abs(std::cos(m_Parent->GetRotAngle())); + float recoilScalar = std::min((heldDevice->GetRecoilForce() / totalGripStrength).GetMagnitude() * 0.4F, 0.8F) * rotAngleScalar; + targetOffset.SetX(targetOffset.GetX() * (1.0F - recoilScalar)); + // Shift Y offset slightly so the device is more likely to go under the shoulder rather than over it. (otherwise it looks goofy) + if (targetOffset.GetY() <= 0) { targetOffset.SetY(targetOffset.GetY() * (1.0F - recoilScalar) + recoilScalar); } + targetOffset *= m_Rotation; // In order to keep the held device from clipping through terrain, we need to determine where its muzzle position will be, and use that to figure out where its midpoint will be, as well as the distance between the two. Vector newMuzzlePos = (m_JointPos + targetOffset) - RotateOffset(heldDevice->GetJointOffset()) + RotateOffset(heldDevice->GetMuzzleOffset()); Vector midToMuzzle = RotateOffset({heldDevice->GetRadius(), 0}); diff --git a/Entities/HDFirearm.cpp b/Entities/HDFirearm.cpp index fc63c3fdf..2ed7d312d 100644 --- a/Entities/HDFirearm.cpp +++ b/Entities/HDFirearm.cpp @@ -971,7 +971,7 @@ void HDFirearm::Update() // Set up the recoil shake offset m_RecoilOffset = m_RecoilForce; - m_RecoilOffset.SetMagnitude(1.25); + m_RecoilOffset.SetMagnitude(std::min(m_RecoilOffset.GetMagnitude(), 1.2F)); } AddImpulseForce(m_RecoilForce, m_RecoilOffset); @@ -997,11 +997,11 @@ void HDFirearm::Update() } if (m_FireEchoSound) { m_FireEchoSound->Play(m_Pos); } } - } - else { + } else { m_Recoiled = false; - if (!m_IsAnimatedManually) - m_Frame = 0; + // TODO: don't use arbitrary numbers? (see Arm.cpp) + m_RecoilForce *= 0.6F; + if (!m_IsAnimatedManually) { m_Frame = 0; } } // Display and override gun animation if there's a special one From 322effa5f1395cd53f1c313b1d4f7f3b75537640 Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 17 Aug 2021 15:48:18 +0300 Subject: [PATCH 13/72] `Actor` refactor; Added `StableRecoverDelay` which is now open for customization; Changed inventory dropping logic slightly so that it adapts better to actors of different sizes, such as exploding craft; Fixed damage sound playing in tandem with death sound when dying from impulse; --- Entities/Actor.cpp | 104 +++++++++++++++++------------------- Entities/Actor.h | 14 ++++- Lua/LuaBindingsEntities.cpp | 1 + 3 files changed, 64 insertions(+), 55 deletions(-) diff --git a/Entities/Actor.cpp b/Entities/Actor.cpp index 6a90c72dc..ffdca2918 100644 --- a/Entities/Actor.cpp +++ b/Entities/Actor.cpp @@ -73,22 +73,23 @@ void Actor::Clear() { m_LastSecondTimer.Reset(); m_LastSecondPos.Reset(); m_RecentMovement.Reset(); - m_RecentMovementMag = 0.0F; - m_TravelImpulseDamage = 750; - m_StableVel.SetXY(15, 25); + m_RecentMovementMag = 0; + m_TravelImpulseDamage = 750.0F; + m_StableVel.SetXY(15.0F, 25.0F); + m_StableRecoverDelay = 1000; m_HeartBeat.Reset(); m_NewControlTmr.Reset(); m_DeathTmr.Reset(); - m_GoldCarried = 0.0F; + m_GoldCarried = 0; m_GoldPicked = false; m_AimState = AIMSTILL; - m_AimAngle = 0.0F; + m_AimAngle = 0; m_AimRange = c_HalfPI; - m_AimDistance = 0.0F; + m_AimDistance = 0; m_AimTmr.Reset(); m_SharpAimTimer.Reset(); m_SharpAimDelay = 250; - m_SharpAimProgress = 0.0F; + m_SharpAimProgress = 0; m_SharpAimMaxedOut = false; m_PointingTarget.Reset(); m_SeenTargetPos.Reset(); @@ -96,8 +97,8 @@ void Actor::Clear() { m_AlarmTimer.SetSimTimeLimitMS(3000); m_AlarmTimer.SetElapsedSimTimeMS(4000); m_LastAlarmPos.Reset(); - m_SightDistance = 450; - m_Perceptiveness = 0.5; + m_SightDistance = 450.0F; + m_Perceptiveness = 0.5F; m_CanRevealUnseen = true; m_CharHeight = 0; m_HolsterOffset.Reset(); @@ -123,17 +124,17 @@ void Actor::Clear() { m_MoveVector.Reset(); m_MovePath.clear(); m_UpdateMovePath = true; - m_MoveProximityLimit = 100; + m_MoveProximityLimit = 100.0F; m_LateralMoveState = LAT_STILL; m_MoveOvershootTimer.Reset(); m_ObstacleState = PROCEEDING; m_TeamBlockState = NOTBLOCKED; m_BlockTimer.Reset(); - m_BestTargetProximity = 10000.0f; + m_BestTargetProximity = 10000.0F; m_ProgressTimer.Reset(); m_StuckTimer.Reset(); m_FallTimer.Reset(); - m_DigStrength = 1; + m_DigStrength = 1.0F; m_DamageMultiplier = 1.0F; } @@ -206,6 +207,7 @@ int Actor::Create(const Actor &reference) m_LastSecondPos = reference.m_LastSecondPos; m_TravelImpulseDamage = reference.m_TravelImpulseDamage; m_StableVel = reference.m_StableVel; + m_StableRecoverDelay = reference.m_StableRecoverDelay; m_GoldCarried = reference.m_GoldCarried; m_AimState = reference.m_AimState; m_AimRange = reference.m_AimRange; @@ -339,6 +341,8 @@ int Actor::ReadProperty(const std::string_view &propName, Reader &reader) reader >> m_TravelImpulseDamage; else if (propName == "StableVelocityThreshold") reader >> m_StableVel; + else if (propName == "StableRecoveryDelay") + reader >> m_StableRecoverDelay; else if (propName == "AimAngle") reader >> m_AimAngle; else if (propName == "AimRange") @@ -420,6 +424,8 @@ int Actor::Save(Writer &writer) const writer << m_TravelImpulseDamage; writer.NewProperty("StableVelocityThreshold"); writer << m_StableVel; + writer.NewProperty("StableRecoveryDelay"); + writer << m_StableRecoverDelay; writer.NewProperty("AimAngle"); writer << m_AimAngle; writer.NewProperty("AimRange"); @@ -885,7 +891,7 @@ void Actor::DropAllInventory() { MovableObject *pObject = 0; Actor *pPassenger = 0; - float velMin, velRange, angularVel; + float velMin, velMax, angularVel; Vector gibROffset, gibVel; for (deque::iterator gItr = m_Inventory.begin(); gItr != m_Inventory.end(); ++gItr) { @@ -895,12 +901,12 @@ void Actor::DropAllInventory() { // Generate the velocities procedurally velMin = 3.0F; - velRange = 10.0F; + velMax = velMin + std::sqrt(m_SpriteRadius); // Randomize the offset from center to be within the original object gibROffset.SetXY(m_SpriteRadius * 0.35F * RandomNormalNum(), m_SpriteRadius * 0.35F * RandomNormalNum()); // Set up its position and velocity according to the parameters of this AEmitter. - pObject->SetPos(m_Pos + gibROffset/*Vector(m_Pos.m_X + 5 * NormalRand(), m_Pos.m_Y + 5 * NormalRand())*/); + pObject->SetPos(m_Pos + gibROffset); pObject->SetRotAngle(m_Rotation.GetRadAngle() + pObject->GetRotMatrix().GetRadAngle()); // Rotational angle pObject->SetAngularVel((pObject->GetAngularVel() * 0.35F) + (pObject->GetAngularVel() * 0.65F / (pObject->GetMass() != 0 ? pObject->GetMass() : 0.0001F)) * RandomNum()); @@ -909,8 +915,8 @@ void Actor::DropAllInventory() if (gibROffset.m_X > m_aSprite[0]->w / 3) { float offCenterRatio = gibROffset.m_X / (m_aSprite[0]->w / 2); - angularVel = fabs(pObject->GetAngularVel() * 0.5F); - angularVel += fabs(pObject->GetAngularVel() * 0.5F * offCenterRatio); + angularVel = std::abs(pObject->GetAngularVel() * 0.5F); + angularVel += std::abs(pObject->GetAngularVel() * 0.5F * offCenterRatio); pObject->SetAngularVel(angularVel * (gibROffset.m_X > 0.0F ? -1 : 1)); } // Gib is too close to center to always make it rotate in one direction, so give it a baseline rotation and then randomize @@ -921,10 +927,11 @@ void Actor::DropAllInventory() // TODO: Optimize making the random angles!") gibVel = gibROffset; - if (gibVel.IsZero()) - gibVel.SetXY(velMin + RandomNum(0.0F, velRange), 0.0F); - else - gibVel.SetMagnitude(velMin + RandomNum(0.0F, velRange)); + if (gibVel.IsZero()) { + gibVel.SetXY(RandomNum(velMin, velMax), 0.0F); + } else { + gibVel.SetMagnitude(RandomNum(velMin, velMax)); + } // Don't! the offset was already rotated! // gibVel = RotateOffset(gibVel); // Distribute any impact implse out over all the gibs @@ -1456,47 +1463,41 @@ void Actor::Update() ///////////////////////////////////////////// // Take damage from large hits during travel - if (m_BodyHitSound && m_TravelImpulse.GetMagnitude() > m_TravelImpulseDamage / 2) { - m_BodyHitSound->Play(m_Pos); - } + if (m_TravelImpulse.GetMagnitude() > m_TravelImpulseDamage * 0.5F) { + if (m_BodyHitSound) { m_BodyHitSound->Play(m_Pos); } - if (m_TravelImpulse.GetMagnitude() > m_TravelImpulseDamage) - { - if (m_PainSound) { m_PainSound->Play(m_Pos); } - const float impulse = m_TravelImpulse.GetMagnitude() - m_TravelImpulseDamage; - const float damage = impulse / (m_GibImpulseLimit - m_TravelImpulseDamage) * m_MaxHealth; - if (damage > 0) - m_Health -= damage; - if (m_Status != DYING && m_Status != DEAD) - m_Status = UNSTABLE; - m_ForceDeepCheck = true; + if (m_TravelImpulse.GetMagnitude() > m_TravelImpulseDamage) { + const float impulse = m_TravelImpulse.GetMagnitude() - m_TravelImpulseDamage; + const float damage = impulse / (m_GibImpulseLimit - m_TravelImpulseDamage) * m_MaxHealth; + if (damage > 0) { + m_Health -= damage; + if (m_Health > 0) { + if (m_PainSound) { m_PainSound->Play(m_Pos); } + } + } + if (m_Status != DYING && m_Status != DEAD) { m_Status = UNSTABLE; } + m_ForceDeepCheck = true; + } } ///////////////////////////// // Stability logic - if (m_Status == STABLE) - { + if (m_Status == STABLE) { // If moving really fast, we're not able to be stable -// TODO don't hardcode this threshold! - if (fabs(m_Vel.m_X) > fabs(m_StableVel.m_X) || fabs(m_Vel.m_Y) > fabs(m_StableVel.m_Y)) - m_Status = UNSTABLE; + if (std::abs(m_Vel.m_X) > std::abs(m_StableVel.m_X) || std::abs(m_Vel.m_Y) > std::abs(m_StableVel.m_Y)) { m_Status = UNSTABLE; } m_StableRecoverTimer.Reset(); } - else if (m_Status == UNSTABLE) - { + else if (m_Status == UNSTABLE) { // Only regain stability if we're not moving too fast and it's been a while since we lost it - if (m_StableRecoverTimer.IsPastSimMS(1000) && !(fabs(m_Vel.m_X) > fabs(m_StableVel.m_X) || fabs(m_Vel.m_Y) > fabs(m_StableVel.m_Y))) - m_Status = STABLE; + if (m_StableRecoverTimer.IsPastSimMS(m_StableRecoverDelay) && !(std::abs(m_Vel.m_X) > std::abs(m_StableVel.m_X) || std::abs(m_Vel.m_Y) > std::abs(m_StableVel.m_Y))) { m_Status = STABLE; } } // Spread the carried items and gold around before death. - if (m_Status == DYING || m_Status == DEAD) - { + if (m_Status == DYING || m_Status == DEAD) { // Actor may die for a long time, no need to call this more than once - if (m_Inventory.size() > 0) - DropAllInventory(); + if (m_Inventory.size() > 0) { DropAllInventory(); } Material const * AuMat = g_SceneMan.GetMaterial(std::string("Gold")); int goldCount = m_GoldCarried/*std::floor(GetGoldCarried())*/; @@ -1538,13 +1539,9 @@ void Actor::Update() } // Prevent dead actors from rotating like mad - if (m_Status == DYING || m_Status == DEAD) - { - m_AngularVel = m_AngularVel * 0.98; - } + if (m_Status == DYING || m_Status == DEAD) { m_AngularVel = m_AngularVel * 0.98F; } - if (m_Status == DYING && m_DeathTmr.GetElapsedSimTimeMS() > 1000) - m_Status = DEAD; + if (m_Status == DYING && m_DeathTmr.GetElapsedSimTimeMS() > 1000) { m_Status = DEAD; } ////////////////////////////////////////////////////// // Save previous second's position so we can detect larger movement @@ -1796,7 +1793,6 @@ void Actor::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichScr } */ std::snprintf(str, sizeof(str), "%.0f", m_Health); -// pSmallFont->DrawAligned(&bitmapInt, drawPos.m_X - 0, drawPos.m_Y - 35, str, GUIFont::Left); pSymbolFont->DrawAligned(&bitmapInt, drawPos.m_X - 0, drawPos.m_Y + m_HUDStack, str, GUIFont::Left); m_HUDStack += -12; diff --git a/Entities/Actor.h b/Entities/Actor.h index 93eafe83e..23c7b8a4a 100644 --- a/Entities/Actor.h +++ b/Entities/Actor.h @@ -1313,6 +1313,18 @@ ClassInfoGetters /// Vector with new values for how fast the actor can travel before losing stability on both axis. void SetStableVel(Vector newVelVector) { m_StableVel = newVelVector; } + /// + /// Gets the recovery delay from UNSTABLE to STABLE, in MS. + /// + /// The recovery delay, in MS. + int GetStableRecoverDelay() const { return m_StableRecoverDelay; } + + /// + /// Sets the recovery delay from UNSTABLE to STABLE, in MS. + /// + /// The recovery delay, in MS. + void SetStableRecoverDelay(int newRecoverDelay) { m_StableRecoverDelay = newRecoverDelay; } + ////////////////////////////////////////////////////////////////////////////////////////// // Protected member variable and method declarations @@ -1347,7 +1359,6 @@ ClassInfoGetters SoundContainer *m_DeathSound; SoundContainer *m_DeviceSwitchSound; -// bool m_FacingRight; int m_Status; float m_Health; // Maximum health @@ -1371,6 +1382,7 @@ ClassInfoGetters Timer m_StableRecoverTimer; // Thresholds in both x and y for how fast the actor can travel before losing stability. Meters per second (m/s). Vector m_StableVel; + int m_StableRecoverDelay; //!< The delay before regaining stability after losing it, in MS // Timer for the heartbeat of this Actor Timer m_HeartBeat; // Timer for timing how long this has been under Control diff --git a/Lua/LuaBindingsEntities.cpp b/Lua/LuaBindingsEntities.cpp index 44af6d339..2d1199586 100644 --- a/Lua/LuaBindingsEntities.cpp +++ b/Lua/LuaBindingsEntities.cpp @@ -204,6 +204,7 @@ namespace RTE { .property("DeathSound", &Actor::GetDeathSound, &ActorSetDeathSound) .property("DeviceSwitchSound", &Actor::GetDeviceSwitchSound, &ActorSetDeviceSwitchSound) .property("ImpulseDamageThreshold", &Actor::GetTravelImpulseDamage, &Actor::SetTravelImpulseDamage) + .property("StableRecoveryDelay", &Actor::GetStableRecoverDelay, &Actor::SetStableRecoverDelay) .property("Status", &Actor::GetStatus, &Actor::SetStatus) .property("Health", &Actor::GetHealth, &Actor::SetHealth) .property("PrevHealth", &Actor::GetPrevHealth) From 70c409a0b890197e64c4125e4674dacb62c9bb82 Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 17 Aug 2021 15:49:29 +0300 Subject: [PATCH 14/72] Introducing `Gib` property `SpreadMode`, stand by for changelog entry --- Entities/Gib.cpp | 4 ++ Entities/Gib.h | 14 +++++ Entities/MOSRotating.cpp | 109 +++++++++++++++++++++++++++------------ 3 files changed, 94 insertions(+), 33 deletions(-) diff --git a/Entities/Gib.cpp b/Entities/Gib.cpp index f3cc735e5..843200667 100644 --- a/Entities/Gib.cpp +++ b/Entities/Gib.cpp @@ -18,6 +18,7 @@ namespace RTE { m_LifeVariation = 0.1F; m_InheritsVel = true; m_IgnoresTeamHits = false; + m_SpreadMode = SPREAD_RANDOM; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -32,6 +33,7 @@ namespace RTE { m_LifeVariation = reference.m_LifeVariation; m_InheritsVel = reference.m_InheritsVel; m_IgnoresTeamHits = reference.m_IgnoresTeamHits; + m_SpreadMode = reference.m_SpreadMode; return 0; } @@ -58,6 +60,8 @@ namespace RTE { reader >> m_InheritsVel; } else if (propName == "IgnoresTeamHits") { reader >> m_IgnoresTeamHits; + } else if (propName == "SpreadMode") { + reader >> m_SpreadMode; } else { return Serializable::ReadProperty(propName, reader); } diff --git a/Entities/Gib.h b/Entities/Gib.h index 01abccf87..fed6179d5 100644 --- a/Entities/Gib.h +++ b/Entities/Gib.h @@ -15,6 +15,13 @@ namespace RTE { public: + enum SpreadMode + { + SPREAD_RANDOM = 0, + SPREAD_EVEN, + SPREAD_SPIRAL + }; + SerializableClassNameGetter SerializableOverrideMethods @@ -100,6 +107,12 @@ namespace RTE { /// /// Whether this Gib's GibParticles should ignore hits with the team of the gibbing parent. bool IgnoresTeamHits() const { return m_IgnoresTeamHits; } + + /// + /// Gets this Gib's spread mode, which determines how velocity angles are applied to the GibParticles. + /// + /// The spread mode of this Gib. + int GetSpreadMode() const { return m_SpreadMode; } #pragma endregion protected: @@ -113,6 +126,7 @@ namespace RTE { float m_LifeVariation; //!< The per-Gib variation in Lifetime, in percentage of the existing Lifetime of the gib. bool m_InheritsVel; //!< Whether this Gib should inherit the velocity of the exploding parent or not. bool m_IgnoresTeamHits; //!< Whether this Gib should ignore hits with the team of the exploding parent or not. + int m_SpreadMode; //!< Determines what kind of logic is used when applying velocity to the GibParticle objects. private: diff --git a/Entities/MOSRotating.cpp b/Entities/MOSRotating.cpp index 491cca02e..bda7e7f40 100644 --- a/Entities/MOSRotating.cpp +++ b/Entities/MOSRotating.cpp @@ -989,44 +989,87 @@ void MOSRotating::CreateGibsWhenGibbing(const Vector &impactImpulse, MovableObje } MovableObject *gibParticleClone = dynamic_cast(gibSettingsObject.GetParticlePreset()->Clone()); + int count = gibSettingsObject.GetCount(); + float lifeVariation = gibSettingsObject.GetLifeVariation(); + float spread = gibSettingsObject.GetSpread(); + float minVelocity = gibSettingsObject.GetMinVelocity(); + float maxVelocity = gibSettingsObject.GetMaxVelocity(); + float mass = (gibParticleClone->GetMass() != 0 ? gibParticleClone->GetMass() : 0.0001F); - float minVelocity = gibSettingsObject.GetMinVelocity(); - float velocityRange = gibSettingsObject.GetMaxVelocity() - gibSettingsObject.GetMinVelocity(); - if (gibSettingsObject.GetMinVelocity() == 0 && gibSettingsObject.GetMaxVelocity() == 0) { + int lifetime = gibParticleClone->GetLifetime(); + + if (minVelocity == 0 && maxVelocity == 0) { minVelocity = m_GibBlastStrength / mass; - velocityRange = 10.0F; + maxVelocity = minVelocity + 10.0F; } + float velocityRange = maxVelocity - minVelocity; Vector rotatedGibOffset = RotateOffset(gibSettingsObject.GetOffset()); - for (int i = 0; i < gibSettingsObject.GetCount(); i++) { - if (i > 0) { gibParticleClone = dynamic_cast(gibSettingsObject.GetParticlePreset()->Clone()); } - - if (gibParticleClone->GetLifetime() != 0) { - gibParticleClone->SetLifetime(static_cast(static_cast(gibParticleClone->GetLifetime()) * (1.0F + (gibSettingsObject.GetLifeVariation() * RandomNormalNum())))); - } - gibParticleClone->SetRotAngle(GetRotAngle() + gibParticleClone->GetRotAngle()); - gibParticleClone->SetAngularVel((gibParticleClone->GetAngularVel() * 0.35F) + (gibParticleClone->GetAngularVel() * 0.65F / mass) * RandomNum()); - if (rotatedGibOffset.GetRoundIntX() > m_aSprite[0]->w / 3) { - float offCenterRatio = rotatedGibOffset.m_X / (static_cast(m_aSprite[0]->w) / 2.0F); - float angularVel = fabs(gibParticleClone->GetAngularVel() * 0.5F) + std::fabs(gibParticleClone->GetAngularVel() * 0.5F * offCenterRatio); - gibParticleClone->SetAngularVel(angularVel * (rotatedGibOffset.m_X > 0 ? -1 : 1)); - } else { - gibParticleClone->SetAngularVel((gibParticleClone->GetAngularVel() * 0.5F + (gibParticleClone->GetAngularVel() * RandomNum())) * (RandomNormalNum() > 0.0F ? 1.0F : -1.0F)); - } - - gibParticleClone->SetPos(m_Pos + rotatedGibOffset); - gibParticleClone->SetHFlipped(m_HFlipped); - Vector gibVelocity = rotatedGibOffset.IsZero() ? Vector(minVelocity + RandomNum(0.0F, velocityRange), 0.0F) : rotatedGibOffset.SetMagnitude(minVelocity + RandomNum(0.0F, velocityRange)); - // TODO: Figure out how much the magnitude of an offset should affect spread - float gibSpread = (rotatedGibOffset.IsZero() && gibSettingsObject.GetSpread() == 0.1F) ? c_PI : gibSettingsObject.GetSpread(); - gibVelocity.RadRotate(impactImpulse.GetAbsRadAngle() + (gibSpread * RandomNormalNum())); - gibParticleClone->SetVel(gibVelocity + (gibSettingsObject.InheritsVelocity() ? m_Vel : Vector())); - if (movableObjectToIgnore) { gibParticleClone->SetWhichMOToNotHit(movableObjectToIgnore); } - gibParticleClone->SetTeam(m_Team); - gibParticleClone->SetIgnoresTeamHits(gibSettingsObject.IgnoresTeamHits()); - - g_MovableMan.AddParticle(gibParticleClone); - } + // The "Spiral" spread mode uses the fermat spiral as means to determine the velocity of the gib particles, resulting in a evenly spaced out circle (or ring) of particles. + if (gibSettingsObject.GetSpreadMode() == Gib::SPREAD_SPIRAL) { + float maxRadius = std::sqrt(static_cast(count)); + float scale = velocityRange / maxRadius; + float randAngle = c_PI * RandomNormalNum(); + + for (int i = 1; i <= count; i++) { + if (i > 1) { gibParticleClone = dynamic_cast(gibSettingsObject.GetParticlePreset()->Clone()); } + + float radius = std::sqrt(static_cast(count - i)); + gibParticleClone->SetPos(m_Pos + rotatedGibOffset); + gibParticleClone->SetHFlipped(m_HFlipped); + Vector gibVelocity(radius * scale + minVelocity, 0); + gibParticleClone->SetVel(gibVelocity.RadRotate(randAngle + RandomNum(0.0F, spread) + static_cast(i) * 2.39996F)); + if (lifetime != 0) { + gibParticleClone->SetLifetime(static_cast(std::max(static_cast(lifetime) * (1.0F - lifeVariation * ((radius / maxRadius) * 0.75F + RandomNormalNum() * 0.25F)), 1.0F))); + } + gibParticleClone->SetRotAngle(gibVelocity.GetAbsRadAngle() + (m_HFlipped ? c_PI : 0)); + gibParticleClone->SetAngularVel((gibParticleClone->GetAngularVel() * 0.35F) + (gibParticleClone->GetAngularVel() * 0.65F / mass) * RandomNum()); + gibParticleClone->SetVel(gibVelocity + (gibSettingsObject.InheritsVelocity() ? (m_PrevVel + m_Vel) / 2 : Vector())); + if (movableObjectToIgnore) { gibParticleClone->SetWhichMOToNotHit(movableObjectToIgnore); } + gibParticleClone->SetTeam(m_Team); + gibParticleClone->SetIgnoresTeamHits(gibSettingsObject.IgnoresTeamHits()); + + g_MovableMan.AddParticle(gibParticleClone); + } + } else { + for (int i = 0; i < count; i++) { + if (i > 0) { gibParticleClone = dynamic_cast(gibSettingsObject.GetParticlePreset()->Clone()); } + + if (gibParticleClone->GetLifetime() != 0) { + gibParticleClone->SetLifetime(static_cast(static_cast(gibParticleClone->GetLifetime()) * (1.0F + (lifeVariation * RandomNormalNum())))); + } + + gibParticleClone->SetRotAngle(GetRotAngle() + gibParticleClone->GetRotAngle()); + gibParticleClone->SetAngularVel((gibParticleClone->GetAngularVel() * 0.35F) + (gibParticleClone->GetAngularVel() * 0.65F / mass) * RandomNum()); + if (rotatedGibOffset.GetRoundIntX() > m_aSprite[0]->w / 3) { + float offCenterRatio = rotatedGibOffset.m_X / (static_cast(m_aSprite[0]->w) / 2.0F); + float angularVel = std::abs(gibParticleClone->GetAngularVel() * 0.5F) + std::abs(gibParticleClone->GetAngularVel() * 0.5F * offCenterRatio); + gibParticleClone->SetAngularVel(angularVel * (rotatedGibOffset.m_X > 0 ? -1 : 1)); + } else { + gibParticleClone->SetAngularVel((gibParticleClone->GetAngularVel() * 0.5F + (gibParticleClone->GetAngularVel() * RandomNum())) * (RandomNormalNum() > 0.0F ? 1.0F : -1.0F)); + } + + gibParticleClone->SetPos(m_Pos + rotatedGibOffset); + gibParticleClone->SetHFlipped(m_HFlipped); + Vector gibVelocity = rotatedGibOffset.IsZero() ? Vector(minVelocity + RandomNum(0.0F, velocityRange), 0.0F) : rotatedGibOffset.SetMagnitude(minVelocity + RandomNum(0.0F, velocityRange)); + // TODO: Figure out how much the magnitude of an offset should affect spread + float gibSpread = (rotatedGibOffset.IsZero() && spread == 0.1F) ? c_PI : spread; + + gibVelocity.RadRotate(gibSettingsObject.InheritsVelocity() ? impactImpulse.GetAbsRadAngle() : m_Rotation.GetRadAngle() + (m_HFlipped ? c_PI : 0)); + // The "Even" spread will spread all gib particles evenly in an arc, while maintaining a randomized velocity magnitude. + if (gibSettingsObject.GetSpreadMode() == Gib::SPREAD_EVEN) { + gibVelocity.RadRotate(gibSpread * 0.5F - gibSpread * i / count); + } else { + gibVelocity.RadRotate(gibSpread * RandomNormalNum()); + } + gibParticleClone->SetVel(gibVelocity + (gibSettingsObject.InheritsVelocity() ? (m_PrevVel + m_Vel) / 2 : Vector())); + if (movableObjectToIgnore) { gibParticleClone->SetWhichMOToNotHit(movableObjectToIgnore); } + gibParticleClone->SetTeam(m_Team); + gibParticleClone->SetIgnoresTeamHits(gibSettingsObject.IgnoresTeamHits()); + + g_MovableMan.AddParticle(gibParticleClone); + } + } } } From 30a66fc88e9c83149169e479ae30ee99473593dd Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 17 Aug 2021 15:51:49 +0300 Subject: [PATCH 15/72] New `MOSRotating` property `WoundCountAffectsImpulseLimitRatio` which can be used to weaken an object's impulse resistance based on the amount of wound "damage" --- Entities/MOSRotating.cpp | 28 ++++++++++++++++++++-------- Entities/MOSRotating.h | 7 +++++++ Lua/LuaBindingsEntities.cpp | 1 + 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/Entities/MOSRotating.cpp b/Entities/MOSRotating.cpp index bda7e7f40..6d171f047 100644 --- a/Entities/MOSRotating.cpp +++ b/Entities/MOSRotating.cpp @@ -73,6 +73,7 @@ void MOSRotating::Clear() m_GibImpulseLimit = 0; m_GibWoundLimit = 0; m_GibBlastStrength = 10.0F; + m_WoundCountAffectsImpulseLimitRatio = 0.25F; m_GibSound = nullptr; m_EffectOnGib = true; m_pFlipBitmap = 0; @@ -254,6 +255,7 @@ int MOSRotating::Create(const MOSRotating &reference) { m_GibImpulseLimit = reference.m_GibImpulseLimit; m_GibWoundLimit = reference.m_GibWoundLimit; m_GibBlastStrength = reference.m_GibBlastStrength; + m_WoundCountAffectsImpulseLimitRatio = reference.m_WoundCountAffectsImpulseLimitRatio; if (reference.m_GibSound) { m_GibSound = dynamic_cast(reference.m_GibSound->Clone()); } m_EffectOnGib = reference.m_EffectOnGib; m_LoudnessOnGib = reference.m_LoudnessOnGib; @@ -324,8 +326,10 @@ int MOSRotating::ReadProperty(const std::string_view &propName, Reader &reader) reader >> m_GibImpulseLimit; else if (propName == "GibWoundLimit" || propName == "WoundLimit") reader >> m_GibWoundLimit; - else if (propName == "GibBlastStrength") { - reader >> m_GibBlastStrength; + else if (propName == "GibBlastStrength") { + reader >> m_GibBlastStrength; + } else if (propName == "WoundCountAffectsImpulseLimitRatio") { + reader >> m_WoundCountAffectsImpulseLimitRatio; } else if (propName == "GibSound") { if (!m_GibSound) { m_GibSound = new SoundContainer; } reader >> m_GibSound; @@ -1166,9 +1170,13 @@ void MOSRotating::ApplyImpulses() totalImpulse += (*iItr).first; } // If impulse gibbing threshold is enabled for this, see if it's below the total impulse force - if (m_GibImpulseLimit > 0 && totalImpulse.GetMagnitude() > m_GibImpulseLimit) - GibThis(totalImpulse); - + if (m_GibImpulseLimit > 0) { + float impulseLimit = m_GibImpulseLimit; + if (m_WoundCountAffectsImpulseLimitRatio != 0 && m_GibWoundLimit > 0) { + impulseLimit *= 1.0F - (static_cast(m_Wounds.size()) / static_cast(m_GibWoundLimit)) * m_WoundCountAffectsImpulseLimitRatio; + } + if (totalImpulse.GetMagnitude() > impulseLimit) { GibThis(totalImpulse); } + } MOSprite::ApplyImpulses(); } @@ -1438,9 +1446,13 @@ void MOSRotating::PostTravel() MOSprite::PostTravel(); // Check if travel hits created enough impulse forces to gib this - if (m_GibImpulseLimit > 0 && m_TravelImpulse.GetMagnitude() > m_GibImpulseLimit) - GibThis(); - + if (m_GibImpulseLimit > 0) { + float impulseLimit = m_GibImpulseLimit; + if (m_WoundCountAffectsImpulseLimitRatio != 0 && m_GibWoundLimit > 0) { + impulseLimit *= 1.0F - (static_cast(m_Wounds.size()) / static_cast(m_GibWoundLimit)) * m_WoundCountAffectsImpulseLimitRatio; + } + if (m_TravelImpulse.GetMagnitude() > impulseLimit) { GibThis(); } + } // Reset m_DeepHardness = 0; diff --git a/Entities/MOSRotating.h b/Entities/MOSRotating.h index 731ec6edb..7c6eb88ab 100644 --- a/Entities/MOSRotating.h +++ b/Entities/MOSRotating.h @@ -645,6 +645,12 @@ ClassInfoGetters /// The new gib wound limit to use. void SetGibWoundLimit(int newGibWoundLimit) { m_GibWoundLimit = newGibWoundLimit; } + /// + /// Gets the rate at which wound count should diminish the gib impulse limit of this MOSRotating. + /// + /// The rate at which wound count affects the impulse limit. + float GetWoundCountAffectsImpulseLimitRatio() const { return m_WoundCountAffectsImpulseLimitRatio; } + /// /// Gets the gib blast strength this MOSRotating, i.e. the strength with which Gibs and Attachables will be launched when this MOSRotating is gibbed. /// @@ -962,6 +968,7 @@ ClassInfoGetters // The number of wound emitters allowed before this gets gibbed. 0 means this can't get gibbed int m_GibWoundLimit; float m_GibBlastStrength; //!< The strength with which Gibs and Attachables will get launched when this MOSRotating is gibbed. + float m_WoundCountAffectsImpulseLimitRatio; //!< At what rate should wound count diminish the gib impulse limit of this MOSRotating. // Gib sound effect SoundContainer *m_GibSound; // Whether to flash effect on gib diff --git a/Lua/LuaBindingsEntities.cpp b/Lua/LuaBindingsEntities.cpp index 2d1199586..c73691f32 100644 --- a/Lua/LuaBindingsEntities.cpp +++ b/Lua/LuaBindingsEntities.cpp @@ -806,6 +806,7 @@ namespace RTE { .property("GibWoundLimit", (int (MOSRotating:: *)() const) &MOSRotating::GetGibWoundLimit, &MOSRotating::SetGibWoundLimit) .property("GibSound", &MOSRotating::GetGibSound, &MOSRotatingSetGibSound) .property("GibImpulseLimit", &MOSRotating::GetGibImpulseLimit, &MOSRotating::SetGibImpulseLimit) + .property("WoundCountAffectsImpulseLimitRatio", &MOSRotating::GetWoundCountAffectsImpulseLimitRatio) .property("DamageMultiplier", &MOSRotating::GetDamageMultiplier, &MOSRotating::SetDamageMultiplier) .property("WoundCount", (int (MOSRotating:: *)() const) &MOSRotating::GetWoundCount) .property("OrientToVel", &MOSRotating::GetOrientToVel, &MOSRotating::SetOrientToVel) From 0edf57673693958b4b089992a43147ab1fdee946 Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 17 Aug 2021 15:52:28 +0300 Subject: [PATCH 16/72] Exposed `ThrownDevice` specific offsets to Lua --- Entities/ThrownDevice.h | 12 ++++++++++++ Lua/LuaBindingsEntities.cpp | 4 +++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Entities/ThrownDevice.h b/Entities/ThrownDevice.h index b2d4cf95b..6b1d9ef1c 100644 --- a/Entities/ThrownDevice.h +++ b/Entities/ThrownDevice.h @@ -61,12 +61,24 @@ namespace RTE { /// A const reference to the current start throw parent offset. Vector GetStartThrowOffset() const { return m_StartThrowOffset; } + /// + /// Sets the Start throw offset for this ThrownDevice. + /// + /// The new start throw offset. + void SetStartThrowOffset(Vector startOffset) { m_StartThrowOffset = startOffset; } + /// /// Gets the End throw offset of this ThrownDevice's joint relative from the parent Actor's position, if attached. /// /// A const reference to the current end throw parent offset. Vector GetEndThrowOffset() const { return m_EndThrowOffset; } + /// + /// Sets the End throw offset for this ThrownDevice. + /// + /// The new end throw offset. + void SetEndThrowOffset(Vector endOffset) { m_EndThrowOffset = endOffset; } + /// /// Gets the minimum throw velocity of this when thrown. /// diff --git a/Lua/LuaBindingsEntities.cpp b/Lua/LuaBindingsEntities.cpp index c73691f32..d13f04d93 100644 --- a/Lua/LuaBindingsEntities.cpp +++ b/Lua/LuaBindingsEntities.cpp @@ -1172,7 +1172,9 @@ namespace RTE { return ConcreteTypeLuaClassDefinition(ThrownDevice, HeldDevice) .property("MinThrowVel", &ThrownDevice::GetMinThrowVel, &ThrownDevice::SetMinThrowVel) - .property("MaxThrowVel", &ThrownDevice::GetMaxThrowVel, &ThrownDevice::SetMaxThrowVel); + .property("MaxThrowVel", &ThrownDevice::GetMaxThrowVel, &ThrownDevice::SetMaxThrowVel) + .property("StartThrowOffset", &ThrownDevice::GetStartThrowOffset, &ThrownDevice::SetStartThrowOffset) + .property("EndThrowOffset", &ThrownDevice::GetEndThrowOffset, &ThrownDevice::SetEndThrowOffset); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// From 4ba4c9df32979bc63ce7fb821fa9dc40cf843f47 Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 17 Aug 2021 15:54:00 +0300 Subject: [PATCH 17/72] New `Round` INI property `ShellVelocityVariation` which can be used to add randomized magnitude to ejected shells --- Entities/HDFirearm.cpp | 2 +- Entities/Round.cpp | 6 ++++++ Entities/Round.h | 13 ++++++++++--- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Entities/HDFirearm.cpp b/Entities/HDFirearm.cpp index 2ed7d312d..d0ba0d5c3 100644 --- a/Entities/HDFirearm.cpp +++ b/Entities/HDFirearm.cpp @@ -885,7 +885,7 @@ void HDFirearm::Update() pShell->SetPos(m_Pos + tempEject); // ##@#@@$ TEMP - shellVel.SetXY(pRound->GetShellVel(), 0); + shellVel.SetXY(pRound->GetShellVel() * (1.0F - pRound->GetShellVelVariation() * RandomNum()), 0); shellVel.DegRotate(degAimAngle + m_ShellEjectAngle * (m_HFlipped ? -1 : 1) + shellSpread); pShell->SetVel(m_Vel + shellVel); pShell->SetRotAngle(m_Rotation.GetRadAngle()); diff --git a/Entities/Round.cpp b/Entities/Round.cpp index dd2d3de2f..686b0c0c6 100644 --- a/Entities/Round.cpp +++ b/Entities/Round.cpp @@ -15,6 +15,7 @@ namespace RTE { m_Separation = 0; m_Shell = 0; m_ShellVel = 0; + m_ShellVelVariation = 0.1F; m_FireSound.Reset(); m_AILifeTime = 0; m_AIFireVel = -1; @@ -53,6 +54,7 @@ namespace RTE { m_Separation = reference.m_Separation; m_Shell = reference.m_Shell; m_ShellVel = reference.m_ShellVel; + m_ShellVelVariation = reference.m_ShellVelVariation; m_FireSound = reference.m_FireSound; m_AILifeTime = reference.m_AILifeTime; m_AIFireVel = reference.m_AIFireVel; @@ -77,6 +79,8 @@ namespace RTE { m_Shell = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); } else if (propName == "ShellVelocity") { reader >> m_ShellVel; + } else if (propName == "ShellVelocityVariation") { + reader >> m_ShellVelVariation; } else if (propName == "FireSound") { reader >> m_FireSound; } else if (propName == "AILifeTime") { @@ -108,6 +112,8 @@ namespace RTE { writer << m_Shell; writer.NewProperty("ShellVelocity"); writer << m_ShellVel; + writer.NewProperty("ShellVelocityVariation"); + writer << m_ShellVelVariation; writer.NewProperty("FireSound"); writer << m_FireSound; writer.NewProperty("AILifeTime"); diff --git a/Entities/Round.h b/Entities/Round.h index a233b55dc..08eb69090 100644 --- a/Entities/Round.h +++ b/Entities/Round.h @@ -100,11 +100,17 @@ namespace RTE { const MovableObject * GetShell() const { return m_Shell; } /// - /// Gets the velocity at which this round's shell is to be ejected. + /// Gets the maximum velocity at which this round's shell is to be ejected. /// - /// A float with the shell velocity in m/s. + /// A float with the maximum shell velocity in m/s. float GetShellVel() const { return m_ShellVel; } + /// + /// Gets the random velocity variation scalar at which this round's shell is to be ejected. + /// + /// A float with the scalar value. + float GetShellVelVariation() const { return m_ShellVelVariation; } + /// /// Shows whether this Round has an extra sound sample to play when fired. /// @@ -148,7 +154,8 @@ namespace RTE { float m_Separation; //!< The range of separation between particles in this Round, in pixels. const MovableObject *m_Shell; //!< Shell particle MovableObject preset instance. - float m_ShellVel; //!< The velocity with which this Round's shell/casing is launched. + float m_ShellVel; //!< The maximum velocity with which this Round's shell/casing is launched. + float m_ShellVelVariation; //!< The velocity variation scalar for this Round's shell/casing. SoundContainer m_FireSound; //!< The extra firing audio of this Round being fired. From 4d13e99c76960649761b201b15e80be01e1a72b7 Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 17 Aug 2021 15:56:30 +0300 Subject: [PATCH 18/72] `HDFirearm` and `HeldDevice` related refactor; Tweaked the `HDFirearm` flash glow behavior similarly to `AEmitter` --- Entities/HDFirearm.cpp | 42 +++++++++++++++++------------------------ Entities/HeldDevice.cpp | 1 + Entities/HeldDevice.h | 2 +- Entities/Magazine.h | 2 +- 4 files changed, 20 insertions(+), 27 deletions(-) diff --git a/Entities/HDFirearm.cpp b/Entities/HDFirearm.cpp index d0ba0d5c3..a0d6ef0b2 100644 --- a/Entities/HDFirearm.cpp +++ b/Entities/HDFirearm.cpp @@ -173,7 +173,7 @@ int HDFirearm::ReadProperty(const std::string_view &propName, Reader &reader) { } else if (propName == "DeactivationSound") { m_DeactivationSound = new SoundContainer; reader >> m_DeactivationSound; - m_DeactivationSound->SetSoundOverlapMode(SoundContainer::SoundOverlapMode::IGNORE_PLAY); + m_DeactivationSound->SetSoundOverlapMode(SoundContainer::SoundOverlapMode::RESTART); } else if (propName == "EmptySound") { m_EmptySound = new SoundContainer; reader >> m_EmptySound; @@ -672,12 +672,11 @@ void HDFirearm::Reload() // Stop any activation m_Activated = false; - if (m_FireSound && m_FireSound->GetLoopSetting() == -1 && m_FireSound->IsBeingPlayed()) - m_FireSound->Stop(); - + if (m_FireSound && m_FireSound->GetLoopSetting() == -1 && m_FireSound->IsBeingPlayed()) { m_FireSound->Stop(); } if (m_ReloadStartSound) { m_ReloadStartSound->Play(m_Pos); } - m_ReloadTmr.Reset(); - m_Reloading = true; + + m_ReloadTmr.Reset(); + m_Reloading = true; } } @@ -914,18 +913,14 @@ void HDFirearm::Update() } pRound = 0; } - } - - // No or empty magazine, so just click. - else if (((m_pMagazine && m_pMagazine->IsEmpty()) || !m_pMagazine) && m_Activated && !m_AlreadyClicked ) - { + } else if (m_Activated && !m_AlreadyClicked) { // Play empty pin click sound. if (m_EmptySound) { m_EmptySound->Play(m_Pos); } // Indicate that we have clicked once during the current activation. m_AlreadyClicked = true; // Auto-reload - Reload(); + if (m_Parent) { Reload(); } } // No magazine, have started to reload, so put new mag in when done @@ -977,11 +972,7 @@ void HDFirearm::Update() AddImpulseForce(m_RecoilForce, m_RecoilOffset); // Display gun animation - if (!m_IsAnimatedManually) - { - if (m_FrameCount > 1) - m_Frame = 1; - } + if (!m_IsAnimatedManually && m_FrameCount > 1) { m_Frame = 1; } // Display gun flame frame. if (m_pFlash) { @@ -1033,8 +1024,7 @@ void HDFirearm::Update() StopActivationSound(); } } else { - if (!m_IsAnimatedManually) - m_Frame = 0; + if (!m_IsAnimatedManually) { m_Frame = 0; } StopActivationSound(); } } @@ -1083,13 +1073,15 @@ void HDFirearm::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mo m_pFlash->Draw(pTargetBitmap, targetPos, mode, onlyPhysical); } - // Fudge the muzzle pos forward a little bit so the glow aligns nicely - Vector muzzlePos = m_MuzzleOff; - muzzlePos.m_X += 4; - muzzlePos = m_Pos + RotateOffset(muzzlePos); // Set the screen flash effect to draw at the final post processing stage - if (m_FireFrame && m_pFlash && m_pFlash->GetScreenEffect() && mode == g_DrawColor && !onlyPhysical && !g_SceneMan.ObscuredPoint(muzzlePos)) { - g_PostProcessMan.RegisterPostEffect(muzzlePos, m_pFlash->GetScreenEffect(), m_pFlash->GetScreenEffectHash(), 55.0F + RandomNum(0.0F, 200.0F), m_pFlash->GetEffectRotAngle()); + if (m_FireFrame && m_pFlash && m_pFlash->GetScreenEffect() && mode == g_DrawColor && !onlyPhysical) { + // Fudge the muzzle pos forward a little bit so the glow aligns nicely + Vector muzzlePos = m_MuzzleOff; + muzzlePos.m_X += m_pFlash->GetSpriteWidth() * 0.3F; + muzzlePos = m_Pos + RotateOffset(muzzlePos); + if (!g_SceneMan.ObscuredPoint(muzzlePos)) { + g_PostProcessMan.RegisterPostEffect(muzzlePos, m_pFlash->GetScreenEffect(), m_pFlash->GetScreenEffectHash(), RandomNum(m_pFlash->GetEffectStopStrength(), m_pFlash->GetEffectStartStrength()), m_pFlash->GetEffectRotAngle()); + } } } diff --git a/Entities/HeldDevice.cpp b/Entities/HeldDevice.cpp index 6bc337e30..4eafd3d2b 100644 --- a/Entities/HeldDevice.cpp +++ b/Entities/HeldDevice.cpp @@ -467,6 +467,7 @@ void HeldDevice::Update() if (m_FrameCount > 1) { + // TODO: Rename SpriteAnimMode LOOPWHENMOVING to something like LOOPWHENACTIVE which is more descriptive and fits both moving Actors and active HeldDevices. if (m_SpriteAnimMode == LOOPWHENMOVING && m_Activated) { float cycleTime = ((long)m_SpriteAnimTimer.GetElapsedSimTimeMS()) % m_SpriteAnimDuration; diff --git a/Entities/HeldDevice.h b/Entities/HeldDevice.h index 043b8fecf..356136cbf 100644 --- a/Entities/HeldDevice.h +++ b/Entities/HeldDevice.h @@ -529,7 +529,7 @@ ClassInfoGetters // Arguments: None. // Return value: Whetehr magazine is full or not. - virtual bool IsFull() const { return false; } + virtual bool IsFull() const { return true; } ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Entities/Magazine.h b/Entities/Magazine.h index daf3e17c7..25a887188 100644 --- a/Entities/Magazine.h +++ b/Entities/Magazine.h @@ -157,7 +157,7 @@ ClassInfoGetters // Arguments: None. // Return value: Whether this Magazine is out of rounds or not. - bool IsEmpty() const { return m_FullCapacity > 0 ? m_RoundCount == 0 : !(m_FullCapacity < 0); } + bool IsEmpty() const { return m_FullCapacity == 0 || m_RoundCount == 0; } ////////////////////////////////////////////////////////////////////////////////////////// From 1f0df503a47910412e0075342be944f576faa4b7 Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 17 Aug 2021 15:58:24 +0300 Subject: [PATCH 19/72] Allow "Always allowed items" or items of a limited kind to show up in the Buy Menu, even if unbuyable; Additionally, allow class `HeldDevice` items to show up under both "Tools" and "Weapons" and not just "Shields" --- Menus/BuyMenuGUI.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Menus/BuyMenuGUI.cpp b/Menus/BuyMenuGUI.cpp index e7b54c58b..245b4ba31 100644 --- a/Menus/BuyMenuGUI.cpp +++ b/Menus/BuyMenuGUI.cpp @@ -1854,11 +1854,11 @@ void BuyMenuGUI::CategoryChange(bool focusOnCategoryTabs) } else if (m_MenuCategory == TOOLS) { - AddObjectsToItemList(catalogList, "HDFirearm", "Tools"); + AddObjectsToItemList(catalogList, "HeldDevice", "Tools"); } else if (m_MenuCategory == GUNS) { - AddObjectsToItemList(catalogList, "HDFirearm", "Weapons"); + AddObjectsToItemList(catalogList, "HeldDevice", "Weapons"); } else if (m_MenuCategory == BOMBS) { @@ -1884,9 +1884,10 @@ void BuyMenuGUI::CategoryChange(bool focusOnCategoryTabs) for (list::iterator oItr = catalogList[moduleID].begin(); oItr != catalogList[moduleID].end(); ++oItr) { pSObject = dynamic_cast(*oItr); - // Buyable and not brain? - if (pSObject && pSObject->IsBuyable() && !pSObject->IsInGroup("Brains")) - tempList.push_back(pSObject); + // Only add buyable and non-brain items, unless they are explicitly set to be available. + if ((pSObject && pSObject->IsBuyable() && !pSObject->IsInGroup("Brains")) || GetOwnedItemsAmount((pSObject)->GetModuleAndPresetName()) > 0 || m_AlwaysAllowedItems.find((pSObject)->GetModuleAndPresetName()) != m_AlwaysAllowedItems.end()) { + tempList.push_back(pSObject); + } } // Don't add anyhting to the real buy item list if the current module didn't yield any valid items From 1d8fef05bdc6b535ef3d76739c057a85ef80079f Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 17 Aug 2021 16:01:30 +0300 Subject: [PATCH 20/72] Added `Entity` property `ModuleName` to eliminate the need to iterate through `GetModuleAndPresetName()` when trying to get the .rte module name --- Lua/LuaBindingsEntities.cpp | 1 + System/Entity.cpp | 11 +++++++++++ System/Entity.h | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/Lua/LuaBindingsEntities.cpp b/Lua/LuaBindingsEntities.cpp index d13f04d93..ebdc416bd 100644 --- a/Lua/LuaBindingsEntities.cpp +++ b/Lua/LuaBindingsEntities.cpp @@ -15,6 +15,7 @@ namespace RTE { .property("Description", &Entity::GetDescription, &Entity::SetDescription) .property("IsOriginalPreset", &Entity::IsOriginalPreset) .property("ModuleID", &Entity::GetModuleID) + .property("ModuleName", &Entity::GetModuleName) .property("RandomWeight", &Entity::GetRandomWeight) .def("Clone", &CloneEntity) diff --git a/System/Entity.cpp b/System/Entity.cpp index 0575ccf19..a4803866b 100644 --- a/System/Entity.cpp +++ b/System/Entity.cpp @@ -158,6 +158,17 @@ namespace RTE { return dataModule->GetFileName() + "/" + GetPresetName(); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + std::string Entity::GetModuleName() const { + if (m_DefinedInModule > 0) { + if (const DataModule *dataModule = g_PresetMan.GetDataModule(m_DefinedInModule)) { + return dataModule->GetFileName(); + } + } + return "Base.rte"; + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Entity::MigrateToModule(int whichModule) { diff --git a/System/Entity.h b/System/Entity.h index 21cda0618..1750dffe7 100644 --- a/System/Entity.h +++ b/System/Entity.h @@ -337,6 +337,12 @@ namespace RTE { /// A string with the module and instance name of this Entity. std::string GetModuleAndPresetName() const; + /// + /// Gets the name of this Entity's Data Module it was defined in, or the Base module if somehow none was found. + /// + /// A string with the module of this Entity. + std::string GetModuleName() const; + /// /// Indicates whether this Entity was explicitly given a new instance name upon creation, or if it was just copied/inherited implicitly. /// From ae9887d9456011cb6a5f98cbe717989e2bfebbe9 Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 17 Aug 2021 16:01:52 +0300 Subject: [PATCH 21/72] Corrected misleading summary --- System/Matrix.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/System/Matrix.h b/System/Matrix.h index 947b50271..c087bd434 100644 --- a/System/Matrix.h +++ b/System/Matrix.h @@ -118,7 +118,7 @@ namespace RTE { /// /// Returns the angle difference between what this is currently representing, to another angle in radians. - /// It will wrap and normalize and give the shortest absolute distance between this and the passed in. + /// It will wrap and normalize and give the smallest angle difference between this and the passed in. /// /// A float with the angle to get the difference to from this, in radians. /// A float with the difference angle between this and the passed-in angle. @@ -126,7 +126,7 @@ namespace RTE { /// /// Returns the angle difference between what this is currently representing, to another angle in degrees. - /// It will wrap and normalize and give the shortest absolute distance between this and the passed in. + /// It will wrap and normalize and give the smallest angle difference between this and the passed in. /// /// A float with the angle to get the difference to from this, in degrees. /// A float with the difference angle between this and the passed-in angle. From 0bf11965acb983ac510dc052b49ecdf8b926f32f Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 17 Aug 2021 16:02:47 +0300 Subject: [PATCH 22/72] Added data reference to a new "inventory" pie icon --- System/PieSlice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/System/PieSlice.cpp b/System/PieSlice.cpp index 3dcdab066..3bf106605 100644 --- a/System/PieSlice.cpp +++ b/System/PieSlice.cpp @@ -55,7 +55,7 @@ namespace RTE { m_Icon = *dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Trade Star")); break; case PieSliceIndex::PSI_FULLINVENTORY: - m_Icon = *dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Unknown")); + m_Icon = *dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Inventory")); break; case PieSliceIndex::PSI_STATS: m_Icon = *dynamic_cast(g_PresetMan.GetEntityPreset("Icon", "Stats")); From 05efd05036f856d375736abdfbc8ed5e852e29e5 Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 17 Aug 2021 17:51:18 +0300 Subject: [PATCH 23/72] Update CHANGELOG.md --- CHANGELOG.md | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4989bbe38..fb52f67a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -181,8 +181,45 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - New `DataModule` property `IsFaction = 0/1` which determines if a module is a playable faction (in MetaGame, etc.). This replaces the need to put "Tech" in the module name. Defaults to false (0). +- New `MOSRotating` INI and Lua (R) property `WoundCountAffectsImpulseLimitRatio` which can be used to make objects more prone to gibbing from impulse when they have also received wounds. + +- New `Gib` INI property `SpreadMode` which sports two new spread logic variants which alter the way velocity is applied to the `GibParticle`s when they spawn. This can be used to create richer explosion effects. + `SpreadMode = 0` is the default, fully randomized spread according to `MinVelocity`, `MaxVelocity` and `Spread` values. Think: a piece of grenade fragment, launching out in an arbitrary direction. + `SpreadMode = 1` is the same as the default, but with evenly spaced out angles. Think: an air blast shockwave, dispersing evenly outward from the explosion. + `SpreadMode = 2` has an entirely different behavior of its own, which utilizes the fermat spiral as means to evenly disperse the particles in a circular area, according to `MaxVelocity` and `MinVelocity`. Since this mode will always result in a full, 360-degree spread, the `Spread` property can be used to add randomization to the gib particles. Think: a cloud of smoke. + +- New `Actor` INI and Lua (R/W) property `StableRecoverDelay` which determines how long it takes for an actor to regain `STABLE` status after being rendered `UNSTABLE`. + +- New `AHuman` Lua (R) property `ThrowProgress` which returns the current throw chargeup progress as a scalar from 0 to 1. + +- New `Round` INI property `ShellVelocityVariation` which can be used to randomize the magnitude at which shells are ejected. + +- New `HDFirearm` Lua (R) property `ReloadProgress` which returns the current reload progress as a scalar from 0 to 1. + +- New `HDFirearm` INI and Lua (R/W) property `Reloadable` which can be used to disable the ability to reload said device. + +- New `HDFirearm` Lua (R) property `RoundInMagCapacity` which returns the maximum capacity of the `Magazine`, or its entity reference (from which new magazines are cloned from), which means that the property will always return the maximum ammo capacity of the device, even when reloading. + +- New `Entity` Lua (R) property `ModuleName` which returns the filename of the data module from which the entity originates from. + +- New gameplay setting `MAX_INSERT_NAME_HERE` that hides the HUD of stranded items at a set distance. + +- `Arm`s will now react to the recoil of held devices. (This effect can be disabled via `RecoilTransmission` the same way as recoil itself) + +- `HDFirearm` reload progress now shows up as a HUD element. + ### Changed +- Exposed `ThrownDevice` specific offsets to Lua (R/W). + +- Class `HeldDevice` objects can now show up under "Tools" and "Weapons" rather than just "Shields". + +- Keyboard-only controlled `AHuman` and `ACrab` actors can now strafe while sharp aiming. + +- Lowered the default `AHuman` Head damage multiplier from 5 to 4. + +- "Fixed" grenades and other fast-moving objects bouncing violently off of doors and other stationary objects. + - Doors in `Team = -1` will now open up for all actors. - `MovableMan` function `KillAllActors` (commonly found in activities) has been appropriately renamed `KillAllEnemyActors`. @@ -199,7 +236,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Craft will now automatically scuttle when opening doors at a 90° angle rather than 45°. -- `AHuman` can now aim slightly while walking, however not while reloading. +- `AHuman` can now sharp-aim slightly while walking, however not while reloading. - Recoil when firing weapons now affects sharp aim. From 2f78fefd32b99e0aa32d54597b0fe19abf501b68 Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 17 Aug 2021 19:38:22 +0300 Subject: [PATCH 24/72] Fix `SPREAD_EVEN` logic --- Entities/MOSRotating.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Entities/MOSRotating.cpp b/Entities/MOSRotating.cpp index 6d171f047..63d744d0f 100644 --- a/Entities/MOSRotating.cpp +++ b/Entities/MOSRotating.cpp @@ -1062,7 +1062,7 @@ void MOSRotating::CreateGibsWhenGibbing(const Vector &impactImpulse, MovableObje gibVelocity.RadRotate(gibSettingsObject.InheritsVelocity() ? impactImpulse.GetAbsRadAngle() : m_Rotation.GetRadAngle() + (m_HFlipped ? c_PI : 0)); // The "Even" spread will spread all gib particles evenly in an arc, while maintaining a randomized velocity magnitude. if (gibSettingsObject.GetSpreadMode() == Gib::SPREAD_EVEN) { - gibVelocity.RadRotate(gibSpread * 0.5F - gibSpread * i / count); + gibVelocity.RadRotate(gibSpread - gibSpread * 2.0F * static_cast(i) / static_cast(count)); } else { gibVelocity.RadRotate(gibSpread * RandomNormalNum()); } From 301c24185269123287728932edab91acfd61a8ff Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 17 Aug 2021 21:14:45 +0300 Subject: [PATCH 25/72] Fix `ACrab` only being able to reload at half capacity --- Entities/ACrab.cpp | 17 +++++++++++++++-- Entities/ACrab.h | 6 ++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Entities/ACrab.cpp b/Entities/ACrab.cpp index 507ed1294..29580e53b 100644 --- a/Entities/ACrab.cpp +++ b/Entities/ACrab.cpp @@ -659,7 +659,7 @@ bool ACrab::OnSink(const Vector &pos) bool ACrab::AddPieMenuSlices(PieMenuGUI *pPieMenu) { - PieSlice reloadSlice("Reload", PieSlice::PieSliceIndex::PSI_RELOAD, PieSlice::SliceDirection::UP); + PieSlice reloadSlice("Reload", PieSlice::PieSliceIndex::PSI_RELOAD, PieSlice::SliceDirection::UP, !FirearmIsFull() && m_Status != INACTIVE); pPieMenu->AddSlice(reloadSlice); PieSlice sentryAISlice("Sentry AI Mode", PieSlice::PieSliceIndex::PSI_SENTRY, PieSlice::SliceDirection::DOWN); @@ -781,6 +781,19 @@ bool ACrab::FirearmIsEmpty() const { ////////////////////////////////////////////////////////////////////////////////////////// // Description: Indicates whether the currently held HDFirearm's is almost out of ammo. +bool ACrab::FirearmIsFull() const { + if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { + for (const HeldDevice *mountedDevice : m_pTurret->GetMountedDevices()) { + if (const HDFirearm *mountedFirearm = dynamic_cast(mountedDevice); mountedFirearm && !mountedFirearm->IsFull()) { + return false; + } + } + } + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////// + bool ACrab::FirearmNeedsReload() const { if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { for (const HeldDevice *mountedDevice : m_pTurret->GetMountedDevices()) { @@ -2229,7 +2242,7 @@ void ACrab::Update() //////////////////////////////////// // Reload held MO, if applicable - if (m_Controller.IsState(WEAPON_RELOAD) && FirearmNeedsReload() && m_Status != INACTIVE) { + if (m_Controller.IsState(WEAPON_RELOAD) && !FirearmIsFull() && m_Status != INACTIVE) { ReloadFirearms(); if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } diff --git a/Entities/ACrab.h b/Entities/ACrab.h index 0026300a6..fc931eb54 100644 --- a/Entities/ACrab.h +++ b/Entities/ACrab.h @@ -348,6 +348,12 @@ class ACrab : bool FirearmNeedsReload() const; + /// + /// Indicates whether all of the MountedDevices are at full capacity. + /// + /// Whether all of the MountedDevices are at full capacity. + bool FirearmIsFull() const; + ////////////////////////////////////////////////////////////////////////////////////////// // Method: FirearmIsSemiAuto From f81d96fcbec2381bbbbb6601b4010dcac86471f9 Mon Sep 17 00:00:00 2001 From: fourZK Date: Fri, 20 Aug 2021 13:31:03 +0300 Subject: [PATCH 26/72] Allign pickup indicator to the center --- Entities/AHuman.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index 7879be02b..ace270710 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -4471,7 +4471,7 @@ void AHuman::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichSc // Pickup GUI if (!m_Controller.IsState(PIE_MENU_ACTIVE) && m_pItemInReach) { std::snprintf(str, sizeof(str), " %c %s", -49, m_pItemInReach->GetPresetName().c_str()); - pSmallFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() - 12, drawPos.GetFloorIntY() + m_HUDStack + 3, str, GUIFont::Left); + pSmallFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 3, str, GUIFont::Centre); } /* // AI Mode select GUI HUD From 84391f9b515c3c2944a96852f28f7900b06bd701 Mon Sep 17 00:00:00 2001 From: fourZK Date: Fri, 20 Aug 2021 13:31:18 +0300 Subject: [PATCH 27/72] Fixed minor HUD-related bug --- Entities/AHuman.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index ace270710..3494676bb 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -3597,6 +3597,7 @@ void AHuman::Update() if (pMO) { m_Inventory.push_back(pMO); } m_pFGArm->SetHeldMO(m_pItemInReach); m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); + m_pItemInReach = nullptr; EquipShieldInBGArm(); m_SharpAimProgress = 0; From 65eeec8485c54ac2776d364053f179b893fa0321 Mon Sep 17 00:00:00 2001 From: fourZK Date: Fri, 20 Aug 2021 13:32:27 +0300 Subject: [PATCH 28/72] Revert `HeldDevice` appearing under "Guns" because `HeldDevice` also encompasses `ThrownDevice` which is not smart --- Menus/BuyMenuGUI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Menus/BuyMenuGUI.cpp b/Menus/BuyMenuGUI.cpp index 245b4ba31..a747287ff 100644 --- a/Menus/BuyMenuGUI.cpp +++ b/Menus/BuyMenuGUI.cpp @@ -1858,7 +1858,7 @@ void BuyMenuGUI::CategoryChange(bool focusOnCategoryTabs) } else if (m_MenuCategory == GUNS) { - AddObjectsToItemList(catalogList, "HeldDevice", "Weapons"); + AddObjectsToItemList(catalogList, "HDFirearm", "Weapons"); } else if (m_MenuCategory == BOMBS) { From 77e13624384ddf6da081d5f53784e8e07ae579f4 Mon Sep 17 00:00:00 2001 From: fourZK Date: Sun, 22 Aug 2021 15:17:41 +0300 Subject: [PATCH 29/72] Setting `Reloadable = false` should also stop the current reloading sequence --- Entities/HDFirearm.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/Entities/HDFirearm.cpp b/Entities/HDFirearm.cpp index a0d6ef0b2..c4185def3 100644 --- a/Entities/HDFirearm.cpp +++ b/Entities/HDFirearm.cpp @@ -923,19 +923,22 @@ void HDFirearm::Update() if (m_Parent) { Reload(); } } - // No magazine, have started to reload, so put new mag in when done - if (m_Reloading && !m_pMagazine && m_pMagazineReference && m_ReloadTmr.IsPastSimMS(m_ReloadTime)) { - SetMagazine(dynamic_cast(m_pMagazineReference->Clone())); - if (m_ReloadEndSound) { m_ReloadEndSound->Play(m_Pos); } + if (m_Reloading) { + if (!m_Reloadable) { + m_Reloading = false; + } else if (!m_pMagazine && m_pMagazineReference && m_ReloadTmr.IsPastSimMS(m_ReloadTime)) { + SetMagazine(dynamic_cast(m_pMagazineReference->Clone())); + if (m_ReloadEndSound) { m_ReloadEndSound->Play(m_Pos); } - m_ActivationTimer.Reset(); - m_LastFireTmr.Reset(); + m_ActivationTimer.Reset(); + m_LastFireTmr.Reset(); - if (m_PreFireSound && m_Activated) { m_PreFireSound->Play(); } + if (m_PreFireSound && m_Activated) { m_PreFireSound->Play(); } - m_Reloading = false; - m_DoneReloading = true; - } + m_Reloading = false; + m_DoneReloading = true; + } + } // Do stuff to deactivate after being activated if (!m_Activated) From fd5d3fa67edc9e97eb86ee65a552cd82204d6849 Mon Sep 17 00:00:00 2001 From: fourZK Date: Thu, 26 Aug 2021 00:36:47 +0300 Subject: [PATCH 30/72] Review changes --- CHANGELOG.md | 18 ++++---- Entities/AHuman.cpp | 93 ++++++++++++++-------------------------- Entities/AHuman.h | 14 +++--- Entities/Actor.cpp | 17 +++----- Entities/Arm.cpp | 2 +- Entities/Gib.h | 12 +++--- Entities/HDFirearm.cpp | 36 +++++++--------- Entities/HDFirearm.h | 9 ++-- Entities/HeldDevice.h | 1 + Entities/MOSRotating.cpp | 6 +-- Entities/MOSRotating.h | 4 +- Entities/ThrownDevice.h | 8 ++-- 12 files changed, 90 insertions(+), 130 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb52f67a0..bc4ac985b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -183,9 +183,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - New `MOSRotating` INI and Lua (R) property `WoundCountAffectsImpulseLimitRatio` which can be used to make objects more prone to gibbing from impulse when they have also received wounds. -- New `Gib` INI property `SpreadMode` which sports two new spread logic variants which alter the way velocity is applied to the `GibParticle`s when they spawn. This can be used to create richer explosion effects. - `SpreadMode = 0` is the default, fully randomized spread according to `MinVelocity`, `MaxVelocity` and `Spread` values. Think: a piece of grenade fragment, launching out in an arbitrary direction. - `SpreadMode = 1` is the same as the default, but with evenly spaced out angles. Think: an air blast shockwave, dispersing evenly outward from the explosion. +- New `Gib` INI property `SpreadMode` which sports two new spread logic variants which alter the way velocity is applied to the `GibParticle`s when they spawn. This can be used to create richer explosion effects. + `SpreadMode = 0` is the default, fully randomized spread according to `MinVelocity`, `MaxVelocity` and `Spread` values. Think: a piece of grenade fragment, launching out in an arbitrary direction. + `SpreadMode = 1` is the same as the default, but with evenly spaced out angles. Think: an air blast shockwave, dispersing evenly outward from the explosion. `SpreadMode = 2` has an entirely different behavior of its own, which utilizes the fermat spiral as means to evenly disperse the particles in a circular area, according to `MaxVelocity` and `MinVelocity`. Since this mode will always result in a full, 360-degree spread, the `Spread` property can be used to add randomization to the gib particles. Think: a cloud of smoke. - New `Actor` INI and Lua (R/W) property `StableRecoverDelay` which determines how long it takes for an actor to regain `STABLE` status after being rendered `UNSTABLE`. @@ -198,23 +198,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - New `HDFirearm` INI and Lua (R/W) property `Reloadable` which can be used to disable the ability to reload said device. -- New `HDFirearm` Lua (R) property `RoundInMagCapacity` which returns the maximum capacity of the `Magazine`, or its entity reference (from which new magazines are cloned from), which means that the property will always return the maximum ammo capacity of the device, even when reloading. +- New `HDFirearm` Lua (R) property `RoundInMagCapacity` which returns the maximum capacity of the `Magazine` or, if there's not currently a `Magazine`, the maximum capacity of the next `Magazine`. This means that the property will always return the maximum ammo capacity of the device, even when reloading. - New `Entity` Lua (R) property `ModuleName` which returns the filename of the data module from which the entity originates from. - New gameplay setting `MAX_INSERT_NAME_HERE` that hides the HUD of stranded items at a set distance. -- `Arm`s will now react to the recoil of held devices. (This effect can be disabled via `RecoilTransmission` the same way as recoil itself) +- `Arm`s will now react to the recoil of `HeldDevice`s. This is affected by the `Arm`'s `GripStrength` and the `HeldDevice`'s `RecoilTransmission`, in the same way as recoil itself. - `HDFirearm` reload progress now shows up as a HUD element. ### Changed -- Exposed `ThrownDevice` specific offsets to Lua (R/W). +- Exposed `ThrownDevice` properties `StartThrowOffset` and `EndThrowOffset` to Lua (R/W). -- Class `HeldDevice` objects can now show up under "Tools" and "Weapons" rather than just "Shields". +- `HeldDevice`s can now show up as "Tools" in the buy menu, rather than just as "Shields". -- Keyboard-only controlled `AHuman` and `ACrab` actors can now strafe while sharp aiming. +- Keyboard-only controlled `AHuman`s and `ACrab`s can now strafe while sharp-aiming. - Lowered the default `AHuman` Head damage multiplier from 5 to 4. @@ -236,7 +236,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Craft will now automatically scuttle when opening doors at a 90° angle rather than 45°. -- `AHuman` can now sharp-aim slightly while walking, however not while reloading. +- `AHuman`s can now sharp-aim slightly while walking, however not while reloading. - Recoil when firing weapons now affects sharp aim. diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index 3494676bb..7a47ebb6f 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -1410,10 +1410,7 @@ bool AHuman::EquipShieldInBGArm() ////////////////////////////////////////////////////////////////////////////////////////// bool AHuman::UnequipFGArm() { - if (!m_pFGArm || !m_pFGArm->IsAttached()) { - return false; - } - if (m_pFGArm->HoldsSomething()) { + if (m_pFGArm && m_pFGArm->HoldsSomething()) { m_pFGArm->GetHeldDevice()->Deactivate(); m_Inventory.push_back(m_pFGArm->ReleaseHeldMO()); return true; @@ -1422,29 +1419,16 @@ bool AHuman::UnequipFGArm() { } ////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: UnequipBGArm -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Unequips whatever is in the BG arm and puts it into the inventory. -// Arguments: None. -// Return value: Whether there was anything to unequip. - -bool AHuman::UnequipBGArm() -{ - if (!(m_pBGArm && m_pBGArm->IsAttached())) - return false; - - // Put back into the inventory what we had in our hand, if anything - if (m_pBGArm->HoldsSomething()) - { - m_pBGArm->GetHeldDevice()->Deactivate(); - m_Inventory.push_back(m_pBGArm->ReleaseHeldMO()); - return true; - } - return false; +bool AHuman::UnequipBGArm() { + if (m_pBGArm && m_pBGArm->HoldsSomething()) { + m_pBGArm->GetHeldDevice()->Deactivate(); + m_Inventory.push_back(m_pBGArm->ReleaseHeldMO()); + return true; + } + return false; } - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual Method: GetEquippedItem ////////////////////////////////////////////////////////////////////////////////////////// @@ -3182,7 +3166,7 @@ void AHuman::Update() // If the pie menu is on, try to preserve whatever move state we had before it going into effect. if (!m_Controller.IsState(PIE_MENU_ACTIVE)) { - if (m_Controller.IsState(MOVE_RIGHT) || m_Controller.IsState(MOVE_LEFT) || m_MoveState == JUMP && m_Status != INACTIVE) { + if ((m_Controller.IsState(MOVE_RIGHT) || m_Controller.IsState(MOVE_LEFT) || m_MoveState == JUMP) && m_Status != INACTIVE) { for (int i = WALK; i < MOVEMENTSTATECOUNT; ++i) { m_Paths[FGROUND][i].SetHFlip(m_HFlipped); m_Paths[BGROUND][i].SetHFlip(m_HFlipped); @@ -3242,41 +3226,26 @@ void AHuman::Update() bool reloadFG = false; if (m_pFGArm && m_Status != INACTIVE) { + bool changeNext = m_Controller.IsState(WEAPON_CHANGE_NEXT); + bool changePrev = m_Controller.IsState(WEAPON_CHANGE_PREV); HDFirearm * pFireArm = dynamic_cast(m_pFGArm->GetHeldMO()); - if (m_Controller.IsState(WEAPON_CHANGE_NEXT) && m_Controller.IsState(WEAPON_CHANGE_PREV)) { - UnequipFGArm(); - UnequipBGArm(); - m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); - if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } - } else { - if (m_Controller.IsState(WEAPON_CHANGE_NEXT)) { - m_DeviceEquipTimer.Reset(); - if (!m_Inventory.empty() || GetEquippedBGItem()) { - if (pFireArm) { pFireArm->StopActivationSound(); } - if (m_Inventory.empty()) { UnequipBGArm(); } - + if ((changeNext || changePrev) && (!m_Inventory.empty() || UnequipBGArm())) { + if (changeNext && changePrev) { + UnequipFGArm(); + } else { + if (pFireArm) { pFireArm->StopActivationSound(); } + if (changeNext) { m_pFGArm->SetHeldMO(SwapNextInventory(m_pFGArm->ReleaseHeldMO())); - m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); - m_PieNeedsUpdate = true; - - EquipShieldInBGArm(); - m_SharpAimProgress = 0; - } - } - if (m_Controller.IsState(WEAPON_CHANGE_PREV)) { - m_DeviceEquipTimer.Reset(); - if (!m_Inventory.empty() || GetEquippedBGItem()) { - if (pFireArm) { pFireArm->StopActivationSound(); } - if (m_Inventory.empty()) { UnequipBGArm(); } - + } else { m_pFGArm->SetHeldMO(SwapPrevInventory(m_pFGArm->ReleaseHeldMO())); - m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); - m_PieNeedsUpdate = true; - - EquipShieldInBGArm(); - m_SharpAimProgress = 0; } + EquipShieldInBGArm(); } + m_DeviceEquipTimer.Reset(); + m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); + if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } + m_PieNeedsUpdate = true; + m_SharpAimProgress = 0; } // Reload held MO, if applicable @@ -3839,7 +3808,7 @@ void AHuman::Update() m_pFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pFGLeg->GetParentOffset()), m_pFGLeg->GetMaxLength(), g_SceneMan.GetGlobalAcc() * deltaTime, m_AngularVel, m_pFGLeg->GetMass(), deltaTime); } if (m_pBGLeg && (!m_Paths[BGROUND][m_MoveState].PathEnded() || m_JetTimeLeft == m_JetTimeTotal)) { - m_pBGFootGroup->FlailAsLimb( m_Pos, RotateOffset(m_pBGLeg->GetParentOffset()), m_pBGLeg->GetMaxLength(), g_SceneMan.GetGlobalAcc() * deltaTime, m_AngularVel, m_pBGLeg->GetMass(), deltaTime); + m_pBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pBGLeg->GetParentOffset()), m_pBGLeg->GetMaxLength(), g_SceneMan.GetGlobalAcc() * deltaTime, m_AngularVel, m_pBGLeg->GetMass(), deltaTime); } if (m_JetTimeLeft <= 0) { m_MoveState = STAND; @@ -4381,14 +4350,14 @@ void AHuman::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichSc rectfill(pTargetBitmap, drawPos.GetFloorIntX() + 1, drawPos.GetFloorIntY() + m_HUDStack + 7, drawPos.GetFloorIntX() + 16, drawPos.GetFloorIntY() + m_HUDStack + 8, 245); rectfill(pTargetBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 6, drawPos.GetFloorIntX() + static_cast(15.0F * jetTimeRatio), drawPos.GetFloorIntY() + m_HUDStack + 7, gaugeColor); - m_HUDStack += -10; + m_HUDStack -= 10; if (m_pFGArm && !m_DeviceEquipTimer.IsPastRealMS(500)) { if (m_pFGArm->HoldsSomething()) { pSmallFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() + 1, drawPos.GetFloorIntY() + m_HUDStack + 3, m_pFGArm->GetHeldMO()->GetPresetName().c_str(), GUIFont::Centre); } else { pSmallFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() + 1, drawPos.GetFloorIntY() + m_HUDStack + 3, "EMPTY", GUIFont::Centre); } - m_HUDStack += -9; + m_HUDStack -= 9; } } // Held-related GUI stuff @@ -4430,7 +4399,7 @@ void AHuman::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichSc } pSmallFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 3, str, GUIFont::Left); - m_HUDStack += -10; + m_HUDStack -= 10; } if (m_Controller.IsState(PIE_MENU_ACTIVE) || !m_DeviceEquipTimer.IsPastRealMS(700)) { @@ -4443,7 +4412,7 @@ void AHuman::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichSc std::snprintf(str, sizeof(str), "%.0f oz", GetGoldCarried()); pSmallFont->DrawAligned(&allegroBitmap, drawPos.m_X - 0, drawPos.m_Y + m_HUDStack + 2, str, GUIFont::Left); - m_HUDStack += -11; + m_HUDStack -= 11; } */ if (m_pFGArm->HoldsSomething()) { @@ -4451,7 +4420,7 @@ void AHuman::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichSc } else { pSmallFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() + 1, drawPos.GetFloorIntY() + m_HUDStack + 3, "EMPTY", GUIFont::Centre); } - m_HUDStack += -9; + m_HUDStack -= 9; /* // Reload GUI, only show when there's nothing to pick up if (!m_pItemInReach && m_pFGArm->HoldsSomething() && pHeldFirearm && !pHeldFirearm->IsFull()) @@ -4466,7 +4435,7 @@ void AHuman::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichSc { std::snprintf(str, sizeof(str), "NO ARM!"); pSmallFont->DrawAligned(&allegroBitmap, drawPos.m_X + 2, drawPos.m_Y + m_HUDStack + 3, str, GUIFont::Centre); - m_HUDStack += -9; + m_HUDStack -= 9; } // Pickup GUI diff --git a/Entities/AHuman.h b/Entities/AHuman.h index bda216190..7be8ebbc6 100644 --- a/Entities/AHuman.h +++ b/Entities/AHuman.h @@ -575,14 +575,10 @@ ClassInfoGetters /// Whether there was anything to unequip. bool UnequipFGArm(); - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual Method: UnequipBGArm -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Unequips whatever is in the BG arm and puts it into the inventory. -// Arguments: None. -// Return value: Whether there was anything to unequip. - + /// + /// Unequips whatever is in the BG arm and puts it into the inventory. + /// + /// Whether there was anything to unequip. bool UnequipBGArm(); @@ -1004,7 +1000,7 @@ ClassInfoGetters Timer m_SharpAimRevertTimer; //!< For timing the transition from sharp aim back to regular aim. float m_FGArmFlailScalar; //!< The rate at which this AHuman's FG Arm follows the the bodily rotation. Best to keep this at 0 so it doesn't complicate aiming. 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_DeviceEquipTimer; //!< Timer for showing the name of the Device we had just equipped. + Timer m_DeviceEquipTimer; //!< Timer for showing the name of any newly equipped Device. //////////////// // AI States diff --git a/Entities/Actor.cpp b/Entities/Actor.cpp index ffdca2918..b222a55da 100644 --- a/Entities/Actor.cpp +++ b/Entities/Actor.cpp @@ -1463,18 +1463,15 @@ void Actor::Update() ///////////////////////////////////////////// // Take damage from large hits during travel - if (m_TravelImpulse.GetMagnitude() > m_TravelImpulseDamage * 0.5F) { + float travelImpulseMagnitude = m_TravelImpulse.GetMagnitude(); + if (travelImpulseMagnitude > m_TravelImpulseDamage * 0.5F) { if (m_BodyHitSound) { m_BodyHitSound->Play(m_Pos); } - if (m_TravelImpulse.GetMagnitude() > m_TravelImpulseDamage) { - const float impulse = m_TravelImpulse.GetMagnitude() - m_TravelImpulseDamage; - const float damage = impulse / (m_GibImpulseLimit - m_TravelImpulseDamage) * m_MaxHealth; - if (damage > 0) { - m_Health -= damage; - if (m_Health > 0) { - if (m_PainSound) { m_PainSound->Play(m_Pos); } - } - } + if (travelImpulseMagnitude > m_TravelImpulseDamage) { + const float impulse = travelImpulseMagnitude - m_TravelImpulseDamage; + const float damage = std::max(impulse / (m_GibImpulseLimit - m_TravelImpulseDamage) * m_MaxHealth, 0.0F); + m_Health -= damage; + if (damage > 0 && m_Health > 0 && m_PainSound) { m_PainSound->Play(m_Pos); } if (m_Status != DYING && m_Status != DEAD) { m_Status = UNSTABLE; } m_ForceDeepCheck = true; } diff --git a/Entities/Arm.cpp b/Entities/Arm.cpp index 2d664d874..19d535707 100644 --- a/Entities/Arm.cpp +++ b/Entities/Arm.cpp @@ -390,7 +390,7 @@ void Arm::UpdateCurrentHandOffset() { if (m_pHeldMO && !dynamic_cast(m_pHeldMO)) { m_DidReach = false; HeldDevice *heldDevice = dynamic_cast(m_pHeldMO); - // TODO: calculate total grip strength from both arms? (also: fine-tune this shit) + // TODO: calculate total grip strength from both arms? (also: fine-tune this shit, and move it elsewhere) float totalGripStrength = (m_GripStrength || heldDevice->GetJointStrength()) * (heldDevice->GetSupported() ? 2.0F : 1.0F); targetOffset = heldDevice->GetStanceOffset(); // Diminish recoil effect when body is horizontal so that the device doesn't get pushed into terrain when prone. diff --git a/Entities/Gib.h b/Entities/Gib.h index fed6179d5..f9af26fe2 100644 --- a/Entities/Gib.h +++ b/Entities/Gib.h @@ -15,16 +15,18 @@ namespace RTE { public: - enum SpreadMode - { + SerializableClassNameGetter + SerializableOverrideMethods + + /// + /// Different types of logic for the Gib to use when applying velocity to its GibParticles. + /// + enum SpreadMode { SPREAD_RANDOM = 0, SPREAD_EVEN, SPREAD_SPIRAL }; - SerializableClassNameGetter - SerializableOverrideMethods - #pragma region Creation /// /// Constructor method used to instantiate a Gib object in system memory. Create() should be called before using the object. diff --git a/Entities/HDFirearm.cpp b/Entities/HDFirearm.cpp index c4185def3..a240a2eaf 100644 --- a/Entities/HDFirearm.cpp +++ b/Entities/HDFirearm.cpp @@ -884,7 +884,7 @@ void HDFirearm::Update() pShell->SetPos(m_Pos + tempEject); // ##@#@@$ TEMP - shellVel.SetXY(pRound->GetShellVel() * (1.0F - pRound->GetShellVelVariation() * RandomNum()), 0); + shellVel.SetXY(pRound->GetShellVel() * (1.0F - RandomNum(0.0F, pRound->GetShellVelVariation())), 0); shellVel.DegRotate(degAimAngle + m_ShellEjectAngle * (m_HFlipped ? -1 : 1) + shellSpread); pShell->SetVel(m_Vel + shellVel); pShell->SetRotAngle(m_Rotation.GetRadAngle()); @@ -923,21 +923,17 @@ void HDFirearm::Update() if (m_Parent) { Reload(); } } - if (m_Reloading) { - if (!m_Reloadable) { - m_Reloading = false; - } else if (!m_pMagazine && m_pMagazineReference && m_ReloadTmr.IsPastSimMS(m_ReloadTime)) { - SetMagazine(dynamic_cast(m_pMagazineReference->Clone())); - if (m_ReloadEndSound) { m_ReloadEndSound->Play(m_Pos); } + if (m_Reloading && !m_pMagazine && m_pMagazineReference && m_ReloadTmr.IsPastSimMS(m_ReloadTime)) { + SetMagazine(dynamic_cast(m_pMagazineReference->Clone())); + if (m_ReloadEndSound) { m_ReloadEndSound->Play(m_Pos); } - m_ActivationTimer.Reset(); - m_LastFireTmr.Reset(); + m_ActivationTimer.Reset(); + m_LastFireTmr.Reset(); - if (m_PreFireSound && m_Activated) { m_PreFireSound->Play(); } + if (m_PreFireSound && m_Activated) { m_PreFireSound->Play(); } - m_Reloading = false; - m_DoneReloading = true; - } + m_Reloading = false; + m_DoneReloading = true; } // Do stuff to deactivate after being activated @@ -1078,10 +1074,7 @@ void HDFirearm::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mo // Set the screen flash effect to draw at the final post processing stage if (m_FireFrame && m_pFlash && m_pFlash->GetScreenEffect() && mode == g_DrawColor && !onlyPhysical) { - // Fudge the muzzle pos forward a little bit so the glow aligns nicely - Vector muzzlePos = m_MuzzleOff; - muzzlePos.m_X += m_pFlash->GetSpriteWidth() * 0.3F; - muzzlePos = m_Pos + RotateOffset(muzzlePos); + Vector muzzlePos = m_Pos + RotateOffset(m_MuzzleOff + Vector(m_pFlash->GetSpriteWidth() * 0.3F, 0)); if (!g_SceneMan.ObscuredPoint(muzzlePos)) { g_PostProcessMan.RegisterPostEffect(muzzlePos, m_pFlash->GetScreenEffect(), m_pFlash->GetScreenEffectHash(), RandomNum(m_pFlash->GetEffectStopStrength(), m_pFlash->GetEffectStartStrength()), m_pFlash->GetEffectRotAngle()); } @@ -1112,13 +1105,14 @@ void HDFirearm::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whic if (!m_Parent || IsReloading() || m_MaxSharpLength == 0) { return; } float sharpLength = std::max(m_MaxSharpLength * m_SharpAim, 20.0F); - int glow, pointCount; + int glowStrength; + int pointCount; if (playerControlled && sharpLength > 20.0F) { pointCount = m_SharpAim > 0.5F ? 4 : 3; - glow = RandomNum(127, 255); + glowStrength = RandomNum(127, 255); } else { pointCount = 2; - glow = RandomNum(63, 127); + glowStrength = RandomNum(63, 127); } int pointSpacing = 10 - pointCount; sharpLength -= static_cast(pointSpacing * pointCount) * 0.5F; @@ -1129,7 +1123,7 @@ void HDFirearm::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whic Vector aimPoint(sharpLength + static_cast(pointSpacing * i), 0); aimPoint = RotateOffset(aimPoint + muzzleOffset) + m_Pos; - g_PostProcessMan.RegisterGlowDotEffect(aimPoint, YellowDot, glow); + g_PostProcessMan.RegisterGlowDotEffect(aimPoint, YellowDot, glowStrength); aimPoint -= targetPos; g_SceneMan.WrapPosition(aimPoint); putpixel(pTargetBitmap, aimPoint.GetFloorIntX(), aimPoint.GetFloorIntY(), g_YellowGlowColor); diff --git a/Entities/HDFirearm.h b/Entities/HDFirearm.h index 07b5d40bc..51aa0212f 100644 --- a/Entities/HDFirearm.h +++ b/Entities/HDFirearm.h @@ -179,9 +179,10 @@ ClassInfoGetters int GetRoundInMagCount() const; /// - /// Gets the maximum number of rounds the magazine or magazine reference of this HDFirearm can hold. + /// Gets the maximum RoundCount a Magazine of this HDFirearm can hold. + /// If there is no Magazine, it gets the RoundCount of the reference Magazine. /// - /// An int with the maximum number of rounds the magazine or magazine reference of this HDFirearm can hold. + /// An int with the maximum RoundCount the magazine or magazine reference of this HDFirearm can hold. int GetRoundInMagCapacity() const; @@ -658,10 +659,10 @@ ClassInfoGetters bool IsReloadable() const { return m_Reloadable; } /// - /// Sets whether this HDFirearm is reloadable or not. + /// Sets whether this HDFirearm is reloadable or not and halts the reloading process. /// /// Whether this HDFirearm is reloadable. - void SetReloadable(bool isReloadable) { m_Reloadable = isReloadable; } + void SetReloadable(bool isReloadable) { m_Reloadable = isReloadable; m_Reloading = m_Reloading && m_Reloadable; } ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Entities/HeldDevice.h b/Entities/HeldDevice.h index 356136cbf..999dc8d96 100644 --- a/Entities/HeldDevice.h +++ b/Entities/HeldDevice.h @@ -635,6 +635,7 @@ ClassInfoGetters // If this HeldDevice is currently being supported by a second hand. bool m_Supported; bool m_IsUnPickupable; //!< Whether or not this HeldDevice should be able to be picked up at all. + //TODO: move this smelly thing elsewhere std::array m_SeenByPlayer; //!< An array of players that can currently see the pickup HUD of this HeldDevice. std::unordered_set m_PickupableByPresetNames; //!< The unordered set of PresetNames that can pick up this HeldDevice if it's dropped. An empty set means there are no PresetName limitations. float m_GripStrengthMultiplier; //!< The multiplier for how well this HeldDevice can be gripped by Arms. diff --git a/Entities/MOSRotating.cpp b/Entities/MOSRotating.cpp index 63d744d0f..a36639766 100644 --- a/Entities/MOSRotating.cpp +++ b/Entities/MOSRotating.cpp @@ -1015,14 +1015,14 @@ void MOSRotating::CreateGibsWhenGibbing(const Vector &impactImpulse, MovableObje float scale = velocityRange / maxRadius; float randAngle = c_PI * RandomNormalNum(); - for (int i = 1; i <= count; i++) { + for (int i = 0; i <= count; i++) { if (i > 1) { gibParticleClone = dynamic_cast(gibSettingsObject.GetParticlePreset()->Clone()); } float radius = std::sqrt(static_cast(count - i)); gibParticleClone->SetPos(m_Pos + rotatedGibOffset); gibParticleClone->SetHFlipped(m_HFlipped); Vector gibVelocity(radius * scale + minVelocity, 0); - gibParticleClone->SetVel(gibVelocity.RadRotate(randAngle + RandomNum(0.0F, spread) + static_cast(i) * 2.39996F)); + gibVelocity.RadRotate(randAngle + RandomNum(0.0F, spread) + static_cast(i) * 2.39996F); if (lifetime != 0) { gibParticleClone->SetLifetime(static_cast(std::max(static_cast(lifetime) * (1.0F - lifeVariation * ((radius / maxRadius) * 0.75F + RandomNormalNum() * 0.25F)), 1.0F))); } @@ -1062,7 +1062,7 @@ void MOSRotating::CreateGibsWhenGibbing(const Vector &impactImpulse, MovableObje gibVelocity.RadRotate(gibSettingsObject.InheritsVelocity() ? impactImpulse.GetAbsRadAngle() : m_Rotation.GetRadAngle() + (m_HFlipped ? c_PI : 0)); // The "Even" spread will spread all gib particles evenly in an arc, while maintaining a randomized velocity magnitude. if (gibSettingsObject.GetSpreadMode() == Gib::SPREAD_EVEN) { - gibVelocity.RadRotate(gibSpread - gibSpread * 2.0F * static_cast(i) / static_cast(count)); + gibVelocity.RadRotate(gibSpread - (gibSpread * 2.0F * static_cast(i) / static_cast(count))); } else { gibVelocity.RadRotate(gibSpread * RandomNormalNum()); } diff --git a/Entities/MOSRotating.h b/Entities/MOSRotating.h index 7c6eb88ab..4133f0c7f 100644 --- a/Entities/MOSRotating.h +++ b/Entities/MOSRotating.h @@ -646,7 +646,7 @@ ClassInfoGetters void SetGibWoundLimit(int newGibWoundLimit) { m_GibWoundLimit = newGibWoundLimit; } /// - /// Gets the rate at which wound count should diminish the gib impulse limit of this MOSRotating. + /// Gets the rate at which wound count of this MOSRotating will diminish the impulse limit. /// /// The rate at which wound count affects the impulse limit. float GetWoundCountAffectsImpulseLimitRatio() const { return m_WoundCountAffectsImpulseLimitRatio; } @@ -968,7 +968,7 @@ ClassInfoGetters // The number of wound emitters allowed before this gets gibbed. 0 means this can't get gibbed int m_GibWoundLimit; float m_GibBlastStrength; //!< The strength with which Gibs and Attachables will get launched when this MOSRotating is gibbed. - float m_WoundCountAffectsImpulseLimitRatio; //!< At what rate should wound count diminish the gib impulse limit of this MOSRotating. + float m_WoundCountAffectsImpulseLimitRatio; //!< The rate at which this MOSRotating's wound count will diminish the impulse limit. // Gib sound effect SoundContainer *m_GibSound; // Whether to flash effect on gib diff --git a/Entities/ThrownDevice.h b/Entities/ThrownDevice.h index 6b1d9ef1c..61657b733 100644 --- a/Entities/ThrownDevice.h +++ b/Entities/ThrownDevice.h @@ -56,25 +56,25 @@ namespace RTE { #pragma region Getters and Setters /// - /// Gets the Start throw offset of this ThrownDevice's joint relative from the parent Actor's position, if attached. + /// Gets the start throw offset of this ThrownDevice's joint relative from the parent Actor's position, if attached. /// /// A const reference to the current start throw parent offset. Vector GetStartThrowOffset() const { return m_StartThrowOffset; } /// - /// Sets the Start throw offset for this ThrownDevice. + /// Sets the start throw offset for this ThrownDevice. /// /// The new start throw offset. void SetStartThrowOffset(Vector startOffset) { m_StartThrowOffset = startOffset; } /// - /// Gets the End throw offset of this ThrownDevice's joint relative from the parent Actor's position, if attached. + /// Gets the end throw offset of this ThrownDevice's joint relative from the parent Actor's position, if attached. /// /// A const reference to the current end throw parent offset. Vector GetEndThrowOffset() const { return m_EndThrowOffset; } /// - /// Sets the End throw offset for this ThrownDevice. + /// Sets the end throw offset for this ThrownDevice. /// /// The new end throw offset. void SetEndThrowOffset(Vector endOffset) { m_EndThrowOffset = endOffset; } From 40f1580e4edf8210078c25a7163d3a1713308547 Mon Sep 17 00:00:00 2001 From: fourZK Date: Thu, 26 Aug 2021 14:48:25 +0300 Subject: [PATCH 31/72] Fix to issue #368 --- Activities/GameActivity.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Activities/GameActivity.cpp b/Activities/GameActivity.cpp index 22bfc9500..8d82f6730 100644 --- a/Activities/GameActivity.cpp +++ b/Activities/GameActivity.cpp @@ -1562,8 +1562,7 @@ void GameActivity::Update() m_ControlledActor[player]->GetController()->SetDisabled(true); // Player is done setting waypoints - if (m_PlayerController[player].IsState(PRESS_SECONDARY)) - { + if (m_PlayerController[player].IsState(PRESS_SECONDARY) || m_PlayerController[player].IsState(ACTOR_NEXT_PREP) || m_PlayerController[player].IsState(ACTOR_PREV_PREP)) { // Stop drawing the waypoints // m_ControlledActor[player]->DrawWaypoints(false); // Update the player's move path now to the first waypoint set @@ -1629,8 +1628,7 @@ void GameActivity::Update() m_ControlledActor[player]->GetController()->SetDisabled(true); // Player is done setting waypoints - if (m_PlayerController[player].IsState(PRESS_SECONDARY)) - { + if (m_PlayerController[player].IsState(PRESS_SECONDARY) || m_PlayerController[player].IsState(ACTOR_NEXT_PREP) || m_PlayerController[player].IsState(ACTOR_PREV_PREP)) { // Give player control back to actor m_ControlledActor[player]->GetController()->SetDisabled(false); // Switch back to normal view From 36524038918dccea6eb082cc32807818bf61e192 Mon Sep 17 00:00:00 2001 From: fourZK Date: Thu, 26 Aug 2021 15:40:40 +0300 Subject: [PATCH 32/72] Review changes + fix'd a little whoopsie --- Entities/AHuman.cpp | 12 ++++++------ Entities/AHuman.h | 2 +- Entities/MOSRotating.cpp | 7 ++++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index 28d15397b..ac7a3bf0a 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -80,7 +80,7 @@ void AHuman::Clear() m_SharpAimRevertTimer.Reset(); m_FGArmFlailScalar = 0.0F; m_BGArmFlailScalar = 0.7F; - m_DeviceEquipTimer.Reset(); + m_EquipHUDTimer.Reset(); m_DeviceState = SCANNING; m_SweepState = NOSWEEP; @@ -3241,7 +3241,7 @@ void AHuman::Update() } EquipShieldInBGArm(); } - m_DeviceEquipTimer.Reset(); + m_EquipHUDTimer.Reset(); m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } m_PieNeedsUpdate = true; @@ -3531,7 +3531,7 @@ void AHuman::Update() EquipShieldInBGArm(); m_SharpAimProgress = 0; m_PieNeedsUpdate = true; - m_DeviceEquipTimer.Reset(); + m_EquipHUDTimer.Reset(); } //////////////////////////////////////// @@ -3573,7 +3573,7 @@ void AHuman::Update() m_PieNeedsUpdate = true; if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } - m_DeviceEquipTimer.Reset(); + m_EquipHUDTimer.Reset(); } /////////////////////////////////////////////////// @@ -4351,7 +4351,7 @@ void AHuman::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichSc rectfill(pTargetBitmap, drawPos.GetFloorIntX(), drawPos.GetFloorIntY() + m_HUDStack + 6, drawPos.GetFloorIntX() + static_cast(15.0F * jetTimeRatio), drawPos.GetFloorIntY() + m_HUDStack + 7, gaugeColor); m_HUDStack -= 10; - if (m_pFGArm && !m_DeviceEquipTimer.IsPastRealMS(500)) { + if (m_pFGArm && !m_EquipHUDTimer.IsPastRealMS(500)) { if (m_pFGArm->HoldsSomething()) { pSmallFont->DrawAligned(&allegroBitmap, drawPos.GetFloorIntX() + 1, drawPos.GetFloorIntY() + m_HUDStack + 3, m_pFGArm->GetHeldMO()->GetPresetName().c_str(), GUIFont::Centre); } else { @@ -4402,7 +4402,7 @@ void AHuman::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichSc m_HUDStack -= 10; } - if (m_Controller.IsState(PIE_MENU_ACTIVE) || !m_DeviceEquipTimer.IsPastRealMS(700)) { + if (m_Controller.IsState(PIE_MENU_ACTIVE) || !m_EquipHUDTimer.IsPastRealMS(700)) { /* // Display Gold tally if gold chunk is in hand if (m_pFGArm->HoldsSomething() && m_pFGArm->GetHeldMO()->IsGold() && GetGoldCarried() > 0) diff --git a/Entities/AHuman.h b/Entities/AHuman.h index 29832e587..b92e6d240 100644 --- a/Entities/AHuman.h +++ b/Entities/AHuman.h @@ -1000,7 +1000,7 @@ ClassInfoGetters; Timer m_SharpAimRevertTimer; //!< For timing the transition from sharp aim back to regular aim. float m_FGArmFlailScalar; //!< The rate at which this AHuman's FG Arm follows the the bodily rotation. Best to keep this at 0 so it doesn't complicate aiming. 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_DeviceEquipTimer; //!< Timer for showing the name of any newly equipped Device. + Timer m_EquipHUDTimer; //!< Timer for showing the name of any newly equipped Device. //////////////// // AI States diff --git a/Entities/MOSRotating.cpp b/Entities/MOSRotating.cpp index 328f3f729..59b65c27f 100644 --- a/Entities/MOSRotating.cpp +++ b/Entities/MOSRotating.cpp @@ -1014,15 +1014,16 @@ void MOSRotating::CreateGibsWhenGibbing(const Vector &impactImpulse, MovableObje float maxRadius = std::sqrt(static_cast(count)); float scale = velocityRange / maxRadius; float randAngle = c_PI * RandomNormalNum(); + float goldenAngle = 2.39996F; - for (int i = 0; i <= count; i++) { - if (i > 1) { gibParticleClone = dynamic_cast(gibSettingsObject.GetParticlePreset()->Clone()); } + for (int i = 0; i < count; i++) { + if (i > 0) { gibParticleClone = dynamic_cast(gibSettingsObject.GetParticlePreset()->Clone()); } float radius = std::sqrt(static_cast(count - i)); gibParticleClone->SetPos(m_Pos + rotatedGibOffset); gibParticleClone->SetHFlipped(m_HFlipped); Vector gibVelocity(radius * scale + minVelocity, 0); - gibVelocity.RadRotate(randAngle + RandomNum(0.0F, spread) + static_cast(i) * 2.39996F); + gibVelocity.RadRotate(randAngle + RandomNum(0.0F, spread) + static_cast(i) * goldenAngle); if (lifetime != 0) { gibParticleClone->SetLifetime(static_cast(std::max(static_cast(lifetime) * (1.0F - lifeVariation * ((radius / maxRadius) * 0.75F + RandomNormalNum() * 0.25F)), 1.0F))); } From 262ea7d8f12feafd310558a44d0f12572d52574d Mon Sep 17 00:00:00 2001 From: fourZK Date: Sun, 29 Aug 2021 22:37:59 +0300 Subject: [PATCH 33/72] Review changes --- Entities/Actor.cpp | 25 ++++++++++++------------- Entities/HDFirearm.cpp | 10 ++++++++-- Entities/HDFirearm.h | 12 ++++++++++++ Entities/HeldDevice.cpp | 3 +-- Entities/MOSprite.cpp | 4 ++-- Entities/MOSprite.h | 2 +- Entities/Round.cpp | 6 ------ Entities/Round.h | 7 ------- Lua/LuaBindingsEntities.cpp | 3 ++- System/Entity.cpp | 2 +- System/Entity.h | 2 +- 11 files changed, 40 insertions(+), 36 deletions(-) diff --git a/Entities/Actor.cpp b/Entities/Actor.cpp index ad900db11..e01f2bf5a 100644 --- a/Entities/Actor.cpp +++ b/Entities/Actor.cpp @@ -1464,18 +1464,17 @@ void Actor::Update() // Take damage from large hits during travel float travelImpulseMagnitude = m_TravelImpulse.GetMagnitude(); - if (travelImpulseMagnitude > m_TravelImpulseDamage * 0.5F) { - if (m_BodyHitSound) { m_BodyHitSound->Play(m_Pos); } - - if (travelImpulseMagnitude > m_TravelImpulseDamage) { - const float impulse = travelImpulseMagnitude - m_TravelImpulseDamage; - const float damage = std::max(impulse / (m_GibImpulseLimit - m_TravelImpulseDamage) * m_MaxHealth, 0.0F); - m_Health -= damage; - if (damage > 0 && m_Health > 0 && m_PainSound) { m_PainSound->Play(m_Pos); } - if (m_Status != DYING && m_Status != DEAD) { m_Status = UNSTABLE; } - m_ForceDeepCheck = true; - } - } + + if (m_BodyHitSound && travelImpulseMagnitude > m_TravelImpulseDamage * 0.5F) { m_BodyHitSound->Play(m_Pos); } + + if (travelImpulseMagnitude > m_TravelImpulseDamage) { + const float impulse = travelImpulseMagnitude - m_TravelImpulseDamage; + const float damage = std::max(impulse / (m_GibImpulseLimit - m_TravelImpulseDamage) * m_MaxHealth, 0.0F); + m_Health -= damage; + if (damage > 0 && m_Health > 0 && m_PainSound) { m_PainSound->Play(m_Pos); } + if (m_Status != DYING && m_Status != DEAD) { m_Status = UNSTABLE; } + m_ForceDeepCheck = true; + } ///////////////////////////// // Stability logic @@ -1556,7 +1555,7 @@ void Actor::Update() if (m_FrameCount > 1) { - if (m_SpriteAnimMode == LOOPWHENMOVING) + if (m_SpriteAnimMode == LOOPWHENACTIVE) { if (m_Controller.IsState(MOVE_LEFT) || m_Controller.IsState(MOVE_RIGHT) || m_Controller.GetAnalogMove().GetLargest() > 0.1) { diff --git a/Entities/HDFirearm.cpp b/Entities/HDFirearm.cpp index 6e9bd6b8a..a785ee0c9 100644 --- a/Entities/HDFirearm.cpp +++ b/Entities/HDFirearm.cpp @@ -59,6 +59,7 @@ void HDFirearm::Clear() m_ShellEjectAngle = 150; m_ShellSpreadRange = 0; m_ShellAngVelRange = 0; + m_ShellVelVariation = 0.1F; m_AIFireVel = -1; m_AIBulletLifeTime = 0; m_AIBulletAccScalar = -1; @@ -134,6 +135,7 @@ int HDFirearm::Create(const HDFirearm &reference) { m_ShellEjectAngle = reference.m_ShellEjectAngle; m_ShellSpreadRange = reference.m_ShellSpreadRange; m_ShellAngVelRange = reference.m_ShellAngVelRange; + m_ShellVelVariation = reference.m_ShellVelVariation; m_MuzzleOff = reference.m_MuzzleOff; m_EjectOff = reference.m_EjectOff; m_MagOff = reference.m_MagOff; @@ -220,6 +222,8 @@ int HDFirearm::ReadProperty(const std::string_view &propName, Reader &reader) { } else if (propName == "ShellAngVelRange") { reader >> m_ShellAngVelRange; m_ShellAngVelRange /= 2; + } else if (propName == "ShellVelVariation") { + reader >> m_ShellVelVariation; } else if (propName == "MuzzleOffset") { reader >> m_MuzzleOff; } else if (propName == "EjectionOffset") { @@ -294,6 +298,8 @@ int HDFirearm::Save(Writer &writer) const writer << m_ShellSpreadRange * 2; writer.NewProperty("ShellAngVelRange"); writer << m_ShellAngVelRange * 2; + writer.NewProperty("ShellVelocityVariation"); + writer << m_ShellVelVariation; writer.NewProperty("MuzzleOffset"); writer << m_MuzzleOff; writer.NewProperty("EjectionOffset"); @@ -884,7 +890,7 @@ void HDFirearm::Update() pShell->SetPos(m_Pos + tempEject); // ##@#@@$ TEMP - shellVel.SetXY(pRound->GetShellVel() * (1.0F - RandomNum(0.0F, pRound->GetShellVelVariation())), 0); + shellVel.SetXY(pRound->GetShellVel() * (1.0F - RandomNum(0.0F, m_ShellVelVariation)), 0); shellVel.DegRotate(degAimAngle + m_ShellEjectAngle * (m_HFlipped ? -1 : 1) + shellSpread); pShell->SetVel(m_Vel + shellVel); pShell->SetRotAngle(m_Rotation.GetRadAngle()); @@ -997,7 +1003,7 @@ void HDFirearm::Update() // Display and override gun animation if there's a special one if (m_FrameCount > 1) { - if (m_SpriteAnimMode == LOOPWHENMOVING) + if (m_SpriteAnimMode == LOOPWHENACTIVE) { if (m_Activated || m_LastFireTmr.GetElapsedSimTimeMS() < m_DeactivationDelay) { // Max rate of the animation when fully activated and firing diff --git a/Entities/HDFirearm.h b/Entities/HDFirearm.h index c9897c085..0301a5dc1 100644 --- a/Entities/HDFirearm.h +++ b/Entities/HDFirearm.h @@ -327,6 +327,17 @@ ClassInfoGetters; void SetParticleSpreadRange(float range) { m_ParticleSpreadRange = range; }; + /// + /// Gets the random velocity variation scalar at which this HDFirearm's shell is to be ejected. + /// + /// A float with the scalar value. + float GetShellVelVariation() const { return m_ShellVelVariation; } + + /// + /// Sets the random velocity variation scalar at which this HDFirearm's shell is to be ejected. + /// + /// The new velocity variation scalar. + void SetShellVelVariation(float newValue) { m_ShellVelVariation = newValue; } ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetAIFireVel @@ -854,6 +865,7 @@ ClassInfoGetters; float m_ShellSpreadRange; // Range of spread in ang vel of ejected shells, in one direction float m_ShellAngVelRange; + float m_ShellVelVariation; //!< The velocity variation scalar of ejected shells. // The muzzle velocity the AI use when aiming this weapon float m_AIFireVel; // The bullet life time the AI use when aiming this weapon diff --git a/Entities/HeldDevice.cpp b/Entities/HeldDevice.cpp index 753ba49bb..c0990a0a1 100644 --- a/Entities/HeldDevice.cpp +++ b/Entities/HeldDevice.cpp @@ -467,8 +467,7 @@ void HeldDevice::Update() if (m_FrameCount > 1) { - // TODO: Rename SpriteAnimMode LOOPWHENMOVING to something like LOOPWHENACTIVE which is more descriptive and fits both moving Actors and active HeldDevices. - if (m_SpriteAnimMode == LOOPWHENMOVING && m_Activated) + if (m_SpriteAnimMode == LOOPWHENACTIVE && m_Activated) { float cycleTime = ((long)m_SpriteAnimTimer.GetElapsedSimTimeMS()) % m_SpriteAnimDuration; m_Frame = std::floor((cycleTime / (float)m_SpriteAnimDuration) * (float)m_FrameCount); diff --git a/Entities/MOSprite.cpp b/Entities/MOSprite.cpp index cf7f3dcd0..a3a33ac89 100644 --- a/Entities/MOSprite.cpp +++ b/Entities/MOSprite.cpp @@ -182,8 +182,8 @@ int MOSprite::ReadProperty(const std::string_view &propName, Reader &reader) m_SpriteAnimMode = ALWAYSLOOP; else if (mode == "ALWAYSPINGPONG") m_SpriteAnimMode = ALWAYSPINGPONG; - else if (mode == "LOOPWHENMOVING") - m_SpriteAnimMode = LOOPWHENMOVING; + else if (mode == "LOOPWHENACTIVE") + m_SpriteAnimMode = LOOPWHENACTIVE; else Abort */ diff --git a/Entities/MOSprite.h b/Entities/MOSprite.h index caac03acf..e6d59ca4f 100644 --- a/Entities/MOSprite.h +++ b/Entities/MOSprite.h @@ -50,7 +50,7 @@ class MOSprite: ALWAYSLOOP, ALWAYSRANDOM, ALWAYSPINGPONG, - LOOPWHENMOVING, + LOOPWHENACTIVE, LOOPWHENOPENCLOSE, PINGPONGOPENCLOSE, OVERLIFETIME, diff --git a/Entities/Round.cpp b/Entities/Round.cpp index d9b54dff8..be264cd01 100644 --- a/Entities/Round.cpp +++ b/Entities/Round.cpp @@ -15,7 +15,6 @@ namespace RTE { m_Separation = 0; m_Shell = 0; m_ShellVel = 0; - m_ShellVelVariation = 0.1F; m_FireSound.Reset(); m_AILifeTime = 0; m_AIFireVel = -1; @@ -54,7 +53,6 @@ namespace RTE { m_Separation = reference.m_Separation; m_Shell = reference.m_Shell; m_ShellVel = reference.m_ShellVel; - m_ShellVelVariation = reference.m_ShellVelVariation; m_FireSound = reference.m_FireSound; m_AILifeTime = reference.m_AILifeTime; m_AIFireVel = reference.m_AIFireVel; @@ -79,8 +77,6 @@ namespace RTE { m_Shell = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); } else if (propName == "ShellVelocity") { reader >> m_ShellVel; - } else if (propName == "ShellVelocityVariation") { - reader >> m_ShellVelVariation; } else if (propName == "FireSound") { reader >> m_FireSound; } else if (propName == "AILifeTime") { @@ -112,8 +108,6 @@ namespace RTE { writer << m_Shell; writer.NewProperty("ShellVelocity"); writer << m_ShellVel; - writer.NewProperty("ShellVelocityVariation"); - writer << m_ShellVelVariation; writer.NewProperty("FireSound"); writer << m_FireSound; writer.NewProperty("AILifeTime"); diff --git a/Entities/Round.h b/Entities/Round.h index ff5e2ffe7..6397e4124 100644 --- a/Entities/Round.h +++ b/Entities/Round.h @@ -105,12 +105,6 @@ namespace RTE { /// A float with the maximum shell velocity in m/s. float GetShellVel() const { return m_ShellVel; } - /// - /// Gets the random velocity variation scalar at which this round's shell is to be ejected. - /// - /// A float with the scalar value. - float GetShellVelVariation() const { return m_ShellVelVariation; } - /// /// Shows whether this Round has an extra sound sample to play when fired. /// @@ -155,7 +149,6 @@ namespace RTE { const MovableObject *m_Shell; //!< Shell particle MovableObject preset instance. float m_ShellVel; //!< The maximum velocity with which this Round's shell/casing is launched. - float m_ShellVelVariation; //!< The velocity variation scalar for this Round's shell/casing. SoundContainer m_FireSound; //!< The extra firing audio of this Round being fired. diff --git a/Lua/LuaBindingsEntities.cpp b/Lua/LuaBindingsEntities.cpp index ebdc416bd..0361a7a15 100644 --- a/Lua/LuaBindingsEntities.cpp +++ b/Lua/LuaBindingsEntities.cpp @@ -612,6 +612,7 @@ namespace RTE { .property("SharpShakeRange", &HDFirearm::GetSharpShakeRange, &HDFirearm::SetSharpShakeRange) .property("NoSupportFactor", &HDFirearm::GetNoSupportFactor, &HDFirearm::SetNoSupportFactor) .property("ParticleSpreadRange", &HDFirearm::GetParticleSpreadRange, &HDFirearm::SetParticleSpreadRange) + .property("ShellVelVariation", &HDFirearm::GetShellVelVariation, &HDFirearm::SetShellVelVariation) .property("FiredOnce", &HDFirearm::FiredOnce) .property("FiredFrame", &HDFirearm::FiredFrame) .property("RoundsFired", &HDFirearm::RoundsFired) @@ -784,7 +785,7 @@ namespace RTE { luabind::value("ALWAYSLOOP", MOSprite::SpriteAnimMode::ALWAYSLOOP), luabind::value("ALWAYSRANDOM", MOSprite::SpriteAnimMode::ALWAYSRANDOM), luabind::value("ALWAYSPINGPONG", MOSprite::SpriteAnimMode::ALWAYSPINGPONG), - luabind::value("LOOPWHENMOVING", MOSprite::SpriteAnimMode::LOOPWHENMOVING), + luabind::value("LOOPWHENACTIVE", MOSprite::SpriteAnimMode::LOOPWHENACTIVE), luabind::value("LOOPWHENOPENCLOSE", MOSprite::SpriteAnimMode::LOOPWHENOPENCLOSE), luabind::value("PINGPONGOPENCLOSE", MOSprite::SpriteAnimMode::PINGPONGOPENCLOSE), luabind::value("OVERLIFETIME", MOSprite::SpriteAnimMode::OVERLIFETIME), diff --git a/System/Entity.cpp b/System/Entity.cpp index a4803866b..2726b65b4 100644 --- a/System/Entity.cpp +++ b/System/Entity.cpp @@ -166,7 +166,7 @@ namespace RTE { return dataModule->GetFileName(); } } - return "Base.rte"; + return ""; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/System/Entity.h b/System/Entity.h index e0e4fd98d..df08001e6 100644 --- a/System/Entity.h +++ b/System/Entity.h @@ -338,7 +338,7 @@ namespace RTE { std::string GetModuleAndPresetName() const; /// - /// Gets the name of this Entity's Data Module it was defined in, or the Base module if somehow none was found. + /// Gets the name of this Entity's Data Module it was defined in. /// /// A string with the module of this Entity. std::string GetModuleName() const; From 1e000768f41d465353b318f789c84437ca5a7723 Mon Sep 17 00:00:00 2001 From: fourZK Date: Sun, 29 Aug 2021 22:38:58 +0300 Subject: [PATCH 34/72] Misc fixes and cleanup --- Entities/AHuman.cpp | 1 - Entities/Attachable.cpp | 4 ++-- Lua/LuaBindingsActivities.cpp | 6 +++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index ac7a3bf0a..300902912 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -3243,7 +3243,6 @@ void AHuman::Update() } m_EquipHUDTimer.Reset(); m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); - if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } m_PieNeedsUpdate = true; m_SharpAimProgress = 0; } diff --git a/Entities/Attachable.cpp b/Entities/Attachable.cpp index a5763a13f..17ab8ed67 100644 --- a/Entities/Attachable.cpp +++ b/Entities/Attachable.cpp @@ -347,7 +347,7 @@ namespace RTE { if (std::abs(currentRotAngleOffset - m_PrevRotAngleOffset) > 0.01745F) { // Update for 1 degree differences Matrix atomRotationForSubgroup(rootParentAsMOSR->FacingAngle(GetRotAngle()) - rootParentAsMOSR->FacingAngle(rootParentAsMOSR->GetRotAngle())); Vector atomOffsetForSubgroup(g_SceneMan.ShortestDistance(rootParentAsMOSR->GetPos(), m_Pos, g_SceneMan.SceneWrapsX()).FlipX(rootParentAsMOSR->IsHFlipped())); - Matrix rootParentAngleToUse(rootParentAsMOSR->GetRotAngle() * static_cast(rootParentAsMOSR->GetFlipFactor())); + Matrix rootParentAngleToUse(rootParentAsMOSR->GetRotAngle() * rootParentAsMOSR->GetFlipFactor()); atomOffsetForSubgroup /= rootParentAngleToUse; rootParentAsMOSR->GetAtomGroup()->UpdateSubAtoms(GetAtomSubgroupID(), atomOffsetForSubgroup, atomRotationForSubgroup); } @@ -458,7 +458,7 @@ namespace RTE { m_Team = newParent->GetTeam(); if (InheritsHFlipped() != 0) { m_HFlipped = m_InheritsHFlipped == 1 ? m_Parent->IsHFlipped() : !m_Parent->IsHFlipped(); } if (InheritsRotAngle()) { - SetRotAngle(m_Parent->GetRotAngle() + m_InheritedRotAngleOffset * static_cast(m_Parent->GetFlipFactor())); + SetRotAngle(m_Parent->GetRotAngle() + m_InheritedRotAngleOffset * m_Parent->GetFlipFactor()); m_AngularVel = 0.0F; } UpdatePositionAndJointPositionBasedOnOffsets(); diff --git a/Lua/LuaBindingsActivities.cpp b/Lua/LuaBindingsActivities.cpp index 59e11691a..0fe302e18 100644 --- a/Lua/LuaBindingsActivities.cpp +++ b/Lua/LuaBindingsActivities.cpp @@ -47,9 +47,9 @@ namespace RTE { .def("TeamFundsChanged", &Activity::TeamFundsChanged) .def("ReportDeath", &Activity::ReportDeath) .def("GetTeamDeathCount", &Activity::GetTeamDeathCount) - .def("Running", &Activity::IsRunning) - .def("Paused", &Activity::IsPaused) - .def("ActivityOver", &Activity::IsOver) + .def("IsRunning", &Activity::IsRunning) + .def("IsPaused", &Activity::IsPaused) + .def("IsOver", &Activity::IsOver) .def("EnteredOrbit", &Activity::EnteredOrbit) .def("SwitchToActor", &Activity::SwitchToActor) .def("SwitchToNextActor", &Activity::SwitchToNextActor) From e675f857e5455997d6d19320421d927aa00b1204 Mon Sep 17 00:00:00 2001 From: fourZK Date: Sun, 29 Aug 2021 23:05:28 +0300 Subject: [PATCH 35/72] Review changes --- CHANGELOG.md | 6 +++++- Entities/HDFirearm.h | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12f89f084..e9e9cff1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -192,7 +192,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - New `AHuman` Lua (R) property `ThrowProgress` which returns the current throw chargeup progress as a scalar from 0 to 1. -- New `Round` INI property `ShellVelocityVariation` which can be used to randomize the magnitude at which shells are ejected. +- New `HDFirearm` INI and Lua (R/W) property `ShellVelVariation` which can be used to randomize the magnitude at which shells are ejected. - New `HDFirearm` Lua (R) property `ReloadProgress` which returns the current reload progress as a scalar from 0 to 1. @@ -210,6 +210,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Changed +- Changed `MOSprite` property `SpriteAnimMode` `Enum` `LOOPWHENMOVING` to `LOOPWHENACTIVE` as it also describes active devices. + +- Changed `Activity` Lua (R) properties `Running`, `Paused` and `ActivityOver` to `IsRunning`, `IsPaused` and `IsOver` respectively. (NOTE: corresponding `ActivityMan` functions remain unchanged) + - Exposed `ThrownDevice` properties `StartThrowOffset` and `EndThrowOffset` to Lua (R/W). - `HeldDevice`s can now show up as "Tools" in the buy menu, rather than just as "Shields". diff --git a/Entities/HDFirearm.h b/Entities/HDFirearm.h index 0301a5dc1..2c7e707ab 100644 --- a/Entities/HDFirearm.h +++ b/Entities/HDFirearm.h @@ -337,7 +337,7 @@ ClassInfoGetters; /// Sets the random velocity variation scalar at which this HDFirearm's shell is to be ejected. /// /// The new velocity variation scalar. - void SetShellVelVariation(float newValue) { m_ShellVelVariation = newValue; } + void SetShellVelVariation(float newVariation) { m_ShellVelVariation = newVariation; } ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetAIFireVel From b379ae814a0e1bfb3c5433e5e1ff919a0c5acca4 Mon Sep 17 00:00:00 2001 From: fourZK Date: Sun, 29 Aug 2021 23:11:41 +0300 Subject: [PATCH 36/72] Implemented `Round` property `LifeVariation` and consequently fixed bug where `LifeVariation` logic would round down to zero, giving objects infinite life when they shouldn't --- CHANGELOG.md | 2 ++ Entities/AEmitter.cpp | 3 +-- Entities/HDFirearm.cpp | 5 +++++ Entities/MOSRotating.cpp | 4 ++-- Entities/PEmitter.cpp | 3 +-- Entities/Round.cpp | 6 ++++++ Entities/Round.h | 7 +++++++ 7 files changed, 24 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9e9cff1a..ec0ccce82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -208,6 +208,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - `HDFirearm` reload progress now shows up as a HUD element. +- New `Round` INI property `LifeVariation` which can be used to randomize the `Lifetime` of shot particles. + ### Changed - Changed `MOSprite` property `SpriteAnimMode` `Enum` `LOOPWHENMOVING` to `LOOPWHENACTIVE` as it also describes active devices. diff --git a/Entities/AEmitter.cpp b/Entities/AEmitter.cpp index 493c32664..8ca54bba6 100644 --- a/Entities/AEmitter.cpp +++ b/Entities/AEmitter.cpp @@ -522,8 +522,7 @@ void AEmitter::Update() pParticle->SetRotAngle(emitVel.GetAbsRadAngle() + (m_HFlipped ? -c_PI : 0)); pParticle->SetHFlipped(m_HFlipped); - if (pParticle->GetLifetime() != 0) - pParticle->SetLifetime(pParticle->GetLifetime() * (1.0F + ((*eItr)->GetLifeVariation() * RandomNormalNum()))); + if (pParticle->GetLifetime() != 0) { pParticle->SetLifetime(std::max(static_cast(pParticle->GetLifetime() * (1.0F + ((*eItr)->GetLifeVariation() * RandomNormalNum()))), 1)); } pParticle->SetTeam(m_Team); pParticle->SetIgnoresTeamHits(true); diff --git a/Entities/HDFirearm.cpp b/Entities/HDFirearm.cpp index a785ee0c9..ae1318b7f 100644 --- a/Entities/HDFirearm.cpp +++ b/Entities/HDFirearm.cpp @@ -827,6 +827,8 @@ void HDFirearm::Update() Vector particlePos; Vector particleVel; + int particleCountMax = pRound->ParticleCount(); + float lifeVariation = pRound->GetLifeVariation(); // Launch all particles in round MovableObject *pParticle = 0; @@ -844,6 +846,9 @@ void HDFirearm::Update() pParticle->SetVel(m_Vel + particleVel); pParticle->SetRotAngle(particleVel.GetAbsRadAngle() + (m_HFlipped ? -c_PI : 0)); pParticle->SetHFlipped(m_HFlipped); + if (lifeVariation != 0 && pParticle->GetLifetime() != 0) { + pParticle->SetLifetime(std::max(static_cast(pParticle->GetLifetime() * (1.0F + (particleCountMax > 1 ? lifeVariation - (lifeVariation * 2.0F * (pRound->ParticleCount() / particleCountMax)) : lifeVariation * RandomNormalNum()))), 1)); + } // F = m * a totalFireForce += pParticle->GetMass() * pParticle->GetVel().GetMagnitude(); diff --git a/Entities/MOSRotating.cpp b/Entities/MOSRotating.cpp index 59b65c27f..c12117d16 100644 --- a/Entities/MOSRotating.cpp +++ b/Entities/MOSRotating.cpp @@ -1025,7 +1025,7 @@ void MOSRotating::CreateGibsWhenGibbing(const Vector &impactImpulse, MovableObje Vector gibVelocity(radius * scale + minVelocity, 0); gibVelocity.RadRotate(randAngle + RandomNum(0.0F, spread) + static_cast(i) * goldenAngle); if (lifetime != 0) { - gibParticleClone->SetLifetime(static_cast(std::max(static_cast(lifetime) * (1.0F - lifeVariation * ((radius / maxRadius) * 0.75F + RandomNormalNum() * 0.25F)), 1.0F))); + gibParticleClone->SetLifetime(std::max(static_cast(static_cast(lifetime) * (1.0F - lifeVariation * ((radius / maxRadius) * 0.75F + RandomNormalNum() * 0.25F))), 1)); } gibParticleClone->SetRotAngle(gibVelocity.GetAbsRadAngle() + (m_HFlipped ? c_PI : 0)); gibParticleClone->SetAngularVel((gibParticleClone->GetAngularVel() * 0.35F) + (gibParticleClone->GetAngularVel() * 0.65F / mass) * RandomNum()); @@ -1041,7 +1041,7 @@ void MOSRotating::CreateGibsWhenGibbing(const Vector &impactImpulse, MovableObje if (i > 0) { gibParticleClone = dynamic_cast(gibSettingsObject.GetParticlePreset()->Clone()); } if (gibParticleClone->GetLifetime() != 0) { - gibParticleClone->SetLifetime(static_cast(static_cast(gibParticleClone->GetLifetime()) * (1.0F + (lifeVariation * RandomNormalNum())))); + gibParticleClone->SetLifetime(std::max(static_cast(static_cast(gibParticleClone->GetLifetime()) * (1.0F + (lifeVariation * RandomNormalNum()))), 1)); } gibParticleClone->SetRotAngle(GetRotAngle() + gibParticleClone->GetRotAngle()); diff --git a/Entities/PEmitter.cpp b/Entities/PEmitter.cpp index 385e9c283..8b70706ab 100644 --- a/Entities/PEmitter.cpp +++ b/Entities/PEmitter.cpp @@ -477,8 +477,7 @@ namespace RTE { emitVel = RotateOffset(emitVel); pParticle->SetVel(parentVel + emitVel); - if (pParticle->GetLifetime() != 0) - pParticle->SetLifetime(pParticle->GetLifetime() * (1.0F + ((*eItr).GetLifeVariation() * RandomNormalNum()))); + if (pParticle->GetLifetime() != 0) { pParticle->SetLifetime(std::max(static_cast(pParticle->GetLifetime() * (1.0F + ((*eItr).GetLifeVariation() * RandomNormalNum()))), 1)); } pParticle->SetTeam(m_Team); pParticle->SetIgnoresTeamHits(true); diff --git a/Entities/Round.cpp b/Entities/Round.cpp index be264cd01..4bec5b44c 100644 --- a/Entities/Round.cpp +++ b/Entities/Round.cpp @@ -13,6 +13,7 @@ namespace RTE { m_ParticleCount = 0; m_FireVel = 0; m_Separation = 0; + m_LifeVariation = 0; m_Shell = 0; m_ShellVel = 0; m_FireSound.Reset(); @@ -51,6 +52,7 @@ namespace RTE { m_ParticleCount = reference.m_ParticleCount; m_FireVel = reference.m_FireVel; m_Separation = reference.m_Separation; + m_LifeVariation = reference.m_LifeVariation; m_Shell = reference.m_Shell; m_ShellVel = reference.m_ShellVel; m_FireSound = reference.m_FireSound; @@ -73,6 +75,8 @@ namespace RTE { reader >> m_FireVel; } else if (propName == "Separation") { reader >> m_Separation; + } else if (propName == "LifeVariation") { + reader >> m_LifeVariation; } else if (propName == "Shell") { m_Shell = dynamic_cast(g_PresetMan.GetEntityPreset(reader)); } else if (propName == "ShellVelocity") { @@ -104,6 +108,8 @@ namespace RTE { writer << m_FireVel; writer.NewProperty("Separation"); writer << m_Separation; + writer.NewProperty("LifeVariation"); + writer << m_LifeVariation; writer.NewProperty("Shell"); writer << m_Shell; writer.NewProperty("ShellVelocity"); diff --git a/Entities/Round.h b/Entities/Round.h index 6397e4124..ad9687bd8 100644 --- a/Entities/Round.h +++ b/Entities/Round.h @@ -93,6 +93,12 @@ namespace RTE { /// A float with the separation range in pixels. float GetSeparation() const { return m_Separation; } + /// + /// Gets the variation in lifetime of the fired particles in this Round. + /// + /// A float with the life variation scalar. + float GetLifeVariation() const { return m_LifeVariation; } + /// /// Gets the shell casing preset of this Round. Ownership IS NOT transferred! /// @@ -146,6 +152,7 @@ namespace RTE { int m_ParticleCount; //!< How many particle copies there are in this Round. float m_FireVel; //!< The velocity with which this Round is fired. float m_Separation; //!< The range of separation between particles in this Round, in pixels. + float m_LifeVariation; //!< The random variation in life time of each fired particle, in percentage of their life time. const MovableObject *m_Shell; //!< Shell particle MovableObject preset instance. float m_ShellVel; //!< The maximum velocity with which this Round's shell/casing is launched. From fc1aab425bd1799a17abb009c1338b080879a794 Mon Sep 17 00:00:00 2001 From: fourZK Date: Sun, 29 Aug 2021 23:11:59 +0300 Subject: [PATCH 37/72] Changelog entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec0ccce82..1aa988926 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -353,6 +353,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Fixed +- Fixed `LifeVariation` logic rounding down to zero and giving particles infinite lifetime. + - Fixed legs going bonkers for one frame when turning around. - `HFlipped` is now properly assigned to emissions, gibs and particles that are shot from a `HDFirearm`'s `Round` when the source object is also flipped. From 1553edca1d473ba8eb41aa9de94fbc3a9981ce95 Mon Sep 17 00:00:00 2001 From: fourZK Date: Sun, 29 Aug 2021 23:25:54 +0300 Subject: [PATCH 38/72] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1aa988926..fe1716d7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -353,7 +353,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Fixed -- Fixed `LifeVariation` logic rounding down to zero and giving particles infinite lifetime. +- Fixed the logic for `Gib` and `Emission` property `LifeVariation` where it would round down to zero, giving particles infinite lifetime. - Fixed legs going bonkers for one frame when turning around. From 87d8a8063b9c34137d8f98caccf8246f029d3cab Mon Sep 17 00:00:00 2001 From: fourZK Date: Wed, 1 Sep 2021 16:00:07 +0300 Subject: [PATCH 39/72] Exposed `MOSprite` property `PrevRotAngle` to Lua (R) --- CHANGELOG.md | 2 ++ Entities/MOSprite.h | 5 +++++ Lua/LuaBindingsEntities.cpp | 1 + 3 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe1716d7e..94ae95f20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -210,6 +210,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - New `Round` INI property `LifeVariation` which can be used to randomize the `Lifetime` of shot particles. +- Exposed `MOSprite` property `PrevRotAngle` to Lua (R). + ### Changed - Changed `MOSprite` property `SpriteAnimMode` `Enum` `LOOPWHENMOVING` to `LOOPWHENACTIVE` as it also describes active devices. diff --git a/Entities/MOSprite.h b/Entities/MOSprite.h index e6d59ca4f..1a7c53c59 100644 --- a/Entities/MOSprite.h +++ b/Entities/MOSprite.h @@ -266,6 +266,11 @@ class MOSprite: float GetRotAngle() const override { return m_Rotation.GetRadAngle(); } + /// + /// Gets the previous rotational angle of this MOSprite, prior to this frame. + /// + /// The previous rotational angle in radians. + float GetPrevRotAngle() const { return m_PrevRotation.GetRadAngle(); } ////////////////////////////////////////////////////////////////////////////////////////// // Method: GetAngularVel diff --git a/Lua/LuaBindingsEntities.cpp b/Lua/LuaBindingsEntities.cpp index 0361a7a15..065392c1f 100644 --- a/Lua/LuaBindingsEntities.cpp +++ b/Lua/LuaBindingsEntities.cpp @@ -762,6 +762,7 @@ namespace RTE { .property("HFlipped", &MOSprite::IsHFlipped, &MOSprite::SetHFlipped) .property("FlipFactor", &MOSprite::GetFlipFactor) .property("RotAngle", &MOSprite::GetRotAngle, &MOSprite::SetRotAngle) + .property("PrevRotAngle", &MOSprite::GetPrevRotAngle) .property("AngularVel", &MOSprite::GetAngularVel, &MOSprite::SetAngularVel) .property("Frame", &MOSprite::GetFrame, &MOSprite::SetFrame) .property("SpriteAnimMode", &MOSprite::GetSpriteAnimMode, &MOSprite::SetSpriteAnimMode) From bbad8209ce29730a2fcf2c1c752cda5fda138741 Mon Sep 17 00:00:00 2001 From: fourZK Date: Sun, 5 Sep 2021 15:03:18 +0300 Subject: [PATCH 40/72] Fixed `LifeVariation` being improperly assigned tue to wrong datatype --- Entities/HDFirearm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Entities/HDFirearm.cpp b/Entities/HDFirearm.cpp index ae1318b7f..9361506e4 100644 --- a/Entities/HDFirearm.cpp +++ b/Entities/HDFirearm.cpp @@ -847,7 +847,7 @@ void HDFirearm::Update() pParticle->SetRotAngle(particleVel.GetAbsRadAngle() + (m_HFlipped ? -c_PI : 0)); pParticle->SetHFlipped(m_HFlipped); if (lifeVariation != 0 && pParticle->GetLifetime() != 0) { - pParticle->SetLifetime(std::max(static_cast(pParticle->GetLifetime() * (1.0F + (particleCountMax > 1 ? lifeVariation - (lifeVariation * 2.0F * (pRound->ParticleCount() / particleCountMax)) : lifeVariation * RandomNormalNum()))), 1)); + pParticle->SetLifetime(std::max(static_cast(pParticle->GetLifetime() * (1.0F + (particleCountMax > 1 ? lifeVariation - (lifeVariation * 2.0F * (static_cast(pRound->ParticleCount()) / static_cast(particleCountMax))) : lifeVariation * RandomNormalNum()))), 1)); } // F = m * a totalFireForce += pParticle->GetMass() * pParticle->GetVel().GetMagnitude(); From f013e276b22956e77a2c71c8d701ae1c5fc0c09e Mon Sep 17 00:00:00 2001 From: fourZK Date: Mon, 6 Sep 2021 00:29:03 +0300 Subject: [PATCH 41/72] Cleaned up a bunch of duplicate logic from `ACRocket` and `ACDropShip` and moved it to `ACraft`, while also adding new property `ScuttleOnDeath` --- CHANGELOG.md | 2 ++ Entities/ACDropShip.cpp | 69 ------------------------------------- Entities/ACDropShip.h | 2 -- Entities/ACRocket.cpp | 69 ------------------------------------- Entities/ACRocket.h | 2 -- Entities/ACraft.cpp | 50 +++++++++++++++++---------- Entities/ACraft.h | 14 ++++++++ Lua/LuaBindingsEntities.cpp | 1 + 8 files changed, 49 insertions(+), 160 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94ae95f20..3abce91b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -212,6 +212,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Exposed `MOSprite` property `PrevRotAngle` to Lua (R). +- New `ACraft` INI and Lua (R/W) property `ScuttleOnDeath` which can be used to disable the automatic self-destruct sequence when the craft's health drops down to zero. + ### Changed - Changed `MOSprite` property `SpriteAnimMode` `Enum` `LOOPWHENMOVING` to `LOOPWHENACTIVE` as it also describes active devices. diff --git a/Entities/ACDropShip.cpp b/Entities/ACDropShip.cpp index 99ac79036..2bedf1eff 100644 --- a/Entities/ACDropShip.cpp +++ b/Entities/ACDropShip.cpp @@ -43,7 +43,6 @@ void ACDropShip::Clear() m_LateralControl = 0; m_LateralControlSpeed = 6.0f; m_AutoStabilize = 1; - m_ScuttleIfFlippedTime = 4000; m_MaxEngineAngle = 20.0f; } @@ -106,7 +105,6 @@ int ACDropShip::Create(const ACDropShip &reference) { m_LateralControl = reference.m_LateralControl; m_LateralControlSpeed = reference.m_LateralControlSpeed; m_AutoStabilize = reference.m_AutoStabilize; - m_ScuttleIfFlippedTime = reference.m_ScuttleIfFlippedTime; m_MaxEngineAngle = reference.m_MaxEngineAngle; @@ -139,8 +137,6 @@ int ACDropShip::ReadProperty(const std::string_view &propName, Reader &reader) { reader >> m_HatchSwingRange; } else if (propName == "AutoStabilize") { reader >> m_AutoStabilize; - } else if (propName == "ScuttleIfFlippedTime") { - reader >> m_ScuttleIfFlippedTime; } else if (propName == "MaxEngineAngle") { reader >> m_MaxEngineAngle; } else if (propName == "LateralControlSpeed") { @@ -179,8 +175,6 @@ int ACDropShip::Save(Writer &writer) const writer << m_HatchSwingRange; writer.NewProperty("AutoStabilize"); writer << m_AutoStabilize; - writer.NewProperty("ScuttleIfFlippedTime"); - writer << m_ScuttleIfFlippedTime; writer.NewProperty("MaxEngineAngle"); writer << m_MaxEngineAngle; writer.NewProperty("LateralControlSpeed"); @@ -683,69 +677,6 @@ void ACDropShip::Update() ///////////////////////////////////////////////// // Update MovableObject, adds on the forces etc, updated viewpoint ACraft::Update(); - - /////////////////////////////////// - // Explosion logic - - if (m_Status == DEAD) - GibThis(); - - //////////////////////////////////////// - // Balance stuff - - // Get the rotation in radians. - float rot = m_Rotation.GetRadAngle(); - - // Eliminate rotations over half a turn - if (std::fabs(rot) > c_PI) { - rot += (rot > 0) ? -c_TwoPI : c_TwoPI; - m_Rotation.SetRadAngle(rot); - } - // If tipped too far for too long, die - if (rot < c_HalfPI && rot > -c_HalfPI) - { - m_FlippedTimer.Reset(); - } - // Start death process if tipped over for too long - else if (m_ScuttleIfFlippedTime >= 0 && m_FlippedTimer.IsPastSimMS(m_ScuttleIfFlippedTime) && m_Status != DYING) // defult is 4s - { - m_Status = DYING; - m_DeathTmr.Reset(); - } - - // Flash if dying, warning of impending explosion - if (m_Status == DYING) - { - if (m_DeathTmr.IsPastSimMS(500) && m_DeathTmr.AlternateSim(100)) - FlashWhite(10); - } - -/* -// rot = fabs(rot) < c_QuarterPI ? rot : (rot > 0 ? c_QuarterPI : -c_QuarterPI); - - // Rotational balancing spring calc - if (m_Status == STABLE) { - // Break the spring if close to target angle. - if (fabs(rot) > 0.1) - m_AngularVel -= rot * fabs(rot); - else if (fabs(m_AngularVel) > 0.1) - m_AngularVel *= 0.5; - } - // Unstable, or without balance - else if (m_Status == DYING) { -// float rotTarget = rot > 0 ? c_HalfPI : -c_HalfPI; - float rotTarget = c_HalfPI; - float rotDiff = rotTarget - rot; - if (fabs(rotDiff) > 0.1) - m_AngularVel += rotDiff * rotDiff; - else - m_Status = DEAD; - -// else if (fabs(m_AngularVel) > 0.1) -// m_AngularVel *= 0.5; - } - m_Rotation.SetRadAngle(rot); -*/ } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Entities/ACDropShip.h b/Entities/ACDropShip.h index 8b2ee19f0..dd0752803 100644 --- a/Entities/ACDropShip.h +++ b/Entities/ACDropShip.h @@ -342,8 +342,6 @@ ClassInfoGetters; // Automatically stabilize the craft with the upper thrusters? Defaults to yes. int m_AutoStabilize; - // The craft explodes if it has been on its side for more than this many MS (default 4000). Disable by setting to -1. - float m_ScuttleIfFlippedTime; // Maximum engine rotation in degrees float m_MaxEngineAngle; diff --git a/Entities/ACRocket.cpp b/Entities/ACRocket.cpp index b4499231e..8e4d3ddf7 100644 --- a/Entities/ACRocket.cpp +++ b/Entities/ACRocket.cpp @@ -50,7 +50,6 @@ void ACRocket::Clear() m_pURThruster = 0; m_pULThruster = 0; m_GearState = RAISED; - m_ScuttleIfFlippedTime = 4000; for (int i = 0; i < GearStateCount; ++i) { m_Paths[RIGHT][i].Reset(); m_Paths[LEFT][i].Reset(); @@ -146,8 +145,6 @@ int ACRocket::Create(const ACRocket &reference) { m_Paths[LEFT][i].Create(reference.m_Paths[LEFT][i]); } - m_ScuttleIfFlippedTime = reference.m_ScuttleIfFlippedTime; - return 0; } @@ -193,8 +190,6 @@ int ACRocket::ReadProperty(const std::string_view &propName, Reader &reader) { reader >> m_Paths[RIGHT][LOWERING]; } else if (propName == "RaisingGearLimbPath") { reader >> m_Paths[RIGHT][RAISING]; - } else if (propName == "ScuttleIfFlippedTime") { - reader >> m_ScuttleIfFlippedTime; } else { return ACraft::ReadProperty(propName, reader); } @@ -239,8 +234,6 @@ int ACRocket::Save(Writer &writer) const writer << m_Paths[RIGHT][LOWERING]; writer.NewProperty("RaisingGearLimbPath"); writer << m_Paths[RIGHT][RAISING]; - writer.NewProperty("ScuttleIfFlippedTime"); - writer << m_ScuttleIfFlippedTime; return 0; } @@ -718,68 +711,6 @@ void ACRocket::Update() // Update MovableObject, adds on the forces etc, updated viewpoint ACraft::Update(); - /////////////////////////////////// - // Explosion logic - - if (m_Status == DEAD) - GibThis(); - - //////////////////////////////////////// - // Balance stuff - - // Get the rotation in radians. - float rot = m_Rotation.GetRadAngle(); - - // Eliminate rotations over half a turn - if (std::fabs(rot) > c_PI) { - rot += (rot > 0) ? -c_TwoPI : c_TwoPI; - m_Rotation.SetRadAngle(rot); - } - // If tipped too far for too long, die - if (rot < c_HalfPI && rot > -c_HalfPI) - { - m_FlippedTimer.Reset(); - } - // Start death process if tipped over for too long - else if (m_ScuttleIfFlippedTime >= 0 && m_FlippedTimer.IsPastSimMS(m_ScuttleIfFlippedTime) && m_Status != DYING) - { - m_Status = DYING; - m_DeathTmr.Reset(); - } - - // Flash if dying, warning of impending explosion - if (m_Status == DYING) - { - if (m_DeathTmr.IsPastSimMS(500) && m_DeathTmr.AlternateSim(100)) - FlashWhite(10); - } -/* -// rot = fabs(rot) < c_QuarterPI ? rot : (rot > 0 ? c_QuarterPI : -c_QuarterPI); - - // Rotational balancing spring calc - if (m_Status == STABLE) { - // Break the spring if close to target angle. - if (fabs(rot) > 0.1) - m_AngularVel -= rot * fabs(rot); - else if (fabs(m_AngularVel) > 0.1) - m_AngularVel *= 0.5; - } - // Unstable, or without balance - else if (m_Status == DYING) { -// float rotTarget = rot > 0 ? c_HalfPI : -c_HalfPI; - float rotTarget = c_HalfPI; - float rotDiff = rotTarget - rot; - if (fabs(rotDiff) > 0.1) - m_AngularVel += rotDiff * rotDiff; - else - m_Status = DEAD; - -// else if (fabs(m_AngularVel) > 0.1) -// m_AngularVel *= 0.5; - } - m_Rotation.SetRadAngle(rot); -*/ - //////////////////////////////////////// // Hatch Operation diff --git a/Entities/ACRocket.h b/Entities/ACRocket.h index 3d2e2e178..fe326250c 100644 --- a/Entities/ACRocket.h +++ b/Entities/ACRocket.h @@ -321,8 +321,6 @@ ClassInfoGetters; // Limb paths for different movement states. // [0] is for the right limbs, and [1] is for left. LimbPath m_Paths[2][GearStateCount]; - // The craft explodes if it has been on its side for more than this many MS (default 4000). Disable by setting to -1. - float m_ScuttleIfFlippedTime; ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Entities/ACraft.cpp b/Entities/ACraft.cpp index 2a4567757..410fa1864 100644 --- a/Entities/ACraft.cpp +++ b/Entities/ACraft.cpp @@ -283,6 +283,8 @@ void ACraft::Clear() m_MaxPassengers = -1; m_DeliveryDelayMultiplier = 1.0; + m_ScuttleIfFlippedTime = 4000; + m_ScuttleOnDeath = true; } @@ -337,6 +339,8 @@ int ACraft::Create(const ACraft &reference) m_MaxPassengers = reference.m_MaxPassengers; m_DeliveryDelayMultiplier = reference.m_DeliveryDelayMultiplier; + m_ScuttleIfFlippedTime = reference.m_ScuttleIfFlippedTime; + m_ScuttleOnDeath = reference.m_ScuttleOnDeath; return 0; } @@ -377,6 +381,10 @@ int ACraft::ReadProperty(const std::string_view &propName, Reader &reader) reader >> m_LandingCraft; else if (propName == "MaxPassengers") reader >> m_MaxPassengers; + else if (propName == "ScuttleIfFlippedTime") + reader >> m_ScuttleIfFlippedTime; + else if (propName == "ScuttleOnDeath") + reader >> m_ScuttleOnDeath; else return Actor::ReadProperty(propName, reader); @@ -417,6 +425,10 @@ int ACraft::Save(Writer &writer) const writer.NewProperty("MaxPassengers"); writer << m_MaxPassengers; + writer.NewProperty("ScuttleIfFlippedTime"); + writer << m_ScuttleIfFlippedTime; + writer.NewProperty("ScuttleOnDeath"); + writer << m_ScuttleOnDeath; return 0; } @@ -889,18 +901,6 @@ void ACraft::Update() } } - // Check for death - if (m_Health <= 0 && m_Status != DYING && m_Status != DEAD) - { - m_Status = DYING; - m_DeathTmr.Reset(); - DropAllInventory(); - } -/* - if (m_Status == DYING && m_DeathTmr.GetElapsedSimTimeMS() > m_HatchDelay * 2) - m_Status = DEAD; -*/ - /////////////////////////////////////////////////// // Doors open logic @@ -973,13 +973,27 @@ void ACraft::Update() } } + if (m_Status == DEAD) { + if (m_ScuttleOnDeath || m_AIMode == AIMODE_SCUTTLE) { GibThis(); } + } else if (m_Status == DYING) { + if ((m_ScuttleOnDeath || m_AIMode == AIMODE_SCUTTLE) && m_DeathTmr.IsPastSimMS(500) && m_DeathTmr.AlternateSim(100)) { FlashWhite(10); } + } else if (m_Health <= 0 || m_AIMode == AIMODE_SCUTTLE || (m_ScuttleIfFlippedTime >= 0 && m_FlippedTimer.IsPastSimMS(m_ScuttleIfFlippedTime))) { + m_Status = DYING; + m_DeathTmr.Reset(); + DropAllInventory(); + } - // Only make the death happen after the user lets go tf the AI menu - if (m_Status != DEAD && m_AIMode == AIMODE_SCUTTLE) - { - DropAllInventory(); - m_Status = DYING; - } + // Get the rotation in radians. + float rot = m_Rotation.GetRadAngle(); + + // Eliminate rotations over half a turn. + if (std::abs(rot) > c_PI) { + rot += (rot > 0) ? -c_TwoPI : c_TwoPI; + m_Rotation.SetRadAngle(rot); + } + if (rot < c_HalfPI && rot > -c_HalfPI) { + m_FlippedTimer.Reset(); + } ///////////////////////////////////////// // Misc. diff --git a/Entities/ACraft.h b/Entities/ACraft.h index b3ac12319..d8422f629 100644 --- a/Entities/ACraft.h +++ b/Entities/ACraft.h @@ -572,6 +572,18 @@ enum void SetDeliveryDelayMultiplier(float newValue) { m_DeliveryDelayMultiplier = newValue; } + /// + /// Gets whether this ACraft will scuttle automatically on death. + /// + /// Whether this ACraft will scuttle automatically on death. + bool GetScuttleOnDeath() const { return m_ScuttleOnDeath; } + + /// + /// Sets whether this ACraft will scuttle automatically on death. + /// + /// Whether this ACraft will scuttle automatically on death. + void SetScuttleOnDeath(bool scuttleOnDeath) { m_ScuttleOnDeath = scuttleOnDeath; } + /// /// Destroys this ACraft and creates its specified Gibs in its place with appropriate velocities. Any Attachables are removed and also given appropriate velocities. /// @@ -657,6 +669,8 @@ enum SoundContainer *m_CrashSound; // The maximum number of actors that fit in the inventory int m_MaxPassengers; + int m_ScuttleIfFlippedTime; //!< The time after which the craft will scuttle automatically, if tipped over. + bool m_ScuttleOnDeath; //!< Whether the craft will self-destruct at zero health. static bool s_CrabBombInEffect; //!< Flag to determine if a craft is triggering the Crab Bomb effect. diff --git a/Lua/LuaBindingsEntities.cpp b/Lua/LuaBindingsEntities.cpp index 065392c1f..6c4fd81da 100644 --- a/Lua/LuaBindingsEntities.cpp +++ b/Lua/LuaBindingsEntities.cpp @@ -138,6 +138,7 @@ namespace RTE { .property("CrashSound", &ACraft::GetCrashSound, &ACraftSetCrashSound) .property("MaxPassengers", &ACraft::GetMaxPassengers) .property("DeliveryDelayMultiplier", &ACraft::GetDeliveryDelayMultiplier) + .property("ScuttleOnDeath", &ACraft::GetScuttleOnDeath, &ACraft::SetScuttleOnDeath) .def("OpenHatch", &ACraft::OpenHatch) .def("CloseHatch", &ACraft::CloseHatch) From acdef808bda9e5512e6f0de3f2ccd6b152e0a39a Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 7 Sep 2021 16:49:51 +0300 Subject: [PATCH 42/72] Review changes --- Entities/ACrab.cpp | 8 ++++---- Entities/ACrab.h | 2 +- Entities/AHuman.cpp | 17 +++++++---------- Entities/AHuman.h | 18 +++++++----------- Entities/Gib.cpp | 4 ++-- Entities/Gib.h | 10 +++------- Entities/HDFirearm.cpp | 13 ++++++++++--- Entities/HeldDevice.cpp | 8 +++----- Entities/MOSRotating.cpp | 4 ++-- 9 files changed, 39 insertions(+), 45 deletions(-) diff --git a/Entities/ACrab.cpp b/Entities/ACrab.cpp index 084ca4a8c..b63701a52 100644 --- a/Entities/ACrab.cpp +++ b/Entities/ACrab.cpp @@ -659,7 +659,7 @@ bool ACrab::OnSink(const Vector &pos) bool ACrab::AddPieMenuSlices(PieMenuGUI *pPieMenu) { - PieSlice reloadSlice("Reload", PieSlice::PieSliceIndex::PSI_RELOAD, PieSlice::SliceDirection::UP, !FirearmIsFull() && m_Status != INACTIVE); + PieSlice reloadSlice("Reload", PieSlice::PieSliceIndex::PSI_RELOAD, PieSlice::SliceDirection::UP, !FirearmsAreFull() && m_Status != INACTIVE); pPieMenu->AddSlice(reloadSlice); PieSlice sentryAISlice("Sentry AI Mode", PieSlice::PieSliceIndex::PSI_SENTRY, PieSlice::SliceDirection::DOWN); @@ -781,7 +781,7 @@ bool ACrab::FirearmIsEmpty() const { ////////////////////////////////////////////////////////////////////////////////////////// // Description: Indicates whether the currently held HDFirearm's is almost out of ammo. -bool ACrab::FirearmIsFull() const { +bool ACrab::FirearmsAreFull() const { if (m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { for (const HeldDevice *mountedDevice : m_pTurret->GetMountedDevices()) { if (const HDFirearm *mountedFirearm = dynamic_cast(mountedDevice); mountedFirearm && !mountedFirearm->IsFull()) { @@ -2242,7 +2242,7 @@ void ACrab::Update() //////////////////////////////////// // Reload held MO, if applicable - if (m_Controller.IsState(WEAPON_RELOAD) && !FirearmIsFull() && m_Status != INACTIVE) { + if (m_Controller.IsState(WEAPON_RELOAD) && !FirearmsAreFull() && m_Status != INACTIVE) { ReloadFirearms(); if (m_DeviceSwitchSound) { m_DeviceSwitchSound->Play(m_Pos); } @@ -2832,7 +2832,7 @@ void ACrab::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichScr // Player AI drawing - // Device aiming reticule + // Device aiming reticle if (m_Controller.IsState(AIM_SHARP) && m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) m_pTurret->GetFirstMountedDevice()->DrawHUD(pTargetBitmap, targetPos, whichScreen, m_Controller.IsPlayerControlled()); diff --git a/Entities/ACrab.h b/Entities/ACrab.h index 989d67577..e1eb52b61 100644 --- a/Entities/ACrab.h +++ b/Entities/ACrab.h @@ -352,7 +352,7 @@ class ACrab : /// Indicates whether all of the MountedDevices are at full capacity. /// /// Whether all of the MountedDevices are at full capacity. - bool FirearmIsFull() const; + bool FirearmsAreFull() const; ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index 300902912..00721b401 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -4141,11 +4141,8 @@ void AHuman::Update() ////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: DrawThrowingReticule -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws an aiming aid in front of this HeldDevice for throwing. -void AHuman::DrawThrowingReticule(BITMAP *pTargetBitmap, const Vector &targetPos, float progressScalar) const { +void AHuman::DrawThrowingReticle(BITMAP *targetBitmap, const Vector &targetPos, float progressScalar) const { const int pointCount = 9; Vector points[pointCount]; //Color colors[pointCount]; @@ -4157,7 +4154,7 @@ void AHuman::DrawThrowingReticule(BITMAP *pTargetBitmap, const Vector &targetPos Vector outOffset(15.0F * GetFlipFactor(), -5.0F); - acquire_bitmap(pTargetBitmap); + acquire_bitmap(targetBitmap); for (int i = 0; i < pointCount * progressScalar; ++i) { points[i].FlipX(m_HFlipped); @@ -4167,13 +4164,13 @@ void AHuman::DrawThrowingReticule(BITMAP *pTargetBitmap, const Vector &targetPos if (m_pFGArm) points[i] += m_pFGArm->GetParentOffset(); - // Put the flickering glows on the reticule dots, in absolute scene coordinates + // Put the flickering glows on the reticle dots, in absolute scene coordinates g_PostProcessMan.RegisterGlowDotEffect(points[i], YellowDot, 55 + RandomNum(0, 100)); - putpixel(pTargetBitmap, points[i].GetFloorIntX() - targetPos.GetFloorIntX(), points[i].GetFloorIntY() - targetPos.GetFloorIntY(), g_YellowGlowColor); + putpixel(targetBitmap, points[i].GetFloorIntX() - targetPos.GetFloorIntX(), points[i].GetFloorIntY() - targetPos.GetFloorIntY(), g_YellowGlowColor); } - release_bitmap(pTargetBitmap); + release_bitmap(targetBitmap); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -4274,7 +4271,7 @@ void AHuman::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichSc if (m_pFGArm && m_pFGArm->HoldsHeldDevice()) { // Draw the aiming dots for the currently held device. if (m_ArmsState == THROWING_PREP) { - DrawThrowingReticule(pTargetBitmap, targetPos, GetThrowProgress()); + DrawThrowingReticle(pTargetBitmap, targetPos, GetThrowProgress()); } else if (m_Controller.IsState(AIM_SHARP) || (m_Controller.IsPlayerControlled() && !m_Controller.IsState(PIE_MENU_ACTIVE))) { m_pFGArm->GetHeldDevice()->DrawHUD(pTargetBitmap, targetPos, whichScreen, m_Controller.IsState(AIM_SHARP) && m_Controller.IsPlayerControlled()); } @@ -4292,7 +4289,7 @@ void AHuman::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichSc { AllegroBitmap allegroBitmap(pTargetBitmap); /* - // Device aiming reticule + // Device aiming reticle if (m_Controller.IsState(AIM_SHARP) && m_pFGArm && m_pFGArm->IsAttached() && m_pFGArm->HoldsHeldDevice()) m_pFGArm->GetHeldDevice()->DrawHUD(pTargetBitmap, targetPos, whichScreen);*/ diff --git a/Entities/AHuman.h b/Entities/AHuman.h index b92e6d240..1e46bfee5 100644 --- a/Entities/AHuman.h +++ b/Entities/AHuman.h @@ -923,17 +923,13 @@ ClassInfoGetters; void ChunkGold(); -////////////////////////////////////////////////////////////////////////////////////////// -// Method: DrawThrowingReticule -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Draws an aiming aid in front of this HeldDevice for throwing. -// Arguments: A pointer to a BITMAP to draw on. -// The absolute position of the target bitmap's upper left corner in the Scene. -// A normalized scalar that determines how much of the magnitude of the -// reticule should be drawn, to indicate force in the throw. -// Return value: None. - - void DrawThrowingReticule(BITMAP *pTargetBitmap, const Vector &targetPos = Vector(), float progressScalar = 1.0F) const; + /// + /// Draws an aiming aid in front of this AHuman for throwing. + /// + /// A pointer to a BITMAP to draw on. + /// The absolute position of the target bitmap's upper left corner in the Scene. + /// A normalized scalar that determines the magnitude of the reticle, to indicate force in the throw. + void DrawThrowingReticle(BITMAP *targetBitmap, const Vector &targetPos = Vector(), float progressScalar = 1.0F) const; // Member variables diff --git a/Entities/Gib.cpp b/Entities/Gib.cpp index 843200667..2900e57d7 100644 --- a/Entities/Gib.cpp +++ b/Entities/Gib.cpp @@ -18,7 +18,7 @@ namespace RTE { m_LifeVariation = 0.1F; m_InheritsVel = true; m_IgnoresTeamHits = false; - m_SpreadMode = SPREAD_RANDOM; + m_SpreadMode = SpreadMode::SpreadRandom; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -61,7 +61,7 @@ namespace RTE { } else if (propName == "IgnoresTeamHits") { reader >> m_IgnoresTeamHits; } else if (propName == "SpreadMode") { - reader >> m_SpreadMode; + m_SpreadMode = static_cast(std::stoi(reader.ReadPropValue())); } else { return Serializable::ReadProperty(propName, reader); } diff --git a/Entities/Gib.h b/Entities/Gib.h index 23eaf1de7..ca445a443 100644 --- a/Entities/Gib.h +++ b/Entities/Gib.h @@ -21,11 +21,7 @@ namespace RTE { /// /// Different types of logic for the Gib to use when applying velocity to its GibParticles. /// - enum SpreadMode { - SPREAD_RANDOM = 0, - SPREAD_EVEN, - SPREAD_SPIRAL - }; + enum SpreadMode { SpreadRandom, SpreadEven, SpreadSpiral }; #pragma region Creation /// @@ -114,7 +110,7 @@ namespace RTE { /// Gets this Gib's spread mode, which determines how velocity angles are applied to the GibParticles. /// /// The spread mode of this Gib. - int GetSpreadMode() const { return m_SpreadMode; } + SpreadMode GetSpreadMode() const { return m_SpreadMode; } #pragma endregion protected: @@ -128,7 +124,7 @@ namespace RTE { float m_LifeVariation; //!< The per-Gib variation in Lifetime, in percentage of the existing Lifetime of the gib. bool m_InheritsVel; //!< Whether this Gib should inherit the velocity of the exploding parent or not. bool m_IgnoresTeamHits; //!< Whether this Gib should ignore hits with the team of the exploding parent or not. - int m_SpreadMode; //!< Determines what kind of logic is used when applying velocity to the GibParticle objects. + SpreadMode m_SpreadMode; //!< Determines what kind of logic is used when applying velocity to the GibParticle objects. private: diff --git a/Entities/HDFirearm.cpp b/Entities/HDFirearm.cpp index 9361506e4..7a40444bf 100644 --- a/Entities/HDFirearm.cpp +++ b/Entities/HDFirearm.cpp @@ -430,7 +430,12 @@ int HDFirearm::GetRoundInMagCount() const ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int HDFirearm::GetRoundInMagCapacity() const { - return m_pMagazine ? m_pMagazine->GetCapacity() : (m_pMagazineReference ? m_pMagazineReference->GetCapacity() : 0); + if (m_pMagazine) { + return m_pMagazine->GetCapacity(); + } else if (m_pMagazineReference) { + return m_pMagazineReference->GetCapacity(); + } + return 0; } @@ -1113,7 +1118,9 @@ void HDFirearm::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whic HeldDevice::DrawHUD(pTargetBitmap, targetPos, whichScreen); - if (!m_Parent || IsReloading() || m_MaxSharpLength == 0) { return; } + if (!m_Parent || IsReloading() || m_MaxSharpLength == 0) { + return; + } float sharpLength = std::max(m_MaxSharpLength * m_SharpAim, 20.0F); int glowStrength; @@ -1139,7 +1146,7 @@ void HDFirearm::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whic g_SceneMan.WrapPosition(aimPoint); putpixel(pTargetBitmap, aimPoint.GetFloorIntX(), aimPoint.GetFloorIntY(), g_YellowGlowColor); } - release_bitmap(pTargetBitmap); + //release_bitmap(pTargetBitmap); } } // namespace RTE diff --git a/Entities/HeldDevice.cpp b/Entities/HeldDevice.cpp index c0990a0a1..37d602c4a 100644 --- a/Entities/HeldDevice.cpp +++ b/Entities/HeldDevice.cpp @@ -45,9 +45,7 @@ void HeldDevice::Clear() m_MaxSharpLength = 0; m_Supported = false; m_SupportOffset.Reset(); - for (int player = Players::PlayerOne; player < Players::MaxPlayerCount; ++player) { - m_SeenByPlayer[player] = false; - } + m_SeenByPlayer.fill(false); m_IsUnPickupable = false; m_PickupableByPresetNames.clear(); m_GripStrengthMultiplier = 1.0F; @@ -599,9 +597,9 @@ void HeldDevice::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whi float dist; // TODO: @MaximDude replace radius with settings property! float radius = 100.0F; //static_cast(g_SettingsMan.GetPickupHUDRadius()); - m_SeenByPlayer[viewingPlayer] = radius < 0 || g_SceneMan.ShortestDistance(m_Pos, g_SceneMan.GetScrollTarget(whichScreen), g_SceneMan.SceneWrapsX()).GetMagnitude() < radius; + m_SeenByPlayer.at(viewingPlayer) = radius < 0 || g_SceneMan.ShortestDistance(m_Pos, g_SceneMan.GetScrollTarget(whichScreen), g_SceneMan.SceneWrapsX()).GetMagnitude() < radius; } - if (m_SeenByPlayer[viewingPlayer]) { + if (m_SeenByPlayer.at(viewingPlayer)) { AllegroBitmap pBitmapInt(pTargetBitmap); pSymbolFont->DrawAligned(&pBitmapInt, drawPos.GetFloorIntX() - 1, drawPos.GetFloorIntY() - 20, str, GUIFont::Centre); std::snprintf(str, sizeof(str), "%s", m_PresetName.c_str()); diff --git a/Entities/MOSRotating.cpp b/Entities/MOSRotating.cpp index c12117d16..ed168dedd 100644 --- a/Entities/MOSRotating.cpp +++ b/Entities/MOSRotating.cpp @@ -1010,7 +1010,7 @@ void MOSRotating::CreateGibsWhenGibbing(const Vector &impactImpulse, MovableObje Vector rotatedGibOffset = RotateOffset(gibSettingsObject.GetOffset()); // The "Spiral" spread mode uses the fermat spiral as means to determine the velocity of the gib particles, resulting in a evenly spaced out circle (or ring) of particles. - if (gibSettingsObject.GetSpreadMode() == Gib::SPREAD_SPIRAL) { + if (gibSettingsObject.GetSpreadMode() == Gib::SpreadMode::SpreadSpiral) { float maxRadius = std::sqrt(static_cast(count)); float scale = velocityRange / maxRadius; float randAngle = c_PI * RandomNormalNum(); @@ -1062,7 +1062,7 @@ void MOSRotating::CreateGibsWhenGibbing(const Vector &impactImpulse, MovableObje gibVelocity.RadRotate(gibSettingsObject.InheritsVelocity() ? impactImpulse.GetAbsRadAngle() : m_Rotation.GetRadAngle() + (m_HFlipped ? c_PI : 0)); // The "Even" spread will spread all gib particles evenly in an arc, while maintaining a randomized velocity magnitude. - if (gibSettingsObject.GetSpreadMode() == Gib::SPREAD_EVEN) { + if (gibSettingsObject.GetSpreadMode() == Gib::SpreadMode::SpreadEven) { gibVelocity.RadRotate(gibSpread - (gibSpread * 2.0F * static_cast(i) / static_cast(count))); } else { gibVelocity.RadRotate(gibSpread * RandomNormalNum()); From de5641946dc5d70fcedf6c6760316e2dea71e0c5 Mon Sep 17 00:00:00 2001 From: fourZK Date: Tue, 7 Sep 2021 16:52:47 +0300 Subject: [PATCH 43/72] Small fix for LifeVariation distribution in multi-particle Rounds --- Entities/HDFirearm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Entities/HDFirearm.cpp b/Entities/HDFirearm.cpp index 7a40444bf..8780ce745 100644 --- a/Entities/HDFirearm.cpp +++ b/Entities/HDFirearm.cpp @@ -852,7 +852,7 @@ void HDFirearm::Update() pParticle->SetRotAngle(particleVel.GetAbsRadAngle() + (m_HFlipped ? -c_PI : 0)); pParticle->SetHFlipped(m_HFlipped); if (lifeVariation != 0 && pParticle->GetLifetime() != 0) { - pParticle->SetLifetime(std::max(static_cast(pParticle->GetLifetime() * (1.0F + (particleCountMax > 1 ? lifeVariation - (lifeVariation * 2.0F * (static_cast(pRound->ParticleCount()) / static_cast(particleCountMax))) : lifeVariation * RandomNormalNum()))), 1)); + pParticle->SetLifetime(std::max(static_cast(pParticle->GetLifetime() * (1.0F + (particleCountMax > 1 ? lifeVariation - (lifeVariation * 2.0F * (static_cast(pRound->ParticleCount()) / static_cast(particleCountMax - 1))) : lifeVariation * RandomNormalNum()))), 1)); } // F = m * a totalFireForce += pParticle->GetMass() * pParticle->GetVel().GetMagnitude(); From e9b33e33b13a981c15c5a0459dabaa926579c422 Mon Sep 17 00:00:00 2001 From: Gareth YR Date: Tue, 7 Sep 2021 11:01:40 -0300 Subject: [PATCH 44/72] Comment out acquire_bitmap to match commented release --- Entities/HDFirearm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Entities/HDFirearm.cpp b/Entities/HDFirearm.cpp index 8780ce745..e43e071ca 100644 --- a/Entities/HDFirearm.cpp +++ b/Entities/HDFirearm.cpp @@ -1136,7 +1136,7 @@ void HDFirearm::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whic sharpLength -= static_cast(pointSpacing * pointCount) * 0.5F; Vector muzzleOffset(std::max(m_MuzzleOff.m_X, m_SpriteRadius), m_MuzzleOff.m_Y); - acquire_bitmap(pTargetBitmap); + //acquire_bitmap(pTargetBitmap); for (int i = 0; i < pointCount; ++i) { Vector aimPoint(sharpLength + static_cast(pointSpacing * i), 0); aimPoint = RotateOffset(aimPoint + muzzleOffset) + m_Pos; From 53c58f5316d2174cf9b4d5658d5b4efc910a8c75 Mon Sep 17 00:00:00 2001 From: MaximDude Date: Thu, 9 Sep 2021 18:44:46 +0300 Subject: [PATCH 45/72] Add Settings.ini and settings menu handling for UnheldItemsHUDDisplayRange --- CHANGELOG.md | 5 ++++- Entities/HeldDevice.cpp | 7 +++---- Managers/SettingsMan.cpp | 4 ++++ Managers/SettingsMan.h | 13 +++++++++++++ Menus/SettingsGameplayGUI.cpp | 32 ++++++++++++++++++++++++++++++++ Menus/SettingsGameplayGUI.h | 9 +++++++++ 6 files changed, 65 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02cb5439c..7c8f470fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -213,7 +213,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - New `ACraft` INI and Lua (R/W) property `ScuttleOnDeath` which can be used to disable the automatic self-destruct sequence when the craft's health drops down to zero. -- New gameplay setting `MAX_INSERT_NAME_HERE` that hides the HUD of stranded items at a set distance. +- New `Settings.ini` property `UnheldItemsHUDDisplayRange = numPixels` that hides the HUD of stranded items at a set distance. Default is 500 (25 meters). + Value of -1 or anything below means all HUDs will be hidden and the only indication an item can be picked up will be on the Actor's HUD when standing on top of it. + Value of 0 means there is no range limit and all items on Scene will display the pick-up HUD. + Valid range values are 1-1000, anything above will be considered as no range limit. diff --git a/Entities/HeldDevice.cpp b/Entities/HeldDevice.cpp index 37d602c4a..70713f85a 100644 --- a/Entities/HeldDevice.cpp +++ b/Entities/HeldDevice.cpp @@ -20,6 +20,7 @@ #include "GUI.h" #include "AllegroBitmap.h" +#include "SettingsMan.h" namespace RTE { @@ -594,10 +595,8 @@ void HeldDevice::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whi } else { m_BlinkTimer.Reset(); // Check for nearby actors that will toggle this pickup HUD - float dist; - // TODO: @MaximDude replace radius with settings property! - float radius = 100.0F; //static_cast(g_SettingsMan.GetPickupHUDRadius()); - m_SeenByPlayer.at(viewingPlayer) = radius < 0 || g_SceneMan.ShortestDistance(m_Pos, g_SceneMan.GetScrollTarget(whichScreen), g_SceneMan.SceneWrapsX()).GetMagnitude() < radius; + float range = g_SettingsMan.GetUnheldItemsHUDDisplayRange(); + m_SeenByPlayer.at(viewingPlayer) = range > -1 && (range == 0 || g_SceneMan.ShortestDistance(m_Pos, g_SceneMan.GetScrollTarget(whichScreen), g_SceneMan.SceneWrapsX()).GetMagnitude() < range); } if (m_SeenByPlayer.at(viewingPlayer)) { AllegroBitmap pBitmapInt(pTargetBitmap); diff --git a/Managers/SettingsMan.cpp b/Managers/SettingsMan.cpp index 2f99e030e..005c8b604 100644 --- a/Managers/SettingsMan.cpp +++ b/Managers/SettingsMan.cpp @@ -20,6 +20,7 @@ namespace RTE { m_FlashOnBrainDamage = true; m_BlipOnRevealUnseen = true; + m_UnheldItemsHUDDisplayRange = 25; m_EndlessMetaGameMode = false; m_EnableCrabBombs = false; m_CrabBombThreshold = 42; @@ -129,6 +130,8 @@ namespace RTE { reader >> m_BlipOnRevealUnseen; } else if (propName == "MaxUnheldItems") { reader >> g_MovableMan.m_MaxDroppedItems; + } else if (propName == "UnheldItemsHUDDisplayRange") { + SetUnheldItemsHUDDisplayRange(std::stof(reader.ReadPropValue())); } else if (propName == "SloMoThreshold") { reader >> g_MovableMan.m_SloMoThreshold; } else if (propName == "SloMoDurationMS") { @@ -299,6 +302,7 @@ namespace RTE { writer.NewPropertyWithValue("FlashOnBrainDamage", m_FlashOnBrainDamage); writer.NewPropertyWithValue("BlipOnRevealUnseen", m_BlipOnRevealUnseen); writer.NewPropertyWithValue("MaxUnheldItems", g_MovableMan.m_MaxDroppedItems); + writer.NewPropertyWithValue("UnheldItemsHUDDisplayRange", m_UnheldItemsHUDDisplayRange); writer.NewPropertyWithValue("SloMoThreshold", g_MovableMan.m_SloMoThreshold); writer.NewPropertyWithValue("SloMoDurationMS", g_MovableMan.m_SloMoDuration); writer.NewPropertyWithValue("EndlessMetaGameMode", m_EndlessMetaGameMode); diff --git a/Managers/SettingsMan.h b/Managers/SettingsMan.h index 31d44944e..de4a86d8c 100644 --- a/Managers/SettingsMan.h +++ b/Managers/SettingsMan.h @@ -90,6 +90,18 @@ namespace RTE { /// New value for Blip on reveal unseen option. void SetBlipOnRevealUnseen(bool newValue) { m_BlipOnRevealUnseen = newValue; } + /// + /// Gets the range in which devices on Scene will show the pick-up HUD. + /// + /// The range in which devices on Scene will show the pick-up HUD, in pixels. -1 means HUDs are hidden, 0 means unlimited range. + float GetUnheldItemsHUDDisplayRange() const { return m_UnheldItemsHUDDisplayRange; } + + /// + /// Sets the range in which devices on Scene will show the pick-up HUD. + /// + /// The new range in which devices on Scene will show the pick-up HUD, in pixels. -1 means HUDs are hidden, 0 means unlimited range. + void SetUnheldItemsHUDDisplayRange(float newRadius) { m_UnheldItemsHUDDisplayRange = std::floor(newRadius); } + /// /// Whether red and white flashes appear when brain is damaged. /// @@ -384,6 +396,7 @@ namespace RTE { bool m_ShowForeignItems; //!< Do not show foreign items in buy menu. bool m_FlashOnBrainDamage; //!< Whether red flashes on brain damage are on or off. bool m_BlipOnRevealUnseen; //!< Blip if unseen is revealed. + float m_UnheldItemsHUDDisplayRange; //!< Range in which devices on Scene will show the pick-up HUD, in pixels. -1 means HUDs are hidden, 0 means unlimited range. bool m_EndlessMetaGameMode; //!< Endless MetaGame mode. bool m_EnableCrabBombs; //!< Whether all actors (except Brains and Doors) should be annihilated if a number exceeding the crab bomb threshold is released at once. int m_CrabBombThreshold; //!< The number of crabs needed to be released at once to trigger the crab bomb effect. diff --git a/Menus/SettingsGameplayGUI.cpp b/Menus/SettingsGameplayGUI.cpp index 5fd65ef7f..f4adf8848 100644 --- a/Menus/SettingsGameplayGUI.cpp +++ b/Menus/SettingsGameplayGUI.cpp @@ -6,6 +6,8 @@ #include "GUICollectionBox.h" #include "GUICheckbox.h" #include "GUITextBox.h" +#include "GUISlider.h" +#include "GUILabel.h" namespace RTE { @@ -41,6 +43,18 @@ namespace RTE { m_CrabBombThresholdTextbox->SetText(std::to_string(g_SettingsMan.GetCrabBombThreshold())); m_CrabBombThresholdTextbox->SetNumericOnly(true); m_CrabBombThresholdTextbox->SetMaxTextLength(3); + + m_UnheldItemsHUDDisplayRangeSlider = dynamic_cast(m_GUIControlManager->GetControl("SliderUnheldItemsHUDRange")); + int unheldItemsHUDDisplayRangeValue = static_cast(g_SettingsMan.GetUnheldItemsHUDDisplayRange()); + if (unheldItemsHUDDisplayRangeValue <= -1) { + m_UnheldItemsHUDDisplayRangeSlider->SetValue(m_UnheldItemsHUDDisplayRangeSlider->GetMinimum()); + } else if (unheldItemsHUDDisplayRangeValue == 0) { + m_UnheldItemsHUDDisplayRangeSlider->SetValue(m_UnheldItemsHUDDisplayRangeSlider->GetMaximum()); + } else { + m_UnheldItemsHUDDisplayRangeSlider->SetValue(static_cast(g_SettingsMan.GetUnheldItemsHUDDisplayRange() / c_PPM)); + } + m_UnheldItemsHUDDisplayRangeLabel = dynamic_cast(m_GUIControlManager->GetControl("LabelUnheldItemsHUDRangeValue")); + UpdateUnheldItemsHUDDisplayRange(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -72,6 +86,22 @@ namespace RTE { m_GameplaySettingsBox->SetFocus(); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void SettingsGameplayGUI::UpdateUnheldItemsHUDDisplayRange() { + int newValue = m_UnheldItemsHUDDisplayRangeSlider->GetValue(); + if (newValue < 3) { + m_UnheldItemsHUDDisplayRangeLabel->SetText("Hidden"); + g_SettingsMan.SetUnheldItemsHUDDisplayRange(-1.0F); + } else if (newValue > 50) { + m_UnheldItemsHUDDisplayRangeLabel->SetText("Unlimited"); + g_SettingsMan.SetUnheldItemsHUDDisplayRange(0); + } else { + m_UnheldItemsHUDDisplayRangeLabel->SetText("Up to " + std::to_string(newValue) + " meters"); + g_SettingsMan.SetUnheldItemsHUDDisplayRange(static_cast(newValue) * c_PPM); + } + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SettingsGameplayGUI::HandleInputEvents(GUIEvent &guiEvent) { @@ -92,6 +122,8 @@ namespace RTE { UpdateMaxUnheldItemsTextbox(); } else if (guiEvent.GetControl() == m_CrabBombThresholdTextbox && guiEvent.GetMsg() == GUITextBox::Enter) { UpdateCrabBombThresholdTextbox(); + } else if (guiEvent.GetControl() == m_UnheldItemsHUDDisplayRangeSlider) { + UpdateUnheldItemsHUDDisplayRange(); // Update both textboxes when clicking the main CollectionBox, otherwise clicking off focused textboxes does not remove their focus or update the setting values and they will still capture keyboard input. } else if (guiEvent.GetControl() == m_GameplaySettingsBox && guiEvent.GetMsg() == GUICollectionBox::Clicked && !m_GameplaySettingsBox->HasFocus()) { UpdateMaxUnheldItemsTextbox(); diff --git a/Menus/SettingsGameplayGUI.h b/Menus/SettingsGameplayGUI.h index cc6ec0503..42116cb63 100644 --- a/Menus/SettingsGameplayGUI.h +++ b/Menus/SettingsGameplayGUI.h @@ -7,6 +7,8 @@ namespace RTE { class GUICollectionBox; class GUICheckbox; class GUITextBox; + class GUISlider; + class GUILabel; class GUIEvent; /// @@ -54,6 +56,8 @@ namespace RTE { GUICheckbox *m_ShowEnemyHUDCheckbox; GUITextBox *m_MaxUnheldItemsTextbox; GUITextBox *m_CrabBombThresholdTextbox; + GUISlider *m_UnheldItemsHUDDisplayRangeSlider; + GUILabel *m_UnheldItemsHUDDisplayRangeLabel; #pragma region Gameplay Settings Handling /// @@ -65,6 +69,11 @@ namespace RTE { /// Updates the CrabBombThreshold textbox to override any invalid input, applies the setting value and removes its focus. /// void UpdateCrabBombThresholdTextbox(); + + /// + /// Updates the UnheldItemsHUDDisplayRange setting and label according to the slider value. + /// + void UpdateUnheldItemsHUDDisplayRange(); #pragma endregion // Disallow the use of some implicit methods. From 1670777d381f01db690a42d41d9a5a8fb18dc3b7 Mon Sep 17 00:00:00 2001 From: Gareth YR Date: Sun, 12 Sep 2021 12:24:31 -0300 Subject: [PATCH 46/72] Changes to inventory menu to support 4zK's changes that allow not always having equipped items Rewrote swapping inventory and equipped item stuff to be generally better Made Actor::SetInventoryItemAtIndex call RemoveInventoryItemAtIndex if there's no newInventoryItem --- Entities/Actor.cpp | 19 ++++++----- Menus/InventoryMenuGUI.cpp | 67 ++++++++++++++++++-------------------- Menus/InventoryMenuGUI.h | 7 ++-- 3 files changed, 45 insertions(+), 48 deletions(-) diff --git a/Entities/Actor.cpp b/Entities/Actor.cpp index e01f2bf5a..0862919cf 100644 --- a/Entities/Actor.cpp +++ b/Entities/Actor.cpp @@ -866,16 +866,17 @@ bool Actor::SwapInventoryItemsByIndex(int inventoryIndex1, int inventoryIndex2) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// MovableObject * Actor::SetInventoryItemAtIndex(MovableObject *newInventoryItem, int inventoryIndex) { - if (newInventoryItem) { - if (inventoryIndex < 0 || inventoryIndex >= m_Inventory.size()) { - m_Inventory.emplace_back(newInventoryItem); - return nullptr; - } - MovableObject *currentInventoryItemAtIndex = m_Inventory.at(inventoryIndex); - m_Inventory.at(inventoryIndex) = newInventoryItem; - return currentInventoryItemAtIndex; + if (!newInventoryItem) { + return RemoveInventoryItemAtIndex(inventoryIndex); } - return nullptr; + + if (inventoryIndex < 0 || inventoryIndex >= m_Inventory.size()) { + m_Inventory.emplace_back(newInventoryItem); + return nullptr; + } + MovableObject *currentInventoryItemAtIndex = m_Inventory.at(inventoryIndex); + m_Inventory.at(inventoryIndex) = newInventoryItem; + return currentInventoryItemAtIndex; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Menus/InventoryMenuGUI.cpp b/Menus/InventoryMenuGUI.cpp index ab242a5f9..0942c06c5 100644 --- a/Menus/InventoryMenuGUI.cpp +++ b/Menus/InventoryMenuGUI.cpp @@ -781,18 +781,10 @@ namespace RTE { g_GUISound.ItemChangeSound()->Play(m_MenuController->GetPlayer()); m_GUIInformationToggleButton->OnLoseFocus(); } else if (guiControl == m_GUIEquippedItemButton) { - if (m_InventoryActorEquippedItems.empty()) { - if (!buttonHeld) { g_GUISound.UserErrorSound()->Play(m_MenuController->GetPlayer()); } - } else { - HandleItemButtonPressOrHold(m_GUIEquippedItemButton, m_InventoryActorEquippedItems.at(m_GUIInventoryActorCurrentEquipmentSetIndex).first, 0, buttonHeld); - } + HandleItemButtonPressOrHold(m_GUIEquippedItemButton, m_InventoryActorEquippedItems.empty() ? nullptr : m_InventoryActorEquippedItems.at(m_GUIInventoryActorCurrentEquipmentSetIndex).first, 0, buttonHeld); } else if (guiControl == m_GUIOffhandEquippedItemButton) { - if (m_InventoryActorEquippedItems.empty()) { - if (!buttonHeld) { g_GUISound.UserErrorSound()->Play(m_MenuController->GetPlayer()); } - } else { - HandleItemButtonPressOrHold(m_GUIOffhandEquippedItemButton, m_InventoryActorEquippedItems.at(m_GUIInventoryActorCurrentEquipmentSetIndex).second, 1, buttonHeld); - m_GUIOffhandEquippedItemButton->OnMouseLeave(0, 0, 0, 0); - } + HandleItemButtonPressOrHold(m_GUIOffhandEquippedItemButton, m_InventoryActorEquippedItems.empty() ? nullptr : m_InventoryActorEquippedItems.at(m_GUIInventoryActorCurrentEquipmentSetIndex).second, 1, buttonHeld); + m_GUIOffhandEquippedItemButton->OnMouseLeave(0, 0, 0, 0); } else if (!buttonHeld && guiControl == m_GUIReloadButton) { ReloadSelectedItem(); } else if (!buttonHeld && guiControl == m_GUIDropButton) { @@ -1186,11 +1178,11 @@ namespace RTE { g_GUISound.UserErrorSound()->Play(m_MenuController->GetPlayer()); } } else { - SwapEquippedItemAndInventoryItem(m_GUISelectedItem->Object, pressedButtonItemIndex); + SwapEquippedItemAndInventoryItem(m_GUISelectedItem->EquippedItemIndex, pressedButtonItemIndex); } } else { if (buttonEquippedItemIndex > -1) { - SwapEquippedItemAndInventoryItem(buttonObject, m_GUISelectedItem->InventoryIndex); + SwapEquippedItemAndInventoryItem(pressedButtonItemIndex, m_GUISelectedItem->InventoryIndex); } else { if (pressedButtonItemIndex >= m_InventoryActor->GetInventorySize()) { m_InventoryActor->AddInventoryItem(m_InventoryActor->RemoveInventoryItemAtIndex(m_GUISelectedItem->InventoryIndex)); @@ -1207,32 +1199,35 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void InventoryMenuGUI::SwapEquippedItemAndInventoryItem(MovableObject *equippedItemToSwapOut, int inventoryItemIndexToSwapIn) { - MovableObject *equippedItem = m_GUIInventoryActorCurrentEquipmentSetIndex < m_InventoryActorEquippedItems.size() ? m_InventoryActorEquippedItems.at(m_GUIInventoryActorCurrentEquipmentSetIndex).first : nullptr; - MovableObject *offhandEquippedItem = m_GUIInventoryActorCurrentEquipmentSetIndex < m_InventoryActorEquippedItems.size() ? m_InventoryActorEquippedItems.at(m_GUIInventoryActorCurrentEquipmentSetIndex).second : nullptr; - bool addOffhandItemToInventory = false; - - if (offhandEquippedItem && inventoryItemIndexToSwapIn >= 0 && inventoryItemIndexToSwapIn < m_InventoryActor->GetInventorySize()) { - const HeldDevice *inventoryItemToSwapIn = dynamic_cast(m_InventoryActor->GetInventory()->at(inventoryItemIndexToSwapIn)); - if (!inventoryItemToSwapIn->IsOneHanded() && !inventoryItemToSwapIn->HasObjectInGroup("Shields")) { - if (equippedItem) { - equippedItemToSwapOut = equippedItem; - addOffhandItemToInventory = true; - } else if (m_InventoryActorIsHuman && !dynamic_cast(m_InventoryActor)->GetFGArm()) { - equippedItemToSwapOut = nullptr; - } - } + void InventoryMenuGUI::SwapEquippedItemAndInventoryItem(int equippedItemIndex, int inventoryItemIndex) { + if (!m_InventoryActorIsHuman) { + g_GUISound.UserErrorSound()->Play(m_MenuController->GetPlayer()); + return; } - if (equippedItemToSwapOut) { - Arm *equippedItemArm = dynamic_cast(equippedItemToSwapOut->GetParent()); - equippedItemArm->SetHeldMO(m_InventoryActor->SetInventoryItemAtIndex(equippedItemArm->ReleaseHeldMO(), inventoryItemIndexToSwapIn)); - equippedItemArm->SetHandPos(m_InventoryActor->GetPos() + m_InventoryActor->GetHolsterOffset().GetXFlipped(m_InventoryActor->IsHFlipped())); - if (addOffhandItemToInventory) { m_InventoryActor->AddInventoryItem(dynamic_cast(offhandEquippedItem->GetParent())->ReleaseHeldMO()); } - m_InventoryActor->GetDeviceSwitchSound()->Play(m_MenuController->GetPlayer()); - } else { + AHuman *inventoryActorAsAHuman = dynamic_cast(m_InventoryActor); + MovableObject *equippedItem = m_GUIInventoryActorCurrentEquipmentSetIndex < m_InventoryActorEquippedItems.size() && !m_InventoryActorEquippedItems.empty() ? m_InventoryActorEquippedItems.at(m_GUIInventoryActorCurrentEquipmentSetIndex).first : nullptr; + MovableObject *offhandEquippedItem = m_GUIInventoryActorCurrentEquipmentSetIndex < m_InventoryActorEquippedItems.size() && !m_InventoryActorEquippedItems.empty() ? m_InventoryActorEquippedItems.at(m_GUIInventoryActorCurrentEquipmentSetIndex).second : nullptr; + + const HeldDevice *inventoryItemToSwapIn = inventoryItemIndex < m_InventoryActor->GetInventorySize() ? dynamic_cast(m_InventoryActor->GetInventory()->at(inventoryItemIndex)) : nullptr; + bool inventoryItemCanGoInOffhand = !inventoryItemToSwapIn || inventoryItemToSwapIn->IsOneHanded() || inventoryItemToSwapIn->HasObjectInGroup("Shields"); + + equippedItemIndex = !inventoryItemCanGoInOffhand || !inventoryActorAsAHuman->GetBGArm() ? 0 : equippedItemIndex; + MovableObject *equippedItemToSwapOut = equippedItemIndex == 0 ? equippedItem : offhandEquippedItem; + + if (equippedItemIndex == 0 && !inventoryActorAsAHuman->GetFGArm()) { g_GUISound.UserErrorSound()->Play(m_MenuController->GetPlayer()); + return; } + + Arm *equippedItemArm = equippedItemIndex == 0 ? inventoryActorAsAHuman->GetFGArm() : inventoryActorAsAHuman->GetBGArm(); + equippedItemArm->SetHeldMO(m_InventoryActor->SetInventoryItemAtIndex(equippedItemArm->ReleaseHeldMO(), inventoryItemIndex)); + equippedItemArm->SetHandPos(m_InventoryActor->GetPos() + m_InventoryActor->GetHolsterOffset().GetXFlipped(m_InventoryActor->IsHFlipped())); + if (!inventoryItemCanGoInOffhand && offhandEquippedItem) { + m_InventoryActor->AddInventoryItem(inventoryActorAsAHuman->GetBGArm()->ReleaseHeldMO()); + inventoryActorAsAHuman->GetBGArm()->SetHandPos(m_InventoryActor->GetPos() + m_InventoryActor->GetHolsterOffset().GetXFlipped(m_InventoryActor->IsHFlipped())); + } + m_InventoryActor->GetDeviceSwitchSound()->Play(m_MenuController->GetPlayer()); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1247,7 +1242,7 @@ namespace RTE { } else if (HDFirearm *selectedItemObjectAsFirearm = dynamic_cast(m_GUISelectedItem->Object)) { if (m_GUISelectedItem->InventoryIndex > -1) { if (!m_InventoryActorEquippedItems.empty() && m_GUIInventoryActorCurrentEquipmentSetIndex < m_InventoryActorEquippedItems.size()) { - SwapEquippedItemAndInventoryItem(m_InventoryActorEquippedItems.at(m_GUIInventoryActorCurrentEquipmentSetIndex).first, m_GUISelectedItem->InventoryIndex); + SwapEquippedItemAndInventoryItem(0, m_GUISelectedItem->InventoryIndex); } else if (inventoryActorAsAHuman->GetFGArm() || inventoryActorAsAHuman->GetBGArm()) { Arm *armToUse = inventoryActorAsAHuman->GetFGArm() ? inventoryActorAsAHuman->GetFGArm() : inventoryActorAsAHuman->GetBGArm(); armToUse->SetHeldMO(m_InventoryActor->RemoveInventoryItemAtIndex(m_GUISelectedItem->InventoryIndex)); diff --git a/Menus/InventoryMenuGUI.h b/Menus/InventoryMenuGUI.h index f1f855ad4..1e762a7df 100644 --- a/Menus/InventoryMenuGUI.h +++ b/Menus/InventoryMenuGUI.h @@ -437,11 +437,12 @@ namespace RTE { //void SwapEquippedItemSet() {} /// - /// Swaps an equipped item with one in the inventory Actor's inventory. + /// Swaps the equipped item at the given equipped item index with one in the inventory Actor's inventory at the given inventory item index. + /// Accounts for either index pointing to empty buttons and any other potential complications. /// - /// A pointer to the equipped item being swapped out. + /// The index of the equipped item being swapped out. /// The index in the inventory of the item being swapped in. - void SwapEquippedItemAndInventoryItem(MovableObject *equippedItemToSwapOut, int inventoryItemIndexToSwapIn); + void SwapEquippedItemAndInventoryItem(int equippedItemIndex, int inventoryItemIndex); /// /// Reloads the selected item if it is equipped, or swaps to it and then reloads it if it isn't. From b764b1b5b14014d6db037c58514c7190323cc0bc Mon Sep 17 00:00:00 2001 From: Gareth YR Date: Tue, 14 Sep 2021 17:27:19 -0300 Subject: [PATCH 47/72] Moved lua bindings into LuaBindingsEntities --- Lua/LuaBindingsEntities.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lua/LuaBindingsEntities.cpp b/Lua/LuaBindingsEntities.cpp index 9cb6c0d84..a374804cc 100644 --- a/Lua/LuaBindingsEntities.cpp +++ b/Lua/LuaBindingsEntities.cpp @@ -364,6 +364,8 @@ namespace RTE { .property("EmitAngle", &AEmitter::GetEmitAngle, &AEmitter::SetEmitAngle) .property("GetThrottle", &AEmitter::GetThrottle, &AEmitter::SetThrottle) .property("Throttle", &AEmitter::GetThrottle, &AEmitter::SetThrottle) + .property("MinThrottleRangeTODORENAME", &AEmitter::GetMinThrottleRange, &AEmitter::SetMinThrottleRange) + .property("MaxThrottleRangeTODORENAME", &AEmitter::GetMaxThrottleRange, &AEmitter::SetMaxThrottleRange) .property("BurstSpacing", &AEmitter::GetBurstSpacing, &AEmitter::SetBurstSpacing) .property("BurstDamage", &AEmitter::GetBurstDamage, &AEmitter::SetBurstDamage) .property("EmitterDamageMultiplier", &AEmitter::GetEmitterDamageMultiplier, &AEmitter::SetEmitterDamageMultiplier) From 19fb486c7bfc518c21c9162260f53da4f125c020 Mon Sep 17 00:00:00 2001 From: Gareth YR Date: Tue, 14 Sep 2021 17:28:29 -0300 Subject: [PATCH 48/72] Undid empty line in luaman.cpp --- Managers/LuaMan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Managers/LuaMan.cpp b/Managers/LuaMan.cpp index 425971e03..d32a8cd25 100644 --- a/Managers/LuaMan.cpp +++ b/Managers/LuaMan.cpp @@ -461,4 +461,4 @@ namespace RTE { void LuaMan::Update() const { lua_gc(m_MasterState, LUA_GCSTEP, 1); } -} +} \ No newline at end of file From 1164d3f8ac31090c28c0d9578dbf5e50d0e59dbd Mon Sep 17 00:00:00 2001 From: fourZK Date: Sun, 19 Sep 2021 21:55:27 +0300 Subject: [PATCH 49/72] Changed throttle logic and renamed `Min/MaxThrottleRange` as `Negative/PositiveThrottleMultiplier` --- Entities/AEmitter.cpp | 45 ++++++++++++++++++---------------- Entities/AEmitter.h | 17 ++++++------- Entities/PEmitter.cpp | 56 +++++++++++++++++++++---------------------- Entities/PEmitter.h | 7 +++--- 4 files changed, 64 insertions(+), 61 deletions(-) diff --git a/Entities/AEmitter.cpp b/Entities/AEmitter.cpp index 8ca54bba6..c38ad07aa 100644 --- a/Entities/AEmitter.cpp +++ b/Entities/AEmitter.cpp @@ -38,8 +38,8 @@ void AEmitter::Clear() m_WasEmitting = false; m_EmitCount = 0; m_EmitCountLimit = 0; - m_MinThrottleRange = 1.0F; - m_MaxThrottleRange = 1.0F; + m_NegativeThrottleMultiplier = 1.0F; + m_PositiveThrottleMultiplier = 1.0F; m_Throttle = 0; m_EmissionsIgnoreThis = false; m_BurstScale = 1.0F; @@ -85,8 +85,8 @@ int AEmitter::Create(const AEmitter &reference) { m_EmitEnabled = reference.m_EmitEnabled; m_EmitCount = reference.m_EmitCount; m_EmitCountLimit = reference.m_EmitCountLimit; - m_MinThrottleRange = reference.m_MinThrottleRange; - m_MaxThrottleRange = reference.m_MaxThrottleRange; + m_NegativeThrottleMultiplier = reference.m_NegativeThrottleMultiplier; + m_PositiveThrottleMultiplier = reference.m_PositiveThrottleMultiplier; m_Throttle = reference.m_Throttle; m_EmissionsIgnoreThis = reference.m_EmissionsIgnoreThis; m_BurstScale = reference.m_BurstScale; @@ -138,10 +138,10 @@ int AEmitter::ReadProperty(const std::string_view &propName, Reader &reader) { reader >> ppm; // Go through all emissions and set the rate so that it emulates the way it used to work, for mod backwards compatibility. for (Emission *emission : m_EmissionList) { emission->m_PPM = ppm / static_cast(m_EmissionList.size()); } - } else if (propName == "MinThrottleRange") { - reader >> m_MinThrottleRange; - } else if (propName == "MaxThrottleRange") { - reader >> m_MaxThrottleRange; + } else if (propName == "NegativeThrottleMultiplier") { + reader >> m_NegativeThrottleMultiplier; + } else if (propName == "PositiveThrottleMultiplier") { + reader >> m_PositiveThrottleMultiplier; } else if (propName == "Throttle") { reader >> m_Throttle; } else if (propName == "EmissionsIgnoreThis") { @@ -212,10 +212,10 @@ int AEmitter::Save(Writer &writer) const writer << m_EmitCountLimit; writer.NewProperty("EmissionsIgnoreThis"); writer << m_EmissionsIgnoreThis; - writer.NewProperty("MinThrottleRange"); - writer << m_MinThrottleRange; - writer.NewProperty("MaxThrottleRange"); - writer << m_MaxThrottleRange; + writer.NewProperty("NegativeThrottleMultiplier"); + writer << m_NegativeThrottleMultiplier; + writer.NewProperty("PositiveThrottleMultiplier"); + writer << m_PositiveThrottleMultiplier; writer.NewProperty("Throttle"); writer << m_Throttle; writer.NewProperty("BurstScale"); @@ -344,12 +344,13 @@ float AEmitter::EstimateImpulse(bool burst) } - // Figure out the throttle factor + // Scale the emission rate up or down according to the appropriate throttle multiplier. float throttleFactor = 1.0F; - if (m_Throttle < 0) { // Negative throttle, scale down according to the min throttle range - throttleFactor += std::abs(m_MinThrottleRange) * m_Throttle; - } else if (m_Throttle > 0) { // Positive throttle, scale up - throttleFactor += std::abs(m_MaxThrottleRange) * m_Throttle; + float absThrottle = std::abs(m_Throttle); + if (m_Throttle < 0) { + throttleFactor = throttleFactor * (1 - absThrottle) + (m_NegativeThrottleMultiplier * absThrottle); + } else if (m_Throttle > 0) { + throttleFactor = throttleFactor * (1 - absThrottle) + (m_PositiveThrottleMultiplier * absThrottle); } // Apply the throttle factor to the emission rate per update if (burst) { return m_AvgBurstImpulse * throttleFactor; } @@ -432,11 +433,13 @@ void AEmitter::Update() // TODO: Potentially get this once outside instead, like in attach/detach") MovableObject *pRootParent = GetRootParent(); + // Scale the emission rate up or down according to the appropriate throttle multiplier. float throttleFactor = 1.0F; - if (m_Throttle < 0) { // Negative throttle, scale down according to the min throttle range - throttleFactor += std::abs(m_MinThrottleRange) * m_Throttle; - } else if (m_Throttle > 0) { // Positive throttle, scale up - throttleFactor += std::abs(m_MaxThrottleRange) * m_Throttle; + float absThrottle = std::abs(m_Throttle); + if (m_Throttle < 0) { + throttleFactor = throttleFactor * (1 - absThrottle) + (m_NegativeThrottleMultiplier * absThrottle); + } else if (m_Throttle > 0) { + throttleFactor = throttleFactor * (1 - absThrottle) + (m_PositiveThrottleMultiplier * absThrottle); } // Check burst triggering against whether the spacing is fulfilled if (m_BurstTriggered && (m_BurstSpacing <= 0 || m_BurstTimer.IsPastSimMS(m_BurstSpacing))) diff --git a/Entities/AEmitter.h b/Entities/AEmitter.h index be4d8d25f..ec048cbbf 100644 --- a/Entities/AEmitter.h +++ b/Entities/AEmitter.h @@ -263,16 +263,16 @@ ClassInfoGetters; float GetThrottle() const { return m_Throttle; } /// - /// Gets the minimum throttle range of this AEmitter. + /// Gets the negative throttle multiplier of this AEmitter. /// - /// The minimum throttle range of this AEmitter. - float GetMinThrottle() const { return m_MinThrottleRange; } + /// The negative throttle multiplier of this AEmitter. + float GetNegativeThrottleMultiplier() const { return m_NegativeThrottleMultiplier; } /// - /// Gets the maximum throttle range of this AEmitter. + /// Gets the positive throttle multiplier of this AEmitter. /// - /// The maximum throttle range of this AEmitter. - float GetMaxThrottle() const { return m_MaxThrottleRange; } + /// The positive throttle multiplier of this AEmitter. + float GetPositiveThrottleMultiplier() const { return m_PositiveThrottleMultiplier; } /* ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetEmitRate @@ -632,8 +632,9 @@ ClassInfoGetters; long m_EmitCount; // The max number of emissions to emit per emit being enabled long m_EmitCountLimit; - float m_MinThrottleRange; //!< The range negative throttle has on emission rate. 1.0 means the rate can be throttled down to 0%, 0 means negative throttle has no effect - float m_MaxThrottleRange; //!< The range positive throttle has on emission rate. 1.0 means the rate can be throttled up to 200%, 0 means positive throttle has no effect + // TODO: explain this property more clearly + float m_NegativeThrottleMultiplier; //!< The multiplier applied to emission rate relative to negative throttle. 1.0 means no change in rate, 0 means negative throttle will scale down to zero. + float m_PositiveThrottleMultiplier; //!< DESCRIPTION PENDING float m_Throttle; //!< The normalized throttle which controls the MSPE between 1.0 * m_MSPERange and -1.0 * m_MSPERange. 0 means emit the regular m_PPM amount. // Whether or not this' emissions ignore hits with itself, even if they are set to hit other MOs. bool m_EmissionsIgnoreThis; diff --git a/Entities/PEmitter.cpp b/Entities/PEmitter.cpp index 8b70706ab..055cb7eec 100644 --- a/Entities/PEmitter.cpp +++ b/Entities/PEmitter.cpp @@ -37,8 +37,8 @@ namespace RTE { m_WasEmitting = false; m_EmitCount = 0; m_EmitCountLimit = 0; - m_MinThrottleRange = -1; - m_MaxThrottleRange = 1; + m_NegativeThrottleMultiplier = 1.0F; + m_PositiveThrottleMultiplier = 1.0F; m_Throttle = 0; m_EmissionsIgnoreThis = false; m_BurstScale = 1.0; @@ -90,8 +90,8 @@ namespace RTE { m_EmitEnabled = reference.m_EmitEnabled; m_EmitCount = reference.m_EmitCount; m_EmitCountLimit = reference.m_EmitCountLimit; - m_MinThrottleRange = reference.m_MinThrottleRange; - m_MaxThrottleRange = reference.m_MaxThrottleRange; + m_NegativeThrottleMultiplier = reference.m_NegativeThrottleMultiplier; + m_PositiveThrottleMultiplier = reference.m_PositiveThrottleMultiplier; m_Throttle = reference.m_Throttle; m_EmissionsIgnoreThis = reference.m_EmissionsIgnoreThis; m_BurstScale = reference.m_BurstScale; @@ -143,10 +143,10 @@ namespace RTE { for (list::iterator eItr = m_EmissionList.begin(); eItr != m_EmissionList.end(); ++eItr) (*eItr).m_PPM = ppm / m_EmissionList.size(); } - else if (propName == "MinThrottleRange") - reader >> m_MinThrottleRange; - else if (propName == "MaxThrottleRange") - reader >> m_MaxThrottleRange; + else if (propName == "NegativeThrottleMultiplier") + reader >> m_NegativeThrottleMultiplier; + else if (propName == "PositiveThrottleMultiplier") + reader >> m_PositiveThrottleMultiplier; else if (propName == "Throttle") reader >> m_Throttle; else if (propName == "EmissionsIgnoreThis") @@ -213,10 +213,10 @@ namespace RTE { writer << m_EmitCountLimit; writer.NewProperty("EmissionsIgnoreThis"); writer << m_EmissionsIgnoreThis; - writer.NewProperty("MinThrottleRange"); - writer << m_MinThrottleRange; - writer.NewProperty("MaxThrottleRange"); - writer << m_MaxThrottleRange; + writer.NewProperty("NegativeThrottleMultiplier"); + writer << m_NegativeThrottleMultiplier; + writer.NewProperty("PositiveThrottleMultiplier"); + writer << m_PositiveThrottleMultiplier; writer.NewProperty("Throttle"); writer << m_Throttle; writer.NewProperty("BurstScale"); @@ -339,13 +339,14 @@ namespace RTE { } - // Figure out the throttle factor - float throttleFactor = 1.0f; - if (m_Throttle < 0) // Negative throttle, scale down according to the min throttle range - throttleFactor += fabs(m_MinThrottleRange) * m_Throttle; - else if (m_Throttle > 0) // Positive throttle, scale up - throttleFactor += fabs(m_MaxThrottleRange) * m_Throttle; - + // Scale the emission rate up or down according to the appropriate throttle multiplier. + float throttleFactor = 1.0F; + float absThrottle = std::abs(m_Throttle); + if (m_Throttle < 0) { + throttleFactor = throttleFactor * (1 - absThrottle) + (m_NegativeThrottleMultiplier * absThrottle); + } else if (m_Throttle > 0) { + throttleFactor = throttleFactor * (1 - absThrottle) + (m_PositiveThrottleMultiplier * absThrottle); + } // Apply the throttle factor to the emission rate per update if (burst) return m_AvgBurstImpulse * throttleFactor; @@ -397,15 +398,14 @@ namespace RTE { // TODO: Potentially get this once outside instead, like in attach/detach") MovableObject *pRootParent = GetRootParent(); - // Figure out the throttle factor - // Negative throttle, scale down according to the min throttle range - float throttleFactor = 1.0f; - if (m_Throttle < 0) - throttleFactor += fabs(m_MinThrottleRange) * m_Throttle; - // Positive throttle, scale up - else if (m_Throttle > 0) - throttleFactor += fabs(m_MaxThrottleRange) * m_Throttle; - + // Scale the emission rate up or down according to the appropriate throttle multiplier. + float throttleFactor = 1.0F; + float absThrottle = std::abs(m_Throttle); + if (m_Throttle < 0) { + throttleFactor = throttleFactor * (1 - absThrottle) + (m_NegativeThrottleMultiplier * absThrottle); + } else if (m_Throttle > 0) { + throttleFactor = throttleFactor * (1 - absThrottle) + (m_PositiveThrottleMultiplier * absThrottle); + } // Check burst triggering against whether the spacing is fulfilled if (m_BurstTriggered && (m_BurstSpacing <= 0 || m_BurstTimer.IsPastSimMS(m_BurstSpacing))) { diff --git a/Entities/PEmitter.h b/Entities/PEmitter.h index 3c9c8d07a..09f8832cf 100644 --- a/Entities/PEmitter.h +++ b/Entities/PEmitter.h @@ -506,10 +506,9 @@ class PEmitter : long m_EmitCount; // The max number of emissions to emit per emit being enabled long m_EmitCountLimit; - // The range negative throttle has on emission rate. 1.0 means the rate can be throttled down to 0%, 0 means negative throttle has no effect - double m_MinThrottleRange; - // The range positive throttle has on emission rate. 1.0 means the rate can be throttled up to 200%, 0 means negative throttle has no effect - double m_MaxThrottleRange; + // TODO: explain this property more clearly + float m_NegativeThrottleMultiplier; //!< The multiplier applied to emission rate relative to negative throttle. 1.0 means no change in rate, 0 means negative throttle will scale down to zero. + float m_PositiveThrottleMultiplier; //!< DESCRIPTION PENDING // The normalized throttle which controls the MSPE between 1.0 * m_MSPERange and -1.0 * m_MSPERange. 0 means emit the regular m_PPM amount. float m_Throttle; // Whether or not this' emissions ignore hits with itself, even if they are set to hit other MOs. From bb4ee03933153b718164af0392e36d7eb03ab12f Mon Sep 17 00:00:00 2001 From: fourZK Date: Sun, 19 Sep 2021 21:58:23 +0300 Subject: [PATCH 50/72] Moved `FlashScale` treatment from jetpack logic to AEmitter --- Entities/ACrab.cpp | 2 -- Entities/AEmitter.cpp | 1 + Entities/AHuman.cpp | 2 -- Entities/PEmitter.cpp | 1 + 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Entities/ACrab.cpp b/Entities/ACrab.cpp index b63701a52..904af37b7 100644 --- a/Entities/ACrab.cpp +++ b/Entities/ACrab.cpp @@ -2143,8 +2143,6 @@ void ACrab::Update() // Jetpack throttle depletes relative to jet time, but only if throttle range values have been defined float jetTimeRatio = std::max(m_JetTimeLeft / m_JetTimeTotal, 0.0F); m_pJetpack->SetThrottle(jetTimeRatio * 2.0F - 1.0F); - float minScale = 1.0F - m_pJetpack->GetMinThrottle(); - m_pJetpack->SetFlashScale(minScale + (1.0F + m_pJetpack->GetMaxThrottle() - minScale) * jetTimeRatio); } // Start Jetpack burn if (m_Controller.IsState(BODY_JUMPSTART) && m_JetTimeLeft > 0 && m_Status != INACTIVE) diff --git a/Entities/AEmitter.cpp b/Entities/AEmitter.cpp index c38ad07aa..99158fae8 100644 --- a/Entities/AEmitter.cpp +++ b/Entities/AEmitter.cpp @@ -441,6 +441,7 @@ void AEmitter::Update() } else if (m_Throttle > 0) { throttleFactor = throttleFactor * (1 - absThrottle) + (m_PositiveThrottleMultiplier * absThrottle); } + m_FlashScale = throttleFactor; // Check burst triggering against whether the spacing is fulfilled if (m_BurstTriggered && (m_BurstSpacing <= 0 || m_BurstTimer.IsPastSimMS(m_BurstSpacing))) { diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index 00721b401..21e12834c 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -3111,8 +3111,6 @@ void AHuman::Update() // Jetpack throttle depletes relative to jet time, but only if throttle range values have been defined float jetTimeRatio = std::max(m_JetTimeLeft / m_JetTimeTotal, 0.0F); m_pJetpack->SetThrottle(jetTimeRatio * 2.0F - 1.0F); - float minScale = 1.0F - m_pJetpack->GetMinThrottle(); - m_pJetpack->SetFlashScale(minScale + (1.0F + m_pJetpack->GetMaxThrottle() - minScale) * jetTimeRatio); } // Start Jetpack burn if (m_Controller.IsState(BODY_JUMPSTART) && m_JetTimeLeft > 0 && m_Status != INACTIVE) diff --git a/Entities/PEmitter.cpp b/Entities/PEmitter.cpp index 055cb7eec..405658163 100644 --- a/Entities/PEmitter.cpp +++ b/Entities/PEmitter.cpp @@ -406,6 +406,7 @@ namespace RTE { } else if (m_Throttle > 0) { throttleFactor = throttleFactor * (1 - absThrottle) + (m_PositiveThrottleMultiplier * absThrottle); } + m_FlashScale = throttleFactor; // Check burst triggering against whether the spacing is fulfilled if (m_BurstTriggered && (m_BurstSpacing <= 0 || m_BurstTimer.IsPastSimMS(m_BurstSpacing))) { From 70ea9ef711282ce9e00d5d85825bf44335467230 Mon Sep 17 00:00:00 2001 From: fourZK Date: Sun, 19 Sep 2021 22:17:34 +0300 Subject: [PATCH 51/72] Bugfixes --- Activities/GameActivity.cpp | 1 + Entities/ACrab.cpp | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Activities/GameActivity.cpp b/Activities/GameActivity.cpp index ccddf3863..ec342a9cd 100644 --- a/Activities/GameActivity.cpp +++ b/Activities/GameActivity.cpp @@ -2111,6 +2111,7 @@ void GameActivity::Update() m_MessageTimer[player].Reset(); pDeliveryCraft->ResetEmissionTimers(); // Reset the engine timers so they don't emit a massive burst after being added to the world + pDeliveryCraft->ResetAllTimers(); pDeliveryCraft->Update(); // Add the delivery craft to the world, TRANSFERRING OWNERSHIP diff --git a/Entities/ACrab.cpp b/Entities/ACrab.cpp index 904af37b7..2a25242db 100644 --- a/Entities/ACrab.cpp +++ b/Entities/ACrab.cpp @@ -2830,10 +2830,9 @@ void ACrab::DrawHUD(BITMAP *pTargetBitmap, const Vector &targetPos, int whichScr // Player AI drawing - // Device aiming reticle - if (m_Controller.IsState(AIM_SHARP) && m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) - m_pTurret->GetFirstMountedDevice()->DrawHUD(pTargetBitmap, targetPos, whichScreen, m_Controller.IsPlayerControlled()); - + if ((m_Controller.IsState(AIM_SHARP) || (m_Controller.IsPlayerControlled() && !m_Controller.IsState(PIE_MENU_ACTIVE))) && m_pTurret && m_pTurret->IsAttached() && m_pTurret->HasMountedDevice()) { + m_pTurret->GetFirstMountedDevice()->DrawHUD(pTargetBitmap, targetPos, whichScreen, m_Controller.IsState(AIM_SHARP) && m_Controller.IsPlayerControlled()); + } ////////////////////////////////////// // Draw stat info HUD char str[64]; From d679a7aad7bcbe1f670134e93ab08ed11b42e48b Mon Sep 17 00:00:00 2001 From: fourZK Date: Sat, 25 Sep 2021 21:24:59 +0300 Subject: [PATCH 52/72] Revert throttle commit --- Entities/AEmitter.cpp | 45 ++++++++++++++++------------------ Entities/AEmitter.h | 17 ++++++------- Entities/PEmitter.cpp | 57 +++++++++++++++++++++---------------------- Entities/PEmitter.h | 7 +++--- 4 files changed, 61 insertions(+), 65 deletions(-) diff --git a/Entities/AEmitter.cpp b/Entities/AEmitter.cpp index 99158fae8..66c77728b 100644 --- a/Entities/AEmitter.cpp +++ b/Entities/AEmitter.cpp @@ -38,8 +38,8 @@ void AEmitter::Clear() m_WasEmitting = false; m_EmitCount = 0; m_EmitCountLimit = 0; - m_NegativeThrottleMultiplier = 1.0F; - m_PositiveThrottleMultiplier = 1.0F; + m_MinThrottleRange = 1.0F; + m_MaxThrottleRange = 1.0F; m_Throttle = 0; m_EmissionsIgnoreThis = false; m_BurstScale = 1.0F; @@ -85,8 +85,8 @@ int AEmitter::Create(const AEmitter &reference) { m_EmitEnabled = reference.m_EmitEnabled; m_EmitCount = reference.m_EmitCount; m_EmitCountLimit = reference.m_EmitCountLimit; - m_NegativeThrottleMultiplier = reference.m_NegativeThrottleMultiplier; - m_PositiveThrottleMultiplier = reference.m_PositiveThrottleMultiplier; + m_MinThrottleRange = reference.m_MinThrottleRange; + m_MaxThrottleRange = reference.m_MaxThrottleRange; m_Throttle = reference.m_Throttle; m_EmissionsIgnoreThis = reference.m_EmissionsIgnoreThis; m_BurstScale = reference.m_BurstScale; @@ -138,10 +138,10 @@ int AEmitter::ReadProperty(const std::string_view &propName, Reader &reader) { reader >> ppm; // Go through all emissions and set the rate so that it emulates the way it used to work, for mod backwards compatibility. for (Emission *emission : m_EmissionList) { emission->m_PPM = ppm / static_cast(m_EmissionList.size()); } - } else if (propName == "NegativeThrottleMultiplier") { - reader >> m_NegativeThrottleMultiplier; - } else if (propName == "PositiveThrottleMultiplier") { - reader >> m_PositiveThrottleMultiplier; + } else if (propName == "MinThrottleRange") { + reader >> m_MinThrottleRange; + } else if (propName == "MaxThrottleRange") { + reader >> m_MaxThrottleRange; } else if (propName == "Throttle") { reader >> m_Throttle; } else if (propName == "EmissionsIgnoreThis") { @@ -212,10 +212,10 @@ int AEmitter::Save(Writer &writer) const writer << m_EmitCountLimit; writer.NewProperty("EmissionsIgnoreThis"); writer << m_EmissionsIgnoreThis; - writer.NewProperty("NegativeThrottleMultiplier"); - writer << m_NegativeThrottleMultiplier; - writer.NewProperty("PositiveThrottleMultiplier"); - writer << m_PositiveThrottleMultiplier; + writer.NewProperty("MinThrottleRange"); + writer << m_MinThrottleRange; + writer.NewProperty("MaxThrottleRange"); + writer << m_MaxThrottleRange; writer.NewProperty("Throttle"); writer << m_Throttle; writer.NewProperty("BurstScale"); @@ -344,13 +344,12 @@ float AEmitter::EstimateImpulse(bool burst) } - // Scale the emission rate up or down according to the appropriate throttle multiplier. + // Figure out the throttle factor float throttleFactor = 1.0F; - float absThrottle = std::abs(m_Throttle); - if (m_Throttle < 0) { - throttleFactor = throttleFactor * (1 - absThrottle) + (m_NegativeThrottleMultiplier * absThrottle); - } else if (m_Throttle > 0) { - throttleFactor = throttleFactor * (1 - absThrottle) + (m_PositiveThrottleMultiplier * absThrottle); + if (m_Throttle < 0) { // Negative throttle, scale down according to the min throttle range + throttleFactor += std::abs(m_MinThrottleRange) * m_Throttle; + } else if (m_Throttle > 0) { // Positive throttle, scale up + throttleFactor += std::abs(m_MaxThrottleRange) * m_Throttle; } // Apply the throttle factor to the emission rate per update if (burst) { return m_AvgBurstImpulse * throttleFactor; } @@ -433,13 +432,11 @@ void AEmitter::Update() // TODO: Potentially get this once outside instead, like in attach/detach") MovableObject *pRootParent = GetRootParent(); - // Scale the emission rate up or down according to the appropriate throttle multiplier. float throttleFactor = 1.0F; - float absThrottle = std::abs(m_Throttle); - if (m_Throttle < 0) { - throttleFactor = throttleFactor * (1 - absThrottle) + (m_NegativeThrottleMultiplier * absThrottle); - } else if (m_Throttle > 0) { - throttleFactor = throttleFactor * (1 - absThrottle) + (m_PositiveThrottleMultiplier * absThrottle); + if (m_Throttle < 0) { // Negative throttle, scale down according to the min throttle range + throttleFactor += std::abs(m_MinThrottleRange) * m_Throttle; + } else if (m_Throttle > 0) { // Positive throttle, scale up + throttleFactor += std::abs(m_MaxThrottleRange) * m_Throttle; } m_FlashScale = throttleFactor; // Check burst triggering against whether the spacing is fulfilled diff --git a/Entities/AEmitter.h b/Entities/AEmitter.h index ec048cbbf..be4d8d25f 100644 --- a/Entities/AEmitter.h +++ b/Entities/AEmitter.h @@ -263,16 +263,16 @@ ClassInfoGetters; float GetThrottle() const { return m_Throttle; } /// - /// Gets the negative throttle multiplier of this AEmitter. + /// Gets the minimum throttle range of this AEmitter. /// - /// The negative throttle multiplier of this AEmitter. - float GetNegativeThrottleMultiplier() const { return m_NegativeThrottleMultiplier; } + /// The minimum throttle range of this AEmitter. + float GetMinThrottle() const { return m_MinThrottleRange; } /// - /// Gets the positive throttle multiplier of this AEmitter. + /// Gets the maximum throttle range of this AEmitter. /// - /// The positive throttle multiplier of this AEmitter. - float GetPositiveThrottleMultiplier() const { return m_PositiveThrottleMultiplier; } + /// The maximum throttle range of this AEmitter. + float GetMaxThrottle() const { return m_MaxThrottleRange; } /* ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetEmitRate @@ -632,9 +632,8 @@ ClassInfoGetters; long m_EmitCount; // The max number of emissions to emit per emit being enabled long m_EmitCountLimit; - // TODO: explain this property more clearly - float m_NegativeThrottleMultiplier; //!< The multiplier applied to emission rate relative to negative throttle. 1.0 means no change in rate, 0 means negative throttle will scale down to zero. - float m_PositiveThrottleMultiplier; //!< DESCRIPTION PENDING + float m_MinThrottleRange; //!< The range negative throttle has on emission rate. 1.0 means the rate can be throttled down to 0%, 0 means negative throttle has no effect + float m_MaxThrottleRange; //!< The range positive throttle has on emission rate. 1.0 means the rate can be throttled up to 200%, 0 means positive throttle has no effect float m_Throttle; //!< The normalized throttle which controls the MSPE between 1.0 * m_MSPERange and -1.0 * m_MSPERange. 0 means emit the regular m_PPM amount. // Whether or not this' emissions ignore hits with itself, even if they are set to hit other MOs. bool m_EmissionsIgnoreThis; diff --git a/Entities/PEmitter.cpp b/Entities/PEmitter.cpp index 405658163..8b70706ab 100644 --- a/Entities/PEmitter.cpp +++ b/Entities/PEmitter.cpp @@ -37,8 +37,8 @@ namespace RTE { m_WasEmitting = false; m_EmitCount = 0; m_EmitCountLimit = 0; - m_NegativeThrottleMultiplier = 1.0F; - m_PositiveThrottleMultiplier = 1.0F; + m_MinThrottleRange = -1; + m_MaxThrottleRange = 1; m_Throttle = 0; m_EmissionsIgnoreThis = false; m_BurstScale = 1.0; @@ -90,8 +90,8 @@ namespace RTE { m_EmitEnabled = reference.m_EmitEnabled; m_EmitCount = reference.m_EmitCount; m_EmitCountLimit = reference.m_EmitCountLimit; - m_NegativeThrottleMultiplier = reference.m_NegativeThrottleMultiplier; - m_PositiveThrottleMultiplier = reference.m_PositiveThrottleMultiplier; + m_MinThrottleRange = reference.m_MinThrottleRange; + m_MaxThrottleRange = reference.m_MaxThrottleRange; m_Throttle = reference.m_Throttle; m_EmissionsIgnoreThis = reference.m_EmissionsIgnoreThis; m_BurstScale = reference.m_BurstScale; @@ -143,10 +143,10 @@ namespace RTE { for (list::iterator eItr = m_EmissionList.begin(); eItr != m_EmissionList.end(); ++eItr) (*eItr).m_PPM = ppm / m_EmissionList.size(); } - else if (propName == "NegativeThrottleMultiplier") - reader >> m_NegativeThrottleMultiplier; - else if (propName == "PositiveThrottleMultiplier") - reader >> m_PositiveThrottleMultiplier; + else if (propName == "MinThrottleRange") + reader >> m_MinThrottleRange; + else if (propName == "MaxThrottleRange") + reader >> m_MaxThrottleRange; else if (propName == "Throttle") reader >> m_Throttle; else if (propName == "EmissionsIgnoreThis") @@ -213,10 +213,10 @@ namespace RTE { writer << m_EmitCountLimit; writer.NewProperty("EmissionsIgnoreThis"); writer << m_EmissionsIgnoreThis; - writer.NewProperty("NegativeThrottleMultiplier"); - writer << m_NegativeThrottleMultiplier; - writer.NewProperty("PositiveThrottleMultiplier"); - writer << m_PositiveThrottleMultiplier; + writer.NewProperty("MinThrottleRange"); + writer << m_MinThrottleRange; + writer.NewProperty("MaxThrottleRange"); + writer << m_MaxThrottleRange; writer.NewProperty("Throttle"); writer << m_Throttle; writer.NewProperty("BurstScale"); @@ -339,14 +339,13 @@ namespace RTE { } - // Scale the emission rate up or down according to the appropriate throttle multiplier. - float throttleFactor = 1.0F; - float absThrottle = std::abs(m_Throttle); - if (m_Throttle < 0) { - throttleFactor = throttleFactor * (1 - absThrottle) + (m_NegativeThrottleMultiplier * absThrottle); - } else if (m_Throttle > 0) { - throttleFactor = throttleFactor * (1 - absThrottle) + (m_PositiveThrottleMultiplier * absThrottle); - } + // Figure out the throttle factor + float throttleFactor = 1.0f; + if (m_Throttle < 0) // Negative throttle, scale down according to the min throttle range + throttleFactor += fabs(m_MinThrottleRange) * m_Throttle; + else if (m_Throttle > 0) // Positive throttle, scale up + throttleFactor += fabs(m_MaxThrottleRange) * m_Throttle; + // Apply the throttle factor to the emission rate per update if (burst) return m_AvgBurstImpulse * throttleFactor; @@ -398,15 +397,15 @@ namespace RTE { // TODO: Potentially get this once outside instead, like in attach/detach") MovableObject *pRootParent = GetRootParent(); - // Scale the emission rate up or down according to the appropriate throttle multiplier. - float throttleFactor = 1.0F; - float absThrottle = std::abs(m_Throttle); - if (m_Throttle < 0) { - throttleFactor = throttleFactor * (1 - absThrottle) + (m_NegativeThrottleMultiplier * absThrottle); - } else if (m_Throttle > 0) { - throttleFactor = throttleFactor * (1 - absThrottle) + (m_PositiveThrottleMultiplier * absThrottle); - } - m_FlashScale = throttleFactor; + // Figure out the throttle factor + // Negative throttle, scale down according to the min throttle range + float throttleFactor = 1.0f; + if (m_Throttle < 0) + throttleFactor += fabs(m_MinThrottleRange) * m_Throttle; + // Positive throttle, scale up + else if (m_Throttle > 0) + throttleFactor += fabs(m_MaxThrottleRange) * m_Throttle; + // Check burst triggering against whether the spacing is fulfilled if (m_BurstTriggered && (m_BurstSpacing <= 0 || m_BurstTimer.IsPastSimMS(m_BurstSpacing))) { diff --git a/Entities/PEmitter.h b/Entities/PEmitter.h index 09f8832cf..3c9c8d07a 100644 --- a/Entities/PEmitter.h +++ b/Entities/PEmitter.h @@ -506,9 +506,10 @@ class PEmitter : long m_EmitCount; // The max number of emissions to emit per emit being enabled long m_EmitCountLimit; - // TODO: explain this property more clearly - float m_NegativeThrottleMultiplier; //!< The multiplier applied to emission rate relative to negative throttle. 1.0 means no change in rate, 0 means negative throttle will scale down to zero. - float m_PositiveThrottleMultiplier; //!< DESCRIPTION PENDING + // The range negative throttle has on emission rate. 1.0 means the rate can be throttled down to 0%, 0 means negative throttle has no effect + double m_MinThrottleRange; + // The range positive throttle has on emission rate. 1.0 means the rate can be throttled up to 200%, 0 means negative throttle has no effect + double m_MaxThrottleRange; // The normalized throttle which controls the MSPE between 1.0 * m_MSPERange and -1.0 * m_MSPERange. 0 means emit the regular m_PPM amount. float m_Throttle; // Whether or not this' emissions ignore hits with itself, even if they are set to hit other MOs. From 69b9c113b79eaefc6811f48c9d146a35cb00bc41 Mon Sep 17 00:00:00 2001 From: fourZK Date: Sat, 25 Sep 2021 21:39:30 +0300 Subject: [PATCH 53/72] Changed throttle logic and renamed `Min/MaxThrottleRange` as `Negative/PositiveThrottleMultiplier`, Added corresponding lua bindings, Removed "ThrottleRangeRedefined" tag for being useless --- Entities/AEmitter.cpp | 50 +++++++++++++++---------------- Entities/AEmitter.h | 42 +++++++++++--------------- Entities/PEmitter.cpp | 60 ++++++++++++++++++------------------- Entities/PEmitter.h | 6 ++-- Lua/LuaBindingsEntities.cpp | 4 +-- 5 files changed, 77 insertions(+), 85 deletions(-) diff --git a/Entities/AEmitter.cpp b/Entities/AEmitter.cpp index bb8a50e7f..9ba6f5e08 100644 --- a/Entities/AEmitter.cpp +++ b/Entities/AEmitter.cpp @@ -38,9 +38,8 @@ void AEmitter::Clear() m_WasEmitting = false; m_EmitCount = 0; m_EmitCountLimit = 0; - m_MinThrottleRange = 1.0F; - m_MaxThrottleRange = 1.0F; - m_ThrottleRangeRedefined = false; + m_NegativeThrottleMultiplier = 1.0F; + m_PositiveThrottleMultiplier = 1.0F; m_Throttle = 0; m_EmissionsIgnoreThis = false; m_BurstScale = 1.0F; @@ -86,9 +85,8 @@ int AEmitter::Create(const AEmitter &reference) { m_EmitEnabled = reference.m_EmitEnabled; m_EmitCount = reference.m_EmitCount; m_EmitCountLimit = reference.m_EmitCountLimit; - m_MinThrottleRange = reference.m_MinThrottleRange; - m_MaxThrottleRange = reference.m_MaxThrottleRange; - m_ThrottleRangeRedefined = reference.m_ThrottleRangeRedefined; + m_NegativeThrottleMultiplier = reference.m_NegativeThrottleMultiplier; + m_PositiveThrottleMultiplier = reference.m_PositiveThrottleMultiplier; m_Throttle = reference.m_Throttle; m_EmissionsIgnoreThis = reference.m_EmissionsIgnoreThis; m_BurstScale = reference.m_BurstScale; @@ -140,12 +138,10 @@ int AEmitter::ReadProperty(const std::string_view &propName, Reader &reader) { reader >> ppm; // Go through all emissions and set the rate so that it emulates the way it used to work, for mod backwards compatibility. for (Emission *emission : m_EmissionList) { emission->m_PPM = ppm / static_cast(m_EmissionList.size()); } - } else if (propName == "MinThrottleRange") { - reader >> m_MinThrottleRange; - m_ThrottleRangeRedefined = true; - } else if (propName == "MaxThrottleRange") { - reader >> m_MaxThrottleRange; - m_ThrottleRangeRedefined = true; + } else if (propName == "NegativeThrottleMultiplier") { + reader >> m_NegativeThrottleMultiplier; + } else if (propName == "PositiveThrottleMultiplier") { + reader >> m_PositiveThrottleMultiplier; } else if (propName == "Throttle") { reader >> m_Throttle; } else if (propName == "EmissionsIgnoreThis") { @@ -216,10 +212,10 @@ int AEmitter::Save(Writer &writer) const writer << m_EmitCountLimit; writer.NewProperty("EmissionsIgnoreThis"); writer << m_EmissionsIgnoreThis; - writer.NewProperty("MinThrottleRange"); - writer << m_MinThrottleRange; - writer.NewProperty("MaxThrottleRange"); - writer << m_MaxThrottleRange; + writer.NewProperty("NegativeThrottleMultiplier"); + writer << m_NegativeThrottleMultiplier; + writer.NewProperty("PositiveThrottleMultiplier"); + writer << m_PositiveThrottleMultiplier; writer.NewProperty("Throttle"); writer << m_Throttle; writer.NewProperty("BurstScale"); @@ -348,12 +344,13 @@ float AEmitter::EstimateImpulse(bool burst) } - // Figure out the throttle factor + // Scale the emission rate up or down according to the appropriate throttle multiplier. float throttleFactor = 1.0F; - if (m_Throttle < 0) { // Negative throttle, scale down according to the min throttle range - throttleFactor += std::abs(m_MinThrottleRange) * m_Throttle; - } else if (m_Throttle > 0) { // Positive throttle, scale up - throttleFactor += std::abs(m_MaxThrottleRange) * m_Throttle; + float absThrottle = std::abs(m_Throttle); + if (m_Throttle < 0) { + throttleFactor = throttleFactor * (1 - absThrottle) + (m_NegativeThrottleMultiplier * absThrottle); + } else if (m_Throttle > 0) { + throttleFactor = throttleFactor * (1 - absThrottle) + (m_PositiveThrottleMultiplier * absThrottle); } // Apply the throttle factor to the emission rate per update if (burst) { return m_AvgBurstImpulse * throttleFactor; } @@ -436,12 +433,15 @@ void AEmitter::Update() // TODO: Potentially get this once outside instead, like in attach/detach") MovableObject *pRootParent = GetRootParent(); + // Scale the emission rate up or down according to the appropriate throttle multiplier. float throttleFactor = 1.0F; - if (m_Throttle < 0) { // Negative throttle, scale down according to the min throttle range - throttleFactor += std::abs(m_MinThrottleRange) * m_Throttle; - } else if (m_Throttle > 0) { // Positive throttle, scale up - throttleFactor += std::abs(m_MaxThrottleRange) * m_Throttle; + float absThrottle = std::abs(m_Throttle); + if (m_Throttle < 0) { + throttleFactor = throttleFactor * (1 - absThrottle) + (m_NegativeThrottleMultiplier * absThrottle); + } else if (m_Throttle > 0) { + throttleFactor = throttleFactor * (1 - absThrottle) + (m_PositiveThrottleMultiplier * absThrottle); } + m_FlashScale = throttleFactor; // Check burst triggering against whether the spacing is fulfilled if (m_BurstTriggered && (m_BurstSpacing <= 0 || m_BurstTimer.IsPastSimMS(m_BurstSpacing))) { diff --git a/Entities/AEmitter.h b/Entities/AEmitter.h index 4fbc18b69..ab316d88d 100644 --- a/Entities/AEmitter.h +++ b/Entities/AEmitter.h @@ -263,34 +263,29 @@ ClassInfoGetters; float GetThrottle() const { return m_Throttle; } /// - /// Gets the minimum throttle range of this AEmitter. + /// Gets the negative throttle multiplier of this AEmitter. /// - /// The minimum throttle range of this AEmitter. - float GetMinThrottleRange() const { return m_MinThrottleRange; } + /// The negative throttle multiplier of this AEmitter. + float GetNegativeThrottleMultiplier() const { return m_NegativeThrottleMultiplier; } - /// - /// Sets the minimum throttle range for this AEmitter. - /// - /// The new minimum throttle range for this AEmitter. - void SetMinThrottleRange(float minThrottleRange) { m_MinThrottleRange = minThrottleRange; m_ThrottleRangeRedefined = true; } + /// + /// Gets the positive throttle multiplier of this AEmitter. + /// + /// The positive throttle multiplier of this AEmitter. + float GetPositiveThrottleMultiplier() const { return m_PositiveThrottleMultiplier; } /// - /// Gets the maximum throttle range of this AEmitter. + /// Sets the negative throttle multiplier of this AEmitter. /// - /// The maximum throttle range of this AEmitter. - float GetMaxThrottleRange() const { return m_MaxThrottleRange; } + /// The new throttle multiplier of this AEmitter. + void SetNegativeThrottleMultiplier(float newValue) { m_NegativeThrottleMultiplier = newValue; } - /// - /// Sets the maximum throttle range for this AEmitter. - /// - /// The new maximum throttle range for this AEmitter. - void SetMaxThrottleRange(float maxThrottleRange) { m_MaxThrottleRange = maxThrottleRange; m_ThrottleRangeRedefined = true; } + /// + /// Sets the positive throttle multiplier of this AEmitter. + /// + /// The new throttle multiplier of this AEmitter. + void SetPositiveThrottleMultiplier(float newValue) { m_PositiveThrottleMultiplier = newValue; } - /// - /// Gets whether or not the MinThrottleRange or MaxThrottleRange of this AEmitter have been redefined in INI. - /// - /// Whether or not the MinThrottleRange or MaxThrottleRange have been redefined in INI. - bool GetThrottleRangeRedefined() const { return m_ThrottleRangeRedefined; } /* ////////////////////////////////////////////////////////////////////////////////////////// // Method: SetEmitRate @@ -650,9 +645,8 @@ ClassInfoGetters; long m_EmitCount; // The max number of emissions to emit per emit being enabled long m_EmitCountLimit; - float m_MinThrottleRange; //!< The range negative throttle has on emission rate. 1.0 means the rate can be throttled down to 0%, 0 means negative throttle has no effect - float m_MaxThrottleRange; //!< The range positive throttle has on emission rate. 1.0 means the rate can be throttled up to 200%, 0 means positive throttle has no effect - bool m_ThrottleRangeRedefined; //!< Whether or not the MinThrottleRange or MaxThrottleRange has been redefined in INI. + float m_NegativeThrottleMultiplier; //!< The multiplier applied to the emission rate when throttle is negative. Relative to the absolute throttle value. + float m_PositiveThrottleMultiplier; //!< The multiplier applied to the emission rate when throttle is positive. Relative to the absolute throttle value. float m_Throttle; //!< The normalized throttle which controls the MSPE between 1.0 * m_MSPERange and -1.0 * m_MSPERange. 0 means emit the regular m_PPM amount. // Whether or not this' emissions ignore hits with itself, even if they are set to hit other MOs. bool m_EmissionsIgnoreThis; diff --git a/Entities/PEmitter.cpp b/Entities/PEmitter.cpp index 385e9c283..405658163 100644 --- a/Entities/PEmitter.cpp +++ b/Entities/PEmitter.cpp @@ -37,8 +37,8 @@ namespace RTE { m_WasEmitting = false; m_EmitCount = 0; m_EmitCountLimit = 0; - m_MinThrottleRange = -1; - m_MaxThrottleRange = 1; + m_NegativeThrottleMultiplier = 1.0F; + m_PositiveThrottleMultiplier = 1.0F; m_Throttle = 0; m_EmissionsIgnoreThis = false; m_BurstScale = 1.0; @@ -90,8 +90,8 @@ namespace RTE { m_EmitEnabled = reference.m_EmitEnabled; m_EmitCount = reference.m_EmitCount; m_EmitCountLimit = reference.m_EmitCountLimit; - m_MinThrottleRange = reference.m_MinThrottleRange; - m_MaxThrottleRange = reference.m_MaxThrottleRange; + m_NegativeThrottleMultiplier = reference.m_NegativeThrottleMultiplier; + m_PositiveThrottleMultiplier = reference.m_PositiveThrottleMultiplier; m_Throttle = reference.m_Throttle; m_EmissionsIgnoreThis = reference.m_EmissionsIgnoreThis; m_BurstScale = reference.m_BurstScale; @@ -143,10 +143,10 @@ namespace RTE { for (list::iterator eItr = m_EmissionList.begin(); eItr != m_EmissionList.end(); ++eItr) (*eItr).m_PPM = ppm / m_EmissionList.size(); } - else if (propName == "MinThrottleRange") - reader >> m_MinThrottleRange; - else if (propName == "MaxThrottleRange") - reader >> m_MaxThrottleRange; + else if (propName == "NegativeThrottleMultiplier") + reader >> m_NegativeThrottleMultiplier; + else if (propName == "PositiveThrottleMultiplier") + reader >> m_PositiveThrottleMultiplier; else if (propName == "Throttle") reader >> m_Throttle; else if (propName == "EmissionsIgnoreThis") @@ -213,10 +213,10 @@ namespace RTE { writer << m_EmitCountLimit; writer.NewProperty("EmissionsIgnoreThis"); writer << m_EmissionsIgnoreThis; - writer.NewProperty("MinThrottleRange"); - writer << m_MinThrottleRange; - writer.NewProperty("MaxThrottleRange"); - writer << m_MaxThrottleRange; + writer.NewProperty("NegativeThrottleMultiplier"); + writer << m_NegativeThrottleMultiplier; + writer.NewProperty("PositiveThrottleMultiplier"); + writer << m_PositiveThrottleMultiplier; writer.NewProperty("Throttle"); writer << m_Throttle; writer.NewProperty("BurstScale"); @@ -339,13 +339,14 @@ namespace RTE { } - // Figure out the throttle factor - float throttleFactor = 1.0f; - if (m_Throttle < 0) // Negative throttle, scale down according to the min throttle range - throttleFactor += fabs(m_MinThrottleRange) * m_Throttle; - else if (m_Throttle > 0) // Positive throttle, scale up - throttleFactor += fabs(m_MaxThrottleRange) * m_Throttle; - + // Scale the emission rate up or down according to the appropriate throttle multiplier. + float throttleFactor = 1.0F; + float absThrottle = std::abs(m_Throttle); + if (m_Throttle < 0) { + throttleFactor = throttleFactor * (1 - absThrottle) + (m_NegativeThrottleMultiplier * absThrottle); + } else if (m_Throttle > 0) { + throttleFactor = throttleFactor * (1 - absThrottle) + (m_PositiveThrottleMultiplier * absThrottle); + } // Apply the throttle factor to the emission rate per update if (burst) return m_AvgBurstImpulse * throttleFactor; @@ -397,15 +398,15 @@ namespace RTE { // TODO: Potentially get this once outside instead, like in attach/detach") MovableObject *pRootParent = GetRootParent(); - // Figure out the throttle factor - // Negative throttle, scale down according to the min throttle range - float throttleFactor = 1.0f; - if (m_Throttle < 0) - throttleFactor += fabs(m_MinThrottleRange) * m_Throttle; - // Positive throttle, scale up - else if (m_Throttle > 0) - throttleFactor += fabs(m_MaxThrottleRange) * m_Throttle; - + // Scale the emission rate up or down according to the appropriate throttle multiplier. + float throttleFactor = 1.0F; + float absThrottle = std::abs(m_Throttle); + if (m_Throttle < 0) { + throttleFactor = throttleFactor * (1 - absThrottle) + (m_NegativeThrottleMultiplier * absThrottle); + } else if (m_Throttle > 0) { + throttleFactor = throttleFactor * (1 - absThrottle) + (m_PositiveThrottleMultiplier * absThrottle); + } + m_FlashScale = throttleFactor; // Check burst triggering against whether the spacing is fulfilled if (m_BurstTriggered && (m_BurstSpacing <= 0 || m_BurstTimer.IsPastSimMS(m_BurstSpacing))) { @@ -477,8 +478,7 @@ namespace RTE { emitVel = RotateOffset(emitVel); pParticle->SetVel(parentVel + emitVel); - if (pParticle->GetLifetime() != 0) - pParticle->SetLifetime(pParticle->GetLifetime() * (1.0F + ((*eItr).GetLifeVariation() * RandomNormalNum()))); + if (pParticle->GetLifetime() != 0) { pParticle->SetLifetime(std::max(static_cast(pParticle->GetLifetime() * (1.0F + ((*eItr).GetLifeVariation() * RandomNormalNum()))), 1)); } pParticle->SetTeam(m_Team); pParticle->SetIgnoresTeamHits(true); diff --git a/Entities/PEmitter.h b/Entities/PEmitter.h index 3c9c8d07a..8bc0acb79 100644 --- a/Entities/PEmitter.h +++ b/Entities/PEmitter.h @@ -506,10 +506,8 @@ class PEmitter : long m_EmitCount; // The max number of emissions to emit per emit being enabled long m_EmitCountLimit; - // The range negative throttle has on emission rate. 1.0 means the rate can be throttled down to 0%, 0 means negative throttle has no effect - double m_MinThrottleRange; - // The range positive throttle has on emission rate. 1.0 means the rate can be throttled up to 200%, 0 means negative throttle has no effect - double m_MaxThrottleRange; + float m_NegativeThrottleMultiplier; //!< The multiplier applied to the emission rate when throttle is negative. Relative to the absolute throttle value. + float m_PositiveThrottleMultiplier; //!< The multiplier applied to the emission rate when throttle is positive. Relative to the absolute throttle value. // The normalized throttle which controls the MSPE between 1.0 * m_MSPERange and -1.0 * m_MSPERange. 0 means emit the regular m_PPM amount. float m_Throttle; // Whether or not this' emissions ignore hits with itself, even if they are set to hit other MOs. diff --git a/Lua/LuaBindingsEntities.cpp b/Lua/LuaBindingsEntities.cpp index a374804cc..3275592f3 100644 --- a/Lua/LuaBindingsEntities.cpp +++ b/Lua/LuaBindingsEntities.cpp @@ -364,8 +364,8 @@ namespace RTE { .property("EmitAngle", &AEmitter::GetEmitAngle, &AEmitter::SetEmitAngle) .property("GetThrottle", &AEmitter::GetThrottle, &AEmitter::SetThrottle) .property("Throttle", &AEmitter::GetThrottle, &AEmitter::SetThrottle) - .property("MinThrottleRangeTODORENAME", &AEmitter::GetMinThrottleRange, &AEmitter::SetMinThrottleRange) - .property("MaxThrottleRangeTODORENAME", &AEmitter::GetMaxThrottleRange, &AEmitter::SetMaxThrottleRange) + .property("NegativeThrottleMultiplier", &AEmitter::GetNegativeThrottleMultiplier, &AEmitter::SetNegativeThrottleMultiplier) + .property("PositiveThrottleMultiplier", &AEmitter::GetPositiveThrottleMultiplier, &AEmitter::SetPositiveThrottleMultiplier) .property("BurstSpacing", &AEmitter::GetBurstSpacing, &AEmitter::SetBurstSpacing) .property("BurstDamage", &AEmitter::GetBurstDamage, &AEmitter::SetBurstDamage) .property("EmitterDamageMultiplier", &AEmitter::GetEmitterDamageMultiplier, &AEmitter::SetEmitterDamageMultiplier) From f02114e0d3f3fbbf0143fa9f91cef9d2fb07e187 Mon Sep 17 00:00:00 2001 From: fourZK Date: Sun, 26 Sep 2021 17:01:00 +0300 Subject: [PATCH 54/72] Update CHANGELOG.md --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6db660072..5c2af8c99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -149,8 +149,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Added `MovableObject` Lua function `EnableOrDisableAllScripts` that allows you to enable or disable all scripts on a `MovableObject` based on the passed in value. -WARNING FIX THIS LINE BEFORE MERGING PR!!!!!!!!!!!! -- Added `AEmitter` Lua properties `MinThrottleRangeTODOFIXNAMEHERE` and `MaxThrottleRangeTODOFIXNAMEHERE` that allow you to 4zK PLEASE FILL IN +- Added `AEmitter` and `PEmitter` Lua (R/W) properties `NegativeThrottleMultiplier` and `PositiveThrottleMultiplier` that affect the emission rate relative to throttle. - Added `Attachable` Lua function and INI property `InheritsFrame` which lets `Attachables` inherit their parent's frame. It is set to false by default. @@ -189,6 +188,10 @@ WARNING FIX THIS LINE BEFORE MERGING PR!!!!!!!!!!!!
Changed +- `AEmitter` and `PEmitter` throttle logic has changed: + The properties `MinThrottleRange` and `MaxThrottleRange` have been changed to `NegativeThrottleMultiplier` and `PositiveThrottleMultiplier` respectively. + The new logic uses the multipliers to multiply the emission rate relative to the absolute throttle value. `NegativeThrottleMultiplier` is used when throttle is negative, and vice versa. + - Doors in `Team = -1` will now open up for all actors. - `MovableMan` function `KillAllActors` (commonly found in activities) has been appropriately renamed `KillAllEnemyActors`. From c63eb0345b577a653b1a83b0eed00fa38b548cce Mon Sep 17 00:00:00 2001 From: fourZK Date: Wed, 29 Sep 2021 17:47:06 +0300 Subject: [PATCH 55/72] Fixed craft scuttling after 4+ seconds of delivery time. Emitters already get reset during `ResetAllTimers()` so `ResetEmissionTimers()` will be removed for being useless --- Activities/GameActivity.cpp | 1 - Entities/ACDropShip.cpp | 23 ----------------------- Entities/ACDropShip.h | 11 ----------- Entities/ACRocket.cpp | 27 --------------------------- Entities/ACRocket.h | 11 ----------- Entities/ACraft.cpp | 7 +++++++ Entities/ACraft.h | 15 ++++----------- 7 files changed, 11 insertions(+), 84 deletions(-) diff --git a/Activities/GameActivity.cpp b/Activities/GameActivity.cpp index ec342a9cd..a8e401356 100644 --- a/Activities/GameActivity.cpp +++ b/Activities/GameActivity.cpp @@ -2110,7 +2110,6 @@ void GameActivity::Update() g_FrameMan.SetScreenText("Your order has arrived!", ScreenOfPlayer(player), 333); m_MessageTimer[player].Reset(); - pDeliveryCraft->ResetEmissionTimers(); // Reset the engine timers so they don't emit a massive burst after being added to the world pDeliveryCraft->ResetAllTimers(); pDeliveryCraft->Update(); diff --git a/Entities/ACDropShip.cpp b/Entities/ACDropShip.cpp index 2bedf1eff..3b2cf9430 100644 --- a/Entities/ACDropShip.cpp +++ b/Entities/ACDropShip.cpp @@ -799,27 +799,4 @@ void ACDropShip::SetLeftHatch(Attachable *newHatch) { } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ResetEmissionTimers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reset the timers of all emissions so they will start/stop at the -// correct relative offsets from now. - -void ACDropShip::ResetEmissionTimers() -{ - if (m_pRThruster && m_pRThruster->IsAttached()) - m_pRThruster->ResetEmissionTimers(); - - if (m_pLThruster && m_pLThruster->IsAttached()) - m_pLThruster->ResetEmissionTimers(); - - if (m_pURThruster && m_pURThruster->IsAttached()) - m_pURThruster->ResetEmissionTimers(); - - if (m_pULThruster && m_pULThruster->IsAttached()) - m_pULThruster->ResetEmissionTimers(); -} } // namespace RTE diff --git a/Entities/ACDropShip.h b/Entities/ACDropShip.h index dd0752803..0c37b4598 100644 --- a/Entities/ACDropShip.h +++ b/Entities/ACDropShip.h @@ -249,17 +249,6 @@ ClassInfoGetters; void SetRightHatch(Attachable *newHatch); -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ResetEmissionTimers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reset the timers of all emissions so they will start/stop at the -// correct relative offsets from now. -// Arguments: None. -// Return value: None. - - void ResetEmissionTimers() override; - - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: GetMaxEngineAngle ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Entities/ACRocket.cpp b/Entities/ACRocket.cpp index 8e4d3ddf7..8131a5e78 100644 --- a/Entities/ACRocket.cpp +++ b/Entities/ACRocket.cpp @@ -885,33 +885,6 @@ void ACRocket::SetULeftThruster(AEmitter *newThruster) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -////////////////////////////////////////////////////////////////////////////////////////// -// Method: ResetEmissionTimers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reset the timers of all emissions so they will start/stop at the -// correct relative offsets from now. - -void ACRocket::ResetEmissionTimers() -{ - if (m_pMThruster && m_pMThruster->IsAttached()) - m_pMThruster->ResetEmissionTimers(); - - if (m_pRThruster && m_pRThruster->IsAttached()) - m_pRThruster->ResetEmissionTimers(); - - if (m_pLThruster && m_pLThruster->IsAttached()) - m_pLThruster->ResetEmissionTimers(); - - if (m_pURThruster && m_pURThruster->IsAttached()) - m_pURThruster->ResetEmissionTimers(); - - if (m_pULThruster && m_pULThruster->IsAttached()) - m_pULThruster->ResetEmissionTimers(); -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void ACRocket::Draw(BITMAP *pTargetBitmap, const Vector &targetPos, DrawMode mode, bool onlyPhysical) const { ACraft::Draw(pTargetBitmap, targetPos, mode, onlyPhysical); diff --git a/Entities/ACRocket.h b/Entities/ACRocket.h index fe326250c..abc2d08bd 100644 --- a/Entities/ACRocket.h +++ b/Entities/ACRocket.h @@ -159,17 +159,6 @@ ClassInfoGetters; void Update() override; -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ResetEmissionTimers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reset the timers of all emissions so they will start/stop at the -// correct relative offsets from now. -// Arguments: None. -// Return value: None. - - void ResetEmissionTimers() override; - - ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Draw ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Entities/ACraft.cpp b/Entities/ACraft.cpp index 410fa1864..6cacf8c2f 100644 --- a/Entities/ACraft.cpp +++ b/Entities/ACraft.cpp @@ -865,6 +865,13 @@ void ACraft::GibThis(const Vector &impactImpulse, MovableObject *movableObjectTo Actor::GibThis(impactImpulse, movableObjectToIgnore); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void ACraft::ResetAllTimers() { + MOSRotating::ResetAllTimers(); + + m_FlippedTimer.Reset(); +} ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: Update diff --git a/Entities/ACraft.h b/Entities/ACraft.h index d8422f629..c711d84e0 100644 --- a/Entities/ACraft.h +++ b/Entities/ACraft.h @@ -478,17 +478,10 @@ enum bool HasDelivered() { return m_HasDelivered; } - -////////////////////////////////////////////////////////////////////////////////////////// -// Virtual method: ResetEmissionTimers -////////////////////////////////////////////////////////////////////////////////////////// -// Description: Reset the timers of all emissions so they will start/stop at the -// correct relative offsets from now. -// Arguments: None. -// Return value: None. - - virtual void ResetEmissionTimers() {} - + /// + /// Resets all the timers related to this, including the scuttle timer. + /// + void ResetAllTimers() override; ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: OnMOHit From 015288ec5dbc08ae8c9cc773412cbced39da8657 Mon Sep 17 00:00:00 2001 From: fourZK Date: Thu, 30 Sep 2021 23:27:48 +0300 Subject: [PATCH 56/72] Missed cleanup --- Entities/ACrab.cpp | 5 ----- Entities/AHuman.cpp | 5 ----- 2 files changed, 10 deletions(-) diff --git a/Entities/ACrab.cpp b/Entities/ACrab.cpp index 290ef7d0a..79b7e43a0 100644 --- a/Entities/ACrab.cpp +++ b/Entities/ACrab.cpp @@ -492,11 +492,6 @@ void ACrab::SetJetpack(AEmitter *newJetpack) { if (m_pJetpack->HasNoSetDamageMultiplier()) { m_pJetpack->SetDamageMultiplier(0.0F); } m_pJetpack->SetApplyTransferredForcesAtOffset(false); m_pJetpack->SetDeleteWhenRemovedFromParent(true); - - if (!m_pJetpack->GetThrottleRangeRedefined()) { - m_pJetpack->SetMinThrottleRange(0); - m_pJetpack->SetMaxThrottleRange(0); - } } } diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index e00548b53..e33e1426a 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -537,11 +537,6 @@ void AHuman::SetJetpack(AEmitter *newJetpack) { if (m_pJetpack->HasNoSetDamageMultiplier()) { m_pJetpack->SetDamageMultiplier(0.0F); } m_pJetpack->SetApplyTransferredForcesAtOffset(false); - - if (!m_pJetpack->GetThrottleRangeRedefined()) { - m_pJetpack->SetMinThrottleRange(0); - m_pJetpack->SetMaxThrottleRange(0); - } } } From f215efca7eb44f09ab36e0e8aabbb58e3549860a Mon Sep 17 00:00:00 2001 From: fourZK Date: Thu, 30 Sep 2021 23:41:05 +0300 Subject: [PATCH 57/72] Missed cleanup --- Entities/ACrab.cpp | 2 -- Entities/AHuman.cpp | 2 -- 2 files changed, 4 deletions(-) diff --git a/Entities/ACrab.cpp b/Entities/ACrab.cpp index 79b7e43a0..429020a4a 100644 --- a/Entities/ACrab.cpp +++ b/Entities/ACrab.cpp @@ -2130,8 +2130,6 @@ void ACrab::Update() // Jetpack throttle depletes relative to jet time, but only if throttle range values have been defined float jetTimeRatio = std::max(m_JetTimeLeft / m_JetTimeTotal, 0.0F); m_pJetpack->SetThrottle(jetTimeRatio * 2.0F - 1.0F); - float minScale = 1.0F - m_pJetpack->GetMinThrottleRange(); - m_pJetpack->SetFlashScale(minScale + (1.0F + m_pJetpack->GetMaxThrottleRange() - minScale) * jetTimeRatio); } // Start Jetpack burn if (m_Controller.IsState(BODY_JUMPSTART) && m_JetTimeLeft > 0 && m_Status != INACTIVE) diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index e33e1426a..825056c6b 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -3130,8 +3130,6 @@ void AHuman::Update() // Jetpack throttle depletes relative to jet time, but only if throttle range values have been defined float jetTimeRatio = std::max(m_JetTimeLeft / m_JetTimeTotal, 0.0F); m_pJetpack->SetThrottle(jetTimeRatio * 2.0F - 1.0F); - float minScale = 1.0F - m_pJetpack->GetMinThrottleRange(); - m_pJetpack->SetFlashScale(minScale + (1.0F + m_pJetpack->GetMaxThrottleRange() - minScale) * jetTimeRatio); } // Start Jetpack burn if (m_Controller.IsState(BODY_JUMPSTART) && m_JetTimeLeft > 0 && m_Status != INACTIVE) From aad1053e9c7e1311d9fc3e3fdf6719b4a1f871d0 Mon Sep 17 00:00:00 2001 From: fourZK Date: Sat, 2 Oct 2021 23:14:01 +0300 Subject: [PATCH 58/72] ACRocket refactor: allow rockets to work without full set of thrusters --- Entities/ACRocket.cpp | 276 ++++++++++++------------------------------ 1 file changed, 80 insertions(+), 196 deletions(-) diff --git a/Entities/ACRocket.cpp b/Entities/ACRocket.cpp index 8131a5e78..0f6499202 100644 --- a/Entities/ACRocket.cpp +++ b/Entities/ACRocket.cpp @@ -492,210 +492,94 @@ void ACRocket::Update() ///////////////////////////////// // Controller update and handling -// TODO: Improve and make optional thrusters more robust! - // Make sure we have all thrusters - if (m_pMThruster && m_pRThruster && m_pLThruster && m_pURThruster && m_pULThruster) - { - if (m_Status != DEAD && m_Status != DYING) - { - // Fire main thrusters - if (m_Controller.IsState(MOVE_UP) || m_Controller.IsState(AIM_UP)) - { - if (!m_pMThruster->IsEmitting()) - { - m_pMThruster->TriggerBurst(); - // This is to make sure se get loose from being sideways stuck - m_ForceDeepCheck = true; - } - m_pMThruster->EnableEmission(true); - // Engines are noisy! - m_pMThruster->AlarmOnEmit(m_Team); - - if (m_HatchState == OPEN) { - CloseHatch(); - m_HatchTimer.Reset(); - } - } - else - { - m_pMThruster->EnableEmission(false); - - // Fire reverse thrusters - if (m_Controller.IsState(MOVE_DOWN) || m_Controller.IsState(AIM_DOWN)) - { - if (!m_pURThruster->IsEmitting()) - m_pURThruster->TriggerBurst(); - if (!m_pULThruster->IsEmitting()) - m_pULThruster->TriggerBurst(); - m_pURThruster->EnableEmission(true); - m_pULThruster->EnableEmission(true); - } - else - { - m_pURThruster->EnableEmission(false); - m_pULThruster->EnableEmission(false); - } - } - - // Fire left thrusters - if (m_Controller.IsState(MOVE_RIGHT)/* || m_Rotation > 0.1*/) - { - if (!m_pLThruster->IsEmitting()) - m_pLThruster->TriggerBurst(); - m_pLThruster->EnableEmission(true); - } - else - m_pLThruster->EnableEmission(false); - - // Fire right thrusters - if (m_Controller.IsState(MOVE_LEFT)/* || m_Rotation < 0.1*/) - { - if (!m_pRThruster->IsEmitting()) - m_pRThruster->TriggerBurst(); - m_pRThruster->EnableEmission(true); - } - else - m_pRThruster->EnableEmission(false); - /* - if (m_Controller.IsState(PRESS_FACEBUTTON)) - { - if (m_GearState == RAISED) - m_GearState = LOWERING; - else if (m_GearState == LOWERED) - m_GearState = RAISING; - } - */ - if (m_Controller.IsState(PRESS_FACEBUTTON)) - { - if (m_HatchState == CLOSED) - DropAllInventory(); - else if (m_HatchState == OPEN) - CloseHatch(); - } - } - else - { - m_pMThruster->EnableEmission(false); - m_pRThruster->EnableEmission(false); - m_pLThruster->EnableEmission(false); - m_pURThruster->EnableEmission(false); - m_pULThruster->EnableEmission(false); - } - -/* - else if (m_Controller && m_Controller.IsState(MOVE_RIGHT) || m_Controller.IsState(MOVE_LEFT)) - { - if (m_MoveState != WALK && m_MoveState != CROUCH) - m_StrideStart = true; - - if (m_MoveState == BODY_JUMPSTART || m_MoveState == BODY_JUMPSTART) - m_MoveState = CROUCH; - else - m_MoveState = WALK; - - if (m_Controller.IsState(MOVE_FAST)) - { - m_Paths[FGROUND][WALK].SetSpeed(FAST); - m_Paths[BGROUND][WALK].SetSpeed(FAST); - } - else - { - m_Paths[FGROUND][WALK].SetSpeed(NORMAL); - m_Paths[BGROUND][WALK].SetSpeed(NORMAL); - } - - if ((m_Controller.IsState(MOVE_RIGHT) && m_HFlipped) || (m_Controller.IsState(MOVE_LEFT) && !m_HFlipped)) - { - m_HFlipped = !m_HFlipped; - m_Paths[FGROUND][WALK].Terminate(); - m_Paths[BGROUND][WALK].Terminate(); - m_Paths[FGROUND][CLIMB].Terminate(); - m_Paths[BGROUND][CLIMB].Terminate(); - m_Paths[FGROUND][STAND].Terminate(); - m_Paths[BGROUND][STAND].Terminate(); - m_StrideStart = true; - } - } - else - m_MoveState = STAND; - - if (m_Controller.IsState(WEAPON_CHANGE_NEXT)) - { - if (m_pFGArm && m_pFGArm->IsAttached()) - { - m_pFGArm->SetDevice(SwapNextDevice(m_pFGArm->ReleaseDevice())); - m_pFGArm->SetHandPos(m_Pos + m_HolsterOffset.GetXFlipped(m_HFlipped)); - } - } -*/ - // No Controller present - if (m_Controller.IsDisabled()) - { - m_pMThruster->EnableEmission(false); - m_pRThruster->EnableEmission(false); - m_pLThruster->EnableEmission(false); - m_pURThruster->EnableEmission(false); - m_pULThruster->EnableEmission(false); + if ((m_Status == STABLE || m_Status == UNSTABLE) && !m_Controller.IsDisabled()) { + if (m_pMThruster) { + if (m_Controller.IsState(MOVE_UP) || m_Controller.IsState(AIM_UP)) { + if (!m_pMThruster->IsEmitting()) { + m_pMThruster->TriggerBurst(); + m_ForceDeepCheck = true; + } + m_pMThruster->EnableEmission(true); + m_pMThruster->AlarmOnEmit(m_Team); + + if (m_HatchState == OPEN) { + CloseHatch(); + m_HatchTimer.Reset(); + } + } else { + m_pMThruster->EnableEmission(false); + } } + if (m_pULThruster || m_pURThruster) { + if (m_Controller.IsState(MOVE_DOWN) || m_Controller.IsState(AIM_DOWN)) { + if (m_pURThruster) { + if (!m_pURThruster->IsEmitting()) { m_pURThruster->TriggerBurst(); } + m_pURThruster->EnableEmission(true); + } + if (m_pULThruster) { + if (!m_pULThruster->IsEmitting()) { m_pULThruster->TriggerBurst(); } + m_pULThruster->EnableEmission(true); + } + } else { + if (m_pURThruster) { m_pURThruster->EnableEmission(false); } + if (m_pULThruster) { m_pULThruster->EnableEmission(false); } + } + } + if (m_pLThruster) { + if (m_Controller.IsState(MOVE_RIGHT)) { + if (!m_pLThruster->IsEmitting()) { m_pLThruster->TriggerBurst(); } + m_pLThruster->EnableEmission(true); + } else { + m_pLThruster->EnableEmission(false); + } + } + if (m_pRThruster) { + if (m_Controller.IsState(MOVE_LEFT)) { + if (!m_pRThruster->IsEmitting()) { m_pRThruster->TriggerBurst(); } + m_pRThruster->EnableEmission(true); + } else { + m_pRThruster->EnableEmission(false); + } + } + if (m_Controller.IsState(PRESS_FACEBUTTON)) { + if (m_HatchState == CLOSED) { + DropAllInventory(); + } else if (m_HatchState == OPEN) { + CloseHatch(); + } + } + } else { + if (m_pMThruster) { m_pMThruster->EnableEmission(false); } + if (m_pRThruster) { m_pRThruster->EnableEmission(false); } + if (m_pLThruster) { m_pLThruster->EnableEmission(false); } + if (m_pURThruster) { m_pURThruster->EnableEmission(false); } + if (m_pULThruster) { m_pULThruster->EnableEmission(false); } } -// m_aSprite->SetAngle((m_AimAngle / 180) * 3.141592654); -// m_aSprite->SetScale(2.0); - /////////////////////////////////////////////////// // Travel the landing gear AtomGroup:s + if (m_pMThruster) { + if (m_pMThruster->IsEmitting()) { + m_Paths[RIGHT][RAISED].SetHFlip(m_HFlipped); + m_Paths[LEFT][RAISED].SetHFlip(!m_HFlipped); - // RAISE the gears - if (m_pMThruster && m_pMThruster->IsEmitting())// && m_pMThruster->IsSetToBurst()) - { - m_Paths[RIGHT][RAISED].SetHFlip(m_HFlipped); - m_Paths[LEFT][RAISED].SetHFlip(!m_HFlipped); - - if (m_pRLeg) - m_pRFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pRLeg->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation, - m_Vel, - m_Rotation, - m_Paths[RIGHT][RAISED], - deltaTime, - 0, - true); - if (m_pLLeg) - m_pLFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pLLeg->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation, - m_Vel, - m_Rotation, - m_Paths[LEFT][RAISED], - deltaTime, - 0, - true); - - m_GearState = RAISED; - } - // LOWER the gears - else if (m_pMThruster && !m_pMThruster->IsEmitting())// && m_GearState != LOWERED) - { - m_Paths[RIGHT][LOWERED].SetHFlip(m_HFlipped); - m_Paths[LEFT][LOWERED].SetHFlip(!m_HFlipped); - - if (m_pRLeg) - m_pRFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pRLeg->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation, - m_Vel, - m_Rotation, - m_Paths[RIGHT][LOWERED], - deltaTime, - 0, - true); - if (m_pLLeg) - m_pLFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pLLeg->GetParentOffset().GetXFlipped(m_HFlipped) * m_Rotation, - m_Vel, - m_Rotation, - m_Paths[LEFT][LOWERED], - deltaTime, - 0, - true); - m_GearState = LOWERED; - } + if (m_pRLeg) { m_pRFootGroup->PushAsLimb(m_Pos.GetFloored() + RotateOffset(m_pRLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[RIGHT][RAISED], deltaTime, 0, true); } + + if (m_pLLeg) { m_pLFootGroup->PushAsLimb(m_Pos.GetFloored() + RotateOffset(m_pLLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[LEFT][RAISED], deltaTime, 0, true); } + + m_GearState = RAISED; + } else { + m_Paths[RIGHT][LOWERED].SetHFlip(m_HFlipped); + m_Paths[LEFT][LOWERED].SetHFlip(!m_HFlipped); + + if (m_pRLeg) { m_pRFootGroup->PushAsLimb(m_Pos.GetFloored() + RotateOffset(m_pRLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[RIGHT][LOWERED], deltaTime, 0, true); } + + if (m_pLLeg) { m_pLFootGroup->PushAsLimb(m_Pos.GetFloored() + RotateOffset(m_pLLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[LEFT][LOWERED], deltaTime, 0, true); } + m_GearState = LOWERED; + } + } ///////////////////////////////// // Manage Attachable:s From 9d02719d335aaad7a003ff79e09d275e16b28f76 Mon Sep 17 00:00:00 2001 From: fourZK Date: Sat, 2 Oct 2021 23:15:07 +0300 Subject: [PATCH 59/72] Exposed `ACraft` property `HatchDelay` to Lua --- Entities/ACraft.h | 12 ++++++++++++ Lua/LuaBindingsEntities.cpp | 1 + 2 files changed, 13 insertions(+) diff --git a/Entities/ACraft.h b/Entities/ACraft.h index c711d84e0..c9a7ba9f1 100644 --- a/Entities/ACraft.h +++ b/Entities/ACraft.h @@ -577,6 +577,18 @@ enum /// Whether this ACraft will scuttle automatically on death. void SetScuttleOnDeath(bool scuttleOnDeath) { m_ScuttleOnDeath = scuttleOnDeath; } + /// + /// Gets the hatch opening/closing delay of this ACraft. + /// + /// The hatch delay of this ACraft. + int GetHatchDelay() const { return m_HatchDelay; } + + /// + /// Sets the hatch opening/closing delay of this ACraft. + /// + /// The new hatch delay of this ACraft. + void SetHatchDelay(int newDelay) { m_HatchDelay = newDelay; } + /// /// Destroys this ACraft and creates its specified Gibs in its place with appropriate velocities. Any Attachables are removed and also given appropriate velocities. /// diff --git a/Lua/LuaBindingsEntities.cpp b/Lua/LuaBindingsEntities.cpp index 6c4fd81da..e389018ac 100644 --- a/Lua/LuaBindingsEntities.cpp +++ b/Lua/LuaBindingsEntities.cpp @@ -139,6 +139,7 @@ namespace RTE { .property("MaxPassengers", &ACraft::GetMaxPassengers) .property("DeliveryDelayMultiplier", &ACraft::GetDeliveryDelayMultiplier) .property("ScuttleOnDeath", &ACraft::GetScuttleOnDeath, &ACraft::SetScuttleOnDeath) + .property("HatchDelay", &ACraft::GetHatchDelay, &ACraft::SetHatchDelay) .def("OpenHatch", &ACraft::OpenHatch) .def("CloseHatch", &ACraft::CloseHatch) From 09eafdce8616f387d31db378d0ce57a8084cacc2 Mon Sep 17 00:00:00 2001 From: fourZK Date: Sat, 2 Oct 2021 23:31:06 +0300 Subject: [PATCH 60/72] Better stillness calculation to be used in mobility-related stuff --- Entities/AHuman.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index 21e12834c..b59472d65 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -3162,6 +3162,8 @@ void AHuman::Update() //////////////////////////////////// // Movement direction + bool still = m_Vel.GetMagnitude() + m_PrevVel.GetMagnitude() < 1.0F; + // If the pie menu is on, try to preserve whatever move state we had before it going into effect. if (!m_Controller.IsState(PIE_MENU_ACTIVE)) { if ((m_Controller.IsState(MOVE_RIGHT) || m_Controller.IsState(MOVE_LEFT) || m_MoveState == JUMP) && m_Status != INACTIVE) { @@ -3170,7 +3172,7 @@ void AHuman::Update() m_Paths[BGROUND][i].SetHFlip(m_HFlipped); } // Only if not jumping, OR if jumping, and apparently stuck on something - then help out with the limbs. - if (m_MoveState != JUMP || m_Vel.GetLargest() < 0.1F) { + if (m_MoveState != JUMP || still) { // Restart the stride if we're just starting to walk or crawl. if ((m_MoveState != WALK && !m_Controller.IsState(BODY_CROUCH)) || (m_MoveState != CRAWL && m_Controller.IsState(BODY_CROUCH))) { m_StrideStart = true; @@ -3589,8 +3591,7 @@ void AHuman::Update() } // WALKING, OR WE ARE JETPACKING AND STUCK - if (m_MoveState == WALK || (m_MoveState == JUMP && m_Vel.GetLargest() < 1.0)) - { + if (m_MoveState == WALK || (m_MoveState == JUMP && still)) { m_Paths[FGROUND][STAND].Terminate(); m_Paths[BGROUND][STAND].Terminate(); @@ -3601,9 +3602,8 @@ void AHuman::Update() bool playStride = false; - // Make sure we are starting a stride if we're basically stopped - if (fabs(m_Vel.GetLargest()) < 0.5) - m_StrideStart = true; + // Make sure we are starting a stride if we're basically stopped. + if (still) { m_StrideStart = true; } if (m_pFGLeg && (!m_pBGLeg || (!(m_Paths[FGROUND][WALK].PathEnded() && BGLegProg < 0.5) || m_StrideStart))) { @@ -3701,9 +3701,8 @@ void AHuman::Update() m_Paths[BGROUND][CLIMB].Terminate(); } - // Restart the climbing stroke if the current one seems to be taking too long without movement - if ((m_ArmClimbing[FGROUND] || m_ArmClimbing[BGROUND]) && fabs(m_Vel.GetLargest()) < 0.5 && m_StrideTimer.IsPastSimMS(m_Paths[BGROUND][CLIMB].GetTotalPathTime() / 4)) - { + // Restart the climbing stroke if the current one seems to be taking too long with no movement. + if ((m_ArmClimbing[FGROUND] || m_ArmClimbing[BGROUND]) && still && m_StrideTimer.IsPastSimMS(static_cast(m_Paths[BGROUND][CLIMB].GetTotalPathTime() * 0.5F))) { m_StrideStart = true; m_Paths[FGROUND][CLIMB].Terminate(); m_Paths[BGROUND][CLIMB].Terminate(); From 304ee6a988e0bc958681190d5b361034a777f459 Mon Sep 17 00:00:00 2001 From: fourZK Date: Sat, 2 Oct 2021 23:54:03 +0300 Subject: [PATCH 61/72] Better flail mechanics for limbs --- Entities/AHuman.cpp | 29 +++++++++++------------------ Entities/AtomGroup.cpp | 14 +++++++++----- Entities/Leg.cpp | 3 ++- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index b59472d65..125db1f65 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -3800,12 +3800,10 @@ void AHuman::Update() } else if (m_pFGLeg || m_pBGLeg) { if (m_MoveState == JUMP) { // TODO: Utilize jump paths in an intuitive way! - if (m_pFGLeg && (!m_Paths[FGROUND][m_MoveState].PathEnded() || m_JetTimeLeft == m_JetTimeTotal)) { - m_pFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pFGLeg->GetParentOffset()), m_pFGLeg->GetMaxLength(), g_SceneMan.GetGlobalAcc() * deltaTime, m_AngularVel, m_pFGLeg->GetMass(), deltaTime); - } - if (m_pBGLeg && (!m_Paths[BGROUND][m_MoveState].PathEnded() || m_JetTimeLeft == m_JetTimeTotal)) { - m_pBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pBGLeg->GetParentOffset()), m_pBGLeg->GetMaxLength(), g_SceneMan.GetGlobalAcc() * deltaTime, m_AngularVel, m_pBGLeg->GetMass(), deltaTime); - } + if (m_pFGLeg) { m_pFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pFGLeg->GetParentOffset()), m_pFGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pFGLeg->GetMass(), deltaTime); } + + if (m_pBGLeg) { m_pBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pBGLeg->GetParentOffset()), m_pBGLeg->GetMaxLength(), m_PrevVel, m_AngularVel, m_pBGLeg->GetMass(), deltaTime); } + if (m_JetTimeLeft <= 0) { m_MoveState = STAND; m_Paths[FGROUND][JUMP].Terminate(); @@ -3841,18 +3839,13 @@ void AHuman::Update() } else { // Not stable/standing, so make sure the end of limbs are moving around limply in a ragdoll fashion. // TODO: Make the limb atom groups fly around and react to terrain, without getting stuck etc. - if (m_pFGArm) { - m_pFGHandGroup->FlailAsLimb(m_Pos, RotateOffset(m_pFGArm->GetParentOffset()), m_pFGArm->GetMaxLength(), (g_SceneMan.GetGlobalAcc() * deltaTime).RadRotate(m_AngularVel * deltaTime), m_AngularVel, m_pFGArm->GetMass(), deltaTime); - } - if (m_pBGArm) { - m_pBGHandGroup->FlailAsLimb(m_Pos, RotateOffset(m_pBGArm->GetParentOffset()), m_pBGArm->GetMaxLength(), (g_SceneMan.GetGlobalAcc() * deltaTime).RadRotate(m_AngularVel * deltaTime), m_AngularVel, m_pBGArm->GetMass(), deltaTime); - } - if (m_pFGLeg) { - m_pFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pFGLeg->GetParentOffset()), m_pFGLeg->GetMaxLength(), (g_SceneMan.GetGlobalAcc() * deltaTime).RadRotate(m_AngularVel * deltaTime), m_AngularVel, m_pFGLeg->GetMass(), deltaTime); - } - if (m_pBGLeg) { - m_pBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pBGLeg->GetParentOffset()), m_pBGLeg->GetMaxLength(), (g_SceneMan.GetGlobalAcc() * deltaTime).RadRotate(m_AngularVel * deltaTime), m_AngularVel, m_pBGLeg->GetMass(), deltaTime); - } + if (m_pFGArm) { m_pFGHandGroup->FlailAsLimb(m_Pos, RotateOffset(m_pFGArm->GetParentOffset()), m_pFGArm->GetMaxLength(), m_PrevVel * m_pFGArm->GetJointStiffness(), m_AngularVel, m_pFGArm->GetMass(), deltaTime); } + + if (m_pBGArm) { m_pBGHandGroup->FlailAsLimb(m_Pos, RotateOffset(m_pBGArm->GetParentOffset()), m_pBGArm->GetMaxLength(), m_PrevVel * m_pBGArm->GetJointStiffness(), m_AngularVel, m_pBGArm->GetMass(), deltaTime); } + + if (m_pFGLeg) { m_pFGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pFGLeg->GetParentOffset()), m_pFGLeg->GetMaxLength(), m_PrevVel * m_pFGLeg->GetJointStiffness(), m_AngularVel, m_pFGLeg->GetMass(), deltaTime); } + + if (m_pBGLeg) { m_pBGFootGroup->FlailAsLimb(m_Pos, RotateOffset(m_pBGLeg->GetParentOffset()), m_pBGLeg->GetMaxLength(), m_PrevVel * m_pBGLeg->GetJointStiffness(), m_AngularVel, m_pBGLeg->GetMass(), deltaTime); } } ///////////////////////////////// diff --git a/Entities/AtomGroup.cpp b/Entities/AtomGroup.cpp index 2b7bfd610..6a0af480a 100644 --- a/Entities/AtomGroup.cpp +++ b/Entities/AtomGroup.cpp @@ -1195,10 +1195,12 @@ namespace RTE { limbPath.SetRotation(rotation); limbPath.SetFrameTime(travelTime); - const Vector limbDist = g_SceneMan.ShortestDistance(jointPos, m_LimbPos); + Vector limbDist = g_SceneMan.ShortestDistance(jointPos, m_LimbPos, g_SceneMan.SceneWrapsX()); - // Restart the path if the limb strayed off the path. - if (limbDist.GetMagnitude() > m_OwnerMOSR->GetDiameter()) { limbPath.Terminate(); } + // Pull back the limb if it strayed off the path. + if (limbDist.GetMagnitude() > m_OwnerMOSR->GetRadius()) { + m_LimbPos = jointPos + limbDist.SetMagnitude(m_OwnerMOSR->GetRadius()); + } // TODO: Change this to a regular while loop if possible. do { @@ -1226,9 +1228,11 @@ namespace RTE { bool didWrap = false; Vector jointPos = ownerPos + jointOffset; - Vector centrifugalVel = jointOffset * std::fabs(angularVel); + Vector totalVel = velocity; + totalVel.RadRotate(angularVel * travelTime); + totalVel += jointOffset * std::abs(angularVel); - Vector pushImpulse = PushTravel(m_LimbPos, velocity + centrifugalVel, 100, didWrap, travelTime, false, false, false); + Vector pushImpulse = PushTravel(m_LimbPos, totalVel, 100, didWrap, travelTime, false, false, false); Vector limbRange = m_LimbPos - jointPos; diff --git a/Entities/Leg.cpp b/Entities/Leg.cpp index 0928465af..19485d4ee 100644 --- a/Entities/Leg.cpp +++ b/Entities/Leg.cpp @@ -175,7 +175,8 @@ namespace RTE { void Leg::UpdateCurrentAnkleOffset() { if (IsAttached()) { Vector targetOffset = g_SceneMan.ShortestDistance(m_JointPos, m_TargetPosition, g_SceneMan.SceneWrapsX()); - if (m_WillIdle && targetOffset.m_Y < -3) { targetOffset = m_IdleOffset.GetXFlipped(m_HFlipped); } + Vector rotatedTargetOffset = Vector(targetOffset.m_X, targetOffset.m_Y).RadRotate(m_Parent->GetRotAngle()); + if (m_WillIdle && rotatedTargetOffset.m_Y < -std::abs(rotatedTargetOffset.m_X)) { targetOffset.m_Y *= -1.0F/(std::abs(targetOffset.m_X) + 1.0F); } Vector distanceFromTargetOffsetToAnkleOffset(targetOffset - m_AnkleOffset); m_AnkleOffset += distanceFromTargetOffsetToAnkleOffset * m_MoveSpeed; From a7da13876aee41c14717ea5a88041ea11fdd8971 Mon Sep 17 00:00:00 2001 From: fourZK Date: Sat, 2 Oct 2021 23:58:58 +0300 Subject: [PATCH 62/72] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b07626d7..4ab1730b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -221,10 +221,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Various improvements to the Buy Menu. You can now navigate tabs with the actor swap buttons, and the menu will smartly navigate when you add an `Actor` to your shop list, so you can quickly select weapons, etc.. There is also a new `Settings.ini` property, `SmartBuyMenuNavigation = 0/1`, which allows you to turn off this smart buy menu navigation, in case you prefer not to have it. +- Exposed `ACraft` property `HatchDelay` to Lua (R/W). +
**Changed** +- `ACRocket`s can now function without a full set of thrusters. This also means that "Null Emitter" thrusters are no longer required for rockets. + - Changed `MOSprite` property `SpriteAnimMode` `Enum` `LOOPWHENMOVING` to `LOOPWHENACTIVE` as it also describes active devices. - Changed `Activity` Lua (R) properties `Running`, `Paused` and `ActivityOver` to `IsRunning`, `IsPaused` and `IsOver` respectively. (NOTE: corresponding `ActivityMan` functions remain unchanged) From 5b92aeb2ca256381f4f5482117ed8aff82aaeb89 Mon Sep 17 00:00:00 2001 From: MaximDude Date: Mon, 11 Oct 2021 02:00:43 +0300 Subject: [PATCH 63/72] Misc minor changes --- Entities/AtomGroup.cpp | 4 +--- Entities/Gib.h | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Entities/AtomGroup.cpp b/Entities/AtomGroup.cpp index 6a0af480a..a87de9d98 100644 --- a/Entities/AtomGroup.cpp +++ b/Entities/AtomGroup.cpp @@ -1198,9 +1198,7 @@ namespace RTE { Vector limbDist = g_SceneMan.ShortestDistance(jointPos, m_LimbPos, g_SceneMan.SceneWrapsX()); // Pull back the limb if it strayed off the path. - if (limbDist.GetMagnitude() > m_OwnerMOSR->GetRadius()) { - m_LimbPos = jointPos + limbDist.SetMagnitude(m_OwnerMOSR->GetRadius()); - } + if (limbDist.GetMagnitude() > m_OwnerMOSR->GetRadius()) { m_LimbPos = jointPos + limbDist.SetMagnitude(m_OwnerMOSR->GetRadius()); } // TODO: Change this to a regular while loop if possible. do { diff --git a/Entities/Gib.h b/Entities/Gib.h index ca445a443..d15cd798c 100644 --- a/Entities/Gib.h +++ b/Entities/Gib.h @@ -21,7 +21,7 @@ namespace RTE { /// /// Different types of logic for the Gib to use when applying velocity to its GibParticles. /// - enum SpreadMode { SpreadRandom, SpreadEven, SpreadSpiral }; + enum class SpreadMode { SpreadRandom, SpreadEven, SpreadSpiral }; #pragma region Creation /// From ca58568c6c7eda928b62f6855fe00bbe08ea60bf Mon Sep 17 00:00:00 2001 From: fourZK Date: Mon, 11 Oct 2021 20:24:31 +0300 Subject: [PATCH 64/72] Exposed `DataModule` property `IsFaction` to lua so that activities can also identify faction modules --- CHANGELOG.md | 2 +- Lua/LuaBindingsSystem.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ab1730b4..f481e6fc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -179,7 +179,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - New `Settings.ini` property `ShowEnemyHUD` which allows disabling of enemy actor HUD in its entirety. -- New `DataModule` property `IsFaction = 0/1` which determines if a module is a playable faction (in MetaGame, etc.). This replaces the need to put "Tech" in the module name. Defaults to false (0). +- New `DataModule` INI and Lua (R) property `IsFaction` which determines whether a module is a playable faction (in MetaGame, etc.). This replaces the need to put "Tech" in the module name. Defaults to false (0). - New `MOSRotating` INI and Lua (R) property `WoundCountAffectsImpulseLimitRatio` which can be used to make objects more prone to gibbing from impulse when they have also received wounds. diff --git a/Lua/LuaBindingsSystem.cpp b/Lua/LuaBindingsSystem.cpp index c28c5c253..84a8cfdea 100644 --- a/Lua/LuaBindingsSystem.cpp +++ b/Lua/LuaBindingsSystem.cpp @@ -119,6 +119,7 @@ namespace RTE { .property("Author", &DataModule::GetAuthor) .property("Description", &DataModule::GetDescription) .property("Version", &DataModule::GetVersionNumber) + .property("IsFaction", &DataModule::IsFaction) .def_readwrite("Presets", &DataModule::m_EntityList, luabind::return_stl_iterator); } From ba9211789d6a6d2061593e43596226241c8728f2 Mon Sep 17 00:00:00 2001 From: Gareth YR Date: Tue, 12 Oct 2021 01:01:20 -0300 Subject: [PATCH 65/72] Minor cleanup Simplified ACRocket leg path handling since it was 99% duplicated code Renamed AHuman local variable "still" to "isStill" for clarity, since "still" is a pretty multi-purpose word --- Entities/ACRocket.cpp | 24 ++++++------------------ Entities/AHuman.cpp | 10 +++++----- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/Entities/ACRocket.cpp b/Entities/ACRocket.cpp index 0f6499202..76f0bc3c1 100644 --- a/Entities/ACRocket.cpp +++ b/Entities/ACRocket.cpp @@ -560,25 +560,13 @@ void ACRocket::Update() // Travel the landing gear AtomGroup:s if (m_pMThruster) { - if (m_pMThruster->IsEmitting()) { - m_Paths[RIGHT][RAISED].SetHFlip(m_HFlipped); - m_Paths[LEFT][RAISED].SetHFlip(!m_HFlipped); + m_GearState = m_pMThruster->IsEmitting() ? LandingGearState::RAISED : LandingGearState::LOWERED; - if (m_pRLeg) { m_pRFootGroup->PushAsLimb(m_Pos.GetFloored() + RotateOffset(m_pRLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[RIGHT][RAISED], deltaTime, 0, true); } - - if (m_pLLeg) { m_pLFootGroup->PushAsLimb(m_Pos.GetFloored() + RotateOffset(m_pLLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[LEFT][RAISED], deltaTime, 0, true); } - - m_GearState = RAISED; - } else { - m_Paths[RIGHT][LOWERED].SetHFlip(m_HFlipped); - m_Paths[LEFT][LOWERED].SetHFlip(!m_HFlipped); - - if (m_pRLeg) { m_pRFootGroup->PushAsLimb(m_Pos.GetFloored() + RotateOffset(m_pRLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[RIGHT][LOWERED], deltaTime, 0, true); } - - if (m_pLLeg) { m_pLFootGroup->PushAsLimb(m_Pos.GetFloored() + RotateOffset(m_pLLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[LEFT][LOWERED], deltaTime, 0, true); } - - m_GearState = LOWERED; - } + m_Paths[RIGHT][m_GearState].SetHFlip(m_HFlipped); + m_Paths[LEFT][m_GearState].SetHFlip(!m_HFlipped); + + if (m_pRLeg) { m_pRFootGroup->PushAsLimb(m_Pos.GetFloored() + RotateOffset(m_pRLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[RIGHT][m_GearState], deltaTime, nullptr, true); } + if (m_pLLeg) { m_pLFootGroup->PushAsLimb(m_Pos.GetFloored() + RotateOffset(m_pLLeg->GetParentOffset()), m_Vel, m_Rotation, m_Paths[LEFT][m_GearState], deltaTime, nullptr, true); } } ///////////////////////////////// diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index 125db1f65..4a3ab8ea7 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -3162,7 +3162,7 @@ void AHuman::Update() //////////////////////////////////// // Movement direction - bool still = m_Vel.GetMagnitude() + m_PrevVel.GetMagnitude() < 1.0F; + bool isStill = m_Vel.GetMagnitude() + m_PrevVel.GetMagnitude() < 1.0F; // If the pie menu is on, try to preserve whatever move state we had before it going into effect. if (!m_Controller.IsState(PIE_MENU_ACTIVE)) { @@ -3172,7 +3172,7 @@ void AHuman::Update() m_Paths[BGROUND][i].SetHFlip(m_HFlipped); } // Only if not jumping, OR if jumping, and apparently stuck on something - then help out with the limbs. - if (m_MoveState != JUMP || still) { + if (m_MoveState != JUMP || isStill) { // Restart the stride if we're just starting to walk or crawl. if ((m_MoveState != WALK && !m_Controller.IsState(BODY_CROUCH)) || (m_MoveState != CRAWL && m_Controller.IsState(BODY_CROUCH))) { m_StrideStart = true; @@ -3591,7 +3591,7 @@ void AHuman::Update() } // WALKING, OR WE ARE JETPACKING AND STUCK - if (m_MoveState == WALK || (m_MoveState == JUMP && still)) { + if (m_MoveState == WALK || (m_MoveState == JUMP && isStill)) { m_Paths[FGROUND][STAND].Terminate(); m_Paths[BGROUND][STAND].Terminate(); @@ -3603,7 +3603,7 @@ void AHuman::Update() bool playStride = false; // Make sure we are starting a stride if we're basically stopped. - if (still) { m_StrideStart = true; } + if (isStill) { m_StrideStart = true; } if (m_pFGLeg && (!m_pBGLeg || (!(m_Paths[FGROUND][WALK].PathEnded() && BGLegProg < 0.5) || m_StrideStart))) { @@ -3702,7 +3702,7 @@ void AHuman::Update() } // Restart the climbing stroke if the current one seems to be taking too long with no movement. - if ((m_ArmClimbing[FGROUND] || m_ArmClimbing[BGROUND]) && still && m_StrideTimer.IsPastSimMS(static_cast(m_Paths[BGROUND][CLIMB].GetTotalPathTime() * 0.5F))) { + if ((m_ArmClimbing[FGROUND] || m_ArmClimbing[BGROUND]) && isStill && m_StrideTimer.IsPastSimMS(static_cast(m_Paths[BGROUND][CLIMB].GetTotalPathTime() * 0.5F))) { m_StrideStart = true; m_Paths[FGROUND][CLIMB].Terminate(); m_Paths[BGROUND][CLIMB].Terminate(); From 635d06c13706c1fc8b21c2d7146adeb17a6cc105 Mon Sep 17 00:00:00 2001 From: Gareth YR Date: Tue, 12 Oct 2021 01:03:50 -0300 Subject: [PATCH 66/72] More minor cleanup + a few separate changes while I was here Changed vector copying then rotating in Leg to use GetRadRotatedCopy. Renamed the Vector GetXRotated methods and tweaked their comments for clarity while I was here + added changelog entry for this rename --- CHANGELOG.md | 2 ++ Entities/Leg.cpp | 2 +- Lua/LuaBindingsSystem.cpp | 4 ++-- System/Vector.cpp | 2 +- System/Vector.h | 16 ++++++++-------- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7086fdfe..4a146433c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -373,6 +373,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Placing "Tech" in a `DataModule`'s `ModuleName` no longer makes the module a playable faction (in MetaGame, etc.). The `IsFaction` property should be used instead. The word "Tech" will also not be omitted from the module name when displayed in any faction selection dropdown list. + +- Renamed Lua methods `GetRadRotated` and `GetDegRotated` to `GetRadRotatedCopy` and `GetDegRotatedCopy` for clarity.
diff --git a/Entities/Leg.cpp b/Entities/Leg.cpp index 19485d4ee..d136b44aa 100644 --- a/Entities/Leg.cpp +++ b/Entities/Leg.cpp @@ -175,7 +175,7 @@ namespace RTE { void Leg::UpdateCurrentAnkleOffset() { if (IsAttached()) { Vector targetOffset = g_SceneMan.ShortestDistance(m_JointPos, m_TargetPosition, g_SceneMan.SceneWrapsX()); - Vector rotatedTargetOffset = Vector(targetOffset.m_X, targetOffset.m_Y).RadRotate(m_Parent->GetRotAngle()); + Vector rotatedTargetOffset = targetOffset.GetRadRotatedCopy(m_Parent->GetRotAngle()); if (m_WillIdle && rotatedTargetOffset.m_Y < -std::abs(rotatedTargetOffset.m_X)) { targetOffset.m_Y *= -1.0F/(std::abs(targetOffset.m_X) + 1.0F); } Vector distanceFromTargetOffsetToAnkleOffset(targetOffset - m_AnkleOffset); diff --git a/Lua/LuaBindingsSystem.cpp b/Lua/LuaBindingsSystem.cpp index 84a8cfdea..5f4128ddc 100644 --- a/Lua/LuaBindingsSystem.cpp +++ b/Lua/LuaBindingsSystem.cpp @@ -272,8 +272,8 @@ namespace RTE { .def("Reset", &Vector::Reset) .def("RadRotate", &Vector::RadRotate) .def("DegRotate", &Vector::DegRotate) - .def("GetRadRotated", &Vector::GetRadRotated) - .def("GetDegRotated", &Vector::GetDegRotated) + .def("GetRadRotatedCopy", &Vector::GetRadRotatedCopy) + .def("GetDegRotatedCopy", &Vector::GetDegRotatedCopy) .def("AbsRotateTo", &Vector::AbsRotateTo) .def("SetXY", &Vector::SetXY); } diff --git a/System/Vector.cpp b/System/Vector.cpp index 304a70cbc..f1fa68a1c 100644 --- a/System/Vector.cpp +++ b/System/Vector.cpp @@ -72,7 +72,7 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Vector Vector::GetRadRotated(const float angle) { + Vector Vector::GetRadRotatedCopy(const float angle) { Vector returnVector = *this; const float adjustedAngle = -angle; returnVector.m_X = m_X * std::cos(adjustedAngle) - m_Y * std::sin(adjustedAngle); diff --git a/System/Vector.h b/System/Vector.h index d8f779b5b..adc0bd5de 100644 --- a/System/Vector.h +++ b/System/Vector.h @@ -199,32 +199,32 @@ namespace RTE { float GetAbsDegAngle() const { return GetAbsRadAngle() / c_PI * 180.0F; } /// - /// Returns a Vector rotated relatively by an angle in radians. + /// Returns a copy of this Vector, rotated relatively by an angle in radians. /// /// The angle in radians to rotate by. Positive angles rotate counter-clockwise, and negative angles clockwise. - /// a Vector rotated relatively to this Vector . - Vector GetRadRotated(const float angle); + /// A rotated copy of this Vector. + Vector GetRadRotatedCopy(const float angle); /// /// Rotate this Vector relatively by an angle in radians. /// /// The angle in radians to rotate by. Positive angles rotate counter-clockwise, and negative angles clockwise. /// Vector reference to this after the operation. - Vector & RadRotate(const float angle) { *this = GetRadRotated(angle); return *this; } + Vector & RadRotate(const float angle) { *this = GetRadRotatedCopy(angle); return *this; } /// - /// Returns a Vector rotated relatively by an angle in degrees. + /// Returns a copy of this Vector, rotated relatively by an angle in degrees. /// /// The angle in degrees to rotate by. Positive angles rotate counter-clockwise, and negative angles clockwise. - /// a Vector rotated relatively to this Vector . - Vector GetDegRotated(const float angle) { return GetRadRotated(angle * c_PI / 180.0F); }; + /// A rotated copy of this Vector. + Vector GetDegRotatedCopy(const float angle) { return GetRadRotatedCopy(angle * c_PI / 180.0F); }; /// /// Rotate this Vector relatively by an angle in degrees. /// /// The angle in degrees to rotate by. Positive angles rotate counter-clockwise, and negative angles clockwise. /// Vector reference to this after the operation. - Vector & DegRotate(const float angle) { *this = GetDegRotated(angle); return *this; } + Vector & DegRotate(const float angle) { *this = GetDegRotatedCopy(angle); return *this; } /// /// Set this Vector to an absolute rotation based on the absolute rotation of another Vector. From e6799eaded98aa2511e57009338735726ee3a5d1 Mon Sep 17 00:00:00 2001 From: fourZK Date: Sun, 17 Oct 2021 19:21:54 +0300 Subject: [PATCH 67/72] Some formatting changes and cleanup --- Entities/AHuman.cpp | 89 ++++++++++++++++++++++----------------------- Entities/Leg.cpp | 4 +- 2 files changed, 44 insertions(+), 49 deletions(-) diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index 125db1f65..4e32d9155 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -3105,49 +3105,37 @@ void AHuman::Update() //////////////////////////////////// // Jetpack activation and blast direction - if (m_pJetpack) - { + if (m_pJetpack) { if (m_JetTimeTotal > 0) { // Jetpack throttle depletes relative to jet time, but only if throttle range values have been defined float jetTimeRatio = std::max(m_JetTimeLeft / m_JetTimeTotal, 0.0F); m_pJetpack->SetThrottle(jetTimeRatio * 2.0F - 1.0F); } - // Start Jetpack burn - if (m_Controller.IsState(BODY_JUMPSTART) && m_JetTimeLeft > 0 && m_Status != INACTIVE) - { + if (m_Controller.IsState(BODY_JUMPSTART) && m_JetTimeLeft > 0 && m_Status != INACTIVE) { m_pJetpack->TriggerBurst(); - // This is to make sure se get loose from being stuck m_ForceDeepCheck = true; m_pJetpack->EnableEmission(true); - // Quadruple this for the burst m_JetTimeLeft = std::max(m_JetTimeLeft - g_TimerMan.GetDeltaTimeMS() * 10.0F, 0.0F); - // Jetpack is ordered to be burning, or the pie menu is on and was burning before it went on } else if ((m_Controller.IsState(BODY_JUMP) || (m_MoveState == JUMP && m_Controller.IsState(PIE_MENU_ACTIVE))) && m_JetTimeLeft > 0 && m_Status != INACTIVE) { m_pJetpack->EnableEmission(true); - // Jetpacks are noisy! m_pJetpack->AlarmOnEmit(m_Team); - // Deduct from the jetpack time m_JetTimeLeft = std::max(m_JetTimeLeft - g_TimerMan.GetDeltaTimeMS(), 0.0F); m_MoveState = JUMP; m_Paths[FGROUND][JUMP].Restart(); m_Paths[BGROUND][JUMP].Restart(); - } - // Jetpack is off/turning off - else { + } else { m_pJetpack->EnableEmission(false); if (m_MoveState == JUMP) { m_MoveState = STAND; } - // Replenish the jetpack time, twice as fast m_JetTimeLeft = std::min(m_JetTimeLeft + g_TimerMan.GetDeltaTimeMS() * 2.0F, m_JetTimeTotal); } float maxAngle = c_HalfPI * m_JetAngleRange; // If pie menu is on, keep the angle to what it was before. if (!m_Controller.IsState(PIE_MENU_ACTIVE)) { - // Direct the jetpack nozzle according to movement stick if analog input is present. + // Direct the jetpack nozzle according to either analog stick input or aim angle. if (m_Controller.GetAnalogMove().GetMagnitude() > 0.1F) { float jetAngle = std::clamp(m_Controller.GetAnalogMove().GetAbsRadAngle() - c_HalfPI, -maxAngle, maxAngle); m_pJetpack->SetEmitAngle(FacingAngle(jetAngle - c_HalfPI)); - // Use the aim angle if we're getting digital input. } else { // Halve the jet angle when looking downwards so the actor isn't forced to go sideways // TODO: don't hardcode this value? @@ -3166,6 +3154,7 @@ void AHuman::Update() // If the pie menu is on, try to preserve whatever move state we had before it going into effect. if (!m_Controller.IsState(PIE_MENU_ACTIVE)) { + bool crouching = m_Controller.IsState(BODY_CROUCH); if ((m_Controller.IsState(MOVE_RIGHT) || m_Controller.IsState(MOVE_LEFT) || m_MoveState == JUMP) && m_Status != INACTIVE) { for (int i = WALK; i < MOVEMENTSTATECOUNT; ++i) { m_Paths[FGROUND][i].SetHFlip(m_HFlipped); @@ -3174,12 +3163,12 @@ void AHuman::Update() // Only if not jumping, OR if jumping, and apparently stuck on something - then help out with the limbs. if (m_MoveState != JUMP || still) { // Restart the stride if we're just starting to walk or crawl. - if ((m_MoveState != WALK && !m_Controller.IsState(BODY_CROUCH)) || (m_MoveState != CRAWL && m_Controller.IsState(BODY_CROUCH))) { + if ((m_MoveState != WALK && !crouching) || (m_MoveState != CRAWL && crouching)) { m_StrideStart = true; MoveOutOfTerrain(g_MaterialGrass); } - m_MoveState = m_Controller.IsState(BODY_CROUCH) ? CRAWL : WALK; + m_MoveState = crouching ? CRAWL : WALK; // Engage prone state, this makes the body's rotational spring pull it horizontal instead of upright. if (m_MoveState == CRAWL && m_ProneState == NOTPRONE) { @@ -3210,15 +3199,18 @@ void AHuman::Update() // Stop the going prone spring. if (m_ProneState == GOPRONE) { m_ProneState = PRONE; } } - // Not moving, so check if we need to be crouched or not. - } else if (m_Controller.IsState(BODY_CROUCH)) { - // Don't go back to crouching if we're already prone, the player has to let go of the crouch button first. If already laying down, just stay put. - m_MoveState = m_ProneState == NOTPRONE ? CROUCH : NOMOVE; } else { - m_MoveState = STAND; + m_ArmClimbing[FGROUND] = false; + m_ArmClimbing[BGROUND] = false; + if (crouching) { + // Don't go back to crouching if we're already prone, the player has to let go of the crouch button first. If already laying down, just stay put. + m_MoveState = m_ProneState == NOTPRONE ? CROUCH : NOMOVE; + } else { + m_MoveState = STAND; + } } // Disengage the prone state as soon as crouch is released. - if (!m_Controller.IsState(BODY_CROUCH)) { m_ProneState = NOTPRONE; } + if (!crouching) { m_ProneState = NOTPRONE; } } //////////////////////////////////// @@ -3333,6 +3325,7 @@ void AHuman::Update() } else m_AimState = AIMSTILL; + float adjustedAimAngle = m_AimAngle * GetFlipFactor(); ////////////////////////////// // Sharp aim calculation @@ -3398,12 +3391,12 @@ void AHuman::Update() } else if (m_ArmsState == THROWING_PREP) { m_ArmsState = THROWING_RELEASE; // TODO: figure out how to properly use EndThrowOffset, since it doesn't play much a role for just one frame! - m_pFGArm->SetHandPos(m_Pos + (m_pFGArm->GetParentOffset() + pThrown->GetEndThrowOffset().RadRotate(m_AimAngle * GetFlipFactor())).GetXFlipped(m_HFlipped) * m_Rotation); + m_pFGArm->SetHandPos(m_Pos + (m_pFGArm->GetParentOffset() + pThrown->GetEndThrowOffset().RadRotate(adjustedAimAngle)).GetXFlipped(m_HFlipped) * m_Rotation); MovableObject *pMO = m_pFGArm->ReleaseHeldMO(); if (pMO) { - pMO->SetPos(m_Pos + (m_pFGArm->GetParentOffset() + Vector(m_pFGArm->GetMaxLength(), -m_pFGArm->GetMaxLength() * 0.5F)).GetXFlipped(m_HFlipped).RadRotate(m_AimAngle * GetFlipFactor()) * m_Rotation); + pMO->SetPos(m_Pos + (m_pFGArm->GetParentOffset() + Vector(m_pFGArm->GetMaxLength(), -m_pFGArm->GetMaxLength() * 0.5F)).GetXFlipped(m_HFlipped).RadRotate(adjustedAimAngle) * m_Rotation); float maxThrowVel = pThrown->GetMaxThrowVel(); float minThrowVel = pThrown->GetMinThrowVel(); if (maxThrowVel == 0) { @@ -3415,7 +3408,7 @@ void AHuman::Update() tossVec.RadRotate(m_AimAngle); pMO->SetVel(tossVec.GetXFlipped(m_HFlipped) * m_Rotation); pMO->SetAngularVel(m_AngularVel + RandomNum(-5.0F, 2.5F) * GetFlipFactor()); - pMO->SetRotAngle(m_AimAngle * GetFlipFactor()); + pMO->SetRotAngle(adjustedAimAngle); if (pMO->IsHeldDevice()) { pMO->SetTeam(m_Team); @@ -3813,27 +3806,31 @@ void AHuman::Update() m_Paths[FGROUND][WALK].Terminate(); m_Paths[BGROUND][WALK].Terminate(); } - } else if (m_MoveState == CROUCH) { - m_Paths[FGROUND][WALK].Terminate(); - m_Paths[BGROUND][WALK].Terminate(); - m_Paths[FGROUND][CRAWL].Terminate(); - m_Paths[BGROUND][CRAWL].Terminate(); + } else { + m_Paths[FGROUND][JUMP].Terminate(); + m_Paths[BGROUND][JUMP].Terminate(); + if (m_MoveState == CROUCH) { + m_Paths[FGROUND][WALK].Terminate(); + m_Paths[BGROUND][WALK].Terminate(); + m_Paths[FGROUND][CRAWL].Terminate(); + m_Paths[BGROUND][CRAWL].Terminate(); - if (m_pFGLeg) { m_pFGFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pFGLeg->GetParentOffset().GetXFlipped(m_HFlipped), m_Vel, Matrix(), m_Paths[FGROUND][CROUCH], deltaTime); } + if (m_pFGLeg) { m_pFGFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pFGLeg->GetParentOffset().GetXFlipped(m_HFlipped), m_Vel, limbPathRotation, m_Paths[FGROUND][CROUCH], deltaTime); } - if (m_pBGLeg) { m_pBGFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pBGLeg->GetParentOffset().GetXFlipped(m_HFlipped), m_Vel, Matrix(), m_Paths[BGROUND][CROUCH], deltaTime); } + if (m_pBGLeg) { m_pBGFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pBGLeg->GetParentOffset().GetXFlipped(m_HFlipped), m_Vel, limbPathRotation, m_Paths[BGROUND][CROUCH], deltaTime); } - } else { - m_Paths[FGROUND][WALK].Terminate(); - m_Paths[BGROUND][WALK].Terminate(); - m_Paths[FGROUND][CRAWL].Terminate(); - m_Paths[BGROUND][CRAWL].Terminate(); - m_Paths[FGROUND][ARMCRAWL].Terminate(); - m_Paths[BGROUND][ARMCRAWL].Terminate(); + } else { + m_Paths[FGROUND][WALK].Terminate(); + m_Paths[BGROUND][WALK].Terminate(); + m_Paths[FGROUND][CRAWL].Terminate(); + m_Paths[BGROUND][CRAWL].Terminate(); + m_Paths[FGROUND][ARMCRAWL].Terminate(); + m_Paths[BGROUND][ARMCRAWL].Terminate(); - if (m_pFGLeg) { m_pFGFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pFGLeg->GetParentOffset().GetXFlipped(m_HFlipped), m_Vel, Matrix(), m_Paths[FGROUND][STAND], deltaTime, 0, false); } + if (m_pFGLeg) { m_pFGFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pFGLeg->GetParentOffset().GetXFlipped(m_HFlipped), m_Vel, limbPathRotation, m_Paths[FGROUND][STAND], deltaTime, 0, !m_pBGLeg); } - if (m_pBGLeg) { m_pBGFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pBGLeg->GetParentOffset().GetXFlipped(m_HFlipped), m_Vel, Matrix(), m_Paths[BGROUND][STAND], deltaTime, 0, false); } + if (m_pBGLeg) { m_pBGFootGroup->PushAsLimb(m_Pos.GetFloored() + m_pBGLeg->GetParentOffset().GetXFlipped(m_HFlipped), m_Vel, limbPathRotation, m_Paths[BGROUND][STAND], deltaTime, 0, !m_pFGLeg); } + } } } } else { @@ -3854,7 +3851,7 @@ void AHuman::Update() float toRotate = 0; // Only rotate the head to match the aim angle if body is stable and upright if (m_Status == STABLE && std::abs(rot) < (c_HalfPI + c_QuarterPI)) { - toRotate = m_pHead->GetRotMatrix().GetRadAngleTo((m_AimAngle * GetFlipFactor()) * m_LookToAimRatio + rot * (0.9F - m_LookToAimRatio)) * 0.15F; + toRotate = m_pHead->GetRotMatrix().GetRadAngleTo((adjustedAimAngle) * m_LookToAimRatio + rot * (0.9F - m_LookToAimRatio)) * 0.15F; } else { // Rotate the head loosely along with the body if upside down, unstable or dying. toRotate = m_pHead->GetRotMatrix().GetRadAngleTo(rot) * m_pHead->GetJointStiffness() * (std::abs(toRotate) + c_QuarterPI); @@ -3882,7 +3879,7 @@ void AHuman::Update() affectingBodyAngle = std::abs(std::sin(rot)) * rot * m_FGArmFlailScalar * (1.0F - aimScalar); } - m_pFGArm->SetRotAngle(affectingBodyAngle + m_AimAngle * GetFlipFactor()); + m_pFGArm->SetRotAngle(affectingBodyAngle + adjustedAimAngle); if (m_Status == STABLE) { if (m_ArmClimbing[FGROUND]) { @@ -3898,7 +3895,7 @@ void AHuman::Update() } if (m_pBGArm) { - m_pBGArm->SetRotAngle(std::abs(std::sin(rot)) * rot * m_BGArmFlailScalar + (m_AimAngle * GetFlipFactor())); + m_pBGArm->SetRotAngle(std::abs(std::sin(rot)) * rot * m_BGArmFlailScalar + (adjustedAimAngle)); if (m_Status == STABLE) { if (m_ArmClimbing[BGROUND]) { // Can't climb or crawl with the shield diff --git a/Entities/Leg.cpp b/Entities/Leg.cpp index 19485d4ee..b0c8332cc 100644 --- a/Entities/Leg.cpp +++ b/Entities/Leg.cpp @@ -160,9 +160,7 @@ namespace RTE { Attachable::Update(); - if (m_FrameCount == 1) { - m_Frame = 0; - } else { + if (m_FrameCount != 1) { m_NormalizedExtension = std::clamp((m_AnkleOffset.GetMagnitude() - m_MinExtension) / (m_MaxExtension - m_MinExtension), 0.0F, 1.0F); m_Frame = std::min(m_FrameCount - 1, static_cast(std::floor(m_NormalizedExtension * static_cast(m_FrameCount)))); } From 22ceee2aec46908dbf567f7a71499f40c0215655 Mon Sep 17 00:00:00 2001 From: fourZK Date: Sun, 17 Oct 2021 19:22:43 +0300 Subject: [PATCH 68/72] Revert idle offset in ankle offset logic --- Entities/Leg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Entities/Leg.cpp b/Entities/Leg.cpp index b0c8332cc..1e2b95f0e 100644 --- a/Entities/Leg.cpp +++ b/Entities/Leg.cpp @@ -174,7 +174,7 @@ namespace RTE { if (IsAttached()) { Vector targetOffset = g_SceneMan.ShortestDistance(m_JointPos, m_TargetPosition, g_SceneMan.SceneWrapsX()); Vector rotatedTargetOffset = Vector(targetOffset.m_X, targetOffset.m_Y).RadRotate(m_Parent->GetRotAngle()); - if (m_WillIdle && rotatedTargetOffset.m_Y < -std::abs(rotatedTargetOffset.m_X)) { targetOffset.m_Y *= -1.0F/(std::abs(targetOffset.m_X) + 1.0F); } + if (m_WillIdle && rotatedTargetOffset.m_Y < -std::abs(rotatedTargetOffset.m_X)) { targetOffset = RotateOffset(m_IdleOffset); } Vector distanceFromTargetOffsetToAnkleOffset(targetOffset - m_AnkleOffset); m_AnkleOffset += distanceFromTargetOffsetToAnkleOffset * m_MoveSpeed; From 07772bed27c5210c3fe6bfd9c54f8b26b24d8ad5 Mon Sep 17 00:00:00 2001 From: fourZK Date: Sun, 17 Oct 2021 19:23:23 +0300 Subject: [PATCH 69/72] `ParentBreakWound` should inherit `DrawAfterParent` of the attachable --- Entities/MOSRotating.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Entities/MOSRotating.cpp b/Entities/MOSRotating.cpp index ed168dedd..439db64f5 100644 --- a/Entities/MOSRotating.cpp +++ b/Entities/MOSRotating.cpp @@ -1643,6 +1643,7 @@ Attachable * MOSRotating::RemoveAttachable(Attachable *attachable, bool addToMov if (!m_ToDelete && attachable->GetParentBreakWound()) { AEmitter *parentBreakWound = dynamic_cast(attachable->GetParentBreakWound()->Clone()); if (parentBreakWound) { + parentBreakWound->SetDrawnAfterParent(attachable->IsDrawnAfterParent()); parentBreakWound->SetEmitAngle((attachable->GetParentOffset() * m_Rotation).GetAbsRadAngle()); AddWound(parentBreakWound, attachable->GetParentOffset(), false); parentBreakWound = nullptr; From 78196e2917fa48a7e9f9d8bd469abfaf189f933c Mon Sep 17 00:00:00 2001 From: fourZK Date: Sun, 17 Oct 2021 19:24:26 +0300 Subject: [PATCH 70/72] Fix bug related to aiming while flying --- Entities/AHuman.cpp | 57 ++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/Entities/AHuman.cpp b/Entities/AHuman.cpp index 4e32d9155..b9f9e0f5f 100644 --- a/Entities/AHuman.cpp +++ b/Entities/AHuman.cpp @@ -3292,39 +3292,32 @@ void AHuman::Update() std::min(m_AimTmr.GetElapsedSimTimeMS() * 0.00015, 0.1); if (m_AimAngle < -m_AimRange) { m_AimAngle = -m_AimRange; } - } else if (analogAim.GetMagnitude() > 0.1F && m_Status != INACTIVE) { - // Hack to avoid the GetAbsRadAngle to mangle an aim angle straight down + } else if (analogAim.GetMagnitude() > 0 && m_Status != INACTIVE) { + // Hack to avoid the GetAbsRadAngle from mangling an aim angle straight down. if (analogAim.m_X == 0) { analogAim.m_X += 0.01F * GetFlipFactor(); } - m_AimAngle = analogAim.GetAbsRadAngle(); - - // Check for flip change - if ((analogAim.m_X > 0 && m_HFlipped) || (analogAim.m_X < 0 && !m_HFlipped)) { - m_HFlipped = !m_HFlipped; - m_CheckTerrIntersection = true; - if (m_ProneState == NOTPRONE) - MoveOutOfTerrain(g_MaterialGrass); - m_Paths[FGROUND][WALK].Terminate(); - m_Paths[BGROUND][WALK].Terminate(); - m_Paths[FGROUND][CLIMB].Terminate(); - m_Paths[BGROUND][CLIMB].Terminate(); - m_Paths[FGROUND][CRAWL].Terminate(); - m_Paths[BGROUND][CRAWL].Terminate(); - m_Paths[FGROUND][ARMCRAWL].Terminate(); - m_Paths[BGROUND][ARMCRAWL].Terminate(); - m_Paths[FGROUND][STAND].Terminate(); - m_Paths[BGROUND][STAND].Terminate(); - m_StrideStart = true; - // Stop the going prone spring - if (m_ProneState == GOPRONE) - m_ProneState = PRONE; - } - // Correct angle based on flip - m_AimAngle = FacingAngle(m_AimAngle); - // Clamp so it's within the range - Clamp(m_AimAngle, m_AimRange, -m_AimRange); - } - else - m_AimState = AIMSTILL; + m_AimAngle = analogAim.GetAbsRadAngle(); + + if ((analogAim.m_X > 0 && m_HFlipped) || (analogAim.m_X < 0 && !m_HFlipped)) { + m_HFlipped = !m_HFlipped; + m_CheckTerrIntersection = true; + if (m_ProneState == NOTPRONE) { MoveOutOfTerrain(g_MaterialGrass); } + for (int i = STAND; i < CLIMB; ++i) { + m_Paths[FGROUND][i].SetHFlip(m_HFlipped); + m_Paths[BGROUND][i].SetHFlip(m_HFlipped); + m_Paths[FGROUND][i].Terminate(); + m_Paths[BGROUND][i].Terminate(); + } + m_StrideStart = true; + // Stop the going prone spring. + if (m_ProneState == GOPRONE) { m_ProneState = PRONE; } + } + // Correct angle based on flip. + m_AimAngle = FacingAngle(m_AimAngle); + // Clamp so it's within the range. + Clamp(m_AimAngle, m_AimRange, -m_AimRange); + } else { + m_AimState = AIMSTILL; + } float adjustedAimAngle = m_AimAngle * GetFlipFactor(); ////////////////////////////// From 3ef7f211b5b52c2abb1c15cde4da7587f61a50b8 Mon Sep 17 00:00:00 2001 From: fourZK Date: Sun, 17 Oct 2021 19:25:15 +0300 Subject: [PATCH 71/72] Fix sounds playing twice in `SoundOverlapMode::RESTART` --- Entities/SoundContainer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Entities/SoundContainer.cpp b/Entities/SoundContainer.cpp index f235ad5c1..82adb84a9 100644 --- a/Entities/SoundContainer.cpp +++ b/Entities/SoundContainer.cpp @@ -173,7 +173,7 @@ namespace RTE { if (HasAnySounds()) { if (IsBeingPlayed()) { if (m_SoundOverlapMode == SoundOverlapMode::RESTART) { - Restart(player); + return Restart(player); } else if (m_SoundOverlapMode == SoundOverlapMode::IGNORE_PLAY) { return false; } From 8d60784b70dfb3c62ce4d1f284c16f895b1e5f29 Mon Sep 17 00:00:00 2001 From: fourZK Date: Mon, 18 Oct 2021 23:56:27 +0300 Subject: [PATCH 72/72] Important leg fix --- Entities/Leg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Entities/Leg.cpp b/Entities/Leg.cpp index e3eebdb07..7bf178817 100644 --- a/Entities/Leg.cpp +++ b/Entities/Leg.cpp @@ -172,7 +172,7 @@ namespace RTE { if (IsAttached()) { Vector targetOffset = g_SceneMan.ShortestDistance(m_JointPos, m_TargetPosition, g_SceneMan.SceneWrapsX()); Vector rotatedTargetOffset = targetOffset.GetRadRotatedCopy(m_Parent->GetRotAngle()); - if (m_WillIdle && rotatedTargetOffset.m_Y < -std::abs(rotatedTargetOffset.m_X)) { targetOffset = RotateOffset(m_IdleOffset); } + if (m_WillIdle && rotatedTargetOffset.m_Y < -std::abs(rotatedTargetOffset.m_X)) { targetOffset = m_Parent->RotateOffset(m_IdleOffset); } Vector distanceFromTargetOffsetToAnkleOffset(targetOffset - m_AnkleOffset); m_AnkleOffset += distanceFromTargetOffsetToAnkleOffset * m_MoveSpeed;