~starkingdoms/starkingdoms

8bbae28ef02dcef8ae19715d43d6b05edb8829ec — ghostly_zsh 6 hours ago 56e94bf
feat: variable storage on hearty appears to store things correctly
M crates/unified/assets/config/parts/hearty.part.toml => crates/unified/assets/config/parts/hearty.part.toml +1 -1
@@ 71,5 71,5 @@ can_craft = true
resource_multiplier = 1.0

[storage]
storage_type = "SingleResource"
storage_type = "MultipleResources"
capacity = 300.0

M crates/unified/src/client/crafting/ui.rs => crates/unified/src/client/crafting/ui.rs +37 -6
@@ 2,14 2,14 @@ use std::collections::HashMap;

use bevy::{input_focus::{AutoFocus, InputFocus}, ui::RelativeCursorPosition};

use crate::{attachment::PartInShip, client::colors, config::recipe::RecipesConfig, ecs::{CanCraft, CraftPartRequest, CraftingUi, Drill, MainCamera, Me, Part, SingleStorage, ToggleDrillEvent}, prelude::*};
use crate::{attachment::PartInShip, client::colors, config::recipe::RecipesConfig, ecs::{CanCraft, CraftPartRequest, CraftingUi, Drill, MainCamera, Me, Part, SingleStorage, ToggleDrillEvent, VariableStorage}, prelude::*};

pub fn crafting_ui_plugin(app: &mut App) {
    app.init_resource::<RecipeCollection>();
    app.add_systems(Startup, load_recipes);
    app.add_systems(PreUpdate, (initial_create_recipe_list, update_recipe_list));
    app.add_systems(Update, (close_button, drill_button, drill_state_change,
            single_storage_display, recipe_buttons));
            single_storage_display, variable_storage_display, recipe_buttons));
}

#[derive(Component)]


@@ 21,6 21,8 @@ struct DrillButton(Entity); // stores corresponding part
#[derive(Component)]
struct SingleStorageDisplay(Entity); // stores corresponding part
#[derive(Component)]
struct VariableStorageDisplay(Entity); // stores corresponding part
#[derive(Component)]
struct RecipesHolder(Entity); // stores corresponding part
#[derive(Component)]
struct PendingRecipesHolder(Entity); // stores corresponding part


@@ 38,13 40,13 @@ fn load_recipes(asset_server: Res<AssetServer>, mut recipe_collection: ResMut<Re

pub fn open_crafting_ui(
    ev: On<Pointer<Press>>,
    crafting_parts: Query<(Entity, &Transform, Option<&Drill>, Option<&SingleStorage>), (With<PartInShip>, With<CanCraft>)>,
    hearty: Query<(Entity, &Transform, Option<&Drill>, Option<&SingleStorage>), (With<Me>, With<CanCraft>)>,
    crafting_parts: Query<(Entity, &Transform, Option<&Drill>, Option<&SingleStorage>, Option<&VariableStorage>), (With<PartInShip>, With<CanCraft>)>,
    hearty: Query<(Entity, &Transform, Option<&Drill>, Option<&SingleStorage>, Option<&VariableStorage>), (With<Me>, With<CanCraft>)>,
    camera: Single<(Entity, &Camera, &GlobalTransform), (With<MainCamera>, Without<PartInShip>)>,
    commands: Commands,
) {
    if matches!(ev.button, PointerButton::Secondary) {
        let (entity, transform, drill, single_storage) = if let Ok(part) = crafting_parts.get(ev.entity) {
        let (entity, transform, drill, single_storage, variable_storage) = if let Ok(part) = crafting_parts.get(ev.entity) {
            part
        } else if let Ok(part) = hearty.get(ev.entity) {
            part


@@ 53,7 55,8 @@ pub fn open_crafting_ui(
        };
        // we have our crafting entity!
        // now make the ui
        setup_ui(entity, transform, commands, camera, drill, single_storage);
        setup_ui(entity, transform, commands, camera, drill, single_storage,
            variable_storage);
    }
}



@@ 64,6 67,7 @@ fn setup_ui(
    camera: Single<(Entity, &Camera, &GlobalTransform), (With<MainCamera>, Without<PartInShip>)>,
    drill: Option<&Drill>,
    single_storage: Option<&SingleStorage>,
    variable_storage: Option<&VariableStorage>,
) {
    let parent_pos = camera.1.world_to_viewport(camera.2, parent_transform.translation).unwrap();
    let entity = commands.spawn((


@@ 146,6 150,20 @@ fn setup_ui(
                SingleStorageDisplay(parent_part),
            ));
        }
        // only add storage if the part has variable storage
        if let Some(variable_storage) = variable_storage {
            parent.spawn((
                Node {
                    ..Default::default()
                },
                TextFont {
                    font_size: 10.0,
                    ..Default::default()
                },
                Text::new(get_variable_storage_display_text(&variable_storage)),
                VariableStorageDisplay(parent_part),
            ));
        }
        // assume CanCraft for now (THIS WILL CHANGE)
        parent.spawn((
            Node {


@@ 353,6 371,19 @@ fn single_storage_display(
        **text = format!("{}: {}", single_storage.resource_name, single_storage.stored);
    }
}
fn variable_storage_display(
    mut variable_storage_display_query: Query<(&mut Text, &VariableStorageDisplay)>,
    part_query: Query<&VariableStorage, With<Part>>,
) {
    for (mut text, variable_storage_display) in &mut variable_storage_display_query {
        let variable_storage = part_query.get(variable_storage_display.0).expect("In variable_storage_display, the entity didn't match a storage.");
        **text = get_variable_storage_display_text(&variable_storage);
    }
}
fn get_variable_storage_display_text(storage: &VariableStorage) -> String {
    storage.resources.iter().map(|(name, amount)| format!("{}: {}", name, amount))
        .collect::<Vec<_>>().join("\n")
}

fn close_button(
    mut commands: Commands,

M crates/unified/src/ecs.rs => crates/unified/src/ecs.rs +6 -0
@@ 162,3 162,9 @@ pub struct SingleStorage {
    pub capacity: f32,
    pub stored: f32,
}
#[derive(Component, Serialize, Deserialize, Debug)]
#[require(Replicated)]
pub struct VariableStorage {
    pub resources: HashMap<String, f32>,
    pub capacity: f32,
}

M crates/unified/src/server/drill.rs => crates/unified/src/server/drill.rs +44 -31
@@ 1,4 1,4 @@
use crate::{attachment::{PartInShip, Parts}, config::planet::Planet, ecs::{Drill, Part, PlanetSensor, Player, SingleStorage, ToggleDrillEvent}, prelude::*};
use crate::{attachment::{PartInShip, Parts}, config::planet::Planet, ecs::{Drill, Part, PlanetSensor, Player, SingleStorage, ToggleDrillEvent, VariableStorage}, prelude::*};

pub fn drill_plugin(app: &mut App) {
    app.add_systems(Update, (toggle_drill, drill_on_planet, do_drilling));


@@ 49,7 49,8 @@ fn do_drilling(
    drills: Query<(&Drill, &PartInShip)>,
    hearty_drills: Query<(Entity, &Drill), With<Player>>,
    parts_query: Query<&Parts>,
    mut storage_part_query: Query<&mut SingleStorage, With<Part>>,
    mut single_storage_part_query: Query<&mut SingleStorage, With<Part>>,
    mut variable_storage_part_query: Query<&mut VariableStorage, With<Part>>,
    planet_query: Query<&Planet>,
    time: Res<Time>,
) {


@@ 73,32 74,18 @@ fn do_drilling(
        'adding_resources: {
            if let Ok(parts_list) = parts_query.get(entity) {
                for part_entity in parts_list.iter() {
                    let Ok(mut storage) = storage_part_query.get_mut(part_entity) else {
                    let Ok(mut storage) = single_storage_part_query.get_mut(part_entity) else {
                        continue
                    };
                    if storage.resource_name.is_empty() {
                        storage.resource_name = planet_resource.name.clone();
                    }
                    // now that the name isn't empty, this will trigger
                    if storage.resource_name == planet_resource.name {
                        storage.stored += planet_resource.mining_speed * drill.resource_multiplier * time.delta_secs();
                        storage.stored = storage.stored.min(storage.capacity);
                        break 'adding_resources;
                    }
                    let to_add = planet_resource.mining_speed * drill.resource_multiplier * time.delta_secs();
                    add_to_single_storage(&mut storage, to_add, &planet_resource.name);
                }
            }
            let Ok(mut storage) = storage_part_query.get_mut(entity) else {
            let Ok(mut storage) = variable_storage_part_query.get_mut(entity) else {
                break 'adding_resources;
            };
            if storage.resource_name.is_empty() {
                storage.resource_name = planet_resource.name.clone();
            }
            // now that the name isn't empty, this will trigger
            if storage.resource_name == planet_resource.name {
                storage.stored += planet_resource.mining_speed * drill.resource_multiplier * time.delta_secs();
                storage.stored = storage.stored.min(storage.capacity);
                break 'adding_resources;
            }
            let to_add = planet_resource.mining_speed * drill.resource_multiplier * time.delta_secs();
            add_to_variable_storage(&mut storage, to_add, &planet_resource.name);
        };
    }
    for (drill, part_in_ship) in drills {


@@ 125,17 112,43 @@ fn do_drilling(
            continue
        };
        for part_entity in parts_list.iter() {
            let Ok(mut storage) = storage_part_query.get_mut(part_entity) else {
            let Ok(mut storage) = single_storage_part_query.get_mut(part_entity) else {
                continue
            };
            if storage.resource_name.is_empty() {
                storage.resource_name = planet_resource.name.clone();
            }
            // now that the name isn't empty, this will trigger
            if storage.resource_name == planet_resource.name {
                storage.stored += planet_resource.mining_speed * drill.resource_multiplier * time.delta_secs();
                storage.stored = storage.stored.min(storage.capacity);
            }
            let to_add = planet_resource.mining_speed * drill.resource_multiplier * time.delta_secs();
            add_to_single_storage(&mut storage, to_add, &planet_resource.name);
        }
    }
}

fn add_to_variable_storage(
    storage: &mut VariableStorage,
    to_add: f32,
    resource_name: &String
) {
    let total_stored = storage.resources.iter().fold(0.0, |acc, (_, resource)| *resource + acc);
    if to_add + total_stored > storage.capacity {
        return;
    }
    if let Some(amount) = storage.resources.get_mut(resource_name) {
        *amount += to_add;
    } else {
        storage.resources.insert(resource_name.clone(), to_add);
    }
}

fn add_to_single_storage(
    storage: &mut SingleStorage,
    to_add: f32,
    resource_name: &String
) {
    if storage.resource_name.is_empty() {
        storage.resource_name = resource_name.clone();
    }
    // now that the name isn't empty, this will trigger
    if storage.resource_name == *resource_name {
        storage.stored += to_add;
        storage.stored = storage.stored.min(storage.capacity);
        return;
    }
}

M crates/unified/src/server/part.rs => crates/unified/src/server/part.rs +17 -7
@@ 1,6 1,8 @@
use std::collections::HashMap;

use crate::attachment::{Joint, JointId, JointOf, Joints, Peer, SnapOf, SnapOfJoint};
use crate::config::part::{CoolingConfig, CraftingConfig, DrillConfig, JointConfig, PartConfig, StorageConfig, StorageType};
use crate::ecs::{CanCraft, Cooler, Drill, Part, PartHandle, Radiator, SingleStorage, Temperature};
use crate::ecs::{CanCraft, Cooler, Drill, Part, PartHandle, Radiator, SingleStorage, Temperature, VariableStorage};
use crate::prelude::*;
use bevy_replicon::prelude::Replicated;
use crate::ecs::thruster::{PartThrusters, Thruster, ThrusterBundle, ThrusterId, ThrusterOfPart};


@@ 167,12 169,20 @@ fn drill_bundle(entity: &mut EntityCommands, config: &DrillConfig) {
    });
}
fn storage_bundle(entity: &mut EntityCommands, config: &StorageConfig) {
    if matches!(config.storage_type, StorageType::SingleResource) {
        entity.insert(SingleStorage {
            resource_name: String::new(),
            capacity: config.capacity,
            stored: 0.0,
        });
    match config.storage_type {
        StorageType::SingleResource => {
            entity.insert(SingleStorage {
                resource_name: String::new(),
                capacity: config.capacity,
                stored: 0.0,
            });
        }
        StorageType::MultipleResources => {
            entity.insert(VariableStorage {
                resources: HashMap::new(),
                capacity: config.capacity,
            });
        }
    }
}
fn spawn_joint_bundle(joint: &JointConfig, part: &PartConfig, parent: &Entity) -> impl Bundle {