Dune Wars Enhanced Patching

No comments without knowing the intended purpose
 
No comments without knowing the intended purpose

The code is to create the following result for the Fremen civ in this mod:
- When a combat arises involving the fremen civ, the loser is a melee or guardsman unit and a city(ies) with the building 'Deathstill' is close by (about 4 tiles from loser), the death of the unit results in the increase of 1 water point (the food in the mod) of the randomly chosen city's (in case of 2 or more cities nearby at the same distance) water (food) bar.

In the Dune book universe, the fremen would reclaim the water from the dead using the deathstill, even from enemies.
 
1) pCity.getNumRealBuilding(self.GetCheckInfo('BUILDING_DEATHSTILL'))
will still give weird results if building does not exist.
The bug util only tells you it does not exist but does not stop it from executing further.
So it will still check whether building with ID -1 exists, which I think is always true.

2) getNumRealBuilding only returns True for buildings which are built literally.
Buildings free like Monuments from Stonehenge will not count, thus for a special case like this, it may be ok if no wonders grant it free, otherwise it is screwed.

3) Rather than displaying the message at Loser Plot, you might as well display it at city which gains the benefit.
 
1) Can it really give weird results? I tested the code with a huge stack attack near a city, with and without a deathstill building, and the results were the intended always.

I'm actually not sure about the use of self.GetCheckInfo. When I started adding in my python lines I just used always gc.getInfoTypeForString directly, but the code from previous modders always used self.GetCheckInfo (having added the comment # Unsafe to perform getInfoTypeForString without checking success), so I thought now that i'm trying to improve my lines I should use it as well.

2) getNumRealBuilding is indeed enough here. I remember that to count free buildings as well one should use simply getNumBuilding.

3) That is indeed better. Also created a water icon to be displayed in the pointing bubble (it does not seem like getSymbolID can work?) and the text will call out the poor bastard loser's name.
I also had forgotten to add a check for the message to be displayed only for the fremen civ.
So, now:
Code:
if (pPlayer.isHuman() and pPlayer.getCivilizationType() == iFremen):
                                                CyInterface().addMessage(pWinner.getOwner(), True, 20, CyTranslator().getText("TXT_KEY_MESSAGE_CITY_ADD_WATER_ENEMY",(CityArray[iCity].getName(), pLoser.getName())), None, 2, 'Art\Interface\Buttons\water.dds', ColorTypes(54), CityArray[iCity].getX(), CityArray[iCity].getY(), True, True)
		                        elif (pPlayerLoser.isHuman() and pPlayerLoser.getCivilizationType() == iFremen):
                                                CyInterface().addMessage(pLoser.getOwner(), True, 20, CyTranslator().getText("TXT_KEY_MESSAGE_CITY_ADD_WATER_FREMEN",(CityArray[iCity].getName(), pLoser.getName())), None, 2, 'Art\Interface\Buttons\water.dds', ColorTypes(54), CityArray[iCity].getX(), CityArray[iCity].getY(), True, True)

Btw, is the check if not loopPlot.isNone() really needed if the "target plot" is a city?
 
1) I meant if there is a typo and 'BUILDING_DEATHSTILL' does not exist at all in the XML...
Who cares what previous modders do so long as you know what you are doing.

3) Why bother with all those checks to display message. Just display it to the city owner.

4) If it is a city, it can't be a none plot, but it is generally added just for extra precaution.
 
I appreciate an opinion about this little combat script that I wrote. It works, but what's the best way and am I missing something?

- If a Ecaz unit wins a combat inside it's own borders, vs any but barb and mech, there's a 20% chance that the defeated unit can be bribed if there's enough money on treasury for the bribe.

Copy of the Loser unit is initialized, getting the Loser's relevant stats as well. Inspired promos can't come along, though.
If AI, unit is always bribed. If single player human, a popup appears asking the player to bribe or kill:



If multiplayer, bribe is automatic for no time wastage.

Combat code:
Code:
def onCombatResult(self, argsList):
        pWinner,pLoser = argsList
        playerX = PyPlayer(pWinner.getOwner())
        unitX = PyInfo.UnitInfo(pWinner.getUnitType())
        playerY = PyPlayer(pLoser.getOwner())
        unitY = PyInfo.UnitInfo(pLoser.getUnitType())
        
        pPlayer = gc.getPlayer(pWinner.getOwner())
        pPlayerLoser = gc.getPlayer(pLoser.getOwner())
        pPlotLoser = pLoser.plot()
        eTeam = gc.getTeam(pPlayer.getTeam())
        
        iFremen = gc.getInfoTypeForString("CIVILIZATION_FREMEN")
        iEcaz = gc.getInfoTypeForString('CIVILIZATION_ECAZ')
        iGess = gc.getInfoTypeForString('CIVILIZATION_GESSERIT')
        iMelee = gc.getInfoTypeForString('UNITCOMBAT_MELEE')
        iGuard = gc.getInfoTypeForString('UNITCOMBAT_GUARDSMAN')
        iMech = gc.getInfoTypeForString('UNITCOMBAT_MECH')
        self.lInspiredProms = {
                                gc.getInfoTypeForString('PROMOTION_INSPIRED1') : 1,
                                gc.getInfoTypeForString('PROMOTION_INSPIRED2') : 1,
                                gc.getInfoTypeForString('PROMOTION_INSPIRED3') : 1,
                                gc.getInfoTypeForString('PROMOTION_INSPIRED4') : 1
                        }



        #Ecaz: combat bribe
        if pPlayer.getCivilizationType() == iEcaz:
                if CyGame().getSorenRandNum(100, "Bribe") < 20:
                        iBribe = (pLoser.getExperience() / 2 + pLoser.baseCombatStr() * 5)
                        iBriber = pPlayer.getID()
                        if pPlayer.getGold() > iBribe:
                                if pPlotLoser.getOwner() == iBriber:
                                        if not pLoser.isBarbarian():
                                                if not pLoser.getUnitCombatType() == iMech:
                                                        if pPlotLoser.getNumVisibleEnemyDefenders(pWinner) > 1:
                                                                newUnit = pPlayer.initUnit(pLoser.getUnitType(), pWinner.getX(), pWinner.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.NO_DIRECTION)
                                                        else:
                                                                newUnit = pPlayer.initUnit(pLoser.getUnitType(), pLoser.getX(), pLoser.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.NO_DIRECTION)
                                                        for i in range(gc.getNumPromotionInfos()):
                                                                if self.lInspiredProms.has_key(i): continue
                                                                newUnit.setHasPromotion(i, pLoser.isHasPromotion(i))
                                                        newUnit.setLevel(pLoser.getLevel())
                                                        newUnit.setExperience(pLoser.getExperience(), -1)
                                                        newUnit.finishMoves()
                                                        newUnit.setDamage(90, newUnit.getOwner())
                                                        if not pPlayer.isHuman():
                                                                pPlayer.changeGold(-iBribe)
                                                                CyInterface().addMessage(pLoser.getOwner(), True, 20, CyTranslator().getText("TXT_KEY_BRIBED_MULTI",(newUnit.getName(),)), None, 2, gc.getUnitInfo(newUnit.getUnitType()).getButton(), ColorTypes(7), newUnit.getX(), newUnit.getY(), True, True)
                                                        else:
                                                                if not gc.getGame().isGameMultiPlayer():
                                                                        #popup
                                                                        inewUnitX = newUnit.getX()
                                                                        inewUnitY = newUnit.getY()
                                                                        iUnitBribed = newUnit.getID()
                                                                        popupInfo = CyPopupInfo()
                                                                        popupInfo.setButtonPopupType(ButtonPopupTypes.BUTTONPOPUP_PYTHON)
                                                                        popupInfo.setText(CyTranslator().getText("TXT_KEY_MESSAGE_BRIBE", (iBribe, pLoser.getName(), pPlayerLoser.getCivilizationAdjectiveKey())))
                                                                        popupInfo.setData1(inewUnitX)
                                                                        popupInfo.setData2(inewUnitY)
                                                                        popupInfo.setData3(iUnitBribed)
                                                                        popupInfo.setOnClickedPythonCallback("Bribe")
                                                                        popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_BRIBE_UNIT", ()), pLoser.getButton())
                                                                        popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_KILL_UNIT", ()), 'Art/Interface/Buttons/Actions/Delete.dds')
                                                                        popupInfo.addPopup(iBriber)
                                                                else:
                                                                        pPlayer.changeGold(-iBribe)
                                                                        CyInterface().addMessage(pWinner.getOwner(), True, 20, CyTranslator().getText("TXT_KEY_BRIBER_MULTI",(iBribe, newUnit.getName(), pPlayerLoser.getCivilizationAdjectiveKey())), None, 2, gc.getUnitInfo(newUnit.getUnitType()).getButton(), ColorTypes(8), newUnit.getX(), newUnit.getY(), True, True)
                                                                        CyInterface().addMessage(pLoser.getOwner(), True, 20, CyTranslator().getText("TXT_KEY_BRIBED_MULTI",(newUnit.getName(),)), None, 2, gc.getUnitInfo(newUnit.getUnitType()).getButton(), ColorTypes(7), newUnit.getX(), newUnit.getY(), True, True)
        #End Ecaz: combat bribe

At CvScreensInterface.py:

Code:
def Bribe(argsList):
	iButtonId, inewUnitX, inewUnitY, iUnitBribed, iFlags, szText, bOption1, bOption2 = argsList
	pPlot = CyMap().plot(inewUnitX, inewUnitY)
        for i in xrange(pPlot.getNumUnits()):
                pUnit = pPlot.getUnit(i)
                if pUnit.getID() == iUnitBribed:
                        pPlayer = gc.getPlayer(pUnit.getOwner())
                        iBribe = (pUnit.getExperience() / 2 + pUnit.baseCombatStr() * 5)
                        if iButtonId == 0:
                                pPlayer.changeGold(-iBribe)
                        elif iButtonId == 1:
                                pUnit.kill(False, PlayerTypes.NO_PLAYER)
 
1) There is a convert function which duplicates standard stats like experience, name, level, damage etc...

2) CombatResults is triggered even for "non-combat" such as "defeating" workers or "defeating" ships by merely entering the city or fort. Those should be excluded.

3) For coastal assaults, it is possible for the new unit to swim when transports are fully loaded...

4) Why bother to loop through the plot units to find the unit. Just transfer player ID and unit ID and use getUnit. Might as well transfer iBribe as well since it is already calculated.
 
1) It seems that one can't convert a dead unit as the init newunit will turn dead as well.

2) Done.

3) Thanks. Actually units in this mod can't attack from inside transports. But I still need such checks for when air units attack from the desert (ocean) into land terrain. Used newUnit.jumpToNearestValidPlot(). Done.

4) *Slaps forehead* Done.

Spoiler :
Code:
def onCombatResult(self, argsList):
        pWinner,pLoser = argsList
        playerX = PyPlayer(pWinner.getOwner())
        unitX = PyInfo.UnitInfo(pWinner.getUnitType())
        playerY = PyPlayer(pLoser.getOwner())
        unitY = PyInfo.UnitInfo(pLoser.getUnitType())
        
        pPlayer = gc.getPlayer(pWinner.getOwner())
        pPlayerLoser = gc.getPlayer(pLoser.getOwner())
        pPlotLoser = pLoser.plot()
        eTeam = gc.getTeam(pPlayer.getTeam())
        
        iFremen = gc.getInfoTypeForString("CIVILIZATION_FREMEN")
        iEcaz = gc.getInfoTypeForString('CIVILIZATION_ECAZ')
        iGess = gc.getInfoTypeForString('CIVILIZATION_GESSERIT')
        
        iMelee = gc.getInfoTypeForString('UNITCOMBAT_MELEE')
        iGuard = gc.getInfoTypeForString('UNITCOMBAT_GUARDSMAN')
        iVehi = gc.getInfoTypeForString('UNITCOMBAT_VEHICLE')
        iSiege = gc.getInfoTypeForString('UNITCOMBAT_SIEGE')
        iMech = gc.getInfoTypeForString('UNITCOMBAT_MECH')
        iHornet = gc.getInfoTypeForString('UNITCOMBAT_HORNET')
        iWorker = gc.getInfoTypeForString('UNITCOMBAT_WORKER')
        iNone = gc.getInfoTypeForString('NONE')
        
        self.lInspiredProms = {
                                gc.getInfoTypeForString('PROMOTION_INSPIRED1') : 1,
                                gc.getInfoTypeForString('PROMOTION_INSPIRED2') : 1,
                                gc.getInfoTypeForString('PROMOTION_INSPIRED3') : 1,
                                gc.getInfoTypeForString('PROMOTION_INSPIRED4') : 1
                        }
        #Ecaz: combat bribe
        if pPlayer.getCivilizationType() == iEcaz:
                if CyGame().getSorenRandNum(100, "Bribe") < 20:
                        if not pLoser.isBarbarian():
                                if not (pLoser.getUnitCombatType() == iMech or pLoser.getUnitCombatType() == iWorker or pLoser.getUnitCombatType() == iHornet or pLoser.getUnitCombatType() == iNone):
                                        iBribe = (pLoser.getExperience() / 2 + pLoser.baseCombatStr() * 5)
                                        if pPlayer.getGold() > iBribe:
                                                iBriber = pPlayer.getID()
                                                if pPlotLoser.getOwner() == iBriber:
                                                        if pPlotLoser.getNumVisibleEnemyDefenders(pWinner) > 1:
                                                                newUnit = pPlayer.initUnit(pLoser.getUnitType(), pWinner.getX(), pWinner.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.NO_DIRECTION)
                                                        else:
                                                                newUnit = pPlayer.initUnit(pLoser.getUnitType(), pLoser.getX(), pLoser.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.NO_DIRECTION)
                                                        for i in range(gc.getNumPromotionInfos()):
                                                                if self.lInspiredProms.has_key(i): continue
                                                                newUnit.setHasPromotion(i, pLoser.isHasPromotion(i))
                                                        newUnit.setLevel(pLoser.getLevel())
                                                        newUnit.setExperience(pLoser.getExperience(), -1)
                                                        newUnit.finishMoves()
                                                        newUnit.setDamage(90, newUnit.getOwner())
                                                        #check if unit can remain on desert terrain
                                                        pPlotnewUnit = newUnit.plot()
                                                        if (pPlotnewUnit.getTerrainType() == gc.getInfoTypeForString('TERRAIN_COAST') or pPlotnewUnit.getTerrainType() == gc.getInfoTypeForString('TERRAIN_OCEAN')):
                                                                if (newUnit.getUnitCombatType() == iMelee or newUnit.getUnitCombatType() == iGuard or newUnit.getUnitCombatType() == iSiege or newUnit.getUnitCombatType() == iVehi):
                                                                        if not (newUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_STILLSUIT')) or newUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_SANDRIDER'))):
                                                                                newUnit.jumpToNearestValidPlot()
                                                        if not pPlayer.isHuman():
                                                                pPlayer.changeGold(-iBribe)
                                                                CyInterface().addMessage(pLoser.getOwner(), True, 20, CyTranslator().getText("TXT_KEY_BRIBED_MULTI",(newUnit.getName(),)), None, 2, gc.getUnitInfo(newUnit.getUnitType()).getButton(), ColorTypes(7), newUnit.getX(), newUnit.getY(), True, True)
                                                        else:
                                                                if not gc.getGame().isGameMultiPlayer():
                                                                        #popup
                                                                        iUnitBribed = newUnit.getID()
                                                                        popupInfo = CyPopupInfo()
                                                                        popupInfo.setButtonPopupType(ButtonPopupTypes.BUTTONPOPUP_PYTHON)
                                                                        popupInfo.setText(CyTranslator().getText("TXT_KEY_MESSAGE_BRIBE", (iBribe, pLoser.getName(), pPlayerLoser.getCivilizationAdjectiveKey())))
                                                                        popupInfo.setData1(iBriber)
                                                                        popupInfo.setData2(iUnitBribed)
                                                                        popupInfo.setData3(iBribe)
                                                                        popupInfo.setOnClickedPythonCallback("Bribe")
                                                                        popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_BRIBE_UNIT", ()), pLoser.getButton())
                                                                        popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_KILL_UNIT", ()), 'Art/Interface/Buttons/Actions/Delete.dds')
                                                                        popupInfo.addPopup(iBriber)
                                                                else:
                                                                        pPlayer.changeGold(-iBribe)
                                                                        CyInterface().addMessage(pWinner.getOwner(), True, 20, CyTranslator().getText("TXT_KEY_BRIBER_MULTI",(iBribe, newUnit.getName(), pPlayerLoser.getCivilizationAdjectiveKey())), None, 2, gc.getUnitInfo(newUnit.getUnitType()).getButton(), ColorTypes(8), newUnit.getX(), newUnit.getY(), True, True)
                                                                        CyInterface().addMessage(pLoser.getOwner(), True, 20, CyTranslator().getText("TXT_KEY_BRIBED_MULTI",(newUnit.getName(),)), None, 2, gc.getUnitInfo(newUnit.getUnitType()).getButton(), ColorTypes(7), newUnit.getX(), newUnit.getY(), True, True)
        #End Ecaz: combat bribe
                                                                        
def Bribe(argsList):
	iButtonId, iBriber, iUnitBribed, iBribe, iFlags, szText, bOption1, bOption2 = argsList
	pPlayer = gc.getPlayer(iBriber)
        if iButtonId == 0:
                 pPlayer.changeGold(-iBribe)
        elif iButtonId == 1:
                pUnit = pPlayer.getUnit(iUnitBribed)
                pUnit.kill(False, PlayerTypes.NO_PLAYER)

Excellent help. It's getting easier, I guess.
 
OK, i can't test right now, but I guess I just do this then:

Code:
pLoser.setDamage(90, pLoser.getOwner())
newUnit.convert(pLoser)
for i in self.lInspiredProms:
    newUnit.setHasPromotion(i, False)

Btw, I'm not sure why we need to call the player at setdamage.
 
You need to explore to look around for simpler codes.
There is isWater check for plots for instance, or domain check for units.

Anyway you don't have to worry about air units attacking because air battles do not trigger oncombatresults.
Defeating air and ships parked in cities will still trigger it
 
Thanks.
Most air units in this mod do regular combat. They are domain sea (desert) and move all terrain. The AI correctly uses them in this mod in all terrain. Aso, units of domain land may move in the sea (desert) if they have certain promos.

I can't use isWater check in this mod. It will be true in every single plot. That may have to do with whatever changes previous modders did to turn ocean into desert. I don't know. The xml tags still use the names ocean and coast for desert terrain.

But you're right. Always simplify. I should use canMoveAllTerrain() and that is enough for the units' check. I don't have to check if the unit have promos that enables moves in all terrain, so that's much better.
 
Oh God, you are all sorts of awesome! :)

That said, my intuition tells me the Atreides special is overpowered. One thing that keeps civs for just steamlolling their enemies once they get going is that the newly conquered cities are a drain on their resources and don't contribute meaningfully to the war effort. I would go for something less extreme (if possible), such as halving the assimilation time. This would still be noticable boon without turning Atreides into an unstoppable monster once they get wind in their sails.

Also: Food for thought, should Richese and Ix have a vendetta?
 
that said, my intuition tells me the atreides special is overpowered. One thing that keeps civs for just steamlolling their enemies once they get going is that the newly conquered cities are a drain on their resources and don't contribute meaningfully to the war effort. I would go for something less extreme (if possible), such as halving the assimilation time. This would still be noticable boon without turning atreides into an unstoppable monster once they get wind in their sails.
12345
 
Horatius,

You got me, I will stop playing the game until the new update comes through :)
 
Top Bottom