Skip to content

Usage instructions

Sven Marcus edited this page Jun 5, 2021 · 1 revision

Using the framework in Galactic Conquest

A single story plot in a GC should be set as the container for the EawX Framework. A high ServiceRate must be set (we use 0.1) in order to ensure that all plugins will receive updates at the correct time. Calling deepcore:galactic will load plugins from the folder specified in the configuration table and return a plugin runner that is responsible for continuously updating your plugins. Additionally a context table that can include variables you want to pass to every plugin init() function and a list of plugin folders may be provided inside the configuration table. If no plugin folder list is given the plugin system will load the file InstalledPlugins.lua from the specified plugin folder.

require("PGDebug")
require("PGStateMachine")
require("PGStoryMode")

require("deepcore/std/deepcore")

function Definitions()
    DebugMessage("%s -- In Definitions", tostring(Script))

    ServiceRate = 0.1

    StoryModeEvents = {Universal_Story_Start = Begin_GC}
end

function Begin_GC(message)
    if message == OnEnter then
        -- The context table allows you to pass variables to
        -- the init() function of your plugins
        local context = {}

        DeepCoreRunner = deepcore:galactic {
            context = context,
            plugin_folder = "eawx-plugins/galactic"
        }
    elseif message == OnUpdate then
        DeepCoreRunner:update()
    end
end

Using the framework with a game object script

The framework isn't limited to Galactic Conquest. You can also use it from a game object script. Similar to galactic mode you can call deepcore:game_object() with a configuration table to load plugins and create a plugin runner. Just like deepcore:galactic() you can provide optional context and plugins tables. It is intended to be used during tactical mode only.

require("PGCommands")
require("PGStateMachine")
require("deepcore/std/deepcore")

function Definitions()
    DebugMessage("%s -- In Definitions", tostring(Script))

    Define_State("State_Init", State_Init)
end

function State_Init(message)
    if message == OnEnter then
        if Get_Game_Mode() ~= "Space" then
            ScriptExit()
        end

        DeepCoreRunner = deepcore:game_object {
            context = {},
            plugin_folder = "eawx-plugins/gameobject/space",
            plugins = { "microjump" }
        }
    elseif message == OnUpdate then
        DeepCoreRunner:update()
    end
end

Creating a plugin definition

A plugin consists of a folder containing at least a file called init.lua. It is used to configure and create the plugin object. The init.lua must return a table with both target and init keys. The target specifies when or how often a plugin gets updated. The targets are defined in plugintargets.lua and can be extended with new targets if needed. init refers to a function that must at least be able to receive the self argument. Its second argument is the plugin context (ctx) table that was defined in the main GC or game object Lua function (see Using the framework in Galactic Conquest and Using the framework with a game object script). If you're defining a galactic conquest plugin the ctx table contains the GalacticConquest object in ctx.galactic_conquest. See The GalacticConquest class for more information on what functions it provides. The init function must return a plugin object with an update function (unless the target is never()). Galactic conquest plugins can set the requires_planets to true. In that case the plugin's update function will be called with one planet at a time.

The code snippet below demonstrates the structure of the init.lua file.

require("deepcore/std/plugintargets")
require("eawx-plugins/galactic/production-listener/ProductionFinishedListener")

return {
    target = PluginTargets.never(),
    requires_planets = false,
    init = function(self, ctx)
        ---@type GalacticConquest
        local galactic_conquest = ctx.galactic_conquest

        return ProductionFinishedListener(galactic_conquest)
    end
}

The framework comes with the following plugin targets out of the box:

Name Parameters Description
always - Allows a plugin to be updated every frame
never - Never allows updates. Use this if you want to react to observable events only
interval number Updates the plugin every X seconds
story_flag string, PlayerWrapper Uses Check_Story_Flag to determine if an update is allowed

Implementing a plugin object

As specified in the previous section most plugin objects require an update function. Plugin objects can be created using a simple table inside the plugin definition:

require("deepcore/std/plugintargets")

return {
    target = PluginTargets.interval(45)
    init = function(self, ctx)
        return {
            update = function(self)
                -- do something
            end
        }
    end
}

They can also be objects located in another file like the ProductionFinishedListener from the previous section. The next section about events will show its code in more detail. To enhance readability and code structure it is recommended to place plugin objects in separate files.

Using the events from the GalacticConquest object

The following example demonstrates how to listen to the events defined in the GalacticConquest object. As explained in the first plugin definition, the GalacticConquest object gets passed to a plugin's init() function via the context table. From there it can be accessed with ctx.galactic_conquest. The events are defined inside a sub-table of the object and can be accessed with ctx.galactic_conquest.Events. The production-listener plugin uses the GalacticProductionFinished event to count the total amount of objects produced by the player and return the build cost of the produced object. To do that it attaches its own function on_production_finished with the additional argument self to the event via attach_listener().

require("deepcore/std/class")

---@class ProductionFinishedListener
ProductionFinishedListener = class()

---@param galactic_conquest GalacticConquest
function ProductionFinishedListener:new(galactic_conquest)
    self.human_player = galactic_conquest.HumanPlayer
    self.total_amount_of_objects = 0
    galactic_conquest.Events.GalacticProductionFinished:attach_listener(self.on_production_finished, self)
end

---We don't like to lose money, so we return it to the player on build completion
---@param planet Planet
---@param object_type_name string
function ProductionFinishedListener:on_production_finished(planet, object_type_name)
    if not planet:get_owner().Is_Human() then
        return
    end

    self.total_amount_of_objects = self.total_amount_of_objects + 1

    local object_type = Find_Object_Type(object_type_name)
    if object_type then
        local cost = object_type.Get_Build_Cost()
        self.human_player.Give_Money(cost)
    end
end

Defining a plugin with dependencies

Dependencies can be added to a plugin by providing a dependencies table in the plugin definition. The table contains a list of plugin folder names.

The plugin in the example below depends on the previously defined production-listener plugin. The plugin loader will initialise the dependencies of a plugin first and then pass the initialised dependencies to the plugin's init() function in the same order as specified in the dependencies table. In this case this means the init() function will receive an instance of ProductionFinishedListener.

Important: Plugin dependencies must not be cyclic. If a plugin A depends on plugin B, B depends on C and C depends on A the plugin loader will stop loading and likely break the GC script.

weekly-game-message-service/init.lua:

require("deepcore/std/plugintargets")
require("eawx-plugins/galactic/weekly-game-message-service/GameMessageService")

return {
    target = PluginTargets.interval(45),
    -- We can specify plugin dependencies in this table
    dependencies = {"production-listener"},
    -- The plugins we specified in the dependencies table will be passed to the init function in order
    init = function(self, ctx, production_finished_listener)
        return GameMessageService(production_finished_listener)
    end
}

weekly-game-message-service/GameMessageService.lua:

require("deepcore/std/class")

---@class GameMessageService
GameMessageService = class()

---@param production_finished_listener ProductionFinishedListener
function GameMessageService:new(production_finished_listener)
    self.production_finished_listener = production_finished_listener
end

-- We'll show a Game_Message for every produced object
function GameMessageService:update()
    local objects_produced = self.production_finished_listener.total_amount_of_objects

    for i = 1, objects_produced do
        Game_Message("TEXT_FACTION_EMPIRE")
    end
end