Foreign Advisor Info Screen

Don't forget about the AttitudeUtils module. This is the place where extracting a single line item should live, or was that added already?
No - that 'function' is not currently in AttitudeUtils. It would be good to add it.
 
That function (probably call it getModifier()) is a companion to the "AttitudeUtils.hasModifier()" function I've been using while testing and was something I was already intending to submit. :D
 
BTW: I looked at turning IconGrid into a scroll-bar version. With the scroll bar screens, you create a panel (Panel A) with scroll bars enabled, add other panels (Panels B) to Panel A and then put your stuff on these smaller panels (Panels B).

I think the issue is that the multiline icon containers that IconGrid uses cannot be added to panels.
 
I think the issue is that the multiline icon containers that IconGrid uses cannot be added to panels.

That was my guess as well. The IconGrid class is so feature-rich that I assumed the author would have made it scrollable in that way if it had been possible. In my version of Ruff's modpack, I changed the scroll arrows to do page down/up. We could add a second set of arrows to allow both ways of scrolling, or make ctrl/alt - click do page up/down.

Actually, we should just bite the bullet and have 3 pairs of buttons: top/bottom, page up/down, line up/down. Heck, you could even mimic a scrollbar, but that's overkill.

That function (probably call it getModifier()) is a companion to the "AttitudeUtils.hasModifier()" function I've been using while testing and was something I was already intending to submit. :D

I'd design an API like this:

Code:
class Attitude:
    "Holds all attitude modifiers that player A has toward player B."
    def __init__(self, iPlayerA, iPlayerB):
        self.playerA = playerA
        self.playerB = playerB
        self.attitude = gc.getPlayer(playerA).AI_attitude(playerB)
        self.attitudes = {}
        # get attitude string
        # parse string into lines
        # for each line, parse into value and text
        # convert text to XML key (e.g. TXT_KEY_DIPLO_HATE_YOUR_UGLY_FACE)
        # put into dictionary above as key -> modifier

    def hasModifier(self, key): bool
    def getModifier(self, key): value   # 0 if not in map
    def getTotal(self): return sum
    def getAttitude(self): return 0 .. 4   # saved above
    def getIcon(self): string
    def getText(self): string   # the original full text

... using it ...

# I always forget which one returns the CyPlayer and which the integer :(
att = AttitudeUtils.Attitude(iLoopPlayer, gc.getGame().getActivePlayer())
if att.hasModifier("TXT_KEY_DIPLO_HATE_YOUR_UGLY_FACE"):
    # avert eyes
if att.hasModifier("TXT_KEY_DIPLO_SAME_RELIGION"):
    # prayer party!
if att.hasModifier("TXT_KEY_DIPLO_REFUSED_CIVIC"):
    # Commie bastard!
if att.getAttitude() < AttitudeUtils.CAUTIOUS:
    # look upset

This would be useful in quite a few screens. It also keeps code that displays multiple attitude values from having to parse the text each time.
 
The one issue we have is that I don't think the game has a way to get a list of all the possible attitude keys which makes building the dictionary problematic. We could hardcode the list by looking at the SDK text manager function which has to access them all, but that makes things less mod-friendly and less, err, patch-proof.
 
Just use a tuple of keys for now:

Code:
MODIFIER_KEYS = (
    "TXT_KEY_DIPLO_REJECTED_CIVIC",
    "TXT_KEY_DIPLO_UGLY_FACE",
    ...
)

In the new core, modders and users will be able to modify this list from XML:

Code:
<init module="AttitudeUtils">
    <arg name="modifierKeys" type="tuple">
        "TXT_KEY_DIPLO_REJECTED_CIVIC",
        "TXT_KEY_DIPLO_UGLY_FACE",
        ...
    </arg>
</init>
 
I might as well post this here so I don't lose them. Here's the full list of modifier keys as of BTS 3.17:

The ones I don't think we can determine at run-time:
Spoiler :
Code:
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

The ones we can to determine at run-time through iterating over the MemoryTypes as in this example.
Code:
for i in range(MemoryTypes.NUM_MEMORY_TYPES):
	key = gc.getMemoryInfo(i).getTextKey()
Spoiler :
Code:
TXT_KEY_MEMORY_DECLARED_WAR
TXT_KEY_MEMORY_DECLARED_WAR_ON_FRIEND
TXT_KEY_MEMORY_HIRED_WAR_ALLY
TXT_KEY_MEMORY_NUKED_US
TXT_KEY_MEMORY_NUKED_FRIEND
TXT_KEY_MEMORY_RAZED_CITY
TXT_KEY_MEMORY_RAZED_HOLY_CITY
TXT_KEY_MEMORY_SPY_CAUGHT
TXT_KEY_MEMORY_GIVE_HELP
TXT_KEY_MEMORY_REFUSED_HELP
TXT_KEY_MEMORY_ACCEPT_DEMAND
TXT_KEY_MEMORY_REJECTED_DEMAND
TXT_KEY_MEMORY_ACCEPTED_RELIGION
TXT_KEY_MEMORY_DENIED_RELIGION
TXT_KEY_MEMORY_ACCEPTED_CIVIC
TXT_KEY_MEMORY_DENIED_CIVIC
TXT_KEY_MEMORY_ACCEPTED_JOIN_WAR
TXT_KEY_MEMORY_DENIED_JOIN_WAR
TXT_KEY_MEMORY_ACCEPTED_STOP_TRADING
TXT_KEY_MEMORY_DENIED_STOP_TRADING
TXT_KEY_MEMORY_STOPPED_TRADING
TXT_KEY_MEMORY_STOPPED_TRADING_RECENT
TXT_KEY_MEMORY_HIRED_TRADE_EMBARGO
TXT_KEY_MEMORY_MADE_DEMAND
TXT_KEY_MEMORY_MADE_DEMAND_RECENT
TXT_KEY_MEMORY_CANCELLED_OPEN_BORDERS
TXT_KEY_MEMORY_TRADED_TECH_TO_US
TXT_KEY_MEMORY_RECEIVED_TECH_FROM_ANY
TXT_KEY_MEMORY_VOTED_AGAINST_US
TXT_KEY_MEMORY_VOTED_FOR_US
TXT_KEY_MEMORY_EVENT_GOOD_TO_US
TXT_KEY_MEMORY_EVENT_BAD_TO_US
TXT_KEY_MEMORY_LIBERATED_CITIES
 
Update: this is what it looks like with religion-based attitude numbers, overall attitude, and a new question mark button NikNaks sent me. Not sure why the attitude column isn't showing the smilies, but I'll worry about that later.

 
Another Core related question. I see CvCustomEventManager has gone the way of the Dodo and been replaced by a shiny new BugEventManager. So how does one do event-hooking with the new Core? I tried creating the following XML file (Custom Assets/Config/FavoriteCivicDetector.xml)
Spoiler :
Code:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!--
	FavoriteCivicDetector
	by Dresden with help from EmperorFool

	Copyright (c) 2008 The BUG Mod.
-->
<mod id="FavoriteCivicDetector" 
	 name="FavoriteCivicDetector" 
	 author="Dresden, EmperorFool" 
	 version="1.0" 
	 date="9/1/2008"
	 url="http://forums.civfanatics.com/showthread.php?t=287856">
	 
	<events module="FavoriteCivicDetector" />
</mod>
and removing the Abstract event handler stuff like in Reminders but it doesn't seem that my event manager is being called as none of my debug prints are showing in the log and the data structure isn't getting initialized.

Here are the two class constructors from Custom Assets/Python/Contrib/FavoriteCivicDetector.py:
Spoiler :
Code:
class FavoriteCivicDetector:
	def __init__(self, eventManager):
		if isDetectionNecessary():
			BugUtil.debug("FavoriteCivicDetector::__init__() Detection Necessary. Resetting data and initing event manager.")
			reset()
			FavoriteCivicDetectorEvent(eventManager, self)
		else:
			BugUtil.debug("FavoriteCivicDetector::__init__() Detection NOT Necessary. Doing very little.")
			return
Spoiler :
Code:
class FavoriteCivicDetectorEvent:
	def __init__(self, eventManager, dataManager):
		## Init event handlers
		eventManager.addEventHandler('BeginActivePlayerTurn', self.onBeginActivePlayerTurn)
		eventManager.addEventHandler("GameStart", self.onGameStart)
		eventManager.addEventHandler("OnLoad", self.onLoadGame)
		eventManager.addEventHandler("OnPreSave", self.onPreSave)

		self.eventMgr = eventManager
		self.dataMgr = dataManager
What am I missing?
 
Right now the BUG core only loads init.xml, and that in turn loads all the other xml files in Config. Eventually the directory will be scanned, but for now just add a <load ...> tag to init.xml.

Everything else looks correct.

Also, you can use a class or functions for events as you see fit. I'm going to be making some modules from scratch that demonstrate some best practices and the easiest ways to do things.
 
Checkboxes make me sad (links to full-size)



I saw this once before, but if I fixed it I've forgotten how. Any ideas?

Code:
			for nCivicOption in range (gc.getNumCivicOptionInfos()):
				nCivic = objLoopPlayer.getCivics (nCivicOption)
				buttonName = self.getNextWidgetName()
				screen.attachCheckBoxGFC(infoPanelName, buttonName, gc.getCivicInfo (nCivic).getButton(), ArtFileMgr.getInterfaceArtInfo("BUTTON_HILITE_SQUARE").getPath(), 46, 46, WidgetTypes.WIDGET_PEDIA_JUMP_TO_CIVIC, nCivic, 1, ButtonStyles.BUTTON_STYLE_LABEL)
				if not bIsActivePlayer and nCivic == objActivePlayer.getCivics(nCivicOption):
					screen.setState(buttonName,True)
 
:confused: :confused: WTH! :confused: :confused:

Perhaps it doesn't like 46 as a button size? Doubtful, but try 64 just to see what happens. That's really bizarre; I suspect it has to do with attaching objects to panels, but I have nothing to support it. :(

I would post this to the SDK/Python forum in case someone else knows the fix. I sure don't. I've looked over my code from Ruff's mod, and my memory of highlighting the matching civics is either wrong or I don't have the latest version of my file. I found it in an old private message I sent to someone, so it's possible I got it working since then. All that version does is add an * next to the favorite civic if it's the same.
 
64 is better but it's still offset. Apparently the highlight box gets drawn from the upper left of the panel (or the attachment point) even though the button image itself gets centered. That's good interface design there.
 
I suppose you could redo the screen to avoid attaching things to panels, but that's a lot more work. Since there's really no game mechanic at work if you share civics with another civ (short of their favorite), I suppose you could just leave that part out.

And when you are both running an AI's favorite civic, it will have the [+2] or whatever next to it, right? So no loss I think.
 
Yeah, if there isn't a simple solution, I'll probably just ditch the highlighting. Shared favorite civics (when it's down to one choice if guessing) will show the dilplo modifier too like with Peter on that screenshot.
 
@Dresden - The Diplomacy Event code is in SVN and ready for your use. Your event handler should look something like this:

Code:
def onCivicDemanded(self, argsList):
	ePlayer, eTargetPlayer, eCivic = argsList
	[I]...register favorite civic of ePlayer as eCivic...[/I]

And you'll hook it up as you have done the others:

Code:
eventManager.addEventHandler("CivicDemanded", self.onCivicDemanded)
 
Thanks for adding the diplo event stuff, EF.

Update: I've been kinda lazy and won't be able to get this whole thing fully cleaned up and tested before I leave for a short trip, so don't expect to see anything commited before the middle of next week.

As it looks now, I'm abandoning the matching-civics highlighting since fixing it would mean like a total redesign of the screen and it's really not that useful since the favorite civic modifier will be explicitly listed.
 
One final question before this thing gets committed. What is the best way to handle display during the part of the game where you only know a couple of the rival civs? I think I prefer Option 3 but I wanted to get some other feedback.

Option 1: Only create panels for known civs.
Advantage: Simplicity
Disadvantage: Huge spacing on player panels


Option 2: Create panel for all civs with unknowns blank; order by player ID
Advantage: More reasonable spacing as long as there are several players left.
Disadvantage: Having knowns in the middle of unknowns might look kinda silly.


Option 3: Create panel for all civs with unknowns blank; show knowns first
Advantage: More reasonable spacing as long as there are several players left.
Disadvantage: Most complicated implementation


Note: This is what the display would look like in every case when all civs are known (pic from debug mode)
 
I don't know what makes it more complicated, but I prefer option 3 as well.

1. How about dropping the unknown civs entirely? The other screens don't show you empty spaces for unknown civs. They simply show the civs you have met.

2. When you are playing with Random Personalities disabled, is the last header "Favorite Civic"?

Looks great! I can't wait to try it out.
 
I don't know what makes it more complicated, but I prefer option 3 as well.
With option 3, I wind up looping over the players twice. The first time it's separated into known/unknown and then each of those groups is looped over afterwards to do the displaying. Not really a big deal, but it is slightly more work and I wanted to list a disadvantage for every option....

1. How about dropping the unknown civs entirely? The other screens don't show you empty spaces for unknown civs. They simply show the civs you have met.
It's pure aesthetics. Option 1 ignores unknown civs, but I just didn't like the humongous spaces between the rows so I came up with some alternatives. Because I am making the engine do almost all the layout work, spacing changes based on how many panels are added.

Also, options 2 or 3 could be done without the question marks, but that affects the spacing too, making the blank rows a bit smaller than the non-blank rows. This would be option 3 without question marks:


2. When you are playing with Random Personalities disabled, is the last header "Favorite Civic"?
Yeppers. (Actually it's "Favorite Civics" since I use the existing civilopedia text key) Example:
 
Back
Top Bottom