diff --git a/CHANGELOG.md b/CHANGELOG.md index 621684c177..4bd0027d35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added Lottery system for loot - Added context-sensitive crosshair - Announce alias changes to all clients - Dance animation diff --git a/assets/common/loot_table.ron b/assets/common/loot_table.ron new file mode 100644 index 0000000000..089f042417 --- /dev/null +++ b/assets/common/loot_table.ron @@ -0,0 +1,103 @@ +[ + // All loot rates go here + // miscellaneous + (0.4, "common.items.velorite"), + (0.6, "common.items.veloritefrag"), + (1.5, "common.items.potion_minor"), + (1, "common.items.collar"), + // swords + (0.1, "common.items.weapons.sword.starter_sword"), + (0.1, "common.items.weapons.sword.wood_sword"), + (0.1, "common.items.weapons.sword.short_sword_0"), + (0.06, "common.items.weapons.sword.greatsword_2h_dam-0"), + (0.06, "common.items.weapons.sword.greatsword_2h_dam-1"), + (0.06, "common.items.weapons.sword.greatsword_2h_dam-2"), + (0.03, "common.items.weapons.sword.greatsword_2h_simple-0"), + (0.03, "common.items.weapons.sword.greatsword_2h_simple-1"), + (0.03, "common.items.weapons.sword.greatsword_2h_simple-2"), + (0.01, "common.items.weapons.sword.greatsword_2h_orn-0"), + (0.01, "common.items.weapons.sword.greatsword_2h_orn-1"), + (0.01, "common.items.weapons.sword.greatsword_2h_orn-2"), + (0.04, "common.items.weapons.sword.long_2h_dam-0"), + (0.04, "common.items.weapons.sword.long_2h_dam-1"), + (0.04, "common.items.weapons.sword.long_2h_dam-2"), + (0.04, "common.items.weapons.sword.long_2h_dam-3"), + (0.04, "common.items.weapons.sword.long_2h_dam-4"), + (0.04, "common.items.weapons.sword.long_2h_dam-5"), + (0.02, "common.items.weapons.sword.long_2h_simple-0"), + (0.02, "common.items.weapons.sword.long_2h_simple-1"), + (0.02, "common.items.weapons.sword.long_2h_simple-2"), + (0.02, "common.items.weapons.sword.long_2h_simple-3"), + (0.02, "common.items.weapons.sword.long_2h_simple-4"), + (0.02, "common.items.weapons.sword.long_2h_simple-5"), + (0.007, "common.items.weapons.sword.long_2h_orn-0"), + (0.007, "common.items.weapons.sword.long_2h_orn-1"), + (0.007, "common.items.weapons.sword.long_2h_orn-2"), + (0.007, "common.items.weapons.sword.long_2h_orn-3"), + (0.007, "common.items.weapons.sword.long_2h_orn-4"), + (0.007, "common.items.weapons.sword.long_2h_orn-5"), + // axes + (1, "common.items.weapons.axe.starter_axe"), + // staves + (1, "common.items.weapons.staff.staff_nature"), + (1, "common.items.weapons.staff.starter_staff"), + // hammers + (1, "common.items.weapons.hammer.starter_hammer"), + // bows + (1, "common.items.weapons.bow.starter_bow"), + // belts + (0.2, "common.items.armor.belt.cloth_blue_0"), + (0.2, "common.items.armor.belt.cloth_green_0"), + (0.2, "common.items.armor.belt.cloth_purple_0"), + (0.15, "common.items.armor.belt.leather_0"), + (0.15, "common.items.armor.belt.leather_2"), + (0.1, "common.items.armor.belt.steel_0"), + (0.05, "common.items.armor.belt.plate_0"), + // chests + (0.2, "common.items.armor.chest.cloth_blue_0"), + (0.2, "common.items.armor.chest.cloth_green_0"), + (0.2, "common.items.armor.chest.cloth_purple_0"), + (0.15, "common.items.armor.chest.leather_0"), + (0.15, "common.items.armor.chest.leather_2"), + (0.1, "common.items.armor.chest.steel_0"), + (0.05, "common.items.armor.chest.plate_green_0"), + // shoes + (0.2, "common.items.armor.foot.cloth_blue_0"), + (0.2, "common.items.armor.foot.cloth_green_0"), + (0.2, "common.items.armor.foot.cloth_purple_0"), + (0.15, "common.items.armor.foot.leather_0"), + (0.15, "common.items.armor.foot.leather_2"), + (0.1, "common.items.armor.foot.steel_0"), + (0.05, "common.items.armor.foot.plate_0"), + // pants + (0.2, "common.items.armor.pants.cloth_blue_0"), + (0.2, "common.items.armor.pants.cloth_green_0"), + (0.2, "common.items.armor.pants.cloth_purple_0"), + (0.15, "common.items.armor.pants.green_0"), + (0.15, "common.items.armor.pants.leather_0"), + (0.1, "common.items.armor.pants.steel_0"), + (0.05, "common.items.armor.pants.plate_green_0"), + // shoulders + (0.2, "common.items.armor.shoulder.cloth_blue_0"), + (0.2, "common.items.armor.shoulder.cloth_green_0"), + (0.2, "common.items.armor.shoulder.cloth_purple_0"), + (0.1, "common.items.armor.shoulder.leather_0"), + (0.1, "common.items.armor.shoulder.leather_1"), + (0.1, "common.items.armor.shoulder.leather_2"), + (0.1, "common.items.armor.shoulder.steel_0"), + (0.05, "common.items.armor.shoulder.plate_0"), + //gloves + (0.2, "common.items.armor.hand.cloth_blue_0"), + (0.2, "common.items.armor.hand.cloth_green_0"), + (0.2, "common.items.armor.hand.cloth_purple_0"), + (0.15, "common.items.armor.hand.leather_0"), + (0.15, "common.items.armor.hand.leather_2"), + (0.1, "common.items.armor.hand.steel_0"), + (0.05, "common.items.armor.hand.plate_0"), + // rings + (0.6, "common.items.armor.ring.ring_0"), + // capes + (0.6, "common.items.armor.back.short_0"), + // necks + (0.6, "common.items.armor.neck.neck_0"), +] diff --git a/common/src/comp/inventory/item/lottery.rs b/common/src/comp/inventory/item/lottery.rs new file mode 100644 index 0000000000..ea202aef5d --- /dev/null +++ b/common/src/comp/inventory/item/lottery.rs @@ -0,0 +1,47 @@ +use crate::assets::{self, Asset}; +use rand::prelude::*; +use std::{fs::File, io::BufReader}; + +// Generate a random float between 0 and 1 +pub fn rand() -> f32 { + let mut rng = rand::thread_rng(); + rng.gen() +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Lottery { + items: Vec<(f32, T)>, + total: f32, +} + +impl Asset for Lottery { + const ENDINGS: &'static [&'static str] = &["ron"]; + + fn parse(buf_reader: BufReader) -> Result { + ron::de::from_reader::, Vec<(f32, String)>>(buf_reader) + .map(|items| Lottery::from_rates(items.into_iter())) + .map_err(assets::Error::parse_error) + } +} + +impl Lottery { + pub fn from_rates(items: impl Iterator) -> Self { + let mut total = 0.0; + let items = items + .map(|(rate, item)| { + total += rate; + (total - rate, item) + }) + .collect(); + Self { items, total } + } + + pub fn choose(&self) -> &T { + let x = rand() * self.total; + &self.items[self + .items + .binary_search_by(|(y, _)| y.partial_cmp(&x).unwrap()) + .unwrap_or_else(|i| i.saturating_sub(1))] + .1 + } +} diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index bb1f94add5..0065b3bb55 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -1,4 +1,5 @@ pub mod armor; +pub mod lottery; pub mod tool; // Reexports @@ -9,7 +10,6 @@ use crate::{ effect::Effect, terrain::{Block, BlockKind}, }; -use rand::seq::SliceRandom; use specs::{Component, FlaggedStorage}; use specs_idvs::IDVStorage; use std::{fs::File, io::BufReader}; @@ -179,118 +179,12 @@ impl Item { }, BlockKind::ShortGrass => Some(assets::load_expect_cloned("common.items.grasses.short")), BlockKind::Coconut => Some(assets::load_expect_cloned("common.items.coconut")), - BlockKind::Chest => Some(assets::load_expect_cloned( - [ - //miscellaneous - "common.items.velorite", - "common.items.veloritefrag", - "common.items.potion_minor", - "common.items.collar", - //swords - "common.items.weapons.sword.starter_sword", - "common.items.weapons.sword.wood_sword", - "common.items.weapons.sword.short_sword_0", - "common.items.weapons.sword.greatsword_2h_simple-0", - "common.items.weapons.sword.greatsword_2h_simple-1", - "common.items.weapons.sword.greatsword_2h_simple-2", - "common.items.weapons.sword.long_2h_simple-0", - "common.items.weapons.sword.long_2h_simple-1", - "common.items.weapons.sword.long_2h_simple-2", - "common.items.weapons.sword.long_2h_simple-3", - "common.items.weapons.sword.long_2h_simple-4", - "common.items.weapons.sword.long_2h_simple-5", - "common.items.weapons.sword.greatsword_2h_dam-0", - "common.items.weapons.sword.greatsword_2h_dam-1", - "common.items.weapons.sword.greatsword_2h_dam-2", - "common.items.weapons.sword.greatsword_2h_orn-0", - "common.items.weapons.sword.greatsword_2h_orn-1", - "common.items.weapons.sword.greatsword_2h_orn-2", - "common.items.weapons.sword.long_2h_dam-0", - "common.items.weapons.sword.long_2h_dam-1", - "common.items.weapons.sword.long_2h_dam-2", - "common.items.weapons.sword.long_2h_dam-3", - "common.items.weapons.sword.long_2h_dam-4", - "common.items.weapons.sword.long_2h_dam-5", - "common.items.weapons.sword.long_2h_orn-0", - "common.items.weapons.sword.long_2h_orn-1", - "common.items.weapons.sword.long_2h_orn-2", - "common.items.weapons.sword.long_2h_orn-3", - "common.items.weapons.sword.long_2h_orn-4", - "common.items.weapons.sword.long_2h_orn-5", - "common.items.weapons.sword.long_2h_simple-0", - "common.items.weapons.sword.long_2h_simple-1", - "common.items.weapons.sword.long_2h_simple-2", - "common.items.weapons.sword.long_2h_simple-3", - "common.items.weapons.sword.long_2h_simple-4", - "common.items.weapons.sword.long_2h_simple-5", - //axes - "common.items.weapons.axe.starter_axe", - //staves - "common.items.weapons.staff.staff_nature", - "common.items.weapons.staff.starter_staff", - //hammers - "common.items.weapons.hammer.starter_hammer", - //bows - "common.items.weapons.bow.starter_bow", - //belts - "common.items.armor.belt.plate_0", - "common.items.armor.belt.steel_0", - "common.items.armor.belt.leather_0", - "common.items.armor.belt.leather_2", - "common.items.armor.belt.cloth_blue_0", - "common.items.armor.belt.cloth_green_0", - "common.items.armor.belt.cloth_purple_0", - //chests - "common.items.armor.chest.plate_green_0", - "common.items.armor.chest.leather_0", - "common.items.armor.chest.steel_0", - "common.items.armor.chest.leather_2", - "common.items.armor.chest.cloth_blue_0", - "common.items.armor.chest.cloth_green_0", - "common.items.armor.chest.cloth_purple_0", - //shoes - "common.items.armor.foot.plate_0", - "common.items.armor.foot.steel_0", - "common.items.armor.foot.leather_0", - "common.items.armor.foot.leather_2", - "common.items.armor.foot.cloth_blue_0", - "common.items.armor.foot.cloth_green_0", - "common.items.armor.foot.cloth_purple_0", - //pants - "common.items.armor.pants.plate_green_0", - "common.items.armor.pants.green_0", - "common.items.armor.pants.leather_0", - "common.items.armor.pants.steel_0", - "common.items.armor.pants.cloth_blue_0", - "common.items.armor.pants.cloth_green_0", - "common.items.armor.pants.cloth_purple_0", - //shoulders - "common.items.armor.shoulder.plate_0", - "common.items.armor.shoulder.steel_0", - "common.items.armor.shoulder.leather_1", - "common.items.armor.shoulder.leather_0", - "common.items.armor.shoulder.leather_2", - "common.items.armor.shoulder.cloth_blue_0", - "common.items.armor.shoulder.cloth_green_0", - "common.items.armor.shoulder.cloth_purple_0", - //gloves - "common.items.armor.hand.leather_0", - "common.items.armor.hand.leather_2", - "common.items.armor.hand.steel_0", - "common.items.armor.hand.plate_0", - "common.items.armor.hand.cloth_blue_0", - "common.items.armor.hand.cloth_green_0", - "common.items.armor.hand.cloth_purple_0", - //rings - "common.items.armor.ring.ring_0", - //capes - "common.items.armor.back.short_0", - //necks - "common.items.armor.neck.neck_0", - ] - .choose(&mut rand::thread_rng()) - .unwrap(), // Can't fail - )), + BlockKind::Chest => { + let chosen = assets::load_expect::>("common.items.loot_table"); + let chosen = chosen.choose(); + + Some(assets::load_expect_cloned(chosen)) + }, _ => None, } } diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index d2edbf6b44..c2c6fbc9ef 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -1,7 +1,7 @@ use crate::{client::Client, Server, SpawnPoint, StateExt}; use common::{ assets, - comp::{self, object, Body, HealthChange, HealthSource, Item, Player, Stats}, + comp::{self, item::lottery::Lottery, object, Body, HealthChange, HealthSource, Player, Stats}, msg::{PlayerListUpdate, ServerMsg}, state::BlockChange, sync::{Uid, WorldSyncExt}, @@ -9,7 +9,6 @@ use common::{ terrain::{Block, TerrainGrid}, vol::{ReadVol, Vox}, }; -use rand::seq::SliceRandom; use specs::{join::Join, Entity as EcsEntity, WorldExt}; use tracing::error; use vek::Vec3; @@ -112,179 +111,10 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc item_drops.remove(entity); item_drop.0 } else { - assets::load_expect_cloned::( - [ - "common.items.cheese", - "common.items.apple", - "common.items.cheese", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.collar", - "common.items.cheese", - "common.items.apple", - "common.items.cheese", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.collar", - "common.items.cheese", - "common.items.apple", - "common.items.cheese", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.collar", - "common.items.veloritefrag", - "common.items.cheese", - "common.items.apple", - "common.items.cheese", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.collar", - "common.items.cheese", - "common.items.apple", - "common.items.cheese", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.collar", - "common.items.cheese", - "common.items.apple", - "common.items.cheese", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.cheese", - "common.items.mushroom", - "common.items.apple", - "common.items.collar", - "common.items.collar", - "common.items.collar", - "common.items.collar", - "common.items.collar", - "common.items.veloritefrag", - "common.items.veloritefrag", - "common.items.veloritefrag", - "common.items.veloritefrag", - "common.items.veloritefrag", - "common.items.veloritefrag", - "common.items.veloritefrag", - "common.items.veloritefrag", - "common.items.velorite", - "common.items.armor.ring.ring_0", - "common.items.armor.neck.neck_0", - "common.items.mushroom", - "common.items.coconut", - "common.items.coconut", - "common.items.coconut", - "common.items.coconut", - "common.items.coconut", - "common.items.potion_minor", - "common.items.potion_minor", - "common.items.potion_minor", - "common.items.potion_minor", - "common.items.potion_minor", - "common.items.potion_minor", - "common.items.weapons.tool.broom", - "common.items.weapons.tool.shovel-1", - "common.items.weapons.staff.staff_nature", - "common.items.flowers.yellow", - "common.items.armor.pants.worker_blue_0", - "common.items.armor.chest.worker_yellow_0", - "common.items.armor.chest.worker_green_0", - "common.items.armor.chest.worker_orange_0", - "common.items.armor.back.short_0", - "common.items.weapons.staff.staff_nature", - "common.items.weapons.sword.starter_sword", - "common.items.weapons.axe.starter_axe", - "common.items.weapons.staff.staff_nature", - "common.items.weapons.hammer.starter_hammer", - "common.items.weapons.bow.starter_bow", - "common.items.weapons.staff.starter_staff", - "common.items.weapons.sword.starter_sword", - "common.items.weapons.axe.starter_axe", - "common.items.weapons.staff.staff_nature", - "common.items.weapons.hammer.starter_hammer", - "common.items.weapons.bow.starter_bow", - "common.items.weapons.staff.starter_staff", - "common.items.weapons.sword.starter_sword", - "common.items.weapons.axe.starter_axe", - "common.items.weapons.staff.staff_nature", - "common.items.weapons.hammer.starter_hammer", - "common.items.weapons.bow.starter_bow", - "common.items.weapons.staff.starter_staff", - "common.items.weapons.sword.greatsword_2h_simple-0", - "common.items.weapons.sword.greatsword_2h_simple-1", - "common.items.weapons.sword.greatsword_2h_simple-2", - "common.items.weapons.sword.long_2h_simple-0", - "common.items.weapons.sword.long_2h_simple-1", - "common.items.weapons.sword.long_2h_simple-2", - "common.items.weapons.sword.long_2h_simple-3", - "common.items.weapons.sword.long_2h_simple-4", - "common.items.weapons.sword.long_2h_simple-5", - ] - .choose(&mut rand::thread_rng()) - .unwrap(), - ) + let chosen = assets::load_expect::>("common.items.loot_table"); + let chosen = chosen.choose(); + + assets::load_expect_cloned(chosen) }; let _ = state.ecs().write_storage().insert(entity, item);