Easy switch of game speed and difficulty

DanF5771

Emperor
Joined
Feb 21, 2008
Messages
1,194
Hi fellow fanatics. :hatsoff:

I see the S&T forum is currently very active with a lot of Cookbooks / Universities / Challenges / LHCs ... That is of course very cool and has brought me back to the old problem of switching game speed and difficulty for a 4000 BC starting save via a WB-scenario-file. Right now quite a lot of work which also involves the danger of exposing yourself to spoiler information is needed to achieve the correct settings and to get the number of starting units and starting techs right for the various difficulties/speeds. Thus I've looked into the WB's Python code CvWBDesc.py and added/changed a few lines of code in order to try to make these kind of switches much more convenient.

1. Change:
Upon starting a new game from a WB-file now ALL the AIs (including the barbs!) will receive the free starting techs according to the difficulty level (Monarch ... Deity). Likewise the human player will receive his free starting techs for difficulties < Noble.

2. Change:
Saving a WB-file will now trigger the creation of a 2nd file (with "_Template" added to the filename), which is a copy of the normal scenario-file but which apart from all the map info only contains the units of the human and the regular 2 starting techs of the respective civilizations. The AIs' units and all the techs which were given for free according to the difficulty (human + AI) are not saved. Furthermore the game settings for speedType and maxTurns remain unset (NONE/0) and only the human player will be saved with isPlayableCiv = 1. Starting from this template will make the game automatically add the correct starting units for the AIs in their starting plots according to the chosen handicap while the units of the human player are loaded from the file (thus no jumping around of the starting Warrior/Scout). The correct techs are added due to Change#1.

The cool thing I found out is that the game automatically creates a new WB-file called WBQuickSave.CivBeyondSwordWBSave for the 1st game after a fresh start of Civ. With the changed CvWBDesc.py the template copy of that file will also be created, so one doesn't even have to open up the WB (and see Horses + Copper in the BFC... :eek:) to produce this template for a given 4000 BC save. Just restart Civ by double-clicking the 4000 BC save and immediately exit to main menu again. Then choose single player --> play a scenario and select the freshly created WBQuickSave_Template. Choose your favourite difficulty and speed and have fun.

The attached modified CvWBDesc.py needs to be unpacked+saved to ...\CustomAssets\python\pywb\CvWBDesc.py. I'd be glad if you tried it out. Please let me know how it works for you. Since I'm far from being a Python expert, I hope I didn't introduce any / too many bugs. I'd be especially thrilled about a feedback from the Python Gurus of the BUG-Team :bowdown::mischief:. Wouldn't it make sense to include these/similar changes in a future version?
 

Attachments

  • CvWBDesc.zip
    11.7 KB · Views: 59
Dan . . .

/bow
/salute
/cheer

You are the MAN bro. TMIT is going to wet himself when he sees this, rolo too!

You should PM this link to Dale the WB guy. He tried to help rolo and I back when we discovered the whole "problem" with the saves. He was actually looking for some way to do this, but hes no python guy either.
 
Dan . . .

/bow
/salute
/cheer

You are the MAN bro. TMIT is going to wet himself when he sees this, rolo too!

You should PM this link to Dale the WB guy. He tried to help rolo and I back when we discovered the whole "problem" with the saves. He was actually looking for some way to do this, but hes no python guy either.

My pants are dry, but this is pretty cool. Thanks :goodjob:.
 
Is it possible to use this to make WB files into mp games where the barbs can retain archery? Also would it cause different assets upon playing mp? If so it should be put somewhere in a mod folder right?
 
This sounds awesome Dan, will try it out the next time I'm looking to convert a game. :goodjob:
 
Thanks for all the replies guys! No complaints yet -- great!
Bleys said:
You should PM this link to Dale the WB guy.
pm'd the mod-god :bowdown:, let's see.
TMIT said:
My pants are dry
I'm rather glad to hear that ;). ... rolo ??
oyzar said:
Is it possible to use this to make WB files into mp games where the barbs can retain archery? Also would it cause different assets upon playing mp? If so it should be put somewhere in a mod folder right?
I'm not 100% certain, since my mp experience is rather limited, but IIUC the whole custom assets folder is simply ignored in a mp game. So I guess all the players of a particular mp game would either need to put the CvWBDesc.py into their default assets folder or into a separate mod folder to avoid causing "different assets!". I just tried a Hotseat mp game with 2 humans (needed to change PlayableCiv=1 for a 2nd player) and the rest AI with the CvWBDesc.py in my default assets folder. I created a scenario with a barb city nearby and used the template file to start the mp game with both humans playing on Monarch. The barbs' first build was an archer so they received their proper starting techs (only Archery on Monarch) -- seems to work!
 
Well, not diminuishing your tireless work to make us more enlightened about this sometimes rather ugly piece of code that we know as Civ IV :p , this makes me ask some questions ( that i would probably see the answer easily if I was not too lazy to read code on Sunday morning ;) :

- In spite of the Firaxis WB save loading is somewhat borked, I always wondered that the way it was done is a design choice and not the costumary code mess that we normally see. You might not want that the civs have the XML given techs in a scenario or the handicap ones, remember? I come to think also that Firaxis never thinked that some Portuguese dude would want to make WB games that give automatically the handicap techs to every level it is loaded and that decision would become popular for other games :p So I'm somewhat bittersweet about the first change as it is :/

-CostumAssets is not ignored in MP games. Proof? I can use Blue marble in MP :p But python costumAssets normally is
 
I'm scanning through the changes you've made as requested, and I think I've found a problem. When writing out the techs for a team, you changed the logic in order to squelch non-free techs for the team's players.

Code:
# write techs
for i in range(gc.getNumTechInfos()):
	[B]if (gc.getTeam(idx).isHasTech(i)):[/B]
		if (bTemplate):
			for iLoopPlayer in range(gc.getMAX_CIV_PLAYERS()):
				if (gc.getPlayer(iLoopPlayer).isAlive()):
					if (gc.getPlayer(iLoopPlayer).getTeam() == idx):
						if (gc.getCivilizationInfo(gc.getPlayer(iLoopPlayer).getCivilizationType()).isCivilizationFreeTechs(i)):
							f.write("\tTech=%s\n" %(gc.getTechInfo(i).getType()))
							continue
		else:
			f.write("\tTech=%s\n" %(gc.getTechInfo(i).getType()))
			[B]if gc.getTechInfo(i).isRepeat():
				for j in range(gc.getTeam(idx).getTechCount(i)):
					f.write("\tTech=%s\n" %(gc.getTechInfo(i).getType()))[/B]

The bold section at the bottom cannot go under the bold if-test at the top because they are mutually exclusive. The result is that WB saves will ignore any repeating techs (e.g. FutureTech) that the player has researched. Also, I think you meant break instead of continue, otherwise teams with multiple players having the same free tech will show multiple entries for that tech.

It won't affect the template, but I think we can fix it for the general case. Similarly, I think checking that the player is alive should be changed to isEverAlive().

Ive rewritten the block to determine how many times it should write out the tech entry (0, 1, or getTechCount() times), and then zero the count if during template mode no players on that team get the tech as a freebie. Note the use of Python's wicked-cool for-loop else clause which executes if the for-loop makes it to the end of the sequence. I really love Python.

Code:
# write techs
for i in range(gc.getNumTechInfos()):
	iCount = 0
	if (gc.getTeam(idx).isHasTech(i)):
		iCount = 1
	elif (gc.getTechInfo(i).isRepeat()):
		iCount = gc.getTeam(idx).getTechCount(i)
	[B]if (iCount > 0):[/B]
		if (bTemplate):
			for iLoopPlayer in range(gc.getMAX_CIV_PLAYERS()):
				if (gc.getPlayer(iLoopPlayer).isEverAlive()):
					if (gc.getPlayer(iLoopPlayer).getTeam() == idx):
						if (gc.getCivilizationInfo(gc.getPlayer(iLoopPlayer).getCivilizationType()).isCivilizationFreeTechs(i)):
							break
			else:
				iCount = 0
		for j in range(iCount):
			f.write("\tTech=%s\n" %(gc.getTechInfo(i).getType()))

Edit: Added bold line above and reattached.

I also made a minor change to this code:

Code:
if (not gc.getTeam(iTeamLoop).isHuman()):
	if (gc.getTeam(iTeamLoop).isAlive()):
		for [B]iTech[/B] in [B]aiAIFreeTechs[/B]:
			gc.getTeam(iTeamLoop).setHasTech([B]iTech[/B], true, PlayerTypes.NO_PLAYER, false, false)
else:
	if (gc.getTeam(iTeamLoop).isAlive()):
		for [B]iTech[/B] in [B]aiPlayerFreeTechs[/B]:
			gc.getTeam(iTeamLoop).setHasTech([B]iTech[/B], true, PlayerTypes.NO_PLAYER, false, false)

If you're doing something to each item in a list, you can iterate the list items directly without using an index.

I haven't tested the attached file as it's pretty late (early). :D
 
I knew I should have given it a quick test. I left in "self" as a parameter to the writeFile() calls. :blush:

I'm updating the original attachment.
 
"Did I rush it? I felt like I rushed it." :(

Wow, this evolves into a great learning experience for me -- cool :cool: (well, kind of :blush:).
Thanks rolo for your thoughts and thanks a lot EF for spotting potential problems.
  • Aha, I didn't know that isHasTech fails for isRepeat-techs and was too focussed on the 4000 BC situation (thousands of years before FutureTech...).
  • continue<-->break = :hammer2: although it didn't really break anything unless FutureTech is modded to be a civ's starting tech
  • for i in aiSomething = better style for sure!
  • for-loop - break - else construct :eek: wicked-cool indeed, starting to like Python more and more
  • isEverAlive - would this also give me the 2 civ starting techs of a dead team mate? isAlive is used in CvGame::initFreeState() which I more or less copied
I also learned that on difficulties < Noble *ALL* teams get the free techs (not just the human player as I've always assumed, this means on Settler the barbs start with Agri, Mining, TW too). So this required another change :mischief:. I've also shifted the handling of the 2 civ starting techs to the block in applyInitialItems because IMHO it makes more sense and might be advantageous to group it with the handling of the other freebie techs. Thus the normal saving of non-templates remains untouched and completely tech-free templates are created now. In order to consider rolo's concerns it would be great if the execution of this freebie-adding code could be restricted to the reading of template files (working on this). Or even cooler, upon loading a scenario have a popup ask the player whether he wants to use the file "as is" or to apply the adjustments according to the chosen difficulty (I'm afraid right now this goes slightly over my python head; popups <--> contexts <--> events :confused:).

Further help + ideas + suggestions (+ demonstrations that I'm a Python greenhorn) are greatly appreciated!!!

Improved version reattached to OP.
 
"Did I rush it? I felt like I rushed it." :(

No way! The sooner you get it out--even a version with some bugs you'll arguably never hit anyway--the sooner we can get it to a 100% complete state. That's why I like when people use BUG directly from our SVN. Sure, we let the occasional bug slip through, but I think everyone wins in the end.

Aha, I didn't know that isHasTech fails for isRepeat-techs and was too focussed on the 4000 BC situation (thousands of years before FutureTech...).

That's an assumption on my part from reading the original code. The block that loops over getTechCount() is in the else-block of the if-test for isHasTech().

for i in aiSomething

Just to be clear, the i's there will not be the index into the list but the elements themselves. You can use this for anything that can act like an iterator (tuple, list, set, dict, string).

isEverAlive - would this also give me the 2 civ starting techs of a dead team mate? isAlive is used in CvGame::initFreeState() which I more or less copied

My thinking was that I wanted to make sure that if someone wanted to use a 1500AD save to make a template (is there a good reason for this?) that any Civs that were killed off up to that point still got their free techs at the beginning. Now that you've moved the free tech stuff to the part where the template is "instantiated" into a new game, the point is moot. I like your new solution better, too.

In fact, I'm wondering if the loading code could be changed to use a non-template WB save (no need for template). The writing code removes all units for the AIs and all techs. Why not just do that when the WB is loaded if in "new from template" mode? Of course, we gotta figure out how to initiate a different mode, and we may be stuck because I don't think the main menu is moddable. :hmm:
 
I was thinking of creating a multi-player scenario for either NC or a new series, so am very happy about this mod. However, since this Python code goes into Custom Assets, does it interfere with the "lock modified assets" feature used in the HOF? Does that mean I need to insert/remove it depending on what sort of game I'm playing?

I have to admit I've not been using this a whole lot. I can *almost* equal the speed with find/replace, but more importantly I typically change other things in the WB saves, so I'd be in the text edit area anyway.
 
Hey, I would use this were I not lazy, but is it possible you tell me how to traditionally change speeds? Would be appreciated. I imagine this is awesome for the active and decisive people though, and will help us in teh LHCs.
 
Top Bottom