Monday, July 14, 2025

Lua Scripting in Command: Modern Operations

Command: Modern Operations (CMO) integrates Lua as a robust scripting language, allowing scenario designers to create highly dynamic and interactive simulations. 

Unlike general-purpose game engines where Lua might be used for core game logic, in CMO, Lua acts as an interface to the simulation engine, enabling you to:

  • Automate events: Trigger actions based on specific conditions (e.g., unit entering an area, time elapsed).

  • Manipulate units: Add, delete, modify, or assign units to missions.

  • Adjust game parameters: Change weather, EMCON settings, or side postures.

  • Create dynamic AI: Implement complex behaviors for computer-controlled forces.

  • Manage scenario state: Save and retrieve custom data within the scenario.

How Lua Scripts are Used in CMO

Lua scripts in CMO are typically executed in a few ways:

  1. Event Editor (Triggers, Conditions, Actions - TCA): This is the most common method. You define "Events" which consist of:

    • Triggers: What initiates the event (e.g., a unit detecting an enemy, a specific time).

    • Conditions (Optional): Additional criteria that must be met for the event to fire (often a Lua script that returns true or false).

    • Actions: What happens when the trigger and conditions are met (can be a Lua script that executes commands).

  2. Lua Console: CMO has an in-game console where you can directly type and execute Lua commands for testing or immediate changes.

  3. Scenario Initialization: Scripts can be set to run automatically when a scenario loads.

Key Lua Functions in CMO

CMO exposes a specific API through global Lua functions, mostly prefixed with ScenEdit_ and Tool_. These functions allow you to interact with various aspects of the simulation. Here are some common categories and examples:

  • Unit Management:

    • ScenEdit_AddUnit(): Adds a new unit (ship, aircraft, submarine, facility).

    • ScenEdit_DeleteUnit(): Removes a unit.

    • ScenEdit_SetUnit(): Modifies properties of an existing unit.

    • ScenEdit_AssignUnitToMission(): Assigns a unit to a mission.

  • Scenario State & Information:

    • ScenEdit_CurrentTime(): Gets the current scenario time.

    • ScenEdit_GetUnit(): Retrieves information about a unit.

    • ScenEdit_GetSide(): Retrieves information about a side.

    • ScenEdit_SetKeyValue(), ScenEdit_GetKeyValue(): Store and retrieve persistent data in the scenario.

  • Environment & Doctrine:

    • ScenEdit_SetWeather(): Changes weather conditions.

    • ScenEdit_SetEMCON(): Sets EMCON (Emission Control) for units, missions, or sides.

    • ScenEdit_SetSidePosture(): Defines the posture between two sides (e.g., Hostile, Neutral).

    • ScenEdit_SetDoctrine(), ScenEdit_GetDoctrine(): Manage unit doctrines.

  • Utilities:

    • print(): Outputs messages to the Lua console.

    • Tool_LOS(): Performs Line of Sight checks.

Example Lua Scripts for CMO

These examples demonstrate common tasks you might want to automate or control in a CMO scenario. Remember that for multi-line scripts, CMO often requires \r\n for newlines.

Example 1: Adding a New Ship and Assigning it to a Mission

This script adds a new destroyer to the "Blue" side at a specific location and then assigns it to an existing mission.

-- Lua Script: AddShipAndAssignToMission.lua

-- Define variables for clarity
local sideName = "Blue"
local shipName = "USS Arleigh Burke"
local dbid = 960 -- Example DBID for an Arleigh Burke class destroyer (you'd find this in CMO's database viewer)
local latitude = "N38.50.00"
local longitude = "E6.50.00"
local missionName = "Patrol Mission Alpha"

-- Add the new ship
-- ScenEdit_AddUnit({side='<Side Name>', type='ship', name='<Unit Name>', dbid=<DBID>, latitude='<Latitude>', longitude='<Longitude>'})
local newShip = ScenEdit_AddUnit({
    side = sideName,
    type = 'ship',
    name = shipName,
    dbid = dbid,
    latitude = latitude,
    longitude = longitude
})

-- Check if the ship was added successfully
if newShip then
    print("Successfully added " .. shipName .. " to " .. sideName .. " side.")
    
    -- Assign the new ship to a mission
    -- ScenEdit_AssignUnitToMission('<Unit Name>', '<Mission Name>')
    local assignResult = ScenEdit_AssignUnitToMission(shipName, missionName)
    
    if assignResult then
        print(shipName .. " successfully assigned to " .. missionName .. ".")
    else
        print("Failed to assign " .. shipName .. " to " .. missionName .. ". Make sure the mission exists.")
    end
else
    print("Failed to add " .. shipName .. ". Check DBID and coordinates.")
end

-- Note: For use in the Event Editor, you might need to concatenate this into a single string
-- with '\r\n' for newlines, or use the multi-line script editor if available.

Example 2: Changing Side Posture Based on Time

This script could be used as an "Action" in an event triggered by time. For instance, at a certain scenario time, Side A becomes hostile towards Side B.

-- Lua Script: SetHostilePosture.lua

-- Define sides
local sideA = "Blue"
local sideB = "Red"

-- Set Side A's posture towards Side B to Hostile
-- ScenEdit_SetSidePosture('<Side A Name>', '<Side B Name>', '<Posture Code>')
-- Posture Codes: H (Hostile), N (Neutral), F (Friendly)
local result = ScenEdit_SetSidePosture(sideA, sideB, "H")

if result then
    print(sideA .. " is now Hostile towards " .. sideB .. ".")
else
    print("Failed to change posture between " .. sideA .. " and " .. sideB .. ".")
end

Example 3: Dynamic Weather Change

This script changes the weather conditions in the scenario. This could be an action in an event triggered by time or another game state.

-- Lua Script: ChangeWeather.lua

-- Define weather parameters
local temperature = 15 -- Degrees Celsius
local rainfallRate = 20 -- Out of 50
local clouds = 0.7 -- Out of 1.0 (70% cloud cover)
local seaState = 4 -- Out of 9 (Moderate sea state)

-- Set the weather
-- ScenEdit_SetWeather({temperature=<temp>, rainfall=<rate>, clouds=<percent>, seastate=<state>})
local result = ScenEdit_SetWeather({
    temperature = temperature,
    rainfall = rainfallRate,
    clouds = clouds,
    seastate = seaState
})

if result then
    print("Weather updated successfully.")
    print("Temperature: " .. temperature .. " C")
    print("Rainfall: " .. rainfallRate .. "/50")
    print("Clouds: " .. clouds * 100 .. "%")
    print("Sea State: " .. seaState .. "/9")
else
    print("Failed to update weather.")
end

Example 4: Checking if a Unit is in a Specific Area (Condition Script)

This script would typically be used as a "Condition" in an event. It returns true if a specified unit is within a predefined circular area, and false otherwise.

-- Lua Script: IsUnitInArea.lua

-- Define the unit to check
local unitName = "USS Arleigh Burke"

-- Define the center of the area (reference point or coordinates)
-- Using a reference point is often easier for scenario design
local areaCenterRefPoint = "Restricted Zone Center" 
local areaRadiusNM = 10 -- Radius in nautical miles

-- Get the unit object
local unit = ScenEdit_GetUnit({name = unitName})

-- Get the reference point object
local refPoint = ScenEdit_GetReferencePoint({name = areaCenterRefPoint})

local isInArea = false

if unit and refPoint then
    -- Calculate distance between unit and reference point
    -- CMO's Lua API might have a direct distance function, or you might need to calculate it
    -- For simplicity, let's assume a hypothetical ScenEdit_GetDistance function or calculate manually
    
    -- *** IMPORTANT: The actual distance calculation function might vary or need to be implemented.
    -- This is a conceptual example. You might need to get unit/ref point lat/lon and use a
    -- geodesic distance formula, or check CMO's API for a built-in function.
    -- For a basic check, let's assume a function exists or you've pre-calculated coordinates.
    
    -- Placeholder for actual distance calculation (replace with CMO's method or a library)
    -- local distance = ScenEdit_GetDistance(unit.latitude, unit.longitude, refPoint.latitude, refPoint.longitude)
    
    -- For demonstration, let's just assume a dummy distance for now
    local dummyDistance = 5 -- nautical miles
    
    if dummyDistance <= areaRadiusNM then
        isInArea = true
        print(unitName .. " is inside the restricted zone.")
    else
        print(unitName .. " is outside the restricted zone (Distance: " .. dummyDistance .. " NM).")
    end
else
    print("Error: Unit '" .. unitName .. "' or Reference Point '" .. areaCenterRefPoint .. "' not found.")
end

-- Conditions must return true or false
return isInArea

Where to Learn More

  • Command Lua Docs: The official and most comprehensive documentation for CMO's Lua API can be found at https://commandlua.github.io/. This is your primary resource for all available functions and their parameters.

  • Matrix Games Forums: The official forums (especially the "Lua Legion" section) are a great place to ask questions, find examples, and learn from other scenario designers.

  • YouTube Tutorials: Search for "Command Modern Operations Lua tutorial" on YouTube. There are several community-made videos that walk through the basics.

  • General Lua Tutorials: If you're new to Lua, it's beneficial to learn the fundamentals of the language itself. Websites like https://www.lua.org/ and various online tutorials can help you grasp core concepts like variables, tables, functions, and control structures.

Lua scripting in CMO can be incredibly powerful for creating complex and immersive scenarios. Start with simple scripts and gradually build up your knowledge by experimenting with the API and consulting the documentation.

No comments: