Simple Python addition problem

Le Sage

Emperor
Joined
Apr 24, 2009
Messages
1,522
Location
Digging up your garden
I am trying to add this to my game. It's a simple Python addition that makes it so that when a specific unit is killed, the player is supposed to win the game.

Spoiler :
def onUnitKilled(self, argsList):
'Unit Killed'
unit, iAttacker = argsList
player = PyPlayer(unit.getOwner())
attacker = PyPlayer(iAttacker)
###new code start
if unit.getType() == gc.getInfoTypeForString("UNIT_BAD_GUY"):
CyGame().setWinner(gc.getPlayer(iAttacker).getTeam(), gc.getInfoTypeForString("VICTORY_BADDYKILL"))
###new code end
if (not self.__LOG_UNITKILLED):
return
CvUtil.pyPrint('Player %d Civilization %s Unit %s was killed by Player %d'
%(player.getID(), player.getCivilizationName(), PyInfo.UnitInfo(unit.getUnitType()).getDescription(), attacker.getID()))


Now, It seems it conflicts somehow with BUG, for some reason or other. Mostly when fiddling with it, I get no user Interface at all, and I get an BugEventManager error (referring to killing units; unit kill event handler, or something like that) when the Interface actually appears and I am to test it out. Can anyone tell me what to do? I have no knowledge in Python; I am just trying to merge this simple function with my mod.
 
Look at RevDCM's movie mod, just follow that as a template (use notepad++'s search in file function to find all instances of MovieMod in the Assets/Config folder and and Assets/Python -including the python directorie's subfolders). That's the simplest way to do it really, will make a lot more sense then trying to explain it. RevDCM is just a 10MB download, I can't think of a simpler modularlized python component to use as a template.

Also whenever you post code wrap the code inside [code]...codeFoo...[/code] tags so that whitespace is maintained and the code is readable.
 
Oh yeah. I've never posted code before. I'll check out RevDCM. I have it downloaded since a while ago too.

Thanks.
 
The tutorial in my sig will walk you through the necessary steps. Using the sample code as a guide you should be able to get this event working. The sections you will need to follow closely are Configuration and Events. Give it a shot and ask questions here when you get stuck to get help. Make sure to enable logging; the instructions are linked from the tutorial and are on the Troubleshooting page in my sig.
 
Thank you kindly. I'm sitting here right now and working on it. I have actually gotten as far as making my Python modular within BUG, but there's still something wrong with the code. I'll look at your tutorial as well. Thanks.
 
The main source of information is going to be PythonErr.log when you get the no interface or messages about your event handler having a problem. Create a shortcut to this file and put it in your Start menu or somewhere easily accessible. This will point out the module and line number that is having a problem. Feel free to post this file here for help. If you do, post the full code of your module and XML using [CODE]...[/CODE] tags.

Second will be PythonDbg.log which is where you can see messages from BUG telling you what's happening. It's daunting to read it, so instead use Find to search the file for the name of your module and/or event handler function. BUG will tell you when it is loading your mod's config XML file and registers your event handler.
 
Here I am again. I just had to go watch the Mayday parade. The wife insisted :mischief:

I am following your tutorial, and it feels like I'm actaully making progress. I get the following report in the PythonErr.log you mentioned.

Spoiler :
PHP:
Traceback (most recent call last):
  File "BugConfig", line 110, in unknown_endtag
  File "BugConfig", line 334, in endChild
  File "BugConfig", line 337, in end
  File "BugConfig", line 318, in process
  File "BugConfig", line 565, in handle
  File "BugUtil", line 621, in getFunction
  File "BugUtil", line 608, in lookupFunction
  File "BugUtil", line 600, in lookupModule
  File "<string>", line 35, in load_module
  File "<string>", line 13, in _get_code
  File "Baddykill", line 19
 
SyntaxError: 'return' outside function (Baddykill, line 19)

Here's the stuff also from the PythonDbg:

Spoiler :
PHP:
16:27:45 DEBUG: BugConfig - loading mod file Baddykill
16:27:45 DEBUG: BugInit - loading mod Baddykill...
16:27:45 INFO : BugCore - creating uninitialized mod Baddykill
16:27:45 DEBUG: BugUtil - looking up Baddykill.onUnitKilled
load_module Baddykill

16:27:45 TRACE: BugConfig - failure parsing D:\Program Files\2K Games\Firaxis Games\Sid Meier's Civilization 4 Complete\Beyond the Sword\Mods\Fairy Tale\Assets\Config\Baddykill.xml at line 6
16:27:45 TRACE: 'return' outside function (Baddykill, line 19)
16:27:45 DEBUG: Timer - load mod [Baddykill] took 203 ms
16:27:45 DEBUG: Timer - load mod [init] took 1789 ms
16:27:45 DEBUG: Timer - BUG init [read configs] took 1790 ms
16:27:45 DEBUG: BugInit - calling init functions...

..Meaning there might be something wrong in my Python...?

Spoiler :
PHP:
# Baddykill
# Kill the Bad Guy, win the game!

from CvPythonExtensions import *
import BugUtil

gc = CyGlobalContext()

    def onUnitKilled(self, argsList):
        'Unit Killed'
        unit, iAttacker = argsList
        player = PyPlayer(unit.getOwner())
        attacker = PyPlayer(iAttacker)
###new code start
        if unit.getType() == gc.getInfoTypeForString("UNIT_DEMON_KING"):
            CyGame().setWinner(gc.getPlayer(iAttacker).getTeam(), gc.getInfoTypeForString("VICTORY_CONQUEST"))
###new code end        
        if (not self.__LOG_UNITKILLED):
            return
        CvUtil.pyPrint('Player %d Civilization %s Unit %s was killed by Player %d' 
            %(player.getID(), player.getCivilizationName(), PyInfo.UnitInfo(unit.getUnitType()).getDescription(), attacker.getID()))

To paste everything I've done, here's also the stuff in the XML in the Config...

Spoiler :
PHP:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!--
    Kill the Bad Guy, win the game!
-->
<mod id="Baddykill" module="Baddykill">
    <event type="unitKilled" function="onUnitKilled"/>
</mod>

I'm really sorry to bother you all with this, yet i think someone else than me might learn something from this thread as well. I am a total beginner regarding Python.
 
The last item in the list is telling the cause of the python exception. So find the file Baddykill, and look at line 19. Also if you want, post the code around line 19 in Badykill for us to take a look at here, jsut remember to wrap the code you post in code tags. This updside is that the error being thrown by that file means BUG is loading the Baddykill file.
 
Yeah! It is indeed finally loading the file at least, and that does make it seem like I'm making progress.

This is the code around line 19. Line 19 is just the "return" below _LOG_UNITKILLED. Reallly this code was only pasted into my Baddykill.py straight from what The_J wrote me...

Spoiler :
PHP:
###new code start
        if unit.getType() == gc.getInfoTypeForString("UNIT_DEMON_KING"):
            CyGame().setWinner(gc.getPlayer(iAttacker).getTeam(), gc.getInfoTypeForString("VICTORY_BADDYKILL"))
###new code end        
        if (not self.__LOG_UNITKILLED):
            return
        CvUtil.pyPrint('Player %d Civilization %s Unit %s was killed by Player %d' 
            %(player.getID(), player.getCivilizationName(), PyInfo.UnitInfo(unit.getUnitType()).getDescription(), attacker.getID()))
 
Show the whole function. From where it says "def myFunction(): to line 19. A blank return doesn't seem right in python. You can do this with void functions in C, but all python functions return something; so that just doesn't seem right. I'm not sure though, I'll look that up in google.

It would help to know what this function is trying to do as well. Is it meant to return a boolean value (true/false) and object, like a specific unit, does it just process something, what does it do?

Edit: no that's fine, an empty return in python just means the function will return "NONE". Anyway the error says that the return statement is outside the function, so I think you may have a whitespace issue, it's hard to figure out without more context though. YOu should really not use spaces and instead use tabs for indenting python, as python uses indentation as part of it's syntax, and spaces are easy to mess things up.
 
I will check the entire code and remove all white spaces that might be there...

This is the entire thing I have in the Baddykill.py...

Spoiler :
PHP:
def onUnitKilled(self, argsList):
# Baddykill
# Kill the Bad Guy, win the game!

from CvPythonExtensions import *
import BugUtil

gc = CyGlobalContext()

def onUnitKilled(self, argsList):
	'Unit Killed'
	unit, iAttacker = argsList
	player = PyPlayer(unit.getOwner())
	attacker = PyPlayer(iAttacker)
###new code start
if unit.getType() == gc.getInfoTypeForString("UNIT_DEMON_KING"):
	CyGame().setWinner(gc.getPlayer(iAttacker).getTeam(), gc.getInfoTypeForString("VICTORY_CONQUEST"))
###new code end
if (not self.__LOG_UNITKILLED):
	return
CvUtil.pyPrint('Player %d Civilization %s Unit %s was killed by Player %d' 
	%(player.getID(), player.getCivilizationName(), PyInfo.UnitInfo(unit.getUnitType()).getDescription(), attacker.getID()))
 
I will check the entire code and remove all white spaces that might be there...

You can't just remove it. Python uses indentation as it's syntax. If you have a control statment (an statement with a clause that must be met for the specified code to be executed, for example an if statement or a while loop), python uses indentation to know what's inside the control statement, ie what code should only be used if the controling condition is met.

Code:
y = 0
if(x != 0):
	x = 2
y = x + 2

In the above code y will be 4 if x was not 0 when the control statement was read, otherwise y will be 2.

The whitespace issue can be a real PITA with python for this reason. Anyway it's highly recommended not to use spaces and instead to just use tabs for indentation just because it's alot easier not to mess up.
 
You have two function definitons for the function on unit killed. Remove the first, and blank
Code:
def onUnitKilled(self, argsList):
at the top of the file.

Python doesn't allow function overloading, so you can't define the same function twice, also just having a blank function doesn't make any sense either.

Edit: Also you need to indent all the code, ie the control statements inside the onUnitKilled function.

What you have is this for the onUnitkilled function:
Code:
def onUnitKilled(self, argsList):
    'Unit Killed'
    unit, iAttacker = argsList
    player = PyPlayer(unit.getOwner())
    attacker = PyPlayer(iAttacker)
Then everything moves back outside it, as it's no longer indented inside the function. This in effect makes that return statement return in the midle of nowhere, which doesn't make sense to the python parser, but the code has alot more problems then that; and as I originally suspected these are issues iwth indentation. You need to look at the code and set up proper indentation so that it flows right, ie you need to figure out what belongs to what, and indent it properly.
 
You were right, Phungus. You should have the Red Order of the Imperial Iron Rose for this. I indented the Baddykill.py and now I don't get error reports when the game starts anymore.

However, when I kill the bad guy, I get the following reports in PythonErr. Does anyone know what this is about and how to correct it? It says I need two arguments and I've only given one. Arguments of what and where?

Spoiler :

PHP:
Traceback (most recent call last):
  File "BugEventManager", line 362, in _handleDefaultEvent
TypeError: onUnitKilled() takes exactly 2 arguments (1 given)
 
onUnitKilled is a function, functions often take arguments as ways of passing variables between each other. In C this is much easier to figure out, as they are specifically enumerated. Unfortunately in python you just have "arglist", so it's much harder to figure out if you don't know what the arglist is. So this is unfortunately as far as I might be able to help you. My hunch is that self is the missing argument; try eliminating it from the argument list, and see if that works. If that fails, try looking for a module that references self, see what python files it's importing, and try importing those so that self is defined. I have no idea if that'll work, but that's what I'd try.
 
Yeah! You were right in that. I already deleted one of the selfs. I am now entangled with CyUnit Object has no attribute 'GetType'...
 
That's this: unit.getType()

Try gc.getUnitInfo(unit).getType()

I'm unsure if this is correct, you probably should look at the Civ4 python API now and ensure you are passing the right objects around. There is a difference between a Unit (usually called pUnit, and referenced by CyUnit), a UnitInfo (usually called kUnit, or unitInfo), and the UnitID number, as well as a UnitType. This is really stuff you just need to dig in the PythonAPI yourself to figure out though.
 
Remove the "self" parameter from onUnitKilled() since this function is no longer sitting inside a class definition. You don't need to understand the second half of that sentence, just the first. ;)

Edit: Left the thread open too long before replying. :)
 
I just tried Phungus' tip here above, but that didn't work either, sadly. I also removed the self (!). So this is what I have right now.


Spoiler :
PHP:
# Baddykill
# Kill the Bad Guy, win the game!

from CvPythonExtensions import *
import BugUtil
import PyHelpers

gc = CyGlobalContext()
PyPlayer = PyHelpers.PyPlayer

def onUnitKilled(argsList):
	'Unit Killed'
	unit, iAttacker = argsList
	player = PyPlayer(unit.getOwner())
	attacker = PyPlayer(iAttacker)
###new code start
	if unit.getType() == gc.getInfoTypeForString("UNIT_DEMON_KING"):
		CyGame().setWinner(gc.getPlayer(iAttacker).getTeam(), gc.getInfoTypeForString("VICTORY_CONQUEST"))
###new code end
	if (not self.__LOG_UNITKILLED):
		return
	CvUtil.pyPrint('Player %d Civilization %s Unit %s was killed by Player %d'
		%(player.getID(), player.getCivilizationName(), PyInfo.UnitInfo(unit.getUnitType()).getDescription(), attacker.getID()))

Which is generating the following in the PythonErr.log:

Spoiler :
PHP:
Traceback (most recent call last):
  File "BugEventManager", line 362, in _handleDefaultEvent
  File "Baddykill", line 17, in onUnitKilled
AttributeError: 'CyUnit' object has no attribute 'getType'
 
Top Bottom