trying to use a minimalist mod to do big things

Ok, I realize that we have managed to confuse you. This i what you do:

1. Launch the game and open up the Python console (should be shift + ~).
2. Type this assignment statement into the console:
Code:
eSettler = gc.getInfoTypeForString("UNIT_SETTLER")
3. Open up your WBS file and count which player is associated with the Roman civilization. Remember to count from zero and up.
4. Next line - replace the <integer> tag with the index number of the player in question:
Code:
eRome = <integer>
5. Import PyHelpers:
Code:
import PyHelpers
6. Spawn the unit:
Code:
PyHelpers.PyPlayer(eRome).initUnit(eSettler, 61, 46)
7. Cork up the champagne.

edit: Since you seem to already have valid IDs for the player and the unit type, you can basically skip steps 2-4 and use the integer values instead of the variables in step 6. You still get to get drunk on step 7 though.
 
alright, this loads up with no errors. Now I just have to put a trigger in CvEventManager so I can watch it happen...
I can't even see how this would load at all, since your indentation is off... Also, you need to differentiate between regular functions (like those you're defining in your module) and class methods.

Basically all the method in the API are class methods, so you need a class instance to invoke them on. And you do this with dot notation. I've already covered this a few paged ago, so I suggest you revisit that post.

This makes no sense:
Code:
	getTurnYear(iGameTurn)
	if year == -3970:
What is the name iGameTurn referencing? Where was it defined? The same with year - you forgot to assign the returned value of the getTurnYear() method to the variable.

Also, you need a CyGame instance to use getTurnYear() with. And you already have one such instance referenced within your module. Its actually the constant cyGame that you included at the top - and forgot about.

Can you figure out how to fix this? (I still think you should prototype methods in the console first, and only add them to the module only once you know how to use them.)
 
ha...I just got that error...it doesn't trigger a message until it's actually called on (I meant the mod loaded without giving a message like I had been getting with errors there). I was trying to use what I read in the API.

Let me take a look and figure out the fixing...
 
Basically, whenever you find something useful in the API you need to figure out these facts:

1. What class does this method belong to? This should be pretty obvious as all the methods are organized by class. Then its a matter of getting hold of an instance of that class. The CyGlobalContext, CyGame and CyMap classes can be created on a fly, but you probably already have constants in your module for each of these. These three classes are very useful for finding other class instances. (CyGlobalContext is basically only used for this, so you should be using it extensively.)

2. What does this method return? This is the first thing displayed on any method entry in the API. If its green and a valid class name (starting with the Cy prefix) then its a class instance. Otherwise it states what type of value it returns (integer, float, boolean, string) or VOID (basically None) The VOID methods are actually the useful ones, as they aren't used for fetching any return value - but rather to manipulate whatever class instance it is they are invoked on.

3. What are the parameters required for use with this method? Each parameter has two definitions: The first one is the type (integer, string, class instance, et cetera) and the next one is the parameter name (basically a variable name). The variable names - together with the method's name - should most of the time give a hint of how the method is used. But sometimes they simply don't make any sense. Test it in the console?
 
Alright, so I did take a look to see how things are formatted that involve something happening on a specific year. In the event manager I found

Code:
	def onGameStart(self, argsList):
		'Called at the start of the game'
		if (gc.getGame().getGameTurnYear() == gc.getDefineINT("START_YEAR") and not gc.getGame().isOption(GameOptionTypes.GAMEOPTION_ADVANCED_START)):
			for iPlayer in range(gc.getMAX_PLAYERS()):
				player = gc.getPlayer(iPlayer)
				if (player.isAlive() and player.isHuman()):
					popupInfo = CyPopupInfo()
					popupInfo.setButtonPopupType(ButtonPopupTypes.BUTTONPOPUP_PYTHON_SCREEN)
					popupInfo.setText(u"showDawnOfMan")
					popupInfo.addPopup(iPlayer)
		else:
			CyInterface().setSoundSelectionReady(true)

now, I could see that this is the popup that gives you that dawn of man info about being nomads and settling down. I found what I was looking for here:
if (gc.getGame().getGameTurnYear() == gc.getDefineINT("START_YEAR") and not gc.getGame().isOption(GameOptionTypes.GAMEOPTION_ADVANCED_START)):

I can see that the if statement calls on the game, then in that finds the game year. it looks to see if the start year is the current year as long as the advanced start option is off. I could now see what it was doing (I don't know if you're considering that cheating or not, but I'm an example-oriented kinda guy).

I then wrote
Code:
#spawns the Roman civ on the map
def romeSpawn():
	if (gc.getGame().getGameTurnYear() == -3985)):
		PyHelpers.PyPlayer(eRome).initUnit(eSettler, 61, 46)

now that it can get past the year issue, the game gives me an error popup:

AttributeError: 'NoneType' object has no attribute 'initUnit'

I know I'm still missing something.
 
Compare the import statements for PyHelpers. They don't match.

This is what I suggested you use with the console:
Code:
import PyHelpers
And this is what is found in your module:
Code:
from PyHelpers import *
Which isn't really the same thing.

If you wanna go with the current import statement (making all of the constants, classes and methods in the PyHelpers module part of your modules name space) then you need to delete the reference to the PyHelpers module:
Code:
		[COLOR="Red"]PyHelpers.[/COLOR]PyPlayer(eRome).initUnit(eSettler, 61, 46)
(Note that you also need to adjust any other code that is referencing PyHelpers or its contents.)

But you could go with the other way of importing PyHelpers and keep the current code intact. Its basically up to you.
 
on a moderately related note...what if I tied the civ spawn blocs to the ages instead of to years? When someone discovers a tech that advances them to the classical era, all the classical civs spawn. the same with the medieval and finally the americans with the renaissance. this way, speed won't matter and civs with preloaded techs will appear when the civs on the map start crossing into that era.
 
on a moderately related note...what if I tied the civ spawn blocs to the ages instead of to years? When someone discovers a tech that advances them to the classical era, all the classical civs spawn. the same with the medieval and finally the americans with the renaissance. this way, speed won't matter and civs with preloaded techs will appear when the civs on the map start crossing into that era.
There are separate ages for every single player/team (depending on techs) and a global game age. I don't know how this would affect your mod, but the method for checking the game's era is CyGame.getCurretEra() - look it up!

No need to check whether or not anyone has discovered a tech - but this is your other option. Because there is a onTechAquired() call - you could either look for specific trigger Techs - or you could check whether or not the player/team receiving the tech is in the requested era.

Either way you need to have some way of preventing the spawn code from firing more than once. One possible solution would be to create an array of some sort that keeps track of which Civs have spawned - and not. But any such data should be stored in a way that it is saved along side with the game. Because otherwise these values will be set to the defaults once the game is reloaded...

There are ways to do all these things, though. Like each of the major Cy classes has a field called scriptData that can be set to any string value with setScriptData(). Because the string is now part of an attribute of the class, it will also be saved alongside with it.

So you would store a "yes" or "no" value for each CyPlayer instance with setScriptData(). Then, any time the code needs to check whether or not the player has spawned, it fetches that string value with CyPlayer.getScriptData(). Not very complicated at all, once you wrap your head around it...
 
well, I was thinking it could be done on tech acquired, as I currently plan to do most of the colony spawns (when england acquires astronomy, they'll get three colony seeds in specific locations, for instance).

hm...maybe year is best for this stuff as I've almost got it working (it was arguing with my syntax before dinner)...alright...back to work on it...
 
Alright, I'm a little lost

in trying all the combinations of import and phrasing that line you suggested, I still get that NoneType error message...

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

from CvPythonExtensions import *
from Popup import PyPopup
from CvEventManager import *
from PyHelpers 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",
(47, 61): "Reykjavik",
(47, 62): "Reykjavik",
(48, 62): "Reykjavik",
(46, 63): "Reykjavik",
(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():
	if (gc.getGame().getGameTurnYear() == -3985):
		PyPlayer(eRome).initUnit(eSettler, 61, 46)
 
So, figuring that the NoneType error had to do with not defining all the fields in the function from the API:

CyUnit initUnit (UnitType iIndex, INT iX, INT iY, UnitAIType eUnitAI, DirectionType eFacingDirection)
CyUnit* initUnit(UnitTypes iIndex, plotX, plotY, UnitAITypes iIndex) - place Unit at X,Y NOTE: Always use UnitAITypes.NO_UNITAI

I went ahead and assigned values for ai and direction, and input the following
Code:
#spawns the Roman civ on the map
def romeSpawn():
	if (gc.getGame().getGameTurnYear() == -3985):
		PyPlayer(eRome).initUnit(eSettler, 61, 46, -1, -1)

however, it now makes an error saying "TypeError: initUnit() takes at most 5 arguments (6 given)"

now...I've got (eSettler, 61, 46, -1, -1) the unit type (1), the x coord (2), the y coord (3), the ai type (4), the direction (5)...where are they getting 6 from? I thought maybe the command for the civ owner was counted, but if I take any out of the parentheses I get the NoneType error again.
 
PyPlayer.initUnit() doesn't match CyPlayer.initUnit() exactly. It probably leaves off the facing direction so remove one of the -1 parameters. BTW, I'm not sure if it will except -1 (UNITAI_NO_UNITAI), but you'll find out soon enough.
 
if i drop the direction i get the Nonetype error...i'll try it dropping that and giving a different ai (the api said to use noai)
 
nope...changing the AI and eliminating the direction brings up the same "NoneType" error I saw before...somehow, they all have to be there, but if they are it thinks there's too many but if I remove one it thinks there's too few...
 
yup...I even went in and eliminated all the e references and just used straight integers, double checking them both against the API
 
You have eRome = 28, which is the value of CIVILIZATION_ROME.
That is not what the PyPlayer takes as its argument. It takes the player ID which would be a number not larger than 17 for non-barbarians (the barbarians are player 18), well that is the case if you are not using a DLL that is set up to allow more than 18 players.
 
Technically, no. It wants the player number, not the team number. On the other hand, if no two players are on the same team then the player number probably works out to match the team number.

In general, you'd count through the player entries in the WBS file with the first one being player 0, the second being player 1, etc. But if each team has only 1 player you'll probably see that the team numbers match the player number if you haven't been fiddling with who's who.
 
well, they're labeled there, so that was easy. I changed rome to be the int value from the WBS file (7) and I still get the same error about only taking 5 arguments but me providing 6...
 
Back
Top Bottom