Naval Mines

BTW, can you post more context? I always wonder what's above when I see a dangling "elif" all by itself. And yes, you still need to address the pUnit2Type problem.
 
BTW, can you post more context? I always wonder what's above when I see a dangling "elif" all by itself. And yes, you still need to address the pUnit2Type problem.

This probably one of the most important functions in the mod. CvEventManager. In it's present form, it's really big! Here it is:

Spoiler :

Code:
def onUnitMove(self, argsList):
	'unit move'
	pPlot,pUnit,pOldPlot = argsList
	player = PyPlayer(pUnit.getOwner())
	unitInfo = PyInfo.UnitInfo(pUnit.getUnitType())		
# Mine Warfare Mod		
	# Naval
	iUnitNavalMine = gc.getInfoTypeForString("UNIT_NAVAL_MINE")
	iUnitNavalMineField = gc.getInfoTypeForString("UNIT_NAVAL_MINE_FIELD")
	iUnitNuclearNavalMine = gc.getInfoTypeForString("UNIT_NUCLEAR_NAVAL_MINE")
	iUnitNavalMineSweeper = gc.getInfoTypeForString("UNIT_NAVAL_MINE_SWEEPER")
	# Land
	iUnitLandMine = gc.getInfoTypeForString("UNIT_LAND_MINE")
	iUnitLandMineField = gc.getInfoTypeForString("UNIT_LAND_MINE_FIELD")
	iUnitNuclearLandMine = gc.getInfoTypeForString("UNIT_NUCLEAR_LAND_MINE")
	iUnitLandMineSweeper = gc.getInfoTypeForString("UNIT_LAND_MINE_SWEEPER")			
	# Combat Units
	iArmorUnits = gc.getInfoTypeForString("UNITCOMBAT_ARMOR")
	iHelicopterUnits = gc.getInfoTypeForString("UNITCOMBAT_HELICOPTER")
	iAirUnits = gc.getInfoTypeForString("UNITCOMBAT_AIR")
	iMineWarfare = gc.getInfoTypeForString("UNITCOMBAT_MINE_WARFARE")
		
	CombatList = [			
		iArmorUnits,
		iHelicopterUnits,
		iAirUnits,
		iMineWarfare
	]
		
				
	MasterMineList = [
		iUnitNavalMine,
		iUnitNavalMineField,
		iUnitNuclearNavalMine,
		iUnitLandMine,
		iUnitLandMineField,
		iUnitNuclearLandMine
	]
		
	MineList = [
		iUnitNavalMine,	
		iUnitLandMine,	
	]
		
	MineFieldList = [
		iUnitNavalMineField,	
		iUnitLandMineField
	]
		
	SweeperList = [
		iUnitNavalMineSweeper,
		iUnitLandMineSweeper
	]
				
	FoundMine = False
	MineBlownup = False
	UnitBlownup = False
	UnitsBlownup = False		
	AIRemoveMine = False
	iCount = 0
		
	for iUnitLoop in range (pPlot.getNumUnits()):
		pUnit1 = pPlot.getUnit(iUnitLoop)
		pUnit1Type = pUnit1.getUnitType()
		# Is this a mine?
		if (pUnit1Type in MasterMineList):
			FoundMine = True
			break		
		
	if FoundMine:
		for iUnitLoop in range (pPlot.getNumUnits()):
			pUnit2 = pPlot.getUnit(iUnitLoop)
			pUnit2Type = pUnit2.getUnitType()
			pUnit2CombatType = pUnit2.getUnitCombatType()
			
			if pUnit2Type != -1:
				info = gc.getUnitInfo(pUnit2Type)
				if (info != -1):
					iTech = info.getPrereqAndTech()
					if iTech != -1:
						iEra = gc.getTechInfo(iTech).getEra()													
					else:
						iEra = 0
			else:
				iMaxEra = -1
				
			if pUnit1.getOwner() != pUnit2.getOwner():
				# is pUnit2 a Sweeper?						
				if (pUnit2Type in SweeperList):
					if gc.getPlayer(pUnit2.getOwner()).isHuman( ):
						CyAudioGame().Play2DSound("AS2D_SHIP_RED_ALERT")
						CyInterface().addImmediateMessage("You have discovered an enemy mine!", "")
						break
									
					else:	
						# AI removes the Mine.
						UnitName = pUnit1.getName()
						AIRemoveMine = True
						break
															
					
				[COLOR="Red"][B]elif not (pUnit2Type in CombatList):[/B][/COLOR]					
					# pUnit2 is not a Sweeper.
					if (pUnit1Type in MineList):
						# Blow up the one unit.
						UnitName = pUnit2.getName()
						pUnit2.kill( -1, -1 )
						UnitBlownup = True
						MineBlownup = True
						break
					
					elif (pUnit1Type in MineFieldList):
						if not (pUnit2Type in MasterMineList):
							if MineWarfare.RandumChanceToDestroyUnit(pPlot, pUnit2, iEra):
								pUnit2.kill( 0, -1 )
								iCount = iCount + 1
								UnitsBlownup = True
							else:
								UnitsBlownup = False				
		
	if AIRemoveMine:
		CyInterface().addImmediateMessage("Your " + str(UnitName) + " was removed by a Minesweeper" , "")
		# Destroy the Mine.
		CyAudioGame().Play2DSound("AS2D_SHIP_LOAD_MINE")
		pUnit1.kill( 0, -1 )
			
			
	if UnitBlownup:
		CyEngine().triggerEffect(gc.getInfoTypeForString("EFFECT_EXPLOSION_LARGE_001"), pPlot.getPoint())
		CyAudioGame().Play2DSound("AS2D_LAND_MINE_EXPLOSION")			
		CyInterface().addImmediateMessage("An enemy " + str(UnitName) + " was blown up by your mine!", "")
			
	if MineBlownup:
		# Destroy the Mine.
		pUnit1.kill( 0, -1 )

	if UnitsBlownup:
		CyEngine().triggerEffect(gc.getInfoTypeForString("EFFECT_EXPLOSION_LARGE_001"), pPlot.getPoint())
		CyAudioGame().Play2DSound("AS2D_LAND_MINE_EXPLOSION")
		CyInterface().addImmediateMessage("One or more units were blown up by your mine field!", "")					
# Mine Warfare Mod
		
	if (not self.__LOG_MOVEMENT):
		return
	if player and unitInfo:
		CvUtil.pyPrint('Player %d Civilization %s unit %s is moving to %d, %d'
			%(player.getID(), player.getCivilizationName(), unitInfo.getDescription(),
			pUnit.getX(), pUnit.getY()))
 
Okay, thanks. BTW, third time: change that red line to this:

Code:
elif not (pUnit2[B][COLOR="Red"]Combat[/COLOR][/B]Type in CombatList):

You really should move the construction of all those lists outside of the onUnitMove() function to improve performance. You are building and destroying those lists every time a unit moves! :eek: I would say to move them to the __init__() function, but I don't recall if they will work there. CvEventManager is created early on in the initialization cycle--probably before that stuff is ready.
 
I'm sorry if this is obvious but I don't understand why you're checking if the unit's, that moves to the tile, type is in your unit combat list. :confused: It doesn't make sense to me, or I'm missing something badly.
So shouldn't you change this:
Code:
				elif not ([COLOR="red"]pUnit2Type[/COLOR] in CombatList):
to this:
Code:
				elif not ([COLOR="Red"]pUnit2CombatType[/COLOR] in CombatList):

EDIT: Oh, EmperorFool was first...
 
Okay, thanks. BTW, third time: change that red line to this:

Code:
elif not (pUnit2[B][COLOR="Red"]Combat[/COLOR][/B]Type in CombatList):

You really should move the construction of all those lists outside of the onUnitMove() function to improve performance. You are building and destroying those lists every time a unit moves! :eek: I would say to move them to the __init__() function, but I don't recall if they will work there. CvEventManager is created early on in the initialization cycle--probably before that stuff is ready.

It worked. I'm just tired and needed another set of eyes to pick out the really obvious. :hammer2:

...and yes, I do need to improve the performance. But, first things are first. My goal is to get it all to work and then tweak it.

:thanx:


Orion Veteran :cool:
 
You really should move the construction of all those lists outside of the onUnitMove() function to improve performance. You are building and destroying those lists every time a unit moves! :eek: I would say to move them to the __init__() function, but I don't recall if they will work there. CvEventManager is created early on in the initialization cycle--probably before that stuff is ready.

OK while I am testing my latest code for land mine placement:

I tried relocating the lists in the __init__() function, but the code stopped working. These and other lists are standard and are used throughout the mod. Is there a place to globally set them up one time, that loads early enough and that can be accessed by any and all python files in the mod?
 
If you move them to __init__() you must put "self." in front of them all when creating and using them. This places them as variables of the CvEventManager object itself (self).

Code:
class CvEventManager:
    def __init__(self):
        ...
        [B]self.unitTypeList[/B] = [ blah blah blah]

...

    def onUnitBuilt(self, argsList):
        ...
        if iUnitType in [B]self.unitTypeList[/B]:
            ...

If you were using BUG you could put all this into a module-level init() function that BUG would call as your mod starts up.
 
If you move them to __init__() you must put "self." in front of them all when creating and using them. This places them as variables of the CvEventManager object itself (self).

Code:
class CvEventManager:
    def __init__(self):
        ...
        [B]self.unitTypeList[/B] = [ blah blah blah]

...

    def onUnitBuilt(self, argsList):
        ...
        if iUnitType in [B]self.unitTypeList[/B]:
            ...

If you were using BUG you could put all this into a module-level init() function that BUG would call as your mod starts up.

After a lot of bone head mistakes and a few adjustments, I finally got it to work.

Thanks,

Orion Veteran :cool:
 
I'm attempting to get the distance from one plot to another. Therefore, I am using the calculatePathDistance command. Problem is the command fails to return anything but -1 for Ocean plots. Looking at the rules for this command; If a mine has just been built in a city, is the city considered a land plot? If so, how can I get around this rule?

INT calculatePathDistance (CyPlot pSource, CyPlot pDest)
finds the shortest passable path between two CyPlots and returns its length, or returns -1 if no such path exists. Note: the path must be all-land or all-water

Code:
iDistance = CyMap().calculatePathDistance(pUnitPlot, pLoopPlot)

if iDistance > 0 and iDistance < iMaxRange:


Orion Veteran :cool:
 
Problem is the command fails to return anything but -1 for Ocean plots. Looking at the rules for this command; If a mine has just been built in a city, is the city considered a land plot? If so, how can I get around this rule?

My stupid suggestion: Check the city's surrounding plots for a water plot and take this instead of the city plot.
 
I can't even look at the A* path-finding code to verify that this is correct since it's in the EXE. :( Go with The_J's suggestion by looping over all plots around the city and getting the path for each one that is water and in the same area (or maybe skip this check) and pick the one with the shortest path. Or just pick the first water tile's distance.
 
I can't even look at the A* path-finding code to verify that this is correct since it's in the EXE. :( Go with The_J's suggestion by looping over all plots around the city and getting the path for each one that is water and in the same area (or maybe skip this check) and pick the one with the shortest path. Or just pick the first water tile's distance.

These mines are like hermits, as they don't want to venture out of the city, where they were built. Once they do venture out, the code works well to move them to a desireable plot. So, what I really need is to force them to step one square out of the city. Is there a simple way to do this without looping through all of the plots on the map?

Orion Veteran :cool:
 
Loop through all the plots adjacent to the city looking for one that's not a fresh-water lake.
 
Loop through all the plots adjacent to the city looking for one that's not a fresh-water lake.

The number of hours and now days I am putting into these last two functions is frustrating! The mod is useless, if I can't get these last two functions to work right.

This function below actually moved the mine into a fresh water lake plot and not the coastal plot that is next to the open sea. Any tips on how to fix this?

Spoiler :

Code:
def doPreAI_NavalMinePlacement(pUnit):
	FoundCoastalPlot = False
	iOwner = pUnit.getOwner()
	AIpPlayer = gc.getPlayer(iOwner)
	iX = pUnit.getX()
	iY = pUnit.getY()
	pUnitPlot = CyMap().plot(iX, iY)
		
	# Make sure this is an AI Player
	if not AIpPlayer.isHuman():
		for iiX in range(iX-1, iX+1, 1):
			for iiY in range(iY-1, iY+1, 1):
				pAdjacentPlot = CyMap().plot(iiX,iiY)
				if pAdjacentPlot.isNone() == False:
					iTerrain = pAdjacentPlot.getTerrainType()
					if iTerrain == gc.getInfoTypeForString("TERRAIN_COAST"):
						if not pAdjacentPlot.isFreshWater():
							if not hasFriendlyMine(pAdjacentPlot, iOwner):
								FoundCoastalPlot = True
								CoastalPlot = pAdjacentPlot
								break
							
	if FoundCoastalPlot:
		pUnit.getGroup().pushMission(MissionTypes.MISSION_MOVE_TO, CoastalPlot.getX( ), CoastalPlot.getY( ), 0, False, True, MissionAITypes.NO_MISSIONAI, pUnit.plot(), pUnit)
		#CyInterface().addImmediateMessage("Found Coastal Plot", "")
		CyInterface().addImmediateMessage("Moved the Mine!", "")


Orion Veteran :cool:
 
Two things.

1) Instead of pAdjacentPlot.isFreshWater() you should probably be using pAdjacentPlot.isLake().

2) By checking to see if there is already a friendly mine on the plot, you can apparently make it impossible for the new mine to leave the city. For example, say the city is at the end of one of those 1 tile wide estuaries (so the city only has 1 ocean tile adjacent to it). You deliberately place a mine there (it is probably a good place for one). Your algorithm will no longer move a new mine out of the city. You need to differentiate between an unarmed mine and armed mines: even if you can only have one armed mine on a plot doesn't mean you shouldn't be able to move unarmed mines through that plot.
 
Two things.

1) Instead of pAdjacentPlot.isFreshWater() you should probably be using pAdjacentPlot.isLake().

2) By checking to see if there is already a friendly mine on the plot, you can apparently make it impossible for the new mine to leave the city. For example, say the city is at the end of one of those 1 tile wide estuaries (so the city only has 1 ocean tile adjacent to it). You deliberately place a mine there (it is probably a good place for one). Your algorithm will no longer move a new mine out of the city. You need to differentiate between an unarmed mine and armed mines: even if you can only have one armed mine on a plot doesn't mean you shouldn't be able to move unarmed mines through that plot.

Good points. I'll let you know how it works.

Edit: The function fails to move the mine if I use pAdjacentPlot.isLake().
 
I just noticed something:

In the iiX and iiY loops, the range() function is wrong. You need to increase the upper end of the range by 1 since range(i,j,1) includes i but not j, it stops at j-1. The second argument is not the highest value or the count of the number of values, it is the value which makes the generator stop producing numbers if the next one would be greater than or equal to it.

So you need
Code:
		for iiX in range(iX-1, iX+2):
			for iiY in range(iY-1, iY+2):
You can leave the ",1" off the end since that is the default.

Using range(3) is the same as range(0,3,1), they both produce the numbers 0, 1, and 2 but not 3. Using range(1,3) is the same as range(1,3,1), they both produce the numbers 1 and 2 but not 3.
 
I just noticed something:

In the iiX and iiY loops, the range() function is wrong. You need to increase the upper end of the range by 1 since range(i,j,1) includes i but not j, it stops at j-1. The second argument is not the highest value or the count of the number of values, it is the value which makes the generator stop producing numbers if the next one would be greater than or equal to it.

So you need
Code:
		for iiX in range(iX-1, iX+2):
			for iiY in range(iY-1, iY+2):
You can leave the ",1" off the end since that is the default.

Using range(3) is the same as range(0,3,1), they both produce the numbers 0, 1, and 2 but not 3. Using range(1,3) is the same as range(1,3,1), they both produce the numbers 1 and 2 but not 3.

Using the range you provided, I have mines moving all over the place with each one moving out 3 squares. I only need to move each mine just 1 square. At that point I will run another function to select the best rated plot and move the mine to that best plot. But for now, I want to hold the mine to only one square, which is adjacent to the city, where it was built. Here is the latest code:

Spoiler :

Code:
def doPreAI_NavalMinePlacement(pUnit):
	FoundCoastalPlot = False
	iOwner = pUnit.getOwner()
	AIpPlayer = gc.getPlayer(iOwner)
	iX = pUnit.getX()
	iY = pUnit.getY()
	pUnitPlot = CyMap().plot(iX, iY)
		
	# Make sure this is an AI Player
	if not AIpPlayer.isHuman():
		for iiX in range(iX-1, iX+2):
			for iiY in range(iY-1, iY+2):			
				pAdjacentPlot = CyMap().plot(iiX,iiY)
				if pAdjacentPlot.isNone() == False:
					# LoopPlot must be unowned or owned by the AI Player
					if (pAdjacentPlot.getOwner() == -1) or (pAdjacentPlot.getOwner() == iOwner):
						# Make sure this is not a city.
						if not pAdjacentPlot.isCity():
							iTerrain = pAdjacentPlot.getTerrainType()
							if iTerrain == gc.getInfoTypeForString("TERRAIN_COAST"):
								if not pAdjacentPlot.isLake():
									FoundCoastalPlot = True
									CoastalPlot = pAdjacentPlot
									break
							
			if FoundCoastalPlot:
				break				
							
	if FoundCoastalPlot:
		pUnit.getGroup().pushMission(MissionTypes.MISSION_MOVE_TO, CoastalPlot.getX( ), CoastalPlot.getY( ), 0, False, True, MissionAITypes.NO_MISSIONAI, pUnit.plot(), pUnit)
		CyInterface().addImmediateMessage("Moved the Mine!", "")


Edit: Changed function as: if not pAdjacentPlot.isLake(): is now working. Also prevented the mine from entering into plots that are owned by another Civ. Mines are still moving 3 spaces. I could run the next function from that plot, but I would prefer to run it from the plot that is adjacent to the city.

Orion Veteran:
 
Back
Top Bottom