ClassicThunder
Prince
- Joined
- Sep 15, 2006
- Messages
- 511
I'm having stability issues with my mod. I've traced it to what I believe is the source of the error. If I remove all of the InitUnit instances then I can auto play game after game with no CTD. If I leave it in then I can't run a game through. Would anyone know why this function would could cause CTDs?
This code works. If I uncomment the InitUnits the crashes begin.
This code works. If I uncomment the InitUnits the crashes begin.
Spoiler :
Code:
## Copyright ClassicThunder 2006-2007
## CreateNests by Sto
## Everything else by ClassicThunder
from CvPythonExtensions import *
import CvUtil
import CvScreensInterface
import CvDebugTools
import CvWBPopups
import PyHelpers
import Popup as PyPopup
import CvCameraControls
import CvTopCivs
import sys
import CvWorldBuilderScreen
import CvAdvisorUtils
import CvTechChooser
import pickle
import math
import string
from CvPythonExtensions import *
import CvMapGeneratorUtil
gc = CyGlobalContext()
localText = CyTranslator()
PyPlayer = PyHelpers.PyPlayer
PyInfo = PyHelpers.PyInfo
class DragonTools:
def __init__(self):
pass
def CreateNests(self): ##Writen By Sto. You're awsome man!!
# initializing datas :
gc = CyGlobalContext()
map = CyMap()
dice = gc.getGame().getSorenRand()
iW = map.getGridWidth()
iH = map.getGridHeight()
iBonusDragon = gc.getInfoTypeForString('BONUS_DRAGONROOST')
iUnitDragon = gc.getInfoTypeForString('UNIT_LAIRDRAGON')
bPlayer = gc.getPlayer(gc.getBARBARIAN_PLAYER())
# the minimum distance between a dragon and an unit on the same area (by air)
iRangeUnit = 6
# the minimum range between a dragon and an unit , even if not on the same area (by air).
iAbsoluteRangeUnit = 3
# the minimum distance between two dragons on the same area (by air)
iMaxRangeDragons = 8
iMinRangeDragons = 6
# the number of time that the function try to place a nest per area , per range of dragons.
# As the plot is aleatory selected and if the size of the area is at the limit ,
# sometimes the first plot can prevent the second to be placed ( for an example )
iPass = 10
# if all nest nest can't be placed on islands or players area , the percent of distance to check if a player have access to a nest
# i.e if the nearest player to a nest is at 12 squares , take players at 1.40 * 12 like possible players to get the nest .
# must be a float number ( 1.40 = 140 % )
percentDistance = 1.40
# if all nest nest can't be placed on islands or players area , the indent distance to check if a player have access to a nest
# i.e if the nearest player to a nest is at 12 squares , take players between 12 and 15 like possible players to get the nest . ( for small maps )
indentDistance = 3
#create library to stock plot coordinates per areas into two libraries : with starting locations and without
areaPlotsPlayers = {}
areaPlotsIslands = {}
areas = CvMapGeneratorUtil.getAreas()
for area in areas :
if area.isWater() : continue
# else create a list where to store plot coordinates into the libraries
if area.getNumStartingPlots()>0 : areaPlotsPlayers[area.getID()]=[]
else : areaPlotsIslands[area.getID()]=[]
# create the list of player and starting plots . used later .
playerList = []
startPlotList = []
nbPlayerMax=gc.getMAX_CIV_PLAYERS()
staX=[0 for i in range(nbPlayerMax)]
staY=[0 for i in range(nbPlayerMax)]
for iPlayer in range(nbPlayerMax):
if gc.getPlayer(iPlayer).isEverAlive() and (iPlayer!=gc.getBARBARIAN_PLAYER()): #just in case barbarian becomes a playable civ ...
startPlot = gc.getPlayer(iPlayer).getStartingPlot()
staX[iPlayer]=startPlot.getX()
staY[iPlayer]=startPlot.getY()
playerList.append(iPlayer)
startPlotList.append((startPlot.getX(),startPlot.getY()))
nbNestToPlace = len(playerList) # note that nbNestToPlace is used as well for nb nest to place or for number of players in the file .
# create the list of team . used to check visibility .
teamList=[]
for iTeam in range(gc.getMAX_TEAMS()):
iLoopTeam = gc.getTeam(iTeam)
if iLoopTeam.isNone() : continue
if iLoopTeam.isBarbarian() : continue
if not iLoopTeam.isAlive() : continue
teamList.append(iTeam)
# create a mask of coordinates plot <= iRangeUnit squares around every non barbarian units
maskPlots = []
# create a mask to be sure that a nest is not on an island close to a starting plot
absoluteMask = []
for iX in range(iW) :
for iY in range(iH) :
pLoopPlot = map.plot(iX, iY)
if pLoopPlot.getNumUnits()==0 : continue
# if the unit is barbarian , just remove the barbarian plot from the possible nest placement
if pLoopPlot.getUnit(0).isBarbarian() :
maskPlots.append((iX, iY))
continue
# if the plot has a non barbarian unit , make sure it's a starting plot
# you can remove this test because you ask for a minimum of 8 squares from every unit .
# I think that checking starting plot is enought ( in case the starting units are not on the same plot of the settler ... )
# In case there is non barbarian units far from starting plots , the visible test is enough .
if not (iX,iY) in startPlotList : continue
# else add iRangeUnit square around to mask and absolute mask
maskPlots += makeMask(iX,iY,iRangeUnit)
absoluteMask += makeMask(iX,iY,iAbsoluteRangeUnit)
# create the list of plots per area
for iX in range(iW) :
for iY in range(iH) :
pLoopPlot = map.plot(iX, iY)
if pLoopPlot.isWater() : continue
if pLoopPlot.isImpassable() : continue
if pLoopPlot.isOwned() : continue # made for scenarios/scripts with barbarian cities at the beginning of the game
bVisible=False
for iTeam in teamList :
if pLoopPlot.isVisible(iTeam ,False) : bVisible=True
if bVisible : continue
iArea = pLoopPlot.getArea()
if iArea in areaPlotsPlayers.keys() :
if (iX,iY) in maskPlots : continue
areaPlotsPlayers[iArea].append((iX,iY))
elif iArea in areaPlotsIslands.keys() :
if (iX,iY) in absoluteMask : continue
areaPlotsIslands[iArea].append((iX,iY))
else :
print " ERROR : can't store plot : "+repr((iX,iY))
# first try to place every nest on players areas and save datas if all nest can't be placed.
nestList = []
posssibleNestList = []
bNearPlayer = False
for iRangeDragons in range( iMaxRangeDragons, iMinRangeDragons-1, -1):
if bNearPlayer : break
for iT in range(iPass):
listNestPlots = []
for iArea in areaPlotsPlayers.keys() :
nbNestToPlaceArea = map.getArea(iArea).getNumStartingPlots()
listAreaPlots = areaPlotsPlayers[iArea]
for iTest in range(iPass):
listNestPlotsTemp = []
listAreaPlotsTemp = [item for item in listAreaPlots] # duplicate the list . now i can modify it without modifing the original
while True :
if len(listAreaPlotsTemp)==0 : break
if len(listNestPlotsTemp)==nbNestToPlaceArea : break
iChoice = dice.get( len(listAreaPlotsTemp) , " Nest Plot coordinates ( players ). " )
(iX,iY) = listAreaPlotsTemp[iChoice]
listNestPlotsTemp.append((iX,iY))
# create a mask of coordinates where the nest can t be placed
maskPlotsTemp = makeMask(iX,iY,iRangeDragons)
# remove the mask plots from the possible nest plot list
listAreaPlotsTemp = [ item for item in listAreaPlotsTemp if not item in maskPlotsTemp ]
if len(listNestPlotsTemp)==nbNestToPlaceArea : break # all nest placed in this area .
listNestPlots += listNestPlotsTemp
if len(listNestPlots) == nbNestToPlace: #all nest placed
bNearPlayer = True
nestList = listNestPlots
print " All nests can be placed near players ..."
break
else : #store datas
posssibleNestList.append(list(listNestPlots))
if not bNearPlayer : # try to place nests on islands , and save datas if all nests can't be placed on islands
bNearIsland = False
posssibleIslandNestList = []
listArea = [ areaPlotsIslands[iArea] for iArea in areaPlotsIslands.keys() ] # change the library into a list
# calculate the max number of nests per Areas
lenlistArea = [ len(item) for item in listArea ]
nbTilesTot = float(sum(lenlistArea ))
nbMaxNests = [ int(lenlistArea[i]*nbNestToPlace/nbTilesTot)+1 for i in range(len(lenlistArea)) ]
# try to place nests
for iRangeDragons in range( iMaxRangeDragons, iMinRangeDragons-1, -1):
if bNearIsland : break
for iT in range(iPass):
listNestPlots = []
nbNestsPlaced = 0
listAreaTemp = [item for item in listArea]
nbMaxNestsTemp = [item for item in nbMaxNests]
while True :
if nbNestsPlaced == nbNestToPlace : break
if len(listAreaTemp)==0 : break
# place first on biggest islands
lLen = [ len(item) for item in listAreaTemp ]
maxL = max(lLen)
indexList = [ lLen.index(item) for item in lLen if item==maxL ]
iList = indexList[dice.get( len(indexList) , " island choice " )]
listAreaPlotsTemp = [ item for item in listAreaTemp[iList] ]
nbPlacedArea = 0
while True :
if nbNestsPlaced == nbNestToPlace : break
if nbPlacedArea == nbMaxNestsTemp[iList] : break
if len(listAreaPlotsTemp)==0 : break
iChoice = dice.get( len(listAreaPlotsTemp) , " Nest Plot coordinates ( islands ) . " )
(iX,iY) = listAreaPlotsTemp[iChoice]
listNestPlots.append((iX,iY))
nbPlacedArea += 1
nbNestsPlaced += 1
# create a mask of coordinates where the nest can t be placed
maskPlotsTemp = makeMask(iX,iY,iRangeDragons)
# remove the mask plots from the possible nest plot list
listAreaPlotsTemp = [ item for item in listAreaPlotsTemp if not item in maskPlotsTemp ]
del(listAreaTemp[iList])
del(nbMaxNestsTemp[iList])
if len(listNestPlots) == nbNestToPlace: #all nest placed
bNearIsland = True
nestList = listNestPlots
print " All nests can be placed on islands ..."
break
else : #store datas
posssibleIslandNestList.append(list(listNestPlots))
if len(nestList)==0 : # Nests can't be all placed near players or on islands
if len(posssibleNestList)>0 :
# first , take only the largest list of nests near players
maxL = max([len(item) for item in posssibleNestList])
pListNests = [item for item in posssibleNestList if len(item)==maxL]
# try to equilibrate nests :
coefPlayers = [ [ 0 for i in range(nbNestToPlace) ] for j in range(len(pListNests)) ]
for iLNest in range(len(pListNests)):
for (iX,iY) in pListNests[iLNest] :
# calculate path distance with each starting point .
pathTemp = [ map.calculatePathDistance(map.plot(iX,iY),map.plot(staX[playerList[iP]],staY[playerList[iP]])) for iP in range(nbNestToPlace) ]
# pathdist return -1 if no path , so replace -1 with 10000
for iP in range(nbNestToPlace):
if pathTemp[iP]==-1:pathTemp[iP]=10000
# now check and set values for players that are near from the possible nest
minDist = min(pathTemp)
if minDist == 10000 : continue # no available player near nest ( in case of an unreachable nest on the area )
for iP in range(nbNestToPlace) :
if pathTemp[iP]<=minDist*percentDistance or pathTemp[iP]<=minDist+indentDistance :
coefPlayers[iLNest][iP]+=1
# now choose the best list of nest near players :
# first take the lists with the maximum of players that can reach a nest
tpCoefList = [ [ i for i in item if i!=0 ] for item in coefPlayers ]
maxL = max([len(item) for item in tpCoefList])
tpIdList = [ tpCoefList.index(item) for item in tpCoefList if len(item)==maxL ]
# now choose the list with the minimum max coef ... to limit at the minimum a player to be near many nest
maxCoef = 100000
finalId = -1
for idList in tpIdList :
maxTp = max(coefPlayers[idList])
if maxTp < maxCoef :
finalId = idList
maxCoef = int(maxTp)
if finalId != -1 :
nestList += pListNests[finalId]
nPlacedNearPlayer = int(len(nestList))
print " Nests placed near players : "+str(nPlacedNearPlayer)
# now place the rest of nests on islands
nbMaxToPlaceIslands = nbNestToPlace - nPlacedNearPlayer
nbPlacedIslands = 0
if nbMaxToPlaceIslands > 0 : # should be all the time true ... but just in case
toMuchList = []
equalList = []
toLessList = []
for item in posssibleIslandNestList :
if len(item)==nbMaxToPlaceIslands :equalList.append(item)
elif len(item)<nbMaxToPlaceIslands :toLessList.append(item)
else :toMuchList.append(item)
if len(equalList)>0:
iChoice = dice.get( len(equalList) , " Nest Plot List ( islands equal) . " )
nestList += equalList[iChoice]
nbPlacedIslands = nbMaxToPlaceIslands
print " equal list chosen "
elif len(toMuchList)>0: # in case there is to much choice , choose the most disparate one
minL = min([len(item) for item in toMuchList])
tpList = [ item for item in toMuchList if len(item)==minL ]
iChoice = dice.get( len(tpList) , " Nest Plot List ( islands too many ) . " )
tpListNest = tpList[iChoice]
nbPl = 0
while True :
if len(tpListNest) == 0 : break # should not be true ... just in case
if nbPl == nbMaxToPlaceIslands : break
iChoice = dice.get( len(tpListNest) , " Nest Plot ( islands too many ) . " )
nestList.append(tpListNest[iChoice])
del(tpListNest[iChoice])
nbPl += 1
nbPlacedIslands = nbMaxToPlaceIslands
print " too many list chosen "
elif len(toLessList)>0: # in case there is not enough nest to place choose the largest one
maxL = max([len(item) for item in toLessList])
tpList = [ item for item in toLessList if len(item)==maxL ]
iChoice = dice.get( len(tpList) , " Nest Plot List ( islands too less ) . " )
nestList += tpList[iChoice]
nbPlacedIslands = maxL
print " too less list chosen "
else : # no nest can be placed on islands : probably no islands on the map
print " no nest on islands "
else :
print " ERROR : there is no nest to place on islands "
print repr(nestList) # debug tool
#Now create nests :
for (iX,iY) in nestList :
pPlot = map.plot(iX, iY)
##Added by ClassicThunder to remove dragons being on realy small islands
if ( map.getArea((pPlot.getArea())).getNumTiles() > 5):
if pPlot.isNone():
print " ERROR plot is none: "+repr((iX,iY)) # debug tool
continue
pPlot.setBonusType( -1) # better to clear the plot bonus before adding another
pPlot.setBonusType( iBonusDragon)
#bPlayer.initUnit( iUnitDragon, iX, iY, UnitAITypes.NO_UNITAI )
## COMMENT ALL THIS PART BEFORE RELEASE
# A message to check where nests were spawn .
##if bNearPlayer : CyInterface().addImmediateMessage( "Nests spawn on players areas : "+str(nbNestToPlace)+" nests spawned." , "")
##elif bNearIsland : CyInterface().addImmediateMessage( "Nests spawn on islands : "+str(nbNestToPlace)+" nests spawned ." , "")
##else : CyInterface().addImmediateMessage( "Nests spawn on player area : "+str(nPlacedNearPlayer)+" , nests spawned on islands : "+str(nbPlacedIslands)+" , nb nest to place : "+str(nbNestToPlace) , "")
# debug : create signs to have a quick look of the result ...
##map.setRevealedPlots(0,True,False)
##for iPlayer in playerList : CyEngine().addSign(CyMap().plot(staX[iPlayer],staY[iPlayer]),0,"Start Plot")
##for (iX,iY) in nestList : CyEngine().addSign(CyMap().plot(iX,iY),0,"NEST NEST")
def DragonDoTurn(self):
if (CyGame().getGameTurn() > 49):
if ( ((CyGame().getGameTurn()) % 5) == 0 ):
for index in range(CyMap().numPlots()):
pPlot = CyMap().plotByIndex(index)
if (pPlot.getBonusType(-1) == gc.getInfoTypeForString('BONUS_DRAGONROOST')):
if ( 13 < gc.getGame().getSorenRandNum( 15 ,"Random Dragon Spawn")):
#unit = gc.getPlayer( gc.getBARBARIAN_PLAYER() ).initUnit( gc.getInfoTypeForString('UNIT_ATTACKDRAGON'), pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI)
#unit.setUnitAIType(5)
pass
def NewNest(self, pUnit):
if (pUnit.getUnitType() == gc.getInfoTypeForString('UNIT_ATTACKDRAGON')):
pPlot = pUnit.plot()
if ( pPlot.getImprovementType() == gc.getInfoTypeForString('IMPROVEMENT_CITY_RUINS') ):
pUnit.kill(False, -1)
pPlot.setBonusType(gc.getInfoTypeForString('BONUS_DRAGONROOST'))
#gc.getPlayer( gc.getBARBARIAN_PLAYER() ).initUnit( gc.getInfoTypeForString('UNIT_LAIRDRAGON'), pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI)
elif ( pPlot.isCity() == true ):
pUnit.kill(False, -1)
(pPlot.getPlotCity()).kill()
pPlot.setImprovementType(2)
pPlot.setBonusType(gc.getInfoTypeForString('BONUS_DRAGONROOST'))
#gc.getPlayer( gc.getBARBARIAN_PLAYER() ).initUnit( gc.getInfoTypeForString('UNIT_LAIRDRAGON'), pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI)
def DragonKilled(self, pUnit):
if (pUnit.getUnitType() == gc.getInfoTypeForString('UNIT_LAIRDRAGON')):
pPlot = pUnit.plot()
pPlot.setBonusType( -1)
def makeMask(sX,sY,iRange):
map = CyMap()
iW = map.getGridWidth()
iH = map.getGridHeight()
maskTemp=[]
for dX in range(-iRange,iRange+1):
for dY in range(-iRange,iRange+1):
pX = sX + dX
if map.isWrapX() : pX = pX % iW # if the map is wrapX . make the coordinate ( 0 <= pX < iW )
pY = sY + dY
if map.isWrapY() : pY = pY % iH # if the map is wrapY . make the coordinate ( 0 <= pY < iH )
maskTemp.append((pX, pY))
return maskTemp