Archipelago_mst.py 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951
  1. #
  2. # FILE: Archipelago_mst.py
  3. # AUTHOR: Bob Thomas (Sirian)
  4. # CONTRIB: Soren Johnson
  5. # PURPOSE: Global map script - Generates a world full of random islands.
  6. #-----------------------------------------------------------------------------
  7. # Copyright (c) 2005 Firaxis Games, Inc. All rights reserved.
  8. #-----------------------------------------------------------------------------
  9. # Script modified for compatibility with MapScriptTools.
  10. #
  11. # 1.1.0 Terkhen 12/10/2016
  12. # - Added, compatibility with MapScriptTools
  13. # - Added, compatibility with 'Planetfall'
  14. # - Added, compatibility with 'Mars Now!'
  15. # - Added, save/load map options.
  16. # - Added, compatibility with RandomMap.
  17. # - Added, tubular wrapping.
  18. # - Added, resource balancement map option.
  19. # - Added, expanded coastal waters map option (like in Civilization V)
  20. # - Added, team start map option
  21. # - Added [Mars Now!], new map option: 'Sands of Mars'/'Terraformed Mars'
  22. # - Added, Marsh terrain, if supported by mod
  23. # - Added, Deep Ocean terrain, if supported by mod
  24. # - Added, rivers on islands and from lakes
  25. # - Added, Map Regions ( BigDent, BigBog, ElementalQuarter, LostIsle )
  26. # - Added, Map Features ( Kelp, HauntedLands, CrystalPlains )
  27. # - Changed, use TXT strings instead of hardcoded ones.
  28. from CvPythonExtensions import *
  29. import CvMapGeneratorUtil
  30. map = CyMap()
  31. gc = CyGlobalContext()
  32. ################################################################
  33. ## MapScriptTools Interface by Temudjin START
  34. ################################################################
  35. import MapScriptTools as mst
  36. balancer = mst.bonusBalancer
  37. def getVersion():
  38. return "1.1.0_mst"
  39. def getDescription():
  40. return "TXT_KEY_MAP_SCRIPT_ARCHIPELAGO_DESCR"
  41. # #################################################################################################
  42. # ######## randomMapBeforeInit() - Starts the map-generation process, called by RandomMap to set
  43. # ######## the map options
  44. # #################################################################################################
  45. def randomMapBeforeInit(moWorldShape, moResources, moCoastalWaters, moTeamStart, moMarsTheme):
  46. print "-- randomMapBeforeInit()"
  47. # Avoid errors while printing custom options.
  48. global op
  49. op = {}
  50. # Map options of this script
  51. global mapOptionLandmass, mapOptionResources
  52. global mapOptionWorldShape, mapOptionCoastalWaters, mapOptionTeamStart, mapOptionMarsTheme
  53. # Options chosen in Random Map
  54. mapOptionWorldShape = moWorldShape
  55. mapOptionResources = moResources
  56. mapOptionCoastalWaters = moCoastalWaters
  57. mapOptionTeamStart = moTeamStart
  58. mapOptionMarsTheme = moMarsTheme
  59. # All other options are chosen randomly.
  60. mapOptionLandmass = gc.getGame().getMapRand().get(3, "Archipelago.randomMapBeforeInit(), mapOptionLandmass")
  61. # #################################################################################################
  62. # ######## beforeInit() - Starts the map-generation process, called after the map-options are known
  63. # ######## - map dimensions, latitudes and wrapping status are not known yet
  64. # ######## - handle map specific options
  65. # #################################################################################################
  66. def beforeInit():
  67. print "-- beforeInit()"
  68. # Selected map options
  69. global mapOptionLandmass, mapOptionResources
  70. global mapOptionWorldShape, mapOptionCoastalWaters, mapOptionTeamStart, mapOptionMarsTheme
  71. mapOptionLandmass = map.getCustomMapOption(2)
  72. mapOptionWorldShape = map.getCustomMapOption(0)
  73. mapOptionResources = map.getCustomMapOption(1)
  74. mapOptionCoastalWaters = mst.iif(mst.bPfall, None, map.getCustomMapOption(3))
  75. mapOptionTeamStart = mst.iif(mst.bMars, None, map.getCustomMapOption( mst.iif(mst.bPfall, 3, 4) ))
  76. mapOptionMarsTheme = mst.iif(mst.bMars, map.getCustomMapOption(4), None)
  77. # #######################################################################################
  78. # ######## beforeGeneration() - Called from system after user input is finished
  79. # ######## - define your latitude formula, get the map-version
  80. # ######## - create map options info string
  81. # ######## - initialize the MapScriptTools
  82. # ######## - initialize MapScriptTools.BonusBalancer
  83. # #######################################################################################
  84. def beforeGeneration():
  85. print "-- beforeGeneration()"
  86. # Create mapInfo string
  87. mapInfo = ""
  88. # Backup current language
  89. iLanguage = CyGame().getCurrentLanguage()
  90. # Force english language for logs
  91. CyGame().setCurrentLanguage(0)
  92. for opt in range( getNumCustomMapOptions() ):
  93. nam = getCustomMapOptionName( [opt] )
  94. sel = map.getCustomMapOption( opt )
  95. txt = getCustomMapOptionDescAt( [opt,sel] )
  96. mapInfo += "%27s: %s\n" % ( nam, txt )
  97. # Restore current language
  98. CyGame().setCurrentLanguage(iLanguage)
  99. # Obtain the map options in use.
  100. lMapOptions = []
  101. for opt in range( getNumCustomMapOptions() ):
  102. iValue = 0 + map.getCustomMapOption( opt )
  103. lMapOptions.append( iValue )
  104. # Save used map options.
  105. mst.mapOptionsStorage.writeConfig(lMapOptions)
  106. # Initialize MapScriptTools
  107. mst.getModInfo( getVersion(), None, mapInfo )
  108. # Initialize bonus balancing
  109. balancer.initialize( mapOptionResources == 1 ) # balance boni if desired, place missing boni, move minerals
  110. # Determine global Mars Theme
  111. mst.bSandsOfMars = (mapOptionMarsTheme == 0)
  112. # #######################################################################################
  113. # ######## generatePlotTypes() - Called from system after beforeGeneration()
  114. # ######## - FIRST STAGE in 'Generate Map'
  115. # ######## - creates an array of plots (ocean, land, hills, peak) in the map dimensions
  116. # #######################################################################################
  117. def generatePlotTypes():
  118. "Generates a very grainy world so we get lots of islands."
  119. fractal_world = ArchipelagoFractalWorld()
  120. NiTextOut("Setting Plot Types (Python Archipelago) ...")
  121. # Get user input.
  122. userInputLandmass = mapOptionLandmass
  123. if userInputLandmass == 2: # Tiny Islands
  124. fractal_world.initFractal(continent_grain = 5, rift_grain = -1, has_center_rift = False, polar = True)
  125. return fractal_world.generatePlotTypes(grain_amount = 4)
  126. elif userInputLandmass == 0: # Snaky Continents
  127. fractal_world.initFractal(continent_grain = 3, rift_grain = -1, has_center_rift = False, polar = True)
  128. return fractal_world.generatePlotTypes(grain_amount = 4)
  129. else: # Archipelago
  130. fractal_world.initFractal(continent_grain = 4, rift_grain = -1, has_center_rift = False, polar = True)
  131. return fractal_world.generatePlotTypes(grain_amount = 4)
  132. # ######################################################################################
  133. # ######## generateTerrainTypes() - Called from system after generatePlotTypes()
  134. # ######## - SECOND STAGE in 'Generate Map'
  135. # ######## - creates an array of terrains (desert,plains,grass,...) in the map dimensions
  136. # #######################################################################################
  137. def generateTerrainTypes():
  138. # Planetfall: more highlands
  139. mst.planetFallMap.buildPfallHighlands()
  140. # Prettify map: Connect small islands
  141. mst.mapPrettifier.bulkifyIslands()
  142. # Prettify map: most coastal peaks -> hills < Archipelago has its own methods for removing peaks on coast.
  143. # mst.mapPrettifier.hillifyCoast()
  144. # Generate terrain
  145. if mst.bMars:
  146. # There are two possible scenarios for 'Mars Now!': either water is converted to desert or not
  147. iDesert = mst.iif(mst.bSandsOfMars, 16, 32)
  148. terrainGen = mst.MST_TerrainGenerator_Mars(iDesert)
  149. else:
  150. terrainGen = mst.MST_TerrainGenerator()
  151. terrainTypes = terrainGen.generateTerrain()
  152. return terrainTypes
  153. # #######################################################################################
  154. # ######## addRivers() - Called from system after generateTerrainTypes()
  155. # ######## - THIRD STAGE in 'Generate Map'
  156. # ######## - puts rivers on the map
  157. # #######################################################################################
  158. def addRivers():
  159. print "-- addRivers()"
  160. # Generate marsh-terrain
  161. mst.marshMaker.convertTerrain()
  162. # Expand coastal waters
  163. if mapOptionCoastalWaters == 1:
  164. mst.mapPrettifier.expandifyCoast()
  165. # Build big dents with very small probability.
  166. mst.mapRegions.buildBigDents(1, 20)
  167. # Build big bogs with very small probability.
  168. mst.mapRegions.buildBigBogs(1, 20)
  169. # Generate DeepOcean-terrain if mod allows for it
  170. mst.deepOcean.buildDeepOcean()
  171. # Prettify the map - create better connected deserts and plains
  172. if not mst.bPfall:
  173. mst.mapPrettifier.lumpifyTerrain( mst.etDesert, mst.etPlains, mst.etGrass )
  174. if not mst.bMars:
  175. mst.mapPrettifier.lumpifyTerrain( mst.etPlains, mst.etDesert, mst.etGrass )
  176. # No standard rivers on Mars
  177. if not mst.bMars:
  178. # Put rivers on the map.
  179. CyPythonMgr().allowDefaultImpl()
  180. # Put rivers on small islands
  181. mst.riverMaker.islandRivers()
  182. # #######################################################################################
  183. # ######## addLakes() - Called from system after addRivers()
  184. # ######## - FOURTH STAGE in 'Generate Map'
  185. # ######## - puts lakes on the map
  186. # #######################################################################################
  187. def addLakes():
  188. print "-- addLakes()"
  189. if not mst.bMars:
  190. CyPythonMgr().allowDefaultImpl()
  191. # #######################################################################################
  192. # ######## addFeatures() - Called from system after addLakes()
  193. # ######## - FIFTH STAGE in 'Generate Map'
  194. # ######## - puts features on the map
  195. # #######################################################################################
  196. def addFeatures():
  197. NiTextOut("Adding Features (Python Archipelago) ...")
  198. # Original addFeatures:
  199. # Remove all peaks along the coasts, before adding Features, Bonuses, Goodies, etc.
  200. # The peaks were bisecting too many islands.
  201. iW = map.getGridWidth()
  202. iH = map.getGridHeight()
  203. for plotIndex in range(iW * iH):
  204. pPlot = map.plotByIndex(plotIndex)
  205. if pPlot.isPeak() and pPlot.isCoastalLand():
  206. # If a peak is along the coast, change to hills and recalc.
  207. pPlot.setPlotType(PlotTypes.PLOT_HILLS, true, true)
  208. # Rest of the code:
  209. # connectifyLakes takes a lot of time due to Archipelago being mostly water. Given that it will not be useful in
  210. # nearly all cases, it has just been removed.
  211. # mst.mapPrettifier.connectifyLakes()
  212. # Sprout rivers from lakes.
  213. mst.riverMaker.buildRiversFromLake( None, 40, 1, 2 ) # all lakes, 40% chance, 1 rivers, lakesize>=2
  214. featuregen = mst.MST_FeatureGenerator()
  215. featuregen.addFeatures()
  216. # Prettify the map - transform coastal volcanos; default: 66% chance
  217. mst.mapPrettifier.beautifyVolcanos()
  218. # Mars Now!: lumpify sandstorms
  219. if mst.bMars: mst.mapPrettifier.lumpifyTerrain( mst.efSandStorm, FeatureTypes.NO_FEATURE )
  220. # Planetfall: handle shelves and trenches
  221. if mst.bPfall: mst.planetFallMap.buildPfallOcean()
  222. # FFH: build ElementalQuarter; default: 5% chance
  223. mst.mapRegions.buildElementalQuarter()
  224. # ############################################################################################
  225. # ######## normalizeStartingPlotLocations() - Called from system after starting-plot selection
  226. # ######## - FIRST STAGE in 'Normalize Starting-Plots'
  227. # ######## - change assignments to starting-plots
  228. # ############################################################################################
  229. def normalizeStartingPlotLocations():
  230. print "-- normalizeStartingPlotLocations()"
  231. # build Lost Isle
  232. # - this region needs to be placed after starting-plots are first assigned. Chance increased in Archipelago to 66%.
  233. mst.mapRegions.buildLostIsle(chance = 66, minDist =7, bAliens = mst.choose(33, True, False))
  234. if mst.bMars or mapOptionTeamStart == 0:
  235. # Mars Now! uses no teams. by default civ places teams near to each other
  236. CyPythonMgr().allowDefaultImpl()
  237. elif mapOptionTeamStart == 1:
  238. mst.teamStart.placeTeamsTogether( False, True ) # shuffle starting-plots to separate teams
  239. elif mapOptionTeamStart == 2:
  240. mst.teamStart.placeTeamsTogether( True, True ) # randomize starting-plots (may be near or not)
  241. else:
  242. mst.teamStart.placeTeamsTogether( False, False ) # leave starting-plots alone
  243. # ############################################################################################
  244. # ######## normalizeAddRiver() - Called from system after normalizeStartingPlotLocations()
  245. # ######## - SECOND STAGE in 'Normalize Starting-Plots'
  246. # ######## - add some rivers if needed
  247. # ############################################################################################
  248. def normalizeAddRiver():
  249. print "-- normalizeAddRiver()"
  250. if not mst.bMars:
  251. CyPythonMgr().allowDefaultImpl()
  252. # ############################################################################################
  253. # ######## normalizeRemovePeaks() - Called from system after normalizeAddRiver()
  254. # ######## - THIRD STAGE in 'Normalize Starting-Plots'
  255. # ######## - remove some peaks if needed
  256. # ############################################################################################
  257. def normalizeRemovePeaks():
  258. print "-- normalizeRemovePeaks()"
  259. return None
  260. # ############################################################################################
  261. # ######## normalizeAddLakesRiver() - Called from system after normalizeRemovePeaks()
  262. # ######## - FOURTH STAGE in 'Normalize Starting-Plots'
  263. # ######## - add some lakes if needed
  264. # ############################################################################################
  265. def normalizeAddLakes():
  266. print "-- normalizeAddLakes()"
  267. if not (mst.bMars and mst.bSandsOfMars):
  268. CyPythonMgr().allowDefaultImpl()
  269. # ############################################################################################
  270. # ######## normalizeRemoveBadFeatures() - Called from system after normalizeAddLakes()
  271. # ######## - FIFTH STAGE in 'Normalize Starting-Plots'
  272. # ######## - remove bad features if needed
  273. # ############################################################################################
  274. def normalizeRemoveBadFeatures():
  275. print "-- normalizeRemoveBadFeatures()"
  276. return None
  277. # ############################################################################################
  278. # ######## normalizeRemoveBadTerrain() - Called from system after normalizeRemoveBadFeatures()
  279. # ######## - SIXTH STAGE in 'Normalize Starting-Plots'
  280. # ######## - change bad terrain if needed
  281. # ############################################################################################
  282. def normalizeRemoveBadTerrain():
  283. print "-- normalizeRemoveBadTerrain()"
  284. if not (mst.bPfall or mst.bMars):
  285. CyPythonMgr().allowDefaultImpl()
  286. # ############################################################################################
  287. # ######## normalizeAddFoodBonuses() - Called from system after normalizeRemoveBadTerrain()
  288. # ######## - SEVENTH STAGE in 'Normalize Starting-Plots'
  289. # ######## - add food if needed
  290. # ############################################################################################
  291. def normalizeAddFoodBonuses():
  292. print "-- normalizeAddFoodBonuses()"
  293. if mst.bMars:
  294. CyPythonMgr().allowDefaultImpl()
  295. # ############################################################################################
  296. # ######## normalizeAddGoodTerrain() - Called from system after normalizeAddFoodBonuses()
  297. # ######## - EIGHTH STAGE in 'Normalize Starting-Plots'
  298. # ######## - add good terrain if needed
  299. # ############################################################################################
  300. def normalizeAddGoodTerrain():
  301. print "-- normalizeAddGoodTerrain()"
  302. if not (mst.bPfall or mst.bMars):
  303. CyPythonMgr().allowDefaultImpl()
  304. # ############################################################################################
  305. # ######## normalizeAddExtras() - Called from system after normalizeAddGoodTerrain()
  306. # ######## - NINTH and LAST STAGE in 'Normalize Starting-Plots'
  307. # ######## - last chance to adjust starting-plots
  308. # ######## - called before startHumansOnSameTile(), which is the last map-function so called
  309. # ############################################################################################
  310. def normalizeAddExtras():
  311. print "-- normalizeAddExtras()"
  312. # Balance boni, place missing boni, move minerals
  313. balancer.normalizeAddExtras()
  314. # Do the default housekeeping
  315. CyPythonMgr().allowDefaultImpl()
  316. # Make sure marshes are on flatlands
  317. mst.marshMaker.normalizeMarshes()
  318. # Give extras to special regions
  319. mst.mapRegions.addRegionExtras()
  320. # Place special features on map
  321. mst.featurePlacer.placeFeatures()
  322. # Kill ice on warm edges
  323. mst.mapPrettifier.deIcifyEdges()
  324. # Print plotMap and differencePlotMap
  325. mst.mapPrint.buildPlotMap( True, "normalizeAddExtras()" )
  326. # Print areaMap
  327. mst.mapPrint.buildAreaMap( True, "normalizeAddExtras()" )
  328. # Print terrainMap
  329. mst.mapPrint.buildTerrainMap( True, "normalizeAddExtras()" )
  330. # Print featureMap
  331. mst.mapPrint.buildFeatureMap( True, "normalizeAddExtras()" )
  332. # Print bonusMap
  333. mst.mapPrint.buildBonusMap( False, "normalizeAddExtras()" )
  334. # Print manaMap if FFH
  335. if mst.bFFH: mst.mapPrint.buildBonusMap( False, "normalizeAddExtras():Mana", None, mst.mapPrint.manaDict )
  336. # Print riverMap
  337. mst.mapPrint.buildRiverMap( True, "normalizeAddExtras()" )
  338. # Print mod and map statistics
  339. mst.mapStats.mapStatistics()
  340. # ############################################################################
  341. # ######## minStartingDistanceModifier() - Called from system at various times
  342. # ######## - FIRST STAGE in 'Select Starting-Plots'
  343. # ######## - adjust starting-plot distances
  344. # ############################################################################
  345. def minStartingDistanceModifier():
  346. if mst.bPfall: return -25
  347. return 0
  348. ################################################################
  349. ## Custom Map Option Interface by Temudjin START
  350. ################################################################
  351. def setCustomOptions():
  352. """ Set all custom options in one place """
  353. global op # { optionID: Name, OptionList, Default, RandomOpt }
  354. # Initialize options to the default values.
  355. lMapOptions = [1, 0, 1, 0, 0]
  356. # Try to read map options from the cfgFile.
  357. mst.mapOptionsStorage.initialize(lMapOptions, 'Archipelago')
  358. lMapOptions = mst.mapOptionsStorage.readConfig()
  359. optionLandmass = ["TXT_KEY_MAP_ARCHIPELAGO_LANDMASS_SNAKY", "TXT_KEY_MAP_ARCHIPELAGO_LANDMASS_ARCHIPELAGO", "TXT_KEY_MAP_ARCHIPELAGO_LANDMASS_TINY_ISLANDS"]
  360. optionShape = ["TXT_KEY_MAP_SCRIPT_WORLD_WRAP_FLAT", "TXT_KEY_MAP_SCRIPT_WORLD_WRAP_CYLINDRICAL", "TXT_KEY_MAP_SCRIPT_WORLD_WRAP_TUBULAR", "TXT_KEY_MAP_SCRIPT_WORLD_WRAP_TOROIDAL"]
  361. op = {
  362. 0: ["TXT_KEY_MAP_SCRIPT_WORLD_WRAP", optionShape, lMapOptions[0], True],
  363. 1: ["TXT_KEY_MAP_RESOURCES", ["TXT_KEY_MAP_RESOURCES_STANDARD", "TXT_KEY_MAP_RESOURCES_BALANCED"], lMapOptions[1], True],
  364. 2: ["TXT_KEY_MAP_ARCHIPELAGO_LANDMASS_TYPE", optionLandmass, lMapOptions[2], False],
  365. 3: ["TXT_KEY_MAP_COASTS", ["TXT_KEY_MAP_COASTS_STANDARD", "TXT_KEY_MAP_COASTS_EXPANDED"], lMapOptions[3], True],
  366. "Hidden": 2
  367. }
  368. if mst.bPfall:
  369. op[3] = ["TXT_KEY_MAP_TEAM_START", ["TXT_KEY_MAP_TEAM_START_NEIGHBORS", "TXT_KEY_MAP_TEAM_START_SEPARATED", "TXT_KEY_MAP_TEAM_START_RANDOM"], lMapOptions[3], False]
  370. elif mst.bMars:
  371. op[4] = ["TXT_KEY_MAP_MARS_THEME", ["TXT_KEY_MAP_MARS_THEME_SANDS_OF_MARS", "TXT_KEY_MAP_MARS_THEME_TERRAFORMED_MARS"], lMapOptions[4], False]
  372. else:
  373. op[4] = ["TXT_KEY_MAP_TEAM_START", ["TXT_KEY_MAP_TEAM_START_NEIGHBORS", "TXT_KEY_MAP_TEAM_START_SEPARATED", "TXT_KEY_MAP_TEAM_START_RANDOM"], lMapOptions[4], False]
  374. mst.printDict(op, "Archipelago Map Options:")
  375. def isAdvancedMap():
  376. """ This map should show up in simple mode """
  377. return 0
  378. # first function to be called by the map building process
  379. def getNumHiddenCustomMapOptions():
  380. """ Default is used for the last n custom-options in 'Play Now' mode. """
  381. setCustomOptions() # Define Options
  382. return op["Hidden"]
  383. def getNumCustomMapOptions():
  384. """ Number of different user-defined options for this map """
  385. return len( op ) - 1
  386. def getCustomMapOptionName(argsList):
  387. """ Returns name of specified option """
  388. optionID = argsList[0]
  389. translated_text = unicode(CyTranslator().getText(op[optionID][0], ()))
  390. return translated_text
  391. def getNumCustomMapOptionValues(argsList):
  392. """ Number of different choices for a particular setting """
  393. optionID = argsList[0]
  394. return len( op[optionID][1] )
  395. def getCustomMapOptionDescAt(argsList):
  396. """ Returns name of value of option at specified row """
  397. optionID = argsList[0]
  398. valueID = argsList[1]
  399. translated_text = unicode(CyTranslator().getText(op[optionID][1][valueID], ()))
  400. return translated_text
  401. def getCustomMapOptionDefault(argsList):
  402. """ Returns default value of specified option """
  403. optionID = argsList[0]
  404. return op[optionID][2]
  405. def isRandomCustomMapOption(argsList):
  406. """ Returns a flag indicating whether a random option should be provided """
  407. optionID = argsList[0]
  408. return op[optionID][3]
  409. ################################################################
  410. ## Interfaces by Temudjin END
  411. ################################################################
  412. '''
  413. def isClimateMap():
  414. """ Uses the Climate options """
  415. return True
  416. def isSeaLevelMap():
  417. """ Uses the Sea Level options """
  418. return True
  419. def getTopLatitude():
  420. """ Default is 90. 75 is past the Arctic Circle """
  421. return 90
  422. def getBottomLatitude():
  423. """ Default is -90. -75 is past the Antartic Circle """
  424. return -90
  425. '''
  426. def getWrapX():
  427. return ( mapOptionWorldShape in [1,3] )
  428. def getWrapY():
  429. return ( mapOptionWorldShape in [2,3] )
  430. ##########
  431. class ArchipelagoFractalWorld(CvMapGeneratorUtil.FractalWorld):
  432. def checkForOverrideDefaultUserInputVariances(self):
  433. # Overriding peak value to counterbalance not having any peaks along the coasts.
  434. extraPeaks = 1 + mapOptionLandmass
  435. self.peakPercent = min(100, self.peakPercent + (15 * extraPeaks))
  436. self.peakPercent = max(0, self.peakPercent)
  437. # Note, the peaks along the coast are not removed until addFeatures()
  438. return
  439. def assignStartingPlots():
  440. # Custom start plot finder for Archipelago (high grain) maps.
  441. # Set up start plot data for all players then access later.
  442. dice = gc.getGame().getMapRand()
  443. iW = map.getGridWidth()
  444. iH = map.getGridHeight()
  445. # Success flag. Set to false if regional assignment fails or is not to be used.
  446. global bSuccessFlag
  447. global start_plots
  448. bSuccessFlag = True
  449. # Check for Snaky Continents user option or invalid number of players. If found, use normal start plot finder!
  450. userInputLandmass = mapOptionLandmass
  451. iPlayers = gc.getGame().countCivPlayersEverAlive()
  452. if userInputLandmass == 0:
  453. CyPythonMgr().allowDefaultImpl()
  454. return
  455. if iPlayers < 2 or iPlayers > 18:
  456. bSuccessFlag = False
  457. CyPythonMgr().allowDefaultImpl()
  458. return
  459. # List of number of regions to be used, indexed by number of players.
  460. if userInputLandmass == 2: # Tiny Islands will have fewer "dud" regions.
  461. configs = [0, 3, 3, 3, 6, 6, 8, 8, 12, 12, 12, 15, 15, 15, 20, 20, 20, 20, 24]
  462. else: # Standard Archipelago needs to account for regions that may be duds.
  463. configs = [0, 3, 3, 6, 6, 8, 8, 12, 12, 15, 15, 15, 20, 20, 20, 24, 24, 24, 24]
  464. iNumRegions = configs[iPlayers]
  465. # Obtain the minimum crow-flies distance figures [minX, minY] for this map size and number of players.
  466. minimums = {3: [0.1, 0.2],
  467. 6: [0.1, 0.125],
  468. 8: [0.07, 0.125],
  469. 12: [0.07, 0.1],
  470. 15: [0.06, 0.1],
  471. 20: [0.06, 0.06],
  472. 24: [0.05, 0.05]}
  473. [minLon, minLat] = minimums[iNumRegions]
  474. minX = max(3, int(minLon * iW))
  475. minY = max(3, int(minLat * iH))
  476. #print "minimums", minX, minY, "-o-o-"
  477. # Templates are nested by keys: {NumRegions: {RegionID: [WestLon, EastLon, SouthLat, NorthLat]}}
  478. templates = {3: {0: [0.0, 0.333, 0.0, 1.0],
  479. 1: [0.333, 0.667, 0.0, 1.0],
  480. 2: [0.667, 1.0, 0.0, 1.0]},
  481. 6: {0: [0.0, 0.333, 0.0, 0.5],
  482. 1: [0.333, 0.667, 0.0, 0.5],
  483. 2: [0.667, 1.0, 0.0, 0.5],
  484. 3: [0.0, 0.333, 0.5, 1.0],
  485. 4: [0.333, 0.667, 0.5, 1.0],
  486. 5: [0.667, 1.0, 0.5, 1.0]},
  487. 8: {0: [0.0, 0.25, 0.0, 0.5],
  488. 1: [0.25, 0.5, 0.0, 0.5],
  489. 2: [0.5, 0.75, 0.0, 0.5],
  490. 3: [0.75, 1.0, 0.0, 0.5],
  491. 4: [0.0, 0.25, 0.5, 1.0],
  492. 5: [0.25, 0.5, 0.5, 1.0],
  493. 6: [0.5, 0.75, 0.5, 1.0],
  494. 7: [0.75, 1.0, 0.5, 1.0]},
  495. 12: {0: [0.0, 0.25, 0.0, 0.35],
  496. 1: [0.25, 0.5, 0.0, 0.35],
  497. 2: [0.5, 0.75, 0.0, 0.35],
  498. 3: [0.75, 1.0, 0.0, 0.35],
  499. 4: [0.0, 0.25, 0.35, 0.63],
  500. 5: [0.25, 0.5, 0.35, 0.63],
  501. 6: [0.5, 0.75, 0.35, 0.63],
  502. 7: [0.75, 1.0, 0.35, 0.63],
  503. 8: [0.0, 0.25, 0.63, 1.0],
  504. 9: [0.25, 0.5, 0.63, 1.0],
  505. 10: [0.5, 0.75, 0.63, 1.0],
  506. 11: [0.75, 1.0, 0.63, 1.0]},
  507. 15: {0: [0.0, 0.2, 0.0, 0.35],
  508. 1: [0.2, 0.4, 0.0, 0.35],
  509. 2: [0.4, 0.6, 0.0, 0.35],
  510. 3: [0.6, 0.8, 0.0, 0.35],
  511. 4: [0.8, 1.0, 0.0, 0.35],
  512. 5: [0.0, 0.2, 0.35, 0.63],
  513. 6: [0.2, 0.4, 0.35, 0.63],
  514. 7: [0.4, 0.6, 0.35, 0.63],
  515. 8: [0.6, 0.8, 0.35, 0.63],
  516. 9: [0.8, 1.0, 0.35, 0.63],
  517. 10: [0.0, 0.2, 0.63, 1.0],
  518. 11: [0.2, 0.4, 0.63, 1.0],
  519. 12: [0.4, 0.6, 0.63, 1.0],
  520. 13: [0.6, 0.8, 0.63, 1.0],
  521. 14: [0.8, 1.0, 0.63, 1.0]},
  522. 20: {0: [0.0, 0.2, 0.0, 0.3],
  523. 1: [0.2, 0.4, 0.0, 0.3],
  524. 2: [0.4, 0.6, 0.0, 0.3],
  525. 3: [0.6, 0.8, 0.0, 0.3],
  526. 4: [0.8, 1.0, 0.0, 0.3],
  527. 5: [0.0, 0.2, 0.3, 0.5],
  528. 6: [0.2, 0.4, 0.3, 0.5],
  529. 7: [0.4, 0.6, 0.3, 0.5],
  530. 8: [0.6, 0.8, 0.3, 0.5],
  531. 9: [0.8, 1.0, 0.3, 0.5],
  532. 10: [0.0, 0.2, 0.5, 0.7],
  533. 11: [0.2, 0.4, 0.5, 0.7],
  534. 12: [0.4, 0.6, 0.5, 0.7],
  535. 13: [0.6, 0.8, 0.5, 0.7],
  536. 14: [0.8, 1.0, 0.5, 0.7],
  537. 15: [0.0, 0.2, 0.7, 1.0],
  538. 16: [0.2, 0.4, 0.7, 1.0],
  539. 17: [0.4, 0.6, 0.7, 1.0],
  540. 18: [0.6, 0.8, 0.7, 1.0],
  541. 19: [0.8, 1.0, 0.7, 1.0]},
  542. 24: {0: [0.0, 0.167, 0.0, 0.3],
  543. 1: [0.167, 0.333, 0.0, 0.3],
  544. 2: [0.333, 0.5, 0.0, 0.3],
  545. 3: [0.5, 0.667, 0.0, 0.3],
  546. 4: [0.667, 0.833, 0.0, 0.3],
  547. 5: [0.833, 1.0, 0.0, 0.3],
  548. 6: [0.0, 0.167, 0.3, 0.5],
  549. 7: [0.167, 0.333, 0.3, 0.5],
  550. 8: [0.333, 0.5, 0.3, 0.5],
  551. 9: [0.5, 0.667, 0.3, 0.5],
  552. 10: [0.667, 0.833, 0.3, 0.5],
  553. 11: [0.833, 1.0, 0.3, 0.5],
  554. 12: [0.0, 0.167, 0.5, 0.7],
  555. 13: [0.167, 0.333, 0.5, 0.7],
  556. 14: [0.333, 0.5, 0.5, 0.7],
  557. 15: [0.5, 0.667, 0.5, 0.7],
  558. 16: [0.667, 0.833, 0.5, 0.7],
  559. 17: [0.833, 1.0, 0.5, 0.7],
  560. 18: [0.0, 0.167, 0.7, 1.0],
  561. 19: [0.167, 0.333, 0.7, 1.0],
  562. 20: [0.333, 0.5, 0.7, 1.0],
  563. 21: [0.5, 0.667, 0.7, 1.0],
  564. 22: [0.667, 0.833, 0.7, 1.0],
  565. 23: [0.833, 1.0, 0.7, 1.0]}
  566. }
  567. # End of template data.
  568. # region_data: [WestX, EastX, SouthY, NorthY,
  569. # numLandPlotsinRegion, numCoastalPlotsinRegion,
  570. # numOceanPlotsinRegion, iRegionNetYield,
  571. # iNumLandAreas, iNumPlotsinRegion]
  572. region_data = []
  573. region_best_areas = []
  574. region_yields = []
  575. sorting_regions = []
  576. for regionLoop in range(iNumRegions):
  577. # Region dimensions
  578. [iWestLon, iEastLon, iSouthLat, iNorthLat] = templates[iNumRegions][regionLoop]
  579. iWestX = int(iW * iWestLon)
  580. iEastX = int(iW * iEastLon) - 1
  581. iSouthY = int(iH * iSouthLat)
  582. iNorthY = int(iH * iNorthLat) -1
  583. # Plot and Area info.
  584. iNumLandPlots = 0
  585. iNumCoastalPlots = 0
  586. iNumOceanPlots = 0
  587. iRegionNetYield = 0
  588. iNumLandAreas = 0
  589. iNumPlotsinRegion = 0
  590. land_areas = []
  591. land_area_plots = {}
  592. land_area_yield = {}
  593. # Cycle through all plots in the region.
  594. for x in range(iWestX, iEastX + 1):
  595. for y in range(iSouthY, iNorthY + 1):
  596. iNumPlotsinRegion += 1
  597. i = y * iW + x
  598. pPlot = map.plot(x, y)
  599. if pPlot.getBonusType(-1) != -1: # Count any bonus resource as added value
  600. iRegionNetYield += 2
  601. if pPlot.isWater(): # Water plot
  602. iFertileCheck = mst.calculateBestNatureYield(pPlot, YieldTypes.YIELD_FOOD)
  603. if iFertileCheck > 1: # If the plot has extra food, count it.
  604. iRegionNetYield += (2 * (iFertileCheck - 1))
  605. if pPlot.isAdjacentToLand(): # Coastal plot
  606. if pPlot.isFreshWater:
  607. iNumCoastalPlots += 1
  608. iRegionNetYield += 2
  609. else:
  610. iNumCoastalPlots += 1
  611. iRegionNetYield += 1
  612. else:
  613. iNumOceanPlots += 1
  614. else: # Land plot
  615. iNumLandPlots += 1
  616. iArea = pPlot.getArea()
  617. iPlotYield = mst.calculateTotalBestNatureYield(pPlot)
  618. iFertileCheck = mst.calculateBestNatureYield(pPlot, YieldTypes.YIELD_FOOD)
  619. if iFertileCheck > 1: # If the plot has extra food, count the extra as double value!
  620. iPlotYield += (iFertileCheck - 1)
  621. iRegionNetYield += iPlotYield
  622. if pPlot.isHills(): iRegionNetYield += 1 # Add a bonus point for Hills plots.
  623. if not iArea in land_areas: # This plot is the first detected in its AreaID.
  624. iNumLandAreas += 1
  625. land_areas.append(iArea)
  626. land_area_plots[iArea] = 1
  627. land_area_yield[iArea] = iPlotYield
  628. else: # This AreaID already known.
  629. land_area_plots[iArea] += 1
  630. land_area_yield[iArea] += iPlotYield
  631. # Sort areas, achieving a list of AreaIDs with best areas first.
  632. area_yields = land_area_yield.values()
  633. area_yields.sort()
  634. area_yields.reverse()
  635. best_areas = []
  636. for areaTestLoop in range(iNumLandAreas):
  637. for landLoop in range(len(land_areas)):
  638. if area_yields[areaTestLoop] == land_area_yield[land_areas[landLoop]]:
  639. best_areas.append(land_areas[landLoop])
  640. del land_areas[landLoop]
  641. break
  642. # Store infos to regional lists.
  643. region_data.append([iWestX, iEastX, iSouthY, iNorthY,
  644. iNumLandPlots, iNumCoastalPlots,
  645. iNumOceanPlots, iRegionNetYield,
  646. iNumLandAreas, iNumPlotsinRegion])
  647. region_best_areas.append(best_areas)
  648. region_yields.append(iRegionNetYield)
  649. sorting_regions.append(iRegionNetYield)
  650. #print region_data
  651. #print "---"
  652. #print region_best_areas
  653. #print "+++"
  654. #print region_yields
  655. # Now sort the regions
  656. best_regions = []
  657. region_numbers = range(iNumRegions)
  658. #print "reg #s", region_numbers
  659. sorting_regions.sort()
  660. sorting_regions.reverse()
  661. #print "---"
  662. #print "sorted regions"
  663. #print sorting_regions
  664. #print "---"
  665. for regionTestLoop in range(iNumRegions):
  666. #print "region test", regionTestLoop
  667. for yieldLoop in range(len(region_numbers)):
  668. #print "yield loop", yieldLoop, region_yields[yieldLoop]
  669. if sorting_regions[regionTestLoop] == region_yields[yieldLoop]:
  670. #print "--"
  671. #print region_numbers[yieldLoop]
  672. #print "++"
  673. best_regions.append(region_numbers[yieldLoop])
  674. del region_numbers[yieldLoop]
  675. del region_yields[yieldLoop]
  676. #print region_numbers
  677. #print region_yields
  678. #print "--"
  679. break
  680. #print "x-x"
  681. #print "-x-"
  682. # Need to discard the worst regions and then reverse the region order.
  683. # Of the regions that will be used, the worst will be assigned first.
  684. #
  685. # This means the civ with the poorest region will get best pick of its
  686. # lands without MinDistance concerns. Richer regions will have to obey
  687. # MinDistances in regard to poorer regions already assigned. This instead
  688. # of giving the richest region pick of its lands and making poorer regions
  689. # even worse off by pushing them around with MinDistances.
  690. best_regions[iPlayers:] = []
  691. best_regions.reverse()
  692. #print "----"
  693. #print best_regions
  694. #print "----"
  695. # Obtain player numbers. (Account for possibility of Open slots!)
  696. player_list = []
  697. for plrCheckLoop in range(18):
  698. if gc.getPlayer(plrCheckLoop).isEverAlive():
  699. player_list.append(plrCheckLoop)
  700. #print "***"
  701. #print "Player ID#s", player_list
  702. #print "***"
  703. # Shuffle start points so that players are assigned regions at random.
  704. shuffledPlayers = []
  705. for playerLoopTwo in range(gc.getGame().countCivPlayersEverAlive()):
  706. iChoosePlayer = dice.get(len(player_list), "Shuffling Regions - Archipelago PYTHON")
  707. shuffledPlayers.append(player_list[iChoosePlayer])
  708. del player_list[iChoosePlayer]
  709. #print "Shuffled Player List:", shuffledPlayers
  710. #print "---"
  711. # Find the oceans. We want all civs to start along the coast of a salt water body.
  712. oceans = []
  713. for i in range(map.getIndexAfterLastArea()):
  714. area = map.getArea(i)
  715. if not area.isNone():
  716. if area.isWater() and not area.isLake():
  717. oceans.append(area)
  718. #print("Oceans: ", oceans)
  719. # Now assign the start plots!
  720. plot_assignments = {}
  721. min_dist = []
  722. # Loop through players/regions.
  723. for assignLoop in range(iPlayers):
  724. playerID = shuffledPlayers[assignLoop]
  725. reg = best_regions[assignLoop]
  726. [westX, eastX, southY, northY] = region_data[reg][0:4]
  727. iNumAreas = region_data[reg][8]
  728. area_list = region_best_areas[reg]
  729. # print Data for debugging
  730. #print "-+-+-"
  731. #print iNumAreas
  732. #print region_data[reg][0:4]
  733. #print area_list
  734. #print "+-+-+"
  735. # Error Handling (if valid start plot not found, reduce MinDistance)
  736. iPass = 0
  737. while (true):
  738. iBestValue = 0
  739. pBestPlot = None
  740. # Loop through best areas in this region
  741. for areaLoop in range(iNumAreas):
  742. areaID = area_list[areaLoop]
  743. #print "!!!"
  744. player = gc.getPlayer(playerID)
  745. #print "-!-"
  746. player.AI_updateFoundValues(True)
  747. #print "!-!"
  748. iRange = player.startingPlotRange()
  749. validFn = None
  750. # Loop through all plots in the region.
  751. for iX in range(westX, eastX + 1):
  752. for iY in range(southY, northY + 1):
  753. pPlot = map.plot(iX, iY)
  754. if pPlot.isWater(): continue
  755. if areaID != pPlot.getArea(): continue
  756. if validFn != None and not validFn(playerID, iX, iY): continue
  757. val = pPlot.getFoundValue(playerID)
  758. if val > iBestValue:
  759. valid = True
  760. for invalid in min_dist:
  761. [invalidX, invalidY] = invalid
  762. if abs(invalidX - iX) < minX and abs(invalidY - iY) < minY:
  763. valid = False
  764. break
  765. if valid:
  766. oceanside = False
  767. for ocean in oceans:
  768. if pPlot.isAdjacentToArea(ocean):
  769. oceanside = True
  770. break
  771. if not oceanside:
  772. valid = False # Not valid unless adjacent to an ocean!
  773. if valid:
  774. for iI in range(gc.getMAX_CIV_PLAYERS()):
  775. if (gc.getPlayer(iI).isAlive()):
  776. if (iI != playerID):
  777. if gc.getPlayer(iI).startingPlotWithinRange(pPlot, playerID, iRange, iPass):
  778. valid = False
  779. break
  780. if valid:
  781. iBestValue = val
  782. pBestPlot = pPlot
  783. if pBestPlot != None:
  784. min_dist.append([pBestPlot.getX(), pBestPlot.getY()])
  785. sPlot = map.plot(pBestPlot.getX(), pBestPlot.getY())
  786. plrID = gc.getPlayer(playerID)
  787. plrID.setStartingPlot(sPlot, true)
  788. #print "- - - - -"
  789. #print "player"
  790. #print playerID
  791. #print "Plot Coords"
  792. #print pBestPlot.getX()
  793. #print pBestPlot.getY()
  794. #print "Plot Index", sPlot
  795. #print "- - - - -"
  796. break # Valid start found, stop checking areas and plots.
  797. else: pass # This area too close to somebody, try the next area.
  798. # Check to see if a valid start was found in ANY areaID.
  799. if pBestPlot == None:
  800. print("player", playerID, "pass", iPass, "failed")
  801. iPass += 1
  802. if iPass <= max(player.startingPlotRange() + eastX - westX, player.startingPlotRange() + northY - southY):
  803. continue
  804. else: # A region has failed to produce any valid starts!
  805. bSuccessFlag = False
  806. print "---"
  807. print "A region has failed"
  808. print "---"
  809. # Regional start plot assignment has failed. Reverting to default.
  810. CyPythonMgr().allowDefaultImpl()
  811. return
  812. else: break # This player has been assigned a start plot.
  813. #print plot_assignments
  814. #print "..."
  815. # Successfully assigned start plots, continue back to C++
  816. return None
  817. def findStartingPlot(argsList):
  818. # This function is only called for Snaky Continents (or if an entire region should fail to produce a valid start plot via the regional method).
  819. [playerID] = argsList
  820. # Check to see if a region failed. If so, try the default implementation. (The rest of this process could get stuck in an infinite loop, so don't risk it!)
  821. global bSuccessFlag
  822. if bSuccessFlag == False:
  823. CyPythonMgr().allowDefaultImpl()
  824. return
  825. # Identify the best land area available to this player.
  826. global areas
  827. global area_values
  828. global iBestArea
  829. iBestValue = 0
  830. iBestArea = -1
  831. areas = CvMapGeneratorUtil.getAreas()
  832. for area in areas:
  833. if area.isWater(): continue # Don't want to start "in the drink"!
  834. iNumPlayersOnArea = area.getNumStartingPlots() + 1 # Number of players starting on the area, plus this player.
  835. iTileValue = area.calculateTotalBestNatureYield() + area.getNumRiverEdges() + 2 * area.countCoastalLand() + 3 * area.countNumUniqueBonusTypes()
  836. iValue = iTileValue / iNumPlayersOnArea
  837. if (iNumPlayersOnArea == 1):
  838. iValue *= 4; iValue /= 3
  839. if (iValue > iBestValue):
  840. iBestValue = iValue
  841. iBestArea = area.getID()
  842. # Ensure that starting plot is in chosen Area and is along the coast.
  843. def isValid(playerID, x, y):
  844. global iBestArea
  845. pPlot = map.plot(x, y)
  846. if pPlot.getArea() != iBestArea:
  847. return false
  848. pWaterArea = pPlot.waterArea()
  849. if (pWaterArea.isNone()):
  850. return false
  851. return not pWaterArea.isLake()
  852. return CvMapGeneratorUtil.findStartingPlot(playerID, isValid)