1
0
mirror of https://github.com/Refactorio/RedMew.git synced 2024-12-14 10:13:13 +02:00
RedMew/features/force_control.lua

271 lines
8.7 KiB
Lua
Raw Normal View History

-- dependencies
local Global = require 'utils.global'
local Event = require 'utils.event'
local raise_event = script.raise_event
local max = math.max
-- this, things that can be done run-time
local ForceControl = {}
ForceControl.events = {
--- triggered when the force levels up
--- uses event = {level_reached = number, force = LuaForce}
on_level_up = script.generate_event_name()
}
-- the builder, can only be accessed through ForceControl.register() and should be avoided used run-time
local ForceControlBuilder = {}
-- all force data being monitored
local forces = {}
-- the table holding the function that calculates the experience to next level
local next_level_cap_calculator = {
execute = nil
}
Global.register(
{
forces = forces,
next_level_cap_calculator = next_level_cap_calculator
},
function(tbl)
forces = tbl.forces
next_level_cap_calculator = tbl.next_level_cap_calculator
end
)
---Asserts if a given variable is of the expected type using type().
---
---@param expected_type string
---@param given any
---@param variable_reference_message string displayed when the expectation is not met
local function assert_type(expected_type, given, variable_reference_message)
local given_type = type(given)
if given_type ~= expected_type then
error('Argument ' .. variable_reference_message .. " must be of type '" .. expected_type .. "', given '" .. given_type .. "'")
end
end
---Returns a valid force based on the lua force or name given.
---@param lua_force_or_name LuaForce|string
local function get_valid_force(lua_force_or_name)
if not lua_force_or_name then
return
end
if type(lua_force_or_name) == 'string' then
local force = game.forces[lua_force_or_name]
if not force or not force.valid then
return
end
return force
end
if type(lua_force_or_name) ~= 'table' or not lua_force_or_name.valid or nil == lua_force_or_name.evolution_factor then
return
end
return lua_force_or_name
end
---Register a reward that checks if a reward should be given if the level
---matches the callback. If so, it will apply the if_level_criteria_matches
---callback.
---
---@param level_matches function function(number level_reached)
---@param callback function function(number level_reached, LuaForce force)
---@param lua_force_name string|nil only register for this force (optional)
function ForceControlBuilder.register(level_matches, callback, lua_force_name)
if game then
error('You can only register level up callbacks before the game is initialized')
end
assert_type('function', level_matches, 'level_matches of function ForceControl.register_reward')
assert_type('function', callback, 'callback of function ForceControlBuilder.register')
local function on_level_up(event)
local level = event.level_reached
if level_matches(level, event.force) then
callback(level, event.force)
end
end
if not lua_force_name then
Event.add(ForceControl.events.on_level_up, on_level_up)
return
end
Event.add(
ForceControl.events.on_level_up,
function(event)
local force = get_valid_force(lua_force_name)
if not force then
error('Can only register a lua force name for ForceControlBuilder.register')
end
if force ~= event.force then
return
end
on_level_up(event)
end
)
end
---Register a reward which triggers when the given level is reached.
---
---@param level number
---@param callback function function(number level_reached, LuaForce force)
---@param lua_force_name string|nil only register for this force (optional)
function ForceControlBuilder.register_on_single_level(level, callback, lua_force_name)
assert_type('number', level, 'level of function ForceControl.register_reward_on_single_level')
assert_type('function', callback, 'callback of function ForceControlBuilder.register_on_single_level')
ForceControlBuilder.register(
function(level_reached)
return level == level_reached
end,
callback,
lua_force_name
)
end
---Always returns true
local function always_true()
return true
end
---Register a reward that triggers for every level.
---
---@param callback function function(number level_reached, LuaForce force)
---@param lua_force_name string|nil only register for this force (optional)
function ForceControlBuilder.register_on_every_level(callback, lua_force_name)
assert_type('function', callback, 'callback of function ForceControlBuilder.register_on_every_level')
ForceControlBuilder.register(always_true, callback, lua_force_name)
end
---Register the config and initialize the feature.
---@param level_up_formula function
function ForceControl.register(level_up_formula)
if next_level_cap_calculator.execute then
error('Can only register one force control.')
end
next_level_cap_calculator.execute = level_up_formula
return ForceControlBuilder
end
---Registers a new force to participate.
---@param lua_force_or_name LuaForce|string
function ForceControl.register_force(lua_force_or_name)
if not next_level_cap_calculator.execute then
error('Can only register a force when the config has been initialized via ForceControl.register(config_table).')
end
local force = get_valid_force(lua_force_or_name)
if not force then
error('Can only register a LuaForce for ForceControl')
end
forces[force.name] = {
current_experience = 0,
current_level = 0,
experience_level_up_cap = next_level_cap_calculator.execute(0)
}
end
---Returns the ForceControlBuilder.
function ForceControl.get_force_control_builder()
if not next_level_cap_calculator.execute then
error('Can only get the force control builder when the config has been initialized via ForceControl.register(config_table).')
end
return ForceControlBuilder
end
---Removes experience from a force. Won't cause de-level nor go below 0.
---@param lua_force_or_name LuaForce|string
---@param experience number amount of experience to add
function ForceControl.remove_experience(lua_force_or_name, experience)
assert_type('number', experience, 'Argument experience of function ForceControl.remove_experience')
if experience < 1 then
return
end
local force = get_valid_force(lua_force_or_name)
if not force then
return
end
local force_config = forces[force.name]
if not force_config then
return
end
force_config.current_experience = max(0, force_config.current_experience - experience)
end
---Adds experience to a force.
---@param lua_force_or_name LuaForce|string
---@param experience number amount of experience to add
function ForceControl.add_experience(lua_force_or_name, experience)
assert_type('number', experience, 'Argument experience of function ForceControl.add_experience')
if experience < 1 then
return
end
local force = get_valid_force(lua_force_or_name)
if not force then
return
end
local force_config = forces[force.name]
if not force_config then
return
end
local new_experience = force_config.current_experience + experience
local experience_level_up_cap = force_config.experience_level_up_cap
if (new_experience < experience_level_up_cap) then
force_config.current_experience = new_experience
return
end
-- level up
local new_level = force_config.current_level + 1
force_config.current_level = new_level
force_config.current_experience = 0
force_config.experience_level_up_cap = next_level_cap_calculator.execute(new_level)
raise_event(ForceControl.events.on_level_up, {level_reached = new_level, force = force})
ForceControl.add_experience(force, new_experience - experience_level_up_cap)
end
---Return the force data as {
--- current_experience = number,
--- current_level = number,
--- experience_level_up_cap = number,
--- experience_percentage = number,
---}
---@param lua_force_or_name LuaForce|string
function ForceControl.get_force_data(lua_force_or_name)
local force = get_valid_force(lua_force_or_name)
if not force then
return
end
local force_config = forces[force.name]
if not force_config then
return
end
return {
current_experience = force_config.current_experience,
current_level = force_config.current_level,
experience_level_up_cap = force_config.experience_level_up_cap,
experience_percentage = (force_config.current_experience / force_config.experience_level_up_cap) * 100
}
end
return ForceControl