How to give workers a trait promotion?

I'm assuming by "trait promotion" you mean having them be eligible for the promotions you get automatically (Sentry for America, Defensive Embarkation for Songhai), etc. I wasn't aware that they didn't get these, but I rarely play those civs.

I haven't tested this, but I'm guessing it checks for one of the following:
1> If the unit has a Unit Combat Class (melee, gunpowder, etc.). Workers, Settlers, and Great People aren't assigned one. But it'd be easy enough to add a new one if this was the problem.

2> If the unit has a Combat rating. This is harder, because adding a nonzero value to workers would change how they stack with other units.

3> If the unit has either the <Found> or <WorkRate> entries with a nonzero value. In that case, there's nothing you can do about it.
I don't bring this last possibility up lightly; the game SOMEHOW knows the difference between a worker-type unit and a settler-type unit, because if you start in later eras it automatically gives you the best resourceless unit of each of these types in the appropriate numbers. In my own mod I'd added a Combat Engineer unit (worker with better movement and build rate) in the later eras, and the game knew to give me those instead of the normal Worker for late-era starts. So it's obviously parsing these units into an internal "Worker" category regardless of their names, and I'm guessing it does so by the <WorkRate> tag.

The game actually does a lot of this sort of parsing; when you add a new resource, it'll categorize it as Luxury, Strategic, or Bonus automatically. (In that order. A strategic that adds happiness is counted as a Luxury and won't be listed in the strategic pulldown unless you edit the Lua to do so. It ignores the ResourceClass you set in the XML, even though that's what that line is FOR.)

If it IS going by #3, then your only recourse would be to create an OnUnitCreated event that adds the appropriate promotions automatically depending on the owner's civ. This should work just fine.

I can try something with this tonight, to see if it's what I think it is.
 
Well, like I said, you can either add a UnitCreated event that gives them the promotion, or you can give them a different Combat Class, assuming that's what it's checking for. But adding a combat class would cause a whole bunch of other issues, starting with the fact that it'd probably change how the AI applies the whole "I don't have enough workers" logic. So you're probably better off doing the Lua hack I mentioned. When I get home from work I'll post the syntax I'm using within my own mod for various unit-based things. Yours would be simple to do, in that you only care about the unit's owner's type, and you'd just have to then look up entries in that appropriate table to see which promotions to give.

Frankly, a lot of things work better this way.
Want barbarians to heal between turns like everyone else does? Lua.
Want certain unit types to start with additional XP? Lua.
Want certain units to NOT have the Embarkation promotion, so that they can move over certain terrain types (like shallow water) without turning into a boat? Yep, Lua.
Want to create a new promotion that causes a shapeshifting custom unit that has it to steal promotions from its opponents instead of gaining them through the normal XP process? Okay, that last one's probably more specific to my own mod, but still, Lua.
 
It's not a custom table, he's talking about an existing one.

Now, technically workers HAVE an entry in the CombatClass field; it's just NULL, and generally speaking XML tables can't use NULL as a valid input. (Once upon a time, I had an improvement that I wanted to be placeable only if a tile did NOT have a Forest or Jungle, so I tried setting its required feature to NULL. It didn't work.)

So you've basically got two choices:
1> Change the XML to where America's custom trait is in the Trait_FreePromotions table (giving it to every unit they have), which'd give it to several other unit types that didn't get it previously
or
2> Add Lua. Unfortunately, the only event like this is the SerialEventUnitCreated. And like other serial events, it only triggers if/when a player first sees the item in question. But you can at least do it; the event has 13 arguments, and all you need to know is that the first argument is the player's ID and the second is the unit's ID.

So something like:

Code:
function SpatzOnUnitCreated(iPlayer,iUnit,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11,arg12,arg13)
  mPlayer = Players[iPlayer];
  mUnit = mPlayer:GetUnitByID(iUnit);
  mType = mUnit:GetUnitType();
 
  if (GameInfo.Units[mType].Combat < 1 and mPlayer:GetCivilizationType() == GameInfoTypes.CIVILIZATION_AMERICA) then
    mUnit:SetHasPromotion(GameInfoTypes.PROMOTION_SENTRY,true);
  endif
end
Events.SerialEventUnitCreated.Add( SpatzOnUnitCreated );

You could replace the part inside the IF check with a call to a table lookup for the Trait_FreePromotionUnitCombats table, but that'd have its own issues.
 
Want certain unit types to start with additional XP? Lua.

How can I distinguish between these circumstances:

  1. Unit enters the game
  2. Unit initialized
SerialEventUnitCreated fires for #2, but what I need is #1. Units are initialized each time the game is loaded or units embark/disembark.
 
How can I distinguish between these circumstances:

  1. Unit created/trained
  2. Unit initialized
SerialEventUnitCreated fires for #2, but what I need is #1.

Well, the only post-creation "initialized" triggers I know of are embarkation and disembarkation. (Debarkation?) What other ones are there? (Air units rebasing, maybe, I haven't tested that.)

I'm actually dealing with this problem myself right now, because my OnUnitCreated function gives bonus XP. There IS one trick you can do: compare Unit:GetTurnCreated() with Game:GetCurrentTurn(). (Those aren't quite the right names, but you should be able to find the right ones pretty easily in the wiki. Or I'll post them when I get home.) The two will only match on the turn the unit is created, so the only way this'd be an issue is if the unit embarked on a transport on the very turn it was created.

But actually, in your case, why do you care? You're giving a promotion to the unit. If the event triggers twice, then what does it harm? It'll already HAVE the promotion, so giving it again won't hurt anything.
Besides, you could always just check to see if the unit has the promotion first.

That's actually related to the solution I'd come up with for my own problem: create a temporary "summoning sickness" promotion (yes, I played a lot of Magic: the Gathering) that's given in this UnitCreated event, and then removed at the end of the turn in a separate EndTurn event.
That way, your UnitCreated event can be set to only give its other effects to units that don't have that promotion or were created on an earlier turn; if the user finds a way to trigger the event twice in one turn, nothing will happen the second time. Give it some token downside, like "-25% to all combat for this turn", to represent that newly-made units aren't quite ready for full combat, and you're done. I'd call it "Green troops", but that makes people think of recycling.

Note that this'd only really penalize "built" units; those that are rushed by the player for gold aren't usable until the next turn anyway (no movement points), so they'll sit in the city with this promotion on them and it'll be gone the next turn when they're finally available to move.
 
Well, the only post-creation "initialized" triggers I know of are embarkation and disembarkation. (Debarkation?) What other ones are there?
Units are initialized each time the game is loaded

But actually, in your case, why do you care? You're giving a promotion to the unit.
Want certain unit types to start with additional XP? Lua.
I give additional XP to AI units when they start. :)

GetTurnCreated doesn't work because it would double-apply effects to units that were created the turn a loaded savegame was saved.

For the temporary promotion, do you give it to all units through the XML? I'm assuming that when a game's loaded if a unit with a FreePromotion doesn't have it, won't be given it... or it'd run into the creation vs loading problem again.
 
GetTurnCreated doesn't work because it would double-apply effects to units that were created the turn a loaded savegame was saved.

Ah. I almost never load a savegame, I generally just play straight through. So yes, I can see the problem.

For the temporary promotion, do you give it to all units through the XML?

No, I mean award it in the Lua function.

So the logic would go something like this: (I'm just going to write some pseudocode, don't try copying this in.)

Code:
OnUnitCreated()
if (Unit:IsHasPromotion(GameInfoTypes.PROMOTION_GREEN) or (Unit:GetTurnCreated() < Game:GetCurrentTurn)) then
-- you already triggered a creation event for this unit, either this turn or previously
-- In this case, you probably want to do nothing
else
-- It's a newly-created unit!
  Unit:SetHasPromotion(GameInfoTypes.PROMOTION_GREEN,true)
  Unit:ChangeExperience(10)
end
(end function set to SerialEventUnitCreated)

and then, make a second function:
UnitCleanup()
for index,pPlayer in pairs(Players) do
  for Unit in Players:Units() do
    if Unit:IsHasPromotion(GameInfoTypes.PROMOTION_GREEN) then
      Unit:SetHasPromotion(GameInfoTypes.PROMOTION_GREEN,false)
    end
  end
end
(end function set to ActivePlayerEndTurn)

There. It's done. I can clean it up into something nicer when I get home, but you should get the idea. All you need to do at that point is create PROMOTION_GREEN in the XML, and make sure the player can't choose it. It doesn't even need to have any effect, although I think I'm going to go with that 25% penalty I'd mentioned for my own version.

In summary:
When a unit is created it triggers the creation event, and is given the Green promotion along with whatever other bonuses you want (like XP).
For the rest of that turn, if the UnitCreated event is called again (loading, embarking, whatever) the routine sees that it already has that promotion, and so does nothing.
At the end of the active player's turn, loop over all units, and remove the Green promotion from any that have it. (You could change this to EACH player's turn, with the new GameEvents system, but I'm not very familiar with that yet.)
On later turns, the game will know that the unit was created in a previous turn and so will do nothing.

I don't see any logical flaws there, unless your mod involves time travel or stealing promotions from your opponents.
 
1> If the unit has a Unit Combat Class (melee, gunpowder, etc.). Workers, Settlers, and Great People aren't assigned one. But it'd be easy enough to add a new one if this was the problem.

I'm trying to do it, but the code below doesn't work (nothing happens):

Any hints?
Thanks in advance :)

EDIT
----
I solved it myself.
The issue was a double <GameData> ..... </GameData> structure into the same XML file
 
Back
Top Bottom