diff --git a/common/src/bin/csv_export/main.rs b/common/src/bin/csv_export/main.rs index ed485fa80f..21bf6eec6c 100644 --- a/common/src/bin/csv_export/main.rs +++ b/common/src/bin/csv_export/main.rs @@ -8,7 +8,7 @@ use std::{ use structopt::StructOpt; use veloren_common::{ - assets::AssetExt, + assets::{self, AssetExt}, comp::{ self, item::{ @@ -278,102 +278,121 @@ fn loot_table(loot_table: &str) -> Result<(), Box> { fn entity_drops(entity_config: &str) -> Result<(), Box> { let mut wtr = csv::Writer::from_path("drop_table.csv")?; - wtr.write_record(&["Percent Chance", "Item Path", "Quantity"])?; - let entity_config = "common.entity.".to_owned() + entity_config; + fn write_entity_loot( + wtr: &mut csv::Writer, + asset_path: &str, + ) -> Result<(), Box> { + wtr.write_record(&["Percent Chance", "Item Path", "Quantity"])?; - let entity_config = EntityConfig::load_expect(&entity_config).read(); + let entity_config = EntityConfig::load_expect(&asset_path).read(); - // Create initial entry in drop table - let entry: (f32, LootSpec) = (1.0, entity_config.loot.clone()); + // Create initial entry in drop table + let entry: (f32, LootSpec) = (1.0, entity_config.loot.clone()); - let mut table = vec![entry]; + let mut table = vec![entry]; - // Keep converting loot table lootspecs into non-loot table lootspecs until no - // more loot tables - while table - .iter() - .any(|(_, loot_spec)| matches!(loot_spec, LootSpec::LootTable(_))) - { - // Partition table of loot specs into a table of items and nothings, and another - // table of loot tables - let (sub_tables, main_table): (Vec<_>, Vec<_>) = table - .into_iter() - .partition(|(_, loot_spec)| matches!(loot_spec, LootSpec::LootTable(_))); - table = main_table; + // Keep converting loot table lootspecs into non-loot table lootspecs until no + // more loot tables + while table + .iter() + .any(|(_, loot_spec)| matches!(loot_spec, LootSpec::LootTable(_))) + { + // Partition table of loot specs into a table of items and nothings, and another + // table of loot tables + let (sub_tables, main_table): (Vec<_>, Vec<_>) = table + .into_iter() + .partition(|(_, loot_spec)| matches!(loot_spec, LootSpec::LootTable(_))); + table = main_table; - // Change table of loot tables to only contain the string that loads the loot - // table - let sub_tables = sub_tables.iter().filter_map(|(chance, loot_spec)| { - if let LootSpec::LootTable(loot_table) = loot_spec { - Some((chance, loot_table)) - } else { - None - } - }); - for (chance, loot_table) in sub_tables { - let loot_table = Lottery::>::load_expect(loot_table).read(); - // Converts from lottery's weight addition for each consecutive entry to keep - // the weights as they are in the ron file - let loot_table: Vec<_> = loot_table - .iter() - .enumerate() - .map(|(i, (chance, item))| { - let chance = if let Some((next_chance, _)) = loot_table.iter().nth(i + 1) { - next_chance - chance - } else { - loot_table.total() - chance - }; - (chance, item) - }) - .collect(); - // Gets sum of all weights to use in normalization of entries - let weights_sum: f32 = loot_table.iter().map(|(chance, _)| chance).sum(); - // Normalizes each entry in sub-loot table - let loot_table = loot_table - .iter() - .map(|(chance, item)| (chance / weights_sum, item)); - for (sub_chance, &item) in loot_table { - // Multiplies normalized entry within each loot table by the chance for the loot - // table to drop in the above table - let entry = (chance * sub_chance, item.clone()); - table.push(entry); + // Change table of loot tables to only contain the string that loads the loot + // table + let sub_tables = sub_tables.iter().filter_map(|(chance, loot_spec)| { + if let LootSpec::LootTable(loot_table) = loot_spec { + Some((chance, loot_table)) + } else { + None + } + }); + for (chance, loot_table) in sub_tables { + let loot_table = Lottery::>::load_expect(loot_table).read(); + // Converts from lottery's weight addition for each consecutive entry to keep + // the weights as they are in the ron file + let loot_table: Vec<_> = loot_table + .iter() + .enumerate() + .map(|(i, (chance, item))| { + let chance = if let Some((next_chance, _)) = loot_table.iter().nth(i + 1) { + next_chance - chance + } else { + loot_table.total() - chance + }; + (chance, item) + }) + .collect(); + // Gets sum of all weights to use in normalization of entries + let weights_sum: f32 = loot_table.iter().map(|(chance, _)| chance).sum(); + // Normalizes each entry in sub-loot table + let loot_table = loot_table + .iter() + .map(|(chance, item)| (chance / weights_sum, item)); + for (sub_chance, &item) in loot_table { + // Multiplies normalized entry within each loot table by the chance for the loot + // table to drop in the above table + let entry = (chance * sub_chance, item.clone()); + table.push(entry); + } } } + + // Normalizes each item drop entry so that everything adds to 1 + let table_weight_sum: f32 = table.iter().map(|(chance, _)| chance).sum(); + let table = table + .iter() + .map(|(chance, item)| (chance / table_weight_sum, item)); + + for (chance, item) in table { + // Changes normalized weight to add to 100, and rounds at 2nd decimal + let percent_chance = chance + .mul(10_f32.powi(4)) + .round() + .div(10_f32.powi(2)) + .to_string(); + + let (item_asset, quantity) = match item { + LootSpec::Item(item) => (Some(item), "1".to_string()), + LootSpec::ItemQuantity(item, lower, upper) => { + (Some(item), format!("{}-{}", lower, upper)) + }, + LootSpec::LootTable(_) => panic!("Shouldn't exist"), + LootSpec::Nothing => (None, "-".to_string()), + }; + + let item = item_asset.map(|asset| Item::new_from_asset_expect(asset)); + + let item_name = if let Some(item) = &item { + item.name() + } else { + "Nothing" + }; + + wtr.write_record(&[&percent_chance, item_name, &quantity])? + } + + Ok(()) } - // Normalizes each item drop entry so that everything adds to 1 - let table_weight_sum: f32 = table.iter().map(|(chance, _)| chance).sum(); - let table = table - .iter() - .map(|(chance, item)| (chance / table_weight_sum, item)); - - for (chance, item) in table { - // Changes normalized weight to add to 100, and rounds at 2nd decimal - let percent_chance = chance - .mul(10_f32.powi(4)) - .round() - .div(10_f32.powi(2)) - .to_string(); - - let (item_asset, quantity) = match item { - LootSpec::Item(item) => (Some(item), "1".to_string()), - LootSpec::ItemQuantity(item, lower, upper) => { - (Some(item), format!("{}-{}", lower, upper)) - }, - LootSpec::LootTable(_) => panic!("Shouldn't exist"), - LootSpec::Nothing => (None, "-".to_string()), - }; - - let item = item_asset.map(|asset| Item::new_from_asset_expect(asset)); - - let item_name = if let Some(item) = &item { - item.name() - } else { - "Nothing" - }; - - wtr.write_record(&[&percent_chance, item_name, &quantity])? + if entity_config.eq_ignore_ascii_case("all") { + let configs = assets::load_dir::("common.entity", true) + .expect("Entity files moved somewhere else maybe?") + .ids(); + for config in configs { + wtr.write_record(&[config, "", ""])?; + write_entity_loot(&mut wtr, config)?; + } + } else { + let entity_config = "common.entity.".to_owned() + entity_config; + write_entity_loot(&mut wtr, &entity_config)?; } wtr.flush()?; @@ -405,7 +424,8 @@ fn main() { } else if args.function.eq_ignore_ascii_case("entity-drops") { let entity_config = get_input( "Specify the name of the entity to export loot drops to csv. Assumes entity config is \ - in directory: assets.common.entity.\n", + in directory: assets.common.entity.\nCan also use \"all\" to export loot from all \ + entity configs.\n", ); if let Err(e) = entity_drops(&entity_config) { println!("Error: {}\n", e)