FfH2 0.16 Bug Thread

Unser Giftzwerg said:
It seems that recon units that 'go stealthy' in forests can also make friendly units in the same tile invisible. I had a Ranger stacked with several weaker units, Priests and magic users, and the entire tile appeared empty. The most powerful unit was the recon unit, so it was at teh top of the stack, it was invisible, so the whole stack was invisible.

I don't know if an enemy player, AI or human, could see the Priest and the magic-users. I suspect not.

Certainly recon units were not intended to mask entire stacks?

Did every unit in the tile get the promotion? If not, I suspect it's just a graphical error. It might be nifty to do in multiplayer games if you think your foe isn't paying attention, but the AI certainly wouldn't be fooled.
 
When you take an enemy city without the Order from someone who has the Order as staterelegion with Sphener (Evangelist promotion), the Order gets added to the city before the owner of the city is changed. So your enemy gets the free disciple unit and you have to kill an additional unit before you get the city, quite annoying.
 
gandhi rules said:
Shouldent the religon bug have been fixed for years ago?

It was reported a while ago, but I still havent been able to fix it. Sorry.
 
Patch "d" is up and linked in the first post.
 
xanaqui42 said:
There is a defect in how the random number generator is used.

I injected the following code into onGameStart:

Spoiler :
Code:
		for iPlayer in range(gc.getMAX_PLAYERS()):
			player = gc.getPlayer(iPlayer)
			if (player.isAlive() and player.isHuman()):
				py = PyPlayer(iPlayer)
				for pUnit in py.getUnitList():
					if pUnit.getUnitClassType() == iSettler:
						pSettler = pUnit
				iRand1 = CyGame().getSorenRandNum(3, "Bob")
				iRand2 = CyGame().getSorenRandNum(3, "Bob")
				iRand3 = CyGame().getSorenRandNum(3, "Bob")
				iRand4 = CyGame().getSorenRandNum(3, "Bob")
				iRand5 = CyGame().getSorenRandNum(3, "Bob")
				iRand6 = CyGame().getSorenRandNum(3, "Bob")
				iRand7 = CyGame().getSorenRandNum(3, "Bob")
				iRand8 = CyGame().getSorenRandNum(3, "Bob")
				iRand9 = CyGame().getSorenRandNum(3, "Bob")
				iRand10 = CyGame().getSorenRandNum(3, "Bob")
				iRand11 = CyGame().getSorenRandNum(3, "Bob")
				iRand12 = CyGame().getSorenRandNum(3, "Bob")
				iRand13 = CyGame().getSorenRandNum(3, "Bob")
				iRand14 = CyGame().getSorenRandNum(3, "Bob")
				iRand15 = CyGame().getSorenRandNum(3, "Bob")
				iRand16 = CyGame().getSorenRandNum(3, "Bob")
				iRand17 = CyGame().getSorenRandNum(3, "Bob")
				iRand18 = CyGame().getSorenRandNum(3, "Bob")
				iRand19 = CyGame().getSorenRandNum(3, "Bob")
				iRand20 = CyGame().getSorenRandNum(3, "Bob")
				iRand21 = CyGame().getSorenRandNum(3, "Bob")
				iRand22 = CyGame().getSorenRandNum(3, "Bob")
				iRand23 = CyGame().getSorenRandNum(3, "Bob")
				iRand24 = CyGame().getSorenRandNum(3, "Bob")
				iRand25 = CyGame().getSorenRandNum(3, "Bob")
				iRand26 = CyGame().getSorenRandNum(3, "Bob")
				iRand27 = CyGame().getSorenRandNum(3, "Bob")
				iRand28 = CyGame().getSorenRandNum(3, "Bob")
				iRand29 = CyGame().getSorenRandNum(3, "Bob")
				iRand30 = CyGame().getSorenRandNum(3, "Bob")
				sMessage = '%(Number1)01d ' % {'Number1': iRand1} + \
					'%(Number2)01d ' % {'Number2': iRand2} + \
					'%(Number3)01d ' % {'Number3': iRand3} + \
					'%(Number4)01d ' % {'Number4': iRand4} + \
					'%(Number5)01d ' % {'Number5': iRand5} + \
					'%(Number6)01d ' % {'Number6': iRand6} + \
					'%(Number7)01d ' % {'Number7': iRand7} + \
					'%(Number8)01d ' % {'Number8': iRand8} + \
					'%(Number9)01d ' % {'Number9': iRand9} + \
					'%(Number10)01d ' % {'Number10': iRand10} + \
					'%(Number11)01d ' % {'Number11': iRand11} + \
					'%(Number12)01d ' % {'Number12': iRand12} + \
					'%(Number13)01d ' % {'Number13': iRand13} + \
					'%(Number14)01d ' % {'Number14': iRand14} + \
					'%(Number15)01d ' % {'Number15': iRand15} + \
					'%(Number16)01d ' % {'Number16': iRand16} + \
					'%(Number17)01d ' % {'Number17': iRand17} + \
					'%(Number18)01d ' % {'Number18': iRand18} + \
					'%(Number19)01d ' % {'Number19': iRand19} + \
					'%(Number20)01d ' % {'Number20': iRand20} + \
					'%(Number21)01d ' % {'Number21': iRand21} + \
					'%(Number22)01d ' % {'Number22': iRand22} + \
					'%(Number23)01d ' % {'Number23': iRand23} + \
					'%(Number24)01d ' % {'Number24': iRand24} + \
					'%(Number25)01d ' % {'Number25': iRand25} + \
					'%(Number26)01d ' % {'Number26': iRand26} + \
					'%(Number27)01d ' % {'Number27': iRand27} + \
					'%(Number28)01d ' % {'Number28': iRand28} + \
					'%(Number29)01d ' % {'Number29': iRand29} + \
					'%(Number30)01d ' % {'Number30': iRand30}
				CyInterface().addMessage(pSettler.getOwner(),True,25,sMessage,'AS2D_DISCOVERBONUS',1,'Art/Interface/Buttons/Religions/Dragon.dds',ColorTypes(8),pSettler.getX(),pSettler.getY(),True,True)
This gives me 30 concecutive "random" numbers on game start-up. I got a total of 10 samples, for a total of 300 "random" numbers. All of these numbers gave me a result in the set {0,1,2}.

The problem is that virtually everywhere (perhaps everywhere) in the python code, the programmers appear to have made the assumption that: CyGame().getSorenRandNum(X, "Bob") given a number in the range {1..X}. It does not; it gives a number in the range {0..X-1}.

This means that a lot of statements that are comparisons with random numbers that are presently <= should be <. as an example, the line:
Code:
if CyGame().getSorenRandNum(100, "Mutation") <= 5:
has a 6% chance of triggering (assuimng that this PSRNG is actually random, which it probably isn't; see below), not the (presumably intended) 5% chance.

Are you planning to fix this range defect within the next few releases? If not, I should make a note in the wiki, and change some entries to reflect the actuals.

In case this helps, here are the LOC I'm guessing are defective; in some cases it's obvious (like Hellfire expecting out-or range values, or an explicit percentage chance), in other cases it's more of a guess (like the assumption that the songs are supposed to be equally weighted). I would not be surprised if I ignored some cases where 1-X was expected, but 0-(X-1) is being generated.:
Spoiler :
Code:
The most common problem is the comparison; <= should be <. Exceptions are noted below.

[B]CustomFunctions.py:[/B]
Line 48:
			if CyGame().getSorenRandNum(10000, "Add Barrows") <= iBarrowChance:
Line 57:
			if CyGame().getSorenRandNum(10000, "Add Ruins") <= iRuinsChance:
Line 78:
				if (iRand <= 75):
Line 82:
				if (iRand <= 50):
Line 86:
				if (iRand <= 25):
Line 164: Assuming that you don't want the current plot, add 1.
						iCurrentPlot = iCurrentPlot + CyGame().getSorenRandNum(5, "FFHFindClearPlot")
Line 187: Assuming that you don't want the current plot, add 1.
					iCurrentPlot = iCurrentPlot + CyGame().getSorenRandNum(5, "FFHFindClearPlot")
Line 248:
					if (iRand <= iPercent):
Line 324:
		if iRnd <= i:
Lines 416-430: All of these numbers should be reduced by 1. Conversely, add 1 to iRnd before line 416.
				if iRnd == 2:
					iUnit = gc.getInfoTypeForString('UNIT_ASH_BEARER')
				if iRnd == 3:
					iUnit = gc.getInfoTypeForString('UNIT_GRAVEBORN')
				if iRnd == 4:
					iUnit = gc.getInfoTypeForString('UNIT_HELLHOUND')
				if iRnd == 5:
					iUnit = gc.getInfoTypeForString('UNIT_SECT_OF_FLIES')
				if iRnd == 6:
					iUnit = gc.getInfoTypeForString('UNIT_HUNTING_DEMON')
				if iRnd == 7:
					iUnit = gc.getInfoTypeForString('UNIT_EXECUTIONER')
				if iRnd == 8:
					iUnit = gc.getInfoTypeForString('UNIT_INFERNAL_LORD')
				if iRnd >= 9:
[B]CvEventManager.py[/B]
Line 425:
				if CyGame().getSorenRandNum(100, "Barrow") <= iLairSpawnChance:
Line 427:
				if CyGame().getSorenRandNum(100, "Ruins") <= iLairSpawnChance:
Line 435:
					if CyGame().getSorenRandNum(100, "Ancient Forest") <= iAncientForestChance:
Line 439:
				if CyGame().getSorenRandNum(100, "Bob") <= 40:
Line 467:
			if CyGame().getSorenRandNum(10000, "Bob") <= gc.getDefineINT('INSANE_CHANCE'):
Line 578: The first comparison is fine.
				if (pUnit.getExperience() < iHeroMaxXP and CyGame().getSorenRandNum(100, "Bob") <= iHeroXPChance):
Line 583:
				if CyGame().getSorenRandNum(100, "Bob") <= iCharmedExpireChance:
Line 586:
				if CyGame().getSorenRandNum(100, "Bob") <= 10:
Line 589:
				if CyGame().getSorenRandNum(100, "Bob") <= iChaosMarauderBetrayalChance:
Line 605:
				if (pUnit.getUnitClassType() != iBerserker and CyGame().getSorenRandNum(100, "Bob") <= 5):
Line 610:
						if CyGame().getSorenRandNum(100, "Bob") <= 3:
LIne 685: The first comparison is fine.
					if (pUnit.cargoSpaceAvailable(-1, gc.getInfoTypeForString('DOMAIN_LAND')) > 0 and CyGame().getSorenRandNum(100, "Sailors Dirge") <= iSailorsDirgeChance):
Line 828:
				if CyGame().getSorenRandNum(100, "Bob") <= gc.getDefineINT('SLAVERY_CHANCE'):
Line 843:
				if iRnd <= 50:
Line 853:
						if CyGame().getSorenRandNum(100, "Bob") <= gc.getDefineINT('DISEASE_CHANCE'):
Line 907:
				if CyGame().getSorenRandNum(100, "Bob") <= 50:
Line 927:
			if (iRand <= gc.getDefineINT('WEREWOLF_BASE_CHANCE') - (gc.getDefineINT('WEREWOLF_MINUS_PER_WEREWOLF') * iNumWW)):
Line 949:
			if (iRand <= 5):
Line 1054:
			if CyGame().getSorenRandNum(100, "Mercenary") <= gc.getDefineINT('MERCENARY_CONVERSION_CHANCE'):
Line 1198:
			if CyGame().getSorenRandNum(100, "Bob") <= 20:
Line 1263:
					if (CyGame().getSorenRandNum(100, "Apocalypse") <= iPercent):
Line 1340:
						if (iRand <= gc.getDefineINT('WRATH_CONVERT_CHANCE')):
Line 1412:
				if CyGame().getSorenRandNum(10000, "Hellfire") <= iHellfireChance:
Line 1436:
			if CyGame().getSorenRandNum(100, "Bob") <= iChance:
Line 1585:
			if iRnd <= 20:
Line 1681:
			if CyGame().getSorenRandNum(100, "Bob") <= 25:
Line 1690:
				if CyGame().getSorenRandNum(100, "Bob") <= gc.getDefineINT('CULT_ADOPTION_CHANCE'):
Line 1878:
				if (iRnd <= gc.getDefineINT('ORDER_CRUSADER_SPAWN_CHANCE')):
Line 2166: The first comparison is fine.
					if (pUnit.getUnitType() == gc.getInfoTypeForString('UNIT_LOKI') and CyGame().getSorenRandNum(100, "Loki") <= gc.getDefineINT('LOKI_UNREST_CHANCE')):
Line 2182:
			if CyGame().getSorenRandNum(100000, "Bob") <= gc.getDefineINT('EVENT_CHANCE_CITY'):
[B]FFHSpells.py[/B]
Line 127: Assuming that you want a different plot, add 1.
				iCurrentPlot = iCurrentPlot + CyGame().getSorenRandNum(5, "FindClearPlot")
Line 203:
	if (CyGame().getSorenRandNum(100, "Bob") <= iResist or target.isHasPromotion(gc.getInfoTypeForString('PROMOTION_MAGIC_IMMUNE'))):
Line 356:
	if CyGame().getSorenRandNum(100, "Burning Blood") <= 75:
Line 383:
	if CyGame().getSorenRandNum(100, "Burning Blood Self") <= 75:
Line 1011:
					if (gc.getBuildingInfo(i).getConquestProbability() != 100 and iRnd <= 25):
Line 1014:
			if iRnd <= 25:
Line 1493:
			if iRnd <= 15:
Line 1499:
			if CyGame().getSorenRandNum(100, "Mutation") <= 15:
Line 1501:
			if CyGame().getSorenRandNum(100, "Mutation") <= 15:
Line 1503:
			if CyGame().getSorenRandNum(100, "Mutation") <= 10:
Line 1505:
			if CyGame().getSorenRandNum(100, "Mutation") <= 10:
Line 1507:
			if CyGame().getSorenRandNum(100, "Mutation") <= 5:
Line 1509:
			if CyGame().getSorenRandNum(100, "Mutation") <= 5:
Line 1511:
			if CyGame().getSorenRandNum(100, "Mutation") <= 5:
Line 1734:
		if iRnd <= 10:
Line 1740:
		elif iRnd <= 20:
Line 1746:
		elif iRnd <= 30:
Line 1752:
		elif iRnd <= 40:
Line 1758:
		elif iRnd <= 50:
Line 1764:
		elif iRnd <= 60:
Line 1770:
		elif iRnd <= 70:
Line 1776:
		elif iRnd <= 80:
Line 1782:
		elif iRnd <= 90:
Line 1788:
		elif iRnd <= 100:
Line 2230: Change to >=
	if iRnd > 25:
Line 2232: Change to >=
	if iRnd > 50:
Line 2234: Change to >=
	if iRnd > 75:
Line 2710:
	if CyGame().getSorenRandNum(100, "Bob") <= gc.getDefineINT('TSUNAMI_FLOOD_CHANCE'):
[B]CvGameInterface.py[/B]
Line 96: Change to >=
			if CyGame().getSorenRandNum(100, "Fear") > iBraveChance:
Line 112: Change to >=
			if CyGame().getSorenRandNum(100, "Fear") > iBraveChance:

Note that this has nothing to do with the randomness (or lack thereof) of getSorenRandNum.
 
Got this error screen when upgrading an zealot to a cultist (not sure if those are correct, talking about oo tier1 and tier2 priests.)

 
Where am I getting the priest from?

EDIT: Nevermind I just realized that it was from the religious discipline civic.
 
That's not a bug in FfH, it's a feature in vanilla. Someone did some analysis, and it's strongly affected by culture, tending towards your low-culture cities - in vanilla, your second city often gets an early religion, in FfH it's more varied because you tend to have more cities by the time any religions are founded.
 
But please can you randomalize this cuz some civics gets +production in capital and i will never build my mithril golem if its not in capital...
 
gandhi rules said:
But please can you randomalize this cuz some civics gets +production in capital and i will never build my mithril golem if its not in capital...

We do it that way so the player doesn't have all of their eggs in one basket. We want their to be more strategic posibilities. Having one city be a civs capital, and a holy city for a particular religion is to much. So we spread them out, both for the player and for your opponents, so their are more options.

Besides its difficult for religions to flourish under the direct gaze of government. But the real reason is a game design one from the paragraph above.

FYI: in FfH and vanilla (we havent changed it) it doesn't have anything to do with culture. It typically goes to the city with the largest population that isn't your capital, and doesnt have another religion.
 
You can move your capital, you know - if you absolutely want the holy city to be your capital, just build a palace in it.
 
Maybe...
Drive on settler for fun (owns evrybody) But discovred something that even on settler the dirge take my runes holy city whith his deam skeletons and maked orthus in that city and maked it to the lair AND killed my poor little rosier the fallen (rust in peace)
 
A few items:

1: Other nation's ships are attacking hidden nationality units in my ships' cargo holds. Any chance of applying the same patch that stops them killing HN units in cities?

2: Shadows still can't see other shadows. Is this intentional?

3: I just finished up a game with the Doviello, and there seem to be two tricks using Graft Flesh which may be a bit exploitable:

Firstly, if you graft a Battlemaster with a slightly-promoted Adept, you get a free Govannon-style trainer, able to teach raise skeleton etc to anyone who happens along. If this isn't intended, the easy solution would be to separate out the trainer promotions

Secondly, if you graft a unit whose strength has already been improved by a weapons promotion, the new golem gets another bonus on top. This one's also exploitable by any other civilisation with mercenaries and mithril.

Using two OO high priests and a mithril-wielding mercenary, you can summon a kraken and graft it to the merc to get a free 14 str, empower x5 hidden nationality golem every turn. The mercenary is even kind enough to summon the next graft victim himself for a minimal fee.
 
my problem is the same boring one, though i dont understand why others arent getting it - the game gets really laggy (this is a whole new game from my last post with the patch correctly loaded) after a certain point (it seems to be near the completion of bazaar of mammon) the ai turn time gets longer and longer until its over ten minutes and you have shut it down ...
this time i was a good boy and enabled python popups though none came up
here is the save
thank you for all yalls hard work i think ive spent more hours on ffh than on all four civs combined ,,,
 
Error obtained when trying to "kill" a unit with a vampire.
 

Attachments

  • KillError0000.JPG
    KillError0000.JPG
    128.2 KB · Views: 84
Back
Top Bottom