Mod-Modders Guide to Fall Further

Try this:
(bolded the changes)
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[b]\*[/b]"; 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
 
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.

The limit on numbers in python are available memory, so that's unlikely to be an issue.

Won't help Corlindale, though. What number are you planning to multiply his strength with?

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.

In base civ there are a lot of concepts that work with it well, but they break quickly once you try to change it. They could easily have been replaced by pure systems that would be much easier to balance.

Even something as seemingly simple as adding a unit that builds slowly without a resource and quickly with it is nigh impossible.

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.

For addition it would be along the lines of

Code:
value = a1*w1+a2*w2+a3*w3+...+an*wn

with a1, a2, a3, ..., an being the value of the attribute
and w1, w2, w3, ..., wn being the weight for the attribute

Example:

Code:
value = strength*1 + level*1 + hero*10 + withered*-2
hero and withered are 1 if the unit has the promotion, otherwise 0

For a product it would be the same only "**" instead of "*" and "*" instead of "+". It doesn't matter that the value shoots through the stratosphere, they are only compared to each other anyway. Warning: Math joke ahead! If the numbers get too big you could always use logarithms.

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.

I was mostly thinking along the lines of flesh golems, that it'd be inappropriate to sacrifice them.

Don't make them worth too little though. Immortal units are often powerhouses that you don't want sent to the capital just to avoid losing some lesser unit.

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

No. I thought pUnit.getName() would return an empty string if no particular name was set. Maybe get the default name of the unit somehow and compare the two.

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.

Some untested code. At the end there are some example uses.

Spoiler :
Code:
def valueOfUnit( pUnit ):
    value = 0
    value += pUnit.getLevel() * 1 # level * weight
    # I still think damage should only be used as a tie breaker, but
    # it's one of those fundamental differences between players.
    strengthWeight = (1.0 - ( pUnit.GetDamage() / 200) ) 
    value += pUnit.baseCombatStr() * strengthWeight
    promotionList = [
        ( 20, 'PROMOTION_HERO'),
        ( 20, 'PROMOTION_CHANNELING3'),
        ( 10, 'PROMOTION_CHANNELING2'),
        (  5, 'PROMOTION_CHANNELING1'),
        (  2, 'PROMOTION_ADVENTURER'),
        (  1, 'PROMOTION_STRONG'),
        ( -1, 'PROMOTION_WEAK'),
        ( -1, 'PROMOTION_CRAZED'),
        ( -1, 'PROMOTION_DISEASED'),
        ( -1, 'PROMOTION_ENRAGED'),
        ( -1, 'PROMOTION_UNDISCIPLINED'),
        ( -1, 'PROMOTION_PLAGUED'),
        ( -1, 'PROMOTION_WITHERED'),
    ]
    for weight, promotionString in promotionList:
        if pUnit.isHasPromotion( gc.getInfoTypeForString(promotionString) ):
            value += weight
    iUnitClassType = pUnit.getUnitClassType()
    if isWorldUnitClass( iUnitClassType ):
        value += 20
    worthlessUnitClasses = set( gc.getInfoTypeForString( s ) for s in ['UNITCLASS_SLAVE','UNITCLASS_SCOUT','UNITCLASS_WARRIOR',] )
    if iUnitClass in worthlessUnitClasses:
        value -= 10
    if pUnit.getCommanderUnit() != -1: # Does the unit have a commander?
        value += 1
    if pUnit.getDuration() > 0:
        value += -20./pUnit.getDuration()
    return value

def listUnitsByValueOnPlot( pPlot, player ):
    # This creates a list of all the units in the tile:
    unitList = [ pPlot.getUnit(i) for i in xrange( pPlot.getNumUnits() ) ]
    # This filters all non player units from the list:
    unitList = [  pUnit for pUnit in unitList if pUnit.getOwner() == player ]
    # or in one line:
    # unitList = [ pUnit for pUnit in (pPlot.getUnit(i) for i in xrange( pPlot.getNumUnits() )) if pUnit.getOwner() == player ]

    # Sort the list according to value, in ascending order.
    unitList.sort( key=valueOfUnit )
    
    # If for some reason you want it in descending order
    # unitList.sort( key=valueOfUnit, reverse=True )
    return unitList

def examples(pPlot, player):
    # Not all of these examples behave gracefully if there are no units on the plot.
    
    # Kill the worthleast unit:
    listUnitsByValueOnPlot(pPlot, player)[0].kill(False, player) # I have no idea what the arguments do.

    # Name the most valuable unit 'Hercules'
    listUnitsByValueOnPlot(pPlot, player)[-1].setName( 'Hercules' )

    # Give the three worthleast units the Strong promotion:
    for pUnit in listUnitsByValueOnPlot(pPlot, player)[3:]:
        pUnit.setHasPromotion( gc.getInfoTypeForString('PROMOTION_STRONG') )
        
    # Decimate the units on the tile!
    for pUnit in listUnitsByValueOnPlot(pPlot, player)[::10]:
        pUnit.kill(False, player)
        
    # Filter out the nonliving units from the result:
    livingUnits = [ pUnit for pUnit in listUnitsByValueOnPlot(pPlot, player) if pUnit.isAlive() ]
    
    # One doesn't have to use listUnitsByValueOnPlot
    # Find the worthleast unit of a player:
    worthleastUnit = sorted( PyHelpers.PyPlayer(player).getUnitList(), key=valueOfUnit )[0]
    
    # Python sort is stable, so let's say Luichirup have a spell that should hit the most valuable
    # unit on the tile, but but in this golems should never be selected if there is another unit.
    def isGolem( pUnit ):
        if pUnit.isHasPromotion( gc.getInfoTypeForString('PROMOTION_GOLEM') ):
            return 1
        else:
            return 0
    golemSortedUnitList = listUnitsByValueOnPlot(pPlot, player)
    golemSortedUnitList.sort( key=isGolem, reverse=True )
    
    # A less contrived example might be to make all living units more valuable that unliving.
    # Otherwise no change in value. Maybe something for Elohim.
    livingOverAll = sorted( listUnitsByValueOnPlot(pPlot, player), key=lambda u: u.isAlive() )
    
    # Obviously, resorting the list is only useful if you need more than one or two units from the list.
 
Some untested code. At the end there are some example uses.

I just realized I forgot the two most important examples:

Code:
# Find the least valuable living unit, do stuff to it.
for pUnit in listUnitsByValueOnPlot(pPlot, player):
    if pUnit.isAlive():
        doStuffToWorthleastAliveUnit( pUnit )
        break
else:
    doStuffWherethereIsNoLivingUnitOnTile()

Code:
# Find the most valuable siege unit on the tile
# The unit is stored in pUnit if found
# If there is no such unit, store -1 in pUnit
for pUnit in reverse( listUnitsByValueOnPlot(pPlot, player) ):
    if pUnit.getUnitClassType() == gc.getInfoTypeForString( 'UNITCOMBAT_SIEGE' ):
        break
else:
    pUnit = -1


# kill the unit found earlier.
if pUnit != -1:
    pUnit.kill( False, -1 )
 
I just realized I forgot the two most important examples:

Code:
# Find the least valuable living unit, do stuff to it.
for pUnit in listUnitsByValueOnPlot(pPlot, player):
    if pUnit.isAlive():
        doStuffToWorthleastAliveUnit( pUnit )
        break
else:
    doStuffWherethereIsNoLivingUnitOnTile()

Maybe I'm misreading, but won't this do something to every unit on the plot, in descending order of worthlessness? That is, unless doStuffToWorthleastAliveUnit() has an internal counter, but that seems a little inefficient. Although if that counter was an accessible int, you could do something like this:

Code:
# Find the least valuable living unit, do stuff to it.

# I don't know if this is the exact syntax, but I know something like this is possible
iNumberHurt = doStuffToWorthleastAliveUnit.hurtCounter

for pUnit in listUnitsByValueOnPlot(pPlot, player)[0:iNumberHurt]:
    if pUnit.isAlive():
        doStuffToWorthleastAliveUnit( pUnit )
        break
    else:
        doStuffWherethereIsNoLivingUnitOnTile()
[/CODE]
 
Maybe I'm misreading, but won't this do something to every unit on the plot, in descending order of worthlessness? That is, unless doStuffToWorthleastAliveUnit() has an internal counter, but that seems a little inefficient. Although if that counter was an accessible int, you could do something like this:

No, my code is flawless.

Code:
# Find the least valuable living unit, do stuff to it.
for pUnit in listUnitsByValueOnPlot(pPlot, player):
    if pUnit.isAlive():
        doStuffToWorthleastAliveUnit( pUnit )
        break
else:
    doStuffWherethereIsNoLivingUnitOnTile()

The if-statement skips all not-alive units, obviously.

The break-statement instantly ends the for-loop. Thus doStuffToWorthleastAliveUnit only happens for the first unit found.

The else-statement (which is properly indented, it belongs to the for-loop) only happens if the for-loop ends normally, id est there were no living units on the tile. If nothing should be done if there is no valid unit it can be removed.

for-loops handles empty lists gracefully, so one doesn't have to worry about empty tiles either.
 
Hmm. I'm in the process of merging patch C's DLL, and I've come across something strange...

CvDLLWidgetData.cpp is missing, while CvDLLWidgetData.h is still there. Any reason for this?

Edit: Yeah, that file REALLY needs to be in there. Has quite a few references to the new pedia... I pulled it off the SVN, as I still have download access (May lose it now... ;)), so I'll upload the file here until it's fixed. Remember, this is only for modders who want to merge any DLL work... Regular players can ignore this.


Oops, I kept on having connection issues with the patch generation process, so in the end I had to pick up all the missing files manually. That was the file that it kept on breaking during I guess, as it was in my patch folder, but 0 bytes of size. I ninja-updated patch C once again to now include that file.
 
Is there a way to require a special Promotion for Upgrading to a Unit? I ask especial because of the Racial Promotion as I whant to prefent cross Race updates for Conqueror/Captured Units (i.e. a Elfen Unit that is after update a Troll Kin).
 
Personally I think that allowing promotions to be prerequisites for or to block upgrades would be a good idea.

I also think that FF needs to have tempImprovement function like the TempTerrain, TempFeature, and TempBonus. The ability to temporarily boost the raw yields of a tile would be quite nice too.
 
I have now more Question, this time how the Modular system works.

Code:
<?xml version="1.0"?>

<!-- Sid Meier's Civilization 4 -->

<!-- Copyright Firaxis Games 2005 -->

<!-- No Crossrace Upgrading 0.1-->

<!-- Unit Infos -->

<Civ4UnitInfos xmlns="x-schema:CIV4UnitSchema.xml">

	<UnitInfos>

		<UnitInfo>
 		<!-- Woodfather -->

			<Class>UNITCLASS_DRUID</Class>

			<Type>UNIT_JOT_WOODFATHER</Type>

	
		</UnitInfo>


		<UnitInfo>
		<!-- Vala -->

			<Class>UNIT_JOT_HIGH_SPEAKER</Class>

			<Type>UNIT_JOT_HIGH_SPEAKER</Type>

		</UnitInfo>
		<UnitInfo>
 		<!-- Skald -->

			<Class>UNIT_JOT_SPEAKER</Class>

			<Type>UNIT_JOT_SPEAKER</Type>

			<UnitClassUpgrades>

			<UnitClassUpgrade>

			<UnitClassUpgradeType>UNIT_JOT_HIGH_SPEAKER</UnitClassUpgradeType>

			<bUnitClassUpgrade>1</bUnitClassUpgrade>

			</UnitClassUpgrade>

			</UnitClassUpgrades>

		</UnitInfo>
	</UnitInfos>

</Civ4UnitInfos>
Will this vew lines enough for UnitInfos if I just whant to change the Unit Class and Upgrades with Modular? (I know I have to edit UnitClassInfos and CIV4CivilizationInfos also)
Will the Skalds still can upgrade to Archmage Class as in Base FF or can a Skald after this line only upgrade to the new UNIT_JOT_HIGH_SPEAKER Class? SO basicly the question is if the lines I write into the Modular are just added to what there is already or if i change i.e <UnitClassUpgrades> then it will overwrite the existing <UnitClassUpgrades> for this Unit.
 
Is the order in which modular mods is loaded exposed to python in any way?

For instance if I have both the Bannor Chain of Command and Piece of Law in NormalModules, is there a way to tell which of those overwrites the other? (I know neither does, but they could.)

The reason I'm asking it that modular python really should load modules in that same order.
 
Yes, those modular mods should work, so long as the modular system is doing the job properly, there are still a few bugs I have to sort out here and there I fear.


The order of loading modules is alphabetical (pretty sure) when not specified by an MLF, if there is an MLF with files listed, then the order is that listed. I do not know if the MLFs are exposed, but I am reasonably certain they are not.

There are ways to cheat. You can have python directly access the MLF XML files and read it. You could take the items shared by the modules (say both add a promotion) and find the one with the earliest enumeration (only works if they ADD something in the same file though, if they only modify then it is a no-go)
 
Any reason you didn't include a 'Unique Technology' sort order?

In case you want it, here you go:

Code:
{
	"name" : "Civilization Specific",
	"Purpose" : "Unique Technologies",
	"Hardcoded" : False,
	"HardcodeList" : [],
	"Value to Check" : 'eTech.getResearchCost() == -1',
	"Desired Result" : 'True',
},

Edit: Not QUITE a unique tech filter, as there is no unique tag for technologies, but aside from one tech in FFPlus for the Mechanos (Steam Power), all unique technologies have a cost of -1. And Steam Power isn't REALLY unique, you just need their religion to research it... And they start with the religion. So it's only researchable if they spread it to you.
 
Alright, I'm attempting to rewrite this for the new system. Any resources of the appropriate type will come up in the sort, in this case Mana.

Code:
if gc.getBonusInfo(x[1]).getBonusClassType() == gc.getInfoTypeForString('BONUSCLASS_MANA'): return true

I had this working previously, but with the new setup I cannot for the life of me get it to go through.
 
You are trying to do this as a filter in the new pedia you mean?

First off, hate your variable name. x doesn't tell anyone what it is and they have to go hunting for where it was defined. Goes against the "Civ Credo" of being readable code ;) But for filters and sorts your variable is already selected for you anyway (unless you add to the system obviously)

As a filter, you would use
"Value to Check" : 'eBonus.getBonusClassType()'
"Desired Result" : 'gc.getInfoTypeForString("BONUSCLASS_MANA")'


Note that in the Desired Result you use double quotes " inside the single quotes ' to avoid premature cancelation.
 
The order of loading modules is alphabetical (pretty sure) when not specified by an MLF, if there is an MLF with files listed, then the order is that listed. I do not know if the MLFs are exposed, but I am reasonably certain they are not.

That sound pretty easy to parse.

When you say alphabetical , are you talking about the regular kind where 'aardvark' comes before 'Aboyne' or the computer kind: ['Aboyne', 'aardvark']?

There are ways to cheat. You can have python directly access the MLF XML files and read it. You could take the items shared by the modules (say both add a promotion) and find the one with the earliest enumeration (only works if they ADD something in the same file though, if they only modify then it is a no-go)

I had hoped to use the same code to get at the order, mostly so that you could change the order without it bothering me. I also have a bit of a phobia about duplicating code.

But are you saying that the load order depends on the content of other files than the MLF xml? I'd have thought that the Modules\LoadOrderVitalModules\FirstLoad was always loaded first, regardless of whether some module in Modules\NormalModules changed the same stuff or not.
 
Back
Top Bottom