Merging BUG with other Mods

I believe that the -PLE module is cruft left over by Ruff as I'm pretty sure he's integrated the Unit Plot List rewrite into the non-PLE module. You should be able to safely delete the -PLE module, but I'm sure Ruff will chime in here shortly with a for-sure response. ;)
 
I believe that the -PLE module is cruft left over by Ruff as I'm pretty sure he's integrated the Unit Plot List rewrite into the non-PLE module. You should be able to safely delete the -PLE module, but I'm sure Ruff will chime in here shortly with a for-sure response. ;)

Thanks for the quick reply, I'll wait to see if Ruff can clarify this and also whether the PLE.py module is something I can delete as it only seems to be imported in the CvMainInterface-PLE module
 
and the answer is ... yes and no. BAT v2.2 is not based on the very latest version of BUG. As such, it has some branch / stump work included in it. The BUG version of the main interface file does import PLE.py.

So - options ... delete PLE.py and the mi-PLE file or wait for LM to release the next version of BAT which should have all of the PLE / BUG unit plot goodies in it.
 
Thanks for the reply. I will probably go with the existing version and delete the files for now, but it is good to know my options. I am too impatient to play the 'waiting for the next release' game, and it is a game that never ends.
 
Except that in this case it will be very, very soon. BUG and BULL are ready, and that's all LM was waiting on.
 
Hi I was wondering if anybody could give me directions how to use BUG with the new Better AI mod.

I'd like to be able to play the BTS games of the month, without having to do much work.

Thanks
 
The work you have to do:
1.) Install BUFFY - for BOTM
2.) Install Better BUG AI - for BUG with Better AI
3.) Load one of those mods (which one will depend on whether or not you are plying a BOTM game :p)

Easy enough?
 
I'm trying to add select parts of what is to my inexperienced eyes the maze of BUG code to Planetfall. At the moment I'm looking at adding the ExoticForeignAdvisor. That file imports DealUtil.py, which means I also need to look if it's safe to add that file.

I see that DealUtil calls the onGameUpdate event. I'm wondering why this is done, and if it would be safe to remove that call if I don't care about seeing for instance a deal between two other players being cancelled?

My worry is that code being run on every GameUpdate decreases performance. I don't know at all if this is the case though. Would this slow down the game by more than 0.1% for instance? Same question for Civ4lerts, which also uses onGameUpdate.
 
Pulling some parts of BUG into a mod may be possible, but designing it that way from the start would have made it less of a mod itself. The code in the onGameUpdate events was written to be very quick (comparing one integer against another integer), but if you don't also have the Advanced Scoreboard it's unnecessary (it updates the scoreboard when a 10-turn peace treaty with the player is canceled).

I don't recall what Civ4lerts does during that event. Can you post the code as I'm currently rewriting it from scratch?
 
BugEventManager.py

Code:
def resetActiveTurn(self, argsList=None):
		self.iActiveTurn = -1
		self.eActivePlayer = -1
		self.bEndTurnFired = False
	
	def checkActivePlayerTurnStart(self):
		"""Fires the BeginActivePlayerTurn event if either the active player or game turn
		have changed since the last check. This signifies that the active player is about
		to be able to move their units.
		
		Called by onGameUpdate() when the End Turn Button is green.
		"""
		iTurn = gc.getGame().getGameTurn()
		ePlayer = gc.getGame().getActivePlayer()
		if self.iActiveTurn != iTurn or self.eActivePlayer != ePlayer:
			self.iActiveTurn = iTurn
			self.eActivePlayer = ePlayer
			self.bEndTurnFired = False
			self.fireEvent("BeginActivePlayerTurn", ePlayer, iTurn)
	
	def checkActivePlayerTurnEnd(self):
		"""Fires the endTurnReady event if it hasn't been fired since the active player's turn started.
		
		Called by onGameUpdate() when the End Turn Button is red.
		"""
		if not self.bEndTurnFired:
			self.bEndTurnFired = True
			self.fireEvent("endTurnReady", self.iActiveTurn)

	
# Used Event Handlers
	
	def onGameUpdate(self, argsList):
		"""
		Checks for active player turn begin/end.
		"""
		eState = CyInterface().getEndTurnState()
		if eState == EndTurnButtonStates.END_TURN_GO:
			self.checkActivePlayerTurnStart()
		else:
			self.checkActivePlayerTurnEnd()

Several Alerts are checked during BeginActivePlayerTurn. See MoreCiv4lerts.py for that.
 
Ah, but BeginActivePlayerTurn is only fired once per turn. So while onGameUpdate() is called four times per second, all it does is compare two integers against two stored integers, and that will be very fast.
 
I'm happy to hear that. People always say how slow Python is, so I figured even such a simple comparison, plus storing some data in Python, might slow down the game by more than 0.1%.

I have succesfully added the ExoticForeignAdvisor by the way, and I noticed that DealUtil isn't used at all in the file. So it doesn't need to be imported.
 
I'm happy to hear that. People always say how slow Python is, so I figured even such a simple comparison, plus storing some data in Python, might slow down the game by more than 0.1%.

I doubt these tiny checks could slow it down at all that much. Keep in mind that these will happen mostly while the player's making moves. The player won't notice this at all.

I have succesfully added the ExoticForeignAdvisor by the way, and I noticed that DealUtil isn't used at all in the file. So it doesn't need to be imported.

Line 409 uses it, but this only is necessary when not using BULL:

Code:
iTurns = [B][COLOR="Red"]DealUtil[/COLOR][/B].Deal(deal).turnsToCancel(self.iActiveLeader)
 
Line 409 uses it, but this only is necessary when not using BULL:

Woops. I must have accidentally checked my modified file, in which non-BULL code is removed.

By the way, I ran into a problem with AttitudeUtil. Do you perhaps know why ATTITUDECOLORS might be causing problems for me? (Python exception attached)

This is the last ExoticForeignAdvisor line:

szText = AttitudeUtil.getAttitudeText(j, iLoopPlayer, True, False, True, True)

Then it goes here:

szText.append(BugUtil.colorText(u"%+d" % nAttitude, getAttitudeColor(nPlayer, nTarget)))

and there:

Code:
def getAttitudeColor (nPlayer, nTarget):
	"""Returns the color of the attitude nPlayer has toward nTarget."""
	iCategory = getAttitudeCategory(nPlayer, nTarget)
	if iCategory is not None:
		return ATTITUDE_COLORS[iCategory]
	return -1

Some more of the file, including all the mentions of ATTITUDE_COLORS:

Code:
ATTITUDE_COLORS = None

...

def init (colors=DEFAULT_COLORS, modifiers=None):
	"""Initializes this module, raising ConfigError if any problems occur."""
	# create font icons for each attitude level
	global ATTITUDE_ICONS
	ATTITUDE_ICONS = [FontUtil.getChar(key) for key in ATTITUDE_KEYS]
	if len(ATTITUDE_ICONS) != NUM_ATTITUDES:
		raise BugUtil.ConfigError("Failed to create attitude icons")
	
	# convert colors to type IDs
	if len(colors) != NUM_ATTITUDES:
		raise BugUtil.ConfigError("Expected %d colors" % NUM_ATTITUDES)
	global ATTITUDE_COLORS
	ATTITUDE_COLORS = map(gc.getInfoTypeForString, colors)
	invalidCount = ATTITUDE_COLORS.count(-1)
	if invalidCount > 0:
		invalid = []
		for id, color in zip(ATTITUDE_COLORS, colors):
			if id == -1:
				invalid.append(color)
		raise BugUtil.ConfigError("Given %d invalid colors: %s" % (invalidCount, str(invalid)))

	# init modifiers, overriding default list as necessary
	if modifiers is not None and (isinstance(modifiers, list) or isinstance(modifiers, tuple)):
		global ATTITUDE_MODIFIERS
		ATTITUDE_MODIFIERS = tuple(modifiers)
	initModifiers()


## Attitude

def hasAttitude (nPlayer, nTarget):
	"""Returns True if nTarget can see nPlayer's attitude toward them."""
	return (nPlayer != -1 and nTarget != -1 and nPlayer != nTarget
	        and gc.getTeam(gc.getPlayer(nPlayer).getTeam()).isHasMet(gc.getPlayer(nTarget).getTeam()))

def getAttitudeString (nPlayer, nTarget):
	"""Returns the full hover text with attitude modifiers nPlayer has toward nTarget."""
	if hasAttitude(nPlayer, nTarget):
		return CyGameTextMgr().getAttitudeString(nPlayer, nTarget)
	return None

def getAttitudeCategory (nPlayer, nTarget):
	"""Returns the attitude level nPlayer has toward nTarget [0,4]."""
	if hasAttitude(nPlayer, nTarget):
		return gc.getPlayer(nPlayer).AI_getAttitude(nTarget)
	return None

def getAttitudeColor (nPlayer, nTarget):
	"""Returns the color of the attitude nPlayer has toward nTarget."""
	iCategory = getAttitudeCategory(nPlayer, nTarget)
	if iCategory is not None:
		return ATTITUDE_COLORS[iCategory]
	return -1

def getAttitudeIcon (nPlayer, nTarget):
	"""Returns the font icon of the attitude nPlayer has toward nTarget."""
	iCategory = getAttitudeCategory(nPlayer, nTarget)
	if iCategory is not None:
		return ATTITUDE_ICONS[iCategory]
	return ""

def getAttitudeCount (nPlayer, nTarget):
	"""Returns the total attitude modifiers nPlayer has toward nTarget."""
	sAttStr = getAttitudeString(nPlayer, nTarget)
	if sAttStr == None:
		return
	nAtt = 0
	# TODO: Replace with simple line-by-line handling
	#	    so it doesn't get tricked by leader names (": " fixes issue)
	ltPlusAndMinuses = re.findall ("[-+][0-9]+\s?: ", sAttStr)
	for i in range (len (ltPlusAndMinuses)):
		nAtt += int (ltPlusAndMinuses[i][:-2])
	return nAtt


def getAttitudeText (nPlayer, nTarget, bNumber, bSmily, bWorstEnemy, bWarPeace):
	"""Returns a string describing the attitude nPlayer has toward nTarget."""
	nAttitude = getAttitudeCount (nPlayer, nTarget)
	if nAttitude == None:
		return None
	
	szText = []
	if bSmily:
		szText.append(getAttitudeIcon(nPlayer, nTarget))
	if bNumber:
		szText.append(BugUtil.colorText(u"%+d" % nAttitude, getAttitudeColor(nPlayer, nTarget)))
...
 
One of the Config XML files must initialize AttitudeUtil and pass in the colors to use for the five attitudes. In BUG this is done in init.xml with

Code:
	<init module="AttitudeUtil">
		<!-- Furious, Annoyed, Cautious, Pleased, Friendly -->
		<arg name="colors" type="tuple">
			"COLOR_RED", 
			"COLOR_CYAN", 
			"COLOR_CLEAR", 
			"COLOR_GREEN", 
			"COLOR_YELLOW"
		</arg>
		<!-- Non-memory attitude modifier keys -->
		<arg name="modifiers" type="tuple">
			"TXT_KEY_MISC_ATTITUDE_LAND_TARGET",
			"TXT_KEY_MISC_ATTITUDE_WAR",
			"TXT_KEY_MISC_ATTITUDE_PEACE",
			"TXT_KEY_MISC_ATTITUDE_SAME_RELIGION",
			"TXT_KEY_MISC_ATTITUDE_DIFFERENT_RELIGION",
			"TXT_KEY_MISC_ATTITUDE_BONUS_TRADE",
			"TXT_KEY_MISC_ATTITUDE_OPEN_BORDERS",
			"TXT_KEY_MISC_ATTITUDE_DEFENSIVE_PACT",
			"TXT_KEY_MISC_ATTITUDE_RIVAL_DEFENSIVE_PACT",
			"TXT_KEY_MISC_ATTITUDE_RIVAL_VASSAL",
			"TXT_KEY_MISC_ATTITUDE_SHARE_WAR",
			"TXT_KEY_MISC_ATTITUDE_FAVORITE_CIVIC",
			"TXT_KEY_MISC_ATTITUDE_TRADE",
			"TXT_KEY_MISC_ATTITUDE_RIVAL_TRADE",
			"TXT_KEY_MISC_ATTITUDE_FREEDOM",
			"TXT_KEY_MISC_ATTITUDE_EXTRA_GOOD",
			"TXT_KEY_MISC_ATTITUDE_EXTRA_BAD"
		</arg>
	</init>

Does your mod remove or modify this in any way? Check PythonDbg.log for a call to AttitudeUtil.init() from BugInit.
 
Does your mod remove or modify this in any way?

*sigh* :( Planetfall doesn't have that file. The only files from BUG which Planetfall currently has are a text file, some modified Python/Screens and Attitude/Bug/Color/Deal/Diplomacy/Font/Game/Player/TradeUtil (with all references to the config files edited out for instance - players won't be able to set their own UI preferences). Planetfall is just too different from vanilla Civ, meaning I can't just copy-paste the BUG mod to Planetfall. I need to inspect and possibly modify every line of code I add, which means I'm limited to adding code I understand. And BUG isn't constructed in a way understandable for someone who had never seen a line of python or C++ code before Civ4. The Civ4 SDK is kindergarten level by comparison.

Is there a way to make the colors work without adding a whole list of additional files I don't understand?? :scared:
 
You can manually initialize AttitudeUtil.py and cross your fingers that it's okay to do so when the module loads. Add this at the very end of the file:

Code:
init()

This will tell it to use the default colors and strings. Hopefully they have been loaded at that point. If that doesn't work let me know.
 
I solved it like this:

Code:
def getAttitudeColor (nPlayer, nTarget):
	"""Returns the color of the attitude nPlayer has toward nTarget."""
	iCategory = getAttitudeCategory(nPlayer, nTarget)
	if iCategory is not None:
		if iCategory == 0:
			return gc.getInfoTypeForString("COLOR_RED")
		elif iCategory == 1:
			return gc.getInfoTypeForString("COLOR_YELLOW")
		elif iCategory == 2:
			return gc.getInfoTypeForString("COLOR_CLEAR")
		elif iCategory == 3:
			return gc.getInfoTypeForString("COLOR_GREEN")
		elif iCategory == 4:
			return gc.getInfoTypeForString("COLOR_CYAN")
		#return ATTITUDE_COLORS[iCategory]
	return -1
 
Back
Top Bottom