Fixing General Winter

Baldyr

"Hit It"
Joined
Dec 5, 2009
Messages
5,530
Location
Sweden
This is the current Russian UP from UniquePowers.py:
Code:
#------------------RUSSIAN U.P.-------------------

        def russianUP(self):
                pRussia = gc.getPlayer(iRussia)
                teamRussia = gc.getTeam(pRussia.getTeam()) 
                for x in range(tRussianTopLeft[0], tRussianBottomRight[0]):
                        for y in range(tRussianTopLeft[1], tRussianBottomRight[1]):
                                pCurrent = gc.getMap().plot( x, y )
                                if (pCurrent.getOwner() == iRussia):
                                        iNumUnitsInAPlot = pCurrent.getNumUnits()
                                        if (iNumUnitsInAPlot):
                                                for i in range(iNumUnitsInAPlot):
                                                        unit = pCurrent.getUnit(i)
                                                        if (teamRussia.isAtWar(unit.getOwner())):
##                                                                print("hp", unit.currHitPoints() )
##                                                                print("damage", unit.getDamage() )
                                                                unit.setDamage(unit.getDamage()+8, iRussia)
##                                                                print("hp now", unit.currHitPoints() 
##                                                                print("damage", unit.getDamage() )
Firstly, the tiles within the defined map coordinates are checked every single turn (after the Russian spawn). This is wasteful since most of the time there will be no foreign unit in this area - and unless they are an invading force the rule won't apply anyway.

Secondly, the AI loses its capacity to carry out offensive operations because every single unit gets automatic damage on every turn - even while stationary and healing previous damage. So it would help if only moving units were affected by this - healing is already hampered outside of friendly territory.

This is why I'm proposing a mod-mod (or rather a mod-mod-component) that relies on the onUnitMove callup from the SDK. This event needs to be included in CvRFCEventHandler.py and the call to russianUP() needs to be located here instead of being connected to the onBeginGameTurn via UniquePowers.UniquePowers.checkTurn().

This is what the improved russianUP() method could look like:
Code:
        def russianUP(self, pUnit, pPlot, pOldPlot):
                iOwner = pUnit.getOwner()
                iOwnerTeam = gc.getPlayer(iOwner).getTeam()
                if pRussia.isAlive() and teamRussia.isAtWar(iOwnerTeam):
                        if iOwner != [COLOR="Red"]pOldPlot.getOwner() == [/COLOR]pPlot.getOwner() == iRussia:
                                pUnit.setDamage(pUnit.getDamage() + [COLOR="Red"]8[/COLOR], iRussia)
Also, these assignment statements should go into the top of the module - there is no need to define these every single time:
Code:
pRussia = gc.getPlayer(iRussia)
teamRussia = gc.getTeam(pRussia.getTeam())
This would also mean that the invading unit must move from one Russian tile into another Russian tile, which is somewhat less severe as the current rule. But this can easily be edited out by deleting the red marked code above. Or the amount of damage could be increased to compensate.

Thoughts? Ideas?
 
In your version "General Winter" would fight even in the Mexico? Doesn't seem right.
Winter was deadly for static armies, not so much for moving ones.
Generally ( :) ) I like the idea. But dmg should be applied when army attacked (and didn't move) as well. Or even harder. And I like version without red code :p
 
The problem with damaging stationary units is that they will stay stationary since the AI won't move then until they are (more or less) fully healed. So a healing enemy shouldn't take (any more) damage, or the units will be stationary most of the time until the war is over. (The proper way would be to change the AI behavior in the SDK however.)

But you're of course right about the latitude issue - the rule of General Winter should only apply in temperate and above regions. The easiest way would be to implement a check for the Y coordinate:
Code:
if pPlot.getY() > iLimit:
(Where iLimit would be the Y coordinate of the latitude. Any suggestions?)

Personally I'm going to extend this into all enemy territory - not only within Russian borders. Call it a "attrition mod" then. :p And General Winter can make a comeback as a battlefield event - affecting both sides of the conflict within some Arctic or Subarctic region. (Not every single turn, obviously.) But nothing connected to the Russian player as such. (So there would be another Russian UP, then.)
 
Looks good, the current UP is really quite abusable against the AI.

But I agree with LuKo that it should only affect Russia proper.
 
A Russia that is expanding beyond its historical bounds is likely to collapse anyway, right? So a Russian Mexico isn't really such a huge issue.

But of course its possible to restrict the area of effect in the X axis also.
Code:
if pPlot.getX() > 64 and pPlot.getY() > 48:
Since you're both actively working on your own mod-mods - why not try something like this out? I'd be interested in hearing about any actual testing. :D
 
I've already made a mental note on the inside of my forehead ;) It's just that I'm already working on multiple features simultaneously and don't want to start testing another. I only hope I don't forget this. Maybe an additional note on the other side of my forehead won't hurt :D
 
So this could be the whole thing, then (without all the annoying indentation or ridiculously elongated lines):
Code:
        def russianUP(self, pUnit, pPlot, pOldPlot):
                iOwner = pUnit.getOwner()
                if ( pPlot.getX() > 64 and pPlot.getY() > 48 \
                     and pRussia.isAlive()\
                     and teamRussia.isAtWar(gc.getPlayer(iOwner).getTeam())\
                     and iOwner != pOldPlot.getOwner() == pPlot.getOwner() == iRussia ):
                        pUnit.setDamage(pUnit.getDamage() + 8, iRussia)
 
I would include it if units who fought this turn (but didn't actually move, because they have withdrawn or there is more units on the attacked plot) would take the damage as well.
I'm not sure how to implement something like this, at least without adding additional code in several places. All I could find in the API was CyUnit.isCombat() - I'm assuming that CyUnit.isDefending() and CyUnit.isFighting() expire once the actual action is over? :confused:

What do you mean by "or there is more units on the attacked plot"? One single unit takes no attrition but two units will? :confused:
 
Or, if non-moving units take damage then it breaks my basic idea. Because the code would be executed on the onUnitMove call up. So we're back at the onBeginGameTurn call up looping coordinates, then? This is already in the mod and working, so no need to "fix" that, right?
 
How about you add a promotion called ''Russian Winter" that is unobtainable, and then give it to enemy units inside Russian borders and remove it when they leave. It could reduce their strength X percent, or other things like that.
 
I actually can't do anything with XML modding myself at this point. But you're welcome to try such an approach... Anything that gets the job done, I guess.
 
Or, if non-moving units take damage then it breaks my basic idea. Because the code would be executed on the onUnitMove call up. So we're back at the onBeginGameTurn call up looping coordinates, then? This is already in the mod and working, so no need to "fix" that, right?

So maybe dmg at the end of fight?
 
Hmm... That would be onCombatResult, then? But what units that are stationary/moving that aren't involved in combat?
 
Could this not apply to the Mongols? They weren't really fazed by the winter and actualy used it against the Russians by traveling over frozen rivers.
 
Could this not apply to the Mongols? They weren't really fazed by the winter and actualy used it against the Russians by traveling over frozen rivers.
My own mod-mod idea ("Attrition mod-mod") would apply the General Winter rule to all enemy territories. But, some unit types - like primitive melee units or nomadic units like Horse Archers - would not be affected by attrition. Because they aren't dependent on supply lines but can rather "live off the land". This also applies to some modern unit types, like the Paratrooper or really any unit with the Commando promotion.

I actually already did this - it was my first programming exercise some six months ago. But I never got it to work exactly how I wanted, so I never published any of it. Such things should really be done in C++ because of the lag involved, but if I can figure out a way to do these things with Python only - who knows? I know a lot more today than I did back then, after all.
 
onCombatResult AND onUnitMove ]:-> Let them suffer!
So that would be double the damage if you move and fight then? And stationary units (who are healing anyway) would be spared any further damage?
 
So what exactly is supposed to happen onCombatResult then? Will only the foreign unit that survives the combat get the (additional) damage - or all units on that tile. (Because there was a battle and the entire stack should get punished, right?)
Code:
        def russianUP(self, argsList):
                bCombatResult = len(argsList) == 1
                if bCombatResult:
                        pUnit = argsList[0]
                        pPlot = pOldPlot = pUnit.plot()
                else:
                        pPlot, pUnit, pOldPlot = argsList
                iOwner = pUnit.getOwner()
                if ( pPlot.getX() > 64 and pPlot.getY() > 48 \
                     and pRussia.isAlive()\
                     and teamRussia.isAtWar(gc.getPlayer(iOwner).getTeam())\
                     and iOwner != pOldPlot.getOwner() == pPlot.getOwner() == iRussia ):
                        if bCombatResult:
                                for i in range(pPlot.getNumUnits()):
                                        pUnit = pPlot.getUnit(i)
                                        if pUnit.getOwner() == iOwner:
                                                pUnit.setDamage(pUnit.getDamage() + 5, iRussia)
                        else:
                                pUnit.setDamage(pUnit.getDamage() + 5, iRussia)
The additions actually make this a dual purpose method. The by passing it the argsList from each SDK call-up the method will know how to extract/fetch all the values it needs. So the method invocation will be the same for both onUnitMove and onCombatResult:
Code:
self.up.russianUP(argsList)
The only thing missing is to add the onUnitMove() method to CvRFCEventHandler module. Under def __init__:
Code:
                eventManager.addEventHandler("unitMove",self.onUnitMove)
And add the onUnitMove() method itself:
Code:
        def onUnitMove(self, argsList):
                self.up.russianUP(argsList)
edit: Note that I lowered the actual amount of damage from 8 to 5 (for a combined total of 10 for both moving and fighting). The two types of attrition could however be different values, like 4 and 6 respectively.
 
Top Bottom