trying to use a minimalist mod to do big things

That error means that the line "if bEnglandTriggered == True:" is being executed before the line that initializes it to True in the block you posted above. Make sure these variables are in the correct scope, i.e. they are global variables.

The CyEngine class is what allows you to place and remove signs on the map. Check the API. Here's what I would have done first: open the API, hit CTRL + F (for find), and typed "sign". This would have shown you the methods you need. When you have no idea what to do, don't stop! Think about what a function might be called and do a quick find.
 
ah, yes, I did find that about the signs.

So, I shouldn't be defining those Triggered things up in the constants, but after the def line? How will it not reset every time python is reinitialized?
 
Huh?

The onTechAcquired event handler does know what the tech being acquired is and who is acquiring it.
Code:
	def onTechAcquired(self, argsList):
		'Tech Acquired'
		iTechType, iTeam, iPlayer, bAnnounce = argsList
So after unpacking the argsList iTechType is the tech, iTeam and iPlayer are the team and player, and bAnnounce is something you probably don't need to worry about.
 
Ok, signs... I'm not sure you can do these with Python. You might be able to set them manually in the WBS and erase them with Python though... Like CyPlot.erase() but that would also erase terrain features and rivers!

You can set the missionary type with:
Code:
eReligion =  pPlayer.getStateReligion()
if eReligion > -1:
    eUnitType = 17 + eReligion
    spawnUnit(pPlayer, eUnitType, iX, iY)
(I'm using the spawnUnit() helper function I suggested earlier, but you can of course do the copy-paste thing instead. :rolleyes:)

"Referenced before assignment" means just that. Did you really include the constants at module level? (At the beginning of the file.) Just for kicks, add the assignment before the condition:
Code:
def colonySpawn():
        [COLOR="Red"]pNewYork = cyMap.plot(30, 47)
        pPhiladelphia = cyMap.plot(28, 46)
        pBoston = cyMap.plot(31, 48)[/COLOR]
#Arabs
		if gc.getTeam(11).isHasTech(42):
				[B]bArabiaTriggered = True[/B]
				if bArabiaTriggered:
						bArabiaTriggered = False
This should trigger the event every time, so you need to put it outside the function:
Code:
[B]bArabiaTriggered = True[/B]
...
def colonySpawn():
#Arabs
		if gc.getTeam(11).isHasTech(42):
				if bArabiaTriggered:
						bArabiaTriggered = False
I also got rid of the assignment statements (marked read above) you've clearly copy-pasted from somewhere else... :rolleyes: Trust me, they do nothing for you, so you don't need to include them just because you've used them before. Are they good luck charms, perhaps? You should basically not do something unless you know why your doing it, because chances are that you're doing something wrong.

Other than that, you could either rename the boolean variable to bArabiaNotTriggered or use a not command in the if statement:
Code:
if not bArabiaTriggered:
Because the way you're doing it now is somewhat confusing.
 
You can definitely create and remove signs from Python. Check out the "Event Signs" mod for code that does this for random events that modify tile yields.
 
you're telling me! Yes, I am confused.

Also, the assignment statements are used for some direct city spawns and renames like

pNewYork.getPlotCity().setName("New York", 1)


so I have to do the assignment first.


For the signs, I saw things referring to signs and to landmarks; I just wasn't sure which was the thing I was thinking of.
 
I don't understand why defining bTriggered anywhere in DawnOfTime.py won't be reset to true when python reloads when loading a saved game. I would think that it reads all those assignments every time it reads that file...(not that I doubt you, I just don't understand why it knows not to do that)
 
alright, did it again just to be sure. putting bEnglandTriggered = True in the top of the file and

Spoiler :
Code:
def colonySpawn():
#English or Celts
		if gc.getTeam(12).isHasTech(42):
				if bEnglandTriggered:
						bEnglandTriggered = False
						for tCoords in getAdjacentList((30, 47)):
								x, y = tCoords
								pCurrentPlot = cyMap.plot(x, y)
								if pCurrentPlot.isCity():
										pCity = pCurrentPlot.getPlotCity()
										pCity.kill()
						for tCoords in getAdjacentList((28, 46)):
								x, y = tCoords
								pCurrentPlot = cyMap.plot(x, y)
								if pCurrentPlot.isCity():
										pCity = pCurrentPlot.getPlotCity()
										pCity.kill()
						for tCoords in getAdjacentList((31, 48)):
								x, y = tCoords
								pCurrentPlot = cyMap.plot(x, y)
								if pCurrentPlot.isCity():
										pCity = pCurrentPlot.getPlotCity()
										pCity.kill()
						PyPlayer(12).initCity(30, 47)
						pNewYork.getPlotCity().setName("New York", 1)
						PyPlayer(12).initUnit(5, 30, 47, 2)
						PyPlayer(12).initUnit(47, 30, 47, 5)
						PyPlayer(12).initCity(28, 46)
						pPhiladelphia.getPlotCity().setName("Philadelphia", 1)
						PyPlayer(12).initUnit(5, 28, 46, 2)
						PyPlayer(12).initUnit(47, 28, 46, 5)
						PyPlayer(12).initCity(31, 48)
						pBoston.getPlotCity().setName("Boston", 1)
						PyPlayer(12).initUnit(5, 31, 48, 2)
						PyPlayer(12).initUnit(47, 31, 48, 5)
						PyPlayer(12).initUnit(4, 28, 51, 1)
						PyPlayer(12).initUnit(5, 28, 51, 2)
						PyPlayer(12).initUnit(47, 28, 51, 5)
						PyPlayer(12).initUnit(4, 64, 12, 1)
						PyPlayer(12).initUnit(5, 64, 12, 2)
						PyPlayer(12).initUnit(47, 64, 12, 5)
						PyPlayer(12).initUnit(4, 118, 13, 1)
						PyPlayer(12).initUnit(5, 118, 13, 2)
						PyPlayer(12).initUnit(47, 118, 13, 5)
						if gc.getPlayer(12).getStateReligion() == 0:
								PyPlayer(12).initUnit(17, 30, 47, 1)
								PyPlayer(12).initUnit(17, 28, 46, 1)
								PyPlayer(12).initUnit(17, 31, 48, 1)
								PyPlayer(12).initUnit(17, 28, 51, 1)
								PyPlayer(12).initUnit(17, 64, 12, 1)
								PyPlayer(12).initUnit(17, 118, 13, 1)
						if gc.getPlayer(12).getStateReligion() == 1:
								PyPlayer(12).initUnit(18, 30, 47, 1)
								PyPlayer(12).initUnit(18, 28, 46, 1)
								PyPlayer(12).initUnit(18, 31, 48, 1)
								PyPlayer(12).initUnit(18, 28, 51, 1)
								PyPlayer(12).initUnit(18, 64, 12, 1)
								PyPlayer(12).initUnit(18, 118, 13, 1)
						if gc.getPlayer(12).getStateReligion() == 2:
								PyPlayer(12).initUnit(19, 30, 47, 1)
								PyPlayer(12).initUnit(19, 28, 46, 1)
								PyPlayer(12).initUnit(19, 31, 48, 1)
								PyPlayer(12).initUnit(19, 28, 51, 1)
								PyPlayer(12).initUnit(19, 64, 12, 1)
								PyPlayer(12).initUnit(19, 118, 13, 1)
						if gc.getPlayer(12).getStateReligion() == 3:
								PyPlayer(12).initUnit(20, 30, 47, 1)
								PyPlayer(12).initUnit(20, 28, 46, 1)
								PyPlayer(12).initUnit(20, 31, 48, 1)
								PyPlayer(12).initUnit(20, 28, 51, 1)
								PyPlayer(12).initUnit(20, 64, 12, 1)
								PyPlayer(12).initUnit(20, 118, 13, 1)
						if gc.getPlayer(12).getStateReligion() == 4:
								PyPlayer(12).initUnit(21, 30, 47, 1)
								PyPlayer(12).initUnit(21, 28, 46, 1)
								PyPlayer(12).initUnit(21, 31, 48, 1)
								PyPlayer(12).initUnit(21, 28, 51, 1)
								PyPlayer(12).initUnit(21, 64, 12, 1)
								PyPlayer(12).initUnit(21, 118, 13, 1)
						if gc.getPlayer(12).getStateReligion() == 5:
								PyPlayer(12).initUnit(22, 30, 47, 1)
								PyPlayer(12).initUnit(22, 28, 46, 1)
								PyPlayer(12).initUnit(22, 31, 48, 1)
								PyPlayer(12).initUnit(22, 28, 51, 1)
								PyPlayer(12).initUnit(22, 64, 12, 1)
								PyPlayer(12).initUnit(22, 118, 13, 1)
						if gc.getPlayer(12).getStateReligion() == 6:
								PyPlayer(12).initUnit(23, 30, 47, 1)
								PyPlayer(12).initUnit(23, 28, 46, 1)
								PyPlayer(12).initUnit(23, 31, 48, 1)
								PyPlayer(12).initUnit(23, 28, 51, 1)
								PyPlayer(12).initUnit(23, 64, 12, 1)
								PyPlayer(12).initUnit(23, 118, 13, 1)

and the code produces the "referenced before assignment" error. It doesn't argue with me if I put it inside the function definition
 
Huh?

The onTechAcquired event handler does know what the tech being acquired is and who is acquiring it.
Code:
	def onTechAcquired(self, argsList):
		'Tech Acquired'
		iTechType, iTeam, iPlayer, bAnnounce = argsList
So after unpacking the argsList iTechType is the tech, iTeam and iPlayer are the team and player, and bAnnounce is something you probably don't need to worry about.


so why can't I just say

Code:
def colonySpawn():
              if iTechType == 42:
                          if iTeam == 12:

etc?...that way, it'll only trigger when team 12 discovers tech 42 onTechAcquired. Since they can only discover the tech once, I won't need to bother with the triggers and whatnot
 
You know...as I think about it, I may have a more sensible way to go (and not just because it gets me out of this sticky situation).

My original intention in coding this part of the mod was to give certain civs colony seeds on discovering astronomy. But that is inevitable and may make things too easy.

What if I were to give the colonies provided the civ has acquired astronomy by a certain date...let's just say, 1500. That way, if you don't discover it fast enough, you will still get to enjoy your galleons, but you won't be getting any free settlers in interesting places.

I'm thinking as a matter of balace that is a better idea because there is a possiblity of failure.

Thoughts?
 
Because "iTechType" is not defined anywhere in your colonySpawn example. Ditto for iTeam.

However, if you call colonySpawn from onTechAcquired like "colonySpawn(iTechType, iTeam)" and define colonySpawn like "def colonySpawn(iTechType, iTeam):" then it would work because the values would be passed on to the colonySpawn function. Values don't just propagate to new functions wily-nilly, they have to be specifically passed to them. Well, that is unless they are global variables but generally you just pass them via the function arguments (it's better that way, really).
 
ah, I wasn't sure just what would import.

I want to understand how to get a one-time trigger code to work for my own edification, but the more I ruminate on it, the more I like the idea of giving a deadline so success would not be guaranteed.
 
antaine, I fear that you're going too fast with this programming thing. This means that you probably end up wasting full work-days worth of time on aimless debugging, trying to do the most elementary things. You really haven't figured out the name space thing and variable assignment yet - this fact should be obvious to anyone reading this thread. And its good that you ask these questions, because there really is no mystery here, nor should there be. You just have to connect the dots and then it will be smooth sailing all the way (at least as far as variable assignment is concerned).

You could probably revisit the textbook in this matter first, and then ask the rest of us what you still don't understand. Otherwise we might be giving you answers to questions you haven't asked, so they wouldn't mean anything to you.

But as an example, if you have this line in your script:
Code:
yourMomma
Well, clearly this is cause for an exception, right? :rolleyes: Because, the Python interpreter wouldn't recognize this name. There is nothing in your module - or in any other module you're importing - like CvPythonExtensions - defining this name. Also, there is no command or built-in function in Python itself that corresponds to this name. So the name is unknown. The interpreter will complain about you using an unknown name.

Now, you could define the name yourself, for example as a variable:
Code:
yourMomma = True
Then, if you wanna access the value assigned to the name - after assignment - you can go:
Code:
print yourMomma
No exception. Instead the word "True" will be printed into the Python Debug Log. But it won't work if you put the print command first and define the name after that line.

The name could be a function:
Code:
def yourMomma():
    pass
This function does absolutely nothing, but its still valid:
Code:
print yourMomma()
This would print only the word None into the debug log, since all functions with no return statement return None as default.

Note that both of these definitions of the name (the variable assignment and the function definition respectively) are done at module level - the initial level. We know this because the line defining the name has no indentation what-so-ever. This means that the name will be available in the entire module - in all functions, in all classes and in all class methods.

This is why you could have this script:
Code:
yourMomma = True

def checkMomma():
    if yourMomma:
        return "Yes"
    else:
        return "No"
You can access the name yourMomma inside the function checkMomma because its a global variable. This because it is defined on the module (or __main__) level. This will however work the same:
Code:
def checkMomma():
    yourMomma = True
    if yourMomma:
        return "Yes"
    else:
        return "No"
It works because the name yourMomma has been defined before it is accessed. (Assignment first, access thereafter.) But the name isn't available in any other function:
Code:
def checkMomma():
    yourMomma = True
    if yourMomma:
        return "Yes"
    else:
        return "No"

def isMomma():
    return yourMomma
When you run the isMomma() function it causes the exception that you're far too familiar with. :p Because the name yourMomma isn't available inside the isMomma() function - it belongs solely to the checkMomma() function. So the correct way to assign the variable is therefore:
Code:
yourMomma = True

def checkMomma():
    if yourMomma:
        return "Yes"
    else:
        return "No"

def isMomma():
    return yourMomma
Now both functions recognize the name - and can therefore access the value it refers to.

Also, about tuple assignment; You're currently using this:
Code:
x, y = tCoords
If we replace the tCoords variable with an actual tuple it might look like this:
Code:
x, y = (34, 45)
This is sort of like a shortcut, and what this assignment statement does is it automatically unpacks the values inside the tuple data structure. And on doing so assigns the values to the specified variables. But you've also encountered this:
iTechType, iTeam, iPlayer, bAnnounce = argsList
No real mystery here either - argsList is actually a tuple (not a list) and it only works because this particular tuple contains four values. (The argsList tuple can contain any number of values, depending on which game event has prompted the Python callup via the Event Manager.) This is what really could be happening if we unveil the argsList variable:
Code:
iTechType, iTeam, iPlayer, bAnnounce = (23, 12, 5, True)
So it would be the same as doing this:
Code:
iTechType = 23
iTeam = 12
iPlayer = 5
bAnnounce = True
Another way of assigning the values from argsList would be:
Code:
iTechType = argsList[0]
iTeam = argsList[1]
iPlayer = argsList[2]
bAnnounce = argsList[3]
Its not that its difficult to do, but its less convenient. This is why tuple assignment is a useful "shortcut".

Another thing you're confused about is how function calls (and method invocations) work. More specifically how values are passed on. Since we've all already tried your best at explaining this, to no avail, I suggest you try reading up on it before we start repeating what we've already said. Then ask what is still unclear and we should be able to explain everything so that there is no more confusion what-so-ever on this subject anymore.

Well, my suggestion anyway. You're doing great but its imperative that you also understand what you're doing.
 
Alright, that made sense. Yes, I will have to return to the "book."

I think part of my problem is also that I'm working on these things in snippets - fifteen minutes here, 20 minutes there - so I can lose continuity in what I'm doing. Basically, I forget what I was doing/reading.

on another note, I've decided that it is ultimately too cheesy to simply hand the human player free colonies just for staying alive.

This is basically what I've got (from the ReadMe):
"Some civs get very lucrative colonies. When played by a human, they must meet certain requirements in order to collect their prize.
Celts and Greeks must discover Metal Casting by 1600BC (Celts can also get the English colony spawn if they meet certain requirements, see below)
Germany must discover Feudalism and Guilds by 1200.
England, France, Spain, Arabia, and Russia must discover Astronomy and Gunpowder by 1400.
America must discover Railroad by 1850."

The AI player will get their goodies automatically (without meeting the requirements), but a human player can still keep cities in the flip zones (for the ones that involve flip zones) by keeping them happy. America's "colony," for instance, is "Manifest Destiny" and flips the area west of the Mississippi.

The Celts and Greeks have lower requirements for the colonies because they are less "useful" - really just designed to keep the AI in the game a little longer since it's often too "dumb" to stick a city on an island when the Persian horde is charging over the Bosporous.

here's the readme:
Spoiler :
PURPOSE OF THIS MOD AND THINGS YOU NEED TO KNOW:

PURPOSE:
The purpose of Dawn of Time is to provide a 4000BC start scenario with timed events which will roughly approximate the historical timeline while maintaining full multiplayer capability. Those of you who enjoy Rhye's and Fall of Civilization will be familiar with the overall vision behind the creation of this mod.

Because alterations to the .dll and random events (including plague, congresses, and possibly even stability) cause OOS errors when attempting multiplayer, the human player will not have any choice regarding new civ spawns, nor will there be an autoplay.

NEW CITY MINIMUM DISTANCE:
Cities can be built one tile away from each other instead of the default two tiles. Evaluate new city sites carefully, however, as just because you can, doesn't mean you should...

CITY NAMES:
There are a number of "key tiles" with geographically static names. A human player can change these names, but when a city is settled on a key tile the "proper" name will come up as a default. There is no dynamic naming scheme like in RFC; all the civs use the same list of names, and there is only one name per tile (although some important names are assigned to a bloc of adjacent tiles).

NO AUTOPLAY:
All civs are enabled from the start, but only seven of them will be playable from 4000BC. The human player will have to <return> through all the turns until it is his civ's turn to spawn if he wishes to play a "later" civ. When an area flips to a new civ, the old civs that have possessions there will lose their cities, but their units will be moved to the edges of the new territory. If you are playing on Marathon, it could take awhile to get to your civ's spawn date.

THE STARTING CIVS:
The civilizations available to those who wish to play from the 4000BC start are
Celts
Egypt
Mesopotamia
India
China
Hebrews
Ethiopia

WHY THE CELTS, HEBREWS AND GREEKS?:
Despite their disunity and decentralization, the Celts have been included because they blanketed the continent from an early time and left linguistic and cultural marks on the modern societies which inhabit those regions. Their true origins and migrations are obscured by the shroud of time. Historians usually fix the beginning of a culture that can be called "Celtic" at about 800 BC, but the recent Genographic Project indicates that genetic evidence shows populations in the traditional heart of Celtic influence (western Ireland and northwestern Spain) show evidence of having been in place since the end of the ice age. The discrepancy between the haplotype evidence and the historical record can be explained in part by the illiterate nature of the people in question (they left no written records to attest to their language) and tribal social structure (they had no great empire or unified kingdom). They have also been started at this time to give the Celtic player some time to colonize western Europe before almost all their territory comes under threat from spawning civs.

The Hebrews have been included both to give the human player a bit more of a fighting chance (their geography doesn't give them much space) and so that Judaism spreads properly (as it is one of the earliest religions discovered in the game). For that reason, they start on the map earlier than their historically accepted entrance into Canaan about 1300BC.

The Greeks have been included from the start somewhat ahistorically as well. This is because they were a growing civilization from the time of agriculture and need time for a human or AI player to expand before the spawn of the Romans. Like the Celts, they will see a good part of their territory carved up by future civs, so an earlier start gives them a fighting chance.

TIMED SPAWNS AND KEEPING YOUR CITIES:
The other civs begin with a workboat as a place holder so that a human player can select them in single player mode. They will spawn in their historic locations at the appropriate time. It is important to note that cities on or adjacent to the later civs' starting locations will be razed regardless of their happiness level. A larger spawn zone has been defined for each later civ. Cities with more than 1/3 unhappiness in that zone will flip to the new civ. If you wish to keep your cities, keep them happy! The later civs are:
Rome
Persia
Japan
Arabia
England
France
Spain
Germany
Russia
America

Some civs have secondary spawns in the 19th and 20th century, so don't be surprised if a pesky AI civ you'd dispatched centuries before reemerges. These secondary spawns will not claim any cities by fiat (like the original spawn capitals), but will use only the happiness gauge to determine if they get any cities and thus are successful in making it back onto the map. Each secondary spawn will only be attempted once, and you will see a message telling you that nationalists are trying to reclaim their country. If they fail to gain a city, the revolution will be over and they will be gone for good.

COLONIES:
Some civs get very lucrative colonies. When played by a human, they must meet certain requirements in order to collect their prize.
Celts and Greeks must discover Metal Casting by 1600BC (Celts can also get the English colony spawn if they meet certain requirements, see below)
Germany must discover Feudalism and Guilds by 1200.
England, France, Spain, Arabia, and Russia must discover Astronomy and Gunpowder by 1400.
America must discover Railroad by 1850.

The check for the satisfaction of the requirements takes place on the specified year, not on discovery of the tech. So if you manage to discover Astronomy and Gunpowder by 1342, don't panic when nothing happens. When 1400 rolls around you'll get the colony spawn.

RELIGIONS:
Anyone can discover a religion by researching the appropriate tech, but the holy city will appear in its historical location. Each religion seeks out one particular city for this. If that city is razed, the holy city status will again appear if that city is rebuilt. This works to ensure a sensible and historical dissemination of religions. If a religion is discovered at a time the intended holy city does not exist, the game will follow the normal rules for assigning a holy city, but that status will move if and when the historical holy city is founded.

NEW CELTIC BUILDING:
Because the Celts will see their territory greatly reduced through new civ spawns (unless they can keep everyone VERY happy), they will likely find themselves with a capital in Ireland or Britain at some point. Since they will be hugging the Atlantic coast (at least until they can get Astronomy and go elsewhere), they have been given a new special building - the Curragh Easeway. That building replaces the harbor and permits water and river tiles to give one hammer in addition to their usual yield.

NEW CELTIC FLAG:
As Ireland is to date the only fully independent Celtic country, the harp insignia has been used for the Celts. This is consistent with many other civs which use later imperial motifs (like the Russians, Germans, French, and others) even from their earliest eras.

HEBREWS:
The Hebrew civ is based on the Sumerian one that came with the game. They have been given a new flag, and the unique building and unit for Sumeria have been renamed for use with the Hebrews. The Hebrew Sicarius is virtually identical to the Sumerian Vulture save that the unit is also invisible. The rest of the artwork and tags have not been changed, so you may still see a Sumerian thumbnail or reference here or there.

THESE ISLANDS AREN'T BIG ENOUGH FOR THE TWO OF US:
Because the British Isles are so small, players will likely find that they can only support one major civ (there are no minor civs in DawnOfTime). Assuming that the two in competition for the space will be the Celts and the English, it is likely that one will force the other out - possibly to extinction. There is a chance here for the Celts to rewrite a little history. If the English are driven extinct, but the Celts are still alive, the Celts will inherit the British Empire (provided they can also discover Astronomy and Gunpowder by 1400). In inheriting the British Empire, the Celts will get the normal British colony spawns when they meet the requirements.

CREDITS:
On the Civfanatics Forum, Baldyr, God-Emperor, and EmperorFool were of tremendous help. The flag for Wildcat's Israel mod was used to add flavor to the Hebrew civ.
 
Scenario design wise its looking mighty good, I thinks. :goodjob:
 
yup, at this point I really just need to add the signs ("Caption = " in the WBS) so that the killboxes can be avoided - provided those coding suggestions really do allow me to remove them with python, and to kill the workboats and reconceal the tiles at the appropriate time(s).

Once I do that, I'll post the whole mod file up here for y'all to peruse...
 
No, you cannot write to a global variable without using the global keyword to tell Python what you mean.

Code:
x = 5

def setX():
    x = 10
    print x

def printX():
    print x

setX()
printX()

This prints

Code:
10
5

because setX() writes to a local variable. To fix it you must use

Code:
def setX():
    global x
    x = 10
    print x

I think the problem you're having is that if you read from the global variable without using the global keyword, Python is fine with it, but then when you try to write to it, that causes an error as a way to help you notice that you're doing something you don't want to do.

Tip: Always use "global" keyword to access global variables that you must modify in the function before you access them in any way.

Pro tip: If you always use the "global" keyword--even when only reading the variable--you'll never cause an error by writing to it later.
 
Signs are visible by one player only. Landmarks are visible by every player. Landmarks are really just signs with ePlayer set to -1 (PlayerTypes.NO_PLAYER).
 
ah, then that's the difference...it's landmarks that I want to deal with. I can simply add them to the WBS. They should be there from the start, so that's good...I just need the python to remove them later using
VOID removeLandmark (CyPlot pPlot)
void (CyPlot* pPlot)
 
Here goes...v1 release. Everything I've done seems to work (even if it doesn't work as elegantly as it could). Give a looksee and let me know if you think it's good to go up on the mods page.
 
Back
Top Bottom