As a French person I feel like it's my duty to explain strikes to you. - AdrienIer

Create an account  

 
Curious Civplayer - Mysteries of the DLL

What might have fooled you in testing is that it's asymmetrical. If there are connections from Civ X to Civ Y through Civ Z's territory, and then X and Z go to war, that breaks the trade routes for Civ X but not for Civ Y.

It's also possible there still existed a route between X and Y that was outside Z's territory that you didn't realize was there.
Reply

@Charriu: Do you have any idea what getSorenRandNum does exactly? Is that a wrapper around the normal random number generator or something Firaxis-specific?
Reply

I have to look it up, but my guess is that it's in the closed part of the SDK
Mods: RtR    CtH

Pitboss: PB39, PB40PB52, PB59 Useful Collections: Pickmethods, Mapmaking, Curious Civplayer

Buy me a coffee
Reply

It's no wrapper but the 'normal' random number generator. Note that in Civ4 different types of random number generators exists.
The internal generator class is defined in CvRandom.h/cpp.

(Python-Interface)
CyGame().getMapRand(), CyGame().getSorenRand(), CyGlobalContext().getASync()
gives you access to the internal C++ objects. Here you could set a new seed, etc.

getMapRandNum(UpperBound, LogMsg), getSorenRandNum(...)
are helper functions. They returning a random number directly and producing a log entry.
Only(?) the 'Soren' variant guarantees that all clients evaluating the same number. It's IHMO named by Soren Johnson who wrote the net code of the game.
Reply

You always generate +1 free beaker per turn, and you generate 20% additional beakers for each optional prerequisite tech you already know. BW always gets researched at 120% of base beakers, but if you had Myst and Mining, then Masonry would be researched at 140% of base.
Current games (All): RtR: PB80 Civ 6: PBEM23

Ended games (Selection): BTS games: PB1, PB3, PBEM2, PBEM4, PBEM5B, PBEM50. RB mod games: PB5, PB15, PB27, PB37, PB42, PB46, PB71. FFH games: PBEMVII, PBEMXII. Civ 6:  PBEM22 Games ded lurked: PB18
Reply

The maintenance formulas are fairly complex. I know really good resources for those only in German. There is probably something on civfanatics.com (could be outdated though). See attachment for my cost spreadsheet.

Beaker added to tech is

(t + 1) * (1 + 0.2 * #arrows + 0.3 * #players knowing tech / #players total)

t is the number you see in the upper left corner. Arrows are the arrows in the tech screen from techs you know to the tech you are researching. For example, Bronzeworking has one arrow from Mining. Since Mining is a necessary prerequisite you always get the 0.2 bonus from knowing Mining when researching Bronzeworking. Suppose that there are 7 players in the game and one of has Bronzeworking already. (You have to have met them for the bonus to work.) Then the total multiplyer is 0.2 (from Mining arrow) + 0.3 / 7 = 0.04 (from one contact knowing the tech rounded to 2 decimals). If you make 10 beakers from palace, city tile and maybe one river tile worked total beakers added to tech is

(10 + 1) * 1.24 = 13 (rounded down again).


Attached Files
.zip   Kosten2.zip (Size: 11.55 KB / Downloads: 6)
Reply

I should mention that one of the constants going into the maintenance formula is the map root which is the maximal distance (by plot distance, are you familiar with that?) of two tiles of the map. Unfortunately, you need to know the exact map dimensions to calculate that. So I usually just reverse engineer the map root when I settle the second city.
Reply

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.

  1. A unit steps on a tile with a goody hut
  2. 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:

  1. Under a one city challenge as a human player you never receive a unit that can found a city (GOODY_SETTLER)
  2. In a multiplayer game you never receive a unit with combat strength > 0 that can attack. (GOODY_WARRIOR)
  3. 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:

  1. You trigger the goody hut with something other then a unit (border pops)
  2. Your unit can not receive any more promotions.
  3. 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:

  1. You trigger the goody hut with something other then a unit (border pops)
  2. 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:

  1. You play with the No barbarian game option
  2. You have no cities
  3. You have only one city and the goody hut is at a plot distance of 7 or lower.
  4. You trigger the goody hut with something other then a unit (border pops)
  5. 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.
Mods: RtR    CtH

Pitboss: PB39, PB40PB52, PB59 Useful Collections: Pickmethods, Mapmaking, Curious Civplayer

Buy me a coffee
Reply

Which tiles are animals allowed to enter?

I recently read that animals cannot enter tiles which a resource, so I figured this is worthwhile exploring some more. So the game determines all the paths a unit can go and checks each tile on that path with the canMoveInto method found in CvUnit.cpp. In there are a lot of different checks, but the interesting part for us in this:

Code:
    if (isAnimal())
    {
        if (pPlot->isOwned())
        {
            return false;
        }

        if (!bAttack)
       {
            if (pPlot->getBonusType() != NO_BONUS)
            {
                return false;
            }

            if (pPlot->getImprovementType() != NO_IMPROVEMENT)
           {
                return false;
            }

            if (pPlot->getNumUnits() > 0)
            {
                return false;
            }
        }
    }

So first of all all animals are not allowed to enter a tile that already is in anybodies culture, which is the isOwned check. The following three checks are only done if moving into the tile does not result in a combat. These test are also straight forward. Animals are not allowed to enter the tile if it contains:

- any bonus resource (hidden or not)
- any improvements
- any unit


and that's it. Fun fact if you ever happen to own an animal as a player all these rules apply to them too. So giving an animal to a player is basically an easy hidden bonus resource detector.
Mods: RtR    CtH

Pitboss: PB39, PB40PB52, PB59 Useful Collections: Pickmethods, Mapmaking, Curious Civplayer

Buy me a coffee
Reply

Thanks again for these posts, Charriu! I had no idea about animals not being able to enter resource tiles.
Reply



Forum Jump: