From 55266b73ffe343747618dc6e250e6409004c241a Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Wed, 6 Dec 2023 18:15:10 -0800 Subject: [PATCH] Support entity/line/upgrade requirements for upgrades --- src/lib.rs | 2 +- src/state/editor_screen/upgrade_panel.rs | 59 +++++++++---------- src/upgrade.rs | 75 +++++++++++++++++------- 3 files changed, 82 insertions(+), 54 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 69f8ccf..da7f75d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -82,7 +82,7 @@ pub enum AppSet { Tick, /// Handle input Input, - /// Enable / update upgrades + /// Install and run upgrades RunUpgrades, /// Step the simulation Simulate, diff --git a/src/state/editor_screen/upgrade_panel.rs b/src/state/editor_screen/upgrade_panel.rs index f7d527d..1c76685 100644 --- a/src/state/editor_screen/upgrade_panel.rs +++ b/src/state/editor_screen/upgrade_panel.rs @@ -295,12 +295,12 @@ impl Default for UpgradeSequence { } impl UpgradeSequence { - fn next(&mut self, upgrade_list: &UpgradeList) -> Option { + fn next(&mut self, upgrade_list: &UpgradeList, simulation: &Simulation) -> Option { while self.next_idx < self.sequence.len() { self.next_idx += 1; - let upgrade_kind = self.sequence[self.next_idx - 1]; - if upgrade_list.get(upgrade_kind).remaining > 0 { - return Some(upgrade_kind); + let kind = self.sequence[self.next_idx - 1]; + if upgrade_list.get(kind).is_unlocked(simulation) { + return Some(kind); } } None @@ -313,10 +313,12 @@ fn replace_available_upgrades( config: Res, upgrade_list: Res, mut upgrade_sequence: ResMut, + simulation: Res, container_query: Query<(Entity, &Children, &UpgradeContainer)>, ) { let theme = &config.editor_screen.dark_theme; for (entity, buttons, container) in &container_query { + // Despawn old upgrade options for &button in buttons { despawn.recursive(button); } @@ -327,36 +329,31 @@ fn replace_available_upgrades( commands.entity(upgrade_button).set_parent(entity); }; - let mut remaining_upgrades = container.slots; + let mut slots = container.slots; - // Initial sequence of upgrades, and then randomly chosen upgrades (weighted) - if let Some(upgrade_kind) = upgrade_sequence.next(&upgrade_list) { - add_upgrade(upgrade_kind); - remaining_upgrades -= 1; - if remaining_upgrades == 0 { - continue; - } + // Try to fill slots from the initial sequence of upgrades first + while slots > 0 { + let Some(kind) = upgrade_sequence.next(&upgrade_list, &simulation) else { + break; + }; + add_upgrade(kind); + slots -= 1; } - let selected_upgrades = ALL_UPGRADE_KINDS.choose_multiple_weighted( - &mut thread_rng(), - remaining_upgrades, - |&kind| { - let upgrade = upgrade_list.get(kind); - if upgrade.remaining > 0 { - upgrade.weight - } else { - 0.0 - } - }, - ); - match selected_upgrades { - Ok(selected_upgrades) => { - for upgrade_kind in selected_upgrades { - add_upgrade(*upgrade_kind); - } - }, - Err(error) => error!("Failed to choose random upgrade(s): {:?}", error), + // Filter the list of all upgrade kinds into just the ones that are unlocked + let unlocked_upgrades = ALL_UPGRADE_KINDS + .into_iter() + .filter(|&kind| upgrade_list.get(kind).is_unlocked(&simulation)) + .collect::>(); + + // Fill the remaining upgrade slots randomly (weighted) from the list of unlocked upgrades + for &kind in unlocked_upgrades + .choose_multiple_weighted(&mut thread_rng(), slots, |&kind| { + upgrade_list.get(kind).weight + }) + .unwrap() + { + add_upgrade(kind); } } } diff --git a/src/upgrade.rs b/src/upgrade.rs index 4aee6e3..cc84c38 100644 --- a/src/upgrade.rs +++ b/src/upgrade.rs @@ -20,12 +20,12 @@ impl Plugin for UpgradePlugin { app.register_type::() .add_event::() .init_resource::() - .init_resource::() + .init_resource::() .add_systems(Startup, load_upgrade_list) .add_systems( Update, ( - (enable_upgrades, run_active_upgrades, apply_deferred) + (install_upgrades, run_installed_upgrades, apply_deferred) .chain() .in_set(AppSet::RunUpgrades), apply_cost_scaling @@ -48,20 +48,32 @@ pub struct Upgrade { /// How much this upgrade contributes to the Fun score of your submission. pub fun_score: f32, - /// How many lines of code this upgrade costs (will increase with cost scaling) + /// How many lines of code this upgrade costs (will increase with cost scaling). pub cost: f64, /// The cost scaling multiplier for this upgrade. pub cost_multiplier: f64, /// Whether enabling this upgrade causes cost scaling. pub scales_costs: bool, - /// The relative odds of this upgrade being offered + /// The relative odds of this upgrade being offered. pub weight: f32, - /// How many more copies of this upgrade can be enabled + /// How many more copies of this upgrade can be installed. pub remaining: usize, + /// The minimum number of entities required for this upgrade to be offered. + pub entity_min: f64, + /// The maximum number of entities allowed for this upgrade to be offered. + pub entity_max: f64, + /// The minimum number of lines of code required for this upgrade to be offered. + pub line_min: f64, + /// The maximum number of lines of code allowed for this upgrade to be offered. + pub line_max: f64, + /// The minimum number of installed upgrades required for this upgrade to be offered. + pub upgrade_min: usize, + /// The maximum number of installed upgrades allowed for this upgrade to be offered. + pub upgrade_max: usize, - /// A one-shot system that runs whenever a copy of this upgrade is enabled - pub enable: Option, - /// A one-shot system that runs every frame for each active copy of this upgrade + /// A one-shot system that runs whenever a copy of this upgrade is installed. + pub install: Option, + /// A one-shot system that runs every frame for each installed copy of this upgrade. pub update: Option, } @@ -73,42 +85,61 @@ impl Default for Upgrade { presentation_score: 0.0, theme_score: 0.0, fun_score: 0.0, + cost: 0.0, cost_multiplier: 1.0, scales_costs: true, weight: 0.0, remaining: 1, - enable: None, + entity_min: 0.0, + entity_max: f64::INFINITY, + line_min: 0.0, + line_max: f64::INFINITY, + upgrade_min: 0, + upgrade_max: usize::MAX, + + install: None, update: None, } } } +impl Upgrade { + pub fn is_unlocked(&self, simulation: &Simulation) -> bool { + self.remaining > 0 + && (self.entity_min <= simulation.entities && simulation.entities <= self.entity_max) + && (self.line_min <= simulation.lines && simulation.lines <= self.line_max) + && (self.upgrade_min <= simulation.upgrades && simulation.upgrades <= self.upgrade_max) + } +} + #[derive(Event, Reflect, Clone, Copy)] pub struct UpgradeEvent(pub UpgradeKind); -fn enable_upgrades(world: &mut World, mut reader: Local>) { +fn install_upgrades(world: &mut World, mut reader: Local>) { for event in reader .read(world.resource::>()) .copied() .collect::>() { - let &Upgrade { enable, update, .. } = world.resource::().get(event.0); - if let Some(enable) = enable { - world.run_system(enable).unwrap(); + let &Upgrade { + install, update, .. + } = world.resource::().get(event.0); + if let Some(install) = install { + world.run_system(install).unwrap(); } if let Some(update) = update { - world.resource_mut::().0.push(update); + world.resource_mut::().0.push(update); } } } #[derive(Resource, Default)] -struct ActiveUpgrades(Vec); +struct UpgradeUpdateSystems(Vec); -fn run_active_upgrades(world: &mut World) { +fn run_installed_upgrades(world: &mut World) { #[allow(clippy::unnecessary_to_owned)] - for update in world.resource::().0.to_vec() { + for update in world.resource::().0.to_vec() { world.run_system(update).unwrap(); } } @@ -182,7 +213,7 @@ generate_upgrade_list!( name: "Dark Mode".to_string(), description: "Rite of passage for all developers. Required to write code.".to_string(), scales_costs: false, - enable: Some(world.register_system(| + install: Some(world.register_system(| mut commands: Commands, root: Res, config: Res, @@ -199,7 +230,7 @@ generate_upgrade_list!( description: "Spawns 1 entity whenever you click inside the scene view.".to_string(), scales_costs: false, cost: 5.0, - enable: Some( + install: Some( world.register_system(|mut scene_view_query: Query<&mut SceneView>| { for mut scene_view in &mut scene_view_query { scene_view.spawns_per_click += 1; @@ -212,7 +243,7 @@ generate_upgrade_list!( name: "Brainstorm".to_string(), description: "Adds 1 extra upgrade slot.".to_string(), scales_costs: false, - enable: Some( + install: Some( world.register_system(|mut query: Query<&mut UpgradeContainer>| { for mut container in &mut query { container.slots += 1; @@ -228,7 +259,7 @@ generate_upgrade_list!( cost_multiplier: 1.2, weight: 1.0, remaining: usize::MAX, - enable: Some( + install: Some( world.register_system(|mut events: EventWriter, bounds: Res| { for _ in 0..32 { events.send(SpawnEvent((bounds.min.xy() + bounds.max.xy()) / 2.0)); @@ -244,7 +275,7 @@ generate_upgrade_list!( scales_costs: false, weight: 1.0, remaining: usize::MAX, - enable: Some(world.register_system(|mut simulation: ResMut| { + install: Some(world.register_system(|mut simulation: ResMut| { simulation.lines += 10.0; })), ..default()