BUG doesn't register a callback (but loads related events)

The_J

Say No 2 Net Validations
Administrator
Supporter
Joined
Oct 22, 2008
Messages
39,570
Location
DE/NL/FR
I have at the moment a problem with BUG, and I can't figure out why.
I hope someone here can help me with that :).

The problem is in this thread.
It's a simple modification, which should make that a world wonder is required for a project.
The whole thing works in BtS, but doesn't in BUG.


The related XML is named ManhattanRequiresPentagon.xml, with the content:
PHP:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!--
    makes the manhattan project require the pentagon
-->
<mod id="ManhattanRequiresPentagon" module="ManhattanRequiresPentagon">
    <event type="cityRazed" function="onCityRazed"/>
    <event type="cityAcquiredAndKept" function="onCityAcquiredAndKept"/>
    <gameutils/>
</mod>

The init.xml is also modified:
PHP:
<load mod="ManhattanRequiresPentagon"/>

The content of the related ManhattanRequiresPentagon.py
Spoiler :
PHP:
## Sid Meier's Civilization 4
from CvPythonExtensions import *
import BugUtil


gc = CyGlobalContext()


def onCityAcquiredAndKept(argsList):
	'City Acquired and Kept'
	iOwner,pCity = argsList
###Manhattan requires Pentagon start
	if pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_PENTAGON"))>0:
                iOldPlayer = iOwner
                pOldTeam = gc.getTeam(gc.getPlayer(iOldPlayer).getTeam())
                iManhattan = gc.getInfoTypeForString("PROJECT_MANHATTAN_PROJECT")
                if pOldTeam.getProjectCount(iManhattan)>0:
                        pTeam = gc.getTeam(gc.getPlayer(pCity.getOwner()).getTeam())
                        pTeam.changeProjectCount(iManhattan,1)
                        pOldTeam.changeProjectCount(iManhattan,-1)
                        cvProject = gc.getProjectInfo(iManhattan)
                        sButton = cvProject.getButton()
                        CyInterface().addMessage(iOldPlayer,False,15,CyTranslator().getText("TXT_KEY_LOST_MANHATTAN",()),'',0,sButton,ColorTypes(gc.getInfoTypeForString("COLOR_RED")), pCity.getX(), pCity.getY(), True,True)
                        CyInterface().addMessage(pCity.getOwner(),False,15,CyTranslator().getText("TXT_KEY_CONQUERED_MANHATTAN",()),'',0,sButton,ColorTypes(gc.getInfoTypeForString("COLOR_GREEN")), pCity.getX(), pCity.getY(), True,True)
                                
###Manhattan requires Pentagon end


def onCityRazed(argsList):
	'City Razed'
	city, iPlayer = argsList
	iOwner = city.findHighestCulture()
###Manhattan requires Pentagon start		
	if city.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_PENTAGON"))>0:
                pTeam = gc.getTeam(gc.getPlayer(pCity.getOwner()).getTeam())
                iManhattan = gc.getInfoTypeForString("PROJECT_MANHATTAN_PROJECT")
                pTeam.changeProjectCount(iManhattan,-1)
                CyInterface().addMessage(pCity.getOwner(),False,15,CyTranslator().getText("TXT_KEY_LOST_MANHATTAN",()),'',0,sButton,ColorTypes(gc.getInfoTypeForString("COLOR_RED")), pCity.getX(), pCity.getY(), True,True)
###Manhattan requires Pentagon end

def cannotCreate(argsList):
	pCity = argsList[0]
	eProject = argsList[1]
	bContinue = argsList[2]
	bTestVisible = argsList[3]
###Manhattan requires Pentagon start
	iManhattan = gc.getInfoTypeForString("PROJECT_MANHATTAN_PROJECT")
	if eProject == iManhattan:
                if pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_PENTAGON"))>0:
                        cvProject = gc.getProjectInfo(iManhattan)
                        iPrereqTech = cvProject.getTechPrereq ()
                        pTeam = gc.getTeam(gc.getPlayer(pCity.getOwner()).getTeam())
                        if pTeam.isHasTech(iPrereqTech):
                                return False
                        else:
                                return True
                else:
                        return True
###Manhattan requires Pentagon end		
	return False


The events work, and are also registered, like I can see in the PythonDBG.log

PHP:
19:44:41 DEBUG: BugConfig - loading mod file ManhattanRequiresPentagon
19:44:41 DEBUG: BugInit - loading mod ManhattanRequiresPentagon...
19:44:41 INFO : BugCore - creating uninitialized mod ManhattanRequiresPentagon
19:44:41 DEBUG: BUG: looking up ManhattanRequiresPentagon.onCityRazed
load_module ManhattanRequiresPentagon

19:44:41 DEBUG: BUG: looking up ManhattanRequiresPentagon.onCityAcquiredAndKept
19:44:41 DEBUG: Timer - load mod [ManhattanRequiresPentagon] took 5 ms
19:44:41 DEBUG: Timer - load mod [init] took 1218 ms

I don't see that the callback is somehow loaded:
PHP:
19:44:36 DEBUG: BUG: binding CvGameUtils.CvGameUtils.cannotCreate to <CvGameUtils.CvGameUtils instance at 0x2DF89580>
19:44:36 DEBUG: BugGameUtils - creating callback cannotCreate>
[...]
19:44:36 DEBUG: BugGameUtils - cannotCreate - setting default to False

The cannotCreate callback is NOT deactivated via the PythonCallbackDefines.xml, so that's not the issue.
I think I've read now everything in the tutorial, and I still can't see what I did wrong.

Can anyone guess where I've made a mistake :)?
 
Have you checked if your callback code is run (e.g. with a debug message) ?
 
def onCityRazed(argsList):
'City Razed'
city, iPlayer = argsList
iOwner = city.findHighestCulture()
###Manhattan requires Pentagon start
if city.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_PENTAGON"))>0:
pTeam = gc.getTeam(gc.getPlayer(pCity.getOwner()).getTeam())
iManhattan = gc.getInfoTypeForString("PROJECT_MANHATTAN_PROJECT")
pTeam.changeProjectCount(iManhattan,-1)
CyInterface().addMessage(pCity.getOwner(),False,15,CyTranslator().getText("TXT_KEY_LOST_MANHATTAN",()),'',0,sButton,ColorTypes(gc.getInfoTypeForString("COLOR_RED")), pCity.getX(), pCity.getY(), True,True)
###Manhattan requires Pentagon end

I don't use BUG, so I not sure about BUG codes, but pCity is not defined here?

Also, personally I prefer to use def onCityAcquired rather than def onCityAcquiredAndKept. Because sometimes the city may be razed instead. For this case, if I capture the Pentagon city and raze it:
1) I believe def onCityAcquiredAndKept is not activated since I did not keep it. Thus, I don't get the benefits of Manhattan as intended.
2) However, previous owner will still keep benefits of Manhattan since nothing was done to remove it from him. Even if something is done to iOwner under defonCityRaze, it may not be the true previous owner as iOwner refers to player with highest culture in city, since he may have snatched the city from the real player who built the city
 
Oops :blush:, oversight from my side :blush:.

Don't understand #1 :confused:, for #2: That's what the code is doing. It removes the project from the owner. The highstculture thing is only for the "ethnic owner", not for the real owner.


And @before: Did now add a debug message to the callback, and it doesn't fire, like expected.
 
1) I was just thinking whether onCityAcquiredAndKept codes will be triggered when the conquerer raze the city directly without keeping it. If it doesn't, then Manhattan will not be removed from previous owner?
 
Could it be that you have a modded BtS :D?
Because in vanilla Bts you can't keep a city and raze it later ;).
You either raze it directly, which triggers onCityAcquired and onCityRazed, or you keep it, which triggers onCityAcquired and onCityAcquiredAndKept.
 
Could it be that you have a modded BtS :D?
Because in vanilla Bts you can't keep a city and raze it later ;).
You either raze it directly, which triggers onCityAcquired and onCityRazed, or you keep it, which triggers onCityAcquired and onCityAcquiredAndKept.

Exactly what I meant. If you conquer the city and raze it directly, this means that all the codes you wrote under def onCityAcquiredAndKept will not be triggered.

Then this is what happens when Saladin killed the last defender in Athens which has Pentagon, and when given a choice to keep or raze, he raze Athens directly.

A) Because it is razed directly, all the codes in onCityAcquiredAndKept are not triggered, so Saladin is not given anything.
However, onCityRazed is triggered, which tries to remove Manhattan project when a city with Pentagon is razed. This results in the system trying to remove Manhattan from Saladin who does NOT have Manhattan.

B) As for Alexander, the city is taken away from him, so he lost Pentagon. But because Athens was razed directly, all the codes in onCityAcquiredAndKept are not triggered again. This means that Manhattan will not be removed from him, even though he lost Pentagon.

Results:
Saladin with a negative number of Manhattan.
Alexander without Pentagon, but still has Manhattan

Edit:
I did some tests and I was right. def onCityRazed, using city.getOwner() in this case will refer to Saladin rather than the original owner, Alexander. Thus, you will end up trying to remove Manhattan from Saladin rather than Alexander when you raze the city directly.

My simple test:
Code:
pPlayer = gc.getPlayer(city.getOwner())
pPlayer.changeGoldenAgeTurns(5)

I razed his city and got golden age
 
You're confusing something here ;).
My problem is not with onCityRazed, but with cannotCreate ;).

The code in onCityRazed is borked, you're right.
The guy whose city was razed should lose the Manhattan project, that's what is intented and what would make sense. It should not be transferred to someone else and then removed.
The current code doesn't make sense, but that's not my problem here ;).

Btw, onCityAcquiredAndKept is not MP safe in a normal DLL. Your code will yield an OoS when run in multiplayer.

eh, why that?
But good to know :).

About the problem: Maybe try the long version of the gameutils tag as described here just to see if it changes anything: http://sourceforge.net/apps/mediawiki/civ4bug/index.php?title=Core_XML_Reference#.3Cgameutils.3E

Ah, thanks, will try it :).
 
Yeah, I know the problem with BUG does not lie in CityRazed.

However, from what I see, what you are doing in def onCityRazed is removing Manhattan from city.getOwner(), which refers to the new owner, not the previous owner. In short, it is removing from the wrong player. The logic here is wrong
 
I'm not sure about that.
It might be that the razing is triggered before the city was occupied, therefore still belonging to the old owner. I thought I had tested that, but since the code is obviously borked, I can't have done that :hmm:.
 
Well I did the simple test stated above.

I razed a rival city => I got the Golden Age. He didn't.
city.getOwner() refers to new owner.

Thus, although it sounds troublesome, for my wonders, I did them all in onCityAcquired and onCityRazed, so that even if he razed it directly, both codes are activated such that the wonder/project is transferred temporaily to the conquerer before being destroyed
 
My motto:
Trial and Error:lol:

Although, that doesn't help you in any bit since the main issue here is conflict with BUG :dunno:
 
eh, why that?
But good to know :).
The code in the DLL that triggers the event is located in the popup result handling so only the one who makes the choice (raze or keep) triggers the event but not the other computers in the game. At least that was the case in the C2C dll which caused a desync so I switched to using a message to trigger the event recently.
 
About the problem: Maybe try the long version of the gameutils tag as described here just to see if it changes anything: http://sourceforge.net/apps/mediawiki/civ4bug/index.php?title=Core_XML_Reference#.3Cgameutils.3E

-> worked, changing the XML from:
PHP:
<gameutils/>

to:
PHP:
<gameutils module="ManhattanRequiresPentagon" handlers="cannotCreate"/>

did the trick.
Not sure where the exact issue was :dunno:.
But whatever, thanks :).

Also thanks to platyping, did the needed changes (and moved the stuff to the MP save function).
 
Top Bottom