• Our friends from AlphaCentauri2.info are in need of technical assistance. If you have experience with the LAMP stack and some hours to spare, please help them out and post here.

Free Technology granted at a specific Year: a Python solution

isenchine

Empress
Joined
Oct 18, 2010
Messages
1,774
Location
Brussels, Belgium
We have all experienced games with Civilizations completely lagging and way behind the calendar. As you know, it depends so much on options at the start, map sizes, game speeds. It's a never-ending process of adjusting Tech costs.

As a remedy, I wanted to have this for my mod: at specific given Years, all Players (including Barbarians) receive a specific Technology if they have not researched it yet. I have certain technologies which are dated (Like 400 BC, AD 1, AD 500, ...). I find it easier to attribute certain Buildings, Units etc related to a period of time rather than a certain technology, but this Python solution is valid for all technologies actually.

I don't really know Python but I can play with it. I started with a piece of code made by The_J which was granting something at a given Year. I completed it with bits of a code from OrionVeteran's Techs by Conquest and started a very long process of trials and errors...

In the end, this is what I have. Amazingly, it works!

Spoiler :
PHP:
	def onBeginPlayerTurn(self, argsList):
		'Called at the beginning of a players turn'
		iGameTurn, iPlayer = argsList
# Isantique
		#iTurn = CyGame().getGameTurn ()
		iTurn = iGameTurn
		iTurn  = iTurn+1
		iYear = gc.getGame().getTurnYear(iTurn)
		pPlayer = gc.getPlayer(iPlayer)
		pTeam = gc.getTeam(pPlayer.getTeam())
		if iYear == -3900:
			iTechnologygiven = gc.getInfoTypeForString("TECH_MEDITATION")
			if not pTeam.isHasTech(iTechnologygiven):
				pTeam.setHasTech(iTechnologygiven, True, iPlayer, False, False)
				# Show tech splash 
				if not CyInterface().noTechSplash():
					if (gc.getGame().isFinalInitialized() and not gc.getGame().GetWorldBuilderMode()):
						if not gc.getGame().isNetworkMultiPlayer():
							popupInfo = CyPopupInfo()
							popupInfo.setButtonPopupType(ButtonPopupTypes.BUTTONPOPUP_PYTHON_SCREEN)
							popupInfo.setData1(iTechnologygiven)
							popupInfo.setText(u"showTechSplash")
							popupInfo.addPopup(iPlayer)

		elif iYear == -3800:
			iTechnologygiven = gc.getInfoTypeForString("TECH_WRITING")
			if not pTeam.isHasTech(iTechnologygiven):
				pTeam.setHasTech(iTechnologygiven, True, iPlayer, False, False)
				# Show tech splash 
				if not CyInterface().noTechSplash():
					if (gc.getGame().isFinalInitialized() and not gc.getGame().GetWorldBuilderMode()):
						if not gc.getGame().isNetworkMultiPlayer():
							popupInfo = CyPopupInfo()
							popupInfo.setButtonPopupType(ButtonPopupTypes.BUTTONPOPUP_PYTHON_SCREEN)
							popupInfo.setData1(iTechnologygiven)
							popupInfo.setText(u"showTechSplash")
							popupInfo.addPopup(iPlayer)
		else: return
# End Isantique
Of course, the two Years and two Technologies are here just for examples, for easier testing. A minus Year means BC.

What I would like now is that a more experienced person in Python look at the code and make any adjustment necessary.

Some questions:
1) is the function onBeginPlayerTurn the best one for this?
2) is the if-elif-else way the most efficient way to do this?
3) ... can't remember right now!...

One problem, depending on game speeds, is that the given Year might not be reached precisely, so I intend to have it rather like iYear >= 1800 and iYear < 1900 for example.

All comments, observations appreciated.

Thank you.

Edit: this is an excerpt from CvEventManager.py. Obvious to some...
 
1) As you already know, comparing game year using == is definitely not ideal since it may not appear for a particular game speed.

2) I rather use onBeginGameTurn or onEndGameTurn.
Using onBeginPlayerTurn, every turn you are checking it X times for every single player to see if the year fits the one you want.
For a game with 18 players, you are checking this 18 times per turn, +1 for barb too.
Why not just check it once per turn to see if the year fits, and if so then loop through each player?

3) If you only intend to have this feature for less than 3 techs, then yeah if else is enough.
If you intend to have it for 50 techs, then obviously writing the code itself is already troublesome.
And at the same time, every turn you are going to check if else 50 times.

Suggestion:
1) Define a variable self.ABCwhatever = [-3890, "TECH_WRITING", -3880, "TECH_CALENDAR", -2000, "TECH_STEALTH", blah blah blah]

2) onGameStart and onGameLoad, check if current game year > first entry in the list.
If true, it means that year has already passed. Everyone should have that tech already since it was checked previously when you played.
Thus, remove the first 2 members of the list.
Continue until current game year < first entry in the list, which means the game has not reached that year yet.

3) Then onBeginPlayerTurn or onEndPlayerTurn, usually I use End, just check one time, whether current game year >= first member in the list.
If so, grant the tech stored directly as next member in the list to all players.
After doing so, remove these 2 members from the list.

Conclusion, one check per turn, irregardless of how many techs there are.
All you need to do is
1) make sure all the data stored in the list are sorted according to game year.
2) remove them after granted.
 
Wow, thanks a lot, platyping!

2) I tried with onBeginGameTurn but ... my knowledge of Python is quite limited: "iPlayer" is not defined there and I couldn't make it work :blush:

3) I intend to have it 18 times so I understand that it is a bit demanding (although I tested it already and it works fine. Everything is relative and it's a computation that probably doesn't take so long with nowadays computers).

Your suggestion looks interestingly intelligent :D

I'll have to think of it. Today I can only think of it...

Thanks again ;).
 
If you intend to have this feature for 18 techs, and you are using those dlls with > 18 civs like 50 civ:

Using your style, first of all you need 18 if else statements.
And every turn you are checking this 18 if else for every single player, dead or alive.
For a 50 civ dll, you are gonna be checking 900 times every turn.

My style will be 1 time every turn :D

Why will you need iPlayer to be defined?

Each turn:
Step 1) Check that list is not empty
Step 2) Check that Game Year >= First member in the list
If true:
1) Loop through every team
2) Grant the tech to them, which is simply the next member in the list
3) Remove these 2 members from the list, so third member in the list now becomes first member
4) If you intend to have some techs with similar or very close years, then use a loop and go back to step 1
 
Example:

List: [-3500, Tech A, -3000, Tech B, -20, Tech C, 500, Tech D]

Assuming current Game Year 500 BC

onGameStart and onGameLoad:
1) Use while loop, check if current Turn >= First member (-3500)
2) True, so remove -3500 and Tech A from List.
List = [-3000, Tech B, -20, Tech C, 500, Tech D]
3) Go back to Step 1
4) True again, remove again
List = [-20, Tech C, 500, Tech D]
5) False, break the loop

Now game is started/loaded, play time
onEndGameTurn
1) Check if list is empty
2) Again, check if game year >= First member (-20)
4) False, so nothing happens

Until 20 BC comes
1) Now this becomes true
2) Loop through every team, Grants Tech C to all teams
3) Remove -20 and Tech C from list
List = [500, Tech D]
4) Go back to Step 1 and check again if there are techs with similar years
5) False, so continue playing

Now year 500
1) Check again, true again
2) Grant again, remove again
3) List now empty.

From now on
1) List empty, ignore all remaining codes

Every turn, there will just be one or two checks.
1) Is the list empty
2) If not, check game year.
3) If true, grant tech.
 
One step at a time...

After 6 hours of struggle, I managed to check the year on 'onBeginGameTurn' instead of on 'onBeginPlayerTurn'. From there, I call a specific file (module) which distributes the Techs when conditions are met. Still it works!

So to take back your example, with 18 if else statements, I check the year 18 times per turn instead of 18 * (18 players + 1 barbarian). Quite a progress!

To arrive finally to your solution (for which I thank you!), I will probably need 60 hours more... :lol:
 
You could have asked for hint :D

Olympics is a perfect base to use for doing that.

For self defined global variable and onGameStart/Load, you can refer to this or that.

Since you like to explore by trial and error, I won't spoil your fun with the solution directly :D
I learnt my python this way also from tsentom1's works so it is actually the best way to learn for me. You already have decent programming skills since you know SDK so python shouldn't be hard.
 
Exactly! Thanks again.

I "know" the SDK but I don't know C++...

By the way, iYear for AD 1 does not seem to be 1. 0? EDIT: Yes, it's 0.

I was thinking of the other way around: Civs who are too ahead of their time. So I disabled all Techs with BC x, AD y. Since they are all pre-requisites for the normal Techs, each Civ is then blocked into a period of time and has to wait until a certain Year is reached before researching new Techs. No problem if there is nothing to research: it works!

Now, I don't know if it could interest somebody, but it would be easy to insert Techs for each new era for example, and to unblock them at specific years. This way, no more Civs far behind or too much ahead of their time!
 
Back
Top Bottom