• We are currently performing site maintenance, parts of civfanatics are currently offline, but will come back online in the coming days. For more updates please see here.

Anyone know how to spawn unit?

gyogen2

Emperor
Joined
May 8, 2014
Messages
1,201
I was attempting to make a script to clone a unit (just to see if I could) and cannot figure out how to use the Create function. I tried a code based on one to create a barbarian in the tutorial.lua but am having problems.

this is the relevant part:

Spoiler script :
local pUnit = UI.GetHeadSelectedUnit();
if (pUnit ~= nil) then
local unitType = GameInfo.Units[pUnit:GetUnitType()].Index;

local player = pUnit:GetOwner();
local plotX = pUnit:GetX() + 1
local plotY = pUnit:GetY()
print("pUnit", pUnit, "player", player, "unitType", unitType, plotX, plotY);
local pPlayer = Players[player];
local playerUnits = pPlayer:GetUnits();
print("playerunits", playerUnits);
local unit = playerUnits:Create(unitType, plotX, plotY);


It works fine until the last line above which I get the error "function expected instead of nil."

I also tried a slightly different code I found in worldbuilderplacement:
Spoiler code :
WorldBuilder.UnitManager():Create(unitType, player, plot);

but received same error.

Thanks for any help
 
I found "Create" in a couple luas, tutorial ans worldbuilderplacement - was also in a couple of the tuner luas
Example from Tutorial.lua:

l
Spoiler code :

local function CreateBarbarianWarrior()
local playerID = Game.GetLocalPlayer()

if playerID ~= PlayerTypes.NONE then
local player = Players[playerID]
local playerCities = player:GetCities()

if playerCities ~= nil then
local capitalCity = playerCities:GetCapitalCity()

if capitalCity ~= nil then
local barbarians = PlayerManager.GetAliveBarbarians()
local barbarianUnits = barbarians[1]:GetUnits()

local unitType = GameInfo.Units["UNIT_WARRIOR"].Index
local plotX = capitalCity:GetX() + 1
local plotY = capitalCity:GetY()
local barbarianUnit = barbarianUnits:Create(unitType, plotX, plotY)
local maxDamage = barbarianUnit:GetMaxDamage()
barbarianUnit:SetDamage(maxDamage - 1)
else
print("tutscenario invalid capital city")
end
else
print("tutscenario invalid player cities")
end
else
print("tutscenario invalid local player")
end
end
 
Last edited:
I've found number of Create & Destroy functions from tunerMapPanel.lua, though it doesn't work in game at UI class.
Code:
 Players[PlayerID]:GetUnits():Create(g_UnitPlacement.UnitType, plot:GetX(), plot:GetY());
 Players[pUnit:GetOwner()]:GetUnits(): Destroy(pUnit);
 ResourceBuilder.SetResourceType(plot, g_ResourcePlacement.ResourceType, g_ResourcePlacement.ResourceAmount);
 ImprovementBuilder.SetImprovementPillaged(plot, true);
 TerrainBuilder.SetFeatureType(plot, g_FeaturePlacement.FeatureType);
Create & Destory functions are hidden. I wonder how to access that references.
lua script was stopped with calling GetUnits():Create() - that means no properties like Create(), though GetUnits() works well.
 
Last edited:
one more example at ProductionPanel.lua
Code:
cityID = UI.GetHeadSelectedCity():GetID()
CityManager.RequestCommand(city, CityCommandTypes.PURCHASE, tParameters);
but it's only request, not create. (not tested yet)

I guess some special includes are hidden. required before create & destory functions
 
Last edited:
One remark. I remember that certain existig LUA mods have to be a DLC rather than a mod. Did you try to exxcute your code when its in DLC folder or in MOD folder?

Not sure if that will solve the problem about Create, Destroy, Set functions though.
 
One remark. I remember that certain existig LUA mods have to be a DLC rather than a mod. Did you try to exxcute your code when its in DLC folder or in MOD folder?

Not sure if that will solve the problem about Create, Destroy, Set functions though.
thanks, I'll check it.
but my works was not a mod, just modified existing UI lua files. I assume same result.
 
Try this: https://www.dropbox.com/s/qkmpk29xw1pl2td/LUA_Testing.zip?dl=0

I am getting this error right after the unit is spawned but it does not seem to be affecting aything and may actually be coming from the event remove command occuring right before the unit is spawned.
Code:
ActionPanel: ERROR: ActionPanel received mismatched blocking types.  Event vs engine call:  23669119 0
But I think it is actually related to the unit spawning itself in some way.

I have some extra stuff in there (including print debug statements) that aren't really necessary. At first I was trying to limit the unit creation based on Turn number, but it did not act as expected because Game.GetCurrentGameTurn() acts differently in Civ6 than it did in Civ5 because the game's turns are counted differently between the two games. In Civ5 the starting turn was Turn #0, whereas in Civ6 it is Turn #1, so I need to get used to that difference.
 
Last edited:
I did not get any errors when testing your script.

Here is code to reveal map. Should be useful for testing.
Code:
   local localPlayerVis:table = PlayersVisibility[Game.GetLocalPlayer()]
 localPlayerVis:RevealAllPlots()
 
I am getting this error right after the unit is spawned but it does not seem to be affecting aything and may actually be coming from the even remove command occuring right before the unit is spawned.
your code works well without debug console. thanks.

here's some code related to. I'm not sure builder class actually work.
Spoiler :
Code:
--set unit to embark
pUnit:Embark();

-- destory unit
Players[pUnit:GetOwner()]:GetUnits():Destroy(pUnit);

-- place grate people
Game.GetGreatPeople():CreatePerson(player:GetID(), g_GreatPersonPlacement.UnitType, plot:GetX(), plot:GetY());

-- place resource, remove resource
ResourceBuilder.SetResourceType(plot, g_ResourcePlacement.ResourceType, g_ResourcePlacement.ResourceAmount);
ResourceBuilder.SetResourceType(plot, -1);

-- place improvement, set pillaged, remove
ImprovementBuilder.SetImprovementType(plot, g_ImprovementPlacement.ImprovementType, g_PlacementSettings.Player);
ImprovementBuilder.SetImprovementPillaged(plot, true);
ImprovementBuilder.SetImprovementType(plot, -1);

-- place feature, remove
TerrainBuilder.SetFeatureType(plot, g_FeaturePlacement.FeatureType);
TerrainBuilder.SetFeatureType(plot, -1);

-- place cities, remove
local playerCities = player:GetCities();
local city = playerCities:Create(plot:GetX(), plot:GetY());
Players[pCity:GetOwner()]:GetCities():Destroy(pCity);

-- place route, set pillaged, remove
RouteBuilder.SetRouteType(plot, g_RoutePlacement.Type);
RouteBuilder.SetRoutePillaged(plot, true);
RouteBuilder.SetRouteType(plot, RouteTypes.NONE);

-- set terrain
TerrainBuilder.SetTerrainType(plot, g_TerrainPlacement.AddType);
TerrainBuilder.SetTerrainType(plot, g_TerrainPlacement.EraseType);
 
Last edited:
I've been looking through Firaxis lua as well and found but not tested any of those as yet. Could be they are only implementable via a map script. If so, that is going to kill off a lot of mod concepts.:cry:
 
did you have the modinfo file set up the same way?

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

@MeerHunter Resource placement works for me through
Code:
ResourceBuilder.SetResourceType(pChosenPlot, iChosenResource, 1)
in the following alteration to the contents of the LUA_Testing.lua file:
Spoiler :
Code:
local iTurnMax = 5
local iWarrior = GameInfo.Units["UNIT_WARRIOR"].Index
local sFlatGrassland = "TERRAIN_GRASS"
local iFlatGrassland = GameInfo.Terrains[sFlatGrassland].Index
local tTerrainValidResources = {[iFlatGrassland] = {}}
for row in GameInfo.Resource_ValidTerrains() do
 if row.TerrainType == sFlatGrassland then
  table.insert(tTerrainValidResources[iFlatGrassland], GameInfo.Resources[row.ResourceType].Index)
 end
end
local iValidResourceCounts = 0
for iItem,iResource in pairs(tTerrainValidResources[iFlatGrassland]) do
 iValidResourceCounts = iValidResourceCounts + 1
end
print("iValidResourceCounts for tTerrainValidResources[iFlatGrassland] is calculated as " .. iValidResourceCounts)

local function ScanAndGetAdjacentPlotsForTerrrain(pPlot, iTerrainType, bNoResourceIsAllowed, bNoFeatureIsAllowed)
 if pPlot == nil then
  return
 end
 local tTableOfPlots, iTableLength = {}, 0
 for direction = 0, DirectionTypes.NUM_DIRECTION_TYPES - 1, 1 do
  local adjacentPlot = Map.GetAdjacentPlot(pPlot:GetX(), pPlot:GetY(), direction);
  if (adjacentPlot) then
   if (adjacentPlot:IsWater() == false)  then
    if (adjacentPlot:GetTerrainType() == iTerrainType) then
     local bAddPlotFromFeatureConstraint = true
     if bNoFeatureIsAllowed and (adjacentPlot:GetFeatureType() ~= -1) then
      --this actually needs to be worked on to account for feature valids of resources
      bAddPlotFromFeatureConstraint = false
     end
     if bAddPlotFromFeatureConstraint then
      if bNoResourceIsAllowed then
       if (adjacentPlot:GetResourceType() == -1) then
        table.insert(tTableOfPlots, adjacentPlot)
        iTableLength = iTableLength + 1
       end
      else
       table.insert(tTableOfPlots, adjacentPlot)
       iTableLength = iTableLength + 1
      end
     end
    end
   end
  end
 end
 return tTableOfPlots, iTableLength
end

local function OnPlayerTurnActivated( iPlayer, bIsFirstTime )
 print("OnPlayerTurnActivated: bIsFirstTime is " .. tostring(bIsFirstTime))
 print("OnPlayerTurnActivated: iPlayer is " .. tostring(iPlayer))
 local pPlayer = Players[iPlayer]
 if pPlayer:IsHuman() and (pPlayer:GetCities():GetCapitalCity() ~= nil) then
  print("OnPlayerTurnActivated: Player is Human and Capital City has been founded")
  print("OnPlayerTurnActivated: Game.GetCurrentGameTurn() reports " .. tostring(Game.GetCurrentGameTurn()))
  if Game.GetCurrentGameTurn() <= iTurnMax then
   print("OnPlayerTurnActivated: Game.GetCurrentGameTurn() <= " .. iTurnMax)
   local pCapitalCity = pPlayer:GetCities():GetCapitalCity()
   local pPlayerUnits = pPlayer:GetUnits()
   local pUnit = pPlayerUnits:Create(iWarrior, pCapitalCity:GetX(), pCapitalCity:GetY())
   local tFlatGrassPlots, iTableLength = ScanAndGetAdjacentPlotsForTerrrain(Map.GetPlot(pCapitalCity:GetX(), pCapitalCity:GetY()), iFlatGrassland, true, true)
   if (tFlatGrassPlots ~= nil) and (iTableLength > 0) then
    local pChosenPlot = tFlatGrassPlots[math.random(iTableLength)]
    local iChosenResource = tTerrainValidResources[iFlatGrassland][math.random(iValidResourceCounts)]
    ResourceBuilder.SetResourceType(pChosenPlot, iChosenResource, 1)
   end
   print("OnPlayerTurnActivated: A Warrior Unit should be getting spawned at plot X" ..  pCapitalCity:GetX() .. ",Y" .. pCapitalCity:GetY())
   Events.PlayerTurnActivated.Remove(OnPlayerTurnActivated)
   return pUnit
  else
   print("OnPlayerTurnActivated: Game.GetCurrentGameTurn() > " .. iTurnMax)
   Events.PlayerTurnActivated.Remove(OnPlayerTurnActivated)
  end
 else
  print("OnPlayerTurnActivated: Player is not Human or Capital City has not been founded")
 end
end

Events.PlayerTurnActivated.Add(OnPlayerTurnActivated)
There's some more going on there than strictly necessary to test the resource placement, but I wrote it with a view toward a couple 'handler' utilities I made for Civ5 being updated toward Civ6 and testing concepts.

The code is only going to give a single randomly selected resource on a single randomly-selected flat grasslands plot adjacent to the human player capital. But will do so only once, and then only if the player has founded their city within the first 5 turns. I suppose you could 'exploit' by saving and then reloading, but I was not interested in concerning myself with defeating exploits. I was interested in testing what we can and cannot do.

There must not be any current resource or feature such as woods or rainforest on the plots either. If a suitable plot can't be found then no resource will be placed.
 
ActionPanel: ERROR: ActionPanel received mismatched blocking types. Event vs engine call: 23669119 0
I'm getting exactly this error when I add a unit in the same place that the game is also adding a recon unit, both triggered off taking a goody hut. The recon unit doesn't appear, but mine does. Did you ever get to the bottom of why you were getting the error?
 
I'm getting exactly this error when I add a unit in the same place that the game is also adding a recon unit, both triggered off taking a goody hut. The recon unit doesn't appear, but mine does. Did you ever get to the bottom of why you were getting the error?
I haven't seen that error in quite a while and I don't get it as part of creating a unit from the RASL starting units code either. But I have been making scans of the target plot to ensure there is not already a unit there for quite a while. Relevant snippet from the code I am running:
Code:
	local bPlotCanBeUsed = true
	local unitList:table = Units.GetUnitsInPlotLayerID( pPlot:GetX(), pPlot:GetY(), MapLayers.ANY );
	if unitList ~= nil then
		print("SelectEmptyPlotFromTableAndMakeUnit: table unitList for the plot is not nil")
		for i, pUnit in ipairs(unitList) do
			tUnitDetails = GameInfo.Units[pUnit:GetType()]
			if tUnitDetails.FormationClass == sForbiddenUnitFormation then
				bPlotCanBeUsed = false
			end
		end
		if not bPlotCanBeUsed then
			print("SelectEmptyPlotFromTableAndMakeUnit: offending unit was found: the plot should not be used")
		else
			print("SelectEmptyPlotFromTableAndMakeUnit: no offending unit was found: the plot will be used")
		end
	else
		print("SelectEmptyPlotFromTableAndMakeUnit: table unitList for the plot is nil")
	end

	if bPlotCanBeUsed then
		pNewUnit = pPlayerUnits:Create(iUnitType, pPlot:GetX(), pPlot:GetY())
	end
variable sForbiddenUnitFormation in this case will be the FormationClass of the unit I wish to try to place on the given plot. If this does not match to the FormationClass of any unit currently on the tile, then the tile is valid for placing the desired unit.

I am also doing additional checks which are not shown in this snip of code that ensure I am not attempting to place a land unit on a sea tile or vice versa, and that I am not trying to place a unit on a mountain or on a natural wonder. Now that I think of it I need to check that I was actually checking for a Natural Wonder. (oops).

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

As a BTW, many of the problems in this thread people were reporting were from the fact that certain lua commands are not valid when run from a UI script, while other commands are not valid when run from a GameplayScripts lua. The code I use to create a unit must be run from the GameplayScripts.
 
Last edited:
Back
Top Bottom