Long time lurker... first time poster. Question about DLL

JDHalfrack

Chieftain
Joined
Mar 18, 2009
Messages
35
First off, let me say how excellent this site is. There are some really smart cookies that post on this site. Anyway... a little about me before I ask my question so you know a little about where I'm coming from (I'll hide it under a "spoiler" tag so if you don't want to read it, you don't have to... ;) ).

Spoiler :
I've been programming for about 5 years (self taught), so I am not a complete novice. In fact, if any of you ever play Madden and head over to football-freaks.com, you can find a lot of my work over there. Ususally I do my programming in Basic (Visual Studio 9.0) but I have messed around with C, C++ and C# enough to figure out the basic coding of the CvGameCoreDLL. Long story short, I am not a complete idiot, and I really did try this on my own before posting.


Okay, so my idea was to make a trait that gives happines to all your cities when you are at war. Basically, +1:)/city per civilization you are at war with. Making the trait is easy enough (once again... spoilers so you don't have to read it):
Spoiler :
Code:
    <TraitInfo>
      <Type>TRAIT_GENECIDAL</Type>
      <Description>TXT_KEY_TRAIT_GENECIDAL</Description>
      <ShortDescription>TXT_KEY_TRAIT_GENECIDAL_SHORT</ShortDescription>
      <Help>TXT_KEY_TRAIT_GENECIDAL_HELP</Help>
      <iHealth>0</iHealth>
      <iHappiness>0</iHappiness>
      <iMaxAnarchy>-1</iMaxAnarchy>
      <iUpkeepModifier>0</iUpkeepModifier>
      <iLevelExperienceModifier>-10</iLevelExperienceModifier>
      <iGreatPeopleRateModifier>0</iGreatPeopleRateModifier>
      <iGreatGeneralRateModifier>25</iGreatGeneralRateModifier>
      <iDomesticGreatGeneralRateModifier>0</iDomesticGreatGeneralRateModifier>
      <iMaxGlobalBuildingProductionModifier>0</iMaxGlobalBuildingProductionModifier>
      <iMaxTeamBuildingProductionModifier>0</iMaxTeamBuildingProductionModifier>
      <iMaxPlayerBuildingProductionModifier>0</iMaxPlayerBuildingProductionModifier>
      <ExtraYieldThresholds/>
      <TradeYieldModifiers/>
      <CommerceChanges/>
      <CommerceModifiers/>
      <FreePromotions>
        <FreePromotion>
          <PromotionType>PROMOTION_CITY_RAIDER1</PromotionType>
          <bFreePromotion>1</bFreePromotion>
        </FreePromotion>
      </FreePromotions>
      <FreePromotionUnitCombats>
        <FreePromotionUnitCombat>
          <UnitCombatType>UNITCOMBAT_MELEE</UnitCombatType>
          <bFreePromotionUnitCombat>1</bFreePromotionUnitCombat>
        </FreePromotionUnitCombat>
        <FreePromotionUnitCombat>
          <UnitCombatType>UNITCOMBAT_SIEGE</UnitCombatType>
          <bFreePromotionUnitCombat>1</bFreePromotionUnitCombat>
        </FreePromotionUnitCombat>
        <FreePromotionUnitCombat>
          <UnitCombatType>UNITCOMBAT_ARMOR</UnitCombatType>
          <bFreePromotionUnitCombat>1</bFreePromotionUnitCombat>
        </FreePromotionUnitCombat>
      </FreePromotionUnitCombats>
    </TraitInfo>
Basically, the idea behind this trait is that any leader with this trait wants to be at war, and thrives on war. So, I did what I could with XML, and moved onto the CvCity.h and CvCity.cpp.

In the CvCity.h file, I added the following code under the 'public:' section:
Code:
DllExport int getWarHappiness() const;
DllExport void changeWarHappiness(int iChange);
I added the following code under the 'protective:' section:
Code:
int m_iWarHappiness;


In the CvCity.cpp file I added the following code in the 'void CvCity::reset' function:
Code:
m_iWarHappiness = 0;
I made the following edit (the bold) in the in the 'int CvCity::happyLevel()' function:
Spoiler :
Code:
int CvCity::happyLevel() const
{
	int iHappiness;

	iHappiness = 0;

	iHappiness += std::max(0, getLargestCityHappiness());
	iHappiness += std::max(0, getMilitaryHappiness());
	iHappiness += std::max(0, getCurrentStateReligionHappiness());
	iHappiness += std::max(0, getBuildingGoodHappiness());
	iHappiness += std::max(0, getExtraBuildingGoodHappiness());
	iHappiness += std::max(0, getFeatureGoodHappiness());
	iHappiness += std::max(0, getBonusGoodHappiness());
	iHappiness += std::max(0, getReligionGoodHappiness());
	iHappiness += std::max(0, getCommerceHappiness());
	iHappiness += std::max(0, area()->getBuildingHappiness(getOwnerINLINE()));
	iHappiness += std::max(0, GET_PLAYER(getOwnerINLINE()).getBuildingHappiness());
	iHappiness += std::max(0, (getExtraHappiness() + GET_PLAYER(getOwnerINLINE()).getExtraHappiness()));
	iHappiness += std::max(0, GC.getHandicapInfo(getHandicapType()).getHappyBonus());
	iHappiness += std::max(0, getVassalHappiness());
	[SIZE="4"][B]iHappiness += std::max(0, getWarHappiness());[/B][/SIZE]

	if (getHappinessTimer() > 0)
	{
		iHappiness += GC.getDefineINT("TEMP_HAPPY");
	}

	return std::max(0, iHappiness);
}
I added the following code in the 'CvCity::read(FDataStreamBase* pStream)' function:
Code:
pStream->Read(&m_iWarHappiness);
I added the following code in the 'CvCity::write(FDataStreamBase* pStream)' function:
Code:
pStream->Write(&m_iWarHappiness);
I added the following two functions at the very end of cvCity.cpp:
Code:
int CvCity::getWarHappiness() const
{
	return m_iWarHappiness;
}

void CvCity::changeWarHappiness(int iChange)
{
	m_iWarHappiness += iChange;
}

At this point, I think I have added all the right coding to add "warhappiness" to a city. The DLL compiles with no errors (takes about 15 - 20 mins). And I copy it to my Assets folder.

So, in the CvEventManager.py file, I editted the following code:
Spoiler :
Code:
def onChangeWar(self, argsList):
		'War Status Changes'
		bIsWar = argsList[0]
		iTeam = argsList[1]
		iRivalTeam = argsList[2]
	
		[B][SIZE="4"]##### Gen Trait Start #####
		
		if (CyGame().isFinalInitialized()):
			
			pTeam = gc.getTeam(iTeam)
			pRivalTeam = gc.getTeam(iRivalTeam)
			iPlayer = pTeam.getLeaderID()
			iRivalPlayer = pRivalTeam.getLeaderID()
			
			pPlayer = gc.getPlayer(iPlayer)

			iTrait = CvUtil.findInfoTypeNum(gc.getTraitInfo,gc.getNumTraitInfos(),'TRAIT_GENECIDAL')
			
			if (pPlayer.hasTrait(iTrait)) and (bIsWar):
				for iCity in range(pPlayer.getNumCities()):
					pCity = pPlayer.getCity(iCity)
					pCity.changeWarHappiness(+1)
				
		##### Gen Trait End #####[/SIZE][/B]
I know this will find every city owned by the player declaring war because I have tested it with other functions (like pCity.change Population). But, when it gets to this point in when I declare war, I get this error:
"AttributeError: 'CyCity' object has no attribute 'changeWarHappiness'
ERR: Python function onEvent failed, module CvEventInterface"

Am I "calling" my function wrong? Or is there a LOT more to this than even I thought?

Thanks ahead for the help guys. If you need more info from me, let me know.

JD
 
Yes, you are calling the wrong function. If you don't sort this out yourself I'll make it my "add a new field" integer value for the "Idiot's Guide" (not an insult, just what I called the thread. Sorry ;) But I am looking for practical examples that will see some light of day and maybe throw me some surprises so I can demonstrate things beyond what I might think people can handle)


Your problem is that ONLY the DLL is allowed to see any of the Cv____.* files. What Python sees are all of the Cy____.* files.

Open up CyCity.cpp, and you'll see that each function just calls over to CvCity.cpp to get a "sister function" to respond. This shelters C++ from Python attempting to access an invalid Pointer and causing a hard crash (instead you just get Python Errors claiming there is no such pointer, and can keep on going with the code).


So, you need a function in CyCity.cpp which will access CvCity.cpp for you. You will have to declare this function in CyCity.h, AND you have to edit CyCityInterface.cpp so that the DLL <--> Python interface can translate what Python says (the first set of quotes) into a call to a function (the listed function parameter, your CyCity.cpp function).


Also, you can skip any use of python for this function if you create a new tag for the XML to use which specifies the need for this adjustment automatically. That is what the guide will walk you through if you want me to go ahead with it (I have a Boolean tag transfer written, and have almost finished polishing a new Boolean tag to post as well, so you may be able to piece things together for yourself from just what is up there now. I know that personally I enjoy running blind for a while before anyone assists my endeavors, so let me know if you would like a guide written for this function).
 
I'll make it my "add a new field" integer value for the "Idiot's Guide" (not an insult, just what I called the thread. Sorry ;) ...)
Trust me, there is no insult taken at all. I actually read through it a little. It was like the 48th awesome post I have read on this site about editting/compiling, so it was a little hard to remember what info I got from what post.

Your problem is that ONLY the DLL is allowed to see any of the Cv____.* files. What Python sees are all of the Cy____.* files.
Okay... NOW it makes more sense. I didn't fully understand the purpose of the "Cy" files. I'll go through and edit all the files you said and try it again.

Also, you can skip any use of python for this function if you create a new tag for the XML to use which specifies the need for this adjustment automatically. That is what the guide will walk you through if you want me to go ahead with it
This is actually what I'd LOVE to do. XML is so much easier. So, I am going to try this myself (because I think it helps me learn it better). However, I would say go ahead and write a guide for this in case I can't figure this out ;) . I think it will help others as well (as I am sure your boolean example has too!). I was actually suprised to not find any buildings or traits, etc... that gave "WarHappiness." I was going to just try and follow their example.

Anyway, thanks for your help (and especially how quickly you helped!). I'll try this later this afternoon.

JD
 
WarHappiness does not make sense while you can still get war weariness (which leads to Unhappiness). Therefore you should also set the war weariness to 0 for players with that trait.
 
WarHappiness does not make sense while you can still get war weariness (which leads to Unhappiness). Therefore you should also set the war weariness to 0 for players with that trait.
Oh yeah... duh. That makes sense. I'll have to remember to do that.

Anyway, here's where I am at with this... I decided to try something else first: make a new option for traits that gives extra population on city creation. I have seen quite a few MODS that have traits that do this, but all seem to use python or internal coding to do this. I wanted to add the option in XML so if I ever release a MOD, others could easily change this value or create a new trait that includes this option. The following all works. I have tested it and it works flawlessly. I am only going to go through the steps so I can get to my new question.

I updated the CIV4CivilizationsSchema.XML file to include my new option by adding the two lines (in the appropriate places):
Code:
<ElementType name="iCityStartExtraPopulation" content="textOnly" dt:type="int"/>
Code:
<element type="iCityStartExtraPopulation" minOccurs="0"/>
So, in a new trait, I added this line at the end:
Code:
<iCityStartExtraPopulation>1</iCityStartExtraPopulation>
I want this to add 1 extra population to each city when it is "founded" regardless of what era you start in or anything else.

To do this, I added the following lines in CvInfos.cpp:
Under CvTraitInfo::CvTraitInfo() : I added
Code:
m_iCityStartExtraPopulation(0),
Under CvTraitInfo::~CvTraitInfo() I added
Code:
int CvTraitInfo::getCityStartExtraPopulation() const
{
	return m_iCityStartExtraPopulation;
}
Under bool CvTraitInfo::read(CvXMLLoadUtility* pXML) I added
Code:
pXML->GetChildXmlValByName(&m_iCityStartExtraPopulation, "iCityStartExtraPopulation");

I added the following lines in CvInfos.h:
In the public: area I added
Code:
DllExport int getCityStartExtraPopulation() const;
In the protected: area I added
Code:
int m_iCityStartExtraPopulation;

I added the following line in CvGameTextMgr.cpp:
Under void CvGameTextMgr::parseTraits(CvWStringBuffer &szHelpString, TraitTypes eTrait, CivilizationTypes eCivilization, bool bDawnOfMan) I added
Code:
if (GC.getTraitInfo(eTrait).getCityStartExtraPopulation() > 0)
		{
			szHelpString.append(gDLL->getText("TXT_KEY_TRAIT_CITY_START_EXTRA_POPULATION", GC.getTraitInfo(eTrait).getCityStartExtraPopulation()));
		}

As a result of this last code, I created a new text XML with the following code:
Code:
<TEXT>
    <Tag>TXT_KEY_TRAIT_CITY_START_EXTRA_POPULATION</Tag>
    <English>[NEWLINE][SPACE][SPACE][ICON_BULLET]Cities start with %d1 extra population</English>
    <French/>
    <German/>
    <Italian/>
    <Spanish/>
  </TEXT>

So, at this point, everything works great, but to get the code to actually do anything, I added the following lines to CvCity.cpp:
In the void CvCity::init(int iID, PlayerTypes eOwner, int iX, int iY, bool bBumpUnits, bool bUpdatePlotGroups) function right after this line:
Code:
changePopulation(GC.getDefineINT("INITIAL_CITY_POPULATION") + GC.getEraInfo(GC.getGameINLINE().getStartEra()).getFreePopulation());
I added

Code:
for (iI = 0; iI < GC.getNumTraitInfos(); iI++)
	{
		if (hasTrait((TraitTypes)iI))
		{
			iJ = GC.getTraitInfo((TraitTypes)iI).getCityStartExtraPopulation();
			if (iJ > 0)
			{
				setPopulation(getPopulation() + iJ);
			}
		}
	}

As I said earlier, this worked perfectly. It's really nice to be able to change this value now just in an XML file instead of having to add code to python or anything else. But the problem is that I tried to do the same thing with a new value: "WarHappiness." I added all of the exact same code in all the same places (except for CvCity.cpp). I copied and pasted the new codes right beneath the "CityStartExtraPopulation" codes and changed everything to WarHappiness where it said CityStartExtraPopulation. I compiled it, and it compiled fine. Even though I don't have it "doing anything" yet (like I have the CityStartPopulation code doing), I should still be able to load my mod and play the game. But for some reason, when I load my mod, Leaders don;t show up, and when I try to play the game, it crashes.

Any ideas?

JD
 
That is the standard approach to adding a new tag. Copy the last one you added in the same place, only changing the names to protect the inn... Hrm. Anyway.

Personal preference: Instead of doing a loop over all traits to see what the owner of the city has, I would instead modify CvPlayer::init to contain your new trait information. Scroll down a little bit to:

Code:
		for (iI = 0; iI < GC.getNumTraitInfos(); iI++)
		{
			if (hasTrait((TraitTypes)iI))
			{
				changeExtraHealth(GC.getTraitInfo((TraitTypes)iI).getHealth());
				changeExtraHappiness(GC.getTraitInfo((TraitTypes)iI).getHappiness());

And add in your own new trait information. You'll then need a variable and some functions (change___(int) & get____()) to handle this as extra information on the CvPlayer object though, so it is completely personal preference for how to handle it.


If you maintain a backup copy of your previous STABLE DLL version (a good practice), swap back to the DLL before adding WarHappiness and see if you can load. If things work fine, then we know the error isn't in the XML fields being added. Most likely this is the case, but it is so easy to check you always want to start with that.


After that, I would remove all of the code you just added piece by piece (or remove all of it and add things back piece by piece). Remove CvGameTextMgr changes first, see if that improves things, then remove the CvInfos stuff and see if that helps (only CvInfos.cpp, leave the header alone so it compiles quicker. It is VERY unlikely you did anything wrong in the header)
 
...it is completely personal preference for how to handle it.
"Personal Preference..." I have only been a member here for like 4 days, and I am already smart enough to realize that when you say "personal preference," you really mean "it's probably best to do it my way." ;) Not listening to your "personal preference" would be like me not taking art advice from Michaelangelo... :)

I do have a question, though. First, why do you prefer to do it this way? It seems like the same thing is happening, just in a different function. Is it because it's more releated to the player rather than the city?

Anyway, thanks again for the help! I will try these things and report back exactly when the MOD doesn't load correctly.

JD
 
Mostly, because the loop already exists here, and will only run once per player. Your way runs once per city founded.

But secondary reasons:

More accessible to python
No concerns about making sure that the new consideration is used properly in captured cities, or those gained through diplomacy
No concerns about making sure that the new consideration is REMOVED properly when captured or given away through diplomacy.


Probably quite a few other possibilities along the way too. For your initial population these aren't quite as large of potential issues as they are for your War Happiness. But the city capture/conversion could be an issue depending on precisely how the ::acquireCity function is written to adjust population.
 
something offtopic: what does genecidal mean? dict.leo.org does not know it. :D google translator either.
 
It's difficult to translate words with spelling errors ;)

I hate you... ;)

I do feel like an idiot, though. Thank you for pointing that out.

So, I do think I made an error in my XML files somewhere. It's traits with free promotions that are crashing the game... This did not happen originally, and I cannot remember editting something in the XML schemas or TraitInfos that didn't work...

I guess I'll go back to checking those out...

EDIT: Okay... so I reverted everything back in both the XML files and the .cpp and .h files to when I just had the CityStartExtraPopulation feature. It's still not working.... could I have messed up something in the compiling process? I am so confused. I had this all working earlier. Grrr...

EDIT #2: I just did a clean recompile and all is good now... God I hate C++... Onto working on WarHappiness. yay.

JD
 
Okay, I think I'm getting a good hang of this. I find that allowing users the option to edit XML values is a lot nicer than user editting Python code. At least, that's my opinion. So, thanks to whoever guided me down that path.

I am practicing my C++ coding by taking all of tsentom1's python traits found here: http://forums.civfanatics.com/showthread.php?t=297557, and converting them to XML edittable values. Basically, I can create a new trait using tsentom1's ideas but a LOT easier because all I need to do is edit XML values now. So, I could create a trait that starts cities with 2 population and also cuts upgrade costs by 50%. Does that make sense?

This is really fantastic. I never knew how much Civilization could be editted. I am finding the hardest part is knowing where to put the code that actually does something. The code itself is usally self-explanitory, just not the location.

Well, there's where I am at. (I haven't even strated the WarHappiness yet...:mischief:).

If anyone is interested in the DLL files and extra XML files I made to make tsentom1's traits a little more customizable, let me know. In NO way am I trying to take credit for his trait ideas! All I did was "convert" them to XML.

JD
 
Are you going to include the Culture being produced by non state Religion trait, found at the end of the thread, or at least the python code for it? I was thinking I needed to move it to the dll anyway, since it doesn't show the culture in the city screen (though it still applys), but I wol't double the work, if you're already writing the code for it, lol.
 
Are you going to include the Culture being produced by non state Religion trait, found at the end of the thread, or at least the python code for it? I was thinking I needed to move it to the dll anyway, since it doesn't show the culture in the city screen (though it still applys), but I wol't double the work, if you're already writing the code for it, lol.

I haven't seen that yet. I'll take a look at it. I probably could. I am about 3/4 of the way done with the traits on the first page. Then I'll go through and add the ones that others have requested within the thread (although I think tsentom1 has done most of them).

JD
 
Wow, sweet. Thanks, and remember to comment your source code for the individual tags. Alot of people (myself included) will only want to merge a couple of the tags you make available, and not all of them. And most of us will need to merge it with other modded SDK stuff.

You're saving me a lot of time there (My only knowledge of coding comes from modding this game, so figuring out how to transfer the python to C++ would have taken me a while), thanks again.
 
Wow, sweet. Thanks, and remember to comment your source code for the individual tags.

Just a couple things, though.

First, it may take me a bit of time (probably within the week, though). I have only been doing this now for two days. So, I too, am learning (although I did have some previous experience).

Second, when you say to comment my tags, is there a particular format? I haven't done anything with "merging," so I don't know how it works. Sorry (I am very noobish on this forum :().


JD
 
Standard convention is:

//Start Name of what your code does
code fu
//End Name of what your code does

This allows people that merge your source with their own modded source to quickly search and cut and paste the component code. Like for me, my .dll comes from RevDCM, plus a few tweaks. So I would need to merge the source code files and recompile a new .dll. To do that though, it helps, or often is only possible to do, if the code is commented so it can be found.
 
Standard convention is:

//Start Name of what your code does
code fu
//End Name of what your code does

This allows people that merge your source with their own modded source to quickly search and cut and paste the component code.

Ok, makes sense. I'll have to go through and change what I am doing right now (//----------JDHALFRACK----------), but that will be a piece of cake. Thanks for the help.

JD
 
Top Bottom