From 352fce239e18f4331e80abf0f527f11af0ff3663 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sun, 18 Apr 2021 20:07:00 +0100 Subject: [PATCH] Allow trading items not sold by merchants --- assets/common/item_price_calculation.ron | 58 ++++++++++---------- common/src/comp/inventory/loadout_builder.rs | 14 +++-- common/src/comp/inventory/trade_pricing.rs | 40 ++++++++------ 3 files changed, 61 insertions(+), 51 deletions(-) diff --git a/assets/common/item_price_calculation.ron b/assets/common/item_price_calculation.ron index 5882174c4d..36638b304b 100644 --- a/assets/common/item_price_calculation.ron +++ b/assets/common/item_price_calculation.ron @@ -2,45 +2,45 @@ loot_tables: [ // balance the loot tables against each other (higher= more common= smaller price) // Weapons - (16.0, "common.loot_tables.weapons.starter"), - (12.0, "common.loot_tables.weapons.tier-0"), - (6.0, "common.loot_tables.weapons.tier-1"), - (4.0, "common.loot_tables.weapons.tier-2"), - (2.0, "common.loot_tables.weapons.tier-3"), - //(0.25, "common.loot_tables.weapons.tier-4"), - //(0.125, "common.loot_tables.weapons.tier-5"), - //(0.05, "common.loot_tables.weapons.cultist"), - //(0.125, "common.loot_tables.weapons.cave"), - //(0.0625, "common.loot_tables.weapons.legendary"), + (16.0, true, "common.loot_tables.weapons.starter"), + (12.0, true, "common.loot_tables.weapons.tier-0"), + (6.0, true, "common.loot_tables.weapons.tier-1"), + (4.0, true, "common.loot_tables.weapons.tier-2"), + (2.0, true, "common.loot_tables.weapons.tier-3"), + (1.0, false, "common.loot_tables.weapons.tier-4"), + (0.5, false, "common.loot_tables.weapons.tier-5"), + (0.05, false, "common.loot_tables.weapons.cultist"), + (0.125, false, "common.loot_tables.weapons.cave"), + (0.0625, false, "common.loot_tables.weapons.legendary"), // Armor - (20.0, "common.loot_tables.armor.cloth"), - (6.0, "common.loot_tables.armor.agile"), - (3.0, "common.loot_tables.armor.swift"), - (6.0, "common.loot_tables.armor.druid"), - (1.0, "common.loot_tables.armor.twigs"), - (1.0, "common.loot_tables.armor.twigsflowers"), - (1.0, "common.loot_tables.armor.twigsleaves"), - (0.5, "common.loot_tables.armor.plate"), - (0.25, "common.loot_tables.armor.steel"), - //(0.075, "common.loot_tables.armor.cultist"), + (20.0, true, "common.loot_tables.armor.cloth"), + (6.0, true, "common.loot_tables.armor.agile"), + (3.0, true, "common.loot_tables.armor.swift"), + (6.0, true, "common.loot_tables.armor.druid"), + (1.0, true, "common.loot_tables.armor.twigs"), + (1.0, true, "common.loot_tables.armor.twigsflowers"), + (1.0, true, "common.loot_tables.armor.twigsleaves"), + (0.5, true, "common.loot_tables.armor.plate"), + (0.25, false, "common.loot_tables.armor.steel"), + (0.075, false, "common.loot_tables.armor.cultist"), // Materials - (7.5, "common.loot_tables.materials.common"), - (8.0, "common.loot_tables.materials.underground"), + (7.5, true, "common.loot_tables.materials.common"), + (8.0, true, "common.loot_tables.materials.underground"), // Food - (0.3, "common.loot_tables.food.farm_ingredients"), - (0.4, "common.loot_tables.food.wild_ingredients"), - (0.2, "common.loot_tables.food.prepared"), + (0.3, true, "common.loot_tables.food.farm_ingredients"), + (0.4, true, "common.loot_tables.food.wild_ingredients"), + (0.2, true, "common.loot_tables.food.prepared"), // TODO: Change consumables when they are split up later - (1.0, "common.loot_tables.consumables"), - (0.5, "common.loot_tables.trading"), + (1.0, true, "common.loot_tables.consumables"), + (0.5, false, "common.loot_tables.trading"), ], // this is the amount of that good the most common item represents // so basically this table balances the goods against each other (higher=less valuable) good_scaling: [ - (Potions, 0.005), // common.items.consumable.potion_minor + (Potions, 0.0075), // common.items.consumable.potion_minor (Food, 0.1), // common.items.food.mushroom (Coin, 1.0), // common.items.utility.coins (Armor, 0.05), // common.items.armor.misc.pants.worker_blue - (Tools, 0.1), // common.items.weapons.staff.starter_staff + (Tools, 0.05), // common.items.weapons.staff.starter_staff (Ingredients, 0.25), // common.items.crafting_ing.leather_scraps ]) diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 0499af2d6b..dca1fb9b93 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -590,7 +590,8 @@ impl LoadoutBuilder { .expect("coins should be stackable"); *s = Some(coin_item); coins = 0; - } else if let Some(item_id) = TradePricing::random_item(Good::Armor, armor) + } else if let Some(item_id) = + TradePricing::random_item(Good::Armor, armor, true) { *s = Some(Item::new_from_asset_expect(&item_id)); } @@ -605,7 +606,8 @@ impl LoadoutBuilder { .unwrap_or_default() / 10.0; for i in bag1.slots_mut() { - if let Some(item_id) = TradePricing::random_item(Good::Tools, weapon) { + if let Some(item_id) = TradePricing::random_item(Good::Tools, weapon, true) + { *i = Some(Item::new_from_asset_expect(&item_id)); } } @@ -638,7 +640,7 @@ impl LoadoutBuilder { / 10.0; for i in bag2.slots_mut() { if let Some(item_id) = - TradePricing::random_item(Good::Ingredients, ingredients) + TradePricing::random_item(Good::Ingredients, ingredients, true) { *i = item_with_amount(&item_id, &mut ingredients); } @@ -658,7 +660,7 @@ impl LoadoutBuilder { .max(10000.0) / 10.0; for i in bag3.slots_mut() { - if let Some(item_id) = TradePricing::random_item(Good::Food, food) { + if let Some(item_id) = TradePricing::random_item(Good::Food, food, true) { *i = item_with_amount(&item_id, &mut food); } } @@ -672,7 +674,9 @@ impl LoadoutBuilder { .unwrap_or_default() / 10.0; for i in bag4.slots_mut() { - if let Some(item_id) = TradePricing::random_item(Good::Potions, potions) { + if let Some(item_id) = + TradePricing::random_item(Good::Potions, potions, true) + { *i = item_with_amount(&item_id, &mut potions); } } diff --git a/common/src/comp/inventory/trade_pricing.rs b/common/src/comp/inventory/trade_pricing.rs index 18dd3c2ac7..e1ba75bd74 100644 --- a/common/src/comp/inventory/trade_pricing.rs +++ b/common/src/comp/inventory/trade_pricing.rs @@ -10,7 +10,7 @@ use lazy_static::lazy_static; use serde::Deserialize; use tracing::{info, warn}; -type Entry = (String, f32); +type Entry = (String, f32, bool); type Entries = Vec; const PRICING_DEBUG: bool = false; @@ -72,7 +72,7 @@ impl From> for ProbabilityFile { #[derive(Debug, Deserialize)] struct TradingPriceFile { - pub loot_tables: Vec<(f32, String)>, + pub loot_tables: Vec<(f32, bool, String)>, pub good_scaling: Vec<(Good, f32)>, // the amount of Good equivalent to the most common item } @@ -168,7 +168,7 @@ impl TradePricing { } fn read() -> Self { - fn add(entryvec: &mut Entries, itemname: &str, probability: f32) { + fn add(entryvec: &mut Entries, itemname: &str, probability: f32, can_sell: bool) { let val = entryvec.iter_mut().find(|j| *j.0 == *itemname); if let Some(r) = val { if PRICING_DEBUG { @@ -179,13 +179,13 @@ impl TradePricing { if PRICING_DEBUG { info!("New {} {}", itemname, probability); } - entryvec.push((itemname.to_string(), probability)); + entryvec.push((itemname.to_string(), probability, can_sell)); } } fn sort_and_normalize(entryvec: &mut [Entry], scale: f32) { if !entryvec.is_empty() { entryvec.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap()); - if let Some((_, max_scale)) = entryvec.last() { + if let Some((_, max_scale, _)) = entryvec.last() { // most common item has frequency max_scale. avoid NaN let rescale = scale / max_scale; for i in entryvec.iter_mut() { @@ -210,9 +210,9 @@ impl TradePricing { if PRICING_DEBUG { info!(?i); } - let loot = ProbabilityFile::load_expect(&i.1); + let loot = ProbabilityFile::load_expect(&i.2); for j in loot.read().content.iter() { - add(&mut result.get_list_by_path_mut(&j.1), &j.1, i.0 * j.0); + add(&mut result.get_list_by_path_mut(&j.1), &j.1, i.0 * j.0, i.1); } } @@ -241,8 +241,8 @@ impl TradePricing { fn price_lookup(s: &TradePricing, name: &str) -> f32 { let vec = s.get_list_by_path(name); vec.iter() - .find(|(n, _)| n == name) - .map(|(_, freq)| 1.0 / freq) + .find(|(n, _, _)| n == name) + .map(|(_, freq, _)| 1.0 / freq) // even if we multiply by INVEST_FACTOR we need to remain above UNAVAILABLE_PRICE (add 1.0 to compensate rounding errors) .unwrap_or(TradePricing::UNAVAILABLE_PRICE/TradePricing::INVEST_FACTOR+1.0) } @@ -276,6 +276,7 @@ impl TradePricing { &mut result.get_list_by_path_mut(&e.output), &e.output, (e.amount as f32) / actual_cost * TradePricing::CRAFTING_FACTOR, + true, ); false } else { @@ -305,7 +306,7 @@ impl TradePricing { result } - fn random_item_impl(&self, good: Good, amount: f32) -> Option { + fn random_item_impl(&self, good: Good, amount: f32, selling: bool) -> Option { if good == Good::Coin { Some(TradePricing::COIN_ITEM.into()) } else { @@ -321,14 +322,19 @@ impl TradePricing { .find(|i| i.1.1 * amount >= 1.0) .map(|i| i.0) .unwrap_or(upper - 1); - let index = (rand::random::() * ((upper - lower) as f32)).floor() as usize + lower; - //.gen_range(lower..upper); - table.get(index).map(|i| i.0.clone()) + loop { + let index = + (rand::random::() * ((upper - lower) as f32)).floor() as usize + lower; + //.gen_range(lower..upper); + if table.get(index).map_or(false, |i| !selling || i.2) { + break table.get(index).map(|i| i.0.clone()); + } + } } } - pub fn random_item(good: Good, amount: f32) -> Option { - TRADE_PRICING.random_item_impl(good, amount) + pub fn random_item(good: Good, amount: f32, selling: bool) -> Option { + TRADE_PRICING.random_item_impl(good, amount, selling) } pub fn get_material(item: &str) -> (Good, f32) { @@ -350,7 +356,7 @@ impl TradePricing { use crate::comp::item::{armor, tool, Item, ItemKind}; // we pass the item and the inverse of the price to the closure - fn printvec(x: &str, e: &[(String, f32)], f: F) + fn printvec(x: &str, e: &[(String, f32, bool)], f: F) where F: Fn(&Item, f32) -> String, { @@ -437,7 +443,7 @@ mod tests { TradePricing::instance().print_sorted(); for _ in 0..5 { - if let Some(item_id) = TradePricing::random_item(Good::Armor, 5.0) { + if let Some(item_id) = TradePricing::random_item(Good::Armor, 5.0, false) { info!("Armor 5 {}", item_id); } }