Creating a unit tag that gains experience every turn

anansethespider

Warlord
Joined
Oct 27, 2016
Messages
288
Is there any way to do this outside of dll modding? For instance, to make a unit that gains +1 experience at the beginning of every turn.
 
You would need to apply the modifier, i think its called something like MODIFIER_GRANT_UNIT_EXPERIENCE, but then you would need to find some trigger for it that happens every turn, not sure what you could use though.
 
Hmmm. yea me either...you would need to set up some kind of repeating event, maybe there's something in the Outback Tycoon scenario I can copy, like from the Outback effects...
 
Figuring out a proper Turn Trigger would be very helpful.
I have two civ traits on the back burner, because they require a unit to be on top of improvement 'X' in order to gain a yield of some sort. Everything is coded fine, except a trigger for the turn.

Closest thing is the MODIFIER_PLAYER_UNIT_ADJUST_HEAL_PER_TURN , or REQUIREMENT_GAME_TURN_ATLEAST, or EFFECT_ADJUST_INFLUENCE_POINTS_PER_TURN 'Types' that I can't seem to reverse engineer to get what I need.
 
Figuring out a proper Turn Trigger would be very helpful.
I have two civ traits on the back burner, because they require a unit to be on top of improvement 'X' in order to gain a yield of some sort. Everything is coded fine, except a trigger for the turn.

Closest thing is the MODIFIER_PLAYER_UNIT_ADJUST_HEAL_PER_TURN , or REQUIREMENT_GAME_TURN_ATLEAST, or EFFECT_ADJUST_INFLUENCE_POINTS_PER_TURN 'Types' that I can't seem to reverse engineer to get what I need.


what about
<Row Type="MODIFIER_GAME_TRIGGER_MECHANIC" Kind="KIND_MODIFIER"/>
 
Hi Ananse! I've tested the following formula, and looks like I finally got it to work. Hopefully you'll find it useful. Cheers!

NOTE: Testing for UnitType.UnitWarrior is not exactly necessary, but I needed it for testing purposes. My tests, albeit brief, indicate that the ability works as intended, giving a quantum of experience to the unit over time. The warrior continued to gain experience and receive promotions (no combat took place in the course of the test, so the experience gain must be attributed solely to the ability), but, for some reason, the XP bar was not continuously updated. The experience value of "-1" needs to be experimented with.

2nd NOTE: The OwnerRequirement formula comes from the diplomacy code in Leaders.xml, if you want to check the source. The original code contains a "Trigger" flag, but that obviously had to be removed because there is no interaction with AI Behaviour Trees in this case.

EDIT: Fixed incorrect cut&paste arguments.

Good luck!

Code:
    <GameInfo>
    <Row Type="GAIN_EXPERIENCE_EVERY TURN" Kind="KIND_ABILITY"/>
    <Row Type="GAIN_EXPERIENCE_EVERY TURN" Tag="CLASS_CONSTANT_EXPERIENCE"/>
    <Row Tag="CLASS_CONSTANT_EXPERIENCE" Vocabulary="ABILITY_CLASS"/>
    <Row Type="UNIT_WARRIOR" Tag="CLASS_CONSTANT_EXPERIENCE"/>
    </GameInfo>

<!-----------------------------------------------------------------------------------------------------------------
BASIC FORMULA
Modifier: MODIFIER_UNITS_ADJUST_GRANT_EXPERIENCE
OwnerRequirementSetId: ON_TURN_STARTED
SubjectRequirementSetId: UNIT_IS_A_WARRIOR (Not exactly necessary, but I needed this for testing)
----------------------------------------------------------------------------------------------------------------->

    <GameInfo>
    <UnitAbilityModifiers>
    <Row>
        <UnitAbilityType>GAIN_EXPERIENCE_EVERY_TURN</UnitAbilityType>
        <ModifierId>BONUS_EXPERIENCE_PER_TURN_EVERY_TURN</ModifierId>
    </Row>
    </UnitAbilityModifiers>
 
    <Modifiers>
    <Row>
        <ModifierId>BONUS_EXPERIENCE_PER_TURN_EVERY_TURN</ModifierId>
        <ModifierType>MODIFIER_UNITS_ADJUST_GRANT_EXPERIENCE</ModifierType>
        <OwnerRequirementSetId>ON_PLAYER_STARTING_THE_TURN</OwnerRequirementSetId>
        <SubjectRequirementSetId>UNIT_IS_A_WARRIOR</SubjectRequirementSetId>
        <RunOnce>false</RunOnce>
        <Permanent>true</Permanent>
    </Row>
    </Modifiers>
 
    <ModifierArguments>
    <Row>
        <ModifierId>BONUS_EXPERIENCE_PER_TURN_EVERY_TURN</ModifierId>
        <Name>Amount</Name>
        <Value>-1</Value>
    </Row>
    </ModifierArguments>
 
    <RequirementSets>
    <Row>
        <RequirementSetId>ON_PLAYER_STARTING_THE_TURN</RequirementSetId>
        <RequirementSetType>REQUIREMENTSET_TEST_ALL</RequirementSetType>
    </Row>
    <Row>
        <RequirementSetId>UNIT_IS_A_WARRIOR</RequirementSetId>
        <RequirementSetType>REQUIREMENTSET_TEST_ALL</RequirementSetType>
    </Row>
    </RequirementSets>
 
    <RequirementSetRequirements>
    <Row>
        <RequirementSetId>ON_PLAYER_STARTING_THE_TURN</RequirementSetId>
        <RequirementId>REQUIRES_PLAYER_STARTED_TURN</RequirementId>
    </Row>
    <Row>
        <RequirementSetId>UNIT_IS_A_WARRIOR</RequirementSetId>
        <RequirementId>REQUIREMENT_UNIT_IS_A_WARRIOR</RequirementId>
    </Row>
    </RequirementSetRequirements>
 
    <Requirements>
    <Row>
        <RequirementId>REQUIRES_PLAYER_STARTED_TURN</RequirementId>
        <RequirementType>REQUIREMENT_PLAYER_TURN_STARTED</RequirementType>
    </Row>
    <Row>
        <RequirementId>REQUIREMENT_UNIT_IS_A_WARRIOR</RequirementId>
        <RequirementType>REQUIREMENT_UNIT_TYPE_MATCHES</RequirementType>
    </Row>
    </Requirements>
 
    <RequirementArguments>
    <Row>
        <RequirementId>REQUIREMENT_UNIT_IS_A_WARRIOR</RequirementId>
        <Name>UnitType</Name>
        <Value>UNIT_WARRIOR</Value>
    </Row>
    </RequirementArguments>
    </GameInfo>
 
Last edited:
ADDENDUM:

So, I've experimented a bit further. Using the "-1" value obviously advances the unit by a whole level every turn. That's excessive. Using any other value will work as advertised. "5" will give 5 points of experience every turn. "1" - 1 point. And so forth. Depending on how large of an experience gain you need, you can set the value in:

<ModifierArguments>
<Row>
<ModifierId>BONUS_EXPERIENCE_PER_TURN_EVERY_TURN</ModifierId>
<Name>Amount</Name>
<Value>-1</Value>
</Row>
</ModifierArguments>

as you like. I assume, by using additional requirements, you can make it even more complex, for a variable gain per turn. E.g. by querying whether the unit has been in combat, etc.


GB
 
I've been studying from your notes! ;) Cheers and tremendous thanks for all the work you do!
 
This is incredible! It dramatically expands the possibilities for units, thankyou so much! I'm gonna start playing with this today and let you know what I come up with. You are a genie or a demigod or something!
 
Hey so I haven't been able to get this working so far. I copy/pasted your code directly into an xml file and it doesn't register any errors but I can't get a unit to actually gain the experience. Send me a PM when you get the chance and we'll figure it out :D
 
Hey! I am new to posting on the website, and can't figure out how to send a DM (I know, it's embarassing). Perhaps I haven't acquired enough tenure to be allowed to use that function? Send me a PM instead, perhaps I can reply to it then? In any event, the code works on my end without fail, but I changed some text when I posted the code on here, so maybe I made an error somewhere. Send me a PM with the exact code you copied into your XML file, and I'll check it carefully to confirm. Please note, also, that, for some reason, the game engine does not seem to update the experience gain in the unit's experience meter right away. It takes some time, but the values do get updated in the UI some time later and always when the promotion is gained, Try experimenting with the -1 value. This results in one promotion gained every turn and illustrates that the method works. With the value of "1", the effect might take too long to observe on slower game speeds. Anyways, let's start with you sending me the code you used and take it from there. I'd love to get this working for you.

Cheers!

GB
 
OH DAMN... LOL :)))

I FOUND THE ERROR...

It's at the very beginning of the code I posted. A very minor syntax error, but that's what screwed it up for you.

INSTEAD OF:

<GameInfo>
<Row Type="GAIN_EXPERIENCE_EVERY TURN" Kind="KIND_ABILITY"/>
<Row Type="GAIN_EXPERIENCE_EVERY TURN" Tag="CLASS_CONSTANT_EXPERIENCE"/>
<Row Tag="CLASS_CONSTANT_EXPERIENCE" Vocabulary="ABILITY_CLASS"/>
<Row Type="UNIT_WARRIOR" Tag="CLASS_CONSTANT_EXPERIENCE"/>
</GameInfo>

You need to USE:

<GameInfo>
<Row Type="GAIN_EXPERIENCE_EVERY_TURN" Kind="KIND_ABILITY"/>
<Row Type="GAIN_EXPERIENCE_EVERY_TURN" Tag="CLASS_CONSTANT_EXPERIENCE"/>
<Row Tag="CLASS_CONSTANT_EXPERIENCE" Vocabulary="ABILITY_CLASS"/>
<Row Type="UNIT_WARRIOR" Tag="CLASS_CONSTANT_EXPERIENCE"/>
</GameInfo>

I missed the connecting underscore between EVERY and TURN (should be: EVERY_TURN, not EVERY TURN), which made the reference point nowhere. So, you had the modifier, requirements, etc., but no link between that and CLASS_CONSTANT_EXPERIENCE.

:) Hope it works now!
 
And, if you are going to paste the code directly (I didn't intend it that way, it was just an example, so I wasn't careful enough), you'd need to do the following. I didn't include all the XML tags the first time.

Code:
<GameInfo>
<Types>
    <Row Type="GAIN_EXPERIENCE_EVERY_TURN" Kind="KIND_ABILITY"/>
</Types>
<Tags>
    <Row Tag="CLASS_CONSTANT_EXPERIENCE" Vocabulary="ABILITY_CLASS"/>
</Tags>
<TypeTags>
    <Row Type="GAIN_EXPERIENCE_EVERY_TURN" Tag="CLASS_CONSTANT_EXPERIENCE"/>
    <Row Type="UNIT_WARRIOR" Tag="CLASS_CONSTANT_EXPERIENCE"/>
</TypeTags>
<UnitAbilityModifiers>
    <Row>
        <UnitAbilityType>GAIN_EXPERIENCE_EVERY_TURN</UnitAbilityType>
        <ModifierId>BONUS_EXPERIENCE_PER_TURN_EVERY_TURN</ModifierId>
    </Row>
</UnitAbilityModifiers>
<Modifiers>
    <Row>
        <ModifierId>BONUS_EXPERIENCE_PER_TURN_EVERY_TURN</ModifierId>
        <ModifierType>MODIFIER_UNITS_ADJUST_GRANT_EXPERIENCE</ModifierType>
        <OwnerRequirementSetId>ON_PLAYER_STARTING_THE_TURN</OwnerRequirementSetId>
        <SubjectRequirementSetId>UNIT_IS_A_WARRIOR</SubjectRequirementSetId>
        <RunOnce>false</RunOnce>
        <Permanent>true</Permanent>
    </Row>
</Modifiers>
<ModifierArguments>
    <Row>
        <ModifierId>BONUS_EXPERIENCE_PER_TURN_EVERY_TURN</ModifierId>
        <Name>Amount</Name>
        <Value>5</Value>
    </Row>
</ModifierArguments>
<RequirementSets>
    <Row>
        <RequirementSetId>ON_PLAYER_STARTING_THE_TURN</RequirementSetId>
        <RequirementSetType>REQUIREMENTSET_TEST_ALL</RequirementSetType>
    </Row>
    <Row>
        <RequirementSetId>UNIT_IS_A_WARRIOR</RequirementSetId>
        <RequirementSetType>REQUIREMENTSET_TEST_ALL</RequirementSetType>
    </Row>
</RequirementSets>
<RequirementSetRequirements>
    <Row>
        <RequirementSetId>ON_PLAYER_STARTING_THE_TURN</RequirementSetId>
        <RequirementId>REQUIRES_PLAYER_STARTED_TURN</RequirementId>
    </Row>
    <Row>
        <RequirementSetId>UNIT_IS_A_WARRIOR</RequirementSetId>
        <RequirementId>REQUIREMENT_UNIT_IS_A_WARRIOR</RequirementId>
    </Row>
</RequirementSetRequirements>
<Requirements>
    <Row>
        <RequirementId>REQUIRES_PLAYER_STARTED_TURN</RequirementId>
        <RequirementType>REQUIREMENT_PLAYER_TURN_STARTED</RequirementType>
    </Row>
    <Row>
        <RequirementId>REQUIREMENT_UNIT_IS_A_WARRIOR</RequirementId>
        <RequirementType>REQUIREMENT_UNIT_TYPE_MATCHES</RequirementType>
    </Row>
</Requirements>
<RequirementArguments>
    <Row>
        <RequirementId>REQUIREMENT_UNIT_IS_A_WARRIOR</RequirementId>
        <Name>UnitType</Name>
        <Value>UNIT_WARRIOR</Value>
    </Row>
</RequirementArguments>
</GameInfo>

NOTE: I changed the Experience Gain to "5" instead of "-1" above, and you can also get rid of the UNIT_IS_A_WARRIOR RequirementSet altogether. It's not needed.

Cheers.

GB
 
Code:
<GameData>
	<Types>
		<Row Type="GAIN_EXPERIENCE_EVERY_TURN" Kind="KIND_ABILITY"/>
	</Types>
	<TypeTags>
		<Row Type="GAIN_EXPERIENCE_EVERY_TURN" Tag="CLASS_CONSTANT_EXPERIENCE"/>
		<Row Type="UNIT_WARRIOR" Tag="CLASS_CONSTANT_EXPERIENCE"/>
	</TypeTags>
	<Tags>
		<Row Tag="CLASS_CONSTANT_EXPERIENCE" Vocabulary="ABILITY_CLASS"/>
	</Tags>
	<UnitAbilities>
		<Row UnitAbilityType="GAIN_EXPERIENCE_EVERY_TURN" Name="LOC_ABILITY_ANTI_CAVALRY_NAME" Description="LOC_ABILITY_ANTI_CAVALRY_DESCRIPTION"/>
	</UnitAbilities>
    <UnitAbilityModifiers>
etc
<GameData> or <GameInfo> don't actually matter which one is used so long as the same is used to open and close.

It works but is very wonky. And I just copied the first handy row from the <UnitAbilities> table and copy/pasted the "GAIN_EXPERIENCE_EVERY_TURN" in. Without it you get DB errors and return to main menu becuase you are referencing something undefined.
 
Yes, that's right Lee, UnitAbilities is also a line I had in my original code, but forgot to include when I pasted the code above. You are correct, it needs to be there.

As for whether it works or not... I find that using a higher number -- i.e. "5" or "-1" for experience -- makes it work more consistently. The problem is that the experience does not get updated every turn on the unit panel. That's probably a LUA issue, since there is no request to update the experience every turn. The experience amount does get updated once a new level is reached.
 
It's working much better now, thank you. But there are definitely still kinks. Some turns it seems to trigger once, others twice, others none at all. I'm pretty sure this is not just a lua issue, but something funky going on inside. I'm gonna keep testing and working at it, I'll let you know results. Thanks again!
 
So here's a few things I notice:

1. It can't be just a lua issue, because sometimes I'll have a warrior at, say, 21/23 experience and he'll stay that way for 4+ turns. If it was just lua for the experience bar, he would still be able to promote I think.

2. One thing that always works is when I train a new warrior, it causes an experience tic for all my warriors (and perhaps all warriors anywhere on the map). The new warrior always starts at 7/7 experience even though I have the number set to 2 per turn.

3. On any given turn, either all warriors I control get a tic, or none of them do.
 
Lua code does it reliably. I specified +1 XP per turn for Warriors belonging to the Human Player, and that is what I get. No wonky behaviors. So I suspect the issue with doing it using XML Modifers/Effects is that the game's DLL does not really "understand" what is desired and is not really interpretting the settings as desired.

The lua code needed
Code:
function TestPlayerUnits(iPlayer, bIsFirstTime)
	local pPlayer = Players[iPlayer]
	if pPlayer:IsHuman() then
		local pPlayerUnits = pPlayer:GetUnits()
		for i, pUnit in pPlayerUnits:Members() do
			if pUnit:GetType() ~= nil then
				if pUnit:GetType() == GameInfo.Units["UNIT_WARRIOR"].Index then
					pUnit:GetExperience():ChangeExperience(1)
				end
			end
		end
	end
end
Events.PlayerTurnActivated.Add(TestPlayerUnits)
See the attached functioning mod.

The lua code can be pretty easily adapted to all land combat units, all land and sea combat units, only specified unit-types (Warriors, Archers, and Horseman only, for example, but not Knights or Slingers or Swordsmen).

It is also trivial in lua to make the effect only occur every other turn, or every third turn, for example, but in such a case you would apply the effect to all qualifying units every other turn or every third turn, etc., instead of attempting to track that Unit #1 should be adjusted on turn # 2, 4, 6, but Unit #2 and #3 should be adjusted on turn #1, 3, 5, etc.
 

Attachments

  • LUA_Testing_WarriorXP.zip
    1.1 KB · Views: 344
Last edited:
First thing I'm using it for is making the naval carrier unit type gain 1xp every turn. How would I want to modify the above code to make it apply to a whole promotion class, or alternately, an ability class of units?

Just to be perfectly clear, I would want this code to make every naval carrier - not just human players - gain 1xp every turn.
 
Top Bottom