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

Add support for limited traverse rate guns #95

Open
wants to merge 49 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
9d69187
Add new ship AI setting: 'useAllPrimaryWeapons'.
exinfinitum Nov 9, 2020
1d07241
Fix LinkedFireSelected for NPCs
exinfinitum Nov 15, 2020
19dfcb1
Initial commit for 'targetCriteria', still need to add save file stuff
exinfinitum Dec 17, 2020
6a40cd0
Merge branch 'weaponTargetCriteria' into useAllPrimaryWeapons
exinfinitum Dec 19, 2020
8717559
Finish up remaining tasks for 'targetCriteria
exinfinitum Dec 19, 2020
976721c
Merge branch 'master' into useAllPrimaryWeapons
exinfinitum Dec 28, 2020
56da1d1
Add file CWeaponTargetDefinition
exinfinitum Dec 28, 2020
b7bb162
Add NPC support for targetCriteria
exinfinitum Dec 20, 2020
9976d52
Oops...forgot to require secondaryWeapon for gun to autofire on player
exinfinitum Dec 31, 2020
ed5d2b8
Fix bug with repeating weapons autofiring
exinfinitum Jan 1, 2021
a1d055a
Make targetCriteria actually work for npcs
exinfinitum Jan 2, 2021
88b6a6a
Fix bug with repeating weapons autofiring
exinfinitum Jan 1, 2021
8d471d2
Make targetCriteria actually work for npcs
exinfinitum Jan 2, 2021
ced8f1e
Merge remote-tracking branch 'origin/master' into useAllPrimaryWeapons
exinfinitum Jan 2, 2021
5b71bce
Implement new aiming code for UseAllPrimaryWeapons
exinfinitum Jan 7, 2021
349a785
Merge branch 'useAllPrimaryWeapons' of github.com:exinfinitum/Transce…
exinfinitum Jan 7, 2021
c56b9b5
Merge branch 'master' into useAllPrimaryWeapons
exinfinitum Jan 24, 2021
0929c84
Merge branch 'weaponTargetCriteria' into useAllPrimaryWeapons
exinfinitum Feb 24, 2021
0df3c17
WIP commit for charging guns
exinfinitum Dec 14, 2020
6d80636
More work on charging guns, squash when done
exinfinitum Jan 18, 2021
2983bdb
Complete weapon charge effect
exinfinitum Mar 7, 2021
bf1c403
Add playSoundOncePerBurst option
exinfinitum Mar 7, 2021
20a3816
Fix aFireTarget not being set properly in charging weapons
exinfinitum Mar 8, 2021
a76a75b
Fix formatting
exinfinitum Mar 8, 2021
9392915
Fix charging guns firing randomly on NPC ships
exinfinitum Mar 12, 2021
595b783
Use ship counter when charging guns
exinfinitum Mar 14, 2021
029a1c4
Revert CHexarcService.cpp
exinfinitum Mar 26, 2021
bc63400
Fix indenting in Devices.cpp
exinfinitum Mar 26, 2021
0072308
Fix formatting in CInstalledDevice.cpp
exinfinitum Mar 26, 2021
7c8e318
Use CString instead of std::string
exinfinitum Mar 26, 2021
7db9e7f
Refactor
exinfinitum Mar 26, 2021
06c1ad1
Merge branch 'master' into useAllPrimaryWeapons
exinfinitum Mar 26, 2021
5f12686
Merge branch 'useAllPrimaryWeapons' into chargingGuns
exinfinitum Mar 27, 2021
22d57e0
Merge remote-tracking branch 'origin/master' into chargingGuns
exinfinitum Mar 27, 2021
ed32d0f
Fix compile errors
exinfinitum Mar 27, 2021
a0f5ddf
Merge remote-tracking branch 'origin/master' into chargingGuns
exinfinitum Jun 6, 2021
76e3ae7
Make GetChargeTime public
exinfinitum Jun 6, 2021
94c46aa
Merge remote-tracking branch 'origin/master' into chargingGuns
exinfinitum Jul 18, 2021
aaadf16
Add support for limited traverse rates on swivel weapons; still need …
exinfinitum Jul 30, 2021
7bcea33
Merge remote-tracking branch 'origin/master' into chargingGuns
exinfinitum Jan 15, 2022
2a55021
Refactor charging code and clean up PR
exinfinitum Jan 15, 2022
ba6a14c
Merge branch 'chargingGuns' into limitedTraverseRateGuns
exinfinitum Jan 15, 2022
2e51eb2
Fix compile errors
exinfinitum Jan 15, 2022
44506a5
Merge branch 'chargingGuns' into limitedTraverseRateGuns
exinfinitum Jan 15, 2022
ada06cb
Fix indents
exinfinitum Jan 15, 2022
8570ce0
Merge branch 'chargingGuns' into limitedTraverseRateGuns
exinfinitum Jan 15, 2022
14b59b9
Change back to pass by reference
exinfinitum Jan 15, 2022
8374ce2
Merge remote-tracking branch 'origin/master' into HEAD
exinfinitum May 4, 2022
2f66bbd
Merge remote-tracking branch 'george/master' into limitedTraverseRate…
exinfinitum May 4, 2022
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
17 changes: 16 additions & 1 deletion Mammoth/Include/TSEDevices.h
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ class CDeviceClass
virtual const CRepairerClass *AsRepairerClass (void) const { return NULL; }
virtual CShieldClass *AsShieldClass (void) { return NULL; }
virtual CWeaponClass *AsWeaponClass (void) { return NULL; }
virtual bool CalcFireSolution (const CInstalledDevice &Device, CSpaceObject &Target, int *retiAimAngle = NULL, Metric *retrDist = NULL) const { return false; }
virtual bool CalcFireSolution (const CInstalledDevice &Device, const CSpaceObject &Target, int *retiAimAngle = NULL, Metric *retrDist = NULL) const { return false; }
virtual int CalcPowerUsed (SUpdateCtx &Ctx, CInstalledDevice *pDevice, CSpaceObject *pSource) { return 0; }
virtual bool CanHitFriends (void) const { return true; }
virtual void Deplete (CInstalledDevice *pDevice, CSpaceObject *pSource) { }
Expand Down Expand Up @@ -366,6 +366,9 @@ class CDeviceClass
bool bUseCustomAmmoCountHandler = false) { if (retsLabel) *retsLabel = NULL_STR; if (retiAmmoLeft) *retiAmmoLeft = -1; if (retpType) *retpType = NULL; }
virtual Metric GetShotSpeed (CItemCtx &Ctx) const { return 0.0; }
virtual void GetStatus (const CInstalledDevice *pDevice, const CSpaceObject *pSource, int *retiStatus, int *retiMaxStatus) { *retiStatus = 0; *retiMaxStatus = 0; }
virtual int GetSwivelPivotPerTick (const CInstalledDevice* pDevice) const { return 360; }
virtual float GetSwivelPivotPerTickExact (const CInstalledDevice* pDevice) const { return 360.0; }
virtual int GetSwivelUpdateRate (const CInstalledDevice* pDevice) const { return 0; }
virtual DWORD GetTargetTypes (const CDeviceItem &DeviceItem) const { return 0; }
virtual int GetValidVariantCount (CSpaceObject *pSource, CInstalledDevice *pDevice) { return 0; }
virtual int GetWeaponEffectiveness (const CDeviceItem &DeviceItem, CSpaceObject *pTarget) const { return 0; }
Expand Down Expand Up @@ -694,6 +697,7 @@ class CInstalledDevice
int GetDefaultFireAngle (void) const { return m_pClass->GetDefaultFireAngle(*this); }
int GetHitPoints (CItemCtx &ItemCtx, int *retiMaxHP = NULL) const { return m_pClass->GetHitPoints(ItemCtx, retiMaxHP); }
CSpaceObject *GetLastShot (CSpaceObject *pSource, int iIndex) const;
int GetLastSwivelTime (void) const { return m_iLastSwivelTime; }
Metric GetMaxEffectiveRange (CSpaceObject *pSource, CSpaceObject *pTarget = NULL) const { return m_pClass->GetMaxEffectiveRange(pSource, this, pTarget); }
Metric GetMaxRange (CItemCtx &ItemCtx) const { return m_pClass->GetMaxRange(ItemCtx); }
CString GetName (void) { return m_pClass->GetName(); }
Expand All @@ -709,6 +713,9 @@ class CInstalledDevice
CSpaceObject *GetSource (void) const { return m_pSource; }
CSpaceObject &GetSourceOrThrow (void) const { if (m_pSource) return *m_pSource; else throw CException(ERR_FAIL); }
void GetStatus (const CSpaceObject *pSource, int *retiStatus, int *retiMaxStatus) const { m_pClass->GetStatus(this, pSource, retiStatus, retiMaxStatus); }
int GetSwivelPivotPerTick (void) const { return m_iSwivelPivotPerTick; }
float GetSwivelPivotPerTickExact (void) { return m_iSwivelUpdateRate == 0 ? 360 : float(m_iSwivelPivotPerTick) / float(m_iSwivelUpdateRate); }
int GetSwivelUpdateRate (void) const { return m_iSwivelUpdateRate; }
CSpaceObject *GetTarget (CSpaceObject *pSource) const;
int GetValidVariantCount (CSpaceObject *pSource) { return m_pClass->GetValidVariantCount(pSource, this); }
CWeaponTargetDefinition *GetWeaponTargetDefinition (void) const { return (m_pWeaponTargetDefinition.get()); }
Expand All @@ -731,6 +738,10 @@ class CInstalledDevice
void SetHitPoints (CItemCtx &ItemCtx, int iHP) { m_pClass->SetHitPoints(ItemCtx, iHP); }
void SetLastShot (CSpaceObject *pObj, int iIndex);
void SetLastShotCount (int iCount);
void SetLastSwivelTime (int iLastSwivelTime) { m_iLastSwivelTime = iLastSwivelTime; }
void SetSwivelPivotPerTick (int iSwivelPivotPerTick) { m_iSwivelPivotPerTick = iSwivelPivotPerTick; }
void SetSwivelPivotPerTick (float fSwivelPivotPerTick) { m_iSwivelPivotPerTick = fSwivelPivotPerTick >= 1.0 ? int(fSwivelPivotPerTick) : 1; m_iSwivelUpdateRate = fSwivelPivotPerTick >= 1.0 ? 1 : int(1.0 / fSwivelPivotPerTick); }
void SetSwivelUpdateRate (int iSwivelUpdateRate) { m_iSwivelUpdateRate = iSwivelUpdateRate; }
inline void SetTarget (CSpaceObject *pObj);
bool ShowActivationDelayCounter (CSpaceObject *pSource) { return m_pClass->ShowActivationDelayCounter(pSource, this); }

Expand Down Expand Up @@ -768,6 +779,10 @@ class CInstalledDevice
int m_iPosZ:16 = 0; // Position of installation (height)
int m_iMinFireArc:16 = 0; // Min angle of fire arc (degrees)
int m_iMaxFireArc:16 = 0; // Max angle of fire arc (degrees)
int m_iSwivelPivotPerTick : 16 = -1; // Max angle the weapon can pivot during a single tick (degrees)
int m_iSwivelUpdateRate : 16 = -1; // Update weapon firing angle every Nth tick during a burst (ticks)
// TODO(heliogenesis): Save swivel values in the save file, and add support for it in device slots and property functions
int m_iLastSwivelTime : 16 = 32767; // Last tick that the weapon updated its firing angle during (tick)

int m_iShotSeparationScale:16 = 32767; // Scaled by 32767. Governs scaling of shot separation for dual etc weapons
int m_iMaxFireRange:16 = 0; // Max effective fire range (in light-seconds); 0 = no limit
Expand Down
12 changes: 9 additions & 3 deletions Mammoth/Include/TSEWeaponClassImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ class CWeaponClass : public CDeviceClass

virtual bool Activate (CInstalledDevice &Device, SActivateCtx &ActivateCtx) override;
virtual CWeaponClass *AsWeaponClass (void) override { return this; }
virtual bool CalcFireSolution (const CInstalledDevice &Device, CSpaceObject &Target, int *retiAimAngle = NULL, Metric *retrDist = NULL) const override;
virtual bool CalcFireSolution (const CInstalledDevice &Device, const CSpaceObject &Target, int *retiAimAngle = NULL, Metric *retrDist = NULL) const override;
virtual int CalcPowerUsed (SUpdateCtx &Ctx, CInstalledDevice *pDevice, CSpaceObject *pSource) override;
virtual ICCItem *FindAmmoItemProperty (CItemCtx &Ctx, const CItem &Ammo, const CString &sProperty) override;
virtual int GetActivateDelay (CItemCtx &ItemCtx) const override;
Expand Down Expand Up @@ -161,6 +161,9 @@ class CWeaponClass : public CDeviceClass
CItemType **retpType = NULL,
bool bUseCustomAmmoCountHandler = false) override;
virtual Metric GetShotSpeed (CItemCtx &Ctx) const override;
virtual int GetSwivelPivotPerTick (const CInstalledDevice* pDevice) const override;
virtual float GetSwivelPivotPerTickExact (const CInstalledDevice* pDevice) const override;
virtual int GetSwivelUpdateRate (const CInstalledDevice* pDevice) const override;
virtual DWORD GetTargetTypes (const CDeviceItem &DeviceItem) const override;
virtual int GetValidVariantCount (CSpaceObject *pSource, CInstalledDevice *pDevice) override;
virtual int GetWeaponEffectiveness (const CDeviceItem &DeviceItem, CSpaceObject *pTarget) const override;
Expand Down Expand Up @@ -249,7 +252,8 @@ class CWeaponClass : public CDeviceClass
Metric CalcConfigurationMultiplier (const CWeaponFireDesc *pShot = NULL, bool bIncludeFragments = true) const;
Metric CalcDamage (const CWeaponFireDesc &ShotDesc, const CItemEnhancementStack *pEnhancements = NULL, DWORD dwDamageFlags = 0) const;
Metric CalcDamagePerShot (const CWeaponFireDesc &ShotDesc, const CItemEnhancementStack *pEnhancements = NULL, DWORD dwDamageFlags = 0) const;
int CalcFireAngle (CItemCtx &ItemCtx, Metric rSpeed, CSpaceObject *pTarget, bool *retbSetDeviceAngle = NULL) const;
int CalcFireAngle (CItemCtx &ItemCtx, Metric rSpeed, const CSpaceObject *pTarget, bool *retbSetDeviceAngle = NULL) const;
int CalcFireAngleRestrictedBySwivelRate (const int iFireAngle, const CInstalledDevice* pDevice, const CSpaceObject* pSource) const;
int CalcLevel (const CWeaponFireDesc &ShotDesc) const;
TArray<CTargetList::STargetResult> CalcMIRVTargets (CInstalledDevice &Device, const CTargetList &TargetList, int iMaxCount) const;
int CalcReachableFireAngle (const CInstalledDevice &Device, int iDesiredAngle, int iDefaultAngle = -1) const;
Expand Down Expand Up @@ -308,7 +312,7 @@ class CWeaponClass : public CDeviceClass
bool IsMIRV (const CWeaponFireDesc &ShotDesc) const { return (m_bMIRV || ShotDesc.IsMIRV()); }
bool IsSinglePointOrigin (void) const { return m_Configuration.IsSinglePointOrigin(); }
bool IsTemperatureEnabled (void) { return (m_Counter == EDeviceCounterType::Temperature); }
bool IsTargetReachable (const CInstalledDevice &Device, CSpaceObject &Target, int iDefaultFireAngle = -1, int *retiFireAngle = NULL, int *retiAimAngle = NULL) const;
bool IsTargetReachable (const CInstalledDevice &Device, const CSpaceObject &Target, int iDefaultFireAngle = -1, int *retiFireAngle = NULL, int *retiAimAngle = NULL) const;
bool IsTracking (const CDeviceItem &DeviceItem, const CWeaponFireDesc *pShot) const;
bool UpdateTemperature (CItemCtx &ItemCtx, const CWeaponFireDesc &ShotDesc, CFailureDesc::EFailureTypes *retiFailureMode, bool *retbSourceDestroyed);
bool UsesAmmo (void) const { return (m_ShotData.GetCount() > 0 && m_ShotData[0].pDesc->GetAmmoType() != NULL); }
Expand Down Expand Up @@ -344,6 +348,8 @@ class CWeaponClass : public CDeviceClass
int m_iContinuousFireDelay = 0; // Delay between shots
bool m_bContinuousConsumePerShot; // If a continuous weapon, consume ammunition for every shot in burst
bool m_bBurstTracksTargets; // If the weapon is continuous, whether or not to track the target during the entire burst
int m_iSwivelPivotPerTick = 0; // Max angle the weapon can pivot during a single tick (degrees)
int m_iSwivelUpdateRate = 0; // Update weapon firing angle every Nth tick during a burst (ticks)

bool m_bCharges; // TRUE if weapon has charges instead of ammo
bool m_bUsesLauncherControls; // TRUE if weapon is selected/fired as a launcher instead of as a primary gun
Expand Down
93 changes: 82 additions & 11 deletions Mammoth/TSE/CWeaponClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#define LAUNCHER_ATTRIB CONSTLIT("launcher")
#define LINKED_FIRE_ATTRIB CONSTLIT("linkedFire")
#define MAX_FIRE_ARC_ATTRIB CONSTLIT("maxFireArc")
#define MAX_SWIVEL_PER_TICK_ATTRIB CONSTLIT("maxSwivelPerTick")
#define MIN_FIRE_ARC_ATTRIB CONSTLIT("minFireArc")
#define MULTI_TARGET_ATTRIB CONSTLIT("multiTarget")
#define CAN_FIRE_WHEN_BLIND_ATTRIB CONSTLIT("canFireWhenBlind")
Expand Down Expand Up @@ -942,7 +943,7 @@ Metric CWeaponClass::CalcDamagePerShot (const CWeaponFireDesc &ShotDesc, const C
return CalcConfigurationMultiplier(&ShotDesc, false) * CalcDamage(ShotDesc, pEnhancements, dwDamageFlags);
}

int CWeaponClass::CalcFireAngle (CItemCtx &ItemCtx, Metric rSpeed, CSpaceObject *pTarget, bool *retbSetDeviceAngle) const
int CWeaponClass::CalcFireAngle (CItemCtx &ItemCtx, Metric rSpeed, const CSpaceObject *pTarget, bool *retbSetDeviceAngle) const

// CalcFireAngle
//
Expand Down Expand Up @@ -999,7 +1000,31 @@ int CWeaponClass::CalcFireAngle (CItemCtx &ItemCtx, Metric rSpeed, CSpaceObject
}
}

bool CWeaponClass::CalcFireSolution (const CInstalledDevice &Device, CSpaceObject &Target, int *retiAimAngle, Metric *retrDist) const
int CWeaponClass::CalcFireAngleRestrictedBySwivelRate (const int iFireAngle, const CInstalledDevice *pDevice, const CSpaceObject *pSource) const
// If the fire angle is restricted due to traversal limits, then restrict the fire angle to the bounds.

{
int iNewFireAngle = iFireAngle;
int iSwivelPivotPerTick = GetSwivelPivotPerTick(pDevice);
if (m_bBurstTracksTargets && iSwivelPivotPerTick > 0 && pDevice->GetFireAngle() != -1)
{
float fPivotAmount = float(pDevice->GetLastSwivelTime() - pDevice->GetContinuousFire()) / max(1.0f, float(GetSwivelUpdateRate(pDevice)));
int pivotAmount = int(fPivotAmount) * iSwivelPivotPerTick;
int iShipRotation = pSource->GetRotation();
int iOldFireAngle = AngleMod(pDevice->GetFireAngle() + iShipRotation);
int iMinDelta = AngleMod(iOldFireAngle - pivotAmount);
int iMaxDelta = AngleMod(iOldFireAngle + pivotAmount);
if (AngleMod(abs(iFireAngle - iOldFireAngle)) > pivotAmount)
{
int iDistToMinDelta = AngleMod(iMinDelta - iFireAngle);
int iDistToMaxDelta = AngleMod(iFireAngle - iMaxDelta);
iNewFireAngle = iDistToMinDelta < iDistToMaxDelta ? iMinDelta : iMaxDelta;
}
}
return iNewFireAngle;
}

bool CWeaponClass::CalcFireSolution (const CInstalledDevice &Device, const CSpaceObject &Target, int *retiAimAngle, Metric *retrDist) const

// CalcFireSolution
//
Expand Down Expand Up @@ -1326,17 +1351,24 @@ bool CWeaponClass::CalcSingleTarget (CInstalledDevice &Device,
// use the same value already stored.

retbSetFireAngle = false;
int iContinuousFire = Device.GetContinuousFire();
int iSwivelUpdateRate = GetSwivelUpdateRate(&Device);
bool bCanUpdateFireAngle = (Device.GetLastSwivelTime() - iContinuousFire) >= iSwivelUpdateRate || iSwivelUpdateRate == 0;

// If we need a target, then get it from the device.

if ((IsTracking(DeviceItem, &ShotDesc) || m_bBurstTracksTargets) && !(Source.IsBlind() && !(m_bCanFireWhenBlind)))
{
retpTarget = Device.GetTarget(&Source);
retiFireAngle = Device.GetFireAngle();
if (m_bBurstTracksTargets)
{
retiFireAngle = AngleMod(retiFireAngle + Source.GetRotation());
}

// If necessary, we recompute the fire angle

if (retiFireAngle == -1 || m_bBurstTracksTargets)
if (retiFireAngle == -1 || (m_bBurstTracksTargets && bCanUpdateFireAngle))
{
if (retpTarget)
{
Expand All @@ -1357,6 +1389,18 @@ bool CWeaponClass::CalcSingleTarget (CInstalledDevice &Device,
else
retiFireAngle = -1;
}

if (GetSwivelPivotPerTick(&Device) > 0 && m_bBurstTracksTargets && retiFireAngle == -1)
{
retiFireAngle = Device.GetDefaultFireAngle();
}

if (retiFireAngle != -1)
{
retiFireAngle = CalcFireAngleRestrictedBySwivelRate(retiFireAngle, &Device, &Source);
Device.SetFireAngle(AngleMod(retiFireAngle - (m_bBurstTracksTargets ? Source.GetRotation() : 0)));
}
Device.SetLastSwivelTime(iContinuousFire);
}
}

Expand All @@ -1375,7 +1419,6 @@ bool CWeaponClass::CalcSingleTarget (CInstalledDevice &Device,
else
{
CDeviceItem DeviceItem = Device.GetDeviceItem();

switch (DeviceItem.CalcTargetType())
{
case CDeviceItem::calcNoTarget:
Expand Down Expand Up @@ -1418,6 +1461,7 @@ bool CWeaponClass::CalcSingleTarget (CInstalledDevice &Device,
default:
return false;
}
Device.SetLastSwivelTime(GetChargeTime(ShotDesc) + ((1 + GetContinuous(ShotDesc)) * (GetContinuousFireDelay(ShotDesc) + 1)));
}

// Fire!
Expand Down Expand Up @@ -1925,12 +1969,15 @@ ALERROR CWeaponClass::CreateFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc, CI

pWeapon->m_iContinuous = pDesc->GetAttributeIntegerBounded(REPEATING_ATTRIB, 0, -1, 0);
pWeapon->m_iContinuousFireDelay = pDesc->GetAttributeIntegerBounded(REPEATING_DELAY_ATTRIB, 0, -1, 0);
Metric fSwivelPerTick = pDesc->GetAttributeFloat(MAX_SWIVEL_PER_TICK_ATTRIB);
pWeapon->m_iSwivelPivotPerTick = fSwivelPerTick > 0.0 ? max(1, int(fSwivelPerTick)) : 0;
pWeapon->m_iSwivelUpdateRate = fSwivelPerTick >= 1.0 || fSwivelPerTick <= 0.0 ? 0 : int(1.0 / fSwivelPerTick);

// NOTE: For now we don't support a combination of repeating fire and
// repeating delay that exceeds 254.

if (pWeapon->m_iContinuous > CONTINUOUS_DATA_LIMIT
|| pWeapon->m_iContinuous * pWeapon->m_iContinuousFireDelay > CONTINUOUS_DATA_LIMIT)
|| pWeapon->m_iContinuous * (pWeapon->m_iContinuousFireDelay + 1) > CONTINUOUS_DATA_LIMIT)
{
Ctx.sError = CONSTLIT("Unfortunately, that combination of repeating= and repeatingDelay= is too high for the engine.");
return ERR_FAIL;
Expand Down Expand Up @@ -2550,6 +2597,10 @@ bool CWeaponClass::FireWeapon (CInstalledDevice &Device,

bool bSetFireAngle;
int iFireAngle;
if (ActivateCtx.iRepeatingCount == 0 && ActivateCtx.iChargeFrame == 0)
{
Device.SetFireAngle(-1);
}
CShotArray Shots = CalcShotsFired(Device, ShotDesc, ActivateCtx, iFireAngle, bSetFireAngle);
if (Shots.GetCount() == 0)
return false;
Expand Down Expand Up @@ -2593,11 +2644,8 @@ bool CWeaponClass::FireWeapon (CInstalledDevice &Device,
Device.SetTarget(Shots[0].pTarget);
if (bSetFireAngle)
{
Device.SetFireAngle(iFireAngle);
}
else if (ActivateCtx.iRepeatingCount == 0)
{
Device.SetFireAngle(-1);
CSpaceObject& Source = Device.GetSourceOrThrow();
Device.SetFireAngle(AngleMod(iFireAngle - (m_bBurstTracksTargets ? Source.GetRotation() : 0)));
}

// Increment polarity, if necessary
Expand Down Expand Up @@ -4031,6 +4079,29 @@ const CWeaponClass::SStdStats &CWeaponClass::GetStdStats (int iLevel)
}
}

int CWeaponClass::GetSwivelPivotPerTick (const CInstalledDevice* pDevice) const
{
int deviceSwivelPivot = pDevice->GetSwivelPivotPerTick();
return deviceSwivelPivot > 0 ? deviceSwivelPivot : m_iSwivelPivotPerTick;
}

float CWeaponClass::GetSwivelPivotPerTickExact (const CInstalledDevice* pDevice) const
{
int deviceSwivelPivot = pDevice->GetSwivelPivotPerTick();
int deviceSwivelUpdateRate = pDevice->GetSwivelUpdateRate();
if (deviceSwivelPivot >= 0 && deviceSwivelUpdateRate >= 0)
{
return deviceSwivelUpdateRate == 0 ? 360 : float(deviceSwivelPivot) / float(deviceSwivelUpdateRate);
}
return m_iSwivelUpdateRate == 0 ? 360 : float(m_iSwivelPivotPerTick) / float(m_iSwivelUpdateRate);
}

int CWeaponClass::GetSwivelUpdateRate (const CInstalledDevice* pDevice) const
{
int deviceSwivelUpdateRate = pDevice->GetSwivelUpdateRate();
return deviceSwivelUpdateRate > 0 ? deviceSwivelUpdateRate : m_iSwivelUpdateRate;
}

int CWeaponClass::GetValidVariantCount (CSpaceObject *pSource, CInstalledDevice *pDevice)

// GetValidVariantCount
Expand Down Expand Up @@ -4758,7 +4829,7 @@ bool CWeaponClass::IsStdDamageType (DamageTypes iDamageType, int iLevel)
return (iLevel >= iTierLevel && iLevel < iTierLevel + 3);
}

bool CWeaponClass::IsTargetReachable (const CInstalledDevice &Device, CSpaceObject &Target, int iDefaultFireAngle, int *retiFireAngle, int *retiAimAngle) const
bool CWeaponClass::IsTargetReachable (const CInstalledDevice &Device, const CSpaceObject &Target, int iDefaultFireAngle, int *retiFireAngle, int *retiAimAngle) const

// IsTargetReachable
//
Expand Down