diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ab40c8844..b7214e8592 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Glider dimensions somewhat increased overall - Dungeon difficulty level starts at 1 instead of 0 - The radius of the safe zone around the starting town has been doubled +- NPCs can sometimes drop no loot at all ### Removed diff --git a/assets/common/loot_tables/creature/bird_large/phoenix.ron b/assets/common/loot_tables/creature/bird_large/phoenix.ron index 705dc8f853..322b00887c 100644 --- a/assets/common/loot_tables/creature/bird_large/phoenix.ron +++ b/assets/common/loot_tables/creature/bird_large/phoenix.ron @@ -1,5 +1,4 @@ [ (0.1, Item("common.items.food.meat.bird_large_raw")), (1.0, Item("common.items.crafting_ing.animal_misc.phoenix_feather")), - ] \ No newline at end of file diff --git a/assets/common/loot_tables/creature/quad_low/carapace.ron b/assets/common/loot_tables/creature/quad_low/carapace.ron index 25c1784e51..1adcfebb28 100644 --- a/assets/common/loot_tables/creature/quad_low/carapace.ron +++ b/assets/common/loot_tables/creature/quad_low/carapace.ron @@ -1,4 +1,5 @@ [ (1.0, Item("common.items.food.meat.tough_raw")), (3.0, Item("common.items.crafting_ing.hide.carapace")), + ] \ No newline at end of file diff --git a/assets/common/loot_tables/creature/quad_low/fanged.ron b/assets/common/loot_tables/creature/quad_low/fanged.ron index cdd0845c50..2dc17ae2f5 100644 --- a/assets/common/loot_tables/creature/quad_low/fanged.ron +++ b/assets/common/loot_tables/creature/quad_low/fanged.ron @@ -1,5 +1,4 @@ [ (4.0, LootTable("common.loot_tables.creature.quad_low.generic")), (2.0, Item("common.items.crafting_ing.animal_misc.sharp_fang")), - ] \ No newline at end of file diff --git a/assets/common/loot_tables/creature/quad_small/fur.ron b/assets/common/loot_tables/creature/quad_small/fur.ron index 90ed478a78..4146078a72 100644 --- a/assets/common/loot_tables/creature/quad_small/fur.ron +++ b/assets/common/loot_tables/creature/quad_small/fur.ron @@ -1,5 +1,4 @@ [ (1.0, ItemQuantity("common.items.crafting_ing.animal_misc.fur", 1, 3)), (0.25, LootTable("common.loot_tables.creature.quad_low.generic")), - ] \ No newline at end of file diff --git a/assets/common/loot_tables/nothing.ron b/assets/common/loot_tables/nothing.ron new file mode 100644 index 0000000000..613a16e6e8 --- /dev/null +++ b/assets/common/loot_tables/nothing.ron @@ -0,0 +1,4 @@ +[ + // No loot is dropped + (1.0, None), +] diff --git a/common/src/bin/csv_export/main.rs b/common/src/bin/csv_export/main.rs index eeb98d6f6d..d1b8776864 100644 --- a/common/src/bin/csv_export/main.rs +++ b/common/src/bin/csv_export/main.rs @@ -267,6 +267,7 @@ fn loot_table(loot_table: &str) -> Result<(), Box> { LootSpec::LootTable(table) => { wtr.write_record(&[&chance, "LootTable", table, "", ""])? }, + LootSpec::None => wtr.write_record(&[&chance, "None", "", ""])?, } } diff --git a/common/src/bin/csv_import/main.rs b/common/src/bin/csv_import/main.rs index 3d09ba9269..67f10dfa87 100644 --- a/common/src/bin/csv_import/main.rs +++ b/common/src/bin/csv_import/main.rs @@ -468,8 +468,9 @@ fn loot_table(loot_table: &str) -> Result<(), Box> { .expect("No loot table") .to_string(), ), + "None" => LootSpec::None, a => panic!( - "Loot specifier kind must be either \"Item\" or \"LootTable\"\n{}", + "Loot specifier kind must be either \"Item\", \"LootTable\", or \"None\"\n{}", a ), }; diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index c425a8056b..a432977fcd 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -795,7 +795,7 @@ impl Item { pub fn slot_mut(&mut self, slot: usize) -> Option<&mut InvSlot> { self.slots.get_mut(slot) } pub fn try_reclaim_from_block(block: Block) -> Option { - Some(block.get_sprite()?.collectible_id()?.to_item()) + block.get_sprite()?.collectible_id()?.to_item() } pub fn ability_spec(&self) -> Option<&AbilitySpec> { self.item_def.ability_spec.as_ref() } diff --git a/common/src/comp/inventory/trade_pricing.rs b/common/src/comp/inventory/trade_pricing.rs index 9ca594da8f..c7619149ed 100644 --- a/common/src/comp/inventory/trade_pricing.rs +++ b/common/src/comp/inventory/trade_pricing.rs @@ -113,6 +113,7 @@ impl From)>> for ProbabilityFile { .collect::>() .into_iter() }, + LootSpec::None => Vec::new().into_iter(), }) .collect(), } diff --git a/common/src/generation.rs b/common/src/generation.rs index afa5279b4a..3a3fba904f 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -255,12 +255,16 @@ impl EntityInfo { match loot { LootKind::Item(asset) => { - self = self.with_loot_drop(Item::new_from_asset_expect(&asset)); + if let Ok(item) = Item::new_from_asset(&asset) { + self = self.with_loot_drop(item); + } }, LootKind::LootTable(asset) => { - let table = Lottery::>::load_expect(&asset); - let drop = table.read().choose().to_item(); - self = self.with_loot_drop(drop); + if let Ok(table) = Lottery::>::load(&asset) { + if let Some(drop) = table.read().choose().to_item() { + self = self.with_loot_drop(drop); + } + } }, LootKind::Uninit => {}, } diff --git a/common/src/lottery.rs b/common/src/lottery.rs index df6da0e618..6e5d103641 100644 --- a/common/src/lottery.rs +++ b/common/src/lottery.rs @@ -84,26 +84,32 @@ pub enum LootSpec> { ItemQuantity(T, u32, u32), /// Loot table LootTable(T), + /// No loot given + None, } impl> LootSpec { - pub fn to_item(&self) -> Item { + pub fn to_item(&self) -> Option { match self { - Self::Item(item) => Item::new_from_asset_expect(item.as_ref()), + Self::Item(item) => Item::new_from_asset(item.as_ref()).ok(), Self::ItemQuantity(item, lower, upper) => { let range = *lower..=*upper; let quantity = thread_rng().gen_range(range); - let mut item = Item::new_from_asset_expect(item.as_ref()); - // TODO: Handle multiple of an item that is unstackable - if item.set_amount(quantity).is_err() { - warn!("Tried to set quantity on non stackable item"); + if let Ok(mut item) = Item::new_from_asset(item.as_ref()) { + // TODO: Handle multiple of an item that is unstackable + if item.set_amount(quantity).is_err() { + warn!("Tried to set quantity on non stackable item"); + } + Some(item) + } else { + None } - item }, Self::LootTable(table) => Lottery::>::load_expect(table.as_ref()) .read() .choose() .to_item(), + Self::None => None, } } } @@ -141,6 +147,7 @@ mod tests { Lottery::>::load_expect_cloned(loot_table); validate_table_contents(loot_table); }, + LootSpec::None => {}, } } } diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 6e96b72cd9..56978c7380 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -529,41 +529,41 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc }) }; - let item = { + if let Some(item) = { let mut item_drops = state.ecs().write_storage::(); item_drops.remove(entity).map_or_else( || lottery().read().choose().to_item(), - |item_drop| item_drop.0, + |item_drop| Some(item_drop.0), ) - }; + } { + let pos = state.ecs().read_storage::().get(entity).cloned(); + let vel = state.ecs().read_storage::().get(entity).cloned(); + if let Some(pos) = pos { + let _ = state + .create_object(comp::Pos(pos.0 + Vec3::unit_z() * 0.25), match old_body { + Some(common::comp::Body::Humanoid(_)) => object::Body::Pouch, + Some(common::comp::Body::BipedSmall(_)) => object::Body::Pouch, + Some(common::comp::Body::Golem(_)) => object::Body::Chest, + Some(common::comp::Body::QuadrupedSmall(_)) => object::Body::SmallMeat, + Some(common::comp::Body::FishMedium(_)) + | Some(common::comp::Body::FishSmall(_)) => object::Body::FishMeat, + Some(common::comp::Body::QuadrupedMedium(_)) => object::Body::BeastMeat, + Some(common::comp::Body::BipedLarge(_)) + | Some(common::comp::Body::QuadrupedLow(_)) => object::Body::ToughMeat, + Some(common::comp::Body::BirdLarge(_)) + | Some(common::comp::Body::BirdMedium(_)) => object::Body::BirdMeat, - let pos = state.ecs().read_storage::().get(entity).cloned(); - let vel = state.ecs().read_storage::().get(entity).cloned(); - if let Some(pos) = pos { - let _ = state - .create_object(comp::Pos(pos.0 + Vec3::unit_z() * 0.25), match old_body { - Some(common::comp::Body::Humanoid(_)) => object::Body::Pouch, - Some(common::comp::Body::BipedSmall(_)) => object::Body::Pouch, - Some(common::comp::Body::Golem(_)) => object::Body::Chest, - Some(common::comp::Body::QuadrupedSmall(_)) => object::Body::SmallMeat, - Some(common::comp::Body::FishMedium(_)) - | Some(common::comp::Body::FishSmall(_)) => object::Body::FishMeat, - Some(common::comp::Body::QuadrupedMedium(_)) => object::Body::BeastMeat, - Some(common::comp::Body::BipedLarge(_)) - | Some(common::comp::Body::QuadrupedLow(_)) => object::Body::ToughMeat, - Some(common::comp::Body::BirdLarge(_)) - | Some(common::comp::Body::BirdMedium(_)) => object::Body::BirdMeat, - - _ => object::Body::BeastMeat, - }) - .maybe_with(vel) - .with(item) - .build(); - } else { - error!( - ?entity, - "Entity doesn't have a position, no bag is being dropped" - ) + _ => object::Body::BeastMeat, + }) + .maybe_with(vel) + .with(item) + .build(); + } else { + error!( + ?entity, + "Entity doesn't have a position, no bag is being dropped" + ) + } } true