Softcoding Inquisition Civics

Wait... Should iInquisitor = eUnit, or something like that? Otherwise, you just assigned iInquisitor a true value.
 
Good catch, yeah that would definatly be wrong. How about this:

Code:
			for j in range(gc.getNumUnitInfos()):
				eUnit = gc.getUnitType(j)
				if gc.getUnitInfo(eUnit).isInquisitor():
					iInquisitor = gc.getUnitInfo(eUnit)

For refrence original code is this:

Code:
			iInquisitor = CvUtil.findInfoTypeNum( gc.getUnitInfo, gc.getNumUnitInfos(), "UNIT_INQUISITOR" )
 
I want to say that gc.getUnitType(j) returns the same value as gc.getInfoTypeForString("UNIT_INQUISITOR")... but I'm not 100% sure. If I am correct, then you should be assigning iInquisitor to eUnit, not gc.getUnitInfo(eUnit). Hopefully EF will know.
 
OK, so I copied the function over to CvMainInterface and put it somewhere where I was sure it would run, then I copied the function that referenced the variable iInquisitor and did the same thing. Found out it wants an integer instead of a unit type. I'm fairly certain I need the UnitID, which is called by getID(). The problem is I do not know how to get to CyUnit. gc.getUnitInfo() gets you to the CyInfoBase stuff, namely CvUnitInfo, but getID() does not exist there, it is in CyUnit. And I don't know how to convert to CyUnit.

The following code doesn't throw an exception, but I do not think it works:

Code:
for j in range(gc.getNumUnitInfos()):
	eUnit = gc.getUnitInfo(j)
	if eUnit.isInquisitor():
		iInquisitor = j

This is close to what I want (I believe) but it throws an exception, for the reason stated above:
Code:
for j in range(gc.getNumUnitInfos()):
	eUnit = gc.getUnitInfo(j)
	if eUnit.isInquisitor():
		iInquisitor = eUnit.getID()

For reference this is what I'm replacing:
Code:
iInquisitor = CvUtil.findInfoTypeNum( gc.getUnitInfo, gc.getNumUnitInfos(), "UNIT_INQUISITOR" )

Anyone more familiure with python have any ideas on how to properly type cast things so I can get this to work? I have been looking through the API but have been unsuccessful.
 
I realized i could just use the original function:

Code:
for j in range(gc.getNumUnitInfos()):
	eUnit = gc.getUnitInfo(j)
	if eUnit.isInquisitor():
		iInquisitor = CvUtil.findInfoTypeNum( gc.getUnitInfo, gc.getNumUnitInfos(), eUnit.getType() )
Seems a little obtuse, so if anyone knows a better way let me know. Otherwise this should work.
 
edit: also add a break

Why do I add a break? Wouldn't that make it so that the function would only return the first unit that had the bInquisitor tag set for the unit? How would the code deal with multiple inquisitor units if a modmaker uses the new tag in UnitInfos?
 
Firaxis used them in 3.19, so if you can find the 3.17 source, compare it with 3.19 to see how they pulled it off.

The specific bits of code would be:

Code:
	uint uiFlag=0;
	pStream->Read(&uiFlag);	// flags for expansion

and

Code:
	uint uiFlag=2;
	pStream->Write(uiFlag);		// flag for expansion

In the initial release, all fllags were 0, this example is from my CvUnit, but I think from a code set where I haven't modified it. Anyway, one place it was used is:

Code:
	if (uiFlag > 0)
	{
		pStream->Read(&m_bAirCombat);
	}

Which states that in initial release, there was no m_bAirCombat variable in CvUnit, so only people who have a save from after the first patch will try to load that boolean.

So to clarify, all I do is where the new long term variable I have added is read (pStream->Read), I add the uint uiFlag=x; (x being 1 number higher then whatever is the largest thing jdog/EF have set for BBAI/BULL), and then add an if clause to the write stream?

Edit:

No I've looked over the code and can't get it. Can you show an example of clearing an added tag so that you know it wol't break save game compatibility or cause other problems? I need to see this done first, as there are no similar examples to what you have in the RevDCM SDK.
 
Since your loop assigns a new value to the same variable, only the last unit found will be returned anyway. Using "break" will have it return the first unit found. Either way, only one unit is returned. If you want to return all matching units you need

Code:
lUnits = []
for ...
    if ...
        lUnits.append(...)
 
:hmm:

The problem is that gives me a list, yet it's used as a variable that stores an integer later on in the code. This code is part of the AI behavior code for revolutions. Specifically the Variable iInquisitor is referenced like so:

Code:
if pCity.canTrain(iInquisitor, 0, 0):
...
if pPlayer.getUnit( lUnits[ iUnit ].getID( ) ).getUnitType( ) == iInquisitor:
...
if not pPlayer.isUnitClassMaxedOut(iInquisitorClass, -1):
	if utils.getRandomNumber(100/buildOdds) == 0:
		gc.getMap( ).plot( pCity.getX( ), pCity.getY( ) ).getPlotCity( ).pushOrder( OrderTypes.ORDER_TRAIN, iInquisitor, -1, False, False, False, True )
									return True

Obvisously if I turn iInquisitor into a list, it'll break the rest of the code. I suppose what I need to do now is go on google and look at ways to pull values from lists again (I assume this will require more loops) to get this to work. Or possibly re-write the code if I see a simpler way to do things.
 
Actually, does this look right:

Code:
iInquisitorUnits = []
for j in range(gc.getNumUnitInfos()):
	eUnit = gc.getUnitInfo(j)
	if eUnit.isInquisitor():
		iInquisitorUnits.append(j)
					
for k in range(iInquisitorUnits):
	if pCity.canTrain(k, 0, 0):
		iInquisitor = k
 
OK, so I've gone through some Python documentation, and I can't figure out how to do this. My goal is to build a list of units trainable in the city, then return the UnitID of the unit that costs the least. Here's where I'm stuck:

Code:
iInquisitorUnits = []
for j in range(gc.getNumUnitInfos()):
	eUnit = gc.getUnitInfo(j)
	if eUnit.isInquisitor():
		iInquisitorUnits.append(j)
			
iInquisitorCityTrainableUnits = []
for k in range(iInquisitorUnits):
	if pCity.canTrain(k, 0, 0):
		iInquisitorCityTrainableUnits.append(k)
if (len(iInquisitorCityTrainableUnits) == 1):
	iInquisitor = max(iInquisitorCityTrainableUnits)
elif (len(iInquisitorCityTrainableUnits) > 1):
	for l in range(iInquisitorCityTrainableUnits):
		eUnit = gc.getUnitInfo(l)
		iCost = eUnit.getProductionCost()

How do I do this? I know how to get a list of trainable units (at least I think that's what the above does). But how can I take this information and return the lowest cost unit, with the overall goal of giving me the UnitID of the lowest cost unit?
 
Code:
iInquisitorUnits = []
for eUnit in range(gc.getNumUnitInfos()):
	info = gc.getUnitInfo(eUnit)
	if info.isInquisitor() and pCity.canTrain(eUnit, False, False):
		iInquisitorUnits.append((info.getProductionCost(), j)
if len(iInquisitorUnits) > 0:
	sort(iInquisitorUnits)
	eBestInquisitor = iInquisitorUnits[0][1]
else:
	eBestInquisitor = None
 
Thanks EF. I need to figure out what's going on with the code here as well.

So we have:
Code:
iInquisitorUnits = []
#Running the loop for the unitInfo number in the range of all units in UnitInfos
for eUnit in range(gc.getNumUnitInfos()):
	info = gc.getUnitInfo(eUnit)

	#Checking if the unit is an inquisitor and if the city can train it (what are the false arguments BTW?)
	if info.isInquisitor() and pCity.canTrain(eUnit, False, False):

	#It looks like you're making a list that pairs values (like a coordinate system), but how is this working, and where did j come from (if j is supposed to be eUnit it would make alot more sense to me --but I can't assume that because I'm not sure about the code here)?
		iInquisitorUnits.append((info.getProductionCost(), j)

#checking the length of the list and if over 1 unit do stuff
if len(iInquisitorUnits) > 0:



	#what does the command sort do?
	sort(iInquisitorUnits)

#I have no idea what iInquisitorUnits[0][1] is doing...
	eBestInquisitor = iInquisitorUnits[0][1]
else:
	eBestInquisitor = None

Edit:
Actually assuming j is supposed to be eUnit, then sort is putting the pairs in order? And the [0][1] is returning the smallest items that the list sorted. But then how is it returning a unitID, when the call is refering to both the unitID and the production cost?
 
You guys already probably know this, but the WoC has/had an inquisition unit already coded in the SDK with AI. It might help to see some things from it. I place it in the RevDCM Rapture test merge I did. Mr Genie did the code. If you want to look at it. It is commented with "INQUISITION"

http://forums.civfanatics.com/downloads.php?do=file&id=14028

These are the tags that were added, and you have to add the new mission to the base xml of course. It will allow multiple inquisitor units.
Code:
<!-- Unit Schema -->

<!-- INQUISITION			10/12/2007			MRGENIE		-->
	<ElementType name="PrereqCivic" content="textOnly"/>
<!-- INQUISITION			END					MRGENIE		-->
<!-- INQUISITION			10/12/2007			MRGENIE		-->
	<ElementType name="bRemoveNonStateReligion" content="textOnly" dt:type="boolean"/>
<!-- INQUISITION			END					MRGENIE		-->
<!-- INQUISITION			10/12/2007			MRGENIE		-->
		<element type="PrereqCivic" minOccurs="0"/>
<!-- INQUISITION			END					MRGENIE		-->
<!-- INQUISITION			10/12/2007			MRGENIE		-->
		<element type="bRemoveNonStateReligion" minOccurs="0"/>
<!-- INQUISITION			END					MRGENIE		-->

Well if you already knew sorry for wasting your time.
 
You guys already probably know this, but the WoC has/had an inquisition unit already coded in the SDK with AI. It might help to see some things from it. I place it in the RevDCM Rapture test merge I did. Mr Genie did the code. If you want to look at it. It is commented with "INQUISITION"

http://forums.civfanatics.com/downloads.php?do=file&id=14028

These are the tags that were added, and you have to add the new mission to the base xml of course. It will allow multiple inquisitor units.
Code:
<!-- Unit Schema -->

<!-- INQUISITION            10/12/2007            MRGENIE        -->
    <ElementType name="PrereqCivic" content="textOnly"/>
<!-- INQUISITION            END                    MRGENIE        -->
<!-- INQUISITION            10/12/2007            MRGENIE        -->
    <ElementType name="bRemoveNonStateReligion" content="textOnly" dt:type="boolean"/>
<!-- INQUISITION            END                    MRGENIE        -->
<!-- INQUISITION            10/12/2007            MRGENIE        -->
        <element type="PrereqCivic" minOccurs="0"/>
<!-- INQUISITION            END                    MRGENIE        -->
<!-- INQUISITION            10/12/2007            MRGENIE        -->
        <element type="bRemoveNonStateReligion" minOccurs="0"/>
<!-- INQUISITION            END                    MRGENIE        -->
Well if you already knew sorry for wasting your time.

I believe Phungus added all the tags we need, but I would be interesting in seeing the AI code.

Edit: I just grabbed WoC's 3.19 source code, and the Inquisitor code there seems pretty sophisticated. I like it better than OrionVetern's method of interrupting normal AI decision making to force them to make an Inquisitor. My 2:commerce: anyway.
 
Well I'm all for moving this code over to the SDK. The less that's in Python the better, in fact it was one of my goals. I already know all the SDK code I've added is stable and effective, so I don't want to change things from that in terms of XML tags. But I'd like to look over the SDK code that adds the AI and purging functionality so I could merge it in and drop all this unnecessary Python, sure. One last note that's important to put out there is that I can't just trust the stability of WoC code though, so this will all need to be merged in carefully, while making sure the components we are grabbing don't cause more harm then good. Also the thing with the current AI code is, is that it's designed to purge mainly for religious revolutionary problems, and that's the main reason to purge non state religion in RevDCM, there isn't much reason to get rid of non state religion otherwise, so I'm a bit curious as to what exactly is in the AI code here, and why the AI is conduction inquisitions for any other reason but Revolutionary ones.
 
Yes, j should be eUnit. The Falses are the zeros you had in your code. Look at the Python API for getProductionCost() to see what they are. I changed it midway through and missed one. It builds a list of tuples (yes, like coordinates). A tuple is a list that cannot be modified; otherwise it's just like a list (array).

The sort() call sorts this list of pairs, and when comparing two tuples/lists, it does so by elements. So (1, 12) < (2, 3). Since the production cost is the first element, it sorts the units by cost and then by unit number (when two units have the same cost).

list[x][y] pulls the xth element of list and then pulls the yth element in the list stored there. In this case, the [0] pulls the first tuple from the list and [1] pulls the second element out of that tuple (eUnit). So this code results in the cheapest inquisitor unit.
 
Thanks EF. I'm still a bit confused about the List[x][y] function though. For instance how does it work for lists that are set up in groups of thread, or just like most lists of single instances. Basically what I don't get is how that code written that way is returning only the value stored in position y, and not position x.
 
Back
Top Bottom