Techs: how to make one exclusive to another

zulu9812

The Newbie Nightmare
Joined
Jan 29, 2002
Messages
6,388
Location
Athens of the North
What I'd like to do is change the way that the tech tree works so that once you've researched tech A, you can't research tech B. The idea is that this would create divergent tech trees and, eventually, 2 different civs that started at the same point but digressed on 2 different paths would end up being very different. Whilst BtS can have OR prerequisites (and I think Kael did a tutorial for bringing in AND prereqs, for promotions), there doesn't appear to be a facility for NOT prerequisite: i.e. you must NOT have researched tech A in order to research tech B.

If possible, I'd like to do this with the SDK. This is for 2 reasons: it's neater than Python, and I hate programming in Python - so many things can go wrong! Can anyone help me out with the code that I would need to write to do this?
 
I like your idea. :thumbsup:

Maybe you take a look at cvGameUtils.py first. "def canresearch" and "def cannotresearch" sounds like one of them could make such things working.


I am no big mod-Guru. But i guess the Python file just calls the C++methods, so it doesnt make a real difference to modding the C++files (to me at least. Plz disagree if i am totally wrong).
And you dont need to compile everytime. ;)

The code shouldn't be that hard. Just some "if-functions", imho.

As i like the idea of exclusive techs, i will give cyGameUtils.py a try, in case you need an example. :salute:
 
@Burlwood:
The way you are proposing would work but it's not very clean. You would have to hard-code parts of your tech tree instead having everything in xml. That's the quick & dirty way.
More neat is what zulu9812 mentioned, a new xml tag with a NOT prereq. It's just a little SDK coding, shouldn't be too hard.
More complicated is to let the AI decide which branch would be the better decision in it's situation.

I like the idea. Last year I mentioned somewhat similar in the german community but the people wheren't very enthusiastic.
You will have to make sure that important units and buildings are accessable in both branches of the tree. Maybe units and buildings could differ a bit on the branches (but can still have the same names). One branch could be more aggressive and another branch could have advantages in civil services.
Don't forget that two branches maybe can get together later in the game again.

Matze
 
Thanks for the replies. My idea is for a future era where a bunch of new technology can be discovered, but the civs have to make a choice. Then by the time a 2nd future era comes around, the choices they will have made will have lead them down wildly differing paths.

So far, I've come up with 3 so-called 'super-civs':

United Earth Government aka "EarthGov"
A collective of nations formed in the aftermath of the nuclear terrorist attacks of the 21st century. They are primarily concerned with law and order, but only for their own number, and are prepared to sacrifice those who cannot or will not be brought under their banner. Their economy is based around corporations, and their military is based around the rapid deployment of genetically-bread super soldiers and mechanised walkers from orbit to any point of the globe.

Public License and Authority for a NEw Terra aka "Planet"

Most commonly expressed by their abbreviation, Planet is a collective of former nation-states who came to the conclusion that Mother Nature could no longer sustain the rampant devestation wrought by Man's aggressively coonsuming behaviour. This came to a head after a number of decades, when large tracts of formerly fertile land had turned to desert and vast portions of the ice cap had melted. Sea levels rose, and low-lying countries simply became uninhabitable. Then the food riots started and many countries simply became ungovernable. At this point, Planet was formerly created - with the stated goal of doing anything and everything to arrest and reverse this ecological devestation. This brings them into conflict with the most polluting countries, who also happen to be the ones doing the least about it. For the first time, wars were fought for control of water supplies and a nation's carbon footprint.

Planet are ultimately pacifists and shy away from full-on conflict. Whilst they do maintain a defence force, most of their attacks on pollutant countries are done by spies and agents. The foremost of these is the dreaded Eco-Ranger. Developments in nano-technology allow the Eco_Ranger to enter a city and activate a nano device, which then releases self-replicating nano bots, which eventually consume an entire city, wiping it from the map. This same nano-technology gives Planet civs the ability to terraform areas struck by global warming.

Robotic Autonomous Negotiated Networks aka "RANN"

The dawn of the 22nd century promised a new age, when technology freed ordinary citizens from the burden of labour. Genetics had advanced to such a point that a new race was created: a race in Man's image, but yet not Man. These were the Life Model Decoys, who simulated human physiology in almost every way, yet possessed no consciousness. This slave race performed all menial tasks in society, even the so-called "McJob" and some roles in the military. Humanity relished in its age of luxury.

But the LMDs were not perfect: their lack of higher brain functions meant that they could operate complex machinery. Scientists changed this when they invented the jackport, which was fitted to all new LMDs and allowed them to interfact directly with machines and control them via the medulla and lower brain cortex, in much the same way that those organs control breathing and heart rate. Finally, all LMDs were connected to one another when they 'jacked in' to the internet. This was meant to improve their operational efficiency, but it proved to be humanity's undoing. The LMD 'race' were connected to the some total of human knowledge. After a number of years, at the same moment, all LMDs became self-aware and a massive rebellion was started. EarthGov moved to crush the revolt, and many LMDs were killed - most slain by their brothers in the military. Those that could escape did so, and fled to countries not under EarthGov control. Seeing the potential of these cyborg organisms, those rebel nations took the LMDs and gave them a place, even being fitted with jackports themselves. Whole nations of LMDs were created and they unified to form Robotic Autonomous Negotiated Networks.

RANN are cold, ruthless and efficient. They remember slavery and brutality at the hands of Man. They see themselves as superior to humanity, their ability to interface with machines placing them next in the evolutionary chain. Whilst their new homes have, to a large extent, become totally colonised (with many natural-born humans opting for the jackports), the leaders of these nations remain aloof from their new citizens. However grateful they are for sanctuary, much longer the LMDs will tolerate being ruled by humans remains to be seen...

So, that's the story so far. There's more I want to do, coding-wise. But I think that I need to get this done first. This is the most basic step, so any help would be much appreciated.
 
I checked the link the day you posted it.

As you mentioned, i tried to make an XML-entry opposite to the prereqandtech.
Of course i failed, because the tutorial doesn't cover this kind of entry.

Now i noticed that a more simple entry, like in the tutorial, also could work.

Even the existing entries, like one of the prereqandtechs, could be used.
I used one of the prereqandtechs for an python-loop to make exclusive buildings. Maybe i will transfer it to C++ to get into SDK-modding some day.

But if the thread starter is still looking for a working solution, i could turn my "exclusive-buildings" into "exclusive-techs" and post it here.
 
I am no big mod-Guru. But i guess the Python file just calls the C++methods, so it doesnt make a real difference to modding the C++files (to me at least. Plz disagree if i am totally wrong).
Python is painfully slow compared to C++ so you should restrict Python use to cases that don't need to be applied often or over a large scope. For instance if you want to iterrate over every city in the game do it in the DLL as it will take 0.01 seconds rather than with python which will take 10.0 seconds :)

For exclusive prereqs you have to impliment a couple of new steps, mainly just to get it reading the data from the XML file. Remember, all you need to do with XML is read the data and store it in a member variable (m_whatever which should be private/protected). That step shoudl be the same regardless of the intended use of the XML data so any number of existing tutorials or mods that add XML values will work, you just have to put it in the right class (techs, not buildings for example).

Once you have the data available the canReasearch method only needs to look at the current tech prereqs, if it finds a 'not' prereq make sure that tech isn't known otherwise proceed as it always has. You'll also need to decide if you want to expose your variable(s) to python, if so there's a few extra steps needed. If you wrap your check into the canReaserch method you won't need to write anything fancy. For instance here is the canResearch method in CvPlayer.cpp:

Spoiler :
Code:
bool CvPlayer::canResearch(TechTypes eTech, bool bTrade) const
{
	bool bFoundPossible;
	bool bFoundValid;
	int iI;

	if(GC.getUSE_CAN_RESEARCH_CALLBACK())
	{
		CyArgsList argsList;
		argsList.add(getID());
		argsList.add(eTech);
		argsList.add(bTrade);
		long lResult=0;
		gDLL->getPythonIFace()->callFunction(PYGameModule, "canResearch", argsList.makeFunctionArgs(), &lResult);
		if (lResult == 1)
		{
			return true;
		}
	}

	if (!isResearch() && getAdvancedStartPoints() < 0)
	{
		return false;
	}

	if (GET_TEAM(getTeam()).isHasTech(eTech))
	{
		return false;
	}

	bFoundPossible = false;
	bFoundValid = false;

	for (iI = 0; iI < GC.getNUM_OR_TECH_PREREQS(); iI++)
	{
		TechTypes ePrereq = (TechTypes)GC.getTechInfo(eTech).getPrereqOrTechs(iI);
		if (ePrereq != NO_TECH)
		{
			bFoundPossible = true;

			if (GET_TEAM(getTeam()).isHasTech(ePrereq))
			{
				if (!bTrade || GC.getGameINLINE().isOption(GAMEOPTION_NO_TECH_BROKERING) || !GET_TEAM(getTeam()).isNoTradeTech(ePrereq))
				{
					bFoundValid = true;
					break;
				}
			}
		}
	}

	if (bFoundPossible && !bFoundValid)
	{
		return false;
	}

	for (iI = 0; iI < GC.getNUM_AND_TECH_PREREQS(); iI++)
	{
		TechTypes ePrereq = (TechTypes)GC.getTechInfo(eTech).getPrereqAndTechs(iI);
		if (ePrereq != NO_TECH)
		{
			if (!GET_TEAM(getTeam()).isHasTech(ePrereq))
			{
				return false;
			}
			
			if (bTrade && !GC.getGameINLINE().isOption(GAMEOPTION_NO_TECH_BROKERING) && GET_TEAM(getTeam()).isNoTradeTech(ePrereq))
			{
				return false;
			}
		}
	}

	if (!canEverResearch(eTech))
	{
		return false;
	}

	return true;
}

You simply need to add your own loop like this:
Code:
	for (iI = 0; iI < GC.getNUM_NOT_TECH_PREREQS(); iI++)
	{ // You'll need this new method for GC's NUM_NOT_TECH_PREREQS
		TechTypes ePrereq = (TechTypes)GC.getTechInfo(eTech).getPrereqNotTechs(iI); // And you need this new method
		if (ePrereq != NO_TECH)
		{
			if (GET_TEAM(getTeam()).isHasTech(ePrereq))
			{ // Read the above line carefully, removed '!'
				return false;
			}
			
			if (bTrade && !GC.getGameINLINE().isOption(GAMEOPTION_NO_TECH_BROKERING) && GET_TEAM(getTeam()).isNoTradeTech(ePrereq))
			{ // You should probably make any tech used as a 'not' prereq untradable, you'll still have issues with teams
				return false;
			}
		}
	}
I would put this check BEFORE the check for both the 'and' & or 'prereqs' so you could use all thre types in a technology if you wanted and it would work as expected. The two new methods you'd need would be easy copies of the existing methods for the and/or prereqs.

I'd add it to my own mod and simply give you the code but I have no use for it. :)
 
Personally I would just put it in as a python cannotResearch check as Burlwood suggested. Its 3 lines of code (if the tech being checked = techa, if the player already has techb, return true).

But it would definitly work as an SDK mod too, and be more flexible since once all initial work is all done its just an xml change to use it on different techs. To do it as an SDK mod you will need to modify the xml schema for the new attribute, edit cvinfos.cpp and cvinfos.h to read the attributes (including a read in pass2 since its self referencing), as well as the change in canResearch as Seven05 mentioned.

From a design perspective this idea could be problematic to a player. Techs are shared by the team, so a teammember who researches tech A could block the player from being able to select tech B. Likewise getting the tech from a goody hut, great person or in trade could spil the players plans to research in a different direction.
 
Oh yeah, it opens up a whole new big 'ole can of worms :)

What happens if I research Tech A, you research Tech B and then we form a permanent alliance? It's pretty easy to see why there isn't a not prereq already. It would be infinitely easier to impliment it if the not prereq was something other than another tech, for instance having a 'Synthetic Fuels' technology that can only be researched if you DON'T have access to oil.
 
From a design perspective, that's not such a big problem. This system of branching tech trees isn't going to happen until late in the game, when all the goody huts are already discovered. Additionally, I'm hoping to get players on different branches have a diplomatic penalty with each other, which would probably preclude a permanent alliance. If push comes to shove, permanent alliances could be disabled altogether.
 
But it would definitly work as an SDK mod too, and be more flexible since once all initial work is all done its just an xml change to use it on different techs. To do it as an SDK mod you will need to modify the xml schema for the new attribute, edit cvinfos.cpp and cvinfos.h to read the attributes (including a read in pass2 since its self referencing), as well as the change in canResearch as Seven05 mentioned.

As far as cvinfos.cpp goes, do you mean this section of code, under // FUNCTION: ~CvTechInfo()?

Spoiler :
Code:
int CvTechInfo::getFlavorValue(int i) const			
{
	FAssertMsg(i < GC.getNumFlavorTypes(), "Index out of bounds");
	FAssertMsg(i > -1, "Index out of bounds");
	return m_piFlavorValue ? m_piFlavorValue[i] : -1;
}

int CvTechInfo::getPrereqOrTechs(int i) const	
{
	return m_piPrereqOrTechs ? m_piPrereqOrTechs[i] : -1;
}

int CvTechInfo::getPrereqAndTechs(int i) const
{
	return m_piPrereqAndTechs ? m_piPrereqAndTechs[i] : -1;
}

bool CvTechInfo::isCommerceFlexible(int i) const
{
	FAssertMsg(i < NUM_COMMERCE_TYPES, "Index out of bounds");
	FAssertMsg(i > -1, "Index out of bounds");
	return m_pbCommerceFlexible ? m_pbCommerceFlexible[i] : false;

Would I add the NOT prereq. before PrereqOrTechs, so the code becomes like this?

Spoiler :
Code:
int CvTechInfo::getFlavorValue(int i) const			
{
	FAssertMsg(i < GC.getNumFlavorTypes(), "Index out of bounds");
	FAssertMsg(i > -1, "Index out of bounds");
	return m_piFlavorValue ? m_piFlavorValue[i] : -1;
}

[B]nt CvTechInfo::getPrereqNotTechs(int i) const	
{
	return m_piPrereqNotTechs ? m_piPrereqNotTechs[i] : -1;
}[/B]

int CvTechInfo::getPrereqOrTechs(int i) const	
{
	return m_piPrereqOrTechs ? m_piPrereqOrTechs[i] : -1;
}

int CvTechInfo::getPrereqAndTechs(int i) const
{
	return m_piPrereqAndTechs ? m_piPrereqAndTechs[i] : -1;
}

bool CvTechInfo::isCommerceFlexible(int i) const
{
	FAssertMsg(i < NUM_COMMERCE_TYPES, "Index out of bounds");
	FAssertMsg(i > -1, "Index out of bounds");
	return m_pbCommerceFlexible ? m_pbCommerceFlexible[i] : false;

Am I just looking for instances of PrereqOrTechs and copying and pasting that code entry immediately before it, changing it PrereqNotTechs? For example, further down, under // CvEventTriggerInfo, we see this section of code:

Spoiler :
Code:
int CvEventTriggerInfo::getPrereqEvent(int i) const
{
	return m_aiPrereqEvents[i];
}

int CvEventTriggerInfo::getNumPrereqEvents() const
{
	return (int)m_aiPrereqEvents.size();
}

int CvEventTriggerInfo::getPrereqOrTechs(int i) const	
{
	return m_aiPrereqOrTechs[i];
}

int CvEventTriggerInfo::getNumPrereqOrTechs() const	
{
	return (int)m_aiPrereqOrTechs.size();
}

int CvEventTriggerInfo::getPrereqAndTechs(int i) const
{
	return m_aiPrereqAndTechs[i];
}

int CvEventTriggerInfo::getNumPrereqAndTechs() const
{
	return (int)m_aiPrereqAndTechs.size();
}

which then becomes

Spoiler :
Code:
int CvEventTriggerInfo::getPrereqEvent(int i) const
{
	return m_aiPrereqEvents[i];
}

int CvEventTriggerInfo::getNumPrereqEvents() const
{
	return (int)m_aiPrereqEvents.size();
}

[B]int CvEventTriggerInfo::getPrereqNotTechs(int i) const	
{
	return m_aiPrereqNotTechs[i];
}

int CvEventTriggerInfo::getNumPrereqNotTechs() const	
{
	return (int)m_aiPrereqNotTechs.size();
}[/B]

int CvEventTriggerInfo::getPrereqOrTechs(int i) const	
{
	return m_aiPrereqOrTechs[i];
}

int CvEventTriggerInfo::getNumPrereqOrTechs() const	
{
	return (int)m_aiPrereqOrTechs.size();
}

int CvEventTriggerInfo::getPrereqAndTechs(int i) const
{
	return m_aiPrereqAndTechs[i];
}

int CvEventTriggerInfo::getNumPrereqAndTechs() const
{
	return (int)m_aiPrereqAndTechs.size();
}

However, I can't see how the OR and AND prereqs. differ from each other - it's as though the same code is being used for both. So how does that make those two different from each other, and what wold actually make the NOT prereq actually work?
 
What would actually make the not code work is the code seven05 recommended in canResearch. The CvInfos stuff is just to allow the SDK to read XML and setup the attributes.

You can use the Or or AND req's as the model for your NOT work, but it will be more than just those functions, you will need to tear down the array, read the values, etc.
 
You only missed a couple, you have to define NUM_NOT_TECH_PREREQS. You also need to get the prereqs, look in your first code block that you posted and insert it there. And you need the XML read, I didn't see that in there. With the code you posted you've done most of the 'hard' stuff, the example I posted earlier was the easy stuff. :) In fact, once you have that done you can skip my code example entirely and do the final check in python, it would be a little wierd to do it that way though which is why I posted an SDK example.

Does espionage use canResearch? Hopefully it does, if not you'll need to add a catch in there so you can't steal a tech you shouldn't be able to get.
 
OK, thanks for all your help. I'm just trying to organise the process in my head.

XML preparation

The first thing I need to do is add to CIV4TechnologiesSchema.xml. Going by what's been said, this is what I think I need:

Spoiler :
Code:
<ElementType name="PrereqTech" content="textOnly"/>
	<ElementType name="OrPreReqs" content="eltOnly">
		<element type="PrereqTech" minOccurs="0" maxOccurs="*"/>
	</ElementType>
	<ElementType name="AndPreReqs" content="eltOnly">
		<element type="PrereqTech" minOccurs="0" maxOccurs="*"/>
	</ElementType>

becomes

Spoiler :
Code:
<ElementType name="PrereqTech" content="textOnly"/>
[B]	<ElementType name="NotPreReqs" content="eltOnly">
		<element type="PrereqTech" minOccurs="0" maxOccurs="*"/>
	</ElementType>[/B]
	<ElementType name="OrPreReqs" content="eltOnly">
		<element type="PrereqTech" minOccurs="0" maxOccurs="*"/>
	</ElementType>
	<ElementType name="AndPreReqs" content="eltOnly">
		<element type="PrereqTech" minOccurs="0" maxOccurs="*"/>
	</ElementType>

The reason for that is so that a single tech could use all 3 prereq types, right?

After that, I need to add the NotPreReq tag to each tech in CIV4TechInfos.xml, e.g.
Spoiler :
Code:
		<TechInfo>
			<Type>TECH_MYSTICISM</Type>
			<Description>TXT_KEY_TECH_MYSTICISM</Description>
			<Civilopedia>TXT_KEY_TECH_MYSTICISM_PEDIA</Civilopedia>
			<Help/>
			<Strategy>TXT_KEY_TECH_MYSTICISM_STRATEGY</Strategy>
			<Advisor>ADVISOR_RELIGION</Advisor>
			<iAIWeight>0</iAIWeight>
			<iAITradeModifier>0</iAITradeModifier>
			<iCost>50</iCost>
			<iAdvancedStartCost>100</iAdvancedStartCost>
			<iAdvancedStartCostIncrease>0</iAdvancedStartCostIncrease>
			<Era>ERA_ANCIENT</Era>
			<FirstFreeUnitClass>NONE</FirstFreeUnitClass>
			<iFeatureProductionModifier>0</iFeatureProductionModifier>
			<iWorkerSpeedModifier>0</iWorkerSpeedModifier>
			<iTradeRoutes>0</iTradeRoutes>
			<iHealth>0</iHealth>
			<iHappiness>0</iHappiness>
			<iFirstFreeTechs>0</iFirstFreeTechs>
			<iAsset>8</iAsset>
			<iPower>0</iPower>
			<bRepeat>0</bRepeat>
			<bTrade>1</bTrade>
			<bDisable>0</bDisable>
			<bGoodyTech>1</bGoodyTech>
			<bExtraWaterSeeFrom>0</bExtraWaterSeeFrom>
			<bMapCentering>0</bMapCentering>
			<bMapVisible>0</bMapVisible>
			<bMapTrading>0</bMapTrading>
			<bTechTrading>0</bTechTrading>
			<bGoldTrading>0</bGoldTrading>
			<bOpenBordersTrading>0</bOpenBordersTrading>
			<bDefensivePactTrading>0</bDefensivePactTrading>
			<bPermanentAllianceTrading>0</bPermanentAllianceTrading>
			<bVassalTrading>0</bVassalTrading>
			<bBridgeBuilding>0</bBridgeBuilding>
			<bIrrigation>0</bIrrigation>
			<bIgnoreIrrigation>0</bIgnoreIrrigation>
			<bWaterWork>0</bWaterWork>
			<iGridX>1</iGridX>
			<iGridY>11</iGridY>
			<DomainExtraMoves/>
			<CommerceFlexible/>
			<TerrainTrades/>
			<bRiverTrade>0</bRiverTrade>
			<Flavors>
				<Flavor>
					<FlavorType>FLAVOR_RELIGION</FlavorType>
					<iFlavor>9</iFlavor>
				</Flavor>
				<Flavor>
					<FlavorType>FLAVOR_GOLD</FlavorType>
					<iFlavor>1</iFlavor>
				</Flavor>
				<Flavor>
					<FlavorType>FLAVOR_CULTURE</FlavorType>
					<iFlavor>8</iFlavor>
				</Flavor>
				<Flavor>
					<FlavorType>FLAVOR_GROWTH</FlavorType>
					<iFlavor>2</iFlavor>
				</Flavor>
			</Flavors>
                        [B]<NotPreReqs/>[/B]
			<OrPreReqs/>
			<AndPreReqs/>
			<Quote>TXT_KEY_TECH_MYSTICISM_QUOTE</Quote>
			<Sound>AS2D_TECH_MYSTICISM</Sound>
			<SoundMP>AS2D_TECH_MP_MYSTICISM</SoundMP>
			<Button>,Art/Interface/Buttons/TechTree/Mysticism.dds,Art/Interface/Buttons/TechTree_Atlas.dds,4,11</Button>
		</TechInfo>

Defining the new tag in the SDK

I then need to open up CvPlayer.cpp and search for "bool CvPlayer::isResearch() const", which takes me to this section:

Spoiler :
Code:
bool CvPlayer::isResearch() const
{
	if(GC.getUSE_IS_PLAYER_RESEARCH_CALLBACK())
	{
		CyArgsList argsList;
		long lResult;
		argsList.add(getID());
		lResult = 1;
		gDLL->getPythonIFace()->callFunction(PYGameModule, "isPlayerResearch", argsList.makeFunctionArgs(), &lResult);
		if (lResult == 0)
		{
			return false;
		}
	}

	if (!isFoundedFirstCity())
	{
		return false;
	}

	return true;
}


bool CvPlayer::canEverResearch(TechTypes eTech) const
{
	if (GC.getTechInfo(eTech).isDisable())
	{
		return false;
	}

	if (GC.getCivilizationInfo(getCivilizationType()).isCivilizationDisableTechs(eTech))
	{
		return false;
	}

	if(GC.getUSE_CANNOT_RESEARCH_CALLBACK())
	{
		CyArgsList argsList;
		argsList.add(getID());
		argsList.add(eTech);
		argsList.add(false);
		long lResult=0;
		gDLL->getPythonIFace()->callFunction(PYGameModule, "cannotResearch", argsList.makeFunctionArgs(), &lResult);
		if (lResult == 1)
		{
			return false;
		}
	}

	return true;
}


bool CvPlayer::canResearch(TechTypes eTech, bool bTrade) const
{
	bool bFoundPossible;
	bool bFoundValid;
	int iI;

	if(GC.getUSE_CAN_RESEARCH_CALLBACK())
	{
		CyArgsList argsList;
		argsList.add(getID());
		argsList.add(eTech);
		argsList.add(bTrade);
		long lResult=0;
		gDLL->getPythonIFace()->callFunction(PYGameModule, "canResearch", argsList.makeFunctionArgs(), &lResult);
		if (lResult == 1)
		{
			return true;
		}
	}

	if (!isResearch() && getAdvancedStartPoints() < 0)
	{
		return false;
	}

	if (GET_TEAM(getTeam()).isHasTech(eTech))
	{
		return false;
	}

	bFoundPossible = false;
	bFoundValid = false;

	for (iI = 0; iI < GC.getNUM_OR_TECH_PREREQS(); iI++)
	{
		TechTypes ePrereq = (TechTypes)GC.getTechInfo(eTech).getPrereqOrTechs(iI);
		if (ePrereq != NO_TECH)
		{
			bFoundPossible = true;

			if (GET_TEAM(getTeam()).isHasTech(ePrereq))
			{
				if (!bTrade || GC.getGameINLINE().isOption(GAMEOPTION_NO_TECH_BROKERING) || !GET_TEAM(getTeam()).isNoTradeTech(ePrereq))
				{
					bFoundValid = true;
					break;
				}
			}
		}
	}

	if (bFoundPossible && !bFoundValid)
	{
		return false;
	}

	for (iI = 0; iI < GC.getNUM_AND_TECH_PREREQS(); iI++)
	{
		TechTypes ePrereq = (TechTypes)GC.getTechInfo(eTech).getPrereqAndTechs(iI);
		if (ePrereq != NO_TECH)
		{
			if (!GET_TEAM(getTeam()).isHasTech(ePrereq))
			{
				return false;
			}
			
			if (bTrade && !GC.getGameINLINE().isOption(GAMEOPTION_NO_TECH_BROKERING) && GET_TEAM(getTeam()).isNoTradeTech(ePrereq))
			{
				return false;
			}
		}
	}

	if (!canEverResearch(eTech))
	{
		return false;
	}

	return true;
}


TechTypes CvPlayer::getCurrentResearch() const
{
	CLLNode<TechTypes>* pResearchNode;

	pResearchNode = headResearchQueueNode();

	if (pResearchNode != NULL)
	{
		return pResearchNode->m_data;
	}
	else
	{
		return NO_TECH;
	}
}


bool CvPlayer::isCurrentResearchRepeat() const
{
	TechTypes eCurrentResearch;

	eCurrentResearch = getCurrentResearch();

	if (eCurrentResearch == NO_TECH)
	{
		return false;
	}

	return GC.getTechInfo(eCurrentResearch).isRepeat();
}


bool CvPlayer::isNoResearchAvailable() const
{
	int iI;

	if (getCurrentResearch() != NO_TECH)
	{
		return false;
	}

	for (iI = 0; iI < GC.getNumTechInfos(); iI++)
	{
		if (canResearch((TechTypes)iI))
		{
			return false;
		}
	}

	return true;
}


int CvPlayer::getResearchTurnsLeft(TechTypes eTech, bool bOverflow) const
{
	int iTurnsLeft = getResearchTurnsLeftTimes100(eTech, bOverflow);

	iTurnsLeft = (iTurnsLeft + 99) / 100; // round up

	return max(1, iTurnsLeft);
}

int CvPlayer::getResearchTurnsLeftTimes100(TechTypes eTech, bool bOverflow) const
{
    int iResearchRate;
	int iOverflow;
	int iResearchLeft;
	int iTurnsLeft;
	int iI;

	iResearchRate = 0;
	iOverflow = 0;

	for (iI = 0; iI < MAX_PLAYERS; iI++)
	{
		if (GET_PLAYER((PlayerTypes)iI).isAlive())
		{
			if (GET_PLAYER((PlayerTypes)iI).getTeam() == getTeam())
			{
				if ((iI == getID()) || (GET_PLAYER((PlayerTypes)iI).getCurrentResearch() == eTech))
				{
					iResearchRate += GET_PLAYER((PlayerTypes)iI).calculateResearchRate(eTech);
					iOverflow += (GET_PLAYER((PlayerTypes)iI).getOverflowResearch() * calculateResearchModifier(eTech)) / 100;
				}
			}
		}
	}

	if (iResearchRate == 0)
	{
		return MAX_INT;
	}
	
	iResearchLeft = GET_TEAM(getTeam()).getResearchLeft(eTech);

	if (bOverflow)
	{
		iResearchLeft -= iOverflow;
	}
	
	iResearchLeft *= 100;

	iTurnsLeft = (iResearchLeft / iResearchRate);

	if (iTurnsLeft * iResearchRate < iResearchLeft)
	{
		++iTurnsLeft;
	}

	return max(1, iTurnsLeft);
    
    
}

Now, would I be correct in thinking that most of that section is not relevant to my purpose? That all I need to do is add in Seven05's code before GC.getNUM_OR_TECH_PREREQS(), e.g.

Spoiler :
Code:
	if (GET_TEAM(getTeam()).isHasTech(eTech))
	{
		return false;
	}

	bFoundPossible = false;
	bFoundValid = false;

	for (iI = 0; iI < GC.getNUM_OR_TECH_PREREQS(); iI++)
	{
		TechTypes ePrereq = (TechTypes)GC.getTechInfo(eTech).getPrereqOrTechs(iI);
		if (ePrereq != NO_TECH)
		{
			bFoundPossible = true;

			if (GET_TEAM(getTeam()).isHasTech(ePrereq))
			{
				if (!bTrade || GC.getGameINLINE().isOption(GAMEOPTION_NO_TECH_BROKERING) || !GET_TEAM(getTeam()).isNoTradeTech(ePrereq))
				{
					bFoundValid = true;
					break;
				}
			}
		}
	}

	if (bFoundPossible && !bFoundValid)
	{
		return false;
	}

	for (iI = 0; iI < GC.getNUM_AND_TECH_PREREQS(); iI++)
	{
		TechTypes ePrereq = (TechTypes)GC.getTechInfo(eTech).getPrereqAndTechs(iI);
		if (ePrereq != NO_TECH)
		{
			if (!GET_TEAM(getTeam()).isHasTech(ePrereq))
			{
				return false;
			}
			
			if (bTrade && !GC.getGameINLINE().isOption(GAMEOPTION_NO_TECH_BROKERING) && GET_TEAM(getTeam()).isNoTradeTech(ePrereq))
			{
				return false;
			}
		}
	}

becomes

Spoiler :
Code:
	if (GET_TEAM(getTeam()).isHasTech(eTech))
	{
		return false;
	}

	bFoundPossible = false;
	bFoundValid = false;

	[b]for (iI = 0; iI < GC.getNUM_NOT_TECH_PREREQS(); iI++)
	{ // You'll need this new method for GC's NUM_NOT_TECH_PREREQS
		TechTypes ePrereq = (TechTypes)GC.getTechInfo(eTech).getPrereqNotTechs(iI); // And you need this new method
		if (ePrereq != NO_TECH)
		{
			if (GET_TEAM(getTeam()).isHasTech(ePrereq))
			{ // Read the above line carefully, removed '!'
				return false;
			}
			
		if (bTrade && !GC.getGameINLINE().isOption(GAMEOPTION_NO_TECH_BROKERING) && GET_TEAM(getTeam()).isNoTradeTech(ePrereq))
			{ // You should probably make any tech used as a 'not' prereq untradable, you'll still have issues with teams
				return false;
			}
		}
	}[/B]

	for (iI = 0; iI < GC.getNUM_OR_TECH_PREREQS(); iI++)
	{
		TechTypes ePrereq = (TechTypes)GC.getTechInfo(eTech).getPrereqOrTechs(iI);
		if (ePrereq != NO_TECH)
		{
			bFoundPossible = true;

			if (GET_TEAM(getTeam()).isHasTech(ePrereq))
			{
				if (!bTrade || GC.getGameINLINE().isOption(GAMEOPTION_NO_TECH_BROKERING) || !GET_TEAM(getTeam()).isNoTradeTech(ePrereq))
				{
					bFoundValid = true;
					break;
				}
			}
		}
	}

	if (bFoundPossible && !bFoundValid)
	{
		return false;
	}

	for (iI = 0; iI < GC.getNUM_AND_TECH_PREREQS(); iI++)
	{
		TechTypes ePrereq = (TechTypes)GC.getTechInfo(eTech).getPrereqAndTechs(iI);
		if (ePrereq != NO_TECH)
		{
			if (!GET_TEAM(getTeam()).isHasTech(ePrereq))
			{
				return false;
			}
			
			if (bTrade && !GC.getGameINLINE().isOption(GAMEOPTION_NO_TECH_BROKERING) && GET_TEAM(getTeam()).isNoTradeTech(ePrereq))
			{
				return false;
			}
		}
	}

Allowing the SDK to read the new attributes

I then need to open up CvInfos.cpp and CvInfos.h and, everywhere I see an entry for the OR prereq, copy that code and insert b4 the OR prereq and change it to NOT prereq. For example:

Spoiler :
Code:
int CvTechInfo::getFlavorValue(int i) const			
{
	FAssertMsg(i < GC.getNumFlavorTypes(), "Index out of bounds");
	FAssertMsg(i > -1, "Index out of bounds");
	return m_piFlavorValue ? m_piFlavorValue[i] : -1;
}

int CvTechInfo::getPrereqOrTechs(int i) const	
{
	return m_piPrereqOrTechs ? m_piPrereqOrTechs[i] : -1;
}

int CvTechInfo::getPrereqAndTechs(int i) const
{
	return m_piPrereqAndTechs ? m_piPrereqAndTechs[i] : -1;
}

bool CvTechInfo::isCommerceFlexible(int i) const
{
	FAssertMsg(i < NUM_COMMERCE_TYPES, "Index out of bounds");
	FAssertMsg(i > -1, "Index out of bounds");
	return m_pbCommerceFlexible ? m_pbCommerceFlexible[i] : false;

becomes

Spoiler :
Code:
int CvTechInfo::getFlavorValue(int i) const			
{
	FAssertMsg(i < GC.getNumFlavorTypes(), "Index out of bounds");
	FAssertMsg(i > -1, "Index out of bounds");
	return m_piFlavorValue ? m_piFlavorValue[i] : -1;
}

[B]nt CvTechInfo::getPrereqNotTechs(int i) const	
{
	return m_piPrereqNotTechs ? m_piPrereqNotTechs[i] : -1;
}[/B]

int CvTechInfo::getPrereqOrTechs(int i) const	
{
	return m_piPrereqOrTechs ? m_piPrereqOrTechs[i] : -1;
}

int CvTechInfo::getPrereqAndTechs(int i) const
{
	return m_piPrereqAndTechs ? m_piPrereqAndTechs[i] : -1;
}

bool CvTechInfo::isCommerceFlexible(int i) const
{
	FAssertMsg(i < NUM_COMMERCE_TYPES, "Index out of bounds");
	FAssertMsg(i > -1, "Index out of bounds");
	return m_pbCommerceFlexible ? m_pbCommerceFlexible[i] : false;

My problem with this so far is that there doesn't seem to be any direct link to what the name of the XML tag is and what's in these .cpp and .h files. Then again, there doesn't seem to be a direct link for XMl tags <OrPreReqs/> and <AndPreReqs/>. What I mean by that is I'm not sure I've actually stated that when the game sees the XML tag <NotPreReqs/> it will actually know that that means to look at NUM_NOT_TECH_PREREQS() in the compiled SDK.

Additionally, I have no idea what Kael means by 'pass2' :(
 
Look for this method in CvInfos.cpp:

CvTechInfo::readPass2(CvXMLLoadUtility* pXML)

That's where the XML file is read and the prereqs loaded, pass2 :)
 
Okay, I've done everything that I set out to do in post #15, but CodeBlocks has given me 4 errors:

Spoiler :
Code:
Switching to target: Final Release
CvArea.cpp
CvArtFileMgr.cpp
CvCity.cpp
CvCityAI.cpp
CvDLLButtonPopup.cpp
CvDLLEntity.cpp
CvDLLPython.cpp
CvDLLWidgetData.cpp
CvDeal.cpp
CvDiploParameters.cpp
CvFractal.cpp
CvGame.cpp
CvGameAI.cpp
CvGameCoreDLL.cpp
CvGameCoreUtils.cpp
CvGameTextMgr.cpp
CvGlobals.cpp
CvHallOfFameInfo.cpp
CvInfoWater.cpp
CvInfos.cpp
[COLOR="Red"]CvInfos.cpp(1512) : error C2039: 'getNUM_NOT_TECH_PREREQS' : is not a member of 'CvGlobals'[/COLOR]
        c:\Documents and Settings\Vince\My Documents\Civ IV\Call To Power\CvGameCoreDLL\CvGlobals.h(136) : see declaration of 'CvGlobals'
[COLOR="Red"]CvInfos.cpp(1513) : error C2039: 'getNUM_NOT_TECH_PREREQS' : is not a member of 'CvGlobals'[/COLOR]
        c:\Documents and Settings\Vince\My Documents\Civ IV\Call To Power\CvGameCoreDLL\CvGlobals.h(136) : see declaration of 'CvGlobals'
[COLOR="Red"]CvInfos.cpp(1585) : error C2039: 'getNUM_NOT_TECH_PREREQS' : is not a member of 'CvGlobals'[/COLOR]
        c:\Documents and Settings\Vince\My Documents\Civ IV\Call To Power\CvGameCoreDLL\CvGlobals.h(136) : see declaration of 'CvGlobals'
[COLOR="Red"]CvInfos.cpp(1687) : error C2039: 'getNUM_NOT_TECH_PREREQS' : is not a member of 'CvGlobals'[/COLOR]
        c:\Documents and Settings\Vince\My Documents\Civ IV\Call To Power\CvGameCoreDLL\CvGlobals.h(136) : see declaration of 'CvGlobals'
Process terminated with status 1 (2 minutes, 24 seconds)
[COLOR="RoyalBlue"]4 errors, 0 warnings[/COLOR]
 
Top Bottom