[Warlords] How to set a city goal

Paolo80

Chieftain
Joined
Dec 20, 2019
Messages
85
Hi guys,

I have a question on modding.

I want to force units (not only human player, but also AI players) to attack a specific city. In other words I want every player try to conquest the same city.

How can I do? I have to modify some python files?

Thanks everyone for help
 
Some of the XML files contain values that guide AI decisions, e.g. "iAIWeight" and "iFlavor". Apart from that, AI behavior is handled almost exclusively by the DLL (in this case mainly AI_findTargetCity, AI_targetCityValue in CvGameCoreDLL\CvPlayerAI.cpp), with only a handful of functions exposed to Python (I think just those at the end of Warlord’s CyPlayerInterface1.cpp). Without modifying (and recompiling) the DLL, I’m afraid you could only give that specific city (Rome, I gather from your other posts) a high population, many wonders and resources; holy city should also help. However, the AI will probably still prefer targeting nearby cities. Getting the AI into a war with the Roman Empire probably isn't that difficult; the scenario can set at-war status initially, or the Romans could be a minor civ perpetually at war with everyone.
 
Maybe I could set an high city value. In other words, I could set the city x,y score 1000 or 10000 to force AI to attack it. Is it possible changing only python files? Maybe I could set an high city population and fix it for all the game turns.
 
Last edited:
You can set a high initial population through the scenario file, or set the population at any time through Python. E.g. in onBeginPlayerTurn (CvEventManager.py), you could use a line like:
gc.getPlayer(iPlayer).getCapitalCity().setPopulation(30)
I haven't tested that; and you'd need a condition that checks whether iPlayer is the Romans. Maybe just
if iPlayer == 1:
if the Romans are in player slot 1.
I think 1 population increases the target value by 0.75 on average:
Code:
iValue += ((pCity->getPopulation() * (50 + pCity->calculateCulturePercent(getID()))) / 100);
I.e. population counts half if the attacker has 0 city culture. And at the end:
Code:
if (bRandomize)
{
   iValue += GC.getGameINLINE().getSorenRandNum(((pCity->getPopulation() / 2) + 1), "AI Target City Value");
}
bRandomize is true except when computing the trade value of a city. So this adds a random number between 1 and 0.5*population. You could probably set a high city culture value for each player, but since the DLL code uses the culture percentage, that wouldn’t really help.

I’ve just noticed that multiple wonders don’t actually increase the target value:
Code:
if (pCity->hasActiveWorldWonder())
{
   iValue += 2;
}
This has been improved in BtS:
Code:
if (pCity->hasActiveWorldWonder())
{
   iValue += 1 + pCity->getNumWorldWonders();
}
So, for BtS, one could create numerous wonders in XML that have no abilities and give Rome all those dummy wonders. However, even BtS counts just +1 for each wonder ...

For Warlords, a resource without abilities could work:
Code:
for (iI = 0; iI < NUM_CITY_PLOTS; iI++)
{
   pLoopPlot = plotCity(pCity->getX_INLINE(), pCity->getY_INLINE(), iI);
   if (pLoopPlot != NULL)
   {
      if (pLoopPlot->getBonusType(getTeam()) != NO_BONUS)
      {
         iValue++;
      }
The getBonusType call checks whether the resource is revealed to the attacker. But the human player isn’t supposed to see them. One could let all AI civs start with a new technology that merely reveals the dummy resource and cannot be researched. The tech and resource would probably appear in Civilopedia though.

One last idea: This condition
Code:
if (pCity->isEverOwned(getID()))
{
   iValue += 3;
}
could perhaps be made true through a Python call to CyPlayer.acquireCity for another +3 target value.

Even if the AI does choose Rome as its target city, AI city attackers may still end up attacking other cities along their path to Rome; but I guess that isn’t necessarily a bad thing.
 
About last condition isEverOwned, could you write me python code to made true through ìCyPlayer.acquireCity?
 
Not easily. I don't normally use Python for this sort of thing. :(
A quick, naive attempt (in CvEventManager.py):
Code:
def onBeginGameTurn(self, argsList):
       'Called at the beginning of the end of each turn'
       iGameTurn = argsList[0]
       if iGameTurn == 0:
           romans = gc.getPlayer(1) # Assumes that the Romans have player id 1
           romanCapital = romans.getCapitalCity()
           for i in range(gc.getMAX_CIV_PLAYERS()):
               p = gc.getPlayer(i)
               if p.isAlive() and i != romans.getID():
                   p.acquireCity(romanCapital, False, True)  # bConquest=False, bTrade=True
           romans.acquireCity(romanCapital, False, True)
A lot of things could be wrong with this. The syntax might be incorrect; it might kill the Romans (it shouldn't if they start with multiple cities); I'm not sure if the scenario setup is already complete at the start of game turn 0; acquiring the city will probably trigger your victory condition (that could hopefully be fixed by adding "not bTrade" to the victory check); the owner changes might look weird to the human player or even crash; the Roman capital will move (will have to move the Palace back to Rome through CyCity.setHasRealBuilding). I guess, as a starting point, it's better than nothing. A more experienced Python modder could probably improvise something better. Still, I expect that this will require some experimentation and might not work at all in the end.
 
I wrote this code:

pCity1 = CyMap().plot(32, 23)

if iGameTurn == 1:
for cPlayer in range(gc.getMAX_PLAYERS()):
pPlayer = gc.getPlayer(cPlayer)
if pPlayer.isAlive() and cPlayer != 4:
pPlayer.acquireCity(pCity1, False, True)​
gc.getPlayer(4).acquireCity(pCity1, False, True)​

But this gives me a python argument error.

What's the mistake?
 
I tried to force AI attack city using function CyTeam.AI_setWarPlan. Here the code:

PHP:
for iPlayer in range(gc.getMAX_PLAYERS()):
            player = gc.getPlayer(iPlayer)
            print(player)
             iTeam = player.getTeam()
            print(iTeam)
                    pTeam = gc.getTeam(iTeam)
            print(pTeam)
             pTeam.AI_setWarPlan(player, WarPlanTypes.WARPLAN_TOTAL)

But when I start the game, it gets this python error: "AttributeError: 'CyTeam' object has no attribute 'AI_setWarPlan'". It seems that AI_setWarPlan isn't defined. What's the mistake?
 
That function isn't exposed to Python; in particular, it's not in CyTeamInterface.cpp. declareWar is in there, but it looks like that will result in WARPLAN_DOGPILE.
 
I've solved adding this code in CvGameUtils.py

Code:
    def AI_unitUpdate(self,argsList):
        pUnit = argsList[0]

        iGameTurn = CyGame().getGameTurn()
        iMelee = gc.getInfoTypeForString("UNITCOMBAT_MELEE")
        iMounted = gc.getInfoTypeForString("UNITCOMBAT_MOUNTED")
        iSiege = gc.getInfoTypeForString("UNITCOMBAT_SIEGE")
        iClass = pUnit.getUnitCombatType()
        iPlayer = pUnit.getOwner()
        pGroup = pUnit.getGroup()
        iX = pUnit.getX()
        iY = pUnit.getY()
        pPlot =    gc.getMap().plot(iX,iY)
        pCity1 = CyMap().plot(32, 23).getPlotCity() #mediolanum
        pCity2 = CyMap().plot(35, 21).getPlotCity() #bedriacum
                pCity3 = CyMap().plot(38, 14).getPlotCity() #roma
        iMission = MissionTypes.MISSION_MOVE_TO
        iMissionType = MissionAITypes.MISSIONAI_ASSAULT

        if (iClass == iMelee or iClass == iMounted or iClass == iSiege) and pPlot.isCity() == False:

            if iGameTurn > 19 and iPlayer == 3:
                if pCity3.getOwner != iPlayer:
                    pGroup.pushMission(iMission, pCity3.getX(), pCity3.getY(), 0, False, False, iMissionType, pPlot, pUnit)
                    return True
                elif pCity2.getOwner != iPlayer:
                    pGroup.pushMission(iMission, pCity2.getX(), pCity2.getY(), 0, False, False, iMissionType, pPlot, pUnit)
                    return True
                elif pCity1.getOwner != iPlayer:
                    pGroup.pushMission(iMission, pCity1.getX(), pCity1.getY(), 0, False, False, iMissionType, pPlot, pUnit)
                    return True
                else:
                    return False

            if iGameTurn > 35 and iPlayer == 0:
                if pCity3.getOwner != iPlayer:
                    pGroup.pushMission(iMission, pCity3.getX(), pCity3.getY(), 0, False, False, iMissionType, pPlot, pUnit)
                    return True
                elif pCity2.getOwner != iPlayer:
                    pGroup.pushMission(iMission, pCity2.getX(), pCity2.getY(), 0, False, False, iMissionType, pPlot, pUnit)
                    return True
                elif pCity1.getOwner != iPlayer:
                    pGroup.pushMission(iMission, pCity1.getX(), pCity1.getY(), 0, False, False, iMissionType, pPlot, pUnit)
                    return True
                else:
                    return False

            if iGameTurn > 48 and iPlayer == 2:
                if pCity3.getOwner != iPlayer:
                    pGroup.pushMission(iMission, pCity3.getX(), pCity3.getY(), 0, False, False, iMissionType, pPlot, pUnit)
                    return True
                elif pCity2.getOwner != iPlayer:
                    pGroup.pushMission(iMission, pCity2.getX(), pCity2.getY(), 0, False, False, iMissionType, pPlot, pUnit)
                    return True
                elif pCity1.getOwner != iPlayer:
                    pGroup.pushMission(iMission, pCity1.getX(), pCity1.getY(), 0, False, False, iMissionType, pPlot, pUnit)
                    return True
                else:
                    return False

            if iGameTurn > 71 and iPlayer == 1:
                if pCity3.getOwner != iPlayer:
                    pGroup.pushMission(iMission, pCity3.getX(), pCity3.getY(), 0, False, False, iMissionType, pPlot, pUnit)
                    return True
                elif pCity2.getOwner != iPlayer:
                    pGroup.pushMission(iMission, pCity2.getX(), pCity2.getY(), 0, False, False, iMissionType, pPlot, pUnit)
                    return True
                elif pCity1.getOwner != iPlayer:
                    pGroup.pushMission(iMission, pCity1.getX(), pCity1.getY(), 0, False, False, iMissionType, pPlot, pUnit)
                    return True
                else:
                    return False


        return False

Where pCity1, pCity2 and pCity3 are the taget cities
 
Back
Top Bottom