Mod-Modders Guide to Fall Further

will promotiondegradesto prevent you aquiring it if you already have what it degrades into though?

I was already going to use that anyway. But my issue is making the Regrowing Skin only apply when stoneskin has been lost, and ideally without cloning stoneskin because that is a messy thing to do.
 
has anyone ever written a bit of code to find the "least valuable" unit in a stack. ie, lowest level, and/or base strength. Generally overall the unit that a player is least likely to care about losing. I have a few applications for something like that..
 
Ok, regrowing stoneskin doesn't seem to be woring. I decided to go with a slightly different method, avoiding adding more than one promotion.


Code:
<PromotionInfo>		<!-- Regenerating Skin -->
			<Type>PROMOTION_REGENERATE_SKIN</Type>
			<Description>TXT_KEY_PROMOTION_REGENERATE_SKIN</Description>
			<bAutoAcquire>1</bAutoAcquire>
			<bNoXP>1</bNoXP>
			<iPower>11</iPower>
			<bAlwaysHeal>1</bAlwaysHeal>
			<iEnemyHealChange>10</iEnemyHealChange>
			<iNeutralHealChange>10</iNeutralHealChange>
			<iFriendlyHealChange>10</iFriendlyHealChange>
			<UnitCombats>
				<UnitCombat>
					<UnitCombatType>UNITCOMBAT_MELEE</UnitCombatType>
					<bUnitCombat>1</bUnitCombat>
				</UnitCombat>
			</UnitCombats>
			<iPromotionDuration>2</iPromotionDuration>
			<PromotionDegradesTo>
				<Promotion>PROMOTION_STONESKIN</Promotion>
			</PromotionDegradesTo>
			<PrereqUnits>
				<PrereqUnit>UNIT_STONESKIN_OGRE</PrereqUnit>
			</PrereqUnits>
			<Button>Art/Interface/Buttons/Promotions/RegenerateSkin.dds</Button>
			<iAIWeight>50</iAIWeight>
			<iMinLevel>-1</iMinLevel>
		</PromotionInfo>

I put a promotionexcludes for this on stoneskin, so it should theoretically be auto aquired by a stoneskin ogre as soon as stoneskin is gone, but it doesn't happen. Any idea why? it displays in the pedia just fine "Requires stoneskin ogre, automatically gained when requirements are met" and I definitely made sure stoneskin ogres are melee units.
 
actually it is what 0 does, but -1 means that you can never select this promo as upgrade.
 
Can anyone explain how to prevent a unit fromy dying after a battle, through pyPostCombatLost or even PyOnDeath ?

Specifically, I want to make it so that Ogre Warchiefs, instead of having normal immortality, will sacrifice one of their followers as a distraction so they always escape, and are thus invincible as long as they have units following them. Related to my above question about finding a worst unit.



also, will unit.baseCombatStr() include promotion-granted extra strength, special damage type strength, and python strBoosts in it's return value?
If not, how can I get a unit's total strength, including all of the above.
 
Ok, need proofreading/thoughts/advice on this function I wrote

The objective is to find the Worst unit on a given plot, based on a few criteria. A value is calculated for each unit, determining it's worth, and then the unit with the lowest value is returned.

To start with, a massive number full of 9s is defined as the worst value. since nothing can really feasibly be higher than that

The base value starts with the unit's strength. I've tried to calculate it's actual fighting strength at the time, factoring in how damaged it is.

After that, a modifier is worked out. This starts at the unit's level / 2, and is increased or decreased depending on whether the unit has certain positive or negative promotions.

After all the checks are done, the strength is multiplied by the modifier, and the final value is checked against the worst value so far. If it's lower, then it becomes the new lowest value, and that unit becomes the new worst unit.

Will it work, as is? Also, does anyone have design advice, on other factors which ought to be included, etc.

I have two uses in mind for this function

1. Ogre warchiefs sacrificing a weak unit to save themselves
2. Fixing the calabim Feed spell to allow eating of the weakest living unit in the stack, rather than only bloodpets.

Spoiler :
Code:
def WorstUnit(player, location):
	py = player
	pPlot = location
	pWorstUnit = -1
	fWorstValue = 9999999999999999999999999999 #Nothing returned could be higher than this value, so the first unit will always be the worst to begin with.
	for i in range(pPlot.getNumUnits()):
		fValue = 999999999999999999
		pUnit = pPlot.getUnit(i)
		if pUnit.getOwner() == player:
			iLevel = pUnit.getLevel()
			iStrength = pUnit.baseCombatStr()
			fStrength = iStrength * (1.0 - (pUnit.GetDamage() / 100))#Find the unit's actual strength by factoring in it's damage
			fModifier = iLevel / 2
			if pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_HERO')) or pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_ADVENTURER')):
				fModifier += 999999999999 #Heroes should never be chosen
			if pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_WEAK')):
				fModifier -= 1.0
			if pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_STRONG')):
				fModifier += 2.0
			if pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_CRAZED')):
				fModifier -= 1.0
			if pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_DISEASED')):
				fModifier -= 1.0
			if pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_ENRAGED')):
				fModifier -= 1.0
			if pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_UNDISCIPLINED')):
				fModifier -= 1.0
			if pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_PLAGUED')):
				fModifier -= 1.0
				
			fValue = fStrength * fModifier
			if fValue < fWorstValue:
				fWorstValue = fValue
				pWorstUnit = pUnit
	
	return pWorstUnit
 
sorry this is kinda a dumb question, but if i get an error like this:

Tag: 1 in Info class was incorrect.
Current XML file is: xml\Units/CIV4PromotionInfos.xml

how can i find and fix it? it dosnt state what line the error is on OR what causes it. have i done something to corrupt the file?
 
Actually, I would consider xp also between units of the same strength.
 
PL, sounds like somewhere you were supposed to use something like >CIVILIZATION_BANNOR< and instead used >1<

Not an easy thing to find since MANY >1< will exist (all the boolean flags), so look carefully at what you have modified lately.
 
The base value starts with the unit's strength. I've tried to calculate it's actual fighting strength at the time, factoring in how damaged it is.

So a dragon with 10 HP left will be chosen before a healthy warrior? Personally I think damage should only be used as a tiebreaker.

Also, 0 strength units will always have equal values. Not so bad for slaves, but Corlindale might object.

After that, a modifier is worked out. This starts at the unit's level / 2, and is increased or decreased depending on whether the unit has certain positive or negative promotions.

I don't think adding a bunch of numbers to a multiplier is a good way to calculate value. Stick with either multiplication or addition. Using both makes it hard to predict. (This is one of the fundamental errors in Civ.)

Will it work, as is? Also, does anyone have design advice, on other factors which ought to be included, etc.

You could replace the high value on Hero with a simple continue statement. This will immediately move on with the next iteration of the loop without considering anything further. Which in turn means that a Hero alone on a tile won't be the weakest unit there, somewhat strange but possibly desireable.

Other conditions that should immediately remove the unit from consideration are Channeling III, immortal and world units. And I'd add a requireLiving argument to the function that determines whether non-living units should be skipped too.

While we're at it Adventurer is not worth the same as Hero. It's not even worth the same as Empower I.

Lesser added value, but still huge is if the units has had it's name changed, by the player if possible. Also provides a non intrusive way to mark a unit as valuable.

Good promotions: Chanelling I & II

Bad promotions: Skirmisher, first levels of werewolf, Withered, Burning Blood

Your code counts Strong and Weak twice, once as strength and again as promotions.

Being a minion should probably add value... If you took the time to add it to an army, it's probably worth a little more than another that didn't make the cut.

Having attacked could be a tiebreaker.

National units should not affect the calculation. The valuable ones already have a high value.

Some units are inherently useless, and should have a great penalty. I'm thinking Slaves, Bloodpets and Scouts.

You'll have to decide what to do about units with duration or that that have been summoned (tigers).

---

One way to deal with options is to sort all units by value in a list and return it. The caller will have to go through the list from an appropriate end until he finds an acceptable unit.

If unitList is a list of units that should be sorted (in this case all units belonging to the player in the tile) and valueFromUnit is a funtion that takes a unit and returns it's value.
Code:
unitList.sort( key=valueFromUnit )
sorts the list in place in ascending order.

That is: unitList[0] will return the most useless unit and unitList[-1] will return the most valuable unit.
 
So a dragon with 10 HP left will be chosen before a healthy warrior? Personally I think damage should only be used as a tiebreaker.

Also, 0 strength units will always have equal values. Not so bad for slaves, but Corlindale might object.

These two things can be fixed by assigning world units an impossibly high value, so shouldn't be an issue. What are the limitations on number sizes in python, by the way? I'm thinking using 999999999999 as a multiplier could cause issues.

As to damage, I think I'll tone that down a bit, have it only apply half. So a unit with 99 damage would only be counted as 49.95% less than it's strength value.

I don't think adding a bunch of numbers to a multiplier is a good way to calculate value. Stick with either multiplication or addition. Using both makes it hard to predict. (This is one of the fundamental errors in Civ.)

How exactly would this work ? I don't think it's a fundamental error in civ at all, there are a lot of design concepts that wouldn't work well without it. But that discussion is for another time.

I've used mostly multiplication, but multiplying the multiplier is a horrible idea and would expand exponentially.

Can you explain how you'd make it work, perhaps ? The entire concept is based on user viewpoints of usefulness, so I don't think there are any real right answeres, merely the most agreeable ones.

You could replace the high value on Hero with a simple continue statement. This will immediately move on with the next iteration of the loop without considering anything further. Which in turn means that a Hero alone on a tile won't be the weakest unit there, somewhat strange but possibly desireable.

I'd like to tone that value down to something a little less likely to cause math errors, but still unfeasibly high. maybe 1000. Having heroes excluded is not desireable, I just want them to always be regarded as the most valuable.

Other conditions that should immediately remove the unit from consideration are Channeling III, immortal and world units. And I'd add a requireLiving argument to the function that determines whether non-living units should be skipped too.
I agree with you on 1 and 3 there, but 2? I don't think so. Immortal units you'd want to be MORE likely to consider, since sacrificing them won't actually make you lose anything.

While we're at it Adventurer is not worth the same as Hero. It's not even worth the same as Empower I.

You have a point, it's not as good as hero, but I still personally value it highly, it's rare and can't be achieved through any reproducible means except for two specific civs

Lesser added value, but still huge is if the units has had it's name changed, by the player if possible. Also provides a non intrusive way to mark a unit as valuable.

Can you give me a quick code snippet to test if the unit's name is different from the default ?

Good promotions: Chanelling I & II

Bad promotions: Skirmisher, first levels of werewolf, Withered, Burning Blood

good points, thank you.

Your code counts Strong and Weak twice, once as strength and again as promotions.
This is a good point, but I think it's alright. These two promotions are very powerful/very horrible, and doubling their impact on the calculations is fine with me.

Being a minion should probably add value... If you took the time to add it to an army, it's probably worth a little more than another that didn't make the cut.

A good point. How do I do this ?


Some units are inherently useless, and should have a great penalty. I'm thinking Slaves, Bloodpets and Scouts.

I'd generalise this a bit, slaves, unitclass warrior, and unitclass scout. The calabim are unlikely to have warriors other than bloodpets anyway, so a special exemption isn't needed.

You'll have to decide what to do about units with duration or that that have been summoned (tigers).

They will get banished to the bottom of the list and almost certainly be returned as worst.

---

One way to deal with options is to sort all units by value in a list and return it. The caller will have to go through the list from an appropriate end until he finds an acceptable unit.

This is a brilliant idea, and is infinitely better than how I'm currently trying to do it. I'm not too familiar with arrays in python though. Does it support multidimensional arrays, or would I need a strided list? Could you possibly show me how to put values into a list, and sort it ?

If unitList is a list of units that should be sorted (in this case all units belonging to the player in the tile) and valueFromUnit is a funtion that takes a unit and returns it's value.
Code:
unitList.sort( key=valueFromUnit )
sorts the list in place in ascending order.

How would I create unitList first of all ? And would the above code snippet test every element in the list and sort them accordingly? that seems pretty useful.

That is: unitList[0] will return the most useless unit and unitList[-1] will return the most valuable unit.
[/QUOTE]

I think rather than this, it'd be best for the function to just return the whole list, and let the caller extract what they want. For example, the Calabim will want the worst, living, non-vampire, non-animal, non-beast unit. Ogre Warchiefs will just sacrifice the nearest thing that comes to hand, whether it be summoned, undead, a tiger, etc.
 
As an aside, could you possibly help with a little structuring of the code? I'm thinking for promotion modifiers, it might be better to create a strided list, of

Promotion
Modifier
Promotion
Modifier

And run a loop that tests the unit against every second element of that list, and if present applies the modifier in the element+1, rather than the cascade of if statements I currently have, which is getting rather messy. Would make it more modular and expandable overall
 
@ Xien:

could any of these:

<fCasterXPRate>1</fCasterXPRate>
<iMinLevel>-1</iMinLevel>

be the source of my issues with that

Tag: 1 in Info class was incorrect.
Current XML file is: xml\Units/CIV4PromotionInfos.xml

error?
 
im trying to make an installer for my mod, and am having trouble. the script im using is this:

Spoiler :
Code:
[Setup]
AppId={{C7D8C738-A91A-4466-8D98-1CA9E286F48E}
AppName=Warhammer Heart of Chaos
AppVerName=WHoC 0.01
DefaultDirName={reg:HKLM\SOFTWARE\Firaxis Games\Sid Meier's Civilization 4 - Beyond the Sword,INSTALLDIR}\Mods\Warhammer Heart of Chaos 0.01\
OutputBaseFilename=WHoCSetup0.01
Compression=lzma
SolidCompression=yes
DisableProgramGroupPage=yes

[Types]
Name: "full"; Description: "Default installation"

[Components]
Name: "WHoC"; Description: "Warhammer Heart of Chaos Core Files"; Types: full; Flags: fixed

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
Name: "french"; MessagesFile: "compiler:Languages\French.isl"
Name: "german"; MessagesFile: "compiler:Languages\German.isl"
Name: "italian"; MessagesFile: "compiler:Languages\Italian.isl"

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked

[Files]
Source: "C:\Games\Civilization\Beyond the Sword\Mods\Warhammer Heart of Chaos 0.01"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: WHoC
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{userdesktop}\Warhammer Heart of Chaos 0.01"; Filename: "{reg:HKLM\SOFTWARE\Firaxis Games\Sid Meier's Civilization 4 - Beyond the Sword,INSTALLDIR}\Civ4BeyondSword.exe"; Parameters: "mod=\Warhammer Heart of Chaos 0.01"; Tasks: desktopicon; IconFilename: "{app}\ChaosIcon.ico"

[Run]
Filename: "{reg:HKLM\SOFTWARE\Firaxis Games\Sid Meier's Civilization 4 - Beyond the Sword,INSTALLDIR}\Civ4BeyondSword.exe"; Parameters: "mod=\Warhammer Heart of Chaos 0.01"; Description: "{cm:LaunchProgram,Warhammer Heart of Chaos}"; Flags: nowait postinstall skipifsilent

im not too sure what the problem is but it seems to be compressing every single mod in my mod directory, but NOT the .xml, .py or any other document for that matter that are inside the assets files.

i only want it to compile the Warhammer Heart of Chaos 0.01 file, but it complied Fall Further, Orbis, FFH2 and everything else too.

oh btw im using Inno.
 
Back
Top Bottom