Skip to content

Commit

Permalink
Support entity/line/upgrade requirements for upgrades
Browse files Browse the repository at this point in the history
  • Loading branch information
benfrankel committed Dec 7, 2023
1 parent 0de6308 commit 55266b7
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 54 deletions.
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ pub enum AppSet {
Tick,
/// Handle input
Input,
/// Enable / update upgrades
/// Install and run upgrades
RunUpgrades,
/// Step the simulation
Simulate,
Expand Down
59 changes: 28 additions & 31 deletions src/state/editor_screen/upgrade_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,12 +295,12 @@ impl Default for UpgradeSequence {
}

impl UpgradeSequence {
fn next(&mut self, upgrade_list: &UpgradeList) -> Option<UpgradeKind> {
fn next(&mut self, upgrade_list: &UpgradeList, simulation: &Simulation) -> Option<UpgradeKind> {
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
Expand All @@ -313,10 +313,12 @@ fn replace_available_upgrades(
config: Res<Config>,
upgrade_list: Res<UpgradeList>,
mut upgrade_sequence: ResMut<UpgradeSequence>,
simulation: Res<Simulation>,
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);
}
Expand All @@ -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::<Vec<_>>();

// 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);
}
}
}
75 changes: 53 additions & 22 deletions src/upgrade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ impl Plugin for UpgradePlugin {
app.register_type::<UpgradeEvent>()
.add_event::<UpgradeEvent>()
.init_resource::<UpgradeList>()
.init_resource::<ActiveUpgrades>()
.init_resource::<UpgradeUpdateSystems>()
.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
Expand All @@ -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<SystemId>,
/// 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<SystemId>,
/// A one-shot system that runs every frame for each installed copy of this upgrade.
pub update: Option<SystemId>,
}

Expand All @@ -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<ManualEventReader<UpgradeEvent>>) {
fn install_upgrades(world: &mut World, mut reader: Local<ManualEventReader<UpgradeEvent>>) {
for event in reader
.read(world.resource::<Events<_>>())
.copied()
.collect::<Vec<_>>()
{
let &Upgrade { enable, update, .. } = world.resource::<UpgradeList>().get(event.0);
if let Some(enable) = enable {
world.run_system(enable).unwrap();
let &Upgrade {
install, update, ..
} = world.resource::<UpgradeList>().get(event.0);
if let Some(install) = install {
world.run_system(install).unwrap();
}
if let Some(update) = update {
world.resource_mut::<ActiveUpgrades>().0.push(update);
world.resource_mut::<UpgradeUpdateSystems>().0.push(update);
}
}
}

#[derive(Resource, Default)]
struct ActiveUpgrades(Vec<SystemId>);
struct UpgradeUpdateSystems(Vec<SystemId>);

fn run_active_upgrades(world: &mut World) {
fn run_installed_upgrades(world: &mut World) {
#[allow(clippy::unnecessary_to_owned)]
for update in world.resource::<ActiveUpgrades>().0.to_vec() {
for update in world.resource::<UpgradeUpdateSystems>().0.to_vec() {
world.run_system(update).unwrap();
}
}
Expand Down Expand Up @@ -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<AppRoot>,
config: Res<Config>,
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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<SpawnEvent>, bounds: Res<SceneViewBounds>| {
for _ in 0..32 {
events.send(SpawnEvent((bounds.min.xy() + bounds.max.xy()) / 2.0));
Expand All @@ -244,7 +275,7 @@ generate_upgrade_list!(
scales_costs: false,
weight: 1.0,
remaining: usize::MAX,
enable: Some(world.register_system(|mut simulation: ResMut<Simulation>| {
install: Some(world.register_system(|mut simulation: ResMut<Simulation>| {
simulation.lines += 10.0;
})),
..default()
Expand Down

0 comments on commit 55266b7

Please sign in to comment.