1. Summary
Please consider adding a native framework that allows hidden (composite) entities to automatically append their specific properties or circuit signals directly below the main "parent" entity's tooltip when a player hovers over it.
To maintain maximum UPS performance and clean code architecture, the tooltip layout must be statically defined in the
Code: Select all
Data StageCode: Select all
Control Stage2. Use Cases
Modders frequently use the "composite entity" pattern to expand vanilla mechanics. A common solution is overlaying hidden helper entities (such as accumulators, electric-energy-interfaces, or fluid boxes) exactly at the same position as the main visible entity.
* Example: A custom modded Roboport that requires a massive internal power backup. The modder places a hidden
Code: Select all
accumulatorCode: Select all
roboport* The Goal: When hovering over the Roboport, the player should see the standard Roboport tooltip, and right beneath it, the engine should automatically render the hidden accumulator's energy charge and custom signals. This provides a clean vanilla-like UX without forcing the player to open any GUIs.
3. Why Current Workarounds Are Inefficient
Currently, there is no clean way to merge tooltips of overlapping entities automatically:
1. UPS Overhead: Modders must use
Code: Select all
on_selected_entity_changedCode: Select all
LuaGui2. UI Inconsistency: Factorio 2.0 introduced amazing advanced scrollable tooltips (via Shift + Mouse Wheel). However, they are still strictly bound to a single top-most entity. Custom Lua GUIs cannot integrate seamlessly inside the native tooltip container.
4. Proposed API Design
Step 1: Data Stage (Encapsulation inside the hidden entity)
The hidden entity prototype declaratively defines which C++ properties it is willing to export if requested by a host. The C++ engine compiles these layouts statically during game startup, ensuring zero runtime layout-generation overhead.
Code: Select all
-- Inside the hidden accumulator prototype
exported_tooltip_fields = {
{
property = "electric-buffer-power", -- Native C++ entity property
localised_string = {"tooltip-format.buffer-power", "__1__"}
},
{
property = "configured-circuit-signal-value", -- Dynamic circuit network signal
default_signal = {type = "virtual", name = "signal-A"}, -- Default runtime fallback
localised_string = {"description.dynamic-signal", "__1__", "__2__"} -- __1__ = icon, __2__ = value
}
}When creating entities via script, the parent entity registers a flat array of its dependencies. The parent does not need to know the inner workings or property names of the hidden entities (preserving strict encapsulation).
Code: Select all
local main_entity = surface.create_entity{name = "my-roboport", position = pos}
local hidden_acc = surface.create_entity{name = "hidden-accumulator", position = pos}
local hidden_fluid = surface.create_entity{name = "hidden-fluid-box", position = pos}
-- THE PARENT registers the flat list of its dependent components
main_entity.register_dependent_tooltips({ hidden_acc, hidden_fluid })This design keeps the C++ engine 100% safe from modder errors, infinite loops, or game crashes:
1. Self-Registration Ban: The engine must explicitly throw an error or reject the call if an entity tries to register itself as a dependency:
Code: Select all
main_entity.register_dependent_tooltips({ main_entity })2. Strict Depth-1 Limitation (No Recursion): When rendering the parent's tooltip, the C++ engine iterates only through the flat array of the first level. It reads properties directly from the static prototype definition of the sub-entities. Even if a modder mistakenly creates a cyclic link at runtime, the engine will completely ignore deeper levels. Tooltip chaining is architecturally impossible.
3. Memory Safety (Dangling Pointers): If a hidden entity is destroyed (by bitters or deconstruction), the C++ engine automatically nullifies its pointer inside the parent's dependency array. The corresponding tooltip lines safely disappear without causing a game crash.
4. Mod Isolation: Multiple independent mods can attach their own hidden entities to the same parent object without cross-mod conflicts, as the parent manages them via a simple dynamic list.
