Absolute Beginner's Intro to Lua Part 1: GetWeather and SetWeather (Full Version)

All Forums >> [New Releases from Matrix Games] >> Command: Modern Operations series >> Mods and Scenarios >> Lua Legion



Message


Rory Noonan -> Absolute Beginner's Intro to Lua Part 1: GetWeather and SetWeather (2/12/2018 8:55:07 PM)

Below is a quick intro to Lua in Command for the Lua beginner with absolutely zero programming experience.

Some great (free) references for what I'm about to describe are:
Lua Programming Wikibook: https://en.wikibooks.org/wiki/Lua_Programming
Lua reference manual: https://www.lua.org/manual/5.3/
Command Lua Documentation: https://commandlua.github.io/index.html

The absolute first thing you need to know about Lua is input types. For the purposes of this quick guide, you only need to know two:

A string is a alphanumeric grouping of characters, and is enclosed in either single or double quotation marks (I am lazy and tend to use single quotations, there are some situations where this creates extra work so then I use double quotation marks - they are fully interchangable, just make sure to start and end a string with the same type)
example: 'Hello, world'

A number is just that, a number. You can perform math functions on numbers, and they are simply written as numbers (no need for quotation marks).
example: 5
example: 2 + 3

Now with that out of the way let's look at the entry for the GetWeather and SetWeather functions in the Command Lua Documentation.

https://commandlua.github.io/index.html#ScenEdit_GetWeather
quote:


ScenEdit_GetWeather ()
Get the current weather conditions.
Returns:

table Table of weather parameters [temp (average), rainfall, undercloud, seastate]

Usage:

ScenEdit_GetWeather()


So what does this mean? First of all, ScenEdit_GetWeather() is a function, which is like a precompiled set of instructions (in this case a precompiled set of instructions to return the weather data from a Command scenario).

To use an analogy of making a coffee, we could describe it as grind coffee, boil water, place coffee in press, add water to press, wait 4 minutes, press plunger, pour coffee. That works, but if you're constantly asking your assistant to make a coffee, it's much easier to teach them once and after that just say 'make a coffee'. A function in Lua is the equivalent of writing down those instructions and then referring your assistant to those instructions rather than repeating them every time.

In the standard Command Lua Library, functions are preceded by the prefix ScenEdit_. This is a naming convention, and each programmer (or group of programmers in a project) will have their own preferences for this. The brackets at the end of the function name are telling the console to 'execute this function now'.

So let's test this out, open up Command, bring up the Lua console (Editor > Lua Script Console). We can either type in ScenEdit_GetWeather() or click the drop down box, select the appropriate entry and click 'Insert'. Now click 'Run' and... Nothing happens. The function is called and executed without an error, but nothing really seems to happen.

This is because the function returns the current weather conditions. That means that when the function is called, it gathers the weather data and presents it. But without anyone to present it to, the data is never used for anything. Not very useful.

So instead, lets try assigning a variable to the function--which is kind of like giving the piece of information it returns a name--and then using the print command to see what the data is.

In the console, input the following:
quote:


ourVariable = ScenEdit_GetWeather()
print (ourVariable)


This should return the following:
quote:


{ seastate = 0, rainfall = 0, undercloud = 0, temp = 15 }


Now we're getting somewhere. How did that work? Essentially once we gave the data returned by ScenEdit_GetWeather a name (variable), we could then make use of it, in this case using the print command.

Note that adding a variable to the data returned by this function is not strictly necessary, but it's a good habit to get into when you're starting out with Lua, as it will help you visualise what's going on when you can assign meaningful names to pieces of data. You can get exactly the same output by simply putting the function directly into the print command. Give it a try:
quote:


print (ScenEdit_GetWeather())


So we've got an output, which probably doesn't make much sense to a Lua beginner in its raw form. First, what's the deal with the curly brackets? When you see these curly brackets { and }, that means that you are dealing with a table. A table is a collection of data that has some sort of relationship or common theme. Once you become more familiar with Lua you will be using tables a lot to store information and cut down on repetitive coding, but for now we're going to focus on how tables relate to the GetWeather and SetWeather functions we're discussing.

The table we have here, which is also assigned to the variable ourVariable (unless you closed Command and re-opened it, in which case follow the code steps above to catch up). In the spirit of relating pieces of data to variables with meaningful names, let's assign that weather data table to the variable scenarioWeather. Input the following into the console:
quote:


scenarioWeather = ourVariable


Q: How else could you assign the scenario weather data to the variable scenarioWeather?
A: scenarioWeather = ScenEdit_GetWeather()


Now we have the data assigned to a meaningful name, lets pick apart what a table is and how to access and use the data contained within. As described above, tables are collections of data that are somehow related. There are two types of tables in Lua, arrays and dictionaries. Type the following example of an array into your console:
quote:


Letters = {'A','B','C','D','E'}

An array is basically a list of values. They can be strings, numbers, nested tables or even functions. To access the data in an array, we use the variable assigned to the array and then square brackets [ and ] around the sequential number of the value we're referring to. So, to print 'C', the 3rd value, we could use the following command:
quote:


print (Letters[3])

This would print the third value of the Letters table to the console, in this case C. Arrays are useful for short, simple lists but the numbering can get confusing if you need to refer to a specific value in a large or nested list. That's where dictionaries come in. A dictionary follows the basic format of an array with one key (ugh) difference. In a dictionary, we have keys and values. Type the following example of a dictionary into your console:
quote:


Car = {colour = 'Silver', make = 'DeLorean', model = 'DMC-12', year = 1982, registration = 'OUTTATIME'}

In the above example, the keys are colour, make, model, year and registration and the values are 'Silver', 'DeLorean', 'DMC-12', 1982 and 'OUTTATIME'. To reference a value within a table, we use the variable name for the table followed by a fullstop/period . and then the desired key. Lets use this dictionary to print the colour of the car:
quote:


print (Car.colour)

This should print Silver to the console. As you can see, the key and value system makes dealing with large quantities of data much easier for human eyes.

Now that we know what an array and a dictionary is and how they differ, we can easily see that our weather data is in the form of a dictionary.

Q: Knowing what you do now, how would you print the temp value from scenarioWeather?
A: print(scenarioWeather.temp) or print(ScenEdit_GetWeather().temp)


Now that we understand the output of GetWeather, let's put it to some meaningful use. First, we'll take a look at the documentation for SetWeather.
quote:


ScenEdit_SetWeather (temperature, rainfall, undercloud, seastate)
Set the current weather conditions.

This function takes four numbers that describe the desired weather conditions. These conditions are applied globally.
Parameters:

temperature number The current baseline temperature (in deg C). Varies by latitude.
rainfall number The rainfall rate, 0-50.
undercloud number The amount of sky that is covered in cloud, 0.0-1.0
seastate number The current sea state 0-9.

Returns:

boolean True/False for Successful/Failure


This tells us that the function ScenEdit_SetWeather requires some parameters, which are listed in the brackets. These parameters are all numbers (as opposed to strings, tables or boolean inputs). There is also a description of the accepted parameters.

So with this in mind, lets make some changes to the weather. Type the following into your console and click 'Run':
quote:


ScenEdit_SetWeather(0,25,0.8,5)

This changes the weather to a pretty nasty storm. How do we know it worked? The quickest method is to simply move your mouse over anywhere on the map and the databox will list the weather conditions. This is nice, but lets say you come back to this in two weeks time... It would be nice to have some meaningful variable names to look at rather than a bunch of numbers. Lets reset the weather to its initial state, this time using the dictionary table we generated earlier with the GetWeather command.
Type the following into your console and click 'Run':
quote:


ScenEdit_SetWeather(scenarioWeather.temp,scenarioWeather.rainfall,scenarioWeather.undercloud,scenarioWeather.seastate)

Move your mouse over the map to see that the weather is reset back to the default. As you can see, while the function requires a number, we can use a variable or a table reference that points to a number. This is incredibly useful as you get more comfortable with Lua and begin to make more complex scripts.

So now we know how to retrieve the current weather data with GetWeather, create variables, reference a table, and alter the weather with SetWeather. Let's do something that is actually useful in scenario design and make a simple script to randomise the weather. To do this, we're going to make use of the native Lua functions math.randomseed(), os.time() and math.random(). Type the following into your console:
quote:


math.randomseed(os.time())
currentWeather = ScenEdit_GetWeather()
newTemp = currentWeather.temp + math.random(-5,5)
newRainfall = math.random(0,50)
newUndercloud = math.random(0,10)/10
newSeastate = math.random(0,9)
ScenEdit_SetWeather(newTemp, newRainfall, newUndercloud, newSeastate)

Now, I know we introduced some new things here. So let's go through it step by step (or line by line, as it were)
quote:


math.randomseed(os.time())

This calls the math.randomseed function, and uses os.time as a parameter. Great, what does that mean? math.randomseed is a native Lua function (i.e. it is part of Lua, not part of the Command Lua framework that is overlaid onto Lua) that sets the seed number used for random calculations. If we were to use the random seed 1, every 'random' result afterwards would be the same 'random' result for each time we ran the script. To avoid that, we use the the os.time function, which returns the operating system time as a number that represents the number of seconds elapsed since January 1, 1970. This is obviously a large number, and will be different every time the script is called, so it's perfect to ensure 'randomly random' results, not 'predictably random' results like we'd get without specifying a new random seed.
quote:


currentWeather = ScenEdit_GetWeather()

This should be obvious for you now, we're assigning the weather data from GetWeather into a variable called currentWeather
quote:


newTemp = currentWeather.temp + math.random(-5,5)

Here we're assigning the value from the temp key in our variable currentWeather to a variable named newTemp. Then, we're adding a random number to it, between -5 and 5. This is done by using the Lua native function math.random, which requires two parameters: the minimum value (-5 in this case) and the maximum value (5 in this case). When a negative number is used as the minimum, 0 is included in the range (i.e. in our example the possible results are:
-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5

Primary school math teaches us that adding a negative number is the same as subtracting, so the actual effect of this line is to take our current temperature, randomly add or subtract up to 5 degrees from it--or add 0 and leave it unchanged--and then store that result as a variable named newTemp for future use.
quote:


newRainfall = math.random(0,50)

This simply assigns a random number between 0 and 50 for rainfall and stores it as the variable newRainfall
quote:


newUndercloud = math.random(0,10)/10

Remember how in the Command Lua Documentation undercloud was specified as a decimal number between 0 and 1.0? math.random can produce very large random decimals, but for our purposes we just want one decimal place. An easy way to do that is to generate a random number between 0 and 10 (meaning 0 is a possible result) and divide by 10. This will give us possible results of:
0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0
quote:


newSeastate = math.random(0,9)

Another simple random number, this time between 0 and 9.
quote:


ScenEdit_SetWeather(newTemp, newRainfall, newUndercloud, newSeastate)

Now we're going to take all of those variables and put them into the SetWeather function and run it.

Et voila, you have now written your first random weather script!

Experiment with different values and math operators for your variables, and explore some of the other functions using the knowledge you've developed here in conjunction with the Command Lua Documentation.




rmunie0613 -> RE: Absolute Beginner's Intro to Lua: GetWeather and SetWeather (2/12/2018 10:04:53 PM)

This is great work Apache. Thank you very much.




rmunie0613 -> RE: Absolute Beginner's Intro to Lua: GetWeather and SetWeather (2/14/2018 5:38:46 AM)

OK in testing this, I got finally a daily change in weather- a huge objective of mine... but the random one did not work... so what I did was for the moment, input set numbers, and event editor to make it change every day..so something of a half-win lol, while I try t o figure what I am doing wrong with regard to the random idea... it is rather fun to play with- even the fails actually. I much appreciate the help.

Next up after the weather, will be trying to make the AI side move units forward and then putting them into missions once they do so (since obviously they move forward on "Ferry"... then must loadout as AAW or ASuW... I see the choices for how to do that, but will figure this weather out first.

One thing also that I will need to figure out that I have been unable to, is where one gets the unit numbers...say you wish to order "Callsign XRay #s 1-10" to a different mission or base...and they are one of 2 F-14A Squadrons... I am not sure how to make a LUA script calling on that particular group to do something...or ordering one specific Surface vessel to refuel- seems a great way around the fact that Pirate vessels run out of fuel too quickly and then sit in the water...but not sure how to specify which unit... ha...but yes, one thing at a time.

The step by step you did is really great.

EDIT- got it!




thewood1 -> RE: Absolute Beginner's Intro to Lua: GetWeather and SetWeather (2/21/2018 12:08:42 PM)

I just noticed this. It should be a standard read for new players getting into Lua. Thanks for the effort.




rmunie0613 -> RE: Absolute Beginner's Intro to Lua: GetWeather and SetWeather (3/1/2018 6:42:22 PM)


quote:

ORIGINAL: thewood1

I just noticed this. It should be a standard read for new players getting into Lua. Thanks for the effort.

I 100% agree... while it had to do with weather, it has given me a huge "leg up" on LUA itself...still hitting the rocks on occasion, but getting better at it.




Gunner98 -> RE: Absolute Beginner's Intro to Lua: GetWeather and SetWeather (3/1/2018 8:30:50 PM)

Well done apache. My usual method is to:

1. copy someone's script
2. modify
3. get it wrong
4. get frustrated
5. call for help

Your explanations help with the 'why' things are done.

Thanks

B




Rory Noonan -> RE: Absolute Beginner's Intro to Lua: GetWeather and SetWeather (3/1/2018 10:45:52 PM)

I'm glad it's of use; I plan to do more stuff like this as time allows in the future




KungPao -> RE: Absolute Beginner's Intro to Lua: GetWeather and SetWeather (6/23/2021 9:45:01 PM)

Thank you. I am learning LUA now


quote:

newRainfall = math.random(0,50)


one question, I changed this a little bit, make this:
newRainfall = currentWeather.rainfall + math.random(-5,5)
to simulate the weather slowly change from one to another. But is there anyway to set the condition that the number should be in the range of 0 to 50?

Thank you




KnightHawk75 -> RE: Absolute Beginner's Intro to Lua: GetWeather and SetWeather (6/24/2021 1:15:48 AM)


quote:

ORIGINAL: KungPao

Thank you. I am learning LUA now


quote:

newRainfall = math.random(0,50)


one question, I changed this a little bit, make this:
newRainfall = currentWeather.rainfall + math.random(-5,5)
to simulate the weather slowly change from one to another. But is there anyway to set the condition that the number should be in the range of 0 to 50?

Thank you


If you are asking how to generate a random that is between two values.
newRainfall = math.random(0,50)




Rory Noonan -> RE: Absolute Beginner's Intro to Lua: GetWeather and SetWeather (6/24/2021 3:07:24 PM)

I think I see what you're trying to do.

In order to do that, you'll need to assign those current values to a variable, alter it, and then use that new value for the updated weather setting.

I wrote some code to do this for you, it's not the most efficient way of doing it but it should demonstrate the principles [:)]

quote:


weatherParameters = {
temp = {min= -50, max = 50},
rainfall = {min = 0, max = 50},
undercloud = {min = 0, max = 1},
seastate = {min = 0, max = 10}
}

function ValueIsWithinWeatherParameters(checkKey,testValue)
local checkValues = weatherParameters[checkKey]
return testValue >= checkValues.min and testValue <= checkValues.max
end

function WeatherDrift(minDriftValue,maxDriftValue)
local existingConditions = ScenEdit_GetWeather()
local newConditions = {}
for key,value in pairs(existingConditions) do
local suitableValueFound = false
local attempts = 0
while not suitableValueFound and attempts < 100 do
local modifier = math.random(minDriftValue,maxDriftValue)
local newValue = value+modifier
if ValueIsWithinWeatherParameters(key, newValue) then
newConditions[key] = newValue
suitableValueFound = true
end
attempts = attempts + 1
end
if not suitableValueFound then
newConditions[key] = existingConditions[key]
end
print ('Randomized value set: '..tostring(suitableValueFound))
print ('Attempts: '..attempts)
print ('Key: '..key)
print ('Initial value: '..existingConditions[key])
print ('Final value: '..newConditions[key])
end
ScenEdit_SetWeather(newConditions.temp, newConditions.rainfall, newConditions.undercloud, newConditions.seastate)
end

WeatherDrift(-5,5)




KungPao -> RE: Absolute Beginner's Intro to Lua: GetWeather and SetWeather (6/24/2021 9:13:52 PM)

wow! Thank you![&o]

Yes, that's what I am looking for
Guess that's too much information for me to absorb . I will spend some to time to study this, probably will ask another question one month later [8D]

Appreciate that.




Rory Noonan -> RE: Absolute Beginner's Intro to Lua: GetWeather and SetWeather (6/25/2021 2:07:16 PM)

No problem, glad to help.

The forum makes a mess of the formatting, but if you tab out the code it makes a lot more sense [:)]




KnightHawk75 -> RE: Absolute Beginner's Intro to Lua: GetWeather and SetWeather (6/26/2021 1:26:59 AM)

quote:

ORIGINAL: Rory Noonan

No problem, glad to help.

The forum makes a mess of the formatting, but if you tab out the code it makes a lot more sense [:)]


Fwi Rory, if you wrap it in a [ code ] tag (one for about every ~20 lines) it will maintain the formatting.
Only down side is it adds a ton of unwanted space at the end, and if you go more than 20 lines it'll mangle the display for some reason.

weatherParameters = {
    temp = {min= -50, max = 50},
    rainfall = {min = 0, max = 50},
    undercloud = {min = 0, max = 1},
    seastate = {min = 0, max = 10}
}

function ValueIsWithinWeatherParameters(checkKey,testValue)
    local checkValues = weatherParameters[checkKey]
    return testValue >= checkValues.min and testValue <= checkValues.max
end 
function WeatherDrift(minDriftValue,maxDriftValue)
    local existingConditions = ScenEdit_GetWeather()
    local newConditions = {}
    for key,value in pairs(existingConditions) do
        local suitableValueFound = false
        local attempts = 0
        while not suitableValueFound and attempts < 100 do
          local modifier = math.random(minDriftValue,maxDriftValue)
          local newValue = value+modifier
          if ValueIsWithinWeatherParameters(key, newValue) then
              newConditions[key] = newValue
              suitableValueFound = true
          end
          attempts = attempts + 1
        end
        if not suitableValueFound then
            newConditions[key] = existingConditions[key]
        end
        print ('Randomized value set: '..tostring(suitableValueFound))
        print ('Attempts: '..attempts)
        print ('Key: '..key)
        print ('Initial value: '..existingConditions[key])
        print ('Final value: '..newConditions[key])
    end
    ScenEdit_SetWeather(newConditions.temp, newConditions.rainfall, newConditions.undercloud, newConditions.seastate);
end

WeatherDrift(-5,5)




Page: [1]

Valid CSS!




Forum Software © ASPPlayground.NET Advanced Edition 2.4.5 ANSI
1.705078