An Idiots Guide to Editing the DLL

Where in the SDK would I go if I wanted to change a python callback on the SDK side of things?

For instance, if I wanted to control whether I could or could not build an improvement on a plot (canBuild/cannotBuild in CvGameUtils.py), there's a canBuild in both CvPlayer.cpp and CvPlot.cpp, one seems to call the other. Which one would I edit? (I want to check if other units are building the same improvement, and if they are, stop the improvement- which I created a boolean tag for- from being built). It seems to work the same way for things like canConstruct, or canFound (except here there's one in CvUnit.cpp and CvPlayer.cpp).
 
It really depends on what information you need for your prereq condition. Typically the information available to you in each of the function calls is rather different (the canConstruct example would be that in CvUnit you know if the unit is a certain level or unit type, and in CvPlayer you only know what technologies the player has so far). Usually one of the two functions is fairly simple and is called at the start of the other function to make things faster by looking for obvious problems (like not having the right technology for the action).


In your specific case, while checking the Plot it is just seeing if the terrain and features and elevation allow the action, and while checking the player it is seeing if the technologies are known. So whichever one has the info you need (you said based on improvements which are already there? So that would be in CvPlot) is the one you write for.
 
Do you know if it's "easy" to clone the

Code:
<ObsoleteTech>NONE</ObsoleteTech>

into Civ4UnitsInfos.xml ?
 
That should be relatively easy. It's a string value, which isn't much differnent then a boolean or integer, pretty much the same thing really, just need to take that into account when defining the stuff in CvInfos, but pretty much you can just follow the cloning a boolean tag example. The hard part would be getting the functionality to work, but that shouldn't be too hard either. I was able to clone the MaxEraStart string from BuildingInfos to UnitInfos pretty easily, and the only problem I experienced that I needed help with was getting the information to display properly in the civilopedia, and those CvGameTextMgr issues weren't too difficult, but I did need to ask a couple questions on it in the Python/SDK forums, got the functionality to work fine on my own though. Overall the ObsoleteTech function should be pretty similar and it's pretty much a straight cloning of the code, just need to take account you're dealing with pUnit instead of pBuilding and such.
 
Can you explain how you have added the information to the civilopedia?
 
One thing you will have to decide is precisely what it means for a unit to be obsolete though. For buildings, they stop working completely, and cannot be built. If my cities are all defended by nothing but warriors and suddenly they go obsolete and disappear off the map, I may be a tad upset. So cities cannot build the unit anymore is an easy thing to decide, but all the unit already existing, do they get upgraded for free? Which of the multiple possible upgrades do they become for units who can go down 2 paths? Does the upgrade still cost gold, but you don't get a choice about it? If so, how do you handle setting the player to a negative gold value in the treasury? Or do the units simply exist on the map with the knowledge that replacements cannot be trained (and that they probably suck and need updated manually)?


But yes, it will be a "simple" import for you. Just that unlike most imports, none of the functional pieces will be "prebuilt" for you, you simply have a nice example to reference at almost every step along the way.
 
O_o good point. The units shall still stay in the game but they should not be buildable anymore. Maybe I really should use maxerastart instead?
 
O_o good point. The units shall still stay in the game but they should not be buildable anymore. Maybe I really should use maxerastart instead?

MaxStartEra and ObsoleteTech are two different concepts. MaxStartEra means that the unit can't be built on the defined era and later starts, ObsoleteTech should mean (based on how it works for buildings) that the unit can't be trained if you have that tech.

You can use the MaxStartEra example as a guide. But you'll need to use common sense to account for the differences. such as all the NO_ERA references need to be NO_TECH, and you'd need to rename MaxStartEra to ObsoleteTech etc. Like I said the biggest issue is the actual functionality but even that isn't too different. Instead of this code for MaxStartEra:

Code:
bool CvPlayer::canTrain(UnitTypes eUnit, bool bContinue, bool bTestVisible, bool bIgnoreCost) const
{
	PROFILE_FUNC();
...
	if (GC.getUnitInfo(eUnit).getMaxStartEra() != NO_ERA)
	{
		if (GC.getGameINLINE().getStartEra() > GC.getUnitInfo(eUnit).getMaxStartEra())
		{
			return false;
		}
	}

You'd use this:

Code:
bool CvPlayer::canTrain(UnitTypes eUnit, bool bContinue, bool bTestVisible, bool bIgnoreCost) const
{
	PROFILE_FUNC();
...
	if (GC.getUnitInfo(eUnit).getObsoleteTech() != NO_TECH)
	{
		if ((GET_TEAM(getTeam()).isHasTech((TechTypes)(GC.getUnitInfo(eUnit).getObsoleteTech()))))
		{
			return false;
		}
	}

You would need to do similar for CvGameTextManager as well.
 
with your suggestion - existing - obsolete units won't disappear off the map?
 
bool CvPlayer::canTrain(UnitTypes eUnit, bool bContinue, bool bTestVisible, bool bIgnoreCost) const

This is just a check in CvPlayer to see if the unit is trainable. The code I show two posts above checks the players techs and makes the unit unable to be trained if the player has the tech, nothing more. I don't even know how to make a unit disapear off the map even if I wanted too.

When in doubt read the code and see if you can figure out what it means. Most of the example code from MaxStartEra and the suggested code above should make sense intuitively, so you should be able to understand it for the most part.
 
To remove units mid-game by force you would have to ask CvPlayer for a list of all units it owns, then loop over that list and run a kill command on each one you deem worthy of removal.

Cybah, remember that even though the names mean something to US and we can see a connection between this function and the other one and ask these kinds of questions, to the computer, the names are meaningless garbage, and it doesn't know that anything else exists, let alone that this is similar to it. So the computer will only ever do what you tell it to do. The way you run into issues with the computer is when you find out that you told it to do something at the wrong time (ie - it asked you a question you weren't expecting, so you had the wrong answer prepared).
 
damn... one thing just came in my mind:

for the maceman/grenadier/rifleman problem we would need TWO obselete techs: military_science and rifling.
 
Xienwolf in the canTrain function:
bool CvPlayer::canTrain(UnitTypes eUnit, bool bContinue, bool bTestVisible, bool bIgnoreCost) const
What is the point of these identifiers?:
Code:
bool bContinue, bool bTestVisible, bool bIgnoreCost
Why can't we just say:
Code:
bool CvPlayer::canTrain(UnitTypes eUnit) const
?
 
What is the point of PROFILE_FUNC()?

That allows the Profiler to track the time spent in this function and the number of calls to it when you have the profiler enabled. VERY useful functions which were used to decide what to cache to make CAR Mod.

damn... one thing just came in my mind:

for the maceman/grenadier/rifleman problem we would need TWO obselete techs: military_science and rifling.

You have two choices: Create an array field, like <TechTypes>, where having any listed tech obsoletes you, or choose just one of the two techs to obsolete with (Or a tech which requires both of them, and just accept a brief additional period of relevance)

Xienwolf in the canTrain function:
bool CvPlayer::canTrain(UnitTypes eUnit, bool bContinue, bool bTestVisible, bool bIgnoreCost) const
What is the point of these identifiers?:
Code:
bool bContinue, bool bTestVisible, bool bIgnoreCost
Why can't we just say:
Code:
bool CvPlayer::canTrain(UnitTypes eUnit) const
?

bContinue will ignore the number you are ALREADY making, but not yet done with. Thus if only 4 of the unit can ever exist and you own 3, you can only start building one more. Each turn the city checks if you are allowed to keep working on whatever the city is making (ie - someone else finished a wonder before you). So even though your unit count is 3 alive + 1 in process, making all OTHER cities unable to train this unit, in the city making him we do a bContinue check so that we are still allowed to make him and thus finish.

bTestVisible is used for "greyed out" units in the build order. Like when you have the tech, but not a required building. a mouseover tells you WHY you cannot build it yet (If the modder responsible for the limiting field updated CvGameTextMgr properly)

bIgnoreCost is there because we also use ::canTrain to find out if we can upgrade existing units. a unit with <iCost>-1</iCost> cannot be built in a city. But by ignoring the cost check, other units can upgrade to the unit.
 
I know that you have effectively stated that vectors should be avoided, but just out of curiousity-- How hard would it be it clone the <Invisible> vector from UnitInfos to PromotionInfos?
 
SeeInvisible is the only vector in BtS code that I am aware of. Invisible is left as just a single type. Cloning SeeInvisible to Promotions wasn't too difficult, but to make Invisible into a vector and add it to PromotionInfos takes a bit more work since you have to reconfigure things to allow a unit to have more than 1 type of invisibility at a time, and decide what that means for finding the unit (my approach had been that if you have 2 types of invis, the opponent must see through both types simultaneously. But you may decide to create a "ranking" for Invis types where only the highest ranking invis matters, and all SeeInvis types above that rank can see you)
 
So long as it could be cloned, I doubt, I'd need the invis to be a vector, I'm really just tossing around ideas that I have in my head, another one, I think I can hijack ::resolveCombat with a pair of bools bVictor and bDefeated, if I can get that done, how hard do you think it would be to add a Commerce Yield like is done with Python in Industry Espionage, Marauder and Sneak? (just in case you aren't familiar they each award the winner of combat with percentage of the losers owners store of :science:, :commerce: and :espionage: respectively)
 
Back
Top Bottom