Troubles with Arrays and Arrays of Arrays...

OK, final step is setting up the functionality. Up until now I've been using the AndTechs as a template. But this is not an and function, it's an Or function, and I can't figure out how to tell the function to return false if there are specific civics set, but to return true if any one of the civics is being currently used by the player.

For code, I'll show the AndTechs as a template:
Code:
bool CvPlayer::canTrain(UnitTypes eUnit, bool bContinue, bool bTestVisible, bool bIgnoreCost) const
{
...
	for (iI = 0; iI < GC.getNUM_UNIT_AND_TECH_PREREQS(); iI++)
	{
		if (GC.getUnitInfo(eUnit).getPrereqAndTechs(iI) != NO_TECH)
		{
			if (!(GET_TEAM(getTeam()).isHasTech((TechTypes)(GC.getUnitInfo(eUnit).getPrereqAndTechs(iI)))))
			{
				return false;
			}
		}
	}

Any ideas how to change that code to a player is currently in a civic Or type check?
 
Phungus, I have no idea how to help you... I'm temporarily hijacking my thread back... ;)

I've added code in the CvGameTextMgr now, for the settechtradehelp and buildingactualhelp an yet, neither shows in game on the civilopedia or tech tree. It compiles fine, but has no effect. Here's the Tech Code:

Code:
void CvGameTextMgr::setTechTradeHelp(CvWStringBuffer &szBuffer, TechTypes eTech, PlayerTypes eTradePlayer, bool bCivilopediaText, bool bPlayerContext, bool bStrategyText, bool bTreeInfo, TechTypes eFromTech)
// BUG - Trade Denial - end
{
...
for (iI = 0; iI < GC.getNumBuildingInfos(); ++iI)
	{
...
	//	Building happy changes
		buildBuildingTechHappinessChangesString(szBuffer, eTech, iI, true, bPlayerContext);
	//	Building health changes
	//	buildBuildingTechHealthChangesString(szBuffer, eTech, iI, true, bPlayerContext);
	}
...
}

Here's the function it is calling:
Code:
void CvGameTextMgr::buildBuildingTechHappinessChangesString(CvWStringBuffer &szBuffer, TechTypes eTech, int iBuildingType, bool bList, bool bPlayerContext)
{
	CvWString szTempBuffer;
	if (bList)
	{
		szTempBuffer.Format(L"<link=literal>%s</link>", GC.getBuildingInfo((BuildingTypes)iBuildingType).getDescription());
	}
	else
	{
		szTempBuffer.Format(L"%c<link=literal>%s</link>", gDLL->getSymbolID(BULLET_CHAR), GC.getBuildingInfo((BuildingTypes)iBuildingType).getDescription());
	}
	
	szTempBuffer.append(gDLL->getText("TXT_KEY_TECH_HAPPINESS_CHANGES", abs(GC.getBuildingInfo((BuildingTypes)iBuildingType).getTechHappinessChanges(eTech)),((GC.getBuildingInfo((BuildingTypes)iBuildingType).getTechHappinessChanges(eTech) > 0) ? gDLL->getSymbolID(HAPPY_CHAR) : gDLL->getSymbolID(UNHAPPY_CHAR))));
}

Here's the building code:

Code:
void CvGameTextMgr::setBuildingHelpActual(CvWStringBuffer &szBuffer, BuildingTypes eBuilding, bool bCivilopediaText, bool bStrategyText, bool bTechChooserText, CvCity* pCity, bool bActual)
{
...
	for (int iTech = 0; iTech < GC.getNumTechInfos(); iTech++)
	{
...		if (kBuilding.getTechHealthChanges(iTech) != 0)
		{
			szFirstBuffer.Format(L"%s%s", NEWLINE, gDLL->getText("TXT_KEY_WITH", abs(kBuilding.getTechHealthChanges(iTech)), ((kBuilding.getTechHealthChanges(iTech) > 0) ? gDLL->getSymbolID(HEALTHY_CHAR): gDLL->getSymbolID(UNHEALTHY_CHAR))).c_str());
			szTempBuffer.Format(L"<link=literal>%s</link>", GC.getTechInfo((TechTypes)iTech).getDescription());
			setListHelp(szBuffer, szFirstBuffer, szTempBuffer, L", ", (kBuilding.getTechHealthChanges(iTech) != iLast));
			iLast = kBuilding.getTechHealthChanges(iTech);
		}

		if (kBuilding.getTechHappinessChanges(iTech) != 0)
		{
			szFirstBuffer.Format(L"%s%s", NEWLINE, gDLL->getText("TXT_KEY_WITH", abs(kBuilding.getTechHappinessChanges(iTech)), ((kBuilding.getTechHappinessChanges(iTech) > 0) ? gDLL->getSymbolID(HAPPY_CHAR) : gDLL->getSymbolID(UNHAPPY_CHAR))).c_str());
			szTempBuffer.Format(L"<link=literal>%s</link>", GC.getTechInfo((TechTypes)iTech).getDescription());
			setListHelp(szBuffer, szFirstBuffer, szTempBuffer, L", ", (kBuilding.getTechHappinessChanges(iTech) != iLast));
			iLast = kBuilding.getTechHappinessChanges(iTech);
		}
	}
...
}

Any idea what I am doing wrong?
 
phungus:

For an AND loop, you start out true, and become false if anything misses:


Code:
bool bValid = true;

for (iI = 0; iI < maxNums; ++iI)
{
    Is current loop requirement NOT met?
    {
        bValid = false
    }
}
if not bValid
{
    return false;
}

Obviously you could instantly "return false;" if that was what would happen if bValid wasn't met, but sometimes you want to continue to process the code, just in a different way, so I wrote it with the bValid check.


But to process an OR check, you need to reverse the logic, and start false, but become true if ANY requirement is met:

Code:
bool bValid = false;

for (iI = 0; iI < maxNums; ++iI)
{
    Is current loop requirement met?
    {
        bValid = true;
    }
}
if not bValid
{
    return false;
}

Thus an OR runs much like an AND, but the logic checks are inverted.


Afforess:

Remember that any time you are playing in CvGameTextMgr and something doesn't show up, place some text outputs which will ALWAYS show up (like 3 NEWLINE commands with a :p after the first) before and after your code does any big IF statements so you can check that the IF passed properly. (granted, displaying the number you are about to perform an IF on is more useful than an emoticon, but who am I to judge?)

I don't see anything wrong with buildBuildingTechHappinessChangesString off the top of my head, so quite possibly you are getting a 0 value in there on everything? Though with what you posted there is never a check that the value is non-zero, so every building in the game ought to display something.


For the last bit of code you posted, did you write: TXT_KEY_WITH yourself? Because I would expect that one to be just:

<English>[space]with[space]</English>

if it is a native Firaxis text key, thus it would ignore all the values you passed to it.
 
OK, here is what I came up with:

Spoiler :
Code:
	//phungus civic canTrain
	for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
	{
		if (GC.getUnitInfo(eUnit).getReqCivicOrs(iI) != NO_CIVIC)
		{
			int bValid;
			bValid = false;
			[B]if ((GET_PLAYER).isCivic((CivicTypes)GC.getUnitInfo(eUnit).getReqCivicOrs(iI)))[/B]
			{
				bValid = true;
			}
			if (!bValid)
			{
				return false;
			}
		}
	}
	//phungus -end
Bolded line is throwing an error, and fails to compile. Says:
1>CvPlayer.cpp(6634) : error C2228: left of '.isCivic' must have class/struct/union type
1> type is 'overloaded-function'

What's wrong?

Nevermind, GET_PLAYER call was unnecessary. We are already examining the player here.
 
OK, here is what I came up with:

Code:
    //phungus civic canTrain
    for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
    {
        if (GC.getUnitInfo(eUnit).getReqCivicOrs(iI) != NO_CIVIC)
        {
            int bValid;
            bValid = false;
            [B]if ((GET_PLAYER).isCivic((CivicTypes)GC.getUnitInfo(eUnit).getReqCivicOrs(iI)))[/B]
            {
                bValid = true;
            }
            if (!bValid)
            {
                return false;
            }
        }
    }
    //phungus -end
Bolded line is throwing an error, and fails to compile. Says:


What's wrong?
First off, shouldn't bValid be a boolean variable, not an int? I don't think you can assign a true/false value to an int. That's the first problem. Why the compile didn't scream at you for that is beyond me.

I'm not sure why GET_PLAYER isn't working though, but I lack experience in these matters...

Edit: Phungus figured some of his problems out, but my first statement still stands.
 
Not going to work, because you are defining bValid = false INSIDE your loop. So each civic you come to it is going to set bValid, regardless of what it was before. Also your bValid check with a Return statement is inside your loop as well, so as soon as it finds a single false case, it will exit.

Also you defined bValid as an integer instead of a boolean, which wouldn't cause HUGE issues, but still isn't right.

Code:
	//phungus civic canTrain
	bool bValid = false;
	for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
	{
		if (GC.getUnitInfo(eUnit).getReqCivicOrs(iI) != NO_CIVIC)
		{
			if ((GET_PLAYER).isCivic((CivicTypes)GC.getUnitInfo(eUnit).getReqCivicOrs(iI)))
			{
				bValid = true;
			}
		}
	}
	if (!bValid)
	{
		return false;
	}
	//phungus -end

It seems wierd to me that you would have a function of

CivicTypes CvUnitInfo::getReqCivicOrs(int iCivicIndex)

Which is what you would need to do the comparison you wrote this function to evaluate. Most likely I would have thought you store a boolean array, so would have a

bool CvUnitInfo::isReqCivicOR(int iCivicIndex)

Thus you would just query the boolean to inquire if it is true (that civic is one of the OR options) or false (that civic doesn't matter)


EDIT: Afforess: true = 1 and false = 0 if you try to use them as integers (or double or any other number type)
 
Afforess:

Remember that any time you are playing in CvGameTextMgr and something doesn't show up, place some text outputs which will ALWAYS show up (like 3 NEWLINE commands with a :p after the first) before and after your code does any big IF statements so you can check that the IF passed properly. (granted, displaying the number you are about to perform an IF on is more useful than an emoticon, but who am I to judge?)

I don't see anything wrong with buildBuildingTechHappinessChangesString off the top of my head, so quite possibly you are getting a 0 value in there on everything? Though with what you posted there is never a check that the value is non-zero, so every building in the game ought to display something.


For the last bit of code you posted, did you write: TXT_KEY_WITH yourself? Because I would expect that one to be just:

<English>[space]with[space]</English>

if it is a native Firaxis text key, thus it would ignore all the values you passed to it.

I think I understand what is wrong with the "setBuildingHelpActual ()" , I'm creating string placeholders (%s), but then, trying to force the TXT_KEY to have it's own placeholders, when it obviously lacks them.

I rewrote this one, and I'll break it down, part by part, and tell me if I understand it correctly:
Code:
		if (kBuilding.getTechHealthChanges(iTech) != 0)
		{
			szFirstBuffer.Format(L"%s%s%s", NEWLINE, abs(kBuilding.getTechHealthChanges(iTech)), (((kBuilding.getTechHealthChanges(iTech) > 0) ? gDLL->getSymbolID(HEALTHY_CHAR): gDLL->getSymbolID(UNHEALTHY_CHAR))), gDLL->getText("TXT_KEY_WITH"));
			szTempBuffer.Format(L"<link=literal>%s</link>", GC.getTechInfo((TechTypes)iTech).getDescription());
			setListHelp(szBuffer, szFirstBuffer, szTempBuffer, L", ", (kBuilding.getTechHealthChanges(iTech) != iLast));
			iLast = kBuilding.getTechHealthChanges(iTech);
		}


  • First part, the IF statement just makes sure it doesn't show when there is a 0 change.
  • Then, it creates a buffer (in memory, I assume) with three string placeholders, the first one a new line, the next, the + :health: or :yuck: symbol, depending on if it is positive or negative. The last placeholder is just a text key that says " With ".
  • The TempBuffer creates another temporary buffer that has one string placeholder, and creates the blue link in the civilopedia to that tech.
  • The third statement displays all the buffers, and if it is the last tech...
  • The last statement sets iLast to a new tech, if there is one.
Should this work, do I understand it correctly?

Edit:
Fixed some parenthesis errors the compiler caught.
 
EDIT: Afforess: true = 1 and false = 0 if you try to use them as integers (or double or any other number type)

I wondered about that. So, it's bad style and wasting computer memory, but technically correct?
 
Yeah forgot about that.

Xienwolf, to clarify, you're saying all through my CvInvos I should change my int to bool for this function?

So, for instance, in CvInfos.h I should change:
Code:
//phungus civic canTrain
	int getReqCivicOrs(int i) const;				// Exposed to Python
	//phungus -end
to
Code:
//phungus civic canTrain
	bool getReqCivicOrs(bool i) const;				// Exposed to Python
	//phungus -end
?

Also in CvGameTextMgr I can't figure out how to use the isCivic call:
Code:
/*//phungus civic canTrain
			for (iI = 0; iI < GC.getNumCivicInfos(); ++iI)
			{
				if (GC.getUnitInfo(eUnit).getReqCivicOrs(iI) != NO_CIVIC)
				{
					if (GC.getGameINLINE().getActivePlayer() != NO_PLAYER || !(GC.getGameINLINE().getActivePlayer()(GET_PLAYER).isCivic(GC.getUnitInfo(eUnit).getReqCivicOrs(iI))))
					{
						szTempBuffer.Format(L"%s%s", NEWLINE, gDLL->getText("TXT_KEY_REQUIRES").c_str());
						setListHelp(szBuffer, szTempBuffer, GC.getCivicInfo((CivicOptions) GC.getUnitInfo(eUnit).getReqCivicOrs(iI)).getDescription(), gDLL->getText("TXT_KEY_OR").c_str(), bFirst);
						bFirst = false;
					}
				}
			}
			//phungus -end*/
currently throws an error, saying .isCivic needs a class union or struct to it's left. This is how the player seems to be referenced everywhere in GameTextMgr, so I'm not sure how to go about this.
 
@Afforess - You are building szTempBuffer but never adding it to the passed-in szBuffer. That's why nothing happens. For the Actual Effects part, you should only need to add in your effects to the iGood and iBad values; it will add those to the hover text for you.

@phungus420 - I recommend changing the name to "PrereqOrCivics" to match similar concepts in the SDK such as PrereqAndTechs that you copied. Yes, it should be a boolean array, and its accessor getPrereqOrCivic(int iCivic) should return bool.

GET_PLAYER acts like a function and requires a PlayerTypes value as its parameter:

Code:
if (GET_PLAYER(GC.getGameINLINE().getActivePlayer()).isCivic(...))
 
@Afforess - You are building szTempBuffer but never adding it to the passed-in szBuffer. That's why nothing happens. For the Actual Effects part, you should only need to add in your effects to the iGood and iBad values; it will add those to the hover text for you.

Can you be more specific? Are you talking about the tech hover or building hover?
 
buildBuildingTechHappinessChangesString(). Also, I don't see where you check if the building is affected by the tech or not--you call the above function regardless. Oh, and I'd put the +1:)/:( before the building name, but you can muck with that once you get something showing.
 
Now that it's a bool array, I cannot get it to work. For instance here is what CvGameTextMgr looks like:

Spoiler :
Code:
			//phungus civic canTrain
			for (iI = 0; iI < GC.getNumCivicInfos(); ++iI)
			{
				if (GC.getUnitInfo(eUnit).getPrereqOrCivics(iI))
				{
					if (GC.getGameINLINE().getActivePlayer() != NO_PLAYER || !(GET_PLAYER(GC.getGameINLINE().getActivePlayer()).isCivic(GC.getUnitInfo(eUnit).getPrereqOrCivics(iI))))
					{
						szTempBuffer.Format(L"%s%s", NEWLINE, gDLL->getText("TXT_KEY_REQUIRES").c_str());
						setListHelp(szBuffer, szTempBuffer, GC.getCivicInfo((CivicOptions) GC.getUnitInfo(eUnit).getPrereqOrCivics(iI)).getDescription(), gDLL->getText("TXT_KEY_OR").c_str(), bFirst);
						bFirst = false;
					}
				}
			}
			//phungus -end

And the compiling error I get:
1>CvGameTextMgr.cpp(10391) : error C2664: 'CvPlayer::isCivic' : cannot convert parameter 1 from 'bool' to 'CivicTypes'
1> Conversion to enumeration type requires an explicit cast (static_cast, C-style cast or function-style cast)

If this doesn't work, for that reason, then CvPlayer's main canTrain check wol't work, as it's basically the same function.

Here are my CvInfos in case that's needed:
Spoiler :
Cv Infos.h
Code:
	//phungus civic canTrain
	bool getPrereqOrCivics(int iCivic) const;				// Exposed to Python
	//phungus -end
...
	//phungus civic req
	bool* m_pbPrereqOrCivics;
	//phungus -end
.cpp
Code:
//phungus civic canTrain
m_pbPrereqOrCivics(NULL),
//phungus -end
...
CvUnitInfo::~CvUnitInfo()
{
	//phungus civic canTrain
	SAFE_DELETE_ARRAY(m_pbPrereqOrCivics);
	//phungus end
...
//phungus civic canTrain
bool CvUnitInfo::getPrereqOrCivics(int iCivic) const	
{
	FAssertMsg(i < GC.getNumCivicInfos(), "Index out of bounds");
	FAssertMsg(i > -1, "Index out of bounds");
	return m_pbPrereqOrCivics ? m_pbPrereqOrCivics[i] : false;
}
//phungus -end
...
	//phungus civic canTrain
	SAFE_DELETE_ARRAY(m_pbPrereqOrCivics);
	m_pbPrereqOrCivics = new bool[GC.getNumCivicInfos()];
	stream->Read(GC.getNumCivicInfos(), m_pbPrereqOrCivics);
	//phungus -end
...
	//phungus civic canTrain
	stream->Write(GC.getNumCivicInfos(), m_pbPrereqOrCivics);
	//phung -end
...
	//phungus civic canTrain
	pXML->SetVariableListTagPair(&m_pbPrereqOrCivics, "PrereqOrCivics", sizeof(GC.getCivicInfo((CivicOptions)0)), GC.getNumCivicInfos());
	//phungus -end
...
	//phungus civic canTrain
	for ( int i = 0; i < GC.getNumCivicInfos(); i++)
	{
		if ( PrereqOrCivics(i) == bDefault )
		{
			m_pbPrereqOrCivics[i] = pClassInfo->getPrereqOrCivics(iCivic);
		}
	}
	//phungus -end

Edit:
Got help from Opera on IRC, fixed the syntax, and it now compiles CvGameTextMgr at least
Spoiler :
Code:
			//phungus civic canTrain
			for (iI = 0; iI < GC.getNumCivicInfos(); ++iI)
			{
				if (GC.getUnitInfo(eUnit).getPrereqOrCivics((CivicTypes)iI))
				{
					if (GC.getGameINLINE().getActivePlayer() == NO_PLAYER || !(GET_PLAYER(GC.getGameINLINE().getActivePlayer()).isCivic((CivicTypes)GC.getUnitInfo(eUnit).getPrereqOrCivics(iI))))
					{
						szTempBuffer.Format(L"%s%s", NEWLINE, gDLL->getText("TXT_KEY_REQUIRES").c_str());
						setListHelp(szBuffer, szTempBuffer, GC.getCivicInfo((CivicTypes)GC.getUnitInfo(eUnit).getPrereqOrCivics((CivicTypes)iI)).getDescription(), gDLL->getText("TXT_KEY_OR").c_str(), bFirst);
						bFirst = false;
					}
				}
			}
			//phungus -end
 
I've gotten it to succesffully compile and started a game, but unfortunately it's not working. I added Civic reqs to the Warrior, and he is still buildable. Here is the code:

Spoiler :

CvInfos.h
Code:
	//phungus civic canTrain
	bool getPrereqOrCivics(int iCivic) const;				// Exposed to Python
	//phungus -end
...
	//phungus civic req
	bool* m_pbPrereqOrCivics;
	//phungus -end

CvInfos.cpp
Code:
//phungus civic canTrain
m_pbPrereqOrCivics(NULL),
//phungus -end
...
	//phungus civic canTrain
	SAFE_DELETE_ARRAY(m_pbPrereqOrCivics);
	//phungus end
...
//phungus civic canTrain
bool CvUnitInfo::getPrereqOrCivics(int iCivic) const	
{
	FAssertMsg(iCivic < GC.getNumCivicInfos(), "Index out of bounds");
	FAssertMsg(iCivic > -1, "Index out of bounds");
	return m_pbPrereqOrCivics ? m_pbPrereqOrCivics[iCivic] : false;
}
//phungus -end
...
	//phungus civic canTrain
	SAFE_DELETE_ARRAY(m_pbPrereqOrCivics);
	m_pbPrereqOrCivics = new bool[GC.getNumCivicInfos()];
	stream->Read(GC.getNumCivicInfos(), m_pbPrereqOrCivics);
	//phungus -end
...
	//phungus civic canTrain
	stream->Write(GC.getNumCivicInfos(), m_pbPrereqOrCivics);
	//phung -end
...
	//phungus civic canTrain
	pXML->SetVariableListTagPair(&m_pbPrereqOrCivics, "PrereqOrCivics", sizeof(GC.getCivicInfo((CivicTypes)0)), GC.getNumCivicInfos());
	//phungus -end
...
	//phungus civic canTrain
	for ( int iCivic = 0; i < GC.getNumCivicInfos(); iCivic++)
	{
		if ( getPrereqOrCivics(iCivic) == bDefault )
		{
			m_pbPrereqOrCivics[iCivic] = pClassInfo->getPrereqOrCivics(iCivic);
		}
	}
	//phungus -end

CvPlayer.cpp
Code:
bool CvPlayer::canTrain(UnitTypes eUnit, bool bContinue, bool bTestVisible, bool bIgnoreCost) const
{
...
	//phungus civic canTrain
	bool bValid = false;
	for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
	{
		if (GC.getUnitInfo(eUnit).getPrereqOrCivics((CivicTypes)iI))
		{
			if (isCivic((CivicTypes)GC.getUnitInfo(eUnit).getPrereqOrCivics(iI)))
			{
				bValid = true;
			}
		}
		else
		{
			bValid = true;
		}
	}
	if (!bValid)
	{
		return false;
	}
	//phungus -end

XML stuff:
schema:
Code:
	<ElementType name="CivicOption" content="textOnly"/>
	<ElementType name="bPrereqCivic" content="textOnly" dt:type="boolean"/>
	<ElementType name="PrereqCivic" content="eltOnly">
		<element type="CivicOption"/>
		<element type="bPrereqCivic"/>
	</ElementType>
	<ElementType name="PrereqOrCivics" content="eltOnly">
		<element type="PrereqCivic" minOccurs="0" maxOccurs="*"/>
	</ElementType>
...
		<element type="PrereqOrCivics" minOccurs="0"/>
And UnitInfos
Code:
			<PrereqOrCivics>
				<PrereqCivic>
					<CivicOption>CIVIC_SLAVERY</CivicOption>
					<bPrereqCivic>1</bPrereqCivic>
				</PrereqCivic>
				<PrereqCivic>
					<CivicOption>CIVIC_ORGANIZED_RELIGION</CivicOption>
					<bPrereqCivic>1</bPrereqCivic>
				</PrereqCivic>
			</PrereqOrCivics>

Anyone see the flaw in my code here? It loads fine, and no errors are thrown. However the warrior is buildable, which he should not be with that code in there to check if it's working in UnitInfos. Also as a note, before I added the else check for bValid in CvPlayer, no units were buildable. So I think the problem is in CvPlayer.
 
Code:
        if (kBuilding.getTechHealthChanges(iTech) != 0)
        {
            szFirstBuffer.Format(L"%s%s%s", NEWLINE, abs(kBuilding.getTechHealthChanges(iTech)), (((kBuilding.getTechHealthChanges(iTech) > 0) ? gDLL->getSymbolID(HEALTHY_CHAR): gDLL->getSymbolID(UNHEALTHY_CHAR))), gDLL->getText("TXT_KEY_WITH"));
            szTempBuffer.Format(L"<link=literal>%s</link>", GC.getTechInfo((TechTypes)iTech).getDescription());
            setListHelp(szBuffer, szFirstBuffer, szTempBuffer, L", ", (kBuilding.getTechHealthChanges(iTech) != iLast));
            iLast = kBuilding.getTechHealthChanges(iTech);
        }
  • First part, the IF statement just makes sure it doesn't show when there is a 0 change.
  • Then, it creates a buffer (in memory, I assume) with three string placeholders, the first one a new line, the next, the + :health: or :yuck: symbol, depending on if it is positive or negative. The last placeholder is just a text key that says " With ".
  • The TempBuffer creates another temporary buffer that has one string placeholder, and creates the blue link in the civilopedia to that tech.
  • The third statement displays all the buffers, and if it is the last tech...
  • The last statement sets iLast to a new tech, if there is one.

1. Correct
2. It isn't a memory buffer, it is just a string CALLED buffer. You will still treat it like any other string once done making it. You claim it will get 3 strings (%s%s%s), but then give it a string, an integer, a character, and a string (%s%d%c%s). You may also want a space before and after the final %s.
3. Correct
4. No, now you are calling a function to apply your 2 strings properly. You say what the old and new values are, if the value hadn't changed, it won't bother to say +5 :) again, and instead just add another tech to the list (making it +5 :) with Agriculture and Refrigeration). If the values are different, then it will add the number/symbol string once again.
5. Not quite, this sets the iLast value to be equal to the value you just loaded, for the above mentioned string abbreviation.

I've gotten it to succesffully compile and started a game, but unfortunately it's not working. I added Civic reqs to the Warrior, and he is still buildable. Here is the code:

Spoiler :

CvInfos.h
Code:
    //phungus civic canTrain
    bool getPrereqOrCivics(int iCivic) const;                // Exposed to Python
    //phungus -end
...
    //phungus civic req
    bool* m_pbPrereqOrCivics;
    //phungus -end
CvInfos.cpp
Code:
//phungus civic canTrain
m_pbPrereqOrCivics(NULL),
//phungus -end
...
    //phungus civic canTrain
    SAFE_DELETE_ARRAY(m_pbPrereqOrCivics);
    //phungus end
...
//phungus civic canTrain
bool CvUnitInfo::getPrereqOrCivics(int iCivic) const    
{
    FAssertMsg(iCivic < GC.getNumCivicInfos(), "Index out of bounds");
    FAssertMsg(iCivic > -1, "Index out of bounds");
    return m_pbPrereqOrCivics ? m_pbPrereqOrCivics[iCivic] : false;
}
//phungus -end
...
    //phungus civic canTrain
    SAFE_DELETE_ARRAY(m_pbPrereqOrCivics);
    m_pbPrereqOrCivics = new bool[GC.getNumCivicInfos()];
    stream->Read(GC.getNumCivicInfos(), m_pbPrereqOrCivics);
    //phungus -end
...
    //phungus civic canTrain
    stream->Write(GC.getNumCivicInfos(), m_pbPrereqOrCivics);
    //phung -end
...
    //phungus civic canTrain
    pXML->SetVariableListTagPair(&m_pbPrereqOrCivics, "PrereqOrCivics", sizeof(GC.getCivicInfo((CivicTypes)0)), GC.getNumCivicInfos());
    //phungus -end
...
    //phungus civic canTrain
    for ( int iCivic = 0; i < GC.getNumCivicInfos(); iCivic++)
    {
        if ( getPrereqOrCivics(iCivic) == bDefault )
        {
            m_pbPrereqOrCivics[iCivic] = pClassInfo->getPrereqOrCivics(iCivic);
        }
    }
    //phungus -end
CvPlayer.cpp
Code:
bool CvPlayer::canTrain(UnitTypes eUnit, bool bContinue, bool bTestVisible, bool bIgnoreCost) const
{
...
    //phungus civic canTrain
    bool bValid = false;
    for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
    {
        if (GC.getUnitInfo(eUnit).getPrereqOrCivics((CivicTypes)iI))
        {
            if (isCivic((CivicTypes)GC.getUnitInfo(eUnit).getPrereqOrCivics(iI)))
            {
                bValid = true;
            }
        }
        else
        {
            bValid = true;
        }
    }
    if (!bValid)
    {
        return false;
    }
    //phungus -end
XML stuff:
schema:
Code:
    <ElementType name="CivicOption" content="textOnly"/>
    <ElementType name="bPrereqCivic" content="textOnly" dt:type="boolean"/>
    <ElementType name="PrereqCivic" content="eltOnly">
        <element type="CivicOption"/>
        <element type="bPrereqCivic"/>
    </ElementType>
    <ElementType name="PrereqOrCivics" content="eltOnly">
        <element type="PrereqCivic" minOccurs="0" maxOccurs="*"/>
    </ElementType>
...
        <element type="PrereqOrCivics" minOccurs="0"/>
And UnitInfos
Code:
            <PrereqOrCivics>
                <PrereqCivic>
                    <CivicOption>CIVIC_SLAVERY</CivicOption>
                    <bPrereqCivic>1</bPrereqCivic>
                </PrereqCivic>
                <PrereqCivic>
                    <CivicOption>CIVIC_ORGANIZED_RELIGION</CivicOption>
                    <bPrereqCivic>1</bPrereqCivic>
                </PrereqCivic>
            </PrereqOrCivics>

Anyone see the flaw in my code here? It loads fine, and no errors are thrown. However the warrior is buildable, which he should not be with that code in there to check if it's working in UnitInfos. Also as a note, before I added the else check for bValid in CvPlayer, no units were buildable. So I think the problem is in CvPlayer.

Is your textMgr code showing up? It is normally best to get that working first, then focus on the functional code, which all seems right so far. I would advise you rename "getPrereqOrCivics" to "isPrereqOrCivics" though, just for clarity
 
I've gotten it to succesffully compile and started a game, but unfortunately it's not working...

if (isCivic((CivicTypes)GC.getUnitInfo(eUnit).getPrereqOrCivics(iI)))

This line does not seem right to me. getPrereqOrCivics returns a bool, and you are passing that to isCivic. I think this line is supposed to check whether the player has the civic. I think it should be just: if (isCivic(iI)). Probably you need to cast iI to CivicTypes. I also find it confusing to use generic loop variable names like iI; I recommend you use iCivic instead.

EDIT2: I am not sure that is the only problem, but I am not sure how your code fits into the overall code for canTrain. I do not think you want the second "else", but also, if there are no Or civics at all, you still want this part of the check to pass.
 
1. Correct
2. It isn't a memory buffer, it is just a string CALLED buffer. You will still treat it like any other string once done making it. You claim it will get 3 strings (%s%s%s), but then give it a string, an integer, a character, and a string (%s%d%c%s). You may also want a space before and after the final %s.
3. Correct
4. No, now you are calling a function to apply your 2 strings properly. You say what the old and new values are, if the value hadn't changed, it won't bother to say +5 :) again, and instead just add another tech to the list (making it +5 :) with Agriculture and Refrigeration). If the values are different, then it will add the number/symbol string once again.
5. Not quite, this sets the iLast value to be equal to the value you just loaded, for the above mentioned string abbreviation.

Thanks, Xienwolf, I've got the building hover text showing up in game now, but it's slightly wrong. Here's my code:

Code:
        if (kBuilding.getTechHealthChanges(iTech) != 0)
        {
            szFirstBuffer.Format(L"%s%c+%d%c %s ", NEWLINE, gDLL->getSymbolID(BULLET_CHAR), abs(kBuilding.getTechHealthChanges(iTech)), (((kBuilding.getTechHealthChanges(iTech) > 0) ? gDLL->getSymbolID(HEALTHY_CHAR): gDLL->getSymbolID(UNHEALTHY_CHAR))), gDLL->getText("TXT_KEY_WITH").GetCString());
            szTempBuffer.Format(L"<link=literal>%s</link>", GC.getTechInfo((TechTypes)iTech).getDescription());
            setListHelp(szBuffer, szFirstBuffer, szTempBuffer, L", ", (kBuilding.getTechHealthChanges(iTech) != iLast));
            iLast = kBuilding.getTechHealthChanges(iTech);
        }
        if (kBuilding.getTechHappinessChanges(iTech) != 0)
        {
            szFirstBuffer.Format(L"%s%c+%d%c%s", NEWLINE, gDLL->getSymbolID(BULLET_CHAR), abs(kBuilding.getTechHappinessChanges(iTech)), (((kBuilding.getTechHappinessChanges(iTech) > 0) ? gDLL->getSymbolID(HAPPY_CHAR): gDLL->getSymbolID(UNHAPPY_CHAR))), gDLL->getText("TXT_KEY_WITH").GetCString());
            szTempBuffer.Format(L"<link=literal>%s</link>", GC.getTechInfo((TechTypes)iTech).getDescription());
            setListHelp(szBuffer, szFirstBuffer, szTempBuffer, L", ", (kBuilding.getTechHappinessChanges(iTech) != iLast));
            iLast = kBuilding.getTechHappinessChanges(iTech);
        }

Here's what's in the XML:

Code:
            <TechHappinessChanges>
                <TechHappinessChange>
                    <PrereqTech>TECH_CROP_ROTATION</PrereqTech>
                    <iHappiness>100</iHappiness>
                </TechHappinessChange>
                <TechHappinessChange>
                    <PrereqTech>TECH_METAL_CASTING</PrereqTech>
                    <iHappiness>-1000</iHappiness>
                </TechHappinessChange>
            </TechHappinessChanges>
            <TechHealthChanges>
                <TechHealthChange>
                    <PrereqTech>TECH_CROP_ROTATION</PrereqTech>
                    <iHealth>100</iHealth>
                </TechHealthChange>
                <TechHealthChange>
                    <PrereqTech>TECH_METAL_CASTING</PrereqTech>
                    <iHealth>-1000</iHealth>
                </TechHealthChange>
            </TechHealthChanges>

And here is what is showing up in the civililopedia:

+100:health: with Crop Rotation, Crop Rotation
+1000 :yuck: with Metal Casting, Metal Casting.

I have no idea why it's showing this wrong. It must be merging the happy and health text together, but why is beyond me.
 
OK, let me rephrase.

How can I go into the array, and pull out the Civics entered in the XML, instead of bool values? Is this possible? If not, why am I making it a bool array?

This line does not seem right to me. getPrereqOrCivics returns a bool, and you are passing that to isCivic. I think this line is supposed to check whether the player has the civic. I think it should be just: if (isCivic(iI)). Probably you need to cast iI to CivicTypes. I also find it confusing to use generic loop variable names like iI; I recommend you use iCivic instead.
This makes sense. Unfortunately this code does not work:
Spoiler :
Code:
	//phungus civic canTrain
	bool bValid = false;
	for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
	{
		if (GC.getUnitInfo(eUnit).getPrereqOrCivics(iI))
		{
			if ((isCivic(CivicTypes(iI))) && (GC.getUnitInfo(eUnit).getPrereqOrCivics(iI)))
			{
				bValid = true;
			}
		}
		else
		{
			bValid = true;
		}
	}
	if (!bValid)
	{
		return false;
	}
	//phungus -end
Still does not seem to work...
Is your textMgr code showing up? It is normally best to get that working first, then focus on the functional code, which all seems right so far. I would advise you rename "getPrereqOrCivics" to "isPrereqOrCivics" though, just for clarity
While this is good advice, in this instance it is impractical. I have to deal with an entire other layer of code for the interface display, as I have to run it through BUG next. Getting that level to work, before I'm even sure how to pull the values I need from the array is very impractical. Also all of the boolean arrays in Civ4's code use get, instead of is, so I will follow the standard convention set up by the Civ4 devs here. Though I'm not convinced using a boolean is the best way to go, since I still have no idea how to actually check what the civic values entered in the XML is now :wallbash:
 
Afforess: It is linking them together because they have the same value for the same tech, and linking them together is what the "setListHelp" function is MEANT to do, it only knows the raw number previously used, it wasn't designed to handle you swapping between health and happiness like you are doing.

So the easy solution would be to make a separate loop for health and for happiness. So just copy everything you have currently, and in one delete the happy stuff, in the other delete the health stuff. Then you are done. It isn't as fast as before since you now do 2 loops, but the loss isn't a horrific thing IMO, saving linespace is the more important factor.
 
Phungus: I would first say you should look at how I rewrote the Civic screen that BUG uses (the scrollable one right?) so that it will parse directly from the DLL instead of requiring python work for each line you ever add.


To see what values are in the XML, you do what you are doing here. Loop over all civics, and ask the UnitInfo for a response to isReqCivic(iI)? If it says YES, then this civic had been included in the XML for this unit.

if ((isCivic(CivicTypes(iI))) and (GC.getUnitInfo(eUnit).getPrereqOrCivics(iI)))

should be

if ((isCivic((CivicTypes)iI) and (GC.getUnitInfo(eUnit).getPrereqOrCivics(iI)))

Note the removal of one close parenthesis before the AND, and the moving of the parenthesis from around iI to around CivicTypes
 
Back
Top Bottom