Feedback, ideas, and brainstorming pls - Lurchuip Scenario

Bad Player

Deity
Joined
Oct 31, 2005
Messages
3,534
Location
(Bris)Vegas!
Phase 1: Ideas

Plot:
The Good nations are allied in war against the evil nations (whom may not be allied together). The evil nations are causing the Armageddon counter to rise. The Lurchuip are led by someone who is trying to take over the world but pretending to be on the side of the good guys (you are not told this at the start of the scenario). The Lurchuip are therefore secretly and treacherously helping the evil nations (through mechanisms discussed below) and may declare war on the good civs if they become powerful enough (via mechanisms discussed below).

You play as the Elohim and you win the scenario if the AC reaches 0. You lose the scenario if you die or another nation wins a conquest/domination victory.


Mechanics of the Scenario:
  • The Lurchuip will declare war on all good civs if the sum of good civs' scores are < 80% of the Lurchuip score
  • If the Lurchuip declare war on the good civs then give all their golems the plague bearer promotion
  • The Lurchuip have the Runes of Kilmorph (RoK) holy shrine in their capital and mithril adjacent to their capital so that they can build the mithril golem
  • Possibly use the "Last Days" setting (to double AC effects)
  • The good civs have a permanent peace treaty so that you can't declare war on the Lurchuip (however they might declare war on you and the other good civs)
  • The Sheiam should have a protected position on one side of the map and have the Celestial Compass built in their capital
  • The Lurchuip should have a protected starting position so that the evil civs can't destroy them easily
  • The Elohim should be on the front line so that they have to choose between fighting and producing their ritual to lower the AC (makes it harder for the player)
  • The Clan of Embers should have the Prophecy of Ragnarok so that the AC is pushed up from their numerous units
  • The Lurchuip help the evil civs in the following ways:
    - Have a held/move 0 adept or mage (whichever has a lower defending priority in a stack) in each starting good city, ostensibly to repair catapults etc in the city. However the Lurchuip give their world map to the Sheiam automatically every 5 turns.​
    - The Lurchuip announce to the good players (ie you the Elohim) that they are building Bane Divine ritual to kill the evil civs' ritualists and profanes and that all the good civs should attack evil cities with armies in 10 turns (the good civs would have to be programmed to try to attack on that turn). However, the turn after Bane Divine is cast, all living units are diseased unexpectedly (which means that the evil civs will have an advantage striking down the good armies in evil territory and there are no priests to cure the armies of good - a cunning Lurchuip plot!).​


Discussion of my Ideas:
I want the scenario to be interesting (I think the good leader betraying everyone and trying to take over the world with a golem army is interesting IMHO) but without complex coding.



Phase 2: Coding


Phase 3: Map design




Some videos of me playing the scenario (showcasing features of it - there are some other things that happen as part of the scenario plot...) - I figured I'd post them because people are still around the forums

Loading the scenario:

Link to video.


There are arsonists trying to sabotage the Ljosalfar - presumably their evil neighbours the Svartalfar... Or is it?

Link to video.



An important part of the game is protecting yourself and your allies from the effects of a rising Armageddon counter (AC). The AC rises partly randomly depending on passive factors such as the number of evils civs in the game and other similar reasons.

Link to video.


Armageddon will be very bad for you! Do everything you can to prevent it! Seems like only the Sheaim and the Luchuirp are doing well with a rising AC. It must be the evil Sheaim trying to destroy the world...

Link to video.


The Luchuirp have suggested creating a massive Stack of Doom (SoD) and marching deep into the evil lands. Perhaps taking down the evil Sheaim will cripple the evil. During a certain time period all NEW good civ units are forcibly marched to a marshalling tile on the map and on a certain turn the SoD starts marching to the Sheaim lands.

Link to video.

Link to video.


Plot spoiler:

Spoiler :
In many (not necessarily all!) of your games, the Luchuirp will become powerful enough that they reveal their true colours and declare war on the good civs!


Link to video.
 
Phase 2: Coding

To be coded:
  • Start of game popup to explain the scenario
    - def onGameStart in CvEventManager.py
    - sf.addPopupWB(CyTranslator().getText("TXT_KEY_WB_ELOHIM_SCENARIO_INTRO",()), 'art/interface/popups/Blood of Angels.dds')
    - Add the story text to CIV4GameText_FFH2.xml
    - See Kael's post on how to add .DDS art from a certain location
  • How to start the AC at e.g. 10
    - In CvEventManager.py at the section def onGameStart(self, argsList):, use CyGame().changeGlobalCounter(10)
  • Sheiam to prioritise their ritual
    - In the file Civ4ProjectInfo.xml, change the value of PROJECT_ELEGY_OF_THE_SHEAIM <iAIWeight>0</iAIWeight> to e.g. 500 (maximum is something like 2147483647)
  • Allow Infernals but discourage Mercurians by making the Mercurian Gate cost 3000 instead of 600.

  • All good civs know eachother and all evil civs know eachother and make good and evil civs at war with eachother

    Code:
    		#Find and set the Team (i.e. player) ID numbers for each civ in the game (could be done manually since it is a scenario with fixed civs)
    		for iGoodAndEvilPlayer in range(gc.getMAX_PLAYERS()):
    			pGoodAndEvilPlayer = gc.getPlayer(iGoodAndEvilPlayer)
    			if pGoodAndEvilPlayer.isAlive():
    				if pGoodAndEvilPlayer.getCivilizationType() == gc.getInfoTypeForString('CIVILIZATION_BANNOR'):
    					iBannorTeamID = gc.getPlayer(iGoodAndEvilPlayer).getTeam()
    				if pGoodAndEvilPlayer.getCivilizationType() == gc.getInfoTypeForString('CIVILIZATION_CALABIM'):
    					iCalabimTeamID = gc.getPlayer(iGoodAndEvilPlayer).getTeam()
    				if pGoodAndEvilPlayer.getCivilizationType() == gc.getInfoTypeForString('CIVILIZATION_CLAN_OF_EMBERS'):
    					iClanOfEmbersTeamID = gc.getPlayer(iGoodAndEvilPlayer).getTeam()
    				if pGoodAndEvilPlayer.getCivilizationType() == gc.getInfoTypeForString('CIVILIZATION_DOVIELLO'):
    					iDovielloTeamID = gc.getPlayer(iGoodAndEvilPlayer).getTeam()
    				if pGoodAndEvilPlayer.getCivilizationType() == gc.getInfoTypeForString('CIVILIZATION_ELOHIM'):
    					iElohimTeamID = gc.getPlayer(iGoodAndEvilPlayer).getTeam()
    				if pGoodAndEvilPlayer.getCivilizationType() == gc.getInfoTypeForString('CIVILIZATION_KURIOTATES'):
    					iKuriotatesTeamID = gc.getPlayer(iGoodAndEvilPlayer).getTeam()
    				if pGoodAndEvilPlayer.getCivilizationType() == gc.getInfoTypeForString('CIVILIZATION_LJOSALFAR'):
    					iLjosalfarTeamID = gc.getPlayer(iGoodAndEvilPlayer).getTeam()
    				if pGoodAndEvilPlayer.getCivilizationType() == gc.getInfoTypeForString('CIVILIZATION_LUCHUIRP'):
    					iLuchuirpTeamID = gc.getPlayer(iGoodAndEvilPlayer).getTeam()
    				if pGoodAndEvilPlayer.getCivilizationType() == gc.getInfoTypeForString('CIVILIZATION_SHEAIM'):
    					iSheaimTeamID = gc.getPlayer(iGoodAndEvilPlayer).getTeam()
    				if pGoodAndEvilPlayer.getCivilizationType() == gc.getInfoTypeForString('CIVILIZATION_SVARTALFAR'):
    					iSvartalfarTeamID = gc.getPlayer(iGoodAndEvilPlayer).getTeam()
    
    
    		
    		for iWarAndPeaceTeam in range(gc.getMAX_CIV_TEAMS()):
    			pWarAndPeaceTeam = gc.getTeam(iWarAndPeaceTeam)
    			if pWarAndPeaceTeam.isAlive():
    				#Make good civs meet other good civs
    				if iWarAndPeaceTeam == iBannorTeamID:
    					pWarAndPeaceTeam.meet(iElohimTeamID, False)
    					pWarAndPeaceTeam.meet(iKuriotatesTeamID, False)
    					pWarAndPeaceTeam.meet(iLjosalfarTeamID, False)
    					pWarAndPeaceTeam.meet(iLuchuirpTeamID, False)
    				if iWarAndPeaceTeam == iElohimTeamID:
    					pWarAndPeaceTeam.meet(iKuriotatesTeamID, False)
    					pWarAndPeaceTeam.meet(iLjosalfarTeamID, False)
    					pWarAndPeaceTeam.meet(iLuchuirpTeamID, False)
    				if iWarAndPeaceTeam == iKuriotatesTeamID:
    					pWarAndPeaceTeam.meet(iLjosalfarTeamID, False)
    					pWarAndPeaceTeam.meet(iLuchuirpTeamID, False)
    				if iWarAndPeaceTeam == iLjosalfarTeamID:
    					pWarAndPeaceTeam.meet(iLuchuirpTeamID, False)
    				#Make evil civs meet other evil civs
    				if iWarAndPeaceTeam == iCalabimTeamID:
    					pWarAndPeaceTeam.meet(iClanOfEmbersTeamID, False)
    					pWarAndPeaceTeam.meet(iDovielloTeamID, False)
    					pWarAndPeaceTeam.meet(iSheaimTeamID, False)
    					pWarAndPeaceTeam.meet(iSvartalfarTeamID, False)
    				if iWarAndPeaceTeam == iClanOfEmbersTeamID:
    					pWarAndPeaceTeam.meet(iDovielloTeamID, False)
    					pWarAndPeaceTeam.meet(iSheaimTeamID, False)
    					pWarAndPeaceTeam.meet(iSvartalfarTeamID, False)
    				if iWarAndPeaceTeam == iDovielloTeamID:
    					pWarAndPeaceTeam.meet(iSheaimTeamID, False)
    					pWarAndPeaceTeam.meet(iSvartalfarTeamID, False)
    				if iWarAndPeaceTeam == iSheaimTeamID:
    					pWarAndPeaceTeam.meet(iSvartalfarTeamID, False)			
    				#This section makes the evils civs at war with the good civs 
    				if iWarAndPeaceTeam == iCalabimTeamID:
    					pWarAndPeaceTeam.declareWar(iBannorTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iBannorTeamID, False)
    					pWarAndPeaceTeam.declareWar(iElohimTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iElohimTeamID, False)
    					pWarAndPeaceTeam.declareWar(iKuriotatesTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iKuriotatesTeamID, False)
    					pWarAndPeaceTeam.declareWar(iLjosalfarTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iLjosalfarTeamID, False)
    					pWarAndPeaceTeam.declareWar(iLuchuirpTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iLuchuirpTeamID, False)
    				if iWarAndPeaceTeam == iClanOfEmbersTeamID:
    					pWarAndPeaceTeam.declareWar(iBannorTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iBannorTeamID, False)
    					pWarAndPeaceTeam.declareWar(iElohimTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iElohimTeamID, False)
    					pWarAndPeaceTeam.declareWar(iKuriotatesTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iKuriotatesTeamID, False)
    					pWarAndPeaceTeam.declareWar(iLjosalfarTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iLjosalfarTeamID, False)
    					pWarAndPeaceTeam.declareWar(iLuchuirpTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iLuchuirpTeamID, False)
    				if iWarAndPeaceTeam == iDovielloTeamID:
    					pWarAndPeaceTeam.declareWar(iBannorTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iBannorTeamID, False)
    					pWarAndPeaceTeam.declareWar(iElohimTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iElohimTeamID, False)
    					pWarAndPeaceTeam.declareWar(iKuriotatesTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iKuriotatesTeamID, False)
    					pWarAndPeaceTeam.declareWar(iLjosalfarTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iLjosalfarTeamID, False)
    					pWarAndPeaceTeam.declareWar(iLuchuirpTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iLuchuirpTeamID, False)
    				if iWarAndPeaceTeam == iSheaimTeamID:
    					pWarAndPeaceTeam.declareWar(iBannorTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iBannorTeamID, False)
    					pWarAndPeaceTeam.declareWar(iElohimTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iElohimTeamID, False)
    					pWarAndPeaceTeam.declareWar(iKuriotatesTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iKuriotatesTeamID, False)
    					pWarAndPeaceTeam.declareWar(iLjosalfarTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iLjosalfarTeamID, False)
    					pWarAndPeaceTeam.declareWar(iLuchuirpTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iLuchuirpTeamID, False)
    				if iWarAndPeaceTeam == iSvartalfarTeamID:
    					pWarAndPeaceTeam.declareWar(iBannorTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iBannorTeamID, False)
    					pWarAndPeaceTeam.declareWar(iElohimTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iElohimTeamID, False)
    					pWarAndPeaceTeam.declareWar(iKuriotatesTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iKuriotatesTeamID, False)
    					pWarAndPeaceTeam.declareWar(iLjosalfarTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iLjosalfarTeamID, False)
    					pWarAndPeaceTeam.declareWar(iLuchuirpTeamID, False, WarPlanTypes.WARPLAN_TOTAL)
    					pWarAndPeaceTeam.setPermanentWarPeace(iLuchuirpTeamID, False)
    				#This makes good civs be at peace with good civs
    				if iWarAndPeaceTeam == iBannorTeamID:
    					pWarAndPeaceTeam.setPermanentWarPeace(iElohimTeamID, True)
    					pWarAndPeaceTeam.setPermanentWarPeace(iKuriotatesTeamID, True)
    					pWarAndPeaceTeam.setPermanentWarPeace(iLjosalfarTeamID, True)
    					pWarAndPeaceTeam.setPermanentWarPeace(iLuchuirpTeamID, True)
    				if iWarAndPeaceTeam == iElohimTeamID:
    					pWarAndPeaceTeam.setPermanentWarPeace(iKuriotatesTeamID, True)
    					pWarAndPeaceTeam.setPermanentWarPeace(iLjosalfarTeamID, True)
    					pWarAndPeaceTeam.setPermanentWarPeace(iLuchuirpTeamID, True)
    				if iWarAndPeaceTeam == iKuriotatesTeamID:
    					pWarAndPeaceTeam.setPermanentWarPeace(iLjosalfarTeamID, True)
    					pWarAndPeaceTeam.setPermanentWarPeace(iLuchuirpTeamID, True)
    				if iWarAndPeaceTeam == iLjosalfarTeamID:
    					pWarAndPeaceTeam.setPermanentWarPeace(iLuchuirpTeamID, True)

  • How to trigger the Lurchuip to declare war (if their score is a certain amount more than other good civs' scores)
    - This code gets the top score of all civs (might not be useful)
    Code:
    		iBestScoreTeam = -1
    		bestScore = 0
    		for iLoopTeam in range(gc.getMAX_CIV_TEAMS()):
    			if (gc.getTeam(iLoopTeam).isAlive() and not gc.getTeam(iLoopTeam).isMinorCiv() and not gc.getTeam(iLoopTeam).isBarbarian()):
    				if (iLoopTeam != iActiveTeam and (activePlayer.getTeam().isHasMet(iLoopTeam) or gc.getGame().isDebugMode())):
    					teamScore = gc.getGame().getTeamScore(iLoopTeam)
    					if (teamScore > bestScore):
    						bestScore = teamScore
    						iBestScoreTeam = iLoopTeam
    - Get the Lurchuip score using iLurchuipScore = gc.getGame().getTeamScore(iLurchuipTeamID)
    - Get the other good civs' scores in the same way then use if iLurchuipScore >= iElohimScore + iKuriotates + iBannor + iLjosalfar:
    - Make a pop-up announcement to explain why the Lurchuip declared war on the good civs
    - use declareWar and setPermanentWarPeace to get rid of their forced peace and make them stay at war (might have to be careful to stop the setPermanentWarPeace - True and make it False and then declareWar???

  • How to give all golems the plague bearer promotion if Lurchuip declare war on good civs

    Code:
    	for iPlayer in range(gc.getMAX_PLAYERS()):
    		player = gc.getPlayer(iPlayer)
    		if player.isAlive():
    			py = PyPlayer(iPlayer)
    			for pUnit in py.getUnitList():
    				if unit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_GOLEM')):
    					Unit.setHasPromotion(gc.getInfoTypeForString('PROMOTION_PLAGUE_CARRIER'), True)
  • How to make the Lurchuip give the Sheiam their world map every 5 turns
  • How to make Lurchuip build Bane Divine starting on a certain turn (in a protected city so it wouldn't get pilllaged early and therefore I know which turn it will be finished)

    Code:
    		#Create a ritual on a particular turn
    		#Push order: 1st BOOL to set it to make it forever; 2nd BOOL to replace current production; 3rd BOOL to put the new production on bottom of the queue; 4th BOOL to force the production even if you can't normally make it
    
    		for iPlayer in range(gc.getMAX_PLAYERS()):
    			pTargetPlayer = gc.getPlayer(iPlayer)
    			if pTargetPlayer.isAlive():
    				if pTargetPlayer.getCivilizationType() == gc.getInfoTypeForString('CIVILIZATION_ILLIANS'):
    					if not pTargetPlayer.isHuman():
    						for pyCity in PyPlayer(iPlayer).getCityList():
    							pTargetCity = pyCity.GetCy()
    							if pTargetCity.isCapital():
    								#if iGameTurn == 3:
    								#Load current (x,y) values of the Stack of Doom and then check if the stack is at the target tile
    								serializedMyPickle = BugData.getTable("MY_TABLE").data
    								myPickle = pickle.loads(serializedMyPickle)
    								iOldX = myPickle[0]
    								iOldY = myPickle[1]
    								iTargetX = 2
    								iTargetY = 10
    								if iOldX == iTargetX and iOldY == iTargetY:						
    									#cf.addUnit(gc.getInfoTypeForString('UNIT_HUNTER'))
    									if pTargetCity.canCreate(gc.getInfoTypeForString('PROJECT_BANE_DIVINE'), True, True):
    										pTargetCity.pushOrder(OrderTypes.ORDER_CREATE, gc.getInfoTypeForString('PROJECT_BANE_DIVINE'), -1, False, True, False, False)
    										pTargetCity.setProduction(10000)
    										return 1



  • How to disease all living units one turn after Bane Divine is built
    - need to trigger the following code 1 turn after Bane Divine
    - In CvEventManager.py, this will trigger diseased promotion for all living units when Bane Divine is built rather than the turn after.
    Code:
    			if iProjectType == gc.getInfoTypeForString('PROJECT_BANE_DIVINE'):
    				iCombatDisciple = gc.getInfoTypeForString('UNITCOMBAT_DISCIPLE')
    				for iLoopPlayer in range(gc.getMAX_PLAYERS()):
    					pLoopPlayer = gc.getPlayer(iLoopPlayer)
    					if pLoopPlayer.isAlive() :
    						apUnitList = PyPlayer(iLoopPlayer).getUnitList()
    						for pUnit in apUnitList:
    							if pUnit.getUnitCombatType() == iCombatDisciple:
    								pUnit.kill(False, pCity.getOwner())
    
    				for iPlayer in range(gc.getMAX_PLAYERS()):
    					player = gc.getPlayer(iPlayer)
    					if player.isAlive():
    						py = PyPlayer(iPlayer)
    						for pUnit in py.getUnitList():
    							if pUnit.isAlive():
    								pUnit.setHasPromotion(gc.getInfoTypeForString('PROMOTION_DISEASED'), True)

  • How to use Last Days setting
    -
    Code:
    BeginGame
    	Era=ERA_CLASSICAL
    	Speed=GAMESPEED_NORMAL
    	Calendar=CALENDAR_DEFAULT
    	Option=GAMEOPTION_DOUBLE_GLOBAL_COUNTER in the scenarioname.CivBeyondSwordWBSave
  • How to make the Elohim player win when AC = 0

    -
    Code:
    		#Makes the human Elohim player win if the AC = 0, #2 is a conquest victory (maybe modify this)
    		iElohimPlayer = 0
    		pElohimPlayer = gc.getPlayer(iElohimPlayer)
    		if CyGame().getGlobalCounter() <= 0:
    			gc.getGame().setWinner(pElohimPlayer.getTeam(), 2)
    			
    		#Debug to test if the victory condition is working
    		#CyGame().changeGlobalCounter(-2)
    - In order to make the victory look like a "peace" victory (even though the mechanics of the code use the 2nd victory option), anw suggested "maybe you could do it like with the vanilla scenarios by adding a hidden gameoption, and then in the victory screen add a check for conquest victory and this new gameoption and change the picture"
    - onVictory- This runs when the player wins the scenario. We use it to popup victory text and reward trophies.

  • How to set basic starting conditions
    - Set gold, starting technologies, buildings (e.g. Planar Gate) via World Builder​
  • How to make you only able to select Elohim civ when you load the scenario from the main screen???

  • Give open borders for good civs to eachother
 
[*]How to make good civs attack evil cities when Lurchuip finish Bane Divine ritual (ie have the good armies at the doorsteps of the evil cities when Bane Divine is finished)
- Option1: onUnitCreated (at a certain turn so that not every good unit is moved) then pushMoveToMission(X, Y) to move those later created units to a certain tile. Then use this code to give the HELD promotion and then move all the units as a stack to the target tile:
Code:
		#This code should give all units in tile(10,10) the HELD promotion so that they can't move anywhere.
		#iTargetX and iTargetY give the coordinates of the tile that the unit stack is moving into
		iTargetX = 60
		iTargetY = 18
		pTargetPlot = CyMap().plot(iTargetX,iTargetY)
		
		if iGameTurn < 5:
			iOldX = 67
			iOldY = 28
			pOldPlot = CyMap().plot(iOldX,iOldY)
			for i in range(pOldPlot.getNumUnits()):
				pUnit = pOldPlot.getUnit(i)
				pUnit.setHasPromotion(gc.getInfoTypeForString('PROMOTION_HELD'), True)
			#Remember the x and y values between turns 4 and 5
			myPickle = [iOldX, iOldY]
			serializedMyPickle = pickle.dumps(myPickle)
			BugData.getTable("MY_TABLE").setData(serializedMyPickle)

		
		if iGameTurn >= 5 and iGameTurn <= 20:
			#Load x and y values
			serializedMyPickle = BugData.getTable("MY_TABLE").data
			myPickle = pickle.loads(serializedMyPickle)
			iOldX = myPickle[0]
			iOldY = myPickle[1]
			pOldPlot = CyMap().plot(iOldX, iOldY)
			UnitsMoved = -1
			
			
			if iOldY == iTargetY and iOldX > iTargetX:
				#First choice is to move W if possible
				if UnitsMoved == -1:
					iNewX = iOldX - 1
					iNewY = iOldY
					pNewPlot = CyMap().plot(iNewX,iNewY)			
					if pNewPlot.isNone() == False:
						if pNewPlot.getNumUnits() == 0:
							if (pNewPlot.isWater() == False and pNewPlot.isPeak() == False and pNewPlot.isCity() == False):
								for i in range(pOldPlot.getNumUnits(), -1, -1):
									pUnit = pOldPlot.getUnit(i)
									pUnit.setXY(iNewX, iNewY, False, True, True)
								UnitsMoved = 1
				
				#Second choice is to move NW if possible
				if UnitsMoved == -1:
					iNewY = iNewY + 1
					pNewPlot = CyMap().plot(iNewX,iNewY)			
					if pNewPlot.isNone() == False:
						if pNewPlot.getNumUnits() == 0:
							if (pNewPlot.isWater() == False and pNewPlot.isPeak() == False and pNewPlot.isCity() == False):
								for i in range(pOldPlot.getNumUnits(), -1, -1):
									pUnit = pOldPlot.getUnit(i)
									pUnit.setXY(iNewX, iNewY, False, True, True)
								UnitsMoved = 1
								
				#Third choice is to move SW if possible
				if UnitsMoved == -1:
					iNewY = iNewY - 2
					pNewPlot = CyMap().plot(iNewX,iNewY)			
					if pNewPlot.isNone() == False:
						if pNewPlot.getNumUnits() == 0:
							if (pNewPlot.isWater() == False and pNewPlot.isPeak() == False and pNewPlot.isCity() == False):
								for i in range(pOldPlot.getNumUnits(), -1, -1):
									pUnit = pOldPlot.getUnit(i)
									pUnit.setXY(iNewX, iNewY, False, True, True)
								UnitsMoved = 1
								
				#Fourth choice is to stay in the same tile
				if UnitsMoved == -1:
					iNewY = iNewY + 1
					iNewX = iNewX + 1
					pNewPlot = CyMap().plot(iNewX,iNewY)
					for i in range(pOldPlot.getNumUnits(), -1, -1):
						pUnit = pOldPlot.getUnit(i)
						pUnit.setXY(iNewX, iNewY, False, True, True)
					UnitsMoved = 1
				
				#Remember the x and y values between turns
				iOldX = iNewX
				iOldY = iNewY
				myPickle = [iOldX, iOldY]
				serializedMyPickle = pickle.dumps(myPickle)
				BugData.getTable("MY_TABLE").setData(serializedMyPickle)

			
			if iOldY > iTargetY and iOldY < iTargetY + 5 and iOldX > iTargetX:
				#First choice is to move SW if possible
				if UnitsMoved == -1:
					iNewX = iOldX - 1
					iNewY = iOldY - 1
					pNewPlot = CyMap().plot(iNewX,iNewY)			
					if pNewPlot.isNone() == False:
						if pNewPlot.getNumUnits() == 0:
							if (pNewPlot.isWater() == False and pNewPlot.isPeak() == False and pNewPlot.isCity() == False):
								for i in range(pOldPlot.getNumUnits(), -1, -1):
									pUnit = pOldPlot.getUnit(i)
									pUnit.setXY(iNewX, iNewY, False, True, True)
								UnitsMoved = 1
				
				#Second choice is to move W if possible
				if UnitsMoved == -1:
					iNewY = iNewY + 1
					pNewPlot = CyMap().plot(iNewX,iNewY)			
					if pNewPlot.isNone() == False:
						if pNewPlot.getNumUnits() == 0:
							if (pNewPlot.isWater() == False and pNewPlot.isPeak() == False and pNewPlot.isCity() == False):
								for i in range(pOldPlot.getNumUnits(), -1, -1):
									pUnit = pOldPlot.getUnit(i)
									pUnit.setXY(iNewX, iNewY, False, True, True)
								UnitsMoved = 1
								
				#Third choice is to move NW if possible
				if UnitsMoved == -1:
					iNewY = iNewY + 1
					pNewPlot = CyMap().plot(iNewX,iNewY)			
					if pNewPlot.isNone() == False:
						if pNewPlot.getNumUnits() == 0:
							if (pNewPlot.isWater() == False and pNewPlot.isPeak() == False and pNewPlot.isCity() == False):
								for i in range(pOldPlot.getNumUnits(), -1, -1):
									pUnit = pOldPlot.getUnit(i)
									pUnit.setXY(iNewX, iNewY, False, True, True)
								UnitsMoved = 1
								
				#Fourth choice is to stay in the same tile
				if UnitsMoved == -1:
					iNewY = iNewY - 1
					iNewX = iNewX + 1
					pNewPlot = CyMap().plot(iNewX,iNewY)
					for i in range(pOldPlot.getNumUnits(), -1, -1):
						pUnit = pOldPlot.getUnit(i)
						pUnit.setXY(iNewX, iNewY, False, True, True)
					UnitsMoved = 1
				
				#Remember the x and y values between turns
				iOldX = iNewX
				iOldY = iNewY
				myPickle = [iOldX, iOldY]
				serializedMyPickle = pickle.dumps(myPickle)
				BugData.getTable("MY_TABLE").setData(serializedMyPickle)

				
			if iOldY < iTargetY and iOldY > iTargetY - 5 and iOldX > iTargetX:
				#First choice is to move NW if possible
				if UnitsMoved == -1:
					iNewX = iOldX - 1
					iNewY = iOldY + 1
					pNewPlot = CyMap().plot(iNewX,iNewY)			
					if pNewPlot.isNone() == False:
						if pNewPlot.getNumUnits() == 0:
							if (pNewPlot.isWater() == False and pNewPlot.isPeak() == False and pNewPlot.isCity() == False):
								for i in range(pOldPlot.getNumUnits(), -1, -1):
									pUnit = pOldPlot.getUnit(i)
									pUnit.setXY(iNewX, iNewY, False, True, True)
								UnitsMoved = 1
				
				#Second choice is to move W if possible
				if UnitsMoved == -1:
					iNewY = iNewY - 1
					pNewPlot = CyMap().plot(iNewX,iNewY)			
					if pNewPlot.isNone() == False:
						if pNewPlot.getNumUnits() == 0:
							if (pNewPlot.isWater() == False and pNewPlot.isPeak() == False and pNewPlot.isCity() == False):
								for i in range(pOldPlot.getNumUnits(), -1, -1):
									pUnit = pOldPlot.getUnit(i)
									pUnit.setXY(iNewX, iNewY, False, True, True)
								UnitsMoved = 1
								
				#Third choice is to move SW if possible
				if UnitsMoved == -1:
					iNewY = iNewY - 1
					pNewPlot = CyMap().plot(iNewX,iNewY)			
					if pNewPlot.isNone() == False:
						if pNewPlot.getNumUnits() == 0:
							if (pNewPlot.isWater() == False and pNewPlot.isPeak() == False and pNewPlot.isCity() == False):
								for i in range(pOldPlot.getNumUnits(), -1, -1):
									pUnit = pOldPlot.getUnit(i)
									pUnit.setXY(iNewX, iNewY, False, True, True)
								UnitsMoved = 1
								
				#Fourth choice is to stay in the same tile
				if UnitsMoved == -1:
					iNewY = iNewY + 1
					iNewX = iNewX + 1
					pNewPlot = CyMap().plot(iNewX,iNewY)
					for i in range(pOldPlot.getNumUnits(), -1, -1):
						pUnit = pOldPlot.getUnit(i)
						pUnit.setXY(iNewX, iNewY, False, True, True)
					UnitsMoved = 1
				
				#Remember the x and y values between turns
				iOldX = iNewX
				iOldY = iNewY
				myPickle = [iOldX, iOldY]
				serializedMyPickle = pickle.dumps(myPickle)
				BugData.getTable("MY_TABLE").setData(serializedMyPickle)
				
				
			if iOldY >= iTargetY + 5 and iOldX > iTargetX:
				#First choice is to move SW if possible
				if UnitsMoved == -1:
					iNewX = iOldX - 1
					iNewY = iOldY - 1
					pNewPlot = CyMap().plot(iNewX,iNewY)			
					if pNewPlot.isNone() == False:
						if pNewPlot.getNumUnits() == 0:
							if (pNewPlot.isWater() == False and pNewPlot.isPeak() == False and pNewPlot.isCity() == False):
								for i in range(pOldPlot.getNumUnits(), -1, -1):
									pUnit = pOldPlot.getUnit(i)
									pUnit.setXY(iNewX, iNewY, False, True, True)
								UnitsMoved = 1
												
				#Second choice is to stay in the same tile
				if UnitsMoved == -1:
					iNewY = iNewY + 1
					iNewX = iNewX + 1
					pNewPlot = CyMap().plot(iNewX,iNewY)
					for i in range(pOldPlot.getNumUnits(), -1, -1):
						pUnit = pOldPlot.getUnit(i)
						pUnit.setXY(iNewX, iNewY, False, True, True)
					UnitsMoved = 1
				
				#Remember the x and y values between turns
				iOldX = iNewX
				iOldY = iNewY
				myPickle = [iOldX, iOldY]
				serializedMyPickle = pickle.dumps(myPickle)
				BugData.getTable("MY_TABLE").setData(serializedMyPickle)
				

			if iOldY <= iTargetY - 5 and iOldX > iTargetX:
				#First choice is to move NW if possible
				if UnitsMoved == -1:
					iNewX = iOldX - 1
					iNewY = iOldY + 1
					pNewPlot = CyMap().plot(iNewX,iNewY)			
					if pNewPlot.isNone() == False:
						if pNewPlot.getNumUnits() == 0:
							if (pNewPlot.isWater() == False and pNewPlot.isPeak() == False and pNewPlot.isCity() == False):
								for i in range(pOldPlot.getNumUnits(), -1, -1):
									pUnit = pOldPlot.getUnit(i)
									pUnit.setXY(iNewX, iNewY, False, True, True)
								UnitsMoved = 1
								
				#Second choice is to stay in the same tile
				if UnitsMoved == -1:
					iNewY = iNewY -1
					iNewX = iNewX + 1
					pNewPlot = CyMap().plot(iNewX,iNewY)
					for i in range(pOldPlot.getNumUnits(), -1, -1):
						pUnit = pOldPlot.getUnit(i)
						pUnit.setXY(iNewX, iNewY, False, True, True)
					UnitsMoved = 1
				
				#Remember the x and y values between turns
				iOldX = iNewX
				iOldY = iNewY
				myPickle = [iOldX, iOldY]
				serializedMyPickle = pickle.dumps(myPickle)
				BugData.getTable("MY_TABLE").setData(serializedMyPickle)
				
			
			if iOldY < iTargetY and iOldY > iTargetY - 5 and iOldX == iTargetX:
				#First choice is to move N if possible
				if UnitsMoved == -1:
					iNewX = iOldX
					iNewY = iOldY + 1
					pNewPlot = CyMap().plot(iNewX,iNewY)			
					if pNewPlot.isNone() == False:
						if pNewPlot.getNumUnits() == 0:
							if (pNewPlot.isWater() == False and pNewPlot.isPeak() == False and pNewPlot.isCity() == False):
								for i in range(pOldPlot.getNumUnits(), -1, -1):
									pUnit = pOldPlot.getUnit(i)
									pUnit.setXY(iNewX, iNewY, False, True, True)
								UnitsMoved = 1
			
				#Second choice is to stay in the same tile
				if UnitsMoved == -1:
					iNewY = iNewY - 1
					iNewX = iNewX
					pNewPlot = CyMap().plot(iNewX,iNewY)
					for i in range(pOldPlot.getNumUnits(), -1, -1):
						pUnit = pOldPlot.getUnit(i)
						pUnit.setXY(iNewX, iNewY, False, True, True)
					UnitsMoved = 1
				
				#Remember the x and y values between turns
				iOldX = iNewX
				iOldY = iNewY
				myPickle = [iOldX, iOldY]
				serializedMyPickle = pickle.dumps(myPickle)
				BugData.getTable("MY_TABLE").setData(serializedMyPickle)
				
			if iOldY > iTargetY and iOldY < iTargetY + 5 and iOldX == iTargetX:
				#First choice is to move S if possible
				if UnitsMoved == -1:
					iNewX = iOldX
					iNewY = iOldY - 1
					pNewPlot = CyMap().plot(iNewX,iNewY)			
					if pNewPlot.isNone() == False:
						if pNewPlot.getNumUnits() == 0:
							if (pNewPlot.isWater() == False and pNewPlot.isPeak() == False and pNewPlot.isCity() == False):
								for i in range(pOldPlot.getNumUnits(), -1, -1):
									pUnit = pOldPlot.getUnit(i)
									pUnit.setXY(iNewX, iNewY, False, True, True)
								UnitsMoved = 1
			
				#Second choice is to stay in the same tile
				if UnitsMoved == -1:
					iNewY = iNewY + 1
					iNewX = iNewX
					pNewPlot = CyMap().plot(iNewX,iNewY)
					for i in range(pOldPlot.getNumUnits(), -1, -1):
						pUnit = pOldPlot.getUnit(i)
						pUnit.setXY(iNewX, iNewY, False, True, True)
					UnitsMoved = 1
				
				#Remember the x and y values between turns
				iOldX = iNewX
				iOldY = iNewY
				myPickle = [iOldX, iOldY]
				serializedMyPickle = pickle.dumps(myPickle)
				BugData.getTable("MY_TABLE").setData(serializedMyPickle)
								
			if iOldY <= iTargetY - 5 and iOldX == iTargetX:
				#First choice is to move N if possible
				if UnitsMoved == -1:
					iNewX = iOldX
					iNewY = iOldY + 1
					pNewPlot = CyMap().plot(iNewX,iNewY)			
					if pNewPlot.isNone() == False:
						if pNewPlot.getNumUnits() == 0:
							if (pNewPlot.isWater() == False and pNewPlot.isPeak() == False and pNewPlot.isCity() == False):
								for i in range(pOldPlot.getNumUnits(), -1, -1):
									pUnit = pOldPlot.getUnit(i)
									pUnit.setXY(iNewX, iNewY, False, True, True)
								UnitsMoved = 1
								
				#Second choice is to stay in the same tile
				if UnitsMoved == -1:
					iNewY = iNewY -1
					iNewX = iNewX
					pNewPlot = CyMap().plot(iNewX,iNewY)
					for i in range(pOldPlot.getNumUnits(), -1, -1):
						pUnit = pOldPlot.getUnit(i)
						pUnit.setXY(iNewX, iNewY, False, True, True)
					UnitsMoved = 1
				
				#Remember the x and y values between turns
				iOldX = iNewX
				iOldY = iNewY
				myPickle = [iOldX, iOldY]
				serializedMyPickle = pickle.dumps(myPickle)
				BugData.getTable("MY_TABLE").setData(serializedMyPickle)
				
				
			if iOldY >= iTargetY + 5 and iOldX == iTargetX:
				#First choice is to move S if possible
				if UnitsMoved == -1:
					iNewX = iOldX
					iNewY = iOldY - 1
					pNewPlot = CyMap().plot(iNewX,iNewY)			
					if pNewPlot.isNone() == False:
						if pNewPlot.getNumUnits() == 0:
							if (pNewPlot.isWater() == False and pNewPlot.isPeak() == False and pNewPlot.isCity() == False):
								for i in range(pOldPlot.getNumUnits(), -1, -1):
									pUnit = pOldPlot.getUnit(i)
									pUnit.setXY(iNewX, iNewY, False, True, True)
								UnitsMoved = 1
								
				#Second choice is to stay in the same tile
				if UnitsMoved == -1:
					iNewY = iNewY +1
					iNewX = iNewX
					pNewPlot = CyMap().plot(iNewX,iNewY)
					for i in range(pOldPlot.getNumUnits(), -1, -1):
						pUnit = pOldPlot.getUnit(i)
						pUnit.setXY(iNewX, iNewY, False, True, True)
					UnitsMoved = 1
				
				#Remember the x and y values between turns
				iOldX = iNewX
				iOldY = iNewY
				myPickle = [iOldX, iOldY]
				serializedMyPickle = pickle.dumps(myPickle)
				BugData.getTable("MY_TABLE").setData(serializedMyPickle)
				

			#Get rid of the HELD promotion when the stack reaches the target tile
			#This is still indented to be included within iGameTurn >5 and <20
			if iOldX == iTargetX and iOldY == iTargetY:
				#cf.addUnit(gc.getInfoTypeForString('UNIT_HUNTER'))
				for i in range(pTargetPlot.getNumUnits()):
					pUnit = pTargetPlot.getUnit(i)
					pUnit.setHasPromotion(gc.getInfoTypeForString('PROMOTION_HELD'), False)

Code:
for i in range (iNum):
	newUnit = pPlayer.initUnit(iUnit, pCity.getX(), pCity.getY(), UnitAITypes.UNITAI_ATTACK_CITY_LEMMING, DirectionTypes.DIRECTION_SOUTH)
	newUnit.getGroup().pushMission(MissionTypes.MISSION_MOVE_TO, 5, 13, 0, False, False, MissionAITypes.NO_MISSIONAI, newUnit.plot(), newUnit)

 
Progress Report:

CvEventManager (MNAI v2.5) http://pastebin.com/qk32ekJm:
- All good civs know eachother and all evil civs know eachother and make good and evil civs at war with eachother
- Make all good civs send their units to the marshaling point to create a Stack of Doom (SoD) and then move the SoD to the target tile
- Make Lurchuip build Bane Divine starting on a certain turn in their capital city (when the SoD arrives nearby to the target tile)
- Disease promotion given to all living units (ie golems will be safe...) 5 turns after Bane Divine is built including popup to explain it
- The Luchuirp declare war on the other good civs if their power > the power of the other good civs combined
CvEventManager.py (MNAI v2.6) http://pastebin.com/AXnBLVxS

CvSpellInterface.py:
- Prevent Luchuirp world spell being cast on T1 when they will be getting several cities soon
-
Spoiler :
Code:
def reqGiftsOfNantosuelta(caster):
	iGameTurn = gc.getGame().getGameTurn()
	pPlayer = gc.getPlayer(caster.getOwner())
	if pPlayer.getNumCities() == 0:
		return False
	if iGameTurn < 20:
		return False
#	if pPlayer.isHuman() == False:
#		map = gc.getMap()
		
#		if gc.getGame().isOption(GameOptionTypes.GAMEOPTION_ONE_CITY_CHALLENGE) or gc.getGame().isOption(GameOptionTypes.GAMEOPTION_NO_SETTLERS):
#			if pPlayer.getNumCities() > 0:
#				return True
#		if CyGame().getSorenRandNum(200, "Gifts") == 1:
#			return True
#		if pPlayer.getNumCities() < gc.getWorldInfo(map.getWorldSize()).getTargetNumCities():
#			return False
	return True

CIV4GameText_FFH2.xml http://pastebin.com/8MnrUv8a:
- Needs creative stories for each popup!

CIV4ProjectInfo.xml:
- iAIweight changed from 0 to 800 in Elegy of the Sheiam http://pastebin.com/AbHpJSkn
- iCost changed from 600 to 300 for Elegy of the Sheaim and Hallowing of the Elohim

CIV4PromotionInfos.xml:
- Created a promotion called SoD http://pastebin.com/xE543xSA

CIV4BuildingInfos.xml:
- Mercurian Gate made more expensive - iCost increased (600 -> 3000)
- Mercurian Gate made less desirable to AI - iAIWeight decreased (500 -> 100)

CIV4LeaderHeadInfos.xml:
- ReligionWeightModifier changes - use -100 to prevent a each evil religion from being adopted by the good civs and good religions being adopted by the evil civs except Garrim who only has a +50 RoK modifier given

GlobalDefinesAlt.xml:
- FLAMES_SPREAD_CHANCE - increased (20 -> 30)
- FLAMES_EXPIRE_CHANCE - decreased (20 -> 15)

CIV4GameText_Misc1.xml and CIV4GameTextInfos.xml
- Can change Civ4 Complete xml files if I want to write holy victory or something instead of diplomatic victory (%s2 is defined in the dll)

Unit Changes:
- Lucian given Shock I
- Doviello start with 4 beastmen + Lucian in capital + 2 beastmen in 2nd city
- Clan of Embers starts with 4 goblins and 12 warriors
- Bannor start with 3 warriors
- Svartlfar start with a worker

City Changes:
- Bannor capital given walls

Civ Starting Gold:
- Calabim = 100G, Ljosalfar = 50G, Bannor = 100G, Doviello = 400G, Svartalfar = 200G, Luchuirp = 300G, Clan of Embers = 500G, Sheaim = 100G

Civ Starting Techs:
- Cartography + City States given to Calabim, Doviello, Clan of Embers, Svartalfar, Sheaim, Luchuirp.
 
I need help with map creation - to make it an interesting map. I've attached a plan of where I want the starting cities. Does anyone want to contribute ideas for where terrain and features should go on the map? E.g. I'm thinking more forests up north where the elves are located.
 

Attachments

  • Map outline Elohim scenario.jpg
    Map outline Elohim scenario.jpg
    73.3 KB · Views: 205
Ichabod and Bobchillingworth on Realms Beyond are good at mapmaking and could help you with ideas.
 
Honestly, I think the best advice is just to start drawing. IME (I've made several maps for multiplayer play, and put some time into scenario building) once you get that started, details will just flow.

Get a vague idea for things (how much water? How much land do you need?) then choose the settings of number of tiles either way (32x32 and so on) - Torusland is a great help for this, but you can just base it off another mapscript if you are so inclined - and pull the trigger. Then turn everything into land, place the civs, and just start drawing and watch as things...happen. :)

I'm happy to give you some advice from that point. But from here it's a bit hard to tell.
 
How about you put in code such that they automatically spawn Settlers periodically. I assume they'll still settle them somewhere yes?
 
Not necessarily...

Bad Player is right in that this is one of the most frustrating things about playing always war in FFH - the AI is often very reluctant about settling cities, whether or not they have the settlers for it, whether or not the location is threatened. I (occasionally) play close death-matches against the AI, often starting in late eras, and I often find that the AI will fail to expand even with the second starting settler.
 
Play testing it a lot and every AI makes a settler but keeps them in their city/ies and never makes a new city UNLESS I remove my war declaration code. So the bug relates to something about what I'm doing declaring permanent war (tried TOTAL_WAR and LIMITED_WAR). I asked about it in my thread in the MNAI subforum.
 
I want the game to have different ways it will play out and the player to have more strategies to try which makes the game more replayable IMO. E.g. some things that could happen are:
- Lurchuip never reveal as evil and you win or lose the game with them on your side
- Infernals may or may not enter (same with Mercurians but very unlikely due to increased cost)
- Evil civs may declare war on one another or not during the game
- Player may win by lowering the AC to 0 or the player may win via conquest



Does anyone know of any scenarios where there are civs at permanent war but they still go out and build new cities?
 
I tried a regular FFH2 MNAI game with 2 civs (Me vs Sheiam) and the only game option being ALWAYS WAR. Turn 161 and the Sheaim have a settler in their only city but they haven't created a new city. Could it be that the AI doesn't build new cities when ALWAYS WAR is on?
 
I tried starting at peace and then declaring war on a couple of the AI (but not the rest). The peaceful AI built multiple cities but the AI that I declared war on had stunted growth. One of them didn't build a new city (but had a settler) and just conquered a barbarian city and the other AI that I declared war on built 2 cities but much later than the peaceful AI civs.
 
It's possible that it's an oversight. I think you should continue. FFH is not perfect and neither will your scenario at the first go. Don't let this usse stop you from making the scenario. Do a dirty fix or whatever and come back later.
 
Thing is scenario coding (which is the hard part) is all done - it's just this weird thing that if you start the civs at war, they don't build new cities.

My best dirty fix idea is to adjust their attitude so that they declare war at first sight and then use setpermanentwarpeace to keep them that way. That's an idea anyway...
 
Top Bottom