--- PerfectWorld2.py	2014-08-15 10:49:00.525036434 -0700
+++ Totestra-20120610.py	2012-06-10 03:46:33.000000000 -0700
@@ -1,6 +1,9 @@
 ##############################################################################
-## File: PerfectWorld.py version 2.06
+## File: Totestra.py version 20120603 (June 3, 2012)
+## Note: This is a finished product.  
+## Original file: PerfectWorld.py version 2.06
 ## Author: Rich Marinaccio
+## Modified by Sam Trenholme; I am assigning all copyright to Rich
 ## Copyright 2007 Rich Marinaccio
 ##
 ## This map script for Civ4 generates a random, earth-like map, usually with a
@@ -46,6 +49,67 @@
 ##
 ##############################################################################
 ## Version History
+## Totestra - Sam Trenholme's update of PerfectWorld2.py
+##
+## 20120603:
+## 1) The chance of a large continent splitting at the edge of the map
+##    has been greatly reduced
+## 2) There is now an option to remove coastal mountains and reduce inland
+##    mountains
+## 3) New map wrap option: PerfectWorld-compatible toric wrap
+## 4) All bits in the service tag are now used up.  I'm declaring Totestra
+##    finished.
+##
+## 20120530:
+## 1) Added 2-bit "parity" to service tag
+##
+## 20120527:
+## 1) Adding test case for bug reported by En Dotter
+##
+## 20120526:
+## 1) En Dotter feature request: It's now possible to change how the map
+##    distributes resources
+##
+## 20120524:
+## 1) New civ placement option: Have all civs placed on the same continent
+##
+## 20120523:
+## 1) New option: Handicap level.  This can give the human player extra
+##    starting resources.
+## 2) Options to split continents and to have players on the "new world"
+##    verified to work.
+##
+## 20120522:
+## 1) "Service tag" added as a sign in maps, so people who forgot to log
+##    Python debug information can still get full support.
+##
+## 20120521:
+## 1) Overhaul of selection code; symbolic names are now used instead of
+##    numbers
+## 2) Map ratios now can be selected (within reason)
+##
+## 20120519:
+## 1) Extra-slow maps disabled; they just put PerfectWorld in to an 
+##    infinite loop
+## 2) "Fast and cheap" maps tested; they work with all three preset 
+##    seeds so I'm declaring these maps stable.
+## 3) Iceberg code overhaul: Polar icebergs are now more common in cold 
+##    and/or large maps, and less common in smaller and/or warmer maps.
+##
+## 20120512:
+## 1) Faster lower-quality maps now tweaked and fixed.  "fast and dirty" caps
+##    the size: if you ask for huge you will get a medium sized map.
+## 2) Climate can now be selected and it affects the map
+## 3) Begin work on making the map ratio user-configurable
+##
+## 20120505: 
+## 1) Water level can now be adjusted in Civ4's GUI
+## 2) It is now possible to use a fixed or a random map seed
+## 3) There is an untested ability to more quickly make lower-quality maps,
+##    or, likewise, more slowly make better maps.
+##
+## Perfect World 2, Cephalo's original map generator changelog history:
+##
 ## 2.06 - Fixed a few bugs from my minimum hill/maximum bad feature function.
 ##
 ## 2.05 - Made maps of standard size and below a bit smaller. Changed the way I
@@ -164,72 +228,83 @@
 import time
 import os
 
+# Options
+OPTION_MapSeed = 10
+OPTION_NewWorld = 0
+OPTION_Pangaea = 1
+OPTION_Wrap = 2
+OPTION_IslandFactor = 3
+OPTION_Patience = 4
+OPTION_MapRatio = 5
+OPTION_MapResources = 6
+OPTION_Handicap = 7
+OPTION_NoRotate = 8
+OPTION_SmoothPeaks = 9
+OPTION_MAX = OPTION_MapSeed + 1 # Add 1 because it's 1-indexed
+
+# Setting this to 1 will allow the buggy 1:2 ratio; this ratio has
+# problems because of limitations in Civ 4's engine.  You have been warned.
+ALLOW_EXTREME_RATIOS = 0
+
+# Setting this to 0 will make it so the map does not have a "Service Tag"
+# sign placed on it.  If the sign (which should be placed in an unusable
+# ice square, usually at the top of the map) annoys you, disabled this, but
+# heed this warning first:
+# I CAN NOT PROVIDE TECHNICAL ASSISTANCE WITHOUT A SERVICE TAG FOR YOUR
+# MAP.  DO NOT FILE A BUG REPORT OR ASK FOR TECHNICAL ASSISTANCE UNLESS YOU
+# HAVE A SERVICE TAG.
+ADD_SERVICE_TAG = 1
+
+# Quick and dirty 2-bit "party" of hex number
+def a91a15d7(x):
+        # a and b are 2-bit inputs for the s-box
+        # s is a 32-bit representation of this s-box
+        def sbox(a,b,s):
+                a &= 3
+                b &= 3
+                index = (a | (b << 2))
+                out = s
+                out >>= (index * 2)
+                out &= 3
+                return out
+
+        if x < 0:
+                return -1 # ERROR
+        out = 0
+        index = 0
+        while(x > 0):
+                q = (x & 3)
+                s = sbox(q,index,0xa91a15d7) # From RadioGatun[32] of "parity"
+                out += s
+                out ^= q
+                out &= 3
+                index += 1
+                index &= 3
+                x >>= 2
+        return out & 3
+
 class MapConstants :
     def __init__(self):
+        self.totestra = 0 
+        self.hmWidth  = 0
+        self.hmHeight = 0
+	self.noRotate = 0
+	self.smoothPeaks = 1
+        self.serviceFlags = 0 # Used for concise description of flags
+	self.xtraFlags = 0 # We're running out of bits :(
+	self.AllowPangeas = False
+	self.serviceString = "MP No Tag" # No cheating in multiplayer!
         return
     def initialize(self):
         print "Initializing map constants"
 ##############################################################################
 ## GLOBAL TUNING VARIABLES: Change these to customize the map results
         
-        #Percent of land vs. water
-        self.landPercent = 0.29
-        
-        #If this variable is set to False, a shower of colossal meteors will attempt to
-        #break up any pangea-like continents. Setting this variable to True will allow
-        #pangeas to sometimes occur and should be greatly favored by any dinosaurs
-        #that might be living on this planet at the time.
-        self.AllowPangeas = False
-
-        #This variable can be used to turn off 'New world' logic and place starting
-        #positions anywhere in the world. For some mods, a new world doesn't make
-        #sense.
-        self.AllowNewWorld = True
-        
-        #How many land squares will be above peak threshold and thus 'peaks'.
-        self.PeakPercent = 0.12
-
-        #How many land squares will be above hill threshold and thus 'hills' unless
-        #they are also above peak threshold in which case they will be 'peaks'.
-        self.HillPercent = 0.35
-
-        #In addition to the relative peak and hill generation, there is also a
-        #process that changes flats to hills or peaks based on altitude. This tends
-        #to randomize the high altitude areas somewhat and improve their appearance.
-        #These variables control the frequency of hills and peaks at the highest altitude.
-        self.HillChanceAtOne = .50
-        self.PeakChanceAtOne = .27
-
-        #How many land squares will be below desert rainfall threshold. In this case,
-        #rain levels close to zero are very likely to be desert, while rain levels close
-        #to the desert threshold will more likely be plains.
-        self.DesertPercent = 0.20
-
-        #How many land squares will be below plains rainfall threshold. Rain levels close
-        #to the desert threshold are likely to be plains, while those close to the plains
-        #threshold are likely to be grassland. 
-        self.PlainsPercent = 0.42
-        
         #---The following variables are not based on percentages. Because temperature
         #---is so strongly tied to latitude, using percentages for things like ice and
         #---tundra leads to very strange results if most of the worlds land lies near
         #---the equator
 
-        #What temperature will be considered cold enough to be ice. Temperatures range
-        #from coldest 0.0 to hottest 1.0.
-        self.SnowTemp = .30
-
-        #What temperature will be considered cold enough to be tundra. Temperatures range
-        #from coldest 0.0 to hottest 1.0.
-        self.TundraTemp = .35
-
-        #Hotter than this temperature will be considered deciduous forest, colder will
-        #be evergreen forest.Temperatures range from coldest 0.0 to hottest 1.0.
-        self.ForestTemp = .50
-
-        #What temperature will be considered hot enough to be jungle. Temperatures range
-        #from coldest 0.0 to hottest 1.0.
-        self.JungleTemp = .7
 
         #Sets the threshold for jungle rainfall by modifying the plains threshold by this factor.
         self.TreeFactor = 1.5
@@ -238,11 +313,6 @@
         #use a value between 0.0 and 1.0
         self.MaxTreeChance = 1.0
 
-        #This variable adjusts the amount of bonuses on the map. Values above 1.0 will add bonus
-        #bonuses. People often want lots of bonuses, and for those people, this variable is definately
-        #a bonus.
-        self.BonusBonus = 1.0
-        
         #How many squares are added to a lake for each unit of drainage flowing
         #into it.
         self.LakeSizePerDrainage = 14.0
@@ -289,7 +359,7 @@
         self.polarFrontLatitude = 60
 
         #Tropics of Cancer and Capricorn plus and minus respectively
-        self.tropicsLatitude = 23
+        #self.tropicsLatitude = 23
 
         #Oceans are slow to gain and lose heat, so the max and min temps
         #are reduced and raised by this much.
@@ -306,20 +376,6 @@
         #matches geostrophic uplift.
         self.monsoonUplift = 500.0
         
-        #Height and Width of main climate and height maps. This does not
-        #reflect the resulting map size. Both dimensions( + 1 if wrapping in
-        #that dimension = False) must be evenly divisble by self.hmMaxGrain
-        self.hmWidth = 144
-        self.hmHeight = 97
-
-        #Controls wrapping (not sure if this makes sense yet)
-        self.WrapX = True
-        self.WrapY = False
-        
-        #Size of largest map increment to begin midpoint displacement. Must
-        #be a power of 2.
-        self.hmMaxGrain = 16
-
         #Option to divide map into two continents as far as the midpoint
         #displacement is concerned. For guaranteed continent separation, further
         #steps will be needed but this option will cause more ocean in the
@@ -351,9 +407,6 @@
         #noise(smooth).
         self.hmNoiseLevel = 2.0
 
-        #Number of tectonic plates
-        self.hmNumberOfPlates = int(float(self.hmWidth * self.hmHeight) * 0.0016)
-
         #Influence of the plate map, or how much of it is added to the height map.
         self.plateMapScale = 1.1
 
@@ -398,23 +451,6 @@
         #the initial large heightmap (mc.hmWidth, mc.hmHeight) before the
         #shrinking process
         self.minInlandSeaSize = 100
-        
-        #After generating the heightmap, bands of ocean can be added to the map
-        #to allow a more consistent climate generation. These bands are useful
-        #if you are generating part of a world where the weather might be coming
-        #in from off the map. These bands can be kept if needed or cropped off
-        #later in the process.
-        self.northWaterBand = 10
-        self.southWaterBand = 10
-        self.eastWaterBand = 0
-        self.westWaterBand = 0
-
-        #These variables are intended for use with the above water band variables
-        #but you can crop the map edge after climate generation for any reason.
-        self.northCrop = 10
-        self.southCrop = 10
-        self.eastCrop = 0
-        self.westCrop = 0
 
         #Too many meteors will simply destroy the Earth, and just
         #in case the meteor shower can't break the pangaea, this will also
@@ -451,17 +487,6 @@
         #value.
         self.RiverCityValueBonus = 1.2
         
-        #Bonus resources to add depending on difficulty settings
-        self.SettlerBonus = 0
-        self.ChieftainBonus = 0
-        self.WarlordBonus = 0
-        self.NobleBonus = 0
-        self.PrinceBonus = 1
-        self.MonarchBonus = 1
-        self.EmperorBonus = 2
-        self.ImmortalBonus = 2
-        self.DeityBonus = 3
-        
         #Decides whether to use the Python random generator or the one that is
         #intended for use with civ maps. The Python random has much higher precision
         #than the civ one. 53 bits for Python result versus 16 for getMapRand. The
@@ -487,9 +512,12 @@
         self.NORTH_SOUTH_SEPARATION = 1
         self.EAST_WEST_SEPARATION = 2
 
+        # THESE DO NOT CHANGE ANYTHING.  They are overwritten by
+        # mmap.getGridWidth() and mmap.getGridHeight()
         self.width = 104
         self.height = 64
 
+        # These should probably not be changed...
         self.OCEAN = 0
         self.LAND = 1
         self.HILLS = 2
@@ -511,32 +539,347 @@
     def initInGameOptions(self):
         gc = CyGlobalContext()
         mmap = gc.getMap()
+
+        # Sea level comes from preferences
+        try:
+            seaLevel = int(mmap.getSeaLevel())
+        except:
+            seaLevel = 1
+        self.landPercent = 0.29
+        if seaLevel == 0:
+            self.landPercent = 0.43
+        elif seaLevel == 2:
+            self.landPercent = 0.19
+
+        self.serviceFlags = 0 # Used for concise description of flags
+	if(seaLevel != 0):
+            self.serviceFlags |= (seaLevel & 3) # 2 bits; total 2
+	else:
+            self.serviceFlags |= 3 # Make sure Service Tag is always 21 digits
+      
+        # Have climate affect the maps 
+        # This is increased for a "tropical" climate
+        self.tropicsLatitude = 23
+ 
+        # These are increased for "rocky" climates
+        #How many land squares will be above peak threshold and thus 'peaks'.
+        self.PeakPercent = 0.12
+
+        #How many land squares will be above hill threshold and thus 'hills' 
+        #unless hey are also above peak threshold in which case they will 
+        #be 'peaks'.
+        self.HillPercent = 0.35
+        
+        #In addition to the relative peak and hill generation, there is also a
+        #process that changes flats to hills or peaks based on altitude. This tends
+        #to randomize the high altitude areas somewhat and improve their appearance.
+        #These variables control the frequency of hills and peaks at the highest altitude.
+        self.HillChanceAtOne = .50
+        self.PeakChanceAtOne = .27
+
+        #How many land squares will be below desert rainfall threshold. In this case,
+        #rain levels close to zero are very likely to be desert, while rain levels close
+        #to the desert threshold will more likely be plains.
+        self.DesertPercent = 0.20
+
+        #How many land squares will be below plains rainfall threshold. Rain levels close
+        #to the desert threshold are likely to be plains, while those close to the plains
+        #threshold are likely to be grassland. 
+        self.PlainsPercent = 0.42
+        
+        #What temperature will be considered cold enough to be ice. Temperatures range
+        #from coldest 0.0 to hottest 1.0.
+        self.SnowTemp = .30
+
+        #What temperature will be considered cold enough to be tundra. Temperatures range
+        #from coldest 0.0 to hottest 1.0.
+        self.TundraTemp = .35
+
+        #Hotter than this temperature will be considered deciduous forest, colder will
+        #be evergreen forest.Temperatures range from coldest 0.0 to hottest 1.0.
+        self.ForestTemp = .50
+
+        #What temperature will be considered hot enough to be jungle. Temperatures range
+        #from coldest 0.0 to hottest 1.0.
+        self.JungleTemp = .7
+
+        # Temperate: 0 Tropical: 1 Arid: 2 Rocky: 3 Cold: 4
+        self.iceChance = 1.0 # Chance of having iceberg at top/bottom of map
+        self.iceRange = 4 # Number of squares we add icebergs to
+        self.iceSlope = 0.66 # How quickly we reduce icebergs
+        clim = mmap.getClimate() 
+        if clim == 1: # Tropical
+            self.tropicsLatitude = 46
+	    self.iceSlope = 0.33 # Less ice
+        elif clim == 2: # Arid
+            self.DesertPercent = 0.40
+            self.PlainsPercent = 0.82
+	    self.iceSlope = 0.33 # Less ice
+        elif clim == 3: # Rocky
+            self.PeakPercent = 0.24
+            self.HillPercent = 0.70
+            self.HillChanceAtOne = 0.70
+            self.PeakChanceAtOnce = 0.43
+	    self.iceSlope = 0.75 # Some more ice
+            self.iceRange = 6
+        elif clim == 4: # Cold
+            self.tropicsLatitude = 0
+            self.SnowTemp = .50
+            self.TundraTemp = .75
+            self.ForestTemp = .85
+            self.JungleTemp = .99
+            self.iceRange = 12
+            self.iceChance = 1.2
+	    self.iceSlope = 0.87 # Lots of ice
+        
+        self.serviceFlags <<= 3
+        self.serviceFlags |= (clim & 7) # 3 bits; total 5
+      
         #New World Rules
-        selectionID = mmap.getCustomMapOption(0)
+        selectionID = mmap.getCustomMapOption(OPTION_NewWorld)
+        self.AllowNewWorld = True
+	self.ShareContinent = False
         if selectionID == 1:
-            self.AllowNewWorld = not self.AllowNewWorld
+            self.AllowNewWorld = False
+	elif selectionID == 2:
+	    self.ShareContinent = True
+
+	self.xtraFlags = 0
+	self.xtraFlags |= ((self.ShareContinent & 1) << 4)
+
+        self.serviceFlags <<= 1
+        self.serviceFlags |= (self.AllowNewWorld & 1) # 1 bit; total 6
+
         #Pangaea Rules
-        selectionID = mmap.getCustomMapOption(1)
+        selectionID = mmap.getCustomMapOption(OPTION_Pangaea)
+        self.AllowPangeas = False
         if selectionID == 1:
-            self.AllowPangeas = not self.AllowPangeas
+            self.AllowPangeas = True
+
+        self.serviceFlags <<= 1
+        self.serviceFlags |= (self.AllowPangeas & 1) # 1 bit; total 7
+
+        # How long are they willing to wait for the map to be made
+        patience = mmap.getCustomMapOption(OPTION_Patience)
+        patience += 1 # Patience at 0 is broken
 
+	# This allows me to have one final usable bit in the service tag
+        self.serviceFlags <<= 3
+        self.serviceFlags |= (patience & 3) # 2 bits; total 10
+
+        # The preset worlds have hard-coded values
+        selectionID = mmap.getCustomMapOption(OPTION_MapSeed)
+
+        # Disabled: we will only alter seed (it's now for debugging)
+        #if selectionID != 0:
+            #patience = 2
+            #if selectionID != 3:
+            #    self.landPercent = 0.29 # We will force this here too
+
+        self.patience = patience
+        #Size of largest map increment to begin midpoint displacement. Must
+        #be a power of 2.
+        self.hmMaxGrain = 2 ** (2 + patience)
+
+        #Height and Width of main climate and height maps. This does not
+        #reflect the resulting map size. Both dimensions( + 1 if wrapping in
+        #that dimension = False) must be evenly divisble by self.hmMaxGrain
+        # SAM:
+        # Make it easy to change the size while keeping the aspect ratio
+        # The bigger the size factor, the smaller the continents and the
+        # slower the map generation process
+
+        # X and Y values for the map's aspect ratio
+	ratioValue = mmap.getCustomMapOption(OPTION_MapRatio)
+	if 1 == 2: # Dummy, does nothing, so we don't get elif problems
+		self.ratioX = 3
+		self.ratioY = 2
+        elif ratioValue == 0: # 2:3
+		self.ratioX = 2
+		self.ratioY = 3
+        elif ratioValue == 1: # 1:1
+		self.ratioX = 2
+		self.ratioY = 2
+	elif ratioValue == 2: # 3:2
+		self.ratioX = 3
+		self.ratioY = 2
+	elif ratioValue == 3: # 2:1
+		self.ratioX = 4
+		self.ratioY = 2
+	elif ratioValue == 4: # 7:1, Ringworld 
+		self.ratioX = 7
+		self.ratioY = 1
+	elif ratioValue == 5: # 3:3, Big square (untested)
+		self.ratioX = 3
+		self.ratioY = 3
+        elif ratioValue == 6: # 1:2; down here because it's buggy
+		self.ratioX = 2
+		self.ratioY = 4
+
+	if patience < 2:
+        	self.ratioX = 3 # One less thing to SQA
+        	self.ratioY = 2
+
+        self.serviceFlags <<= 3
+        self.serviceFlags |= (ratioValue & 7) # 3 bits; total 13
+
+        selectionID = mmap.getCustomMapOption(OPTION_IslandFactor)
+
+        self.serviceFlags <<= 2
+        self.serviceFlags |= (selectionID & 3) # Island factor
+
+	# If they want a fast map, don't allow them to select more islands
+	if(patience < 2):
+		selectionID = 0
+
+        heightmap_size_factor = 3 + selectionID
+        self.hmWidth  = (self.hmMaxGrain * self.ratioX * 
+                         heightmap_size_factor)
+        self.hmHeight = (self.hmMaxGrain * self.ratioY * 
+                         heightmap_size_factor) + 1
+
+        # These are expressed in 4x4 "Grid" units
+        self.maxMapWidth = int(self.hmWidth / 4)
+        self.maxMapHeight = int(self.hmHeight / 4)
+         
         #Wrap options
-        selectionID = mmap.getCustomMapOption(2)
+        selectionID = mmap.getCustomMapOption(OPTION_Wrap)
         wrapString = "Cylindrical"
-        if selectionID == 1: #Toroidal
+	self.WrapX = True
+	self.WrapY = False
+
+        self.serviceFlags <<= 2 
+        self.serviceFlags |= (selectionID & 3) # Map wrap; 2 bits total 15
+        self.serviceFlags <<= 6 # 6 bits so we know the map size total 21
+
+	# handicap of 0 means player is equal to AI and may get a 
+	# starting position that can not be won at higher difficulty
+	# settings.  Values of 1, 2, or 3 make it easier for the player
+	handicap = mmap.getCustomMapOption(OPTION_Handicap)
+	self.xtraFlags |= ((handicap & 3) << 5)
+	
+        #Bonus resources to add depending on difficulty settings
+        self.SettlerBonus = handicap
+        self.ChieftainBonus = handicap 
+        self.WarlordBonus = handicap 
+        self.NobleBonus = handicap 
+        self.PrinceBonus = handicap 
+        self.MonarchBonus = handicap 
+        self.EmperorBonus = handicap 
+        self.ImmortalBonus = handicap 
+        self.DeityBonus = handicap 
+       
+	# Now that we have calculated the player's bonus resources, how many
+	# resources should the map as a whole have? 
+
+        #This variable adjusts the amount of bonuses on the map. Values above 1.0 will add bonus
+        #bonuses. People often want lots of bonuses, and for those people, this variable is definately
+        #a bonus.
+        self.BonusBonus = 1.0
+	self.spreadResources = False 
+	bonus_add = mmap.getCustomMapOption(OPTION_MapResources)
+	if bonus_add == 1: # More evenly spread out
+		self.BonusBonus = 0.7 # Compensate for spread's increase
+		self.spreadResources = True # Increases resources
+	if bonus_add == 2: # Full of resources
+		self.BonusBonus = 1.5 # Increases resources
+		self.spreadResources = True # Increases resources more
+	self.xtraFlags |= ((bonus_add & 3) << 2)
+       
+        self.noRotate = mmap.getCustomMapOption(OPTION_NoRotate)
+	self.smoothPeaks = mmap.getCustomMapOption(OPTION_SmoothPeaks)
+ 
+        #After generating the heightmap, bands of ocean can be added to the map
+        #to allow a more consistent climate generation. These bands are useful
+        #if you are generating part of a world where the weather might be coming
+        #in from off the map. These bands can be kept if needed or cropped off
+        #later in the process.
+        self.northWaterBand = 10
+        self.southWaterBand = 10
+        self.eastWaterBand = 0
+        self.westWaterBand = 0
+        #These variables are intended for use with the above water band variables
+        #but you can crop the map edge after climate generation for any reason.
+        self.northCrop = 10
+        self.southCrop = 10
+        self.eastCrop = 0
+        self.westCrop = 0
+
+        if selectionID == 1 or selectionID == 3: #Toroidal
             self.hmHeight -= 1
             self.WrapY = True
+	    if selectionID == 1:
+                self.iceChance *= 0.1
             self.northWaterBand = 0
             self.northCrop = 0
             self.southWaterBand = 0
             self.southCrop = 0
             wrapString = "Toroidal"
-            
         elif selectionID == 2: #Flat
             self.hmWidth += 1
             self.WrapX = False
             wrapString = "Flat"
-            
+       
+        # Random seed options (fixed or random)
+        selectionID = mmap.getCustomMapOption(OPTION_MapSeed)
+        mapRString = "Random"
+        self.totestra = 0 
+        if selectionID == 1: # Totestra
+            self.totestra = 8885098498360902 # Fixed map seed
+            #mapRstring = "Totestra"
+            # Ignore most map parameters
+            #self.wrapX = True
+            #self.wrapY = False
+            #wrapString = "Cylindrical"
+            #self.hmWidth = 240
+            #self.hmHeight = 161
+            #heightmap_size_factor = 5
+            #self.AllowPangeas = False
+        elif selectionID == 2: # Cephalo
+            self.totestra = 4316490043753041 # Fixed map seed
+            #mapRstring = "Cephalo"
+            # Ignore most map parameters
+            #self.wrapX = True
+            #self.wrapY = False
+            #wrapString = "Cylindrical"
+            #self.hmWidth = 144
+            #self.hmHeight = 97
+            #heightmap_size_factor = 3
+            #self.AllowPangeas = False
+        elif selectionID == 3: # Caulixtla
+            self.totestra = 8939185639133313 # Fixed map seed
+            #mapRstring = "Caulixtla"
+            # Ignore most map parameters
+            #self.wrapX = True
+            #self.wrapY = False
+            #wrapString = "Cylindrical"
+            #self.hmWidth = 144
+            #self.hmHeight = 97
+            #heightmap_size_factor = 3
+            #self.AllowPangeas = False
+	elif selectionID == 4: # En Dotter 1
+            self.totestra = 0x8f3d2735334af # En Dotter's low on resources map 
+	elif selectionID == 5:
+	    self.totestra = 0x4eceb2bbd8853 # En Dotter's nearby starts map
+
+        #Number of tectonic plates
+        self.hmNumberOfPlates = int(float(self.hmWidth * self.hmHeight) * 0.0016)
+        if patience == 0:
+            self.hmNumberOfPlates = int(
+                float(self.hmWidth * self.hmHeight) * 0.0032)
+        elif patience == 1:
+            self.hmNumberOfPlates = int(
+                float(self.hmWidth * self.hmHeight) * 0.0024)
+        elif patience == 3:
+            self.hmNumberOfPlates = int(
+                float(self.hmWidth * self.hmHeight) * 0.0008)
+        elif patience == 4:
+            self.hmNumberOfPlates = int(
+                float(self.hmWidth * self.hmHeight) * 0.0004)
+        elif patience >= 5:
+            self.hmNumberOfPlates = int(
+                float(self.hmWidth * self.hmHeight) * 0.0002)
+
         self.optionsString = "Map Options: \n"
         if self.AllowNewWorld:
             self.optionsString += "AllowNewWorld = true\n"
@@ -547,8 +890,15 @@
         else:
             self.optionsString += "AllowPangeas = false\n"
         self.optionsString += "Wrap Option = " + wrapString + "\n"
-        
+        self.optionsString += "Map world = " + mapRString + "\n" 
+        self.optionsString += "Land percent = " + str(self.landPercent) + "\n"
+        self.optionsString += "RatioX = " + str(self.ratioX) + "\n"
+        self.optionsString += "RatioY = " + str(self.ratioY) + "\n"
+        self.optionsString += "Climate = " + str(clim) + "\n"
+        self.optionsString += "Patience = " + str(patience) + "\n"
+        self.optionsString += "Island factor = " + str(heightmap_size_factor) +"\n" 
 
+        print str(self.optionsString) + "\n" 
         return
     
 mc = MapConstants()
@@ -569,13 +919,24 @@
             # Python 'long' has unlimited precision, while the random generator
             # has 53 bits of precision, so I'm using a 53 bit integer to seed the map!
             seed() #Start with system time
-            seedValue = randint(0,9007199254740991)
+            if(mc.totestra == 0):
+                seedValue = randint(0,9007199254740991)
+                self.seedString = "Random seed (Using Python rands) for this map is %(s)20d" % {"s":seedValue}
+            else:
+                seedValue = mc.totestra
+                self.seedString = "Fixed seed (Using Python rands) for this map is %(s)20d" % {"s":seedValue}
+	    mc.serviceTag = (seedValue & 0xffffffffffffff)
+	    mc.serviceTag |= (mc.serviceFlags << 60)
+	    mc.serviceTag |= (mc.xtraFlags << 53)
+	    if(mc.noRotate == 0):
+		mc.serviceTag |= (1 << 83)
+	    if(mc.smoothPeaks == 1):
+		mc.serviceTag |= (1 << 75)
+	    mc.serviceTag |= (a91a15d7(mc.serviceTag) << 53)
+	    mc.serviceString = ("%x" % mc.serviceTag)
+            print "SERVICE TAG: " + mc.serviceString 
             seed(seedValue)
-            self.seedString = "Random seed (Using Python rands) for this map is %(s)20d" % {"s":seedValue}
             
-##            seedValue = 4316490043753041
-##            seed(seedValue)
-##            self.seedString = "Pre-set seed (Using Pyhon rands) for this map is %(s)20d" % {"s":seedValue}     
         else:
             gc = CyGlobalContext()
             self.mapRand = gc.getGame().getMapRand()
@@ -588,7 +949,7 @@
 ##            self.mapRand.init(seedValue)
 ##            self.seedString = "Pre-set seed (Using getMapRand) for this map is %(s)20d" % {"s":seedValue}
             
-        print self.seedString
+	print str(self.seedString)
         return
     def random(self):
         if self.usePR:
@@ -632,7 +993,7 @@
                 popupInfo.addPythonButton("Ok","")
                 popupInfo.addPopup(iPlayer)
                 
-#This function converts x and y to an index.
+#This function converts x and y to a one-dimensional index.
 def GetIndex(x,y):
     #Check X for wrap
     if mc.WrapX == True:
@@ -652,6 +1013,7 @@
     i = yy * mc.width + xx
     return i
 
+# This does the same thing for the height map (as opposed to the plot map)
 def GetHmIndex(x,y):
     #Check X for wrap
     if mc.WrapX == True:
@@ -691,7 +1053,6 @@
     i = yy * width + xx
     return i
 
-
 #This function scales a float map so that all values are between
 #0.0 and 1.0.
 def NormalizeMap(fMap,width,height):
@@ -718,8 +1079,26 @@
         for x in range(width):
             fMap[GetIndexGeneral(x,y,width,height)] = fMap[GetIndexGeneral(x,y,width,height)] * scaler              
     return
+
+# This takes a large map and scales it to make it smaller
 def ShrinkMap(largeMap,lWidth,lHeight,sWidth,sHeight):
+
     smallMap = array('d')
+    for y in range(sHeight):
+        for x in range(sWidth):
+	    smallMap.append(0)
+
+    # If the "small" map is, in fact, bigger, repeat the "large" map
+    # This looks **REALLY BAD** and should not be done
+    if(sWidth > lWidth and sHeight > lHeight):
+        for x in range(sWidth):
+	    for y in range(sHeight):
+	        smallMap[GetIndexGeneral(x,y,sWidth,sHeight)] = (
+	    	 largeMap[GetIndexGeneral(x % lWidth,y % lHeight, 
+		 lWidth, lHeight)])
+        return smallMap
+
+    #Scale down a large map down to a small map
     yScale = float(lHeight)/float(sHeight)
     xScale = float(lWidth)/float(sWidth)
     for y in range(sHeight):
@@ -747,9 +1126,13 @@
                     weights += weight
                     contributors += weight * contributor
 ##            print " final height = %f" % (contributors/weights)        
-            smallMap.append(contributors/weights)
+            #smallMap.append(contributors/weights)
+	    smallMap[GetIndexGeneral(x,y,sWidth,sHeight)] = (
+	    	contributors/weights)
+            #smallMap.append(contributors/weights)
                     
     return smallMap
+
 def GetWeight(x,y,xx,yy,xScale,yScale):
     xWeight = 1.0
 ##    print "   xScale = %f" % xScale
@@ -1006,7 +1389,7 @@
             ok = False
 
         if ok == False:
-            raise ValueError, "height map dimesions not divisible by mc.hmMaxGrain. also check wrapping options"
+            raise ValueError, ("height map dimesions not divisible by mc.hmMaxGrain. also check wrapping options Width %d Height %d w %d h %d %s %s" % (mc.hmWidth , mc.hmHeight, width, height, str(mc.WrapX), str(mc.WrapY)))
             
         return
 
@@ -1396,6 +1779,40 @@
 
         NormalizeMap(self.heightMap,mc.hmWidth,mc.hmHeight)
 
+    def rotateMap(self):
+
+	if mc.noRotate != 0:
+		return
+
+        # This rotates a map east or west so that the map wraps around where
+        # the lowest vertical band is on the map
+	low = 0
+	min = 10000.0
+
+	# Find the place on the map with the most water (lowest heightfield)
+        for x in range(mc.hmWidth):
+		sum = 0.0
+		for y in range(mc.hmHeight):
+			sum += self.heightMap[GetHmIndex(x,y)]
+		#print "for x %d sum is %f min %f" % (x,sum,min)
+		if(sum < min):
+			low = x
+			min = sum
+	#print "low x is %d" % (low) #DEBUG
+	
+	# Rotate the height map so we wrap where there is more water
+	for y in range(mc.hmHeight):
+		tempStripe = []
+		for x in range(mc.hmWidth):
+			tempStripe.append(self.heightMap[GetHmIndex(x,y)])
+		for x in range(mc.hmWidth):
+			self.heightMap[GetHmIndex(x,y)] = tempStripe[
+				((x + low) % mc.hmWidth)]
+
+	# Done
+	return
+
+
     def addWaterBands(self):
         #validate water bands. Maps that wrap cannot have one in that direction
         if mc.WrapX and (mc.eastWaterBand != 0 or mc.westWaterBand != 0):
@@ -1422,6 +1839,14 @@
         self.heightMap = newHeightMap
         
     def calculateSeaLevel(self):
+        if mc.patience == 0:
+            mc.landPercent *= 2
+            if mc.landPercent > 1:
+                mc.landPercent = 1
+        if mc.patience == 1:
+            mc.landPercent *= 1.5
+            if mc.landPercent > 1:
+                mc.landPercent = 1
         self.seaLevel = FindValueFromPercent(self.heightMap,mc.hmWidth,mc.hmHeight,mc.landPercent,0.02,True)
         return
     
@@ -1866,6 +2291,7 @@
             for x in range(mc.hmWidth):
                 tempMap.append(self.getInitialTemp(x,y,tropic))
         return
+
     def getInitialTemp(self,x,y,tropic):
         i = GetHmIndex(x,y)
         lat = self.getLatitude(y)
@@ -2074,6 +2500,7 @@
                     self.averageTempMap.append(0.0)
 
         #Smooth coasts so there are fewer hills on coast
+	# (This might make deep ocean difficult to implement)
         for y in range(mc.height):
             for x in range(mc.width):
                 if self.isBelowSeaLevel(x,y):
@@ -2193,7 +2620,7 @@
         for y in range(mc.height):
             for x in range(mc.width):
                 i = GetIndex(x,y)
-                if self.plotMap == mc.HILLS:
+                if self.plotMap[i] == mc.HILLS and mc.smoothPeaks == 1:
                     allHills = True
                     for direction in range(1,9,1):
                         xx,yy = GetXYFromDirection(x,y,direction)
@@ -2202,15 +2629,26 @@
                             allHills = False
                     if allHills == True:
                         self.plotMap[i] = mc.LAND
-                if self.plotMap == mc.PEAK:
+                if self.plotMap[i] == mc.PEAK and mc.smoothPeaks == 1:
                     allPeaks = True
+		    peakCount = 0
+		    nextToOcean = False
+		    #print "peak at %d %d" % (x,y) #DEBUG
+		    # While we're here, let's eliminate seaside peaks
                     for direction in range(1,9,1):
                         xx,yy = GetXYFromDirection(x,y,direction)
                         ii = GetIndex(xx,yy)
                         if self.plotMap[ii] != mc.PEAK:
                             allPeaks = False
-                    if allPeaks == True:
+			else:
+			    peakCount += 1
+                        if self.plotMap[ii] == mc.OCEAN:
+			    #print "nextToOcean %d %d" % (x,y) #DEBUG
+                            nextToOcean = True
+                    if allPeaks == True or peakCount > 3:
                         self.plotMap[i] = mc.HILLS
+		    if nextToOcean == True:
+			self.plotMap[i] = mc.HILLS
         
         return
     def createTerrainMap(self):
@@ -2986,7 +3424,10 @@
         
         for n in range(len(continentList)):
             oldWorldSize += continentList[0].size
-            del continentList[0]
+	    # Don't delete "new worlds" from the list if we're going to
+            # put everyone on the "old world" continent
+	    if mc.ShareContinent == False:
+                del continentList[0]
             if float(oldWorldSize)/float(totalLand) > 0.60:
                 break
 
@@ -3428,7 +3869,12 @@
                 rainFall = self.averageRainfallMap[i]
                 xx = x
                 yy = y
+                loop = 1
                 while(flow != self.L and flow != self.O):
+                    loop += 1
+                    if(loop > 512):
+		        raise ValueError, "Rainfall infinite loop"
+
                     if(flow == self.N):
                         yy += 1
                     elif(flow == self.S):
@@ -3676,8 +4122,7 @@
             if len(placementList) > 0:
                 placementList = ShuffleList(placementList)
                 for eBonus in placementList:
-                    self.AddBonusType(eBonus,plotIndexList)                        
-                
+                    self.AddBonusType(eBonus,plotIndexList) 
 
         #now check to see that all resources have been placed at least once, this
         #pass ignoring area rules
@@ -3958,7 +4403,8 @@
         if plot.isPotentialCityWork() == False:
             return False
         
-        if bIgnoreArea == False and bonusInfo.isOneArea() == True:
+        if (bIgnoreArea == False and bonusInfo.isOneArea() == True and
+		mc.spreadResources == False):
             areaID = plot.getArea()
             areaFound = False
             for i in range(len(self.bonusList)):
@@ -5011,7 +5457,7 @@
     Return an integer
     """
     mc.initialize()
-    return 3
+    return OPTION_MAX
 	
 def getCustomMapOptionName(argsList):
         """
@@ -5020,12 +5466,28 @@
         Return a Unicode string
         """
         optionID = argsList[0]
-        if optionID == 0:
-            return "New World Rules"
-        elif optionID == 1:
-            return "Pangaea Rules"
-        elif optionID == 2:
+        if optionID == OPTION_NewWorld:
+            return "Civ placement"
+        elif optionID == OPTION_Pangaea:
+            return "Pangaea Rule"
+        elif optionID == OPTION_Wrap:
             return "Wrap Option"
+        elif optionID == OPTION_MapSeed:
+            return "Map seed"
+        elif optionID == OPTION_IslandFactor:
+            return "Continent amount"
+        elif optionID == OPTION_Patience:
+            return "Patience amount"
+        elif optionID == OPTION_MapRatio:
+            return "Map ratio"
+        elif optionID == OPTION_Handicap:
+            return "Player bonus resources"
+	elif optionID == OPTION_MapResources:
+	    return "Map resources"
+	elif optionID == OPTION_NoRotate:
+	    return "Wrap land"
+	elif optionID == OPTION_SmoothPeaks:
+	    return "Reduce Mountains"
 
         return u""
 	
@@ -5036,12 +5498,32 @@
         Return an integer
         """
         optionID = argsList[0]
-        if optionID == 0:
+        if optionID == OPTION_NewWorld:
+            return 3
+        elif optionID == OPTION_Pangaea:
             return 2
-        elif optionID == 1:
+        elif optionID == OPTION_Wrap:
+            return 4
+        elif optionID == OPTION_MapSeed: # Map world
+            return 6
+        elif optionID == OPTION_IslandFactor: # Number continents
+            return 4
+        elif optionID == OPTION_Patience: # Speed/quality tradeoff
+	    # Slow but good disabled: Causes infinite loops
             return 2
-        elif optionID == 2:
-            return 3
+        elif optionID == OPTION_MapRatio: # Map ratio
+	    if ALLOW_EXTREME_RATIOS == 1:
+	    	return 7 
+	    else:
+                return 6
+        elif optionID == OPTION_Handicap:
+	    return 4
+	elif optionID == OPTION_MapResources:
+	    return 3
+	elif optionID == OPTION_NoRotate:
+	    return 2
+	elif optionID == OPTION_SmoothPeaks:
+	    return 2
         return 0
 	
 def getCustomMapOptionDescAt(argsList):
@@ -5053,35 +5535,99 @@
     """
     optionID = argsList[0]
     selectionID = argsList[1]
-    if optionID == 0:
+    if optionID == OPTION_NewWorld:
         if selectionID == 0:
-            if mc.AllowNewWorld:
-                return "Start in Old World"
-            else:
-                return "Start Anywhere"
+            return "Keep New World empty"
         elif selectionID == 1:
-            if mc.AllowNewWorld:
-                return "Start Anywhere"
-            else:
-                return "Start in Old World"
-    elif optionID == 1:
+            return "Start anywhere reasonable"
+	elif selectionID == 2:
+	    return "Everyone on same landmass"
+    elif optionID == OPTION_Pangaea:
         if selectionID == 0:
-            if mc.AllowPangeas:
-                return "Allow Pangaeas"
-            else:
-                return "Break Pangaeas"
+            return "Break Pangaeas"
         elif selectionID == 1:
-            if mc.AllowPangeas:
-                return "Break Pangaeas"
-            else:
-                return "Allow Pangaeas"
-    elif optionID == 2:
+            return "Allow Pangaeas"
+    elif optionID == OPTION_Wrap:
         if selectionID == 0:
             return "Cylindrical"
         elif selectionID == 1:
             return "Toroidal"
         elif selectionID == 2:
             return "Flat"
+	elif selectionID == 3:
+	    return "Toric w/ ice band"
+    elif optionID == OPTION_MapSeed:
+        if selectionID == 0:
+            return "Random"
+        elif selectionID == 1:
+            return "Preset #1 (Totestra)"
+        if selectionID == 2:
+            return "Preset #2 (Cephalo)"
+        elif selectionID == 3:
+            return "Preset #3 (Caulixtla)"
+	elif selectionID == 4:
+	    return "Preset #4 (En Dotter 1)"
+	elif selectionID == 5:
+	    return "Preset #5 (En Dotter 2)"
+    elif optionID == OPTION_IslandFactor:
+        if selectionID == 0:
+            return "Few (faster)"
+        elif selectionID == 1:
+            return "Some"
+        if selectionID == 2:
+            return "Many"
+        elif selectionID == 3:
+            return "Lots (slow)"
+    elif optionID == OPTION_Patience:
+        if selectionID == -1:
+            return "Not very (Faster mapgen)"
+        elif selectionID == 0:
+            return "A little (faster mapgen)"
+        if selectionID == 1:
+            return "Somewhat"
+        elif selectionID == 2:
+            return "Extremely (nicer map)"
+    elif optionID == OPTION_MapRatio: # Map ratio
+        if selectionID == 0: 
+            return "2:3 (Tall map)" 
+        elif selectionID == 1: 
+            return "1:1 (Square map)"
+        elif selectionID == 2:
+            return "3:2 (Earth-like)"
+        elif selectionID == 3:
+            return "2:1 (Wide map)"
+        elif selectionID == 6: 
+            return "1:2 (BUGGY)" 
+        elif selectionID == 4:
+            return "7:1 (Ringworld)"
+        elif selectionID == 5:
+            return "3:3 (Big square)"
+    elif optionID == OPTION_Handicap:
+	if selectionID == 0:
+	    return "None (Player equal to AI)"
+	if selectionID == 1:
+	    return "A little"
+	if selectionID == 2:
+	    return "Some"
+	if selectionID == 3:
+	    return "Lots (Easier for player)"
+    elif optionID == OPTION_MapResources:
+	if selectionID == 0:
+	    return "Like Perfect World"
+	if selectionID == 1:
+	    return "Resources evenly spread"
+	if selectionID == 2:
+	    return "Full of resources"
+    elif optionID == OPTION_NoRotate:
+	if selectionID == 0:
+	    return "Fix continent split"
+	else:
+	    return "PerfectWorld style"
+    elif optionID == OPTION_SmoothPeaks:
+	if selectionID == 0:
+	    return "No"
+	else:
+	    return "Yes"
     return u""
 	
 def getCustomMapOptionDefault(argsList):
@@ -5091,7 +5637,15 @@
 	Return an integer
 	"""
 	#Always zero in this case
-	return 0
+	#print argsList[0]
+        if argsList[0] == OPTION_Patience:
+            return 1 # Slow speed/good quality (Mark's default)
+	elif argsList[0] == OPTION_MapRatio:
+	    return 2 # 3:2 Earthlike map
+	elif argsList[0] == OPTION_SmoothPeaks:
+	    return 1 # By default, smooth coastal and other peaks
+        else: # Everything else defaults to first choice
+            return 0
     
 def isRandomCustomMapOption(argsList):
 	"""
@@ -5119,23 +5673,19 @@
     return
 def normalizeRemovePeaks():
     return
+GlobalThisIsAdvanced = 0
 def isAdvancedMap():
 	"""
 	Advanced maps only show up in the map script pulldown on the advanced menu.
 	Return 0 if you want your map to show up in the simple singleplayer menu
 	"""
+        GlobalThisIsAdvanced = 1
 	return 0
 def isClimateMap():
-	"""
-	Uses the Climate options
-	"""
-	return 0
+	return 1
 	
 def isSeaLevelMap():
-	"""
-	Uses the Sea Level options
-	"""
-	return 0
+	return 1
     
 def getTopLatitude():
 	"Default is 90. 75 is past the Arctic Circle"
@@ -5157,9 +5707,31 @@
     if (argsList[0] == -1): # (-1,) is passed to function on loads
             return []
     [eWorldSize] = argsList
-    return grid_sizes[eWorldSize]
+
+    (sizex, sizey) = grid_sizes[eWorldSize]
+    # The above values are for a 3:2 ratio.  We also support other ratios
+    base_size = float(sizey) / 2 # base sizes: 3.5, 5, 6.5, 8, 10, 12
+    sizex = int((base_size * mc.ratioX) + 0.5)
+    sizey = int((base_size * mc.ratioY) + 0.5)
+    
+    # Let's reduce ice on smaller maps
+    if(sizey < 20):
+	mc.iceSlope *= 0.85
+    if(sizey < 11):
+	mc.iceSlope *= 0.85
+
+    # The map generator goes in to an infinite loop if the output map is
+    # bigger than (hmWidth, hmHeight)
+    if(sizex > mc.maxMapWidth):
+	sizex = mc.maxMapWidth
+    if(sizey > mc.maxMapHeight):
+	sizey = mc.maxMapHeight
+
+    mc.serviceFlags |= sizey # 21 bits, we have 3 more for other flags
+    return (sizex, sizey)
 
 def generatePlotTypes():
+    # Core height map generator
     gc = CyGlobalContext()
     mmap = gc.getMap()
     mc.width = mmap.getGridWidth()
@@ -5174,6 +5746,7 @@
     pb.breakPangaeas()
 ##    hm.Erode()
 ##    hm.printHeightMap()
+    hm.rotateMap()
     hm.addWaterBands()
 ##    hm.printHeightMap()
     cm.createClimateMaps()
@@ -5193,6 +5766,7 @@
             plotTypes[i] = PlotTypes.PLOT_OCEAN
     print "Finished generating plot types."         
     return plotTypes
+
 def generateTerrainTypes():
     NiTextOut("Generating Terrain  ...")
     print "Adding Terrain"
@@ -5304,6 +5878,7 @@
             plot.setWOfRiver(True,CardinalDirectionTypes.CARDINALDIRECTION_NORTH)
         elif rm.riverMap[ii] == rm.W:
             plot.setNOfRiver(True,CardinalDirectionTypes.CARDINALDIRECTION_WEST)
+
 '''
 This function examines a lake area and removes ugly surrounding rivers. Any
 river that is flowing away from the lake, or alongside the lake will be
@@ -5368,6 +5943,7 @@
     #Southeast plot is not relevant 
             
     return riversIntoLake
+
 '''
 This function replaces rivers to update the river crossings after a lake or
 channel is placed at X,Y. There had been a long standing problem where water tiles
@@ -5719,27 +6295,36 @@
     gc = CyGlobalContext()
     mmap = gc.getMap()
     featureIce = gc.getInfoTypeForString("FEATURE_ICE")
-    if mc.WrapY == True:
-        iceChance = 0.5
-    else:
-        iceChance = 1.0
-    for y in range(4):
+    iceChance = mc.iceChance
+    iceRange = mc.iceRange
+    iceSlope = mc.iceSlope
+    signadded = 0 # We add the "Service Tag" sign while drawing ice
+    if(ADD_SERVICE_TAG != 1):
+	signadded = 1 # WARNING: NO SERVICE TAG MEANS NO SUPPORT
+    print "SERVICE TAG: " + mc.serviceString # Always log it, of course
+    for y in range(iceRange):
         for x in range(mc.width):
             plot = mmap.plot(x,y)
             if plot != 0 and plot.isWater() == True and PRand.random() < iceChance:
                 plot.setFeatureType(featureIce,0)
-        iceChance *= .66
-    if mc.WrapY == True:
-        iceChance = 0.5
-    else:
-        iceChance = 1.0
-    for y in range(mc.height - 1,mc.height - 5,-1):
+        iceChance *= iceSlope
+    iceChance = mc.iceChance
+    for y in range(mc.height - 1,mc.height - 1 - iceRange,-1):
         for x in range(mc.width):
             plot = mmap.plot(x,y)
             if plot != 0 and plot.isWater() == True and PRand.random() < iceChance:
                 plot.setFeatureType(featureIce,0)
-        iceChance *= .66
-        
+                if signadded == 0:
+			CyEngine().addSign(plot, -1, mc.serviceString)
+			print "Sign added at %d %d" % (x,y)
+			signadded = 1
+        iceChance *= iceSlope
+    # Make sure the service tag gets added to the map
+    if signadded == 0:
+	plot = mmap.plot(0,0)
+	CyEngine().addSign(plot, -1, mc.serviceString)    
+	print "Sign added at 0 0" % (x,y)
+
 def addBonuses():
     bp.AddBonuses()
     return
@@ -5750,8 +6335,8 @@
     spf.SetStartingPlots()
 def beforeInit():
     print "Initializing Custom Map Options"
-    mc.initialize()
     mc.initInGameOptions()
+    mc.initialize()
 
 ##mc.initialize()
 ##PRand.seed()
