Mod-Modders Guide to Fall Further

yeah, just use python cannottrain callback in cvgameutils.py

if getunitclasscountplusmaking(unitclass a)+getunitclasscountplusmaking(unitclass b)>X:
return false
That would be a very painful way to do what I had planned, but it's nice to know that there is a way, so thank you.

My vision is to make it so each civ has multiple options for heroes, but they can only have one at a time. Doing a python function like that for every unit would be a lot of coding. I was hoping for something more along the lines of a way to put multiple units in a single unitclass, so I could just make a "hero" class with a national limit of one and put all the heroes in it.
 
just define UNITCLASS_HERO1,2,3 and then use the code I wrote. In civilization xml file define what the each civs hero for Unitclass_hero 1,2,3 is.
 
I need your help to do 2 things:

1] I would like my new civ to automaticaly destroy captured cities. however i cannot find away to force the usual choice between destroy and capture.

2] I would in python to change the value of the XML tag dynamically for a building in a city:

Code:
            <YieldModifiers>
                <iYield>0</iYield>
                <iYield>-70</iYield>
                <iYield>0</iYield>
            </YieldModifiers>

I found the following function but doesn't seem to work as I wish : setBuildingYieldChange

As usual thanks in advance for your help. :)
 
You want to modify a building which is already in a city, or modify a building's stats midgame for a player so that all future ones he builds have the new stats? The later cannot be done with existing code, and the former is done quite extensively. Look in the Python for Gaelan (modifies Mage Guild Commerces) and DedicateMonument (modifies Monument Yields and Commerces).


You need some DLL modification to make LeaderHeadInfos (or CivilizationInfos) contain the bAutoRaze tag, then you could hitch on that to force razing. Or you might be able to use onCityAcquiredAndKept callback for it as well. Player will get a choice, but even if they decide to keep the city it will get destroyed.
 
You want to modify a building which is already in a city, or modify a building's stats midgame for a player so that all future ones he builds have the new stats? The later cannot be done with existing code, and the former is done quite extensively. Look in the Python for Gaelan (modifies Mage Guild Commerces) and DedicateMonument (modifies Monument Yields and Commerces).

Actually, I could modify the yield rate, or commerce rate, but not the yield rate modifier. I should have been more specific, but when digging into the SDK it seems only the yield rate can be modified and not the yield rate modifier. I finally created 8 versions of the building (-100, -50, -20, -10, 10, 20, 50, 100) to simulate the varying rate modifier, but it is not sexy.
 
Ah, yeah to modify the modifier (fun! Maybe we can modify that modification of the modifier too? ;)) you would need to write your own DLL code to handle a new function. Even if you don't feel like writing up a generalized XML tag you could just create the functions to allow python to manipulate it.
 
I tried to add a COMMAND similiar to COMMAND_CAST to allow automatic spellcasting. However the Command doesn't show up yet. Is it this code in CvDLLWidgetData.cpp that makes the COMMAND_CAST show up? Or do I need additional/other modifications too?
Code:
			else if (GC.getActionInfo(widgetDataStruct.m_iData1).getCommandType() == COMMAND_CAST)
			{
				GAMETEXT.parseSpellHelp(szBuffer, ((SpellTypes)(GC.getActionInfo(widgetDataStruct.m_iData1).getCommandData())));
			}
 
You need to modify CvEnum.h to get a new Command Type to exist at all. And CIV4CommandInfos.xml needs to list the same Commands in the same order as CvEnums (just like adding a new Game Option if you have experience with that). THEN CvDLLWidgetData::parseActionHelp will control what you see when you do a mouseover of the button for the Command, and CvUnit::canDoCommand will determine if it shows up, while CvUnit::doCommand will actually make something happen.
 
You need to modify CvEnum.h to get a new Command Type to exist at all. And CIV4CommandInfos.xml needs to list the same Commands in the same order as CvEnums (just like adding a new Game Option if you have experience with that). THEN CvDLLWidgetData::parseActionHelp will control what you see when you do a mouseover of the button for the Command, and CvUnit::canDoCommand will determine if it shows up, while CvUnit::doCommand will actually make something happen.

Thanks a lot Xienwolf, it was good to know that this CvDLLWidgetdata isn't that important. I had already done the other things you mentioned but I hadn't defined an Actionsubtype for the command. I still don't completly understand it, but it works now and that is all that matters :D
 
Hi there!

I'm trying to add a bUniqueCult tag to traits so that "agnostic" leaders will get a penalty with leaders following a state religion. I used the bInsane tag to help me and I did everything like it does. The only thing different is this:
Code:
/*************************************************************************************************/
/** bUniqueCult        Opera for LE/Orbis         06/07/09                                      **/
/*************************************************************************************************/
	if ((getStateReligion() != NO_RELIGION) && (GET_PLAYER(ePlayer).isUniqueCult()))
	{
	    iAttitude += GC.getLeaderHeadInfo(getPersonalityType()).getDifferentReligionAttitudeChange();
	}
/*************************************************************************************************/
/** End                                                                                         **/
/*************************************************************************************************/
I put it at the end of AI_getDifferentReligionAttitude() in CvPlayerAI.cpp, just before the return.

However, it doesn't work. Well, it works but not correctly. Whenever a leader is following a religion, (s)he gives a malus towards any other leader. The weird thing is that it does it even for leaders' following the same religion... displaying both a positive modifier and a negative one.
 
I would recommend that instead you modify one line which will achieve much the same as what you want:

Code:
int CvPlayerAI::AI_getDifferentReligionAttitude(PlayerTypes ePlayer) const
{
	int iAttitudeChange;
	int iAttitude;

	iAttitude = 0;

	if ((getStateReligion() != NO_RELIGION) && (GET_PLAYER(ePlayer).getStateReligion() != NO_RELIGION) && (getStateReligion() != GET_PLAYER(ePlayer).getStateReligion()))

Change the last line to instead read:

Code:
	if [COLOR="Lime"](isUniqueCult() &&  GET_PLAYER(ePlayer).getStateReligion() != NO_RELIGION) || [/COLOR]((getStateReligion() != NO_RELIGION) && [COLOR="Lime"](GET_PLAYER(ePlayer).isUniqueCult() || [/COLOR](GET_PLAYER(ePlayer).getStateReligion() != NO_RELIGION) && (getStateReligion() != GET_PLAYER(ePlayer).getStateReligion()))[COLOR="Lime"])[/COLOR]

This taps into the normal setup and will cause your religion opinion to be further shifted if they you posses the Holy City. The extra bit I placed on the front also makes those who have a unique cult hate those who follow any religion (but they won't hate other people who follow unique cults, as it would be hard to identify if they follow a DIFFERENT unique cult unless you add an integer tag instead of a boolean. Then if it is non-zero we hate all normal religion people, and all other non-zero values which aren't OUR non-zero value)

That would work something like this instead:

Code:
	if ((getStateReligion() != NO_RELIGION) && (GET_PLAYER(ePlayer).getStateReligion() != NO_RELIGION) && (getStateReligion() != GET_PLAYER(ePlayer).getStateReligion()))[COLOR="Lime"] || (getUniqueCult() != GET_PLAYER(ePlayer).getUniqueCult())[/COLOR]

getUniqueCult would return an integer value, as set by the XML, so if those match, we follow the same cult. Set it up so that by default your value in this field is a number you don't use in python (0 is easiest, so just avoid using 0 for anyone who DOES have a unique cult), and anyone who doesn't have any unique cult will be 0, thus only match other non-unique cult people. Otherwise if you DO have a cult, you'll like those who have the same one, hate those with a different one. Well, liking those with the same cult would require changing:

Code:
int CvPlayerAI::AI_getSameReligionAttitude(PlayerTypes ePlayer) const
{
	int iAttitudeChange;
	int iAttitude;

	iAttitude = 0;

	if ((getStateReligion() != NO_RELIGION) && (getStateReligion() == GET_PLAYER(ePlayer).getStateReligion()))

so the last line is instead:

Code:
	if ((getStateReligion() != NO_RELIGION) && (getStateReligion() == GET_PLAYER(ePlayer).getStateReligion()))[COLOR="Lime"] || (getUniqueCult() == GET_PLAYER(ePlayer).getUniqueCult())[/COLOR]
 
Hmm, I did what you said (skipping the multicult part, I'll do it when I'd have this working) but I can't seem to make it work. This time, no malus whatsoever. Garrym Gyr was RoK, I was OO: he gave me a -2 straight off the bat when I met him but he never gave Raitlor (uniqueCult leader) any malus...

So, here are my modifications:

CvGameTxtMgr.cpp
(I don't think it has anything relevant in but who knows)

Line 4550
Code:
/*************************************************************************************************/
/** bUniqueCult     Opera for LE/Orbis      06/07/09                                            **/
/*************************************************************************************************/
        if (GC.getTraitInfo(eTrait).isUniqueCult())
        {
            szHelpString.append(gDLL->getText("TXT_KEY_TRAIT_UNIQUE_CULT_HELP"));
        }
/*************************************************************************************************/
/** End                                                                                         **/
/*************************************************************************************************/

CvInfos.cpp

Line 21547
Code:
/*************************************************************************************************/
/** bUniqueCult         Opera for LE/Orbis          06/07/09                                    **/
/*************************************************************************************************/
m_bUniqueCult(false)
/*************************************************************************************************/
/** End                                                                                         **/
/*************************************************************************************************/

Line 21882
Code:
/*************************************************************************************************/
/** bUniqueCult             Opera for LE/Orbis          06/07/09                                **/
/*************************************************************************************************/
bool CvTraitInfo::isUniqueCult() const
{
	return m_bUniqueCult;
}
/*************************************************************************************************/
/** End                                                                                         **/
/*************************************************************************************************/

Line 22157
Code:
/*************************************************************************************************/
/** bUniqueCult             Opera for LE/Orbis              06/07/09                            **/
/*************************************************************************************************/
	pXML->GetChildXmlValByName(&m_bUniqueCult, "bUniqueCult");
/*************************************************************************************************/
/** End                                                                                         **/
/*************************************************************************************************/

CvInfos.h

Line 5627
Code:
/*************************************************************************************************/
/** bUniqueCult         Opera for LE/Orbis          06/07/09                                    **/
/*************************************************************************************************/
    bool isUniqueCult() const;
/*************************************************************************************************/
/** End                                                                                         **/
/*************************************************************************************************/

Line 5708
Code:
/*************************************************************************************************/
/** bUniqueCult         Opera for LE/Orbis          06/07/09                                    **/
/*************************************************************************************************/
    bool m_bUniqueCult;
/*************************************************************************************************/
/** End                                                                                         **/
/*************************************************************************************************/

CvPlayer.cpp

Line 698
Code:
/*************************************************************************************************/
/** bUniqueCult         Opera for LE/Orbis          06/07/09                                    **/
/*************************************************************************************************/
    m_bUniqueCult = false;
/*************************************************************************************************/
/** End                                                                                         **/
/*************************************************************************************************/

Line 2865
Code:
/*************************************************************************************************/
/** bUniqueCult         Opera for LE/Orbis          06/07/09                                    **/
/*************************************************************************************************/
    if (GC.getTraitInfo(eTrait).isUniqueCult())
    {
        setUniqueCult(bNewValue);
    }
/*************************************************************************************************/
/** End                                                                                         **/
/*************************************************************************************************/

Line 17984
Code:
/*************************************************************************************************/
/** bUniqueCult             Opera for LE/Orbis             06/07/09                             **/
/*************************************************************************************************/
    pStream->Read(&m_bUniqueCult);
/*************************************************************************************************/
/** End                                                                                         **/
/*************************************************************************************************/

Line 18574
Code:
/*************************************************************************************************/
/** bUniqueCult             Opera for LE/Orbis             06/07/09                             **/
/*************************************************************************************************/
    pStream->Write(&m_bUniqueCult);
/*************************************************************************************************/
/** End                                                                                         **/
/*************************************************************************************************/

Line 24400
Code:
/*************************************************************************************************/
/** bUniqueCult         Opera for LE/Orbis                                                      **/
/*************************************************************************************************/
bool CvPlayer::isUniqueCult() const
{
    return m_bUniqueCult;
}

void CvPlayer::setUniqueCult(bool bNewValue)
{
	m_bUniqueCult = bNewValue;
}
/*************************************************************************************************/
/** End                                                                                         **/
/*************************************************************************************************/

CvPlayer.h

Line 1124
Code:
/*************************************************************************************************/
/** bUniqueCult             Opera for LE/Orbis          06/07/09                                **/
/*************************************************************************************************/
	bool isUniqueCult() const;
	void setUniqueCult(bool bNewValue);
/*************************************************************************************************/
/** End                                                                                         **/
/*************************************************************************************************/

Line 1401
Code:
/*************************************************************************************************/
/** bUniqueCult         Opera for LE/Orbis          06/07/09                                    **/
/*************************************************************************************************/
    bool m_bUniqueCult;
/*************************************************************************************************/
/** End                                                                                         **/
/*************************************************************************************************/

CvPlayerAI.cpp

Line 5550
Code:
/*************************************************************************************************/
/** bUniqueCult         Opera for LE/Orbis      07/07/09                                        **/
/*************************************************************************************************/
/** -- Start original code --
	if ((getStateReligion() != NO_RELIGION) && (GET_PLAYER(ePlayer).getStateReligion() != NO_RELIGION) && (getStateReligion() != GET_PLAYER(ePlayer).getStateReligion()))
/** -- End original code --                                                                     **/
    if ((isUniqueCult() &&  GET_PLAYER(ePlayer).getStateReligion() != NO_RELIGION) || ((getStateReligion() != NO_RELIGION) && (GET_PLAYER(ePlayer).isUniqueCult() || (GET_PLAYER(ePlayer).getStateReligion() != NO_RELIGION) && (getStateReligion() != GET_PLAYER(ePlayer).getStateReligion()))))
If this isn't clear enough, I attached a 7z with the concerned source files... :confused:

Edit: Maybe I screwed up the parenthesis of the last portion of code. Currently, I read it as:

IF
-> A is bUniqueCult && B stateReligion is NOT no_religion: continue
OR IF
-> A stateReligion is NOT no_religion
AND EITHER
{
-> B is bUniqueCult
OR
-> B stateReligion is NOT no_religion
}
AND
-> A stateReligion is NOT B stateReligion

I don't if the check is okay if B stateReligion is no_religion when comparing it to A stateReligion.

I may be seeing things too :(
 

Attachments

First question I always check when something isn't working and it should: Does the Text Key show up?


Check your trait in the Civilopedia, if the text key you wrote doesn't show, then you aren't loading the XML properly. Maybe you made a typo and the XML tag isn't "bUniqueCult" like the DLL is checking for?


Hrm... you seem to be messed up on some critical parenthesis here...

Code:
    if ([COLOR=Lime](isUniqueCult() &&  GET_PLAYER(ePlayer).getStateReligion() != NO_RELIGION)[/COLOR] || [COLOR=Orange]((getStateReligion() != NO_RELIGION) && (GET_PLAYER(ePlayer).isUniqueCult() || (GET_PLAYER(ePlayer).getStateReligion() != NO_RELIGION) && (getStateReligion() != GET_PLAYER(ePlayer).getStateReligion())))[/COLOR])
change that to:

Code:
    if ([COLOR=Lime](isUniqueCult() &&  GET_PLAYER(ePlayer).getStateReligion() != NO_RELIGION)[/COLOR] || [COLOR=Orange]((getStateReligion() != NO_RELIGION) && GET_PLAYER(ePlayer).isUniqueCult())[/COLOR] || [COLOR=Magenta]((GET_PLAYER(ePlayer).getStateReligion() != NO_RELIGION) && (getStateReligion() != GET_PLAYER(ePlayer).getStateReligion()))[/COLOR])


You could also use this, but it doesn't lend quite as easily to color code layout so you can see the error

Code:
    if ([COLOR=Lime](isUniqueCult() &&  GET_PLAYER(ePlayer).getStateReligion() != NO_RELIGION)[/COLOR] || [COLOR=Red]((getStateReligion() != NO_RELIGION) && [/COLOR][COLOR=Orange](GET_PLAYER(ePlayer).isUniqueCult()) || (getStateReligion() != GET_PLAYER(ePlayer).getStateReligion())[/COLOR][COLOR=Red])[/COLOR])

Note that we are just combining the two checks for getStateReligion() != NO_RELIGION in this last version, so it is faster by a VERY insignificant amount.
 
Yes, the TXT_KEY is perfectly loaded :)

I wasn't seeing things then. Parenthesis are giving me so much troubles in those cases... I'll try it, thanks!
 
Okay, tested and it still doesn't work. Not at all this time :(

I only changed to the second fix you posted:
Code:
   if ((isUniqueCult() &&  GET_PLAYER(ePlayer).getStateReligion() != NO_RELIGION) || ((getStateReligion() != NO_RELIGION) && (GET_PLAYER(ePlayer).isUniqueCult()) || (getStateReligion() != GET_PLAYER(ePlayer).getStateReligion())))[
I'm not sure if it is correctly nested either. I'll try with the first one tomorrow.
 
Missing a parenthesis in the one you posted. You left the final OR floating and so it negates the getReligion() != NO_RELIGION check. This version should make someone without a religion hate everyone who has one, as well as those who have religions hating those who have different ones.


As long as your hands are in this function, you might want to allow some religions to tolerate certain other ones. But first, get this working so that you have a grasp on what is what (unless writing from scratch works easier for you, then go for broke ;))
 
Missing a parenthesis in the one you posted. You left the final OR floating and so it negates the getReligion() != NO_RELIGION check. This version should make someone without a religion hate everyone who has one, as well as those who have religions hating those who have different ones.
I fixed the parenthesis but it still doesn't work :crazyeye: I've got people hating each other because they don't follow the same religion but no one hates me because I'm uniqueCult. I guess there is a problem with CvPlayer::isUniqueCult?
Code:
    if ((isUniqueCult() &&  GET_PLAYER(ePlayer).getStateReligion() != NO_RELIGION) || ((getStateReligion() != NO_RELIGION) && ((GET_PLAYER(ePlayer).isUniqueCult()) || (getStateReligion() != GET_PLAYER(ePlayer).getStateReligion()))))


As long as your hands are in this function, you might want to allow some religions to tolerate certain other ones. But first, get this working so that you have a grasp on what is what (unless writing from scratch works easier for you, then go for broke ;))
Like how? Lowering the malus? Or removing it altogether? I guess this would still depends on each leader. I thought doing it via the religion weights. They could be used as modifiers for the same/different religion values so that a leader with -50 with give +50% negative points to a leader following the said religion; thus if the leader is allowed a maximum iDifferentReligionChange of -4, it will still go to -6 if it has -50 in the religion weight.

Edit: I wonder if this could conflict with my changes:
Code:
		if (hasHolyCity(getStateReligion()))
		{
			iAttitude--;
		}
And:
Code:
//FfH: Added by Kael 11/05/2007
    if (!canSeeReligion(GET_PLAYER(ePlayer).getStateReligion(), NULL) || !GET_PLAYER(ePlayer).canSeeReligion(getStateReligion(), NULL))
    {
        iAttitude = 0;
    }
//FfH: End Add
Those two snippets are searching for stateReligion things that won't be there in case of bUniqueCult, so maybe the check added by Kael is passed and the attitude is reset to 0 because of bUniqueCult?

Edit2: It seems to be that. I compiled the DLL with those two snippets commented out and it works! However, I did some changes to the Build Option: Final Release, so that may be a coincidence? I don't think those changes would change anything but... Well, anyway, I think I have to change those snippets to:
Code:
		if (!isUniqueCult())
		{
		    if (hasHolyCity(getStateReligion()))
            {
                iAttitude--;
            }
		}
And:
Code:
    if (!isUniqueCult())
    {
        if (!GET_PLAYER(ePlayer).canSeeReligion(getStateReligion(), NULL))
        {
            iAttitude = 0;
        }
    }
    
    if (!GET_PLAYER(ePlayer).isUniqueCult())
    {
        if (!canSeeReligion(GET_PLAYER(ePlayer).getStateReligion(), NULL)
        {
            iAttitude = 0;
        }
    }
Maybe I can make those two lasts in one check only?
 
Here is a potentially all inclusive list of what I have set up for 051. I don't go into too much detail about how modular loading works now, just know that it was a huge pain to set up, but now absolutely everything you want to do in a module (except Python) should be possible. And quite a few things you didn't know you wanted to do even.

First part: Everything is possible: This basically just means you can set a PrereqPromotion in PromotionInfos and not have the world end.

Second Part: New things you didn't know you wanted are possible: You can now modify a single line of anything in the XML to be a new non-default value and it will change ONLY that line. So if you wanted to have Acheron have a higher strength, you could simply have your module be:

Code:
<Civ4UnitInfos xmlns="x-schema:CIV4UnitSchema.xml">
	<UnitInfos>
		<UnitInfo>
			<Type>UNIT_ACHERON</Type>
			<iCombat>999</iCombat>
		</UnitInfo>
	</UnitInfos>
</Civ4UnitInfos>

Acheron would retain all of his other stats, the FreePromotions and everything else, but now have a strength of 999.

Now, it gets a bit annoying if you want to REMOVE something which is not a default value. In that case you have to set one of the new boolean flags in your module and include the full data for that type. But that is how people currently write modules anyway, so it isn't like life got much harder ;)


Anyway, on with the list:
  • CIV4PromotionInfos.xml & CIV4UnitInfos.xml
    • <CommanderPromotion>, <MinionPromotions>, <MasterPromotions> & <SlavePromotions> - These fields specify Promotions which the unit will grant to other units in this relationship with itself. <MinionPromotions> are granted to any Minions(Followers) attached to the unit, so are useful for Commanders. <MasterPromotions> are granted to the unit who summoned you into being, so are useful on summons...
      • Commander and Minion promotions are limited by the CommandRange of the Commander in the relationship. If the Minion also has a Command Range, that doesn't matter at all.
      • MinionPromotions must pass CvUnit::isPromotionValid checks, which primarily means that the Minion must have a valid UnitCombat for the promotion which is being attached to him, otherwise he won't gain anything. For Minions capable of changing UnitCombat, I advise that you set any promotions limited by UnitCombat to <bValidate> so they are removed from the Minion automatically when he changes to an invalid UnitCombat type
    • <iCommandLimit> - This modifies how many units the Commander is able to have attached to him. Units are not forced to stop following the Commander when his Command Limit is reduced (by gaining a negative or losing a positive), so python needs to force the units off of the Commander when doing such a thing, or you just have to allow the Commander to have some extra Followers for a while.
    • <iCommandRange> - This modifies the range at which the Commander and the Minion apply promotions to each other.
    • <iCommandXPShareRate> - This modifies the XP gained by the Commander from his Minion's when they get XP in combat
  • CIV4UnitInfos.xml
    • <iCommandLimit> - This modifies how many units the Commander is able to have attached to him.
    • <iCommandRange> - This modifies the range at which the Commander and the Minion apply promotions to each other.
    • <iCommandXPShareRate> - This modifies the XP gained by the Commander from his Minion's when they get XP in combat
  • CIV4SpellInfos.xml
    • <PrereqAvailableCommander> - Spell can only be cast if there is any Commander on the tile other than the unit trying to cast the spell which has free space in its Command Limit
    • <PrereqIsMinion> - Spell can only be cast if the unit has a Commander Unit it is attached to
  • CIV4ReligionInfos.xml & CIV4CorporationInfos.xml
    • <iTGAIndex> - This indicates which TGA icon to use for the Religion/Corporation. It allows you to link the icons to the Religions/Corporations in any order you desire, and even skip some if you want to. Magister should love that, he can finally sort his XML from Good to Evil or whatever else he wants :p Note that you are saying what order they go in, not which TGA slot to use (as each religion/Corporation actually uses 2 slots, so the number here is multiplied by 2 to choose the main slot, and 1 is added to that to get the Holy City/HQ slot). You'll notice that our TGA includes every resource I could find, even though we don't personally use them all yet, and every religion/Coporation from both Orbis and Fall Further. I decided to be nice to Ahwaric and save him the hassle of modification to the TGA if he imports this feature.
  • CIV4BuildingInfos.xml
    • <EquipmentPromotion> - This field is required for the new "no Unit's" approach to Equipment. If a building is "portable" like the Crown of Akharien and Dragon's Horde, identify what promotion carries the building with this field.
  • GlobalDefinesAlt.xml
    • GAMEFONT_TGA_RELIGIONS & GAMEFONT_TGA_CORPORATIONS - Specifies how many blocks are available for Coporations and Religions in the TGA. This is required so that the Corporations and Bonuses start to load from the proper location. Change this if you modify the dimensions of the TGA for some reason (which you would only really do if you were seeking to add more than 46 total religions/Corporations to the game, which is just sick and wrong! :p)
    • EQUIPMENT_HOLDER - Informs the DLL which Unit to use to place Promotions on when items are dropped in the field. This unit should always be flagged bNeverHostile and bCommunalProperty or things massively fail for the system. I know, hardcoding sucks, but sometimes it is needed :(
    • BASE_COMMANDER_XP_SHARE - Sets the basic percentage of XP gained in Combat by a Follower which is also granted to his Commander. Tags in UnitInfos and PromotionInfos further modify this value. Negatives are allowed, though I wouldn't advise it as I never set up safeguards to prevent negative total XP on a unit and don't know what will happen if that comes to be.
  • CIV4GameOptionInfos.xml
    • GAMEOPTION_DARK_FORESTS - Controls the presence of Dark Forests or Daikanos as an extra early game hazard (which one of the two you actually get is randomly selected)

-----------------------------------------------------------------------------------------------------------------------------------------------

New Modular Loading System:

Ok, so I have integrated WoC Lite, details are in the first post with a link to their thread. The basics which are important to modders are these:

Modular Loading can now handle EVERY DAMN FIELD, even ones which are self-referencing.

You must absolutely NEVER turn Modular Loading on in the INI. It will always work if you place a module in the Modules folder, but if you turn on modular loading, then things break because we revert half of the system to base Firaxis methods. If you wish to disable your modules, delete them, move them, or use the MLF files.

I will not go into details with anyone about the MLF files unless they are at the point where they are needed. In short: You don't need them. Individual modders can read up on WoC to see what they are and how they work, but I don't see anyone getting to "fancy" any time too soon that they need help with this, so I won't confuse you by discussing it. If you need help after reading up on WoC, then post here for more details.

There are some new tags involved with the WoC setup. As with the MLF, I don't see anyone needing them too badly, so just use the details in the first post and ask questions as you need to. The two things I will remind you of from the first post is that bForceOverwrite is something I wrote for the system myself, so don't look for information on that in WoC, and that these tags can be added to any Schema I didn't already add them to and they'll still work perfectly fine. They are INCREDIBLY UNIQUE in that regard, so don't expect this to work with any other fields in XML. And I added them to pretty much every file you might want them in anyway, so it probably won't come up

-----------------------------------------------------------------------------------------------------------------------------------------------

Equipment System:

One major change you'll notice is that Equipment no longer exists as individual units. With 2 exceptions, the Golden Hammer (because it is a Worldspell, so deserves to have a flashy item for a little while, but the AI might actually handle it better if I went ahead and got rid of them completely as well. Also if I didn't let them stay as a unit then you would need to pick up the Hammers before you could settle them in your city as you cannot control the Container) and Pieces of Barnaxus. Again because it is kind of special as a World Item, though actually it'll only show up if placed by worldbuilder if I don't miss my guess. Both could easily be ditched for the current system, but I felt kinda bad about deleting EVERYTHING from the old item system, ya know?

Anyway, now all equipment not on a unit exists in a copy of the Treasure Chest known as a "Container" in the code. Python will tend to create a new container every time it "places a promotion on the ground" where the DLL is smarter and will add dropped items to pre-existing containers on the plot, but if there is a duplicate promotion trying to drop (damn those Jade Torcs!) then a new container is made. Once any container loses all promotions on it, it is deleted. As a backup precaution, if any container runs the ::doTurn sequence and doesn't have any promotions, it deletes itself. And since it is now they first item in the XML, no more Athame's laying all over the map because you click around blindly when opening worldbuilder!

Now, the stranger things about containers: They all belong to the Orcs. No more having a civilization stay alive because they were once carrying Orthus's Axe and then dying a second time when someone finally picks it up. But everyone can take gear from a Container due to the bCommunalProperty tag, and even if you drop an item in an undefended town (delete the last defender while he is holding an item) the Barbarians won't claim the city because they are also bNeverHostile. The end result of this is that even HN units can now take equipment, AND you can pick up equipment which was dropped by an ally, because no longer do you have to kill something to gain the gear.

One minor side-effect I can recall from this setup is that now a unit carrying the Golden Hammer can give it to a Craftsman directly. This was briefly mentioned earlier as a part of the reason Golden Hammer's still exist. When a unit carrying a Hammer dies they drop a Container which belongs to the Orc Barbarians, and thus you don't have a Hammer which can join the City as a free specialist.

-----------------------------------------------------------------------------------------------------------------------------------------------

Master/Commander System:

Ok, so for a while now we have had a Master/Slave system in place for summoned units. It makes it real handy to see what you have summoned and who summoned you and switch between them. There are a few more things I want to do with this than just restrict summons to actually 1 per caster, but allow errant exceptions to work in your favor (like summoning Skeletons on a Graveyard meaning your cap is temporarily 2 higher, rather than you are now 2 closer to it). A few of those systems are in place now, along with a "Sister System"

Feedback promotions: These can originate from the Caster or from the Summoned Slave. They apply a promotion to the other member of the link. So for instance: You could modify the Balseraph's "Summon Puppet" spell to create 2 puppets which are permanent, but they are more easily resisted and have a chance to fizzle spells. This (under the old system) would mean you have 3 casters now instead of 1, and 2 of them are kinda crappy. But with the new system, you can apply a promotion to the puppets which uses the Feedback System to place a <bCannotCast> promotion on their Master. Now the main caster is not allowed to cast spells anymore, so you have moved from "I get 2 more casters for free? I don't care if they suck! Gimmie!" to "I can use my REAL caster at full potential, but risk getting killed on the battlefield, OR I can summon my puppets and have TWO casters (though they aren't quite as effective individually) AND keep my mage back home where he is safe..." See, now it is a tactical decision... (NOTE: The puppet change has not been implemented, I just think it is a good idea personally, but haven't pushed to get it placed in the actual mod. Not sure I have even mentioned it before now...)

Of course, this can be applied in the other direction as well, but having the Master apply promotions to his slaves is ALMOST just like the PromotionSummonPerk field. Except in this method you can grant as many promotions to the slaves as you want to with a single promotion on the master, AND if the master learns the promotion with the perk after having summoned a unit it is automatically applied to his currently existing slaves instead of forcing him to re-summon them to gain the bonus.



Now, the "Sister System" I mentioned is that in addition to Master/Slave, we now have a new relationship category of Commander/Follower (in the code it is called Commander and Minion, don't get confused). This is used for the Great Generals, and a few assorted UUs/Heroes. Unlike the Master/Slave system, you use existing units to be Followers, and you can separate the bond at your leisure. Commanders have a certain limit for how many units may follow them, AND there is a range involved, more on that in a bit. Also unlike the Master/Slave system, is the XP feedback. For Master/Slave you need the slave to die, then the Master gains a portion of the slave's XP for himself. But in the Commander/Follower system, any XP gained by the followers in combat (so not training XP or Free XP, or Spirit Guide, or Event, or Goody hut....) is also applied in part to the Commander. The rate is not terribly high, and it is further modified by the difference in levels between the Commander and his Followers. So Winston Churchill leading a Boy Scout means that while the Boy Scout might get a TON of XP from slaughtering some field mice, Mr. Churchill hardly feels it is worth waking up to take notes. Likewise if that Boy Scout turns around and leads Donal Lugh himself, the things which Donal is doing to random Orcs while wiping his mouth after a satisfying mid-morning brunch are so far beyond what the Scout can understand that his notes will be inadequate to impart much learning from. (in short, Feedback XP is most significant if the levels of the Commander and Follower are well matched, so changing out your Followers or ensuring that each of them gains XP at a roughly equal pace is ideal, otherwise they out-level the commander and he starts sucking in the XP gain department. Likewise using a maxxed out Commander to Powerlevel your new recruits means that Mr. Commander isn't going to get any further promotions any time soon).

Now, you probably forgot that I mentioned Command Range. Commander's ALWAYS gain XP from their followers, no matter what the range is. And follower death just means another slot is open for a new follower on the commander. But the promotions applied by the Commander to his Followers (or the Followers to their Commander) are restricted by the Command Range of the Commander, thus forcing them to be out on the front lines. For many Commanders that limit might be 0 (same tile only). Additionally, Commanders don't just apply their promotion bonuses to EVERY unit which follows them, many times the unit must meet certain requirements (primarily UnitCombat) to gain the promotion. The Promotions which Followers can grant to their Commanders are also limited to the Commander's Command range, but are not limited by any other factors (like Commander's UnitCombat).


I have it set up right now to only display your first 6 Summons or Followers. No unit can have more than 1 Master or Commander, so that portion of the interface is not an issue. But the Slaves/Followers are controlled by 2 variables in CvMainInterface near the top of the file:

g_iSlaveRowLength = 1
g_iSlaveColumnHeight = 6


The interface looks fine if you change the 1 to a 2, and then you can display up to 12 summons or followers. You can modify the 6 as well, but then things start not to look quite as pleasant. Of course, someone who is comfortable with CvMainInterface can modify things outside of those 2 variables to their heart's content to seek a better layout than I cobbled toegether :)

-----------------------------------------------------------------------------------------------------------------------------------------------

Domination:

As a minor add-on to the Commander/Follower system, Domination has been changed. Now when you Dominate a unit, it is attached to your Archmage as a Follower. To my knowledge there aren't any Commander Promotions available to anyone capable of learning Mind III, so you won't be granting them any promotions, but you do get to enjoy the XP feedback which the Commander System employs. As a counter-balance though, you are now limited to 3 Dominated units at a time, and if the Archmage dies, or you attempt to stop the Dominated unit from Following the Archmage, the unit will return to their original owner (assuming that player is still in the game, if they are not, then the unit just becomes a normal non-Commanded unit). You are not allowed to simply delete the Dominated unit either, so if you want to free up a slot, you need to either return one of the units, kill the original owner of one of the units, or suicide one of the units by means more creative than the DELETE key.


-----------------------------------------------------------------------------------------------------------------------------------------------

New Civic Screen:

Ok, one thing which is a HUGE PITA about the Civic screen is that if you add a new capability to a Civic you need to modify the DLL AND Python to display the information. This is because it no longer supports multi-line text. Not sure why it doesn't support it, I'll be seeing if I can re-enable that though because that portion of the current setup is a huge pain for modders.

Also seeking to eventually add a "Quick View" row to the top where you will just see a button for each of your current civics, and tiny buttons under them for each civic you are capable of selecting within the category at that time. Then if you know the Civics by look alone, or are content with mouse-over help popups, you don't need to scroll through the screen. Overall, should be fairly easy to set this up, but it wasn't worth delaying 051's release for me to have the time to make sure I did it all right.

-----------------------------------------------------------------------------------------------------------------------------------------------
 
Damn :eek:

I'm looking forward this Commander/Minion thing. Looks very interesting! Was it hard to code? Hard in xienwolf-hard, of course.

Edit: Okay, this bUniqueCult is tougher than expected :p I also have to change how the differentReligionCounter work now :lol:
 
Back
Top Bottom