Mod-Modders Guide to Fall Further

CvUnit. In CvPlayer it is mostly just checking for Technologies and other things which are a quick way to eliminate the check for a lot of orders and make things faster. Once that has been checked, then the unit itself is checked (pretty sure you'll find that the CvPlayer one is called at the start of the CvUnit one actually)

Alright, thought I had it working (code compiled, at least :lol:), but ran into an error when loading the mod I haven't seen before. Pretty sure it's an obvious one, and something I should be able to recognize, but I've never seen it before. :confused:

Error said:
Index is -1 inside function

Uploaded a folder containing the three edited DLL files (CvInfos.h, .cpp, and CvUnit.cpp) and the schema (And the compiled DLL, though I'm not sure why... Can't use it without RifE, unlike the rest. :lol:).

All of the code can be found by searching for 'Workers Paradise', same name as the folder.


Also, I'm sure this code chunk could be optimized... Not actually sure it works yet because of this error. :p It's the part that actually DOES something, in CvUnit:
Code:
/*************************************************************************************************/
/**	Workers Paradise						01/08/10											**/
/**																								**/
/**							Allows promotions to affect build orders							**/
/*************************************************************************************************/
	bool bValid = true;
	if (!(m_pUnitInfo->getBuilds(eBuild)))
	{
		bValid = false;
		for (int iJ=0;iJ<GC.getNumPromotionInfos();iJ++)
		{
			if ((isHasPromotion((PromotionTypes)iJ)) && (GC.getPromotionInfo((PromotionTypes)iJ).getNumPromotionBuilds()>0))
			{
				for (int iI=0;iI<GC.getNumBuildInfos();iI++)
				{
					if (GC.getPromotionInfo((PromotionTypes)iJ).getPromotionBuilds(iI) == eBuild)
					{
						bValid = true;
					}
				}
			}
		}
		if (!bValid)
		{
			return false;
		}
	}
/*************************************************************************************************/
/**	Workers Paradise						END													**/
/*************************************************************************************************/
The original code for the spot:
Code:
	if (!(m_pUnitInfo->getBuilds(eBuild)))
	{
		return false;
	}
 

Attachments

  • Workers Paradise.rar
    1.5 MB · Views: 147
Interesting, so the main issue with events/triggers seemed to be an inability to add new events maybe? It could be that something unique happens with them at the end of loading, like a hook in CvInitCore or something... doesn't quite sound right when I say that though. I know that one of the base BtS mods had done something to address the issues with events, but don't know if they managed to completely fix it or not. I'll have to track that down when I get to them.
 
this here is from Rife SDK, but I really really doubt Valkrionn changed anything at it.
cveventinfos::copynondefaults
Code:
	for (int iBuildingClass = 0; iBuildingClass < GC.getNumBuildingClassInfos(); iBuildingClass++)
	{
		for (int iCommerce = 0; iCommerce < NUM_COMMERCE_TYPES; iCommerce++)
		{
			if (pClassInfo->getBuildingYieldChange(iBuildingClass, iCommerce) != 0)
			{
				BuildingCommerceChange kChange;
				kChange.eBuildingClass = (BuildingClassTypes)iBuildingClass;
				kChange.eCommerce = (CommerceTypes)iCommerce;
				kChange.iChange =																	pClassInfo->getBuildingCommerceChange(iBuildingClass, iCommerce);
																	m_aBuildingCommerceChanges.push_back(kChange);
			}
		}
	}
It uses Yield changes one time, Commerce changes another time. I tried to fix this and other issues in Wildmana, but gave up shortly as it seemed better to wait for a new FF version (was long ago when FF was still updating)
 
Sephi:
Ugh, that is messy. Too many copy/paste mistakes.

Valk:
Sorry, I didn't see your post when I made my last one:

For your error here, if you are using my type of list instead of a Firaxian Array, you do NOT want a loop over all NumBuildInfos, instead you want a loop over getNumPromotionBuilds() for the promotion you are checking at the time (since no promotion authorizes ALL build orders, you are going outside the length of the list every check with your code)
 
Sephi:
Ugh, that is messy. Too many copy/paste mistakes.

Valk:
Sorry, I didn't see your post when I made my last one:

For your error here, if you are using my type of list instead of a Firaxian Array, you do NOT want a loop over all NumBuildInfos, instead you want a loop over getNumPromotionBuilds() for the promotion you are checking at the time (since no promotion authorizes ALL build orders, you are going outside the length of the list every check with your code)

I thought it would be something with that, main reason that's the only code I actually posted in the thread. :lol:

I think this would correct the issue then? Relevant section is in bold.

Code:
/*************************************************************************************************/
/**    Workers Paradise                        01/08/10                                            **/
/**                                                                                                **/
/**                            Allows promotions to affect build orders                            **/
/*************************************************************************************************/
    bool bValid = true;
    if (!(m_pUnitInfo->getBuilds(eBuild)))
    {
        bValid = false;
        for (int iJ=0;iJ<GC.getNumPromotionInfos();iJ++)
        {
            if (isHasPromotion((PromotionTypes)iJ))
            {
[B]                iNumPromotionBuilds = GC.getPromotionInfo((PromotionTypes)iJ).getNumPromotionBuilds();
                if (iNumPromotionBuilds > 0)
                {
                    for (int iI=0; iI < iNumPromotionBuilds ;iI++)
                    {
                        if (GC.getPromotionInfo((PromotionTypes)iJ).getPromotionBuilds(iI) == eBuild)
                        {
                            bValid = true;
                        }
                    }
                }[/B]
[B]  iNumPromotionCannotBuilds = GC.getPromotionInfo((PromotionTypes)iJ).getNumPromotion[/B][B]Cannot[/B][B]Builds();
                if (iNumPromotion[/B][B]Cannot[/B][B]Builds > 0)
                {
                    for (int iI=0; iI < iNumPromotion[/B][B]Cannot[/B][B]Builds ;iI++)
                    {
                        if (GC.getPromotionInfo((PromotionTypes)iJ).getPromotion[/B][B]Cannot[/B][B]Builds(iI) == eBuild)
                        {
                            bValid = false;
                        }
                    }
                }[/B]
            }
        }
        if (!bValid)
        {
            return false;
        }
    }
/*************************************************************************************************/
/**    Workers Paradise                        END                                                    **/
/*************************************************************************************************/
Edit: Nope, same exact issue. Get an xml error on loading, reading 'Error: Index is -1 inside function'.
 
Oh, while loading? I had thought it a wierd error for such a location anyway. This code looks right now (though you should ditch the first check of getNumPromoBuilds > 0), but doesn't launch at all until you have set up your custom game and have your first unit spawned on the map.

Search for the exact text of the error and you'll find the assert most likely, then you can back trace from there to see the issue. Most likely it is an Assert in your CvInfos in one of the ::get functions, probably being called from a CopyNonDefault which is written improperly (likely the same mistake made here?)
 
Oh, while loading? I had thought it a wierd error for such a location anyway. This code looks right now (though you should ditch the first check of getNumPromoBuilds > 0), but doesn't launch at all until you have set up your custom game and have your first unit spawned on the map.

Search for the exact text of the error and you'll find the assert most likely, then you can back trace from there to see the issue. Most likely it is an Assert in your CvInfos in one of the ::get functions, probably being called from a CopyNonDefault which is written improperly (likely the same mistake made here?)

Can't figure anything out from the assert (it's in XMLLoadUtilitySet), and I can't see anything obvious in my code either... Cloned it from 'PrereqPromotionsNotOnTile'.

Spoiler :
Code:
/*************************************************************************************************/
/**	Workers Paradise						01/08/10											**/
/**																								**/
/**							Allows promotions to affect build orders							**/
/*************************************************************************************************/
int CvPromotionInfo::getPromotionBuilds(int iI) const					{return (getNumPromotionBuilds() > iI)				? m_piPromotionBuilds[iI]					: -1;}
int CvPromotionInfo::getNumPromotionBuilds() const						{return m_iNumPromotionBuilds;}
/*************************************************************************************************/
/**	Workers Paradise						END													**/
/*************************************************************************************************/
/*************************************************************************************************/
/**	Workers Paradise						01/08/10											**/
/**																								**/
/**							Allows promotions to affect build orders							**/
/*************************************************************************************************/
	stream->Read(&m_iNumPromotionBuilds);
	if (m_iNumPromotionBuilds > 0)
	{
		SAFE_DELETE_ARRAY(m_piPromotionBuilds);
		m_piPromotionBuilds = new int[m_iNumPromotionBuilds];
		stream->Read(m_iNumPromotionBuilds, m_piPromotionBuilds);
	}
/*************************************************************************************************/
/**	Workers Paradise						END													**/
/*************************************************************************************************/
/*************************************************************************************************/
/**	Workers Paradise						01/08/10											**/
/**																								**/
/**							Allows promotions to affect build orders							**/
/*************************************************************************************************/
	stream->Write(m_iNumPromotionBuilds);
	if (m_iNumPromotionBuilds > 0)
		stream->Write(m_iNumPromotionBuilds, m_piPromotionBuilds);
/*************************************************************************************************/
/**	Workers Paradise						END													**/
/*************************************************************************************************/
/*************************************************************************************************/
/**	Workers Paradise						01/08/10											**/
/**																								**/
/**							Allows promotions to affect build orders							**/
/*************************************************************************************************/
	if (gDLL->getXMLIFace()->SetToChildByTagName(pXML->GetXML(),"PromotionBuilds"))
		pXML->SetIntWithChildList(&m_iNumPromotionBuilds, &m_piPromotionBuilds);
/*************************************************************************************************/
/**	Workers Paradise						END													**/
/*************************************************************************************************/
/*************************************************************************************************/
/**	Workers Paradise						01/08/10											**/
/**																								**/
/**							Allows promotions to affect build orders							**/
/*************************************************************************************************/
if(pClassInfo->getNumPromotionBuilds() > 0)
	{
		int* tempArray = new int[getNumPromotionBuilds() + pClassInfo->getNumPromotionBuilds()];
		for(int i = 0; i< getNumPromotionBuilds(); ++i)
		{
			tempArray[i] = getPromotionBuilds(i);
		}
		int iNewItems = 0;
		for(int i = 0; i < pClassInfo->getNumPromotionBuilds(); ++i)
		{
			bool bLoad = true;
			for(int j=0;j<getNumPromotionBuilds();++j)
			{
				if(pClassInfo->getPromotionBuilds(i) == getPromotionBuilds(j))
				{
					bLoad = false;
					break;
				}
			}
			if(bLoad)
			{
				tempArray[iNewItems+getNumPromotionBuilds()] = pClassInfo->getPromotionBuilds(i);
				iNewItems++;
			}
		}
		SAFE_DELETE_ARRAY(m_piPromotionBuilds);
		int iGoalSize = getNumPromotionBuilds() + iNewItems;
		m_piPromotionBuilds = new int[iGoalSize];
		for(int i = 0; i < iGoalSize; ++i)
		{
			m_piPromotionBuilds[i] = tempArray[i];
			FAssertMsg(m_piPromotionBuilds[i] < GC.getNumPromotionInfos() ,"Out of Bounds Array Melding");
			FAssertMsg(m_piPromotionBuilds[i] > -1 ,"Out of Bounds Array Melding");
		}
		m_iNumPromotionBuilds = iGoalSize;
		SAFE_DELETE_ARRAY(tempArray);
	}
/*************************************************************************************************/
/**	Workers Paradise						END													**/
/*************************************************************************************************/
/*************************************************************************************************/
/**	Workers Paradise						01/08/10											**/
/**																								**/
/**							Allows promotions to affect build orders							**/
/*************************************************************************************************/
	if(pClassInfo->getNumPromotionBuilds() > 0)
	{
		int iGoalSize = bOver ? pClassInfo->getNumPromotionBuilds() : getNumPromotionBuilds() + pClassInfo->getNumPromotionBuilds();
		int* tempArray = new int[iGoalSize];
		for(int i = 0; i < pClassInfo->getNumPromotionBuilds(); ++i)
		{
			tempArray[i] = pClassInfo->getPromotionBuilds(i);
		}
		if(!bOver)
		{
			int iOffset = pClassInfo->getNumPromotionBuilds();
			for(int i = 0; i< getNumPromotionBuilds(); ++i)
			{
				tempArray[i+iOffset] = getPromotionBuilds(i);
			}
		}
		SAFE_DELETE_ARRAY(m_piPromotionBuilds);
		m_piPromotionBuilds = new int[iGoalSize];
		for(int i = 0; i < iGoalSize; ++i)
		{
			m_piPromotionBuilds[i] = tempArray[i];
			FAssertMsg(m_piPromotionBuilds[i] < GC.getNumPromotionInfos() ,"Out of Bounds Array Melding");
			FAssertMsg(m_piPromotionBuilds[i] >= 0 ,"Out of Bounds Array Melding");
		}
		m_iNumPromotionBuilds = iGoalSize;
		SAFE_DELETE_ARRAY(tempArray);
	}
/*************************************************************************************************/
/**	Workers Paradise						END													**/
/*************************************************************************************************/
/*************************************************************************************************/
/**	Workers Paradise						01/08/10											**/
/**																								**/
/**							Allows promotions to affect build orders							**/
/*************************************************************************************************/
	bool bValid = true;
	if (!(m_pUnitInfo->getBuilds(eBuild)))
	{
		bValid = false;
		for (int iJ=0;iJ<GC.getNumPromotionInfos();iJ++)
		{
			if (isHasPromotion((PromotionTypes)iJ))
			{				
				int iNumPromotionBuilds = GC.getPromotionInfo((PromotionTypes)iJ).getNumPromotionBuilds();
				if (iNumPromotionBuilds > 0)
				{			
					for (int iI=0; iI< iNumPromotionBuilds; iI++)
					{
						if (GC.getPromotionInfo((PromotionTypes)iJ).getPromotionBuilds(iI) == eBuild)
						{
							bValid = true;
						}
					}
				}
			}
		}
		if (!bValid)
		{
			return false;
		}
	}
/*************************************************************************************************/
/**	Workers Paradise						END													**/
/*************************************************************************************************/
 
Woo! Thank you! That was EXACTLY the problem. Copied the code used for 'PrereqUnitTypesNOTOnTile', and everything works awesome. Would post screenies but I need to add it to the display first. :lol:

Added the following code to Combat1 for testing (shows syntax as well):
Code:
<PromotionBuilds>
    <PromotionBuild>BUILD_FORT</PromotionBuild>
</PromotionBuilds>

As soon as I added the promotion to a unit, the buildorder came up. Of course, it was on a warrior and so had an inordinately long build time, but adding Dwarven dropped it quite a bit (+%, not a flat rate, so still 100 or so turns.... Better than nearly infinite, though. :lol:)

Again, thank you for the help. :goodjob:

Edit: Actually, one last question. What exactly is the issue with giving units a buildorder but NOT allowing them to build a road? Why/how does that cause AI issues, and is it possible to fix it?
 
There is a section in the code where it will check if the unit is able to improve a tile, and then send them to the tile to improve it. But the unit arrives at the tile, finishing the mission it was on, then checks for orders again. The code sees that it can improve the tile it is on, but that tile isn't connected, so has them connect it as the priority before improving it. Should be easily fixed, IF that is the only location with the issue. Not sure if it should ever be called for a unit which isn't on UNITAI_WORKER though, so may not be an issue for you. If you do allow non-workers to gain build orders, you'll want to make a hook in their AI code to allow for them to go and build something when there is no better action, or make them prioritize choosing to defend workers (so that they can assist and get things done faster)
 
There is a section in the code where it will check if the unit is able to improve a tile, and then send them to the tile to improve it. But the unit arrives at the tile, finishing the mission it was on, then checks for orders again. The code sees that it can improve the tile it is on, but that tile isn't connected, so has them connect it as the priority before improving it. Should be easily fixed, IF that is the only location with the issue. Not sure if it should ever be called for a unit which isn't on UNITAI_WORKER though, so may not be an issue for you. If you do allow non-workers to gain build orders, you'll want to make a hook in their AI code to allow for them to go and build something when there is no better action, or make them prioritize choosing to defend workers (so that they can assist and get things done faster)

I'm not planning on allowing non-workers build orders, I just wanted to see if the problem with roads still existed or not mainly. Or rather, Grey Fox did. :lol:

Second question: How would I go about removing builds from the list? In the case of Fortification Expert, it allows you to build a Castle... but you can still also build a fort, which is either good or bad. Not sure if I want to remove the build or not, but atm I don't know how to approach it at all. :lol:

Aside from a second loop checking to see if the improvement built by the promotion build is an upgrade to the native build, and if so blocking it... But there has to be a better way. Could always clone the entire PromotionBuilds tag as a PromotionNotBuilds, allowing you to specify which are added and which are removed....
 
hi,

i have a few questions again:

first i saw some code in the CustomFunctions.py file:
Spoiler for full code :

Code:
	def exploreLairGoodEquipment(self, caster):
		iPlayer = caster.getOwner()
		pPlot = caster.plot()
		pPlayer = gc.getPlayer(caster.getOwner())
		lList = []
		if pPlayer.canReceiveGoody(pPlot, gc.getInfoTypeForString('GOODY_EXPLORE_LAIR_ITEM_HEALING_SALVE'), caster):
			lList = ['ITEM_HEALING_SALVE']
		if pPlot.isHills():
			if pPlayer.canReceiveGoody(pPlot, gc.getInfoTypeForString('GOODY_MARNOK_CLIMBING_KIT_RECON'), caster):
				lList = lList + ['PROMOTION_CLIMBING_KIT_RECON']
		if pPlot.getTerrainType() == gc.getInfoTypeForString('TERRAIN_DESERT'):
			if pPlayer.canReceiveGoody(pPlot, gc.getInfoTypeForString('GOODY_MARNOK_DESERT_GEAR_RECON'), caster):
				lList = lList + ['PROMOTION_DESERT_GEAR_RECON']
		if pPlot.getTerrainType() == gc.getInfoTypeForString('TERRAIN_SNOW') or pPlot.getTerrainType() == gc.getInfoTypeForString('TERRAIN_TUNDRA'):
			if pPlayer.canReceiveGoody(pPlot, gc.getInfoTypeForString('GOODY_MARNOK_SNOW_GEAR_RECON'), caster):
				lList = lList + ['PROMOTION_SNOW_GEAR_RECON']
		if pPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_FOREST') or pPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_FOREST_ANCIENT'):
			if pPlayer.canReceiveGoody(pPlot, gc.getInfoTypeForString('GOODY_MARNOK_WOODS_GEAR_RECON'), caster):
				lList = lList + ['PROMOTION_WOODS_GEAR_RECON']
		if pPlayer.canReceiveGoody(pPlot, gc.getInfoTypeForString('GOODY_MARNOK_FINE_KIT_RECON'), caster):
			lList = lList + ['PROMOTION_FINE_KIT_RECON']
		if pPlayer.canReceiveGoody(pPlot, gc.getInfoTypeForString('GOODY_MARNOK_MANTRAPS_RECON'), caster):
			lList = lList + ['PROMOTION_MANTRAPS_RECON']

		if len(lList) > 0:
			sGoody = lList[CyGame().getSorenRandNum(len(lList), "Pick Goody")-1]
			if sGoody == 'ITEM_HEALING_SALVE':
				pPlayer.receiveGoody(pPlot, gc.getInfoTypeForString('GOODY_EXPLORE_LAIR_ITEM_HEALING_SALVE'), caster)
				return 100
			if sGoody == 'PROMOTION_CLIMBING_KIT_RECON':
				pPlayer.receiveGoody(pPlot, gc.getInfoTypeForString('GOODY_MARNOK_CLIMBING_KIT_RECON'), caster)
				return 100
			if sGoody == 'PROMOTION_DESERT_GEAR_RECON':
				pPlayer.receiveGoody(pPlot, gc.getInfoTypeForString('GOODY_MARNOK_DESERT_GEAR_RECON'), caster)
				return 100
			if sGoody == 'PROMOTION_FINE_KIT_RECON':
				pPlayer.receiveGoody(pPlot, gc.getInfoTypeForString('GOODY_MARNOK_FINE_KIT_RECON'), caster)
				return 100
			if sGoody == 'PROMOTION_MANTRAPS_RECON':
				pPlayer.receiveGoody(pPlot, gc.getInfoTypeForString('GOODY_MARNOK_MANTRAPS_RECON'), caster)
				return 100
			if sGoody == 'PROMOTION_SNOW_GEAR_RECON':
				pPlayer.receiveGoody(pPlot, gc.getInfoTypeForString('GOODY_MARNOK_SNOW_GEAR_RECON'), caster)
				return 100
			if sGoody == 'PROMOTION_WOODS_GEAR_RECON':
				pPlayer.receiveGoody(pPlot, gc.getInfoTypeForString('GOODY_MARNOK_WOODS_GEAR_RECON'), caster)
				return 100
		return self.exploreLairGood(caster)
here is the line i'm interested in:
sGoody = lList[CyGame().getSorenRandNum(len(lList), "Pick Goody")-1]

for example you have a lList with 5 elements the len(lList) will return you a 5.
and i always thought when you use CyGame().getSorenRandNum( 5, "stuff") it will return a number between 0 and 4. isn't that so?
why the -1 at the end of the sGoody code? (maybe it's working because lList[-1] means the last element but i don't know for certain)

but more important:
  1. a long time ago i was able to "create" some kind of CvPythonExtensions.hlp.txt file.
    how can i create such a file with ff - was it some ini file entry?...

  2. and i have a problem with some code of mine:
    edit: nevermind - found the error

thx
 
That's one which had also confused me. Oddly enough, removing the -1 in a few cases breaks the code. In other cases it isn't used at all. One guy more familiar with python explained that the -1 will point to the last element of the list with python though, so it doesn't actually break anything to use it this way. As for why it will break when you DON'T use it this way... no clue.
 
So, I was attempting to add a new racial promotion for Kuriotate recon units. Rather than make UU's, I wanted to make the racial autoacquire... Only to find that's impossible? Any reason why?

As it is, I made a clone of the promotion that is NOT a race, and decays into the racial in one turn... Worked fine that way, but looks odd.
 
Not precisely certain why it would fail. Did you try removing the bRace flag and seeing if that was an issue? I believe there may be a block in ::canAcquirePromotion which specifically forbids you to buy any bRace promo with XP, which would also wind up blocking AA.
 
Not precisely certain why it would fail. Did you try removing the bRace flag and seeing if that was an issue? I believe there may be a block in ::canAcquirePromotion which specifically forbids you to buy any bRace promo with XP, which would also wind up blocking AA.

Yes, it's specifically the bRace flag. Like I said, the dummy promo has the same exact effect as the race, it just lacks that one tag.

I don't see why we need to block the acquisition of racial promotions, as they can't be taken on level up anyway; Only time it would be possible is when it's intended. Any issues you can think of from removing it?

Also, having some issues with allowing trade without routes over desert terrain for Malakim; Generally works fine, but when a resource is on a hill, and not immediately adjacent to the city, it doesn't connect up. Any idea why? Fairly sure it has something to do with the plottype, but I'm not sure where that would be in the code.
 
Not sure if that block exists, just makes some sense for it to be there since racial promotions are "special" though MinLevel accomplishes the task just fine. So if that block DOES exist, then yes, feel free to remove it, then make certain to test and ensure you cannot purchase any racial promotions afterward.

Not sure on the no route thing. Are you talking trade-routes (show up in city and give you commerce) or connection routes (grant you resources from map)? Two very different things, your description sounds like the second (easier) option, so I'd assume it is that one. Not sure why a hill would refuse to behave like any other TERRAIN_DESERT item. Add a "Terrain Type" list to chipotle popup for tiles to make sure that a hill/desert still shows desert, and not hill.
 
Top Bottom