How is the goody from goody huts determined
The interesting part starts in CvPlayer.cpp and in there in the method
doGoody. But first we do need to know who calls this method. There are only to (obvious) situations in which it is called.
- A unit steps on a tile with a goody hut
- You pop borders and trigger a goody hut
With that out of the way we look into the method.
Code:
void CvPlayer::doGoody(CvPlot* pPlot, CvUnit* pUnit)
...
long result=0;
bool ok = gDLL->getPythonIFace()->callFunction(PYGameModule, "doGoody", argsList.makeFunctionArgs(), &result);
if (ok && result)
{
return;
}
FAssertMsg(pPlot->isGoody(), "pPlot->isGoody is expected to be true");
pPlot->removeGoody();
if (!isBarbarian())
{
for (int iI = 0; iI < GC.getDefineINT("NUM_DO_GOODY_ATTEMPTS"); iI++)
{
if (GC.getHandicapInfo(getHandicapType()).getNumGoodies() > 0)
{
GoodyTypes eGoody = (GoodyTypes)GC.getHandicapInfo(getHandicapType()).getGoodies(GC.getGameINLINE().getSorenRandNum(GC.getHandicapInfo(getHandicapType()).getNumGoodies(), "Goodies"));
FAssert(eGoody >= 0);
FAssert(eGoody < GC.getNumGoodyInfos());
if (canReceiveGoody(pPlot, eGoody, pUnit))
{
receiveGoody(pPlot, eGoody, pUnit);
// Python Event
CvEventReporter::getInstance().goodyReceived(getID(), pPlot, pUnit, eGoody);
break;
}
}
}
}
}
First of all notice that we put two things into the method a CvPlot and a CvUnit. The CvPlot should be fairly obvious as it is the plot that holds the goody hut. The CvUnit is the unit that triggered the goody hut or in the case of a border pop it is null, which will become important later.
We start the method with some boring initialization stuff. There first interesting stuff is this part:
Code:
long result=0;
bool ok = gDLL->getPythonIFace()->callFunction(PYGameModule, "doGoody", argsList.makeFunctionArgs(), &result);
if (ok && result)
{
return;
}
Looks like the developers prepared something for modders. With this you can code stuff in python that will be triggered instead of the following code. But we forget that soon as this is only for modders.
After some security checks we remove the goody hut from the plot and check if the player triggering the goody hut is not barbarian. All of that should be very obvious, but next we encounter something interesting.
Code:
for (int iI = 0; iI < GC.getDefineINT("NUM_DO_GOODY_ATTEMPTS"); iI++)
{
if (GC.getHandicapInfo(getHandicapType()).getNumGoodies() > 0)
{
GoodyTypes eGoody = (GoodyTypes)GC.getHandicapInfo(getHandicapType()).getGoodies(GC.getGameINLINE().getSorenRandNum(GC.getHandicapInfo(getHandicapType()).getNumGoodies(), "Goodies"));
...
if (canReceiveGoody(pPlot, eGoody, pUnit))
{
receiveGoody(pPlot, eGoody, pUnit);
...
break;
}
}
}
We start a for-loop. This "NUM_DO_GOODY_ATTEMPTS" is set to 10, so the game makes 10 attempts of determining the effect of the goody hut. Next up we look at our current handicap and make sure that there are actual goody effects assigned to the handicap (in BtS every handicap has goody effects). For this you need to cross-reference to Civ4HandicapInfo. In there you find the Goodies tag containing all the effects.
After making sure we have effects we choose one of the handicaps goodie effects at random. The weighting happens inside Civ4HandicapInfo by adding multiple effects of the same kind.
After another safety check we check if you can actually receive the goody effect in
canReceiveGoody and this is where things get really interesting and I will return to that shortly. When we can receive the rolled goody effect we just receive it. There are a few more things related to barbs and techs happening in the
receiveGoody method, but for the most part it is straight forward and nothing surprising.
Now remember that the game makes 10 attempts at rolling the effect and if you fail
canReceiveGoody 10 times in a row then you get nothing.
The goodie effects
The goodie effects referenced in Civ4HandicapInfo are all defined in the Civ4GoodyInfo.xml.
GOODY_LOW_GOLD, GOODY_HIGH_GOLD
canReceiveGoody always returns true for this effect, so you will always get it. The amount of gold you get is fairly simple calculated:
iGold + [random amount of gold between 0 and iGoldRand1] + [random amount of gold between 0 and iGoldRand2]
This sum is then multiplied by (iGrowthPercent / 100) from Civ4GameSpeedInfo.xml which on normal is (100 / 100). You receive the result in gold.
GOODY_MAP
canReceiveGoody always returns true for this effect, so you will always get it. The method of revealing random tiles of the map is a bit complicated and there isn't anything in there, which you as a player could manipulate. To keep this post shorter I leave the actual calculation out for now.
GOODY_SETTLER, GOODY_WARRIOR, GOODY_SCOUT, GOODY_WORKER
All of these give you a unit (determined by UnitClass). But there are some strings attached to them in the
canReceiveGoody method, which returns false under these circumstances, therefore preventing the effect from happening:
- Under a one city challenge as a human player you never receive a unit that can found a city (GOODY_SETTLER)
- In a multiplayer game you never receive a unit with combat strength > 0 that can attack. (GOODY_WARRIOR)
- In the first 20 turns of a game you never receive a unit with combat strength > 0 that can attack. (GOODY_WARRIOR)
GOODY_EXPERIENCE
Your unit gets the XP defined under iExperience (5). But there are some strings attached to them in the
canReceiveGoody method, which returns false under these circumstances, therefore preventing the effect from happening:
- You trigger the goody hut with something other then a unit (border pops)
- Your unit can not receive any more promotions.
- In the first 10 turns this effect always returns false
GOODY_HEALING
Your unit is healed by iHealing (100, therefore fully healed). But there are some strings attached to them in the
canReceiveGoody method, which returns false under these circumstances, therefore preventing the effect from happening:
- You trigger the goody hut with something other then a unit (border pops)
- Your unit is above iDamagePrereq % of it's max HP (iDamagePrereq = 60)
GOODY_TECH
You receive a tech that has bGoodyTech set to 1. Those are:
- Aesthetics
- Agriculture
- Animal Husbandry
- Archery
- Astronomy
- Bronze Working
- Calendar
- Compass
- Construction
- Currency
- Drama
- Fishing
- Horseback Riding
- Hunting
- Iron Working
- Literature
- Masonry
- Mathematics
- Metal Casting
- Mining
- Monarchy
- Music
- Mysticism
- Pottery
- Priesthood
- Sailing
- The Wheel
- Writing
You need to be able to research the tech otherwise
canReceiveGoody will return false therefore denying that effect. For every tech you can research of that list a random number between 1 and 10000 is rolled. The tech with the highest number is choosen. A tie is deceided by the tech being higher in the Civ4TechInfo.xml.
GOODY_BARBARIANS_WEAK, GOODY_BARBARIANS_STRONG
These spawn barbarian units on one of the 8 neighboring goody hut tiles (unit type determined by BarbarianClass, which is set to warrior for both). The amount of units is determined by iMinBarbarians (1 and 2). The game will always spawn at least 1 barbarian unit. Every additional unit is determined by a random roll. The game rolls a number between 0 and 100 and if that number is lower then iBarbarianUnitProb (20 and 40) an additional barb is spawned. Little side note for modders. The game attempts to spawn barbs twice on every neighboring tile. That means at maximum the game makes only 16 attempts to spawn a unit.
Now what prevents this effect in
canReceiveGoody? That method returns false under the following situations:
- You play with the No barbarian game option
- You have no cities
- You have only one city and the goody hut is at a plot distance of 7 or lower.
- You trigger the goody hut with something other then a unit (border pops)
- You trigger the goody hut with a unit that has bNoBadGoodies = 1 (scout and explorer)
Now about that last point this is actually happening for each goody effect that has bBad set to 1, which in BtS are only the two barb effects.
Summary
The game makes 10 attempts to trigger a goody effect. In every attempt the game chooses 1 of the effects in Handicap.xmls goodies. It checks if it can apply that effect and if true does apply it. For all the different effects check the chapter above.