|
RapidGameFramework
Reusable Godot managers for data-driven small games
|
I
This page documents the framework-facing APIs that new games should compose. Implementation lives under scripts/systems/.
The systems are intentionally small and dictionary-oriented. A game scene should load game-specific JSON, compose the managers it needs, and save each manager through saveManager.collect_manager_state() rather than baking one game's rules into the framework layer.
The framework currently supports four playable demos:
The next seeded direction is a platformer test room. New games should first compose existing managers, then extract any repeated scene logic back into scripts/systems/.
Physics foundations are now seeded as a data-oriented layer. They are intended for lightweight deterministic gameplay, reusable tests, projectiles, arcade motion, platformer helpers, and board-piece motion, while Godot's built-in physics can still be used for more complex scene-native simulations.
eventManager.load_events(event_pack_dir) loads every JSON pack in a directory.eventManager.load_event_pack_data(pack_id, data) indexes an already-parsed event-pack array, useful when scenes or tools load catalogs through dataCacheManager.eventManager.get_events(filters) returns events matching pack, rarity, or repeatable filters.eventManager.next_event(filters) draws and stores the next weighted event.eventManager.draw_events(count, filters, allow_duplicates) supports card-pack style event draws.eventManager.set_current_event_by_id(event_id) restores saved current event state.Use this for decision games, random encounters, card offers, prompts, or other weighted content.
resourceManager.load(resource_file_path) loads resource definitions and starting values.resourceManager.load_resource_data(data) configures resources from an already-parsed dictionary while preserving the same clamping/order behavior.resourceManager.apply_effects(effects) applies clamped resource changes.resourceManager.can_apply_effects(effects) checks whether a choice is legal.resourceManager.get_effect_indicators(effects, ordered) returns HUD-ready icons, paths, marks, and values.resourceManager.get_state() and resourceManager.apply_state(state) persist resources.sprite_id and legacy icon_path icons through spriteManager, including scaled texture caching for compact HUD bars.Use this for pools, economies, settlement resources, character meters, faction favor, or any bounded numeric state.
Card, Shop, Inventory, and Battle System
Use this group for deck builders, creature collectors, tactical card games, roguelike reward loops, shop-driven games, and any game that needs an owned-object inventory.
cardManager.load_cards(file_path) loads JSON card definitions.cardManager.load_card_data(data) indexes already-loaded card catalog data, which lets scenes pair card catalogs with dataCacheManager.cardManager.get_card(id) returns one base card definition.cardManager.get_all_cards() returns the full card library.cardManager.get_random_card_by_rarity(rarity) supports pack rolls and reward tables.cardModifierManager.roll_card_instance(base_card) creates an owned card with a uid, modifiers, level, and locked state.cardModifierManager.apply_instance(base_card, instance) returns a gameplay-ready card with modifier and merge bonuses applied.cardModifierManager.preview_upgrade(base_card, instance, material) previews merge results before consuming a card.cardModifierManager.get_max_level(base_card) and can_upgrade(...) enforce rarity-based merge limits.cardInventoryAdapter.configure(card_manager, modifier_manager, options) prepares a reusable adapter for owned-card lists.cardInventoryAdapter.filter_instances(...), sort_instances(...), and table_records(...) convert owned card instances into inventoryList rows with common rarity ordering, duplicate/upgraded/locked filters, selected state, stat highlighting, and sell-value cells. Games provide icon callbacks while the adapter owns reusable card list behavior.packManager.load_packs(file_path) loads booster pack definitions.packManager.load_pack_data(data) indexes already-loaded pack catalog data.packManager.open_pack(pack_id, card_manager) rolls base cards from rarity weights.shopManager.generate_singles(card_manager, count) creates a singles market.shopManager.get_single_price(card) applies the reusable singles markup.shopManager.buy_pack(...), buy_single(...), and sell_card(...) coordinate economy and inventory.shopManager.buy_item(...) and sell_item(...) support generic stackable item shops.economyManager.set_money(...), add_money(...), spend(...), and can_spend(...) provide a reusable single-currency wallet.inventoryManager.add_card_instance(instance) stores a concrete owned card.inventoryManager.get_instances() returns owned card instances for UI lists.inventoryManager.set_locked(uid, locked) protects important cards.inventoryManager.upgrade_instance(uid, consumed_count, max_level) merges cards by adding consumed card levels without exceeding the cap.inventoryManager.get_best_merge_material(...) finds a safe unlocked merge material.inventoryManager.count_unlocked_merge_material(...) supports merge buttons and validation.inventoryManager.add_item(...), remove_item(...), and get_items() support generic stackable inventories.inventoryManager.add_to_category(category, id, amount), remove_from_category(...), get_category(...), and get_category_count(...) support reusable categorized inventories for resources, forms, modifiers, consumables, equipment, and crafting parts.inventoryManager.add_to_stack(stacks, id, amount) mutates any stack dictionary with signed add/remove behavior, erasing zero-count entries.inventoryManager.positive_stack_ids(stacks), stack_rows(stacks, formatter), and stack_lines(stacks, formatter) provide stable sorted data for category panels, logs, and summary screens.inventoryCategoryPanel.create(parent, options) and render(categories, options) render reusable framed category panels for stack inventories such as Resources, Forms, Modifiers, Consumables, and Items.collectionManager.mark_discovered(...) and mark_inventory_discovered(...) track cards ever seen.collectionManager.get_card_status(card_id, inventory_manager) returns unknown, discovered, or owned for collection screens.battleManager.start_battle(player_card, opponent_card, reward, starting_opponent_hp) starts turn-based combat.battleManager.take_turn() resolves one dice-driven combat turn.battleManager.get_state() returns UI-ready combat state.battleSequenceManager.select_mode(mode) and start_sequence(mode) support standard battles and raids.battleSequenceManager.tune_opponent(card) scales opponents across raid steps.battleSequenceManager.get_reward_multiplier() lets scenes scale payouts by battle type.rewardManager.roll_id(table, fallback_ids) rolls {id, weight} reward tables and falls back to a random known id when the table is missing or empty.saveManager.save_state(state) and load_state() persist manager dictionaries.saveManager.queue_save_state(state, delay), step_pending_save(delta), flush_pending_save(), and has_pending_save() provide a reusable delayed save path for fast gameplay events. Queue non-critical mid-run state, step it from _process, and flush on match completion, menu exit, or scene teardown.saveManager.collect_manager_state(managers) and apply_manager_state(managers, state) reduce save boilerplate when composing managers.saveManager.use_profile(profile_id) switches the active save to a named profile path.saveManager.get_profile_summary(profile_id) reads lightweight save-slot metadata for menu display.saveManager.register_migration(version, callable) upgrades older save dictionaries in order.saveManager/game_save_migrations.gd shows concrete migration registration examples for the current games.tests/fixtures/saves/*.cfg should be expanded before every future save-shape change so migrations have known old files to load.State, Scene, and Settings System
sceneManager centralizes scene transitions.gameState stores cross-scene transient intent such as new-game requests, selected save slots, save-path overrides, and saved-game availability.guidancePanel.create(parent, options) and render(panel, title, text) provide reusable data-driven onboarding/objective panels for alpha guidance, tutorial hints, lab pages, and active-run screens.objectivePanel.create(parent, options) and render(objectives, options) render required/optional objective rows with status marks, active state, detail text, and reward hints for quests, runs, and tutorials.arcade_system match_system
Use this group for Pong, breakout variants, duel arenas, sports minigames, and small score-driven arcade games.
arcadePhysicsManager.create_ball(...) and create_paddle(...) create saveable state dictionaries.arcadePhysicsManager.step_ball(ball, delta, playfield) advances a ball, stores previous_position, and bounces from top/bottom bounds.arcadePhysicsManager.step_balls(balls, delta, playfield, post_step) advances multiball arrays while preserving order. The optional callback lets a game clamp, decorate, or normalize each ball after the shared bounds step.arcadePhysicsManager.step_ball_group(primary_ball, extra_balls, delta,
playfield, post_step) advances a primary ball plus active multiballs through one reusable call. Callbacks receive index -1 for the primary ball and 0..n for extras.arcadePhysicsManager.collect_scoring_balls(balls, playfield) separates scored multiballs from balls still in play and returns { scoring_sides, active_balls }.arcadePhysicsManager.collect_ball_group_scoring(primary_ball, extra_balls,
playfield) collects all scoring sides and promotes the first remaining extra ball to primary when the original primary scores, so multiball rounds continue without game scenes reimplementing this bookkeeping.arcadePhysicsManager.move_paddle(...) applies directional input while clamping to the playfield.arcadePhysicsManager.ball_intersects_paddle(...), ball_swept_intersects_paddle(...), and bounce_from_paddle(...) keep collision response reusable. Use the swept check alongside overlap checks for fast arcade balls so browser frame spikes do not tunnel through paddles.arcadePhysicsManager.create_projectile(...), create_horizontal_projectile(...), step_projectile(...), step_projectiles(...), projectile_intersects_paddle(...), and projectile_out_of_bounds(...) provide reusable arcade projectile state/motion/collision helpers. step_projectiles(...) partitions a batch into active projectiles, target hits, and out-of-bounds rows so game scenes only handle hit consequences.arcadePhysicsManager.create_split_ball(source_ball, options) creates a slower inherited ball state for split/multiball effects.arcadePhysicsManager.get_score_side(...) reports which player scored when a ball leaves the playfield.matchManager.configure(players, score_to_win) sets up player ids and target score.matchManager.start(), pause(), and tick(delta) track active match timing.matchManager.add_score(player_id, amount) updates score and marks the winner when the target is reached.matchManager.get_state() and apply_state(state) make match progress saveable.powerupInventoryManager.configure(owner_ids, slots) creates fixed power-up slots for each player or AI owner.powerupInventoryManager.add_powerup(owner_id, powerup_id) fills the first empty slot without reordering existing slots.powerupInventoryManager.consume_slot(owner_id, slot) uses exactly one slot and leaves the other slots in place for mobile-friendly hotkeys.powerupInventoryManager.first_filled_slot(owner_id) and has_powerups(owner_id) support AI-controlled held power-ups.powerupInventoryManager.get_state() and apply_state(state) make held power-up inventories saveable.arcadePowerupManager.load_definitions(file_path, options) loads JSON rows like data/games/arcade_pong/powerups.json.arcadePowerupManager.configure(definitions, options) sets reusable field power-up ids, labels, colors, effect metadata, and spawn tuning.arcadePowerupManager.update(delta, playfield, collectors, options) advances field pickups and returns {spawned, collected, expired} event arrays.arcadePowerupManager.spawn(playfield, powerup_id), get_active(), set_active(rows), clear(), get_state(), and apply_state(state) let arcade/action scenes draw, save, and reset field pickups without owning the lifecycle details.arcadePowerupManager.get_definition(id), get_label(id), get_color(id), and get_powerup_ids() expose data-driven presentation/effect metadata.arcadePowerupManager.normalize_effect(id, effect, context) and apply_effects(id, context, handlers) turn JSON effect rows into stable operations and dispatch generic callbacks such as timers, ball speed changes, reversals, labels, plus a custom_effect fallback for scene-specific actions.data/games/arcade_pong/paddle_colors.json for match-start paddle color choices. The player selects one of six standard colors; the CPU rolls a random color whose cpu_bonus applies one small stat modifier such as paddle speed, bounce speed, tracking, power-up timing, curve, or recovery.data/games/arcade_pong/ball_skins.json and map to stable sprite ids in data/games/arcade_pong/sprites.json. Current skins include plain white, soccer, world-cup-style, neon core, and sunset variants.spriteRenderManager.draw_directional_sprite(..., options) accepts rotation_offset in addition to velocity-facing rotation. Arcade Pong uses this to make ball skins visually roll while still pointing single-direction sprites along the current travel vector.build_profile_system
buildInfo.load_info("res://data/build_info.json") loads lightweight build metadata for player-visible menus through dataCacheManager.buildInfo.get_label() returns date-style labels such as Build 20260729. Local/editor runs resolve this from the current system date so stale metadata does not linger during playtests. CI/export builds write the packaging date into data/build_info.json. This is intentionally not semantic versioning; production versioning can be added later when release packaging matures.buildInfo.get_launch_scene() returns the scene the shared splash screen should open after showing the RGF and Godot screens. CI writes this from each build profile so the all-demos build launches the main menu while single-game builds launch their specific game.buildInfo.get_cache_stats() exposes build metadata cache hits/misses for smoke tests and optimization audits.Alchemy System Codex System Deck Rotation System Grid Combat System Grid World System Targeting System Battlefield Generator
Use this group for Project Alchemy, action RPGs, deck builders, discovery games, research journals, and real-time card combat.
alchemyManager.load_definitions(file_path) loads elements and compounds.alchemyManager.load_reactions(file_path) loads normalized reaction recipes.alchemyManager.load_definition_data(data) and load_reaction_data(data) load already-cached definitions/reactions while preserving the path-based convenience APIs.alchemyManager.get_reactions() returns reaction definitions for JSON-driven lab screens and validation tools.alchemyManager.attempt_reaction(inputs, catalysts) returns success/failure metadata without requiring callers to know input order.alchemyGraphManager.load_graph(definitions_path, reactions_path) loads reaction data for validation tools.alchemyGraphManager.load_graph_data(definition_data, reaction_data) validates in-memory graph fixtures and editor data without writing temporary files.alchemyGraphManager.get_cache_stats() exposes cached JSON loader diagnostics for file-based graph loads.alchemyGraphManager.validate_graph(starter_ids) reports missing inputs, missing results, duplicate reaction ids, and unreachable results.alchemyGraphManager.get_reachable_results(starter_ids) resolves multi-step discovery chains from starter elements.alchemyGraphManager.get_dead_end_results(starter_ids) helps authors identify compounds that do not feed future reactions.researchLabManager.attempt_inventory_reaction(alchemy_manager, inventory, inputs) resolves two-input research attempts using real inventory counts.researchLabManager.apply_inventory_delta(inventory, result) applies explicit consume/gain deltas after successful or failed research.codexManager.record_discovery(reaction) stores permanent discovery entries.codexManager.record_failure(attempt) stores failed reactions so experiments still teach the player.codexManager.get_reaction_history(filters) returns recent successful/failed attempts for research timelines.codexManager.get_collection_entries(filters) returns reusable discovery/failure/note rows for searchable codex screens.deckRotationManager.configure(cards, active_slot_count, pinned_cards, options) builds active combat slots. Pass {"random_draw": true} when used cards should be replaced by a random remaining card.deckRotationManager.use_slot(slot) consumes a card and immediately rotates in the next card unless the slot is pinned.spellLoadoutManager.configure(unlocked_ids, equipped_ids, options) owns saveable unlocked/equipped spell ids for games with active combat loadouts.spellLoadoutManager.add_unlocks(unlock_ids, auto_equip) adds discoveries and can fill open active slots automatically.spellLoadoutManager.equip(id), unequip(id), and toggle(id) enforce slot limits and minimum equipped counts outside scene code.combatCardFactory.load_catalog(file_path) loads JSON card catalogs with manifestations and modifiers.combatCardFactory.load_definitions(file_path) loads element/compound definitions for composed cards.combatCardFactory.load_catalog_data(data) and load_definition_data(data) let larger Project Alchemy catalogs share cached reads with alchemy and graph validation systems.combatCardFactory.get_compound_definition(id) returns a loaded element/compound definition for lab UIs and validation.combatCardFactory.compose_card(compound, manifestation_id, modifier_ids) creates a save-friendly combat card from data-driven composition rules.combatCardFactory.preview_composition(...) marks composed cards as previews for lab/card-creation screens.combatCardFactory.register_card(card) adds a composed or generated card to the live catalog so normal deck/loadout systems can use it.combatCardFactory.unregister_card(card_id) removes a runtime-composed card when it is deconstructed or otherwise leaves the live catalog.combatCardFactory.get_composition_issues(compound_ids) reports missing compound ids and incomplete composition metadata.combatCardFactory.get_cards_for_reaction(reaction) resolves card ids unlocked by successful reactions.combatCardFactory.get_cards_for_unlocks(unlocked_ids, fallback_ids) builds starter/discovery decks without scene-specific lookup code.targetingManager.get_target_cells(card, origin, direction, bounds, blocked_cells, config, primary_target) returns affected grid cells for projectile, beam, lob, homing, raw element one-tile, and AOE abilities.targetingManager.get_targeting_summary(card, config) returns mode/range metadata for UI labels such as Projectile | 5 squares ahead or AOE | radius 2.targetingManager.format_card_summary(card, config) formats compact energy/damage/targeting text for reusable action buttons.combatCardSlotPanel.create_button(slot_index, card, state, targeting_manager) renders active spell/card slots with cost, damage, mode/range, cooldown, and selected/aiming state while preserving normal Button behavior.targetingManager.get_attack_trace_cells(actions, bounds, blocked_cells, config) turns combat action logs into player/enemy highlight cells.gridWorldManager.configure(world_config, tile_size) loads a grid world dictionary.gridWorldManager.get_bounds(), get_blocked_cells(), and get_movement_blocked_cells(occupied_cells) expose reusable movement/camera inputs.gridWorldManager.collect_material_at(cell, collected_nodes) returns pickup deltas without mutating caller save state.gridWorldManager.resolve_spell_interactions(card, affected_cells, activated_interactions) activates data-defined environmental spell targets.gridViewportManager.world_draw_rect(panel_size, options) returns a centered square playfield inside a wide or tall panel.gridViewportManager.camera_rect(player_position, tile_size, camera_config) keeps grid cameras square and stable while preserving the half-cell context used by Project Alchemy's visible field.gridViewportManager.visible_world_rect(camera_rect, world_bounds), world_point_to_panel(...), and cell_rect_to_panel(...) provide reusable clipping and coordinate transforms for node-backed or custom-drawn boards.gridViewportManager.cell_in_visible_object_range(...) lets scenes draw the known terrain radius while hiding enemies/items beyond the configured reveal range.gridViewportManager.grid_line_ranges(...) exposes clamped line ranges so half-cell edges do not draw stray grid lines outside the playable field.battlefieldGenerator.generate(prototype, options) creates a generated world/enemy dictionary from a prototype, optional seed, layout tuning, and mode options such as arena or split-side battle.adventureManager.load_adventure(file_path) and load_adventures(file_paths) load authored multi-level adventure JSON files.adventureManager.load_adventure_data(data) and load_adventures_data(rows) load already-cached adventure dictionaries for data-heavy menu/run startup.gridCombatManager.configure(enemies) loads saveable tactical-grid enemy dictionaries.gridCombatManager.load_archetypes(file_path) loads reusable enemy defaults from JSON.gridCombatManager.load_archetype_data(data) loads already-cached enemy archetype defaults.gridCombatManager.resolve_card(card, affected_cells, origin_cell, direction) applies card damage to enemies occupying affected cells and returns hit metadata.gridCombatManager.collect_defeat_rewards(hits) resolves defeated-enemy drops from hit records.gridCombatManager.take_enemy_turn(player_cell, bounds, blocked_cells, config) runs simple chase, ranged, and guard enemy roles with per-archetype cooldown timing, then returns player damage/action metadata.gridCombatManager.step(delta), get_active_projectiles(), and get_floating_numbers() expose short-lived combat feedback state for scene rendering.gridCombatManager.get_summary() exposes combat counts for HUDs, save-slot summaries, and test assertions.topDownController2D.create_state(position, config) creates saveable action movement state.topDownController2D.step(state, input, delta, bounds, obstacles, config) advances eight-direction movement with rectangular obstacle collision.topDownController2D.step_grid(state, direction, bounds, blocked_cells, config) advances one cardinal grid tile for tactical combat rooms.topDownController2D.trace_grid_line(origin, direction, max_range, bounds, blocked_cells, config) returns projectile/beam target cells until blocked.onscreenControlManager.create_overlay({"layout": "dpad", ...}) creates reusable four-direction touch controls for top-down games.action_effect_system
Use this group for data-driven spells, cards, power-ups, consumables, enemy abilities, environmental tools, and future RPG/platformer actions.
actionEffectManager.normalize_action(action) converts one action dictionary into a stable shape with id, name, kind, costs, cooldown, targeting, and effects.actionEffectManager.load_actions(path) loads cached action catalogs from JSON, while load_action_data(data) accepts already-parsed dictionaries.actionEffectManager.can_use_action(action, pools, cooldowns) checks generic resource-style costs and cooldown state before a game tries to execute an action.actionEffectManager.use_action(action, pools, cooldowns, context) spends costs, starts cooldowns, and resolves effect dictionaries through optional callbacks keyed by effect type, such as damage, heal, resource, currency, status, spawn, or custom.actionEffectManager.tick_cooldowns(cooldowns, delta) returns a save-friendly cooldown dictionary with expired entries removed.Current migration target: Project Alchemy spells and Arcade Pong power-ups should move toward this action shape first, followed by Card Game battle actions and future platformer/RPG abilities.
Example action JSON:
run_system
Use this group for bounded gameplay sessions: Project Alchemy field tests and Adventures, Arcade Pong matches, Emoji Card Collector fights/raids, Reflecting Pool runs, platformer levels, roguelike floors, and board-game rounds.
runManager.create_run(options) creates a save-friendly active run dictionary with game id, mode, seed, attempt, objectives, counters, collected run loot, rewards, result metadata, and timestamps.runManager.set_objective_complete(run_state, objective_id, complete) updates objective state without mutating the caller's dictionary.runManager.required_objectives_complete(run_state) returns whether all required completion gates are satisfied.runManager.add_collected(run_state, counts) and increment_counter(run_state, key, amount) track run-only pickups and summary counters before rewards are committed.runManager.complete_run(run_state, rewards, result, options), fail_run(...), and abandon_run(...) return terminal run states for result screens, rollback logic, stats, and save data.runManager.retry_run(run_state, options) starts a fresh active attempt while preserving mode, seed, objectives, and metadata unless overridden.runManager.get_summary(run_state) and format_result_line(run_state) build compact result/save-slot data for reusable results screens.Current rollout examples: Project Alchemy active fields and Platformer Demo level attempts now use runManager-style active/terminal state. Follow-up migrations should target Card Game battle/raid results, Pong match summaries, and Reflecting Pool run-over summaries.
results_screen_system interaction_trigger_system condition_system unlock_system data_validation_system
Use these groups for reusable post-run screens and data-driven interaction events, progression gates, and content validation.
resultsScreenController.create(summary, actions, options) builds a reusable result panel from a runManager summary with counters, collected loot, rewards, objective progress, message text, and action buttons.resultsScreenController.create_popup(owner, summary, actions, options) wraps the result panel in a popup for battle, match, level, and run completion flows. Pass show_sections: false for compact match/result modals that only need a title, message, and actions.resultsScreenController.show_popup(owner, summary, actions, options) is the preferred one-call path for normal result dialogs. It creates, sizes, centers, themes, applies text scale, and focuses the popup using the supplied layout_manager, settings_manager, ui_flow_manager, and focus options.resultsScreenController.popup_centered(...) and apply_theme_and_focus(...) provide the common popup polish layer for sizing, theme, text scale, and focus. apply_theme_and_focus accepts focus_node or focus_labels options so controller/default focus can land on actions such as Reset, Continue, or Medium.DetailsScroll/Details while action buttons remain fixed. Use fit_popup_to_viewport(...) or popup_centered(...) for match-complete, reward, retry, and failure dialogs so content cannot extend beyond phone/browser viewports.free_on_action: true only for one-shot popups that should be destroyed after an action.interactionTriggerManager.configure(triggers, state) loads trigger dictionaries and restored once-only fired state.interactionTriggerManager.evaluate(event, context) returns fired trigger results whose conditions match the supplied context.spell.element, nested all/any, at_least, at_most, contains, and in checks.interactionTriggerManager.get_state() and apply_state(state) persist fired trigger state for save files and adventure progression.conditionEvaluator.matches(conditions, context) is the shared condition implementation used by trigger/tutorial-style systems. It supports dotted keys, all, any, not, equals, not_equals, at_least, at_most, greater_than, less_than, contains, in, and exists.conditionEvaluator.value_at(context, dotted_key, default) returns nested values for data-driven rules and debug UIs.unlockManager.load_rules(file_path) loads JSON unlock rows from {"unlocks": [...]}.unlockManager.configure(rules, state) accepts in-memory unlock rows and restored save state.unlockManager.evaluate(context) returns newly unlocked targets whose conditions match the supplied context.unlockManager.is_unlocked(target_id), set_unlocked(target_id, value), get_unlocks(filters), get_state(), and apply_state(state) provide a reusable save-friendly unlock surface for modes, adventures, cards, items, features, tutorials, and achievements.dataValidationManager.load_json(file_path) loads JSON and returns {ok, data, issues}.dataValidationManager.validate_records(records, schema, path) checks required fields, duplicate ids, invalid records, and simple references.dataValidationManager.validate_section(data, section, schema) validates one named array section from a JSON object.dataValidationManager.validate_bundle(bundle) validates multiple file/section/schema checks at once for smoke tests, CI, and editor tools.dataValidationManager.summarize(issues) returns stable issue counts by type.dataCacheManager.load_json(path) caches parsed JSON dictionaries and returns duplicated data so callers cannot mutate the cached source.dataCacheManager.load_array(path, key, fallback) returns one top-level array from a cached JSON dictionary. Use this for stable runtime catalogs such as cosmetics, match presets, tuning rows, and lightweight UI data.dataCacheManager.load_root_array(path, fallback) returns a duplicated top-level JSON array, which supports event-pack files and other list-first catalogs.dataCacheManager.invalidate(path), clear(), and get_cache_state() support editor saves, smoke checks, and performance reports.dataCacheManager.get_cache_stats() exposes numeric entry, hit, and miss diagnostics compatible with performanceMonitor.capture_provider_stats(...). Current examples: Arcade Pong uses it for cosmetics, AI, power-ups, settings, and achievements; Platformer Demo uses it for movement settings and level index startup data. The convenience file loaders on gameSettingsPanel, aiController, arcadePowerupManager, cardManager, packManager, combatCardFactory, alchemyManager, gridCombatManager, adventureManager, menuManager, inputManager, levelManager, paletteManager, tutorialManager, unlockManager, eventManager, resourceManager, actionEffectManager, and alchemyGraphManager also use dataCacheManager internally, so direct manager use still follows the cached runtime path.Current migration target: Arcade Pong match results now use resultsScreenController. Project Alchemy field victory/defeat/retry popups should continue moving toward the same controller, and Adventure/field exits, bramble gates, switches, and scripted events should move toward interactionTriggerManager.
Example data files:
data/games/arcade_pong/ai_profiles.jsondata/games/arcade_pong/settings.jsondata/games/project_alchemy/settings.jsondata/games/project_alchemy/tutorial.jsondata/games/project_alchemy/unlocks.jsonArcade AI profiles now support Pong-style paddle opponents through aiController.choose_pong_paddle_decision(paddle, balls, playfield, profile) and aiController.should_use_pong_powerup(actor, ball, playfield, profile). Profiles may tune reaction cadence, prediction lead, tracking error, deadzones, and power-up timing without editing the game scene.
entity_system
Use this group for save-friendly state shared by renderers, combat systems, physics helpers, level builders, editors, and game-specific scene code. Entity dictionaries can represent players, enemies, projectiles, pickups, hazards, interactables, board pieces, cards-as-combatants, or temporary field markers.
entityManager.configure(entities) loads an ordered array of entity dictionaries.entityManager.create_entity(data) normalizes ids, kind/type, team, owner, tags, state, alive/hp/max_hp, grid cell, position, velocity, facing, statuses, and metadata.entityManager.get_entities(filters) returns copied entities filtered by kind, type, team, owner, alive, or required tags.entityManager.set_cell(id, cell) and set_position(id, position) update movement state without binding to a specific physics or grid implementation.entityManager.get_entities_at_cell(cell, filters) and get_occupied_cells(filters) provide movement/collision inputs for grid games.entityManager.add_status(id, status) and tick_statuses(delta) manage timed status dictionaries.entityManager.apply_damage(id, amount, options) and heal(id, amount) offer minimal shared health helpers for games that do not need a deeper combat rules engine.entityManager.get_state() and apply_state(state) round-trip stable saveable entity state.Current migration target: Project Alchemy field actors/materials/interactions should move first, then Pong balls/paddles/power-ups/projectiles, then Card Game combatants and future platformer level actors.
ai_system entity_system
Use these groups to convert JSON content into entities and choose reusable AI actions from those entities.
entityDefinitionAdapter.from_world(world, options) converts common sections such as enemies, actors, pickups, material_nodes, hazards, interactables, spell_interactions, and exit into entity dictionaries.entityDefinitionAdapter.from_placements(placements, default_kind, options) converts editor/level placement rows into entities.entityDefinitionAdapter.to_placement(entity) exports a simple placement row for tools and editors.aiController.nearest_target(actor, targets, options) returns the nearest target entity/dictionary.aiController.configure_profiles(profiles), load_profiles(file_path), and get_profile(profile_id, overrides) let games store behavior presets in JSON and override playtest values at runtime.aiController.choose_paddle_action(paddle, balls, profile) returns a vertical move-axis action for Pong/breakout-style opponents.aiController.choose_grid_action(actor, targets, world, profile) returns attack, move, or wait actions for chase, flee, patrol, and wander tactical-grid behaviors.aiController.should_use_action(actor, context, profile) centralizes simple timing/chance gates for AI power-ups, abilities, or items.Current migration target: Arcade Pong AI should move to aiController first, then Project Alchemy aggro/wander logic can follow once entity migration starts.
animation_state_system entity_render_system
Use these groups to turn save-friendly entity dictionaries into sprite ids and visible playfield nodes without hard-coding every game scene.
animationStateManager.direction_name(value, fallback) resolves strings, vectors, arrays, or dictionaries into up, down, left, or right.animationStateManager.get_state_key(entity, options) returns directional state keys such as idle_down, move_left, or cast_up.animationStateManager.get_sprite_candidates(entity, options) returns stable sprite ids from most-specific to broad fallback, using explicit sprite_id, animation_id, archetype/id/kind, state, and direction.animationStateManager.required_sprite_ids(base_id, states, directions) generates sprite manifest checklists for actors, enemies, projectiles, and future effects.entityRenderer.render(parent, entities, sprite_manager, options) draws entities with sprite candidates, fallback colored rectangles, pooled nodes, and optional health bars from resourceBar.entityRenderer.entity_rect(entity, options) exposes shared cell/position to draw-rect conversion for custom overlays and hit previews.Current migration target: Project Alchemy's active field renderer should move toward entity dictionaries plus entityRenderer, then Arcade Pong balls, paddles, rockets, and field power-ups.
Use this for per-game tuning controls. Global display/audio/theme/text settings belong in standard_settings_popup; gameplay-specific sliders and toggles belong here.
gameSettingsPanel.create(definition, state, changed_callback) builds rows from data definitions using checkbox, slider, number, option, and text controls. Definitions may include row_name, control_name, show_value, and format so generated panels keep stable node paths and readable slider labels.gameSettingsPanel.load_definition(file_path) loads a settings definition from JSON.gameSettingsPanel.get_default_state(definition) extracts default values for save initialization and reset buttons.gameSettingsPanel.read_state(controls) returns a save-friendly settings dictionary.gameSettingsPanel.apply_state(controls, state) applies saved or preset values to an existing generated panel.Current example: Arcade Pong renders its Custom pre-match tuning panel from data/games/arcade_pong/settings.json. Project Alchemy and Platformer movement settings remain good next migrations.
tutorial_system
Use this for first-run checklists, contextual onboarding, tutorial prompts, mode unlock guidance, and playtest-friendly next-step hints.
tutorialManager.configure(steps, state) loads data-driven tutorial steps.tutorialManager.load_steps(file_path) loads steps from JSON.tutorialManager.update_from_context(context) marks steps complete when their conditions match the supplied context.tutorialManager.get_next_step(context) returns the next incomplete step.tutorialManager.get_checklist(context) returns rows with complete flags for UI panels.tutorialManager.get_state() and apply_state(state) save and restore completed step state.Tutorial conditions support direct equality, dotted keys such as spells.created, nested all/any, at_least, and contains.
platformer_system
Use this group for side-scrollers, precision platformers, metroidvania rooms, arcade obstacle courses, and grid-authored level prototypes.
characterController2D.create_state() creates data-only movement state.characterController2D.step(state, input, delta, on_floor, config) advances run, jump, gravity, coyote time, jump buffer, and dash state.levelManager.load_level(file_path) loads JSON grid levels from data/levels/, including both legacy tiles/spawns and schema v1 layers/objects.levelManager.get_tile_dimensions() and get_pixel_dimensions() expose cached level bounds.levelManager.get_rects_by_type("solid") and get_rects_by_type("hazard") convert tile legends into cached collision rectangles.levelManager.get_spawns(kind) returns reusable player, enemy, pickup, and checkpoint spawn metadata.levelManager.get_layers(), get_objects(kind), get_exits(), and get_triggers(type) expose layered map/editor data for platformers and tactical rooms.levelManager.get_objects_in_rect(kind, world_rect, options), object_world_rect(object, fallback_size, offset), and rect_intersects_world_rect(rect, world_rect, margin) provide shared culling helpers for sprite-backed exits, enemies, pickups, NPCs, and editor previews in large side-scrolling or room-based maps.levelManager.get_tile_records(options) returns renderer/editor-friendly tile records with cell, position, layer, tile id, and legend metadata. Pass world_rect plus optional margin to retrieve only records visible to a camera or editor viewport. The full tile record arrays are cached per include_empty mode so larger platformer/metroidvania levels do not rebuild every tile row during hot draw/pickup loops.levelManager.get_cache_stats() and clear_caches() expose derived-cache diagnostics and reset hooks for performance baselines, editor reloads, and tests that need to prove visible tile slices are reusing cached records.levelManager.validate_level(data) returns issue dictionaries for editor, CI, and smoke-test validation.levelManager.make_template_level(id, width, height) generates starter schema v1 level JSON.levelManager.get_background() returns background/parallax metadata for scene rendering.levelEditorManager.create_history(max_entries) creates reusable undo/redo state for editor tools.levelEditorManager.set_tile(...), remove_objects_at_cell(...), fill_tiles(...), and pick_tile(...) operate on level dictionaries without depending on a specific scene tree.levelEditorManager.palette_visibility(tool) and input_badges(controller_active) keep maker-style editor UI consistent across platformer, tactical, metroidvania, and future room editors.customLevelLibrary.game_dir(game_id) resolves the repo-local custom level folder at data/games/<game_id>/custom_levels/.customLevelLibrary.list_levels(game_types) discovers editor-authored level JSON for menus and runtime loaders without editing shipped level indexes.customLevelLibrary.save_level(level_data, game_id) writes schema v1 level JSON into the selected game's custom-level folder.scenes/level_editor.tscn is the shared Godot level editor for creating, painting, editing, validating, and saving schema v1 JSON levels. It supports arbitrary tile-map size, legend-backed sprite tiles, common object placement such as players, enemies, exits, checkpoints, pickups, hazards, and raw JSON edits for game-specific metadata. New templates save to data/games/<game_id>/custom_levels/. Its non-visual editing operations are backed by levelEditorManager so future editor screens inherit the same history, fill, pick, erase, and palette behavior.addons/rgf_level_tools/ is an optional Godot editor plugin that adds an RGF Levels dock to the IDE. It browses game-local custom levels, creates template JSON files, lists supported built-in levels, validates selected files through levelManager, opens selected JSON files, builds editable Godot 2D scenes from selected JSON, and exports the active RGF scene back to JSON. Built-in Platformer levels overwrite their source data/levels/ files, while Project Alchemy adventure stages round-trip back into the selected adventure JSON entry. Its separate Library tab reads data/games/<game_id>/editor_palette.json and shows game-defined tiles and objects in one shared list so authors can place common nodes without switching modes. It supports exact-cell placement from the dock and opt-in click placement in Godot's 2D viewport. Use it when level authors want Godot's normal 2D viewport, node tree, Inspector, and transform tools while preserving the framework's portable JSON source of truth. Generated .tscn scenes are authoring bridges over JSON, not a replacement runtime format.scenes/platformer_demo.tscn is the first playable platformer slice. It loads data/games/platformer_demo/levels.json, renders shared level JSON, and exercises lives, coins, hazards, enemies, exits, progress-gated level select, camera following, sprite-pack lookup, and runtime discovery of data/games/platformer_demo/custom_levels/.Current extraction target: move the prototype's level construction and collision-object spawning into a reusable platformerSceneBuilder, then move respawn/checkpoint/collectible state into save-friendly managers before platformer progress is persisted.
physics_system
Use this group when a future game needs deterministic, saveable, dictionary-based physics without tying simulation state directly to scene nodes.
physicsEngine/spatial_state.gd stores bodies, positions, velocities, shape metadata, layers, masks, and saveable state.physicsEngine/motion_integrator.gd applies forces, impulses, gravity, damping, and semi-implicit velocity integration.physicsEngine/collision_pipeline.gd provides broad-phase AABB pair detection and narrow-phase circle/AABB contacts.physicsEngine/constraint_solver.gd resolves collision response, positional correction, and distance constraints.physicsEngine/physics_queries.gd supports point, area, and ray-style queries.physicsEngine/physics_world.gd coordinates the full step: integrate, broad phase, narrow phase, collision resolution, constraints, and query access.Body dictionaries use a portable shape format:
{ "shape": { "type": "circle", "radius": 8.0 } }{ "shape": { "type": "aabb", "size": Vector2(16, 16) } }This layer is intentionally modest. Future games should add specialized helpers on top, such as platformer collision adapters, projectile pools, arcade arenas, or board-piece motion rules.
menuManager.load_menu(file_path) loads reusable JSON menu stacks for game selectors, submenus, and save-slot flows.menuManager.set_context(context) supplies state for disabled_when rules and metadata formatters.menuManager.get_render_items() returns menu items annotated with disabled and display_label.menuManager.expand_item(item) supports reusable templates such as save_slots.menuManager.go_back() supports nested menu stacks.menuManager.get_confirmation(item) exposes reusable confirmation dialog text from JSON.menuManager.get_item_sprite_id(item) resolves system menu icons from menu JSON using explicit item sprite_id, label rules, action rules, target-screen rules, and a fallback. Scenes should use this instead of guessing icons so Back, Settings, Saves, Achievements, and future system items stay consistent.Menu JSON supports reusable item fields:
disabled_when: Rules such as { "key": "has_save", "equals": false }.visible_when: Uses the same rule shape to hide items.all, any, not, greater_than, less_than, at_least, and at_most.confirm: Dialog text with title, message, confirm_label, and cancel_label.metadata_formatter: Appends simple context metadata to an item label.template: Expands reusable item templates such as save slots from context data.icons: Optional top-level mappings for actions, targets, labels, and fallback. This lets each game/menu data file assign system icons without scene-specific button logic.background_image, background_opacity, and background_overlay. The shared start menu cover-crops the image behind the menu and applies the overlay so buttons and descriptions remain readable.layoutManager.load_theme_file(file_path) loads editable theme and density presets from JSON.themes.<id> with name, description, colors, and optional typography. Common color keys include background, surface, surface_alt, panel, panel_alt, button, button_hover, button_pressed, button_disabled, border, text, muted, accent, positive, negative, and warning; additional keys can be added for game-specific custom drawing.typography and per-theme overrides under themes.<id>.typography. Supported keys include font_paths (default, title, button, mono), base_sizes (body, caption, button, title, list), scale_presets (small, medium, large), and button_height_multiplier.themes.<id>.styles.screen, styles.panel, and styles.button, each with background_image, texture_mode (stretch, tile, or keep), and opacity. layoutManager.apply_theme(...) applies panel and button style targets, while layoutManager.apply_screen_background(root, theme_id) applies the screen background target explicitly.layoutManager.get_theme_catalog() returns loaded theme ids, names, descriptions, and parsed Color palettes for tools or future theme previews.layoutManager.get_custom_draw_palette(theme_id, domain) returns semantic colors for custom _draw() content that cannot be styled like normal controls.layoutManager.make_panel_style(...), make_button_style(...), and achievement row styles are cached by theme/state/color. Use get_cache_stats() to capture stylebox count/hit/miss diagnostics and clear_style_cache() after editing theme or density data at runtime.layoutManager.create_pause_overlay(actions) builds a reusable pause overlay for resume, settings, input remaps, achievements, main menu, and quit actions.layoutManager.apply_theme(root, theme_id) applies the selected theme to common controls.layoutManager.apply_responsive_shell(root, viewport_size) standardizes margins and spacing across phone/tablet/desktop breakpoints.layoutManager.popup_size_for_viewport(viewport_size, preferred, minimum, margin), fit_popup_to_viewport(...), and popup_centered_standard(...) clamp reusable system popups to mobile-safe dimensions and fit a Content child with consistent margins.layoutManager.create_screen_shell(name, options), create_panel_shell(...), and create_scroll_shell(...) provide common page structure. options can hide bottom navigation or set header/nav heights for future game-specific shells.layoutManager.create_bottom_nav(items) builds phone-friendly navigation bars.layoutManager.create_save_slot_list(...) and create_save_slot_row(...) render generic save-slot screens.layoutManager.create_achievement_notification(...) and create_achievement_collection(achievements, options) render achievement UI with collection summaries.layoutManager.create_achievement_filter_bar(options, current_filters, callback) renders reusable filter controls for achievement views.layoutManager.create_confirmation_dialog(...) builds consistent modal content.layoutManager/pause_overlay_controller.gd binds and shows pause overlays with less scene code. Call pauseOverlayController.dispose() before freeing a scene owner when a pause submenu may have queued a deferred return/focus call.achievementScreen.show_popup(...) renders a reusable achievement popup for pause/in-game overlays, while achievementScreen.render_page(...) renders the same title, filters, scrollable collection, and Back button into normal full-screen menu content. Use the page form for main menus and mobile-first navigation where popups are too cramped.infoPanel.create_popup(owner, title, pages, options) builds reusable tabbed information popups for controls, how-to-play text, power-up reference pages, tutorial notes, and other compact game guides. Each page is a data dictionary with id, title, and lines.infoPanel.step_tab(popup, direction) and reset_tab(popup) let games bind RB/LB, PageUp/PageDown, or other shared navigation actions without rebuilding tab logic in every scene.buttonRowPool.bind(parent) and buttonRowPool.render(items, options) keep fixed rows of action buttons alive across refreshes. Use this for power-up slots, spell/action bars, HUD rows, editor toolbars, and other small button strips where labels/icons change often but the layout shape stays stable. Items support id, text, icon, disabled, style/color overrides, and a callback(index, id). get_stats()/get_cache_stats() expose create/reuse counts for optimization passes.fieldMenuController.create_popup(options, actions) builds opaque active-run menus from action dictionaries with labels, names, and callbacks.fieldMenuController.popup_centered_for_viewport(popup, viewport_size, options) opens those menus with mobile-safe maximum dimensions.statusFeedManager.push(message, now_ms, options) adds a whole transient status message, deduping exact repeats without splitting multi-sentence text.statusFeedManager.render(parent, now_ms, rect, options) renders a stacked, newest-first feed with explicit line wrapping and timed expiry. Rendered panel/text nodes are reused and hidden on expiry instead of recreated on every frame.resourceBar.render(parent, rect, state, options) renders an outlined bounded value bar with centered high-contrast shadowed text and the same green/yellow/red thresholds as the resource HUD for health, energy, timers, or cooldowns. Pass fill only when a screen intentionally needs a custom fixed color.renderNodePool.begin(parent), rect(...), sprite(...), and end() reuse Control-based draw nodes for high-frequency playfields. Use it when a scene redraws many tiles, actors, projectiles, traces, or markers every frame.projectileLifecycleManager.should_queue(card, direction, affected_cells) decides whether a data-defined tactical effect should travel before it resolves. Raw one-tile element attacks resolve immediately.projectileLifecycleManager.create_effect(...) returns a save-friendly active projectile/lob dictionary with origin, end, direction, affected cells, color, age, and duration.projectileLifecycleManager.step_effects(active_effects, delta) advances those dictionaries and returns { "active": [...], "ready": [...] } so games can resolve ready effects through their own combat/environment systems.settingsManager persists display/audio/theme/input/text-size/low-power settings and emits settings_changed.settingsManager.load_theme_options(file_path) populates the system Settings theme dropdown from the shared theme JSON, using name as the user-facing label and the JSON key as the saved theme id.settingsManager.get_theme_text_size_preset(theme_id, size_id) returns the theme typography scaled by the user's Small/Medium/Large text-size choice.settingsManager.apply_text_size(root) applies theme-derived font sizes and optional font resources to common controls while preserving per-control text_size_role and max_font_size caps for compact reusable widgets.scenes/theme_editor.tscn is a framework utility available from the main menu for creating new themes, editing core palette colors with synchronized hex fields and color pickers, previewing live swatches/sample buttons/sample text, assigning platform font dropdowns to typography roles, warning before discarding unsaved edits, and saving custom themes to user://custom_themes.json. The custom catalog is merged into Settings and layout rendering at runtime.settingsManager.set_text_size(value) persists small, medium, or large readable text presets.settingsManager.apply_text_size(root) applies text-size overrides to common UI nodes in a scene tree.settingsManager.set_theme(value) persists the selected visual theme id.settingsManager.set_low_power_mode(value) persists the shared low-power flag. Games should query settingsManager.is_low_power_mode() to reduce optional visual effects, warm caches, background animation, or field clutter while keeping gameplay-critical simulation and collision rates intact.settingsManager.load_low_power_profiles(file_path) loads reusable low-power tuning profiles from JSON. The default catalog lives at res://data/system/low_power_presets.json.settingsManager.get_low_power_profile(profile_id) returns duplicated preset data for framework modules and games. Current profile sections include audio, arcade, visuals, and cache; future games should read optional values from these profiles instead of hardcoding mobile/browser reductions.settingsManager.set_input_remaps(state) persists remap state captured by inputManager.settings_panel_controller.bind(controls) connects a settings panel to the shared manager.standard_settings_popup.create_popup(owner, options) builds an embedded framework settings popup with window size, fullscreen, theme, text size, low-power mode, master/music/SFX sliders, and mute toggles. Use this as the default system settings surface for new games instead of rebuilding sound/display controls in each scene.inputManager.load_actions(file_path) loads remappable action definitions from JSON.inputManager.get_actions() returns only user-remappable actions by default; game_pause/Start and game_settings/Select are fixed system actions and cannot be remapped.inputManager.bind_from_event(action, event) supports user-facing remap screens for keyboard, mouse, controller buttons, and controller axes.inputManager.get_state() and apply_state(state) persist and restore prompts plus keyboard/mouse/controller bindings.inputManager.get_prompt(action, device) returns UI text for input prompts.inputManager.is_pressed(action) and just_pressed(action) centralize action checks.inputManager.note_input_event(event) records whether keyboard/mouse or controller was used most recently.inputManager.is_controller_active() reports connected/recent controller use.inputManager.get_slot_label(index) returns numeric labels for keyboard/touch and controller labels such as A, X, Y, and B when a controller is active.inputManager.update_input_module(root, delta, onscreen_controls,
user_onscreen_enabled) is the preferred per-frame hook for future games. It applies right-stick scrolling to visible scroll containers and automatically hides on-screen controls while a controller is active without changing the user's touch-control preference. It avoids redundant visibility writes on steady-state frames so touch/controller switching does not churn UI nodes.onscreenControlManager.attach(parent, options) creates translucent touch overlays such as up/down paddle controls. Layout options include side, anchor, margin, button size, gap, opacity, labels, and future-facing action ids.onscreenControlManager.get_axis(negative_id, positive_id) returns a reusable movement axis from pressed touch controls.onscreenControlManager.get_state() and apply_state(state) preserve overlay visibility and layout preferences for future settings/remap screens.onscreenControlManager.get_stats() exposes overlay count, button count, queued tap count, and visibility-change diagnostics for mobile/browser smoke tests and performance audits.uiFlowManager.focus_first(root) focuses the first visible, enabled focusable control under a menu, popup, or settings panel.uiFlowManager.focus_named(root, node_name, fallback_to_first) focuses a known control by node name for stable default selection, such as a Reset button on a result screen.uiFlowManager.focus_button_by_text(root, labels, fallback_to_first) focuses a visible enabled button by label, useful when a data-driven menu wants Medium, Continue, or another specific default without knowing the node path.uiFlowManager.set_focus_mode_recursive(root, mode) applies a focus mode to an entire generated nav/control tree.uiFlowManager.show_screen(screen_id, screens, scroll) shows one screen from an id-to-Control dictionary and optionally resets its scroll container.uiFlowManager.shifted_id(current_id, ordered_ids, delta) centralizes previous/next tab navigation for controller shoulder buttons.uiFlowManager.format_local_time(unix_time) formats save-slot timestamps.uiFlowManager.set_label_text_if_changed(label, text) updates HUD/status labels only when text changes, reducing layout churn in per-frame game loops.inventoryList.create(parent, insert_index, options) builds reusable inventory tables with optional quick filters, sortable headers, scrollable rows, wide mobile scrollbars, and configurable columns.inventoryList.render(records, state) redraws records supplied by a game-specific adapter. Records can provide icons, selected state, border colors, and per-column text/color/alignment.inventoryList pools row buttons across renders and hides unused rows instead of freeing them. Use get_stats() or get_cache_stats() to capture row counts, visible rows, hidden rows, and per-pass create/reuse counts during performance work.inventoryList.set_enabled(enabled) lets active gameplay screens temporarily disable selection while preserving the table layout.inventoryCategoryPanel.create(parent, options) builds a reusable grid of framed category panels for stack-based inventories.inventoryCategoryPanel.render(categories, options) redraws category dictionaries with titles, descriptions, count rows, and empty states. Project Alchemy uses this for Resources, Forms, Modifiers, Consumables, and Items.collectionManager.discovered_count() and is_collection_complete(all_ids) support collection-progress achievements and reusable silhouette/completion screens.statsManager.load_achievements(file_path) loads achievement/unlock rules from game data through dataCacheManager.statsManager.increment(...), record_high(...), record_low(...), and set_flag(...) update reusable stat primitives.statsManager.record_events(events, evaluate := true) applies a batch of reusable stat events (counter, set_counter, record_high, record_low, or flag) and can immediately return newly unlocked achievements. Prefer this for gameplay hooks that update several related stats at once.statsManager.register_derived_stat(...) creates calculated stats from counters and records.statsManager.add_run_summary(...) keeps rolling run history for menus and save-slot summaries.statsManager.evaluate_achievements() returns newly unlocked achievements for notifications.statsManager.get_achievement_collection(filters) returns UI-ready achievement rows filtered by game, category, locked/unlocked state, or arrays of accepted game/category values.statsManager.get_achievement_filter_options() returns available games, categories, and states for reusable filter bars.statsManager.get_achievement_summary() returns counts by game and category for content audits, menus, and roadmap planning.statsManager.load_achievement_data(data, game_id) registers already-loaded achievement dictionaries, allowing games to pair achievement catalogs with dataCacheManager while preserving the existing load_achievements(path) convenience API.statsManager.get_cache_stats() exposes cached achievement catalog loader diagnostics for smoke tests and performance reports.statsPanel.create_popup(parent, title, rows, options), update_popup(...), and popup_centered(...) render compact stats popups from strings or label/value row dictionaries. The shared centering helper keeps Back buttons fixed and stat rows in a scrollable middle region while clamping to small mobile/browser viewports. Arcade Pong and Card Game use this for their in-game stats panels.achievementNotifier.configure(layout_manager, settings_manager, options) and achievementNotifier.show(parent, stats_manager, ids) queue unlock notifications one at a time through the shared achievement notification UI.achievementScreen.configure(layout_manager, settings_manager, ui_flow_manager,
options) and show_popup(parent, stats_manager, filters, options) render a themed, text-scaled, focus-ready achievement collection popup from reusable statsManager.get_achievement_collection(...) filters. Reflecting Pool and Card Collector use this for their pause-menu collection overlays.achievementScreen.popup_centered(popup, viewport_size, options) applies the same mobile-safe modal clamp used by stats and result popups, while keeping the achievement collection scrollable and the Back button visible.Data-driven framework files now include:
data/menus/main_menu.json for game selection, nested menus, and save-slot templates.data/input/actions.json for remappable input actions and prompts.data/themes/default.json for editable theme colors and density presets.data/palettes/game_palettes.json for runtime sprite palette swaps.data/levels/*.json for starter grid levels used by platformer and arcade prototypes.docs/schemas/level_json.md documents the shared level JSON shape for platformer, tactical, and arcade room authoring.data/games/<game_id>/achievements.json for unlock definitions.data/games/project_alchemy/prototype.json for current player/world/material/training-target setup.data/games/project_alchemy/adventures/*.json for multi-level Adventure definitions with metadata, objectives, exits, terrain, objects, enemies, rewards, and scripted events. Current scripted event triggers include level_start and objective_complete; current event types include dialogue for field status text and codex_note for persistent codex notes.assets/sprites/<category>/<pack>.json for reusable sprite catalogs.tests/fixtures/saves/*.cfg for migration tests against older save shapes.tests/fixtures/saves/project_alchemy_adventure.cfg for the first Adventure-mode save shape with selected adventure and level progress.The current extraction backlog is focused on moving recently proven scene glue into reusable systems: card battle panels, inventory record adapters, data-driven game settings panels, full-screen achievement pages, save-slot metadata adapters, arcade power-ups, and arcade AI profiles.
audioManager loads reusable music and sound effects from res://assets/music and res://assets/sfx. Its music manifest is read through dataCacheManager, while imported audio streams stay on Godot's resource-loading path.finished signal path to reduce visible browser hitches when the next stream starts.audioManager.prepare_next_music_track() queues the next music stream on an idle player before playback switches. This two-player handoff keeps browser music transitions from doing fresh stream assignment work on the same frame the current track ends.settingsManager.is_low_power_mode() is enabled, audioManager skips the queued next-track stream and clears any already queued stream. This trades slightly less seamless transitions for lower memory and decode pressure on older phones and browser targets.audioManager.get_music_cache_state() returns lightweight diagnostics for smoke tests and performance reports.audioManager.get_cache_stats() returns numeric track, SFX, generated-SFX, player, queued-stream, low-power, playback, and manifest cache gauges for performanceMonitor provider snapshots.audioManager.dispose() stops active players, disconnects settings signals, clears loaded music/SFX stream caches, and is intended for temporary test/tool instances. Do not remove the normal application autoload mid-frame; scenes may still have deferred UI callbacks that reference it.spriteManager.load_pack(file_path) loads a JSON manifest such as res://assets/sprites/ui/ui_core.json.spriteManager.load_packs(file_paths) composes several category manifests such as UI, actors, enemies, items, world, and effects.spriteManager.get_sprite_path(sprite_id) returns a stable res:// path for UI code.spriteManager.get_texture(sprite_id) loads a sprite texture by id.spriteManager.get_scaled_texture(sprite_id, size) returns nearest-neighbor scaled textures for button icons.spriteManager.get_texture_with_fallback(sprite_ids) loads the first available texture from a preferred/fallback id list, useful while migrating from older ids such as pong_ball to directional ids such as pong_ball_right.spriteManager.rotation_for_vector(direction, base_direction) returns the radians needed to rotate a canonical-facing sprite toward movement. Arcade Pong uses pong_ball_right as the source sprite and rotates it to match ball velocity.spriteManager.get_sprite_set(set_id) returns reusable spritesheet metadata.spriteManager.get_animation_frames(sprite_id) extracts a configured sequence from spritesheet cells.spriteManager.get_animation_definition(sprite_id) exposes frame cells and animation speed, currently as frames-per-second.spriteManager.create_texture_node(sprite_id, options) returns a normal TextureRect for static sprites or a reusable animated TextureRect helper for spritesheet frame ranges.spriteManager.warm_texture_cache(sprite_ids) preloads textures and animation frames for selected ids. Omit sprite_ids to warm every loaded sprite in the current game/pack set.spriteManager.warm_texture_cache_for_profile(profile_id, options) preloads a manifest-defined warm profile containing static sprites, animations, and scaled icon entries. Warm profile entries may be strings or dictionaries such as { "id": "skin_alt", "optional": true }; pass { "skip_optional": true } for low-power/browser starts that should warm only critical gameplay art.spriteManager.clear_transient_cache(options) clears generated scene/UI texture caches while preserving decoded source images by default. Call this when leaving a game scene, editor tool, or large modal.spriteManager.trim_texture_cache(keep_sprite_ids, options) removes cached texture outputs not needed by the next screen while keeping selected ids warm. This is the preferred boundary between sub-screens inside one game.spriteManager.clear_texture_cache() clears cached direct textures, scaled textures, cropped sheet cells, and animation frame arrays.spriteManager.get_cache_stats() returns lightweight diagnostics for cache counts.spriteManager.ensure_required_sprites(config) creates placeholder definitions for required states. Configs may include directions and directional_states to generate ids such as player_idle_down, player_run_left, slime_attack_up, spell_fire_projectile_right, and non-directional states such as player_death.spriteManager.manifest_with_required_sprites(data) returns editable manifest data with missing required placeholders inserted. The sprite mapper uses this so opening a game's sprites.json reveals every required assignment that still needs art.spriteManager.record_sprite_request(sprite_id, metadata) records a stable id requested by renderer code during development.spriteRenderManager.draw_sprite(...), draw_sprite_with_fallback(...), and draw_directional_sprite(...) provide reusable CanvasItem drawing helpers for custom playfields. They use spriteManager, record requested sprite ids, draw fallbacks when art is missing, and rotate canonical sprites toward velocity or aim vectors.spriteRenderManager.reset_stats(), get_stats(), and get_cache_stats() expose lightweight draw diagnostics: draw calls, sprite hits, missing lookups, fallback rectangles, rotated draws, and manifest requests recorded. Capture these with performanceMonitor around dense playfield redraws.spriteManager.manifest_with_requested_sprites(data) returns manifest data with all renderer-requested ids added as required placeholder rows. This is the bridge from prototype rendering to a complete game-specific sprites.json.spriteManager.clear_requested_sprites() clears the runtime request ledger before a new audit/playtest pass.spriteManager.get_missing_assignments() reports required sprite ids that are still placeholders or have no texture source.spriteManager.find_by_tags(tags) supports generic selectors such as all audio or combat icons.assets/sprites/arcade/arcade_core.json.assets/sprites/world/platforms.json.data/games/<game_id>/sprites.json. Current active examples include Reflecting Pool resources/events/effects, Emoji Card Collector cards/packs, Arcade Pong playfield/power-ups, and Project Alchemy actors/effects.mirror_h: true and/or mirror_v: true to flip the rendered texture node without creating a second PNG or sheet range. This is useful for left/right variants while art is still being assigned.greyscale: true to render a sprite in greyscale without changing the source image. grayscale is accepted as a compatibility spelling.sprites.json now declares required manifest entries for the current slice: player states, every implemented enemy archetype, material/resource ids, terrain/object placeholders, and directional projectile effects for implemented elements and compounds.sprite_id and treat direct path fields such as icon_path and image_path as compatibility fallbacks.warm_profiles so games can preload scene/menu sprites from data. A warm profile can list sprites, animations, and scaled entries such as { "id": "material_fire", "size": 24 }.Project Alchemy's battlefield renderer now uses this pattern for player, enemy, material, projectile, terrain/obstacle, interaction, and exit markers. Animated sheet ranges are rendered through sprite_animation_rect.gd. Scene code should keep a fallback for missing assignments so placeholder reports can guide art work without making a playtest screen blank.
For performance, spriteManager keeps a framework-level cache:
Texture2D.get_image() for every derived output.icon_path or image_path lookups while games migrate to stable sprite_id fields.spriteManager.get_texture_for_path(path) is the shared path-texture entry point for menu backgrounds, imported one-off images, and editor previews that are not yet represented by a stable sprite_id.layoutManager also uses this path-texture entry point for theme-defined screen, panel, and button background images, keeping JSON themes editable without reintroducing direct texture loads in UI code.warm_texture_cache() after loading their sprite packs to preload the current game's art before the first heavy render.spriteManager.get_warm_profile_ids(), get_warm_profile(id), and warm_texture_cache_for_profile(id) preload static sprites, animation frames, and scaled icons from manifest data. Current game examples are Arcade Pong's match, Card Game's visible_card_shell, Reflecting Pool's hud_and_pool, Platformer's level_core, and Project Alchemy's field_slice.trim_texture_cache(keep_sprite_ids) and clear_transient_cache() are the standard scene-boundary APIs. They drop generated textures without forcing the next scene to decode PNGs or spritesheets again unless preserve_source_images is set to false.spriteManager.get_cache_stats() includes source_images alongside texture, scaled texture, animation, path texture, and sheet-cell counts for Sprint 2 baselines.For very busy scenes, pair that texture cache with renderNodePool. The pool keeps rect/sprite nodes alive for the active scene, updates their texture/position/visibility on redraw, hides unused nodes, and only creates new nodes when the visible object count grows. Project Alchemy uses this for its active battlefield; arcade and platformer prototypes should use the same path as their renderers become denser. renderNodePool.get_stats() reports pool count, node count, visible nodes, nodes hidden on the last pass, and pass/lifetime created/reused/acquired counts. Feed those stats into performanceMonitor.capture_provider_stats() during optimization passes to verify a renderer is reusing nodes instead of allocating fresh UI nodes every redraw.
paletteManager.load_palettes(file_path) loads 16-color palette sets from JSON.paletteManager.recolor_texture(texture, source_palette_id, target_palette_id) maps a sprite from one palette to another.paletteManager.get_recolored_sprite_texture(sprite_manager, sprite_id, source_palette_id, target_palette_id, size) combines sprite lookup, optional scaling, recoloring, and caching.paletteManager.get_cache_stats() reports loaded palette count and recolored texture cache count for performance baselines. Missing recolor sources are not cached as null entries.performanceMonitor is the shared baseline tool for optimization sprints. It is opt-in and stores plain dictionaries, so it can be used in headless smoke tests, editor tools, live playtest overlays, or temporary scene instrumentation without locking games into a profiler UI.
performanceMonitor.start_timer(id) and stop_timer(id, metadata) capture elapsed microseconds and aggregate count, total, min, max, last, and average durations per id.record_duration(id, duration_usec, metadata) records measured timings from systems that already own their own clocks.increment(id, amount) stores counters for cache hits, allocations avoided, draw calls, loaded records, save writes, and other optimization signals.set_gauge(id, value) stores current point-in-time values such as active nodes, visible entities, cached textures, or menu item counts.sample_frame(delta) records rolling frame samples with configurable history length.capture_node_counts(root, id) walks a node tree and records total, Control, CanvasItem, visible, and hidden counts as gauges.capture_provider_stats(id, provider) calls get_cache_stats() or get_stats() on another manager and records numeric values as gauges.measure_callable(id, action, metadata) wraps a callable and records its duration while returning the callable result in the timing sample.capture_provider_delta(id, provider, action, metadata) captures provider stats before and after a callable, records numeric stat deltas as gauges, and returns before/after/delta/timing dictionaries. This is the preferred baseline wrapper for sprite cache warming, layout rebuilds, and scene-open loops.benchmark_scene_instantiation(id, packed_scene, parent, iterations, metadata) instantiates a scene repeatedly, records average/min/max instantiate time, captures parent and instance node counts, frees each instance, and stores scene transition marks. Use this for smoke-safe open/close baselines before changing menu, modal, or game-screen lifecycle code.load_baseline_profiles(file_path) reads JSON baseline definitions through dataCacheManager, with profile ids, labels, scene paths, categories, and iteration counts.benchmark_scene_profiles(profiles, parent, options) runs a list of scene baseline profiles and returns { results, errors, count }. Sprint 1 uses res://data/performance/sprint1_baselines.json as the active informational baseline list for all current game entry scenes.get_summary(), get_state(), apply_state(state), and format_summary_lines() expose report, serialization, restore, and compact text output helpers.get_cache_stats() exposes baseline-profile JSON cache diagnostics.format_summary_text() and write_summary_file(file_path) provide opt-in local debug output for playtest builds and CI artifacts when a sprint needs a persisted baseline report.performance_overlay.gd renders a compact, wrapped monitor summary panel for temporary local/debug builds. It reuses a stable panel and label node, supports hide/clear, and should be enabled only when actively profiling.builds/performance/performance_baseline_report.json every run. This avoids the Godot 4.7 Windows headless crash reproduced when report output was controlled through command-line script args or temporary environment variables.Use the monitor before and after refactors. A sprint should generally introduce a baseline metric first, then optimize the module, then keep or update the smoke coverage so regressions are visible.
Sprint 1 closeout: instrumentation and smoke-safe scene-entry baselines are complete. Sprint 2 should use these tools against spriteManager, spriteRenderManager, paletteManager, and renderNodePool before changing cache behavior.
Game scene scripts should:
saveManager.build_profile_system
Use this group to describe which game should be packaged from the shared framework project. CI now consumes these profiles through scripts/ci/build_profiles.py and writes profile-named artifacts under builds/<game_id>/<platform>/.
buildProfileManager.load_profiles(directory_path) loads every JSON profile under data/build_profiles/ through dataCacheManager.buildProfileManager.load_profile(file_path) loads one profile and indexes it by game_id.buildProfileManager.get_profile_ids() and get_profile_options(include_all_demos) expose compact rows for editor pickers, build dashboards, or future game-selection tooling.buildProfileManager.get_cache_stats() exposes cached profile JSON loader diagnostics for CI/editor dashboards.buildProfileManager.validate_profile(profile) checks required fields, start scene existence, include roots, and platform artifact definitions.buildProfileManager.get_export_plan(profile, all_game_ids) returns a normalized plan with entry/launch scenes, include roots, excluded game data roots, platform export presets, and artifact paths.buildProfileManager.audit_single_game_profile(profile, all_game_ids,
required_shared_roots, required_game_roots) returns a release/checklist summary for one game package, including missing shared roots, missing game roots, missing other-game data excludes, accidental other-game data includes, and a valid flag. Use this in CI or editor dashboards before long export jobs so single-game packages stay lean.buildProfileManager.audit_single_game_profiles(profile_ids,
required_shared_roots_by_game, required_game_roots_by_game) applies the same audit to every selected individual game profile and returns a combined valid flag plus per-game audit rows.scripts/ci/build_profiles.py is the CI/local build entrypoint. It temporarily patches project.godot, export_presets.cfg, and data/build_info.json per profile, runs the requested Godot exports, verifies the expected artifacts, and restores the source files afterward. It also writes builds/build_manifest.json for artifact inspection.Profile JSON files should define:
game_id, label, start_scene, entry_scene, launch_scene, menu_route, and output_name.entry_scene is the scene Godot opens first, usually the shared splash screen. launch_scene is where that splash screen goes next.metadata: menu_label, menu_order, sprite_id, and description.shared_roots for framework systems, input/theme data, shared assets, and other reusable resources.include_roots for the game's data, scene, script, and specific assets.exclude_game_ids or exclude_roots so single-game exports can omit other game data where Godot export filters allow it.platforms with export preset, artifact path, and optional mode metadata.CI selection variables:
BUILD_PROFILES=all or a comma list such as project_alchemy,arcade_pong.BUILD_PLATFORMS=all or a comma list such as web,windows.Seeded profiles:
data/build_profiles/all_demos.jsondata/build_profiles/reflecting_pool.jsondata/build_profiles/emoji_card_collector.jsondata/build_profiles/arcade_pong.jsondata/build_profiles/project_alchemy.jsondata/build_profiles/platformer_demo.jsonThe main start menu uses the game_profiles menu template and the loaded build profiles to populate top-level game buttons. menu_route should match the target submenu id, while metadata.menu_order controls display order.
When adding a new game:
data/games/<game_id>/ or shared level data under data/levels/.assets/sprites/<category>/ with a manifest.layoutManager.create_screen_shell() and shared pause/settings where possible.statsManager plus layoutManager.data/build_profiles/<game_id>.json.tests/headless_smoke.gd.