Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Customized] Customized Vehicle Turret Rotation #1480

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ This page lists all the individual contributions to the project by their author.
- Fix `LimboKill` not working reliably
- Allow using waypoints, area guard and attack move with aircraft
- Fix `Stop` command not working so well in some cases
- Customized Vehicle Turret Rotation
- **Ollerus**
- Build limit group enhancement
- Customizable rocker amplitude
Expand Down
1 change: 1 addition & 0 deletions Phobos.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
<ClCompile Include="src\Ext\Techno\Hooks.ReceiveDamage.cpp" />
<ClCompile Include="src\Ext\Techno\Hooks.TargetEvaluation.cpp" />
<ClCompile Include="src\Ext\Techno\Hooks.Transport.cpp" />
<ClCompile Include="src\Ext\Techno\Hooks.Facing.cpp" />
<ClCompile Include="src\Ext\TerrainType\Body.cpp" />
<ClCompile Include="src\Ext\TerrainType\Hooks.cpp" />
<ClCompile Include="src\Ext\TerrainType\Hooks.Passable.cpp" />
Expand Down
38 changes: 38 additions & 0 deletions docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -1366,6 +1366,44 @@ Convert.HumanToComputer = ; TechnoType
Convert.ComputerToHuman = ; TechnoType
```

### Customized Vehicle Turret Rotation

- When `ExpandTurretRotation` is set to true, the following functions will be enabled.
- Units with turret without `TurretSpins=true` can looks more vivid when it is in idle.
- `Turret.IdleRotate` controls whether units can rotate their turrets when in idle. Defaults to `[AudioVisual]` -> `Turret.IdleRotate`.
- `Turret.PointToMouse` controls whether units will turn their turrets to your mouse when in idle. Defaults to `[AudioVisual]` -> `Turret.PointToMouse`.
- `Turret.IdleRestartMin` and `Turret.IdleRestartMax` control the delay from idle to action occurrence together.
- `Turret.IdleIntervalMin` and `Turret.IdleIntervalMax` control the delay between every idle actions together.
- The turret and body of the units can now be rotated under control.
- `Turret.Restriction` defines the angle at which the turret can be turned to both sides.
- `Turret.ExtraAngle` defines the additional rotation angle of the turret, and `Turret.Restriction` will also rotate with this value. The positive number is clockwise and the negative number is counterclockwise, that is, what angle the turret should use by default or face the target.
- `Turret.BodyOrientation` controls whether the body needs to aim at the target when firing.
- `Turret.BodyOrientationAngle` defines the additional rotation angle of the body when aiming at the target. The positive number is clockwise and the negative number is counterclockwise, that is, what angle the body should use to face the target.
- `Turret.BodyOrientationSymmetric` controls whether both sides of the additional body rotation angle can be used.

In `rulesmd.ini`:
```ini
[General]
ExpandTurretRotation=no ; boolean

[AudioVisual]
Turret.IdleRotate=false ; boolean
Turret.PointToMouse=false ; boolean
Turret.IdleRestartMin=150 ; integer, number of frames
Turret.IdleRestartMax=300 ; integer, number of frames
Turret.IdleIntervalMin=150 ; integer, number of frames
Turret.IdleIntervalMax=450 ; integer, number of frames

[SOMEUNIT] ; VehicleType, `Turret=yes`
Turret.IdleRotate= ; boolean
Turret.PointToMouse= ; boolean
Turret.Restriction=180.0 ; floating point value
Turret.ExtraAngle=0 ; floating point value
Turret.BodyOrientation=no ; boolean
Turret.BodyOrientationAngle=0 ; floating point value
Turret.BodyOrientationSymmetric=yes ; boolean
```

## Terrain

### Destroy animation & sound
Expand Down
1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ New:
- Waypoint path is drawn for all units under player control or if `[GlobalControls]->DebugPlanningPaths=yes` (by Trsdy)
- `RemoveDisguise` now works on vehicle disguises (by Trsdy)
- Allow anchoring extended tooltips to the left side of the sidebar (by Trsdy)
- Customized Vehicle Turret Rotation (by CrimRecya)
- Toggle to allow spawned aircraft to attack immediately after being spawned (by Starkku)
- `ZAdjust` for OverlayTypes (by Starkku)
- Allow customizing extra tint intensity for Iron Curtain & Force Shield (by Starkku)
Expand Down
15 changes: 15 additions & 0 deletions src/Ext/Rules/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,14 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI)
this->Vehicles_DefaultDigitalDisplayTypes.Read(exINI, GameStrings::AudioVisual, "Vehicles.DefaultDigitalDisplayTypes");
this->Aircraft_DefaultDigitalDisplayTypes.Read(exINI, GameStrings::AudioVisual, "Aircraft.DefaultDigitalDisplayTypes");

this->ExpandTurretRotation.Read(exINI, GameStrings::General, "ExpandTurretRotation");
this->Turret_IdleRotate.Read(exINI, GameStrings::AudioVisual, "Turret.IdleRotate");
this->Turret_PointToMouse.Read(exINI, GameStrings::AudioVisual, "Turret.PointToMouse");
this->Turret_IdleRestartMin.Read(exINI, GameStrings::AudioVisual, "Turret.IdleRestartMin");
this->Turret_IdleRestartMax.Read(exINI, GameStrings::AudioVisual, "Turret.IdleRestartMax");
this->Turret_IdleIntervalMin.Read(exINI, GameStrings::AudioVisual, "Turret.IdleIntervalMin");
this->Turret_IdleIntervalMax.Read(exINI, GameStrings::AudioVisual, "Turret.IdleIntervalMax");

this->AircraftLevelLightMultiplier.Read(exINI, GameStrings::AudioVisual, "AircraftLevelLightMultiplier");
this->JumpjetLevelLightMultiplier.Read(exINI, GameStrings::AudioVisual, "JumpjetLevelLightMultiplier");

Expand Down Expand Up @@ -376,6 +384,13 @@ void RulesExt::ExtData::Serialize(T& Stm)
.Process(this->Infantry_DefaultDigitalDisplayTypes)
.Process(this->Vehicles_DefaultDigitalDisplayTypes)
.Process(this->Aircraft_DefaultDigitalDisplayTypes)
.Process(this->ExpandTurretRotation)
.Process(this->Turret_IdleRotate)
.Process(this->Turret_PointToMouse)
.Process(this->Turret_IdleRestartMin)
.Process(this->Turret_IdleRestartMax)
.Process(this->Turret_IdleIntervalMin)
.Process(this->Turret_IdleIntervalMax)
.Process(this->ShowDesignatorRange)
.Process(this->DropPodTrailer)
.Process(this->PodImage)
Expand Down
15 changes: 15 additions & 0 deletions src/Ext/Rules/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,14 @@ class RulesExt
ValueableVector<DigitalDisplayTypeClass*> Vehicles_DefaultDigitalDisplayTypes;
ValueableVector<DigitalDisplayTypeClass*> Aircraft_DefaultDigitalDisplayTypes;

Valueable<bool> ExpandTurretRotation;
Valueable<bool> Turret_IdleRotate;
Valueable<bool> Turret_PointToMouse;
Valueable<int> Turret_IdleRestartMin;
Valueable<int> Turret_IdleRestartMax;
Valueable<int> Turret_IdleIntervalMin;
Valueable<int> Turret_IdleIntervalMax;

Valueable<bool> ShowDesignatorRange;
Valueable<bool> IsVoiceCreatedGlobal;
Valueable<int> SelectionFlashDuration;
Expand Down Expand Up @@ -272,6 +280,13 @@ class RulesExt
, Infantry_DefaultDigitalDisplayTypes {}
, Vehicles_DefaultDigitalDisplayTypes {}
, Aircraft_DefaultDigitalDisplayTypes {}
, ExpandTurretRotation { false }
, Turret_IdleRotate { false }
, Turret_PointToMouse { false }
, Turret_IdleRestartMin { 150 }
, Turret_IdleRestartMax { 300 }
, Turret_IdleIntervalMin { 150 }
, Turret_IdleIntervalMax { 450 }
, ShowDesignatorRange { true }
, DropPodTrailer { }
, PodImage { }
Expand Down
117 changes: 117 additions & 0 deletions src/Ext/Techno/Body.Update.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include <Utilities/EnumFunctions.h>
#include <Utilities/AresFunctions.h>

#include <WWMouseClass.h>
#include <TacticalClass.h>

// TechnoClass_AI_0x6F9E50
// It's not recommended to do anything more here it could have a better place for performance consideration
Expand Down Expand Up @@ -588,6 +590,121 @@ void TechnoExt::ExtData::UpdateMindControlAnim()
}
}

void TechnoExt::ExtData::StopIdleAction()
{
if (this->UnitIdleActionTimer.IsTicking())
this->UnitIdleActionTimer.Stop();

if (this->UnitIdleActionGapTimer.IsTicking())
{
this->UnitIdleActionGapTimer.Stop();
const auto pTypeExt = this->TypeExtData;
this->StopRotateWithNewROT(pTypeExt->TurretROT.Get(pTypeExt->OwnerObject()->ROT));
}
}

void TechnoExt::ExtData::ApplyIdleAction()
{
const auto pThis = this->OwnerObject();
const auto turret = &pThis->SecondaryFacing;

if (this->UnitIdleActionTimer.Completed()) // Set first direction
{
this->UnitIdleActionTimer.Stop();
this->UnitIdleActionGapTimer.Start(ScenarioClass::Instance->Random.RandomRanged(RulesExt::Global()->Turret_IdleIntervalMin, RulesExt::Global()->Turret_IdleIntervalMax));
bool noTurn = false;

if (const auto pUnit = abstract_cast<UnitClass*>(pThis))
noTurn = pUnit->BunkerLinkedItem || !pUnit->Type->Speed || (pUnit->Type->IsSimpleDeployer && pUnit->Deployed);
else if (pThis->WhatAmI() == AbstractType::Building)
noTurn = true;

const auto raw = static_cast<short>(ScenarioClass::Instance->Random.RandomRanged(0, 65535) - 32768);
const auto pTypeExt = this->TypeExtData;

this->StopRotateWithNewROT(ScenarioClass::Instance->Random.RandomRanged(2,4) >> 1);
turret->SetDesired(pTypeExt->GetTurretDesiredDir(DirStruct { (pTypeExt->GetTurretLimitedRaw(noTurn ? raw : (raw / 4)) + static_cast<short>(pThis->PrimaryFacing.Current().Raw)) }));
}
else if (this->UnitIdleActionGapTimer.IsTicking()) // Check change direction
{
if (!this->UnitIdleActionGapTimer.HasTimeLeft()) // Set next direction
{
this->UnitIdleActionGapTimer.Start(ScenarioClass::Instance->Random.RandomRanged(RulesExt::Global()->Turret_IdleIntervalMin, RulesExt::Global()->Turret_IdleIntervalMax));
bool noTurn = false;

if (const auto pUnit = abstract_cast<UnitClass*>(pThis))
noTurn = pUnit->BunkerLinkedItem || !pUnit->Type->Speed || (pUnit->Type->IsSimpleDeployer && pUnit->Deployed);
else if (pThis->WhatAmI() == AbstractType::Building)
noTurn = true;

const auto raw = static_cast<short>(ScenarioClass::Instance->Random.RandomRanged(0, 65535) - 32768);
const auto pTypeExt = this->TypeExtData;

this->StopRotateWithNewROT(ScenarioClass::Instance->Random.RandomRanged(2,4) >> 1);
turret->SetDesired(pTypeExt->GetTurretDesiredDir(DirStruct { (pTypeExt->GetTurretLimitedRaw(noTurn ? raw : (raw / 4)) + static_cast<short>(pThis->PrimaryFacing.Current().Raw)) }));
}
}
else if (!this->UnitIdleActionTimer.IsTicking()) // In idle now
{
this->UnitIdleActionTimer.Start(ScenarioClass::Instance->Random.RandomRanged(RulesExt::Global()->Turret_IdleRestartMin, RulesExt::Global()->Turret_IdleRestartMax));
bool noTurn = false;

if (const auto pUnit = abstract_cast<UnitClass*>(pThis))
noTurn = pUnit->BunkerLinkedItem || !pUnit->Type->Speed || (pUnit->Type->IsSimpleDeployer && pUnit->Deployed);
else if (pThis->WhatAmI() == AbstractType::Building)
noTurn = true;

if (!noTurn)
turret->SetDesired(this->TypeExtData->GetTurretDesiredDir(pThis->PrimaryFacing.Current()));
}
}

void TechnoExt::ExtData::ManualIdleAction()
{
const auto pThis = this->OwnerObject();
const auto turret = &pThis->SecondaryFacing;

if (pThis->IsSelected)
{
const auto pTypeExt = this->TypeExtData;

if (pTypeExt->Turret_IdleRotate.Get(RulesExt::Global()->Turret_IdleRotate))
this->StopIdleAction();

this->UnitIdleIsSelected = true;
const auto mouseCoords = TacticalClass::Instance->ClientToCoords(WWMouseClass::Instance->XY1);

if (mouseCoords != CoordStruct::Empty) // Mouse in tactical
{
const auto offset = -static_cast<int>(pThis->GetCoords().Z * 1.2307692307692307692307692307692); // ((Unsorted::LeptonsPerCell / 2) / Unsorted::LevelHeight)
const auto targetDir = pThis->GetTargetDirection(MapClass::Instance->GetCellAt(CoordStruct { mouseCoords.X - offset, mouseCoords.Y - offset, 0 }));

if (const auto pFoot = abstract_cast<FootClass*>(pThis))
pTypeExt->SetTurretLimitedDir(pFoot, targetDir);
else
turret->SetDesired(pTypeExt->GetTurretDesiredDir(targetDir));
}
}
else if (this->UnitIdleIsSelected) // Immediately stop when is not selected
{
this->UnitIdleIsSelected = false;
this->StopRotateWithNewROT();
}
}

void TechnoExt::ExtData::StopRotateWithNewROT(int ROT)
{
const auto turret = &this->OwnerObject()->SecondaryFacing;

const auto currentFacingDirection = turret->Current();
turret->DesiredFacing = currentFacingDirection;
turret->StartFacing = currentFacingDirection;
turret->RotationTimer.Start(0);

if (ROT >= 0)
turret->SetROT(ROT);
}

void TechnoExt::ApplyGainedSelfHeal(TechnoClass* pThis)
{
if (!RulesExt::Global()->GainSelfHealAllowMultiplayPassive && pThis->Owner->Type->MultiplayPassive)
Expand Down
3 changes: 3 additions & 0 deletions src/Ext/Techno/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,9 @@ void TechnoExt::ExtData::Serialize(T& Stm)
.Process(this->LastRearmWasFullDelay)
.Process(this->CanCloakDuringRearm)
.Process(this->WHAnimRemainingCreationInterval)
.Process(this->UnitIdleIsSelected)
.Process(this->UnitIdleActionTimer)
.Process(this->UnitIdleActionGapTimer)
.Process(this->FiringObstacleCell)
.Process(this->IsDetachingForCloak)
.Process(this->OriginalPassengerOwner)
Expand Down
10 changes: 10 additions & 0 deletions src/Ext/Techno/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ class TechnoExt
bool CanCloakDuringRearm; // Current rearm timer was started by DecloakToFire=no weapon.
int WHAnimRemainingCreationInterval;
bool CanCurrentlyDeployIntoBuilding; // Only set on UnitClass technos with DeploysInto set in multiplayer games, recalculated once per frame so no need to serialize.
bool UnitIdleIsSelected;
CDTimerClass UnitIdleActionTimer;
CDTimerClass UnitIdleActionGapTimer;
CellClass* FiringObstacleCell; // Set on firing if there is an obstacle cell between target and techno, used for updating WaveClass target etc.
bool IsDetachingForCloak; // Used for checking animation detaching, set to true before calling Detach_All() on techno when this anim is attached to and to false after when cloaking only.

Expand Down Expand Up @@ -86,6 +89,9 @@ class TechnoExt
, CanCloakDuringRearm { false }
, WHAnimRemainingCreationInterval { 0 }
, CanCurrentlyDeployIntoBuilding { false }
, UnitIdleIsSelected { false }
, UnitIdleActionTimer {}
, UnitIdleActionGapTimer {}
, FiringObstacleCell {}
, IsDetachingForCloak { false }
, OriginalPassengerOwner {}
Expand Down Expand Up @@ -115,6 +121,10 @@ class TechnoExt
void UpdateSelfOwnedAttachEffects();
bool HasAttachedEffects(std::vector<AttachEffectTypeClass*> attachEffectTypes, bool requireAll, bool ignoreSameSource, TechnoClass* pInvoker, AbstractClass* pSource, std::vector<int> const* minCounts, std::vector<int> const* maxCounts) const;
int GetAttachedEffectCumulativeCount(AttachEffectTypeClass* pAttachEffectType, bool ignoreSameSource = false, TechnoClass* pInvoker = nullptr, AbstractClass* pSource = nullptr) const;
void StopIdleAction();
void ApplyIdleAction();
void ManualIdleAction();
void StopRotateWithNewROT(int ROT = -1);

virtual ~ExtData() override;
virtual void InvalidatePointer(void* ptr, bool bRemoved) override { }
Expand Down
Loading
Loading