Specific Civ getting techs from conquest

Jarlaxe Baenre

Emperor
Joined
Feb 17, 2010
Messages
1,959
Location
Calgary, Alberta, Canada
I checked for existing mods with this, but none of them specified a specific civ and I don't know where to add it. What I want is for a certain civ (let's say the civ's name is Random) to randomly get 100-200 research points for a tech that civ had, but they didn't, upon conquest. If possible, for it to only happen in cities with libraries.
 
How much Python do you know? Because its just a question of finding the methods you need in the API and putting them together with conditional statements and what have you. Then you need to plug the code into some Event Manager that is available in your mod. Did you even look in the API?

I don't understand the random thing though. How can it be a random Civ if that Civ has conquered a city? :confused: Or doens't it matter - some other (random) Civ will get the research bonus if a third party loses a library? (I'm trying to visualize this and its not easy!)

But anything you describe can be done with Python - and you don't need to build on some pre-existing code. Unless you know what you're doing that might prove to be even harder.
 
Ok, here is what I could dig up in the API. Everyone should start here because this is how you do Python modding.

First and foremost - Techs are really team issues so I naturally look in the CyTeam class. There I find changeResearchProgress() which is listed as #32 and isHasTech() #134. The first one is listed as VOID and this means that it is used to manipulate some value inside the game. The second one is BOOL and this means that it returns either a True or False value. But you still need to look these methods up to see what parameters/settings they take.

Working with the CyTeam class requires a valid CyTeam instance however, and for that you need the CyGlobalContext class. The actual method you need is getTeam() #227. If you look in the API entry you can see for yourself that it returns a CyTeam instance, and this is what you need.

Since you wanna execute your code any time a city is taken by force, you will need to get hold of the actual CyCity instance. Once you got that you will be able to use isHasBuilding() #334 in a conditional statement to limit the bonus to Library cities only.

(It could be however that the Library has already been destroyed, and will thus not be registered. Then you could change the settings in the XML so that Libraries aren't destroyed on conquest.)

If none of this made any sense what-so-ever you probably need to take a couple of hours and read up on the basics. Or you could spend those hours looking for a ready-to-use solution, for waiting on replies in this thread, or go about trying to make what you need on random. But I basically spelled it all out for your in this post. I could of course do the whole thing for you to your specifications, but what would you have learned then?
 
I was just giving an example for the civ name. I'll give a different example: The Huns (because that's the civ I am going to give this bonus in a mod) conquers the Roman city of Rome. It has a library. The Romans have several techs the Huns don't have. A random tech (with a preference to Military ones) is chosen from the list. Military Tradition is chosen. They recieve 300 beakers towards it.

Go back one turn to where the Huns were 1 turn away from conquering Rome, and conquer Antium instead. Antium does not have a library. They get 200 beakers toward a random tech the Romans have that the Huns don't. The tech may or may not be Military Tradition.
 
Ok, what you need is the Player index number of the Huns. Lets say that you assign this value to the constant eHuns.

Plug into onCityAcquired (or whatever its called) in CvEventManager and check if the conquering Civ is equal to eHuns. If they are you might do another check and see whether or not the city was conquered. Next you need to get the ID of the previous owner of the city. And lastly you need the CyCity instance of the city in question. All these values are available from the DLL inside the tuple argsList, but they may already be assigned to variables in the Event Manager of choice.

You could just add code in the Event Manager or you could create your own module for this mod. The latter is more proper but you can just flood the former with code if you want.

Anyway, once you have a actual CyCity instance of a city conquered by the Huns, you can add another conditional about the Library building. If that also passes, then you need to detect some random Tech belonging to the former owner of the city/library. (Probably by looping through all Techs...) But first you need to get the CyTeam instance of that Player.

Random numbers are generated with CyGame.getSorenRandNum(), by the way.

When you're getting random Techs you can compare them to both the Huns' team and the city owners team with isHasTech(). If the latter has it but not the former, then you assign it to a variable like eTech and end the search with the break command.

Now its merely a matter of granting some random number of beakers to the Huns for the eTech research progress.

This actually sounds like a pretty good homework assignment. :king:

edit: You meant that the Huns always get beakers towards a random Tech, but the Library grants them additional 100 beakers? This can of course also be done.
 
You basically need to know Python. Take the time to learn this stuff and it will be the easiest thing imaginable. (At least compared to some of the really advanced stuff.)

But to answer your second question, you need to locate the definition for onCityAcquired (or something like that, I don't know this stuff by heart). But you still need to know how indentation works, what a block of code are, how to make conditional statements, what it means that functions/methods return values, basic stuff like that.

One tip however: If you're trying to get into Python for real, there is a module called PyHelpers that comes with the game. If you can figure out how to use that you can probably skip using the main API and work with PyHelpers instead. (It shrinks the interface down to the most basic methods - and also offers some powerful tools not available elsewhere.) Locate the PyHelpers.py file and take a look. I'll be glad to explain it all for you, but there is no way to utilize it without basic Python knowledge.

Too bad I didn't understand how to work with PyHelpers back when I would have benefited from it the most. :p
 
Further about PyHelpers - instead of having to use both a CyPlayer and CyTeam class to do Python with a specific Civ, there is only the PyPlayer class that is used for both the actual Player and the Player's team. There are methods for looking up if a PyPlayer has a Tech - or to fetch a ready-to-use list of all Techs belonging to that PyPlayer. Useful things like that. :goodjob:
 
A "conditional statement" could look like this:
Code:
if pOwnerTeam.isHasTech(eCurrentTech) and not pHunsTeam.isHasTech(eCurrentTech):
    pHunsTeam.setHasTech(eCurrentTech, True, eHuns, False, True)
The first line actually consists of a conditional statement with a pair of boolean statements (which can either be True or False) making up a logical statement (with the and boolean operator). The second line is indented because it makes up a new block of code - belonging to the conditional statement on the previous line.

But you won't be able to just copy-paste this into the Event Manager either. All those variables need to be defined first. And you need to adjust indentation for both lines. And you also need to add some form of iteration that loops through available Techs, so that you get a eCurrentTech value to compare with.

And you still need several more conditional statements before you get to this bit.

Anyway way you look at this its a real programming job. Thankfully programming skill can be readily acquired. (Link in my sig. ;))
 
Oh, the variables would mostly depend on what city was conquered, not? The eHuns and pHunsTeam variables you could of course have as constants - a lot like in RFC. But you don't need an entire Python module for that. Just assign them somewhere so that they are assigned before they are referenced. At the beginning of the module you are using them in would be fine.

The definition for onCityAcquired() includes a variable called argsList (or something similar). Its actually a tuple (or a list?) - basically a data structure - containing all the stuff you need. The trick is to extract the data and assign it to variables - but as I said before - it could already be done in the CvEventManager you're using for this. (Look it up - it might not even be an issue.)

The tricky bit is probably to get the CyTeam instances to use with the Tech related methods (see my sample code). But if this is still not making any particular sense to you, we need to back it up so that you can follow. It makes no sense trying to talk over your head.

And the mod could probably be done with PyHelpers - but that would probably be just as complicated to understand. It could pay in the long run, however.
 
Well, I guess I could add that I am making an RFC mod-mod replacing the Khmer with the Huns, and this python is their UP, so all I really need is the actual getting the techs after the conquest part and putting it in Uniquepowers.py
 
I think Baldyr is throwing a lot of information (that you my not yet be equipped to catch)...

Variables like that are normally defined right before you use them, or at the beginning of the function where they are used, although they can be set up earlier if they are actually constants that are not going to change over the course of the game (for example: an iAmTheHuns value to identify the Huns, which could be set when the game starts or is loaded from a save).

The onCityAcquired event handler gets arguments that include the owner index for the previous and new owners of the city, plus the city object itself and flags indicating whether it was conquered or traded. You can use these to get anything else you might need, like you can get the actual player object from the player index value (via gc.getPlayer(iPreviousOwner)).

Here is an overview of how I would go about doing the things you have stated that you want. (Basic software design = make a plan for how to go about doing the things you want to do in a somewhat broad step by step manner. During this stage you decide which of various alternative approaches to use. Also during this planning you can often identify issues you hadn't thought of before. More may arise when you actually start coding.)

First check that the city's new owner is the Huns. If not, skip the rest.

Then I would either use the PyHelpers to get a PyPlayer for each of the two players to use the getResearchedTechList function or (more likely) not actually use the PyPlayer class but copy the code from that function (it is full of useful examples). I'd use that to makes a list of all techs known by the previous owner. I'd then loop through the previous owner's list and copy any that are not already known by the Hun. (This is where the "various alternative approaches" crops up - you could have also made a list of the Huns' techs and then checked each entry in the other list to see if it is already in the Huns' list via Python's "in" operator that does just that, removing any that are. This is probably infinitesimally slower and would use a little more memory.) The reason for making a new list is that removing items from a list as you are looping over the list is not a good plan - it can produce "unexpected results".

If the new list is empty, you are done (and the Huns are probably a serious threat to this enemy civ as they have all the techs their opponent has and maybe more).

Here we come to a design issue: You have to decide if you want to give research points to the Huns for techs that they can't actually currently research (techs that they don't have a prereq for). If you don't care then you don't have to do anything else to the list. If you want to limit it to techs they could actually research themselves at that point in the game then you need to process the list again using the Huns' player object's canResearch function to see if they can currently research the tech, removing any from the list that they can't research (well, making a new list with only the valid ones in it - be careful about the difference between how you describe what you want to do and how it is actually done).

Then, if the fully processed list is not empty, I'd build a new list to weight the techs as to whether or not they are military techs. Looping through the culled list, I'd check each tech's military flavor value and give each tech a weight value of something like 5 or 10 + the military flavor value and insert the weight and the tech into a new list as a tuple. I'd also keep a running total of all of the weights.

Once that list is done, I'd generate a random number specifying the total weight as the range (giving a random number from 0 to 1 less than the total weight). Then loop through the list adding the weights of each element to a count until the count exceeds the random number. Whichever tech you are on in the list when that happens is the tech that you have picked.

Now set an iResearchToGive variable to 200. Check the city for a library and if there is one change it to 300. Then give the Huns that number of research points towards the tech that was selected.

One problem is that the DLL eliminates buildings from the city before onCityAcuqired is called. The city is already in the state that it will be in should the player decide to keep it - this Python event happens just before the screen for keep/raze/view city pops up. The default game gives libraries a 0% chance of surviving. Therefore there will never be a library if you use the same data. So you might want to set them to have some chance of surviving in your mod, although I wouldn't make it 100% (probably a lot lower). You can make them stop giving culture when captured if you want (but that would reduce the possible culture since when they building is destroyed you can rebuild it and get the culture) via the CommerceChangeOriginalOwners like wonders do.

So that answers the question that should probably be asked more often: "What would God-Emperor do?"
 
Oh, its for RFC. If you were able to add a new Civ to the mod :eek: then you have already defined the Huns in the Consts module, right?

And you don't use the CvEventManager module to launch your code, but you rather use the CvRFCEventHandler module. If you look up onCityAcquired() you can see that some of work has already been done for you:
Code:
        def onCityAcquired(self, argsList):
                owner,playerType,city,bConquest,bTrade = argsList
This type of assignment statement is called "tuple assignment" as it assigns values to several variables at once - from inside a data structure like argsList.

Once you learn Python you can surely take it from here.
 
But not everyone is willed, and not everyone can.

And you've wrote here a ton more than is needed to solve the problem ;).


----------------------------------------------------------

No idea, what the file is exactly in RFC, but should be somehow have "EventManager" in it, open this file, search for "onCityAcquired", and merge everything from the following code, from techconquest on, in this file.
PHP:
	def onCityAcquired(self, argsList):
		'City Acquired'
		iPreviousOwner,iNewOwner,pCity,bConquest,bTrade = argsList
		CvUtil.pyPrint('City Acquired Event: %s' %(pCity.getName()))
### techconquest begin ###		
		pLoser = gc.getPlayer(iPreviousOwner)
		pWinner = gc.getPlayer(iNewOwner)
		WinnerTeam=gc.getTeam(pWinner.getTeam())
		LoserTeam=gc.getTeam(pLoser.getTeam())
		if pWinner.getCivilizationType ()==gc.getInfoTypeForString("CIVILIZATION_XY"):
                        if pCity.isHasBuilding(gc.getInfoTypeForString("BUILDINGY_SOMETHING")):
                                iMaxTech = gc.getNumTechInfos ()
                                iX=pCity.getX()
                                iY=pCity.getY()                     
                                for techcount in range (iMaxTech):
                                        if ((LoserTeam.isHasTech(techcount)==true)and ((WinnerTeam.isHasTech(techcount)==false))):
                                                WinnerTeam.setHasTech(techcount,1,iNewOwner,0,1)
                                                iRand = CyGame().getSorenRandNum(200, 'TellMeTheBeakers')
                                                iOldProgress = WinnerTeam.getResearchProgress(techcount)
                                                iNewProgress = iRand+iOldProgress
                                                WinnerTeam.changeResearchProgress(techcount,iNewProgress,iNewOwner)
                                                CyInterface().addMessage(iNewOwner,false,15,CyTranslator().getText("TXT_KEY_TECH_CONQUERED",()),'',0,'Art/Interface/Buttons/General/happy_person.dds',ColorTypes(44), iX, iY, True,True)
                                                break
### techconquest end ###

Civilization, building and the message have to be defined.

Don't forget to make a copy of the file before, and turn on the python exceptions in your BtS version.
 
Nice! :goodjob:

Now its up to the poor guy to test and debug all that... If nothing else, there will be some obvious improvements to make. But you only figure those out by actual play testing.

This is actually a good enough reason not to do other peoples programming projects - you automatically become their tech support... :rolleyes:

But firstly he has to manage the "merger". So its still a programming job, really. And the code probably makes little to no sense, so nothing is learned.

But I'm glad someone actually took the time to help out with the actual programming. Way to go, The_J! :king: (I'm actually thinking that those who know this stuff could pool together resources in order to help those who can't, I mean won't learn, programming. Because there are just so many cool ideas out there! Too bad about the testing and debugging involved though - otherwise I'd do at least a couple of projects myself a week, minimum! ;))
 
Top Bottom