Lua Scripting Possibilities

Discussion in 'Civ2 - General Discussions' started by JPetroski, Jan 21, 2018.

  1. JPetroski

    JPetroski Deity

    Joined:
    Jan 24, 2011
    Messages:
    4,081
    So I plugged this in (I just put it all in onKeyPress events) and while I had to correct a couple minor typos in the above I was able to get to a point where there's no error however it also isn't firing. You mentioned that I might have an older version of the template - I hesitate to add it to this scenario given all the big changes you made. Am I going to run into compatability problems if I simply change the luacore file (which is the one I don't touch?) I can't really update the other ones as that would wipe all the work I've done within them.
     
  2. Prof. Garfield

    Prof. Garfield Deity Supporter

    Joined:
    Mar 6, 2004
    Messages:
    3,712
    Location:
    Ontario
    Turns out I still have a copy of the scenario you're working on, so I was able to test. There seems to be an issue with the game not updating the player's "chart" (visible map), and with failing to redraw. Replace the General Library with the version attached here, and add the two lines indicated to the script.

    Code:
            if choice == 2 then
                activeUnit.moveSpent = activeUnit.moveSpent + totpp.movementMultipliers.aggregate*railUpgradeMoveCost
                activeUnit.owner.money = activeUnit.owner.money - railUpgradeMoneyCost
                gen.placeRailroad(activeUnit.location)
                gen.chartRailroad(activeUnit.location,activeUnit.owner) -- new line
                civ.ui.redrawTile(activeUnit.location) -- new line
            end
    
     

    Attached Files:

  3. JPetroski

    JPetroski Deity

    Joined:
    Jan 24, 2011
    Messages:
    4,081
    Worked like a charm! Thank you. If anyone else is following along, this would be a good method to get one specific settler function out of a unit without the concern of a road network being built from scratch all over the map, if desired.
     
  4. JPetroski

    JPetroski Deity

    Joined:
    Jan 24, 2011
    Messages:
    4,081
    Can anyone recommend a cleaner way to write this? I don't like how there is terrain type, owner, improvements, etc. If there's no way around it, I'll live with it. BTW @Knighttime your supply line module is working very well :) I just want to give the player some idea where the enemy unit that can't move is located.

    Code:
        civ.ui.text("We have encircled this ".. tostring(unit.owner.adjective) .. " ".. tostring(unit.type.name) .. " at ".. tostring(unit.location) .. " ! " ) 

    upload_2021-12-4_15-57-51.png


    Edit - as often happens 2 seconds after I post this I'm guessing if I write "unit.location.y" "...x" "...z" in each their own strings, it'll work. But just curious if there's a simpler way to achieve this.
     
  5. Knighttime

    Knighttime Prince

    Joined:
    Sep 20, 2002
    Messages:
    312
    @JPetroski Your guess is right, and there isn't really a simpler way. I think you can "shortcut" unit.location.x as simply unit.x (etc.), and you can probably skip the tostring() function for fields that you know are strings or integers and will always be populated (never nil).

    civ.ui.text("We have encircled this " .. unit.owner.adjective .. " " .. unit.type.name .. " at " .. unit.x .. "," .. unit.y .. "," .. unit.z .. "!")

    Glad supply lines is working well for you! Let me know if you have any questions about it or if there's any more advanced features you'd like to try with it.
     
    JPetroski likes this.
  6. Prof. Garfield

    Prof. Garfield Deity Supporter

    Joined:
    Mar 6, 2004
    Messages:
    3,712
    Location:
    Ontario
    The text.lua module has a function
    Code:
    text.coordinates(tile) --> string
    
    which returns the 3 coordinates of a tile (without the parentheses on either side). Might be slightly easier.
     
    Knighttime and JPetroski like this.
  7. JPetroski

    JPetroski Deity

    Joined:
    Jan 24, 2011
    Messages:
    4,081
    Thanks gents!
     
  8. JPetroski

    JPetroski Deity

    Joined:
    Jan 24, 2011
    Messages:
    4,081
    I was playing around with the new city.currentProduction and got this error:

    Code:
    ...cenario\Hinge of Fate\LuaTriggerEvents\triggerEvents.lua:953: bad argument #2 to '__eq' (civ.improvement expected, got civ.unittype)
    stack traceback:
        [C]: in metamethod '__eq'
        ...cenario\Hinge of Fate\LuaTriggerEvents\triggerEvents.lua:953: in function 'triggerEvents.onTurn'
        D:\Test of Time\Scenario\Hinge of Fate\events.lua:155: in function <D:\Test of Time\Scenario\Hinge of Fate\events.lua:147>
    
    It says it is expecting a civ.improvement, but it shouldn't need that per the directions in the functions guide. Here's the code I used:

    Code:
    if turn >= 1 then 
            for city in civ.iterateCities() do 
                if city.currentProduction == object.uNeutralTerritory then
                city.currentProduction = object.uConscripts
    
                end
            end 
       
       
        end
    
    Basically, I've noticed in playtests that cities default to building neutral territory for some reason, so I'm trying to catch it and change it to conscripts, which are a generic unit everyone can use.

    Any idea what I've goofed? It doesn't seem to make sense to me (but then so little does).

    Thanks!
     
  9. Prof. Garfield

    Prof. Garfield Deity Supporter

    Joined:
    Mar 6, 2004
    Messages:
    3,712
    Location:
    Ontario
    In some (or all, I'm not sure) cases involving the TOTPP Objects, you can only use == to compare between similar object types. It looks like city.currentProduction was an improvement, so it was expecting to compare with an improvement on the other side of the ==, but caused an error since it was a unit type.

    For your particular case, you'll want something like
    Code:
    if civ.isUnitType(city.currentProduction) and city.currentProduction == object.uNeutralTerritory then
        city.currentProduction = object.uConscripts
    end
    
    You can override the effects of the comparison operators (and a lot of other stuff) for tables using metatables and metamethods, in order to create custom data types. It looks like something similar is done for the TOTPP data types, so you have to check that the data types are the same first. Perhaps @TheNamelessOne could provide something similar to the 'type' function in a future release, so there is an easy way to check the types of the TOTPP objects, rather than going through a list of civ.isAnObjectType functions.
     
    JPetroski likes this.
  10. JPetroski

    JPetroski Deity

    Joined:
    Jan 24, 2011
    Messages:
    4,081
    That's an easy enough fix. Thanks!

    This scenario has more than 127 units, as I recycle them a bit differently than has been done in the past, so I'm going to be using this quite a bit or else there'd be all sorts of crazy effects. I appreciate the help.
     
  11. JPetroski

    JPetroski Deity

    Joined:
    Jan 24, 2011
    Messages:
    4,081
    @Prof. Garfield I'm putting this here in case others can benefit, but I noticed in the general library you've made accommodations to change the different flags of units. I just want to confirm that I am using this right with this code, as I'm having naval vessels use the sub flag until such time that they no longer need to.

    Note that I have this in onActivate because the D-Day happened flag can happen after a turn, and also after production:

    Code:
    if (unit.type == object.uLightCruiser or unit.type == object.uHeavyCruiser or unit.type == object.uBattleship)  and flag.value("D-Day Happened") == true then 
            gen.removeSubmarine(unit)   
    end
    
    Is this the correct way to use this tool or am I making an error? I just want to ensure that writing gen.removeSubmarine(unit) will have my desired effect. Thanks.
     
  12. Prof. Garfield

    Prof. Garfield Deity Supporter

    Joined:
    Mar 6, 2004
    Messages:
    3,712
    Location:
    Ontario
    You should use
    Code:
    gen.removeSubmarine(unit.type)
    
     
    JPetroski likes this.
  13. JPetroski

    JPetroski Deity

    Joined:
    Jan 24, 2011
    Messages:
    4,081
    I'm stumped again. I'm trying to add a little more danger to sending out your capitol ships. I was trying to have a random chance that group of warships spawns near the battleship. I'm using a simple -5 location on the .x because I was worried it would appear off map. However, 'unit.location.x - 5' does not appear to work, and no units are created. I also tried unit.type.location but that too did not work. Any ideas on what I need to type to get this working? Thank you.

    Code:
    if unit.type == object.uTirpitz and math.random(1, 1) == 1 and unit.location.x >= 20 and unit.location.x <= 53 and unit.location.z == 0 and flag.value("First British City Captured") == false then 
    
        text.simple("An Allied battle squadron is sent to chase down and sink the Tirpitz!","Sink the Tirpitz!",object.mTirpitz)
        civlua.createUnit(object.uBattleship, object.tBritain, {{unit.location.x - 5,unit.location.y,unit.location.z}},{count=2, randomize=false, veteran=false})
    
    end
    
     
  14. Knighttime

    Knighttime Prince

    Joined:
    Sep 20, 2002
    Messages:
    312
    All tile coordinates have the property that X + Y is an even number (they're either both evens or both odds). So you can't subtract an odd number from X, keep Y the same, and have that be a valid tile. My guess is that if you subtracted 4 or 6, this would work.
     
  15. JPetroski

    JPetroski Deity

    Joined:
    Jan 24, 2011
    Messages:
    4,081
    Yes that worked like a charm - I would have NEVER thought of that, so thanks!
     
  16. Prof. Garfield

    Prof. Garfield Deity Supporter

    Joined:
    Mar 6, 2004
    Messages:
    3,712
    Location:
    Ontario
    @Knighttime , @TheNamelessOne , or anyone else who might have an idea:

    I want to make it so that there can be optional files in the Lua Scenario Template. At the moment, I'm using code along the lines of
    Code:
    local fileFound, prefix = pcall(require,"optionalModule")
    
    This has been working fine, except that now I've realised that if the file is found, but causes an error when it is read, pcall suppresses the error and acts as if the file wasn't found. What I want is for syntax errors and error messages I've deliberately inserted (e.g. when loading legacy events fails) to appear as they would regularly, but for Lua to let me implement some alternate condition if the file is not actually found. Does anyone have any idea of how to do this?

    The best I've thought of is to check 'prefix' (which will be an error message if the file isn't found) for "module '___' not found", and not throw an error if that happens, but to throw an error (or use an unprotected require) in other situations.

    Since this is something that I re-write in every file it is needed, I'd like to see if anyone has any ideas for a more elegant solution.
     
  17. Knighttime

    Knighttime Prince

    Joined:
    Sep 20, 2002
    Messages:
    312
    Prof. Garfield likes this.
  18. Prof. Garfield

    Prof. Garfield Deity Supporter

    Joined:
    Mar 6, 2004
    Messages:
    3,712
    Location:
    Ontario
    Thanks! I think my searching may have focused too much on "require" specifically. If the first answer works as advertised, it will do what I need. It's also put me on to the package library, which looks like it will let me tailor the code more specifically to my particular use case.
     
    Knighttime likes this.
  19. Prof. Garfield

    Prof. Garfield Deity Supporter

    Joined:
    Mar 6, 2004
    Messages:
    3,712
    Location:
    Ontario
    I ultimately went with this code. It was easy to substitute into the existing lines
    Code:
    local fileFound, prefix = gen.requireIfAvailable("optionalModule")
    
    and the code only required changing the function name line if I wanted to copy it directly rather than require the General Library.

    Code:
    
    -- gen.requireIfAvailable(fileName) --> boolean, modulePrefix
    --      Attempts to require the module called fileName
    --      returns true, modulePrefix if the module is found
    --      returns false, nil if no module is found
    --      makes an error if there is a problem loading the module
    --      Note: if you change the function name here, the function
    --      can be copied and pasted if you don't want to require
    --      the general library
    function gen.requireIfAvailable(fileName)
        if package.loaded[fileName] then
            return true, require(fileName)
        else
            for _,searcher in ipairs(package.searchers) do
                local loader = searcher(fileName)
                if type(loader) == 'function' then
                    return true, require(fileName)
                end
            end
            return false, nil
        end
    end
    
    I chose to explicitly call the require function rather than try to do the work of require so that I could minimise the amount of testing I had to do to be confident that this works.
     

Share This Page