Tying Unit and Building Availability to Civics choices

Joined
Jul 21, 2003
Messages
7,819
Location
Adelaide, South Australia
Hi guys, have a MAJOR python question for you. I have tried time and again, but have yet to work out a python script that will alter the build queue according to the civics you are currently operating under. i.e. I want it so that certain buildings or units ONLY appear in the build queue if you are in an 'appropriate' civic setting.

On a related note, does anyone know how to make already existing units or buildings disappear if a player changes civics?

Any help people can offer would be VERY much appreciated!!

Yours,
Aussie_Lurker.
 
Okies - number 1:

Had a quick look through and there doesn't seem a way you can allow/disallow production of units/buildings directly. However - it can be done. You'll be limited by the event triggers, but there is nothing we can do about that.

Basically, you make all the civic-specific units require a unresearchable (disabled) tech - and give/take the techs depending on what civics there are. To change the techs you use CyTeam.setHasTech(variables go here). I'm not quite sure where in the turn cycle you'd want to put your check - I don't know if onBeginPlayerTurn will catch it or not.

I'm pretty sure that'll work.

As for number 2:

You have the same event issue as before here... but the script will work. Basically, you cycle through all the player's units/buildings, and (for units) gc.getUnitInfo(iUnit).getPrereqAndTechs(i), where i relates to the order in the xml file (you might want it to check the full range here), will give you the tech requirements. Compare this to the team's current techs, and kill all the ones that don't match!

It's rather late, so if I'm unclear at all, feel free to ask!
 
Thanks so much for your quick reply, Great Apple. First up, how would I go about linking the disabled tech to the various civics-this is what I am having the greatest trouble figuring out? It might help if I gave a specific example.

You have the civic choice 'slavery', which I have already altered in my mod to grant a free slave specialist (+2 hammers, +1 commerce and -1 culture). However, I also want a 'Slave Labour System' National Project to become available if the player has a combination of the 'Masonry' tech AND the 'slavery' civic. Is it possible to create a Python script to deal with this? I look forward to hearing back from you when you have had a bit of sleep ;)!

Yours,
Aussie_Lurker.
 
Sleep is for the weak!

Where pPlayer is a CyPlayer entity
Code:
pTeam = gc.getTeam(pPlayer.getTeam())
iPlayer = pPlayer.getID()

### Gives slavery tech if you have the slavery civic.
if pPlayer.getCivics(gc.getInfoTypeForString("CIVICOPTION_LABOR")) == gc.getInfoTypeForString("CIVIC_SLAVERY"):
	pTeam.setHasTech(gc.getInfoTypeForString("TECH_SLAVERY"), true, iPlayer, 0, 0).
So, that should give the slavery tech to the team. Now you just make your building require both TECH_SLAVERY and TECH_MASONRY (which you can do in the xml file - use <TechTypes> with the subtag <PrereqTech> to add more).

If you have more than one tech you'll need to disable all the other civic-specific techs on the check (change true to false).

I've probably got the syntax wrong in one or two bits, but the idea is there :mischief:... and as I said, I don't know what event you'll need to trigger this properly.

Right... bed time!
 
Thank you Great Apple-that explains EVERYTHING-I really feel confident about giving the mod another shot! You are a CHAMP!!!

Yours,
Aussie_Lurker.
 
Hi again Great Apple. OK, just so I have this straight, how do I disable the tech 'Slavery'? i.e. I have added Slavery as a tech to my techinfo.xml file, how do I then go on to disable it-or do I simply remove it from the techinfo file? Sorry to be such a pain, but you have been such a HUGE help so far (Syntax was only a LITTLE out, easily solved ;)!)

Yours,
Aussie_Lurker.
 
Their is a <Disable>0</Disable> boolean near the top of the tec just set this to 1 aka True to disable the tec. Disabled techs do not apear in the Civopedia and cant be researched.
 
Impaler[WrG] said:
Their is a <Disable>0</Disable> boolean near the top of the tec just set this to 1 aka True to disable the tec. Disabled techs do not apear in the Civopedia and cant be researched.
Err... I think they do appear in the Civopedia - but, yes, that would be the way to do it.
 
My apology guys. I actually figured it out not long after I wrote the above message, but didn't have time to get back and let you know the problem was solved-sorry :(. I have also solved the interface problems I mentioned in the other thread. So far, the mod is looking OK, though it apparently is saying that pPlayer is 'not defined' on line 41 of my script. I have made a few minor alterations, based on my 'specialist script', and hope to try it out tonight.

Yours,
Aussie_Lurker.
 
Hey Great Apple. I tried your script suggestion and unfortunately it didn't work (first some minor syntax issues then just the usual errors.)
Anyway, I have re-written the script-but it still won't work-and I was hoping you could look at it and suggest where I might be going wrong. Thanks in advance.

Code:
## Sid Meier's Civilization 4
## Copyright Firaxis Games 2005

from CvPythonExtensions import *
import CvUtil
import CvEventManager
import PyHelpers
import CvAdvisorUtils
import sys
import CustomFunctions
	
# globals
gc = CyGlobalContext()
PyPlayer = PyHelpers.PyPlayer
cf = CustomFunctions.CustomFunctions()


###################################################
class CvCustomEventManager(CvEventManager.CvEventManager):
	def __init__(self):
		# initialize base class
		self.parent = CvEventManager.CvEventManager
		self.parent.__init__(self)

        def onBeginPlayerTurn(self, argsList):
                'Called at the beginning of a players turn'
                iGameTurn, iPlayer = argsList

                player = gc.getActivePlayer()
                if (player.isCivic(gc.getInfoTypeForString("CIVIC_SLAVERY"))):
                        for i in range(player.getNumCities()):
                                player.getCity(i).setFreeSpecialistCount(gc.getInfoTypeForString("SPECIALIST_SLAVE"), 1)
                else:
                        for i in range(player.getNumCities()):
                                player.getCity(i).setFreeSpecialistCount(gc.getInfoTypeForString("SPECIALIST_SLAVE"), 0)

        def onBeginPlayerTurn(self, argsList):
                'Called at the beginning of a players turn'
                iGameTurn, iPlayer = argsList
                
                player = gc.getActivePlayer()
                ### Gives slavery tech if you have the slavery civic.
                if (player.isCivic(gc.getInfoTypeForString("CIVIC_SLAVERY"))):
                        player.getFreeTech(gc.getInfoTypeForString("TECH_SLAVERY"), 1)

                else:
                        player.getFreeTech(gc.getInfoTypeForString("TECH_SLAVERY"), 0)

Yours,
Aussie_Lurker.
 
Ah, isCivic will work - hadn't seen that!

However, I'm not so sure about player.getFreeTech. I don't think it's the right function. Usually functions with a prefix "get" will return data about the thingy, rather than changing it. At a guess I'd say that function is to get data about the starting techs of a player.

The function for giving techs is under CyTeam (because in team games, you can't give a player a tech, only a team). You will need to find the team entity for the player, and then use setHasTech(TechID, bNewValue, iPlayer, bFirst, bAnnounce).
 
Sorry to bug you again, Great Apple, but the TechID part of the setHasTech string, is this its numerical ID or its name? I am guessing the former, but am not 100% sure. Also, does the rest of the string remain as is (i.e. bNewValue, iPlayer, bFirst, bAnnounce) or is there something I need to put in each of their place?
Thanks in advance for your help. It is VERY much appreciated.

Yours,
Aussie_Lurker.
 
Oh, whilst on the subject of modding, would you happen to know how this function might work?

Code:
changeResearchProgress(...)
void (TechID, iChange, iPlayer )

I am hoping that this can be used to alter tech rates if you have certain resources or civics. Any clues Great Apple?

Yours,
Aussie_Lurker.
 
- bNewValue is the boolean to set whether you have the tech or not.
- iPlayer is the integer value corresponding to that player in the game
- bFirst indicates that the game should check if the player is the first to research the tech, and if so, give any first-research bonuses
- bAnnounce indicates whether or not to announce the researching of the tech to the global ingame event log

I'm pretty sure TechID corresponds to the numerical location of the tech in Civ4TechInfos.xml. There's also constants set up for each tech, although you might be able to refer to a tech directly by it's name. I hadn't seen the isCivic function though, I guess it does the same thing as GetCivic. Go redundancy :)

Here's the code I use for an improvement that's enabled under the Environmentalism civic, which works without problems ingame:

Code:
def onBeginPlayerTurn(self, argsList):
	iGameTurn, iPlayer = argsList

	CIVICOPTION_ECONOMY = 3
	CIVIC_ENVIRONMENTALISM = 19
	TECH_ENVIRONMENTALISM = 86
                
	ePlayer = gc.getPlayer(iPlayer)
	eTeam = gc.getTeam(ePlayer.getTeam())
                
	if(ePlayer.getCivics(CIVICOPTION_ECONOMY)==CIVIC_ENVIRONMENTALISM):
		eTeam.setHasTech(TECH_ENVIRONMENTALISM, True, iPlayer, False, False)
	else:
		eTeam.setHasTech(TECH_ENVIRONMENTALISM, False, iPlayer, False, False)

Unfortunatly (as far as I know), there's no event that is accessable to us for when a player changes civics, so effects won't take place until the next turn for Spiritual players.
 
changeResearchProgress sounds to me like it changes the amount of beakers the player currently has in a tech. I suppose you might be able to bodge it to increase or decrease tech rates depending on certain values. Not sure...

As for weather it has to be "TECH_SLAVERY" or the int corresponding to it. I've used the int, though sometimes both seem to work. You could always test it (you can change the python while playing and it takes up the new changes).

@Thalassicus

Instead of defining TECH_ENVIRONMENTALISM = 86 at the start, can you not use gc.getInfoTypeForString("TECH_ENVIRONMENTALISM"). It'll do roughly the same thing, but it'll work find if the tech's number changes, and saves alot of counting ;)
 
OK, seems like I am FINALLY moving forward-in a manner of speaking. When I got masonry, the build queue DID change, unfortunately all build options VANISHED :(. Though this is bad, the fact that it has never happened before suggests that my python script seems to be doing SOMETHING.
The only other problem I seem to have is that my workers have the Airlift ability, even though its only the ancient age, and I have NO idea why??

Can either Thalassicus or Great Apple possibly let me know what might be wrong in either of these cases?

Oh and, as suggested elsewhere, when I tried to start this mod from scratch, it wouldn't create the new specialists, even though I did everything the same as what I did it before. Again, any suggestions?
Thanks in advance.

Yours,
Aussie_Lurker.
 
Right, now I am TRULY confused :confused:. This is the story-I have now eliminated ALL of my python errors messages, which should mean that my script should work. However, when I get Masonry, I STILL can't build a slave market, suggesting that I don't have the slavery tech. Now, that said, I was trying it out on a previously saved game, so I will try it on a brand new game tomorrow, and let you know if it works. All things being equal, it SHOULD, but I do wish I could also get rid of some of these annoying little errors I mentioned above!
Anyway, hopefully you should see Civic_Mod v0.0001 posted here sometime tomorrow!

EDIT:Still no luck getting the Slave Market to appear. I just don't get it, my PythonErr Logs are showing no errors any more, and I have followed the gist of Thallassicus' script to the letter, so I see no reason why its not working. The only things I can think of is that it has something to do with the way I have assigned tech prerequisites for Slave Market, the way I have set up the Slavery Tech (disabled, no cost but visible) or just something still not right with the script!
Here is the script for you to look at, if either of you guys can see where I am going wrong, please let me know!
Code:
def onBeginPlayerTurn(self, argsList):
		'Called at the beginning of a players turn'
                self.parent.onBeginPlayerTurn(self, argsList);
		#Grants free Slave when slavery civic is taken

                player = gc.getActivePlayer()
                if (player.isCivic(gc.getInfoTypeForString("CIVIC_SLAVERY"))):
                        for i in range(player.getNumCities()):
                                player.getCity(i).setFreeSpecialistCount(gc.getInfoTypeForString("SPECIALIST_SLAVE"), 1)
                else:
                         for i in range(player.getNumCities()):
                                player.getCity(i).setFreeSpecialistCount(gc.getInfoTypeForString("SPECIALIST_SLAVE"), 0)

                #Grants free Serf when Serfdom Civic is taken

                player = gc.getActivePlayer()
                if (player.isCivic(gc.getInfoTypeForString("CIVIC_SERFDOM"))):
                        for i in range(player.getNumCities()):
                                player.getCity(i).setFreeSpecialistCount(gc.getInfoTypeForString("SPECIALIST_SERF"), 1)
                else:
                        for i in range(player.getNumCities()):
                                player.getCity(i).setFreeSpecialistCount(gc.getInfoTypeForString("SPECIALIST_SERF"), 0)
                                
                #Grants access to Slave Market in the City Build Queue
                iGameTurn, iPlayer = argsList
                
                CIVICOPTION_LABOUR = 3
                CIVIC_SLAVERY = 11
                TECH_SLAVERY = 86

                ePlayer = gc.getPlayer(iPlayer)
                eTeam = gc.getTeam(ePlayer.getTeam())

                if(ePlayer.getCivics(CIVICOPTION_LABOUR) == CIVIC_SLAVERY):
                        eTeam.setHasTech(TECH_SLAVERY, True, iPlayer, False, False)
                else:
                        eTeam.setHasTech(TECH_SLAVERY, False, iPlayer, False, False)

                #Grants access to Feudal Manor in the City Build Queue
                iGameTurn, iPlayer = argsList
                
                CIVICOPTION_LABOUR = 3
                CIVIC_SERFDOM = 12
                TECH_FEALTY = 87
                
                ePlayer = gc.getPlayer(iPlayer)
                eTeam = gc.getTeam(ePlayer.getTeam())

                if(ePlayer.getCivics(CIVICOPTION_LABOUR) == CIVIC_SERFDOM):
                        eTeam.setHasTech(TECH_FEALTY, True, iPlayer, False, False)
                else:
                        eTeam.setHasTech(TECH_FEALTY, False, iPlayer, False, False)

Again, thanks in advance for all your help :)

Yours,
Aussie_Lurker.
 
Hmmm, there's nothing majorly wrong there from what I can see with a brief look. There are a few slightly crude bits (if you don't mind me saying), like defining some stuff twice... but that shouldn't cause it to do what you are saying it does.

Maybe it's something to do with savegames - I don't know.
 
The Great Apple said:
Instead of defining TECH_ENVIRONMENTALISM = 86 at the start, can you not use gc.getInfoTypeForString("TECH_ENVIRONMENTALISM"). It'll do roughly the same thing, but it'll work find if the tech's number changes, and saves alot of counting ;)
Thank you, I didn't realize there was a function for that. I was using the guide in the API :)

A_L, try doing just a small portion at a time to get it to work, then impliment it in the rest. For example, first test just enabling a tech and disabling it depending on whether a civic choice is selected, and see if that works; if not, insert debug messages to see where the problem might occur. It's a lot easier to debug small blocks of code at a time. :)
 
I just interrupt the cannotTrain event and disable the availability of certain units based on whatever criteria I set. I've done it in a bunch of different ways but here is an example of disabling units for specific religions (so 'The Order' Religion isn't allowed to make Eidolons).

You can do the same based off of anything you can query. You can do the same with buildings if you interrupt the CannotConstruct event. Although I think the enable/disable tech idea could work, this method seems simplier to me.

Code:
def cannotTrain(argsList):
	pCity = argsList[0]
	eUnit = argsList[1]
	bContinue = argsList[2]
	bTestVisible = argsList[3]
	ePlayer = pCity.getOwner()
	pPlayer = gc.getPlayer(ePlayer)

	if eUnit == gc.getInfoTypeForString('UNIT_EIDOLON'):
		if pPlayer.getStateReligion() == gc.getInfoTypeForString('RELIGION_THE_ORDER'):
			return True

	if eUnit == gc.getInfoTypeForString('UNIT_EIDOLON'):
		if pPlayer.getStateReligion() == gc.getInfoTypeForString('RELIGION_RUNES_OF_KILMORPH'):
			return True

	if eUnit == gc.getInfoTypeForString('UNIT_PALADIN'):
		if pPlayer.getStateReligion() == gc.getInfoTypeForString('RELIGION_THE_ASHEN_VEIL'):
			return True

	if eUnit == gc.getInfoTypeForString('UNIT_PALADIN'):
		if pPlayer.getStateReligion() == gc.getInfoTypeForString('RELIGION_OCTOPUS_OVERLORDS'):
			return True

	if eUnit == gc.getInfoTypeForString('UNIT_HIGH_PRIEST'):
		if pPlayer.getStateReligion() == gc.getInfoTypeForString('RELIGION_THE_ASHEN_VEIL'):
			return True

	if eUnit == gc.getInfoTypeForString('UNIT_DEMON_SUMMONER'):
		if pPlayer.getStateReligion() == gc.getInfoTypeForString('RELIGION_THE_ORDER'):
			return True

	return False

As for how to destroy the buildings you are going to have to build a function on begin player turn that checks for buildings that require civics and makes sure the civics are still in place. Which someone else mentioned earlier in the thread.
 
Back
Top Bottom