limiting building to a civic?

davidlallen

Deity
Joined
Apr 28, 2008
Messages
4,743
Location
California
I want to add a new civic category for Dune Wars (background in this thread). One key point is that certain buildings should only buildable with this civic. I can see how to change the yields for buildings using civics, but not preventing the building from being built.

There may be some other mods which do this; but does anybody know the details of how it works? There is a python hook canBuild where I could check the civic. But, the problem is that this building ties to a victory condition. If I use canBuild, the AI will not *understand* that the civic prevents the building. The civic attributes that are set by XML could at least theoretically, be used by the AI to decide whether or not the civic is a good idea. Hiding the restriction in python means the AI cannot use this to decide.

Any suggestions on how to do this? I can also associate the building with a tech, if that helps any, but there is no way to restrict techs by civic either.
 
Even if you do this in the SDK (which you'll need to do to remove items from the queue if they switch out of the civic) and have XML attributes for buildings, the AI won't magically understand how to use it. You'll need to add something to the AI's programming so that it knows how to handle it. And it will need to be more than just modifying the city governor to know not to try to build the building without the civic. You'll have to modify the civic selection code to take the victory condition and building into account.

This might be a good reason to make the leap from Python into C++. :D C++ code looks a lot more complicated (and can be if you use the more advanced features), but the SDK itself relies on only the most basic aspects of C++. The biggest difficulty in working with the SDK is its complete and utter lack of documentation, but the same can be said of the Python code. :cry:
 
I guess this aspect of the civic goes further than I initially thought. I will rethink whether a civic is necessary or if there is some other way to do this. Maybe restricting techs has fewer side effects, and I can hide the buildings inside techs.

I have programmed professionally in C++, Tcl and other languages for > 20 years, so the language isn't the barrier. Spending a few hours looking through the python API and writing a new function is "fun". Spending a few days installing tools, getting the right DLL's from 2003, getting a huge makefile to work and then reverse engineering undocumented 500 line functions is ... not so much fun.
 
Yes, I know what you mean. I got into modding Civ4 because it was Python, and I love learning new languages. Honestly, setting up the SDK toolchain only took me a couple hours and that's because I didn't want to follow the directions exactly. Typical coder mentality. :mischief:

I ended up using VS 2008 (only because it's newer) with the same makefile. I have since rewritten the makefile to be cleaner, but it's essentially the same as the original.

The SDK code is . . . challenging. No documentation, rare comments (usually don't apply anymore or are cryptic), mismatched APIs and name usage, and an apparent hatred of local variables. Once you get past that, it's not too bad. However, most of my work as been in the hover text and CvCity area where it's a little less intense. :lol:

It sounds like you and I have a similar background. I've spent far more time in Java with only a few years of professional C++. Python brought back a lot of the true fun of programming since there is so little cruft-writing necessary. Rewriting the BUG Core was a great learning experience; Python has some great features when it comes to building application frameworks.
 
There is a python hook canBuild where I could check the civic. But, the problem is that this building ties to a victory condition. If I use canBuild, the AI will not *understand* that the civic prevents the building.

The problem is, that you fear, the AI will begin to build that building, and then switch the civic?
Or is the problem, that you could make a civ want to build that building, and it can't be build?

If the first: You can prevent changing civics throug can/cannotDoCivic. Just check, if this AI has one of these buildings, or is building one, and just don't allow them to change.

The SDK code is . . . challenging. No documentation, rare comments (usually don't apply anymore or are cryptic), mismatched APIs and name usage, and an apparent hatred of local variables. Once you get past that, it's not too bad. However, most of my work as been in the hover text and CvCity area where it's a little less intense. :lol:

Have you ever met a programmer, who likes to comment is code :D?


In my RL work, i've found some comments in my current code...comments in russian, so don't complain about comments :D.
 
You can prevent changing civics throug can/cannotDoCivic. Just check, if this AI has one of these buildings, or is building one, and just don't allow them to change.

That is a good tip. Maybe with canBuild/canDoCivic together, I can get the right behavior.

Have you ever met a programmer, who likes to comment is code?

This is turning philosophical and off-topic, but it is not that hard to keep code in 30-50 line functions with a one or two line header comment for each function. When I come back to my own code two years later, I can't remember any details, but I find it very easy to pick up from the header comments.
 
That is a good tip. Maybe with canBuild/canDoCivic together, I can get the right behavior.

I think you can make it so the AI can't violate the rule, but I don't know that it will make the AI act intelligently. If you create a new victory condition, you'll need to write code so the AI knows how to achieve it efficiently. Sure, they might stumble upon the victory by accidentally switching to the civic and building the right things, but they won't be able to decide to pursue that victory and do what's necessary to make it happen on purpose.

It is not that hard to keep code in 30-50 line functions with a one or two line header comment for each function.

Exactly. Short functions (10-20 lines) and good variable/function names can go a long way to shortening the amount of commenting needed. In my professional work, writing good comments is a part of the job. If you don't comment well enough for the code you write, you make everyone's job that much harder.

The problem with really long functions is that if there are comments they tend to be more like Cliff's Notes for the code. They don't tell you the intention or input/output requirements of the function or any gotchas with using it. Instead they summarize what the code does because it would take too long to read the code to figure it out. And that's if there are any comments. :rolleyes:

</preach>

To summarize, I'm not surprised that there are no comments--just frustrated.
 
I have this working, mostly using cannotConstruct. I would have preferred a way to have the building icon show up, greyed out, in the area where possible builds are displayed. For example, if you have iron working tech but no iron resource connected, a swordsman icon is there but greyed out. You can hover, and find out it requires iron ore. In what I have done, the buildings are simply missing from this area unless you have the proper civic value. This is workable, but less "obvious" to a player.

Using the undocumented victory condition of "N buildings built", with the assistance of Favorite Civics, I can see that the AI will pursue this victory condition. Maybe not on purpose, but I see autoplayed games being won with this. So I am happy with the result.
 
When you normally check if a building/unit can be constructed, there is a bVisible parameter that determines whether or not the icon is visible separate from being enabled. Is there a similar parameter (or any at all) to the cannotConstruct callback?
 
davidlallen said:
Using the undocumented victory condition of "N buildings built"

the_j said:
could you maybe say a bit more about this...?

In civ4projectinfos, the space ship items like thruster have a victorythresholds group:
Code:
<VictoryThresholds>
	<VictoryThreshold>
		<VictoryType>VICTORY_SPACE_RACE</VictoryType>
		<iThreshold>1</iThreshold>
	</VictoryThreshold>
</VictoryThresholds>

This says in order to get a space race victory, you need one of these items. In buildingclassinfos, there is the same victorythresholds group; you can see it in the schema. I tried it, and it turns out you can make a victory which requires N number of buildings. Actually, N of any of the buildings in the buildingclass.

There is a bug in the vanilla python when you try it, however. If you look at vanilla python/screens/cvvictoryscreen.py, and search for iBestBuildingTeam, you will see a loop which tests for this victory condition. However, when the text is displayed, there is an argument mismatch error:
Code:
screen.setTableText(szTable, 3, iRow, unicode(activePlayer.getTeam().getBuildingClassCount(i)), "", WidgetTypes.WIDGET_GENERAL, -1, -1, CvUtil.FONT_LEFT_JUSTIFY)

In vanilla, the unicode() call is missing, and the game throws an alert when an integer is passed instead of a string. Fix this by adding the unicode() call, and you can use this victory condition.

I am pretty sure I am the only one who has ever used this, although it could be that others have independently discovered the vanilla bug. The actual condition for Dune Wars is that 7 Reservoirs of Liet must be built to win a terraforming victory.
 
When you normally check if a building/unit can be constructed, there is a bVisible parameter that determines whether or not the icon is visible separate from being enabled. Is there a similar parameter (or any at all) to the cannotConstruct callback?

Interesting question, it would be great if there is a way to do this. Sadly no; cannotConstruct just returns a boolean. If it could return either false, or a *string* giving the reason, that would be great.
 
When you normally check if a building/unit can be constructed, there is a bVisible parameter that determines whether or not the icon is visible separate from being enabled. Is there a similar parameter (or any at all) to the cannotConstruct callback?

You get the bVisible variable, but i guess, that it's a read only function.


Interesting, thanks :goodjob:.

I have an FfH2 Altar of the Luonotar victory, and i'm doing that through python atm, but i'll change that.
I guess, if it's in the schema, the AI will maybe know, how to use it. And i'll get around modifying the screen (okay, only the small thing you've mentioned).
 
You get the bVisible variable, but i guess, that it's a read only function.

Excellent. You don't want to modify bVisible. Instead, check its value. If it's True, return False (don't block construction) if you want the button to be visible. If it's False, check if they are in the correct civic and return True if not as you are currently doing.

I assume that this function is called only once the building is determined to be constructable by the normal means (e.g. enabling tech), so you shouldn't have to check that stuff--just what's specific to your mod (the civic).
 
Excellent. You don't want to modify bVisible. Instead, check its value. If it's True, return False (don't block construction) if you want the button to be visible. If it's False, check if they are in the correct civic and return True if not as you are currently doing.

:confused: sorry, i don't understand you.
The only return value is the value for the construct callback, nothing else.
 
You do get the bTestVisible value in the function. Here's how it should look for you:

Code:
def cannotConstruct(self,argsList):
	pCity = argsList[0]
	eBuilding = argsList[1]
	bContinue = argsList[2]
	bTestVisible = argsList[3]
	bIgnoreCost = argsList[4]
	
	[B]if not bTestVisible:[/B]
		... your current civic-checking code ...
	
	return False

You still return only True or False, but you only do the blockage when the button is being tested for enabling. You skip your test when the button is being checked for visibility.

The function is called twice for each button: once to see if it should be visible on-screen (bTestVisible is True) and again to test if it should be enabled.
 
You still return only True or False, but you only do the blockage when the button is being tested for enabling. You skip your test when the button is being checked for visibility.

That is kind of interesting, I guess. Is there any way to supply a help string saying why it is disabled, like swordsman when you have no iron connected?
 
Is there any way to supply a help string saying why it is disabled, like swordsman when you have no iron connected?

Yes, using CvGameUtils.getWidgetHelp() you can append text to the hover help text. You can do whatever you want to it using the SDK, but if you want Python this is what you do:

Code:
[B]localText = CyTranslator()[/B]

...

def __init__(self):
	[B]self.BUILDING_ZOO = gc.getInfoTypeForString("BUILDING_ZOO")[/B]
	[s]pass[/s]

...

def getWidgetHelp(self, argsList):
	eWidgetType, iData1, iData2, bOption = argsList
	
	[B]if eWidgetType = WidgetTypes.WIDGET_CONSTRUCT and iData1 = self.BUILDING_ZOO:
		if <not-running-civic>:
			return localText.getText("TXT_KEY_WRONG_CIVIC", ())[/B]
	
	return u""

I haven't tried this myself, but I'm wondering if bOption might be False of the button is disabled. That would be very handy as you wouldn't need to check for the civic. When you create a widget, you only specify its type and two data values--no boolean option.
 
Tested it all, it fits :).

For displaying the civic, i just use the help text for the units/buildings, which i restrict.
They'll still appear, when they can be build, but that shouldn't matter.



The victory:
I've added it, also the correction for the screen.
But only with this, the victory doesn't work. I've built the building, and nothing happend.
I wonder, if this is related to the problem, that the number is not correctly displayed in the victory screen (like for the spaceship parts).
 
The victory:
I've added it, also the correction for the screen. But only with this, the victory doesn't work. I've built the building, and nothing happend.

I can tell you in Dune Wars, both the human player and the AI can win using the "7 buildings" method. I am not sure what may be different in your setup.
 
Back
Top Bottom