Custom python code for units

pepper2000

King
Joined
Apr 14, 2013
Messages
899
Hi all,

I'm looking for some help on adding units that have special effects, e.g. settlers that can only settle on particular terrains. Here's an example of a unit that can only settle in cislunar space.

Spoiler :

Code:
<!-- New settler units-->
        <UnitInfo>
            <Class>UNITCLASS_CISLUNARSETTLER</Class>
            <Type>UNIT_CISLUNARSETTLER</Type>
            <Capture>UNITCLASS_CAPTIVE_CIVILIAN</Capture>
            <Combat>UNITCOMBAT_SETTLER</Combat>
            <SubCombatTypes>
                <SubCombatType>UNITCOMBAT_EARLY_SPACESHIP</SubCombatType>
                <SubCombatType>UNITCOMBAT_CIVILIAN</SubCombatType>
                <SubCombatType>UNITCOMBAT_SPECIES_HUMAN</SubCombatType>
                <SubCombatType>UNITCOMBAT_MOTILITY_PILOTING</SubCombatType>
                <SubCombatType>UNITCOMBAT_QUALITY_INFERIOR</SubCombatType>
                <SubCombatType>UNITCOMBAT_GROUP_SOLO</SubCombatType>
                <SubCombatType>UNITCOMBAT_SIZE_HUGE</SubCombatType>
                <SubCombatType>UNITCOMBAT_HEALS_AS_PEOPLE</SubCombatType>
                <SubCombatType>UNITCOMBAT_HEALS_AS_SPACECRAFT</SubCombatType>
            </SubCombatTypes>
            <Domain>DOMAIN_LAND</Domain>
            <MapCategoryTypes>
                <MapCategoryType>MAPCATEGORY_EARTH</MapCategoryType>
                <MapCategoryType>MAPCATEGORY_SPACE</MapCategoryType>
            </MapCategoryTypes>
            <DefaultUnitAI>UNITAI_SETTLE</DefaultUnitAI>
            <Description>TXT_KEY_UNIT_CISLUNARSETTLER</Description>
            <ExtraHoverText>TXT_KEY_UNIT_CISLUNARSETTLER_HOVER</ExtraHoverText>
            <Help>TXT_KEY_UNIT_CISLUNARSETTLER_HOVER</Help>
            <Civilopedia>TXT_KEY_UNIT_SETTLER_PEDIA</Civilopedia>
            <Strategy>TXT_KEY_UNIT_SETTLER_STRATEGY</Strategy>
            <Advisor>ADVISOR_GROWTH</Advisor>
            <bCanMoveAllTerrain>1</bCanMoveAllTerrain>
            <bCanMoveImpassable>1</bCanMoveImpassable>
            <bFood>1</bFood>
            <UnitAIs>
                <UnitAI>
                    <UnitAIType>UNITAI_SETTLE</UnitAIType>
                    <bUnitAI>1</bUnitAI>
                </UnitAI>
            </UnitAIs>
            <PrereqBuilding>BUILDING_COMMERCIAL_SPACEPORT</PrereqBuilding>
            <PrereqTech>TECH_SPACE_COLONIES</PrereqTech>
            <ProductionTraits>
                <ProductionTrait>
                    <ProductionTraitType>TRAIT_IMPERIALIST</ProductionTraitType>
                    <iProductionTrait>50</iProductionTrait>
                </ProductionTrait>
            </ProductionTraits>
            <iCost>4850</iCost>
            <iAdvancedStartCost>100</iAdvancedStartCost>
            <iMinAreaSize>-1</iMinAreaSize>
            <iMoves>24</iMoves>
            <iNukeRange>-1</iNukeRange>
            <iExtraCost>10</iExtraCost>
            <iAsset>1</iAsset>
            <UnitMeshGroups>
                <iGroupSize>1</iGroupSize>
                <fMaxSpeed>1.75</fMaxSpeed>
                <fPadTime>1</fPadTime>
                <iMeleeWaveSize>1</iMeleeWaveSize>
                <iRangedWaveSize>0</iRangedWaveSize>
                <UnitMeshGroup>
                    <iRequired>1</iRequired>
                    <EarlyArtDefineTag>ART_DEF_UNIT_SPACECRAFT_SETTLER</EarlyArtDefineTag>
                </UnitMeshGroup>
            </UnitMeshGroups>
            <FormationType>FORMATION_TYPE_DEFAULT</FormationType>
            <Actions>
                <Action>
                    <MissionType>MISSION_PLANET_LAND</MissionType>
                    <iCost>
                        <Adapt>
                            <ID>ADAPT_DEFAULT</ID>
                            <Constant>0</Constant>
                        </Adapt>
                    </iCost>
                    <ActionOutcomes>
                        <Outcome>
                            <OutcomeType>OUTCOME_UPGRADE_TO_PLANET_SETTLER</OutcomeType>
                            <iChance>100</iChance>
                            <PythonName>BuildCityCislunar</PythonName>
                            <Python>
                                from CvPythonExtensions import CyGlobalContext
                                def isPossible(unit,plot):
                                    gc = CyGlobalContext()
                                    if not gc.getActivePlayer().canFound(plot.getX(),plot.getY()):
                                        return False
                                    valid_terrain_strings = ['SPACE']
                                    valid_terrains = [gc.getInfoTypeForString('TERRAIN_'+t) for t in valid_terrain_strings]
                                    return plot.getTerrainType() in valid_terrains
                                def doOutcome(unit,plot,eDefPlayer, eDefUnitType):
                                    gc = CyGlobalContext()
                                    gc.getActivePlayer().found(plot.getX(),plot.getY())
                                def getDisplay(unit,plot):
                                    return u"Build a city on this plot."
                                def getAIValue(unit, plot):
                                    return 1
                            </Python>
                        </Outcome>
                    </ActionOutcomes>
                </Action>
            </Actions>
        </UnitInfo>


It works fine when I run normally, but when I turn C2C on debug mode, I get the error message noted below. I am not able to click any of the buttons to see what the debugger has to say. Does anyone know about this? I also recognize that no units currently in the game have this sort of embedded python code.

Otherwise, what would be the proper way to do what I am trying to do? I am looking for a general solution that would allow me to insert any custom python code both for when the mission can be executed and for the outcome, because I have a variety of other things like the Lunar Rover landing, planetary probes, etc.

Just to add more context, the isPossible function seems to work all right even in debug mode. I am able to move the Cislunar Settler around, and the settle icon appears and disappears depending on whether the unit is over the right terrain. It's only when I click the button to settle that I get a problem.

My best guess as to the cause of the problem is that when I try to execute the unit's command, an extra call to makePythonObject() is triggered in CvOutcome.cpp, which causes the assert (CvDLLPythonIFaceBase.h, line 86) to fail. But I'm in over my head on this one.
 

Attachments

  • ErrorMessage.png
    ErrorMessage.png
    187.6 KB · Views: 44
Last edited:
I suspect that I never tested that in debug mode.
The assert checks the internals of the python object and assumes that the reference count is 1 from the Boost Python Object plus 1 from the explicit reference count increase. This seems to be the case for class instances passed that way but in the specific case of the function call for doOutcome two enums are passed to that function. It may be that because of Boost internals the reference is increased once more to a total of 3. As long as that is reduced again at the end of the function when the Boost Python Object is destroyed that is fine.
I would suggest removing that assertion from makePythonObject and instead add one for testing purposes after line 1392 in CvOutcome.cpp like this:
Code:
assert(pyDefeatedPlayer->ob_refcnt==1);
assert(pyDefeatedUnitType->ob_refcnt==1);
If those don't trigger, then there won't be any leakage.

In general your usage of this tag is fine. That is how it is intended to be used.
Two things though:
getActivePlayer should not be used because of Multiplayer sync issues. Instead go for the owning player of unit.
In the long run, getAIValue should probably be connected to the AI calculations for founding value.
 
Thanks a lot. I'll try that tonight. For clarification, should I substitute getActivePlayer with getAIValue?
 
Thanks a lot. I'll try that tonight. For clarification, should I substitute getActivePlayer with getAIValue?
No, those two lines were about different things.
Instead of getActivePlayer use the unit that is passed to the function and get its owning player (I don't remember the method name, too long since I modded Civ 4).

getAIValue is supposed to return a value that represents the value of the action if the AI uses it on the given plot so in the long run it should probably return the value of a city there. I don't know the current state of the AI in regards to using actions so it is very likely more than that is needed to get the AI to use it. For now you can ignore that for your tests.
 
Thanks a lot AIAndy. First, is getOwner() the method I want to use to get the player that owns the unit?

Second, yes I haven't thought at all about how the AI is going to use these units. First they have to know to build the space settlers when they become available, second they have to know to move them to the appropriate terrain, and third they have to know to execute the custom city founding mission. One of these days I (or someone else) will have to figure that out.
 
Thanks a lot AIAndy. First, is getOwner() the method I want to use to get the player that owns the unit?
Yes, I just looked that up, but that returns an enum / id so a call to gc.getPlayer is needed:
Code:
gc.getPlayer( unit.getOwner( ) )
 
That sounds pretty cool! So, when you can limit the ability to settle to a certain set of terrains this way, could you limit it to a certain improvment as well?
My idea early on was that you had to send a set of ships that build and upgrade an improvment on the moon (Base -> Outpost ->?) and then have a settler that can only settle on these to simulate the needed cargo missions before you can build a self sustaining city on the moon. It was also said this concept might be useful for the nomadic start.
 
That sounds pretty cool! So, when you can limit the ability to settle to a certain set of terrains this way, could you limit it to a certain improvment as well?
My idea early on was that you had to send a set of ships that build and upgrade an improvment on the moon (Base -> Outpost ->?) and then have a settler that can only settle on these to simulate the needed cargo missions before you can build a self sustaining city on the moon. It was also said this concept might be useful for the nomadic start.

Very doable. Just add the python code to check for improvement. That's a good idea. Since I moved the Lunar Settler to the Lunar Bases tech, going through a chain of upgrades will make the first round of lunar settlement a big project.
 
Second, yes I haven't thought at all about how the AI is going to use these units. First they have to know to build the space settlers when they become available, second they have to know to move them to the appropriate terrain, and third they have to know to execute the custom city founding mission. One of these days I (or someone else) will have to figure that out.
Normally you'd program a new unit AI type in the code but since you're going to be using mostly python, you'll have to program some python that specifically works on something special about this unit. The code will check if there's a python AI routine that should apply 'in this case' when the unit comes up to run through it's decision making parameters. You have to program the python to instruct this particular unit on the basis of a unique feature or set of features on the unit. For example, if you want to be very specific, you'd say, if this unit happens to be a Cislunar Settler unit type, then do what this bit of python is going to tell it to do.

There are examples of this kind of programming in the Gods of Old mod python for the prophets. We probably have some examples as well in our python.

The thing is, settler code is a REAL issue of great complexity. You would probably be FAR better served to develop some ability to work with the C++ for that. Given how easily you understand the python beyond what I can understand how to employ and how well you were able to work with the XML implementation of it, I'd be deeply shocked if you'd find the C++ difficult to figure out. I can help but my time is growing thinner and thinner. That said, if you did get knowledgeable on that side of things, you'd find it's much easier to make the breakthroughs you need to make in general to achieve the space era goals and beyond.

How to value the creation of a unit, that's usually easy enough but again some direct in-code adjustments to city decision makingto account for the unique motivators to train a cislunar settler would be possibly much easier than anything you'd try to do via python.
 
The thing is, settler code is a REAL issue of great complexity. You would probably be FAR better served to develop some ability to work with the C++ for that. Given how easily you understand the python beyond what I can understand how to employ and how well you were able to work with the XML implementation of it, I'd be deeply shocked if you'd find the C++ difficult to figure out. I can help but my time is growing thinner and thinner. That said, if you did get knowledgeable on that side of things, you'd find it's much easier to make the breakthroughs you need to make in general to achieve the space era goals and beyond.

In the long run, that would probably make me most effective. It's been years since I've worked with C++, but I'm sure I'd pick it right back up. The hardest part is understanding all the components of the mod and how everything fits together.

The thing is, though, that I am hoping to wrap up my modding work soon. It's been a lot of fun, but I have other projects I'm anxious to get to, so I am thinking about my C2C work more in terms of tying up loose ends.
 
Very doable. Just add the python code to check for improvement. That's a good idea. Since I moved the Lunar Settler to the Lunar Bases tech, going through a chain of upgrades will make the first round of lunar settlement a big project.

Yes, that was excactly my intention! I also had some buildings in my building tree that were only buildable by a unit (like some Dances), and without them you can't progeress any further. For your second colony, if they were connected, these ships are mostly not needed, since the first city could then provide it. It would me much easier (and cheaper) to manufacture solar panels and wires in your lunar city and transport them to your 2nd colony then sending them from earth to the 2nd one.

Now I'm really keen to take a look in what you have done by now :goodjob:
 
In the long run, that would probably make me most effective. It's been years since I've worked with C++, but I'm sure I'd pick it right back up. The hardest part is understanding all the components of the mod and how everything fits together.

The thing is, though, that I am hoping to wrap up my modding work soon. It's been a lot of fun, but I have other projects I'm anxious to get to, so I am thinking about my C2C work more in terms of tying up loose ends.
I can show you some stuff when I get back from my vacation here... so next week.
 
The thing is, though, that I am hoping to wrap up my modding work soon. It's been a lot of fun, but I have other projects I'm anxious to get to, so I am thinking about my C2C work more in terms of tying up loose ends.

Sorry to read this.

Your additions, stop XP users from using the latest versions of this mod. That may be due to memory overload or XML errors.

I thought you would at least try to solve any XML errors using the validator program before leaving..
 
Your additions, stop XP users from using the latest versions of this mod. That may be due to memory overload or XML errors.

Hmm, that's no good. I know I had some XML errors in the past but I thought they were fixed by now. If it's a memory overload, we've been operating at the upper limits of what C2C can handle for a long time. I'm not done modding yet, especially if I've introduced bugs that still need to be fixed.
 
If it is memory overload. Nothing you can do - XP users just have to buy a new system.

But checking XML errors, can help. XP is less forgiving than W10.
 
Top Bottom