Sample Python Code

TheLopez said:
naf4ever that is good but this gives you a lot more flexibility.
Basically this is a file almost completely gutted except for the important code and example. Lets call this file CvFoo.py.

Code:
# Import statements
# ...
# ...

# Global variables 
# ...
# ...

# Change this to enable or disable debug messages
g_bDebug = true

LOG_ONLY = 1
SCREEN_ONLY = 2
LOG_AND_SCREEN = 3

# Change to redirect the debug messages to the logs, screen or both.
g_iDebugMessageTarget = LOG_ONLY

class CvFoo:

	def fooBar(self):

		# Debug code - Start
		if(g_bDebug):
			self.printDebugMessage("Hello, World!")
		# Debug code - End		

		return iHangingBodyCount


	# Prints the debug message to the logs, screen or both	
	def printDebugMessage(self, strMessage):
	
		if(g_iDebugMessageTarget == LOG_ONLY):
			CvUtil.pyPrint(strMessage)
		elif(g_iDebugMessageTarget == SCREEN_ONLY):
			CyInterface().addImmediateMessage(strMessage)
		else:
			CvUtil.pyPrint(strMessage)
			CyInterface().addImmediateMessage(strMessage)


Hmm... I guess i meant mine for use mainly for when there are no coding errors. When the code is correct but the problem is some loop or incorrect variable being called up, which happens to me a lot. This way i can print its value to see whats going.

Can you explain what yours here does more? How does it differ from the python popup message i get when im in toggle.gamedebugmode ?
 
naf4ever said:
Hmm... I guess i meant mine for use mainly for when there are no coding errors. When the code is correct but the problem is some loop or incorrect variable being called up, which happens to me a lot. This way i can print its value to see whats going.

Can you explain what yours here does more? How does it differ from the python popup message i get when im in toggle.gamedebugmode ?

Sure, it allows you to turn debug messages on/off and where they should be displayed/recorded.
 
Hello, I do not know if I am on good forium but I have a question. Is you he possible with the Python to make so that the embraque computer of the ICBM in its under sailors. By knowing well on that I already modified code XML por that it is possible. (it makes some would be necessary to modify the IA of the plays)

Thank you
 
adamamri said:
Hello, I do not know if I am on good forium but I have a question. Is you he possible with the Python to make so that the embraque computer of the ICBM in its under sailors. By knowing well on that I already modified code XML por that it is possible. (it makes some would be necessary to modify the IA of the plays)

Thank you

You may want to start a thread in the creation and customizaation forum to ask this question. This thread is just for modders to post working python code they have written.
 
This is from TGA's post on the first page of this thread: (pretend everything is tabbed)

iPlotX = pCity.getX()
iPlotY = pCity.getY()
for iXLoop in range(iPlotX - 2, iPlotX + 3):
for iYLoop in range(iPlotY - 2, iPlotY + 3):
### Checks for tiles not in city radius
if ((iXLoop != 2) and (iYLoop != 2)) or \
((iXLoop != -2) and (iYLoop != 2)) or \
((iXLoop != 2) and (iYLoop != -2)) or \
((iXLoop != -2) and (iYLoop != -2)):

Im unclear what the functions under "### Checks for tiles not in city radius" are doing. Im thinking they exclude the squares at the very corners so you only return back squares from the Fat Cross. Is this correct? Also what do the "\" at the end of the lines do?
 
naf4ever said:
Im thinking they exclude the squares at the very corners so you only return back squares from the Fat Cross. Is this correct?
Yup. There is probably a marginally better way involving finding the magnetude of the values, and if both have magnetude 2, avoiding the plot. This way it easier to read though.
naf4ever said:
Also what do the "\" at the end of the lines do?
It signifies "jump to the next line". I'm not actually sure they are neccessary - I find it more readable though.
 
Code:
# Finds plot - takes wrapping into account
def findPlot(self, plotX, plotY):
		
	if CyMap().isWrapX():
		if plotX >= CyMap().getGridWidth():
			plotX -= CyMap().getGridWidth()
		elif plotX < 0:
			plotX += CyMap().getGridWidth()	
			
	if CyMap().isWrapY():
		if plotY >= CyMap().getGridHeight():
			plotY -= CyMap().getGridHeight()
		elif plotY < 0:
			plotY += CyMap().getGridHeight()
				
	return CyMap().plot(plotX, plotY)
What it does: Returns the real plot from a pair of co-ordinates which may not necessarily be within the map bounds. It does this by checking the plot is in the bounds of the map, and if it is not, it will change the co-ordinate to the real co-ordinate.

NOTE: I've been a bit lazy and not included instances when there is no wrapping. This is because Civ will recognise an invalid plot, and not let you do anything on it anyway.

Uses: Whenever you want to find all the plots in a specific area around an object you should always check to see if the plot actually lies on the map... and if it does not fix it so that it does.
 
I was playign with the AI_unitUpdate() function in CvGameInterface.py and I was amazed at how flexible it is. Here is an example of being able to control the AI just from python:

Code:
def AI_unitUpdate(argsList):
	'AI moves units - return 0 to let AI handle it, return 1 to say that the move is handled in python '
	#CvUtil.pyPrint( "CvGameInterface.AI_unitUpdate" )
	pUnit = argsList[0]

	if pUnit.getUnitClassType() == gc.getInfoTypeForString('UNITCLASS_GIANT_SPIDER'):
		iX = pUnit.getX()
		iY = pUnit.getY()
		for iiX in range(iX-1, iX+2, 1):
			for iiY in range(iY-1, iY+2, 1):
				pPlot = CyMap().plot(iiX,iiY)
				for i in range(pPlot.getNumUnits()):
					if pPlot.getUnit(i).getOwner() != pUnit.getOwner():
						return 0
		return 1
	
	return gameUtils().AI_unitUpdate(argsList)

What it does: If the unit being controlled is a Giant Spider it looks for units int he surrounding tiles that have a different owner than itself. If it finds one it is allowed to move (presumably to attack, but it is for whatever the AI wants to do with it). If there isn't a unit in the surrounding tile then the spider stays and waits.

Uses: We could have made the spider attack with this function, we could push missions to units, we could have the unit build a road, run away.

We could have a successful attack on an AI city cause an "alarm" building to be created. Then all units within x amount of tiles of a city with an "alarm" could run through some python logic to decide if they should stay where they are, or head to the city.

You could create an event in a scenerio that would cause the AI to all rush for and attack a specific city with this function, all without the SDK. There are a lot of possibilities.

There are similiar functions for AI_chooseTech, AI_chooseProduction, AI_doWar and AI_doDiplo.
 
Kael said:
I was playign with the AI_unitUpdate() function in CvGameInterface.py and I was amazed at how flexible it is.

While we're on the subject, I was just wondering if you had by any chance run into the instance where you want the AI_unitUpdate function to run the behaviors of units that are being automated. Typically, it works just as well for simple AI units, and for all human automated units, but if there's any time you want the unit to un-automate all by itself, you'll run into a problem.

The CvUnitAI::AI_update function in the SDK is called from the CvSelectionGroupAI::AI_update function. However, the selection group update does not call the unit update once during the time it's on the stack, since the unit might need to do multiple moves (the first call to CvUnitAI::AI_update will move the unit, the next will push a build mission, etc.). It's interesting code that will sometimes prove troublesome, and the hack to test for an infinite loop has saved my butt a few times :p

Spoiler CvSelectionGroupAI::AI_update() :

Code:
	while (readyToMove())
	{
		pHeadUnit = getHeadUnit();

		if (pHeadUnit == NULL)
		{
			break;
		}

		iTempHack++;
		if (iTempHack > 100)
		{
			FAssert(false);
			pHeadUnit->finishMoves();
			break;
		}

		resetPath();

		pHeadUnit->AI_update(); [b]//Python function called in this function[/b]

		if (doDelayedDeath())
		{
			bDead = true;
			break;
		}
	}


As you can see, the python function might be called multiple times during a CvSelectionGroup::AI_update call. When I was writing that python function, instead of checking for the type of unit, I needed to check for the automate type. Since I'm not expecting that python function to be called with the automate type being NO_AUTOMATE, I didn't include that. Here's the code:

Spoiler CvGameUtils.py, AI_unitUpdate() :

Code:
def AI_unitUpdate(self,argsList):
		pUnit = argsList[0]

                # Added by Gerikes for harvester and destroy automation
                pPlot = pUnit.plot()
                pGroup = pUnit.getGroup()
                pPlayer = gc.getPlayer(pUnit.getOwner())
                
                if (pGroup.getAutomateType() == AutomateTypes.AUTOMATE_HARVEST):
                    # Snipped code to handle harvester automation                
                    return True # We handled the AI, so don't let the SDK try handling it


                if (pGroup.getAutomateType() == AutomateTypes.AUTOMATE_DESTROY):
                    pTargetUnit = pGroup.getTargetUnit() [b]# getTargetUnit is a function I added to the selection group class[/b]

                    # Something happened to the unit... perhaps someone killed it, who knows.
                    # All I know is that it's no longer valid, my work is done.
                    if (pTargetUnit is None):
                        pGroup.setAutomateType(AutomateTypes.NO_AUTOMATE)
                        return True # We handled the AI, so don't let the SDK try handling it


                    # Snipped code handling the "destroy" automation.
                    

                return False # Nothing we touched. Let the SDK handle it.


If during the first time the loop in CvSelectionGroupAI::AI_update() is called the python function makes the unit un-automated, the second time through the loop that python function falls through, returns False, and suddenly I have the actual CvUnitAI::AI_update() function running it's course. This is an undesired effect.

My current workaround is this: I've changed the above code in CvSelectionGroupAI as follows:

Spoiler CvSelectionGroupAI::AI_update() :

Code:
/*
	 * Modified by Gerikes to allow for a breakout in case the group needs to be
	 * un-automatized during their automatization. The added portion is exactly the same
	 * as AI_isAutomated, but I think an automated unit loses it's inheritance of the AI
	 * classes after it's automation is turned off.
	 */
	//while (readyToMove())
	while (readyToMove() && (!isHuman() || isAutomated()))
	/* End modified by Gerikes */
	{
		pHeadUnit = getHeadUnit();

		if (pHeadUnit == NULL)
		{
			break;
		}

		iTempHack++;
		if (iTempHack > 100)
		{
			FAssert(false);
			pHeadUnit->finishMoves();
			break;
		}

		resetPath();

		pHeadUnit->AI_update();

		if (doDelayedDeath())
		{
			bDead = true;
			break;
		}
	}

	/* Added by Gerikes to break out of this function if this unit is no longer automated */
	if (isHuman() && !isAutomated() )
	{
		return false;
	}
	/* End Added by Gerikes to break out of this function if this unit is no longer automated */


As read in the comments, I tried to use AI_isAutomated, as that function is used in this very same function earlier, but I kept getting run-time errors where that function suddenly was not linked to that unit. Do you think that my assumption is correct that the AI_* functions are no longer linked to the unit after it's lost it's automatization, or is there something else I'm missing?
 
First of all I'd like to wholeheartdly thank everyone that has contributed to this thread. It really is an amazing resource to an aspiring Civ4 python modder. Keep up the good work :goodjob:


Now for my question. I'm a bit confused about how to use gc.getActivePlayer() and gc.getPlayer(). Doesn't gc.getActivePlayer() just return the human player when in singleplayer? If thats the case then is the following piece of code that Aussie_Lurker correct?

Aussie_Lurker said:
Well, as many of you here will know, I have had my fair share of problems with the Python language. However I did get THIS one to work:

Code:
def onBeginPlayerTurn(self, argsList):
        'Called at the beginning of a players turn'
        iGameTurn, iPlayer = argsList

        player = gc.getActivePlayer()
        if (player.isCivic(gc.getInfoTypeForString("CIVIC_SLAVERY"))):
            for i in range(player.getNumCities()):
                player.getCity(i).setFreeSpecialistCount(gc.getInfoTypeForString("SPECIALIST_SLAVE"), 1)
        else:
            for i in range(player.getNumCities()):
                player.getCity(i).setFreeSpecialistCount(gc.getInfoTypeForString("SPECIALIST_SLAVE"), 0)
What it Does: Looks to check what Civic you are in and-if it matches the one it is looking for-then it grants a free specialist of the type mentioned in every city.

Use Not Currently using it in this form (as I wanted a different way to make slavery more....interesting. However, in modified form, it will allow certain Civics to grant you quite unique benefits (like Theocracy might grant you a free priest).

Yours,
Aussie_Lurker.
I was thinking that instead of
player = gc.getActivePlayer()
it should say
player = gc.getPlayer(iPlayer)

I'm assuming that what he was trying to do is supposed to apply to every player, not just the human.
 
I use this for a wonder called the Eyes and Ears Network that Bebematos submited as an entry in the Design a Wonder contest. The wonder grants the owning player any tech that is known by 3 or more player the owning player has open borders agreements with.

Code:
		if pCity.isHasRealBuilding(gc.getInfoTypeForString('BUILDING_EYES_AND_EARS_NETWORK')):
			eTeam = gc.getTeam(pPlayer.getTeam())
			listTeams = []
			for iPlayer2 in range(gc.getMAX_PLAYERS()):
				pPlayer2 = gc.getPlayer(iPlayer2)
				if (pPlayer2.isAlive() and iPlayer2 != iPlayer):
					iTeam2 = pPlayer2.getTeam()
					if eTeam.isOpenBorders(iTeam2):
						listTeams.append(gc.getTeam(iTeam2))
			if len(listTeams) >= 3:
				for iTech in range(gc.getNumTechInfos()):
					if eTeam.isHasTech(iTech) == False:
						iCount = 0
						for i in range(len(listTeams)):
							if listTeams[i].isHasTech(iTech):
								iCount = iCount + 1
						if iCount >= 3:
							eTeam.setHasTech(iTech, True, iPlayer, False, True)
							CyInterface().addMessage(iPlayer,True,25,'You recived a free Tech from the Eyes and Ears Network.','AS2D_TECH_DING',1,'Art/Interface/Buttons/Buildings/Eyesandearsnetwork.dds',ColorTypes(8),pCity.getX(),pCity.getY(),True,True)

What it Does: This code is in the onCityDoTurn event in CvEventManager.py. iPlayer, pPlayer and pCity are defined earlier in the file. If the city has the Eyes and Ears Network ("Eyes") then the game checks all of the players in the game. If any of those players has an open border agreement with the Eyes owner then that players team is added to the listTeams list.

The second part of the function only runs if their are more than 3 entries on the list (that part is just to minimize processing since I dont want to perform the tech searches if I wont get any positive hits anyway). Assuming there are 3 or more players that the Eyes owner has open borders agreements with the function goes through all of the techs. If the Eyes owner doesnt have the tech and 3 or more players he has open borders agreements with do have the tech, the Eyes owner gets the tech for free.

Uses: If you are particuarly old school, this function could be easily modified to make the Civ3 great library (you gain any tech if half the players in the game have it). You could also have it give some progress towards the techs instead of the whole tech.

I think the code is also a decent example of a way to build a list based on criteria and then pass the list through a function. I could have checked all players on all techs but this way is more efficient, I will only check the players that matter (those with open borders agreements) and I wont even run the tech checks at all if I have less than 3 open borders agreements.

The code to check and add techs is also useful if you haven't dealt with it before. Its a little confusing since techs are associated with teams, not players, but its not to bad.
 
Having things pemited to the screen is cool and printing it to the log is totally helpful, but I am not the quickest person some times and I don't always catch the words on the screen before they disappear and alt tabing to the logs constantly can get to be a hassle. So I use these to methods for my messages.

Code:
class Blah:

        def alertPlayer(self, iPlayer, message, iColor = 7):

                pPlayer = gc.getPlayer(iPlayer)

                # Return immediately if the player passed in is invalid
		if(pPlayer == None):
			return None
			
		# Return immediately if the player passed in is invalid
		if(pPlayer.isNone()):
			return None

                # If the player isn't human then no need to send a message
                if (not pPlayer.isHuman()):
                        return None

                eventMessageTimeLong = gc.getDefineINT("EVENT_MESSAGE_TIME_LONG")
                szIcon = None
                iFlashX = 0
                iFlashY = 0

                szString = str(message)

                CyInterface().addMessage(iPlayer, True, eventMessageTimeLong,
                                         szString, None, 0, szIcon, ColorTypes(iColor),
                                         iFlashX, iFlashY, True, True)

        def alertIconPlayer(self, iPlayer, message, pObject, szIcon = None, iColor = 7):

                pPlayer = gc.getPlayer(iPlayer)

                # Return immediately if the player passed in is invalid
		if(pPlayer == None):
			return None
			
		# Return immediately if the player passed in is invalid
		if(pPlayer.isNone()):
			return None

                # If the player isn't human then no need to send a message
                if (not pPlayer.isHuman()):
                        return None

                eventMessageTimeLong = gc.getDefineINT("EVENT_MESSAGE_TIME_LONG")

                strMessage = str(message)

                CyInterface().addMessage(iPlayer, True, eventMessageTimeLong,
                                         strMessage, None, 0, szIcon, ColorTypes(iColor),
                                         pObject.getX(), pObject.getY(), True, True)

What it Does: These two methods to basicly the same thing with one small difference. The first method takes 'iPlayer' an int type of a player and verifies it is a valid human player. Second it takes the argument 'message' and turns it into a string. An optional third integer argument may be included to change the color of the text, if none is given the text will default to 7 which is red, I think -1 will make the text uncolored.
The text message is then displayed on the player's screen. In addition to displaying the text on the screen, it will also record the text in the ingame Event Log which is the most helpful part in my opinion.
The second method works just as the first except it takes two additional arguments, the one required and the other optional.
The third argument is required and is used to get the x and y that the icon should be pointed at and can be a pUnit, pCity, pPlot, or any obj that has the .getX() and .getY() methods.
The forth argument is optional and allows you to set which icon will be shown pointing at the before mentioned x y plot.


Uses: I use this to display debuging info when I'm writing code and to give players ingame messages when they are playing. Combine this with the code mentioned earlyer in this thread by TheLopez to turn debug pemiting on and off and add in the printing to txt logs and it gets alot easyer to see what is happening behind the scenes.
 
Please keep contributing to this thread- to those new to the whole Python game (like me) it is invaluable.

On that note, here is my first ever working code which im kinda proud of, simple and in construction tho it is. Note that my added code is in bold:

Code:
	def onCombatResult(self, argsList):
		'Combat Result'
		pWinner,pLoser = argsList
		playerX = PyPlayer(pWinner.getOwner())
		unitX = PyInfo.UnitInfo(pWinner.getUnitType())
		playerY = PyPlayer(pLoser.getOwner())
		unitY = PyInfo.UnitInfo(pLoser.getUnitType())
[B]		if pLoser.getUnitType() == gc.getInfoTypeForString('UNIT_KNIGHT'):
                        CyInterface().addImmediateMessage("Your knight has survived the fall, and gets to his feet", "")
                        deadUnitX = pLoser.getX()
                        deadUnitY = pLoser.getY()
                        newUnit = playerY.initUnit(gc.getInfoTypeForString('UNIT_SWORDSMAN'), deadUnitX, deadUnitY, UnitAITypes.NO_UNITAI)[/B]
		if (not self.__LOG_COMBAT):
			return
		if playerX and playerX and unitX and playerY:
			CvUtil.pyPrint('Player %d Civilization %s Unit %s has defeated Player %d Civilization %s Unit %s' 
				%(playerX.getID(), playerX.getCivilizationName(), unitX.getDescription(), 
				playerY.getID(), playerY.getCivilizationName(), unitY.getDescription()))

What it does:
It is run in the onCombatResult event trigger. Checks to see if the losing unit is a Knight, and if it is then gets the Knight's plot and creates a Swordsman on it, along with displaying a corny "Your Knight survived the fall" message.

Use in my mod:
Will be used in my mod with a random number so that there is a random chance that it occurs. Is meant to simulate the Knight surviving his horse's death, and give him another bite at the cherry, as it were. NOTE: Currently the chance of this happening is 100%, as i havnt put in the random number bit yet.

Thanks to Kael and Gerikes for their help with getting this working. Still working on improving it but as its my first working Python thingumajig im kinda proud of it!

mrkingkong
 
I would like this thread to be stickied as well. Woodelf?

And I encourage people to keep contributing to it! It helps so much and it's getting me back in the mood to work with python again.
 
Multi-dimensional array. If there already exists something like this in python (built-in), please, let me know :)

Code:
class mdArray:
	"multi dimensional array handler"
	def __init__(self, dimensions, initial = None):
		dimlen = len(dimensions)
		self._dimensions = [0] * dimlen
		self._factors = [0] * dimlen
		product = 1
		i = dimlen - 1
		while i >= 0:
			self._dimensions[i] = dimensions[i]
			self._factors[i] = product
			product *= self._dimensions[i]
			i -= 1
		self._data = [initial] * product

	def getOffset(self, indices):
		if len(indices) != len(self._dimensions):
			raise IndexError
		offset = 0
		for i, dim in enumerate(self._dimensions):
			if indices[i] < 0 or indices[i] >= dim:
				raise IndexError
			offset += self._factors[i] * indices[i]
		return offset

	def __getitem__(self, indices):
		return self._data[self.getOffset(indices)]

	def __setitem__(self, indices, value):
		self._data[self.getOffset(indices)] = value

	def get(self):
		return self._data

	def __len__():
		return len(self._data)

Usage:

Code:
# to create array, use one of the following:
array2d = mdArray([5,9]) #creates 5x9 2-dimensional array
array3d = mdArray([5,9,2]) #creates 5x9x9 3-dimensional array

array3d = mdArray([5,9],30) #creates 5x9 array with all values equal to 30

#changind data in array:
array2d[1,0] = 12 #sets 1x0 item to 12
array3d[4,8,1] = -30 #sets 4,8,1 item to -30

#reading data:
print array2d[1,0] #prints value 1x0 form array

#getting internal array offset (this class is wrapper around simple 1 dimension array):
array2d.getOffset([1,2]) #returns internal array index

World array for storing tile plot types. Wraps in X automatically. Uses mdArray.
Code:
class worldArray(mdArray):
	def __init__(self, dimensions, initial = None):
		if len(dimensions) != 2: #only allow 2 dimensions
			raise IndexError
		dimensions_copy = [dimensions[1],dimensions[0]] #reverse dimensions (required to pass plot types corectly)
		dimlen = len(dimensions_copy)
		self._dimensions = [0] * dimlen
		self._factors = [0] * dimlen
		product = 1
		i = dimlen - 1
		while i >= 0:
			self._dimensions[i] = dimensions_copy[i]
			self._factors[i] = product
			product *= self._dimensions[i]
			i -= 1
		self._data = [initial] * product

	def getOffset(self, indices):
		if len(indices) != 2: #only allow 2 indices
			raise IndexError

		indices_copy = [indices[1],indices[0]] #reverse indices (required to match dimensions)

		if len(indices_copy) != len(self._dimensions) or len(indices_copy) < 2:
			raise IndexError

		# make sure array wraps if out of bounds
		while indices_copy[0] < 0:
			indices_copy[0] += self._dimensions[0]
		while indices_copy[0] > self._dimensions[0] - 1:
			indices_copy[0] -= self._dimensions[0]

		while indices_copy[1] < 0:
			indices_copy[1] += self._dimensions[1]
		while indices_copy[1] > self._dimensions[1] - 1:
			indices_copy[1] -= self._dimensions[1]

		offset = 0
		for i, dim in enumerate(self._dimensions):
			if indices_copy[i] < 0 or indices_copy[i] >= dim:
				raise IndexError
			offset += self._factors[i] * indices_copy[i]
		return offset

	def getHeight(self):
		return self._dimensions[0]

	def getWidth(self):
		return self._dimensions[1]

Usage:

Code:
#example map-script code:
def generatePlotTypes():
	cgc = CyGlobalContext()
	map = cgc.getMap()
	iW = map.getGridWidth()
	iH = map.getGridHeight()

	#world is two-dimensional array which wraps in X automagically, so you may not worry about edges
	world = worldArray([iW,iH],PlotTypes.PLOT_OCEAN)

	#create two land tiles
	world[5,5] = PlotTypes.PLOT_LAND
	world[5,6] = PlotTypes.PLOT_LAND

	# return our world in one-dimensional array to Firaxis to do the rest :)
	return world.get()
 
Array wrapper with binary search optimization:

Code:
class bArray:
	def __init__(self):
		self._data = []

	def __setitem__(self, power, newValue):
		index, value = self._search(power,0,len(self._data)-1)
		if value == None:
			self._data.insert(index, [power, newValue])
		else:
			self._data[index][1] = newValue

	def __getitem__(self, power):
		index, value = self._search(power,0,len(self._data)-1)
		return value

	def __len__(self):
		return len(self._data)

	# removes element by index
	def pop(self, index = None):
		if index == None:
			return self._data.pop()
		else:
			return self._data.pop(index)

	# removes element by key
	def remove(self, power):
		index, value = self._search(power,0,len(self._data)-1)
		if value != None:
			self._data.pop(index)
			return True
		else:
			return False

	# returns random element from array and removes it
	def random(self, dice):
		if len(self._data) == 0:
			return None
		else:
			item = dice.get(len(self._data),"Python - random bArray item with removing.")
			value = self._data[item][1]
			self._data.pop(item)
			return value

	# returns random element from array
	def randomItem(self, dice):
		if len(self._data) == 0:
			return None
		else:
			item = dice.get(len(self._data),"Python - random bArray item without removing.")
			value = self._data[item][1]
			return value

	# internal function. searches for key value in array in specified limits
	# if fails, returns None
	def _search(self,value,fromI,toI):
		left = fromI
		right = toI
		while left <= right:
			mid = (right-left) / 2 + left
			if value > self._data[mid][0]:
				left = mid + 1
			elif value < self._data[mid][0]:
				right = mid-1
			else:
				return mid, self._data[mid][1]
		return left, None

	# iteration
	def __iter__(self):
		self.index = -1
		return self

	def next(self):
		if self.index == len(self._data) - 1:
			raise StopIteration
		self.index += 1
		return self._data[self.index][1]

Usage:
This array is useful when you have large amount of data indexed by keys (for example, in my case, continent tile list, indexed by world.getOffset()), and want to check if specific key exists in that list VERY often. Or, you want to be able to access data very quickly. Keys are unique.

Code:
# creating array:
a = bArray()

# adding data:
a[2] = "data1" #data with key 2
a[5] = "data2" #data with key 5
a[2] = "data3" #update item with key 2 to "data3"

#retrieving data:
print a[5]

#check if key exists
if a[2] != None:
	print "exists"

#iterating data:
for item in a:
	print item

#retrieving random item:
item = a.randomItem(dice)

#retrieving random item, and removing it at the same time:
item = a.random(dice)
 
I have one request for a small python script sample. I need a script to add a button to a unit, that when pressed, the button will fire off a custom event.

I can add the button to the unit, and I can set the graphics for it. But I cannot figure out how to assign it to fire off another python function.
 
Top Bottom