Modder's Guide to Final Frontier Plus

PsiCorps, I've posted an update in the development thread that handles unique units via the mapscripts rather than here and uses <FreeUnitClasses> (I could have also deleted this code- didn't think to do that). So if you want, you could do the following:

-Fill out <FreeUnitClasses> again in CIV4CivilizationInfos.xml (assuming you haven't deleted it- I just want to make sure you haven't...)

-Download that update, called "FFMapscriptCore".

-In that update, delete the CIV4CivilizationInfos.xml file (I just wanted to be sure that <FreeUnitClasses> was filled out so I provided it)

-Copy over the other files (should be one new Python file and versions of all the FF+ mapscripts- if you modified the mapscripts, back them up).

-In that chunk of code you posted before, comment out the block under the comment. In short, make it look like this:

Code:
				# Check the player for unique units, swapping out the starting ship(s) for the player's UU if appropriate
		#		pCivilization = gc.getCivilizationInfo(pPlayer.getCivilizationType())
		#		pyPlayer = PyPlayer(iPlayerLoop)
		#		apUnitList = pyPlayer.getUnitList()
		#		for pUnitLoop in apUnitList :
		#			iUnitType = pUnitLoop.getUnitType() # this unit's unit type
		#			iUnitClass = pUnitLoop.getUnitClassType() # this unit's unit class
		#			iCivUnitType = pCivilization.getCivilizationUnits(iUnitClass) # this civ's unit type of this class
		#			if (iUnitType != iCivUnitType ):
						# existing unit's type does not match this civ's unit for the unit's class
		#				printd("Found unit to swap: id = %d, from type = %d to type = %d" %(pUnitLoop.getID(), iUnitType, iCivUnitType))
						# add new unit
		#				pNewUnit = pPlayer.initUnit(iCivUnitType, pCity.getX(), pCity.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.NO_DIRECTION)
						# remove old unit
		#				pUnitLoop.kill(false, -1)
		
				# Set up Player stuff: Star Systems & Gold		
				self.doBeginTurnAI(iPlayerLoop, false)

Then try running a game.

You should probably back things up before trying that, though.
 
Hi TC01, i followed the instructions above but the game fails to initialize when i start it. Really don't know what to try now.

First- did you delete your copy of CIV4CivilizationInfos.xml or the one in the update?

Did you install the update before deleting that file?

Do you have backups?

Did you modify the mapscripts in B5?

I'm really not sure why it would cause an intialization failure- unless you handled the CIV4CivilizationInfos.xml incorrectly.
 
did you delete your copy of CIV4CivilizationInfos.xml or the one in the update?
I didn't delete it but i didn't use it, i compared it to what i have and all the same tags are there so i kept the B5 one, which has a unique starting unit....not the Planetary Defense unit.

Did you install the update before deleting that file?
I manually placed the files into a copied version of the mod and so avoided placing the files incorrectly.

Do you have backups?
yes.

Did you modify the mapscripts in B5?
I don't recall making any changes to the map scripts. I'll replace the map scripts currently in the mod with standard FF+ map scripts and see if that makes a difference tonight when i get home from work.

I have had a look at the FFMapScriptCore.py file and noticed that you have
Code:
iNumUnits = pCiv.getCivilizationFreeUnitsClass(iUnitClass)
shouldn't this match the text in CIV4CivilizationInfos.xml i.e.
Code:
iNumUnits = pCiv.getCivilizationFreeUnitClasses(iUnitClass)
?
 
Not according to the Python API.

Well, when you have a chance, try with the standard FF+ mapscripts. If they don't work, then I really have no clue what the problem is.

Except... you said you hadn't touched the mapscripts, and that B5 has a unique starting unit? That means the old mapscripts were still trying to place a Planetary Defense Ship. Now, if you've formatted the <FreeUnitClasses> tags correctly, the game should now be trying to place your custom unique starting unit. Not sure how that would make a difference.

Oh, what exact error do you get? Just a crash when loading the game?
 
Well, when you have a chance, try with the standard FF+ mapscripts.
Just tried that and it's still a no go.
Except... you said you hadn't touched the mapscripts, and that B5 has a unique starting unit? That means the old mapscripts were still trying to place a Planetary Defense Ship. Now, if you've formatted the <FreeUnitClasses> tags correctly, the game should now be trying to place your custom unique starting unit. Not sure how that would make a difference.
Yeah, totally forgot about that minor change but even checking that is correct it still doesn't load up. I was thrilled when i got the mod to load with no XML errors but now there's a problem i don't know how to investigate as there is no error message and no clue, that i can see, as to where to start investigating other than it is on initialisation.

However, i've sent you and God_Emperor a PM which i hope isn't an imposition.
 
Got a worldbuilder issue. I tried to make a new scenario for Star Trek with the new worldbuilder code, but when I tried to open the map, I got "you have been defeated" along with this python exception:

Traceback (most recent call last):

File "CvWBInterface", line 47, in applyInitialItems

File "CvWBDesc", line 2118, in applyInitialItems

File "CvWBDesc", line 1474, in applyCity

File "CvWBDesc", line 998, in apply

File "CvSolarSystem", line 156, in getPlanetByIndex

IndexError: list index out of range
ERR: Python function applyInitialItems failed, module CvWBInterface
 
I just looked at CvWBDesc.py and it looks like there is a bug in it on line 998.

The area around that line is this:
Code:
		if not iNumPlanets <= 7:
			for planet8Bldg in (self.planet8BldgType):
				iBuilding = gc.getInfoTypeForString(planet8Bldg)
				pSystem.getPlanetByIndex(7).setHasBuilding(iBuilding, true)
		if not iNumPlanets <= 6:
			for planet7Bldg in (self.planet7BldgType):
				iBuilding = gc.getInfoTypeForString(planet7Bldg)
				[B]pSystem.getPlanetByIndex([COLOR="DarkRed"]8[/COLOR]).setHasBuilding(iBuilding, true)[/B]
		if not iNumPlanets <= 5:
			for planet6Bldg in (self.planet6BldgType):
				iBuilding = gc.getInfoTypeForString(planet6Bldg)
				pSystem.getPlanetByIndex(5).setHasBuilding(iBuilding, true)

The 8 being passed to getPlanetByIndex should be a 6.
 
Looks like I broke the barbarian code when I merged this into Star Trek. I was seeing barbarian probes in the very limited testing I did (even though probes shouldn't have been able to spawn, based on the XML) and now I'm getting reports of barb Light I/II when the AI only has Warp 7 Ships.

I play on Lt Commander, mainly because I enjoy playing the game rather than rushing to win (or lose!) it.

I do enjoy having barbarians in all my games. It adds the spice of combat and uncertainty without needing to be at war with neighbouring civilisations, but while in normal Civ the barbarians are manageable by the AI civs in Star Trek they run wild and knock out the races quickly.

It's quite scary to open the worldbuilder and see the map swarming with hostile ships and this is without having Aggressive ticked or the challenge of cities for them as well!!

They seem to improve their ships so quickly as well. Often having Light I-II while the AI races are still with warp 7 ships only.

It's sad because I have to play without the barbarians or find that half of the civs the game starts with are eliminated within 200 turns or so.

With so much fog of war I can't really see a good way to keep them and avoid this problem, perhaps limiting them to Warp 7 ships at best or giving everyone else's ships say a 50% bonus against them?



Regarding the Borg, I absolutely agree that from a realism point of view the Borg gain knowledge by assimilation (stated quite clearly in the Voyager episode "Scorpion") but I also recall that they do NOT assimilate inferior races/ships (Seven of Nine commenting that the Kazon were ignored for being "unworthy of assimilation")which would not add to their "perfection" so would it be possible to configure it so the AI recognises which civs are technologically inferior and leave them alone until they become more "interesting" (or of course attack them!) ?

Marla x
 
I am assuming you updated your DLL with the changes in the last patch, as the unpatched version has a barbarian horde problem that is pretty easy to notice.

For the number of units, have you checked your XML to see how many barbarians there should be?

In CIV4HandicapInfo.xml for each difficulty level there is a value called iUnownedTilesPerBarbarianUnit. A higher value results in fewer pirates. Doubling the value cuts the number of pirate ships in half (on average). The values for this in regular FFP (v1.651) are 420 for Settler, 360 for Chieftain, 300 for Warlord, 240 for Noble, 200 for Prince, 160 for Monarch, 130 for Emperor, 120 for Immortal, and 110 for Deity. Immortal produces twice the pirates that Noble has (since the value is half as much), and Chieftan has 2/3 as many as Noble (since the value is 3/2 as much). Since this is tied to the amount of unowned map tiles, big maps will produce more in total especially if they don't have a lot of civilizations (should result in a few more in the early game) or if there are not very many star systems (resulting in more in the late game).

Note that these iUnownedTilesPerBarbarianUnit numbers changed in 1.651 to be twice what they used to be, so if you are still using the old numbers the number of barbarian units will be double what we would get in FFP for the same map at the same difficulty. In conjunction with changing the formula in the DLL for how the exact number is determined this should result in a number of them in the first era that is "good", increasing a bit with each era unless the players expand their territory rapidly. Exactly how many you want is a matter of opinion, so some tweaking of this set of values may be needed. If you have a lot of eras that pass quickly then the number will escalate since there is an era related multiplier which starts at 2 in the first era and increases by 1 per era after that so if the players owned no additional tiles there would be 50% more in the second era - but since the players will normally control an increasing number of tiles this counteracts the per-era multiplier to some extent. The expected behavior is that in each era the number of barb units gradually decreases due to the number of owned tiles increasing, then the number jumps up some when the average era advances.

As for which units are showing up, that is controlled by the MinBarbarianSpawnEra and MaxBarbarianSpawnEra tags in the CIV4UnitInfos.xml file. This "era" for this is not the player's era, but the average for all civs in the game. Unless everybody miraculously advances in lockstep, about half of the civs will be an era behind the rest when the change in the barbarian unit selection changes. If you have some run-away tech leaders who are two eras or more ahead of some of the others, or some that are falling way behind the rest, then it can get even worse for the ones who lag behind.
 
I don't think I ever made the change that caused that bug in the first place. I'm still on this line: int iTarget = ((pLoopArea->getNumUnownedTiles() / iDivisor) - iNumBarbs ) * ((int)GC.getGame().getCurrentEra()
I can adjust the values, but unlike Final Frontier Plus, I have barb animals/cities, so I didn't want to mess with those values (and I'm actually not sure if the animals even spawn correctly, since they come in the later eras). Probes don't have a min or max era set at all; does that allow them to spawn the entire game?
 
I don't think I ever made the change that caused that bug in the first place. I'm still on this line: int iTarget = ((pLoopArea->getNumUnownedTiles() / iDivisor) - iNumBarbs ) * ((int)GC.getGame().getCurrentEra()
I can adjust the values, but unlike Final Frontier Plus, I have barb animals/cities, so I didn't want to mess with those values (and I'm actually not sure if the animals even spawn correctly, since they come in the later eras). Probes don't have a min or max era set at all; does that allow them to spawn the entire game?

The default for max and min is "NO_ERA", so if you don't set the tag, those are the values it will use. It shouldn't let probes spawn at all.

Part of the barbarian changes I made was commenting out the code to spawn barbarian animals (if I remember correctly). It's at the top of the function to spawn barbarian units.
 
That's entirely possible.

I did say when I posted that file that the code was untested, because of a lack of time on my part.

Is the AI building Sensor Posts and not Starbases, or neither of them?
 
I've been looking into this...

The reason it is not building any bases of any sort is that in CvAI.py and the CvAI class the list self.stations is always empty.

It turns out that it is empty because at the time the CvAI object is initialized, the XML has not been loaded yet (or, at least, not the relevant stuff). The CvAI object is currently being initialized in CvFinalFrontierEvents.py at the module level, up in the code labeled "# globals" just after the "import CvAI". This makes it initialized hte CvAI object pretty early, during the "init Python" phase of the startup.

There should be a simple solution: make 3 small changes to CvFinalFrontierEvents.py
1) change this line
Code:
# globals
gc = CyGlobalContext()
localText = CyTranslator()
[COLOR="DarkRed"][B]AI = CvAI.CvAI()[/B][/COLOR]
to be this:
Code:
# globals
gc = CyGlobalContext()
localText = CyTranslator()
[COLOR="DarkRed"][B]AI = None[/B][/COLOR]
so it is still a global variable, but it is set to be nothing.

2) Add an initialization for this in 2 places, onGameStart and onLoadGame:
onGameStart
Code:
	def onGameStart(self, argsList):
		'Called at the start of the game'
		self.parent.onGameStart(self, argsList)
		
[COLOR="DarkRed"][B]		global AI
		AI = CvAI.CvAI()[/B][/COLOR]
		
		self.iWinningTeam = -1
		self.iTimeLeft = 0
and likewise in onLoadGame
Code:
	def onLoadGame(self, argsList):
		self.parent.onLoadGame(self, argsList)

[COLOR="DarkRed"][B]		global AI
		AI = CvAI.CvAI()[/B][/COLOR]
		
		self.iWinningTeam = -1
		self.iTimeLeft = 0

I did this in the Galaxy scenario (I have been playing it - it is past the end of the tech tree, I have Future Tech 5, and there are a lot of construction ship sitting around doing very little in the other civs' star systems). This lead to the discovery of another minor bug: in a printd statement in the doConstructionShipAI function there is one that has a %d where it should have a %s...
EDIT: I should have mentioned that everything after this is in CvAI.py.
Code:
	printd("Checking whether [COLOR="DarkRed"][B]%s[/B][/COLOR] can build station %d" % (pUnit.getName(), iBuild)) # Fixed a bug - was %d isntead of %s

So now it gets past that. And we come to the next bug. In a call to pPlayerAIInfo.setUnitIDStationConstructor( A, B), the two arguments are swapped. My corrected version says
Code:
				pPlayerAIInfo.setUnitIDStationConstructor([COLOR="DarkRed"][B]iBuild, pUnit.getID()[/B][/COLOR]) # Fixed a bug - the arguments were reversed

Getting near the end of the function and... another bug. A simple typo - a function name is missing the letter "s", corrected like so
Code:
			if (pPlayerAIInfo.getUnitIDStationCon[COLOR="DarkRed"][B]s[/B][/COLOR]tructor(iBuild) == pUnit.getID()): # Fixed a bug - "Constructor" was missing its "s"

On pretty much the last possible line of that function that can have a bug. No bug there, but the function it calls has one. In doStationConstructionAI the very first line of code tries to use iStation, but the function calls it iBuild. Fixed via
Code:
		pBuildInfo = gc.getBuildInfo([COLOR="DarkRed"][B]iBuild[/B][/COLOR]) # bug fix - was using the non-existent "iStation" instead of iBuild

It then proceeds to the canConstructSensorStation where it tries to use a on-existant variable called iPlayer. Fixed via
Code:
		pBestPlot, iBestValue = self.findBestChokepoint([COLOR="DarkRed"][B]pUnit.getOwner()[/B][/COLOR], true) # bug fix - was trying to use non-existent iPlayer
and while I was at it, before launching the game again and testing that fix, I fixed the matching bug in the canConstructStarbase function:
Code:
		pBestPlot, iBestValue = self.findBestResourcePlot([COLOR="DarkRed"][B]pUnit.getOwner()[/B][/COLOR], true) # bug fix - was trying to use non-existent iPlayer
But we are not done yet. In findBestChokepoint it ends up trying to getFeatureInfo for a non-existent (-1) feature type when it is looking at plosts that don't have a feature.
I fixed this one with this
Code:
						elif [COLOR="DarkRed"][B](iFeature != -1) and [/B][/COLOR](gc.getFeatureInfo(iFeature).getTurnDamage() > 0): # bug fix - make sure there is a feature before trying to get the info for it, taking advantage of the short-circuit conditional evaluation

At this point, every civ sent out constructions ship to build a starbase and a sensor station. From one turns of processing, there were 65 construction ships that checked the AI and 18 that got the override (2 from each of the 9 AI civs that are still alive).

EDIT: I forgot to mention that I have tested it for exactly one turn. That was enough to get the AIs to send construction ships to build things, but they have not arrived and tried to actually build them yet. So the stuff that runs after the construction ship gets to the desired location has not been tested (yet).
 
OK, I ran some turns and discovered that there are some flaws, but it is having units build starbases and sensor stations.

One flaw is that for some reason the Dominion is trying to build both a starbase and a sensor station at the same spot (or maybe it is trying to use a group of 2 to build one of them instead of a lone construction ship - I didn't think of this possibility until just now, so I didn't check, but I think the log indicated one was a SB build at the location and the other a sensor station at the same coordinates). The "I'm here, now what do I do" code does not tell the unit to do anything (it exits without overriding) for some reason. Sadly, it really does do nothing to the unit, so it still is the designated station builder for that station type and it still has not received any orders so the DLL just tries again another 99 times where it runs the same "best spot" check and always gets the same answer (it's the same principle as a bug I squashed in the original SB code that didn't check to see if the plot it was trying to build on was owned by somebody else). This can probably be fixed by adding a check for a unit that is already building a base to the code that rates the locations, which would force it to pick a different spot. It even already has a unit checking loop, but it is only checking for finished bases and not construction ships building a base. As it is currently, I see a massive slowdown in my game as it repeats the entire "evaluate all plots to find best plot, then try to build there" process 100 times for the second ship. Fortunately this is a temporary condition - even without a fix it will stop happening the instant the other base is completed at the location since it will no longer be rated as the best location.

The second flaw is in the location system. The location with the two ships competing to build at is not a good location for either type of base, as you can see here:
attachment.php

There are no resources, wormholes, or planets for a starbase to be claiming/protecting.

For the sensor station evaluation, I'm guessing that it considers this to be an excellent choke point since it has lots of restrictions on movement... I'm thinking there needs to be an upper limit on the number of impassable plots adjacent to the location (once you hit 7, it is not a choke point it is a dead-end).

I have no idea why it is apparently trying to build a starbase there.

So with the fixes I posted it is working, but not well. More testing is needed to determine what, other than the looping fix, needs to be done to the location selection algorithms.

I think there is some fundamental logic problem with the thing, but have not figured it out yet.

EDIT: Having reviewed the map in debug/reveal mode I can see that everybody is sending construction ships to the best hole in the nebula that they know about. So far it is only the Dominion that has actually reached their location, but if the game was played some more they would all be doing the same sort of thing.
 

Attachments

  • SBLocationLogicFlaw.jpg
    SBLocationLogicFlaw.jpg
    36 KB · Views: 241
I just noticed that I posted a couple of things over in the regular bug thread that were supposed to go here.

The fix for sending construction ships for both starbases and sensor stations to the same place is in post 112 over there. Basically, due to a bug it was using the (bad) sensor station positioning algorithm for both types.

An improved "choke point locator" used to position the sensor stations is also over there in post 117.

I have not tested this sensor station positioning a lot, but my impression is that it is a lot better than the original but still not very good, at least for the map in the Galaxy scenario and probably any spiral galaxy map - the large expanses of nebulae with jagged edges are just very attractive to it, and the bulk of damaging terrain is in the center of the map intermixed with lots of asteroid fields. The new algorithm gives high ratings to plots near a fairly large amount of nebula but not in a small dead-end area, and/or near a fair number of damaging terrain plots. It also likes to have a few asteroid plots around, especially if it is on top of one, but not very many. It also likes to put them a ways away from the capital star system. The sliding weighting system should also be helpful since sensor stations are basically sitting ducks and should die a lot since they are 0 strength and unprotected by the AI so you don't really want them building a lot of them. About half the sensor station locations it picked that I checked were pretty bad and most of the rest were so-so at best.

Currently it tries to avoid building sensor stations near the capital (there is a large penalty to the rating applied for plots within 3 and it doesn't hit it's maximum range related bonus until plots that are 13 away on the Galaxy map - there is pretty much no way it will build them there, even if the other conditions are prefect for a maximum possible rating, due to the applied threshold), or within 2 of other star systems. Since sensor stations can detect cloaked ships in ST, I'm thinking that this penalty may be a bad idea - a more complicated check that gives a bonus instead of a penalty if there is no other sensor station near the capital or other star system might be a good idea.
 
PsiCorps, I've posted an update in the development thread that handles unique units via the mapscripts rather than here and uses <FreeUnitClasses> (I could have also deleted this code- didn't think to do that). So if you want, you could do the following:

-Fill out <FreeUnitClasses> again in CIV4CivilizationInfos.xml (assuming you haven't deleted it- I just want to make sure you haven't...)

-Download that update, called "FFMapscriptCore".

-In that update, delete the CIV4CivilizationInfos.xml file (I just wanted to be sure that <FreeUnitClasses> was filled out so I provided it)

-Copy over the other files (should be one new Python file and versions of all the FF+ mapscripts- if you modified the mapscripts, back them up).

-In that chunk of code you posted before, comment out the block under the comment. In short, make it look like this:

Code:
				# Check the player for unique units, swapping out the starting ship(s) for the player's UU if appropriate
		#		pCivilization = gc.getCivilizationInfo(pPlayer.getCivilizationType())
		#		pyPlayer = PyPlayer(iPlayerLoop)
		#		apUnitList = pyPlayer.getUnitList()
		#		for pUnitLoop in apUnitList :
		#			iUnitType = pUnitLoop.getUnitType() # this unit's unit type
		#			iUnitClass = pUnitLoop.getUnitClassType() # this unit's unit class
		#			iCivUnitType = pCivilization.getCivilizationUnits(iUnitClass) # this civ's unit type of this class
		#			if (iUnitType != iCivUnitType ):
						# existing unit's type does not match this civ's unit for the unit's class
		#				printd("Found unit to swap: id = %d, from type = %d to type = %d" %(pUnitLoop.getID(), iUnitType, iCivUnitType))
						# add new unit
		#				pNewUnit = pPlayer.initUnit(iCivUnitType, pCity.getX(), pCity.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.NO_DIRECTION)
						# remove old unit
		#				pUnitLoop.kill(false, -1)
		
				# Set up Player stuff: Star Systems & Gold		
				self.doBeginTurnAI(iPlayerLoop, false)

Then try running a game.

You should probably back things up before trying that, though.
Hi TC01, i have finally managed to resolve the issues i was having with this and the game now loads and runs just as i want it to with the Orbital Defense Satellite as the starting unit. However in fixing the issue i had with this first time around the different Civs are now unable to build the O.D.S. (or it's later variations). They appear in the Civilopedia as i expect them too but on starting up the game there is no option to build them. There are no prereq techs or resources needed to build them so why they cannot be built is a mystery to me. However, i think it may be to do with the fact that i have not based the O.D.S. off of an existing unit. It is a new unit to FF but should be available to all Civs, much the same as the CommRelay/Sensor Station that was brought in with FF+, is there anything else i need to do to introduce a new unit into the game besides the UnitInfo, UnitClass and ArtDefines_Unit XML entries?
 
Back
Top Bottom