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

Trajectory projectiles enhancement #1246

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,9 @@ This page lists all the individual contributions to the project by their author.
- Re-enable the Veinhole Monster and Weeds from TS
- Recreate the weed-charging of SWs like the TS Chemical Missile
- Allow to change the speed of gas particles
- **NaotoYuuki**
- Allow trajectory projectiles to use `Inaccurate` & `BallisticScatter` keys
- Vertical & meteor trajectory projectile prototypes
- **Ares developers**
- YRpp and Syringe which are used, save/load, project foundation and generally useful code from Ares
- unfinished RadTypes code
Expand All @@ -343,6 +346,7 @@ This page lists all the individual contributions to the project by their author.
- **SukaHati (Erzoid)** - Minimum interceptor guard range
- **E1 Elite** - TileSet 255 and above bridge repair fix
- **AutoGavy** - interceptor logic, Warhead critical hit logic
- **Ollerus** - Enhance bombard trajectory with vertical & meteor trajectory
- **Chasheen (Chasheenburg)** - CN docs help
- **tomsons26** - all-around help, assistance and guidance in reverse-engineering, YR binary mappings
- **CCHyper** - all-around help, current project logo, assistance and guidance in reverse-engineering, YR binary mappings, custom locomotors example implementation
Expand Down
25 changes: 21 additions & 4 deletions docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -653,13 +653,30 @@ Trajectory.Straight.PassThrough=false ; boolean

#### Bombard trajectory

- Similar trajectory to `Straight`, but targets a coordinate above the intended target (height determined by `Trajectory.Bombard.Height`). When the projectile approaches that coordinate, it will free fall and explodes when it hits the target or ground.
- Similar trajectory to `Straight`, but targets a coordinate between the attacker and intended target first. When the projectile approaches that turning point, it'll turn to the intended target and explodes when it hits the target or ground.
- `Trajectory.Bombard.Height` controls the height of the turning point.
- `Trajectory.Bombard.FallPercent` controls the distance of the turning point by its percentage of the total distance between attacker and intended target. If set to 0%, then it'll fly up vertically. If set to 100%, then it'll travel to the top of the intended target.
- For each launch the turning point percentage could add or minus a random value, which is not greater than `Trajectory.Bombard.FallPercentShift`. If set to 0%, random shift will be disabled.
- You can also makes the turning point scatter randomly in a circle with `Trajectory.Bombard.FallScatterRange` as its radius. If set to 0, random scatter will be disabled.
- `Trajectory.Bombard.FreeFallOnTarget` controls how it'll hit the intended target. If set to true, the projectile will be respawned above the intended target and free fall. If set to false, the projectile will travel to the intended target from the turning point.
- `Trajectory.Bombard.NoLaunch` controls whether the attacker will fire the projectile by itself. If set to true, projectile will directly fall from the turning point.
- `Trajectory.Bombard.FallSpeed` controls the initial speed of the projectile after it turns. If set to 0.0, then it'll use `Trajectory.Speed`. Can't work together with `Trajectory.Bombard.FreeFallOnTarget=true`.
- `Trajectory.Bombard.TargetSnapDistance` controls the maximum distance in cells from intended target the projectile can be at moment of detonation to make the projectile 'snap' on the intended target. Set to 0 to disable snapping.
- `Trajectory.Bombard.TurningPointAnim`, if set, will play an anim when the projectile reaches the turning point. If `Trajectory.Bombard.FreeFallOnTarget` is set to true, it'll be spawned above the target with the projectile together. If `Trajectory.Bombard.NoLaunch` is set to true, it'll be played at where the projectile falls, no matter if it's free fall or not.

In `rulesmd.ini`:
```ini
[SOMEPROJECTILE] ; Projectile
Trajectory=Bombard ; Trajectory type
Trajectory.Bombard.Height=0.0 ; double
[SOMEPROJECTILE] ; Projectile
Trajectory=Bombard ; Trajectory type
Trajectory.Bombard.Height=0.0 ; double
Trajectory.Bombard.FallPercent=1.0 ; double
Trajectory.Bombard.FallPercentShift=0.0 ; double
Trajectory.Bombard.FallScatterRange=0.0 ; floating point value
Trajectory.Bombard.FreeFallOnTarget=true ; boolean
Trajectory.Bombard.NoLaunch=false ; boolean
Trajectory.Bombard.FallSpeed=0.0 ; double
Trajectory.Bombard.TargetSnapDistance=0.5 ; floating point value
Trajectory.Bombard.TurningPointAnim= ; Animation
```

### Shrapnel enhancements
Expand Down
2 changes: 2 additions & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,8 @@ New:
- Allow customizing Aircraft weapon strafing regardless of `ROT` and `Strafing.Shots` values beyond 5 (by Trsdy)
- Allow strafing weapons to deduct ammo per shot instead of per strafing run (by Starkku)
- Allow `CloakVisible=true` laser trails optinally be seen only if unit is detected (by Starkku)
- Allow trajectory projectiles to use `Inaccurate` & `BallisticScatter` keys (by NaotoYuuki)
- Enhance bombard trajectory with vertical and meteor trajectory (by NaotoYuuki & Ollerus)

Vanilla fixes:
- Allow AI to repair structures built from base nodes/trigger action 125/SW delivery in single player missions (by Trsdy)
Expand Down
247 changes: 232 additions & 15 deletions src/Ext/Bullet/Trajectories/BombardTrajectory.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include "BombardTrajectory.h"

#include <Ext/BulletType/Body.h>
#include <AnimClass.h>
#include <Ext/Bullet/Body.h>

PhobosTrajectory* BombardTrajectoryType::CreateInstance() const
{
Expand All @@ -10,20 +10,52 @@ PhobosTrajectory* BombardTrajectoryType::CreateInstance() const
bool BombardTrajectoryType::Load(PhobosStreamReader& Stm, bool RegisterForChange)
{
this->PhobosTrajectoryType::Load(Stm, false);
Stm.Process(this->Height, false);
Stm
.Process(this->Height, false)
.Process(this->FallPercent, false)
.Process(this->FallPercentShift, false)
.Process(this->FallScatterRange, false)
.Process(this->FallSpeed, false)
.Process(this->TargetSnapDistance, false)
.Process(this->FreeFallOnTarget, false)
.Process(this->NoLaunch, false)
.Process(this->TurningPointAnim, false)
;

return true;
}

bool BombardTrajectoryType::Save(PhobosStreamWriter& Stm) const
{
this->PhobosTrajectoryType::Save(Stm);
Stm.Process(this->Height);
Stm
.Process(this->Height)
.Process(this->FallPercent)
.Process(this->FallPercentShift)
.Process(this->FallScatterRange)
.Process(this->FallSpeed)
.Process(this->TargetSnapDistance)
.Process(this->FreeFallOnTarget)
.Process(this->NoLaunch)
.Process(this->TurningPointAnim)
;

return true;
}

void BombardTrajectoryType::Read(CCINIClass* const pINI, const char* pSection)
{
this->Height = pINI->ReadDouble(pSection, "Trajectory.Bombard.Height", 0.0);
INI_EX exINI(pINI);

this->Height.Read(exINI, pSection, "Trajectory.Bombard.Height");
this->FallPercent.Read(exINI, pSection, "Trajectory.Bombard.FallPercent");
this->FallPercentShift.Read(exINI, pSection, "Trajectory.Bombard.FallPercentShift");
this->FallScatterRange.Read(exINI, pSection, "Trajectory.Bombard.FallScatterRange");
this->FallSpeed.Read(exINI, pSection, "Trajectory.Bombard.FallSpeed");
this->TargetSnapDistance.Read(exINI, pSection, "Trajectory.Bombard.TargetSnapDistance");
this->FreeFallOnTarget.Read(exINI, pSection, "Trajectory.Bombard.FreeFallOnTarget");
this->NoLaunch.Read(exINI, pSection, "Trajectory.Bombard.NoLaunch");
this->TurningPointAnim.Read(exINI, pSection, "Trajectory.Bombard.TurningPointAnim");
}

bool BombardTrajectory::Load(PhobosStreamReader& Stm, bool RegisterForChange)
Expand All @@ -33,6 +65,14 @@ bool BombardTrajectory::Load(PhobosStreamReader& Stm, bool RegisterForChange)
Stm
.Process(this->IsFalling)
.Process(this->Height)
.Process(this->RemainingDistance)
.Process(this->FallPercent)
.Process(this->FallPercentShift)
.Process(this->FallScatterRange)
.Process(this->FallSpeed)
.Process(this->TargetSnapDistance)
.Process(this->FreeFallOnTarget)
.Process(this->NoLaunch)
;

return true;
Expand All @@ -45,19 +85,103 @@ bool BombardTrajectory::Save(PhobosStreamWriter& Stm) const
Stm
.Process(this->IsFalling)
.Process(this->Height)
.Process(this->RemainingDistance)
.Process(this->FallPercent)
.Process(this->FallPercentShift)
.Process(this->FallScatterRange)
.Process(this->FallSpeed)
.Process(this->TargetSnapDistance)
.Process(this->FreeFallOnTarget)
.Process(this->NoLaunch)
;

return true;
}

void BombardTrajectory::OnUnlimbo(BulletClass* pBullet, CoordStruct* pCoord, BulletVelocity* pVelocity)
{
this->Height = this->GetTrajectoryType<BombardTrajectoryType>(pBullet)->Height + pBullet->TargetCoords.Z;
auto const pType = this->GetTrajectoryType<BombardTrajectoryType>(pBullet);
this->Height = pType->Height + pBullet->TargetCoords.Z;

// use scaling since RandomRanged only support int
this->FallPercentShift = pType->FallPercentShift;
double fallPercentShift = ScenarioClass::Instance->Random.RandomRanged(0, static_cast<int>(200 * this->FallPercentShift)) / 100.0;
this->FallPercent = pType->FallPercent - this->FallPercentShift + fallPercentShift;

this->FallScatterRange = pType->FallScatterRange;
this->FallSpeed = pType->FallSpeed ? pType->FallSpeed : this->GetTrajectorySpeed(pBullet);
this->TargetSnapDistance = pType->TargetSnapDistance;
this->FreeFallOnTarget = pType->FreeFallOnTarget;
this->NoLaunch = pType->NoLaunch;

if (pBullet->Type->Inaccurate)
{
auto const pTypeExt = BulletTypeExt::ExtMap.Find(pBullet->Type);

int ballisticScatter = RulesClass::Instance->BallisticScatter;
int scatterMax = (int)(pTypeExt->BallisticScatter_Max.Get(Leptons { ballisticScatter }));
int scatterMin = (int)(pTypeExt->BallisticScatter_Min.Get(Leptons { scatterMax / 2 }));

pBullet->Velocity.X = static_cast<double>(pBullet->TargetCoords.X - pBullet->SourceCoords.X);
pBullet->Velocity.Y = static_cast<double>(pBullet->TargetCoords.Y - pBullet->SourceCoords.Y);
pBullet->Velocity.Z = static_cast<double>(this->Height - pBullet->SourceCoords.Z);
pBullet->Velocity *= this->GetTrajectorySpeed(pBullet) / pBullet->Velocity.Magnitude();
double random = ScenarioClass::Instance->Random.RandomRanged(scatterMin, scatterMax);
double theta = ScenarioClass::Instance->Random.RandomDouble() * Math::TwoPi;

CoordStruct offset
{
static_cast<int>(random * Math::cos(theta)),
static_cast<int>(random * Math::sin(theta)),
0
};
pBullet->TargetCoords += offset;
}

if (!this->NoLaunch)
{
pBullet->Velocity.X = static_cast<double>(pBullet->TargetCoords.X - pBullet->SourceCoords.X) * this->FallPercent;
pBullet->Velocity.Y = static_cast<double>(pBullet->TargetCoords.Y - pBullet->SourceCoords.Y) * this->FallPercent;
pBullet->Velocity.Z = static_cast<double>(this->Height - pBullet->SourceCoords.Z);
pBullet->Velocity *= this->GetTrajectorySpeed(pBullet) / pBullet->Velocity.Magnitude();
}
else
{
this->IsFalling = true;
CoordStruct SourceLocation;
SourceLocation.Z = static_cast<int>(this->Height - pBullet->SourceCoords.Z);
int scatterRange = static_cast<int>(this->FallScatterRange);
double angel = ScenarioClass::Instance->Random.RandomDouble() * Math::TwoPi;
double length = ScenarioClass::Instance->Random.RandomRanged(-scatterRange, scatterRange);
int scatterX = static_cast<int>(length * Math::cos(angel));
int scatterY = static_cast<int>(length * Math::sin(angel));

if (!this->FreeFallOnTarget)
{
SourceLocation.X = pBullet->SourceCoords.X + static_cast<int>((pBullet->TargetCoords.X - pBullet->SourceCoords.X) * this->FallPercent) + scatterX;
SourceLocation.Y = pBullet->SourceCoords.Y + static_cast<int>((pBullet->TargetCoords.Y - pBullet->SourceCoords.Y) * this->FallPercent) + scatterY;
pBullet->Limbo();
pBullet->Unlimbo(SourceLocation, DirType::North);
pBullet->Velocity.X = static_cast<double>(pBullet->TargetCoords.X - SourceLocation.X);
pBullet->Velocity.Y = static_cast<double>(pBullet->TargetCoords.Y - SourceLocation.Y);
pBullet->Velocity.Z = static_cast<double>(pBullet->TargetCoords.Z - SourceLocation.Z);
pBullet->Velocity *= this->FallSpeed / pBullet->Velocity.Magnitude();
}
else
{
SourceLocation.X = pBullet->TargetCoords.X + scatterX;
SourceLocation.Y = pBullet->TargetCoords.Y + scatterY;
pBullet->Limbo();
pBullet->Unlimbo(SourceLocation, DirType::North);
pBullet->Velocity.X = 0.0;
pBullet->Velocity.Y = 0.0;
pBullet->Velocity.Z = 0.0;
}

this->RemainingDistance = static_cast<int>(pBullet->TargetCoords.DistanceFrom(SourceLocation) + this->FallSpeed);
if (auto turningPointAnim = pType->TurningPointAnim.Get())
{
auto const pAnim = GameCreate<AnimClass>(turningPointAnim, SourceLocation);
pAnim->SetOwnerObject(pBullet->Owner);
pAnim->Owner = pBullet->Owner ? pBullet->Owner->Owner : BulletExt::ExtMap.Find(pBullet)->FirerHouse;
}
}
}

bool BombardTrajectory::OnAI(BulletClass* pBullet)
Expand All @@ -66,25 +190,118 @@ bool BombardTrajectory::OnAI(BulletClass* pBullet)
if (pBullet->TargetCoords.DistanceFrom(pBullet->Location) < 100) // This value maybe adjusted?
return true;

// Extra check for trajectory falling
auto const pOwner = pBullet->Owner ? pBullet->Owner->Owner : BulletExt::ExtMap.Find(pBullet)->FirerHouse;

if (this->IsFalling && !this->FreeFallOnTarget && BulletDetonatePreCheck(pBullet, pOwner, this->FallSpeed))
return true;

return false;
}

void BombardTrajectory::OnAIPreDetonate(BulletClass* pBullet)
{
auto pTarget = abstract_cast<ObjectClass*>(pBullet->Target);
auto pCoords = pTarget ? pTarget->GetCoords() : pBullet->Data.Location;

if (pCoords.DistanceFrom(pBullet->Location) <= this->TargetSnapDistance)
{
auto const pExt = BulletExt::ExtMap.Find(pBullet);
pExt->SnappedToTarget = true;
pBullet->SetLocation(pCoords);
}
}

void BombardTrajectory::OnAIVelocity(BulletClass* pBullet, BulletVelocity* pSpeed, BulletVelocity* pPosition)
{
auto const trajType = this->GetTrajectoryType<BombardTrajectoryType>(pBullet);
if (!this->IsFalling)
{
pSpeed->Z += BulletTypeExt::GetAdjustedGravity(pBullet->Type);
if (pBullet->Location.Z + pBullet->Velocity.Z >= this->Height)
{
this->IsFalling = true;
pSpeed->X = 0.0;
pSpeed->Y = 0.0;
pSpeed->Z = 0.0;
pPosition->X = pBullet->TargetCoords.X;
pPosition->Y = pBullet->TargetCoords.Y;
if (!this->FreeFallOnTarget)
{
pSpeed->X = static_cast<double>(pBullet->TargetCoords.X - pBullet->Location.X - pBullet->Velocity.X);
pSpeed->Y = static_cast<double>(pBullet->TargetCoords.Y - pBullet->Location.Y - pBullet->Velocity.Y);
pSpeed->Z = static_cast<double>(pBullet->TargetCoords.Z - pBullet->Location.Z - pBullet->Velocity.Z);
(*pSpeed) *= this->FallSpeed / pSpeed->Magnitude();
pPosition->X = pBullet->Location.X + pBullet->Velocity.X;
pPosition->Y = pBullet->Location.Y + pBullet->Velocity.Y;
pPosition->Z = pBullet->Location.Z + pBullet->Velocity.Z;
}
else
{
pSpeed->X = 0.0;
pSpeed->Y = 0.0;
pSpeed->Z = 0.0;

if (this->FallPercent != 1.0) // change position and recreate laser trail
Coronia marked this conversation as resolved.
Show resolved Hide resolved
{
auto pExt = BulletExt::ExtMap.Find(pBullet);
pExt->LaserTrails.clear();
CoordStruct target = pBullet->TargetCoords;
target.Z += static_cast<int>(trajType->Height);
pBullet->Limbo();
pBullet->Unlimbo(target, DirType::North);
pPosition->X = pBullet->TargetCoords.X;
pPosition->Y = pBullet->TargetCoords.Y;
pPosition->Z = pBullet->TargetCoords.Z + this->GetTrajectoryType<BombardTrajectoryType>(pBullet)->Height;

if (auto pTypeExt = BulletTypeExt::ExtMap.Find(pBullet->Type))
{
auto pThis = pExt->OwnerObject();
auto pOwner = pThis->Owner ? pThis->Owner->Owner : nullptr;

for (auto const& idxTrail : pTypeExt->LaserTrail_Types)
{
if (auto const pLaserType = LaserTrailTypeClass::Array[idxTrail].get())
pExt->LaserTrails.push_back(LaserTrailClass { pLaserType, pOwner });
}
}
}
else
{
pPosition->X = pBullet->TargetCoords.X;
pPosition->Y = pBullet->TargetCoords.Y;
}
}

CoordStruct BulletLocation {
static_cast<int>(pPosition->X),
static_cast<int>(pPosition->Y),
static_cast<int>(pPosition->Z)
};
this->RemainingDistance = static_cast<int>(pBullet->TargetCoords.DistanceFrom(BulletLocation) + this->FallSpeed);
if (auto const turningPointAnim = trajType->TurningPointAnim.Get())
{
auto const pAnim = GameCreate<AnimClass>(turningPointAnim, BulletLocation);
pAnim->SetOwnerObject(pBullet->Owner);
pAnim->Owner = pBullet->Owner ? pBullet->Owner->Owner : BulletExt::ExtMap.Find(pBullet)->FirerHouse;
}
}
}
else if (!this->FreeFallOnTarget)
{
pSpeed->Z += BulletTypeExt::GetAdjustedGravity(pBullet->Type);
}
}

bool BombardTrajectory::BulletDetonatePreCheck(BulletClass* pBullet, HouseClass* pOwner, double StraightSpeed)
{
this->RemainingDistance -= static_cast<int>(StraightSpeed);

if (this->RemainingDistance < 0)
return true;

if (this->RemainingDistance < StraightSpeed)
{
pBullet->Velocity *= this->RemainingDistance / StraightSpeed;
this->RemainingDistance = 0;
}

return false;
}

TrajectoryCheckReturnType BombardTrajectory::OnAITargetCoordCheck(BulletClass* pBullet)
Expand Down
Loading
Loading