Quick Modding Questions Thread

Figuring out exactly the RNG call that corresponds to a particular leader should be possible, but sounds like a lot of trial and error. Probably easier and cleaner to use CvInitCore::setLeader to overwrite the assignments made by the EXE. That'll involve reimplementing the logic for enumerating the valid leaders for a given civ (really just need to go through all leaders and check CvCivilizationInfo::isLeaders for each, I think), and the timing is a bit tricky: The EXE needs to be finished with its civ and leader randomization, but the trait effects shouldn't already have been cached (CvPlayer::init). In my mod, I do it immediately when the final setLeader call from the EXE happens: CvInitCore::setLeaderExternal
Edit: Re-reading my code, I think it's really crucial to do the re-assignment immediately because, otherwise, the EXE won't sychronize the assignments in network games.

The re-routing of external calls (coming from the EXE) to setLeaderExternal (instead of setLeader) happens through the mod's def file passed to the linker through the /def option here. I don't think you'd need to worry about my Civ/LeaderRandomlyChosen functions (and synchronizing that info in network games); I only keep track of that – iirc – because I want my Settings screen to show which leaders were chosen manually and which ones randomly. Here is where I check CvCivilizationInfo::isLeaders. I've changed the algorithm so that leaders are chosen directly - rather than first civs and then leaders -, so most of the surrounding code is probably not useful or necessary for what you're attempting. Edit: This little trick for setting up the synchronized RNG could be handy.
The depth of your knowledge about this game is nothing short of amazing. I think I have to learn a lot more about C++ before attempting this, for example the def file just leaves me entirely dumbfounded. Thanks for the reply! I'm bookmarking this for some later time where I can hopefully use it better.
 
I haven't checked this myself, but "pTrade.ItemType" should be an enum type, which is essentially a wrapper of integer values. TRADE_TECHNOLOGIES equals 10, but when you cast it to a string you will get "10" instead of the expected "TRADE_TECHNOLOGIES". The better way of doing this would be to check for equality with the enum value directly:
Python:
if pTrade.ItemType == TradeableItems.TRADE_TECHNOLOGIES:
    ...
The TradeableItems enum should be available in Python - if it isn't, your best bet is to go via integers:
Python:
TRADEABLE_ITEM_TRADE_TECHNOLOGIES = 10

if int(pTrade.ItemType) == TRADEABLE_ITEM_TRADE_TECHNOLOGIES:
    ...
The proper condition would be pTrade.ItemType == TradeableItems.TRADE_TECHNOLOGIES although this is comparing a TradeableItems object TRADE_TECHNOLOGIES instead of an integer, so my ItemType checks should work if there was anything to find, e.g. I can return "TRADE_PEACE_TREATY" from str(pTrade.ItemType). I haven't even considered how to get the integer tbh.

Also, it looks like only active deals are kept on the record, so even peace treaties and resource trades get cleared once ended, and tech deals are never recorded (not even in the same turn).

One alternative I've come up with so far is to update a persistent variable in the trade screen, but I think this will only work for trades involving humans as I doubt the ai would ever use a screen.

Any other ideas on how to determine if a tech has been traded?
 
Any other ideas on how to determine if a tech has been traded?
CvTeam::isNoTradeTech? That flag seems to get set when a tech is traded, even if the No Tech Brokering option isn't enabled: CvDeal::startTrade.
Edit: Ah, even without that option, brokering is disallowed for one turn. NoTradeTech gets cleared at the end of a team turn in CvTeam::doTurn.

Memory counts (for "fear getting too advanced") are also increased, but I don't think Python can access those.
The depth of your knowledge about this game is nothing short of amazing. I think I have to learn a lot more about C++ before attempting this, for example the def file just leaves me entirely dumbfounded. Thanks for the reply! I'm bookmarking this for some later time where I can hopefully use it better.
The def file is a Microsoft Visual C++ thing specifically. I haven't learned much about MSVC at the source, mostly just picked things up from other forum users. ( I think C2C was the first mod to make use of a def file.) Overwriting the leaders probably doesn't require a def file either. One could probably just do the have-all-leaders-been-assigned check in the setLeader function that gets called by both the EXE and the DLL; just shouldn't call that same function when overwriting the leaders, to avoid getting into an infinite loop. But I agree that it's still not exactly an easy change to make, especially if it turns out that things just don't work out as expected.
 
  • Like
Reactions: CSD
Thanks f1rpo, isNoTradeTech is exactly what I was hoping for! :D

Due to the minor nuisance of onTechAcquired being called before isNoTradeTech gets set, I moved the relevant code to a new function that gets called from onGameUpdate using a persistent variable:

Python:
    def handleFreeTechsAllowed(self, argsList):
        #...
        if (team.isNoTradeTech(iTechType)):
            pass
        elif (csd.get(player, "freeTechsAllowed") > 0):
            csd.add(player, "freeTechsAllowed", -1)
        else:
            team.setHasTech(iTechType,False,iPlayer,False,False)
            #...

    def onTechAcquired(self, argsList):
        #...
        csd.set(game, "runOnGameUpdate", ("handleFreeTechsAllowed", argsList))
        #...
        
    def onGameUpdate(self, argsList):
        #...
        if (csd.get(game, "runOnGameUpdate", "")):
            functionName, functionArgsList = csd.get(game, "runOnGameUpdate", "")
            csd.set(game, "runOnGameUpdate", "")
            if (functionName == "handleFreeTechsAllowed"):
                self.handleFreeTechsAllowed(functionArgsList)

Since onGameUpdate gets called about twice a second or so, and only checks one if statement usually, this refactoring should have a neglible performance impact.

For anyone who wants to record the traded techs, it would also be easy to do that with another persistent variable.

Here's to hoping this is the last infinite free tech bug change I'll have to make :rolleyes:
 
Due to the minor nuisance of onTechAcquired being called before isNoTradeTech gets set,
Oh ...
I moved the relevant code to a new function that gets called from onGameUpdate using a persistent variable
A-ha, great! :)
A caveat: Goody techs, stolen techs and techs that colonial vassals start with also receive the no-brokering flag. I guess that may actually work to your advantage; not sure.
Since onGameUpdate gets called about twice a second or so, and only checks one if statement usually, this refactoring should have a neglible performance impact.
4 times according to a comment in CvMainInterface.updateScreen. Anyway, yes, should be fine to run even rather slow code once per update.
 
Hi, how do I write a python code that will allow mounted units created in a city to receive defensive bonuses after building a wonder?
 
Hi, how do I write a python code that will allow mounted units created in a city to receive defensive bonuses after building a wonder?
From what it looks like, this is directly handled by the DLL with no way of Python to change it. The ability to receive defensive bonuses is directly tied to the unit type.

Best solution I could think of that does not involve changing the DLL:
  1. Create copies of all mounted unit types you want to be affected by this
  2. Grant those new unit types the ability to receive defensive bonuses
  3. Add a dummy technology that cannot be researched
  4. Make the new unit types require the dummy technology in addition to their normal requirements
  5. Make building the wonder grant the dummy technology that unlocks the variety units
I think you can achieve (5) using the XML event system, alternatively it's also possible in Python. It is probably a good idea to make the old unit types upgrade to their copied counterparts, so it's not possible to build both at the same time. Also, you would have to ensure that the dummy tech is removed and assigned to a new owner when the wonder changes hands.
 
Also, you would have to ensure that the dummy tech is removed and assigned to a new owner when the wonder changes hands.
I read once that it is not possible to un-learn a tech. I don't know though.
 
From what it looks like, this is directly handled by the DLL with no way of Python to change it. The ability to receive defensive bonuses is directly tied to the unit type.

Best solution I could think of that does not involve changing the DLL:
  1. Create copies of all mounted unit types you want to be affected by this
  2. Grant those new unit types the ability to receive defensive bonuses
  3. Add a dummy technology that cannot be researched
  4. Make the new unit types require the dummy technology in addition to their normal requirements
  5. Make building the wonder grant the dummy technology that unlocks the variety units
I think you can achieve (5) using the XML event system, alternatively it's also possible in Python. It is probably a good idea to make the old unit types upgrade to their copied counterparts, so it's not possible to build both at the same time. Also, you would have to ensure that the dummy tech is removed and assigned to a new owner when the wonder changes hands.
Is it possible to create a new promotion that gives 15% combat strength when defending then? And if so what would the code look like?
 
iCityDefense in Civ4PromotionInfos.xml should work even for units with bNoDefensiveBonus; I've run a quick test. Maybe that's close enough? There's no generic defensive modifier for promotions, and I don't think Python can make a difference there. Combat modifiers generally seem to be far removed from any Python callbacks.
 
Yeah, a duplicate of the existing city defender promotion that is accessible to mounted units should work. However, associating the promotion with a wonder would still require a workaround like the above - there is no Python callback for that either.
 
Why not just create a promotion that duplicates city defender and is not available to any unit type and than use a script hooked to unit creation that automatically gives all mounted units that promotion if the player has the specific wonder?

Or if you want to do it XML only take the code for any of the wonders that give you free buildings like Stonehenge. Copy that code to your custom wonder but have it add a custom building without a pedia entry. And than have that custom building use the code from the Red Cross to give your units that specific promotion. I think that should work.
 
Why not just create a promotion that duplicates city defender and is not available to any unit type and than use a script hooked to unit creation that automatically gives all mounted units that promotion if the player has the specific wonder?

Or if you want to do it XML only take the code for any of the wonders that give you free buildings like Stonehenge. Copy that code to your custom wonder but have it add a custom building without a pedia entry. And than have that custom building use the code from the Red Cross to give your units that specific promotion. I think that should work.
+1
And you can make the free dummy building invisible in the pedia by using a tag (I don't recall its name).

And if you want all your existing units to receive that promotion, that's also possible via an event.

This seems to be the best solution since the ai is not aware of Python bonuses but it is of XML ones. I'm not sure about event given bonuses though...
 
Why not just create a promotion that duplicates city defender and is not available to any unit type and than use a script hooked to unit creation that automatically gives all mounted units that promotion if the player has the specific wonder?

Or if you want to do it XML only take the code for any of the wonders that give you free buildings like Stonehenge. Copy that code to your custom wonder but have it add a custom building without a pedia entry. And than have that custom building use the code from the Red Cross to give your units that specific promotion. I think that should work.
There is an XML-only way to restrict a promotion based on unit combat type (e.g. mounted) via the UnitCombat tag:

XML:
<PromotionInfo>
    <Type>PROMOTION_MOUNTED_DEFENSE</Type>
    <iCityDefense>25</iCityDefense>
    <iHillsDefense>25</iHillsDefense>
    <FeatureDefenses>
        <FeatureDefense>
            <FeatureType>FEATURE_JUNGLE</FeatureType>
            <iFeatureDefense>50</iFeatureDefense>
        </FeatureDefense>
        <FeatureDefense>
            <FeatureType>FEATURE_FOREST</FeatureType>
            <iFeatureDefense>50</iFeatureDefense>
        </FeatureDefense>
    </FeatureDefenses>
    <UnitCombats>
        <UnitCombat>
            <UnitCombatType>UNITCOMBAT_MOUNTED</UnitCombatType>
            <bUnitCombat>1</bUnitCombat>
        </UnitCombat>
    </UnitCombats>
</PromotionInfo>


And this is all that's needed for the building/wonder:

XML:
<BuildingInfo>
    <FreePromotion>PROMOTION_MOUNTED_DEFENSE</FreePromotion>
</BuildingInfo>


Should work just like the Celtic Dun which only grants the Guerilla I promotion to units that could otherwise promote from experience points.

Also the defense values listed in the promotion can emulate defensive bonuses well, except for city, which probably ought to be relatively low for mounted units anyhow.
 
I read once that it is not possible to un-learn a tech. I don't know though.
You can use setHasTech(eIndex, False, ePlayer, bFirst, bAnnounce) to make a team unlearn a tech.
 
There is an XML-only way to restrict a promotion based on unit combat type (e.g. mounted) via the UnitCombat tag:

XML:
<PromotionInfo>
    <Type>PROMOTION_MOUNTED_DEFENSE</Type>
    <iCityDefense>25</iCityDefense>
    <iHillsDefense>25</iHillsDefense>
    <FeatureDefenses>
        <FeatureDefense>
            <FeatureType>FEATURE_JUNGLE</FeatureType>
            <iFeatureDefense>50</iFeatureDefense>
        </FeatureDefense>
        <FeatureDefense>
            <FeatureType>FEATURE_FOREST</FeatureType>
            <iFeatureDefense>50</iFeatureDefense>
        </FeatureDefense>
    </FeatureDefenses>
    <UnitCombats>
        <UnitCombat>
            <UnitCombatType>UNITCOMBAT_MOUNTED</UnitCombatType>
            <bUnitCombat>1</bUnitCombat>
        </UnitCombat>
    </UnitCombats>
</PromotionInfo>


And this is all that's needed for the building/wonder:

XML:
<BuildingInfo>
    <FreePromotion>PROMOTION_MOUNTED_DEFENSE</FreePromotion>
</BuildingInfo>


Should work just like the Celtic Dun which only grants the Guerilla I promotion to units that could otherwise promote from experience points.

Also the defense values listed in the promotion can emulate defensive bonuses well, except for city, which probably ought to be relatively low for mounted units anyhow.

In addition, this way also fails to emulate a defensive bonus for unit fortification.

It's also worth mentioning that this would approximately double the defensive modifier on mounted units that already do receive defensive bonuses (e.g. Conquistador).
 
And this is all that's needed for the building/wonder:

XML:
<BuildingInfo>
    <FreePromotion>PROMOTION_MOUNTED_DEFENSE</FreePromotion>
</BuildingInfo>


Should work just like the Celtic Dun which only grants the Guerilla I promotion to units that could otherwise promote from experience points.
I interpreted the original question as the wonder granting access to a promotion, not a free promotion for all units. This solution also only grants the promotions to units built in the wonder's city, not any applicable unit built.
 
I interpreted the original question as the wonder granting access to a promotion, not a free promotion for all units. This solution also only grants the promotions to units built in the wonder's city, not any applicable unit built.
Combine what he said and what I said though and you basically have a free promotion for the unit through the whole empire.

If he wants just access to the promotion instead of actually granting it that can be done easily enough as well.
  1. Create the custom promotion you want.
  2. Now create a second custom promotion that has no effects and is a required prerequisite to the special promotion.
  3. Use the code we described to add that second promotion to all relevant units.
This will now allow all relevant units to get that special promotion he originally wanted but only as a regular purchase.
 
Is there a way to play mp3 track under some specific conditions? I mean playing an mp3 in a way that it stops/mutes the current era music and when finished the era music resumes.
I mean other than construction of a building or researching a tech.
 
There is CyInterface.DoSoundtrack which I learned about from Rhye (and have no idea how he learned about it because afaict the base game does not use it).

See here for a usage in my mod: it expects the name of the 2D sound XML entry to play. The way it plays depends on the XML definition: if set to loop it will play until something else causes another song to start. Otherwise it will play once and the soundtrack returns to the era playlist afterwards.
 
Top Bottom