BUG & Problems with adding new Diplomacy Options

Afforess

The White Wizard
Joined
Jul 31, 2007
Messages
12,239
Location
Austin, Texas
I recently added several new diplomatic trading options to my SDK for Rise of Mankind, which uses BUG & BULL, whatever the latest version is. The new diplomatic options are to trade "Embassies", "Workers", "Military Units", and to Pledge "Secretary General Votes." (These are really just modified versions of what Dom Pedro's ToT added, and I stole...)

My changes were purely SDK based, there were no changes to the Python at all.

I didn't have any problems adding them, but BUG is giving me some python errors now, and I also have another question...

Python Error:
Traceback (most recent call last):
File "BugInit", line 98, in callInits
File "BugUtil", line 586, in __call__
File "BugUtil", line 583, in call
File "AttitudeUtil", line 95, in init
File "AttitudeUtil", line 207, in initModifiers
AttributeError: 'NoneType' object has no attribute 'getTextKey'

I'm certain this has to do with my changes, but I haven't the slightest clue what could cause this. It doesn't seem have any effect on the game whatsoever, except at the start of a game it says AttitudeUtil - failed to load, or something like that. Any Idea how to fix this?

Also, I like the notifications BUG gives for when the AI will trade gold, techs, etc... with the human player. What would I need to modify to add my new diplomatic trades in too?
 
It looks like you have a modified version of AttitudeUtil because line 207 is a comment and 95 isn't in the init() function. Can you post it?

Those trade alerts are in MoreCiv4lerts. I did some work recently to make it easier to add alerts for new tradeable items. Take a look near the end of Contrib/MoreCiv4lerts, but I'll warn you that I haven't completely rewritten that module. The original code was pretty cumbersome, and much of it remains. Hopefully you can follow one of the examples such as Open Borders.
 
It looks like you have a modified version of AttitudeUtil because line 207 is a comment and 95 isn't in the init() function. Can you post it?

Well, if it's modified, then who ever modified it did it without adding any comments, so I couldn't tell.


Those trade alerts are in MoreCiv4lerts. I did some work recently to make it easier to add alerts for new tradeable items. Take a look near the end of Contrib/MoreCiv4lerts, but I'll warn you that I haven't completely rewritten that module. The original code was pretty cumbersome, and much of it remains. Hopefully you can follow one of the examples such as Open Borders.

Oh, I should have figured. I'll take a look at those.

Oh Dang, I just realized, I'll probably need to expose some of these new functions I've added for Diplomacy to python... :(

I'll work on this some tomorrow, and report back.
 

Attachments

The error occurs while getting the descriptions of memory types:

Code:
	for iMemType in range(MemoryTypes.NUM_MEMORY_TYPES):
		sKey = str([B][COLOR="Red"]gc.getMemoryInfo(iMemType).getTextKey()[/COLOR][/B])
		sStr = BugUtil.getPlainText(sKey, "NONE")
		if (sStr != "NONE"):
			# These modifier strings have no extra text and so
			# we can use them directly
			MODIFIER_STRING_TO_KEY[unicode(sStr)] = sKey

So your mod must add some new memory type (increased the value of NUM_MEMORY_TYPES) but didn't provide a CvMemoryInfo in the XML. That's my guess.
 
The error occurs while getting the descriptions of memory types:

Code:
    for iMemType in range(MemoryTypes.NUM_MEMORY_TYPES):
        sKey = str([B][COLOR=Red]gc.getMemoryInfo(iMemType).getTextKey()[/COLOR][/B])
        sStr = BugUtil.getPlainText(sKey, "NONE")
        if (sStr != "NONE"):
            # These modifier strings have no extra text and so
            # we can use them directly
            MODIFIER_STRING_TO_KEY[unicode(sStr)] = sKey
So your mod must add some new memory type (increased the value of NUM_MEMORY_TYPES) but didn't provide a CvMemoryInfo in the XML. That's my guess.

Ahh. Yes, I did add a few memory types. So, if I expose those changes to XML, the error will go away?

Also, a second question, when I am trading a new object in diplomacy, I get a message from BUG afterwards, it always says
"TradeUtil -- Unknown Object 17" What does this mean?
 
If you meant "expose to Python", then yes that should do it.

TradeUtil builds strings to represent the trades for logging (Autolog), and it doesn't know how to format your new tradeable items. I thought I had coded it to ignore new item types it didn't understand, but apparently not. :( I'll take a look at it this weekend. Do you get anything in PythonErr.log when that happens?
 
TradeUtil builds strings to represent the trades for logging (Autolog), and it doesn't know how to format your new tradeable items. I thought I had coded it to ignore new item types it didn't understand, but apparently not. :( I'll take a look at it this weekend. Do you get anything in PythonErr.log when that happens?

No, nothing in the error logs. Just on the screen.

Adding the new memory types into the XML did resolve the other error though...
 
If you meant "expose to Python", then yes that should do it.

TradeUtil builds strings to represent the trades for logging (Autolog), and it doesn't know how to format your new tradeable items. I thought I had coded it to ignore new item types it didn't understand, but apparently not. :( I'll take a look at it this weekend. Do you get anything in PythonErr.log when that happens?

Any Ideas on how to fix this? I updated to BUG 4.2, but I am still getting those unknown object warnings whenever I trade a new item.
 
You need to create an init() function for your mod that tells TradeUtil about your new TradeableItems. There are four helper functions to do this in TradeUtil with examples in its initTradeableItems().

  • addPlainTrade(): The description is a static XML string, e.g. "Maps" or "Open Borders"
  • addSimpleTrade(): The description is an XML string with a single int placeholder for iData, e.g. "%d Gold"
  • addAppendingTrade(): The description is a static XML string which gets another string appended to it. The second string is a formatted string that takes parameters from a function call using data from the trade.
  • addComplexTrade(): Like addAppendingTrade() except the second string is itself pulled from a function before being formatted with the arguments.
If you give me precise examples of what new tradeable items you've create do and what data they contain I can help by pointing you to the appropriate function to call if the examples for the existing items are not sufficient.
 
You need to create an init() function for your mod that tells TradeUtil about your new TradeableItems. There are four helper functions to do this in TradeUtil with examples in its initTradeableItems().

  • addPlainTrade(): The description is a static XML string, e.g. "Maps" or "Open Borders"
  • addSimpleTrade(): The description is an XML string with a single int placeholder for iData, e.g. "%d Gold"
  • addAppendingTrade(): The description is a static XML string which gets another string appended to it. The second string is a formatted string that takes parameters from a function call using data from the trade.
  • addComplexTrade(): Like addAppendingTrade() except the second string is itself pulled from a function before being formatted with the arguments.
If you give me precise examples of what new tradeable items you've create do and what data they contain I can help by pointing you to the appropriate function to call if the examples for the existing items are not sufficient.

Well... Lets see here I added:


  • Right of Passage (works like open borders, but with only defensive units) (Plain Trade)
  • Trading Military Units (Complex Trade)
  • Trading Workers (Complex Trade)
  • Trading Secretary General Votes (Not Sure If Complex or Not...)
  • Trading UN Pledges (You pledge to vote for issue XYZ...) (Not Sure If Complex or Not...)
  • Trading Embassies (Complex Trade)
  • Trading Corporation HQ's (Complex Trade)
I think that's all of them...
 
You need to be more specific about the data.

Trade Workers sounds to me like "I'll give you a worker" which sounds like Plain would work fine--just like Maps.

If trading Military Units means "I'll give you a <unit-type>" then you can use Appending. The function will convert the iData UnitTypes into a string (gc.getUnitInfo(iData).getDescrption()) which can be appended to the base text string "I'll give you a[space]".

Voting pledges can probably use Appending as well: you just need to write a function to turn iData into a description of the vote.

Embassies? What does this mean?

Corporate HQs, as in "I'll give you Mining Inc."? Appending.

Take a look at the examples, and let's work out one of the Appending ones so you can see how it works. From there I'd bet you could do the others easily enough.
 
For example, trading cities seems like a very complicated thing. Here's the code:

Code:
# extracts the city's name from the TradeData object
def getTradeCity(player, trade):
	return PlayerUtil.getPlayer(player).getCity(trade.iData).getName()

...

# creates the tradeable item definition
addComplexTrade("city", TradeableItems.TRADE_CITIES, getTradeCity)

Tada!
 
You need to be more specific about the data.

Trade Workers sounds to me like "I'll give you a worker" which sounds like Plain would work fine--just like Maps.

If trading Military Units means "I'll give you a <unit-type>" then you can use Appending. The function will convert the iData UnitTypes into a string (gc.getUnitInfo(iData).getDescrption()) which can be appended to the base text string "I'll give you a[space]".

Voting pledges can probably use Appending as well: you just need to write a function to turn iData into a description of the vote.

Embassies? What does this mean?

Corporate HQs, as in "I'll give you Mining Inc."? Appending.

Take a look at the examples, and let's work out one of the Appending ones so you can see how it works. From there I'd bet you could do the others easily enough.

Sorry, I'll be more specific:

Trading workers creates a list of workers that are available to trade, just like the list of cities. It IS a complex trade.

Embassies is a plain trade, it's just an "object" that you need to trade in order to have more advanced trades unlocked, like Open Borders, and Defensive Pacts. Having an embassy boosts relationships...etc...

Corporate HQ's is a complex list, of available HQ's you can trade. It can be more than 1, it looks similar to the city trade list again...

Trading Military units creates a pretty big list of units available for trade.

Voting pledges opens a popup box where you choose the vote.

I'm sorry I'm not very specific, I stole 1/2 the code, but I'm having to rewrite most of it...
 
For example, trading cities seems like a very complicated thing. Here's the code:

Code:
# extracts the city's name from the TradeData object
def getTradeCity(player, trade):
    return PlayerUtil.getPlayer(player).getCity(trade.iData).getName()

...

# creates the tradeable item definition
addComplexTrade("city", TradeableItems.TRADE_CITIES, getTradeCity)
Tada!

Okay, thanks!
 
Ugh, I just realized, I'm going to have to expose these all to python, aren't I?
...
 
Ugh, I just realized, I'm going to have to expose these all to python, aren't I?
...

These must be written in Python. You only need to expose the values you added to TradeableItems in CvEnum.h.
 
These must be written in Python. You only need to expose the values you added to TradeableItems in CvEnum.h.

Oh, Okay. I already exposed all those. ;)

Slightly offtopic, but still on topic enough:

How many player options does BULL use up? I added a new one and was surprised today when I saw option 1 and 2 used up. There are only 3 total... :(
 
I was a bit off on Complex. It is similar to Appending in that it uses a function to build the parameters to insert into a string, but it differs in that it doesn't append to an XML string. Instead you must build the entire format string in Python or use the default to have your built description be the entire description of the trade.
 
Okay, I'm trying to understand Tradeutil.py, but most of it doesn't make sense to me.

This part here:

Code:
def getTradeCity(player, trade):
	return PlayerUtil.getPlayer(player).getCity(trade.iData).getName()

def getTradeCity(player, trade)

-That's just the heading, right? Parameters, and all that good stuff?

But what is the PlayerUtil.getPlayer(player).getCity(trade.iData).getName()?

What is it doing?
 
def​

This is a function definition.

getTradeCity​

Name of the function.

(player, trade)​

These are the names of the two parameters this function expects. Unlike C++ you don't ever declare the type of a variable. Every variable can hold any type of value or object. You can assign an int to a variable currently holding a string. Variables are truly just names for values and objects.

PlayerUtil.getPlayer(player)​

PlayerUtil is a Python module (PlayerUtil.py) that has a function called getPlayer() that takes a single "player" parameter.

BUG's PlayerUtil module makes dealing with players and teams easier with functions like getPlayer(), getPlayerAndID(), getActivePlayerID(), etc. All of the functions in it that take a player will accept either a CyPlayer object or its PlayerTypes ID. This makes it so I don't have to worry about which thing I have. Here I need a CyPlayer which is exactly what PlayerUtil.getPlayer() returns. If you pass it a CyPlayer, it returns it. If you pass it a PlayerTypes, it uses gc.getPlayer(ePlayer) to look it up.

getCity(trade.iData)​

This uses the CyPlayer.getCity() function to get the CyCity with the ID stored in the TradeData's iData field. When you use -> in C++ to access a function or value on a pointer, you are doing something similar. C++ also has the . operator for when you have an actual object instead of a pointer to an object.

return ...getName()​

Pulls the name of the CyCity and returns it from this function.
 
Back
Top Bottom