Dresden
Emperor
- Joined
- Jul 10, 2008
- Messages
- 1,081
This was originally going to be a simple bug report, but then I found a second bug.... and then when I looked in the SDK source to suggest fixes for those I came upon a suggestion and a potential new feature too. So since this has grown monstrously out-of-hand, I'm making it a new topic.
The Current Calculation
For reference, here is how CvMainInterface currently calculates whip overflow for display in the city screen. The CDA and the Civ4lerts each make essentially the same calculation too. (Perhaps it should be farmed out to a utility function.)
Bug #1: Production Modifers
If the current build has any production modifiers, those get "reversed" when the overflow gets calculated. Here are 2 examples from my Noble/Epic NCXI game:
For Yekaterinburg, the Library has 42/135 hammers. The 2-pop whip adds 180 resulting in the BUG estimated overflow of 180 - (135 - 42) = 87. But after whipping, the actual overflow turns out to be just 47. Similarly for Yaroslavl', the estimated overflow is 59 but the actual is just 31. The majority of the discrepancy comes from not reversing those +100% modifiers.
The modifier reversal can thus be done in Python with the following that I essentially just converted from the SDK's CvCity::changeOverflowProduction() function:
If that is used on the above two examples, the revised estimate for Yekaterinburg is 43 (actual was 47) and the revised estimate for Yaroslavl' is 29 (actual is 31). Pretty close now. This leads us into....
Suggestion: Add Current Production
The difference in those previous estimates comes from the fact that the city still contributes actual hammers to the build after the whip. In Yekaterinburg, my people were still producing 8 hammers after the whipping so the raw overflow was really 95; half of which gives us the 47 actual overflow. In Yaroslavl' I whipped away part of the production so they only added 4 more hammers giving a raw overflow of 63, resulting in the actual overflow of 31.
While you could MM the city to change the production any way you want after whipping, I expect that most people don't bother. And I claim without any evidence whatsoever that the post-whip production will normally be pretty close to the pre-whip production or at least it'll be a lot closer than assuming zero post-whip production. So why not factor that pre-whip production into the overlap like below?
Note there's also a small optimization there calling productionLeft() instead of using getProductionNeeded() - getProduction(). I'm sure it's essentially meaningless in terms of speed but why do two function calls when you can do one and make the C++ function do the subtraction? For Yekaterinburg, the estimate would now be spot on. For Yaroslavl, it'd be one over at 32. I think this will provide a better estimate than ignoring next turn's production more often than not. The sharp-eyed might notice that this estimate is slightly worse for settlers/workers since that True is telling getCurrentProductionDifference() to always ignore food surpluses, but again the estimate should still be better than completely ignoring next turn's production. However, I'm not done screwing with the overflow estimation...
Bug #2: Overflow Cap
The current estimates ignore the overflow cap. Somewhere along the line (I think in a vanilla patch) Firaxis implemented an overflow cap to reduce "pre-build" exploits and we need to factor this in too. The maximum overflow you can get is the production cost of the current build. Here's another example:
This time there are no production modifiers in play but we still get way less overflow. The reason is the cap. An epic scout costs 22 hammers, so our raw overflow of 49 (44 from the whip and 5 from next turn's production) becomes a measly 22. Those bastards! What'd they do with my 27 hammers? Let's come back to that in a minute. For now, enforcing the cap, which is nice and simple:
That'll take care of the cap and report a proper 22 for the scout situation.
Feature Request: Free Money
So what happened to those 27 hammers I lost? Did they just go away? Note the Treasury portion of my image. The turn of the whip, I had 105 gold and was losing 3 per turn, so the next turn I should have had 102. But instead, I have 129. There's my hammers; they got turned into gold!
An oddity about this is that this gold amount is calculated before the modifier reversal that I talked about first. As a result there is actually a fairly exploitive strategy based on this where you crank up a super-producing military city to build the cheapest unit available like a chariot and just delete them after production. Because of modifiers like the HE and a military academy you get a huge overflow that gets turned into money after the overflow cap is applied and the result is substantially better than if you had simply built Wealth. This has been discussed in overflow mechanics topics and I first saw it in an OCC game report. Despite that I can see how you might rule this part of overflow knowledge to be too obscure or too revealing of internal mechanics, but since I'm probably putting it in my Frankenstein version (BUGenstein?) anyway I figure I should propose it for general use.
This would require a tweaking of the alert and city bar output and either a bigger column or second column on the CDA. The money is calculated based on a global define so let's do that, again using similar logic to the SDK code.
So, if all fixes and suggestions are implemented, here's what the CvMainInterface.py code would look like:
Along with a new XML entry:
And the results in-game on two of my above examples:

The Current Calculation
For reference, here is how CvMainInterface currently calculates whip overflow for display in the city screen. The CDA and the Civ4lerts each make essentially the same calculation too. (Perhaps it should be farmed out to a utility function.)
Code:
iHurryOverflow = pHeadSelectedCity.hurryProduction(HURRY_WHIP) - (pHeadSelectedCity.getProductionNeeded() - pHeadSelectedCity.getProduction())
Bug #1: Production Modifers
If the current build has any production modifiers, those get "reversed" when the overflow gets calculated. Here are 2 examples from my Noble/Epic NCXI game:

For Yekaterinburg, the Library has 42/135 hammers. The 2-pop whip adds 180 resulting in the BUG estimated overflow of 180 - (135 - 42) = 87. But after whipping, the actual overflow turns out to be just 47. Similarly for Yaroslavl', the estimated overflow is 59 but the actual is just 31. The majority of the discrepancy comes from not reversing those +100% modifiers.
The modifier reversal can thus be done in Python with the following that I essentially just converted from the SDK's CvCity::changeOverflowProduction() function:
Code:
100 * iHurryOverflow / city.getBaseYieldRateModifier(gc.getInfoTypeForString("YIELD_PRODUCTION"), city.getProductionModifier())
Suggestion: Add Current Production
The difference in those previous estimates comes from the fact that the city still contributes actual hammers to the build after the whip. In Yekaterinburg, my people were still producing 8 hammers after the whipping so the raw overflow was really 95; half of which gives us the 47 actual overflow. In Yaroslavl' I whipped away part of the production so they only added 4 more hammers giving a raw overflow of 63, resulting in the actual overflow of 31.
While you could MM the city to change the production any way you want after whipping, I expect that most people don't bother. And I claim without any evidence whatsoever that the post-whip production will normally be pretty close to the pre-whip production or at least it'll be a lot closer than assuming zero post-whip production. So why not factor that pre-whip production into the overlap like below?
Code:
iHurryOverflow = pHeadSelectedCity.hurryProduction(HURRY_WHIP) - pHeadSelectedCity.productionLeft() + pHeadSelectedCity.getCurrentProductionDifference(True, False)
Bug #2: Overflow Cap
The current estimates ignore the overflow cap. Somewhere along the line (I think in a vanilla patch) Firaxis implemented an overflow cap to reduce "pre-build" exploits and we need to factor this in too. The maximum overflow you can get is the production cost of the current build. Here's another example:

This time there are no production modifiers in play but we still get way less overflow. The reason is the cap. An epic scout costs 22 hammers, so our raw overflow of 49 (44 from the whip and 5 from next turn's production) becomes a measly 22. Those bastards! What'd they do with my 27 hammers? Let's come back to that in a minute. For now, enforcing the cap, which is nice and simple:
Code:
iMaxOverflow = min(pHeadSelectedCity.getProductionNeeded(), iHurryOverflow)
Feature Request: Free Money
So what happened to those 27 hammers I lost? Did they just go away? Note the Treasury portion of my image. The turn of the whip, I had 105 gold and was losing 3 per turn, so the next turn I should have had 102. But instead, I have 129. There's my hammers; they got turned into gold!
An oddity about this is that this gold amount is calculated before the modifier reversal that I talked about first. As a result there is actually a fairly exploitive strategy based on this where you crank up a super-producing military city to build the cheapest unit available like a chariot and just delete them after production. Because of modifiers like the HE and a military academy you get a huge overflow that gets turned into money after the overflow cap is applied and the result is substantially better than if you had simply built Wealth. This has been discussed in overflow mechanics topics and I first saw it in an OCC game report. Despite that I can see how you might rule this part of overflow knowledge to be too obscure or too revealing of internal mechanics, but since I'm probably putting it in my Frankenstein version (BUGenstein?) anyway I figure I should propose it for general use.
This would require a tweaking of the alert and city bar output and either a bigger column or second column on the CDA. The money is calculated based on a global define so let's do that, again using similar logic to the SDK code.
Code:
iOverflowGold = max(0, iHurryOverflow - iMaxOverflow) * gc.getDefineINT("MAXED_UNIT_GOLD_PERCENT") / 100
So, if all fixes and suggestions are implemented, here's what the CvMainInterface.py code would look like:
Code:
if (BugCityScreen.isShowWhipAssist() and pHeadSelectedCity.canHurry(HURRY_WHIP, False)):
iHurryPop = pHeadSelectedCity.hurryPopulation(HURRY_WHIP)
[COLOR="Red"][s]#iHurryOverflow = pHeadSelectedCity.hurryProduction(HURRY_WHIP) - (pHeadSelectedCity.getProductionNeeded() - pHeadSelectedCity.getProduction())[/s][/COLOR]
[COLOR="Green"]iHurryOverflow = pHeadSelectedCity.hurryProduction(HURRY_WHIP) - pHeadSelectedCity.productionLeft() + pHeadSelectedCity.getCurrentProductionDifference(True, False)
iMaxOverflow = min(pHeadSelectedCity.getProductionNeeded(), iHurryOverflow)
iOverflowGold = max(0, iHurryOverflow - iMaxOverflow) * gc.getDefineINT("MAXED_UNIT_GOLD_PERCENT") / 100
iHurryOverflow = 100 * iMaxOverflow / pHeadSelectedCity.getBaseYieldRateModifier(gc.getInfoTypeForString("YIELD_PRODUCTION"), pHeadSelectedCity.getProductionModifier())
if (iOverflowGold > 0):
szBuffer = localText.getText("INTERFACE_CITY_PRODUCTION_WHIP_PLUS_GOLD", (pHeadSelectedCity.getProductionNameKey(), pHeadSelectedCity.getProductionTurnsLeft(), iHurryPop, iHurryOverflow, iOverflowGold))
else:[/COLOR]
szBuffer = localText.getText("INTERFACE_CITY_PRODUCTION_WHIP", (pHeadSelectedCity.getProductionNameKey(), pHeadSelectedCity.getProductionTurnsLeft(), iHurryPop, iHurryOverflow))
Code:
<TEXT>
<Tag>INTERFACE_CITY_PRODUCTION_WHIP_PLUS_GOLD</Tag>
<English>%s1 (%d2) %d3[ICON_ANGRYPOP] %d4[ICON_PRODUCTION] %D5[ICON_GOLD]</English>
<French>%s1 (%d2) %d3[ICON_ANGRYPOP] %d4[ICON_PRODUCTION] %D5[ICON_GOLD]</French>
<German>%s1 (%d2) %d3[ICON_ANGRYPOP] %d4[ICON_PRODUCTION] %D5[ICON_GOLD]</German>
<Italian>%s1 (%d2) %d3[ICON_ANGRYPOP] %d4[ICON_PRODUCTION] %D5[ICON_GOLD]</Italian>
<Spanish>%s1 (%d2) %d3[ICON_ANGRYPOP] %d4[ICON_PRODUCTION] %D5[ICON_GOLD]</Spanish>
</TEXT>
And the results in-game on two of my above examples:
