Military Advisor

yeah, ok. Got any training available? Its 141mb - does that sound right? eclipse-SDK-3.3.2-win32.zip. Will have to wait until I get home (or work).
 
Have the current sorting options, plus a few others: Location: Everywhere, Can Promote, and Current Level.

You're mixing sorting and filtering, and I think we covered the main ones above. I'll be adding checkboxes for filtering: wounded/healthy, promotable, etc. Everywhere is covered by not sorting by location. Level is a sorting.

The Current Level will be sorted within it's own pane as well, so you can select all level 5 units.

By "own pane" do you mean a subtree within the whole tree, just as the current view separates units in "Friendly Territory" from all other locations, but still in the same single tree? If so, this is covered by selecting the Level sorting (soon).

And I know it's a bit US-centric, but what about changing Sit-Rep to War Room?

I don't know about War Room since all tabs in this screen essentially make up a War Room, but I do think that a non-abbreviated word might be a good choice. This is more about foreign intelligence, so maybe "Intel" (ya, abbreviated, suckola). Dunno, I do like Sit-Rep though, so I'll just shut up now.

Also, I think we should add a panel below the Combat Experience that when you select a unit, will show avaliable promotions to that unit, and when you click on one it actually will promote the unit.

Hmm, that might be cool, but it seriously clutter the screen. Once i get the basic sort/filter functionality complete, we'll see how much screen real estate we have left. If we do promos, upgrades would be a good compliment.
 
Got any training available?

A quick walk-through with Alerum or myself would during setup would help greatly. There are a few things you need to add (SVN and Python support) that is simple to do once you've done it, but not obvious. If you try on your own, you want the "pydev" and "subclipse" plugins -- and the sites for each do have install instructions, so you can do it on your own no doubt.

What I've done is create a workspace for Eclipse (it's where all project files and settings are stored) as "C:\Coding\Civ". Then I created a new "SVN Checkout" project named "BUG Mod" and checked out the "BUG Mod" subtree of our repo. The one option you must make sure to enable is Pydev's "force tab mode" or something like that. If you use spaces for tabs, you'll screw up any files you modify.

Its 141mb - does that sound right? eclipse-SDK-3.3.2-win32.zip.

That sounds right.

Random Typographer's Note: You only use two spaces after sentence-ending punctuation when using a monospace font like Courier or a typewriter.
 
Random Typographer's Note: You only use two spaces after sentence-ending punctuation when using a monospace font like Courier or a typewriter.

I'm guessing this statement goes hand in hand with the other one you made a few days ago about not having anyone to test a MultiPlayer game with?:p

It's really simple to set up Eclipse Ruff. If EF isn't around to walk you through the set up, then send me an IM on yahoo and I'll help.
 
Random Typographer's Note: You only use two spaces after sentence-ending punctuation when using a monospace font like Courier or a typewriter.
L i s t e n b u c k w h e a t - s o m e o f u s l e a r n t t o t y p e o n a n o n - e l e c t r i c t y p e w r i t e r a n d n o w o u r s p a c e b a r t h u m b i s a u t o p r o g r a m e d t o h i t t h e s p a c e b a r t w i c e !

I'm guessing this statement goes hand in hand with the other one you made a few days ago about not having anyone to test a MultiPlayer game with?:p
Well said!
 
@ruff:

Eclipse is just plain great. I use it myself. Besides, EF can help you with everything - he is the man ! :)
 
d/l eclipse, unzipped, ran exe, failed to start, reviewing instructions .....

You will need a Java runtime environment (JRE) to use Eclipse (Java 5 JRE recommended). All downloads are provided under the terms and conditions of the Eclipse Foundation Software User Agreement unless otherwise specified

A who? And what the heck is an IDE? Why can't I just use notepad? :waaaaaaaaaaah:
 
IDE: Integrated Development Environment
You should have JRE (Java), that can be acquired from SUN fairly easily.
I haven't installed Eclipse myself, but have used other Java apps for video editing and what not. You should be able to just doubleclick or rightclick on the .jar file and "open with Java"
or choose the Java .exe from "choose program..."

I use MetaPad, ConTEXT (syntax hilighting sometimes) and NotePad as a scratchpad occasionally (I have it sized to open as a rectangle small pane).

IDE's are the future, and where the industry is headed, but I'm oldschool still. I may buckle under one of these days.
 
ok, eclipse and two "pydev" and "subclipse" plugins installed. Next (tonight or tomorrow, weekend, next month) will involve changing some config options (outlined by EF above) and seeing if it actually works.
 
The latest BUG-MA has a few issues, and I have some suggestions.

Spoiler Screenshot :
Worst Enemy: Column is too narrow now (threat bar partially inside WE icon) and the header says only "Wo..." Perhaps use a small icon so only the actual leader of the row uses a large icon, and make the header be two rows.

Maybe put the WHEOOH fist next to the threat index bar again since they kinda go hand-in-hand.

I had a game where the bar was not drawn at all for a single civ, though it did say "Elevated" IIRC. Sorry, I thought I had that saved, but I can't find which save now. Looking at the code I can't see any reason why this would happen. Is there a case that I'm not thinking of where that's intended behavior?

In my current game (Lizard Breath), all Strategic Advantages cells have the red "n/a" symbol, but Qin has Feudalism and Longbows in his nearby cities and I do not. Also Monty has Horses and Chariots and I do not.

In the same save, Qin has no worst enemy (same red "n/a"), but I think I am his WE because he DoWs me in a few turns. Is it possible for an AI to not have a WE, or is this because I have a non-standard name (not the actual leader's name) for my leader? So I just demanded Monotheism from him 20 times, moving him from Cautious to Furious, and I'm still not his WE. The scoreboard also doesn't show it, but I know the WE indicator has worked for me plenty, and I never use a normal name -- I always change it, so I don't think that's it. Can he really not have a WE?

Hmm, in the second save, Boudica, de Gaulle has no WE either, though note that he hasn't warred with any of the remaining civs yet. Also, the Strat Advs for Saladin show "N/A", yet he is behind me in techs (Riflemen vs. Infantry IIRC, no Cannons). The other SAs look correct I think (they at least show something).

Thinking about the "Active Wars" and "Will Declare?" columns, perhaps group them just like the "Strategic Advantages" columns. Put "War" as the main heading, and "Active" and "Willing"/"Would"/"Bribe" or something like that for "Will Declare?" This would make it clear that both columns are about war. I was also thinking maybe reverse the two columns, so "Active" is on the left.

Note that I'm using 3.13, Bhruic's DLL, and the latest BUG from SVN. Removing Bhruic's patch makes no difference.
 
A civ can have no WE. I'll take a look at your other suggestions and use these games to track down some of the issues re strat advantage.
 
I'm not looking at the code, but I know you were thinking of having it loop through all cities to check for buildability of the SAs. I was thinking it should loop through only the cities that you know about, so if you don't know they have any coastal cities, you'd think they couldn't build boats. If you like that idea, I can code it up pretty quick if you'd like.

However, as I see their cultural borders develop, sometimes you can deduce where a city must be, and I mark those with a sign. There's no way to work these into the algorithm without leaking information (telling me if I'm correct about my sign). I typed this because I was going to suggest treating the signs as an assumed city, but I don't see how that can be done. You need an actual CyCity to test for buildability, and that's where the leak comes from. So ignore this paragraph. :)
 
I see in the code that you're looping through all cities. The change to make it ignore cities you don't know about, I think you can use CyCity.isRevealed(eTeam, False):

Code:
pPlayer = gc.getPlayer(iPlayer)
[B]eTeam = gc.getPlayer(self.iActivePlayer).getTeam()[/B]
...
for c in range(pPlayer.getNumCities()):
	pCity = pPlayer.getCity(c)
	if pCity 
	and not pCity.isNone() 
	[B]and pCity.isRevealed(eTeam, False)[/B]
	and pCity.canTrain(iUnit, False, False):
		iUnits.add(iUnit)
		break
 
Code:
pPlayer = gc.getPlayer(iPlayer)
eTeam = gc.getPlayer(self.iActivePlayer).getTeam()
What is the difference between 'p' and 'e' at the beginning of player and team? I put in the 'p' for pointer. You put in the 'e'? Or are you just messin' with my head?
 
I'm going to look at the sit-rep this afternoon during my plane flight - unless I play a HOF game :D. Things that I will look at changing:
  • put a header over the declare and current called 'War Status'
  • Change 'Will Declare' to 'Optional'
  • Swap 'Active' and 'Optional'
  • widen the screen for people with wider screens (we should look at this for all of the adviser screens - the HOF guys did
  • move (or remove) worse enemy, just not sure where to - might remove it and replace it with an icon if YOU are the AIs worse enemy, leave the AI-AI worst enemy info on Glance
  • change the '*' to 'est' so it is clearer
  • look at EFool's 'Strategic Advantage issues mentioned above
 
What is the difference between 'p' and 'e' at the beginning of player and team?

In Python, it is only a convention to denote what the variable holds. Just so that's clear, it doesn't do anything to the code.

Now, you are right that a "p" prefix means Pointer. The "e" prefix denotes an enumeration (set number of items like the religions or the teams or players). It holds an integer from 0..max-1. -1 can sometimes be used to mean "none", for example NO_RELIGION == -1.

The Python API is quite confusing. CyGame.getActivePlayer() returns an ePlayer. CyGlobalContext.getActivePlayer() returns a CyPlayer object. CyPlayer.getTeam() returns an eTeam, so to get the CyTeam object you must use gc.getTeam(pPlayer.getTeam()).

A lot of the existing mod code uses "i" instead of "e". I try to match what already exists. "e" is what all the C++ code uses. In the end, they're all integers. :)

Please don't remove WE from MA. I like having one-key access to that screen whereas I typically leave the EFA on the Techs tab.

When I get a generic hover-text function for screens, I can make it so hovering over the Threat Index will explain what the * means.

I would love to widen the screens, and that's good to know it's already been done. CDA would require another set of pages as well.
 
The latest BUG-MA has a few issues, and I have some suggestions.

Oh, good. Sorry for the suggestion I've given by mail, I've not yet read this thread.

Worst Enemy: Column is too narrow now (threat bar partially inside WE icon) and the header says only "Wo..." Perhaps use a small icon so only the actual leader of the row uses a large icon, and make the header be two rows.

I agree, great idea!

Maybe put the WHEOOH fist next to the threat index bar again since they kinda go hand-in-hand.

Good

Thinking about the "Active Wars" and "Will Declare?" columns, perhaps group them just like the "Strategic Advantages" columns. Put "War" as the main heading, and "Active" and "Willing"/"Would"/"Bribe" or something like that for "Will Declare?" This would make it clear that both columns are about war. I was also thinking maybe reverse the two columns, so "Active" is on the left.

Yes, maybe it's better in this way.
 
Maybe I just don't know, but is there any way to use the "beta" MilAdv while waiting for the new version of BUG?

Could you please pack the files in a zip and post it here?
 
BUG 2.30 has the latest Military Advisor, unless I'm off my meds again and am just forgetting. I haven't had a chance to tackle this beast yet. Do you see the "Sit-Rep" tab? If so, you have the goodies.

If you want to get crazy, you can check out the different grouping properties. Open CvBUGMilitaryAdvisor.py and go to line 1086.

PHP:
grouping1 = self.stats.getGrouping("loc")
grouping2 = self.stats.getGrouping("type")
You can change the strings ("loc", "type") to any of these values:

  • "type" - Unit Type
  • "combat" - Combat Type
  • "level" - Level
  • "loc" - Location
  • "promo" - Promotion*
* For this one to work properly, you'll actually need a modified file. Replace the entire contents of the file "Python\BUG\UnitGrouper.py" with this:

Spoiler UnitGrouper.py :
Code:
## UnitGrouper
## Builds groups of units for use in reporting or screens.
## Copyright (c) 2008 The BUG Mod.

from CvPythonExtensions import *
import BugUtil

# globals
gc = CyGlobalContext()

# Base grouping classes

class Grouper:
	"""
	Holds all Grouping definitions.
	"""
	def __init__(self):
		self.groupings = []
		self.groupingsByKey = {}
	
	def _addGrouping(self, grouping):
		grouping.index = len(self.groupings)
		self.groupings.append(grouping)
		self.groupingsByKey[grouping.key] = grouping
	
	def getGrouping(self, key):
		if key in self.groupingsByKey:
			return self.groupingsByKey[key]
		else:
			return None

class Grouping:
	"""
	Applies a formula to place units into groups.
	
	key: used for sorting groupings; must be in the range [0, 999] inclusive
	title: used to display the group
	"""
	def __init__(self, key, title):
		self.index = None
		self.key = key
		self.title = title
		self.groups = {}
	
	def _addGroup(self, group):
		self.groups[group.key] = group
	
	def calcGroupKeys(self, unit, player, team):
		return None

class Group:
	"""
	Represents a single group value within a grouping.
	
	key: used for sorting groups; must be in the range [0, 999] inclusive
	title: used to display the group
	"""
	def __init__(self, grouping, key, title):
		self.grouping = grouping
		self.key = key
		self.title = title
	
	def getTitle(self):
		return self.title


# Grouping definitions

class UnitTypeGrouping(Grouping):
	"""
	Groups units by their unit type.
	Ex: Warrior, Maceman, Panzer
	"""
	def __init__(self):
		Grouping.__init__(self, "type", "Unit Type")
		
		for i in range(gc.getNumUnitInfos()):
			info = gc.getUnitInfo(i)
			if info:
				self._addGroup(Group(self, i, info.getDescription()))
	
	def calcGroupKeys(self, unit, player, team):
		return (unit.getUnitType(),)

class UnitCombatGrouping(Grouping):
	"""
	Groups units by their combat type.
	Ex: None, Melee, Gunpowder, Naval
	"""
	def __init__(self):
		Grouping.__init__(self, "combat", "Combat Type")
		self.NONE = 0
		
		self._addGroup(Group(self, self.NONE, "None"))
		for i in range(gc.getNumUnitCombatInfos()):
			info = gc.getUnitCombatInfo(i)
			if info:
				self._addGroup(Group(self, i + 1, info.getDescription()))
	
	def calcGroupKeys(self, unit, player, team):
		return (gc.getUnitInfo(unit.getUnitType()).getUnitCombatType() + 1,)

class LevelGrouping(Grouping):
	"""
	Groups units by their level, 1 to MAX_LEVEL (30).
	Units over level MAX_LEVEL are put into the MAX_LEVEL group.
	"""
	def __init__(self):
		Grouping.__init__(self, "level", "Level")
		
		self.MAX_LEVEL = 30
		for i in range(self.MAX_LEVEL):
			self._addGroup(Group(self, i, BugUtil.getText("TXT_KEY_UNIT_GROUPER_LEVEL_GROUP", (str(i),))))
		self._addGroup(Group(self, self.MAX_LEVEL, BugUtil.getText("TXT_KEY_UNIT_GROUPER_LEVEL_GROUP", ("%d+" % self.MAX_LEVEL,))))
	
	def calcGroupKeys(self, unit, player, team):
		return (max(0, min(unit.getLevel(), self.MAX_LEVEL)),)

class PromotionGrouping(Grouping):
	"""
	Groups units by their promotions.
	Ex: Combat 1, Cover, Tactics
	"""
	def __init__(self):
		Grouping.__init__(self, "promo", "Promotion")
		
		self.NONE = 0
		self._addGroup(Group(self, self.NONE, "None"))
		for i in range(gc.getNumPromotionInfos()):
			info = gc.getPromotionInfo(i)
			if info:
				self._addGroup(Group(self, i + 1, info.getDescription()))
	
	def calcGroupKeys(self, unit, player, team):
		promos = []
		for iPromo in range(gc.getNumPromotionInfos()):
			if unit.isHasPromotion(iPromo):
				promos.append(iPromo + 1)
		if not promos:
			promos = (self.NONE,)
		return promos

class LocationGrouping(Grouping):
	"""
	Groups units by their location on the map.
	Ex: Domestic City, Friendly City, Enemy Territory
	"""
	def __init__(self):
		Grouping.__init__(self, "loc", "Location")
		self.DOMESTIC_CITY = 0
		self.DOMESTIC_TERRITORY = self.DOMESTIC_CITY + 1
		self.TEAM_CITY = self.DOMESTIC_TERRITORY + 1
		self.TEAM_TERRITORY = self.TEAM_CITY + 1
		self.FRIENDLY_CITY = self.TEAM_TERRITORY + 1
		self.FRIENDLY_TERRITORY = self.FRIENDLY_CITY + 1
		self.NEUTRAL_TERRITORY = self.FRIENDLY_TERRITORY + 1
		self.ENEMY_TERRITORY = self.NEUTRAL_TERRITORY + 1
		self.BARBARIAN_TERRITORY = self.ENEMY_TERRITORY + 1
		
		self._addGroup(Group(self, self.DOMESTIC_CITY, "Domestic City"))
		self._addGroup(Group(self, self.DOMESTIC_TERRITORY, "Domestic Territory"))
		self._addGroup(Group(self, self.TEAM_CITY, "Team City"))
		self._addGroup(Group(self, self.TEAM_TERRITORY, "Team Territory"))
		self._addGroup(Group(self, self.FRIENDLY_CITY, "Friendly City"))
		self._addGroup(Group(self, self.FRIENDLY_TERRITORY, "Friendly Territory"))
		self._addGroup(Group(self, self.NEUTRAL_TERRITORY, "Neutral Territory"))
		self._addGroup(Group(self, self.ENEMY_TERRITORY, "Enemy Territory"))
		self._addGroup(Group(self, self.BARBARIAN_TERRITORY, "Barbarian Territory"))
	
	def calcGroupKeys(self, unit, player, team):
		plot = unit.plot()
		if not plot or plot.isNone():
			return None
		if plot.isBarbarian():
			return (self.BARBARIAN_TERRITORY,)
		teamId = team.getID()
		ownerId = plot.getRevealedOwner(teamId, False)
		if ownerId == -1:
			return (self.NEUTRAL_TERRITORY,)
		elif ownerId == player.getID():
			if plot.isCity():
				return (self.DOMESTIC_CITY,)
			else:
				return (self.DOMESTIC_TERRITORY,)
		else:
			owner = gc.getPlayer(ownerId)
			ownerTeamId = owner.getTeam()
			if ownerTeamId == teamId:
				if plot.isCity():
					return (self.TEAM_CITY,)
				else:
					return (self.TEAM_TERRITORY,)
			elif team.isAtWar(ownerTeamId):
				return (self.ENEMY_TERRITORY,)
			else:
				if plot.isCity():
					return (self.FRIENDLY_CITY,)
				else:
					return (self.FRIENDLY_TERRITORY,)

class StandardGrouper(Grouper):
	def __init__(self):
		Grouper.__init__(self)
		
		self._addGrouping(UnitTypeGrouping())
		self._addGrouping(UnitCombatGrouping())
		self._addGrouping(LevelGrouping())
		self._addGrouping(PromotionGrouping())
		self._addGrouping(LocationGrouping())


# Classes for tracking stats about groups and units

class GrouperStats:
	"""
	Holds stats for a set of groupings.
	"""
	def __init__(self, grouper):
		self.grouper = grouper
		self.groupings = {}

		for grouping in self.grouper.groupings:
			self._addGrouping(GroupingStats(grouping))
	
	def _addGrouping(self, grouping):
		self.groupings[grouping.grouping.key] = grouping
	
	def processUnit(self, player, team, unit):
		stats = UnitStats(unit.getOwner(), unit.getID(), unit)
		for grouping in self.groupings.itervalues():
			grouping._processUnit(player, team, stats)
		return stats
	
	def getGrouping(self, key):
		if key in self.groupings:
			return self.groupings[key]
		else:
			return None
	
	def itergroupings(self):
		return self.groupings.itervalues()

class GroupingStats:
	"""
	Holds stats for a grouping.
	"""
	def __init__(self, grouping):
		self.grouping = grouping
		self.groups = {}
		
		for group in self.grouping.groups.itervalues():
			self._addGroup(GroupStats(group))
	
	def _addGroup(self, group):
		self.groups[group.group.key] = group
	
	def _processUnit(self, player, team, unitStats):
		keys = self.grouping.calcGroupKeys(unitStats.unit, player, team)
		for key in keys:
			self.groups[key]._addUnit(unitStats)
	
	def itergroups(self):
		return self.groups.itervalues()

class GroupStats:
	"""
	Holds stats for a group of units.
	"""
	def __init__(self, group):
		self.group = group
		self.units = set()
	
	def _addUnit(self, unitStats):
		self.units.add(unitStats)
	
	def title(self):
		return self.group.title
	
	def size(self):
		return len(self.units)
	
	def isEmpty(self):
		return self.size() == 0

class UnitStats:
	"""
	Holds stats about a single unit.
	"""
	def __init__(self, playerId, unitId, unit):
		self.key = (playerId, unitId)
		self.unit = unit

	def __hash__(self):
		return hash(self.key)

	def __eq__(self, other):
		return self.key == other.key
 
Top Bottom