[BOUNTY #2434] Fix GLA Cash Bounty using incorrect value for China Red Guard#2485
Open
zhaog100 wants to merge 2 commits intoTheSuperHackers:mainfrom
Open
[BOUNTY #2434] Fix GLA Cash Bounty using incorrect value for China Red Guard#2485zhaog100 wants to merge 2 commits intoTheSuperHackers:mainfrom
zhaog100 wants to merge 2 commits intoTheSuperHackers:mainfrom
Conversation
…ue for China Red Guard When a unit is produced in quantity via QuantityModifier (e.g. China Red Guards come in pairs for $300 total), the BuildCost on the template reflects the total cost for the whole batch, not the per-unit cost. Cash Bounty was using this total cost for each individual unit, resulting in double the expected bounty per Red Guard killed. Fix by looking up the victim's producer and checking its QuantityModifiers at bounty time. If the unit was produced in a batch, divide the template cost by the production quantity to get the correct per-unit cost. Before: Level 1 Cash Bounty gives $15 per Red Guard (5% of $300) After: Level 1 Cash Bounty gives $7 per Red Guard (5% of $150) Closes TheSuperHackers#2434
|
| Filename | Overview |
|---|---|
| Generals/Code/GameEngine/Source/Common/RTS/Player.cpp | Adds per-unit cost correction in doBountyForKill() by dynamic-casting the producer's interface pointer to the concrete ProductionUpdate class and calling getQuantityForTemplate(). Logic is correct and edge cases are gracefully handled; minor architectural concern around the dynamic_cast bypassing the interface. |
| GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp | Identical fix mirrored for Zero Hour (GeneralsMD); same observations apply as for the Generals variant. |
| Generals/Code/GameEngine/Include/GameLogic/Module/ProductionUpdate.h | Adds public getQuantityForTemplate() declaration to the concrete ProductionUpdate class. Uses #pragma once correctly; license header is valid. |
| GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ProductionUpdate.h | Same header change mirrored for Zero Hour; correct "Zero Hour(tm)" branding in file header, #pragma once in place. |
| Generals/Code/GameEngine/Source/GameLogic/Object/Update/ProductionUpdate.cpp | Implements getQuantityForTemplate() by iterating over m_quantityModifiers and resolving template names via TheThingFactory at call time; logic is correct but template name lookup is repeated on every call rather than being cached. |
| GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ProductionUpdate.cpp | Same implementation mirrored for Zero Hour; same template-lookup-per-call observation applies. |
Sequence Diagram
sequenceDiagram
participant K as Killer (Object)
participant V as Victim (Object)
participant GL as TheGameLogic
participant PU as ProductionUpdate (Producer)
participant TF as TheThingFactory
participant P as Player (bounty recipient)
K->>V: (unit killed)
P->>V: doBountyForKill(killer, victim)
P->>V: getTemplate()->calcCostToBuild()
Note over P: costToBuild = batch cost from template
P->>V: getProducerID()
alt producerID != INVALID_ID
P->>GL: findObjectByID(producerID)
GL-->>P: producer (Object*)
P->>P: dynamic_cast<ProductionUpdate*>(producer->getProductionUpdateInterface())
alt cast succeeds
P->>PU: getQuantityForTemplate(victim->getTemplate())
loop each QuantityModifier
PU->>TF: findTemplate(m_templateName)
TF-->>PU: ThingTemplate*
PU->>PU: isEquivalentTo(thingTemplate)?
end
PU-->>P: quantity (Int)
alt quantity > 1
P->>P: costToBuild = costToBuild / quantity
end
end
end
P->>P: bounty = ceil(costToBuild * m_cashBountyPercent)
P->>P: getMoney()->deposit(bounty)
Prompt To Fix All With AI
This is a comment left during a code review.
Path: Generals/Code/GameEngine/Source/GameLogic/Object/Update/ProductionUpdate.cpp
Line: 1408
Comment:
**Template name lookup on every kill**
`TheThingFactory->findTemplate(it->m_templateName)` is a name-string lookup performed for every modifier entry on every kill event. For the typical case (1–2 entries per building) this is negligible, but the result is always the same for a given producer — the modifier list never changes at runtime. Consider caching the resolved `ThingTemplate*` pointers inside `QuantityModifier` (e.g. a `mutable const ThingTemplate* m_cachedTemplate`) and resolving lazily on first use to make the hot path a simple pointer comparison.
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: Generals/Code/GameEngine/Source/Common/RTS/Player.cpp
Line: 2044
Comment:
**`dynamic_cast` to concrete type bypasses interface**
`dynamic_cast<ProductionUpdate*>` couples `Player::doBountyForKill()` directly to the concrete class rather than going through `ProductionUpdateInterface`. The previous review already suggested the cleaner alternative — exposing a virtual `getQuantityForTemplate` (or equivalent) on the interface itself — which would avoid the RTTI cast and allow any future alternate implementation of `ProductionUpdateInterface` to participate correctly.
The current approach is safe and falls back gracefully when the cast fails (i.e. returns `nullptr`), but an interface-level virtual method would be the preferred idiomatic pattern for this codebase. The same applies to `GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp` line 2444.
How can I resolve this? If you propose a fix, please make it concise.Reviews (2): Last reviewed commit: "fix: move quantity modifier lookup into ..." | Re-trigger Greptile
…ForTemplate() The previous code called getProductionUpdateModuleData() on a ProductionUpdateInterface*, but that method is private in the ProductionUpdate class (generated by MAKE_STANDARD_MODULE_DATA_MACRO_ABC). VC6 compiler correctly rejects this. Extract the lookup into a new public method on ProductionUpdate and use static_cast instead of dynamic_cast (VC6 has limited RTTI support).
What if we spawn Red Guard? According to this they would still give the full template cost bounty. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the GLA Cash Bounty giving incorrect (doubled) bounty values for China Red Guards and any other unit produced in batches via
QuantityModifier.Problem
When a unit is produced in quantity (e.g. China Red Guards cost $300 for 2 units), the
BuildCoston theThingTemplatereflects the total cost for the whole batch, not the per-unit cost. Cash Bounty was using this total cost for each individual unit:This affected all 3 bounty levels and any other unit produced with a
QuantityModifier.Solution
In
Player::doBountyForKill(), look up the victim's producer and check itsProductionUpdatemodule'sQuantityModifiers. If the unit's template matches a quantity modifier entry with quantity > 1, divide the template cost by that quantity to get the correct per-unit cost.Changes
Generals/Code/GameEngine/Source/Common/RTS/Player.cpp- Added include + bounty cost correction logicGeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp- Same fix for Zero HourEdge Cases Handled
Closes #2434