NPCs can drop nothing

This commit is contained in:
ubruntu 2021-09-01 23:17:36 +00:00 committed by Samuel Keiffer
parent 82886dcd3f
commit bbc366e1b3
13 changed files with 63 additions and 46 deletions

View File

@ -59,6 +59,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Glider dimensions somewhat increased overall - Glider dimensions somewhat increased overall
- Dungeon difficulty level starts at 1 instead of 0 - Dungeon difficulty level starts at 1 instead of 0
- The radius of the safe zone around the starting town has been doubled - The radius of the safe zone around the starting town has been doubled
- NPCs can sometimes drop no loot at all
### Removed ### Removed

View File

@ -1,5 +1,4 @@
[ [
(0.1, Item("common.items.food.meat.bird_large_raw")), (0.1, Item("common.items.food.meat.bird_large_raw")),
(1.0, Item("common.items.crafting_ing.animal_misc.phoenix_feather")), (1.0, Item("common.items.crafting_ing.animal_misc.phoenix_feather")),
] ]

View File

@ -1,4 +1,5 @@
[ [
(1.0, Item("common.items.food.meat.tough_raw")), (1.0, Item("common.items.food.meat.tough_raw")),
(3.0, Item("common.items.crafting_ing.hide.carapace")), (3.0, Item("common.items.crafting_ing.hide.carapace")),
] ]

View File

@ -1,5 +1,4 @@
[ [
(4.0, LootTable("common.loot_tables.creature.quad_low.generic")), (4.0, LootTable("common.loot_tables.creature.quad_low.generic")),
(2.0, Item("common.items.crafting_ing.animal_misc.sharp_fang")), (2.0, Item("common.items.crafting_ing.animal_misc.sharp_fang")),
] ]

View File

@ -1,5 +1,4 @@
[ [
(1.0, ItemQuantity("common.items.crafting_ing.animal_misc.fur", 1, 3)), (1.0, ItemQuantity("common.items.crafting_ing.animal_misc.fur", 1, 3)),
(0.25, LootTable("common.loot_tables.creature.quad_low.generic")), (0.25, LootTable("common.loot_tables.creature.quad_low.generic")),
] ]

View File

@ -0,0 +1,4 @@
[
// No loot is dropped
(1.0, None),
]

View File

@ -267,6 +267,7 @@ fn loot_table(loot_table: &str) -> Result<(), Box<dyn Error>> {
LootSpec::LootTable(table) => { LootSpec::LootTable(table) => {
wtr.write_record(&[&chance, "LootTable", table, "", ""])? wtr.write_record(&[&chance, "LootTable", table, "", ""])?
}, },
LootSpec::None => wtr.write_record(&[&chance, "None", "", ""])?,
} }
} }

View File

@ -468,8 +468,9 @@ fn loot_table(loot_table: &str) -> Result<(), Box<dyn Error>> {
.expect("No loot table") .expect("No loot table")
.to_string(), .to_string(),
), ),
"None" => LootSpec::None,
a => panic!( a => panic!(
"Loot specifier kind must be either \"Item\" or \"LootTable\"\n{}", "Loot specifier kind must be either \"Item\", \"LootTable\", or \"None\"\n{}",
a a
), ),
}; };

View File

@ -795,7 +795,7 @@ impl Item {
pub fn slot_mut(&mut self, slot: usize) -> Option<&mut InvSlot> { self.slots.get_mut(slot) } 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<Self> { pub fn try_reclaim_from_block(block: Block) -> Option<Self> {
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() } pub fn ability_spec(&self) -> Option<&AbilitySpec> { self.item_def.ability_spec.as_ref() }

View File

@ -113,6 +113,7 @@ impl From<Vec<(f32, LootSpec<String>)>> for ProbabilityFile {
.collect::<Vec<_>>() .collect::<Vec<_>>()
.into_iter() .into_iter()
}, },
LootSpec::None => Vec::new().into_iter(),
}) })
.collect(), .collect(),
} }

View File

@ -255,12 +255,16 @@ impl EntityInfo {
match loot { match loot {
LootKind::Item(asset) => { 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) => { LootKind::LootTable(asset) => {
let table = Lottery::<LootSpec<String>>::load_expect(&asset); if let Ok(table) = Lottery::<LootSpec<String>>::load(&asset) {
let drop = table.read().choose().to_item(); if let Some(drop) = table.read().choose().to_item() {
self = self.with_loot_drop(drop); self = self.with_loot_drop(drop);
}
}
}, },
LootKind::Uninit => {}, LootKind::Uninit => {},
} }

View File

@ -84,26 +84,32 @@ pub enum LootSpec<T: AsRef<str>> {
ItemQuantity(T, u32, u32), ItemQuantity(T, u32, u32),
/// Loot table /// Loot table
LootTable(T), LootTable(T),
/// No loot given
None,
} }
impl<T: AsRef<str>> LootSpec<T> { impl<T: AsRef<str>> LootSpec<T> {
pub fn to_item(&self) -> Item { pub fn to_item(&self) -> Option<Item> {
match self { 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) => { Self::ItemQuantity(item, lower, upper) => {
let range = *lower..=*upper; let range = *lower..=*upper;
let quantity = thread_rng().gen_range(range); let quantity = thread_rng().gen_range(range);
let mut item = Item::new_from_asset_expect(item.as_ref()); if let Ok(mut item) = Item::new_from_asset(item.as_ref()) {
// TODO: Handle multiple of an item that is unstackable // TODO: Handle multiple of an item that is unstackable
if item.set_amount(quantity).is_err() { if item.set_amount(quantity).is_err() {
warn!("Tried to set quantity on non stackable item"); warn!("Tried to set quantity on non stackable item");
}
Some(item)
} else {
None
} }
item
}, },
Self::LootTable(table) => Lottery::<LootSpec<String>>::load_expect(table.as_ref()) Self::LootTable(table) => Lottery::<LootSpec<String>>::load_expect(table.as_ref())
.read() .read()
.choose() .choose()
.to_item(), .to_item(),
Self::None => None,
} }
} }
} }
@ -141,6 +147,7 @@ mod tests {
Lottery::<LootSpec<String>>::load_expect_cloned(loot_table); Lottery::<LootSpec<String>>::load_expect_cloned(loot_table);
validate_table_contents(loot_table); validate_table_contents(loot_table);
}, },
LootSpec::None => {},
} }
} }
} }

View File

@ -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::<comp::ItemDrop>(); let mut item_drops = state.ecs().write_storage::<comp::ItemDrop>();
item_drops.remove(entity).map_or_else( item_drops.remove(entity).map_or_else(
|| lottery().read().choose().to_item(), || lottery().read().choose().to_item(),
|item_drop| item_drop.0, |item_drop| Some(item_drop.0),
) )
}; } {
let pos = state.ecs().read_storage::<comp::Pos>().get(entity).cloned();
let vel = state.ecs().read_storage::<comp::Vel>().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::<comp::Pos>().get(entity).cloned(); _ => object::Body::BeastMeat,
let vel = state.ecs().read_storage::<comp::Vel>().get(entity).cloned(); })
if let Some(pos) = pos { .maybe_with(vel)
let _ = state .with(item)
.create_object(comp::Pos(pos.0 + Vec3::unit_z() * 0.25), match old_body { .build();
Some(common::comp::Body::Humanoid(_)) => object::Body::Pouch, } else {
Some(common::comp::Body::BipedSmall(_)) => object::Body::Pouch, error!(
Some(common::comp::Body::Golem(_)) => object::Body::Chest, ?entity,
Some(common::comp::Body::QuadrupedSmall(_)) => object::Body::SmallMeat, "Entity doesn't have a position, no bag is being dropped"
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"
)
} }
true true