Changing Experience to Units in City

Code:
local function AwardXPFinally(pUnit, XPToAward)
	local XPAlreadyHave = pUnit:GetExperience()
	local XPTotal = XPAlreadyHave + XPToAward
	print(XPTotal)
	pUnit:SetExperience(XPTotal)
end

local function CheckBuildingsInCity(pUnit)
	local pPlot = pUnit:GetPlot()
	local XPToAward = 0;
	for _, iBuilding in pairs(buildings) do
		if (pPlot:GetNumBuilding(iBuilding) > 0) then
			XPToAward = XPToAward + 5
		end
	end
	print(XPToAward)
	AwardXPFinally(pUnit, XPToAward)
end

local function CheckForNewlyBuiltUnits(iPlayer, iCurrentTurn)
   local pPlot = pCity:Plot()
   for iUnit = 0, pPlot:GetNumUnits()-1, 1 do
      local pUnit = pPlot:GetUnit(iUnit)
      if pUnit:GetGameTurnCreated() == iCurrentTurn then
         CheckBuildingsInCity(pUnit)
      end
   end
end

local function AwardXPIfSkaldic(iPlayer, iCurrentTurn)
   -- Norway has skaldic verse trait
   if iPlayer:GetCivilizationType() == GameInfo.Civilizations.CIVILIZATION_NORWAY.ID then
      print("Norway!")
	  CheckForNewlyBuiltUnits(iPlayer,ICurrentTurn)
   else
	  print("Not Norway :(")
   end
end

function CheckforPreviousMajorCiv(iPlayer, iCurrentTurn)
   for i = (iPlayer - 1), 0, -1 do
      if (Players[i]:IsEverAlive() and not Players[i]:IsMinorCiv() and not Players[i]:IsBarbarian()) then 
	     print("In block one")
         AwardXPIfSkaldic(iPlayer, iCurrentTurn)
		 break
      end
   end
   for i = GameDefines.MAX_MAJOR_CIVS - 1, (iPlayer + 1), -1 do
	  if (Players[i]:IsEverAlive() and not Players[i]:IsMinorCiv() and not Players[i]:IsBarbarian()) then 
         print("In block two")
		 AwardXPIfSkaldic(iPlayer, iCurrentTurn - 1)
      end
   end   
end

function MainSkaldicScript(iPlayer)
   print("This is the Skaldic Script")
   local pPlayer = iPlayer
   local CurrentTurn = Game.GetGameTurn()
   if (Players[pPlayer]:IsEverAlive() and not Players[pPlayer]:IsMinorCiv() and not Players[pPlayer]:IsBarbarian()) then
      print("It's Alive!")
	  CheckforPreviousMajorCiv(pPlayer, CurrentTurn)
   end

end
GameEvents.PlayerDoTurn.Add(MainSkaldicScript)

That's what I have done. I may have done something glaringly obviously stupid, because I'm definitely an amateur.

But for sure, the event hook is being called, because at the very least "This is the Skaldic Script" and "It's Alive!" print. "In block one", "In block two" don't, although they did get printed when I used CheckForPreviousMajorCiv as the event hook.
 
CheckforPreviousMajorCiv(pPlayer, CurrentTurn)

VS

function CheckforPreviousMajorCiv(iPlayer, iCurrentTurn)

Edit: Errors like that shoudl show in lua.log as "attemping to perform maths on an object" or some such error - so ALWAYS check the log files :)
 
This line in function MainSkaldicScript(iPlayer) is quite useless
Code:
local pPlayer = iPlayer
also, while harmless, it might be confusing because pPlayer is usully used for a variable that points to a player object (that's why it's called pPlayer)
Since you are actually passing the player ID to the next function, and parameters are already local variables in functions, you can remove it and pass iPlayer directly.

Then, i think there is an oversight in
Code:
function CheckforPreviousMajorCiv(iPlayer, iCurrentTurn)
   for i = (iPlayer - 1), 0, -1 do
      if (Players[i]:IsEverAlive() and not Players[i]:IsMinorCiv() and not Players[i]:IsBarbarian()) then 
	     print("In block one")
         AwardXPIfSkaldic(iPlayer, iCurrentTurn)
		 break
      end
   end
   for i = GameDefines.MAX_MAJOR_CIVS - 1, (iPlayer + 1), -1 do
	  if (Players[i]:IsEverAlive() and not Players[i]:IsMinorCiv() and not Players[i]:IsBarbarian()) then 
         print("In block two")
		 AwardXPIfSkaldic(iPlayer, iCurrentTurn - 1)
      end
   end   
end
Since you are player 0, the first for block will start looping at i= -1, and then will substract 1 each time it loops (loop 2 will have i=-2, loop 3 i=-3 ...). It would stop when it reaches 0. Can you see what's wrong? ;)
Congratulations, you just created your first endless loop :goodjob:
Don't worry, you'll create many more during your amateur programmer life, trust me :lol:

Can you test if this works?
Code:
function CheckforPreviousMajorCiv(iPlayer, iCurrentTurn)
    if (iPlayer > 0) then
       for i = (iPlayer - 1), 0, -1 do
          if (Players[i]:IsEverAlive() and not Players[i]:IsMinorCiv() and not Players[i]:IsBarbarian()) then 
             print("In block one")
             AwardXPIfSkaldic(iPlayer, iCurrentTurn)
             break
          end
       end
    else
       for i = GameDefines.MAX_MAJOR_CIVS - 1, (iPlayer + 1), -1 do
          if (Players[i]:IsEverAlive() and not Players[i]:IsMinorCiv() and not Players[i]:IsBarbarian()) then 
             print("In block two")
             AwardXPIfSkaldic(iPlayer, iCurrentTurn - 1)
             break
          end
       end
    end
end

EDIT : was investigating while whoward answered.
Are you sure he really has an object? It seems like he's just using pPlayer to hold the player ID.
Now, i didn't check the functions to see whether he needs the ID or the object.
I still think he has an endless loop.

EDIT2, Should i get a keyboard with bigger keys?? :think:
 
OK, so my loop works properly, to the point where I can see that this function is working:

Code:
function AwardXPIfSkaldic(iPlayer, iCurrentTurn)
   -- Norway has skaldic verse trait
   print("Checking for norway")
   if iPlayer:GetCivilizationType() == GameInfo.Civilizations.CIVILIZATION_NORWAY.ID then
      print("Norway!")
	  CheckForNewlyBuiltUnits(iPlayer,ICurrentTurn)
   else
	  print("Not Norway :(")
   end
end

So I can get it that "Checking for Norway" prints, but not "Norway!" or "Not Norway :(". Will keep trying to solve this, maybe someone can shed some light on my newest idiocy?

Found it. Was checking iPlayer:GetCivilizationType() when I should have been checking Players[iPlayer]:GetCivilizationType(). On to my next issue...
 
Code:
function CheckForNewlyBuiltUnits(iPlayer, iCurrentTurn)
   print("Norway!")
   local pPlot = pCity:Plot()
   for iUnit = 0, pPlot:GetNumUnits()-1, 1 do
      local pUnit = pPlot:GetUnit(iUnit)
	  print("constructed pUnit")
      if pUnit[iUnit]:GetGameTurnCreated() == iCurrentTurn then
	     print("Check buildings")
         CheckBuildingsInCity(iUnit)
      end
   end
end

I made this using code whoward provided in Post #6. From the .lua log, "Norway!" is printing, but "constructed pUnit" and "Check Buildings" is not. I'm at a loss as to what to do. Again, any help is appreciated.
 
Did you define pCity as a global somewhere? If not it's an empty variable and you can't get a plot from it. You should get an error message in FireTuner if you pCity is empty (something along the lines "trying to use a method on nil value", don't recall the exact words).
 
What is pCity? You can't just magically use things - you have to tell the code what they are!!!
 
Did you define pCity as a global somewhere? If not it's an empty variable and you can't get a plot from it. You should get an error message in FireTuner if you pCity is empty (something along the lines "trying to use a method on nil value", don't recall the exact words).

whoward69 said:
What is pCity? You can't just magically use things - you have to tell the code what they are!!!

Oh. Um...whoops? I have to admit, I just used the code you gave me without understanding what everything was. I assumed pCity was some kind of special array or something that was included in the civ engine...:dunno: I haven't done much programming since I had to learn Turbo Pascal in Grade Ten in High School six years ago. Why did I learn Turbo Pascal in High School in 2006? Because my teacher was a Jazz musician. I kid you not.

Well, I have to be honest, I don't really understand how to define these object things that well, so I'm sticking to looping through IDs and using the IDs on arrays. Now that I know what the problem is, I'll hopefully be able to work it out soon. Thanks for your patience.
 
Hooray! The code works! Here is the whole thing if anyone is interested.

The only thing is that it doesn't quite work like it's supposed to. At this point, it works as it's supposed to for units that are bought, but for units that are built you have to keep them in the city one extra turn for it to work properly, because the turn it was made is not the turn the event fires. I'll play around with this, and maybe it'll work.

Code:
-- Lua Script1
-- DateCreated: 12/18/2012 8:24:47 AM
--------------------------------------------------------------
--prelimerary wonder checkers
local function IsWonder(pBuilding)
  return (GameInfo.BuildingClasses[pBuilding.BuildingClass].MaxGlobalInstances == 1)
end

local function AddWonders(buildings)
  for pWonder in GameInfo.Buildings() do
    if (IsWonder(pWonder)) then
      table.insert(buildings, pWonder.ID)
    end
  end
end

--build list of buildings
local buildings = {
     GameInfoTypes.BUILDING_MONUMENT,
	 GameInfoTypes.BUILDING_SHRINE,
     GameInfoTypes.BUILDING_STAVE_CHURCH, --replaces temple
     GameInfoTypes.BUILDING_AMPITHEATRE,
     GameInfoTypes.BUILDING_OPERA_HOUSE,
	 GameInfoTypes.BUILDING_MUSEUM,
     GameInfoTypes.BUILDING_BROADCAST_TOWER,
	 GameInfoTypes.BUILDING_MONASTERY,
	 GameInfoTypes.BUILDING_CATHEDRAL,
	 GameInfoTypes.BUILDING_MOSQUE,
	 GameInfoTypes.BUILDING_PAGODA
	 }

-- Now add all the World Wonders
AddWonders(buildings)

--functions for checking buildings and awarding XP.

function AwardXPFinally(pUnit, XPToAward)
	local XPAlreadyHave = pUnit:GetExperience()
	local XPTotal = XPAlreadyHave + XPToAward
	print(XPTotal)
	pUnit:SetExperience(XPTotal)
end

function CheckBuildingsInCity(pUnit, pCity)
    print("XP Finder")
	local XPToAward = 0;
	for _, iBuilding in pairs(buildings) do
		if (pCity:GetNumBuilding(iBuilding) > 0) then
		    print("XP to award is: " .. XPToAward)
			XPToAward = XPToAward + 5
		end
	end
	print(XPToAward)
	AwardXPFinally(pUnit, XPToAward)
end

function CheckForNewlyBuiltUnits(iPlayer, iCurrentTurn)
   print("Norway!")
   for cityindex = 0, Players[iPlayer]:GetNumCities() - 1, 1 do
      print("city index is: " .. cityindex)
	  local pCity = Players[iPlayer]:GetCityByID(cityindex)
      local pPlot = pCity:Plot()
      for iUnit = 0, pPlot:GetNumUnits() - 1, 1 do
	     print("iUnit is: " .. iUnit)
		 print("The CurrentTurn is: ")
		 print(iCurrentTurn)	
		 local pUnit = pPlot:GetUnit(iUnit)	
		 print("The turn this unit was created is: " .. pUnit:GetGameTurnCreated()) 	 
         if pUnit:GetGameTurnCreated() == iCurrentTurn then
	        print("Check buildings")
            CheckBuildingsInCity(pUnit, pCity)
         end
      end
   end
end

function AwardXPIfSkaldic(iPlayer, iCurrentTurn)
   -- Norway has skaldic verse trait
   if (Players[iPlayer]:GetCivilizationType() == GameInfoTypes["CIVILIZATION_NORWAY"]) then
	  CheckForNewlyBuiltUnits(iPlayer, iCurrentTurn)
   end
end

function CheckForPreviousMajorCiv(iPlayer, iCurrentTurn)
--player 0 always major. also, player 63 is always barbarian.
--do not call on player 0, call instead on player 63
--this way "turn wrap" is not required
   while iPlayer > 0 do
      if Players[iPlayer - 1]:IsEverAlive() and not Players[iPlayer - 1]:IsMinorCiv() then
		 AwardXPIfSkaldic(iPlayer - 1, iCurrentTurn)
	     return
	  end
	  iPlayer = iPlayer - 1
   end
end

function MainSkaldicScript(iPlayer)
   local iCurrentTurn = Game.GetGameTurn()
   --Script runs for barbars so that all this happens on the same turn.
   if Players[iPlayer]:IsEverAlive() and not Players[iPlayer]:IsMinorCiv() then
      CheckForPreviousMajorCiv(iPlayer, iCurrentTurn)
   end   
end
GameEvents.PlayerDoTurn.Add(MainSkaldicScript)
 
Code:
-- Lua Script1
-- DateCreated: 12/18/2012 8:24:47 AM
--------------------------------------------------------------
--prelimerary wonder checkers
local function IsWonder(pBuilding)
  return (GameInfo.BuildingClasses[pBuilding.BuildingClass].MaxGlobalInstances == 1)
end

local function AddWonders(buildings)
  for pWonder in GameInfo.Buildings() do
    if (IsWonder(pWonder)) then
      table.insert(buildings, pWonder.ID)
    end
  end
end

--build list of buildings
local buildings = {
     GameInfoTypes.BUILDING_MONUMENT,
	 GameInfoTypes.BUILDING_SHRINE,
     GameInfoTypes.BUILDING_STAVE_CHURCH, --replaces temple
     GameInfoTypes.BUILDING_AMPITHEATRE,
     GameInfoTypes.BUILDING_OPERA_HOUSE,
	 GameInfoTypes.BUILDING_MUSEUM,
     GameInfoTypes.BUILDING_BROADCAST_TOWER,
	 GameInfoTypes.BUILDING_MONASTERY,
	 GameInfoTypes.BUILDING_CATHEDRAL,
	 GameInfoTypes.BUILDING_MOSQUE,
	 GameInfoTypes.BUILDING_PAGODA
	 }

-- Now add all the World Wonders
AddWonders(buildings)

--functions for checking buildings and awarding XP.

function AwardXPFinally(pUnit, XPToAward)
	local XPAlreadyHave = pUnit:GetExperience()
	local XPTotal = XPAlreadyHave + XPToAward
	print(XPTotal)
	pUnit:SetExperience(XPTotal)
end

function CheckBuildingsInCity(pUnit, pCity)
    print("XP Finder")
	local XPToAward = 0;
	for _, iBuilding in pairs(buildings) do
		if (pCity:GetNumBuilding(iBuilding) > 0) then
		    print("XP to award is: " .. XPToAward)
			XPToAward = XPToAward + 5
		end
	end
	print(XPToAward)
	AwardXPFinally(pUnit, XPToAward)
end

function CheckForNewlyBuiltUnits(iPlayer, iCurrentTurn)
   print("Norway!")
   for cityindex = 0, Players[iPlayer]:GetNumCities() - 1, 1 do
      print("city index is: " .. cityindex)
	  local pCity = Players[iPlayer]:GetCityByID(cityindex)
      local pPlot = pCity:Plot()
      for iUnit = 0, pPlot:GetNumUnits() - 1, 1 do
	     print("iUnit is: " .. iUnit)
		 print("The CurrentTurn is: ")
		 print(iCurrentTurn)	
		 local pUnit = pPlot:GetUnit(iUnit)	
		 print("The turn this unit was created is: " .. pUnit:GetGameTurnCreated()) 	 
         if pUnit:GetGameTurnCreated() == iCurrentTurn then
	        print("Check buildings")
            CheckBuildingsInCity(pUnit, pCity)
         end
      end
   end
end

function AwardXPIfSkaldic(iPlayer, iCurrentTurn)
   -- Norway has skaldic verse trait
   print(iPlayer)
   if (Players[iPlayer]:GetCivilizationType() == GameInfoTypes["CIVILIZATION_NORWAY"]) then
	  CheckForNewlyBuiltUnits(iPlayer, iCurrentTurn)
   end
end

function MainSkaldicScript(iPlayer)
   local iCurrentTurn = Game.GetGameTurn()
   AwardXPIfSkaldic(iPlayer, iCurrentTurn)
end
GameEvents.PlayerDoTurn.Add(MainSkaldicScript)

I made a slightly different version, and this version has a different problem. If you build a unit, it will be awarded experience right when it is built, like it should be working, but if you BUY a unit, then it won't award experience, because it was "built" a turn before! The difference with this script is that MainSkaldicScript immediately calls AwardXPIfSkaldic, and skips CheckForPreviousMajorCiv.

Is there a way to check if a Unit was purchased or built? Or is there no difference. Or, better yet, does something fire when a unit is built?
 
Is there a way to check if a Unit was purchased or built? Or is there no difference. Or, better yet, does something fire when a unit is built?
Nope, we are back to your first question. Nothing triggers when a unit is built in a city.
As far as i can tell, the only event related to unit creation is Events.SerialEventUnitCreated
However, this event will trigger for any unit creation : built in city, unit upgraded, spawnrd by whatever event, even game loaded!
 
I think I've got it!

I'll use the most recent version, which works for units built but not units bought.

But then, I'll hook the previous version, which works for units bought and for units built a turn too late, but add an extra check for Unit:HasMoved() == false AND Unit:GetMoves() == 0, which will only be units that were bought, because newly built units would either have moved or have positive number of moves (or both).

This doesn't solve a couple other of my problems, though. Units that are bought on Turn 0 wouldn't get the XP (though this shouldn't cause problems except maybe for advanced starts) and I think I may have some issues with upgrading units. If you upgrade a unit, then what happens IIRC is that the unit is destroyed and a new unit is built in its place. I assume this means that the upgraded unit will yield a different Unit:GetGameTurnCreated than the first unit, right?
 
Well, that doesn't work, because it returns multiples of 60 for unit moves, presumably because the unit moves are given back once the turn ends?

Is there an event that fires at the end of each turn? Maybe I can attach something to that. Otherwise, I may have to make a dummy promotion.
 
Well, that doesn't work, because it returns multiples of 60 for unit moves,

Which is correct. A world hex is 60 "somethings" (possibly pixels) across, so to move a unit from one hex to another it has to traverse those 60 somethings. Look in GlobalDefines.xml and you will find

Code:
<Row Name="MOVE_DENOMINATOR">
  <Value>60</Value>
</Row>

so to get a units moves you just divide by MOVE_DENOMINATOR
 
Which is correct. A world hex is 60 "somethings" (possibly pixels) across, so to move a unit from one hex to another it has to traverse those 60 somethings. Look in GlobalDefines.xml and you will find

Code:
<Row Name="MOVE_DENOMINATOR">
  <Value>60</Value>
</Row>

so to get a units moves you just divide by MOVE_DENOMINATOR

Yes, but the issue isn't "why is this a multiple of 60", the issue is that I was expecting that a unit would regain it's movement points at the beginning of it's next turn, as opposed to when the current turn had passed.

More clearly: I wanted my script to recognize units just purchased by the previous player by checking the city for units such that Unit:TurnCreated == TheCurrentTurn (pseudocode), and then checking that Unit:HasNotMoved and Unit:NumMovesLeft == 0, which by my reckoning would be ONLY newly purchased units. However, this is not the case, because it appears that once the turn is done, the unit receives moves. If the units received moves at the beginning of their next turn, my idea would work.

I think I'm going to add a dummy promotion that gets awarded in the first part of the script and gets checked in the second part. Hopefully, this works.
 
Great, the trick with the dummy promotion works. It's a little unsightly, but hey, you win some, you lose some.

I only have one issue with it. Basically, these events don't fire until the second turn (i.e. Turn 1), which means that newly purchased units on the first turn (i.e. Turn 0) won't get XP from the city buildings. This isn't an issue for regular games, but for advanced starts it is a problem.

Is there a way to award a promotion to every unit that a player starts with, but nothing else? This way, i can make a distinction between units that a player started with and units that a player purchased on the very first turn (Turn 0).

Edit: there appear to be two events, http://modiki.civfanatics.com/index.php/Events.SerialEventStartGame_(Civ5_API) and http://modiki.civfanatics.com/index.php/GameEvents.PreGameStart_(Civ5_API), but I'm not sure that's what I'm looking for.
 
Back
Top Bottom