# # FILE: Team_Battleground.py # AUTHOR: Bob Thomas (Sirian) # CONTRIB: Andy Szybalski # PURPOSE: Regional map script - Ideal for quick MP team games. #----------------------------------------------------------------------------- # Copyright (c) 2005 Firaxis Games, Inc. All rights reserved. #----------------------------------------------------------------------------- # from CvPythonExtensions import * import CvUtil import CvMapGeneratorUtil import sys from CvMapGeneratorUtil import FractalWorld from CvMapGeneratorUtil import HintedWorld from CvMapGeneratorUtil import TerrainGenerator from CvMapGeneratorUtil import FeatureGenerator def getDescription(): return "TXT_KEY_MAP_SCRIPT_TEAM_BATTLEGROUND_DESCR" resourcesToBalance = ('BONUS_IRON', 'BONUS_HORSE', 'BONUS_COPPER', 'BONUS_MARBLE', 'BONUS_STONE', 'BONUS_IVORY', 'BONUS_PIG', 'BONUS_RICE', 'BONUS_SHEEP', 'BONUS_GOLD', 'BONUS_WHEAT', 'BONUS_CORN', 'BONUS_GEMS') stratresourcesToBalance = ('BONUS_OIL', 'BONUS_URANIUM', 'BONUS_ALUMINUM', 'BONUS_COAL') #resourcesToEliminate = ('BONUS_SILVER') def getNumCustomMapOptions(): return 3 def getNumHiddenCustomMapOptions(): return 1 def getCustomMapOptionName(argsList): [iOption] = argsList option_names = { 0: "TXT_KEY_MAP_SCRIPT_TEAM_PLACEMENT", 1: "TXT_KEY_MAP_SCRIPT_TEAM_SETTING", 2: "TXT_KEY_MAP_WORLD_WRAP" } translated_text = unicode(CyTranslator().getText(option_names[iOption], ())) return translated_text def getNumCustomMapOptionValues(argsList): [iOption] = argsList option_values = { 0: 3, 1: 3, 2: 3 } return option_values[iOption] def getCustomMapOptionDescAt(argsList): [iOption, iSelection] = argsList selection_names = { 0: { 0: "TXT_KEY_MAP_SCRIPT_LEFT_VS_RIGHT", 1: "TXT_KEY_MAP_SCRIPT_TOP_VS_BOTTOM", 2: "TXT_KEY_MAP_SCRIPT_FOUR_CORNERS" }, 1: { 0: "TXT_KEY_MAP_SCRIPT_START_TOGETHER", 1: "TXT_KEY_MAP_SCRIPT_START_SEPARATED", 2: "TXT_KEY_MAP_SCRIPT_START_ANYWHERE" }, 2: { 0: "TXT_KEY_MAP_WRAP_FLAT", 1: "TXT_KEY_MAP_WRAP_CYLINDER", 2: "TXT_KEY_MAP_WRAP_TOROID" } } translated_text = unicode(CyTranslator().getText(selection_names[iOption][iSelection], ())) return translated_text def getCustomMapOptionDefault(argsList): [iOption] = argsList option_defaults = { 0: 1, 1: 0, 2: 0 } return option_defaults[iOption] def isRandomCustomMapOption(argsList): [iOption] = argsList option_random = { 0: true, 1: false, 2: false } return option_random[iOption] def isAdvancedMap(): "This map should not show up in simple mode" return 1 def isSeaLevelMap(): return 1 def getWrapX(): map = CyMap() return (map.getCustomMapOption(2) == 1 or map.getCustomMapOption(2) == 2) def getWrapY(): map = CyMap() return (map.getCustomMapOption(2) == 2) def getTopLatitude(): return 80 def getBottomLatitude(): return -80 def minStartingDistanceModifier(): return -65 def beforeGeneration(): global equator global team_num team_num = [] team_index = 0 topVsBottomCheck = CyMap().getCustomMapOption(0) if topVsBottomCheck == 1: equator = CyGlobalContext().getInfoTypeForString("TERRAIN_GRASS") else: equator = CyGlobalContext().getInfoTypeForString("TERRAIN_GRASS") for teamCheckLoop in range(18): if CyGlobalContext().getTeam(teamCheckLoop).isEverAlive(): team_num.append(team_index) team_index += 1 else: team_num.append(-1) return None def getGridSize(argsList): "Different grids, depending on the choice of Team Placement. Very small worlds." if (argsList[0] == -1): # (-1,) is passed to function on loads return [] # Get user input. grid_choice = CyMap().getCustomMapOption(0) if grid_choice == 2: grid_choice = 1 grid_sizes = [{WorldSizeTypes.WORLDSIZE_DUEL: (5,3), WorldSizeTypes.WORLDSIZE_TINY: (6,4), WorldSizeTypes.WORLDSIZE_SMALL: (8,5), WorldSizeTypes.WORLDSIZE_STANDARD: (13,10), WorldSizeTypes.WORLDSIZE_LARGE: (13,8), WorldSizeTypes.WORLDSIZE_HUGE: (16,10)}, {WorldSizeTypes.WORLDSIZE_DUEL: (4,4), WorldSizeTypes.WORLDSIZE_TINY: (5,5), WorldSizeTypes.WORLDSIZE_SMALL: (6,6), WorldSizeTypes.WORLDSIZE_STANDARD: (8,8), WorldSizeTypes.WORLDSIZE_LARGE: (10,10), WorldSizeTypes.WORLDSIZE_HUGE: (13,13)} ] [eWorldSize] = argsList return grid_sizes[grid_choice][eWorldSize] def generatePlotTypes(): NiTextOut("Setting Plot Types (Python Team Battleground) ...") global hinted_world, mapRand global fractal_world gc = CyGlobalContext() map = CyMap() mapRand = gc.getGame().getMapRand() userInputPlots = map.getCustomMapOption(0) if userInputPlots == 2: # Four Corners hinted_world = HintedWorld() iNumPlotsX = map.getGridWidth() iNumPlotsY = map.getGridHeight() centery = (hinted_world.h - 1)//2 centerx = (hinted_world.w - 1)//2 iCenterXList = [] iCenterXList.append(centerx-1) iCenterXList.append(centerx) iCenterXList.append(centerx+1) iCenterYList = [] iCenterYList.append(centery-1) iCenterYList.append(centery) iCenterYList.append(centery+1) bridgey = centery # Set all blocks to land except a strip in the center for x in range(hinted_world.w): for y in range(hinted_world.h): if x == centerx: if y == bridgey: hinted_world.setValue(x,y,128) # coast else: hinted_world.setValue(x,y,0) else: hinted_world.setValue(x,y,255) if y in iCenterYList: hinted_world.setValue(x,y,128) # coast if y == centery: hinted_world.setValue(x,y,0) # ocean hinted_world.buildAllContinents() plotTypes = hinted_world.generatePlotTypes(20) # Remove any land bridge that exists centerplotx = (iNumPlotsX - 1)//2 dx = 1 for x in range(centerplotx-dx, centerplotx+dx+1): for y in range(iNumPlotsY): i = map.plotNum(x, y) if plotTypes[i] != PlotTypes.PLOT_OCEAN: plotTypes[i] = PlotTypes.PLOT_OCEAN centerploty = (iNumPlotsY - 1)//2 dy = 1 for y in range(centerploty-dy, centerploty+dy+1): for x in range(iNumPlotsX): i = map.plotNum(x, y) if plotTypes[i] != PlotTypes.PLOT_OCEAN: plotTypes[i] = PlotTypes.PLOT_OCEAN # Now add the bridge across the center! sizekey = map.getWorldSize() sizevalues = { WorldSizeTypes.WORLDSIZE_DUEL: 3, WorldSizeTypes.WORLDSIZE_TINY: 4, WorldSizeTypes.WORLDSIZE_SMALL: 5, WorldSizeTypes.WORLDSIZE_STANDARD: 8, WorldSizeTypes.WORLDSIZE_LARGE: 10, WorldSizeTypes.WORLDSIZE_HUGE: 12 } shift = sizevalues[sizekey] linewidth = 3 offsetstart = 0 - int(linewidth/2) offsetrange = range(offsetstart, offsetstart + linewidth) westX1, southY1, eastX1, northY1 = centerplotx - shift, centerploty - shift, centerplotx + shift, centerploty + shift westX2, southY2, eastX2, northY2 = centerplotx - shift, centerploty - shift, centerplotx + shift, centerploty + shift bridge_data = [[westX1, southY1, eastX1, northY1], [westX2, northY2, eastX2, southY2]] for bridge_loop in range(2): [startx, starty, endx, endy] = bridge_data[bridge_loop] if abs(endy-starty) < abs(endx-startx): # line is closer to horizontal if startx > endx: startx, starty, endx, endy = endx, endy, startx, starty # swap start and end dx = endx-startx dy = endy-starty if dx == 0 or dy == 0: slope = 0 else: slope = float(dy)/float(dx) y = starty for x in range(startx, endx+1): for offset in offsetrange: if map.isPlot(x, int(round(y+offset))): i = map.plotNum(x, int(round(y+offset))) plotTypes[i] = PlotTypes.PLOT_LAND y += slope else: # line is closer to vertical if starty > endy: startx, starty, endx, endy = endx, endy, startx, starty # swap start and end dx, dy = endx-startx, endy-starty if dx == 0 or dy == 0: slope = 0 else: slope = float(dx)/float(dy) x = startx for y in range(starty, endy+1): for offset in offsetrange: if map.isPlot(int(round(x+offset)), y): i = map.plotNum(int(round(x+offset)), y) plotTypes[i] = PlotTypes.PLOT_LAND x += slope return plotTypes elif userInputPlots == 1: # Top vs Bottom fractal_world = FractalWorld(fracXExp=6, fracYExp=6) fractal_world.initFractal(continent_grain = 4, rift_grain = -1, has_center_rift = False, invert_heights = True) plot_types = fractal_world.generatePlotTypes(water_percent = 8) return plot_types else: # Left vs Right iNumPlotsX = map.getGridWidth() iNumPlotsY = map.getGridHeight() hinted_world = HintedWorld(4,2) centerx = (hinted_world.w - 1)//2 centery = (hinted_world.h - 1)//2 bridgey = centery # set all blocks to land except a strip in the center for x in range(hinted_world.w): for y in range(hinted_world.h): if x == centerx: if y == bridgey: hinted_world.setValue(x,y,128) # coast else: hinted_world.setValue(x,y,0) else: hinted_world.setValue(x,y,255) hinted_world.buildAllContinents() plotTypes = hinted_world.generatePlotTypes(20) #fix any land bridge that exists centerplotx = (iNumPlotsX - 1)//2 dx = 1 for x in range(centerplotx-dx, centerplotx+dx+1): for y in range(iNumPlotsY): i = map.plotNum(x, y) if plotTypes[i] != PlotTypes.PLOT_OCEAN: plotTypes[i] = PlotTypes.PLOT_OCEAN return plotTypes class TeamBGTerrainGenerator(CvMapGeneratorUtil.TerrainGenerator): def generateTerrainAtPlot(self, iX, iY): global equator lat = 0.8 * self.getLatitudeAtPlot(iX,iY) if (self.map.plot(iX, iY).isWater()): return self.map.plot(iX, iY).getTerrainType() terrainVal = self.terrainGrass if lat >= self.fSnowLatitude: terrainVal = self.terrainGrass #Eliminate Ice and Tundra elif lat >= self.fTundraLatitude: terrainVal = self.terrainGrass #Eliminiate Ice and Tundra elif lat < self.fGrassLatitude: terrainVal = equator # Equator is grass usually, but desert for TvB else: desertVal = self.deserts.getHeight(iX, iY) plainsVal = self.plains.getHeight(iX, iY) if ((desertVal >= self.iDesertBottom) and (desertVal <= self.iDesertTop) and (lat >= self.fDesertBottomLatitude) and (lat < self.fDesertTopLatitude)): terrainVal = self.terrainDesert elif ((plainsVal >= self.iPlainsBottom) and (plainsVal <= self.iPlainsTop)): terrainVal = self.terrainPlains else: terrainVal =self.terrainGrass if (terrainVal == TerrainTypes.NO_TERRAIN): return self.map.plot(iX, iY).getTerrainType() return terrainVal def generateTerrainTypes(): NiTextOut("Generating Terrain (Python Team Battleground) ...") terraingen = TeamBGTerrainGenerator() terraingen.__init__(iDesertPercent=1) terrainTypes = terraingen.generateTerrain() return terrainTypes class TeamBGFeatureGenerator(CvMapGeneratorUtil.FeatureGenerator): def getLatitudeAtPlot(self, iX, iY): "returns a value in the range of 0.0 (tropical) to 1.0 (polar)" return 0.8 * (abs((self.iGridH/2) - iY)/float(self.iGridH/2)) def addFeatures(): NiTextOut("Adding Features (Python Team Battleground) ...") featuregen = TeamBGFeatureGenerator() featuregen.addFeatures() return 0 def assignStartingPlots(): gc = CyGlobalContext() dice = gc.getGame().getMapRand() global shuffle global shuffledTeams global assignedPlayers assignedPlayers = [0] * gc.getGame().countCivTeamsEverAlive() print assignedPlayers shuffle = gc.getGame().getMapRand().get(2, "Start Location Shuffle - PYTHON") if gc.getGame().countCivTeamsEverAlive() < 5: team_list = [0, 1, 2, 3] shuffledTeams = [] for teamLoop in range(gc.getGame().countCivTeamsEverAlive()): iChooseTeam = dice.get(len(team_list), "Shuffling Regions - TBG PYTHON") shuffledTeams.append(team_list[iChooseTeam]) del team_list[iChooseTeam] CyPythonMgr().allowDefaultImpl() def findStartingPlot(argsList): [playerID] = argsList global assignedPlayers global team_num thisTeamID = CyGlobalContext().getPlayer(playerID).getTeam() teamID = team_num[thisTeamID] assignedPlayers[teamID] += 1 def isValid(playerID, x, y): map = CyMap() numTeams = CyGlobalContext().getGame().countCivTeamsAlive() if numTeams > 4 or numTeams < 2: # Put em anywhere, and let the normalizer sort em out. return true userInputProximity = map.getCustomMapOption(1) if userInputProximity == 2: # Start anywhere! return true global shuffle global shuffledTeams global team_num thisTeamID = CyGlobalContext().getPlayer(playerID).getTeam() teamID = team_num[thisTeamID] userInputPlots = map.getCustomMapOption(0) iW = map.getGridWidth() iH = map.getGridHeight() # Two Teams, Start Together if numTeams == 2 and userInputProximity == 0: # Two teams, Start Together if userInputPlots == 1: # TvB if teamID == 0 and shuffle and y >= iH * 0.6: return true if teamID == 1 and not shuffle and y >= iH * 0.6: return true if teamID == 0 and not shuffle and y <= iH * 0.4: return true if teamID == 1 and shuffle and y <= iH * 0.4: return true return false elif userInputPlots == 0: # LvR if teamID == 0 and shuffle and x >= iW * 0.6: return true if teamID == 1 and not shuffle and x >= iW * 0.6: return true if teamID == 0 and not shuffle and x <= iW * 0.4: return true if teamID == 1 and shuffle and x <= iW * 0.4: return true return false else: # 4C corner = shuffledTeams[teamID] if corner == 0 and x <= iW * 0.4 and y <= iH * 0.4: return true if corner == 1 and x >= iW * 0.6 and y <= iH * 0.4: return true if corner == 2 and x <= iW * 0.4 and y >= iH * 0.6: return true if corner == 3 and x >= iW * 0.6 and y >= iH * 0.6: return true return false # Three or Four Teams elif (numTeams == 3 or numTeams == 4) and userInputProximity == 0: # 3 or 4 teams, Start Together corner = shuffledTeams[teamID] if corner == 0 and x <= iW * 0.4 and y <= iH * 0.4: return true if corner == 1 and x >= iW * 0.6 and y <= iH * 0.4: return true if corner == 2 and x <= iW * 0.4 and y >= iH * 0.6: return true if corner == 3 and x >= iW * 0.6 and y >= iH * 0.6: return true return false elif (numTeams == 3 or numTeams == 4) and userInputProximity == 1: # 3 or 4 teams, Start Separated corner = shuffledTeams[teamID] + assignedPlayers[teamID] while corner >= 4: corner -= 4 if corner == 0 and x <= iW * 0.4 and y <= iH * 0.4: return true if corner == 1 and x >= iW * 0.6 and y <= iH * 0.4: return true if corner == 2 and x <= iW * 0.4 and y >= iH * 0.6: return true if corner == 3 and x >= iW * 0.6 and y >= iH * 0.6: return true return false # Two Teams, Start Separated elif numTeams == 2 and userInputProximity == 1: # Two teams, Start Separated if (shuffle and teamID == 0) or (not shuffle and teamID == 1): side = assignedPlayers[teamID] else: side = 1 + assignedPlayers[teamID] while side >= 2: side -= 2 if userInputPlots == 1: # TvB if teamID == 0 and side and y >= iH * 0.6: return true if teamID == 1 and not side and y >= iH * 0.6: return true if teamID == 0 and not side and y <= iH * 0.4: return true if teamID == 1 and side and y <= iH * 0.4: return true return false elif userInputPlots == 0: # LvR if teamID == 0 and side and x >= iW * 0.6: return true if teamID == 1 and not side and x >= iW * 0.6: return true if teamID == 0 and not side and x <= iW * 0.4: return true if teamID == 1 and side and x <= iW * 0.4: return true return false else: # 4C corner = shuffledTeams[side] if corner == 0 and x <= iW * 0.4 and y <= iH * 0.4: return true if corner == 1 and x >= iW * 0.6 and y <= iH * 0.4: return true if corner == 2 and x <= iW * 0.4 and y >= iH * 0.6: return true if corner == 3 and x >= iW * 0.6 and y >= iH * 0.6: return true return false # All conditions have failed? Wow. Is that even possible? :) return true return CvMapGeneratorUtil.findStartingPlot(playerID, isValid) def normalizeStartingPlotLocations(): numTeams = CyGlobalContext().getGame().countCivTeamsAlive() userInputProximity = CyMap().getCustomMapOption(1) if (numTeams > 4 or numTeams < 2) and userInputProximity == 0: CyPythonMgr().allowDefaultImpl() else: return None def getRiverStartCardinalDirection(argsList): "Returns the cardinal direction of the first river segment." pPlot = argsList[0] print pPlot map = CyMap() x, y = pPlot.getX(), pPlot.getY() iW = map.getGridWidth() iH = map.getGridHeight() userInputPlots = CyMap().getCustomMapOption(0) if userInputPlots == 0: if x < iW/2: return CardinalDirectionTypes.CARDINALDIRECTION_EAST else: return CardinalDirectionTypes.CARDINALDIRECTION_WEST elif userInputPlots == 2: if y < iH/2: return CardinalDirectionTypes.CARDINALDIRECTION_NORTH else: return CardinalDirectionTypes.CARDINALDIRECTION_SOUTH else: CyPythonMgr().allowDefaultImpl() def normalizeAddExtras(): gc = CyGlobalContext() map = CyMap() for i in range(gc.getMAX_CIV_PLAYERS()): if (gc.getPlayer(i).isAlive()): start_plot = gc.getPlayer(i).getStartingPlot() # returns a CyPlot startx, starty = start_plot.getX(), start_plot.getY() plots = [] # build a list of the plots near the starting plot for dx in range(-2,3): for dy in range(-2,3): x,y = startx+dx, starty+dy pLoopPlot = map.plot(x,y) if not pLoopPlot.isNone(): plots.append(pLoopPlot) resources_placed = [] for pass_num in range(7): bIgnoreUniqueRange = pass_num >= 1 bIgnoreOneArea = pass_num >= 2 bIgnoreAdjacent = pass_num >= 3 for bonus in range(gc.getNumBonusInfos()): type_string = gc.getBonusInfo(bonus).getType() if (type_string not in resources_placed) and (type_string in resourcesToBalance): for (pLoopPlot) in plots: if (pLoopPlot.canHaveBonus(bonus, True)): if isBonusValid(bonus, pLoopPlot, bIgnoreUniqueRange, bIgnoreOneArea, bIgnoreAdjacent): pLoopPlot.setBonusType(bonus) resources_placed.append(type_string) #print "placed", type_string, "on pass", pass_num break # go to the next bonus for i in range(gc.getMAX_CIV_PLAYERS()): if (gc.getPlayer(i).isAlive()): start_plot = gc.getPlayer(i).getStartingPlot() # returns a CyPlot startx, starty = start_plot.getX(), start_plot.getY() plots = [] # build a list of the plots near the starting plot for dx in range(-3,4): for dy in range(-3,4): x,y = startx+dx, starty+dy pLoopPlot = map.plot(x,y) if not pLoopPlot.isNone(): plots.append(pLoopPlot) resources_placed = [] for pass_num in range(4): bIgnoreUniqueRange = pass_num >= 1 bIgnoreOneArea = pass_num >= 2 bIgnoreAdjacent = pass_num >= 3 for bonus in range(gc.getNumBonusInfos()): type_string = gc.getBonusInfo(bonus).getType() if (type_string not in resources_placed) and (type_string in stratresourcesToBalance): for (pLoopPlot) in plots: if (pLoopPlot.canHaveBonus(bonus, True)): if isBonusValid(bonus, pLoopPlot, bIgnoreUniqueRange, bIgnoreOneArea, bIgnoreAdjacent): pLoopPlot.setBonusType(bonus) resources_placed.append(type_string) #print "placed", type_string, "on pass", pass_num break # go to the next bonus CyPythonMgr().allowDefaultImpl() # do the rest of the usual normalizeStartingPlots stuff, don't overrride def addBonusType(argsList): [iBonusType] = argsList gc = CyGlobalContext() type_string = gc.getBonusInfo(iBonusType).getType() if (type_string in resourcesToEliminate): return None # don't place any of this bonus randomly else: CyPythonMgr().allowDefaultImpl() # pretend we didn't implement this method, and let C handle this bonus in the default way def isBonusValid(eBonus, pPlot, bIgnoreUniqueRange, bIgnoreOneArea, bIgnoreAdjacent): "Returns true if we can place a bonus here" map = CyMap() gc = CyGlobalContext() iX, iY = pPlot.getX(), pPlot.getY() if (not bIgnoreOneArea) and gc.getBonusInfo(eBonus).isOneArea(): if map.getNumBonuses(eBonus) > 0: if map.getArea(pPlot.getArea()).getNumBonuses(eBonus) == 0: return False if not bIgnoreAdjacent: for iI in range(DirectionTypes.NUM_DIRECTION_TYPES): pLoopPlot = plotDirection(iX, iY, DirectionTypes(iI)) if not pLoopPlot.isNone(): if (pLoopPlot.getBonusType(-1) != -1) and (pLoopPlot.getBonusType(-1) != eBonus): return False if not bIgnoreUniqueRange: uniqueRange = gc.getBonusInfo(eBonus).getUniqueRange() for iDX in range(-uniqueRange, uniqueRange+1): for iDY in range(-uniqueRange, uniqueRange+1): pLoopPlot = plotXY(iX, iY, iDX, iDY) if not pLoopPlot.isNone() and pLoopPlot.getBonusType(-1) == eBonus: return False return True