A unit whose current weapon set limits its firing pitch stops respecting that limit after a save is loaded. It accepts a target it cannot elevate or depress to, commits to the attack, and then never lands a shot.
The flag that gates the check, WeaponSet::m_hasPitchLimit, is not carried in the save. WeaponSet::xfer writes two bools, and both of them are m_hasDamageWeapon:
https://github.com/TheSuperHackers/GeneralsGameCode/blob/main/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/WeaponSet.cpp#L296-L297
The value does not recover after the load either. updateWeaponSet recomputes the flag only when the weapon template set changes, and the load restores that set, so the comparison at the top of the function is already satisfied and the body never runs. The flag keeps whatever the object was constructed with, which is the default weapon set rather than the saved one.
Only the stale false direction misbehaves. A stale true still runs the per weapon loop in isAnyWithinTargetPitch and reaches the right answer, so the fault appears when the saved weapon set has a pitch limit that the default set does not, such as a set gained through an upgrade, veterancy or a rider.
Repro:
- Take an object whose current weapon set has a pitch limit that its default weapon set does not
- Save
- Load that save
- Order it to attack a target outside its firing pitch
isAnyWithinTargetPitch returns true from its first line, the order is accepted, and the unit does not connect. Before the save, the same order is rejected as an invalid shot.
Measured with a probe on the flag, forcing a value at save time that the default weapon set cannot produce, and logging both sides of the transfer with m_hasDamageWeapon as a control:
|
saved |
loaded |
| pitch limit flag |
1 |
0 |
| damage weapon flag (control) |
0 |
0 |
The control survives the round trip, so the record itself is written and read; only the pitch limit flag is missing from it.
The same two lines are present in Generals at Generals/Code/GameEngine/Source/GameLogic/Object/WeaponSet.cpp#L288-L289.
A unit whose current weapon set limits its firing pitch stops respecting that limit after a save is loaded. It accepts a target it cannot elevate or depress to, commits to the attack, and then never lands a shot.
The flag that gates the check,
WeaponSet::m_hasPitchLimit, is not carried in the save.WeaponSet::xferwrites two bools, and both of them arem_hasDamageWeapon:https://github.com/TheSuperHackers/GeneralsGameCode/blob/main/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/WeaponSet.cpp#L296-L297
The value does not recover after the load either.
updateWeaponSetrecomputes the flag only when the weapon template set changes, and the load restores that set, so the comparison at the top of the function is already satisfied and the body never runs. The flag keeps whatever the object was constructed with, which is the default weapon set rather than the saved one.Only the stale
falsedirection misbehaves. A staletruestill runs the per weapon loop inisAnyWithinTargetPitchand reaches the right answer, so the fault appears when the saved weapon set has a pitch limit that the default set does not, such as a set gained through an upgrade, veterancy or a rider.Repro:
isAnyWithinTargetPitchreturns true from its first line, the order is accepted, and the unit does not connect. Before the save, the same order is rejected as an invalid shot.Measured with a probe on the flag, forcing a value at save time that the default weapon set cannot produce, and logging both sides of the transfer with
m_hasDamageWeaponas a control:The control survives the round trip, so the record itself is written and read; only the pitch limit flag is missing from it.
The same two lines are present in Generals at
Generals/Code/GameEngine/Source/GameLogic/Object/WeaponSet.cpp#L288-L289.