ui components
Guide for ui components
UI Implementation
Spell Book UI
File:
src/systems/ui/spell_book_ui.rsArchitecture
The Spell Book is integrated into the main game UI as a tab (not a standalone window), matching the pattern of Inventory and Stats tabs.
State Management
#[derive(Resource, Default)]
pub struct SpellBookState {
pub selected_tab: SpellBookTab,
}
#[derive(Default, PartialEq)]
pub enum SpellBookTab {
#[default]
Combat,
Utility,
Teleport,
}Main Render Function
pub fn render_magic_tab(
ui: &mut egui::Ui,
spell_book_state: &mut SpellBookState,
skills_data: &SkillsData,
player_entity: Entity,
skills: &Skills,
active_spell: Option<&ActiveSpell>,
select_spell_events: &mut EventWriter<SelectSpellEvent>,
coconut_ui_state: &mut CoconutUIState,
)Called from:
ui_main_layout in src/systems/ui/mod.rs when Magic tab is activeTab Rendering
Combat Tab
fn render_combat_spells(
ui: &mut egui::Ui,
skills_data: &SkillsData,
magic_level: i32,
active_spell: Option<&ActiveSpell>,
player_entity: Entity,
select_spell_events: &mut EventWriter<SelectSpellEvent>,
)Features:
- Sorts spells by level requirement
- Shows spell name, level, max hit
- Displays bija costs (e.g., "2x Air, 1x Chandra")
- Highlights selected spell with green background
- Grays out spells above player's level
- Tooltips on hover showing full details
- Click to select/deselect spell (dispatches
SelectSpellEvent)
Layout: Grid of spell buttons
Utility Tab
fn render_utility_spells(
ui: &mut egui::Ui,
skills_data: &SkillsData,
magic_level: i32,
)Features:
- Lists utility spells (healing, buffs, etc.)
- Shows effect description
- Displays bija costs
- Level-based availability
Teleport Tab
fn render_teleport_spells(
ui: &mut egui::Ui,
skills_data: &SkillsData,
magic_level: i32,
)Features:
- Lists teleport recipes
- Shows destination
- Displays required bijas for each eye
- Level requirements
- XP rewards
Integration Pattern
In
src/systems/ui/mod.rs:// Module declaration
mod spell_book_ui;
// Resource initialization
app.init_resource::<spell_book_ui::SpellBookState>()
// In ui_main_layout match statement
UiTab::Magic => {
if let Ok((player_entity, skills, active_spell)) = player_q.get_single() {
spell_book_ui::render_magic_tab(
ui,
&mut spell_book_state,
&skills_data,
player_entity,
skills,
active_spell,
&mut select_spell_writer,
&mut coconut_ui_state
);
}
}Coconut Stacking UI
File:
src/systems/ui/coconut_stacking_ui.rsArchitecture
Standalone egui Window that can be toggled on/off.
State Management
#[derive(Resource, Default)]
pub struct CoconutUIState {
pub is_open: bool,
pub eye_1: Option<String>, // Bija ID
pub eye_2: Option<String>,
pub eye_3: Option<String>,
}Main Render Function
pub fn render_coconut_ui(
mut contexts: EguiContexts,
mut ui_state: ResMut<CoconutUIState>,
player_query: Query<(Entity, &Inventory), With<Player>>,
skills_data: Res<SkillsData>,
mut prepare_event: EventWriter<PrepareCoconutEvent>,
)Called from:
Update schedule in src/systems/ui/mod.rsUI Layout
┌─────────────────────────────┐
│ Coconut Ritual │
├─────────────────────────────┤
│ Eye 1 (Top) Eye 2 (Left) │
│ [Vishnu] [Air] │
│ │
│ Eye 3 (Right) │
│ [Earth] │
├─────────────────────────────┤
│ Available Bijas: │
│ [Air] [Water] [Earth]... │
├─────────────────────────────┤
│ [Charge Coconut] │
│ [Close] │
└─────────────────────────────┘Features
- Eye Slots: 3 vertical slots showing placed bijas
- Click placed bija to remove it
- Shows "Empty" if no bija placed
- Available Bijas: Horizontal scrollable list
- Iterates through known bija IDs
- Only shows bijas player has in inventory
- Click to place in first empty eye
- Charge Button:
- Enabled only when all 3 eyes filled
- Validates against known recipes
- Dispatches
PrepareCoconutEventif valid - Clears UI and closes window on success
- Recipe Matching Logic:
let mut found_recipe = None;
for recipe in &skills_data.teleport_recipes {
let mut match_1 = false;
let mut match_2 = false;
let mut match_3 = false;
for bija_req in &recipe.coconut_bijas {
if bija_req.eye == 1 && Some(&bija_req.bija_id) == ui_state.eye_1.as_ref() {
match_1 = true;
}
// ... similar for eye 2 and 3
}
if match_1 && match_2 && match_3 {
found_recipe = Some(recipe.teleport_id.clone());
break;
}
}Toggle Mechanism
From Spell Book UI:
// In render_magic_tab, after tabs
if ui.button("🥥 Ritual").clicked() {
coconut_ui_state.is_open = !coconut_ui_state.is_open;
}System Parameter Optimization
Problem
The
ui_main_layout system exceeded Bevy's 16-parameter limit after adding magic UI parameters.Solution
Created a
SystemParam group:#[derive(bevy::ecs::system::SystemParam)]
pub struct UiParams<'w, 's> {
pub ui_assets: Res<'w, UiAssets>,
pub interactables: Query<'w, 's, &'static Interactable>,
pub player_q: Query<'w, 's, (Entity, &'static Skills, Option<&'static ActiveSpell>), With<Player>>,
pub skills_q: Query<'w, 's, &'static Skills>,
pub inventory_q: Query<'w, 's, &'static Inventory>,
pub skills_data: Res<'w, SkillsData>,
}Usage:
fn ui_main_layout(
mut contexts: EguiContexts,
mut ui_state: ResMut<UiState>,
params: UiParams, // Grouped params
// ... other individual params
) {
// Unpack for convenience
let ui_assets = params.ui_assets;
let player_q = params.player_q;
// ...
}This reduces the parameter count and keeps the system under the limit.
Key Design Decisions
Tab Integration vs Standalone Window
Decision: Spell Book as tab, Coconut UI as window
Rationale:
- Spell Book is frequently accessed (like Inventory/Stats) → deserves tab
- Coconut UI is occasional ritual → popup window is appropriate
- Matches OSRS pattern (spellbook = interface, teleport creation = special interface)
Event-Driven Architecture
All UI interactions dispatch events rather than directly modifying game state:
SelectSpellEventfor spell selectionPrepareCoconutEventfor coconut charging
Benefits:
- Decouples UI from game logic
- Enables networking (events can be serialized)
- Easier to test and debug
Bija Display in UI
Challenge: Inventory stores
ItemId (u32 hash), but UI needs item namesSolution: Hardcoded known bija list in Coconut UI:
let known_bijas = [
"air_bija", "water_bija", "earth_bija", "fire_bija",
"chandra_bija", "hanuman_bija", "vishnu_bija", "indra_bija",
"rahu_bija", "kali_bija", "yama_bija", "rudra_bija"
];Future Improvement: Could iterate
items_db and filter by item type/category.