LUA: Loading a save game triggers SerialEventUnitCreated for existing units!

ww2commander

Emperor
Joined
Aug 23, 2003
Messages
1,243
Location
Australia
I have noticed a strange situation where the Events.SerialEventUnitCreated.Add function seems to be triggering for all my existing units on the map when I load from a saved game! Is this normal behavior or a bug?

How can I get around this so that it ignores existing units on the map and does not treat them as new???
 
normal, it's an UI event means to be triggered when the unit graphic model is created: new unit, embarking, disembarking, upgrading, loading game, starting scenario, etc...

In my mod I track all units in a table so I can "initialize" them only where they are new.

You can also use a combination of unit turn created function, game get turn function and a temporary "new unit" promotion to check if the unit is "new" at one given turn.
 
I'm having this exact same problem with my mod.

As I'm inexperienced with lua, is there an example of how to correct this problem?

The code I am using below is generating a game start whereas I'd prefer it didn't...

Code:
-- On Unit Created by France spawn unit in Frank Cities with Weaponsmith or Stable

function HandleFranceUnit( playerID, unitID, hexVec, unitType, cultureType, civID, primaryColor, secondaryColor, unitFlagIndex, fogState, selected, military, notInvisible )

    if( Players[ playerID ] == nil or
        Players[ playerID ]:GetUnitByID( unitID ) == nil or
        Players[ playerID ]:GetUnitByID( unitID ):IsDead() )
    then
        return
    end
    
	local player = Players[ playerID ]
	local unit = player:GetUnitByID( unitID )
	local unitType = unit:GetUnitType()
	local builderID = playerID	
	local Franks
	local iFranks

	-- find Franks

	for PlayerID=GameDefines.MAX_MAJOR_CIVS, GameDefines.MAX_CIV_PLAYERS-1, 1 do  

	local pFranks = Players[PlayerID]

		if (GameInfo.MinorCivilizations.MINOR_CIV_FRANKS.ID == pFranks:GetMinorCivType()) then
	
		Franks = pFranks
		iFranks = PlayerID

		end	
	end

	--Find France and create Frank units

	if (GameInfo.Civilizations.CIVILIZATION_FRANCE.ID == player:GetCivilizationType()) then
		
		-- Enumerate Franks cities

		for pCity in Franks:Cities() do

			-- Horseman unit created

			if (unitType == (GameInfoTypes["UNIT_HORSEMAN"])) then
		
				Stables = pCity:GetNumRealBuilding(GameInfoTypes["BUILDING_STABLE"])
		
				if pCity ~= nil and Stables ~= 0 then
		
				local pPlot = pCity:GetCityIndexPlot();
				local Spawnunit;
				local iSpawnX = pPlot:GetX();
				local iSpawnY = pPlot:GetY();

				Spawnunit = Franks:InitUnit(GameInfoTypes["UNIT_HORSEMAN"], iSpawnX, iSpawnY, UNITAI_ATTACK, DIRECTION_NORTHWEST );
				print (pCity:GetName() ,"...is spawning Horseman from France... ");
						
				end	

			-- Swordsman unit created
	
			elseif (unitType == (GameInfoTypes["UNIT_FRANCISCAMAN"])) then
		
				Weaponsmiths = pCity:GetNumRealBuilding(GameInfoTypes["BUILDING_WEAPONSMITH"])
		
				if pCity ~= nil and Weaponsmiths ~= 0 then
		
				local pPlot = pCity:GetCityIndexPlot();
				local Spawnunit;
				local iSpawnX = pPlot:GetX();
				local iSpawnY = pPlot:GetY();

				Spawnunit = Franks:InitUnit(GameInfoTypes["UNIT_SWORDSMAN"], iSpawnX, iSpawnY, UNITAI_ATTACK, DIRECTION_NORTHWEST );
				print (pCity:GetName() ,"...is spawning Swordsman from France... ");
						
				end	
			end

		end
	end	
end

Events.SerialEventUnitCreated.Add( HandleFranceUnit )

Thank-you.
 
Gedemon... I noticed some code in city state UU

Code:
if unit:GetGameTurnCreated() ~= Game.GetGameTurn() and Game.GetGameTurn() > 1 then
		print ("  Not a new unit, do nothing")
		return
		end

Would simple adding that function below the point where I determine France is the unit creator correct the error? It looks like it might without needing a faux promotions...

Thank-you.
 
Okay, thank-you... note to self... "be careful using Events" :) .

Did a quick check of all my files... fortunately, almost all of my hooks are to GameEvents. Unless those are also affected? I'm guessing it is just Events, though.

Anyhow, while I can't stop the code from running after save games, I think that by using the current game turn and unit creation turn, I can stop it from generating the resulting units.
 
I have some lua code that fires an event only when units are created. It is very simple and I expect most other experienced mod makers have written similar stuff.

Spoiler :

Lua file
Code:
-- Unit_CreatedFunctions.lua
-- Author: Machiavelli
-- DateCreated: 6/1/2012 9:02:22 AM
--------------------------------------------------------------
function CallSerialEventUnitCreatedGood(playerID, unitID, hexVec, unitType, cultureType, civID, primaryColor, secondaryColor, unitFlagIndex, fogState, selected, military, notInvisible)
	if(Players[playerID] == nil or
		Players[playerID]:GetUnitByID(unitID) == nil or
		Players[playerID]:GetUnitByID(unitID):IsDead() or
		Players[playerID]:GetUnitByID(unitID):IsHasPromotion(GameInfoTypes["PROMOTION_CREATED"])) then
        return;
    end

	-- Always mark the unit with the created promotion
	Players[playerID]:GetUnitByID(unitID):SetHasPromotion(GameInfoTypes["PROMOTION_CREATED"], true);

	-- Call the good event
	LuaEvents.SerialEventUnitCreatedGood(playerID, unitID, hexVec, unitType, cultureType, civID, primaryColor, secondaryColor, unitFlagIndex, fogState, selected, military, notInvisible);
end

--------------
-- Initialization check.  Ensures this code isn't loaded twice
--------------
local retVal = {};
LuaEvents.SerialEventUnitCreatedGood_IsInitialized(retVal);

-- If retVal isn't changed, no other mod has initialized this code.
if (retVal.isInitialized == nil) then
	LuaEvents.SerialEventUnitCreatedGood_IsInitialized.Add(function (retVal) retVal.isInitialized = true; end);
	-- Initialize the code
	Events.SerialEventUnitCreated.Add(CallSerialEventUnitCreatedGood);
end



Spoiler :

XML file
Code:
<?xml version="1.0" encoding="utf-8"?>
<!-- Created by ModBuddy on 9/19/2012 6:52:52 PM -->
<GameData>
	<UnitPromotions>
		<InsertOrAbort>
			<Type>PROMOTION_CREATED</Type>
			<Description>TXT_KEY_PROMOTION_CREATED</Description>
			<Help>TXT_KEY_PROMOTION_CREATED_HELP</Help>
			<Sound>AS2D_IF_LEVELUP</Sound>
			<CannotBeChosen>true</CannotBeChosen>
			<PortraitIndex>26</PortraitIndex>
			<IconAtlas>PROMOTION_ATLAS</IconAtlas>
			<PediaType>PEDIA_SHARED</PediaType>
			<PediaEntry>TXT_KEY_PROMOTION_CREATED</PediaEntry>
		</InsertOrAbort>
	</UnitPromotions>

	<Language_en_US>
		<Delete Tag="TXT_KEY_PROMOTION_CREATED" />
		<Row Tag="TXT_KEY_PROMOTION_CREATED">
			<Text>Created</Text>
		</Row>
		<Delete Tag="TXT_KEY_PROMOTION_CREATED_HELP" />
		<Row Tag="TXT_KEY_PROMOTION_CREATED_HELP">
			<Text>This promotion is only used internally and has no effect. Ignore it.</Text>
		</Row>
	</Language_en_US>
</GameData>
 
Just to clarify, the code above adds the promotion to units when a game is started or a save game is loaded.... I think.

So any use of Events.SerialEventUnitCreated.Add should check for the promotion(or lack thereof), I take it. Is that correct? I would use "if not unit:IsHasPromotion(PromotionType) then ..." as the check, then.

I also take it that the reason that

Code:
if unit:GetGameTurnCreated() ~= Game.GetGameTurn() and Game.GetGameTurn() > 1 then
		print ("  Not a new unit, do nothing")
		return
		end

is not adequate in and of itself, is that units created on the turn the game is saved will not be eliminated from processing. In fact, is the turn created check even necessary if I use the promotion?

Anyhow, is my reasoning correct and this is how I should use the code you so kindly provided? That is, run my function hooked to "Events.SerialEventUnitCreated.Add" and run a check for the lack of the promotion? With a, perhaps redundant, check or the game turn build of the unit?

Or am I totally misconstruing the code due to my lack of ability :).

Thank-you once again for the assistance.
 
Machiavelli24, thanks...

I downloaded a couple of your mods and see how the function works... it actually creates an alternate event that can be used in place of Events.SerialEventUnitCreated... I think. Anyhow, have added in the function and will test after replacing Events.SerialEventUnitCreated where I use it in my mod.
 
...it actually creates an alternate event that can be used in place of Events.SerialEventUnitCreated... I think.
You are 100% correct. The problem with Events.SerialEventUnitCreated is that it fires too often. The code I wrote takes Events.SerialEventUnitCreated as input and spits out LuaEvents.SerialEventUnitCreatedGood as output during the subset of times where a unit was actually created.

Just add your code to LuaEvents.SerialEventUnitCreatedGood and your code will only run when a unit is actually created. (After putting the two files in my last post in your mod.)

If you have any questions or anything is confusing let me know. I try to write code that will be useful to others.
 
Thank-you... it's working beautifully now. Once I saw how things were implimented in your mode it was a simple matter to change the original event to the lua event you created. Is it okay if I add the code to my Lua snippets thread (credited) for anyone else who needs it? I will add a brief explanation of use for those who are not knowledgable to figure things out by simply looking at the code.

Again, thanks.
 
Is it okay if I add the code to my Lua snippets thread (credited) for anyone else who needs it?
Certainly. I've long meant to put my various reusable snippets on the workshop or in a centralized location instead of giving it out haphazardly.
 
Top Bottom