Local specialist yield changes

Maniac

Apolyton Sage
Joined
Nov 27, 2004
Messages
5,588
Location
Gent, Belgium
I'd like buildings to be able to change the yield of a specialist in their city alone (instead of empire-wide like Angkor Wat).

CCCP already had something like that. Unfortunately that mod was for Warlords 2.08. So some code needed to be changed. I don't know if I made some mistakes transferring, or if the original code was never working properly to begin with, but the code is working not properly at all.

I added this building which lets the Citizen produce +2 hammers, for a total of three. It leads to a bunch of strange situations though. Examples:
1) The Engineer doesn't produce any hammers at all anymore.
2) If I appoint two citizen specialist, the city yield is 9 hammers, while it should be seven (six from citizens, plus one from city square).
3) If I add the Genejack Factory (building which gives the extra hammers to citizens) in Worldbuilder while I already had two citizens appointed, there's no extra yield. When I remove the citizen specialists, the city production changes to -1...

There are also a bunch of troubles with gametext.

So therefore I'm wondering if anyone would be interested in having a look at the code, see if the problem is easy to fix, or if not, if someone would perhaps be interested in writing a modcomp for local specialist yield changes.

I'll list the changes to CvCity.cpp here:

Code:
CvCity::CvCity()
{
	m_ppaaiLocalSpecialistExtraYield = NULL;
}

Code:
void CvCity::uninit()
{
	int iI;
	if (m_ppaaiLocalSpecialistExtraYield != NULL)
	{
		for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
		{
			SAFE_DELETE_ARRAY(m_ppaaiLocalSpecialistExtraYield[iI]);
		}
		SAFE_DELETE_ARRAY(m_ppaaiLocalSpecialistExtraYield);
	}
}

Code:
void CvCity::reset(int iID, PlayerTypes eOwner, int iX, int iY, bool bConstructorCall)
{
	int iI;
	if (!bConstructorCall)
	{
		FAssertMsg(0 < GC.getNumSpecialistInfos(), "GC.getNumSpecialistInfos() is not greater than zero but it is used to allocate memory in CvPlayer::reset");
		FAssertMsg(m_ppaaiLocalSpecialistExtraYield==NULL, "about to leak memory, CvPlayer::m_ppaaiSpecialistExtraYield");
		m_ppaaiLocalSpecialistExtraYield = new int*[GC.getNumSpecialistInfos()];
		for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
		{
			m_ppaaiLocalSpecialistExtraYield[iI] = new int[NUM_YIELD_TYPES];
			for (int iJ = 0; iJ < NUM_YIELD_TYPES; iJ++)
			{
				m_ppaaiLocalSpecialistExtraYield[iI][iJ] = 0;
			}
		}
	}
}

Code:
void CvCity::processBuilding(BuildingTypes eBuilding, int iChange, bool bObsolete)
{
	int iI, iJ;

	if (!(GET_TEAM(getTeam()).isObsoleteBuilding(eBuilding)) || bObsolete)
	{
		for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
		{
			for (iJ = 0; iJ < NUM_YIELD_TYPES; iJ++)
			{
				changeLocalSpecialistYieldChange(((SpecialistTypes)iI), ((YieldTypes)iJ), (GC.getBuildingInfo(eBuilding).getLocalSpecialistYieldChange(iI, iJ) * iChange));
			}
		}
	}
}

Code:
void CvCity::processSpecialist(SpecialistTypes eSpecialist, int iChange)
{
	int iI;
	for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
	{
		changeBaseYieldRate(((YieldTypes)iI), ((GC.getSpecialistInfo(eSpecialist).getYieldChange(iI) + getLocalSpecialistYieldChange(eSpecialist, YieldTypes(iI))) * iChange));
	}
}

For the following getExtraSpecialistYield code I found it strange Impaler (the writer of this code) removed the reference to FreeSpecialists. However neither with or without a reference to them, the same problems remain. For the record, in my mod all cities start with one free specialist.

Code:
int CvCity::getExtraSpecialistYield(YieldTypes eIndex, SpecialistTypes eSpecialist) const
{
	FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
	FAssertMsg(eIndex < NUM_YIELD_TYPES, "eIndex expected to be < NUM_YIELD_TYPES");
	FAssertMsg(eSpecialist >= 0, "eSpecialist expected to be >= 0");
	FAssertMsg(eSpecialist < GC.getNumSpecialistInfos(), "GC.getNumSpecialistInfos expected to be >= 0");
	//return ((getSpecialistCount(eSpecialist) + getFreeSpecialistCount(eSpecialist)) * GET_PLAYER(getOwnerINLINE()).getSpecialistExtraYield(eSpecialist, eIndex)); // original
	//return ((getSpecialistCount(eSpecialist) + getFreeSpecialistCount(eSpecialist)) * (GET_PLAYER(getOwnerINLINE()).getSpecialistExtraYield(eSpecialist, eIndex)  + getLocalSpecialistYieldChange(eSpecialist, eIndex))); // Planetfall Maniac attempt
	return (getSpecialistCount(eSpecialist) * (GET_PLAYER(getOwnerINLINE()).getSpecialistExtraYield(eSpecialist, eIndex)  + getLocalSpecialistYieldChange(eSpecialist, eIndex))); // Impaler
}

int CvCity::getLocalSpecialistYieldChange(SpecialistTypes eSpecialist, YieldTypes eYield) const //Addition LocalSpecialistYieldChange by Impaler[WrG]
{
    return m_ppaaiLocalSpecialistExtraYield[eSpecialist][eYield];
}

void CvCity::changeLocalSpecialistYieldChange(SpecialistTypes eSpecialist, YieldTypes eYield, int iChange)  //Addition LocalSpecialistYieldChange by Impaler[WrG]  5/16/06
{
    int iYield = m_ppaaiLocalSpecialistExtraYield[eSpecialist][eYield];
    m_ppaaiLocalSpecialistExtraYield[eSpecialist][eYield] = iYield + iChange;
}

Code:
void CvCity::read(FDataStreamBase* pStream)
{
	int iI;

	for (iI=0;iI<GC.getNumSpecialistInfos();iI++)
	{
		pStream->Read(NUM_YIELD_TYPES, m_ppaaiLocalSpecialistExtraYield[iI]);
	}
}

Code:
void CvCity::write(FDataStreamBase* pStream)
{
	int iI;
	for (iI=0;iI<GC.getNumSpecialistInfos();iI++)
	{
		pStream->Write(NUM_YIELD_TYPES, m_ppaaiLocalSpecialistExtraYield[iI]);
	}
}

In the Gametext file there are modifications in two places: specialisthelp and buildinghelp.

This is the Warlords code for buildinghelp:

Code:
void CvGameTextMgr::setBuildingHelp(CvWString &szBuffer, BuildingTypes eBuilding, bool bCivilopediaText, bool bStrategyText, bool bTechChooserText, CvCity* pCity)
{
	CvWString szFirstBuffer;
	int iI;

	for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
	{
		szFirstBuffer += NEWLINE + gDLL->getText("TXT_KEY_BUILDING_FROM_THIS_CITY", GC.getSpecialistInfo((SpecialistTypes) iI).getTextKeyWide());
		setYieldChangeHelp(szBuffer, L"", L"", szFirstBuffer, GC.getBuildingInfo(eBuilding).getLocalSpecialistYieldChangeArray(iI));
	}
}

Gametext seems to be done a little differently in BtS, so I changed it accordingly. szBuffer.append(NEWLINE); for instance.

Anyway, I don't know if this is what Impaler intended, but the help gave a list of ALL specialists for ALL buildings, and in the line there under what yield bonus they gave. Usually nothing.

So I tried modifying something myself, which resulted in this. Note that I don't really understand the code. I'm just kinda mimicking some stuff i've seen elsewhere, and hoping it works.

Code:
void CvGameTextMgr::setBuildingHelp(CvWStringBuffer &szBuffer, BuildingTypes eBuilding, bool bCivilopediaText, bool bStrategyText, bool bTechChooserText, CvCity* pCity)
{
	CvWString szFirstBuffer;
	int iI;
    int iJ;

	for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
	{
		for (iJ = 0; iJ < NUM_YIELD_TYPES; ++iJ)
		{
			if (GC.getBuildingInfo(eBuilding).getLocalSpecialistYieldChange(iI, iJ) != 0)
			{
				szBuffer.append(NEWLINE);
				szBuffer.append(gDLL->getText("TXT_KEY_BUILDING_FROM_THIS_BASE", GC.getBuildingInfo(eBuilding).getLocalSpecialistYieldChangeArray(iI), GC.getSpecialistInfo((SpecialistTypes) iI).getTextKeyWide()));
				setYieldChangeHelp(szBuffer, L"", L"", L"", GC.getBuildingInfo(eBuilding).getLocalSpecialistYieldChangeArray(iI));
			}
		}
	}
}
Code:
	<TEXT>
		<Tag>TXT_KEY_BUILDING_FROM_THIS_BASE</Tag>
		<English>%s1_Yield[SPACE]from [COLOR_UNIT_TEXT][LINK=literal]%s1_SpclstName[\LINK][COLOR_REVERT] in This Base</English>
		<French>%s1_Yield[SPACE]de tous les [COLOR_UNIT_TEXT][LINK=literal]%s1_SpclstName[\LINK][COLOR_REVERT] dans toutes les villes</French>
		<German>%s1_Yield[SPACE]per [LINK=literal]%s1:2_SpclstName[\LINK] in allen Städten</German>
		<Italian>%s1_Yield[SPACE]per [COLOR_UNIT_TEXT][LINK=literal]%s1:2_SpclstName[\LINK][COLOR_REVERT] in tutte le città</Italian>
		<Spanish>%s1_Yield[SPACE]por [COLOR_UNIT_TEXT][LINK=literal]%s1_SpclstName[\LINK][COLOR_REVERT] en todas las ciudades</Spanish>
	</TEXT>

The result is only partially succesful. There is no longer a list of all specialists for all buildings. However I'd prefer the help text to say "+2 hammers from Citizen in this base". Instead it says "%s1_Yield from in This Base". and on the next line "+2 hammers". I've no idea how to create my desired help text.


Then there's the specialist help text. Impaler's code has a really inconsistent use of indentation, plus there even seemed to be some {}'s missing, so I don't really see how it could have worked. :confused: Here it is:

Code:
void CvGameTextMgr::parseSpecialistHelp(CvWString &szHelpString, SpecialistTypes eSpecialist, CvCity* pCity, bool bCivilopediaText)
{
	CvWString szText;
	int aiYields[NUM_YIELD_TYPES];
	int aiCommerces[NUM_COMMERCE_TYPES];
	int iI;

	if (eSpecialist != NO_SPECIALIST)
	{
		if (!bCivilopediaText)
		{
			szHelpString=GC.getSpecialistInfo(eSpecialist).getDescription();

			for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
			{
                //Start Change LocalSpecialistYieldChange by Impaler[WrG] 5/16/06
			    //ORIGINAL CODE aiYields[iI] = GET_PLAYER((pCity != NULL) ? pCity->getOwnerINLINE() : GC.getGameINLINE().getActivePlayer()).specialistYield(eSpecialist, ((YieldTypes)iI));
				int LocalYieldChange = 0;
				if (pCity != NULL)
                    LocalYieldChange = pCity->getLocalSpecialistYieldChange(eSpecialist, (YieldTypes)iI);
				aiYields[iI] = GET_PLAYER((pCity != NULL) ? pCity->getOwnerINLINE() : GC.getGameINLINE().getActivePlayer()).specialistYield(eSpecialist, ((YieldTypes)iI)) + LocalYieldChange;
                //End Change
            }
            setYieldChangeHelp(szHelpString, L"", L"", L"", aiYields);

		if (bCivilopediaText)
		{
            for (int iY = 0; iY < NUM_YIELD_TYPES; iY++)
            {
                //Start Change SpecialistPediaStrings by Impaler[WrG]
                //ORIGINAL CODE  aiYields= GC.getSpecialistInfo(eSpecialist).getYieldChange(iI);
                if (GC.getSpecialistInfo(eSpecialist).getYieldChange(iY) != 0)
                {
                    szHelpString += gDLL->getText("TXT_KEY_BASE_SPECIALIST_OUTPUT", GC.getSpecialistInfo(eSpecialist).getYieldChange(iY), GC.getYieldInfo((YieldTypes)iY).getChar());
                    szHelpString += NEWLINE;
				}
                //End Change
			}
            szHelpString += NEWLINE;

            for (int iYield = 0; iYield < NUM_YIELD_TYPES; iYield++)
            {
                for (int iBuilding = 0; iBuilding < GC.getNumBuildingInfos(); iBuilding++)
                {
                    if(0 != GC.getBuildingInfo((BuildingTypes)iBuilding).getLocalSpecialistYieldChange(eSpecialist, iYield))
					{
                        szHelpString += gDLL->getText("TXT_KEY_PEDIA_TECH_YIELD", GC.getYieldInfo((YieldTypes)iYield).getTextKeyWide(), GC.getBuildingInfo((BuildingTypes)iBuilding).getLocalSpecialistYieldChange(eSpecialist, iYield), GC.getYieldInfo((YieldTypes)iYield).getChar(), GC.getBuildingInfo((BuildingTypes)iBuilding).getTextKeyWide());
                        szHelpString += NEWLINE;
                    }
				}
			}

I figured I'd start more modest and only copy over part of the stuff.

Code:
void CvGameTextMgr::parseSpecialistHelp(CvWStringBuffer &szHelpString, SpecialistTypes eSpecialist, CvCity* pCity, bool bCivilopediaText)
{
	PROFILE_FUNC();

	CvWString szText;
	int aiYields[NUM_YIELD_TYPES];
	int aiCommerces[NUM_COMMERCE_TYPES];
	int iI;

	if (eSpecialist != NO_SPECIALIST)
	{
		if (!bCivilopediaText)
		{
			szHelpString.append(GC.getSpecialistInfo(eSpecialist).getDescription());
		}

		for (iI = 0; iI < NUM_YIELD_TYPES; ++iI)
		{
			if (GC.getGameINLINE().getActivePlayer() == NO_PLAYER)
			{
				aiYields[iI] = GC.getSpecialistInfo(eSpecialist).getYieldChange(iI);
			}
			else
			{
				int LocalYieldChange = 0;
				if (pCity != NULL)
				{
					LocalYieldChange = pCity->getLocalSpecialistYieldChange(eSpecialist, (YieldTypes)iI);
				aiYields[iI] = GET_PLAYER((pCity != NULL) ? pCity->getOwnerINLINE() : GC.getGameINLINE().getActivePlayer()).specialistYield(eSpecialist, ((YieldTypes)iI)) + LocalYieldChange;
				}
			}
		}

		setYieldChangeHelp(szHelpString, L"", L"", L"", aiYields);
...

While I'm looking at civilopedia from the start screen, there's no problem. However, when in a game the help for all specialists in Civilopedia says "+ some infinite number hammers, + infinite commerce". In the city screen, the citizen correctly says +3 hammers when there's a Genejack Factory though.

Can anyone make sense of this mess? :(
 
processSpecialist()

You added the call to getLocalSpecialistYieldChange(), but you turn iI into a YieldTypes enum differently from the regular Civ code. I doubt it's a problem, but here it is anyway:

Code:
changeBaseYieldRate(((YieldTypes)iI), ((GC.getSpecialistInfo(eSpecialist).getYieldChange(iI) + getLocalSpecialistYieldChange(eSpecialist, YieldTypes(iI))) * iChange));

I'm not a C++ expert, so maybe it has the same effect (I'd expect the compiler to balk if not):

YieldTypes(iI) -- instantiation, or compiled as cast?​

versus

(YieldTypes)iI -- standard cast​

setBuildingHelp()

First, setYieldChangeHelp() defaults bNewLine to true, resulting in the extra line and a bullet character. I assume you want each specialist to appear on its own line preceded by a bullet. Try this:

Code:
[s]szBuffer.append(NEWLINE);[/s]
[B]szFirstBuffer = [/B]gDLL->getText("TXT_KEY_BUILDING_FROM_THIS_BASE", [s]GC.getBuildingInfo(eBuilding).getLocalSpecialistYieldChangeArray(iI),[/s] GC.getSpecialistInfo((SpecialistTypes) iI).getTextKeyWide());
setYieldChangeHelp(szBuffer, L"", L"", [B]szFirstBuffer[/B], GC.getBuildingInfo(eBuilding).getLocalSpecialistYieldChangeArray(iI));

<English>[SPACE]from [COLOR_UNIT_TEXT][LINK=literal]%s1_SpclstName[\LINK][COLOR_REVERT] in This Base</English>

The first step replaces the specialist name in the "from ... in this Base" fragment. Then setYieldChangeHelp() builds the whole line from the array values and the fragment ending.

You'll need to change the translations accordingly.

If I add the Genejack Factory (building which gives the extra hammers to citizens) in Worldbuilder while I already had two citizens appointed, there's no extra yield. When I remove the citizen specialists, the city production changes to -1.

You need to add the change in yield rate for the building multiplied by the number of specialists in processBuilding() or changeLocalSpecialistYieldChange(). This is similar to what processSpecialist() does.

I don't see why Engineers are affected differently nor why you get the wrong value when adding a citizen (9 versus 7 hammers).
 
Thanks for the help! :)
The building help text works correctly now.
Unfortunately the change of the yieldtypes brackets doesn't solve the problem.

You need to add the change in yield rate for the building multiplied by the number of specialists in processBuilding() or changeLocalSpecialistYieldChange(). This is similar to what processSpecialist() does.

Isn't that code already in? :confused:
 
Isn't that code already in? :confused:

Not as far as I can tell.

When you add a specialist, processSpecialist() calls changeBaseYieldRate() with the specialist's local yield values multiplied by the change in number of specialists.

However, when you add/remove a building, you are only changing the local specialist yields used inside processSpecialist(). You aren't changing the current base yield rate.

Change the nested loops in processBuilding() to this:

Code:
// I reversed the order of the loops: yields and then specialists
// I think I reversed all the iI/iJ in the rest as well
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
	iTotalYieldChange = 0
	for (iJ = 0; iJ < GC.getNumSpecialistInfos(); iJ++)
	{
		iYieldChange = GC.getBuildingInfo(eBuilding).getLocalSpecialistYieldChange(iJ, iI) * iChange
		iTotalYieldChange += iYieldChange * getSpecialistCount(((SpecialistTypes)iJ))
		changeLocalSpecialistYieldChange(((SpecialistTypes)iJ), ((YieldTypes)iI), iYieldChange);
	}
	changeBaseYieldRate(((YieldTypes)iI), iTotalYieldChange)
}
 
Yeah I figured it out. :D The solution is not what you say, but your reference to changeBaseYieldRate set me on the right track. Thanks! :)

I can give the long story if you want, but the short one is:

The change mentioned in the first post to ProcessSpecialist should be removed. That function already points towards updateExtraSpecialistYield(). And updateExtraSpecialistYield() already changes changeBaseYieldRate. So that second reference in ProcessSpecialist had as consequence the genejac citizen bonus was counted twice.

Likewise, your code also sometimes had as consequence the bonus was counted twice. The solution is to simply include a reference towards updateExtraSpecialistYield(), which does the rest.

Code:
		for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
		{
			for (iJ = 0; iJ < NUM_YIELD_TYPES; iJ++)
			{
				changeLocalSpecialistYieldChange(((SpecialistTypes)iI), ((YieldTypes)iJ), (GC.getBuildingInfo(eBuilding).getLocalSpecialistYieldChange(iI, iJ) * iChange));
				[i]updateExtraSpecialistYield();[/i]
			}
		}


Now just figure out how to fix specialisthelp messages. :scared:
 
The change mentioned in the first post to ProcessSpecialist should be removed. That function already points towards updateExtraSpecialistYield().

I thought that seemed odd for processSpecialist() to call changeBaseYieldRate() given my limited scanning of the other SDK code. Glad I was able to help partially.

Now just figure out how to fix specialisthelp messages. :scared:

Can you summarize this last part? You had a lot of code and description in your previous post, and I think I missed this part. I assume that the hover text for specialists is also incorrect. If so, how and what's the latest code?
 
The basic problem is:

With the unmodded code...
Code:
		for (iI = 0; iI < NUM_YIELD_TYPES; ++iI)
		{
			if (GC.getGameINLINE().getActivePlayer() == NO_PLAYER)
			{
				aiYields[iI] = GC.getSpecialistInfo(eSpecialist).getYieldChange(iI);
			}
			else
			{
				aiYields[iI] = GET_PLAYER((pCity != NULL) ? pCity->getOwnerINLINE() : GC.getGameINLINE().getActivePlayer()).specialistYield(eSpecialist, ((YieldTypes)iI));
			}
		}
...the civilopedia works fine, but Citizen specialist mouseover in the city screen claim they give +1 hammer, even with a Genejack Factory present.

With this code however...
Code:
		for (iI = 0; iI < NUM_YIELD_TYPES; ++iI)
		{
			if (GC.getGameINLINE().getActivePlayer() == NO_PLAYER)
			{
				aiYields[iI] = GC.getSpecialistInfo(eSpecialist).getYieldChange(iI);
			}
			else
			{
				int LocalYieldChange = 0;
				if (pCity != NULL)
				{
					LocalYieldChange = pCity->getLocalSpecialistYieldChange(eSpecialist, (YieldTypes)iI);
					aiYields[iI] = GET_PLAYER((pCity != NULL) ? pCity->getOwnerINLINE() : GC.getGameINLINE().getActivePlayer()).specialistYield(eSpecialist, ((YieldTypes)iI)) + LocalYieldChange;
				}
			}
		}
... the Citizen in the city screen is correctly stated as giving +3 hammers with a Genejack Factory present, but in the Civilopedia all specialists are said to provide "+ infinite hammers, + infinite commerce".

Also, I don't understand this line
Code:
aiYields[iI] = GET_PLAYER((pCity != NULL) ? pCity->getOwnerINLINE() : GC.getGameINLINE().getActivePlayer()).specialistYield(eSpecialist, ((YieldTypes)iI)) + LocalYieldChange;

"x : y ? z" means "if x, then y, else z", no? I don't see how "pCity->getOwnerINLINE()" can result in a yield number. :confused:
 
"x : y ? z" means "if x, then y, else z", no? I don't see how "pCity->getOwnerINLINE()" can result in a yield number. :confused:

It's all about the parentheses:

Code:
aiYields[iI] = GET_PLAYER[COLOR="Red"][B]([/B][/COLOR][COLOR="Green"][B]([/B][/COLOR]pCity != NULL[COLOR="Green"][B])[/B][/COLOR] ? pCity->getOwnerINLINE() : GC.getGameINLINE().getActivePlayer()[COLOR="Red"][B])[/B][/COLOR].specialistYield(eSpecialist, ((YieldTypes)iI)) + LocalYieldChange;
In other words, if pCity is valid then we get the owner of the city and if it's not we get the active player. Then we call the specialistYield() method on whichever one of those 2 players was picked and add the change to the result.

As for the infinite hammers problem, I haven't been following the discussion closely enough to comment, plus I'm sleepy ;)
 
Easy part first. ;)

"x : y ? z" means "if x, then y, else z", no?

Yes, but it's "x ? y : z" (as in the code). Your confusion is due to missing a parenthesis in the code. It is choosing a player ID to pass to GET_PLAYER(): pCity's owner if it exists or the active player. Then it calls specialistYield() on the player retrieved.

Edit: Crosspost with Dresden.

As for the hover text, the problem is that when no city is selected (in Civilopedia), you don't calculate the yield at all.

You just need to move the last line out of its enclosing block:

Code:
else
{
	int LocalYieldChange = 0;
	if (pCity != NULL)
	{
		LocalYieldChange = pCity->getLocalSpecialistYieldChange(eSpecialist, (YieldTypes)iI);
		[s]aiYields[iI] = GET_PLAYER((pCity != NULL) ? pCity->getOwnerINLINE() : GC.getGameINLINE().getActivePlayer()).specialistYield(eSpecialist, ((YieldTypes)iI)) + LocalYieldChange;[/s]
	}
	[B]aiYields[iI] = GET_PLAYER((pCity != NULL) ? pCity->getOwnerINLINE() : GC.getGameINLINE().getActivePlayer()).specialistYield(eSpecialist, ((YieldTypes)iI)) + LocalYieldChange;[/B]
}
 
Top Bottom