trying to use a minimalist mod to do big things

ID numbers for dynamic elements like units, civilizations, and leaders are based on their position in the XML files--really, the order in which they are loaded. Very few things are compiled into the C++ SDK, such as domain and AI types.

You can find the IDs of any object using gc.getInfoTypeForString().

Code:
eRome = gc.getInfoTypeForString("CIVILIZATION_ROME")
eSettler = gc.getInfoTypeForString("UNIT_SETTLER")

eRome above is the Roman civilization--not the player using that civilization type. To find which player is Roman requires looping over the players and checking their getCivilizationType() against eRome.

Code:
for ePlayer in range(gc.getMAX_CIV_PLAYERS()):
    pPlayer = gc.getPlayer(ePlayer)
    if pPlayer.getCivilizationType() == eRome:
        pRome = pPlayer
        break
else:
    pRome = None

if pRome:
    pRome.initUnit(...)
 
You need to start using the API. For real.

In the lower left pane there is a link called "Type List" and it contains all the default types of BtS. What you need is UnitTypes to look up the ID of UNIT_SETTLER. If you look at PlayerTypes you will realize that these are set dynamically from your own scenario - look it up in your own WBS file! (If Rome is the first player then its enumerated as zero. And so on.)

The API is what you have to work with, so its vital that you learn how to find anything you need in there. And also make a habit of looking up all methods suggested by others or that you find in other people's code. Because they are all documented in the API. You need to know what a given method returns and what parameter it requires. Otherwise you end up wasting another 100 hours on aimless debugging...
 
The API is one of those things I've got open as I'm working, but still having trouble finding what I need when I need it and integrating what it tells me. Still learning as I go along...
 
ID numbers for dynamic elements like units, civilizations, and leaders are based on their position in the XML files--really, the order in which they are loaded. Very few things are compiled into the C++ SDK, such as domain and AI types.

You can find the IDs of any object using gc.getInfoTypeForString().

Code:
eRome = gc.getInfoTypeForString("CIVILIZATION_ROME")
eSettler = gc.getInfoTypeForString("UNIT_SETTLER")
Personally I prefer to use the actual "types" instances:
Code:
eSettler = UnitTypes.UNIT_SETTLER

eRome above is the Roman civilization--not the player using that civilization type. To find which player is Roman requires looping over the players and checking their getCivilizationType() against eRome.
This is not really required for a scenario, where you always know what the IDs for each player will be. So integer constants are sufficient:
Code:
eEgypt = 0
eIndia = 1
eChina = 2
eBabylonia = 3
This is what Rhye did in RFC, by the way. But it is confusing to use Civ names as player IDs...
 
The API is one of those things I've got open as I'm working, but still having trouble finding what I need when I need it and integrating what it tells me. Still learning as I go along...
It took me long enough to figure out myself, but don't consider it as a badge of honor if you "figure it out" yourself. Just ask if something is confusing, because the community will be able to explain everything. Just as with Python in general - there is no real reason for not understanding everything you might encounter. That is just handicapping yourself without any reason.
 
Ah, to work on a mod where you can count on everything being where you put it. Alas, I do not have that luxury in BUG Mod where I need to handle whatever mod BUG gets put into without forcing the mod developer to change it.

Does BTS really declare UnitTypes.UNIT_SETTLER? These certainly cannot be baked into the SDK, but it's entirely possible for the SDK to create them on-the-fly as it loads the XML. I just didn't think it did that for units and techs.
 
I can't seem to get the console to play nice. I'm going to try to get the year trigger code written and see if the game will spawn the units on its own. I also went in and defined constants for all civs and units I anticipate using for this using the eEgypt = CivilizationTypes.CIVILIZATION_EGYPT format
 
Does BTS really declare UnitTypes.UNIT_SETTLER? These certainly cannot be baked into the SDK, but it's entirely possible for the SDK to create them on-the-fly as it loads the XML. I just didn't think it did that for units and techs.
Well, maybe it doesn't... :rolleyes: Once again, what do I know?

I was just assuming things - like that all the types are set dynamically on initialization. I'm not even sure if I have used this for UnitTypes myself...

Actually, I made this little helper function for fetching enumerated index values:
Code:
def eIndex(category, value):
        for i in range(len(value)):
                if value[i] == " ":
                        value = value[:i] + "_" + value[i+1:]
                        break
        return gc.getInfoTypeForString(string.upper(category + "_" + value))
This is really for end users of my API but I would use it myself also.:
Code:
eSettler = eIndex("unit", "Settler")
The big "feature" would be that it also works with:
Code:
eHorseArcher = eIndex("unit", "Horse Archer")
(Replacing the whitespace in the unit name string with a underscore, or whatever its called.)
 
OMFG you should totally learn the Python API and stop wasting so much time!!!! Hehe, just kidding. :p

Code:
def eIndex(category, value):
        return gc.getInfoTypeForString((category + "_" + [B][COLOR="Red"]value.replace(" ", "_")[/COLOR][/B]).upper())
 
OMFG you should totally learn the Python API and stop wasting so much time!!!! Hehe, just kidding. :p

Code:
def eIndex(category, value):
        return gc.getInfoTypeForString((category + "_" + [B][COLOR="Red"]value.replace(" ", "_")[/COLOR][/B]).upper())
Ok, back to school! :eek2:

This involves importing the value module, then? And the category string also needs to be in uppercase, so it should probably be:
Code:
def eIndex(category, value):
        return gc.getInfoTypeForString((category[B][COLOR="Red"].upper()[/COLOR][/B] + "_" + value.replace(" ", "_")).upper())
I still think its more convenient to use the string module for this particular task:

Code:
def eIndex(category, value):
        return gc.getInfoTypeForString([B][COLOR="Red"]string.upper[/COLOR][/B](category + "_" + value.replace(" ", "_")))
The Python API is still a lot more complex to "figure out" than the CivIV Python API. But I guess that hardly is an excuse for me not really knowing how to interpret it... :rolleyes:
 
Check those parentheses. "value" is the string argument passed in to the function, and replace() and upper() are methods of the string class. There's nothing to import here, and my code was concatenating category, an underscore, and value (with underscores) before uppercasing the whole thing.

Effectively, it was this:

Code:
def eIndex(category, value):
    value = value.replace(" ", "_")
    key = category + "_" + value
    return gc.getInfoTypeForString(key.upper())
 
yeah...it's not liking those attributes.

just to be clear, how should i be referencing those integers for the unit and civ types when I define my constants?
 
Check those parentheses. "value" is the string argument passed in to the function, and replace() and upper() are methods of the string class. There's nothing to import here, and my code was concatenating category, an underscore, and value (with underscores) before uppercasing the whole thing.
Yeah, you're right. :p Of course. :rolleyes: But one of these days I'll catch you with a proper mistake, you just wait! :lol:

What is the string module good for? Right now my application is importing it and using it, but I realize I don't really need it. The string class methods makes it a pretty superfluous module, not?
 
I remember when doing Rhye's he used something like
iWorker = con.iWorker

and then in the constants file he had them assigned integers, but how does the game know which pool of integer values it's linking since the units and the civs both have lists that run 0, 1, 2, 3...
 
yeah...it's not liking those attributes.
You mean eRome and eSettler?

If you were mod-modding RFC you could just import the Consts module and use iRome and iSettler... But now you need to define your own constants.

just to be clear, how should i be referencing those integers for the unit and civ types when I define my constants?
Note that there is a real difference between player types and civilization types. Read what EmperorFool said above.

Other than that, I believe that we've already given you several completely different approaches in this discussion. Read what we said and test them all, if you like.
 
...or it simply knows what the integer value means when it goes looking for that slot in the function argument...
 
I remember when doing Rhye's he used something like
iWorker = con.iWorker

and then in the constants file he had them assigned integers, but how does the game know which pool of integer values it's linking since the units and the civs both have lists that run 0, 1, 2, 3...
Forget about RFC - you don't wanna copy that code.

Basically, all the game elements (players, civs, units, unit types, you name it) are enumerated in the C++ code. This is AFAIK done dynamically from reading the XML at initialization. And the computer always counts from zero and up (by default) - so this is why all indexes also start with zero.

This is why the first player that appears in your WBS file is indexed with the integer value 0, as does the first building in the XML (Palace) and the first unit (Lion). There is no conflict between these integers, as they relate to completely different things.

Like every city in the game has a numerical ID - ranging from zero and up. And every unit is also indexed with a ID - ranging from zero and up. Of course there is no way the game would interpret a CyCity instance for a CyUnit instance, right? Because you'd get get CyCity instances with getCity(ID) and the unit with getUnit(ID). The first one returns a CyCity instance and the second one a CyUnit instance. The ID is just the "key" that identifies the city among other cities, and the unit among other units.

The same goes for types of things, like unit types, promotion types or building types. The context in which you are using the ID value determines what it means.
 
I forgot that it doesn't assign all those values in constants actively, it only substitutes the assigned value when the codeword is used in a function (in other words...it won't find a conflict with
eRome = CivilizationTypes.CIVILIZATION_ROME
until it encounters something that calls for eRome...then it looks to see what eRome is and says "huh?"). so it doesn't matter that the lion unit and American civ have the same integer value because where eAmerica or eLion come in the string will dictate where it goes to look up that value (whether in the list of civ types or unit types)
 
alright, this loads up with no errors. Now I just have to put a trigger in CvEventManager so I can watch it happen...

Spoiler :
Code:
#done by Antaine with great indebtedness to Baldyr, God-Emperor, and EmperorFool

from CvPythonExtensions import *
from PyHelpers import *
from Popup import PyPopup
from CvEventManager import *

gc = CyGlobalContext()
cyGame = CyGame()
cyMap = CyMap()
pyGame = PyGame()

#units for later civ spawns
eSettler = 4
eWorker = 5
eArcher = 57
eLongbowman = 60
eMusketman = 42
eRifleman = 46

#for barbarian spawns
eGalley = 90
eViking = 36
eZulu = 38
eMongol = 68
eNative = 33
eAztec = 27
eInca = 25
eMaya = 39

#civs in the game
eCeltia = 6
eEgypt = 8
eBabylon = 3
eGreece = 13
eIndia = 16
eChina = 7
eRome = 28
ePersia = 26
eJapan = 17
eKorea = 19
eArabia = 1
eEngland = 9
eFrance = 11
eSpain = 30
eGermany = 12
eRussia = 29
eAmerica = 0


cityNameDict = {
(55, 54): "London",
(55, 53): "London",
(56, 53): "London",
(56, 54): "London",
(63, 52): "Berlin",
(62, 52): "Berlin",
(62, 53): "Berlin",
(63, 53): "Berlin",
(51, 55): "Dublin",
(51, 56): "Dublin",
(55, 58): "Edinburgh",
(54, 57): "Edinburgh",
(54, 58): "Edinburgh",
(58, 51): "Paris",
(59, 51): "Paris",
(58, 52): "Paris",
(57, 51): "Paris",
(58, 50): "Paris",
(57, 50): "Paris",
(55, 45): "Madrid",
(55, 46): "Madrid",
(54, 46): "Madrid",
(54, 45): "Madrid",
(53, 45): "Lisbon",
(53, 44): "Lisbon",
(53, 46): "Lisbon",
(61, 46): "Rome",
(62, 45): "Rome",
(62, 46): "Rome",
(61, 47): "Rome",
(60, 47): "Rome",
(67, 43): "Athens",
(66, 42): "Athens",
(67, 44): "Athens",
(66, 44): "Athens",
(68, 46): "Byzantium",
(67, 46): "Byzantium",
(69, 45): "Byzantium",
(73, 40): "Jerusalem",
(73, 41): "Jerusalem",
(72, 40): "Jerusalem",
(73, 42): "Antioch",
(73, 43): "Antioch",
(68, 39): "Alexandria",
(69, 39): "Alexandria",
(67, 39): "Alexandria",
(30, 47): "New York",
(31, 47): "New York",
(29, 47): "New York",
(29, 46): "New York",
(31, 48): "Boston",
(31, 49): "Boston",
(32, 49): "Boston",
(24, 43): "New Orleans",
(23, 43): "New Orleans",
(25, 43): "New Orleans",
(24, 48): "Chicago",
(24, 47): "Chicago",
(24, 49): "Chicago",
(25, 47): "Chicago",
(12, 48): "San Francisco",
(12, 47): "San Francisco",
(12, 49): "San Francisco",
(12, 50): "Seattle",
(12, 46): "Los Angeles",
(75, 35): "Mecca",
(74, 36): "Mecca",
(75, 34): "Mecca",
(74, 37): "Mecca",
(73, 37): "Mecca",
(76, 34): "Mecca",
(68, 57): "St. Petersburg",
(67, 56): "St. Petersburg",
(66, 56): "St. Petersburg",
(68, 56): "St. Petersburg",
(68, 58): "St. Petersburg",
(67, 58): "St. Petersburg",
(71, 54): "Moscow",
(71, 53): "Moscow",
(70, 53): "Moscow",
(72, 53): "Moscow",
(72, 54): "Moscow",
(70, 54): "Moscow",
(70, 56): "Moscow",
(71, 55): "Moscow",
(72, 55): "Moscow",
(69, 52): "Kiev",
(69, 51): "Kiev",
(70, 51): "Kiev",
(70, 50): "Odessa",
(71, 50): "Odessa",
(89, 35): "Bombay",
(89, 34): "Bombay",
(88, 35): "Bombay",
(88, 36): "Bombay",
(92, 34): "Pondicherry",
(91, 34): "Pondicherry",
(91, 33): "Pondicherry",
(102, 47): "Peking",
(101, 48): "Peking",
(102, 48): "Peking",
(103, 48): "Peking",
(101, 47): "Peking",
(103, 47): "Peking",
(101, 46): "Peking",
(102, 46): "Peking",
(103, 46): "Peking",
(115, 46): "Tokyo",
(115, 45): "Tokyo",
(114, 44): "Tokyo",
(106, 43): "Shanghai",
(106, 44): "Shanghai",
(106, 45): "Shanghai",
(107, 43): "Shanghai",
(107, 42): "Shanghai",
(105, 39): "Canton",
(105, 40): "Canton",
(106, 40): "Canton",
(106, 41): "Canton",
(103, 38): "Hong Kong",
(103, 39): "Hong Kong",
(104, 39): "Hong Kong",
(102, 38): "Hong Kong",
(102, 32): "Saigon",
(103, 33): "Saigon",
(102, 33): "Saigon",
(1, 38): "Honolulu",
(3, 37): "Honolulu",
(4, 35): "Hilo",
(5, 36): "Hilo",
(103, 14): "Perth",
(104, 14): "Perth",
(104, 13): "Perth",
(104, 12): "Perth",
(103, 12): "Perth",
(107, 19): "Darwin",
(107, 20): "Darwin",
(108, 20): "Darwin",
(108, 21): "Darwin",
(118, 13): "Sydney",
(117, 12): "Sydney",
(118, 15): "Sydney",
(118, 16): "Sydney",
(118, 17): "Sydney",
(114, 10): "Adelaide",
(114, 11): "Adelaide",
(113, 11): "Adelaide",
(115, 10): "Adelaide",
(77, 41): "Babylon",
(77, 40): "Babylon",
(76, 41): "Babylon",
(77, 42): "Babylon",
(76, 42): "Babylon",
(58, 47): "Marseilles",
(58, 48): "Marseilles",
(59, 48): "Marseilles",
(56, 48): "Bordeaux",
(56, 49): "Bordeaux",
(55, 51): "Brest",
(56, 51): "Brest",
(56, 44): "Gibraltar",
(54, 44): "Gibraltar",
(53, 54): "Cardiff",
(54, 54): "Cardiff",
(53, 55): "Cardiff",
(53, 53): "Cardiff",
(55, 56): "York",
(54, 56): "York",
(49, 54): "Cork",
(49, 56): "Galway",
(50, 56): "Galway",
(51, 54): "Waterford",
(50, 54): "Cashel",
(51, 57): "Belfast",
(50, 57): "Derry",
(50, 55): "Limerick",
(60, 55): "Copenhagen",
(60, 54): "Copenhagen",
(60, 53): "Copenhagen",
(62, 52): "Trondheim",
(63, 63): "Trondheim",
(64, 64): "Trondheim",
(60, 58): "Oslo",
(59, 57): "Oslo",
(58, 58): "Oslo",
(62, 58): "Uppsala",
(62, 59): "Uppsala",
(62, 60): "Uppsala",
(63, 58): "Stockholm",
(63, 57): "Stockholm",
(63, 56): "Stockholm",
(62, 56): "Stockholm",
(66, 59): "Helsinki",
(65, 59): "Helsinki",
(65, 58): "Helsinki",
(65, 58): "Vilnius",
(65, 55): "Vilnius",
(66, 54): "Vilnius",
(66, 55): "Vilnius",
(64, 53): "Vilnius",
(65, 53): "Vilnius",
(66, 53): "Vilnius",
(63, 50): "Vienna",
(64, 50): "Vienna",
(65, 50): "Vienna",
(65, 49): "Vienna",
(64, 49): "Vienna",
(63, 49): "Vienna",
(64, 52): "Warsaw",
(63, 51): "Warsaw",
(62, 48): "Venice",
(61, 48): "Venice",
(63, 48): "Venice",
(61, 43): "Palermo",
(62, 43): "Palermo",
(76, 32): "Sanaa",
(77, 32): "Sanaa",
(77, 33): "Sanaa",
(78, 33): "Sanaa",
(64, 12): "Cape Town",
(64, 13): "Cape Town",
(65, 13): "Cape Town",
(65, 12): "Cape Town",
(42, 18): "Rio De Janeiro",
(43, 19): "Rio De Janeiro",
(41, 18): "Rio De Janeiro",
(39, 15): "Montevideo",
(38, 14): "Montevideo",
(38, 13): "Montevideo",
(36, 13): "Buenos Aires",
(37, 13): "Buenos Aires",
(36, 12): "Buenos Aires",
(18, 39): "Mexico City",
(18, 38): "Mexico City",
(19, 39): "Mexico City",
(19, 40): "Mexico City",
(18, 49): "Mexico City",
(24, 45): "St. Louis",
(25, 45): "St. Louis",
(25, 46): "St. Louis",
(24, 46): "St. Louis",
(27, 38): "Havanna",
(28, 38): "Havanna",
(31, 37): "Port-au-Prince",
(31, 39): "Port-au-Prince",
(56, 50): "Nantes",
(59, 53): "Amsterdam",
(59, 52): "Amsterdam",
(28, 46): "Philadelphia",
(28, 45): "Philadelphia",
(27, 45): "Washington",
(27, 44): "Washington",
(58, 42): "Carthage",
(59, 42): "Carthage",
(59, 41): "Carthage",
(59, 40): "Carthage",
(60, 40): "Tripoli",
(61, 40): "Tripoli",
(62, 40): "Tripoli",
(96, 39): "Calcutta"
}



#searches in the dictionary to get a name for a key tile
def nameNorm(pCity):
   coord = (pCity.getX(), pCity.getY())
   if coord in cityNameDict:
      name = cityNameDict[coord]
      pCity.setName(name, True)
      return True
   else:
      return False

#spawns the Roman civ on the map
def romeSpawn():
	getTurnYear(iGameTurn)
	if year == -3970:
		PyHelpers.PyPlayer(eRome).initUnit(eSettler, 61, 46)
 
What is the string module good for? Right now my application is importing it and using it, but I realize I don't really need it. The string class methods makes it a pretty superfluous module, not?

The string module was around before Python introduce classes and objects. Originally a string was just another type like int or boolean. It didn't have any methods on it that you could call so you'd have to use the module's functions to operate on string data.

At this time there may be functions in the string module that aren't also methods on the class, but I haven't needed any yet. Wait, not true. I do remember importing the string module in one place in BUG, but I don't recall where.

@antaine - You're close about those integer values. The key is that when you pass a number to gc.getPlayer(), it knows you want a player from the function name. So it looks for the xth player in the list. When you call gc.getCivilizationInfo(), it looks in a different list--the list of civilization info objects.
 
Back
Top Bottom