mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2026-11-11 23:54:52 +09:00
The community name, Discord, website, status, GitHub, and Patreon links lived in exp_legacy/config/server_detail.lua, so changing them meant editing the scenario. They are now the community_details feature and can be edited from the Scenario Features page. Chat auto reply, help bubbles, and the readme read the live config instead of copying the values when the file loads, so an edit shows up without a restart. Help bubbles store the location name in their task and build the messages each cycle; the readme tab tooltip can be a function which is called when the readme opens. Every feature gets an enabled switch from the framework. There is no sensible meaning for a disabled community name, so the values are always used and the feature description says the switch does nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
137 lines
4.4 KiB
Lua
137 lines
4.4 KiB
Lua
--[[-- Control - Help Bubbles
|
|
Adds friendly biters that walk around and give helpful messages
|
|
]]
|
|
|
|
local Async = require("modules/exp_util/async")
|
|
local Storage = require("modules/exp_util/storage")
|
|
local Feature = require("modules/exp_scenario/features")
|
|
local community_details = require("modules/exp_scenario/config/community_details").config
|
|
|
|
local feature = Feature.register("help_bubbles")
|
|
|
|
--- Ticks between message changes
|
|
local message_cycle = 60 * 15
|
|
|
|
local locations = {
|
|
["Spawn"] = {
|
|
spawn_position = { x = 0, y = 0 },
|
|
spawn_surface = "nauvis",
|
|
entity_name = "small-biter",
|
|
--- Built each cycle so changes to community_details show without a restart
|
|
messages = function()
|
|
return {
|
|
{ "info.website", community_details.website },
|
|
{ "info.read-readme" },
|
|
{ "info.discord", community_details.discord },
|
|
{ "info.softmod" },
|
|
{ "info.redmew" },
|
|
{ "info.custom-commands" },
|
|
{ "info.status", community_details.status },
|
|
{ "info.lhd" },
|
|
{ "info.github", community_details.github },
|
|
{ "info.patreon", community_details.patreon },
|
|
}
|
|
end,
|
|
}
|
|
|
|
}
|
|
|
|
--- @type table<string, Async.AsyncReturn<any>>
|
|
local persistent_locations = {}
|
|
Storage.register(persistent_locations, function(tbl)
|
|
persistent_locations = tbl
|
|
end)
|
|
|
|
--- @class speech_bubble_task_param
|
|
--- @field entity LuaEntity
|
|
--- @field messages LocalisedString[]?
|
|
--- @field location string? Used when there are no messages, a key of locations
|
|
--- @field previous_message LuaEntity?
|
|
--- @field current_message_index number
|
|
|
|
--- Used by create_entity within speech_bubble_async
|
|
local speech_bubble_param = {
|
|
name = "compi-speech-bubble",
|
|
position = { 0, 0 },
|
|
}
|
|
|
|
--- Cycle between a set category of messages above an entity
|
|
local speech_bubble_task =
|
|
Async.register(function(task)
|
|
--- @cast task speech_bubble_task_param
|
|
local entity = task.entity
|
|
if not entity.valid then
|
|
return Async.status.complete()
|
|
end
|
|
|
|
local messages = task.messages or locations[task.location].messages()
|
|
local index = task.current_message_index
|
|
if index > #messages then
|
|
index = 1
|
|
end
|
|
|
|
local previous_message = task.previous_message
|
|
if previous_message and previous_message.valid then
|
|
previous_message.destroy()
|
|
end
|
|
|
|
speech_bubble_param.source = entity
|
|
speech_bubble_param.text = messages[index]
|
|
task.previous_message = entity.surface.create_entity(speech_bubble_param)
|
|
|
|
task.current_message_index = index + 1
|
|
return Async.status.delay(message_cycle, task)
|
|
end)
|
|
|
|
--- Register an entity to start spawning speech bubbles
|
|
--- @param entity LuaEntity the entity which will have messages spawn from it
|
|
--- @param messages LocalisedString[] the messages which should be shown
|
|
--- @param starting_index number? the message index to start at, default 1
|
|
--- @return Async.AsyncReturn<any>
|
|
local function register_entity(entity, messages, starting_index)
|
|
return speech_bubble_task{
|
|
entity = entity,
|
|
messages = messages,
|
|
current_message_index = starting_index or 1,
|
|
}
|
|
end
|
|
|
|
--- Check all persistent locations from the config are active
|
|
local function check_persistent_locations()
|
|
for name, location in pairs(locations) do
|
|
local task = persistent_locations[name]
|
|
if task and not task.completed then
|
|
goto continue
|
|
end
|
|
|
|
local surface = game.get_surface(location.spawn_surface)
|
|
if not surface then
|
|
goto continue
|
|
end
|
|
|
|
local position = surface.find_non_colliding_position(location.entity_name, location.spawn_position, 1.5, 0.5)
|
|
if not position then
|
|
goto continue
|
|
end
|
|
|
|
local entity = surface.create_entity{ name = location.entity_name, position = position, force = game.forces.neutral }
|
|
if not entity then
|
|
goto continue
|
|
end
|
|
|
|
persistent_locations[name] = speech_bubble_task{
|
|
entity = entity,
|
|
location = name,
|
|
current_message_index = 1,
|
|
}
|
|
::continue::
|
|
end
|
|
end
|
|
|
|
return feature:guard{
|
|
on_nth_tick = {
|
|
[message_cycle] = check_persistent_locations,
|
|
},
|
|
register_entity = register_entity,
|
|
}
|