231 lines
8.3 KiB
Lua

local events = {
-- Custom event fired to request all name lists register themselves
on_list_rebuild = script.generate_event_name()
}
local MOD_SETTINGS = {
["namelists-backer-roboports"] = true,
["namelists-backer-labs"] = true,
["namelists-backer-stations"] = true,
["namelists-backer-locomotives"] = true,
["namelists-backer-radars"] = true,
["namelists-backer-default"] = true
}
function rebuild_lists()
global.names = {}
global.types = {}
global.defaults = {weight = 0, lists = {}}
if settings.global["namelists-backer-roboports"].value then
register_list({interface = "base", func = "backer_name", category = "type", target = "roboport", weight = #game.backer_names, backer = true})
end
if settings.global["namelists-backer-labs"].value then
register_list({interface = "base", func = "backer_name", category = "type", target = "lab", weight = #game.backer_names, backer = true})
end
if settings.global["namelists-backer-stations"].value then
register_list({interface = "base", func = "backer_name", category = "type", target = "train-stop", weight = #game.backer_names, backer = true})
end
if settings.global["namelists-backer-locomotives"].value then
register_list({interface = "base", func = "backer_name", category = "type", target = "locomotive", weight = #game.backer_names, backer = true})
end
if settings.global["namelists-backer-radars"].value then
register_list({interface = "base", func = "backer_name", category = "type", target = "radar", weight = #game.backer_names, backer = true})
end
if settings.global["namelists-backer-default"].value then
register_list({interface = "base", func = "backer_name", weight = #game.backer_names, backer = true})
end
script.raise_event(events.on_list_rebuild, {})
log("Interface lists rebuilt")
end
script.on_init(rebuild_lists)
script.on_configuration_changed(rebuild_lists)
script.on_event(defines.events.on_runtime_mod_setting_changed, function(event)
if MOD_SETTINGS[event.setting] then
rebuild_lists()
end
end)
-- Registers a name list for the specified name/type
-- If name or type isn't specified, register as generic
-- parameters is a table of {interface = "", func = "", category = "", target = "", weight = ""}
function register_list(parameters)
global.names = global.names or {}
global.types = global.types or {}
if not (parameters.interface and parameters.func and parameters.weight) then
log("Error registering: missing parameters")
return
end
if parameters.category and not parameters.target then
log("Error registering: category specified but no target")
return
end
local id = parameters.interface .. "." .. parameters.func
if parameters.category == "name" then
global.names[parameters.target] = global.names[parameters.target] or {weight = 0, lists = {}}
if not global.names[parameters.target].lists[id] then
log("Registering interface " .. id .. " for name " .. parameters.target)
global.names[parameters.target].lists[id] =
{
interface = parameters.interface,
func = parameters.func,
weight = parameters.weight,
backer = parameters.backer
}
global.names[parameters.target].weight = global.names[parameters.target].weight + parameters.weight
else
log("Eror registering interface " .. id .. ": already registered for name " .. parameters.target)
end
elseif parameters.category == "type" then
global.types[parameters.target] = global.types[parameters.target] or {weight = 0, lists = {}}
if not global.types[parameters.target].lists[id] then
log("Registering interface " .. id .. " for type " .. parameters.target)
global.types[parameters.target].lists[id] =
{
interface = parameters.interface,
func = parameters.func,
weight = parameters.weight,
backer = parameters.backer
}
global.types[parameters.target].weight = global.types[parameters.target].weight + parameters.weight
else
log("Eror registering interface " .. id .. ": already registered for type " .. parameters.target)
end
else
if not global.defaults.lists[id] then
log("Registering interface " .. id .. " as a default")
global.defaults.lists[id] =
{
interface = parameters.interface,
func = parameters.func,
weight = parameters.weight,
backer = parameters.backer
}
global.defaults.weight = global.defaults.weight + parameters.weight
else
log("Eror registering interface " .. id .. ": already registered as default")
end
end
end
-- remove list with id from category, because we've tried to use it and it wasn't there
function remove_list(category, id)
category.lists[id] = nil
category.weight = 0
for i,list in pairs(category.lists) do
category.weight = category.weight + list.weight
end
end
-- Keybind
script.on_event("random-name", function(event)
local selection = game.players[event.player_index].selected
if selection and selection.valid and
selection.supports_backer_name() then
selection.backer_name = pick_name(selection)
end
end)
-- Entity built
-- We don't catch the robot_built event because the ghost will already have a name chosen
-- in the player event
script.on_event(defines.events.on_built_entity, function(event)
if event.created_entity.supports_backer_name() then
auto_name(event.created_entity)
end
end)
-- Pick a name unless this is a station built without a ghost
-- Don't pick a name for hand-built stations, in case they were built over a ghost
function auto_name(entity)
if entity.type == "entity-ghost" then
if entity.backer_name == "" then
entity.backer_name = pick_name(entity)
end
elseif entity.type ~= "train-stop" then
entity.backer_name = pick_name(entity)
end
end
-- Get a pick a name list at random from the given category using the weighting
-- Request a name from that list
-- If a list is broken, deregister is and try again
function get_weighted_name(category, entity)
::roll::
if category.weight > 0 then
local dice_roll = math.random(category.weight) - 1
for id,list in pairs(category.lists) do
dice_roll = dice_roll - list.weight
if dice_roll < 0 then
if list.backer then
return game.backer_names[math.random(#game.backer_names)]
elseif remote.interfaces[list.interface] and remote.interfaces[list.interface][list.func] then
return remote.call(list.interface, list.func, entity)
else
-- if this list has stopped working, remove it and try again
log("Removing non-functioning interface " .. id)
remove_list(category, id)
goto roll
end
end
end
end
end
-- Make a call to a registered list, preferably by name but otherwise by type
-- Picks a generic name if no more specific list is available, falls back to a backer name
function pick_name(entity)
if not (entity and entity.valid) then
-- ABORT
return
end
local ent_name = entity.name
local ent_type = entity.type
-- is this a ghost? get the inner type
if entity.type == "entity-ghost" then
ent_name = entity.ghost_name
ent_type = entity.ghost_type
end
local new_name = nil
-- try to get a name based on entity name
if global.names[ent_name] and global.names[ent_name].weight > 0 then
new_name = get_weighted_name(global.names[ent_name], entity)
if new_name then
return new_name
end
end
-- try to get a name based on entity type
if global.types[ent_type] and global.types[ent_type].weight > 0 then
new_name = get_weighted_name(global.types[ent_type], entity)
if new_name then
return new_name
end
end
-- try to get a name from a default list
if global.defaults and global.defaults.weight > 0 then
new_name = get_weighted_name(global.defaults, entity)
if new_name then
return new_name
end
end
-- fallback: return a built-in backer name
return game.backer_names[math.random(#game.backer_names)]
end
-- Does a list exist for this entity, either by name or by type?
-- Deprecated, Namelists now always returns a name if the entity is valid
function has_name_list(entity)
return true
end
-- Add interfaces
remote.add_interface("Namelists", {
pick_name = pick_name,
has_list = has_name_list,
rebuild = rebuild_lists,
register = register_list,
get_events = function()
return events
end
})