Earlygame Scions

Hmm... how about a WS that allows a bunch of embodied people to escape the Underworld and/or Hell? (I'd have to look at the Lore to see which would be easier - or possible - to justify.)

The effect is Awakened and military units - tilted strongly toward defense - appear together somewhere in the world outside Scion territory. Use them to conquer a chunk of territory, or (perhaps more realistically) get them to Scion territory for a pop boost. A lot would depend on the map. Maybe they'd just get snuffed. Either way you're likely to face some decisions and challenges. (Maybe just "Where are the boats?")

Minimum tech around the level of Sorcery, probably more. I'd want to make it such that the longer you wait the more units you get, though I'm not sure how to justify it. (Maybe base it on techs possessed?)

I'd probably give the spell to Themoch. Ideally he casts it, disappears for awhile, and re-appears later with the escapees. Or an invisible-to-non-Scions "beacon" unit could appear where the Spiral Gate will open in X turns. Give the player a chance to scramble some support.

Maybe make the spell highly responsive to techs possessed. Some make more Reborn likely, some more military units, some better military units, etc.

Civs without an Open Borders agreement or an existing war - I'd say the units appearing declares war.

(This is my favorite idea yet. Fingers crossed no one comes up with a reason shooting it down as quickly as all the others. :p)

It might be nice for the location to be something specific and notable, such as the Remnants of Patria, or Bradeline's well.

Imagine, spawning awakened on the Remnants of Patria, dependant on the amount of haunted lands in the world. And possibly the number of luxury resources you have, too. It would make an interesting quest to capture it at all costs. especially if say... the scions could be tweaked to usually start far away from it.

In case it isn't in the world, maybe they could have a python effect to create it on turn 1 (far away from their starting location)
 
Problem is that you can't guarantee Unique Features will be in any given game. And some people like to turn them off so I dunno about Python.

I'm okay with it being somewhere random in unowned lands.
 
Here's what I've got so far -

Can be cast at Sorcery, though you need Themoch. He comes from Tracking.

The top 30-some techs with the exception of Rage give units.
Armored Cav. gives 10 xps to the Horse Archers, Military Strat gives 6 xps to the Melee, Archer, and Recon units.
The Arcane units start out with some spheres.

I suspect it needs to be trimmed. I'll total it up and see... ATM if you've got every tech but Rage or Future Tech you get:

1 Phalanx
1 Abomination
1 Marksman
2 Principes
3 Bone Horde
1 Revenant
4 Horse Archer
3 Ghostwalker
5 Necromancer
3 Longbowman
2 Honored Band
2 Archer
That's 28 military units. So an average of a little under 1 Mil. unit per tech. Most techs giving military units give a couple, though.

Plus:
42(!) Awakened
11 Supplies

That seems high, but I'm not sure what's appropriate for waiting 'till Every-Tech-But-One. If cast ASAP you get 1 Necromancer and 3 Awakened.

No siege units. May be good to change that.


If anyone wants to see the python:

Spoiler :

Code:
def spellSpiralGate(caster):
	pPlayer = gc.getPlayer(caster.getOwner())		
	eTeam = gc.getTeam(pPlayer.getTeam())	
	pCapital = pPlayer.getCapitalCity()
		
	pBestPlot = -1
	iBestPlot = -1
	for i in range (CyMap().numPlots()):
		pPlot = CyMap().plotByIndex(i)
		iPlot = -1
		iPlotOwner = pPlot.getOwner()
		if not iPlotOwner == pPlayer:
			if pPlot.isWater() == False:
				if pPlot.getNumUnits() == 0:
					if pPlot.isCity() == False:
						if pPlot.isImpassable() == False:
							iPlot = CyGame().getSorenRandNum(500, "Place Spiral Gate")
							iPlot = iPlot + (pPlot.area().getNumTiles() * 2)
							iPlot = iPlot + (pPlot.area().getNumUnownedTiles() * 10)
							iPlot = iPlot + (plotDistance(pPlot.getX(), pPlot.getY(), pCapital.getX(), pCapital.getY()) * 5)
							if pPlot.isOwned() == False:
								iPlot = iPlot + 200
		if iPlot > iBestPlot:
			iBestPlot = iPlot
			pBestPlot = pPlot
				
		if pBestPlot != -1:	
			newUnit1 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_NECROMANCER'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			newUnit1.setHasPromotion(gc.getInfoTypeForString('PROMOTION_DEATH1'), true)
			newUnit1.setHasPromotion(gc.getInfoTypeForString('PROMOTION_DEATH2'), true)
			newUnit1.setHasPromotion(gc.getInfoTypeForString('PROMOTION_SPIRIT1'), true)
			newUnit1.setHasPromotion(gc.getInfoTypeForString('PROMOTION_SPIRIT2'), true)
			newUnit1.setHasPromotion(gc.getInfoTypeForString('PROMOTION_SHADOW1'), true)
			newUnit1.setHasPromotion(gc.getInfoTypeForString('PROMOTION_SHADOW2'), true)
			newUnit1.setHasPromotion(gc.getInfoTypeForString('PROMOTION_COMBAT1'), true)
			newUnit1.setExperienceTimes100(1000, -1)
			newUnit1.setName(CyTranslator().getText('TXT_KEY_UNIT_FALSE_LAROTH',()))
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_ARCHERY')) == True:
				newUnit2 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_ARCHER'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit3 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_ARCHER'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_BRONZE_WORKING')) == True:
				newUnit4 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_HONORED_BAND'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit5 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_HONORED_BAND'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit4.setHasPromotion(gc.getInfoTypeForString('PROMOTION_BRONZE_WEAPONS'), true)
				newUnit5.setHasPromotion(gc.getInfoTypeForString('PROMOTION_BRONZE_WEAPONS'), true)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_IRON_WORKING')) == True:
				newUnit6 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_PRINCIPES'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit6.setHasPromotion(gc.getInfoTypeForString('PROMOTION_IRON_WEAPONS'), true)
				newUnit7 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_PRINCIPES'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit7.setHasPromotion(gc.getInfoTypeForString('PROMOTION_IRON_WEAPONS'), true)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_TRADE')) == True:
				newUnit8 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_SPIRAL_SUPPLIES'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_STIRRUPS')) == True:
				newUnit9 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_HORSE_ARCHER'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit10 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_HORSE_ARCHER'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_WARHORSES')) == True:
				newUnit11 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_HORSE_ARCHER'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit12 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_HORSE_ARCHER'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit35 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_ARMORED_CAVALRY')) == True:
				newUnit13 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_SPIRAL_SUPPLIES'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit14 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_SPIRAL_SUPPLIES'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit9.setExperienceTimes100(1000, -1)
				newUnit10.setExperienceTimes100(1000, -1)
				newUnit11.setExperienceTimes100(1000, -1)
				newUnit12.setExperienceTimes100(1000, -1)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_ANIMAL_HANDLING')) == True:
				newUnit15 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_GHOSTWALKER'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit36 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_FERAL_BOND')) == True:
				newUnit17 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_GHOSTWALKER'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit18 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_ANIMAL_MASTERY')) == True:
				newUnit19 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_GHOSTWALKER'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit20 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit21 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_SMELTING')) == True:
				newUnit22 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_SPIRAL_SUPPLIES'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit23 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_SANITATION')) == True:
				newUnit24 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit25 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_MEDICINE')) == True:
				newUnit26 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit27 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_MITHRIL_WORKING')) == True:
				newUnit28 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_SPIRAL_SUPPLIES'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit29 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_PHALANX'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit29.setHasPromotion(gc.getInfoTypeForString('PROMOTION_IRON_WEAPONS'), true)
				newUnit30 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_ASTRONOMY')) == True:
				newUnit28 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_SPIRAL_SUPPLIES'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_BOWYERS')) == True:
				newUnit29 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_LONGBOWMAN'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit30 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_LONGBOWMAN'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit31 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_LONGBOWMAN'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_PRECISION')) == True:
				newUnit32 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_MARKSMAN'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit33 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_TAXATION')) == True:
				newUnit29 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_GUILDS')) == True:
				newUnit37 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_SPIRAL_SUPPLIES'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit38 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_MERCANTILISM')) == True:
				newUnit39= pPlayer.initUnit(gc.getInfoTypeForString('UNIT_SPIRAL_SUPPLIES'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit41= pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit42 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_MILITARY_STRATEGY')) == True:
				newUnit43 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit2.setExperienceTimes100(600, -1)
				newUnit3.setExperienceTimes100(600, -1)
				newUnit4.setExperienceTimes100(600, -1)
				newUnit5.setExperienceTimes100(600, -1)
				newUnit6.setExperienceTimes100(600, -1)
				newUnit7.setExperienceTimes100(600, -1)
				newUnit15.setExperienceTimes100(600, -1)
				newUnit17.setExperienceTimes100(600, -1)
				newUnit19.setExperienceTimes100(600, -1)
				newUnit29.setExperienceTimes100(600, -1)
				newUnit30.setExperienceTimes100(600, -1)
				newUnit31.setExperienceTimes100(600, -1)
				newUnit32.setExperienceTimes100(600, -1)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_ENGINEERING')) == True:
				newUnit56 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_SPIRAL_SUPPLIES'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_BLASTING_POWDER')) == True:
				newUnit44 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_BONE_HORDE'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit45 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_BONE_HORDE'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit46 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_BONE_HORDE'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_ARCANE_LORE')) == True:
				newUnit47 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_NECROMANCER'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit47.setHasPromotion(gc.getInfoTypeForString('PROMOTION_DEATH1'), true)
				newUnit47.setHasPromotion(gc.getInfoTypeForString('PROMOTION_DEATH2'), true)
				newUnit48 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_STRENGTH_OF_WILL')) == True:
				newUnit49 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_NECROMANCER'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit49.setHasPromotion(gc.getInfoTypeForString('PROMOTION_DEATH1'), true)
				newUnit49.setHasPromotion(gc.getInfoTypeForString('PROMOTION_DEATH2'), true)
				newUnit49.setHasPromotion(gc.getInfoTypeForString('PROMOTION_COMBAT1'), true)
				newUnit50 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit51 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_PASS_THROUGH_THE_ETHER')) == True:
				newUnit52 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_NECROMANCER'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit52.setHasPromotion(gc.getInfoTypeForString('PROMOTION_DEATH1'), true)
				newUnit52.setHasPromotion(gc.getInfoTypeForString('PROMOTION_COMBAT1'), true)
				newUnit52.setHasPromotion(gc.getInfoTypeForString('PROMOTION_COMBAT2'), true)
				newUnit53 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit54 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit55 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_SPIRAL_SUPPLIES'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_MACHINERY')) == True:
				newUnit57 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit58 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_SPIRAL_SUPPLIES'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_OMNISCIENCE')) == True:
				newUnit59 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit60 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit61 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_NECROMANCER'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit61.setHasPromotion(gc.getInfoTypeForString('PROMOTION_DEATH1'), true)
				newUnit61.setHasPromotion(gc.getInfoTypeForString('PROMOTION_DEATH2'), true)
				newUnit61.setHasPromotion(gc.getInfoTypeForString('PROMOTION_COMBAT1'), true)
				newUnit61.setHasPromotion(gc.getInfoTypeForString('PROMOTION_COMBAT2'), true)
				newUnit61.setHasPromotion(gc.getInfoTypeForString('PROMOTION_COMBAT3'), true)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_RELIGIOUS_LAW')) == True:
				newUnit62 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit63 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_THEOLOGY')) == True:
				newUnit64 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit65 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit66 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_COMMUNE_WITH_NATURE')) == True:
				newUnit67 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit68 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_FANATICISM')) == True:
				newUnit69 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit70 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit71 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_REVENANT'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_MALEVOLANT_DESIGN')) == True:
				newUnit72 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit73 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit74 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_RIGHTEOUSNESS')) == True:
				newUnit75 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit76 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit77 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_DIVINE_ESSENCE')) == True:
				newUnit81 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit82 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit83 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AWAKENED'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
				newUnit84 = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_ABOMINATION'), pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_BRONZE_WORKING')) == True:
				newUnit29.setHasPromotion(gc.getInfoTypeForString('PROMOTION_BRONZE_WEAPONS'), true)
				newUnit30.setHasPromotion(gc.getInfoTypeForString('PROMOTION_BRONZE_WEAPONS'), true)
				newUnit31.setHasPromotion(gc.getInfoTypeForString('PROMOTION_BRONZE_WEAPONS'), true)
				newUnit32.setHasPromotion(gc.getInfoTypeForString('PROMOTION_BRONZE_WEAPONS'), true)
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_IRON_WORKING')) == True:
				newUnit29.setHasPromotion(gc.getInfoTypeForString('PROMOTION_IRON_WEAPONS'), true)
				newUnit30.setHasPromotion(gc.getInfoTypeForString('PROMOTION_IRON_WEAPONS'), true)
				newUnit31.setHasPromotion(gc.getInfoTypeForString('PROMOTION_IRON_WEAPONS'), true)
				newUnit4.setHasPromotion(gc.getInfoTypeForString('PROMOTION_IRON_WEAPONS'), true)
				newUnit5.setHasPromotion(gc.getInfoTypeForString('PROMOTION_IRON_WEAPONS'), true)
				newUnit32.setHasPromotion(gc.getInfoTypeForString('PROMOTION_IRON_WEAPONS'), true)
				
			return


I've got a question about the python: I'm not sure if this is actually doing anything (line 20.)

Code:
iPlot = iPlot + (plotDistance(pPlot.getX(), pPlot.getY(), pCapital.getX(), pCapital.getY()) * 5)

It's supposed to make selection of a plot more likely the further it gets from the Scion capital. But I'm unclear on both the BestPlot function and line 20. It was copied in fine script-kiddie tradition from PromoteSettlement.
 
Code:
iPlot = iPlot + (plotDistance(pPlot.getX(), pPlot.getY(), pCapital.getX(), pCapital.getY()) * 5)

It's supposed to make selection of a plot more likely the further it gets from the Scion capital. But I'm unclear on both the BestPlot function and line 20. It was copied in fine script-kiddie tradition from PromoteSettlement.

It does something, it adds the 5*distance to your capital to the 'value' of the tile. Any tile that is not owned or water or otherwise unacceptable starts at a random value of 0-500. Then the 2*number of tiles in the local area is added, and then 10*unowned tiles in the area. If I remember correctly the area is essentially the entire continent.

So there is bias for large continents, a huge bias for unowned continents and and a small bias for distance to capital.

In short, the biggest continent will be selected unless there a large unowned continent. On that continent, an unowned plot far from your capital will be selected.

None of that matters though, since later in the code, all that is ignored and simply the first found tile is used.

Then later I'm fairly certain you can have TECH_MILITARY_STRATEGY without having TECH_BOWYERS, for instance. That will cause an exception since newUnit29 hasn't been defined. Although if you have TECH_TAXATION, one of your awakened will get 6 xp instead. If you also have TECH_IRON_WORKING, that same awakened will also get iron weapons.
 
I fixed the obvious bugs, changed a few names and refactored it so that the code is clearer, to me at least. I haven't tested it, so there are probably still a few bugs left.

Spoiler :
Code:
def spellSpiralGate(argslist):
    if isinstance( argslist, tuple ):
        makeHelpText = True
        eSpell, caster = argslist
    else:
        caster = argslist
        makeHelpText=False
    iPlayer = caster.getOwner()
    pPlayer = gc.getPlayer(iPlayer)
    eTeam = gc.getTeam(pPlayer.getTeam())

    bonusCategory = dict()
    bonusCategory['false laroth'] = dict()
    bonusCategory['archer'] = dict()
    bonusCategory['adept'] = dict()
    bonusCategory['melee'] = dict()
    bonusCategory['recon'] = dict()
    bonusCategory['mounted'] = dict()
    bonusCategory['supplies'] = dict()
    bonusCategory['population'] = dict()
    unitUpgrades = dict()

    # A list of units to be given
    # Each unit is described with a tuple
    # ( unitString, bonusCategoryName, promotionList, name)
    giveUnits = []
    giveUnits.append( ( 'UNIT_NECROMANCER', 'false laroth', ['PROMOTION_DEATH1', 'PROMOTION_DEATH2', 'PROMOTION_SPIRIT1', 'PROMOTION_SPIRIT2', 'PROMOTION_SHADOW1', 'PROMOTION_SHADOW2', 'PROMOTION_COMBAT1', 'PROMOTION_ADVENTURER', ], CyTranslator().getText('TXT_KEY_UNIT_FALSE_LAROTH',() ) ) )
    bonusCategory['false laroth']['xp'] = 10
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_ARCHERY') ):
        giveUnits.append( ( 'UNIT_ARCHER', 'archer', ['PROMOTION_ADVENTURER', ],  'Anyon' ) )
        giveUnits.append( ( 'UNIT_ARCHER', 'archer', ['PROMOTION_ADVENTURER', ],  'Lann' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_BRONZE_WORKING') ):
        giveUnits.append( ( 'UNIT_HONORED_BAND', 'melee', ['PROMOTION_ADVENTURER', ],  'Medb' ) )
        giveUnits.append( ( 'UNIT_HONORED_BAND', 'melee', ['PROMOTION_ADVENTURER', ],  'Aife' ) )
        bonusCategory['melee']['weapons'] = 'PROMOTION_BRONZE_WEAPONS'
        bonusCategory['false laroth']['weapons'] = 'PROMOTION_BRONZE_WEAPONS'
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_IRON_WORKING') ):
        giveUnits.append( ( 'UNIT_PRINCIPES', 'melee', ['PROMOTION_ADVENTURER', ],  'Scathach' ) )
        giveUnits.append( ( 'UNIT_PRINCIPES', 'melee', ['PROMOTION_ADVENTURER', ],  'Voadica' ) )
        bonusCategory['melee']['weapons'] = 'PROMOTION_IRON_WEAPONS'
        bonusCategory['false laroth']['weapons'] = 'PROMOTION_IRON_WEAPONS'
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_TRADE')):
        giveUnits.append( ( 'UNIT_SPIRAL_SUPPLIES', 'supplies', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_STIRRUPS')):
        giveUnits.append( ( 'UNIT_HORSE_ARCHER', 'mounted', [],  '' ) )
        giveUnits.append( ( 'UNIT_HORSE_ARCHER', 'mounted', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_WARHORSES')):
        giveUnits.append( ( 'UNIT_HORSE_ARCHER', 'mounted', [],  '' ) )
        giveUnits.append( ( 'UNIT_HORSE_ARCHER', 'mounted', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_ARMORED_CAVALRY')):
        giveUnits.append( ( 'UNIT_SPIRAL_SUPPLIES', 'supplies', [],  '' ) )
        giveUnits.append( ( 'UNIT_SPIRAL_SUPPLIES', 'supplies', [],  '' ) )
        bonusCategory['mounted']['xp'] = 10
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_ANIMAL_HANDLING')):
        giveUnits.append( ( 'UNIT_GHOSTWALKER', 'recon', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_FERAL_BOND')):
        giveUnits.append( ( 'UNIT_GHOSTWALKER', 'recon', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_ANIMAL_MASTERY')):
        giveUnits.append( ( 'UNIT_GHOSTWALKER', 'recon', ['PROMOTION_ADVENTURER', ],  'Gurith' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_SMELTING')):
        giveUnits.append( ( 'UNIT_SPIRAL_SUPPLIES', 'supplies', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_SANITATION')):
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_MEDICINE')):
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_MITHRIL_WORKING')):
        giveUnits.append( ( 'UNIT_SPIRAL_SUPPLIES', 'supplies', [],  '' ) )
        giveUnits.append( ( 'UNIT_PHALANX', 'melee', ['PROMOTION_ADVENTURER', ],  'Stikla' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        bonusCategory['melee'].setdefault( 'promotions', [] ).append( 'PROMOTION_ENCHANTED_BLADE' )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_ASTRONOMY')):
        giveUnits.append( ( 'UNIT_SPIRAL_SUPPLIES', 'supplies', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_BOWYERS')):
        giveUnits.append( ( 'UNIT_LONGBOWMAN', 'archer', [],  '' ) )
        giveUnits.append( ( 'UNIT_LONGBOWMAN', 'archer', [],  '' ) )
        giveUnits.append( ( 'UNIT_LONGBOWMAN', 'archer', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_PRECISION')):
        giveUnits.append( ( 'UNIT_MARKSMAN', 'archer', ['PROMOTION_ADVENTURER', ],  'Tadc' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_TAXATION')):
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_GUILDS')):
        giveUnits.append( ( 'UNIT_SPIRAL_SUPPLIES', 'supplies', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_MERCANTILISM')):
        giveUnits.append( ( 'UNIT_SPIRAL_SUPPLIES', 'supplies', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_MILITARY_STRATEGY')):
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        bonusCategory['melee']['xp'] = 6
        bonusCategory['archer']['xp'] = 6
        bonusCategory['recon']['xp'] = 6
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_ENGINEERING')):
        giveUnits.append( ( 'UNIT_SPIRAL_SUPPLIES', 'supplies', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_BLASTING_POWDER')):
        giveUnits.append( ( 'UNIT_BONE_HORDE', 'archer', [],  '' ) )
        giveUnits.append( ( 'UNIT_BONE_HORDE', 'archer', [],  '' ) )
        giveUnits.append( ( 'UNIT_BONE_HORDE', 'archer', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_SORCERY')):
        unitUpgrades['UNIT_ADEPT'] = 'UNIT_NECROMANCER'
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_ARCANE_LORE')):
        giveUnits.append( ( 'UNIT_NECROMANCER', 'adept', ['PROMOTION_DEATH1', 'PROMOTION_DEATH2', ], '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_STRENGTH_OF_WILL')):
        giveUnits.append( ( 'UNIT_NECROMANCER', 'adept', ['PROMOTION_DEATH1', 'PROMOTION_DEATH2', 'PROMOTION_COMBAT1', ], '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_PASS_THROUGH_THE_ETHER')):
        giveUnits.append( ( 'UNIT_NECROMANCER', 'adept', ['PROMOTION_DEATH1', 'PROMOTION_COMBAT2', 'PROMOTION_COMBAT1', ], '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_SPIRAL_SUPPLIES', 'supplies', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_MACHINERY')):
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_SPIRAL_SUPPLIES', 'supplies', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_OMNISCIENCE')):
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_NECROMANCER', 'adept', ['PROMOTION_DEATH1', 'PROMOTION_DEATH2', 'PROMOTION_COMBAT1', 'PROMOTION_COMBAT2', 'PROMOTION_COMBAT3', ], '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_DIVINATION')):
        giveUnits.append( ( 'UNIT_ADEPT', 'adept', ['PROMOTION_ILLUSIONIST', 'PROMOTION_LAW1', 'PROMOTION_MIND1', 'PROMOTION_ADVENTURER', ], 'Reche of the False Heart' ) )
        bonusCategory['adept']['xp'] = bonusCategory['adept'].get('xp', 0 ) +2
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_ALTERATION')):
        giveUnits.append( ( 'UNIT_GHOSTWALKER', 'recon', ['PROMOTION_ILLUSION', 'PROMOTION_ADVENTURER', ],  'Otal the Faded' ) )
        bonusCategory['adept']['xp'] = bonusCategory['adept'].get('xp', 0 ) +2
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_NECROMANCY')):
        bonusCategory['adept']['xp'] = bonusCategory['adept'].get('xp', 0 ) +2
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_ELEMENTALISM')):
        bonusCategory['archer'].setdefault( 'promotions', [] ).append( 'PROMOTION_FLAMING_ARROWS' )
        bonusCategory['adept']['xp'] = bonusCategory['adept'].get('xp', 0 ) +2
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_RELIGIOUS_LAW')):
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_THEOLOGY')):
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_COMMUNE_WITH_NATURE')):
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_FANATICISM')):
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_REVENANT', 'melee', ['PROMOTION_ADVENTURER', ],  'Rasaec the Hollow' ) ) # Revenants are disciples, but very melee like disciples
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_MALEVOLENT_DESIGNS')):
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_RIGHTEOUSNESS')):
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
    if eTeam.isHasTech(gc.getInfoTypeForString('TECH_DIVINE_ESSENCE')):
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_AWAKENED', 'population', [],  '' ) )
        giveUnits.append( ( 'UNIT_ABOMINATION', 'melee', [],  '' ) )

    if not makeHelpText:
        pCapital = pPlayer.getCapitalCity()
        capitalX = pCapital.getX()
        capitalY = pCapital.getY()
        bestPlotGoodness = -1
        iBestPlot = None
        for i in xrange (CyMap().numPlots()):
            pPlot = CyMap().plotByIndex(i)
            plotGoodness = -1
            if ( pPlot.getOwner() != iPlayer
                and not pPlot.isWater()
                and pPlot.getNumUnits() == 0
                and not pPlot.isCity()
                and not pPlot.isImpassable() ):
                    plotGoodness = CyGame().getSorenRandNum(500, "Place Spiral Gate")
                    # I'm placing a limit on the added goodness.
                    # This makes final goodness a bit less map dependant and less predictable.
                    # The limits will need finetuning.
                    # With a limit of 250, all continents with at least 175 tiles are equally good.
                    plotGoodness += min( pPlot.area().getNumTiles() * 2, 250 )
                    # With a limit of 500, all continents with at least 100 unclaimed tiles are equally good.
                    plotGoodness += min( pPlot.area().getNumUnownedTiles() * 5, 500 )
                    # Distance to capital is squared, since area is squared
                    # With a limit of 450, all tiles further away than 450**.5 or 21 squares are equally good
                    plotGoodness += min( ( (pPlot.getX()-capitalX)**2 + (pPlot.getY()-capitalY)**2 ), 450)
                    if not pPlot.isOwned():
                        plotGoodness += 300
            if plotGoodness > bestPlotGoodness:
                bestPlotGoodness = plotGoodness
                pBestPlot = pPlot
        if pBestPlot is None:
            # It's a world spell, it shouldn't fail just because. Probably won't anyway
            # but just to be sure.
            pBestPlot = pCapital.plot()

        commonInitUnitArgs = ( pBestPlot.getX(), pBestPlot.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH )
        for unitString, bonusCategoryName, promotionList, name in giveUnits:
            while unitString in unitUpgrades:
                unitString = unitUpgrades[unitString]
            newUnit = pPlayer.initUnit( gc.getInfoTypeForString(unitString), *commonInitUnitArgs )
            for promotionString in promotionList:
                newUnit.setHasPromotion( gc.getInfoTypeForString( promotionString ), True)
            if 'promotions' in bonusCategory[bonusCategoryName]:
                for promotionString in bonusCategory[bonusCategoryName]['promotions']:
                    newUnit.setHasPromotion( gc.getInfoTypeForString( promotionString ), True)
            if 'weapons' in bonusCategory[bonusCategoryName]:
                newUnit.setHasPromotion( gc.getInfoTypeForString( bonusCategory[bonusCategoryName]['weapons'] ), True)
            if 'xp' in bonusCategory[bonusCategoryName]:
                newUnit.setExperienceTimes100(bonusCategory[bonusCategoryName]['xp']*100, -1)
            if name:
                newUnit.setName(name)
    else:
        counts = dict( (key,0) for key in [ 'false laroth', 'archer', 'adept', 'melee', 'recon', 'mounted', 'supplies', 'population' ])
        for unitString, bonusCategoryName, promotionList, name in giveUnits:
            counts[unitString] = counts.get( unitString, 0) +1
            counts[bonusCategoryName] += 1
        counts['military'] = sum( c for key, c in counts.iteritems() if key in set( [ 'false laroth', 'archer', 'adept', 'melee', 'recon', 'mounted', ] ) )
        templateHelpString = 'Casting this spell will give %(military)i military units, %(supplies)i supplies and %(population)i awakened.'
        return templateHelpString % counts

I also made a few additions, mostly for fun.

  • Plot selection is differently random.
  • The false Laroth gets bronze or iron weapons, making him more like a mini-hero.
  • Two more mini heroes, Reche of the False Heart and Otal the Faded.
  • Arcane units get +2 experience for each mana tech.
  • Adept units are upgraded to Necromancers with Sorcery. Reche is the only adept unit given, but the point was more to add the option.
  • Archer units get Flaming Arrows with Elementalism.
  • Melee units get Enchanted Blade with Mithril Weapons.
  • Personal names for a few units. They also get the Adventurer promotion, to change the group size to 1.

Also, considering the wildly varying effects of this spell, there should probably be a PyHelp, which I added. Just add spellSpiralGate to the PyHelp field. That part will need an editor though, the text is uninspiring and could give a lot more information.
 
What if you received less military units as time went on, but the military units you do get start off with more experience? (and upgrading in unit-type depending on the techs the player has) So, in the late-game, you could potentially have another hero unit, along with a legion of Awakened. Conversely, if you cast it as soon as you can, you would have a few Awakened and a small army.


Korrina's Worldspell could be something that empowers the Risen Emperor somehow (better the later you use it), with... some sort of different effect if he isn't released yet, like empowering a building of some sort.

Why Themoch would be unable to use his "Deliverance" ability when Korinna is leading, we shall never know... I guess only the Risen Emperor was clever enough to think it up.


p.s. Yes, I like heroes and hero-like units...
 
I fixed the obvious bugs, changed a few names and refactored it so that the code is clearer, to me at least.

Wow, thanks! It's far less clear to me, but I'm sure I'll get it. Enough, at least. And since I didn't really understand the previous one it's not like anything's been lost. :)

The help function in particular is a great addition.

[*]Plot selection is differently random.

Thanks for the comments there. I'll probably de-empahsize continent size, and maybe increase the distance factor.

Hmm... though I just realized the ability to get some Awakened and a few military units on an unoccupied continent at Sorcery (currently spell's prereq tech) might be a big balance problem. Though ideally - in the "It's a feature, not a bug." tradition - it's just an opportunity. FF barbarians, demons, etc. could might make it sufficiently dangerous. And of course if you're running Godking it could be pricey.

[*]The false Laroth gets bronze or iron weapons, making him more like a mini-hero.
[*]Two more mini heroes, Reche of the False Heart and Otal the Faded.

I like the additions to !=Laroth, and an Illusionary Ghostwalker is too cool to not keep.

[*]Melee units get Enchanted Blade with Mithril Weapons.

Nice touch.

So, in the late-game, you could potentially have another hero unit, along with a legion of Awakened. Conversely, if you cast it as soon as you can, you would have a few Awakened and a small army.

Interesting idea.

Korrina's Worldspell...

For now I'm most likely going to have just 1 Scion WS. Even though this one is tied closely to the Emperor he's enough of a Presence even when Korrina is the Leader. ("Immanent yet immaterial." is how Melante puts it.)
 
Hmm... though I just realized the ability to get some Awakened and a few military units on an unoccupied continent at Sorcery (currently spell's prereq tech) might be a big balance problem.

I think fallow pretty much negates any advantage that gives. The colony wont be able to grow. Besides, the AI is so horrible with navies that I only play on one continent maps.
 
For now I'm most likely going to have just 1 Scion WS. Even though this one is tied closely to the Emperor he's enough of a Presence even when Korrina is the Leader. ("Immanent yet immaterial." is how Melante puts it.)

So what about when Xiven leads? You're going to make me do a custom worldspell too, aren't you.
 
I can't get the help text to display. Even after remembering to add the "True" argument to spellinfos. I know the "help = true" code is running because I get errors when I mess with it, but that's the only headway I've made.


So what about when Xiven leads? You're going to make me do a custom worldspell too, aren't you.

Two. You wouldn't leave out Korrina, would you? :)
 
I can't get the help text to display.

That's my fault. I didn't notice the PyHelp is called in a slightly different way from the req and cast variants.

I updated the code in my original post. The PyHelp should be just the name of the function, spellSpiralGate in this case.
 
Looking for some balance feedback:

Units given from all Ancient tech. (Easy to total up thanks to odalrick.)
Military: 5
Supplies: 0
Awakened: 0

All Classical (Sorcery level and a few past.)
Military: 20
Supplies: 6
Awakened: 13

All Med.:
Military: 42
Supplies: 15
Awakened: 43 (A lot of Awakened in the high-level Religious techs.)

ATM the spell will reliably place the units quite far from the Scion capital, assuming any empty plots.
 
Looking for some balance feedback:

Units given from all Ancient tech. (Easy to total up thanks to odalrick.)
Military: 5
Supplies: 0
Awakened: 0

All Classical (Sorcery level and a few past.)
Military: 20
Supplies: 6
Awakened: 13

All Med.:
Military: 42
Supplies: 15
Awakened: 43 (A lot of Awakened in the high-level Religious techs.)

ATM the spell will reliably place the units quite far from the Scion capital, assuming any empty plots.

Looks pretty good to me. I like the balance between military units and population.
 
Top Bottom