--[[-- 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> 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 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, }