1. We have added a Gift Upgrades feature that allows you to gift an account upgrade to another member, just in time for the holiday season. You can see the gift option when going to the Account Upgrades screen, or on any user profile screen.
    Dismiss Notice

Help Creating a Unit

Discussion in 'Civ5 - Creation & Customization' started by MattKan, Jan 31, 2013.

  1. MattKan

    MattKan Chieftain

    Joined:
    Jan 31, 2013
    Messages:
    8
    Hello,

    I'm new to these forums and new to Civilization modding in general. I was wondering if anybody could show me how I could spawn a new unit using a LUA Script. I don't mean a custom unit, but just a normal unit. (e.g. on turn 25 spawn an american spearman at plot (7,18))

    I've searched the wiki for the last hour or so but haven't had much luck :p
     
  2. keirathi

    keirathi Chieftain

    Joined:
    Jan 12, 2013
    Messages:
    91
    The relevant function to spawn a unit at a certain location is:

    pPlayer:InitUnit(unitID, plotX, plotY)

    Where pPlayer is a player object.

    As for the rest of a function to spawn them, it really depends on how/when/why you are spawning it. Are you spawning a unit when you build a building? When you adopt a policy? When you research a tech? When X happens? Every 10 turns? It entirely depends on what you're trying to do.
     
  3. whoward69

    whoward69 DLL Minion

    Joined:
    May 30, 2011
    Messages:
    8,529
    Location:
    Near Portsmouth, UK
    Best place to search for functionality such as this is in the DLC scenario code, eg, Korean rebels spawn every X turns, Mongol re-inforcements spawn when military city states are conquered, most scenariors place units on turn 0, etc
     
  4. MattKan

    MattKan Chieftain

    Joined:
    Jan 31, 2013
    Messages:
    8
    My goal is to spawn a unit on a specific turn for one of the AI players.
     
  5. MattKan

    MattKan Chieftain

    Joined:
    Jan 31, 2013
    Messages:
    8
    I've figured out the turn bit and am using the ActivePlayerTurnEnd() event to get the right turn, but I'm still not quite sure about how to spawn the unit. I don't really understand the 'pPlayer:InitUnit(1, 13, 13)' bit though. Here's what I have:

    PHP:

    function SpawnUnits()
        

        
    pPlayer:InitUnit(11313)

    end

    Events
    .ActivePlayerTurnEnd.Add(SpawnUnits)
     
  6. keirathi

    keirathi Chieftain

    Joined:
    Jan 12, 2013
    Messages:
    91
    First off, ActivePlayerTurnEnd only fires for the person playing the game (AKA you), not for AIs.

    You'll probably want to use GameEvents.PlayerDoTurn (although that fires at the start of a turn, rather than the end of the turn. I'm not sure what to use if you specifically want it to fire at the end of the turn).

    Anyways, on the assumption that PlayerDoTurn will work for what you want to do, then look at the wiki. You'll notice that it takes 1 argument: a PlayerID.

    So, first line:

    function SpawnUnits(playerID)

    Now, in my post earlier, I said you needed a player OBJECT, not a player ID. So, we have to get the player object from just the ID. Luckily, there is a Players array! The index you'll be looking for is the playerID that is passed to your function. So your next line would be something like:

    local pPlayer = Players[playerID]

    So now we have the player object that we need. So now we need to figure out which unit to spawn. In your example you hardcoded a "1" in there for the unit type to spawn. In an unmodded game, the unit with unitID of 1 is a Worker, so I'm going to use that for this example. Next line would be something like this:

    local unitID = GameInfoTypes.UNIT_WORKER

    As a note, you could skip this step and just put the GameInfoTypes.UNIT_WORKER part inside your InitUnit call, but its good practice to store that kind of stuff into localized variables. When your lua scripts start getting more complex, every time you use GameInfoTypes/etc, you are doing a database lookup. They take more time to process than just reading a variable, and can really slow down the turn times if you're using a lot of them. Also, its a bad idea to just put a '1' in there like you did. What if someone else using your mod has another mod installed, and '1' is no longer a worker? Then you would be spawning a completely different unit.

    Anyways, moving on. Now we need to know WHERE to spawn the unit. I'm not really sure where you got the 13,13 from. If you are including a scenario map with fixed spawn points, and you will always want it to be spawned there, that will work. For the sake of simplicity, I will assume you are right and spawning in hex 13,13 every game will work just fine.

    So what do we have so far?

    Code:
    function SpawnUnits(playerID)
    
    	local pPlayer = Players[playerID]
    	local unitID = GameInfoTypes.UNIT_WORKER
    	
    	pPlayer:InitUnit(unitID, 13, 13)
    	
    end
    GameEvents.PlayerDoTurn.Add(SpawnUnits)
    
    Now, what's wrong with this script? It will spawn a worker every turn, for every civ in the game! We haven't done any logic checks to see if the player is the civ you want to add the unit to, or to check what turn it is.

    To check what turn it is, we'll do something like:

    local currentTurn = Game.GetGameTurn()

    Then check it in an if statement, like so:

    if (currentTurn == 50) then
    ----stuff
    end

    Now, since you only want to add the unit to a single, specific civ, we'll also have to check what civ the player is using. From the wiki, we can see that there is another Player function called GetCivilizationType, which returns something along the lines of CIVILIZATION_AMERICA. So you will need something like:

    local playerCiv = pPlayer:GetCivilizationType()

    Then check that in an if statement as well!

    if (playerCiv == CIVILIZATION_AMERICA) then
    ---stuff
    end

    Now, we're checking what turn it is, checking what civ the player is so we aren't spawning the unit for EVERY civ, and then we're actually spawning the unit. All put together, your function would look something like:

    Code:
    function SpawnUnits(playerID)
    
    	local pPlayer = Players[playerID]
    	local unitID = GameInfoTypes.UNIT_WORKER
    	local currentTurn = Game.GetGameTurn()
    	local playerCiv = pPlayer:GetCivilizationType()
    	
    	if (currentTurn == 50) then
    		if (playerCiv == CIVILIZATION_AMERICA) then
    			pPlayer:InitUnit(unitID, 13, 13)
    		end
    	end
    	
    end
    GameEvents.PlayerDoTurn.Add(SpawnUnits)
    That code, as written, should spawn a worker for America on turn 50 in plot 13,13. Hopefully this helps!
     
  7. MattKan

    MattKan Chieftain

    Joined:
    Jan 31, 2013
    Messages:
    8
    Thanks, but I still get the error message: 'attempt to index local 'pPlayer' (a nil value)' at Line 23, which is 'local playerCiv = pPlayer:GetCivilizationType()'

    I checked it with a print and 'pPlayer' is indeed nil.
     
  8. whoward69

    whoward69 DLL Minion

    Joined:
    May 30, 2011
    Messages:
    8,529
    Location:
    Near Portsmouth, UK
    Which implies that playerID is wrong, so a) how is that called and b) what do you get if you print that out?
     
  9. MattKan

    MattKan Chieftain

    Joined:
    Jan 31, 2013
    Messages:
    8
    I made a typo in the bottom part, but I fixed it and the issue still persists. I'm using exactly what he posted now.

    It's getting everybody's player IDs correctly, but for some reason when 'local playerCiv = pPlayer:GetCivilizationType()' is called, it makes the playerCiv just the player ID.
     
  10. keirathi

    keirathi Chieftain

    Joined:
    Jan 12, 2013
    Messages:
    91
    I am extremely confused. There is one error. The "if (playerCiv == CIVILIZATION_AMERICA) then" line should be "if (playerCiv == GameInfoTypes.CIVILIZATION_AMERICA) then". But after changing that, the script works for me exactly as is, without changing anything (except I changed what X and Y the unit spawned, and made it spawn on turn 5 instead of 50 for test purposes).

    Copy and paste your code here, please!

    Edit: For reference, I tested in game with this function. 100% works for me.

    Code:
    function SpawnUnits(playerID)
    
    	local pPlayer = Players[playerID]
    	local unitID = GameInfoTypes.UNIT_WORKER
    	local currentTurn = Game.GetGameTurn()
    	local playerCiv = pPlayer:GetCivilizationType()
    	local capital = pPlayer:GetCapitalCity()
    	local unitX = capital:GetX()
    	local unitY = capital:GetY()
    	
    	if (currentTurn == 5) then
    		if (playerCiv == GameInfoTypes.CIVILIZATION_AMERICA) then
    			print("Worker Spawned!")
    			pPlayer:InitUnit(unitID, unitX, unitY)
    		end
    	end
    	
    end
    GameEvents.PlayerDoTurn.Add(SpawnUnits)
    
     
  11. JA_Lamb

    JA_Lamb Warlord

    Joined:
    Aug 19, 2009
    Messages:
    231
    Location:
    The changing Dark Continent
    Thanks for the extensive example keirathi. Moderators, can this become a sticky for newbs please?
     
  12. whoward69

    whoward69 DLL Minion

    Joined:
    May 30, 2011
    Messages:
    8,529
    Location:
    Near Portsmouth, UK
    Stickies should only really be for major posts, which this is not. Perhaps copy the relevant bits as a new post in the Tutorials and Reference sub-forum.

    BTW there is a major exploit in that code IIRC. Get to turn 5, get the free worker, save the game, reload, get another free worker, repeat until you have all the workers you will ever need!
     
  13. keirathi

    keirathi Chieftain

    Joined:
    Jan 12, 2013
    Messages:
    91
    Hmm, you're right. That probably would give a unit every reload. I have trouble trying to think in terms of designing around exploits, because I'm not a person that ever really tried to think of ways TO exploit.

    In this example, I really don't know how to fix it though. Can you just use a global boolean? I'm not sure how/when globals are initialized, so that might not even work. If that doesn't, I would be completely stumped.
     
  14. JA_Lamb

    JA_Lamb Warlord

    Joined:
    Aug 19, 2009
    Messages:
    231
    Location:
    The changing Dark Continent
    Thanks for the run around eye opener :lol:.

    Ok not a sticky, but for newbies, this type of explanation and interweaving of the oneliners from modiki is invaluable as a starting point to get something to happen ingame with Lua for ourselves. It greatly increases appetite.

    As far as the exploit, how is it stopped? By a flag or an iteration or checking if that Civ already has such a unit?
     
  15. MattKan

    MattKan Chieftain

    Joined:
    Jan 31, 2013
    Messages:
    8
    It would make sense that for it to work if you call it at the end of the turn rather than the beginning, but I'm not sure if that's possible.
     
  16. MattKan

    MattKan Chieftain

    Joined:
    Jan 31, 2013
    Messages:
    8
    This example is working.

    I really appreciate not only your help but the extent you've gone to explain everything. Thank you so much!
     
  17. keirathi

    keirathi Chieftain

    Joined:
    Jan 12, 2013
    Messages:
    91
    No problem! If you have more questions, don't hesitate to ask! I'm pretty new to modding myself (whoward was answering a ton of questions for me just a week or two ago!), so it's pretty easy to pick up once you sit down with it and play around a bit.
     

Share This Page