From e832fa86f18386d699908a78d2c659c94e9accd0 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sat, 5 Jun 2021 02:10:15 +0300 Subject: [PATCH 001/180] Add entity template and make 'loadouts' singular --- assets/common/entity/test.ron | 31 +++++++++++++++++++ .../common/{loadouts => loadout}/default.ron | 0 .../dungeon/tier-0/gnarling.ron | 0 .../dungeon/tier-1/adlet_bow.ron | 0 .../dungeon/tier-1/adlet_spear.ron | 0 .../dungeon/tier-2/sahagin.ron | 0 .../dungeon/tier-3/haniwa.ron | 0 .../dungeon/tier-4/myrmidon.ron | 0 .../dungeon/tier-5/beastmaster.ron | 0 .../dungeon/tier-5/husk.ron | 0 .../dungeon/tier-5/warlock.ron | 0 .../dungeon/tier-5/warlord.ron | 0 assets/common/{loadouts => loadout}/test.ron | 0 .../{loadouts => loadout}/village/guard.ron | 0 .../village/merchant.ron | 0 .../village/villager.ron | 0 common/src/comp/inventory/loadout_builder.rs | 30 +++++++++--------- 17 files changed, 46 insertions(+), 15 deletions(-) create mode 100644 assets/common/entity/test.ron rename assets/common/{loadouts => loadout}/default.ron (100%) rename assets/common/{loadouts => loadout}/dungeon/tier-0/gnarling.ron (100%) rename assets/common/{loadouts => loadout}/dungeon/tier-1/adlet_bow.ron (100%) rename assets/common/{loadouts => loadout}/dungeon/tier-1/adlet_spear.ron (100%) rename assets/common/{loadouts => loadout}/dungeon/tier-2/sahagin.ron (100%) rename assets/common/{loadouts => loadout}/dungeon/tier-3/haniwa.ron (100%) rename assets/common/{loadouts => loadout}/dungeon/tier-4/myrmidon.ron (100%) rename assets/common/{loadouts => loadout}/dungeon/tier-5/beastmaster.ron (100%) rename assets/common/{loadouts => loadout}/dungeon/tier-5/husk.ron (100%) rename assets/common/{loadouts => loadout}/dungeon/tier-5/warlock.ron (100%) rename assets/common/{loadouts => loadout}/dungeon/tier-5/warlord.ron (100%) rename assets/common/{loadouts => loadout}/test.ron (100%) rename assets/common/{loadouts => loadout}/village/guard.ron (100%) rename assets/common/{loadouts => loadout}/village/merchant.ron (100%) rename assets/common/{loadouts => loadout}/village/villager.ron (100%) diff --git a/assets/common/entity/test.ron b/assets/common/entity/test.ron new file mode 100644 index 0000000000..64b15c7be2 --- /dev/null +++ b/assets/common/entity/test.ron @@ -0,0 +1,31 @@ +{ + /// Name of Entity + name: Some("Paddy"), + + /// Body + /// Can be Exact (Body with all fields e.g BodyType, Species, Hair color and such) + /// or Random (will use random if available for this Body) + /// or RandomWith (will use random_with if available for this Body) + body: Humanoid(Random), + + /// Loot + /// Can be Item (with asset_specifier for item) + /// or LootTable (with asset_specifier for loot table) + loot: LootTable("common.loot_tables.humanoids"), + + /// Main and second tools + /// Can be Option (with asset_specifier for item) + /// or Choice + /// (array of pairs with weight of choosing some item and Option) + main_tool: Some(Item("common.items.weapons.axe_1h.orichalcum-0")), + second_tool: None, + + /// Loadout Config as Option (with asset_specifier for loadout) + loadout_config: Some(Loadout("common.loadout.village.merchant")), + + /// Skillset Config as Option (with asset_specifier for skillset) + skillset_config: None, + + /// Meta Info (level, alignment, agency, etc) + meta: {}, +} diff --git a/assets/common/loadouts/default.ron b/assets/common/loadout/default.ron similarity index 100% rename from assets/common/loadouts/default.ron rename to assets/common/loadout/default.ron diff --git a/assets/common/loadouts/dungeon/tier-0/gnarling.ron b/assets/common/loadout/dungeon/tier-0/gnarling.ron similarity index 100% rename from assets/common/loadouts/dungeon/tier-0/gnarling.ron rename to assets/common/loadout/dungeon/tier-0/gnarling.ron diff --git a/assets/common/loadouts/dungeon/tier-1/adlet_bow.ron b/assets/common/loadout/dungeon/tier-1/adlet_bow.ron similarity index 100% rename from assets/common/loadouts/dungeon/tier-1/adlet_bow.ron rename to assets/common/loadout/dungeon/tier-1/adlet_bow.ron diff --git a/assets/common/loadouts/dungeon/tier-1/adlet_spear.ron b/assets/common/loadout/dungeon/tier-1/adlet_spear.ron similarity index 100% rename from assets/common/loadouts/dungeon/tier-1/adlet_spear.ron rename to assets/common/loadout/dungeon/tier-1/adlet_spear.ron diff --git a/assets/common/loadouts/dungeon/tier-2/sahagin.ron b/assets/common/loadout/dungeon/tier-2/sahagin.ron similarity index 100% rename from assets/common/loadouts/dungeon/tier-2/sahagin.ron rename to assets/common/loadout/dungeon/tier-2/sahagin.ron diff --git a/assets/common/loadouts/dungeon/tier-3/haniwa.ron b/assets/common/loadout/dungeon/tier-3/haniwa.ron similarity index 100% rename from assets/common/loadouts/dungeon/tier-3/haniwa.ron rename to assets/common/loadout/dungeon/tier-3/haniwa.ron diff --git a/assets/common/loadouts/dungeon/tier-4/myrmidon.ron b/assets/common/loadout/dungeon/tier-4/myrmidon.ron similarity index 100% rename from assets/common/loadouts/dungeon/tier-4/myrmidon.ron rename to assets/common/loadout/dungeon/tier-4/myrmidon.ron diff --git a/assets/common/loadouts/dungeon/tier-5/beastmaster.ron b/assets/common/loadout/dungeon/tier-5/beastmaster.ron similarity index 100% rename from assets/common/loadouts/dungeon/tier-5/beastmaster.ron rename to assets/common/loadout/dungeon/tier-5/beastmaster.ron diff --git a/assets/common/loadouts/dungeon/tier-5/husk.ron b/assets/common/loadout/dungeon/tier-5/husk.ron similarity index 100% rename from assets/common/loadouts/dungeon/tier-5/husk.ron rename to assets/common/loadout/dungeon/tier-5/husk.ron diff --git a/assets/common/loadouts/dungeon/tier-5/warlock.ron b/assets/common/loadout/dungeon/tier-5/warlock.ron similarity index 100% rename from assets/common/loadouts/dungeon/tier-5/warlock.ron rename to assets/common/loadout/dungeon/tier-5/warlock.ron diff --git a/assets/common/loadouts/dungeon/tier-5/warlord.ron b/assets/common/loadout/dungeon/tier-5/warlord.ron similarity index 100% rename from assets/common/loadouts/dungeon/tier-5/warlord.ron rename to assets/common/loadout/dungeon/tier-5/warlord.ron diff --git a/assets/common/loadouts/test.ron b/assets/common/loadout/test.ron similarity index 100% rename from assets/common/loadouts/test.ron rename to assets/common/loadout/test.ron diff --git a/assets/common/loadouts/village/guard.ron b/assets/common/loadout/village/guard.ron similarity index 100% rename from assets/common/loadouts/village/guard.ron rename to assets/common/loadout/village/guard.ron diff --git a/assets/common/loadouts/village/merchant.ron b/assets/common/loadout/village/merchant.ron similarity index 100% rename from assets/common/loadouts/village/merchant.ron rename to assets/common/loadout/village/merchant.ron diff --git a/assets/common/loadouts/village/villager.ron b/assets/common/loadout/village/villager.ron similarity index 100% rename from assets/common/loadouts/village/villager.ron rename to assets/common/loadout/village/villager.ron diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index f373b4d800..ed508f39e8 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -485,7 +485,7 @@ impl LoadoutBuilder { /// Set default armor items for the loadout. This may vary with game /// updates, but should be safe defaults for a new character. #[must_use] - pub fn defaults(self) -> Self { self.apply_asset_expect("common.loadouts.default") } + pub fn defaults(self) -> Self { self.apply_asset_expect("common.loadout.default") } /// Builds loadout of creature when spawned #[must_use] @@ -529,45 +529,45 @@ impl LoadoutBuilder { match config { LoadoutConfig::Gnarling => match active_tool_kind { Some(ToolKind::Bow | ToolKind::Staff | ToolKind::Spear) => { - builder.apply_asset_expect("common.loadouts.dungeon.tier-0.gnarling") + builder.apply_asset_expect("common.loadout.dungeon.tier-0.gnarling") }, _ => builder, }, LoadoutConfig::Adlet => match active_tool_kind { Some(ToolKind::Bow) => { - builder.apply_asset_expect("common.loadouts.dungeon.tier-1.adlet_bow") + builder.apply_asset_expect("common.loadout.dungeon.tier-1.adlet_bow") }, Some(ToolKind::Spear | ToolKind::Staff) => { - builder.apply_asset_expect("common.loadouts.dungeon.tier-1.adlet_spear") + builder.apply_asset_expect("common.loadout.dungeon.tier-1.adlet_spear") }, _ => builder, }, LoadoutConfig::Sahagin => { - builder.apply_asset_expect("common.loadouts.dungeon.tier-2.sahagin") + builder.apply_asset_expect("common.loadout.dungeon.tier-2.sahagin") }, LoadoutConfig::Haniwa => { - builder.apply_asset_expect("common.loadouts.dungeon.tier-3.haniwa") + builder.apply_asset_expect("common.loadout.dungeon.tier-3.haniwa") }, LoadoutConfig::Myrmidon => { - builder.apply_asset_expect("common.loadouts.dungeon.tier-4.myrmidon") + builder.apply_asset_expect("common.loadout.dungeon.tier-4.myrmidon") }, LoadoutConfig::Husk => { - builder.apply_asset_expect("common.loadouts.dungeon.tier-5.husk") + builder.apply_asset_expect("common.loadout.dungeon.tier-5.husk") }, LoadoutConfig::Beastmaster => { - builder.apply_asset_expect("common.loadouts.dungeon.tier-5.beastmaster") + builder.apply_asset_expect("common.loadout.dungeon.tier-5.beastmaster") }, LoadoutConfig::Warlord => { - builder.apply_asset_expect("common.loadouts.dungeon.tier-5.warlord") + builder.apply_asset_expect("common.loadout.dungeon.tier-5.warlord") }, LoadoutConfig::Warlock => { - builder.apply_asset_expect("common.loadouts.dungeon.tier-5.warlock") + builder.apply_asset_expect("common.loadout.dungeon.tier-5.warlock") }, LoadoutConfig::Villager => builder - .apply_asset_expect("common.loadouts.village.villager") + .apply_asset_expect("common.loadout.village.villager") .bag(ArmorSlot::Bag1, Some(make_potion_bag(10))), LoadoutConfig::Guard => builder - .apply_asset_expect("common.loadouts.village.guard") + .apply_asset_expect("common.loadout.village.guard") .bag(ArmorSlot::Bag1, Some(make_potion_bag(25))), LoadoutConfig::Merchant => { let mut backpack = @@ -685,7 +685,7 @@ impl LoadoutBuilder { } } builder - .apply_asset_expect("common.loadouts.village.merchant") + .apply_asset_expect("common.loadout.village.merchant") .back(Some(backpack)) .bag(ArmorSlot::Bag1, Some(bag1)) .bag(ArmorSlot::Bag2, Some(bag2)) @@ -947,7 +947,7 @@ mod tests { // It just load everything that could // TODO: add some checks, e.g. that Armor(Head) key correspond // to Item with ItemKind Head(_) - let loadouts = LoadoutList::load_expect_cloned("common.loadouts.*").0; + let loadouts = LoadoutList::load_expect_cloned("common.loadout.*").0; for loadout in loadouts { let spec = loadout.0; for (key, entry) in spec { From 102f6d3338bc7027f7ec64fb3518083662ac0540 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sat, 5 Jun 2021 17:30:20 +0300 Subject: [PATCH 002/180] EntityInfo assetization * Rename skillset_config to skillset_preset * Rename loadout_config to loadout_preset * Add skillset_config for asset_specifier of skillset * Add loadout_config for asset_specifier of loadout --- common/src/generation.rs | 22 ++++++++++++++++++---- common/src/states/basic_summon.rs | 9 +++++---- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/common/src/generation.rs b/common/src/generation.rs index e922f604c7..e91763659a 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -25,8 +25,10 @@ pub struct EntityInfo { // TODO: Properly give NPCs skills pub level: Option, pub loot_drop: Option, - pub loadout_config: Option, - pub skillset_config: Option, + pub loadout_config: Option, + pub loadout_preset: Option, + pub skillset_config: Option, + pub skillset_preset: Option, pub pet: Option>, // we can't use DHashMap, do we want to move that into common? pub trading_information: Option, @@ -49,7 +51,9 @@ impl EntityInfo { level: None, loot_drop: None, loadout_config: None, + loadout_preset: None, skillset_config: None, + skillset_preset: None, pet: None, trading_information: None, } @@ -117,16 +121,26 @@ impl EntityInfo { self } - pub fn with_loadout_config(mut self, config: LoadoutConfig) -> Self { + pub fn with_loadout_config(mut self, config: String) -> Self { self.loadout_config = Some(config); self } - pub fn with_skillset_config(mut self, config: SkillSetConfig) -> Self { + pub fn with_loadout_preset(mut self, preset: LoadoutConfig) -> Self { + self.loadout_preset = Some(preset); + self + } + + pub fn with_skillset_config(mut self, config: String) -> Self { self.skillset_config = Some(config); self } + pub fn with_skillset_preset(mut self, preset: SkillSetConfig) -> Self { + self.skillset_preset = Some(preset); + self + } + pub fn with_automatic_name(mut self) -> Self { let npc_names = NPC_NAMES.read(); self.name = match &self.body { diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 4abd7f8d8e..857ab191ed 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -79,14 +79,14 @@ impl CharacterBehavior for Data { let loadout = LoadoutBuilder::build_loadout( body, None, - self.static_data.summon_info.loadout_config, + self.static_data.summon_info.loadout_preset, None, ) .build(); let stats = comp::Stats::new("Summon".to_string()); let skill_set = SkillSetBuilder::build_skillset( &None, - self.static_data.summon_info.skillset_config, + self.static_data.summon_info.skillset_preset, ) .build(); @@ -175,6 +175,7 @@ pub struct SummonInfo { body: comp::Body, scale: Option, health_scaling: u16, - loadout_config: Option, - skillset_config: Option, + // TODO: use assets for specifying skills and loadouts? + loadout_preset: Option, + skillset_preset: Option, } From e153cbe20e1cfb0d63ca80335ef6a3394a85e7d0 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sat, 5 Jun 2021 18:20:12 +0300 Subject: [PATCH 003/180] Rename LoadoutBuilder::apply_asset_expect to LoadoutBuilder::with_asset_expect --- common/src/comp/inventory/loadout_builder.rs | 32 ++++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index ed508f39e8..87013950cc 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -397,7 +397,7 @@ impl LoadoutBuilder { pub fn from_asset_expect(asset_specifier: &str) -> Self { let loadout = Self::new(); - loadout.apply_asset_expect(asset_specifier) + loadout.with_asset_expect(asset_specifier) } /// # Usage @@ -409,7 +409,7 @@ impl LoadoutBuilder { /// 2) Will panic if path to item specified in loadout file doesn't exist /// 3) Will panic while runs in tests and asset doesn't have "correct" form #[must_use] - pub fn apply_asset_expect(mut self, asset_specifier: &str) -> Self { + pub fn with_asset_expect(mut self, asset_specifier: &str) -> Self { let spec = LoadoutSpec::load_expect(asset_specifier).read().0.clone(); for (key, entry) in spec { let item = match entry.try_to_item(asset_specifier) { @@ -485,7 +485,7 @@ impl LoadoutBuilder { /// Set default armor items for the loadout. This may vary with game /// updates, but should be safe defaults for a new character. #[must_use] - pub fn defaults(self) -> Self { self.apply_asset_expect("common.loadout.default") } + pub fn defaults(self) -> Self { self.with_asset_expect("common.loadout.default") } /// Builds loadout of creature when spawned #[must_use] @@ -529,45 +529,45 @@ impl LoadoutBuilder { match config { LoadoutConfig::Gnarling => match active_tool_kind { Some(ToolKind::Bow | ToolKind::Staff | ToolKind::Spear) => { - builder.apply_asset_expect("common.loadout.dungeon.tier-0.gnarling") + builder.with_asset_expect("common.loadout.dungeon.tier-0.gnarling") }, _ => builder, }, LoadoutConfig::Adlet => match active_tool_kind { Some(ToolKind::Bow) => { - builder.apply_asset_expect("common.loadout.dungeon.tier-1.adlet_bow") + builder.with_asset_expect("common.loadout.dungeon.tier-1.adlet_bow") }, Some(ToolKind::Spear | ToolKind::Staff) => { - builder.apply_asset_expect("common.loadout.dungeon.tier-1.adlet_spear") + builder.with_asset_expect("common.loadout.dungeon.tier-1.adlet_spear") }, _ => builder, }, LoadoutConfig::Sahagin => { - builder.apply_asset_expect("common.loadout.dungeon.tier-2.sahagin") + builder.with_asset_expect("common.loadout.dungeon.tier-2.sahagin") }, LoadoutConfig::Haniwa => { - builder.apply_asset_expect("common.loadout.dungeon.tier-3.haniwa") + builder.with_asset_expect("common.loadout.dungeon.tier-3.haniwa") }, LoadoutConfig::Myrmidon => { - builder.apply_asset_expect("common.loadout.dungeon.tier-4.myrmidon") + builder.with_asset_expect("common.loadout.dungeon.tier-4.myrmidon") }, LoadoutConfig::Husk => { - builder.apply_asset_expect("common.loadout.dungeon.tier-5.husk") + builder.with_asset_expect("common.loadout.dungeon.tier-5.husk") }, LoadoutConfig::Beastmaster => { - builder.apply_asset_expect("common.loadout.dungeon.tier-5.beastmaster") + builder.with_asset_expect("common.loadout.dungeon.tier-5.beastmaster") }, LoadoutConfig::Warlord => { - builder.apply_asset_expect("common.loadout.dungeon.tier-5.warlord") + builder.with_asset_expect("common.loadout.dungeon.tier-5.warlord") }, LoadoutConfig::Warlock => { - builder.apply_asset_expect("common.loadout.dungeon.tier-5.warlock") + builder.with_asset_expect("common.loadout.dungeon.tier-5.warlock") }, LoadoutConfig::Villager => builder - .apply_asset_expect("common.loadout.village.villager") + .with_asset_expect("common.loadout.village.villager") .bag(ArmorSlot::Bag1, Some(make_potion_bag(10))), LoadoutConfig::Guard => builder - .apply_asset_expect("common.loadout.village.guard") + .with_asset_expect("common.loadout.village.guard") .bag(ArmorSlot::Bag1, Some(make_potion_bag(25))), LoadoutConfig::Merchant => { let mut backpack = @@ -685,7 +685,7 @@ impl LoadoutBuilder { } } builder - .apply_asset_expect("common.loadout.village.merchant") + .with_asset_expect("common.loadout.village.merchant") .back(Some(backpack)) .bag(ArmorSlot::Bag1, Some(bag1)) .bag(ArmorSlot::Bag2, Some(bag2)) From aad65c615917e0e9c6f8bc26f14ffec07d9981c0 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sat, 5 Jun 2021 19:22:51 +0300 Subject: [PATCH 004/180] Move traveler loadout declaration to asset * New loadout/world/traveler.ron file to specify traveler loadout * LoadoutBuilder::with_asset_expect now can use passed rng to choose items --- assets/common/loadout/world/traveler.ron | 28 +++++++++ common/src/comp/inventory/loadout_builder.rs | 64 +++++++++++--------- server/src/rtsim/entity.rs | 59 +----------------- 3 files changed, 66 insertions(+), 85 deletions(-) create mode 100644 assets/common/loadout/world/traveler.ron diff --git a/assets/common/loadout/world/traveler.ron b/assets/common/loadout/world/traveler.ron new file mode 100644 index 0000000000..c2126e5496 --- /dev/null +++ b/assets/common/loadout/world/traveler.ron @@ -0,0 +1,28 @@ +({ + ActiveMainhand: Choice([ + (1.0, Some(Item("common.items.weapons.sword.wood-2"))), + (1.0, Some(Item("common.items.weapons.sword.starter"))), + (1.0, Some(Item("common.items.weapons.sword.wood-0"))), + (1.0, Some(Item("common.items.weapons.bow.starter"))), + (1.0, Some(Item("common.items.weapons.bow.hardwood-2"))), + ]), + + Armor(Chest): Item("common.items.npc_armor.chest.leather_blue"), + Armor(Legs): Item("common.items.npc_armor.pants.leather_blue"), + Armor(Shoulders): Item("common.items.armor.swift.shoulder"), + + Armor(Back): Choice([ + (1.0, Some(Item("common.items.armor.hide.rawhide.back"))), + (1.0, Some(Item("common.items.armor.misc.back.backpack"))), + (1.0, Some(Item("common.items.npc_armor.back.backpack_blue"))), + (1.0, Some(Item("common.items.npc_armor.back.leather_blue"))), + (1.0, None), + ]), + + Lantern: Choice([ + (1.0, Some(Item("common.items.lantern.black_0"))), + (1.0, Some(Item("common.items.lantern.blue_0"))), + (1.0, Some(Item("common.items.lantern.green_0"))), + (1.0, Some(Item("common.items.lantern.red_0"))), + ]), +}) diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 87013950cc..5453a9b0c2 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -73,16 +73,16 @@ enum ItemSpec { } impl ItemSpec { - fn try_to_item(&self, asset_specifier: &str) -> Option { + fn try_to_item(&self, asset_specifier: &str, rng: &mut impl Rng) -> Option { match self { ItemSpec::Item(specifier) => Some(Item::new_from_asset_expect(&specifier)), ItemSpec::Choice(items) => { - choose(&items, asset_specifier) + choose(&items, asset_specifier, rng) .as_ref() .and_then(|e| match e { - entry @ ItemSpec::Item { .. } => entry.try_to_item(asset_specifier), - choice @ ItemSpec::Choice { .. } => choice.try_to_item(asset_specifier), + entry @ ItemSpec::Item { .. } => entry.try_to_item(asset_specifier, rng), + choice @ ItemSpec::Choice { .. } => choice.try_to_item(asset_specifier, rng), }) }, } @@ -108,10 +108,12 @@ impl ItemSpec { } } -fn choose<'a>(items: &'a [(f32, Option)], asset_specifier: &str) -> &'a Option { - let mut rng = rand::thread_rng(); - - items.choose_weighted(&mut rng, |item| item.0).map_or_else( +fn choose<'a>( + items: &'a [(f32, Option)], + asset_specifier: &str, + rng: &mut impl Rng, +) -> &'a Option { + items.choose_weighted(rng, |item| item.0).map_or_else( |err| match err { WeightedError::NoItem | WeightedError::AllWeightsZero => &None, WeightedError::InvalidWeight => { @@ -394,10 +396,15 @@ impl LoadoutBuilder { } #[must_use] - pub fn from_asset_expect(asset_specifier: &str) -> Self { + pub fn from_asset_expect(asset_specifier: &str, rng: Option<&mut impl Rng>) -> Self { let loadout = Self::new(); - loadout.with_asset_expect(asset_specifier) + if let Some(rng) = rng { + loadout.with_asset_expect(asset_specifier, rng) + } else { + let rng = &mut rand::thread_rng(); + loadout.with_asset_expect(asset_specifier, rng) + } } /// # Usage @@ -409,10 +416,10 @@ impl LoadoutBuilder { /// 2) Will panic if path to item specified in loadout file doesn't exist /// 3) Will panic while runs in tests and asset doesn't have "correct" form #[must_use] - pub fn with_asset_expect(mut self, asset_specifier: &str) -> Self { + pub fn with_asset_expect(mut self, asset_specifier: &str, rng: &mut impl Rng) -> Self { let spec = LoadoutSpec::load_expect(asset_specifier).read().0.clone(); for (key, entry) in spec { - let item = match entry.try_to_item(asset_specifier) { + let item = match entry.try_to_item(asset_specifier, rng) { Some(item) => item, None => continue, }; @@ -485,7 +492,10 @@ impl LoadoutBuilder { /// Set default armor items for the loadout. This may vary with game /// updates, but should be safe defaults for a new character. #[must_use] - pub fn defaults(self) -> Self { self.with_asset_expect("common.loadout.default") } + pub fn defaults(self) -> Self { + let rng = &mut rand::thread_rng(); + self.with_asset_expect("common.loadout.default", rng) + } /// Builds loadout of creature when spawned #[must_use] @@ -523,51 +533,52 @@ impl LoadoutBuilder { } }); // Creates rest of loadout + let rng = &mut rand::thread_rng(); let loadout_builder = if let Some(config) = config { let builder = Self::new().active_mainhand(active_item); // NOTE: we apply asset after active mainhand so asset has ability override it match config { LoadoutConfig::Gnarling => match active_tool_kind { Some(ToolKind::Bow | ToolKind::Staff | ToolKind::Spear) => { - builder.with_asset_expect("common.loadout.dungeon.tier-0.gnarling") + builder.with_asset_expect("common.loadout.dungeon.tier-0.gnarling", rng) }, _ => builder, }, LoadoutConfig::Adlet => match active_tool_kind { Some(ToolKind::Bow) => { - builder.with_asset_expect("common.loadout.dungeon.tier-1.adlet_bow") + builder.with_asset_expect("common.loadout.dungeon.tier-1.adlet_bow", rng) }, Some(ToolKind::Spear | ToolKind::Staff) => { - builder.with_asset_expect("common.loadout.dungeon.tier-1.adlet_spear") + builder.with_asset_expect("common.loadout.dungeon.tier-1.adlet_spear", rng) }, _ => builder, }, LoadoutConfig::Sahagin => { - builder.with_asset_expect("common.loadout.dungeon.tier-2.sahagin") + builder.with_asset_expect("common.loadout.dungeon.tier-2.sahagin", rng) }, LoadoutConfig::Haniwa => { - builder.with_asset_expect("common.loadout.dungeon.tier-3.haniwa") + builder.with_asset_expect("common.loadout.dungeon.tier-3.haniwa", rng) }, LoadoutConfig::Myrmidon => { - builder.with_asset_expect("common.loadout.dungeon.tier-4.myrmidon") + builder.with_asset_expect("common.loadout.dungeon.tier-4.myrmidon", rng) }, LoadoutConfig::Husk => { - builder.with_asset_expect("common.loadout.dungeon.tier-5.husk") + builder.with_asset_expect("common.loadout.dungeon.tier-5.husk", rng) }, LoadoutConfig::Beastmaster => { - builder.with_asset_expect("common.loadout.dungeon.tier-5.beastmaster") + builder.with_asset_expect("common.loadout.dungeon.tier-5.beastmaster", rng) }, LoadoutConfig::Warlord => { - builder.with_asset_expect("common.loadout.dungeon.tier-5.warlord") + builder.with_asset_expect("common.loadout.dungeon.tier-5.warlord", rng) }, LoadoutConfig::Warlock => { - builder.with_asset_expect("common.loadout.dungeon.tier-5.warlock") + builder.with_asset_expect("common.loadout.dungeon.tier-5.warlock", rng) }, LoadoutConfig::Villager => builder - .with_asset_expect("common.loadout.village.villager") + .with_asset_expect("common.loadout.village.villager", rng) .bag(ArmorSlot::Bag1, Some(make_potion_bag(10))), LoadoutConfig::Guard => builder - .with_asset_expect("common.loadout.village.guard") + .with_asset_expect("common.loadout.village.guard", rng) .bag(ArmorSlot::Bag1, Some(make_potion_bag(25))), LoadoutConfig::Merchant => { let mut backpack = @@ -618,7 +629,6 @@ impl LoadoutBuilder { } } } - let mut rng = rand::thread_rng(); let mut item_with_amount = |item_id: &str, amount: &mut f32| { if *amount > 0.0 { let mut item = Item::new_from_asset_expect(item_id); @@ -685,7 +695,7 @@ impl LoadoutBuilder { } } builder - .with_asset_expect("common.loadout.village.merchant") + .with_asset_expect("common.loadout.village.merchant", rng) .back(Some(backpack)) .bag(ArmorSlot::Bag1, Some(bag1)) .bag(ArmorSlot::Bag2, Some(bag2)) diff --git a/server/src/rtsim/entity.rs b/server/src/rtsim/entity.rs index 940297025d..5405f10cea 100644 --- a/server/src/rtsim/entity.rs +++ b/server/src/rtsim/entity.rs @@ -77,65 +77,8 @@ impl Entity { pub fn get_loadout(&self) -> comp::inventory::loadout::Loadout { let mut rng = self.rng(PERM_LOADOUT); - let main_tool = comp::Item::new_from_asset_expect( - (&[ - "common.items.weapons.sword.wood-2", - "common.items.weapons.sword.starter", - "common.items.weapons.sword.wood-0", - "common.items.weapons.bow.starter", - "common.items.weapons.bow.hardwood-2", - ]) - .choose(&mut rng) - .unwrap(), - ); - let back = match rng.gen_range(0..5) { - 0 => Some(comp::Item::new_from_asset_expect( - "common.items.armor.hide.rawhide.back", - )), - 1 => Some(comp::Item::new_from_asset_expect( - "common.items.armor.misc.back.backpack", - )), - 2 => Some(comp::Item::new_from_asset_expect( - "common.items.npc_armor.back.backpack_blue", - )), - 3 => Some(comp::Item::new_from_asset_expect( - "common.items.npc_armor.back.leather_blue", - )), - _ => None, - }; - - let lantern = match rng.gen_range(0..4) { - 0 => Some(comp::Item::new_from_asset_expect( - "common.items.lantern.black_0", - )), - 1 => Some(comp::Item::new_from_asset_expect( - "common.items.lantern.blue_0", - )), - 2 => Some(comp::Item::new_from_asset_expect( - "common.items.lantern.green_0", - )), - _ => Some(comp::Item::new_from_asset_expect( - "common.items.lantern.red_0", - )), - }; - - let chest = Some(comp::Item::new_from_asset_expect( - "common.items.npc_armor.chest.leather_blue", - )); - let pants = Some(comp::Item::new_from_asset_expect( - "common.items.npc_armor.pants.leather_blue", - )); - let shoulder = Some(comp::Item::new_from_asset_expect( - "common.items.armor.hide.leather.shoulder", - )); - - LoadoutBuilder::build_loadout(self.get_body(), Some(main_tool), None, None) - .back(back) - .lantern(lantern) - .chest(chest) - .pants(pants) - .shoulder(shoulder) + LoadoutBuilder::from_asset_expect("common.loadout.world.traveler", &mut rng) .bag( comp::inventory::slot::ArmorSlot::Bag1, Some(comp::inventory::loadout_builder::make_potion_bag(100)), From 5f3eaddb70c8621e2851a86ce9e02adfc9873886 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sat, 5 Jun 2021 21:05:31 +0300 Subject: [PATCH 005/180] Split LodoutBuilder::build_loadout LoadoutBuilder::build_loadout is a function which has four parameters and 3 of them are Option<>, and although fourth (body) isn't Option<>, it's optional too because it is used only in some combinations of another arguments. Because these combinations produces quirky code flow, it will be better to split it to different methods. So we did following changes to remove it and rewrite code that was using it to use better methods. * Introduce LoadoutPreset as new LoadoutConfig, currently it's only used in Summon ability, because SummonInfo uses Copy and we can't specify String for specifying asset path for loadout. Everything else is rewritten to use asset path to create loadouts. * More builder methods for LoadoutBuilder. Namely: - from_default which is used in server/src/cmd.rs in "/spawn" command. - with_default_equipment, with_default_maintool to use default loadout for specific body - with_preset to use LoadoutPreset * Add new make_loadout field with `fn (loadout_builder, trading_info) -> loadout_builder` to EntityInfo which allows to lazily construct loadout without modifying LoadoutBuilder code * Fix Merchants not having trade site We had heuristic that if something has Merchant LoadoutConfig - it's merchant, which can be false, especially if we create Merchant loadout lazily As side note, we do same check for Guards and it fails too. Too fix it, we introduce new agent::Mark, which explicitly specifies kind of agent for entity * `LoadoutBuilder::build_loadout` was written in a such way that depending on main_tool you will have different loadout. Turns out it was this way only for Adlets though and this behaviour is reproduced by specifying different loadouts directly in world code. --- assets/common/entity/test.ron | 6 +- common/src/comp/agent.rs | 6 + common/src/comp/inventory/loadout_builder.rs | 429 +++++-------------- common/src/generation.rs | 48 ++- common/src/states/basic_summon.rs | 20 +- server/src/cmd.rs | 2 +- server/src/rtsim/entity.rs | 2 +- server/src/sys/terrain.rs | 66 ++- world/src/site/dungeon/mod.rs | 69 +-- world/src/site/settlement/mod.rs | 177 +++++++- 10 files changed, 413 insertions(+), 412 deletions(-) diff --git a/assets/common/entity/test.ron b/assets/common/entity/test.ron index 64b15c7be2..2a70be337c 100644 --- a/assets/common/entity/test.ron +++ b/assets/common/entity/test.ron @@ -6,7 +6,7 @@ /// Can be Exact (Body with all fields e.g BodyType, Species, Hair color and such) /// or Random (will use random if available for this Body) /// or RandomWith (will use random_with if available for this Body) - body: Humanoid(Random), + // body: Humanoid(Random), /// Loot /// Can be Item (with asset_specifier for item) @@ -24,8 +24,8 @@ loadout_config: Some(Loadout("common.loadout.village.merchant")), /// Skillset Config as Option (with asset_specifier for skillset) - skillset_config: None, + // skillset_config: None, /// Meta Info (level, alignment, agency, etc) - meta: {}, + // meta: {}, } diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index 3fade71b8c..8b792f361f 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -32,6 +32,12 @@ pub enum Alignment { Passive, } +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Mark { + Merchant, + Guard, +} + impl Alignment { // Always attacks pub fn hostile_towards(self, other: Alignment) -> bool { diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 5453a9b0c2..8e49b3c4ae 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -7,12 +7,11 @@ use crate::{ inventory::{ loadout::Loadout, slot::{ArmorSlot, EquipSlot}, - trade_pricing::TradePricing, }, - item::{tool::ToolKind, Item, ItemKind}, + item::Item, object, quadruped_low, quadruped_medium, theropod, Body, }, - trade::{Good, SiteInformation}, + trade::SiteInformation, }; use hashbrown::HashMap; use rand::{self, distributions::WeightedError, seq::SliceRandom, Rng}; @@ -41,20 +40,9 @@ use tracing::warn; #[derive(Clone)] pub struct LoadoutBuilder(Loadout); -#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, Debug, EnumIter)] -pub enum LoadoutConfig { - Gnarling, - Adlet, - Sahagin, - Haniwa, - Myrmidon, +#[derive(Copy, Clone, PartialEq, Deserialize, Serialize, Debug, EnumIter)] +pub enum LoadoutPreset { Husk, - Beastmaster, - Warlord, - Warlock, - Villager, - Guard, - Merchant, } #[derive(Debug, Deserialize, Clone)] @@ -82,7 +70,9 @@ impl ItemSpec { .as_ref() .and_then(|e| match e { entry @ ItemSpec::Item { .. } => entry.try_to_item(asset_specifier, rng), - choice @ ItemSpec::Choice { .. } => choice.try_to_item(asset_specifier, rng), + choice @ ItemSpec::Choice { .. } => { + choice.try_to_item(asset_specifier, rng) + }, }) }, } @@ -165,7 +155,7 @@ pub fn make_potion_bag(quantity: u32) -> Item { // Also we are using default tools for un-specified species so // it's fine to have wildcards #[allow(clippy::too_many_lines, clippy::match_wildcard_for_single_variants)] -pub fn default_main_tool(body: &Body) -> Option { +fn default_main_tool(body: &Body) -> Option { match body { Body::Golem(golem) => match golem.species { golem::Species::StoneGolem => Some(Item::new_from_asset_expect( @@ -350,63 +340,108 @@ impl LoadoutBuilder { pub fn new() -> Self { Self(Loadout::new_empty()) } #[must_use] - fn with_default_equipment(body: &Body, active_item: Option) -> Self { - let mut builder = Self::new(); - builder = match body { - Body::BipedLarge(biped_large::Body { - species: biped_large::Species::Mindflayer, - .. - }) => builder.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.biped_large.mindflayer", - ))), - Body::BipedLarge(biped_large::Body { - species: biped_large::Species::Minotaur, - .. - }) => builder.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.biped_large.minotaur", - ))), - Body::BipedLarge(biped_large::Body { - species: biped_large::Species::Tidalwarrior, - .. - }) => builder.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.biped_large.tidal_warrior", - ))), - Body::BipedLarge(biped_large::Body { - species: biped_large::Species::Yeti, - .. - }) => builder.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.biped_large.yeti", - ))), - Body::BipedLarge(biped_large::Body { - species: biped_large::Species::Harvester, - .. - }) => builder.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.biped_large.harvester", - ))), - Body::Golem(golem::Body { - species: golem::Species::ClayGolem, - .. - }) => builder.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.golem.claygolem", - ))), - _ => builder, - }; - - builder.active_mainhand(active_item) - } - - #[must_use] + /// Construct new `LoadoutBuilder` from `asset_specifier` + /// Will panic if asset is broken pub fn from_asset_expect(asset_specifier: &str, rng: Option<&mut impl Rng>) -> Self { + // It's impossible to use lambdas because `loadout` is used by value + #![allow(clippy::option_if_let_else)] let loadout = Self::new(); if let Some(rng) = rng { loadout.with_asset_expect(asset_specifier, rng) } else { - let rng = &mut rand::thread_rng(); - loadout.with_asset_expect(asset_specifier, rng) + let fallback_rng = &mut rand::thread_rng(); + loadout.with_asset_expect(asset_specifier, fallback_rng) } } + #[must_use] + /// Construct new default `LoadoutBuilder` for corresponding `body` + /// + /// NOTE: make sure that you check what is default for this body + /// Use it if you don't care much about it, for example in "/spawn" command + pub fn from_default(body: &Body) -> Self { + let loadout = Self::new(); + loadout + .with_default_maintool(body) + .with_default_equipment(body) + } + + #[must_use] + /// Set default active mainhand weapon based on `body` + pub fn with_default_maintool(self, body: &Body) -> Self { + self.active_mainhand(default_main_tool(body)) + } + + #[must_use] + /// Set default equipement based on `body` + pub fn with_default_equipment(mut self, body: &Body) -> Self { + self = match body { + Body::BipedLarge(biped_large::Body { + species: biped_large::Species::Mindflayer, + .. + }) => self.chest(Some(Item::new_from_asset_expect( + "common.items.npc_armor.biped_large.mindflayer", + ))), + Body::BipedLarge(biped_large::Body { + species: biped_large::Species::Minotaur, + .. + }) => self.chest(Some(Item::new_from_asset_expect( + "common.items.npc_armor.biped_large.minotaur", + ))), + Body::BipedLarge(biped_large::Body { + species: biped_large::Species::Tidalwarrior, + .. + }) => self.chest(Some(Item::new_from_asset_expect( + "common.items.npc_armor.biped_large.tidal_warrior", + ))), + Body::BipedLarge(biped_large::Body { + species: biped_large::Species::Yeti, + .. + }) => self.chest(Some(Item::new_from_asset_expect( + "common.items.npc_armor.biped_large.yeti", + ))), + Body::BipedLarge(biped_large::Body { + species: biped_large::Species::Harvester, + .. + }) => self.chest(Some(Item::new_from_asset_expect( + "common.items.npc_armor.biped_large.harvester", + ))), + Body::Golem(golem::Body { + species: golem::Species::ClayGolem, + .. + }) => self.chest(Some(Item::new_from_asset_expect( + "common.items.npc_armor.golem.claygolem", + ))), + _ => self, + }; + + self + } + + #[must_use] + pub fn with_preset(mut self, preset: LoadoutPreset) -> Self { + let rng = &mut rand::thread_rng(); + match preset { + LoadoutPreset::Husk => { + self = self.with_asset_expect("common.loadout.dungeon.tier-5.husk", rng) + }, + } + + self + } + + #[must_use] + pub fn with_creator( + mut self, + creator: fn(LoadoutBuilder, Option<&SiteInformation>) -> LoadoutBuilder, + economy: Option<&SiteInformation>, + ) -> LoadoutBuilder { + self = creator(self, economy); + + self + } + /// # Usage /// Creates new `LoadoutBuilder` with all field replaced from /// `asset_specifier` which corresponds to loadout config @@ -497,219 +532,6 @@ impl LoadoutBuilder { self.with_asset_expect("common.loadout.default", rng) } - /// Builds loadout of creature when spawned - #[must_use] - // The reason why this function is so long is creating merchant inventory - // with all items to sell. - // Maybe we should do it on the caller side? - #[allow( - clippy::too_many_lines, - clippy::cast_precision_loss, - clippy::cast_sign_loss, - clippy::cast_possible_truncation - )] - pub fn build_loadout( - body: Body, - mut main_tool: Option, - config: Option, - economy: Option<&SiteInformation>, - ) -> Self { - // If no main tool is passed in, checks if species has a default main tool - if main_tool.is_none() { - main_tool = default_main_tool(&body); - } - - // Constructs ItemConfig from Item - let active_item = if let Some(ItemKind::Tool(_)) = main_tool.as_ref().map(Item::kind) { - main_tool - } else { - Some(Item::empty()) - }; - let active_tool_kind = active_item.as_ref().and_then(|i| { - if let ItemKind::Tool(tool) = &i.kind() { - Some(tool.kind) - } else { - None - } - }); - // Creates rest of loadout - let rng = &mut rand::thread_rng(); - let loadout_builder = if let Some(config) = config { - let builder = Self::new().active_mainhand(active_item); - // NOTE: we apply asset after active mainhand so asset has ability override it - match config { - LoadoutConfig::Gnarling => match active_tool_kind { - Some(ToolKind::Bow | ToolKind::Staff | ToolKind::Spear) => { - builder.with_asset_expect("common.loadout.dungeon.tier-0.gnarling", rng) - }, - _ => builder, - }, - LoadoutConfig::Adlet => match active_tool_kind { - Some(ToolKind::Bow) => { - builder.with_asset_expect("common.loadout.dungeon.tier-1.adlet_bow", rng) - }, - Some(ToolKind::Spear | ToolKind::Staff) => { - builder.with_asset_expect("common.loadout.dungeon.tier-1.adlet_spear", rng) - }, - _ => builder, - }, - LoadoutConfig::Sahagin => { - builder.with_asset_expect("common.loadout.dungeon.tier-2.sahagin", rng) - }, - LoadoutConfig::Haniwa => { - builder.with_asset_expect("common.loadout.dungeon.tier-3.haniwa", rng) - }, - LoadoutConfig::Myrmidon => { - builder.with_asset_expect("common.loadout.dungeon.tier-4.myrmidon", rng) - }, - LoadoutConfig::Husk => { - builder.with_asset_expect("common.loadout.dungeon.tier-5.husk", rng) - }, - LoadoutConfig::Beastmaster => { - builder.with_asset_expect("common.loadout.dungeon.tier-5.beastmaster", rng) - }, - LoadoutConfig::Warlord => { - builder.with_asset_expect("common.loadout.dungeon.tier-5.warlord", rng) - }, - LoadoutConfig::Warlock => { - builder.with_asset_expect("common.loadout.dungeon.tier-5.warlock", rng) - }, - LoadoutConfig::Villager => builder - .with_asset_expect("common.loadout.village.villager", rng) - .bag(ArmorSlot::Bag1, Some(make_potion_bag(10))), - LoadoutConfig::Guard => builder - .with_asset_expect("common.loadout.village.guard", rng) - .bag(ArmorSlot::Bag1, Some(make_potion_bag(25))), - LoadoutConfig::Merchant => { - let mut backpack = - Item::new_from_asset_expect("common.items.armor.misc.back.backpack"); - let mut coins = economy - .and_then(|e| e.unconsumed_stock.get(&Good::Coin)) - .copied() - .unwrap_or_default() - .round() - .min(rand::thread_rng().gen_range(1000.0..3000.0)) - as u32; - let armor = economy - .and_then(|e| e.unconsumed_stock.get(&Good::Armor)) - .copied() - .unwrap_or_default() - / 10.0; - for s in backpack.slots_mut() { - if coins > 0 { - let mut coin_item = - Item::new_from_asset_expect("common.items.utility.coins"); - coin_item - .set_amount(coins) - .expect("coins should be stackable"); - *s = Some(coin_item); - coins = 0; - } else if armor > 0.0 { - if let Some(item_id) = - TradePricing::random_item(Good::Armor, armor, true) - { - *s = Some(Item::new_from_asset_expect(&item_id)); - } - } - } - let mut bag1 = Item::new_from_asset_expect( - "common.items.armor.misc.bag.reliable_backpack", - ); - let weapon = economy - .and_then(|e| e.unconsumed_stock.get(&Good::Tools)) - .copied() - .unwrap_or_default() - / 10.0; - if weapon > 0.0 { - for i in bag1.slots_mut() { - if let Some(item_id) = - TradePricing::random_item(Good::Tools, weapon, true) - { - *i = Some(Item::new_from_asset_expect(&item_id)); - } - } - } - let mut item_with_amount = |item_id: &str, amount: &mut f32| { - if *amount > 0.0 { - let mut item = Item::new_from_asset_expect(item_id); - // NOTE: Conversion to and from f32 works fine because we make sure the - // number we're converting is ≤ 100. - let max = amount.min(16.min(item.max_amount()) as f32) as u32; - let n = rng.gen_range(1..max.max(2)); - *amount -= if item.set_amount(n).is_ok() { - n as f32 - } else { - 1.0 - }; - Some(item) - } else { - None - } - }; - let mut bag2 = Item::new_from_asset_expect( - "common.items.armor.misc.bag.reliable_backpack", - ); - let mut ingredients = economy - .and_then(|e| e.unconsumed_stock.get(&Good::Ingredients)) - .copied() - .unwrap_or_default() - / 10.0; - for i in bag2.slots_mut() { - if let Some(item_id) = - TradePricing::random_item(Good::Ingredients, ingredients, true) - { - *i = item_with_amount(&item_id, &mut ingredients); - } - } - let mut bag3 = Item::new_from_asset_expect( - "common.items.armor.misc.bag.reliable_backpack", - ); - // TODO: currently econsim spends all its food on population, resulting in none - // for the players to buy; the `.max` is temporary to ensure that there's some - // food for sale at every site, to be used until we have some solution like NPC - // houses as a limit on econsim population growth - let mut food = economy - .and_then(|e| e.unconsumed_stock.get(&Good::Food)) - .copied() - .unwrap_or_default() - .max(10000.0) - / 10.0; - for i in bag3.slots_mut() { - if let Some(item_id) = TradePricing::random_item(Good::Food, food, true) { - *i = item_with_amount(&item_id, &mut food); - } - } - let mut bag4 = Item::new_from_asset_expect( - "common.items.armor.misc.bag.reliable_backpack", - ); - let mut potions = economy - .and_then(|e| e.unconsumed_stock.get(&Good::Potions)) - .copied() - .unwrap_or_default() - / 10.0; - for i in bag4.slots_mut() { - if let Some(item_id) = - TradePricing::random_item(Good::Potions, potions, true) - { - *i = item_with_amount(&item_id, &mut potions); - } - } - builder - .with_asset_expect("common.loadout.village.merchant", rng) - .back(Some(backpack)) - .bag(ArmorSlot::Bag1, Some(bag1)) - .bag(ArmorSlot::Bag2, Some(bag2)) - .bag(ArmorSlot::Bag3, Some(bag3)) - .bag(ArmorSlot::Bag4, Some(bag4)) - }, - } - } else { - Self::with_default_equipment(&body, active_item) - }; - - Self(loadout_builder.build()) - } - #[must_use] pub fn active_mainhand(mut self, item: Option) -> Self { self.0.swap(EquipSlot::ActiveMainhand, item); @@ -838,40 +660,13 @@ mod tests { use rand::thread_rng; use strum::IntoEnumIterator; - // Testing all configs in loadout with weapons of different toolkinds + // Testing all loadout presets // // Things that will be catched - invalid assets paths #[test] - fn test_loadout_configs() { - let test_weapons = vec![ - // Melee - "common.items.weapons.sword.starter", // Sword - "common.items.weapons.axe.starter_axe", // Axe - "common.items.weapons.hammer.starter_hammer", // Hammer - // Ranged - "common.items.weapons.bow.starter", // Bow - "common.items.weapons.staff.starter_staff", // Staff - "common.items.weapons.sceptre.starter_sceptre", // Sceptre - // Other - "common.items.weapons.dagger.starter_dagger", // Dagger - "common.items.weapons.shield.shield_1", // Shield - "common.items.npc_weapons.biped_small.sahagin.wooden_spear", // Spear - // Exotic - "common.items.npc_weapons.unique.beast_claws", // Natural - "common.items.weapons.tool.rake", // Farming - "common.items.tool.pickaxe_stone", // Pick - "common.items.weapons.empty.empty", // Empty - ]; - - for config in LoadoutConfig::iter() { - for test_weapon in &test_weapons { - std::mem::drop(LoadoutBuilder::build_loadout( - Body::Humanoid(comp::humanoid::Body::random()), - Some(Item::new_from_asset_expect(test_weapon)), - Some(config), - None, - )); - } + fn test_loadout_presets() { + for preset in LoadoutPreset::iter() { + std::mem::drop(LoadoutBuilder::default().with_preset(preset)); } } @@ -895,17 +690,11 @@ mod tests { body_type: comp::$species::BodyType::Male, ..body }; - std::mem::drop(LoadoutBuilder::build_loadout( - Body::$body(female_body), - None, - None, - None, + std::mem::drop(LoadoutBuilder::from_default( + &Body::$body(female_body), )); - std::mem::drop(LoadoutBuilder::build_loadout( - Body::$body(male_body), - None, - None, - None, + std::mem::drop(LoadoutBuilder::from_default( + &Body::$body(male_body), )); } }; diff --git a/common/src/generation.rs b/common/src/generation.rs index e91763659a..78e5726bbb 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -1,7 +1,12 @@ use crate::{ - comp::{self, humanoid, inventory::loadout_builder::LoadoutConfig, Alignment, Body, Item}, + comp::{ + self, agent, humanoid, + inventory::loadout_builder::{LoadoutBuilder, LoadoutPreset}, + Alignment, Body, Item, + }, npc::{self, NPC_NAMES}, skillset_builder::SkillSetConfig, + trade, trade::SiteInformation, }; use vek::*; @@ -17,6 +22,7 @@ pub struct EntityInfo { pub is_giant: bool, pub has_agency: bool, pub alignment: Alignment, + pub agent_mark: Option, pub body: Body, pub name: Option, pub main_tool: Option, @@ -25,13 +31,16 @@ pub struct EntityInfo { // TODO: Properly give NPCs skills pub level: Option, pub loot_drop: Option, + // FIXME: using both preset and asset is silly, make it enum + // so it will be correct by construction pub loadout_config: Option, - pub loadout_preset: Option, + pub loadout_preset: Option, + pub make_loadout: Option) -> LoadoutBuilder>, pub skillset_config: Option, pub skillset_preset: Option, pub pet: Option>, // we can't use DHashMap, do we want to move that into common? - pub trading_information: Option, + pub trading_information: Option, //Option>, /* price and available amount */ } @@ -43,6 +52,7 @@ impl EntityInfo { is_giant: false, has_agency: true, alignment: Alignment::Wild, + agent_mark: None, body: Body::Humanoid(humanoid::Body::random()), name: None, main_tool: None, @@ -52,6 +62,7 @@ impl EntityInfo { loot_drop: None, loadout_config: None, loadout_preset: None, + make_loadout: None, skillset_config: None, skillset_preset: None, pet: None, @@ -96,6 +107,11 @@ impl EntityInfo { self } + pub fn with_agent_mark(mut self, agent_mark: agent::Mark) -> Self { + self.agent_mark = Some(agent_mark); + self + } + pub fn with_main_tool(mut self, main_tool: Item) -> Self { self.main_tool = Some(main_tool); self @@ -121,18 +137,21 @@ impl EntityInfo { self } - pub fn with_loadout_config(mut self, config: String) -> Self { - self.loadout_config = Some(config); - self - } - - pub fn with_loadout_preset(mut self, preset: LoadoutConfig) -> Self { + pub fn with_loadout_preset(mut self, preset: LoadoutPreset) -> Self { self.loadout_preset = Some(preset); self } - pub fn with_skillset_config(mut self, config: String) -> Self { - self.skillset_config = Some(config); + pub fn with_loadout_config(mut self, config: &str) -> Self { + self.loadout_config = Some(config.to_owned()); + self + } + + pub fn with_lazy_loadout( + mut self, + creator: fn(LoadoutBuilder, Option<&trade::SiteInformation>) -> LoadoutBuilder, + ) -> Self { + self.make_loadout = Some(creator); self } @@ -141,6 +160,13 @@ impl EntityInfo { self } + // FIXME: Doesn't work for now, because skills can't be loaded from assets for + // now + pub fn with_skillset_config(mut self, config: String) -> Self { + self.skillset_config = Some(config); + self + } + pub fn with_automatic_name(mut self) -> Self { let npc_names = NPC_NAMES.read(); self.name = match &self.body { diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 857ab191ed..5385dca55a 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ self, - inventory::loadout_builder::{LoadoutBuilder, LoadoutConfig}, + inventory::loadout_builder::{LoadoutBuilder, LoadoutPreset}, Behavior, BehaviorCapability, CharacterState, StateUpdate, }, event::{LocalEvent, ServerEvent}, @@ -76,13 +76,15 @@ impl CharacterBehavior for Data { { let body = self.static_data.summon_info.body; - let loadout = LoadoutBuilder::build_loadout( - body, - None, - self.static_data.summon_info.loadout_preset, - None, - ) - .build(); + let mut loadout_builder = + LoadoutBuilder::new().with_default_maintool(&body); + + if let Some(preset) = self.static_data.summon_info.loadout_preset { + loadout_builder = loadout_builder.with_preset(preset); + } + + let loadout = loadout_builder.build(); + let stats = comp::Stats::new("Summon".to_string()); let skill_set = SkillSetBuilder::build_skillset( &None, @@ -176,6 +178,6 @@ pub struct SummonInfo { scale: Option, health_scaling: u16, // TODO: use assets for specifying skills and loadouts? - loadout_preset: Option, + loadout_preset: Option, skillset_preset: Option, } diff --git a/server/src/cmd.rs b/server/src/cmd.rs index a8f747e5b5..1ca186fbb3 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -1005,7 +1005,7 @@ fn handle_spawn( ); let body = body(); - let loadout = LoadoutBuilder::build_loadout(body, None, None, None).build(); + let loadout = LoadoutBuilder::from_default(&body).build(); let inventory = Inventory::new_with_loadout(loadout); let mut entity_base = server diff --git a/server/src/rtsim/entity.rs b/server/src/rtsim/entity.rs index 5405f10cea..eebcb404c6 100644 --- a/server/src/rtsim/entity.rs +++ b/server/src/rtsim/entity.rs @@ -78,7 +78,7 @@ impl Entity { pub fn get_loadout(&self) -> comp::inventory::loadout::Loadout { let mut rng = self.rng(PERM_LOADOUT); - LoadoutBuilder::from_asset_expect("common.loadout.world.traveler", &mut rng) + LoadoutBuilder::from_asset_expect("common.loadout.world.traveler", Some(&mut rng)) .bag( comp::inventory::slot::ArmorSlot::Bag1, Some(comp::inventory::loadout_builder::make_potion_bag(100)), diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index b352de9438..9925a5926a 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -3,12 +3,9 @@ use crate::{ presence::Presence, rtsim::RtSim, settings::Settings, SpawnPoint, Tick, }; use common::{ - comp::{ - self, bird_medium, inventory::loadout_builder::LoadoutConfig, Alignment, - BehaviorCapability, Pos, - }, + comp::{self, agent, bird_medium, Alignment, BehaviorCapability, Pos}, event::{EventBus, ServerEvent}, - generation::get_npc_name, + generation::{get_npc_name, EntityInfo}, npc::NPC_NAMES, terrain::TerrainGrid, LoadoutBuilder, SkillSetBuilder, @@ -178,7 +175,6 @@ impl<'a> System<'a> for Sys { let mut body = entity.body; let name = entity.name.unwrap_or_else(|| "Unnamed".to_string()); let alignment = entity.alignment; - let main_tool = entity.main_tool; let mut stats = comp::Stats::new(name); let mut scale = entity.scale; @@ -198,14 +194,53 @@ impl<'a> System<'a> for Sys { scale = 2.0 + rand::random::(); } - let loadout_config = entity.loadout_config; - let economy = entity.trading_information.as_ref(); - let skillset_config = entity.skillset_config; + let EntityInfo { + skillset_preset, + main_tool, + loadout_preset, + loadout_config, + make_loadout, + trading_information: economy, + .. + } = entity; let skill_set = - SkillSetBuilder::build_skillset(&main_tool, skillset_config).build(); - let loadout = - LoadoutBuilder::build_loadout(body, main_tool, loadout_config, economy).build(); + SkillSetBuilder::build_skillset(&main_tool, skillset_preset).build(); + + let mut loadout_builder = LoadoutBuilder::new(); + let rng = &mut rand::thread_rng(); + + // If main tool is passed, use it. Otherwise fallback to default tool + if let Some(main_tool) = main_tool { + loadout_builder = loadout_builder.active_mainhand(Some(main_tool)); + } else { + loadout_builder = loadout_builder.with_default_maintool(&body); + } + + // If there are configs, apply them + match (loadout_preset, &loadout_config) { + (Some(preset), Some(config)) => { + loadout_builder = loadout_builder.with_preset(preset); + loadout_builder = loadout_builder.with_asset_expect(&config, rng); + }, + (Some(preset), None) => { + loadout_builder = loadout_builder.with_preset(preset); + }, + (None, Some(config)) => { + loadout_builder = loadout_builder.with_asset_expect(&config, rng); + }, + // If not, use default equipement for this body + (None, None) => { + loadout_builder = loadout_builder.with_default_equipment(&body); + }, + } + + // Evaluate lazy function for loadout creation + if let Some(make_loadout) = make_loadout { + loadout_builder = loadout_builder.with_creator(make_loadout, economy.as_ref()); + } + + let loadout = loadout_builder.build(); let health = comp::Health::new(body, entity.level.unwrap_or(0)); let poise = comp::Poise::new(body); @@ -219,7 +254,7 @@ impl<'a> System<'a> for Sys { }, _ => false, }; - let trade_for_site = if matches!(loadout_config, Some(LoadoutConfig::Merchant)) { + let trade_for_site = if matches!(entity.agent_mark, Some(agent::Mark::Merchant)) { economy.map(|e| e.id) } else { None @@ -250,10 +285,7 @@ impl<'a> System<'a> for Sys { can_speak.then(|| BehaviorCapability::SPEAK), ) .with_trade_site(trade_for_site), - matches!( - loadout_config, - Some(comp::inventory::loadout_builder::LoadoutConfig::Guard) - ), + matches!(entity.agent_mark, Some(agent::Mark::Guard)), )) } else { None diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index 266fba7ed4..c52ce446eb 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -11,10 +11,7 @@ use crate::{ use common::{ assets::{AssetExt, AssetHandle}, astar::Astar, - comp::{ - inventory::loadout_builder, - {self}, - }, + comp::{self}, generation::{ChunkSupplement, EntityInfo}, lottery::{LootSpec, Lottery}, store::{Id, Store}, @@ -936,8 +933,8 @@ fn enemy_0(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { ), )) .with_name("Gnarling") - .with_loadout_config(loadout_builder::LoadoutConfig::Gnarling) - .with_skillset_config(common::skillset_builder::SkillSetConfig::Gnarling) + .with_loadout_config("common.loadout.dungeon.tier-0.gnarling") + .with_skillset_preset(common::skillset_builder::SkillSetConfig::Gnarling) .with_loot_drop(chosen.read().choose().to_item()) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0..5) { @@ -951,21 +948,31 @@ fn enemy_0(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { fn enemy_1(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-1.enemy"); - entity + let adlet = entity .with_body(comp::Body::BipedSmall( comp::biped_small::Body::random_with(dynamic_rng, &comp::biped_small::Species::Adlet), )) .with_name("Adlet") - .with_loadout_config(loadout_builder::LoadoutConfig::Adlet) - .with_skillset_config(common::skillset_builder::SkillSetConfig::Adlet) - .with_loot_drop(chosen.read().choose().to_item()) - .with_main_tool(comp::Item::new_from_asset_expect( - match dynamic_rng.gen_range(0..5) { - 0 => "common.items.npc_weapons.biped_small.adlet.adlet_bow", - 1 => "common.items.npc_weapons.biped_small.adlet.gnoll_staff", - _ => "common.items.npc_weapons.biped_small.adlet.wooden_spear", - }, - )) + .with_skillset_preset(common::skillset_builder::SkillSetConfig::Adlet) + .with_loot_drop(chosen.read().choose().to_item()); + + match dynamic_rng.gen_range(0..5) { + 0 => adlet + .with_main_tool(comp::Item::new_from_asset_expect( + "common.items.npc_weapons.biped_small.adlet.adlet_bow", + )) + .with_loadout_config("common.loadout.dungeon.tier-1.adlet_bow"), + 1 => adlet + .with_main_tool(comp::Item::new_from_asset_expect( + "common.items.npc_weapons.biped_small.adlet.adlet_staff", + )) + .with_loadout_config("common.loadout.dungeon.tier-1.adlet_spear"), + _ => adlet + .with_main_tool(comp::Item::new_from_asset_expect( + "common.items.npc_weapons.biped_small.adlet.adlet_spear", + )) + .with_loadout_config("common.loadout.dungeon.tier-1.adlet_spear"), + } } fn enemy_2(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { @@ -976,8 +983,8 @@ fn enemy_2(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { comp::biped_small::Body::random_with(dynamic_rng, &comp::biped_small::Species::Sahagin), )) .with_name("Sahagin") - .with_loadout_config(loadout_builder::LoadoutConfig::Sahagin) - .with_skillset_config(common::skillset_builder::SkillSetConfig::Sahagin) + .with_loadout_config("common.loadout.dungeon.tier-2.sahagin") + .with_skillset_preset(common::skillset_builder::SkillSetConfig::Sahagin) .with_loot_drop(chosen.read().choose().to_item()) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0..5) { @@ -1005,8 +1012,8 @@ fn enemy_3(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { ), )) .with_name("Haniwa") - .with_loadout_config(loadout_builder::LoadoutConfig::Haniwa) - .with_skillset_config(common::skillset_builder::SkillSetConfig::Haniwa) + .with_loadout_config("common.loadout.dungeon.tier-3.haniwa") + .with_skillset_preset(common::skillset_builder::SkillSetConfig::Haniwa) .with_loot_drop(chosen.read().choose().to_item()) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0..5) { @@ -1028,8 +1035,8 @@ fn enemy_4(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { ), )) .with_name("Myrmidon") - .with_loadout_config(loadout_builder::LoadoutConfig::Myrmidon) - .with_skillset_config(common::skillset_builder::SkillSetConfig::Myrmidon) + .with_loadout_config("common.loadout.dungeon.tier-4.myrmidon") + .with_skillset_preset(common::skillset_builder::SkillSetConfig::Myrmidon) .with_loot_drop(chosen.read().choose().to_item()) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0..5) { @@ -1052,16 +1059,16 @@ fn enemy_5(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { 1 => entity .with_body(comp::Body::Humanoid(comp::humanoid::Body::random())) .with_name("Cultist Warlock") - .with_loadout_config(loadout_builder::LoadoutConfig::Warlock) - .with_skillset_config(common::skillset_builder::SkillSetConfig::Warlock) + .with_loadout_config("common.loadout.dungeon.tier-5.warlock") + .with_skillset_preset(common::skillset_builder::SkillSetConfig::Warlock) .with_loot_drop(chosen.read().choose().to_item()) .with_main_tool(comp::Item::new_from_asset_expect( "common.items.weapons.staff.cultist_staff", )), _ => entity .with_name("Cultist Warlord") - .with_loadout_config(loadout_builder::LoadoutConfig::Warlord) - .with_skillset_config(common::skillset_builder::SkillSetConfig::Warlord) + .with_loadout_config("common.loadout.dungeon.tier-5.warlord") + .with_skillset_preset(common::skillset_builder::SkillSetConfig::Warlord) .with_loot_drop(chosen.read().choose().to_item()) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0..6) { @@ -1176,7 +1183,7 @@ fn boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec) -> Vec "common.items.weapons.axe.malachite_axe-0", @@ -1321,7 +1328,7 @@ fn mini_boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec entity @@ -960,27 +967,28 @@ impl Settlement { "common.items.weapons.bow.eldwood-0", )) .with_name("Merchant") + .with_agent_mark(agent::Mark::Merchant) + .with_economy(&economy) + .with_lazy_loadout(merchant_loadout) .with_level(dynamic_rng.gen_range(10..15)) - .with_loadout_config(loadout_builder::LoadoutConfig::Merchant) - .with_skillset_config( + .with_skillset_preset( common::skillset_builder::SkillSetConfig::Merchant, - ) - .with_economy(&economy), + ), _ => entity .with_main_tool(Item::new_from_asset_expect( match dynamic_rng.gen_range(0..7) { - 0 => "common.items.weapons.tool.broom", - 1 => "common.items.weapons.tool.hoe", - 2 => "common.items.weapons.tool.pickaxe", - 3 => "common.items.weapons.tool.pitchfork", - 4 => "common.items.weapons.tool.rake", - 5 => "common.items.weapons.tool.shovel-0", - _ => "common.items.weapons.tool.shovel-1", - //_ => "common.items.weapons.bow.starter", TODO: Re-Add this when we have a better way of distributing npc_weapons here - }, + 0 => "common.items.weapons.tool.broom", + 1 => "common.items.weapons.tool.hoe", + 2 => "common.items.weapons.tool.pickaxe", + 3 => "common.items.weapons.tool.pitchfork", + 4 => "common.items.weapons.tool.rake", + 5 => "common.items.weapons.tool.shovel-0", + _ => "common.items.weapons.tool.shovel-1", + //_ => "common.items.weapons.bow.starter", TODO: Re-Add this when we have a better way of distributing npc_weapons here + }, )) - .with_loadout_config(loadout_builder::LoadoutConfig::Villager) - .with_skillset_config( + .with_lazy_loadout(villager_loadout) + .with_skillset_preset( common::skillset_builder::SkillSetConfig::Villager, ), } @@ -1043,6 +1051,137 @@ impl Settlement { } } +fn merchant_loadout( + loadout_builder: LoadoutBuilder, + economy: Option<&trade::SiteInformation>, +) -> LoadoutBuilder { + let rng = &mut rand::thread_rng(); + let mut backpack = Item::new_from_asset_expect("common.items.armor.misc.back.backpack"); + let mut coins = economy + .and_then(|e| e.unconsumed_stock.get(&Good::Coin)) + .copied() + .unwrap_or_default() + .round() + .min(rand::thread_rng().gen_range(1000.0..3000.0)) as u32; + let armor = economy + .and_then(|e| e.unconsumed_stock.get(&Good::Armor)) + .copied() + .unwrap_or_default() + / 10.0; + for s in backpack.slots_mut() { + if coins > 0 { + let mut coin_item = Item::new_from_asset_expect("common.items.utility.coins"); + coin_item + .set_amount(coins) + .expect("coins should be stackable"); + *s = Some(coin_item); + coins = 0; + } else if armor > 0.0 { + if let Some(item_id) = TradePricing::random_item(Good::Armor, armor, true) { + *s = Some(Item::new_from_asset_expect(&item_id)); + } + } + } + let mut bag1 = Item::new_from_asset_expect("common.items.armor.misc.bag.reliable_backpack"); + let weapon = economy + .and_then(|e| e.unconsumed_stock.get(&Good::Tools)) + .copied() + .unwrap_or_default() + / 10.0; + if weapon > 0.0 { + for i in bag1.slots_mut() { + if let Some(item_id) = TradePricing::random_item(Good::Tools, weapon, true) { + *i = Some(Item::new_from_asset_expect(&item_id)); + } + } + } + let mut item_with_amount = |item_id: &str, amount: &mut f32| { + if *amount > 0.0 { + let mut item = Item::new_from_asset_expect(item_id); + // NOTE: Conversion to and from f32 works fine because we make sure the + // number we're converting is ≤ 100. + let max = amount.min(16.min(item.max_amount()) as f32) as u32; + let n = rng.gen_range(1..max.max(2)); + *amount -= if item.set_amount(n).is_ok() { + n as f32 + } else { + 1.0 + }; + Some(item) + } else { + None + } + }; + let mut bag2 = Item::new_from_asset_expect("common.items.armor.misc.bag.reliable_backpack"); + let mut ingredients = economy + .and_then(|e| e.unconsumed_stock.get(&Good::Ingredients)) + .copied() + .unwrap_or_default() + / 10.0; + for i in bag2.slots_mut() { + if let Some(item_id) = TradePricing::random_item(Good::Ingredients, ingredients, true) { + *i = item_with_amount(&item_id, &mut ingredients); + } + } + let mut bag3 = Item::new_from_asset_expect("common.items.armor.misc.bag.reliable_backpack"); + // TODO: currently econsim spends all its food on population, resulting in none + // for the players to buy; the `.max` is temporary to ensure that there's some + // food for sale at every site, to be used until we have some solution like NPC + // houses as a limit on econsim population growth + let mut food = economy + .and_then(|e| e.unconsumed_stock.get(&Good::Food)) + .copied() + .unwrap_or_default() + .max(10000.0) + / 10.0; + for i in bag3.slots_mut() { + if let Some(item_id) = TradePricing::random_item(Good::Food, food, true) { + *i = item_with_amount(&item_id, &mut food); + } + } + let mut bag4 = Item::new_from_asset_expect("common.items.armor.misc.bag.reliable_backpack"); + let mut potions = economy + .and_then(|e| e.unconsumed_stock.get(&Good::Potions)) + .copied() + .unwrap_or_default() + / 10.0; + for i in bag4.slots_mut() { + if let Some(item_id) = TradePricing::random_item(Good::Potions, potions, true) { + *i = item_with_amount(&item_id, &mut potions); + } + } + + loadout_builder + .with_asset_expect("common.loadout.village.merchant", rng) + .back(Some(backpack)) + .bag(ArmorSlot::Bag1, Some(bag1)) + .bag(ArmorSlot::Bag2, Some(bag2)) + .bag(ArmorSlot::Bag3, Some(bag3)) + .bag(ArmorSlot::Bag4, Some(bag4)) +} + +fn guard_loadout( + loadout_builder: LoadoutBuilder, + _economy: Option<&trade::SiteInformation>, +) -> LoadoutBuilder { + let rng = &mut rand::thread_rng(); + + loadout_builder + .with_asset_expect("common.loadout.village.guard", rng) + .bag(ArmorSlot::Bag1, Some(make_potion_bag(25))) +} + +fn villager_loadout( + loadout_builder: LoadoutBuilder, + _economy: Option<&trade::SiteInformation>, +) -> LoadoutBuilder { + let rng = &mut rand::thread_rng(); + + loadout_builder + .with_asset_expect("common.loadout.village.villager", rng) + .bag(ArmorSlot::Bag1, Some(make_potion_bag(10))) +} + #[derive(Copy, Clone, PartialEq)] pub enum Crop { Corn, From f5bf991eb0ddb0a97689f8dbf2e12bb6dbb008d2 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sun, 6 Jun 2021 18:55:04 +0300 Subject: [PATCH 006/180] Start to load EntityInfo from assets in dungeons * All enemies in dungeons are now specify loadout_config, name and main_tool in assets * Add more variance to the enemies names --- assets/common/entity/dungeon/tier-0/bow.ron | 8 ++ assets/common/entity/dungeon/tier-0/spear.ron | 8 ++ assets/common/entity/dungeon/tier-0/staff.ron | 8 ++ assets/common/entity/dungeon/tier-1/bow.ron | 8 ++ assets/common/entity/dungeon/tier-1/spear.ron | 8 ++ assets/common/entity/dungeon/tier-1/staff.ron | 8 ++ assets/common/entity/dungeon/tier-2/bow.ron | 8 ++ assets/common/entity/dungeon/tier-2/spear.ron | 8 ++ assets/common/entity/dungeon/tier-2/staff.ron | 8 ++ assets/common/entity/dungeon/tier-3/bow.ron | 8 ++ assets/common/entity/dungeon/tier-3/spear.ron | 8 ++ assets/common/entity/dungeon/tier-3/staff.ron | 8 ++ assets/common/entity/dungeon/tier-4/bow.ron | 8 ++ assets/common/entity/dungeon/tier-4/spear.ron | 8 ++ assets/common/entity/dungeon/tier-4/staff.ron | 8 ++ .../common/entity/dungeon/tier-5/warlock.ron | 8 ++ .../common/entity/dungeon/tier-5/warlord.ron | 14 ++ assets/common/entity/test.ron | 18 +-- common/src/comp/inventory/loadout_builder.rs | 6 +- common/src/generation.rs | 92 +++++++++++- world/src/site/dungeon/mod.rs | 132 +++++++----------- 21 files changed, 292 insertions(+), 98 deletions(-) create mode 100644 assets/common/entity/dungeon/tier-0/bow.ron create mode 100644 assets/common/entity/dungeon/tier-0/spear.ron create mode 100644 assets/common/entity/dungeon/tier-0/staff.ron create mode 100644 assets/common/entity/dungeon/tier-1/bow.ron create mode 100644 assets/common/entity/dungeon/tier-1/spear.ron create mode 100644 assets/common/entity/dungeon/tier-1/staff.ron create mode 100644 assets/common/entity/dungeon/tier-2/bow.ron create mode 100644 assets/common/entity/dungeon/tier-2/spear.ron create mode 100644 assets/common/entity/dungeon/tier-2/staff.ron create mode 100644 assets/common/entity/dungeon/tier-3/bow.ron create mode 100644 assets/common/entity/dungeon/tier-3/spear.ron create mode 100644 assets/common/entity/dungeon/tier-3/staff.ron create mode 100644 assets/common/entity/dungeon/tier-4/bow.ron create mode 100644 assets/common/entity/dungeon/tier-4/spear.ron create mode 100644 assets/common/entity/dungeon/tier-4/staff.ron create mode 100644 assets/common/entity/dungeon/tier-5/warlock.ron create mode 100644 assets/common/entity/dungeon/tier-5/warlord.ron diff --git a/assets/common/entity/dungeon/tier-0/bow.ron b/assets/common/entity/dungeon/tier-0/bow.ron new file mode 100644 index 0000000000..0ba5a289d6 --- /dev/null +++ b/assets/common/entity/dungeon/tier-0/bow.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Gnarling Stalker"), + + main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.adlet_bow")), + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-0.gnarling"), +) diff --git a/assets/common/entity/dungeon/tier-0/spear.ron b/assets/common/entity/dungeon/tier-0/spear.ron new file mode 100644 index 0000000000..ffc2e9cf4f --- /dev/null +++ b/assets/common/entity/dungeon/tier-0/spear.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Gnarling Mugger"), + + main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.wooden_spear")), + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-0.gnarling"), +) diff --git a/assets/common/entity/dungeon/tier-0/staff.ron b/assets/common/entity/dungeon/tier-0/staff.ron new file mode 100644 index 0000000000..b0b6a7a751 --- /dev/null +++ b/assets/common/entity/dungeon/tier-0/staff.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Gnarling Shaman"), + + main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.gnoll_staff")), + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-0.gnarling"), +) diff --git a/assets/common/entity/dungeon/tier-1/bow.ron b/assets/common/entity/dungeon/tier-1/bow.ron new file mode 100644 index 0000000000..fde472fdb5 --- /dev/null +++ b/assets/common/entity/dungeon/tier-1/bow.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Adlet Tracker"), + + main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.adlet_bow")), + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-1.adlet_bow"), +) diff --git a/assets/common/entity/dungeon/tier-1/spear.ron b/assets/common/entity/dungeon/tier-1/spear.ron new file mode 100644 index 0000000000..48a07585be --- /dev/null +++ b/assets/common/entity/dungeon/tier-1/spear.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Adlet Hunter"), + + main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.wooden_spear")), + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-1.adlet_spear"), +) diff --git a/assets/common/entity/dungeon/tier-1/staff.ron b/assets/common/entity/dungeon/tier-1/staff.ron new file mode 100644 index 0000000000..820c12436c --- /dev/null +++ b/assets/common/entity/dungeon/tier-1/staff.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Adlet Shaman"), + + main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.gnoll_staff")), + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-1.adlet_spear"), +) diff --git a/assets/common/entity/dungeon/tier-2/bow.ron b/assets/common/entity/dungeon/tier-2/bow.ron new file mode 100644 index 0000000000..8ea21fa08c --- /dev/null +++ b/assets/common/entity/dungeon/tier-2/bow.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Sahagin Sniper"), + + main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.adlet_bow")), + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-2.sahagin"), +) diff --git a/assets/common/entity/dungeon/tier-2/spear.ron b/assets/common/entity/dungeon/tier-2/spear.ron new file mode 100644 index 0000000000..bd8d7c9e4e --- /dev/null +++ b/assets/common/entity/dungeon/tier-2/spear.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Sahagin Spearman"), + + main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.wooden_spear")), + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-2.sahagin"), +) diff --git a/assets/common/entity/dungeon/tier-2/staff.ron b/assets/common/entity/dungeon/tier-2/staff.ron new file mode 100644 index 0000000000..73cb84ee55 --- /dev/null +++ b/assets/common/entity/dungeon/tier-2/staff.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Sahagin Sorcerer"), + + main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.gnoll_staff")), + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-2.sahagin"), +) diff --git a/assets/common/entity/dungeon/tier-3/bow.ron b/assets/common/entity/dungeon/tier-3/bow.ron new file mode 100644 index 0000000000..d1a9d98924 --- /dev/null +++ b/assets/common/entity/dungeon/tier-3/bow.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Haniwa Archer"), + + main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.adlet_bow")), + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-3.haniwa"), +) diff --git a/assets/common/entity/dungeon/tier-3/spear.ron b/assets/common/entity/dungeon/tier-3/spear.ron new file mode 100644 index 0000000000..5e303990af --- /dev/null +++ b/assets/common/entity/dungeon/tier-3/spear.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Haniwa Guard"), + + main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.wooden_spear")), + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-3.haniwa"), +) diff --git a/assets/common/entity/dungeon/tier-3/staff.ron b/assets/common/entity/dungeon/tier-3/staff.ron new file mode 100644 index 0000000000..ef130792c0 --- /dev/null +++ b/assets/common/entity/dungeon/tier-3/staff.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Haniwa Sorcerer"), + + main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.gnoll_staff")), + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-3.haniwa"), +) diff --git a/assets/common/entity/dungeon/tier-4/bow.ron b/assets/common/entity/dungeon/tier-4/bow.ron new file mode 100644 index 0000000000..7a9006fb5a --- /dev/null +++ b/assets/common/entity/dungeon/tier-4/bow.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Myrmidon Marksman"), + + main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.adlet_bow")), + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-4.myrmidon"), +) diff --git a/assets/common/entity/dungeon/tier-4/spear.ron b/assets/common/entity/dungeon/tier-4/spear.ron new file mode 100644 index 0000000000..3ae610eb7e --- /dev/null +++ b/assets/common/entity/dungeon/tier-4/spear.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Myrmidon Hoplite"), + + main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.wooden_spear")), + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-4.myrmidon"), +) diff --git a/assets/common/entity/dungeon/tier-4/staff.ron b/assets/common/entity/dungeon/tier-4/staff.ron new file mode 100644 index 0000000000..23d0521872 --- /dev/null +++ b/assets/common/entity/dungeon/tier-4/staff.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Myrmidon Wizard"), + + main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.gnoll_staff")), + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-4.myrmidon"), +) diff --git a/assets/common/entity/dungeon/tier-5/warlock.ron b/assets/common/entity/dungeon/tier-5/warlock.ron new file mode 100644 index 0000000000..f151381032 --- /dev/null +++ b/assets/common/entity/dungeon/tier-5/warlock.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Cultist Warlock"), + + main_tool: Some(Item("common.items.weapons.staff.cultist_staff")), + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-5.warlock"), +) diff --git a/assets/common/entity/dungeon/tier-5/warlord.ron b/assets/common/entity/dungeon/tier-5/warlord.ron new file mode 100644 index 0000000000..8369c3f29c --- /dev/null +++ b/assets/common/entity/dungeon/tier-5/warlord.ron @@ -0,0 +1,14 @@ +EntityConfig ( + name: Some("Cultist Warlord"), + + main_tool: Some(Choice([ + (1.0, Some(Item("common.items.weapons.axe_1h.orichalcum-0"))), + (2.0, Some(Item("common.items.weapons.sword.cultist"))), + (1.0, Some(Item("common.items.weapons.hammer.cultist_purp_2h-0"))), + (1.0, Some(Item("common.items.weapons.hammer_1h.orichalcum-0"))), + (1.0, Some(Item("common.items.weapons.bow.velorite"))), + ])), + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-5.warlord"), +) diff --git a/assets/common/entity/test.ron b/assets/common/entity/test.ron index 2a70be337c..040cc162b2 100644 --- a/assets/common/entity/test.ron +++ b/assets/common/entity/test.ron @@ -1,4 +1,4 @@ -{ +EntityConfig ( /// Name of Entity name: Some("Paddy"), @@ -8,11 +8,6 @@ /// or RandomWith (will use random_with if available for this Body) // body: Humanoid(Random), - /// Loot - /// Can be Item (with asset_specifier for item) - /// or LootTable (with asset_specifier for loot table) - loot: LootTable("common.loot_tables.humanoids"), - /// Main and second tools /// Can be Option (with asset_specifier for item) /// or Choice @@ -20,12 +15,17 @@ main_tool: Some(Item("common.items.weapons.axe_1h.orichalcum-0")), second_tool: None, - /// Loadout Config as Option (with asset_specifier for loadout) - loadout_config: Some(Loadout("common.loadout.village.merchant")), + /// Loadout Config (with asset_specifier for loadout) + loadout_config: Some("common.loadout.village.merchant"), /// Skillset Config as Option (with asset_specifier for skillset) // skillset_config: None, + /// Loot + /// Can be Item (with asset_specifier for item) + /// or LootTable (with asset_specifier for loot table) + // loot: LootTable("common.loot_tables.humanoids"), + /// Meta Info (level, alignment, agency, etc) // meta: {}, -} +) diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 8e49b3c4ae..fff159b3fe 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -46,7 +46,7 @@ pub enum LoadoutPreset { } #[derive(Debug, Deserialize, Clone)] -enum ItemSpec { +pub enum ItemSpec { /// One specific item. /// Example: /// Item("common.items.armor.steel.foot") @@ -61,7 +61,7 @@ enum ItemSpec { } impl ItemSpec { - fn try_to_item(&self, asset_specifier: &str, rng: &mut impl Rng) -> Option { + pub fn try_to_item(&self, asset_specifier: &str, rng: &mut impl Rng) -> Option { match self { ItemSpec::Item(specifier) => Some(Item::new_from_asset_expect(&specifier)), @@ -80,7 +80,7 @@ impl ItemSpec { #[cfg(test)] // Read everything and checks if it's loading - fn validate(&self, key: EquipSlot) { + pub fn validate(&self, key: EquipSlot) { match self { ItemSpec::Item(specifier) => std::mem::drop(Item::new_from_asset_expect(&specifier)), ItemSpec::Choice(items) => { diff --git a/common/src/generation.rs b/common/src/generation.rs index 78e5726bbb..79959c5552 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -1,7 +1,8 @@ use crate::{ + assets::{self, AssetExt}, comp::{ self, agent, humanoid, - inventory::loadout_builder::{LoadoutBuilder, LoadoutPreset}, + inventory::loadout_builder::{ItemSpec, LoadoutBuilder, LoadoutPreset}, Alignment, Body, Item, }, npc::{self, NPC_NAMES}, @@ -9,10 +10,21 @@ use crate::{ trade, trade::SiteInformation, }; +use serde::Deserialize; use vek::*; -pub enum EntityTemplate { - Traveller, +#[derive(Debug, Deserialize, Clone)] +struct EntityConfig { + name: Option, + main_tool: Option, + second_tool: Option, + loadout_config: Option, +} + +impl assets::Asset for EntityConfig { + type Loader = assets::RonLoader; + + const EXTENSION: &'static str = "ron"; } #[derive(Clone)] @@ -70,6 +82,44 @@ impl EntityInfo { } } + pub fn with_asset_expect(self, asset_specifier: &str) -> Self { + let config = EntityConfig::load_expect(asset_specifier).read().clone(); + + self.with_entity_config(config, Some(asset_specifier)) + } + + // helper function to apply config + fn with_entity_config(mut self, config: EntityConfig, asset_specifier: Option<&str>) -> Self { + let EntityConfig { + name, + main_tool, + second_tool, + loadout_config, + } = config; + + if let Some(name) = name { + self = self.with_name(name); + } + + let rng = &mut rand::thread_rng(); + if let Some(main_tool) = + main_tool.and_then(|i| i.try_to_item(asset_specifier.unwrap_or("??"), rng)) + { + self = self.with_main_tool(main_tool); + } + if let Some(second_tool) = + second_tool.and_then(|i| i.try_to_item(asset_specifier.unwrap_or("??"), rng)) + { + self = self.with_main_tool(second_tool); + } + + if let Some(loadout_config) = loadout_config { + self = self.with_loadout_config(&loadout_config); + } + + self + } + pub fn do_if(mut self, cond: bool, f: impl FnOnce(Self) -> Self) -> Self { if cond { self = f(self); @@ -224,3 +274,39 @@ pub fn get_npc_name< ) -> &'a str { &body_data.species[&species].generic } + +#[cfg(test)] +mod tests { + use super::*; + use assets::Error; + + #[test] + fn test_all_entity_assets() { + #[derive(Clone)] + struct EntityList(Vec); + impl assets::Compound for EntityList { + fn load( + cache: &assets::AssetCache, + specifier: &str, + ) -> Result { + let list = cache + .load::(specifier)? + .read() + .iter() + .map(|spec| EntityConfig::load_cloned(spec)) + .collect::>()?; + + Ok(Self(list)) + } + } + + // It just load everything that could + // TODO: add some checks, e.g. that Armor(Head) key correspond + // to Item with ItemKind Head(_) + let entity_configs = EntityList::load_expect_cloned("common.entity.*").0; + for config in entity_configs { + let pos = Vec3::new(0.0, 0.0, 0.0); + std::mem::drop(EntityInfo::at(pos).with_entity_config(config, None)); + } + } +} diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index c52ce446eb..86b8401b06 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -925,24 +925,21 @@ impl Floor { fn enemy_0(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-0.enemy"); - entity + let gnarling = entity .with_body(comp::Body::BipedSmall( comp::biped_small::Body::random_with( dynamic_rng, &comp::biped_small::Species::Gnarling, ), )) - .with_name("Gnarling") - .with_loadout_config("common.loadout.dungeon.tier-0.gnarling") - .with_skillset_preset(common::skillset_builder::SkillSetConfig::Gnarling) .with_loot_drop(chosen.read().choose().to_item()) - .with_main_tool(comp::Item::new_from_asset_expect( - match dynamic_rng.gen_range(0..5) { - 0 => "common.items.npc_weapons.biped_small.gnarling.adlet_bow", - 1 => "common.items.npc_weapons.biped_small.gnarling.gnoll_staff", - _ => "common.items.npc_weapons.biped_small.gnarling.wooden_spear", - }, - )) + .with_skillset_preset(common::skillset_builder::SkillSetConfig::Gnarling); + + match dynamic_rng.gen_range(0..5) { + 0 => gnarling.with_asset_expect("common.entity.dungeon.tier-0.bow"), + 1 => gnarling.with_asset_expect("common.entity.dungeon.tier-0.staff"), + _ => gnarling.with_asset_expect("common.entity.dungeon.tier-0.spear"), + } } fn enemy_1(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { @@ -952,48 +949,33 @@ fn enemy_1(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { .with_body(comp::Body::BipedSmall( comp::biped_small::Body::random_with(dynamic_rng, &comp::biped_small::Species::Adlet), )) - .with_name("Adlet") .with_skillset_preset(common::skillset_builder::SkillSetConfig::Adlet) .with_loot_drop(chosen.read().choose().to_item()); match dynamic_rng.gen_range(0..5) { - 0 => adlet - .with_main_tool(comp::Item::new_from_asset_expect( - "common.items.npc_weapons.biped_small.adlet.adlet_bow", - )) - .with_loadout_config("common.loadout.dungeon.tier-1.adlet_bow"), - 1 => adlet - .with_main_tool(comp::Item::new_from_asset_expect( - "common.items.npc_weapons.biped_small.adlet.adlet_staff", - )) - .with_loadout_config("common.loadout.dungeon.tier-1.adlet_spear"), - _ => adlet - .with_main_tool(comp::Item::new_from_asset_expect( - "common.items.npc_weapons.biped_small.adlet.adlet_spear", - )) - .with_loadout_config("common.loadout.dungeon.tier-1.adlet_spear"), + 0 => adlet.with_asset_expect("common.entity.dungeon.tier-1.bow"), + 1 => adlet.with_asset_expect("common.entity.dungeon.tier-1.staff"), + _ => adlet.with_asset_expect("common.entity.dungeon.tier-1.spear"), } } fn enemy_2(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-2.enemy"); - entity + let sahagin = entity .with_body(comp::Body::BipedSmall( comp::biped_small::Body::random_with(dynamic_rng, &comp::biped_small::Species::Sahagin), )) - .with_name("Sahagin") - .with_loadout_config("common.loadout.dungeon.tier-2.sahagin") .with_skillset_preset(common::skillset_builder::SkillSetConfig::Sahagin) - .with_loot_drop(chosen.read().choose().to_item()) - .with_main_tool(comp::Item::new_from_asset_expect( - match dynamic_rng.gen_range(0..5) { - 0 => "common.items.npc_weapons.biped_small.sahagin.adlet_bow", - 1 => "common.items.npc_weapons.biped_small.sahagin.gnoll_staff", - _ => "common.items.npc_weapons.biped_small.sahagin.wooden_spear", - }, - )) + .with_loot_drop(chosen.read().choose().to_item()); + + match dynamic_rng.gen_range(0..5) { + 0 => sahagin.with_asset_expect("common.entity.dungeon.tier-2.bow"), + 1 => sahagin.with_asset_expect("common.entity.dungeon.tier-2.staff"), + _ => sahagin.with_asset_expect("common.entity.dungeon.tier-2.spear"), + } } + fn enemy_3(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-3.enemy"); @@ -1004,48 +986,45 @@ fn enemy_3(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { .with_loot_drop(comp::Item::new_from_asset_expect( "common.items.crafting_ing.stones", )), - _ => entity - .with_body(comp::Body::BipedSmall( - comp::biped_small::Body::random_with( - dynamic_rng, - &comp::biped_small::Species::Haniwa, - ), - )) - .with_name("Haniwa") - .with_loadout_config("common.loadout.dungeon.tier-3.haniwa") - .with_skillset_preset(common::skillset_builder::SkillSetConfig::Haniwa) - .with_loot_drop(chosen.read().choose().to_item()) - .with_main_tool(comp::Item::new_from_asset_expect( - match dynamic_rng.gen_range(0..5) { - 0 => "common.items.npc_weapons.biped_small.haniwa.adlet_bow", - 1 => "common.items.npc_weapons.biped_small.haniwa.gnoll_staff", - _ => "common.items.npc_weapons.biped_small.haniwa.wooden_spear", - }, - )), + _ => { + let haniwa = entity + .with_body(comp::Body::BipedSmall( + comp::biped_small::Body::random_with( + dynamic_rng, + &comp::biped_small::Species::Haniwa, + ), + )) + .with_skillset_preset(common::skillset_builder::SkillSetConfig::Haniwa) + .with_loot_drop(chosen.read().choose().to_item()); + + match dynamic_rng.gen_range(0..5) { + 0 => haniwa.with_asset_expect("common.entity.dungeon.tier-3.bow"), + 1 => haniwa.with_asset_expect("common.entity.dungeon.tier-3.staff"), + _ => haniwa.with_asset_expect("common.entity.dungeon.tier-3.spear"), + } + }, } } fn enemy_4(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-4.enemy"); - entity + let myrmidon = entity .with_body(comp::Body::BipedSmall( comp::biped_small::Body::random_with( dynamic_rng, &comp::biped_small::Species::Myrmidon, ), )) - .with_name("Myrmidon") - .with_loadout_config("common.loadout.dungeon.tier-4.myrmidon") .with_skillset_preset(common::skillset_builder::SkillSetConfig::Myrmidon) - .with_loot_drop(chosen.read().choose().to_item()) - .with_main_tool(comp::Item::new_from_asset_expect( - match dynamic_rng.gen_range(0..5) { - 0 => "common.items.npc_weapons.biped_small.myrmidon.adlet_bow", - 1 => "common.items.npc_weapons.biped_small.myrmidon.gnoll_staff", - _ => "common.items.npc_weapons.biped_small.myrmidon.wooden_spear", - }, - )) + .with_loot_drop(chosen.read().choose().to_item()); + + match dynamic_rng.gen_range(0..5) { + 0 => myrmidon.with_asset_expect("common.entity.dungeon.tier-4.bow"), + 1 => myrmidon.with_asset_expect("common.entity.dungeon.tier-4.staff"), + _ => myrmidon.with_asset_expect("common.entity.dungeon.tier-4.spear"), + } } + fn enemy_5(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-5.enemy"); @@ -1058,27 +1037,14 @@ fn enemy_5(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { )), 1 => entity .with_body(comp::Body::Humanoid(comp::humanoid::Body::random())) - .with_name("Cultist Warlock") - .with_loadout_config("common.loadout.dungeon.tier-5.warlock") .with_skillset_preset(common::skillset_builder::SkillSetConfig::Warlock) .with_loot_drop(chosen.read().choose().to_item()) - .with_main_tool(comp::Item::new_from_asset_expect( - "common.items.weapons.staff.cultist_staff", - )), + .with_asset_expect("common.entity.dungeon.tier-5.warlock"), _ => entity - .with_name("Cultist Warlord") - .with_loadout_config("common.loadout.dungeon.tier-5.warlord") + .with_body(comp::Body::Humanoid(comp::humanoid::Body::random())) .with_skillset_preset(common::skillset_builder::SkillSetConfig::Warlord) .with_loot_drop(chosen.read().choose().to_item()) - .with_main_tool(comp::Item::new_from_asset_expect( - match dynamic_rng.gen_range(0..6) { - 0 => "common.items.weapons.axe_1h.orichalcum-0", - 1..=2 => "common.items.weapons.sword.cultist", - 3 => "common.items.weapons.hammer.cultist_purp_2h-0", - 4 => "common.items.weapons.hammer_1h.orichalcum-0", - _ => "common.items.weapons.bow.bone-1", - }, - )), + .with_asset_expect("common.entity.dungeon.tier-5.warlord"), } } From 171c66d53d7af45d8f15ebea17e2c45cf33de2a8 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sun, 6 Jun 2021 20:38:17 +0300 Subject: [PATCH 007/180] docs --- common/src/comp/inventory/loadout_builder.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index fff159b3fe..6382dd393f 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -79,7 +79,11 @@ impl ItemSpec { } #[cfg(test)] - // Read everything and checks if it's loading + /// # Usage + /// Read everything and checks if it's loading + /// + /// # Panics + /// 1) If weights are invalid pub fn validate(&self, key: EquipSlot) { match self { ItemSpec::Item(specifier) => std::mem::drop(Item::new_from_asset_expect(&specifier)), From 45e5554ff51f9ffc704f80ba6d55cc4e636db1e5 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sun, 6 Jun 2021 21:47:12 +0300 Subject: [PATCH 008/180] Fix issue with villagers not having any item --- common/src/comp/inventory/loadout_builder.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 6382dd393f..4bf4cab6ff 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -159,8 +159,8 @@ pub fn make_potion_bag(quantity: u32) -> Item { // Also we are using default tools for un-specified species so // it's fine to have wildcards #[allow(clippy::too_many_lines, clippy::match_wildcard_for_single_variants)] -fn default_main_tool(body: &Body) -> Option { - match body { +fn default_main_tool(body: &Body) -> Item { + let maybe_tool = match body { Body::Golem(golem) => match golem.species { golem::Species::StoneGolem => Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.stone_golems_fist", @@ -332,7 +332,9 @@ fn default_main_tool(body: &Body) -> Option { )), }, _ => None, - } + }; + + maybe_tool.unwrap_or_else(Item::empty) } impl Default for LoadoutBuilder { @@ -374,7 +376,7 @@ impl LoadoutBuilder { #[must_use] /// Set default active mainhand weapon based on `body` pub fn with_default_maintool(self, body: &Body) -> Self { - self.active_mainhand(default_main_tool(body)) + self.active_mainhand(Some(default_main_tool(body))) } #[must_use] From c3a120c551be74970d0b9e09205dfad63dc42f6b Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Mon, 7 Jun 2021 01:54:15 +0300 Subject: [PATCH 009/180] Post refactoring --- .../custom/mindflayer/summonminions.ron | 4 ++-- .../abilities/custom/tidalwarrior/totem.ron | 2 +- .../entity/dungeon/tier-5/beastmaster.ron | 12 +++++++++++ assets/common/entity/dungeon/tier-5/husk.ron | 8 ++++++++ common/src/comp/inventory/loadout_builder.rs | 10 +++++----- common/src/generation.rs | 17 ++++------------ common/src/states/basic_summon.rs | 12 +++++------ server/src/sys/terrain.rs | 20 ++++++------------- world/src/site/dungeon/mod.rs | 13 ++---------- 9 files changed, 46 insertions(+), 52 deletions(-) create mode 100644 assets/common/entity/dungeon/tier-5/beastmaster.ron create mode 100644 assets/common/entity/dungeon/tier-5/husk.ron diff --git a/assets/common/abilities/custom/mindflayer/summonminions.ron b/assets/common/abilities/custom/mindflayer/summonminions.ron index d26ecb51a4..99117ce2c0 100644 --- a/assets/common/abilities/custom/mindflayer/summonminions.ron +++ b/assets/common/abilities/custom/mindflayer/summonminions.ron @@ -10,7 +10,7 @@ BasicSummon( )), scale: None, health_scaling: 80, - loadout_config: Some(Husk), + loadout_config: Some(HuskSummon), skillset_config: None, ), -) \ No newline at end of file +) diff --git a/assets/common/abilities/custom/tidalwarrior/totem.ron b/assets/common/abilities/custom/tidalwarrior/totem.ron index 343f0e4c2d..31df64a7cd 100644 --- a/assets/common/abilities/custom/tidalwarrior/totem.ron +++ b/assets/common/abilities/custom/tidalwarrior/totem.ron @@ -10,4 +10,4 @@ BasicSummon( loadout_config: None, skillset_config: None, ), -) \ No newline at end of file +) diff --git a/assets/common/entity/dungeon/tier-5/beastmaster.ron b/assets/common/entity/dungeon/tier-5/beastmaster.ron new file mode 100644 index 0000000000..c34e9bc99e --- /dev/null +++ b/assets/common/entity/dungeon/tier-5/beastmaster.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Beastmaster"), + + main_tool: Some(Choice([ + (1.0, Some(Item("common.items.weapons.axe.malachite_axe-0"))), + (1.0, Some(Item("common.items.weapons.sword.bloodsteel-1"))), + (1.0, Some(Item("common.items.weapons.bow.velorite"))), + ])), + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-5.beastmaster"), +) diff --git a/assets/common/entity/dungeon/tier-5/husk.ron b/assets/common/entity/dungeon/tier-5/husk.ron new file mode 100644 index 0000000000..4e8bd6d35b --- /dev/null +++ b/assets/common/entity/dungeon/tier-5/husk.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Cultist Husk"), + + main_tool: None, + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-5.husk"), +) diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 4bf4cab6ff..0d8d6469a4 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -41,8 +41,8 @@ use tracing::warn; pub struct LoadoutBuilder(Loadout); #[derive(Copy, Clone, PartialEq, Deserialize, Serialize, Debug, EnumIter)] -pub enum LoadoutPreset { - Husk, +pub enum Preset { + HuskSummon, } #[derive(Debug, Deserialize, Clone)] @@ -426,10 +426,10 @@ impl LoadoutBuilder { } #[must_use] - pub fn with_preset(mut self, preset: LoadoutPreset) -> Self { + pub fn with_preset(mut self, preset: Preset) -> Self { let rng = &mut rand::thread_rng(); match preset { - LoadoutPreset::Husk => { + Preset::HuskSummon => { self = self.with_asset_expect("common.loadout.dungeon.tier-5.husk", rng) }, } @@ -671,7 +671,7 @@ mod tests { // Things that will be catched - invalid assets paths #[test] fn test_loadout_presets() { - for preset in LoadoutPreset::iter() { + for preset in Preset::iter() { std::mem::drop(LoadoutBuilder::default().with_preset(preset)); } } diff --git a/common/src/generation.rs b/common/src/generation.rs index 79959c5552..4cf13c361e 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -2,7 +2,7 @@ use crate::{ assets::{self, AssetExt}, comp::{ self, agent, humanoid, - inventory::loadout_builder::{ItemSpec, LoadoutBuilder, LoadoutPreset}, + inventory::loadout_builder::{ItemSpec, LoadoutBuilder}, Alignment, Body, Item, }, npc::{self, NPC_NAMES}, @@ -43,10 +43,7 @@ pub struct EntityInfo { // TODO: Properly give NPCs skills pub level: Option, pub loot_drop: Option, - // FIXME: using both preset and asset is silly, make it enum - // so it will be correct by construction pub loadout_config: Option, - pub loadout_preset: Option, pub make_loadout: Option) -> LoadoutBuilder>, pub skillset_config: Option, pub skillset_preset: Option, @@ -73,7 +70,6 @@ impl EntityInfo { level: None, loot_drop: None, loadout_config: None, - loadout_preset: None, make_loadout: None, skillset_config: None, skillset_preset: None, @@ -114,7 +110,7 @@ impl EntityInfo { } if let Some(loadout_config) = loadout_config { - self = self.with_loadout_config(&loadout_config); + self = self.with_loadout_config(loadout_config); } self @@ -187,13 +183,8 @@ impl EntityInfo { self } - pub fn with_loadout_preset(mut self, preset: LoadoutPreset) -> Self { - self.loadout_preset = Some(preset); - self - } - - pub fn with_loadout_config(mut self, config: &str) -> Self { - self.loadout_config = Some(config.to_owned()); + pub fn with_loadout_config(mut self, config: String) -> Self { + self.loadout_config = Some(config); self } diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 5385dca55a..8b69c6a3ab 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ self, - inventory::loadout_builder::{LoadoutBuilder, LoadoutPreset}, + inventory::loadout_builder::{self, LoadoutBuilder}, Behavior, BehaviorCapability, CharacterState, StateUpdate, }, event::{LocalEvent, ServerEvent}, @@ -79,7 +79,7 @@ impl CharacterBehavior for Data { let mut loadout_builder = LoadoutBuilder::new().with_default_maintool(&body); - if let Some(preset) = self.static_data.summon_info.loadout_preset { + if let Some(preset) = self.static_data.summon_info.loadout_config { loadout_builder = loadout_builder.with_preset(preset); } @@ -88,7 +88,7 @@ impl CharacterBehavior for Data { let stats = comp::Stats::new("Summon".to_string()); let skill_set = SkillSetBuilder::build_skillset( &None, - self.static_data.summon_info.skillset_preset, + self.static_data.summon_info.skillset_config, ) .build(); @@ -177,7 +177,7 @@ pub struct SummonInfo { body: comp::Body, scale: Option, health_scaling: u16, - // TODO: use assets for specifying skills and loadouts? - loadout_preset: Option, - skillset_preset: Option, + // TODO: use assets for specifying skills and loadout? + loadout_config: Option, + skillset_config: Option, } diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 9925a5926a..b4decd5bad 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -197,7 +197,6 @@ impl<'a> System<'a> for Sys { let EntityInfo { skillset_preset, main_tool, - loadout_preset, loadout_config, make_loadout, trading_information: economy, @@ -217,20 +216,13 @@ impl<'a> System<'a> for Sys { loadout_builder = loadout_builder.with_default_maintool(&body); } - // If there are configs, apply them - match (loadout_preset, &loadout_config) { - (Some(preset), Some(config)) => { - loadout_builder = loadout_builder.with_preset(preset); - loadout_builder = loadout_builder.with_asset_expect(&config, rng); + // If there is config, apply it. + // If not, use default equipement for this body. + match loadout_config { + Some(asset) => { + loadout_builder = loadout_builder.with_asset_expect(&asset, rng); }, - (Some(preset), None) => { - loadout_builder = loadout_builder.with_preset(preset); - }, - (None, Some(config)) => { - loadout_builder = loadout_builder.with_asset_expect(&config, rng); - }, - // If not, use default equipement for this body - (None, None) => { + None => { loadout_builder = loadout_builder.with_default_equipment(&body); }, } diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index 86b8401b06..dc06ebc758 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -1257,17 +1257,9 @@ fn mini_boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec "common.items.weapons.axe.malachite_axe-0", - 1 => "common.items.weapons.sword.bloodsteel-1", - _ => "common.items.weapons.bow.velorite", - }, - )), + .with_asset_expect("common.entity.dungeon.tier-5.beastmaster"), ); entities.resize_with(entities.len() + 2, || { EntityInfo::at(tile_wcenter.map(|e| e as f32)) @@ -1292,9 +1284,8 @@ fn mini_boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec Date: Mon, 7 Jun 2021 14:38:08 +0300 Subject: [PATCH 010/180] SkillSetBuilder pre-refactoring --- common/src/comp/inventory/loadout_builder.rs | 4 +- common/src/skillset_builder.rs | 68 ++++++++++++++++---- 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 0d8d6469a4..a2d7a12a25 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -112,7 +112,7 @@ fn choose<'a>( WeightedError::NoItem | WeightedError::AllWeightsZero => &None, WeightedError::InvalidWeight => { let err = format!("Negative values of probability in {}.", asset_specifier); - if cfg!(tests) { + if cfg!(any(debug_assertions, test)) { panic!("{}", err); } else { warn!("{}", err); @@ -121,7 +121,7 @@ fn choose<'a>( }, WeightedError::TooMany => { let err = format!("More than u32::MAX values in {}.", asset_specifier); - if cfg!(tests) { + if cfg!(any(debug_assertions, test)) { panic!("{}", err); } else { warn!("{}", err); diff --git a/common/src/skillset_builder.rs b/common/src/skillset_builder.rs index e23fa36a29..d61ed16be7 100644 --- a/common/src/skillset_builder.rs +++ b/common/src/skillset_builder.rs @@ -682,25 +682,57 @@ impl SkillSetBuilder { } } + #[must_use] + /// # Panics + /// will panic only in tests + /// 1) If added skill doesn't have any group + /// 2) If added skill already applied + /// 3) If added skill wasn't applied at the end pub fn with_skill(mut self, skill: Skill, level: Option) -> Self { - if let Some(skill_group) = skill.skill_group_kind() { - for _ in 0..level.unwrap_or(1) { - self.0 - .add_skill_points(skill_group, self.0.skill_cost(skill)); - self.0.unlock_skill(skill); - if !self.0.has_skill(skill) { - warn!( - "Failed to add skill: {:?}. Verify that it has the appropriate skill \ - group available and meets all prerequisite skills.", - skill - ); - } - } + #![warn(clippy::pedantic)] + let group = if let Some(skill_group) = skill.skill_group_kind() { + skill_group } else { - warn!( + let err = format!( "Tried to add skill: {:?} which does not have an associated skill group.", skill ); + if cfg!(test) { + panic!("{}", err); + } else { + warn!("{}", err); + } + return self; + }; + + let SkillSetBuilder(ref mut skill_set) = self; + if skill_is_applied(skill_set, skill, level) { + let err = format!( + "Tried to add skill: {:?} with level {:?} which is already applied", + skill, level, + ); + if cfg!(test) { + panic!("{}", err); + } else { + warn!("{}", err); + } + return self; + } + for _ in 0..level.unwrap_or(1) { + skill_set.add_skill_points(group, skill_set.skill_cost(skill)); + skill_set.unlock_skill(skill); + } + if !skill_is_applied(skill_set, skill, level) { + let err = format!( + "Failed to add skill: {:?}. Verify that it has the appropriate skill group \ + available and meets all prerequisite skills.", + skill + ); + if cfg!(test) { + panic!("{}", err); + } else { + warn!("{}", err); + } } self } @@ -711,3 +743,11 @@ impl SkillSetBuilder { pub fn build(self) -> SkillSet { self.0 } } + +fn skill_is_applied(skill_set: &SkillSet, skill: Skill, level: Option) -> bool { + if let Ok(applied_level) = skill_set.skill_level(skill) { + applied_level == level + } else { + false + } +} From 0c9f05b8d1b0bb315dcfc7cb10fa91770a774b4b Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Tue, 8 Jun 2021 00:58:05 +0300 Subject: [PATCH 011/180] Load skillsets from assets Done: support loading from assets in skillset_builder.rs entity_config field with skillset asset field move every SkillSet config to assets tests for skillset assets tests for assets in entity configs --- assets/common/entity/dungeon/tier-0/bow.ron | 3 +- assets/common/entity/dungeon/tier-0/spear.ron | 3 +- assets/common/entity/dungeon/tier-0/staff.ron | 3 +- assets/common/entity/dungeon/tier-1/bow.ron | 3 +- assets/common/entity/dungeon/tier-1/spear.ron | 3 +- assets/common/entity/dungeon/tier-1/staff.ron | 3 +- assets/common/entity/dungeon/tier-2/bow.ron | 3 +- assets/common/entity/dungeon/tier-2/spear.ron | 3 +- assets/common/entity/dungeon/tier-2/staff.ron | 3 +- assets/common/entity/dungeon/tier-3/bow.ron | 3 +- assets/common/entity/dungeon/tier-3/spear.ron | 3 +- assets/common/entity/dungeon/tier-3/staff.ron | 3 +- assets/common/entity/dungeon/tier-4/bow.ron | 3 +- assets/common/entity/dungeon/tier-4/spear.ron | 3 +- assets/common/entity/dungeon/tier-4/staff.ron | 3 +- .../entity/dungeon/tier-5/beastmaster.ron | 4 +- assets/common/entity/dungeon/tier-5/boss.ron | 9 + assets/common/entity/dungeon/tier-5/husk.ron | 3 +- .../common/entity/dungeon/tier-5/warlock.ron | 3 +- .../common/entity/dungeon/tier-5/warlord.ron | 3 +- assets/common/entity/test.ron | 6 +- assets/common/entity/village/guard.ron | 9 + assets/common/entity/village/merchant.ron | 9 + assets/common/entity/village/villager.ron | 16 + assets/common/loadout/world/traveler.ron | 2 +- assets/common/skillset/dungeon/tier-0/bow.ron | 12 + assets/common/skillset/dungeon/tier-1/bow.ron | 12 + assets/common/skillset/dungeon/tier-2/bow.ron | 12 + assets/common/skillset/dungeon/tier-3/bow.ron | 12 + assets/common/skillset/dungeon/tier-4/bow.ron | 12 + assets/common/skillset/dungeon/tier-5/axe.ron | 21 + assets/common/skillset/dungeon/tier-5/bow.ron | 21 + .../common/skillset/dungeon/tier-5/enemy.ron | 8 + .../common/skillset/dungeon/tier-5/hammer.ron | 21 + .../skillset/dungeon/tier-5/mindflayer.ron | 21 + .../common/skillset/dungeon/tier-5/staff.ron | 21 + .../common/skillset/dungeon/tier-5/sword.ron | 19 + assets/common/skillset/village/guard.ron | 24 + assets/common/skillset/village/merchant.ron | 17 + common/base/src/lib.rs | 21 + common/src/comp/inventory/loadout_builder.rs | 30 +- common/src/generation.rs | 72 +- common/src/skillset_builder.rs | 817 +++--------------- common/src/states/basic_summon.rs | 41 +- server/src/sys/terrain.rs | 57 +- world/src/site/dungeon/mod.rs | 13 +- world/src/site/settlement/mod.rs | 32 +- 47 files changed, 585 insertions(+), 840 deletions(-) create mode 100644 assets/common/entity/dungeon/tier-5/boss.ron create mode 100644 assets/common/entity/village/guard.ron create mode 100644 assets/common/entity/village/merchant.ron create mode 100644 assets/common/entity/village/villager.ron create mode 100644 assets/common/skillset/dungeon/tier-0/bow.ron create mode 100644 assets/common/skillset/dungeon/tier-1/bow.ron create mode 100644 assets/common/skillset/dungeon/tier-2/bow.ron create mode 100644 assets/common/skillset/dungeon/tier-3/bow.ron create mode 100644 assets/common/skillset/dungeon/tier-4/bow.ron create mode 100644 assets/common/skillset/dungeon/tier-5/axe.ron create mode 100644 assets/common/skillset/dungeon/tier-5/bow.ron create mode 100644 assets/common/skillset/dungeon/tier-5/enemy.ron create mode 100644 assets/common/skillset/dungeon/tier-5/hammer.ron create mode 100644 assets/common/skillset/dungeon/tier-5/mindflayer.ron create mode 100644 assets/common/skillset/dungeon/tier-5/staff.ron create mode 100644 assets/common/skillset/dungeon/tier-5/sword.ron create mode 100644 assets/common/skillset/village/guard.ron create mode 100644 assets/common/skillset/village/merchant.ron diff --git a/assets/common/entity/dungeon/tier-0/bow.ron b/assets/common/entity/dungeon/tier-0/bow.ron index 0ba5a289d6..e135808266 100644 --- a/assets/common/entity/dungeon/tier-0/bow.ron +++ b/assets/common/entity/dungeon/tier-0/bow.ron @@ -4,5 +4,6 @@ EntityConfig ( main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.adlet_bow")), second_tool: None, - loadout_config: Some("common.loadout.dungeon.tier-0.gnarling"), + loadout_asset: Some("common.loadout.dungeon.tier-0.gnarling"), + skillset_asset: Some("common.skillset.dungeon.tier-0.bow"), ) diff --git a/assets/common/entity/dungeon/tier-0/spear.ron b/assets/common/entity/dungeon/tier-0/spear.ron index ffc2e9cf4f..72c3b46fa3 100644 --- a/assets/common/entity/dungeon/tier-0/spear.ron +++ b/assets/common/entity/dungeon/tier-0/spear.ron @@ -4,5 +4,6 @@ EntityConfig ( main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.wooden_spear")), second_tool: None, - loadout_config: Some("common.loadout.dungeon.tier-0.gnarling"), + loadout_asset: Some("common.loadout.dungeon.tier-0.gnarling"), + skillset_asset: None, ) diff --git a/assets/common/entity/dungeon/tier-0/staff.ron b/assets/common/entity/dungeon/tier-0/staff.ron index b0b6a7a751..d8928f39fe 100644 --- a/assets/common/entity/dungeon/tier-0/staff.ron +++ b/assets/common/entity/dungeon/tier-0/staff.ron @@ -4,5 +4,6 @@ EntityConfig ( main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.gnoll_staff")), second_tool: None, - loadout_config: Some("common.loadout.dungeon.tier-0.gnarling"), + loadout_asset: Some("common.loadout.dungeon.tier-0.gnarling"), + skillset_asset: None, ) diff --git a/assets/common/entity/dungeon/tier-1/bow.ron b/assets/common/entity/dungeon/tier-1/bow.ron index fde472fdb5..e7ea1ed0dc 100644 --- a/assets/common/entity/dungeon/tier-1/bow.ron +++ b/assets/common/entity/dungeon/tier-1/bow.ron @@ -4,5 +4,6 @@ EntityConfig ( main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.adlet_bow")), second_tool: None, - loadout_config: Some("common.loadout.dungeon.tier-1.adlet_bow"), + loadout_asset: Some("common.loadout.dungeon.tier-1.adlet_bow"), + skillset_asset: Some("common.skillset.dungeon.tier-1.bow"), ) diff --git a/assets/common/entity/dungeon/tier-1/spear.ron b/assets/common/entity/dungeon/tier-1/spear.ron index 48a07585be..0f2d3322e5 100644 --- a/assets/common/entity/dungeon/tier-1/spear.ron +++ b/assets/common/entity/dungeon/tier-1/spear.ron @@ -4,5 +4,6 @@ EntityConfig ( main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.wooden_spear")), second_tool: None, - loadout_config: Some("common.loadout.dungeon.tier-1.adlet_spear"), + loadout_asset: Some("common.loadout.dungeon.tier-1.adlet_spear"), + skillset_asset: None, ) diff --git a/assets/common/entity/dungeon/tier-1/staff.ron b/assets/common/entity/dungeon/tier-1/staff.ron index 820c12436c..522ec1d415 100644 --- a/assets/common/entity/dungeon/tier-1/staff.ron +++ b/assets/common/entity/dungeon/tier-1/staff.ron @@ -4,5 +4,6 @@ EntityConfig ( main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.gnoll_staff")), second_tool: None, - loadout_config: Some("common.loadout.dungeon.tier-1.adlet_spear"), + loadout_asset: Some("common.loadout.dungeon.tier-1.adlet_spear"), + skillset_asset: None, ) diff --git a/assets/common/entity/dungeon/tier-2/bow.ron b/assets/common/entity/dungeon/tier-2/bow.ron index 8ea21fa08c..73950982ee 100644 --- a/assets/common/entity/dungeon/tier-2/bow.ron +++ b/assets/common/entity/dungeon/tier-2/bow.ron @@ -4,5 +4,6 @@ EntityConfig ( main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.adlet_bow")), second_tool: None, - loadout_config: Some("common.loadout.dungeon.tier-2.sahagin"), + loadout_asset: Some("common.loadout.dungeon.tier-2.sahagin"), + skillset_asset: Some("common.skillset.dungeon.tier-2.bow"), ) diff --git a/assets/common/entity/dungeon/tier-2/spear.ron b/assets/common/entity/dungeon/tier-2/spear.ron index bd8d7c9e4e..c9499cfa47 100644 --- a/assets/common/entity/dungeon/tier-2/spear.ron +++ b/assets/common/entity/dungeon/tier-2/spear.ron @@ -4,5 +4,6 @@ EntityConfig ( main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.wooden_spear")), second_tool: None, - loadout_config: Some("common.loadout.dungeon.tier-2.sahagin"), + loadout_asset: Some("common.loadout.dungeon.tier-2.sahagin"), + skillset_asset: None, ) diff --git a/assets/common/entity/dungeon/tier-2/staff.ron b/assets/common/entity/dungeon/tier-2/staff.ron index 73cb84ee55..4ecbcd121e 100644 --- a/assets/common/entity/dungeon/tier-2/staff.ron +++ b/assets/common/entity/dungeon/tier-2/staff.ron @@ -4,5 +4,6 @@ EntityConfig ( main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.gnoll_staff")), second_tool: None, - loadout_config: Some("common.loadout.dungeon.tier-2.sahagin"), + loadout_asset: Some("common.loadout.dungeon.tier-2.sahagin"), + skillset_asset: None, ) diff --git a/assets/common/entity/dungeon/tier-3/bow.ron b/assets/common/entity/dungeon/tier-3/bow.ron index d1a9d98924..e642c37333 100644 --- a/assets/common/entity/dungeon/tier-3/bow.ron +++ b/assets/common/entity/dungeon/tier-3/bow.ron @@ -4,5 +4,6 @@ EntityConfig ( main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.adlet_bow")), second_tool: None, - loadout_config: Some("common.loadout.dungeon.tier-3.haniwa"), + loadout_asset: Some("common.loadout.dungeon.tier-3.haniwa"), + skillset_asset: Some("common.skillset.dungeon.tier-3.bow"), ) diff --git a/assets/common/entity/dungeon/tier-3/spear.ron b/assets/common/entity/dungeon/tier-3/spear.ron index 5e303990af..4c107bdd73 100644 --- a/assets/common/entity/dungeon/tier-3/spear.ron +++ b/assets/common/entity/dungeon/tier-3/spear.ron @@ -4,5 +4,6 @@ EntityConfig ( main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.wooden_spear")), second_tool: None, - loadout_config: Some("common.loadout.dungeon.tier-3.haniwa"), + loadout_asset: Some("common.loadout.dungeon.tier-3.haniwa"), + skillset_asset: None, ) diff --git a/assets/common/entity/dungeon/tier-3/staff.ron b/assets/common/entity/dungeon/tier-3/staff.ron index ef130792c0..adfe8857e8 100644 --- a/assets/common/entity/dungeon/tier-3/staff.ron +++ b/assets/common/entity/dungeon/tier-3/staff.ron @@ -4,5 +4,6 @@ EntityConfig ( main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.gnoll_staff")), second_tool: None, - loadout_config: Some("common.loadout.dungeon.tier-3.haniwa"), + loadout_asset: Some("common.loadout.dungeon.tier-3.haniwa"), + skillset_asset: None, ) diff --git a/assets/common/entity/dungeon/tier-4/bow.ron b/assets/common/entity/dungeon/tier-4/bow.ron index 7a9006fb5a..04cf0e7b02 100644 --- a/assets/common/entity/dungeon/tier-4/bow.ron +++ b/assets/common/entity/dungeon/tier-4/bow.ron @@ -4,5 +4,6 @@ EntityConfig ( main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.adlet_bow")), second_tool: None, - loadout_config: Some("common.loadout.dungeon.tier-4.myrmidon"), + loadout_asset: Some("common.loadout.dungeon.tier-4.myrmidon"), + skillset_asset: Some("common.skillset.dungeon.tier-4.bow"), ) diff --git a/assets/common/entity/dungeon/tier-4/spear.ron b/assets/common/entity/dungeon/tier-4/spear.ron index 3ae610eb7e..d73950e575 100644 --- a/assets/common/entity/dungeon/tier-4/spear.ron +++ b/assets/common/entity/dungeon/tier-4/spear.ron @@ -4,5 +4,6 @@ EntityConfig ( main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.wooden_spear")), second_tool: None, - loadout_config: Some("common.loadout.dungeon.tier-4.myrmidon"), + loadout_asset: Some("common.loadout.dungeon.tier-4.myrmidon"), + skillset_asset: None, ) diff --git a/assets/common/entity/dungeon/tier-4/staff.ron b/assets/common/entity/dungeon/tier-4/staff.ron index 23d0521872..a022a4b330 100644 --- a/assets/common/entity/dungeon/tier-4/staff.ron +++ b/assets/common/entity/dungeon/tier-4/staff.ron @@ -4,5 +4,6 @@ EntityConfig ( main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.gnoll_staff")), second_tool: None, - loadout_config: Some("common.loadout.dungeon.tier-4.myrmidon"), + loadout_asset: Some("common.loadout.dungeon.tier-4.myrmidon"), + skillset_asset: None, ) diff --git a/assets/common/entity/dungeon/tier-5/beastmaster.ron b/assets/common/entity/dungeon/tier-5/beastmaster.ron index c34e9bc99e..4c2421ac72 100644 --- a/assets/common/entity/dungeon/tier-5/beastmaster.ron +++ b/assets/common/entity/dungeon/tier-5/beastmaster.ron @@ -8,5 +8,7 @@ EntityConfig ( ])), second_tool: None, - loadout_config: Some("common.loadout.dungeon.tier-5.beastmaster"), + loadout_asset: Some("common.loadout.dungeon.tier-5.beastmaster"), + // TODO: make skillset for him, I'm just too lazy + skillset_asset: Some("common.skillset.dungeon.tier-5.enemy"), ) diff --git a/assets/common/entity/dungeon/tier-5/boss.ron b/assets/common/entity/dungeon/tier-5/boss.ron new file mode 100644 index 0000000000..042951cee6 --- /dev/null +++ b/assets/common/entity/dungeon/tier-5/boss.ron @@ -0,0 +1,9 @@ +EntityConfig ( + name: Some("Mindflayer"), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: Some("common.skillset.dungeon.tier-5.mindflayer"), +) diff --git a/assets/common/entity/dungeon/tier-5/husk.ron b/assets/common/entity/dungeon/tier-5/husk.ron index 4e8bd6d35b..6c23289a44 100644 --- a/assets/common/entity/dungeon/tier-5/husk.ron +++ b/assets/common/entity/dungeon/tier-5/husk.ron @@ -4,5 +4,6 @@ EntityConfig ( main_tool: None, second_tool: None, - loadout_config: Some("common.loadout.dungeon.tier-5.husk"), + loadout_asset: Some("common.loadout.dungeon.tier-5.husk"), + skillset_asset: None, ) diff --git a/assets/common/entity/dungeon/tier-5/warlock.ron b/assets/common/entity/dungeon/tier-5/warlock.ron index f151381032..8e5fe4ae70 100644 --- a/assets/common/entity/dungeon/tier-5/warlock.ron +++ b/assets/common/entity/dungeon/tier-5/warlock.ron @@ -4,5 +4,6 @@ EntityConfig ( main_tool: Some(Item("common.items.weapons.staff.cultist_staff")), second_tool: None, - loadout_config: Some("common.loadout.dungeon.tier-5.warlock"), + loadout_asset: Some("common.loadout.dungeon.tier-5.warlock"), + skillset_asset: Some("common.skillset.dungeon.tier-5.enemy"), ) diff --git a/assets/common/entity/dungeon/tier-5/warlord.ron b/assets/common/entity/dungeon/tier-5/warlord.ron index 8369c3f29c..37430fc373 100644 --- a/assets/common/entity/dungeon/tier-5/warlord.ron +++ b/assets/common/entity/dungeon/tier-5/warlord.ron @@ -10,5 +10,6 @@ EntityConfig ( ])), second_tool: None, - loadout_config: Some("common.loadout.dungeon.tier-5.warlord"), + loadout_asset: Some("common.loadout.dungeon.tier-5.warlord"), + skillset_asset: Some("common.skillset.dungeon.tier-5.enemy"), ) diff --git a/assets/common/entity/test.ron b/assets/common/entity/test.ron index 040cc162b2..915f98ade2 100644 --- a/assets/common/entity/test.ron +++ b/assets/common/entity/test.ron @@ -16,10 +16,10 @@ EntityConfig ( second_tool: None, /// Loadout Config (with asset_specifier for loadout) - loadout_config: Some("common.loadout.village.merchant"), + loadout_asset: Some("common.loadout.village.merchant"), - /// Skillset Config as Option (with asset_specifier for skillset) - // skillset_config: None, + /// Skillset Config (with asset_specifier for skillset) + skillset_asset: Some("common.skillset.village.merchant"), /// Loot /// Can be Item (with asset_specifier for item) diff --git a/assets/common/entity/village/guard.ron b/assets/common/entity/village/guard.ron new file mode 100644 index 0000000000..93d770cb2c --- /dev/null +++ b/assets/common/entity/village/guard.ron @@ -0,0 +1,9 @@ +EntityConfig ( + name: Some("Guard"), + + main_tool: Some(Item("common.items.weapons.sword.iron-4")), + second_tool: None, + + loadout_asset: None, + skillset_asset: Some("common.skillset.village.guard"), +) diff --git a/assets/common/entity/village/merchant.ron b/assets/common/entity/village/merchant.ron new file mode 100644 index 0000000000..07dd497fdf --- /dev/null +++ b/assets/common/entity/village/merchant.ron @@ -0,0 +1,9 @@ +EntityConfig ( + name: Some("Merchant"), + + main_tool: Some(Item("common.items.weapons.bow.eldwood-0")), + second_tool: None, + + loadout_asset: None, + skillset_asset: Some("common.skillset.village.merchant"), +) diff --git a/assets/common/entity/village/villager.ron b/assets/common/entity/village/villager.ron new file mode 100644 index 0000000000..45a92d5abb --- /dev/null +++ b/assets/common/entity/village/villager.ron @@ -0,0 +1,16 @@ +EntityConfig ( + name: None, + + main_tool: Some(Choice([ + (1.0, Some(Item("common.items.weapons.tool.broom"))), + (1.0, Some(Item("common.items.weapons.tool.hoe"))), + (1.0, Some(Item("common.items.weapons.tool.pickaxe"))), + (1.0, Some(Item("common.items.weapons.tool.rake"))), + (1.0, Some(Item("common.items.weapons.tool.shovel-0"))), + (1.0, Some(Item("common.items.weapons.tool.shovel-1"))), + ])), + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/loadout/world/traveler.ron b/assets/common/loadout/world/traveler.ron index c2126e5496..2a38bafaef 100644 --- a/assets/common/loadout/world/traveler.ron +++ b/assets/common/loadout/world/traveler.ron @@ -9,7 +9,7 @@ Armor(Chest): Item("common.items.npc_armor.chest.leather_blue"), Armor(Legs): Item("common.items.npc_armor.pants.leather_blue"), - Armor(Shoulders): Item("common.items.armor.swift.shoulder"), + Armor(Shoulders): Item("common.items.armor.hide.leather.shoulder"), Armor(Back): Choice([ (1.0, Some(Item("common.items.armor.hide.rawhide.back"))), diff --git a/assets/common/skillset/dungeon/tier-0/bow.ron b/assets/common/skillset/dungeon/tier-0/bow.ron new file mode 100644 index 0000000000..5a99b61b66 --- /dev/null +++ b/assets/common/skillset/dungeon/tier-0/bow.ron @@ -0,0 +1,12 @@ +([ + Group(Weapon(Bow)), + + // Charged + Skill((Bow(CDamage), Some(1))), + Skill((Bow(CKnockback), Some(1))), + Skill((Bow(CSpeed), Some(1))), + Skill((Bow(CMove), Some(1))), + + // Repeater + Skill((Bow(RDamage), Some(1))), +]) diff --git a/assets/common/skillset/dungeon/tier-1/bow.ron b/assets/common/skillset/dungeon/tier-1/bow.ron new file mode 100644 index 0000000000..5a99b61b66 --- /dev/null +++ b/assets/common/skillset/dungeon/tier-1/bow.ron @@ -0,0 +1,12 @@ +([ + Group(Weapon(Bow)), + + // Charged + Skill((Bow(CDamage), Some(1))), + Skill((Bow(CKnockback), Some(1))), + Skill((Bow(CSpeed), Some(1))), + Skill((Bow(CMove), Some(1))), + + // Repeater + Skill((Bow(RDamage), Some(1))), +]) diff --git a/assets/common/skillset/dungeon/tier-2/bow.ron b/assets/common/skillset/dungeon/tier-2/bow.ron new file mode 100644 index 0000000000..5a99b61b66 --- /dev/null +++ b/assets/common/skillset/dungeon/tier-2/bow.ron @@ -0,0 +1,12 @@ +([ + Group(Weapon(Bow)), + + // Charged + Skill((Bow(CDamage), Some(1))), + Skill((Bow(CKnockback), Some(1))), + Skill((Bow(CSpeed), Some(1))), + Skill((Bow(CMove), Some(1))), + + // Repeater + Skill((Bow(RDamage), Some(1))), +]) diff --git a/assets/common/skillset/dungeon/tier-3/bow.ron b/assets/common/skillset/dungeon/tier-3/bow.ron new file mode 100644 index 0000000000..5a99b61b66 --- /dev/null +++ b/assets/common/skillset/dungeon/tier-3/bow.ron @@ -0,0 +1,12 @@ +([ + Group(Weapon(Bow)), + + // Charged + Skill((Bow(CDamage), Some(1))), + Skill((Bow(CKnockback), Some(1))), + Skill((Bow(CSpeed), Some(1))), + Skill((Bow(CMove), Some(1))), + + // Repeater + Skill((Bow(RDamage), Some(1))), +]) diff --git a/assets/common/skillset/dungeon/tier-4/bow.ron b/assets/common/skillset/dungeon/tier-4/bow.ron new file mode 100644 index 0000000000..5a99b61b66 --- /dev/null +++ b/assets/common/skillset/dungeon/tier-4/bow.ron @@ -0,0 +1,12 @@ +([ + Group(Weapon(Bow)), + + // Charged + Skill((Bow(CDamage), Some(1))), + Skill((Bow(CKnockback), Some(1))), + Skill((Bow(CSpeed), Some(1))), + Skill((Bow(CMove), Some(1))), + + // Repeater + Skill((Bow(RDamage), Some(1))), +]) diff --git a/assets/common/skillset/dungeon/tier-5/axe.ron b/assets/common/skillset/dungeon/tier-5/axe.ron new file mode 100644 index 0000000000..abfcaae590 --- /dev/null +++ b/assets/common/skillset/dungeon/tier-5/axe.ron @@ -0,0 +1,21 @@ +([ + Group(Weapon(Axe)), + + // DoubleStrike + Skill((Axe(DsCombo), None)), + Skill((Axe(DsDamage), Some(1))), + Skill((Axe(DsSpeed), Some(1))), + Skill((Axe(DsRegen), Some(1))), + + // Spin + Skill((Axe(SInfinite), None)), + Skill((Axe(SHelicopter), None)), + Skill((Axe(SDamage), Some(1))), + Skill((Axe(SSpeed), Some(1))), + + // Leap + Skill((Axe(UnlockLeap), None)), + Skill((Axe(LDamage), Some(1))), + Skill((Axe(LKnockback), Some(1))), + Skill((Axe(LDistance), Some(1))), +]) diff --git a/assets/common/skillset/dungeon/tier-5/bow.ron b/assets/common/skillset/dungeon/tier-5/bow.ron new file mode 100644 index 0000000000..a1cd8b0def --- /dev/null +++ b/assets/common/skillset/dungeon/tier-5/bow.ron @@ -0,0 +1,21 @@ +([ + Group(Weapon(Bow)), + + // Charged + Skill((Bow(CDamage), Some(1))), + Skill((Bow(CRegen), Some(1))), + Skill((Bow(CKnockback), Some(1))), + Skill((Bow(CSpeed), Some(1))), + Skill((Bow(CMove), Some(1))), + + // Repeater + Skill((Bow(RDamage), Some(1))), + Skill((Bow(RSpeed), Some(1))), + + // Shotgun + Skill((Bow(UnlockShotgun), None)), + Skill((Bow(SDamage), Some(1))), + Skill((Bow(SSpread), Some(1))), + Skill((Bow(SArrows), Some(1))), + Skill((Bow(SCost), Some(1))), +]) diff --git a/assets/common/skillset/dungeon/tier-5/enemy.ron b/assets/common/skillset/dungeon/tier-5/enemy.ron new file mode 100644 index 0000000000..0e5ff1c8e5 --- /dev/null +++ b/assets/common/skillset/dungeon/tier-5/enemy.ron @@ -0,0 +1,8 @@ +([ + // Just gather everything + Tree("common.skillset.dungeon.tier-5.sword"), + Tree("common.skillset.dungeon.tier-5.axe"), + Tree("common.skillset.dungeon.tier-5.hammer"), + Tree("common.skillset.dungeon.tier-5.bow"), + Tree("common.skillset.dungeon.tier-5.staff"), +]) diff --git a/assets/common/skillset/dungeon/tier-5/hammer.ron b/assets/common/skillset/dungeon/tier-5/hammer.ron new file mode 100644 index 0000000000..5f0e7561a3 --- /dev/null +++ b/assets/common/skillset/dungeon/tier-5/hammer.ron @@ -0,0 +1,21 @@ +([ + Group(Weapon(Hammer)), + + // Single Strike, as single as you are + Skill((Hammer(SsKnockback), Some(1))), + Skill((Hammer(SsDamage), Some(1))), + Skill((Hammer(SsSpeed), Some(1))), + Skill((Hammer(SsRegen), Some(1))), + + // Charged + Skill((Hammer(CKnockback), Some(1))), + Skill((Hammer(CDamage), Some(1))), + Skill((Hammer(CDrain), Some(1))), + + // Leap + Skill((Hammer(UnlockLeap), None)), + Skill((Hammer(LDamage), Some(1))), + Skill((Hammer(LDistance), Some(1))), + Skill((Hammer(LKnockback), Some(1))), + Skill((Hammer(LRange), Some(1))), +]) diff --git a/assets/common/skillset/dungeon/tier-5/mindflayer.ron b/assets/common/skillset/dungeon/tier-5/mindflayer.ron new file mode 100644 index 0000000000..09cba3e4bd --- /dev/null +++ b/assets/common/skillset/dungeon/tier-5/mindflayer.ron @@ -0,0 +1,21 @@ +([ + Group(Weapon(Staff)), + + // Fireball + Skill((Staff(BDamage), Some(3))), + Skill((Staff(BRegen), Some(2))), + Skill((Staff(BRadius), Some(2))), + + // Flamethrower + Skill((Staff(FDamage), Some(3))), + Skill((Staff(FRange), Some(2))), + Skill((Staff(FDrain), Some(2))), + Skill((Staff(FVelocity), Some(2))), + + // Shockwave + Skill((Staff(UnlockShockwave), None)), + Skill((Staff(SDamage), Some(2))), + Skill((Staff(SKnockback), Some(2))), + Skill((Staff(SRange), Some(2))), + Skill((Staff(SCost), Some(2))), +]) diff --git a/assets/common/skillset/dungeon/tier-5/staff.ron b/assets/common/skillset/dungeon/tier-5/staff.ron new file mode 100644 index 0000000000..4c3dc1af3a --- /dev/null +++ b/assets/common/skillset/dungeon/tier-5/staff.ron @@ -0,0 +1,21 @@ +([ + Group(Weapon(Staff)), + + // Fireball + Skill((Staff(BDamage), Some(1))), + Skill((Staff(BRegen), Some(1))), + Skill((Staff(BRadius), Some(1))), + + // Flamethrower + Skill((Staff(FRange), Some(1))), + Skill((Staff(FDrain), Some(1))), + Skill((Staff(FDamage), Some(1))), + Skill((Staff(FVelocity), Some(1))), + + // Shockwave + Skill((Staff(UnlockShockwave), None)), + Skill((Staff(SDamage), Some(1))), + Skill((Staff(SKnockback), Some(1))), + Skill((Staff(SRange), Some(1))), + Skill((Staff(SCost), Some(1))), +]) diff --git a/assets/common/skillset/dungeon/tier-5/sword.ron b/assets/common/skillset/dungeon/tier-5/sword.ron new file mode 100644 index 0000000000..2f945fc42f --- /dev/null +++ b/assets/common/skillset/dungeon/tier-5/sword.ron @@ -0,0 +1,19 @@ +([ + Group(Weapon(Sword)), + + // TripleStrike + Skill((Sword(TsCombo), None)), + Skill((Sword(TsDamage), Some(1))), + Skill((Sword(TsRegen), Some(1))), + + // Dash + Skill((Sword(DDamage), Some(1))), + Skill((Sword(DCost), Some(1))), + Skill((Sword(DDrain), Some(1))), + + // Spin of death + Skill((Sword(UnlockSpin), None)), + Skill((Sword(SDamage), Some(1))), + Skill((Sword(SSpins), Some(2))), + Skill((Sword(SCost), Some(1))), +]) diff --git a/assets/common/skillset/village/guard.ron b/assets/common/skillset/village/guard.ron new file mode 100644 index 0000000000..ad041b0f2d --- /dev/null +++ b/assets/common/skillset/village/guard.ron @@ -0,0 +1,24 @@ +([ + Group(Weapon(Sword)), + + // TripleStrike + Skill((Sword(TsCombo), None)), + Skill((Sword(TsDamage), Some(1))), + Skill((Sword(TsRegen), Some(1))), + Skill((Sword(TsSpeed), Some(1))), + + // Dash + Skill((Sword(DDamage), Some(1))), + Skill((Sword(DCost), Some(1))), + Skill((Sword(DDrain), Some(1))), + Skill((Sword(DScaling), Some(1))), + Skill((Sword(DSpeed), None)), + Skill((Sword(DInfinite), None)), + + // Spin of death + Skill((Sword(UnlockSpin), None)), + Skill((Sword(SDamage), Some(1))), + Skill((Sword(SSpeed), Some(1))), + Skill((Sword(SSpins), Some(2))), + Skill((Sword(SCost), Some(1))), +]) diff --git a/assets/common/skillset/village/merchant.ron b/assets/common/skillset/village/merchant.ron new file mode 100644 index 0000000000..06355a4ff4 --- /dev/null +++ b/assets/common/skillset/village/merchant.ron @@ -0,0 +1,17 @@ +([ + Group(Weapon(Bow)), + + // Charged + Skill((Bow(CDamage), Some(1))), + Skill((Bow(CRegen), Some(1))), + Skill((Bow(CKnockback), Some(1))), + Skill((Bow(CSpeed), Some(1))), + + // Repeater + Skill((Bow(RDamage), Some(1))), + Skill((Bow(RCost), Some(1))), + + // Shotgun + Skill((Bow(UnlockShotgun), None)), + Skill((Bow(SCost), Some(1))), +]) diff --git a/common/base/src/lib.rs b/common/base/src/lib.rs index 3325612858..d05b952127 100644 --- a/common/base/src/lib.rs +++ b/common/base/src/lib.rs @@ -23,6 +23,27 @@ macro_rules! plot { }; } +// Panic in debug or tests, warn in release +#[macro_export] +macro_rules! dev_panic { + ($msg:expr) => { + if cfg!(any(debug_assertions, test)) { + panic!("{}", $msg); + } else { + tracing::warn!("{}", $msg); + } + }; + + ($msg:expr, or return $result:expr) => { + if cfg!(any(debug_assertions, test)) { + panic!("{}", $msg); + } else { + tracing::warn!("{}", $msg); + return $result; + } + }; +} + // https://discordapp.com/channels/676678179678715904/676685797524766720/723358438943621151 #[macro_export] macro_rules! span { diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index a2d7a12a25..eb8fc67148 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -45,6 +45,14 @@ pub enum Preset { HuskSummon, } +#[derive(Debug, Deserialize, Clone)] +pub struct LoadoutSpec(HashMap); +impl assets::Asset for LoadoutSpec { + type Loader = assets::RonLoader; + + const EXTENSION: &'static str = "ron"; +} + #[derive(Debug, Deserialize, Clone)] pub enum ItemSpec { /// One specific item. @@ -112,35 +120,17 @@ fn choose<'a>( WeightedError::NoItem | WeightedError::AllWeightsZero => &None, WeightedError::InvalidWeight => { let err = format!("Negative values of probability in {}.", asset_specifier); - if cfg!(any(debug_assertions, test)) { - panic!("{}", err); - } else { - warn!("{}", err); - &None - } + common_base::dev_panic!(err, or return &None) }, WeightedError::TooMany => { let err = format!("More than u32::MAX values in {}.", asset_specifier); - if cfg!(any(debug_assertions, test)) { - panic!("{}", err); - } else { - warn!("{}", err); - &None - } + common_base::dev_panic!(err, or return &None) }, }, |(_p, itemspec)| itemspec, ) } -#[derive(Debug, Deserialize, Clone)] -pub struct LoadoutSpec(HashMap); -impl assets::Asset for LoadoutSpec { - type Loader = assets::RonLoader; - - const EXTENSION: &'static str = "ron"; -} - #[must_use] pub fn make_potion_bag(quantity: u32) -> Item { let mut bag = Item::new_from_asset_expect("common.items.armor.misc.bag.tiny_leather_pouch"); diff --git a/common/src/generation.rs b/common/src/generation.rs index 4cf13c361e..f31aa7628e 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -6,7 +6,6 @@ use crate::{ Alignment, Body, Item, }, npc::{self, NPC_NAMES}, - skillset_builder::SkillSetConfig, trade, trade::SiteInformation, }; @@ -18,7 +17,8 @@ struct EntityConfig { name: Option, main_tool: Option, second_tool: Option, - loadout_config: Option, + loadout_asset: Option, + skillset_asset: Option, } impl assets::Asset for EntityConfig { @@ -43,10 +43,9 @@ pub struct EntityInfo { // TODO: Properly give NPCs skills pub level: Option, pub loot_drop: Option, - pub loadout_config: Option, + pub loadout_asset: Option, pub make_loadout: Option) -> LoadoutBuilder>, - pub skillset_config: Option, - pub skillset_preset: Option, + pub skillset_asset: Option, pub pet: Option>, // we can't use DHashMap, do we want to move that into common? pub trading_information: Option, @@ -69,10 +68,9 @@ impl EntityInfo { scale: 1.0, level: None, loot_drop: None, - loadout_config: None, + loadout_asset: None, make_loadout: None, - skillset_config: None, - skillset_preset: None, + skillset_asset: None, pet: None, trading_information: None, } @@ -90,7 +88,8 @@ impl EntityInfo { name, main_tool, second_tool, - loadout_config, + loadout_asset, + skillset_asset, } = config; if let Some(name) = name { @@ -109,8 +108,12 @@ impl EntityInfo { self = self.with_main_tool(second_tool); } - if let Some(loadout_config) = loadout_config { - self = self.with_loadout_config(loadout_config); + if let Some(loadout_asset) = loadout_asset { + self = self.with_loadout_asset(loadout_asset); + } + + if let Some(skillset_asset) = skillset_asset { + self = self.with_skillset_asset(skillset_asset); } self @@ -183,8 +186,8 @@ impl EntityInfo { self } - pub fn with_loadout_config(mut self, config: String) -> Self { - self.loadout_config = Some(config); + pub fn with_loadout_asset(mut self, asset: String) -> Self { + self.loadout_asset = Some(asset); self } @@ -196,15 +199,8 @@ impl EntityInfo { self } - pub fn with_skillset_preset(mut self, preset: SkillSetConfig) -> Self { - self.skillset_preset = Some(preset); - self - } - - // FIXME: Doesn't work for now, because skills can't be loaded from assets for - // now - pub fn with_skillset_config(mut self, config: String) -> Self { - self.skillset_config = Some(config); + pub fn with_skillset_asset(mut self, asset: String) -> Self { + self.skillset_asset = Some(asset); self } @@ -269,6 +265,7 @@ pub fn get_npc_name< #[cfg(test)] mod tests { use super::*; + use crate::{comp::inventory::slot::EquipSlot, SkillSetBuilder}; use assets::Error; #[test] @@ -292,12 +289,35 @@ mod tests { } // It just load everything that could - // TODO: add some checks, e.g. that Armor(Head) key correspond - // to Item with ItemKind Head(_) let entity_configs = EntityList::load_expect_cloned("common.entity.*").0; for config in entity_configs { - let pos = Vec3::new(0.0, 0.0, 0.0); - std::mem::drop(EntityInfo::at(pos).with_entity_config(config, None)); + let EntityConfig { + main_tool, + second_tool, + loadout_asset, + skillset_asset, + .. + } = config; + + if let Some(main_tool) = main_tool { + main_tool.validate(EquipSlot::ActiveMainhand); + } + + if let Some(second_tool) = second_tool { + second_tool.validate(EquipSlot::ActiveOffhand); + } + + if let Some(loadout_asset) = loadout_asset { + let rng = &mut rand::thread_rng(); + let builder = LoadoutBuilder::default(); + // we need to just load it check if it exists, + // because all loadouts are tested in LoadoutBuilder module + std::mem::drop(builder.with_asset_expect(&loadout_asset, rng)); + } + + if let Some(skillset_asset) = skillset_asset { + std::mem::drop(SkillSetBuilder::from_asset_expect(&skillset_asset)); + } } } } diff --git a/common/src/skillset_builder.rs b/common/src/skillset_builder.rs index d61ed16be7..8fddd2b7b1 100644 --- a/common/src/skillset_builder.rs +++ b/common/src/skillset_builder.rs @@ -1,30 +1,56 @@ -use crate::comp::{ - item::{tool::ToolKind, Item, ItemKind}, - skills::{ - AxeSkill, BowSkill, HammerSkill, Skill, SkillGroupKind, SkillSet, StaffSkill, SwordSkill, - }, -}; +#![warn(clippy::pedantic)] +//#![warn(clippy::nursery)] +use crate::comp::skills::{Skill, SkillGroupKind, SkillSet}; + +use crate::assets::{self, AssetExt}; use serde::{Deserialize, Serialize}; use tracing::warn; #[derive(Copy, Clone, PartialEq, Serialize, Deserialize, Debug)] -pub enum SkillSetConfig { - Adlet, - Gnarling, - Sahagin, - Haniwa, - Myrmidon, - Guard, - Villager, - Merchant, - Outcast, - Highwayman, - Bandit, - CultistNovice, - CultistAcolyte, - Warlord, - Warlock, - Mindflayer, +pub enum Preset { + Empty, +} + +#[derive(Debug, Deserialize, Clone)] +struct SkillSetTree(Vec); +impl assets::Asset for SkillSetTree { + type Loader = assets::RonLoader; + + const EXTENSION: &'static str = "ron"; +} + +#[derive(Debug, Deserialize, Clone)] +enum SkillNode { + Tree(String), + Skill((Skill, Option)), + Group(SkillGroupKind), +} + +#[must_use] +fn skills_from_asset_expect(asset_specifier: &str) -> Vec<(Skill, Option)> { + let nodes = SkillSetTree::load_expect(asset_specifier).read().0.clone(); + + skills_from_nodes(nodes) +} + +#[must_use] +fn skills_from_nodes(nodes: Vec) -> Vec<(Skill, Option)> { + let mut skills = Vec::new(); + for node in nodes { + match node { + SkillNode::Tree(asset) => { + skills.append(&mut skills_from_asset_expect(&asset)); + }, + SkillNode::Skill(req) => { + skills.push(req); + }, + SkillNode::Group(group) => { + skills.push((Skill::UnlockGroup(group), None)); + }, + } + } + + skills } pub struct SkillSetBuilder(SkillSet); @@ -34,652 +60,40 @@ impl Default for SkillSetBuilder { } impl SkillSetBuilder { - pub fn build_skillset(main_tool: &Option, config: Option) -> Self { - let active_item = main_tool.as_ref().and_then(|ic| { - if let ItemKind::Tool(tool) = &ic.kind() { - Some(tool.kind) - } else { - None - } - }); + /// Creates `SkillSetBuilder` from `asset_specifier` + #[must_use] + pub fn from_asset_expect(asset_specifier: &str) -> Self { + let builder = Self::default(); - use SkillSetConfig::*; - match config { - Some(Adlet) => { - match active_item { - Some(ToolKind::Bow) => { - // Bow - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) - .with_skill(Skill::Bow(BowSkill::CSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::CMove), Some(1)) - .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) - }, - _ => Self::default(), - } - }, - Some(Gnarling) => { - match active_item { - Some(ToolKind::Bow) => { - // Bow - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) - .with_skill(Skill::Bow(BowSkill::CSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::CMove), Some(1)) - .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) - }, - _ => Self::default(), - } - }, - Some(Sahagin) => { - match active_item { - Some(ToolKind::Bow) => { - // Bow - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) - .with_skill(Skill::Bow(BowSkill::CSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::CMove), Some(1)) - .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) - }, - _ => Self::default(), - } - }, - Some(Haniwa) => { - match active_item { - Some(ToolKind::Bow) => { - // Bow - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) - .with_skill(Skill::Bow(BowSkill::CSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::CMove), Some(1)) - .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) - }, - _ => Self::default(), - } - }, - Some(Myrmidon) => { - match active_item { - Some(ToolKind::Bow) => { - // Bow - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) - .with_skill(Skill::Bow(BowSkill::CSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::CMove), Some(1)) - .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) - }, - _ => Self::default(), - } - }, - Some(Guard) => { - if let Some(ToolKind::Sword) = active_item { - // Sword - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Sword)) - .with_skill(Skill::Sword(SwordSkill::TsCombo), None) - .with_skill(Skill::Sword(SwordSkill::TsDamage), Some(1)) - .with_skill(Skill::Sword(SwordSkill::TsRegen), Some(1)) - .with_skill(Skill::Sword(SwordSkill::TsSpeed), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DDamage), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DCost), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DDrain), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DScaling), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DSpeed), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DInfinite), None) - .with_skill(Skill::Sword(SwordSkill::UnlockSpin), None) - .with_skill(Skill::Sword(SwordSkill::SDamage), Some(1)) - .with_skill(Skill::Sword(SwordSkill::SSpeed), Some(1)) - .with_skill(Skill::Sword(SwordSkill::SSpins), Some(1)) - .with_skill(Skill::Sword(SwordSkill::SCost), Some(1)) - } else { - Self::default() - } - }, - Some(Outcast) => { - match active_item { - Some(ToolKind::Sword) => { - // Sword - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Sword)) - .with_skill(Skill::Sword(SwordSkill::TsCombo), None) - .with_skill(Skill::Sword(SwordSkill::DDamage), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DCost), Some(1)) - }, - Some(ToolKind::Axe) => { - // Axe - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Axe)) - .with_skill(Skill::Axe(AxeSkill::DsCombo), None) - .with_skill(Skill::Axe(AxeSkill::SInfinite), Some(1)) - .with_skill(Skill::Axe(AxeSkill::SSpeed), Some(1)) - .with_skill(Skill::Axe(AxeSkill::SCost), Some(1)) - }, - Some(ToolKind::Hammer) => { - // Hammer - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Hammer)) - .with_skill(Skill::Hammer(HammerSkill::SsKnockback), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::SsSpeed), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::CKnockback), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::CSpeed), Some(1)) - }, - Some(ToolKind::Bow) => { - // Bow - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::ProjSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) - .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::RSpeed), Some(1)) - }, - Some(ToolKind::Staff) => { - // Staff - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Staff)) - .with_skill(Skill::Staff(StaffSkill::FDamage), Some(1)) - .with_skill(Skill::Staff(StaffSkill::FDrain), Some(1)) - .with_skill(Skill::Staff(StaffSkill::FVelocity), Some(1)) - }, - _ => Self::default(), - } - }, - Some(Highwayman) => { - match active_item { - Some(ToolKind::Sword) => { - // Sword - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Sword)) - .with_skill(Skill::Sword(SwordSkill::TsCombo), None) - .with_skill(Skill::Sword(SwordSkill::TsDamage), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DDamage), Some(1)) - .with_skill(Skill::Sword(SwordSkill::UnlockSpin), None) - .with_skill(Skill::Sword(SwordSkill::SSpins), Some(1)) - .with_skill(Skill::Sword(SwordSkill::SCost), Some(1)) - }, - Some(ToolKind::Axe) => { - // Axe - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Axe)) - .with_skill(Skill::Axe(AxeSkill::DsCombo), None) - .with_skill(Skill::Axe(AxeSkill::DsDamage), Some(1)) - .with_skill(Skill::Axe(AxeSkill::SInfinite), None) - .with_skill(Skill::Axe(AxeSkill::SDamage), Some(1)) - .with_skill(Skill::Axe(AxeSkill::SSpeed), Some(1)) - .with_skill(Skill::Axe(AxeSkill::SCost), Some(1)) - .with_skill(Skill::Axe(AxeSkill::UnlockLeap), None) - }, - Some(ToolKind::Hammer) => { - // Hammer - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Hammer)) - .with_skill(Skill::Hammer(HammerSkill::SsKnockback), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::SsDamage), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::SsSpeed), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::CKnockback), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::UnlockLeap), None) - .with_skill(Skill::Hammer(HammerSkill::LKnockback), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::LRange), Some(1)) - }, - Some(ToolKind::Bow) => { - // Bow - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) - .with_skill(Skill::Bow(BowSkill::CSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::CMove), Some(1)) - .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::UnlockShotgun), None) - .with_skill(Skill::Bow(BowSkill::SArrows), Some(1)) - }, - Some(ToolKind::Staff) => { - // Staff - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Staff)) - .with_skill(Skill::Staff(StaffSkill::BRegen), Some(1)) - .with_skill(Skill::Staff(StaffSkill::BRadius), Some(1)) - .with_skill(Skill::Staff(StaffSkill::FDamage), Some(1)) - .with_skill(Skill::Staff(StaffSkill::FRange), Some(1)) - .with_skill(Skill::Staff(StaffSkill::FVelocity), Some(1)) - .with_skill(Skill::Staff(StaffSkill::UnlockShockwave), None) - }, - _ => Self::default(), - } - }, - Some(Bandit) | Some(Merchant) => { - match active_item { - Some(ToolKind::Sword) => { - // Sword - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Sword)) - .with_skill(Skill::Sword(SwordSkill::TsCombo), None) - .with_skill(Skill::Sword(SwordSkill::TsDamage), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DDamage), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DCost), Some(1)) - .with_skill(Skill::Sword(SwordSkill::UnlockSpin), None) - .with_skill(Skill::Sword(SwordSkill::SDamage), Some(1)) - .with_skill(Skill::Sword(SwordSkill::SSpins), Some(1)) - .with_skill(Skill::Sword(SwordSkill::SCost), Some(1)) - }, - Some(ToolKind::Axe) => { - // Axe - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Axe)) - .with_skill(Skill::Axe(AxeSkill::DsCombo), None) - .with_skill(Skill::Axe(AxeSkill::DsSpeed), Some(1)) - .with_skill(Skill::Axe(AxeSkill::DsRegen), Some(1)) - .with_skill(Skill::Axe(AxeSkill::SInfinite), None) - .with_skill(Skill::Axe(AxeSkill::SDamage), Some(1)) - .with_skill(Skill::Axe(AxeSkill::UnlockLeap), None) - .with_skill(Skill::Axe(AxeSkill::LKnockback), Some(1)) - .with_skill(Skill::Axe(AxeSkill::LCost), Some(1)) - .with_skill(Skill::Axe(AxeSkill::LDistance), Some(1)) - }, - Some(ToolKind::Hammer) => { - // Hammer - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Hammer)) - .with_skill(Skill::Hammer(HammerSkill::SsKnockback), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::SsDamage), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::SsRegen), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::CKnockback), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::CDamage), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::UnlockLeap), None) - .with_skill(Skill::Hammer(HammerSkill::LDamage), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::LCost), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::LDistance), Some(1)) - }, - Some(ToolKind::Bow) => { - // Bow - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::ProjSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::CRegen), Some(1)) - .with_skill(Skill::Bow(BowSkill::CSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::RCost), Some(1)) - .with_skill(Skill::Bow(BowSkill::UnlockShotgun), None) - .with_skill(Skill::Bow(BowSkill::SCost), Some(1)) - .with_skill(Skill::Bow(BowSkill::RCost), Some(1)) - }, - Some(ToolKind::Staff) => { - // Staff - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Staff)) - .with_skill(Skill::Staff(StaffSkill::FDamage), Some(1)) - .with_skill(Skill::Staff(StaffSkill::FRange), Some(1)) - .with_skill(Skill::Staff(StaffSkill::FDrain), Some(1)) - .with_skill(Skill::Staff(StaffSkill::UnlockShockwave), None) - .with_skill(Skill::Staff(StaffSkill::SDamage), Some(1)) - .with_skill(Skill::Staff(StaffSkill::SRange), Some(1)) - }, - _ => Self::default(), - } - }, - Some(CultistNovice) => { - match active_item { - Some(ToolKind::Sword) => { - // Sword - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Sword)) - .with_skill(Skill::Sword(SwordSkill::TsCombo), None) - .with_skill(Skill::Sword(SwordSkill::TsRegen), Some(1)) - .with_skill(Skill::Sword(SwordSkill::TsSpeed), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DDamage), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DCost), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DDrain), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DScaling), Some(1)) - .with_skill(Skill::Sword(SwordSkill::UnlockSpin), None) - .with_skill(Skill::Sword(SwordSkill::SSpeed), Some(1)) - .with_skill(Skill::Sword(SwordSkill::SSpins), Some(1)) - .with_skill(Skill::Sword(SwordSkill::SCost), Some(1)) - }, - Some(ToolKind::Axe) => { - // Axe - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Axe)) - .with_skill(Skill::Axe(AxeSkill::DsCombo), None) - .with_skill(Skill::Axe(AxeSkill::SInfinite), None) - .with_skill(Skill::Axe(AxeSkill::SHelicopter), None) - .with_skill(Skill::Axe(AxeSkill::SDamage), Some(1)) - .with_skill(Skill::Axe(AxeSkill::UnlockLeap), None) - .with_skill(Skill::Axe(AxeSkill::LKnockback), Some(1)) - .with_skill(Skill::Axe(AxeSkill::LDistance), Some(1)) - }, - Some(ToolKind::Hammer) => { - // Hammer - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Hammer)) - .with_skill(Skill::Hammer(HammerSkill::SsKnockback), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::SsSpeed), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::SsRegen), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::CKnockback), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::CDamage), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::CDrain), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::CSpeed), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::UnlockLeap), None) - .with_skill(Skill::Hammer(HammerSkill::LDamage), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::LKnockback), Some(1)) - }, - Some(ToolKind::Bow) => { - // Bow - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::ProjSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) - .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::RSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::RCost), Some(1)) - .with_skill(Skill::Bow(BowSkill::UnlockShotgun), None) - .with_skill(Skill::Bow(BowSkill::SDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::SArrows), Some(1)) - .with_skill(Skill::Bow(BowSkill::SSpread), Some(1)) - }, - Some(ToolKind::Staff) => { - // Staff - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Staff)) - .with_skill(Skill::Staff(StaffSkill::BDamage), Some(1)) - .with_skill(Skill::Staff(StaffSkill::BRadius), Some(1)) - .with_skill(Skill::Staff(StaffSkill::FDamage), Some(1)) - .with_skill(Skill::Staff(StaffSkill::FRange), Some(1)) - .with_skill(Skill::Staff(StaffSkill::FDrain), Some(1)) - .with_skill(Skill::Staff(StaffSkill::FVelocity), Some(1)) - .with_skill(Skill::Staff(StaffSkill::UnlockShockwave), None) - .with_skill(Skill::Staff(StaffSkill::SDamage), Some(1)) - .with_skill(Skill::Staff(StaffSkill::SRange), Some(1)) - }, - _ => Self::default(), - } - }, - Some(CultistAcolyte) => { - match active_item { - Some(ToolKind::Sword) => { - // Sword - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Sword)) - .with_skill(Skill::Sword(SwordSkill::TsCombo), None) - .with_skill(Skill::Sword(SwordSkill::TsDamage), Some(1)) - .with_skill(Skill::Sword(SwordSkill::TsSpeed), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DDamage), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DScaling), Some(1)) - .with_skill(Skill::Sword(SwordSkill::UnlockSpin), None) - .with_skill(Skill::Sword(SwordSkill::SDamage), Some(1)) - .with_skill(Skill::Sword(SwordSkill::SSpeed), Some(1)) - .with_skill(Skill::Sword(SwordSkill::SSpins), Some(1)) - }, - Some(ToolKind::Axe) => { - // Axe - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Axe)) - .with_skill(Skill::Axe(AxeSkill::DsCombo), None) - .with_skill(Skill::Axe(AxeSkill::DsDamage), Some(1)) - .with_skill(Skill::Axe(AxeSkill::SInfinite), Some(1)) - .with_skill(Skill::Axe(AxeSkill::SDamage), Some(1)) - .with_skill(Skill::Axe(AxeSkill::SCost), Some(1)) - .with_skill(Skill::Axe(AxeSkill::UnlockLeap), None) - .with_skill(Skill::Axe(AxeSkill::LDamage), Some(1)) - .with_skill(Skill::Axe(AxeSkill::LKnockback), Some(1)) - .with_skill(Skill::Axe(AxeSkill::LCost), Some(1)) - .with_skill(Skill::Axe(AxeSkill::LDistance), Some(1)) - }, - Some(ToolKind::Hammer) => { - // Hammer - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Hammer)) - .with_skill(Skill::Hammer(HammerSkill::SsKnockback), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::SsDamage), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::SsRegen), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::CKnockback), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::CDamage), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::CDrain), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::UnlockLeap), None) - .with_skill(Skill::Hammer(HammerSkill::LDamage), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::LRange), Some(1)) - }, - Some(ToolKind::Bow) => { - // Bow - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) - .with_skill(Skill::Bow(BowSkill::CSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::RCost), Some(1)) - .with_skill(Skill::Bow(BowSkill::UnlockShotgun), None) - .with_skill(Skill::Bow(BowSkill::SDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::SSpread), Some(1)) - .with_skill(Skill::Bow(BowSkill::SArrows), Some(1)) - .with_skill(Skill::Bow(BowSkill::SCost), Some(1)) - }, - Some(ToolKind::Staff) => { - // Staff - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Staff)) - .with_skill(Skill::Staff(StaffSkill::BDamage), Some(1)) - .with_skill(Skill::Staff(StaffSkill::BRadius), Some(1)) - .with_skill(Skill::Staff(StaffSkill::FDamage), Some(1)) - .with_skill(Skill::Staff(StaffSkill::FRange), Some(1)) - .with_skill(Skill::Staff(StaffSkill::FVelocity), Some(1)) - .with_skill(Skill::Staff(StaffSkill::UnlockShockwave), None) - .with_skill(Skill::Staff(StaffSkill::SDamage), Some(1)) - .with_skill(Skill::Staff(StaffSkill::SKnockback), Some(1)) - .with_skill(Skill::Staff(StaffSkill::SRange), Some(1)) - }, - _ => Self::default(), - } - }, - Some(Warlord) => { - match active_item { - Some(ToolKind::Sword) => { - // Sword - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Sword)) - .with_skill(Skill::Sword(SwordSkill::TsCombo), None) - .with_skill(Skill::Sword(SwordSkill::TsDamage), Some(1)) - .with_skill(Skill::Sword(SwordSkill::TsRegen), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DDamage), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DCost), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DDrain), Some(1)) - .with_skill(Skill::Sword(SwordSkill::UnlockSpin), None) - .with_skill(Skill::Sword(SwordSkill::SDamage), Some(1)) - .with_skill(Skill::Sword(SwordSkill::SSpins), Some(2)) - .with_skill(Skill::Sword(SwordSkill::SCost), Some(1)) - }, - Some(ToolKind::Axe) => { - // Axe - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Axe)) - .with_skill(Skill::Axe(AxeSkill::DsCombo), None) - .with_skill(Skill::Axe(AxeSkill::DsDamage), Some(1)) - .with_skill(Skill::Axe(AxeSkill::DsSpeed), Some(1)) - .with_skill(Skill::Axe(AxeSkill::DsRegen), Some(1)) - .with_skill(Skill::Axe(AxeSkill::SInfinite), None) - .with_skill(Skill::Axe(AxeSkill::SHelicopter), None) - .with_skill(Skill::Axe(AxeSkill::SDamage), Some(1)) - .with_skill(Skill::Axe(AxeSkill::SSpeed), Some(1)) - .with_skill(Skill::Axe(AxeSkill::UnlockLeap), None) - .with_skill(Skill::Axe(AxeSkill::LDamage), Some(1)) - .with_skill(Skill::Axe(AxeSkill::LKnockback), Some(1)) - .with_skill(Skill::Axe(AxeSkill::LDistance), Some(1)) - }, - Some(ToolKind::Hammer) => { - // Hammer - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Hammer)) - .with_skill(Skill::Hammer(HammerSkill::SsKnockback), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::SsDamage), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::SsSpeed), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::SsRegen), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::CKnockback), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::CDamage), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::CDrain), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::UnlockLeap), None) - .with_skill(Skill::Hammer(HammerSkill::LDamage), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::LDistance), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::LKnockback), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::LRange), Some(1)) - }, - Some(ToolKind::Bow) => { - // Bow - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::CRegen), Some(1)) - .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) - .with_skill(Skill::Bow(BowSkill::CSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::CMove), Some(1)) - .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::RSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::UnlockShotgun), None) - .with_skill(Skill::Bow(BowSkill::SDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::SSpread), Some(1)) - .with_skill(Skill::Bow(BowSkill::SArrows), Some(1)) - .with_skill(Skill::Bow(BowSkill::SCost), Some(1)) - }, - Some(ToolKind::Staff) => { - // Staff - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Staff)) - .with_skill(Skill::Staff(StaffSkill::BDamage), Some(1)) - .with_skill(Skill::Staff(StaffSkill::BRegen), Some(1)) - .with_skill(Skill::Staff(StaffSkill::BRadius), Some(1)) - .with_skill(Skill::Staff(StaffSkill::FDamage), Some(1)) - .with_skill(Skill::Staff(StaffSkill::FDrain), Some(1)) - .with_skill(Skill::Staff(StaffSkill::FVelocity), Some(1)) - .with_skill(Skill::Staff(StaffSkill::UnlockShockwave), None) - .with_skill(Skill::Staff(StaffSkill::SDamage), Some(1)) - .with_skill(Skill::Staff(StaffSkill::SKnockback), Some(1)) - .with_skill(Skill::Staff(StaffSkill::SCost), Some(1)) - }, - _ => Self::default(), - } - }, - Some(Warlock) => { - match active_item { - Some(ToolKind::Sword) => { - // Sword - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Sword)) - .with_skill(Skill::Sword(SwordSkill::TsCombo), None) - .with_skill(Skill::Sword(SwordSkill::TsDamage), Some(1)) - .with_skill(Skill::Sword(SwordSkill::TsRegen), Some(1)) - .with_skill(Skill::Sword(SwordSkill::TsSpeed), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DDamage), Some(2)) - .with_skill(Skill::Sword(SwordSkill::DCost), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DDrain), Some(1)) - .with_skill(Skill::Sword(SwordSkill::DScaling), Some(1)) - .with_skill(Skill::Sword(SwordSkill::UnlockSpin), None) - .with_skill(Skill::Sword(SwordSkill::SDamage), Some(1)) - .with_skill(Skill::Sword(SwordSkill::SSpeed), Some(1)) - .with_skill(Skill::Sword(SwordSkill::SSpins), Some(2)) - .with_skill(Skill::Sword(SwordSkill::SCost), Some(1)) - }, - Some(ToolKind::Axe) => { - // Axe - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Axe)) - .with_skill(Skill::Axe(AxeSkill::DsCombo), None) - .with_skill(Skill::Axe(AxeSkill::DsDamage), Some(1)) - .with_skill(Skill::Axe(AxeSkill::DsSpeed), Some(1)) - .with_skill(Skill::Axe(AxeSkill::DsRegen), Some(1)) - .with_skill(Skill::Axe(AxeSkill::SInfinite), None) - .with_skill(Skill::Axe(AxeSkill::SHelicopter), None) - .with_skill(Skill::Axe(AxeSkill::SDamage), Some(1)) - .with_skill(Skill::Axe(AxeSkill::SSpeed), Some(1)) - .with_skill(Skill::Axe(AxeSkill::SCost), Some(1)) - .with_skill(Skill::Axe(AxeSkill::UnlockLeap), None) - .with_skill(Skill::Axe(AxeSkill::LDamage), Some(1)) - .with_skill(Skill::Axe(AxeSkill::LKnockback), Some(1)) - .with_skill(Skill::Axe(AxeSkill::LCost), Some(1)) - .with_skill(Skill::Axe(AxeSkill::LDistance), Some(1)) - }, - Some(ToolKind::Hammer) => { - // Hammer - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Hammer)) - .with_skill(Skill::Hammer(HammerSkill::SsKnockback), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::SsDamage), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::SsSpeed), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::SsRegen), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::CKnockback), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::CDamage), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::CDrain), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::CSpeed), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::UnlockLeap), None) - .with_skill(Skill::Hammer(HammerSkill::LDamage), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::LCost), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::LDistance), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::LKnockback), Some(1)) - .with_skill(Skill::Hammer(HammerSkill::LRange), Some(1)) - }, - Some(ToolKind::Bow) => { - // Bow - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::ProjSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::CRegen), Some(1)) - .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) - .with_skill(Skill::Bow(BowSkill::CSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::CMove), Some(1)) - .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::RSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::RCost), Some(1)) - .with_skill(Skill::Bow(BowSkill::UnlockShotgun), None) - .with_skill(Skill::Bow(BowSkill::SDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::SSpread), Some(1)) - .with_skill(Skill::Bow(BowSkill::SArrows), Some(1)) - .with_skill(Skill::Bow(BowSkill::SCost), Some(1)) - }, - Some(ToolKind::Staff) => { - // Staff - Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Staff)) - .with_skill(Skill::Staff(StaffSkill::BDamage), Some(1)) - .with_skill(Skill::Staff(StaffSkill::BRegen), Some(1)) - .with_skill(Skill::Staff(StaffSkill::BRadius), Some(1)) - .with_skill(Skill::Staff(StaffSkill::FDamage), Some(1)) - .with_skill(Skill::Staff(StaffSkill::FRange), Some(1)) - .with_skill(Skill::Staff(StaffSkill::FDrain), Some(1)) - .with_skill(Skill::Staff(StaffSkill::FVelocity), Some(1)) - .with_skill(Skill::Staff(StaffSkill::UnlockShockwave), None) - .with_skill(Skill::Staff(StaffSkill::SDamage), Some(1)) - .with_skill(Skill::Staff(StaffSkill::SKnockback), Some(1)) - .with_skill(Skill::Staff(StaffSkill::SRange), Some(1)) - .with_skill(Skill::Staff(StaffSkill::SCost), Some(1)) - }, - _ => Self::default(), - } - }, - Some(Mindflayer) => Self::default() - .with_skill_group(SkillGroupKind::Weapon(ToolKind::Staff)) - .with_skill(Skill::Staff(StaffSkill::BDamage), Some(3)) - .with_skill(Skill::Staff(StaffSkill::BRegen), Some(2)) - .with_skill(Skill::Staff(StaffSkill::BRadius), Some(2)) - .with_skill(Skill::Staff(StaffSkill::FDamage), Some(3)) - .with_skill(Skill::Staff(StaffSkill::FRange), Some(2)) - .with_skill(Skill::Staff(StaffSkill::FDrain), Some(2)) - .with_skill(Skill::Staff(StaffSkill::FVelocity), Some(2)) - .with_skill(Skill::Staff(StaffSkill::UnlockShockwave), None) - .with_skill(Skill::Staff(StaffSkill::SDamage), Some(2)) - .with_skill(Skill::Staff(StaffSkill::SKnockback), Some(2)) - .with_skill(Skill::Staff(StaffSkill::SRange), Some(2)) - .with_skill(Skill::Staff(StaffSkill::SCost), Some(2)), - Some(Villager) | None => Self::default(), + builder.with_asset_expect(asset_specifier) + } + + /// Applies `asset_specifier` with needed skill tree + #[must_use] + pub fn with_asset_expect(mut self, asset_specifier: &str) -> Self { + let tree = skills_from_asset_expect(asset_specifier); + for (skill, level) in tree { + self = self.with_skill(skill, level); } + + self + } + + /// Creates `SkillSetBuilder` for given preset + #[must_use] + pub fn from_preset(preset: Preset) -> Self { + let builder = Self::default(); + + builder.with_preset(preset) + } + + /// Applies preset + #[must_use] + pub const fn with_preset(self, preset: Preset) -> Self { + match preset { + Preset::Empty => {}, + } + self } #[must_use] @@ -689,7 +103,6 @@ impl SkillSetBuilder { /// 2) If added skill already applied /// 3) If added skill wasn't applied at the end pub fn with_skill(mut self, skill: Skill, level: Option) -> Self { - #![warn(clippy::pedantic)] let group = if let Some(skill_group) = skill.skill_group_kind() { skill_group } else { @@ -697,12 +110,7 @@ impl SkillSetBuilder { "Tried to add skill: {:?} which does not have an associated skill group.", skill ); - if cfg!(test) { - panic!("{}", err); - } else { - warn!("{}", err); - } - return self; + common_base::dev_panic!(err, or return self); }; let SkillSetBuilder(ref mut skill_set) = self; @@ -711,12 +119,7 @@ impl SkillSetBuilder { "Tried to add skill: {:?} with level {:?} which is already applied", skill, level, ); - if cfg!(test) { - panic!("{}", err); - } else { - warn!("{}", err); - } - return self; + common_base::dev_panic!(err, or return self); } for _ in 0..level.unwrap_or(1) { skill_set.add_skill_points(group, skill_set.skill_cost(skill)); @@ -728,22 +131,16 @@ impl SkillSetBuilder { available and meets all prerequisite skills.", skill ); - if cfg!(test) { - panic!("{}", err); - } else { - warn!("{}", err); - } + common_base::dev_panic!(err); } self } - pub fn with_skill_group(self, skill_group: SkillGroupKind) -> Self { - self.with_skill(Skill::UnlockGroup(skill_group), None) - } - + #[must_use] pub fn build(self) -> SkillSet { self.0 } } +#[must_use] fn skill_is_applied(skill_set: &SkillSet, skill: Skill, level: Option) -> bool { if let Ok(applied_level) = skill_set.skill_level(skill) { applied_level == level @@ -751,3 +148,45 @@ fn skill_is_applied(skill_set: &SkillSet, skill: Skill, level: Option) -> b false } } + +#[cfg(test)] +mod tests { + use super::*; + use assets::Error; + + #[test] + fn test_all_skillset_assets() { + #[derive(Clone)] + struct SkillSetList(Vec); + + impl assets::Compound for SkillSetList { + fn load( + cache: &assets::AssetCache, + specifier: &str, + ) -> Result { + let list = cache + .load::(specifier)? + .read() + .iter() + .map(|spec| SkillSetTree::load_cloned(spec)) + .collect::>()?; + + Ok(Self(list)) + } + } + + let skillsets = SkillSetList::load_expect_cloned("common.skillset.*").0; + for skillset in skillsets { + std::mem::drop({ + let mut skillset_builder = SkillSetBuilder::default(); + let nodes = skillset.0; + let tree = skills_from_nodes(nodes); + for (skill, level) in tree { + skillset_builder = skillset_builder.with_skill(skill, level); + } + + skillset_builder + }); + } + } +} diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 8b69c6a3ab..1c094b8883 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -6,7 +6,7 @@ use crate::{ }, event::{LocalEvent, ServerEvent}, outcome::Outcome, - skillset_builder::{SkillSetBuilder, SkillSetConfig}, + skillset_builder::{self, SkillSetBuilder}, states::{ behavior::{CharacterBehavior, JoinData}, utils::*, @@ -74,24 +74,33 @@ impl CharacterBehavior for Data { > self.static_data.cast_duration * self.summon_count / self.static_data.summon_amount { - let body = self.static_data.summon_info.body; + let SummonInfo { + body, + loadout_config, + skillset_config, + .. + } = self.static_data.summon_info; - let mut loadout_builder = - LoadoutBuilder::new().with_default_maintool(&body); + let loadout = { + let loadout_builder = + LoadoutBuilder::new().with_default_maintool(&body); + if let Some(preset) = loadout_config { + loadout_builder.with_preset(preset).build() + } else { + loadout_builder.build() + } + }; - if let Some(preset) = self.static_data.summon_info.loadout_config { - loadout_builder = loadout_builder.with_preset(preset); - } - - let loadout = loadout_builder.build(); + let skill_set = { + let skillset_builder = SkillSetBuilder::default(); + if let Some(preset) = skillset_config { + skillset_builder.with_preset(preset).build() + } else { + skillset_builder.build() + } + }; let stats = comp::Stats::new("Summon".to_string()); - let skill_set = SkillSetBuilder::build_skillset( - &None, - self.static_data.summon_info.skillset_config, - ) - .build(); - // Send server event to create npc update.server_events.push_front(ServerEvent::CreateNpc { pos: *data.pos, @@ -179,5 +188,5 @@ pub struct SummonInfo { health_scaling: u16, // TODO: use assets for specifying skills and loadout? loadout_config: Option, - skillset_config: Option, + skillset_config: Option, } diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index b4decd5bad..458c710b28 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -195,44 +195,49 @@ impl<'a> System<'a> for Sys { } let EntityInfo { - skillset_preset, + skillset_asset, main_tool, - loadout_config, + loadout_asset, make_loadout, trading_information: economy, .. } = entity; - let skill_set = - SkillSetBuilder::build_skillset(&main_tool, skillset_preset).build(); + let skill_set = { + let skillset_builder = SkillSetBuilder::default(); + if let Some(skillset_asset) = skillset_asset { + skillset_builder.with_asset_expect(&skillset_asset).build() + } else { + skillset_builder.build() + } + }; - let mut loadout_builder = LoadoutBuilder::new(); - let rng = &mut rand::thread_rng(); + let loadout = { + let mut loadout_builder = LoadoutBuilder::new(); + let rng = &mut rand::thread_rng(); - // If main tool is passed, use it. Otherwise fallback to default tool - if let Some(main_tool) = main_tool { - loadout_builder = loadout_builder.active_mainhand(Some(main_tool)); - } else { - loadout_builder = loadout_builder.with_default_maintool(&body); - } + // If main tool is passed, use it. Otherwise fallback to default tool + if let Some(main_tool) = main_tool { + loadout_builder = loadout_builder.active_mainhand(Some(main_tool)); + } else { + loadout_builder = loadout_builder.with_default_maintool(&body); + } - // If there is config, apply it. - // If not, use default equipement for this body. - match loadout_config { - Some(asset) => { + // If there is config, apply it. + // If not, use default equipement for this body. + if let Some(asset) = loadout_asset { loadout_builder = loadout_builder.with_asset_expect(&asset, rng); - }, - None => { + } else { loadout_builder = loadout_builder.with_default_equipment(&body); - }, - } + } - // Evaluate lazy function for loadout creation - if let Some(make_loadout) = make_loadout { - loadout_builder = loadout_builder.with_creator(make_loadout, economy.as_ref()); - } - - let loadout = loadout_builder.build(); + // Evaluate lazy function for loadout creation + if let Some(make_loadout) = make_loadout { + loadout_builder = + loadout_builder.with_creator(make_loadout, economy.as_ref()); + } + loadout_builder.build() + }; let health = comp::Health::new(body, entity.level.unwrap_or(0)); let poise = comp::Poise::new(body); diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index dc06ebc758..2439027220 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -932,8 +932,7 @@ fn enemy_0(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { &comp::biped_small::Species::Gnarling, ), )) - .with_loot_drop(chosen.read().choose().to_item()) - .with_skillset_preset(common::skillset_builder::SkillSetConfig::Gnarling); + .with_loot_drop(chosen.read().choose().to_item()); match dynamic_rng.gen_range(0..5) { 0 => gnarling.with_asset_expect("common.entity.dungeon.tier-0.bow"), @@ -949,7 +948,6 @@ fn enemy_1(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { .with_body(comp::Body::BipedSmall( comp::biped_small::Body::random_with(dynamic_rng, &comp::biped_small::Species::Adlet), )) - .with_skillset_preset(common::skillset_builder::SkillSetConfig::Adlet) .with_loot_drop(chosen.read().choose().to_item()); match dynamic_rng.gen_range(0..5) { @@ -966,7 +964,6 @@ fn enemy_2(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { .with_body(comp::Body::BipedSmall( comp::biped_small::Body::random_with(dynamic_rng, &comp::biped_small::Species::Sahagin), )) - .with_skillset_preset(common::skillset_builder::SkillSetConfig::Sahagin) .with_loot_drop(chosen.read().choose().to_item()); match dynamic_rng.gen_range(0..5) { @@ -994,7 +991,6 @@ fn enemy_3(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { &comp::biped_small::Species::Haniwa, ), )) - .with_skillset_preset(common::skillset_builder::SkillSetConfig::Haniwa) .with_loot_drop(chosen.read().choose().to_item()); match dynamic_rng.gen_range(0..5) { @@ -1015,7 +1011,6 @@ fn enemy_4(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { &comp::biped_small::Species::Myrmidon, ), )) - .with_skillset_preset(common::skillset_builder::SkillSetConfig::Myrmidon) .with_loot_drop(chosen.read().choose().to_item()); match dynamic_rng.gen_range(0..5) { @@ -1037,12 +1032,10 @@ fn enemy_5(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { )), 1 => entity .with_body(comp::Body::Humanoid(comp::humanoid::Body::random())) - .with_skillset_preset(common::skillset_builder::SkillSetConfig::Warlock) .with_loot_drop(chosen.read().choose().to_item()) .with_asset_expect("common.entity.dungeon.tier-5.warlock"), _ => entity .with_body(comp::Body::Humanoid(comp::humanoid::Body::random())) - .with_skillset_preset(common::skillset_builder::SkillSetConfig::Warlord) .with_loot_drop(chosen.read().choose().to_item()) .with_asset_expect("common.entity.dungeon.tier-5.warlord"), } @@ -1147,9 +1140,8 @@ fn boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec) -> Vec entity - .with_main_tool(Item::new_from_asset_expect( - "common.items.weapons.sword.iron-4", - )) - .with_name("Guard") .with_agent_mark(agent::Mark::Guard) .with_lazy_loadout(guard_loadout) .with_level(dynamic_rng.gen_range(10..15)) - .with_skillset_preset( - common::skillset_builder::SkillSetConfig::Guard, - ), + .with_asset_expect("common.entity.village.guard"), 1 | 2 => entity - .with_main_tool(Item::new_from_asset_expect( - "common.items.weapons.bow.eldwood-0", - )) - .with_name("Merchant") .with_agent_mark(agent::Mark::Merchant) .with_economy(&economy) .with_lazy_loadout(merchant_loadout) .with_level(dynamic_rng.gen_range(10..15)) - .with_skillset_preset( - common::skillset_builder::SkillSetConfig::Merchant, - ), + .with_asset_expect("common.entity.village.merchant"), _ => entity - .with_main_tool(Item::new_from_asset_expect( - match dynamic_rng.gen_range(0..7) { - 0 => "common.items.weapons.tool.broom", - 1 => "common.items.weapons.tool.hoe", - 2 => "common.items.weapons.tool.pickaxe", - 3 => "common.items.weapons.tool.pitchfork", - 4 => "common.items.weapons.tool.rake", - 5 => "common.items.weapons.tool.shovel-0", - _ => "common.items.weapons.tool.shovel-1", - //_ => "common.items.weapons.bow.starter", TODO: Re-Add this when we have a better way of distributing npc_weapons here - }, - )) .with_lazy_loadout(villager_loadout) - .with_skillset_preset( - common::skillset_builder::SkillSetConfig::Villager, - ), + .with_asset_expect("common.entity.village.villager"), } }); From 229b253a1762e5bd2d202e0d110306939e13de01 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Tue, 8 Jun 2021 21:27:35 +0300 Subject: [PATCH 012/180] remove Mindflayer skillset --- assets/common/entity/dungeon/tier-5/boss.ron | 2 +- .../skillset/dungeon/tier-5/mindflayer.ron | 21 ------------------- 2 files changed, 1 insertion(+), 22 deletions(-) delete mode 100644 assets/common/skillset/dungeon/tier-5/mindflayer.ron diff --git a/assets/common/entity/dungeon/tier-5/boss.ron b/assets/common/entity/dungeon/tier-5/boss.ron index 042951cee6..ffa83720a7 100644 --- a/assets/common/entity/dungeon/tier-5/boss.ron +++ b/assets/common/entity/dungeon/tier-5/boss.ron @@ -5,5 +5,5 @@ EntityConfig ( second_tool: None, loadout_asset: None, - skillset_asset: Some("common.skillset.dungeon.tier-5.mindflayer"), + skillset_asset: None, ) diff --git a/assets/common/skillset/dungeon/tier-5/mindflayer.ron b/assets/common/skillset/dungeon/tier-5/mindflayer.ron deleted file mode 100644 index 09cba3e4bd..0000000000 --- a/assets/common/skillset/dungeon/tier-5/mindflayer.ron +++ /dev/null @@ -1,21 +0,0 @@ -([ - Group(Weapon(Staff)), - - // Fireball - Skill((Staff(BDamage), Some(3))), - Skill((Staff(BRegen), Some(2))), - Skill((Staff(BRadius), Some(2))), - - // Flamethrower - Skill((Staff(FDamage), Some(3))), - Skill((Staff(FRange), Some(2))), - Skill((Staff(FDrain), Some(2))), - Skill((Staff(FVelocity), Some(2))), - - // Shockwave - Skill((Staff(UnlockShockwave), None)), - Skill((Staff(SDamage), Some(2))), - Skill((Staff(SKnockback), Some(2))), - Skill((Staff(SRange), Some(2))), - Skill((Staff(SCost), Some(2))), -]) From a4cc1e24ee3b7fbc2d9caa2c5515befb72c2aafc Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Wed, 9 Jun 2021 00:58:15 +0300 Subject: [PATCH 013/180] Move body to EntityConfig assets * currently works only for random and random_with, uses FromStr for NpcKind --- assets/common/entity/dungeon/tier-0/bow.ron | 1 + assets/common/entity/dungeon/tier-0/spear.ron | 1 + assets/common/entity/dungeon/tier-0/staff.ron | 1 + assets/common/entity/dungeon/tier-1/bow.ron | 1 + assets/common/entity/dungeon/tier-1/spear.ron | 1 + assets/common/entity/dungeon/tier-1/staff.ron | 1 + assets/common/entity/dungeon/tier-2/bow.ron | 1 + assets/common/entity/dungeon/tier-2/spear.ron | 1 + assets/common/entity/dungeon/tier-2/staff.ron | 1 + assets/common/entity/dungeon/tier-3/bow.ron | 1 + assets/common/entity/dungeon/tier-3/spear.ron | 1 + assets/common/entity/dungeon/tier-3/staff.ron | 1 + assets/common/entity/dungeon/tier-4/bow.ron | 1 + assets/common/entity/dungeon/tier-4/spear.ron | 1 + assets/common/entity/dungeon/tier-4/staff.ron | 1 + .../entity/dungeon/tier-5/beastmaster.ron | 1 + assets/common/entity/dungeon/tier-5/boss.ron | 1 + assets/common/entity/dungeon/tier-5/husk.ron | 1 + .../common/entity/dungeon/tier-5/warlock.ron | 1 + .../common/entity/dungeon/tier-5/warlord.ron | 1 + assets/common/entity/test.ron | 2 +- assets/common/entity/village/guard.ron | 2 ++ assets/common/entity/village/merchant.ron | 2 ++ assets/common/entity/village/villager.ron | 2 ++ common/src/generation.rs | 35 ++++++++++++++++++- world/src/site/dungeon/mod.rs | 28 --------------- 26 files changed, 61 insertions(+), 30 deletions(-) diff --git a/assets/common/entity/dungeon/tier-0/bow.ron b/assets/common/entity/dungeon/tier-0/bow.ron index e135808266..1c3f4539f2 100644 --- a/assets/common/entity/dungeon/tier-0/bow.ron +++ b/assets/common/entity/dungeon/tier-0/bow.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Gnarling Stalker"), + body: Some(RandomWith("gnarling")), main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.adlet_bow")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-0/spear.ron b/assets/common/entity/dungeon/tier-0/spear.ron index 72c3b46fa3..05cd042552 100644 --- a/assets/common/entity/dungeon/tier-0/spear.ron +++ b/assets/common/entity/dungeon/tier-0/spear.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Gnarling Mugger"), + body: Some(RandomWith("gnarling")), main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.wooden_spear")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-0/staff.ron b/assets/common/entity/dungeon/tier-0/staff.ron index d8928f39fe..43b1948197 100644 --- a/assets/common/entity/dungeon/tier-0/staff.ron +++ b/assets/common/entity/dungeon/tier-0/staff.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Gnarling Shaman"), + body: Some(RandomWith("gnarling")), main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.gnoll_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-1/bow.ron b/assets/common/entity/dungeon/tier-1/bow.ron index e7ea1ed0dc..51c93c9929 100644 --- a/assets/common/entity/dungeon/tier-1/bow.ron +++ b/assets/common/entity/dungeon/tier-1/bow.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Adlet Tracker"), + body: Some(RandomWith("adlet")), main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.adlet_bow")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-1/spear.ron b/assets/common/entity/dungeon/tier-1/spear.ron index 0f2d3322e5..9943b5b3c7 100644 --- a/assets/common/entity/dungeon/tier-1/spear.ron +++ b/assets/common/entity/dungeon/tier-1/spear.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Adlet Hunter"), + body: Some(RandomWith("adlet")), main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.wooden_spear")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-1/staff.ron b/assets/common/entity/dungeon/tier-1/staff.ron index 522ec1d415..d196d2654f 100644 --- a/assets/common/entity/dungeon/tier-1/staff.ron +++ b/assets/common/entity/dungeon/tier-1/staff.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Adlet Shaman"), + body: Some(RandomWith("adlet")), main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.gnoll_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-2/bow.ron b/assets/common/entity/dungeon/tier-2/bow.ron index 73950982ee..435199764c 100644 --- a/assets/common/entity/dungeon/tier-2/bow.ron +++ b/assets/common/entity/dungeon/tier-2/bow.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Sahagin Sniper"), + body: Some(RandomWith("sahagin")), main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.adlet_bow")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-2/spear.ron b/assets/common/entity/dungeon/tier-2/spear.ron index c9499cfa47..b7270579ff 100644 --- a/assets/common/entity/dungeon/tier-2/spear.ron +++ b/assets/common/entity/dungeon/tier-2/spear.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Sahagin Spearman"), + body: Some(RandomWith("sahagin")), main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.wooden_spear")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-2/staff.ron b/assets/common/entity/dungeon/tier-2/staff.ron index 4ecbcd121e..1bf74fc93e 100644 --- a/assets/common/entity/dungeon/tier-2/staff.ron +++ b/assets/common/entity/dungeon/tier-2/staff.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Sahagin Sorcerer"), + body: Some(RandomWith("sahagin")), main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.gnoll_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-3/bow.ron b/assets/common/entity/dungeon/tier-3/bow.ron index e642c37333..a667ca255b 100644 --- a/assets/common/entity/dungeon/tier-3/bow.ron +++ b/assets/common/entity/dungeon/tier-3/bow.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Haniwa Archer"), + body: Some(RandomWith("haniwa")), main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.adlet_bow")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-3/spear.ron b/assets/common/entity/dungeon/tier-3/spear.ron index 4c107bdd73..9d74bd0ba6 100644 --- a/assets/common/entity/dungeon/tier-3/spear.ron +++ b/assets/common/entity/dungeon/tier-3/spear.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Haniwa Guard"), + body: Some(RandomWith("haniwa")), main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.wooden_spear")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-3/staff.ron b/assets/common/entity/dungeon/tier-3/staff.ron index adfe8857e8..649c669272 100644 --- a/assets/common/entity/dungeon/tier-3/staff.ron +++ b/assets/common/entity/dungeon/tier-3/staff.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Haniwa Sorcerer"), + body: Some(RandomWith("haniwa")), main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.gnoll_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-4/bow.ron b/assets/common/entity/dungeon/tier-4/bow.ron index 04cf0e7b02..205cca7248 100644 --- a/assets/common/entity/dungeon/tier-4/bow.ron +++ b/assets/common/entity/dungeon/tier-4/bow.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Myrmidon Marksman"), + body: Some(RandomWith("myrmidon")), main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.adlet_bow")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-4/spear.ron b/assets/common/entity/dungeon/tier-4/spear.ron index d73950e575..c5688f6fe9 100644 --- a/assets/common/entity/dungeon/tier-4/spear.ron +++ b/assets/common/entity/dungeon/tier-4/spear.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Myrmidon Hoplite"), + body: Some(RandomWith("myrmidon")), main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.wooden_spear")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-4/staff.ron b/assets/common/entity/dungeon/tier-4/staff.ron index a022a4b330..2b4926db59 100644 --- a/assets/common/entity/dungeon/tier-4/staff.ron +++ b/assets/common/entity/dungeon/tier-4/staff.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Myrmidon Wizard"), + body: Some(RandomWith("myrmidon")), main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.gnoll_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-5/beastmaster.ron b/assets/common/entity/dungeon/tier-5/beastmaster.ron index 4c2421ac72..3bf5cac4f2 100644 --- a/assets/common/entity/dungeon/tier-5/beastmaster.ron +++ b/assets/common/entity/dungeon/tier-5/beastmaster.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Beastmaster"), + body: Some(RandomWith("humanoid")), main_tool: Some(Choice([ (1.0, Some(Item("common.items.weapons.axe.malachite_axe-0"))), diff --git a/assets/common/entity/dungeon/tier-5/boss.ron b/assets/common/entity/dungeon/tier-5/boss.ron index ffa83720a7..9f7c6d376a 100644 --- a/assets/common/entity/dungeon/tier-5/boss.ron +++ b/assets/common/entity/dungeon/tier-5/boss.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Mindflayer"), + body: Some(RandomWith("mindflayer")), main_tool: None, second_tool: None, diff --git a/assets/common/entity/dungeon/tier-5/husk.ron b/assets/common/entity/dungeon/tier-5/husk.ron index 6c23289a44..0409d98436 100644 --- a/assets/common/entity/dungeon/tier-5/husk.ron +++ b/assets/common/entity/dungeon/tier-5/husk.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Cultist Husk"), + body: Some(RandomWith("husk")), main_tool: None, second_tool: None, diff --git a/assets/common/entity/dungeon/tier-5/warlock.ron b/assets/common/entity/dungeon/tier-5/warlock.ron index 8e5fe4ae70..4553ea5ff9 100644 --- a/assets/common/entity/dungeon/tier-5/warlock.ron +++ b/assets/common/entity/dungeon/tier-5/warlock.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Cultist Warlock"), + body: Some(RandomWith("humanoid")), main_tool: Some(Item("common.items.weapons.staff.cultist_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-5/warlord.ron b/assets/common/entity/dungeon/tier-5/warlord.ron index 37430fc373..53d3605fb0 100644 --- a/assets/common/entity/dungeon/tier-5/warlord.ron +++ b/assets/common/entity/dungeon/tier-5/warlord.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Cultist Warlord"), + body: Some(RandomWith("humanoid")), main_tool: Some(Choice([ (1.0, Some(Item("common.items.weapons.axe_1h.orichalcum-0"))), diff --git a/assets/common/entity/test.ron b/assets/common/entity/test.ron index 915f98ade2..e967f36b6c 100644 --- a/assets/common/entity/test.ron +++ b/assets/common/entity/test.ron @@ -6,7 +6,7 @@ EntityConfig ( /// Can be Exact (Body with all fields e.g BodyType, Species, Hair color and such) /// or Random (will use random if available for this Body) /// or RandomWith (will use random_with if available for this Body) - // body: Humanoid(Random), + body: Some(RandomWith("humanoid")), /// Main and second tools /// Can be Option (with asset_specifier for item) diff --git a/assets/common/entity/village/guard.ron b/assets/common/entity/village/guard.ron index 93d770cb2c..6332487f7a 100644 --- a/assets/common/entity/village/guard.ron +++ b/assets/common/entity/village/guard.ron @@ -1,5 +1,7 @@ EntityConfig ( name: Some("Guard"), + // body is specified outsite + body: None, main_tool: Some(Item("common.items.weapons.sword.iron-4")), second_tool: None, diff --git a/assets/common/entity/village/merchant.ron b/assets/common/entity/village/merchant.ron index 07dd497fdf..c1b39be174 100644 --- a/assets/common/entity/village/merchant.ron +++ b/assets/common/entity/village/merchant.ron @@ -1,5 +1,7 @@ EntityConfig ( name: Some("Merchant"), + // body is specified outsite + body: None, main_tool: Some(Item("common.items.weapons.bow.eldwood-0")), second_tool: None, diff --git a/assets/common/entity/village/villager.ron b/assets/common/entity/village/villager.ron index 45a92d5abb..bef5f5bbad 100644 --- a/assets/common/entity/village/villager.ron +++ b/assets/common/entity/village/villager.ron @@ -1,5 +1,7 @@ EntityConfig ( name: None, + // body is specified outsite + body: None, main_tool: Some(Choice([ (1.0, Some(Item("common.items.weapons.tool.broom"))), diff --git a/common/src/generation.rs b/common/src/generation.rs index f31aa7628e..131815bbe2 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -12,9 +12,15 @@ use crate::{ use serde::Deserialize; use vek::*; +#[derive(Debug, Deserialize, Clone)] +enum BodyKind { + RandomWith(String), +} + #[derive(Debug, Deserialize, Clone)] struct EntityConfig { name: Option, + body: Option, main_tool: Option, second_tool: Option, loadout_asset: Option, @@ -86,6 +92,7 @@ impl EntityInfo { fn with_entity_config(mut self, config: EntityConfig, asset_specifier: Option<&str>) -> Self { let EntityConfig { name, + body, main_tool, second_tool, loadout_asset, @@ -96,6 +103,19 @@ impl EntityInfo { self = self.with_name(name); } + if let Some(body) = body { + match body { + BodyKind::RandomWith(string) => { + let npc::NpcBody(_body_kind, mut body_creator) = + string.parse::().unwrap_or_else(|err| { + panic!("failed to parse body {:?}. Err: {:?}", &string, err) + }); + let body = body_creator(); + self = self.with_body(body); + }, + } + } + let rng = &mut rand::thread_rng(); if let Some(main_tool) = main_tool.and_then(|i| i.try_to_item(asset_specifier.unwrap_or("??"), rng)) @@ -296,7 +316,8 @@ mod tests { second_tool, loadout_asset, skillset_asset, - .. + name: _name, + body, } = config; if let Some(main_tool) = main_tool { @@ -307,6 +328,18 @@ mod tests { second_tool.validate(EquipSlot::ActiveOffhand); } + if let Some(body) = body { + match body { + BodyKind::RandomWith(string) => { + let npc::NpcBody(_body_kind, mut body_creator) = + string.parse::().unwrap_or_else(|err| { + panic!("failed to parse body {:?}. Err: {:?}", &string, err) + }); + std::mem::drop(body_creator()); + }, + } + } + if let Some(loadout_asset) = loadout_asset { let rng = &mut rand::thread_rng(); let builder = LoadoutBuilder::default(); diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index 2439027220..7fc3cd0410 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -926,12 +926,6 @@ fn enemy_0(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-0.enemy"); let gnarling = entity - .with_body(comp::Body::BipedSmall( - comp::biped_small::Body::random_with( - dynamic_rng, - &comp::biped_small::Species::Gnarling, - ), - )) .with_loot_drop(chosen.read().choose().to_item()); match dynamic_rng.gen_range(0..5) { @@ -945,9 +939,6 @@ fn enemy_1(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-1.enemy"); let adlet = entity - .with_body(comp::Body::BipedSmall( - comp::biped_small::Body::random_with(dynamic_rng, &comp::biped_small::Species::Adlet), - )) .with_loot_drop(chosen.read().choose().to_item()); match dynamic_rng.gen_range(0..5) { @@ -961,9 +952,6 @@ fn enemy_2(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-2.enemy"); let sahagin = entity - .with_body(comp::Body::BipedSmall( - comp::biped_small::Body::random_with(dynamic_rng, &comp::biped_small::Species::Sahagin), - )) .with_loot_drop(chosen.read().choose().to_item()); match dynamic_rng.gen_range(0..5) { @@ -985,12 +973,6 @@ fn enemy_3(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { )), _ => { let haniwa = entity - .with_body(comp::Body::BipedSmall( - comp::biped_small::Body::random_with( - dynamic_rng, - &comp::biped_small::Species::Haniwa, - ), - )) .with_loot_drop(chosen.read().choose().to_item()); match dynamic_rng.gen_range(0..5) { @@ -1005,12 +987,6 @@ fn enemy_4(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-4.enemy"); let myrmidon = entity - .with_body(comp::Body::BipedSmall( - comp::biped_small::Body::random_with( - dynamic_rng, - &comp::biped_small::Species::Myrmidon, - ), - )) .with_loot_drop(chosen.read().choose().to_item()); match dynamic_rng.gen_range(0..5) { @@ -1031,11 +1007,9 @@ fn enemy_5(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { "common.items.crafting_ing.twigs", )), 1 => entity - .with_body(comp::Body::Humanoid(comp::humanoid::Body::random())) .with_loot_drop(chosen.read().choose().to_item()) .with_asset_expect("common.entity.dungeon.tier-5.warlock"), _ => entity - .with_body(comp::Body::Humanoid(comp::humanoid::Body::random())) .with_loot_drop(chosen.read().choose().to_item()) .with_asset_expect("common.entity.dungeon.tier-5.warlord"), } @@ -1308,7 +1282,6 @@ mod tests { boss_fallback(&mut dynamic_rng, tile_wcenter); } - //FIXME: it will miss items with rng branching #[test] fn test_creating_enemies() { let mut dynamic_rng = rand::thread_rng(); @@ -1322,7 +1295,6 @@ mod tests { enemy_fallback(&mut dynamic_rng, raw_entity); } - //FIXME: it will miss items with rng branching #[test] fn test_creating_minibosses() { let mut dynamic_rng = rand::thread_rng(); From 057aa7fecffcf7e17f4f149d7c2b5885514c12af Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Wed, 9 Jun 2021 01:07:48 +0300 Subject: [PATCH 014/180] Move loot tables to entityconfigs * Moved all entities in dungeons to assets --- .../common/entity/dungeon/fallback/boss.ron | 12 + .../common/entity/dungeon/fallback/enemy.ron | 20 ++ .../entity/dungeon/fallback/miniboss.ron | 12 + assets/common/entity/dungeon/tier-0/boss.ron | 12 + assets/common/entity/dungeon/tier-0/bow.ron | 2 + .../common/entity/dungeon/tier-0/miniboss.ron | 12 + assets/common/entity/dungeon/tier-0/spear.ron | 2 + assets/common/entity/dungeon/tier-0/staff.ron | 2 + assets/common/entity/dungeon/tier-1/boss.ron | 12 + assets/common/entity/dungeon/tier-1/bow.ron | 2 + assets/common/entity/dungeon/tier-1/rat.ron | 12 + assets/common/entity/dungeon/tier-1/spear.ron | 2 + assets/common/entity/dungeon/tier-1/staff.ron | 2 + assets/common/entity/dungeon/tier-2/boss.ron | 12 + assets/common/entity/dungeon/tier-2/bow.ron | 2 + .../common/entity/dungeon/tier-2/hakulaq.ron | 12 + assets/common/entity/dungeon/tier-2/spear.ron | 2 + assets/common/entity/dungeon/tier-2/staff.ron | 2 + .../entity/dungeon/tier-3/bonerattler.ron | 12 + assets/common/entity/dungeon/tier-3/boss.ron | 12 + assets/common/entity/dungeon/tier-3/bow.ron | 2 + .../common/entity/dungeon/tier-3/sentry.ron | 12 + assets/common/entity/dungeon/tier-3/spear.ron | 2 + assets/common/entity/dungeon/tier-3/staff.ron | 2 + assets/common/entity/dungeon/tier-4/boss.ron | 12 + assets/common/entity/dungeon/tier-4/bow.ron | 2 + .../common/entity/dungeon/tier-4/miniboss.ron | 12 + assets/common/entity/dungeon/tier-4/spear.ron | 2 + assets/common/entity/dungeon/tier-4/staff.ron | 2 + .../entity/dungeon/tier-5/beastmaster.ron | 4 +- assets/common/entity/dungeon/tier-5/boss.ron | 2 + assets/common/entity/dungeon/tier-5/hound.ron | 12 + assets/common/entity/dungeon/tier-5/husk.ron | 2 + .../common/entity/dungeon/tier-5/turret.ron | 12 + .../common/entity/dungeon/tier-5/warlock.ron | 2 + .../common/entity/dungeon/tier-5/warlord.ron | 2 + assets/common/entity/test.ron | 5 +- assets/common/entity/village/guard.ron | 2 + assets/common/entity/village/merchant.ron | 3 + assets/common/entity/village/villager.ron | 2 + common/src/generation.rs | 38 +- world/src/site/dungeon/mod.rs | 337 +++++------------- 42 files changed, 373 insertions(+), 256 deletions(-) create mode 100644 assets/common/entity/dungeon/fallback/boss.ron create mode 100644 assets/common/entity/dungeon/fallback/enemy.ron create mode 100644 assets/common/entity/dungeon/fallback/miniboss.ron create mode 100644 assets/common/entity/dungeon/tier-0/boss.ron create mode 100644 assets/common/entity/dungeon/tier-0/miniboss.ron create mode 100644 assets/common/entity/dungeon/tier-1/boss.ron create mode 100644 assets/common/entity/dungeon/tier-1/rat.ron create mode 100644 assets/common/entity/dungeon/tier-2/boss.ron create mode 100644 assets/common/entity/dungeon/tier-2/hakulaq.ron create mode 100644 assets/common/entity/dungeon/tier-3/bonerattler.ron create mode 100644 assets/common/entity/dungeon/tier-3/boss.ron create mode 100644 assets/common/entity/dungeon/tier-3/sentry.ron create mode 100644 assets/common/entity/dungeon/tier-4/boss.ron create mode 100644 assets/common/entity/dungeon/tier-4/miniboss.ron create mode 100644 assets/common/entity/dungeon/tier-5/hound.ron create mode 100644 assets/common/entity/dungeon/tier-5/turret.ron diff --git a/assets/common/entity/dungeon/fallback/boss.ron b/assets/common/entity/dungeon/fallback/boss.ron new file mode 100644 index 0000000000..caf7fc2256 --- /dev/null +++ b/assets/common/entity/dungeon/fallback/boss.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Crazy Sheep"), + body: Some(RandomWith("sheep")), + + loot: Some(LootTable("common.loot_tables.fallback")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/fallback/enemy.ron b/assets/common/entity/dungeon/fallback/enemy.ron new file mode 100644 index 0000000000..63e9172578 --- /dev/null +++ b/assets/common/entity/dungeon/fallback/enemy.ron @@ -0,0 +1,20 @@ +EntityConfig ( + name: Some("Yan Hus"), + body: Some(RandomWith("humanoid")), + + loot: Some(LootTable("common.loot_tables.fallback")), + + main_tool: Some(Choice([ + (1.0, Some(Item("common.items.weapons.tool.broom"))), + (1.0, Some(Item("common.items.weapons.tool.hoe"))), + (1.0, Some(Item("common.items.weapons.tool.pickaxe"))), + (1.0, Some(Item("common.items.weapons.tool.rake"))), + (1.0, Some(Item("common.items.weapons.tool.shovel-0"))), + (1.0, Some(Item("common.items.weapons.tool.shovel-1"))), + (1.0, Some(Item("common.items.weapons.bow.bone-1"))), + ])), + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/fallback/miniboss.ron b/assets/common/entity/dungeon/fallback/miniboss.ron new file mode 100644 index 0000000000..6f7ba8171e --- /dev/null +++ b/assets/common/entity/dungeon/fallback/miniboss.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Big Goose"), + body: Some(RandomWith("goose")), + + loot: Some(LootTable("common.loot_tables.fallback")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-0/boss.ron b/assets/common/entity/dungeon/tier-0/boss.ron new file mode 100644 index 0000000000..98483efc4d --- /dev/null +++ b/assets/common/entity/dungeon/tier-0/boss.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Harvester"), + body: Some(RandomWith("harvester")), + + loot: Some(LootTable("common.loot_tables.dungeon.tier-0.boss")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-0/bow.ron b/assets/common/entity/dungeon/tier-0/bow.ron index 1c3f4539f2..be2948eb55 100644 --- a/assets/common/entity/dungeon/tier-0/bow.ron +++ b/assets/common/entity/dungeon/tier-0/bow.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Gnarling Stalker"), body: Some(RandomWith("gnarling")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-0.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.adlet_bow")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-0/miniboss.ron b/assets/common/entity/dungeon/tier-0/miniboss.ron new file mode 100644 index 0000000000..411ef1a7b1 --- /dev/null +++ b/assets/common/entity/dungeon/tier-0/miniboss.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Deadwood"), + body: Some(RandomWith("deadwood")), + + loot: Some(LootTable("common.loot_tables.dungeon.tier-0.miniboss")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-0/spear.ron b/assets/common/entity/dungeon/tier-0/spear.ron index 05cd042552..e5a4d9e2ad 100644 --- a/assets/common/entity/dungeon/tier-0/spear.ron +++ b/assets/common/entity/dungeon/tier-0/spear.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Gnarling Mugger"), body: Some(RandomWith("gnarling")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-0.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.wooden_spear")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-0/staff.ron b/assets/common/entity/dungeon/tier-0/staff.ron index 43b1948197..7202904324 100644 --- a/assets/common/entity/dungeon/tier-0/staff.ron +++ b/assets/common/entity/dungeon/tier-0/staff.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Gnarling Shaman"), body: Some(RandomWith("gnarling")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-0.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.gnoll_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-1/boss.ron b/assets/common/entity/dungeon/tier-1/boss.ron new file mode 100644 index 0000000000..30035d7c3f --- /dev/null +++ b/assets/common/entity/dungeon/tier-1/boss.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Yeti"), + body: Some(RandomWith("yeti")), + + loot: Some(LootTable("common.loot_tables.dungeon.tier-1.boss")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-1/bow.ron b/assets/common/entity/dungeon/tier-1/bow.ron index 51c93c9929..013fd7c04c 100644 --- a/assets/common/entity/dungeon/tier-1/bow.ron +++ b/assets/common/entity/dungeon/tier-1/bow.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Adlet Tracker"), body: Some(RandomWith("adlet")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-1.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.adlet_bow")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-1/rat.ron b/assets/common/entity/dungeon/tier-1/rat.ron new file mode 100644 index 0000000000..565057282d --- /dev/null +++ b/assets/common/entity/dungeon/tier-1/rat.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Rat"), + body: Some(RandomWith("rat")), + + loot: Some(LootTable("common.loot_tables.creature.quad_small.generic")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-1/spear.ron b/assets/common/entity/dungeon/tier-1/spear.ron index 9943b5b3c7..0ce2a1b28c 100644 --- a/assets/common/entity/dungeon/tier-1/spear.ron +++ b/assets/common/entity/dungeon/tier-1/spear.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Adlet Hunter"), body: Some(RandomWith("adlet")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-1.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.wooden_spear")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-1/staff.ron b/assets/common/entity/dungeon/tier-1/staff.ron index d196d2654f..d9dc6d2a3e 100644 --- a/assets/common/entity/dungeon/tier-1/staff.ron +++ b/assets/common/entity/dungeon/tier-1/staff.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Adlet Shaman"), body: Some(RandomWith("adlet")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-1.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.gnoll_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-2/boss.ron b/assets/common/entity/dungeon/tier-2/boss.ron new file mode 100644 index 0000000000..4db8d8d5d2 --- /dev/null +++ b/assets/common/entity/dungeon/tier-2/boss.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Tidal Warrior"), + body: Some(RandomWith("tidalwarrior")), + + loot: Some(LootTable("common.loot_tables.dungeon.tier-2.boss")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-2/bow.ron b/assets/common/entity/dungeon/tier-2/bow.ron index 435199764c..abd5927b14 100644 --- a/assets/common/entity/dungeon/tier-2/bow.ron +++ b/assets/common/entity/dungeon/tier-2/bow.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Sahagin Sniper"), body: Some(RandomWith("sahagin")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-2.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.adlet_bow")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-2/hakulaq.ron b/assets/common/entity/dungeon/tier-2/hakulaq.ron new file mode 100644 index 0000000000..8f279e3943 --- /dev/null +++ b/assets/common/entity/dungeon/tier-2/hakulaq.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Hakulaq"), + body: Some(RandomWith("hakulaq")), + + loot: Some(LootTable("common.loot_tables.creature.quad_low.fanged")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-2/spear.ron b/assets/common/entity/dungeon/tier-2/spear.ron index b7270579ff..bce3699599 100644 --- a/assets/common/entity/dungeon/tier-2/spear.ron +++ b/assets/common/entity/dungeon/tier-2/spear.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Sahagin Spearman"), body: Some(RandomWith("sahagin")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-2.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.wooden_spear")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-2/staff.ron b/assets/common/entity/dungeon/tier-2/staff.ron index 1bf74fc93e..e41cddb070 100644 --- a/assets/common/entity/dungeon/tier-2/staff.ron +++ b/assets/common/entity/dungeon/tier-2/staff.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Sahagin Sorcerer"), body: Some(RandomWith("sahagin")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-2.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.gnoll_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-3/bonerattler.ron b/assets/common/entity/dungeon/tier-3/bonerattler.ron new file mode 100644 index 0000000000..1fbf9329eb --- /dev/null +++ b/assets/common/entity/dungeon/tier-3/bonerattler.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Bonerattler"), + body: Some(RandomWith("bonerattler")), + + loot: Some(LootTable("common.loot_tables.creature.quad_medium.carapace")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-3/boss.ron b/assets/common/entity/dungeon/tier-3/boss.ron new file mode 100644 index 0000000000..cf6ddc11ed --- /dev/null +++ b/assets/common/entity/dungeon/tier-3/boss.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Clay Golem"), + body: Some(RandomWith("claygolem")), + + loot: Some(LootTable("common.loot_tables.dungeon.tier-3.boss")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-3/bow.ron b/assets/common/entity/dungeon/tier-3/bow.ron index a667ca255b..f693a38d03 100644 --- a/assets/common/entity/dungeon/tier-3/bow.ron +++ b/assets/common/entity/dungeon/tier-3/bow.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Haniwa Archer"), body: Some(RandomWith("haniwa")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-3.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.adlet_bow")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-3/sentry.ron b/assets/common/entity/dungeon/tier-3/sentry.ron new file mode 100644 index 0000000000..724f259ee4 --- /dev/null +++ b/assets/common/entity/dungeon/tier-3/sentry.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Haniwa Sentry"), + body: None, + + loot: Some(Item("common.items.crafting_ing.stones")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-3/spear.ron b/assets/common/entity/dungeon/tier-3/spear.ron index 9d74bd0ba6..376d2d67e6 100644 --- a/assets/common/entity/dungeon/tier-3/spear.ron +++ b/assets/common/entity/dungeon/tier-3/spear.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Haniwa Guard"), body: Some(RandomWith("haniwa")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-3.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.wooden_spear")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-3/staff.ron b/assets/common/entity/dungeon/tier-3/staff.ron index 649c669272..f8d051f2b7 100644 --- a/assets/common/entity/dungeon/tier-3/staff.ron +++ b/assets/common/entity/dungeon/tier-3/staff.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Haniwa Sorcerer"), body: Some(RandomWith("haniwa")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-3.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.gnoll_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-4/boss.ron b/assets/common/entity/dungeon/tier-4/boss.ron new file mode 100644 index 0000000000..5f8883c1dd --- /dev/null +++ b/assets/common/entity/dungeon/tier-4/boss.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Minotaur"), + body: Some(RandomWith("minotaur")), + + loot: Some(LootTable("common.loot_tables.dungeon.tier-4.boss")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-4/bow.ron b/assets/common/entity/dungeon/tier-4/bow.ron index 205cca7248..04ec56dc59 100644 --- a/assets/common/entity/dungeon/tier-4/bow.ron +++ b/assets/common/entity/dungeon/tier-4/bow.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Myrmidon Marksman"), body: Some(RandomWith("myrmidon")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-4.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.adlet_bow")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-4/miniboss.ron b/assets/common/entity/dungeon/tier-4/miniboss.ron new file mode 100644 index 0000000000..46ba728ead --- /dev/null +++ b/assets/common/entity/dungeon/tier-4/miniboss.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Dullahan"), + body: Some(RandomWith("dullahan")), + + loot: Some(LootTable("common.loot_tables.dungeon.tier-4.miniboss")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-4/spear.ron b/assets/common/entity/dungeon/tier-4/spear.ron index c5688f6fe9..dffc6440dd 100644 --- a/assets/common/entity/dungeon/tier-4/spear.ron +++ b/assets/common/entity/dungeon/tier-4/spear.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Myrmidon Hoplite"), body: Some(RandomWith("myrmidon")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-4.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.wooden_spear")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-4/staff.ron b/assets/common/entity/dungeon/tier-4/staff.ron index 2b4926db59..8d432875d2 100644 --- a/assets/common/entity/dungeon/tier-4/staff.ron +++ b/assets/common/entity/dungeon/tier-4/staff.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Myrmidon Wizard"), body: Some(RandomWith("myrmidon")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-4.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.gnoll_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-5/beastmaster.ron b/assets/common/entity/dungeon/tier-5/beastmaster.ron index 3bf5cac4f2..29987a13e2 100644 --- a/assets/common/entity/dungeon/tier-5/beastmaster.ron +++ b/assets/common/entity/dungeon/tier-5/beastmaster.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Beastmaster"), body: Some(RandomWith("humanoid")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-5.miniboss")), + main_tool: Some(Choice([ (1.0, Some(Item("common.items.weapons.axe.malachite_axe-0"))), (1.0, Some(Item("common.items.weapons.sword.bloodsteel-1"))), @@ -10,6 +12,6 @@ EntityConfig ( second_tool: None, loadout_asset: Some("common.loadout.dungeon.tier-5.beastmaster"), - // TODO: make skillset for him, I'm just too lazy + // TODO: make own skillset for him? skillset_asset: Some("common.skillset.dungeon.tier-5.enemy"), ) diff --git a/assets/common/entity/dungeon/tier-5/boss.ron b/assets/common/entity/dungeon/tier-5/boss.ron index 9f7c6d376a..9957ec5482 100644 --- a/assets/common/entity/dungeon/tier-5/boss.ron +++ b/assets/common/entity/dungeon/tier-5/boss.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Mindflayer"), body: Some(RandomWith("mindflayer")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-5.boss")), + main_tool: None, second_tool: None, diff --git a/assets/common/entity/dungeon/tier-5/hound.ron b/assets/common/entity/dungeon/tier-5/hound.ron new file mode 100644 index 0000000000..0b78c67746 --- /dev/null +++ b/assets/common/entity/dungeon/tier-5/hound.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Tamed Darkhound"), + body: Some(RandomWith("darkhound")), + + loot: Some(LootTable("common.loot_tables.dungeon.tier-5.minion")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-5/husk.ron b/assets/common/entity/dungeon/tier-5/husk.ron index 0409d98436..a28e46db0e 100644 --- a/assets/common/entity/dungeon/tier-5/husk.ron +++ b/assets/common/entity/dungeon/tier-5/husk.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Cultist Husk"), body: Some(RandomWith("husk")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-5.minion")), + main_tool: None, second_tool: None, diff --git a/assets/common/entity/dungeon/tier-5/turret.ron b/assets/common/entity/dungeon/tier-5/turret.ron new file mode 100644 index 0000000000..bb84413d6a --- /dev/null +++ b/assets/common/entity/dungeon/tier-5/turret.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Possessed Turret"), + body: None, + + loot: Some(Item("common.items.crafting_ing.twigs")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-5/warlock.ron b/assets/common/entity/dungeon/tier-5/warlock.ron index 4553ea5ff9..0932bd714c 100644 --- a/assets/common/entity/dungeon/tier-5/warlock.ron +++ b/assets/common/entity/dungeon/tier-5/warlock.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Cultist Warlock"), body: Some(RandomWith("humanoid")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-5.enemy")), + main_tool: Some(Item("common.items.weapons.staff.cultist_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-5/warlord.ron b/assets/common/entity/dungeon/tier-5/warlord.ron index 53d3605fb0..b85cb504de 100644 --- a/assets/common/entity/dungeon/tier-5/warlord.ron +++ b/assets/common/entity/dungeon/tier-5/warlord.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Cultist Warlord"), body: Some(RandomWith("humanoid")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-5.enemy")), + main_tool: Some(Choice([ (1.0, Some(Item("common.items.weapons.axe_1h.orichalcum-0"))), (2.0, Some(Item("common.items.weapons.sword.cultist"))), diff --git a/assets/common/entity/test.ron b/assets/common/entity/test.ron index e967f36b6c..20c8ff8fb6 100644 --- a/assets/common/entity/test.ron +++ b/assets/common/entity/test.ron @@ -4,8 +4,7 @@ EntityConfig ( /// Body /// Can be Exact (Body with all fields e.g BodyType, Species, Hair color and such) - /// or Random (will use random if available for this Body) - /// or RandomWith (will use random_with if available for this Body) + /// or RandomWith (will generate random body or species) body: Some(RandomWith("humanoid")), /// Main and second tools @@ -24,7 +23,7 @@ EntityConfig ( /// Loot /// Can be Item (with asset_specifier for item) /// or LootTable (with asset_specifier for loot table) - // loot: LootTable("common.loot_tables.humanoids"), + loot: Some(LootTable("common.loot_tables.humanoids")), /// Meta Info (level, alignment, agency, etc) // meta: {}, diff --git a/assets/common/entity/village/guard.ron b/assets/common/entity/village/guard.ron index 6332487f7a..a80314380c 100644 --- a/assets/common/entity/village/guard.ron +++ b/assets/common/entity/village/guard.ron @@ -3,6 +3,8 @@ EntityConfig ( // body is specified outsite body: None, + loot: None, + main_tool: Some(Item("common.items.weapons.sword.iron-4")), second_tool: None, diff --git a/assets/common/entity/village/merchant.ron b/assets/common/entity/village/merchant.ron index c1b39be174..7fb1de003d 100644 --- a/assets/common/entity/village/merchant.ron +++ b/assets/common/entity/village/merchant.ron @@ -3,6 +3,9 @@ EntityConfig ( // body is specified outsite body: None, + // considering giving some gold/gems/materials? + loot: None, + main_tool: Some(Item("common.items.weapons.bow.eldwood-0")), second_tool: None, diff --git a/assets/common/entity/village/villager.ron b/assets/common/entity/village/villager.ron index bef5f5bbad..37d6528807 100644 --- a/assets/common/entity/village/villager.ron +++ b/assets/common/entity/village/villager.ron @@ -3,6 +3,8 @@ EntityConfig ( // body is specified outsite body: None, + loot: None, + main_tool: Some(Choice([ (1.0, Some(Item("common.items.weapons.tool.broom"))), (1.0, Some(Item("common.items.weapons.tool.hoe"))), diff --git a/common/src/generation.rs b/common/src/generation.rs index 131815bbe2..4994a93702 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -5,6 +5,7 @@ use crate::{ inventory::loadout_builder::{ItemSpec, LoadoutBuilder}, Alignment, Body, Item, }, + lottery::{LootSpec, Lottery}, npc::{self, NPC_NAMES}, trade, trade::SiteInformation, @@ -17,10 +18,17 @@ enum BodyKind { RandomWith(String), } +#[derive(Debug, Deserialize, Clone)] +enum LootKind { + Item(String), + LootTable(String), +} + #[derive(Debug, Deserialize, Clone)] struct EntityConfig { name: Option, body: Option, + loot: Option, main_tool: Option, second_tool: Option, loadout_asset: Option, @@ -93,6 +101,7 @@ impl EntityInfo { let EntityConfig { name, body, + loot, main_tool, second_tool, loadout_asset, @@ -116,6 +125,19 @@ impl EntityInfo { } } + if let Some(loot) = loot { + match loot { + LootKind::Item(asset) => { + self = self.with_loot_drop(Item::new_from_asset_expect(&asset)); + }, + LootKind::LootTable(asset) => { + let table = Lottery::::load_expect(&asset); + let drop = table.read().choose().to_item(); + self = self.with_loot_drop(drop); + }, + } + } + let rng = &mut rand::thread_rng(); if let Some(main_tool) = main_tool.and_then(|i| i.try_to_item(asset_specifier.unwrap_or("??"), rng)) @@ -318,6 +340,7 @@ mod tests { skillset_asset, name: _name, body, + loot, } = config; if let Some(main_tool) = main_tool { @@ -335,7 +358,20 @@ mod tests { string.parse::().unwrap_or_else(|err| { panic!("failed to parse body {:?}. Err: {:?}", &string, err) }); - std::mem::drop(body_creator()); + let _ = body_creator(); + }, + } + } + + if let Some(loot) = loot { + match loot { + LootKind::Item(asset) => { + std::mem::drop(Item::new_from_asset_expect(&asset)); + }, + LootKind::LootTable(asset) => { + // we need to just load it check if it exists, + // because all loot tables are tested in Lottery module + let _ = Lottery::::load_expect(&asset); }, } } diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index 7fc3cd0410..aa6b6bda4a 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -9,11 +9,10 @@ use crate::{ }; use common::{ - assets::{AssetExt, AssetHandle}, + assets::AssetHandle, astar::Astar, comp::{self}, generation::{ChunkSupplement, EntityInfo}, - lottery::{LootSpec, Lottery}, store::{Id, Store}, terrain::{Block, BlockKind, SpriteKind, Structure, StructuresGroup, TerrainChunkSize}, vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, WriteVol}, @@ -567,7 +566,7 @@ impl Floor { 3 => enemy_3(dynamic_rng, raw_entity), 4 => enemy_4(dynamic_rng, raw_entity), 5 => enemy_5(dynamic_rng, raw_entity), - _ => enemy_fallback(dynamic_rng, raw_entity), + _ => enemy_fallback(raw_entity), }; supplement.add_entity( entity.with_alignment(comp::Alignment::Enemy).with_level( @@ -597,13 +596,13 @@ impl Floor { if tile_pos == boss_spawn_tile && tile_wcenter.xy() == wpos2d { let entities = match room.difficulty { - 0 => boss_0(dynamic_rng, tile_wcenter), - 1 => boss_1(dynamic_rng, tile_wcenter), - 2 => boss_2(dynamic_rng, tile_wcenter), - 3 => boss_3(dynamic_rng, tile_wcenter), - 4 => boss_4(dynamic_rng, tile_wcenter), - 5 => boss_5(dynamic_rng, tile_wcenter), - _ => boss_fallback(dynamic_rng, tile_wcenter), + 0 => boss_0(tile_wcenter), + 1 => boss_1(tile_wcenter), + 2 => boss_2(tile_wcenter), + 3 => boss_3(tile_wcenter), + 4 => boss_4(tile_wcenter), + 5 => boss_5(tile_wcenter), + _ => boss_fallback(tile_wcenter), }; for entity in entities { @@ -640,13 +639,13 @@ impl Floor { if tile_pos == miniboss_spawn_tile && tile_wcenter.xy() == wpos2d { let entities = match room.difficulty { - 0 => mini_boss_0(dynamic_rng, tile_wcenter), - 1 => mini_boss_1(dynamic_rng, tile_wcenter), - 2 => mini_boss_2(dynamic_rng, tile_wcenter), - 3 => mini_boss_3(dynamic_rng, tile_wcenter), - 4 => mini_boss_4(dynamic_rng, tile_wcenter), + 0 => mini_boss_0(tile_wcenter), + 1 => mini_boss_1(tile_wcenter), + 2 => mini_boss_2(tile_wcenter), + 3 => mini_boss_3(tile_wcenter), + 4 => mini_boss_4(tile_wcenter), 5 => mini_boss_5(dynamic_rng, tile_wcenter), - _ => mini_boss_fallback(dynamic_rng, tile_wcenter), + _ => mini_boss_fallback(tile_wcenter), }; for entity in entities { @@ -923,292 +922,150 @@ impl Floor { } fn enemy_0(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-0.enemy"); - - let gnarling = entity - .with_loot_drop(chosen.read().choose().to_item()); - match dynamic_rng.gen_range(0..5) { - 0 => gnarling.with_asset_expect("common.entity.dungeon.tier-0.bow"), - 1 => gnarling.with_asset_expect("common.entity.dungeon.tier-0.staff"), - _ => gnarling.with_asset_expect("common.entity.dungeon.tier-0.spear"), + 0 => entity.with_asset_expect("common.entity.dungeon.tier-0.bow"), + 1 => entity.with_asset_expect("common.entity.dungeon.tier-0.staff"), + _ => entity.with_asset_expect("common.entity.dungeon.tier-0.spear"), } } fn enemy_1(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-1.enemy"); - - let adlet = entity - .with_loot_drop(chosen.read().choose().to_item()); - match dynamic_rng.gen_range(0..5) { - 0 => adlet.with_asset_expect("common.entity.dungeon.tier-1.bow"), - 1 => adlet.with_asset_expect("common.entity.dungeon.tier-1.staff"), - _ => adlet.with_asset_expect("common.entity.dungeon.tier-1.spear"), + 0 => entity.with_asset_expect("common.entity.dungeon.tier-1.bow"), + 1 => entity.with_asset_expect("common.entity.dungeon.tier-1.staff"), + _ => entity.with_asset_expect("common.entity.dungeon.tier-1.spear"), } } fn enemy_2(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-2.enemy"); - - let sahagin = entity - .with_loot_drop(chosen.read().choose().to_item()); - match dynamic_rng.gen_range(0..5) { - 0 => sahagin.with_asset_expect("common.entity.dungeon.tier-2.bow"), - 1 => sahagin.with_asset_expect("common.entity.dungeon.tier-2.staff"), - _ => sahagin.with_asset_expect("common.entity.dungeon.tier-2.spear"), + 0 => entity.with_asset_expect("common.entity.dungeon.tier-2.bow"), + 1 => entity.with_asset_expect("common.entity.dungeon.tier-2.staff"), + _ => entity.with_asset_expect("common.entity.dungeon.tier-2.spear"), } } fn enemy_3(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-3.enemy"); - - match dynamic_rng.gen_range(0..4) { + match dynamic_rng.gen_range(0..5) { 0 => entity .with_body(comp::Body::Object(comp::object::Body::HaniwaSentry)) - .with_name("Haniwa Sentry".to_string()) - .with_loot_drop(comp::Item::new_from_asset_expect( - "common.items.crafting_ing.stones", - )), - _ => { - let haniwa = entity - .with_loot_drop(chosen.read().choose().to_item()); - - match dynamic_rng.gen_range(0..5) { - 0 => haniwa.with_asset_expect("common.entity.dungeon.tier-3.bow"), - 1 => haniwa.with_asset_expect("common.entity.dungeon.tier-3.staff"), - _ => haniwa.with_asset_expect("common.entity.dungeon.tier-3.spear"), - } - }, + .with_asset_expect("common.entity.dungeon.tier-3.sentry"), + 1 => entity.with_asset_expect("common.entity.dungeon.tier-3.bow"), + 2 => entity.with_asset_expect("common.entity.dungeon.tier-3.staff"), + _ => entity.with_asset_expect("common.entity.dungeon.tier-3.spear"), } } fn enemy_4(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-4.enemy"); - - let myrmidon = entity - .with_loot_drop(chosen.read().choose().to_item()); - match dynamic_rng.gen_range(0..5) { - 0 => myrmidon.with_asset_expect("common.entity.dungeon.tier-4.bow"), - 1 => myrmidon.with_asset_expect("common.entity.dungeon.tier-4.staff"), - _ => myrmidon.with_asset_expect("common.entity.dungeon.tier-4.spear"), + 0 => entity.with_asset_expect("common.entity.dungeon.tier-4.bow"), + 1 => entity.with_asset_expect("common.entity.dungeon.tier-4.staff"), + _ => entity.with_asset_expect("common.entity.dungeon.tier-4.spear"), } } fn enemy_5(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-5.enemy"); - match dynamic_rng.gen_range(0..6) { 0 => entity .with_body(comp::Body::Object(comp::object::Body::Crossbow)) - .with_name("Possessed Turret".to_string()) - .with_loot_drop(comp::Item::new_from_asset_expect( - "common.items.crafting_ing.twigs", - )), - 1 => entity - .with_loot_drop(chosen.read().choose().to_item()) - .with_asset_expect("common.entity.dungeon.tier-5.warlock"), - _ => entity - .with_loot_drop(chosen.read().choose().to_item()) - .with_asset_expect("common.entity.dungeon.tier-5.warlord"), + .with_asset_expect("common.entity.dungeon.tier-5.turret"), + 1 => entity.with_asset_expect("common.entity.dungeon.tier-5.warlock"), + _ => entity.with_asset_expect("common.entity.dungeon.tier-5.warlord"), } } -fn enemy_fallback(_dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { - let chosen = Lottery::::load_expect("common.loot_tables.fallback"); - - entity - .with_name("Humanoid") - .with_loot_drop(chosen.read().choose().to_item()) - .with_main_tool(comp::Item::new_from_asset_expect( - "common.items.weapons.bow.bone-1", - )) +fn enemy_fallback(entity: EntityInfo) -> EntityInfo { + entity.with_asset_expect("common.entity.dungeon.fallback.enemy") } -fn boss_0(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-0.boss"); - +fn boss_0(tile_wcenter: Vec3) -> Vec { vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::BipedLarge( - comp::biped_large::Body::random_with( - dynamic_rng, - &comp::biped_large::Species::Harvester, - ), - )) - .with_name("Harvester".to_string()) - .with_loot_drop(chosen.read().choose().to_item()), + .with_asset_expect("common.entity.dungeon.tier-0.boss"), ] } -fn boss_1(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-1.boss"); +fn boss_1(tile_wcenter: Vec3) -> Vec { vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::BipedLarge( - comp::biped_large::Body::random_with( - dynamic_rng, - &comp::biped_large::Species::Yeti, - ), - )) - .with_name("Yeti".to_string()) - .with_loot_drop(chosen.read().choose().to_item()), + .with_asset_expect("common.entity.dungeon.tier-1.boss"), ] } -fn boss_2(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-2.boss"); +fn boss_2(tile_wcenter: Vec3) -> Vec { vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::BipedLarge( - comp::biped_large::Body::random_with( - dynamic_rng, - &comp::biped_large::Species::Tidalwarrior, - ), - )) - .with_name("Tidal Warrior".to_string()) - .with_loot_drop(chosen.read().choose().to_item()), + .with_asset_expect("common.entity.dungeon.tier-2.boss"), ] } -fn boss_3(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-3.boss"); - +fn boss_3(tile_wcenter: Vec3) -> Vec { let mut entities = Vec::new(); entities.resize_with(2, || { EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::Golem(comp::golem::Body::random_with( - dynamic_rng, - &comp::golem::Species::ClayGolem, - ))) - .with_name("Clay Golem".to_string()) - .with_loot_drop(chosen.read().choose().to_item()) + .with_asset_expect("common.entity.dungeon.tier-3.boss") }); + entities } -fn boss_4(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-4.boss"); - +fn boss_4(tile_wcenter: Vec3) -> Vec { vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::BipedLarge( - comp::biped_large::Body::random_with( - dynamic_rng, - &comp::biped_large::Species::Minotaur, - ), - )) - .with_name("Minotaur".to_string()) - .with_loot_drop(chosen.read().choose().to_item()), + .with_asset_expect("common.entity.dungeon.tier-4.boss"), ] } -fn boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-5.boss"); - +fn boss_5(tile_wcenter: Vec3) -> Vec { vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::BipedLarge( - comp::biped_large::Body::random_with( - dynamic_rng, - &comp::biped_large::Species::Mindflayer, - ), - )) - .with_loot_drop(chosen.read().choose().to_item()) .with_asset_expect("common.entity.dungeon.tier-5.boss"), ] } -fn boss_fallback(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - vec![ - EntityInfo::at(tile_wcenter.map(|e| e as f32)).with_body(comp::Body::QuadrupedSmall( - comp::quadruped_small::Body::random_with( - dynamic_rng, - &comp::quadruped_small::Species::Sheep, - ), - )), - ] -} - -fn mini_boss_0(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-0.miniboss"); +fn boss_fallback(tile_wcenter: Vec3) -> Vec { vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::QuadrupedLow( - comp::quadruped_low::Body::random_with( - dynamic_rng, - &comp::quadruped_low::Species::Deadwood, - ), - )) - .with_name("Deadwood".to_string()) - .with_loot_drop(chosen.read().choose().to_item()), + .with_asset_expect("common.entity.dungeon.fallback.boss"), ] } -fn mini_boss_1(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = Lottery::::load_expect("common.loot_tables.creature.quad_small.generic"); +fn mini_boss_0(tile_wcenter: Vec3) -> Vec { + vec![ + EntityInfo::at(tile_wcenter.map(|e| e as f32)) + .with_asset_expect("common.entity.dungeon.tier-0.miniboss"), + ] +} + +fn mini_boss_1(tile_wcenter: Vec3) -> Vec { let mut entities = Vec::new(); entities.resize_with(8, || { EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::QuadrupedSmall( - comp::quadruped_small::Body::random_with( - dynamic_rng, - &comp::quadruped_small::Species::Rat, - ), - )) - .with_name("Rat".to_string()) - .with_loot_drop(chosen.read().choose().to_item()) + .with_asset_expect("common.entity.dungeon.tier-1.rat") }); entities } -fn mini_boss_2(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = Lottery::::load_expect("common.loot_tables.creature.quad_low.fanged"); +fn mini_boss_2(tile_wcenter: Vec3) -> Vec { let mut entities = Vec::new(); entities.resize_with(6, || { EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::QuadrupedLow( - comp::quadruped_low::Body::random_with( - dynamic_rng, - &comp::quadruped_low::Species::Hakulaq, - ), - )) - .with_name("Hakulaq".to_string()) - .with_loot_drop(chosen.read().choose().to_item()) + .with_asset_expect("common.entity.dungeon.tier-2.hakulaq") }); entities } -fn mini_boss_3(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = - Lottery::::load_expect("common.loot_tables.creature.quad_medium.carapace"); +fn mini_boss_3(tile_wcenter: Vec3) -> Vec { let mut entities = Vec::new(); entities.resize_with(3, || { EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::QuadrupedMedium( - comp::quadruped_medium::Body::random_with( - dynamic_rng, - &comp::quadruped_medium::Species::Bonerattler, - ), - )) - .with_name("Bonerattler".to_string()) - .with_loot_drop(chosen.read().choose().to_item()) + .with_asset_expect("common.entity.dungeon.tier-3.bonerattler") }); entities } -fn mini_boss_4(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-4.miniboss"); +fn mini_boss_4(tile_wcenter: Vec3) -> Vec { vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::BipedLarge( - comp::biped_large::Body::random_with( - dynamic_rng, - &comp::biped_large::Species::Dullahan, - ), - )) - .with_name("Dullahan Guard".to_string()) - .with_loot_drop(chosen.read().choose().to_item()), + .with_asset_expect("common.entity.dungeon.tier-4.miniboss"), ] } @@ -1216,40 +1073,18 @@ fn mini_boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let trainer_loot = - Lottery::::load_expect("common.loot_tables.dungeon.tier-5.miniboss"); - let hound_loot = - Lottery::::load_expect("common.loot_tables.dungeon.tier-5.minion"); entities.push( EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::Humanoid(comp::humanoid::Body::random())) - .with_loot_drop(trainer_loot.read().choose().to_item()) .with_asset_expect("common.entity.dungeon.tier-5.beastmaster"), ); entities.resize_with(entities.len() + 2, || { EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::QuadrupedMedium( - comp::quadruped_medium::Body::random_with( - dynamic_rng, - &comp::quadruped_medium::Species::Darkhound, - ), - )) - .with_name("Tamed Darkhound".to_string()) - .with_loot_drop(hound_loot.read().choose().to_item()) + .with_asset_expect("common.entity.dungeon.tier-5.hound") }); }, _ => { - let chosen = - Lottery::::load_expect("common.loot_tables.dungeon.tier-5.minion"); entities.resize_with(10, || { EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::BipedSmall( - comp::biped_small::Body::random_with( - dynamic_rng, - &comp::biped_small::Species::Husk, - ), - )) - .with_loot_drop(chosen.read().choose().to_item()) .with_asset_expect("common.entity.dungeon.tier-5.husk") }); }, @@ -1257,11 +1092,10 @@ fn mini_boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec) -> Vec { +fn mini_boss_fallback(tile_wcenter: Vec3) -> Vec { vec![ - EntityInfo::at(tile_wcenter.map(|e| e as f32)).with_body(comp::Body::BirdMedium( - comp::bird_medium::Body::random_with(dynamic_rng, &comp::bird_medium::Species::Goose), - )), + EntityInfo::at(tile_wcenter.map(|e| e as f32)) + .with_asset_expect("common.entity.dungeon.fallback.miniboss"), ] } @@ -1271,18 +1105,18 @@ mod tests { #[test] fn test_creating_bosses() { - let mut dynamic_rng = rand::thread_rng(); let tile_wcenter = Vec3::new(0, 0, 0); - boss_0(&mut dynamic_rng, tile_wcenter); - boss_1(&mut dynamic_rng, tile_wcenter); - boss_2(&mut dynamic_rng, tile_wcenter); - boss_3(&mut dynamic_rng, tile_wcenter); - boss_4(&mut dynamic_rng, tile_wcenter); - boss_5(&mut dynamic_rng, tile_wcenter); - boss_fallback(&mut dynamic_rng, tile_wcenter); + boss_0(tile_wcenter); + boss_1(tile_wcenter); + boss_2(tile_wcenter); + boss_3(tile_wcenter); + boss_4(tile_wcenter); + boss_5(tile_wcenter); + boss_fallback(tile_wcenter); } #[test] + // FIXME: Uses random, test may be not great fn test_creating_enemies() { let mut dynamic_rng = rand::thread_rng(); let raw_entity = EntityInfo::at(Vec3::new(0.0, 0.0, 0.0)); @@ -1292,19 +1126,20 @@ mod tests { enemy_3(&mut dynamic_rng, raw_entity.clone()); enemy_4(&mut dynamic_rng, raw_entity.clone()); enemy_5(&mut dynamic_rng, raw_entity.clone()); - enemy_fallback(&mut dynamic_rng, raw_entity); + enemy_fallback(raw_entity); } #[test] + // FIXME: Uses random, test may be not great fn test_creating_minibosses() { let mut dynamic_rng = rand::thread_rng(); let tile_wcenter = Vec3::new(0, 0, 0); - mini_boss_0(&mut dynamic_rng, tile_wcenter); - mini_boss_1(&mut dynamic_rng, tile_wcenter); - mini_boss_2(&mut dynamic_rng, tile_wcenter); - mini_boss_3(&mut dynamic_rng, tile_wcenter); - mini_boss_4(&mut dynamic_rng, tile_wcenter); + mini_boss_0(tile_wcenter); + mini_boss_1(tile_wcenter); + mini_boss_2(tile_wcenter); + mini_boss_3(tile_wcenter); + mini_boss_4(tile_wcenter); mini_boss_5(&mut dynamic_rng, tile_wcenter); - mini_boss_fallback(&mut dynamic_rng, tile_wcenter); + mini_boss_fallback(tile_wcenter); } } From 005f528e25b294ffe3d30efde5d93f4dbf9243f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Wed, 9 Jun 2021 15:36:54 +0200 Subject: [PATCH 015/180] fix tracy-server compilation via cargo tracy-server --- server/src/lib.rs | 3 ++- server/src/test_world.rs | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/server/src/lib.rs b/server/src/lib.rs index 00c1459335..b7ca95298c 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -300,7 +300,7 @@ impl Server { let map = world.get_map_data(index.as_index_ref(), &state.thread_pool()); #[cfg(not(feature = "worldgen"))] - let (world, index) = World::generate(settings.world_seed, &state.thread_pool()); + let (world, index) = World::generate(settings.world_seed); #[cfg(not(feature = "worldgen"))] let map = WorldMapMsg { dimensions_lg: Vec2::zero(), @@ -310,6 +310,7 @@ impl Server { sea_level: 0.0, alt: Grid::new(Vec2::new(1, 1), 1), sites: Vec::new(), + pois: Vec::new(), }; #[cfg(feature = "worldgen")] diff --git a/server/src/test_world.rs b/server/src/test_world.rs index ecdcd88986..3f0da5f23a 100644 --- a/server/src/test_world.rs +++ b/server/src/test_world.rs @@ -1,5 +1,6 @@ use common::{ generation::{ChunkSupplement, EntityInfo}, + resources::TimeOfDay, terrain::{ Block, BlockKind, MapSizeLg, SpriteKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize, }, @@ -48,6 +49,7 @@ impl World { _index: IndexRef, chunk_pos: Vec2, _should_continue: impl FnMut() -> bool, + _time: Option, ) -> Result<(TerrainChunk, ChunkSupplement), ()> { let (x, y) = chunk_pos.map(|e| e.to_le_bytes()).into_tuple(); let mut rng = SmallRng::from_seed([ From cc566abe5c1c72bff5cc8f68272f9f1ddec57fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Capucho?= Date: Wed, 9 Jun 2021 21:14:53 +0100 Subject: [PATCH 016/180] Update wgpu and check for best swap chain format --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- voxygen/src/render/renderer.rs | 6 +++++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 145b1f4fee..28f9e22c5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6721,7 +6721,7 @@ dependencies = [ [[package]] name = "wgpu" version = "0.8.0" -source = "git+https://github.com/gfx-rs/wgpu-rs.git?rev=7486bdad64bb5d17b709ecccb41e063469efff88#7486bdad64bb5d17b709ecccb41e063469efff88" +source = "git+https://github.com/gfx-rs/wgpu.git?rev=a92b8549a8e2cb9dac781bafc5ed32828f3caf46#a92b8549a8e2cb9dac781bafc5ed32828f3caf46" dependencies = [ "arrayvec", "js-sys", @@ -6741,7 +6741,7 @@ dependencies = [ [[package]] name = "wgpu-core" version = "0.8.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=53eab747a32414232be45d47cae8a43a369395d0#53eab747a32414232be45d47cae8a43a369395d0" +source = "git+https://github.com/gfx-rs/wgpu.git?rev=a92b8549a8e2cb9dac781bafc5ed32828f3caf46#a92b8549a8e2cb9dac781bafc5ed32828f3caf46" dependencies = [ "arrayvec", "bitflags", @@ -6781,7 +6781,7 @@ dependencies = [ [[package]] name = "wgpu-types" version = "0.8.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=53eab747a32414232be45d47cae8a43a369395d0#53eab747a32414232be45d47cae8a43a369395d0" +source = "git+https://github.com/gfx-rs/wgpu.git?rev=a92b8549a8e2cb9dac781bafc5ed32828f3caf46#a92b8549a8e2cb9dac781bafc5ed32828f3caf46" dependencies = [ "bitflags", "serde", diff --git a/Cargo.toml b/Cargo.toml index 88c76c276e..76f9d2fc02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -118,7 +118,7 @@ nativeBuildInputs = ["cmake", "python3", "gnumake"] winit = { git = "https://gitlab.com/veloren/winit.git", branch = "macos-test-spiffed" } vek = { git = "https://gitlab.com/veloren/vek.git", branch = "fix_intrinsics2" } # patch wgpu so we can use wgpu-profiler crate -wgpu = { git = "https://github.com/gfx-rs/wgpu-rs.git", rev = "7486bdad64bb5d17b709ecccb41e063469efff88" } +wgpu = { git = "https://github.com/gfx-rs/wgpu.git", rev = "a92b8549a8e2cb9dac781bafc5ed32828f3caf46" } # # use the latest fixes in naga (remove when updates trickle down to wgpu-rs) # naga = { git = "https://github.com/gfx-rs/naga.git", rev = "3a0f0144112ff621dd7f731bf455adf6cab19164" } diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index 6870c32e42..d5e9f2157d 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -241,9 +241,13 @@ impl Renderer { "selected graphics device" ); + let format = adapter + .get_swap_chain_preferred_format(&surface) + .expect("No supported swap chain format found"); + let sc_desc = wgpu::SwapChainDescriptor { usage: wgpu::TextureUsage::RENDER_ATTACHMENT, - format: wgpu::TextureFormat::Bgra8UnormSrgb, + format, width: dims.width, height: dims.height, present_mode: mode.present_mode.into(), From cba8e157974e1157a5532f2b5b7c5ba39f58bd8c Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Thu, 10 Jun 2021 15:32:21 +0300 Subject: [PATCH 017/180] Split consumables to different tables in trading --- assets/common/item_price_calculation.ron | 7 +++++-- assets/common/loot_tables/consumable/throwable.ron | 3 +-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/assets/common/item_price_calculation.ron b/assets/common/item_price_calculation.ron index a3ca4f3f4a..875321c988 100644 --- a/assets/common/item_price_calculation.ron +++ b/assets/common/item_price_calculation.ron @@ -24,8 +24,11 @@ loot_tables: [ (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 and split them up - (1.0, true, "common.loot_tables.consumable.default"), + // Potions + (0.2, true, "common.loot_tables.consumable.potion"), + // Misc + (0.1, true, "common.loot_tables.consumable.throwable"), + (0.7, true, "common.loot_tables.consumable.misc"), (1.0, false, "common.loot_tables.trading"), ], // this is the amount of that good the most common item represents diff --git a/assets/common/loot_tables/consumable/throwable.ron b/assets/common/loot_tables/consumable/throwable.ron index 3fc2ea2b8d..984662026f 100644 --- a/assets/common/loot_tables/consumable/throwable.ron +++ b/assets/common/loot_tables/consumable/throwable.ron @@ -1,5 +1,4 @@ [ // bombs - (0.6, Item("common.items.utility.bomb")), - (0.2, Item("common.items.utility.bomb_pile")), + (1.0, ItemQuantity("common.items.utility.bomb", 1, 3)), ] From fb1e37154d9a641fe60e3d37b118c2e08caff87d Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 11 Jun 2021 01:31:46 +0300 Subject: [PATCH 018/180] add temporary "middle" skill preset --- assets/server/manifests/presets.ron | 128 +++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 3 deletions(-) diff --git a/assets/server/manifests/presets.ron b/assets/server/manifests/presets.ron index 341049b2b3..615af7a8da 100644 --- a/assets/server/manifests/presets.ron +++ b/assets/server/manifests/presets.ron @@ -128,6 +128,131 @@ (Sceptre(ARange), 2), (Sceptre(ACost), 2), ], + // Just copypasta from max with random reductions + "middle": [ + // General skills + (General(HealthIncrease), 6), + (General(EnergyIncrease), 4), + (Roll(Cost), 2), + (Roll(Strength), 1), + (Roll(Duration), 1), + + // Sword + (UnlockGroup(Weapon(Sword)), 1), + + (Sword(InterruptingAttacks), 1), + + (Sword(TsCombo), 1), + (Sword(TsDamage), 2), + (Sword(TsRegen), 1), + (Sword(TsSpeed), 2), + + (Sword(DCost), 1), + (Sword(DDrain), 2), + (Sword(DDamage), 2), + (Sword(DScaling), 2), + (Sword(DSpeed), 1), + (Sword(DInfinite), 1), + + (Sword(UnlockSpin), 1), + (Sword(SDamage), 1), + (Sword(SSpeed), 2), + (Sword(SCost), 2), + (Sword(SSpins), 2), + // Axe + (UnlockGroup(Weapon(Axe)), 1), + + (Axe(DsCombo), 1), + (Axe(DsDamage), 2), + (Axe(DsRegen), 2), + (Axe(DsSpeed), 1), + + (Axe(SInfinite), 1), + (Axe(SHelicopter), 1), + (Axe(SDamage), 2), + (Axe(SSpeed), 2), + (Axe(SCost), 2), + + (Axe(UnlockLeap), 1), + (Axe(LDamage), 2), + (Axe(LKnockback), 2), + (Axe(LCost), 2), + (Axe(LDistance), 2), + // Hammer + (UnlockGroup(Weapon(Hammer)), 1), + + (Hammer(SsKnockback), 1), + (Hammer(SsDamage), 2), + (Hammer(SsRegen), 2), + (Hammer(SsSpeed), 2), + + (Hammer(CDamage), 2), + (Hammer(CKnockback), 1), + (Hammer(CDrain), 2), + (Hammer(CSpeed), 2), + + (Hammer(UnlockLeap), 1), + (Hammer(LDamage), 2), + (Hammer(LCost), 2), + (Hammer(LDistance), 1), + (Hammer(LKnockback), 2), + (Hammer(LRange), 2), + // Bow + (UnlockGroup(Weapon(Bow)), 1), + + (Bow(ProjSpeed), 2), + + (Bow(CDamage), 2), + (Bow(CKnockback), 2), + (Bow(CSpeed), 1), + (Bow(CRegen), 1), + (Bow(CMove), 2), + + (Bow(RDamage), 3), + (Bow(RCost), 1), + (Bow(RSpeed), 2), + + (Bow(UnlockShotgun), 1), + (Bow(SDamage), 2), + (Bow(SCost), 1), + (Bow(SArrows), 2), + (Bow(SSpread), 2), + + // Staff + (UnlockGroup(Weapon(Staff)), 1), + + (Staff(BDamage), 1), + (Staff(BRegen), 1), + (Staff(BRadius), 2), + + (Staff(FRange), 2), + (Staff(FDamage), 2), + (Staff(FDrain), 1), + (Staff(FVelocity), 2), + + (Staff(UnlockShockwave), 1), + (Staff(SDamage), 1), + (Staff(SKnockback), 2), + (Staff(SRange), 2), + (Staff(SCost), 2), + // Sceptre + (UnlockGroup(Weapon(Sceptre)), 1), + + (Sceptre(LDamage), 2), + (Sceptre(LRange), 2), + (Sceptre(LLifesteal), 2), + (Sceptre(LRegen), 2), + + (Sceptre(HHeal), 2), + (Sceptre(HCost), 2), + (Sceptre(HRange), 2), + + (Sceptre(UnlockAura), 1), + (Sceptre(AStrength), 2), + (Sceptre(ADuration), 1), + (Sceptre(ARange), 2), + (Sceptre(ACost), 2), + ], // Basic skill preset to unlock all abilities "basic": [ // General skills @@ -135,14 +260,11 @@ // Sword (UnlockGroup(Weapon(Sword)), 1), - (Sword(InterruptingAttacks), 1), - (Sword(DInfinite), 1), (Sword(UnlockSpin), 1), // Axe (UnlockGroup(Weapon(Axe)), 1), - (Axe(SInfinite), 1), (Axe(UnlockLeap), 1), // Hammer From 4f49012aca397c1d2b21e2aef3ab5e25fe4877b0 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 11 Jun 2021 01:33:18 +0300 Subject: [PATCH 019/180] add new armour and legendary weapons to /kit --- assets/server/manifests/kits.ron | 364 ++++++++++++++++++++++--------- 1 file changed, 264 insertions(+), 100 deletions(-) diff --git a/assets/server/manifests/kits.ron b/assets/server/manifests/kits.ron index 09c4726a39..61d7ffc46c 100644 --- a/assets/server/manifests/kits.ron +++ b/assets/server/manifests/kits.ron @@ -6,33 +6,271 @@ ("common.items.debug.velorite_bow_debug", 1), ("common.items.debug.admin",1), ], - "admin_cosmetics": [ - ("common.items.debug.cultist_belt",1), - ("common.items.debug.cultist_boots",1), - ("common.items.debug.cultist_chest_blue",1), - ("common.items.debug.cultist_hands_blue",1), - ("common.items.debug.cultist_legs_blue",1), - ("common.items.debug.cultist_shoulder_blue",1), - ("common.items.debug.dungeon_purple",1), - ("common.items.debug.admin_back",1), + "consumables": [ + ("common.items.consumable.potion_minor", 100), + ("common.items.consumable.potion_med", 100), + ("common.items.consumable.potion_big", 100), + ("common.items.food.apple_mushroom_curry", 100), ], - "cultist": [ - ("common.items.armor.cultist.chest",1), - ("common.items.armor.cultist.pants",1), - ("common.items.armor.cultist.hand",1), - ("common.items.armor.cultist.foot",1), - ("common.items.armor.cultist.shoulder",1), - ("common.items.armor.cultist.belt",1), + "endgame": [ + // Biju ("common.items.armor.misc.neck.plain_1",1), ("common.items.armor.misc.ring.skull",1), ("common.items.armor.misc.ring.skull",1), - ("common.items.armor.misc.back.dungeon_purple",1), + + // Cultist weapons ("common.items.weapons.hammer.cultist_purp_2h-0",1), ("common.items.weapons.staff.cultist_staff",1), ("common.items.weapons.sword.cultist",1), ("common.items.weapons.bow.velorite",1), ("common.items.weapons.axe.malachite_axe-0",1), ("common.items.weapons.sceptre.sceptre_velorite_0",1), + + // Legendaries + ("common.items.weapons.hammer.mjolnir",1), + ("common.items.weapons.staff.laevateinn",1), + ("common.items.weapons.staff.phoenix",1), + ("common.items.weapons.sword.caladbolg",1), + ("common.items.weapons.bow.sagitta",1), + ("common.items.weapons.axe.parashu",1), + ("common.items.weapons.sceptre.caduceus",1), + ("common.items.weapons.sceptre.root_evil",1), + + ], + "tier-5": [ + // Hide + ("common.items.armor.hide.dragonscale.back", 1), + ("common.items.armor.hide.dragonscale.belt", 1), + ("common.items.armor.hide.dragonscale.chest", 1), + ("common.items.armor.hide.dragonscale.foot", 1), + ("common.items.armor.hide.dragonscale.hand", 1), + ("common.items.armor.hide.dragonscale.pants", 1), + ("common.items.armor.hide.dragonscale.shoulder", 1), + + // Cloth + ("common.items.armor.cloth.sunsilk.back", 1), + ("common.items.armor.cloth.sunsilk.belt", 1), + ("common.items.armor.cloth.sunsilk.chest", 1), + ("common.items.armor.cloth.sunsilk.foot", 1), + ("common.items.armor.cloth.sunsilk.hand", 1), + ("common.items.armor.cloth.sunsilk.pants", 1), + ("common.items.armor.cloth.sunsilk.shoulder", 1), + + // Mail + ("common.items.armor.mail.orichalcum.back", 1), + ("common.items.armor.mail.orichalcum.belt", 1), + ("common.items.armor.mail.orichalcum.chest", 1), + ("common.items.armor.mail.orichalcum.foot", 1), + ("common.items.armor.mail.orichalcum.hand", 1), + ("common.items.armor.mail.orichalcum.pants", 1), + ("common.items.armor.mail.orichalcum.shoulder", 1), + + // Weapons + ("common.items.weapons.sword.bloodsteel-0", 1), + ("common.items.weapons.axe.bloodsteel_axe-0", 1), + ("common.items.weapons.hammer.runic_hammer", 1), + ("common.items.weapons.bow.eldwood-0", 1), + ("common.items.weapons.staff.ruby_rod", 1), + ("common.items.weapons.sceptre.emerald", 1), + + // Potion Kit + ("common.items.consumable.potion_med", 100), + ], + "tier-4": [ + // Hide + ("common.items.armor.hide.primal.back", 1), + ("common.items.armor.hide.primal.belt", 1), + ("common.items.armor.hide.primal.chest", 1), + ("common.items.armor.hide.primal.foot", 1), + ("common.items.armor.hide.primal.hand", 1), + ("common.items.armor.hide.primal.pants", 1), + ("common.items.armor.hide.primal.shoulder", 1), + + // Cloth + ("common.items.armor.cloth.moonweave.back", 1), + ("common.items.armor.cloth.moonweave.belt", 1), + ("common.items.armor.cloth.moonweave.chest", 1), + ("common.items.armor.cloth.moonweave.foot", 1), + ("common.items.armor.cloth.moonweave.hand", 1), + ("common.items.armor.cloth.moonweave.pants", 1), + ("common.items.armor.cloth.moonweave.shoulder", 1), + + // Mail + ("common.items.armor.mail.bloodsteel.back", 1), + ("common.items.armor.mail.bloodsteel.belt", 1), + ("common.items.armor.mail.bloodsteel.chest", 1), + ("common.items.armor.mail.bloodsteel.foot", 1), + ("common.items.armor.mail.bloodsteel.hand", 1), + ("common.items.armor.mail.bloodsteel.pants", 1), + ("common.items.armor.mail.bloodsteel.shoulder", 1), + + // Weapons + ("common.items.weapons.sword.cobalt-0", 1), + ("common.items.weapons.axe.cobalt_axe-0", 1), + ("common.items.weapons.hammer.cobalt_hammer-0", 1), + ("common.items.weapons.bow.frostwood-0", 1), + ("common.items.weapons.staff.frostwood_torch", 1), + ("common.items.weapons.sceptre.fork0", 1), + + // Potion Kit + ("common.items.consumable.potion_med", 100), + ], + "tier-3": [ + // Hide + ("common.items.armor.hide.carapace.back", 1), + ("common.items.armor.hide.carapace.belt", 1), + ("common.items.armor.hide.carapace.chest", 1), + ("common.items.armor.hide.carapace.foot", 1), + ("common.items.armor.hide.carapace.hand", 1), + ("common.items.armor.hide.carapace.pants", 1), + ("common.items.armor.hide.carapace.shoulder", 1), + + // Cloth + ("common.items.armor.cloth.druid.back", 1), + ("common.items.armor.cloth.druid.belt", 1), + ("common.items.armor.cloth.druid.chest", 1), + ("common.items.armor.cloth.druid.foot", 1), + ("common.items.armor.cloth.druid.hand", 1), + ("common.items.armor.cloth.druid.pants", 1), + ("common.items.armor.cloth.druid.shoulder", 1), + + // Mail + ("common.items.armor.mail.cobalt.back", 1), + ("common.items.armor.mail.cobalt.belt", 1), + ("common.items.armor.mail.cobalt.chest", 1), + ("common.items.armor.mail.cobalt.foot", 1), + ("common.items.armor.mail.cobalt.hand", 1), + ("common.items.armor.mail.cobalt.pants", 1), + ("common.items.armor.mail.cobalt.shoulder", 1), + + // Weapons + ("common.items.weapons.sword.steel-0", 1), + ("common.items.weapons.axe.steel_axe-0", 1), + ("common.items.weapons.hammer.steel_hammer-0", 1), + ("common.items.weapons.bow.metal-0", 1), + ("common.items.weapons.staff.flamethrower_0", 1), + ("common.items.weapons.sceptre.coralline_cane", 1), + + // Potion Kit + ("common.items.consumable.potion_med", 100), + ], + "tier-2": [ + // Hide + ("common.items.armor.hide.scale.back", 1), + ("common.items.armor.hide.scale.belt", 1), + ("common.items.armor.hide.scale.chest", 1), + ("common.items.armor.hide.scale.foot", 1), + ("common.items.armor.hide.scale.hand", 1), + ("common.items.armor.hide.scale.pants", 1), + ("common.items.armor.hide.scale.shoulder", 1), + + // Cloth + ("common.items.armor.cloth.silken.back", 1), + ("common.items.armor.cloth.silken.belt", 1), + ("common.items.armor.cloth.silken.chest", 1), + ("common.items.armor.cloth.silken.foot", 1), + ("common.items.armor.cloth.silken.hand", 1), + ("common.items.armor.cloth.silken.pants", 1), + ("common.items.armor.cloth.silken.shoulder", 1), + + // Mail + ("common.items.armor.mail.steel.back", 1), + ("common.items.armor.mail.steel.belt", 1), + ("common.items.armor.mail.steel.chest", 1), + ("common.items.armor.mail.steel.foot", 1), + ("common.items.armor.mail.steel.hand", 1), + ("common.items.armor.mail.steel.pants", 1), + ("common.items.armor.mail.steel.shoulder", 1), + + // Weapons + ("common.items.weapons.sword.iron-0", 1), + ("common.items.weapons.axe.iron_axe-0", 1), + ("common.items.weapons.hammer.iron_hammer-0", 1), + ("common.items.weapons.bow.hardwood-0", 1), + ("common.items.weapons.staff.heated_arm", 1), + ("common.items.weapons.sceptre.druids_arbor", 1), + + // Potion Kit + ("common.items.consumable.potion_med", 100), + ], + "tier-1": [ + // Hide + ("common.items.armor.hide.leather.back", 1), + ("common.items.armor.hide.leather.belt", 1), + ("common.items.armor.hide.leather.chest", 1), + ("common.items.armor.hide.leather.foot", 1), + ("common.items.armor.hide.leather.hand", 1), + ("common.items.armor.hide.leather.pants", 1), + ("common.items.armor.hide.leather.shoulder", 1), + + // Cloth + ("common.items.armor.cloth.woolen.back", 1), + ("common.items.armor.cloth.woolen.belt", 1), + ("common.items.armor.cloth.woolen.chest", 1), + ("common.items.armor.cloth.woolen.foot", 1), + ("common.items.armor.cloth.woolen.hand", 1), + ("common.items.armor.cloth.woolen.pants", 1), + ("common.items.armor.cloth.woolen.shoulder", 1), + + // Mail + ("common.items.armor.mail.iron.back", 1), + ("common.items.armor.mail.iron.belt", 1), + ("common.items.armor.mail.iron.chest", 1), + ("common.items.armor.mail.iron.foot", 1), + ("common.items.armor.mail.iron.hand", 1), + ("common.items.armor.mail.iron.pants", 1), + ("common.items.armor.mail.iron.shoulder", 1), + + // Weapons + ("common.items.weapons.sword.bronze-0", 1), + ("common.items.weapons.axe.bronze_axe-0", 1), + ("common.items.weapons.hammer.bronze_hammer-0", 1), + ("common.items.weapons.bow.bone-0", 1), + ("common.items.weapons.staff.bone_staff", 1), + ("common.items.weapons.sceptre.divine_gohei", 1), + + // Potion Kit + ("common.items.consumable.potion_minor", 100), + ], + "tier-0": [ + // Hide + ("common.items.armor.hide.rawhide.back", 1), + ("common.items.armor.hide.rawhide.belt", 1), + ("common.items.armor.hide.rawhide.chest", 1), + ("common.items.armor.hide.rawhide.foot", 1), + ("common.items.armor.hide.rawhide.hand", 1), + ("common.items.armor.hide.rawhide.pants", 1), + ("common.items.armor.hide.rawhide.shoulder", 1), + + // Cloth + ("common.items.armor.cloth.linen.back", 1), + ("common.items.armor.cloth.linen.belt", 1), + ("common.items.armor.cloth.linen.chest", 1), + ("common.items.armor.cloth.linen.foot", 1), + ("common.items.armor.cloth.linen.hand", 1), + ("common.items.armor.cloth.linen.pants", 1), + ("common.items.armor.cloth.linen.shoulder", 1), + + // Mail + ("common.items.armor.mail.bronze.back", 1), + ("common.items.armor.mail.bronze.belt", 1), + ("common.items.armor.mail.bronze.chest", 1), + ("common.items.armor.mail.bronze.foot", 1), + ("common.items.armor.mail.bronze.hand", 1), + ("common.items.armor.mail.bronze.pants", 1), + ("common.items.armor.mail.bronze.shoulder", 1), + + // weapons + ("common.items.weapons.sword.stone-0", 1), + ("common.items.weapons.axe_1h.stone-0", 1), + ("common.items.weapons.axe_1h.stone-1", 1), + ("common.items.weapons.hammer.stone_hammer-0", 1), + ("common.items.weapons.bow.wood-0", 1), + ("common.items.weapons.staff.bent_fuse", 1), + ("common.items.weapons.sceptre.divine_gohei", 1), + + // potion kit + ("common.items.consumable.potion_minor", 100), ], "ferocious": [ ("common.items.armor.ferocious.chest",1), @@ -44,88 +282,14 @@ ("common.items.armor.ferocious.back",1), ("common.items.weapons.sword.bloodsteel-1",1), ], - "consumables": [ - ("common.items.consumable.potion_minor", 100), - ("common.items.consumable.potion_med", 100), - ("common.items.consumable.potion_big", 100), - ("common.items.food.apple_mushroom_curry", 100), - ], - "tier-4": [ - ("common.items.armor.mail.steel.belt", 1), - ("common.items.armor.mail.steel.chest", 1), - ("common.items.armor.mail.steel.foot", 1), - ("common.items.armor.mail.steel.hand", 1), - ("common.items.armor.mail.steel.pants", 1), - ("common.items.armor.mail.steel.shoulder", 1), - ("common.items.weapons.sword.cobalt-0", 1), - ("common.items.weapons.axe.cobalt_axe-0", 1), - ("common.items.weapons.hammer.cobalt_hammer-0", 1), - ("common.items.weapons.bow.frostwood-0", 1), - ("common.items.weapons.staff.frostwood_torch", 1), - ("common.items.weapons.sceptre.fork0", 1), - ("common.items.consumable.potion_med", 100), - ], - "tier-3": [ - ("common.items.armor.plate.belt", 1), - ("common.items.armor.plate.chest", 1), - ("common.items.armor.plate.foot", 1), - ("common.items.armor.plate.hand", 1), - ("common.items.armor.plate.pants", 1), - ("common.items.armor.plate.shoulder", 1), - ("common.items.weapons.sword.steel-0", 1), - ("common.items.weapons.axe.steel_axe-0", 1), - ("common.items.weapons.hammer.steel_hammer-0", 1), - ("common.items.weapons.bow.metal-0", 1), - ("common.items.weapons.staff.flamethrower_0", 1), - ("common.items.weapons.sceptre.coralline_cane", 1), - ("common.items.consumable.potion_med", 100), - ], - "tier-2": [ - ("common.items.armor.twigs.belt", 1), - ("common.items.armor.twigs.chest", 1), - ("common.items.armor.twigs.foot", 1), - ("common.items.armor.twigs.hand", 1), - ("common.items.armor.twigs.pants", 1), - ("common.items.armor.twigs.shoulder", 1), - ("common.items.weapons.sword.iron-0", 1), - ("common.items.weapons.axe.iron_axe-0", 1), - ("common.items.weapons.hammer.iron_hammer-0", 1), - ("common.items.weapons.bow.hardwood-0", 1), - ("common.items.weapons.staff.heated_arm", 1), - ("common.items.weapons.sceptre.druids_arbor", 1), - ("common.items.consumable.potion_med", 100), - ], - "tier-1": [ - ("common.items.armor.agile.belt", 1), - ("common.items.armor.agile.chest", 1), - ("common.items.armor.agile.foot", 1), - ("common.items.armor.agile.hand", 1), - ("common.items.armor.agile.pants", 1), - ("common.items.armor.agile.shoulder", 1), - ("common.items.weapons.sword.bronze-0", 1), - ("common.items.weapons.axe.bronze_axe-0", 1), - ("common.items.weapons.hammer.bronze_hammer-0", 1), - ("common.items.weapons.bow.bone-0", 1), - ("common.items.weapons.staff.bone_staff", 1), - ("common.items.weapons.sceptre.divine_gohei", 1), - ("common.items.consumable.potion_minor", 100), - ], - "tier-0": [ - ("common.items.armor.cloth_purple.belt", 1), - ("common.items.armor.cloth_purple.chest", 1), - ("common.items.armor.cloth_purple.foot", 1), - ("common.items.armor.cloth_purple.hand", 1), - ("common.items.armor.cloth_purple.pants", 1), - ("common.items.armor.cloth_purple.shoulder", 1), - - ("common.items.weapons.sword.stone-0", 1), - ("common.items.weapons.axe_1h.stone-0", 1), - ("common.items.weapons.axe_1h.stone-1", 1), - ("common.items.weapons.hammer.stone_hammer-0", 1), - ("common.items.weapons.bow.wood-0", 1), - ("common.items.weapons.staff.bent_fuse", 1), - ("common.items.weapons.sceptre.divine_gohei", 1), - - ("common.items.consumable.potion_minor", 100), + "admin_cosmetics": [ + ("common.items.debug.cultist_belt",1), + ("common.items.debug.cultist_boots",1), + ("common.items.debug.cultist_chest_blue",1), + ("common.items.debug.cultist_hands_blue",1), + ("common.items.debug.cultist_legs_blue",1), + ("common.items.debug.cultist_shoulder_blue",1), + ("common.items.debug.dungeon_purple",1), + ("common.items.debug.admin_back",1), ], }) From 5084548b53a5699022e1962fa3f1bb853ce2336f Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 11 Jun 2021 01:33:47 +0300 Subject: [PATCH 020/180] Add warnings about non-existent items in /kit --- server/src/cmd.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/src/cmd.rs b/server/src/cmd.rs index a8f747e5b5..926b439588 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -1615,6 +1615,8 @@ fn handle_kit( comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Debug), ), ); + } else { + warn!("Unknown item: {}", &item_id); } } Ok(()) From a5e4a829eb9fee865593988bff2179ea3b9adaa4 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 11 Jun 2021 01:34:33 +0300 Subject: [PATCH 021/180] Balanse fixes - Make bow charge time even smaller and reduce damage/enery reward a little - Buff staff firebomb - Nerf spear dash - Longer recover time of Tidal shockwaves and less knockback - Nerf Tidal bubbles --- assets/common/abilities/bow/charged.ron | 8 ++++---- .../common/abilities/custom/tidalwarrior/bubbles.ron | 6 +++--- .../abilities/custom/tidalwarrior/totem_wave.ron | 6 +++--- assets/common/abilities/spear/dash.ron | 10 +++++----- assets/common/abilities/staff/firebomb.ron | 4 ++-- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/assets/common/abilities/bow/charged.ron b/assets/common/abilities/bow/charged.ron index dc945d1b0a..b52942e3c9 100644 --- a/assets/common/abilities/bow/charged.ron +++ b/assets/common/abilities/bow/charged.ron @@ -2,14 +2,14 @@ ChargedRanged( energy_cost: 0, energy_drain: 0, initial_regen: 20, - scaled_regen: 120, - initial_damage: 20, - scaled_damage: 100, + scaled_regen: 100, + initial_damage: 10, + scaled_damage: 90, initial_knockback: 0.0, scaled_knockback: 10.0, speed: 1.0, buildup_duration: 0.2, - charge_duration: 1.2, + charge_duration: 0.5, recover_duration: 0.3, projectile_body: Object(Arrow), projectile_light: None, diff --git a/assets/common/abilities/custom/tidalwarrior/bubbles.ron b/assets/common/abilities/custom/tidalwarrior/bubbles.ron index e53f23bd9c..65cbadc67c 100644 --- a/assets/common/abilities/custom/tidalwarrior/bubbles.ron +++ b/assets/common/abilities/custom/tidalwarrior/bubbles.ron @@ -2,14 +2,14 @@ BasicBeam( buildup_duration: 0.5, recover_duration: 0.5, beam_duration: 2.5, - damage: 100, + damage: 50, tick_rate: 2.0, range: 25.0, max_angle: 15.0, damage_effect: Some(Buff(( kind: Wet, - dur_secs: 15.0, - strength: Value(4.5), + dur_secs: 5.0, + strength: Value(2.5), chance: 1.0, ))), energy_regen: 0, diff --git a/assets/common/abilities/custom/tidalwarrior/totem_wave.ron b/assets/common/abilities/custom/tidalwarrior/totem_wave.ron index 93f076461b..0bc8176d3c 100644 --- a/assets/common/abilities/custom/tidalwarrior/totem_wave.ron +++ b/assets/common/abilities/custom/tidalwarrior/totem_wave.ron @@ -1,11 +1,11 @@ Shockwave( energy_cost: 0, - buildup_duration: 1.4, + buildup_duration: 0.4, swing_duration: 0.1, - recover_duration: 0.5, + recover_duration: 3.5, damage: 10, poise_damage: 0, - knockback: ( strength: 100.0, direction: Up), + knockback: ( strength: 25.0, direction: Away), shockwave_angle: 360.0, shockwave_vertical_angle: 30.0, shockwave_speed: 10.0, diff --git a/assets/common/abilities/spear/dash.ron b/assets/common/abilities/spear/dash.ron index f72771b1e7..6dc288fd3e 100644 --- a/assets/common/abilities/spear/dash.ron +++ b/assets/common/abilities/spear/dash.ron @@ -1,9 +1,9 @@ DashMelee( energy_cost: 100, - base_damage: 60, - scaled_damage: 100, - base_poise_damage: 45, - scaled_poise_damage: 0, + base_damage: 10, + scaled_damage: 80, + base_poise_damage: 0, + scaled_poise_damage: 25, base_knockback: 8.0, scaled_knockback: 7.0, range: 3.0, @@ -13,7 +13,7 @@ DashMelee( buildup_duration: 0.5, charge_duration: 1.0, swing_duration: 0.1, - recover_duration: 0.5, + recover_duration: 0.8, charge_through: true, is_interruptible: true, damage_kind: Piercing, diff --git a/assets/common/abilities/staff/firebomb.ron b/assets/common/abilities/staff/firebomb.ron index c803a6a0c1..926755af35 100644 --- a/assets/common/abilities/staff/firebomb.ron +++ b/assets/common/abilities/staff/firebomb.ron @@ -3,9 +3,9 @@ BasicRanged( buildup_duration: 0.5, recover_duration: 0.4, projectile: Fireball( - damage: 90.0, + damage: 120.0, radius: 4.0, - energy_regen: 50, + energy_regen: 80, ), projectile_body: Object(BoltFire), projectile_speed: 60.0, From edc4af80239cd944d53927ffa28e205aa9c58b09 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 11 Jun 2021 01:45:02 +0300 Subject: [PATCH 022/180] fix Wet debuff image --- voxygen/src/hud/img_ids.rs | 1 + voxygen/src/hud/mod.rs | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index f4ab8b2ec1..f96bd8b610 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -588,6 +588,7 @@ image_ids! { debuff_burning_0: "voxygen.element.de_buffs.debuff_burning_0", debuff_crippled_0: "voxygen.element.de_buffs.debuff_cripple_0", debuff_frozen_0: "voxygen.element.de_buffs.debuff_frozen_0", + debuff_wet_0: "voxygen.element.de_buffs.debuff_wet_0", // Animation Frames // Buff Frame diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 246511ac64..585361849d 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -3770,8 +3770,7 @@ pub fn get_buff_image(buff: BuffKind, imgs: &Imgs) -> conrod_core::image::Id { BuffKind::Burning { .. } => imgs.debuff_burning_0, BuffKind::Crippled { .. } => imgs.debuff_crippled_0, BuffKind::Frozen { .. } => imgs.debuff_frozen_0, - // TODO: Get icon for this before merging. Anyone doing code review open a comment here. - BuffKind::Wet { .. } => imgs.debuff_burning_0, + BuffKind::Wet { .. } => imgs.debuff_wet_0, } } From 2e5f868f31c9dd215c107263c368ed01729b3822 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 11 Jun 2021 01:46:32 +0300 Subject: [PATCH 023/180] Make bow zoom less intense --- voxygen/src/session/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 32c584af77..d189219479 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -311,8 +311,11 @@ impl PlayState for SessionState { .read_storage::() .get(player_entity) { - if cr.charge_frac() > 0.25 { - fov_scaling -= 3.0 * cr.charge_frac() / 4.0; + let crit_frac = 0.5; + if cr.charge_frac() > crit_frac { + let r1 = 3.0; + let r2 = 5.0; + fov_scaling -= r1 * cr.charge_frac() / r2; } let max_dist = if let Some(dist) = self.saved_zoom_dist { dist From 358f9f268d078d746817c049868133b3a5c37c81 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 11 Jun 2021 07:09:58 +0100 Subject: [PATCH 024/180] Prevent creatures spawning in the ground --- world/src/layer/mod.rs | 116 +++++++++++++++++++----------------- world/src/layer/wildlife.rs | 52 ++++++++-------- 2 files changed, 88 insertions(+), 80 deletions(-) diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index c44d21492e..9c12b4c130 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -439,62 +439,70 @@ pub fn apply_caves_supplement<'a>( let cave_depth = (col_sample.alt - cave.alt).max(0.0); //slightly different from earlier cave depth? // Scatter things in caves - if RandomField::new(index.seed).chance(wpos2d.into(), 0.0018) - && cave_base < surface_z as i32 - 40 - { - let is_hostile: bool; - let entity = EntityInfo::at(Vec3::new( - wpos2d.x as f32, - wpos2d.y as f32, - cave_base as f32, - )) - .with_body(if cave_depth < 70.0 { - is_hostile = false; - let species = match dynamic_rng.gen_range(0..4) { - 0 => comp::quadruped_small::Species::Truffler, - 1 => comp::quadruped_small::Species::Dodarock, - 2 => comp::quadruped_small::Species::Holladon, - _ => comp::quadruped_small::Species::Batfox, - }; - comp::quadruped_small::Body::random_with(dynamic_rng, &species).into() - } else if cave_depth < 120.0 { - is_hostile = true; - let species = match dynamic_rng.gen_range(0..3) { - 0 => comp::quadruped_low::Species::Rocksnapper, - 1 => comp::quadruped_low::Species::Salamander, - _ => comp::quadruped_low::Species::Asp, - }; - comp::quadruped_low::Body::random_with(dynamic_rng, &species).into() - } else if cave_depth < 200.0 { - is_hostile = true; - let species = match dynamic_rng.gen_range(0..3) { - 0 => comp::quadruped_low::Species::Rocksnapper, - 1 => comp::quadruped_low::Species::Lavadrake, - _ => comp::quadruped_low::Species::Basilisk, - }; - comp::quadruped_low::Body::random_with(dynamic_rng, &species).into() - } else { - is_hostile = true; - let species = match dynamic_rng.gen_range(0..5) { - 0 => comp::biped_large::Species::Ogre, - 1 => comp::biped_large::Species::Cyclops, - 2 => comp::biped_large::Species::Wendigo, - 3 => match dynamic_rng.gen_range(0..2) { - 0 => comp::biped_large::Species::Blueoni, - _ => comp::biped_large::Species::Redoni, - }, - _ => comp::biped_large::Species::Troll, - }; - comp::biped_large::Body::random_with(dynamic_rng, &species).into() + if let Some(z) = (-4..8).map(|z| cave_base + z).find(|z| { + (0..2).all(|z_offs| { + vol.get(offs.with_z(z + z_offs)) + .map_or(true, |b| b.is_fluid()) }) - .with_alignment(if is_hostile { - comp::Alignment::Enemy - } else { - comp::Alignment::Wild - }) - .with_automatic_name(); + }) { + if RandomField::new(index.seed).chance(wpos2d.into(), 0.0018) + && cave_base < surface_z as i32 - 40 + { + let is_hostile: bool; + let entity = + EntityInfo::at(Vec3::new(wpos2d.x as f32, wpos2d.y as f32, z as f32)) + .with_body(if cave_depth < 70.0 { + is_hostile = false; + let species = match dynamic_rng.gen_range(0..4) { + 0 => comp::quadruped_small::Species::Truffler, + 1 => comp::quadruped_small::Species::Dodarock, + 2 => comp::quadruped_small::Species::Holladon, + _ => comp::quadruped_small::Species::Batfox, + }; + comp::quadruped_small::Body::random_with(dynamic_rng, &species) + .into() + } else if cave_depth < 120.0 { + is_hostile = true; + let species = match dynamic_rng.gen_range(0..3) { + 0 => comp::quadruped_low::Species::Rocksnapper, + 1 => comp::quadruped_low::Species::Salamander, + _ => comp::quadruped_low::Species::Asp, + }; + comp::quadruped_low::Body::random_with(dynamic_rng, &species) + .into() + } else if cave_depth < 200.0 { + is_hostile = true; + let species = match dynamic_rng.gen_range(0..3) { + 0 => comp::quadruped_low::Species::Rocksnapper, + 1 => comp::quadruped_low::Species::Lavadrake, + _ => comp::quadruped_low::Species::Basilisk, + }; + comp::quadruped_low::Body::random_with(dynamic_rng, &species) + .into() + } else { + is_hostile = true; + let species = match dynamic_rng.gen_range(0..5) { + 0 => comp::biped_large::Species::Ogre, + 1 => comp::biped_large::Species::Cyclops, + 2 => comp::biped_large::Species::Wendigo, + 3 => match dynamic_rng.gen_range(0..2) { + 0 => comp::biped_large::Species::Blueoni, + _ => comp::biped_large::Species::Redoni, + }, + _ => comp::biped_large::Species::Troll, + }; + comp::biped_large::Body::random_with(dynamic_rng, &species) + .into() + }) + .with_alignment(if is_hostile { + comp::Alignment::Enemy + } else { + comp::Alignment::Wild + }) + .with_automatic_name(); - supplement.add_entity(entity); + supplement.add_entity(entity); + } } } } diff --git a/world/src/layer/wildlife.rs b/world/src/layer/wildlife.rs index 8e2c007608..f15ec3a9bc 100644 --- a/world/src/layer/wildlife.rs +++ b/world/src/layer/wildlife.rs @@ -1063,37 +1063,37 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }, ); + let alt = col_sample.alt as i32; + if let Some((make_entity, group_size)) = entity_group { - let alt = col_sample.alt as i32; - // Find the intersection between ground and air, if there is one near the - // surface - if let Some(solid_end) = (-4..8) - .find(|z| { - vol.get(Vec3::new(offs.x, offs.y, alt + z)) - .map(|b| b.is_solid()) - .unwrap_or(false) - }) - .and_then(|solid_start| { - (1..8).map(|z| solid_start + z).find(|z| { - vol.get(Vec3::new(offs.x, offs.y, alt + z)) + let group_size = dynamic_rng.gen_range(group_size.start..group_size.end); + let entity = make_entity( + (wpos2d.map(|e| e as f32) + 0.5).with_z(alt as f32), + dynamic_rng, + ); + for e in 0..group_size { + // Choose a nearby position + let offs_wpos2d = (Vec2::new( + (e as f32 / group_size as f32 * 2.0 * f32::consts::PI).sin(), + (e as f32 / group_size as f32 * 2.0 * f32::consts::PI).cos(), + ) * (5.0 + dynamic_rng.gen::().powf(0.5) * 5.0)) + .map(|e| e as i32); + // Clamp position to chunk + let offs_wpos2d = (offs + offs_wpos2d) + .clamped(Vec2::zero(), vol.size_xy().map(|e| e as i32) - 1) + - offs; + + // Find the intersection between ground and air, if there is one near the + // surface + if let Some(solid_end) = (-8..8).find(|z| { + (0..2).all(|z2| { + vol.get(Vec3::new(offs.x, offs.y, alt) + offs_wpos2d.with_z(z + z2)) .map(|b| !b.is_solid()) .unwrap_or(true) }) - }) - { - let group_size = dynamic_rng.gen_range(group_size.start..group_size.end); - let entity = make_entity( - Vec3::new(wpos2d.x, wpos2d.y, alt + solid_end).map(|e| e as f32), - dynamic_rng, - ); - for e in 0..group_size { + }) { let mut entity = entity.clone(); - entity.pos = entity.pos.map(|e| e + dynamic_rng.gen::()) - + Vec3::new( - (e as f32 / group_size as f32 * 2.0 * f32::consts::PI).sin(), - (e as f32 / group_size as f32 * 2.0 * f32::consts::PI).cos(), - 0.0, - ) * (5.0 + dynamic_rng.gen::().powf(0.5) * 5.0); + entity.pos += offs_wpos2d.with_z(solid_end).map(|e| e as f32); supplement.add_entity(entity.with_automatic_name()); } } From 1b1287f407c4f9f0da5c7a9d31550c1c2edc5d93 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 11 Jun 2021 08:33:32 +0100 Subject: [PATCH 025/180] Repaired common such that it works for plugin targets --- common/Cargo.toml | 14 ++++++++------ common/src/combat.rs | 8 +++++++- common/src/comp/buff.rs | 12 +----------- common/src/comp/fluid_dynamics.rs | 8 ++++---- common/src/comp/mod.rs | 2 ++ common/src/lib.rs | 7 +++---- common/src/resources.rs | 5 +++++ 7 files changed, 30 insertions(+), 26 deletions(-) diff --git a/common/Cargo.toml b/common/Cargo.toml index 7a58d607c5..a34fce597f 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -21,13 +21,20 @@ common-base = { package = "veloren-common-base", path = "base" } # Serde serde = { version = "1.0.110", features = ["derive", "rc"] } +# Util +enum-iterator = "0.6" +vek = { version = "=0.14.1", features = ["serde"] } + +# Strum +strum = "0.20" +strum_macros = "0.20" + [target.'cfg(not(target_arch = "wasm32"))'.dependencies] approx = "0.4.0" clap = "2.33" crossbeam-utils = "0.8.1" bitflags = "1.2" crossbeam-channel = "0.5" -enum-iterator = "0.6" lazy_static = "1.4.0" num-derive = "0.3" num-traits = "0.2" @@ -36,7 +43,6 @@ rayon = "1.5" roots = "0.0.6" spin_sleep = "1.0" tracing = { version = "0.1", default-features = false } -vek = { version = "=0.14.1", features = ["serde"] } uuid = { version = "0.8.1", default-features = false, features = ["serde", "v4"] } rand = "0.8" @@ -61,10 +67,6 @@ slotmap = { version = "1.0", features = ["serde"] } indexmap = "1.3.0" slab = "0.4.2" -# Strum -strum = "0.20" -strum_macros = "0.20" - # ECS specs = { git = "https://github.com/amethyst/specs.git", features = ["serde", "storage-event-control", "nightly"], rev = "f985bec5d456f7b0dd8aae99848f9473c2cd9d46" } specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", rev = "8be2abcddf8f524cb5876e8dd20a7e47cfaf7573" } diff --git a/common/src/combat.rs b/common/src/combat.rs index 9e6a5d4524..66e2116411 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -1,7 +1,7 @@ +use crate::comp::buff::{Buff, BuffChange, BuffData, BuffKind, BuffSource}; #[cfg(not(target_arch = "wasm32"))] use crate::{ comp::{ - buff::{Buff, BuffChange, BuffData, BuffKind, BuffSource}, inventory::{ item::{ armor::Protection, @@ -814,6 +814,7 @@ pub fn get_weapons(inv: &Inventory) -> (Option, Option) { ) } +#[cfg(not(target_arch = "wasm32"))] pub fn weapon_rating(item: &T, msm: &MaterialStatManifest) -> f32 { const DAMAGE_WEIGHT: f32 = 2.0; const POISE_WEIGHT: f32 = 1.0; @@ -834,6 +835,7 @@ pub fn weapon_rating(item: &T, msm: &MaterialStatManifest) -> f32 { } } +#[cfg(not(target_arch = "wasm32"))] fn weapon_skills(inventory: &Inventory, skill_set: &SkillSet) -> f32 { let (mainhand, offhand) = get_weapons(inventory); let mainhand_skills = if let Some(tool) = mainhand { @@ -849,6 +851,7 @@ fn weapon_skills(inventory: &Inventory, skill_set: &SkillSet) -> f32 { mainhand_skills.max(offhand_skills) } +#[cfg(not(target_arch = "wasm32"))] fn get_weapon_rating(inventory: &Inventory, msm: &MaterialStatManifest) -> f32 { let mainhand_rating = if let Some((item, _)) = equipped_item_and_tool(inventory, EquipSlot::ActiveMainhand) { @@ -901,6 +904,7 @@ pub fn combat_rating( combined_rating * body.combat_multiplier() } +#[cfg(not(target_arch = "wasm32"))] pub fn compute_crit_mult(inventory: Option<&Inventory>) -> f32 { // Starts with a value of 1.25 when summing the stats from each armor piece, and // defaults to a value of 1.25 if no inventory is equipped @@ -918,6 +922,7 @@ pub fn compute_crit_mult(inventory: Option<&Inventory>) -> f32 { } /// Computes the energy reward modifer from worn armor +#[cfg(not(target_arch = "wasm32"))] pub fn compute_energy_reward_mod(inventory: Option<&Inventory>) -> f32 { // Starts with a value of 1.0 when summing the stats from each armor piece, and // defaults to a value of 1.0 if no inventory is present @@ -936,6 +941,7 @@ pub fn compute_energy_reward_mod(inventory: Option<&Inventory>) -> f32 { /// Computes the modifier that should be applied to max energy from the /// currently equipped items +#[cfg(not(target_arch = "wasm32"))] pub fn compute_max_energy_mod(energy: &Energy, inventory: Option<&Inventory>) -> f32 { // Defaults to a value of 0 if no inventory is present let energy_increase = inventory.map_or(0, |inv| { diff --git a/common/src/comp/buff.rs b/common/src/comp/buff.rs index f76965898d..412ab0affa 100644 --- a/common/src/comp/buff.rs +++ b/common/src/comp/buff.rs @@ -1,5 +1,5 @@ -#[cfg(not(target_arch = "wasm32"))] use crate::uid::Uid; +use core::{cmp::Ordering, time::Duration}; #[cfg(not(target_arch = "wasm32"))] use hashbrown::HashMap; use serde::{Deserialize, Serialize}; @@ -7,8 +7,6 @@ use serde::{Deserialize, Serialize}; use specs::{Component, DerefFlaggedStorage}; #[cfg(not(target_arch = "wasm32"))] use specs_idvs::IdvStorage; -#[cfg(not(target_arch = "wasm32"))] -use std::{cmp::Ordering, time::Duration}; use strum_macros::EnumIter; /// De/buff Kind. @@ -101,7 +99,6 @@ impl BuffKind { } // Struct used to store data relevant to a buff -#[cfg(not(target_arch = "wasm32"))] #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] pub struct BuffData { pub strength: f32, @@ -116,7 +113,6 @@ impl BuffData { /// De/buff category ID. /// Similar to `BuffKind`, but to mark a category (for more generic usage, like /// positive/negative buffs). -#[cfg(not(target_arch = "wasm32"))] #[derive(Clone, Copy, Eq, PartialEq, Debug, Serialize, Deserialize)] pub enum BuffCategory { Natural, @@ -127,7 +123,6 @@ pub enum BuffCategory { FromAura(bool), // bool used to check if buff recently set by aura } -#[cfg(not(target_arch = "wasm32"))] #[derive(Clone, Debug, Serialize, Deserialize)] pub enum ModifierKind { Additive, @@ -135,7 +130,6 @@ pub enum ModifierKind { } /// Data indicating and configuring behaviour of a de/buff. -#[cfg(not(target_arch = "wasm32"))] #[derive(Clone, Debug, Serialize, Deserialize)] pub enum BuffEffect { /// Periodically damages or heals entity @@ -175,7 +169,6 @@ pub enum BuffEffect { /// /// To provide more classification info when needed, /// buff can be in one or more buff category. -#[cfg(not(target_arch = "wasm32"))] #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Buff { pub kind: BuffKind, @@ -188,7 +181,6 @@ pub struct Buff { /// Information about whether buff addition or removal was requested. /// This to implement "on_add" and "on_remove" hooks for constant buffs. -#[cfg(not(target_arch = "wasm32"))] #[derive(Clone, Debug)] pub enum BuffChange { /// Adds this buff. @@ -371,7 +363,6 @@ impl PartialEq for Buff { } /// Source of the de/buff -#[cfg(not(target_arch = "wasm32"))] #[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)] pub enum BuffSource { /// Applied by a character @@ -485,7 +476,6 @@ impl Buffs { } } -#[cfg(not(target_arch = "wasm32"))] pub type BuffId = u64; #[cfg(not(target_arch = "wasm32"))] diff --git a/common/src/comp/fluid_dynamics.rs b/common/src/comp/fluid_dynamics.rs index 4ec07d51aa..6529efc82c 100644 --- a/common/src/comp/fluid_dynamics.rs +++ b/common/src/comp/fluid_dynamics.rs @@ -1,7 +1,6 @@ -use super::{ - body::{object, Body}, - Density, Ori, Vel, -}; +#[cfg(not(target_arch = "wasm32"))] +use super::body::{object, Body}; +use super::{Density, Ori, Vel}; use crate::{ consts::{AIR_DENSITY, WATER_DENSITY}, util::{Dir, Plane, Projection}, @@ -93,6 +92,7 @@ pub struct Wings { pub ori: Ori, } +#[cfg(not(target_arch = "wasm32"))] impl Body { pub fn aerodynamic_forces( &self, diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index f120798be7..8b6b775349 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -12,8 +12,10 @@ mod character_state; pub mod compass; #[cfg(not(target_arch = "wasm32"))] mod controller; +#[cfg(not(target_arch = "wasm32"))] pub mod dialogue; #[cfg(not(target_arch = "wasm32"))] mod energy; +#[cfg(not(target_arch = "wasm32"))] pub mod fluid_dynamics; #[cfg(not(target_arch = "wasm32"))] pub mod group; mod health; diff --git a/common/src/lib.rs b/common/src/lib.rs index a63fa26949..ec08f93737 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -17,6 +17,7 @@ )] /// Re-exported crates +#[cfg(not(target_arch = "wasm32"))] pub use uuid; // modules @@ -31,7 +32,6 @@ pub mod character; #[cfg(not(target_arch = "wasm32"))] pub mod cmd; pub mod combat; pub mod comp; -#[cfg(not(target_arch = "wasm32"))] pub mod consts; #[cfg(not(target_arch = "wasm32"))] pub mod depot; #[cfg(not(target_arch = "wasm32"))] @@ -46,9 +46,7 @@ pub mod generation; #[cfg(not(target_arch = "wasm32"))] pub mod grid; #[cfg(not(target_arch = "wasm32"))] pub mod lottery; -#[cfg(not(target_arch = "wasm32"))] -#[cfg(not(target_arch = "wasm32"))] -pub mod npc; +#[cfg(not(target_arch = "wasm32"))] pub mod npc; #[cfg(not(target_arch = "wasm32"))] pub mod outcome; #[cfg(not(target_arch = "wasm32"))] pub mod path; @@ -61,6 +59,7 @@ pub mod resources; #[cfg(not(target_arch = "wasm32"))] pub mod rtsim; #[cfg(not(target_arch = "wasm32"))] pub mod skillset_builder; +#[cfg(not(target_arch = "wasm32"))] pub mod slowjob; #[cfg(not(target_arch = "wasm32"))] pub mod spiral; diff --git a/common/src/resources.rs b/common/src/resources.rs index 13966a633c..e6d798ce84 100644 --- a/common/src/resources.rs +++ b/common/src/resources.rs @@ -1,5 +1,7 @@ +#[cfg(not(target_arch = "wasm32"))] use crate::comp::Pos; use serde::{Deserialize, Serialize}; +#[cfg(not(target_arch = "wasm32"))] use specs::Entity; /// A resource that stores the time of day. @@ -14,6 +16,7 @@ pub struct Time(pub f64); #[derive(Default)] pub struct DeltaTime(pub f32); +#[cfg(not(target_arch = "wasm32"))] #[derive(Default)] pub struct EntitiesDiedLastTick(pub Vec<(Entity, Pos)>); @@ -34,6 +37,7 @@ pub enum GameMode { /// A resource that stores the player's entity (on the client), and None on the /// server +#[cfg(not(target_arch = "wasm32"))] #[derive(Copy, Clone, Default, Debug)] pub struct PlayerEntity(pub Option); @@ -65,6 +69,7 @@ impl PlayerPhysicsSetting { /// List of which players are using client-authoratative vs server-authoratative /// physics, as a stop-gap until we can use server-authoratative physics for /// everyone +#[cfg(not(target_arch = "wasm32"))] #[derive(Clone, Default, Debug)] pub struct PlayerPhysicsSettings { pub settings: hashbrown::HashMap, From 7eda90b8f7fba5b35b7486e022b2bdb9820ffdbd Mon Sep 17 00:00:00 2001 From: Justin Shipsey Date: Fri, 11 Jun 2021 08:32:49 +0000 Subject: [PATCH 026/180] Update simple_leather.ron --- assets/common/recipe_book.ron | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/common/recipe_book.ron b/assets/common/recipe_book.ron index e344738606..fa306b04d3 100644 --- a/assets/common/recipe_book.ron +++ b/assets/common/recipe_book.ron @@ -1724,7 +1724,7 @@ ], ), "leather": ( - output: ("common.items.crafting_ing.leather.thick_leather", 1), + output: ("common.items.crafting_ing.leather.simple_leather", 1), inputs: [ (Tag(Material(Leather)), 1), ], From 25cbff68d345c9298df816ce484d633543c31415 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 11 Jun 2021 11:44:48 +0300 Subject: [PATCH 027/180] remove meaningless variables --- voxygen/src/session/mod.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index d189219479..9abef76774 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -311,11 +311,8 @@ impl PlayState for SessionState { .read_storage::() .get(player_entity) { - let crit_frac = 0.5; - if cr.charge_frac() > crit_frac { - let r1 = 3.0; - let r2 = 5.0; - fov_scaling -= r1 * cr.charge_frac() / r2; + if cr.charge_frac() > 0.5 { + fov_scaling -= 3.0 * cr.charge_frac() / 5.0; } let max_dist = if let Some(dist) = self.saved_zoom_dist { dist From 8e0891e3b10fbde55e9076eb0040e6b992eff876 Mon Sep 17 00:00:00 2001 From: Cr0ss0vr Date: Fri, 11 Jun 2021 11:59:09 +0000 Subject: [PATCH 028/180] Fix incorrect tooltip on Staff Damage skill - Update diary.rs --- voxygen/src/hud/diary.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/voxygen/src/hud/diary.rs b/voxygen/src/hud/diary.rs index a4561655b4..2e679c6362 100644 --- a/voxygen/src/hud/diary.rs +++ b/voxygen/src/hud/diary.rs @@ -1682,7 +1682,7 @@ impl<'a> Widget for Diary<'a> { Skill::Staff(BDamage), self.imgs.magic_damage_skill, state.skills_top_l[1], - "st_stamina_regen", + "st_damage", state.skill_staff_basic_1, ui, &mut events, From 6c11aa80e06c7406e75febac16ffd4ac470b19f1 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 11 Jun 2021 15:41:38 +0300 Subject: [PATCH 029/180] adjust staff firebomb --- assets/common/abilities/staff/firebomb.ron | 4 ++-- server/src/events/entity_manipulation.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/common/abilities/staff/firebomb.ron b/assets/common/abilities/staff/firebomb.ron index 926755af35..4766bf9765 100644 --- a/assets/common/abilities/staff/firebomb.ron +++ b/assets/common/abilities/staff/firebomb.ron @@ -3,9 +3,9 @@ BasicRanged( buildup_duration: 0.5, recover_duration: 0.4, projectile: Fireball( - damage: 120.0, + damage: 90.0, radius: 4.0, - energy_regen: 80, + energy_regen: 60, ), projectile_body: Object(BoltFire), projectile_speed: 60.0, diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 91fe9325dc..78b14d3517 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -689,7 +689,7 @@ pub fn handle_explosion(server: &Server, pos: Vec3, explosion: Explosion, o // Compare both checks, take whichever gives weaker effect, sets minimum of 0 so // that explosions reach a max strength on edge of entity - ((horiz_dist.max(vert_distance).max(0.0) / radius).min(1.0) - 1.0).powi(2) + ((horiz_dist.max(vert_distance).max(0.0) / radius).min(1.0) - 1.0).abs() } for effect in explosion.effects { From 11ded86a2badb2f1ac78abcdfe0c6995723894a8 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 11 Jun 2021 15:56:16 +0300 Subject: [PATCH 030/180] Make bow more snipy --- assets/common/abilities/bow/charged.ron | 10 +++++----- .../items/npc_weapons/biped_small/haniwa/adlet_bow.ron | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/assets/common/abilities/bow/charged.ron b/assets/common/abilities/bow/charged.ron index b52942e3c9..03ccf7873e 100644 --- a/assets/common/abilities/bow/charged.ron +++ b/assets/common/abilities/bow/charged.ron @@ -1,15 +1,15 @@ ChargedRanged( energy_cost: 0, energy_drain: 0, - initial_regen: 20, - scaled_regen: 100, - initial_damage: 10, - scaled_damage: 90, + initial_regen: 5, + scaled_regen: 150, + initial_damage: 5, + scaled_damage: 150, initial_knockback: 0.0, scaled_knockback: 10.0, speed: 1.0, buildup_duration: 0.2, - charge_duration: 0.5, + charge_duration: 0.9, recover_duration: 0.3, projectile_body: Object(Arrow), projectile_light: None, diff --git a/assets/common/items/npc_weapons/biped_small/haniwa/adlet_bow.ron b/assets/common/items/npc_weapons/biped_small/haniwa/adlet_bow.ron index 1d3b7fe37c..47487b341e 100644 --- a/assets/common/items/npc_weapons/biped_small/haniwa/adlet_bow.ron +++ b/assets/common/items/npc_weapons/biped_small/haniwa/adlet_bow.ron @@ -14,4 +14,4 @@ ItemDef( )), quality: Moderate, tags: [], -) \ No newline at end of file +) From e63c21d29ea411a286f7bca0694b529d9c1c7d6b Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 11 Jun 2021 16:18:59 +0300 Subject: [PATCH 031/180] Nerf recover duration for animal dashes --- assets/common/abilities/custom/quadlowbreathe/dash.ron | 2 +- assets/common/abilities/custom/quadlowquick/dash.ron | 2 +- assets/common/abilities/custom/quadmedquick/dash.ron | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/common/abilities/custom/quadlowbreathe/dash.ron b/assets/common/abilities/custom/quadlowbreathe/dash.ron index 2c101ee339..645e4b8516 100644 --- a/assets/common/abilities/custom/quadlowbreathe/dash.ron +++ b/assets/common/abilities/custom/quadlowbreathe/dash.ron @@ -13,7 +13,7 @@ DashMelee( buildup_duration: 0.5, charge_duration: 1.0, swing_duration: 0.1, - recover_duration: 0.8, + recover_duration: 1.0, charge_through: true, is_interruptible: false, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadlowquick/dash.ron b/assets/common/abilities/custom/quadlowquick/dash.ron index 5130e840cd..cb0dac45e8 100644 --- a/assets/common/abilities/custom/quadlowquick/dash.ron +++ b/assets/common/abilities/custom/quadlowquick/dash.ron @@ -13,7 +13,7 @@ DashMelee( buildup_duration: 0.5, charge_duration: 0.8, swing_duration: 0.1, - recover_duration: 0.5, + recover_duration: 1.0, charge_through: true, is_interruptible: false, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadmedquick/dash.ron b/assets/common/abilities/custom/quadmedquick/dash.ron index e305185db5..20bb1c0666 100644 --- a/assets/common/abilities/custom/quadmedquick/dash.ron +++ b/assets/common/abilities/custom/quadmedquick/dash.ron @@ -13,7 +13,7 @@ DashMelee( buildup_duration: 1.2, charge_duration: 1.0, swing_duration: 0.1, - recover_duration: 0.5, + recover_duration: 1.0, charge_through: true, is_interruptible: false, damage_kind: Crushing, From 982e5986c3a70e6ad7444d07ed0fd41932aacfdf Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 11 Jun 2021 17:20:46 +0300 Subject: [PATCH 032/180] revert bow to almost `master` values --- assets/common/abilities/bow/charged.ron | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/common/abilities/bow/charged.ron b/assets/common/abilities/bow/charged.ron index 03ccf7873e..5f13546486 100644 --- a/assets/common/abilities/bow/charged.ron +++ b/assets/common/abilities/bow/charged.ron @@ -2,14 +2,14 @@ ChargedRanged( energy_cost: 0, energy_drain: 0, initial_regen: 5, - scaled_regen: 150, + scaled_regen: 120, initial_damage: 5, - scaled_damage: 150, + scaled_damage: 120, initial_knockback: 0.0, scaled_knockback: 10.0, speed: 1.0, buildup_duration: 0.2, - charge_duration: 0.9, + charge_duration: 1.0, recover_duration: 0.3, projectile_body: Object(Arrow), projectile_light: None, From 4167621f5d40ba306f5faa97d64919e4053cac00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Sat, 12 Jun 2021 10:14:07 +0200 Subject: [PATCH 033/180] Change the version number to 0.10 --- CHANGELOG.md | 13 ++++++++++++- Cargo.lock | 32 ++++++++++++++++---------------- client/Cargo.toml | 2 +- common/Cargo.toml | 2 +- common/assets/Cargo.toml | 2 +- common/base/Cargo.toml | 2 +- common/ecs/Cargo.toml | 2 +- common/frontend/Cargo.toml | 2 +- common/net/Cargo.toml | 2 +- common/state/Cargo.toml | 2 +- common/systems/Cargo.toml | 2 +- server-cli/Cargo.toml | 2 +- server/Cargo.toml | 2 +- voxygen/Cargo.toml | 2 +- voxygen/anim/Cargo.toml | 2 +- voxygen/anim/dyn/Cargo.toml | 2 +- voxygen/i18n/Cargo.toml | 2 +- world/Cargo.toml | 2 +- 18 files changed, 44 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a6d616d3a..4466ae1851 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +### Changed + +### Removed + +### Fixed + +## [0.10.0] - 2021-06-12 + +### Added + - New Skills for Climbing: Climbing Speed and Climbing Cost - Pickaxes (can be used to collect gems and mine weak rock) - You can now jump out of rolls for a slight jump boost @@ -727,7 +737,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 _0.1.0 was part of the legacy engine_ -[unreleased]: https://gitlab.com/veloren/veloren/compare?from=v0.9.0&to=master +[unreleased]: https://gitlab.com/veloren/veloren/compare?from=v0.10.0&to=master +[0.9.0]: https://gitlab.com/veloren/veloren/compare?from=v0.9.0&to=v0.10.0 [0.9.0]: https://gitlab.com/veloren/veloren/compare?from=v0.8.0&to=v0.9.0 [0.8.0]: https://gitlab.com/veloren/veloren/compare?from=v0.7.0&to=v0.8.0 [0.7.0]: https://gitlab.com/veloren/veloren/compare?from=v0.6.0&to=v0.7.0 diff --git a/Cargo.lock b/Cargo.lock index d6f4c1a325..cff916f047 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5747,7 +5747,7 @@ dependencies = [ [[package]] name = "veloren-client" -version = "0.9.0" +version = "0.10.0" dependencies = [ "async-channel", "authc", @@ -5779,7 +5779,7 @@ dependencies = [ [[package]] name = "veloren-common" -version = "0.9.0" +version = "0.10.0" dependencies = [ "approx 0.4.0", "bitflags", @@ -5821,7 +5821,7 @@ dependencies = [ [[package]] name = "veloren-common-assets" -version = "0.9.0" +version = "0.10.0" dependencies = [ "assets_manager", "dot_vox", @@ -5835,7 +5835,7 @@ dependencies = [ [[package]] name = "veloren-common-base" -version = "0.9.0" +version = "0.10.0" dependencies = [ "directories-next", "tracing", @@ -5844,7 +5844,7 @@ dependencies = [ [[package]] name = "veloren-common-ecs" -version = "0.9.0" +version = "0.10.0" dependencies = [ "float-cmp", "specs", @@ -5854,7 +5854,7 @@ dependencies = [ [[package]] name = "veloren-common-frontend" -version = "0.9.0" +version = "0.10.0" dependencies = [ "termcolor", "tracing", @@ -5867,7 +5867,7 @@ dependencies = [ [[package]] name = "veloren-common-net" -version = "0.9.0" +version = "0.10.0" dependencies = [ "bincode", "flate2", @@ -5885,7 +5885,7 @@ dependencies = [ [[package]] name = "veloren-common-state" -version = "0.9.0" +version = "0.10.0" dependencies = [ "bincode", "hashbrown 0.11.2", @@ -5908,7 +5908,7 @@ dependencies = [ [[package]] name = "veloren-common-systems" -version = "0.9.0" +version = "0.10.0" dependencies = [ "hashbrown 0.11.2", "indexmap", @@ -5927,7 +5927,7 @@ dependencies = [ [[package]] name = "veloren-i18n" -version = "0.9.0" +version = "0.10.0" dependencies = [ "clap", "deunicode", @@ -6016,7 +6016,7 @@ dependencies = [ [[package]] name = "veloren-server" -version = "0.9.0" +version = "0.10.0" dependencies = [ "atomicwrites", "authc", @@ -6060,7 +6060,7 @@ dependencies = [ [[package]] name = "veloren-server-cli" -version = "0.9.0" +version = "0.10.0" dependencies = [ "ansi-parser", "clap", @@ -6084,7 +6084,7 @@ dependencies = [ [[package]] name = "veloren-voxygen" -version = "0.9.0" +version = "0.10.0" dependencies = [ "backtrace", "bincode", @@ -6154,7 +6154,7 @@ dependencies = [ [[package]] name = "veloren-voxygen-anim" -version = "0.9.0" +version = "0.10.0" dependencies = [ "bytemuck", "find_folder", @@ -6168,14 +6168,14 @@ dependencies = [ [[package]] name = "veloren-voxygen-anim-dyn" -version = "0.9.0" +version = "0.10.0" dependencies = [ "veloren-voxygen-anim", ] [[package]] name = "veloren-world" -version = "0.9.0" +version = "0.10.0" dependencies = [ "arr_macro", "assets_manager", diff --git a/client/Cargo.toml b/client/Cargo.toml index e878fec0b0..449b18ba21 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "veloren-client" -version = "0.9.0" +version = "0.10.0" authors = ["Joshua Barretto "] edition = "2018" diff --git a/common/Cargo.toml b/common/Cargo.toml index a34fce597f..ebc46b92bd 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "veloren-common" -version = "0.9.0" +version = "0.10.0" authors = ["Joshua Barretto ", "Maciej Ćwięka ", "Imbris "] edition = "2018" diff --git a/common/assets/Cargo.toml b/common/assets/Cargo.toml index f72aa12cea..e4e67aed29 100644 --- a/common/assets/Cargo.toml +++ b/common/assets/Cargo.toml @@ -3,7 +3,7 @@ authors = ["juliancoffee ", "Marcel Märtens "] edition = "2018" name = "veloren-common-base" description = "minimal dependency for crates to now depend on whole common" -version = "0.9.0" +version = "0.10.0" [features] tracy = ["tracy-client"] diff --git a/common/ecs/Cargo.toml b/common/ecs/Cargo.toml index 591a26a3a4..687949a9df 100644 --- a/common/ecs/Cargo.toml +++ b/common/ecs/Cargo.toml @@ -2,7 +2,7 @@ authors = ["Marcel Märtens "] edition = "2018" name = "veloren-common-ecs" -version = "0.9.0" +version = "0.10.0" [features] tracy = ["common-base/tracy"] diff --git a/common/frontend/Cargo.toml b/common/frontend/Cargo.toml index 83c06a72b4..fcde12a404 100644 --- a/common/frontend/Cargo.toml +++ b/common/frontend/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Marcel Märtens ", "Imbris "] edition = "2018" name = "veloren-common-net" -version = "0.9.0" +version = "0.10.0" [features] tracy = ["common/tracy"] diff --git a/common/state/Cargo.toml b/common/state/Cargo.toml index 2df6c875c9..f613bb9a3c 100644 --- a/common/state/Cargo.toml +++ b/common/state/Cargo.toml @@ -2,7 +2,7 @@ authors = ["Marcel Märtens "] edition = "2018" name = "veloren-common-state" -version = "0.9.0" +version = "0.10.0" [features] tracy = ["common/tracy"] diff --git a/common/systems/Cargo.toml b/common/systems/Cargo.toml index 582315e8bb..8f1bacdf86 100644 --- a/common/systems/Cargo.toml +++ b/common/systems/Cargo.toml @@ -2,7 +2,7 @@ authors = ["Marcel Märtens "] edition = "2018" name = "veloren-common-systems" -version = "0.9.0" +version = "0.10.0" [features] tracy = ["common/tracy"] diff --git a/server-cli/Cargo.toml b/server-cli/Cargo.toml index 5efc0363c6..193fe74446 100644 --- a/server-cli/Cargo.toml +++ b/server-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "veloren-server-cli" -version = "0.9.0" +version = "0.10.0" authors = ["Joshua Barretto "] edition = "2018" diff --git a/server/Cargo.toml b/server/Cargo.toml index c7715758b5..455a38147e 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "veloren-server" -version = "0.9.0" +version = "0.10.0" authors = ["Joshua Barretto "] edition = "2018" diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 85b21bfccb..b84a19dcb5 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Joshua Barretto ", "Imbris ", "Imbris "] edition = "2018" name = "veloren-voxygen-anim" -version = "0.9.0" +version = "0.10.0" [features] use-dyn-lib = ["libloading", "notify", "lazy_static", "tracing", "find_folder"] diff --git a/voxygen/anim/dyn/Cargo.toml b/voxygen/anim/dyn/Cargo.toml index becc2a2697..6bf01bc546 100644 --- a/voxygen/anim/dyn/Cargo.toml +++ b/voxygen/anim/dyn/Cargo.toml @@ -2,7 +2,7 @@ authors = ["Imbris "] edition = "2018" name = "veloren-voxygen-anim-dyn" -version = "0.9.0" +version = "0.10.0" [lib] # Using dylib instead of cdylib increases the size 3 -> 13 mb diff --git a/voxygen/i18n/Cargo.toml b/voxygen/i18n/Cargo.toml index 5580c453df..0d7ee94b70 100644 --- a/voxygen/i18n/Cargo.toml +++ b/voxygen/i18n/Cargo.toml @@ -3,7 +3,7 @@ authors = ["juliancoffee ", "Rémy Phelipot"] edition = "2018" name = "veloren-i18n" description = "Crate for internalization and diagnostic of existing localizations." -version = "0.9.0" +version = "0.10.0" [[bin]] name = "i18n-check" diff --git a/world/Cargo.toml b/world/Cargo.toml index 3451fc6c5c..07b4084e78 100644 --- a/world/Cargo.toml +++ b/world/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "veloren-world" -version = "0.9.0" +version = "0.10.0" authors = ["Joshua Barretto "] edition = "2018" From 62eaabfe88fcc54a72ddcec9e56f00559ccf8288 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 11 Jun 2021 22:00:06 +0300 Subject: [PATCH 034/180] Introduce hit_timing for combo_melee bonus: speeding up first swing of sword from 0.15 to 0.1 and reducing poise damage of hammer from 25 to 20 --- assets/common/abilities/axe/doublestrike.ron | 2 ++ .../abilities/axesimple/doublestrike.ron | 2 ++ .../custom/basilisk/singlestrike.ron | 1 + .../custom/basilisk/triplestrike.ron | 3 ++ .../custom/birdlargebreathe/triplestrike.ron | 3 ++ .../custom/birdlargefire/triplestrike.ron | 3 ++ .../abilities/custom/husk/singlestrike.ron | 1 + .../abilities/custom/husk/triplestrike.ron | 3 ++ .../custom/quadlowbasic/singlestrike.ron | 1 + .../custom/quadlowbasic/triplestrike.ron | 3 ++ .../custom/quadlowbreathe/triplestrike.ron | 3 ++ .../custom/quadlowquick/quadstrike.ron | 4 +++ .../custom/quadlowranged/singlestrike.ron | 1 + .../custom/quadlowtail/triplestrike.ron | 3 ++ .../custom/quadmedbasic/singlestrike.ron | 1 + .../custom/quadmedbasic/triplestrike.ron | 3 ++ .../custom/quadmedcharge/doublestrike.ron | 2 ++ .../custom/quadmedjump/doublestrike.ron | 2 ++ .../custom/quadmedquick/triplestrike.ron | 3 ++ .../custom/quadsmallbasic/singlestrike.ron | 1 + .../custom/stonegolemfist/singlestrike.ron | 1 + .../custom/theropodbasic/singlestrike.ron | 1 + .../custom/theropodbasic/triplestrike.ron | 3 ++ .../custom/theropodbird/singlestrike.ron | 1 + .../custom/theropodbird/triplestrike.ron | 3 ++ .../custom/wendigomagic/singlestrike.ron | 1 + .../common/abilities/hammer/singlestrike.ron | 3 +- .../abilities/hammersimple/doublestrike.ron | 2 ++ .../common/abilities/spear/doublestrike.ron | 2 ++ .../common/abilities/sword/triplestrike.ron | 5 ++- .../abilities/swordsimple/doublestrike.ron | 2 ++ common/src/comp/ability.rs | 1 + common/src/states/charged_melee.rs | 2 +- common/src/states/combo_melee.rs | 36 ++++++++++++++++--- .../audio/sfx/event_mapper/combat/tests.rs | 4 +++ 35 files changed, 104 insertions(+), 8 deletions(-) diff --git a/assets/common/abilities/axe/doublestrike.ron b/assets/common/abilities/axe/doublestrike.ron index 545aab3ae9..029bb4cdaf 100644 --- a/assets/common/abilities/axe/doublestrike.ron +++ b/assets/common/abilities/axe/doublestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 50.0, base_buildup_duration: 0.15, base_swing_duration: 0.075, + hit_timing: 0.6, base_recover_duration: 0.35, forward_movement: 0.5, damage_kind: Slashing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.2, base_swing_duration: 0.1, + hit_timing: 0.6, base_recover_duration: 0.35, forward_movement: 0.25, damage_kind: Slashing, diff --git a/assets/common/abilities/axesimple/doublestrike.ron b/assets/common/abilities/axesimple/doublestrike.ron index 2a87b78073..d97b3dbcce 100644 --- a/assets/common/abilities/axesimple/doublestrike.ron +++ b/assets/common/abilities/axesimple/doublestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 50.0, base_buildup_duration: 0.6, base_swing_duration: 0.12, + hit_timing: 0.5, base_recover_duration: 0.6, forward_movement: 3.5, damage_kind: Slashing, @@ -26,6 +27,7 @@ ComboMelee( angle: 15.0, base_buildup_duration: 0.5, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 1.2, forward_movement: 4.5, damage_kind: Slashing, diff --git a/assets/common/abilities/custom/basilisk/singlestrike.ron b/assets/common/abilities/custom/basilisk/singlestrike.ron index 3f21ccc88a..65846146d6 100644 --- a/assets/common/abilities/custom/basilisk/singlestrike.ron +++ b/assets/common/abilities/custom/basilisk/singlestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 60.0, base_buildup_duration: 0.4, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.4, forward_movement: 3.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/basilisk/triplestrike.ron b/assets/common/abilities/custom/basilisk/triplestrike.ron index fced56cfda..cd4942d2bb 100644 --- a/assets/common/abilities/custom/basilisk/triplestrike.ron +++ b/assets/common/abilities/custom/basilisk/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.65, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 2.0, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Crushing, @@ -41,6 +43,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/birdlargebreathe/triplestrike.ron b/assets/common/abilities/custom/birdlargebreathe/triplestrike.ron index eb332435a4..b0a727e645 100644 --- a/assets/common/abilities/custom/birdlargebreathe/triplestrike.ron +++ b/assets/common/abilities/custom/birdlargebreathe/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 2.0, damage_kind: Slashing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Slashing, @@ -41,6 +43,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.65, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Slashing, diff --git a/assets/common/abilities/custom/birdlargefire/triplestrike.ron b/assets/common/abilities/custom/birdlargefire/triplestrike.ron index eb332435a4..b0a727e645 100644 --- a/assets/common/abilities/custom/birdlargefire/triplestrike.ron +++ b/assets/common/abilities/custom/birdlargefire/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 2.0, damage_kind: Slashing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Slashing, @@ -41,6 +43,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.65, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Slashing, diff --git a/assets/common/abilities/custom/husk/singlestrike.ron b/assets/common/abilities/custom/husk/singlestrike.ron index 5d4aa94c8b..92e1e2d3eb 100644 --- a/assets/common/abilities/custom/husk/singlestrike.ron +++ b/assets/common/abilities/custom/husk/singlestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 60.0, base_buildup_duration: 0.25, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.25, forward_movement: 0.5, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/husk/triplestrike.ron b/assets/common/abilities/custom/husk/triplestrike.ron index d14f182feb..25e985d3d2 100644 --- a/assets/common/abilities/custom/husk/triplestrike.ron +++ b/assets/common/abilities/custom/husk/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.2, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.2, forward_movement: 1.0, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.22, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.2, forward_movement: 0.0, damage_kind: Crushing, @@ -41,6 +43,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.2, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.2, forward_movement: 1.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadlowbasic/singlestrike.ron b/assets/common/abilities/custom/quadlowbasic/singlestrike.ron index 8f60297e99..c0bd5a47fb 100644 --- a/assets/common/abilities/custom/quadlowbasic/singlestrike.ron +++ b/assets/common/abilities/custom/quadlowbasic/singlestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 60.0, base_buildup_duration: 0.4, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.4, forward_movement: 3.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadlowbasic/triplestrike.ron b/assets/common/abilities/custom/quadlowbasic/triplestrike.ron index 3fae8726f5..f7b49ad5ac 100644 --- a/assets/common/abilities/custom/quadlowbasic/triplestrike.ron +++ b/assets/common/abilities/custom/quadlowbasic/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.65, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 2.0, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Crushing, @@ -41,6 +43,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadlowbreathe/triplestrike.ron b/assets/common/abilities/custom/quadlowbreathe/triplestrike.ron index 19320571bb..6944d309f3 100644 --- a/assets/common/abilities/custom/quadlowbreathe/triplestrike.ron +++ b/assets/common/abilities/custom/quadlowbreathe/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.65, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 2.0, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Crushing, @@ -41,6 +43,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadlowquick/quadstrike.ron b/assets/common/abilities/custom/quadlowquick/quadstrike.ron index 2bd6e570f3..3c7f848f43 100644 --- a/assets/common/abilities/custom/quadlowquick/quadstrike.ron +++ b/assets/common/abilities/custom/quadlowquick/quadstrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.6, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.1, forward_movement: 1.5, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.15, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.1, forward_movement: 0.8, damage_kind: Crushing, @@ -41,6 +43,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.2, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.1, forward_movement: 0.8, damage_kind: Crushing, @@ -56,6 +59,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.2, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.1, forward_movement: 0.8, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadlowranged/singlestrike.ron b/assets/common/abilities/custom/quadlowranged/singlestrike.ron index 7d36295027..81b81f3cd0 100644 --- a/assets/common/abilities/custom/quadlowranged/singlestrike.ron +++ b/assets/common/abilities/custom/quadlowranged/singlestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 60.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.4, forward_movement: 3.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadlowtail/triplestrike.ron b/assets/common/abilities/custom/quadlowtail/triplestrike.ron index a35efe048e..457328d1c0 100644 --- a/assets/common/abilities/custom/quadlowtail/triplestrike.ron +++ b/assets/common/abilities/custom/quadlowtail/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.65, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.2, forward_movement: 2.0, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.2, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.2, forward_movement: 1.0, damage_kind: Crushing, @@ -41,6 +43,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.2, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.2, forward_movement: 1.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadmedbasic/singlestrike.ron b/assets/common/abilities/custom/quadmedbasic/singlestrike.ron index fef9810b42..2dcb7c3eb5 100644 --- a/assets/common/abilities/custom/quadmedbasic/singlestrike.ron +++ b/assets/common/abilities/custom/quadmedbasic/singlestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 60.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.4, forward_movement: 1.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadmedbasic/triplestrike.ron b/assets/common/abilities/custom/quadmedbasic/triplestrike.ron index 4b8544b345..1a5577da1d 100644 --- a/assets/common/abilities/custom/quadmedbasic/triplestrike.ron +++ b/assets/common/abilities/custom/quadmedbasic/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.45, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.2, forward_movement: 1.0, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.2, forward_movement: 0.0, damage_kind: Crushing, @@ -41,6 +43,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.2, forward_movement: 1.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadmedcharge/doublestrike.ron b/assets/common/abilities/custom/quadmedcharge/doublestrike.ron index 9ec940c20f..83ba1ae46b 100644 --- a/assets/common/abilities/custom/quadmedcharge/doublestrike.ron +++ b/assets/common/abilities/custom/quadmedcharge/doublestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.65, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.0, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 0.5, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadmedjump/doublestrike.ron b/assets/common/abilities/custom/quadmedjump/doublestrike.ron index 635eb2bfac..d57e0c63df 100644 --- a/assets/common/abilities/custom/quadmedjump/doublestrike.ron +++ b/assets/common/abilities/custom/quadmedjump/doublestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.65, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.0, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadmedquick/triplestrike.ron b/assets/common/abilities/custom/quadmedquick/triplestrike.ron index cdf07c952d..596df64343 100644 --- a/assets/common/abilities/custom/quadmedquick/triplestrike.ron +++ b/assets/common/abilities/custom/quadmedquick/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 40.0, base_buildup_duration: 0.6, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 0.4, forward_movement: 0.3, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 40.0, base_buildup_duration: 0.4, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 0.5, damage_kind: Crushing, @@ -41,6 +43,7 @@ ComboMelee( angle: 40.0, base_buildup_duration: 0.4, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 0.5, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadsmallbasic/singlestrike.ron b/assets/common/abilities/custom/quadsmallbasic/singlestrike.ron index 4ce36a7a0f..0448be9345 100644 --- a/assets/common/abilities/custom/quadsmallbasic/singlestrike.ron +++ b/assets/common/abilities/custom/quadsmallbasic/singlestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 50.0, base_buildup_duration: 0.3, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/stonegolemfist/singlestrike.ron b/assets/common/abilities/custom/stonegolemfist/singlestrike.ron index 74d2853a78..0c22b0bdb7 100644 --- a/assets/common/abilities/custom/stonegolemfist/singlestrike.ron +++ b/assets/common/abilities/custom/stonegolemfist/singlestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 60.0, base_buildup_duration: 0.9, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.9, forward_movement: 3.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/theropodbasic/singlestrike.ron b/assets/common/abilities/custom/theropodbasic/singlestrike.ron index 17c2f98b28..08cf1209e4 100644 --- a/assets/common/abilities/custom/theropodbasic/singlestrike.ron +++ b/assets/common/abilities/custom/theropodbasic/singlestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 60.0, base_buildup_duration: 0.5, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 0.4, forward_movement: 3.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/theropodbasic/triplestrike.ron b/assets/common/abilities/custom/theropodbasic/triplestrike.ron index 5c19bf5d62..c8766e2bbd 100644 --- a/assets/common/abilities/custom/theropodbasic/triplestrike.ron +++ b/assets/common/abilities/custom/theropodbasic/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.9, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.0, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.5, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 0.15, forward_movement: 1.0, damage_kind: Crushing, @@ -41,6 +43,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.35, base_swing_duration: 0.125, + hit_timing: 0.5, base_recover_duration: 0.9, forward_movement: 1.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/theropodbird/singlestrike.ron b/assets/common/abilities/custom/theropodbird/singlestrike.ron index db41b51c21..1401500a4b 100644 --- a/assets/common/abilities/custom/theropodbird/singlestrike.ron +++ b/assets/common/abilities/custom/theropodbird/singlestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 15.0, base_buildup_duration: 0.4, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 0.4, forward_movement: 3.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/theropodbird/triplestrike.ron b/assets/common/abilities/custom/theropodbird/triplestrike.ron index 0ae2472f83..9693aa46d0 100644 --- a/assets/common/abilities/custom/theropodbird/triplestrike.ron +++ b/assets/common/abilities/custom/theropodbird/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 15.0, base_buildup_duration: 0.65, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.0, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 15.0, base_buildup_duration: 0.4, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 0.15, forward_movement: 1.0, damage_kind: Crushing, @@ -41,6 +43,7 @@ ComboMelee( angle: 15.0, base_buildup_duration: 0.35, base_swing_duration: 0.125, + hit_timing: 0.5, base_recover_duration: 0.9, forward_movement: 1.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/wendigomagic/singlestrike.ron b/assets/common/abilities/custom/wendigomagic/singlestrike.ron index c6fe60fade..39185a761d 100644 --- a/assets/common/abilities/custom/wendigomagic/singlestrike.ron +++ b/assets/common/abilities/custom/wendigomagic/singlestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.6, base_swing_duration: 0.2, + hit_timing: 0.5, base_recover_duration: 0.4, forward_movement: 5.0, damage_kind: Crushing, diff --git a/assets/common/abilities/hammer/singlestrike.ron b/assets/common/abilities/hammer/singlestrike.ron index 5a7657c7ea..85da88b26e 100644 --- a/assets/common/abilities/hammer/singlestrike.ron +++ b/assets/common/abilities/hammer/singlestrike.ron @@ -3,13 +3,14 @@ ComboMelee( stage: 1, base_damage: 150, damage_increase: 10, - base_poise_damage: 25, + base_poise_damage: 20, poise_damage_increase: 0, knockback: 5.0, range: 4.5, angle: 50.0, base_buildup_duration: 0.2, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.45, forward_movement: 0.0, damage_kind: Crushing, diff --git a/assets/common/abilities/hammersimple/doublestrike.ron b/assets/common/abilities/hammersimple/doublestrike.ron index 4bc8e7f3b2..13d7423c1e 100644 --- a/assets/common/abilities/hammersimple/doublestrike.ron +++ b/assets/common/abilities/hammersimple/doublestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 50.0, base_buildup_duration: 0.6, base_swing_duration: 0.08, + hit_timing: 0.5, base_recover_duration: 0.6, forward_movement: 3.5, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.5, base_swing_duration: 0.25, + hit_timing: 0.5, base_recover_duration: 1.2, forward_movement: 2.0, damage_kind: Crushing, diff --git a/assets/common/abilities/spear/doublestrike.ron b/assets/common/abilities/spear/doublestrike.ron index e7e1c92980..075850a90c 100644 --- a/assets/common/abilities/spear/doublestrike.ron +++ b/assets/common/abilities/spear/doublestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 15.0, base_buildup_duration: 0.35, base_swing_duration: 0.075, + hit_timing: 0.5, base_recover_duration: 0.4, forward_movement: 0.7, damage_kind: Piercing, @@ -26,6 +27,7 @@ ComboMelee( angle: 15.0, base_buildup_duration: 0.5, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.5, forward_movement: 0.7, damage_kind: Piercing, diff --git a/assets/common/abilities/sword/triplestrike.ron b/assets/common/abilities/sword/triplestrike.ron index ea05f387d8..b078f9f180 100644 --- a/assets/common/abilities/sword/triplestrike.ron +++ b/assets/common/abilities/sword/triplestrike.ron @@ -9,8 +9,9 @@ ComboMelee( knockback: 1.0, range: 4.0, angle: 30.0, - base_buildup_duration: 0.15, + base_buildup_duration: 0.1, base_swing_duration: 0.075, + hit_timing: 0.5, base_recover_duration: 0.15, forward_movement: 0.5, damage_kind: Slashing, @@ -26,6 +27,7 @@ ComboMelee( angle: 40.0, base_buildup_duration: 0.1, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 0.0, damage_kind: Slashing, @@ -41,6 +43,7 @@ ComboMelee( angle: 10.0, base_buildup_duration: 0.15, base_swing_duration: 0.1, + hit_timing: 0.2, base_recover_duration: 0.35, forward_movement: 1.2, damage_kind: Piercing, diff --git a/assets/common/abilities/swordsimple/doublestrike.ron b/assets/common/abilities/swordsimple/doublestrike.ron index 5aefeb50a6..f8a9b3860e 100644 --- a/assets/common/abilities/swordsimple/doublestrike.ron +++ b/assets/common/abilities/swordsimple/doublestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 50.0, base_buildup_duration: 0.4, base_swing_duration: 0.08, + hit_timing: 0.5, base_recover_duration: 0.5, forward_movement: 2.5, damage_kind: Slashing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.7, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.7, forward_movement: 2.0, damage_kind: Slashing, diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 8ab75b8928..d0eeb5d074 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -1383,6 +1383,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { ori_modifier: *ori_modifier as f32, ability_info, }, + exhausted: false, stage: 1, timer: Duration::default(), stage_section: StageSection::Buildup, diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index 7ad6b9cb04..27d5c11896 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -62,7 +62,7 @@ pub struct Data { pub stage_section: StageSection, /// Timer for each stage pub timer: Duration, - /// Whether the attack fired already + /// Whether the attack executed already pub exhausted: bool, /// How much the attack charged by pub charge_amount: f32, diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index a59315043a..fd547dfd6a 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -33,6 +33,8 @@ pub struct Stage { /// Duration of stage spent in swing (controls animation stuff, and can also /// be used to handle movement separately to buildup) pub base_swing_duration: T, + /// At what fraction of the swing duration to apply the melee "hit" + pub hit_timing: f32, /// Initial recover duration of stage (how long until character exits state) pub base_recover_duration: T, /// How much forward movement there is in the swing portion of the stage @@ -53,6 +55,7 @@ impl Stage { range: self.range, angle: self.angle, base_buildup_duration: Duration::from_secs_f32(self.base_buildup_duration), + hit_timing: self.hit_timing, base_swing_duration: Duration::from_secs_f32(self.base_swing_duration), base_recover_duration: Duration::from_secs_f32(self.base_recover_duration), forward_movement: self.forward_movement, @@ -112,6 +115,8 @@ pub struct Data { /// Struct containing data that does not change over the course of the /// character state pub static_data: StaticData, + /// Whether the attack was executed already + pub exhausted: bool, /// Indicates what stage the combo is in pub stage: u32, /// Timer for each stage @@ -155,8 +160,24 @@ impl CharacterBehavior for Data { stage_section: StageSection::Swing, ..*self }); + } + }, + StageSection::Swing => { + if self.timer.as_secs_f32() + > self.static_data.stage_data[stage_index].hit_timing + * self.static_data.stage_data[stage_index] + .base_swing_duration + .as_secs_f32() + && !self.exhausted + { + // Swing + update.character = CharacterState::ComboMelee(Data { + static_data: self.static_data.clone(), + timer: tick_attack_or_default(data, self.timer, None), + exhausted: true, + ..*self + }); - // Hit attempt let damage = self.static_data.stage_data[stage_index].base_damage + (self .static_data @@ -177,6 +198,7 @@ impl CharacterBehavior for Data { CombatEffect::Poise(poise), ) .with_requirement(CombatRequirement::AnyDamage); + let knockback = AttackEffect::new( Some(GroupTarget::OutOfGroup), CombatEffect::Knockback(Knockback { @@ -185,13 +207,17 @@ impl CharacterBehavior for Data { }), ) .with_requirement(CombatRequirement::AnyDamage); + let energy = self.static_data.max_energy_gain.min( self.static_data.initial_energy_gain + data.combo.counter() as f32 * self.static_data.energy_increase, ); + let energy = AttackEffect::new(None, CombatEffect::EnergyReward(energy)) .with_requirement(CombatRequirement::AnyDamage); + let buff = CombatEffect::Buff(CombatBuff::default_physical()); + let damage = AttackDamage::new( Damage { source: DamageSource::Melee, @@ -201,8 +227,10 @@ impl CharacterBehavior for Data { Some(GroupTarget::OutOfGroup), ) .with_effect(buff); + let (crit_chance, crit_mult) = get_crit_data(data, self.static_data.ability_info); + let attack = Attack::default() .with_damage(damage) .with_crit(crit_chance, crit_mult) @@ -228,10 +256,8 @@ impl CharacterBehavior for Data { }) .filter(|(_, tool)| tool == &Some(ToolKind::Pick)), }); - } - }, - StageSection::Swing => { - if self.timer < self.static_data.stage_data[stage_index].base_swing_duration { + } else if self.timer < self.static_data.stage_data[stage_index].base_swing_duration + { handle_orientation(data, &mut update, 0.4 * self.static_data.ori_modifier); // Forward movement diff --git a/voxygen/src/audio/sfx/event_mapper/combat/tests.rs b/voxygen/src/audio/sfx/event_mapper/combat/tests.rs index a024d559ea..73df919023 100644 --- a/voxygen/src/audio/sfx/event_mapper/combat/tests.rs +++ b/voxygen/src/audio/sfx/event_mapper/combat/tests.rs @@ -128,6 +128,7 @@ fn matches_ability_stage() { angle: 30.0, base_buildup_duration: Duration::from_millis(500), base_swing_duration: Duration::from_millis(200), + hit_timing: 0.5, base_recover_duration: Duration::from_millis(400), forward_movement: 0.5, damage_kind: DamageKind::Slashing, @@ -142,6 +143,7 @@ fn matches_ability_stage() { ori_modifier: 1.0, ability_info: empty_ability_info(), }, + exhausted: false, stage: 1, timer: Duration::default(), stage_section: states::utils::StageSection::Swing, @@ -187,6 +189,7 @@ fn ignores_different_ability_stage() { angle: 30.0, base_buildup_duration: Duration::from_millis(500), base_swing_duration: Duration::from_millis(200), + hit_timing: 0.5, base_recover_duration: Duration::from_millis(400), forward_movement: 0.5, damage_kind: DamageKind::Slashing, @@ -201,6 +204,7 @@ fn ignores_different_ability_stage() { ori_modifier: 1.0, ability_info: empty_ability_info(), }, + exhausted: false, stage: 1, timer: Duration::default(), stage_section: states::utils::StageSection::Swing, From d02ff2db200647c37138604ce29c340e21510883 Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Sat, 12 Jun 2021 11:10:06 -0400 Subject: [PATCH 035/180] Don't apply e2e pushback during a forced movement character state. --- CHANGELOG.md | 1 + assets/voxygen/i18n/en/_manifest.ron | 1 + common/src/comp/character_state.rs | 11 ++++++++++- common/systems/src/phys.rs | 15 +++++++++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4466ae1851..fb77d098c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added ### Changed +- Entity-entity pushback is no longer applied in forced movement states like rolling and leaping. ### Removed diff --git a/assets/voxygen/i18n/en/_manifest.ron b/assets/voxygen/i18n/en/_manifest.ron index 737713c4dc..2be067818b 100644 --- a/assets/voxygen/i18n/en/_manifest.ron +++ b/assets/voxygen/i18n/en/_manifest.ron @@ -58,6 +58,7 @@ "You can toggle showing your amount of health on the healthbar in the settings.", "Sit near a campfire (with the 'K' key) to slowly recover from your injuries.", "Need more bags or better armor to continue your journey? Press 'C' to open the crafting menu!", + "Try jumping when rolling through creatures.", ], "npc.speech.villager": [ "Isn't it such a lovely day?", diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 4e40b2ac6c..b21d90008f 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -2,7 +2,7 @@ use crate::{ combat::Attack, comp::{tool::ToolKind, Density, Energy, InputAttr, InputKind, Ori, Pos, Vel}, event::{LocalEvent, ServerEvent}, - states::{behavior::JoinData, *}, + states::{behavior::JoinData, utils::StageSection, *}, }; use serde::{Deserialize, Serialize}; use specs::{Component, DerefFlaggedStorage, VecStorage}; @@ -196,6 +196,15 @@ impl CharacterState { pub fn is_stunned(&self) -> bool { matches!(self, CharacterState::Stunned(_)) } + pub fn is_forced_movement(&self) -> bool { + matches!(self, + CharacterState::ComboMelee(s) if s.stage_section == StageSection::Swing) + || matches!(self, CharacterState::DashMelee(s) if s.stage_section == StageSection::Charge) + || matches!(self, CharacterState::LeapMelee(s) if s.stage_section == StageSection::Movement) + || matches!(self, CharacterState::SpinMelee(s) if s.stage_section == StageSection::Swing) + || matches!(self, CharacterState::Roll(s) if s.stage_section == StageSection::Movement) + } + /// Compares for shallow equality (does not check internal struct equality) pub fn same_variant(&self, other: &Self) -> bool { // Check if state is the same without looking at the inner data diff --git a/common/systems/src/phys.rs b/common/systems/src/phys.rs index 48c36e506d..1f4521496c 100644 --- a/common/systems/src/phys.rs +++ b/common/systems/src/phys.rs @@ -333,6 +333,21 @@ impl<'a> PhysicsData<'a> { }; } + // Don't apply e2e pushback to entities that are in a forced movement state + // (e.g. roll, leapmelee). This allows leaps to work properly (since you won't + // get pushed away before delivering the hit), and allows rolling through an + // enemy when trapped (e.g. with minotaur). This allows using e2e pushback to + // gain speed by jumping out of a roll while in the middle of a collider, this + // is an intentional combat mechanic. + if let Some(cs) = char_state_maybe { + if cs.is_forced_movement() { + return PhysicsMetrics { + entity_entity_collision_checks, + entity_entity_collisions, + }; + } + } + let z_limits = calc_z_limit(char_state_maybe, collider); // Resets touch_entities in physics From f47838ee9d17afed8d9fc9af948411faab368db8 Mon Sep 17 00:00:00 2001 From: Ben Wallis Date: Sat, 12 Jun 2021 13:43:53 +0100 Subject: [PATCH 036/180] Improved inventory swap --- common/src/comp/inventory/mod.rs | 21 ++++------------ common/src/comp/inventory/test.rs | 40 +++++++++++++++++++++++++++---- 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index a08b7d1a0e..7152f130a2 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -710,15 +710,14 @@ impl Inventory { let unloaded_items = from_equip .map(|mut from_equip| { // Unload any items held inside the previously equipped item - let items: Vec = from_equip.drain().collect(); + let mut items: Vec = from_equip.drain().collect(); // Attempt to put the unequipped item in the same slot that the inventory item // was in - if that slot no longer exists (because a large container was - // swapped for a smaller one) then push the item to the first free - // inventory slot instead. + // swapped for a smaller one) then we will attempt to push it to the inventory + // with the rest of the unloaded items. if let Err(returned) = self.insert_at(inv_slot_id, from_equip) { - self.push(returned) - .expect("Unable to push to inventory, no slots (bug in can_swap()?)"); + items.insert(0, returned); } items @@ -778,17 +777,7 @@ impl Inventory { return false; } - // If we're swapping an equipped item with an empty inventory slot, make - // sure that there will be enough space in the inventory after any - // slots granted by the item being unequipped have been removed. - if let Some(inv_slot) = self.slot(inv_slot_id) { - if inv_slot.is_none() && self.free_slots_minus_equipped_item(equip_slot) == 0 { - // No free inventory slots after slots provided by the equipped - //item are discounted - trace!("can_swap = false, no free slots minus item"); - return false; - } - } else { + if self.slot(inv_slot_id).is_none() { debug!( "can_swap = false, tried to swap into non-existent inventory slot: {:?}", inv_slot_id diff --git a/common/src/comp/inventory/test.rs b/common/src/comp/inventory/test.rs index f2acbb1775..e75c923ce7 100644 --- a/common/src/comp/inventory/test.rs +++ b/common/src/comp/inventory/test.rs @@ -207,7 +207,7 @@ fn can_swap_equipped_bag_into_empty_inv_slot( } #[test] -fn can_swap_equipped_bag_into_only_empty_slot_provided_by_itself_should_return_false() { +fn can_swap_equipped_bag_into_only_empty_slot_provided_by_itself_should_return_true() { let mut inv = Inventory::new_empty(); inv.replace_loadout_item(EquipSlot::Armor(ArmorSlot::Bag1), Some(get_test_bag(18))); @@ -216,7 +216,7 @@ fn can_swap_equipped_bag_into_only_empty_slot_provided_by_itself_should_return_f let result = inv.can_swap(InvSlotId::new(15, 17), EquipSlot::Armor(ArmorSlot::Bag1)); - assert!(!result); + assert!(result); } #[test] @@ -320,7 +320,7 @@ fn equip_equipping_smaller_bag_from_last_slot_of_big_bag() { } #[test] -fn unequip_unequipping_bag_into_its_own_slot_with_no_other_free_slots() { +fn unequip_unequipping_bag_into_its_own_slot_with_no_other_free_slots_returns_one_item() { let mut inv = Inventory::new_empty(); let bag = get_test_bag(9); @@ -335,7 +335,14 @@ fn unequip_unequipping_bag_into_its_own_slot_with_no_other_free_slots() { let result = inv.swap_inventory_loadout(InvSlotId::new(15, 0), EquipSlot::Armor(ArmorSlot::Bag1)); - assert!(result.is_empty()) + + assert_eq!(result.len(), 1); + // Because the slot the bag was swapped with no longer exists as it was provided + // by itself, the bag is returned to the caller + assert_eq!( + result[0].item_definition_id(), + "common.items.testing.test_bag" + ); } #[test] @@ -458,6 +465,31 @@ fn free_after_swap_inv_item_without_slots_swapped_with_empty_equip_slot() { assert_eq!(13, result); } +// This test is a regression test for a bug that crashed the server when +// swapping an equipped item providing slots with an item that does not +// provide slots. +#[test] +fn backpack_crash() { + let mut inv = Inventory::new_empty(); + + let backpack = Item::new_from_asset_expect("common.items.armor.misc.back.backpack"); + inv.loadout + .swap(EquipSlot::Armor(ArmorSlot::Back), Some(backpack)); + + fill_inv_slots(&mut inv, 35); + + let cape = Item::new_from_asset_expect("common.items.armor.misc.back.admin"); + assert!(inv.push(cape).is_ok()); + + let returned_items = + inv.swap_inventory_loadout(InvSlotId::new(9, 17), EquipSlot::Armor(ArmorSlot::Back)); + assert_eq!(18, returned_items.len()); + assert_eq!( + "common.items.armor.misc.back.backpack", + returned_items[0].item_definition_id() + ); +} + fn fill_inv_slots(inv: &mut Inventory, items: u16) { let msm = &MaterialStatManifest::default(); let ability_map = &AbilityMap::default(); From c17e3ad9969802e10d6a81595a46a40980870103 Mon Sep 17 00:00:00 2001 From: Monty Date: Sun, 13 Jun 2021 13:52:56 +0200 Subject: [PATCH 037/180] Cactus Colada Made cacti lootable Cactus colada recipe and item price balance fmt "make it 8 and drop it to 20 or 25? Not really sure tbh" --- assets/common/items/crafting_ing/cactus.ron | 9 ++++++++ assets/common/items/food/cactus_colada.ron | 22 ++++++++++++++++++++ assets/common/loot_tables/food/prepared.ron | 1 + assets/common/recipe_book.ron | 8 +++++++ assets/voxygen/item_image_manifest.ron | 8 +++++++ assets/voxygen/voxel/object/cactus_drink.vox | 3 +++ common/src/comp/inventory/item/mod.rs | 3 +++ common/src/terrain/sprite.rs | 3 +++ 8 files changed, 57 insertions(+) create mode 100644 assets/common/items/crafting_ing/cactus.ron create mode 100644 assets/common/items/food/cactus_colada.ron create mode 100644 assets/voxygen/voxel/object/cactus_drink.vox diff --git a/assets/common/items/crafting_ing/cactus.ron b/assets/common/items/crafting_ing/cactus.ron new file mode 100644 index 0000000000..33c34e742b --- /dev/null +++ b/assets/common/items/crafting_ing/cactus.ron @@ -0,0 +1,9 @@ +ItemDef( + name: "Cactus", + description: "Grows in warm and dry places.", + kind: Ingredient( + kind: "Cactus", + ), + quality: Common, + tags: [], +) \ No newline at end of file diff --git a/assets/common/items/food/cactus_colada.ron b/assets/common/items/food/cactus_colada.ron new file mode 100644 index 0000000000..cef46eee0d --- /dev/null +++ b/assets/common/items/food/cactus_colada.ron @@ -0,0 +1,22 @@ +ItemDef( + name: "Cactus Colada", + description: "Giving you that special prickle.", + kind: Consumable( + kind: "CactusColada", + effect: [ + Buff(( + kind: Saturation, + data: ( + strength: 25.0, + duration: Some(( + secs: 15, + nanos: 0, + )), + ), + cat_ids: [Natural], + )), + ] + ), + quality: Moderate, + tags: [Food], +) diff --git a/assets/common/loot_tables/food/prepared.ron b/assets/common/loot_tables/food/prepared.ron index 89d78c10bf..a738723739 100644 --- a/assets/common/loot_tables/food/prepared.ron +++ b/assets/common/loot_tables/food/prepared.ron @@ -6,4 +6,5 @@ (1.2, Item("common.items.food.plainsalad")), (0.5, Item("common.items.food.sunflower_icetea")), (1.0, Item("common.items.food.tomatosalad")), + (1.4, Item("common.items.food.cactus_colada")), ] diff --git a/assets/common/recipe_book.ron b/assets/common/recipe_book.ron index fa306b04d3..85f6d48076 100644 --- a/assets/common/recipe_book.ron +++ b/assets/common/recipe_book.ron @@ -49,6 +49,14 @@ ], craft_sprite: Some(Cauldron), ), + "cactus_colada": ( + output: ("common.items.food.cactus_colada", 1), + inputs: [ + (Item("common.items.crafting_ing.empty_vial"), 1), + (Item("common.items.crafting_ing.cactus"), 8), + ], + craft_sprite: Some(Cauldron), + ), "collar_basic": ( output: ("common.items.utility.collar", 1), inputs: [ diff --git a/assets/voxygen/item_image_manifest.ron b/assets/voxygen/item_image_manifest.ron index 5bb8796f1c..0e93d0282e 100644 --- a/assets/voxygen/item_image_manifest.ron +++ b/assets/voxygen/item_image_manifest.ron @@ -2272,6 +2272,10 @@ Consumable("Coconut"): Png( "element.items.item_coconut", ), + Consumable("CactusColada"): VoxTrans( + "voxel.object.cactus_drink", + (-1.0, 1.0, 0.0), (-50.0, 30.0, 20.0), 0.8, + ), Consumable("PotionMed"): VoxTrans( "voxel.object.potion_red", (0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.7, @@ -2448,6 +2452,10 @@ "voxel.sprite.rocks.rock-0", (0.0, 0.0, 0.0), (-50.0, 40.0, 20.0), 0.8, ), + Ingredient("Cactus"): VoxTrans( + "voxel.sprite.cacti.flat_cactus_med", + (0.0, 0.0, 0.0), (-50.0, 40.0, 20.0), 0.9, + ), Ingredient("Seashells"): VoxTrans( "voxel.sprite.seashells.shell-0", (0.0, 0.0, 0.0), (-50.0, 40.0, 20.0), 0.8, diff --git a/assets/voxygen/voxel/object/cactus_drink.vox b/assets/voxygen/voxel/object/cactus_drink.vox new file mode 100644 index 0000000000..bb9815be42 --- /dev/null +++ b/assets/voxygen/voxel/object/cactus_drink.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b36bfc8f2a1eecd3563e4686efa4834a8ae891ce8ad2013571ad8ee6f204a4b +size 2656 diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index 64583dfa60..d246a16039 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -851,6 +851,9 @@ impl Item { SpriteKind::Pyrebloom => "common.items.flowers.pyrebloom", SpriteKind::WildFlax => "common.items.flowers.wild_flax", SpriteKind::Seashells => "common.items.crafting_ing.seashells", + SpriteKind::RoundCactus => "common.items.crafting_ing.cactus", + SpriteKind::ShortFlatCactus => "common.items.crafting_ing.cactus", + SpriteKind::MedFlatCactus => "common.items.crafting_ing.cactus", // Containers // IMPORTANT: Add any new container to `SpriteKind::is_container` container diff --git a/common/src/terrain/sprite.rs b/common/src/terrain/sprite.rs index d859ac0495..e35a99035f 100644 --- a/common/src/terrain/sprite.rs +++ b/common/src/terrain/sprite.rs @@ -298,6 +298,9 @@ impl SpriteKind { SpriteKind::Moonbell => true, SpriteKind::Pyrebloom => true, SpriteKind::WildFlax => true, + SpriteKind::RoundCactus => true, + SpriteKind::ShortFlatCactus => true, + SpriteKind::MedFlatCactus => true, _ => false, } } From 2e305744fec42dc7367bc67f3e27ad8c5e13c373 Mon Sep 17 00:00:00 2001 From: "K. Kisa" Date: Sun, 13 Jun 2021 13:08:52 +0000 Subject: [PATCH 038/180] =?UTF-8?q?=D0=9D=D0=B5=20=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D1=8B=D0=B9=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B2=D0=BE=D0=B4=20?= =?UTF-8?q?=D0=BD=D0=B0=D0=B2=D1=8B=D0=BA=D0=BE=D0=B2,=20=D0=B8=D1=81?= =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D0=B3=D0=B8=D0=BD=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B=D0=B5=20?= =?UTF-8?q?=D0=BE=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B8=20?= =?UTF-8?q?=D0=BD=D0=B0=D0=B7=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/voxygen/i18n/ru_RU/skills.ron | 246 +++++++++++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 assets/voxygen/i18n/ru_RU/skills.ron diff --git a/assets/voxygen/i18n/ru_RU/skills.ron b/assets/voxygen/i18n/ru_RU/skills.ron new file mode 100644 index 0000000000..5b898545cf --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/skills.ron @@ -0,0 +1,246 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + "hud.rank_up": "Новый скиллпоинт", + "hud.skill.sp_available": "{number} SP доступны", + "hud.skill.not_unlocked": "Еще не разблокирован", + "hud.skill.req_sp": "\n\nНеобходимо {number} SP", + // Skills + // General + "hud.skill.inc_health_title": "Повышение здоровья", + "hud.skill.inc_health": "Увеличивает максимальное здоровье на 5{SP}", + "hud.skill.inc_stam_title": "Повышение Выносливости", + "hud.skill.inc_stam": "Увеличивает максимальную выносливость на 5{SP}", + "hud.skill.unlck_sword_title": "Разблокировать меч", + "hud.skill.unlck_sword": "Разблокировать древо навыков владения мечом{SP}", + "hud.skill.unlck_axe_title": "Разблокировать топор", + "hud.skill.unlck_axe": "Разблокировать древо навыков владения топором{SP}", + "hud.skill.unlck_hammer_title": "Разблокировать молот", + "hud.skill.unlck_hammer": "Разблокировать древо навыков владения молотом{SP}", + "hud.skill.unlck_bow_title": "Разблокировать лук", + "hud.skill.unlck_bow": "Разблокировать древо навыков владения луком{SP}", + "hud.skill.unlck_staff_title": "Разблокировать посох", + "hud.skill.unlck_staff": "Разблокировать древо навыков владения посохом{SP}", + "hud.skill.unlck_sceptre_title": "Разблокировать скипетр", + "hud.skill.unlck_sceptre": "Разблокировать древо навыков владения скипетром{SP}", + "hud.skill.dodge_title": "Перекат", + "hud.skill.dodge": "Перекат активируется средним щелчком мыши. Во время переката вы игнорируете урон ближнего боя.", + "hud.skill.roll_stamina_title": "Стоимость активации переката", + "hud.skill.roll_stamina": "Перекат использует на 10% меньше выносливости{SP}", + "hud.skill.roll_speed_title": "Скорость переката", + "hud.skill.roll_speed": "Перекат на 10% быстрее{SP}", + "hud.skill.roll_dur_title": "Продолжительность переката", + "hud.skill.roll_dur": "Перекат на 10% дольше{SP}", + "hud.skill.climbing_title": "Скалолазание", + "hud.skill.climbing": "Высота прыжка на вершине подъема", + "hud.skill.climbing_cost_title": "Затраты выносливости на подъем", + "hud.skill.climbing_cost": "Скалолазание использует на 20% меньше выносливости{SP}", + "hud.skill.climbing_speed_title": "Скорость подъема", + "hud.skill.climbing_speed": "Вы поднимаетесь на 20% быстрее{SP}", + "hud.skill.swim_title": "Плавание", + "hud.skill.swim": "Перемещение в воде", + "hud.skill.swim_speed_title": "Скорость плавания", + "hud.skill.swim_speed": "Плавание на 40% быстрее{SP}", + // Sceptre + "hud.skill.sc_lifesteal_title": "Луч вампиризма", + "hud.skill.sc_lifesteal": "Высосите жизнь из ваших врагов", + "hud.skill.sc_lifesteal_damage_title": "Урон", + "hud.skill.sc_lifesteal_damage": "Наносит на 20% больше урона{SP}", + "hud.skill.sc_lifesteal_range_title": "Дистанция", + "hud.skill.sc_lifesteal_range": "Ваш луч на 20% дальше{SP}", + "hud.skill.sc_lifesteal_lifesteal_title": "Вампиризм", + "hud.skill.sc_lifesteal_lifesteal": "Преобразуйте дополнительные 15% урона в здоровье{SP}", + "hud.skill.sc_lifesteal_regen_title": "Восстановление выносливости", + "hud.skill.sc_lifesteal_regen": "Востановите свою выносливость на 20%{SP}", + "hud.skill.sc_heal_title": "Исцеляющий луч", + "hud.skill.sc_heal": "Исцеляйте своих союзников, используя здоровье своих врагов", + "hud.skill.sc_heal_heal_title": "Лечение", + "hud.skill.sc_heal_heal": "Увеличивает эффективность исцеления союзников на 20%{SP}", + "hud.skill.sc_heal_cost_title": "Затраты выносливости", + "hud.skill.sc_heal_cost": "Исцеление требует на 20% меньше выносливости{SP}", + "hud.skill.sc_heal_range_title": "Дистанция", + "hud.skill.sc_heal_range": "Ваш луч на 20% дальше{SP}", + "hud.skill.sc_wardaura_unlock_title": "Разблокировать ауру защиты", + "hud.skill.sc_wardaura_unlock": "Позволяет вам повысить защиту своих союзников (и себя){SP}", + "hud.skill.sc_wardaura_strength_title": "Сила", + "hud.skill.sc_wardaura_strength": "Сила вашей защиты увеличивается на 15%{SP}", + "hud.skill.sc_wardaura_duration_title": "Продолжительность", + "hud.skill.sc_wardaura_duration": "Эффект длится на 20% дольше{SP}", + "hud.skill.sc_wardaura_range_title": "Радиус", + "hud.skill.sc_wardaura_range": "Радиус на 25% больше{SP}", + "hud.skill.sc_wardaura_cost_title": "Затраты выносливости", + "hud.skill.sc_wardaura_cost": "Создание ауры потребует на 15% меньше выносливости{SP}", + // Staff + "hud.skill.st_shockwave_range_title" : "Диапазон волны", + "hud.skill.st_shockwave_range" : "Радиус больше на 20%{SP}", + "hud.skill.st_shockwave_cost_title" : "Затраты выносливости", + "hud.skill.st_shockwave_cost" : "Затраты выносливости ниже на 20%{SP}", + "hud.skill.st_shockwave_knockback_title" : "Отбрасывание волны", + "hud.skill.st_shockwave_knockback" : "Увеличивает потенциал отбрасывания на 30%{SP}", + "hud.skill.st_shockwave_damage_title" : "Урон кольца огня", + "hud.skill.st_shockwave_damage" : "Увеличивает наносимый урон на 30%{SP}", + "hud.skill.st_shockwave_unlock_title" : "Разблокировать кольцо огня", + "hud.skill.st_shockwave_unlock" : "Открывает возможность отбрасывать врагов с помощью огня{SP}", + "hud.skill.st_flamethrower_title" : "Поток пламени", + "hud.skill.st_flamethrower" : "Плотный поток пламени из вашего посоха", + "hud.skill.st_flame_velocity_title" : "Скорость пламени", + "hud.skill.st_flame_velocity" : "Скорость создания огня выше на 25%{SP}", + "hud.skill.st_flamethrower_range_title" : "Дальность действия потока пламени", + "hud.skill.st_flamethrower_range" : "Дальность действия на 25% выше{SP}", + "hud.skill.st_energy_drain_title" : "Экономия выносливости", + "hud.skill.st_energy_drain" : "Уменьшает скорость траты выносливости на 20%{SP}", + "hud.skill.st_flamethrower_damage_title" : "Урон потока пламени", + "hud.skill.st_flamethrower_damage" : "Урон больше на 30%{SP}", + "hud.skill.st_explosion_radius_title" : "Радиус взрыва", + "hud.skill.st_explosion_radius" : "Радиус взрыва больше на 15%{SP}", + "hud.skill.st_stamina_regen_title" : "Востановление выносливости", + "hud.skill.st_stamina_regen" : "Увеличивает прирост выносливости на 20%{SP}", + "hud.skill.st_fireball_title" : "Огненый шар", + "hud.skill.st_fireball" : "Стреляет огненным шаром, который взрывается при ударе", + "hud.skill.st_damage_title" : "Урон", + "hud.skill.st_damage" : "Увеличивает урон на 20%{SP}", + // Bow + "hud.skill.bow_projectile_speed_title" : "Скорость снаряда", + "hud.skill.bow_projectile_speed" : "Стрелы летят дальше и быстрее на 30%{SP}", + "hud.skill.bow_charged_title" : "Прицельный выстрел", + "hud.skill.bow_charged" : "Более сильный выстрел за счет натяжения титевы", + "hud.skill.bow_charged_damage_title" : "Наносимый урон", + "hud.skill.bow_charged_damage" : "Урон увеличен на 20%{SP}", + "hud.skill.bow_charged_energy_regen_title" : "Востановление выносливости", + "hud.skill.bow_charged_energy_regen" : "Повышает востановление выносливости на 20%{SP}", + "hud.skill.bow_charged_knockback_title" : "Отбрасывание выстрела", + "hud.skill.bow_charged_knockback" : "Отбрасывание врагов больше на 20%{SP}", + "hud.skill.bow_charged_speed_title" : "Скорость атаки", + "hud.skill.bow_charged_speed" : "Увеличивает скорость атаки на 10%{SP}", + "hud.skill.bow_charged_move_title" : "Скорость перемещения", + "hud.skill.bow_charged_move" : "Увеличивает скорость перемешивания при прицеливании на 10%{SP}", + "hud.skill.bow_repeater_title" : "Repeater", + "hud.skill.bow_repeater" : "Shoots faster the longer you fire for", + "hud.skill.bow_repeater_damage_title" : "Repeater Урон", + "hud.skill.bow_repeater_damage" : "Увеличивает наносимый урон на 20%{SP}", + "hud.skill.bow_repeater_cost_title" : "Repeater расход выносливости", + "hud.skill.bow_repeater_cost" : "Снижает затраты выносливости на 20%{SP}", + "hud.skill.bow_repeater_speed_title" : "Repeater скорость", + "hud.skill.bow_repeater_speed" : "Увеличивает скорость стрельбы стрелами на 20%{SP}", + "hud.skill.bow_shotgun_unlock_title" : "Разблокировать Shotgun", + "hud.skill.bow_shotgun_unlock" : "Разблокирует возможность стрелять несколькими стрелами одновременно{SP}", + "hud.skill.bow_shotgun_damage_title" : "Shotgun Урон", + "hud.skill.bow_shotgun_damage" : "Увеличивает наносимый урон на 20%{SP}", + "hud.skill.bow_shotgun_cost_title" : "Shotgun затраты выносливости", + "hud.skill.bow_shotgun_cost" : "Снижает расход выносливости на 20%{SP}", + "hud.skill.bow_shotgun_arrow_count_title" : "Shotgun стрелы", + "hud.skill.bow_shotgun_arrow_count" : "Увеличивает количество стрел в серии ударов на 1{SP}", + "hud.skill.bow_shotgun_spread_title" : "Shotgun разброс", + "hud.skill.bow_shotgun_spread" : "Уменьшает разброс стрел на 20%{SP}", + // Hammer + "hud.skill.hmr_leap_radius_title" : "Радиус", + "hud.skill.hmr_leap_radius" : "Увеличивает радиус атаки при ударе об землю на 1 метр{SP}", + "hud.skill.hmr_leap_distance_title" : "Высота прыжка", + "hud.skill.hmr_leap_distance" : "Увеличивает расстояние прыжка на 25%{SP}", + "hud.skill.hmr_leap_cost_title" : "Затраты выносливости", + "hud.skill.hmr_leap_cost" : "Снижает затраты выносливости на 25%{SP}", + "hud.skill.hmr_leap_knockback_title" : "Leap Knockback", + "hud.skill.hmr_leap_knockback" : "Увеличивает отбрасивание от удара на 50%{SP}", + "hud.skill.hmr_leap_damage_title" : "Урон", + "hud.skill.hmr_leap_damage" : "Увеличивает урон на 40%{SP}", + "hud.skill.hmr_unlock_leap_title" : "Разблокировать Leap", + "hud.skill.hmr_unlock_leap" : "Разблокировать a leap{SP}", + "hud.skill.hmr_charged_melee_title" : "Силовая атака", + "hud.skill.hmr_charged_melee" : "ПКМ совершает оглушающий удар. Зажатие ПКМ накапливает силу и оглушает врага отбрасывая его", + "hud.skill.hmr_charged_rate_title" : "Скорость атаки", + "hud.skill.hmr_charged_rate" : "Увеличивает скорость с которой вы накапливаете силу удара на 25%{SP}", + "hud.skill.hmr_charged_melee_nrg_drain_title" : "Расход выносливости", + "hud.skill.hmr_charged_melee_nrg_drain" : "Уменьшает расход выносливости на 25%{SP}", + "hud.skill.hmr_charged_melee_damage_title" : "Урон атаки", + "hud.skill.hmr_charged_melee_damage" : "Увеличивает урон на 25%{SP}", + "hud.skill.hmr_charged_melee_knockback_title" : "Оглушение", + "hud.skill.hmr_charged_melee_knockback" : "Увеличивает отбрасывание 50%{SP}", + "hud.skill.hmr_single_strike_title" : "Одиночный удар", + "hud.skill.hmr_single_strike" : "Обычный удар", + "hud.skill.hmr_single_strike_regen_title" : "Востановление выносливости", + "hud.skill.hmr_single_strike_regen" : "Увеличивает прирост выносливости с каждым последующим ударом{SP}", + "hud.skill.hmr_single_strike_speed_title" : "Скорость атаки", + "hud.skill.hmr_single_strike_speed" : "Увеличивает скорость атаки с каждым последующим ударом{SP}", + "hud.skill.hmr_single_strike_damage_title" : "Урон", + "hud.skill.hmr_single_strike_damage" : "Увеличивает урон с каждым последующим ударом{SP}", + "hud.skill.hmr_single_strike_knockback_title" : "Отбрасывание", + "hud.skill.hmr_single_strike_knockback" : "Увеличьте потенциал отбрасывания на 50%{SP}", + "hud.skill." : "", + // Sword + "hud.skill.sw_trip_str_title": "Тройное вращение", + "hud.skill.sw_trip_str": "Вращение на 3 оборота", + "hud.skill.sw_trip_str_combo_title": "Тройной удар Комбо", + "hud.skill.sw_trip_str_combo": "Разблокирует комбо при тройном вращении{SP}", + "hud.skill.sw_trip_str_dmg_title": "Урон тройного вращения", + "hud.skill.sw_trip_str_dmg": "Увеличивает урон, наносимый каждым последующим ударом{SP}", + "hud.skill.sw_trip_str_sp_title": "Скорость атаки", + "hud.skill.sw_trip_str_sp": "Увеличивает скорость атаки, получаемую при каждом последующем ударе{SP}", + "hud.skill.sw_trip_str_reg_title": "Triple Strike Regen", + "hud.skill.sw_trip_str_reg": "Increases stamina gain on each successive strike{SP}", + "hud.skill.sw_dash_title": "Dash", + "hud.skill.sw_dash": "Pin through your enemies", + "hud.skill.sw_dash_dmg_title": "Dash Damage", + "hud.skill.sw_dash_dmg": "Increases initial damage of the dash by 20%{SP}", + "hud.skill.sw_dash_drain_title": "Dash Drain", + "hud.skill.sw_dash_drain": "Decreases the rate energy is drained while dashing by 25%{SP}", + "hud.skill.sw_dash_cost_title": "Dash Cost", + "hud.skill.sw_dash_cost": "Decreases the initial cost of the dash by 25%{SP}", + "hud.skill.sw_dash_speed_title": "Dash Speed", + "hud.skill.sw_dash_speed": "Increases how fast you go while dashing by 30%{SP}", + "hud.skill.sw_dash_charge_through_title": "Charge Through", + "hud.skill.sw_dash_charge_through": "Allows you to charge through the first enemies you hit{SP}", + "hud.skill.sw_dash_scale_title": "Dash Scaling Damage", + "hud.skill.sw_dash_scale": "Increases the damage scaling from the dash by 20%{SP}", + "hud.skill.sw_spin_title": "Spin Unlock", + "hud.skill.sw_spin": "Unlocks the sword spin{SP}", + "hud.skill.sw_spin_dmg_title": "Spin Damage", + "hud.skill.sw_spin_dmg": "Increases the damage done by 40%{SP}", + "hud.skill.sw_spin_spd_title": "Spin Speed", + "hud.skill.sw_spin_spd": "Increase the speed at which you spin by 25%{SP}", + "hud.skill.sw_spin_cost_title": "Spin Cost", + "hud.skill.sw_spin_cost": "Decreases the energy cost of each spin by 25%{SP}", + "hud.skill.sw_spin_spins_title": "Spin Spins", + "hud.skill.sw_spin_spins": "Increases the number of times you can spin{SP}", + "hud.skill.sw_interrupt_title": "Interrupting Attacks", + "hud.skill.sw_interrupt": "Allows you to immediately cancel an attack with another attack{SP}", + // Axe + "hud.skill.axe_double_strike_title": "Double Strike", + "hud.skill.axe_double_strike": "Chop down those villains", + "hud.skill.axe_double_strike_combo_title": "Double Strike Combo", + "hud.skill.axe_double_strike_combo": "Unlocks a second strike{SP}", + "hud.skill.axe_double_strike_damage_title": "Double Strike Damage", + "hud.skill.axe_double_strike_damage": "Increases the damage dealt in each successive strike{SP}", + "hud.skill.axe_double_strike_speed_title": "Double Strike Speed", + "hud.skill.axe_double_strike_speed": "Increases the attack speed with each successive strike{SP}", + "hud.skill.axe_double_strike_regen_title": "Double Strike Regen", + "hud.skill.axe_double_strike_regen": "Increases stamina gain with each successive strike{SP}", + "hud.skill.axe_spin_title": "Axe Spin", + "hud.skill.axe_spin": "You spin it right round ...", + "hud.skill.axe_infinite_axe_spin_title": "Infinite Axe Spin", + "hud.skill.axe_infinite_axe_spin": "Spin for as long as you have energy{SP}", + "hud.skill.axe_spin_damage_title": "Spin Damage", + "hud.skill.axe_spin_damage": "Increases the damage each spin does by 30%{SP}", + "hud.skill.axe_spin_helicopter_title": "Spin Helicopter", + "hud.skill.axe_spin_helicopter": "You fall a little slower while spinning{SP}", + "hud.skill.axe_spin_speed_title": "Spin Speed", + "hud.skill.axe_spin_speed": "Increases your spin speed by 25%{SP}", + "hud.skill.axe_spin_cost_title": "Spin Cost", + "hud.skill.axe_spin_cost": "Decreases stamina cost of spinning by 25%{SP}", + "hud.skill.axe_unlock_leap_title": "Unlock Leap", + "hud.skill.axe_unlock_leap": "Unlocks a leap spin{SP}", + "hud.skill.axe_leap_damage_title": "Leap Damage", + "hud.skill.axe_leap_damage": "Increases damage of leap by 35%{SP}", + "hud.skill.axe_leap_knockback_title": "Leap Knockback", + "hud.skill.axe_leap_knockback": "Increases knockback from leap by 40%{SP}", + "hud.skill.axe_leap_cost_title": "Leap Cost", + "hud.skill.axe_leap_cost": "Decreases cost of leap by 25%{SP}", + "hud.skill.axe_leap_distance_title": "Leap Distance", + "hud.skill.axe_leap_distance": "Increases distance of leap by 20%{SP}", + }, + + + vector_map: { + } +) From acb05892779dcb44be1ce4e337a6fed5300c8e5c Mon Sep 17 00:00:00 2001 From: "K. Kisa" Date: Sun, 13 Jun 2021 13:24:57 +0000 Subject: [PATCH 039/180] =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D0=BD=D1=8B=D0=B9=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=B2=D0=BE=D0=B4=20=D0=BD=D0=B0=20?= =?UTF-8?q?=D1=80=D1=83=D1=81=D1=81=D0=BA=D0=B8=D0=B9=20=D1=8F=D0=B7=D1=8B?= =?UTF-8?q?=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/voxygen/i18n/ru_RU/main.ron | 69 ++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 assets/voxygen/i18n/ru_RU/main.ron diff --git a/assets/voxygen/i18n/ru_RU/main.ron b/assets/voxygen/i18n/ru_RU/main.ron new file mode 100644 index 0000000000..26a42b40d1 --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/main.ron @@ -0,0 +1,69 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for RU +( + string_map: { + /// Start Main screen section + "main.username": "Никнейм", + "main.server": "Сервер", + "main.password": "Пароль", + "main.connecting": "Соединение", + "main.creating_world": "Создание мира", + "main.tip": "Совет:", + + // Welcome notice that appears the first time Veloren is started + "main.notice": r#"Добро пожаловать в альфа версию Veloren! + +Прежде чем погрузиться в веселье, пожалуйста, имейте в виду несколько вещей: + +- Это очень ранняя альфа. Ожидайте ошибок, крайне незавершенного геймплея, неполированной механики и отсутствующих функций. + +- Если у вас есть конструктивные отзывы или сообщения об ошибках, вы можете связаться с нами через Reddit, GitLab или наш сервер разногласий сообщества. + +- Veloren лицензирован по лицензии GPL 3 с открытым исходным кодом. Это означает, что вы можете играть, изменять и распространять игру +по своему усмотрению (при условии, что производная работа также находится в рамках GPL 3). + +- Велорен-это некоммерческий общественный проект, и все, кто работает над ним, являются добровольцами. +Если вам нравится то, что вы видите, вы можете присоединиться к командам разработчиков или художников! + +Спасибо, что нашли время прочитать это уведомление, мы надеемся, что вам понравится игра! + +~ Разработчики Велорена"#, + + // Login process description + "main.login_process": r#"Информация о процессе входа в систему: + +Обратите внимание, что вам нужна учетная +запись для игры на серверах с поддержкой аутентификации. + +Вы можете создать учетную запись по адресу +https://veloren.net/account/."#, + "main.login.server_not_found": "Сервер не найден", + "main.login.authentication_error": "Ошибка аутентификации на сервере", + "main.login.internal_error": "Внутренняя ошибка на клиенте (скорее всего, персонаж игрока был удален)", + "main.login.failed_auth_server_url_invalid": "Не удалось подключиться к серверу аутентификации", + "main.login.insecure_auth_scheme": "Схема аутентификации HTTP не поддерживается. Это небезопасно! В целях разработки HTTP разрешен для 'локального хоста' или отладочных сборок", + "main.login.server_full": "Сервер заполнен", + "main.login.untrusted_auth_server": "Сервер аутентификации не является доверенным", + "main.login.outdated_client_or_server": "ServerWentMad: Возможно, версии несовместимы, проверьте наличие обновлений.", + "main.login.timeout": "Тайм-аут: Сервер не ответил вовремя. (Перегрузка или проблемы с сетью).", + "main.login.server_shut_down": "Сервер выключен", + "main.login.network_error": "Ошибка сети", + "main.login.network_wrong_version": "Несоответствие версии сервера и клиента, пожалуйста, обновите свой игровой клиент.", + "main.login.failed_sending_request": "Не удалось выполнить запрос на сервер аутентификации", + "main.login.invalid_character": "Выбранный символ недопустим", + "main.login.client_crashed": "Клиент разбился", + "main.login.not_on_whitelist": "Вам нужна запись в Белом списке от администратора, чтобы присоединиться", + "main.login.banned": "Вы были забанены по следующей причине", + "main.login.kicked": "Вас выгнали по следующей причине", + "main.login.select_language": "Выберите язык", + "main.login.client_version": "Версия клиента", + "main.login.server_version": "Server Version", + "main.servers.select_server": "Версия сервера", + /// End Main screen section + }, + + + vector_map: { + } +) From 883297d4cc00f502fbca516362f487fc2ba460a7 Mon Sep 17 00:00:00 2001 From: "K. Kisa" Date: Sun, 13 Jun 2021 13:30:42 +0000 Subject: [PATCH 040/180] Upload New File --- assets/voxygen/i18n/ru_RU/esc_menu.ron | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 assets/voxygen/i18n/ru_RU/esc_menu.ron diff --git a/assets/voxygen/i18n/ru_RU/esc_menu.ron b/assets/voxygen/i18n/ru_RU/esc_menu.ron new file mode 100644 index 0000000000..48066287f6 --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/esc_menu.ron @@ -0,0 +1,13 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for RUS +( + string_map: { + "esc_menu.logout": "Выйти в меню", + "esc_menu.quit_game": "Выйти из игры", + }, + + + vector_map: { + } +) From 5f2b44002e863332ebb3b896e77dd13442f612f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Mon, 5 Apr 2021 15:50:44 +0200 Subject: [PATCH 041/180] make test less flanky, try to avoid absolute comparisions and compare jobs relative. --- common/src/slowjob.rs | 75 +++++++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/common/src/slowjob.rs b/common/src/slowjob.rs index fac0c98846..16186ddbd2 100644 --- a/common/src/slowjob.rs +++ b/common/src/slowjob.rs @@ -73,7 +73,7 @@ impl InternalSlowJobPool { fn maintain(&self) { let jobs_available = - self.global_limit as i64 - self.global_running_jobs.load(Ordering::Relaxed); + self.global_limit as i64 - self.global_running_jobs.load(Ordering::SeqCst); if jobs_available < 0 { tracing::warn!(?jobs_available, "Some math is wrong in slowjob code"); } @@ -104,7 +104,7 @@ impl InternalSlowJobPool { let c = lock.get(&name).unwrap(); ( name, - c.spawned_total.load(Ordering::Relaxed) / c.max_local, + c.spawned_total.load(Ordering::SeqCst) / c.max_local, c.max_local, ) }) @@ -122,7 +122,7 @@ impl InternalSlowJobPool { }; if let Some(queue) = map.remove(&firstkey) { - if queue.local_running_jobs.load(Ordering::Relaxed) < *max as i64 { + if queue.local_running_jobs.load(Ordering::SeqCst) < *max as i64 { self.fire(queue); } else { map.insert(firstkey, queue); @@ -134,9 +134,9 @@ impl InternalSlowJobPool { } fn fire(&self, queue: Queue) { - queue.spawned_total.fetch_add(1, Ordering::Relaxed); - queue.local_running_jobs.fetch_add(1, Ordering::Relaxed); - self.global_running_jobs.fetch_add(1, Ordering::Relaxed); + queue.spawned_total.fetch_add(1, Ordering::SeqCst); + queue.local_running_jobs.fetch_add(1, Ordering::SeqCst); + self.global_running_jobs.fetch_add(1, Ordering::SeqCst); self.threadpool.spawn(queue.task); } } @@ -167,7 +167,7 @@ impl SlowJobPool { where F: FnOnce() + Send + Sync + 'static, { - let id = self.internal.next_id.fetch_add(1, Ordering::Relaxed); + let id = self.internal.next_id.fetch_add(1, Ordering::SeqCst); self.internal .queue .write() @@ -208,8 +208,8 @@ impl SlowJobPool { task: Box::new(move || { common_base::prof_span!(_guard, &_name_clones); f(); - local_running_jobs_clone.fetch_sub(1, Ordering::Relaxed); - global_running_jobs_clone.fetch_sub(1, Ordering::Relaxed); + local_running_jobs_clone.fetch_sub(1, Ordering::SeqCst); + global_running_jobs_clone.fetch_sub(1, Ordering::SeqCst); // directly maintain the next task afterwards internal.maintain(); }), @@ -291,13 +291,22 @@ mod tests { let f6 = measure(f6, start); let f7 = measure(f7, start); assert_eq!(done.load(Ordering::Relaxed), 7); - assert!(f1 < 500); - assert!(f2 < 500); - assert!(f3 < 500); - assert!(f4 < 1000); - assert!(f5 < 1000); - assert!(f6 < 1000); - assert!(f7 < 1500); + // Just test relative times, not absolute + assert!(f1 < f4); + assert!(f1 < f5); + assert!(f1 < f6); + assert!(f1 < f7); + assert!(f2 < f4); + assert!(f2 < f5); + assert!(f2 < f6); + assert!(f2 < f7); + assert!(f3 < f4); + assert!(f3 < f5); + assert!(f3 < f6); + assert!(f3 < f7); + assert!(f4 < f7); + assert!(f5 < f7); + assert!(f6 < f7); } #[test] @@ -338,13 +347,21 @@ mod tests { let f6 = measure(f6, start); let f7 = measure(f7, start); assert_eq!(done.load(Ordering::Relaxed), 7); - assert!(f1 < 500); - assert!(f2 < 500); - assert!(f3 < 500); - assert!(f4 < 1000); - assert!(f5 < 1000); - assert!(f6 < 1000); - assert!(f7 < 1500); + assert!(f1 < f4); + assert!(f1 < f5); + assert!(f1 < f6); + assert!(f1 < f7); + assert!(f2 < f4); + assert!(f2 < f5); + assert!(f2 < f6); + assert!(f2 < f7); + assert!(f3 < f4); + assert!(f3 < f5); + assert!(f3 < f6); + assert!(f3 < f7); + assert!(f4 < f7); + assert!(f5 < f7); + assert!(f6 < f7); } #[test] @@ -356,12 +373,12 @@ mod tests { let pool = SlowJobPool::new(2, Arc::new(threadpool)); pool.configure("FOO", |n| n); pool.configure("BAR", |n| n / 2); - let start = Instant::now(); let f1 = Arc::new(Mutex::new(None)); let f2 = Arc::new(Mutex::new(None)); let b1 = Arc::new(Mutex::new(None)); let b2 = Arc::new(Mutex::new(None)); let done = Arc::new(AtomicI64::new(0)); + let start = Instant::now(); pool.spawn("FOO", mock_fn("foo1", &f1, &done)); pool.spawn("FOO", mock_fn("foo2", &f2, &done)); std::thread::sleep(Duration::from_millis(1000)); @@ -380,11 +397,13 @@ mod tests { // [B1] // [B2] assert_eq!(done.load(Ordering::Relaxed), 4); - assert!(f1 < 500); - assert!(f2 < 500); + assert!(f1 < 600); + assert!(f2 < 600); println!("b1 {}", b1); println!("b2 {}", b2); - assert!((1000..1500).contains(&b1)); - assert!((1500..2000).contains(&b2)); + // would be to flanky: + //assert!((1000..1500).contains(&b1)); + //assert!((1500..2000).contains(&b2)); + assert!(b1 < b2); } } From e2a91289767c103035108fbda7a4612e3099b04a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Mon, 12 Apr 2021 12:24:24 +0200 Subject: [PATCH 042/180] redo slowjobs in order to have a `try_run` fn --- common/src/slowjob.rs | 831 ++++++++++++++++++++++++++---------------- 1 file changed, 524 insertions(+), 307 deletions(-) diff --git a/common/src/slowjob.rs b/common/src/slowjob.rs index 16186ddbd2..5175e4b069 100644 --- a/common/src/slowjob.rs +++ b/common/src/slowjob.rs @@ -1,9 +1,14 @@ use hashbrown::HashMap; use rayon::ThreadPool; -use std::sync::{ - atomic::{AtomicI64, AtomicU64, Ordering}, - Arc, RwLock, +use std::{ + collections::VecDeque, + sync::{Arc, Mutex}, + time::{Duration, Instant} }; +use tracing::{error, warn}; + +const LAST_JOBS_METRICS: usize = 32; +const FAIR_TIME_INTERVAL: Duration = Duration::from_millis(1000); /// Provides a Wrapper around rayon threadpool to execute slow-jobs. /// slow means, the job doesn't need to not complete within the same tick. @@ -11,11 +16,26 @@ use std::sync::{ /// Jobs run here, will reduce the ammount of threads rayon can use during the /// main tick. /// +/// ## Configuration /// This Pool allows you to configure certain names of jobs and assign them a /// maximum number of threads # Example /// Your system has 16 cores, you assign 12 cores for slow-jobs. /// Then you can configure all jobs with the name `CHUNK_GENERATOR` to spawn on -/// max 50% (6 = cores) ```rust +/// max 50% (6 = cores) +/// +/// ## Spawn Order +/// - At least 1 job of a configuration is allowed to run if global limit isn't +/// hit. +/// - remaining capacities are spread in relation to their limit. e.g. a +/// configuration with double the limit will be sheduled to spawn double the +/// tasks. +/// +/// ## States +/// - queued +/// - spawned +/// - started +/// - finished +/// ``` /// # use veloren_common::slowjob::SlowJobPool; /// # use std::sync::Arc; /// @@ -25,126 +45,248 @@ use std::sync::{ /// .unwrap(); /// let pool = SlowJobPool::new(3, Arc::new(threadpool)); /// pool.configure("CHUNK_GENERATOR", |n| n / 2); -/// pool.spawn("CHUNK_GENERATOR", move || println("this is a job")); +/// pool.spawn("CHUNK_GENERATOR", move || println!("this is a job")); /// ``` #[derive(Clone)] pub struct SlowJobPool { - internal: Arc, + internal: Arc>, } +#[derive(Debug)] pub struct SlowJob { name: String, id: u64, } struct InternalSlowJobPool { - next_id: Arc, - queue: RwLock>>, - running_jobs: RwLock>>, - configs: RwLock>, - global_running_jobs: Arc, + next_id: u64, + queue: HashMap>, + configs: HashMap, + global_spawned_and_running: u64, global_limit: u64, threadpool: Arc, + internal: Option>>, } +#[derive(Debug)] struct Config { - max_local: u64, - spawned_total: Arc, + local_limit: u64, + local_spawned_and_running: u64, + /// hold the start and time of the last LAST_JOBS_METRICS jobs + last_jobs: VecDeque<(std::time::Instant, Option)>, } struct Queue { + id: u64, + name: String, task: Box, - spawned_total: Arc, - local_running_jobs: Arc, +} + +impl Queue { + fn new(name: &str, id: u64, internal: &Arc>, f: F) -> Self + where + F: FnOnce() + Send + Sync + 'static, + { + let internal = Arc::clone(&internal); + let name_cloned = name.to_owned(); + Self { + id, + name: name.to_owned(), + task: Box::new(move || { + common_base::prof_span!(_guard, &name_cloned); + f(); + // directly maintain the next task afterwards + { + let mut lock = internal.lock().expect("slowjob lock poisoned"); + lock.finish(&name_cloned); + lock.spawn_queued(); + } + }), + } + } } impl InternalSlowJobPool { - pub fn new(global_limit: u64, threadpool: Arc) -> Self { - Self { - next_id: Arc::new(AtomicU64::new(0)), - queue: RwLock::new(HashMap::new()), - running_jobs: RwLock::new(HashMap::new()), - configs: RwLock::new(HashMap::new()), - global_running_jobs: Arc::new(AtomicI64::new(0)), + pub fn new(global_limit: u64, threadpool: Arc) -> Arc> { + let link = Arc::new(Mutex::new(Self { + next_id: 0, + queue: HashMap::new(), + configs: HashMap::new(), + global_spawned_and_running: 0, global_limit: global_limit.max(1), threadpool, - } + internal: None, + })); + + let link_clone = Arc::clone(&link); + link.lock() + .expect("poisoned on InternalSlowJobPool::new") + .internal = Some(link_clone); + link } - fn maintain(&self) { - let jobs_available = - self.global_limit as i64 - self.global_running_jobs.load(Ordering::SeqCst); - if jobs_available < 0 { - tracing::warn!(?jobs_available, "Some math is wrong in slowjob code"); - } - if jobs_available <= 0 { - // we run at limit, can't spawn - return; - } - let possible = { - let lock = self.queue.read().unwrap(); - lock.iter() - .map(|(name, queues)| { - if !queues.is_empty() { - Some(name.clone()) - } else { - None - } - }) - .flatten() - .collect::>() - }; - - let mut possible_total = { - let mut possible = possible; - let lock = self.configs.read().unwrap(); - possible - .drain(..) - .map(|name| { - let c = lock.get(&name).unwrap(); - ( - name, - c.spawned_total.load(Ordering::SeqCst) / c.max_local, - c.max_local, - ) - }) - .collect::>() - }; - possible_total.sort_by_key(|(_, i, _)| *i); - - let mut lock = self.queue.write().unwrap(); - for i in 0..jobs_available as usize { - if let Some((name, _, max)) = possible_total.get(i) { - if let Some(map) = lock.get_mut(name) { - let firstkey = match map.keys().next() { - Some(k) => *k, - None => continue, - }; - - if let Some(queue) = map.remove(&firstkey) { - if queue.local_running_jobs.load(Ordering::SeqCst) < *max as i64 { - self.fire(queue); - } else { - map.insert(firstkey, queue); - } - } + /// returns order of configuration which are queued next + fn calc_queued_order( + &self, + mut queued: HashMap<&String, u64>, + mut limit: usize, + ) -> Vec { + let mut result = vec![]; + let spawned = self + .configs + .iter() + .map(|(n, c)| (n, c.local_spawned_and_running)) + .collect::>(); + let mut queried_caped = self + .configs + .iter() + .map(|(n, c)| { + ( + n, + queued + .get(&n) + .cloned() + .unwrap_or(0) + .min(c.local_limit - c.local_spawned_and_running), + ) + }) + .collect::>(); + // grab all configs that are queued and not running + for (&n, c) in queued.iter_mut() { + if *c > 0 && spawned.get(n).cloned().unwrap_or(0) == 0 { + result.push(n.clone()); + *c -= 1; + limit -= 1; + queried_caped.get_mut(&n).map(|v| *v -= 1); + if limit == 0 { + return result; } } } + //schedule rest + let total_limit = queried_caped.values().sum::() as f32; + if total_limit < f32::EPSILON { + return result; + } + let mut spawn_rates = queried_caped + .iter() + .map(|(&n, l)| (n, ((*l as f32 * limit as f32) / total_limit).min(*l as f32))) + .collect::>(); + while limit > 0 { + spawn_rates.sort_by(|(_, a), (_, b)| { + if b < a { + core::cmp::Ordering::Less + } else if (b - a).abs() < f32::EPSILON { + core::cmp::Ordering::Equal + } else { + core::cmp::Ordering::Greater + } + }); + match spawn_rates.first_mut() { + Some((n, r)) => { + if *r > f32::EPSILON { + result.push(n.clone()); + limit -= 1; + *r -= 1.0; + } else { + break; + } + }, + None => break, + } + } + result } - fn fire(&self, queue: Queue) { - queue.spawned_total.fetch_add(1, Ordering::SeqCst); - queue.local_running_jobs.fetch_add(1, Ordering::SeqCst); - self.global_running_jobs.fetch_add(1, Ordering::SeqCst); - self.threadpool.spawn(queue.task); + fn can_spawn(&self, name: &str) -> bool { + let queued = self + .queue + .iter() + .map(|(n, m)| (n, m.len() as u64)) + .collect::>(); + let mut to_be_queued = queued.clone(); + let name = name.to_owned(); + *to_be_queued.entry(&name).or_default() += 1; + let limit = (self.global_limit - self.global_spawned_and_running) as usize; + // calculate to_be_queued first + let to_be_queued_order = self.calc_queued_order(to_be_queued, limit); + let queued_order = self.calc_queued_order(queued, limit); + // if its queued one time more then its okay to spawn + let to_be_queued_cnt = to_be_queued_order + .into_iter() + .filter(|n| n == &name) + .count(); + let queued_cnt = queued_order.into_iter().filter(|n| n == &name).count(); + to_be_queued_cnt > queued_cnt + } + + pub fn spawn(&mut self, name: &str, f: F) -> SlowJob + where + F: FnOnce() + Send + Sync + 'static, + { + let id = self.next_id; + self.next_id += 1; + let queue = Queue::new(name, id, self.internal.as_ref().expect("internal empty"), f); + self.queue + .entry(name.to_string()) + .or_default() + .push_back(queue); + //spawn already queued + self.spawn_queued(); + SlowJob { + name: name.to_string(), + id, + } + } + + fn finish(&mut self, name: &str) { + self.global_spawned_and_running -= 1; + if let Some(c) = self.configs.get_mut(name) { + c.local_spawned_and_running -= 1; + c.last_jobs. + } else { + warn!(?name, "sync_maintain on a no longer existing config"); + } + } + + fn spawn_queued(&mut self) { + let queued = self + .queue + .iter() + .map(|(n, m)| (n, m.len() as u64)) + .collect::>(); + let limit = self.global_limit as usize; + let queued_order = self.calc_queued_order(queued, limit); + for name in queued_order.into_iter() { + match self.queue.get_mut(&name) { + Some(deque) => match deque.pop_front() { + Some(queue) => { + //fire + self.global_spawned_and_running += 1; + self.configs + .get_mut(&queue.name) + .expect("cannot fire a unconfigured job") + .local_spawned_and_running += 1; + self.threadpool.spawn(queue.task); + }, + None => error!( + "internal calculation is wrong, we extected a schedulable job to be \ + present in the queue" + ), + }, + None => error!( + "internal calculation is wrong, we marked a queue as schedulable which \ + doesn't exist" + ), + } + } } } impl SlowJobPool { pub fn new(global_limit: u64, threadpool: Arc) -> Self { Self { - internal: Arc::new(InternalSlowJobPool::new(global_limit, threadpool)), + internal: InternalSlowJobPool::new(global_limit, threadpool), } } @@ -154,256 +296,331 @@ impl SlowJobPool { where F: Fn(u64) -> u64, { + let mut lock = self.internal.lock().expect("lock poisoned while configure"); let cnf = Config { - max_local: f(self.internal.global_limit).max(1), - spawned_total: Arc::new(AtomicU64::new(0)), + local_limit: f(lock.global_limit).max(1), + local_spawned_and_running: 0, + last_jobs: VecDeque::with_capacity(LAST_JOBS_METRICS), }; - let mut lock = self.internal.configs.write().unwrap(); - lock.insert(name.to_string(), cnf); + lock.configs.insert(name.to_owned(), cnf); + } + + /// spawn a new slow job on a certain NAME IF it can run immediately + #[allow(clippy::result_unit_err)] + pub fn try_run(&self, name: &str, f: F) -> Result + where + F: FnOnce() + Send + Sync + 'static, + { + let mut lock = self.internal.lock().expect("lock poisoned while try_run"); + //spawn already queued + lock.spawn_queued(); + if lock.can_spawn(name) { + Ok(lock.spawn(name, f)) + } else { + Err(()) + } } - /// spawn a new slow job on a certain NAME pub fn spawn(&self, name: &str, f: F) -> SlowJob where F: FnOnce() + Send + Sync + 'static, { - let id = self.internal.next_id.fetch_add(1, Ordering::SeqCst); self.internal - .queue - .write() - .unwrap() - .entry(name.to_string()) - .or_default() - .insert(id, self.queue(name, f)); - self.maintain(); - SlowJob { - name: name.to_string(), - id, - } + .lock() + .expect("lock poisoned while spawn") + .spawn(name, f) } - fn queue(&self, name: &str, f: F) -> Queue - where - F: FnOnce() + Send + Sync + 'static, - { - let internal = Arc::clone(&self.internal); - let spawned_total = Arc::clone( - &self - .internal - .configs - .read() - .unwrap() - .get(name) - .expect("can't spawn a non-configued slowjob") - .spawned_total, - ); - let local_running_jobs_clone = { - let mut lock = self.internal.running_jobs.write().unwrap(); - Arc::clone(&lock.entry(name.to_string()).or_default()) - }; - let local_running_jobs = Arc::clone(&local_running_jobs_clone); - let global_running_jobs_clone = Arc::clone(&self.internal.global_running_jobs); - let _name_clones = name.to_string(); - Queue { - task: Box::new(move || { - common_base::prof_span!(_guard, &_name_clones); - f(); - local_running_jobs_clone.fetch_sub(1, Ordering::SeqCst); - global_running_jobs_clone.fetch_sub(1, Ordering::SeqCst); - // directly maintain the next task afterwards - internal.maintain(); - }), - spawned_total, - local_running_jobs, + pub fn cancel(&self, job: SlowJob) -> Result<(), SlowJob> { + let mut lock = self.internal.lock().expect("lock poisoned while cancel"); + if let Some(m) = lock.queue.get_mut(&job.name) { + let p = match m.iter().position(|p| p.id == job.id) { + Some(p) => p, + None => return Err(job), + }; + if m.remove(p).is_some() { + return Ok(()); + } } + Err(job) } - - pub fn cancel(&self, job: SlowJob) { - let mut lock = self.internal.queue.write().unwrap(); - if let Some(map) = lock.get_mut(&job.name) { - map.remove(&job.id); - } - } - - fn maintain(&self) { self.internal.maintain() } } #[cfg(test)] mod tests { use super::*; - use std::{ - sync::Mutex, - time::{Duration, Instant}, - }; - fn mock_fn( - name: &str, - start_time: &Arc>>, - done: &Arc, - ) -> impl FnOnce() { - let name = name.to_string(); - let start_time = Arc::clone(start_time); - let done = Arc::clone(done); - move || { - println!("Start {}", name); - *start_time.lock().unwrap() = Some(Instant::now()); - std::thread::sleep(Duration::from_millis(500)); - done.fetch_add(1, Ordering::Relaxed); - println!("Finished {}", name); + #[allow(clippy::blacklisted_name)] + fn mock_pool( + pool_threads: usize, + global_threads: u64, + foo: u64, + bar: u64, + baz: u64, + ) -> SlowJobPool { + let threadpool = rayon::ThreadPoolBuilder::new() + .num_threads(pool_threads) + .build() + .unwrap(); + let pool = SlowJobPool::new(global_threads, Arc::new(threadpool)); + if foo != 0 { + pool.configure("FOO", |x| x / foo); } + if bar != 0 { + pool.configure("BAR", |x| x / bar); + } + if baz != 0 { + pool.configure("BAZ", |x| x / baz); + } + pool } #[test] - fn global_limit() { - let threadpool = rayon::ThreadPoolBuilder::new() - .num_threads(4) - .build() - .unwrap(); - let pool = SlowJobPool::new(3, Arc::new(threadpool)); - pool.configure("FOO", |_| 1000); - let start = Instant::now(); - let f1 = Arc::new(Mutex::new(None)); - let f2 = Arc::new(Mutex::new(None)); - let f3 = Arc::new(Mutex::new(None)); - let f4 = Arc::new(Mutex::new(None)); - let f5 = Arc::new(Mutex::new(None)); - let f6 = Arc::new(Mutex::new(None)); - let f7 = Arc::new(Mutex::new(None)); - let done = Arc::new(AtomicI64::new(0)); - pool.spawn("FOO", mock_fn("foo1", &f1, &done)); - pool.spawn("FOO", mock_fn("foo2", &f2, &done)); - pool.spawn("FOO", mock_fn("foo3", &f3, &done)); - std::thread::sleep(Duration::from_millis(300)); - pool.spawn("FOO", mock_fn("foo4", &f4, &done)); - pool.spawn("FOO", mock_fn("foo5", &f5, &done)); - pool.spawn("FOO", mock_fn("foo6", &f6, &done)); - std::thread::sleep(Duration::from_millis(300)); - pool.spawn("FOO", mock_fn("foo7", &f7, &done)); - std::thread::sleep(Duration::from_secs(1)); - let measure = |a: Arc>>, s: Instant| { - a.lock().unwrap().unwrap().duration_since(s).as_millis() - }; - let f1 = measure(f1, start); - let f2 = measure(f2, start); - let f3 = measure(f3, start); - let f4 = measure(f4, start); - let f5 = measure(f5, start); - let f6 = measure(f6, start); - let f7 = measure(f7, start); - assert_eq!(done.load(Ordering::Relaxed), 7); - // Just test relative times, not absolute - assert!(f1 < f4); - assert!(f1 < f5); - assert!(f1 < f6); - assert!(f1 < f7); - assert!(f2 < f4); - assert!(f2 < f5); - assert!(f2 < f6); - assert!(f2 < f7); - assert!(f3 < f4); - assert!(f3 < f5); - assert!(f3 < f6); - assert!(f3 < f7); - assert!(f4 < f7); - assert!(f5 < f7); - assert!(f6 < f7); + fn simple_queue() { + let pool = mock_pool(4, 4, 1, 0, 0); + let internal = pool.internal.lock().unwrap(); + let queue_data = [("FOO", 1u64)] + .iter() + .map(|(n, c)| ((*n).to_owned(), *c)) + .collect::>(); + let queued = queue_data + .iter() + .map(|(s, c)| (s, *c)) + .collect::>(); + let result = internal.calc_queued_order(queued, 4); + assert_eq!(result.len(), 1); + assert_eq!(result[0], "FOO"); } #[test] - fn local_limit() { - let threadpool = rayon::ThreadPoolBuilder::new() - .num_threads(4) - .build() - .unwrap(); - let pool = SlowJobPool::new(100, Arc::new(threadpool)); - pool.configure("FOO", |_| 3); - let start = Instant::now(); - let f1 = Arc::new(Mutex::new(None)); - let f2 = Arc::new(Mutex::new(None)); - let f3 = Arc::new(Mutex::new(None)); - let f4 = Arc::new(Mutex::new(None)); - let f5 = Arc::new(Mutex::new(None)); - let f6 = Arc::new(Mutex::new(None)); - let f7 = Arc::new(Mutex::new(None)); - let done = Arc::new(AtomicI64::new(0)); - pool.spawn("FOO", mock_fn("foo1", &f1, &done)); - pool.spawn("FOO", mock_fn("foo2", &f2, &done)); - pool.spawn("FOO", mock_fn("foo3", &f3, &done)); - std::thread::sleep(Duration::from_millis(300)); - pool.spawn("FOO", mock_fn("foo4", &f4, &done)); - pool.spawn("FOO", mock_fn("foo5", &f5, &done)); - pool.spawn("FOO", mock_fn("foo6", &f6, &done)); - std::thread::sleep(Duration::from_millis(300)); - pool.spawn("FOO", mock_fn("foo7", &f7, &done)); - std::thread::sleep(Duration::from_secs(1)); - let measure = |a: Arc>>, s: Instant| { - a.lock().unwrap().unwrap().duration_since(s).as_millis() - }; - let f1 = measure(f1, start); - let f2 = measure(f2, start); - let f3 = measure(f3, start); - let f4 = measure(f4, start); - let f5 = measure(f5, start); - let f6 = measure(f6, start); - let f7 = measure(f7, start); - assert_eq!(done.load(Ordering::Relaxed), 7); - assert!(f1 < f4); - assert!(f1 < f5); - assert!(f1 < f6); - assert!(f1 < f7); - assert!(f2 < f4); - assert!(f2 < f5); - assert!(f2 < f6); - assert!(f2 < f7); - assert!(f3 < f4); - assert!(f3 < f5); - assert!(f3 < f6); - assert!(f3 < f7); - assert!(f4 < f7); - assert!(f5 < f7); - assert!(f6 < f7); + fn multiple_queue() { + let pool = mock_pool(4, 4, 1, 0, 0); + let internal = pool.internal.lock().unwrap(); + let queue_data = [("FOO", 2u64)] + .iter() + .map(|(n, c)| ((*n).to_owned(), *c)) + .collect::>(); + let queued = queue_data + .iter() + .map(|(s, c)| (s, *c)) + .collect::>(); + let result = internal.calc_queued_order(queued, 4); + assert_eq!(result.len(), 2); + assert_eq!(result[0], "FOO"); + assert_eq!(result[1], "FOO"); } #[test] - fn pool() { - let threadpool = rayon::ThreadPoolBuilder::new() - .num_threads(2) - .build() - .unwrap(); - let pool = SlowJobPool::new(2, Arc::new(threadpool)); - pool.configure("FOO", |n| n); - pool.configure("BAR", |n| n / 2); - let f1 = Arc::new(Mutex::new(None)); - let f2 = Arc::new(Mutex::new(None)); - let b1 = Arc::new(Mutex::new(None)); - let b2 = Arc::new(Mutex::new(None)); - let done = Arc::new(AtomicI64::new(0)); - let start = Instant::now(); - pool.spawn("FOO", mock_fn("foo1", &f1, &done)); - pool.spawn("FOO", mock_fn("foo2", &f2, &done)); - std::thread::sleep(Duration::from_millis(1000)); - pool.spawn("BAR", mock_fn("bar1", &b1, &done)); - pool.spawn("BAR", mock_fn("bar2", &b2, &done)); - std::thread::sleep(Duration::from_secs(2)); - let measure = |a: Arc>>, s: Instant| { - a.lock().unwrap().unwrap().duration_since(s).as_millis() + fn limit_queue() { + let pool = mock_pool(5, 5, 1, 0, 0); + let internal = pool.internal.lock().unwrap(); + let queue_data = [("FOO", 80u64)] + .iter() + .map(|(n, c)| ((*n).to_owned(), *c)) + .collect::>(); + let queued = queue_data + .iter() + .map(|(s, c)| (s, *c)) + .collect::>(); + let result = internal.calc_queued_order(queued, 4); + assert_eq!(result.len(), 4); + assert_eq!(result[0], "FOO"); + assert_eq!(result[1], "FOO"); + assert_eq!(result[2], "FOO"); + assert_eq!(result[3], "FOO"); + } + + #[test] + fn simple_queue_2() { + let pool = mock_pool(4, 4, 1, 1, 0); + let internal = pool.internal.lock().unwrap(); + let queue_data = [("FOO", 1u64), ("BAR", 1u64)] + .iter() + .map(|(n, c)| ((*n).to_owned(), *c)) + .collect::>(); + let queued = queue_data + .iter() + .map(|(s, c)| (s, *c)) + .collect::>(); + let result = internal.calc_queued_order(queued, 4); + assert_eq!(result.len(), 2); + assert_eq!(result.iter().filter(|&x| x == "FOO").count(), 1); + assert_eq!(result.iter().filter(|&x| x == "BAR").count(), 1); + } + + #[test] + fn multiple_queue_3() { + let pool = mock_pool(4, 4, 1, 1, 0); + let internal = pool.internal.lock().unwrap(); + let queue_data = [("FOO", 2u64), ("BAR", 2u64)] + .iter() + .map(|(n, c)| ((*n).to_owned(), *c)) + .collect::>(); + let queued = queue_data + .iter() + .map(|(s, c)| (s, *c)) + .collect::>(); + let result = internal.calc_queued_order(queued, 4); + assert_eq!(result.len(), 4); + assert_eq!(result.iter().filter(|&x| x == "FOO").count(), 2); + assert_eq!(result.iter().filter(|&x| x == "BAR").count(), 2); + } + + #[test] + fn multiple_queue_4() { + let pool = mock_pool(4, 4, 2, 1, 0); + let internal = pool.internal.lock().unwrap(); + let queue_data = [("FOO", 3u64), ("BAR", 3u64)] + .iter() + .map(|(n, c)| ((*n).to_owned(), *c)) + .collect::>(); + let queued = queue_data + .iter() + .map(|(s, c)| (s, *c)) + .collect::>(); + let result = internal.calc_queued_order(queued, 4); + assert_eq!(result.len(), 4); + assert_eq!(result.iter().filter(|&x| x == "FOO").count(), 2); + assert_eq!(result.iter().filter(|&x| x == "BAR").count(), 2); + } + + #[test] + fn multiple_queue_5() { + let pool = mock_pool(4, 4, 2, 1, 0); + let internal = pool.internal.lock().unwrap(); + let queue_data = [("FOO", 5u64), ("BAR", 5u64)] + .iter() + .map(|(n, c)| ((*n).to_owned(), *c)) + .collect::>(); + let queued = queue_data + .iter() + .map(|(s, c)| (s, *c)) + .collect::>(); + let result = internal.calc_queued_order(queued, 5); + assert_eq!(result.len(), 5); + assert_eq!(result.iter().filter(|&x| x == "FOO").count(), 2); + assert_eq!(result.iter().filter(|&x| x == "BAR").count(), 3); + } + + #[test] + fn multiple_queue_6() { + let pool = mock_pool(40, 40, 2, 1, 0); + let internal = pool.internal.lock().unwrap(); + let queue_data = [("FOO", 5u64), ("BAR", 5u64)] + .iter() + .map(|(n, c)| ((*n).to_owned(), *c)) + .collect::>(); + let queued = queue_data + .iter() + .map(|(s, c)| (s, *c)) + .collect::>(); + let result = internal.calc_queued_order(queued, 11); + assert_eq!(result.len(), 10); + assert_eq!(result.iter().filter(|&x| x == "FOO").count(), 5); + assert_eq!(result.iter().filter(|&x| x == "BAR").count(), 5); + } + + #[test] + #[should_panic] + fn unconfigured() { + let pool = mock_pool(4, 4, 2, 1, 0); + let mut internal = pool.internal.lock().unwrap(); + internal.spawn("UNCONFIGURED", || println!()); + } + + #[test] + fn correct_spawn_doesnt_panic() { + let pool = mock_pool(4, 4, 2, 1, 0); + let mut internal = pool.internal.lock().unwrap(); + internal.spawn("FOO", || println!("foo")); + internal.spawn("BAR", || println!("bar")); + } + + #[test] + fn can_spawn() { + let pool = mock_pool(4, 4, 2, 1, 0); + let internal = pool.internal.lock().unwrap(); + assert!(internal.can_spawn("FOO")); + assert!(internal.can_spawn("BAR")); + } + + #[test] + fn try_run_works() { + let pool = mock_pool(4, 4, 2, 1, 0); + pool.try_run("FOO", || println!("foo")).unwrap(); + pool.try_run("BAR", || println!("bar")).unwrap(); + } + + #[test] + fn try_run_exhausted() { + use std::{thread::sleep, time::Duration}; + let pool = mock_pool(8, 8, 4, 2, 0); + let func = || loop { + sleep(Duration::from_secs(1)) }; - let f1 = measure(f1, start); - let f2 = measure(f2, start); - let b1 = measure(b1, start); - let b2 = measure(b2, start); - // Expect: - // [F1, F2] - // [B1] - // [B2] - assert_eq!(done.load(Ordering::Relaxed), 4); - assert!(f1 < 600); - assert!(f2 < 600); - println!("b1 {}", b1); - println!("b2 {}", b2); - // would be to flanky: - //assert!((1000..1500).contains(&b1)); - //assert!((1500..2000).contains(&b2)); - assert!(b1 < b2); + pool.try_run("FOO", func).unwrap(); + pool.try_run("BAR", func).unwrap(); + pool.try_run("FOO", func).unwrap(); + pool.try_run("BAR", func).unwrap(); + pool.try_run("FOO", func).unwrap_err(); + pool.try_run("BAR", func).unwrap(); + pool.try_run("FOO", func).unwrap_err(); + pool.try_run("BAR", func).unwrap(); + pool.try_run("FOO", func).unwrap_err(); + pool.try_run("BAR", func).unwrap_err(); + pool.try_run("FOO", func).unwrap_err(); + } + + #[test] + fn actually_runs_1() { + let pool = mock_pool(4, 4, 0, 0, 1); + let barrier = Arc::new(std::sync::Barrier::new(2)); + let barrier_clone = Arc::clone(&barrier); + pool.try_run("BAZ", move || { + barrier_clone.wait(); + }) + .unwrap(); + barrier.wait(); + } + + #[test] + fn actually_runs_2() { + let pool = mock_pool(4, 4, 0, 0, 1); + let barrier = Arc::new(std::sync::Barrier::new(2)); + let barrier_clone = Arc::clone(&barrier); + pool.spawn("BAZ", move || { + barrier_clone.wait(); + }); + barrier.wait(); + } + + #[test] + fn actually_waits() { + use std::sync::{ + atomic::{AtomicBool, Ordering}, + Barrier, + }; + let pool = mock_pool(4, 4, 4, 0, 1); + let ops_i_ran = Arc::new(AtomicBool::new(false)); + let ops_i_ran_clone = Arc::clone(&ops_i_ran); + let barrier = Arc::new(Barrier::new(2)); + let barrier_clone = Arc::clone(&barrier); + let barrier2 = Arc::new(Barrier::new(2)); + let barrier2_clone = Arc::clone(&barrier2); + pool.try_run("FOO", move || { + barrier_clone.wait(); + }) + .unwrap(); + pool.spawn("FOO", move || { + ops_i_ran_clone.store(true, Ordering::SeqCst); + barrier2_clone.wait(); + }); + // in this case we have to sleep + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(ops_i_ran.load(Ordering::SeqCst), false); + // now finish the first job + barrier.wait(); + // now wait on the second job to be actually finished + barrier2.wait(); } } From 34f5ff62d471c5f03f61e13836c47cb24570c157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Fri, 21 May 2021 09:51:00 +0200 Subject: [PATCH 043/180] implement a simple roundrobin to assure if multiple are spawned the older one has prio, spelling --- common/src/slowjob.rs | 106 +++++++++++++++++++++++++++-------- voxygen/src/ui/keyed_jobs.rs | 5 +- 2 files changed, 87 insertions(+), 24 deletions(-) diff --git a/common/src/slowjob.rs b/common/src/slowjob.rs index 5175e4b069..fa77b03ba6 100644 --- a/common/src/slowjob.rs +++ b/common/src/slowjob.rs @@ -3,13 +3,9 @@ use rayon::ThreadPool; use std::{ collections::VecDeque, sync::{Arc, Mutex}, - time::{Duration, Instant} }; use tracing::{error, warn}; -const LAST_JOBS_METRICS: usize = 32; -const FAIR_TIME_INTERVAL: Duration = Duration::from_millis(1000); - /// Provides a Wrapper around rayon threadpool to execute slow-jobs. /// slow means, the job doesn't need to not complete within the same tick. /// DO NOT USE I/O blocking jobs, but only CPU heavy jobs. @@ -28,7 +24,7 @@ const FAIR_TIME_INTERVAL: Duration = Duration::from_millis(1000); /// hit. /// - remaining capacities are spread in relation to their limit. e.g. a /// configuration with double the limit will be sheduled to spawn double the -/// tasks. +/// tasks, starting by a round robin. /// /// ## States /// - queued @@ -62,6 +58,7 @@ struct InternalSlowJobPool { next_id: u64, queue: HashMap>, configs: HashMap, + last_spawned_configs: Vec, global_spawned_and_running: u64, global_limit: u64, threadpool: Arc, @@ -72,8 +69,6 @@ struct InternalSlowJobPool { struct Config { local_limit: u64, local_spawned_and_running: u64, - /// hold the start and time of the last LAST_JOBS_METRICS jobs - last_jobs: VecDeque<(std::time::Instant, Option)>, } struct Queue { @@ -112,6 +107,7 @@ impl InternalSlowJobPool { next_id: 0, queue: HashMap::new(), configs: HashMap::new(), + last_spawned_configs: Vec::new(), global_spawned_and_running: 0, global_limit: global_limit.max(1), threadpool, @@ -131,13 +127,14 @@ impl InternalSlowJobPool { mut queued: HashMap<&String, u64>, mut limit: usize, ) -> Vec { + let mut roundrobin = self.last_spawned_configs.clone(); let mut result = vec![]; let spawned = self .configs .iter() .map(|(n, c)| (n, c.local_spawned_and_running)) .collect::>(); - let mut queried_caped = self + let mut queried_capped = self .configs .iter() .map(|(n, c)| { @@ -151,24 +148,31 @@ impl InternalSlowJobPool { ) }) .collect::>(); - // grab all configs that are queued and not running - for (&n, c) in queued.iter_mut() { - if *c > 0 && spawned.get(n).cloned().unwrap_or(0) == 0 { - result.push(n.clone()); - *c -= 1; - limit -= 1; - queried_caped.get_mut(&n).map(|v| *v -= 1); - if limit == 0 { - return result; + // grab all configs that are queued and not running. in roundrobin order + for n in roundrobin.clone().into_iter() { + if let Some(c) = queued.get_mut(&n) { + if *c > 0 && spawned.get(&n).cloned().unwrap_or(0) == 0 { + result.push(n.clone()); + *c -= 1; + limit -= 1; + queried_capped.get_mut(&n).map(|v| *v -= 1); + roundrobin + .iter() + .position(|e| e == &n) + .map(|i| roundrobin.remove(i)); + roundrobin.push(n); + if limit == 0 { + return result; + } } } } - //schedule rest - let total_limit = queried_caped.values().sum::() as f32; + //schedule rest based on their possible limites, don't use round robin here + let total_limit = queried_capped.values().sum::() as f32; if total_limit < f32::EPSILON { return result; } - let mut spawn_rates = queried_caped + let mut spawn_rates = queried_capped .iter() .map(|(&n, l)| (n, ((*l as f32 * limit as f32) / total_limit).min(*l as f32))) .collect::>(); @@ -231,6 +235,10 @@ impl InternalSlowJobPool { .entry(name.to_string()) .or_default() .push_back(queue); + debug_assert!( + self.configs.contains_key(name), + "Can't spawn unconfigured task!" + ); //spawn already queued self.spawn_queued(); SlowJob { @@ -243,7 +251,6 @@ impl InternalSlowJobPool { self.global_spawned_and_running -= 1; if let Some(c) = self.configs.get_mut(name) { c.local_spawned_and_running -= 1; - c.last_jobs. } else { warn!(?name, "sync_maintain on a no longer existing config"); } @@ -267,6 +274,11 @@ impl InternalSlowJobPool { .get_mut(&queue.name) .expect("cannot fire a unconfigured job") .local_spawned_and_running += 1; + self.last_spawned_configs + .iter() + .position(|e| e == &queue.name) + .map(|i| self.last_spawned_configs.remove(i)); + self.last_spawned_configs.push(queue.name.to_owned()); self.threadpool.spawn(queue.task); }, None => error!( @@ -300,9 +312,9 @@ impl SlowJobPool { let cnf = Config { local_limit: f(lock.global_limit).max(1), local_spawned_and_running: 0, - last_jobs: VecDeque::with_capacity(LAST_JOBS_METRICS), }; lock.configs.insert(name.to_owned(), cnf); + lock.last_spawned_configs.push(name.to_owned()); } /// spawn a new slow job on a certain NAME IF it can run immediately @@ -520,6 +532,54 @@ mod tests { assert_eq!(result.iter().filter(|&x| x == "BAR").count(), 5); } + #[test] + fn roundrobin() { + let pool = mock_pool(4, 4, 2, 2, 0); + let queue_data = [("FOO", 5u64), ("BAR", 5u64)] + .iter() + .map(|(n, c)| ((*n).to_owned(), *c)) + .collect::>(); + let queued = queue_data + .iter() + .map(|(s, c)| (s, *c)) + .collect::>(); + // Spawn a FOO task. + pool.internal + .lock() + .unwrap() + .spawn("FOO", || println!("foo")); + // a barrier in f doesnt work as we need to wait for the cleanup + while pool.internal.lock().unwrap().global_spawned_and_running != 0 { + std::thread::yield_now(); + } + let result = pool + .internal + .lock() + .unwrap() + .calc_queued_order(queued.clone(), 1); + assert_eq!(result.len(), 1); + assert_eq!(result[0], "BAR"); + // keep order if no new is spawned + let result = pool + .internal + .lock() + .unwrap() + .calc_queued_order(queued.clone(), 1); + assert_eq!(result.len(), 1); + assert_eq!(result[0], "BAR"); + // spawn a BAR task + pool.internal + .lock() + .unwrap() + .spawn("BAR", || println!("bar")); + while pool.internal.lock().unwrap().global_spawned_and_running != 0 { + std::thread::yield_now(); + } + let result = pool.internal.lock().unwrap().calc_queued_order(queued, 1); + assert_eq!(result.len(), 1); + assert_eq!(result[0], "FOO"); + } + #[test] #[should_panic] fn unconfigured() { @@ -617,7 +677,7 @@ mod tests { }); // in this case we have to sleep std::thread::sleep(std::time::Duration::from_secs(1)); - assert_eq!(ops_i_ran.load(Ordering::SeqCst), false); + assert!(!ops_i_ran.load(Ordering::SeqCst)); // now finish the first job barrier.wait(); // now wait on the second job to be actually finished diff --git a/voxygen/src/ui/keyed_jobs.rs b/voxygen/src/ui/keyed_jobs.rs index 72f61b33b7..cacd5195f3 100644 --- a/voxygen/src/ui/keyed_jobs.rs +++ b/voxygen/src/ui/keyed_jobs.rs @@ -4,6 +4,7 @@ use std::{ hash::Hash, time::{Duration, Instant}, }; +use tracing::warn; enum KeyedJobTask { Pending(Instant, Option), @@ -60,7 +61,9 @@ impl Key let fresh = now - *at < KEYEDJOBS_GC_INTERVAL; if !fresh { if let Some(job) = job.take() { - pool.cancel(job) + if let Err(e) = pool.cancel(job) { + warn!(?e, "failed to cancel job"); + } } } fresh From faf5b3fc9c0f68db7009cc9cf0c2f7dd03501b15 Mon Sep 17 00:00:00 2001 From: Yusuf Bera Ertan Date: Sun, 13 Jun 2021 17:55:08 +0300 Subject: [PATCH 044/180] chore(deps): update nix flake deps --- flake.lock | 88 +++++++----------------------------------------------- flake.nix | 2 +- 2 files changed, 11 insertions(+), 79 deletions(-) diff --git a/flake.lock b/flake.lock index cbb32d0c2a..47a42b63e2 100644 --- a/flake.lock +++ b/flake.lock @@ -1,22 +1,5 @@ { "nodes": { - "crate2nix": { - "flake": false, - "locked": { - "lastModified": 1619462726, - "narHash": "sha256-bQuUBOGzPnL3S+aweK/P9WRfNGk/tuoLDPfzIiX7XXY=", - "owner": "yusdacra", - "repo": "crate2nix", - "rev": "e10f71834d1464cd4b07d1bf7965c65abbff3fab", - "type": "github" - }, - "original": { - "owner": "yusdacra", - "ref": "feat/builtinfetchgit", - "repo": "crate2nix", - "type": "github" - } - }, "devshell": { "locked": { "lastModified": 1622711433, @@ -32,71 +15,36 @@ "type": "github" } }, - "flakeUtils": { - "locked": { - "lastModified": 1622445595, - "narHash": "sha256-m+JRe6Wc5OZ/mKw2bB3+Tl0ZbtyxxxfnAWln8Q5qs+Y=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "7d706970d94bc5559077eb1a6600afddcd25a7c8", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "naersk": { - "flake": false, - "locked": { - "lastModified": 1619312121, - "narHash": "sha256-Zx1rTlonsp54lVlnIg38HV3bYx6GdIoKS1SgDnV+YBY=", - "owner": "yusdacra", - "repo": "naersk", - "rev": "07d0b56bdbd353a705f26b799e3a125c7be0f8c3", - "type": "github" - }, - "original": { - "owner": "yusdacra", - "ref": "feat/cargolock-git-deps", - "repo": "naersk", - "type": "github" - } - }, "nixCargoIntegration": { "inputs": { - "crate2nix": "crate2nix", "devshell": "devshell", - "flakeUtils": "flakeUtils", - "naersk": "naersk", "nixpkgs": [ "nixpkgs" ], - "preCommitHooks": "preCommitHooks", "rustOverlay": "rustOverlay" }, "locked": { - "lastModified": 1622713739, - "narHash": "sha256-+X+MJno3JKwrtJxLbl37/0YihznIfjowRyxeEwoFEk0=", + "lastModified": 1623594891, + "narHash": "sha256-JmjOTdIW0QIsMoqU6oIQx9fqH0Ims83g2jK8jmokHsI=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "f383dd1d1915ec1fc38d855fe9351d8f5778211e", + "rev": "5394e8fa179346854e44e05edced0b9d7dc818b7", "type": "github" }, "original": { "owner": "yusdacra", + "ref": "fix/crate2nix", "repo": "nix-cargo-integration", "type": "github" } }, "nixpkgs": { "locked": { - "lastModified": 1622368643, - "narHash": "sha256-/BEAmTreS2JjRvnuNNr65tA20FWcRsonURKfYdJecJA=", + "lastModified": 1623176226, + "narHash": "sha256-54a9uvHlIlK3i0b36HfGMc4zqM0BpMOOiFYBxEhQFK8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "774fe1878b045411e6bdd0dd90d8581e82b10993", + "rev": "51bb9f3e9ab6161a3bf7746e20b955712cef618b", "type": "github" }, "original": { @@ -106,22 +54,6 @@ "type": "github" } }, - "preCommitHooks": { - "flake": false, - "locked": { - "lastModified": 1622650193, - "narHash": "sha256-qSzUpJDv04ajS9FXoCq6NjVF3qOt9IiGIiGh0P8amyw=", - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "0398f0649e0a741660ac5e8216760bae5cc78579", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "type": "github" - } - }, "root": { "inputs": { "nixCargoIntegration": "nixCargoIntegration", @@ -131,11 +63,11 @@ "rustOverlay": { "flake": false, "locked": { - "lastModified": 1622689331, - "narHash": "sha256-b8c0t2Uo4uk7DNhSfhFDpqzY/w/tIUgBut5Ug1Pl6DU=", + "lastModified": 1623550815, + "narHash": "sha256-RumRrkE6OTJDndHV4qZNZv8kUGnzwRHZQSyzx29r6/g=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "7ad7640ad12ff6b0af4f96bffd95175ad0898d49", + "rev": "9824f142cbd7bc3e2a92eefbb79addfff8704cd3", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 54b4bd0c43..a34463137d 100644 --- a/flake.nix +++ b/flake.nix @@ -4,7 +4,7 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; nixCargoIntegration = { - url = "github:yusdacra/nix-cargo-integration"; + url = "github:yusdacra/nix-cargo-integration/fix/crate2nix"; inputs.nixpkgs.follows = "nixpkgs"; }; }; From fd480fdfbc02d58785ceda64483219b2cd951c21 Mon Sep 17 00:00:00 2001 From: Yusuf Bera Ertan Date: Sun, 13 Jun 2021 19:09:10 +0300 Subject: [PATCH 045/180] build(nix): fix shaderc lib --- Cargo.toml | 4 ---- flake.nix | 6 ++++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 88c76c276e..bd0e04061c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -109,10 +109,6 @@ key = "veloren-nix.cachix.org-1:zokfKJqVsNV6kI/oJdLF6TYBdNPYGSb+diMVQPn/5Rc=" buildInputs = ["openssl"] nativeBuildInputs = ["pkg-config"] -[workspace.metadata.nix.crateOverride.shaderc-sys] -buildInputs = ["shaderc"] -nativeBuildInputs = ["cmake", "python3", "gnumake"] - [patch.crates-io] # macos CI fix isn't released yet winit = { git = "https://gitlab.com/veloren/winit.git", branch = "macos-test-spiffed" } diff --git a/flake.nix b/flake.nix index a34463137d..9a7353e892 100644 --- a/flake.nix +++ b/flake.nix @@ -60,6 +60,12 @@ # veloren-world = oldAttrs: { # crateBin = lib.filter (bin: bin.name != "chunk_compression_benchmarks") oldAttrs.crateBin; # }; + shaderc-sys = _: + let SHADERC_LIB_DIR = "${common.pkgs.shaderc.lib}/lib"; in + { + inherit SHADERC_LIB_DIR; + propagatedEnv = { inherit SHADERC_LIB_DIR; }; + }; veloren-client = oldAttrs: { crateBin = lib.filter (bin: bin.name != "bot") oldAttrs.crateBin; }; From ffad3a5f662f852278d9053a21d4feff9c107f08 Mon Sep 17 00:00:00 2001 From: Yusuf Bera Ertan Date: Sun, 13 Jun 2021 19:17:37 +0300 Subject: [PATCH 046/180] build(nix): dont try to compile i18n-check binary --- flake.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/flake.nix b/flake.nix index 9a7353e892..199b8be1b5 100644 --- a/flake.nix +++ b/flake.nix @@ -69,6 +69,9 @@ veloren-client = oldAttrs: { crateBin = lib.filter (bin: bin.name != "bot") oldAttrs.crateBin; }; + veloren-i18n = oldAttrs: { + crateBin = lib.filter (bin: bin.name != "i18n-check") oldAttrs.crateBin; + }; veloren-common = oldAttrs: { # Disable `git-lfs` check here since we check it ourselves # We have to include the command output here, otherwise Nix won't run it From 27216d9a5e9cc7c8da9dff8a51b3dd5148666909 Mon Sep 17 00:00:00 2001 From: Yusuf Bera Ertan Date: Sun, 13 Jun 2021 19:23:00 +0300 Subject: [PATCH 047/180] build(nix): dont try to compile the recipe_graphviz binary --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 199b8be1b5..26bebdf085 100644 --- a/flake.nix +++ b/flake.nix @@ -79,7 +79,7 @@ # Declare env values here so that `common/build.rs` sees them NIX_GIT_HASH = prettyRev; NIX_GIT_TAG = tag; - crateBin = lib.filter (bin: bin.name != "csv_export" && bin.name != "csv_import") oldAttrs.crateBin; + crateBin = lib.filter (bin: bin.name != "csv_export" && bin.name != "csv_import" && bin.name != "recipe_graphviz") oldAttrs.crateBin; }; veloren-voxygen = oldAttrs: { VELOREN_USERDATA_STRATEGY = "system"; From e27a6fcaa1a714b6d6c923ac5bf1883a26f36d82 Mon Sep 17 00:00:00 2001 From: Yusuf Bera Ertan Date: Sun, 13 Jun 2021 19:33:38 +0300 Subject: [PATCH 048/180] build(nix): switch back to master branch nix-cargo-integration, use defaultOutputs --- flake.lock | 7 +- flake.nix | 196 ++++++++++++++++++++++++++--------------------------- 2 files changed, 100 insertions(+), 103 deletions(-) diff --git a/flake.lock b/flake.lock index 47a42b63e2..dace752322 100644 --- a/flake.lock +++ b/flake.lock @@ -24,16 +24,15 @@ "rustOverlay": "rustOverlay" }, "locked": { - "lastModified": 1623594891, - "narHash": "sha256-JmjOTdIW0QIsMoqU6oIQx9fqH0Ims83g2jK8jmokHsI=", + "lastModified": 1623601923, + "narHash": "sha256-zQDRV3OMYKlewfp3k0ilmFNfcFDxBCCk0tbcde8HMYM=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "5394e8fa179346854e44e05edced0b9d7dc818b7", + "rev": "ab922adefdeb387ce105b1bde75f62ffb971cec4", "type": "github" }, "original": { "owner": "yusdacra", - "ref": "fix/crate2nix", "repo": "nix-cargo-integration", "type": "github" } diff --git a/flake.nix b/flake.nix index 26bebdf085..e31ceb2257 100644 --- a/flake.nix +++ b/flake.nix @@ -4,110 +4,108 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; nixCargoIntegration = { - url = "github:yusdacra/nix-cargo-integration/fix/crate2nix"; + url = "github:yusdacra/nix-cargo-integration"; inputs.nixpkgs.follows = "nixpkgs"; }; }; outputs = inputs: - let - output = inputs.nixCargoIntegration.lib.makeOutputs { - root = ./.; - buildPlatform = "crate2nix"; - overrides = { - build = common: prev: { - runTests = !prev.release && prev.runTests; - }; - crateOverrides = common: prev: - let - pkgs = common.pkgs; - lib = common.lib; - - gitLfsCheckFile = ./assets/voxygen/background/bg_main.png; - utils = import ./nix/utils.nix { inherit pkgs; }; - - sourceInfo = - if inputs.self.sourceInfo ? rev - then inputs.self.sourceInfo // { - # Tag would have to be set manually for stable releases flake - # because there's currently no way to get the tag via the interface. - # tag = v0.9.0; - } - else (throw "Can't get revision because the git tree is dirty"); - - prettyRev = with sourceInfo; builtins.substring 0 8 rev + "/" + utils.dateTimeFormat lastModified; - - tag = with sourceInfo; - if sourceInfo ? tag - then sourceInfo.tag - else ""; - - # If gitTag has a tag (meaning the commit we are on is a *release*), use - # it as version, else: just use the prettified hash we have, if we don't - # have it the build fails. - # Must be in format f4987672/2020-12-10-12:00 - version = - if tag != "" then tag - else if prettyRev != "" then prettyRev - else throw "Need a tag or pretty revision in order to determine version"; - - veloren-assets = pkgs.runCommand "makeAssetsDir" { } '' - mkdir $out - ln -sf ${./assets} $out/assets - ''; - in - { - # veloren-world = oldAttrs: { - # crateBin = lib.filter (bin: bin.name != "chunk_compression_benchmarks") oldAttrs.crateBin; - # }; - shaderc-sys = _: - let SHADERC_LIB_DIR = "${common.pkgs.shaderc.lib}/lib"; in - { - inherit SHADERC_LIB_DIR; - propagatedEnv = { inherit SHADERC_LIB_DIR; }; - }; - veloren-client = oldAttrs: { - crateBin = lib.filter (bin: bin.name != "bot") oldAttrs.crateBin; - }; - veloren-i18n = oldAttrs: { - crateBin = lib.filter (bin: bin.name != "i18n-check") oldAttrs.crateBin; - }; - veloren-common = oldAttrs: { - # Disable `git-lfs` check here since we check it ourselves - # We have to include the command output here, otherwise Nix won't run it - DISABLE_GIT_LFS_CHECK = utils.isGitLfsSetup gitLfsCheckFile; - # Declare env values here so that `common/build.rs` sees them - NIX_GIT_HASH = prettyRev; - NIX_GIT_TAG = tag; - crateBin = lib.filter (bin: bin.name != "csv_export" && bin.name != "csv_import" && bin.name != "recipe_graphviz") oldAttrs.crateBin; - }; - veloren-voxygen = oldAttrs: { - VELOREN_USERDATA_STRATEGY = "system"; - postInstall = '' - if [ -f $out/bin/veloren-voxygen ]; then - wrapProgram $out/bin/veloren-voxygen \ - --set VELOREN_ASSETS ${veloren-assets} \ - --set LD_LIBRARY_PATH ${lib.makeLibraryPath common.runtimeLibs} - fi - ''; - patches = [ - (import ./nix/nullOggPatch.nix { nullOgg = ./assets/voxygen/audio/null.ogg; inherit pkgs; }) - ]; - }; - veloren-server-cli = oldAttrs: { - VELOREN_USERDATA_STRATEGY = "system"; - postInstall = '' - if [ -f $out/bin/veloren-server-cli ]; then - wrapProgram $out/bin/veloren-server-cli --set VELOREN_ASSETS ${veloren-assets} - fi - ''; - }; - }; - }; + inputs.nixCargoIntegration.lib.makeOutputs { + root = ./.; + buildPlatform = "crate2nix"; + defaultOutputs = { + package = "veloren-voxygen"; + app = "veloren-voxygen"; + }; + overrides = { + build = common: prev: { + runTests = !prev.release && prev.runTests; + }; + crateOverrides = common: prev: + let + pkgs = common.pkgs; + lib = common.lib; + + gitLfsCheckFile = ./assets/voxygen/background/bg_main.png; + utils = import ./nix/utils.nix { inherit pkgs; }; + + sourceInfo = + if inputs.self.sourceInfo ? rev + then inputs.self.sourceInfo // { + # Tag would have to be set manually for stable releases flake + # because there's currently no way to get the tag via the interface. + # tag = v0.9.0; + } + else (throw "Can't get revision because the git tree is dirty"); + + prettyRev = with sourceInfo; builtins.substring 0 8 rev + "/" + utils.dateTimeFormat lastModified; + + tag = with sourceInfo; + if sourceInfo ? tag + then sourceInfo.tag + else ""; + + # If gitTag has a tag (meaning the commit we are on is a *release*), use + # it as version, else: just use the prettified hash we have, if we don't + # have it the build fails. + # Must be in format f4987672/2020-12-10-12:00 + version = + if tag != "" then tag + else if prettyRev != "" then prettyRev + else throw "Need a tag or pretty revision in order to determine version"; + + veloren-assets = pkgs.runCommand "makeAssetsDir" { } '' + mkdir $out + ln -sf ${./assets} $out/assets + ''; + in + { + # veloren-world = oldAttrs: { + # crateBin = lib.filter (bin: bin.name != "chunk_compression_benchmarks") oldAttrs.crateBin; + # }; + shaderc-sys = _: + let SHADERC_LIB_DIR = "${common.pkgs.shaderc.lib}/lib"; in + { + inherit SHADERC_LIB_DIR; + propagatedEnv = { inherit SHADERC_LIB_DIR; }; + }; + veloren-client = oldAttrs: { + crateBin = lib.filter (bin: bin.name != "bot") oldAttrs.crateBin; + }; + veloren-i18n = oldAttrs: { + crateBin = lib.filter (bin: bin.name != "i18n-check") oldAttrs.crateBin; + }; + veloren-common = oldAttrs: { + # Disable `git-lfs` check here since we check it ourselves + # We have to include the command output here, otherwise Nix won't run it + DISABLE_GIT_LFS_CHECK = utils.isGitLfsSetup gitLfsCheckFile; + # Declare env values here so that `common/build.rs` sees them + NIX_GIT_HASH = prettyRev; + NIX_GIT_TAG = tag; + crateBin = lib.filter (bin: bin.name != "csv_export" && bin.name != "csv_import" && bin.name != "recipe_graphviz") oldAttrs.crateBin; + }; + veloren-voxygen = oldAttrs: { + VELOREN_USERDATA_STRATEGY = "system"; + postInstall = '' + if [ -f $out/bin/veloren-voxygen ]; then + wrapProgram $out/bin/veloren-voxygen \ + --set VELOREN_ASSETS ${veloren-assets} \ + --set LD_LIBRARY_PATH ${lib.makeLibraryPath common.runtimeLibs} + fi + ''; + patches = [ + (import ./nix/nullOggPatch.nix { nullOgg = ./assets/voxygen/audio/null.ogg; inherit pkgs; }) + ]; + }; + veloren-server-cli = oldAttrs: { + VELOREN_USERDATA_STRATEGY = "system"; + postInstall = '' + if [ -f $out/bin/veloren-server-cli ]; then + wrapProgram $out/bin/veloren-server-cli --set VELOREN_ASSETS ${veloren-assets} + fi + ''; + }; + }; }; - in - output // { - defaultApp = builtins.mapAttrs (_: apps: apps.veloren-voxygen) output.apps; - defaultPackage = builtins.mapAttrs (_: packages: packages.veloren-voxygen) output.packages; }; } From 0e394029de70651045419cb758f15e1bd4330c1d Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Wed, 9 Jun 2021 01:14:20 -0400 Subject: [PATCH 049/180] Mining skill tree. --- CHANGELOG.md | 1 + .../common/skill_trees/skill_max_levels.ron | 3 + .../skills_skill-groups_manifest.ron | 5 ++ .../resource_experience_manifest.ron | 19 +++++ .../element/skills/pickaxe_gemgain.png | 3 + .../element/skills/pickaxe_oregain.png | 3 + .../voxygen/element/skills/pickaxe_speed.png | 3 + assets/voxygen/element/weapons/pickaxe.png | 3 + assets/voxygen/i18n/en/skills.ron | 9 +++ assets/voxygen/item_image_manifest.ron | 4 + common/src/cmd.rs | 2 +- common/src/comp/ability.rs | 17 ++++ common/src/comp/inventory/item/tool.rs | 15 ++++ common/src/comp/skills.rs | 29 ++++--- common/src/event.rs | 1 + common/systems/src/melee.rs | 1 + server/src/cmd.rs | 1 + server/src/events/entity_manipulation.rs | 13 ++-- server/src/events/interaction.rs | 78 ++++++++++++++++++- server/src/events/mod.rs | 4 +- server/src/migrations/V41__mining_tree.sql | 7 ++ server/src/persistence/json_models.rs | 17 ++-- voxygen/src/hud/diary.rs | 73 ++++++++++++++++- voxygen/src/hud/img_ids.rs | 5 ++ voxygen/src/hud/mod.rs | 1 + 25 files changed, 290 insertions(+), 27 deletions(-) create mode 100644 assets/server/manifests/resource_experience_manifest.ron create mode 100644 assets/voxygen/element/skills/pickaxe_gemgain.png create mode 100644 assets/voxygen/element/skills/pickaxe_oregain.png create mode 100644 assets/voxygen/element/skills/pickaxe_speed.png create mode 100644 assets/voxygen/element/weapons/pickaxe.png create mode 100644 server/src/migrations/V41__mining_tree.sql diff --git a/CHANGELOG.md b/CHANGELOG.md index 4466ae1851..99fa93a4cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -85,6 +85,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Meat drops from animals - New ores, plants and hides to be looted from the world and processed into craft ingredients - Added more crafting stations, loom, spinning wheel, tanning rack, forge +- Added a skill tree for mining, which gains xp from mining ores and gems. ### Changed diff --git a/assets/common/skill_trees/skill_max_levels.ron b/assets/common/skill_trees/skill_max_levels.ron index 275c23b15b..a319299903 100644 --- a/assets/common/skill_trees/skill_max_levels.ron +++ b/assets/common/skill_trees/skill_max_levels.ron @@ -76,4 +76,7 @@ Climb(Cost): Some(2), Climb(Speed): Some(2), Swim(Speed): Some(2), + Pick(Speed): Some(3), + Pick(OreGain): Some(3), + Pick(GemGain): Some(3), }) diff --git a/assets/common/skill_trees/skills_skill-groups_manifest.ron b/assets/common/skill_trees/skills_skill-groups_manifest.ron index 220f2b1e97..641ba74520 100644 --- a/assets/common/skill_trees/skills_skill-groups_manifest.ron +++ b/assets/common/skill_trees/skills_skill-groups_manifest.ron @@ -109,4 +109,9 @@ Sceptre(ARange), Sceptre(ACost), ], + Weapon(Pick): [ + Pick(Speed), + Pick(OreGain), + Pick(GemGain), + ], }) diff --git a/assets/server/manifests/resource_experience_manifest.ron b/assets/server/manifests/resource_experience_manifest.ron new file mode 100644 index 0000000000..6345e0d392 --- /dev/null +++ b/assets/server/manifests/resource_experience_manifest.ron @@ -0,0 +1,19 @@ +ResourceExperienceManifest({ + "common.items.mineral.gem.amethyst": 20, + "common.items.mineral.gem.sapphire": 50, + "common.items.mineral.gem.topaz": 20, + "common.items.mineral.gem.diamond": 100, + "common.items.mineral.gem.emerald": 50, + "common.items.mineral.gem.ruby": 75, + + "common.items.mineral.ore.coal": 25, + "common.items.mineral.ore.gold": 100, + "common.items.mineral.ore.iron": 20, + "common.items.mineral.ore.silver": 75, + "common.items.mineral.ore.velorite": 30, + "common.items.mineral.ore.veloritefrag": 20, + "common.items.mineral.ore.bloodstone": 100, + "common.items.mineral.ore.cobalt": 75, + "common.items.mineral.ore.copper": 10, + "common.items.mineral.ore.tin": 10, +}) diff --git a/assets/voxygen/element/skills/pickaxe_gemgain.png b/assets/voxygen/element/skills/pickaxe_gemgain.png new file mode 100644 index 0000000000..3615cd1d7d --- /dev/null +++ b/assets/voxygen/element/skills/pickaxe_gemgain.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:87cb3de9b1db989b909b5db2b4d4f0f91773f881df5caeac5e7b92644c52b955 +size 1780 diff --git a/assets/voxygen/element/skills/pickaxe_oregain.png b/assets/voxygen/element/skills/pickaxe_oregain.png new file mode 100644 index 0000000000..9d42b64f7d --- /dev/null +++ b/assets/voxygen/element/skills/pickaxe_oregain.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7b3de05de2d4ca7a76769aa93369c92bbd18fe98c2b337a412c380d726808e4e +size 1790 diff --git a/assets/voxygen/element/skills/pickaxe_speed.png b/assets/voxygen/element/skills/pickaxe_speed.png new file mode 100644 index 0000000000..7a462f3940 --- /dev/null +++ b/assets/voxygen/element/skills/pickaxe_speed.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27f66e968c4f475123d726c096e80bb34c2ebea856c4c0bf1d20969ac4d01dbc +size 1734 diff --git a/assets/voxygen/element/weapons/pickaxe.png b/assets/voxygen/element/weapons/pickaxe.png new file mode 100644 index 0000000000..0f4ec67ac9 --- /dev/null +++ b/assets/voxygen/element/weapons/pickaxe.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ff8c3173b5d87c4bf00233d222d2b47045bbb3cf602afea5814d85167ce9f9da +size 1720 diff --git a/assets/voxygen/i18n/en/skills.ron b/assets/voxygen/i18n/en/skills.ron index 93ae9ffb7e..66250941ff 100644 --- a/assets/voxygen/i18n/en/skills.ron +++ b/assets/voxygen/i18n/en/skills.ron @@ -238,6 +238,15 @@ "hud.skill.axe_leap_cost": "Decreases cost of leap by 25%{SP}", "hud.skill.axe_leap_distance_title": "Leap Distance", "hud.skill.axe_leap_distance": "Increases distance of leap by 20%{SP}", + // Pick + "hud.skill.pick_strike_title": "Pickaxe Strike", + "hud.skill.pick_strike": "Hit rocks with the pickaxe to gain ore and gems and experience", + "hud.skill.pick_strike_speed_title": "Pickaxe Strike Speed", + "hud.skill.pick_strike_speed": "Mine rocks faster{SP}", + "hud.skill.pick_strike_oregain_title": "Pickaxe Strike Ore Yield", + "hud.skill.pick_strike_oregain": "Chance to gain extra ore (5% per level){SP}", + "hud.skill.pick_strike_gemgain_title": "Pickaxe Strike Gem Yield", + "hud.skill.pick_strike_gemgain": "Chance to gain extra gems (5% per level){SP}", }, diff --git a/assets/voxygen/item_image_manifest.ron b/assets/voxygen/item_image_manifest.ron index 5bb8796f1c..e0d711ad6a 100644 --- a/assets/voxygen/item_image_manifest.ron +++ b/assets/voxygen/item_image_manifest.ron @@ -65,6 +65,10 @@ "voxel.weapon.sceptre.wood-nature", (-1.0, 0.0, 0.0), (-90.0, 55.0, 0.0), 1.0, ), + Tool("example_pick"): VoxTrans( + "voxel.weapon.tool.pickaxe_green-0", + (0.0, 0.0, 0.0), (-135.0, 90.0, 0.0), 1.0, + ), Tool("example_dagger"): VoxTrans( "voxel.weapon.dagger.dagger_basic-0", (0.0, 0.0, 0.0), (90.0, 90.0, 0.0), 1.0, diff --git a/common/src/cmd.rs b/common/src/cmd.rs index e8f9054605..d8752cffd5 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -127,7 +127,7 @@ lazy_static! { .iter() .map(|s| s.to_string()) .collect(); - static ref SKILL_TREES: Vec = vec!["general", "sword", "axe", "hammer", "bow", "staff", "sceptre"] + static ref SKILL_TREES: Vec = vec!["general", "sword", "axe", "hammer", "bow", "staff", "sceptre", "pick"] .iter() .map(|s| s.to_string()) .collect(); diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 8ab75b8928..bab3f56117 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -1174,6 +1174,23 @@ impl CharacterAbility { _ => {}, } }, + Some(ToolKind::Pick) => { + use skills::PickSkill::*; + if let BasicMelee { + ref mut buildup_duration, + ref mut swing_duration, + ref mut recover_duration, + .. + } = self + { + if let Ok(Some(level)) = skillset.skill_level(Pick(Speed)) { + let speed = 1.1_f32.powi(level.into()); + *buildup_duration /= speed; + *swing_duration /= speed; + *recover_duration /= speed; + } + } + }, None => { if let CharacterAbility::Roll { ref mut energy_cost, diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index e91b52c0de..c0f3ee2d03 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -50,6 +50,21 @@ impl ToolKind { ToolKind::Empty => "empty", } } + + pub fn gains_combat_xp(&self) -> bool { + matches!( + self, + ToolKind::Sword + | ToolKind::Axe + | ToolKind::Hammer + | ToolKind::Bow + | ToolKind::Dagger + | ToolKind::Staff + | ToolKind::Spear + | ToolKind::Sceptre + | ToolKind::Shield + ) + } } #[derive(Clone, Copy, Debug, Serialize, Deserialize)] diff --git a/common/src/comp/skills.rs b/common/src/comp/skills.rs index e1c113d8f2..c130b067c9 100644 --- a/common/src/comp/skills.rs +++ b/common/src/comp/skills.rs @@ -107,6 +107,7 @@ pub enum Skill { Roll(RollSkill), Climb(ClimbSkill), Swim(SwimSkill), + Pick(PickSkill), } pub enum SkillError { @@ -263,6 +264,13 @@ pub enum SwimSkill { Speed, } +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] +pub enum PickSkill { + Speed, + OreGain, + GemGain, +} + #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] pub enum SkillGroupKind { General, @@ -344,7 +352,10 @@ impl Default for SkillSet { /// player fn default() -> Self { Self { - skill_groups: vec![SkillGroup::new(SkillGroupKind::General)], + skill_groups: vec![ + SkillGroup::new(SkillGroupKind::General), + SkillGroup::new(SkillGroupKind::Weapon(ToolKind::Pick)), + ], skills: HashMap::new(), modify_health: false, modify_energy: false, @@ -365,7 +376,7 @@ impl SkillSet { /// let mut skillset = SkillSet::default(); /// skillset.unlock_skill_group(SkillGroupKind::Weapon(ToolKind::Sword)); /// - /// assert_eq!(skillset.skill_groups.len(), 2); + /// assert_eq!(skillset.skill_groups.len(), 3); /// ``` pub fn unlock_skill_group(&mut self, skill_group_kind: SkillGroupKind) { if !self.contains_skill_group(skill_group_kind) { @@ -668,13 +679,13 @@ mod tests { skillset.add_skill_points(SkillGroupKind::Weapon(ToolKind::Axe), 1); skillset.unlock_skill(Skill::Axe(AxeSkill::UnlockLeap)); - assert_eq!(skillset.skill_groups[1].available_sp, 0); + assert_eq!(skillset.skill_groups[2].available_sp, 0); assert_eq!(skillset.skills.len(), 1); assert!(skillset.has_skill(Skill::Axe(AxeSkill::UnlockLeap))); skillset.refund_skill(Skill::Axe(AxeSkill::UnlockLeap)); - assert_eq!(skillset.skill_groups[1].available_sp, 1); + assert_eq!(skillset.skill_groups[2].available_sp, 1); assert_eq!(skillset.skills.get(&Skill::Axe(AxeSkill::UnlockLeap)), None); } @@ -683,9 +694,9 @@ mod tests { let mut skillset = SkillSet::default(); skillset.unlock_skill_group(SkillGroupKind::Weapon(ToolKind::Axe)); - assert_eq!(skillset.skill_groups.len(), 2); + assert_eq!(skillset.skill_groups.len(), 3); assert_eq!( - skillset.skill_groups[1], + skillset.skill_groups[2], SkillGroup::new(SkillGroupKind::Weapon(ToolKind::Axe)) ); } @@ -697,13 +708,13 @@ mod tests { skillset.unlock_skill_group(SkillGroupKind::Weapon(ToolKind::Axe)); skillset.add_skill_points(SkillGroupKind::Weapon(ToolKind::Axe), 1); - assert_eq!(skillset.skill_groups[1].available_sp, 1); + assert_eq!(skillset.skill_groups[2].available_sp, 1); assert_eq!(skillset.skills.len(), 0); // Try unlocking a skill with enough skill points skillset.unlock_skill(Skill::Axe(AxeSkill::UnlockLeap)); - assert_eq!(skillset.skill_groups[1].available_sp, 0); + assert_eq!(skillset.skill_groups[2].available_sp, 0); assert_eq!(skillset.skills.len(), 1); assert!(skillset.has_skill(Skill::Axe(AxeSkill::UnlockLeap))); @@ -720,6 +731,6 @@ mod tests { skillset.unlock_skill_group(SkillGroupKind::Weapon(ToolKind::Axe)); skillset.add_skill_points(SkillGroupKind::Weapon(ToolKind::Axe), 1); - assert_eq!(skillset.skill_groups[1].available_sp, 1); + assert_eq!(skillset.skill_groups[2].available_sp, 1); } } diff --git a/common/src/event.rs b/common/src/event.rs index 1c507d0876..d21eee05c5 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -164,6 +164,7 @@ pub enum ServerEvent { }, // Attempt to mine a block, turning it into an item MineBlock { + entity: EcsEntity, pos: Vec3, tool: Option, }, diff --git a/common/systems/src/melee.rs b/common/systems/src/melee.rs index 561b9a4b5b..46a3b004fd 100644 --- a/common/systems/src/melee.rs +++ b/common/systems/src/melee.rs @@ -86,6 +86,7 @@ impl<'a> System<'a> for Sys { < (rad + scale * melee_attack.range).powi(2) { server_emitter.emit(ServerEvent::MineBlock { + entity: attacker, pos: block_pos, tool, }); diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 926b439588..eb6af36349 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -2610,6 +2610,7 @@ fn parse_skill_tree(skill_tree: &str) -> CmdResult "bow" => Ok(SkillGroupKind::Weapon(ToolKind::Bow)), "staff" => Ok(SkillGroupKind::Weapon(ToolKind::Staff)), "sceptre" => Ok(SkillGroupKind::Weapon(ToolKind::Sceptre)), + "pick" => Ok(SkillGroupKind::Weapon(ToolKind::Pick)), _ => Err(format!("{} is not a skill group!", skill_tree)), } } diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 78b14d3517..c559ba19b6 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -992,13 +992,12 @@ fn handle_exp_gain( // Closure to add xp pool corresponding to weapon type equipped in a particular // EquipSlot let mut add_tool_from_slot = |equip_slot| { - let tool_kind = inventory.equipped(equip_slot).and_then(|i| { - if let ItemKind::Tool(tool) = &i.kind() { - Some(tool.kind) - } else { - None - } - }); + let tool_kind = inventory + .equipped(equip_slot) + .and_then(|i| match &i.kind() { + ItemKind::Tool(tool) if tool.kind.gains_combat_xp() => Some(tool.kind), + _ => None, + }); if let Some(weapon) = tool_kind { // Only adds to xp pools if entity has that skill group available if skill_set.contains_skill_group(SkillGroupKind::Weapon(weapon)) { diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index e8c4a5e0e9..3220a8a25b 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -3,6 +3,7 @@ use tracing::error; use vek::*; use common::{ + assets, comp::{ self, agent::{AgentEvent, Sound, MAX_LISTEN_DIST}, @@ -11,7 +12,7 @@ use common::{ item, slot::Slot, tool::ToolKind, - Inventory, Pos, + Inventory, Pos, SkillGroupKind, }, consts::{MAX_MOUNT_RANGE, SOUND_TRAVEL_DIST_PER_VOLUME}, outcome::Outcome, @@ -27,6 +28,10 @@ use crate::{ Server, }; +use hashbrown::HashMap; +use lazy_static::lazy_static; +use serde::Deserialize; + pub fn handle_lantern(server: &mut Server, entity: EcsEntity, enable: bool) { let ecs = server.state_mut().ecs(); @@ -281,13 +286,80 @@ fn within_mounting_range(player_position: Option<&Pos>, mount_position: Option<& } } -pub fn handle_mine_block(server: &mut Server, pos: Vec3, tool: Option) { +#[derive(Deserialize)] +struct ResourceExperienceManifest(HashMap); + +impl assets::Asset for ResourceExperienceManifest { + type Loader = assets::RonLoader; + + const EXTENSION: &'static str = "ron"; +} + +lazy_static! { + static ref RESOURCE_EXPERIENCE_MANIFEST: assets::AssetHandle = + assets::AssetExt::load_expect("server.manifests.resource_experience_manifest"); +} + +pub fn handle_mine_block( + server: &mut Server, + entity: EcsEntity, + pos: Vec3, + tool: Option, +) { let state = server.state_mut(); if state.can_set_block(pos) { let block = state.terrain().get(pos).ok().copied(); if let Some(block) = block.filter(|b| b.mine_tool().map_or(false, |t| Some(t) == tool)) { // Drop item if one is recoverable from the block - if let Some(item) = comp::Item::try_reclaim_from_block(block) { + if let Some(mut item) = comp::Item::try_reclaim_from_block(block) { + if let Some(mut skillset) = state + .ecs() + .write_storage::() + .get_mut(entity) + { + if let (Some(tool), Some(uid), Some(exp_reward)) = ( + tool, + state.ecs().uid_from_entity(entity), + RESOURCE_EXPERIENCE_MANIFEST + .read() + .0 + .get(item.item_definition_id()), + ) { + skillset.change_experience(SkillGroupKind::Weapon(tool), *exp_reward); + state + .ecs() + .write_resource::>() + .push(Outcome::ExpChange { + uid, + exp: *exp_reward, + }); + } + use common::comp::skills::{PickSkill, Skill}; + use rand::Rng; + let mut rng = rand::thread_rng(); + if item.item_definition_id().contains("mineral.ore.") + && rng.gen_bool( + 0.05 * skillset + .skill_level(Skill::Pick(PickSkill::OreGain)) + .ok() + .flatten() + .unwrap_or(0) as f64, + ) + { + let _ = item.increase_amount(1); + } + if item.item_definition_id().contains("mineral.gem.") + && rng.gen_bool( + 0.05 * skillset + .skill_level(Skill::Pick(PickSkill::GemGain)) + .ok() + .flatten() + .unwrap_or(0) as f64, + ) + { + let _ = item.increase_amount(1); + } + } state .create_object(Default::default(), comp::object::Body::Pouch) .with(comp::Pos(pos.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0))) diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index c808e8f28a..7079b3bda3 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -207,7 +207,9 @@ impl Server { handle_combo_change(&self, entity, change) }, ServerEvent::RequestSiteInfo { entity, id } => handle_site_info(&self, entity, id), - ServerEvent::MineBlock { pos, tool } => handle_mine_block(self, pos, tool), + ServerEvent::MineBlock { entity, pos, tool } => { + handle_mine_block(self, entity, pos, tool) + }, ServerEvent::TeleportTo { entity, target, diff --git a/server/src/migrations/V41__mining_tree.sql b/server/src/migrations/V41__mining_tree.sql new file mode 100644 index 0000000000..e4cd339df7 --- /dev/null +++ b/server/src/migrations/V41__mining_tree.sql @@ -0,0 +1,7 @@ +-- Every character should have the pick skilltree unlocked by default. +-- This is handled by `SkillSet::default()` for new characters (and their skill +-- sets serialize properly during character creation), but since the database +-- deserialization builds the SkillSet fields from empty Vecs/HashMaps, the skill +-- tree needs to manually be added to each character. +INSERT INTO skill_group (entity_id, skill_group_kind, exp, available_sp, earned_sp) + SELECT character_id, 'Weapon Pick', 0, 0, 0 FROM character; diff --git a/server/src/persistence/json_models.rs b/server/src/persistence/json_models.rs index 81d9531022..b74955ae46 100644 --- a/server/src/persistence/json_models.rs +++ b/server/src/persistence/json_models.rs @@ -40,8 +40,8 @@ pub fn skill_to_db_string(skill: comp::skills::Skill) -> String { use comp::{ item::tool::ToolKind, skills::{ - AxeSkill, BowSkill, ClimbSkill, GeneralSkill, HammerSkill, RollSkill, SceptreSkill, - Skill::*, SkillGroupKind, StaffSkill, SwimSkill, SwordSkill, + AxeSkill, BowSkill, ClimbSkill, GeneralSkill, HammerSkill, PickSkill, RollSkill, + SceptreSkill, Skill::*, SkillGroupKind, StaffSkill, SwimSkill, SwordSkill, }, }; let skill_string = match skill { @@ -135,6 +135,9 @@ pub fn skill_to_db_string(skill: comp::skills::Skill) -> String { Climb(ClimbSkill::Cost) => "Climb Cost", Climb(ClimbSkill::Speed) => "Climb Speed", Swim(SwimSkill::Speed) => "Swim Speed", + Pick(PickSkill::Speed) => "Pick Speed", + Pick(PickSkill::OreGain) => "Pick OreGain", + Pick(PickSkill::GemGain) => "Pick GemGain", UnlockGroup(SkillGroupKind::Weapon(ToolKind::Sword)) => "Unlock Weapon Sword", UnlockGroup(SkillGroupKind::Weapon(ToolKind::Axe)) => "Unlock Weapon Axe", UnlockGroup(SkillGroupKind::Weapon(ToolKind::Hammer)) => "Unlock Weapon Hammer", @@ -160,8 +163,8 @@ pub fn db_string_to_skill(skill_string: &str) -> comp::skills::Skill { use comp::{ item::tool::ToolKind, skills::{ - AxeSkill, BowSkill, ClimbSkill, GeneralSkill, HammerSkill, RollSkill, SceptreSkill, - Skill::*, SkillGroupKind, StaffSkill, SwimSkill, SwordSkill, + AxeSkill, BowSkill, ClimbSkill, GeneralSkill, HammerSkill, PickSkill, RollSkill, + SceptreSkill, Skill::*, SkillGroupKind, StaffSkill, SwimSkill, SwordSkill, }, }; match skill_string { @@ -255,6 +258,9 @@ pub fn db_string_to_skill(skill_string: &str) -> comp::skills::Skill { "Climb Cost" => Climb(ClimbSkill::Cost), "Climb Speed" => Climb(ClimbSkill::Speed), "Swim Speed" => Swim(SwimSkill::Speed), + "Pick Speed" => Pick(PickSkill::Speed), + "Pick GemGain" => Pick(PickSkill::GemGain), + "Pick OreGain" => Pick(PickSkill::OreGain), "Unlock Weapon Sword" => UnlockGroup(SkillGroupKind::Weapon(ToolKind::Sword)), "Unlock Weapon Axe" => UnlockGroup(SkillGroupKind::Weapon(ToolKind::Axe)), "Unlock Weapon Hammer" => UnlockGroup(SkillGroupKind::Weapon(ToolKind::Hammer)), @@ -280,12 +286,12 @@ pub fn skill_group_to_db_string(skill_group: comp::skills::SkillGroupKind) -> St Weapon(ToolKind::Bow) => "Weapon Bow", Weapon(ToolKind::Staff) => "Weapon Staff", Weapon(ToolKind::Sceptre) => "Weapon Sceptre", + Weapon(ToolKind::Pick) => "Weapon Pick", Weapon(ToolKind::Dagger) | Weapon(ToolKind::Shield) | Weapon(ToolKind::Spear) | Weapon(ToolKind::Debug) | Weapon(ToolKind::Farming) - | Weapon(ToolKind::Pick) | Weapon(ToolKind::Empty) | Weapon(ToolKind::Natural) => panic!( "Tried to add unsupported skill group to database: {:?}", @@ -305,6 +311,7 @@ pub fn db_string_to_skill_group(skill_group_string: &str) -> comp::skills::Skill "Weapon Bow" => Weapon(ToolKind::Bow), "Weapon Staff" => Weapon(ToolKind::Staff), "Weapon Sceptre" => Weapon(ToolKind::Sceptre), + "Weapon Pick" => Weapon(ToolKind::Pick), _ => panic!( "Tried to convert an unsupported string from the database: {}", skill_group_string diff --git a/voxygen/src/hud/diary.rs b/voxygen/src/hud/diary.rs index 2e679c6362..48182bda38 100644 --- a/voxygen/src/hud/diary.rs +++ b/voxygen/src/hud/diary.rs @@ -149,6 +149,11 @@ widget_ids! { skill_sceptre_aura_2, skill_sceptre_aura_3, skill_sceptre_aura_4, + pick_render, + skill_pick_m1, + skill_pick_m1_0, + skill_pick_m1_1, + skill_pick_m1_2, general_combat_render_0, general_combat_render_1, skill_general_stat_0, @@ -227,7 +232,7 @@ impl<'a> Diary<'a> { pub type SelectedSkillTree = skills::SkillGroupKind; -const TREES: [&str; 7] = [ +const TREES: [&str; 8] = [ "General Combat", "Sword", "Hammer", @@ -235,6 +240,7 @@ const TREES: [&str; 7] = [ "Sceptre", "Bow", "Fire Staff", + "Pickaxe", ]; pub enum Event { @@ -353,6 +359,7 @@ impl<'a> Widget for Diary<'a> { "Sceptre" => self.imgs.sceptre, "Bow" => self.imgs.bow, "Fire Staff" => self.imgs.staff, + "Pickaxe" => self.imgs.pickaxe, _ => self.imgs.nothing, }); @@ -500,6 +507,9 @@ impl<'a> Widget for Diary<'a> { SelectedSkillTree::Weapon(ToolKind::Staff) => { self.localized_strings.get("common.weapons.staff") }, + SelectedSkillTree::Weapon(ToolKind::Pick) => { + self.localized_strings.get("common.tool.pick") + }, _ => "Unknown", }; self.create_new_text(&tree_title, state.content_align, 2.0, 34, TEXT_COLOR) @@ -531,6 +541,7 @@ impl<'a> Widget for Diary<'a> { SelectedSkillTree::Weapon(ToolKind::Bow) => 6, SelectedSkillTree::Weapon(ToolKind::Staff) => 4, SelectedSkillTree::Weapon(ToolKind::Sceptre) => 5, + SelectedSkillTree::Weapon(ToolKind::Pick) => 4, _ => 0, }; let skills_top_r = match sel_tab { @@ -1976,6 +1987,65 @@ impl<'a> Widget for Diary<'a> { &diary_tooltip, ); }, + SelectedSkillTree::Weapon(ToolKind::Pick) => { + use skills::PickSkill::*; + // Pick + Image::new(animate_by_pulse( + &self + .item_imgs + .img_ids_or_not_found_img(Tool("example_pick".to_string())), + self.pulse, + )) + .wh(art_size) + .middle_of(state.content_align) + .color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0))) + .set(state.pick_render, ui); + // Top Left skills + // 5 1 6 + // 3 0 4 + // 8 2 7 + Button::image(self.imgs.pickaxe) + .w_h(74.0, 74.0) + .mid_top_with_margin_on(state.skills_top_l[0], 3.0) + .with_tooltip( + self.tooltip_manager, + &self.localized_strings.get("hud.skill.pick_strike_title"), + &self.localized_strings.get("hud.skill.pick_strike"), + &diary_tooltip, + TEXT_COLOR, + ) + .set(state.skill_pick_m1, ui); + self.create_unlock_skill_button( + Skill::Pick(Speed), + self.imgs.pickaxe_speed_skill, + state.skills_top_l[1], + "pick_strike_speed", + state.skill_pick_m1_0, + ui, + &mut events, + &diary_tooltip, + ); + self.create_unlock_skill_button( + Skill::Pick(OreGain), + self.imgs.pickaxe_oregain_skill, + state.skills_top_l[2], + "pick_strike_oregain", + state.skill_pick_m1_1, + ui, + &mut events, + &diary_tooltip, + ); + self.create_unlock_skill_button( + Skill::Pick(GemGain), + self.imgs.pickaxe_gemgain_skill, + state.skills_top_l[3], + "pick_strike_gemgain", + state.skill_pick_m1_2, + ui, + &mut events, + &diary_tooltip, + ); + }, _ => {}, } @@ -2034,6 +2104,7 @@ fn skill_tree_from_str(string: &str) -> Option { "Sceptre" => Some(SelectedSkillTree::Weapon(ToolKind::Sceptre)), "Bow" => Some(SelectedSkillTree::Weapon(ToolKind::Bow)), "Fire Staff" => Some(SelectedSkillTree::Weapon(ToolKind::Staff)), + "Pickaxe" => Some(SelectedSkillTree::Weapon(ToolKind::Pick)), _ => None, } } diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index f96bd8b610..e451dd3409 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -78,6 +78,7 @@ image_ids! { hammer: "voxygen.element.weapons.hammer", bow: "voxygen.element.weapons.bow", staff: "voxygen.element.weapons.staff", + pickaxe: "voxygen.element.weapons.pickaxe", lock: "voxygen.element.ui.diary.buttons.lock", wpn_icon_border_skills: "voxygen.element.ui.diary.buttons.border_skills", wpn_icon_border: "voxygen.element.ui.generic.buttons.border", @@ -303,6 +304,10 @@ image_ids! { utility_speed_skill: "voxygen.element.skills.skilltree.utility_speed", utility_duration_skill: "voxygen.element.skills.skilltree.utility_duration", + pickaxe_speed_skill: "voxygen.element.skills.pickaxe_speed", + pickaxe_oregain_skill: "voxygen.element.skills.pickaxe_oregain", + pickaxe_gemgain_skill: "voxygen.element.skills.pickaxe_gemgain", + // Skillbar level_up: "voxygen.element.ui.skillbar.level_up", bar_content: "voxygen.element.ui.skillbar.bar_content", diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 585361849d..90aef9563f 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -1353,6 +1353,7 @@ impl Hud { Weapon(ToolKind::Sceptre) => &i18n.get("common.weapons.sceptre"), Weapon(ToolKind::Bow) => &i18n.get("common.weapons.bow"), Weapon(ToolKind::Staff) => &i18n.get("common.weapons.staff"), + Weapon(ToolKind::Pick) => &i18n.get("common.tool.pick"), _ => "Unknown", }; Text::new(skill) From 43b6780c98d39edc944e17d3fd09b4b1ce7f0a64 Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Wed, 9 Jun 2021 01:35:30 -0400 Subject: [PATCH 050/180] Address MR 2406 comments. - Tweak ore xp values per Slipped's advice. --- assets/server/manifests/resource_experience_manifest.ron | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/server/manifests/resource_experience_manifest.ron b/assets/server/manifests/resource_experience_manifest.ron index 6345e0d392..c597adef8f 100644 --- a/assets/server/manifests/resource_experience_manifest.ron +++ b/assets/server/manifests/resource_experience_manifest.ron @@ -9,11 +9,11 @@ ResourceExperienceManifest({ "common.items.mineral.ore.coal": 25, "common.items.mineral.ore.gold": 100, "common.items.mineral.ore.iron": 20, - "common.items.mineral.ore.silver": 75, + "common.items.mineral.ore.silver": 90, "common.items.mineral.ore.velorite": 30, "common.items.mineral.ore.veloritefrag": 20, - "common.items.mineral.ore.bloodstone": 100, - "common.items.mineral.ore.cobalt": 75, + "common.items.mineral.ore.bloodstone": 80, + "common.items.mineral.ore.cobalt": 60, "common.items.mineral.ore.copper": 10, "common.items.mineral.ore.tin": 10, }) From 01a04a80fa2699961f2831d48b8213766fbaabbe Mon Sep 17 00:00:00 2001 From: Monty Marz Date: Wed, 9 Jun 2021 21:07:34 +0200 Subject: [PATCH 051/180] icons and naming --- .../element/{weapons => skills}/pickaxe.png | 0 assets/voxygen/element/weapons/axe.png | 4 ++-- assets/voxygen/element/weapons/bow.png | 4 ++-- assets/voxygen/element/weapons/daggers.png | 4 ++-- assets/voxygen/element/weapons/hammer.png | 4 ++-- assets/voxygen/element/weapons/mining.png | 3 +++ assets/voxygen/element/weapons/staff.png | 4 ++-- assets/voxygen/element/weapons/sword.png | 4 ++-- assets/voxygen/i18n/en/common.ron | 1 + assets/voxygen/i18n/en/skills.ron | 3 ++- common/src/comp/ability.rs | 2 +- common/src/comp/skills.rs | 4 ++-- server/src/events/interaction.rs | 6 +++--- server/src/persistence/json_models.rs | 16 ++++++++-------- voxygen/src/hud/diary.rs | 12 ++++++------ voxygen/src/hud/img_ids.rs | 3 ++- voxygen/src/hud/mod.rs | 3 ++- 17 files changed, 42 insertions(+), 35 deletions(-) rename assets/voxygen/element/{weapons => skills}/pickaxe.png (100%) create mode 100644 assets/voxygen/element/weapons/mining.png diff --git a/assets/voxygen/element/weapons/pickaxe.png b/assets/voxygen/element/skills/pickaxe.png similarity index 100% rename from assets/voxygen/element/weapons/pickaxe.png rename to assets/voxygen/element/skills/pickaxe.png diff --git a/assets/voxygen/element/weapons/axe.png b/assets/voxygen/element/weapons/axe.png index ee44e81671..79274d2f20 100644 --- a/assets/voxygen/element/weapons/axe.png +++ b/assets/voxygen/element/weapons/axe.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:37844f6d3ad21e036df2e8dd43bbb05b96728e00295084179a4841585ecabfef -size 168 +oid sha256:ad4f860670fef14577606c9b39f393484ecf6568cdf79d7d36b384fbf439eb90 +size 285 diff --git a/assets/voxygen/element/weapons/bow.png b/assets/voxygen/element/weapons/bow.png index d147b3e33f..136bfa3d86 100644 --- a/assets/voxygen/element/weapons/bow.png +++ b/assets/voxygen/element/weapons/bow.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a1380273d271639ff7d3016fe10331f5847e7e2339f6bbcb2eb7f9de1f714d1 -size 169 +oid sha256:8fb101f42ea28b599fdfab86ca06c59c46edd03fa4c854230465bd04f93a9d11 +size 294 diff --git a/assets/voxygen/element/weapons/daggers.png b/assets/voxygen/element/weapons/daggers.png index 2a5671b5c7..e2b5dd7c19 100644 --- a/assets/voxygen/element/weapons/daggers.png +++ b/assets/voxygen/element/weapons/daggers.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7018ea3f92db46e0e2fde2e28b8c023156d52dd7445611bb189c996a067844dd -size 183 +oid sha256:ee65025e8edaafaf00d43fe4f0af74e15bd9263bd9f399f60ca8b87ba807aa9b +size 319 diff --git a/assets/voxygen/element/weapons/hammer.png b/assets/voxygen/element/weapons/hammer.png index c96bcd6a1a..16990f4088 100644 --- a/assets/voxygen/element/weapons/hammer.png +++ b/assets/voxygen/element/weapons/hammer.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6244d3f8f071eff3a3ac3413bd4b20f4c0c555dc8e656574bc0de25d166fb741 -size 157 +oid sha256:c23362300e7b0cddf081fc664ec2cc2a8accf0a6af87129cf853020c238946de +size 276 diff --git a/assets/voxygen/element/weapons/mining.png b/assets/voxygen/element/weapons/mining.png new file mode 100644 index 0000000000..e6b4f1a2b2 --- /dev/null +++ b/assets/voxygen/element/weapons/mining.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4d40df9df229492a46afaa7236177d6f0ba0536e9e1bae4c4a46063ee588187a +size 435 diff --git a/assets/voxygen/element/weapons/staff.png b/assets/voxygen/element/weapons/staff.png index 3455ac846a..4ca0d49763 100644 --- a/assets/voxygen/element/weapons/staff.png +++ b/assets/voxygen/element/weapons/staff.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0b7ee9205baca5c27bfa681ef0fe64ca49b2f326265777522df15f503a4b0b3d -size 149 +oid sha256:bda8b960270c230a49bb4e29f9f65598f08359672c8c5256b1efea8015811e8b +size 279 diff --git a/assets/voxygen/element/weapons/sword.png b/assets/voxygen/element/weapons/sword.png index d76d41b7e2..2ebd2b3293 100644 --- a/assets/voxygen/element/weapons/sword.png +++ b/assets/voxygen/element/weapons/sword.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:46b2aaa0d65ad5e645c460ec76029cdc42aedf7ceb5b1840e2a9e8885b57c75d -size 158 +oid sha256:1c293dfb161fd9c99670913ac96c55103907fb0dd9e8812f81406a8acda51b59 +size 288 diff --git a/assets/voxygen/i18n/en/common.ron b/assets/voxygen/i18n/en/common.ron index 6694770192..1dd6bf2c10 100644 --- a/assets/voxygen/i18n/en/common.ron +++ b/assets/voxygen/i18n/en/common.ron @@ -78,6 +78,7 @@ Is the client up to date?"#, "common.tool.debug": "Debug", "common.tool.faming": "Farming Tool", "common.tool.pick": "Pickaxe", + "common.tool.mining": "Mining", "common.kind.modular_component": "Modular Component", "common.kind.glider": "Glider", "common.kind.consumable": "Consumable", diff --git a/assets/voxygen/i18n/en/skills.ron b/assets/voxygen/i18n/en/skills.ron index 66250941ff..1a4d6ab3b8 100644 --- a/assets/voxygen/i18n/en/skills.ron +++ b/assets/voxygen/i18n/en/skills.ron @@ -238,7 +238,8 @@ "hud.skill.axe_leap_cost": "Decreases cost of leap by 25%{SP}", "hud.skill.axe_leap_distance_title": "Leap Distance", "hud.skill.axe_leap_distance": "Increases distance of leap by 20%{SP}", - // Pick + // Mining + "hud.skill.mining_title": "Mining", "hud.skill.pick_strike_title": "Pickaxe Strike", "hud.skill.pick_strike": "Hit rocks with the pickaxe to gain ore and gems and experience", "hud.skill.pick_strike_speed_title": "Pickaxe Strike Speed", diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index bab3f56117..5fa8c3c112 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -1175,7 +1175,7 @@ impl CharacterAbility { } }, Some(ToolKind::Pick) => { - use skills::PickSkill::*; + use skills::MiningSkill::*; if let BasicMelee { ref mut buildup_duration, ref mut swing_duration, diff --git a/common/src/comp/skills.rs b/common/src/comp/skills.rs index c130b067c9..bd7e999f04 100644 --- a/common/src/comp/skills.rs +++ b/common/src/comp/skills.rs @@ -107,7 +107,7 @@ pub enum Skill { Roll(RollSkill), Climb(ClimbSkill), Swim(SwimSkill), - Pick(PickSkill), + Pick(MiningSkill), } pub enum SkillError { @@ -265,7 +265,7 @@ pub enum SwimSkill { } #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] -pub enum PickSkill { +pub enum MiningSkill { Speed, OreGain, GemGain, diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index 3220a8a25b..9653ef4fc1 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -334,13 +334,13 @@ pub fn handle_mine_block( exp: *exp_reward, }); } - use common::comp::skills::{PickSkill, Skill}; + use common::comp::skills::{MiningSkill, Skill}; use rand::Rng; let mut rng = rand::thread_rng(); if item.item_definition_id().contains("mineral.ore.") && rng.gen_bool( 0.05 * skillset - .skill_level(Skill::Pick(PickSkill::OreGain)) + .skill_level(Skill::Pick(MiningSkill::OreGain)) .ok() .flatten() .unwrap_or(0) as f64, @@ -351,7 +351,7 @@ pub fn handle_mine_block( if item.item_definition_id().contains("mineral.gem.") && rng.gen_bool( 0.05 * skillset - .skill_level(Skill::Pick(PickSkill::GemGain)) + .skill_level(Skill::Pick(MiningSkill::GemGain)) .ok() .flatten() .unwrap_or(0) as f64, diff --git a/server/src/persistence/json_models.rs b/server/src/persistence/json_models.rs index b74955ae46..d359cc4672 100644 --- a/server/src/persistence/json_models.rs +++ b/server/src/persistence/json_models.rs @@ -40,7 +40,7 @@ pub fn skill_to_db_string(skill: comp::skills::Skill) -> String { use comp::{ item::tool::ToolKind, skills::{ - AxeSkill, BowSkill, ClimbSkill, GeneralSkill, HammerSkill, PickSkill, RollSkill, + AxeSkill, BowSkill, ClimbSkill, GeneralSkill, HammerSkill, MiningSkill, RollSkill, SceptreSkill, Skill::*, SkillGroupKind, StaffSkill, SwimSkill, SwordSkill, }, }; @@ -135,9 +135,9 @@ pub fn skill_to_db_string(skill: comp::skills::Skill) -> String { Climb(ClimbSkill::Cost) => "Climb Cost", Climb(ClimbSkill::Speed) => "Climb Speed", Swim(SwimSkill::Speed) => "Swim Speed", - Pick(PickSkill::Speed) => "Pick Speed", - Pick(PickSkill::OreGain) => "Pick OreGain", - Pick(PickSkill::GemGain) => "Pick GemGain", + Pick(MiningSkill::Speed) => "Pick Speed", + Pick(MiningSkill::OreGain) => "Pick OreGain", + Pick(MiningSkill::GemGain) => "Pick GemGain", UnlockGroup(SkillGroupKind::Weapon(ToolKind::Sword)) => "Unlock Weapon Sword", UnlockGroup(SkillGroupKind::Weapon(ToolKind::Axe)) => "Unlock Weapon Axe", UnlockGroup(SkillGroupKind::Weapon(ToolKind::Hammer)) => "Unlock Weapon Hammer", @@ -163,7 +163,7 @@ pub fn db_string_to_skill(skill_string: &str) -> comp::skills::Skill { use comp::{ item::tool::ToolKind, skills::{ - AxeSkill, BowSkill, ClimbSkill, GeneralSkill, HammerSkill, PickSkill, RollSkill, + AxeSkill, BowSkill, ClimbSkill, GeneralSkill, HammerSkill, MiningSkill, RollSkill, SceptreSkill, Skill::*, SkillGroupKind, StaffSkill, SwimSkill, SwordSkill, }, }; @@ -258,9 +258,9 @@ pub fn db_string_to_skill(skill_string: &str) -> comp::skills::Skill { "Climb Cost" => Climb(ClimbSkill::Cost), "Climb Speed" => Climb(ClimbSkill::Speed), "Swim Speed" => Swim(SwimSkill::Speed), - "Pick Speed" => Pick(PickSkill::Speed), - "Pick GemGain" => Pick(PickSkill::GemGain), - "Pick OreGain" => Pick(PickSkill::OreGain), + "Pick Speed" => Pick(MiningSkill::Speed), + "Pick GemGain" => Pick(MiningSkill::GemGain), + "Pick OreGain" => Pick(MiningSkill::OreGain), "Unlock Weapon Sword" => UnlockGroup(SkillGroupKind::Weapon(ToolKind::Sword)), "Unlock Weapon Axe" => UnlockGroup(SkillGroupKind::Weapon(ToolKind::Axe)), "Unlock Weapon Hammer" => UnlockGroup(SkillGroupKind::Weapon(ToolKind::Hammer)), diff --git a/voxygen/src/hud/diary.rs b/voxygen/src/hud/diary.rs index 48182bda38..96a0cb6fc7 100644 --- a/voxygen/src/hud/diary.rs +++ b/voxygen/src/hud/diary.rs @@ -240,7 +240,7 @@ const TREES: [&str; 8] = [ "Sceptre", "Bow", "Fire Staff", - "Pickaxe", + "Mining", ]; pub enum Event { @@ -359,7 +359,7 @@ impl<'a> Widget for Diary<'a> { "Sceptre" => self.imgs.sceptre, "Bow" => self.imgs.bow, "Fire Staff" => self.imgs.staff, - "Pickaxe" => self.imgs.pickaxe, + "Mining" => self.imgs.mining, _ => self.imgs.nothing, }); @@ -508,7 +508,7 @@ impl<'a> Widget for Diary<'a> { self.localized_strings.get("common.weapons.staff") }, SelectedSkillTree::Weapon(ToolKind::Pick) => { - self.localized_strings.get("common.tool.pick") + self.localized_strings.get("common.tool.mining") }, _ => "Unknown", }; @@ -1988,8 +1988,8 @@ impl<'a> Widget for Diary<'a> { ); }, SelectedSkillTree::Weapon(ToolKind::Pick) => { - use skills::PickSkill::*; - // Pick + use skills::MiningSkill::*; + // Mining Image::new(animate_by_pulse( &self .item_imgs @@ -2104,7 +2104,7 @@ fn skill_tree_from_str(string: &str) -> Option { "Sceptre" => Some(SelectedSkillTree::Weapon(ToolKind::Sceptre)), "Bow" => Some(SelectedSkillTree::Weapon(ToolKind::Bow)), "Fire Staff" => Some(SelectedSkillTree::Weapon(ToolKind::Staff)), - "Pickaxe" => Some(SelectedSkillTree::Weapon(ToolKind::Pick)), + "Mining" => Some(SelectedSkillTree::Weapon(ToolKind::Pick)), _ => None, } } diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index e451dd3409..2a48f5e028 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -78,7 +78,8 @@ image_ids! { hammer: "voxygen.element.weapons.hammer", bow: "voxygen.element.weapons.bow", staff: "voxygen.element.weapons.staff", - pickaxe: "voxygen.element.weapons.pickaxe", + mining: "voxygen.element.weapons.mining", + pickaxe: "voxygen.element.skills.pickaxe", lock: "voxygen.element.ui.diary.buttons.lock", wpn_icon_border_skills: "voxygen.element.ui.diary.buttons.border_skills", wpn_icon_border: "voxygen.element.ui.generic.buttons.border", diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 90aef9563f..605833dd5f 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -1353,7 +1353,7 @@ impl Hud { Weapon(ToolKind::Sceptre) => &i18n.get("common.weapons.sceptre"), Weapon(ToolKind::Bow) => &i18n.get("common.weapons.bow"), Weapon(ToolKind::Staff) => &i18n.get("common.weapons.staff"), - Weapon(ToolKind::Pick) => &i18n.get("common.tool.pick"), + Weapon(ToolKind::Pick) => &i18n.get("common.tool.mining"), _ => "Unknown", }; Text::new(skill) @@ -1378,6 +1378,7 @@ impl Hud { Weapon(ToolKind::Sceptre) => self.imgs.sceptre, Weapon(ToolKind::Bow) => self.imgs.bow, Weapon(ToolKind::Staff) => self.imgs.staff, + Weapon(ToolKind::Pick) => self.imgs.mining, _ => self.imgs.swords_crossed, }) .w_h(20.0, 20.0) From 9d4a65e8ac0117a83fd8518785619575e09acd6d Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Wed, 9 Jun 2021 16:03:25 -0400 Subject: [PATCH 052/180] Adjust mining xp numbers and add SkillGroupKind information to Outcome::ExpChange. --- .../resource_experience_manifest.ron | 32 +++++++++---------- common/src/outcome.rs | 4 ++- server/src/events/entity_manipulation.rs | 5 +-- server/src/events/interaction.rs | 4 ++- voxygen/src/hud/mod.rs | 9 ++++-- 5 files changed, 31 insertions(+), 23 deletions(-) diff --git a/assets/server/manifests/resource_experience_manifest.ron b/assets/server/manifests/resource_experience_manifest.ron index c597adef8f..829cea21c8 100644 --- a/assets/server/manifests/resource_experience_manifest.ron +++ b/assets/server/manifests/resource_experience_manifest.ron @@ -1,19 +1,19 @@ ResourceExperienceManifest({ - "common.items.mineral.gem.amethyst": 20, - "common.items.mineral.gem.sapphire": 50, - "common.items.mineral.gem.topaz": 20, - "common.items.mineral.gem.diamond": 100, - "common.items.mineral.gem.emerald": 50, - "common.items.mineral.gem.ruby": 75, + "common.items.mineral.gem.amethyst": 4, + "common.items.mineral.gem.sapphire": 8, + "common.items.mineral.gem.topaz": 4, + "common.items.mineral.gem.diamond": 25, + "common.items.mineral.gem.emerald": 10, + "common.items.mineral.gem.ruby": 12, - "common.items.mineral.ore.coal": 25, - "common.items.mineral.ore.gold": 100, - "common.items.mineral.ore.iron": 20, - "common.items.mineral.ore.silver": 90, - "common.items.mineral.ore.velorite": 30, - "common.items.mineral.ore.veloritefrag": 20, - "common.items.mineral.ore.bloodstone": 80, - "common.items.mineral.ore.cobalt": 60, - "common.items.mineral.ore.copper": 10, - "common.items.mineral.ore.tin": 10, + "common.items.mineral.ore.coal": 6, + "common.items.mineral.ore.gold": 25, + "common.items.mineral.ore.iron": 4, + "common.items.mineral.ore.silver": 22, + "common.items.mineral.ore.velorite": 8, + "common.items.mineral.ore.veloritefrag": 4, + "common.items.mineral.ore.bloodstone": 20, + "common.items.mineral.ore.cobalt": 15, + "common.items.mineral.ore.copper": 3, + "common.items.mineral.ore.tin": 3, }) diff --git a/common/src/outcome.rs b/common/src/outcome.rs index 559139d23a..d1ec35e15c 100644 --- a/common/src/outcome.rs +++ b/common/src/outcome.rs @@ -1,5 +1,6 @@ use crate::{comp, uid::Uid}; -use comp::{beam, item::Reagent, poise::PoiseState}; +use comp::{beam, item::Reagent, poise::PoiseState, skills::SkillGroupKind}; +use hashbrown::HashSet; use serde::{Deserialize, Serialize}; use vek::*; @@ -36,6 +37,7 @@ pub enum Outcome { ExpChange { uid: Uid, exp: i32, + xp_pools: HashSet, }, SkillPointGain { uid: Uid, diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index c559ba19b6..15dd48bae3 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -1011,12 +1011,13 @@ fn handle_exp_gain( add_tool_from_slot(EquipSlot::InactiveMainhand); add_tool_from_slot(EquipSlot::InactiveOffhand); let num_pools = xp_pools.len() as f32; - for pool in xp_pools { - skill_set.change_experience(pool, (exp_reward / num_pools).ceil() as i32); + for pool in xp_pools.iter() { + skill_set.change_experience(*pool, (exp_reward / num_pools).ceil() as i32); } outcomes.push(Outcome::ExpChange { uid: *uid, exp: exp_reward as i32, + xp_pools, }); } diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index 9653ef4fc1..03b5fe992a 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -28,9 +28,10 @@ use crate::{ Server, }; -use hashbrown::HashMap; +use hashbrown::{HashMap, HashSet}; use lazy_static::lazy_static; use serde::Deserialize; +use std::iter::FromIterator; pub fn handle_lantern(server: &mut Server, entity: EcsEntity, enable: bool) { let ecs = server.state_mut().ecs(); @@ -332,6 +333,7 @@ pub fn handle_mine_block( .push(Outcome::ExpChange { uid, exp: *exp_reward, + xp_pools: HashSet::from_iter(vec![SkillGroupKind::Weapon(tool)]), }); } use common::comp::skills::{MiningSkill, Skill}; diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 605833dd5f..b2944c0f39 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -1353,7 +1353,7 @@ impl Hud { Weapon(ToolKind::Sceptre) => &i18n.get("common.weapons.sceptre"), Weapon(ToolKind::Bow) => &i18n.get("common.weapons.bow"), Weapon(ToolKind::Staff) => &i18n.get("common.weapons.staff"), - Weapon(ToolKind::Pick) => &i18n.get("common.tool.mining"), + Weapon(ToolKind::Pick) => &i18n.get("common.tool.pick"), _ => "Unknown", }; Text::new(skill) @@ -1378,7 +1378,6 @@ impl Hud { Weapon(ToolKind::Sceptre) => self.imgs.sceptre, Weapon(ToolKind::Bow) => self.imgs.bow, Weapon(ToolKind::Staff) => self.imgs.staff, - Weapon(ToolKind::Pick) => self.imgs.mining, _ => self.imgs.swords_crossed, }) .w_h(20.0, 20.0) @@ -3659,7 +3658,11 @@ impl Hud { pub fn handle_outcome(&mut self, outcome: &Outcome) { match outcome { - Outcome::ExpChange { uid, exp } => self.floaters.exp_floaters.push(ExpFloater { + Outcome::ExpChange { + uid, + exp, + xp_pools: _, + } => self.floaters.exp_floaters.push(ExpFloater { owner: *uid, exp_change: *exp, timer: 4.0, From 45f79059d0c0623ebc37411491e11b7ab5f2a88f Mon Sep 17 00:00:00 2001 From: Monty Marz Date: Thu, 10 Jun 2021 02:23:27 +0200 Subject: [PATCH 053/180] mining exp sct color and icon; add mining to skill_preset --- assets/server/manifests/presets.ron | 4 ++ assets/voxygen/element/weapons/pickaxe.png | 3 ++ server/src/cmd.rs | 2 +- voxygen/src/hud/img_ids.rs | 1 + voxygen/src/hud/mod.rs | 48 +++++++++++++++------- 5 files changed, 43 insertions(+), 15 deletions(-) create mode 100644 assets/voxygen/element/weapons/pickaxe.png diff --git a/assets/server/manifests/presets.ron b/assets/server/manifests/presets.ron index 615af7a8da..136ef46054 100644 --- a/assets/server/manifests/presets.ron +++ b/assets/server/manifests/presets.ron @@ -127,6 +127,10 @@ (Sceptre(ADuration), 2), (Sceptre(ARange), 2), (Sceptre(ACost), 2), + // Mining + (Pick(Speed), 3), + (Pick(OreGain), 3), + (Pick(GemGain), 3), ], // Just copypasta from max with random reductions "middle": [ diff --git a/assets/voxygen/element/weapons/pickaxe.png b/assets/voxygen/element/weapons/pickaxe.png new file mode 100644 index 0000000000..9346118046 --- /dev/null +++ b/assets/voxygen/element/weapons/pickaxe.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2a179c4898dabc76a0f0ebd660fb8c979f2548a9bd367c71b91a9eb06676ae23 +size 466 diff --git a/server/src/cmd.rs b/server/src/cmd.rs index eb6af36349..e2499761af 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -2610,7 +2610,7 @@ fn parse_skill_tree(skill_tree: &str) -> CmdResult "bow" => Ok(SkillGroupKind::Weapon(ToolKind::Bow)), "staff" => Ok(SkillGroupKind::Weapon(ToolKind::Staff)), "sceptre" => Ok(SkillGroupKind::Weapon(ToolKind::Sceptre)), - "pick" => Ok(SkillGroupKind::Weapon(ToolKind::Pick)), + "mining" => Ok(SkillGroupKind::Weapon(ToolKind::Pick)), _ => Err(format!("{} is not a skill group!", skill_tree)), } } diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index 2a48f5e028..6b5b5cfec0 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -80,6 +80,7 @@ image_ids! { staff: "voxygen.element.weapons.staff", mining: "voxygen.element.weapons.mining", pickaxe: "voxygen.element.skills.pickaxe", + pickaxe_ico: "voxygen.element.weapons.pickaxe", lock: "voxygen.element.ui.diary.buttons.lock", wpn_icon_border_skills: "voxygen.element.ui.diary.buttons.border_skills", wpn_icon_border: "voxygen.element.ui.generic.buttons.border", diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index b2944c0f39..a1501f6a2c 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -98,7 +98,7 @@ use conrod_core::{ widget::{self, Button, Image, Text}, widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, }; -use hashbrown::HashMap; +use hashbrown::{HashMap, HashSet}; use rand::Rng; use specs::{Entity as EcsEntity, Join, WorldExt}; use std::{ @@ -211,6 +211,7 @@ widget_ids! { player_rank_up_icon, sct_exp_bgs[], sct_exps[], + sct_exp_icons[], sct_lvl_bg, sct_lvl, hurt_bg, @@ -324,6 +325,7 @@ pub struct ExpFloater { pub exp_change: i32, pub timer: f32, pub rand_offset: (f32, f32), + pub xp_pools: HashSet, } pub struct SkillPointGain { @@ -1252,7 +1254,11 @@ impl Hud { &mut self.ids.player_scts, &mut ui_widgets.widget_id_generator(), ); - // Increase font size based on fraction of maximum health + let player_sct_icon_id = player_sct_id_walker.next( + &mut self.ids.player_scts, + &mut ui_widgets.widget_id_generator(), + ); + // Increase font size based on fraction of maximum Experience // "flashes" by having a larger size in the first 100ms let font_size_xp = 30 + ((floater.exp_change as f32 / 300.0).min(1.0) * 50.0) as u32; @@ -1266,6 +1272,7 @@ impl Hud { }; if floater.exp_change > 0 { + let xp_pool = &floater.xp_pools; // Don't show 0 Exp Text::new(&format!("{} Exp", floater.exp_change.max(1))) .font_size(font_size_xp) @@ -1280,12 +1287,25 @@ impl Hud { Text::new(&format!("{} Exp", floater.exp_change.max(1))) .font_size(font_size_xp) .font_id(self.fonts.cyri.conrod_id) - .color(Color::Rgba(0.59, 0.41, 0.67, fade)) + .color( + if xp_pool.contains(&SkillGroupKind::Weapon(ToolKind::Pick)) { + Color::Rgba(0.18, 0.32, 0.9, fade) + } else { + Color::Rgba(0.59, 0.41, 0.67, fade) + }, + ) .x_y( ui_widgets.win_w * (0.5 * floater.rand_offset.0 as f64 - 0.25), ui_widgets.win_h * (0.15 * floater.rand_offset.1 as f64) + y, ) .set(player_sct_id, ui_widgets); + // Exp Source Image + if xp_pool.contains(&SkillGroupKind::Weapon(ToolKind::Pick)) { + Image::new(self.imgs.pickaxe_ico) + .w_h(font_size_xp as f64, font_size_xp as f64) + .left_from(player_sct_id, 5.0) + .set(player_sct_icon_id, ui_widgets); + } } } } @@ -1353,7 +1373,7 @@ impl Hud { Weapon(ToolKind::Sceptre) => &i18n.get("common.weapons.sceptre"), Weapon(ToolKind::Bow) => &i18n.get("common.weapons.bow"), Weapon(ToolKind::Staff) => &i18n.get("common.weapons.staff"), - Weapon(ToolKind::Pick) => &i18n.get("common.tool.pick"), + Weapon(ToolKind::Pick) => &i18n.get("common.tool.mining"), _ => "Unknown", }; Text::new(skill) @@ -1378,6 +1398,7 @@ impl Hud { Weapon(ToolKind::Sceptre) => self.imgs.sceptre, Weapon(ToolKind::Bow) => self.imgs.bow, Weapon(ToolKind::Staff) => self.imgs.staff, + Weapon(ToolKind::Pick) => self.imgs.mining, _ => self.imgs.swords_crossed, }) .w_h(20.0, 20.0) @@ -3658,16 +3679,15 @@ impl Hud { pub fn handle_outcome(&mut self, outcome: &Outcome) { match outcome { - Outcome::ExpChange { - uid, - exp, - xp_pools: _, - } => self.floaters.exp_floaters.push(ExpFloater { - owner: *uid, - exp_change: *exp, - timer: 4.0, - rand_offset: rand::thread_rng().gen::<(f32, f32)>(), - }), + Outcome::ExpChange { uid, exp, xp_pools } => { + self.floaters.exp_floaters.push(ExpFloater { + owner: *uid, + exp_change: *exp, + timer: 4.0, + rand_offset: rand::thread_rng().gen::<(f32, f32)>(), + xp_pools: xp_pools.clone(), + }) + }, Outcome::SkillPointGain { uid, skill_tree, From 104de523b7d9728f69b5ea376c474c242434447b Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Sun, 13 Jun 2021 14:35:44 -0400 Subject: [PATCH 054/180] Move changelog entry for mining to 0.10 section. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99fa93a4cc..d42eaa89b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- Added a skill tree for mining, which gains xp from mining ores and gems. ### Changed @@ -85,7 +86,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Meat drops from animals - New ores, plants and hides to be looted from the world and processed into craft ingredients - Added more crafting stations, loom, spinning wheel, tanning rack, forge -- Added a skill tree for mining, which gains xp from mining ores and gems. ### Changed From 1af9ac568f0abd43028158bd2f98144c88a5e885 Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Sun, 13 Jun 2021 20:38:03 -0400 Subject: [PATCH 055/180] Move force-movement e2e check so that it doesn't confer immunity to arrows. --- common/systems/src/phys.rs | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/common/systems/src/phys.rs b/common/systems/src/phys.rs index 1f4521496c..613431035c 100644 --- a/common/systems/src/phys.rs +++ b/common/systems/src/phys.rs @@ -333,21 +333,6 @@ impl<'a> PhysicsData<'a> { }; } - // Don't apply e2e pushback to entities that are in a forced movement state - // (e.g. roll, leapmelee). This allows leaps to work properly (since you won't - // get pushed away before delivering the hit), and allows rolling through an - // enemy when trapped (e.g. with minotaur). This allows using e2e pushback to - // gain speed by jumping out of a roll while in the middle of a collider, this - // is an intentional combat mechanic. - if let Some(cs) = char_state_maybe { - if cs.is_forced_movement() { - return PhysicsMetrics { - entity_entity_collision_checks, - entity_entity_collisions, - }; - } - } - let z_limits = calc_z_limit(char_state_maybe, collider); // Resets touch_entities in physics @@ -440,16 +425,21 @@ impl<'a> PhysicsData<'a> { entity_entity_collisions += 1; } - // Don't apply repulsive force to projectiles or if - // we're - // colliding - // with a terrain-like entity, or if we are a - // terrain-like - // entity + // Don't apply e2e pushback to entities that are in a forced movement state + // (e.g. roll, leapmelee). This allows leaps to work properly (since you won't + // get pushed away before delivering the hit), and allows rolling through an + // enemy when trapped (e.g. with minotaur). This allows using e2e pushback to + // gain speed by jumping out of a roll while in the middle of a collider, this + // is an intentional combat mechanic. + let forced_movement = matches!(char_state_maybe, Some(cs) if cs.is_forced_movement()); + + // Don't apply repulsive force to projectiles or if we're colliding with a + // terrain-like entity, or if we are a terrain-like entity // // Don't apply force when entity is a sticky which is on the // ground (or on the wall) - if !(is_sticky && !is_mid_air) + if !forced_movement && + !(is_sticky && !is_mid_air) && diff.magnitude_squared() > 0.0 && !is_projectile && !matches!( From d5cbe27612a08312d72fcf9e297ecffba07b095c Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Mon, 14 Jun 2021 13:55:25 +0300 Subject: [PATCH 056/180] Responding to review - make `skillset_builder::Preset` void enum and left comment about how to extend it - add `.with_default_equipment()` in case if preset is missing to `basic_summon` loadout creation to match old `build_loadout()` behaviour --- common/src/skillset_builder.rs | 14 +++++--------- common/src/states/basic_summon.rs | 3 ++- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/common/src/skillset_builder.rs b/common/src/skillset_builder.rs index 8fddd2b7b1..4f5726b1d2 100644 --- a/common/src/skillset_builder.rs +++ b/common/src/skillset_builder.rs @@ -6,10 +6,11 @@ use crate::assets::{self, AssetExt}; use serde::{Deserialize, Serialize}; use tracing::warn; +/// `SkillSetBuilder` preset. Consider using loading from assets, when possible. +/// When you're adding new enum variant, +/// handle it in [`with_preset`](SkillSetBuilder::with_preset) method #[derive(Copy, Clone, PartialEq, Serialize, Deserialize, Debug)] -pub enum Preset { - Empty, -} +pub enum Preset {} #[derive(Debug, Deserialize, Clone)] struct SkillSetTree(Vec); @@ -89,12 +90,7 @@ impl SkillSetBuilder { /// Applies preset #[must_use] - pub const fn with_preset(self, preset: Preset) -> Self { - match preset { - Preset::Empty => {}, - } - self - } + pub const fn with_preset(self, _preset: Preset) -> Self { self } #[must_use] /// # Panics diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 1c094b8883..6a49e33c60 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -84,10 +84,11 @@ impl CharacterBehavior for Data { let loadout = { let loadout_builder = LoadoutBuilder::new().with_default_maintool(&body); + // If preset is none, use default equipment if let Some(preset) = loadout_config { loadout_builder.with_preset(preset).build() } else { - loadout_builder.build() + loadout_builder.with_default_equipment(&body).build() } }; From a77b156cd2ecbffce832e99b9eeb14ac23a4192f Mon Sep 17 00:00:00 2001 From: Vincent Junge Date: Mon, 31 May 2021 16:34:31 +0200 Subject: [PATCH 057/180] Added dungeon shape to MagicaVoxel export example --- world/examples/dungeon_voxel_export.rs | 209 +++++++++++++++++++++++++ world/src/site/dungeon/mod.rs | 53 +++---- 2 files changed, 234 insertions(+), 28 deletions(-) create mode 100644 world/examples/dungeon_voxel_export.rs diff --git a/world/examples/dungeon_voxel_export.rs b/world/examples/dungeon_voxel_export.rs new file mode 100644 index 0000000000..3f0b58d783 --- /dev/null +++ b/world/examples/dungeon_voxel_export.rs @@ -0,0 +1,209 @@ +use std::{ + collections::HashMap, + fs::File, + io::{prelude::*, SeekFrom}, +}; +type Result = std::io::Result<()>; + +use common::{ + terrain::{Block, BlockKind}, + vol::{BaseVol, ReadVol, RectSizedVol, WriteVol}, +}; +use vek::{Vec2, Vec3}; +use veloren_world::{index::Index, IndexOwned}; + +/// This exports a dungeon (structure only, no entities or sprites) to a +/// MagicaVoxel .vox file + +fn main() -> Result { + let export_path = "dungeon.vox"; + let seed = 0; + + println!("Saving into {}", export_path); + let mut volume = ExportVol::new(); + let index = IndexOwned::new(Index::new(seed)); + let dungeon = veloren_world::site::Dungeon::generate( + volume.size_xy().map(|p| p as i32 / 2), + None, + &mut rand::thread_rng(), + ); + dungeon.apply_to(index.as_index_ref(), Vec2::new(0, 0), |_| None, &mut volume); + volume.write(&mut File::create(export_path)?) +} + +struct ExportVol { + models: HashMap, Vec>, + width: i32, + default_block: Block, +} + +impl ExportVol { + const CHUNK_SIZE: i32 = 256; + + fn new() -> Self { + Self { + models: HashMap::new(), + width: 1000, + default_block: Block::empty(), + } + } + + fn write(&self, file: &mut File) -> Result { + // We need to split the structure into multiple models if it's too big + // However, the create_vox crate doesn't yet work with scene graphs + // Luckily, writing vox files is easy enough + // File format defined at https://github.com/ephtracy/voxel-model + + fn write_i32(file: &mut File, value: i32) -> Result { + // The spec doesn't specify endianess?!? + file.write_all(&value.to_le_bytes()) + } + + fn write_chunk( + file: &mut File, + name: &str, + write_body: &dyn Fn(&mut File) -> Result, + ) -> Result { + file.write_all(name.as_bytes())?; + write_i32(file, 28)?; // Chunk size (unknown at this point) + write_i32(file, 0)?; // Size of child chunks + let chunk_start = file.stream_position()?; + write_body(file)?; + let chunk_end = file.stream_position()?; + file.seek(SeekFrom::Start(chunk_start - 8))?; + write_i32(file, chunk_end as i32 - chunk_start as i32)?; + file.seek(SeekFrom::Start(chunk_end))?; + Ok(()) + } + + fn write_translation_node( + file: &mut File, + id: i32, + child_id: i32, + pos: Vec3, + ) -> Result { + write_chunk(file, "nTRN", &|file| { + write_i32(file, id)?; // Node index + write_i32(file, 0)?; // Number of attributes + write_i32(file, child_id)?; // Child node index + write_i32(file, -1)?; // Reserved + write_i32(file, 0)?; // Layer + write_i32(file, 1)?; // Frames + write_i32(file, 1)?; // Number of frame attributes + write_i32(file, "_t".len() as i32)?; // Attribute name len + file.write_all("_t".as_bytes())?; // Attribute name + let translation_string = format!("{} {} {}", pos.x, pos.y, pos.z); + write_i32(file, translation_string.len() as i32)?; // Value len + file.write_all(translation_string.as_bytes()) // Value + }) + } + + write!(file, "VOX ")?; // Magic number + write_i32(file, 150)?; // Version + + write!(file, "MAIN")?; + write_i32(file, 0)?; // Chunk size + write_i32(file, 0)?; // Size of child chunks (set later) + let chunks_start = file.stream_position()?; + + // Model data + for (_, model) in self.models.iter() { + write_chunk(file, "SIZE", &|file| { + write_i32(file, Self::CHUNK_SIZE)?; // Size X + write_i32(file, Self::CHUNK_SIZE)?; // Size Y + write_i32(file, Self::CHUNK_SIZE) // Size Z + })?; + write_chunk(file, "XYZI", &|file| { + write_i32(file, model.len() as i32 / 4)?; // Number of voxels + file.write_all(&model) + })?; + } + + // Scene graph + // Root Transform node + write_translation_node(file, 0, 1, Vec3::new(0, 0, 0))?; + + // Group node + write_chunk(file, "nGRP", &|file| { + write_i32(file, 1)?; // Node index + write_i32(file, 0)?; // Number of attributes + write_i32(file, self.models.len() as i32)?; // Number of child nodes + for index in 0..self.models.len() { + write_i32(file, index as i32 * 2 + 2)?; + } + Ok(()) + })?; + + for (index, (model_pos, _)) in self.models.iter().enumerate() { + // Transform node + let pos = model_pos + .map(|p| p * Self::CHUNK_SIZE + Self::CHUNK_SIZE / 2 + if p < 0 { 0 } else { 1 }); + let pos = pos - Vec3::new(self.width / 2, self.width / 2, 0); + let transform_node_id = index as i32 * 2 + 2; + let shape_node_id = index as i32 * 2 + 3; + write_translation_node(file, transform_node_id, shape_node_id, pos)?; + + // Shape node + write_chunk(file, "nSHP", &|file| { + write_i32(file, shape_node_id)?; + write_i32(file, 0)?; // Number of attributes + write_i32(file, 1)?; // Number of models + write_i32(file, index as i32)?; // Model index (independent of scene graph index) + write_i32(file, 0) // Number model of attributes + })?; + } + + // Palette + write_chunk(file, "RGBA", &|file| { + file.write_all(&[220, 220, 255, 0])?; // Air + file.write_all(&[100, 100, 100, 0])?; // Rock + file.write_all(&[0; 4 * (256 - 2)]) + })?; + + let chunks_end = file.stream_position()?; + file.seek(SeekFrom::Start(chunks_start - 4))?; + write_i32(file, chunks_end as i32 - chunks_start as i32)?; + + Ok(()) + } +} + +impl BaseVol for ExportVol { + type Error = (); + type Vox = Block; +} + +impl RectSizedVol for ExportVol { + fn lower_bound_xy(&self) -> Vec2 { Vec2::new(0, 0) } + + fn upper_bound_xy(&self) -> Vec2 { Vec2::new(self.width, self.width) } +} + +impl ReadVol for ExportVol { + fn get(&self, _: vek::Vec3) -> std::result::Result<&Self::Vox, Self::Error> { + Ok(&self.default_block) + } +} + +impl WriteVol for ExportVol { + fn set( + &mut self, + pos: vek::Vec3, + vox: Self::Vox, + ) -> std::result::Result { + // Because the dungeon may need to be split into multiple models, we can't + // stream this directly to the file + + let model_pos = pos.map(|p| p.div_euclid(Self::CHUNK_SIZE)); + let rel_pos = pos.map(|p| (p % Self::CHUNK_SIZE) as u8); + self.models + .entry(model_pos) + .or_default() + .extend_from_slice(&[rel_pos.x, rel_pos.y, rel_pos.z, match vox.kind() { + BlockKind::Air => 1, + BlockKind::Rock => 2, + _ => 3, + }]); + Ok(vox) + } +} diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index 266fba7ed4..c84f9aec9a 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -129,35 +129,32 @@ impl Dungeon { let rpos = wpos2d - self.origin; // Apply the dungeon entrance - let col_sample = if let Some(col) = get_column(offs) { - col - } else { - continue; - }; - for z in entrance.get_bounds().min.z..entrance.get_bounds().max.z { - let wpos = Vec3::new(offs.x, offs.y, self.alt + z + ALT_OFFSET); - let spos = Vec3::new(rpos.x - TILE_SIZE / 2, rpos.y - TILE_SIZE / 2, z); - if let Some(block) = entrance - .get(spos) - .ok() - .copied() - .map(|sb| { - block_from_structure( - index, - sb, - spos, - self.origin, - self.seed, - col_sample, - // TODO: Take environment into account. - Block::air, - ) - }) - .unwrap_or(None) - { - let _ = vol.set(wpos, block); + if let Some(col_sample) = get_column(offs) { + for z in entrance.get_bounds().min.z..entrance.get_bounds().max.z { + let wpos = Vec3::new(offs.x, offs.y, self.alt + z + ALT_OFFSET); + let spos = Vec3::new(rpos.x - TILE_SIZE / 2, rpos.y - TILE_SIZE / 2, z); + if let Some(block) = entrance + .get(spos) + .ok() + .copied() + .map(|sb| { + block_from_structure( + index, + sb, + spos, + self.origin, + self.seed, + col_sample, + // TODO: Take environment into account. + Block::air, + ) + }) + .unwrap_or(None) + { + let _ = vol.set(wpos, block); + } } - } + }; // Apply the dungeon internals let mut z = self.alt + ALT_OFFSET; From c5f82b241dc341d60efb52f0e68c2adad22f0c38 Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Mon, 14 Jun 2021 13:39:50 -0400 Subject: [PATCH 058/180] Mitigate conrod widget id crash by disabling pickaxe icon in xp scroller. --- common/src/cmd.rs | 2 +- voxygen/src/hud/mod.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/common/src/cmd.rs b/common/src/cmd.rs index d8752cffd5..892da21885 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -127,7 +127,7 @@ lazy_static! { .iter() .map(|s| s.to_string()) .collect(); - static ref SKILL_TREES: Vec = vec!["general", "sword", "axe", "hammer", "bow", "staff", "sceptre", "pick"] + static ref SKILL_TREES: Vec = vec!["general", "sword", "axe", "hammer", "bow", "staff", "sceptre", "mining"] .iter() .map(|s| s.to_string()) .collect(); diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index a1501f6a2c..983277688d 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -1254,10 +1254,10 @@ impl Hud { &mut self.ids.player_scts, &mut ui_widgets.widget_id_generator(), ); - let player_sct_icon_id = player_sct_id_walker.next( + /*let player_sct_icon_id = player_sct_id_walker.next( &mut self.ids.player_scts, &mut ui_widgets.widget_id_generator(), - ); + );*/ // Increase font size based on fraction of maximum Experience // "flashes" by having a larger size in the first 100ms let font_size_xp = @@ -1299,13 +1299,13 @@ impl Hud { ui_widgets.win_h * (0.15 * floater.rand_offset.1 as f64) + y, ) .set(player_sct_id, ui_widgets); - // Exp Source Image - if xp_pool.contains(&SkillGroupKind::Weapon(ToolKind::Pick)) { + // Exp Source Image (TODO: fix widget id crash) + /*if xp_pool.contains(&SkillGroupKind::Weapon(ToolKind::Pick)) { Image::new(self.imgs.pickaxe_ico) .w_h(font_size_xp as f64, font_size_xp as f64) .left_from(player_sct_id, 5.0) .set(player_sct_icon_id, ui_widgets); - } + }*/ } } } From 8568dfb38e4f792505e854a2d1acf1cc794926c9 Mon Sep 17 00:00:00 2001 From: asumface Date: Mon, 14 Jun 2021 22:03:57 +0200 Subject: [PATCH 059/180] Bump rodio to 0.14 --- CHANGELOG.md | 2 ++ Cargo.lock | 4 ++-- voxygen/Cargo.toml | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2694d34f41..195158a9c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,10 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Entity-entity pushback is no longer applied in forced movement states like rolling and leaping. +- Updated audio library (rodio 0.13 -> 0.14). ### Removed ### Fixed +- Cases where no audio output could be produced before. ## [0.10.0] - 2021-06-12 diff --git a/Cargo.lock b/Cargo.lock index cff916f047..9733a92724 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4377,9 +4377,9 @@ dependencies = [ [[package]] name = "rodio" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65c2eda643191f6d1bb12ea323a9db8d9ba95374e9be3780b5a9fb5cfb8520f" +checksum = "4d98f5e557b61525057e2bc142c8cd7f0e70d75dc32852309bec440e6e046bf9" dependencies = [ "cpal", "lewton", diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index b84a19dcb5..c322e6a1af 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -95,7 +95,7 @@ num = "0.4" ordered-float = { version = "2.0.1", default-features = false } rand = "0.8" rayon = "1.5" -rodio = {version = "0.13", default-features = false, features = ["vorbis"]} +rodio = {version = "0.14", default-features = false, features = ["vorbis"]} ron = {version = "0.6", default-features = false} serde = {version = "1.0", features = [ "rc", "derive" ]} strum = "0.20" From 6fe3a3c77f8e8b6f4e3e0a9f81324e34ad9e6fec Mon Sep 17 00:00:00 2001 From: Imbris Date: Mon, 14 Jun 2021 21:42:50 -0400 Subject: [PATCH 060/180] Fallback to executable strategy for the userdata rather than panicking when USERDATA_STRATEGY isn't set and the executable is moved out of the project folder --- common/base/src/userdata_dir.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/common/base/src/userdata_dir.rs b/common/base/src/userdata_dir.rs index a038c348d7..e1fe66c479 100644 --- a/common/base/src/userdata_dir.rs +++ b/common/base/src/userdata_dir.rs @@ -1,4 +1,5 @@ use std::path::PathBuf; +use tracing::warn; const VELOREN_USERDATA_ENV: &str = "VELOREN_USERDATA"; @@ -52,14 +53,20 @@ pub fn userdata_dir(workspace: bool, strategy: Option<&str>, manifest_dir: &str) } let exe_path = std::env::current_exe() .expect("Failed to retrieve executable path!"); - // Ensure this path exists - // Ensure that the binary path is prefixed by this path - if !path.exists() || !exe_path.starts_with(&path) { - panic!("Recompile with VELOREN_USERDATA_STRATEGY set to \"system\" or \"executable\" to run the binary outside of the project folder"); + // If this path exists + // and the binary path is prefixed by this path + // put the userdata folder there + if path.exists() && exe_path.starts_with(&path) { + path.push("userdata"); + path + } else { + // otherwise warn and fallback to the executable strategy + warn!("This binary was moved to outside the project folder where it was compiled and was not compiled with VELOREN_USERDATA_STRATEGY set to \"system\" or \"executable\". Falling back the to the \"executable\" strategy (the userdata folder will be placed in the same folder as the executable)"); + let mut path = exe_path; + path.pop(); + path.push("userdata"); + path } - - path.push("userdata"); - path }) } From cbf0a6baf100656ca1fa2279ee0aaff3dc9648cc Mon Sep 17 00:00:00 2001 From: Imbris Date: Tue, 15 Jun 2021 01:53:00 -0400 Subject: [PATCH 061/180] Fix trying to create a zero sized shadow texture when resizing window down to a small size --- voxygen/src/render/renderer/shadow_map.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/voxygen/src/render/renderer/shadow_map.rs b/voxygen/src/render/renderer/shadow_map.rs index b7361adebe..5832c77ff3 100644 --- a/voxygen/src/render/renderer/shadow_map.rs +++ b/voxygen/src/render/renderer/shadow_map.rs @@ -188,7 +188,9 @@ impl ShadowMap { let diag_two_size = u32::checked_next_power_of_two(diag_size) .filter(|&e| e <= max_texture_size) // Limit to max texture resolution rather than error. - .unwrap_or(max_texture_size); + .unwrap_or(max_texture_size) + // Make sure we don't try to create a zero sized texture (divided by 4 below) + .max(4); let point_shadow_tex = wgpu::TextureDescriptor { label: None, From f3657903c9c5f76d6f87d8af37c61708d7f27396 Mon Sep 17 00:00:00 2001 From: Imbris Date: Tue, 15 Jun 2021 02:02:10 -0400 Subject: [PATCH 062/180] [iced renderer] Skip content processing if the clip intersection is zero sized or invalid --- voxygen/src/ui/ice/renderer/mod.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/voxygen/src/ui/ice/renderer/mod.rs b/voxygen/src/ui/ice/renderer/mod.rs index 83eb8f1320..ae83454051 100644 --- a/voxygen/src/ui/ice/renderer/mod.rs +++ b/voxygen/src/ui/ice/renderer/mod.rs @@ -711,14 +711,9 @@ impl IcedRenderer { if intersection.is_valid() && intersection.size().map(|s| s > 0).reduce_and() { intersection } else { - // Create a aabr with width 1 and height 1, technically - // it would be more correct to do it with 0,0 but that's - // a validation error in wgpu so enjoy your funky pixel - // in the top left of your screen - Aabr { - min: Vec2::zero(), - max: Vec2::one(), - } + // If the intersection is invalid or zero sized we don't need to process + // the content primitive + return; } }; // Not expecting this case: new_scissor == current_scissor @@ -737,8 +732,8 @@ impl IcedRenderer { self.draw_commands.push(DrawCommand::Scissor(new_scissor)); // TODO: support nested clips? - // TODO: if last command is a clip changing back to the default replace it with - // this + // TODO: if previous command was a clip changing back to the default replace it + // with this // TODO: cull primitives outside the current scissor // Renderer child From 97ce50e5d46ee25b90d1b52ec979ee963c33ef1f Mon Sep 17 00:00:00 2001 From: Snowram Date: Wed, 26 May 2021 02:37:31 +0200 Subject: [PATCH 063/180] Add troll variants and roc npcs --- .../common/abilities/ability_set_manifest.ron | 12 ++ .../custom/birdlargebasic/summontornadoes.ron | 13 ++ .../common/abilities/custom/tornado/spin.ron | 19 ++ .../npc_weapons/unique/birdlargebasic.ron | 19 ++ assets/common/npc_names.ron | 18 +- assets/voxygen/shaders/particle-vert.glsl | 11 + .../voxel/biped_large_central_manifest.ron | 138 ++++++++++-- .../voxel/biped_large_lateral_manifest.ron | 196 +++++++++++++++--- .../voxel/bird_large_central_manifest.ron | 52 +++++ .../voxel/bird_large_lateral_manifest.ron | 84 ++++++++ assets/voxygen/voxel/npc/roc/male/beak.vox | 3 + assets/voxygen/voxel/npc/roc/male/chest.vox | 3 + assets/voxygen/voxel/npc/roc/male/foot_r.vox | 3 + assets/voxygen/voxel/npc/roc/male/head.vox | 3 + assets/voxygen/voxel/npc/roc/male/leg_r.vox | 3 + assets/voxygen/voxel/npc/roc/male/neck.vox | 3 + .../voxygen/voxel/npc/roc/male/tail_front.vox | 3 + .../voxygen/voxel/npc/roc/male/tail_rear.vox | 3 + .../voxygen/voxel/npc/roc/male/wing_in_r.vox | 3 + .../voxygen/voxel/npc/roc/male/wing_mid_r.vox | 3 + .../voxygen/voxel/npc/roc/male/wing_out_r.vox | 3 + .../voxygen/voxel/npc/troll/male/foot_l.vox | 3 - .../voxygen/voxel/npc/troll/male/foot_r.vox | 3 - .../voxygen/voxel/npc/troll/male/hand_l.vox | 3 - .../voxygen/voxel/npc/troll/male/hand_r.vox | 3 - assets/voxygen/voxel/npc/troll/male/head.vox | 3 - assets/voxygen/voxel/npc/troll/male/jaw.vox | 3 - assets/voxygen/voxel/npc/troll/male/leg_l.vox | 3 - assets/voxygen/voxel/npc/troll/male/leg_r.vox | 3 - .../voxel/npc/troll/male/shoulder_l.vox | 3 - .../voxel/npc/troll/male/shoulder_r.vox | 3 - .../voxel/npc/troll/male/torso_lower.vox | 3 - .../voxel/npc/troll/male/torso_upper.vox | 3 - .../voxel/npc/troll_cave/male/foot_l.vox | 3 + .../voxel/npc/troll_cave/male/foot_r.vox | 3 + .../voxel/npc/troll_cave/male/hand_l.vox | 3 + .../voxel/npc/troll_cave/male/hand_r.vox | 3 + .../voxel/npc/troll_cave/male/head.vox | 3 + .../voxygen/voxel/npc/troll_cave/male/jaw.vox | 3 + .../voxel/npc/troll_cave/male/leg_l.vox | 3 + .../voxel/npc/troll_cave/male/leg_r.vox | 3 + .../voxel/npc/troll_cave/male/shoulder_l.vox | 3 + .../voxel/npc/troll_cave/male/shoulder_r.vox | 3 + .../voxel/npc/troll_cave/male/torso_lower.vox | 3 + .../voxel/npc/troll_cave/male/torso_upper.vox | 3 + .../voxel/npc/troll_mountain/male/foot_l.vox | 3 + .../voxel/npc/troll_mountain/male/foot_r.vox | 3 + .../voxel/npc/troll_mountain/male/hand_l.vox | 3 + .../voxel/npc/troll_mountain/male/hand_r.vox | 3 + .../voxel/npc/troll_mountain/male/head.vox | 3 + .../voxel/npc/troll_mountain/male/jaw.vox | 3 + .../voxel/npc/troll_mountain/male/leg_l.vox | 3 + .../voxel/npc/troll_mountain/male/leg_r.vox | 3 + .../npc/troll_mountain/male/shoulder_l.vox | 3 + .../npc/troll_mountain/male/shoulder_r.vox | 3 + .../npc/troll_mountain/male/torso_lower.vox | 3 + .../npc/troll_mountain/male/torso_upper.vox | 3 + .../voxel/npc/troll_swamp/male/foot_l.vox | 3 + .../voxel/npc/troll_swamp/male/foot_r.vox | 3 + .../voxel/npc/troll_swamp/male/hand_l.vox | 3 + .../voxel/npc/troll_swamp/male/hand_r.vox | 3 + .../voxel/npc/troll_swamp/male/head.vox | 3 + .../voxel/npc/troll_swamp/male/jaw.vox | 3 + .../voxel/npc/troll_swamp/male/leg_l.vox | 3 + .../voxel/npc/troll_swamp/male/leg_r.vox | 3 + .../voxel/npc/troll_swamp/male/shoulder_l.vox | 3 + .../voxel/npc/troll_swamp/male/shoulder_r.vox | 3 + .../npc/troll_swamp/male/torso_lower.vox | 3 + .../npc/troll_swamp/male/torso_upper.vox | 3 + assets/voxygen/voxel/object_manifest.ron | 10 + common/src/comp/body.rs | 9 +- common/src/comp/body/biped_large.rs | 42 ++-- common/src/comp/body/bird_large.rs | 5 +- common/src/comp/body/object.rs | 6 +- common/src/comp/inventory/loadout_builder.rs | 11 +- server/src/events/entity_manipulation.rs | 6 +- server/src/sys/agent.rs | 40 ++++ voxygen/anim/src/biped_large/mod.rs | 59 +++--- voxygen/anim/src/bird_large/mod.rs | 13 ++ voxygen/src/render/pipelines/particle.rs | 1 + voxygen/src/scene/particle.rs | 23 ++ world/src/layer/mod.rs | 2 +- world/src/layer/wildlife.rs | 5 +- 83 files changed, 832 insertions(+), 139 deletions(-) create mode 100644 assets/common/abilities/custom/birdlargebasic/summontornadoes.ron create mode 100644 assets/common/abilities/custom/tornado/spin.ron create mode 100644 assets/common/items/npc_weapons/unique/birdlargebasic.ron create mode 100644 assets/voxygen/voxel/npc/roc/male/beak.vox create mode 100644 assets/voxygen/voxel/npc/roc/male/chest.vox create mode 100644 assets/voxygen/voxel/npc/roc/male/foot_r.vox create mode 100644 assets/voxygen/voxel/npc/roc/male/head.vox create mode 100644 assets/voxygen/voxel/npc/roc/male/leg_r.vox create mode 100644 assets/voxygen/voxel/npc/roc/male/neck.vox create mode 100644 assets/voxygen/voxel/npc/roc/male/tail_front.vox create mode 100644 assets/voxygen/voxel/npc/roc/male/tail_rear.vox create mode 100644 assets/voxygen/voxel/npc/roc/male/wing_in_r.vox create mode 100644 assets/voxygen/voxel/npc/roc/male/wing_mid_r.vox create mode 100644 assets/voxygen/voxel/npc/roc/male/wing_out_r.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/foot_l.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/foot_r.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/hand_l.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/hand_r.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/head.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/jaw.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/leg_l.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/leg_r.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/shoulder_l.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/shoulder_r.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/torso_lower.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/torso_upper.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/foot_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/foot_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/hand_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/hand_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/head.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/jaw.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/leg_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/leg_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/shoulder_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/shoulder_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/torso_lower.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/torso_upper.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/foot_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/foot_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/hand_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/hand_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/head.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/jaw.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/leg_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/leg_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/shoulder_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/shoulder_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/torso_lower.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/torso_upper.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/foot_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/foot_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/hand_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/hand_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/head.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/jaw.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/leg_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/leg_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/shoulder_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/shoulder_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/torso_lower.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/torso_upper.vox diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index 6a3c4f4f57..7c300eebeb 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -264,6 +264,18 @@ (None, "common.abilities.custom.birdlargefire.fireshockwave"), ], ), + Custom("Bird Large Basic"): ( + primary: "common.abilities.custom.birdlargebreathe.firebomb", + secondary: "common.abilities.custom.birdlargebreathe.triplestrike", + abilities: [ + (None, "common.abilities.custom.birdlargebasic.summontornadoes"), + ], + ), + Custom("Tornado"): ( + primary: "common.abilities.custom.tornado.spin", + secondary: "common.abilities.empty.basic", + abilities: [], + ), Tool(Debug): ( primary: "common.abilities.debug.forwardboost", secondary: "common.abilities.debug.upboost", diff --git a/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron b/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron new file mode 100644 index 0000000000..9ad07751b4 --- /dev/null +++ b/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron @@ -0,0 +1,13 @@ +BasicSummon( + buildup_duration: 0.5, + cast_duration: 1.0, + recover_duration: 0.5, + summon_amount: 6, + summon_info: ( + body: Object(Tornado), + scale: None, + health_scaling: 80, + loadout_config: None, + skillset_config: None, + ), +) \ No newline at end of file diff --git a/assets/common/abilities/custom/tornado/spin.ron b/assets/common/abilities/custom/tornado/spin.ron new file mode 100644 index 0000000000..df0320d9c2 --- /dev/null +++ b/assets/common/abilities/custom/tornado/spin.ron @@ -0,0 +1,19 @@ +SpinMelee( + buildup_duration: 0.2, + swing_duration: 0.6, + recover_duration: 0.2, + base_damage: 70, + base_poise_damage: 25, + knockback: ( strength: 0.0, direction: Away), + range: 3.5, + damage_effect: None, + energy_cost: 100, + is_infinite: true, + movement_behavior: AxeHover, + is_interruptible: false, + forward_speed: 0.0, + num_spins: 1, + specifier: None, + target: Some(OutOfGroup), + damage_kind: Slashing, +) diff --git a/assets/common/items/npc_weapons/unique/birdlargebasic.ron b/assets/common/items/npc_weapons/unique/birdlargebasic.ron new file mode 100644 index 0000000000..a97d610359 --- /dev/null +++ b/assets/common/items/npc_weapons/unique/birdlargebasic.ron @@ -0,0 +1,19 @@ +ItemDef( + name: "Bird Large Basic", + description: "testing123", + kind: Tool(( + kind: Natural, + hands: Two, + stats: Direct(( + equip_time_secs: 0.01, + power: 1.0, + poise_strength: 1.0, + speed: 1.0, + crit_chance: 0.0625, + crit_mult: 1.9142857, + )), + )), + quality: Low, + tags: [], + ability_spec: Some(Custom("Bird Large Basic")), +) \ No newline at end of file diff --git a/assets/common/npc_names.ron b/assets/common/npc_names.ron index 2729cacb40..ca8be884c0 100644 --- a/assets/common/npc_names.ron +++ b/assets/common/npc_names.ron @@ -826,9 +826,17 @@ keyword: "wendigo", generic: "Wendigo" ), - troll: ( - keyword: "troll", - generic: "Troll" + troll_cave: ( + keyword: "troll_cave", + generic: "Cave Troll" + ), + troll_mountain: ( + keyword: "troll_mountain", + generic: "Mountain Troll" + ), + troll_swamp: ( + keyword: "troll_swamp", + generic: "Swamp Troll" ), dullahan: ( keyword: "dullahan", @@ -1080,6 +1088,10 @@ keyword: "cockatrice", generic: "Cockatrice" ), + roc: ( + keyword: "roc", + generic: "Roc" + ), ) ), quadruped_low: ( diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index a97e1bdffe..bb3cc72945 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -71,6 +71,7 @@ const int BUBBLES = 29; const int WATER = 30; const int ICE_SPIKES = 31; const int DRIP = 32; +const int TORNADO = 33; // meters per second squared (acceleration) const float earth_gravity = 9.807; @@ -543,6 +544,16 @@ void main() { spin_in_axis(vec3(1,0,0),0) ); break; + case TORNADO: + f_reflect = 0.0; // Fire doesn't reflect light, it emits it + attr = Attr( + //vec3(sin(lifetime * 400.0) * 3.0 * percent(), cos(lifetime * 400.0) * 3.0 * percent(), lifetime * 5.0), + spiral_motion(vec3(0, 0, 5), abs(rand0) + abs(rand1) * percent() * 3.0, percent(), 15.0 * abs(rand2), rand3), + vec3((2.5 * (1 - slow_start(0.05)))), + vec4(1.2 + 0.5 * percent(), 1.2 + 0.5 * percent(), 1.2 + 0.5 * percent(), 2.5), + spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9) + ); + break; default: attr = Attr( linear_motion( diff --git a/assets/voxygen/voxel/biped_large_central_manifest.ron b/assets/voxygen/voxel/biped_large_central_manifest.ron index e002711ae2..4d2830c1b2 100644 --- a/assets/voxygen/voxel/biped_large_central_manifest.ron +++ b/assets/voxygen/voxel/biped_large_central_manifest.ron @@ -156,22 +156,22 @@ central: ("armor.empty"), ) ), - (Troll, Male): ( + (Cavetroll, Male): ( head: ( - offset: (-8.0, -8.5, -9.0), - central: ("npc.troll.male.head"), + offset: (-8.0, -8.0, -8.5), + central: ("npc.troll_cave.male.head"), ), torso_upper: ( - offset: (-8.0, -7.5, -11.0), - central: ("npc.troll.male.torso_upper"), + offset: (-8.0, -9.5, -11.0), + central: ("npc.troll_cave.male.torso_upper"), ), torso_lower: ( - offset: (-6.0, -3.5, -5.0), - central: ("npc.troll.male.torso_lower"), + offset: (-7.0, -5.5, -7.0), + central: ("npc.troll_cave.male.torso_lower"), ), jaw: ( - offset: (-4.0, 0.0, -4.5), - central: ("npc.troll.male.jaw"), + offset: (-5.0, 0.0, -4.5), + central: ("npc.troll_cave.male.jaw"), ), tail: ( offset: (0.0, 0.0, 0.0), @@ -182,22 +182,126 @@ central: ("armor.empty"), ) ), - (Troll, Female): ( + (Cavetroll, Female): ( head: ( - offset: (-8.0, -8.5, -9.0), - central: ("npc.troll.male.head"), + offset: (-8.0, -8.0, -8.5), + central: ("npc.troll_cave.male.head"), ), torso_upper: ( - offset: (-8.0, -7.5, -11.0), - central: ("npc.troll.male.torso_upper"), + offset: (-8.0, -9.5, -11.0), + central: ("npc.troll_cave.male.torso_upper"), ), torso_lower: ( - offset: (-6.0, -3.5, -5.0), - central: ("npc.troll.male.torso_lower"), + offset: (-7.0, -5.5, -7.0), + central: ("npc.troll_cave.male.torso_lower"), + ), + jaw: ( + offset: (-5.0, 0.0, -4.5), + central: ("npc.troll_cave.male.jaw"), + ), + tail: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + second: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), + (Mountaintroll, Male): ( + head: ( + offset: (-9.0, -8.5, -10.0), + central: ("npc.troll_mountain.male.head"), + ), + torso_upper: ( + offset: (-9.0, -10.0, -14.5), + central: ("npc.troll_mountain.male.torso_upper"), + ), + torso_lower: ( + offset: (-7.0, -5.5, -7.0), + central: ("npc.troll_mountain.male.torso_lower"), ), jaw: ( offset: (-4.0, 0.0, -4.5), - central: ("npc.troll.male.jaw"), + central: ("npc.troll_mountain.male.jaw"), + ), + tail: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + second: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), + (Mountaintroll, Female): ( + head: ( + offset: (-9.0, -8.5, -10.0), + central: ("npc.troll_mountain.male.head"), + ), + torso_upper: ( + offset: (-9.0, -10.0, -14.5), + central: ("npc.troll_mountain.male.torso_upper"), + ), + torso_lower: ( + offset: (-7.0, -5.5, -7.0), + central: ("npc.troll_mountain.male.torso_lower"), + ), + jaw: ( + offset: (-4.0, 0.0, -4.5), + central: ("npc.troll_mountain.male.jaw"), + ), + tail: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + second: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), + (Swamptroll, Male): ( + head: ( + offset: (-10.0, -6.5, -6.0), + central: ("npc.troll_swamp.male.head"), + ), + torso_upper: ( + offset: (-9.0, -9.0, -12.5), + central: ("npc.troll_swamp.male.torso_upper"), + ), + torso_lower: ( + offset: (-8.0, -6.5, -10.0), + central: ("npc.troll_swamp.male.torso_lower"), + ), + jaw: ( + offset: (-6.0, 0.0, -4.0), + central: ("npc.troll_swamp.male.jaw"), + ), + tail: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + second: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), + (Swamptroll, Female): ( + head: ( + offset: (-10.0, -6.5, -6.0), + central: ("npc.troll_swamp.male.head"), + ), + torso_upper: ( + offset: (-9.0, -9.0, -12.5), + central: ("npc.troll_swamp.male.torso_upper"), + ), + torso_lower: ( + offset: (-8.0, -6.5, -10.0), + central: ("npc.troll_swamp.male.torso_lower"), + ), + jaw: ( + offset: (-6.0, 0.0, -4.0), + central: ("npc.troll_swamp.male.jaw"), ), tail: ( offset: (0.0, 0.0, 0.0), diff --git a/assets/voxygen/voxel/biped_large_lateral_manifest.ron b/assets/voxygen/voxel/biped_large_lateral_manifest.ron index 0ba3855037..8c8f0c046b 100644 --- a/assets/voxygen/voxel/biped_large_lateral_manifest.ron +++ b/assets/voxygen/voxel/biped_large_lateral_manifest.ron @@ -203,72 +203,208 @@ lateral: ("npc.wendigo.male.foot_r"), ), ), - (Troll, Male): ( + (Cavetroll, Male): ( shoulder_l: ( - offset: (-5.0, -4.5, -11.0), - lateral: ("npc.troll.male.shoulder_l"), + offset: (-5.5, -4.0, -8.0), + lateral: ("npc.troll_cave.male.shoulder_l"), ), shoulder_r: ( - offset: (-5.0, -4.5, -11.0), - lateral: ("npc.troll.male.shoulder_r"), + offset: (-5.5, -4.0, -8.0), + lateral: ("npc.troll_cave.male.shoulder_r"), ), hand_l: ( - offset: (-3.5, -4.0, -12.0), - lateral: ("npc.troll.male.hand_l"), + offset: (-4.5, -4.0, -14.0), + lateral: ("npc.troll_cave.male.hand_l"), ), hand_r: ( - offset: (-3.5, -4.0, -12.0), - lateral: ("npc.troll.male.hand_r"), + offset: (-4.5, -4.0, -14.0), + lateral: ("npc.troll_cave.male.hand_r"), ), leg_l: ( - offset: (-3.0, -2.5, -4.5), - lateral: ("npc.troll.male.leg_l"), + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_cave.male.leg_l"), ), leg_r: ( - offset: (-3.0, -2.5, -4.5), - lateral: ("npc.troll.male.leg_r"), + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_cave.male.leg_r"), ), foot_l: ( offset: (-3.0, -5.0, -2.5), - lateral: ("npc.troll.male.foot_l"), + lateral: ("npc.troll_cave.male.foot_l"), ), foot_r: ( offset: (-3.0, -5.0, -2.5), - lateral: ("npc.troll.male.foot_r"), + lateral: ("npc.troll_cave.male.foot_r"), ), ), - (Troll, Female): ( + (Cavetroll, Female): ( shoulder_l: ( - offset: (-5.0, -4.5, -11.0), - lateral: ("npc.troll.male.shoulder_l"), + offset: (-5.5, -4.0, -8.0), + lateral: ("npc.troll_cave.male.shoulder_l"), ), shoulder_r: ( - offset: (-5.0, -4.5, -11.0), - lateral: ("npc.troll.male.shoulder_r"), + offset: (-5.5, -4.0, -8.0), + lateral: ("npc.troll_cave.male.shoulder_r"), ), hand_l: ( - offset: (-3.5, -4.0, -12.0), - lateral: ("npc.troll.male.hand_l"), + offset: (-4.5, -4.0, -14.0), + lateral: ("npc.troll_cave.male.hand_l"), ), hand_r: ( - offset: (-3.5, -4.0, -12.0), - lateral: ("npc.troll.male.hand_r"), + offset: (-4.5, -4.0, -14.0), + lateral: ("npc.troll_cave.male.hand_r"), ), leg_l: ( - offset: (-3.0, -2.5, -4.5), - lateral: ("npc.troll.male.leg_l"), + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_cave.male.leg_l"), ), leg_r: ( - offset: (-3.0, -2.5, -4.5), - lateral: ("npc.troll.male.leg_r"), + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_cave.male.leg_r"), ), foot_l: ( offset: (-3.0, -5.0, -2.5), - lateral: ("npc.troll.male.foot_l"), + lateral: ("npc.troll_cave.male.foot_l"), ), foot_r: ( offset: (-3.0, -5.0, -2.5), - lateral: ("npc.troll.male.foot_r"), + lateral: ("npc.troll_cave.male.foot_r"), + ), + ), + (Mountaintroll, Male): ( + shoulder_l: ( + offset: (-6.5, -5.0, -8.5), + lateral: ("npc.troll_mountain.male.shoulder_l"), + ), + shoulder_r: ( + offset: (-6.5, -5.0, -8.5), + lateral: ("npc.troll_mountain.male.shoulder_r"), + ), + hand_l: ( + offset: (-5.0, -4.5, -14.0), + lateral: ("npc.troll_mountain.male.hand_l"), + ), + hand_r: ( + offset: (-5.0, -4.5, -14.0), + lateral: ("npc.troll_mountain.male.hand_r"), + ), + leg_l: ( + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_mountain.male.leg_l"), + ), + leg_r: ( + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_mountain.male.leg_r"), + ), + foot_l: ( + offset: (-3.0, -5.0, -2.5), + lateral: ("npc.troll_mountain.male.foot_l"), + ), + foot_r: ( + offset: (-3.0, -5.0, -2.5), + lateral: ("npc.troll_mountain.male.foot_r"), + ), + ), + (Mountaintroll, Female): ( + shoulder_l: ( + offset: (-6.5, -5.0, -8.5), + lateral: ("npc.troll_mountain.male.shoulder_l"), + ), + shoulder_r: ( + offset: (-6.5, -5.0, -8.5), + lateral: ("npc.troll_mountain.male.shoulder_r"), + ), + hand_l: ( + offset: (-5.0, -4.5, -14.0), + lateral: ("npc.troll_mountain.male.hand_l"), + ), + hand_r: ( + offset: (-5.0, -4.5, -14.0), + lateral: ("npc.troll_mountain.male.hand_r"), + ), + leg_l: ( + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_mountain.male.leg_l"), + ), + leg_r: ( + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_mountain.male.leg_r"), + ), + foot_l: ( + offset: (-3.0, -5.0, -2.5), + lateral: ("npc.troll_mountain.male.foot_l"), + ), + foot_r: ( + offset: (-3.0, -5.0, -2.5), + lateral: ("npc.troll_mountain.male.foot_r"), + ), + ), + (Swamptroll, Male): ( + shoulder_l: ( + offset: (-5.5, -5.0, -8.5), + lateral: ("npc.troll_swamp.male.shoulder_l"), + ), + shoulder_r: ( + offset: (-5.5, -5.0, -8.5), + lateral: ("npc.troll_swamp.male.shoulder_r"), + ), + hand_l: ( + offset: (-5.0, -4.0, -16.0), + lateral: ("npc.troll_swamp.male.hand_l"), + ), + hand_r: ( + offset: (-5.0, -4.0, -16.0), + lateral: ("npc.troll_swamp.male.hand_r"), + ), + leg_l: ( + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_swamp.male.leg_l"), + ), + leg_r: ( + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_swamp.male.leg_r"), + ), + foot_l: ( + offset: (-3.0, -5.0, -2.5), + lateral: ("npc.troll_swamp.male.foot_l"), + ), + foot_r: ( + offset: (-3.0, -5.0, -2.5), + lateral: ("npc.troll_swamp.male.foot_r"), + ), + ), + (Swamptroll, Female): ( + shoulder_l: ( + offset: (-5.5, -5.0, -8.5), + lateral: ("npc.troll_swamp.male.shoulder_l"), + ), + shoulder_r: ( + offset: (-5.5, -5.0, -8.5), + lateral: ("npc.troll_swamp.male.shoulder_r"), + ), + hand_l: ( + offset: (-5.0, -4.0, -16.0), + lateral: ("npc.troll_swamp.male.hand_l"), + ), + hand_r: ( + offset: (-5.0, -4.0, -16.0), + lateral: ("npc.troll_swamp.male.hand_r"), + ), + leg_l: ( + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_swamp.male.leg_l"), + ), + leg_r: ( + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_swamp.male.leg_r"), + ), + foot_l: ( + offset: (-3.0, -5.0, -2.5), + lateral: ("npc.troll_swamp.male.foot_l"), + ), + foot_r: ( + offset: (-3.0, -5.0, -2.5), + lateral: ("npc.troll_swamp.male.foot_r"), ), ), (Dullahan, Male): ( diff --git a/assets/voxygen/voxel/bird_large_central_manifest.ron b/assets/voxygen/voxel/bird_large_central_manifest.ron index f8a0999986..a706aa79cf 100644 --- a/assets/voxygen/voxel/bird_large_central_manifest.ron +++ b/assets/voxygen/voxel/bird_large_central_manifest.ron @@ -103,4 +103,56 @@ central: ("npc.cockatrice.male.tail_rear"), ) ), + (Roc, Male): ( + head: ( + offset: (-4.5, -4.0, -6.0), + central: ("npc.roc.male.head"), + ), + beak: ( + offset: (-3.5, 0.0, -8.0), + central: ("npc.roc.male.beak"), + ), + neck: ( + offset: (-5.5, 0.0, -10.5), + central: ("npc.roc.male.neck"), + ), + chest: ( + offset: (-8.5, -12.5, -13.5), + central: ("npc.roc.male.chest"), + ), + tail_front: ( + offset: (-4.5, -9.0, -8.0), + central: ("npc.roc.male.tail_front"), + ), + tail_rear: ( + offset: (-9.5, -28.0, -6.0), + central: ("npc.roc.male.tail_rear"), + ) + ), + (Roc, Female): ( + head: ( + offset: (-4.5, -4.0, -6.0), + central: ("npc.roc.male.head"), + ), + beak: ( + offset: (-3.5, 0.0, -8.0), + central: ("npc.roc.male.beak"), + ), + neck: ( + offset: (-5.5, 0.0, -10.5), + central: ("npc.roc.male.neck"), + ), + chest: ( + offset: (-8.5, -12.5, -13.5), + central: ("npc.roc.male.chest"), + ), + tail_front: ( + offset: (-4.5, -9.0, -8.0), + central: ("npc.roc.male.tail_front"), + ), + tail_rear: ( + offset: (-9.5, -28.0, -6.0), + central: ("npc.roc.male.tail_rear"), + ) + ), }) diff --git a/assets/voxygen/voxel/bird_large_lateral_manifest.ron b/assets/voxygen/voxel/bird_large_lateral_manifest.ron index 3eca741462..fadf7ddb56 100644 --- a/assets/voxygen/voxel/bird_large_lateral_manifest.ron +++ b/assets/voxygen/voxel/bird_large_lateral_manifest.ron @@ -167,4 +167,88 @@ lateral: ("npc.cockatrice.male.foot_r"), ) ), + (Roc, Male): ( + wing_in_l: ( + offset: (-13.0, -14.0, -2.5), + lateral: ("npc.roc.male.wing_in_r"), + ), + wing_in_r: ( + offset: (0.0, -14.0, -2.5), + lateral: ("npc.roc.male.wing_in_r"), + ), + wing_mid_l: ( + offset: (-11.0, -18.0, -2.0), + lateral: ("npc.roc.male.wing_mid_r"), + ), + wing_mid_r: ( + offset: (0.0, -18.0, -2.0), + lateral: ("npc.roc.male.wing_mid_r"), + ), + wing_out_l: ( + offset: (-20.0, -18.0, -2.0), + lateral: ("npc.roc.male.wing_out_r"), + ), + wing_out_r: ( + offset: (0.0, -18.0, -2.0), + lateral: ("npc.roc.male.wing_out_r"), + ), + leg_l: ( + offset: (-4.0, -5.5, -7.5), + lateral: ("npc.roc.male.leg_r"), + ), + leg_r: ( + offset: (-4.0, -5.5, -7.5), + lateral: ("npc.roc.male.leg_r"), + ), + foot_l: ( + offset: (-5.5, -3.0, -12.0), + lateral: ("npc.roc.male.foot_r"), + ), + foot_r: ( + offset: (-5.5, -3.0, -12.0), + lateral: ("npc.roc.male.foot_r"), + ) + ), + (Roc, Female): ( + wing_in_l: ( + offset: (-13.0, -14.0, -2.5), + lateral: ("npc.roc.male.wing_in_r"), + ), + wing_in_r: ( + offset: (0.0, -14.0, -2.5), + lateral: ("npc.roc.male.wing_in_r"), + ), + wing_mid_l: ( + offset: (-11.0, -18.0, -2.0), + lateral: ("npc.roc.male.wing_mid_r"), + ), + wing_mid_r: ( + offset: (0.0, -18.0, -2.0), + lateral: ("npc.roc.male.wing_mid_r"), + ), + wing_out_l: ( + offset: (-20.0, -18.0, -2.0), + lateral: ("npc.roc.male.wing_out_r"), + ), + wing_out_r: ( + offset: (0.0, -18.0, -2.0), + lateral: ("npc.roc.male.wing_out_r"), + ), + leg_l: ( + offset: (-4.0, -5.5, -7.5), + lateral: ("npc.roc.male.leg_r"), + ), + leg_r: ( + offset: (-4.0, -5.5, -7.5), + lateral: ("npc.roc.male.leg_r"), + ), + foot_l: ( + offset: (-5.5, -3.0, -12.0), + lateral: ("npc.roc.male.foot_r"), + ), + foot_r: ( + offset: (-5.5, -3.0, -12.0), + lateral: ("npc.roc.male.foot_r"), + ) + ), }) \ No newline at end of file diff --git a/assets/voxygen/voxel/npc/roc/male/beak.vox b/assets/voxygen/voxel/npc/roc/male/beak.vox new file mode 100644 index 0000000000..eead2a2c19 --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/beak.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:908494c22844e83f749eba053878f7b9a2025dac1ef3645bb5ff7c4c8a319a7a +size 1716 diff --git a/assets/voxygen/voxel/npc/roc/male/chest.vox b/assets/voxygen/voxel/npc/roc/male/chest.vox new file mode 100644 index 0000000000..1f836f255d --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/chest.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0fc31c5cc5ad16914dec01999994d4f3c38caf3d5fc2ef38a09d22ba8f07ce45 +size 16556 diff --git a/assets/voxygen/voxel/npc/roc/male/foot_r.vox b/assets/voxygen/voxel/npc/roc/male/foot_r.vox new file mode 100644 index 0000000000..12e6eacd68 --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/foot_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:41697e4f9cf309df975058049be4d2b72cbce83e0e48a30fae5245cd368966fc +size 2216 diff --git a/assets/voxygen/voxel/npc/roc/male/head.vox b/assets/voxygen/voxel/npc/roc/male/head.vox new file mode 100644 index 0000000000..b4949cf1fb --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/head.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0f5b391704993b817759d13f50f413ca2718c742a542b110fdac48cd27a86d01 +size 3236 diff --git a/assets/voxygen/voxel/npc/roc/male/leg_r.vox b/assets/voxygen/voxel/npc/roc/male/leg_r.vox new file mode 100644 index 0000000000..ec21c11e00 --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/leg_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:87045fb82df52741d6b01bdb1712499d58db6ac10a2d1bea5124310219c0e850 +size 3400 diff --git a/assets/voxygen/voxel/npc/roc/male/neck.vox b/assets/voxygen/voxel/npc/roc/male/neck.vox new file mode 100644 index 0000000000..3dce2215a0 --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/neck.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d68bc72562b91296c1426ccc4e96df0da1736c257ec5717a1315e9ed3ed9ed38 +size 5768 diff --git a/assets/voxygen/voxel/npc/roc/male/tail_front.vox b/assets/voxygen/voxel/npc/roc/male/tail_front.vox new file mode 100644 index 0000000000..eff23adc77 --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/tail_front.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b3ca88120eae851fba31dde22cedce0404d207f539d1d3a1515296bcb1c056e1 +size 2240 diff --git a/assets/voxygen/voxel/npc/roc/male/tail_rear.vox b/assets/voxygen/voxel/npc/roc/male/tail_rear.vox new file mode 100644 index 0000000000..c884b56c4f --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/tail_rear.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cc3d03630c67f788a46e463d24cdb8734d67157eca5c06ee3a18fb01ad6b5581 +size 3400 diff --git a/assets/voxygen/voxel/npc/roc/male/wing_in_r.vox b/assets/voxygen/voxel/npc/roc/male/wing_in_r.vox new file mode 100644 index 0000000000..a3a212a3e2 --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/wing_in_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ed3f018074a5c18226bcd857ec549bb1558c2c23b558e7a2b3ca89628ad994b8 +size 3112 diff --git a/assets/voxygen/voxel/npc/roc/male/wing_mid_r.vox b/assets/voxygen/voxel/npc/roc/male/wing_mid_r.vox new file mode 100644 index 0000000000..51239e7c94 --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/wing_mid_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:afa35e2aaf6c7572db4369aae76892583a0b9d0dac95dc145ec938143261cfba +size 3448 diff --git a/assets/voxygen/voxel/npc/roc/male/wing_out_r.vox b/assets/voxygen/voxel/npc/roc/male/wing_out_r.vox new file mode 100644 index 0000000000..bfd0ff77bb --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/wing_out_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0bf07f759bff02d4ce47fff3529fb5abc5806a3fc28763132d266cb17036d5b0 +size 4440 diff --git a/assets/voxygen/voxel/npc/troll/male/foot_l.vox b/assets/voxygen/voxel/npc/troll/male/foot_l.vox deleted file mode 100644 index 4d01fb8f7b..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/foot_l.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7841f6c64137537d9339ce27f57530714f7da32a05570abc5a53df5755c733bc -size 1772 diff --git a/assets/voxygen/voxel/npc/troll/male/foot_r.vox b/assets/voxygen/voxel/npc/troll/male/foot_r.vox deleted file mode 100644 index 614e74f9e0..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/foot_r.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dadb50f16c2ecec316b734490125ceec5600dd3814fba5aa7c69b39f3bc6d163 -size 1772 diff --git a/assets/voxygen/voxel/npc/troll/male/hand_l.vox b/assets/voxygen/voxel/npc/troll/male/hand_l.vox deleted file mode 100644 index 0eac100b7e..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/hand_l.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e6499cdfbca1a5fd80a3160709345f486475e531556bbf1967a7597c4adcf493 -size 2912 diff --git a/assets/voxygen/voxel/npc/troll/male/hand_r.vox b/assets/voxygen/voxel/npc/troll/male/hand_r.vox deleted file mode 100644 index 4802cd12a1..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/hand_r.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:65d2871153549e7de7eb336d0d8b2d240c99d97285b44bc80442a37711f85963 -size 2912 diff --git a/assets/voxygen/voxel/npc/troll/male/head.vox b/assets/voxygen/voxel/npc/troll/male/head.vox deleted file mode 100644 index 6d56c9ee94..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/head.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6090d7c9b1b722fe84eabaaa3fd7b0678dd16b35ac59ffa4f997e8d5fdb52253 -size 4704 diff --git a/assets/voxygen/voxel/npc/troll/male/jaw.vox b/assets/voxygen/voxel/npc/troll/male/jaw.vox deleted file mode 100644 index 23ccf45db1..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/jaw.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3560d24655bb6dfc1e54e8f526814f99a43d626af7b3fc5a4404acd0093048aa -size 1940 diff --git a/assets/voxygen/voxel/npc/troll/male/leg_l.vox b/assets/voxygen/voxel/npc/troll/male/leg_l.vox deleted file mode 100644 index e80f022e90..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/leg_l.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dd23c042d142e17a606119472bc52c2e3f773dd83ef8bee9d368baca65320ab6 -size 1664 diff --git a/assets/voxygen/voxel/npc/troll/male/leg_r.vox b/assets/voxygen/voxel/npc/troll/male/leg_r.vox deleted file mode 100644 index 4db2576313..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/leg_r.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:61f27cd9bdf30ac16748abfa090eacc6113097ca73f7e937f9201c40a6bae57a -size 1664 diff --git a/assets/voxygen/voxel/npc/troll/male/shoulder_l.vox b/assets/voxygen/voxel/npc/troll/male/shoulder_l.vox deleted file mode 100644 index f1c7a99037..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/shoulder_l.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:15606bec0680a834eff98efab9a06331c857a2a3dea9f140af3d0dc0ec169af2 -size 2964 diff --git a/assets/voxygen/voxel/npc/troll/male/shoulder_r.vox b/assets/voxygen/voxel/npc/troll/male/shoulder_r.vox deleted file mode 100644 index 535dc664ce..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/shoulder_r.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c111e35534f571b31d72eee59ed8c512c0f9545a55040981f34b473464c51fff -size 2964 diff --git a/assets/voxygen/voxel/npc/troll/male/torso_lower.vox b/assets/voxygen/voxel/npc/troll/male/torso_lower.vox deleted file mode 100644 index e69fe423eb..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/torso_lower.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:04cb99c4a0fe61494d022df4b7a557d00017e3672e3244fe1123a1d6b3b81ac7 -size 2336 diff --git a/assets/voxygen/voxel/npc/troll/male/torso_upper.vox b/assets/voxygen/voxel/npc/troll/male/torso_upper.vox deleted file mode 100644 index 8c900374a4..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/torso_upper.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4abae28c1df2eaaa2b75fec7b3b754f330f159590a66fd89a69387781de22ab7 -size 10424 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/foot_l.vox b/assets/voxygen/voxel/npc/troll_cave/male/foot_l.vox new file mode 100644 index 0000000000..4c97a42ee0 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/foot_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1480dfe9aa192f690217f831b441303546520bf27c9eadc44ef29eb0be3eca60 +size 1792 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/foot_r.vox b/assets/voxygen/voxel/npc/troll_cave/male/foot_r.vox new file mode 100644 index 0000000000..243373daaf --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/foot_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4beb7a960e88dce016f74c945918135c983980716d30ee60019d683f0b8c1e48 +size 1792 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/hand_l.vox b/assets/voxygen/voxel/npc/troll_cave/male/hand_l.vox new file mode 100644 index 0000000000..18086a74e3 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/hand_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bda1327dced03be263a98b2ddc8d248b6f1df914a3400a48d1b50bb5945557b5 +size 3892 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/hand_r.vox b/assets/voxygen/voxel/npc/troll_cave/male/hand_r.vox new file mode 100644 index 0000000000..770b893669 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/hand_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0f2c60a45ffdf3446e5b84f029050a43405b8daffbba6f4cac14d06e089570bf +size 3892 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/head.vox b/assets/voxygen/voxel/npc/troll_cave/male/head.vox new file mode 100644 index 0000000000..6975647fa5 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/head.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a7a1c16e75cc3c5422dcbbdabd59516df4a9218559902204226144a38762e2d +size 4860 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/jaw.vox b/assets/voxygen/voxel/npc/troll_cave/male/jaw.vox new file mode 100644 index 0000000000..66b423fb91 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/jaw.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4c365c9c309e55d58cc289da3d5451a24f6b9d312ecb9912e97187f0601d5ca5 +size 2032 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/leg_l.vox b/assets/voxygen/voxel/npc/troll_cave/male/leg_l.vox new file mode 100644 index 0000000000..c5ef8c1c31 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/leg_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7c8f500708cdba942b42890741d20b275f7b9f5458ae45fac3813882c19b6fa8 +size 1844 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/leg_r.vox b/assets/voxygen/voxel/npc/troll_cave/male/leg_r.vox new file mode 100644 index 0000000000..7ce31c07a9 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/leg_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:34dfcd5f9ebdfff349942c2533c995e0d07a526374e467acff78dd839a53cf4d +size 1844 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/shoulder_l.vox b/assets/voxygen/voxel/npc/troll_cave/male/shoulder_l.vox new file mode 100644 index 0000000000..46ad49ff2d --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/shoulder_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2d33ab14221792cbb9867b26d80e78db0ed417d704c2bd95b17d14321cbae7d9 +size 3576 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/shoulder_r.vox b/assets/voxygen/voxel/npc/troll_cave/male/shoulder_r.vox new file mode 100644 index 0000000000..7898013ed7 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/shoulder_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c98dced3fb3614e412ea30f321936575d9a98bc547e72740766b8e41d0c4e76 +size 3576 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/torso_lower.vox b/assets/voxygen/voxel/npc/troll_cave/male/torso_lower.vox new file mode 100644 index 0000000000..3b2cc83635 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/torso_lower.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b2177e310320e46de10ba867446175bc9d8adc34f4516550b644fdb6c78f66ed +size 3016 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/torso_upper.vox b/assets/voxygen/voxel/npc/troll_cave/male/torso_upper.vox new file mode 100644 index 0000000000..2252446a88 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/torso_upper.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:787fa629bcd7cdec77b7e4fc4bdcfa0830b9cdf5644eb2e06d565ff4a9ea3beb +size 12836 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/foot_l.vox b/assets/voxygen/voxel/npc/troll_mountain/male/foot_l.vox new file mode 100644 index 0000000000..3a8d7dbdce --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/foot_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:10449f2e7f7a32bd1829c32d2efc600b1fd1a5450aefa70be70818c84eba5522 +size 1792 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/foot_r.vox b/assets/voxygen/voxel/npc/troll_mountain/male/foot_r.vox new file mode 100644 index 0000000000..fb5a17f957 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/foot_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78d433a27a4a292dd9b67d301e6ce6ea4feb0a41ac4a5f4fdc6c425b4a7543e7 +size 1792 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/hand_l.vox b/assets/voxygen/voxel/npc/troll_mountain/male/hand_l.vox new file mode 100644 index 0000000000..c000331be5 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/hand_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2a8fbcde634a5d5c876f649353e456035522144e9fd713dea169d8d20d3b0a9d +size 4380 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/hand_r.vox b/assets/voxygen/voxel/npc/troll_mountain/male/hand_r.vox new file mode 100644 index 0000000000..563acd4fc5 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/hand_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59e83cf8d8cb6241a65ecdc14c84057030f44589ca7557f9a28fff8c17531c87 +size 4380 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/head.vox b/assets/voxygen/voxel/npc/troll_mountain/male/head.vox new file mode 100644 index 0000000000..53ee848827 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/head.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f0ee0b6c88e917361d7f035895c85ad2193c3e7596251adb98dc69962dacc4c4 +size 5032 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/jaw.vox b/assets/voxygen/voxel/npc/troll_mountain/male/jaw.vox new file mode 100644 index 0000000000..6b43d3a792 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/jaw.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b39d0ae768f6213c4c0446274f7085f484b66e9dbb87bd527147448abcc6df35 +size 1716 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/leg_l.vox b/assets/voxygen/voxel/npc/troll_mountain/male/leg_l.vox new file mode 100644 index 0000000000..5b69dd1ea5 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/leg_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d6dd45bed68be177695948a44fb88f8c87112d1757d10d4cd3b823ba4c2cd2b +size 1844 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/leg_r.vox b/assets/voxygen/voxel/npc/troll_mountain/male/leg_r.vox new file mode 100644 index 0000000000..fff2e85edd --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/leg_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3462c6eeb62e0166543725bc65a0745314042c62470fe5774f1b38de5b6166a0 +size 1844 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/shoulder_l.vox b/assets/voxygen/voxel/npc/troll_mountain/male/shoulder_l.vox new file mode 100644 index 0000000000..306a820bd7 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/shoulder_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4cbe941566ac9ca3959466198f481a1a4073a36b8329bc4eed5c47eac8213b72 +size 4280 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/shoulder_r.vox b/assets/voxygen/voxel/npc/troll_mountain/male/shoulder_r.vox new file mode 100644 index 0000000000..10ab7e8e53 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/shoulder_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fd60e29f9c631a0beeb69950f705df6a110935c413ae3d6aa5d1a94a6a8b97fb +size 4280 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/torso_lower.vox b/assets/voxygen/voxel/npc/troll_mountain/male/torso_lower.vox new file mode 100644 index 0000000000..f5cdbce07c --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/torso_lower.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0658b7b334a1b1a358dbdfdb717c821fbd6e8e860951d4f80a7a964992db4144 +size 3352 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/torso_upper.vox b/assets/voxygen/voxel/npc/troll_mountain/male/torso_upper.vox new file mode 100644 index 0000000000..dfd42a133a --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/torso_upper.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:22e91d9fc7ec260437e2b93758c85cce0c8c4964c6dc449b41dc708b8fac5134 +size 17700 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/foot_l.vox b/assets/voxygen/voxel/npc/troll_swamp/male/foot_l.vox new file mode 100644 index 0000000000..e653439955 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/foot_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7642ee74ae6e7ce95953e5a2d3a8bb943bc5b55f3c0930c995ad2a2655ef5ce9 +size 1792 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/foot_r.vox b/assets/voxygen/voxel/npc/troll_swamp/male/foot_r.vox new file mode 100644 index 0000000000..71fbf5c5be --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/foot_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:324949b49db1d316abe1dbe73ac4b6df9e6ab1010e59ed516716638dfb4b5ae2 +size 1792 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/hand_l.vox b/assets/voxygen/voxel/npc/troll_swamp/male/hand_l.vox new file mode 100644 index 0000000000..86817a9b1d --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/hand_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6c27c05d8c56968cc23a2bdfb107f7f7860d64d15a5df7ab1d9c40b5abf562b0 +size 3864 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/hand_r.vox b/assets/voxygen/voxel/npc/troll_swamp/male/hand_r.vox new file mode 100644 index 0000000000..89f24c221d --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/hand_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:257249a24fc89f1005fdaf6c80d9360405178dfff9d3b6c24a597b48d0c14da2 +size 3864 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/head.vox b/assets/voxygen/voxel/npc/troll_swamp/male/head.vox new file mode 100644 index 0000000000..7aa317ed56 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/head.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:652e49b00fab3d180ef9c1afb60f55fd11e139df4be2a498ff59261ae3a571de +size 5000 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/jaw.vox b/assets/voxygen/voxel/npc/troll_swamp/male/jaw.vox new file mode 100644 index 0000000000..817683d5f5 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/jaw.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d7cc30f4d2a4a394ed269c27824f7ffea58479bc68ecac24571108324569b088 +size 3240 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/leg_l.vox b/assets/voxygen/voxel/npc/troll_swamp/male/leg_l.vox new file mode 100644 index 0000000000..b20571cd2d --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/leg_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75810c18ae8e9edc2f453cd06e7c53946f70f0f3196f312a29f269d1f7c36653 +size 1844 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/leg_r.vox b/assets/voxygen/voxel/npc/troll_swamp/male/leg_r.vox new file mode 100644 index 0000000000..06a40b57a4 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/leg_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:76a925036b31a3ca58a74ab2340c896c12c36c0e2104ae97df94b99f7bfc6225 +size 1844 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/shoulder_l.vox b/assets/voxygen/voxel/npc/troll_swamp/male/shoulder_l.vox new file mode 100644 index 0000000000..c18e498017 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/shoulder_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9880df256c9aef3efbdfc8398338d342b37a8872414f1d2a440aeed54cce0348 +size 4216 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/shoulder_r.vox b/assets/voxygen/voxel/npc/troll_swamp/male/shoulder_r.vox new file mode 100644 index 0000000000..51431710c4 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/shoulder_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d6dd821308ff92b0cdb902d3df6d00570bc97164959babf8dfe177e9c75a751b +size 4208 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/torso_lower.vox b/assets/voxygen/voxel/npc/troll_swamp/male/torso_lower.vox new file mode 100644 index 0000000000..33ef62a61a --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/torso_lower.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b28a8af5f99d52a8676be3810ba27c485c26cc4efefafeb80087ff5d168fffb9 +size 4256 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/torso_upper.vox b/assets/voxygen/voxel/npc/troll_swamp/male/torso_upper.vox new file mode 100644 index 0000000000..9d36e25b7e --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/torso_upper.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7a6a36f03d750445c13299892ca9156f2b6da1e582cbb42f0d18bd64b45d49be +size 16488 diff --git a/assets/voxygen/voxel/object_manifest.ron b/assets/voxygen/voxel/object_manifest.ron index 54f85607bb..8f7cfaace4 100644 --- a/assets/voxygen/voxel/object_manifest.ron +++ b/assets/voxygen/voxel/object_manifest.ron @@ -759,4 +759,14 @@ central: ("armor.empty"), ) ), + Tornado: ( + bone0: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + bone1: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), }) diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 2cc9582c5a..10854409bb 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -412,6 +412,7 @@ impl Body { Body::BirdLarge(body) => match body.species { bird_large::Species::Cockatrice => 4000, bird_large::Species::Phoenix => 6000, + bird_large::Species::Roc => 5000, }, Body::Humanoid(_) => 750, _ => 1000, @@ -477,7 +478,9 @@ impl Body { biped_large::Species::Ogre => 3200, biped_large::Species::Cyclops => 3200, biped_large::Species::Wendigo => 2800, - biped_large::Species::Troll => 2400, + biped_large::Species::Cavetroll => 2400, + biped_large::Species::Mountaintroll => 2400, + biped_large::Species::Swamptroll => 2400, biped_large::Species::Dullahan => 3000, biped_large::Species::Mindflayer => 12500, biped_large::Species::Tidalwarrior => 16000, @@ -594,7 +597,9 @@ impl Body { biped_large::Species::Ogre => 70, biped_large::Species::Cyclops => 80, biped_large::Species::Wendigo => 80, - biped_large::Species::Troll => 60, + biped_large::Species::Cavetroll => 60, + biped_large::Species::Mountaintroll => 60, + biped_large::Species::Swamptroll => 60, biped_large::Species::Dullahan => 120, biped_large::Species::Yeti => 0, biped_large::Species::Harvester => 80, diff --git a/common/src/comp/body/biped_large.rs b/common/src/comp/body/biped_large.rs index 3891c0a4f8..d152693658 100644 --- a/common/src/comp/body/biped_large.rs +++ b/common/src/comp/body/biped_large.rs @@ -37,19 +37,21 @@ make_case_elim!( Ogre = 0, Cyclops = 1, Wendigo = 2, - Troll = 3, - Dullahan = 4, - Werewolf = 5, - Occultsaurok = 6, - Mightysaurok = 7, - Slysaurok = 8, - Mindflayer = 9, - Minotaur = 10, - Tidalwarrior = 11, - Yeti = 12, - Harvester = 13, - Blueoni = 14, - Redoni = 15, + Cavetroll = 3, + Mountaintroll = 4, + Swamptroll = 5, + Dullahan = 6, + Werewolf = 7, + Occultsaurok = 8, + Mightysaurok = 9, + Slysaurok = 10, + Mindflayer = 11, + Minotaur = 12, + Tidalwarrior = 13, + Yeti = 14, + Harvester = 15, + Blueoni = 16, + Redoni = 17, } ); @@ -61,7 +63,9 @@ pub struct AllSpecies { pub ogre: SpeciesMeta, pub cyclops: SpeciesMeta, pub wendigo: SpeciesMeta, - pub troll: SpeciesMeta, + pub troll_cave: SpeciesMeta, + pub troll_mountain: SpeciesMeta, + pub troll_swamp: SpeciesMeta, pub dullahan: SpeciesMeta, pub werewolf: SpeciesMeta, pub saurok_occult: SpeciesMeta, @@ -85,7 +89,9 @@ impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies Species::Ogre => &self.ogre, Species::Cyclops => &self.cyclops, Species::Wendigo => &self.wendigo, - Species::Troll => &self.troll, + Species::Cavetroll => &self.troll_cave, + Species::Mountaintroll => &self.troll_mountain, + Species::Swamptroll => &self.troll_swamp, Species::Dullahan => &self.dullahan, Species::Werewolf => &self.werewolf, Species::Occultsaurok => &self.saurok_occult, @@ -102,11 +108,13 @@ impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies } } -pub const ALL_SPECIES: [Species; 16] = [ +pub const ALL_SPECIES: [Species; 18] = [ Species::Ogre, Species::Cyclops, Species::Wendigo, - Species::Troll, + Species::Cavetroll, + Species::Mountaintroll, + Species::Swamptroll, Species::Dullahan, Species::Werewolf, Species::Occultsaurok, diff --git a/common/src/comp/body/bird_large.rs b/common/src/comp/body/bird_large.rs index 629317082a..8d282c0480 100644 --- a/common/src/comp/body/bird_large.rs +++ b/common/src/comp/body/bird_large.rs @@ -36,6 +36,7 @@ make_case_elim!( pub enum Species { Phoenix = 0, Cockatrice = 1, + Roc = 2, } ); @@ -46,6 +47,7 @@ make_case_elim!( pub struct AllSpecies { pub phoenix: SpeciesMeta, pub cockatrice: SpeciesMeta, + pub roc: SpeciesMeta, } impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies { @@ -56,11 +58,12 @@ impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies match index { Species::Phoenix => &self.phoenix, Species::Cockatrice => &self.cockatrice, + Species::Roc => &self.roc, } } } -pub const ALL_SPECIES: [Species; 2] = [Species::Phoenix, Species::Cockatrice]; +pub const ALL_SPECIES: [Species; 3] = [Species::Phoenix, Species::Cockatrice, Species::Roc]; impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies { type IntoIter = std::iter::Copied>; diff --git a/common/src/comp/body/object.rs b/common/src/comp/body/object.rs index 95d8978b9d..2cdb72aaac 100644 --- a/common/src/comp/body/object.rs +++ b/common/src/comp/body/object.rs @@ -88,6 +88,7 @@ make_case_elim!( BirdMeat = 73, FishMeat = 74, SmallMeat = 75, + Tornado = 76, } ); @@ -98,7 +99,7 @@ impl Body { } } -pub const ALL_OBJECTS: [Body; 76] = [ +pub const ALL_OBJECTS: [Body; 77] = [ Body::Arrow, Body::Bomb, Body::Scarecrow, @@ -175,6 +176,7 @@ pub const ALL_OBJECTS: [Body; 76] = [ Body::BirdMeat, Body::FishMeat, Body::SmallMeat, + Body::Tornado, ]; impl From for super::Body { @@ -260,6 +262,7 @@ impl Body { Body::BirdMeat => "bird_meat", Body::FishMeat => "fish_meat", Body::SmallMeat => "small_meat", + Body::Tornado => "tornado", } } @@ -357,6 +360,7 @@ impl Body { Body::FishMeat => 10.0, Body::BirdMeat => 10.0, Body::SmallMeat => 10.0, + Body::Tornado => 50.0, }; Mass(m) diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index eb8fc67148..6f316f1e62 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -253,7 +253,13 @@ fn default_main_tool(body: &Body) -> Item { (biped_large::Species::Ogre, biped_large::BodyType::Female) => Some( Item::new_from_asset_expect("common.items.npc_weapons.staff.ogre_staff"), ), - (biped_large::Species::Troll, _) => Some(Item::new_from_asset_expect( + (biped_large::Species::Cavetroll, _) => Some(Item::new_from_asset_expect( + "common.items.npc_weapons.hammer.troll_hammer", + )), + (biped_large::Species::Mountaintroll, _) => Some(Item::new_from_asset_expect( + "common.items.npc_weapons.hammer.troll_hammer", + )), + (biped_large::Species::Swamptroll, _) => Some(Item::new_from_asset_expect( "common.items.npc_weapons.hammer.troll_hammer", )), (biped_large::Species::Wendigo, _) => Some(Item::new_from_asset_expect( @@ -320,6 +326,9 @@ fn default_main_tool(body: &Body) -> Item { (bird_large::Species::Phoenix, _) => Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.birdlargefire", )), + (bird_large::Species::Roc, _) => Some(Item::new_from_asset_expect( + "common.items.npc_weapons.unique.birdlargebasic", + )), }, _ => None, }; diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 15dd48bae3..3ce4a8178f 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -431,7 +431,11 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc biped_large::Species::Wendigo => { "common.loot_tables.creature.biped_large.wendigo" }, - biped_large::Species::Troll => "common.loot_tables.creature.biped_large.troll", + biped_large::Species::Cavetroll + | biped_large::Species::Mountaintroll + | biped_large::Species::Swamptroll => { + "common.loot_tables.creature.biped_large.troll" + }, biped_large::Species::Occultsaurok | biped_large::Species::Mightysaurok | biped_large::Species::Slysaurok => { diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 152e49030b..d22a68604d 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -116,10 +116,12 @@ pub enum Tactic { Mindflayer, BirdLargeBreathe, BirdLargeFire, + BirdLargeBasic, Minotaur, ClayGolem, TidalWarrior, Yeti, + Tornado, } #[derive(SystemData)] @@ -1605,6 +1607,7 @@ impl<'a> AgentData<'a> { "Haniwa Sentry" => Tactic::RotatingTurret, "Bird Large Breathe" => Tactic::BirdLargeBreathe, "Bird Large Fire" => Tactic::BirdLargeFire, + "Bird Large Basic" => Tactic::BirdLargeBasic, "Mindflayer" => Tactic::Mindflayer, "Minotaur" => Tactic::Minotaur, "Clay Golem" => Tactic::ClayGolem, @@ -1833,6 +1836,7 @@ impl<'a> AgentData<'a> { &tgt_data, &read_data, ), + Tactic::Tornado => self.handle_tornado_attack(controller), Tactic::Mindflayer => self.handle_mindflayer_attack( agent, controller, @@ -1855,6 +1859,13 @@ impl<'a> AgentData<'a> { &tgt_data, &read_data, ), + Tactic::BirdLargeBasic => self.handle_birdlarge_basic_attack( + agent, + controller, + &attack_data, + &tgt_data, + &read_data, + ), Tactic::Minotaur => { self.handle_minotaur_attack(agent, controller, &attack_data, &tgt_data, &read_data) }, @@ -2906,6 +2917,12 @@ impl<'a> AgentData<'a> { } } + fn handle_tornado_attack(&self, controller: &mut Controller) { + controller + .actions + .push(ControlAction::basic_input(InputKind::Primary)); + } + fn handle_mindflayer_attack( &self, agent: &mut Agent, @@ -3261,6 +3278,29 @@ impl<'a> AgentData<'a> { } } + fn handle_birdlarge_basic_attack( + &self, + agent: &mut Agent, + controller: &mut Controller, + attack_data: &AttackData, + tgt_data: &TargetData, + read_data: &ReadData, + ) { + if can_see_tgt( + &*read_data.terrain, + self.pos, + tgt_data.pos, + attack_data.dist_sqrd, + ) && attack_data.angle < 15.0 + { + controller + .actions + .push(ControlAction::basic_input(InputKind::Primary)); + } else { + agent.target = None; + } + } + fn handle_minotaur_attack( &self, agent: &mut Agent, diff --git a/voxygen/anim/src/biped_large/mod.rs b/voxygen/anim/src/biped_large/mod.rs index 0db26fca2b..b16f0cb019 100644 --- a/voxygen/anim/src/biped_large/mod.rs +++ b/voxygen/anim/src/biped_large/mod.rs @@ -210,7 +210,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, Female) => (1.0, 7.5), (Cyclops, _) => (9.5, 7.5), (Wendigo, _) => (3.0, 7.5), - (Troll, _) => (6.0, 10.0), + (Cavetroll, _) => (9.0, 7.0), + (Mountaintroll, _) => (13.0, 2.0), + (Swamptroll, _) => (11.0, 2.0), (Dullahan, _) => (3.0, 6.0), (Werewolf, _) => (11.5, 1.0), (Occultsaurok, _) => (6.0, 3.5), @@ -228,7 +230,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, _) => (0.0, 0.0), (Cyclops, _) => (-4.5, -6.0), (Wendigo, _) => (0.0, 0.0), - (Troll, _) => (2.0, -4.0), + (Cavetroll, _) => (0.0, -4.0), + (Mountaintroll, _) => (-1.0, -8.0), + (Swamptroll, _) => (-4.0, -4.5), (Dullahan, _) => (0.0, 0.0), (Werewolf, _) => (5.0, -4.5), (Occultsaurok, _) => (1.0, -2.5), @@ -247,7 +251,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, Female) => (0.0, 28.0), (Cyclops, _) => (-2.0, 31.0), (Wendigo, _) => (-1.0, 29.0), - (Troll, _) => (-1.0, 26.5), + (Cavetroll, _) => (-1.0, 26.5), + (Mountaintroll, _) => (-1.0, 30.5), + (Swamptroll, _) => (-1.0, 28.5), (Dullahan, _) => (0.0, 29.0), (Werewolf, _) => (3.0, 26.0), (Occultsaurok, _) => (3.0, 24.0), @@ -266,7 +272,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, Female) => (0.0, -6.0), (Cyclops, _) => (1.0, -8.5), (Wendigo, _) => (-1.5, -6.0), - (Troll, _) => (1.0, -10.5), + (Cavetroll, _) => (1.0, -9.5), + (Mountaintroll, _) => (1.0, -13.5), + (Swamptroll, _) => (1.5, -11.5), (Dullahan, _) => (0.0, -6.5), (Werewolf, _) => (1.0, -10.0), (Occultsaurok, _) => (0.0, -5.0), @@ -293,7 +301,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, Female) => (8.0, 0.5, 2.0), (Cyclops, _) => (15.0, 3.5, 1.5), (Wendigo, _) => (9.0, 0.5, 2.5), - (Troll, _) => (11.0, 0.5, 4.5), + (Cavetroll, _) => (13.0, 0.0, 0.5), + (Mountaintroll, _) => (14.0, -0.5, -2.0), + (Swamptroll, _) => (14.0, 0.0, 0.0), (Dullahan, _) => (14.0, 0.5, 3.5), (Werewolf, _) => (9.0, 4.0, -3.0), (Occultsaurok, _) => (7.5, 1.0, 1.5), @@ -312,7 +322,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, Female) => (9.0, 0.5, -4.5), (Cyclops, _) => (14.0, 2.0, -5.5), (Wendigo, _) => (12.0, 0.0, -3.5), - (Troll, _) => (11.5, 0.0, -5.5), + (Cavetroll, _) => (13.5, 1.0, -6.0), + (Mountaintroll, _) => (13.5, 0.0, -10.0), + (Swamptroll, _) => (17.0, 1.0, -8.0), (Dullahan, _) => (14.5, 0.0, -2.5), (Werewolf, _) => (10.0, 2.5, -11.0), (Occultsaurok, _) => (8.0, 1.5, -5.5), @@ -331,7 +343,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, Female) => (0.0, 0.0, -2.0), (Cyclops, _) => (4.5, 1.0, -8.5), (Wendigo, _) => (2.0, 2.0, -2.5), - (Troll, _) => (5.0, 0.0, -6.0), + (Cavetroll, _) => (4.5, -1.0, -7.5), + (Mountaintroll, _) => (3.5, 0.0, -7.5), + (Swamptroll, _) => (4.5, -0.5, -7.5), (Dullahan, _) => (0.0, 0.0, -5.0), (Werewolf, _) => (4.5, 1.0, -5.0), (Occultsaurok, _) => (3.0, 0.5, -4.0), @@ -350,7 +364,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, Female) => (4.0, 0.5, -13.5), (Cyclops, _) => (6.0, 3.5, -15.5), (Wendigo, _) => (5.0, 2.5, -17.0), - (Troll, _) => (6.0, 1.5, -13.0), + (Cavetroll, _) => (5.5, 0.0, -14.0), + (Mountaintroll, _) => (4.5, 1.0, -14.0), + (Swamptroll, _) => (5.5, 0.0, -14.0), (Dullahan, _) => (4.0, 2.5, -14.0), (Werewolf, _) => (5.5, 3.0, -6.5), (Occultsaurok, _) => (3.5, 3.5, -10.0), @@ -369,7 +385,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, Female) => 1.12, (Cyclops, _) => 1.28, (Wendigo, _) => 1.1, - (Troll, _) => 1.1, + (Cavetroll, _) => 1.1, + (Mountaintroll, _) => 1.1, + (Swamptroll, _) => 1.1, (Dullahan, _) => 1.12, (Werewolf, _) => 1.0, (Occultsaurok, _) => 1.0, @@ -387,7 +405,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, Male) => 0.9, (Ogre, Female) => 0.9, (Cyclops, _) => 0.8, - (Troll, _) => 0.9, + (Cavetroll, _) => 0.9, + (Mountaintroll, _) => 0.9, + (Swamptroll, _) => 0.9, (Dullahan, _) => 0.8, (Minotaur, _) => 0.8, _ => 1.0, @@ -397,7 +417,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, Female) => (8.0, 0.0), (Cyclops, _) => (12.0, 0.0), (Wendigo, _) => (15.0, 0.0), - (Troll, _) => (12.0, 0.0), + (Cavetroll, _) => (13.0, 1.5), + (Mountaintroll, _) => (13.0, 1.5), + (Swamptroll, _) => (13.0, 1.5), (Dullahan, _) => (15.0, 0.0), (Werewolf, _) => (13.0, 0.0), (Occultsaurok, _) => (10.0, 0.0), @@ -428,29 +450,14 @@ impl<'a> From<&'a Body> for SkeletonAttr { }, hhl: match (body.species, body.body_type) { (Ogre, Male) => (-9.0, -10.0, 23.0, 1.57, -0.57, 0.0), - (Cyclops, _) => (-6.0, -10.0, 17.0, 1.57, -0.57, 0.0), - (Troll, _) => (-6.0, -10.0, 17.0, 1.57, -0.57, 0.0), - (Yeti, _) => (-6.0, -10.0, 17.0, 1.57, -0.57, 0.0), - (Blueoni, _) => (-6.0, -10.0, 17.0, 1.57, -0.57, 0.0), - (Redoni, _) => (-6.0, -10.0, 17.0, 1.57, -0.57, 0.0), _ => (-6.0, -10.0, 17.0, 1.57, -0.57, 0.0), }, hhr: match (body.species, body.body_type) { (Ogre, Male) => (-5.0, -13.0, 0.0, 1.57, -0.57, 0.0), - (Cyclops, _) => (-6.0, -10.0, 0.0, 1.57, -0.57, 0.0), - (Troll, _) => (-6.0, -10.0, 0.0, 1.57, -0.57, 0.0), - (Yeti, _) => (-6.0, -10.0, 0.0, 1.57, -0.57, 0.0), - (Blueoni, _) => (-6.0, -10.0, 0.0, 1.57, -0.57, 0.0), - (Redoni, _) => (-6.0, -10.0, 0.0, 1.57, -0.57, 0.0), _ => (-6.0, -10.0, 0.0, 1.57, -0.57, 0.0), }, hc: match (body.species, body.body_type) { (Ogre, Male) => (11.5, 9.0, -13.0, -0.57, -1.57, 1.0), - (Cyclops, _) => (8.5, 6.0, -12.0, -0.57, -1.57, 1.0), - (Troll, _) => (8.5, 6.0, -12.0, -0.57, -1.57, 1.0), - (Yeti, _) => (8.5, 6.0, -12.0, -0.57, -1.57, 1.0), - (Blueoni, _) => (8.5, 6.0, -12.0, -0.57, -1.57, 1.0), - (Redoni, _) => (8.5, 6.0, -12.0, -0.57, -1.57, 1.0), _ => (8.5, 6.0, -12.0, -0.57, -1.57, 1.0), }, sthl: match (body.species, body.body_type) { diff --git a/voxygen/anim/src/bird_large/mod.rs b/voxygen/anim/src/bird_large/mod.rs index 55ac715d38..1c4c83663e 100644 --- a/voxygen/anim/src/bird_large/mod.rs +++ b/voxygen/anim/src/bird_large/mod.rs @@ -149,54 +149,67 @@ impl<'a> From<&'a Body> for SkeletonAttr { chest: match (body.species, body.body_type) { (Phoenix, _) => (2.5, 16.0), (Cockatrice, _) => (2.5, 16.0), + (Roc, _) => (2.5, 27.5), }, neck: match (body.species, body.body_type) { (Phoenix, _) => (2.5, -5.5), (Cockatrice, _) => (5.0, -1.5), + (Roc, _) => (9.5, -1.5), }, head: match (body.species, body.body_type) { (Phoenix, _) => (6.0, 12.0), (Cockatrice, _) => (8.0, 4.5), + (Roc, _) => (17.0, -3.5), }, beak: match (body.species, body.body_type) { (Phoenix, _) => (5.0, 3.0), (Cockatrice, _) => (2.0, -3.0), + (Roc, _) => (0.0, -3.0), }, tail_front: match (body.species, body.body_type) { (Phoenix, _) => (-9.5, -1.0), (Cockatrice, _) => (-5.0, -2.5), + (Roc, _) => (-7.5, -3.5), }, tail_rear: match (body.species, body.body_type) { (Phoenix, _) => (-11.0, 0.0), (Cockatrice, _) => (-8.0, -3.0), + (Roc, _) => (-8.0, -3.0), }, wing_in: match (body.species, body.body_type) { (Phoenix, _) => (3.0, 2.5, 2.0), (Cockatrice, _) => (3.5, 7.0, 3.5), + (Roc, _) => (5.5, 7.5, -1.0), }, wing_mid: match (body.species, body.body_type) { (Phoenix, _) => (10.0, 1.0, 0.0), (Cockatrice, _) => (6.0, 0.0, 0.0), + (Roc, _) => (12.0, 1.0, -0.5), }, wing_out: match (body.species, body.body_type) { (Phoenix, _) => (7.0, 2.0, 1.5), (Cockatrice, _) => (4.0, -1.0, 1.0), + (Roc, _) => (10.0, -2.0, 0.0), }, leg: match (body.species, body.body_type) { (Phoenix, _) => (4.0, 1.5, 12.0), (Cockatrice, _) => (3.5, 2.5, 13.0), + (Roc, _) => (5.5, -1.5, 17.5), }, foot: match (body.species, body.body_type) { (Phoenix, _) => (0.5, -0.5, -2.5), (Cockatrice, _) => (0.5, -3.0, -3.0), + (Roc, _) => (2.5, -2.5, -5.5), }, scaler: match (body.species, body.body_type) { (Phoenix, _) => (1.0), (Cockatrice, _) => (1.0), + (Roc, _) => (1.0), }, feed: match (body.species, body.body_type) { (Phoenix, _) => (-0.65), (Cockatrice, _) => (-0.5), + (Roc, _) => (-0.65), }, } } diff --git a/voxygen/src/render/pipelines/particle.rs b/voxygen/src/render/pipelines/particle.rs index 947c861695..381ff30cd2 100644 --- a/voxygen/src/render/pipelines/particle.rs +++ b/voxygen/src/render/pipelines/particle.rs @@ -83,6 +83,7 @@ pub enum ParticleMode { Water = 30, IceSpikes = 31, Drip = 32, + Tornado = 33, } impl ParticleMode { diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index 33452ec101..d21640e0d6 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -323,6 +323,9 @@ impl ParticleMgr { Body::Object(object::Body::BoltNature) => { self.maintain_boltnature_particles(scene_data, pos, vel) }, + Body::Object(object::Body::Tornado) => { + self.maintain_tornado_particles(scene_data, pos, vel) + }, Body::Object( object::Body::Bomb | object::Body::FireworkBlue @@ -498,6 +501,26 @@ impl ParticleMgr { ); } + fn maintain_tornado_particles(&mut self, scene_data: &SceneData, pos: &Pos, vel: Option<&Vel>) { + let time = scene_data.state.get_time(); + let dt = scene_data.state.get_delta_time(); + let mut rng = thread_rng(); + + // nature + self.particles.resize_with( + self.particles.len() + usize::from(self.scheduler.heartbeats(Duration::from_millis(5))), + || { + Particle::new( + Duration::from_millis(1000), + time, + ParticleMode::Tornado, + pos.0.map(|e| e + rng.gen_range(-0.25..0.25)) + + vel.map_or(Vec3::zero(), |v| -v.0 * dt * rng.gen::()), + ) + }, + ); + } + fn maintain_bomb_particles(&mut self, scene_data: &SceneData, pos: &Pos, vel: Option<&Vel>) { span!( _guard, diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 9c12b4c130..bff44eb784 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -489,7 +489,7 @@ pub fn apply_caves_supplement<'a>( 0 => comp::biped_large::Species::Blueoni, _ => comp::biped_large::Species::Redoni, }, - _ => comp::biped_large::Species::Troll, + _ => comp::biped_large::Species::Cavetroll, }; comp::biped_large::Body::random_with(dynamic_rng, &species) .into() diff --git a/world/src/layer/wildlife.rs b/world/src/layer/wildlife.rs index f15ec3a9bc..5c240d1235 100644 --- a/world/src/layer/wildlife.rs +++ b/world/src/layer/wildlife.rs @@ -454,9 +454,8 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( 0 => { biped_large::Body::random_with(rng, &biped_large::Species::Ogre).into() }, - 1 => { - biped_large::Body::random_with(rng, &biped_large::Species::Troll).into() - }, + 1 => biped_large::Body::random_with(rng, &biped_large::Species::Swamptroll) + .into(), _ => biped_large::Body::random_with(rng, &biped_large::Species::Cyclops) .into(), }) From 3ba0500b90c7fcb35fd356a09e5db7ec7bf68ba5 Mon Sep 17 00:00:00 2001 From: Snowram Date: Fri, 4 Jun 2021 00:42:50 +0200 Subject: [PATCH 064/180] Tornado summoning attack --- .../custom/birdlargebasic/summontornadoes.ron | 7 +- .../custom/mindflayer/summonminions.ron | 4 +- .../abilities/custom/tidalwarrior/totem.ron | 4 +- .../common/abilities/custom/tornado/spin.ron | 14 ++-- .../items/npc_weapons/unique/tornado.ron | 19 +++++ common/src/comp/ability.rs | 6 ++ common/src/comp/inventory/loadout_builder.rs | 3 + common/src/event.rs | 3 +- common/src/states/basic_summon.rs | 77 +++++++++++++++++-- common/src/states/behavior.rs | 4 + common/systems/src/character_behavior.rs | 7 +- server/src/cmd.rs | 4 +- server/src/events/entity_creation.rs | 9 ++- server/src/events/mod.rs | 2 + server/src/rtsim/tick.rs | 3 +- server/src/state_ext.rs | 6 +- server/src/sys/terrain.rs | 3 +- voxygen/anim/src/bird_large/mod.rs | 2 +- 18 files changed, 147 insertions(+), 30 deletions(-) create mode 100644 assets/common/items/npc_weapons/unique/tornado.ron diff --git a/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron b/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron index 9ad07751b4..21c90ae04a 100644 --- a/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron +++ b/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron @@ -3,11 +3,16 @@ BasicSummon( cast_duration: 1.0, recover_duration: 0.5, summon_amount: 6, + summon_distance: (1, 5), summon_info: ( body: Object(Tornado), scale: None, - health_scaling: 80, + health_scaling: None, loadout_config: None, skillset_config: None, ), + duration: Some(( + secs: 5, + nanos: 0, + )), ) \ No newline at end of file diff --git a/assets/common/abilities/custom/mindflayer/summonminions.ron b/assets/common/abilities/custom/mindflayer/summonminions.ron index 99117ce2c0..7fdfb01ec1 100644 --- a/assets/common/abilities/custom/mindflayer/summonminions.ron +++ b/assets/common/abilities/custom/mindflayer/summonminions.ron @@ -3,14 +3,16 @@ BasicSummon( cast_duration: 1.0, recover_duration: 0.5, summon_amount: 6, + summon_distance: (3, 3), summon_info: ( body: BipedSmall(( species: Husk, body_type: Male, )), scale: None, - health_scaling: 80, + health_scaling: Some(80), loadout_config: Some(HuskSummon), skillset_config: None, ), + duration: None, ) diff --git a/assets/common/abilities/custom/tidalwarrior/totem.ron b/assets/common/abilities/custom/tidalwarrior/totem.ron index 31df64a7cd..c997b8954c 100644 --- a/assets/common/abilities/custom/tidalwarrior/totem.ron +++ b/assets/common/abilities/custom/tidalwarrior/totem.ron @@ -3,11 +3,13 @@ BasicSummon( cast_duration: 1.0, recover_duration: 0.5, summon_amount: 1, + summon_distance: (1, 1), summon_info: ( body: Object(SeaLantern), scale: None, - health_scaling: 0, + health_scaling: Some(0), loadout_config: None, skillset_config: None, ), + duration: None, ) diff --git a/assets/common/abilities/custom/tornado/spin.ron b/assets/common/abilities/custom/tornado/spin.ron index df0320d9c2..de072c981e 100644 --- a/assets/common/abilities/custom/tornado/spin.ron +++ b/assets/common/abilities/custom/tornado/spin.ron @@ -1,13 +1,13 @@ SpinMelee( - buildup_duration: 0.2, - swing_duration: 0.6, - recover_duration: 0.2, - base_damage: 70, - base_poise_damage: 25, - knockback: ( strength: 0.0, direction: Away), + buildup_duration: 0.0, + swing_duration: 0.5, + recover_duration: 0.0, + base_damage: 15000, + base_poise_damage: 200, + knockback: ( strength: 200.0, direction: Away), range: 3.5, damage_effect: None, - energy_cost: 100, + energy_cost: 0, is_infinite: true, movement_behavior: AxeHover, is_interruptible: false, diff --git a/assets/common/items/npc_weapons/unique/tornado.ron b/assets/common/items/npc_weapons/unique/tornado.ron new file mode 100644 index 0000000000..14c1054395 --- /dev/null +++ b/assets/common/items/npc_weapons/unique/tornado.ron @@ -0,0 +1,19 @@ +ItemDef( + name: "Tornado", + description: "Tornado weapon", + kind: Tool(( + kind: Natural, + hands: Two, + stats: Direct(( + equip_time_secs: 0.01, + power: 1.0, + poise_strength: 1.0, + speed: 1.0, + crit_chance: 0.0, + crit_mult: 0.0, + )), + )), + quality: Low, + tags: [], + ability_spec: Some(Custom("Tornado")), +) \ No newline at end of file diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index d224061a8e..f755fe58d5 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -280,7 +280,9 @@ pub enum CharacterAbility { cast_duration: f32, recover_duration: f32, summon_amount: u32, + summon_distance: (f32, f32), summon_info: basic_summon::SummonInfo, + duration: Option, }, SelfBuff { buildup_duration: f32, @@ -1737,15 +1739,19 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { cast_duration, recover_duration, summon_amount, + summon_distance, summon_info, + duration, } => CharacterState::BasicSummon(basic_summon::Data { static_data: basic_summon::StaticData { buildup_duration: Duration::from_secs_f32(*buildup_duration), cast_duration: Duration::from_secs_f32(*cast_duration), recover_duration: Duration::from_secs_f32(*recover_duration), summon_amount: *summon_amount, + summon_distance: *summon_distance, summon_info: *summon_info, ability_info, + duration: *duration, }, summon_count: 0, timer: Duration::default(), diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 6f316f1e62..1978cf3fd9 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -306,6 +306,9 @@ fn default_main_tool(body: &Body) -> Item { object::Body::SeaLantern => Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.tidal_totem", )), + object::Body::Tornado => Some(Item::new_from_asset_expect( + "common.items.npc_weapons.unique.tornado", + )), _ => None, }, Body::BipedSmall(biped_small) => match (biped_small.species, biped_small.body_type) { diff --git a/common/src/event.rs b/common/src/event.rs index d21eee05c5..ffa178fdd4 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -117,7 +117,7 @@ pub enum ServerEvent { pos: comp::Pos, stats: comp::Stats, skill_set: comp::SkillSet, - health: comp::Health, + health: Option, poise: comp::Poise, loadout: comp::inventory::loadout::Loadout, body: comp::Body, @@ -127,6 +127,7 @@ pub enum ServerEvent { home_chunk: Option, drop_item: Option, rtsim_entity: Option, + projectile: Option, }, CreateShip { pos: comp::Pos, diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 6a49e33c60..44c71b9f47 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -2,7 +2,7 @@ use crate::{ comp::{ self, inventory::loadout_builder::{self, LoadoutBuilder}, - Behavior, BehaviorCapability, CharacterState, StateUpdate, + Behavior, BehaviorCapability, CharacterState, Projectile, StateUpdate, }, event::{LocalEvent, ServerEvent}, outcome::Outcome, @@ -11,9 +11,13 @@ use crate::{ behavior::{CharacterBehavior, JoinData}, utils::*, }, + terrain::Block, + vol::ReadVol, }; +use rand::Rng; use serde::{Deserialize, Serialize}; -use std::time::Duration; +use std::{f32::consts::PI, time::Duration}; +use vek::*; /// Separated out to condense update portions of character state #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -26,10 +30,14 @@ pub struct StaticData { pub recover_duration: Duration, /// How many creatures the state should summon pub summon_amount: u32, + /// Range of the summons relative to the summonner + pub summon_distance: (f32, f32), /// Information about the summoned creature pub summon_info: SummonInfo, /// Miscellaneous information about the ability pub ability_info: AbilityInfo, + /// Duration of the summoned entity + pub duration: Option, } #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -102,15 +110,67 @@ impl CharacterBehavior for Data { }; let stats = comp::Stats::new("Summon".to_string()); + + let health_scaling = self + .static_data + .summon_info + .health_scaling + .map(|health_scaling| comp::Health::new(body, health_scaling)); + + // Ray cast to check where summon should happen + let summon_frac = + self.summon_count as f32 / self.static_data.summon_amount as f32; + + let length = rand::thread_rng().gen_range( + self.static_data.summon_distance.0..self.static_data.summon_distance.1, + ); + + // Summon in a clockwise fashion + let ray_vector = Vec3::new( + (summon_frac * 2.0 * PI).sin() * length, + (summon_frac * 2.0 * PI).cos() * length, + data.body.eye_height(), + ); + + // Check for collision on the xy plane + let obstacle_xy = data + .terrain + .ray(data.pos.0, data.pos.0 + length * ray_vector) + .until(Block::is_solid) + .cast() + .0; + + let collision_vector = Vec3::new( + data.pos.0.x + (summon_frac * 2.0 * PI).sin() * obstacle_xy, + data.pos.0.y + (summon_frac * 2.0 * PI).cos() * obstacle_xy, + data.pos.0.z, + ); + + // Check for collision in z up to 50 blocks + let obstacle_z = data + .terrain + .ray(collision_vector, collision_vector - Vec3::unit_z() * 50.0) + .until(Block::is_solid) + .cast() + .0; + + // If a duration is specified, create a projectile componenent for the npc + let projectile = self.static_data.duration.map(|duration| Projectile { + hit_solid: Vec::new(), + hit_entity: Vec::new(), + time_left: duration, + owner: Some(*data.uid), + ignore_group: true, + is_sticky: false, + is_point: false, + }); + // Send server event to create npc update.server_events.push_front(ServerEvent::CreateNpc { - pos: *data.pos, + pos: comp::Pos(collision_vector - Vec3::unit_z() * obstacle_z), stats, skill_set, - health: comp::Health::new( - body, - self.static_data.summon_info.health_scaling, - ), + health: health_scaling, poise: comp::Poise::new(body), loadout, body, @@ -129,6 +189,7 @@ impl CharacterBehavior for Data { home_chunk: None, drop_item: None, rtsim_entity: None, + projectile, }); // Send local event used for frontend shenanigans @@ -186,7 +247,7 @@ impl CharacterBehavior for Data { pub struct SummonInfo { body: comp::Body, scale: Option, - health_scaling: u16, + health_scaling: Option, // TODO: use assets for specifying skills and loadout? loadout_config: Option, skillset_config: Option, diff --git a/common/src/states/behavior.rs b/common/src/states/behavior.rs index a1fea34724..d66c827b9a 100644 --- a/common/src/states/behavior.rs +++ b/common/src/states/behavior.rs @@ -5,6 +5,7 @@ use crate::{ InventoryAction, Mass, Melee, Ori, PhysicsState, Pos, SkillSet, StateUpdate, Stats, Vel, }, resources::DeltaTime, + terrain::TerrainGrid, uid::Uid, }; use specs::{ @@ -96,6 +97,7 @@ pub struct JoinData<'a> { pub msm: &'a MaterialStatManifest, pub combo: &'a Combo, pub alignment: Option<&'a comp::Alignment>, + pub terrain: &'a TerrainGrid, } type RestrictedMut<'a, C> = PairedStorage< @@ -128,6 +130,7 @@ pub struct JoinStruct<'a> { pub skill_set: &'a SkillSet, pub combo: &'a Combo, pub alignment: Option<&'a comp::Alignment>, + pub terrain: &'a TerrainGrid, } impl<'a> JoinData<'a> { @@ -161,6 +164,7 @@ impl<'a> JoinData<'a> { msm, combo: j.combo, alignment: j.alignment, + terrain: j.terrain, } } } diff --git a/common/systems/src/character_behavior.rs b/common/systems/src/character_behavior.rs index e7db1e0b36..382067f7d7 100644 --- a/common/systems/src/character_behavior.rs +++ b/common/systems/src/character_behavior.rs @@ -1,6 +1,6 @@ use specs::{ - shred::ResourceId, Entities, Join, LazyUpdate, Read, ReadStorage, SystemData, World, Write, - WriteStorage, + shred::ResourceId, Entities, Join, LazyUpdate, Read, ReadExpect, ReadStorage, SystemData, + World, Write, WriteStorage, }; use common::{ @@ -16,6 +16,7 @@ use common::{ self, behavior::{CharacterBehavior, JoinData, JoinStruct}, }, + terrain::TerrainGrid, uid::Uid, }; use common_ecs::{Job, Origin, Phase, System}; @@ -67,6 +68,7 @@ pub struct ReadData<'a> { msm: Read<'a, MaterialStatManifest>, combos: ReadStorage<'a, Combo>, alignments: ReadStorage<'a, comp::Alignment>, + terrain: ReadExpect<'a, TerrainGrid>, } /// ## Character Behavior System @@ -280,6 +282,7 @@ impl<'a> System<'a> for Sys { skill_set: &skill_set, combo: &combo, alignment: read_data.alignments.get(entity), + terrain: &read_data.terrain, }; for action in actions { diff --git a/server/src/cmd.rs b/server/src/cmd.rs index b8d56c4fbb..fcdd197e1a 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -1014,7 +1014,7 @@ fn handle_spawn( pos, comp::Stats::new(get_npc_name(id, npc::BodyType::from_body(body))), comp::SkillSet::default(), - comp::Health::new(body, 1), + Some(comp::Health::new(body, 1)), comp::Poise::new(body), inventory, body, @@ -1116,7 +1116,7 @@ fn handle_spawn_training_dummy( pos, stats, skill_set, - health, + Some(health), poise, Inventory::new_empty(), body, diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index c66317cf0e..014ed849a7 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -53,7 +53,7 @@ pub fn handle_create_npc( pos: Pos, stats: Stats, skill_set: SkillSet, - health: Health, + health: Option, poise: Poise, loadout: Loadout, body: Body, @@ -63,6 +63,7 @@ pub fn handle_create_npc( drop_item: Option, home_chunk: Option, rtsim_entity: Option, + projectile: Option, ) { let inventory = Inventory::new_with_loadout(loadout); @@ -96,6 +97,12 @@ pub fn handle_create_npc( entity }; + let entity = if let Some(projectile) = projectile { + entity.with(projectile) + } else { + entity + }; + let new_entity = entity.build(); // Add to group system if a pet diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index 7079b3bda3..7ab52e6b60 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -147,6 +147,7 @@ impl Server { home_chunk, drop_item, rtsim_entity, + projectile, } => handle_create_npc( self, pos, @@ -162,6 +163,7 @@ impl Server { drop_item, home_chunk, rtsim_entity, + projectile, ), ServerEvent::CreateShip { pos, diff --git a/server/src/rtsim/tick.rs b/server/src/rtsim/tick.rs index c729445fa9..70f0608f61 100644 --- a/server/src/rtsim/tick.rs +++ b/server/src/rtsim/tick.rs @@ -127,7 +127,7 @@ impl<'a> System<'a> for Sys { pos: comp::Pos(spawn_pos), stats: comp::Stats::new(entity.get_name()), skill_set: comp::SkillSet::default(), - health: comp::Health::new(body, 10), + health: Some(comp::Health::new(body, 10)), loadout: match body { comp::Body::Humanoid(_) => entity.get_loadout(), _ => LoadoutBuilder::new().build(), @@ -146,6 +146,7 @@ impl<'a> System<'a> for Sys { drop_item: None, home_chunk: None, rtsim_entity, + projectile: None, }, }; server_emitter.emit(event); diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index a929d8224d..339ec7b921 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -39,7 +39,7 @@ pub trait StateExt { pos: comp::Pos, stats: comp::Stats, skill_set: comp::SkillSet, - health: comp::Health, + health: Option, poise: comp::Poise, inventory: comp::Inventory, body: comp::Body, @@ -175,7 +175,7 @@ impl StateExt for State { pos: comp::Pos, stats: comp::Stats, skill_set: comp::SkillSet, - health: comp::Health, + health: Option, poise: comp::Poise, inventory: comp::Inventory, body: comp::Body, @@ -215,7 +215,7 @@ impl StateExt for State { )) .with(stats) .with(skill_set) - .with(health) + .maybe_with(health) .with(poise) .with(comp::Alignment::Npc) .with(comp::CharacterState::default()) diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 458c710b28..eb668c7690 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -239,7 +239,7 @@ impl<'a> System<'a> for Sys { loadout_builder.build() }; - let health = comp::Health::new(body, entity.level.unwrap_or(0)); + let health = Some(comp::Health::new(body, entity.level.unwrap_or(0))); let poise = comp::Poise::new(body); let can_speak = match body { @@ -293,6 +293,7 @@ impl<'a> System<'a> for Sys { home_chunk: Some(comp::HomeChunk(key)), drop_item: entity.loot_drop, rtsim_entity: None, + projectile: None, }) } diff --git a/voxygen/anim/src/bird_large/mod.rs b/voxygen/anim/src/bird_large/mod.rs index 1c4c83663e..67e7da5166 100644 --- a/voxygen/anim/src/bird_large/mod.rs +++ b/voxygen/anim/src/bird_large/mod.rs @@ -209,7 +209,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { feed: match (body.species, body.body_type) { (Phoenix, _) => (-0.65), (Cockatrice, _) => (-0.5), - (Roc, _) => (-0.65), + (Roc, _) => (-0.4), }, } } From cb0566299a0765f8877e919fa2c2a653ee7f92fa Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Thu, 3 Jun 2021 21:25:42 -0400 Subject: [PATCH 065/180] Make tornado (and empty models in general) work on WGPU. --- .../common/abilities/ability_set_manifest.ron | 4 +- .../abilities/custom/birdlargebasic/dash.ron | 20 ++ .../custom/birdlargebasic/summontornadoes.ron | 8 +- .../custom/birdlargebasic/triplestrike.ron | 57 ++++++ .../common/abilities/custom/tornado/spin.ron | 6 +- common/src/comp/body.rs | 10 +- common/src/comp/body/object.rs | 1 + common/src/comp/inventory/loadout_builder.rs | 13 +- server/src/rtsim/tick.rs | 5 + server/src/sys/agent.rs | 53 +++++- voxygen/anim/src/biped_large/mod.rs | 2 +- voxygen/anim/src/bird_large/dash.rs | 177 ++++++++++++++++++ voxygen/anim/src/bird_large/mod.rs | 8 +- voxygen/anim/src/bird_large/summon.rs | 114 +++++++++++ voxygen/src/render/pipelines/figure.rs | 2 +- voxygen/src/scene/figure/mod.rs | 75 +++++++- voxygen/src/scene/simple.rs | 20 +- world/src/layer/wildlife.rs | 23 ++- 18 files changed, 544 insertions(+), 54 deletions(-) create mode 100644 assets/common/abilities/custom/birdlargebasic/dash.ron create mode 100644 assets/common/abilities/custom/birdlargebasic/triplestrike.ron create mode 100644 voxygen/anim/src/bird_large/dash.rs create mode 100644 voxygen/anim/src/bird_large/summon.rs diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index 7c300eebeb..e84291d621 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -265,8 +265,8 @@ ], ), Custom("Bird Large Basic"): ( - primary: "common.abilities.custom.birdlargebreathe.firebomb", - secondary: "common.abilities.custom.birdlargebreathe.triplestrike", + primary: "common.abilities.custom.birdlargebasic.dash", + secondary: "common.abilities.custom.birdlargebasic.triplestrike", abilities: [ (None, "common.abilities.custom.birdlargebasic.summontornadoes"), ], diff --git a/assets/common/abilities/custom/birdlargebasic/dash.ron b/assets/common/abilities/custom/birdlargebasic/dash.ron new file mode 100644 index 0000000000..2421a1dd02 --- /dev/null +++ b/assets/common/abilities/custom/birdlargebasic/dash.ron @@ -0,0 +1,20 @@ +DashMelee( + energy_cost: 0, + base_damage: 70, + scaled_damage: 150, + base_poise_damage: 50, + scaled_poise_damage: 100, + base_knockback: 12.0, + scaled_knockback: 17.0, + range: 6.0, + angle: 20.0, + energy_drain: 0, + forward_speed: 1.5, + buildup_duration: 0.5, + charge_duration: 1.2, + swing_duration: 0.1, + recover_duration: 1.1, + charge_through: true, + is_interruptible: false, + damage_kind: Crushing, +) diff --git a/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron b/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron index 21c90ae04a..9e487d8922 100644 --- a/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron +++ b/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron @@ -1,9 +1,9 @@ BasicSummon( buildup_duration: 0.5, - cast_duration: 1.0, - recover_duration: 0.5, + cast_duration: 0.2, + recover_duration: 0.2, summon_amount: 6, - summon_distance: (1, 5), + summon_distance: (1, 3), summon_info: ( body: Object(Tornado), scale: None, @@ -12,7 +12,7 @@ BasicSummon( skillset_config: None, ), duration: Some(( - secs: 5, + secs: 10, nanos: 0, )), ) \ No newline at end of file diff --git a/assets/common/abilities/custom/birdlargebasic/triplestrike.ron b/assets/common/abilities/custom/birdlargebasic/triplestrike.ron new file mode 100644 index 0000000000..eb332435a4 --- /dev/null +++ b/assets/common/abilities/custom/birdlargebasic/triplestrike.ron @@ -0,0 +1,57 @@ +ComboMelee( + stage_data: [ + ( + stage: 1, + base_damage: 100, + damage_increase: 0, + base_poise_damage: 0, + poise_damage_increase: 0, + knockback: 5.0, + range: 4.5, + angle: 30.0, + base_buildup_duration: 0.4, + base_swing_duration: 0.1, + base_recover_duration: 0.3, + forward_movement: 2.0, + damage_kind: Slashing, + ), + ( + stage: 2, + base_damage: 80, + damage_increase: 0, + base_poise_damage: 0, + poise_damage_increase: 0, + knockback: 5.0, + range: 3.5, + angle: 30.0, + base_buildup_duration: 0.4, + base_swing_duration: 0.1, + base_recover_duration: 0.3, + forward_movement: 1.5, + damage_kind: Slashing, + ), + ( + stage: 3, + base_damage: 130, + damage_increase: 0, + base_poise_damage: 0, + poise_damage_increase: 0, + knockback: 10.0, + range: 3.5, + angle: 30.0, + base_buildup_duration: 0.65, + base_swing_duration: 0.1, + base_recover_duration: 0.3, + forward_movement: 1.5, + damage_kind: Slashing, + ), + ], + initial_energy_gain: 0, + max_energy_gain: 0, + energy_increase: 0, + speed_increase: 0.0, + max_speed_increase: 0.0, + scales_from_combo: 0, + is_interruptible: false, + ori_modifier: 0.7, +) diff --git a/assets/common/abilities/custom/tornado/spin.ron b/assets/common/abilities/custom/tornado/spin.ron index de072c981e..2418c50b38 100644 --- a/assets/common/abilities/custom/tornado/spin.ron +++ b/assets/common/abilities/custom/tornado/spin.ron @@ -2,9 +2,9 @@ SpinMelee( buildup_duration: 0.0, swing_duration: 0.5, recover_duration: 0.0, - base_damage: 15000, - base_poise_damage: 200, - knockback: ( strength: 200.0, direction: Away), + base_damage: 50, + base_poise_damage: 100, + knockback: ( strength: 50.0, direction: Away), range: 3.5, damage_effect: None, energy_cost: 0, diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 10854409bb..7b1765ebed 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -472,7 +472,10 @@ impl Body { }, Body::FishMedium(_) => 250, Body::Dragon(_) => 5000, - Body::BirdLarge(_) => 3000, + Body::BirdLarge(bird_large) => match bird_large.species { + bird_large::Species::Roc => 2400, + _ => 3000, + }, Body::FishSmall(_) => 20, Body::BipedLarge(biped_large) => match biped_large.species { biped_large::Species::Ogre => 3200, @@ -591,7 +594,10 @@ impl Body { }, Body::FishMedium(_) => 10, Body::Dragon(_) => 500, - Body::BirdLarge(_) => 120, + Body::BirdLarge(bird_large) => match bird_large.species { + bird_large::Species::Roc => 100, + _ => 120, + }, Body::FishSmall(_) => 10, Body::BipedLarge(biped_large) => match biped_large.species { biped_large::Species::Ogre => 70, diff --git a/common/src/comp/body/object.rs b/common/src/comp/body/object.rs index 2cdb72aaac..b452715362 100644 --- a/common/src/comp/body/object.rs +++ b/common/src/comp/body/object.rs @@ -376,6 +376,7 @@ impl Body { Body::HaniwaSentry => Vec3::new(0.8, 0.8, 1.4), Body::SeaLantern => Vec3::new(0.5, 0.5, 1.0), Body::Snowball => Vec3::broadcast(2.5), + Body::Tornado => Vec3::new(2.0, 2.0, 3.4), _ => Vec3::broadcast(0.5), } } diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 1978cf3fd9..c1ced2481c 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -253,13 +253,12 @@ fn default_main_tool(body: &Body) -> Item { (biped_large::Species::Ogre, biped_large::BodyType::Female) => Some( Item::new_from_asset_expect("common.items.npc_weapons.staff.ogre_staff"), ), - (biped_large::Species::Cavetroll, _) => Some(Item::new_from_asset_expect( - "common.items.npc_weapons.hammer.troll_hammer", - )), - (biped_large::Species::Mountaintroll, _) => Some(Item::new_from_asset_expect( - "common.items.npc_weapons.hammer.troll_hammer", - )), - (biped_large::Species::Swamptroll, _) => Some(Item::new_from_asset_expect( + ( + biped_large::Species::Mountaintroll + | biped_large::Species::Swamptroll + | biped_large::Species::Cavetroll, + _, + ) => Some(Item::new_from_asset_expect( "common.items.npc_weapons.hammer.troll_hammer", )), (biped_large::Species::Wendigo, _) => Some(Item::new_from_asset_expect( diff --git a/server/src/rtsim/tick.rs b/server/src/rtsim/tick.rs index 70f0608f61..cb4457f37f 100644 --- a/server/src/rtsim/tick.rs +++ b/server/src/rtsim/tick.rs @@ -137,6 +137,11 @@ impl<'a> System<'a> for Sys { agent, alignment: match body { comp::Body::Humanoid(_) => comp::Alignment::Npc, + comp::Body::BirdLarge(bird_large) => match bird_large.species { + comp::bird_large::Species::Roc => comp::Alignment::Enemy, + comp::bird_large::Species::Cockatrice => comp::Alignment::Enemy, + _ => comp::Alignment::Wild, + }, _ => comp::Alignment::Wild, }, scale: match body { diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index d22a68604d..33d181693c 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -3286,18 +3286,57 @@ impl<'a> AgentData<'a> { tgt_data: &TargetData, read_data: &ReadData, ) { - if can_see_tgt( - &*read_data.terrain, - self.pos, - tgt_data.pos, - attack_data.dist_sqrd, - ) && attack_data.angle < 15.0 + if !read_data + .terrain + .ray(self.pos.0, self.pos.0 - (Vec3::unit_z() * 2.0)) + .until(Block::is_solid) + .cast() + .1 + .map_or(true, |b| b.is_some()) + { + // Fly to target + controller + .actions + .push(ControlAction::basic_input(InputKind::Fly)); + let move_dir = tgt_data.pos.0 - self.pos.0; + controller.inputs.move_dir = + move_dir.xy().try_normalized().unwrap_or_else(Vec2::zero) * 2.0; + controller.inputs.move_z = move_dir.z - 0.5; + } else if agent.action_state.timer > 7.0 { + controller + .actions + .push(ControlAction::basic_input(InputKind::Ability(0))); + // Reset timer + agent.action_state.timer = 0.0; + } else if attack_data.angle < 90.0 + && attack_data.dist_sqrd < (1.5 * attack_data.min_attack_dist).powi(2) + && agent.action_state.timer < 6.0 + { + controller.inputs.move_dir = Vec2::zero(); + controller + .actions + .push(ControlAction::basic_input(InputKind::Secondary)); + agent.action_state.timer += read_data.dt.0; + } else if attack_data.dist_sqrd < (3.0 * attack_data.min_attack_dist).powi(2) + && attack_data.dist_sqrd > (2.0 * attack_data.min_attack_dist).powi(2) + && attack_data.angle < 90.0 + && agent.action_state.timer < 6.0 { controller .actions .push(ControlAction::basic_input(InputKind::Primary)); + controller.inputs.move_dir = (tgt_data.pos.0 - self.pos.0) + .xy() + .rotated_z(-0.47 * PI) + .try_normalized() + .unwrap_or_else(Vec2::unit_y); + agent.action_state.timer += read_data.dt.0; + } else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) { + self.path_toward_target(agent, controller, tgt_data, read_data, true, None); + agent.action_state.timer += read_data.dt.0; } else { - agent.target = None; + self.path_toward_target(agent, controller, tgt_data, read_data, false, None); + agent.action_state.timer += read_data.dt.0; } } diff --git a/voxygen/anim/src/biped_large/mod.rs b/voxygen/anim/src/biped_large/mod.rs index b16f0cb019..cee364ed3b 100644 --- a/voxygen/anim/src/biped_large/mod.rs +++ b/voxygen/anim/src/biped_large/mod.rs @@ -419,7 +419,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Wendigo, _) => (15.0, 0.0), (Cavetroll, _) => (13.0, 1.5), (Mountaintroll, _) => (13.0, 1.5), - (Swamptroll, _) => (13.0, 1.5), + (Swamptroll, _) => (15.0, 0.5), (Dullahan, _) => (15.0, 0.0), (Werewolf, _) => (13.0, 0.0), (Occultsaurok, _) => (10.0, 0.0), diff --git a/voxygen/anim/src/bird_large/dash.rs b/voxygen/anim/src/bird_large/dash.rs new file mode 100644 index 0000000000..65717f32fd --- /dev/null +++ b/voxygen/anim/src/bird_large/dash.rs @@ -0,0 +1,177 @@ +use super::{ + super::{vek::*, Animation}, + BirdLargeSkeleton, SkeletonAttr, +}; +use common::states::utils::StageSection; +use std::f32::consts::PI; + +pub struct DashAnimation; +type DashAnimationDependency<'a> = ( + Vec3, + Vec3, + Vec3, + f32, + Option, + f32, + f32, +); + +impl Animation for DashAnimation { + type Dependency<'a> = DashAnimationDependency<'a>; + type Skeleton = BirdLargeSkeleton; + + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"bird_large_dash\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "bird_large_dash")] + fn update_skeleton_inner<'a>( + skeleton: &Self::Skeleton, + (velocity, orientation, last_ori, acc_vel, stage_section, global_time, timer): Self::Dependency<'a>, + anim_time: f32, + rate: &mut f32, + s_a: &SkeletonAttr, + ) -> Self::Skeleton { + let mut next = (*skeleton).clone(); + let speed = (Vec2::::from(velocity).magnitude()).min(22.0); + *rate = 1.0; + + let (movement1base, chargemovementbase, movement2base, movement3, legtell) = + match stage_section { + Some(StageSection::Buildup) => (anim_time.sqrt(), 0.0, 0.0, 0.0, anim_time), + Some(StageSection::Charge) => (1.0, 1.0, 0.0, 0.0, 0.0), + Some(StageSection::Swing) => (1.0, 0.0, anim_time.powi(4), 0.0, 1.0), + Some(StageSection::Recover) => (1.0, 0.0, 1.0, anim_time, 1.0), + _ => (0.0, 0.0, 0.0, 0.0, 0.0), + }; + let pullback = 1.0 - movement3; + let subtract = global_time - timer; + let check = subtract - subtract.trunc(); + let mirror = (check - 0.5).signum(); + let movement1abs = movement1base * pullback; + let movement2abs = movement2base * pullback; + let legtwitch = (legtell * 6.0).sin() * pullback; + let legswing = legtell * pullback; + let chargeanim = (chargemovementbase * anim_time * 15.0).sin(); + + //let speednorm = speed / 13.0; + let speednorm = (speed / 13.0).powf(0.25); + + let speedmult = 0.8; + let lab: f32 = 0.6; //6 + + // acc_vel and anim_time mix to make sure phase lenght isn't starting at + // +infinite + let mixed_vel = acc_vel + anim_time * 5.0; //sets run frequency using speed, with anim_time setting a floor + + let short = ((1.0 + / (0.72 + + 0.28 * ((mixed_vel * 1.0 * lab * speedmult + PI * -0.15 - 0.5).sin()).powi(2))) + .sqrt()) + * ((mixed_vel * 1.0 * lab * speedmult + PI * -0.15 - 0.5).sin()) + * speednorm; + + // + let shortalt = (mixed_vel * 1.0 * lab * speedmult + PI * 3.0 / 8.0 - 0.5).sin() * speednorm; + + let ori: Vec2 = Vec2::from(orientation); + let last_ori = Vec2::from(last_ori); + let tilt = if ::vek::Vec2::new(ori, last_ori) + .map(|o| o.magnitude_squared()) + .map(|m| m > 0.001 && m.is_finite()) + .reduce_and() + && ori.angle_between(last_ori).is_finite() + { + ori.angle_between(last_ori).min(0.2) + * last_ori.determine_side(Vec2::zero(), ori).signum() + } else { + 0.0 + } * 1.3; + + next.head.scale = Vec3::one() * 0.98; + next.neck.scale = Vec3::one() * 1.02; + next.leg_l.scale = Vec3::one() / 8.0 * 0.98; + next.leg_r.scale = Vec3::one() / 8.0 * 0.98; + next.foot_l.scale = Vec3::one() * 1.02; + next.foot_r.scale = Vec3::one() * 1.02; + next.chest.scale = Vec3::one() * s_a.scaler / 8.0; + + next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1); + next.head.orientation = Quaternion::rotation_x( + -0.1 * speednorm + short * -0.05 + movement1abs * -0.8 + movement2abs * 0.2, + ) * Quaternion::rotation_y(tilt * 0.2) + * Quaternion::rotation_z(shortalt * -0.05 - tilt * 1.5); + + next.beak.position = Vec3::new(0.0, s_a.beak.0, s_a.beak.1); + next.beak.orientation = + Quaternion::rotation_x(short * -0.02 - 0.02 + movement1abs * -0.4 + movement2abs * 0.4); + + next.neck.position = Vec3::new(0.0, s_a.neck.0, s_a.neck.1); + next.neck.orientation = Quaternion::rotation_x(-0.1 * speednorm + short * -0.04) + * Quaternion::rotation_y(tilt * 0.1) + * Quaternion::rotation_z(shortalt * -0.1 - tilt * 0.5); + + next.chest.position = Vec3::new( + 0.0, + s_a.chest.0, + s_a.chest.1 + short * 0.5 + 0.5 * speednorm, + ) * s_a.scaler + / 8.0; + next.chest.orientation = + Quaternion::rotation_x(short * 0.07 + movement1abs * 0.8 + movement2abs * -1.2) + * Quaternion::rotation_y(tilt * 0.8) + * Quaternion::rotation_z(shortalt * 0.10); + + next.tail_front.position = Vec3::new(0.0, s_a.tail_front.0, s_a.tail_front.1); + next.tail_front.orientation = + Quaternion::rotation_x(0.6 + short * -0.02 + movement1abs * -0.8 + movement2abs * 0.8); + + next.tail_rear.position = Vec3::new(0.0, s_a.tail_rear.0, s_a.tail_rear.1); + next.tail_rear.orientation = Quaternion::rotation_x(-0.2 + short * -0.1); + + next.wing_in_l.position = Vec3::new(-s_a.wing_in.0, s_a.wing_in.1, s_a.wing_in.2); + next.wing_in_r.position = Vec3::new(s_a.wing_in.0, s_a.wing_in.1, s_a.wing_in.2); + + next.wing_in_l.orientation = + Quaternion::rotation_y( + -0.8 + movement1abs * 1.0 + chargeanim * 0.2 - movement2abs * 0.6, + ) * Quaternion::rotation_z(0.2 - movement1abs * 0.6 - movement2abs * 0.6); + next.wing_in_r.orientation = + Quaternion::rotation_y( + 0.8 - movement1abs * 1.0 - chargeanim * 0.2 + movement2abs * 0.6, + ) * Quaternion::rotation_z(-0.2 + movement1abs * 0.6 + movement2abs * 0.6); + + next.wing_mid_l.position = Vec3::new(-s_a.wing_mid.0, s_a.wing_mid.1, s_a.wing_mid.2); + next.wing_mid_r.position = Vec3::new(s_a.wing_mid.0, s_a.wing_mid.1, s_a.wing_mid.2); + next.wing_mid_l.orientation = Quaternion::rotation_y(-0.1) * Quaternion::rotation_z(0.7); + next.wing_mid_r.orientation = Quaternion::rotation_y(0.1) * Quaternion::rotation_z(-0.7); + + next.wing_out_l.position = Vec3::new(-s_a.wing_out.0, s_a.wing_out.1, s_a.wing_out.2); + next.wing_out_r.position = Vec3::new(s_a.wing_out.0, s_a.wing_out.1, s_a.wing_out.2); + next.wing_out_l.orientation = + Quaternion::rotation_y(-0.2 + short * 0.05) * Quaternion::rotation_z(0.2); + next.wing_out_r.orientation = + Quaternion::rotation_y(0.2 + short * -0.05) * Quaternion::rotation_z(-0.2); + + if legtell > 0.0 { + if mirror.is_sign_positive() { + next.leg_l.orientation = Quaternion::rotation_x(legswing * 1.1); + + next.foot_l.orientation = Quaternion::rotation_x(legswing * -1.1 + legtwitch * 0.5); + + next.leg_r.orientation = Quaternion::rotation_x(0.0); + + next.foot_r.orientation = Quaternion::rotation_x(0.0); + } else { + next.leg_l.orientation = Quaternion::rotation_x(0.0); + + next.foot_l.orientation = Quaternion::rotation_x(0.0); + + next.leg_r.orientation = Quaternion::rotation_x(legswing * 1.1); + + next.foot_r.orientation = Quaternion::rotation_x(legswing * -1.1 + legtwitch * 0.5); + } + } + + next + } +} diff --git a/voxygen/anim/src/bird_large/mod.rs b/voxygen/anim/src/bird_large/mod.rs index 67e7da5166..58a3ae27a1 100644 --- a/voxygen/anim/src/bird_large/mod.rs +++ b/voxygen/anim/src/bird_large/mod.rs @@ -1,5 +1,6 @@ pub mod alpha; pub mod breathe; +pub mod dash; pub mod feed; pub mod fly; pub mod idle; @@ -7,13 +8,14 @@ pub mod run; pub mod shockwave; pub mod shoot; pub mod stunned; +pub mod summon; pub mod swim; // Reexports pub use self::{ - alpha::AlphaAnimation, breathe::BreatheAnimation, feed::FeedAnimation, fly::FlyAnimation, - idle::IdleAnimation, run::RunAnimation, shockwave::ShockwaveAnimation, shoot::ShootAnimation, - stunned::StunnedAnimation, swim::SwimAnimation, + alpha::AlphaAnimation, breathe::BreatheAnimation, dash::DashAnimation, feed::FeedAnimation, + fly::FlyAnimation, idle::IdleAnimation, run::RunAnimation, shockwave::ShockwaveAnimation, + shoot::ShootAnimation, stunned::StunnedAnimation, summon::SummonAnimation, swim::SwimAnimation, }; use super::{make_bone, vek::*, FigureBoneData, Skeleton}; diff --git a/voxygen/anim/src/bird_large/summon.rs b/voxygen/anim/src/bird_large/summon.rs new file mode 100644 index 0000000000..5176540e77 --- /dev/null +++ b/voxygen/anim/src/bird_large/summon.rs @@ -0,0 +1,114 @@ +use super::{ + super::{vek::*, Animation}, + BirdLargeSkeleton, SkeletonAttr, +}; +use common::{states::utils::StageSection, util::Dir}; + +pub struct SummonAnimation; + +type SummonAnimationDependency = (f32, Option, f32, Dir, bool); + +impl Animation for SummonAnimation { + type Dependency<'a> = SummonAnimationDependency; + type Skeleton = BirdLargeSkeleton; + + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"bird_large_summon\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "bird_large_summon")] + #[allow(clippy::approx_constant)] // TODO: Pending review in #587 + fn update_skeleton_inner<'a>( + skeleton: &Self::Skeleton, + (global_time, stage_section, timer, look_dir, on_ground): Self::Dependency<'a>, + anim_time: f32, + rate: &mut f32, + s_a: &SkeletonAttr, + ) -> Self::Skeleton { + *rate = 1.0; + let mut next = (*skeleton).clone(); + + let (movement1base, movement2base, movement3, twitch) = match stage_section { + Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0, 0.0), + Some(StageSection::Cast) => (1.0, anim_time.min(1.0).powf(0.1), 0.0, anim_time), + Some(StageSection::Recover) => (1.0, 1.0, anim_time.min(1.0).powi(2), 1.0), + _ => (0.0, 0.0, 0.0, 0.0), + }; + + let pullback = 1.0 - movement3; + let subtract = global_time - timer; + let check = subtract - subtract.trunc(); + let mirror = (check - 0.5).signum(); + let twitch2 = mirror * (twitch * 20.0).sin() * pullback; + + let movement1abs = movement1base * pullback; + let movement2abs = movement2base * pullback; + + let wave_slow_cos = (anim_time * 4.5).cos(); + + next.head.scale = Vec3::one() * 0.98; + next.neck.scale = Vec3::one() * 1.02; + next.leg_l.scale = Vec3::one() / 8.0 * 0.98; + next.leg_r.scale = Vec3::one() / 8.0 * 0.98; + next.foot_l.scale = Vec3::one() * 1.02; + next.foot_r.scale = Vec3::one() * 1.02; + next.chest.scale = Vec3::one() * s_a.scaler / 8.0; + + next.chest.position = Vec3::new( + 0.0, + s_a.chest.0, + s_a.chest.1 + wave_slow_cos * 0.06 + twitch2 * 0.1, + ) * s_a.scaler + / 8.0; + + next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1); + next.head.orientation = + Quaternion::rotation_x(movement1abs * -1.0 - movement2abs * 0.1 + look_dir.z * 0.4); + + next.beak.position = Vec3::new(0.0, s_a.beak.0, s_a.beak.1); + next.beak.orientation = Quaternion::rotation_x(movement1abs * -0.7 + twitch2 * 0.1); + + if on_ground { + next.neck.position = Vec3::new(0.0, s_a.neck.0, s_a.neck.1); + next.neck.orientation = Quaternion::rotation_x(movement1abs * 0.5 - movement2abs * 0.5); + + next.chest.orientation = + Quaternion::rotation_x(movement1abs * 1.1 - movement2abs * 0.1); + + next.wing_in_l.position = Vec3::new(-s_a.wing_in.0, s_a.wing_in.1, s_a.wing_in.2); + next.wing_in_r.position = Vec3::new(s_a.wing_in.0, s_a.wing_in.1, s_a.wing_in.2); + + next.wing_in_l.orientation = + Quaternion::rotation_x(movement1abs * 0.4 - movement2abs * 0.4) + * Quaternion::rotation_y(-1.0 + movement1abs * 1.6 - movement2abs * 1.8) + * Quaternion::rotation_z(0.2 - movement1abs * 1.8 + movement2abs * 0.4); + next.wing_in_r.orientation = + Quaternion::rotation_x(movement1abs * 0.4 - movement2abs * 0.4) + * Quaternion::rotation_y(1.0 - movement1abs * 1.6 + movement2abs * 1.8) + * Quaternion::rotation_z(-0.2 + movement1abs * 1.8 - movement2abs * 0.4); + + next.wing_mid_l.position = Vec3::new(-s_a.wing_mid.0, s_a.wing_mid.1, s_a.wing_mid.2); + next.wing_mid_r.position = Vec3::new(s_a.wing_mid.0, s_a.wing_mid.1, s_a.wing_mid.2); + next.wing_mid_l.orientation = + Quaternion::rotation_y(-0.1 - movement2abs * 0.4) * Quaternion::rotation_z(0.7); + next.wing_mid_r.orientation = + Quaternion::rotation_y(0.1 + movement2abs * 0.4) * Quaternion::rotation_z(-0.7); + + next.wing_out_l.position = Vec3::new(-s_a.wing_out.0, s_a.wing_out.1, s_a.wing_out.2); + next.wing_out_r.position = Vec3::new(s_a.wing_out.0, s_a.wing_out.1, s_a.wing_out.2); + next.wing_out_l.orientation = + Quaternion::rotation_y(-0.2 - movement2abs * 0.4) * Quaternion::rotation_z(0.2); + next.wing_out_r.orientation = + Quaternion::rotation_y(0.2 + movement2abs * 0.4) * Quaternion::rotation_z(-0.2); + + next.tail_front.position = Vec3::new(0.0, s_a.tail_front.0, s_a.tail_front.1); + next.tail_front.orientation = + Quaternion::rotation_x(-movement1abs * 0.1 + movement2abs * 0.1 + twitch2 * 0.02); + next.tail_rear.position = Vec3::new(0.0, s_a.tail_rear.0, s_a.tail_rear.1); + next.tail_rear.orientation = + Quaternion::rotation_x(-movement1abs * 0.1 + movement2abs * 0.1 + twitch2 * 0.02); + } else { + } + + next + } +} diff --git a/voxygen/src/render/pipelines/figure.rs b/voxygen/src/render/pipelines/figure.rs index 662f5db4c6..5502b4babd 100644 --- a/voxygen/src/render/pipelines/figure.rs +++ b/voxygen/src/render/pipelines/figure.rs @@ -80,7 +80,7 @@ impl Default for BoneData { } pub struct FigureModel { - pub opaque: Model, + pub opaque: Option>, /* TODO: Consider using mipmaps instead of storing multiple texture atlases for different * LOD levels. */ } diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 167bbf2571..c43d262185 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -88,11 +88,12 @@ pub struct FigureModelEntry { } impl FigureModelEntry { - pub fn lod_model(&self, lod: usize) -> SubModel { + pub fn lod_model(&self, lod: usize) -> Option> { // Note: Range doesn't impl Copy even for trivially Cloneable things self.model .opaque - .submodel(self.lod_vertex_ranges[lod].clone()) + .as_ref() + .map(|m| m.submodel(self.lod_vertex_ranges[lod].clone())) } } @@ -3480,6 +3481,70 @@ impl FigureMgr { skeleton_attr, ) }, + CharacterState::BasicSummon(s) => { + let stage_time = s.timer.as_secs_f32(); + let stage_progress = match s.stage_section { + StageSection::Buildup => { + stage_time / s.static_data.buildup_duration.as_secs_f32() + }, + + StageSection::Cast => { + stage_time / s.static_data.cast_duration.as_secs_f32() + }, + StageSection::Recover => { + stage_time / s.static_data.recover_duration.as_secs_f32() + }, + _ => 0.0, + }; + + anim::bird_large::SummonAnimation::update_skeleton( + &target_base, + ( + time, + Some(s.stage_section), + state.state_time, + look_dir, + physics.on_ground, + ), + stage_progress, + &mut state_animation_rate, + skeleton_attr, + ) + }, + CharacterState::DashMelee(s) => { + let stage_time = s.timer.as_secs_f32(); + let stage_progress = match s.stage_section { + StageSection::Buildup => { + stage_time / s.static_data.buildup_duration.as_secs_f32() + }, + StageSection::Charge => { + stage_time / s.static_data.charge_duration.as_secs_f32() + }, + StageSection::Swing => { + stage_time / s.static_data.swing_duration.as_secs_f32() + }, + StageSection::Recover => { + stage_time / s.static_data.recover_duration.as_secs_f32() + }, + _ => 0.0, + }; + anim::bird_large::DashAnimation::update_skeleton( + &target_base, + ( + rel_vel, + // TODO: Update to use the quaternion. + ori * anim::vek::Vec3::::unit_y(), + state.last_ori * anim::vek::Vec3::::unit_y(), + state.acc_vel, + Some(s.stage_section), + time, + state.state_time, + ), + stage_progress, + &mut state_animation_rate, + skeleton_attr, + ) + }, CharacterState::Stunned(s) => { let stage_time = s.timer.as_secs_f32(); let stage_progress = match s.stage_section { @@ -5163,7 +5228,7 @@ impl FigureMgr { model_entry.lod_model(0) }; - Some((bound, model, col_lights_.texture(model_entry))) + Some((bound, model?, col_lights_.texture(model_entry))) } else { // trace!("Body has no saved figure"); None @@ -5221,9 +5286,7 @@ impl FigureColLights { let col_lights = renderer.figure_bind_col_light(col_lights); let model_len = u32::try_from(opaque.vertices().len()) .expect("The model size for this figure does not fit in a u32!"); - let model = renderer - .create_model(&opaque) - .expect("The model contains no vertices!"); + let model = renderer.create_model(&opaque); vertex_ranges.iter().for_each(|range| { assert!( diff --git a/voxygen/src/scene/simple.rs b/voxygen/src/scene/simple.rs index 54fd63bea4..19cec5b20d 100644 --- a/voxygen/src/scene/simple.rs +++ b/voxygen/src/scene/simple.rs @@ -366,20 +366,20 @@ impl Scene { ); if let Some(model) = model { - figure_drawer.draw( - model.lod_model(0), - self.figure_state.bound(), - &self.col_lights.texture(model), - ); + if let Some(lod) = model.lod_model(0) { + figure_drawer.draw( + lod, + self.figure_state.bound(), + &self.col_lights.texture(model), + ); + } } } if let Some((model, state)) = &self.backdrop { - figure_drawer.draw( - model.lod_model(0), - state.bound(), - &self.col_lights.texture(model), - ); + if let Some(lod) = model.lod_model(0) { + figure_drawer.draw(lod, state.bound(), &self.col_lights.texture(model)); + } } drop(figure_drawer); diff --git a/world/src/layer/wildlife.rs b/world/src/layer/wildlife.rs index 5c240d1235..362fcf2fed 100644 --- a/world/src/layer/wildlife.rs +++ b/world/src/layer/wildlife.rs @@ -119,9 +119,15 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( Entry { make_entity: |pos, rng| { EntityInfo::at(pos) - .with_body( - biped_large::Body::random_with(rng, &biped_large::Species::Wendigo).into(), - ) + .with_body(match rng.gen_range(0..2) { + 0 => biped_large::Body::random_with(rng, &biped_large::Species::Wendigo) + .into(), + _ => biped_large::Body::random_with( + rng, + &biped_large::Species::Mountaintroll, + ) + .into(), + }) .with_alignment(Alignment::Enemy) }, group_size: 1..2, @@ -623,7 +629,7 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( Entry { make_entity: |pos, rng| { EntityInfo::at(pos) - .with_body(match rng.gen_range(0..4) { + .with_body(match rng.gen_range(0..5) { 0 => theropod::Body::random_with(rng, &theropod::Species::Odonto).into(), 1 => { biped_large::Body::random_with(rng, &biped_large::Species::Mightysaurok) @@ -633,6 +639,8 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( biped_large::Body::random_with(rng, &biped_large::Species::Occultsaurok) .into() }, + 3 => bird_large::Body::random_with(rng, &bird_large::Species::Cockatrice) + .into(), _ => biped_large::Body::random_with(rng, &biped_large::Species::Slysaurok) .into(), }) @@ -652,12 +660,11 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( Entry { make_entity: |pos, rng| { EntityInfo::at(pos) - .with_body(match rng.gen_range(0..4) { + .with_body(match rng.gen_range(0..3) { 0 => bird_medium::Body::random_with(rng, &bird_medium::Species::Parrot) .into(), - 1 => bird_large::Body::random_with(rng, &bird_large::Species::Cockatrice) - .into(), - 2 => quadruped_small::Body::random_with( + + 1 => quadruped_small::Body::random_with( rng, &quadruped_small::Species::Quokka, ) From f2c78361619f11dc95c1f2f3c2faffd529b060ec Mon Sep 17 00:00:00 2001 From: Snowram Date: Fri, 11 Jun 2021 03:15:58 +0200 Subject: [PATCH 066/180] Makes summon range inclusive --- .../abilities/custom/birdlargebasic/dash.ron | 10 ++++---- assets/voxygen/shaders/particle-vert.glsl | 5 ++-- common/src/comp/body.rs | 4 ++-- common/src/states/basic_summon.rs | 2 +- server/src/sys/agent.rs | 24 ++++++++++++++----- voxygen/src/scene/particle.rs | 10 ++++---- 6 files changed, 32 insertions(+), 23 deletions(-) diff --git a/assets/common/abilities/custom/birdlargebasic/dash.ron b/assets/common/abilities/custom/birdlargebasic/dash.ron index 2421a1dd02..8741cc774a 100644 --- a/assets/common/abilities/custom/birdlargebasic/dash.ron +++ b/assets/common/abilities/custom/birdlargebasic/dash.ron @@ -4,17 +4,17 @@ DashMelee( scaled_damage: 150, base_poise_damage: 50, scaled_poise_damage: 100, - base_knockback: 12.0, - scaled_knockback: 17.0, - range: 6.0, + base_knockback: 6.0, + scaled_knockback: 12.0, + range: 2.0, angle: 20.0, energy_drain: 0, forward_speed: 1.5, buildup_duration: 0.5, - charge_duration: 1.2, + charge_duration: 3.0, swing_duration: 0.1, recover_duration: 1.1, - charge_through: true, + charge_through: false, is_interruptible: false, damage_kind: Crushing, ) diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index bb3cc72945..3cd9721a47 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -545,12 +545,11 @@ void main() { ); break; case TORNADO: - f_reflect = 0.0; // Fire doesn't reflect light, it emits it + f_reflect = 0.0; attr = Attr( - //vec3(sin(lifetime * 400.0) * 3.0 * percent(), cos(lifetime * 400.0) * 3.0 * percent(), lifetime * 5.0), spiral_motion(vec3(0, 0, 5), abs(rand0) + abs(rand1) * percent() * 3.0, percent(), 15.0 * abs(rand2), rand3), vec3((2.5 * (1 - slow_start(0.05)))), - vec4(1.2 + 0.5 * percent(), 1.2 + 0.5 * percent(), 1.2 + 0.5 * percent(), 2.5), + vec4(vec3(1.2 + 0.5 * percent()), 1), spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9) ); break; diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 7b1765ebed..470763465a 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -473,7 +473,7 @@ impl Body { Body::FishMedium(_) => 250, Body::Dragon(_) => 5000, Body::BirdLarge(bird_large) => match bird_large.species { - bird_large::Species::Roc => 2400, + bird_large::Species::Roc => 2800, _ => 3000, }, Body::FishSmall(_) => 20, @@ -595,7 +595,7 @@ impl Body { Body::FishMedium(_) => 10, Body::Dragon(_) => 500, Body::BirdLarge(bird_large) => match bird_large.species { - bird_large::Species::Roc => 100, + bird_large::Species::Roc => 110, _ => 120, }, Body::FishSmall(_) => 10, diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 44c71b9f47..9ff74f016b 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -122,7 +122,7 @@ impl CharacterBehavior for Data { self.summon_count as f32 / self.static_data.summon_amount as f32; let length = rand::thread_rng().gen_range( - self.static_data.summon_distance.0..self.static_data.summon_distance.1, + self.static_data.summon_distance.0..=self.static_data.summon_distance.1, ); // Summon in a clockwise fashion diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 33d181693c..448af696d9 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -3286,6 +3286,7 @@ impl<'a> AgentData<'a> { tgt_data: &TargetData, read_data: &ReadData, ) { + // If higher than 2 blocks if !read_data .terrain .ray(self.pos.0, self.pos.0 - (Vec3::unit_z() * 2.0)) @@ -3294,7 +3295,7 @@ impl<'a> AgentData<'a> { .1 .map_or(true, |b| b.is_some()) { - // Fly to target + // Fly to target and land controller .actions .push(ControlAction::basic_input(InputKind::Fly)); @@ -3302,26 +3303,35 @@ impl<'a> AgentData<'a> { controller.inputs.move_dir = move_dir.xy().try_normalized().unwrap_or_else(Vec2::zero) * 2.0; controller.inputs.move_z = move_dir.z - 0.5; - } else if agent.action_state.timer > 7.0 { + // If near a target and timer higher than 7 + } else if agent.action_state.timer > 6.0 + && attack_data.dist_sqrd < (3.0 * attack_data.min_attack_dist).powi(2) + { + // Cast tornadoes controller .actions .push(ControlAction::basic_input(InputKind::Ability(0))); // Reset timer agent.action_state.timer = 0.0; + // If near and in front of target and timer lower than 6 } else if attack_data.angle < 90.0 && attack_data.dist_sqrd < (1.5 * attack_data.min_attack_dist).powi(2) - && agent.action_state.timer < 6.0 + && agent.action_state.timer < 5.0 { + // Basic strike controller.inputs.move_dir = Vec2::zero(); controller .actions .push(ControlAction::basic_input(InputKind::Secondary)); + // Increase timer agent.action_state.timer += read_data.dt.0; + // If far from the target and timer lower than 6 } else if attack_data.dist_sqrd < (3.0 * attack_data.min_attack_dist).powi(2) - && attack_data.dist_sqrd > (2.0 * attack_data.min_attack_dist).powi(2) - && attack_data.angle < 90.0 - && agent.action_state.timer < 6.0 + && attack_data.dist_sqrd > (1.5 * attack_data.min_attack_dist).powi(2) + && attack_data.angle < 60.0 + && agent.action_state.timer < 5.0 { + // Dash controller .actions .push(ControlAction::basic_input(InputKind::Primary)); @@ -3331,7 +3341,9 @@ impl<'a> AgentData<'a> { .try_normalized() .unwrap_or_else(Vec2::unit_y); agent.action_state.timer += read_data.dt.0; + // If very far from the player } else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) { + // Walk to the player self.path_toward_target(agent, controller, tgt_data, read_data, true, None); agent.action_state.timer += read_data.dt.0; } else { diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index d21640e0d6..76bd714891 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -324,7 +324,7 @@ impl ParticleMgr { self.maintain_boltnature_particles(scene_data, pos, vel) }, Body::Object(object::Body::Tornado) => { - self.maintain_tornado_particles(scene_data, pos, vel) + self.maintain_tornado_particles(scene_data, pos) }, Body::Object( object::Body::Bomb @@ -501,12 +501,11 @@ impl ParticleMgr { ); } - fn maintain_tornado_particles(&mut self, scene_data: &SceneData, pos: &Pos, vel: Option<&Vel>) { + fn maintain_tornado_particles(&mut self, scene_data: &SceneData, pos: &Pos) { let time = scene_data.state.get_time(); - let dt = scene_data.state.get_delta_time(); let mut rng = thread_rng(); - // nature + // air particles self.particles.resize_with( self.particles.len() + usize::from(self.scheduler.heartbeats(Duration::from_millis(5))), || { @@ -514,8 +513,7 @@ impl ParticleMgr { Duration::from_millis(1000), time, ParticleMode::Tornado, - pos.0.map(|e| e + rng.gen_range(-0.25..0.25)) - + vel.map_or(Vec3::zero(), |v| -v.0 * dt * rng.gen::()), + pos.0.map(|e| e + rng.gen_range(-0.25..0.25)), ) }, ); From 8351aab25d67d8a006f3aa7668d3e4580d348115 Mon Sep 17 00:00:00 2001 From: Snowram Date: Tue, 15 Jun 2021 01:34:17 +0200 Subject: [PATCH 067/180] Addresses comments about roc AI --- .../common/abilities/ability_set_manifest.ron | 6 +- .../abilities/custom/birdlargebasic/dash.ron | 6 +- .../custom/birdlargebasic/summontornadoes.ron | 4 +- .../custom/birdlargebasic/triplestrike.ron | 21 +++--- .../common/abilities/custom/tornado/spin.ron | 6 +- .../items/npc_weapons/unique/tornado.ron | 2 +- .../loot_tables/creature/bird_large/roc.ron | 4 + common/src/states/basic_summon.rs | 2 +- server/src/events/entity_manipulation.rs | 1 + server/src/sys/agent.rs | 74 +++++++++---------- 10 files changed, 65 insertions(+), 61 deletions(-) create mode 100644 assets/common/loot_tables/creature/bird_large/roc.ron diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index e84291d621..ef9979ce3f 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -265,10 +265,10 @@ ], ), Custom("Bird Large Basic"): ( - primary: "common.abilities.custom.birdlargebasic.dash", - secondary: "common.abilities.custom.birdlargebasic.triplestrike", + primary: "common.abilities.custom.birdlargebasic.triplestrike", + secondary: "common.abilities.custom.birdlargebasic.summontornadoes", abilities: [ - (None, "common.abilities.custom.birdlargebasic.summontornadoes"), + (None, "common.abilities.custom.birdlargebasic.dash"), ], ), Custom("Tornado"): ( diff --git a/assets/common/abilities/custom/birdlargebasic/dash.ron b/assets/common/abilities/custom/birdlargebasic/dash.ron index 8741cc774a..0760657973 100644 --- a/assets/common/abilities/custom/birdlargebasic/dash.ron +++ b/assets/common/abilities/custom/birdlargebasic/dash.ron @@ -1,6 +1,6 @@ DashMelee( energy_cost: 0, - base_damage: 70, + base_damage: 80, scaled_damage: 150, base_poise_damage: 50, scaled_poise_damage: 100, @@ -9,11 +9,11 @@ DashMelee( range: 2.0, angle: 20.0, energy_drain: 0, - forward_speed: 1.5, + forward_speed: 1.9, buildup_duration: 0.5, charge_duration: 3.0, swing_duration: 0.1, - recover_duration: 1.1, + recover_duration: 0.7, charge_through: false, is_interruptible: false, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron b/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron index 9e487d8922..66240b64a6 100644 --- a/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron +++ b/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron @@ -2,8 +2,8 @@ BasicSummon( buildup_duration: 0.5, cast_duration: 0.2, recover_duration: 0.2, - summon_amount: 6, - summon_distance: (1, 3), + summon_amount: 12, + summon_distance: (4, 9), summon_info: ( body: Object(Tornado), scale: None, diff --git a/assets/common/abilities/custom/birdlargebasic/triplestrike.ron b/assets/common/abilities/custom/birdlargebasic/triplestrike.ron index eb332435a4..ae24969a40 100644 --- a/assets/common/abilities/custom/birdlargebasic/triplestrike.ron +++ b/assets/common/abilities/custom/birdlargebasic/triplestrike.ron @@ -2,7 +2,7 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 100, + base_damage: 110, damage_increase: 0, base_poise_damage: 0, poise_damage_increase: 0, @@ -11,13 +11,14 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, - forward_movement: 2.0, - damage_kind: Slashing, + forward_movement: 3.0, + damage_kind: Crushing, ), ( stage: 2, - base_damage: 80, + base_damage: 90, damage_increase: 0, base_poise_damage: 0, poise_damage_increase: 0, @@ -26,13 +27,14 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, - forward_movement: 1.5, - damage_kind: Slashing, + forward_movement: 3.0, + damage_kind: Crushing, ), ( stage: 3, - base_damage: 130, + base_damage: 140, damage_increase: 0, base_poise_damage: 0, poise_damage_increase: 0, @@ -41,9 +43,10 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.65, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, - forward_movement: 1.5, - damage_kind: Slashing, + forward_movement: 3.5, + damage_kind: Crushing, ), ], initial_energy_gain: 0, diff --git a/assets/common/abilities/custom/tornado/spin.ron b/assets/common/abilities/custom/tornado/spin.ron index 2418c50b38..f5108fad61 100644 --- a/assets/common/abilities/custom/tornado/spin.ron +++ b/assets/common/abilities/custom/tornado/spin.ron @@ -2,14 +2,14 @@ SpinMelee( buildup_duration: 0.0, swing_duration: 0.5, recover_duration: 0.0, - base_damage: 50, - base_poise_damage: 100, + base_damage: 400, + base_poise_damage: 0, knockback: ( strength: 50.0, direction: Away), range: 3.5, damage_effect: None, energy_cost: 0, is_infinite: true, - movement_behavior: AxeHover, + movement_behavior: ForwardGround, is_interruptible: false, forward_speed: 0.0, num_spins: 1, diff --git a/assets/common/items/npc_weapons/unique/tornado.ron b/assets/common/items/npc_weapons/unique/tornado.ron index 14c1054395..ee38a3be6b 100644 --- a/assets/common/items/npc_weapons/unique/tornado.ron +++ b/assets/common/items/npc_weapons/unique/tornado.ron @@ -7,7 +7,7 @@ ItemDef( stats: Direct(( equip_time_secs: 0.01, power: 1.0, - poise_strength: 1.0, + poise_strength: 0.0, speed: 1.0, crit_chance: 0.0, crit_mult: 0.0, diff --git a/assets/common/loot_tables/creature/bird_large/roc.ron b/assets/common/loot_tables/creature/bird_large/roc.ron new file mode 100644 index 0000000000..a5ec41abcf --- /dev/null +++ b/assets/common/loot_tables/creature/bird_large/roc.ron @@ -0,0 +1,4 @@ +[ + (0.5, Item("common.items.food.meat.beast_large_raw")), + (1.0, Item("common.items.crafting_ing.animal_misc.raptor_feather")), +] \ No newline at end of file diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 9ff74f016b..80eab6e301 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -143,7 +143,7 @@ impl CharacterBehavior for Data { let collision_vector = Vec3::new( data.pos.0.x + (summon_frac * 2.0 * PI).sin() * obstacle_xy, data.pos.0.y + (summon_frac * 2.0 * PI).cos() * obstacle_xy, - data.pos.0.z, + data.pos.0.z + data.body.eye_height(), ); // Check for collision in z up to 50 blocks diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 3ce4a8178f..0eb466a80c 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -423,6 +423,7 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc bird_large::Species::Cockatrice => { "common.loot_tables.creature.bird_large.cockatrice" }, + bird_large::Species::Roc => "common.loot_tables.creature.bird_large.roc", _ => "common.loot_tables.creature.bird_large.phoenix", }, Some(common::comp::Body::FishMedium(_)) => "common.loot_tables.creature.fish", diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 448af696d9..addd351b2f 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -3286,6 +3286,11 @@ impl<'a> AgentData<'a> { tgt_data: &TargetData, read_data: &ReadData, ) { + const BIRD_ATTACK_RANGE: f32 = 4.0; + const BIRD_CHARGE_DISTANCE: f32 = 15.0; + let bird_attack_distance = self.body.map_or(0.0, |b| b.radius()) + BIRD_ATTACK_RANGE; + // Increase action timer + agent.action_state.timer += read_data.dt.0; // If higher than 2 blocks if !read_data .terrain @@ -3303,53 +3308,44 @@ impl<'a> AgentData<'a> { controller.inputs.move_dir = move_dir.xy().try_normalized().unwrap_or_else(Vec2::zero) * 2.0; controller.inputs.move_z = move_dir.z - 0.5; - // If near a target and timer higher than 7 - } else if agent.action_state.timer > 6.0 - && attack_data.dist_sqrd < (3.0 * attack_data.min_attack_dist).powi(2) - { - // Cast tornadoes - controller - .actions - .push(ControlAction::basic_input(InputKind::Ability(0))); - // Reset timer - agent.action_state.timer = 0.0; - // If near and in front of target and timer lower than 6 - } else if attack_data.angle < 90.0 - && attack_data.dist_sqrd < (1.5 * attack_data.min_attack_dist).powi(2) - && agent.action_state.timer < 5.0 - { - // Basic strike - controller.inputs.move_dir = Vec2::zero(); + } else if agent.action_state.timer > 8.0 { + // If action timer higher than 8, make bird summon tornadoes controller .actions .push(ControlAction::basic_input(InputKind::Secondary)); - // Increase timer - agent.action_state.timer += read_data.dt.0; - // If far from the target and timer lower than 6 - } else if attack_data.dist_sqrd < (3.0 * attack_data.min_attack_dist).powi(2) - && attack_data.dist_sqrd > (1.5 * attack_data.min_attack_dist).powi(2) - && attack_data.angle < 60.0 - && agent.action_state.timer < 5.0 + if matches!(self.char_state, CharacterState::BasicSummon(c) if matches!(c.stage_section, StageSection::Recover)) + { + // Reset timer + agent.action_state.timer = 0.0; + } + } else if matches!(self.char_state, CharacterState::DashMelee(c) if !matches!(c.stage_section, StageSection::Recover)) { - // Dash + // If already in dash, keep dashing if not in recover + controller + .actions + .push(ControlAction::basic_input(InputKind::Ability(0))); + } else if matches!(self.char_state, CharacterState::ComboMelee(c) if matches!(c.stage_section, StageSection::Recover)) + { + // If already in combo keep comboing if not in recover controller .actions .push(ControlAction::basic_input(InputKind::Primary)); - controller.inputs.move_dir = (tgt_data.pos.0 - self.pos.0) - .xy() - .rotated_z(-0.47 * PI) - .try_normalized() - .unwrap_or_else(Vec2::unit_y); - agent.action_state.timer += read_data.dt.0; - // If very far from the player - } else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) { - // Walk to the player - self.path_toward_target(agent, controller, tgt_data, read_data, true, None); - agent.action_state.timer += read_data.dt.0; - } else { - self.path_toward_target(agent, controller, tgt_data, read_data, false, None); - agent.action_state.timer += read_data.dt.0; + } else if attack_data.dist_sqrd > BIRD_CHARGE_DISTANCE.powi(2) { + // Charges at target if they are far enough away + if attack_data.angle < 60.0 { + controller + .actions + .push(ControlAction::basic_input(InputKind::Ability(0))); + } + } else if attack_data.dist_sqrd < bird_attack_distance.powi(2) { + // Combo melee target + controller + .actions + .push(ControlAction::basic_input(InputKind::Primary)); + agent.action_state.condition = true; } + // Make bird move towards target + self.path_toward_target(agent, controller, tgt_data, read_data, true, None); } fn handle_minotaur_attack( From 3f176ca068fc080a7c6b2039e8d79fc545cfb7e0 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Tue, 15 Jun 2021 19:02:30 +0300 Subject: [PATCH 068/180] Panic if can't parse file --- voxygen/i18n/src/analysis.rs | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/voxygen/i18n/src/analysis.rs b/voxygen/i18n/src/analysis.rs index 13344b9c30..50887955fd 100644 --- a/voxygen/i18n/src/analysis.rs +++ b/voxygen/i18n/src/analysis.rs @@ -231,17 +231,14 @@ fn complete_key_versions<'a>( let full_path = i18n_file.path(); let path = full_path.strip_prefix(root_dir).unwrap(); let i18n_blob = read_file_from_path(&repo, &head_ref, &path); - let i18n: LocalizationFragment = match from_bytes(i18n_blob.content()) { - Ok(v) => v, - Err(e) => { - eprintln!( + let i18n: LocalizationFragment = + from_bytes(i18n_blob.content()).unwrap_or_else(|e| { + panic!( "Could not parse {} RON file, skipping: {}", i18n_file.path().to_string_lossy(), e - ); - continue; - }, - }; + ) + }); i18n_key_versions.extend(generate_key_version(&repo, &i18n, &path, &i18n_blob)); } } @@ -304,17 +301,13 @@ fn test_localization_directory( // Find the localization entry state let current_blob = read_file_from_path(&repo, &head_ref, &relfile); - let current_loc: RawLocalization = match from_bytes(current_blob.content()) { - Ok(v) => v, - Err(e) => { - eprintln!( - "Could not parse {} RON file, skipping: {}", - relfile.to_string_lossy(), - e - ); - return None; - }, - }; + let current_loc: RawLocalization = from_bytes(current_blob.content()).unwrap_or_else(|e| { + panic!( + "Could not parse {} RON file, skipping: {}", + relfile.to_string_lossy(), + e + ) + }); // Gather state of current localization let mut current_i18n = gather_state( From fbb34b73312a2041ca1420a44d96d8b8f58875c2 Mon Sep 17 00:00:00 2001 From: "K. Kisa" Date: Tue, 15 Jun 2021 16:36:41 +0000 Subject: [PATCH 069/180] Veloren/veloren/master --- assets/voxygen/i18n/ru_RU/_manifest.ron | 780 +++++-------------- assets/voxygen/i18n/ru_RU/char_selection.ron | 32 + assets/voxygen/i18n/ru_RU/common.ron | 117 +++ assets/voxygen/i18n/ru_RU/gameinput.ron | 73 ++ assets/voxygen/i18n/ru_RU/main.ron | 8 +- assets/voxygen/i18n/ru_RU/skills.ron | 10 +- 6 files changed, 407 insertions(+), 613 deletions(-) create mode 100644 assets/voxygen/i18n/ru_RU/char_selection.ron create mode 100644 assets/voxygen/i18n/ru_RU/common.ron create mode 100644 assets/voxygen/i18n/ru_RU/gameinput.ron diff --git a/assets/voxygen/i18n/ru_RU/_manifest.ron b/assets/voxygen/i18n/ru_RU/_manifest.ron index b3844898b4..25d1d25c93 100644 --- a/assets/voxygen/i18n/ru_RU/_manifest.ron +++ b/assets/voxygen/i18n/ru_RU/_manifest.ron @@ -1,18 +1,6 @@ -/// Translation document instructions -/// -/// In order to keep localization documents readible please follow the following -/// rules: -/// - separate the string map sections using a commentary describing the purpose -/// of the next section -/// - prepend multi-line strings with a commentary -/// - append one blank lines after a multi-line strings and two after sections -/// -/// To add a new language in Veloren, just write an additional `.ron` file in -/// `assets/voxygen/i18n` and that's it! -/// /// WARNING: Localization files shall be saved in UTF-8 format without BOM -/// Localization for "global" Russian +/// Localization for RUS ( metadata: ( language_name: "Русский", @@ -41,516 +29,9 @@ scale_ratio: 1.0, ), }, - sub_directories: [], + sub_directories: [], + string_map: { - /// Start Common section - // Texts used in multiple locations with the same formatting - "common.username": "Имя пользователя", - "common.singleplayer": "Одиночная игра", - "common.multiplayer": "Мультиплеер", - "common.servers": "Сервера", - "common.quit": "Выход", - "common.settings": "Настройки", - "common.languages": "Язык", - "common.interface": "Интерфейс", - "common.gameplay": "Геймплей", - "common.controls": "Управление", - "common.video": "Видео", - "common.sound": "Звук", - "common.languages": "Языки", - "common.resume": "Продолжить", - "common.characters": "Персонажи", - "common.close": "Закрыть", - "common.yes": "Да", - "common.no": "Нет", - "common.back": "Назад", - "common.create": "Создать", - "common.okay": "Окей", - "common.accept": "Принять", - "common.decline": "Отклонить", - "common.disclaimer": "Дисклеймер", - "common.cancel": "Отмена", - "common.none": "Нет", - "common.error": "Ошибка", - "common.fatal_error": "Критическая ошибка", - "common.you": "Вы", - "common.automatic": "Авто", - "common.random": "Случайно", - // Settings Window title - "common.interface_settings": "Настройки интерфейса", - "common.gameplay_settings": "Настройки геймплея", - "common.controls_settings": "Настройки управления", - "common.video_settings": "Графические настройки", - "common.sound_settings": "Звуковые настройки", - "common.language_settings": "Языковые настройки", - - // Message when connection to the server is lost - "common.connection_lost": r#"Соединение потеряно! -Сервер перезагрузился? -Клиент обновлен до последней версии?"#, - - - "common.species.orc": "Орк", - "common.species.human": "Человек", - "common.species.dwarf": "Дварф", - "common.species.elf": "Эльф", - "common.species.undead": "Нежить", - "common.species.danari": "Данари", - - "common.weapons.axe": "Топор", - "common.weapons.sword": "Меч", - "common.weapons.staff": "Посох", - "common.weapons.bow": "Лук", - "common.weapons.hammer": "Молот", - "common.weapons.sceptre": "Лечащий посох", - "common.rand_appearance": "Случайная внешность и имя", - /// End Common section - - - /// Start Main screen section - "main.username": "Имя пользователя", - "main.server": "Сервер", - "main.password": "Пароль", - "main.connecting": "Подключение", - "main.creating_world": "Создание мира", - "main.tip": "Совет:", - - // Welcome notice that appears the first time Veloren is started - "main.notice": r#"Добро пожаловать в Veloren-Alpha! - -Прежде чем начать веселье, прими во внимание следующие вещи: - -- Это очень ранняя альфа. Тут есть баги, крайне незавершенный геймплей, неотполированные механики и отсутсвующие фичи. - -- Если у вас есть конструктивный фидбек или сообщение об ошибке, вы можете связаться с нами через Reddit, GitLab или наш Discord-сервер. - -- Veloren лицензирован GPL 3 open-source licence. Это означает, игра бесплатна, ее можно модифицировать и переделывать на свой вкус (при условии, что готовая работа тоже лицензирована GPL 3). - -- Veloren - некоммерческий проект, каждый работает над ним добровольно. -Если тебе нравится, что ты видишь, милости просим присоединиться к команде разработчиков или художественной команде! - -Спасибо за прочтение, мы надеемся, вам понравится игра! - -~ Команда разработчиков Veloren"#, - - // Login process description - "main.login_process": r#"Информация по входу: - -Обратите внимание, что теперь вам нужен аккаунт -играть на серверах с включенной аутентификацией. - -Вы можете создать аккаунт тут: - -https://veloren.net/account/."#, - "main.login.server_not_found": "Сервер не найден", - "main.login.authentication_error": "Ошибка аутентификации на сервер", - "main.login.server_full": "Сервер полон", - "main.login.untrusted_auth_server": "Аутентификация не пройдена", - "main.login.outdated_client_or_server": "ServerWentMad: Возможно, версии несовместимы. Проверьте наличие обновлений.", - "main.login.timeout": "Timeout: Сервер не ответил вовремя. (Перегрузка или проблемы с сетью).", - "main.login.server_shut_down": "Сервер выключен", - "main.login.network_error": "Ошибка сети", - "main.login.failed_sending_request": "Запрос аутентификации провален", - "main.login.invalid_character": "Выбранный персонаж недоступен", - "main.login.client_crashed": "Клиент вылетел", - "main.login.not_on_whitelist": "Чтобы войти, необходимо быть внесенным в Вайтлист", - "main.login.banned": "Вы были забанены по следующей причине", - "main.login.kicked": "Вы были кикнуты по следующей причине", - "main.login.select_language": "Выбрать язык", - - "main.servers.select_server": "Выбрать сервер", - - /// End Main screen section - - - /// Start HUD Section - "hud.do_not_show_on_startup": "Не показывать это при запуске", - "hud.show_tips": "Показать советы", - "hud.quests": "Квесты", - "hud.you_died": "Вы мертвы", - "hud.waypoint_saved": "Точка спауна сохранена", - - "hud.press_key_to_show_keybindings_fmt": "[{key}] Раскладка", - "hud.press_key_to_toggle_lantern_fmt": "[{key}] Фонарь", - "hud.press_key_to_show_debug_info_fmt": "Нажмите {key}, чтобы показать панель отладки", - "hud.press_key_to_toggle_keybindings_fmt": "Нажмите {key}, чтобы показать раскладку", - "hud.press_key_to_toggle_debug_info_fmt": "Нажмите {key}, чтобы включить панель отладки", - - // Chat outputs - "hud.chat.online_msg": "[{name}] сейчас онлайн.", - "hud.chat.offline_msg": "[{name}] сейчас оффлайн.", - - "hud.chat.default_death_msg": "[{name}] умер", - "hud.chat.environmental_kill_msg": "[{name}] умер от {environment}", - "hud.chat.fall_kill_msg": "[{name}] разбился", - "hud.chat.suicide_msg": "[{name}] сам стал виной своей смерти", - - "hud.chat.pvp_melee_kill_msg": "[{attacker}] одолел [{victim}]", - "hud.chat.pvp_ranged_kill_msg": "[{attacker}] подстрелил [{victim}]", - "hud.chat.pvp_explosion_kill_msg": "[{attacker}] взорвал [{victim}]", - "hud.chat.pvp_energy_kill_msg": "[{attacker}] убил [{victim}] с помощью магии", - "hud.chat.pvp_buff_kill_msg": "[{attacker}] убил [{victim}]", - - - "hud.chat.npc_melee_kill_msg": "{attacker} убил [{victim}]", - "hud.chat.npc_ranged_kill_msg": "{attacker} подстрелил [{victim}]", - "hud.chat.npc_explosion_kill_msg": "{attacker} взорвал [{victim}]", - "hud.chat.npc_energy_kill_msg": "{attacker} убил [{victim}] с помощью магии", - "hud.chat.npc_other_kill_msg": "{attacker} убил [{victim}]", - - "hud.chat.loot_msg": "Вы подобрали [{item}]", - "hud.chat.loot_fail": "Ваш инвентарь полон!", - "hud.chat.goodbye": "До встречи!", - "hud.chat.connection_lost": "Соединение потеряно. Кик через {time} секунд.", - - // SCT outputs - "hud.sct.experience": "{amount} Опыт", - "hud.sct.block": "ЗАБЛОКИРОВАНО", - - // Respawn message - "hud.press_key_to_respawn": r#"Нажмите {key}, чтобы возродиться на последнем костре, который вы посетили."#, - - // Welcome message - "hud.welcome": r#"Добро пожаловать в Veloren-Alpha!, - - -Немного советов перед тем началом игры: - - -Нажмите F1, чтобы увидеть доступые команды. - -Напишите /help, чтобы увидеть команды чата. - - -В мире есть сундуки и другие рандомно генерируемые объекты! - -Нажмите ПКМ, чтобы собрать их. - -Чтобы использовать то, что вы нашли в сундуках, откройте свой инвентарь 'B'. - -Двойной клик на предмет в инвентаре использует или экипирует его. - -Чтобы выкинуть его, кликните на предмет, а потом кликните вне инвентаря. - - -Ночи в Veloren могут быть довольно темными. - -Зажгите свой фонарь, нажав на 'G'. - - -Хотите увидеть курсор, чтобы закрыть это окно? Нажмите TAB! - - -Наслаждайтесь миром Veloren."#, - -"hud.temp_quest_headline": r#"Пожалуйста, путешественник, помоги нам!"#, -"hud.temp_quest_text": r#"Подземелья наполнены злыми культистами, -которые появились вокруг наших мирных городов! - - -Собери компанию, запасись едой -и победи мерзкого лидера и его приспешников. - - -Может быть ты даже сможешь получить один из их -магических предметов?"#, - - - - // Inventory - "hud.bag.inventory": "Инвентарь", - "hud.bag.stats_title": "Характеристики", - "hud.bag.exp": "Опыт", - "hud.bag.armor": "Броня", - "hud.bag.stats": "Статы", - "hud.bag.head": "Голова", - "hud.bag.neck": "Шея", - "hud.bag.tabard": "Накидка", - "hud.bag.shoulders": "Плечи", - "hud.bag.chest": "Нагрудник", - "hud.bag.hands": "Руки", - "hud.bag.lantern": "Фонарь", - "hud.bag.glider": "Глайдер", - "hud.bag.belt": "Пояс", - "hud.bag.ring": "Кольцо", - "hud.bag.back": "Спина", - "hud.bag.legs": "Ноги", - "hud.bag.feet": "Ботинки", - "hud.bag.mainhand": "Главная рука", - "hud.bag.offhand": "Второстепенная рука", - - - // Map and Questlog - "hud.map.map_title": "Карта", - "hud.map.qlog_title": "Квесты", - - // Settings - "hud.settings.general": "Общие", - "hud.settings.none": "Нет", - "hud.settings.press_behavior.toggle": "Переключить", - "hud.settings.press_behavior.hold": "Держать", - "hud.settings.help_window": "Окно помощи", - "hud.settings.debug_info": "Панель отладки", - "hud.settings.tips_on_startup": "Советы на старте", - "hud.settings.ui_scale": "Размер интерфейса", - "hud.settings.relative_scaling": "Относительное масштабирование", - "hud.settings.custom_scaling": "Пользовательское масштабирование", - "hud.settings.crosshair": "Перекрестие", - "hud.settings.transparency": "Прозрачность", - "hud.settings.hotbar": "Хотбар", - "hud.settings.toggle_shortcuts": "Отображать ярлыки", - "hud.settings.buffs_skillbar": "Баффы на панели навыков", - "hud.settings.buffs_mmap": "Баффы на миникарте", - "hud.settings.toggle_bar_experience": "Отображать полоску опыта", - "hud.settings.scrolling_combat_text": "Боевой журнал", - "hud.settings.single_damage_number": "Отдельные числа урона", - "hud.settings.cumulated_damage": "Суммарный урон", - "hud.settings.incoming_damage": "Входящий урон", - "hud.settings.cumulated_incoming_damage": "Суммарный входящий урон", - "hud.settings.speech_bubble": "Текстовые облачка", - "hud.settings.speech_bubble_dark_mode": "Темный режим текстовых облачков", - "hud.settings.speech_bubble_icon": "Значок текстовых облачков", - "hud.settings.energybar_numbers": "Отображение полоски энергии", - "hud.settings.values": "Значение", - "hud.settings.percentages": "Проценты", - "hud.settings.chat": "Чат", - "hud.settings.background_transparency": "Прозрачность заднего фона", - "hud.settings.chat_character_name": "Имя персонажа в чате", - "hud.settings.loading_tips": "Советы при загрузке", - - "hud.settings.pan_sensitivity": "Чувствительность камеры", - "hud.settings.zoom_sensitivity": "Чувствительность зума", - "hud.settings.invert_scroll_zoom": "Инвертировать прокрутку зума", - "hud.settings.invert_mouse_y_axis": "Инвертировать ось Y", - "hud.settings.enable_mouse_smoothing": "Размытие камеры", - "hud.settings.free_look_behavior": "Настройка свободной камеры", - "hud.settings.auto_walk_behavior": "Автодвижение", - "hud.settings.stop_auto_walk_on_input": "Остановить автодвижение на кнопку движения", - - "hud.settings.view_distance": "Дальность прорисовки", - "hud.settings.sprites_view_distance": "Дальность прорисовки спрайтов", - "hud.settings.figures_view_distance": "Дальность прорисовки объектов", - "hud.settings.maximum_fps": "Максимум FPS", - "hud.settings.fov": "Поле зрения (градусы)", - "hud.settings.gamma": "Гамма", - "hud.settings.exposure": "Экспозиция", - "hud.settings.ambiance": "Яркость окружения", - "hud.settings.antialiasing_mode": "Сглаживание", - "hud.settings.cloud_rendering_mode": "Рендер облаков", - "hud.settings.fluid_rendering_mode": "Рендер жидкостей", - "hud.settings.fluid_rendering_mode.cheap": "Низко", - "hud.settings.fluid_rendering_mode.shiny": "Высоко", - "hud.settings.cloud_rendering_mode.minimal": "Минимально", - "hud.settings.cloud_rendering_mode.low": "Низко", - "hud.settings.cloud_rendering_mode.medium": "Средне", - "hud.settings.cloud_rendering_mode.high": "Высоко", - "hud.settings.cloud_rendering_mode.ultra": "Ультра", - "hud.settings.fullscreen": "Полный экран", - "hud.settings.fullscreen_mode": "Режим полного экрана", - "hud.settings.fullscreen_mode.exclusive": "Особый", - "hud.settings.fullscreen_mode.borderless": "Без рамок", - "hud.settings.particles": "Частицы", - "hud.settings.resolution": "Разрешение", - "hud.settings.bit_depth": "Разрядность", - "hud.settings.refresh_rate": "Частота обновления", - "hud.settings.save_window_size": "Сохранить размер окна", - "hud.settings.lighting_rendering_mode": "Режим рендера освещения", - "hud.settings.lighting_rendering_mode.ashikhmin": "Тип A - Высоко ", - "hud.settings.lighting_rendering_mode.blinnphong": "Тип B - Средне", - "hud.settings.lighting_rendering_mode.lambertian": "Тип L - Низко", - "hud.settings.shadow_rendering_mode": "Режим рендера теней", - "hud.settings.shadow_rendering_mode.none": "Нет", - "hud.settings.shadow_rendering_mode.cheap": "Низко", - "hud.settings.shadow_rendering_mode.map": "Карта", - "hud.settings.shadow_rendering_mode.map.resolution": "Разрешение", - "hud.settings.lod_detail": "LoD-детали", - "hud.settings.save_window_size": "Сохранить размер окна", - - - "hud.settings.music_volume": "Громкость музыки", - "hud.settings.sound_effect_volume": "Громкость звуковых эффектов", - "hud.settings.audio_device": "Аудио устройство", - - "hud.settings.awaitingkey": "Нажми клавишу...", - "hud.settings.unbound": "Ничего", - "hud.settings.reset_keybinds": "По-умолчанию", - - "hud.social": "Другие игроки", - "hud.social.online": "Онлайн", - "hud.social.friends": "Друзья", - "hud.social.not_yet_available": "Пока недоступно", - "hud.social.faction": "Фракция", - "hud.social.play_online_fmt": "{nb_player} игрок(ов) онлайн", - "hud.social.name": "Имя", - "hud.social.level": "Ур.", - "hud.social.zone": "Зона", - "hud.social.account": "Аккаунт", - - - "hud.crafting": "Крафт", - "hud.crafting.recipes": "Рецепты", - "hud.crafting.ingredients": "Ингредиенты:", - "hud.crafting.craft": "Создать", - "hud.crafting.tool_cata": "Требуется:", - - "hud.group": "Группа", - "hud.group.invite_to_join": "[{name}] пригласил вас в свою группу!", - "hud.group.invite": "Пригласить", - "hud.group.kick": "Кикнуть", - "hud.group.assign_leader": "Назначить лидером", - "hud.group.leave": "Покинуть группу", - "hud.group.dead" : "Мертв", - "hud.group.out_of_range": "Слишком далеко", - "hud.group.add_friend": "Добавить в друзья", - "hud.group.link_group": "Объединить группы", - "hud.group.in_menu": "В меню", - "hud.group.members": "Участники группы", - - "hud.spell": "Заклинания", - - "hud.free_look_indicator": "Свободная камера активна. Нажмите {key}, чтобы выключить", - "hud.auto_walk_indicator": "Автодвижение активно", - - "hud.map.difficulty": "Сложность", - "hud.map.towns": "Города", - "hud.map.castles": "Замки", - "hud.map.dungeons": "Данжи", - "hud.map.caves": "Пещеры", - "hud.map.cave": "Пещера", - "hud.map.town": "Город", - "hud.map.castle": "Замок", - "hud.map.dungeon": "Данж", - "hud.map.difficulty_dungeon": "Сложность данжа: {difficulty}", - "hud.map.drag": "Перетащить", - "hud.map.zoom": "Зум", - "hud.map.recenter": "Рецентрировать", - - /// End HUD section - - - /// Start GameInput section - - "gameinput.primary": "Основная атака", - "gameinput.secondary": "Второстепенная атака/Блок/Прицел", - "gameinput.slot1": "Быстрый слот 1", - "gameinput.slot2": "Быстрый слот 2", - "gameinput.slot3": "Быстрый слот 3", - "gameinput.slot4": "Быстрый слот 4", - "gameinput.slot5": "Быстрый слот 5", - "gameinput.slot6": "Быстрый слот 6", - "gameinput.slot7": "Быстрый слот 7", - "gameinput.slot8": "Быстрый слот 8", - "gameinput.slot9": "Быстрый слот 9", - "gameinput.slot10": "Быстрый слот 10", - "gameinput.swaploadout": "Сменить снаряжение", - "gameinput.togglecursor": "Отображать курсор", - "gameinput.help": "Отображать окно помощи", - "gameinput.toggleinterface": "Отображать интерфейс", - "gameinput.toggledebug": "Отображать FPS и экран отладки", - "gameinput.screenshot": "Сделать скриншот", - "gameinput.toggleingameui": "Отображать неймтеги", - "gameinput.fullscreen": "Включить полный экран", - "gameinput.moveforward": "Двигаться вперед", - "gameinput.moveleft": "Двигаться влево", - "gameinput.moveright": "Двигаться вправо", - "gameinput.moveback": "Двигаться назад", - "gameinput.jump": "Прыжок", - "gameinput.glide": "Глайдер", - "gameinput.roll": "Кувырок", - "gameinput.climb": "Карабкаться", - "gameinput.climbdown": "Карабкаться вниз", - "gameinput.wallleap": "Прыжок от стены", - "gameinput.togglelantern": "Включить фонарь", - "gameinput.mount": "Оседлать", - "gameinput.enter": "Войти", - "gameinput.chat": "Чат", - "gameinput.command": "Командовать", - "gameinput.escape": "Выйти", - "gameinput.map": "Карта", - "gameinput.bag": "Рюкзак", - "gameinput.social": "Социальное", - "gameinput.sit": "Сесть", - "gameinput.spellbook": "Заклинания", - "gameinput.settings": "Настройки", - "gameinput.respawn": "Возродиться", - "gameinput.charge": "Зарядить", - "gameinput.togglewield": "Достать/убрать оружие", - "gameinput.interact": "Взаимодействовать", - "gameinput.freelook": "Свободная камера", - "gameinput.autowalk": "Автодвижение", - "gameinput.dance": "Танцевать", - "gameinput.select": "Выбрать объект", - "gameinput.acceptgroupinvite": "Принять приглашение в группу", - "gameinput.declinegroupinvite": "Отклонить приглашение в группу", - "gameinput.crafting": "Крафт", - "gameinput.fly": "Полет", - "gameinput.sneak": "Скрытность", - "gameinput.swimdown": "Плыть вниз", - "gameinput.swimup": "Плыть вверх", - - /// End GameInput section - - - /// Start chracter selection section - "char_selection.loading_characters": "Загрузка персонажей...", - "char_selection.delete_permanently": "Навсегда удалить этого персонажа?", - "char_selection.deleting_character": "Удаление персонажа...", - "char_selection.change_server": "Сменить сервер", - "char_selection.enter_world": "Войти в мир", - "char_selection.logout": "Выйти в меню", - "char_selection.create_new_character": "Создать нового персонажа", - "char_selection.creating_character": "Создание персонажа...", - "char_selection.character_creation": "Создание персонажа", - - "char_selection.human_default": "Стандартный человек", - "char_selection.level_fmt": "Уровень {level_nb}", - "char_selection.uncanny_valley": "Wilderness", - "char_selection.plains_of_uncertainty": "Plains of Uncertainty", - "char_selection.beard": "Борода", - "char_selection.hair_style": "Прическа", - "char_selection.hair_color": "Цвет волос", - "char_selection.eye_color": "Цвет глаз", - "char_selection.skin": "Кожа", - "char_selection.eyeshape": "Детали глаз", - "char_selection.accessories": "Аксессуары", - "char_selection.create_info_name": "Вашему персонажу необходимо имя!", - - /// End chracter selection section - - - /// Start character window section - "character_window.character_name": "Имя персонажа", - // Charater stats - "character_window.character_stats": r#"Стойкость - -Выносливость - -Сила воли - -Защита -"#, - /// End character window section - - - /// Start Escape Menu Section - "esc_menu.logout": "Выйти в меню", - "esc_menu.quit_game": "Выйти из игры", - /// End Escape Menu Section - - /// Buffs and Debuffs - "buff.remove": "Кликните, чтобы убрать", - "buff.title.missing": "Отсутствует название", - "buff.desc.missing": "Отсутствует описание", - // Buffs - "buff.title.heal": "Лечение", - "buff.desc.heal": "Лечит в течении времени.", - "buff.title.potion": "Зелье", - "buff.desc.potion": "Пьем...", - "buff.title.saturation": "Сыт", - "buff.desc.saturation": "Получайте здоровье от расходников в течении времени.", - // Debuffs - "buff.title.bleed": "Кровотечение", - "buff.desc.bleed": "Наносит переодический урон.", }, @@ -558,105 +39,196 @@ https://veloren.net/account/."#, vector_map: { "loading.tips": [ "Нажмите 'G', чтобы зажечь фонарь.", - "Нажмите 'F1', чтобы увидеть управление по-умолчанию.", - "Вы можете написать /say или /s, чтобы обратиться только к игрокам вблизи вас.", - "Вы можете написать /region или /r, чтобы обратиться к игрокам в нескольких сотнях блоков вокруг вас.", - "Вы можете написать /group или /g, чтобы обратиться к игрокам в вашей группе.", - "Чтобы отправить приватное сообщение, напишите /tell, а затем имя персонажа и сообщение.", - "NPC одного уровня могут быть разной сложности.", - "Осматривайтесь, чтобы найти еду, сундуки и другой лут!", - "Инвентарь забит едой? Попробуйте скрафтить из нее еду получше!", - "Думаете, чем заняться? Подземелья отмечены коричневыми метками на карте!", - "Не забудьте настроить графику под свою систему. Нажмите 'N', чтобы открыть настройки.", - "Играть с остальными веселее! Нажмите 'O', чтобы посмотреть кто онлайн.", - "NPC с черепом около полоски здоровья намного сильнее, по сравнению с вами.", - "Нажмите 'J', чтобы танцевать. Тусовка!", - "Нажмите 'L-Shift', чтобы открыть Глайдер и покорить небеса.", - "Veloren все еще пре-альфа. Мы стараемся улучшать его каждый день!", - "Если вы хотите присоединиться к команде разработчиков или просто пообщаться с нами, заходите на наш Дискорд-сервер.", - "Вы можете включить отображение чисел на полосе здоровья в настройках.", - "Чтобы увидеть свои характеристики нажмите на 'Статы' в инвентаре.", + "Нажмите 'F1', чтобы просмотреть все клавиши по умолчанию.", + "Вы можете ввести /tell или /s, чтобы общаться только с игроками непосредственно вокруг вас.", + "Вы можете ввести /region или /r, чтобы общаться только с игроками в паре сотен блоков вокруг вас.", + "Администраторы могут использовать команду /build для входа в режим постройки.", + "Вы можете ввести /group или /g, чтобы общаться только с игроками в вашей текущей группе.", + "Чтобы отправить личные сообщения, введите /tell, а затем имя игрока и ваше сообщение.", + "Смотрите внимательно чтобы найти еду, сундуки и другие предметы, разбросанные по всему миру!", + "Инвентарь, заполненный едой? Попробуйте приготовить из нее еду получше!", + "Интересно, чем можно заняться? Попробуйте пройти одно из подземелий, отмеченных на карте!", + "Не забудьте настроить графику для вашей системы. Нажмите 'N', чтобы открыть настройки.", + "Играть с другими-это весело! Нажмите 'О', чтобы узнать, кто находится в сети.", + "Нажмите 'J', чтобы танцевать. Вечеринка!", + "Нажмите 'L-Shift', чтобы открыть свой дельтаплан и покорить небо.", + "Veloren все еще находится в Пре-Альфе. Мы делаем все возможное, чтобы улучшать его каждый день!", + "Если вы хотите присоединиться к команде разработчиков или просто пообщаться с нами, присоединяйтесь к нашему серверу Discord.", + "Вы можете переключить отображение количества здоровья на панели здоровья в настройках.", + "Сядьте у костра (с помощью клавиши 'К'), чтобы залечить свои раны.", + "Вам нужно больше сумок или лучшая броня, чтобы продолжить свое путешествие? Нажмите 'C', чтобы открыть меню крафта!", + ], + "npc.speech.villager": [ + "Хорошая погода?", + "Как дела?", + "Доброе утро!", + "Интересно, что думает Catoblepas когда ест траву.", /// in original Catoblepas (киса) но яб сменил на овцу в оригинале + "Хорошая погода, не правда ли?", + "Мысли об этих подземельях пугают меня. Надеюсь их кто-нибудь уничтожит", + "Когда я вырасту, я буду исследовать пещеры.", + "Вы не видели моего кота?", + "Вы когда-нибудь слышали о свирепых сухопутных акулах? Я слышал, они живут в пустынях", + "Говорят, в пещерах можно найти блестящие драгоценные камни всех видов.", + "Я просто помешан на сыре!", + "Ты не зайдешь? Мы как раз собирались съесть немного сыра", + "Говорят, мухоморы полезны для здоровья. Сам я их не ем.", + "Не забудь про печенья!", + "Я просто обожаю сыр дварфов. Я бы хотел научиться его готовить.", + "Интересно, что по ту сторону гор.", + "Я надеюсь сделать свой собственный дельтаплан когда-нибудь.", + "Хочешь, покажу тебе свой сад? Ладно, может в следующий раз.", + "Прекрасный день для прогулки по лесу!", + "Быть или не быть? Я подумываю о том чтобы стать фермером.", + "Тебе не кажется, что наша деревня самая лучшая?", + "Как ты думаешь, что заставляет Glowing Remains светься?", + "Время второго завтрака!", + "Ты когда - нибудь ловил светлячка?", + "I just can't understand where those Sauroks keep coming from.", + "Я бы хотел, чтобы кто-нибудь держал волков подальше от деревнию.", + "Прошлой ночью мне приснился чудесный сон о сыре. Что это значит?", + "Я оставила немного сыра у брата. Теперь я не знаю, съеден сыр или нет. Я называю его сыром Шредингера.", + "Я оставил немного сыра у сестры. Теперь я не знаю, съеден сыр или нет. Я называю его сыром Шредингера.", + "Кто-то должен что-то сделать с этими культистами. Желательно не я.", + "Надеюсь, скоро пойдет дождь. Это было бы хорошо для урожая.", + "Я люблю мед! И я ненавижу пчел.", + "Я хочу однажды увидеть мир. В жизни должно быть что-то большее, чем эта деревня.", + ], + "npc.speech.villager_decline_trade": [ + "Извините, мне нечем торговать.", + "Торговля? Как будто у меня есть что-то, что может вас заинтересовать.", + "Мой дом принадлежит мне, я не променяю его ни на что.", + ], + "npc.speech.merchant_advertisement": [ + "Могу ли я заинтересовать вас сделкой?", + "Ты хочешь со мной поторговать?", + "У меня много товаров, не хочешь взглянуть?" + ], + "npc.speech.merchant_busy": [ + "Эй, подожди своей очереди.", + "Пожалуйста, подождите, я здесь один на всех.", + "Видите другого человека перед собой?", + "Минутку, дай мне закончить.", + "Не лезь вне очереди!", + "Я занят, зайди попозже." + ], + "npc.speech.merchant_trade_successful": [ + "Спасибо, что торгуете со мной!", + "Спасибо вам!", + ], + "npc.speech.merchant_trade_declined": [ + "Может быть, в другой раз, хорошего дня!", + "Жаль, тогда, может быть, в следующий раз!" + ], + "npc.speech.villager_cultist_alarm": [ + "Берегись! На свободе разгуливает культист!", + "К оружию! Культисты атакуют!", + "Как посмели культисты напасть на нашу деревню!", + "Смерть культистам!", + "Культистов здесь не потерпят!", + "Кровожадный культист!", + "Попробуй на вкус острие моего меча, грязный культист!", + "Ничего не сможет смыть кровь с твоих рук, культист!", + "Миллиарды пузырящихся синих ракушек! Культист среди нас!", + "Зло этого культиста вот-вот закончится!", + "Этот культист мой!", + "Приготовься встретить своего создателя, грязный культист!", + "Я вижу культиста! Схватите его!", + "Я вижу культиста! В атаку!", + "Я вижу культиста! Не дайте им сбежать!", + "Будет ли самый почтенный культист заботиться о какой-то СМЕРТИ?!", + "Никогда не прощу! Никогда не забуду! Культист, сожалею!", + "Умри, культист!", + "Ваше царство террора захвачено!", + "Вот тебе за все, что ты сделал!", + "Мы не очень хорошо относимся к вашим людям здесь.", + "Тебе следовало оставаться под землей!", ], "npc.speech.villager_under_attack": [ - "Помогите, меня атакуют!", - "Помогите, меня атакуют!", - "Ай! Меня атакуют!", - "Ай! Меня атакуют! На помощь!", - "Помогите мне! Меня атакуют!", + "Помогите, Меня атакуют!", + "Помогите, Меня атакуют!", + "Оуч, Меня атакуют!", + "Оуч, Меня атакуют!", + "Помоги мне! Меня атакуют!", "Меня атакуют! Помогите!", - "Меня атакуют!! На помощь!", + "Меня атакуют! Помогите мне!", "Помогите!", - "На помощь! На помощь!", + "Помогите! Помогите!", "Помогите! Помогите! Помогите!", "Меня атакуют!", - "ААА! Меня атакуют!", - "AAA! Меня атакуют! На помощь!", - "Помогите! Нас атакуют!", - "На помощь! Убийца!", - "Помогите! Здесь убийца!", - "На помощь! Меня пытаются убить!", - "Стража, меня атакуют!", - "Стража, на помощь!", + "АААААА! Меня атакуют!", + "АААААА! Меня атакуют! Помогите!", + "Помогите! Мы атакованны!", + "Помогите! Убийца!", + "Помогите! Убийца на свободе!", + "Помогите! Они пытаются меня убить!", + "Стража, Меня атакуют!", + "Стража! Меня атакуют!", "Меня атакуют! Стража!", "Помогите! Стража! Меня атакуют!", - "Стража! Cкорее!", + "Стража! Скорее!", "Стража! Стража!", - "Стража! На меня напали!", - "Стража, убейте этого мерзкого злодея", - "Стража! Тут убийца!", - "Стража! Помогите мне!", - "Тебе это не сойдет с рук! Охрана!", - "Ты враг!", - "Помогите!", - "На помощь! Пожалуйста!", - "Ай! Стража, помогите!", - "Они пришли за мной!", - "На помощь, на помощь, на меня напали!", + "Стража! Этот злодей бьёт меня!", + "Стража, Схватите этого негодяя!", + "Стража! Здесь убийца!", + "Стража! Помогите me!", + "Тебе это не сойдет с рук! Стража!", + "Ты изверг!", + "Помогите мне!", + "Помогите! Пожалуйста!", + "Ой! Стража! Помогите!", + "Они идут за мной!", + "Помогите! Помогите! Меня постигла расплата!", "Ах, теперь мы видим насилие, присущее системе.", "Это всего лишь царапина!", - "Прекрати!", - "Что я вообще тебе сделал?!", - "Пожалуйста, прекрати меня бить!", - "Эй, поаккуратнее с этой штукой!", - "Мерзкий негодняй, отстань!", - "Остановись! Уходи!", - "Ты злишь меня!", - "Ай! Кем ты себя возомнил?!", - "Я лишу тебя головы за это!", - "Остановись! У меня нет ничего ценного!", - "Я натравлю на тебя братьев! Они больше меня!", - "Нееет, я расскажу маме!", + "Остановитесь!", + "Что я тебе сделал?!", + "Пожалуйста, не бей!", + "Эй! Смотри, куда направляешь эту штуку", + "Гнусный негодяй, проваливай отсюда!", + "Прекрати! Уходи!", + "Ты уже достал!", + "Эй! Что ты возомнил о себе?!", + "Я тебе башку оторву!", + "Остановись пожалуйста. У меня ничего нет!", + "Я позову брата, он больше меня", + "Нет! Я расскажу маме!", "Будь ты проклят!", - "Пожалуйста, не надо!", - "Это было недружелюбно!", - "Хорошо, ты сильный, а теперь убери оружие!", - "Пощади меня!", + "Пожалуйста, не делай этого.", + "Это не приятно!", + "Ваше оружие работает, вы можете убрать его прямо сейчас!", + "Пощади!", "Пожалуйста, у меня семья!", - "Я слишком молод, чтобы умирать!", - "Мы можем решить все словами?", + "Я слишком молод чтобы умереть!", + "Может договоримся?", "Насилие не выход!", - "Так и знал, что день будет плохим...", - "Эй, больно же!", - "Эй!", - "Как некультурно!", - "Остановись, я прошу!", - "Проклятие!", + "Сегодня выдался плохой день...", + "Эй, это больно!", + "Ик!", + "Как грубо!", + "Остановись, прошу тебя!", + "Чтоб ты сдох!", "Это не смешно.", "Как ты смеешь?!", "Ты заплатишь за это!", - "Не продолжай, а то пожалеешь!", - "Не заставляй делать тебе больно!", - "Ты все неправильно понял!", - "Зачем ты так?!", - "Проваливай, вражина!", - "Это было больно!", - "Почему ты это делаешь?", - "Ради духов, уймись!", - "Ты меня с кем-то спутал!", - "Я не заслужил этого!", - "Пожалуйста, не делай так больше.", - "Стража, киньте этого монстра в озеро!", - "Я натравлю на тебя своего тараска!", + "Ты об этом пожалеешь!", + "Не заставляй меня делать тебе больно!", + "Произошла какая то ошибка!", + "Не делай этого!", + "Изыди, дьявол", + "Это очень больно!", + "Зачем ты это сделал?", + "Ради всего святого, прекрати!", + "Ты меня перепутал с кем то", + "Я не заслуживаю этого!", + "Пожалуйста, больше так не делай.", + "Стража, утопите этого монстра в озере!", + "Я натравлю своего tarasque на тебя!", + "Почему я?", ], + "npc.speech.villager_enemy_killed": [ + "Я уничтожил врага!", + "Наконец-то мир!", + "... что же я наделал?", + ] } ) diff --git a/assets/voxygen/i18n/ru_RU/char_selection.ron b/assets/voxygen/i18n/ru_RU/char_selection.ron new file mode 100644 index 0000000000..a990ea7775 --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/char_selection.ron @@ -0,0 +1,32 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + "char_selection.loading_characters": "Загрузка персонажей...", + "char_selection.delete_permanently": "Навсегда удалить этого персонажа?", + "char_selection.deleting_character": "Удаление Персонажа...", + "char_selection.change_server": "Сменить сервер", + "char_selection.enter_world": "Войти в мир", + "char_selection.logout": "Разлогиниться", + "char_selection.create_new_character": "Создать нового персонажа", + "char_selection.creating_character": "Создание персонажа...", + "char_selection.character_creation": "Персонаж создан", + "char_selection.human_default": "Стандартный человек", + "char_selection.level_fmt": "Уровень {level_nb}", + "char_selection.uncanny_valley": "Wilderness", + "char_selection.plains_of_uncertainty": "Plains of Uncertainty", + "char_selection.beard": "Борода", + "char_selection.hair_style": "Прическа", + "char_selection.hair_color": "Цвет волос", + "char_selection.eye_color": "Цвет глаз", + "char_selection.skin": "Кожа", + "char_selection.eyeshape": "Детали глаз", + "char_selection.accessories": "Аксессуары", + "char_selection.create_info_name": "Вашему персонажу нужно имя!", + "char_selection.version_mismatch": "ПРЕДУПРЕЖДЕНИЕ! На этом сервере работает другая, возможно, несовместимая версия игры. Пожалуйста, обновите свою игру.", + }, + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/ru_RU/common.ron b/assets/voxygen/i18n/ru_RU/common.ron new file mode 100644 index 0000000000..b72812e10c --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/common.ron @@ -0,0 +1,117 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for RUS +( + string_map: { + // Texts used in multiple locations with the same formatting + "common.username": "Юзернейм", + "common.singleplayer": "Одиночная игра", + "common.multiplayer": "Онлайн игра", + "common.servers": "Сервера", + "common.quit": "Выход", + "common.settings": "Настройки", + "common.languages": "Языки", + "common.interface": "Интерфейс", + "common.gameplay": "Гемплей", + "common.controls": "Управление", + "common.video": "Графика", + "common.sound": "Звук", + "common.chat": "Чат", + "common.resume": "Продолжить", + "common.characters": "Персонажи", + "common.close": "Закрыть", + "common.yes": "Да", + "common.no": "Нет", + "common.back": "Назад", + "common.create": "Создание", + "common.okay": "Окей", + "common.add": "Добавить", + "common.accept": "Принять", + "common.decline": "Отказаться", + "common.disclaimer": "Предупреждение", + "common.cancel": "Отменить", + "common.none": "Отключены", + "common.error": "Ошибка", + "common.fatal_error": "Фатальная ошибка", + "common.you": "Вы", + "common.automatic": "Авто", + "common.random": "Случайно", + "common.empty": "Пустой", + + // Settings Window title + "common.interface_settings": "Настройки интерфейса", + "common.gameplay_settings": "Настройки гемплея", + "common.controls_settings": "Настройки управления", + "common.video_settings": "Графические настройки", + "common.sound_settings": "Настройки звука", + "common.language_settings": "Настройки языка", + "common.chat_settings": "Настройки чата", + + // Message when connection to the server is lost + "common.connection_lost": r#"Связь потеряна! +Клиент обновлен до версии сервера?"#, + + + "common.species.orc": "Орк", + "common.species.human": "Человек", + "common.species.dwarf": "Дварфы", + "common.species.elf": "Эльфы", + "common.species.undead": "Нежить", + "common.species.danari": "Данари", + + "common.weapons.axe": "Топор", + "common.weapons.sword": "Меч", + "common.weapons.staff": "Посох", + "common.weapons.bow": "Лук", + "common.weapons.hammer": "Молот", + "common.weapons.general": "Общий бой", + "common.weapons.sceptre": "Скипетр", + "common.weapons.shield": "Защита", + "common.weapons.spear": "Копье", + "common.weapons.hammer_simple": "Простой молот", + "common.weapons.sword_simple": "Простой меч", + "common.weapons.staff_simple": "Простой посох", + "common.weapons.axe_simple": "Простой топор", + "common.weapons.bow_simple": "Простой лук", + "common.weapons.unique": "Уникальный", + "common.tool.debug": "Дебаг", + "common.tool.faming": "Сельскохозяйственный инструмент", + "common.tool.pick": "Кирка", + "common.kind.modular_component": "Компонент", + "common.kind.glider": "Дельтаплан", + "common.kind.consumable": "Расходуемый", + "common.kind.throwable": "Можно бросить", + "common.kind.utility": "Полезность", + "common.kind.ingredient": "Ингредиент", + "common.kind.lantern": "Фонарь", + "common.hands.one": "Одноручное", + "common.hands.two": "Двуручное", + + "common.rand_appearance": "Случайная внешность", + "common.rand_name": "Случайное имя", + + "common.stats.combat_rating": "CR", + "common.stats.power": "Сила", + "common.stats.speed": "Скорость", + "common.stats.poise": "Равновесие", + "common.stats.crit_chance": "Крит шанс", + "common.stats.crit_mult": "Множитель крита", + "common.stats.armor": "Броня", + "common.stats.poise_res":"Оглушение", + "common.stats.energy_max": "Максимальная выносливость", + "common.stats.energy_reward": "Востановление выносливости", + "common.stats.crit_power": "Сила крита", + "common.stats.stealth": "Скрытность", + "common.stats.slots": "Слоты", + + "common.material.metal": "Метал", + "common.material.wood": "Дерево", + "common.material.stone": "Камень", + "common.material.cloth": "Ткань", + "common.material.hide": "Кожа", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/ru_RU/gameinput.ron b/assets/voxygen/i18n/ru_RU/gameinput.ron new file mode 100644 index 0000000000..db8d764eac --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/gameinput.ron @@ -0,0 +1,73 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for RUS +( + string_map: { + "gameinput.primary": "Основная атака", + "gameinput.secondary": "Вторичная атака", + "gameinput.block": "Блок", + "gameinput.slot1": "Быстрое меню 1", + "gameinput.slot2": "Быстрое меню 2", + "gameinput.slot3": "Быстрое меню 3", + "gameinput.slot4": "Быстрое меню 4", + "gameinput.slot5": "Быстрое меню 5", + "gameinput.slot6": "Быстрое меню 6", + "gameinput.slot7": "Быстрое меню 7", + "gameinput.slot8": "Быстрое меню 8", + "gameinput.slot9": "Быстрое меню 9", + "gameinput.slot10": "Быстрое меню 10", + "gameinput.swaploadout": "Сменить снаряжение", + "gameinput.togglecursor": "ВКЛ/ВЫКЛ курсор", + "gameinput.help": "ВКЛ/ВЫКЛ окно помощи", + "gameinput.toggleinterface": "ВКЛ/ВЫКЛ интерфейс", + "gameinput.toggledebug": "ВКЛ/ВЫКЛ FPS и отладочную информацию", + "gameinput.screenshot": "Сделать скриншот", + "gameinput.toggleingameui": "ВЫКЛ/ВЫКЛ никнеймы и имена", + "gameinput.fullscreen": "ВКЛ/ВЫКЛ полноэкранный режим", + "gameinput.moveforward": "Двигаться вперед", + "gameinput.moveleft": "Двигаться влево", + "gameinput.moveright": "Двигаться вправо", + "gameinput.moveback": "Двигаться назад", + "gameinput.jump": "Прыжок", + "gameinput.glide": "Дельтаплан", + "gameinput.roll": "Перекат", + "gameinput.climb": "Взбираться", + "gameinput.climbdown": "Спускаться", + "gameinput.wallleap": "Прыжок от стены", + "gameinput.togglelantern": "ВКЛ/ВЫКЛ фонарь", + "gameinput.mount": "Оседлать", + "gameinput.chat": "Чат", + "gameinput.command": "Команда", + "gameinput.escape": "Назад", + "gameinput.map": "Карта", + "gameinput.bag": "Рюкзак", + "gameinput.trade": "Обмен", + "gameinput.social": "Список игроков", + "gameinput.sit": "Сидеть", + "gameinput.spellbook": "Навыки", + "gameinput.settings": "Настройки", + "gameinput.respawn": "Возродиться", + "gameinput.charge": "Зарядить", + "gameinput.togglewield": "Переключить Управление", + "gameinput.interact": "Взаимодействовать", + "gameinput.freelook": "Свободная камера", + "gameinput.autowalk": "Автодвижение", + "gameinput.cameraclamp": "Фиксирование камеры", + "gameinput.dance": "Танцевать", + "gameinput.select": "Выбор объекта", + "gameinput.acceptgroupinvite": "Принять приглашение в группу", + "gameinput.declinegroupinvite": "Отвергнуть приглашение в группу", + "gameinput.cyclecamera": "Переключение камеры", + "gameinput.crafting": "Крафтинг", + "gameinput.fly": "Полет", + "gameinput.sneak": "Красться", + "gameinput.swimdown": "Погружение (в воду)", + "gameinput.swimup": "Всплыть", + "gameinput.mapzoomin": "Увеличение масштаба карты", + "gameinput.mapzoomout": "Уменьшение масштаба карты", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/ru_RU/main.ron b/assets/voxygen/i18n/ru_RU/main.ron index 26a42b40d1..752188d27f 100644 --- a/assets/voxygen/i18n/ru_RU/main.ron +++ b/assets/voxygen/i18n/ru_RU/main.ron @@ -1,6 +1,6 @@ /// WARNING: Localization files shall be saved in UTF-8 format without BOM -/// Localization for RU +/// Localization for RUS ( string_map: { /// Start Main screen section @@ -52,14 +52,14 @@ https://veloren.net/account/."#, "main.login.network_wrong_version": "Несоответствие версии сервера и клиента, пожалуйста, обновите свой игровой клиент.", "main.login.failed_sending_request": "Не удалось выполнить запрос на сервер аутентификации", "main.login.invalid_character": "Выбранный символ недопустим", - "main.login.client_crashed": "Клиент разбился", + "main.login.client_crashed": "Клиент крашнулся", "main.login.not_on_whitelist": "Вам нужна запись в Белом списке от администратора, чтобы присоединиться", "main.login.banned": "Вы были забанены по следующей причине", "main.login.kicked": "Вас выгнали по следующей причине", "main.login.select_language": "Выберите язык", "main.login.client_version": "Версия клиента", - "main.login.server_version": "Server Version", - "main.servers.select_server": "Версия сервера", + "main.login.server_version": "Версия сервера", + "main.servers.select_server": "Выбор сервера", /// End Main screen section }, diff --git a/assets/voxygen/i18n/ru_RU/skills.ron b/assets/voxygen/i18n/ru_RU/skills.ron index 5b898545cf..8b347dcd22 100644 --- a/assets/voxygen/i18n/ru_RU/skills.ron +++ b/assets/voxygen/i18n/ru_RU/skills.ron @@ -1,6 +1,6 @@ /// WARNING: Localization files shall be saved in UTF-8 format without BOM -/// Localization for "global" English +/// Localization for RUS ( string_map: { "hud.rank_up": "Новый скиллпоинт", @@ -169,11 +169,11 @@ "hud.skill.hmr_single_strike_knockback" : "Увеличьте потенциал отбрасывания на 50%{SP}", "hud.skill." : "", // Sword - "hud.skill.sw_trip_str_title": "Тройное вращение", - "hud.skill.sw_trip_str": "Вращение на 3 оборота", + "hud.skill.sw_trip_str_title": "Тройной удар", + "hud.skill.sw_trip_str": "Атака из 3х ударов в комбо", "hud.skill.sw_trip_str_combo_title": "Тройной удар Комбо", - "hud.skill.sw_trip_str_combo": "Разблокирует комбо при тройном вращении{SP}", - "hud.skill.sw_trip_str_dmg_title": "Урон тройного вращения", + "hud.skill.sw_trip_str_combo": "Разблокирует комбо для тройного удара{SP}", + "hud.skill.sw_trip_str_dmg_title": "Урон тройного удара", "hud.skill.sw_trip_str_dmg": "Увеличивает урон, наносимый каждым последующим ударом{SP}", "hud.skill.sw_trip_str_sp_title": "Скорость атаки", "hud.skill.sw_trip_str_sp": "Увеличивает скорость атаки, получаемую при каждом последующем ударе{SP}", From 84080607cd9c5f0d6cbd471ebd0ee1763b1989a1 Mon Sep 17 00:00:00 2001 From: Snowram Date: Tue, 15 Jun 2021 19:36:20 +0200 Subject: [PATCH 070/180] Truncates consumables value to the first decimal place --- voxygen/src/hud/util.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/voxygen/src/hud/util.rs b/voxygen/src/hud/util.rs index 7f93c96049..c66f332cfd 100644 --- a/voxygen/src/hud/util.rs +++ b/voxygen/src/hud/util.rs @@ -116,13 +116,13 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> String { let buff_desc = match buff.kind { BuffKind::Saturation | BuffKind::Regeneration | BuffKind::Potion => i18n .get("buff.stat.health") - .replace("{str_total}", &str_total.to_string()), + .replace("{str_total}", &*format!("{:.1}", &str_total)), BuffKind::IncreaseMaxEnergy => i18n .get("buff.stat.increase_max_stamina") - .replace("{strength}", &strength.to_string()), + .replace("{strength}", &*format!("{:.1}", &strength)), BuffKind::IncreaseMaxHealth => i18n .get("buff.stat.increase_max_health") - .replace("{strength}", &strength.to_string()), + .replace("{strength}", &*format!("{:.1}", &strength)), BuffKind::Invulnerability => i18n.get("buff.stat.invulnerability").to_string(), BuffKind::Bleeding | BuffKind::Burning From 52c965fb6d1d92f52fe623a831b0fba71303531c Mon Sep 17 00:00:00 2001 From: Zakru Date: Tue, 15 Jun 2021 20:52:59 +0300 Subject: [PATCH 071/180] Avoid saving settings if maps are not updating Fixes #1193 --- voxygen/src/hud/map.rs | 14 +++++++++----- voxygen/src/hud/minimap.rs | 4 ++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/voxygen/src/hud/map.rs b/voxygen/src/hud/map.rs index f2389230ad..7ea03cf1b8 100644 --- a/voxygen/src/hud/map.rs +++ b/voxygen/src/hud/map.rs @@ -327,7 +327,9 @@ impl<'a> Widget for Map<'a> { .sum(); // Drag represents offset of view from the player_pos in chunk coords let drag_new = drag + dragged / map_size / zoom * max_zoom; - events.push(Event::SettingsChange(MapDrag(drag_new))); + if drag_new != drag { + events.push(Event::SettingsChange(MapDrag(drag_new))); + } let rect_src = position::Rect::from_xy_dim( [ @@ -392,10 +394,12 @@ impl<'a> Widget for Map<'a> { .scrolls() .map(|scroll| scroll.y) .sum(); - let new_zoom_lvl = (self.global_state.settings.interface.map_zoom - * (scrolled * 0.05 * -1.0).exp2()) - .clamped(1.25, max_zoom / 64.0); - events.push(Event::SettingsChange(MapZoom(new_zoom_lvl as f64))); + if scrolled != 0.0 { + let new_zoom_lvl = (self.global_state.settings.interface.map_zoom + * (scrolled * 0.05 * -1.0).exp2()) + .clamped(1.25, max_zoom / 64.0); + events.push(Event::SettingsChange(MapZoom(new_zoom_lvl as f64))); + } // Icon settings // Alignment Rectangle::fill_with([150.0, 200.0], color::TRANSPARENT) diff --git a/voxygen/src/hud/minimap.rs b/voxygen/src/hud/minimap.rs index 7eb760a8aa..6eabfb7275 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -514,6 +514,7 @@ impl<'a> Widget for MiniMap<'a> { // Set the image dimensions here, rather than recomputing each time. zoom = min_zoom.max(zoom / ZOOM_FACTOR); // set_image_dims(zoom); + events.push(Event::SettingsChange(MinimapZoom(zoom))); } if Button::image(self.imgs.mmap_plus) .w_h(18.0 * SCALE, 18.0 * SCALE) @@ -528,6 +529,7 @@ impl<'a> Widget for MiniMap<'a> { { zoom = min_zoom.max(zoom * ZOOM_FACTOR); // set_image_dims(zoom); + events.push(Event::SettingsChange(MinimapZoom(zoom))); } // Always northfacing button @@ -555,8 +557,6 @@ impl<'a> Widget for MiniMap<'a> { events.push(Event::SettingsChange(MinimapFaceNorth(!is_facing_north))); } - events.push(Event::SettingsChange(MinimapZoom(zoom))); - // Coordinates let player_pos = self .client From afc04ae22fd25326c8350509cbd4db5238e0568a Mon Sep 17 00:00:00 2001 From: Zakru Date: Tue, 15 Jun 2021 20:56:06 +0300 Subject: [PATCH 072/180] Fix settings not saving when zooming map with keys --- voxygen/src/hud/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 983277688d..99710dda72 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -3394,10 +3394,12 @@ impl Hud { .clamped(1.25, max_zoom / 64.0); global_state.settings.interface.map_zoom = new_zoom_lvl; + global_state.settings.save_to_file_warn(); } else if global_state.settings.interface.minimap_show { let new_zoom_lvl = global_state.settings.interface.minimap_zoom * factor; global_state.settings.interface.minimap_zoom = new_zoom_lvl; + global_state.settings.save_to_file_warn(); } show.map && global_state.settings.interface.minimap_show From 4d3362586fc9f15fdaa9dd0e6a37cc293e797bc0 Mon Sep 17 00:00:00 2001 From: Snowram Date: Tue, 15 Jun 2021 22:11:06 +0200 Subject: [PATCH 073/180] Double the speed factor of pos interp --- voxygen/src/ecs/sys/interpolation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/voxygen/src/ecs/sys/interpolation.rs b/voxygen/src/ecs/sys/interpolation.rs index 97dba3a071..0657846372 100644 --- a/voxygen/src/ecs/sys/interpolation.rs +++ b/voxygen/src/ecs/sys/interpolation.rs @@ -43,7 +43,7 @@ impl<'a> System<'a> for Sys { { // Update interpolation values, but don't interpolate far things or objects if i.pos.distance_squared(pos.0) < 64.0 * 64.0 && !matches!(body, Body::Object(_)) { - i.pos = Lerp::lerp(i.pos, pos.0 + vel.0 * 0.03, 10.0 * dt.0); + i.pos = Lerp::lerp(i.pos, pos.0 + vel.0 * 0.03, 20.0 * dt.0); i.ori = Ori::slerp(i.ori, *ori, base_ori_interp(body) * dt.0); } else { i.pos = pos.0; From b82dde23320ee07fe1d4543a1966f8b71138b4d8 Mon Sep 17 00:00:00 2001 From: Snowram Date: Tue, 15 Jun 2021 22:12:11 +0000 Subject: [PATCH 074/180] Revert "Merge branch 'snowram/snappier-pos-interp' into 'master'" This reverts merge request !2457 --- voxygen/src/ecs/sys/interpolation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/voxygen/src/ecs/sys/interpolation.rs b/voxygen/src/ecs/sys/interpolation.rs index 0657846372..97dba3a071 100644 --- a/voxygen/src/ecs/sys/interpolation.rs +++ b/voxygen/src/ecs/sys/interpolation.rs @@ -43,7 +43,7 @@ impl<'a> System<'a> for Sys { { // Update interpolation values, but don't interpolate far things or objects if i.pos.distance_squared(pos.0) < 64.0 * 64.0 && !matches!(body, Body::Object(_)) { - i.pos = Lerp::lerp(i.pos, pos.0 + vel.0 * 0.03, 20.0 * dt.0); + i.pos = Lerp::lerp(i.pos, pos.0 + vel.0 * 0.03, 10.0 * dt.0); i.ori = Ori::slerp(i.ori, *ori, base_ori_interp(body) * dt.0); } else { i.pos = pos.0; From e5f23eb41ab834c55b090c2cd599cbb86d352075 Mon Sep 17 00:00:00 2001 From: Imbris Date: Tue, 15 Jun 2021 01:04:42 -0400 Subject: [PATCH 075/180] Add debug line tables and avoid building unwinding landing pads in release builds --- .gitlab-ci.yml | 2 +- CHANGELOG.md | 1 + Cargo.toml | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c497e9c1c1..4967ab327b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,7 +13,7 @@ variables: # https://docs.gitlab.com/ee/ci/yaml/#shallow-cloning GIT_DEPTH: 3 GIT_CLEAN_FLAGS: -f - CACHE_IMAGE_TAG: 55629eab + CACHE_IMAGE_TAG: 8490f4b9 default: # https://docs.gitlab.com/ee/ci/pipelines/settings.html#auto-cancel-pending-pipelines diff --git a/CHANGELOG.md b/CHANGELOG.md index 2694d34f41..a09f064b99 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 a skill tree for mining, which gains xp from mining ores and gems. +- Added debug line info to release builds, enhancing the usefulness of panic backtraces ### Changed - Entity-entity pushback is no longer applied in forced movement states like rolling and leaping. diff --git a/Cargo.toml b/Cargo.toml index bd0e04061c..1f5d7b0750 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,7 +86,8 @@ opt-level = 3 overflow-checks = false debug-assertions = false lto = true -debug = false +debug = 1 # line tables so we can have useful backtraces +panic = "abort" # don't need unwinding so we can skip including the landing pads for that # used for cargo bench [profile.bench] From 5e6363dbcc6681eb9e2443a8b7d65e6418e55dc4 Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Tue, 15 Jun 2021 21:12:13 -0400 Subject: [PATCH 076/180] Make e2t hitboxes tighter by checking the floating point coordinates before looking up voxels. --- CHANGELOG.md | 1 + common/systems/src/phys.rs | 15 +++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 195158a9c3..5f0bdaf59d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Entity-entity pushback is no longer applied in forced movement states like rolling and leaping. - Updated audio library (rodio 0.13 -> 0.14). +- Improve entity-terrain physics performance by reducing the number of voxel lookups. ### Removed diff --git a/common/systems/src/phys.rs b/common/systems/src/phys.rs index 613431035c..ab6ac06eb4 100644 --- a/common/systems/src/phys.rs +++ b/common/systems/src/phys.rs @@ -1283,11 +1283,18 @@ fn box_voxel_collision<'a, T: BaseVol + ReadVol>( near_iter.filter_map(move |(i, j, k)| { let block_pos = pos.map(|e| e.floor() as i32) + Vec3::new(i, j, k); + // `near_iter` could be a few blocks too large due to being integer aligned and + // rounding up, so skip points outside of the tighter bounds before looking them + // up in the terrain (which incurs a hashmap cost for volgrids) + let player_aabb = Aabb { + min: pos + Vec3::new(-radius, -radius, z_range.start), + max: pos + Vec3::new(radius, radius, z_range.end), + }; + if !player_aabb.contains_point(block_pos.as_() + Vec3::broadcast(0.5)) { + return None; + } + if let Some(block) = terrain.get(block_pos).ok().copied().filter(hit) { - let player_aabb = Aabb { - min: pos + Vec3::new(-radius, -radius, z_range.start), - max: pos + Vec3::new(radius, radius, z_range.end), - }; let block_aabb = Aabb { min: block_pos.map(|e| e as f32), max: block_pos.map(|e| e as f32) + Vec3::new(1.0, 1.0, height(&block)), From 96ffae4387e5e87603bd907e20fbb7a195fdf594 Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Tue, 15 Jun 2021 22:52:02 -0400 Subject: [PATCH 077/180] Address MR 2460 comments. - Switch from point query to AABB query to take into account block thickness. --- common/systems/src/phys.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/common/systems/src/phys.rs b/common/systems/src/phys.rs index ab6ac06eb4..5852454f5f 100644 --- a/common/systems/src/phys.rs +++ b/common/systems/src/phys.rs @@ -1290,7 +1290,11 @@ fn box_voxel_collision<'a, T: BaseVol + ReadVol>( min: pos + Vec3::new(-radius, -radius, z_range.start), max: pos + Vec3::new(radius, radius, z_range.end), }; - if !player_aabb.contains_point(block_pos.as_() + Vec3::broadcast(0.5)) { + let block_approx = Aabb { + min: block_pos.as_(), + max: block_pos.as_() + Vec3::new(1.0, 1.0, Block::MAX_HEIGHT), + }; + if !player_aabb.collides_with_aabb(block_approx) { return None; } From b7162ac15c64c96870ed6d690c43550cad230be4 Mon Sep 17 00:00:00 2001 From: Imbris Date: Tue, 15 Jun 2021 04:01:57 -0400 Subject: [PATCH 078/180] Upgrade to winit 0.25 --- Cargo.lock | 168 ++++++------------ Cargo.toml | 2 - voxygen/Cargo.toml | 8 +- voxygen/src/lib.rs | 2 +- voxygen/src/main.rs | 2 +- voxygen/src/menu/char_selection/ui/mod.rs | 2 +- voxygen/src/menu/main/ui/mod.rs | 2 +- voxygen/src/ui/ice/mod.rs | 7 +- voxygen/src/ui/ice/renderer/widget/button.rs | 2 +- .../ui/ice/widget/aspect_ratio_container.rs | 6 +- .../src/ui/ice/widget/background_container.rs | 6 +- voxygen/src/ui/ice/widget/mouse_detector.rs | 4 +- voxygen/src/ui/ice/widget/overlay.rs | 40 +++-- voxygen/src/ui/ice/widget/tooltip.rs | 6 +- 14 files changed, 102 insertions(+), 155 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aeb3a27172..37398ea1ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -613,15 +613,6 @@ dependencies = [ "objc_id", ] -[[package]] -name = "clipboard_wayland" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61bcb8cde0387fde807b9b7af66ce8bd1665ef736e46e6e47fda82ea003e6ade" -dependencies = [ - "smithay-clipboard", -] - [[package]] name = "clipboard_wayland" version = "0.2.0" @@ -631,16 +622,6 @@ dependencies = [ "smithay-clipboard", ] -[[package]] -name = "clipboard_x11" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40403aa5220e5cd303d32dc4248cac8aa92bf47e3ae31e0e2481081755a63ff1" -dependencies = [ - "thiserror", - "x11rb", -] - [[package]] name = "clipboard_x11" version = "0.3.1" @@ -931,8 +912,8 @@ dependencies = [ "lazy_static", "libc", "mach 0.3.2", - "ndk 0.3.0", - "ndk-glue 0.3.0", + "ndk", + "ndk-glue", "nix 0.20.0", "oboe", "parking_lot 0.11.1", @@ -1054,6 +1035,20 @@ dependencies = [ "itertools 0.9.0", ] +[[package]] +name = "crossbeam" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-channel", + "crossbeam-deque 0.8.0", + "crossbeam-epoch 0.9.5", + "crossbeam-queue", + "crossbeam-utils 0.8.5", +] + [[package]] name = "crossbeam-channel" version = "0.5.1" @@ -2386,13 +2381,13 @@ dependencies = [ [[package]] name = "iced_core" -version = "0.3.0" -source = "git+https://github.com/hecrj/iced?rev=8d882d787e6b7fd7c2435f42f82933e2ed904edf#8d882d787e6b7fd7c2435f42f82933e2ed904edf" +version = "0.4.0" +source = "git+https://github.com/Imberflur/iced?tag=winit-0.25#18a48a5eeefd2aa6e50f5dd0751ac2e94c88a9eb" [[package]] name = "iced_futures" -version = "0.2.0" -source = "git+https://github.com/hecrj/iced?rev=8d882d787e6b7fd7c2435f42f82933e2ed904edf#8d882d787e6b7fd7c2435f42f82933e2ed904edf" +version = "0.3.0" +source = "git+https://github.com/Imberflur/iced?tag=winit-0.25#18a48a5eeefd2aa6e50f5dd0751ac2e94c88a9eb" dependencies = [ "futures", "log", @@ -2401,8 +2396,8 @@ dependencies = [ [[package]] name = "iced_graphics" -version = "0.1.0" -source = "git+https://github.com/hecrj/iced?rev=8d882d787e6b7fd7c2435f42f82933e2ed904edf#8d882d787e6b7fd7c2435f42f82933e2ed904edf" +version = "0.2.0" +source = "git+https://github.com/Imberflur/iced?tag=winit-0.25#18a48a5eeefd2aa6e50f5dd0751ac2e94c88a9eb" dependencies = [ "bytemuck", "glam", @@ -2414,8 +2409,8 @@ dependencies = [ [[package]] name = "iced_native" -version = "0.3.0" -source = "git+https://github.com/hecrj/iced?rev=8d882d787e6b7fd7c2435f42f82933e2ed904edf#8d882d787e6b7fd7c2435f42f82933e2ed904edf" +version = "0.4.0" +source = "git+https://github.com/Imberflur/iced?tag=winit-0.25#18a48a5eeefd2aa6e50f5dd0751ac2e94c88a9eb" dependencies = [ "iced_core", "iced_futures", @@ -2426,16 +2421,16 @@ dependencies = [ [[package]] name = "iced_style" -version = "0.2.0" -source = "git+https://github.com/hecrj/iced?rev=8d882d787e6b7fd7c2435f42f82933e2ed904edf#8d882d787e6b7fd7c2435f42f82933e2ed904edf" +version = "0.3.0" +source = "git+https://github.com/Imberflur/iced?tag=winit-0.25#18a48a5eeefd2aa6e50f5dd0751ac2e94c88a9eb" dependencies = [ "iced_core", ] [[package]] name = "iced_winit" -version = "0.2.0" -source = "git+https://github.com/hecrj/iced?rev=8d882d787e6b7fd7c2435f42f82933e2ed904edf#8d882d787e6b7fd7c2435f42f82933e2ed904edf" +version = "0.3.0" +source = "git+https://github.com/Imberflur/iced?tag=winit-0.25#18a48a5eeefd2aa6e50f5dd0751ac2e94c88a9eb" dependencies = [ "iced_futures", "iced_graphics", @@ -2443,7 +2438,7 @@ dependencies = [ "log", "thiserror", "winapi 0.3.9", - "window_clipboard 0.1.4", + "window_clipboard", "winit", ] @@ -2637,7 +2632,7 @@ dependencies = [ [[package]] name = "keyboard-keynames" version = "0.1.0" -source = "git+https://gitlab.com/Frinksy/keyboard-keynames.git?rev=a97ae509cdb9dc70cf1bf0af762d2d1d3a0d6e0c#a97ae509cdb9dc70cf1bf0af762d2d1d3a0d6e0c" +source = "git+https://gitlab.com/Frinksy/keyboard-keynames.git?rev=9ae8f89014d0b0c5b61d0e821c5aeb6140c5c0dc#9ae8f89014d0b0c5b61d0e821c5aeb6140c5c0dc" dependencies = [ "libc", "memmap", @@ -3028,6 +3023,18 @@ dependencies = [ "slab", ] +[[package]] +name = "mio-misc" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ddf05411bb159cdb5801bb10002afb66cb4572be656044315e363460ce69dc2" +dependencies = [ + "crossbeam", + "crossbeam-queue", + "log", + "mio 0.7.11", +] + [[package]] name = "miow" version = "0.2.2" @@ -3097,18 +3104,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "ndk" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb167c1febed0a496639034d0c76b3b74263636045db5489eee52143c246e73" -dependencies = [ - "jni-sys", - "ndk-sys", - "num_enum 0.4.3", - "thiserror", -] - [[package]] name = "ndk" version = "0.3.0" @@ -3117,24 +3112,10 @@ checksum = "8794322172319b972f528bf90c6b467be0079f1fa82780ffb431088e741a73ab" dependencies = [ "jni-sys", "ndk-sys", - "num_enum 0.5.1", + "num_enum", "thiserror", ] -[[package]] -name = "ndk-glue" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf399b8b7a39c6fb153c4ec32c72fd5fe789df24a647f229c239aa7adb15241" -dependencies = [ - "lazy_static", - "libc", - "log", - "ndk 0.2.1", - "ndk-macro", - "ndk-sys", -] - [[package]] name = "ndk-glue" version = "0.3.0" @@ -3144,7 +3125,7 @@ dependencies = [ "lazy_static", "libc", "log", - "ndk 0.3.0", + "ndk", "ndk-macro", "ndk-sys", ] @@ -3465,16 +3446,6 @@ dependencies = [ "libc", ] -[[package]] -name = "num_enum" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca565a7df06f3d4b485494f25ba05da1435950f4dc263440eda7a6fa9b8e36e4" -dependencies = [ - "derivative", - "num_enum_derive 0.4.3", -] - [[package]] name = "num_enum" version = "0.5.1" @@ -3482,19 +3453,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "226b45a5c2ac4dd696ed30fa6b94b057ad909c7b7fc2e0d0808192bced894066" dependencies = [ "derivative", - "num_enum_derive 0.5.1", -] - -[[package]] -name = "num_enum_derive" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffa5a33ddddfee04c0283a7653987d634e880347e96b5b2ed64de07efb59db9d" -dependencies = [ - "proc-macro-crate", - "proc-macro2 1.0.27", - "quote 1.0.9", - "syn 1.0.72", + "num_enum_derive", ] [[package]] @@ -3574,8 +3533,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa187b38ae20374617b7ad418034ed3dc90ac980181d211518bd03537ae8f8d" dependencies = [ "jni", - "ndk 0.3.0", - "ndk-glue 0.3.0", + "ndk", + "ndk-glue", "num-derive", "num-traits", "oboe-sys", @@ -6147,7 +6106,7 @@ dependencies = [ "veloren-world", "wgpu", "wgpu-profiler", - "window_clipboard 0.2.1", + "window_clipboard", "winit", "winres", ] @@ -6850,19 +6809,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "window_clipboard" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37cf16659e398a96f4ab8deff2b9db2ca0c3c5d6c1b59b1d577b7f888f0f03c6" -dependencies = [ - "clipboard-win 4.2.1", - "clipboard_macos", - "clipboard_wayland 0.1.2", - "clipboard_x11 0.2.0", - "raw-window-handle", -] - [[package]] name = "window_clipboard" version = "0.2.1" @@ -6871,15 +6817,16 @@ checksum = "33a4518b538a45ad39d138a8c3bea8f6b4452174aeb38143d1dd643a3a838ccc" dependencies = [ "clipboard-win 4.2.1", "clipboard_macos", - "clipboard_wayland 0.2.0", - "clipboard_x11 0.3.1", + "clipboard_wayland", + "clipboard_x11", "raw-window-handle", ] [[package]] name = "winit" -version = "0.24.0" -source = "git+https://gitlab.com/veloren/winit.git?branch=macos-test-spiffed#488c511802dfd95ca54f6f76a38547c93c7b02c9" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79610794594d5e86be473ef7763f604f2159cbac8c94debd00df8fb41e86c2f8" dependencies = [ "bitflags", "cocoa", @@ -6891,15 +6838,16 @@ dependencies = [ "lazy_static", "libc", "log", - "mio 0.6.23", - "mio-extras", - "ndk 0.2.1", - "ndk-glue 0.2.1", + "mio 0.7.11", + "mio-misc", + "ndk", + "ndk-glue", "ndk-sys", "objc", "parking_lot 0.11.1", "percent-encoding", "raw-window-handle", + "scopeguard", "serde", "smithay-client-toolkit", "wayland-client 0.28.5", diff --git a/Cargo.toml b/Cargo.toml index db690b31c4..0342211826 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -111,8 +111,6 @@ buildInputs = ["openssl"] nativeBuildInputs = ["pkg-config"] [patch.crates-io] -# macos CI fix isn't released yet -winit = { git = "https://gitlab.com/veloren/winit.git", branch = "macos-test-spiffed" } vek = { git = "https://gitlab.com/veloren/vek.git", branch = "fix_intrinsics2" } # patch wgpu so we can use wgpu-profiler crate wgpu = { git = "https://github.com/gfx-rs/wgpu.git", rev = "a92b8549a8e2cb9dac781bafc5ed32828f3caf46" } diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index c322e6a1af..150f3d35fb 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -44,7 +44,7 @@ anim = {package = "veloren-voxygen-anim", path = "anim"} i18n = {package = "veloren-i18n", path = "i18n"} # Graphics -winit = {version = "0.24.0", features = ["serde"]} +winit = {version = "0.25.0", features = ["serde"]} wgpu = { version = "=0.8.0", features = ["trace", "cross"] } wgpu-profiler = { git = "https://github.com/Imberflur/wgpu-profiler", tag = "wgpu-0.8" } bytemuck = { version="1.4", features=["derive"] } @@ -54,11 +54,11 @@ shaderc = "0.6.2" conrod_core = {git = "https://gitlab.com/veloren/conrod.git", branch="copypasta_0.7"} conrod_winit = {git = "https://gitlab.com/veloren/conrod.git", branch="copypasta_0.7"} euc = "0.5.0" -iced = {package = "iced_native", git = "https://github.com/hecrj/iced", rev = "8d882d787e6b7fd7c2435f42f82933e2ed904edf"} -iced_winit = {git = "https://github.com/hecrj/iced", rev = "8d882d787e6b7fd7c2435f42f82933e2ed904edf"} +iced = {package = "iced_native", git = "https://github.com/Imberflur/iced", tag = "winit-0.25"} +iced_winit = {git = "https://github.com/Imberflur/iced", tag = "winit-0.25"} window_clipboard = "0.2" glyph_brush = "0.7.0" -keyboard-keynames = { git = "https://gitlab.com/Frinksy/keyboard-keynames.git", rev = "a97ae509cdb9dc70cf1bf0af762d2d1d3a0d6e0c" } +keyboard-keynames = { git = "https://gitlab.com/Frinksy/keyboard-keynames.git", rev = "9ae8f89014d0b0c5b61d0e821c5aeb6140c5c0dc" } # ECS specs = {git = "https://github.com/amethyst/specs.git", rev = "f985bec5d456f7b0dd8aae99848f9473c2cd9d46"} diff --git a/voxygen/src/lib.rs b/voxygen/src/lib.rs index 4d2a1a04dd..7532eb903d 100644 --- a/voxygen/src/lib.rs +++ b/voxygen/src/lib.rs @@ -62,7 +62,7 @@ pub struct GlobalState { pub singleplayer: Option, // TODO: redo this so that the watcher doesn't have to exist for reloading to occur pub i18n: LocalizationHandle, - pub clipboard: Option, + pub clipboard: iced_winit::Clipboard, // NOTE: This can be removed from GlobalState if client state behavior is refactored to not // enter the game before confirmation of successful character load /// An error returned by Client that needs to be displayed by the UI diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index 89ad6e7b9d..10e7742d10 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -181,7 +181,7 @@ fn main() { // Create window let (mut window, event_loop) = Window::new(&settings).expect("Failed to create window!"); - let clipboard = iced_winit::Clipboard::new(window.window()); + let clipboard = iced_winit::Clipboard::connect(window.window()); let lazy_init = SpriteRenderContext::new(window.renderer_mut()); diff --git a/voxygen/src/menu/char_selection/ui/mod.rs b/voxygen/src/menu/char_selection/ui/mod.rs index cc1c853910..a874e8c91e 100644 --- a/voxygen/src/menu/char_selection/ui/mod.rs +++ b/voxygen/src/menu/char_selection/ui/mod.rs @@ -1564,7 +1564,7 @@ impl CharSelectionUi { .view(&global_state.settings, &client, &self.error, &i18n), global_state.window.renderer_mut(), None, - global_state.clipboard.as_ref(), + &mut global_state.clipboard, ); if self.enter_pressed { diff --git a/voxygen/src/menu/main/ui/mod.rs b/voxygen/src/menu/main/ui/mod.rs index d17c06309f..cb333a9671 100644 --- a/voxygen/src/menu/main/ui/mod.rs +++ b/voxygen/src/menu/main/ui/mod.rs @@ -569,7 +569,7 @@ impl MainMenuUi { self.controls.view(&global_state.settings, dt.as_secs_f32()), global_state.window.renderer_mut(), None, - global_state.clipboard.as_ref(), + &mut global_state.clipboard, ); messages.into_iter().for_each(|message| { diff --git a/voxygen/src/ui/ice/mod.rs b/voxygen/src/ui/ice/mod.rs index 849ab7a76a..91393ad0cf 100644 --- a/voxygen/src/ui/ice/mod.rs +++ b/voxygen/src/ui/ice/mod.rs @@ -148,7 +148,7 @@ impl IcedUi { root: E, renderer: &mut Renderer, pool: Option<&SlowJobPool>, - clipboard: Option<&Clipboard>, + clipboard: &mut Clipboard, ) -> (Vec, mouse::Interaction) { span!(_guard, "maintain", "IcedUi::maintain"); // Handle window resizing, dpi factor change, and scale mode changing @@ -194,11 +194,8 @@ impl IcedUi { let _event_status_list = user_interface.update( &self.events, cursor_position, - match clipboard { - Some(c) => Some(c), - None => None, - }, &self.renderer, + clipboard, &mut messages, ); messages diff --git a/voxygen/src/ui/ice/renderer/widget/button.rs b/voxygen/src/ui/ice/renderer/widget/button.rs index cf05a448bf..40a6bff71c 100644 --- a/voxygen/src/ui/ice/renderer/widget/button.rs +++ b/voxygen/src/ui/ice/renderer/widget/button.rs @@ -5,7 +5,7 @@ impl button::Renderer for IcedRenderer { // TODO: what if this gets large enough to not be copied around? type Style = style::button::Style; - const DEFAULT_PADDING: u16 = 0; + const DEFAULT_PADDING: iced::Padding = iced::Padding::ZERO; fn draw( &mut self, diff --git a/voxygen/src/ui/ice/widget/aspect_ratio_container.rs b/voxygen/src/ui/ice/widget/aspect_ratio_container.rs index 8db9bd33ca..e420f22441 100644 --- a/voxygen/src/ui/ice/widget/aspect_ratio_container.rs +++ b/voxygen/src/ui/ice/widget/aspect_ratio_container.rs @@ -119,17 +119,17 @@ where event: Event, layout: Layout<'_>, cursor_position: Point, - messages: &mut Vec, renderer: &R, - clipboard: Option<&dyn Clipboard>, + clipboard: &mut dyn Clipboard, + messages: &mut Vec, ) -> iced::event::Status { self.content.on_event( event, layout.children().next().unwrap(), cursor_position, - messages, renderer, clipboard, + messages, ) } diff --git a/voxygen/src/ui/ice/widget/background_container.rs b/voxygen/src/ui/ice/widget/background_container.rs index ce267b7a13..53de3a647d 100644 --- a/voxygen/src/ui/ice/widget/background_container.rs +++ b/voxygen/src/ui/ice/widget/background_container.rs @@ -255,17 +255,17 @@ where event: Event, layout: Layout<'_>, cursor_position: Point, - messages: &mut Vec, renderer: &R, - clipboard: Option<&dyn Clipboard>, + clipboard: &mut dyn Clipboard, + messages: &mut Vec, ) -> iced::event::Status { self.content.on_event( event, layout.children().next().unwrap(), cursor_position, - messages, renderer, clipboard, + messages, ) } diff --git a/voxygen/src/ui/ice/widget/mouse_detector.rs b/voxygen/src/ui/ice/widget/mouse_detector.rs index a93df67ae5..036a2f19a9 100644 --- a/voxygen/src/ui/ice/widget/mouse_detector.rs +++ b/voxygen/src/ui/ice/widget/mouse_detector.rs @@ -48,9 +48,9 @@ where event: Event, layout: Layout<'_>, _cursor_position: Point, - _messages: &mut Vec, _renderer: &R, - _clipboard: Option<&dyn Clipboard>, + _clipboard: &mut dyn Clipboard, + _messages: &mut Vec, ) -> iced::event::Status { if let Event::Mouse(mouse::Event::CursorMoved { position: Point { x, y }, diff --git a/voxygen/src/ui/ice/widget/overlay.rs b/voxygen/src/ui/ice/widget/overlay.rs index c9b1ff66b9..0bcd0d3833 100644 --- a/voxygen/src/ui/ice/widget/overlay.rs +++ b/voxygen/src/ui/ice/widget/overlay.rs @@ -1,6 +1,6 @@ use iced::{ - layout, mouse, Align, Clipboard, Element, Event, Hasher, Layout, Length, Point, Rectangle, - Size, Widget, + layout, mouse, Align, Clipboard, Element, Event, Hasher, Layout, Length, Padding, Point, + Rectangle, Size, Widget, }; use std::hash::Hash; @@ -10,7 +10,7 @@ use std::hash::Hash; /// the front widget /// Alignment and padding is used for the front widget pub struct Overlay<'a, M, R: self::Renderer> { - padding: u16, + padding: Padding, width: Length, height: Length, max_width: u32, @@ -32,7 +32,7 @@ where U: Into>, { Self { - padding: 0, + padding: Padding::ZERO, width: Length::Shrink, height: Length::Shrink, max_width: u32::MAX, @@ -44,8 +44,8 @@ where } } - pub fn padding(mut self, pad: u16) -> Self { - self.padding = pad; + pub fn padding>(mut self, pad: P) -> Self { + self.padding = pad.into(); self } @@ -99,8 +99,6 @@ where fn height(&self) -> Length { self.height } fn layout(&self, renderer: &R, limits: &layout::Limits) -> layout::Node { - let padding = self.padding as f32; - let limits = limits .loose() .max_width(self.max_width) @@ -111,16 +109,22 @@ where let under = self.under.layout(renderer, &limits.loose()); let under_size = under.size(); - let limits = limits.pad(padding); + let limits = limits.pad(self.padding); let mut over = self.over.layout(renderer, &limits.loose()); let over_size = over.size(); - let size = limits.resolve(Size { - width: under_size.width.max(over_size.width + padding * 2.0), - height: under_size.height.max(over_size.height + padding * 2.0), - }); + let size = limits.resolve( + Size { + width: under_size.width.max(over_size.width), + height: under_size.height.max(over_size.height), + } + .pad(self.padding), + ); - over.move_to(Point::new(padding, padding)); + over.move_to(Point::new( + self.padding.left.into(), + self.padding.top.into(), + )); over.align(self.horizontal_alignment, self.vertical_alignment, size); layout::Node::with_children(size, vec![over, under]) @@ -131,9 +135,9 @@ where event: Event, layout: Layout<'_>, cursor_position: Point, - messages: &mut Vec, renderer: &R, - clipboard: Option<&dyn Clipboard>, + clipboard: &mut dyn Clipboard, + messages: &mut Vec, ) -> iced::event::Status { let mut children = layout.children(); let over_layout = children.next().unwrap(); @@ -143,9 +147,9 @@ where event.clone(), over_layout, cursor_position, - messages, renderer, clipboard, + messages, ); // If mouse press check if over the overlay widget before sending to under @@ -158,9 +162,9 @@ where event, children.next().unwrap(), cursor_position, - messages, renderer, clipboard, + messages, ) .merge(status) } else { diff --git a/voxygen/src/ui/ice/widget/tooltip.rs b/voxygen/src/ui/ice/widget/tooltip.rs index 2e616a1f85..7ef1e58a42 100644 --- a/voxygen/src/ui/ice/widget/tooltip.rs +++ b/voxygen/src/ui/ice/widget/tooltip.rs @@ -189,17 +189,17 @@ where event: Event, layout: Layout<'_>, cursor_position: Point, - messages: &mut Vec, renderer: &R, - clipboard: Option<&dyn Clipboard>, + clipboard: &mut dyn Clipboard, + messages: &mut Vec, ) -> iced::event::Status { self.content.on_event( event, layout, cursor_position, - messages, renderer, clipboard, + messages, ) } From f26de7bbe9450df5ada5e21fff1a89571328a62f Mon Sep 17 00:00:00 2001 From: donovanlank Date: Tue, 15 Jun 2021 22:01:39 -0700 Subject: [PATCH 079/180] lowered spin.ron base poise damage to 13 from 25 --- assets/common/abilities/sword/spin.ron | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/common/abilities/sword/spin.ron b/assets/common/abilities/sword/spin.ron index ac5cdee91f..faf3361b1c 100644 --- a/assets/common/abilities/sword/spin.ron +++ b/assets/common/abilities/sword/spin.ron @@ -3,7 +3,7 @@ SpinMelee( swing_duration: 0.4, recover_duration: 0.5, base_damage: 160, - base_poise_damage: 25, + base_poise_damage: 13, knockback: ( strength: 10.0, direction: Away), range: 3.5, damage_effect: None, From 345add91dbaf9a010b88591f2d00c72c9ed42c81 Mon Sep 17 00:00:00 2001 From: Imbris Date: Wed, 16 Jun 2021 01:36:14 -0400 Subject: [PATCH 080/180] Eliminate panics when .current_monitor() returns None --- voxygen/src/hud/settings_window/video.rs | 5 +- voxygen/src/window.rs | 73 ++++++++++++++++-------- 2 files changed, 50 insertions(+), 28 deletions(-) diff --git a/voxygen/src/hud/settings_window/video.rs b/voxygen/src/hud/settings_window/video.rs index c064da77de..0ce0cc2b0e 100644 --- a/voxygen/src/hud/settings_window/video.rs +++ b/voxygen/src/hud/settings_window/video.rs @@ -164,9 +164,8 @@ impl<'a> Widget for Video<'a> { .window .window() .current_monitor() - .unwrap() - .video_modes() - .collect(); + .map(|monitor| monitor.video_modes().collect()) + .unwrap_or_default(); State { ids: Ids::new(id_gen), diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index e72590f769..2dfe0a9c85 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -1130,7 +1130,10 @@ impl Window { pub fn is_fullscreen(&self) -> bool { self.fullscreen.enabled } - pub fn select_video_mode_rec( + /// Select a video mode that fits the specified requirements + /// Returns None if a matching video mode doesn't exist or if + /// the current monitor can't be retrieved + fn select_video_mode_rec( &self, resolution: [u16; 2], bit_depth: Option, @@ -1142,15 +1145,16 @@ impl Window { // if a previous iteration of this method filtered the available video modes for // the correct resolution already, load that value, otherwise filter it // in this iteration - let correct_res = correct_res.unwrap_or_else(|| { - self.window - .current_monitor() - .unwrap() + let correct_res = match correct_res { + Some(correct_res) => correct_res, + None => self + .window + .current_monitor()? .video_modes() .filter(|mode| mode.size().width == resolution[0] as u32) .filter(|mode| mode.size().height == resolution[1] as u32) - .collect() - }); + .collect(), + }; match bit_depth { // A bit depth is given @@ -1270,12 +1274,12 @@ impl Window { } } - pub fn select_video_mode( + fn select_video_mode( &self, resolution: [u16; 2], bit_depth: Option, refresh_rate: Option, - ) -> VideoMode { + ) -> Option { // (resolution, bit depth, refresh rate) represents a video mode // spec: as specified // max: maximum value available @@ -1285,24 +1289,34 @@ impl Window { // (spec, spec, max), (spec, max, spec) // (spec, max, max) // (max, max, max) - self.select_video_mode_rec(resolution, bit_depth, refresh_rate, None, None, None) - // if there is no video mode with the specified resolution, fall back to the video mode with max resolution, bit depth and refresh rate - .unwrap_or_else(|| { + match self.select_video_mode_rec(resolution, bit_depth, refresh_rate, None, None, None) { + Some(mode) => Some(mode), + // if there is no video mode with the specified resolution, + // fall back to the video mode with max resolution, bit depth and refresh rate + None => { warn!( "Resolution specified in settings is incompatible with the monitor. Choosing \ highest resolution possible instead." ); + if let Some(monitor) = self.window.current_monitor() { + let mode = monitor + .video_modes() + // Prefer bit depth over refresh rate + .sorted_by_key(|mode| mode.refresh_rate()) + .sorted_by_key(|mode| mode.bit_depth()) + .max_by_key(|mode| mode.size().width); - self - .window - .current_monitor().unwrap() - .video_modes() - // Prefer bit depth over refresh rate - .sorted_by_key(|mode| mode.refresh_rate()) - .sorted_by_key(|mode| mode.bit_depth()) - .max_by_key(|mode| mode.size().width) - .expect("No video modes available!!") - }) + if mode.is_none() { + warn!("Failed to select video mode, no video modes available!!") + } + + mode + } else { + warn!("Failed to select video mode, can't get the current monitor!"); + None + } + }, + } } pub fn set_fullscreen_mode(&mut self, fullscreen: FullScreenSettings) { @@ -1310,14 +1324,23 @@ impl Window { self.fullscreen = fullscreen; window.set_fullscreen(fullscreen.enabled.then(|| match fullscreen.mode { FullscreenMode::Exclusive => { - winit::window::Fullscreen::Exclusive(self.select_video_mode( + if let Some(video_mode) = self.select_video_mode( fullscreen.resolution, fullscreen.bit_depth, fullscreen.refresh_rate, - )) + ) { + winit::window::Fullscreen::Exclusive(video_mode) + } else { + warn!( + "Failed to select a video mode for exclusive fullscreen. Falling back to \ + borderless fullscreen." + ); + winit::window::Fullscreen::Borderless(None) + } }, FullscreenMode::Borderless => { - winit::window::Fullscreen::Borderless(window.current_monitor()) + // None here will fullscreen on the current monitor + winit::window::Fullscreen::Borderless(None) }, })); } From 8f429d532a1f58540f1f63a7611dd5e8752b92bb Mon Sep 17 00:00:00 2001 From: Imbris Date: Tue, 15 Jun 2021 23:18:25 -0400 Subject: [PATCH 081/180] Add note on the reasoning for the visual position interpolation rate value in voxygen --- voxygen/src/ecs/sys/interpolation.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/voxygen/src/ecs/sys/interpolation.rs b/voxygen/src/ecs/sys/interpolation.rs index 97dba3a071..9abe8af9c5 100644 --- a/voxygen/src/ecs/sys/interpolation.rs +++ b/voxygen/src/ecs/sys/interpolation.rs @@ -43,7 +43,11 @@ impl<'a> System<'a> for Sys { { // Update interpolation values, but don't interpolate far things or objects if i.pos.distance_squared(pos.0) < 64.0 * 64.0 && !matches!(body, Body::Object(_)) { - i.pos = Lerp::lerp(i.pos, pos.0 + vel.0 * 0.03, 10.0 * dt.0); + // Note, these values are specifically tuned for smoother motion with high + // network latency or low network sampling rate and for smooth + // block hopping (which is instantaneous) + const POS_LERP_RATE_FACTOR: f32 = 10.0; + i.pos = Lerp::lerp(i.pos, pos.0 + vel.0 * 0.03, POS_LERP_RATE_FACTOR * dt.0); i.ori = Ori::slerp(i.ori, *ori, base_ori_interp(body) * dt.0); } else { i.pos = pos.0; From 09a914aa843f3f2ea908c965e40bcf69c4342506 Mon Sep 17 00:00:00 2001 From: Imbris Date: Tue, 15 Jun 2021 23:28:04 -0400 Subject: [PATCH 082/180] Log format used for the swapchain --- voxygen/src/render/renderer.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index d5e9f2157d..d94fb5be73 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -244,6 +244,7 @@ impl Renderer { let format = adapter .get_swap_chain_preferred_format(&surface) .expect("No supported swap chain format found"); + info!("Using {:?} as the swapchain format", format); let sc_desc = wgpu::SwapChainDescriptor { usage: wgpu::TextureUsage::RENDER_ATTACHMENT, From 19f741b33fe7cc959b2af4eab7968e4cba671014 Mon Sep 17 00:00:00 2001 From: Imbris Date: Wed, 16 Jun 2021 00:10:33 -0400 Subject: [PATCH 083/180] Show the graphics backend in the hud debug info and include the adapter info when panicking in the wgpu error handler --- voxygen/src/hud/mod.rs | 12 +++++++++++ voxygen/src/render/renderer.rs | 37 +++++++++++++++++++++------------- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 99710dda72..a012c63847 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -239,6 +239,7 @@ widget_ids! { num_lights, num_figures, num_particles, + graphics_backend, gpu_timings[], // Game Version @@ -2211,6 +2212,17 @@ impl Hud { .font_size(self.fonts.cyri.scale(14)) .set(self.ids.num_particles, ui_widgets); + // Graphics backend + Text::new(&format!( + "Graphics backend: {}", + global_state.window.renderer().graphics_backend(), + )) + .color(TEXT_COLOR) + .down_from(self.ids.num_particles, 5.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .set(self.ids.graphics_backend, ui_widgets); + // GPU timing for different pipelines let gpu_timings = global_state.window.renderer().timings(); if !gpu_timings.is_empty() { diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index d94fb5be73..8f2c3e8b94 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -139,6 +139,9 @@ pub struct Renderer { // This checks is added because windows resizes the window to 0,0 when // minimizing and this causes a bunch of validation errors is_minimized: bool, + + // To remember the backend info after initialization for debug purposes + graphics_backend: String, } impl Renderer { @@ -188,6 +191,17 @@ impl Renderer { )) .ok_or(RenderError::CouldNotFindAdapter)?; + let info = adapter.get_info(); + info!( + ?info.name, + ?info.vendor, + ?info.backend, + ?info.device, + ?info.device_type, + "selected graphics device" + ); + let graphics_backend = format!("{:?}", &info.backend); + let limits = wgpu::Limits { max_push_constant_size: 64, ..Default::default() @@ -213,12 +227,12 @@ impl Renderer { // Set error handler for wgpu errors // This is better for use than their default because it includes the error in // the panic message - device.on_uncaptured_error(|error| { + device.on_uncaptured_error(move |error| { error!("{}", &error); panic!( - "wgpu error (handling all wgpu errors as fatal): {:?}", - &error, - ) + "wgpu error (handling all wgpu errors as fatal):\n{:?}\n{:?}", + &error, &info, + ); }); let profiler_features_enabled = device @@ -231,16 +245,6 @@ impl Renderer { ); } - let info = adapter.get_info(); - info!( - ?info.name, - ?info.vendor, - ?info.backend, - ?info.device, - ?info.device_type, - "selected graphics device" - ); - let format = adapter .get_swap_chain_preferred_format(&surface) .expect("No supported swap chain format found"); @@ -402,9 +406,14 @@ impl Renderer { profiler_features_enabled, is_minimized: false, + + graphics_backend, }) } + /// Get the graphics backend being used + pub fn graphics_backend(&self) -> &str { &self.graphics_backend } + /// Check the status of the intial pipeline creation /// Returns `None` if complete /// Returns `Some((total, complete))` if in progress From 2d7b82c2f4622807c249c0f8aac07a4a32c9f0cf Mon Sep 17 00:00:00 2001 From: Imbris Date: Wed, 16 Jun 2021 00:24:47 -0400 Subject: [PATCH 084/180] Do some checks to make sure WGPU_TRACE_DIR is useable if set and exit early if it isn't to avoid the user not being aware that they are failing to collect a trace --- voxygen/src/render/renderer.rs | 57 ++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index 8f2c3e8b94..06cf8b6386 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -207,22 +207,47 @@ impl Renderer { ..Default::default() }; - let (device, queue) = futures_executor::block_on( - adapter.request_device( - &wgpu::DeviceDescriptor { - // TODO - label: None, - features: wgpu::Features::DEPTH_CLAMPING - | wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER - | wgpu::Features::PUSH_CONSTANTS - | (adapter.features() & wgpu_profiler::GpuProfiler::REQUIRED_WGPU_FEATURES), - limits, - }, - std::env::var_os("WGPU_TRACE_DIR") - .as_ref() - .map(|v| std::path::Path::new(v)), - ), - )?; + let (device, queue) = futures_executor::block_on(adapter.request_device( + &wgpu::DeviceDescriptor { + // TODO + label: None, + features: wgpu::Features::DEPTH_CLAMPING + | wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER + | wgpu::Features::PUSH_CONSTANTS + | (adapter.features() & wgpu_profiler::GpuProfiler::REQUIRED_WGPU_FEATURES), + limits, + }, + std::env::var_os("WGPU_TRACE_DIR").as_ref().map(|v| { + let path = std::path::Path::new(v); + // We don't want to continue if we can't actually collect the api trace + if !path.exists() { + panic!( + "WGPU_TRACE_DIR is set to the path \"{}\" which doesn't exist", + path.display() + ); + } + if !path.is_dir() { + panic!( + "WGPU_TRACE_DIR is set to the path \"{}\" which is not a directory", + path.display() + ); + } + if path + .read_dir() + .expect("Could not read the directory that is specified by WGPU_TRACE_DIR") + .next() + .is_some() + { + panic!( + "WGPU_TRACE_DIR is set to the path \"{}\" which already contains other \ + files", + path.display() + ); + } + + path + }), + ))?; // Set error handler for wgpu errors // This is better for use than their default because it includes the error in From 1483c2be7458b31525a95e4583431809e617bfd1 Mon Sep 17 00:00:00 2001 From: Imbris Date: Wed, 16 Jun 2021 00:39:26 -0400 Subject: [PATCH 085/180] Reset the slot 1 bind group to shadows when dropping the DebugDrawer --- voxygen/src/render/renderer/drawer.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/voxygen/src/render/renderer/drawer.rs b/voxygen/src/render/renderer/drawer.rs index 7e25c349d2..a41a1df1a7 100644 --- a/voxygen/src/render/renderer/drawer.rs +++ b/voxygen/src/render/renderer/drawer.rs @@ -5,7 +5,7 @@ use super::{ model::{DynamicModel, Model, SubModel}, pipelines::{ blit, clouds, debug, figure, fluid, lod_terrain, particle, shadow, skybox, sprite, - terrain, ui, ColLights, GlobalsBindGroup, + terrain, ui, ColLights, GlobalsBindGroup, ShadowTexturesBindGroup, }, }, Renderer, ShadowMap, ShadowMapRenderer, @@ -198,6 +198,7 @@ impl<'frame> Drawer<'frame> { borrow: &self.borrow, pipelines, globals: self.globals, + shadows: &shadow.bind, }) } @@ -522,6 +523,7 @@ pub struct FirstPassDrawer<'pass> { borrow: &'pass RendererBorrow<'pass>, pipelines: &'pass super::Pipelines, globals: &'pass GlobalsBindGroup, + shadows: &'pass ShadowTexturesBindGroup, } impl<'pass> FirstPassDrawer<'pass> { @@ -540,7 +542,10 @@ impl<'pass> FirstPassDrawer<'pass> { render_pass.set_pipeline(&self.pipelines.debug.pipeline); set_quad_index_buffer::(&mut render_pass, &self.borrow); - DebugDrawer { render_pass } + DebugDrawer { + render_pass, + shadows: self.shadows, + } } pub fn draw_lod_terrain<'data: 'pass>(&mut self, model: &'data Model) { @@ -613,6 +618,7 @@ impl<'pass> FirstPassDrawer<'pass> { pub struct DebugDrawer<'pass_ref, 'pass: 'pass_ref> { render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>, + shadows: &'pass ShadowTexturesBindGroup, } impl<'pass_ref, 'pass: 'pass_ref> DebugDrawer<'pass_ref, 'pass> { @@ -627,6 +633,14 @@ impl<'pass_ref, 'pass: 'pass_ref> DebugDrawer<'pass_ref, 'pass> { } } +impl<'pass_ref, 'pass: 'pass_ref> Drop for DebugDrawer<'pass_ref, 'pass> { + fn drop(&mut self) { + // Maintain that the shadow bind group is set in + // slot 1 by default during the main pass + self.render_pass + .set_bind_group(1, &self.shadows.bind_group, &[]); + } +} pub struct FigureDrawer<'pass_ref, 'pass: 'pass_ref> { render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>, } From cb55676bb959d6fee239c1a0bd4f2586020262f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Capucho?= Date: Wed, 16 Jun 2021 14:07:23 +0100 Subject: [PATCH 086/180] poll device to cleanup resources --- voxygen/src/lib.rs | 1 + voxygen/src/render/renderer.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/voxygen/src/lib.rs b/voxygen/src/lib.rs index 7532eb903d..f975e0bbed 100644 --- a/voxygen/src/lib.rs +++ b/voxygen/src/lib.rs @@ -80,6 +80,7 @@ impl GlobalState { pub fn maintain(&mut self, dt: std::time::Duration) { span!(_guard, "maintain", "GlobalState::maintain"); self.audio.maintain(dt); + self.window.renderer().maintain() } #[cfg(feature = "singleplayer")] diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index 06cf8b6386..f917a83e2a 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -600,6 +600,8 @@ impl Renderer { Ok(()) } + pub fn maintain(&self) { self.device.poll(wgpu::Maintain::Poll) } + /// Create render target views fn create_rt_views( device: &wgpu::Device, From 559311e1b0b2945be00fce8044fc6d1b63eeba99 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 15 Jun 2021 17:15:58 +0100 Subject: [PATCH 087/180] Chasing sounds --- assets/voxygen/audio/sfx.ron | 6 ++++++ .../audio/sfx/utterance/ogre_angry.ogg | 3 +++ .../audio/sfx/utterance/ogre_angry2.ogg | 3 +++ .../audio/sfx/utterance/ogre_angry3.ogg | 3 +++ .../audio/sfx/utterance/wendigo_angry.mp3 | Bin 0 -> 64307 bytes .../audio/sfx/utterance/wendigo_angry.ogg | 3 +++ common/src/comp/agent.rs | 7 +++--- common/src/comp/controller.rs | 8 +++++++ common/src/comp/mod.rs | 2 +- common/src/outcome.rs | 10 +++++++-- common/systems/src/controller.rs | 20 ++++++++++++++++-- server/src/events/interaction.rs | 17 ++++++++++++++- server/src/sys/agent.rs | 6 +++++- voxygen/src/audio/sfx/mod.rs | 6 ++++++ voxygen/src/scene/particle.rs | 3 ++- 15 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 assets/voxygen/audio/sfx/utterance/ogre_angry.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/ogre_angry3.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/wendigo_angry.mp3 create mode 100644 assets/voxygen/audio/sfx/utterance/wendigo_angry.ogg diff --git a/assets/voxygen/audio/sfx.ron b/assets/voxygen/audio/sfx.ron index bd9434f765..3eb3a6d828 100644 --- a/assets/voxygen/audio/sfx.ron +++ b/assets/voxygen/audio/sfx.ron @@ -831,5 +831,11 @@ ], threshold: 0.2, ), + Utterance(Angry, BipedLarge((species: Wendigo, body_type: Female))): ( + files: [ + "voxygen.audio.sfx.utterance.wendigo_angry", + ], + threshold: 4.0, + ), } ) diff --git a/assets/voxygen/audio/sfx/utterance/ogre_angry.ogg b/assets/voxygen/audio/sfx/utterance/ogre_angry.ogg new file mode 100644 index 0000000000..819348a228 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/ogre_angry.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78f705397c01f73fd2bfad14f0aa3949a5409556cd5f004c185dea358fbd9701 +size 34809 diff --git a/assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg b/assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg new file mode 100644 index 0000000000..65c67e368c --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:83bbec96fb1192ac5d5c0907062242c5c37ba7f8e6f330556ee22e29423f14c6 +size 53337 diff --git a/assets/voxygen/audio/sfx/utterance/ogre_angry3.ogg b/assets/voxygen/audio/sfx/utterance/ogre_angry3.ogg new file mode 100644 index 0000000000..f4080ed7c2 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/ogre_angry3.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f4ad549122e5fec0722260c11309079e99e20ec9d42be3655a44a416027d3f9 +size 55056 diff --git a/assets/voxygen/audio/sfx/utterance/wendigo_angry.mp3 b/assets/voxygen/audio/sfx/utterance/wendigo_angry.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..87d5aec70f739eaa729bacb12fcb87c9a4f950a6 GIT binary patch literal 64307 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 literal 0 HcmV?d00001 diff --git a/assets/voxygen/audio/sfx/utterance/wendigo_angry.ogg b/assets/voxygen/audio/sfx/utterance/wendigo_angry.ogg new file mode 100644 index 0000000000..4c02e0bd36 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/wendigo_angry.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cfbac68b83de452e7be824075ee9104559ca96a9a710be303c89ede0ab4a6a03 +size 42373 diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index 8b792f361f..7c64f62fca 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -1,5 +1,5 @@ use crate::{ - comp::{humanoid, quadruped_low, quadruped_medium, quadruped_small, ship, Body}, + comp::{humanoid, quadruped_low, quadruped_medium, quadruped_small, ship, Body, UtteranceKind}, path::Chaser, rtsim::RtSimController, trade::{PendingTrade, ReducedInventory, SiteId, SitePrices, TradeId, TradeResult}, @@ -96,7 +96,7 @@ bitflags::bitflags! { /// # Behavior Component /// This component allow an Entity to register one or more behavior tags. -/// These tags act as flags of what an Entity can do, or what it is doing. +/// These tags act as flags of what an Entity can do, or what it is doing. /// Behaviors Tags can be added and removed as the Entity lives, to update its /// state when needed #[derive(Default, Copy, Clone, Debug)] @@ -117,7 +117,7 @@ impl From for Behavior { } impl Behavior { - /// Builder function + /// Builder function /// Set capabilities if Option is Some pub fn maybe_with_capabilities( mut self, @@ -297,6 +297,7 @@ pub enum SoundKind { Explosion, Beam, Shockwave, + Utterance(UtteranceKind, Body), } #[derive(Clone, Debug)] diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index 9e13b4e327..d157b87853 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -96,6 +96,13 @@ pub enum GroupManip { AssignLeader(Uid), } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum UtteranceKind { + Calm, + Angry, + Surprised, +} + #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum ControlEvent { //ToggleLantern, @@ -111,6 +118,7 @@ pub enum ControlEvent { GroupManip(GroupManip), RemoveBuff(BuffKind), Respawn, + Utterance(UtteranceKind), } #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 8b6b775349..c8998b4a7d 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -67,7 +67,7 @@ pub use self::{ combo::Combo, controller::{ Climb, ControlAction, ControlEvent, Controller, ControllerInputs, GroupManip, InputAttr, - InputKind, InventoryAction, InventoryEvent, InventoryManip, MountState, Mounting, + InputKind, InventoryAction, InventoryEvent, InventoryManip, MountState, Mounting, UtteranceKind, }, energy::{Energy, EnergyChange, EnergySource}, fluid_dynamics::Fluid, diff --git a/common/src/outcome.rs b/common/src/outcome.rs index d1ec35e15c..feeb1e8fec 100644 --- a/common/src/outcome.rs +++ b/common/src/outcome.rs @@ -1,5 +1,5 @@ use crate::{comp, uid::Uid}; -use comp::{beam, item::Reagent, poise::PoiseState, skills::SkillGroupKind}; +use comp::{beam, item::Reagent, poise::PoiseState, skills::SkillGroupKind, UtteranceKind}; use hashbrown::HashSet; use serde::{Deserialize, Serialize}; use vek::*; @@ -73,6 +73,11 @@ pub enum Outcome { GroundSlam { pos: Vec3, }, + Utterance { + pos: Vec3, + body: comp::Body, + kind: UtteranceKind, + }, } impl Outcome { @@ -87,7 +92,8 @@ impl Outcome { | Outcome::Damage { pos, .. } | Outcome::Block { pos, .. } | Outcome::PoiseChange { pos, .. } - | Outcome::GroundSlam { pos } => Some(*pos), + | Outcome::GroundSlam { pos } + | Outcome::Utterance { pos, .. } => Some(*pos), Outcome::BreakBlock { pos, .. } => Some(pos.map(|e| e as f32 + 0.5)), Outcome::ExpChange { .. } | Outcome::ComboChange { .. } => None, } diff --git a/common/systems/src/controller.rs b/common/systems/src/controller.rs index 42b448ad80..daeb8f36e3 100644 --- a/common/systems/src/controller.rs +++ b/common/systems/src/controller.rs @@ -1,5 +1,5 @@ use common::{ - comp::{BuffChange, ControlEvent, Controller}, + comp::{BuffChange, ControlEvent, Controller, Pos, Body, agent::{Sound, SoundKind}}, event::{EventBus, ServerEvent}, uid::UidAllocator, }; @@ -7,7 +7,7 @@ use common_ecs::{Job, Origin, Phase, System}; use specs::{ saveload::{Marker, MarkerAllocator}, shred::ResourceId, - Entities, Join, Read, SystemData, World, WriteStorage, + Entities, Join, Read, SystemData, World, WriteStorage, ReadStorage, }; use vek::*; @@ -16,6 +16,8 @@ pub struct ReadData<'a> { entities: Entities<'a>, uid_allocator: Read<'a, UidAllocator>, server_bus: Read<'a, EventBus>, + positions: ReadStorage<'a, Pos>, + bodies: ReadStorage<'a, Body>, } #[derive(Default)] @@ -92,6 +94,20 @@ impl<'a> System<'a> for Sys { server_emitter.emit(ServerEvent::GroupManip(entity, manip)) }, ControlEvent::Respawn => server_emitter.emit(ServerEvent::Respawn(entity)), + ControlEvent::Utterance(kind) => { + if let (Some(pos), Some(body)) = ( + read_data.positions.get(entity), + read_data.bodies.get(entity), + ) { + let sound = Sound::new( + SoundKind::Utterance(kind, *body), + pos.0, + 8.0, // TODO: Come up with a better way of determining this + 1.0, + ); + server_emitter.emit(ServerEvent::Sound { sound }); + } + }, } } } diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index 03b5fe992a..f3b380f231 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -6,7 +6,7 @@ use common::{ assets, comp::{ self, - agent::{AgentEvent, Sound, MAX_LISTEN_DIST}, + agent::{AgentEvent, Sound, MAX_LISTEN_DIST, SoundKind}, dialogue::Subject, inventory::slot::EquipSlot, item, @@ -386,6 +386,7 @@ pub fn handle_sound(server: &mut Server, sound: &Sound) { let positions = &ecs.read_storage::(); let agents = &mut ecs.write_storage::(); + // TODO: Reduce the complexity of this problem by using spatial partitioning system for (agent, agent_pos) in (agents, positions).join() { // TODO: Use pathfinding for more dropoff around obstacles let agent_dist_sqrd = agent_pos.0.distance_squared(sound.pos); @@ -402,5 +403,19 @@ pub fn handle_sound(server: &mut Server, sound: &Sound) { .inbox .push_back(AgentEvent::ServerSound(propagated_sound)); } + + // Attempt to turn this sound into an outcome to be received by frontends. + if let Some(outcome) = match sound.kind { + SoundKind::Utterance(kind, body) => Some(Outcome::Utterance { + kind, + pos: sound.pos, + body, + }), + _ => None, + } { + ecs + .write_resource::>() + .push(outcome); + } } } diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index addd351b2f..67dc39634d 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -21,7 +21,7 @@ use common::{ Agent, Alignment, BehaviorCapability, BehaviorState, Body, CharacterAbility, CharacterState, ControlAction, ControlEvent, Controller, Energy, Health, HealthChange, InputKind, Inventory, InventoryAction, LightEmitter, MountState, Ori, PhysicsState, Pos, - Scale, SkillSet, Stats, UnresolvedChatMsg, Vel, + Scale, SkillSet, Stats, UnresolvedChatMsg, Vel, UtteranceKind, }, consts::GRAVITY, effect::{BuffEffect, Effect}, @@ -1548,6 +1548,10 @@ impl<'a> AgentData<'a> { .min_by_key(|(_, e_pos, _, _, _, _, _)| (e_pos.0.distance_squared(self.pos.0) * 100.0) as i32) // TODO choose target by more than just distance .map(|(e, _, _, _, _, _, _)| e); + if agent.target.is_none() && target.is_some() { + controller.push_event(ControlEvent::Utterance(UtteranceKind::Angry)); + } + agent.target = target.map(|target| Target { target, hostile: true, diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index f0ab9e4a55..57198031ed 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -96,6 +96,7 @@ use common::{ object, poise::PoiseState, Body, CharacterAbilityType, InventoryUpdateEvent, + UtteranceKind, }, outcome::Outcome, terrain::{BlockKind, TerrainChunk}, @@ -182,6 +183,7 @@ pub enum SfxEvent { FlameThrower, PoiseChange(PoiseState), GroundSlam, + Utterance(UtteranceKind, Body), } #[derive(Clone, Debug, PartialEq, Deserialize, Hash, Eq)] @@ -452,6 +454,10 @@ impl SfxMgr { audio.emit_sfx(sfx_trigger_item, *pos, None, false); }, }, + Outcome::Utterance { pos, kind, body } => { + let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Utterance(*kind, *body)); + audio.emit_sfx(sfx_trigger_item, *pos, None, false); + }, Outcome::ExpChange { .. } | Outcome::ComboChange { .. } | Outcome::SummonedCreature { .. } => {}, diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index 76bd714891..1b70bd660f 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -248,7 +248,8 @@ impl ParticleMgr { | Outcome::SkillPointGain { .. } | Outcome::ComboChange { .. } | Outcome::Damage { .. } - | Outcome::PoiseChange { .. } => {}, + | Outcome::PoiseChange { .. } + | Outcome::Utterance { .. } => {}, } } From 0ef29a6989162bbee3dfc9184acb514078571acb Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 15 Jun 2021 17:57:52 +0100 Subject: [PATCH 088/180] VoiceKind for better sound effect specs --- assets/voxygen/audio/sfx.ron | 15 +++++++- .../audio/sfx/utterance/ogre_angry2.ogg | 4 +-- .../audio/sfx/utterance/ogre_angry3.ogg | 3 -- .../audio/sfx/utterance/saurok_angry.ogg | 3 ++ .../audio/sfx/utterance/wendigo_angry.mp3 | Bin 64307 -> 0 bytes common/src/comp/mod.rs | 3 +- common/systems/src/controller.rs | 7 ++-- server/src/events/interaction.rs | 9 +++-- server/src/sys/agent.rs | 2 +- voxygen/src/audio/sfx/mod.rs | 34 ++++++++++++++---- 10 files changed, 59 insertions(+), 21 deletions(-) delete mode 100644 assets/voxygen/audio/sfx/utterance/ogre_angry3.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/saurok_angry.ogg delete mode 100644 assets/voxygen/audio/sfx/utterance/wendigo_angry.mp3 diff --git a/assets/voxygen/audio/sfx.ron b/assets/voxygen/audio/sfx.ron index 3eb3a6d828..cecb6f25d0 100644 --- a/assets/voxygen/audio/sfx.ron +++ b/assets/voxygen/audio/sfx.ron @@ -831,11 +831,24 @@ ], threshold: 0.2, ), - Utterance(Angry, BipedLarge((species: Wendigo, body_type: Female))): ( + Utterance(Angry, Wendigo): ( files: [ "voxygen.audio.sfx.utterance.wendigo_angry", ], threshold: 4.0, ), + Utterance(Angry, BipedLarge): ( + files: [ + "voxygen.audio.sfx.utterance.ogre_angry", + "voxygen.audio.sfx.utterance.ogre_angry2", + ], + threshold: 4.0, + ), + Utterance(Angry, Saurok): ( + files: [ + "voxygen.audio.sfx.utterance.saurok_angry", + ], + threshold: 4.0, + ), } ) diff --git a/assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg b/assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg index 65c67e368c..da49817cca 100644 --- a/assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg +++ b/assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:83bbec96fb1192ac5d5c0907062242c5c37ba7f8e6f330556ee22e29423f14c6 -size 53337 +oid sha256:5c97dcb8f2ff2543a041d07147c04a74b59e43421dd7452eccb095a947bd7466 +size 52973 diff --git a/assets/voxygen/audio/sfx/utterance/ogre_angry3.ogg b/assets/voxygen/audio/sfx/utterance/ogre_angry3.ogg deleted file mode 100644 index f4080ed7c2..0000000000 --- a/assets/voxygen/audio/sfx/utterance/ogre_angry3.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9f4ad549122e5fec0722260c11309079e99e20ec9d42be3655a44a416027d3f9 -size 55056 diff --git a/assets/voxygen/audio/sfx/utterance/saurok_angry.ogg b/assets/voxygen/audio/sfx/utterance/saurok_angry.ogg new file mode 100644 index 0000000000..a550c1bbe2 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/saurok_angry.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f859d590ac38bac163f0821eec8e2157f4f021d34d59575ccca2aeedde919ce +size 56816 diff --git a/assets/voxygen/audio/sfx/utterance/wendigo_angry.mp3 b/assets/voxygen/audio/sfx/utterance/wendigo_angry.mp3 deleted file mode 100644 index 87d5aec70f739eaa729bacb12fcb87c9a4f950a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64307 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 diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index c8998b4a7d..2242ad236e 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -67,7 +67,8 @@ pub use self::{ combo::Combo, controller::{ Climb, ControlAction, ControlEvent, Controller, ControllerInputs, GroupManip, InputAttr, - InputKind, InventoryAction, InventoryEvent, InventoryManip, MountState, Mounting, UtteranceKind, + InputKind, InventoryAction, InventoryEvent, InventoryManip, MountState, Mounting, + UtteranceKind, }, energy::{Energy, EnergyChange, EnergySource}, fluid_dynamics::Fluid, diff --git a/common/systems/src/controller.rs b/common/systems/src/controller.rs index daeb8f36e3..c576e22dc6 100644 --- a/common/systems/src/controller.rs +++ b/common/systems/src/controller.rs @@ -1,5 +1,8 @@ use common::{ - comp::{BuffChange, ControlEvent, Controller, Pos, Body, agent::{Sound, SoundKind}}, + comp::{ + agent::{Sound, SoundKind}, + Body, BuffChange, ControlEvent, Controller, Pos, + }, event::{EventBus, ServerEvent}, uid::UidAllocator, }; @@ -7,7 +10,7 @@ use common_ecs::{Job, Origin, Phase, System}; use specs::{ saveload::{Marker, MarkerAllocator}, shred::ResourceId, - Entities, Join, Read, SystemData, World, WriteStorage, ReadStorage, + Entities, Join, Read, ReadStorage, SystemData, World, WriteStorage, }; use vek::*; diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index f3b380f231..3a04f63aec 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -6,7 +6,7 @@ use common::{ assets, comp::{ self, - agent::{AgentEvent, Sound, MAX_LISTEN_DIST, SoundKind}, + agent::{AgentEvent, Sound, SoundKind, MAX_LISTEN_DIST}, dialogue::Subject, inventory::slot::EquipSlot, item, @@ -386,7 +386,8 @@ pub fn handle_sound(server: &mut Server, sound: &Sound) { let positions = &ecs.read_storage::(); let agents = &mut ecs.write_storage::(); - // TODO: Reduce the complexity of this problem by using spatial partitioning system + // TODO: Reduce the complexity of this problem by using spatial partitioning + // system for (agent, agent_pos) in (agents, positions).join() { // TODO: Use pathfinding for more dropoff around obstacles let agent_dist_sqrd = agent_pos.0.distance_squared(sound.pos); @@ -413,9 +414,7 @@ pub fn handle_sound(server: &mut Server, sound: &Sound) { }), _ => None, } { - ecs - .write_resource::>() - .push(outcome); + ecs.write_resource::>().push(outcome); } } } diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 67dc39634d..b0b3d88e34 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -21,7 +21,7 @@ use common::{ Agent, Alignment, BehaviorCapability, BehaviorState, Body, CharacterAbility, CharacterState, ControlAction, ControlEvent, Controller, Energy, Health, HealthChange, InputKind, Inventory, InventoryAction, LightEmitter, MountState, Ori, PhysicsState, Pos, - Scale, SkillSet, Stats, UnresolvedChatMsg, Vel, UtteranceKind, + Scale, SkillSet, Stats, UnresolvedChatMsg, UtteranceKind, Vel, }, consts::GRAVITY, effect::{BuffEffect, Effect}, diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index 57198031ed..782825d0a4 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -91,12 +91,11 @@ use client::Client; use common::{ assets::{self, AssetExt, AssetHandle}, comp::{ - beam, + beam, biped_large, item::{ItemKind, ToolKind}, object, poise::PoiseState, - Body, CharacterAbilityType, InventoryUpdateEvent, - UtteranceKind, + Body, CharacterAbilityType, InventoryUpdateEvent, UtteranceKind, }, outcome::Outcome, terrain::{BlockKind, TerrainChunk}, @@ -183,7 +182,29 @@ pub enum SfxEvent { FlameThrower, PoiseChange(PoiseState), GroundSlam, - Utterance(UtteranceKind, Body), + Utterance(UtteranceKind, VoiceKind), +} + +#[derive(Clone, Debug, PartialEq, Deserialize, Hash, Eq)] +pub enum VoiceKind { + Mute, + Human, + BipedLarge, + Wendigo, + Saurok, +} + +fn body_to_voice(body: &Body) -> VoiceKind { + match body { + Body::BipedLarge(body) => match body.species { + biped_large::Species::Wendigo => VoiceKind::Wendigo, + biped_large::Species::Occultsaurok + | biped_large::Species::Mightysaurok + | biped_large::Species::Slysaurok => VoiceKind::Saurok, + _ => VoiceKind::BipedLarge, + }, + _ => VoiceKind::Mute, + } } #[derive(Clone, Debug, PartialEq, Deserialize, Hash, Eq)] @@ -455,8 +476,9 @@ impl SfxMgr { }, }, Outcome::Utterance { pos, kind, body } => { - let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Utterance(*kind, *body)); - audio.emit_sfx(sfx_trigger_item, *pos, None, false); + let sfx_trigger_item = + triggers.get_key_value(&SfxEvent::Utterance(*kind, body_to_voice(body))); + audio.emit_sfx(sfx_trigger_item, *pos, Some(2.5), false); }, Outcome::ExpChange { .. } | Outcome::ComboChange { .. } From fb51fd3230ddfa7e24a4967b7e42d0cbc2a6fc10 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 15 Jun 2021 23:01:16 +0100 Subject: [PATCH 089/180] Emit sound from head --- common/src/comp/controller.rs | 1 + common/systems/src/controller.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index d157b87853..fafd138da2 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -101,6 +101,7 @@ pub enum UtteranceKind { Calm, Angry, Surprised, + Hurt, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] diff --git a/common/systems/src/controller.rs b/common/systems/src/controller.rs index c576e22dc6..2aea04b55a 100644 --- a/common/systems/src/controller.rs +++ b/common/systems/src/controller.rs @@ -104,7 +104,7 @@ impl<'a> System<'a> for Sys { ) { let sound = Sound::new( SoundKind::Utterance(kind, *body), - pos.0, + pos.0 + Vec3::unit_z() * body.eye_height(), 8.0, // TODO: Come up with a better way of determining this 1.0, ); From e3c44ba7fe4716622106d59cc991c2f8415d3d13 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 16 Jun 2021 13:49:43 +0100 Subject: [PATCH 090/180] More voice kinds, more opportunities for sounds --- assets/voxygen/audio/sfx.ron | 8 +++- .../audio/sfx/utterance/bird_angry.ogg | 3 ++ common/src/comp/controller.rs | 1 + server/src/sys/agent.rs | 6 +++ voxygen/src/audio/mod.rs | 4 +- voxygen/src/audio/sfx/mod.rs | 42 +++++++++++++++++-- 6 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 assets/voxygen/audio/sfx/utterance/bird_angry.ogg diff --git a/assets/voxygen/audio/sfx.ron b/assets/voxygen/audio/sfx.ron index cecb6f25d0..b4f3a0f5e8 100644 --- a/assets/voxygen/audio/sfx.ron +++ b/assets/voxygen/audio/sfx.ron @@ -844,11 +844,17 @@ ], threshold: 4.0, ), - Utterance(Angry, Saurok): ( + Utterance(Angry, Reptile): ( files: [ "voxygen.audio.sfx.utterance.saurok_angry", ], threshold: 4.0, ), + Utterance(Angry, Bird): ( + files: [ + "voxygen.audio.sfx.utterance.bird_angry", + ], + threshold: 4.0, + ), } ) diff --git a/assets/voxygen/audio/sfx/utterance/bird_angry.ogg b/assets/voxygen/audio/sfx/utterance/bird_angry.ogg new file mode 100644 index 0000000000..bd0c9a7e77 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/bird_angry.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e028570750b872652dc16dff1dd4cafe34ad568bc079bd8ba379a5ceac66942 +size 11508 diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index fafd138da2..c27a4c1c50 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -102,6 +102,7 @@ pub enum UtteranceKind { Angry, Surprised, Hurt, + Greeting, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index b0b3d88e34..dd77610f29 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -945,6 +945,10 @@ impl<'a> AgentData<'a> { controller.actions.push(ControlAction::Unwield); } + if thread_rng().gen_bool(0.001) { + controller.push_event(ControlEvent::Utterance(UtteranceKind::Calm)); + } + // Sit if thread_rng().gen::() < 0.0035 { controller.actions.push(ControlAction::Sit); @@ -990,6 +994,8 @@ impl<'a> AgentData<'a> { if self.look_toward(controller, read_data, &target) { controller.actions.push(ControlAction::Stand); controller.actions.push(ControlAction::Talk); + controller.push_event(ControlEvent::Utterance(UtteranceKind::Greeting)); + match subject { Subject::Regular => { if let ( diff --git a/voxygen/src/audio/mod.rs b/voxygen/src/audio/mod.rs index d6811f77ab..c0fb8dc175 100644 --- a/voxygen/src/audio/mod.rs +++ b/voxygen/src/audio/mod.rs @@ -209,7 +209,7 @@ impl AudioFrontend { // TODO: Should this take `underwater` into consideration? match self.play_sfx(sfx_file, self.listener.pos, None, false) { Ok(_) => {}, - Err(e) => warn!("Failed to play sfx. {}", e), + Err(e) => warn!("Failed to play sfx '{:?}'. {}", sfx_file, e), } } else { debug!("Missing sfx trigger config for external sfx event.",); @@ -244,7 +244,7 @@ impl AudioFrontend { match self.play_sfx(sfx_file, position, volume, underwater) { Ok(_) => {}, - Err(e) => warn!("Failed to play sfx. {}", e), + Err(e) => warn!("Failed to play sfx '{:?}'. {}", sfx_file, e), } } else { debug!( diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index 782825d0a4..c4a0905337 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -95,7 +95,8 @@ use common::{ item::{ItemKind, ToolKind}, object, poise::PoiseState, - Body, CharacterAbilityType, InventoryUpdateEvent, UtteranceKind, + quadruped_medium, quadruped_small, Body, CharacterAbilityType, InventoryUpdateEvent, + UtteranceKind, }, outcome::Outcome, terrain::{BlockKind, TerrainChunk}, @@ -191,18 +192,53 @@ pub enum VoiceKind { Human, BipedLarge, Wendigo, - Saurok, + Reptile, + Bird, + Critter, + Sheep, + Pig, + Cow, + Canine, + BigCat, } fn body_to_voice(body: &Body) -> VoiceKind { match body { + Body::Humanoid(_) => VoiceKind::Human, + Body::QuadrupedSmall(body) => match body.species { + quadruped_small::Species::Sheep => VoiceKind::Sheep, + quadruped_small::Species::Pig | quadruped_small::Species::Boar => VoiceKind::Pig, + _ => VoiceKind::Critter, + }, + Body::QuadrupedMedium(body) => match body.species { + quadruped_medium::Species::Saber + | quadruped_medium::Species::Tiger + | quadruped_medium::Species::Lion + | quadruped_medium::Species::Frostfang + | quadruped_medium::Species::Snowleopard => VoiceKind::BigCat, + quadruped_medium::Species::Wolf + | quadruped_medium::Species::Roshwalr + | quadruped_medium::Species::Tarasque + | quadruped_medium::Species::Darkhound + | quadruped_medium::Species::Bonerattler + | quadruped_medium::Species::Grolgar => VoiceKind::Canine, + quadruped_medium::Species::Cattle + | quadruped_medium::Species::Catoblepas + | quadruped_medium::Species::Highland + | quadruped_medium::Species::Yak + | quadruped_medium::Species::Moose + | quadruped_medium::Species::Dreadhorn => VoiceKind::Cow, + _ => VoiceKind::Mute, + }, + Body::BirdMedium(_) | Body::BirdLarge(_) => VoiceKind::Bird, Body::BipedLarge(body) => match body.species { biped_large::Species::Wendigo => VoiceKind::Wendigo, biped_large::Species::Occultsaurok | biped_large::Species::Mightysaurok - | biped_large::Species::Slysaurok => VoiceKind::Saurok, + | biped_large::Species::Slysaurok => VoiceKind::Reptile, _ => VoiceKind::BipedLarge, }, + Body::Theropod(_) | Body::Dragon(_) => VoiceKind::Reptile, _ => VoiceKind::Mute, } } From 9510869870504692e9f7b3c21ad9bec3a9a1eefa Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 16 Jun 2021 15:04:00 +0100 Subject: [PATCH 091/180] Significantly more efficient sound effect processing, more NPC sounds --- assets/voxygen/audio/sfx.ron | 24 ++++++++++++++ .../voxygen/audio/sfx/utterance/cow_calm.ogg | 3 ++ .../sfx/utterance/humanmale_greeting.ogg | 3 ++ .../voxygen/audio/sfx/utterance/pig_calm.ogg | 3 ++ .../audio/sfx/utterance/sheep_calm.ogg | 3 ++ server/src/sys/agent.rs | 16 +++++++++- voxygen/src/audio/mod.rs | 12 +++---- voxygen/src/audio/sfx/mod.rs | 10 ++++-- voxygen/src/audio/soundcache.rs | 32 +++++++++---------- 9 files changed, 77 insertions(+), 29 deletions(-) create mode 100644 assets/voxygen/audio/sfx/utterance/cow_calm.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/humanmale_greeting.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/pig_calm.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/sheep_calm.ogg diff --git a/assets/voxygen/audio/sfx.ron b/assets/voxygen/audio/sfx.ron index b4f3a0f5e8..1f6ae01af3 100644 --- a/assets/voxygen/audio/sfx.ron +++ b/assets/voxygen/audio/sfx.ron @@ -856,5 +856,29 @@ ], threshold: 4.0, ), + Utterance(Calm, Pig): ( + files: [ + "voxygen.audio.sfx.utterance.pig_calm", + ], + threshold: 4.0, + ), + Utterance(Calm, Cow): ( + files: [ + "voxygen.audio.sfx.utterance.cow_calm", + ], + threshold: 4.0, + ), + Utterance(Calm, Sheep): ( + files: [ + "voxygen.audio.sfx.utterance.sheep_calm", + ], + threshold: 4.0, + ), + Utterance(Greeting, HumanMale): ( + files: [ + "voxygen.audio.sfx.utterance.humanmale_greeting", + ], + threshold: 4.0, + ), } ) diff --git a/assets/voxygen/audio/sfx/utterance/cow_calm.ogg b/assets/voxygen/audio/sfx/utterance/cow_calm.ogg new file mode 100644 index 0000000000..fcb6058d93 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/cow_calm.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb54f07923524c80c8cb35d908165fa55582f0b085790473802fbb578766f347 +size 22691 diff --git a/assets/voxygen/audio/sfx/utterance/humanmale_greeting.ogg b/assets/voxygen/audio/sfx/utterance/humanmale_greeting.ogg new file mode 100644 index 0000000000..69dbe5a088 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/humanmale_greeting.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac12e8915ff261097d5aa24debf5685c4da3bc439ba64e6fd0bc12070f2ebff7 +size 12604 diff --git a/assets/voxygen/audio/sfx/utterance/pig_calm.ogg b/assets/voxygen/audio/sfx/utterance/pig_calm.ogg new file mode 100644 index 0000000000..b5660f8b39 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/pig_calm.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:397fca64b10178b05d5007b86e166e2ff335380dae69491f95df0e5724b7c2a5 +size 10729 diff --git a/assets/voxygen/audio/sfx/utterance/sheep_calm.ogg b/assets/voxygen/audio/sfx/utterance/sheep_calm.ogg new file mode 100644 index 0000000000..23e9151f17 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/sheep_calm.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32fd1868f9f58774e1cd93b5e577795e20a6c3f93683ba4adc7666f2157d0eb3 +size 12028 diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index dd77610f29..d6dab17616 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -409,6 +409,14 @@ impl<'a> System<'a> for Sys { .uid_allocator .retrieve_entity_internal(by.id()) { + if agent.target.is_none() { + controller.push_event( + ControlEvent::Utterance( + UtteranceKind::Angry, + ), + ); + } + agent.target = Some(Target { target: attacker, hostile: true, @@ -511,6 +519,12 @@ impl<'a> System<'a> for Sys { &mut event_emitter, ); } else { + if agent.target.is_none() { + controller.push_event(ControlEvent::Utterance( + UtteranceKind::Angry, + )); + } + agent.target = Some(Target { target: attacker, hostile: true, @@ -945,7 +959,7 @@ impl<'a> AgentData<'a> { controller.actions.push(ControlAction::Unwield); } - if thread_rng().gen_bool(0.001) { + if thread_rng().gen::() < 0.0015 { controller.push_event(ControlEvent::Utterance(UtteranceKind::Calm)); } diff --git a/voxygen/src/audio/mod.rs b/voxygen/src/audio/mod.rs index c0fb8dc175..e6f50f9e9f 100644 --- a/voxygen/src/audio/mod.rs +++ b/voxygen/src/audio/mod.rs @@ -265,8 +265,8 @@ impl AudioFrontend { ) -> Result<(), rodio::decoder::DecoderError> { if self.audio_stream.is_some() { let sound = OggSound::load_expect(sound) - .cloned() - .decoder()? + .read() + .to_source() .amplify(vol.unwrap_or(1.0)); let listener = self.listener.clone(); @@ -291,9 +291,7 @@ impl AudioFrontend { ) { if self.audio_stream.is_some() { if let Some(channel) = self.get_ambient_channel(channel_tag, volume_multiplier) { - if let Ok(sound) = OggSound::load_expect(sound).cloned().decoder() { - channel.play(sound); - } + channel.play(OggSound::load_expect(sound).read().to_source()); } } } @@ -349,9 +347,7 @@ impl AudioFrontend { fn play_music(&mut self, sound: &str, channel_tag: MusicChannelTag) { if self.music_enabled() { if let Some(channel) = self.get_music_channel(channel_tag) { - if let Ok(sound) = OggSound::load_expect(sound).cloned().decoder() { - channel.play(sound, channel_tag); - } + channel.play(OggSound::load_expect(sound).read().to_source(), channel_tag); } } } diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index c4a0905337..99193012be 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -91,7 +91,7 @@ use client::Client; use common::{ assets::{self, AssetExt, AssetHandle}, comp::{ - beam, biped_large, + beam, biped_large, humanoid, item::{ItemKind, ToolKind}, object, poise::PoiseState, @@ -189,7 +189,8 @@ pub enum SfxEvent { #[derive(Clone, Debug, PartialEq, Deserialize, Hash, Eq)] pub enum VoiceKind { Mute, - Human, + HumanFemale, + HumanMale, BipedLarge, Wendigo, Reptile, @@ -204,7 +205,10 @@ pub enum VoiceKind { fn body_to_voice(body: &Body) -> VoiceKind { match body { - Body::Humanoid(_) => VoiceKind::Human, + Body::Humanoid(body) => match &body.body_type { + humanoid::BodyType::Female => VoiceKind::HumanFemale, + humanoid::BodyType::Male => VoiceKind::HumanMale, + }, Body::QuadrupedSmall(body) => match body.species { quadruped_small::Species::Sheep => VoiceKind::Sheep, quadruped_small::Species::Pig | quadruped_small::Species::Boar => VoiceKind::Pig, diff --git a/voxygen/src/audio/soundcache.rs b/voxygen/src/audio/soundcache.rs index 545f6cef42..7fcdae187f 100644 --- a/voxygen/src/audio/soundcache.rs +++ b/voxygen/src/audio/soundcache.rs @@ -1,6 +1,7 @@ //! Handles caching and retrieval of decoded `.ogg` sfx sound data, eliminating //! the need to decode files on each playback -use common::assets; +use common::assets::{self, Loader}; +use rodio::{source::Buffered, Decoder, Source}; use std::{borrow::Cow, io, sync::Arc}; use tracing::warn; @@ -10,16 +11,16 @@ use tracing::warn; pub struct SoundLoader; #[derive(Clone)] -pub struct OggSound(Arc>); +pub struct OggSound(Buffered>>>); -impl AsRef<[u8]> for OggSound { - fn as_ref(&self) -> &[u8] { &self.0 } -} +// impl AsRef<[u8]> for OggSound { +// fn as_ref(&self) -> &[u8] { &self.0 } +// } -impl assets::Loader for SoundLoader { +impl Loader for SoundLoader { fn load(content: Cow<[u8]>, _: &str) -> Result { - let arc = Arc::new(content.into_owned()); - Ok(OggSound(arc)) + let source = Decoder::new(io::Cursor::new(content.into_owned()))?.buffered(); + Ok(OggSound(source)) } } @@ -37,16 +38,13 @@ impl assets::Asset for OggSound { /// Wrapper for decoded audio data impl OggSound { - pub fn decoder( - self, - ) -> Result>, rodio::decoder::DecoderError> { - let cursor = io::Cursor::new(self); - rodio::Decoder::new(cursor) - } + pub fn to_source(&self) -> impl Source + Iterator { self.0.clone() } pub fn empty() -> OggSound { - OggSound(Arc::new( - include_bytes!("../../../assets/voxygen/audio/null.ogg").to_vec(), - )) + SoundLoader::load( + Cow::Borrowed(include_bytes!("../../../assets/voxygen/audio/null.ogg")), + "empty", + ) + .unwrap() } } From b8749dc21941996e975e8886e0a6369202f5dbf6 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 16 Jun 2021 17:16:05 +0100 Subject: [PATCH 092/180] Added more cow noises, prepare for merge --- CHANGELOG.md | 4 + assets/voxygen/audio/sfx.ron | 102 +++++++++--------- .../voxygen/audio/sfx/utterance/cow_calm2.ogg | 3 + .../voxygen/audio/sfx/utterance/cow_calm3.ogg | 3 + client/src/lib.rs | 6 +- server/src/events/interaction.rs | 22 ++-- voxygen/src/audio/sfx/mod.rs | 30 ++++-- voxygen/src/audio/soundcache.rs | 6 +- voxygen/src/session/mod.rs | 7 +- voxygen/src/settings/control.rs | 1 + voxygen/src/window.rs | 3 + 11 files changed, 108 insertions(+), 79 deletions(-) create mode 100644 assets/voxygen/audio/sfx/utterance/cow_calm2.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/cow_calm3.ogg diff --git a/CHANGELOG.md b/CHANGELOG.md index a556f9bff8..6ad28d3149 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added a skill tree for mining, which gains xp from mining ores and gems. - Added debug line info to release builds, enhancing the usefulness of panic backtraces +- NPCs and animals can now make sounds in response to certain events +- Players can press H to greet others ### Changed - Entity-entity pushback is no longer applied in forced movement states like rolling and leaping. @@ -18,7 +20,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed ### Fixed + - Cases where no audio output could be produced before. +- Significantly improved the performance of playing sound effects ## [0.10.0] - 2021-06-12 diff --git a/assets/voxygen/audio/sfx.ron b/assets/voxygen/audio/sfx.ron index 1f6ae01af3..81bca564d1 100644 --- a/assets/voxygen/audio/sfx.ron +++ b/assets/voxygen/audio/sfx.ron @@ -5,7 +5,7 @@ // Campfire: ( files: [ - "voxygen.audio.sfx.ambient.fire", + "voxygen.audio.sfx.ambient.fire", ], threshold: 21.835, ), @@ -831,54 +831,56 @@ ], threshold: 0.2, ), - Utterance(Angry, Wendigo): ( - files: [ - "voxygen.audio.sfx.utterance.wendigo_angry", - ], - threshold: 4.0, - ), - Utterance(Angry, BipedLarge): ( - files: [ - "voxygen.audio.sfx.utterance.ogre_angry", - "voxygen.audio.sfx.utterance.ogre_angry2", - ], - threshold: 4.0, - ), - Utterance(Angry, Reptile): ( - files: [ - "voxygen.audio.sfx.utterance.saurok_angry", - ], - threshold: 4.0, - ), - Utterance(Angry, Bird): ( - files: [ - "voxygen.audio.sfx.utterance.bird_angry", - ], - threshold: 4.0, - ), - Utterance(Calm, Pig): ( - files: [ - "voxygen.audio.sfx.utterance.pig_calm", - ], - threshold: 4.0, - ), - Utterance(Calm, Cow): ( - files: [ - "voxygen.audio.sfx.utterance.cow_calm", - ], - threshold: 4.0, - ), - Utterance(Calm, Sheep): ( - files: [ - "voxygen.audio.sfx.utterance.sheep_calm", - ], - threshold: 4.0, - ), - Utterance(Greeting, HumanMale): ( - files: [ - "voxygen.audio.sfx.utterance.humanmale_greeting", - ], - threshold: 4.0, - ), + Utterance(Angry, Wendigo): ( + files: [ + "voxygen.audio.sfx.utterance.wendigo_angry", + ], + threshold: 0.2, + ), + Utterance(Angry, BipedLarge): ( + files: [ + "voxygen.audio.sfx.utterance.ogre_angry", + "voxygen.audio.sfx.utterance.ogre_angry2", + ], + threshold: 0.2, + ), + Utterance(Angry, Reptile): ( + files: [ + "voxygen.audio.sfx.utterance.saurok_angry", + ], + threshold: 0.2, + ), + Utterance(Angry, Bird): ( + files: [ + "voxygen.audio.sfx.utterance.bird_angry", + ], + threshold: 0.2, + ), + Utterance(Calm, Pig): ( + files: [ + "voxygen.audio.sfx.utterance.pig_calm", + ], + threshold: 0.2, + ), + Utterance(Calm, Cow): ( + files: [ + "voxygen.audio.sfx.utterance.cow_calm", + "voxygen.audio.sfx.utterance.cow_calm2", + "voxygen.audio.sfx.utterance.cow_calm3", + ], + threshold: 0.2, + ), + Utterance(Calm, Sheep): ( + files: [ + "voxygen.audio.sfx.utterance.sheep_calm", + ], + threshold: 0.2, + ), + Utterance(Greeting, HumanMale): ( + files: [ + "voxygen.audio.sfx.utterance.humanmale_greeting", + ], + threshold: 0.2, + ), } ) diff --git a/assets/voxygen/audio/sfx/utterance/cow_calm2.ogg b/assets/voxygen/audio/sfx/utterance/cow_calm2.ogg new file mode 100644 index 0000000000..f060217fe4 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/cow_calm2.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:581a6c86eb6f06ed2694d0cdfa08691fa8bf38fd7fffb57680b41ed5dc1060ed +size 23328 diff --git a/assets/voxygen/audio/sfx/utterance/cow_calm3.ogg b/assets/voxygen/audio/sfx/utterance/cow_calm3.ogg new file mode 100644 index 0000000000..59ae620508 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/cow_calm3.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb77a69af59eb015c20de58bad181f7e02bc82649d33c274302018c8baf4b85a +size 14690 diff --git a/client/src/lib.rs b/client/src/lib.rs index 661c2ad5f7..9f09ffb9d0 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -28,7 +28,7 @@ use common::{ skills::Skill, slot::Slot, ChatMode, ControlAction, ControlEvent, Controller, ControllerInputs, GroupManip, InputKind, - InventoryAction, InventoryEvent, InventoryUpdateEvent, + InventoryAction, InventoryEvent, InventoryUpdateEvent, UtteranceKind, }, event::{EventBus, LocalEvent}, grid::Grid, @@ -1224,6 +1224,10 @@ impl Client { } } + pub fn utter(&mut self, kind: UtteranceKind) { + self.send_msg(ClientGeneral::ControlEvent(ControlEvent::Utterance(kind))); + } + pub fn toggle_sneak(&mut self) { let is_sneaking = self .state diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index 3a04f63aec..acd935f9a6 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -404,17 +404,17 @@ pub fn handle_sound(server: &mut Server, sound: &Sound) { .inbox .push_back(AgentEvent::ServerSound(propagated_sound)); } + } - // Attempt to turn this sound into an outcome to be received by frontends. - if let Some(outcome) = match sound.kind { - SoundKind::Utterance(kind, body) => Some(Outcome::Utterance { - kind, - pos: sound.pos, - body, - }), - _ => None, - } { - ecs.write_resource::>().push(outcome); - } + // Attempt to turn this sound into an outcome to be received by frontends. + if let Some(outcome) = match sound.kind { + SoundKind::Utterance(kind, body) => Some(Outcome::Utterance { + kind, + pos: sound.pos, + body, + }), + _ => None, + } { + ecs.write_resource::>().push(outcome); } } diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index 99193012be..1f5fe7ab89 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -106,7 +106,7 @@ use event_mapper::SfxEventMapper; use hashbrown::HashMap; use rand::prelude::*; use serde::Deserialize; -use tracing::warn; +use tracing::{debug, warn}; use vek::*; /// We watch the states of nearby entities in order to emit SFX at their @@ -186,9 +186,8 @@ pub enum SfxEvent { Utterance(UtteranceKind, VoiceKind), } -#[derive(Clone, Debug, PartialEq, Deserialize, Hash, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Hash, Eq)] pub enum VoiceKind { - Mute, HumanFemale, HumanMale, BipedLarge, @@ -203,8 +202,8 @@ pub enum VoiceKind { BigCat, } -fn body_to_voice(body: &Body) -> VoiceKind { - match body { +fn body_to_voice(body: &Body) -> Option { + Some(match body { Body::Humanoid(body) => match &body.body_type { humanoid::BodyType::Female => VoiceKind::HumanFemale, humanoid::BodyType::Male => VoiceKind::HumanMale, @@ -232,7 +231,7 @@ fn body_to_voice(body: &Body) -> VoiceKind { | quadruped_medium::Species::Yak | quadruped_medium::Species::Moose | quadruped_medium::Species::Dreadhorn => VoiceKind::Cow, - _ => VoiceKind::Mute, + _ => return None, }, Body::BirdMedium(_) | Body::BirdLarge(_) => VoiceKind::Bird, Body::BipedLarge(body) => match body.species { @@ -243,8 +242,8 @@ fn body_to_voice(body: &Body) -> VoiceKind { _ => VoiceKind::BipedLarge, }, Body::Theropod(_) | Body::Dragon(_) => VoiceKind::Reptile, - _ => VoiceKind::Mute, - } + _ => return None, + }) } #[derive(Clone, Debug, PartialEq, Deserialize, Hash, Eq)] @@ -516,9 +515,18 @@ impl SfxMgr { }, }, Outcome::Utterance { pos, kind, body } => { - let sfx_trigger_item = - triggers.get_key_value(&SfxEvent::Utterance(*kind, body_to_voice(body))); - audio.emit_sfx(sfx_trigger_item, *pos, Some(2.5), false); + if let Some(voice) = body_to_voice(body) { + let sfx_trigger_item = + triggers.get_key_value(&SfxEvent::Utterance(*kind, voice)); + if let Some(sfx_trigger_item) = sfx_trigger_item { + audio.emit_sfx(Some(sfx_trigger_item), *pos, Some(2.5), false); + } else { + debug!( + "No utterance sound effect exists for ({:?}, {:?})", + kind, voice + ); + } + } }, Outcome::ExpChange { .. } | Outcome::ComboChange { .. } diff --git a/voxygen/src/audio/soundcache.rs b/voxygen/src/audio/soundcache.rs index 7fcdae187f..f08a4bdab6 100644 --- a/voxygen/src/audio/soundcache.rs +++ b/voxygen/src/audio/soundcache.rs @@ -2,7 +2,7 @@ //! the need to decode files on each playback use common::assets::{self, Loader}; use rodio::{source::Buffered, Decoder, Source}; -use std::{borrow::Cow, io, sync::Arc}; +use std::{borrow::Cow, io}; use tracing::warn; // Implementation of sound taken from this github issue: @@ -13,10 +13,6 @@ pub struct SoundLoader; #[derive(Clone)] pub struct OggSound(Buffered>>>); -// impl AsRef<[u8]> for OggSound { -// fn as_ref(&self) -> &[u8] { &self.0 } -// } - impl Loader for SoundLoader { fn load(content: Cow<[u8]>, _: &str) -> Result { let source = Decoder::new(io::Cursor::new(content.into_owned()))?.buffered(); diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 9abef76774..104459f2b8 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -15,7 +15,7 @@ use common::{ inventory::slot::{EquipSlot, Slot}, invite::InviteKind, item::{tool::ToolKind, ItemDef, ItemDesc}, - ChatMsg, ChatType, InputKind, InventoryUpdateEvent, Pos, Vel, + ChatMsg, ChatType, InputKind, InventoryUpdateEvent, Pos, UtteranceKind, Vel, }, consts::{MAX_MOUNT_RANGE, MAX_PICKUP_RANGE}, outcome::Outcome, @@ -525,6 +525,11 @@ impl PlayState for SessionState { self.client.borrow_mut().toggle_dance(); } }, + GameInput::Greet => { + if state { + self.client.borrow_mut().utter(UtteranceKind::Greeting); + } + }, GameInput::Sneak => { if state { self.stop_auto_walk(); diff --git a/voxygen/src/settings/control.rs b/voxygen/src/settings/control.rs index 67b4defb63..c0b9eb1bd6 100644 --- a/voxygen/src/settings/control.rs +++ b/voxygen/src/settings/control.rs @@ -123,6 +123,7 @@ impl ControlSettings { GameInput::Jump => KeyMouse::Key(VirtualKeyCode::Space), GameInput::Sit => KeyMouse::Key(VirtualKeyCode::K), GameInput::Dance => KeyMouse::Key(VirtualKeyCode::J), + GameInput::Greet => KeyMouse::Key(VirtualKeyCode::H), GameInput::Glide => KeyMouse::Key(VirtualKeyCode::LShift), GameInput::Climb => KeyMouse::Key(VirtualKeyCode::Space), GameInput::ClimbDown => KeyMouse::Key(VirtualKeyCode::LControl), diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 2dfe0a9c85..118fe109e9 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -39,6 +39,7 @@ pub enum GameInput { Jump, Sit, Dance, + Greet, Glide, Climb, ClimbDown, @@ -94,6 +95,7 @@ impl GameInput { GameInput::Jump => "gameinput.jump", GameInput::Sit => "gameinput.sit", GameInput::Dance => "gameinput.dance", + GameInput::Greet => "gameinput.greet", GameInput::Glide => "gameinput.glide", GameInput::Climb => "gameinput.climb", GameInput::ClimbDown => "gameinput.climbdown", @@ -159,6 +161,7 @@ impl GameInput { GameInput::Jump, GameInput::Sit, GameInput::Dance, + GameInput::Greet, GameInput::Glide, GameInput::Climb, GameInput::ClimbDown, From 9454c34cc2c9535c463577a4758f0aa77603b3a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Capucho?= Date: Wed, 16 Jun 2021 20:22:33 +0100 Subject: [PATCH 093/180] Submit queue when maintaining and minimized --- voxygen/src/render/renderer.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index f917a83e2a..b43e7cb2e8 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -600,7 +600,13 @@ impl Renderer { Ok(()) } - pub fn maintain(&self) { self.device.poll(wgpu::Maintain::Poll) } + pub fn maintain(&self) { + if self.is_minimized { + self.queue.submit(std::iter::empty()); + } + + self.device.poll(wgpu::Maintain::Poll) + } /// Create render target views fn create_rt_views( From 0941f03608ad2305f0767070fb8e65a6efed4252 Mon Sep 17 00:00:00 2001 From: jshipsey Date: Tue, 15 Jun 2021 01:43:49 -0400 Subject: [PATCH 094/180] buffs to stretch progression --- .../common/abilities/ability_set_manifest.ron | 15 ++- assets/common/abilities/axe/doublestrike.ron | 4 +- .../common/abilities/custom/asp/firebomb.ron | 18 ++++ .../{quadlowranged => asp}/singlestrike.ron | 8 +- .../abilities/custom/basilisk/petrify.ron | 19 ++++ .../custom/basilisk/triplestrike.ron | 14 +-- .../abilities/custom/husk/singlestrike.ron | 4 +- .../abilities/custom/husk/triplestrike.ron | 24 ++--- .../abilities/custom/maneater/poisonball.ron | 18 ++++ .../{basilisk => maneater}/singlestrike.ron | 14 +-- .../custom/mindflayer/necroticsphere.ron | 2 +- .../custom/mindflayer/necroticvortex.ron | 4 +- .../custom/quadlowbasic/singlestrike.ron | 4 +- .../custom/quadlowbasic/triplestrike.ron | 12 +-- .../abilities/custom/quadlowbreathe/dash.ron | 4 +- .../custom/quadlowbreathe/flamethrower.ron | 2 +- .../custom/quadlowbreathe/triplestrike.ron | 18 ++-- .../custom/quadlowquick/quadstrike.ron | 6 +- .../custom/quadlowranged/firebomb.ron | 6 +- .../abilities/custom/quadlowtail/charged.ron | 2 +- .../custom/quadmedbasic/singlestrike.ron | 2 +- .../custom/quadmedcharge/doublestrike.ron | 4 +- .../abilities/custom/quadmedhoof/basic.ron | 6 +- .../custom/quadsmallbasic/singlestrike.ron | 2 +- .../custom/theropodbasic/singlestrike.ron | 4 +- .../custom/theropodbasic/triplestrike.ron | 18 ++-- .../custom/wendigomagic/frostbomb.ron | 2 +- .../custom/wendigomagic/singlestrike.ron | 4 +- .../common/abilities/hammer/singlestrike.ron | 2 +- .../abilities/hammersimple/doublestrike.ron | 16 ++-- .../common/abilities/sword/triplestrike.ron | 4 +- assets/common/cave_scatter/dark_floor.ron | 7 +- assets/common/cave_scatter/deep_floor.ron | 10 +- assets/common/cave_scatter/shallow_floor.ron | 2 +- .../items/npc_armor/biped_large/generic.ron | 17 ++++ .../npc_armor/biped_large/mindflayer.ron | 2 +- .../items/npc_armor/quadruped_low/generic.ron | 17 ++++ .../items/npc_armor/quadruped_low/shell.ron | 17 ++++ .../items/npc_armor/theropod/rugged.ron | 17 ++++ .../unique/{quadlowranged.ron => asp.ron} | 4 +- .../items/npc_weapons/unique/maneater.ron | 18 ++++ .../creature/quad_small/generic.ron | 2 +- assets/common/loot_tables/trading.ron | 13 ++- assets/common/recipe_book.ron | 91 +++++++++---------- assets/voxygen/i18n/en/hud/misc.ron | 4 +- common/src/comp/body.rs | 9 +- common/src/comp/inventory/loadout_builder.rs | 49 +++++++++- common/src/states/utils.rs | 4 +- server/src/sys/agent.rs | 6 +- world/src/layer/mod.rs | 10 +- 50 files changed, 380 insertions(+), 181 deletions(-) create mode 100644 assets/common/abilities/custom/asp/firebomb.ron rename assets/common/abilities/custom/{quadlowranged => asp}/singlestrike.ron (79%) create mode 100644 assets/common/abilities/custom/basilisk/petrify.ron create mode 100644 assets/common/abilities/custom/maneater/poisonball.ron rename assets/common/abilities/custom/{basilisk => maneater}/singlestrike.ron (67%) create mode 100644 assets/common/items/npc_armor/biped_large/generic.ron create mode 100644 assets/common/items/npc_armor/quadruped_low/generic.ron create mode 100644 assets/common/items/npc_armor/quadruped_low/shell.ron create mode 100644 assets/common/items/npc_armor/theropod/rugged.ron rename assets/common/items/npc_weapons/unique/{quadlowranged.ron => asp.ron} (80%) create mode 100644 assets/common/items/npc_weapons/unique/maneater.ron diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index ef9979ce3f..a9e33c3b0b 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -149,13 +149,18 @@ abilities: [], ), Custom("Basilisk"): ( - primary: "common.abilities.custom.basilisk.singlestrike", - secondary: "common.abilities.custom.basilisk.triplestrike", + primary: "common.abilities.custom.basilisk.triplestrike", + secondary: "common.abilities.custom.basilisk.petrify", abilities: [], ), - Custom("Quad Low Ranged"): ( - primary: "common.abilities.custom.quadlowranged.singlestrike", - secondary: "common.abilities.custom.quadlowranged.firebomb", + Custom("Asp"): ( + primary: "common.abilities.custom.asp.singlestrike", + secondary: "common.abilities.custom.asp.firebomb", + abilities: [], + ), + Custom("Maneater"): ( + primary: "common.abilities.custom.maneater.singlestrike", + secondary: "common.abilities.custom.maneater.poisonball", abilities: [], ), Custom("Quad Low Breathe"): ( diff --git a/assets/common/abilities/axe/doublestrike.ron b/assets/common/abilities/axe/doublestrike.ron index 029bb4cdaf..80de5e37e0 100644 --- a/assets/common/abilities/axe/doublestrike.ron +++ b/assets/common/abilities/axe/doublestrike.ron @@ -6,7 +6,7 @@ ComboMelee( base_poise_damage: 12, damage_increase: 10, poise_damage_increase: 0, - knockback: 8.0, + knockback: 5.0, range: 3.5, angle: 50.0, base_buildup_duration: 0.15, @@ -22,7 +22,7 @@ ComboMelee( base_poise_damage: 20, damage_increase: 15, poise_damage_increase: 0, - knockback: 12.0, + knockback: 6.0, range: 3.5, angle: 30.0, base_buildup_duration: 0.2, diff --git a/assets/common/abilities/custom/asp/firebomb.ron b/assets/common/abilities/custom/asp/firebomb.ron new file mode 100644 index 0000000000..383f35ee39 --- /dev/null +++ b/assets/common/abilities/custom/asp/firebomb.ron @@ -0,0 +1,18 @@ +BasicRanged( + energy_cost: 0, + buildup_duration: 0.8, + recover_duration: 0.35, + projectile: Fireball( + damage: 130.0, + radius: 5.0, + energy_regen: 0, + ), + projectile_body: Object(BoltFire), + /*projectile_light: Some(LightEmitter { + col: (1.0, 0.75, 0.11).into(), + ..Default::default() + }),*/ + projectile_speed: 70.0, + num_projectiles: 1, + projectile_spread: 0.0, +) diff --git a/assets/common/abilities/custom/quadlowranged/singlestrike.ron b/assets/common/abilities/custom/asp/singlestrike.ron similarity index 79% rename from assets/common/abilities/custom/quadlowranged/singlestrike.ron rename to assets/common/abilities/custom/asp/singlestrike.ron index 81b81f3cd0..ec3acbcd37 100644 --- a/assets/common/abilities/custom/quadlowranged/singlestrike.ron +++ b/assets/common/abilities/custom/asp/singlestrike.ron @@ -2,18 +2,18 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 80, + base_damage: 140, damage_increase: 0, base_poise_damage: 28, poise_damage_increase: 0, knockback: 3.0, range: 3.5, angle: 60.0, - base_buildup_duration: 0.4, + base_buildup_duration: 0.6, base_swing_duration: 0.1, hit_timing: 0.5, - base_recover_duration: 0.4, - forward_movement: 3.0, + base_recover_duration: 0.2, + forward_movement: 2.0, damage_kind: Crushing, ), ], diff --git a/assets/common/abilities/custom/basilisk/petrify.ron b/assets/common/abilities/custom/basilisk/petrify.ron new file mode 100644 index 0000000000..c8fb5b3021 --- /dev/null +++ b/assets/common/abilities/custom/basilisk/petrify.ron @@ -0,0 +1,19 @@ +BasicBeam( + buildup_duration: 0.9, + recover_duration: 1.0, + beam_duration: 1.0, + damage: 420, + tick_rate: 0.5, + range: 22.0, + max_angle: 5.0, + damage_effect: Some(Buff(( + kind: Frozen, + dur_secs: 0.8, + strength: Value(5.0), + chance: 1.0, + ))), + energy_regen: 0, + energy_drain: 0, + orientation_behavior: Normal, + specifier: Cultist, +) \ No newline at end of file diff --git a/assets/common/abilities/custom/basilisk/triplestrike.ron b/assets/common/abilities/custom/basilisk/triplestrike.ron index cd4942d2bb..db05cf3e26 100644 --- a/assets/common/abilities/custom/basilisk/triplestrike.ron +++ b/assets/common/abilities/custom/basilisk/triplestrike.ron @@ -2,14 +2,14 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 100, + base_damage: 180, damage_increase: 0, base_poise_damage: 15, poise_damage_increase: 0, - knockback: 7.0, + knockback: 3.0, range: 2.8, angle: 30.0, - base_buildup_duration: 0.65, + base_buildup_duration: 0.7, base_swing_duration: 0.07, hit_timing: 0.5, base_recover_duration: 0.3, @@ -18,11 +18,11 @@ ComboMelee( ), ( stage: 2, - base_damage: 100, + base_damage: 180, damage_increase: 0, base_poise_damage: 18, poise_damage_increase: 0, - knockback: 7.0, + knockback: 3.0, range: 2.8, angle: 30.0, base_buildup_duration: 0.4, @@ -34,11 +34,11 @@ ComboMelee( ), ( stage: 3, - base_damage: 100, + base_damage: 180, damage_increase: 0, base_poise_damage: 20, poise_damage_increase: 0, - knockback: 7.0, + knockback: 3.0, range: 2.8, angle: 30.0, base_buildup_duration: 0.4, diff --git a/assets/common/abilities/custom/husk/singlestrike.ron b/assets/common/abilities/custom/husk/singlestrike.ron index 92e1e2d3eb..d64184bfa8 100644 --- a/assets/common/abilities/custom/husk/singlestrike.ron +++ b/assets/common/abilities/custom/husk/singlestrike.ron @@ -2,12 +2,12 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 90, + base_damage: 160, damage_increase: 0, base_poise_damage: 12, poise_damage_increase: 0, knockback: 5.0, - range: 3.5, + range: 2.5, angle: 60.0, base_buildup_duration: 0.25, base_swing_duration: 0.07, diff --git a/assets/common/abilities/custom/husk/triplestrike.ron b/assets/common/abilities/custom/husk/triplestrike.ron index 25e985d3d2..d4b66e6a50 100644 --- a/assets/common/abilities/custom/husk/triplestrike.ron +++ b/assets/common/abilities/custom/husk/triplestrike.ron @@ -2,12 +2,12 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 120, + base_damage: 160, damage_increase: 0, - base_poise_damage: 8, + base_poise_damage: 6, poise_damage_increase: 0, - knockback: 5.0, - range: 3.5, + knockback: 3.0, + range: 2.5, angle: 30.0, base_buildup_duration: 0.2, base_swing_duration: 0.07, @@ -18,12 +18,12 @@ ComboMelee( ), ( stage: 2, - base_damage: 120, + base_damage: 160, damage_increase: 0, - base_poise_damage: 10, + base_poise_damage: 8, poise_damage_increase: 0, - knockback: 5.0, - range: 3.5, + knockback: 3.0, + range: 2.5, angle: 30.0, base_buildup_duration: 0.22, base_swing_duration: 0.07, @@ -34,12 +34,12 @@ ComboMelee( ), ( stage: 3, - base_damage: 120, + base_damage: 160, damage_increase: 0, - base_poise_damage: 12, + base_poise_damage: 10, poise_damage_increase: 0, - knockback: 5.0, - range: 3.5, + knockback: 3.0, + range: 2.5, angle: 30.0, base_buildup_duration: 0.2, base_swing_duration: 0.07, diff --git a/assets/common/abilities/custom/maneater/poisonball.ron b/assets/common/abilities/custom/maneater/poisonball.ron new file mode 100644 index 0000000000..f8a2b14c9b --- /dev/null +++ b/assets/common/abilities/custom/maneater/poisonball.ron @@ -0,0 +1,18 @@ +BasicRanged( + energy_cost: 0, + buildup_duration: 0.8, + recover_duration: 0.35, + projectile: NecroticSphere( + damage: 260.0, + radius: 5.0, + energy_regen: 0, + ), + projectile_body: Object(FireworkPurple), + /*projectile_light: Some(LightEmitter { + col: (1.0, 0.75, 0.11).into(), + ..Default::default() + }),*/ + projectile_speed: 70.0, + num_projectiles: 3, + projectile_spread: 0.2, +) diff --git a/assets/common/abilities/custom/basilisk/singlestrike.ron b/assets/common/abilities/custom/maneater/singlestrike.ron similarity index 67% rename from assets/common/abilities/custom/basilisk/singlestrike.ron rename to assets/common/abilities/custom/maneater/singlestrike.ron index 65846146d6..45ebb89522 100644 --- a/assets/common/abilities/custom/basilisk/singlestrike.ron +++ b/assets/common/abilities/custom/maneater/singlestrike.ron @@ -2,18 +2,18 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 130, + base_damage: 200, damage_increase: 0, base_poise_damage: 28, poise_damage_increase: 0, knockback: 3.0, - range: 3.0, + range: 3.5, angle: 60.0, - base_buildup_duration: 0.4, - base_swing_duration: 0.07, + base_buildup_duration: 0.5, + base_swing_duration: 0.075, hit_timing: 0.5, - base_recover_duration: 0.4, - forward_movement: 3.0, + base_recover_duration: 0.2, + forward_movement: 2.0, damage_kind: Crushing, ), ], @@ -24,5 +24,5 @@ ComboMelee( max_speed_increase: 0.0, scales_from_combo: 0, is_interruptible: false, - ori_modifier: 0.6, + ori_modifier: 0.65, ) diff --git a/assets/common/abilities/custom/mindflayer/necroticsphere.ron b/assets/common/abilities/custom/mindflayer/necroticsphere.ron index e8ab890b78..08c2039552 100644 --- a/assets/common/abilities/custom/mindflayer/necroticsphere.ron +++ b/assets/common/abilities/custom/mindflayer/necroticsphere.ron @@ -3,7 +3,7 @@ BasicRanged( buildup_duration: 0.75, recover_duration: 0.4, projectile: NecroticSphere( - damage: 300.0, + damage: 450.0, radius: 5.0, ), projectile_body: Object(FireworkPurple), diff --git a/assets/common/abilities/custom/mindflayer/necroticvortex.ron b/assets/common/abilities/custom/mindflayer/necroticvortex.ron index 896b380b62..b4d750fea2 100644 --- a/assets/common/abilities/custom/mindflayer/necroticvortex.ron +++ b/assets/common/abilities/custom/mindflayer/necroticvortex.ron @@ -1,12 +1,12 @@ SpinMelee( - buildup_duration: 0.5, + buildup_duration: 0.8, swing_duration: 0.2, recover_duration: 0.6, base_damage: 80.0, base_poise_damage: 1.0, knockback: ( strength: 7.0, direction: Towards), range: 16.0, - damage_effect: Some(Lifesteal(1.0)), + damage_effect: Some(Lifesteal(2.0)), energy_cost: 0.0, is_infinite: true, movement_behavior: Stationary, diff --git a/assets/common/abilities/custom/quadlowbasic/singlestrike.ron b/assets/common/abilities/custom/quadlowbasic/singlestrike.ron index c0bd5a47fb..d0ce162fcd 100644 --- a/assets/common/abilities/custom/quadlowbasic/singlestrike.ron +++ b/assets/common/abilities/custom/quadlowbasic/singlestrike.ron @@ -2,14 +2,14 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 100, + base_damage: 200, damage_increase: 0, base_poise_damage: 28, poise_damage_increase: 0, knockback: 3.0, range: 2.0, angle: 60.0, - base_buildup_duration: 0.4, + base_buildup_duration: 0.6, base_swing_duration: 0.07, hit_timing: 0.5, base_recover_duration: 0.4, diff --git a/assets/common/abilities/custom/quadlowbasic/triplestrike.ron b/assets/common/abilities/custom/quadlowbasic/triplestrike.ron index f7b49ad5ac..db548b316b 100644 --- a/assets/common/abilities/custom/quadlowbasic/triplestrike.ron +++ b/assets/common/abilities/custom/quadlowbasic/triplestrike.ron @@ -2,11 +2,11 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 80, + base_damage: 140, damage_increase: 0, base_poise_damage: 15, poise_damage_increase: 0, - knockback: 7.0, + knockback: 3.0, range: 2.2, angle: 30.0, base_buildup_duration: 0.65, @@ -18,11 +18,11 @@ ComboMelee( ), ( stage: 2, - base_damage: 80, + base_damage: 140, damage_increase: 0, base_poise_damage: 18, poise_damage_increase: 0, - knockback: 7.0, + knockback: 3.0, range: 2.2, angle: 30.0, base_buildup_duration: 0.4, @@ -34,11 +34,11 @@ ComboMelee( ), ( stage: 3, - base_damage: 80, + base_damage: 140, damage_increase: 0, base_poise_damage: 20, poise_damage_increase: 0, - knockback: 7.0, + knockback: 3.0, range: 2.2, angle: 30.0, base_buildup_duration: 0.4, diff --git a/assets/common/abilities/custom/quadlowbreathe/dash.ron b/assets/common/abilities/custom/quadlowbreathe/dash.ron index 645e4b8516..32ae96a698 100644 --- a/assets/common/abilities/custom/quadlowbreathe/dash.ron +++ b/assets/common/abilities/custom/quadlowbreathe/dash.ron @@ -1,7 +1,7 @@ DashMelee( energy_cost: 0, - base_damage: 50, - scaled_damage: 100, + base_damage: 80, + scaled_damage: 150, base_poise_damage: 25, scaled_poise_damage: 0, base_knockback: 4.0, diff --git a/assets/common/abilities/custom/quadlowbreathe/flamethrower.ron b/assets/common/abilities/custom/quadlowbreathe/flamethrower.ron index 6a24929f17..ddc09e0ef6 100644 --- a/assets/common/abilities/custom/quadlowbreathe/flamethrower.ron +++ b/assets/common/abilities/custom/quadlowbreathe/flamethrower.ron @@ -2,7 +2,7 @@ BasicBeam( buildup_duration: 0.4, recover_duration: 0.25, beam_duration: 0.5, - damage: 40, + damage: 70, tick_rate: 3.0, range: 15.0, max_angle: 22.5, diff --git a/assets/common/abilities/custom/quadlowbreathe/triplestrike.ron b/assets/common/abilities/custom/quadlowbreathe/triplestrike.ron index 6944d309f3..6c68f6f61a 100644 --- a/assets/common/abilities/custom/quadlowbreathe/triplestrike.ron +++ b/assets/common/abilities/custom/quadlowbreathe/triplestrike.ron @@ -2,11 +2,11 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 75, + base_damage: 220, damage_increase: 0, - base_poise_damage: 0, + base_poise_damage: 20, poise_damage_increase: 0, - knockback: 10.0, + knockback: 3.0, range: 2.5, angle: 30.0, base_buildup_duration: 0.65, @@ -18,11 +18,11 @@ ComboMelee( ), ( stage: 2, - base_damage: 75, + base_damage: 220, damage_increase: 0, - base_poise_damage: 0, + base_poise_damage: 20, poise_damage_increase: 0, - knockback: 10.0, + knockback: 3.0, range: 2.5, angle: 30.0, base_buildup_duration: 0.4, @@ -34,11 +34,11 @@ ComboMelee( ), ( stage: 3, - base_damage: 75, + base_damage: 220, damage_increase: 0, - base_poise_damage: 0, + base_poise_damage: 20, poise_damage_increase: 0, - knockback: 10.0, + knockback: 3.0, range: 2.5, angle: 30.0, base_buildup_duration: 0.4, diff --git a/assets/common/abilities/custom/quadlowquick/quadstrike.ron b/assets/common/abilities/custom/quadlowquick/quadstrike.ron index 3c7f848f43..fdf237aebf 100644 --- a/assets/common/abilities/custom/quadlowquick/quadstrike.ron +++ b/assets/common/abilities/custom/quadlowquick/quadstrike.ron @@ -6,7 +6,7 @@ ComboMelee( damage_increase: 0, base_poise_damage: 15, poise_damage_increase: 0, - knockback: 2.0, + knockback: 1.0, range: 2.5, angle: 30.0, base_buildup_duration: 0.6, @@ -22,7 +22,7 @@ ComboMelee( damage_increase: 0, base_poise_damage: 15, poise_damage_increase: 0, - knockback: 2.0, + knockback: 1.0, range: 2.5, angle: 30.0, base_buildup_duration: 0.15, @@ -38,7 +38,7 @@ ComboMelee( damage_increase: 0, base_poise_damage: 15, poise_damage_increase: 0, - knockback: 2.0, + knockback: 1.0, range: 2.5, angle: 30.0, base_buildup_duration: 0.2, diff --git a/assets/common/abilities/custom/quadlowranged/firebomb.ron b/assets/common/abilities/custom/quadlowranged/firebomb.ron index beb3c27f1c..4c758438cf 100644 --- a/assets/common/abilities/custom/quadlowranged/firebomb.ron +++ b/assets/common/abilities/custom/quadlowranged/firebomb.ron @@ -2,12 +2,12 @@ BasicRanged( energy_cost: 0, buildup_duration: 0.8, recover_duration: 0.35, - projectile: Fireball( - damage: 80.0, + projectile: NecroticSphere( + damage: 130.0, radius: 5.0, energy_regen: 0, ), - projectile_body: Object(BoltFire), + projectile_body: Object(FireworkPurple), /*projectile_light: Some(LightEmitter { col: (1.0, 0.75, 0.11).into(), ..Default::default() diff --git a/assets/common/abilities/custom/quadlowtail/charged.ron b/assets/common/abilities/custom/quadlowtail/charged.ron index fb03117f1c..d2c563f69c 100644 --- a/assets/common/abilities/custom/quadlowtail/charged.ron +++ b/assets/common/abilities/custom/quadlowtail/charged.ron @@ -13,6 +13,6 @@ ChargedMelee( charge_duration: 0.8, swing_duration: 0.7, hit_timing: 0.9, - recover_duration: 1.2, + recover_duration: 0.7, damage_kind: Crushing, ) diff --git a/assets/common/abilities/custom/quadmedbasic/singlestrike.ron b/assets/common/abilities/custom/quadmedbasic/singlestrike.ron index 2dcb7c3eb5..d5659cd650 100644 --- a/assets/common/abilities/custom/quadmedbasic/singlestrike.ron +++ b/assets/common/abilities/custom/quadmedbasic/singlestrike.ron @@ -6,7 +6,7 @@ ComboMelee( damage_increase: 0, base_poise_damage: 28, poise_damage_increase: 0, - knockback: 5.0, + knockback: 3.0, range: 2.7, angle: 60.0, base_buildup_duration: 0.4, diff --git a/assets/common/abilities/custom/quadmedcharge/doublestrike.ron b/assets/common/abilities/custom/quadmedcharge/doublestrike.ron index 83ba1ae46b..d535ccb97a 100644 --- a/assets/common/abilities/custom/quadmedcharge/doublestrike.ron +++ b/assets/common/abilities/custom/quadmedcharge/doublestrike.ron @@ -6,7 +6,7 @@ ComboMelee( damage_increase: 0, base_poise_damage: 22, poise_damage_increase: 0, - knockback: 10.0, + knockback: 4.0, range: 2.5, angle: 30.0, base_buildup_duration: 0.65, @@ -22,7 +22,7 @@ ComboMelee( damage_increase: 0, base_poise_damage: 0, poise_damage_increase: 22, - knockback: 10.0, + knockback: 4.0, range: 2.5, angle: 30.0, base_buildup_duration: 0.4, diff --git a/assets/common/abilities/custom/quadmedhoof/basic.ron b/assets/common/abilities/custom/quadmedhoof/basic.ron index a7608d6b22..b1d70e5f7f 100644 --- a/assets/common/abilities/custom/quadmedhoof/basic.ron +++ b/assets/common/abilities/custom/quadmedhoof/basic.ron @@ -1,12 +1,12 @@ BasicMelee( energy_cost: 0, - buildup_duration: 0.45, - swing_duration: 0.5, + buildup_duration: 0.65, + swing_duration: 0.3, recover_duration: 0.35, base_damage: 100, base_poise_damage: 28, knockback: ( strength: 25.0, direction: Away), - range: 1.2, + range: 0.8, max_angle: 50.0, damage_effect: None, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadsmallbasic/singlestrike.ron b/assets/common/abilities/custom/quadsmallbasic/singlestrike.ron index 0448be9345..d494ab6974 100644 --- a/assets/common/abilities/custom/quadsmallbasic/singlestrike.ron +++ b/assets/common/abilities/custom/quadsmallbasic/singlestrike.ron @@ -6,7 +6,7 @@ ComboMelee( damage_increase: 0, base_poise_damage: 10, poise_damage_increase: 0, - knockback: 3.0, + knockback: 1.0, range: 1.5, angle: 50.0, base_buildup_duration: 0.3, diff --git a/assets/common/abilities/custom/theropodbasic/singlestrike.ron b/assets/common/abilities/custom/theropodbasic/singlestrike.ron index 08cf1209e4..9c607d0058 100644 --- a/assets/common/abilities/custom/theropodbasic/singlestrike.ron +++ b/assets/common/abilities/custom/theropodbasic/singlestrike.ron @@ -2,14 +2,14 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 130, + base_damage: 270, damage_increase: 0, base_poise_damage: 40, poise_damage_increase: 0, knockback: 4.0, range: 7.5, angle: 60.0, - base_buildup_duration: 0.5, + base_buildup_duration: 0.4, base_swing_duration: 0.15, hit_timing: 0.5, base_recover_duration: 0.4, diff --git a/assets/common/abilities/custom/theropodbasic/triplestrike.ron b/assets/common/abilities/custom/theropodbasic/triplestrike.ron index c8766e2bbd..7157a77620 100644 --- a/assets/common/abilities/custom/theropodbasic/triplestrike.ron +++ b/assets/common/abilities/custom/theropodbasic/triplestrike.ron @@ -2,14 +2,14 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 140, + base_damage: 300, damage_increase: 0, base_poise_damage: 35, poise_damage_increase: 0, - knockback: 5.0, + knockback: 3.0, range: 7.5, angle: 30.0, - base_buildup_duration: 0.9, + base_buildup_duration: 0.7, base_swing_duration: 0.15, hit_timing: 0.5, base_recover_duration: 0.3, @@ -18,14 +18,14 @@ ComboMelee( ), ( stage: 2, - base_damage: 160, + base_damage: 340, damage_increase: 0, base_poise_damage: 35, poise_damage_increase: 0, - knockback: 5.0, + knockback: 3.0, range: 5.5, angle: 30.0, - base_buildup_duration: 0.5, + base_buildup_duration: 0.4, base_swing_duration: 0.15, hit_timing: 0.5, base_recover_duration: 0.15, @@ -34,14 +34,14 @@ ComboMelee( ), ( stage: 3, - base_damage: 200, + base_damage: 400, damage_increase: 0, base_poise_damage: 35, poise_damage_increase: 0, - knockback: 5.0, + knockback: 25.0, range: 5.5, angle: 30.0, - base_buildup_duration: 0.35, + base_buildup_duration: 0.3, base_swing_duration: 0.125, hit_timing: 0.5, base_recover_duration: 0.9, diff --git a/assets/common/abilities/custom/wendigomagic/frostbomb.ron b/assets/common/abilities/custom/wendigomagic/frostbomb.ron index 5cf95514b2..d49f94fcc1 100644 --- a/assets/common/abilities/custom/wendigomagic/frostbomb.ron +++ b/assets/common/abilities/custom/wendigomagic/frostbomb.ron @@ -3,7 +3,7 @@ BasicRanged( buildup_duration: 0.5, recover_duration: 0.35, projectile: Frostball( - damage: 80.0, + damage: 120.0, radius: 5.0, ), projectile_body: Object(BoltFire), // TODO: Get ice projectile model diff --git a/assets/common/abilities/custom/wendigomagic/singlestrike.ron b/assets/common/abilities/custom/wendigomagic/singlestrike.ron index 39185a761d..efdf2b7bc9 100644 --- a/assets/common/abilities/custom/wendigomagic/singlestrike.ron +++ b/assets/common/abilities/custom/wendigomagic/singlestrike.ron @@ -2,12 +2,12 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 120, + base_damage: 180, damage_increase: 0, base_poise_damage: 40, poise_damage_increase: 0, knockback: 3.0, - range: 3.5, + range: 2.5, angle: 30.0, base_buildup_duration: 0.6, base_swing_duration: 0.2, diff --git a/assets/common/abilities/hammer/singlestrike.ron b/assets/common/abilities/hammer/singlestrike.ron index 85da88b26e..d4aef1f59c 100644 --- a/assets/common/abilities/hammer/singlestrike.ron +++ b/assets/common/abilities/hammer/singlestrike.ron @@ -5,7 +5,7 @@ ComboMelee( damage_increase: 10, base_poise_damage: 20, poise_damage_increase: 0, - knockback: 5.0, + knockback: 3.5, range: 4.5, angle: 50.0, base_buildup_duration: 0.2, diff --git a/assets/common/abilities/hammersimple/doublestrike.ron b/assets/common/abilities/hammersimple/doublestrike.ron index 13d7423c1e..fa333528e3 100644 --- a/assets/common/abilities/hammersimple/doublestrike.ron +++ b/assets/common/abilities/hammersimple/doublestrike.ron @@ -2,12 +2,12 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 90, + base_damage: 240, damage_increase: 10, - base_poise_damage: 30, + base_poise_damage: 40, poise_damage_increase: 0, knockback: 4.0, - range: 3.5, + range: 4.5, angle: 50.0, base_buildup_duration: 0.6, base_swing_duration: 0.08, @@ -18,14 +18,14 @@ ComboMelee( ), ( stage: 2, - base_damage: 130, + base_damage: 320, damage_increase: 15, - base_poise_damage: 30, + base_poise_damage: 40, poise_damage_increase: 0, knockback: 16.0, - range: 1.5, + range: 2.5, angle: 30.0, - base_buildup_duration: 0.5, + base_buildup_duration: 0.6, base_swing_duration: 0.25, hit_timing: 0.5, base_recover_duration: 1.2, @@ -40,5 +40,5 @@ ComboMelee( max_speed_increase: 0.0, scales_from_combo: 0, is_interruptible: false, - ori_modifier: 0.6, + ori_modifier: 0.65, ) diff --git a/assets/common/abilities/sword/triplestrike.ron b/assets/common/abilities/sword/triplestrike.ron index b078f9f180..93ae1ffd07 100644 --- a/assets/common/abilities/sword/triplestrike.ron +++ b/assets/common/abilities/sword/triplestrike.ron @@ -6,7 +6,7 @@ ComboMelee( damage_increase: 10, base_poise_damage: 10, poise_damage_increase: 0, - knockback: 1.0, + knockback: 0.0, range: 4.0, angle: 30.0, base_buildup_duration: 0.1, @@ -38,7 +38,7 @@ ComboMelee( damage_increase: 20, base_poise_damage: 15, poise_damage_increase: 0, - knockback: 4.0, + knockback: 2.0, range: 6.0, angle: 10.0, base_buildup_duration: 0.15, diff --git a/assets/common/cave_scatter/dark_floor.ron b/assets/common/cave_scatter/dark_floor.ron index 1b3f456bff..bd89d3a6dd 100644 --- a/assets/common/cave_scatter/dark_floor.ron +++ b/assets/common/cave_scatter/dark_floor.ron @@ -1,10 +1,11 @@ [ - (20, Velorite), - (30, VeloriteFrag), - (5, CaveMushroom), + (50, Velorite), + (60, VeloriteFrag), + (40, CaveMushroom), (16, SapphireSmall), (12, EmeraldSmall), (15, Cobalt), + (30, Bloodstone), (40, Coal), (10, RubySmall), ] diff --git a/assets/common/cave_scatter/deep_floor.ron b/assets/common/cave_scatter/deep_floor.ron index 66c2e3ce59..c18d335e7a 100644 --- a/assets/common/cave_scatter/deep_floor.ron +++ b/assets/common/cave_scatter/deep_floor.ron @@ -1,14 +1,14 @@ [ - (30, Velorite), + (40, Velorite), (40, VeloriteFrag), - (10, CaveMushroom), + (30, CaveMushroom), (30, Mushroom), - (10, AmethystSmall), - (10, TopazSmall), + (30, AmethystSmall), + (30, TopazSmall), (16, SapphireSmall), (60, CrystalLow), (12, EmeraldSmall), - (5, Cobalt), + (15, Cobalt), (40, Coal), (70, Iron), (10, RubySmall), diff --git a/assets/common/cave_scatter/shallow_floor.ron b/assets/common/cave_scatter/shallow_floor.ron index 290a476ac3..f80601194a 100644 --- a/assets/common/cave_scatter/shallow_floor.ron +++ b/assets/common/cave_scatter/shallow_floor.ron @@ -1,6 +1,6 @@ [ (110, Stones), - (150, ShortGrass), + (250, ShortGrass), (50, CaveMushroom), (50, Mushroom), (30, AmethystSmall), diff --git a/assets/common/items/npc_armor/biped_large/generic.ron b/assets/common/items/npc_armor/biped_large/generic.ron new file mode 100644 index 0000000000..edf92287cb --- /dev/null +++ b/assets/common/items/npc_armor/biped_large/generic.ron @@ -0,0 +1,17 @@ +ItemDef( + name: "Generic Biped Large", + description: "Worn by bipeds.", + kind: Armor(( + kind: Chest("GenericBipedLarge"), + stats: ( + protection: Normal(45.0), + poise_resilience: Normal(1.0), + energy_max: 0, + energy_reward: 0.0, + crit_power: 0.0, + stealth: 0.0, + ), + )), + quality: Moderate, + tags: [], +) \ No newline at end of file diff --git a/assets/common/items/npc_armor/biped_large/mindflayer.ron b/assets/common/items/npc_armor/biped_large/mindflayer.ron index cfddaadbfa..2d1696b29f 100644 --- a/assets/common/items/npc_armor/biped_large/mindflayer.ron +++ b/assets/common/items/npc_armor/biped_large/mindflayer.ron @@ -4,7 +4,7 @@ ItemDef( kind: Armor(( kind: Chest("Mindflayer"), stats: ( - protection: Normal(60.0), + protection: Normal(110.0), poise_resilience: Normal(1.0), energy_max: 0, energy_reward: 0.0, diff --git a/assets/common/items/npc_armor/quadruped_low/generic.ron b/assets/common/items/npc_armor/quadruped_low/generic.ron new file mode 100644 index 0000000000..a5e634b032 --- /dev/null +++ b/assets/common/items/npc_armor/quadruped_low/generic.ron @@ -0,0 +1,17 @@ +ItemDef( + name: "Quad Low Generic", + description: "Scaly.", + kind: Armor(( + kind: Chest("QuadrupedLowGeneric"), + stats: ( + protection: Normal(40.0), + poise_resilience: Normal(0.0), + energy_max: 0, + energy_reward: 0.0, + crit_power: 0.0, + stealth: 0.0, + ), + )), + quality: Moderate, + tags: [], +) diff --git a/assets/common/items/npc_armor/quadruped_low/shell.ron b/assets/common/items/npc_armor/quadruped_low/shell.ron new file mode 100644 index 0000000000..0fcf7e270e --- /dev/null +++ b/assets/common/items/npc_armor/quadruped_low/shell.ron @@ -0,0 +1,17 @@ +ItemDef( + name: "Quad Low Shell", + description: "Shell.", + kind: Armor(( + kind: Chest("QuadrupedLowShell"), + stats: ( + protection: Normal(750.0), + poise_resilience: Normal(0.0), + energy_max: 0, + energy_reward: 0.0, + crit_power: 0.0, + stealth: 0.0, + ), + )), + quality: Moderate, + tags: [], +) diff --git a/assets/common/items/npc_armor/theropod/rugged.ron b/assets/common/items/npc_armor/theropod/rugged.ron new file mode 100644 index 0000000000..01c260de60 --- /dev/null +++ b/assets/common/items/npc_armor/theropod/rugged.ron @@ -0,0 +1,17 @@ +ItemDef( + name: "Theropod Rugged", + description: "stronk.", + kind: Armor(( + kind: Chest("TheropodRugged"), + stats: ( + protection: Normal(80.0), + poise_resilience: Normal(0.0), + energy_max: 0, + energy_reward: 0.0, + crit_power: 0.0, + stealth: 0.0, + ), + )), + quality: Moderate, + tags: [], +) diff --git a/assets/common/items/npc_weapons/unique/quadlowranged.ron b/assets/common/items/npc_weapons/unique/asp.ron similarity index 80% rename from assets/common/items/npc_weapons/unique/quadlowranged.ron rename to assets/common/items/npc_weapons/unique/asp.ron index 2f4bcf2464..4312766e53 100644 --- a/assets/common/items/npc_weapons/unique/quadlowranged.ron +++ b/assets/common/items/npc_weapons/unique/asp.ron @@ -1,5 +1,5 @@ ItemDef( - name: "Quad Low Ranged", + name: "Asp", description: "testing123", kind: Tool(( kind: Natural, @@ -14,5 +14,5 @@ ItemDef( )), quality: Low, tags: [], - ability_spec: Some(Custom("Quad Low Ranged")), + ability_spec: Some(Custom("Asp")), ) \ No newline at end of file diff --git a/assets/common/items/npc_weapons/unique/maneater.ron b/assets/common/items/npc_weapons/unique/maneater.ron new file mode 100644 index 0000000000..93fc149b6c --- /dev/null +++ b/assets/common/items/npc_weapons/unique/maneater.ron @@ -0,0 +1,18 @@ +ItemDef( + name: "Maneater", + description: "testing123", + kind: Tool(( + kind: Natural, + hands: Two, + stats: Direct(( + equip_time_secs: 0.01, + power: 1.0, + poise_strength: 1.0, + speed: 1.0, + crit_chance: 0.0625, + )), + )), + quality: Low, + tags: [], + ability_spec: Some(Custom("Maneater")), +) \ No newline at end of file diff --git a/assets/common/loot_tables/creature/quad_small/generic.ron b/assets/common/loot_tables/creature/quad_small/generic.ron index 431e082771..d7aeb6531a 100644 --- a/assets/common/loot_tables/creature/quad_small/generic.ron +++ b/assets/common/loot_tables/creature/quad_small/generic.ron @@ -1,4 +1,4 @@ [ - (1.0, Item("common.items.crafting_ing.hide.animal_hide")), + (1.0, ItemQuantity("common.items.crafting_ing.hide.animal_hide", 1, 2)), (0.25, Item("common.items.food.meat.beast_small_raw")), ] \ No newline at end of file diff --git a/assets/common/loot_tables/trading.ron b/assets/common/loot_tables/trading.ron index c109d6db31..9db4df6cdb 100644 --- a/assets/common/loot_tables/trading.ron +++ b/assets/common/loot_tables/trading.ron @@ -8,12 +8,17 @@ (1.0, Item("common.items.crafting_ing.hide.animal_hide")), (0.5, Item("common.items.crafting_ing.hide.tough_hide")), (0.2, Item("common.items.crafting_ing.hide.scales")), - (0.08, Item("common.items.crafting_ing.animal_misc.fur")), - (0.08, Item("common.items.crafting_ing.animal_misc.grim_eyeball")), - (0.08, Item("common.items.crafting_ing.animal_misc.icy_fang")), + (0.8, Item("common.items.crafting_ing.animal_misc.fur")), + (0.15, Item("common.items.crafting_ing.animal_misc.grim_eyeball")), + (0.1, Item("common.items.crafting_ing.animal_misc.icy_fang")), (0.08, Item("common.items.crafting_ing.animal_misc.large_horn")), - (0.08, Item("common.items.crafting_ing.animal_misc.lively_vine")), + (0.15, Item("common.items.crafting_ing.animal_misc.lively_vine")), (0.08, Item("common.items.crafting_ing.animal_misc.phoenix_feather")), + (1.0, Item("common.items.food.meat.beast_small_raw")), + (0.6, Item("common.items.food.meat.beast_large_raw")), + (1.3, Item("common.items.food.meat.bird_raw")), + (1.2, Item("common.items.food.meat.fish_raw")), + (0.8, Item("common.items.food.meat.tough_raw")), (0.2, Item("common.items.mineral.ore.bloodstone")), (1.0, Item("common.items.mineral.ore.coal")), (0.4, Item("common.items.mineral.ore.cobalt")), diff --git a/assets/common/recipe_book.ron b/assets/common/recipe_book.ron index 85f6d48076..9f8f48e7dd 100644 --- a/assets/common/recipe_book.ron +++ b/assets/common/recipe_book.ron @@ -418,10 +418,9 @@ (Item("common.items.crafting_ing.animal_misc.raptor_feather"), 6), (Item("common.items.crafting_ing.twigs"), 5), (Item("common.items.crafting_ing.leather.thick_leather"), 4), - (Item("common.items.crafting_ing.cloth.linen"), 5), - (Item("common.items.mineral.gem.ruby"), 1), + (Item("common.items.crafting_ing.cloth.wool"), 5), + (Item("common.items.mineral.gem.sapphire"), 1), (Item("common.items.tool.craftsman_hammer"), 0), - (Item("common.items.crafting_tools.sewing_set"), 0), ], craft_sprite: Some(CraftingBench), ), @@ -431,11 +430,10 @@ (Item("common.items.crafting_ing.animal_misc.raptor_feather"), 6), (Item("common.items.crafting_ing.twigs"), 5), (Item("common.items.crafting_ing.leather.thick_leather"), 4), - (Item("common.items.crafting_ing.cloth.linen"), 5), + (Item("common.items.crafting_ing.cloth.silk"), 5), (Item("common.items.crafting_ing.animal_misc.icy_fang"), 1), (Item("common.items.mineral.gem.ruby"), 1), (Item("common.items.tool.craftsman_hammer"), 0), - (Item("common.items.crafting_tools.sewing_set"), 0), ], craft_sprite: Some(CraftingBench), ), @@ -443,12 +441,11 @@ output: ("common.items.glider.glider_woodraptor", 1), inputs: [ (Item("common.items.crafting_ing.animal_misc.raptor_feather"), 6), - (Item("common.items.crafting_ing.twigs"), 15), - (Item("common.items.crafting_ing.leather.leather_strips"), 5), - (Item("common.items.crafting_ing.cloth.linen"), 5), - (Item("common.items.mineral.gem.ruby"), 1), + (Item("common.items.crafting_ing.twigs"), 5), + (Item("common.items.crafting_ing.leather.thick_leather"), 4), + (Item("common.items.crafting_ing.cloth.lifecloth"), 5), + (Item("common.items.mineral.gem.emerald"), 1), (Item("common.items.tool.craftsman_hammer"), 0), - (Item("common.items.crafting_tools.sewing_set"), 0), ], craft_sprite: Some(CraftingBench), ), @@ -466,7 +463,6 @@ inputs: [ (Item("common.items.crafting_ing.leather.leather_strips"), 8), (Item("common.items.crafting_ing.twigs"), 6), - (Item("common.items.crafting_ing.stones"), 0), ], craft_sprite: Some(CraftingBench), ), @@ -476,7 +472,6 @@ (Item("common.items.crafting_ing.leather.leather_strips"), 4), (Item("common.items.crafting_ing.twigs"), 10), (Item("common.items.mineral.ore.veloritefrag"), 1), - (Item("common.items.crafting_ing.stones"), 0), ], craft_sprite: Some(Anvil), ), @@ -662,8 +657,8 @@ "carapace back": ( output: ("common.items.armor.hide.carapace.back", 1), inputs: [ - (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.hide.carapace"), 3), + (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.leather.leather_strips"), 2), ], craft_sprite: Some(CraftingBench), @@ -671,8 +666,8 @@ "carapace belt": ( output: ("common.items.armor.hide.carapace.belt", 1), inputs: [ - (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.hide.carapace"), 2), + (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.leather.leather_strips"), 2), ], craft_sprite: Some(CraftingBench), @@ -680,8 +675,8 @@ "carapace chest": ( output: ("common.items.armor.hide.carapace.chest", 1), inputs: [ - (Item("common.items.mineral.ingot.steel"), 2), (Item("common.items.crafting_ing.hide.carapace"), 10), + (Item("common.items.mineral.ingot.steel"), 2), (Item("common.items.crafting_ing.leather.leather_strips"), 4), ], craft_sprite: Some(CraftingBench), @@ -689,8 +684,8 @@ "carapace feet": ( output: ("common.items.armor.hide.carapace.foot", 1), inputs: [ - (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.hide.carapace"), 3), + (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.leather.leather_strips"), 2), ], craft_sprite: Some(CraftingBench), @@ -698,8 +693,8 @@ "carapace hands": ( output: ("common.items.armor.hide.carapace.hand", 1), inputs: [ - (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.hide.carapace"), 3), + (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.leather.leather_strips"), 2), ], craft_sprite: Some(CraftingBench), @@ -707,8 +702,8 @@ "carapace pants": ( output: ("common.items.armor.hide.carapace.pants", 1), inputs: [ - (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.hide.carapace"), 8), + (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.leather.leather_strips"), 4), ], craft_sprite: Some(CraftingBench), @@ -716,8 +711,8 @@ "carapace shoulder": ( output: ("common.items.armor.hide.carapace.shoulder", 1), inputs: [ - (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.hide.carapace"), 8), + (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.leather.leather_strips"), 6), ], craft_sprite: Some(CraftingBench), @@ -726,9 +721,9 @@ "primal back": ( output: ("common.items.armor.hide.primal.back", 1), inputs: [ + (Item("common.items.crafting_ing.hide.plate"), 3), (Item("common.items.crafting_ing.animal_misc.large_horn"), 1), (Item("common.items.crafting_ing.cloth.lifecloth"), 1), - (Item("common.items.crafting_ing.hide.plate"), 3), (Item("common.items.crafting_ing.leather.rigid_leather"), 2), ], craft_sprite: Some(CraftingBench), @@ -736,8 +731,8 @@ "primal belt": ( output: ("common.items.armor.hide.primal.belt", 1), inputs: [ - (Item("common.items.crafting_ing.cloth.lifecloth"), 1), (Item("common.items.crafting_ing.hide.plate"), 2), + (Item("common.items.crafting_ing.cloth.lifecloth"), 1), (Item("common.items.crafting_ing.leather.rigid_leather"), 2), ], craft_sprite: Some(CraftingBench), @@ -745,9 +740,9 @@ "primal chest": ( output: ("common.items.armor.hide.primal.chest", 1), inputs: [ + (Item("common.items.crafting_ing.hide.plate"), 10), (Item("common.items.crafting_ing.animal_misc.large_horn"), 1), (Item("common.items.crafting_ing.cloth.lifecloth"), 2), - (Item("common.items.crafting_ing.hide.plate"), 10), (Item("common.items.crafting_ing.leather.rigid_leather"), 4), ], craft_sprite: Some(CraftingBench), @@ -755,8 +750,8 @@ "primal feet": ( output: ("common.items.armor.hide.primal.foot", 1), inputs: [ - (Item("common.items.crafting_ing.cloth.lifecloth"), 1), (Item("common.items.crafting_ing.hide.plate"), 3), + (Item("common.items.crafting_ing.cloth.lifecloth"), 1), (Item("common.items.crafting_ing.leather.rigid_leather"), 2), ], craft_sprite: Some(CraftingBench), @@ -764,8 +759,8 @@ "primal hands": ( output: ("common.items.armor.hide.primal.hand", 1), inputs: [ - (Item("common.items.crafting_ing.cloth.lifecloth"), 1), (Item("common.items.crafting_ing.hide.plate"), 3), + (Item("common.items.crafting_ing.cloth.lifecloth"), 1), (Item("common.items.crafting_ing.leather.rigid_leather"), 2), ], craft_sprite: Some(CraftingBench), @@ -773,9 +768,9 @@ "primal pants": ( output: ("common.items.armor.hide.primal.pants", 1), inputs: [ + (Item("common.items.crafting_ing.hide.plate"), 8), (Item("common.items.crafting_ing.animal_misc.large_horn"), 1), (Item("common.items.crafting_ing.cloth.lifecloth"), 2), - (Item("common.items.crafting_ing.hide.plate"), 8), (Item("common.items.crafting_ing.leather.rigid_leather"), 4), ], craft_sprite: Some(CraftingBench), @@ -783,9 +778,9 @@ "primal shoulder": ( output: ("common.items.armor.hide.primal.shoulder", 1), inputs: [ + (Item("common.items.crafting_ing.hide.plate"), 8), (Item("common.items.crafting_ing.animal_misc.large_horn"), 2), (Item("common.items.crafting_ing.cloth.lifecloth"), 2), - (Item("common.items.crafting_ing.hide.plate"), 8), (Item("common.items.crafting_ing.leather.rigid_leather"), 6), ], craft_sprite: Some(CraftingBench), @@ -794,8 +789,8 @@ "dragonscale back": ( output: ("common.items.armor.hide.dragonscale.back", 1), inputs: [ - (Item("common.items.mineral.ingot.bloodsteel"), 2), (Item("common.items.crafting_ing.hide.dragon_scale"), 3), + (Item("common.items.mineral.ingot.bloodsteel"), 2), (Item("common.items.crafting_ing.leather.rigid_leather"), 1), (Item("common.items.crafting_ing.hide.scales"), 2), ], @@ -804,8 +799,8 @@ "dragonscale belt": ( output: ("common.items.armor.hide.dragonscale.belt", 1), inputs: [ - (Item("common.items.mineral.ingot.bloodsteel"), 2), (Item("common.items.crafting_ing.hide.dragon_scale"), 2), + (Item("common.items.mineral.ingot.bloodsteel"), 2), (Item("common.items.crafting_ing.hide.scales"), 2), ], craft_sprite: Some(CraftingBench), @@ -813,8 +808,8 @@ "dragonscale chest": ( output: ("common.items.armor.hide.dragonscale.chest", 1), inputs: [ - (Item("common.items.mineral.ingot.bloodsteel"), 3), (Item("common.items.crafting_ing.hide.dragon_scale"), 10), + (Item("common.items.mineral.ingot.bloodsteel"), 3), (Item("common.items.crafting_ing.leather.rigid_leather"), 2), (Item("common.items.crafting_ing.hide.scales"), 4), ], @@ -823,8 +818,8 @@ "dragonscale feet": ( output: ("common.items.armor.hide.dragonscale.foot", 1), inputs: [ - (Item("common.items.mineral.ingot.bloodsteel"), 2), (Item("common.items.crafting_ing.hide.dragon_scale"), 3), + (Item("common.items.mineral.ingot.bloodsteel"), 2), (Item("common.items.crafting_ing.hide.scales"), 2), ], craft_sprite: Some(CraftingBench), @@ -832,8 +827,8 @@ "dragonscale hands": ( output: ("common.items.armor.hide.dragonscale.hand", 1), inputs: [ - (Item("common.items.mineral.ingot.bloodsteel"), 2), (Item("common.items.crafting_ing.hide.dragon_scale"), 3), + (Item("common.items.mineral.ingot.bloodsteel"), 2), (Item("common.items.crafting_ing.hide.scales"), 2), ], craft_sprite: Some(CraftingBench), @@ -841,8 +836,8 @@ "dragonscale pants": ( output: ("common.items.armor.hide.dragonscale.pants", 1), inputs: [ - (Item("common.items.mineral.ingot.bloodsteel"), 3), (Item("common.items.crafting_ing.hide.dragon_scale"), 8), + (Item("common.items.mineral.ingot.bloodsteel"), 3), (Item("common.items.crafting_ing.leather.rigid_leather"), 1), (Item("common.items.crafting_ing.hide.scales"), 4), ], @@ -851,8 +846,8 @@ "dragonscale shoulder": ( output: ("common.items.armor.hide.dragonscale.shoulder", 1), inputs: [ - (Item("common.items.mineral.ingot.bloodsteel"), 3), (Item("common.items.crafting_ing.hide.dragon_scale"), 8), + (Item("common.items.mineral.ingot.bloodsteel"), 3), (Item("common.items.crafting_ing.leather.rigid_leather"), 2), (Item("common.items.crafting_ing.hide.scales"), 6), ], @@ -1109,6 +1104,7 @@ inputs: [ (Item("common.items.crafting_ing.cloth.moonweave"), 3), (Item("common.items.crafting_ing.hide.leather_troll"), 3), + (Item("common.items.mineral.ingot.silver"), 1), (Item("common.items.mineral.ingot.cobalt"), 2), (Item("common.items.crafting_tools.sewing_set"), 0), ], @@ -1129,6 +1125,7 @@ inputs: [ (Item("common.items.crafting_ing.cloth.moonweave"), 10), (Item("common.items.crafting_ing.hide.leather_troll"), 5), + (Item("common.items.mineral.ingot.silver"), 1), (Item("common.items.mineral.ingot.cobalt"), 3), (Item("common.items.crafting_tools.sewing_set"), 0), ], @@ -1180,7 +1177,7 @@ inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 3), (Item("common.items.crafting_ing.animal_misc.phoenix_feather"), 1), - (Item("common.items.mineral.ore.silver"), 1), + (Item("common.items.mineral.ingot.gold"), 1), (Item("common.items.crafting_tools.sewing_set"), 0), ], craft_sprite: Some(Loom), @@ -1189,7 +1186,7 @@ output: ("common.items.armor.cloth.sunsilk.belt", 1), inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 2), - (Item("common.items.mineral.ore.silver"), 1), + (Item("common.items.mineral.ingot.gold"), 1), (Item("common.items.crafting_tools.sewing_set"), 0), ], craft_sprite: Some(Loom), @@ -1199,7 +1196,7 @@ inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 10), (Item("common.items.crafting_ing.animal_misc.phoenix_feather"), 2), - (Item("common.items.mineral.ore.silver"), 2), + (Item("common.items.mineral.ingot.gold"), 2), (Item("common.items.crafting_tools.sewing_set"), 0), ], craft_sprite: Some(Loom), @@ -1208,7 +1205,7 @@ output: ("common.items.armor.cloth.sunsilk.foot", 1), inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 3), - (Item("common.items.mineral.ore.silver"), 2), + (Item("common.items.mineral.ingot.gold"), 2), (Item("common.items.crafting_tools.sewing_set"), 0), ], craft_sprite: Some(Loom), @@ -1217,7 +1214,7 @@ output: ("common.items.armor.cloth.sunsilk.hand", 1), inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 3), - (Item("common.items.mineral.ore.silver"), 2), + (Item("common.items.mineral.ingot.gold"), 2), (Item("common.items.crafting_tools.sewing_set"), 0), ], craft_sprite: Some(Loom), @@ -1226,7 +1223,7 @@ output: ("common.items.armor.cloth.sunsilk.pants", 1), inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 8), - (Item("common.items.mineral.ore.silver"), 3), + (Item("common.items.mineral.ingot.gold"), 3), (Item("common.items.crafting_tools.sewing_set"), 0), ], craft_sprite: Some(Loom), @@ -1235,7 +1232,7 @@ output: ("common.items.armor.cloth.sunsilk.shoulder", 1), inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 8), - (Item("common.items.mineral.ore.silver"), 3), + (Item("common.items.mineral.ingot.gold"), 3), (Item("common.items.crafting_tools.sewing_set"), 0), ], craft_sprite: Some(Loom), @@ -1635,13 +1632,13 @@ "tiny leather pouch": ( output: ("common.items.armor.misc.bag.tiny_leather_pouch", 1), inputs: [ - (Item("common.items.crafting_ing.leather.leather_strips"), 6), + (Item("common.items.crafting_ing.leather.leather_strips"), 12), ], ), "knitted red pouch": ( output: ("common.items.armor.misc.bag.knitted_red_pouch", 1), inputs: [ - (Item("common.items.crafting_ing.cloth.linen_red"), 3), + (Item("common.items.crafting_ing.cloth.wool"), 6), (Item("common.items.armor.misc.bag.tiny_red_pouch"), 2), (Item("common.items.crafting_tools.sewing_set"), 0), ], @@ -1649,7 +1646,7 @@ "woven red bag": ( output: ("common.items.armor.misc.bag.woven_red_bag", 1), inputs: [ - (Item("common.items.crafting_ing.cloth.linen_red"), 6), + (Item("common.items.crafting_ing.cloth.silk"), 6), (Item("common.items.armor.misc.bag.knitted_red_pouch"), 1), (Item("common.items.crafting_tools.sewing_set"), 0), ], @@ -1659,7 +1656,7 @@ inputs: [ (Item("common.items.mineral.gem.diamond"), 2), (Item("common.items.crafting_ing.twigs"), 2), - (Item("common.items.crafting_ing.cloth.linen"), 3), + (Item("common.items.crafting_ing.cloth.silk"), 4), (Item("common.items.crafting_ing.leather.leather_strips"), 3), (Item("common.items.armor.misc.bag.tiny_leather_pouch"), 2), (Item("common.items.crafting_tools.sewing_set"), 0), @@ -1669,9 +1666,9 @@ "sturdy red backpack": ( output: ("common.items.armor.misc.bag.sturdy_red_backpack", 1), inputs: [ - (Item("common.items.mineral.gem.diamond"), 2), + (Item("common.items.mineral.gem.amethyst"), 2), (Item("common.items.crafting_ing.cloth.linen_red"), 3), - (Item("common.items.crafting_ing.leather.thick_leather"), 3), + (Item("common.items.crafting_ing.leather.thick_leather"), 6), (Item("common.items.armor.misc.bag.woven_red_bag"), 1), (Item("common.items.crafting_tools.sewing_set"), 0), ], @@ -1689,7 +1686,7 @@ output: ("common.items.armor.misc.bag.mindflayer_spellbag", 1), inputs: [ (Item("common.items.crafting_ing.mindflayer_bag_damaged"), 1), - (Item("common.items.crafting_ing.leather.thick_leather"), 8), + (Item("common.items.crafting_ing.leather.rigid_leather"), 8), (Item("common.items.mineral.gem.diamond"), 4), (Item("common.items.mineral.ore.veloritefrag"), 10), (Item("common.items.crafting_tools.sewing_set"), 0), diff --git a/assets/voxygen/i18n/en/hud/misc.ron b/assets/voxygen/i18n/en/hud/misc.ron index 04b74ef477..d447525a9f 100644 --- a/assets/voxygen/i18n/en/hud/misc.ron +++ b/assets/voxygen/i18n/en/hud/misc.ron @@ -32,9 +32,9 @@ You are welcome to take whatever you need on your journey! Look at the bottom right of the screen to find various things like your bag, the crafting menu and the map. -The crafting menu allows you to create armor, weapons, food and much more! +The crafting stations allow you to create armor, weapons, food and much more! -The wild animals all around town are a great source of Leather Scraps to create some protection against the dangers of the world. +The wild animals all around town are a great source of Animal Hide to create some protection against the dangers of the world. Whenever you feel ready, try to get even better equipment from the many challenges marked on your map! "#, diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 470763465a..72a2f4748f 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -517,6 +517,7 @@ impl Body { Body::Theropod(theropod) => match theropod.species { theropod::Species::Archaeos => 3500, theropod::Species::Odonto => 3000, + theropod::Species::Ntouka => 3000, _ => 1100, }, Body::QuadrupedLow(quadruped_low) => match quadruped_low.species { @@ -527,7 +528,7 @@ impl Body { quadruped_low::Species::Tortoise => 900, quadruped_low::Species::Rocksnapper => 1400, quadruped_low::Species::Pangolin => 400, - quadruped_low::Species::Maneater => 700, + quadruped_low::Species::Maneater => 1300, quadruped_low::Species::Sandshark => 900, quadruped_low::Species::Hakulaq => 500, quadruped_low::Species::Lavadrake => 1600, @@ -685,7 +686,11 @@ impl Body { pub fn base_poise(&self) -> u32 { match self { Body::Humanoid(_) => 100, - Body::BipedLarge(_) => 250, + Body::BipedLarge(biped_large) => match biped_large.species { + biped_large::Species::Mindflayer => 320, + biped_large::Species::Minotaur => 280, + _ => 250, + }, Body::Golem(_) => 300, _ => 100, } diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index c1ced2481c..5c8f48345f 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -197,9 +197,12 @@ fn default_main_tool(body: &Body) -> Item { )), }, Body::QuadrupedLow(quadruped_low) => match quadruped_low.species { - quadruped_low::Species::Maneater | quadruped_low::Species::Asp => Some( - Item::new_from_asset_expect("common.items.npc_weapons.unique.quadlowranged"), - ), + quadruped_low::Species::Maneater => Some(Item::new_from_asset_expect( + "common.items.npc_weapons.unique.maneater", + )), + quadruped_low::Species::Asp => Some(Item::new_from_asset_expect( + "common.items.npc_weapons.unique.asp", + )), quadruped_low::Species::Crocodile | quadruped_low::Species::Alligator | quadruped_low::Species::Salamander => Some(Item::new_from_asset_expect( @@ -414,12 +417,52 @@ impl LoadoutBuilder { }) => self.chest(Some(Item::new_from_asset_expect( "common.items.npc_armor.biped_large.harvester", ))), + Body::BipedLarge(biped_large::Body { + species: + biped_large::Species::Ogre + | biped_large::Species::Cyclops + | biped_large::Species::Blueoni + | biped_large::Species::Redoni + | biped_large::Species::Troll + | biped_large::Species::Wendigo, + .. + }) => self.chest(Some(Item::new_from_asset_expect( + "common.items.npc_armor.biped_large.generic", + ))), Body::Golem(golem::Body { species: golem::Species::ClayGolem, .. }) => self.chest(Some(Item::new_from_asset_expect( "common.items.npc_armor.golem.claygolem", ))), + Body::QuadrupedLow(quadruped_low::Body { + species: + quadruped_low::Species::Basilisk + | quadruped_low::Species::Asp + | quadruped_low::Species::Lavadrake + | quadruped_low::Species::Maneater + | quadruped_low::Species::Rocksnapper + | quadruped_low::Species::Sandshark, + .. + }) => self.chest(Some(Item::new_from_asset_expect( + "common.items.npc_armor.quadruped_low.generic", + ))), + Body::QuadrupedLow(quadruped_low::Body { + species: quadruped_low::Species::Tortoise, + .. + }) => self.chest(Some(Item::new_from_asset_expect( + "common.items.npc_armor.quadruped_low.shell", + ))), + Body::Theropod(theropod::Body { + species: + theropod::Species::Archaeos + | theropod::Species::Yale + | theropod::Species::Ntouka + | theropod::Species::Odonto, + .. + }) => self.chest(Some(Item::new_from_asset_expect( + "common.items.npc_armor.theropod.rugged", + ))), _ => self, }; diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 3d933bd396..eefa5e1a15 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -92,7 +92,7 @@ impl Body { quadruped_low::Species::Alligator => 110.0, quadruped_low::Species::Salamander => 85.0, quadruped_low::Species::Monitor => 160.0, - quadruped_low::Species::Asp => 130.0, + quadruped_low::Species::Asp => 110.0, quadruped_low::Species::Tortoise => 60.0, quadruped_low::Species::Rocksnapper => 70.0, quadruped_low::Species::Pangolin => 120.0, @@ -100,7 +100,7 @@ impl Body { quadruped_low::Species::Sandshark => 160.0, quadruped_low::Species::Hakulaq => 140.0, quadruped_low::Species::Lavadrake => 100.0, - quadruped_low::Species::Basilisk => 120.0, + quadruped_low::Species::Basilisk => 90.0, quadruped_low::Species::Deadwood => 140.0, }, Body::Ship(_) => 0.0, diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index d6dab17616..a4b45e9149 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -1617,8 +1617,10 @@ impl<'a> AgentData<'a> { circle_time: 1, }, "Quad Med Basic" => Tactic::QuadMedBasic, - "Quad Low Ranged" => Tactic::QuadLowRanged, - "Quad Low Breathe" | "Quad Low Beam" => Tactic::QuadLowBeam, + "Asp" | "Maneater" => Tactic::QuadLowRanged, + "Quad Low Breathe" | "Quad Low Beam" | "Basilisk" => { + Tactic::QuadLowBeam + }, "Quad Low Tail" => Tactic::TailSlap, "Quad Low Quick" => Tactic::QuadLowQuick, "Quad Low Basic" => Tactic::QuadLowBasic, diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index bff44eb784..847650313c 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -328,7 +328,7 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) { // Scatter things in caves if cave_depth > 40.0 && cave_depth < 80.0 { - if rng.gen::() < 0.2 * (cave_x.max(0.5).powf(4.0)) && !vein_condition { + if rng.gen::() < 0.14 * (cave_x.max(0.5).powf(4.0)) && !vein_condition { let kind = *Lottery::::load_expect("common.cave_scatter.shallow_floor") .read() @@ -349,7 +349,7 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) { ); } } else if cave_depth < 200.0 && cave_depth > 80.0 { - if rng.gen::() < 0.12 * (cave_x.max(0.5).powf(4.0)) && !vein_condition { + if rng.gen::() < 0.08 * (cave_x.max(0.5).powf(4.0)) && !vein_condition { let kind = *Lottery::::load_expect("common.cave_scatter.deep_floor") .read() @@ -370,7 +370,7 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) { ); } } else { - if rng.gen::() < 0.12 * (cave_x.max(0.5).powf(4.0)) + if rng.gen::() < 0.08 * (cave_x.max(0.5).powf(4.0)) && cave_depth > 40.0 && !vein_condition { @@ -445,7 +445,7 @@ pub fn apply_caves_supplement<'a>( .map_or(true, |b| b.is_fluid()) }) }) { - if RandomField::new(index.seed).chance(wpos2d.into(), 0.0018) + if RandomField::new(index.seed).chance(wpos2d.into(), 0.0014) && cave_base < surface_z as i32 - 40 { let is_hostile: bool; @@ -470,7 +470,7 @@ pub fn apply_caves_supplement<'a>( }; comp::quadruped_low::Body::random_with(dynamic_rng, &species) .into() - } else if cave_depth < 200.0 { + } else if cave_depth < 190.0 { is_hostile = true; let species = match dynamic_rng.gen_range(0..3) { 0 => comp::quadruped_low::Species::Rocksnapper, From a42e6e20b8929c5fde02821a83ef7f5188297d00 Mon Sep 17 00:00:00 2001 From: jshipsey Date: Wed, 16 Jun 2021 21:48:24 -0400 Subject: [PATCH 095/180] basilisk tweaks --- .../common/abilities/ability_set_manifest.ron | 8 +++++--- .../common/abilities/custom/basilisk/dash.ron | 20 +++++++++++++++++++ assets/common/cave_scatter/deep_floor.ron | 2 +- common/src/comp/inventory/loadout_builder.rs | 2 +- world/src/layer/mod.rs | 2 +- 5 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 assets/common/abilities/custom/basilisk/dash.ron diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index a9e33c3b0b..12eb751aad 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -149,9 +149,11 @@ abilities: [], ), Custom("Basilisk"): ( - primary: "common.abilities.custom.basilisk.triplestrike", - secondary: "common.abilities.custom.basilisk.petrify", - abilities: [], + primary: "common.abilities.custom.basilisk.petrify", + secondary: "common.abilities.custom.basilisk.triplestrike", + abilities: [ + (None, "common.abilities.custom.basilisk.dash"), + ], ), Custom("Asp"): ( primary: "common.abilities.custom.asp.singlestrike", diff --git a/assets/common/abilities/custom/basilisk/dash.ron b/assets/common/abilities/custom/basilisk/dash.ron new file mode 100644 index 0000000000..8221e8092d --- /dev/null +++ b/assets/common/abilities/custom/basilisk/dash.ron @@ -0,0 +1,20 @@ +DashMelee( + energy_cost: 0, + base_damage: 120, + scaled_damage: 180, + base_poise_damage: 25, + scaled_poise_damage: 0, + base_knockback: 4.0, + scaled_knockback: 17.0, + range: 2.5, + angle: 45.0, + energy_drain: 0, + forward_speed: 4.0, + buildup_duration: 0.8, + charge_duration: 1.0, + swing_duration: 0.1, + recover_duration: 1.0, + charge_through: true, + is_interruptible: false, + damage_kind: Crushing, +) diff --git a/assets/common/cave_scatter/deep_floor.ron b/assets/common/cave_scatter/deep_floor.ron index c18d335e7a..0594c8679a 100644 --- a/assets/common/cave_scatter/deep_floor.ron +++ b/assets/common/cave_scatter/deep_floor.ron @@ -6,7 +6,7 @@ (30, AmethystSmall), (30, TopazSmall), (16, SapphireSmall), - (60, CrystalLow), + (100, CrystalLow), (12, EmeraldSmall), (15, Cobalt), (40, Coal), diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 5c8f48345f..d4396b2749 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -423,7 +423,7 @@ impl LoadoutBuilder { | biped_large::Species::Cyclops | biped_large::Species::Blueoni | biped_large::Species::Redoni - | biped_large::Species::Troll + | biped_large::Species::Cavetroll | biped_large::Species::Wendigo, .. }) => self.chest(Some(Item::new_from_asset_expect( diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 847650313c..f448f71f79 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -349,7 +349,7 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) { ); } } else if cave_depth < 200.0 && cave_depth > 80.0 { - if rng.gen::() < 0.08 * (cave_x.max(0.5).powf(4.0)) && !vein_condition { + if rng.gen::() < 0.065 * (cave_x.max(0.5).powf(4.0)) && !vein_condition { let kind = *Lottery::::load_expect("common.cave_scatter.deep_floor") .read() From 9a5b95bc1696c9141bf074dce1f97dd1fae77faa Mon Sep 17 00:00:00 2001 From: DaforLynx Date: Thu, 17 Jun 2021 05:49:09 +0000 Subject: [PATCH 096/180] Added npc hurt sfx --- assets/voxygen/audio/sfx.ron | 118 ++++++++++++++---- .../audio/sfx/utterance/adlet_angry1.ogg | 3 + .../audio/sfx/utterance/adlet_angry2.ogg | 3 + .../audio/sfx/utterance/adlet_hurt1.ogg | 3 + .../audio/sfx/utterance/adlet_hurt2.ogg | 3 + .../audio/sfx/utterance/alligator_angry1.ogg | 3 + .../audio/sfx/utterance/alligator_angry2.ogg | 3 + .../audio/sfx/utterance/antelope_angry1.ogg | 3 + .../audio/sfx/utterance/antelope_hurt1.ogg | 3 + .../{bird_angry.ogg => bird_angry1.ogg} | 0 .../utterance/{cow_calm.ogg => cow_calm1.ogg} | 0 ...e_greeting.ogg => humanmale_greeting1.ogg} | 0 .../audio/sfx/utterance/humanmale_hurt1.ogg | 3 + .../audio/sfx/utterance/mandragora_hurt1.ogg | 3 + .../audio/sfx/utterance/maneater_hurt1.ogg | 3 + .../audio/sfx/utterance/marlin_hurt1.ogg | 3 + .../audio/sfx/utterance/mindflayer_hurt1.ogg | 3 + .../{ogre_angry.ogg => ogre_angry1.ogg} | 0 .../utterance/{pig_calm.ogg => pig_calm1.ogg} | 0 .../{saurok_angry.ogg => saurok_angry1.ogg} | 0 .../{sheep_calm.ogg => sheep_calm1.ogg} | 0 .../{wendigo_angry.ogg => wendigo_angry1.ogg} | 0 common/src/comp/agent.rs | 1 + server/src/events/entity_manipulation.rs | 9 +- server/src/sys/agent.rs | 31 ++++- voxygen/src/audio/music.rs | 2 +- voxygen/src/audio/sfx/mod.rs | 47 ++++--- 27 files changed, 203 insertions(+), 44 deletions(-) create mode 100644 assets/voxygen/audio/sfx/utterance/adlet_angry1.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/adlet_angry2.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/adlet_hurt1.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/adlet_hurt2.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/alligator_angry1.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/alligator_angry2.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/antelope_angry1.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/antelope_hurt1.ogg rename assets/voxygen/audio/sfx/utterance/{bird_angry.ogg => bird_angry1.ogg} (100%) rename assets/voxygen/audio/sfx/utterance/{cow_calm.ogg => cow_calm1.ogg} (100%) rename assets/voxygen/audio/sfx/utterance/{humanmale_greeting.ogg => humanmale_greeting1.ogg} (100%) create mode 100644 assets/voxygen/audio/sfx/utterance/humanmale_hurt1.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/mandragora_hurt1.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/maneater_hurt1.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/marlin_hurt1.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/mindflayer_hurt1.ogg rename assets/voxygen/audio/sfx/utterance/{ogre_angry.ogg => ogre_angry1.ogg} (100%) rename assets/voxygen/audio/sfx/utterance/{pig_calm.ogg => pig_calm1.ogg} (100%) rename assets/voxygen/audio/sfx/utterance/{saurok_angry.ogg => saurok_angry1.ogg} (100%) rename assets/voxygen/audio/sfx/utterance/{sheep_calm.ogg => sheep_calm1.ogg} (100%) rename assets/voxygen/audio/sfx/utterance/{wendigo_angry.ogg => wendigo_angry1.ogg} (100%) diff --git a/assets/voxygen/audio/sfx.ron b/assets/voxygen/audio/sfx.ron index 81bca564d1..cb25f11d92 100644 --- a/assets/voxygen/audio/sfx.ron +++ b/assets/voxygen/audio/sfx.ron @@ -831,56 +831,126 @@ ], threshold: 0.2, ), - Utterance(Angry, Wendigo): ( - files: [ - "voxygen.audio.sfx.utterance.wendigo_angry", - ], - threshold: 0.2, - ), Utterance(Angry, BipedLarge): ( files: [ - "voxygen.audio.sfx.utterance.ogre_angry", + "voxygen.audio.sfx.utterance.ogre_angry1", "voxygen.audio.sfx.utterance.ogre_angry2", ], - threshold: 0.2, - ), - Utterance(Angry, Reptile): ( - files: [ - "voxygen.audio.sfx.utterance.saurok_angry", - ], - threshold: 0.2, + threshold: 1.0, ), Utterance(Angry, Bird): ( files: [ - "voxygen.audio.sfx.utterance.bird_angry", + "voxygen.audio.sfx.utterance.bird_angry1", ], - threshold: 0.2, + threshold: 1.0, ), Utterance(Calm, Pig): ( files: [ - "voxygen.audio.sfx.utterance.pig_calm", + "voxygen.audio.sfx.utterance.pig_calm1", ], - threshold: 0.2, + threshold: 1.0, + ), + Utterance(Angry, Adlet): ( + files: [ + "voxygen.audio.sfx.utterance.adlet_angry1", + "voxygen.audio.sfx.utterance.adlet_angry2", + ], + threshold: 1.0, + ), + Utterance(Angry, Alligator): ( + files: [ + "voxygen.audio.sfx.utterance.alligator_angry1", + "voxygen.audio.sfx.utterance.alligator_angry2", + ], + threshold: 1.0, + ), + Utterance(Angry, Antelope): ( + files: [ + "voxygen.audio.sfx.utterance.antelope_angry1", + ], + threshold: 1.0, + ), + Utterance(Angry, Reptile): ( + files: [ + "voxygen.audio.sfx.utterance.alligator_angry1", + "voxygen.audio.sfx.utterance.alligator_angry2", + ], + threshold: 1.0, + ), + Utterance(Angry, Saurok): ( + files: [ + "voxygen.audio.sfx.utterance.saurok_angry1", + ], + threshold: 1.0, + ), + Utterance(Angry, Wendigo): ( + files: [ + "voxygen.audio.sfx.utterance.wendigo_angry1", + ], + threshold: 1.0, ), Utterance(Calm, Cow): ( files: [ - "voxygen.audio.sfx.utterance.cow_calm", + "voxygen.audio.sfx.utterance.cow_calm1", "voxygen.audio.sfx.utterance.cow_calm2", "voxygen.audio.sfx.utterance.cow_calm3", ], - threshold: 0.2, + threshold: 1.0, ), Utterance(Calm, Sheep): ( files: [ - "voxygen.audio.sfx.utterance.sheep_calm", + "voxygen.audio.sfx.utterance.sheep_calm1", ], - threshold: 0.2, + threshold: 1.0, ), Utterance(Greeting, HumanMale): ( files: [ - "voxygen.audio.sfx.utterance.humanmale_greeting", + "voxygen.audio.sfx.utterance.humanmale_greeting1", ], - threshold: 0.2, + threshold: 1.0, + ), + Utterance(Hurt, Adlet): ( + files: [ + "voxygen.audio.sfx.utterance.adlet_hurt1", + "voxygen.audio.sfx.utterance.adlet_hurt2", + ], + threshold: 1.0, + ), + Utterance(Hurt, Antelope): ( + files: [ + "voxygen.audio.sfx.utterance.antelope", + ], + threshold: 1.0, + ), + Utterance(Hurt, HumanMale): ( + files: [ + "voxygen.audio.sfx.utterance.humanmale_hurt1", + ], + threshold: 1.0, + ), + Utterance(Hurt, Lion): ( + files: [ + "voxygen.audio.sfx.utterance.lion_hurt1", + ], + threshold: 1.0, + ), + Utterance(Hurt, Marlin): ( + files: [ + "voxygen.audio.sfx.utterance.marlin_hurt1", + ], + threshold: 1.0, + ), + Utterance(Hurt, Maneater): ( + files: [ + "voxygen.audio.sfx.utterance.maneater_hurt1", + ], + threshold: 1.0, + ), + Utterance(Hurt, Mindflayer): ( + files: [ + "voxygen.audio.sfx.utterance.mindflayer_hurt1", + ], + threshold: 1.0, ), } ) diff --git a/assets/voxygen/audio/sfx/utterance/adlet_angry1.ogg b/assets/voxygen/audio/sfx/utterance/adlet_angry1.ogg new file mode 100644 index 0000000000..2989a25e5e --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/adlet_angry1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:79edc121cf180d76d07d879de2f7886938c562be46d6fe52e7e04cb31384d78a +size 14109 diff --git a/assets/voxygen/audio/sfx/utterance/adlet_angry2.ogg b/assets/voxygen/audio/sfx/utterance/adlet_angry2.ogg new file mode 100644 index 0000000000..4550863e5b --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/adlet_angry2.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b0a280ff8c3556bb4492358a7de1801e138b727c1f9da4878e7737e1bbeafad2 +size 17091 diff --git a/assets/voxygen/audio/sfx/utterance/adlet_hurt1.ogg b/assets/voxygen/audio/sfx/utterance/adlet_hurt1.ogg new file mode 100644 index 0000000000..1d3eb6fa3d --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/adlet_hurt1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2fe322b89f4370904f56ee67e60b4e5019692fd1a7cb4a1743931d169aed0d1c +size 9159 diff --git a/assets/voxygen/audio/sfx/utterance/adlet_hurt2.ogg b/assets/voxygen/audio/sfx/utterance/adlet_hurt2.ogg new file mode 100644 index 0000000000..1c7fe8305a --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/adlet_hurt2.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64173b57b5cfa18f86d83d3fb850cdee3a7a1ec58f0714a314381643f4544d28 +size 12955 diff --git a/assets/voxygen/audio/sfx/utterance/alligator_angry1.ogg b/assets/voxygen/audio/sfx/utterance/alligator_angry1.ogg new file mode 100644 index 0000000000..7980df43f9 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/alligator_angry1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be7e1a79c23e049c46f6ace589907bcb47a4373476dd1c1ff6831f23f90c2b28 +size 20861 diff --git a/assets/voxygen/audio/sfx/utterance/alligator_angry2.ogg b/assets/voxygen/audio/sfx/utterance/alligator_angry2.ogg new file mode 100644 index 0000000000..7f0b1ca572 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/alligator_angry2.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d0e767a3979318558bc583ed97c729025924411b9ac4cf1b40f6fb76997d1215 +size 15381 diff --git a/assets/voxygen/audio/sfx/utterance/antelope_angry1.ogg b/assets/voxygen/audio/sfx/utterance/antelope_angry1.ogg new file mode 100644 index 0000000000..bcc9688a46 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/antelope_angry1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:24def83183120c92f0e4722a83afdebf990758e5cc54f1d31c2688ed12da720c +size 16845 diff --git a/assets/voxygen/audio/sfx/utterance/antelope_hurt1.ogg b/assets/voxygen/audio/sfx/utterance/antelope_hurt1.ogg new file mode 100644 index 0000000000..8816597048 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/antelope_hurt1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3afdd23faea117d7c6f51604a823f349230b37a05714ce14b6c4c47f11eba67f +size 15679 diff --git a/assets/voxygen/audio/sfx/utterance/bird_angry.ogg b/assets/voxygen/audio/sfx/utterance/bird_angry1.ogg similarity index 100% rename from assets/voxygen/audio/sfx/utterance/bird_angry.ogg rename to assets/voxygen/audio/sfx/utterance/bird_angry1.ogg diff --git a/assets/voxygen/audio/sfx/utterance/cow_calm.ogg b/assets/voxygen/audio/sfx/utterance/cow_calm1.ogg similarity index 100% rename from assets/voxygen/audio/sfx/utterance/cow_calm.ogg rename to assets/voxygen/audio/sfx/utterance/cow_calm1.ogg diff --git a/assets/voxygen/audio/sfx/utterance/humanmale_greeting.ogg b/assets/voxygen/audio/sfx/utterance/humanmale_greeting1.ogg similarity index 100% rename from assets/voxygen/audio/sfx/utterance/humanmale_greeting.ogg rename to assets/voxygen/audio/sfx/utterance/humanmale_greeting1.ogg diff --git a/assets/voxygen/audio/sfx/utterance/humanmale_hurt1.ogg b/assets/voxygen/audio/sfx/utterance/humanmale_hurt1.ogg new file mode 100644 index 0000000000..f9db0309e9 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/humanmale_hurt1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bca43c8a18501f7345b2e5314af80c33bca3e464a16a8d9caef5b6e1adb57d5e +size 10669 diff --git a/assets/voxygen/audio/sfx/utterance/mandragora_hurt1.ogg b/assets/voxygen/audio/sfx/utterance/mandragora_hurt1.ogg new file mode 100644 index 0000000000..737590936a --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/mandragora_hurt1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:85a420c21ce7150d4841ad895f065a0bc0d2204b1799c6ec3acb09e2ed65c8ee +size 9531 diff --git a/assets/voxygen/audio/sfx/utterance/maneater_hurt1.ogg b/assets/voxygen/audio/sfx/utterance/maneater_hurt1.ogg new file mode 100644 index 0000000000..cba5563006 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/maneater_hurt1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ee1d5b9976d7db6c4978d865653658eeb2d98b14e7bbb0a5e5fd2d92fc6d0922 +size 22347 diff --git a/assets/voxygen/audio/sfx/utterance/marlin_hurt1.ogg b/assets/voxygen/audio/sfx/utterance/marlin_hurt1.ogg new file mode 100644 index 0000000000..bd24a8d35e --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/marlin_hurt1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b539cb75f734be4cf5353fe99342899c6d4e612ab25de6347d372eca83986b5f +size 20057 diff --git a/assets/voxygen/audio/sfx/utterance/mindflayer_hurt1.ogg b/assets/voxygen/audio/sfx/utterance/mindflayer_hurt1.ogg new file mode 100644 index 0000000000..adc0c22a35 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/mindflayer_hurt1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78902402673d74be5e3abf8980a95963e08fd52b3ec5ad2de7440ee95297a383 +size 15376 diff --git a/assets/voxygen/audio/sfx/utterance/ogre_angry.ogg b/assets/voxygen/audio/sfx/utterance/ogre_angry1.ogg similarity index 100% rename from assets/voxygen/audio/sfx/utterance/ogre_angry.ogg rename to assets/voxygen/audio/sfx/utterance/ogre_angry1.ogg diff --git a/assets/voxygen/audio/sfx/utterance/pig_calm.ogg b/assets/voxygen/audio/sfx/utterance/pig_calm1.ogg similarity index 100% rename from assets/voxygen/audio/sfx/utterance/pig_calm.ogg rename to assets/voxygen/audio/sfx/utterance/pig_calm1.ogg diff --git a/assets/voxygen/audio/sfx/utterance/saurok_angry.ogg b/assets/voxygen/audio/sfx/utterance/saurok_angry1.ogg similarity index 100% rename from assets/voxygen/audio/sfx/utterance/saurok_angry.ogg rename to assets/voxygen/audio/sfx/utterance/saurok_angry1.ogg diff --git a/assets/voxygen/audio/sfx/utterance/sheep_calm.ogg b/assets/voxygen/audio/sfx/utterance/sheep_calm1.ogg similarity index 100% rename from assets/voxygen/audio/sfx/utterance/sheep_calm.ogg rename to assets/voxygen/audio/sfx/utterance/sheep_calm1.ogg diff --git a/assets/voxygen/audio/sfx/utterance/wendigo_angry.ogg b/assets/voxygen/audio/sfx/utterance/wendigo_angry1.ogg similarity index 100% rename from assets/voxygen/audio/sfx/utterance/wendigo_angry.ogg rename to assets/voxygen/audio/sfx/utterance/wendigo_angry1.ogg diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index 7c64f62fca..6b8059cedc 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -261,6 +261,7 @@ pub enum AgentEvent { )>, ), ServerSound(Sound), + Hurt, } #[derive(Copy, Clone, Debug)] diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 0eb466a80c..1dfba60f4e 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -1,7 +1,7 @@ use crate::{ client::Client, comp::{ - agent::{Sound, SoundKind}, + agent::{Agent, AgentEvent, Sound, SoundKind}, biped_large, bird_large, quadruped_low, quadruped_medium, quadruped_small, skills::SkillGroupKind, theropod, PhysicsState, @@ -60,6 +60,13 @@ pub fn handle_damage(server: &Server, entity: EcsEntity, change: HealthChange) { if let Some(mut health) = ecs.write_storage::().get_mut(entity) { health.change_by(change); } + // This if statement filters out anything under 5 damage, for DOT ticks + // TODO: Find a better way to separate direct damage from DOT here + if change.amount < -50 { + if let Some(agent) = ecs.write_storage::().get_mut(entity) { + agent.inbox.push_front(AgentEvent::Hurt); + } + } } pub fn handle_knockback(server: &Server, entity: EcsEntity, impulse: Vec3) { diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index a4b45e9149..fb4f013f69 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -633,11 +633,27 @@ impl<'a> AgentData<'a> { } // Interact if incoming messages if !agent.inbox.is_empty() { - if !matches!(agent.inbox.front(), Some(AgentEvent::ServerSound(_))) { + if matches!( + agent.inbox.front(), + Some(AgentEvent::ServerSound(_)) | Some(AgentEvent::Hurt) + ) { + let sound = agent.inbox.pop_front(); + match sound { + Some(AgentEvent::ServerSound(sound)) => { + agent.sounds_heard.push(sound); + agent.awareness += sound.vol; + }, + Some(AgentEvent::Hurt) => { + // Hurt utterances at random upon receiving damage + if thread_rng().gen::() < 0.4 { + controller.push_event(ControlEvent::Utterance(UtteranceKind::Hurt)); + } + }, + //Note: this should be unreachable + Some(_) | None => return, + } + } else { agent.action_state.timer = 0.1; - } else if let Some(AgentEvent::ServerSound(sound)) = agent.inbox.pop_front() { - agent.sounds_heard.push(sound); - agent.awareness += sound.vol; } } if agent.action_state.timer > 0.0 { @@ -676,6 +692,13 @@ impl<'a> AgentData<'a> { return; } + if let Some(AgentEvent::Hurt) = agent.inbox.pop_front() { + // Hurt utterances at random upon receiving damage + if thread_rng().gen::() < 0.4 { + controller.push_event(ControlEvent::Utterance(UtteranceKind::Hurt)); + } + } + if let Some(Target { target, selected_at, diff --git a/voxygen/src/audio/music.rs b/voxygen/src/audio/music.rs index 5298e006b8..502bb61ced 100644 --- a/voxygen/src/audio/music.rs +++ b/voxygen/src/audio/music.rs @@ -316,7 +316,7 @@ impl MusicMgr { // Adds a bit of randomness between plays let silence_between_tracks_seconds: f32 = if matches!(music_state, MusicState::Activity(MusicActivity::Explore)) { - rng.gen_range(60.0..120.0) + rng.gen_range(90.0..180.0) } else { 0.0 }; diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index 1f5fe7ab89..65623f5bd2 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -91,12 +91,12 @@ use client::Client; use common::{ assets::{self, AssetExt, AssetHandle}, comp::{ - beam, biped_large, humanoid, + beam, biped_large, biped_small, humanoid, item::{ItemKind, ToolKind}, object, poise::PoiseState, - quadruped_medium, quadruped_small, Body, CharacterAbilityType, InventoryUpdateEvent, - UtteranceKind, + quadruped_low, quadruped_medium, quadruped_small, Body, CharacterAbilityType, + InventoryUpdateEvent, UtteranceKind, }, outcome::Outcome, terrain::{BlockKind, TerrainChunk}, @@ -199,7 +199,14 @@ pub enum VoiceKind { Pig, Cow, Canine, - BigCat, + Lion, + Mindflayer, + Marlin, + Maneater, + Adlet, + Antelope, + Alligator, + Saurok, } fn body_to_voice(body: &Body) -> Option { @@ -208,6 +215,11 @@ fn body_to_voice(body: &Body) -> Option { humanoid::BodyType::Female => VoiceKind::HumanFemale, humanoid::BodyType::Male => VoiceKind::HumanMale, }, + Body::QuadrupedLow(body) => match body.species { + quadruped_low::Species::Maneater => VoiceKind::Maneater, + quadruped_low::Species::Alligator => VoiceKind::Alligator, + _ => return None, + }, Body::QuadrupedSmall(body) => match body.species { quadruped_small::Species::Sheep => VoiceKind::Sheep, quadruped_small::Species::Pig | quadruped_small::Species::Boar => VoiceKind::Pig, @@ -218,7 +230,7 @@ fn body_to_voice(body: &Body) -> Option { | quadruped_medium::Species::Tiger | quadruped_medium::Species::Lion | quadruped_medium::Species::Frostfang - | quadruped_medium::Species::Snowleopard => VoiceKind::BigCat, + | quadruped_medium::Species::Snowleopard => VoiceKind::Lion, quadruped_medium::Species::Wolf | quadruped_medium::Species::Roshwalr | quadruped_medium::Species::Tarasque @@ -231,17 +243,24 @@ fn body_to_voice(body: &Body) -> Option { | quadruped_medium::Species::Yak | quadruped_medium::Species::Moose | quadruped_medium::Species::Dreadhorn => VoiceKind::Cow, + quadruped_medium::Species::Antelope => VoiceKind::Antelope, _ => return None, }, Body::BirdMedium(_) | Body::BirdLarge(_) => VoiceKind::Bird, + Body::BipedSmall(body) => match body.species { + biped_small::Species::Adlet => VoiceKind::Adlet, + _ => return None, + }, Body::BipedLarge(body) => match body.species { biped_large::Species::Wendigo => VoiceKind::Wendigo, biped_large::Species::Occultsaurok | biped_large::Species::Mightysaurok - | biped_large::Species::Slysaurok => VoiceKind::Reptile, + | biped_large::Species::Slysaurok => VoiceKind::Saurok, + biped_large::Species::Mindflayer => VoiceKind::Mindflayer, _ => VoiceKind::BipedLarge, }, Body::Theropod(_) | Body::Dragon(_) => VoiceKind::Reptile, + Body::FishSmall(_) | Body::FishMedium(_) => VoiceKind::Marlin, _ => return None, }) } @@ -480,15 +499,15 @@ impl SfxMgr { }, Outcome::Damage { pos, .. } => { let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Damage); - audio.emit_sfx(sfx_trigger_item, *pos, None, false); + audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false); }, Outcome::Block { pos, parry, .. } => { if *parry { let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Parry); - audio.emit_sfx(sfx_trigger_item, *pos, Some(2.0), false); + audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false); } else { let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Block); - audio.emit_sfx(sfx_trigger_item, *pos, Some(2.0), false); + audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false); } }, Outcome::PoiseChange { pos, state, .. } => match state { @@ -496,22 +515,22 @@ impl SfxMgr { PoiseState::Interrupted => { let sfx_trigger_item = triggers.get_key_value(&SfxEvent::PoiseChange(PoiseState::Interrupted)); - audio.emit_sfx(sfx_trigger_item, *pos, None, false); + audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false); }, PoiseState::Stunned => { let sfx_trigger_item = triggers.get_key_value(&SfxEvent::PoiseChange(PoiseState::Stunned)); - audio.emit_sfx(sfx_trigger_item, *pos, None, false); + audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false); }, PoiseState::Dazed => { let sfx_trigger_item = triggers.get_key_value(&SfxEvent::PoiseChange(PoiseState::Dazed)); - audio.emit_sfx(sfx_trigger_item, *pos, None, false); + audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false); }, PoiseState::KnockedDown => { let sfx_trigger_item = triggers.get_key_value(&SfxEvent::PoiseChange(PoiseState::KnockedDown)); - audio.emit_sfx(sfx_trigger_item, *pos, None, false); + audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false); }, }, Outcome::Utterance { pos, kind, body } => { @@ -519,7 +538,7 @@ impl SfxMgr { let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Utterance(*kind, voice)); if let Some(sfx_trigger_item) = sfx_trigger_item { - audio.emit_sfx(Some(sfx_trigger_item), *pos, Some(2.5), false); + audio.emit_sfx(Some(sfx_trigger_item), *pos, Some(1.5), false); } else { debug!( "No utterance sound effect exists for ({:?}, {:?})", From 1af6f685d175e84689b66444a3a51f11a11c861c Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Thu, 17 Jun 2021 15:19:25 +0100 Subject: [PATCH 097/180] Enabled debug lines for Veloren crates only to significantly reduce binary size --- Cargo.toml | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0342211826..23e860d861 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,8 +86,27 @@ opt-level = 3 overflow-checks = false debug-assertions = false lto = true -debug = 1 # line tables so we can have useful backtraces +debug = false panic = "abort" # don't need unwinding so we can skip including the landing pads for that +# line tables so we can have useful backtraces for in-house crates +[profile.release.package."veloren-network"] +debug = 1 +[profile.release.package."veloren-network-protocol"] +debug = 1 +[profile.release.package."veloren-common"] +debug = 1 +[profile.release.package."veloren-common-systems"] +debug = 1 +[profile.release.package."veloren-client"] +debug = 1 +[profile.release.package."veloren-server"] +debug = 1 +[profile.release.package."veloren-server-cli"] +debug = 1 +[profile.release.package."veloren-voxygen"] +debug = 1 +[profile.release.package."veloren-world"] +debug = 1 # used for cargo bench [profile.bench] From ff27c550d105ce51170a57448e5a0bb214eb3513 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Thu, 17 Jun 2021 15:30:09 +0300 Subject: [PATCH 098/180] clippy & code cleaning & test diagnostic - add clippy::pedantic - move internal functions to methods/functions - rename one-letter variables - use tuple pattern matching instead of indexing - make print_sorted fancier, also show prices on `.other` entries --- common/src/comp/inventory/trade_pricing.rs | 528 ++++++++++++--------- 1 file changed, 299 insertions(+), 229 deletions(-) diff --git a/common/src/comp/inventory/trade_pricing.rs b/common/src/comp/inventory/trade_pricing.rs index 12daa3b38b..78fc2b7dda 100644 --- a/common/src/comp/inventory/trade_pricing.rs +++ b/common/src/comp/inventory/trade_pricing.rs @@ -1,3 +1,6 @@ +#![warn(clippy::pedantic)] +//#![warn(clippy::nursery)] + use crate::{ assets::{self, AssetExt}, lottery::{LootSpec, Lottery}, @@ -10,19 +13,19 @@ use lazy_static::lazy_static; use serde::Deserialize; use tracing::{info, warn}; -type Entry = (String, f32, bool); - -type Entries = Vec; const PRICING_DEBUG: bool = false; #[derive(Default, Debug)] pub struct TradePricing { + // items of different good kinds tools: Entries, armor: Entries, potions: Entries, food: Entries, ingredients: Entries, other: Entries, + + // good_scaling of coins coin_scale: f32, // rng: ChaChaRng, @@ -31,6 +34,46 @@ pub struct TradePricing { equality_set: EqualitySet, } +// item asset specifier, probability, whether it's sellable by merchants +type Entry = (String, f32, bool); + +#[derive(Default, Debug)] +struct Entries { + entries: Vec, +} + +impl Entries { + fn add(&mut self, eqset: &EqualitySet, item_name: &str, probability: f32, can_sell: bool) { + let canonical_itemname = eqset + .equivalence_class + .get(item_name) + .map_or(item_name, |i| &**i); + + let old = self + .entries + .iter_mut() + .find(|(name, _, _)| *name == *canonical_itemname); + + // Increase probability if already in entries, or add new entry + if let Some((asset, ref mut old_probability, _)) = old { + if PRICING_DEBUG { + info!("Update {} {}+{}", asset, old_probability, probability); + } + *old_probability += probability; + } else { + if PRICING_DEBUG { + info!("New {} {}", item_name, probability); + } + self.entries + .push((canonical_itemname.to_owned(), probability, can_sell)); + if canonical_itemname != item_name { + // Add the non-canonical item so that it'll show up in merchant inventories + self.entries.push((item_name.to_owned(), 0.0, can_sell)); + } + } + } +} + lazy_static! { static ref TRADE_PRICING: TradePricing = TradePricing::read(); } @@ -47,21 +90,24 @@ impl assets::Asset for ProbabilityFile { } impl From> for ProbabilityFile { - fn from(content: Vec<(f32, LootSpec)>) -> ProbabilityFile { + #[allow(clippy::cast_precision_loss)] + fn from(content: Vec<(f32, LootSpec)>) -> Self { Self { content: content .into_iter() - .flat_map(|(a, b)| match b { - LootSpec::Item(c) => vec![(a, c)].into_iter(), - LootSpec::ItemQuantity(c, d, e) => { - vec![(a * (d + e) as f32 / 2.0, c)].into_iter() + .flat_map(|(p0, loot)| match loot { + LootSpec::Item(asset) => vec![(p0, asset)].into_iter(), + LootSpec::ItemQuantity(asset, a, b) => { + vec![(p0 * (a + b) as f32 / 2.0, asset)].into_iter() }, - LootSpec::LootTable(c) => { - let total = Lottery::::load_expect(&c).read().total(); - ProbabilityFile::load_expect_cloned(&c) + LootSpec::LootTable(table_asset) => { + let total = Lottery::::load_expect(&table_asset) + .read() + .total(); + Self::load_expect_cloned(&table_asset) .content .into_iter() - .map(|(d, e)| (a * d / total, e)) + .map(|(p1, asset)| (p0 * p1 / total, asset)) .collect::>() .into_iter() }, @@ -74,11 +120,12 @@ impl From> for ProbabilityFile { #[derive(Debug, Deserialize)] struct TradingPriceFile { pub loot_tables: Vec<(f32, bool, String)>, - pub good_scaling: Vec<(Good, f32)>, // the amount of Good equivalent to the most common item + // the amount of Good equivalent to the most common item + pub good_scaling: Vec<(Good, f32)>, } impl assets::Asset for TradingPriceFile { - type Loader = assets::LoadFrom; + type Loader = assets::RonLoader; const EXTENSION: &'static str = "ron"; } @@ -95,10 +142,10 @@ impl assets::Compound for EqualitySet { id: &str, ) -> Result { let manifest = cache.load::>>>(id)?; - let mut ret = EqualitySet { + let mut ret = Self { equivalence_class: HashMap::new(), }; - for set in manifest.read().0.iter() { + for set in &manifest.read().0 { let mut iter = set.iter(); if let Some(first) = iter.next() { let first = first.to_string(); @@ -121,151 +168,179 @@ struct RememberedRecipe { input: Vec<(String, u32)>, } +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() { + // most common item has frequency max_scale. avoid NaN + let rescale = scale / max_scale; + for i in entryvec.iter_mut() { + i.1 *= rescale; + } + } + } +} + +fn get_scaling(contents: &AssetGuard, good: Good) -> f32 { + contents + .good_scaling + .iter() + .find(|(good_kind, _)| *good_kind == good) + .map_or(1.0, |(_, scaling)| *scaling) +} + impl TradePricing { const COIN_ITEM: &'static str = "common.items.utility.coins"; const CRAFTING_FACTOR: f32 = 0.95; // increase price a bit compared to sum of ingredients const INVEST_FACTOR: f32 = 0.33; - const UNAVAILABLE_PRICE: f32 = 1000000.0; + const UNAVAILABLE_PRICE: f32 = 1_000_000.0; // add this much of a non-consumed crafting tool price fn get_list(&self, good: Good) -> &[Entry] { match good { - Good::Armor => &self.armor, - Good::Tools => &self.tools, - Good::Potions => &self.potions, - Good::Food => &self.food, - Good::Ingredients => &self.ingredients, + Good::Armor => &self.armor.entries, + Good::Tools => &self.tools.entries, + Good::Potions => &self.potions.entries, + Good::Food => &self.food.entries, + Good::Ingredients => &self.ingredients.entries, _ => &[], } } fn get_list_mut(&mut self, good: Good) -> &mut [Entry] { match good { - Good::Armor => &mut self.armor, - Good::Tools => &mut self.tools, - Good::Potions => &mut self.potions, - Good::Food => &mut self.food, - Good::Ingredients => &mut self.ingredients, + Good::Armor => &mut self.armor.entries, + Good::Tools => &mut self.tools.entries, + Good::Potions => &mut self.potions.entries, + Good::Food => &mut self.food.entries, + Good::Ingredients => &mut self.ingredients.entries, _ => &mut [], } } fn get_list_by_path(&self, name: &str) -> &[Entry] { match name { - "common.items.crafting_ing.mindflayer_bag_damaged" => &self.armor, - _ if name.starts_with("common.items.crafting_ing.") => &self.ingredients, - _ if name.starts_with("common.items.armor.") => &self.armor, - _ if name.starts_with("common.items.glider.") => &self.other, - _ if name.starts_with("common.items.weapons.") => &self.tools, - _ if name.starts_with("common.items.consumable.") => &self.potions, - _ if name.starts_with("common.items.food.") => &self.food, - _ if name.starts_with("common.items.utility.") => &self.other, - _ if name.starts_with("common.items.boss_drops.") => &self.other, - _ if name.starts_with("common.items.mineral.") => &self.ingredients, - _ if name.starts_with("common.items.flowers.") => &self.ingredients, - _ if name.starts_with("common.items.crafting_tools.") => &self.other, - _ if name.starts_with("common.items.lantern.") => &self.other, - _ if name.starts_with("common.items.tool.") => &self.tools, + // Armor + // TODO: balance mindflayer bag price so this isn't needed + "common.items.crafting_ing.mindflayer_bag_damaged" => &self.armor.entries, + _ if name.starts_with("common.items.armor.") => &self.armor.entries, + // Tools + _ if name.starts_with("common.items.weapons.") => &self.tools.entries, + _ if name.starts_with("common.items.tool.") => &self.tools.entries, + // Ingredients + _ if name.starts_with("common.items.crafting_ing.") => &self.ingredients.entries, + _ if name.starts_with("common.items.mineral.") => &self.ingredients.entries, + _ if name.starts_with("common.items.flowers.") => &self.ingredients.entries, + // Potions + _ if name.starts_with("common.items.consumable.") => &self.potions.entries, + // Food + _ if name.starts_with("common.items.food.") => &self.food.entries, + // Other + _ if name.starts_with("common.items.glider.") => &self.other.entries, + _ if name.starts_with("common.items.utility.") => &self.other.entries, + _ if name.starts_with("common.items.boss_drops.") => &self.other.entries, + _ if name.starts_with("common.items.crafting_tools.") => &self.other.entries, + _ if name.starts_with("common.items.lantern.") => &self.other.entries, _ => { - info!("unknown loot item {}", name); - &self.other + warn!("unknown loot item {}", name); + &self.other.entries }, } } fn get_list_by_path_mut(&mut self, name: &str) -> &mut Entries { match name { + // Armor + // TODO: balance mindflayer bag price so this isn't needed "common.items.crafting_ing.mindflayer_bag_damaged" => &mut self.armor, - _ if name.starts_with("common.items.crafting_ing.") => &mut self.ingredients, _ if name.starts_with("common.items.armor.") => &mut self.armor, - _ if name.starts_with("common.items.glider.") => &mut self.other, + // Tools _ if name.starts_with("common.items.weapons.") => &mut self.tools, - _ if name.starts_with("common.items.consumable.") => &mut self.potions, - _ if name.starts_with("common.items.food.") => &mut self.food, - _ if name.starts_with("common.items.utility.") => &mut self.other, - _ if name.starts_with("common.items.boss_drops.") => &mut self.other, + _ if name.starts_with("common.items.tool.") => &mut self.tools, + // Ingredients + _ if name.starts_with("common.items.crafting_ing.") => &mut self.ingredients, _ if name.starts_with("common.items.mineral.") => &mut self.ingredients, _ if name.starts_with("common.items.flowers.") => &mut self.ingredients, + // Potions + _ if name.starts_with("common.items.consumable.") => &mut self.potions, + // Food + _ if name.starts_with("common.items.food.") => &mut self.food, + // Other + _ if name.starts_with("common.items.glider.") => &mut self.other, + _ if name.starts_with("common.items.utility.") => &mut self.other, + _ if name.starts_with("common.items.boss_drops.") => &mut self.other, _ if name.starts_with("common.items.crafting_tools.") => &mut self.other, _ if name.starts_with("common.items.lantern.") => &mut self.other, - _ if name.starts_with("common.items.tool.") => &mut self.tools, _ => { - info!("unknown loot item {}", name); + warn!("unknown loot item {}", name); &mut self.other }, } } - fn read() -> Self { - fn add( - entryvec: &mut Entries, - eqset: &EqualitySet, - itemname: &str, - probability: f32, - can_sell: bool, - ) { - let canonical_itemname = eqset - .equivalence_class - .get(itemname) - .map(|i| &**i) - .unwrap_or(itemname); - let val = entryvec.iter_mut().find(|j| *j.0 == *canonical_itemname); - if let Some(r) = val { - if PRICING_DEBUG { - info!("Update {} {}+{}", r.0, r.1, probability); - } - r.1 += probability; - } else { - if PRICING_DEBUG { - info!("New {} {}", itemname, probability); - } - entryvec.push((canonical_itemname.to_string(), probability, can_sell)); - if canonical_itemname != itemname { - // Add the non-canonical item so that it'll show up in merchant inventories - entryvec.push((itemname.to_string(), 0.0, 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() { - // most common item has frequency max_scale. avoid NaN - let rescale = scale / max_scale; - for i in entryvec.iter_mut() { - i.1 *= rescale; - } - } - } - } - fn get_scaling(contents: &AssetGuard, good: Good) -> f32 { - contents - .good_scaling - .iter() - .find(|i| i.0 == good) - .map(|i| i.1) - .unwrap_or(1.0) - } + // look up price (inverse frequency) of an item + fn price_lookup(&self, eqset: &EqualitySet, requested_name: &str) -> f32 { + let canonical_name = eqset + .equivalence_class + .get(requested_name) + .map_or(requested_name, |name| &**name); - let mut result = TradePricing::default(); - let files = TradingPriceFile::load_expect("common.item_price_calculation"); - let eqset = EqualitySet::load_expect("common.item_price_equality"); - result.equality_set = eqset.read().clone(); - let contents = files.read(); - for i in contents.loot_tables.iter() { + let goods = self.get_list_by_path(canonical_name); + // even if we multiply by INVEST_FACTOR we need to remain + // above UNAVAILABLE_PRICE (add 1.0 to compensate rounding errors) + goods + .iter() + .find(|(name, _, _)| name == canonical_name) + .map_or( + Self::UNAVAILABLE_PRICE / Self::INVEST_FACTOR + 1.0, + |(_, freq, _)| 1.0 / freq, + ) + } + + #[allow(clippy::cast_precision_loss)] + fn calculate_material_cost(&self, r: &RememberedRecipe, eqset: &EqualitySet) -> f32 { + r.input + .iter() + .map(|(name, amount)| { + self.price_lookup(eqset, name) * (*amount as f32).max(Self::INVEST_FACTOR) + }) + .sum() + } + + // re-look up prices and sort the vector by ascending material cost, return + // whether first cost is finite + fn sort_by_price(&self, recipes: &mut Vec, eqset: &EqualitySet) -> bool { + for recipe in recipes.iter_mut() { + recipe.material_cost = self.calculate_material_cost(recipe, eqset); + } + recipes.sort_by(|a, b| a.material_cost.partial_cmp(&b.material_cost).unwrap()); + //info!(?recipes); + recipes + .first() + .filter(|recipe| recipe.material_cost < Self::UNAVAILABLE_PRICE) + .is_some() + } + + #[allow(clippy::cast_precision_loss)] + fn read() -> Self { + let mut result = Self::default(); + let price_config = TradingPriceFile::load_expect("common.item_price_calculation").read(); + let eqset = EqualitySet::load_expect("common.item_price_equality").read(); + result.equality_set = eqset.clone(); + for table in &price_config.loot_tables { if PRICING_DEBUG { - info!(?i); + info!(?table); } - let loot = ProbabilityFile::load_expect(&i.2); - for j in loot.read().content.iter() { - add( - &mut result.get_list_by_path_mut(&j.1), - &eqset.read(), - &j.1, - i.0 * j.0, - i.1, + let (frequency, can_sell, asset_path) = table; + let loot = ProbabilityFile::load_expect(asset_path); + for (p, item_asset) in &loot.read().content { + result.get_list_by_path_mut(item_asset).add( + &eqset, + item_asset, + frequency * p, + *can_sell, ); } } @@ -273,12 +348,13 @@ impl TradePricing { // Apply recipe book let book = default_recipe_book().read(); let mut ordered_recipes: Vec = Vec::new(); - for (_, r) in book.iter() { + for (_, recipe) in book.iter() { + let (ref asset_path, amount) = recipe.output; ordered_recipes.push(RememberedRecipe { - output: r.output.0.id().into(), - amount: r.output.1, - material_cost: TradePricing::UNAVAILABLE_PRICE, - input: r + output: asset_path.id().into(), + amount, + material_cost: Self::UNAVAILABLE_PRICE, + input: recipe .inputs .iter() .filter_map(|&(ref recipe_input, count)| { @@ -291,61 +367,19 @@ impl TradePricing { .collect(), }); } - // look up price (inverse frequency) of an item - fn price_lookup(s: &TradePricing, eqset: &EqualitySet, name: &str) -> f32 { - let name = eqset - .equivalence_class - .get(name) - .map(|i| &**i) - .unwrap_or(name); - let vec = s.get_list_by_path(name); - vec.iter() - .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) - } - fn calculate_material_cost( - s: &TradePricing, - eqset: &EqualitySet, - r: &RememberedRecipe, - ) -> f32 { - r.input - .iter() - .map(|(name, amount)| { - price_lookup(s, eqset, name) * (*amount as f32).max(TradePricing::INVEST_FACTOR) - }) - .sum() - } - // re-look up prices and sort the vector by ascending material cost, return - // whether first cost is finite - fn price_sort( - s: &TradePricing, - eqset: &EqualitySet, - vec: &mut Vec, - ) -> bool { - for e in vec.iter_mut() { - e.material_cost = calculate_material_cost(s, eqset, e); - } - vec.sort_by(|a, b| a.material_cost.partial_cmp(&b.material_cost).unwrap()); - //info!(?vec); - vec.first() - .filter(|recipe| recipe.material_cost < TradePricing::UNAVAILABLE_PRICE) - .is_some() - } + // re-evaluate prices based on crafting tables // (start with cheap ones to avoid changing material prices after evaluation) - while price_sort(&result, &eqset.read(), &mut ordered_recipes) { - ordered_recipes.retain(|e| { - if e.material_cost < 1e-5 { + while result.sort_by_price(&mut ordered_recipes, &eqset) { + ordered_recipes.retain(|recipe| { + if recipe.material_cost < 1e-5 { false - } else if e.material_cost < TradePricing::UNAVAILABLE_PRICE { - let actual_cost = calculate_material_cost(&result, &eqset.read(), e); - add( - &mut result.get_list_by_path_mut(&e.output), - &eqset.read(), - &e.output, - (e.amount as f32) / actual_cost * TradePricing::CRAFTING_FACTOR, + } else if recipe.material_cost < Self::UNAVAILABLE_PRICE { + let actual_cost = result.calculate_material_cost(recipe, &eqset); + result.get_list_by_path_mut(&recipe.output).add( + &eqset, + &recipe.output, + (recipe.amount as f32) / actual_cost * Self::CRAFTING_FACTOR, true, ); false @@ -363,22 +397,31 @@ impl TradePricing { Good::Food, Good::Ingredients, ]; - for &g in good_list.iter() { - sort_and_normalize(result.get_list_mut(g), get_scaling(&contents, g)); + + for good in &good_list { + sort_and_normalize( + result.get_list_mut(*good), + get_scaling(&price_config, *good), + ); let mut materials = result - .get_list(g) + .get_list(*good) .iter() - .map(|i| (i.0.clone(), (g, 1.0 / i.1))) + .map(|i| (i.0.clone(), (*good, 1.0 / i.1))) .collect::>(); result.material_cache.extend(materials.drain(..)); } - result.coin_scale = get_scaling(&contents, Good::Coin); + result.coin_scale = get_scaling(&price_config, Good::Coin); result } + #[allow( + clippy::cast_possible_truncation, + clippy::cast_sign_loss, + clippy::cast_precision_loss + )] fn random_item_impl(&self, good: Good, amount: f32, selling: bool) -> Option { if good == Good::Coin { - Some(TradePricing::COIN_ITEM.into()) + Some(Self::COIN_ITEM.into()) } else { let table = self.get_list(good); if table.is_empty() { @@ -390,8 +433,7 @@ impl TradePricing { .iter() .enumerate() .find(|i| i.1.1 * amount >= 1.0) - .map(|i| i.0) - .unwrap_or(upper - 1); + .map_or(upper - 1, |i| i.0); loop { let index = (rand::random::() * ((upper - lower) as f32)).floor() as usize + lower; @@ -403,21 +445,23 @@ impl TradePricing { } } + #[must_use] pub fn random_item(good: Good, amount: f32, selling: bool) -> Option { TRADE_PRICING.random_item_impl(good, amount, selling) } + #[must_use] pub fn get_material(item: &str) -> (Good, f32) { - if item == TradePricing::COIN_ITEM { + if item == Self::COIN_ITEM { (Good::Coin, 1.0) } else { let item = TRADE_PRICING .equality_set .equivalence_class .get(item) - .map(|i| &**i) - .unwrap_or(item); - TRADE_PRICING.material_cache.get(item).cloned().map_or( + .map_or(item, |i| &**i); + + TRADE_PRICING.material_cache.get(item).copied().map_or( (Good::Terrain(crate::terrain::BiomeKind::Void), 0.0), |(a, b)| (a, b * TRADE_PRICING.coin_scale), ) @@ -436,61 +480,87 @@ impl TradePricing { where F: Fn(&Item, f32) -> String, { - println!("{}", x); + println!("\n======{:^15}======", x); for i in e.iter() { let it = Item::new_from_asset_expect(&i.0); let price = 1.0 / i.1; - println!("{} {:.2} {:?} {}", i.0, price, it.quality, f(&it, i.1)); + println!( + "<{}>\n{:>4.2} {:?} {}", + i.0, + price, + it.quality, + f(&it, i.1) + ); } } - printvec("Armor", &self.armor, |i, p| match &i.kind { - ItemKind::Armor(a) => match a.protection() { - armor::Protection::Invincible => "Invincible".into(), - armor::Protection::Normal(x) => format!("{:.4} prot/val", x * p), - }, - _ => format!("{:?}", i.kind), + printvec("Armor", &self.armor.entries, |i, p| { + if let ItemKind::Armor(a) = &i.kind { + match a.protection() { + armor::Protection::Invincible => "Invincible".into(), + armor::Protection::Normal(x) => format!("{:.4} prot/val", x * p), + } + } else { + format!("{:?}", i.kind) + } }); - printvec("Tools", &self.tools, |i, p| match &i.kind { - ItemKind::Tool(t) => match &t.stats { - tool::StatKind::Direct(d) => { - format!("{:.4} dps/val", d.power * d.speed * p) - }, - tool::StatKind::Modular => "Modular".into(), - }, - _ => format!("{:?}", i.kind), - }); - printvec("Potions", &self.potions, |i, p| match &i.kind { - ItemKind::Consumable { kind: _, effect } => effect - .iter() - .map(|e| match e { - crate::effect::Effect::Buff(b) => { - format!("{:.2} str/val", b.data.strength * p) + printvec("Tools", &self.tools.entries, |i, p| { + if let ItemKind::Tool(t) = &i.kind { + match &t.stats { + tool::StatKind::Direct(d) => { + format!("{:.4} dps/val", d.power * d.speed * p) }, - _ => format!("{:?}", e), - }) - .collect::>() - .join(" "), - _ => format!("{:?}", i.kind), + tool::StatKind::Modular => "Modular".into(), + } + } else { + format!("{:?}", i.kind) + } }); - printvec("Food", &self.food, |i, p| match &i.kind { - ItemKind::Consumable { kind: _, effect } => effect - .iter() - .map(|e| match e { - crate::effect::Effect::Buff(b) => { - format!("{:.2} str/val", b.data.strength * p) - }, - _ => format!("{:?}", e), - }) - .collect::>() - .join(" "), - _ => format!("{:?}", i.kind), + printvec("Potions", &self.potions.entries, |i, p| { + if let ItemKind::Consumable { kind: _, effect } = &i.kind { + effect + .iter() + .map(|e| { + if let crate::effect::Effect::Buff(b) = e { + format!("{:.2} str/val", b.data.strength * p) + } else { + format!("{:?}", e) + } + }) + .collect::>() + .join(" ") + } else { + format!("{:?}", i.kind) + } }); - printvec("Ingredients", &self.ingredients, |i, _p| match &i.kind { - ItemKind::Ingredient { kind } => kind.clone(), - _ => format!("{:?}", i.kind), + printvec("Food", &self.food.entries, |i, p| { + if let ItemKind::Consumable { kind: _, effect } = &i.kind { + effect + .iter() + .map(|e| { + if let crate::effect::Effect::Buff(b) = e { + format!("{:.2} str/val", b.data.strength * p) + } else { + format!("{:?}", e) + } + }) + .collect::>() + .join(" ") + } else { + format!("{:?}", i.kind) + } }); - println!("{} {}", TradePricing::COIN_ITEM, self.coin_scale); + printvec("Ingredients", &self.ingredients.entries, |i, _p| { + if let ItemKind::Ingredient { kind } = &i.kind { + kind.clone() + } else { + format!("{:?}", i.kind) + } + }); + printvec("Other", &self.other.entries, |i, _p| { + format!("{:?}", i.kind) + }); + println!("<{}>\n{}", Self::COIN_ITEM, self.coin_scale); } } From 96cbf60c3f5dc4cb4f2d049874234edc88a1fb5b Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Thu, 17 Jun 2021 19:55:21 +0100 Subject: [PATCH 099/180] Made commands a distinct ClientMsg to avoid possible sanitisation problems for clients --- Cargo.lock | 7 +++++++ client/src/lib.rs | 11 ++++++++--- common/net/src/msg/client.rs | 5 ++++- common/src/event.rs | 2 +- server/src/cmd.rs | 9 +++++++-- server/src/events/mod.rs | 8 ++++---- server/src/lib.rs | 23 ++++++++--------------- server/src/sys/msg/general.rs | 12 ++++++------ voxygen/Cargo.toml | 1 + voxygen/src/hud/chat.rs | 13 ++++++++++++- voxygen/src/hud/mod.rs | 4 ++++ voxygen/src/session/mod.rs | 3 +++ 12 files changed, 65 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 37398ea1ff..4a55a6ba37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -707,6 +707,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "comma" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96677551532ffe910f470bd767a9a7daf9ba53b1f5532e0891dba6c735f692e5" + [[package]] name = "concurrent-queue" version = "1.2.2" @@ -6049,6 +6055,7 @@ dependencies = [ "bincode", "bytemuck", "chrono", + "comma", "conrod_core", "conrod_winit", "copy_dir", diff --git a/client/src/lib.rs b/client/src/lib.rs index 9f09ffb9d0..de2f3d56b4 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -808,9 +808,9 @@ impl Client { //Only in game, terrain ClientGeneral::TerrainChunkRequest { .. } => &mut self.terrain_stream, //Always possible - ClientGeneral::ChatMsg(_) | ClientGeneral::Terminate => { - &mut self.general_stream - }, + ClientGeneral::ChatMsg(_) + | ClientGeneral::Command(_, _) + | ClientGeneral::Terminate => &mut self.general_stream, }; stream.send(msg) }, @@ -1370,6 +1370,11 @@ impl Client { } } + /// Send a command to the server. + pub fn send_command(&mut self, name: String, args: Vec) { + self.send_msg(ClientGeneral::Command(name, args)); + } + /// Remove all cached terrain pub fn clear_terrain(&mut self) { self.state.clear_terrain(); diff --git a/common/net/src/msg/client.rs b/common/net/src/msg/client.rs index 9252e5ec85..0cc811ceb9 100644 --- a/common/net/src/msg/client.rs +++ b/common/net/src/msg/client.rs @@ -80,6 +80,7 @@ pub enum ClientGeneral { }, //Always possible ChatMsg(String), + Command(String, Vec), Terminate, RequestPlayerPhysics { server_authoritative: bool, @@ -129,7 +130,9 @@ impl ClientMsg { c_type == ClientType::Game && presence.is_some() }, //Always possible - ClientGeneral::ChatMsg(_) | ClientGeneral::Terminate => true, + ClientGeneral::ChatMsg(_) + | ClientGeneral::Command(_, _) + | ClientGeneral::Terminate => true, } }, ClientMsg::Ping(_) => true, diff --git a/common/src/event.rs b/common/src/event.rs index ffa178fdd4..efb1ddf535 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -140,7 +140,7 @@ pub enum ServerEvent { ClientDisconnect(EcsEntity, DisconnectReason), ClientDisconnectWithoutPersistence(EcsEntity), ChunkRequest(EcsEntity, Vec2), - ChatCmd(EcsEntity, String), + ChatCmd(EcsEntity, String, Vec), /// Send a chat message to the player from an npc or other player Chat(comp::UnresolvedChatMsg), Aura { diff --git a/server/src/cmd.rs b/server/src/cmd.rs index fcdd197e1a..a10e1276d1 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -52,11 +52,15 @@ use scan_fmt::{scan_fmt, scan_fmt_some}; use tracing::{error, info, warn}; pub trait ChatCommandExt { - fn execute(&self, server: &mut Server, entity: EcsEntity, args: String); + fn execute(&self, server: &mut Server, entity: EcsEntity, args: Vec); } impl ChatCommandExt for ChatCommand { #[allow(clippy::needless_return)] // TODO: Pending review in #587 - fn execute(&self, server: &mut Server, entity: EcsEntity, args: String) { + fn execute(&self, server: &mut Server, entity: EcsEntity, args: Vec) { + // TODO: Pass arguments to commands as Vec, not String, to support + // proper parsing. + let args = args.join(" "); + if let Err(err) = do_command(server, entity, entity, args, self) { server.notify_client( entity, @@ -101,6 +105,7 @@ fn do_command( cmd.keyword() )); } + let handler: CommandHandler = match cmd { ChatCommand::Adminify => handle_adminify, ChatCommand::Airship => handle_spawn_airship, diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index 7ab52e6b60..387f8c0fa1 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -188,8 +188,8 @@ impl Server { ServerEvent::ChunkRequest(entity, key) => { requested_chunks.push((entity, key)); }, - ServerEvent::ChatCmd(entity, cmd) => { - chat_commands.push((entity, cmd)); + ServerEvent::ChatCmd(entity, name, args) => { + chat_commands.push((entity, name, args)); }, ServerEvent::Chat(msg) => { chat_messages.push(msg); @@ -229,8 +229,8 @@ impl Server { self.generate_chunk(entity, key); } - for (entity, cmd) in chat_commands { - self.process_chat_cmd(entity, cmd); + for (entity, name, args) in chat_commands { + self.process_chat_cmd(entity, name, args); } for msg in chat_messages { diff --git a/server/src/lib.rs b/server/src/lib.rs index b7ca95298c..5f5a944863 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -983,16 +983,9 @@ impl Server { ); } - fn process_chat_cmd(&mut self, entity: EcsEntity, cmd: String) { - // Separate string into keyword and arguments. - let sep = cmd.find(' '); - let (kwd, args) = match sep { - Some(i) => (cmd[..i].to_string(), cmd[(i + 1)..].to_string()), - None => (cmd, "".to_string()), - }; - + fn process_chat_cmd(&mut self, entity: EcsEntity, name: String, args: Vec) { // Find the command object and run its handler. - if let Ok(command) = kwd.parse::() { + if let Ok(command) = name.parse::() { command.execute(self, entity, args); } else { #[cfg(feature = "plugins")] @@ -1020,8 +1013,8 @@ impl Server { let rs = plugin_manager.execute_event( &ecs_world, &plugin_api::event::ChatCommandEvent { - command: kwd.clone(), - command_args: args.split(' ').map(|x| x.to_owned()).collect(), + command: name.clone(), + command_args: args.clone(), player: plugin_api::event::Player { id: uid }, }, ); @@ -1035,7 +1028,7 @@ impl Server { format!( "Unknown command '/{}'.\nType '/help' for available \ commands", - kwd + name ), ), ); @@ -1059,7 +1052,7 @@ impl Server { comp::ChatType::CommandError, format!( "Error occurred while executing command '/{}'.\n{}", - kwd, e + name, e ), ), ); @@ -1068,7 +1061,7 @@ impl Server { } }, Err(e) => { - error!(?e, "Can't execute command {} {}", kwd, args); + error!(?e, "Can't execute command {} {:?}", name, args); self.notify_client( entity, ServerGeneral::server_msg( @@ -1076,7 +1069,7 @@ impl Server { format!( "Internal error while executing '/{}'.\nContact the server \ administrator", - kwd + name ), ), ); diff --git a/server/src/sys/msg/general.rs b/server/src/sys/msg/general.rs index 26eaebb489..664efef403 100644 --- a/server/src/sys/msg/general.rs +++ b/server/src/sys/msg/general.rs @@ -29,12 +29,7 @@ impl Sys { if player.is_some() { match validate_chat_msg(&message) { Ok(()) => { - if let Some(message) = message.strip_prefix('/') { - if !message.is_empty() { - let argv = String::from(message); - server_emitter.emit(ServerEvent::ChatCmd(entity, argv)); - } - } else if let Some(from) = uids.get(entity) { + if let Some(from) = uids.get(entity) { const CHAT_MODE_DEFAULT: &ChatMode = &ChatMode::default(); let mode = chat_modes.get(entity).unwrap_or(CHAT_MODE_DEFAULT); // Send chat message @@ -52,6 +47,11 @@ impl Sys { } } }, + ClientGeneral::Command(name, args) => { + if player.is_some() { + server_emitter.emit(ServerEvent::ChatCmd(entity, name, args)); + } + }, ClientGeneral::Terminate => { debug!(?entity, "Client send message to terminate session"); server_emitter.emit(ServerEvent::ClientDisconnect( diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 150f3d35fb..70cb8f3b90 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -78,6 +78,7 @@ backtrace = "0.3.40" bincode = "1.3.1" chrono = { version = "0.4.9", features = ["serde"] } cpal = "0.13" +comma = "0.1" copy_dir = "0.1.2" crossbeam-utils = "0.8.1" crossbeam-channel = "0.5" diff --git a/voxygen/src/hud/chat.rs b/voxygen/src/hud/chat.rs index 9bcd04ce3a..43283d68a0 100644 --- a/voxygen/src/hud/chat.rs +++ b/voxygen/src/hud/chat.rs @@ -168,6 +168,7 @@ pub struct State { pub enum Event { TabCompletionStart(String), SendMessage(String), + SendCommand(String, Vec), Focus(Id), ChangeChatTab(Option), ShowChatTabSettings(usize), @@ -645,7 +646,17 @@ impl<'a> Widget for Chat<'a> { s.history.truncate(self.history_max); } }); - events.push(Event::SendMessage(msg)); + if let Some(msg) = msg.strip_prefix('/') { + match msg.parse::() { + Ok(cmd) => events.push(Event::SendCommand(cmd.name, cmd.arguments)), + Err(err) => self.new_messages.push_back(ChatMsg { + chat_type: ChatType::CommandError, + message: err.to_string(), + }), + } + } else { + events.push(Event::SendMessage(msg)); + } } events } diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index a012c63847..a61df245bc 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -375,6 +375,7 @@ pub struct HudInfo { #[derive(Clone)] pub enum Event { SendMessage(String), + SendCommand(String, Vec), CharacterSelection, UseSlot { @@ -2722,6 +2723,9 @@ impl Hud { chat::Event::SendMessage(message) => { events.push(Event::SendMessage(message)); }, + chat::Event::SendCommand(name, args) => { + events.push(Event::SendCommand(name, args)); + }, chat::Event::Focus(focus_id) => { self.to_focus = Some(Some(focus_id)); }, diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 104459f2b8..d4e2e4cb39 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -1032,6 +1032,9 @@ impl PlayState for SessionState { // TODO: Handle result self.client.borrow_mut().send_chat(msg); }, + HudEvent::SendCommand(name, args) => { + self.client.borrow_mut().send_command(name, args); + }, HudEvent::CharacterSelection => { self.client.borrow_mut().request_remove_character() }, From 0067cea5602c976c8be6b847fe0add8d139e9b18 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Thu, 17 Jun 2021 19:55:46 +0100 Subject: [PATCH 100/180] Made /alias moderator-only --- common/src/cmd.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/common/src/cmd.rs b/common/src/cmd.rs index 892da21885..54c00c9af4 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -295,7 +295,11 @@ impl ChatCommand { "Spawns an airship", Some(Admin), ), - ChatCommand::Alias => cmd(vec![Any("name", Required)], "Change your alias", None), + ChatCommand::Alias => cmd( + vec![Any("name", Required)], + "Change your alias", + Some(Moderator), + ), ChatCommand::ApplyBuff => cmd( vec![ Enum("buff", BUFFS.clone(), Required), From f8008afa85b534e1eb7dfa21c9006962451a5290 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Thu, 17 Jun 2021 21:50:31 +0300 Subject: [PATCH 101/180] trading.ron rebalance - Name all section in trading.ron - Balance post-meat materials - Make gems more rarer and expensive --- assets/common/cave_scatter/shallow_floor.ron | 4 +- assets/common/loot_tables/materials/gems.ron | 8 +-- .../loot_tables/materials/underground.ron | 4 +- assets/common/loot_tables/trading.ron | 66 +++++++++---------- 4 files changed, 41 insertions(+), 41 deletions(-) diff --git a/assets/common/cave_scatter/shallow_floor.ron b/assets/common/cave_scatter/shallow_floor.ron index f80601194a..894aa3d97b 100644 --- a/assets/common/cave_scatter/shallow_floor.ron +++ b/assets/common/cave_scatter/shallow_floor.ron @@ -3,8 +3,8 @@ (250, ShortGrass), (50, CaveMushroom), (50, Mushroom), - (30, AmethystSmall), - (15, TopazSmall), + (5, AmethystSmall), + (5, TopazSmall), (15, Tin), (12, Copper), (15, Iron), diff --git a/assets/common/loot_tables/materials/gems.ron b/assets/common/loot_tables/materials/gems.ron index 226beed85a..1df71497b1 100644 --- a/assets/common/loot_tables/materials/gems.ron +++ b/assets/common/loot_tables/materials/gems.ron @@ -1,8 +1,8 @@ [ - (3.0, Item("common.items.mineral.gem.topaz")), - (3.0, Item("common.items.mineral.gem.amethyst")), + (6.0, Item("common.items.mineral.gem.topaz")), + (8.0, Item("common.items.mineral.gem.amethyst")), (1.6, Item("common.items.mineral.gem.sapphire")), (1.2, Item("common.items.mineral.gem.emerald")), (0.8, Item("common.items.mineral.gem.ruby")), - (0.4, Item("common.items.mineral.gem.diamond")), -] \ No newline at end of file + (0.3, Item("common.items.mineral.gem.diamond")), +] diff --git a/assets/common/loot_tables/materials/underground.ron b/assets/common/loot_tables/materials/underground.ron index 9147aa87c2..88a247006c 100644 --- a/assets/common/loot_tables/materials/underground.ron +++ b/assets/common/loot_tables/materials/underground.ron @@ -2,5 +2,5 @@ (2.0, Item("common.items.crafting_ing.stones")), (0.5, Item("common.items.mineral.ore.veloritefrag")), (0.25, Item("common.items.mineral.ore.velorite")), - (0.25, LootTable("common.loot_tables.materials.gems")), -] \ No newline at end of file + (0.15, LootTable("common.loot_tables.materials.gems")), +] diff --git a/assets/common/loot_tables/trading.ron b/assets/common/loot_tables/trading.ron index 9db4df6cdb..84d002278c 100644 --- a/assets/common/loot_tables/trading.ron +++ b/assets/common/loot_tables/trading.ron @@ -1,38 +1,38 @@ // Loot table that exists purely for price rationalisation +// Please keep it sorting by rarity so it's easier to reason about things [ - (1.0, Item("common.items.crafting_ing.honey")), - (1.5, Item("common.items.crafting_ing.leather.leather_strips")), - (0.08, Item("common.items.crafting_ing.leather.rigid_leather")), - (1.0, Item("common.items.crafting_ing.leather.simple_leather")), - (0.4, Item("common.items.crafting_ing.leather.thick_leather")), - (1.0, Item("common.items.crafting_ing.hide.animal_hide")), - (0.5, Item("common.items.crafting_ing.hide.tough_hide")), - (0.2, Item("common.items.crafting_ing.hide.scales")), - (0.8, Item("common.items.crafting_ing.animal_misc.fur")), - (0.15, Item("common.items.crafting_ing.animal_misc.grim_eyeball")), - (0.1, Item("common.items.crafting_ing.animal_misc.icy_fang")), - (0.08, Item("common.items.crafting_ing.animal_misc.large_horn")), - (0.15, Item("common.items.crafting_ing.animal_misc.lively_vine")), - (0.08, Item("common.items.crafting_ing.animal_misc.phoenix_feather")), - (1.0, Item("common.items.food.meat.beast_small_raw")), - (0.6, Item("common.items.food.meat.beast_large_raw")), - (1.3, Item("common.items.food.meat.bird_raw")), - (1.2, Item("common.items.food.meat.fish_raw")), - (0.8, Item("common.items.food.meat.tough_raw")), - (0.2, Item("common.items.mineral.ore.bloodstone")), - (1.0, Item("common.items.mineral.ore.coal")), - (0.4, Item("common.items.mineral.ore.cobalt")), - (1.5, Item("common.items.mineral.ore.tin")), - (1.5, Item("common.items.mineral.ore.copper")), + // Ores (0.03, Item("common.items.mineral.ore.gold")), - (0.8, Item("common.items.mineral.ore.iron")), - (0.05, Item("common.items.mineral.ore.silver")), - (1.2, Item("common.items.mineral.ore.velorite")), + (0.045, Item("common.items.mineral.ore.silver")), + (0.1, Item("common.items.mineral.ore.bloodstone")), + (0.2, Item("common.items.mineral.ore.cobalt")), + (0.25, Item("common.items.mineral.ore.coal")), + (0.3, Item("common.items.mineral.ore.iron")), + (0.5, Item("common.items.mineral.ore.velorite")), (0.6, Item("common.items.mineral.ore.veloritefrag")), - (0.8, Item("common.items.mineral.gem.amethyst")), - (0.2, Item("common.items.mineral.gem.diamond")), - (0.6, Item("common.items.mineral.gem.emerald")), - (0.4, Item("common.items.mineral.gem.ruby")), - (0.4, Item("common.items.mineral.gem.sapphire")), - (0.9, Item("common.items.mineral.gem.topaz")), + (1.5, Item("common.items.mineral.ore.copper")), + (1.5, Item("common.items.mineral.ore.tin")), + // Animal Hide + (0.1, Item("common.items.crafting_ing.leather.rigid_leather")), + (0.2, Item("common.items.crafting_ing.hide.scales")), + (0.4, Item("common.items.crafting_ing.leather.thick_leather")), + (0.7, Item("common.items.crafting_ing.hide.tough_hide")), + (1.0, Item("common.items.crafting_ing.hide.animal_hide")), + (1.2, Item("common.items.crafting_ing.leather.simple_leather")), + (1.5, Item("common.items.crafting_ing.leather.leather_strips")), + // Mob Drops + (0.01, Item("common.items.crafting_ing.animal_misc.phoenix_feather")), + (0.08, Item("common.items.crafting_ing.animal_misc.large_horn")), + (0.1, Item("common.items.crafting_ing.animal_misc.icy_fang")), + (0.15, Item("common.items.crafting_ing.animal_misc.grim_eyeball")), + (0.15, Item("common.items.crafting_ing.animal_misc.lively_vine")), + (1.2, Item("common.items.crafting_ing.animal_misc.fur")), + // Meats + (0.6, Item("common.items.food.meat.beast_large_raw")), + (0.8, Item("common.items.food.meat.tough_raw")), + (1.0, Item("common.items.food.meat.beast_small_raw")), + (1.2, Item("common.items.food.meat.fish_raw")), + (1.3, Item("common.items.food.meat.bird_raw")), + // Others + (1.0, Item("common.items.crafting_ing.honey")), ] From 54b339862a1fba68a9ad01416265a3f2e60b14a6 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Thu, 17 Jun 2021 23:45:34 +0300 Subject: [PATCH 102/180] tweak item_price_calculation coefficients --- assets/common/item_price_calculation.ron | 18 +++++++++--------- assets/common/loot_tables/materials/gems.ron | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/assets/common/item_price_calculation.ron b/assets/common/item_price_calculation.ron index 875321c988..f5d8388518 100644 --- a/assets/common/item_price_calculation.ron +++ b/assets/common/item_price_calculation.ron @@ -2,16 +2,16 @@ loot_tables: [ // balance the loot tables against each other (higher= more common= smaller price) // Weapons - (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"), + (32.0, true, "common.loot_tables.weapons.starter"), + (16.0, true, "common.loot_tables.weapons.tier-0"), + (8.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.05, false, "common.loot_tables.weapons.cave"), - (0.04, false, "common.loot_tables.weapons.legendary"), + (0.025, false, "common.loot_tables.weapons.cultist"), + (0.025, false, "common.loot_tables.weapons.cave"), + (0.02, false, "common.loot_tables.weapons.legendary"), // Armor (20.0, true, "common.loot_tables.armor.cloth"), (1.0, true, "common.loot_tables.armor.twigs"), @@ -37,7 +37,7 @@ good_scaling: [ (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.10), // common.items.weapons.staff.starter_staff - (Ingredients, 0.15), // common.items.crafting_ing.leather_scraps + (Armor, 0.5), // common.items.armor.misc.pants.worker_blue + (Tools, 0.25), // common.items.weapons.staff.starter_staff + (Ingredients, 0.25), // common.items.crafting_ing.leather_scraps ]) diff --git a/assets/common/loot_tables/materials/gems.ron b/assets/common/loot_tables/materials/gems.ron index 1df71497b1..572025896a 100644 --- a/assets/common/loot_tables/materials/gems.ron +++ b/assets/common/loot_tables/materials/gems.ron @@ -4,5 +4,5 @@ (1.6, Item("common.items.mineral.gem.sapphire")), (1.2, Item("common.items.mineral.gem.emerald")), (0.8, Item("common.items.mineral.gem.ruby")), - (0.3, Item("common.items.mineral.gem.diamond")), + (0.4, Item("common.items.mineral.gem.diamond")), ] From d4bf4bf51bd4c07a23be20c6b7c8ccbc409460b1 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 18 Jun 2021 00:03:51 +0300 Subject: [PATCH 103/180] Remove high tier ignots and big potions from trade --- assets/common/item_price_calculation.ron | 9 ++++++++- assets/common/loot_tables/trading.ron | 13 ++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/assets/common/item_price_calculation.ron b/assets/common/item_price_calculation.ron index f5d8388518..8b4416054e 100644 --- a/assets/common/item_price_calculation.ron +++ b/assets/common/item_price_calculation.ron @@ -25,7 +25,14 @@ loot_tables: [ (0.4, true, "common.loot_tables.food.wild_ingredients"), (0.2, true, "common.loot_tables.food.prepared"), // Potions - (0.2, true, "common.loot_tables.consumable.potion"), + // + // crafted from food, no need to duplicate it here. + // Big potions aren't crafted, but our potions + // from merchants are already abused + // + // Place them back we will have better situation with potions + // and economy. + // // Misc (0.1, true, "common.loot_tables.consumable.throwable"), (0.7, true, "common.loot_tables.consumable.misc"), diff --git a/assets/common/loot_tables/trading.ron b/assets/common/loot_tables/trading.ron index 84d002278c..0f6c2373e5 100644 --- a/assets/common/loot_tables/trading.ron +++ b/assets/common/loot_tables/trading.ron @@ -2,10 +2,11 @@ // Please keep it sorting by rarity so it's easier to reason about things [ // Ores - (0.03, Item("common.items.mineral.ore.gold")), - (0.045, Item("common.items.mineral.ore.silver")), - (0.1, Item("common.items.mineral.ore.bloodstone")), - (0.2, Item("common.items.mineral.ore.cobalt")), + // Uncomment when bug with crafting doesn't propagating can_sell will be fixed + // (0.03, Item("common.items.mineral.ore.gold")), + // (0.045, Item("common.items.mineral.ore.silver")), + // (0.1, Item("common.items.mineral.ore.bloodstone")), + // (0.2, Item("common.items.mineral.ore.cobalt")), (0.25, Item("common.items.mineral.ore.coal")), (0.3, Item("common.items.mineral.ore.iron")), (0.5, Item("common.items.mineral.ore.velorite")), @@ -14,11 +15,9 @@ (1.5, Item("common.items.mineral.ore.tin")), // Animal Hide (0.1, Item("common.items.crafting_ing.leather.rigid_leather")), - (0.2, Item("common.items.crafting_ing.hide.scales")), - (0.4, Item("common.items.crafting_ing.leather.thick_leather")), + (0.4, Item("common.items.crafting_ing.hide.scales")), (0.7, Item("common.items.crafting_ing.hide.tough_hide")), (1.0, Item("common.items.crafting_ing.hide.animal_hide")), - (1.2, Item("common.items.crafting_ing.leather.simple_leather")), (1.5, Item("common.items.crafting_ing.leather.leather_strips")), // Mob Drops (0.01, Item("common.items.crafting_ing.animal_misc.phoenix_feather")), From 6b335a105e354c37d94491fb723e812eab2f5934 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 18 Jun 2021 00:13:42 +0300 Subject: [PATCH 104/180] balance scale to catch steel --- assets/common/loot_tables/trading.ron | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/common/loot_tables/trading.ron b/assets/common/loot_tables/trading.ron index 0f6c2373e5..559a023911 100644 --- a/assets/common/loot_tables/trading.ron +++ b/assets/common/loot_tables/trading.ron @@ -15,7 +15,7 @@ (1.5, Item("common.items.mineral.ore.tin")), // Animal Hide (0.1, Item("common.items.crafting_ing.leather.rigid_leather")), - (0.4, Item("common.items.crafting_ing.hide.scales")), + (0.2, Item("common.items.crafting_ing.hide.scales")), (0.7, Item("common.items.crafting_ing.hide.tough_hide")), (1.0, Item("common.items.crafting_ing.hide.animal_hide")), (1.5, Item("common.items.crafting_ing.leather.leather_strips")), From fb157082a8c1a3ba90e5a053e9e4a4a1d8bc74e8 Mon Sep 17 00:00:00 2001 From: donovanlank Date: Thu, 17 Jun 2021 18:40:29 -0700 Subject: [PATCH 105/180] made it so Cool Blue Lantern is actually blue --- assets/common/items/lantern/blue_0.ron | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/common/items/lantern/blue_0.ron b/assets/common/items/lantern/blue_0.ron index 7de0286462..a464e756a7 100644 --- a/assets/common/items/lantern/blue_0.ron +++ b/assets/common/items/lantern/blue_0.ron @@ -4,11 +4,11 @@ ItemDef( kind: Lantern( ( kind: "Blue0", - color: (r: 255, g: 128, b: 26), - strength_thousandths: 6000, - flicker_thousandths: 250, + color: (r: 55, g: 100, b: 255), + strength_thousandths: 7000, + flicker_thousandths: 300, ), ), - quality: Common, + quality: High, tags: [Utility], ) From 247606f8d5c212748c5badba2d9fe14116461016 Mon Sep 17 00:00:00 2001 From: Imbris Date: Wed, 16 Jun 2021 02:23:13 -0400 Subject: [PATCH 106/180] Log the raw client error at the ERROR level and include the dates in the message displayed in the UI --- voxygen/src/menu/main/mod.rs | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index ca4bd328a4..e2b104027a 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -135,7 +135,7 @@ impl PlayState for MainMenuState { }, Some(InitMsg::Done(Err(e))) => { self.init = InitState::None; - tracing::trace!(?e, "raw Client Init error"); + error!(?e, "Client Init failed raw error"); let e = get_client_msg_error(e, &global_state.i18n); // Log error for possible additional use later or incase that the error // displayed is cut of. @@ -336,21 +336,26 @@ impl PlayState for MainMenuState { } } -fn get_client_msg_error(e: client_init::Error, localized_strings: &LocalizationHandle) -> String { +fn get_client_msg_error( + error: client_init::Error, + localized_strings: &LocalizationHandle, +) -> String { let localization = localized_strings.read(); // When a network error is received and there is a mismatch between the client // and server version it is almost definitely due to this mismatch rather than // a true networking error. - let net_e = |error: String, mismatched_server_info: Option| -> String { + let net_error = |error: String, mismatched_server_info: Option| -> String { if let Some(server_info) = mismatched_server_info { format!( - "{} {}: {} {}: {}", + "{} {}: {} ({}) {}: {} ({})", localization.get("main.login.network_wrong_version"), localization.get("main.login.client_version"), - common::util::GIT_HASH.to_string(), + &*common::util::GIT_HASH, + &*common::util::GIT_DATE, localization.get("main.login.server_version"), - server_info.git_hash + server_info.git_hash, + server_info.git_date, ) } else { format!( @@ -362,7 +367,7 @@ fn get_client_msg_error(e: client_init::Error, localized_strings: &LocalizationH }; use client::Error; - match e { + match error { InitError::ClientError { error, mismatched_server_info, @@ -389,15 +394,15 @@ fn get_client_msg_error(e: client_init::Error, localized_strings: &LocalizationH Error::InvalidCharacter => localization.get("main.login.invalid_character").into(), Error::NetworkErr(NetworkError::ConnectFailed(NetworkConnectError::Handshake( InitProtocolError::WrongVersion(_), - ))) => net_e( + ))) => net_error( localization .get("main.login.network_wrong_version") .to_owned(), mismatched_server_info, ), - Error::NetworkErr(e) => net_e(e.to_string(), mismatched_server_info), - Error::ParticipantErr(e) => net_e(e.to_string(), mismatched_server_info), - Error::StreamErr(e) => net_e(e.to_string(), mismatched_server_info), + Error::NetworkErr(e) => net_error(e.to_string(), mismatched_server_info), + Error::ParticipantErr(e) => net_error(e.to_string(), mismatched_server_info), + Error::StreamErr(e) => net_error(e.to_string(), mismatched_server_info), Error::HostnameLookupFailed(e) => { format!("{}: {}", localization.get("main.login.server_not_found"), e) }, @@ -419,9 +424,7 @@ fn get_client_msg_error(e: client_init::Error, localized_strings: &LocalizationH client::AuthClientError::InsecureSchema => { localization.get("main.login.insecure_auth_scheme").into() }, - client::AuthClientError::ServerError(_, e) => { - String::from_utf8_lossy(&e).to_string() - }, + client::AuthClientError::ServerError(_, e) => String::from_utf8_lossy(&e).into(), }, Error::AuthServerUrlInvalid(e) => { format!( From 15bf8157506bcc8c0770c44d00900c80cd68ea42 Mon Sep 17 00:00:00 2001 From: Imbris Date: Wed, 16 Jun 2021 02:51:09 -0400 Subject: [PATCH 107/180] Fix error message when no connection attempts succeed --- voxygen/src/menu/main/client_init.rs | 10 +++++----- voxygen/src/menu/main/mod.rs | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/voxygen/src/menu/main/client_init.rs b/voxygen/src/menu/main/client_init.rs index 01d6188095..e68b48b774 100644 --- a/voxygen/src/menu/main/client_init.rs +++ b/voxygen/src/menu/main/client_init.rs @@ -22,6 +22,7 @@ pub enum Error { mismatched_server_info: Option, }, ClientCrashed, + ServerNotFound, } #[allow(clippy::large_enum_variant)] // TODO: Pending review in #587 @@ -125,11 +126,10 @@ impl ClientInit { tokio::time::sleep(Duration::from_secs(5)).await; } - // Only possibility for no last_err is aborting - let _ = tx.send(Msg::Done(Err(last_err.unwrap_or(Error::ClientError { - error: ClientError::Other("Connection attempt aborted by user".to_owned()), - mismatched_server_info: None, - })))); + // Parsing/host name resolution successful but no connection succeeded + // If last_err is None this typically means there was no server up at the input + // address and all the attempts timed out. + let _ = tx.send(Msg::Done(Err(last_err.unwrap_or(Error::ServerNotFound)))); // Safe drop runtime tokio::task::block_in_place(move || drop(runtime2)); diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index ca4bd328a4..4c524a90d9 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -432,6 +432,7 @@ fn get_client_msg_error(e: client_init::Error, localized_strings: &LocalizationH }, }, InitError::ClientCrashed => localization.get("main.login.client_crashed").into(), + InitError::ServerNotFound => localization.get("main.login.server_not_found").into(), } } From c85238014a963be5c3542b82ad7f38e56f69a4ef Mon Sep 17 00:00:00 2001 From: DaforLynx Date: Fri, 18 Jun 2021 06:24:57 +0000 Subject: [PATCH 108/180] More npc sfx --- assets/voxygen/audio/sfx.ron | 57 ++++++++++++++----- .../voxygen/audio/sfx/utterance/cat_calm1.ogg | 3 + .../voxygen/audio/sfx/utterance/cat_calm2.ogg | 3 + .../audio/sfx/utterance/goat_calm1.ogg | 3 + .../sfx/utterance/humanmale_greeting1.ogg | 4 +- .../audio/sfx/utterance/humanmale_hurt1.ogg | 4 +- .../audio/sfx/utterance/lion_hurt1.ogg | 3 + .../audio/sfx/utterance/ogre_angry1.ogg | 4 +- .../audio/sfx/utterance/ogre_angry2.ogg | 4 +- .../audio/sfx/utterance/ogre_hurt1.ogg | 3 + .../audio/sfx/utterance/pig_angry1.ogg | 3 + .../audio/sfx/utterance/pig_angry2.ogg | 3 + .../voxygen/audio/sfx/utterance/pig_calm2.ogg | 3 + .../voxygen/audio/sfx/utterance/pig_calm3.ogg | 3 + .../audio/sfx/utterance/saurok_angry1.ogg | 4 +- common/src/comp/controller.rs | 3 + voxygen/src/audio/sfx/mod.rs | 6 +- 17 files changed, 87 insertions(+), 26 deletions(-) create mode 100644 assets/voxygen/audio/sfx/utterance/cat_calm1.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/cat_calm2.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/goat_calm1.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/lion_hurt1.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/ogre_hurt1.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/pig_angry1.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/pig_angry2.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/pig_calm2.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/pig_calm3.ogg diff --git a/assets/voxygen/audio/sfx.ron b/assets/voxygen/audio/sfx.ron index cb25f11d92..eaac0f0241 100644 --- a/assets/voxygen/audio/sfx.ron +++ b/assets/voxygen/audio/sfx.ron @@ -831,6 +831,19 @@ ], threshold: 0.2, ), + Utterance(Angry, Alligator): ( + files: [ + "voxygen.audio.sfx.utterance.alligator_angry1", + "voxygen.audio.sfx.utterance.alligator_angry2", + ], + threshold: 1.0, + ), + Utterance(Angry, Antelope): ( + files: [ + "voxygen.audio.sfx.utterance.antelope_angry1", + ], + threshold: 1.0, + ), Utterance(Angry, BipedLarge): ( files: [ "voxygen.audio.sfx.utterance.ogre_angry1", @@ -844,12 +857,6 @@ ], threshold: 1.0, ), - Utterance(Calm, Pig): ( - files: [ - "voxygen.audio.sfx.utterance.pig_calm1", - ], - threshold: 1.0, - ), Utterance(Angry, Adlet): ( files: [ "voxygen.audio.sfx.utterance.adlet_angry1", @@ -857,16 +864,10 @@ ], threshold: 1.0, ), - Utterance(Angry, Alligator): ( + Utterance(Angry, Pig): ( files: [ - "voxygen.audio.sfx.utterance.alligator_angry1", - "voxygen.audio.sfx.utterance.alligator_angry2", - ], - threshold: 1.0, - ), - Utterance(Angry, Antelope): ( - files: [ - "voxygen.audio.sfx.utterance.antelope_angry1", + "voxygen.audio.sfx.utterance.pig_angry1", + "voxygen.audio.sfx.utterance.pig_angry2", ], threshold: 1.0, ), @@ -889,6 +890,13 @@ ], threshold: 1.0, ), + Utterance(Calm, Cat): ( + files: [ + "voxygen.audio.sfx.utterance.cat_calm1", + "voxygen.audio.sfx.utterance.cat_calm2", + ], + threshold: 1.0, + ), Utterance(Calm, Cow): ( files: [ "voxygen.audio.sfx.utterance.cow_calm1", @@ -897,6 +905,19 @@ ], threshold: 1.0, ), + Utterance(Calm, Goat): ( + files: [ + "voxygen.audio.sfx.utterance.goat_calm1", + ], + threshold: 1.0, + ), + Utterance(Calm, Pig): ( + files: [ + "voxygen.audio.sfx.utterance.pig_calm1", + "voxygen.audio.sfx.utterance.pig_calm2", + ], + threshold: 1.0, + ), Utterance(Calm, Sheep): ( files: [ "voxygen.audio.sfx.utterance.sheep_calm1", @@ -922,6 +943,12 @@ ], threshold: 1.0, ), + Utterance(Hurt, BipedLarge): ( + files: [ + "voxygen.audio.sfx.utterance.ogre_hurt1", + ], + threshold: 1.0, + ), Utterance(Hurt, HumanMale): ( files: [ "voxygen.audio.sfx.utterance.humanmale_hurt1", diff --git a/assets/voxygen/audio/sfx/utterance/cat_calm1.ogg b/assets/voxygen/audio/sfx/utterance/cat_calm1.ogg new file mode 100644 index 0000000000..d4b4d28b0d --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/cat_calm1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:322d4281684f9097034e8fc700afcb7c72da260cf391cc4a5a7f994337b01a00 +size 11019 diff --git a/assets/voxygen/audio/sfx/utterance/cat_calm2.ogg b/assets/voxygen/audio/sfx/utterance/cat_calm2.ogg new file mode 100644 index 0000000000..14c891302d --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/cat_calm2.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:10bc4f2f26fe2c7926700f71e4da79d2dd888643464e3e3855f27226ee252842 +size 10417 diff --git a/assets/voxygen/audio/sfx/utterance/goat_calm1.ogg b/assets/voxygen/audio/sfx/utterance/goat_calm1.ogg new file mode 100644 index 0000000000..2aa9585d6f --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/goat_calm1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7534a1bfc7a12790cc788ef2f5fb4af687f3af301bbe4ef77f66b2de8d730fdc +size 10435 diff --git a/assets/voxygen/audio/sfx/utterance/humanmale_greeting1.ogg b/assets/voxygen/audio/sfx/utterance/humanmale_greeting1.ogg index 69dbe5a088..5e07e3f7d5 100644 --- a/assets/voxygen/audio/sfx/utterance/humanmale_greeting1.ogg +++ b/assets/voxygen/audio/sfx/utterance/humanmale_greeting1.ogg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ac12e8915ff261097d5aa24debf5685c4da3bc439ba64e6fd0bc12070f2ebff7 -size 12604 +oid sha256:6eb2f32ec21cab716fc1cc25a7696aa9dc527471e10dd365532fc288e6b62329 +size 12077 diff --git a/assets/voxygen/audio/sfx/utterance/humanmale_hurt1.ogg b/assets/voxygen/audio/sfx/utterance/humanmale_hurt1.ogg index f9db0309e9..3a6c64d0d5 100644 --- a/assets/voxygen/audio/sfx/utterance/humanmale_hurt1.ogg +++ b/assets/voxygen/audio/sfx/utterance/humanmale_hurt1.ogg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bca43c8a18501f7345b2e5314af80c33bca3e464a16a8d9caef5b6e1adb57d5e -size 10669 +oid sha256:7b2ae464ac53a6dee238984827220b3b109ebff89837e5414f20103f126bf202 +size 9646 diff --git a/assets/voxygen/audio/sfx/utterance/lion_hurt1.ogg b/assets/voxygen/audio/sfx/utterance/lion_hurt1.ogg new file mode 100644 index 0000000000..1198c37769 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/lion_hurt1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b2c7ebca12210b894823d68ce4da292aaca1b8f7aa1e19ec55b0b51ca2e0db38 +size 10713 diff --git a/assets/voxygen/audio/sfx/utterance/ogre_angry1.ogg b/assets/voxygen/audio/sfx/utterance/ogre_angry1.ogg index 819348a228..19bedebcd4 100644 --- a/assets/voxygen/audio/sfx/utterance/ogre_angry1.ogg +++ b/assets/voxygen/audio/sfx/utterance/ogre_angry1.ogg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:78f705397c01f73fd2bfad14f0aa3949a5409556cd5f004c185dea358fbd9701 -size 34809 +oid sha256:1e5c47f8ce7a014d2b7e312d6dd613164ffab6bce85070c99f0505e6a47d1d3d +size 19633 diff --git a/assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg b/assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg index da49817cca..80ddb9f150 100644 --- a/assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg +++ b/assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c97dcb8f2ff2543a041d07147c04a74b59e43421dd7452eccb095a947bd7466 -size 52973 +oid sha256:7b08e44983ea97c4a538a30f89b3687b4bef7e6f1d12bb3af323b2d1d7c96c7f +size 23645 diff --git a/assets/voxygen/audio/sfx/utterance/ogre_hurt1.ogg b/assets/voxygen/audio/sfx/utterance/ogre_hurt1.ogg new file mode 100644 index 0000000000..d0efb951ba --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/ogre_hurt1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ccc56b985270b0c1dad59d8a8182e597113958e6ba53195d3f0d7141fa67f0a +size 9182 diff --git a/assets/voxygen/audio/sfx/utterance/pig_angry1.ogg b/assets/voxygen/audio/sfx/utterance/pig_angry1.ogg new file mode 100644 index 0000000000..291788ee47 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/pig_angry1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c3e8ca94271ee87509000dc64cdaf2d6840b64e6e49cd547663a4d6939f1c92 +size 10431 diff --git a/assets/voxygen/audio/sfx/utterance/pig_angry2.ogg b/assets/voxygen/audio/sfx/utterance/pig_angry2.ogg new file mode 100644 index 0000000000..d272adfd7a --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/pig_angry2.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b8105d7d18a397ff8267d9f7972ff05f1df06f81d6aa4c11eccf00803bf6a37 +size 10818 diff --git a/assets/voxygen/audio/sfx/utterance/pig_calm2.ogg b/assets/voxygen/audio/sfx/utterance/pig_calm2.ogg new file mode 100644 index 0000000000..c8e6200aed --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/pig_calm2.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b1a31ada99bcefd8cbf4ce89a814b2b5f8cec64760ba6a3a480f043923c2bed +size 9641 diff --git a/assets/voxygen/audio/sfx/utterance/pig_calm3.ogg b/assets/voxygen/audio/sfx/utterance/pig_calm3.ogg new file mode 100644 index 0000000000..875677e38e --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/pig_calm3.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a357b2b1dbe69e2e3844b8cac20a6ceeb70d00ea18a8b7440577c18e47b25e88 +size 9440 diff --git a/assets/voxygen/audio/sfx/utterance/saurok_angry1.ogg b/assets/voxygen/audio/sfx/utterance/saurok_angry1.ogg index a550c1bbe2..7ea8a88872 100644 --- a/assets/voxygen/audio/sfx/utterance/saurok_angry1.ogg +++ b/assets/voxygen/audio/sfx/utterance/saurok_angry1.ogg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f859d590ac38bac163f0821eec8e2157f4f021d34d59575ccca2aeedde919ce -size 56816 +oid sha256:15416996c72da986349d277b9fc581776593d765d888e9dbffd85f6fd98e9931 +size 27654 diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index c27a4c1c50..63c3664f8d 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -103,6 +103,9 @@ pub enum UtteranceKind { Surprised, Hurt, Greeting, + /* Death, + * TODO: Wait for more post-death features (i.e. animiations) before implementing death + * sounds */ } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index 65623f5bd2..061cd592ca 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -207,6 +207,8 @@ pub enum VoiceKind { Antelope, Alligator, Saurok, + Cat, + Goat, } fn body_to_voice(body: &Body) -> Option { @@ -223,6 +225,8 @@ fn body_to_voice(body: &Body) -> Option { Body::QuadrupedSmall(body) => match body.species { quadruped_small::Species::Sheep => VoiceKind::Sheep, quadruped_small::Species::Pig | quadruped_small::Species::Boar => VoiceKind::Pig, + quadruped_small::Species::Cat => VoiceKind::Cat, + quadruped_small::Species::Goat => VoiceKind::Goat, _ => VoiceKind::Critter, }, Body::QuadrupedMedium(body) => match body.species { @@ -410,7 +414,7 @@ impl SfxMgr { }, Outcome::GroundSlam { pos, .. } => { let sfx_trigger_item = triggers.get_key_value(&SfxEvent::GroundSlam); - audio.emit_sfx(sfx_trigger_item, *pos, Some(1.0), false); + audio.emit_sfx(sfx_trigger_item, *pos, Some(2.0), false); }, Outcome::ProjectileShot { pos, body, .. } => { match body { From 288a7a020b945c19d05a9d268ac36e4666968596 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 18 Jun 2021 12:01:02 +0300 Subject: [PATCH 109/180] Proof of concept non-explodable sprites --- common/src/terrain/block.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 8cb8c73aea..3a5db4cf69 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -225,15 +225,24 @@ impl Block { /// arbitrary and only important when compared to one-another. #[inline] pub fn explode_power(&self) -> Option { + // Explodable means that the terrain sprite will get removed anyway, + // so all is good for empty fluids. match self.kind() { BlockKind::Leaves => Some(0.25), BlockKind::Grass => Some(0.5), BlockKind::WeakRock => Some(0.75), BlockKind::Snow => Some(0.1), - // Explodable means that the terrain sprite will get removed anyway, so all is good for - // empty fluids. - // TODO: Handle the case of terrain sprites we don't want to have explode - _ => self.get_sprite().map(|_| 0.25), + _ => self.get_sprite().and_then(|sprite| match sprite { + SpriteKind::Anvil + | SpriteKind::Cauldron + | SpriteKind::CookingPot + | SpriteKind::CraftingBench + | SpriteKind::Forge + | SpriteKind::Loom + | SpriteKind::SpinningWheel + | SpriteKind::TanningRack => None, + _ => Some(0.25), + }), } } From 8b44f7ed34d6f09d01043278ffc784555d6adaa1 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 18 Jun 2021 12:36:04 +0300 Subject: [PATCH 110/180] Add non-exploadable crafting stations to CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ad28d3149..3a2502f075 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed ### Fixed - +- Crafting Stations aren't exploadable anymore - Cases where no audio output could be produced before. - Significantly improved the performance of playing sound effects From 294b6e394a0ce312cff4d1316685676ef4355241 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 18 Jun 2021 11:31:06 +0100 Subject: [PATCH 111/180] Renamed command event --- common/src/event.rs | 2 +- server/src/events/mod.rs | 10 +++++----- server/src/lib.rs | 2 +- server/src/sys/msg/general.rs | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/common/src/event.rs b/common/src/event.rs index efb1ddf535..1056da4716 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -140,7 +140,7 @@ pub enum ServerEvent { ClientDisconnect(EcsEntity, DisconnectReason), ClientDisconnectWithoutPersistence(EcsEntity), ChunkRequest(EcsEntity, Vec2), - ChatCmd(EcsEntity, String, Vec), + Command(EcsEntity, String, Vec), /// Send a chat message to the player from an npc or other player Chat(comp::UnresolvedChatMsg), Aura { diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index 387f8c0fa1..38c73e6c52 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -51,7 +51,7 @@ impl Server { let mut frontend_events = Vec::new(); let mut requested_chunks = Vec::new(); - let mut chat_commands = Vec::new(); + let mut commands = Vec::new(); let mut chat_messages = Vec::new(); let events = self @@ -188,8 +188,8 @@ impl Server { ServerEvent::ChunkRequest(entity, key) => { requested_chunks.push((entity, key)); }, - ServerEvent::ChatCmd(entity, name, args) => { - chat_commands.push((entity, name, args)); + ServerEvent::Command(entity, name, args) => { + commands.push((entity, name, args)); }, ServerEvent::Chat(msg) => { chat_messages.push(msg); @@ -229,8 +229,8 @@ impl Server { self.generate_chunk(entity, key); } - for (entity, name, args) in chat_commands { - self.process_chat_cmd(entity, name, args); + for (entity, name, args) in commands { + self.process_command(entity, name, args); } for msg in chat_messages { diff --git a/server/src/lib.rs b/server/src/lib.rs index 5f5a944863..f1620b6680 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -983,7 +983,7 @@ impl Server { ); } - fn process_chat_cmd(&mut self, entity: EcsEntity, name: String, args: Vec) { + fn process_command(&mut self, entity: EcsEntity, name: String, args: Vec) { // Find the command object and run its handler. if let Ok(command) = name.parse::() { command.execute(self, entity, args); diff --git a/server/src/sys/msg/general.rs b/server/src/sys/msg/general.rs index 664efef403..e3787cc0ca 100644 --- a/server/src/sys/msg/general.rs +++ b/server/src/sys/msg/general.rs @@ -49,7 +49,7 @@ impl Sys { }, ClientGeneral::Command(name, args) => { if player.is_some() { - server_emitter.emit(ServerEvent::ChatCmd(entity, name, args)); + server_emitter.emit(ServerEvent::Command(entity, name, args)); } }, ClientGeneral::Terminate => { From 0265da1074405a3d26a6838ed795d54d0d1ff696 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 18 Jun 2021 12:27:39 +0100 Subject: [PATCH 112/180] Added support for JPEG backgrounds --- .gitattributes | 2 ++ assets/voxygen/background/bg_1.png | 3 --- assets/voxygen/background/bg_10.png | 3 --- assets/voxygen/background/bg_11.png | 3 --- assets/voxygen/background/bg_12.png | 3 --- assets/voxygen/background/bg_13.png | 3 --- assets/voxygen/background/bg_2.png | 3 --- assets/voxygen/background/bg_3.png | 3 --- assets/voxygen/background/bg_4.png | 3 --- assets/voxygen/background/bg_5.png | 3 --- assets/voxygen/background/bg_6.png | 3 --- assets/voxygen/background/bg_7.png | 3 --- assets/voxygen/background/bg_8.png | 3 --- assets/voxygen/background/bg_9.png | 3 --- assets/voxygen/background/bg_main.png | 3 --- common/assets/src/lib.rs | 13 +++++++------ 16 files changed, 9 insertions(+), 48 deletions(-) delete mode 100644 assets/voxygen/background/bg_1.png delete mode 100644 assets/voxygen/background/bg_10.png delete mode 100644 assets/voxygen/background/bg_11.png delete mode 100644 assets/voxygen/background/bg_12.png delete mode 100644 assets/voxygen/background/bg_13.png delete mode 100644 assets/voxygen/background/bg_2.png delete mode 100644 assets/voxygen/background/bg_3.png delete mode 100644 assets/voxygen/background/bg_4.png delete mode 100644 assets/voxygen/background/bg_5.png delete mode 100644 assets/voxygen/background/bg_6.png delete mode 100644 assets/voxygen/background/bg_7.png delete mode 100644 assets/voxygen/background/bg_8.png delete mode 100644 assets/voxygen/background/bg_9.png delete mode 100755 assets/voxygen/background/bg_main.png diff --git a/.gitattributes b/.gitattributes index dccc09d154..3e4661f797 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,6 @@ *.png filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text +*.jpeg filter=lfs diff=lfs merge=lfs -text *.vox filter=lfs diff=lfs merge=lfs -text *.ttf filter=lfs diff=lfs merge=lfs -text *.wav filter=lfs diff=lfs merge=lfs -text diff --git a/assets/voxygen/background/bg_1.png b/assets/voxygen/background/bg_1.png deleted file mode 100644 index 48772268ba..0000000000 --- a/assets/voxygen/background/bg_1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c2f501dcf2404832c9f713fc704c62bfa76b20b07b234755adb9198ff3d6893f -size 1042062 diff --git a/assets/voxygen/background/bg_10.png b/assets/voxygen/background/bg_10.png deleted file mode 100644 index 466e35655c..0000000000 --- a/assets/voxygen/background/bg_10.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:62a1fb6b416664dc346015492f4ed010eed55a4d59da25ea14837165ea5a8020 -size 2878018 diff --git a/assets/voxygen/background/bg_11.png b/assets/voxygen/background/bg_11.png deleted file mode 100644 index e4bee3831b..0000000000 --- a/assets/voxygen/background/bg_11.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9343b6be6caf0099edeac7260bf65103b7d1ec33211629988068b2f1412945d6 -size 3256584 diff --git a/assets/voxygen/background/bg_12.png b/assets/voxygen/background/bg_12.png deleted file mode 100644 index ef49a9452b..0000000000 --- a/assets/voxygen/background/bg_12.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ae98e12dbdb515556281313ac8d5e93be059aaafbd3f0578c3af58750841d682 -size 505241 diff --git a/assets/voxygen/background/bg_13.png b/assets/voxygen/background/bg_13.png deleted file mode 100644 index 7b7eccaf52..0000000000 --- a/assets/voxygen/background/bg_13.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:52edcbfe10dec67d5351d7940059591884f19bbc6acda7ffd3092aa7ed90ccf3 -size 1282505 diff --git a/assets/voxygen/background/bg_2.png b/assets/voxygen/background/bg_2.png deleted file mode 100644 index a33086f1e9..0000000000 --- a/assets/voxygen/background/bg_2.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f6149efbefa2c9acb45a5b20c36aa085e6e413ecd17c66929c2c3e20c62a3171 -size 961708 diff --git a/assets/voxygen/background/bg_3.png b/assets/voxygen/background/bg_3.png deleted file mode 100644 index 89a5200c38..0000000000 --- a/assets/voxygen/background/bg_3.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:27ab466038b11f3026452e0c3eb4480300a328e31a708c323dc3b0cae89c04e1 -size 2272839 diff --git a/assets/voxygen/background/bg_4.png b/assets/voxygen/background/bg_4.png deleted file mode 100644 index beed1bcbf8..0000000000 --- a/assets/voxygen/background/bg_4.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9ff1f9d09e04ca1343cb481403428d8a604e4090f2e1760caecb1a8aee149caa -size 897858 diff --git a/assets/voxygen/background/bg_5.png b/assets/voxygen/background/bg_5.png deleted file mode 100644 index 3c320bdacb..0000000000 --- a/assets/voxygen/background/bg_5.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0ccdede73a20ae6355782d9c10b18f868a515a27f8da79013109930b8c39bd8c -size 2044001 diff --git a/assets/voxygen/background/bg_6.png b/assets/voxygen/background/bg_6.png deleted file mode 100644 index 1bac40b2c7..0000000000 --- a/assets/voxygen/background/bg_6.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7ae41c4fac3190e13d1bfdafe6905ede9eeb2f8bdc6b5708d28b82a499aaa5ab -size 2079829 diff --git a/assets/voxygen/background/bg_7.png b/assets/voxygen/background/bg_7.png deleted file mode 100644 index 23de419292..0000000000 --- a/assets/voxygen/background/bg_7.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5f69f880541dbb2962060419c893b0ab061c8e429d2a836b5bdcf9733c10c7d8 -size 1100362 diff --git a/assets/voxygen/background/bg_8.png b/assets/voxygen/background/bg_8.png deleted file mode 100644 index b7db0e1bbb..0000000000 --- a/assets/voxygen/background/bg_8.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b8945a91805b8c0a6fbb9f7eee972c70a47ca1ac24d25ae9b91a3aa8fff5a336 -size 659820 diff --git a/assets/voxygen/background/bg_9.png b/assets/voxygen/background/bg_9.png deleted file mode 100644 index 3067fcb80c..0000000000 --- a/assets/voxygen/background/bg_9.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e101b354202f8d2fc872808041930b7b683d358b35cfe95203486e175f8cf6eb -size 1585712 diff --git a/assets/voxygen/background/bg_main.png b/assets/voxygen/background/bg_main.png deleted file mode 100755 index 7696d64216..0000000000 --- a/assets/voxygen/background/bg_main.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:31135eadd773c6846a749e661197c1e95f99808f75e34b74c357476d706a5a7e -size 936308 diff --git a/common/assets/src/lib.rs b/common/assets/src/lib.rs index b3031053d6..ee3156c5b9 100644 --- a/common/assets/src/lib.rs +++ b/common/assets/src/lib.rs @@ -97,19 +97,20 @@ impl Image { pub fn to_image(&self) -> Arc { Arc::clone(&self.0) } } -pub struct PngLoader; -impl Loader for PngLoader { - fn load(content: Cow<[u8]>, _: &str) -> Result { - let format = image::ImageFormat::Png; +pub struct ImageLoader; +impl Loader for ImageLoader { + fn load(content: Cow<[u8]>, ext: &str) -> Result { + let format = image::ImageFormat::from_extension(ext) + .ok_or_else(|| format!("Invalid file extension {}", ext))?; let image = image::load_from_memory_with_format(&content, format)?; Ok(Image(Arc::new(image))) } } impl Asset for Image { - type Loader = PngLoader; + type Loader = ImageLoader; - const EXTENSION: &'static str = "png"; + const EXTENSIONS: &'static [&'static str] = &["png", "jpg", "jpeg"]; } pub struct DotVoxAsset(pub DotVoxData); From f6d64a424f1d59b94bd50cd0087d46f523db606e Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 18 Jun 2021 12:28:55 +0100 Subject: [PATCH 113/180] Added JPEG backgrounds --- assets/voxygen/background/bg_1.jpg | 3 +++ assets/voxygen/background/bg_10.jpg | 3 +++ assets/voxygen/background/bg_11.jpg | 3 +++ assets/voxygen/background/bg_12.jpg | 3 +++ assets/voxygen/background/bg_13.jpg | 3 +++ assets/voxygen/background/bg_2.jpg | 3 +++ assets/voxygen/background/bg_3.jpg | 3 +++ assets/voxygen/background/bg_4.jpg | 3 +++ assets/voxygen/background/bg_5.jpg | 3 +++ assets/voxygen/background/bg_6.jpg | 3 +++ assets/voxygen/background/bg_7.jpg | 3 +++ assets/voxygen/background/bg_8.jpg | 3 +++ assets/voxygen/background/bg_9.jpg | 3 +++ assets/voxygen/background/bg_main.jpg | 3 +++ 14 files changed, 42 insertions(+) create mode 100644 assets/voxygen/background/bg_1.jpg create mode 100644 assets/voxygen/background/bg_10.jpg create mode 100644 assets/voxygen/background/bg_11.jpg create mode 100644 assets/voxygen/background/bg_12.jpg create mode 100644 assets/voxygen/background/bg_13.jpg create mode 100644 assets/voxygen/background/bg_2.jpg create mode 100644 assets/voxygen/background/bg_3.jpg create mode 100644 assets/voxygen/background/bg_4.jpg create mode 100644 assets/voxygen/background/bg_5.jpg create mode 100644 assets/voxygen/background/bg_6.jpg create mode 100644 assets/voxygen/background/bg_7.jpg create mode 100644 assets/voxygen/background/bg_8.jpg create mode 100644 assets/voxygen/background/bg_9.jpg create mode 100644 assets/voxygen/background/bg_main.jpg diff --git a/assets/voxygen/background/bg_1.jpg b/assets/voxygen/background/bg_1.jpg new file mode 100644 index 0000000000..1f3a36e64a --- /dev/null +++ b/assets/voxygen/background/bg_1.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a27164c57a5c6e6adce341252317dbc34a00375f10b3a250d8c14b221ae2ec27 +size 226101 diff --git a/assets/voxygen/background/bg_10.jpg b/assets/voxygen/background/bg_10.jpg new file mode 100644 index 0000000000..447bf5fac4 --- /dev/null +++ b/assets/voxygen/background/bg_10.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aea5f2986b1ec58751309e20c9c0c61a1eab8930316196a91255b7d2d6d9b5aa +size 918935 diff --git a/assets/voxygen/background/bg_11.jpg b/assets/voxygen/background/bg_11.jpg new file mode 100644 index 0000000000..abd63f9c7a --- /dev/null +++ b/assets/voxygen/background/bg_11.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f2ea61625000f6404fd1e5ffd72785ede6409a5b688db25ac7734105ae57c775 +size 888841 diff --git a/assets/voxygen/background/bg_12.jpg b/assets/voxygen/background/bg_12.jpg new file mode 100644 index 0000000000..8fc111642a --- /dev/null +++ b/assets/voxygen/background/bg_12.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:26dc6a512343266c3b486c30db6da8a92b8a3fa2be83159a4001d66d1e5e8009 +size 149508 diff --git a/assets/voxygen/background/bg_13.jpg b/assets/voxygen/background/bg_13.jpg new file mode 100644 index 0000000000..12af3eb536 --- /dev/null +++ b/assets/voxygen/background/bg_13.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d6df7d1e82f20da3f46a7ab909ae8a0b6102f7945794d0a690218cdc6d34dceb +size 166695 diff --git a/assets/voxygen/background/bg_2.jpg b/assets/voxygen/background/bg_2.jpg new file mode 100644 index 0000000000..9661ef9dc5 --- /dev/null +++ b/assets/voxygen/background/bg_2.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a90c88c855a19e27580ea42f7d85248c0c15a48bdcda959a295f911dd9ab4221 +size 204292 diff --git a/assets/voxygen/background/bg_3.jpg b/assets/voxygen/background/bg_3.jpg new file mode 100644 index 0000000000..e4bd6a7322 --- /dev/null +++ b/assets/voxygen/background/bg_3.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:348dace603b51dcf9aa56602fe24e1e34631142d96e12eadce14c228a2c7825f +size 838789 diff --git a/assets/voxygen/background/bg_4.jpg b/assets/voxygen/background/bg_4.jpg new file mode 100644 index 0000000000..5e80d2b35d --- /dev/null +++ b/assets/voxygen/background/bg_4.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3774ae810427c148a8b8d48b434f85438ea3352414229ca51213115e1444dde5 +size 362772 diff --git a/assets/voxygen/background/bg_5.jpg b/assets/voxygen/background/bg_5.jpg new file mode 100644 index 0000000000..6575f8fa53 --- /dev/null +++ b/assets/voxygen/background/bg_5.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2bc1c2f6f9f1dfc904f46eb6540fdbb15e5367f44a14322a525111cc4d666e28 +size 619281 diff --git a/assets/voxygen/background/bg_6.jpg b/assets/voxygen/background/bg_6.jpg new file mode 100644 index 0000000000..1d72e29ef8 --- /dev/null +++ b/assets/voxygen/background/bg_6.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7385e6c32080459cf8a667cb1e302bed9416e7dcd5e39e2930726e797238d4be +size 522922 diff --git a/assets/voxygen/background/bg_7.jpg b/assets/voxygen/background/bg_7.jpg new file mode 100644 index 0000000000..280be23cb0 --- /dev/null +++ b/assets/voxygen/background/bg_7.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a712e86e1ab5a3a2db292e34d637e1d7936dd711401df4bfa18e136f4462dad0 +size 208441 diff --git a/assets/voxygen/background/bg_8.jpg b/assets/voxygen/background/bg_8.jpg new file mode 100644 index 0000000000..e276755ec8 --- /dev/null +++ b/assets/voxygen/background/bg_8.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:882088822becbf3ff812c3c952606a353f6c448d5c2d923b6e34dab9c9fb0db4 +size 192300 diff --git a/assets/voxygen/background/bg_9.jpg b/assets/voxygen/background/bg_9.jpg new file mode 100644 index 0000000000..bc3a7f6181 --- /dev/null +++ b/assets/voxygen/background/bg_9.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64f48dbd1541451d73c290df7c12620030f9ac6c146a776c736cb0e1d59343fd +size 429957 diff --git a/assets/voxygen/background/bg_main.jpg b/assets/voxygen/background/bg_main.jpg new file mode 100644 index 0000000000..b287b8064e --- /dev/null +++ b/assets/voxygen/background/bg_main.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e5cdc80edb4c64b436d4fa119edb6c8e3a963c5765697101613d7581c1be0d15 +size 326620 From 747ef50a5831c4ca18f0631344e2e26a8a8a49f3 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 18 Jun 2021 12:42:08 +0100 Subject: [PATCH 114/180] Removed .jpeg as valid image format (to encourage consistency) --- common/assets/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/assets/src/lib.rs b/common/assets/src/lib.rs index ee3156c5b9..e8807680e2 100644 --- a/common/assets/src/lib.rs +++ b/common/assets/src/lib.rs @@ -110,7 +110,7 @@ impl Loader for ImageLoader { impl Asset for Image { type Loader = ImageLoader; - const EXTENSIONS: &'static [&'static str] = &["png", "jpg", "jpeg"]; + const EXTENSIONS: &'static [&'static str] = &["png", "jpg"]; } pub struct DotVoxAsset(pub DotVoxData); From 68d88ea2715b937800ab292bfa9e659b7d9bbbb8 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 18 Jun 2021 12:59:15 +0100 Subject: [PATCH 115/180] Switched to new CI cache image --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4967ab327b..bbf42dd77f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,7 +13,7 @@ variables: # https://docs.gitlab.com/ee/ci/yaml/#shallow-cloning GIT_DEPTH: 3 GIT_CLEAN_FLAGS: -f - CACHE_IMAGE_TAG: 8490f4b9 + CACHE_IMAGE_TAG: c6476744 default: # https://docs.gitlab.com/ee/ci/pipelines/settings.html#auto-cancel-pending-pipelines From 9a5f749bc7bc772e90a2cc143ee2c26d3378efba Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 18 Jun 2021 20:41:18 +0300 Subject: [PATCH 116/180] use bg_main.jpg instead of bg_main.png --- common/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/build.rs b/common/build.rs index 60fd6fe813..3a1d6ed8e9 100644 --- a/common/build.rs +++ b/common/build.rs @@ -55,7 +55,7 @@ fn main() { // Check if git-lfs is working if std::env::var("DISABLE_GIT_LFS_CHECK").is_err() && cfg!(not(feature = "no-assets")) { - let asset_path: PathBuf = ["..", "assets", "voxygen", "background", "bg_main.png"] + let asset_path: PathBuf = ["..", "assets", "voxygen", "background", "bg_main.jpg"] .iter() .collect(); let asset_file = match File::open(&asset_path) { From 36fa8bf4f7bd35896af773772cbe7ba535fd5489 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 18 Jun 2021 18:40:25 +0300 Subject: [PATCH 117/180] Use Golem Shockwave only after loosing 30% health + Make shockwave buildup longer --- .../common/abilities/custom/claygolem/shockwave.ron | 2 +- server/src/sys/agent.rs | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/assets/common/abilities/custom/claygolem/shockwave.ron b/assets/common/abilities/custom/claygolem/shockwave.ron index 5a88d9f395..b06bb052d4 100644 --- a/assets/common/abilities/custom/claygolem/shockwave.ron +++ b/assets/common/abilities/custom/claygolem/shockwave.ron @@ -1,6 +1,6 @@ Shockwave( energy_cost: 0, - buildup_duration: 0.6, + buildup_duration: 1.5, swing_duration: 0.12, recover_duration: 1.2, damage: 500, diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index fb4f013f69..86afd4dbe8 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -3477,6 +3477,9 @@ impl<'a> AgentData<'a> { const GOLEM_LONG_RANGE: f32 = 50.0; const GOLEM_TARGET_SPEED: f32 = 8.0; let golem_melee_range = self.body.map_or(0.0, |b| b.radius()) + GOLEM_MELEE_RANGE; + // Fraction of health, used for activation of shockwave + // If golem don't have health for some reason, assume it's full + let health_fraction = self.health.map_or(1.0, |h| h.fraction()); // Magnitude squared of cross product of target velocity with golem orientation let target_speed_cross_sqd = agent .target @@ -3517,8 +3520,9 @@ impl<'a> AgentData<'a> { controller .actions .push(ControlAction::basic_input(InputKind::Secondary)); - } else { - // Else target moving too fast for laser, shockwave time + } else if health_fraction < 0.7 { + // Else target moving too fast for laser, shockwave time. + // But only if damaged enough controller .actions .push(ControlAction::basic_input(InputKind::Ability(0))); @@ -3536,8 +3540,9 @@ impl<'a> AgentData<'a> { controller .actions .push(ControlAction::basic_input(InputKind::Ability(1))); - } else { - // Else target moving too fast for laser, shockwave time + } else if health_fraction < 0.7 { + // Else target moving too fast for laser, shockwave time. + // But only if damaged enough controller .actions .push(ControlAction::basic_input(InputKind::Ability(0))); From 384409a1ed0f6d62821ddaacd91f9ae7d74ab07c Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 18 Jun 2021 20:30:12 +0300 Subject: [PATCH 118/180] Load ori_rate for BasicBeam from assets + make fire turret ori_rate slower --- .../common/abilities/custom/basilisk/petrify.ron | 3 ++- .../custom/birdlargebreathe/flamethrower.ron | 3 ++- .../custom/birdlargefire/flamethrower.ron | 3 ++- assets/common/abilities/custom/claygolem/laser.ron | 3 ++- .../common/abilities/custom/claygolem/rocket.ron | 6 +++--- .../abilities/custom/claygolem/shockwave.ron | 4 ++-- .../abilities/custom/mindflayer/cursedflames.ron | 3 ++- .../abilities/custom/quadlowbeam/healingbeam.ron | 3 ++- .../custom/quadlowbreathe/flamethrower.ron | 3 ++- .../abilities/custom/tidalwarrior/bubbles.ron | 1 + .../abilities/custom/turret/flamethrower.ron | 3 ++- .../common/abilities/custom/yeti/frostbreath.ron | 3 ++- assets/common/abilities/sceptre/lifestealbeam.ron | 3 ++- assets/common/abilities/staff/flamethrower.ron | 3 ++- .../common/abilities/staffsimple/flamethrower.ron | 1 + common/src/comp/ability.rs | 3 +++ common/src/states/basic_beam.rs | 14 ++++++-------- 17 files changed, 38 insertions(+), 24 deletions(-) diff --git a/assets/common/abilities/custom/basilisk/petrify.ron b/assets/common/abilities/custom/basilisk/petrify.ron index c8fb5b3021..b8691b25fa 100644 --- a/assets/common/abilities/custom/basilisk/petrify.ron +++ b/assets/common/abilities/custom/basilisk/petrify.ron @@ -15,5 +15,6 @@ BasicBeam( energy_regen: 0, energy_drain: 0, orientation_behavior: Normal, + ori_rate: 0.6, specifier: Cultist, -) \ No newline at end of file +) diff --git a/assets/common/abilities/custom/birdlargebreathe/flamethrower.ron b/assets/common/abilities/custom/birdlargebreathe/flamethrower.ron index 4bf507fa86..1cb6d4fdf8 100644 --- a/assets/common/abilities/custom/birdlargebreathe/flamethrower.ron +++ b/assets/common/abilities/custom/birdlargebreathe/flamethrower.ron @@ -10,5 +10,6 @@ BasicBeam( energy_regen: 0, energy_drain: 0, orientation_behavior: Normal, + ori_rate: 0.6, specifier: Flamethrower, -) \ No newline at end of file +) diff --git a/assets/common/abilities/custom/birdlargefire/flamethrower.ron b/assets/common/abilities/custom/birdlargefire/flamethrower.ron index 4bf507fa86..1cb6d4fdf8 100644 --- a/assets/common/abilities/custom/birdlargefire/flamethrower.ron +++ b/assets/common/abilities/custom/birdlargefire/flamethrower.ron @@ -10,5 +10,6 @@ BasicBeam( energy_regen: 0, energy_drain: 0, orientation_behavior: Normal, + ori_rate: 0.6, specifier: Flamethrower, -) \ No newline at end of file +) diff --git a/assets/common/abilities/custom/claygolem/laser.ron b/assets/common/abilities/custom/claygolem/laser.ron index 8e029678d4..c5e6fac8b0 100644 --- a/assets/common/abilities/custom/claygolem/laser.ron +++ b/assets/common/abilities/custom/claygolem/laser.ron @@ -10,5 +10,6 @@ BasicBeam( energy_regen: 50, energy_drain: 0, orientation_behavior: FromOri, + ori_rate: 0.1, specifier: ClayGolem, -) \ No newline at end of file +) diff --git a/assets/common/abilities/custom/claygolem/rocket.ron b/assets/common/abilities/custom/claygolem/rocket.ron index 852a4db918..c58011bfb1 100644 --- a/assets/common/abilities/custom/claygolem/rocket.ron +++ b/assets/common/abilities/custom/claygolem/rocket.ron @@ -1,11 +1,11 @@ BasicRanged( energy_cost: 0, - buildup_duration: 0.5, - recover_duration: 0.8, + buildup_duration: 0.8, + recover_duration: 0.5, projectile: ClayRocket( damage: 500.0, knockback: 25.0, - radius: 10.0, + radius: 5.0, ), projectile_body: Object(ClayRocket), projectile_light: None, diff --git a/assets/common/abilities/custom/claygolem/shockwave.ron b/assets/common/abilities/custom/claygolem/shockwave.ron index b06bb052d4..ac4b9a8ae1 100644 --- a/assets/common/abilities/custom/claygolem/shockwave.ron +++ b/assets/common/abilities/custom/claygolem/shockwave.ron @@ -9,9 +9,9 @@ Shockwave( shockwave_angle: 180.0, shockwave_vertical_angle: 90.0, shockwave_speed: 15.0, - shockwave_duration: 2.5, + shockwave_duration: 3.5, requires_ground: true, move_efficiency: 0.0, damage_kind: Crushing, specifier: Ground, -) \ No newline at end of file +) diff --git a/assets/common/abilities/custom/mindflayer/cursedflames.ron b/assets/common/abilities/custom/mindflayer/cursedflames.ron index 9d83b5a243..0cfddbd5f5 100644 --- a/assets/common/abilities/custom/mindflayer/cursedflames.ron +++ b/assets/common/abilities/custom/mindflayer/cursedflames.ron @@ -15,5 +15,6 @@ BasicBeam( energy_regen: 0, energy_drain: 0, orientation_behavior: Normal, + ori_rate: 0.6, specifier: Cultist, -) \ No newline at end of file +) diff --git a/assets/common/abilities/custom/quadlowbeam/healingbeam.ron b/assets/common/abilities/custom/quadlowbeam/healingbeam.ron index 6776a79944..2aba7a0d1b 100644 --- a/assets/common/abilities/custom/quadlowbeam/healingbeam.ron +++ b/assets/common/abilities/custom/quadlowbeam/healingbeam.ron @@ -10,5 +10,6 @@ BasicBeam( energy_regen: 25, energy_drain: 0, orientation_behavior: Normal, + ori_rate: 0.6, specifier: HealingBeam, -) \ No newline at end of file +) diff --git a/assets/common/abilities/custom/quadlowbreathe/flamethrower.ron b/assets/common/abilities/custom/quadlowbreathe/flamethrower.ron index ddc09e0ef6..5eba0f6226 100644 --- a/assets/common/abilities/custom/quadlowbreathe/flamethrower.ron +++ b/assets/common/abilities/custom/quadlowbreathe/flamethrower.ron @@ -10,5 +10,6 @@ BasicBeam( energy_regen: 0, energy_drain: 0, orientation_behavior: Normal, + ori_rate: 0.6, specifier: Flamethrower, -) \ No newline at end of file +) diff --git a/assets/common/abilities/custom/tidalwarrior/bubbles.ron b/assets/common/abilities/custom/tidalwarrior/bubbles.ron index 65cbadc67c..737e144574 100644 --- a/assets/common/abilities/custom/tidalwarrior/bubbles.ron +++ b/assets/common/abilities/custom/tidalwarrior/bubbles.ron @@ -15,5 +15,6 @@ BasicBeam( energy_regen: 0, energy_drain: 0, orientation_behavior: Normal, + ori_rate: 0.6, specifier: Bubbles, ) diff --git a/assets/common/abilities/custom/turret/flamethrower.ron b/assets/common/abilities/custom/turret/flamethrower.ron index 3814d1f235..33f5dc51c9 100644 --- a/assets/common/abilities/custom/turret/flamethrower.ron +++ b/assets/common/abilities/custom/turret/flamethrower.ron @@ -15,5 +15,6 @@ BasicBeam( energy_regen: 0, energy_drain: 0, orientation_behavior: Normal, + ori_rate: 0.3, specifier: Flamethrower, -) \ No newline at end of file +) diff --git a/assets/common/abilities/custom/yeti/frostbreath.ron b/assets/common/abilities/custom/yeti/frostbreath.ron index 826f59bbcb..67e16b78ff 100644 --- a/assets/common/abilities/custom/yeti/frostbreath.ron +++ b/assets/common/abilities/custom/yeti/frostbreath.ron @@ -15,5 +15,6 @@ BasicBeam( energy_regen: 0, energy_drain: 0, orientation_behavior: Normal, + ori_rate: 0.6, specifier: Frost, -) \ No newline at end of file +) diff --git a/assets/common/abilities/sceptre/lifestealbeam.ron b/assets/common/abilities/sceptre/lifestealbeam.ron index 0484fef1c4..994e959b1d 100644 --- a/assets/common/abilities/sceptre/lifestealbeam.ron +++ b/assets/common/abilities/sceptre/lifestealbeam.ron @@ -10,5 +10,6 @@ BasicBeam( energy_regen: 50, energy_drain: 0, orientation_behavior: Normal, + ori_rate: 0.6, specifier: LifestealBeam -) \ No newline at end of file +) diff --git a/assets/common/abilities/staff/flamethrower.ron b/assets/common/abilities/staff/flamethrower.ron index c3c491b94a..82fa30d8f8 100644 --- a/assets/common/abilities/staff/flamethrower.ron +++ b/assets/common/abilities/staff/flamethrower.ron @@ -15,5 +15,6 @@ BasicBeam( energy_regen: 0, energy_drain: 350, orientation_behavior: Normal, + ori_rate: 0.6, specifier: Flamethrower, -) \ No newline at end of file +) diff --git a/assets/common/abilities/staffsimple/flamethrower.ron b/assets/common/abilities/staffsimple/flamethrower.ron index 4842fdd20f..14eead7fa6 100644 --- a/assets/common/abilities/staffsimple/flamethrower.ron +++ b/assets/common/abilities/staffsimple/flamethrower.ron @@ -10,5 +10,6 @@ BasicBeam( energy_regen: 0, energy_drain: 350, orientation_behavior: Normal, + ori_rate: 0.6, specifier: Flamethrower, ) diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index f755fe58d5..4d5c4aaf50 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -247,6 +247,7 @@ pub enum CharacterAbility { energy_regen: f32, energy_drain: f32, orientation_behavior: basic_beam::OrientationBehavior, + ori_rate: f32, specifier: beam::FrontendSpecifier, }, BasicAura { @@ -1651,6 +1652,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { energy_regen, energy_drain, orientation_behavior, + ori_rate, specifier, } => CharacterState::BasicBeam(basic_beam::Data { static_data: basic_beam::StaticData { @@ -1666,6 +1668,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { energy_drain: *energy_drain, ability_info, orientation_behavior: *orientation_behavior, + ori_rate: *ori_rate, specifier: *specifier, }, timer: Duration::default(), diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index defb22f81f..31080f42c0 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -40,6 +40,8 @@ pub struct StaticData { pub energy_drain: f32, /// Used to dictate how orientation functions in this state pub orientation_behavior: OrientationBehavior, + /// How fast enemy can rotate with beam + pub ori_rate: f32, /// What key is used to press ability pub ability_info: AbilityInfo, /// Used to specify the beam to the frontend @@ -61,14 +63,10 @@ impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); - let ori_rate = match self.static_data.orientation_behavior { - OrientationBehavior::Normal => 0.6, - OrientationBehavior::Turret => { - update.ori = Ori::from(data.inputs.look_dir); - 0.6 - }, - OrientationBehavior::FromOri => 0.1, - }; + let ori_rate = self.static_data.ori_rate; + if self.static_data.orientation_behavior == OrientationBehavior::Turret { + update.ori = Ori::from(data.inputs.look_dir); + } handle_orientation(data, &mut update, ori_rate); handle_move(data, &mut update, 0.4); From 573607c32a097e1e585a4c032f1100680e30f800 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 18 Jun 2021 22:03:39 +0300 Subject: [PATCH 119/180] Nerf golem laser dmg and ori_rate, make it fire + Make laser state duration lower --- assets/common/abilities/custom/claygolem/laser.ron | 11 ++++++++--- assets/common/abilities/custom/claygolem/strike.ron | 2 +- server/src/sys/agent.rs | 4 ++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/assets/common/abilities/custom/claygolem/laser.ron b/assets/common/abilities/custom/claygolem/laser.ron index c5e6fac8b0..c93f0f7ce8 100644 --- a/assets/common/abilities/custom/claygolem/laser.ron +++ b/assets/common/abilities/custom/claygolem/laser.ron @@ -2,14 +2,19 @@ BasicBeam( buildup_duration: 0.5, recover_duration: 0.4, beam_duration: 0.25, - damage: 100, + damage: 70, tick_rate: 2.0, range: 40.0, max_angle: 1.0, - damage_effect: None, + damage_effect: Some(Buff(( + kind: Burning, + dur_secs: 5.0, + strength: DamageFraction(0.75), + chance: 0.75, + ))), energy_regen: 50, energy_drain: 0, orientation_behavior: FromOri, - ori_rate: 0.1, + ori_rate: 0.07, specifier: ClayGolem, ) diff --git a/assets/common/abilities/custom/claygolem/strike.ron b/assets/common/abilities/custom/claygolem/strike.ron index 08e15584bc..9552e7f2bc 100644 --- a/assets/common/abilities/custom/claygolem/strike.ron +++ b/assets/common/abilities/custom/claygolem/strike.ron @@ -1,7 +1,7 @@ BasicMelee( energy_cost: 0, buildup_duration: 0.8, - swing_duration: 0.2, + swing_duration: 0.1, recover_duration: 0.5, base_damage: 200, base_poise_damage: 50, diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 86afd4dbe8..67240676be 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -3505,7 +3505,7 @@ impl<'a> AgentData<'a> { } } } else if attack_data.dist_sqrd < GOLEM_LASER_RANGE.powi(2) { - if matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs(10)) + if matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs(5)) || target_speed_cross_sqd < GOLEM_TARGET_SPEED.powi(2) && can_see_tgt( &*read_data.terrain, @@ -3515,7 +3515,7 @@ impl<'a> AgentData<'a> { ) && attack_data.angle < 45.0 { - // If target in range threshold and haven't been lasering for more than 10 + // If target in range threshold and haven't been lasering for more than 5 // seconds already or if target is moving slow-ish, laser them controller .actions From 57649a2e90053191e516b934470ab7d75a8cca56 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 18 Jun 2021 22:04:58 +0300 Subject: [PATCH 120/180] tweak clay_golem HP --- common/src/comp/body.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 72a2f4748f..df1500f996 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -511,7 +511,7 @@ impl Body { _ => 10000, }, Body::Golem(golem) => match golem.species { - golem::Species::ClayGolem => 7500, + golem::Species::ClayGolem => 4500, _ => 10000, }, Body::Theropod(theropod) => match theropod.species { @@ -676,7 +676,7 @@ impl Body { _ => 1.0, }, Body::Golem(g) => match g.species { - golem::Species::ClayGolem => 1.2, + golem::Species::ClayGolem => 2.0, _ => 1.0, }, _ => 1.0, From 7bf8c678085a73db20632b85e10bf504aea5a2ed Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 18 Jun 2021 20:04:50 +0300 Subject: [PATCH 121/180] Don't spawn enemies in boss room --- world/src/site/dungeon/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index 514c5116de..72f9763664 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -541,7 +541,7 @@ impl Floor { .map(|density| dynamic_rng.gen_range(0..density.recip() as usize) == 0) .unwrap_or(false) && !tile_is_pillar - && !(room.boss && room.difficulty == 5) + && !room.boss { // Randomly displace them a little let raw_entity = EntityInfo::at( From 41b6d806354b5fe2d2e408a46ef3775a359b077e Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 18 Jun 2021 22:58:51 +0300 Subject: [PATCH 122/180] Add CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30cc28e9cc..65d10d4099 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,8 +17,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Entity-entity pushback is no longer applied in forced movement states like rolling and leaping. - Updated audio library (rodio 0.13 -> 0.14). - Improve entity-terrain physics performance by reducing the number of voxel lookups. +- Clay Golem uses shockwave only after specific fraction of health and other difficulty adjustments. ### Removed +- Enemies no more spawn in dungeon boss room ### Fixed From 2bb91e8d7d3332aa27b58a06ae027211e718fded Mon Sep 17 00:00:00 2001 From: Imbris Date: Fri, 18 Jun 2021 02:23:48 -0400 Subject: [PATCH 123/180] Fix span macros by putting cfgs outside the macro (they are evaluated in the crate where the macro is used), add shorthand for common case of prof_span macro, add some spans to the client code and spiff bits of it --- client/src/lib.rs | 41 ++++++++++++++++--------- common/base/src/lib.rs | 70 ++++++++++++++++++++++++++++++------------ 2 files changed, 77 insertions(+), 34 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index de2f3d56b4..36b2d4bb79 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -43,7 +43,7 @@ use common::{ uid::{Uid, UidAllocator}, vol::RectVolSize, }; -use common_base::span; +use common_base::{prof_span, span}; use common_net::{ msg::{ self, validate_chat_msg, @@ -760,6 +760,7 @@ impl Client { where S: Into, { + prof_span!("send_msg_err"); let msg: ClientMsg = msg.into(); #[cfg(debug_assertions)] { @@ -1426,6 +1427,7 @@ impl Client { // 1) Handle input from frontend. // Pass character actions from frontend input to the player's entity. if self.presence.is_some() { + prof_span!("handle and send inputs"); if let Err(e) = self .state .ecs() @@ -1457,21 +1459,25 @@ impl Client { // Prepare for new events { + prof_span!("Last comps update"); let ecs = self.state.ecs(); - for (entity, _) in (&ecs.entities(), &ecs.read_storage::()).join() { - let mut last_character_states = - ecs.write_storage::>(); - if let Some(client_character_state) = - ecs.read_storage::().get(entity) + let mut last_character_states = ecs.write_storage::>(); + for (entity, _, character_state) in ( + &ecs.entities(), + &ecs.read_storage::(), + &ecs.read_storage::(), + ) + .join() + { + if let Some(l) = last_character_states + .entry(entity) + .ok() + .map(|l| l.or_insert_with(|| comp::Last(character_state.clone()))) + // TODO: since this just updates when the variant changes we should + // just store the variant to avoid the clone overhead + .filter(|l| !character_state.same_variant(&l.0)) { - if let Some(l) = last_character_states - .entry(entity) - .ok() - .map(|l| l.or_insert_with(|| comp::Last(client_character_state.clone()))) - .filter(|l| !client_character_state.same_variant(&l.0)) - { - *l = comp::Last(client_character_state.clone()); - } + *l = comp::Last(character_state.clone()); } } } @@ -1520,6 +1526,7 @@ impl Client { .get(self.entity()) .cloned(); if let (Some(pos), Some(view_distance)) = (pos, self.view_distance) { + prof_span!("terrain"); let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32)); // Remove chunks that are too far from the player. @@ -1653,6 +1660,7 @@ impl Client { frontend_events: &mut Vec, msg: ServerGeneral, ) -> Result<(), Error> { + prof_span!("handle_server_msg"); match msg { ServerGeneral::Disconnect(reason) => match reason { DisconnectReason::Shutdown => return Err(Error::ServerShutdown), @@ -1805,6 +1813,7 @@ impl Client { frontend_events: &mut Vec, msg: ServerGeneral, ) -> Result<(), Error> { + prof_span!("handle_server_in_game_msg"); match msg { ServerGeneral::GroupUpdate(change_notification) => { use comp::group::ChangeNotification::*; @@ -1979,6 +1988,7 @@ impl Client { #[allow(clippy::unnecessary_wraps)] fn handle_server_terrain_msg(&mut self, msg: ServerGeneral) -> Result<(), Error> { + prof_span!("handle_server_terrain_mgs"); match msg { ServerGeneral::TerrainChunkUpdate { key, chunk } => { if let Some(chunk) = chunk.ok().and_then(|c| c.to_chunk()) { @@ -2004,6 +2014,7 @@ impl Client { events: &mut Vec, msg: ServerGeneral, ) -> Result<(), Error> { + prof_span!("handle_character_screen_msg"); match msg { ServerGeneral::CharacterListUpdate(character_list) => { self.character_list.characters = character_list; @@ -2034,6 +2045,7 @@ impl Client { } fn handle_ping_msg(&mut self, msg: PingMsg) -> Result<(), Error> { + prof_span!("handle_ping_msg"); match msg { PingMsg::Ping => { self.send_msg_err(PingMsg::Pong)?; @@ -2088,6 +2100,7 @@ impl Client { /// Handle new server messages. fn handle_new_messages(&mut self) -> Result, Error> { + prof_span!("handle_new_messages"); let mut frontend_events = Vec::new(); // Check that we have an valid connection. diff --git a/common/base/src/lib.rs b/common/base/src/lib.rs index d05b952127..800869cf01 100644 --- a/common/base/src/lib.rs +++ b/common/base/src/lib.rs @@ -6,20 +6,21 @@ pub use userdata_dir::userdata_dir; #[cfg(feature = "tracy")] pub use tracy_client; +#[cfg(not(feature = "tracy"))] #[macro_export] macro_rules! plot { ($name:expr, $value:expr) => { - #[cfg(feature = "tracy")] - { - use $crate::tracy_client::{create_plot, Plot}; - static PLOT: Plot = create_plot!($name); - PLOT.point($value); - } - #[cfg(not(feature = "tracy"))] - { - // type check - let _: f64 = $value; - } + // type check + let _: f64 = $value; + }; +} +#[cfg(feature = "tracy")] +#[macro_export] +macro_rules! plot { + ($name:expr, $value:expr) => { + use $crate::tracy_client::{create_plot, Plot}; + static PLOT: Plot = create_plot!($name); + PLOT.point($value); }; } @@ -45,6 +46,7 @@ macro_rules! dev_panic { } // https://discordapp.com/channels/676678179678715904/676685797524766720/723358438943621151 +#[cfg(not(feature = "tracy"))] #[macro_export] macro_rules! span { ($guard_name:tt, $level:ident, $name:expr, $($fields:tt)*) => { @@ -56,12 +58,27 @@ macro_rules! span { let $guard_name = span.enter(); }; ($guard_name:tt, $name:expr) => { - #[cfg(not(feature = "tracy"))] let span = tracing::span!(tracing::Level::TRACE, $name); - #[cfg(not(feature = "tracy"))] let $guard_name = span.enter(); + }; + ($guard_name:tt, $no_tracy_name:expr, $tracy_name:expr) => { + $crate::span!($guard_name, $no_tracy_name); + }; +} + +#[cfg(feature = "tracy")] +#[macro_export] +macro_rules! span { + ($guard_name:tt, $level:ident, $name:expr, $($fields:tt)*) => { + let span = tracing::span!(tracing::Level::$level, $name, $($fields)*); + let $guard_name = span.enter(); + }; + ($guard_name:tt, $level:ident, $name:expr) => { + let span = tracing::span!(tracing::Level::$level, $name); + let $guard_name = span.enter(); + }; + ($guard_name:tt, $name:expr) => { // Directly use `tracy_client` to decrease overhead for better timing - #[cfg(feature = "tracy")] let $guard_name = $crate::tracy_client::Span::new( $name, "", @@ -72,9 +89,6 @@ macro_rules! span { ); }; ($guard_name:tt, $no_tracy_name:expr, $tracy_name:expr) => { - #[cfg(not(feature = "tracy"))] - $crate::span!($guard_name, $no_tracy_name); - #[cfg(feature = "tracy")] $crate::span!($guard_name, $tracy_name); }; } @@ -87,9 +101,22 @@ pub struct ProfSpan; /// Like the span macro but only used when profiling and not in regular tracing /// operations #[macro_export] +#[cfg(not(feature = "tracy"))] +macro_rules! prof_span { + ($guard_name:tt, $name:expr) => { + let $guard_name = $crate::ProfSpan; + }; + // Shorthand for when you want the guard to just be dropped at the end of the scope instead + // of controlling it manually + ($name:expr) => {}; +} + +/// Like the span macro but only used when profiling and not in regular tracing +/// operations +#[macro_export] +#[cfg(feature = "tracy")] macro_rules! prof_span { ($guard_name:tt, $name:expr) => { - #[cfg(feature = "tracy")] let $guard_name = $crate::ProfSpan($crate::tracy_client::Span::new( $name, "", @@ -98,8 +125,11 @@ macro_rules! prof_span { // No callstack since this has significant overhead 0, )); - #[cfg(not(feature = "tracy"))] - let $guard_name = $crate::ProfSpan; + }; + // Shorthand for when you want the guard to just be dropped at the end of the scope instead + // of controlling it manually + ($name:expr) => { + $crate::prof_span!(_guard, $name); }; } From 044b2316a6580ded6a7ceb7136ccdc8008064262 Mon Sep 17 00:00:00 2001 From: Imbris Date: Fri, 18 Jun 2021 03:17:55 -0400 Subject: [PATCH 124/180] Eliminate strange wait time of ~1 ms during message handling on the client where nothing seemed to be occuring --- Cargo.lock | 1 - client/Cargo.toml | 1 - client/src/lib.rs | 63 +++++++++++++++++++++-------------------------- 3 files changed, 28 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4a55a6ba37..43527b3c08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5718,7 +5718,6 @@ dependencies = [ "authc", "byteorder", "clap", - "futures-util", "hashbrown 0.11.2", "image", "num 0.4.0", diff --git a/client/Cargo.toml b/client/Cargo.toml index 449b18ba21..5b59f0f179 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -21,7 +21,6 @@ common-net = { package = "veloren-common-net", path = "../common/net" } network = { package = "veloren-network", path = "../network", features = ["compression","quic"], default-features = false } byteorder = "1.3.2" -futures-util = "0.3.7" tokio = { version = "1", default-features = false, features = ["rt-multi-thread"] } quinn = "0.7.2" image = { version = "0.23.12", default-features = false, features = ["png"] } diff --git a/client/src/lib.rs b/client/src/lib.rs index 36b2d4bb79..8a179e57f0 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -58,7 +58,6 @@ use common_net::{ use common_state::State; use common_systems::add_local_systems; use comp::BuffKind; -use futures_util::FutureExt; use hashbrown::{HashMap, HashSet}; use image::DynamicImage; use network::{ConnectAddr, Network, Participant, Pid, Stream}; @@ -71,7 +70,7 @@ use std::{ sync::Arc, time::{Duration, Instant}, }; -use tokio::{runtime::Runtime, select}; +use tokio::runtime::Runtime; use tracing::{debug, error, trace, warn}; use vek::*; @@ -2014,7 +2013,7 @@ impl Client { events: &mut Vec, msg: ServerGeneral, ) -> Result<(), Error> { - prof_span!("handle_character_screen_msg"); + prof_span!("handle_server_character_screen_msg"); match msg { ServerGeneral::CharacterListUpdate(character_list) => { self.character_list.characters = character_list; @@ -2066,34 +2065,37 @@ impl Client { Ok(()) } - async fn handle_messages( + fn handle_messages( &mut self, frontend_events: &mut Vec, - cnt: &mut u64, - ) -> Result<(), Error> { + ) -> Result { + let mut cnt = 0; loop { - let (m1, m2, m3, m4, m5) = select!( - msg = self.general_stream.recv().fuse() => (Some(msg), None, None, None, None), - msg = self.ping_stream.recv().fuse() => (None, Some(msg), None, None, None), - msg = self.character_screen_stream.recv().fuse() => (None, None, Some(msg), None, None), - msg = self.in_game_stream.recv().fuse() => (None, None, None, Some(msg), None), - msg = self.terrain_stream.recv().fuse() => (None, None, None, None, Some(msg)), - ); - *cnt += 1; - if let Some(msg) = m1 { - self.handle_server_msg(frontend_events, msg?)?; + let cnt_start = cnt; + + while let Some(msg) = self.general_stream.try_recv()? { + cnt += 1; + self.handle_server_msg(frontend_events, msg)?; } - if let Some(msg) = m2 { - self.handle_ping_msg(msg?)?; + while let Some(msg) = self.ping_stream.try_recv()? { + cnt += 1; + self.handle_ping_msg(msg)?; } - if let Some(msg) = m3 { - self.handle_server_character_screen_msg(frontend_events, msg?)?; + while let Some(msg) = self.character_screen_stream.try_recv()? { + cnt += 1; + self.handle_server_character_screen_msg(frontend_events, msg)?; } - if let Some(msg) = m4 { - self.handle_server_in_game_msg(frontend_events, msg?)?; + while let Some(msg) = self.in_game_stream.try_recv()? { + cnt += 1; + self.handle_server_in_game_msg(frontend_events, msg)?; } - if let Some(msg) = m5 { - self.handle_server_terrain_msg(msg?)?; + while let Some(msg) = self.terrain_stream.try_recv()? { + cnt += 1; + self.handle_server_terrain_msg(msg)?; + } + + if cnt_start == cnt { + return Ok(cnt); } } } @@ -2122,18 +2124,9 @@ impl Client { } } - let mut handles_msg = 0; + let msg_count = self.handle_messages(&mut frontend_events)?; - let runtime = Arc::clone(&self.runtime); - runtime.block_on(async { - //TIMEOUT 0.01 ms for msg handling - select!( - _ = tokio::time::sleep(std::time::Duration::from_micros(10)).fuse() => Ok(()), - err = self.handle_messages(&mut frontend_events, &mut handles_msg).fuse() => err, - ) - })?; - - if handles_msg == 0 + if msg_count == 0 && self.state.get_time() - self.last_server_pong > self.client_timeout.as_secs() as f64 { return Err(Error::ServerTimeout); From 659b42e81363b96cb127bd1a66ea9a7612bccf63 Mon Sep 17 00:00:00 2001 From: Imbris Date: Fri, 18 Jun 2021 03:26:35 -0400 Subject: [PATCH 125/180] Remove tracy features that are no longer needed now that macros have been fixed --- client/Cargo.toml | 1 - common/Cargo.toml | 1 - common/ecs/Cargo.toml | 1 - common/frontend/Cargo.toml | 2 +- common/net/Cargo.toml | 1 - common/state/Cargo.toml | 1 - common/systems/Cargo.toml | 1 - server-cli/Cargo.toml | 2 +- server/Cargo.toml | 1 - voxygen/Cargo.toml | 2 +- world/Cargo.toml | 1 - 11 files changed, 3 insertions(+), 11 deletions(-) diff --git a/client/Cargo.toml b/client/Cargo.toml index 5b59f0f179..7e3877a9e8 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -5,7 +5,6 @@ authors = ["Joshua Barretto "] edition = "2018" [features] -tracy = ["common/tracy", "common-base/tracy", "common-state/tracy", "common-systems/tracy", "common-net/tracy", "common-frontend/tracy"] simd = ["vek/platform_intrinsics"] plugins = ["common-state/plugins"] bin_bot = ["common-ecs", "serde", "ron", "clap", "rustyline", "common-frontend", "async-channel"] diff --git a/common/Cargo.toml b/common/Cargo.toml index ebc46b92bd..ab7811d778 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -6,7 +6,6 @@ edition = "2018" [features] no-assets = [] -tracy = ["common-base/tracy"] simd = ["vek/platform_intrinsics"] bin_csv = ["ron", "csv", "structopt"] bin_graphviz = ["petgraph"] diff --git a/common/ecs/Cargo.toml b/common/ecs/Cargo.toml index 687949a9df..17d39592ac 100644 --- a/common/ecs/Cargo.toml +++ b/common/ecs/Cargo.toml @@ -5,7 +5,6 @@ name = "veloren-common-ecs" version = "0.10.0" [features] -tracy = ["common-base/tracy"] [dependencies] diff --git a/common/frontend/Cargo.toml b/common/frontend/Cargo.toml index fcde12a404..c3091bf29e 100644 --- a/common/frontend/Cargo.toml +++ b/common/frontend/Cargo.toml @@ -19,4 +19,4 @@ tracing-log = "0.1.1" tracing-subscriber = { version = "0.2.3", default-features = false, features = ["env-filter", "fmt", "chrono", "ansi", "smallvec", "tracing-log"]} # Tracy -tracing-tracy = { version = "0.6.0", optional = true } \ No newline at end of file +tracing-tracy = { version = "0.6.0", optional = true } diff --git a/common/net/Cargo.toml b/common/net/Cargo.toml index 5700672819..74ae0304d4 100644 --- a/common/net/Cargo.toml +++ b/common/net/Cargo.toml @@ -5,7 +5,6 @@ name = "veloren-common-net" version = "0.10.0" [features] -tracy = ["common/tracy"] simd = ["vek/platform_intrinsics"] default = ["simd"] diff --git a/common/state/Cargo.toml b/common/state/Cargo.toml index f613bb9a3c..de24d09f7a 100644 --- a/common/state/Cargo.toml +++ b/common/state/Cargo.toml @@ -5,7 +5,6 @@ name = "veloren-common-state" version = "0.10.0" [features] -tracy = ["common/tracy"] simd = ["vek/platform_intrinsics"] plugins = ["toml", "tar", "wasmer", "bincode", "plugin-api", "serde"] diff --git a/common/systems/Cargo.toml b/common/systems/Cargo.toml index 8f1bacdf86..23cc60178e 100644 --- a/common/systems/Cargo.toml +++ b/common/systems/Cargo.toml @@ -5,7 +5,6 @@ name = "veloren-common-systems" version = "0.10.0" [features] -tracy = ["common/tracy"] simd = ["vek/platform_intrinsics"] default = ["simd"] diff --git a/server-cli/Cargo.toml b/server-cli/Cargo.toml index 193fe74446..10636a8ad0 100644 --- a/server-cli/Cargo.toml +++ b/server-cli/Cargo.toml @@ -17,7 +17,7 @@ This package includes the official server CLI. [features] worldgen = ["server/worldgen"] default = ["worldgen"] -tracy = ["common/tracy", "server/tracy", "common-net/tracy", "common-frontend/tracy"] +tracy = ["common-frontend/tracy"] plugins = ["server/plugins"] [dependencies] diff --git a/server/Cargo.toml b/server/Cargo.toml index 455a38147e..4236c3d887 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -6,7 +6,6 @@ edition = "2018" [features] worldgen = [] -tracy = ["common/tracy", "common-base/tracy", "common-ecs/tracy", "common-state/tracy", "common-net/tracy", "world/tracy"] simd = ["vek/platform_intrinsics"] plugins = ["common-state/plugins"] diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 70cb8f3b90..dfc7fcb121 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -25,7 +25,7 @@ buildInputs = ["xorg.libxcb"] hot-anim = ["anim/use-dyn-lib"] singleplayer = ["server"] simd = ["vek/platform_intrinsics"] -tracy = ["profiling", "profiling/profile-with-tracy", "common/tracy", "common-ecs/tracy", "common-frontend/tracy", "common-net/tracy", "common-systems/tracy", "common-state/tracy", "client/tracy"] +tracy = ["profiling", "profiling/profile-with-tracy", "common-frontend/tracy"] plugins = ["client/plugins"] default = ["singleplayer", "native-dialog", "plugins", "simd"] diff --git a/world/Cargo.toml b/world/Cargo.toml index 07b4084e78..4beef26bd3 100644 --- a/world/Cargo.toml +++ b/world/Cargo.toml @@ -5,7 +5,6 @@ authors = ["Joshua Barretto "] edition = "2018" [features] -tracy = ["common/tracy", "common-net/tracy"] simd = ["vek/platform_intrinsics"] bin_compression = ["lz-fear", "deflate", "flate2", "image/jpeg", "num-traits"] From 315ab872f5893fd14d0e281a408c577a1cf21116 Mon Sep 17 00:00:00 2001 From: Imbris Date: Sat, 19 Jun 2021 01:04:05 -0400 Subject: [PATCH 126/180] add prof_span to hud elements --- client/src/lib.rs | 5 +- voxygen/src/hud/bag.rs | 1 + voxygen/src/hud/buffs.rs | 1 + voxygen/src/hud/buttons.rs | 1 + voxygen/src/hud/chat.rs | 2 + voxygen/src/hud/crafting.rs | 1 + voxygen/src/hud/diary.rs | 1 + voxygen/src/hud/esc_menu.rs | 1 + voxygen/src/hud/group.rs | 1 + voxygen/src/hud/map.rs | 1 + voxygen/src/hud/minimap.rs | 1 + voxygen/src/hud/mod.rs | 104 ++++++++++--------- voxygen/src/hud/popup.rs | 1 + voxygen/src/hud/prompt_dialog.rs | 1 + voxygen/src/hud/settings_window/chat.rs | 1 + voxygen/src/hud/settings_window/controls.rs | 1 + voxygen/src/hud/settings_window/gameplay.rs | 1 + voxygen/src/hud/settings_window/interface.rs | 1 + voxygen/src/hud/settings_window/language.rs | 1 + voxygen/src/hud/settings_window/mod.rs | 1 + voxygen/src/hud/settings_window/sound.rs | 1 + voxygen/src/hud/settings_window/video.rs | 1 + voxygen/src/hud/skillbar.rs | 1 + voxygen/src/hud/social.rs | 1 + voxygen/src/hud/trade.rs | 1 + 25 files changed, 80 insertions(+), 53 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index 8a179e57f0..66fb721872 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -2065,10 +2065,7 @@ impl Client { Ok(()) } - fn handle_messages( - &mut self, - frontend_events: &mut Vec, - ) -> Result { + fn handle_messages(&mut self, frontend_events: &mut Vec) -> Result { let mut cnt = 0; loop { let cnt_start = cnt; diff --git a/voxygen/src/hud/bag.rs b/voxygen/src/hud/bag.rs index 7a1027da61..ee8e47d735 100644 --- a/voxygen/src/hud/bag.rs +++ b/voxygen/src/hud/bag.rs @@ -574,6 +574,7 @@ impl<'a> Widget for Bag<'a> { #[allow(clippy::useless_format)] // TODO: Pending review in #587 fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Bag::update"); let widget::UpdateArgs { state, ui, .. } = args; let i18n = &self.localized_strings; let key_layout = &self.global_state.window.key_layout; diff --git a/voxygen/src/hud/buffs.rs b/voxygen/src/hud/buffs.rs index 12cff265e7..5fc82242df 100644 --- a/voxygen/src/hud/buffs.rs +++ b/voxygen/src/hud/buffs.rs @@ -99,6 +99,7 @@ impl<'a> Widget for BuffsBar<'a> { fn style(&self) -> Self::Style {} fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("BuffsBar::update"); let widget::UpdateArgs { state, ui, .. } = args; let mut event = Vec::new(); let localized_strings = self.localized_strings; diff --git a/voxygen/src/hud/buttons.rs b/voxygen/src/hud/buttons.rs index dce7b90060..71b591d1b1 100644 --- a/voxygen/src/hud/buttons.rs +++ b/voxygen/src/hud/buttons.rs @@ -123,6 +123,7 @@ impl<'a> Widget for Buttons<'a> { fn style(&self) -> Self::Style { () } fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Buttons::update"); let widget::UpdateArgs { state, ui, .. } = args; let invs = self.client.inventories(); let inventory = match invs.get(self.client.entity()) { diff --git a/voxygen/src/hud/chat.rs b/voxygen/src/hud/chat.rs index 43283d68a0..2b4c70081a 100644 --- a/voxygen/src/hud/chat.rs +++ b/voxygen/src/hud/chat.rs @@ -203,6 +203,8 @@ impl<'a> Widget for Chat<'a> { #[allow(clippy::redundant_clone)] // TODO: Pending review in #587 #[allow(clippy::single_match)] // TODO: Pending review in #587 fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Chat::update"); + let widget::UpdateArgs { id, state, ui, .. } = args; let mut events = Vec::new(); diff --git a/voxygen/src/hud/crafting.rs b/voxygen/src/hud/crafting.rs index d2d34f96e5..2013b20ab8 100644 --- a/voxygen/src/hud/crafting.rs +++ b/voxygen/src/hud/crafting.rs @@ -234,6 +234,7 @@ impl<'a> Widget for Crafting<'a> { fn style(&self) -> Self::Style { () } fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Crafting::update"); let widget::UpdateArgs { state, ui, .. } = args; let mut events = Vec::new(); diff --git a/voxygen/src/hud/diary.rs b/voxygen/src/hud/diary.rs index 96a0cb6fc7..96479dfb46 100644 --- a/voxygen/src/hud/diary.rs +++ b/voxygen/src/hud/diary.rs @@ -260,6 +260,7 @@ impl<'a> Widget for Diary<'a> { fn style(&self) -> Self::Style { () } fn update(mut self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Diary::update"); let widget::UpdateArgs { id: _, state, ui, .. } = args; diff --git a/voxygen/src/hud/esc_menu.rs b/voxygen/src/hud/esc_menu.rs index fb949a249f..a85bde4861 100644 --- a/voxygen/src/hud/esc_menu.rs +++ b/voxygen/src/hud/esc_menu.rs @@ -67,6 +67,7 @@ impl<'a> Widget for EscMenu<'a> { fn style(&self) -> Self::Style { () } fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("EscMenu::update"); let widget::UpdateArgs { state, ui, .. } = args; Image::new(self.imgs.esc_frame) diff --git a/voxygen/src/hud/group.rs b/voxygen/src/hud/group.rs index 4145e072f9..fa5c31621e 100644 --- a/voxygen/src/hud/group.rs +++ b/voxygen/src/hud/group.rs @@ -148,6 +148,7 @@ impl<'a> Widget for Group<'a> { #[allow(clippy::unused_unit)] // TODO: Pending review in #587 #[allow(clippy::blocks_in_if_conditions)] // TODO: Pending review in #587 fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Group::update"); let widget::UpdateArgs { state, ui, .. } = args; let mut events = Vec::new(); let localized_strings = self.localized_strings; diff --git a/voxygen/src/hud/map.rs b/voxygen/src/hud/map.rs index 7ea03cf1b8..5e5634d82d 100644 --- a/voxygen/src/hud/map.rs +++ b/voxygen/src/hud/map.rs @@ -194,6 +194,7 @@ impl<'a> Widget for Map<'a> { #[allow(clippy::useless_format)] // TODO: Pending review in #587 fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Map::update"); let widget::UpdateArgs { state, ui, .. } = args; let zoom = self.global_state.settings.interface.map_zoom * 0.8; let show_difficulty = self.global_state.settings.interface.map_show_difficulty; diff --git a/voxygen/src/hud/minimap.rs b/voxygen/src/hud/minimap.rs index 6eabfb7275..4519e87edc 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -437,6 +437,7 @@ impl<'a> Widget for MiniMap<'a> { fn style(&self) -> Self::Style { () } fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Minimap::update"); let mut events = Vec::new(); let widget::UpdateArgs { state, ui, .. } = args; diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index a61df245bc..d409045156 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -88,7 +88,7 @@ use common::{ util::srgba_to_linear, vol::RectRasterableVol, }; -use common_base::span; +use common_base::{prof_span, span}; use common_net::{ msg::{world_msg::SiteId, Notification, PresenceKind}, sync::WorldSyncExt, @@ -977,6 +977,8 @@ impl Hud { let key_layout = &global_state.window.key_layout; if self.show.ingame { + prof_span!("ingame elements"); + let ecs = client.state().ecs(); let pos = ecs.read_storage::(); let stats = ecs.read_storage::(); @@ -1912,63 +1914,65 @@ impl Hud { // Temporary Example Quest let arrow_ani = (self.pulse * 4.0/* speed factor */).cos() * 0.5 + 0.8; //Animation timer + let show_intro = self.show.intro; // borrow check doesn't understand closures if let Some(toggle_cursor_key) = global_state .settings .controls .get_binding(GameInput::ToggleCursor) + .filter(|_| !show_intro) { - if !self.show.intro { - match global_state.settings.interface.intro_show { - Intro::Show => { - if Button::image(self.imgs.button) - .w_h(150.0, 40.0) - .hover_image(self.imgs.button_hover) - .press_image(self.imgs.button_press) - .bottom_left_with_margins_on(ui_widgets.window, 200.0, 120.0) - .label(&i18n.get("hud.tutorial_btn")) - .label_font_id(self.fonts.cyri.conrod_id) - .label_font_size(self.fonts.cyri.scale(18)) - .label_color(TEXT_COLOR) - .label_y(conrod_core::position::Relative::Scalar(2.0)) - .image_color(ENEMY_HP_COLOR) - .set(self.ids.intro_button, ui_widgets) - .was_clicked() - { - self.show.intro = true; - self.show.want_grab = true; - } - Image::new(self.imgs.sp_indicator_arrow) - .w_h(20.0, 11.0) - .mid_top_with_margin_on(self.ids.intro_button, -20.0 + arrow_ani as f64) - .color(Some(QUALITY_LEGENDARY)) - .set(self.ids.tut_arrow, ui_widgets); - Text::new(&i18n.get("hud.tutorial_click_here").replace( - "{key}", - toggle_cursor_key.display_string(key_layout).as_str(), - )) - .mid_top_with_margin_on(self.ids.tut_arrow, -18.0) - .font_id(self.fonts.cyri.conrod_id) - .font_size(self.fonts.cyri.scale(14)) - .color(BLACK) - .set(self.ids.tut_arrow_txt_bg, ui_widgets); - Text::new(&i18n.get("hud.tutorial_click_here").replace( - "{key}", - toggle_cursor_key.display_string(key_layout).as_str(), - )) - .bottom_right_with_margins_on(self.ids.tut_arrow_txt_bg, 1.0, 1.0) - .font_id(self.fonts.cyri.conrod_id) - .font_size(self.fonts.cyri.scale(14)) - .color(QUALITY_LEGENDARY) - .set(self.ids.tut_arrow_txt, ui_widgets); - }, - Intro::Never => { - self.show.intro = false; - }, - } + prof_span!("temporary example quest"); + match global_state.settings.interface.intro_show { + Intro::Show => { + if Button::image(self.imgs.button) + .w_h(150.0, 40.0) + .hover_image(self.imgs.button_hover) + .press_image(self.imgs.button_press) + .bottom_left_with_margins_on(ui_widgets.window, 200.0, 120.0) + .label(&i18n.get("hud.tutorial_btn")) + .label_font_id(self.fonts.cyri.conrod_id) + .label_font_size(self.fonts.cyri.scale(18)) + .label_color(TEXT_COLOR) + .label_y(conrod_core::position::Relative::Scalar(2.0)) + .image_color(ENEMY_HP_COLOR) + .set(self.ids.intro_button, ui_widgets) + .was_clicked() + { + self.show.intro = true; + self.show.want_grab = true; + } + Image::new(self.imgs.sp_indicator_arrow) + .w_h(20.0, 11.0) + .mid_top_with_margin_on(self.ids.intro_button, -20.0 + arrow_ani as f64) + .color(Some(QUALITY_LEGENDARY)) + .set(self.ids.tut_arrow, ui_widgets); + Text::new(&i18n.get("hud.tutorial_click_here").replace( + "{key}", + toggle_cursor_key.display_string(key_layout).as_str(), + )) + .mid_top_with_margin_on(self.ids.tut_arrow, -18.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .color(BLACK) + .set(self.ids.tut_arrow_txt_bg, ui_widgets); + Text::new(&i18n.get("hud.tutorial_click_here").replace( + "{key}", + toggle_cursor_key.display_string(key_layout).as_str(), + )) + .bottom_right_with_margins_on(self.ids.tut_arrow_txt_bg, 1.0, 1.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .color(QUALITY_LEGENDARY) + .set(self.ids.tut_arrow_txt, ui_widgets); + }, + Intro::Never => { + self.show.intro = false; + }, } } // TODO: Add event/stat based tutorial system if self.show.intro && !self.show.esc_menu { + prof_span!("intro show"); match global_state.settings.interface.intro_show { Intro::Show => { if self.show.intro { @@ -2062,6 +2066,7 @@ impl Hud { // Display debug window. if let Some(debug_info) = debug_info { + prof_span!("debug info"); // Alpha Version Text::new(&version) .top_left_with_margins_on(ui_widgets.window, 5.0, 5.0) @@ -2281,6 +2286,7 @@ impl Hud { .set(self.ids.debug_info, ui_widgets); } } else { + prof_span!("help window"); // Help Window if let Some(help_key) = global_state.settings.controls.get_binding(GameInput::Help) { Text::new( diff --git a/voxygen/src/hud/popup.rs b/voxygen/src/hud/popup.rs index 78c44bc085..f1e8ef2f97 100644 --- a/voxygen/src/hud/popup.rs +++ b/voxygen/src/hud/popup.rs @@ -85,6 +85,7 @@ impl<'a> Widget for Popup<'a> { #[allow(clippy::single_match)] // TODO: Pending review in #587 fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Popup::update"); let widget::UpdateArgs { state, ui, .. } = args; const FADE_IN: f32 = 0.5; diff --git a/voxygen/src/hud/prompt_dialog.rs b/voxygen/src/hud/prompt_dialog.rs index 409495d53d..eea312fb6b 100644 --- a/voxygen/src/hud/prompt_dialog.rs +++ b/voxygen/src/hud/prompt_dialog.rs @@ -83,6 +83,7 @@ impl<'a> Widget for PromptDialog<'a> { fn style(&self) -> Self::Style { () } fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("PromptDialog::update"); let widget::UpdateArgs { state, ui, .. } = args; let _localized_strings = &self.localized_strings; let mut event: Option = None; diff --git a/voxygen/src/hud/settings_window/chat.rs b/voxygen/src/hud/settings_window/chat.rs index 12b5f9b22f..3db6833f0d 100644 --- a/voxygen/src/hud/settings_window/chat.rs +++ b/voxygen/src/hud/settings_window/chat.rs @@ -122,6 +122,7 @@ impl<'a> Widget for Chat<'a> { fn style(&self) -> Self::Style { () } fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Chat::update"); let widget::UpdateArgs { state, ui, .. } = args; let mut events = Vec::new(); diff --git a/voxygen/src/hud/settings_window/controls.rs b/voxygen/src/hud/settings_window/controls.rs index 42daae7175..42fe82d28f 100644 --- a/voxygen/src/hud/settings_window/controls.rs +++ b/voxygen/src/hud/settings_window/controls.rs @@ -72,6 +72,7 @@ impl<'a> Widget for Controls<'a> { fn style(&self) -> Self::Style { () } fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Controls::update"); let widget::UpdateArgs { state, ui, .. } = args; let mut events = Vec::new(); diff --git a/voxygen/src/hud/settings_window/gameplay.rs b/voxygen/src/hud/settings_window/gameplay.rs index 5b08fac973..8797769007 100644 --- a/voxygen/src/hud/settings_window/gameplay.rs +++ b/voxygen/src/hud/settings_window/gameplay.rs @@ -98,6 +98,7 @@ impl<'a> Widget for Gameplay<'a> { fn style(&self) -> Self::Style { () } fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Gameplay::update"); let widget::UpdateArgs { state, ui, .. } = args; let mut events = Vec::new(); diff --git a/voxygen/src/hud/settings_window/interface.rs b/voxygen/src/hud/settings_window/interface.rs index 4a4c5cb32f..30478a2e20 100644 --- a/voxygen/src/hud/settings_window/interface.rs +++ b/voxygen/src/hud/settings_window/interface.rs @@ -137,6 +137,7 @@ impl<'a> Widget for Interface<'a> { fn style(&self) -> Self::Style { () } fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Interface::update"); let widget::UpdateArgs { state, ui, .. } = args; let mut events = Vec::new(); diff --git a/voxygen/src/hud/settings_window/language.rs b/voxygen/src/hud/settings_window/language.rs index 285a70df19..c317247d34 100644 --- a/voxygen/src/hud/settings_window/language.rs +++ b/voxygen/src/hud/settings_window/language.rs @@ -67,6 +67,7 @@ impl<'a> Widget for Language<'a> { fn style(&self) -> Self::Style { () } fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Language::update"); let widget::UpdateArgs { state, ui, .. } = args; let mut events = Vec::new(); diff --git a/voxygen/src/hud/settings_window/mod.rs b/voxygen/src/hud/settings_window/mod.rs index 8ef0a31c3e..874ae01bad 100644 --- a/voxygen/src/hud/settings_window/mod.rs +++ b/voxygen/src/hud/settings_window/mod.rs @@ -148,6 +148,7 @@ impl<'a> Widget for SettingsWindow<'a> { fn style(&self) -> Self::Style { () } fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("SettingsWindow::update"); let widget::UpdateArgs { state, ui, .. } = args; let mut events = Vec::new(); diff --git a/voxygen/src/hud/settings_window/sound.rs b/voxygen/src/hud/settings_window/sound.rs index eb3c4972e0..a60d0f5b15 100644 --- a/voxygen/src/hud/settings_window/sound.rs +++ b/voxygen/src/hud/settings_window/sound.rs @@ -80,6 +80,7 @@ impl<'a> Widget for Sound<'a> { fn style(&self) -> Self::Style { () } fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Sound::update"); let widget::UpdateArgs { state, ui, .. } = args; let mut events = Vec::new(); diff --git a/voxygen/src/hud/settings_window/video.rs b/voxygen/src/hud/settings_window/video.rs index 0ce0cc2b0e..d4b58bf0dd 100644 --- a/voxygen/src/hud/settings_window/video.rs +++ b/voxygen/src/hud/settings_window/video.rs @@ -177,6 +177,7 @@ impl<'a> Widget for Video<'a> { fn style(&self) -> Self::Style { () } fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Video::update"); let widget::UpdateArgs { state, ui, .. } = args; let mut events = Vec::new(); diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index f48eb11aa9..9956ab55d8 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -226,6 +226,7 @@ impl<'a> Widget for Skillbar<'a> { fn style(&self) -> Self::Style { () } fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Skillbar::update"); let widget::UpdateArgs { state, ui, .. } = args; let max_hp = self.health.base_max().max(self.health.maximum()); diff --git a/voxygen/src/hud/social.rs b/voxygen/src/hud/social.rs index 6e90ee40ee..2d428f3e60 100644 --- a/voxygen/src/hud/social.rs +++ b/voxygen/src/hud/social.rs @@ -109,6 +109,7 @@ impl<'a> Widget for Social<'a> { fn style(&self) -> Self::Style { () } fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Social::update"); let widget::UpdateArgs { state, ui, .. } = args; let mut events = Vec::new(); let button_tooltip = Tooltip::new({ diff --git a/voxygen/src/hud/trade.rs b/voxygen/src/hud/trade.rs index 7dbdc061a7..3d48fdf92e 100644 --- a/voxygen/src/hud/trade.rs +++ b/voxygen/src/hud/trade.rs @@ -522,6 +522,7 @@ impl<'a> Widget for Trade<'a> { fn style(&self) -> Self::Style {} fn update(mut self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Trade::update"); let widget::UpdateArgs { mut state, ui, .. } = args; let mut event = None; From 83c49895ae1bcd9922c465e619067e6ae8912adf Mon Sep 17 00:00:00 2001 From: Imbris Date: Sat, 19 Jun 2021 04:25:39 -0400 Subject: [PATCH 127/180] Improve rendering spans for tracy --- voxygen/src/scene/mod.rs | 16 +++++++++++----- voxygen/src/session/mod.rs | 24 +++++++++++++++--------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 50ef89f52f..365c5a0490 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -33,7 +33,7 @@ use common::{ terrain::{BlockKind, TerrainChunk}, vol::ReadVol, }; -use common_base::span; +use common_base::{prof_span, span}; use common_state::State; use comp::item::Reagent; use hashbrown::HashMap; @@ -1064,6 +1064,7 @@ impl Scene { // would instead have this as an extension. if drawer.render_mode().shadow.is_map() && (is_daylight || !self.light_data.is_empty()) { if is_daylight { + prof_span!("directed shadows"); if let Some(mut shadow_pass) = drawer.shadow_pass() { // Render terrain directed shadows. self.terrain @@ -1080,12 +1081,16 @@ impl Scene { } // Render terrain point light shadows. - drawer.draw_point_shadows( - &self.data.point_light_matrices, - self.terrain.chunks_for_point_shadows(focus_pos), - ) + { + prof_span!("point shadows"); + drawer.draw_point_shadows( + &self.data.point_light_matrices, + self.terrain.chunks_for_point_shadows(focus_pos), + ) + } } + prof_span!(guard, "main pass"); if let Some(mut first_pass) = drawer.first_pass() { self.figure_mgr.render_player( &mut first_pass.draw_figures(), @@ -1125,6 +1130,7 @@ impl Scene { // Render debug shapes self.debug.render(&mut first_pass.draw_debug()); } + drop(guard); } pub fn maintain_debug_hitboxes( diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index d4e2e4cb39..2e7464f658 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -27,7 +27,7 @@ use common::{ }, vol::ReadVol, }; -use common_base::span; +use common_base::{prof_span, span}; use common_net::{ msg::{server::InviteAnswer, PresenceKind}, sync::WorldSyncExt, @@ -1444,16 +1444,22 @@ impl PlayState for SessionState { } // Clouds - if let Some(mut second_pass) = drawer.second_pass() { - second_pass.draw_clouds(); + { + prof_span!("clouds"); + if let Some(mut second_pass) = drawer.second_pass() { + second_pass.draw_clouds(); + } } // PostProcess and UI - let mut third_pass = drawer.third_pass(); - third_pass.draw_postprocess(); - // Draw the UI to the screen - if let Some(mut ui_drawer) = third_pass.draw_ui() { - self.hud.render(&mut ui_drawer); - }; // Note: this semicolon is needed for the third_pass borrow to be dropped before it's lifetime ends + { + prof_span!("post-process and ui"); + let mut third_pass = drawer.third_pass(); + third_pass.draw_postprocess(); + // Draw the UI to the screen + if let Some(mut ui_drawer) = third_pass.draw_ui() { + self.hud.render(&mut ui_drawer); + }; // Note: this semicolon is needed for the third_pass borrow to be dropped before it's lifetime ends + } } } From 81bc3d196650f82b209b7909b940dc2f7e691b9c Mon Sep 17 00:00:00 2001 From: Yusuf Bera Ertan Date: Sat, 19 Jun 2021 22:05:22 +0300 Subject: [PATCH 128/180] build(nix): fix git ref not found and git lfs check --- flake.lock | 18 +++++++++--------- flake.nix | 2 +- nix/utils.nix | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/flake.lock b/flake.lock index dace752322..206ff4bf46 100644 --- a/flake.lock +++ b/flake.lock @@ -24,11 +24,11 @@ "rustOverlay": "rustOverlay" }, "locked": { - "lastModified": 1623601923, - "narHash": "sha256-zQDRV3OMYKlewfp3k0ilmFNfcFDxBCCk0tbcde8HMYM=", + "lastModified": 1624129119, + "narHash": "sha256-Mq7SkCA35dEEbWWfO8by94/+sZbvWfv3P8ByxNCVmOg=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "ab922adefdeb387ce105b1bde75f62ffb971cec4", + "rev": "4018999c0998137c34b81ff2ff032d719cd25096", "type": "github" }, "original": { @@ -39,11 +39,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1623176226, - "narHash": "sha256-54a9uvHlIlK3i0b36HfGMc4zqM0BpMOOiFYBxEhQFK8=", + "lastModified": 1623862044, + "narHash": "sha256-mY7nldu9Wl/Yb0qMPihZrWw0bRWXNsk6iyYztw/6F6Y=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "51bb9f3e9ab6161a3bf7746e20b955712cef618b", + "rev": "0747387223edf1aa5beaedf48983471315d95e16", "type": "github" }, "original": { @@ -62,11 +62,11 @@ "rustOverlay": { "flake": false, "locked": { - "lastModified": 1623550815, - "narHash": "sha256-RumRrkE6OTJDndHV4qZNZv8kUGnzwRHZQSyzx29r6/g=", + "lastModified": 1624069337, + "narHash": "sha256-9mTcx7osE4biF2Hm/GU19s1T3+KvphWj4QaUcJh39lU=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "9824f142cbd7bc3e2a92eefbb79addfff8704cd3", + "rev": "67dc2a9543a7c24591e6cb102ad0121c3a704aab", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index e31ceb2257..8854e66204 100644 --- a/flake.nix +++ b/flake.nix @@ -26,7 +26,7 @@ pkgs = common.pkgs; lib = common.lib; - gitLfsCheckFile = ./assets/voxygen/background/bg_main.png; + gitLfsCheckFile = ./assets/voxygen/background/bg_main.jpg; utils = import ./nix/utils.nix { inherit pkgs; }; sourceInfo = diff --git a/nix/utils.nix b/nix/utils.nix index d6ce5b648f..dd47bc3c21 100644 --- a/nix/utils.nix +++ b/nix/utils.nix @@ -3,7 +3,7 @@ let gitLfsCheckOutput = builtins.readFile (pkgs.runCommand "gitLfsCheck" { } '' - ([ "$(${pkgs.file}/bin/file --mime-type ${checkFile})" = "${checkFile}: image/png" ] && printf "0" || printf "1") > $out + ([ "$(${pkgs.file}/bin/file --mime-type ${checkFile})" = "${checkFile}: image/jpeg" ] && printf "0" || printf "1") > $out ''); in if gitLfsCheckOutput == "0" then From e5c63b2ca7d330dcd134a0f081ed4fc89a0c1abc Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sat, 19 Jun 2021 23:19:43 +0300 Subject: [PATCH 129/180] Add config file for dungeon distribution - Now you can specify probability of "spawn" of dungeon with specific difficulty --- .../world/dungeon/difficulty_distribution.ron | 20 +++++++++++ world/src/site/dungeon/mod.rs | 35 ++++++++++++++----- 2 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 assets/world/dungeon/difficulty_distribution.ron diff --git a/assets/world/dungeon/difficulty_distribution.ron b/assets/world/dungeon/difficulty_distribution.ron new file mode 100644 index 0000000000..09cd410377 --- /dev/null +++ b/assets/world/dungeon/difficulty_distribution.ron @@ -0,0 +1,20 @@ +/// Distribution of different dungeon levels. +/// +/// first number is dungeon level, integer +/// second number is probability, any normal positive float (not a NaN, for example) +/// +/// Values are relative to each other, +/// lesser probability means there will be less dungeons. +/// +/// General rules: +/// 1) Probability should not be less or equal to zero +/// 2) Keep it synced with number of dungeon levels +/// 3) Keep these pairs sorted from lowest to highest tier +([ + (0, 1.0), + (1, 1.0), + (2, 1.0), + (3, 1.0), + (4, 1.0), + (5, 1.0), +]) diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index 72f9763664..981e3f5650 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -9,7 +9,7 @@ use crate::{ }; use common::{ - assets::AssetHandle, + assets::{self, AssetExt, AssetHandle}, astar::Astar, comp::{self}, generation::{ChunkSupplement, EntityInfo}, @@ -20,7 +20,7 @@ use common::{ use core::{f32, hash::BuildHasherDefault}; use fxhash::FxHasher64; use lazy_static::lazy_static; -use rand::prelude::*; +use rand::{prelude::*, seq::SliceRandom}; use serde::Deserialize; use vek::*; @@ -47,13 +47,34 @@ pub struct Colors { const ALT_OFFSET: i32 = -2; +#[derive(Deserialize)] +struct DungeonDistribution(Vec<(u32, f32)>); +impl assets::Asset for DungeonDistribution { + type Loader = assets::RonLoader; + + const EXTENSION: &'static str = "ron"; +} + +lazy_static! { + static ref DUNGEON_DISTRIBUTION: Vec<(u32, f32)> = + DungeonDistribution::load_expect("world.dungeon.difficulty_distribution") + .read() + .0 + .clone(); +} + impl Dungeon { - #[allow(clippy::let_and_return)] // TODO: Pending review in #587 pub fn generate(wpos: Vec2, sim: Option<&WorldSim>, rng: &mut impl Rng) -> Self { let mut ctx = GenCtx { sim, rng }; - let difficulty = ctx.rng.gen_range(0..6); + + let difficulty = DUNGEON_DISTRIBUTION + .choose_weighted(&mut ctx.rng, |pair| pair.1) + .map(|(difficulty, _)| *difficulty) + .expect("this can never fail"); + let floors = 3 + difficulty / 2; - let this = Self { + + Self { name: { let name = NameGen::location(ctx.rng).generate(); match ctx.rng.gen_range(0..5) { @@ -81,9 +102,7 @@ impl Dungeon { }) .collect(), difficulty, - }; - - this + } } pub fn name(&self) -> &str { &self.name } From 2226a4c6a921d6a89d7e3c56f061b5de21086aee Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Wed, 16 Jun 2021 17:40:18 -0400 Subject: [PATCH 130/180] Add lava to caves, which sets you on fire if you swim in it. Currently requires uncommenting `#define LAVA` in the shaders, and only looks good with cheap fluid mode. --- assets/voxygen/shaders/fluid-frag/cheap.glsl | 19 +++++++- assets/voxygen/shaders/fluid-frag/shiny.glsl | 18 ++++++++ assets/voxygen/shaders/include/srgb.glsl | 2 + common/net/src/msg/compression.rs | 2 +- common/src/comp/fluid_dynamics.rs | 46 +++++++++++++++++--- common/src/terrain/block.rs | 15 ++++++- common/systems/src/buff.rs | 35 ++++++++------- common/systems/src/phys.rs | 43 ++++++++++-------- world/src/layer/mod.rs | 7 ++- 9 files changed, 143 insertions(+), 44 deletions(-) diff --git a/assets/voxygen/shaders/fluid-frag/cheap.glsl b/assets/voxygen/shaders/fluid-frag/cheap.glsl index f405fdd039..98ddf92dc4 100644 --- a/assets/voxygen/shaders/fluid-frag/cheap.glsl +++ b/assets/voxygen/shaders/fluid-frag/cheap.glsl @@ -37,6 +37,8 @@ layout(location = 1) flat in uint f_pos_norm; // ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; // }; +//#define LAVA + layout(std140, set = 2, binding = 0) uniform u_locals { vec3 model_offs; @@ -78,7 +80,11 @@ void main() { // vec3 view_dir = normalize(-vec3(vert_pos4)/* / vert_pos4.w*/); vec3 view_dir = -cam_to_frag; // vec3 surf_color = /*srgb_to_linear*/(vec3(0.4, 0.7, 2.0)); +#ifdef LAVA + vec3 water_color = (1.0 - MU_LAVA); +#else /*const */vec3 water_color = (1.0 - MU_WATER) * MU_SCATTER;//srgb_to_linear(vec3(0.2, 0.5, 1.0)); +#endif // /*const */vec3 water_color = srgb_to_linear(vec3(0.0, 0.25, 0.5)); /* vec3 sun_dir = get_sun_dir(time_of_day.x); @@ -118,7 +124,11 @@ void main() { // Water is transparent so both normals are valid. vec3 cam_norm = faceforward(f_norm, f_norm, cam_to_frag); +#ifdef LAVA + vec3 mu = MU_LAVA; +#else vec3 mu = MU_WATER; +#endif // NOTE: Default intersection point is camera position, meaning if we fail to intersect we assume the whole camera is in water. vec3 cam_attenuation = vec3(1.0);//compute_attenuation_point(f_pos, -view_dir, mu, fluid_alt, cam_pos.xyz); @@ -177,7 +187,14 @@ void main() { // float reflected_light_point = /*length*/(diffuse_light_point.r) + f_light * point_shadow; // reflected_light += k_d * (diffuse_light_point + f_light * point_shadow * shade_frac) + specular_light_point; - float passthrough = clamp(dot(cam_norm, -cam_to_frag) * 1.0 - 0.2, 0, 1); +#ifdef LAVA + float opacity = 0.9; + emitted_light = vec3(0.1, 0, 0); +#else + float opacity = 0.2; +#endif + + float passthrough = clamp(dot(cam_norm, -cam_to_frag) * 1.0 - opacity, 0, 1); float min_refl = min(emitted_light.r, min(emitted_light.g, emitted_light.b)); vec3 surf_color = illuminate(max_light, view_dir, water_color * /* fog_color * */emitted_light, /*surf_color * */water_color * reflected_light); diff --git a/assets/voxygen/shaders/fluid-frag/shiny.glsl b/assets/voxygen/shaders/fluid-frag/shiny.glsl index 019e2b0618..9c9b5c331a 100644 --- a/assets/voxygen/shaders/fluid-frag/shiny.glsl +++ b/assets/voxygen/shaders/fluid-frag/shiny.glsl @@ -88,6 +88,8 @@ float wave_height(vec3 pos) { return pow(abs(height), 0.5) * sign(height) * 15.0; } +//#define LAVA + void main() { // First 3 normals are negative, next 3 are positive vec3 normals[6] = vec3[](vec3(-1,0,0), vec3(1,0,0), vec3(0,-1,0), vec3(0,1,0), vec3(0,0,-1), vec3(0,0,1)); @@ -148,7 +150,12 @@ void main() { vec3 norm = vec3(0, 0, 1) * nmap.z + b_norm * nmap.x + c_norm * nmap.y; // vec3 norm = f_norm; +#ifdef LAVA + vec3 water_color = (1.0 - MU_LAVA); +#else vec3 water_color = (1.0 - MU_WATER) * MU_SCATTER; +#endif + #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY) float f_alt = alt_at(f_pos.xy); #elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP) @@ -230,7 +237,12 @@ void main() { // vec3 water_color_direct = exp(-MU_WATER);//exp(-MU_WATER);//vec3(1.0); // vec3 water_color_direct = exp(-water_attenuation * (water_depth_to_light + water_depth_to_camera)); // vec3 water_color_ambient = exp(-water_attenuation * (water_depth_to_vertical + water_depth_to_camera)); + +#ifdef LAVA + vec3 mu = MU_LAVA; +#else vec3 mu = MU_WATER; +#endif // NOTE: Default intersection point is camera position, meaning if we fail to intersect we assume the whole camera is in water. vec3 cam_attenuation = compute_attenuation_point(f_pos, -view_dir, mu, fluid_alt, cam_pos.xyz); // float water_depth_to_vertical = max(/*f_alt - f_pos.z*/f_light, 0.0); @@ -290,7 +302,13 @@ void main() { // reflected_light += point_light; // vec3 surf_color = srgb_to_linear(vec3(0.2, 0.5, 1.0)) * light * diffuse_light * ambient_light; const float REFLECTANCE = 0.5; +#ifdef LAVA + //vec3 surf_color = illuminate(max_light, view_dir, water_color * emitted_light, reflect_color * REFLECTANCE + water_color * reflected_light); + vec3 surf_color = vec3(1, 0.25, 0); +#else vec3 surf_color = illuminate(max_light, view_dir, water_color * emitted_light/* * log(1.0 - MU_WATER)*/, /*cam_attenuation * *//*water_color * */reflect_color * REFLECTANCE + water_color * reflected_light/* * log(1.0 - MU_WATER)*/); +#endif + //vec3 surf_color = illuminate(max_light, view_dir, water_color * emitted_light/* * log(1.0 - MU_WATER)*/, /*cam_attenuation * *//*water_color * */reflect_color * REFLECTANCE + water_color * reflected_light/* * log(1.0 - MU_WATER)*/); // passthrough = pow(passthrough, 1.0 / (1.0 + water_depth_to_camera)); /* surf_color = cam_attenuation.g < 0.5 ? diff --git a/assets/voxygen/shaders/include/srgb.glsl b/assets/voxygen/shaders/include/srgb.glsl index 118221a137..92ee63d21c 100644 --- a/assets/voxygen/shaders/include/srgb.glsl +++ b/assets/voxygen/shaders/include/srgb.glsl @@ -2,7 +2,9 @@ #define SRGB_GLSL // Linear RGB, attenuation coefficients for water at roughly R, G, B wavelengths. // See https://en.wikipedia.org/wiki/Electromagnetic_absorption_by_water + const vec3 MU_WATER = vec3(0.6, 0.04, 0.01); +const vec3 MU_LAVA = vec3(0.1, 0.75, 1.0); // // NOTE: Automatic in v4.0 // float diff --git a/common/net/src/msg/compression.rs b/common/net/src/msg/compression.rs index e6666503a2..f48f22fb63 100644 --- a/common/net/src/msg/compression.rs +++ b/common/net/src/msg/compression.rs @@ -557,7 +557,7 @@ impl VoxelImageDecoding for TriPngEncoding Rgb { r: 0, g: 0, b: 0 }, + Air | Water | Lava => Rgb { r: 0, g: 0, b: 0 }, Rock => Rgb { r: 93, g: 110, diff --git a/common/src/comp/fluid_dynamics.rs b/common/src/comp/fluid_dynamics.rs index 6529efc82c..081ca46560 100644 --- a/common/src/comp/fluid_dynamics.rs +++ b/common/src/comp/fluid_dynamics.rs @@ -9,11 +9,38 @@ use serde::{Deserialize, Serialize}; use std::f32::consts::PI; use vek::*; +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum LiquidKind { + Water, + Lava, +} + +impl LiquidKind { + /// If an entity is in multiple overlapping liquid blocks, which one takes + /// precedence? (should be a rare edge case, since checkerboard patterns of + /// water and lava shouldn't show up in worldgen) + pub fn merge(self, other: LiquidKind) -> LiquidKind { + use LiquidKind::{Lava, Water}; + match (self, other) { + (Water, Water) => Water, + (Water, Lava) => Lava, + (Lava, _) => Lava, + } + } +} + /// Fluid medium in which the entity exists #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum Fluid { - Air { vel: Vel, elevation: f32 }, - Water { vel: Vel, depth: f32 }, + Air { + vel: Vel, + elevation: f32, + }, + Liquid { + kind: LiquidKind, + vel: Vel, + depth: f32, + }, } impl Fluid { @@ -21,7 +48,14 @@ impl Fluid { pub fn density(&self) -> Density { match self { Self::Air { .. } => Density(AIR_DENSITY), - Self::Water { .. } => Density(WATER_DENSITY), + Self::Liquid { + kind: LiquidKind::Water, + .. + } => Density(WATER_DENSITY), + Self::Liquid { + kind: LiquidKind::Lava, + .. + } => Density(WATER_DENSITY), } } @@ -53,14 +87,14 @@ impl Fluid { pub fn flow_vel(&self) -> Vel { match self { Self::Air { vel, .. } => *vel, - Self::Water { vel, .. } => *vel, + Self::Liquid { vel, .. } => *vel, } } // Very simple but useful in reducing mental overhead pub fn relative_flow(&self, vel: &Vel) -> Vel { Vel(self.flow_vel().0 - vel.0) } - pub fn is_liquid(&self) -> bool { matches!(self, Fluid::Water { .. }) } + pub fn is_liquid(&self) -> bool { matches!(self, Fluid::Liquid { .. }) } pub fn elevation(&self) -> Option { match self { @@ -71,7 +105,7 @@ impl Fluid { pub fn depth(&self) -> Option { match self { - Fluid::Water { depth, .. } => Some(*depth), + Fluid::Liquid { depth, .. } => Some(*depth), _ => None, } } diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 3a5db4cf69..bdeb0d6b28 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -1,5 +1,8 @@ use super::SpriteKind; -use crate::{comp::tool::ToolKind, make_case_elim}; +use crate::{ + comp::{fluid_dynamics::LiquidKind, tool::ToolKind}, + make_case_elim, +}; use enum_iterator::IntoEnumIterator; use hashbrown::HashMap; use lazy_static::lazy_static; @@ -27,6 +30,7 @@ make_case_elim!( pub enum BlockKind { Air = 0x00, // Air counts as a fluid Water = 0x01, + Lava = 0x02, // 0x02 <= x < 0x10 are reserved for other fluids. These are 2^n aligned to allow bitwise // checking of common conditions. For example, `is_fluid` is just `block_kind & // 0x0F == 0` (this is a very common operation used in meshing that could do with @@ -63,6 +67,15 @@ impl BlockKind { #[inline] pub const fn is_liquid(&self) -> bool { self.is_fluid() && !self.is_air() } + #[inline] + pub const fn liquid_kind(&self) -> Option { + Some(match self { + BlockKind::Water => LiquidKind::Water, + BlockKind::Lava => LiquidKind::Lava, + _ => return None, + }) + } + /// Determine whether the block is filled (i.e: fully solid). Right now, /// this is the opposite of being a fluid. #[inline] diff --git a/common/systems/src/buff.rs b/common/systems/src/buff.rs index b504dbc4a2..b2d50da3dc 100644 --- a/common/systems/src/buff.rs +++ b/common/systems/src/buff.rs @@ -1,8 +1,8 @@ use common::{ comp::{ - fluid_dynamics::Fluid, Buff, BuffCategory, BuffChange, BuffEffect, BuffId, BuffKind, - BuffSource, Buffs, Energy, Health, HealthChange, HealthSource, Inventory, ModifierKind, - PhysicsState, Stats, + fluid_dynamics::{LiquidKind, Fluid}, Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, + BuffKind, BuffSource, Buffs, Energy, Health, HealthChange, HealthSource, Inventory, + ModifierKind, PhysicsState, Stats, }, event::{EventBus, ServerEvent}, resources::DeltaTime, @@ -45,34 +45,39 @@ impl<'a> System<'a> for Sys { // Set to false to avoid spamming server buffs.set_event_emission(false); stats.set_event_emission(false); - for (entity, mut buff_comp, energy, mut stat, health) in ( + for (entity, mut buff_comp, energy, mut stat, health, physics_state) in ( &read_data.entities, &mut buffs, &read_data.energies, &mut stats, &read_data.healths, + read_data.physics_states.maybe(), ) .join() { + let in_fluid = physics_state.and_then(|p| p.in_fluid); + + if matches!(in_fluid, Some(Fluid::Liquid { kind: LiquidKind::Lava, .. })) { + if !buff_comp.contains(BuffKind::Burning) { + buff_comp.insert(Buff::new( + BuffKind::Burning, + BuffData::new(200.0, None), + vec![BuffCategory::Natural], + BuffSource::World, + )); + } + } + let (buff_comp_kinds, buff_comp_buffs): ( &HashMap>, &mut HashMap, ) = buff_comp.parts(); let mut expired_buffs = Vec::::new(); + // For each buff kind present on entity, if the buff kind queues, only ticks // duration of strongest buff of that kind, else it ticks durations of all buffs // of that kind. Any buffs whose durations expire are marked expired. for (kind, ids) in buff_comp_kinds.iter() { - // Only get the physics state component if the entity has the burning buff, as - // we don't need it for any other conditions yet - let in_fluid = if matches!(kind, BuffKind::Burning) { - read_data - .physics_states - .get(entity) - .and_then(|p| p.in_fluid) - } else { - None - }; if kind.queues() { if let Some((Some(buff), id)) = ids.get(0).map(|id| (buff_comp_buffs.get_mut(id), id)) @@ -257,7 +262,7 @@ fn tick_buff( } if let Some(remaining_time) = &mut buff.time { // Extinguish Burning buff when in water - if matches!(buff.kind, BuffKind::Burning) && matches!(in_fluid, Some(Fluid::Water { .. })) { + if matches!(buff.kind, BuffKind::Burning) && matches!(in_fluid, Some(Fluid::Liquid { kind: LiquidKind::Water, .. })) { *remaining_time = Duration::default(); } diff --git a/common/systems/src/phys.rs b/common/systems/src/phys.rs index 5852454f5f..5e62cbbdf3 100644 --- a/common/systems/src/phys.rs +++ b/common/systems/src/phys.rs @@ -1,7 +1,7 @@ use common::{ comp::{ body::ship::figuredata::{VoxelCollider, VOXEL_COLLIDER_MANIFEST}, - fluid_dynamics::{Fluid, Wings}, + fluid_dynamics::{Fluid, LiquidKind, Wings}, BeamSegment, Body, CharacterState, Collider, Density, Mass, Mounting, Ori, PhysicsState, Pos, PosVelDefer, PreviousPhysCache, Projectile, Scale, Shockwave, Stats, Sticky, Vel, }, @@ -905,13 +905,15 @@ impl<'a> PhysicsData<'a> { .terrain .get(pos.0.map(|e| e.floor() as i32)) .ok() - .and_then(|vox| vox.is_liquid().then_some(1.0)) - .map(|depth| Fluid::Water { - depth, - vel: Vel::zero(), + .and_then(|vox| { + vox.liquid_kind().map(|kind| Fluid::Liquid { + kind, + depth: 1.0, + vel: Vel::zero(), + }) }) .or_else(|| match physics_state.in_fluid { - Some(Fluid::Water { .. }) | None => Some(Fluid::Air { + Some(Fluid::Liquid { .. }) | None => Some(Fluid::Air { elevation: pos.0.z, vel: Vel::default(), }), @@ -1541,24 +1543,26 @@ fn box_voxel_collision<'a, T: BaseVol + ReadVol>( }); // Find liquid immersion and wall collision all in one round of iteration - let mut max_liquid_z = None::; + let mut liquid = None::<(LiquidKind, f32)>; let mut wall_dir_collisions = [false; 4]; near_iter.for_each(|(i, j, k)| { let block_pos = player_voxel_pos + Vec3::new(i, j, k); if let Some(block) = terrain.get(block_pos).ok().copied() { // Check for liquid blocks - if block.is_liquid() { + if let Some(block_liquid) = block.liquid_kind() { let liquid_aabb = Aabb { min: block_pos.map(|e| e as f32), // The liquid part of a liquid block always extends 1 block high. max: block_pos.map(|e| e as f32) + Vec3::one(), }; if player_aabb.collides_with_aabb(liquid_aabb) { - max_liquid_z = Some(match max_liquid_z { - Some(z) => z.max(liquid_aabb.max.z), - None => liquid_aabb.max.z, - }); + liquid = match liquid { + Some((kind, max_liquid_z)) => { + Some((kind.merge(block_liquid), max_liquid_z.max(liquid_aabb.max.z))) + }, + None => Some((block_liquid, liquid_aabb.max.z)), + }; } } // Check for walls @@ -1594,23 +1598,24 @@ fn box_voxel_collision<'a, T: BaseVol + ReadVol>( physics_state.ground_vel = ground_vel; } - physics_state.in_fluid = max_liquid_z - .map(|max_z| max_z - pos.0.z) // NOTE: assumes min_z == 0.0 - .map(|depth| { - physics_state + physics_state.in_fluid = liquid + .map(|(kind, max_z)| (kind, max_z - pos.0.z)) // NOTE: assumes min_z == 0.0 + .map(|(kind, depth)| { + (kind, physics_state .in_liquid() // This is suboptimal because it doesn't check for true depth, // so it can cause problems for situations like swimming down // a river and spawning or teleporting in(/to) water .map(|old_depth| (old_depth + old_pos.z - pos.0.z).max(depth)) - .unwrap_or(depth) + .unwrap_or(depth)) }) - .map(|depth| Fluid::Water { + .map(|(kind, depth)| Fluid::Liquid { + kind, depth, vel: Vel::zero(), }) .or_else(|| match physics_state.in_fluid { - Some(Fluid::Water { .. }) | None => Some(Fluid::Air { + Some(Fluid::Liquid { .. }) | None => Some(Fluid::Air { elevation: pos.0.z, vel: Vel::default(), }), diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index f448f71f79..ee7545fbbe 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -210,10 +210,15 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) { //make pits for z in cave_base - pit_depth..cave_base { if pit_condition && (cave_roof - cave_base) > 10 { + let kind = if z < (cave_base - pit_depth) + (3 * pit_depth / 4) { + BlockKind::Lava + } else { + BlockKind::Air + }; canvas.set( Vec3::new(wpos2d.x, wpos2d.y, z), Block::new( - BlockKind::Air, + kind, noisy_color(info.index().colors.layer.scaffold.into(), 8), ), ); From ecd61df49b06980a2d4d4f621deee84adcaa57a9 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sat, 19 Jun 2021 23:32:12 +0300 Subject: [PATCH 131/180] Tip how to make map with dungeons of same level --- assets/world/dungeon/difficulty_distribution.ron | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assets/world/dungeon/difficulty_distribution.ron b/assets/world/dungeon/difficulty_distribution.ron index 09cd410377..50a5ce163a 100644 --- a/assets/world/dungeon/difficulty_distribution.ron +++ b/assets/world/dungeon/difficulty_distribution.ron @@ -10,6 +10,10 @@ /// 1) Probability should not be less or equal to zero /// 2) Keep it synced with number of dungeon levels /// 3) Keep these pairs sorted from lowest to highest tier +/// +/// Tips: +/// 1) Set every probability to 0.0 and left one with any other number +/// and you will have map full of dungeons of same level ([ (0, 1.0), (1, 1.0), From f1a1160b807e6d01b309a9179c2f3d0711e2d499 Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Fri, 18 Jun 2021 16:35:30 -0400 Subject: [PATCH 132/180] Make lava solid w.r.t. rendering while still being liquid w.r.t. physics. --- assets/world/style/colors.ron | 1 + common/src/terrain/block.rs | 7 +++++-- common/systems/src/buff.rs | 39 +++++++++++++++++++++++------------ common/systems/src/phys.rs | 7 ++++--- world/src/layer/mod.rs | 6 ++---- 5 files changed, 38 insertions(+), 22 deletions(-) diff --git a/assets/world/style/colors.ron b/assets/world/style/colors.ron index b46018a8c7..78e282b410 100644 --- a/assets/world/style/colors.ron +++ b/assets/world/style/colors.ron @@ -64,6 +64,7 @@ cave_roof: (38, 21, 79), dirt: (69, 48, 15), scaffold: (195, 190, 212), + lava: (184, 39, 0), vein: (222, 140, 39), ), site: ( diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index bdeb0d6b28..4327137fda 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -30,13 +30,13 @@ make_case_elim!( pub enum BlockKind { Air = 0x00, // Air counts as a fluid Water = 0x01, - Lava = 0x02, // 0x02 <= x < 0x10 are reserved for other fluids. These are 2^n aligned to allow bitwise // checking of common conditions. For example, `is_fluid` is just `block_kind & // 0x0F == 0` (this is a very common operation used in meshing that could do with // being *very* fast). Rock = 0x10, WeakRock = 0x11, // Explodable + Lava = 0x12, // 0x12 <= x < 0x20 is reserved for future rocks Grass = 0x20, // Note: *not* the same as grass sprites Snow = 0x21, @@ -181,6 +181,9 @@ impl Block { #[inline] pub fn get_glow(&self) -> Option { + if matches!(self.kind, BlockKind::Lava) { + return Some(24); + } match self.get_sprite()? { SpriteKind::StreetLamp | SpriteKind::StreetLampTall => Some(24), SpriteKind::Ember => Some(20), @@ -230,7 +233,7 @@ impl Block { pub fn is_solid(&self) -> bool { self.get_sprite() .map(|s| s.solid_height().is_some()) - .unwrap_or(true) + .unwrap_or(!matches!(self.kind, BlockKind::Lava)) } /// Can this block be exploded? If so, what 'power' is required to do so? diff --git a/common/systems/src/buff.rs b/common/systems/src/buff.rs index b2d50da3dc..20e8a0c612 100644 --- a/common/systems/src/buff.rs +++ b/common/systems/src/buff.rs @@ -1,8 +1,8 @@ use common::{ comp::{ - fluid_dynamics::{LiquidKind, Fluid}, Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, - BuffKind, BuffSource, Buffs, Energy, Health, HealthChange, HealthSource, Inventory, - ModifierKind, PhysicsState, Stats, + fluid_dynamics::{Fluid, LiquidKind}, + Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buffs, + Energy, Health, HealthChange, HealthSource, Inventory, ModifierKind, PhysicsState, Stats, }, event::{EventBus, ServerEvent}, resources::DeltaTime, @@ -57,15 +57,20 @@ impl<'a> System<'a> for Sys { { let in_fluid = physics_state.and_then(|p| p.in_fluid); - if matches!(in_fluid, Some(Fluid::Liquid { kind: LiquidKind::Lava, .. })) { - if !buff_comp.contains(BuffKind::Burning) { - buff_comp.insert(Buff::new( - BuffKind::Burning, - BuffData::new(200.0, None), - vec![BuffCategory::Natural], - BuffSource::World, - )); - } + if matches!( + in_fluid, + Some(Fluid::Liquid { + kind: LiquidKind::Lava, + .. + }) + ) && !buff_comp.contains(BuffKind::Burning) + { + buff_comp.insert(Buff::new( + BuffKind::Burning, + BuffData::new(200.0, None), + vec![BuffCategory::Natural], + BuffSource::World, + )); } let (buff_comp_kinds, buff_comp_buffs): ( @@ -262,7 +267,15 @@ fn tick_buff( } if let Some(remaining_time) = &mut buff.time { // Extinguish Burning buff when in water - if matches!(buff.kind, BuffKind::Burning) && matches!(in_fluid, Some(Fluid::Liquid { kind: LiquidKind::Water, .. })) { + if matches!(buff.kind, BuffKind::Burning) + && matches!( + in_fluid, + Some(Fluid::Liquid { + kind: LiquidKind::Water, + .. + }) + ) + { *remaining_time = Duration::default(); } diff --git a/common/systems/src/phys.rs b/common/systems/src/phys.rs index 5e62cbbdf3..81d1cec1c5 100644 --- a/common/systems/src/phys.rs +++ b/common/systems/src/phys.rs @@ -1558,9 +1558,10 @@ fn box_voxel_collision<'a, T: BaseVol + ReadVol>( }; if player_aabb.collides_with_aabb(liquid_aabb) { liquid = match liquid { - Some((kind, max_liquid_z)) => { - Some((kind.merge(block_liquid), max_liquid_z.max(liquid_aabb.max.z))) - }, + Some((kind, max_liquid_z)) => Some(( + kind.merge(block_liquid), + max_liquid_z.max(liquid_aabb.max.z), + )), None => Some((block_liquid, liquid_aabb.max.z)), }; } diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index ee7545fbbe..56592205df 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -34,6 +34,7 @@ pub struct Colors { pub cave_roof: (u8, u8, u8), pub dirt: (u8, u8, u8), pub scaffold: (u8, u8, u8), + pub lava: (u8, u8, u8), pub vein: (u8, u8, u8), } @@ -217,10 +218,7 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) { }; canvas.set( Vec3::new(wpos2d.x, wpos2d.y, z), - Block::new( - kind, - noisy_color(info.index().colors.layer.scaffold.into(), 8), - ), + Block::new(kind, noisy_color(info.index().colors.layer.lava.into(), 8)), ); } } From 0ec6a4e5ffed583d6839f07a9c343fde669682e8 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sun, 20 Jun 2021 00:14:54 +0300 Subject: [PATCH 133/180] Create distribution map for dungeons * tl;dr T0 - 27%, T1/T2 - 22%, T3/T4 - 11%, T5 - 5%. Before every dungeon had 16% chance to be created. --- .../world/dungeon/difficulty_distribution.ron | 21 ++++++++++--------- world/src/site/dungeon/mod.rs | 7 ++++++- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/assets/world/dungeon/difficulty_distribution.ron b/assets/world/dungeon/difficulty_distribution.ron index 50a5ce163a..1460bfc6e6 100644 --- a/assets/world/dungeon/difficulty_distribution.ron +++ b/assets/world/dungeon/difficulty_distribution.ron @@ -1,24 +1,25 @@ /// Distribution of different dungeon levels. /// /// first number is dungeon level, integer -/// second number is probability, any normal positive float (not a NaN, for example) +/// second number is weight, any normal positive float (not a NaN, for example) /// /// Values are relative to each other, -/// lesser probability means there will be less dungeons. +/// lesser weight means there will be less dungeons of that tier. /// /// General rules: -/// 1) Probability should not be less or equal to zero -/// 2) Keep it synced with number of dungeon levels -/// 3) Keep these pairs sorted from lowest to highest tier +/// 1) Weight should not be less then zero +/// 2) At least some of weights shouldn't be a zero +/// 3) Keep it synced with number of dungeon levels +/// 4) Keep these pairs sorted from lowest to highest tier /// /// Tips: /// 1) Set every probability to 0.0 and left one with any other number /// and you will have map full of dungeons of same level ([ - (0, 1.0), - (1, 1.0), - (2, 1.0), - (3, 1.0), - (4, 1.0), + (0, 5.0), + (1, 4.0), + (2, 4.0), + (3, 2.0), + (4, 2.0), (5, 1.0), ]) diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index 981e3f5650..bfbfe39a9d 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -70,7 +70,12 @@ impl Dungeon { let difficulty = DUNGEON_DISTRIBUTION .choose_weighted(&mut ctx.rng, |pair| pair.1) .map(|(difficulty, _)| *difficulty) - .expect("this can never fail"); + .unwrap_or_else(|err| { + panic!( + "Failed to choose difficulty (check instruction in config). Error: {}", + err + ) + }); let floors = 3 + difficulty / 2; From 0f9181cbcb0d759e0a7128d582cb869ecfd704ad Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Sun, 20 Jun 2021 11:32:05 -0400 Subject: [PATCH 134/180] Address MR 2482 comments. - Change lava density to 3x water density. - Use `ServerEvent::Buff` instead of modifying the buff component directly. - Revert commented-out changes to fluid shaders. --- assets/voxygen/shaders/fluid-frag/cheap.glsl | 19 +------------------ assets/voxygen/shaders/fluid-frag/shiny.glsl | 18 ------------------ assets/voxygen/shaders/include/srgb.glsl | 2 -- common/src/comp/fluid_dynamics.rs | 4 ++-- common/src/consts.rs | 3 +++ common/systems/src/buff.rs | 15 +++++++++------ 6 files changed, 15 insertions(+), 46 deletions(-) diff --git a/assets/voxygen/shaders/fluid-frag/cheap.glsl b/assets/voxygen/shaders/fluid-frag/cheap.glsl index 98ddf92dc4..f405fdd039 100644 --- a/assets/voxygen/shaders/fluid-frag/cheap.glsl +++ b/assets/voxygen/shaders/fluid-frag/cheap.glsl @@ -37,8 +37,6 @@ layout(location = 1) flat in uint f_pos_norm; // ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; // }; -//#define LAVA - layout(std140, set = 2, binding = 0) uniform u_locals { vec3 model_offs; @@ -80,11 +78,7 @@ void main() { // vec3 view_dir = normalize(-vec3(vert_pos4)/* / vert_pos4.w*/); vec3 view_dir = -cam_to_frag; // vec3 surf_color = /*srgb_to_linear*/(vec3(0.4, 0.7, 2.0)); -#ifdef LAVA - vec3 water_color = (1.0 - MU_LAVA); -#else /*const */vec3 water_color = (1.0 - MU_WATER) * MU_SCATTER;//srgb_to_linear(vec3(0.2, 0.5, 1.0)); -#endif // /*const */vec3 water_color = srgb_to_linear(vec3(0.0, 0.25, 0.5)); /* vec3 sun_dir = get_sun_dir(time_of_day.x); @@ -124,11 +118,7 @@ void main() { // Water is transparent so both normals are valid. vec3 cam_norm = faceforward(f_norm, f_norm, cam_to_frag); -#ifdef LAVA - vec3 mu = MU_LAVA; -#else vec3 mu = MU_WATER; -#endif // NOTE: Default intersection point is camera position, meaning if we fail to intersect we assume the whole camera is in water. vec3 cam_attenuation = vec3(1.0);//compute_attenuation_point(f_pos, -view_dir, mu, fluid_alt, cam_pos.xyz); @@ -187,14 +177,7 @@ void main() { // float reflected_light_point = /*length*/(diffuse_light_point.r) + f_light * point_shadow; // reflected_light += k_d * (diffuse_light_point + f_light * point_shadow * shade_frac) + specular_light_point; -#ifdef LAVA - float opacity = 0.9; - emitted_light = vec3(0.1, 0, 0); -#else - float opacity = 0.2; -#endif - - float passthrough = clamp(dot(cam_norm, -cam_to_frag) * 1.0 - opacity, 0, 1); + float passthrough = clamp(dot(cam_norm, -cam_to_frag) * 1.0 - 0.2, 0, 1); float min_refl = min(emitted_light.r, min(emitted_light.g, emitted_light.b)); vec3 surf_color = illuminate(max_light, view_dir, water_color * /* fog_color * */emitted_light, /*surf_color * */water_color * reflected_light); diff --git a/assets/voxygen/shaders/fluid-frag/shiny.glsl b/assets/voxygen/shaders/fluid-frag/shiny.glsl index 9c9b5c331a..019e2b0618 100644 --- a/assets/voxygen/shaders/fluid-frag/shiny.glsl +++ b/assets/voxygen/shaders/fluid-frag/shiny.glsl @@ -88,8 +88,6 @@ float wave_height(vec3 pos) { return pow(abs(height), 0.5) * sign(height) * 15.0; } -//#define LAVA - void main() { // First 3 normals are negative, next 3 are positive vec3 normals[6] = vec3[](vec3(-1,0,0), vec3(1,0,0), vec3(0,-1,0), vec3(0,1,0), vec3(0,0,-1), vec3(0,0,1)); @@ -150,12 +148,7 @@ void main() { vec3 norm = vec3(0, 0, 1) * nmap.z + b_norm * nmap.x + c_norm * nmap.y; // vec3 norm = f_norm; -#ifdef LAVA - vec3 water_color = (1.0 - MU_LAVA); -#else vec3 water_color = (1.0 - MU_WATER) * MU_SCATTER; -#endif - #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY) float f_alt = alt_at(f_pos.xy); #elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP) @@ -237,12 +230,7 @@ void main() { // vec3 water_color_direct = exp(-MU_WATER);//exp(-MU_WATER);//vec3(1.0); // vec3 water_color_direct = exp(-water_attenuation * (water_depth_to_light + water_depth_to_camera)); // vec3 water_color_ambient = exp(-water_attenuation * (water_depth_to_vertical + water_depth_to_camera)); - -#ifdef LAVA - vec3 mu = MU_LAVA; -#else vec3 mu = MU_WATER; -#endif // NOTE: Default intersection point is camera position, meaning if we fail to intersect we assume the whole camera is in water. vec3 cam_attenuation = compute_attenuation_point(f_pos, -view_dir, mu, fluid_alt, cam_pos.xyz); // float water_depth_to_vertical = max(/*f_alt - f_pos.z*/f_light, 0.0); @@ -302,13 +290,7 @@ void main() { // reflected_light += point_light; // vec3 surf_color = srgb_to_linear(vec3(0.2, 0.5, 1.0)) * light * diffuse_light * ambient_light; const float REFLECTANCE = 0.5; -#ifdef LAVA - //vec3 surf_color = illuminate(max_light, view_dir, water_color * emitted_light, reflect_color * REFLECTANCE + water_color * reflected_light); - vec3 surf_color = vec3(1, 0.25, 0); -#else vec3 surf_color = illuminate(max_light, view_dir, water_color * emitted_light/* * log(1.0 - MU_WATER)*/, /*cam_attenuation * *//*water_color * */reflect_color * REFLECTANCE + water_color * reflected_light/* * log(1.0 - MU_WATER)*/); -#endif - //vec3 surf_color = illuminate(max_light, view_dir, water_color * emitted_light/* * log(1.0 - MU_WATER)*/, /*cam_attenuation * *//*water_color * */reflect_color * REFLECTANCE + water_color * reflected_light/* * log(1.0 - MU_WATER)*/); // passthrough = pow(passthrough, 1.0 / (1.0 + water_depth_to_camera)); /* surf_color = cam_attenuation.g < 0.5 ? diff --git a/assets/voxygen/shaders/include/srgb.glsl b/assets/voxygen/shaders/include/srgb.glsl index 92ee63d21c..118221a137 100644 --- a/assets/voxygen/shaders/include/srgb.glsl +++ b/assets/voxygen/shaders/include/srgb.glsl @@ -2,9 +2,7 @@ #define SRGB_GLSL // Linear RGB, attenuation coefficients for water at roughly R, G, B wavelengths. // See https://en.wikipedia.org/wiki/Electromagnetic_absorption_by_water - const vec3 MU_WATER = vec3(0.6, 0.04, 0.01); -const vec3 MU_LAVA = vec3(0.1, 0.75, 1.0); // // NOTE: Automatic in v4.0 // float diff --git a/common/src/comp/fluid_dynamics.rs b/common/src/comp/fluid_dynamics.rs index 081ca46560..6e233545e3 100644 --- a/common/src/comp/fluid_dynamics.rs +++ b/common/src/comp/fluid_dynamics.rs @@ -2,7 +2,7 @@ use super::body::{object, Body}; use super::{Density, Ori, Vel}; use crate::{ - consts::{AIR_DENSITY, WATER_DENSITY}, + consts::{AIR_DENSITY, LAVA_DENSITY, WATER_DENSITY}, util::{Dir, Plane, Projection}, }; use serde::{Deserialize, Serialize}; @@ -55,7 +55,7 @@ impl Fluid { Self::Liquid { kind: LiquidKind::Lava, .. - } => Density(WATER_DENSITY), + } => Density(LAVA_DENSITY), } } diff --git a/common/src/consts.rs b/common/src/consts.rs index e4a047e5a8..4cbaa9598e 100644 --- a/common/src/consts.rs +++ b/common/src/consts.rs @@ -13,6 +13,9 @@ pub const FRIC_GROUND: f32 = 0.15; // kg/m³ pub const AIR_DENSITY: f32 = 1.225; pub const WATER_DENSITY: f32 = 999.1026; +// LAVA_DENSITY is unsourced, estimated as "roughly three times higher" than +// water +pub const LAVA_DENSITY: f32 = 3000.0; pub const IRON_DENSITY: f32 = 7870.0; // pub const HUMAN_DENSITY: f32 = 1010.0; // real value pub const HUMAN_DENSITY: f32 = 990.0; // value we use to make humanoids gently float diff --git a/common/systems/src/buff.rs b/common/systems/src/buff.rs index 20e8a0c612..3275bb60bb 100644 --- a/common/systems/src/buff.rs +++ b/common/systems/src/buff.rs @@ -65,12 +65,15 @@ impl<'a> System<'a> for Sys { }) ) && !buff_comp.contains(BuffKind::Burning) { - buff_comp.insert(Buff::new( - BuffKind::Burning, - BuffData::new(200.0, None), - vec![BuffCategory::Natural], - BuffSource::World, - )); + server_emitter.emit(ServerEvent::Buff { + entity, + buff_change: BuffChange::Add(Buff::new( + BuffKind::Burning, + BuffData::new(200.0, None), + vec![BuffCategory::Natural], + BuffSource::World, + )), + }); } let (buff_comp_kinds, buff_comp_buffs): ( From 79d7e79776db577faf24380233c7d492a848ca25 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 20 Jun 2021 16:00:37 +0000 Subject: [PATCH 135/180] Accelerate econsim using arrays instead of hashmaps --- server/src/events/information.rs | 16 +- world/economy_testinput2.ron | 45 ++ world/examples/economy_tree.rs | 32 +- world/src/sim2/mod.rs | 418 +++++++++----- world/src/site/economy.rs | 956 +++++++++++++++++++------------ world/src/site/mod.rs | 2 +- world/src/util/map_array.rs | 110 ++++ world/src/util/mod.rs | 1 + 8 files changed, 1040 insertions(+), 540 deletions(-) create mode 100644 world/economy_testinput2.ron create mode 100644 world/src/util/map_array.rs diff --git a/server/src/events/information.rs b/server/src/events/information.rs index 490fe2ba44..cd11861ccd 100644 --- a/server/src/events/information.rs +++ b/server/src/events/information.rs @@ -1,4 +1,5 @@ use crate::{client::Client, Server}; +use common::trade::Good; use common_net::msg::{world_msg::EconomyInfo, ServerGeneral}; use specs::{Entity as EcsEntity, WorldExt}; use std::collections::HashMap; @@ -32,25 +33,30 @@ pub fn handle_site_info(server: &Server, entity: EcsEntity, id: u64) { EconomyInfo { id, population: site.economy.pop.floor() as u32, - stock: site.economy.stocks.iter().map(|(g, a)| (g, *a)).collect(), + stock: site + .economy + .stocks + .iter() + .map(|(g, a)| (Good::from(g), *a)) + .collect(), labor_values: site .economy .labor_values .iter() - .filter_map(|(g, a)| a.map(|a| (g, a))) + .filter_map(|(g, a)| a.map(|a| (Good::from(g), a))) .collect(), values: site .economy .values .iter() - .filter_map(|(g, a)| a.map(|a| (g, a))) + .filter_map(|(g, a)| a.map(|a| (Good::from(g), a))) .collect(), labors: site.economy.labors.iter().map(|(_, a)| (*a)).collect(), last_exports: site .economy .last_exports .iter() - .map(|(g, a)| (g, *a)) + .map(|(g, a)| (Good::from(g), *a)) .collect(), resources: site .economy @@ -59,7 +65,7 @@ pub fn handle_site_info(server: &Server, entity: EcsEntity, id: u64) { .iter() .map(|(g, a)| { ( - g, + Good::from(g), ((*a) as f32) * site.economy.natural_resources.average_yield_per_chunk[g], ) }) diff --git a/world/economy_testinput2.ron b/world/economy_testinput2.ron new file mode 100644 index 0000000000..16e10df778 --- /dev/null +++ b/world/economy_testinput2.ron @@ -0,0 +1,45 @@ +[ + ( + name: "Forest Settlement", + position: (1, 1), + kind: Settlement, + neighbors: [ + (1, 10), + (2, 10), + ], + resources: [ + ( + good: Terrain(Forest), + amount: 1000, + ), + ], + ), + ( + name: "Moutain Peak", + position: (10, 10), + kind: Settlement, + neighbors: [ + (0, 10), + ], + resources: [ + ( + good: Terrain(Mountain), + amount: 1000, + ), + ], + ), + ( + name: "Farmer Village", + position: (20, 10), + kind: Settlement, + neighbors: [ + (0, 10), + ], + resources: [ + ( + good: Terrain(Grassland), + amount: 1000, + ), + ], + ), +] \ No newline at end of file diff --git a/world/examples/economy_tree.rs b/world/examples/economy_tree.rs index 4bc05d322d..d60ac0ac79 100644 --- a/world/examples/economy_tree.rs +++ b/world/examples/economy_tree.rs @@ -21,13 +21,13 @@ fn main() -> Result<(), std::io::Error> { let mut f = std::fs::File::create("economy.gv")?; writeln!(f, "digraph economy {{")?; - for i in good_list().iter() { - let color = if economy::direct_use_goods().contains(i) { + for i in good_list() { + let color = if economy::direct_use_goods().contains(&i) { "green" } else { "orange" }; - writeln!(f, "{:?} [color=\"{}\"];", good_name(*i), color)?; // shape doubleoctagon ? + writeln!(f, "{:?} [color=\"{}\"];", good_name(i.into()), color)?; // shape doubleoctagon ? } writeln!(f)?; @@ -42,9 +42,9 @@ fn main() -> Result<(), std::io::Error> { for i in o.iter() { for j in i.1.iter() { if i.0.is_some() { - let style = if matches!(j.0, Good::Tools) - || matches!(j.0, Good::Armor) - || matches!(j.0, Good::Potions) + let style = if matches!(j.0.into(), Good::Tools) + || matches!(j.0.into(), Good::Armor) + || matches!(j.0.into(), Good::Potions) { ", style=dashed, color=orange" } else { @@ -53,7 +53,7 @@ fn main() -> Result<(), std::io::Error> { writeln!( f, "{:?} -> {:?} [label=\"{:.1}\"{}];", - good_name(j.0), + good_name(j.0.into()), labor_name(i.0.unwrap()), j.1, style @@ -62,7 +62,7 @@ fn main() -> Result<(), std::io::Error> { writeln!( f, "{:?} -> Everyone [label=\"{:.1}\"];", - good_name(j.0), + good_name(j.0.into()), j.1 )?; } @@ -72,15 +72,13 @@ fn main() -> Result<(), std::io::Error> { writeln!(f)?; writeln!(f, "// Products")?; for i in p.iter() { - for j in i.1.iter() { - writeln!( - f, - "{:?} -> {:?} [label=\"{:.1}\"];", - labor_name(i.0), - good_name(j.0), - j.1 - )?; - } + writeln!( + f, + "{:?} -> {:?} [label=\"{:.1}\"];", + labor_name(i.0), + good_name(i.1.0.into()), + i.1.1 + )?; } writeln!(f, "}}")?; diff --git a/world/src/sim2/mod.rs b/world/src/sim2/mod.rs index 92b64705b6..4c27fa849c 100644 --- a/world/src/sim2/mod.rs +++ b/world/src/sim2/mod.rs @@ -2,12 +2,12 @@ use crate::{ sim::WorldSim, site::{ economy::{ - decay_rate, direct_use_goods, good_list, transportation_effort, Economy, Labor, - TradeDelivery, TradeOrder, + decay_rate, direct_use_goods, good_list, transportation_effort, Economy, GoodIndex, + GoodMap, LaborIndex, LaborMap, TradeDelivery, TradeOrder, }, Site, SiteKind, }, - util::{DHashMap, DHashSet, MapVec}, + util::{DHashMap, DHashSet}, Index, }; use common::{ @@ -17,7 +17,8 @@ use common::{ Good::{Coin, Transportation}, }, }; -use std::cmp::Ordering::Less; +use lazy_static::lazy_static; +use std::{cmp::Ordering::Less, convert::TryInto}; use tracing::{debug, info}; const MONTH: f32 = 30.0; @@ -28,6 +29,32 @@ const HISTORY_DAYS: f32 = 500.0 * YEAR; // 500 years const GENERATE_CSV: bool = false; const INTER_SITE_TRADE: bool = true; +// this is an empty replacement for https://github.com/cpetig/vergleich +// which can be used to compare values acros runs +mod vergleich { + pub struct Error {} + impl Error { + pub fn to_string(&self) -> &'static str { "" } + } + pub struct ProgramRun {} + impl ProgramRun { + pub fn new(_: &str) -> Result { Ok(Self {}) } + + pub fn set_epsilon(&mut self, _: f32) {} + + pub fn context(&mut self, _: &str) -> Context { Context {} } + + //pub fn value(&mut self, _: &str, val: f32) -> f32 { val } + } + pub struct Context {} + impl Context { + pub fn context(&mut self, _: &str) -> Context { Context {} } + + pub fn value(&mut self, _: &str, val: f32) -> f32 { val } + } +} + +/// Statistics collector (min, max, avg) #[derive(Debug)] struct EconStatistics { pub count: u32, @@ -41,12 +68,16 @@ impl Default for EconStatistics { Self { count: 0, sum: 0.0, - min: 1e30, - max: 0.0, + min: f32::INFINITY, + max: -f32::INFINITY, } } } +impl std::ops::AddAssign for EconStatistics { + fn add_assign(&mut self, rhs: f32) { self.collect(rhs); } +} + impl EconStatistics { fn collect(&mut self, value: f32) { self.count += 1; @@ -58,6 +89,8 @@ impl EconStatistics { self.min = value; } } + + fn valid(&self) -> bool { self.min.is_finite() } } pub fn csv_entry(f: &mut std::fs::File, site: &Site) -> Result<(), std::io::Error> { @@ -71,24 +104,24 @@ pub fn csv_entry(f: &mut std::fs::File, site: &Site) -> Result<(), std::io::Erro site.economy.pop )?; for g in good_list() { - write!(*f, "{:?},", site.economy.values[*g].unwrap_or(-1.0))?; + write!(*f, "{:?},", site.economy.values[g].unwrap_or(-1.0))?; } for g in good_list() { - write!(f, "{:?},", site.economy.labor_values[*g].unwrap_or(-1.0))?; + write!(f, "{:?},", site.economy.labor_values[g].unwrap_or(-1.0))?; } for g in good_list() { - write!(f, "{:?},", site.economy.stocks[*g])?; + write!(f, "{:?},", site.economy.stocks[g])?; } for g in good_list() { - write!(f, "{:?},", site.economy.marginal_surplus[*g])?; + write!(f, "{:?},", site.economy.marginal_surplus[g])?; } - for l in Labor::list() { + for l in LaborIndex::list() { write!(f, "{:?},", site.economy.labors[l] * site.economy.pop)?; } - for l in Labor::list() { + for l in LaborIndex::list() { write!(f, "{:?},", site.economy.productivity[l])?; } - for l in Labor::list() { + for l in LaborIndex::list() { write!(f, "{:?},", site.economy.yields[l])?; } writeln!(f) @@ -113,13 +146,13 @@ fn simulate_return(index: &mut Index, world: &mut WorldSim) -> Result<(), std::i for g in good_list() { write!(f, "{:?} Surplus,", g)?; } - for l in Labor::list() { + for l in LaborIndex::list() { write!(f, "{:?} Labor,", l)?; } - for l in Labor::list() { + for l in LaborIndex::list() { write!(f, "{:?} Productivity,", l)?; } - for l in Labor::list() { + for l in LaborIndex::list() { write!(f, "{:?} Yields,", l)?; } writeln!(f)?; @@ -129,12 +162,15 @@ fn simulate_return(index: &mut Index, world: &mut WorldSim) -> Result<(), std::i }; tracing::info!("economy simulation start"); + let mut vr = vergleich::ProgramRun::new("economy_compare.sqlite") + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + vr.set_epsilon(0.1); for i in 0..(HISTORY_DAYS / TICK_PERIOD) as i32 { if (index.time / YEAR) as i32 % 50 == 0 && (index.time % YEAR) as i32 == 0 { debug!("Year {}", (index.time / YEAR) as i32); } - tick(index, world, TICK_PERIOD); + tick(index, world, TICK_PERIOD, vr.context(&i.to_string())); if let Some(f) = f.as_mut() { if i % 5 == 0 { @@ -165,33 +201,40 @@ fn simulate_return(index: &mut Index, world: &mut WorldSim) -> Result<(), std::i for site in index.sites.ids() { let site = &index.sites[site]; match site.kind { - SiteKind::Dungeon(_) => dungeons.collect(site.economy.pop), - SiteKind::Settlement(_) => towns.collect(site.economy.pop), - SiteKind::Castle(_) => castles.collect(site.economy.pop), + SiteKind::Dungeon(_) => dungeons += site.economy.pop, + SiteKind::Settlement(_) => towns += site.economy.pop, + SiteKind::Castle(_) => castles += site.economy.pop, SiteKind::Tree(_) => (), SiteKind::Refactor(_) => (), } } - info!( - "Towns {:.0}-{:.0} avg {:.0} inhabitants", - towns.min, - towns.max, - towns.sum / (towns.count as f32) - ); - info!( - "Castles {:.0}-{:.0} avg {:.0}", - castles.min, - castles.max, - castles.sum / (castles.count as f32) - ); - info!( - "Dungeons {:.0}-{:.0} avg {:.0}", - dungeons.min, - dungeons.max, - dungeons.sum / (dungeons.count as f32) - ); + if towns.valid() { + info!( + "Towns {:.0}-{:.0} avg {:.0} inhabitants", + towns.min, + towns.max, + towns.sum / (towns.count as f32) + ); + } + if castles.valid() { + info!( + "Castles {:.0}-{:.0} avg {:.0}", + castles.min, + castles.max, + castles.sum / (castles.count as f32) + ); + } + if dungeons.valid() { + info!( + "Dungeons {:.0}-{:.0} avg {:.0}", + dungeons.min, + dungeons.max, + dungeons.sum / (dungeons.count as f32) + ); + } check_money(index); } + Ok(()) } @@ -203,12 +246,12 @@ pub fn simulate(index: &mut Index, world: &mut WorldSim) { fn check_money(index: &mut Index) { let mut sum_stock: f32 = 0.0; for site in index.sites.values() { - sum_stock += site.economy.stocks[Coin]; + sum_stock += site.economy.stocks[*COIN_INDEX]; } let mut sum_del: f32 = 0.0; for v in index.trade.deliveries.values() { for del in v.iter() { - sum_del += del.amount[Coin]; + sum_del += del.amount[*COIN_INDEX]; } } info!( @@ -219,15 +262,16 @@ fn check_money(index: &mut Index) { ); } -pub fn tick(index: &mut Index, _world: &mut WorldSim, dt: f32) { +pub fn tick(index: &mut Index, _world: &mut WorldSim, dt: f32, mut vc: vergleich::Context) { let site_ids = index.sites.ids().collect::>(); for site in site_ids { - tick_site_economy(index, site, dt); + tick_site_economy(index, site, dt, vc.context(&site.id().to_string())); } if INTER_SITE_TRADE { for (&site, orders) in index.trade.orders.iter_mut() { let siteinfo = index.sites.get_mut(site); if siteinfo.do_economic_simulation() { + // let name: String = siteinfo.name().into(); trade_at_site( site, orders, @@ -242,6 +286,12 @@ pub fn tick(index: &mut Index, _world: &mut WorldSim, dt: f32) { index.time += dt; } +lazy_static! { + static ref COIN_INDEX: GoodIndex = Coin.try_into().unwrap_or_default(); + static ref FOOD_INDEX: GoodIndex = Good::Food.try_into().unwrap_or_default(); + static ref TRANSPORTATION_INDEX: GoodIndex = Transportation.try_into().unwrap_or_default(); +} + /// plan the trading according to missing goods and prices at neighboring sites /// (1st step of trading) // returns wares spent (-) and procured (+) @@ -251,8 +301,8 @@ fn plan_trade_for_site( site_id: &Id, transportation_capacity: f32, external_orders: &mut DHashMap, Vec>, - potential_trade: &mut MapVec, -) -> MapVec { + potential_trade: &mut GoodMap, +) -> GoodMap { // TODO: Do we have some latency of information here (using last years // capacity?) //let total_transport_capacity = site.economy.stocks[Transportation]; @@ -264,14 +314,14 @@ fn plan_trade_for_site( let mut collect_capacity = transportation_capacity; let mut missing_dispatch: f32 = 0.0; let mut missing_collect: f32 = 0.0; - let mut result = MapVec::from_default(0.0); + let mut result = GoodMap::default(); const MIN_SELL_PRICE: f32 = 1.0; // value+amount per good - let mut missing_goods: Vec<(Good, (f32, f32))> = site + let mut missing_goods: Vec<(GoodIndex, (f32, f32))> = site .economy .surplus .iter() - .filter(|(g, a)| (**a < 0.0 && *g != Transportation)) + .filter(|(g, a)| (**a < 0.0 && *g != *TRANSPORTATION_INDEX)) .map(|(g, a)| { ( g, @@ -283,17 +333,20 @@ fn plan_trade_for_site( }) .collect(); missing_goods.sort_by(|a, b| b.1.0.partial_cmp(&a.1.0).unwrap_or(Less)); - let mut extra_goods: MapVec = MapVec::from_iter( + let mut extra_goods: GoodMap = GoodMap::from_iter( site.economy .surplus .iter() - .chain(core::iter::once((Coin, &site.economy.stocks[Coin]))) - .filter(|(g, a)| (**a > 0.0 && *g != Transportation)) + .chain(core::iter::once(( + *COIN_INDEX, + &site.economy.stocks[*COIN_INDEX], + ))) + .filter(|(g, a)| (**a > 0.0 && *g != *TRANSPORTATION_INDEX)) .map(|(g, a)| (g, *a)), 0.0, ); // ratio+price per good and site - type GoodRatioPrice = Vec<(Good, (f32, f32))>; + type GoodRatioPrice = Vec<(GoodIndex, (f32, f32))>; let good_payment: DHashMap, GoodRatioPrice> = site .economy .neighbors @@ -322,7 +375,7 @@ fn plan_trade_for_site( .collect(); // price+stock per site and good type SitePriceStock = Vec<(Id, (f32, f32))>; - let mut good_price: DHashMap = missing_goods + let mut good_price: DHashMap = missing_goods .iter() .map(|(g, _)| { (*g, { @@ -340,11 +393,11 @@ fn plan_trade_for_site( .collect(); // TODO: we need to introduce priority (according to available transportation // capacity) - let mut neighbor_orders: DHashMap, MapVec> = site + let mut neighbor_orders: DHashMap, GoodMap> = site .economy .neighbors .iter() - .map(|n| (n.id, MapVec::default())) + .map(|n| (n.id, GoodMap::default())) .collect(); if site_id.id() == 1 { // cut down number of lines printed @@ -450,7 +503,7 @@ fn plan_trade_for_site( } let to = TradeOrder { customer: *site_id, - amount: orders.clone(), + amount: *orders, }; if let Some(o) = external_orders.get_mut(&n.id) { // this is just to catch unbound growth (happened in development) @@ -474,7 +527,8 @@ fn plan_trade_for_site( missing_collect, missing_dispatch, ); - result[Transportation] = -(transportation_capacity - collect_capacity.min(dispatch_capacity) + result[*TRANSPORTATION_INDEX] = -(transportation_capacity + - collect_capacity.min(dispatch_capacity) + missing_collect.max(missing_dispatch)); if site_id.id() == 1 { debug!("Trade {:?}", result); @@ -493,7 +547,7 @@ fn trade_at_site( // TODO: rework using economy.unconsumed_stock let internal_orders = economy.get_orders(); - let mut next_demand = MapVec::from_default(0.0); + let mut next_demand = GoodMap::from_default(0.0); for (labor, orders) in &internal_orders { let workers = if let Some(labor) = labor { economy.labors[*labor] @@ -506,13 +560,13 @@ fn trade_at_site( } } //info!("Trade {} {}", site.id(), orders.len()); - let mut total_orders: MapVec = MapVec::from_default(0.0); + let mut total_orders: GoodMap = GoodMap::from_default(0.0); for i in orders.iter() { for (g, &a) in i.amount.iter().filter(|(_, a)| **a > 0.0) { total_orders[g] += a; } } - let order_stock_ratio: MapVec> = MapVec::from_iter( + let order_stock_ratio: GoodMap> = GoodMap::from_iter( economy .stocks .iter() @@ -522,7 +576,7 @@ fn trade_at_site( None, ); debug!("trade {} {:?}", site.id(), order_stock_ratio); - let prices = MapVec::from_iter( + let prices = GoodMap::from_iter( economy .values .iter() @@ -532,14 +586,14 @@ fn trade_at_site( for o in orders.drain(..) { // amount, local value (sell low value, buy high value goods first (trading // town's interest)) - let mut sorted_sell: Vec<(Good, f32, f32)> = o + let mut sorted_sell: Vec<(GoodIndex, f32, f32)> = o .amount .iter() .filter(|(_, &a)| a > 0.0) .map(|(g, a)| (g, *a, prices[g])) .collect(); sorted_sell.sort_by(|a, b| (a.2.partial_cmp(&b.2).unwrap_or(Less))); - let mut sorted_buy: Vec<(Good, f32, f32)> = o + let mut sorted_buy: Vec<(GoodIndex, f32, f32)> = o .amount .iter() .filter(|(_, &a)| a < 0.0) @@ -552,7 +606,7 @@ fn trade_at_site( sorted_sell, sorted_buy ); - let mut good_delivery = MapVec::from_default(0.0); + let mut good_delivery = GoodMap::from_default(0.0); for (g, amount, price) in sorted_sell.iter() { if let Some(order_stock_ratio) = order_stock_ratio[*g] { let allocated_amount = *amount / order_stock_ratio.max(1.0); @@ -605,8 +659,8 @@ fn trade_at_site( } let delivery = TradeDelivery { supplier: site, - prices: prices.clone(), - supply: MapVec::from_iter( + prices, + supply: GoodMap::from_iter( economy.stocks.iter().map(|(g, a)| { (g, { (a - next_demand[g] - total_orders[g]).max(0.0) + good_delivery[g] @@ -630,9 +684,13 @@ fn trade_at_site( } /// 3rd step of trading -fn collect_deliveries(site: &mut Site, deliveries: &mut Vec) { +fn collect_deliveries( + site: &mut Site, + deliveries: &mut Vec, + ctx: &mut vergleich::Context, +) { // collect all the goods we shipped - let mut last_exports = MapVec::from_iter( + let mut last_exports = GoodMap::from_iter( site.economy .active_exports .iter() @@ -642,8 +700,9 @@ fn collect_deliveries(site: &mut Site, deliveries: &mut Vec) { ); // TODO: properly rate benefits created by merchants (done below?) for mut d in deliveries.drain(..) { + let mut ictx = ctx.context(&format!("suppl {}", d.supplier.id())); for i in d.amount.iter() { - last_exports[i.0] -= *i.1; + last_exports[i.0] -= ictx.value(&format!("{:?}", i.0), *i.1); } // remember price if let Some(n) = site @@ -691,7 +750,12 @@ fn collect_deliveries(site: &mut Site, deliveries: &mut Vec) { /// dynamically react to environmental changes. If a product becomes available /// through a mechanism such as trade, an entire arm of the economy may /// materialise to take advantage of this. -pub fn tick_site_economy(index: &mut Index, site_id: Id, dt: f32) { +pub fn tick_site_economy( + index: &mut Index, + site_id: Id, + dt: f32, + mut vc: vergleich::Context, +) { let site = &mut index.sites[site_id]; if !site.do_economic_simulation() { return; @@ -701,14 +765,19 @@ pub fn tick_site_economy(index: &mut Index, site_id: Id, dt: f32) { if INTER_SITE_TRADE { let deliveries = index.trade.deliveries.get_mut(&site_id); if let Some(deliveries) = deliveries { - collect_deliveries(site, deliveries); + collect_deliveries(site, deliveries, &mut vc); } } let orders = site.economy.get_orders(); let productivity = site.economy.get_productivity(); - let mut demand = MapVec::from_default(0.0); + for i in productivity.iter() { + vc.context("productivity") + .value(&std::format!("{:?}{:?}", i.0, Good::from(i.1.0)), i.1.1); + } + + let mut demand = GoodMap::from_default(0.0); for (labor, orders) in &orders { let workers = if let Some(labor) = labor { site.economy.labors[*labor] @@ -719,33 +788,49 @@ pub fn tick_site_economy(index: &mut Index, site_id: Id, dt: f32) { demand[*good] += *amount * workers; } } + if INTER_SITE_TRADE { + demand[*COIN_INDEX] += Economy::STARTING_COIN; // if we spend coin value increases + } // which labor is the merchant let merchant_labor = productivity .iter() - .find(|(_, v)| (**v).iter().any(|(g, _)| *g == Transportation)) + .find(|(_, v)| v.0 == *TRANSPORTATION_INDEX) .map(|(l, _)| l); - let mut supply = site.economy.stocks.clone(); //MapVec::from_default(0.0); + let mut supply = site.economy.stocks; //GoodMap::from_default(0.0); for (labor, goodvec) in productivity.iter() { - for (output_good, _) in goodvec.iter() { - supply[*output_good] += - site.economy.yields[labor] * site.economy.labors[labor] * site.economy.pop; - } + //for (output_good, _) in goodvec.iter() { + //info!("{} supply{:?}+={}", site_id.id(), Good::from(goodvec.0), + // site.economy.yields[labor] * site.economy.labors[labor] * site.economy.pop); + supply[goodvec.0] += + site.economy.yields[labor] * site.economy.labors[labor] * site.economy.pop; + vc.context(&std::format!("{:?}-{:?}", Good::from(goodvec.0), labor)) + .value("yields", site.economy.yields[labor]); + vc.context(&std::format!("{:?}-{:?}", Good::from(goodvec.0), labor)) + .value("labors", site.economy.labors[labor]); + //} + } + + for i in supply.iter() { + vc.context("supply") + .value(&std::format!("{:?}", Good::from(i.0)), *i.1); } let stocks = &site.economy.stocks; - site.economy.surplus = demand - .clone() - .map(|g, demand| supply[g] + stocks[g] - demand); - site.economy.marginal_surplus = demand.clone().map(|g, demand| supply[g] - demand); + for i in stocks.iter() { + vc.context("stocks") + .value(&std::format!("{:?}", Good::from(i.0)), *i.1); + } + site.economy.surplus = demand.map(|g, demand| supply[g] + stocks[g] - demand); + site.economy.marginal_surplus = demand.map(|g, demand| supply[g] - demand); // plan trading with other sites let mut external_orders = &mut index.trade.orders; - let mut potential_trade = MapVec::from_default(0.0); + let mut potential_trade = GoodMap::from_default(0.0); // use last year's generated transportation for merchants (could we do better? // this is in line with the other professions) - let transportation_capacity = site.economy.stocks[Transportation]; + let transportation_capacity = site.economy.stocks[*TRANSPORTATION_INDEX]; let trade = if INTER_SITE_TRADE { let trade = plan_trade_for_site( site, @@ -754,10 +839,12 @@ pub fn tick_site_economy(index: &mut Index, site_id: Id, dt: f32) { &mut external_orders, &mut potential_trade, ); - site.economy.active_exports = MapVec::from_iter(trade.iter().map(|(g, a)| (g, -*a)), 0.0); // TODO: check for availability? + site.economy.active_exports = GoodMap::from_iter(trade.iter().map(|(g, a)| (g, -*a)), 0.0); // TODO: check for availability? // add the wares to sell to demand and the goods to buy to supply for (g, a) in trade.iter() { + vc.context("trade") + .value(&std::format!("{:?}", Good::from(g)), *a); if *a > 0.0 { supply[g] += *a; assert!(supply[g] >= 0.0); @@ -766,72 +853,87 @@ pub fn tick_site_economy(index: &mut Index, site_id: Id, dt: f32) { assert!(demand[g] >= 0.0); } } - demand[Coin] += Economy::STARTING_COIN; // if we spend coin value increases trade } else { - MapVec::default() + GoodMap::default() }; // Update values according to the surplus of each stock // Note that values are used for workforce allocation and are not the same thing // as price + // fall back to old (less wrong than other goods) coin logic + let old_coin_surplus = site.economy.stocks[*COIN_INDEX] - demand[*COIN_INDEX]; let values = &mut site.economy.values; - site.economy - .surplus - .iter() - .chain(std::iter::once(( - Coin, - &(site.economy.stocks[Coin] - demand[Coin]), - ))) - .for_each(|(good, surplus)| { - // Value rationalisation - let val = 2.0f32.powf(1.0 - *surplus / demand[good]); - let smooth = 0.8; - values[good] = if val > 0.001 && val < 1000.0 { - Some(smooth * values[good].unwrap_or(val) + (1.0 - smooth) * val) - } else { - None - }; - }); - let all_trade_goods: DHashSet = trade + site.economy.surplus.iter().for_each(|(good, surplus)| { + let old_surplus = if good == *COIN_INDEX { + old_coin_surplus + } else { + *surplus + }; + // Value rationalisation + let goodname = std::format!("{:?}", Good::from(good)); + vc.context("old_surplus").value(&goodname, old_surplus); + vc.context("demand").value(&goodname, demand[good]); + let val = 2.0f32.powf(1.0 - old_surplus / demand[good]); + let smooth = 0.8; + values[good] = if val > 0.001 && val < 1000.0 { + Some(vc.context("values").value( + &goodname, + smooth * values[good].unwrap_or(val) + (1.0 - smooth) * val, + )) + } else { + None + }; + }); + + let all_trade_goods: DHashSet = trade .iter() - .filter(|(_, a)| **a > 0.0) .chain(potential_trade.iter()) + .filter(|(_, a)| **a > 0.0) .map(|(g, _)| g) .collect(); - let empty_goods: DHashSet = DHashSet::default(); + //let empty_goods: DHashSet = DHashSet::default(); // TODO: Does avg/max/sum make most sense for labors creating more than one good // summing favors merchants too much (as they will provide multiple // goods, so we use max instead) - let labor_ratios: MapVec = productivity.clone().map(|labor, goodvec| { - let trade_boost = if Some(labor) == merchant_labor { - all_trade_goods.iter() - } else { - empty_goods.iter() - }; - goodvec - .iter() - .map(|(g, _)| g) - .chain(trade_boost) - .map(|output_good| site.economy.values[*output_good].unwrap_or(0.0)) - .max_by(|a, b| a.abs().partial_cmp(&b.abs()).unwrap_or(Less)) - .unwrap_or(0.0) - * site.economy.productivity[labor] - }); + let labor_ratios: LaborMap = LaborMap::from_iter( + productivity.iter().map(|(labor, goodvec)| { + ( + labor, + if Some(labor) == merchant_labor { + all_trade_goods + .iter() + .chain(std::iter::once(&goodvec.0)) + .map(|&output_good| site.economy.values[output_good].unwrap_or(0.0)) + .max_by(|a, b| a.abs().partial_cmp(&b.abs()).unwrap_or(Less)) + } else { + site.economy.values[goodvec.0] + } + .unwrap_or(0.0) + * site.economy.productivity[labor], + ) + }), + 0.0, + ); debug!(?labor_ratios); let labor_ratio_sum = labor_ratios.iter().map(|(_, r)| *r).sum::().max(0.01); + let mut labor_context = vc.context("labor"); productivity.iter().for_each(|(labor, _)| { let smooth = 0.8; - site.economy.labors[labor] = smooth * site.economy.labors[labor] - + (1.0 - smooth) - * (labor_ratios[labor].max(labor_ratio_sum / 1000.0) / labor_ratio_sum); + site.economy.labors[labor] = labor_context.value( + &format!("{:?}", labor), + smooth * site.economy.labors[labor] + + (1.0 - smooth) + * (labor_ratios[labor].max(labor_ratio_sum / 1000.0) / labor_ratio_sum), + ); assert!(site.economy.labors[labor] >= 0.0); }); // Production - let stocks_before = site.economy.stocks.clone(); + let stocks_before = site.economy.stocks; + // TODO: Should we recalculate demand after labor reassignment? let direct_use = direct_use_goods(); // Handle the stocks you can't pile (decay) @@ -839,9 +941,9 @@ pub fn tick_site_economy(index: &mut Index, site_id: Id, dt: f32) { site.economy.stocks[*g] = 0.0; } - let mut total_labor_values = MapVec::<_, f32>::default(); + let mut total_labor_values = GoodMap::::default(); // TODO: trade - let mut total_outputs = MapVec::<_, f32>::default(); + let mut total_outputs = GoodMap::::default(); for (labor, orders) in orders.iter() { let workers = if let Some(labor) = labor { site.economy.labors[*labor] @@ -886,7 +988,7 @@ pub fn tick_site_economy(index: &mut Index, site_id: Id, dt: f32) { site.economy.stocks[*good] = (site.economy.stocks[*good] - used).max(0.0); } } - let mut produced_goods: MapVec = MapVec::from_default(0.0); + let mut produced_goods: GoodMap = GoodMap::from_default(0.0); if INTER_SITE_TRADE && is_merchant { // TODO: replan for missing merchant productivity??? for (g, a) in trade.iter() { @@ -944,16 +1046,14 @@ pub fn tick_site_economy(index: &mut Index, site_id: Id, dt: f32) { //let workers = site.economy.labors[*labor] * site.economy.pop; //let final_rate = rate; //let yield_per_worker = labor_productivity; - site.economy.yields[*labor] = - labor_productivity * work_products.iter().map(|(_, r)| r).sum::(); + site.economy.yields[*labor] = labor_productivity * work_products.1; site.economy.productivity[*labor] = labor_productivity; //let total_product_rate: f32 = work_products.iter().map(|(_, r)| *r).sum(); - for (stock, rate) in work_products { - let total_output = labor_productivity * *rate * workers; - assert!(total_output >= 0.0); - site.economy.stocks[*stock] += total_output; - produced_goods[*stock] += total_output; - } + let (stock, rate) = work_products; + let total_output = labor_productivity * *rate * workers; + assert!(total_output >= 0.0); + site.economy.stocks[*stock] += total_output; + produced_goods[*stock] += total_output; let produced_amount: f32 = produced_goods.iter().map(|(_, a)| *a).sum(); for (stock, amount) in produced_goods.iter() { @@ -997,17 +1097,20 @@ pub fn tick_site_economy(index: &mut Index, site_id: Id, dt: f32) { // Births/deaths const NATURAL_BIRTH_RATE: f32 = 0.05; const DEATH_RATE: f32 = 0.005; - let birth_rate = if site.economy.surplus[Good::Food] > 0.0 { + let birth_rate = if site.economy.surplus[*FOOD_INDEX] > 0.0 { NATURAL_BIRTH_RATE } else { 0.0 }; - site.economy.pop += dt / YEAR * site.economy.pop * (birth_rate - DEATH_RATE); + site.economy.pop += vc.value( + "pop", + dt / YEAR * site.economy.pop * (birth_rate - DEATH_RATE), + ); // calculate the new unclaimed stock //let next_orders = site.economy.get_orders(); // orders are static - let mut next_demand = MapVec::from_default(0.0); + let mut next_demand = GoodMap::from_default(0.0); for (labor, orders) in orders.iter() { let workers = if let Some(labor) = labor { site.economy.labors[*labor] @@ -1019,25 +1122,26 @@ pub fn tick_site_economy(index: &mut Index, site_id: Id, dt: f32) { assert!(next_demand[*good] >= 0.0); } } - site.economy.unconsumed_stock = MapVec::from_iter( - site.economy - .stocks - .iter() - .map(|(g, a)| (g, *a - next_demand[g])), + let mut us = vc.context("unconsumed"); + site.economy.unconsumed_stock = GoodMap::from_iter( + site.economy.stocks.iter().map(|(g, a)| { + ( + g, + us.value(&format!("{:?}", Good::from(g)), *a - next_demand[g]), + ) + }), 0.0, ); } #[cfg(test)] mod tests { - use crate::{ - sim, - util::{seed_expan, MapVec}, - }; + use crate::{sim, site::economy::GoodMap, util::seed_expan}; use common::trade::Good; use rand::SeedableRng; use rand_chacha::ChaChaRng; use serde::{Deserialize, Serialize}; + use std::convert::TryInto; use tracing::{info, Level}; use tracing_subscriber::{ filter::{EnvFilter, LevelFilter}, @@ -1095,7 +1199,7 @@ mod tests { .chunks_per_resource .iter() .map(|(good, a)| ResourcesSetup { - good, + good: good.into(), amount: *a * i.economy.natural_resources.average_yield_per_chunk[good], }) .collect(); @@ -1131,10 +1235,10 @@ mod tests { } } else { let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(seed)); - let ron_file = std::fs::File::open("economy_testinput.ron") - .expect("economy_testinput.ron not found"); + let ron_file = std::fs::File::open("economy_testinput2.ron") + .expect("economy_testinput2.ron not found"); let econ_testinput: Vec = - ron::de::from_reader(ron_file).expect("economy_testinput.ron parse error"); + ron::de::from_reader(ron_file).expect("economy_testinput2.ron parse error"); for i in econ_testinput.iter() { let wpos = Vec2 { x: i.position.0, @@ -1159,8 +1263,10 @@ mod tests { //let c = sim::SimChunk::new(); //settlement.economy.add_chunk(ch, distance_squared) // bypass the API for now - settlement.economy.natural_resources.chunks_per_resource[g.good] = g.amount; - settlement.economy.natural_resources.average_yield_per_chunk[g.good] = 1.0; + settlement.economy.natural_resources.chunks_per_resource + [g.good.try_into().unwrap_or_default()] = g.amount; + settlement.economy.natural_resources.average_yield_per_chunk + [g.good.try_into().unwrap_or_default()] = 1.0; } index.sites.insert(settlement); } @@ -1176,8 +1282,8 @@ mod tests { .map(|(nid, dist)| crate::site::economy::NeighborInformation { id: nid, travel_distance: *dist, - last_values: MapVec::from_default(0.0), - last_supplies: MapVec::from_default(0.0), + last_values: GoodMap::from_default(0.0), + last_supplies: GoodMap::from_default(0.0), }) .collect(); index diff --git a/world/src/site/economy.rs b/world/src/site/economy.rs index e842b543b2..20dd4ef789 100644 --- a/world/src/site/economy.rs +++ b/world/src/site/economy.rs @@ -1,8 +1,11 @@ use crate::{ - assets::{self, AssetExt, AssetHandle}, + assets::{self, AssetExt}, sim::SimChunk, site::Site, - util::{DHashMap, MapVec}, + util::{ + map_array::{enum_from_index, index_from_enum, GenericIndex, NotFound}, + DHashMap, + }, }; use common::{ store::Id, @@ -11,347 +14,24 @@ use common::{ }; use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; -use std::{fmt, marker::PhantomData, sync::Once}; +use std::{ + convert::{TryFrom, TryInto}, + fmt::{self, Write}, + marker::PhantomData, + ops::{Index, IndexMut}, +}; use Good::*; -#[derive(Debug, Serialize, Deserialize)] -pub struct Profession { - pub name: String, - pub orders: Vec<(Good, f32)>, - pub products: Vec<(Good, f32)>, +// the opaque index type into the "map" of Goods +#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct GoodIndex { + idx: usize, } -// reference to profession -#[derive(Clone, Copy, Eq, Hash, PartialEq)] -pub struct Labor(u8, PhantomData); - -#[derive(Debug)] -pub struct AreaResources { - pub resource_sum: MapVec, - pub resource_chunks: MapVec, - pub chunks: u32, -} - -impl Default for AreaResources { - fn default() -> Self { - Self { - resource_sum: MapVec::default(), - resource_chunks: MapVec::default(), - chunks: 0, - } - } -} - -#[derive(Debug)] -pub struct NaturalResources { - // resources per distance, we should increase labor cost for far resources - pub per_area: Vec, - - // computation simplifying cached values - pub chunks_per_resource: MapVec, - pub average_yield_per_chunk: MapVec, -} - -impl Default for NaturalResources { - fn default() -> Self { - Self { - per_area: Vec::new(), - chunks_per_resource: MapVec::default(), - average_yield_per_chunk: MapVec::default(), - } - } -} - -#[derive(Debug, Deserialize)] -pub struct RawProfessions(Vec); - -impl assets::Asset for RawProfessions { - type Loader = assets::RonLoader; - - const EXTENSION: &'static str = "ron"; -} - -pub fn default_professions() -> AssetHandle { - RawProfessions::load_expect("common.professions") -} - -lazy_static! { - static ref LABOR: AssetHandle = default_professions(); - // used to define resources needed by every person - static ref DUMMY_LABOR: Labor = Labor( - LABOR - .read() - .0 - .iter() - .position(|a| a.name == "_") - .unwrap_or(0) as u8, - PhantomData - ); -} - -impl fmt::Debug for Labor { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if (self.0 as usize) < LABOR.read().0.len() { - f.write_str(&LABOR.read().0[self.0 as usize].name) - } else { - f.write_str("?") - } - } -} - -#[derive(Debug)] -pub struct TradeOrder { - pub customer: Id, - pub amount: MapVec, // positive for orders, negative for exchange -} - -#[derive(Debug)] -pub struct TradeDelivery { - pub supplier: Id, - pub amount: MapVec, // positive for orders, negative for exchange - pub prices: MapVec, // at the time of interaction - pub supply: MapVec, // maximum amount available, at the time of interaction -} - -#[derive(Debug)] -pub struct TradeInformation { - pub orders: DHashMap, Vec>, // per provider - pub deliveries: DHashMap, Vec>, // per receiver -} - -impl Default for TradeInformation { - fn default() -> Self { - Self { - orders: Default::default(), - deliveries: Default::default(), - } - } -} - -#[derive(Debug)] -pub struct NeighborInformation { - pub id: Id, - pub travel_distance: usize, - - // remembered from last interaction - pub last_values: MapVec, - pub last_supplies: MapVec, -} - -#[derive(Debug)] -pub struct Economy { - // Population - pub pop: f32, - - /// Total available amount of each good - pub stocks: MapVec, - /// Surplus stock compared to demand orders - pub surplus: MapVec, - /// change rate (derivative) of stock in the current situation - pub marginal_surplus: MapVec, - /// amount of wares not needed by the economy (helps with trade planning) - pub unconsumed_stock: MapVec, - // For some goods, such a goods without any supply, it doesn't make sense to talk about value - pub values: MapVec>, - pub last_exports: MapVec, - pub active_exports: MapVec, // unfinished trade (amount unconfirmed) - //pub export_targets: MapVec, - pub labor_values: MapVec>, - pub material_costs: MapVec, - - // Proportion of individuals dedicated to an industry - pub labors: MapVec, - // Per worker, per year, of their output good - pub yields: MapVec, - pub productivity: MapVec, - - pub natural_resources: NaturalResources, - // usize is distance - pub neighbors: Vec, -} - -static INIT: Once = Once::new(); - -impl Default for Economy { - fn default() -> Self { - INIT.call_once(|| { - LABOR.read(); - }); - Self { - pop: 32.0, - - stocks: MapVec::from_list(&[(Coin, Economy::STARTING_COIN)], 100.0), - surplus: Default::default(), - marginal_surplus: Default::default(), - values: MapVec::from_list(&[(Coin, Some(2.0))], None), - last_exports: Default::default(), - active_exports: Default::default(), - - labor_values: Default::default(), - material_costs: Default::default(), - - labors: MapVec::from_default(0.01), - yields: MapVec::from_default(1.0), - productivity: MapVec::from_default(1.0), - - natural_resources: Default::default(), - neighbors: Default::default(), - unconsumed_stock: Default::default(), - } - } -} - -impl Economy { - pub const MINIMUM_PRICE: f32 = 0.1; - pub const STARTING_COIN: f32 = 1000.0; - const _NATURAL_RESOURCE_SCALE: f32 = 1.0 / 9.0; - - pub fn cache_economy(&mut self) { - for &g in good_list() { - let amount: f32 = self - .natural_resources - .per_area - .iter() - .map(|a| a.resource_sum[g]) - .sum(); - let chunks = self - .natural_resources - .per_area - .iter() - .map(|a| a.resource_chunks[g]) - .sum(); - if chunks > 0.001 { - self.natural_resources.chunks_per_resource[g] = chunks; - self.natural_resources.average_yield_per_chunk[g] = amount / chunks; - } - } - } - - pub fn get_orders(&self) -> DHashMap, Vec<(Good, f32)>> { - LABOR - .read() - .0 - .iter() - .enumerate() - .map(|(i, p)| { - ( - if p.name == "_" { - None - } else { - Some(Labor(i as u8, PhantomData)) - }, - p.orders.clone(), - ) - }) - .collect() - } - - pub fn get_productivity(&self) -> MapVec> { - let products: MapVec> = MapVec::from_iter( - LABOR - .read() - .0 - .iter() - .enumerate() - .filter(|(_, p)| !p.products.is_empty()) - .map(|(i, p)| (Labor(i as u8, PhantomData), p.products.clone())), - vec![(Good::Terrain(BiomeKind::Void), 0.0)], - ); - products.map(|l, vec| { - let labor_ratio = self.labors[l]; - let total_workers = labor_ratio * self.pop; - // apply economy of scale (workers get more productive in numbers) - let relative_scale = 1.0 + labor_ratio; - let absolute_scale = (1.0 + total_workers / 100.0).min(3.0); - let scale = relative_scale * absolute_scale; - vec.iter() - .map(|(good, amount)| (*good, amount * scale)) - .collect() - }) - } - - pub fn replenish(&mut self, _time: f32) { - for (good, &ch) in self.natural_resources.chunks_per_resource.iter() { - let per_year = self.natural_resources.average_yield_per_chunk[good] * ch; - self.stocks[good] = self.stocks[good].max(per_year); - } - // info!("resources {:?}", self.stocks); - } - - pub fn add_chunk(&mut self, ch: &SimChunk, distance_squared: i64) { - // let biome = ch.get_biome(); - // we don't scale by pi, although that would be correct - let distance_bin = (distance_squared >> 16).min(64) as usize; - if self.natural_resources.per_area.len() <= distance_bin { - self.natural_resources - .per_area - .resize_with(distance_bin + 1, Default::default); - } - self.natural_resources.per_area[distance_bin].chunks += 1; - // self.natural_resources.per_area[distance_bin].resource_sum[Terrain(biome)] += - // 1.0; self.natural_resources.per_area[distance_bin]. - // resource_chunks[Terrain(biome)] += 1.0; TODO: Scale resources by - // rockiness or tree_density? - - let mut add_biome = |biome, amount| { - self.natural_resources.per_area[distance_bin].resource_sum[Terrain(biome)] += amount; - self.natural_resources.per_area[distance_bin].resource_chunks[Terrain(biome)] += amount; - }; - if ch.river.is_ocean() { - add_biome(BiomeKind::Ocean, 1.0); - } else if ch.river.is_lake() { - add_biome(BiomeKind::Lake, 1.0); - } else { - add_biome(BiomeKind::Forest, 0.5 + ch.tree_density); - add_biome(BiomeKind::Grassland, 0.5 + ch.humidity); - add_biome(BiomeKind::Jungle, 0.5 + ch.humidity * ch.temp.max(0.0)); - add_biome(BiomeKind::Mountain, 0.5 + (ch.alt / 4000.0).max(0.0)); - add_biome( - BiomeKind::Desert, - 0.5 + (1.0 - ch.humidity) * ch.temp.max(0.0), - ); - add_biome(BiomeKind::Snowland, 0.5 + (-ch.temp).max(0.0)); - } - } - - pub fn add_neighbor(&mut self, id: Id, distance: usize) { - self.neighbors.push(NeighborInformation { - id, - travel_distance: distance, - - last_values: MapVec::from_default(Economy::MINIMUM_PRICE), - last_supplies: Default::default(), - }); - } - - pub fn get_site_prices(&self) -> SitePrices { - let normalize = |xs: MapVec>| { - let sum = xs - .iter() - .map(|(_, x)| (*x).unwrap_or(0.0)) - .sum::() - .max(0.001); - xs.map(|_, x| Some(x? / sum)) - }; - - SitePrices { - values: { - let labor_values = normalize(self.labor_values.clone()); - // Use labor values as prices. Not correct (doesn't care about exchange value) - let prices = normalize(self.values.clone()).map(|good, value| { - (labor_values[good].unwrap_or(Economy::MINIMUM_PRICE) - + value.unwrap_or(Economy::MINIMUM_PRICE)) - * 0.5 - }); - prices.iter().map(|(g, v)| (g, *v)).collect() - }, - } - } -} - -pub fn good_list() -> &'static [Good] { - static GOODS: [Good; 23] = [ +impl GenericIndex for GoodIndex { + // static list of all Goods traded + const VALUES: [Good; GoodIndex::LENGTH] = [ // controlled resources Territory(BiomeKind::Grassland), Territory(BiomeKind::Forest), @@ -381,11 +61,563 @@ pub fn good_list() -> &'static [Good] { Terrain(BiomeKind::Ocean), ]; - &GOODS + fn from_usize(idx: usize) -> Self { Self { idx } } + + fn into_usize(self) -> usize { self.idx } } -pub fn transportation_effort(g: Good) -> f32 { - match g { +impl TryFrom for GoodIndex { + type Error = NotFound; + + fn try_from(e: Good) -> Result { index_from_enum(e) } +} + +impl From for Good { + fn from(gi: GoodIndex) -> Good { enum_from_index(gi) } +} + +impl std::fmt::Debug for GoodIndex { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + GoodIndex::VALUES[self.idx].fmt(f) + } +} + +// the "map" itself +#[derive(Copy, Clone)] +pub struct GoodMap { + data: [V; GoodIndex::LENGTH], +} + +impl Default for GoodMap { + fn default() -> Self { + GoodMap { + data: [V::default(); GoodIndex::LENGTH], + } + } +} + +impl fmt::Debug for GoodMap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("{ ")?; + for i in self.iter() { + if *i.1 != V::default() { + Good::from(i.0).fmt(f)?; + f.write_char(':')?; + i.1.fmt(f)?; + f.write_char(' ')?; + } + } + f.write_char('}') + } +} + +impl Index for GoodMap { + type Output = V; + + fn index(&self, index: GoodIndex) -> &Self::Output { &self.data[index.idx] } +} + +impl IndexMut for GoodMap { + fn index_mut(&mut self, index: GoodIndex) -> &mut Self::Output { &mut self.data[index.idx] } +} + +impl GoodMap { + pub fn iter(&self) -> impl Iterator + '_ { + (&self.data) + .iter() + .enumerate() + .map(|(idx, v)| (GoodIndex { idx }, v)) + } + + pub fn iter_mut(&mut self) -> impl Iterator + '_ { + (&mut self.data) + .iter_mut() + .enumerate() + .map(|(idx, v)| (GoodIndex { idx }, v)) + } +} + +impl GoodMap { + pub fn from_default(default: V) -> Self { + GoodMap { + data: [default; GoodIndex::LENGTH], + } + } + + pub fn from_iter(i: impl Iterator, default: V) -> Self { + let mut result = Self::from_default(default); + for j in i { + result.data[j.0.idx] = j.1; + } + result + } + + pub fn map(self, mut f: impl FnMut(GoodIndex, V) -> U) -> GoodMap { + let mut result = GoodMap::::from_default(U::default()); + for j in self.data.iter().enumerate() { + result.data[j.0] = f(GoodIndex::from_usize(j.0), *j.1); + } + result + } + + pub fn from_list<'a>(i: impl IntoIterator, default: V) -> Self + where + V: 'a, + { + let mut result = Self::from_default(default); + for j in i { + result.data[j.0.idx] = j.1; + } + result + } +} + +#[derive(Debug, Serialize, Deserialize)] +struct RawProfession { + pub name: String, + pub orders: Vec<(Good, f32)>, + pub products: Vec<(Good, f32)>, +} + +#[derive(Debug)] +pub struct Profession { + pub name: String, + pub orders: Vec<(GoodIndex, f32)>, + pub products: (GoodIndex, f32), +} + +// reference to profession +#[derive(Clone, Copy, Eq, Hash, PartialEq)] +pub struct Labor(u8, PhantomData); + +// the opaque index type into the "map" of Labors (as Labor already contains a +// monotonous index we reuse it) +pub type LaborIndex = Labor; + +impl LaborIndex { + fn from_usize(idx: usize) -> Self { Self(idx as u8, PhantomData) } + + fn into_usize(self) -> usize { self.0 as usize } +} + +// the "map" itself +#[derive(Clone)] +pub struct LaborMap { + data: Vec, +} + +impl Default for LaborMap { + fn default() -> Self { + LaborMap { + data: std::iter::repeat(V::default()).take(*LABOR_COUNT).collect(), + } + } +} + +impl fmt::Debug for LaborMap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("{ ")?; + for i in self.iter() { + if *i.1 != V::default() { + i.0.fmt(f)?; + f.write_char(':')?; + (*i.1).fmt(f)?; + f.write_char(' ')?; + } + } + f.write_char('}') + } +} + +impl Index for LaborMap { + type Output = V; + + fn index(&self, index: LaborIndex) -> &Self::Output { &self.data[index.into_usize()] } +} + +impl IndexMut for LaborMap { + fn index_mut(&mut self, index: LaborIndex) -> &mut Self::Output { + &mut self.data[index.into_usize()] + } +} + +impl LaborMap { + pub fn iter(&self) -> impl Iterator + '_ { + (&self.data) + .iter() + .enumerate() + .map(|(idx, v)| (LaborIndex::from_usize(idx), v)) + } +} + +impl LaborMap { + pub fn from_default(default: V) -> Self { + LaborMap { + data: std::iter::repeat(default).take(*LABOR_COUNT).collect(), + } + } +} + +impl LaborMap { + pub fn from_iter(i: impl Iterator, default: V) -> Self { + let mut result = Self::from_default(default); + for j in i { + result.data[j.0.into_usize()] = j.1; + } + result + } + + pub fn map(&self, f: impl Fn(LaborIndex, &V) -> U) -> LaborMap { + LaborMap { + data: self.iter().map(|i| f(i.0, i.1)).collect(), + } + } +} + +#[derive(Debug)] +pub struct AreaResources { + pub resource_sum: GoodMap, + pub resource_chunks: GoodMap, + pub chunks: u32, +} + +impl Default for AreaResources { + fn default() -> Self { + Self { + resource_sum: GoodMap::default(), + resource_chunks: GoodMap::default(), + chunks: 0, + } + } +} + +#[derive(Debug)] +pub struct NaturalResources { + // resources per distance, we should increase labor cost for far resources + pub per_area: Vec, + + // computation simplifying cached values + pub chunks_per_resource: GoodMap, + pub average_yield_per_chunk: GoodMap, +} + +impl Default for NaturalResources { + fn default() -> Self { + Self { + per_area: Vec::new(), + chunks_per_resource: GoodMap::default(), + average_yield_per_chunk: GoodMap::default(), + } + } +} + +#[derive(Debug, Deserialize)] +pub struct RawProfessions(Vec); + +impl assets::Asset for RawProfessions { + type Loader = assets::RonLoader; + + const EXTENSION: &'static str = "ron"; +} + +pub fn default_professions() -> Vec { + RawProfessions::load_expect("common.professions") + .read() + .0 + .iter() + .map(|r| Profession { + name: r.name.clone(), + orders: r + .orders + .iter() + .map(|i| (i.0.try_into().unwrap_or_default(), i.1)) + .collect(), + products: r + .products + .first() + .map(|p| (p.0.try_into().unwrap_or_default(), p.1)) + .unwrap_or_default(), + }) + .collect() +} + +lazy_static! { + static ref LABOR: Vec = default_professions(); + // used to define resources needed by every person + static ref DUMMY_LABOR: Labor = Labor( + LABOR + .iter() + .position(|a| a.name == "_") + .unwrap_or(0) as u8, + PhantomData + ); + // do not count the DUMMY_LABOR (has to be last entry) + static ref LABOR_COUNT: usize = LABOR.len()-1; +} + +impl fmt::Debug for Labor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if (self.0 as usize) < *LABOR_COUNT { + f.write_str(&LABOR[self.0 as usize].name) + } else { + f.write_str("?") + } + } +} + +impl Default for Labor { + fn default() -> Self { *DUMMY_LABOR } +} + +#[derive(Debug)] +pub struct TradeOrder { + pub customer: Id, + pub amount: GoodMap, // positive for orders, negative for exchange +} + +#[derive(Debug)] +pub struct TradeDelivery { + pub supplier: Id, + pub amount: GoodMap, // positive for orders, negative for exchange + pub prices: GoodMap, // at the time of interaction + pub supply: GoodMap, // maximum amount available, at the time of interaction +} + +#[derive(Debug)] +pub struct TradeInformation { + pub orders: DHashMap, Vec>, // per provider + pub deliveries: DHashMap, Vec>, // per receiver +} + +impl Default for TradeInformation { + fn default() -> Self { + Self { + orders: Default::default(), + deliveries: Default::default(), + } + } +} + +#[derive(Debug)] +pub struct NeighborInformation { + pub id: Id, + pub travel_distance: usize, + + // remembered from last interaction + pub last_values: GoodMap, + pub last_supplies: GoodMap, +} + +#[derive(Debug)] +pub struct Economy { + // Population + pub pop: f32, + + /// Total available amount of each good + pub stocks: GoodMap, + /// Surplus stock compared to demand orders + pub surplus: GoodMap, + /// change rate (derivative) of stock in the current situation + pub marginal_surplus: GoodMap, + /// amount of wares not needed by the economy (helps with trade planning) + pub unconsumed_stock: GoodMap, + // For some goods, such a goods without any supply, it doesn't make sense to talk about value + pub values: GoodMap>, + pub last_exports: GoodMap, + pub active_exports: GoodMap, // unfinished trade (amount unconfirmed) + //pub export_targets: GoodMap, + pub labor_values: GoodMap>, + pub material_costs: GoodMap, + + // Proportion of individuals dedicated to an industry + pub labors: LaborMap, + // Per worker, per year, of their output good + pub yields: LaborMap, + pub productivity: LaborMap, + + pub natural_resources: NaturalResources, + // usize is distance + pub neighbors: Vec, +} + +impl Default for Economy { + fn default() -> Self { + let coin_index: GoodIndex = GoodIndex::try_from(Coin).unwrap_or_default(); + Self { + pop: 32.0, + + stocks: GoodMap::from_list(&[(coin_index, Economy::STARTING_COIN)], 100.0), + surplus: Default::default(), + marginal_surplus: Default::default(), + values: GoodMap::from_list(&[(coin_index, Some(2.0))], None), + last_exports: Default::default(), + active_exports: Default::default(), + + labor_values: Default::default(), + material_costs: Default::default(), + + labors: LaborMap::from_default(0.01), + yields: LaborMap::from_default(1.0), + productivity: LaborMap::from_default(1.0), + + natural_resources: Default::default(), + neighbors: Default::default(), + unconsumed_stock: Default::default(), + } + } +} + +impl Economy { + pub const MINIMUM_PRICE: f32 = 0.1; + pub const STARTING_COIN: f32 = 1000.0; + const _NATURAL_RESOURCE_SCALE: f32 = 1.0 / 9.0; + + pub fn cache_economy(&mut self) { + for g in good_list() { + let amount: f32 = self + .natural_resources + .per_area + .iter() + .map(|a| a.resource_sum[g]) + .sum(); + let chunks = self + .natural_resources + .per_area + .iter() + .map(|a| a.resource_chunks[g]) + .sum(); + if chunks > 0.001 { + self.natural_resources.chunks_per_resource[g] = chunks; + self.natural_resources.average_yield_per_chunk[g] = amount / chunks; + } + } + } + + pub fn get_orders(&self) -> DHashMap, Vec<(GoodIndex, f32)>> { + LABOR + .iter() + .enumerate() + .map(|(i, p)| { + ( + if i == DUMMY_LABOR.0 as usize { + None + } else { + Some(LaborIndex::from_usize(i)) + }, + p.orders.clone(), + ) + }) + .collect() + } + + pub fn get_productivity(&self) -> LaborMap<(GoodIndex, f32)> { + // cache the site independent part of production + lazy_static! { + static ref PRODUCTS: LaborMap<(GoodIndex, f32)> = LaborMap::from_iter( + LABOR + .iter() + .enumerate() + .filter(|(_, p)| p.products.1 > 0.0) + .map(|(i, p)| { (LaborIndex::from_usize(i), p.products,) }), + (GoodIndex::default(), 0.0), + ); + } + PRODUCTS.map(|l, vec| { + //dbg!((l,vec)); + let labor_ratio = self.labors[l]; + let total_workers = labor_ratio * self.pop; + // apply economy of scale (workers get more productive in numbers) + let relative_scale = 1.0 + labor_ratio; + let absolute_scale = (1.0 + total_workers / 100.0).min(3.0); + let scale = relative_scale * absolute_scale; + (vec.0, vec.1 * scale) + }) + } + + pub fn replenish(&mut self, _time: f32) { + for (good, &ch) in self.natural_resources.chunks_per_resource.iter() { + let per_year = self.natural_resources.average_yield_per_chunk[good] * ch; + self.stocks[good] = self.stocks[good].max(per_year); + } + // info!("resources {:?}", self.stocks); + } + + pub fn add_chunk(&mut self, ch: &SimChunk, distance_squared: i64) { + // let biome = ch.get_biome(); + // we don't scale by pi, although that would be correct + let distance_bin = (distance_squared >> 16).min(64) as usize; + if self.natural_resources.per_area.len() <= distance_bin { + self.natural_resources + .per_area + .resize_with(distance_bin + 1, Default::default); + } + self.natural_resources.per_area[distance_bin].chunks += 1; + + let mut add_biome = |biome, amount| { + if let Ok(idx) = GoodIndex::try_from(Terrain(biome)) { + self.natural_resources.per_area[distance_bin].resource_sum[idx] += amount; + self.natural_resources.per_area[distance_bin].resource_chunks[idx] += amount; + } + }; + if ch.river.is_ocean() { + add_biome(BiomeKind::Ocean, 1.0); + } else if ch.river.is_lake() { + add_biome(BiomeKind::Lake, 1.0); + } else { + add_biome(BiomeKind::Forest, 0.5 + ch.tree_density); + add_biome(BiomeKind::Grassland, 0.5 + ch.humidity); + add_biome(BiomeKind::Jungle, 0.5 + ch.humidity * ch.temp.max(0.0)); + add_biome(BiomeKind::Mountain, 0.5 + (ch.alt / 4000.0).max(0.0)); + add_biome( + BiomeKind::Desert, + 0.5 + (1.0 - ch.humidity) * ch.temp.max(0.0), + ); + add_biome(BiomeKind::Snowland, 0.5 + (-ch.temp).max(0.0)); + } + } + + pub fn add_neighbor(&mut self, id: Id, distance: usize) { + self.neighbors.push(NeighborInformation { + id, + travel_distance: distance, + + last_values: GoodMap::from_default(Economy::MINIMUM_PRICE), + last_supplies: Default::default(), + }); + } + + pub fn get_site_prices(&self) -> SitePrices { + let normalize = |xs: GoodMap>| { + let sum = xs + .iter() + .map(|(_, x)| (*x).unwrap_or(0.0)) + .sum::() + .max(0.001); + xs.map(|_, x| Some(x? / sum)) + }; + + SitePrices { + values: { + let labor_values = normalize(self.labor_values); + // Use labor values as prices. Not correct (doesn't care about exchange value) + let prices = normalize(self.values).map(|good, value| { + (labor_values[good].unwrap_or(Economy::MINIMUM_PRICE) + + value.unwrap_or(Economy::MINIMUM_PRICE)) + * 0.5 + }); + prices.iter().map(|(g, v)| (Good::from(g), *v)).collect() + }, + } + } +} + +pub fn good_list() -> impl Iterator { + (0..GoodIndex::LENGTH).map(GoodIndex::from_usize) +} + +// cache in GoodMap ? +pub fn transportation_effort(g: GoodIndex) -> f32 { + match Good::from(g) { Terrain(_) | Territory(_) | RoadSecurity => 0.0, Coin => 0.01, Potions => 0.1, @@ -397,8 +629,8 @@ pub fn transportation_effort(g: Good) -> f32 { } } -pub fn decay_rate(g: Good) -> f32 { - match g { +pub fn decay_rate(g: GoodIndex) -> f32 { + match Good::from(g) { Food => 0.2, Flour => 0.1, Meat => 0.25, @@ -408,28 +640,30 @@ pub fn decay_rate(g: Good) -> f32 { } /** you can't accumulate or save these options/resources for later */ -pub fn direct_use_goods() -> &'static [Good] { - static DIRECT_USE: [Good; 13] = [ - Transportation, - Territory(BiomeKind::Grassland), - Territory(BiomeKind::Forest), - Territory(BiomeKind::Lake), - Territory(BiomeKind::Ocean), - Territory(BiomeKind::Mountain), - RoadSecurity, - Terrain(BiomeKind::Grassland), - Terrain(BiomeKind::Forest), - Terrain(BiomeKind::Lake), - Terrain(BiomeKind::Ocean), - Terrain(BiomeKind::Mountain), - Terrain(BiomeKind::Desert), - ]; - &DIRECT_USE +pub fn direct_use_goods() -> &'static [GoodIndex] { + lazy_static! { + static ref DIRECT_USE: [GoodIndex; 13] = [ + GoodIndex::try_from(Transportation).unwrap_or_default(), + GoodIndex::try_from(Territory(BiomeKind::Grassland)).unwrap_or_default(), + GoodIndex::try_from(Territory(BiomeKind::Forest)).unwrap_or_default(), + GoodIndex::try_from(Territory(BiomeKind::Lake)).unwrap_or_default(), + GoodIndex::try_from(Territory(BiomeKind::Ocean)).unwrap_or_default(), + GoodIndex::try_from(Territory(BiomeKind::Mountain)).unwrap_or_default(), + GoodIndex::try_from(RoadSecurity).unwrap_or_default(), + GoodIndex::try_from(Terrain(BiomeKind::Grassland)).unwrap_or_default(), + GoodIndex::try_from(Terrain(BiomeKind::Forest)).unwrap_or_default(), + GoodIndex::try_from(Terrain(BiomeKind::Lake)).unwrap_or_default(), + GoodIndex::try_from(Terrain(BiomeKind::Ocean)).unwrap_or_default(), + GoodIndex::try_from(Terrain(BiomeKind::Mountain)).unwrap_or_default(), + GoodIndex::try_from(Terrain(BiomeKind::Desert)).unwrap_or_default(), + ]; + } + &*DIRECT_USE } impl Labor { pub fn list() -> impl Iterator { - (0..LABOR.read().0.len()) + (0..LABOR.len()) .filter(|&i| i != (DUMMY_LABOR.0 as usize)) .map(|i| Self(i as u8, PhantomData)) } diff --git a/world/src/site/mod.rs b/world/src/site/mod.rs index 37283b40a7..90c6635ae7 100644 --- a/world/src/site/mod.rs +++ b/world/src/site/mod.rs @@ -151,7 +151,7 @@ impl Site { .economy .unconsumed_stock .iter() - .map(|(g, a)| (g, *a)) + .map(|(g, a)| (g.into(), *a)) .collect(), }; s.apply_supplement(dynamic_rng, wpos2d, get_column, supplement, economy) diff --git a/world/src/util/map_array.rs b/world/src/util/map_array.rs new file mode 100644 index 0000000000..84f6fde108 --- /dev/null +++ b/world/src/util/map_array.rs @@ -0,0 +1,110 @@ +use std::cmp::PartialEq; + +pub trait GenericIndex { + const LENGTH: usize = N; + const VALUES: [V; N]; + + fn from_usize(n: usize) -> Self; + fn into_usize(self) -> usize; +} + +#[derive(Debug)] +pub struct NotFound(); + +pub fn index_from_enum, const N: usize>( + val: E, +) -> Result { + I::VALUES + .iter() + .position(|v| val == *v) + .ok_or(NotFound {}) + .map(I::from_usize) +} + +pub fn enum_from_index, const N: usize>(idx: I) -> E { + I::VALUES[idx.into_usize()].clone() +} + +#[cfg(test)] +mod tests { + use crate::util::map_array::{enum_from_index, index_from_enum, GenericIndex, NotFound}; + use std::{ + convert::{TryFrom, TryInto}, + ops::{Index, IndexMut}, + }; + + // the Values we want to generate an Index for + #[derive(Debug, Clone, PartialEq, Eq)] + enum MyEnum0 { + A, + B, + } + + #[derive(Debug, Clone, PartialEq, Eq)] + enum MyEnum { + C(MyEnum0), + D, + } + + // the opaque index type into the "map" + struct MyIndex(usize); + + impl GenericIndex for MyIndex { + const VALUES: [MyEnum; MyIndex::LENGTH] = + [MyEnum::C(MyEnum0::B), MyEnum::C(MyEnum0::A), MyEnum::D]; + + fn from_usize(n: usize) -> Self { Self(n) } + + fn into_usize(self) -> usize { self.0 } + } + + impl TryFrom for MyIndex { + type Error = NotFound; + + fn try_from(e: MyEnum) -> Result { index_from_enum(e) } + } + + impl From for MyEnum { + fn from(idx: MyIndex) -> MyEnum { enum_from_index(idx) } + } + + // the "map" itself + struct MyMap([V; MyIndex::LENGTH]); + + impl Default for MyMap { + fn default() -> Self { MyMap([V::default(); MyIndex::LENGTH]) } + } + + impl Index for MyMap { + type Output = V; + + fn index(&self, index: MyIndex) -> &Self::Output { &self.0[index.0] } + } + + impl IndexMut for MyMap { + fn index_mut(&mut self, index: MyIndex) -> &mut Self::Output { &mut self.0[index.0] } + } + + impl MyMap { + pub fn iter(&self) -> impl Iterator + '_ { + (&self.0).iter().enumerate().map(|(i, v)| (MyIndex(i), v)) + } + } + + // test: create a map, set some values and output it + // Output: m[C(B)]=19 m[C(A)]=42 m[D]=0 + #[test] + fn test_map_array() { + let mut m = MyMap::default(); + if let Ok(i) = MyEnum::C(MyEnum0::A).try_into() { + m[i] = 42.0; + } + if let Ok(i) = MyEnum::C(MyEnum0::B).try_into() { + m[i] = 19.0; + } + for (k, v) in m.iter() { + let k2: MyEnum = k.into(); + println!("m[{:?}]={}", k2, *v); + } + } +} diff --git a/world/src/util/mod.rs b/world/src/util/mod.rs index 0a22405bcd..27cc30c080 100644 --- a/world/src/util/mod.rs +++ b/world/src/util/mod.rs @@ -1,4 +1,5 @@ pub mod fast_noise; +pub mod map_array; pub mod map_vec; pub mod math; pub mod random; From 667e667ac8d01012e84b6de1d0c65e9b1db20884 Mon Sep 17 00:00:00 2001 From: Justin Shipsey Date: Sun, 20 Jun 2021 19:42:21 +0000 Subject: [PATCH 136/180] Update difficulty_distribution.ron --- assets/world/dungeon/difficulty_distribution.ron | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/assets/world/dungeon/difficulty_distribution.ron b/assets/world/dungeon/difficulty_distribution.ron index 1460bfc6e6..9fb85a61ef 100644 --- a/assets/world/dungeon/difficulty_distribution.ron +++ b/assets/world/dungeon/difficulty_distribution.ron @@ -16,10 +16,10 @@ /// 1) Set every probability to 0.0 and left one with any other number /// and you will have map full of dungeons of same level ([ - (0, 5.0), - (1, 4.0), - (2, 4.0), - (3, 2.0), - (4, 2.0), - (5, 1.0), + (0, 0.3), + (1, 0.2), + (2, 0.05), + (3, 0.05), + (4, 0.1), + (5, 0.3), ]) From 80c45b63964ff84c311df62c4fa1a29062438a50 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sun, 20 Jun 2021 22:55:58 +0100 Subject: [PATCH 137/180] Added another .replace --- common/src/cmd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/cmd.rs b/common/src/cmd.rs index 54c00c9af4..0a1b91eba6 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -239,7 +239,7 @@ lazy_static! { if path.is_dir(){ list_items(&path, &base, &mut items)?; } else if let Ok(path) = path.strip_prefix(base) { - let path = path.to_string_lossy().trim_end_matches(".ron").replace('/', "."); + let path = path.to_string_lossy().trim_end_matches(".ron").replace('/', ".").replace('\\', "."); items.push(path); } } From 288f2c13d416a53a8f36d88d9cf4d12db4630889 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Mon, 21 Jun 2021 13:27:19 +0100 Subject: [PATCH 138/180] Enable strafing in first-person mode --- common/src/comp/character_state.rs | 2 ++ common/src/comp/controller.rs | 4 ++++ common/src/states/utils.rs | 5 ++++- voxygen/anim/src/character/stand.rs | 19 +++++++++++++++++-- voxygen/src/scene/camera.rs | 6 +++--- voxygen/src/scene/figure/mod.rs | 11 ++++++++++- voxygen/src/session/mod.rs | 4 ++++ 7 files changed, 44 insertions(+), 7 deletions(-) diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index b21d90008f..86db71191b 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -19,6 +19,7 @@ pub struct StateUpdate { pub density: Density, pub energy: Energy, pub swap_equipped_weapons: bool, + pub should_strafe: bool, pub queued_inputs: BTreeMap, pub removed_inputs: Vec, pub local_events: VecDeque, @@ -34,6 +35,7 @@ impl From<&JoinData<'_>> for StateUpdate { density: *data.density, energy: *data.energy, swap_equipped_weapons: false, + should_strafe: data.inputs.strafing, character: data.character.clone(), queued_inputs: BTreeMap::new(), removed_inputs: Vec::new(), diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index 63c3664f8d..2979de12a5 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -199,6 +199,10 @@ pub struct ControllerInputs { * limits) */ pub look_dir: Dir, pub select_pos: Option>, + /// Attempt to enable strafing. + /// Currently, setting this to false will *not* disable strafing during a + /// wielding character state. + pub strafing: bool, } #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index eefa5e1a15..876d049618 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -307,7 +307,10 @@ pub fn handle_forced_movement(data: &JoinData, update: &mut StateUpdate, movemen } pub fn handle_orientation(data: &JoinData, update: &mut StateUpdate, efficiency: f32) { - let strafe_aim = update.character.is_aimed() && data.body.can_strafe(); + // TODO: Don't always check `character.is_aimed()`, allow the frontend to + // control whether the player strafes during an aimed `CharacterState`. + let strafe_aim = + (update.character.is_aimed() || update.should_strafe) && data.body.can_strafe(); if let Some(dir) = (strafe_aim || update.character.is_attack()) .then(|| data.inputs.look_dir.to_horizontal().unwrap_or_default()) .or_else(|| Dir::from_unnormalized(data.inputs.move_dir.into())) diff --git a/voxygen/anim/src/character/stand.rs b/voxygen/anim/src/character/stand.rs index f1829e666e..50ddea63a7 100644 --- a/voxygen/anim/src/character/stand.rs +++ b/voxygen/anim/src/character/stand.rs @@ -13,6 +13,8 @@ impl Animation for StandAnimation { Option, Option, (Option, Option), + Vec3, + Vec3, f32, Vec3, ); @@ -24,7 +26,7 @@ impl Animation for StandAnimation { #[cfg_attr(feature = "be-dyn-lib", export_name = "character_stand")] fn update_skeleton_inner<'a>( skeleton: &Self::Skeleton, - (active_tool_kind, second_tool_kind, hands, global_time, avg_vel): Self::Dependency<'a>, + (active_tool_kind, second_tool_kind, hands, orientation, last_ori, global_time, avg_vel): Self::Dependency<'a>, anim_time: f32, _rate: &mut f32, s_a: &SkeletonAttr, @@ -33,6 +35,19 @@ impl Animation for StandAnimation { let slow = (anim_time * 1.0).sin(); let impact = (avg_vel.z).max(-15.0); + let ori: Vec2 = Vec2::from(orientation); + let last_ori = Vec2::from(last_ori); + let tilt = if ::vek::Vec2::new(ori, last_ori) + .map(|o| o.magnitude_squared()) + .map(|m| m > 0.001 && m.is_finite()) + .reduce_and() + && ori.angle_between(last_ori).is_finite() + { + ori.angle_between(last_ori).min(0.2) + * last_ori.determine_side(Vec2::zero(), ori).signum() + } else { + 0.0 + } * 1.3; let head_look = Vec2::new( ((global_time + anim_time) / 10.0).floor().mul(7331.0).sin() * 0.15, ((global_time + anim_time) / 10.0).floor().mul(1337.0).sin() * 0.07, @@ -159,7 +174,7 @@ impl Animation for StandAnimation { next.lantern.position = Vec3::new(-0.5, -0.5, -2.5); next.lantern.orientation = next.hand_r.orientation.inverse() * Quaternion::rotation_x(fast * 0.1) - * Quaternion::rotation_y(fast2 * 0.1); + * Quaternion::rotation_y(fast2 * 0.1 + tilt * 3.0); } next.torso.position = Vec3::new(0.0, 0.0, 0.0) * s_a.scaler; diff --git a/voxygen/src/scene/camera.rs b/voxygen/src/scene/camera.rs index d4aa8aa752..807b9ff0fa 100644 --- a/voxygen/src/scene/camera.rs +++ b/voxygen/src/scene/camera.rs @@ -6,7 +6,7 @@ use treeculler::Frustum; use vek::*; pub const NEAR_PLANE: f32 = 0.0625; -pub const FAR_PLANE: f32 = 524288.06; // excessive precision: 524288.0625 +pub const FAR_PLANE: f32 = 524288.06; // excessive precision: 524288.0625 const FIRST_PERSON_INTERP_TIME: f32 = 0.1; const THIRD_PERSON_INTERP_TIME: f32 = 0.1; @@ -415,8 +415,8 @@ impl Camera { self.tgt_ori.x = (self.tgt_ori.x + delta.x).rem_euclid(2.0 * PI); // Clamp camera pitch to the vertical limits self.tgt_ori.y = (self.tgt_ori.y + delta.y) - .min(PI / 2.0 - 0.0001) - .max(-PI / 2.0 + 0.0001); + .min(PI / 2.0 - 0.001) + .max(-PI / 2.0 + 0.001); // Wrap camera roll self.tgt_ori.z = (self.tgt_ori.z + delta.z).rem_euclid(2.0 * PI); } diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index c43d262185..5174bb0330 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -807,7 +807,16 @@ impl FigureMgr { // Standing (true, false, false) => anim::character::StandAnimation::update_skeleton( &CharacterSkeleton::new(holding_lantern), - (active_tool_kind, second_tool_kind, hands, time, rel_avg_vel), + ( + active_tool_kind, + second_tool_kind, + hands, + // TODO: Update to use the quaternion. + ori * anim::vek::Vec3::::unit_y(), + state.last_ori * anim::vek::Vec3::::unit_y(), + time, + rel_avg_vel, + ), state.state_time, &mut state_animation_rate, skeleton_attr, diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 2e7464f658..5792cb46e4 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -846,6 +846,10 @@ impl PlayState for SessionState { Dir::from_unnormalized(cam_dir + aim_dir_offset).unwrap(); } } + self.inputs.strafing = matches!( + self.scene.camera().get_mode(), + camera::CameraMode::FirstPerson + ); // Get the current state of movement related inputs let input_vec = self.key_state.dir_vec(); From 8ab222bf80c94a2fdc42b2ca67f137bbed4ef61b Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Mon, 21 Jun 2021 17:06:43 +0100 Subject: [PATCH 139/180] Selectively disable glow flicker for small wins on low-end systems --- assets/voxygen/shaders/include/light.glsl | 2 +- assets/voxygen/shaders/include/sky.glsl | 25 +++++++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/assets/voxygen/shaders/include/light.glsl b/assets/voxygen/shaders/include/light.glsl index 2567109c10..63001b605c 100644 --- a/assets/voxygen/shaders/include/light.glsl +++ b/assets/voxygen/shaders/include/light.glsl @@ -173,7 +173,7 @@ float lights_at(vec3 wpos, vec3 wnorm, vec3 /*cam_to_frag*/view_dir, vec3 mu, ve float light_distance = sqrt(distance_2); vec3 light_dir = -difference / light_distance; // normalize(-difference); // light_dir = faceforward(light_dir, wnorm, light_dir); - bool is_direct = dot(-light_dir, wnorm) > 0.0; + bool is_direct = dot(difference, wnorm) > 0.0; // reflected_light += color * (distance_2 == 0.0 ? vec3(1.0) : light_reflection_factor(wnorm, cam_to_frag, light_dir, k_d, k_s, alpha)); vec3 direct_light_dir = is_direct ? light_dir : -light_dir; // vec3 direct_norm_dir = is_direct ? wnorm : -wnorm; diff --git a/assets/voxygen/shaders/include/sky.glsl b/assets/voxygen/shaders/include/sky.glsl index f6587037ab..e358ea02da 100644 --- a/assets/voxygen/shaders/include/sky.glsl +++ b/assets/voxygen/shaders/include/sky.glsl @@ -54,7 +54,11 @@ const vec3 GLOW_COLOR = vec3(0.89, 0.95, 0.52); // Calculate glow from static light sources, + some noise for flickering. // TODO: Optionally disable the flickering for performance? vec3 glow_light(vec3 pos) { - return GLOW_COLOR * (1.0 + (noise_3d(vec3(pos.xy * 0.005, tick.x * 0.5)) - 0.5) * 1.0); + #if (SHADOW_MODE <= SHADOW_MODE_NONE) + return GLOW_COLOR; + #else + return GLOW_COLOR * (1.0 + (noise_3d(vec3(pos.xy * 0.005, tick.x * 0.5)) - 0.5) * 1.0); + #endif } //vec3 get_sun_dir(float time_of_day) { @@ -420,7 +424,7 @@ vec3 get_sky_light(vec3 dir, float time_of_day, bool with_stars) { // Add white dots for stars. Note these flicker and jump due to FXAA float star = 0.0; if (with_stars) { - vec3 star_dir = normalize(sun_dir.xyz * dir.z + cross(sun_dir.xyz, vec3(0, 1, 0)) * dir.x + vec3(0, 1, 0) * dir.y); + vec3 star_dir = sun_dir.xyz * dir.z + cross(sun_dir.xyz, vec3(0, 1, 0)) * dir.x + vec3(0, 1, 0) * dir.y; star = is_star_at(star_dir); } @@ -485,7 +489,13 @@ vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float q pow(max(-sun_dir.z, 0.0), 0.5) ); - vec3 sun_halo = sun_halo_color * 25 * pow(max(dot(dir, -sun_dir), 0), 20.0); + float sun_halo_power = 20.0; + #if (CLOUD_MODE == CLOUD_MODE_NONE) + sun_halo_power = 1000.0; + sun_halo_color *= 0.1; + #endif + + vec3 sun_halo = sun_halo_color * 25 * pow(max(dot(dir, -sun_dir), 0), sun_halo_power); vec3 sun_surf = vec3(0); if (with_features) { float angle = 0.00035; @@ -498,7 +508,14 @@ vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float q const vec3 MOON_HALO_COLOR = vec3(0.015, 0.015, 0.05) * 250; vec3 moon_halo_color = MOON_HALO_COLOR; - vec3 moon_halo = moon_halo_color * pow(max(dot(dir, -moon_dir), 0), 100.0); + + float moon_halo_power = 20.0; + #if (CLOUD_MODE == CLOUD_MODE_NONE) + moon_halo_power = 2500.0; + moon_halo_color *= 0.1; + #endif + + vec3 moon_halo = moon_halo_color * pow(max(dot(dir, -moon_dir), 0), moon_halo_power); vec3 moon_surf = vec3(0); if (with_features) { float angle = 0.00035; From b84708ca7561b9ec7ebccc3815b023c334d5cb1a Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Mon, 21 Jun 2021 20:16:03 +0100 Subject: [PATCH 140/180] Added chat toggle --- CHANGELOG.md | 1 + assets/voxygen/i18n/en/hud/hud_settings.ron | 1 + voxygen/src/hud/mod.rs | 76 ++++++++++---------- voxygen/src/hud/settings_window/interface.rs | 28 +++++++- voxygen/src/session/settings_change.rs | 4 ++ voxygen/src/settings/interface.rs | 2 + 6 files changed, 74 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee737abab2..b0874a123a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added debug line info to release builds, enhancing the usefulness of panic backtraces - NPCs and animals can now make sounds in response to certain events - Players can press H to greet others +- Ability to toggle chat visibility ### Changed - Entity-entity pushback is no longer applied in forced movement states like rolling and leaping. diff --git a/assets/voxygen/i18n/en/hud/hud_settings.ron b/assets/voxygen/i18n/en/hud/hud_settings.ron index 24023e2ba3..1292fb06c7 100644 --- a/assets/voxygen/i18n/en/hud/hud_settings.ron +++ b/assets/voxygen/i18n/en/hud/hud_settings.ron @@ -11,6 +11,7 @@ "hud.settings.help_window": "Help Window", "hud.settings.debug_info": "Debug Info", "hud.settings.show_hitboxes": "Show hitboxes", + "hud.settings.show_chat": "Show chat", "hud.settings.tips_on_startup": "Tips-On-Startup", "hud.settings.ui_scale": "UI-Scale", "hud.settings.relative_scaling": "Relative Scaling", diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index d409045156..fa478cb929 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -2706,43 +2706,45 @@ impl Hud { .retain(|m| !matches!(m.chat_type, comp::ChatType::Npc(_, _))); // Chat box - for event in Chat::new( - &mut self.new_messages, - &client, - global_state, - self.pulse, - &self.imgs, - &self.fonts, - i18n, - ) - .and_then(self.force_chat_input.take(), |c, input| c.input(input)) - .and_then(self.tab_complete.take(), |c, input| { - c.prepare_tab_completion(input) - }) - .and_then(self.force_chat_cursor.take(), |c, pos| c.cursor_pos(pos)) - .set(self.ids.chat, ui_widgets) - { - match event { - chat::Event::TabCompletionStart(input) => { - self.tab_complete = Some(input); - }, - chat::Event::SendMessage(message) => { - events.push(Event::SendMessage(message)); - }, - chat::Event::SendCommand(name, args) => { - events.push(Event::SendCommand(name, args)); - }, - chat::Event::Focus(focus_id) => { - self.to_focus = Some(Some(focus_id)); - }, - chat::Event::ChangeChatTab(tab) => { - events.push(Event::SettingsChange(ChatChange::ChangeChatTab(tab).into())); - }, - chat::Event::ShowChatTabSettings(tab) => { - self.show.chat_tab_settings_index = Some(tab); - self.show.settings_tab = SettingsTab::Chat; - self.show.settings(true); - }, + if global_state.settings.interface.toggle_chat { + for event in Chat::new( + &mut self.new_messages, + &client, + global_state, + self.pulse, + &self.imgs, + &self.fonts, + i18n, + ) + .and_then(self.force_chat_input.take(), |c, input| c.input(input)) + .and_then(self.tab_complete.take(), |c, input| { + c.prepare_tab_completion(input) + }) + .and_then(self.force_chat_cursor.take(), |c, pos| c.cursor_pos(pos)) + .set(self.ids.chat, ui_widgets) + { + match event { + chat::Event::TabCompletionStart(input) => { + self.tab_complete = Some(input); + }, + chat::Event::SendMessage(message) => { + events.push(Event::SendMessage(message)); + }, + chat::Event::SendCommand(name, args) => { + events.push(Event::SendCommand(name, args)); + }, + chat::Event::Focus(focus_id) => { + self.to_focus = Some(Some(focus_id)); + }, + chat::Event::ChangeChatTab(tab) => { + events.push(Event::SettingsChange(ChatChange::ChangeChatTab(tab).into())); + }, + chat::Event::ShowChatTabSettings(tab) => { + self.show.chat_tab_settings_index = Some(tab); + self.show.settings_tab = SettingsTab::Chat; + self.show.settings(true); + }, + } } } diff --git a/voxygen/src/hud/settings_window/interface.rs b/voxygen/src/hud/settings_window/interface.rs index 30478a2e20..11dd1ff536 100644 --- a/voxygen/src/hud/settings_window/interface.rs +++ b/voxygen/src/hud/settings_window/interface.rs @@ -38,6 +38,8 @@ widget_ids! { debug_button_label, hitboxes_button, hitboxes_button_label, + chat_button, + chat_button_label, ch_title, ch_transp_slider, ch_transp_value, @@ -266,9 +268,33 @@ impl<'a> Widget for Interface<'a> { .color(TEXT_COLOR) .set(state.ids.hitboxes_button_label, ui); + // Chat + let show_chat = ToggleButton::new( + self.global_state.settings.interface.toggle_chat, + self.imgs.checkbox, + self.imgs.checkbox_checked, + ) + .w_h(18.0, 18.0) + .down_from(state.ids.hitboxes_button, 8.0) + .hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo) + .press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked) + .set(state.ids.chat_button, ui); + + if self.global_state.settings.interface.toggle_chat != show_chat { + events.push(ToggleChat(show_chat)); + } + + Text::new(&self.localized_strings.get("hud.settings.show_chat")) + .right_from(state.ids.chat_button, 10.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .graphics_for(state.ids.chat_button) + .color(TEXT_COLOR) + .set(state.ids.chat_button_label, ui); + // Ui Scale Text::new(&self.localized_strings.get("hud.settings.ui_scale")) - .down_from(state.ids.hitboxes_button, 20.0) + .down_from(state.ids.chat_button, 20.0) .font_size(self.fonts.cyri.scale(18)) .font_id(self.fonts.cyri.conrod_id) .color(TEXT_COLOR) diff --git a/voxygen/src/session/settings_change.rs b/voxygen/src/session/settings_change.rs index cbb4fdaa64..0b62766580 100644 --- a/voxygen/src/session/settings_change.rs +++ b/voxygen/src/session/settings_change.rs @@ -97,6 +97,7 @@ pub enum Interface { ToggleHelp(bool), ToggleDebug(bool), ToggleHitboxes(bool), + ToggleChat(bool), ToggleTips(bool), CrosshairTransp(f32), @@ -450,6 +451,9 @@ impl SettingsChange { Interface::ToggleHitboxes(toggle_hitboxes) => { settings.interface.toggle_hitboxes = toggle_hitboxes; }, + Interface::ToggleChat(toggle_chat) => { + settings.interface.toggle_chat = toggle_chat; + }, Interface::ToggleTips(loading_tips) => { settings.interface.loading_tips = loading_tips; }, diff --git a/voxygen/src/settings/interface.rs b/voxygen/src/settings/interface.rs index 0e632d4765..b85caac831 100644 --- a/voxygen/src/settings/interface.rs +++ b/voxygen/src/settings/interface.rs @@ -11,6 +11,7 @@ use vek::*; pub struct InterfaceSettings { pub toggle_debug: bool, pub toggle_hitboxes: bool, + pub toggle_chat: bool, pub sct: bool, pub sct_player_batch: bool, pub sct_damage_batch: bool, @@ -46,6 +47,7 @@ impl Default for InterfaceSettings { Self { toggle_debug: false, toggle_hitboxes: false, + toggle_chat: true, sct: true, sct_player_batch: false, sct_damage_batch: false, From 90e5bac395287b3fa6b18598758a19c4a9df6166 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Mon, 21 Jun 2021 20:32:20 +0100 Subject: [PATCH 141/180] Made movement slower when strafing in non-forward directions --- common/src/states/utils.rs | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 876d049618..99236a42fa 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -216,6 +216,10 @@ impl Body { } pub fn can_climb(&self) -> bool { matches!(self, Body::Humanoid(_)) } + + /// Returns how well a body can move backwards while strafing (0.0 = not at + /// all, 1.0 = same as forward) + pub fn reverse_move_factor(&self) -> f32 { 0.5 } } /// Handles updating `Components` to move player based on state of `JoinData` @@ -257,6 +261,19 @@ fn basic_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) { * accel * if data.body.can_strafe() { data.inputs.move_dir + * if is_strafing(data, update) { + Lerp::lerp( + Vec2::from(update.ori) + .try_normalized() + .unwrap_or_else(Vec2::zero) + .dot(data.inputs.move_dir) + .max(0.0), + 1.0, + data.body.reverse_move_factor(), + ) + } else { + 1.0 + } } else { let fw = Vec2::from(update.ori); fw * data.inputs.move_dir.dot(fw).max(0.0) @@ -307,11 +324,7 @@ pub fn handle_forced_movement(data: &JoinData, update: &mut StateUpdate, movemen } pub fn handle_orientation(data: &JoinData, update: &mut StateUpdate, efficiency: f32) { - // TODO: Don't always check `character.is_aimed()`, allow the frontend to - // control whether the player strafes during an aimed `CharacterState`. - let strafe_aim = - (update.character.is_aimed() || update.should_strafe) && data.body.can_strafe(); - if let Some(dir) = (strafe_aim || update.character.is_attack()) + if let Some(dir) = (is_strafing(data, update) || update.character.is_attack()) .then(|| data.inputs.look_dir.to_horizontal().unwrap_or_default()) .or_else(|| Dir::from_unnormalized(data.inputs.move_dir.into())) { @@ -712,6 +725,12 @@ pub fn handle_dodge_input(data: &JoinData, update: &mut StateUpdate) { } } +pub fn is_strafing(data: &JoinData, update: &StateUpdate) -> bool { + // TODO: Don't always check `character.is_aimed()`, allow the frontend to + // control whether the player strafes during an aimed `CharacterState`. + (update.character.is_aimed() || update.should_strafe) && data.body.can_strafe() +} + pub fn unwrap_tool_data<'a>(data: &'a JoinData, equip_slot: EquipSlot) -> Option<&'a Tool> { if let Some(ItemKind::Tool(tool)) = data.inventory.equipped(equip_slot).map(|i| i.kind()) { Some(&tool) From e930ff6779ae0b385b40077bebbb98b6b9ff24b7 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Mon, 21 Jun 2021 21:13:49 +0100 Subject: [PATCH 142/180] Sanitise controller inputs --- CHANGELOG.md | 5 +++++ common/src/comp/controller.rs | 14 ++++++++++++++ common/systems/src/controller.rs | 12 ++---------- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0874a123a..9bb77bcb69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added + - Added a skill tree for mining, which gains xp from mining ores and gems. - Added debug line info to release builds, enhancing the usefulness of panic backtraces - NPCs and animals can now make sounds in response to certain events @@ -15,15 +16,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Ability to toggle chat visibility ### Changed + - Entity-entity pushback is no longer applied in forced movement states like rolling and leaping. - Updated audio library (rodio 0.13 -> 0.14). - Improve entity-terrain physics performance by reducing the number of voxel lookups. - Clay Golem uses shockwave only after specific fraction of health and other difficulty adjustments. +- Made strafing slightly slower ### Removed + - Enemies no more spawn in dungeon boss room ### Fixed + - Crafting Stations aren't exploadable anymore - Cases where no audio output could be produced before. - Significantly improved the performance of playing sound effects diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index 2979de12a5..7d4a1a7448 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -215,6 +215,20 @@ pub struct Controller { } impl ControllerInputs { + /// Sanitize inputs to avoid clients sending bad data. + pub fn sanitize(&mut self) { + self.move_dir = if self.move_dir.map(|e| e.is_finite()).reduce_and() { + self.move_dir / self.move_dir.magnitude().max(1.0) + } else { + Vec2::zero() + }; + self.move_z = if self.move_z.is_finite() { + self.move_z.clamped(-1.0, 1.0) + } else { + 0.0 + }; + } + /// Updates Controller inputs with new version received from the client pub fn update_with_new(&mut self, new: Self) { self.climb = new.climb; diff --git a/common/systems/src/controller.rs b/common/systems/src/controller.rs index 2aea04b55a..7cc1e67e23 100644 --- a/common/systems/src/controller.rs +++ b/common/systems/src/controller.rs @@ -37,16 +37,8 @@ impl<'a> System<'a> for Sys { let mut server_emitter = read_data.server_bus.emitter(); for (entity, controller) in (&read_data.entities, &mut controllers).join() { - let mut inputs = &mut controller.inputs; - - // Update `inputs.move_dir`. - inputs.move_dir = if inputs.move_dir.magnitude_squared() > 1.0 { - // Cap move_dir to 1 - inputs.move_dir.normalized() - } else { - inputs.move_dir - }; - inputs.move_z = inputs.move_z.clamped(-1.0, 1.0); + // Sanitize inputs to avoid clients sending bad data + controller.inputs.sanitize(); // Process other controller events for event in controller.events.drain(..) { From ccf7c7a0e48ee2beb1eacd26cb3ef5182edef7ce Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Tue, 22 Jun 2021 20:36:30 +0300 Subject: [PATCH 143/180] Balance food to have sane health per second regen * Gatherable food gives you slightly variable regen with variable time based on tier and that fact that we have x2 health on endgame character. For example while mushroom gives you 5 health over 10 seconds (0.5 health-per-second), mushroom curry will give you 120 health over 80 seconds (1.5 health-per-second). * Meat gives you twice powerful regen strength, but with twice shorter duration --- assets/common/items/food/apple.ron | 4 ++-- assets/common/items/food/apple_mushroom_curry.ron | 4 ++-- assets/common/items/food/apple_stick.ron | 4 ++-- assets/common/items/food/cactus_colada.ron | 4 ++-- assets/common/items/food/cheese.ron | 4 ++-- assets/common/items/food/coconut.ron | 4 ++-- assets/common/items/food/meat.ron | 4 ++-- assets/common/items/food/meat/beast_small_cooked.ron | 4 ++-- assets/common/items/food/meat/bird_large_cooked.ron | 4 ++-- assets/common/items/food/mushroom_stick.ron | 4 ++-- assets/common/items/food/plainsalad.ron | 4 ++-- assets/common/items/food/sunflower_icetea.ron | 4 ++-- assets/common/items/food/tomatosalad.ron | 4 ++-- 13 files changed, 26 insertions(+), 26 deletions(-) diff --git a/assets/common/items/food/apple.ron b/assets/common/items/food/apple.ron index aa6a35341e..4648ace03b 100644 --- a/assets/common/items/food/apple.ron +++ b/assets/common/items/food/apple.ron @@ -7,9 +7,9 @@ ItemDef( Buff(( kind: Saturation, data: ( - strength: 10.0, + strength: 5.0, duration: Some(( - secs: 10, + secs: 20, nanos: 0, )), ), diff --git a/assets/common/items/food/apple_mushroom_curry.ron b/assets/common/items/food/apple_mushroom_curry.ron index 372b0f5450..62d591d08b 100644 --- a/assets/common/items/food/apple_mushroom_curry.ron +++ b/assets/common/items/food/apple_mushroom_curry.ron @@ -7,9 +7,9 @@ ItemDef( Buff(( kind: Saturation, data: ( - strength: 120.0, + strength: 15.0, duration: Some(( - secs: 10, + secs: 80, nanos: 0, )), ), diff --git a/assets/common/items/food/apple_stick.ron b/assets/common/items/food/apple_stick.ron index 010f151623..185f75a2bb 100644 --- a/assets/common/items/food/apple_stick.ron +++ b/assets/common/items/food/apple_stick.ron @@ -7,9 +7,9 @@ ItemDef( Buff(( kind: Saturation, data: ( - strength: 25.0, + strength: 10.0, duration: Some(( - secs: 10, + secs: 25, nanos: 0, )), ), diff --git a/assets/common/items/food/cactus_colada.ron b/assets/common/items/food/cactus_colada.ron index cef46eee0d..421c140c23 100644 --- a/assets/common/items/food/cactus_colada.ron +++ b/assets/common/items/food/cactus_colada.ron @@ -7,9 +7,9 @@ ItemDef( Buff(( kind: Saturation, data: ( - strength: 25.0, + strength: 10.0, duration: Some(( - secs: 15, + secs: 25, nanos: 0, )), ), diff --git a/assets/common/items/food/cheese.ron b/assets/common/items/food/cheese.ron index 3a11c642a0..22141cfe96 100644 --- a/assets/common/items/food/cheese.ron +++ b/assets/common/items/food/cheese.ron @@ -7,9 +7,9 @@ ItemDef( Buff(( kind: Saturation, data: ( - strength: 15.0, + strength: 10.0, duration: Some(( - secs: 10, + secs: 15, nanos: 0, )), ), diff --git a/assets/common/items/food/coconut.ron b/assets/common/items/food/coconut.ron index 94732f2cab..d9ee0f05a1 100644 --- a/assets/common/items/food/coconut.ron +++ b/assets/common/items/food/coconut.ron @@ -7,9 +7,9 @@ ItemDef( Buff(( kind: Saturation, data: ( - strength: 20.0, + strength: 10.0, duration: Some(( - secs: 10, + secs: 20, nanos: 0, )), ), diff --git a/assets/common/items/food/meat.ron b/assets/common/items/food/meat.ron index 6f94633577..9ae711fcb3 100644 --- a/assets/common/items/food/meat.ron +++ b/assets/common/items/food/meat.ron @@ -7,9 +7,9 @@ ItemDef( Buff(( kind: Saturation, data: ( - strength: 5.0, + strength: 10.0, duration: Some(( - secs: 10, + secs: 5, nanos: 0, )), ), diff --git a/assets/common/items/food/meat/beast_small_cooked.ron b/assets/common/items/food/meat/beast_small_cooked.ron index 1091b9bcee..e7c5a70809 100644 --- a/assets/common/items/food/meat/beast_small_cooked.ron +++ b/assets/common/items/food/meat/beast_small_cooked.ron @@ -7,9 +7,9 @@ ItemDef( Buff(( kind: Saturation, data: ( - strength: 15.0, + strength: 19.0, duration: Some(( - secs: 10, + secs: 8, nanos: 0, )), ), diff --git a/assets/common/items/food/meat/bird_large_cooked.ron b/assets/common/items/food/meat/bird_large_cooked.ron index a1c94aa038..5664416901 100644 --- a/assets/common/items/food/meat/bird_large_cooked.ron +++ b/assets/common/items/food/meat/bird_large_cooked.ron @@ -7,9 +7,9 @@ ItemDef( Buff(( kind: Saturation, data: ( - strength: 80.0, + strength: 32.0, duration: Some(( - secs: 10, + secs: 25, nanos: 0, )), ), diff --git a/assets/common/items/food/mushroom_stick.ron b/assets/common/items/food/mushroom_stick.ron index e756cdf58b..326eec6540 100644 --- a/assets/common/items/food/mushroom_stick.ron +++ b/assets/common/items/food/mushroom_stick.ron @@ -7,9 +7,9 @@ ItemDef( Buff(( kind: Saturation, data: ( - strength: 20.0, + strength: 10.0, duration: Some(( - secs: 10, + secs: 20, nanos: 0, )), ), diff --git a/assets/common/items/food/plainsalad.ron b/assets/common/items/food/plainsalad.ron index d928b978d6..276648092e 100644 --- a/assets/common/items/food/plainsalad.ron +++ b/assets/common/items/food/plainsalad.ron @@ -7,9 +7,9 @@ ItemDef( Buff(( kind: Saturation, data: ( - strength: 10.0, + strength: 5.0, duration: Some(( - secs: 10, + secs: 20, nanos: 0, )), ), diff --git a/assets/common/items/food/sunflower_icetea.ron b/assets/common/items/food/sunflower_icetea.ron index 5497914d26..aed7842885 100644 --- a/assets/common/items/food/sunflower_icetea.ron +++ b/assets/common/items/food/sunflower_icetea.ron @@ -7,9 +7,9 @@ ItemDef( Buff(( kind: Saturation, data: ( - strength: 50.0, + strength: 10.0, duration: Some(( - secs: 10, + secs: 50, nanos: 0, )), ), diff --git a/assets/common/items/food/tomatosalad.ron b/assets/common/items/food/tomatosalad.ron index 6c6f4d35f3..eb550f9bb3 100644 --- a/assets/common/items/food/tomatosalad.ron +++ b/assets/common/items/food/tomatosalad.ron @@ -7,9 +7,9 @@ ItemDef( Buff(( kind: Saturation, data: ( - strength: 15.0, + strength: 10.0, duration: Some(( - secs: 10, + secs: 15, nanos: 0, )), ), From ced180bbc9a8bee1943456f3da22a711a2a7cc0c Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Tue, 22 Jun 2021 20:44:59 +0300 Subject: [PATCH 144/180] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bb77bcb69..28ecd811af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improve entity-terrain physics performance by reducing the number of voxel lookups. - Clay Golem uses shockwave only after specific fraction of health and other difficulty adjustments. - Made strafing slightly slower +- Food now has limited regeneration strength but longer duration. ### Removed From 81aaf30d00b221f1d1fa66723824231e3f362ef9 Mon Sep 17 00:00:00 2001 From: jshipsey Date: Tue, 22 Jun 2021 22:08:34 -0400 Subject: [PATCH 145/180] stride speed backwardd-dependence --- common/src/states/utils.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 99236a42fa..c8350249eb 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -15,9 +15,11 @@ use crate::{ util::Dir, }; use serde::{Deserialize, Serialize}; -use std::time::Duration; +use std::{ + ops::{Add, Div}, + time::Duration, +}; use vek::*; - pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0; impl Body { @@ -219,7 +221,7 @@ impl Body { /// Returns how well a body can move backwards while strafing (0.0 = not at /// all, 1.0 = same as forward) - pub fn reverse_move_factor(&self) -> f32 { 0.5 } + pub fn reverse_move_factor(&self) -> f32 { 0.45 } } /// Handles updating `Components` to move player based on state of `JoinData` @@ -266,7 +268,14 @@ fn basic_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) { Vec2::from(update.ori) .try_normalized() .unwrap_or_else(Vec2::zero) - .dot(data.inputs.move_dir) + .dot( + data.inputs + .move_dir + .try_normalized() + .unwrap_or_else(Vec2::zero), + ) + .add(1.0) + .div(2.0) .max(0.0), 1.0, data.body.reverse_move_factor(), From 1b3a8db8d4b154ae7eb613f74da95590de9242eb Mon Sep 17 00:00:00 2001 From: Yusuf Bera Ertan Date: Wed, 23 Jun 2021 08:52:04 +0300 Subject: [PATCH 146/180] build(nix): update audio resource patch --- flake.lock | 18 +++++++++--------- nix/nullOggPatch.nix | 19 ++++++++++--------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/flake.lock b/flake.lock index 206ff4bf46..2d3567a0ab 100644 --- a/flake.lock +++ b/flake.lock @@ -24,11 +24,11 @@ "rustOverlay": "rustOverlay" }, "locked": { - "lastModified": 1624129119, - "narHash": "sha256-Mq7SkCA35dEEbWWfO8by94/+sZbvWfv3P8ByxNCVmOg=", + "lastModified": 1624414763, + "narHash": "sha256-+urGJyMUNUZhDJQjIs7/1Q+lMqtgh/dseMViZlss0ug=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "4018999c0998137c34b81ff2ff032d719cd25096", + "rev": "cf46cfce46bcea48fc68abc8199f2615ad12c272", "type": "github" }, "original": { @@ -39,11 +39,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1623862044, - "narHash": "sha256-mY7nldu9Wl/Yb0qMPihZrWw0bRWXNsk6iyYztw/6F6Y=", + "lastModified": 1624353498, + "narHash": "sha256-Edpj+fDbn9p9TSiDDXEUxUk9j12imolnjSzZutXhGG4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "0747387223edf1aa5beaedf48983471315d95e16", + "rev": "b27eaa18b47b77fbbf9f3bd7a17046b2d64b637b", "type": "github" }, "original": { @@ -62,11 +62,11 @@ "rustOverlay": { "flake": false, "locked": { - "lastModified": 1624069337, - "narHash": "sha256-9mTcx7osE4biF2Hm/GU19s1T3+KvphWj4QaUcJh39lU=", + "lastModified": 1624414452, + "narHash": "sha256-vvnagNTPTT3zcPNjD8Gz7STHniQkc3+9cs+4h2pcuVU=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "67dc2a9543a7c24591e6cb102ad0121c3a704aab", + "rev": "09149337f9899b4b9fa6e4f3889467e8646c9fc5", "type": "github" }, "original": { diff --git a/nix/nullOggPatch.nix b/nix/nullOggPatch.nix index 27f22303a4..2362b6f5d9 100644 --- a/nix/nullOggPatch.nix +++ b/nix/nullOggPatch.nix @@ -3,16 +3,17 @@ pkgs.writeText "nullOgg.patch" '' diff --git a/src/audio/soundcache.rs b/src/audio/soundcache.rs - index 8cf703577..678295cac 100644 + index f08a4bdab..c2036336e 100644 --- a/src/audio/soundcache.rs +++ b/src/audio/soundcache.rs - @@ -44,7 +44,7 @@ impl OggSound { - + @@ -38,7 +38,7 @@ impl OggSound { + pub fn empty() -> OggSound { - OggSound(Arc::new( - - include_bytes!("../../../assets/voxygen/audio/null.ogg").to_vec(), - + include_bytes!("${nullOgg}").to_vec(), - )) - } - } + SoundLoader::load( + - Cow::Borrowed(include_bytes!("../../../assets/voxygen/audio/null.ogg")), + + Cow::Borrowed(include_bytes!("${nullOgg}")), + "empty", + ) + .unwrap() '' + From f1b83962bd7a2fb95740afd434b99e539d70b8ed Mon Sep 17 00:00:00 2001 From: AldanTanneo Date: Wed, 23 Jun 2021 21:13:15 +0200 Subject: [PATCH 147/180] Fixed smooth layer at z=434 (and a few other) --- assets/voxygen/shaders/include/random.glsl | 40 ++++++++++------------ 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/assets/voxygen/shaders/include/random.glsl b/assets/voxygen/shaders/include/random.glsl index 2b6347b71c..482e631911 100644 --- a/assets/voxygen/shaders/include/random.glsl +++ b/assets/voxygen/shaders/include/random.glsl @@ -7,27 +7,25 @@ layout(set = 0, binding = 2) uniform sampler s_noise; float hash(vec4 p) { p = fract(p * 0.3183099 + 0.1) - fract(p + 23.22121); p *= 17.0; - return (fract(p.x * p.y * p.z * p.w * (p.x + p.y + p.z + p.w)) - 0.5) * 2.0; + return (fract(p.x * p.y * (1.0 - p.z) * p.w * (p.x + p.y + p.z + p.w)) - 0.5) * 2.0; } -#define M1 1597334677U //1719413*929 -#define M2 3812015801U //140473*2467*11 +#define M1 1597334677U //1719413*929 +#define M2 3812015801U //140473*2467*11 #define M3 2741598923U -float hash_one(uint q) -{ +float hash_one(uint q) { uint n = ((M3 * q) ^ M2) * M1; - return float(n) * (1.0/float(0xffffffffU)); + return float(n) * (1.0 / float(0xffffffffU)); } -float hash_fast(uvec3 q) -{ +float hash_fast(uvec3 q) { q *= uvec3(M1, M2, M3); uint n = (q.x ^ q.y ^ q.z) * M1; - return float(n) * (1.0/float(0xffffffffU)); + return float(n) * (1.0 / float(0xffffffffU)); } // 2D, but using shifted 2D textures @@ -51,12 +49,12 @@ float snoise3(in vec3 x) { //f = f * f * (3.0 - 2.0 * f); return mix( mix( - mix(hash_fast(p + uvec3(0,0,0)), hash_fast(p + uvec3(1,0,0)), f.x), - mix(hash_fast(p + uvec3(0,1,0)), hash_fast(p + uvec3(1,1,0)), f.x), + mix(hash_fast(p + uvec3(0, 0, 0)), hash_fast(p + uvec3(1, 0, 0)), f.x), + mix(hash_fast(p + uvec3(0, 1, 0)), hash_fast(p + uvec3(1, 1, 0)), f.x), f.y), mix( - mix(hash_fast(p + uvec3(0,0,1)), hash_fast(p + uvec3(1,0,1)), f.x), - mix(hash_fast(p + uvec3(0,1,1)), hash_fast(p + uvec3(1,1,1)), f.x), + mix(hash_fast(p + uvec3(0, 0, 1)), hash_fast(p + uvec3(1, 0, 1)), f.x), + mix(hash_fast(p + uvec3(0, 1, 1)), hash_fast(p + uvec3(1, 1, 1)), f.x), f.y), f.z); } @@ -69,22 +67,22 @@ float snoise(in vec4 x) { return mix( mix( mix( - mix(hash(p + vec4(0,0,0,0)), hash(p + vec4(1,0,0,0)), f.x), - mix(hash(p + vec4(0,1,0,0)), hash(p + vec4(1,1,0,0)), f.x), + mix(hash(p + vec4(0, 0, 0, 0)), hash(p + vec4(1, 0, 0, 0)), f.x), + mix(hash(p + vec4(0, 1, 0, 0)), hash(p + vec4(1, 1, 0, 0)), f.x), f.y), mix( - mix(hash(p + vec4(0,0,1,0)), hash(p + vec4(1,0,1,0)), f.x), - mix(hash(p + vec4(0,1,1,0)), hash(p + vec4(1,1,1,0)), f.x), + mix(hash(p + vec4(0, 0, 1, 0)), hash(p + vec4(1, 0, 1, 0)), f.x), + mix(hash(p + vec4(0, 1, 1, 0)), hash(p + vec4(1, 1, 1, 0)), f.x), f.y), f.z), mix( mix( - mix(hash(p + vec4(0,0,0,1)), hash(p + vec4(1,0,0,1)), f.x), - mix(hash(p + vec4(0,1,0,1)), hash(p + vec4(1,1,0,1)), f.x), + mix(hash(p + vec4(0, 0, 0, 1)), hash(p + vec4(1, 0, 0, 1)), f.x), + mix(hash(p + vec4(0, 1, 0, 1)), hash(p + vec4(1, 1, 0, 1)), f.x), f.y), mix( - mix(hash(p + vec4(0,0,1,1)), hash(p + vec4(1,0,1,1)), f.x), - mix(hash(p + vec4(0,1,1,1)), hash(p + vec4(1,1,1,1)), f.x), + mix(hash(p + vec4(0, 0, 1, 1)), hash(p + vec4(1, 0, 1, 1)), f.x), + mix(hash(p + vec4(0, 1, 1, 1)), hash(p + vec4(1, 1, 1, 1)), f.x), f.y), f.z), f.w); From df4f5365231c1c7568d9cde7453943e34d9003d4 Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Tue, 15 Jun 2021 00:45:16 -0400 Subject: [PATCH 148/180] Progress on converting dungeons to site2. Adds Sampling and Translate primitives. --- Cargo.lock | 10 + world/Cargo.toml | 2 +- world/src/civ/mod.rs | 12 +- world/src/lib.rs | 2 +- world/src/site/mod.rs | 17 +- world/src/site2/gen.rs | 18 ++ world/src/site2/mod.rs | 62 +++- world/src/site2/plot.rs | 4 +- .../dungeon/mod.rs => site2/plot/dungeon.rs} | 285 +++++++++++++++++- 9 files changed, 380 insertions(+), 32 deletions(-) rename world/src/{site/dungeon/mod.rs => site2/plot/dungeon.rs} (79%) diff --git a/Cargo.lock b/Cargo.lock index 43527b3c08..275329b5a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2492,6 +2492,15 @@ dependencies = [ "serde", ] +[[package]] +name = "inline_tweak" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7033e97b20277cc0d043226d1940fa7719ff08d2305d1fc7421e53066d00eb4b" +dependencies = [ + "lazy_static", +] + [[package]] name = "inotify" version = "0.7.1" @@ -6154,6 +6163,7 @@ dependencies = [ "fxhash", "hashbrown 0.11.2", "image", + "inline_tweak", "itertools 0.10.0", "lazy_static", "lz-fear", diff --git a/world/Cargo.toml b/world/Cargo.toml index 4beef26bd3..4ba4994243 100644 --- a/world/Cargo.toml +++ b/world/Cargo.toml @@ -35,7 +35,7 @@ rayon = "1.5" serde = { version = "1.0.110", features = ["derive"] } ron = { version = "0.6", default-features = false } assets_manager = {version = "0.4.3", features = ["ron"]} -#inline_tweak = "1.0.2" +inline_tweak = "1.0.2" # compression benchmarks lz-fear = { version = "0.1.1", optional = true } diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index 077707fd57..fd3ad9df0e 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -5,7 +5,7 @@ mod econ; use crate::{ config::CONFIG, sim::{RiverKind, WorldSim}, - site::{namegen::NameGen, Castle, Dungeon, Settlement, Site as WorldSite, Tree}, + site::{namegen::NameGen, Castle, Settlement, Site as WorldSite, Tree}, site2, util::{attempt, seed_expan, CARDINALS, NEIGHBORS}, Index, Land, @@ -193,13 +193,15 @@ impl Civs { SiteKind::Settlement => { WorldSite::settlement(Settlement::generate(wpos, Some(ctx.sim), &mut rng)) }, - SiteKind::Dungeon => { - WorldSite::dungeon(Dungeon::generate(wpos, Some(ctx.sim), &mut rng)) - }, + SiteKind::Dungeon => WorldSite::dungeon(site2::Site::generate_dungeon( + &Land::from_sim(&ctx.sim), + &mut rng, + wpos, + )), SiteKind::Castle => { WorldSite::castle(Castle::generate(wpos, Some(ctx.sim), &mut rng)) }, - SiteKind::Refactor => WorldSite::refactor(site2::Site::generate( + SiteKind::Refactor => WorldSite::refactor(site2::Site::generate_city( &Land::from_sim(&ctx.sim), &mut rng, wpos, diff --git a/world/src/lib.rs b/world/src/lib.rs index 83f160b68f..70479d03ee 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -144,7 +144,7 @@ impl World { civ::SiteKind::Settlement => world_msg::SiteKind::Town, civ::SiteKind::Dungeon => world_msg::SiteKind::Dungeon { difficulty: match site.site_tmp.map(|id| &index.sites[id].kind) { - Some(site::SiteKind::Dungeon(d)) => d.difficulty(), + Some(site::SiteKind::Dungeon(d)) => d.difficulty().unwrap_or(0), _ => 0, }, }, diff --git a/world/src/site/mod.rs b/world/src/site/mod.rs index 90c6635ae7..64d6961b88 100644 --- a/world/src/site/mod.rs +++ b/world/src/site/mod.rs @@ -1,6 +1,5 @@ mod block_mask; mod castle; -mod dungeon; pub mod economy; pub mod namegen; mod settlement; @@ -8,7 +7,7 @@ mod tree; // Reexports pub use self::{ - block_mask::BlockMask, castle::Castle, dungeon::Dungeon, economy::Economy, + block_mask::BlockMask, castle::Castle, economy::Economy, settlement::Settlement, tree::Tree, }; @@ -21,7 +20,7 @@ use vek::*; #[derive(Deserialize)] pub struct Colors { pub castle: castle::Colors, - pub dungeon: dungeon::Colors, + pub dungeon: site2::plot::dungeon::Colors, pub settlement: settlement::Colors, } @@ -40,7 +39,7 @@ pub struct Site { pub enum SiteKind { Settlement(Settlement), - Dungeon(Dungeon), + Dungeon(site2::Site), Castle(Castle), Refactor(site2::Site), Tree(tree::Tree), @@ -54,7 +53,7 @@ impl Site { } } - pub fn dungeon(d: Dungeon) -> Self { + pub fn dungeon(d: site2::Site) -> Self { Self { kind: SiteKind::Dungeon(d), economy: Economy::default(), @@ -95,7 +94,7 @@ impl Site { pub fn get_origin(&self) -> Vec2 { match &self.kind { SiteKind::Settlement(s) => s.get_origin(), - SiteKind::Dungeon(d) => d.get_origin(), + SiteKind::Dungeon(d) => d.origin, SiteKind::Castle(c) => c.get_origin(), SiteKind::Refactor(s) => s.origin, SiteKind::Tree(t) => t.origin, @@ -117,7 +116,7 @@ impl Site { SiteKind::Settlement(s) => s.name(), SiteKind::Dungeon(d) => d.name(), SiteKind::Castle(c) => c.name(), - SiteKind::Refactor(_) => "Town", + SiteKind::Refactor(s) => s.name(), SiteKind::Tree(_) => "Giant Tree", } } @@ -127,7 +126,7 @@ impl Site { let get_col = |wpos| info.col(wpos + info.wpos); match &self.kind { SiteKind::Settlement(s) => s.apply_to(canvas.index, canvas.wpos, get_col, canvas.chunk), - SiteKind::Dungeon(d) => d.apply_to(canvas.index, canvas.wpos, get_col, canvas.chunk), + SiteKind::Dungeon(d) => d.render(canvas, dynamic_rng), SiteKind::Castle(c) => c.apply_to(canvas.index, canvas.wpos, get_col, canvas.chunk), SiteKind::Refactor(s) => s.render(canvas, dynamic_rng), SiteKind::Tree(t) => t.render(canvas, dynamic_rng), @@ -156,7 +155,7 @@ impl Site { }; s.apply_supplement(dynamic_rng, wpos2d, get_column, supplement, economy) }, - SiteKind::Dungeon(d) => d.apply_supplement(dynamic_rng, wpos2d, get_column, supplement), + SiteKind::Dungeon(d) => {}, //d.apply_supplement(dynamic_rng, wpos2d, get_column, supplement), SiteKind::Castle(c) => c.apply_supplement(dynamic_rng, wpos2d, get_column, supplement), SiteKind::Refactor(_) => {}, SiteKind::Tree(_) => {}, diff --git a/world/src/site2/gen.rs b/world/src/site2/gen.rs index f958b8732a..3293a7caac 100644 --- a/world/src/site2/gen.rs +++ b/world/src/site2/gen.rs @@ -17,6 +17,7 @@ pub enum Primitive { Cone(Aabb), Sphere(Aabb), Plane(Aabr, Vec3, Vec2), + Sampling(Box) -> bool>), // Combinators And(Id, Id), @@ -26,8 +27,10 @@ pub enum Primitive { Diff(Id, Id), // Operators Rotate(Id, Mat3), + Translate(Id, Vec3), } +#[derive(Debug)] pub enum Fill { Block(Block), Brick(BlockKind, Rgb, u8), @@ -95,6 +98,7 @@ impl Fill { .as_() .dot(*gradient) as i32) }, + Primitive::Sampling(f) => f(pos), Primitive::And(a, b) => { self.contains_at(tree, *a, pos) && self.contains_at(tree, *b, pos) }, @@ -112,6 +116,9 @@ impl Fill { let diff = pos - (aabb.min + mat.cols.map(|x| x.reduce_min())); self.contains_at(tree, *prim, aabb.min + mat.transposed() * diff) }, + Primitive::Translate(prim, vec) => { + self.contains_at(tree, *prim, pos.map2(*vec, i32::saturating_sub)) + } } } @@ -169,6 +176,10 @@ impl Fill { }; aabb.made_valid() }, + Primitive::Sampling(_) => Aabb { + min: Vec3::broadcast(std::i32::MIN), + max: Vec3::broadcast(std::i32::MAX), + }, Primitive::And(a, b) => or_zip_with( self.get_bounds_inner(tree, *a), self.get_bounds_inner(tree, *b), @@ -189,6 +200,13 @@ impl Fill { }; new_aabb.made_valid() }, + Primitive::Translate(prim, vec) => { + let aabb = self.get_bounds_inner(tree, *prim)?; + Aabb { + min: aabb.min.map2(*vec, i32::saturating_add), + max: aabb.max.map2(*vec, i32::saturating_add), + } + }, }) } diff --git a/world/src/site2/mod.rs b/world/src/site2/mod.rs index 6a65ebc154..52cc424fca 100644 --- a/world/src/site2/mod.rs +++ b/world/src/site2/mod.rs @@ -1,5 +1,5 @@ mod gen; -mod plot; +pub mod plot; mod tile; use self::{ @@ -38,6 +38,7 @@ fn reseed(rng: &mut impl Rng) -> impl Rng { ChaChaRng::from_seed(rng.gen::<[u8; #[derive(Default)] pub struct Site { pub(crate) origin: Vec2, + name: String, tiles: TileGrid, plots: Store, plazas: Vec>, @@ -275,7 +276,21 @@ impl Site { }); } - pub fn generate(land: &Land, rng: &mut impl Rng, origin: Vec2) -> Self { + pub fn name(&self) -> &str { + &self.name + } + + pub fn difficulty(&self) -> Option { + self.plots.iter().filter_map(|(_, plot)| { + if let PlotKind::Dungeon(d) = &plot.kind { + Some(d.difficulty()) + } else { + None + } + }).max() + } + + pub fn generate_dungeon(land: &Land, rng: &mut impl Rng, origin: Vec2) -> Self { let mut rng = reseed(rng); let mut site = Site { @@ -283,6 +298,41 @@ impl Site { ..Site::default() }; + site.demarcate_obstacles(land); + let dungeon = plot::Dungeon::generate(origin, land, &mut rng); + site.name = dungeon.name().to_string(); + //let size = (2.0 + rng.gen::().powf(8.0) * 3.0).round() as i32; + let size = 8; + + let aabr = Aabr { + min: Vec2::broadcast(-size), + max: Vec2::broadcast(size), + }; + + let plot = site.create_plot(Plot { + kind: PlotKind::Dungeon(dungeon), + root_tile: aabr.center(), + tiles: aabr_tiles(aabr).collect(), + seed: rng.gen(), + }); + + site.blit_aabr(aabr, Tile { + kind: TileKind::Plaza, + plot: Some(plot), + }); + + site + } + + pub fn generate_city(land: &Land, rng: &mut impl Rng, origin: Vec2) -> Self { + let mut rng = reseed(rng); + + let mut site = Site { + origin, + name: "Town".into(), + ..Site::default() + }; + site.demarcate_obstacles(land); site.make_plaza(land, &mut rng); @@ -702,6 +752,7 @@ impl Site { } } } + tracing::info!("{:?}: {:?}", canvas.wpos(), plots.len()); let mut plots_to_render = plots.into_iter().collect::>(); plots_to_render.sort_unstable(); @@ -710,6 +761,11 @@ impl Site { let (prim_tree, fills) = match &self.plots[plot].kind { PlotKind::House(house) => house.render_collect(self), PlotKind::Castle(castle) => castle.render_collect(self), + PlotKind::Dungeon(dungeon) => { + let (prim_tree, fills) = dungeon.render_collect(self); + tracing::info!("{:?}: {:?} {:?}", dungeon.name(), prim_tree.ids().count(), fills.len()); + (prim_tree, fills) + } _ => continue, }; @@ -732,7 +788,7 @@ impl Site { } } -pub fn test_site() -> Site { Site::generate(&Land::empty(), &mut thread_rng(), Vec2::zero()) } +pub fn test_site() -> Site { Site::generate_city(&Land::empty(), &mut thread_rng(), Vec2::zero()) } fn wpos_is_hazard(land: &Land, wpos: Vec2) -> Option { if land diff --git a/world/src/site2/plot.rs b/world/src/site2/plot.rs index 3b0bcaa1f1..cbc79461e0 100644 --- a/world/src/site2/plot.rs +++ b/world/src/site2/plot.rs @@ -1,7 +1,8 @@ mod castle; +pub mod dungeon; mod house; -pub use self::{castle::Castle, house::House}; +pub use self::{castle::Castle, dungeon::Dungeon, house::House}; use super::*; use crate::util::DHashSet; @@ -30,4 +31,5 @@ pub enum PlotKind { Plaza, Castle(Castle), Road(Path>), + Dungeon(Dungeon), } diff --git a/world/src/site/dungeon/mod.rs b/world/src/site2/plot/dungeon.rs similarity index 79% rename from world/src/site/dungeon/mod.rs rename to world/src/site2/plot/dungeon.rs index bfbfe39a9d..0b31a0cde0 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site2/plot/dungeon.rs @@ -2,10 +2,10 @@ use super::SpawnRules; use crate::{ block::block_from_structure, column::ColumnSample, - sim::WorldSim, site::{namegen::NameGen, BlockMask}, + site2::{self, Primitive, Fill, Structure as SiteStructure}, util::{attempt, Grid, RandomField, Sampler, CARDINALS, DIRS}, - IndexRef, + IndexRef, Land, }; use common::{ @@ -36,7 +36,7 @@ pub struct Dungeon { } pub struct GenCtx<'a, R: Rng> { - sim: Option<&'a WorldSim>, + land: &'a Land<'a>, rng: &'a mut R, } @@ -64,9 +64,8 @@ lazy_static! { } impl Dungeon { - pub fn generate(wpos: Vec2, sim: Option<&WorldSim>, rng: &mut impl Rng) -> Self { - let mut ctx = GenCtx { sim, rng }; - + pub fn generate(wpos: Vec2, land: &Land, rng: &mut impl Rng) -> Self { + let mut ctx = GenCtx { land, rng }; let difficulty = DUNGEON_DISTRIBUTION .choose_weighted(&mut ctx.rng, |pair| pair.1) .map(|(difficulty, _)| *difficulty) @@ -76,7 +75,6 @@ impl Dungeon { err ) }); - let floors = 3 + difficulty / 2; Self { @@ -91,11 +89,7 @@ impl Dungeon { } }, origin: wpos - TILE_SIZE / 2, - alt: ctx - .sim - .and_then(|sim| sim.get_alt_approx(wpos)) - .unwrap_or(0.0) as i32 - + 6, + alt: ctx.land.get_alt_approx(wpos) as i32 + 6, seed: ctx.rng.gen(), noise: RandomField::new(ctx.rng.gen()), floors: (0..floors) @@ -1164,3 +1158,270 @@ mod tests { mini_boss_fallback(tile_wcenter); } } + +pub fn spiral_staircase( + origin: Vec3, + radius: f32, + inner_radius: f32, + stretch: f32, +) -> Box) -> bool> { + Box::new(move |pos: Vec3| { + let pos = pos + origin; + if (pos.xy().magnitude_squared() as f32) < inner_radius.powi(2) { + true + } else if (pos.xy().magnitude_squared() as f32) < radius.powi(2) { + if ((pos.x as f32).atan2(pos.y as f32) / (f32::consts::PI * 2.0) * stretch + + pos.z as f32) + .rem_euclid(stretch) + < 1.5 + { + true + } else { + false + } + } else { + false + } + }) +} + +pub fn wall_staircase( + origin: Vec3, + radius: f32, + stretch: f32, +) -> Box) -> bool> { + Box::new(move |pos: Vec3| { + let pos = pos - origin; + if (pos.x.abs().max(pos.y.abs())) as f32 > 0.6 * radius { + if ((pos.x as f32).atan2(pos.y as f32) / (f32::consts::PI * 2.0) * stretch + + pos.z as f32) + .rem_euclid(stretch) + < 1.0 + { + true + } else { + false + } + } else { + false + } + }) +} + +impl SiteStructure for Dungeon { + fn render Id, G: FnMut(Id, Fill)>( + &self, + site: &site2::Site, + mut prim: F, + mut fill: G, + ) { + //let rpos = pos - self.tile_offset * TILE_SIZE; + //let tile_pos = rpos.map(|e| e.div_euclid(TILE_SIZE)); + //let tile_center = tile_pos * TILE_SIZE + TILE_SIZE / 2; + //let rtile_pos = rpos - tile_center; + + //let colors = &index.colors.site.dungeon; + + let vacant = Block::air(SpriteKind::Empty); + //let stone = Block::new(BlockKind::Rock, colors.stone.into()); + let stone_red = Block::new(BlockKind::Rock, Rgb::new(255, 0, 0)); + let stone_green = Block::new(BlockKind::Rock, Rgb::new(0, 255, 0)); + let stone_blue = Block::new(BlockKind::Rock, Rgb::new(0, 0, 255)); + + use inline_tweak::tweak; + let cutout_size = tweak!(9); + + let origin = self.origin.with_z(self.alt); + let cutout = prim(Primitive::Aabb(Aabb { + min: origin - Vec2::broadcast(cutout_size * 7).with_z(self.alt-1), + max: origin + Vec2::broadcast(cutout_size * 7).with_z(100), + })); + fill(cutout, Fill::Block(vacant)); + + + let stairs_inf = prim(Primitive::Sampling(wall_staircase( + origin, + TILE_SIZE as f32 / 2.0, + tweak!(27.0), + ))); + let bounding_box = prim(Primitive::Aabb(Aabb { + min: origin - Vec3::new(8, 8, self.alt-1), + max: origin + Vec3::new(8, 8, 400), + })); + //let stairs_inf = prim(Primitive::Sampling(Box::new(|_| true))); + let stairs = prim(Primitive::And(bounding_box, stairs_inf)); + let stairs_tr1 = prim(Primitive::Translate(stairs, Vec3::unit_z())); + let stairs_tr2 = prim(Primitive::Translate(stairs, Vec3::broadcast(tweak!(-16)))); + /*let stairs = prim(Primitive::Cone(Aabb { + min: self.origin.with_z(self.alt) - Vec3::broadcast(16), + max: self.origin.with_z(self.alt + 100) + Vec3::broadcast(16), + }));*/ + fill(stairs, Fill::Block(stone_red)); + fill(stairs_tr1, Fill::Block(stone_green)); + fill(stairs_tr2, Fill::Block(stone_blue)); + + /*let make_staircase = move |kind: &StairsKind, + pos: Vec3, + radius: f32, + inner_radius: f32, + stretch: f32, + height_limit: i32| { + match kind { + StairsKind::Spiral => make_spiral_staircase(pos, radius, inner_radius, stretch), + StairsKind::WallSpiral => { + make_wall_staircase(pos, radius, stretch * 3.0, height_limit) + }, + } + }; + + let wall_thickness = 3.0; + let dist_to_wall = self + .nearest_wall(rpos) + .map(|nearest| (nearest.distance_squared(rpos) as f32).sqrt()) + .unwrap_or(TILE_SIZE as f32); + let tunnel_dist = + 1.0 - (dist_to_wall - wall_thickness).max(0.0) / (TILE_SIZE as f32 - wall_thickness); + + let floor_sprite = if RandomField::new(7331).chance(Vec3::from(pos), 0.001) { + BlockMask::new( + with_sprite( + match (RandomField::new(1337).get(Vec3::from(pos)) / 2) % 30 { + 0 => SpriteKind::Apple, + 1 => SpriteKind::VeloriteFrag, + 2 => SpriteKind::Velorite, + 3..=8 => SpriteKind::Mushroom, + 9..=15 => SpriteKind::FireBowlGround, + _ => SpriteKind::ShortGrass, + }, + ), + 1, + ) + } else if let Some(Tile::Room(room)) | Some(Tile::DownStair(room)) = + self.tiles.get(tile_pos) + { + let room = &self.rooms[*room]; + if RandomField::new(room.seed).chance(Vec3::from(pos), room.loot_density * 0.5) { + match room.difficulty { + 0 => BlockMask::new(with_sprite(SpriteKind::DungeonChest0), 1), + 1 => BlockMask::new(with_sprite(SpriteKind::DungeonChest1), 1), + 2 => BlockMask::new(with_sprite(SpriteKind::DungeonChest2), 1), + 3 => BlockMask::new(with_sprite(SpriteKind::DungeonChest3), 1), + 4 => BlockMask::new(with_sprite(SpriteKind::DungeonChest4), 1), + 5 => BlockMask::new(with_sprite(SpriteKind::DungeonChest5), 1), + _ => BlockMask::new(with_sprite(SpriteKind::Chest), 1), + } + } else { + vacant + } + } else { + vacant + }; + + let tunnel_height = if self.final_level { 16.0 } else { 8.0 }; + let pillar_thickness: i32 = 4; + + move |z| match self.tiles.get(tile_pos) { + Some(Tile::Solid) => BlockMask::nothing(), + Some(Tile::Tunnel) => { + let light_offset: i32 = 7; + if (dist_to_wall - wall_thickness) as i32 == 1 + && rtile_pos.map(|e| e % light_offset == 0).reduce_bitxor() + && z == 1 + { + let ori = + Floor::relative_ori(rpos, self.nearest_wall(rpos).unwrap_or_default()); + let furniture = SpriteKind::WallSconce; + BlockMask::new(Block::air(furniture).with_ori(ori).unwrap(), 1) + } else if dist_to_wall >= wall_thickness + && (z as f32) < tunnel_height * (1.0 - tunnel_dist.powi(4)) + { + if z == 0 { floor_sprite } else { vacant } + } else { + BlockMask::nothing() + } + }, + Some(Tile::Room(room)) | Some(Tile::DownStair(room)) + if dist_to_wall < wall_thickness + || z as f32 + >= self.rooms[*room].height as f32 * (1.0 - tunnel_dist.powi(4)) => + { + BlockMask::nothing() + }, + + Some(Tile::Room(room)) | Some(Tile::DownStair(room)) + if self.rooms[*room] + .pillars + .map(|pillar_space| { + tile_pos + .map(|e| e.rem_euclid(pillar_space) == 0) + .reduce_and() + && rtile_pos.map(|e| e as f32).magnitude_squared() + < (pillar_thickness as f32 + 0.5).powi(2) + }) + .unwrap_or(false) => + { + if z == 1 && rtile_pos.product() == 0 && rtile_pos.sum().abs() == pillar_thickness { + let ori = Floor::relative_ori(rtile_pos, Vec2::zero()); + let furniture = SpriteKind::WallSconce; + BlockMask::new(Block::air(furniture).with_ori(ori).unwrap(), 1) + } else if z < self.rooms[*room].height + && rtile_pos.map(|e| e as f32).magnitude_squared() + > (pillar_thickness as f32 - 0.5).powi(2) + { + vacant + } else { + BlockMask::nothing() + } + } + + Some(Tile::Room(_)) => { + let light_offset = 7; + if z == 0 { + floor_sprite + } else if dist_to_wall as i32 == 4 + && rtile_pos.map(|e| e % light_offset == 0).reduce_bitxor() + && z == 1 + { + let ori = Floor::relative_ori( + rpos, + self.nearest_wall(rpos).unwrap_or_else(Vec2::zero), + ); + let furniture = SpriteKind::WallSconce; + BlockMask::new(Block::air(furniture).with_ori(ori).unwrap(), 1) + } else { + vacant + } + }, + Some(Tile::DownStair(_)) => vacant, + Some(Tile::UpStair(room, kind)) => { + let inner_radius: f32 = 0.5; + let stretch = 9; + let block = make_staircase( + kind, + Vec3::new(rtile_pos.x, rtile_pos.y, z), + TILE_SIZE as f32 / 2.0, + inner_radius, + stretch as f32, + self.total_depth(), + ); + let furniture = SpriteKind::WallSconce; + let ori = Floor::relative_ori(Vec2::zero(), rtile_pos); + if z < self.rooms[*room].height { + block.resolve_with(vacant) + } else if z % stretch == 0 && rtile_pos.x == 0 && rtile_pos.y == -TILE_SIZE / 2 { + BlockMask::new(Block::air(furniture).with_ori(ori).unwrap(), 1) + } else { + make_staircase( + kind, + Vec3::new(rtile_pos.x, rtile_pos.y, z), + TILE_SIZE as f32 / 2.0, + inner_radius, + stretch as f32, + self.total_depth(), + ) + } + }, + None => BlockMask::nothing(), + }*/ + } +} From 95214649dbfc107af3034faa4876f6adf482f4c1 Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Tue, 15 Jun 2021 19:26:33 -0400 Subject: [PATCH 149/180] Get structures loading via sampling primitive. --- world/src/site2/mod.rs | 2 +- world/src/site2/plot/dungeon.rs | 29 ++++++++++++++++++++++++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/world/src/site2/mod.rs b/world/src/site2/mod.rs index 52cc424fca..be3b561c79 100644 --- a/world/src/site2/mod.rs +++ b/world/src/site2/mod.rs @@ -302,7 +302,7 @@ impl Site { let dungeon = plot::Dungeon::generate(origin, land, &mut rng); site.name = dungeon.name().to_string(); //let size = (2.0 + rng.gen::().powf(8.0) * 3.0).round() as i32; - let size = 8; + let size = (dungeon.radius() / (2.0 * tile::TILE_SIZE as f32)).ceil() as i32; let aabr = Aabr { min: Vec2::broadcast(-size), diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index 0b31a0cde0..60fda0d854 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -3,7 +3,7 @@ use crate::{ block::block_from_structure, column::ColumnSample, site::{namegen::NameGen, BlockMask}, - site2::{self, Primitive, Fill, Structure as SiteStructure}, + site2::{self, Fill, Primitive, Structure as SiteStructure}, util::{attempt, Grid, RandomField, Sampler, CARDINALS, DIRS}, IndexRef, Land, }; @@ -14,7 +14,9 @@ use common::{ comp::{self}, generation::{ChunkSupplement, EntityInfo}, store::{Id, Store}, - terrain::{Block, BlockKind, SpriteKind, Structure, StructuresGroup, TerrainChunkSize}, + terrain::{ + Block, BlockKind, SpriteKind, Structure, structure::StructureBlock, StructuresGroup, TerrainChunkSize, + }, vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, WriteVol}, }; use core::{f32, hash::BuildHasherDefault}; @@ -1233,19 +1235,18 @@ impl SiteStructure for Dungeon { let origin = self.origin.with_z(self.alt); let cutout = prim(Primitive::Aabb(Aabb { - min: origin - Vec2::broadcast(cutout_size * 7).with_z(self.alt-1), + min: origin - Vec2::broadcast(cutout_size * 7).with_z(self.alt - 1), max: origin + Vec2::broadcast(cutout_size * 7).with_z(100), })); fill(cutout, Fill::Block(vacant)); - let stairs_inf = prim(Primitive::Sampling(wall_staircase( origin, TILE_SIZE as f32 / 2.0, tweak!(27.0), ))); let bounding_box = prim(Primitive::Aabb(Aabb { - min: origin - Vec3::new(8, 8, self.alt-1), + min: origin - Vec3::new(8, 8, self.alt - 1), max: origin + Vec3::new(8, 8, 400), })); //let stairs_inf = prim(Primitive::Sampling(Box::new(|_| true))); @@ -1260,6 +1261,24 @@ impl SiteStructure for Dungeon { fill(stairs_tr1, Fill::Block(stone_green)); fill(stairs_tr2, Fill::Block(stone_blue)); + lazy_static! { + pub static ref ENTRANCES: AssetHandle = + Structure::load_group("dungeon_entrances"); + } + + let entrances = ENTRANCES.read(); + let entrance = entrances[self.seed as usize % entrances.len()].clone(); + + let entrance_aabb = prim(Primitive::Aabb(entrance.get_bounds())); + let entrance = prim(Primitive::Sampling(Box::new(move |pos| { + entrance + .get(pos) + .map_or(false, |b| !matches!(b, StructureBlock::None)) + }))); + let entrance = prim(Primitive::And(entrance, entrance_aabb)); + let entrance = prim(Primitive::Translate(entrance, origin)); + fill(entrance, Fill::Block(stone_red)); + /*let make_staircase = move |kind: &StairsKind, pos: Vec3, radius: f32, From c6bb61f2e6131e296ddaade7fad86953f3190675 Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Tue, 22 Jun 2021 01:16:33 -0400 Subject: [PATCH 150/180] Add `Primitive::Prefab` and `Fill::Prefab` for coloring dungeon entrances. --- common/src/terrain/structure.rs | 5 +++-- common/src/volumes/dyna.rs | 1 + world/src/site/mod.rs | 6 +++--- world/src/site2/gen.rs | 34 ++++++++++++++++++++++++++++++--- world/src/site2/mod.rs | 32 ++++++++++++++++++------------- world/src/site2/plot/dungeon.rs | 17 ++++++++++++----- 6 files changed, 69 insertions(+), 26 deletions(-) diff --git a/common/src/terrain/structure.rs b/common/src/terrain/structure.rs index 14a658b43a..be38e9ca92 100644 --- a/common/src/terrain/structure.rs +++ b/common/src/terrain/structure.rs @@ -11,7 +11,7 @@ use vek::*; make_case_elim!( structure_block, - #[derive(Copy, Clone, PartialEq)] + #[derive(Copy, Clone, PartialEq, Debug)] #[repr(u32)] pub enum StructureBlock { None = 0, @@ -40,12 +40,13 @@ pub enum StructureError { OutOfBounds, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Structure { center: Vec3, base: Arc, } +#[derive(Debug)] struct BaseStructure { vol: Dyna, default_kind: BlockKind, diff --git a/common/src/volumes/dyna.rs b/common/src/volumes/dyna.rs index 9f38c189cd..2be6b25cbe 100644 --- a/common/src/volumes/dyna.rs +++ b/common/src/volumes/dyna.rs @@ -140,6 +140,7 @@ pub trait Access { fn idx(pos: Vec3, sz: Vec3) -> usize; } +#[derive(Copy, Clone, Debug)] pub struct ColumnAccess; impl Access for ColumnAccess { diff --git a/world/src/site/mod.rs b/world/src/site/mod.rs index 64d6961b88..2f109f2ab7 100644 --- a/world/src/site/mod.rs +++ b/world/src/site/mod.rs @@ -7,8 +7,7 @@ mod tree; // Reexports pub use self::{ - block_mask::BlockMask, castle::Castle, economy::Economy, - settlement::Settlement, tree::Tree, + block_mask::BlockMask, castle::Castle, economy::Economy, settlement::Settlement, tree::Tree, }; use crate::{column::ColumnSample, site2, Canvas}; @@ -155,7 +154,8 @@ impl Site { }; s.apply_supplement(dynamic_rng, wpos2d, get_column, supplement, economy) }, - SiteKind::Dungeon(d) => {}, //d.apply_supplement(dynamic_rng, wpos2d, get_column, supplement), + SiteKind::Dungeon(d) => {}, /* d.apply_supplement(dynamic_rng, wpos2d, get_column, + * supplement), */ SiteKind::Castle(c) => c.apply_supplement(dynamic_rng, wpos2d, get_column, supplement), SiteKind::Refactor(_) => {}, SiteKind::Tree(_) => {}, diff --git a/world/src/site2/gen.rs b/world/src/site2/gen.rs index 3293a7caac..d3c616ecb8 100644 --- a/world/src/site2/gen.rs +++ b/world/src/site2/gen.rs @@ -1,8 +1,15 @@ use super::*; -use crate::util::{RandomField, Sampler}; +use crate::{ + block::block_from_structure, + util::{RandomField, Sampler}, +}; use common::{ store::{Id, Store}, - terrain::{Block, BlockKind}, + terrain::{ + structure::{Structure as PrefabStructure, StructureBlock}, + Block, BlockKind, + }, + vol::ReadVol, }; use vek::*; @@ -18,6 +25,7 @@ pub enum Primitive { Sphere(Aabb), Plane(Aabr, Vec3, Vec2), Sampling(Box) -> bool>), + Prefab(PrefabStructure), // Combinators And(Id, Id), @@ -34,6 +42,10 @@ pub enum Primitive { pub enum Fill { Block(Block), Brick(BlockKind, Rgb, u8), + // TODO: the offset field for Prefab is a hack that breaks the compositionality of Translate, + // we probably need an evaluator for the primitive tree that gets which point is queried at + // leaf nodes given an input point to make Translate/Rotate work generally + Prefab(PrefabStructure, Vec3, u32), } impl Fill { @@ -99,6 +111,7 @@ impl Fill { .dot(*gradient) as i32) }, Primitive::Sampling(f) => f(pos), + Primitive::Prefab(p) => !matches!(p.get(pos), Err(_) | Ok(StructureBlock::None)), Primitive::And(a, b) => { self.contains_at(tree, *a, pos) && self.contains_at(tree, *b, pos) }, @@ -118,7 +131,7 @@ impl Fill { }, Primitive::Translate(prim, vec) => { self.contains_at(tree, *prim, pos.map2(*vec, i32::saturating_sub)) - } + }, } } @@ -127,6 +140,7 @@ impl Fill { tree: &Store, prim: Id, pos: Vec3, + canvas: &Canvas, ) -> Option { if self.contains_at(tree, prim, pos) { match self { @@ -137,6 +151,19 @@ impl Fill { .get((pos + Vec3::new(pos.z, pos.z, 0)) / Vec3::new(2, 2, 1)) % *range as u32) as u8, )), + Fill::Prefab(p, tr, seed) => p.get(pos - tr).ok().and_then(|sb| { + let info = canvas.info; + let col_sample = info.col(info.wpos)?; + block_from_structure( + canvas.index, + *sb, + pos - tr, + info.wpos, + *seed, + col_sample, + Block::air, + ) + }), } } else { None @@ -180,6 +207,7 @@ impl Fill { min: Vec3::broadcast(std::i32::MIN), max: Vec3::broadcast(std::i32::MAX), }, + Primitive::Prefab(p) => p.get_bounds(), Primitive::And(a, b) => or_zip_with( self.get_bounds_inner(tree, *a), self.get_bounds_inner(tree, *b), diff --git a/world/src/site2/mod.rs b/world/src/site2/mod.rs index be3b561c79..b27687ef77 100644 --- a/world/src/site2/mod.rs +++ b/world/src/site2/mod.rs @@ -276,18 +276,19 @@ impl Site { }); } - pub fn name(&self) -> &str { - &self.name - } + pub fn name(&self) -> &str { &self.name } pub fn difficulty(&self) -> Option { - self.plots.iter().filter_map(|(_, plot)| { - if let PlotKind::Dungeon(d) = &plot.kind { - Some(d.difficulty()) - } else { - None - } - }).max() + self.plots + .iter() + .filter_map(|(_, plot)| { + if let PlotKind::Dungeon(d) = &plot.kind { + Some(d.difficulty()) + } else { + None + } + }) + .max() } pub fn generate_dungeon(land: &Land, rng: &mut impl Rng, origin: Vec2) -> Self { @@ -763,9 +764,14 @@ impl Site { PlotKind::Castle(castle) => castle.render_collect(self), PlotKind::Dungeon(dungeon) => { let (prim_tree, fills) = dungeon.render_collect(self); - tracing::info!("{:?}: {:?} {:?}", dungeon.name(), prim_tree.ids().count(), fills.len()); + tracing::info!( + "{:?}: {:?} {:?}", + dungeon.name(), + prim_tree.ids().count(), + fills.len() + ); (prim_tree, fills) - } + }, _ => continue, }; @@ -777,7 +783,7 @@ impl Site { for z in aabb.min.z..aabb.max.z { let pos = Vec3::new(x, y, z); - if let Some(block) = fill.sample_at(&prim_tree, prim, pos) { + if let Some(block) = fill.sample_at(&prim_tree, prim, pos, &canvas) { canvas.set(pos, block); } } diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index 60fda0d854..61e8b0baab 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -15,7 +15,8 @@ use common::{ generation::{ChunkSupplement, EntityInfo}, store::{Id, Store}, terrain::{ - Block, BlockKind, SpriteKind, Structure, structure::StructureBlock, StructuresGroup, TerrainChunkSize, + structure::StructureBlock, Block, BlockKind, SpriteKind, Structure, StructuresGroup, + TerrainChunkSize, }, vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, WriteVol}, }; @@ -1245,9 +1246,10 @@ impl SiteStructure for Dungeon { TILE_SIZE as f32 / 2.0, tweak!(27.0), ))); + let stairs_radius = 7; let bounding_box = prim(Primitive::Aabb(Aabb { - min: origin - Vec3::new(8, 8, self.alt - 1), - max: origin + Vec3::new(8, 8, 400), + min: origin - Vec3::new(stairs_radius, stairs_radius, self.alt - 1), + max: origin + Vec3::new(stairs_radius, stairs_radius, 400), })); //let stairs_inf = prim(Primitive::Sampling(Box::new(|_| true))); let stairs = prim(Primitive::And(bounding_box, stairs_inf)); @@ -1269,7 +1271,7 @@ impl SiteStructure for Dungeon { let entrances = ENTRANCES.read(); let entrance = entrances[self.seed as usize % entrances.len()].clone(); - let entrance_aabb = prim(Primitive::Aabb(entrance.get_bounds())); + /*let entrance_aabb = prim(Primitive::Aabb(entrance.get_bounds())); let entrance = prim(Primitive::Sampling(Box::new(move |pos| { entrance .get(pos) @@ -1277,7 +1279,12 @@ impl SiteStructure for Dungeon { }))); let entrance = prim(Primitive::And(entrance, entrance_aabb)); let entrance = prim(Primitive::Translate(entrance, origin)); - fill(entrance, Fill::Block(stone_red)); + fill(entrance, Fill::Block(stone_red));*/ + let entrance_prim = prim(Primitive::Prefab(entrance.clone())); + let entrance_prim = prim(Primitive::Translate(entrance_prim, origin)); + let entrance_prim = prim(Primitive::Diff(entrance_prim, bounding_box)); + //fill(entrance_prim, Fill::Block(stone_red)); + fill(entrance_prim, Fill::Prefab(entrance, origin, self.seed)); /*let make_staircase = move |kind: &StairsKind, pos: Vec3, From c8383aba389c0f4d56429ed1eaad07d217767265 Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Tue, 22 Jun 2021 20:56:37 -0400 Subject: [PATCH 151/180] Traverse the dungeon tile structure to create the rooms, and align the stairs. --- world/src/site/mod.rs | 4 +- world/src/site2/gen.rs | 2 +- world/src/site2/mod.rs | 12 +- world/src/site2/plot/dungeon.rs | 235 ++++++++++++++++++++------------ 4 files changed, 153 insertions(+), 100 deletions(-) diff --git a/world/src/site/mod.rs b/world/src/site/mod.rs index 2f109f2ab7..47a425a050 100644 --- a/world/src/site/mod.rs +++ b/world/src/site/mod.rs @@ -154,8 +154,8 @@ impl Site { }; s.apply_supplement(dynamic_rng, wpos2d, get_column, supplement, economy) }, - SiteKind::Dungeon(d) => {}, /* d.apply_supplement(dynamic_rng, wpos2d, get_column, - * supplement), */ + SiteKind::Dungeon(d) => {}, /* d.apply_supplement(dynamic_rng, wpos2d, get_column, */ + // supplement), SiteKind::Castle(c) => c.apply_supplement(dynamic_rng, wpos2d, get_column, supplement), SiteKind::Refactor(_) => {}, SiteKind::Tree(_) => {}, diff --git a/world/src/site2/gen.rs b/world/src/site2/gen.rs index d3c616ecb8..ae82e7eed8 100644 --- a/world/src/site2/gen.rs +++ b/world/src/site2/gen.rs @@ -158,7 +158,7 @@ impl Fill { canvas.index, *sb, pos - tr, - info.wpos, + p.get_bounds().center().xy(), *seed, col_sample, Block::air, diff --git a/world/src/site2/mod.rs b/world/src/site2/mod.rs index b27687ef77..e92131321c 100644 --- a/world/src/site2/mod.rs +++ b/world/src/site2/mod.rs @@ -753,7 +753,6 @@ impl Site { } } } - tracing::info!("{:?}: {:?}", canvas.wpos(), plots.len()); let mut plots_to_render = plots.into_iter().collect::>(); plots_to_render.sort_unstable(); @@ -762,16 +761,7 @@ impl Site { let (prim_tree, fills) = match &self.plots[plot].kind { PlotKind::House(house) => house.render_collect(self), PlotKind::Castle(castle) => castle.render_collect(self), - PlotKind::Dungeon(dungeon) => { - let (prim_tree, fills) = dungeon.render_collect(self); - tracing::info!( - "{:?}: {:?} {:?}", - dungeon.name(), - prim_tree.ids().count(), - fills.len() - ); - (prim_tree, fills) - }, + PlotKind::Dungeon(dungeon) => dungeon.render_collect(self), _ => continue, }; diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index 61e8b0baab..f555048bb1 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -1169,7 +1169,7 @@ pub fn spiral_staircase( stretch: f32, ) -> Box) -> bool> { Box::new(move |pos: Vec3| { - let pos = pos + origin; + let pos = pos - origin; if (pos.xy().magnitude_squared() as f32) < inner_radius.powi(2) { true } else if (pos.xy().magnitude_squared() as f32) < radius.powi(2) { @@ -1211,104 +1211,31 @@ pub fn wall_staircase( }) } -impl SiteStructure for Dungeon { +impl Floor { fn render Id, G: FnMut(Id, Fill)>( &self, - site: &site2::Site, mut prim: F, mut fill: G, + dungeon: &Dungeon, + floor_z: i32, ) { //let rpos = pos - self.tile_offset * TILE_SIZE; //let tile_pos = rpos.map(|e| e.div_euclid(TILE_SIZE)); //let tile_center = tile_pos * TILE_SIZE + TILE_SIZE / 2; //let rtile_pos = rpos - tile_center; - + let vacant = Block::air(SpriteKind::Empty); + let stone_red = Block::new(BlockKind::Rock, Rgb::new(255, 0, 0)); + let stone_orange = Block::new(BlockKind::Rock, Rgb::new(255, 128, 0)); + let stone_green = Block::new(BlockKind::Rock, Rgb::new(0, 255, 0)); + let stone_cyan = Block::new(BlockKind::Rock, Rgb::new(0, 255, 255)); + let stone_blue = Block::new(BlockKind::Rock, Rgb::new(0, 0, 255)); //let colors = &index.colors.site.dungeon; - let vacant = Block::air(SpriteKind::Empty); - //let stone = Block::new(BlockKind::Rock, colors.stone.into()); - let stone_red = Block::new(BlockKind::Rock, Rgb::new(255, 0, 0)); - let stone_green = Block::new(BlockKind::Rock, Rgb::new(0, 255, 0)); - let stone_blue = Block::new(BlockKind::Rock, Rgb::new(0, 0, 255)); - - use inline_tweak::tweak; - let cutout_size = tweak!(9); - - let origin = self.origin.with_z(self.alt); - let cutout = prim(Primitive::Aabb(Aabb { - min: origin - Vec2::broadcast(cutout_size * 7).with_z(self.alt - 1), - max: origin + Vec2::broadcast(cutout_size * 7).with_z(100), - })); - fill(cutout, Fill::Block(vacant)); - - let stairs_inf = prim(Primitive::Sampling(wall_staircase( - origin, - TILE_SIZE as f32 / 2.0, - tweak!(27.0), - ))); - let stairs_radius = 7; - let bounding_box = prim(Primitive::Aabb(Aabb { - min: origin - Vec3::new(stairs_radius, stairs_radius, self.alt - 1), - max: origin + Vec3::new(stairs_radius, stairs_radius, 400), - })); - //let stairs_inf = prim(Primitive::Sampling(Box::new(|_| true))); - let stairs = prim(Primitive::And(bounding_box, stairs_inf)); - let stairs_tr1 = prim(Primitive::Translate(stairs, Vec3::unit_z())); - let stairs_tr2 = prim(Primitive::Translate(stairs, Vec3::broadcast(tweak!(-16)))); - /*let stairs = prim(Primitive::Cone(Aabb { - min: self.origin.with_z(self.alt) - Vec3::broadcast(16), - max: self.origin.with_z(self.alt + 100) + Vec3::broadcast(16), - }));*/ - fill(stairs, Fill::Block(stone_red)); - fill(stairs_tr1, Fill::Block(stone_green)); - fill(stairs_tr2, Fill::Block(stone_blue)); - - lazy_static! { - pub static ref ENTRANCES: AssetHandle = - Structure::load_group("dungeon_entrances"); - } - - let entrances = ENTRANCES.read(); - let entrance = entrances[self.seed as usize % entrances.len()].clone(); - - /*let entrance_aabb = prim(Primitive::Aabb(entrance.get_bounds())); - let entrance = prim(Primitive::Sampling(Box::new(move |pos| { - entrance - .get(pos) - .map_or(false, |b| !matches!(b, StructureBlock::None)) + let floor_sprite = prim(Primitive::Sampling(Box::new(|pos| { + RandomField::new(7331).chance(Vec3::from(pos), 0.001) }))); - let entrance = prim(Primitive::And(entrance, entrance_aabb)); - let entrance = prim(Primitive::Translate(entrance, origin)); - fill(entrance, Fill::Block(stone_red));*/ - let entrance_prim = prim(Primitive::Prefab(entrance.clone())); - let entrance_prim = prim(Primitive::Translate(entrance_prim, origin)); - let entrance_prim = prim(Primitive::Diff(entrance_prim, bounding_box)); - //fill(entrance_prim, Fill::Block(stone_red)); - fill(entrance_prim, Fill::Prefab(entrance, origin, self.seed)); - /*let make_staircase = move |kind: &StairsKind, - pos: Vec3, - radius: f32, - inner_radius: f32, - stretch: f32, - height_limit: i32| { - match kind { - StairsKind::Spiral => make_spiral_staircase(pos, radius, inner_radius, stretch), - StairsKind::WallSpiral => { - make_wall_staircase(pos, radius, stretch * 3.0, height_limit) - }, - } - }; - - let wall_thickness = 3.0; - let dist_to_wall = self - .nearest_wall(rpos) - .map(|nearest| (nearest.distance_squared(rpos) as f32).sqrt()) - .unwrap_or(TILE_SIZE as f32); - let tunnel_dist = - 1.0 - (dist_to_wall - wall_thickness).max(0.0) / (TILE_SIZE as f32 - wall_thickness); - - let floor_sprite = if RandomField::new(7331).chance(Vec3::from(pos), 0.001) { + /*let floor_sprite = if RandomField::new(7331).chance(Vec3::from(pos), 0.001) { BlockMask::new( with_sprite( match (RandomField::new(1337).get(Vec3::from(pos)) / 2) % 30 { @@ -1341,8 +1268,114 @@ impl SiteStructure for Dungeon { } } else { vacant + };*/ + let aabb_edges = |prim: &mut F, aabb: Aabb<_>| { + let f = |prim: &mut F, ret, vec| { + let sub = prim(Primitive::Aabb(Aabb { + min: aabb.min + vec, + max: aabb.max - vec, + })); + prim(Primitive::Diff(ret, sub)) + }; + let mut ret = prim(Primitive::Aabb(aabb)); + ret = f(prim, ret, Vec3::new(1, 0, 0)); + ret = f(prim, ret, Vec3::new(0, 1, 0)); + ret = f(prim, ret, Vec3::new(0, 0, 1)); + ret }; + fn aabr_with_z(aabr: Aabr, z: std::ops::Range) -> Aabb { + Aabb { + min: aabr.min.with_z(z.start), + max: aabr.max.with_z(z.end), + } + } + + let mut stairs_bb = prim(Primitive::Empty); + let mut stairs = prim(Primitive::Empty); + + for (tile_pos, tile) in self.tiles.iter() { + let tile_corner = dungeon.origin + TILE_SIZE * (self.tile_offset + tile_pos); + let tile_aabr = Aabr { + min: tile_corner, + max: tile_corner + Vec2::broadcast(TILE_SIZE), + }; + let (color, mut height, room) = match tile { + Tile::UpStair(room, kind) => { + let center = (tile_corner + Vec2::broadcast(TILE_SIZE) / 2).with_z(floor_z); + let radius = TILE_SIZE as f32 / 2.0; + let aabb = + aabr_with_z(tile_aabr, floor_z + 1..floor_z + self.total_depth() + 1); + let bb = prim(match kind { + StairsKind::Spiral => Primitive::Cylinder(aabb), + StairsKind::WallSpiral => Primitive::Aabb(aabb), + }); + let stair = prim(Primitive::Sampling(match kind { + StairsKind::Spiral => spiral_staircase(center, radius, 0.5, 9.0), + StairsKind::WallSpiral => wall_staircase(center, radius, 27.0), + })); + let stair = prim(Primitive::And(bb, stair)); + stairs_bb = prim(Primitive::Or(stairs_bb, bb)); + stairs = prim(Primitive::Or(stairs, stair)); + (stone_cyan, self.hollow_depth, Some(room)) + }, + Tile::DownStair(room) => (stone_green, self.hollow_depth, Some(room)), + Tile::Room(room) => (stone_blue, self.hollow_depth, Some(room)), + Tile::Tunnel => (stone_orange, self.hollow_depth / 2, None), + //Tile::Solid => (vacant, self.total_depth(), None), + Tile::Solid => continue, + }; + + if let Some(room) = room.map(|i| self.rooms.get(*i)) { + height = height.min(room.height); + } + + let tile_air = prim(Primitive::Aabb(aabr_with_z( + tile_aabr, + floor_z..floor_z + height, + ))); + fill(tile_air, Fill::Block(vacant)); + let tile_edges = + aabb_edges(&mut prim, aabr_with_z(tile_aabr, floor_z..floor_z + height)); + + let floor_prim = prim(Primitive::Aabb(aabr_with_z( + tile_aabr, + floor_z..floor_z + 1, + ))); + fill(floor_prim, Fill::Block(color)); + let sprite_layer = prim(Primitive::Aabb(aabr_with_z( + tile_aabr, + floor_z + 1..floor_z + 2, + ))); + let sprite_intersection = prim(Primitive::And(floor_sprite, sprite_layer)); + fill(sprite_intersection, Fill::Block(stone_red)); + fill(tile_edges, Fill::Block(Block::air(SpriteKind::Lantern))); + } + + fill(stairs_bb, Fill::Block(vacant)); + fill(stairs, Fill::Block(stone_red)); + /*let make_staircase = move |kind: &StairsKind, + pos: Vec3, + radius: f32, + inner_radius: f32, + stretch: f32, + height_limit: i32| { + match kind { + StairsKind::Spiral => make_spiral_staircase(pos, radius, inner_radius, stretch), + StairsKind::WallSpiral => { + make_wall_staircase(pos, radius, stretch * 3.0, height_limit) + }, + } + }; + + let wall_thickness = 3.0; + let dist_to_wall = self + .nearest_wall(rpos) + .map(|nearest| (nearest.distance_squared(rpos) as f32).sqrt()) + .unwrap_or(TILE_SIZE as f32); + let tunnel_dist = + 1.0 - (dist_to_wall - wall_thickness).max(0.0) / (TILE_SIZE as f32 - wall_thickness); + let tunnel_height = if self.final_level { 16.0 } else { 8.0 }; let pillar_thickness: i32 = 4; @@ -1451,3 +1484,33 @@ impl SiteStructure for Dungeon { }*/ } } + +impl SiteStructure for Dungeon { + fn render Id, G: FnMut(Id, Fill)>( + &self, + site: &site2::Site, + mut prim: F, + mut fill: G, + ) { + let origin = (self.origin + Vec2::broadcast(TILE_SIZE / 2)).with_z(self.alt + ALT_OFFSET); + + lazy_static! { + pub static ref ENTRANCES: AssetHandle = + Structure::load_group("dungeon_entrances"); + } + + let entrances = ENTRANCES.read(); + let entrance = entrances[self.seed as usize % entrances.len()].clone(); + + let entrance_prim = prim(Primitive::Prefab(entrance.clone())); + let entrance_prim = prim(Primitive::Translate(entrance_prim, origin)); + fill(entrance_prim, Fill::Prefab(entrance, origin, self.seed)); + + let mut z = self.alt + ALT_OFFSET; + for floor in &self.floors { + z -= floor.total_depth(); + + floor.render(&mut prim, &mut fill, &self, z); + } + } +} From c68d79315efa3e4843e6130c5a922e376caaa4ad Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Wed, 23 Jun 2021 13:11:15 -0400 Subject: [PATCH 152/180] Add wall contours and sprites to site2 dungeon. Also add septagon decal in boss rooms. --- world/src/site2/gen.rs | 21 ++- world/src/site2/plot/dungeon.rs | 263 ++++++++++++++++++++------------ 2 files changed, 177 insertions(+), 107 deletions(-) diff --git a/world/src/site2/gen.rs b/world/src/site2/gen.rs index ae82e7eed8..7b705248f4 100644 --- a/world/src/site2/gen.rs +++ b/world/src/site2/gen.rs @@ -11,6 +11,7 @@ use common::{ }, vol::ReadVol, }; +use std::sync::Arc; use vek::*; #[allow(dead_code)] @@ -19,12 +20,17 @@ pub enum Primitive { // Shapes Aabb(Aabb), - Pyramid { aabb: Aabb, inset: i32 }, + Pyramid { + aabb: Aabb, + inset: i32, + }, Cylinder(Aabb), Cone(Aabb), Sphere(Aabb), Plane(Aabr, Vec3, Vec2), - Sampling(Box) -> bool>), + /// A sampling function is always a subset of another primitive to avoid + /// needing infinite bounds + Sampling(Id, Box) -> bool>), Prefab(PrefabStructure), // Combinators @@ -38,7 +44,7 @@ pub enum Primitive { Translate(Id, Vec3), } -#[derive(Debug)] +#[derive(Clone)] pub enum Fill { Block(Block), Brick(BlockKind, Rgb, u8), @@ -46,6 +52,7 @@ pub enum Fill { // we probably need an evaluator for the primitive tree that gets which point is queried at // leaf nodes given an input point to make Translate/Rotate work generally Prefab(PrefabStructure, Vec3, u32), + Sampling(Arc) -> Block>), } impl Fill { @@ -110,7 +117,7 @@ impl Fill { .as_() .dot(*gradient) as i32) }, - Primitive::Sampling(f) => f(pos), + Primitive::Sampling(a, f) => self.contains_at(tree, *a, pos) && f(pos), Primitive::Prefab(p) => !matches!(p.get(pos), Err(_) | Ok(StructureBlock::None)), Primitive::And(a, b) => { self.contains_at(tree, *a, pos) && self.contains_at(tree, *b, pos) @@ -164,6 +171,7 @@ impl Fill { Block::air, ) }), + Fill::Sampling(f) => Some(f(pos)), } } else { None @@ -203,10 +211,7 @@ impl Fill { }; aabb.made_valid() }, - Primitive::Sampling(_) => Aabb { - min: Vec3::broadcast(std::i32::MIN), - max: Vec3::broadcast(std::i32::MAX), - }, + Primitive::Sampling(a, _) => self.get_bounds_inner(tree, *a)?, Primitive::Prefab(p) => p.get_bounds(), Primitive::And(a, b) => or_zip_with( self.get_bounds_inner(tree, *a), diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index f555048bb1..318519011f 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -14,10 +14,7 @@ use common::{ comp::{self}, generation::{ChunkSupplement, EntityInfo}, store::{Id, Store}, - terrain::{ - structure::StructureBlock, Block, BlockKind, SpriteKind, Structure, StructuresGroup, - TerrainChunkSize, - }, + terrain::{Block, BlockKind, SpriteKind, Structure, StructuresGroup, TerrainChunkSize}, vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, WriteVol}, }; use core::{f32, hash::BuildHasherDefault}; @@ -25,6 +22,7 @@ use fxhash::FxHasher64; use lazy_static::lazy_static; use rand::{prelude::*, seq::SliceRandom}; use serde::Deserialize; +use std::sync::Arc; use vek::*; pub struct Dungeon { @@ -692,23 +690,7 @@ impl Floor { fn total_depth(&self) -> i32 { self.solid_depth + self.hollow_depth } fn nearest_wall(&self, rpos: Vec2) -> Option> { - let tile_pos = rpos.map(|e| e.div_euclid(TILE_SIZE)); - - DIRS.iter() - .map(|dir| tile_pos + *dir) - .filter(|other_tile_pos| { - self.tiles - .get(*other_tile_pos) - .filter(|tile| tile.is_passable()) - .is_none() - }) - .map(|other_tile_pos| { - rpos.clamped( - other_tile_pos * TILE_SIZE, - (other_tile_pos + 1) * TILE_SIZE - 1, - ) - }) - .min_by_key(|nearest| rpos.distance_squared(*nearest)) + tilegrid_nearest_wall(&self.tiles, rpos) } // Find orientation of a position relative to another position @@ -1162,6 +1144,26 @@ mod tests { } } +pub fn tilegrid_nearest_wall(tiles: &Grid, rpos: Vec2) -> Option> { + let tile_pos = rpos.map(|e| e.div_euclid(TILE_SIZE)); + + DIRS.iter() + .map(|dir| tile_pos + *dir) + .filter(|other_tile_pos| { + tiles + .get(*other_tile_pos) + .filter(|tile| tile.is_passable()) + .is_none() + }) + .map(|other_tile_pos| { + rpos.clamped( + other_tile_pos * TILE_SIZE, + (other_tile_pos + 1) * TILE_SIZE - 1, + ) + }) + .min_by_key(|nearest| rpos.distance_squared(*nearest)) +} + pub fn spiral_staircase( origin: Vec3, radius: f32, @@ -1211,6 +1213,30 @@ pub fn wall_staircase( }) } +pub fn inscribed_polystar( + origin: Vec2, + radius: f32, + sides: usize, +) -> Box) -> bool> { + Box::new(move |pos| { + use std::f32::consts::TAU; + let rpos: Vec2 = pos.xy().as_() - origin.as_(); + let is_border = rpos.magnitude_squared() > (radius - 2.0).powi(2); + let is_line = (0..sides).into_iter().any(|i| { + let f = |j: f32| { + let t = j * TAU / sides as f32; + radius * Vec2::new(t.cos(), t.sin()) + }; + let line = LineSegment2 { + start: f(i as f32), + end: f((i + 2) as f32), + }; + line.distance_to_point(rpos) <= 1.0 + }); + is_border || is_line + }) +} + impl Floor { fn render Id, G: FnMut(Id, Fill)>( &self, @@ -1219,56 +1245,43 @@ impl Floor { dungeon: &Dungeon, floor_z: i32, ) { + let floor_corner = dungeon.origin + TILE_SIZE * self.tile_offset; + let floor_aabb = prim(Primitive::Aabb(Aabb { + min: floor_corner.with_z(floor_z), + max: (floor_corner + TILE_SIZE * self.tiles.size()) + .with_z(floor_z + self.total_depth()), + })); //let rpos = pos - self.tile_offset * TILE_SIZE; //let tile_pos = rpos.map(|e| e.div_euclid(TILE_SIZE)); //let tile_center = tile_pos * TILE_SIZE + TILE_SIZE / 2; //let rtile_pos = rpos - tile_center; let vacant = Block::air(SpriteKind::Empty); let stone_red = Block::new(BlockKind::Rock, Rgb::new(255, 0, 0)); - let stone_orange = Block::new(BlockKind::Rock, Rgb::new(255, 128, 0)); - let stone_green = Block::new(BlockKind::Rock, Rgb::new(0, 255, 0)); - let stone_cyan = Block::new(BlockKind::Rock, Rgb::new(0, 255, 255)); - let stone_blue = Block::new(BlockKind::Rock, Rgb::new(0, 0, 255)); + //let stone_orange = Block::new(BlockKind::Rock, Rgb::new(255, 128, 0)); + let stone_purple = Block::new(BlockKind::Rock, Rgb::new(96, 0, 128)); + //let stone_green = Block::new(BlockKind::Rock, Rgb::new(0, 255, 0)); + //let stone_cyan = Block::new(BlockKind::Rock, Rgb::new(0, 255, 255)); + //let stone_blue = Block::new(BlockKind::Rock, Rgb::new(0, 0, 255)); //let colors = &index.colors.site.dungeon; - let floor_sprite = prim(Primitive::Sampling(Box::new(|pos| { - RandomField::new(7331).chance(Vec3::from(pos), 0.001) - }))); + let floor_sprite = prim(Primitive::Sampling( + floor_aabb, + Box::new(|pos| RandomField::new(7331).chance(Vec3::from(pos), 0.001)), + )); - /*let floor_sprite = if RandomField::new(7331).chance(Vec3::from(pos), 0.001) { - BlockMask::new( - with_sprite( - match (RandomField::new(1337).get(Vec3::from(pos)) / 2) % 30 { - 0 => SpriteKind::Apple, - 1 => SpriteKind::VeloriteFrag, - 2 => SpriteKind::Velorite, - 3..=8 => SpriteKind::Mushroom, - 9..=15 => SpriteKind::FireBowlGround, - _ => SpriteKind::ShortGrass, - }, - ), - 1, + let floor_sprite_fill = Fill::Sampling(Arc::new(|pos| { + Block::air( + match (RandomField::new(1337).get(Vec3::from(pos)) / 2) % 30 { + 0 => SpriteKind::Apple, + 1 => SpriteKind::VeloriteFrag, + 2 => SpriteKind::Velorite, + 3..=8 => SpriteKind::Mushroom, + 9..=15 => SpriteKind::FireBowlGround, + _ => SpriteKind::ShortGrass, + }, ) - } else if let Some(Tile::Room(room)) | Some(Tile::DownStair(room)) = - self.tiles.get(tile_pos) - { - let room = &self.rooms[*room]; - if RandomField::new(room.seed).chance(Vec3::from(pos), room.loot_density * 0.5) { - match room.difficulty { - 0 => BlockMask::new(with_sprite(SpriteKind::DungeonChest0), 1), - 1 => BlockMask::new(with_sprite(SpriteKind::DungeonChest1), 1), - 2 => BlockMask::new(with_sprite(SpriteKind::DungeonChest2), 1), - 3 => BlockMask::new(with_sprite(SpriteKind::DungeonChest3), 1), - 4 => BlockMask::new(with_sprite(SpriteKind::DungeonChest4), 1), - 5 => BlockMask::new(with_sprite(SpriteKind::DungeonChest5), 1), - _ => BlockMask::new(with_sprite(SpriteKind::Chest), 1), - } - } else { - vacant - } - } else { - vacant - };*/ + })); + let aabb_edges = |prim: &mut F, aabb: Aabb<_>| { let f = |prim: &mut F, ret, vec| { let sub = prim(Primitive::Aabb(Aabb { @@ -1291,8 +1304,29 @@ impl Floor { } } - let mut stairs_bb = prim(Primitive::Empty); - let mut stairs = prim(Primitive::Empty); + let wall_thickness = 3.0; + let tunnel_height = if self.final_level { 16.0 } else { 8.0 }; + let pillar_thickness: i32 = 4; + + let tiles = self.tiles.clone(); + let wall_contours = prim(Primitive::Sampling( + floor_aabb, + Box::new(move |pos| { + let rpos = pos.xy() - floor_corner; + let dist_to_wall = tilegrid_nearest_wall(&tiles, rpos) + .map(|nearest| (nearest.distance_squared(rpos) as f32).sqrt()) + .unwrap_or(TILE_SIZE as f32); + let tunnel_dist = 1.0 + - (dist_to_wall - wall_thickness).max(0.0) + / (TILE_SIZE as f32 - wall_thickness); + dist_to_wall >= wall_thickness + && ((pos.z - floor_z) as f32) < tunnel_height * (1.0 - tunnel_dist.powi(4)) + }), + )); + + let mut stairs_bb = Vec::new(); + let mut stairs = Vec::new(); + let mut boss_room_center = None; for (tile_pos, tile) in self.tiles.iter() { let tile_corner = dungeon.origin + TILE_SIZE * (self.tile_offset + tile_pos); @@ -1300,60 +1334,102 @@ impl Floor { min: tile_corner, max: tile_corner + Vec2::broadcast(TILE_SIZE), }; - let (color, mut height, room) = match tile { + let (mut height, room) = match tile { Tile::UpStair(room, kind) => { let center = (tile_corner + Vec2::broadcast(TILE_SIZE) / 2).with_z(floor_z); let radius = TILE_SIZE as f32 / 2.0; - let aabb = - aabr_with_z(tile_aabr, floor_z + 1..floor_z + self.total_depth() + 1); + let aabb = aabr_with_z(tile_aabr, floor_z..floor_z + self.total_depth()); let bb = prim(match kind { StairsKind::Spiral => Primitive::Cylinder(aabb), StairsKind::WallSpiral => Primitive::Aabb(aabb), }); - let stair = prim(Primitive::Sampling(match kind { + let stair = prim(Primitive::Sampling(bb, match kind { StairsKind::Spiral => spiral_staircase(center, radius, 0.5, 9.0), StairsKind::WallSpiral => wall_staircase(center, radius, 27.0), })); - let stair = prim(Primitive::And(bb, stair)); - stairs_bb = prim(Primitive::Or(stairs_bb, bb)); - stairs = prim(Primitive::Or(stairs, stair)); - (stone_cyan, self.hollow_depth, Some(room)) + stairs_bb.push(bb); + stairs.push(stair); + (self.hollow_depth, Some(room)) }, - Tile::DownStair(room) => (stone_green, self.hollow_depth, Some(room)), - Tile::Room(room) => (stone_blue, self.hollow_depth, Some(room)), - Tile::Tunnel => (stone_orange, self.hollow_depth / 2, None), - //Tile::Solid => (vacant, self.total_depth(), None), + Tile::DownStair(room) => (self.hollow_depth, Some(room)), + Tile::Room(room) => (self.hollow_depth, Some(room)), + Tile::Tunnel => (tunnel_height as i32, None), Tile::Solid => continue, }; + let sprite_layer = prim(Primitive::Aabb(aabr_with_z( + tile_aabr, + floor_z..floor_z + 1, + ))); + let sprite_layer = prim(Primitive::And(sprite_layer, wall_contours)); + + let mut chests = None; + if let Some(room) = room.map(|i| self.rooms.get(*i)) { height = height.min(room.height); + if matches!(tile, Tile::Room(_) | Tile::DownStair(_)) { + let seed = room.seed; + let loot_density = room.loot_density; + let difficulty = room.difficulty; + let chest_sprite = prim(Primitive::Sampling( + sprite_layer, + Box::new(move |pos| { + RandomField::new(seed).chance(Vec3::from(pos), loot_density * 0.5) + }), + )); + let chest_sprite_fill = Fill::Block(Block::air(match difficulty { + 0 => SpriteKind::DungeonChest0, + 1 => SpriteKind::DungeonChest1, + 2 => SpriteKind::DungeonChest2, + 3 => SpriteKind::DungeonChest3, + 4 => SpriteKind::DungeonChest4, + 5 => SpriteKind::DungeonChest5, + _ => SpriteKind::Chest, + })); + chests = Some((chest_sprite, chest_sprite_fill)); + } + + if room.boss { + boss_room_center = Some(floor_corner + TILE_SIZE * room.area.center()); + } } let tile_air = prim(Primitive::Aabb(aabr_with_z( tile_aabr, floor_z..floor_z + height, ))); + let tile_air = prim(Primitive::And(tile_air, wall_contours)); fill(tile_air, Fill::Block(vacant)); + if let Some((chest_sprite, chest_sprite_fill)) = chests { + let chest_sprite = prim(Primitive::And(chest_sprite, wall_contours)); + fill(chest_sprite, chest_sprite_fill); + } let tile_edges = aabb_edges(&mut prim, aabr_with_z(tile_aabr, floor_z..floor_z + height)); - let floor_prim = prim(Primitive::Aabb(aabr_with_z( - tile_aabr, - floor_z..floor_z + 1, - ))); - fill(floor_prim, Fill::Block(color)); - let sprite_layer = prim(Primitive::Aabb(aabr_with_z( - tile_aabr, - floor_z + 1..floor_z + 2, - ))); - let sprite_intersection = prim(Primitive::And(floor_sprite, sprite_layer)); - fill(sprite_intersection, Fill::Block(stone_red)); + let floor_sprite = prim(Primitive::And(sprite_layer, floor_sprite)); + fill(floor_sprite, floor_sprite_fill.clone()); fill(tile_edges, Fill::Block(Block::air(SpriteKind::Lantern))); } - fill(stairs_bb, Fill::Block(vacant)); - fill(stairs, Fill::Block(stone_red)); + if let Some(boss_room_center) = boss_room_center { + let magic_circle_bb = prim(Primitive::Cylinder(Aabb { + min: (boss_room_center - 3 * Vec2::broadcast(TILE_SIZE) / 2).with_z(floor_z - 1), + max: (boss_room_center + 3 * Vec2::broadcast(TILE_SIZE) / 2).with_z(floor_z), + })); + let magic_circle = prim(Primitive::Sampling( + magic_circle_bb, + inscribed_polystar(boss_room_center, 1.4 * TILE_SIZE as f32, 7), + )); + fill(magic_circle, Fill::Block(stone_purple)); + } + + for stair_bb in stairs_bb.iter() { + fill(*stair_bb, Fill::Block(vacant)); + } + for stair in stairs.iter() { + fill(*stair, Fill::Block(stone_red)); + } /*let make_staircase = move |kind: &StairsKind, pos: Vec3, radius: f32, @@ -1368,17 +1444,6 @@ impl Floor { } }; - let wall_thickness = 3.0; - let dist_to_wall = self - .nearest_wall(rpos) - .map(|nearest| (nearest.distance_squared(rpos) as f32).sqrt()) - .unwrap_or(TILE_SIZE as f32); - let tunnel_dist = - 1.0 - (dist_to_wall - wall_thickness).max(0.0) / (TILE_SIZE as f32 - wall_thickness); - - let tunnel_height = if self.final_level { 16.0 } else { 8.0 }; - let pillar_thickness: i32 = 4; - move |z| match self.tiles.get(tile_pos) { Some(Tile::Solid) => BlockMask::nothing(), Some(Tile::Tunnel) => { From 033dbbf02a9169f3610d4bc0a1b1e3b436d8635f Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Wed, 23 Jun 2021 17:45:20 -0400 Subject: [PATCH 153/180] Add pillars and wall sconces to site2 dungeons. --- world/src/site2/gen.rs | 4 +- world/src/site2/plot/dungeon.rs | 83 ++++++++++++++++++++++++--------- 2 files changed, 62 insertions(+), 25 deletions(-) diff --git a/world/src/site2/gen.rs b/world/src/site2/gen.rs index 7b705248f4..75e707388e 100644 --- a/world/src/site2/gen.rs +++ b/world/src/site2/gen.rs @@ -52,7 +52,7 @@ pub enum Fill { // we probably need an evaluator for the primitive tree that gets which point is queried at // leaf nodes given an input point to make Translate/Rotate work generally Prefab(PrefabStructure, Vec3, u32), - Sampling(Arc) -> Block>), + Sampling(Arc) -> Option>), } impl Fill { @@ -171,7 +171,7 @@ impl Fill { Block::air, ) }), - Fill::Sampling(f) => Some(f(pos)), + Fill::Sampling(f) => f(pos), } } else { None diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index 318519011f..36448dedc0 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -1270,7 +1270,7 @@ impl Floor { )); let floor_sprite_fill = Fill::Sampling(Arc::new(|pos| { - Block::air( + Some(Block::air( match (RandomField::new(1337).get(Vec3::from(pos)) / 2) % 30 { 0 => SpriteKind::Apple, 1 => SpriteKind::VeloriteFrag, @@ -1279,7 +1279,7 @@ impl Floor { 9..=15 => SpriteKind::FireBowlGround, _ => SpriteKind::ShortGrass, }, - ) + )) })); let aabb_edges = |prim: &mut F, aabb: Aabb<_>| { @@ -1308,9 +1308,9 @@ impl Floor { let tunnel_height = if self.final_level { 16.0 } else { 8.0 }; let pillar_thickness: i32 = 4; - let tiles = self.tiles.clone(); - let wall_contours = prim(Primitive::Sampling( - floor_aabb, + let tiles = Arc::new(self.tiles.clone()); + let wall_contours = prim(Primitive::Sampling(floor_aabb, { + let tiles = Arc::clone(&tiles); Box::new(move |pos| { let rpos = pos.xy() - floor_corner; let dist_to_wall = tilegrid_nearest_wall(&tiles, rpos) @@ -1321,11 +1321,32 @@ impl Floor { / (TILE_SIZE as f32 - wall_thickness); dist_to_wall >= wall_thickness && ((pos.z - floor_z) as f32) < tunnel_height * (1.0 - tunnel_dist.powi(4)) - }), - )); + }) + })); + + let sconces_fill = Fill::Sampling(Arc::new(move |pos| { + let rpos = pos.xy() - floor_corner; + let tile_pos = rpos.map(|e| e.div_euclid(TILE_SIZE)); + let tile_center = tile_pos * TILE_SIZE + TILE_SIZE / 2; + let rtile_pos = rpos - tile_center; + let nearest = tilegrid_nearest_wall(&tiles, rpos); + let dist_to_wall = nearest + .map(|nearest| (nearest.distance_squared(rpos) as f32).sqrt()) + .unwrap_or(TILE_SIZE as f32); + let ori = Floor::relative_ori(rpos, nearest.unwrap_or_default()); + let light_offset: i32 = 7; + if (dist_to_wall - wall_thickness) as i32 == 1 + && rtile_pos.map(|e| e % light_offset == 0).reduce_bitxor() + { + Block::air(SpriteKind::WallSconce).with_ori(ori) + } else { + None + } + })); let mut stairs_bb = Vec::new(); let mut stairs = Vec::new(); + let mut pillars = Vec::new(); let mut boss_room_center = None; for (tile_pos, tile) in self.tiles.iter() { @@ -1334,9 +1355,10 @@ impl Floor { min: tile_corner, max: tile_corner + Vec2::broadcast(TILE_SIZE), }; + let tile_center = tile_corner + Vec2::broadcast(TILE_SIZE/2); let (mut height, room) = match tile { Tile::UpStair(room, kind) => { - let center = (tile_corner + Vec2::broadcast(TILE_SIZE) / 2).with_z(floor_z); + let center = tile_center.with_z(floor_z); let radius = TILE_SIZE as f32 / 2.0; let aabb = aabr_with_z(tile_aabr, floor_z..floor_z + self.total_depth()); let bb = prim(match kind { @@ -1387,6 +1409,22 @@ impl Floor { _ => SpriteKind::Chest, })); chests = Some((chest_sprite, chest_sprite_fill)); + + if room + .pillars + .map(|pillar_space| { + tile_pos + .map(|e| e.rem_euclid(pillar_space) == 0) + .reduce_and() + }) + .unwrap_or(false) + { + let pillar = prim(Primitive::Cylinder(Aabb { + min: (tile_center - Vec2::broadcast(pillar_thickness)).with_z(floor_z), + max: (tile_center + Vec2::broadcast(pillar_thickness)).with_z(floor_z + height) + })); + pillars.push((tile_center, pillar)); + } } if room.boss { @@ -1400,6 +1438,8 @@ impl Floor { ))); let tile_air = prim(Primitive::And(tile_air, wall_contours)); fill(tile_air, Fill::Block(vacant)); + let sconces_layer = prim(Primitive::And(tile_air, sprite_layer)); + fill(sconces_layer, sconces_fill.clone()); if let Some((chest_sprite, chest_sprite_fill)) = chests { let chest_sprite = prim(Primitive::And(chest_sprite, wall_contours)); fill(chest_sprite, chest_sprite_fill); @@ -1408,8 +1448,9 @@ impl Floor { aabb_edges(&mut prim, aabr_with_z(tile_aabr, floor_z..floor_z + height)); let floor_sprite = prim(Primitive::And(sprite_layer, floor_sprite)); + fill(floor_sprite, floor_sprite_fill.clone()); - fill(tile_edges, Fill::Block(Block::air(SpriteKind::Lantern))); + //fill(tile_edges, Fill::Block(Block::air(SpriteKind::Lantern))); } if let Some(boss_room_center) = boss_room_center { @@ -1424,26 +1465,22 @@ impl Floor { fill(magic_circle, Fill::Block(stone_purple)); } + for (pos, pillar) in pillars.iter() { + if let Some(boss_room_center) = boss_room_center { + if pos.distance_squared(boss_room_center) < (2 * TILE_SIZE).pow(2) { + continue + } + } + //fill(*pillar, Fill::Block(Block::new(BlockKind::Lava, [0, 0, 0]))); + fill(*pillar, Fill::Block(stone_red)); + } for stair_bb in stairs_bb.iter() { fill(*stair_bb, Fill::Block(vacant)); } for stair in stairs.iter() { fill(*stair, Fill::Block(stone_red)); } - /*let make_staircase = move |kind: &StairsKind, - pos: Vec3, - radius: f32, - inner_radius: f32, - stretch: f32, - height_limit: i32| { - match kind { - StairsKind::Spiral => make_spiral_staircase(pos, radius, inner_radius, stretch), - StairsKind::WallSpiral => { - make_wall_staircase(pos, radius, stretch * 3.0, height_limit) - }, - } - }; - + /* move |z| match self.tiles.get(tile_pos) { Some(Tile::Solid) => BlockMask::nothing(), Some(Tile::Tunnel) => { From 879aafad5cc63ab951ac2d0188a8d056dbeac9aa Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 9 Jun 2021 21:04:19 -0500 Subject: [PATCH 154/180] 3 attacks done --- .../common/abilities/ability_set_manifest.ron | 8 ++ .../custom/birdlargebreathe/flamethrower.ron | 7 +- .../custom/birdlargefire/flamethrower.ron | 7 +- .../custom/harvester/ensnaringvines.ron | 15 ++++ .../custom/harvester/explodingpumpkin.ron | 15 ++++ .../abilities/custom/harvester/firebreath.ron | 19 +++++ .../abilities/custom/harvester/scythe.ron | 13 ++++ .../custom/quadlowbreathe/flamethrower.ron | 7 +- .../abilities/staffsimple/flamethrower.ron | 7 +- .../npc_weapons/hammer/harvester_scythe.ron | 6 +- common/src/comp/projectile.rs | 73 ++++++++++++++++++- 11 files changed, 167 insertions(+), 10 deletions(-) create mode 100644 assets/common/abilities/custom/harvester/ensnaringvines.ron create mode 100644 assets/common/abilities/custom/harvester/explodingpumpkin.ron create mode 100644 assets/common/abilities/custom/harvester/firebreath.ron create mode 100644 assets/common/abilities/custom/harvester/scythe.ron diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index 12eb751aad..cdbfd1ac58 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -257,6 +257,14 @@ (None, "common.abilities.custom.yeti.snowball"), ], ), + Custom("Harvester"): ( + primary: "common.abilities.custom.harvester.scythe", + secondary: "common.abilities.custom.harvester.firebreath", + abilities: [ + (None, "common.abilities.custom.harvester.ensnaringvines"), + (None, "common.abilities.custom.harvester.explodingpumpkin"), + ], + ), Custom("Bird Large Breathe"): ( primary: "common.abilities.custom.birdlargebreathe.firebomb", secondary: "common.abilities.custom.birdlargebreathe.triplestrike", diff --git a/assets/common/abilities/custom/birdlargebreathe/flamethrower.ron b/assets/common/abilities/custom/birdlargebreathe/flamethrower.ron index 1cb6d4fdf8..705bfc0247 100644 --- a/assets/common/abilities/custom/birdlargebreathe/flamethrower.ron +++ b/assets/common/abilities/custom/birdlargebreathe/flamethrower.ron @@ -6,7 +6,12 @@ BasicBeam( tick_rate: 3.0, range: 15.0, max_angle: 22.5, - damage_effect: None, + damage_effect: Some(Buff(( + kind: Burning, + dur_secs: 10.0, + strength: DamageFraction(0.5), + chance: 0.25, + ))), energy_regen: 0, energy_drain: 0, orientation_behavior: Normal, diff --git a/assets/common/abilities/custom/birdlargefire/flamethrower.ron b/assets/common/abilities/custom/birdlargefire/flamethrower.ron index 1cb6d4fdf8..705bfc0247 100644 --- a/assets/common/abilities/custom/birdlargefire/flamethrower.ron +++ b/assets/common/abilities/custom/birdlargefire/flamethrower.ron @@ -6,7 +6,12 @@ BasicBeam( tick_rate: 3.0, range: 15.0, max_angle: 22.5, - damage_effect: None, + damage_effect: Some(Buff(( + kind: Burning, + dur_secs: 10.0, + strength: DamageFraction(0.5), + chance: 0.25, + ))), energy_regen: 0, energy_drain: 0, orientation_behavior: Normal, diff --git a/assets/common/abilities/custom/harvester/ensnaringvines.ron b/assets/common/abilities/custom/harvester/ensnaringvines.ron new file mode 100644 index 0000000000..88b2b2d48a --- /dev/null +++ b/assets/common/abilities/custom/harvester/ensnaringvines.ron @@ -0,0 +1,15 @@ +BasicRanged( + energy_cost: 0, + buildup_duration: 0.5, + recover_duration: 0.8, + projectile: ExplodingPumpkin( + damage: 500.0, + knockback: 25.0, + radius: 10.0, + ), + projectile_body: Object(Pumpkin), + projectile_light: None, + projectile_speed: 30.0, + num_projectiles: 1, + projectile_spread: 0.0, +) diff --git a/assets/common/abilities/custom/harvester/explodingpumpkin.ron b/assets/common/abilities/custom/harvester/explodingpumpkin.ron new file mode 100644 index 0000000000..88b2b2d48a --- /dev/null +++ b/assets/common/abilities/custom/harvester/explodingpumpkin.ron @@ -0,0 +1,15 @@ +BasicRanged( + energy_cost: 0, + buildup_duration: 0.5, + recover_duration: 0.8, + projectile: ExplodingPumpkin( + damage: 500.0, + knockback: 25.0, + radius: 10.0, + ), + projectile_body: Object(Pumpkin), + projectile_light: None, + projectile_speed: 30.0, + num_projectiles: 1, + projectile_spread: 0.0, +) diff --git a/assets/common/abilities/custom/harvester/firebreath.ron b/assets/common/abilities/custom/harvester/firebreath.ron new file mode 100644 index 0000000000..e7a23ad6de --- /dev/null +++ b/assets/common/abilities/custom/harvester/firebreath.ron @@ -0,0 +1,19 @@ +BasicBeam( + buildup_duration: 0.5, + recover_duration: 0.5, + beam_duration: 1.0, + damage: 35, + tick_rate: 3.0, + range: 20.0, + max_angle: 15.0, + damage_effect: Some(Buff(( + kind: Burning, + dur_secs: 10.0, + strength: DamageFraction(0.5), + chance: 0.25, + ))), + energy_regen: 0, + energy_drain: 0, + orientation_behavior: Normal, + specifier: Flamethrower, +) diff --git a/assets/common/abilities/custom/harvester/scythe.ron b/assets/common/abilities/custom/harvester/scythe.ron new file mode 100644 index 0000000000..ac8f1cef3e --- /dev/null +++ b/assets/common/abilities/custom/harvester/scythe.ron @@ -0,0 +1,13 @@ +BasicMelee( + energy_cost: 0, + buildup_duration: 0.6, + swing_duration: 0.1, + recover_duration: 0.5, + base_damage: 60, + base_poise_damage: 10, + knockback: ( strength: 10.0, direction: Away), + range: 4.0, + max_angle: 60.0, + damage_effect: None, + damage_kind: Slashing, +) diff --git a/assets/common/abilities/custom/quadlowbreathe/flamethrower.ron b/assets/common/abilities/custom/quadlowbreathe/flamethrower.ron index 5eba0f6226..7b376b28b5 100644 --- a/assets/common/abilities/custom/quadlowbreathe/flamethrower.ron +++ b/assets/common/abilities/custom/quadlowbreathe/flamethrower.ron @@ -6,7 +6,12 @@ BasicBeam( tick_rate: 3.0, range: 15.0, max_angle: 22.5, - damage_effect: None, + damage_effect: Some(Buff(( + kind: Burning, + dur_secs: 10.0, + strength: DamageFraction(0.5), + chance: 0.25, + ))), energy_regen: 0, energy_drain: 0, orientation_behavior: Normal, diff --git a/assets/common/abilities/staffsimple/flamethrower.ron b/assets/common/abilities/staffsimple/flamethrower.ron index 14eead7fa6..82cedf954f 100644 --- a/assets/common/abilities/staffsimple/flamethrower.ron +++ b/assets/common/abilities/staffsimple/flamethrower.ron @@ -6,7 +6,12 @@ BasicBeam( tick_rate: 3.0, range: 20.0, max_angle: 15.0, - damage_effect: None, + damage_effect: Some(Buff(( + kind: Burning, + dur_secs: 10.0, + strength: DamageFraction(0.5), + chance: 0.25, + ))), energy_regen: 0, energy_drain: 350, orientation_behavior: Normal, diff --git a/assets/common/items/npc_weapons/hammer/harvester_scythe.ron b/assets/common/items/npc_weapons/hammer/harvester_scythe.ron index 7156218a30..8ac2bd7abe 100644 --- a/assets/common/items/npc_weapons/hammer/harvester_scythe.ron +++ b/assets/common/items/npc_weapons/hammer/harvester_scythe.ron @@ -2,11 +2,11 @@ ItemDef( name: "Harvester Sythe", description: "Placeholder", kind: Tool(( - kind: Hammer, + kind: Natural, hands: Two, stats: Direct(( equip_time_secs: 0.001, - power: 1.6, + power: 1.0, poise_strength: 1.0, speed: 1.0, crit_chance: 0.05078125, @@ -14,5 +14,5 @@ ItemDef( )), quality: Low, tags: [], - ability_spec: Some(Custom("Hammer Simple")), + ability_spec: Some(Custom("Harvester")), ) \ No newline at end of file diff --git a/common/src/comp/projectile.rs b/common/src/comp/projectile.rs index a3e716e1be..68ccee18a6 100644 --- a/common/src/comp/projectile.rs +++ b/common/src/comp/projectile.rs @@ -1,9 +1,9 @@ use crate::{ combat::{ - Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement, Damage, - DamageKind, DamageSource, GroupTarget, Knockback, KnockbackDir, + Attack, AttackDamage, AttackEffect, CombatBuff, CombatBuffStrength, CombatEffect, + CombatRequirement, Damage, DamageKind, DamageSource, GroupTarget, Knockback, KnockbackDir, }, - comp::item::Reagent, + comp::{buff::BuffKind, item::Reagent}, uid::Uid, Explosion, RadiusEffect, }; @@ -72,6 +72,11 @@ pub enum ProjectileConstructor { damage: f32, radius: f32, }, + ExplodingPumpkin { + damage: f32, + radius: f32, + knockback: f32, + }, } impl ProjectileConstructor { @@ -298,6 +303,60 @@ impl ProjectileConstructor { is_point: false, } }, + ExplodingPumpkin { + damage, + radius, + knockback, + } => { + let knockback = AttackEffect::new( + Some(GroupTarget::OutOfGroup), + CombatEffect::Knockback(Knockback { + strength: knockback, + direction: KnockbackDir::Away, + }), + ) + .with_requirement(CombatRequirement::AnyDamage); + let buff = AttackEffect::new( + Some(GroupTarget::OutOfGroup), + CombatEffect::Buff(CombatBuff { + kind: BuffKind::Burning, + dur_secs: 5.0, + strength: CombatBuffStrength::DamageFraction(0.2), + chance: 1.0, + }), + ) + .with_requirement(CombatRequirement::AnyDamage); + let damage = AttackDamage::new( + Damage { + source: DamageSource::Explosion, + kind: DamageKind::Energy, + value: damage, + }, + Some(GroupTarget::OutOfGroup), + ); + let attack = Attack::default() + .with_damage(damage) + .with_crit(crit_chance, crit_mult) + .with_effect(knockback) + .with_effect(buff); + let explosion = Explosion { + effects: vec![ + RadiusEffect::Attack(attack), + RadiusEffect::TerrainDestruction(5.0), + ], + radius, + reagent: Some(Reagent::Red), + }; + Projectile { + hit_solid: vec![Effect::Explode(explosion.clone()), Effect::Vanish], + hit_entity: vec![Effect::Explode(explosion), Effect::Vanish], + time_left: Duration::from_secs(10), + owner, + ignore_group: true, + is_sticky: true, + is_point: true, + } + }, } } @@ -354,6 +413,14 @@ impl ProjectileConstructor { *damage *= power; *radius *= range; }, + ExplodingPumpkin { + ref mut damage, + ref mut radius, + .. + } => { + *damage *= power; + *radius *= range; + }, } self } From 15a2fbc5551d59c3bac965452b37dd1f6bc6f170 Mon Sep 17 00:00:00 2001 From: Snowram Date: Sun, 13 Jun 2021 02:35:32 +0200 Subject: [PATCH 155/180] 3 attack anims done --- common/src/comp/body.rs | 1 + voxygen/anim/src/biped_large/alpha.rs | 57 +++++++++++++++++++ voxygen/anim/src/biped_large/beam.rs | 63 +++++++++++++++++++++ voxygen/anim/src/biped_large/shoot.rs | 81 +++++++++++++++++++++++++++ voxygen/anim/src/biped_large/wield.rs | 28 +++++++++ 5 files changed, 230 insertions(+) diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index df1500f996..6c999c3635 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -305,6 +305,7 @@ impl Body { biped_large::Species::Occultsaurok => Vec3::new(4.0, 3.0, 3.4), biped_large::Species::Slysaurok => Vec3::new(4.0, 3.0, 3.4), biped_large::Species::Werewolf => Vec3::new(4.0, 3.0, 3.5), + biped_large::Species::Harvester => Vec3::new(4.6, 3.0, 5.4), _ => Vec3::new(4.6, 3.0, 6.0), }, diff --git a/voxygen/anim/src/biped_large/alpha.rs b/voxygen/anim/src/biped_large/alpha.rs index 583f64aa63..0d3e7c61aa 100644 --- a/voxygen/anim/src/biped_large/alpha.rs +++ b/voxygen/anim/src/biped_large/alpha.rs @@ -327,6 +327,63 @@ impl Animation for AlphaAnimation { * Quaternion::rotation_y(-1.8 + move1 * -0.8 + move2 * 3.0) * Quaternion::rotation_z(move1 * -0.8 + move2 * -0.8); }, + "Harvester" => { + next.head.orientation = + Quaternion::rotation_x(move1 * -0.25 + move2 * 0.25) + * Quaternion::rotation_z(move1 * -0.3 + move2 * 0.4); + + next.jaw.position = Vec3::new(0.0, s_a.jaw.0, s_a.jaw.1 + move2 * -0.5); + next.jaw.orientation = Quaternion::rotation_x(move2 * -0.15); + + next.upper_torso.orientation = + Quaternion::rotation_x(move1 * 0.2 + move2 * -0.2) + * Quaternion::rotation_z(move1 * -1.0 + move2 * 1.0); + + next.shoulder_l.position = Vec3::new( + -s_a.shoulder.0, + s_a.shoulder.1, + s_a.shoulder.2 - foothorir * 1.0, + ); + next.shoulder_l.orientation = + Quaternion::rotation_x(-0.4 + move1 * 1.0 + move2 * -1.0) + * Quaternion::rotation_y(move1 * -0.2); + next.shoulder_r.orientation = + Quaternion::rotation_y(0.4 + move1 * -0.8 + move2 * 0.8) + * Quaternion::rotation_x(0.4 + move1 * -0.4 + move2 * 0.8); + + if speed == 0.0 { + next.leg_l.orientation = + Quaternion::rotation_x(move1 * 0.4 + move2 * -0.4); + + next.foot_l.position = Vec3::new( + -s_a.foot.0, + s_a.foot.1, + s_a.foot.2 + move1 * 2.0 + move2 * -2.0, + ); + next.foot_l.orientation = + Quaternion::rotation_x(move1 * -0.6 + move2 * 0.6); + } + + next.control_l.position = Vec3::new(1.0, 2.0, 8.0); + next.control_r.position = Vec3::new(1.0, 1.0, -2.0); + + next.control.position = Vec3::new( + -7.0 + move1 * 26.0 - move2 * 32.0, + 0.0 + s_a.grip.0 / 1.0 - move1 * 4.0, + -s_a.grip.0 / 0.8, + ); + + next.control_l.orientation = Quaternion::rotation_x(PI / 2.0) + * Quaternion::rotation_y(-0.0) + * Quaternion::rotation_z(PI); + next.control_r.orientation = Quaternion::rotation_x(PI / 2.0 + 0.2) + * Quaternion::rotation_y(-1.0 + move1 * 1.0) + * Quaternion::rotation_z(0.0); + + next.control.orientation = Quaternion::rotation_x(-1.4 + move1 * -0.4) + * Quaternion::rotation_y(-2.8 + move1 * 3.0 + move2 * -3.0) + * Quaternion::rotation_z(move1 * -1.5); + }, _ => {}, } } diff --git a/voxygen/anim/src/biped_large/beam.rs b/voxygen/anim/src/biped_large/beam.rs index 62f8e01481..91185538ed 100644 --- a/voxygen/anim/src/biped_large/beam.rs +++ b/voxygen/anim/src/biped_large/beam.rs @@ -215,6 +215,69 @@ impl Animation for BeamAnimation { move1 * -0.8 + move2 * 1.1 + move2shake * 0.02, ); }, + "Harvester" => { + next.head.orientation = Quaternion::rotation_x( + move1 * 0.5 + move2 * -0.4 + move2shake * -0.02, + ); + + next.jaw.position = Vec3::new(0.0, s_a.jaw.0, s_a.jaw.1); + next.jaw.orientation = + Quaternion::rotation_x(move2 * -0.5 + move2shake * -0.1); + + next.upper_torso.position = Vec3::new( + 0.0, + s_a.upper_torso.0 + move1 * -3.0 + move2 * 3.0, + s_a.upper_torso.1 + move1 * -0.4, + ); + next.upper_torso.orientation = Quaternion::rotation_x( + move1 * 0.8 + move2 * -1.1 + move2shake * -0.02, + ); + next.lower_torso.position = + Vec3::new(0.0, s_a.lower_torso.0, s_a.lower_torso.1); + next.lower_torso.orientation = Quaternion::rotation_x( + move1 * -0.8 + move2 * 1.1 + move2shake * 0.02, + ); + + next.control_l.position = Vec3::new(1.0, 2.0, 8.0); + next.control_r.position = Vec3::new(1.0, 1.0, -2.0); + + next.control.position = + Vec3::new(-6.0, 0.0 + s_a.grip.0 / 1.0, -s_a.grip.0 / 0.8); + + next.control_l.orientation = + Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_z(PI); + next.control_r.orientation = Quaternion::rotation_x(PI / 2.0 + 0.2) + * Quaternion::rotation_y(-1.0); + + next.control.orientation = + Quaternion::rotation_x(-1.4) * Quaternion::rotation_y(-2.8); + + next.weapon_l.position = + Vec3::new(move1 * 8.0, move1 * 1.0, move1 * 6.0); + next.weapon_l.orientation = Quaternion::rotation_x(move1 * 0.5) + * Quaternion::rotation_y(move1 * -0.8); + + next.shoulder_l.position = Vec3::new( + -s_a.shoulder.0, + s_a.shoulder.1, + s_a.shoulder.2 - foothorir * 1.0, + ); + next.shoulder_l.orientation = + Quaternion::rotation_y(-0.4 + move1 * 0.8) + * Quaternion::rotation_x(-0.4 + move1 * -0.2); + next.shoulder_r.orientation = + Quaternion::rotation_y(0.4 + move1 * -0.8) + * Quaternion::rotation_x(0.4 + move1 * -0.8); + + next.hand_r.position = Vec3::new( + -s_a.grip.1 + move1 * -5.0, + 0.0 + move1 * 6.0, + s_a.grip.0 + move1 * 13.0, + ); + next.hand_r.orientation = Quaternion::rotation_x(move1 * -3.0) + * Quaternion::rotation_y(move1 * 1.5) + * Quaternion::rotation_z(move1 * -1.5); + }, _ => {}, } } diff --git a/voxygen/anim/src/biped_large/shoot.rs b/voxygen/anim/src/biped_large/shoot.rs index 206d2ad2bd..c5fba1b268 100644 --- a/voxygen/anim/src/biped_large/shoot.rs +++ b/voxygen/anim/src/biped_large/shoot.rs @@ -262,6 +262,87 @@ impl Animation for ShootAnimation { next.arm_control_r.position = Vec3::new(0.0, move1 * 10.0 + move2 * -10.0, 0.0); }, + "Harvester" => { + let (move1, move2, move3) = match stage_section { + Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), + Some(StageSection::Swing) => (1.0, anim_time, 0.0), + Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)), + _ => (0.0, 0.0, 0.0), + }; + next.control_l.position = Vec3::new(1.0, 2.0, 8.0); + next.control_r.position = Vec3::new(1.0, 1.0, -2.0); + + next.control.position = + Vec3::new(-7.0, 0.0 + s_a.grip.0 / 1.0, -s_a.grip.0 / 0.8); + + next.control_l.orientation = + Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_z(PI); + next.control_r.orientation = Quaternion::rotation_x(PI / 2.0 + 0.2) + * Quaternion::rotation_y(-1.0) + * Quaternion::rotation_z(0.0); + + next.control.orientation = + Quaternion::rotation_x(-1.4) * Quaternion::rotation_y(-2.8); + + next.head.orientation = Quaternion::rotation_x(move1 * 0.2); + next.jaw.position = Vec3::new(0.0, s_a.jaw.0, s_a.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(move2 * -0.3); + + let twist = move1 * 0.8 + move3 * -0.8; + next.upper_torso.position = Vec3::new( + 0.0, + s_a.upper_torso.0, + s_a.upper_torso.1 + move1 * 1.0 + move2 * -1.0, + ); + next.upper_torso.orientation = + Quaternion::rotation_x(move1 * 0.8 + move2 * -1.1) + * Quaternion::rotation_z( + twist * -0.2 + move1 * -0.1 + move2 * 0.3, + ); + + next.lower_torso.orientation = + Quaternion::rotation_x(move1 * -0.8 + move2 * 1.1) + * Quaternion::rotation_z(-twist + move1 * 0.4); + + next.shoulder_l.position = Vec3::new( + -s_a.shoulder.0, + s_a.shoulder.1, + s_a.shoulder.2 - foothorir * 1.0, + ); + next.shoulder_l.orientation = Quaternion::rotation_x(-0.4); + + next.shoulder_r.position = Vec3::new( + s_a.shoulder.0 + move2 * -2.0, + s_a.shoulder.1, + s_a.shoulder.2, + ); + next.shoulder_r.orientation = Quaternion::rotation_y(move1 * -PI / 2.0) + * Quaternion::rotation_x(move2 * 2.0) + * Quaternion::rotation_z(move1 * -PI / 2.0); + + next.hand_r.position = Vec3::new( + -s_a.grip.1 + move1 * -2.0 + move2 * 8.0, + 0.0 + move1 * 6.0, + s_a.grip.0 + move1 * 18.0 + move2 * -19.0, + ); + next.hand_r.orientation = + Quaternion::rotation_x(move1 * -3.0 + move2 * 3.0) + * Quaternion::rotation_y(move1 * 0.5 + move2 * -1.5) + * Quaternion::rotation_z(move1 * -1.5); + + if speed == 0.0 { + next.leg_l.orientation = + Quaternion::rotation_x(move1 * 0.8 + move2 * -0.8); + + next.foot_l.position = Vec3::new( + -s_a.foot.0, + s_a.foot.1, + s_a.foot.2 + move1 * 4.0 + move2 * -4.0, + ); + next.foot_l.orientation = + Quaternion::rotation_x(move1 * -0.6 + move2 * 0.6); + } + }, _ => {}, } } diff --git a/voxygen/anim/src/biped_large/wield.rs b/voxygen/anim/src/biped_large/wield.rs index 9035c2d468..c3d985fd71 100644 --- a/voxygen/anim/src/biped_large/wield.rs +++ b/voxygen/anim/src/biped_large/wield.rs @@ -470,6 +470,34 @@ impl Animation for WieldAnimation { next.shoulder_r.orientation = Quaternion::rotation_x(-0.3); }, + "Harvester" => { + next.control_l.position = Vec3::new(1.0, 2.0, 8.0); + next.control_r.position = Vec3::new(1.0, 1.0, -2.0); + + next.control.position = Vec3::new( + -7.0, + 0.0 + s_a.grip.0 / 1.0, + -s_a.grip.0 / 0.8 + short * -1.5, + ); + + next.control_l.orientation = + Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_z(PI); + next.control_r.orientation = Quaternion::rotation_x(PI / 2.0 + 0.2) + * Quaternion::rotation_y(-1.0) + * Quaternion::rotation_z(0.0); + + next.control.orientation = + Quaternion::rotation_x(-1.4) * Quaternion::rotation_y(-2.8); + + next.shoulder_l.position = Vec3::new( + -s_a.shoulder.0, + s_a.shoulder.1, + s_a.shoulder.2 - foothorir * 1.0, + ); + next.shoulder_l.orientation = Quaternion::rotation_x(-0.4); + next.shoulder_r.orientation = + Quaternion::rotation_y(0.4) * Quaternion::rotation_x(0.4); + }, _ => {}, } } From ed503236d6ca3216cde2c626c25592ab0395708f Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 19 Jun 2021 13:53:23 -0500 Subject: [PATCH 156/180] Slightly functional sprite summon. --- .../custom/harvester/ensnaringvines.ron | 22 +-- .../voxel/sprite/misc/ensnaring_vines.vox | 3 + assets/voxygen/voxel/sprite_manifest.ron | 10 ++ common/src/comp/ability.rs | 47 +++++- common/src/comp/character_state.rs | 8 + common/src/event.rs | 5 + common/src/spiral.rs | 10 ++ common/src/states/mod.rs | 1 + common/src/states/sprite_summon.rs | 156 ++++++++++++++++++ common/src/terrain/sprite.rs | 2 + common/systems/src/character_behavior.rs | 2 + common/systems/src/stats.rs | 3 +- server/src/events/interaction.rs | 15 ++ server/src/events/mod.rs | 7 +- 14 files changed, 271 insertions(+), 20 deletions(-) create mode 100644 assets/voxygen/voxel/sprite/misc/ensnaring_vines.vox create mode 100644 common/src/states/sprite_summon.rs diff --git a/assets/common/abilities/custom/harvester/ensnaringvines.ron b/assets/common/abilities/custom/harvester/ensnaringvines.ron index 88b2b2d48a..ede5f831dc 100644 --- a/assets/common/abilities/custom/harvester/ensnaringvines.ron +++ b/assets/common/abilities/custom/harvester/ensnaringvines.ron @@ -1,15 +1,7 @@ -BasicRanged( - energy_cost: 0, - buildup_duration: 0.5, - recover_duration: 0.8, - projectile: ExplodingPumpkin( - damage: 500.0, - knockback: 25.0, - radius: 10.0, - ), - projectile_body: Object(Pumpkin), - projectile_light: None, - projectile_speed: 30.0, - num_projectiles: 1, - projectile_spread: 0.0, -) +SpriteSummon( + buildup_duration: 0.3, + cast_duration: 1.0, + recover_duration: 0.2, + sprite: EnsnaringVines, + summon_distance: (0, 25), +) \ No newline at end of file diff --git a/assets/voxygen/voxel/sprite/misc/ensnaring_vines.vox b/assets/voxygen/voxel/sprite/misc/ensnaring_vines.vox new file mode 100644 index 0000000000..2bf31ebd2d --- /dev/null +++ b/assets/voxygen/voxel/sprite/misc/ensnaring_vines.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:35a24b6eefb92f621b565fc14a7d502e22cecc416ff45b9856c621c39abf17ec +size 1480 diff --git a/assets/voxygen/voxel/sprite_manifest.ron b/assets/voxygen/voxel/sprite_manifest.ron index 8ca8d3bbb2..36e7be1102 100644 --- a/assets/voxygen/voxel/sprite_manifest.ron +++ b/assets/voxygen/voxel/sprite_manifest.ron @@ -3247,4 +3247,14 @@ CookingPot: Some(( ], wind_sway: 0.0, )), +EnsnaringVines: Some(( + variations: [ + ( + model: "voxygen.voxel.sprite.misc.ensnaring_vines", + offset: (-5.0, -6.5, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ], + wind_sway: 0.0, +)), ) diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 4d5c4aaf50..9931ac54c6 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -10,6 +10,7 @@ use crate::{ utils::{AbilityInfo, StageSection}, *, }, + terrain::SpriteKind, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -294,6 +295,13 @@ pub enum CharacterAbility { buff_duration: Option, energy_cost: f32, }, + SpriteSummon { + buildup_duration: f32, + cast_duration: f32, + recover_duration: f32, + sprite: SpriteKind, + summon_distance: (f32, f32), + }, } impl Default for CharacterAbility { @@ -365,7 +373,8 @@ impl CharacterAbility { | CharacterAbility::Boost { .. } | CharacterAbility::BasicBeam { .. } | CharacterAbility::Blink { .. } - | CharacterAbility::BasicSummon { .. } => true, + | CharacterAbility::BasicSummon { .. } + | CharacterAbility::SpriteSummon { .. } => true, } } @@ -627,6 +636,17 @@ impl CharacterAbility { *cast_duration /= speed; *recover_duration /= speed; }, + SpriteSummon { + ref mut buildup_duration, + ref mut cast_duration, + ref mut recover_duration, + .. + } => { + // TODO: Figure out how/if power should affect this + *buildup_duration /= speed; + *cast_duration /= speed; + *recover_duration /= speed; + }, } self } @@ -655,7 +675,11 @@ impl CharacterAbility { 0 } }, - Boost { .. } | ComboMelee { .. } | Blink { .. } | BasicSummon { .. } => 0, + Boost { .. } + | ComboMelee { .. } + | Blink { .. } + | BasicSummon { .. } + | SpriteSummon { .. } => 0, } } @@ -1781,6 +1805,25 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { timer: Duration::default(), stage_section: StageSection::Buildup, }), + CharacterAbility::SpriteSummon { + buildup_duration, + cast_duration, + recover_duration, + sprite, + summon_distance, + } => CharacterState::SpriteSummon(sprite_summon::Data { + static_data: sprite_summon::StaticData { + buildup_duration: Duration::from_secs_f32(*buildup_duration), + cast_duration: Duration::from_secs_f32(*cast_duration), + recover_duration: Duration::from_secs_f32(*recover_duration), + sprite: *sprite, + summon_distance: *summon_distance, + ability_info, + }, + timer: Duration::default(), + stage_section: StageSection::Buildup, + achieved_radius: 0, + }), } } } diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 86db71191b..87102015bb 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -103,6 +103,8 @@ pub enum CharacterState { BasicSummon(basic_summon::Data), /// Inserts a buff on the caster SelfBuff(self_buff::Data), + /// Creates sprites around the caster + SpriteSummon(sprite_summon::Data), } impl CharacterState { @@ -125,6 +127,9 @@ impl CharacterState { | CharacterState::BasicAura(_) | CharacterState::HealingBeam(_) | CharacterState::SelfBuff(_) + | CharacterState::Blink(_) + | CharacterState::BasicSummon(_) + | CharacterState::SpriteSummon(_) ) } @@ -149,6 +154,9 @@ impl CharacterState { | CharacterState::BasicAura(_) | CharacterState::HealingBeam(_) | CharacterState::SelfBuff(_) + | CharacterState::Blink(_) + | CharacterState::BasicSummon(_) + | CharacterState::SpriteSummon(_) ) } diff --git a/common/src/event.rs b/common/src/event.rs index 1056da4716..617726765c 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -9,6 +9,7 @@ use crate::{ }, outcome::Outcome, rtsim::RtSimEntity, + terrain::SpriteKind, trade::{TradeAction, TradeId}, uid::Uid, util::Dir, @@ -181,6 +182,10 @@ pub enum ServerEvent { Sound { sound: Sound, }, + CreateSprite { + pos: Vec3, + sprite: SpriteKind, + }, } pub struct EventBus { diff --git a/common/src/spiral.rs b/common/src/spiral.rs index 7b0cb2da51..fc763a8278 100644 --- a/common/src/spiral.rs +++ b/common/src/spiral.rs @@ -10,12 +10,22 @@ pub struct Spiral2d { impl Spiral2d { #[allow(clippy::new_without_default)] // TODO: Pending review in #587 + /// Creates a new spiral starting at the origin pub fn new() -> Self { Self { layer: 0, i: 0 } } + /// Creates an iterator over points in a spiral starting at the origin and + /// going out to some radius pub fn radius(self, radius: i32) -> impl Iterator> { self.take((radius * 2 + 1).pow(2) as usize) .filter(move |pos| pos.magnitude_squared() < (radius + 1).pow(2)) } + + /// Creates an iterator over points in the edge of a circle of some radius + pub fn edge_radius(self, radius: i32) -> impl Iterator> { + self.take((radius * 2 + 1).pow(2) as usize) + .filter(move |pos| pos.magnitude_squared() < (radius + 1).pow(2)) + .filter(move |pos| pos.magnitude_squared() >= radius.pow(2)) + } } impl Iterator for Spiral2d { diff --git a/common/src/states/mod.rs b/common/src/states/mod.rs index 30b86e3766..0e63dd48d4 100644 --- a/common/src/states/mod.rs +++ b/common/src/states/mod.rs @@ -26,6 +26,7 @@ pub mod shockwave; pub mod sit; pub mod sneak; pub mod spin_melee; +pub mod sprite_summon; pub mod stunned; pub mod talk; pub mod utils; diff --git a/common/src/states/sprite_summon.rs b/common/src/states/sprite_summon.rs new file mode 100644 index 0000000000..76ec4f0062 --- /dev/null +++ b/common/src/states/sprite_summon.rs @@ -0,0 +1,156 @@ +use crate::{ + comp::{CharacterState, StateUpdate}, + event::ServerEvent, + spiral::Spiral2d, + states::{ + behavior::{CharacterBehavior, JoinData}, + utils::*, + }, + terrain::{Block, SpriteKind}, + vol::ReadVol, +}; +use serde::{Deserialize, Serialize}; +use std::time::Duration; +use vek::*; + +/// Separated out to condense update portions of character state +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct StaticData { + /// How long the state builds up for + pub buildup_duration: Duration, + /// How long the state is casting for + pub cast_duration: Duration, + /// How long the state recovers for + pub recover_duration: Duration, + /// What kind of sprite is created by this state + pub sprite: SpriteKind, + /// Range that sprites are created relative to the summonner + pub summon_distance: (f32, f32), + /// Miscellaneous information about the ability + pub ability_info: AbilityInfo, +} + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Data { + /// Struct containing data that does not change over the course of the + /// character state + pub static_data: StaticData, + /// Timer for each stage + pub timer: Duration, + /// What section the character stage is in + pub stage_section: StageSection, + /// What radius of sprites have already been summoned + pub achieved_radius: i32, +} + +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + let mut update = StateUpdate::from(data); + + match self.stage_section { + StageSection::Buildup => { + if self.timer < self.static_data.buildup_duration { + // Build up + update.character = CharacterState::SpriteSummon(Data { + timer: tick_attack_or_default(data, self.timer, None), + ..*self + }); + } else { + // Transitions to recover section of stage + update.character = CharacterState::SpriteSummon(Data { + timer: Duration::default(), + stage_section: StageSection::Cast, + ..*self + }); + } + }, + StageSection::Cast => { + if self.timer < self.static_data.cast_duration { + let timer_frac = + self.timer.as_secs_f32() / self.static_data.cast_duration.as_secs_f32(); + + // Determines distance from summoner sprites should be created. Goes outward + // with time. + let summon_distance = timer_frac + * (self.static_data.summon_distance.1 - self.static_data.summon_distance.0) + + self.static_data.summon_distance.0; + let summon_distance = summon_distance.round() as i32; + + // Only summons sprites if summon distance is greater than achieved radius + if summon_distance > self.achieved_radius { + // Creates a spiral iterator for the newly achieved radius + let spiral = Spiral2d::new().edge_radius(summon_distance); + for point in spiral { + // The coordinates of where the sprite is created + let sprite_pos = Vec3::new( + data.pos.0.x.floor() as i32 + point.x, + data.pos.0.y.floor() as i32 + point.y, + data.pos.0.z.floor() as i32, + ); + + // Check for collision in z up to 25 blocks up or down + let obstacle_z = data + .terrain + .ray( + sprite_pos.map(|x| x as f32 + 0.5) + Vec3::unit_z() * 25.0, + sprite_pos.map(|x| x as f32 + 0.5) - Vec3::unit_z() * 25.0, + ) + .until(|b| { + Block::is_solid(b) + && !matches!( + b.get_sprite(), + Some(SpriteKind::EnsnaringVines) + ) + }) + .cast() + .0; + + // z height relative to caster + let z = sprite_pos.z + (25.5 - obstacle_z).ceil() as i32; + + // Location sprite will be created + let sprite_pos = Vec3::new(sprite_pos.x as i32, sprite_pos.y as i32, z); + + // Send server event to create sprite + update.server_events.push_front(ServerEvent::CreateSprite { + pos: sprite_pos, + sprite: self.static_data.sprite, + }); + } + } + + update.character = CharacterState::SpriteSummon(Data { + timer: tick_attack_or_default(data, self.timer, None), + achieved_radius: summon_distance, + ..*self + }); + } else { + // Transitions to recover section of stage + update.character = CharacterState::SpriteSummon(Data { + timer: Duration::default(), + stage_section: StageSection::Recover, + ..*self + }); + } + }, + StageSection::Recover => { + if self.timer < self.static_data.recover_duration { + // Recovery + update.character = CharacterState::SpriteSummon(Data { + timer: tick_attack_or_default(data, self.timer, None), + ..*self + }); + } else { + // Done + update.character = CharacterState::Wielding; + } + }, + _ => { + // If it somehow ends up in an incorrect stage section + update.character = CharacterState::Wielding; + }, + } + + update + } +} diff --git a/common/src/terrain/sprite.rs b/common/src/terrain/sprite.rs index e35a99035f..021efcce82 100644 --- a/common/src/terrain/sprite.rs +++ b/common/src/terrain/sprite.rs @@ -173,6 +173,7 @@ make_case_elim!( CrystalLow = 0x92, CeilingMushroom = 0x93, Orb = 0x94, + EnsnaringVines = 0x95, } ); @@ -256,6 +257,7 @@ impl SpriteKind { | SpriteKind::Tin | SpriteKind::Silver | SpriteKind::Gold => 0.6, + SpriteKind::EnsnaringVines => 0.1, _ => return None, }) } diff --git a/common/systems/src/character_behavior.rs b/common/systems/src/character_behavior.rs index 382067f7d7..1782ef693f 100644 --- a/common/systems/src/character_behavior.rs +++ b/common/systems/src/character_behavior.rs @@ -331,6 +331,7 @@ impl<'a> System<'a> for Sys { CharacterState::Blink(data) => data.handle_event(&j, action), CharacterState::BasicSummon(data) => data.handle_event(&j, action), CharacterState::SelfBuff(data) => data.handle_event(&j, action), + CharacterState::SpriteSummon(data) => data.handle_event(&j, action), }; local_emitter.append(&mut state_update.local_events); server_emitter.append(&mut state_update.server_events); @@ -386,6 +387,7 @@ impl<'a> System<'a> for Sys { CharacterState::Blink(data) => data.behavior(&j), CharacterState::BasicSummon(data) => data.behavior(&j), CharacterState::SelfBuff(data) => data.behavior(&j), + CharacterState::SpriteSummon(data) => data.behavior(&j), }; local_emitter.append(&mut state_update.local_events); diff --git a/common/systems/src/stats.rs b/common/systems/src/stats.rs index ed6c9b3a9f..50a7578e8c 100644 --- a/common/systems/src/stats.rs +++ b/common/systems/src/stats.rs @@ -279,7 +279,8 @@ impl<'a> System<'a> for Sys { | CharacterState::HealingBeam { .. } | CharacterState::Blink { .. } | CharacterState::BasicSummon { .. } - | CharacterState::SelfBuff { .. } => { + | CharacterState::SelfBuff { .. } + | CharacterState::SpriteSummon { .. } => { if energy.get_unchecked().regen_rate != 0.0 { energy.get_mut_unchecked().regen_rate = 0.0 } diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index acd935f9a6..bf5047154b 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -16,6 +16,7 @@ use common::{ }, consts::{MAX_MOUNT_RANGE, SOUND_TRAVEL_DIST_PER_VOLUME}, outcome::Outcome, + terrain::{Block, SpriteKind}, uid::Uid, vol::ReadVol, }; @@ -418,3 +419,17 @@ pub fn handle_sound(server: &mut Server, sound: &Sound) { ecs.write_resource::>().push(outcome); } } + +pub fn handle_create_sprite(server: &mut Server, pos: Vec3, sprite: SpriteKind) { + let state = server.state_mut(); + if state.can_set_block(pos) { + let block = state.terrain().get(pos).ok().copied(); + if block.map_or(false, |b| (*b).is_air()) { + let new_block = state + .get_block(pos) + .unwrap_or_else(|| Block::air(SpriteKind::Empty)) + .with_sprite(sprite); + server.state.set_block(pos, new_block); + } + } +} diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index 38c73e6c52..43093f391f 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -13,8 +13,8 @@ use entity_manipulation::{ use group_manip::handle_group; use information::handle_site_info; use interaction::{ - handle_lantern, handle_mine_block, handle_mount, handle_npc_interaction, handle_possess, - handle_sound, handle_unmount, + handle_create_sprite, handle_lantern, handle_mine_block, handle_mount, handle_npc_interaction, + handle_possess, handle_sound, handle_unmount, }; use inventory_manip::handle_inventory; use invite::{handle_invite, handle_invite_response}; @@ -221,6 +221,9 @@ impl Server { self.state.create_safezone(range, pos).build(); }, ServerEvent::Sound { sound } => handle_sound(self, &sound), + ServerEvent::CreateSprite { pos, sprite } => { + handle_create_sprite(self, pos, sprite) + }, } } From 763461ebef3dc86710ed4938f6ddeaf62d03b6d6 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 19 Jun 2021 22:51:04 -0500 Subject: [PATCH 157/180] Ensnaring vines now ensnare you. --- .../abilities/custom/harvester/firebreath.ron | 1 + common/src/comp/ability.rs | 2 +- common/src/comp/phys.rs | 6 +-- common/src/states/climb.rs | 8 +++- common/src/states/dance.rs | 2 +- common/src/states/glide.rs | 10 ++++- common/src/states/glide_wield.rs | 2 +- common/src/states/leap_melee.rs | 2 +- common/src/states/sit.rs | 2 +- common/src/states/sneak.rs | 2 +- common/src/states/sprite_summon.rs | 6 +-- common/src/states/utils.rs | 25 ++++++----- common/systems/src/phys.rs | 45 +++++++++++-------- common/systems/src/shockwave.rs | 2 +- server/src/sys/agent.rs | 6 +-- .../audio/sfx/event_mapper/movement/mod.rs | 10 ++--- voxygen/src/scene/figure/mod.rs | 40 ++++++++--------- voxygen/src/scene/mod.rs | 2 +- 18 files changed, 96 insertions(+), 77 deletions(-) diff --git a/assets/common/abilities/custom/harvester/firebreath.ron b/assets/common/abilities/custom/harvester/firebreath.ron index e7a23ad6de..eff08060f6 100644 --- a/assets/common/abilities/custom/harvester/firebreath.ron +++ b/assets/common/abilities/custom/harvester/firebreath.ron @@ -15,5 +15,6 @@ BasicBeam( energy_regen: 0, energy_drain: 0, orientation_behavior: Normal, + ori_rate: 0.6, specifier: Flamethrower, ) diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 9931ac54c6..c6e18f530f 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -337,7 +337,7 @@ impl CharacterAbility { pub fn requirements_paid(&self, data: &JoinData, update: &mut StateUpdate) -> bool { match self { CharacterAbility::Roll { energy_cost, .. } => { - data.physics.on_ground + data.physics.on_ground.is_some() && data.vel.0.xy().magnitude_squared() > 0.5 && update .energy diff --git a/common/src/comp/phys.rs b/common/src/comp/phys.rs index e03552722b..e1e862221d 100644 --- a/common/src/comp/phys.rs +++ b/common/src/comp/phys.rs @@ -1,5 +1,5 @@ use super::Fluid; -use crate::{consts::WATER_DENSITY, uid::Uid}; +use crate::{consts::WATER_DENSITY, terrain::Block, uid::Uid}; use hashbrown::HashSet; use serde::{Deserialize, Serialize}; use specs::{Component, DerefFlaggedStorage, NullStorage}; @@ -142,7 +142,7 @@ impl Component for Sticky { // PhysicsState #[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] pub struct PhysicsState { - pub on_ground: bool, + pub on_ground: Option, pub on_ceiling: bool, pub on_wall: Option>, pub touch_entities: HashSet, @@ -165,7 +165,7 @@ impl PhysicsState { pub fn on_surface(&self) -> Option> { self.on_ground - .then_some(-Vec3::unit_z()) + .map(|_| -Vec3::unit_z()) .or_else(|| self.on_ceiling.then_some(Vec3::unit_z())) .or(self.on_wall) } diff --git a/common/src/states/climb.rs b/common/src/states/climb.rs index 712516c4de..71d87e84ec 100644 --- a/common/src/states/climb.rs +++ b/common/src/states/climb.rs @@ -57,7 +57,7 @@ impl CharacterBehavior for Data { let mut update = StateUpdate::from(data); // If no wall is in front of character or we stopped climbing; - let (wall_dir, climb) = if let (Some(wall_dir), Some(climb), false) = ( + let (wall_dir, climb) = if let (Some(wall_dir), Some(climb), None) = ( data.physics.on_wall, data.inputs.climb, data.physics.on_ground, @@ -105,7 +105,11 @@ impl CharacterBehavior for Data { // Smooth orientation update.ori = update.ori.slerped_towards( Ori::from(ori_dir), - if data.physics.on_ground { 9.0 } else { 2.0 } * data.dt.0, + if data.physics.on_ground.is_some() { + 9.0 + } else { + 2.0 + } * data.dt.0, ); }; diff --git a/common/src/states/dance.rs b/common/src/states/dance.rs index cc5242f316..5a78a7400f 100644 --- a/common/src/states/dance.rs +++ b/common/src/states/dance.rs @@ -16,7 +16,7 @@ impl CharacterBehavior for Data { handle_jump(data, &mut update, 1.0); // Try to Fall/Stand up/Move - if !data.physics.on_ground || data.inputs.move_dir.magnitude_squared() > 0.0 { + if data.physics.on_ground.is_none() || data.inputs.move_dir.magnitude_squared() > 0.0 { update.character = CharacterState::Idle; } diff --git a/common/src/states/glide.rs b/common/src/states/glide.rs index 72aed78390..dafd7caa19 100644 --- a/common/src/states/glide.rs +++ b/common/src/states/glide.rs @@ -74,7 +74,7 @@ impl CharacterBehavior for Data { let mut update = StateUpdate::from(data); // If player is on ground, end glide - if data.physics.on_ground + if data.physics.on_ground.is_some() && (data.vel.0 - data.physics.ground_vel).magnitude_squared() < 2_f32.powi(2) { update.character = CharacterState::GlideWield; @@ -163,7 +163,13 @@ impl CharacterBehavior for Data { let accel_factor = accel.magnitude_squared().min(1.0) / 1.0; Quaternion::rotation_3d( - PI / 2.0 * accel_factor * if data.physics.on_ground { -1.0 } else { 1.0 }, + PI / 2.0 + * accel_factor + * if data.physics.on_ground.is_some() { + -1.0 + } else { + 1.0 + }, ori.up() .cross(accel) .try_normalized() diff --git a/common/src/states/glide_wield.rs b/common/src/states/glide_wield.rs index 23c0b9b807..42b64e72ae 100644 --- a/common/src/states/glide_wield.rs +++ b/common/src/states/glide_wield.rs @@ -20,7 +20,7 @@ impl CharacterBehavior for Data { handle_wield(data, &mut update); // If not on the ground while wielding glider enter gliding state - if !data.physics.on_ground { + if data.physics.on_ground.is_none() { update.character = CharacterState::Glide(glide::Data::new(10.0, 0.6, *data.ori)); } if data diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index 59c6e2a49c..3ce97f8309 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -101,7 +101,7 @@ impl CharacterBehavior for Data { timer: tick_attack_or_default(data, self.timer, None), ..*self }); - } else if data.physics.on_ground { + } else if data.physics.on_ground.is_some() { // Transitions to swing portion of state upon hitting ground update.character = CharacterState::LeapMelee(Data { timer: Duration::default(), diff --git a/common/src/states/sit.rs b/common/src/states/sit.rs index 459cba1ac6..555ac4252a 100644 --- a/common/src/states/sit.rs +++ b/common/src/states/sit.rs @@ -16,7 +16,7 @@ impl CharacterBehavior for Data { handle_jump(data, &mut update, 1.0); // Try to Fall/Stand up/Move - if !data.physics.on_ground || data.inputs.move_dir.magnitude_squared() > 0.0 { + if data.physics.on_ground.is_none() || data.inputs.move_dir.magnitude_squared() > 0.0 { update.character = CharacterState::Idle; } diff --git a/common/src/states/sneak.rs b/common/src/states/sneak.rs index 768dce701c..e025db9fc2 100644 --- a/common/src/states/sneak.rs +++ b/common/src/states/sneak.rs @@ -18,7 +18,7 @@ impl CharacterBehavior for Data { handle_dodge_input(data, &mut update); // Try to Fall/Stand up/Move - if !data.physics.on_ground { + if data.physics.on_ground.is_none() { update.character = CharacterState::Idle; } diff --git a/common/src/states/sprite_summon.rs b/common/src/states/sprite_summon.rs index 76ec4f0062..75bfcccf60 100644 --- a/common/src/states/sprite_summon.rs +++ b/common/src/states/sprite_summon.rs @@ -96,11 +96,9 @@ impl CharacterBehavior for Data { sprite_pos.map(|x| x as f32 + 0.5) - Vec3::unit_z() * 25.0, ) .until(|b| { + // Until reaching a solid block that is not the created sprite Block::is_solid(b) - && !matches!( - b.get_sprite(), - Some(SpriteKind::EnsnaringVines) - ) + && b.get_sprite() != Some(self.static_data.sprite) }) .cast() .0; diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index c8350249eb..8b96c3ff4c 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -233,13 +233,14 @@ pub fn handle_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) { if input_is_pressed(data, InputKind::Fly) && submersion.map_or(true, |sub| sub < 1.0) - && (!data.physics.on_ground || data.body.jump_impulse().is_none()) + && (data.physics.on_ground.is_none() || data.body.jump_impulse().is_none()) && data.body.fly_thrust().is_some() { fly_move(data, update, efficiency); - } else if let Some(submersion) = (!data.physics.on_ground && data.body.swim_thrust().is_some()) - .then_some(submersion) - .flatten() + } else if let Some(submersion) = (data.physics.on_ground.is_none() + && data.body.swim_thrust().is_some()) + .then_some(submersion) + .flatten() { swim_move(data, update, efficiency, submersion); } else { @@ -252,7 +253,7 @@ pub fn handle_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) { fn basic_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) { let efficiency = efficiency * data.stats.move_speed_modifier * data.stats.friction_modifier; - let accel = if data.physics.on_ground { + let accel = if data.physics.on_ground.is_some() { data.body.base_accel() } else { data.body.air_accel() @@ -294,7 +295,7 @@ pub fn handle_forced_movement(data: &JoinData, update: &mut StateUpdate, movemen match movement { ForcedMovement::Forward { strength } => { let strength = strength * data.stats.move_speed_modifier * data.stats.friction_modifier; - if let Some(accel) = data.physics.on_ground.then_some(data.body.base_accel()) { + if let Some(accel) = data.physics.on_ground.map(|_| data.body.base_accel()) { update.vel.0 += Vec2::broadcast(data.dt.0) * accel * (data.inputs.move_dir + Vec2::from(update.ori)) @@ -499,25 +500,25 @@ pub fn attempt_wield(data: &JoinData, update: &mut StateUpdate) { /// Checks that player can `Sit` and updates `CharacterState` if so pub fn attempt_sit(data: &JoinData, update: &mut StateUpdate) { - if data.physics.on_ground { + if data.physics.on_ground.is_some() { update.character = CharacterState::Sit; } } pub fn attempt_dance(data: &JoinData, update: &mut StateUpdate) { - if data.physics.on_ground && data.body.is_humanoid() { + if data.physics.on_ground.is_some() && data.body.is_humanoid() { update.character = CharacterState::Dance; } } pub fn attempt_talk(data: &JoinData, update: &mut StateUpdate) { - if data.physics.on_ground { + if data.physics.on_ground.is_some() { update.character = CharacterState::Talk; } } pub fn attempt_sneak(data: &JoinData, update: &mut StateUpdate) { - if data.physics.on_ground && data.body.is_humanoid() { + if data.physics.on_ground.is_some() && data.body.is_humanoid() { update.character = CharacterState::Sneak; } } @@ -526,7 +527,7 @@ pub fn attempt_sneak(data: &JoinData, update: &mut StateUpdate) { pub fn handle_climb(data: &JoinData, update: &mut StateUpdate) -> bool { if data.inputs.climb.is_some() && data.physics.on_wall.is_some() - && !data.physics.on_ground + && data.physics.on_ground.is_none() && !data .physics .in_liquid() @@ -585,7 +586,7 @@ pub fn attempt_glide_wield(data: &JoinData, update: &mut StateUpdate) { /// Checks that player can jump and sends jump event if so pub fn handle_jump(data: &JoinData, update: &mut StateUpdate, strength: f32) -> bool { - (input_is_pressed(data, InputKind::Jump) && data.physics.on_ground) + (input_is_pressed(data, InputKind::Jump) && data.physics.on_ground.is_some()) .then(|| data.body.jump_impulse()) .flatten() .map(|impulse| { diff --git a/common/systems/src/phys.rs b/common/systems/src/phys.rs index 81d1cec1c5..e305270449 100644 --- a/common/systems/src/phys.rs +++ b/common/systems/src/phys.rs @@ -10,7 +10,7 @@ use common::{ outcome::Outcome, resources::DeltaTime, states, - terrain::{Block, TerrainGrid}, + terrain::{Block, SpriteKind, TerrainGrid}, uid::Uid, util::{Projection, SpatialGrid}, vol::{BaseVol, ReadVol}, @@ -319,7 +319,8 @@ impl<'a> PhysicsData<'a> { char_state_maybe, )| { let is_sticky = sticky.is_some(); - let is_mid_air = physics.on_wall.is_none() && physics.on_ground; + // Code reviewers: remind me to check why on_ground was true instead of false here? + let is_mid_air = physics.on_wall.is_none() && physics.on_ground.is_some(); let mut entity_entity_collision_checks = 0; let mut entity_entity_collisions = 0; @@ -753,7 +754,7 @@ impl<'a> PhysicsData<'a> { // velocities or entirely broken position snapping. let mut tgt_pos = pos.0 + pos_delta; - let was_on_ground = physics_state.on_ground; + let was_on_ground = physics_state.on_ground.is_some(); let block_snap = body.map_or(false, |b| !matches!(b, Body::Object(_) | Body::Ship(_))); let climbing = @@ -879,7 +880,7 @@ impl<'a> PhysicsData<'a> { > block_rpos.xy().map(|e| e.abs()).reduce_partial_max() { if block_rpos.z > 0.0 { - physics_state.on_ground = true; + physics_state.on_ground = block.copied(); } else { physics_state.on_ceiling = true; } @@ -1068,10 +1069,11 @@ impl<'a> PhysicsData<'a> { // union in the state updates, so that the state isn't just // based on the most // recent terrain that collision was attempted with - if physics_state_delta.on_ground { + if physics_state_delta.on_ground.is_some() { physics_state.ground_vel = vel_other; } - physics_state.on_ground |= physics_state_delta.on_ground; + physics_state.on_ground = + physics_state.on_ground.or(physics_state_delta.on_ground); physics_state.on_ceiling |= physics_state_delta.on_ceiling; physics_state.on_wall = physics_state.on_wall.or_else(|| { physics_state_delta @@ -1339,10 +1341,10 @@ fn box_voxel_collision<'a, T: BaseVol + ReadVol>( .is_some() } - physics_state.on_ground = false; + physics_state.on_ground = None; physics_state.on_ceiling = false; - let mut on_ground = false; + let mut on_ground = None; let mut on_ceiling = false; let mut attempts = 0; // Don't loop infinitely here @@ -1360,7 +1362,7 @@ fn box_voxel_collision<'a, T: BaseVol + ReadVol>( const MAX_ATTEMPTS: usize = 16; // While the player is colliding with the terrain... - while let Some((_block_pos, block_aabb, block_height)) = + while let Some((_block_pos, block_aabb, block_height, block)) = (attempts < MAX_ATTEMPTS).then(|| { // Calculate the player's AABB let player_aabb = Aabb { @@ -1391,12 +1393,13 @@ fn box_voxel_collision<'a, T: BaseVol + ReadVol>( max: block_pos.map(|e| e as f32) + Vec3::new(1.0, 1.0, block.solid_height()), }, block.solid_height(), + block, ) }) // Determine whether the block's AABB collides with the player's AABB - .filter(|(_, block_aabb, _)| block_aabb.collides_with_aabb(player_aabb)) + .filter(|(_, block_aabb, _, _)| block_aabb.collides_with_aabb(player_aabb)) // Find the maximum of the minimum collision axes (this bit is weird, trust me that it works) - .min_by_key(|(_, block_aabb, _)| { + .min_by_key(|(_, block_aabb, _, _)| { ordered_float::OrderedFloat((block_aabb.center() - player_aabb.center() - Vec3::unit_z() * 0.5) .map(f32::abs) .sum()) @@ -1428,7 +1431,7 @@ fn box_voxel_collision<'a, T: BaseVol + ReadVol>( if resolve_dir.z > 0.0 /* && vel.0.z <= 0.0 */ { - on_ground = true; + on_ground = Some(block).copied(); if !was_on_ground { land_on_ground(entity, *vel); @@ -1466,7 +1469,7 @@ fn box_voxel_collision<'a, T: BaseVol + ReadVol>( if (vel.0 * resolve_dir).xy().magnitude_squared() < 1.0f32.powi(2) { pos.0 -= resolve_dir.normalized() * 0.05; } - on_ground = true; + on_ground = Some(block).copied(); break; } else { // Correct the velocity @@ -1497,8 +1500,8 @@ fn box_voxel_collision<'a, T: BaseVol + ReadVol>( physics_state.on_ceiling = true; } - if on_ground { - physics_state.on_ground = true; + if on_ground.is_some() { + physics_state.on_ground = on_ground; // If the space below us is free, then "snap" to the ground } else if collision_with( pos.0 - Vec3::unit_z() * 1.1, @@ -1519,7 +1522,7 @@ fn box_voxel_collision<'a, T: BaseVol + ReadVol>( .unwrap_or(0.0); vel.0.z = 0.0; pos.0.z = (pos.0.z - 0.1).floor() + snap_height; - physics_state.on_ground = true; + physics_state.on_ground = on_ground; } let player_aabb = Aabb { @@ -1593,8 +1596,14 @@ fn box_voxel_collision<'a, T: BaseVol + ReadVol>( } } physics_state.on_wall = on_wall; - let fric_mod = read.stats.get(entity).map_or(1.0, |s| s.friction_modifier); - if physics_state.on_ground || (physics_state.on_wall.is_some() && climbing) { + let fric_mod = read.stats.get(entity).map_or(1.0, |s| s.friction_modifier) + * physics_state + .on_ground + .map_or(1.0, |b| match b.get_sprite() { + Some(SpriteKind::EnsnaringVines) => 5.0, + _ => 1.0, + }); + if physics_state.on_ground.is_some() || (physics_state.on_wall.is_some() && climbing) { vel.0 *= (1.0 - FRIC_GROUND.min(1.0) * fric_mod).powf(dt.0 * 60.0); physics_state.ground_vel = ground_vel; } diff --git a/common/systems/src/shockwave.rs b/common/systems/src/shockwave.rs index d1d4fbb575..88c1aaaba0 100644 --- a/common/systems/src/shockwave.rs +++ b/common/systems/src/shockwave.rs @@ -181,7 +181,7 @@ impl<'a> System<'a> for Sys { arc_strip.collides_with_circle(Disk::new(pos_b2, rad_b)) } && (pos_b_ground - pos.0).angle_between(pos_b.0 - pos.0) < max_angle - && (!shockwave.requires_ground || physics_state_b.on_ground); + && (!shockwave.requires_ground || physics_state_b.on_ground.is_some()); if hit { let dir = Dir::from_unnormalized(pos_b.0 - pos.0).unwrap_or(look_dir); diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 67240676be..a11989aad7 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -280,7 +280,7 @@ impl<'a> System<'a> for Sys { let is_gliding = matches!( read_data.char_states.get(entity), Some(CharacterState::GlideWield) | Some(CharacterState::Glide(_)) - ) && !physics_state.on_ground; + ) && physics_state.on_ground.is_none(); if let Some(pid) = agent.position_pid_controller.as_mut() { pid.add_measurement(read_data.time.0, pos.0); @@ -295,7 +295,7 @@ impl<'a> System<'a> for Sys { let traversal_config = TraversalConfig { node_tolerance, slow_factor, - on_ground: physics_state.on_ground, + on_ground: physics_state.on_ground.is_some(), in_liquid: physics_state.in_liquid().is_some(), min_tgt_dist: 1.0, can_climb: body.map(|b| b.can_climb()).unwrap_or(false), @@ -365,7 +365,7 @@ impl<'a> System<'a> for Sys { // inputs. // If falling fast and can glide, save yourself! - if data.glider_equipped && !data.physics_state.on_ground { + if data.glider_equipped && data.physics_state.on_ground.is_none() { // toggle glider when vertical velocity is above some threshold (here ~ // glider fall vertical speed) data.glider_fall(agent, controller, &read_data); diff --git a/voxygen/src/audio/sfx/event_mapper/movement/mod.rs b/voxygen/src/audio/sfx/event_mapper/movement/mod.rs index d4300e1610..670c86308b 100644 --- a/voxygen/src/audio/sfx/event_mapper/movement/mod.rs +++ b/voxygen/src/audio/sfx/event_mapper/movement/mod.rs @@ -121,7 +121,7 @@ impl EventMapper for MovementEventMapper { // update state to determine the next event. We only record the time (above) if // it was dispatched internal_state.event = mapped_event; - internal_state.on_ground = physics.on_ground; + internal_state.on_ground = physics.on_ground.is_some(); internal_state.in_water = physics.in_liquid().is_some(); let dt = ecs.fetch::().0; internal_state.distance_travelled += vel.0.magnitude() * dt; @@ -197,8 +197,8 @@ impl MovementEventMapper { || !previous_state.in_water && physics_state.in_liquid().is_some() { return SfxEvent::Swim; - } else if physics_state.on_ground && vel.magnitude() > 0.1 - || !previous_state.on_ground && physics_state.on_ground + } else if physics_state.on_ground.is_some() && vel.magnitude() > 0.1 + || !previous_state.on_ground && physics_state.on_ground.is_some() { return if matches!(character_state, CharacterState::Roll(_)) { SfxEvent::Roll @@ -238,7 +238,7 @@ impl MovementEventMapper { ) -> SfxEvent { if physics_state.in_liquid().is_some() && vel.magnitude() > 0.1 { SfxEvent::Swim - } else if physics_state.on_ground && vel.magnitude() > 0.1 { + } else if physics_state.on_ground.is_some() && vel.magnitude() > 0.1 { match underfoot_block_kind { BlockKind::Snow => SfxEvent::Run(BlockKind::Snow), BlockKind::Rock | BlockKind::WeakRock => SfxEvent::Run(BlockKind::Rock), @@ -259,7 +259,7 @@ impl MovementEventMapper { ) -> SfxEvent { if physics_state.in_liquid().is_some() && vel.magnitude() > 0.1 { SfxEvent::Swim - } else if physics_state.on_ground && vel.magnitude() > 0.1 { + } else if physics_state.on_ground.is_some() && vel.magnitude() > 0.1 { match underfoot_block_kind { BlockKind::Snow => SfxEvent::QuadRun(BlockKind::Snow), BlockKind::Rock | BlockKind::WeakRock => SfxEvent::QuadRun(BlockKind::Rock), diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 5174bb0330..3a5ec2de86 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -800,7 +800,7 @@ impl FigureMgr { } let target_base = match ( - physics.on_ground, + physics.on_ground.is_some(), rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving physics.in_liquid().is_some(), // In water ) { @@ -1596,7 +1596,7 @@ impl FigureMgr { } let target_base = match ( - physics.on_ground, + physics.on_ground.is_some(), rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving physics.in_liquid().is_some(), // In water ) { @@ -1798,7 +1798,7 @@ impl FigureMgr { } let target_base = match ( - physics.on_ground, + physics.on_ground.is_some(), rel_vel.magnitude_squared() > 0.25, // Moving physics.in_liquid().is_some(), // In water ) { @@ -2125,7 +2125,7 @@ impl FigureMgr { } let target_base = match ( - physics.on_ground, + physics.on_ground.is_some(), rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving physics.in_liquid().is_some(), // In water ) { @@ -2484,7 +2484,7 @@ impl FigureMgr { } let target_base = match ( - physics.on_ground, + physics.on_ground.is_some(), rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving physics.in_liquid().is_some(), // In water ) { @@ -2594,7 +2594,7 @@ impl FigureMgr { } let target_base = match ( - physics.on_ground, + physics.on_ground.is_some(), rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving physics.in_liquid().is_some(), // In water ) { @@ -2683,7 +2683,7 @@ impl FigureMgr { } let target_base = match ( - physics.on_ground, + physics.on_ground.is_some(), rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving physics.in_liquid().is_some(), // In water ) { @@ -3028,7 +3028,7 @@ impl FigureMgr { } let target_base = match ( - physics.on_ground, + physics.on_ground.is_some(), rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving physics.in_liquid().is_some(), // In water ) { @@ -3123,7 +3123,7 @@ impl FigureMgr { } let target_base = match ( - physics.on_ground, + physics.on_ground.is_some(), rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving physics.in_liquid().is_some(), // In water ) { @@ -3309,7 +3309,7 @@ impl FigureMgr { } let target_base = match ( - physics.on_ground, + physics.on_ground.is_some(), rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving physics.in_liquid().is_some(), // In water ) { @@ -3396,7 +3396,7 @@ impl FigureMgr { Some(s.stage_section), state.state_time, look_dir, - physics.on_ground, + physics.on_ground.is_some(), ), stage_progress, &mut state_animation_rate, @@ -3434,7 +3434,7 @@ impl FigureMgr { Some(s.stage_section), ori * anim::vek::Vec3::::unit_y(), state.last_ori * anim::vek::Vec3::::unit_y(), - physics.on_ground, + physics.on_ground.is_some(), ), stage_progress, &mut state_animation_rate, @@ -3461,7 +3461,7 @@ impl FigureMgr { Some(s.stage_section), state.state_time, look_dir, - physics.on_ground, + physics.on_ground.is_some(), ), stage_progress, &mut state_animation_rate, @@ -3484,7 +3484,7 @@ impl FigureMgr { }; anim::bird_large::ShockwaveAnimation::update_skeleton( &target_base, - (Some(s.stage_section), physics.on_ground), + (Some(s.stage_section), physics.on_ground.is_some()), stage_progress, &mut state_animation_rate, skeleton_attr, @@ -3513,7 +3513,7 @@ impl FigureMgr { Some(s.stage_section), state.state_time, look_dir, - physics.on_ground, + physics.on_ground.is_some(), ), stage_progress, &mut state_animation_rate, @@ -3637,7 +3637,7 @@ impl FigureMgr { } let target_base = match ( - physics.on_ground, + physics.on_ground.is_some(), rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving physics.in_liquid().is_some(), // In water ) { @@ -3726,7 +3726,7 @@ impl FigureMgr { } let target_base = match ( - physics.on_ground, + physics.on_ground.is_some(), rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving physics.in_liquid().is_some(), // In water ) { @@ -4327,7 +4327,7 @@ impl FigureMgr { } let target_base = match ( - physics.on_ground, + physics.on_ground.is_some(), rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving physics.in_liquid().is_some(), // In water ) { @@ -4581,7 +4581,7 @@ impl FigureMgr { } let target_base = match ( - physics.on_ground, + physics.on_ground.is_some(), rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving physics.in_liquid().is_some(), // In water ) { @@ -4710,7 +4710,7 @@ impl FigureMgr { } let target_base = match ( - physics.on_ground, + physics.on_ground.is_some(), rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving physics.in_liquid().is_some(), // In water ) { diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 365c5a0490..012cf9e12f 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -469,7 +469,7 @@ impl Scene { let on_ground = ecs .read_storage::() .get(scene_data.player_entity) - .map(|p| p.on_ground); + .map(|p| p.on_ground.is_some()); let (player_height, player_eye_height) = scene_data .state From 0a32b676c84373b9d648f735d9c9066a26d75a21 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 20 Jun 2021 00:37:22 -0500 Subject: [PATCH 158/180] Vines now ensnare you by applying a buff. --- assets/voxygen/i18n/en/buff.ron | 2 + common/src/cmd.rs | 1 + common/src/comp/ability.rs | 2 +- common/src/comp/body.rs | 3 + common/src/comp/buff.rs | 9 ++ common/src/states/utils.rs | 2 +- common/systems/src/buff.rs | 89 +++++++++++-------- common/systems/src/phys.rs | 10 +-- .../audio/sfx/event_mapper/movement/tests.rs | 16 ++-- voxygen/src/hud/chat.rs | 2 +- voxygen/src/hud/mod.rs | 3 + voxygen/src/hud/util.rs | 6 +- 12 files changed, 88 insertions(+), 57 deletions(-) diff --git a/assets/voxygen/i18n/en/buff.ron b/assets/voxygen/i18n/en/buff.ron index 1109fc4120..e2c4ed10c6 100644 --- a/assets/voxygen/i18n/en/buff.ron +++ b/assets/voxygen/i18n/en/buff.ron @@ -34,6 +34,8 @@ "buff.desc.frozen": "Your movements and attacks are slowed.", "buff.title.wet": "Wet", "buff.desc.wet": "The ground rejects your feet, making it hard to stop.", + "buff.title.ensnared": "Ensnared", + "buff.desc.ensnared": "Vines grasp at your legs, impeding your movement.", // Buffs stats "buff.stat.health": "Restores {str_total} Health", "buff.stat.increase_max_stamina": "Raises Maximum Stamina by {strength}", diff --git a/common/src/cmd.rs b/common/src/cmd.rs index 0a1b91eba6..c9991a74c2 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -197,6 +197,7 @@ lazy_static! { BuffKind::Crippled => "crippled", BuffKind::Frozen => "frozen", BuffKind::Wet => "wet", + BuffKind::Ensnared => "ensnared", }; let mut buff_parser = HashMap::new(); BuffKind::iter().for_each(|kind| {buff_parser.insert(string_from_buff(kind).to_string(), kind);}); diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index c6e18f530f..72e31105f4 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -1822,7 +1822,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { }, timer: Duration::default(), stage_section: StageSection::Buildup, - achieved_radius: 0, + achieved_radius: summon_distance.0.floor() as i32 - 1, }), } } diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 6c999c3635..7117c1189c 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -659,6 +659,9 @@ impl Body { Body::Object(object::Body::HaniwaSentry) => true, _ => false, }, + BuffKind::Ensnared => { + matches!(self, Body::BipedLarge(b) if matches!(b.species, biped_large::Species::Harvester)) + }, _ => false, } } diff --git a/common/src/comp/buff.rs b/common/src/comp/buff.rs index 412ab0affa..c0472798a3 100644 --- a/common/src/comp/buff.rs +++ b/common/src/comp/buff.rs @@ -69,6 +69,10 @@ pub enum BuffKind { /// Strength scales the friction you ignore non-linearly. 0.5 is 50% ground /// friction, 1.0 is 33% ground friction. Wet, + /// Makes you move slower. + /// Strength scales the movement speed debuff non-linearly. 0.5 is 50% + /// speed, 1.0 is 33% speed. + Ensnared, } #[cfg(not(target_arch = "wasm32"))] @@ -91,6 +95,7 @@ impl BuffKind { BuffKind::Frenzied => true, BuffKind::Frozen => false, BuffKind::Wet => false, + BuffKind::Ensnared => false, } } @@ -319,6 +324,10 @@ impl Buff { vec![BuffEffect::GroundFriction(1.0 - nn_scaling(data.strength))], data.duration, ), + BuffKind::Ensnared => ( + vec![BuffEffect::MovementSpeed(1.0 - nn_scaling(data.strength))], + data.duration, + ), }; Buff { kind, diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 8b96c3ff4c..4027e4eda4 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -592,7 +592,7 @@ pub fn handle_jump(data: &JoinData, update: &mut StateUpdate, strength: f32) -> .map(|impulse| { update.local_events.push_front(LocalEvent::Jump( data.entity, - strength * impulse / data.mass.0, + strength * impulse / data.mass.0 * data.stats.move_speed_modifier, )); }) .is_some() diff --git a/common/systems/src/buff.rs b/common/systems/src/buff.rs index 3275bb60bb..f25dda0a68 100644 --- a/common/systems/src/buff.rs +++ b/common/systems/src/buff.rs @@ -1,11 +1,15 @@ use common::{ comp::{ + buff::{ + Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, + Buffs, + }, fluid_dynamics::{Fluid, LiquidKind}, - Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buffs, Energy, Health, HealthChange, HealthSource, Inventory, ModifierKind, PhysicsState, Stats, }, event::{EventBus, ServerEvent}, resources::DeltaTime, + terrain::SpriteKind, Damage, DamageSource, }; use common_ecs::{Job, Origin, Phase, System}; @@ -55,25 +59,52 @@ impl<'a> System<'a> for Sys { ) .join() { - let in_fluid = physics_state.and_then(|p| p.in_fluid); - - if matches!( - in_fluid, - Some(Fluid::Liquid { - kind: LiquidKind::Lava, - .. - }) - ) && !buff_comp.contains(BuffKind::Burning) - { - server_emitter.emit(ServerEvent::Buff { - entity, - buff_change: BuffChange::Add(Buff::new( - BuffKind::Burning, - BuffData::new(200.0, None), - vec![BuffCategory::Natural], - BuffSource::World, - )), - }); + // Apply buffs to entity based off of their current physics_state + if let Some(physics_state) = physics_state { + if matches!( + physics_state.on_ground.and_then(|b| b.get_sprite()), + Some(SpriteKind::EnsnaringVines) + ) { + server_emitter.emit(ServerEvent::Buff { + entity, + buff_change: BuffChange::Add(Buff::new( + BuffKind::Ensnared, + BuffData::new(1.5, Some(Duration::from_secs_f32(1.0))), + Vec::new(), + BuffSource::World, + )), + }); + } + if matches!( + physics_state.in_fluid, + Some(Fluid::Liquid { + kind: LiquidKind::Lava, + .. + }) + ) { + server_emitter.emit(ServerEvent::Buff { + entity, + buff_change: BuffChange::Add(Buff::new( + BuffKind::Burning, + BuffData::new(200.0, None), + vec![BuffCategory::Natural], + BuffSource::World, + )), + }); + } else if matches!( + physics_state.in_fluid, + Some(Fluid::Liquid { + kind: LiquidKind::Water, + .. + }) + ) { + if buff_comp.kinds.contains_key(&BuffKind::Burning) { + server_emitter.emit(ServerEvent::Buff { + entity, + buff_change: BuffChange::RemoveByKind(BuffKind::Burning), + }); + } + } } let (buff_comp_kinds, buff_comp_buffs): ( @@ -90,14 +121,14 @@ impl<'a> System<'a> for Sys { if let Some((Some(buff), id)) = ids.get(0).map(|id| (buff_comp_buffs.get_mut(id), id)) { - tick_buff(*id, buff, dt, in_fluid, |id| expired_buffs.push(id)); + tick_buff(*id, buff, dt, |id| expired_buffs.push(id)); } } else { for (id, buff) in buff_comp_buffs .iter_mut() .filter(|(i, _)| ids.iter().any(|id| id == *i)) { - tick_buff(*id, buff, dt, in_fluid, |id| expired_buffs.push(id)); + tick_buff(*id, buff, dt, |id| expired_buffs.push(id)); } } } @@ -257,7 +288,6 @@ fn tick_buff( id: u64, buff: &mut Buff, dt: f32, - in_fluid: Option, mut expire_buff: impl FnMut(u64), ) { // If a buff is recently applied from an aura, do not tick duration @@ -269,19 +299,6 @@ fn tick_buff( return; } if let Some(remaining_time) = &mut buff.time { - // Extinguish Burning buff when in water - if matches!(buff.kind, BuffKind::Burning) - && matches!( - in_fluid, - Some(Fluid::Liquid { - kind: LiquidKind::Water, - .. - }) - ) - { - *remaining_time = Duration::default(); - } - if let Some(new_duration) = remaining_time.checked_sub(Duration::from_secs_f32(dt)) { // The buff still continues. *remaining_time = new_duration; diff --git a/common/systems/src/phys.rs b/common/systems/src/phys.rs index e305270449..a191d36c65 100644 --- a/common/systems/src/phys.rs +++ b/common/systems/src/phys.rs @@ -10,7 +10,7 @@ use common::{ outcome::Outcome, resources::DeltaTime, states, - terrain::{Block, SpriteKind, TerrainGrid}, + terrain::{Block, TerrainGrid}, uid::Uid, util::{Projection, SpatialGrid}, vol::{BaseVol, ReadVol}, @@ -1596,13 +1596,7 @@ fn box_voxel_collision<'a, T: BaseVol + ReadVol>( } } physics_state.on_wall = on_wall; - let fric_mod = read.stats.get(entity).map_or(1.0, |s| s.friction_modifier) - * physics_state - .on_ground - .map_or(1.0, |b| match b.get_sprite() { - Some(SpriteKind::EnsnaringVines) => 5.0, - _ => 1.0, - }); + let fric_mod = read.stats.get(entity).map_or(1.0, |s| s.friction_modifier); if physics_state.on_ground.is_some() || (physics_state.on_wall.is_some() && climbing) { vel.0 *= (1.0 - FRIC_GROUND.min(1.0) * fric_mod).powf(dt.0 * 60.0); physics_state.ground_vel = ground_vel; diff --git a/voxygen/src/audio/sfx/event_mapper/movement/tests.rs b/voxygen/src/audio/sfx/event_mapper/movement/tests.rs index 919c5187ea..047cc80a9f 100644 --- a/voxygen/src/audio/sfx/event_mapper/movement/tests.rs +++ b/voxygen/src/audio/sfx/event_mapper/movement/tests.rs @@ -6,7 +6,7 @@ use common::{ Ori, PhysicsState, }, states, - terrain::BlockKind, + terrain::{Block, BlockKind}, }; use std::time::{Duration, Instant}; @@ -95,7 +95,7 @@ fn maps_idle() { let result = MovementEventMapper::map_movement_event( &CharacterState::Idle {}, &PhysicsState { - on_ground: true, + on_ground: Some(Block::empty()), ..Default::default() }, &PreviousEntityState { @@ -117,7 +117,7 @@ fn maps_run_with_sufficient_velocity() { let result = MovementEventMapper::map_movement_event( &CharacterState::Idle {}, &PhysicsState { - on_ground: true, + on_ground: Some(Block::empty()), ..Default::default() }, &PreviousEntityState { @@ -139,7 +139,7 @@ fn does_not_map_run_with_insufficient_velocity() { let result = MovementEventMapper::map_movement_event( &CharacterState::Idle {}, &PhysicsState { - on_ground: true, + on_ground: Some(Block::empty()), ..Default::default() }, &PreviousEntityState { @@ -194,7 +194,7 @@ fn maps_roll() { was_combo: None, }), &PhysicsState { - on_ground: true, + on_ground: Some(Block::empty()), ..Default::default() }, &PreviousEntityState { @@ -216,7 +216,7 @@ fn maps_land_on_ground_to_run() { let result = MovementEventMapper::map_movement_event( &CharacterState::Idle {}, &PhysicsState { - on_ground: true, + on_ground: Some(Block::empty()), ..Default::default() }, &PreviousEntityState { @@ -296,7 +296,7 @@ fn maps_glider_close_when_landing() { let result = MovementEventMapper::map_movement_event( &CharacterState::Idle {}, &PhysicsState { - on_ground: true, + on_ground: Some(Block::empty()), ..Default::default() }, &PreviousEntityState { @@ -317,7 +317,7 @@ fn maps_glider_close_when_landing() { fn maps_quadrupeds_running() { let result = MovementEventMapper::map_non_humanoid_movement_event( &PhysicsState { - on_ground: true, + on_ground: Some(Block::empty()), ..Default::default() }, Vec3::new(0.5, 0.8, 0.0), diff --git a/voxygen/src/hud/chat.rs b/voxygen/src/hud/chat.rs index 2b4c70081a..5c7638ee2e 100644 --- a/voxygen/src/hud/chat.rs +++ b/voxygen/src/hud/chat.rs @@ -769,7 +769,7 @@ fn insert_killing_buff(buff: BuffKind, localized_strings: &Localization, templat tracing::error!("Player was killed by a positive buff!"); localized_strings.get("hud.outcome.mysterious") }, - BuffKind::Wet => { + BuffKind::Wet | BuffKind::Ensnared => { tracing::error!("Player was killed by a debuff that doesn't do damage!"); localized_strings.get("hud.outcome.mysterious") }, diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index fa478cb929..5d1b45e4e0 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -3822,6 +3822,7 @@ pub fn get_buff_image(buff: BuffKind, imgs: &Imgs) -> conrod_core::image::Id { BuffKind::Crippled { .. } => imgs.debuff_crippled_0, BuffKind::Frozen { .. } => imgs.debuff_frozen_0, BuffKind::Wet { .. } => imgs.debuff_wet_0, + BuffKind::Ensnared { .. } => imgs.debuff_crippled_0, } } @@ -3844,6 +3845,7 @@ pub fn get_buff_title(buff: BuffKind, localized_strings: &Localization) -> &str BuffKind::Crippled { .. } => localized_strings.get("buff.title.crippled"), BuffKind::Frozen { .. } => localized_strings.get("buff.title.frozen"), BuffKind::Wet { .. } => localized_strings.get("buff.title.wet"), + BuffKind::Ensnared { .. } => localized_strings.get("buff.title.ensnared"), } } @@ -3878,6 +3880,7 @@ pub fn get_buff_desc(buff: BuffKind, data: BuffData, localized_strings: &Localiz BuffKind::Crippled { .. } => Cow::Borrowed(localized_strings.get("buff.desc.crippled")), BuffKind::Frozen { .. } => Cow::Borrowed(localized_strings.get("buff.desc.frozen")), BuffKind::Wet { .. } => Cow::Borrowed(localized_strings.get("buff.desc.wet")), + BuffKind::Ensnared { .. } => Cow::Borrowed(localized_strings.get("buff.desc.ensnared")), } } diff --git a/voxygen/src/hud/util.rs b/voxygen/src/hud/util.rs index c66f332cfd..5f9f15142b 100644 --- a/voxygen/src/hud/util.rs +++ b/voxygen/src/hud/util.rs @@ -132,7 +132,8 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> String { | BuffKind::Crippled | BuffKind::Frenzied | BuffKind::Frozen - | BuffKind::Wet => continue, + | BuffKind::Wet + | BuffKind::Ensnared => continue, }; write!(&mut description, "{}", buff_desc).unwrap(); @@ -156,7 +157,8 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> String { | BuffKind::Crippled | BuffKind::Frenzied | BuffKind::Frozen - | BuffKind::Wet => continue, + | BuffKind::Wet + | BuffKind::Ensnared => continue, } } else if let BuffKind::Saturation | BuffKind::Regeneration = buff.kind { i18n.get("buff.text.every_second").to_string() From c162827f9767cac6858b1434fc0177cff1c7f445 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 20 Jun 2021 12:04:06 -0500 Subject: [PATCH 159/180] Harvester AI --- server/src/sys/agent.rs | 83 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index a11989aad7..475115cc36 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -122,6 +122,7 @@ pub enum Tactic { TidalWarrior, Yeti, Tornado, + Harvester, } #[derive(SystemData)] @@ -1663,6 +1664,7 @@ impl<'a> AgentData<'a> { "Tidal Warrior" => Tactic::TidalWarrior, "Tidal Totem" => Tactic::RadialTurret, "Yeti" => Tactic::Yeti, + "Harvester" => Tactic::Harvester, _ => Tactic::Melee, }, AbilitySpec::Tool(tool_kind) => tool_tactic(*tool_kind), @@ -1763,6 +1765,18 @@ impl<'a> AgentData<'a> { ), ) }, + Tactic::Harvester if matches!(self.char_state, CharacterState::BasicRanged(_)) => { + const PUMPKIN_SPEED: f32 = 30.0; + aim_projectile( + PUMPKIN_SPEED, + Vec3::new(self.pos.0.x, self.pos.0.y, self.pos.0.z + eye_offset), + Vec3::new( + tgt_data.pos.0.x, + tgt_data.pos.0.y, + tgt_data.pos.0.z + tgt_eye_offset, + ), + ) + }, _ => Dir::from_unnormalized( Vec3::new( tgt_data.pos.0.x, @@ -1942,6 +1956,9 @@ impl<'a> AgentData<'a> { Tactic::Yeti => { self.handle_yeti_attack(agent, controller, &attack_data, &tgt_data, &read_data) }, + Tactic::Harvester => { + self.handle_harvester_attack(agent, controller, &attack_data, &tgt_data, &read_data) + }, } } @@ -3693,6 +3710,72 @@ impl<'a> AgentData<'a> { self.path_toward_target(agent, controller, tgt_data, read_data, false, None); } + fn handle_harvester_attack( + &self, + agent: &mut Agent, + controller: &mut Controller, + attack_data: &AttackData, + tgt_data: &TargetData, + read_data: &ReadData, + ) { + const VINE_CREATION_THRESHOLD: f32 = 0.50; + const FIRE_BREATH_RANGE: f32 = 20.0; + const MAX_PUMPKIN_RANGE: f32 = 50.0; + let health_fraction = self.health.map_or(0.5, |h| h.fraction()); + + if health_fraction < VINE_CREATION_THRESHOLD && !agent.action_state.condition { + // Summon vines when reach threshold of health + controller + .actions + .push(ControlAction::basic_input(InputKind::Ability(0))); + + if matches!(self.char_state, CharacterState::SpriteSummon(c) if matches!(c.stage_section, StageSection::Recover)) + { + agent.action_state.condition = true; + } + } else if attack_data.dist_sqrd < FIRE_BREATH_RANGE.powi(2) { + if matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs(10)) + { + // Keep breathing fire if close enough and have not been breathing for more than + // 10 seconds + controller + .actions + .push(ControlAction::basic_input(InputKind::Secondary)); + } else if attack_data.in_min_range() && attack_data.angle < 60.0 { + // Scythe them if they're in range and angle + controller + .actions + .push(ControlAction::basic_input(InputKind::Primary)); + } else if attack_data.angle < 30.0 + && can_see_tgt( + &*read_data.terrain, + self.pos, + tgt_data.pos, + attack_data.dist_sqrd, + ) + { + // Start breathing fire at them if close enough, in angle, and can see target + controller + .actions + .push(ControlAction::basic_input(InputKind::Secondary)); + } + } else if attack_data.dist_sqrd < MAX_PUMPKIN_RANGE.powi(2) + && can_see_tgt( + &*read_data.terrain, + self.pos, + tgt_data.pos, + attack_data.dist_sqrd, + ) + { + // Throw a pumpkin at them if close enough and can see them + controller + .actions + .push(ControlAction::basic_input(InputKind::Ability(1))); + } + // Always attempt to path towards target + self.path_toward_target(agent, controller, tgt_data, read_data, false, None); + } + fn follow( &self, agent: &mut Agent, From 47cc040301fb9af9200b3ef3d5e74c57ce1003ee Mon Sep 17 00:00:00 2001 From: Snowram Date: Mon, 21 Jun 2021 01:31:28 +0200 Subject: [PATCH 160/180] Adds SpriteSummon animation --- .../custom/harvester/ensnaringvines.ron | 6 +- voxygen/anim/src/biped_large/mod.rs | 4 +- voxygen/anim/src/biped_large/spritesummon.rs | 144 ++++++++++++++++++ voxygen/src/scene/figure/mod.rs | 28 ++++ 4 files changed, 178 insertions(+), 4 deletions(-) create mode 100644 voxygen/anim/src/biped_large/spritesummon.rs diff --git a/assets/common/abilities/custom/harvester/ensnaringvines.ron b/assets/common/abilities/custom/harvester/ensnaringvines.ron index ede5f831dc..e4e5fd979d 100644 --- a/assets/common/abilities/custom/harvester/ensnaringvines.ron +++ b/assets/common/abilities/custom/harvester/ensnaringvines.ron @@ -1,7 +1,7 @@ SpriteSummon( - buildup_duration: 0.3, - cast_duration: 1.0, - recover_duration: 0.2, + buildup_duration: 0.6, + cast_duration: 0.4, + recover_duration: 0.3, sprite: EnsnaringVines, summon_distance: (0, 25), ) \ No newline at end of file diff --git a/voxygen/anim/src/biped_large/mod.rs b/voxygen/anim/src/biped_large/mod.rs index cee364ed3b..9597f8f2e7 100644 --- a/voxygen/anim/src/biped_large/mod.rs +++ b/voxygen/anim/src/biped_large/mod.rs @@ -15,6 +15,7 @@ pub mod shockwave; pub mod shoot; pub mod spin; pub mod spinmelee; +pub mod spritesummon; pub mod stunned; pub mod summon; pub mod wield; @@ -26,7 +27,8 @@ pub use self::{ equip::EquipAnimation, idle::IdleAnimation, jump::JumpAnimation, leapmelee::LeapAnimation, run::RunAnimation, selfbuff::SelfBuffAnimation, shockwave::ShockwaveAnimation, shoot::ShootAnimation, spin::SpinAnimation, spinmelee::SpinMeleeAnimation, - stunned::StunnedAnimation, summon::SummonAnimation, wield::WieldAnimation, + spritesummon::SpriteSummonAnimation, stunned::StunnedAnimation, summon::SummonAnimation, + wield::WieldAnimation, }; use super::{make_bone, vek::*, FigureBoneData, Skeleton}; diff --git a/voxygen/anim/src/biped_large/spritesummon.rs b/voxygen/anim/src/biped_large/spritesummon.rs new file mode 100644 index 0000000000..9a7e918e8e --- /dev/null +++ b/voxygen/anim/src/biped_large/spritesummon.rs @@ -0,0 +1,144 @@ +use super::{ + super::{vek::*, Animation}, + BipedLargeSkeleton, SkeletonAttr, +}; +use common::{ + comp::item::{AbilitySpec, ToolKind}, + states::utils::StageSection, +}; +use std::f32::consts::PI; + +pub struct SpriteSummonAnimation; + +type SpriteSummonAnimationDependency<'a> = ( + (Option, Option<&'a AbilitySpec>), + (Option, Option<&'a AbilitySpec>), + f32, + f32, + Option, +); +impl Animation for SpriteSummonAnimation { + type Dependency<'a> = SpriteSummonAnimationDependency<'a>; + type Skeleton = BipedLargeSkeleton; + + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"biped_large_sprite_summon\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "biped_large_sprite_summon")] + #[allow(clippy::single_match)] // TODO: Pending review in #587 + fn update_skeleton_inner<'a>( + skeleton: &Self::Skeleton, + ( + (active_tool_kind, active_tool_spec), + _second_tool_kind, + _global_time, + velocity, + stage_section, + ): Self::Dependency<'a>, + anim_time: f32, + rate: &mut f32, + s_a: &SkeletonAttr, + ) -> Self::Skeleton { + *rate = 1.0; + let mut next = (*skeleton).clone(); + + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0); + + next.hand_l.position = Vec3::new(0.0, 0.0, s_a.grip.0); + next.hand_r.position = Vec3::new(0.0, 0.0, s_a.grip.0); + + next.hand_l.orientation = Quaternion::rotation_x(0.0); + next.hand_r.orientation = Quaternion::rotation_x(0.0); + + match active_tool_kind { + Some(ToolKind::Natural) => { + if let Some(AbilitySpec::Custom(spec)) = active_tool_spec { + match spec.as_str() { + "Harvester" => { + let (move1, move1pow, move2, move3) = match stage_section { + Some(StageSection::Buildup) => { + (anim_time, anim_time.powf(0.1), 0.0, 0.0) + }, + Some(StageSection::Cast) => { + (1.0, 1.0, (anim_time.powf(4.0) * 80.0).min(1.0), 0.0) + }, + Some(StageSection::Recover) => (1.0, 1.0, 1.0, anim_time), + _ => (0.0, 0.0, 0.0, 0.0), + }; + + let speed = Vec2::::from(velocity).magnitude(); + + let pullback = 1.0 - move3; + let move1 = move1 * pullback; + let move1pow = move1pow * pullback; + let move2 = move2 * pullback; + + next.head.orientation = Quaternion::rotation_x(move1 * 0.2); + next.jaw.position = Vec3::new(0.0, s_a.jaw.0, s_a.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(move2 * -0.3); + + let twist = move1 * 0.8 + move3 * -0.8; + next.upper_torso.position = Vec3::new( + 0.0, + s_a.upper_torso.0, + s_a.upper_torso.1 + move1 * 1.0 + move2 * -1.0, + ); + next.upper_torso.orientation = + Quaternion::rotation_x(move1 * 0.8 + move2 * -1.1) + * Quaternion::rotation_z( + twist * -0.2 + move1 * -0.1 + move2 * 0.3, + ); + + next.lower_torso.orientation = + Quaternion::rotation_x(move1 * -0.8 + move2 * 1.1) + * Quaternion::rotation_z(-twist + move1 * 0.4); + + next.control_l.position = Vec3::new(1.0, 2.0, 8.0); + next.control_r.position = Vec3::new(1.0, 1.0, -2.0); + + next.control.position = Vec3::new( + -7.0 + move1pow * 7.0, + 0.0 + s_a.grip.0 / 1.0 + move1pow * 12.0, + -s_a.grip.0 / 0.8 + move1pow * 20.0 + move2 * -3.0, + ); + + next.control_l.orientation = + Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_z(PI); + next.control_r.orientation = Quaternion::rotation_x(PI / 2.0 + 0.2) + * Quaternion::rotation_y(-1.0) + * Quaternion::rotation_z(0.0); + + next.control.orientation = + Quaternion::rotation_x(-1.4 + move1pow * 2.2 + move2 * -0.6) + * Quaternion::rotation_y(-PI); + + next.shoulder_l.position = + Vec3::new(-s_a.shoulder.0, s_a.shoulder.1, s_a.shoulder.2); + next.shoulder_l.orientation = + Quaternion::rotation_x(-0.4 + move1pow * 1.6); + next.shoulder_r.orientation = Quaternion::rotation_y(0.4) + * Quaternion::rotation_x(0.4 + move1pow * 1.0); + + if speed == 0.0 { + next.leg_l.orientation = + Quaternion::rotation_x(move1 * 0.8 + move2 * -0.8); + + next.foot_l.position = Vec3::new( + -s_a.foot.0, + s_a.foot.1, + s_a.foot.2 + move1 * 4.0 + move2 * -4.0, + ); + next.foot_l.orientation = + Quaternion::rotation_x(move1 * -0.6 + move2 * 0.6); + } + }, + _ => {}, + } + } + }, + _ => {}, + } + next + } +} diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 3a5ec2de86..8a2699c75c 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -4274,6 +4274,34 @@ impl FigureMgr { skeleton_attr, ) }, + CharacterState::SpriteSummon(s) => { + let stage_time = s.timer.as_secs_f32(); + let stage_progress = match s.stage_section { + StageSection::Buildup => { + stage_time / s.static_data.buildup_duration.as_secs_f32() + }, + StageSection::Cast => { + stage_time / s.static_data.cast_duration.as_secs_f32() + }, + StageSection::Recover => { + stage_time / s.static_data.recover_duration.as_secs_f32() + }, + _ => 0.0, + }; + anim::biped_large::SpriteSummonAnimation::update_skeleton( + &target_base, + ( + (active_tool_kind, active_tool_spec), + (second_tool_kind, second_tool_spec), + time, + rel_vel.magnitude(), + Some(s.stage_section), + ), + stage_progress, + &mut state_animation_rate, + skeleton_attr, + ) + }, // TODO! _ => target_base, }; From aac24ad60108ffe78655fa0ab6638e1454527912 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 21 Jun 2021 20:50:31 -0500 Subject: [PATCH 161/180] Added ensnared debuff icon. --- .../element/de_buffs/debuff_ensnared_0.png | 3 +++ common/systems/src/buff.rs | 23 ++++++++----------- voxygen/src/hud/img_ids.rs | 1 + voxygen/src/hud/mod.rs | 2 +- 4 files changed, 15 insertions(+), 14 deletions(-) create mode 100644 assets/voxygen/element/de_buffs/debuff_ensnared_0.png diff --git a/assets/voxygen/element/de_buffs/debuff_ensnared_0.png b/assets/voxygen/element/de_buffs/debuff_ensnared_0.png new file mode 100644 index 0000000000..64eba638bc --- /dev/null +++ b/assets/voxygen/element/de_buffs/debuff_ensnared_0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3d8e1eaeb32e0c43054a92d6e045e0a2db93a15fc030238572efe8d119cd9466 +size 198 diff --git a/common/systems/src/buff.rs b/common/systems/src/buff.rs index f25dda0a68..364ee9832f 100644 --- a/common/systems/src/buff.rs +++ b/common/systems/src/buff.rs @@ -65,6 +65,7 @@ impl<'a> System<'a> for Sys { physics_state.on_ground.and_then(|b| b.get_sprite()), Some(SpriteKind::EnsnaringVines) ) { + // If on ensnaring vines, apply ensnared debuff server_emitter.emit(ServerEvent::Buff { entity, buff_change: BuffChange::Add(Buff::new( @@ -82,6 +83,7 @@ impl<'a> System<'a> for Sys { .. }) ) { + // If in lava fluid, apply burning debuff server_emitter.emit(ServerEvent::Buff { entity, buff_change: BuffChange::Add(Buff::new( @@ -97,13 +99,13 @@ impl<'a> System<'a> for Sys { kind: LiquidKind::Water, .. }) - ) { - if buff_comp.kinds.contains_key(&BuffKind::Burning) { - server_emitter.emit(ServerEvent::Buff { - entity, - buff_change: BuffChange::RemoveByKind(BuffKind::Burning), - }); - } + ) && buff_comp.kinds.contains_key(&BuffKind::Burning) + { + // If in water fluid and currently burning, remove burning debuffs + server_emitter.emit(ServerEvent::Buff { + entity, + buff_change: BuffChange::RemoveByKind(BuffKind::Burning), + }); } } @@ -284,12 +286,7 @@ impl<'a> System<'a> for Sys { } } -fn tick_buff( - id: u64, - buff: &mut Buff, - dt: f32, - mut expire_buff: impl FnMut(u64), -) { +fn tick_buff(id: u64, buff: &mut Buff, dt: f32, mut expire_buff: impl FnMut(u64)) { // If a buff is recently applied from an aura, do not tick duration if buff .cat_ids diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index 6b5b5cfec0..4c0ee48503 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -596,6 +596,7 @@ image_ids! { debuff_crippled_0: "voxygen.element.de_buffs.debuff_cripple_0", debuff_frozen_0: "voxygen.element.de_buffs.debuff_frozen_0", debuff_wet_0: "voxygen.element.de_buffs.debuff_wet_0", + debuff_ensnared_0: "voxygen.element.de_buffs.debuff_ensnared_0", // Animation Frames // Buff Frame diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 5d1b45e4e0..b212622cbc 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -3822,7 +3822,7 @@ pub fn get_buff_image(buff: BuffKind, imgs: &Imgs) -> conrod_core::image::Id { BuffKind::Crippled { .. } => imgs.debuff_crippled_0, BuffKind::Frozen { .. } => imgs.debuff_frozen_0, BuffKind::Wet { .. } => imgs.debuff_wet_0, - BuffKind::Ensnared { .. } => imgs.debuff_crippled_0, + BuffKind::Ensnared { .. } => imgs.debuff_ensnared_0, } } From 68c6a0a49511bba26b0de439230f39fbfaf02f8f Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 21 Jun 2021 22:50:16 -0500 Subject: [PATCH 162/180] Balance tweaks for harvester. --- .../abilities/custom/harvester/explodingpumpkin.ron | 4 ++-- .../common/abilities/custom/harvester/firebreath.ron | 6 +++--- common/src/states/sprite_summon.rs | 8 ++++---- server/src/sys/agent.rs | 12 +++++++++--- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/assets/common/abilities/custom/harvester/explodingpumpkin.ron b/assets/common/abilities/custom/harvester/explodingpumpkin.ron index 88b2b2d48a..ecea54a587 100644 --- a/assets/common/abilities/custom/harvester/explodingpumpkin.ron +++ b/assets/common/abilities/custom/harvester/explodingpumpkin.ron @@ -3,9 +3,9 @@ BasicRanged( buildup_duration: 0.5, recover_duration: 0.8, projectile: ExplodingPumpkin( - damage: 500.0, + damage: 200.0, knockback: 25.0, - radius: 10.0, + radius: 5.0, ), projectile_body: Object(Pumpkin), projectile_light: None, diff --git a/assets/common/abilities/custom/harvester/firebreath.ron b/assets/common/abilities/custom/harvester/firebreath.ron index eff08060f6..c94818ad01 100644 --- a/assets/common/abilities/custom/harvester/firebreath.ron +++ b/assets/common/abilities/custom/harvester/firebreath.ron @@ -2,8 +2,8 @@ BasicBeam( buildup_duration: 0.5, recover_duration: 0.5, beam_duration: 1.0, - damage: 35, - tick_rate: 3.0, + damage: 30, + tick_rate: 1.5, range: 20.0, max_angle: 15.0, damage_effect: Some(Buff(( @@ -15,6 +15,6 @@ BasicBeam( energy_regen: 0, energy_drain: 0, orientation_behavior: Normal, - ori_rate: 0.6, + ori_rate: 0.2, specifier: Flamethrower, ) diff --git a/common/src/states/sprite_summon.rs b/common/src/states/sprite_summon.rs index 75bfcccf60..0139c4fa15 100644 --- a/common/src/states/sprite_summon.rs +++ b/common/src/states/sprite_summon.rs @@ -88,12 +88,12 @@ impl CharacterBehavior for Data { data.pos.0.z.floor() as i32, ); - // Check for collision in z up to 25 blocks up or down + // Check for collision in z up to 10 blocks up or down let obstacle_z = data .terrain .ray( - sprite_pos.map(|x| x as f32 + 0.5) + Vec3::unit_z() * 25.0, - sprite_pos.map(|x| x as f32 + 0.5) - Vec3::unit_z() * 25.0, + sprite_pos.map(|x| x as f32 + 0.5) + Vec3::unit_z() * 10.0, + sprite_pos.map(|x| x as f32 + 0.5) - Vec3::unit_z() * 10.0, ) .until(|b| { // Until reaching a solid block that is not the created sprite @@ -104,7 +104,7 @@ impl CharacterBehavior for Data { .0; // z height relative to caster - let z = sprite_pos.z + (25.5 - obstacle_z).ceil() as i32; + let z = sprite_pos.z + (10.5 - obstacle_z).ceil() as i32; // Location sprite will be created let sprite_pos = Vec3::new(sprite_pos.x as i32, sprite_pos.y as i32, z); diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 475115cc36..54d9fa04c2 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -3734,10 +3734,16 @@ impl<'a> AgentData<'a> { agent.action_state.condition = true; } } else if attack_data.dist_sqrd < FIRE_BREATH_RANGE.powi(2) { - if matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs(10)) + if matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs(5)) + && can_see_tgt( + &*read_data.terrain, + self.pos, + tgt_data.pos, + attack_data.dist_sqrd, + ) { - // Keep breathing fire if close enough and have not been breathing for more than - // 10 seconds + // Keep breathing fire if close enough, can see target, and have not been + // breathing for more than 5 seconds controller .actions .push(ControlAction::basic_input(InputKind::Secondary)); From f0c5ed1397b4168fffdcb061600436c65222a453 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 21 Jun 2021 23:12:39 -0500 Subject: [PATCH 163/180] Added sparseness field to sprite summon. --- .../custom/harvester/ensnaringvines.ron | 1 + common/src/comp/ability.rs | 3 + common/src/states/sprite_summon.rs | 72 +++++++++++-------- 3 files changed, 45 insertions(+), 31 deletions(-) diff --git a/assets/common/abilities/custom/harvester/ensnaringvines.ron b/assets/common/abilities/custom/harvester/ensnaringvines.ron index e4e5fd979d..b040d57b6a 100644 --- a/assets/common/abilities/custom/harvester/ensnaringvines.ron +++ b/assets/common/abilities/custom/harvester/ensnaringvines.ron @@ -4,4 +4,5 @@ SpriteSummon( recover_duration: 0.3, sprite: EnsnaringVines, summon_distance: (0, 25), + sparseness: 0.5, ) \ No newline at end of file diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 72e31105f4..d8b1413fa7 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -301,6 +301,7 @@ pub enum CharacterAbility { recover_duration: f32, sprite: SpriteKind, summon_distance: (f32, f32), + sparseness: f64, }, } @@ -1811,6 +1812,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { recover_duration, sprite, summon_distance, + sparseness, } => CharacterState::SpriteSummon(sprite_summon::Data { static_data: sprite_summon::StaticData { buildup_duration: Duration::from_secs_f32(*buildup_duration), @@ -1818,6 +1820,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { recover_duration: Duration::from_secs_f32(*recover_duration), sprite: *sprite, summon_distance: *summon_distance, + sparseness: *sparseness, ability_info, }, timer: Duration::default(), diff --git a/common/src/states/sprite_summon.rs b/common/src/states/sprite_summon.rs index 0139c4fa15..1a3610e8bd 100644 --- a/common/src/states/sprite_summon.rs +++ b/common/src/states/sprite_summon.rs @@ -9,6 +9,7 @@ use crate::{ terrain::{Block, SpriteKind}, vol::ReadVol, }; +use rand::{thread_rng, Rng}; use serde::{Deserialize, Serialize}; use std::time::Duration; use vek::*; @@ -26,6 +27,8 @@ pub struct StaticData { pub sprite: SpriteKind, /// Range that sprites are created relative to the summonner pub summon_distance: (f32, f32), + /// Chance that sprite is not created on a particular square + pub sparseness: f64, /// Miscellaneous information about the ability pub ability_info: AbilityInfo, } @@ -77,43 +80,50 @@ impl CharacterBehavior for Data { let summon_distance = summon_distance.round() as i32; // Only summons sprites if summon distance is greater than achieved radius - if summon_distance > self.achieved_radius { + for radius in self.achieved_radius..=summon_distance { + // 1 added to make range correct, too lazy to add 1 to both variables above + let radius = radius + 1; // Creates a spiral iterator for the newly achieved radius - let spiral = Spiral2d::new().edge_radius(summon_distance); + let spiral = Spiral2d::new().edge_radius(radius); for point in spiral { - // The coordinates of where the sprite is created - let sprite_pos = Vec3::new( - data.pos.0.x.floor() as i32 + point.x, - data.pos.0.y.floor() as i32 + point.y, - data.pos.0.z.floor() as i32, - ); + // If square is not sparse, generate sprite + if !thread_rng().gen_bool(self.static_data.sparseness) { + // The coordinates of where the sprite is created + let sprite_pos = Vec3::new( + data.pos.0.x.floor() as i32 + point.x, + data.pos.0.y.floor() as i32 + point.y, + data.pos.0.z.floor() as i32, + ); - // Check for collision in z up to 10 blocks up or down - let obstacle_z = data - .terrain - .ray( - sprite_pos.map(|x| x as f32 + 0.5) + Vec3::unit_z() * 10.0, - sprite_pos.map(|x| x as f32 + 0.5) - Vec3::unit_z() * 10.0, - ) - .until(|b| { - // Until reaching a solid block that is not the created sprite - Block::is_solid(b) - && b.get_sprite() != Some(self.static_data.sprite) - }) - .cast() - .0; + // Check for collision in z up to 10 blocks up or down + let obstacle_z = data + .terrain + .ray( + sprite_pos.map(|x| x as f32 + 0.5) + Vec3::unit_z() * 10.0, + sprite_pos.map(|x| x as f32 + 0.5) - Vec3::unit_z() * 10.0, + ) + .until(|b| { + // Until reaching a solid block that is not the created + // sprite + Block::is_solid(b) + && b.get_sprite() != Some(self.static_data.sprite) + }) + .cast() + .0; - // z height relative to caster - let z = sprite_pos.z + (10.5 - obstacle_z).ceil() as i32; + // z height relative to caster + let z = sprite_pos.z + (10.5 - obstacle_z).ceil() as i32; - // Location sprite will be created - let sprite_pos = Vec3::new(sprite_pos.x as i32, sprite_pos.y as i32, z); + // Location sprite will be created + let sprite_pos = + Vec3::new(sprite_pos.x as i32, sprite_pos.y as i32, z); - // Send server event to create sprite - update.server_events.push_front(ServerEvent::CreateSprite { - pos: sprite_pos, - sprite: self.static_data.sprite, - }); + // Send server event to create sprite + update.server_events.push_front(ServerEvent::CreateSprite { + pos: sprite_pos, + sprite: self.static_data.sprite, + }); + } } } From 645cc60dc04537dbf80deac321ebf84ed4ae24ef Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 23 Jun 2021 20:31:45 -0500 Subject: [PATCH 164/180] Balance tweaks. --- CHANGELOG.md | 1 + .../common/abilities/custom/harvester/ensnaringvines.ron | 2 +- assets/common/abilities/custom/harvester/firebreath.ron | 2 +- assets/common/abilities/custom/harvester/scythe.ron | 6 +++--- common/src/comp/body.rs | 7 ++++--- common/systems/src/buff.rs | 2 +- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28ecd811af..31b7a1ec1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Clay Golem uses shockwave only after specific fraction of health and other difficulty adjustments. - Made strafing slightly slower - Food now has limited regeneration strength but longer duration. +- Harvester boss now has new abilities and AI ### Removed diff --git a/assets/common/abilities/custom/harvester/ensnaringvines.ron b/assets/common/abilities/custom/harvester/ensnaringvines.ron index b040d57b6a..e050dc8241 100644 --- a/assets/common/abilities/custom/harvester/ensnaringvines.ron +++ b/assets/common/abilities/custom/harvester/ensnaringvines.ron @@ -4,5 +4,5 @@ SpriteSummon( recover_duration: 0.3, sprite: EnsnaringVines, summon_distance: (0, 25), - sparseness: 0.5, + sparseness: 0.67, ) \ No newline at end of file diff --git a/assets/common/abilities/custom/harvester/firebreath.ron b/assets/common/abilities/custom/harvester/firebreath.ron index c94818ad01..94f0a66de4 100644 --- a/assets/common/abilities/custom/harvester/firebreath.ron +++ b/assets/common/abilities/custom/harvester/firebreath.ron @@ -2,7 +2,7 @@ BasicBeam( buildup_duration: 0.5, recover_duration: 0.5, beam_duration: 1.0, - damage: 30, + damage: 40, tick_rate: 1.5, range: 20.0, max_angle: 15.0, diff --git a/assets/common/abilities/custom/harvester/scythe.ron b/assets/common/abilities/custom/harvester/scythe.ron index ac8f1cef3e..f299ddaf66 100644 --- a/assets/common/abilities/custom/harvester/scythe.ron +++ b/assets/common/abilities/custom/harvester/scythe.ron @@ -1,9 +1,9 @@ BasicMelee( energy_cost: 0, - buildup_duration: 0.6, + buildup_duration: 0.7, swing_duration: 0.1, - recover_duration: 0.5, - base_damage: 60, + recover_duration: 0.6, + base_damage: 70, base_poise_damage: 10, knockback: ( strength: 10.0, direction: Away), range: 4.0, diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 7117c1189c..623f164283 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -490,7 +490,7 @@ impl Body { biped_large::Species::Tidalwarrior => 16000, biped_large::Species::Yeti => 12000, biped_large::Species::Minotaur => 30000, - biped_large::Species::Harvester => 3000, + biped_large::Species::Harvester => 5000, biped_large::Species::Blueoni => 2400, biped_large::Species::Redoni => 2400, _ => 1200, @@ -609,12 +609,12 @@ impl Body { biped_large::Species::Mountaintroll => 60, biped_large::Species::Swamptroll => 60, biped_large::Species::Dullahan => 120, - biped_large::Species::Yeti => 0, - biped_large::Species::Harvester => 80, // Boss enemies have their health set, not adjusted by level. biped_large::Species::Mindflayer => 0, biped_large::Species::Minotaur => 0, biped_large::Species::Tidalwarrior => 0, + biped_large::Species::Yeti => 0, + biped_large::Species::Harvester => 0, _ => 100, }, Body::BipedSmall(_) => 10, @@ -677,6 +677,7 @@ impl Body { biped_large::Species::Minotaur => 3.2, biped_large::Species::Tidalwarrior => 2.25, biped_large::Species::Yeti => 2.0, + biped_large::Species::Harvester => 2.4, _ => 1.0, }, Body::Golem(g) => match g.species { diff --git a/common/systems/src/buff.rs b/common/systems/src/buff.rs index 364ee9832f..8e32991f80 100644 --- a/common/systems/src/buff.rs +++ b/common/systems/src/buff.rs @@ -70,7 +70,7 @@ impl<'a> System<'a> for Sys { entity, buff_change: BuffChange::Add(Buff::new( BuffKind::Ensnared, - BuffData::new(1.5, Some(Duration::from_secs_f32(1.0))), + BuffData::new(1.0, Some(Duration::from_secs_f32(1.0))), Vec::new(), BuffSource::World, )), From 1eb9c165be61bf343ba4f993e7c4f5118b68db8c Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Thu, 24 Jun 2021 19:52:38 +0300 Subject: [PATCH 165/180] (uk_UA) small fitting fix --- assets/voxygen/i18n/uk_UA/buff.ron | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/assets/voxygen/i18n/uk_UA/buff.ron b/assets/voxygen/i18n/uk_UA/buff.ron index 75db881df0..0d30744b88 100644 --- a/assets/voxygen/i18n/uk_UA/buff.ron +++ b/assets/voxygen/i18n/uk_UA/buff.ron @@ -31,13 +31,13 @@ "buff.title.crippled": "Калічення", "buff.desc.crippled": "Ваші рухи дуже скуті через отримані травми.", // Stats - "buff.stat.health": "Відновлює {str_total} Здоров'я", - "buff.stat.increase_max_health": "Піднімає Максимальне Здоров'я на {strength}", - "buff.stat.increase_max_stamina": "Піднімає Максимальну Енергію на {strength}", + "buff.stat.health": "Відновлює {str_total} ОЗ", + "buff.stat.increase_max_health": "Підвищує Максимальне Здоров'я на {strength}", + "buff.stat.increase_max_stamina": "Підвищує Максимальну Енергію на {strength}", "buff.stat.invulnerability": "Дає невразливість", // Text - "buff.text.for_seconds": "протягом {dur_secs} секунд", - "buff.text.over_seconds": "впродовж {dur_secs} секунд", + "buff.text.for_seconds": "протягом {dur_secs} сек.", + "buff.text.over_seconds": "впродовж {dur_secs} сек.", }, From 87ca00fac048773bf6afcf96183e15cff7d4608a Mon Sep 17 00:00:00 2001 From: Snowram Date: Mon, 14 Jun 2021 01:39:43 +0200 Subject: [PATCH 166/180] Adds new species --- assets/common/npc_names.ron | 14 +- .../voxel/biped_large_central_manifest.ron | 52 +++++++ .../voxel/biped_large_lateral_manifest.ron | 68 +++++++++ .../voxel/bird_medium_central_manifest.ron | 18 +-- .../voxel/bird_medium_lateral_manifest.ron | 16 +-- .../voxel/npc/cultist_warlord/male/foot_l.vox | 3 + .../voxel/npc/cultist_warlord/male/foot_r.vox | 3 + .../voxel/npc/cultist_warlord/male/hand_l.vox | 3 + .../voxel/npc/cultist_warlord/male/hand_r.vox | 3 + .../voxel/npc/cultist_warlord/male/head.vox | 3 + .../voxel/npc/cultist_warlord/male/leg_l.vox | 3 + .../voxel/npc/cultist_warlord/male/leg_r.vox | 3 + .../npc/cultist_warlord/male/shoulder_l.vox | 3 + .../npc/cultist_warlord/male/shoulder_r.vox | 3 + .../npc/cultist_warlord/male/torso_lower.vox | 3 + .../npc/cultist_warlord/male/torso_upper.vox | 3 + assets/voxygen/voxel/npc/duck/female/head.vox | 4 +- .../voxygen/voxel/npc/duck/female/leg_r.vox | 4 +- assets/voxygen/voxel/npc/duck/female/tail.vox | 4 +- .../voxygen/voxel/npc/duck/female/torso.vox | 4 +- assets/voxygen/voxel/npc/duck/female/wing.vox | 4 +- assets/voxygen/voxel/npc/duck/male/head.vox | 4 +- assets/voxygen/voxel/npc/duck/male/leg_r.vox | 4 +- assets/voxygen/voxel/npc/duck/male/tail.vox | 4 +- assets/voxygen/voxel/npc/duck/male/torso.vox | 4 +- assets/voxygen/voxel/npc/duck/male/wing.vox | 4 +- .../voxel/npc/mammoth/male/foot_br.vox | 3 + .../voxel/npc/mammoth/male/foot_fr.vox | 3 + .../voxygen/voxel/npc/mammoth/male/head.vox | 3 + assets/voxygen/voxel/npc/mammoth/male/jaw.vox | 3 + .../voxygen/voxel/npc/mammoth/male/leg_br.vox | 3 + .../voxygen/voxel/npc/mammoth/male/leg_fr.vox | 3 + .../voxygen/voxel/npc/mammoth/male/neck.vox | 3 + .../voxygen/voxel/npc/mammoth/male/tail.vox | 3 + .../voxel/npc/mammoth/male/torso_back.vox | 3 + .../voxel/npc/mammoth/male/torso_front.vox | 3 + .../voxel/npc/ngoubou/male/foot_br.vox | 3 + .../voxel/npc/ngoubou/male/foot_fr.vox | 3 + .../voxygen/voxel/npc/ngoubou/male/head.vox | 3 + assets/voxygen/voxel/npc/ngoubou/male/jaw.vox | 3 + .../voxygen/voxel/npc/ngoubou/male/leg_br.vox | 3 + .../voxygen/voxel/npc/ngoubou/male/leg_fr.vox | 3 + .../voxygen/voxel/npc/ngoubou/male/neck.vox | 3 + .../voxygen/voxel/npc/ngoubou/male/tail.vox | 3 + .../voxel/npc/ngoubou/male/torso_back.vox | 3 + .../voxel/npc/ngoubou/male/torso_front.vox | 3 + .../quadruped_medium_central_manifest.ron | 120 ++++++++++++++++ .../quadruped_medium_lateral_manifest.ron | 136 ++++++++++++++++++ common/src/comp/body.rs | 6 + common/src/comp/body/biped_large.rs | 6 +- common/src/comp/body/quadruped_medium.rs | 10 +- common/src/comp/inventory/loadout_builder.rs | 10 +- common/src/states/utils.rs | 2 + voxygen/anim/src/biped_large/mod.rs | 10 ++ voxygen/anim/src/bird_medium/mod.rs | 10 +- voxygen/anim/src/quadruped_medium/mod.rs | 25 ++++ 56 files changed, 587 insertions(+), 49 deletions(-) create mode 100644 assets/voxygen/voxel/npc/cultist_warlord/male/foot_l.vox create mode 100644 assets/voxygen/voxel/npc/cultist_warlord/male/foot_r.vox create mode 100644 assets/voxygen/voxel/npc/cultist_warlord/male/hand_l.vox create mode 100644 assets/voxygen/voxel/npc/cultist_warlord/male/hand_r.vox create mode 100644 assets/voxygen/voxel/npc/cultist_warlord/male/head.vox create mode 100644 assets/voxygen/voxel/npc/cultist_warlord/male/leg_l.vox create mode 100644 assets/voxygen/voxel/npc/cultist_warlord/male/leg_r.vox create mode 100644 assets/voxygen/voxel/npc/cultist_warlord/male/shoulder_l.vox create mode 100644 assets/voxygen/voxel/npc/cultist_warlord/male/shoulder_r.vox create mode 100644 assets/voxygen/voxel/npc/cultist_warlord/male/torso_lower.vox create mode 100644 assets/voxygen/voxel/npc/cultist_warlord/male/torso_upper.vox create mode 100644 assets/voxygen/voxel/npc/mammoth/male/foot_br.vox create mode 100644 assets/voxygen/voxel/npc/mammoth/male/foot_fr.vox create mode 100644 assets/voxygen/voxel/npc/mammoth/male/head.vox create mode 100644 assets/voxygen/voxel/npc/mammoth/male/jaw.vox create mode 100644 assets/voxygen/voxel/npc/mammoth/male/leg_br.vox create mode 100644 assets/voxygen/voxel/npc/mammoth/male/leg_fr.vox create mode 100644 assets/voxygen/voxel/npc/mammoth/male/neck.vox create mode 100644 assets/voxygen/voxel/npc/mammoth/male/tail.vox create mode 100644 assets/voxygen/voxel/npc/mammoth/male/torso_back.vox create mode 100644 assets/voxygen/voxel/npc/mammoth/male/torso_front.vox create mode 100644 assets/voxygen/voxel/npc/ngoubou/male/foot_br.vox create mode 100644 assets/voxygen/voxel/npc/ngoubou/male/foot_fr.vox create mode 100644 assets/voxygen/voxel/npc/ngoubou/male/head.vox create mode 100644 assets/voxygen/voxel/npc/ngoubou/male/jaw.vox create mode 100644 assets/voxygen/voxel/npc/ngoubou/male/leg_br.vox create mode 100644 assets/voxygen/voxel/npc/ngoubou/male/leg_fr.vox create mode 100644 assets/voxygen/voxel/npc/ngoubou/male/neck.vox create mode 100644 assets/voxygen/voxel/npc/ngoubou/male/tail.vox create mode 100644 assets/voxygen/voxel/npc/ngoubou/male/torso_back.vox create mode 100644 assets/voxygen/voxel/npc/ngoubou/male/torso_front.vox diff --git a/assets/common/npc_names.ron b/assets/common/npc_names.ron index ca8be884c0..c898788d85 100644 --- a/assets/common/npc_names.ron +++ b/assets/common/npc_names.ron @@ -546,7 +546,15 @@ snowleopard: ( keyword: "snowleopard", generic: "Snow Leopard" - ) + ), + mammoth: ( + keyword: "mammoth", + generic: "Mammoth" + ), + ngoubou: ( + keyword: "ngoubou", + generic: "Ngoubou" + ), ) ), quadruped_small: ( @@ -886,6 +894,10 @@ keyword: "oni_red", generic: "Red Oni" ), + cultist_warlord: ( + keyword: "cultist_warlord", + generic: "Cultist Warlord" + ), ) ), golem: ( diff --git a/assets/voxygen/voxel/biped_large_central_manifest.ron b/assets/voxygen/voxel/biped_large_central_manifest.ron index 4d2830c1b2..5f489af07c 100644 --- a/assets/voxygen/voxel/biped_large_central_manifest.ron +++ b/assets/voxygen/voxel/biped_large_central_manifest.ron @@ -936,4 +936,56 @@ central: ("armor.empty"), ) ), + (Cultistwarlord, Male): ( + head: ( + offset: (-10.0, -6.0, -9.0), + central: ("npc.cultist_warlord.male.head"), + ), + torso_upper: ( + offset: (-8.0, -6.0, -6.0), + central: ("npc.cultist_warlord.male.torso_upper"), + ), + torso_lower: ( + offset: (-7.0, -5.0, -9.0), + central: ("npc.cultist_warlord.male.torso_lower"), + ), + jaw: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + tail: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + second: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), + (Cultistwarlord, Female): ( + head: ( + offset: (-10.0, -6.0, -9.0), + central: ("npc.cultist_warlord.male.head"), + ), + torso_upper: ( + offset: (-8.0, -6.0, -6.0), + central: ("npc.cultist_warlord.male.torso_upper"), + ), + torso_lower: ( + offset: (-7.0, -5.0, -9.0), + central: ("npc.cultist_warlord.male.torso_lower"), + ), + jaw: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + tail: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + second: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), }) diff --git a/assets/voxygen/voxel/biped_large_lateral_manifest.ron b/assets/voxygen/voxel/biped_large_lateral_manifest.ron index 8c8f0c046b..184d2a10a2 100644 --- a/assets/voxygen/voxel/biped_large_lateral_manifest.ron +++ b/assets/voxygen/voxel/biped_large_lateral_manifest.ron @@ -1223,4 +1223,72 @@ lateral: ("npc.oni_red.male.foot_r"), ) ), + (Cultistwarlord, Male): ( + shoulder_l: ( + offset: (-6.0, -5.5, -6.0), + lateral: ("npc.cultist_warlord.male.shoulder_l"), + ), + shoulder_r: ( + offset: (-6.0, -5.5, -6.0), + lateral: ("npc.cultist_warlord.male.shoulder_r"), + ), + hand_l: ( + offset: (-3.5, -3.5, -10.0), + lateral: ("npc.cultist_warlord.male.hand_l"), + ), + hand_r: ( + offset: (-3.5, -3.5, -10.0), + lateral: ("npc.cultist_warlord.male.hand_r"), + ), + leg_l: ( + offset: (-1.5, -2.0, -2.5), + lateral: ("npc.cultist_warlord.male.leg_l"), + ), + leg_r: ( + offset: (-1.5, -2.0, -2.5), + lateral: ("npc.cultist_warlord.male.leg_r"), + ), + foot_l: ( + offset: (-2.5, -4.0, -4.0), + lateral: ("npc.cultist_warlord.male.foot_l"), + ), + foot_r: ( + offset: (-2.5, -4.0, -4.0), + lateral: ("npc.cultist_warlord.male.foot_r"), + ) + ), + (Cultistwarlord, Female): ( + shoulder_l: ( + offset: (-6.0, -5.5, -6.0), + lateral: ("npc.cultist_warlord.male.shoulder_l"), + ), + shoulder_r: ( + offset: (-6.0, -5.5, -6.0), + lateral: ("npc.cultist_warlord.male.shoulder_r"), + ), + hand_l: ( + offset: (-3.5, -3.5, -10.0), + lateral: ("npc.cultist_warlord.male.hand_l"), + ), + hand_r: ( + offset: (-3.5, -3.5, -10.0), + lateral: ("npc.cultist_warlord.male.hand_r"), + ), + leg_l: ( + offset: (-1.5, -2.0, -2.5), + lateral: ("npc.cultist_warlord.male.leg_l"), + ), + leg_r: ( + offset: (-1.5, -2.0, -2.5), + lateral: ("npc.cultist_warlord.male.leg_r"), + ), + foot_l: ( + offset: (-2.5, -4.0, -4.0), + lateral: ("npc.cultist_warlord.male.foot_l"), + ), + foot_r: ( + offset: (-2.5, -4.0, -4.0), + lateral: ("npc.cultist_warlord.male.foot_r"), + ) + ), }) \ No newline at end of file diff --git a/assets/voxygen/voxel/bird_medium_central_manifest.ron b/assets/voxygen/voxel/bird_medium_central_manifest.ron index d24a0be621..de8da461ad 100644 --- a/assets/voxygen/voxel/bird_medium_central_manifest.ron +++ b/assets/voxygen/voxel/bird_medium_central_manifest.ron @@ -1,30 +1,30 @@ ({ (Duck, Male): ( head: ( - offset: (-1.5, -2.0, -1.5), + offset: (-2.0, -1.5, -2.5), central: ("npc.duck.male.head"), ), torso: ( - offset: (-2.5, -3.0, -2.0), + offset: (-3.0, -4.5, -3.0), central: ("npc.duck.male.torso"), ), tail: ( - offset: (-1.5, -0.5, -2.5), + offset: (-2.0, -1.5, -3.0), central: ("npc.duck.male.tail"), ) ), (Duck, Female): ( head: ( - offset: (-1.5, -2.0, -1.5), - central: ("npc.duck.female.head"), + offset: (-2.0, -1.5, -2.5), + central: ("npc.duck.male.head"), ), torso: ( - offset: (-2.5, -3.0, -2.0), - central: ("npc.duck.female.torso"), + offset: (-3.0, -4.5, -3.0), + central: ("npc.duck.male.torso"), ), tail: ( - offset: (-1.5, -0.5, -2.5), - central: ("npc.duck.female.tail"), + offset: (-2.0, -1.5, -3.0), + central: ("npc.duck.male.tail"), ) ), (Chicken, Male): ( diff --git a/assets/voxygen/voxel/bird_medium_lateral_manifest.ron b/assets/voxygen/voxel/bird_medium_lateral_manifest.ron index 95c4027cc7..14a20071d7 100644 --- a/assets/voxygen/voxel/bird_medium_lateral_manifest.ron +++ b/assets/voxygen/voxel/bird_medium_lateral_manifest.ron @@ -1,37 +1,37 @@ ({ (Duck, Male): ( wing_l: ( - offset: (-0.5, -2.5, -3.0), + offset: (-0.5, -4.0, -4.0), lateral: ("npc.duck.male.wing"), ), wing_r: ( - offset: (-0.5, -2.5, -3.0), + offset: (-0.5, -4.0, -4.0), lateral: ("npc.duck.male.wing"), ), foot_l: ( - offset: (-1.0, 0.0, -4.0), + offset: (-1.5, -1.5, -4.0), lateral: ("npc.duck.male.leg_r"), ), foot_r: ( - offset: (-1.0, 0.0, -4.0), + offset: (-1.5, -1.5, -4.0), lateral: ("npc.duck.male.leg_r"), ) ), (Duck, Female): ( wing_l: ( - offset: (-0.5, -2.5, -3.0), + offset: (-0.5, -4.0, -4.0), lateral: ("npc.duck.female.wing"), ), wing_r: ( - offset: (-0.5, -2.5, -3.0), + offset: (-0.5, -4.0, -4.0), lateral: ("npc.duck.female.wing"), ), foot_l: ( - offset: (-1.0, 0.0, -4.0), + offset: (-1.5, -1.5, -4.0), lateral: ("npc.duck.female.leg_r"), ), foot_r: ( - offset: (-1.0, 0.0, -4.0), + offset: (-1.5, -1.5, -4.0), lateral: ("npc.duck.female.leg_r"), ) ), diff --git a/assets/voxygen/voxel/npc/cultist_warlord/male/foot_l.vox b/assets/voxygen/voxel/npc/cultist_warlord/male/foot_l.vox new file mode 100644 index 0000000000..e9c9cfb02e --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlord/male/foot_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75369587a0b1d86769209181f7af5668001092d24e318b7f39a3219821c163cb +size 1748 diff --git a/assets/voxygen/voxel/npc/cultist_warlord/male/foot_r.vox b/assets/voxygen/voxel/npc/cultist_warlord/male/foot_r.vox new file mode 100644 index 0000000000..e9c9cfb02e --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlord/male/foot_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75369587a0b1d86769209181f7af5668001092d24e318b7f39a3219821c163cb +size 1748 diff --git a/assets/voxygen/voxel/npc/cultist_warlord/male/hand_l.vox b/assets/voxygen/voxel/npc/cultist_warlord/male/hand_l.vox new file mode 100644 index 0000000000..f26a404b30 --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlord/male/hand_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1cef08ce9ae8b24d616369a9bf0688c3db2eed06ac570b4279ca941b05cebb9b +size 2008 diff --git a/assets/voxygen/voxel/npc/cultist_warlord/male/hand_r.vox b/assets/voxygen/voxel/npc/cultist_warlord/male/hand_r.vox new file mode 100644 index 0000000000..87ad00599e --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlord/male/hand_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:98c055f9737bc153d9859069651ce3f04f53f9f14fef852216bfa69e14f85b1e +size 2008 diff --git a/assets/voxygen/voxel/npc/cultist_warlord/male/head.vox b/assets/voxygen/voxel/npc/cultist_warlord/male/head.vox new file mode 100644 index 0000000000..c1f7ac8e5b --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlord/male/head.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0837160f64bd433fd8ea27b99845e958e213fca1cc5350e58d82fe84f45e7dce +size 5352 diff --git a/assets/voxygen/voxel/npc/cultist_warlord/male/leg_l.vox b/assets/voxygen/voxel/npc/cultist_warlord/male/leg_l.vox new file mode 100644 index 0000000000..3e750c7909 --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlord/male/leg_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:607ba31c5d549002b91277c4f103da92bbc3fbbb56d24bd88e47991b63e3feda +size 1304 diff --git a/assets/voxygen/voxel/npc/cultist_warlord/male/leg_r.vox b/assets/voxygen/voxel/npc/cultist_warlord/male/leg_r.vox new file mode 100644 index 0000000000..3e750c7909 --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlord/male/leg_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:607ba31c5d549002b91277c4f103da92bbc3fbbb56d24bd88e47991b63e3feda +size 1304 diff --git a/assets/voxygen/voxel/npc/cultist_warlord/male/shoulder_l.vox b/assets/voxygen/voxel/npc/cultist_warlord/male/shoulder_l.vox new file mode 100644 index 0000000000..0136e7ba9d --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlord/male/shoulder_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:171568a256c6786d12c3dec399844bd89df9edfca737331f305f64604e184bdb +size 2304 diff --git a/assets/voxygen/voxel/npc/cultist_warlord/male/shoulder_r.vox b/assets/voxygen/voxel/npc/cultist_warlord/male/shoulder_r.vox new file mode 100644 index 0000000000..8c7fb02e0a --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlord/male/shoulder_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:68e0affa713344ca7fff1a73eea51e5d6e15807de92f8500533bcb8f89f1e000 +size 2304 diff --git a/assets/voxygen/voxel/npc/cultist_warlord/male/torso_lower.vox b/assets/voxygen/voxel/npc/cultist_warlord/male/torso_lower.vox new file mode 100644 index 0000000000..98275c1648 --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlord/male/torso_lower.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:82febe57d17d665f7f4b5b6fb85a1d2969229503d89d8287d0c0e07c08536797 +size 2848 diff --git a/assets/voxygen/voxel/npc/cultist_warlord/male/torso_upper.vox b/assets/voxygen/voxel/npc/cultist_warlord/male/torso_upper.vox new file mode 100644 index 0000000000..ffb9d8a9b9 --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlord/male/torso_upper.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:205b5329da47997a653c43b8ff9ae26e52d3915ef5c7b8df084b770fd2af33a4 +size 4736 diff --git a/assets/voxygen/voxel/npc/duck/female/head.vox b/assets/voxygen/voxel/npc/duck/female/head.vox index 3b26df99d5..ad0ff800a8 100644 --- a/assets/voxygen/voxel/npc/duck/female/head.vox +++ b/assets/voxygen/voxel/npc/duck/female/head.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:81e5bc6d27fc6f8946331487853c0c8c1cee1b8bf08efb4393340ede5e1ec490 -size 1216 +oid sha256:768c794779e181c73376955cd87401790b00789620cfb66e6b3da8795049f197 +size 1408 diff --git a/assets/voxygen/voxel/npc/duck/female/leg_r.vox b/assets/voxygen/voxel/npc/duck/female/leg_r.vox index 3be613af5c..acb6a89c83 100644 --- a/assets/voxygen/voxel/npc/duck/female/leg_r.vox +++ b/assets/voxygen/voxel/npc/duck/female/leg_r.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:483b2b9d8617f0f902f98578a96844242442831762dd4afa09e1198798924079 -size 1128 +oid sha256:6d3b38afae202d17812b45118b605bc72dae7b0e8ddaffec79612a074eaecf48 +size 1184 diff --git a/assets/voxygen/voxel/npc/duck/female/tail.vox b/assets/voxygen/voxel/npc/duck/female/tail.vox index 0c0e2707eb..291e649074 100644 --- a/assets/voxygen/voxel/npc/duck/female/tail.vox +++ b/assets/voxygen/voxel/npc/duck/female/tail.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:618e0daa8cd2901c75e3fe17658a79a335835e2b7bbb00c5bfb7138314168ec1 -size 1156 +oid sha256:d660e8a544a9c1dabdb37f22c9ec356edc3865fca26df6fd63fb7987286bcdb5 +size 1216 diff --git a/assets/voxygen/voxel/npc/duck/female/torso.vox b/assets/voxygen/voxel/npc/duck/female/torso.vox index 7125dfee9d..6846ff27c4 100644 --- a/assets/voxygen/voxel/npc/duck/female/torso.vox +++ b/assets/voxygen/voxel/npc/duck/female/torso.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f2d93cf83277b348fcb0775554e77a78315ec9890570486701da787175cb37b -size 1576 +oid sha256:fd2a3167681fc1de1ad34749462bdfa6f872dea66cb65da73cdad15bbc103c7c +size 1944 diff --git a/assets/voxygen/voxel/npc/duck/female/wing.vox b/assets/voxygen/voxel/npc/duck/female/wing.vox index 9849a28317..c428e03774 100644 --- a/assets/voxygen/voxel/npc/duck/female/wing.vox +++ b/assets/voxygen/voxel/npc/duck/female/wing.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6ca0fc4e689a37163e7c5b64db9cae5e2a79791489326bd67a393ab6cc3b9e9 -size 1144 +oid sha256:3a80090d025a00b03083643bc2b749eafea5034ec992c882a0fb41a0b06db4d3 +size 1184 diff --git a/assets/voxygen/voxel/npc/duck/male/head.vox b/assets/voxygen/voxel/npc/duck/male/head.vox index 684343bdfe..9a429784a5 100644 --- a/assets/voxygen/voxel/npc/duck/male/head.vox +++ b/assets/voxygen/voxel/npc/duck/male/head.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee6a99b5e62b4bf623ae2df82a31b59fcbca98239e9bbece43bc53e0782bbcec -size 1216 +oid sha256:627dce91d0b1710e4dcbf654170282f8262409f0731b2183e0dc10904715fb96 +size 1408 diff --git a/assets/voxygen/voxel/npc/duck/male/leg_r.vox b/assets/voxygen/voxel/npc/duck/male/leg_r.vox index 3be613af5c..4ba416c1c0 100644 --- a/assets/voxygen/voxel/npc/duck/male/leg_r.vox +++ b/assets/voxygen/voxel/npc/duck/male/leg_r.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:483b2b9d8617f0f902f98578a96844242442831762dd4afa09e1198798924079 -size 1128 +oid sha256:148f9d4c74f57435de6375dd79279782797bc275bab0a24455a928e4ab213a9a +size 1184 diff --git a/assets/voxygen/voxel/npc/duck/male/tail.vox b/assets/voxygen/voxel/npc/duck/male/tail.vox index fc79b8de2a..4298179b35 100644 --- a/assets/voxygen/voxel/npc/duck/male/tail.vox +++ b/assets/voxygen/voxel/npc/duck/male/tail.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a3503b0c9cdb6d847a3c5eaf8e256be3a9e1042108074450c3d7738394f5a966 -size 1156 +oid sha256:c1117e32ff560cfe68a57d7b15cdcf05616322479c26e7de3ca365dad6089967 +size 1216 diff --git a/assets/voxygen/voxel/npc/duck/male/torso.vox b/assets/voxygen/voxel/npc/duck/male/torso.vox index 8f2b02aa1b..fe15a206d9 100644 --- a/assets/voxygen/voxel/npc/duck/male/torso.vox +++ b/assets/voxygen/voxel/npc/duck/male/torso.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f44ec1e38436dcb5236696922956349593a725cfd95cdae5279bd0d2e7f39ded -size 1576 +oid sha256:5455748a19d29c00f6e4fbc7079e45a5c44e65d6e4bbb6d1de06b2a4cb075193 +size 1944 diff --git a/assets/voxygen/voxel/npc/duck/male/wing.vox b/assets/voxygen/voxel/npc/duck/male/wing.vox index 5e83039067..80245520d7 100644 --- a/assets/voxygen/voxel/npc/duck/male/wing.vox +++ b/assets/voxygen/voxel/npc/duck/male/wing.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd07b1d470989d85762b47d14afbf0dd0438c4557144d4216c0cdd4921e42d75 -size 1144 +oid sha256:a86bc4b4ffe7981cccba366339b66e1b74eafa4c0172ea9f5230dd6bec369328 +size 1184 diff --git a/assets/voxygen/voxel/npc/mammoth/male/foot_br.vox b/assets/voxygen/voxel/npc/mammoth/male/foot_br.vox new file mode 100644 index 0000000000..4c1ffc5762 --- /dev/null +++ b/assets/voxygen/voxel/npc/mammoth/male/foot_br.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:72ca78fc160a200233ecdeed8028c39aa9e716b878ea860fa8f9e90d77bfdaad +size 2540 diff --git a/assets/voxygen/voxel/npc/mammoth/male/foot_fr.vox b/assets/voxygen/voxel/npc/mammoth/male/foot_fr.vox new file mode 100644 index 0000000000..54adfbf8c4 --- /dev/null +++ b/assets/voxygen/voxel/npc/mammoth/male/foot_fr.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7525e53849ae288bc7da334fe6bc8e58595c77324db19af4b29e7e280e8b349b +size 3016 diff --git a/assets/voxygen/voxel/npc/mammoth/male/head.vox b/assets/voxygen/voxel/npc/mammoth/male/head.vox new file mode 100644 index 0000000000..a3002dc57c --- /dev/null +++ b/assets/voxygen/voxel/npc/mammoth/male/head.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8fccfbe78cf12875199bd3b768745634a3a6858347923fe4981fd30cdfae7e57 +size 7472 diff --git a/assets/voxygen/voxel/npc/mammoth/male/jaw.vox b/assets/voxygen/voxel/npc/mammoth/male/jaw.vox new file mode 100644 index 0000000000..46484ad33e --- /dev/null +++ b/assets/voxygen/voxel/npc/mammoth/male/jaw.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c301cc09e81ecee8f3d2fbc5860f5072a051788a29a7ab886ef94d8675d0173a +size 2008 diff --git a/assets/voxygen/voxel/npc/mammoth/male/leg_br.vox b/assets/voxygen/voxel/npc/mammoth/male/leg_br.vox new file mode 100644 index 0000000000..652202accb --- /dev/null +++ b/assets/voxygen/voxel/npc/mammoth/male/leg_br.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cc8872a768b8edea8f6bdca0658c1c959ceaee04b12a5bcc135dd1e89b8ee80c +size 3544 diff --git a/assets/voxygen/voxel/npc/mammoth/male/leg_fr.vox b/assets/voxygen/voxel/npc/mammoth/male/leg_fr.vox new file mode 100644 index 0000000000..ce870688f4 --- /dev/null +++ b/assets/voxygen/voxel/npc/mammoth/male/leg_fr.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2aa8ba06cf034a63080bafbadd1a4833a06592af5c48d55bf92d54b5bb6969ca +size 3528 diff --git a/assets/voxygen/voxel/npc/mammoth/male/neck.vox b/assets/voxygen/voxel/npc/mammoth/male/neck.vox new file mode 100644 index 0000000000..d975fd3015 --- /dev/null +++ b/assets/voxygen/voxel/npc/mammoth/male/neck.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c959cdab2dcf2ff153e116808c28a973f8b29b4d8d43d34e19ea37951545b99a +size 5816 diff --git a/assets/voxygen/voxel/npc/mammoth/male/tail.vox b/assets/voxygen/voxel/npc/mammoth/male/tail.vox new file mode 100644 index 0000000000..3f6faea1a2 --- /dev/null +++ b/assets/voxygen/voxel/npc/mammoth/male/tail.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f9c5dc4d9307d8c94d0527082d3328e18290bf5f08f3cabb3591f5162bc92b51 +size 1504 diff --git a/assets/voxygen/voxel/npc/mammoth/male/torso_back.vox b/assets/voxygen/voxel/npc/mammoth/male/torso_back.vox new file mode 100644 index 0000000000..b7d51ad7df --- /dev/null +++ b/assets/voxygen/voxel/npc/mammoth/male/torso_back.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d291c1dc28a8dd6eb8c86f3a36a04341699313fbbb15928b55910c0d592693d0 +size 15328 diff --git a/assets/voxygen/voxel/npc/mammoth/male/torso_front.vox b/assets/voxygen/voxel/npc/mammoth/male/torso_front.vox new file mode 100644 index 0000000000..f13f4d590e --- /dev/null +++ b/assets/voxygen/voxel/npc/mammoth/male/torso_front.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a4d57f44b7cd6758c937a9c14f254539df9b0073cc6e8faafb33052e71d0b21b +size 21228 diff --git a/assets/voxygen/voxel/npc/ngoubou/male/foot_br.vox b/assets/voxygen/voxel/npc/ngoubou/male/foot_br.vox new file mode 100644 index 0000000000..ced741ac58 --- /dev/null +++ b/assets/voxygen/voxel/npc/ngoubou/male/foot_br.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:42248a01f1294622eae3c5861ba8d35e45b432428d3ea6caf064deccc69e5cd8 +size 1696 diff --git a/assets/voxygen/voxel/npc/ngoubou/male/foot_fr.vox b/assets/voxygen/voxel/npc/ngoubou/male/foot_fr.vox new file mode 100644 index 0000000000..75dd001f17 --- /dev/null +++ b/assets/voxygen/voxel/npc/ngoubou/male/foot_fr.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fc232484d1090b2f6d717a4a5bb5ce405f82b2ff87de792236d95a8605e7991f +size 1672 diff --git a/assets/voxygen/voxel/npc/ngoubou/male/head.vox b/assets/voxygen/voxel/npc/ngoubou/male/head.vox new file mode 100644 index 0000000000..2923035a1d --- /dev/null +++ b/assets/voxygen/voxel/npc/ngoubou/male/head.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21b51a9da4e8d18b66f81fab1266b58134d08ec7a8b39363a12984fe65c9f9bd +size 5372 diff --git a/assets/voxygen/voxel/npc/ngoubou/male/jaw.vox b/assets/voxygen/voxel/npc/ngoubou/male/jaw.vox new file mode 100644 index 0000000000..35420f25e9 --- /dev/null +++ b/assets/voxygen/voxel/npc/ngoubou/male/jaw.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1c76bfe12da54ebe26596ebe27d589d58d6f0d0dc39d44c5b532b79987a4820b +size 1300 diff --git a/assets/voxygen/voxel/npc/ngoubou/male/leg_br.vox b/assets/voxygen/voxel/npc/ngoubou/male/leg_br.vox new file mode 100644 index 0000000000..675e6fac73 --- /dev/null +++ b/assets/voxygen/voxel/npc/ngoubou/male/leg_br.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0709c1bad08dfc0ed4126d0fece7be080ac9f09aadfc6cb12e02ef16325c02cb +size 2912 diff --git a/assets/voxygen/voxel/npc/ngoubou/male/leg_fr.vox b/assets/voxygen/voxel/npc/ngoubou/male/leg_fr.vox new file mode 100644 index 0000000000..464ed57872 --- /dev/null +++ b/assets/voxygen/voxel/npc/ngoubou/male/leg_fr.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:65edc12de44621ee147b964c51716685ddd22ed3f45d0ee6a20e17b6fddaae29 +size 2440 diff --git a/assets/voxygen/voxel/npc/ngoubou/male/neck.vox b/assets/voxygen/voxel/npc/ngoubou/male/neck.vox new file mode 100644 index 0000000000..daff29a255 --- /dev/null +++ b/assets/voxygen/voxel/npc/ngoubou/male/neck.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e52a2e6ee9ccbac40e1525f8e6c2de6a2c6835b7356bf932c80e105f91b6aeec +size 3204 diff --git a/assets/voxygen/voxel/npc/ngoubou/male/tail.vox b/assets/voxygen/voxel/npc/ngoubou/male/tail.vox new file mode 100644 index 0000000000..99878c3355 --- /dev/null +++ b/assets/voxygen/voxel/npc/ngoubou/male/tail.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:67089b1afbf7381dbec7214101d574ccac4b863c44dd89d7df1c5f26c8c2e4c6 +size 1396 diff --git a/assets/voxygen/voxel/npc/ngoubou/male/torso_back.vox b/assets/voxygen/voxel/npc/ngoubou/male/torso_back.vox new file mode 100644 index 0000000000..d39ed61f33 --- /dev/null +++ b/assets/voxygen/voxel/npc/ngoubou/male/torso_back.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cef260646963e12fb959dad520bd3574e0fd752ad580d0aa7c4ed9069679b52c +size 6852 diff --git a/assets/voxygen/voxel/npc/ngoubou/male/torso_front.vox b/assets/voxygen/voxel/npc/ngoubou/male/torso_front.vox new file mode 100644 index 0000000000..5b594e579f --- /dev/null +++ b/assets/voxygen/voxel/npc/ngoubou/male/torso_front.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7dbd8af2890598f01cb74da774ef9ec48ff07aea12260114b50e5dc7ce7a5034 +size 6332 diff --git a/assets/voxygen/voxel/quadruped_medium_central_manifest.ron b/assets/voxygen/voxel/quadruped_medium_central_manifest.ron index 72e6b57a67..a9a7ff6c04 100644 --- a/assets/voxygen/voxel/quadruped_medium_central_manifest.ron +++ b/assets/voxygen/voxel/quadruped_medium_central_manifest.ron @@ -1799,4 +1799,124 @@ central: ("npc.snowleopard.male.tail"), ), ), + (Mammoth, Male): ( + head: ( + offset: (-13.0, 0.0, -11.0), + central: ("npc.mammoth.male.head"), + ), + neck: ( + offset: (-6.0, -3.5, -11.5), + central: ("npc.mammoth.male.neck"), + ), + jaw: ( + offset: (-3.0, 0.0, -13.0), + central: ("npc.mammoth.male.jaw"), + ), + torso_front: ( + offset: (-9.0, -14.0, -15.0), + central: ("npc.mammoth.male.torso_front"), + ), + torso_back: ( + offset: (-8.0, -14.0, -12.5), + central: ("npc.mammoth.male.torso_back"), + ), + ears: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + tail: ( + offset: (-2.0, -6.0, -15.0), + central: ("npc.mammoth.male.tail"), + ), + ), + (Mammoth, Female): ( + head: ( + offset: (-13.0, 0.0, -11.0), + central: ("npc.mammoth.male.head"), + ), + neck: ( + offset: (-6.0, -3.5, -11.5), + central: ("npc.mammoth.male.neck"), + ), + jaw: ( + offset: (-3.0, 0.0, -13.0), + central: ("npc.mammoth.male.jaw"), + ), + torso_front: ( + offset: (-9.0, -14.0, -15.0), + central: ("npc.mammoth.male.torso_front"), + ), + torso_back: ( + offset: (-8.0, -14.0, -12.5), + central: ("npc.mammoth.male.torso_back"), + ), + ears: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + tail: ( + offset: (-2.0, -6.0, -15.0), + central: ("npc.mammoth.male.tail"), + ), + ), + (Ngoubou, Male): ( + head: ( + offset: (-9.5, 0.0, -9.0), + central: ("npc.ngoubou.male.head"), + ), + neck: ( + offset: (-4.5, -3.0, -7.5), + central: ("npc.ngoubou.male.neck"), + ), + jaw: ( + offset: (-2.5, 0.0, -3.0), + central: ("npc.ngoubou.male.jaw"), + ), + torso_front: ( + offset: (-5.5, -9.0, -9.5), + central: ("npc.ngoubou.male.torso_front"), + ), + torso_back: ( + offset: (-5.5, -13.0, -7.5), + central: ("npc.ngoubou.male.torso_back"), + ), + ears: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + tail: ( + offset: (-1.5, -8.0, -14.0), + central: ("npc.ngoubou.male.tail"), + ), + ), + (Ngoubou, Female): ( + head: ( + offset: (-9.5, 0.0, -9.0), + central: ("npc.ngoubou.male.head"), + ), + neck: ( + offset: (-4.5, -3.0, -7.5), + central: ("npc.ngoubou.male.neck"), + ), + jaw: ( + offset: (-2.5, 0.0, -3.0), + central: ("npc.ngoubou.male.jaw"), + ), + torso_front: ( + offset: (-5.5, -9.0, -9.5), + central: ("npc.ngoubou.male.torso_front"), + ), + torso_back: ( + offset: (-5.5, -13.0, -7.5), + central: ("npc.ngoubou.male.torso_back"), + ), + ears: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + tail: ( + offset: (-1.5, -8.0, -14.0), + central: ("npc.ngoubou.male.tail"), + ), + ), }) diff --git a/assets/voxygen/voxel/quadruped_medium_lateral_manifest.ron b/assets/voxygen/voxel/quadruped_medium_lateral_manifest.ron index 31d36969bb..c548271783 100644 --- a/assets/voxygen/voxel/quadruped_medium_lateral_manifest.ron +++ b/assets/voxygen/voxel/quadruped_medium_lateral_manifest.ron @@ -2039,4 +2039,140 @@ lateral: ("npc.snowleopard.male.foot_br"), ), ), + (Mammoth, Male): ( + leg_fl: ( + offset: (-4.0, -5.0, -6.0), + lateral: ("npc.mammoth.male.leg_fr"), + ), + leg_fr: ( + offset: (-4.0, -5.0, -6.0), + lateral: ("npc.mammoth.male.leg_fr"), + ), + leg_bl: ( + offset: (-3.5, -5.0, -5.5), + lateral: ("npc.mammoth.male.leg_br"), + ), + leg_br: ( + offset: (-3.5, -5.0, -5.5), + lateral: ("npc.mammoth.male.leg_br"), + ), + foot_fl: ( + offset: (-3.5, -4.5, -10.0), + lateral: ("npc.mammoth.male.foot_fr"), + ), + foot_fr: ( + offset: (-3.5, -4.5, -10.0), + lateral: ("npc.mammoth.male.foot_fr"), + ), + foot_bl: ( + offset: (-3.0, -4.5, -9.0), + lateral: ("npc.mammoth.male.foot_br"), + ), + foot_br: ( + offset: (-3.0, -4.5, -9.0), + lateral: ("npc.mammoth.male.foot_br"), + ), + ), + (Mammoth, Female): ( + leg_fl: ( + offset: (-4.0, -5.0, -6.0), + lateral: ("npc.mammoth.male.leg_fr"), + ), + leg_fr: ( + offset: (-4.0, -5.0, -6.0), + lateral: ("npc.mammoth.male.leg_fr"), + ), + leg_bl: ( + offset: (-3.5, -5.0, -5.5), + lateral: ("npc.mammoth.male.leg_br"), + ), + leg_br: ( + offset: (-3.5, -5.0, -5.5), + lateral: ("npc.mammoth.male.leg_br"), + ), + foot_fl: ( + offset: (-3.5, -4.5, -10.0), + lateral: ("npc.mammoth.male.foot_fr"), + ), + foot_fr: ( + offset: (-3.5, -4.5, -10.0), + lateral: ("npc.mammoth.male.foot_fr"), + ), + foot_bl: ( + offset: (-3.0, -4.5, -9.0), + lateral: ("npc.mammoth.male.foot_br"), + ), + foot_br: ( + offset: (-3.0, -4.5, -9.0), + lateral: ("npc.mammoth.male.foot_br"), + ), + ), + (Ngoubou, Male): ( + leg_fl: ( + offset: (-2.5, -4.0, -7.0), + lateral: ("npc.ngoubou.male.leg_fr"), + ), + leg_fr: ( + offset: (-2.5, -4.0, -7.0), + lateral: ("npc.ngoubou.male.leg_fr"), + ), + leg_bl: ( + offset: (-3.0, -4.5, -5.5), + lateral: ("npc.ngoubou.male.leg_br"), + ), + leg_br: ( + offset: (-3.0, -4.5, -5.5), + lateral: ("npc.ngoubou.male.leg_br"), + ), + foot_fl: ( + offset: (-2.5, -3.5, -9.0), + lateral: ("npc.ngoubou.male.foot_fr"), + ), + foot_fr: ( + offset: (-2.5, -3.5, -9.0), + lateral: ("npc.ngoubou.male.foot_fr"), + ), + foot_bl: ( + offset: (-2.5, -3.5, -9.0), + lateral: ("npc.ngoubou.male.foot_br"), + ), + foot_br: ( + offset: (-2.5, -3.5, -9.0), + lateral: ("npc.ngoubou.male.foot_br"), + ), + ), + (Ngoubou, Female): ( + leg_fl: ( + offset: (-2.5, -4.0, -7.0), + lateral: ("npc.ngoubou.male.leg_fr"), + ), + leg_fr: ( + offset: (-2.5, -4.0, -7.0), + lateral: ("npc.ngoubou.male.leg_fr"), + ), + leg_bl: ( + offset: (-3.0, -4.5, -5.5), + lateral: ("npc.ngoubou.male.leg_br"), + ), + leg_br: ( + offset: (-3.0, -4.5, -5.5), + lateral: ("npc.ngoubou.male.leg_br"), + ), + foot_fl: ( + offset: (-2.5, -3.5, -9.0), + lateral: ("npc.ngoubou.male.foot_fr"), + ), + foot_fr: ( + offset: (-2.5, -3.5, -9.0), + lateral: ("npc.ngoubou.male.foot_fr"), + ), + foot_bl: ( + offset: (-2.5, -3.5, -9.0), + lateral: ("npc.ngoubou.male.foot_br"), + ), + foot_br: ( + offset: (-2.5, -3.5, -9.0), + lateral: ("npc.ngoubou.male.foot_br"), + ), + ), }) diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 623f164283..923347e2ba 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -358,6 +358,8 @@ impl Body { quadruped_medium::Species::Saber => Vec3::new(2.0, 3.0, 2.0), quadruped_medium::Species::Tarasque => Vec3::new(2.0, 4.0, 2.6), quadruped_medium::Species::Yak => Vec3::new(2.0, 3.6, 3.0), + quadruped_medium::Species::Mammoth => Vec3::new(5.0, 7.0, 8.0), + quadruped_medium::Species::Ngoubou => Vec3::new(2.0, 3.2, 2.4), _ => Vec3::new(2.0, 3.0, 2.0), }, Body::QuadrupedSmall(body) => match body.species { @@ -460,6 +462,8 @@ impl Body { quadruped_medium::Species::Bear => 900, quadruped_medium::Species::Moose => 800, quadruped_medium::Species::Dreadhorn => 1100, + quadruped_medium::Species::Mammoth => 1700, + quadruped_medium::Species::Ngoubou => 1500, _ => 700, }, Body::BirdMedium(bird_medium) => match bird_medium.species { @@ -583,6 +587,8 @@ impl Body { quadruped_medium::Species::Bear => 40, quadruped_medium::Species::Moose => 30, quadruped_medium::Species::Dreadhorn => 50, + quadruped_medium::Species::Mammoth => 70, + quadruped_medium::Species::Ngoubou => 50, _ => 20, }, Body::BirdMedium(bird_medium) => match bird_medium.species { diff --git a/common/src/comp/body/biped_large.rs b/common/src/comp/body/biped_large.rs index d152693658..7a816f512e 100644 --- a/common/src/comp/body/biped_large.rs +++ b/common/src/comp/body/biped_large.rs @@ -52,6 +52,7 @@ make_case_elim!( Harvester = 15, Blueoni = 16, Redoni = 17, + Cultistwarlord = 18, } ); @@ -78,6 +79,7 @@ pub struct AllSpecies { pub harvester: SpeciesMeta, pub oni_blue: SpeciesMeta, pub oni_red: SpeciesMeta, + pub cultist_warlord: SpeciesMeta, } impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies { @@ -104,11 +106,12 @@ impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies Species::Harvester => &self.harvester, Species::Blueoni => &self.oni_blue, Species::Redoni => &self.oni_red, + Species::Cultistwarlord => &self.cultist_warlord, } } } -pub const ALL_SPECIES: [Species; 18] = [ +pub const ALL_SPECIES: [Species; 19] = [ Species::Ogre, Species::Cyclops, Species::Wendigo, @@ -127,6 +130,7 @@ pub const ALL_SPECIES: [Species; 18] = [ Species::Harvester, Species::Blueoni, Species::Redoni, + Species::Cultistwarlord, ]; impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies { diff --git a/common/src/comp/body/quadruped_medium.rs b/common/src/comp/body/quadruped_medium.rs index 192e7559b5..f814b57a05 100644 --- a/common/src/comp/body/quadruped_medium.rs +++ b/common/src/comp/body/quadruped_medium.rs @@ -62,6 +62,8 @@ pub enum Species { Dreadhorn = 29, Moose = 30, Snowleopard = 31, + Mammoth = 32, + Ngoubou = 33, } /// Data representing per-species generic data. @@ -99,6 +101,8 @@ pub struct AllSpecies { pub dreadhorn: SpeciesMeta, pub moose: SpeciesMeta, pub snowleopard: SpeciesMeta, + pub mammoth: SpeciesMeta, + pub ngoubou: SpeciesMeta, } impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies { @@ -137,11 +141,13 @@ impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies Species::Dreadhorn => &self.dreadhorn, Species::Moose => &self.moose, Species::Snowleopard => &self.snowleopard, + Species::Mammoth => &self.mammoth, + Species::Ngoubou => &self.ngoubou, } } } -pub const ALL_SPECIES: [Species; 30] = [ +pub const ALL_SPECIES: [Species; 32] = [ Species::Grolgar, Species::Saber, Species::Tiger, @@ -172,6 +178,8 @@ pub const ALL_SPECIES: [Species; 30] = [ Species::Dreadhorn, Species::Moose, Species::Snowleopard, + Species::Mammoth, + Species::Ngoubou, ]; impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies { diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index d4396b2749..5571bf7ef9 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -184,7 +184,9 @@ fn default_main_tool(body: &Body) -> Item { quadruped_medium::Species::Tuskram | quadruped_medium::Species::Roshwalr | quadruped_medium::Species::Moose - | quadruped_medium::Species::Dreadhorn => Some(Item::new_from_asset_expect( + | quadruped_medium::Species::Dreadhorn + | quadruped_medium::Species::Mammoth + | quadruped_medium::Species::Ngoubou => Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.quadmedcharge", )), quadruped_medium::Species::Highland @@ -294,9 +296,9 @@ fn default_main_tool(body: &Body) -> Item { (biped_large::Species::Blueoni, _) => Some(Item::new_from_asset_expect( "common.items.npc_weapons.axe.oni_blue_axe", )), - (biped_large::Species::Redoni, _) => Some(Item::new_from_asset_expect( - "common.items.npc_weapons.hammer.oni_red_hammer", - )), + (biped_large::Species::Redoni | biped_large::Species::Cultistwarlord, _) => Some( + Item::new_from_asset_expect("common.items.npc_weapons.hammer.oni_red_hammer"), + ), }, Body::Object(body) => match body { object::Body::Crossbow => Some(Item::new_from_asset_expect( diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 4027e4eda4..d105035c43 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -68,6 +68,8 @@ impl Body { quadruped_medium::Species::Dreadhorn => 140.0, quadruped_medium::Species::Moose => 130.0, quadruped_medium::Species::Snowleopard => 160.0, + quadruped_medium::Species::Mammoth => 180.0, + quadruped_medium::Species::Ngoubou => 170.0, }, Body::BipedLarge(body) => match body.species { biped_large::Species::Slysaurok => 100.0, diff --git a/voxygen/anim/src/biped_large/mod.rs b/voxygen/anim/src/biped_large/mod.rs index 9597f8f2e7..74548f9e7c 100644 --- a/voxygen/anim/src/biped_large/mod.rs +++ b/voxygen/anim/src/biped_large/mod.rs @@ -227,6 +227,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Harvester, _) => (6.0, 11.0), (Blueoni, _) => (10.5, -3.0), (Redoni, _) => (10.5, -3.0), + (Cultistwarlord, _) => (0.5, 14.5), }, jaw: match (body.species, body.body_type) { (Ogre, _) => (0.0, 0.0), @@ -247,6 +248,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Harvester, _) => (-2.0, -7.0), (Blueoni, _) => (0.0, 3.5), (Redoni, _) => (0.0, 3.5), + (Cultistwarlord, _) => (0.0, 3.5), }, upper_torso: match (body.species, body.body_type) { (Ogre, Male) => (0.0, 27.5), @@ -268,6 +270,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Harvester, _) => (-1.0, 18.0), (Blueoni, _) => (-1.0, 26.5), (Redoni, _) => (-1.0, 26.5), + (Cultistwarlord, _) => (-1.0, 18.5), }, lower_torso: match (body.species, body.body_type) { (Ogre, Male) => (1.0, -7.0), @@ -289,6 +292,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Harvester, _) => (-1.0, -4.5), (Blueoni, _) => (0.0, -8.5), (Redoni, _) => (0.0, -8.5), + (Cultistwarlord, _) => (0.0, -1.5), }, tail: match (body.species, body.body_type) { (Werewolf, _) => (-5.5, -2.0), @@ -318,6 +322,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Harvester, _) => (8.0, 1.0, -1.5), (Blueoni, _) => (11.0, 2.0, -5.5), (Redoni, _) => (11.0, 2.0, -5.5), + (Cultistwarlord, _) => (11.5, -1.0, 4.5), }, hand: match (body.species, body.body_type) { (Ogre, Male) => (14.5, 0.0, -4.0), @@ -339,6 +344,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Harvester, _) => (11.5, 1.5, -5.5), (Blueoni, _) => (13.5, 0.5, -8.0), (Redoni, _) => (13.5, 0.5, -8.0), + (Cultistwarlord, _) => (11.5, -1.0, -1.0), }, leg: match (body.species, body.body_type) { (Ogre, Male) => (0.0, 0.0, -4.0), @@ -360,6 +366,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Harvester, _) => (3.5, 1.0, -4.0), (Blueoni, _) => (4.5, 2.0, -5.5), (Redoni, _) => (4.5, 2.0, -5.5), + (Cultistwarlord, _) => (3.5, -1.0, -8.5), }, foot: match (body.species, body.body_type) { (Ogre, Male) => (4.0, 1.0, -12.0), @@ -381,6 +388,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Harvester, _) => (4.5, 0.5, -9.5), (Blueoni, _) => (5.0, 5.0, -12.5), (Redoni, _) => (5.0, 5.0, -12.5), + (Cultistwarlord, _) => (3.5, 0.0, -12.5), }, scaler: match (body.species, body.body_type) { (Ogre, Male) => 1.12, @@ -402,6 +410,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Harvester, _) => 1.2, (Blueoni, _) => 1.2, (Redoni, _) => 1.2, + (Cultistwarlord, _) => 1.0, }, tempo: match (body.species, body.body_type) { (Ogre, Male) => 0.9, @@ -434,6 +443,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Harvester, _) => (7.5, 0.0), (Blueoni, _) => (12.5, 0.0), (Redoni, _) => (12.5, 0.0), + (Cultistwarlord, _) => (12.5, 0.0), }, shl: match (body.species, body.body_type) { (Dullahan, _) => (-4.75, -11.0, 8.5, 1.47, -0.2, 0.0), diff --git a/voxygen/anim/src/bird_medium/mod.rs b/voxygen/anim/src/bird_medium/mod.rs index ab72ea8173..5caa8f17ae 100644 --- a/voxygen/anim/src/bird_medium/mod.rs +++ b/voxygen/anim/src/bird_medium/mod.rs @@ -90,7 +90,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { use comp::bird_medium::{BodyType::*, Species::*}; Self { head: match (body.species, body.body_type) { - (Duck, _) => (4.0, 3.0), + (Duck, _) => (2.0, 5.5), (Chicken, Male) => (3.0, 4.5), (Chicken, Female) => (3.0, 6.0), (Goose, _) => (5.0, 2.5), @@ -101,7 +101,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Parrot, _) => (0.5, 4.5), }, chest: match (body.species, body.body_type) { - (Duck, _) => (0.0, 5.0), + (Duck, _) => (0.0, 6.0), (Chicken, Male) => (0.0, 6.5), (Chicken, Female) => (0.0, 6.5), (Goose, _) => (0.0, 8.0), @@ -112,7 +112,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Parrot, _) => (0.0, 5.0), }, tail: match (body.species, body.body_type) { - (Duck, _) => (-3.0, 1.5), + (Duck, _) => (-5.0, 1.0), (Chicken, Male) => (-7.5, 3.5), (Chicken, Female) => (-4.5, 3.0), (Goose, _) => (-5.0, 3.0), @@ -123,7 +123,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Parrot, _) => (-8.0, -2.0), }, wing: match (body.species, body.body_type) { - (Duck, _) => (2.75, 0.0, 1.0), + (Duck, _) => (3.5, -0.5, 2.0), (Chicken, Male) => (3.0, -1.0, 2.5), (Chicken, Female) => (3.0, -1.5, 2.5), (Goose, _) => (3.75, -1.0, 2.0), @@ -134,7 +134,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Parrot, _) => (2.0, -4.5, 3.0), }, foot: match (body.species, body.body_type) { - (Duck, _) => (2.0, -1.5, 4.0), + (Duck, _) => (2.5, -2.0, 4.0), (Chicken, Male) => (2.0, 0.0, 6.0), (Chicken, Female) => (2.0, 0.0, 6.0), (Goose, _) => (2.0, -1.5, 7.0), diff --git a/voxygen/anim/src/quadruped_medium/mod.rs b/voxygen/anim/src/quadruped_medium/mod.rs index c4ae82537f..5c780cc851 100644 --- a/voxygen/anim/src/quadruped_medium/mod.rs +++ b/voxygen/anim/src/quadruped_medium/mod.rs @@ -175,6 +175,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Moose, Male) => (-0.5, 5.0), (Moose, Female) => (3.5, 0.5), (Snowleopard, _) => (1.5, 0.5), + (Mammoth, _) => (0.5, -1.5), + (Ngoubou, _) => (0.5, -2.5), }, neck: match (body.species, body.body_type) { (Grolgar, _) => (1.0, -1.0), @@ -208,6 +210,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Dreadhorn, _) => (0.5, 0.0), (Moose, _) => (-0.5, 0.5), (Snowleopard, _) => (0.0, 1.5), + (Mammoth, _) => (0.5, -0.5), + (Ngoubou, _) => (2.0, 1.0), }, jaw: match (body.species, body.body_type) { (Grolgar, _) => (7.0, 2.0), @@ -242,6 +246,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Moose, Male) => (10.0, -7.0), (Moose, Female) => (6.0, -2.5), (Snowleopard, _) => (3.0, -3.0), + (Mammoth, _) => (4.5, -3.0), + (Ngoubou, _) => (4.5, -4.0), }, tail: match (body.species, body.body_type) { (Grolgar, _) => (-11.5, -0.5), @@ -275,6 +281,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Dreadhorn, _) => (-5.5, 1.5), (Moose, _) => (-12.5, 3.5), (Snowleopard, _) => (-10.5, 3.0), + (Mammoth, _) => (-13.0, -1.5), + (Ngoubou, _) => (-12.0, 5.5), }, torso_front: match (body.species, body.body_type) { (Grolgar, _) => (10.0, 13.0), @@ -308,6 +316,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Dreadhorn, _) => (1.5, 15.5), (Moose, _) => (1.5, 19.5), (Snowleopard, _) => (1.5, 13.0), + (Mammoth, _) => (11.5, 20.5), + (Ngoubou, _) => (9.5, 16.5), }, torso_back: match (body.species, body.body_type) { (Grolgar, _) => (-10.0, 1.5), @@ -341,6 +351,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Dreadhorn, _) => (-20.0, -1.0), (Moose, _) => (-10.0, -1.0), (Snowleopard, _) => (-11.0, 0.0), + (Mammoth, _) => (-13.0, -2.5), + (Ngoubou, _) => (-8.0, -2.0), }, ears: match (body.species, body.body_type) { (Grolgar, _) => (5.0, 8.0), @@ -375,6 +387,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Moose, Male) => (6.0, 1.0), (Moose, Female) => (2.0, 4.5), (Snowleopard, _) => (1.5, 3.0), + (Mammoth, _) => (12.0, -3.0), + (Ngoubou, _) => (12.0, -3.0), }, leg_f: match (body.species, body.body_type) { (Grolgar, _) => (7.5, -5.5, -1.0), @@ -408,6 +422,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Dreadhorn, _) => (8.5, -7.0, -0.5), (Moose, _) => (5.5, -4.0, 1.0), (Snowleopard, _) => (6.5, -4.0, -2.5), + (Mammoth, _) => (10.0, -5.0, -5.0), + (Ngoubou, _) => (7.5, -4.0, -1.5), }, leg_b: match (body.species, body.body_type) { (Grolgar, _) => (6.0, -6.5, -4.0), @@ -441,6 +457,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Dreadhorn, _) => (6.0, 0.0, -3.0), (Moose, _) => (4.5, -10.0, -2.0), (Snowleopard, _) => (5.5, -5.0, -1.5), + (Mammoth, _) => (7.5, -7.0, -5.0), + (Ngoubou, _) => (4.5, -9.5, 0.0), }, feet_f: match (body.species, body.body_type) { (Grolgar, _) => (0.0, 0.0, -4.0), @@ -474,6 +492,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Dreadhorn, _) => (-0.5, 0.5, -5.0), (Moose, _) => (-1.0, 1.5, -9.5), (Snowleopard, _) => (0.5, 0.5, -4.5), + (Mammoth, _) => (-0.5, -0.5, -6.0), + (Ngoubou, _) => (-1.0, 0.5, -6.0), }, feet_b: match (body.species, body.body_type) { (Grolgar, _) => (0.5, -1.5, -3.0), @@ -507,6 +527,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Dreadhorn, _) => (-0.5, 0.5, -3.5), (Moose, _) => (-1.0, 0.0, -6.5), (Snowleopard, _) => (0.5, 0.5, -5.5), + (Mammoth, _) => (0.5, -0.5, -4.5), + (Ngoubou, _) => (0.5, 1.0, -5.5), }, scaler: match (body.species, body.body_type) { (Grolgar, _) => (1.05), @@ -531,6 +553,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Dreadhorn, _) => (1.6), (Moose, _) => (0.95), (Snowleopard, _) => (0.95), + (Mammoth, _) => (3.0), + (Ngoubou, _) => (1.0), _ => (0.9), }, startangle: match (body.species, body.body_type) { @@ -594,6 +618,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { _ => (1.0), }, feed: match (body.species, body.body_type) { + // TODO: Rework some species to allow for feed anim (Tuskram, _) => (true, 0.5), (Mouflon, _) => (true, 0.7), (Deer, _) => (true, 1.0), From fef0f407d3358e683662263b2038851c631e32b2 Mon Sep 17 00:00:00 2001 From: Snowram Date: Wed, 16 Jun 2021 21:27:31 +0200 Subject: [PATCH 167/180] Removes warlord and warlock armours --- .../common/abilities/ability_set_manifest.ron | 4 +-- .../common/entity/dungeon/tier-5/cultist.ron | 18 ++++++++++ .../common/entity/dungeon/tier-5/warlock.ron | 4 +-- .../common/entity/dungeon/tier-5/warlord.ron | 12 +++---- .../items/crafting_ing/hide/rugged_hide.ron | 2 +- .../items/weapons/bow/bipedlarge-velorite.ron | 18 ++++++++++ .../hammer/bipedlarge-cultist_purp_2h-0.ron | 18 ++++++++++ .../staff/bipedlarge_cultist_staff.ron | 18 ++++++++++ .../weapons/sword/bipedlarge-cultist.ron | 18 ++++++++++ .../common/loadout/dungeon/tier-5/cultist.ron | 17 ++++++++++ .../common/loadout/dungeon/tier-5/warlock.ron | 17 ---------- .../common/loadout/dungeon/tier-5/warlord.ron | 17 ---------- .../creature/quad_medium/horned.ron | 4 +++ .../creature/quad_medium/mammoth.ron | 4 +++ .../voxygen/voxel/biped_weapon_manifest.ron | 16 +++++++++ common/src/comp/body.rs | 2 +- common/src/comp/inventory/loadout_builder.rs | 13 ++++---- server/src/events/entity_manipulation.rs | 6 ++++ .../migrations/V41_remove_warlock_warlord.sql | 33 +++++++++++++++++++ voxygen/anim/src/biped_large/mod.rs | 5 ++- voxygen/anim/src/biped_large/shockwave.rs | 2 +- voxygen/anim/src/biped_large/shoot.rs | 11 ++++--- world/src/site/dungeon/mod.rs | 3 +- 23 files changed, 200 insertions(+), 62 deletions(-) create mode 100644 assets/common/entity/dungeon/tier-5/cultist.ron create mode 100644 assets/common/items/weapons/bow/bipedlarge-velorite.ron create mode 100644 assets/common/items/weapons/hammer/bipedlarge-cultist_purp_2h-0.ron create mode 100644 assets/common/items/weapons/staff/bipedlarge_cultist_staff.ron create mode 100644 assets/common/items/weapons/sword/bipedlarge-cultist.ron create mode 100644 assets/common/loadout/dungeon/tier-5/cultist.ron delete mode 100644 assets/common/loadout/dungeon/tier-5/warlock.ron delete mode 100644 assets/common/loadout/dungeon/tier-5/warlord.ron create mode 100644 assets/common/loot_tables/creature/quad_medium/horned.ron create mode 100644 assets/common/loot_tables/creature/quad_medium/mammoth.ron create mode 100644 server/src/migrations/V41_remove_warlock_warlord.sql diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index cdbfd1ac58..4a089589b7 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -38,7 +38,6 @@ primary: "common.abilities.spear.doublestrike", secondary: "common.abilities.spear.dash", abilities: [], - ), Custom("Hammer Simple"): ( primary: "common.abilities.hammersimple.doublestrike", @@ -53,8 +52,7 @@ Custom("Sword Simple"): ( primary: "common.abilities.swordsimple.doublestrike", secondary: "common.abilities.swordsimple.dash", - abilities: [ - ], + abilities: [], ), Custom("Staff Simple"): ( primary: "common.abilities.staffsimple.firebomb", diff --git a/assets/common/entity/dungeon/tier-5/cultist.ron b/assets/common/entity/dungeon/tier-5/cultist.ron new file mode 100644 index 0000000000..8b19091d2d --- /dev/null +++ b/assets/common/entity/dungeon/tier-5/cultist.ron @@ -0,0 +1,18 @@ +EntityConfig ( + name: Some("Cultist"), + body: Some(RandomWith("humanoid")), + + loot: Some(LootTable("common.loot_tables.dungeon.tier-5.enemy")), + + main_tool: Some(Choice([ + (1.0, Some(Item("common.items.weapons.axe_1h.orichalcum-0"))), + (2.0, Some(Item("common.items.weapons.sword.cultist"))), + (1.0, Some(Item("common.items.weapons.hammer.cultist_purp_2h-0"))), + (1.0, Some(Item("common.items.weapons.hammer_1h.orichalcum-0"))), + (1.0, Some(Item("common.items.weapons.bow.velorite"))), + ])), + second_tool: None, + + loadout_asset: Some("common.loadout.dungeon.tier-5.cultist"), + skillset_asset: Some("common.skillset.dungeon.tier-5.enemy"), +) diff --git a/assets/common/entity/dungeon/tier-5/warlock.ron b/assets/common/entity/dungeon/tier-5/warlock.ron index 0932bd714c..6ee268d287 100644 --- a/assets/common/entity/dungeon/tier-5/warlock.ron +++ b/assets/common/entity/dungeon/tier-5/warlock.ron @@ -1,12 +1,12 @@ EntityConfig ( name: Some("Cultist Warlock"), - body: Some(RandomWith("humanoid")), + body: Some(RandomWith("cultist_warlord")), loot: Some(LootTable("common.loot_tables.dungeon.tier-5.enemy")), main_tool: Some(Item("common.items.weapons.staff.cultist_staff")), second_tool: None, - loadout_asset: Some("common.loadout.dungeon.tier-5.warlock"), + loadout_asset: None, skillset_asset: Some("common.skillset.dungeon.tier-5.enemy"), ) diff --git a/assets/common/entity/dungeon/tier-5/warlord.ron b/assets/common/entity/dungeon/tier-5/warlord.ron index b85cb504de..4f7c0c4327 100644 --- a/assets/common/entity/dungeon/tier-5/warlord.ron +++ b/assets/common/entity/dungeon/tier-5/warlord.ron @@ -1,18 +1,16 @@ EntityConfig ( name: Some("Cultist Warlord"), - body: Some(RandomWith("humanoid")), + body: Some(RandomWith("cultist_warlord")), loot: Some(LootTable("common.loot_tables.dungeon.tier-5.enemy")), main_tool: Some(Choice([ - (1.0, Some(Item("common.items.weapons.axe_1h.orichalcum-0"))), - (2.0, Some(Item("common.items.weapons.sword.cultist"))), - (1.0, Some(Item("common.items.weapons.hammer.cultist_purp_2h-0"))), - (1.0, Some(Item("common.items.weapons.hammer_1h.orichalcum-0"))), - (1.0, Some(Item("common.items.weapons.bow.velorite"))), + (2.0, Some(Item("common.items.weapons.sword.bipedlarge-cultist"))), + (1.0, Some(Item("common.items.weapons.hammer.bipedlarge-cultist_purp_2h-0"))), + (1.0, Some(Item("common.items.weapons.bow.bipedlarge-velorite"))), ])), second_tool: None, - loadout_asset: Some("common.loadout.dungeon.tier-5.warlord"), + loadout_asset: None, skillset_asset: Some("common.skillset.dungeon.tier-5.enemy"), ) diff --git a/assets/common/items/crafting_ing/hide/rugged_hide.ron b/assets/common/items/crafting_ing/hide/rugged_hide.ron index 0c8bcc29d7..3a2f8ae583 100644 --- a/assets/common/items/crafting_ing/hide/rugged_hide.ron +++ b/assets/common/items/crafting_ing/hide/rugged_hide.ron @@ -4,7 +4,7 @@ ItemDef( kind: Ingredient( kind: "RuggedHide", ), - quality: Common, + quality: Epic, tags: [ Material(Leather) ], diff --git a/assets/common/items/weapons/bow/bipedlarge-velorite.ron b/assets/common/items/weapons/bow/bipedlarge-velorite.ron new file mode 100644 index 0000000000..f9fe278cf3 --- /dev/null +++ b/assets/common/items/weapons/bow/bipedlarge-velorite.ron @@ -0,0 +1,18 @@ +ItemDef( + name: "Velorite Bow (bipedlarge)", + description: "Infused with Velorite power.", + kind: Tool(( + kind: Bow, + hands: Two, + stats: Direct(( + equip_time_secs: 0.4, + power: 2.0, + poise_strength: 1.1, + speed: 1.0, + crit_chance: 0.140625, + )), + )), + quality: Epic, + tags: [], + ability_spec: Some(Custom("Bow Simple")), +) \ No newline at end of file diff --git a/assets/common/items/weapons/hammer/bipedlarge-cultist_purp_2h-0.ron b/assets/common/items/weapons/hammer/bipedlarge-cultist_purp_2h-0.ron new file mode 100644 index 0000000000..a68c9c4a58 --- /dev/null +++ b/assets/common/items/weapons/hammer/bipedlarge-cultist_purp_2h-0.ron @@ -0,0 +1,18 @@ +ItemDef( + name: "Magical Cultist Warhammer (bipedlarge)", + description: "This belonged to an evil Cult Leader.", + kind: Tool(( + kind: Hammer, + hands: Two, + stats: Direct(( + equip_time_secs: 0.5, + power: 2.0, + poise_strength: 1.0, + speed: 1.0, + crit_chance: 0.140625, + )), + )), + quality: Epic, + tags: [], + ability_spec: Some(Custom("Hammer Simple")), +) \ No newline at end of file diff --git a/assets/common/items/weapons/staff/bipedlarge_cultist_staff.ron b/assets/common/items/weapons/staff/bipedlarge_cultist_staff.ron new file mode 100644 index 0000000000..f0ac7f7bf3 --- /dev/null +++ b/assets/common/items/weapons/staff/bipedlarge_cultist_staff.ron @@ -0,0 +1,18 @@ +ItemDef( + name: "Cultist Staff (bipedlarge)", + description: "The fire gives off no heat.", + kind: Tool(( + kind: Staff, + hands: Two, + stats: Direct(( + equip_time_secs: 0.3, + power: 2.0, + poise_strength: 1.0, + speed: 1.0, + crit_chance: 0.140625, + )), + )), + quality: Epic, + tags: [], + ability_spec: Some(Custom("Staff Simple")), +) \ No newline at end of file diff --git a/assets/common/items/weapons/sword/bipedlarge-cultist.ron b/assets/common/items/weapons/sword/bipedlarge-cultist.ron new file mode 100644 index 0000000000..1f33916eb1 --- /dev/null +++ b/assets/common/items/weapons/sword/bipedlarge-cultist.ron @@ -0,0 +1,18 @@ +ItemDef( + name: "Magical Cultist Greatsword (bipedlarge)", + description: "This belonged to an evil Cult Leader.", + kind: Tool(( + kind: Sword, + hands: Two, + stats: Direct(( + equip_time_secs: 0.5, + power: 2.0, + poise_strength: 1.0, + speed: 1.0, + crit_chance: 0.140625, + )), + )), + quality: Epic, + tags: [], + ability_spec: Some(Custom("Sword Simple")), +) \ No newline at end of file diff --git a/assets/common/loadout/dungeon/tier-5/cultist.ron b/assets/common/loadout/dungeon/tier-5/cultist.ron new file mode 100644 index 0000000000..426a32c963 --- /dev/null +++ b/assets/common/loadout/dungeon/tier-5/cultist.ron @@ -0,0 +1,17 @@ +({ + Armor(Shoulders): Item("common.items.armor.cultist.shoulder"), + Armor(Chest): Item("common.items.armor.cultist.chest"), + Armor(Belt): Item("common.items.armor.cultist.belt"), + Armor(Hands): Item("common.items.armor.cultist.hand"), + Armor(Legs): Item("common.items.armor.cultist.pants"), + Armor(Feet): Item("common.items.armor.cultist.foot"), + + Armor(Back): Item("common.items.armor.misc.back.dungeon_purple"), + + Lantern: Choice([ + (1.0, Some(Item("common.items.lantern.black_0"))), + (2.0, None), + ]), + + Glider: Item("common.items.glider.glider_purp"), +}) diff --git a/assets/common/loadout/dungeon/tier-5/warlock.ron b/assets/common/loadout/dungeon/tier-5/warlock.ron deleted file mode 100644 index bcc428fbda..0000000000 --- a/assets/common/loadout/dungeon/tier-5/warlock.ron +++ /dev/null @@ -1,17 +0,0 @@ -({ - Armor(Shoulders): Item("common.items.armor.warlock.shoulder"), - Armor(Chest): Item("common.items.armor.warlock.chest"), - Armor(Belt): Item("common.items.armor.warlock.belt"), - Armor(Hands): Item("common.items.armor.warlock.hand"), - Armor(Legs): Item("common.items.armor.warlock.pants"), - Armor(Feet): Item("common.items.armor.warlock.foot"), - - Armor(Back): Item("common.items.armor.warlock.back"), - - Lantern: Choice([ - (1.0, Some(Item("common.items.lantern.black_0"))), - (2.0, None), - ]), - - Glider: Item("common.items.glider.glider_purp"), -}) diff --git a/assets/common/loadout/dungeon/tier-5/warlord.ron b/assets/common/loadout/dungeon/tier-5/warlord.ron deleted file mode 100644 index 2d83fef3fa..0000000000 --- a/assets/common/loadout/dungeon/tier-5/warlord.ron +++ /dev/null @@ -1,17 +0,0 @@ -({ - Armor(Shoulders): Item("common.items.armor.warlord.shoulder"), - Armor(Chest): Item("common.items.armor.warlord.chest"), - Armor(Belt): Item("common.items.armor.warlord.belt"), - Armor(Hands): Item("common.items.armor.warlord.hand"), - Armor(Legs): Item("common.items.armor.warlord.pants"), - Armor(Feet): Item("common.items.armor.warlord.foot"), - - Armor(Back): Item("common.items.armor.warlord.back"), - - Lantern: Choice([ - (1.0, Some(Item("common.items.lantern.black_0"))), - (2.0, None), - ]), - - Glider: Item("common.items.glider.glider_purp"), -}) diff --git a/assets/common/loot_tables/creature/quad_medium/horned.ron b/assets/common/loot_tables/creature/quad_medium/horned.ron new file mode 100644 index 0000000000..d3767b3bc1 --- /dev/null +++ b/assets/common/loot_tables/creature/quad_medium/horned.ron @@ -0,0 +1,4 @@ +[ + (1.0, Item("common.items.crafting_ing.hide.rugged_hide")), + (1.0, Item("common.items.crafting_ing.animal_misc.large_horn")), +] \ No newline at end of file diff --git a/assets/common/loot_tables/creature/quad_medium/mammoth.ron b/assets/common/loot_tables/creature/quad_medium/mammoth.ron new file mode 100644 index 0000000000..73dd2ad8a6 --- /dev/null +++ b/assets/common/loot_tables/creature/quad_medium/mammoth.ron @@ -0,0 +1,4 @@ +[ + (1.0, ItemQuantity("common.items.crafting_ing.hide.tough_hide", 2, 3)), + (1.0, ItemQuantity("common.items.crafting_ing.animal_misc.large_horn", 2, 3)), +] \ No newline at end of file diff --git a/assets/voxygen/voxel/biped_weapon_manifest.ron b/assets/voxygen/voxel/biped_weapon_manifest.ron index 4cf216a2b6..f2210787e1 100644 --- a/assets/voxygen/voxel/biped_weapon_manifest.ron +++ b/assets/voxygen/voxel/biped_weapon_manifest.ron @@ -1070,6 +1070,22 @@ vox_spec: ("weapon.hammer.2hhammer_oni_red", (-3.0, -4.5, -12.0)), color: None ), + "common.items.weapons.sword.bipedlarge-cultist": ( + vox_spec: ("weapon.sword.cultist", (-1.5, -4.0, -6.0)), + color: None + ), + "common.items.weapons.hammer.bipedlarge-cultist_purp_2h-0": ( + vox_spec: ("weapon.hammer.cult_purp-0", (-3.5, -4.5, -5.0)), + color: None + ), + "common.items.weapons.bow.bipedlarge-velorite": ( + vox_spec: ("weapon.bow.velorite", (-1.5, -4.0, -15.0)), + color: None + ), + "common.items.weapons.staff.bipedlarge-cultist_staff": ( + vox_spec: ("weapon.staff.firestaff_cultist", (-2.5, -2.5, -4.0)), + color: None + ), //BIPEDSMALL "common.items.npc_weapons.biped_small.adlet.adlet_bow": ( vox_spec: ("weapon.biped_small.bow.adlet", (-1.5, -3.0, -5.0)), diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 923347e2ba..96ffd4263f 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -306,7 +306,7 @@ impl Body { biped_large::Species::Slysaurok => Vec3::new(4.0, 3.0, 3.4), biped_large::Species::Werewolf => Vec3::new(4.0, 3.0, 3.5), biped_large::Species::Harvester => Vec3::new(4.6, 3.0, 5.4), - + biped_large::Species::Cultistwarlord => Vec3::new(3.0, 3.0, 4.5), _ => Vec3::new(4.6, 3.0, 6.0), }, Body::BipedSmall(body) => match body.species { diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 5571bf7ef9..759b4ece11 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -184,9 +184,7 @@ fn default_main_tool(body: &Body) -> Item { quadruped_medium::Species::Tuskram | quadruped_medium::Species::Roshwalr | quadruped_medium::Species::Moose - | quadruped_medium::Species::Dreadhorn - | quadruped_medium::Species::Mammoth - | quadruped_medium::Species::Ngoubou => Some(Item::new_from_asset_expect( + | quadruped_medium::Species::Dreadhorn => Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.quadmedcharge", )), quadruped_medium::Species::Highland @@ -296,9 +294,12 @@ fn default_main_tool(body: &Body) -> Item { (biped_large::Species::Blueoni, _) => Some(Item::new_from_asset_expect( "common.items.npc_weapons.axe.oni_blue_axe", )), - (biped_large::Species::Redoni | biped_large::Species::Cultistwarlord, _) => Some( - Item::new_from_asset_expect("common.items.npc_weapons.hammer.oni_red_hammer"), - ), + (biped_large::Species::Redoni, _) => Some(Item::new_from_asset_expect( + "common.items.npc_weapons.hammer.oni_red_hammer", + )), + (biped_large::Species::Cultistwarlord, _) => Some(Item::new_from_asset_expect( + "common.items.weapons.sword.bipedlarge-cultist", + )), }, Body::Object(body) => match body { object::Body::Crossbow => Some(Item::new_from_asset_expect( diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 1dfba60f4e..4f640bd1bc 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -421,6 +421,12 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc | quadruped_medium::Species::Yak => { "common.loot_tables.creature.quad_medium.gentle" }, + quadruped_medium::Species::Ngoubou => { + "common.loot_tables.creature.quad_medium.horned" + }, + quadruped_medium::Species::Mammoth => { + "common.loot_tables.creature.quad_medium.mammoth" + }, _ => "common.loot_tables.creature.quad_medium.fanged", }, Some(common::comp::Body::BirdMedium(_)) => { diff --git a/server/src/migrations/V41_remove_warlock_warlord.sql b/server/src/migrations/V41_remove_warlock_warlord.sql new file mode 100644 index 0000000000..e445c8a50d --- /dev/null +++ b/server/src/migrations/V41_remove_warlock_warlord.sql @@ -0,0 +1,33 @@ +-- xx +UPDATE item +SET item_definition_id = 'common.items.armor.warlock.shoulder' WHERE item_definition_id = 'common.items.armor.cultist.shoulder'; +UPDATE item +SET item_definition_id = 'common.items.armor.warlock.chest' WHERE item_definition_id = 'common.items.armor.cultist.chest'; +UPDATE item +SET item_definition_id = 'common.items.armor.warlock.belt' WHERE item_definition_id = 'common.items.armor.cultist.belt'; +UPDATE item +SET item_definition_id = 'common.items.armor.warlock.hand' WHERE item_definition_id = 'common.items.armor.cultist.hand'; +UPDATE item +SET item_definition_id = 'common.items.armor.warlock.pants' WHERE item_definition_id = 'common.items.armor.cultist.pants'; +UPDATE item +SET item_definition_id = 'common.items.armor.warlock.foot' WHERE item_definition_id = 'common.items.armor.cultist.foot'; +UPDATE item +SET item_definition_id = 'common.items.armor.warlock.back' WHERE item_definition_id = 'common.items.armor.misc.back.dungeon_purple'; +UPDATE item +SET item_definition_id = 'common.items.armor.warlock.head' WHERE item_definition_id = 'common.items.armor.cultist.head'; +UPDATE item +SET item_definition_id = 'common.items.armor.warlord.shoulder' WHERE item_definition_id = 'common.items.armor.cultist.shoulder'; +UPDATE item +SET item_definition_id = 'common.items.armor.warlord.chest' WHERE item_definition_id = 'common.items.armor.cultist.chest'; +UPDATE item +SET item_definition_id = 'common.items.armor.warlord.belt' WHERE item_definition_id = 'common.items.armor.cultist.belt'; +UPDATE item +SET item_definition_id = 'common.items.armor.warlord.hand' WHERE item_definition_id = 'common.items.armor.cultist.hand'; +UPDATE item +SET item_definition_id = 'common.items.armor.warlord.pants' WHERE item_definition_id = 'common.items.armor.cultist.pants'; +UPDATE item +SET item_definition_id = 'common.items.armor.warlord.foot' WHERE item_definition_id = 'common.items.armor.cultist.foot'; +UPDATE item +SET item_definition_id = 'common.items.armor.warlord.back' WHERE item_definition_id = 'common.items.armor.misc.back.dungeon_purple'; +UPDATE item +SET item_definition_id = 'common.items.armor.warlord.head' WHERE item_definition_id = 'common.items.armor.cultist.head'; \ No newline at end of file diff --git a/voxygen/anim/src/biped_large/mod.rs b/voxygen/anim/src/biped_large/mod.rs index 74548f9e7c..567df6f2b7 100644 --- a/voxygen/anim/src/biped_large/mod.rs +++ b/voxygen/anim/src/biped_large/mod.rs @@ -157,6 +157,7 @@ pub struct SkeletonAttr { bc: (f32, f32, f32, f32, f32, f32), beast: bool, float: bool, + height: f32, } impl<'a> std::convert::TryFrom<&'a comp::Body> for SkeletonAttr { @@ -199,6 +200,7 @@ impl Default for SkeletonAttr { bc: (0.0, 0.0, 0.0, 0.0, 0.0, 0.0), beast: false, float: false, + height: 0.0, } } } @@ -443,7 +445,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Harvester, _) => (7.5, 0.0), (Blueoni, _) => (12.5, 0.0), (Redoni, _) => (12.5, 0.0), - (Cultistwarlord, _) => (12.5, 0.0), + (Cultistwarlord, _) => (8.0, 0.0), }, shl: match (body.species, body.body_type) { (Dullahan, _) => (-4.75, -11.0, 8.5, 1.47, -0.2, 0.0), @@ -504,6 +506,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { }, beast: matches!((body.species, body.body_type), (Werewolf, _)), float: matches!((body.species, body.body_type), (Mindflayer, _)), + height: comp::Body::BipedLarge(*body).dimensions().z, } } } diff --git a/voxygen/anim/src/biped_large/shockwave.rs b/voxygen/anim/src/biped_large/shockwave.rs index 81d373014a..07614db1d6 100644 --- a/voxygen/anim/src/biped_large/shockwave.rs +++ b/voxygen/anim/src/biped_large/shockwave.rs @@ -62,7 +62,7 @@ impl Animation for ShockwaveAnimation { next.hand_r.orientation = Quaternion::rotation_x(0.0); match active_tool_kind { - Some(ToolKind::Sceptre) => { + Some(ToolKind::Sceptre | ToolKind::Staff) => { next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1); next.hand_l.position = Vec3::new(s_a.sthl.0, s_a.sthl.1, s_a.sthl.2); diff --git a/voxygen/anim/src/biped_large/shoot.rs b/voxygen/anim/src/biped_large/shoot.rs index c5fba1b268..bcebd91075 100644 --- a/voxygen/anim/src/biped_large/shoot.rs +++ b/voxygen/anim/src/biped_large/shoot.rs @@ -156,18 +156,19 @@ impl Animation for ShootAnimation { next.control.position = Vec3::new( -1.0 + move1 * 2.0, 6.0 + s_a.grip.0 / 1.2 + move1 * 7.0, - -5.0 + -s_a.grip.0 / 2.0 + move1 * 8.0, + -5.0 + -s_a.grip.0 / 2.0 + move1 * s_a.height * 2.8, ); next.control_l.orientation = - Quaternion::rotation_x(PI / 2.0 + move2 * 0.4) * Quaternion::rotation_y(-0.2); + Quaternion::rotation_x(move1 * 0.2 + PI / 2.0 + move2 * 0.4) + * Quaternion::rotation_y(-0.2); next.control_r.orientation = Quaternion::rotation_x(PI / 2.2 + move1 * 0.4) - * Quaternion::rotation_y(0.2) + * Quaternion::rotation_y(0.4) * Quaternion::rotation_z(0.0); next.control.orientation = Quaternion::rotation_x(-0.2) * Quaternion::rotation_y(1.0 + move1 * -0.4) - * Quaternion::rotation_z(-0.3); + * Quaternion::rotation_z(-0.1); next.head.orientation = Quaternion::rotation_z(move1 * 0.25); next.shoulder_l.position = Vec3::new( -s_a.shoulder.0, @@ -175,7 +176,7 @@ impl Animation for ShootAnimation { s_a.shoulder.2 - foothorir * 1.0, ); next.shoulder_l.orientation = - Quaternion::rotation_x(move1 * 0.8 + 1.2 * speednorm + (footrotr * -0.2)); + Quaternion::rotation_x(move1 * 1.2 + 1.2 * speednorm + (footrotr * -0.2)); next.shoulder_r.position = Vec3::new( s_a.shoulder.0, diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index bfbfe39a9d..fa228fa181 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -990,7 +990,8 @@ fn enemy_5(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { .with_body(comp::Body::Object(comp::object::Body::Crossbow)) .with_asset_expect("common.entity.dungeon.tier-5.turret"), 1 => entity.with_asset_expect("common.entity.dungeon.tier-5.warlock"), - _ => entity.with_asset_expect("common.entity.dungeon.tier-5.warlord"), + 2 => entity.with_asset_expect("common.entity.dungeon.tier-5.warlord"), + _ => entity.with_asset_expect("common.entity.dungeon.tier-5.cultist"), } } From 58b95d12d42f8160f03fbf7618a087fc8b7d9031 Mon Sep 17 00:00:00 2001 From: Snowram Date: Sun, 20 Jun 2021 04:00:21 +0200 Subject: [PATCH 168/180] Adds alpaca and llama npcs --- .../common/entity/dungeon/tier-5/warlock.ron | 4 +- assets/common/items/armor/warlock/back.ron | 17 --- assets/common/items/armor/warlock/belt.ron | 17 --- assets/common/items/armor/warlock/chest.ron | 17 --- assets/common/items/armor/warlock/foot.ron | 17 --- assets/common/items/armor/warlock/hand.ron | 17 --- assets/common/items/armor/warlock/head.ron | 17 --- assets/common/items/armor/warlock/pants.ron | 17 --- .../common/items/armor/warlock/shoulder.ron | 17 --- assets/common/items/armor/warlord/back.ron | 17 --- assets/common/items/armor/warlord/belt.ron | 17 --- assets/common/items/armor/warlord/chest.ron | 17 --- assets/common/items/armor/warlord/foot.ron | 17 --- assets/common/items/armor/warlord/hand.ron | 17 --- assets/common/items/armor/warlord/head.ron | 17 --- assets/common/items/armor/warlord/pants.ron | 17 --- .../common/items/armor/warlord/shoulder.ron | 17 --- ...staff.ron => bipedlarge-cultist_staff.ron} | 0 assets/common/npc_names.ron | 16 +++ assets/voxygen/item_image_manifest.ron | 58 -------- .../voxel/biped_large_central_manifest.ron | 104 ++++++++++++++ .../voxel/biped_large_lateral_manifest.ron | 136 ++++++++++++++++++ .../voxel/humanoid_armor_back_manifest.ron | 8 -- .../voxel/humanoid_armor_belt_manifest.ron | 8 -- .../voxel/humanoid_armor_chest_manifest.ron | 8 -- .../voxel/humanoid_armor_foot_manifest.ron | 8 -- .../voxel/humanoid_armor_hand_manifest.ron | 20 --- .../voxel/humanoid_armor_pants_manifest.ron | 8 -- .../humanoid_armor_shoulder_manifest.ron | 20 --- assets/voxygen/voxel/npc/alpaca/male/ears.vox | 3 + .../voxygen/voxel/npc/alpaca/male/foot_br.vox | 3 + .../voxygen/voxel/npc/alpaca/male/foot_fr.vox | 3 + assets/voxygen/voxel/npc/alpaca/male/head.vox | 3 + assets/voxygen/voxel/npc/alpaca/male/jaw.vox | 3 + .../voxygen/voxel/npc/alpaca/male/leg_br.vox | 3 + .../voxygen/voxel/npc/alpaca/male/leg_fr.vox | 3 + assets/voxygen/voxel/npc/alpaca/male/neck.vox | 3 + assets/voxygen/voxel/npc/alpaca/male/tail.vox | 3 + .../voxel/npc/alpaca/male/torso_back.vox | 3 + .../voxel/npc/alpaca/male/torso_front.vox | 3 + .../voxel/npc/cultist_warlock/male/foot_l.vox | 3 + .../voxel/npc/cultist_warlock/male/foot_r.vox | 3 + .../voxel/npc/cultist_warlock/male/hand_l.vox | 3 + .../voxel/npc/cultist_warlock/male/hand_r.vox | 3 + .../voxel/npc/cultist_warlock/male/head.vox | 3 + .../voxel/npc/cultist_warlock/male/leg_l.vox | 3 + .../voxel/npc/cultist_warlock/male/leg_r.vox | 3 + .../npc/cultist_warlock/male/shoulder_l.vox | 3 + .../npc/cultist_warlock/male/shoulder_r.vox | 3 + .../npc/cultist_warlock/male/torso_lower.vox | 3 + .../npc/cultist_warlock/male/torso_upper.vox | 3 + .../voxel/npc/husk_brute/male/foot_l.vox | 3 + .../voxel/npc/husk_brute/male/foot_r.vox | 3 + .../voxel/npc/husk_brute/male/hand_l.vox | 3 + .../voxel/npc/husk_brute/male/hand_r.vox | 3 + .../voxel/npc/husk_brute/male/head.vox | 3 + .../voxel/npc/husk_brute/male/leg_l.vox | 3 + .../voxel/npc/husk_brute/male/leg_r.vox | 3 + .../voxel/npc/husk_brute/male/shoulder_l.vox | 3 + .../voxel/npc/husk_brute/male/shoulder_r.vox | 3 + .../voxel/npc/husk_brute/male/torso_lower.vox | 3 + .../voxel/npc/husk_brute/male/torso_upper.vox | 3 + assets/voxygen/voxel/npc/llama/male/ears.vox | 3 + .../voxygen/voxel/npc/llama/male/foot_br.vox | 3 + .../voxygen/voxel/npc/llama/male/foot_fr.vox | 3 + assets/voxygen/voxel/npc/llama/male/head.vox | 3 + assets/voxygen/voxel/npc/llama/male/jaw.vox | 3 + .../voxygen/voxel/npc/llama/male/leg_br.vox | 3 + .../voxygen/voxel/npc/llama/male/leg_fr.vox | 3 + assets/voxygen/voxel/npc/llama/male/neck.vox | 3 + assets/voxygen/voxel/npc/llama/male/tail.vox | 3 + .../voxel/npc/llama/male/torso_back.vox | 3 + .../voxel/npc/llama/male/torso_front.vox | 3 + .../quadruped_medium_central_manifest.ron | 120 ++++++++++++++++ .../quadruped_medium_lateral_manifest.ron | 136 ++++++++++++++++++ common/src/comp/body.rs | 1 + common/src/comp/body/biped_large.rs | 10 +- common/src/comp/body/quadruped_medium.rs | 10 +- common/src/comp/inventory/loadout_builder.rs | 9 +- common/src/states/utils.rs | 2 + server/src/events/entity_manipulation.rs | 4 +- voxygen/anim/src/biped_large/mod.rs | 20 +++ voxygen/anim/src/quadruped_medium/mod.rs | 22 +++ 83 files changed, 718 insertions(+), 418 deletions(-) delete mode 100644 assets/common/items/armor/warlock/back.ron delete mode 100644 assets/common/items/armor/warlock/belt.ron delete mode 100644 assets/common/items/armor/warlock/chest.ron delete mode 100644 assets/common/items/armor/warlock/foot.ron delete mode 100644 assets/common/items/armor/warlock/hand.ron delete mode 100644 assets/common/items/armor/warlock/head.ron delete mode 100644 assets/common/items/armor/warlock/pants.ron delete mode 100644 assets/common/items/armor/warlock/shoulder.ron delete mode 100644 assets/common/items/armor/warlord/back.ron delete mode 100644 assets/common/items/armor/warlord/belt.ron delete mode 100644 assets/common/items/armor/warlord/chest.ron delete mode 100644 assets/common/items/armor/warlord/foot.ron delete mode 100644 assets/common/items/armor/warlord/hand.ron delete mode 100644 assets/common/items/armor/warlord/head.ron delete mode 100644 assets/common/items/armor/warlord/pants.ron delete mode 100644 assets/common/items/armor/warlord/shoulder.ron rename assets/common/items/weapons/staff/{bipedlarge_cultist_staff.ron => bipedlarge-cultist_staff.ron} (100%) create mode 100644 assets/voxygen/voxel/npc/alpaca/male/ears.vox create mode 100644 assets/voxygen/voxel/npc/alpaca/male/foot_br.vox create mode 100644 assets/voxygen/voxel/npc/alpaca/male/foot_fr.vox create mode 100644 assets/voxygen/voxel/npc/alpaca/male/head.vox create mode 100644 assets/voxygen/voxel/npc/alpaca/male/jaw.vox create mode 100644 assets/voxygen/voxel/npc/alpaca/male/leg_br.vox create mode 100644 assets/voxygen/voxel/npc/alpaca/male/leg_fr.vox create mode 100644 assets/voxygen/voxel/npc/alpaca/male/neck.vox create mode 100644 assets/voxygen/voxel/npc/alpaca/male/tail.vox create mode 100644 assets/voxygen/voxel/npc/alpaca/male/torso_back.vox create mode 100644 assets/voxygen/voxel/npc/alpaca/male/torso_front.vox create mode 100644 assets/voxygen/voxel/npc/cultist_warlock/male/foot_l.vox create mode 100644 assets/voxygen/voxel/npc/cultist_warlock/male/foot_r.vox create mode 100644 assets/voxygen/voxel/npc/cultist_warlock/male/hand_l.vox create mode 100644 assets/voxygen/voxel/npc/cultist_warlock/male/hand_r.vox create mode 100644 assets/voxygen/voxel/npc/cultist_warlock/male/head.vox create mode 100644 assets/voxygen/voxel/npc/cultist_warlock/male/leg_l.vox create mode 100644 assets/voxygen/voxel/npc/cultist_warlock/male/leg_r.vox create mode 100644 assets/voxygen/voxel/npc/cultist_warlock/male/shoulder_l.vox create mode 100644 assets/voxygen/voxel/npc/cultist_warlock/male/shoulder_r.vox create mode 100644 assets/voxygen/voxel/npc/cultist_warlock/male/torso_lower.vox create mode 100644 assets/voxygen/voxel/npc/cultist_warlock/male/torso_upper.vox create mode 100644 assets/voxygen/voxel/npc/husk_brute/male/foot_l.vox create mode 100644 assets/voxygen/voxel/npc/husk_brute/male/foot_r.vox create mode 100644 assets/voxygen/voxel/npc/husk_brute/male/hand_l.vox create mode 100644 assets/voxygen/voxel/npc/husk_brute/male/hand_r.vox create mode 100644 assets/voxygen/voxel/npc/husk_brute/male/head.vox create mode 100644 assets/voxygen/voxel/npc/husk_brute/male/leg_l.vox create mode 100644 assets/voxygen/voxel/npc/husk_brute/male/leg_r.vox create mode 100644 assets/voxygen/voxel/npc/husk_brute/male/shoulder_l.vox create mode 100644 assets/voxygen/voxel/npc/husk_brute/male/shoulder_r.vox create mode 100644 assets/voxygen/voxel/npc/husk_brute/male/torso_lower.vox create mode 100644 assets/voxygen/voxel/npc/husk_brute/male/torso_upper.vox create mode 100644 assets/voxygen/voxel/npc/llama/male/ears.vox create mode 100644 assets/voxygen/voxel/npc/llama/male/foot_br.vox create mode 100644 assets/voxygen/voxel/npc/llama/male/foot_fr.vox create mode 100644 assets/voxygen/voxel/npc/llama/male/head.vox create mode 100644 assets/voxygen/voxel/npc/llama/male/jaw.vox create mode 100644 assets/voxygen/voxel/npc/llama/male/leg_br.vox create mode 100644 assets/voxygen/voxel/npc/llama/male/leg_fr.vox create mode 100644 assets/voxygen/voxel/npc/llama/male/neck.vox create mode 100644 assets/voxygen/voxel/npc/llama/male/tail.vox create mode 100644 assets/voxygen/voxel/npc/llama/male/torso_back.vox create mode 100644 assets/voxygen/voxel/npc/llama/male/torso_front.vox diff --git a/assets/common/entity/dungeon/tier-5/warlock.ron b/assets/common/entity/dungeon/tier-5/warlock.ron index 6ee268d287..3ad3030460 100644 --- a/assets/common/entity/dungeon/tier-5/warlock.ron +++ b/assets/common/entity/dungeon/tier-5/warlock.ron @@ -1,10 +1,10 @@ EntityConfig ( name: Some("Cultist Warlock"), - body: Some(RandomWith("cultist_warlord")), + body: Some(RandomWith("cultist_warlock")), loot: Some(LootTable("common.loot_tables.dungeon.tier-5.enemy")), - main_tool: Some(Item("common.items.weapons.staff.cultist_staff")), + main_tool: Some(Item("common.items.weapons.staff.bipedlarge-cultist_staff")), second_tool: None, loadout_asset: None, diff --git a/assets/common/items/armor/warlock/back.ron b/assets/common/items/armor/warlock/back.ron deleted file mode 100644 index 8f71921ef1..0000000000 --- a/assets/common/items/armor/warlock/back.ron +++ /dev/null @@ -1,17 +0,0 @@ -ItemDef( - name: "Warlock cape", - description: "Belong to a mighty warlock.", - kind: Armor(( - kind: Back("Warlock"), - stats: ( - protection: Normal(4.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, - stealth: 0.0, - ), - )), - quality: Moderate, - tags: [], -) \ No newline at end of file diff --git a/assets/common/items/armor/warlock/belt.ron b/assets/common/items/armor/warlock/belt.ron deleted file mode 100644 index 571cf5f704..0000000000 --- a/assets/common/items/armor/warlock/belt.ron +++ /dev/null @@ -1,17 +0,0 @@ -ItemDef( - name: "Warlock Belt", - description: "Belong to a mighty warlock.", - kind: Armor(( - kind: Belt("Warlock"), - stats: ( - protection: Normal(8.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, - stealth: 0.0, - ), - )), - quality: Moderate, - tags: [], -) \ No newline at end of file diff --git a/assets/common/items/armor/warlock/chest.ron b/assets/common/items/armor/warlock/chest.ron deleted file mode 100644 index 83879ed7b8..0000000000 --- a/assets/common/items/armor/warlock/chest.ron +++ /dev/null @@ -1,17 +0,0 @@ -ItemDef( - name: "Warlock Vest", - description: "Belong to a mighty warlock.", - kind: Armor(( - kind: Chest("Warlock"), - stats: ( - protection: Normal(40.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, - stealth: 0.0, - ), - )), - quality: Moderate, - tags: [], -) \ No newline at end of file diff --git a/assets/common/items/armor/warlock/foot.ron b/assets/common/items/armor/warlock/foot.ron deleted file mode 100644 index 6d94314cea..0000000000 --- a/assets/common/items/armor/warlock/foot.ron +++ /dev/null @@ -1,17 +0,0 @@ -ItemDef( - name: "Warlock Slippers", - description: "Belong to a mighty warlock.", - kind: Armor(( - kind: Foot("Warlock"), - stats: ( - protection: Normal(8.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, - stealth: 0.0, - ), - )), - quality: Moderate, - tags: [], -) \ No newline at end of file diff --git a/assets/common/items/armor/warlock/hand.ron b/assets/common/items/armor/warlock/hand.ron deleted file mode 100644 index 95a430624d..0000000000 --- a/assets/common/items/armor/warlock/hand.ron +++ /dev/null @@ -1,17 +0,0 @@ -ItemDef( - name: "Warlock Gloves", - description: "Belong to a mighty warlock.", - kind: Armor(( - kind: Hand("Warlock"), - stats: ( - protection: Normal(15.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, - stealth: 0.0, - ), - )), - quality: Common, - tags: [], -) \ No newline at end of file diff --git a/assets/common/items/armor/warlock/head.ron b/assets/common/items/armor/warlock/head.ron deleted file mode 100644 index 5efede3c7e..0000000000 --- a/assets/common/items/armor/warlock/head.ron +++ /dev/null @@ -1,17 +0,0 @@ -ItemDef( - name: "Warlock", - description: "Belong to a mighty warlock.", - kind: Armor(( - kind: Head("Warlock"), - stats: ( - protection: Normal(10.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, - stealth: 0.0, - ), - )), - quality: Common, - tags: [], -) \ No newline at end of file diff --git a/assets/common/items/armor/warlock/pants.ron b/assets/common/items/armor/warlock/pants.ron deleted file mode 100644 index 6140b196af..0000000000 --- a/assets/common/items/armor/warlock/pants.ron +++ /dev/null @@ -1,17 +0,0 @@ -ItemDef( - name: "Warlock Kilt", - description: "Belong to a mighty warlock.", - kind: Armor(( - kind: Pants("Warlock"), - stats: ( - protection: Normal(30.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, - stealth: 0.0, - ), - )), - quality: Moderate, - tags: [], -) \ No newline at end of file diff --git a/assets/common/items/armor/warlock/shoulder.ron b/assets/common/items/armor/warlock/shoulder.ron deleted file mode 100644 index 105a3458c6..0000000000 --- a/assets/common/items/armor/warlock/shoulder.ron +++ /dev/null @@ -1,17 +0,0 @@ -ItemDef( - name: "Warlock Shoulders", - description: "Belong to a mighty warlock.", - kind: Armor(( - kind: Shoulder("Warlock"), - stats: ( - protection: Normal(22.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, - stealth: 0.0, - ), - )), - quality: Common, - tags: [], -) \ No newline at end of file diff --git a/assets/common/items/armor/warlord/back.ron b/assets/common/items/armor/warlord/back.ron deleted file mode 100644 index 2cd9450e7e..0000000000 --- a/assets/common/items/armor/warlord/back.ron +++ /dev/null @@ -1,17 +0,0 @@ -ItemDef( - name: "Warlord cape", - description: "Belong to a mighty warlord.", - kind: Armor(( - kind: Back("Warlord"), - stats: ( - protection: Normal(4.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, - stealth: 0.0, - ), - )), - quality: Moderate, - tags: [], -) \ No newline at end of file diff --git a/assets/common/items/armor/warlord/belt.ron b/assets/common/items/armor/warlord/belt.ron deleted file mode 100644 index ae60e2fcd2..0000000000 --- a/assets/common/items/armor/warlord/belt.ron +++ /dev/null @@ -1,17 +0,0 @@ -ItemDef( - name: "Warlord Belt", - description: "Belong to a mighty warlord.", - kind: Armor(( - kind: Belt("Warlord"), - stats: ( - protection: Normal(8.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, - stealth: 0.0, - ), - )), - quality: Moderate, - tags: [], -) \ No newline at end of file diff --git a/assets/common/items/armor/warlord/chest.ron b/assets/common/items/armor/warlord/chest.ron deleted file mode 100644 index dbaee7548f..0000000000 --- a/assets/common/items/armor/warlord/chest.ron +++ /dev/null @@ -1,17 +0,0 @@ -ItemDef( - name: "Warlord Cuirass", - description: "Belong to a mighty warlord.", - kind: Armor(( - kind: Chest("Warlord"), - stats: ( - protection: Normal(40.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, - stealth: 0.0, - ), - )), - quality: Moderate, - tags: [], -) \ No newline at end of file diff --git a/assets/common/items/armor/warlord/foot.ron b/assets/common/items/armor/warlord/foot.ron deleted file mode 100644 index 993cd6d067..0000000000 --- a/assets/common/items/armor/warlord/foot.ron +++ /dev/null @@ -1,17 +0,0 @@ -ItemDef( - name: "Warlord Feet", - description: "Belong to a mighty warlord.", - kind: Armor(( - kind: Foot("Warlord"), - stats: ( - protection: Normal(8.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, - stealth: 0.0, - ), - )), - quality: Moderate, - tags: [], -) \ No newline at end of file diff --git a/assets/common/items/armor/warlord/hand.ron b/assets/common/items/armor/warlord/hand.ron deleted file mode 100644 index 36f79edf77..0000000000 --- a/assets/common/items/armor/warlord/hand.ron +++ /dev/null @@ -1,17 +0,0 @@ -ItemDef( - name: "Warlord Handguards", - description: "Belong to a mighty warlord.", - kind: Armor(( - kind: Hand("Warlord"), - stats: ( - protection: Normal(15.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, - stealth: 0.0, - ), - )), - quality: Common, - tags: [], -) \ No newline at end of file diff --git a/assets/common/items/armor/warlord/head.ron b/assets/common/items/armor/warlord/head.ron deleted file mode 100644 index ba518e0cdf..0000000000 --- a/assets/common/items/armor/warlord/head.ron +++ /dev/null @@ -1,17 +0,0 @@ -ItemDef( - name: "Warlord Helmet", - description: "Belong to a mighty warlord.", - kind: Armor(( - kind: Head("Warlord"), - stats: ( - protection: Normal(10.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, - stealth: 0.0, - ), - )), - quality: Common, - tags: [], -) \ No newline at end of file diff --git a/assets/common/items/armor/warlord/pants.ron b/assets/common/items/armor/warlord/pants.ron deleted file mode 100644 index b896347aa2..0000000000 --- a/assets/common/items/armor/warlord/pants.ron +++ /dev/null @@ -1,17 +0,0 @@ -ItemDef( - name: "Warlord Legguards", - description: "Belong to a mighty warlord.", - kind: Armor(( - kind: Pants("Warlord"), - stats: ( - protection: Normal(30.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, - stealth: 0.0, - ), - )), - quality: Moderate, - tags: [], -) \ No newline at end of file diff --git a/assets/common/items/armor/warlord/shoulder.ron b/assets/common/items/armor/warlord/shoulder.ron deleted file mode 100644 index c38f423aff..0000000000 --- a/assets/common/items/armor/warlord/shoulder.ron +++ /dev/null @@ -1,17 +0,0 @@ -ItemDef( - name: "Warlord Shoulderguards", - description: "Belong to a mighty warlord.", - kind: Armor(( - kind: Shoulder("Warlord"), - stats: ( - protection: Normal(22.0), - poise_resilience: Normal(0.0), - energy_max: 0, - energy_reward: 0.0, - crit_power: 0.0, - stealth: 0.0, - ), - )), - quality: Common, - tags: [], -) \ No newline at end of file diff --git a/assets/common/items/weapons/staff/bipedlarge_cultist_staff.ron b/assets/common/items/weapons/staff/bipedlarge-cultist_staff.ron similarity index 100% rename from assets/common/items/weapons/staff/bipedlarge_cultist_staff.ron rename to assets/common/items/weapons/staff/bipedlarge-cultist_staff.ron diff --git a/assets/common/npc_names.ron b/assets/common/npc_names.ron index c898788d85..0339622c22 100644 --- a/assets/common/npc_names.ron +++ b/assets/common/npc_names.ron @@ -555,6 +555,14 @@ keyword: "ngoubou", generic: "Ngoubou" ), + llama: ( + keyword: "llama", + generic: "Llama" + ), + alpaca: ( + keyword: "alpaca", + generic: "Alpaca" + ), ) ), quadruped_small: ( @@ -898,6 +906,14 @@ keyword: "cultist_warlord", generic: "Cultist Warlord" ), + cultist_warlock: ( + keyword: "cultist_warlock", + generic: "Cultist Warlock" + ), + husk_brute: ( + keyword: "husk_brute", + generic: "Husk Brute" + ), ) ), golem: ( diff --git a/assets/voxygen/item_image_manifest.ron b/assets/voxygen/item_image_manifest.ron index 8159668653..6d6e8d75ce 100644 --- a/assets/voxygen/item_image_manifest.ron +++ b/assets/voxygen/item_image_manifest.ron @@ -1548,56 +1548,6 @@ "voxel.armor.bonerattler.shoulder", (0.0, 0.0, 0.0), (-90.0, 130.0, 0.0), 1.2, ), - //Warlord Set - Armor(Chest("Warlord")): VoxTrans( - "voxel.armor.warlord.chest", - (0.0, 0.0, 0.0), (-90.0, 180.0, 0.0), 1.2, - ), - Armor(Pants("Warlord")): VoxTrans( - "voxel.armor.warlord.pants", - (0.0, 0.0, 0.0), (-90.0, 180.0, 0.0), 1.2, - ), - Armor(Belt("Warlord")): VoxTrans( - "voxel.armor.warlord.belt", - (0.0, 0.0, 0.0), (-90.0, 180.0, 0.0), 1.4, - ), - Armor(Foot("Warlord")): VoxTrans( - "voxel.armor.warlord.foot", - (0.0, 0.0, 0.0), (-95.0, 140.0, 0.0), 1.1, - ), - Armor(Hand("Warlord")): VoxTrans( - "voxel.armor.warlord.hand", - (0.0, -1.0, 0.0), (-90.0, 135.0, 0.0), 1.0, - ), - Armor(Shoulder("Warlord")): VoxTrans( - "voxel.armor.warlord.shoulder", - (0.0, 0.0, 0.0), (-90.0, 130.0, 0.0), 1.2, - ), - //Warlock Set - Armor(Chest("Warlock")): VoxTrans( - "voxel.armor.warlock.chest", - (0.0, 0.0, 0.0), (-90.0, 180.0, 0.0), 1.2, - ), - Armor(Pants("Warlock")): VoxTrans( - "voxel.armor.warlock.pants", - (0.0, 0.0, 0.0), (-90.0, 180.0, 0.0), 1.2, - ), - Armor(Belt("Warlock")): VoxTrans( - "voxel.armor.warlock.belt", - (0.0, 0.0, 0.0), (-90.0, 180.0, 0.0), 1.4, - ), - Armor(Foot("Warlock")): VoxTrans( - "voxel.armor.warlock.foot", - (0.0, 0.0, 0.0), (-95.0, 140.0, 0.0), 1.1, - ), - Armor(Hand("Warlock")): VoxTrans( - "voxel.armor.warlock.hand", - (0.0, -1.0, 0.0), (-90.0, 135.0, 0.0), 1.0, - ), - Armor(Shoulder("Warlock")): VoxTrans( - "voxel.armor.warlock.shoulder", - (0.0, 0.0, 0.0), (-90.0, 130.0, 0.0), 1.2, - ), //Rawhide Set Armor(Chest("Rawhide")): VoxTrans( "voxel.armor.hide.rawhide.chest", @@ -2183,14 +2133,6 @@ "voxel.armor.misc.back.backpack", (0.0, 0.0, 0.0), (-90.0, 0.0, 0.0), 1.0, ), - Armor(Back("Warlord")): VoxTrans( - "voxel.armor.warlord.back", - (0.0, 0.0, 0.0), (-90.0, 0.0, 0.0), 1.0, - ), - Armor(Back("Warlock")): VoxTrans( - "voxel.armor.warlock.back", - (0.0, 0.0, 0.0), (-90.0, 0.0, 0.0), 1.0, - ), // Rings Armor(Ring("Scratched")): VoxTrans( "voxel.armor.misc.ring.scratched", diff --git a/assets/voxygen/voxel/biped_large_central_manifest.ron b/assets/voxygen/voxel/biped_large_central_manifest.ron index 5f489af07c..2ecc30c8f1 100644 --- a/assets/voxygen/voxel/biped_large_central_manifest.ron +++ b/assets/voxygen/voxel/biped_large_central_manifest.ron @@ -988,4 +988,108 @@ central: ("armor.empty"), ) ), + (Cultistwarlock, Male): ( + head: ( + offset: (-6.0, -5.5, -6.5), + central: ("npc.cultist_warlock.male.head"), + ), + torso_upper: ( + offset: (-7.0, -5.5, -5.5), + central: ("npc.cultist_warlock.male.torso_upper"), + ), + torso_lower: ( + offset: (-6.0, -4.5, -10.0), + central: ("npc.cultist_warlock.male.torso_lower"), + ), + jaw: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + tail: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + second: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), + (Cultistwarlock, Female): ( + head: ( + offset: (-6.0, -5.5, -6.5), + central: ("npc.cultist_warlock.male.head"), + ), + torso_upper: ( + offset: (-7.0, -5.5, -5.5), + central: ("npc.cultist_warlock.male.torso_upper"), + ), + torso_lower: ( + offset: (-6.0, -4.5, -10.0), + central: ("npc.cultist_warlock.male.torso_lower"), + ), + jaw: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + tail: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + second: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), + (Huskbrute, Male): ( + head: ( + offset: (-6.0, -6.0, -6.5), + central: ("npc.husk_brute.male.head"), + ), + torso_upper: ( + offset: (-8.0, -8.0, -8.0), + central: ("npc.husk_brute.male.torso_upper"), + ), + torso_lower: ( + offset: (-7.0, -6.5, -9.0), + central: ("npc.husk_brute.male.torso_lower"), + ), + jaw: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + tail: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + second: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), + (Huskbrute, Female): ( + head: ( + offset: (-6.0, -6.0, -6.5), + central: ("npc.husk_brute.male.head"), + ), + torso_upper: ( + offset: (-8.0, -8.0, -8.0), + central: ("npc.husk_brute.male.torso_upper"), + ), + torso_lower: ( + offset: (-7.0, -6.5, -9.0), + central: ("npc.husk_brute.male.torso_lower"), + ), + jaw: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + tail: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + second: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), }) diff --git a/assets/voxygen/voxel/biped_large_lateral_manifest.ron b/assets/voxygen/voxel/biped_large_lateral_manifest.ron index 184d2a10a2..c5f8f2f087 100644 --- a/assets/voxygen/voxel/biped_large_lateral_manifest.ron +++ b/assets/voxygen/voxel/biped_large_lateral_manifest.ron @@ -1291,4 +1291,140 @@ lateral: ("npc.cultist_warlord.male.foot_r"), ) ), + (Cultistwarlock, Male): ( + shoulder_l: ( + offset: (-3.5, -3.5, -3.0), + lateral: ("npc.cultist_warlock.male.shoulder_l"), + ), + shoulder_r: ( + offset: (-3.5, -3.5, -3.0), + lateral: ("npc.cultist_warlock.male.shoulder_r"), + ), + hand_l: ( + offset: (-3.5, -3.5, -10.0), + lateral: ("npc.cultist_warlock.male.hand_l"), + ), + hand_r: ( + offset: (-3.5, -3.5, -10.0), + lateral: ("npc.cultist_warlock.male.hand_r"), + ), + leg_l: ( + offset: (-1.5, -2.0, -2.5), + lateral: ("npc.cultist_warlock.male.leg_l"), + ), + leg_r: ( + offset: (-1.5, -2.0, -2.5), + lateral: ("npc.cultist_warlock.male.leg_r"), + ), + foot_l: ( + offset: (-2.5, -4.0, -4.0), + lateral: ("npc.cultist_warlock.male.foot_l"), + ), + foot_r: ( + offset: (-2.5, -4.0, -4.0), + lateral: ("npc.cultist_warlock.male.foot_r"), + ) + ), + (Cultistwarlock, Female): ( + shoulder_l: ( + offset: (-3.5, -3.5, -3.0), + lateral: ("npc.cultist_warlock.male.shoulder_l"), + ), + shoulder_r: ( + offset: (-3.5, -3.5, -3.0), + lateral: ("npc.cultist_warlock.male.shoulder_r"), + ), + hand_l: ( + offset: (-3.5, -3.5, -10.0), + lateral: ("npc.cultist_warlock.male.hand_l"), + ), + hand_r: ( + offset: (-3.5, -3.5, -10.0), + lateral: ("npc.cultist_warlock.male.hand_r"), + ), + leg_l: ( + offset: (-1.5, -2.0, -2.5), + lateral: ("npc.cultist_warlock.male.leg_l"), + ), + leg_r: ( + offset: (-1.5, -2.0, -2.5), + lateral: ("npc.cultist_warlock.male.leg_r"), + ), + foot_l: ( + offset: (-2.5, -4.0, -4.0), + lateral: ("npc.cultist_warlock.male.foot_l"), + ), + foot_r: ( + offset: (-2.5, -4.0, -4.0), + lateral: ("npc.cultist_warlock.male.foot_r"), + ) + ), + (Huskbrute, Male): ( + shoulder_l: ( + offset: (-4.0, -4.5, -4.0), + lateral: ("npc.husk_brute.male.shoulder_l"), + ), + shoulder_r: ( + offset: (-4.0, -4.5, -4.0), + lateral: ("npc.husk_brute.male.shoulder_r"), + ), + hand_l: ( + offset: (-4.0, -3.5, -17.0), + lateral: ("npc.husk_brute.male.hand_l"), + ), + hand_r: ( + offset: (-4.0, -3.5, -17.0), + lateral: ("npc.husk_brute.male.hand_r"), + ), + leg_l: ( + offset: (-2.0, -2.5, -3.5), + lateral: ("npc.husk_brute.male.leg_l"), + ), + leg_r: ( + offset: (-2.0, -2.5, -3.5), + lateral: ("npc.husk_brute.male.leg_r"), + ), + foot_l: ( + offset: (-2.5, -3.5, -3.5), + lateral: ("npc.husk_brute.male.foot_l"), + ), + foot_r: ( + offset: (-2.5, -3.5, -3.5), + lateral: ("npc.husk_brute.male.foot_r"), + ) + ), + (Huskbrute, Female): ( + shoulder_l: ( + offset: (-4.0, -4.5, -4.0), + lateral: ("npc.husk_brute.male.shoulder_l"), + ), + shoulder_r: ( + offset: (-4.0, -4.5, -4.0), + lateral: ("npc.husk_brute.male.shoulder_r"), + ), + hand_l: ( + offset: (-4.0, -3.5, -17.0), + lateral: ("npc.husk_brute.male.hand_l"), + ), + hand_r: ( + offset: (-4.0, -3.5, -17.0), + lateral: ("npc.husk_brute.male.hand_r"), + ), + leg_l: ( + offset: (-2.0, -2.5, -3.5), + lateral: ("npc.husk_brute.male.leg_l"), + ), + leg_r: ( + offset: (-2.0, -2.5, -3.5), + lateral: ("npc.husk_brute.male.leg_r"), + ), + foot_l: ( + offset: (-2.5, -3.5, -3.5), + lateral: ("npc.husk_brute.male.foot_l"), + ), + foot_r: ( + offset: (-2.5, -3.5, -3.5), + lateral: ("npc.husk_brute.male.foot_r"), + ) + ), }) \ No newline at end of file diff --git a/assets/voxygen/voxel/humanoid_armor_back_manifest.ron b/assets/voxygen/voxel/humanoid_armor_back_manifest.ron index 61f2a8225d..1459d036eb 100644 --- a/assets/voxygen/voxel/humanoid_armor_back_manifest.ron +++ b/assets/voxygen/voxel/humanoid_armor_back_manifest.ron @@ -40,14 +40,6 @@ vox_spec: ("armor.leather_blue.back", (-5.0, -1.0, -11.0)), color: None ), - "Warlord": ( - vox_spec: ("armor.warlord.back", (-5.0, -1.0, -14.0)), - color: None - ), - "Warlock": ( - vox_spec: ("armor.warlock.back", (-5.0, -1.5, -15.5)), - color: None - ), "Rawhide": ( vox_spec: ("armor.hide.rawhide.back", (-4.0, -1.0, -6.0)), color: None diff --git a/assets/voxygen/voxel/humanoid_armor_belt_manifest.ron b/assets/voxygen/voxel/humanoid_armor_belt_manifest.ron index 3678edd514..e6b95396a8 100644 --- a/assets/voxygen/voxel/humanoid_armor_belt_manifest.ron +++ b/assets/voxygen/voxel/humanoid_armor_belt_manifest.ron @@ -72,14 +72,6 @@ vox_spec: ("armor.velorite_battlemage.belt", (-5.0, -3.5, 2.0)), color: None ), - "Warlord":( - vox_spec: ("armor.warlord.belt", (-4.0, -3.5, 1.0)), - color: None - ), - "Warlock":( - vox_spec: ("armor.warlock.belt", (-4.0, -3.0, 2.0)), - color: None - ), "Rawhide": ( vox_spec: ("armor.hide.rawhide.belt", (-4.0, -3.5, 2.0)), color: None diff --git a/assets/voxygen/voxel/humanoid_armor_chest_manifest.ron b/assets/voxygen/voxel/humanoid_armor_chest_manifest.ron index cbe2304567..db7a6b80ec 100644 --- a/assets/voxygen/voxel/humanoid_armor_chest_manifest.ron +++ b/assets/voxygen/voxel/humanoid_armor_chest_manifest.ron @@ -142,14 +142,6 @@ vox_spec: ("armor.velorite_battlemage.chest", (-7.0, -3.5, 0.5)), color: None ), - "Warlord": ( - vox_spec: ("armor.warlord.chest", (-7.0, -3.5, -0.5)), - color: None - ), - "Warlock": ( - vox_spec: ("armor.warlock.chest", (-7.0, -3.5, 0.5)), - color: None - ), "Rawhide": ( vox_spec: ("armor.hide.rawhide.chest", (-7.0, -3.5, 2.0)), color: None diff --git a/assets/voxygen/voxel/humanoid_armor_foot_manifest.ron b/assets/voxygen/voxel/humanoid_armor_foot_manifest.ron index 5895fd0204..617d459fb3 100644 --- a/assets/voxygen/voxel/humanoid_armor_foot_manifest.ron +++ b/assets/voxygen/voxel/humanoid_armor_foot_manifest.ron @@ -72,14 +72,6 @@ vox_spec: ("armor.velorite_battlemage.foot", (-2.5, -3.5, -2.0)), color: None ), - "Warlord": ( - vox_spec: ("armor.warlord.foot", (-2.5, -3.5, -2.0)), - color: None - ), - "Warlock": ( - vox_spec: ("armor.warlock.foot", (-2.5, -3.5, -2.0)), - color: None - ), "Rawhide": ( vox_spec: ("armor.hide.rawhide.foot", (-2.5, -3.5, -2.0)), color: None diff --git a/assets/voxygen/voxel/humanoid_armor_hand_manifest.ron b/assets/voxygen/voxel/humanoid_armor_hand_manifest.ron index de569659e1..7d3bff8625 100644 --- a/assets/voxygen/voxel/humanoid_armor_hand_manifest.ron +++ b/assets/voxygen/voxel/humanoid_armor_hand_manifest.ron @@ -150,26 +150,6 @@ color: None ) ), - "Warlord": ( - left: ( - vox_spec: ("armor.warlord.hand", (-3.0, -2.0, -5.0)), - color: None - ), - right: ( - vox_spec: ("armor.warlord.hand", (-2.0, -2.0, -5.0)), - color: None - ) - ), - "Warlock": ( - left: ( - vox_spec: ("armor.warlock.hand", (-4.5, -3.5, -5.0)), - color: None - ), - right: ( - vox_spec: ("armor.warlock.hand", (-2.5, -3.5, -5.0)), - color: None - ) - ), "Rawhide": ( left: ( vox_spec: ("armor.hide.rawhide.hand", (-1.5, -1.5, -2.5)), diff --git a/assets/voxygen/voxel/humanoid_armor_pants_manifest.ron b/assets/voxygen/voxel/humanoid_armor_pants_manifest.ron index c92c63f592..9aa5dde696 100644 --- a/assets/voxygen/voxel/humanoid_armor_pants_manifest.ron +++ b/assets/voxygen/voxel/humanoid_armor_pants_manifest.ron @@ -100,14 +100,6 @@ vox_spec: ("armor.leather_blue.pants", (-5.0, -3.5, 1.0)), color: None ), - "Warlord": ( - vox_spec: ("armor.warlord.pants", (-6.0, -4.5, -2.0)), - color: None - ), - "Warlock": ( - vox_spec: ("armor.warlock.pants", (-5.0, -4.0, -2.0)), - color: None - ), "Rawhide": ( vox_spec: ("armor.hide.rawhide.pants", (-5.0, -3.5, 1.0)), color: None diff --git a/assets/voxygen/voxel/humanoid_armor_shoulder_manifest.ron b/assets/voxygen/voxel/humanoid_armor_shoulder_manifest.ron index 5203c3c5d8..0b027a70a6 100644 --- a/assets/voxygen/voxel/humanoid_armor_shoulder_manifest.ron +++ b/assets/voxygen/voxel/humanoid_armor_shoulder_manifest.ron @@ -231,26 +231,6 @@ color: None ) ), - "Warlord": ( - left: ( - vox_spec: ("armor.warlord.shoulder", (-8.0, -3.5 , -2.5)), - color: None - ), - right: ( - vox_spec: ("armor.warlord.shoulder", (-1.0, -3.5 , -2.5)), - color: None - ) - ), - "Warlock": ( - left: ( - vox_spec: ("armor.warlock.shoulder", (-6.0, -4.0 , -2.0)), - color: None - ), - right: ( - vox_spec: ("armor.warlock.shoulder", (0.0, -4.0, -2.0)), - color: None - ) - ), "Rawhide": ( left: ( vox_spec: ("armor.hide.rawhide.shoulder", (-4.0, -3.2, -0.5)), diff --git a/assets/voxygen/voxel/npc/alpaca/male/ears.vox b/assets/voxygen/voxel/npc/alpaca/male/ears.vox new file mode 100644 index 0000000000..49f210963f --- /dev/null +++ b/assets/voxygen/voxel/npc/alpaca/male/ears.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fdad078882b54abf446d5edfa8d78a887167ccc1930ac6ce670eb37f3508e055 +size 1160 diff --git a/assets/voxygen/voxel/npc/alpaca/male/foot_br.vox b/assets/voxygen/voxel/npc/alpaca/male/foot_br.vox new file mode 100644 index 0000000000..f2eb6ef3c8 --- /dev/null +++ b/assets/voxygen/voxel/npc/alpaca/male/foot_br.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6080c3cd99c4c589ff55c4b9ee57ef9680a0b6df33deb51506ca7cd41e7b0950 +size 1308 diff --git a/assets/voxygen/voxel/npc/alpaca/male/foot_fr.vox b/assets/voxygen/voxel/npc/alpaca/male/foot_fr.vox new file mode 100644 index 0000000000..945049145d --- /dev/null +++ b/assets/voxygen/voxel/npc/alpaca/male/foot_fr.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4fa63a6c47240fec203c05fcb318488e47b9aa52546617bb0f008ae7e2792f04 +size 1280 diff --git a/assets/voxygen/voxel/npc/alpaca/male/head.vox b/assets/voxygen/voxel/npc/alpaca/male/head.vox new file mode 100644 index 0000000000..c461d33700 --- /dev/null +++ b/assets/voxygen/voxel/npc/alpaca/male/head.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c00b97957b55be36b0997f469fddf94bee64a27585ea11cd2397e3dfee4233d7 +size 2440 diff --git a/assets/voxygen/voxel/npc/alpaca/male/jaw.vox b/assets/voxygen/voxel/npc/alpaca/male/jaw.vox new file mode 100644 index 0000000000..b2a82b30da --- /dev/null +++ b/assets/voxygen/voxel/npc/alpaca/male/jaw.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c4dc44735ca5304a10f42d3293c2dcd9f990460f3699ca903454cb07d37b17b0 +size 1120 diff --git a/assets/voxygen/voxel/npc/alpaca/male/leg_br.vox b/assets/voxygen/voxel/npc/alpaca/male/leg_br.vox new file mode 100644 index 0000000000..c56f91a222 --- /dev/null +++ b/assets/voxygen/voxel/npc/alpaca/male/leg_br.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d98172a595b57ad9753a57f15bac269dd3d76db7d74d9856fdfdcb9d6ef5295a +size 1680 diff --git a/assets/voxygen/voxel/npc/alpaca/male/leg_fr.vox b/assets/voxygen/voxel/npc/alpaca/male/leg_fr.vox new file mode 100644 index 0000000000..887ba16285 --- /dev/null +++ b/assets/voxygen/voxel/npc/alpaca/male/leg_fr.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:269e2de8b4218c5a2c5aa20f0dacbadbc654ea0a26ec3d41c0cf65958ae438b9 +size 1480 diff --git a/assets/voxygen/voxel/npc/alpaca/male/neck.vox b/assets/voxygen/voxel/npc/alpaca/male/neck.vox new file mode 100644 index 0000000000..b04ba96fb8 --- /dev/null +++ b/assets/voxygen/voxel/npc/alpaca/male/neck.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:60427aa00f289c9db4344a00363f3e1b593fe769029b6ea67bfa189aebe9901c +size 2544 diff --git a/assets/voxygen/voxel/npc/alpaca/male/tail.vox b/assets/voxygen/voxel/npc/alpaca/male/tail.vox new file mode 100644 index 0000000000..bc0ab0cce8 --- /dev/null +++ b/assets/voxygen/voxel/npc/alpaca/male/tail.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d971b2f9ea4aafc9418be9b9a3ec6d357939889ff154ff595e4410b72ed72586 +size 1464 diff --git a/assets/voxygen/voxel/npc/alpaca/male/torso_back.vox b/assets/voxygen/voxel/npc/alpaca/male/torso_back.vox new file mode 100644 index 0000000000..5a1ba82be9 --- /dev/null +++ b/assets/voxygen/voxel/npc/alpaca/male/torso_back.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:55fc286c9669344f7a54b87dd2afa1926a4922e573bff51a5ab758d56706a21f +size 3464 diff --git a/assets/voxygen/voxel/npc/alpaca/male/torso_front.vox b/assets/voxygen/voxel/npc/alpaca/male/torso_front.vox new file mode 100644 index 0000000000..38c9f15361 --- /dev/null +++ b/assets/voxygen/voxel/npc/alpaca/male/torso_front.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:83fc57bea7a36092d9c2b57d3d01653da6d28f9d0f4f027a0ad69b02034a4f25 +size 2904 diff --git a/assets/voxygen/voxel/npc/cultist_warlock/male/foot_l.vox b/assets/voxygen/voxel/npc/cultist_warlock/male/foot_l.vox new file mode 100644 index 0000000000..117dc3cdc1 --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlock/male/foot_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9be2da809e07b808d54d1c94ac72de7fd6d9bafc439d9794f55fff80294171c3 +size 1544 diff --git a/assets/voxygen/voxel/npc/cultist_warlock/male/foot_r.vox b/assets/voxygen/voxel/npc/cultist_warlock/male/foot_r.vox new file mode 100644 index 0000000000..117dc3cdc1 --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlock/male/foot_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9be2da809e07b808d54d1c94ac72de7fd6d9bafc439d9794f55fff80294171c3 +size 1544 diff --git a/assets/voxygen/voxel/npc/cultist_warlock/male/hand_l.vox b/assets/voxygen/voxel/npc/cultist_warlock/male/hand_l.vox new file mode 100644 index 0000000000..f8179318b4 --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlock/male/hand_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e15d76fe8fe2c7e9180bd4bd48de2b7ab014f0c0a7eb403b140d9d8d3d1e02b +size 2028 diff --git a/assets/voxygen/voxel/npc/cultist_warlock/male/hand_r.vox b/assets/voxygen/voxel/npc/cultist_warlock/male/hand_r.vox new file mode 100644 index 0000000000..51c1180379 --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlock/male/hand_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:77ebf94f05529c2a7e2f1cd1f5fe5c21ce24f06c50c88d90d78ac66b66728df9 +size 2028 diff --git a/assets/voxygen/voxel/npc/cultist_warlock/male/head.vox b/assets/voxygen/voxel/npc/cultist_warlock/male/head.vox new file mode 100644 index 0000000000..01d7de9669 --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlock/male/head.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c2e46f89a7a07825b91f1b6443669ec298bb2155bc1ff6d3f4459f5e82f5aec5 +size 4840 diff --git a/assets/voxygen/voxel/npc/cultist_warlock/male/leg_l.vox b/assets/voxygen/voxel/npc/cultist_warlock/male/leg_l.vox new file mode 100644 index 0000000000..3f78f19314 --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlock/male/leg_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75f01e69263aa2d6e1a3595e5aa9cdd93194b57e13e7d404983cd894ae911b0e +size 1304 diff --git a/assets/voxygen/voxel/npc/cultist_warlock/male/leg_r.vox b/assets/voxygen/voxel/npc/cultist_warlock/male/leg_r.vox new file mode 100644 index 0000000000..3f78f19314 --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlock/male/leg_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75f01e69263aa2d6e1a3595e5aa9cdd93194b57e13e7d404983cd894ae911b0e +size 1304 diff --git a/assets/voxygen/voxel/npc/cultist_warlock/male/shoulder_l.vox b/assets/voxygen/voxel/npc/cultist_warlock/male/shoulder_l.vox new file mode 100644 index 0000000000..c93fa97b9e --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlock/male/shoulder_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8085ef273ef650b35ed115871f6fd97b6b83c67319f2923aa18d805d2ae6f18b +size 1724 diff --git a/assets/voxygen/voxel/npc/cultist_warlock/male/shoulder_r.vox b/assets/voxygen/voxel/npc/cultist_warlock/male/shoulder_r.vox new file mode 100644 index 0000000000..d2a6c72a30 --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlock/male/shoulder_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c88e14776ec6ee85c70845032d05001a20249fe0e878c69ad6ad6c0d2d052cba +size 1724 diff --git a/assets/voxygen/voxel/npc/cultist_warlock/male/torso_lower.vox b/assets/voxygen/voxel/npc/cultist_warlock/male/torso_lower.vox new file mode 100644 index 0000000000..200dcbfca3 --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlock/male/torso_lower.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c8ea34977c2dce4d84e7c7fc687be8a9ac0204fe5fff00186a52bdc1020ab93f +size 2800 diff --git a/assets/voxygen/voxel/npc/cultist_warlock/male/torso_upper.vox b/assets/voxygen/voxel/npc/cultist_warlock/male/torso_upper.vox new file mode 100644 index 0000000000..b3716b50bf --- /dev/null +++ b/assets/voxygen/voxel/npc/cultist_warlock/male/torso_upper.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bc111ef29d545cf5182a19f55178eaccf6f7a8a6455bc01df89f1637293a540f +size 4004 diff --git a/assets/voxygen/voxel/npc/husk_brute/male/foot_l.vox b/assets/voxygen/voxel/npc/husk_brute/male/foot_l.vox new file mode 100644 index 0000000000..73112faae9 --- /dev/null +++ b/assets/voxygen/voxel/npc/husk_brute/male/foot_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3a85347ae6106c2dc616ef80280b81d5c713a314d3b6effb1960ebfad8473ed7 +size 1660 diff --git a/assets/voxygen/voxel/npc/husk_brute/male/foot_r.vox b/assets/voxygen/voxel/npc/husk_brute/male/foot_r.vox new file mode 100644 index 0000000000..f549c46f3f --- /dev/null +++ b/assets/voxygen/voxel/npc/husk_brute/male/foot_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:023987c725b7092a0f8d81eff8c2b9f05f74848f46f6fb84127bc66cdb00aac1 +size 1660 diff --git a/assets/voxygen/voxel/npc/husk_brute/male/hand_l.vox b/assets/voxygen/voxel/npc/husk_brute/male/hand_l.vox new file mode 100644 index 0000000000..92b89b98ff --- /dev/null +++ b/assets/voxygen/voxel/npc/husk_brute/male/hand_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:814635331cd931787e9f00177f013574d60643a4db756a58c25d4c733bbb4877 +size 3236 diff --git a/assets/voxygen/voxel/npc/husk_brute/male/hand_r.vox b/assets/voxygen/voxel/npc/husk_brute/male/hand_r.vox new file mode 100644 index 0000000000..d703d02be1 --- /dev/null +++ b/assets/voxygen/voxel/npc/husk_brute/male/hand_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5dbbc132be878fe286e69a6aee1d63522086d7f7f6ec9aca4ed0198befe56394 +size 3236 diff --git a/assets/voxygen/voxel/npc/husk_brute/male/head.vox b/assets/voxygen/voxel/npc/husk_brute/male/head.vox new file mode 100644 index 0000000000..c967bdbf2a --- /dev/null +++ b/assets/voxygen/voxel/npc/husk_brute/male/head.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a156a2f61b141b285dc10da3ec453c96b10f9845a088c6f256a773153b85c978 +size 4792 diff --git a/assets/voxygen/voxel/npc/husk_brute/male/leg_l.vox b/assets/voxygen/voxel/npc/husk_brute/male/leg_l.vox new file mode 100644 index 0000000000..30b089643c --- /dev/null +++ b/assets/voxygen/voxel/npc/husk_brute/male/leg_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f4e335af3e38b0f2700c9e79e84839d91ed03d2ce9cd70d1f27add95d329a6c +size 1504 diff --git a/assets/voxygen/voxel/npc/husk_brute/male/leg_r.vox b/assets/voxygen/voxel/npc/husk_brute/male/leg_r.vox new file mode 100644 index 0000000000..5fb1be5c86 --- /dev/null +++ b/assets/voxygen/voxel/npc/husk_brute/male/leg_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d84c5521dfe543640114541c9cc7c6fda31c0e416dc077786da631f29866fb0 +size 1504 diff --git a/assets/voxygen/voxel/npc/husk_brute/male/shoulder_l.vox b/assets/voxygen/voxel/npc/husk_brute/male/shoulder_l.vox new file mode 100644 index 0000000000..cca05a1690 --- /dev/null +++ b/assets/voxygen/voxel/npc/husk_brute/male/shoulder_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:659f615c80a701244ab916514ed6dc69fc081de586f51fb0d3260b6a125d94c2 +size 2864 diff --git a/assets/voxygen/voxel/npc/husk_brute/male/shoulder_r.vox b/assets/voxygen/voxel/npc/husk_brute/male/shoulder_r.vox new file mode 100644 index 0000000000..20a04fc8a8 --- /dev/null +++ b/assets/voxygen/voxel/npc/husk_brute/male/shoulder_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:91119f24cd15de41f28f6325b375218c545edd06d0e7208c4478f86062720d83 +size 2864 diff --git a/assets/voxygen/voxel/npc/husk_brute/male/torso_lower.vox b/assets/voxygen/voxel/npc/husk_brute/male/torso_lower.vox new file mode 100644 index 0000000000..ad4b4f146b --- /dev/null +++ b/assets/voxygen/voxel/npc/husk_brute/male/torso_lower.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81c0ed7bbfcef1802c30e7a2e0d4140d42c979824c65420fdf6ccfc32bc70714 +size 3628 diff --git a/assets/voxygen/voxel/npc/husk_brute/male/torso_upper.vox b/assets/voxygen/voxel/npc/husk_brute/male/torso_upper.vox new file mode 100644 index 0000000000..c479d5ccba --- /dev/null +++ b/assets/voxygen/voxel/npc/husk_brute/male/torso_upper.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7b9f45f40abf6ff014283caba855857fd1d0ec2ed11c25228a396aa275e8939f +size 10652 diff --git a/assets/voxygen/voxel/npc/llama/male/ears.vox b/assets/voxygen/voxel/npc/llama/male/ears.vox new file mode 100644 index 0000000000..9d4f67958c --- /dev/null +++ b/assets/voxygen/voxel/npc/llama/male/ears.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4e30eab986f71e9a8480d4f818c1a793db7ccd96411a56f9f5bd5586fd169b0f +size 1200 diff --git a/assets/voxygen/voxel/npc/llama/male/foot_br.vox b/assets/voxygen/voxel/npc/llama/male/foot_br.vox new file mode 100644 index 0000000000..e6c780fac0 --- /dev/null +++ b/assets/voxygen/voxel/npc/llama/male/foot_br.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c2927d862e271a814e073dd8c10565c3fb0467a2089b175138a70e3a16f5356 +size 1356 diff --git a/assets/voxygen/voxel/npc/llama/male/foot_fr.vox b/assets/voxygen/voxel/npc/llama/male/foot_fr.vox new file mode 100644 index 0000000000..44bbd8341d --- /dev/null +++ b/assets/voxygen/voxel/npc/llama/male/foot_fr.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0671223cb64a2b59d006d726ca69b7053a96fd37971a29829be97dd96e5923b4 +size 1368 diff --git a/assets/voxygen/voxel/npc/llama/male/head.vox b/assets/voxygen/voxel/npc/llama/male/head.vox new file mode 100644 index 0000000000..19c4326a7a --- /dev/null +++ b/assets/voxygen/voxel/npc/llama/male/head.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:788e36255e3137423b1993616f224b71e2f285efae8a036bd2f81be9df8beea6 +size 2360 diff --git a/assets/voxygen/voxel/npc/llama/male/jaw.vox b/assets/voxygen/voxel/npc/llama/male/jaw.vox new file mode 100644 index 0000000000..1c2872c516 --- /dev/null +++ b/assets/voxygen/voxel/npc/llama/male/jaw.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1882295dc5e2fdc166c91325ec49cd05ab168605587d15cee89e8f0e50ed7f6f +size 1208 diff --git a/assets/voxygen/voxel/npc/llama/male/leg_br.vox b/assets/voxygen/voxel/npc/llama/male/leg_br.vox new file mode 100644 index 0000000000..567bab2e4b --- /dev/null +++ b/assets/voxygen/voxel/npc/llama/male/leg_br.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b51f1ce14a4daacacfd3bdfa85c17f19cbd70b158265ac5b1cc5ccde95b53b41 +size 1684 diff --git a/assets/voxygen/voxel/npc/llama/male/leg_fr.vox b/assets/voxygen/voxel/npc/llama/male/leg_fr.vox new file mode 100644 index 0000000000..bfb864c7ca --- /dev/null +++ b/assets/voxygen/voxel/npc/llama/male/leg_fr.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:50d293d04e5271d8c1992a8fc0e59ec7376a2d4908e23d3e52dc986df4c29b1c +size 1604 diff --git a/assets/voxygen/voxel/npc/llama/male/neck.vox b/assets/voxygen/voxel/npc/llama/male/neck.vox new file mode 100644 index 0000000000..396f54b223 --- /dev/null +++ b/assets/voxygen/voxel/npc/llama/male/neck.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6784e3f6a9f5574baaabadd06961624ce5cf9c90d7ce44e83b44b866326a18a +size 3744 diff --git a/assets/voxygen/voxel/npc/llama/male/tail.vox b/assets/voxygen/voxel/npc/llama/male/tail.vox new file mode 100644 index 0000000000..53d61a3a56 --- /dev/null +++ b/assets/voxygen/voxel/npc/llama/male/tail.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:73000c9ce82db82c6d784a1cb2963ab49c9a32730eb7f0e5d4dc7bf41448154a +size 1344 diff --git a/assets/voxygen/voxel/npc/llama/male/torso_back.vox b/assets/voxygen/voxel/npc/llama/male/torso_back.vox new file mode 100644 index 0000000000..61d600f9a0 --- /dev/null +++ b/assets/voxygen/voxel/npc/llama/male/torso_back.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aed403d3a29983553fad51efc06b372f623101948871a351d1de2613b386d430 +size 4208 diff --git a/assets/voxygen/voxel/npc/llama/male/torso_front.vox b/assets/voxygen/voxel/npc/llama/male/torso_front.vox new file mode 100644 index 0000000000..944fbc586b --- /dev/null +++ b/assets/voxygen/voxel/npc/llama/male/torso_front.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1512a046c72120af9c2a048a356dbf6d86e23c0ed3770393a9f0a1d9b30b8edb +size 4424 diff --git a/assets/voxygen/voxel/quadruped_medium_central_manifest.ron b/assets/voxygen/voxel/quadruped_medium_central_manifest.ron index a9a7ff6c04..a1f9869837 100644 --- a/assets/voxygen/voxel/quadruped_medium_central_manifest.ron +++ b/assets/voxygen/voxel/quadruped_medium_central_manifest.ron @@ -1919,4 +1919,124 @@ central: ("npc.ngoubou.male.tail"), ), ), + (Llama, Male): ( + head: ( + offset: (-3.0, -2.0, -3.5), + central: ("npc.llama.male.head"), + ), + neck: ( + offset: (-3.0, -4.5, -9.5), + central: ("npc.llama.male.neck"), + ), + jaw: ( + offset: (-2.0, 0.0, -2.0), + central: ("npc.llama.male.jaw"), + ), + torso_front: ( + offset: (-6.0, -9.0, -6.0), + central: ("npc.llama.male.torso_front"), + ), + torso_back: ( + offset: (-5.0, -12.0, -5.0), + central: ("npc.llama.male.torso_back"), + ), + ears: ( + offset: (-4.0, -1.0, 0.0), + central: ("npc.llama.male.ears"), + ), + tail: ( + offset: (-2.0, -5.0, -7.0), + central: ("npc.llama.male.tail"), + ), + ), + (Llama, Female): ( + head: ( + offset: (-3.0, -2.0, -3.5), + central: ("npc.llama.male.head"), + ), + neck: ( + offset: (-3.0, -4.5, -9.5), + central: ("npc.llama.male.neck"), + ), + jaw: ( + offset: (-2.0, 0.0, -2.0), + central: ("npc.llama.male.jaw"), + ), + torso_front: ( + offset: (-6.0, -9.0, -6.0), + central: ("npc.llama.male.torso_front"), + ), + torso_back: ( + offset: (-5.0, -12.0, -5.0), + central: ("npc.llama.male.torso_back"), + ), + ears: ( + offset: (-4.0, -1.0, 0.0), + central: ("npc.llama.male.ears"), + ), + tail: ( + offset: (-2.0, -5.0, -7.0), + central: ("npc.llama.male.tail"), + ), + ), + (Alpaca, Male): ( + head: ( + offset: (-4.0, -3.0, -4.0), + central: ("npc.alpaca.male.head"), + ), + neck: ( + offset: (-3.0, -3.5, -6.5), + central: ("npc.alpaca.male.neck"), + ), + jaw: ( + offset: (-1.0, 0.0, -0.5), + central: ("npc.alpaca.male.jaw"), + ), + torso_front: ( + offset: (-5.0, -8.0, -4.5), + central: ("npc.alpaca.male.torso_front"), + ), + torso_back: ( + offset: (-5.0, -10.0, -4.5), + central: ("npc.alpaca.male.torso_back"), + ), + ears: ( + offset: (-4.0, -1.0, 0.0), + central: ("npc.alpaca.male.ears"), + ), + tail: ( + offset: (-3.0, -5.0, -8.0), + central: ("npc.alpaca.male.tail"), + ), + ), + (Alpaca, Female): ( + head: ( + offset: (-4.0, -3.0, -4.0), + central: ("npc.alpaca.male.head"), + ), + neck: ( + offset: (-3.0, -3.5, -6.5), + central: ("npc.alpaca.male.neck"), + ), + jaw: ( + offset: (-1.0, 0.0, -0.5), + central: ("npc.alpaca.male.jaw"), + ), + torso_front: ( + offset: (-5.0, -8.0, -4.5), + central: ("npc.alpaca.male.torso_front"), + ), + torso_back: ( + offset: (-5.0, -10.0, -4.5), + central: ("npc.alpaca.male.torso_back"), + ), + ears: ( + offset: (-4.0, -1.0, 0.0), + central: ("npc.alpaca.male.ears"), + ), + tail: ( + offset: (-3.0, -5.0, -8.0), + central: ("npc.alpaca.male.tail"), + ), + ), }) diff --git a/assets/voxygen/voxel/quadruped_medium_lateral_manifest.ron b/assets/voxygen/voxel/quadruped_medium_lateral_manifest.ron index c548271783..8e0ff588b2 100644 --- a/assets/voxygen/voxel/quadruped_medium_lateral_manifest.ron +++ b/assets/voxygen/voxel/quadruped_medium_lateral_manifest.ron @@ -2175,4 +2175,140 @@ lateral: ("npc.ngoubou.male.foot_br"), ), ), + (Llama, Male): ( + leg_fl: ( + offset: (-2.0, -2.5, -6.0), + lateral: ("npc.llama.male.leg_fr"), + ), + leg_fr: ( + offset: (-2.0, -2.5, -6.0), + lateral: ("npc.llama.male.leg_fr"), + ), + leg_bl: ( + offset: (-2.0, -3.0, -5.0), + lateral: ("npc.llama.male.leg_br"), + ), + leg_br: ( + offset: (-2.0, -3.0, -5.0), + lateral: ("npc.llama.male.leg_br"), + ), + foot_fl: ( + offset: (-1.5, -2.0, -8.0), + lateral: ("npc.llama.male.foot_fr"), + ), + foot_fr: ( + offset: (-1.5, -2.0, -8.0), + lateral: ("npc.llama.male.foot_fr"), + ), + foot_bl: ( + offset: (-1.5, -2.5, -9.0), + lateral: ("npc.llama.male.foot_br"), + ), + foot_br: ( + offset: (-1.5, -2.5, -9.0), + lateral: ("npc.llama.male.foot_br"), + ), + ), + (Llama, Female): ( + leg_fl: ( + offset: (-2.0, -2.5, -6.0), + lateral: ("npc.llama.male.leg_fr"), + ), + leg_fr: ( + offset: (-2.0, -2.5, -6.0), + lateral: ("npc.llama.male.leg_fr"), + ), + leg_bl: ( + offset: (-2.0, -3.0, -5.0), + lateral: ("npc.llama.male.leg_br"), + ), + leg_br: ( + offset: (-2.0, -3.0, -5.0), + lateral: ("npc.llama.male.leg_br"), + ), + foot_fl: ( + offset: (-1.5, -2.0, -8.0), + lateral: ("npc.llama.male.foot_fr"), + ), + foot_fr: ( + offset: (-1.5, -2.0, -8.0), + lateral: ("npc.llama.male.foot_fr"), + ), + foot_bl: ( + offset: (-1.5, -2.5, -9.0), + lateral: ("npc.llama.male.foot_br"), + ), + foot_br: ( + offset: (-1.5, -2.5, -9.0), + lateral: ("npc.llama.male.foot_br"), + ), + ), + (Alpaca, Male): ( + leg_fl: ( + offset: (-1.5, -2.5, -4.0), + lateral: ("npc.alpaca.male.leg_fr"), + ), + leg_fr: ( + offset: (-1.5, -2.5, -4.0), + lateral: ("npc.alpaca.male.leg_fr"), + ), + leg_bl: ( + offset: (-2.5, -2.5, -4.5), + lateral: ("npc.alpaca.male.leg_br"), + ), + leg_br: ( + offset: (-2.5, -2.5, -4.5), + lateral: ("npc.alpaca.male.leg_br"), + ), + foot_fl: ( + offset: (-1.5, -2.0, -6.0), + lateral: ("npc.alpaca.male.foot_fr"), + ), + foot_fr: ( + offset: (-1.5, -2.0, -6.0), + lateral: ("npc.alpaca.male.foot_fr"), + ), + foot_bl: ( + offset: (-2.0, -2.0, -6.0), + lateral: ("npc.alpaca.male.foot_br"), + ), + foot_br: ( + offset: (-2.0, -2.0, -6.0), + lateral: ("npc.alpaca.male.foot_br"), + ), + ), + (Alpaca, Female): ( + leg_fl: ( + offset: (-1.5, -2.5, -4.0), + lateral: ("npc.alpaca.male.leg_fr"), + ), + leg_fr: ( + offset: (-1.5, -2.5, -4.0), + lateral: ("npc.alpaca.male.leg_fr"), + ), + leg_bl: ( + offset: (-2.5, -2.5, -4.5), + lateral: ("npc.alpaca.male.leg_br"), + ), + leg_br: ( + offset: (-2.5, -2.5, -4.5), + lateral: ("npc.alpaca.male.leg_br"), + ), + foot_fl: ( + offset: (-1.5, -2.0, -6.0), + lateral: ("npc.alpaca.male.foot_fr"), + ), + foot_fr: ( + offset: (-1.5, -2.0, -6.0), + lateral: ("npc.alpaca.male.foot_fr"), + ), + foot_bl: ( + offset: (-2.0, -2.0, -6.0), + lateral: ("npc.alpaca.male.foot_br"), + ), + foot_br: ( + offset: (-2.0, -2.0, -6.0), + lateral: ("npc.alpaca.male.foot_br"), + ), + ), }) diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 96ffd4263f..661f9639a3 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -307,6 +307,7 @@ impl Body { biped_large::Species::Werewolf => Vec3::new(4.0, 3.0, 3.5), biped_large::Species::Harvester => Vec3::new(4.6, 3.0, 5.4), biped_large::Species::Cultistwarlord => Vec3::new(3.0, 3.0, 4.5), + biped_large::Species::Cultistwarlock => Vec3::new(3.0, 3.0, 4.5), _ => Vec3::new(4.6, 3.0, 6.0), }, Body::BipedSmall(body) => match body.species { diff --git a/common/src/comp/body/biped_large.rs b/common/src/comp/body/biped_large.rs index 7a816f512e..44285fa21e 100644 --- a/common/src/comp/body/biped_large.rs +++ b/common/src/comp/body/biped_large.rs @@ -53,6 +53,8 @@ make_case_elim!( Blueoni = 16, Redoni = 17, Cultistwarlord = 18, + Cultistwarlock = 19, + Huskbrute = 20, } ); @@ -80,6 +82,8 @@ pub struct AllSpecies { pub oni_blue: SpeciesMeta, pub oni_red: SpeciesMeta, pub cultist_warlord: SpeciesMeta, + pub cultist_warlock: SpeciesMeta, + pub husk_brute: SpeciesMeta, } impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies { @@ -107,11 +111,13 @@ impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies Species::Blueoni => &self.oni_blue, Species::Redoni => &self.oni_red, Species::Cultistwarlord => &self.cultist_warlord, + Species::Cultistwarlock => &self.cultist_warlock, + Species::Huskbrute => &self.husk_brute, } } } -pub const ALL_SPECIES: [Species; 19] = [ +pub const ALL_SPECIES: [Species; 21] = [ Species::Ogre, Species::Cyclops, Species::Wendigo, @@ -131,6 +137,8 @@ pub const ALL_SPECIES: [Species; 19] = [ Species::Blueoni, Species::Redoni, Species::Cultistwarlord, + Species::Cultistwarlock, + Species::Huskbrute, ]; impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies { diff --git a/common/src/comp/body/quadruped_medium.rs b/common/src/comp/body/quadruped_medium.rs index f814b57a05..04efc68c25 100644 --- a/common/src/comp/body/quadruped_medium.rs +++ b/common/src/comp/body/quadruped_medium.rs @@ -64,6 +64,8 @@ pub enum Species { Snowleopard = 31, Mammoth = 32, Ngoubou = 33, + Llama = 34, + Alpaca = 35, } /// Data representing per-species generic data. @@ -103,6 +105,8 @@ pub struct AllSpecies { pub snowleopard: SpeciesMeta, pub mammoth: SpeciesMeta, pub ngoubou: SpeciesMeta, + pub llama: SpeciesMeta, + pub alpaca: SpeciesMeta, } impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies { @@ -143,11 +147,13 @@ impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies Species::Snowleopard => &self.snowleopard, Species::Mammoth => &self.mammoth, Species::Ngoubou => &self.ngoubou, + Species::Llama => &self.llama, + Species::Alpaca => &self.alpaca, } } } -pub const ALL_SPECIES: [Species; 32] = [ +pub const ALL_SPECIES: [Species; 34] = [ Species::Grolgar, Species::Saber, Species::Tiger, @@ -180,6 +186,8 @@ pub const ALL_SPECIES: [Species; 32] = [ Species::Snowleopard, Species::Mammoth, Species::Ngoubou, + Species::Llama, + Species::Alpaca, ]; impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies { diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 759b4ece11..d9fea9965b 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -267,9 +267,9 @@ fn default_main_tool(body: &Body) -> Item { (biped_large::Species::Wendigo, _) => Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.wendigo_magic", )), - (biped_large::Species::Werewolf, _) => Some(Item::new_from_asset_expect( - "common.items.npc_weapons.unique.beast_claws", - )), + (biped_large::Species::Werewolf | biped_large::Species::Huskbrute, _) => Some( + Item::new_from_asset_expect("common.items.npc_weapons.unique.beast_claws"), + ), (biped_large::Species::Cyclops, _) => Some(Item::new_from_asset_expect( "common.items.npc_weapons.hammer.cyclops_hammer", )), @@ -300,6 +300,9 @@ fn default_main_tool(body: &Body) -> Item { (biped_large::Species::Cultistwarlord, _) => Some(Item::new_from_asset_expect( "common.items.weapons.sword.bipedlarge-cultist", )), + (biped_large::Species::Cultistwarlock, _) => Some(Item::new_from_asset_expect( + "common.items.weapons.staff.bipedlarge-cultist_staff", + )), }, Body::Object(body) => match body { object::Body::Crossbow => Some(Item::new_from_asset_expect( diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index d105035c43..10968783d6 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -70,6 +70,8 @@ impl Body { quadruped_medium::Species::Snowleopard => 160.0, quadruped_medium::Species::Mammoth => 180.0, quadruped_medium::Species::Ngoubou => 170.0, + quadruped_medium::Species::Llama => 130.0, + quadruped_medium::Species::Alpaca => 120.0, }, Body::BipedLarge(body) => match body.species { biped_large::Species::Slysaurok => 100.0, diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 4f640bd1bc..983dc3e1f6 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -418,7 +418,9 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc | quadruped_medium::Species::Antelope | quadruped_medium::Species::Kelpie | quadruped_medium::Species::Cattle - | quadruped_medium::Species::Yak => { + | quadruped_medium::Species::Yak + | quadruped_medium::Species::Llama + | quadruped_medium::Species::Alpaca => { "common.loot_tables.creature.quad_medium.gentle" }, quadruped_medium::Species::Ngoubou => { diff --git a/voxygen/anim/src/biped_large/mod.rs b/voxygen/anim/src/biped_large/mod.rs index 567df6f2b7..9754e4370c 100644 --- a/voxygen/anim/src/biped_large/mod.rs +++ b/voxygen/anim/src/biped_large/mod.rs @@ -230,6 +230,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Blueoni, _) => (10.5, -3.0), (Redoni, _) => (10.5, -3.0), (Cultistwarlord, _) => (0.5, 14.5), + (Cultistwarlock, _) => (0.5, 14.5), + (Huskbrute, _) => (8.5, 4.0), }, jaw: match (body.species, body.body_type) { (Ogre, _) => (0.0, 0.0), @@ -251,6 +253,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Blueoni, _) => (0.0, 3.5), (Redoni, _) => (0.0, 3.5), (Cultistwarlord, _) => (0.0, 3.5), + (Cultistwarlock, _) => (0.0, 3.5), + (Huskbrute, _) => (-5.0, -5.0), }, upper_torso: match (body.species, body.body_type) { (Ogre, Male) => (0.0, 27.5), @@ -273,6 +277,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Blueoni, _) => (-1.0, 26.5), (Redoni, _) => (-1.0, 26.5), (Cultistwarlord, _) => (-1.0, 18.5), + (Cultistwarlock, _) => (-1.0, 18.5), + (Huskbrute, _) => (-1.0, 23.5), }, lower_torso: match (body.species, body.body_type) { (Ogre, Male) => (1.0, -7.0), @@ -295,6 +301,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Blueoni, _) => (0.0, -8.5), (Redoni, _) => (0.0, -8.5), (Cultistwarlord, _) => (0.0, -1.5), + (Cultistwarlock, _) => (0.0, -1.5), + (Huskbrute, _) => (0.0, -6.5), }, tail: match (body.species, body.body_type) { (Werewolf, _) => (-5.5, -2.0), @@ -325,6 +333,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Blueoni, _) => (11.0, 2.0, -5.5), (Redoni, _) => (11.0, 2.0, -5.5), (Cultistwarlord, _) => (11.5, -1.0, 4.5), + (Cultistwarlock, _) => (11.5, -1.0, 4.5), + (Huskbrute, _) => (10.5, 1.0, -2.5), }, hand: match (body.species, body.body_type) { (Ogre, Male) => (14.5, 0.0, -4.0), @@ -347,6 +357,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Blueoni, _) => (13.5, 0.5, -8.0), (Redoni, _) => (13.5, 0.5, -8.0), (Cultistwarlord, _) => (11.5, -1.0, -1.0), + (Cultistwarlock, _) => (11.5, -1.0, -1.0), + (Huskbrute, _) => (12.0, 1.5, -6.0), }, leg: match (body.species, body.body_type) { (Ogre, Male) => (0.0, 0.0, -4.0), @@ -369,6 +381,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Blueoni, _) => (4.5, 2.0, -5.5), (Redoni, _) => (4.5, 2.0, -5.5), (Cultistwarlord, _) => (3.5, -1.0, -8.5), + (Cultistwarlock, _) => (3.5, -1.0, -8.5), + (Huskbrute, _) => (4.0, 0.0, -5.5), }, foot: match (body.species, body.body_type) { (Ogre, Male) => (4.0, 1.0, -12.0), @@ -391,6 +405,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Blueoni, _) => (5.0, 5.0, -12.5), (Redoni, _) => (5.0, 5.0, -12.5), (Cultistwarlord, _) => (3.5, 0.0, -12.5), + (Cultistwarlock, _) => (3.5, 0.0, -12.5), + (Huskbrute, _) => (4.5, 0.5, -12.5), }, scaler: match (body.species, body.body_type) { (Ogre, Male) => 1.12, @@ -413,6 +429,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Blueoni, _) => 1.2, (Redoni, _) => 1.2, (Cultistwarlord, _) => 1.0, + (Cultistwarlock, _) => 1.0, + (Huskbrute, _) => 1.2, }, tempo: match (body.species, body.body_type) { (Ogre, Male) => 0.9, @@ -446,6 +464,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Blueoni, _) => (12.5, 0.0), (Redoni, _) => (12.5, 0.0), (Cultistwarlord, _) => (8.0, 0.0), + (Cultistwarlock, _) => (8.0, 0.0), + (Huskbrute, _) => (12.5, 0.0), }, shl: match (body.species, body.body_type) { (Dullahan, _) => (-4.75, -11.0, 8.5, 1.47, -0.2, 0.0), diff --git a/voxygen/anim/src/quadruped_medium/mod.rs b/voxygen/anim/src/quadruped_medium/mod.rs index 5c780cc851..c4f74eeb05 100644 --- a/voxygen/anim/src/quadruped_medium/mod.rs +++ b/voxygen/anim/src/quadruped_medium/mod.rs @@ -177,6 +177,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Snowleopard, _) => (1.5, 0.5), (Mammoth, _) => (0.5, -1.5), (Ngoubou, _) => (0.5, -2.5), + (Llama, _) => (0.5, 10.0), + (Alpaca, _) => (0.5, 7.5), }, neck: match (body.species, body.body_type) { (Grolgar, _) => (1.0, -1.0), @@ -212,6 +214,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Snowleopard, _) => (0.0, 1.5), (Mammoth, _) => (0.5, -0.5), (Ngoubou, _) => (2.0, 1.0), + (Llama, _) => (2.5, 4.5), + (Alpaca, _) => (-1.5, 3.0), }, jaw: match (body.species, body.body_type) { (Grolgar, _) => (7.0, 2.0), @@ -248,6 +252,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Snowleopard, _) => (3.0, -3.0), (Mammoth, _) => (4.5, -3.0), (Ngoubou, _) => (4.5, -4.0), + (Llama, _) => (4.0, -1.0), + (Alpaca, _) => (3.0, -2.5), }, tail: match (body.species, body.body_type) { (Grolgar, _) => (-11.5, -0.5), @@ -283,6 +289,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Snowleopard, _) => (-10.5, 3.0), (Mammoth, _) => (-13.0, -1.5), (Ngoubou, _) => (-12.0, 5.5), + (Llama, _) => (-9.0, 6.0), + (Alpaca, _) => (-8.5, 3.5), }, torso_front: match (body.species, body.body_type) { (Grolgar, _) => (10.0, 13.0), @@ -318,6 +326,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Snowleopard, _) => (1.5, 13.0), (Mammoth, _) => (11.5, 20.5), (Ngoubou, _) => (9.5, 16.5), + (Llama, _) => (7.0, 15.0), + (Alpaca, _) => (7.0, 11.5), }, torso_back: match (body.species, body.body_type) { (Grolgar, _) => (-10.0, 1.5), @@ -353,6 +363,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Snowleopard, _) => (-11.0, 0.0), (Mammoth, _) => (-13.0, -2.5), (Ngoubou, _) => (-8.0, -2.0), + (Llama, _) => (-8.0, 0.0), + (Alpaca, _) => (-6.0, 0.0), }, ears: match (body.species, body.body_type) { (Grolgar, _) => (5.0, 8.0), @@ -389,6 +401,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Snowleopard, _) => (1.5, 3.0), (Mammoth, _) => (12.0, -3.0), (Ngoubou, _) => (12.0, -3.0), + (Llama, _) => (1.0, 3.5), + (Alpaca, _) => (1.0, 2.0), }, leg_f: match (body.species, body.body_type) { (Grolgar, _) => (7.5, -5.5, -1.0), @@ -424,6 +438,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Snowleopard, _) => (6.5, -4.0, -2.5), (Mammoth, _) => (10.0, -5.0, -5.0), (Ngoubou, _) => (7.5, -4.0, -1.5), + (Llama, _) => (5.0, -1.5, -1.0), + (Alpaca, _) => (3.5, -2.5, -0.5), }, leg_b: match (body.species, body.body_type) { (Grolgar, _) => (6.0, -6.5, -4.0), @@ -459,6 +475,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Snowleopard, _) => (5.5, -5.0, -1.5), (Mammoth, _) => (7.5, -7.0, -5.0), (Ngoubou, _) => (4.5, -9.5, 0.0), + (Llama, _) => (5.0, -7.0, -2.0), + (Alpaca, _) => (3.5, -7.0, 0.0), }, feet_f: match (body.species, body.body_type) { (Grolgar, _) => (0.0, 0.0, -4.0), @@ -494,6 +512,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Snowleopard, _) => (0.5, 0.5, -4.5), (Mammoth, _) => (-0.5, -0.5, -6.0), (Ngoubou, _) => (-1.0, 0.5, -6.0), + (Llama, _) => (-0.5, 0.5, -6.0), + (Alpaca, _) => (0.0, -0.5, -5.0), }, feet_b: match (body.species, body.body_type) { (Grolgar, _) => (0.5, -1.5, -3.0), @@ -529,6 +549,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Snowleopard, _) => (0.5, 0.5, -5.5), (Mammoth, _) => (0.5, -0.5, -4.5), (Ngoubou, _) => (0.5, 1.0, -5.5), + (Llama, _) => (0.5, -1.5, -3.5), + (Alpaca, _) => (-0.5, -0.5, -5.5), }, scaler: match (body.species, body.body_type) { (Grolgar, _) => (1.05), From 8760b6934536c281804561ca4987acfbb0194ad7 Mon Sep 17 00:00:00 2001 From: Snowram Date: Wed, 23 Jun 2021 01:43:18 +0200 Subject: [PATCH 169/180] Husk brute animations --- .../common/abilities/ability_set_manifest.ron | 5 ++ .../custom/husk_brute/chargedmelee.ron | 18 ++++++ .../custom/husk_brute/singlestrike.ron | 28 +++++++++ .../entity/dungeon/tier-5/husk_brute.ron | 12 ++++ .../items/npc_weapons/unique/husk_brute.ron | 18 ++++++ .../loot_tables/creature/quad_medium/wool.ron | 6 ++ .../quad_small/{sheep.ron => wool.ron} | 0 .../voxygen/voxel/biped_weapon_manifest.ron | 4 ++ .../voxel/npc/husk_brute/male/foot_l.vox | 2 +- .../voxel/npc/husk_brute/male/foot_r.vox | 2 +- .../voxel/npc/husk_brute/male/leg_r.vox | 2 +- .../voxel/npc/llama/male/torso_back.vox | 4 +- common/src/comp/agent.rs | 2 + common/src/comp/body.rs | 8 ++- common/src/comp/inventory/loadout_builder.rs | 13 ++-- common/src/states/utils.rs | 4 +- server/src/events/entity_manipulation.rs | 12 ++-- voxygen/anim/src/biped_large/alpha.rs | 42 +++++++++++++ voxygen/anim/src/biped_large/chargemelee.rs | 60 +++++++++++++++++++ voxygen/anim/src/biped_large/mod.rs | 18 +++--- voxygen/anim/src/biped_large/wield.rs | 57 ++++++++++++++++++ world/src/layer/wildlife.rs | 14 ++++- world/src/site/dungeon/mod.rs | 8 ++- 23 files changed, 310 insertions(+), 29 deletions(-) create mode 100644 assets/common/abilities/custom/husk_brute/chargedmelee.ron create mode 100644 assets/common/abilities/custom/husk_brute/singlestrike.ron create mode 100644 assets/common/entity/dungeon/tier-5/husk_brute.ron create mode 100644 assets/common/items/npc_weapons/unique/husk_brute.ron create mode 100644 assets/common/loot_tables/creature/quad_medium/wool.ron rename assets/common/loot_tables/creature/quad_small/{sheep.ron => wool.ron} (100%) diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index 4a089589b7..41b0c565d9 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -34,6 +34,11 @@ secondary: "common.abilities.custom.husk.triplestrike", abilities: [], ), + Custom("Husk Brute"): ( + primary: "common.abilities.custom.husk_brute.singlestrike", + secondary: "common.abilities.custom.husk_brute.chargedmelee", + abilities: [], + ), Tool(Spear): ( primary: "common.abilities.spear.doublestrike", secondary: "common.abilities.spear.dash", diff --git a/assets/common/abilities/custom/husk_brute/chargedmelee.ron b/assets/common/abilities/custom/husk_brute/chargedmelee.ron new file mode 100644 index 0000000000..e4006d5949 --- /dev/null +++ b/assets/common/abilities/custom/husk_brute/chargedmelee.ron @@ -0,0 +1,18 @@ +ChargedMelee( + energy_cost: 0, + energy_drain: 0, + initial_damage: 0, + scaled_damage: 500, + initial_poise_damage: 50, + scaled_poise_damage: 150, + initial_knockback: 0.0, + scaled_knockback: 0.0, + range: 5.0, + max_angle: 45.0, + speed: 1.0, + charge_duration: 1.5, + swing_duration: 0.1, + hit_timing: 0.8, + recover_duration: 0.5, + damage_kind: Crushing, +) diff --git a/assets/common/abilities/custom/husk_brute/singlestrike.ron b/assets/common/abilities/custom/husk_brute/singlestrike.ron new file mode 100644 index 0000000000..d64184bfa8 --- /dev/null +++ b/assets/common/abilities/custom/husk_brute/singlestrike.ron @@ -0,0 +1,28 @@ +ComboMelee( + stage_data: [ + ( + stage: 1, + base_damage: 160, + damage_increase: 0, + base_poise_damage: 12, + poise_damage_increase: 0, + knockback: 5.0, + range: 2.5, + angle: 60.0, + base_buildup_duration: 0.25, + base_swing_duration: 0.07, + hit_timing: 0.5, + base_recover_duration: 0.25, + forward_movement: 0.5, + damage_kind: Crushing, + ), + ], + initial_energy_gain: 0, + max_energy_gain: 0, + energy_increase: 0, + speed_increase: 0.0, + max_speed_increase: 0.0, + scales_from_combo: 0, + is_interruptible: false, + ori_modifier: 0.6, +) diff --git a/assets/common/entity/dungeon/tier-5/husk_brute.ron b/assets/common/entity/dungeon/tier-5/husk_brute.ron new file mode 100644 index 0000000000..fc2592fb47 --- /dev/null +++ b/assets/common/entity/dungeon/tier-5/husk_brute.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Husk Brute"), + body: Some(RandomWith("husk_brute")), + + loot: Some(LootTable("common.loot_tables.dungeon.tier-5.minion")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/items/npc_weapons/unique/husk_brute.ron b/assets/common/items/npc_weapons/unique/husk_brute.ron new file mode 100644 index 0000000000..c2389e824c --- /dev/null +++ b/assets/common/items/npc_weapons/unique/husk_brute.ron @@ -0,0 +1,18 @@ +ItemDef( + name: "Husk Brute", + description: "testing123", + kind: Tool(( + kind: Natural, + hands: Two, + stats: Direct(( + equip_time_secs: 0.0, + power: 1.0, + poise_strength: 1.0, + speed: 1.0, + crit_chance: 0.0625, + )), + )), + quality: Low, + tags: [], + ability_spec: Some(Custom("Husk Brute")), +) \ No newline at end of file diff --git a/assets/common/loot_tables/creature/quad_medium/wool.ron b/assets/common/loot_tables/creature/quad_medium/wool.ron new file mode 100644 index 0000000000..3b3fa3f62d --- /dev/null +++ b/assets/common/loot_tables/creature/quad_medium/wool.ron @@ -0,0 +1,6 @@ +[ + (1.5, Item("common.items.food.meat.beast_small_raw")), + (0.5, Item("common.items.food.meat.beast_large_raw")), + (1.0, Item("common.items.crafting_ing.hide.animal_hide")), + (5.0, ItemQuantity("common.items.crafting_ing.cloth.wool", 2, 5)), +] \ No newline at end of file diff --git a/assets/common/loot_tables/creature/quad_small/sheep.ron b/assets/common/loot_tables/creature/quad_small/wool.ron similarity index 100% rename from assets/common/loot_tables/creature/quad_small/sheep.ron rename to assets/common/loot_tables/creature/quad_small/wool.ron diff --git a/assets/voxygen/voxel/biped_weapon_manifest.ron b/assets/voxygen/voxel/biped_weapon_manifest.ron index f2210787e1..93ea6dc59f 100644 --- a/assets/voxygen/voxel/biped_weapon_manifest.ron +++ b/assets/voxygen/voxel/biped_weapon_manifest.ron @@ -1050,6 +1050,10 @@ vox_spec: ("armor.empty", (0.0, 0.0, 0.0)), color: None ), + "common.items.npc_weapons.unique.husk_brute": ( + vox_spec: ("armor.empty", (0.0, 0.0, 0.0)), + color: None + ), "common.items.npc_weapons.axe.minotaur_axe": ( vox_spec: ("weapon.axe.2haxe_minotaur", (-2.5, -9.0, -8.0)), color: None diff --git a/assets/voxygen/voxel/npc/husk_brute/male/foot_l.vox b/assets/voxygen/voxel/npc/husk_brute/male/foot_l.vox index 73112faae9..781e93ba16 100644 --- a/assets/voxygen/voxel/npc/husk_brute/male/foot_l.vox +++ b/assets/voxygen/voxel/npc/husk_brute/male/foot_l.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a85347ae6106c2dc616ef80280b81d5c713a314d3b6effb1960ebfad8473ed7 +oid sha256:def1166360040579420940df95e73f33e21425d0f3bb493be53f7a98bb98f32e size 1660 diff --git a/assets/voxygen/voxel/npc/husk_brute/male/foot_r.vox b/assets/voxygen/voxel/npc/husk_brute/male/foot_r.vox index f549c46f3f..73112faae9 100644 --- a/assets/voxygen/voxel/npc/husk_brute/male/foot_r.vox +++ b/assets/voxygen/voxel/npc/husk_brute/male/foot_r.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:023987c725b7092a0f8d81eff8c2b9f05f74848f46f6fb84127bc66cdb00aac1 +oid sha256:3a85347ae6106c2dc616ef80280b81d5c713a314d3b6effb1960ebfad8473ed7 size 1660 diff --git a/assets/voxygen/voxel/npc/husk_brute/male/leg_r.vox b/assets/voxygen/voxel/npc/husk_brute/male/leg_r.vox index 5fb1be5c86..30b089643c 100644 --- a/assets/voxygen/voxel/npc/husk_brute/male/leg_r.vox +++ b/assets/voxygen/voxel/npc/husk_brute/male/leg_r.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d84c5521dfe543640114541c9cc7c6fda31c0e416dc077786da631f29866fb0 +oid sha256:7f4e335af3e38b0f2700c9e79e84839d91ed03d2ce9cd70d1f27add95d329a6c size 1504 diff --git a/assets/voxygen/voxel/npc/llama/male/torso_back.vox b/assets/voxygen/voxel/npc/llama/male/torso_back.vox index 61d600f9a0..39ae1405c7 100644 --- a/assets/voxygen/voxel/npc/llama/male/torso_back.vox +++ b/assets/voxygen/voxel/npc/llama/male/torso_back.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aed403d3a29983553fad51efc06b372f623101948871a351d1de2613b386d430 -size 4208 +oid sha256:cbd269e4736b47909d50871dfcb9994c16da4539b78f3d1cb35478e2f323a0a7 +size 4232 diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index 6b8059cedc..f7262502a7 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -218,6 +218,8 @@ impl<'a> From<&'a Body> for Psyche { quadruped_medium::Species::Darkhound => 0.9, quadruped_medium::Species::Dreadhorn => 0.8, quadruped_medium::Species::Snowleopard => 0.7, + quadruped_medium::Species::Llama => 0.6, + quadruped_medium::Species::Alpaca => 0.6, _ => 0.5, }, Body::QuadrupedLow(quadruped_low) => match quadruped_low.species { diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 661f9639a3..6755a2bb18 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -180,6 +180,9 @@ impl Body { biped_large::Species::Mightysaurok => 400.0, biped_large::Species::Mindflayer => 420.0, biped_large::Species::Minotaur => 500.0, + biped_large::Species::Cavetroll => 600.0, + biped_large::Species::Mountaintroll => 600.0, + biped_large::Species::Swamptroll => 600.0, _ => 400.0, }, Body::BipedSmall(_) => 50.0, @@ -308,6 +311,7 @@ impl Body { biped_large::Species::Harvester => Vec3::new(4.6, 3.0, 5.4), biped_large::Species::Cultistwarlord => Vec3::new(3.0, 3.0, 4.5), biped_large::Species::Cultistwarlock => Vec3::new(3.0, 3.0, 4.5), + biped_large::Species::Huskbrute => Vec3::new(4.6, 3.0, 5.0), _ => Vec3::new(4.6, 3.0, 6.0), }, Body::BipedSmall(body) => match body.species { @@ -359,8 +363,10 @@ impl Body { quadruped_medium::Species::Saber => Vec3::new(2.0, 3.0, 2.0), quadruped_medium::Species::Tarasque => Vec3::new(2.0, 4.0, 2.6), quadruped_medium::Species::Yak => Vec3::new(2.0, 3.6, 3.0), - quadruped_medium::Species::Mammoth => Vec3::new(5.0, 7.0, 8.0), + quadruped_medium::Species::Mammoth => Vec3::new(2.0, 7.0, 8.0), quadruped_medium::Species::Ngoubou => Vec3::new(2.0, 3.2, 2.4), + quadruped_medium::Species::Llama => Vec3::new(2.0, 2.5, 2.6), + quadruped_medium::Species::Alpaca => Vec3::new(2.0, 2.0, 2.0), _ => Vec3::new(2.0, 3.0, 2.0), }, Body::QuadrupedSmall(body) => match body.species { diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index d9fea9965b..69a85e3402 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -175,7 +175,9 @@ fn default_main_tool(body: &Body) -> Item { | quadruped_medium::Species::Kelpie | quadruped_medium::Species::Hirdrasil | quadruped_medium::Species::Deer - | quadruped_medium::Species::Antelope => Some(Item::new_from_asset_expect( + | quadruped_medium::Species::Antelope + | quadruped_medium::Species::Llama + | quadruped_medium::Species::Alpaca => Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.quadmedhoof", )), quadruped_medium::Species::Saber => Some(Item::new_from_asset_expect( @@ -267,9 +269,9 @@ fn default_main_tool(body: &Body) -> Item { (biped_large::Species::Wendigo, _) => Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.wendigo_magic", )), - (biped_large::Species::Werewolf | biped_large::Species::Huskbrute, _) => Some( - Item::new_from_asset_expect("common.items.npc_weapons.unique.beast_claws"), - ), + (biped_large::Species::Werewolf, _) => Some(Item::new_from_asset_expect( + "common.items.npc_weapons.unique.beast_claws", + )), (biped_large::Species::Cyclops, _) => Some(Item::new_from_asset_expect( "common.items.npc_weapons.hammer.cyclops_hammer", )), @@ -303,6 +305,9 @@ fn default_main_tool(body: &Body) -> Item { (biped_large::Species::Cultistwarlock, _) => Some(Item::new_from_asset_expect( "common.items.weapons.staff.bipedlarge-cultist_staff", )), + (biped_large::Species::Huskbrute, _) => Some(Item::new_from_asset_expect( + "common.items.npc_weapons.unique.husk_brute", + )), }, Body::Object(body) => match body { object::Body::Crossbow => Some(Item::new_from_asset_expect( diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 10968783d6..102260b501 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -70,8 +70,8 @@ impl Body { quadruped_medium::Species::Snowleopard => 160.0, quadruped_medium::Species::Mammoth => 180.0, quadruped_medium::Species::Ngoubou => 170.0, - quadruped_medium::Species::Llama => 130.0, - quadruped_medium::Species::Alpaca => 120.0, + quadruped_medium::Species::Llama => 120.0, + quadruped_medium::Species::Alpaca => 110.0, }, Body::BipedLarge(body) => match body.species { biped_large::Species::Slysaurok => 100.0, diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 983dc3e1f6..35ac92b0a5 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -369,7 +369,7 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc "common.loot_tables.creature.quad_small.mushroom" }, quadruped_small::Species::Sheep => { - "common.loot_tables.creature.quad_small.sheep" + "common.loot_tables.creature.quad_small.wool" }, quadruped_small::Species::Skunk | quadruped_small::Species::Quokka @@ -407,8 +407,7 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc quadruped_medium::Species::Dreadhorn => { "common.loot_tables.creature.quad_medium.dreadhorn" }, - quadruped_medium::Species::Mouflon - | quadruped_medium::Species::Camel + quadruped_medium::Species::Camel | quadruped_medium::Species::Deer | quadruped_medium::Species::Hirdrasil | quadruped_medium::Species::Horse @@ -418,10 +417,13 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc | quadruped_medium::Species::Antelope | quadruped_medium::Species::Kelpie | quadruped_medium::Species::Cattle - | quadruped_medium::Species::Yak + | quadruped_medium::Species::Yak => { + "common.loot_tables.creature.quad_medium.gentle" + }, + quadruped_medium::Species::Mouflon | quadruped_medium::Species::Llama | quadruped_medium::Species::Alpaca => { - "common.loot_tables.creature.quad_medium.gentle" + "common.loot_tables.creature.quad_medium.wool" }, quadruped_medium::Species::Ngoubou => { "common.loot_tables.creature.quad_medium.horned" diff --git a/voxygen/anim/src/biped_large/alpha.rs b/voxygen/anim/src/biped_large/alpha.rs index 0d3e7c61aa..7f2ac702ed 100644 --- a/voxygen/anim/src/biped_large/alpha.rs +++ b/voxygen/anim/src/biped_large/alpha.rs @@ -384,6 +384,48 @@ impl Animation for AlphaAnimation { * Quaternion::rotation_y(-2.8 + move1 * 3.0 + move2 * -3.0) * Quaternion::rotation_z(move1 * -1.5); }, + "Husk Brute" => { + next.shoulder_l.position = + Vec3::new(-s_a.shoulder.0, s_a.shoulder.1, s_a.shoulder.2); + next.shoulder_r.position = + Vec3::new(s_a.shoulder.0, s_a.shoulder.1, s_a.shoulder.2); + next.hand_l.position = Vec3::new(-s_a.hand.0, s_a.hand.1, s_a.hand.2); + next.hand_r.position = Vec3::new(s_a.hand.0, s_a.hand.1, s_a.hand.2); + + if mirror > 0.0 { + next.shoulder_l.orientation = Quaternion::rotation_x(move1 * 1.0) + * Quaternion::rotation_y(move1 * 1.0 + move2 * -2.0); + + next.shoulder_r.orientation = Quaternion::rotation_x(move1 * -0.6); + + next.upper_torso.orientation = + Quaternion::rotation_z(move1 * 0.4 + move2 * -1.1); + next.lower_torso.orientation = + Quaternion::rotation_z(move1 * -0.2 + move2 * 0.6); + + next.hand_l.orientation = Quaternion::rotation_x(move1 * 1.2) + * Quaternion::rotation_y(move1 * 1.0 + move2 * -2.0); + + next.hand_r.orientation = Quaternion::rotation_z(move1 * 1.0) + * Quaternion::rotation_y(move1 * 0.8 + move2 * -1.2); + } else { + next.shoulder_r.orientation = Quaternion::rotation_x(move1 * 1.0) + * Quaternion::rotation_y(move1 * -1.0 + move2 * 2.0); + + next.shoulder_l.orientation = Quaternion::rotation_x(move1 * 0.6); + + next.upper_torso.orientation = + Quaternion::rotation_z(move1 * -0.4 + move2 * 1.1); + next.lower_torso.orientation = + Quaternion::rotation_z(move1 * 0.2 + move2 * -0.6); + + next.hand_r.orientation = Quaternion::rotation_x(move1 * 1.2) + * Quaternion::rotation_y(move1 * -1.0 + move2 * 2.0); + + next.hand_l.orientation = Quaternion::rotation_z(move1 * 1.0) + * Quaternion::rotation_y(move1 * -0.8 + move2 * 1.2); + } + }, _ => {}, } } diff --git a/voxygen/anim/src/biped_large/chargemelee.rs b/voxygen/anim/src/biped_large/chargemelee.rs index 16e25c148b..74162d0691 100644 --- a/voxygen/anim/src/biped_large/chargemelee.rs +++ b/voxygen/anim/src/biped_large/chargemelee.rs @@ -148,6 +148,66 @@ impl Animation for ChargeMeleeAnimation { next.shoulder_r.orientation = Quaternion::rotation_x(-0.3 + move1 * 1.0); }, + "Husk Brute" => { + next.second.scale = Vec3::one() * 0.0; + + next.head.orientation = + Quaternion::rotation_x(move1 * 0.3 + move2 * -0.6); + next.jaw.position = Vec3::new(0.0, s_a.jaw.0, s_a.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(move2 * -0.3); + next.control_l.position = Vec3::new(-0.5, 4.0, 1.0); + next.control_r.position = Vec3::new(-0.5, 4.0, 1.0); + next.control_l.orientation = Quaternion::rotation_x(1.57); + next.control_r.orientation = Quaternion::rotation_x(1.57); + next.weapon_l.position = + Vec3::new(-12.0 + (move1 * 10.0).min(6.0), -1.0, -15.0); + next.weapon_r.position = + Vec3::new(12.0 + (move1 * -10.0).max(-6.0), -1.0, -15.0); + + next.weapon_l.orientation = Quaternion::rotation_x(-1.57 - 0.1) + * Quaternion::rotation_z(move1 * -0.8); + next.weapon_r.orientation = Quaternion::rotation_x(-1.57 - 0.1) + * Quaternion::rotation_z(move1 * 0.8); + + next.shoulder_l.orientation = + Quaternion::rotation_x(-0.3 + move1 * 2.8 + move2 * -2.8); + + next.shoulder_r.orientation = + Quaternion::rotation_x(-0.3 + move1 * 2.8 + move2 * -2.8); + + next.control.orientation = + Quaternion::rotation_x(move1 * 2.5 + move2 * -2.0); + + next.upper_torso.position = + Vec3::new(0.0, s_a.upper_torso.0, s_a.upper_torso.1); + next.upper_torso.orientation = + Quaternion::rotation_x(move1 * 0.2 + move2 * -0.6); + next.lower_torso.orientation = + Quaternion::rotation_x(move1 * -0.2 + move2 * 0.6); + + if speed < 0.1 { + next.foot_l.position = Vec3::new( + -s_a.foot.0, + s_a.foot.1 + move1 * -7.0 + move2 * 7.0, + s_a.foot.2, + ); + next.foot_l.orientation = + Quaternion::rotation_x(move1 * -0.8 + move2 * 0.8) + * Quaternion::rotation_z(move1 * 0.3 + move2 * -0.3); + + next.foot_r.position = Vec3::new( + s_a.foot.0, + s_a.foot.1 + move1 * 5.0 + move2 * -5.0, + s_a.foot.2, + ); + next.foot_r.orientation = + Quaternion::rotation_y(move1 * -0.3 + move2 * 0.3) + * Quaternion::rotation_z(move1 * 0.4 + move2 * -0.4); + } + next.main.orientation = + Quaternion::rotation_y(move1 * 0.4 + move2 * -0.6) + * Quaternion::rotation_x(move2 * -0.4); + }, _ => {}, } } diff --git a/voxygen/anim/src/biped_large/mod.rs b/voxygen/anim/src/biped_large/mod.rs index 9754e4370c..998777d2e3 100644 --- a/voxygen/anim/src/biped_large/mod.rs +++ b/voxygen/anim/src/biped_large/mod.rs @@ -230,7 +230,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Blueoni, _) => (10.5, -3.0), (Redoni, _) => (10.5, -3.0), (Cultistwarlord, _) => (0.5, 14.5), - (Cultistwarlock, _) => (0.5, 14.5), + (Cultistwarlock, _) => (0.5, 11.0), (Huskbrute, _) => (8.5, 4.0), }, jaw: match (body.species, body.body_type) { @@ -301,8 +301,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Blueoni, _) => (0.0, -8.5), (Redoni, _) => (0.0, -8.5), (Cultistwarlord, _) => (0.0, -1.5), - (Cultistwarlock, _) => (0.0, -1.5), - (Huskbrute, _) => (0.0, -6.5), + (Cultistwarlock, _) => (1.0, -3.5), + (Huskbrute, _) => (-0.5, -7.0), }, tail: match (body.species, body.body_type) { (Werewolf, _) => (-5.5, -2.0), @@ -333,8 +333,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Blueoni, _) => (11.0, 2.0, -5.5), (Redoni, _) => (11.0, 2.0, -5.5), (Cultistwarlord, _) => (11.5, -1.0, 4.5), - (Cultistwarlock, _) => (11.5, -1.0, 4.5), - (Huskbrute, _) => (10.5, 1.0, -2.5), + (Cultistwarlock, _) => (8.0, 0.0, 3.5), + (Huskbrute, _) => (10.5, 0.0, -1.5), }, hand: match (body.species, body.body_type) { (Ogre, Male) => (14.5, 0.0, -4.0), @@ -357,8 +357,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Blueoni, _) => (13.5, 0.5, -8.0), (Redoni, _) => (13.5, 0.5, -8.0), (Cultistwarlord, _) => (11.5, -1.0, -1.0), - (Cultistwarlock, _) => (11.5, -1.0, -1.0), - (Huskbrute, _) => (12.0, 1.5, -6.0), + (Cultistwarlock, _) => (9.5, -1.0, 1.0), + (Huskbrute, _) => (13.0, 0.5, -4.0), }, leg: match (body.species, body.body_type) { (Ogre, Male) => (0.0, 0.0, -4.0), @@ -382,7 +382,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Redoni, _) => (4.5, 2.0, -5.5), (Cultistwarlord, _) => (3.5, -1.0, -8.5), (Cultistwarlock, _) => (3.5, -1.0, -8.5), - (Huskbrute, _) => (4.0, 0.0, -5.5), + (Huskbrute, _) => (4.0, 0.0, -7.5), }, foot: match (body.species, body.body_type) { (Ogre, Male) => (4.0, 1.0, -12.0), @@ -405,7 +405,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Blueoni, _) => (5.0, 5.0, -12.5), (Redoni, _) => (5.0, 5.0, -12.5), (Cultistwarlord, _) => (3.5, 0.0, -12.5), - (Cultistwarlock, _) => (3.5, 0.0, -12.5), + (Cultistwarlock, _) => (3.5, 0.0, -10.5), (Huskbrute, _) => (4.5, 0.5, -12.5), }, scaler: match (body.species, body.body_type) { diff --git a/voxygen/anim/src/biped_large/wield.rs b/voxygen/anim/src/biped_large/wield.rs index c3d985fd71..ef94cd66e8 100644 --- a/voxygen/anim/src/biped_large/wield.rs +++ b/voxygen/anim/src/biped_large/wield.rs @@ -498,6 +498,63 @@ impl Animation for WieldAnimation { next.shoulder_r.orientation = Quaternion::rotation_y(0.4) * Quaternion::rotation_x(0.4); }, + "Husk Brute" => { + if speed > 0.1 { + next.hand_l.position = + Vec3::new(-s_a.hand.0, s_a.hand.1, s_a.hand.2); + next.hand_l.orientation = + Quaternion::rotation_x(1.4 + slow * 0.1) + * Quaternion::rotation_z(-PI / 2.0); + + next.hand_r.position = + Vec3::new(s_a.hand.0, s_a.hand.1, s_a.hand.2); + next.hand_r.orientation = + Quaternion::rotation_x(1.4 - slow * 0.1) + * Quaternion::rotation_z(PI / 2.0); + + next.shoulder_l.position = + Vec3::new(-s_a.shoulder.0, s_a.shoulder.1, s_a.shoulder.2); + next.shoulder_l.orientation = + Quaternion::rotation_x(1.1 + slow * 0.1); + + next.shoulder_r.position = + Vec3::new(s_a.shoulder.0, s_a.shoulder.1, s_a.shoulder.2); + next.shoulder_r.orientation = + Quaternion::rotation_x(1.1 - slow * 0.1); + } else { + next.shoulder_l.position = + Vec3::new(-s_a.shoulder.0, s_a.shoulder.1, s_a.shoulder.2); + next.shoulder_l.orientation = Quaternion::rotation_x(breathe); + + next.shoulder_r.position = + Vec3::new(s_a.shoulder.0, s_a.shoulder.1, s_a.shoulder.2); + next.shoulder_r.orientation = Quaternion::rotation_x(breathe); + + next.hand_l.position = Vec3::new( + -s_a.hand.0, + s_a.hand.1, + s_a.hand.2 + torso * -0.1, + ); + + next.hand_r.position = Vec3::new( + s_a.hand.0, + s_a.hand.1, + s_a.hand.2 + torso * -0.1, + ); + + next.leg_l.position = + Vec3::new(-s_a.leg.0, s_a.leg.1, s_a.leg.2 + torso * -0.2); + + next.leg_r.position = + Vec3::new(s_a.leg.0, s_a.leg.1, s_a.leg.2 + torso * -0.2); + + next.foot_l.position = + Vec3::new(-s_a.foot.0, s_a.foot.1, s_a.foot.2); + + next.foot_r.position = + Vec3::new(s_a.foot.0, s_a.foot.1, s_a.foot.2); + } + }, _ => {}, } } diff --git a/world/src/layer/wildlife.rs b/world/src/layer/wildlife.rs index 362fcf2fed..d09e3412d5 100644 --- a/world/src/layer/wildlife.rs +++ b/world/src/layer/wildlife.rs @@ -195,7 +195,7 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( Entry { make_entity: |pos, rng| { EntityInfo::at(pos) - .with_body(match rng.gen_range(0..3) { + .with_body(match rng.gen_range(0..5) { 0 => quadruped_medium::Body::random_with( rng, &quadruped_medium::Species::Mouflon, @@ -206,6 +206,16 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( &quadruped_medium::Species::Yak, ) .into(), + 2 => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Llama, + ) + .into(), + 3 => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Alpaca, + ) + .into(), _ => quadruped_medium::Body::random_with( rng, &quadruped_medium::Species::Highland, @@ -916,7 +926,7 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( close(c.temp, CONFIG.desert_temp + 0.2, 0.3) * close(c.humidity, CONFIG.desert_hum, 0.5) * BASE_DENSITY - * 0.01 + * 0.005 }, }, // Desert solitary wild diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index fa228fa181..750119baaa 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -1093,7 +1093,7 @@ fn mini_boss_4(tile_wcenter: Vec3) -> Vec { fn mini_boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { let mut entities = Vec::new(); - match dynamic_rng.gen_range(0..2) { + match dynamic_rng.gen_range(0..3) { 0 => { entities.push( EntityInfo::at(tile_wcenter.map(|e| e as f32)) @@ -1104,6 +1104,12 @@ fn mini_boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { + entities.resize_with(2, || { + EntityInfo::at(tile_wcenter.map(|e| e as f32)) + .with_asset_expect("common.entity.dungeon.tier-5.husk_brute") + }); + }, _ => { entities.resize_with(10, || { EntityInfo::at(tile_wcenter.map(|e| e as f32)) From d7f25627abaa7a7d125280f1effde5c3ff3987b9 Mon Sep 17 00:00:00 2001 From: Snowram Date: Wed, 23 Jun 2021 22:15:05 +0200 Subject: [PATCH 170/180] Ajustments to new npcs --- .../common/abilities/custom/husk_brute/chargedmelee.ron | 2 +- .../common/abilities/custom/husk_brute/singlestrike.ron | 2 +- assets/common/entity/dungeon/tier-5/warlock.ron | 5 ++++- assets/common/entity/dungeon/tier-5/warlord.ron | 3 +-- assets/common/items/weapons/bow/bipedlarge-velorite.ron | 4 ++-- .../items/weapons/hammer/bipedlarge-cultist_purp_2h-0.ron | 4 ++-- .../items/weapons/staff/bipedlarge-cultist_staff.ron | 4 ++-- assets/common/items/weapons/sword/bipedlarge-cultist.ron | 4 ++-- assets/voxygen/voxel/npc/ngoubou/male/torso_back.vox | 4 ++-- .../voxygen/voxel/quadruped_medium_central_manifest.ron | 6 +++--- common/src/comp/body.rs | 6 +++--- common/src/comp/inventory/loadout_builder.rs | 4 +++- common/src/states/utils.rs | 8 +++++++- server/src/sys/agent.rs | 4 ++-- voxygen/anim/src/biped_large/idle.rs | 2 +- voxygen/anim/src/biped_large/mod.rs | 2 +- voxygen/anim/src/biped_large/run.rs | 2 +- voxygen/anim/src/biped_large/shoot.rs | 2 +- voxygen/anim/src/quadruped_medium/mod.rs | 4 ++-- 19 files changed, 41 insertions(+), 31 deletions(-) diff --git a/assets/common/abilities/custom/husk_brute/chargedmelee.ron b/assets/common/abilities/custom/husk_brute/chargedmelee.ron index e4006d5949..94c388f71b 100644 --- a/assets/common/abilities/custom/husk_brute/chargedmelee.ron +++ b/assets/common/abilities/custom/husk_brute/chargedmelee.ron @@ -7,7 +7,7 @@ ChargedMelee( scaled_poise_damage: 150, initial_knockback: 0.0, scaled_knockback: 0.0, - range: 5.0, + range: 3.5, max_angle: 45.0, speed: 1.0, charge_duration: 1.5, diff --git a/assets/common/abilities/custom/husk_brute/singlestrike.ron b/assets/common/abilities/custom/husk_brute/singlestrike.ron index d64184bfa8..3c93be79ca 100644 --- a/assets/common/abilities/custom/husk_brute/singlestrike.ron +++ b/assets/common/abilities/custom/husk_brute/singlestrike.ron @@ -7,7 +7,7 @@ ComboMelee( base_poise_damage: 12, poise_damage_increase: 0, knockback: 5.0, - range: 2.5, + range: 3.5, angle: 60.0, base_buildup_duration: 0.25, base_swing_duration: 0.07, diff --git a/assets/common/entity/dungeon/tier-5/warlock.ron b/assets/common/entity/dungeon/tier-5/warlock.ron index 3ad3030460..b6d9ae4608 100644 --- a/assets/common/entity/dungeon/tier-5/warlock.ron +++ b/assets/common/entity/dungeon/tier-5/warlock.ron @@ -4,7 +4,10 @@ EntityConfig ( loot: Some(LootTable("common.loot_tables.dungeon.tier-5.enemy")), - main_tool: Some(Item("common.items.weapons.staff.bipedlarge-cultist_staff")), + main_tool: Some(Choice([ + (1.5, Some(Item("common.items.weapons.staff.bipedlarge-cultist_staff"))), + (1.0, Some(Item("common.items.weapons.bow.bipedlarge-velorite"))), + ])), second_tool: None, loadout_asset: None, diff --git a/assets/common/entity/dungeon/tier-5/warlord.ron b/assets/common/entity/dungeon/tier-5/warlord.ron index 4f7c0c4327..e248724c60 100644 --- a/assets/common/entity/dungeon/tier-5/warlord.ron +++ b/assets/common/entity/dungeon/tier-5/warlord.ron @@ -5,9 +5,8 @@ EntityConfig ( loot: Some(LootTable("common.loot_tables.dungeon.tier-5.enemy")), main_tool: Some(Choice([ - (2.0, Some(Item("common.items.weapons.sword.bipedlarge-cultist"))), + (1.5, Some(Item("common.items.weapons.sword.bipedlarge-cultist"))), (1.0, Some(Item("common.items.weapons.hammer.bipedlarge-cultist_purp_2h-0"))), - (1.0, Some(Item("common.items.weapons.bow.bipedlarge-velorite"))), ])), second_tool: None, diff --git a/assets/common/items/weapons/bow/bipedlarge-velorite.ron b/assets/common/items/weapons/bow/bipedlarge-velorite.ron index f9fe278cf3..800a221e8e 100644 --- a/assets/common/items/weapons/bow/bipedlarge-velorite.ron +++ b/assets/common/items/weapons/bow/bipedlarge-velorite.ron @@ -6,9 +6,9 @@ ItemDef( hands: Two, stats: Direct(( equip_time_secs: 0.4, - power: 2.0, + power: 1.6, poise_strength: 1.1, - speed: 1.0, + speed: 0.8, crit_chance: 0.140625, )), )), diff --git a/assets/common/items/weapons/hammer/bipedlarge-cultist_purp_2h-0.ron b/assets/common/items/weapons/hammer/bipedlarge-cultist_purp_2h-0.ron index a68c9c4a58..6161fa12a7 100644 --- a/assets/common/items/weapons/hammer/bipedlarge-cultist_purp_2h-0.ron +++ b/assets/common/items/weapons/hammer/bipedlarge-cultist_purp_2h-0.ron @@ -6,9 +6,9 @@ ItemDef( hands: Two, stats: Direct(( equip_time_secs: 0.5, - power: 2.0, + power: 1.6, poise_strength: 1.0, - speed: 1.0, + speed: 0.8, crit_chance: 0.140625, )), )), diff --git a/assets/common/items/weapons/staff/bipedlarge-cultist_staff.ron b/assets/common/items/weapons/staff/bipedlarge-cultist_staff.ron index f0ac7f7bf3..c709f9a11b 100644 --- a/assets/common/items/weapons/staff/bipedlarge-cultist_staff.ron +++ b/assets/common/items/weapons/staff/bipedlarge-cultist_staff.ron @@ -6,9 +6,9 @@ ItemDef( hands: Two, stats: Direct(( equip_time_secs: 0.3, - power: 2.0, + power: 1.6, poise_strength: 1.0, - speed: 1.0, + speed: 0.8, crit_chance: 0.140625, )), )), diff --git a/assets/common/items/weapons/sword/bipedlarge-cultist.ron b/assets/common/items/weapons/sword/bipedlarge-cultist.ron index 1f33916eb1..dd62980f60 100644 --- a/assets/common/items/weapons/sword/bipedlarge-cultist.ron +++ b/assets/common/items/weapons/sword/bipedlarge-cultist.ron @@ -6,9 +6,9 @@ ItemDef( hands: Two, stats: Direct(( equip_time_secs: 0.5, - power: 2.0, + power: 1.6, poise_strength: 1.0, - speed: 1.0, + speed: 0.8, crit_chance: 0.140625, )), )), diff --git a/assets/voxygen/voxel/npc/ngoubou/male/torso_back.vox b/assets/voxygen/voxel/npc/ngoubou/male/torso_back.vox index d39ed61f33..e612f567c2 100644 --- a/assets/voxygen/voxel/npc/ngoubou/male/torso_back.vox +++ b/assets/voxygen/voxel/npc/ngoubou/male/torso_back.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cef260646963e12fb959dad520bd3574e0fd752ad580d0aa7c4ed9069679b52c -size 6852 +oid sha256:ffc5d23315c7e532b43836ac282b13bf0f3bc3d4361d38a8af4e0a599148cd1e +size 7264 diff --git a/assets/voxygen/voxel/quadruped_medium_central_manifest.ron b/assets/voxygen/voxel/quadruped_medium_central_manifest.ron index a1f9869837..69901c6685 100644 --- a/assets/voxygen/voxel/quadruped_medium_central_manifest.ron +++ b/assets/voxygen/voxel/quadruped_medium_central_manifest.ron @@ -1809,7 +1809,7 @@ central: ("npc.mammoth.male.neck"), ), jaw: ( - offset: (-3.0, 0.0, -13.0), + offset: (-3.0, -5.0, -13.0), central: ("npc.mammoth.male.jaw"), ), torso_front: ( @@ -1839,7 +1839,7 @@ central: ("npc.mammoth.male.neck"), ), jaw: ( - offset: (-3.0, 0.0, -13.0), + offset: (-3.0, -5.0, -13.0), central: ("npc.mammoth.male.jaw"), ), torso_front: ( @@ -1869,7 +1869,7 @@ central: ("npc.ngoubou.male.neck"), ), jaw: ( - offset: (-2.5, 0.0, -3.0), + offset: (-2.5, -7.0, -3.0), central: ("npc.ngoubou.male.jaw"), ), torso_front: ( diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 6755a2bb18..8a1ab6de4a 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -310,7 +310,7 @@ impl Body { biped_large::Species::Werewolf => Vec3::new(4.0, 3.0, 3.5), biped_large::Species::Harvester => Vec3::new(4.6, 3.0, 5.4), biped_large::Species::Cultistwarlord => Vec3::new(3.0, 3.0, 4.5), - biped_large::Species::Cultistwarlock => Vec3::new(3.0, 3.0, 4.5), + biped_large::Species::Cultistwarlock => Vec3::new(3.0, 3.0, 3.5), biped_large::Species::Huskbrute => Vec3::new(4.6, 3.0, 5.0), _ => Vec3::new(4.6, 3.0, 6.0), }, @@ -469,7 +469,7 @@ impl Body { quadruped_medium::Species::Bear => 900, quadruped_medium::Species::Moose => 800, quadruped_medium::Species::Dreadhorn => 1100, - quadruped_medium::Species::Mammoth => 1700, + quadruped_medium::Species::Mammoth => 1900, quadruped_medium::Species::Ngoubou => 1500, _ => 700, }, @@ -594,7 +594,7 @@ impl Body { quadruped_medium::Species::Bear => 40, quadruped_medium::Species::Moose => 30, quadruped_medium::Species::Dreadhorn => 50, - quadruped_medium::Species::Mammoth => 70, + quadruped_medium::Species::Mammoth => 80, quadruped_medium::Species::Ngoubou => 50, _ => 20, }, diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 69a85e3402..c3b6f5d3ff 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -186,7 +186,9 @@ fn default_main_tool(body: &Body) -> Item { quadruped_medium::Species::Tuskram | quadruped_medium::Species::Roshwalr | quadruped_medium::Species::Moose - | quadruped_medium::Species::Dreadhorn => Some(Item::new_from_asset_expect( + | quadruped_medium::Species::Dreadhorn + | quadruped_medium::Species::Mammoth + | quadruped_medium::Species::Ngoubou => Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.quadmedcharge", )), quadruped_medium::Species::Highland diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 102260b501..d18830cf56 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -79,6 +79,9 @@ impl Body { biped_large::Species::Mightysaurok => 100.0, biped_large::Species::Mindflayer => 90.0, biped_large::Species::Minotaur => 60.0, + biped_large::Species::Huskbrute => 130.0, + biped_large::Species::Cultistwarlord => 110.0, + biped_large::Species::Cultistwarlock => 90.0, _ => 80.0, }, Body::BirdMedium(_) => 80.0, @@ -138,7 +141,10 @@ impl Body { match self { Body::Humanoid(_) => 3.5, Body::QuadrupedSmall(_) => 3.0, - Body::QuadrupedMedium(_) => 2.8, + Body::QuadrupedMedium(quadruped_medium) => match quadruped_medium.species { + quadruped_medium::Species::Mammoth => 2.2, + _ => 2.8, + }, Body::BirdMedium(_) => 6.0, Body::FishMedium(_) => 6.0, Body::Dragon(_) => 1.0, diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 54d9fa04c2..2810a7cd40 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -1637,7 +1637,7 @@ impl<'a> AgentData<'a> { }, "Quad Med Jump" => Tactic::QuadMedJump, "Quad Med Charge" => Tactic::CircleCharge { - radius: 12, + radius: 6, circle_time: 1, }, "Quad Med Basic" => Tactic::QuadMedBasic, @@ -1645,7 +1645,7 @@ impl<'a> AgentData<'a> { "Quad Low Breathe" | "Quad Low Beam" | "Basilisk" => { Tactic::QuadLowBeam }, - "Quad Low Tail" => Tactic::TailSlap, + "Quad Low Tail" | "Husk Brute" => Tactic::TailSlap, "Quad Low Quick" => Tactic::QuadLowQuick, "Quad Low Basic" => Tactic::QuadLowBasic, "Theropod Basic" | "Theropod Bird" => Tactic::Theropod, diff --git a/voxygen/anim/src/biped_large/idle.rs b/voxygen/anim/src/biped_large/idle.rs index 96a2448fce..366a4c33bd 100644 --- a/voxygen/anim/src/biped_large/idle.rs +++ b/voxygen/anim/src/biped_large/idle.rs @@ -90,7 +90,7 @@ impl Animation for IdleAnimation { match active_tool_kind { Some(ToolKind::Bow) => { - next.main.position = Vec3::new(-2.0, -5.0, -6.0); + next.main.position = Vec3::new(0.0, -6.0, 0.0); next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, Some(ToolKind::Staff) | Some(ToolKind::Sceptre) => { diff --git a/voxygen/anim/src/biped_large/mod.rs b/voxygen/anim/src/biped_large/mod.rs index 998777d2e3..f90f29f610 100644 --- a/voxygen/anim/src/biped_large/mod.rs +++ b/voxygen/anim/src/biped_large/mod.rs @@ -421,7 +421,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Occultsaurok, _) => 1.0, (Mightysaurok, _) => 1.0, (Slysaurok, _) => 1.0, - (Mindflayer, _) => 1.5, + (Mindflayer, _) => 1.6, (Minotaur, _) => 1.7, (Tidalwarrior, _) => 1.7, (Yeti, _) => 1.2, diff --git a/voxygen/anim/src/biped_large/run.rs b/voxygen/anim/src/biped_large/run.rs index e4c074d2f5..51c5829d0d 100644 --- a/voxygen/anim/src/biped_large/run.rs +++ b/voxygen/anim/src/biped_large/run.rs @@ -295,7 +295,7 @@ impl Animation for RunAnimation { match active_tool_kind { Some(ToolKind::Bow) => { - next.main.position = Vec3::new(-2.0, -5.0, -6.0); + next.main.position = Vec3::new(0.0, -6.0, 0.0); next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, diff --git a/voxygen/anim/src/biped_large/shoot.rs b/voxygen/anim/src/biped_large/shoot.rs index bcebd91075..2b9428d06f 100644 --- a/voxygen/anim/src/biped_large/shoot.rs +++ b/voxygen/anim/src/biped_large/shoot.rs @@ -156,7 +156,7 @@ impl Animation for ShootAnimation { next.control.position = Vec3::new( -1.0 + move1 * 2.0, 6.0 + s_a.grip.0 / 1.2 + move1 * 7.0, - -5.0 + -s_a.grip.0 / 2.0 + move1 * s_a.height * 2.8, + -5.0 + -s_a.grip.0 / 2.0 + move1 * s_a.height * 3.4, ); next.control_l.orientation = diff --git a/voxygen/anim/src/quadruped_medium/mod.rs b/voxygen/anim/src/quadruped_medium/mod.rs index c4f74eeb05..96cdd6a2ac 100644 --- a/voxygen/anim/src/quadruped_medium/mod.rs +++ b/voxygen/anim/src/quadruped_medium/mod.rs @@ -250,8 +250,8 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Moose, Male) => (10.0, -7.0), (Moose, Female) => (6.0, -2.5), (Snowleopard, _) => (3.0, -3.0), - (Mammoth, _) => (4.5, -3.0), - (Ngoubou, _) => (4.5, -4.0), + (Mammoth, _) => (9.5, -3.0), + (Ngoubou, _) => (8.5, -4.0), (Llama, _) => (4.0, -1.0), (Alpaca, _) => (3.0, -2.5), }, From ef6d6563ea05883f1265abe70c8cacd3d6d56b78 Mon Sep 17 00:00:00 2001 From: Snowram Date: Thu, 24 Jun 2021 23:04:17 +0200 Subject: [PATCH 171/180] Ajust new npcs from feedback --- .../entity/dungeon/tier-5/husk_brute.ron | 2 +- .../common/entity/dungeon/tier-5/warlord.ron | 2 +- .../creature/quad_low/salamander.ron | 4 +++ .../creature/quad_medium/dreadhorn.ron | 2 +- .../creature/quad_medium/mammoth.ron | 2 +- .../loot_tables/creature/theropod/generic.ron | 2 +- .../loot_tables/creature/theropod/raptor.ron | 2 +- assets/common/recipe_book.ron | 18 +++++----- .../skillset/dungeon/tier-5/sword_simple.ron | 13 +++++++ .../skillset/dungeon/tier-5/warlord.ron | 5 +++ common/src/comp/body.rs | 19 ++++++---- server/src/events/entity_manipulation.rs | 5 ++- .../migrations/V41_remove_warlock_warlord.sql | 36 +++++++++---------- voxygen/anim/src/biped_large/mod.rs | 4 +-- world/src/layer/wildlife.rs | 28 ++++++++++++--- 15 files changed, 97 insertions(+), 47 deletions(-) create mode 100644 assets/common/loot_tables/creature/quad_low/salamander.ron create mode 100644 assets/common/skillset/dungeon/tier-5/sword_simple.ron create mode 100644 assets/common/skillset/dungeon/tier-5/warlord.ron diff --git a/assets/common/entity/dungeon/tier-5/husk_brute.ron b/assets/common/entity/dungeon/tier-5/husk_brute.ron index fc2592fb47..40f26815b6 100644 --- a/assets/common/entity/dungeon/tier-5/husk_brute.ron +++ b/assets/common/entity/dungeon/tier-5/husk_brute.ron @@ -2,7 +2,7 @@ EntityConfig ( name: Some("Husk Brute"), body: Some(RandomWith("husk_brute")), - loot: Some(LootTable("common.loot_tables.dungeon.tier-5.minion")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-5.miniboss")), main_tool: None, second_tool: None, diff --git a/assets/common/entity/dungeon/tier-5/warlord.ron b/assets/common/entity/dungeon/tier-5/warlord.ron index e248724c60..01f7c29a71 100644 --- a/assets/common/entity/dungeon/tier-5/warlord.ron +++ b/assets/common/entity/dungeon/tier-5/warlord.ron @@ -11,5 +11,5 @@ EntityConfig ( second_tool: None, loadout_asset: None, - skillset_asset: Some("common.skillset.dungeon.tier-5.enemy"), + skillset_asset: Some("common.skillset.dungeon.tier-5.warlord"), ) diff --git a/assets/common/loot_tables/creature/quad_low/salamander.ron b/assets/common/loot_tables/creature/quad_low/salamander.ron new file mode 100644 index 0000000000..faad9ec4e5 --- /dev/null +++ b/assets/common/loot_tables/creature/quad_low/salamander.ron @@ -0,0 +1,4 @@ +[ + (1.0, Item("common.items.crafting_ing.hide.tough_hide")), + (3.0, Item("common.items.crafting_ing.hide.scales")), +] \ No newline at end of file diff --git a/assets/common/loot_tables/creature/quad_medium/dreadhorn.ron b/assets/common/loot_tables/creature/quad_medium/dreadhorn.ron index 74c79cede6..dbdeda8a3a 100644 --- a/assets/common/loot_tables/creature/quad_medium/dreadhorn.ron +++ b/assets/common/loot_tables/creature/quad_medium/dreadhorn.ron @@ -2,5 +2,5 @@ (1.5, Item("common.items.food.meat.beast_small_raw")), (0.5, Item("common.items.food.meat.beast_large_raw")), (2.0, Item("common.items.crafting_ing.animal_misc.large_horn")), - (3.0, Item("common.items.crafting_ing.hide.carapace")), + (3.0, Item("common.items.crafting_ing.hide.rugged_hide")), ] \ No newline at end of file diff --git a/assets/common/loot_tables/creature/quad_medium/mammoth.ron b/assets/common/loot_tables/creature/quad_medium/mammoth.ron index 73dd2ad8a6..810ff5b9a5 100644 --- a/assets/common/loot_tables/creature/quad_medium/mammoth.ron +++ b/assets/common/loot_tables/creature/quad_medium/mammoth.ron @@ -1,4 +1,4 @@ [ - (1.0, ItemQuantity("common.items.crafting_ing.hide.tough_hide", 2, 3)), + (1.0, ItemQuantity("common.items.crafting_ing.hide.rugged_hide", 1, 2)), (1.0, ItemQuantity("common.items.crafting_ing.animal_misc.large_horn", 2, 3)), ] \ No newline at end of file diff --git a/assets/common/loot_tables/creature/theropod/generic.ron b/assets/common/loot_tables/creature/theropod/generic.ron index 14dbec1175..17b921280a 100644 --- a/assets/common/loot_tables/creature/theropod/generic.ron +++ b/assets/common/loot_tables/creature/theropod/generic.ron @@ -1,3 +1,3 @@ [ - (1.0, Item("common.items.crafting_ing.hide.plate")), + (1.0, Item("common.items.crafting_ing.hide.rugged_hide")), ] \ No newline at end of file diff --git a/assets/common/loot_tables/creature/theropod/raptor.ron b/assets/common/loot_tables/creature/theropod/raptor.ron index 2cd8de953f..818f740948 100644 --- a/assets/common/loot_tables/creature/theropod/raptor.ron +++ b/assets/common/loot_tables/creature/theropod/raptor.ron @@ -1,5 +1,5 @@ [ (0.5, Item("common.items.food.meat.beast_large_raw")), - (1.0, Item("common.items.crafting_ing.hide.tough_hide")), + (1.0, Item("common.items.crafting_ing.hide.scales")), (1.0, Item("common.items.crafting_ing.animal_misc.raptor_feather")), ] \ No newline at end of file diff --git a/assets/common/recipe_book.ron b/assets/common/recipe_book.ron index 9f8f48e7dd..16bf38cecd 100644 --- a/assets/common/recipe_book.ron +++ b/assets/common/recipe_book.ron @@ -275,7 +275,7 @@ output: ("common.items.mineral.ingot.bloodsteel", 2), inputs: [ (Item("common.items.mineral.ore.bloodstone"), 1), - (Item("common.items.mineral.ore.iron"), 1), + (Item("common.items.mineral.ingot.iron"), 1), (Item("common.items.mineral.ore.coal"), 1), ], craft_sprite: Some(Forge), @@ -283,7 +283,7 @@ "steel ingot": ( output: ("common.items.mineral.ingot.steel", 1), inputs: [ - (Item("common.items.mineral.ore.iron"), 1), + (Item("common.items.mineral.ingot.iron"), 1), (Item("common.items.mineral.ore.coal"), 1), ], craft_sprite: Some(Forge), @@ -917,8 +917,8 @@ "woolen belt": ( output: ("common.items.armor.cloth.woolen.belt", 1), inputs: [ - (Item("common.items.crafting_ing.cloth.wool"), 2), - (Item("common.items.crafting_ing.cloth.cotton"), 2), + (Item("common.items.crafting_ing.cloth.wool"), 1), + (Item("common.items.crafting_ing.cloth.cotton"), 1), (Item("common.items.crafting_ing.animal_misc.fur"), 1), (Item("common.items.crafting_tools.sewing_set"), 0), ], @@ -927,7 +927,7 @@ "woolen chest": ( output: ("common.items.armor.cloth.woolen.chest", 1), inputs: [ - (Item("common.items.crafting_ing.cloth.wool"), 10), + (Item("common.items.crafting_ing.cloth.wool"), 8), (Item("common.items.crafting_ing.cloth.cotton"), 2), (Item("common.items.crafting_ing.animal_misc.fur"), 2), (Item("common.items.crafting_tools.sewing_set"), 0), @@ -937,7 +937,7 @@ "woolen feet": ( output: ("common.items.armor.cloth.woolen.foot", 1), inputs: [ - (Item("common.items.crafting_ing.cloth.wool"), 3), + (Item("common.items.crafting_ing.cloth.wool"), 2), (Item("common.items.crafting_ing.cloth.cotton"), 1), (Item("common.items.crafting_ing.animal_misc.fur"), 1), (Item("common.items.crafting_tools.sewing_set"), 0), @@ -947,7 +947,7 @@ "woolen hands": ( output: ("common.items.armor.cloth.woolen.hand", 1), inputs: [ - (Item("common.items.crafting_ing.cloth.wool"), 3), + (Item("common.items.crafting_ing.cloth.wool"), 2), (Item("common.items.crafting_ing.cloth.cotton"), 1), (Item("common.items.crafting_ing.animal_misc.fur"), 1), (Item("common.items.crafting_tools.sewing_set"), 0), @@ -957,7 +957,7 @@ "woolen pants": ( output: ("common.items.armor.cloth.woolen.pants", 1), inputs: [ - (Item("common.items.crafting_ing.cloth.wool"), 8), + (Item("common.items.crafting_ing.cloth.wool"), 6), (Item("common.items.crafting_ing.cloth.cotton"), 2), (Item("common.items.crafting_ing.animal_misc.fur"), 2), (Item("common.items.crafting_tools.sewing_set"), 0), @@ -967,7 +967,7 @@ "woolen shoulder": ( output: ("common.items.armor.cloth.woolen.shoulder", 1), inputs: [ - (Item("common.items.crafting_ing.cloth.wool"), 8), + (Item("common.items.crafting_ing.cloth.wool"), 6), (Item("common.items.crafting_ing.cloth.cotton"), 2), (Item("common.items.crafting_ing.animal_misc.fur"), 2), (Item("common.items.crafting_tools.sewing_set"), 0), diff --git a/assets/common/skillset/dungeon/tier-5/sword_simple.ron b/assets/common/skillset/dungeon/tier-5/sword_simple.ron new file mode 100644 index 0000000000..28d8bc547d --- /dev/null +++ b/assets/common/skillset/dungeon/tier-5/sword_simple.ron @@ -0,0 +1,13 @@ +([ + Group(Weapon(Sword)), + + // TripleStrike + Skill((Sword(TsCombo), None)), + Skill((Sword(TsDamage), Some(1))), + Skill((Sword(TsRegen), Some(1))), + + // Dash + Skill((Sword(DDamage), Some(1))), + Skill((Sword(DCost), Some(1))), + Skill((Sword(DDrain), Some(1))), +]) diff --git a/assets/common/skillset/dungeon/tier-5/warlord.ron b/assets/common/skillset/dungeon/tier-5/warlord.ron new file mode 100644 index 0000000000..85c6edcab3 --- /dev/null +++ b/assets/common/skillset/dungeon/tier-5/warlord.ron @@ -0,0 +1,5 @@ +([ + // Just gather everything + Tree("common.skillset.dungeon.tier-5.sword_simple"), + Tree("common.skillset.dungeon.tier-5.hammer"), +]) diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 8a1ab6de4a..2ae9bc6905 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -468,9 +468,9 @@ impl Body { quadruped_medium::Species::Panda => 900, quadruped_medium::Species::Bear => 900, quadruped_medium::Species::Moose => 800, - quadruped_medium::Species::Dreadhorn => 1100, - quadruped_medium::Species::Mammoth => 1900, - quadruped_medium::Species::Ngoubou => 1500, + quadruped_medium::Species::Dreadhorn => 1300, + quadruped_medium::Species::Mammoth => 2500, + quadruped_medium::Species::Ngoubou => 1800, _ => 700, }, Body::BirdMedium(bird_medium) => match bird_medium.species { @@ -504,6 +504,7 @@ impl Body { biped_large::Species::Harvester => 5000, biped_large::Species::Blueoni => 2400, biped_large::Species::Redoni => 2400, + biped_large::Species::Huskbrute => 8000, _ => 1200, }, Body::BipedSmall(biped_small) => match biped_small.species { @@ -541,7 +542,7 @@ impl Body { quadruped_low::Species::Rocksnapper => 1400, quadruped_low::Species::Pangolin => 400, quadruped_low::Species::Maneater => 1300, - quadruped_low::Species::Sandshark => 900, + quadruped_low::Species::Sandshark => 1100, quadruped_low::Species::Hakulaq => 500, quadruped_low::Species::Lavadrake => 1600, quadruped_low::Species::Basilisk => 2000, @@ -595,7 +596,7 @@ impl Body { quadruped_medium::Species::Moose => 30, quadruped_medium::Species::Dreadhorn => 50, quadruped_medium::Species::Mammoth => 80, - quadruped_medium::Species::Ngoubou => 50, + quadruped_medium::Species::Ngoubou => 60, _ => 20, }, Body::BirdMedium(bird_medium) => match bird_medium.species { @@ -622,6 +623,7 @@ impl Body { biped_large::Species::Mountaintroll => 60, biped_large::Species::Swamptroll => 60, biped_large::Species::Dullahan => 120, + biped_large::Species::Huskbrute => 100, // Boss enemies have their health set, not adjusted by level. biped_large::Species::Mindflayer => 0, biped_large::Species::Minotaur => 0, @@ -644,7 +646,7 @@ impl Body { quadruped_low::Species::Rocksnapper => 50, quadruped_low::Species::Pangolin => 10, quadruped_low::Species::Maneater => 30, - quadruped_low::Species::Sandshark => 40, + quadruped_low::Species::Sandshark => 45, quadruped_low::Species::Hakulaq => 10, quadruped_low::Species::Deadwood => 30, _ => 20, @@ -670,6 +672,11 @@ impl Body { Body::Golem(g) => matches!(g.species, golem::Species::ClayGolem), Body::BipedSmall(b) => matches!(b.species, biped_small::Species::Haniwa), Body::Object(object::Body::HaniwaSentry) => true, + Body::QuadrupedLow(q) => matches!(q.species, quadruped_low::Species::Lavadrake), + Body::BirdLarge(b) => matches!( + b.species, + bird_large::Species::Phoenix | bird_large::Species::Cockatrice + ), _ => false, }, BuffKind::Ensnared => { diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 35ac92b0a5..b01f17e13a 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -368,7 +368,7 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc quadruped_small::Species::Truffler | quadruped_small::Species::Fungome => { "common.loot_tables.creature.quad_small.mushroom" }, - quadruped_small::Species::Sheep => { + quadruped_small::Species::Sheep | quadruped_small::Species::Goat => { "common.loot_tables.creature.quad_small.wool" }, quadruped_small::Species::Skunk @@ -495,6 +495,9 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc quadruped_low::Species::Basilisk => { "common.loot_tables.creature.quad_low.basilisk" }, + quadruped_low::Species::Salamander => { + "common.loot_tables.creature.quad_low.salamander" + }, _ => "common.loot_tables.creature.quad_low.generic", } }, diff --git a/server/src/migrations/V41_remove_warlock_warlord.sql b/server/src/migrations/V41_remove_warlock_warlord.sql index e445c8a50d..500a505c56 100644 --- a/server/src/migrations/V41_remove_warlock_warlord.sql +++ b/server/src/migrations/V41_remove_warlock_warlord.sql @@ -1,33 +1,31 @@ --- xx +-- Replace all warlord and warlock sets into cultist set UPDATE item -SET item_definition_id = 'common.items.armor.warlock.shoulder' WHERE item_definition_id = 'common.items.armor.cultist.shoulder'; +SET item_definition_id = 'common.items.armor.cultist.shoulder' WHERE item_definition_id = 'common.items.armor.warlock.shoulder'; UPDATE item -SET item_definition_id = 'common.items.armor.warlock.chest' WHERE item_definition_id = 'common.items.armor.cultist.chest'; +SET item_definition_id = 'common.items.armor.cultist.chest' WHERE item_definition_id = 'common.items.armor.warlock.chest'; UPDATE item -SET item_definition_id = 'common.items.armor.warlock.belt' WHERE item_definition_id = 'common.items.armor.cultist.belt'; +SET item_definition_id = 'common.items.armor.cultist.belt' WHERE item_definition_id = 'common.items.armor.warlock.belt'; UPDATE item -SET item_definition_id = 'common.items.armor.warlock.hand' WHERE item_definition_id = 'common.items.armor.cultist.hand'; +SET item_definition_id = 'common.items.armor.cultist.hand' WHERE item_definition_id = 'common.items.armor.warlock.hand'; UPDATE item -SET item_definition_id = 'common.items.armor.warlock.pants' WHERE item_definition_id = 'common.items.armor.cultist.pants'; +SET item_definition_id = 'common.items.armor.cultist.pants' WHERE item_definition_id = 'common.items.armor.warlock.pants'; UPDATE item -SET item_definition_id = 'common.items.armor.warlock.foot' WHERE item_definition_id = 'common.items.armor.cultist.foot'; +SET item_definition_id = 'common.items.armor.cultist.foot' WHERE item_definition_id = 'common.items.armor.warlock.foot'; UPDATE item -SET item_definition_id = 'common.items.armor.warlock.back' WHERE item_definition_id = 'common.items.armor.misc.back.dungeon_purple'; +SET item_definition_id = 'common.items.armor.misc.back.dungeon_purple' WHERE item_definition_id = 'common.items.armor.warlock.back'; +DELETE FROM item WHERE item_definition_id = 'common.items.armor.warlock.head'; UPDATE item -SET item_definition_id = 'common.items.armor.warlock.head' WHERE item_definition_id = 'common.items.armor.cultist.head'; +SET item_definition_id = 'common.items.armor.cultist.shoulder' WHERE item_definition_id = 'common.items.armor.warlord.shoulder'; UPDATE item -SET item_definition_id = 'common.items.armor.warlord.shoulder' WHERE item_definition_id = 'common.items.armor.cultist.shoulder'; +SET item_definition_id = 'common.items.armor.cultist.chest' WHERE item_definition_id = 'common.items.armor.warlord.chest'; UPDATE item -SET item_definition_id = 'common.items.armor.warlord.chest' WHERE item_definition_id = 'common.items.armor.cultist.chest'; +SET item_definition_id = 'common.items.armor.cultist.belt' WHERE item_definition_id = 'common.items.armor.warlord.belt'; UPDATE item -SET item_definition_id = 'common.items.armor.warlord.belt' WHERE item_definition_id = 'common.items.armor.cultist.belt'; +SET item_definition_id = 'common.items.armor.cultist.hand' WHERE item_definition_id = 'common.items.armor.warlord.hand'; UPDATE item -SET item_definition_id = 'common.items.armor.warlord.hand' WHERE item_definition_id = 'common.items.armor.cultist.hand'; +SET item_definition_id = 'common.items.armor.cultist.pants' WHERE item_definition_id = 'common.items.armor.warlord.pants'; UPDATE item -SET item_definition_id = 'common.items.armor.warlord.pants' WHERE item_definition_id = 'common.items.armor.cultist.pants'; +SET item_definition_id = 'common.items.armor.cultist.foot' WHERE item_definition_id = 'common.items.armor.warlord.foot'; UPDATE item -SET item_definition_id = 'common.items.armor.warlord.foot' WHERE item_definition_id = 'common.items.armor.cultist.foot'; -UPDATE item -SET item_definition_id = 'common.items.armor.warlord.back' WHERE item_definition_id = 'common.items.armor.misc.back.dungeon_purple'; -UPDATE item -SET item_definition_id = 'common.items.armor.warlord.head' WHERE item_definition_id = 'common.items.armor.cultist.head'; \ No newline at end of file +SET item_definition_id = 'common.items.armor.misc.back.dungeon_purple' WHERE item_definition_id = 'common.items.armor.warlord.back'; +DELETE FROM item WHERE item_definition_id = 'common.items.armor.warlord.head'; \ No newline at end of file diff --git a/voxygen/anim/src/biped_large/mod.rs b/voxygen/anim/src/biped_large/mod.rs index f90f29f610..3af791809f 100644 --- a/voxygen/anim/src/biped_large/mod.rs +++ b/voxygen/anim/src/biped_large/mod.rs @@ -277,7 +277,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Blueoni, _) => (-1.0, 26.5), (Redoni, _) => (-1.0, 26.5), (Cultistwarlord, _) => (-1.0, 18.5), - (Cultistwarlock, _) => (-1.0, 18.5), + (Cultistwarlock, _) => (-1.0, 17.5), (Huskbrute, _) => (-1.0, 23.5), }, lower_torso: match (body.species, body.body_type) { @@ -301,7 +301,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Blueoni, _) => (0.0, -8.5), (Redoni, _) => (0.0, -8.5), (Cultistwarlord, _) => (0.0, -1.5), - (Cultistwarlock, _) => (1.0, -3.5), + (Cultistwarlock, _) => (1.0, -2.5), (Huskbrute, _) => (-0.5, -7.0), }, tail: match (body.species, body.body_type) { diff --git a/world/src/layer/wildlife.rs b/world/src/layer/wildlife.rs index d09e3412d5..df7c579196 100644 --- a/world/src/layer/wildlife.rs +++ b/world/src/layer/wildlife.rs @@ -119,9 +119,14 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( Entry { make_entity: |pos, rng| { EntityInfo::at(pos) - .with_body(match rng.gen_range(0..2) { + .with_body(match rng.gen_range(0..3) { 0 => biped_large::Body::random_with(rng, &biped_large::Species::Wendigo) .into(), + 1 => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Mammoth, + ) + .into(), _ => biped_large::Body::random_with( rng, &biped_large::Species::Mountaintroll, @@ -133,7 +138,7 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( group_size: 1..2, is_underwater: false, day_period: vec![Night, Morning, Noon, Evening], - get_density: |c, _col| close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * 0.1, + get_density: |c, _col| close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * 0.15, }, // Tundra rock solitary ennemies Entry { @@ -312,7 +317,7 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( Entry { make_entity: |pos, rng| { EntityInfo::at(pos) - .with_body(match rng.gen_range(0..12) { + .with_body(match rng.gen_range(0..14) { 0 => quadruped_medium::Body::random_with( rng, &quadruped_medium::Species::Deer, @@ -364,6 +369,16 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( quadruped_small::Body::random_with(rng, &quadruped_small::Species::Goat) .into() }, + 11 => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Llama, + ) + .into(), + 12 => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Alpaca, + ) + .into(), _ => bird_medium::Body::random_with(rng, &bird_medium::Species::Chicken) .into(), }) @@ -832,7 +847,7 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( Entry { make_entity: |pos, rng| { EntityInfo::at(pos) - .with_body(match rng.gen_range(0..3) { + .with_body(match rng.gen_range(0..4) { 0 => quadruped_medium::Body::random_with( rng, &quadruped_medium::Species::Bonerattler, @@ -841,6 +856,11 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( 1 => { theropod::Body::random_with(rng, &theropod::Species::Sandraptor).into() }, + 2 => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Ngoubou, + ) + .into(), _ => quadruped_low::Body::random_with( rng, &quadruped_low::Species::Sandshark, From 19979413d5248816c08429ba3404a692c0eddb85 Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Thu, 24 Jun 2021 23:03:24 -0400 Subject: [PATCH 172/180] Convert dungeon lights to CSG, tweak pillars, convert the magic circle from lava to glowing rock, make enemies spawn in dungeons, and fix clippy warnings. --- common/net/src/msg/compression.rs | 2 +- common/src/terrain/block.rs | 4 +- world/src/site/mod.rs | 3 +- world/src/site2/gen.rs | 38 ++++ world/src/site2/mod.rs | 15 +- world/src/site2/plot/dungeon.rs | 348 +++++++++++++----------------- 6 files changed, 204 insertions(+), 206 deletions(-) diff --git a/common/net/src/msg/compression.rs b/common/net/src/msg/compression.rs index f48f22fb63..0fd0f89276 100644 --- a/common/net/src/msg/compression.rs +++ b/common/net/src/msg/compression.rs @@ -557,7 +557,7 @@ impl VoxelImageDecoding for TriPngEncoding Rgb { r: 0, g: 0, b: 0 }, + Air | Water | Lava | GlowingRock => Rgb { r: 0, g: 0, b: 0 }, Rock => Rgb { r: 93, g: 110, diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 4327137fda..69fc6e21f3 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -37,6 +37,7 @@ make_case_elim!( Rock = 0x10, WeakRock = 0x11, // Explodable Lava = 0x12, + GlowingRock = 0x13, // 0x12 <= x < 0x20 is reserved for future rocks Grass = 0x20, // Note: *not* the same as grass sprites Snow = 0x21, @@ -181,7 +182,7 @@ impl Block { #[inline] pub fn get_glow(&self) -> Option { - if matches!(self.kind, BlockKind::Lava) { + if matches!(self.kind, BlockKind::Lava | BlockKind::GlowingRock) { return Some(24); } match self.get_sprite()? { @@ -248,6 +249,7 @@ impl Block { BlockKind::Grass => Some(0.5), BlockKind::WeakRock => Some(0.75), BlockKind::Snow => Some(0.1), + BlockKind::Lava => None, _ => self.get_sprite().and_then(|sprite| match sprite { SpriteKind::Anvil | SpriteKind::Cauldron diff --git a/world/src/site/mod.rs b/world/src/site/mod.rs index 47a425a050..59511a52cf 100644 --- a/world/src/site/mod.rs +++ b/world/src/site/mod.rs @@ -154,8 +154,7 @@ impl Site { }; s.apply_supplement(dynamic_rng, wpos2d, get_column, supplement, economy) }, - SiteKind::Dungeon(d) => {}, /* d.apply_supplement(dynamic_rng, wpos2d, get_column, */ - // supplement), + SiteKind::Dungeon(d) => d.apply_supplement(dynamic_rng, wpos2d, supplement), SiteKind::Castle(c) => c.apply_supplement(dynamic_rng, wpos2d, get_column, supplement), SiteKind::Refactor(_) => {}, SiteKind::Tree(_) => {}, diff --git a/world/src/site2/gen.rs b/world/src/site2/gen.rs index 75e707388e..879876cb14 100644 --- a/world/src/site2/gen.rs +++ b/world/src/site2/gen.rs @@ -42,6 +42,7 @@ pub enum Primitive { // Operators Rotate(Id, Mat3), Translate(Id, Vec3), + Scale(Id, Vec3), } #[derive(Clone)] @@ -139,6 +140,15 @@ impl Fill { Primitive::Translate(prim, vec) => { self.contains_at(tree, *prim, pos.map2(*vec, i32::saturating_sub)) }, + Primitive::Scale(prim, vec) => { + let center = + self.get_bounds(tree, *prim).center().as_::() - Vec3::broadcast(0.5); + let fpos = pos.as_::(); + let spos = (center + ((center - fpos) / vec)) + .map(|x| x.round()) + .as_::(); + self.contains_at(tree, *prim, spos) + }, } } @@ -240,6 +250,14 @@ impl Fill { max: aabb.max.map2(*vec, i32::saturating_add), } }, + Primitive::Scale(prim, vec) => { + let aabb = self.get_bounds_inner(tree, *prim)?; + let center = aabb.center(); + Aabb { + min: center + ((aabb.min - center).as_::() * vec).as_::(), + max: center + ((aabb.max - center).as_::() * vec).as_::(), + } + }, }) } @@ -265,3 +283,23 @@ pub trait Structure { (tree, fills) } } + +#[allow(dead_code)] +/// Just the corners of an AABB, good for outlining stuff when debugging +pub fn aabb_corners Id>( + prim: &mut F, + aabb: Aabb, +) -> Id { + let f = |prim: &mut F, ret, vec| { + let sub = prim(Primitive::Aabb(Aabb { + min: aabb.min + vec, + max: aabb.max - vec, + })); + prim(Primitive::Diff(ret, sub)) + }; + let mut ret = prim(Primitive::Aabb(aabb)); + ret = f(prim, ret, Vec3::new(1, 0, 0)); + ret = f(prim, ret, Vec3::new(0, 1, 0)); + ret = f(prim, ret, Vec3::new(0, 0, 1)); + ret +} diff --git a/world/src/site2/mod.rs b/world/src/site2/mod.rs index e92131321c..02044fde95 100644 --- a/world/src/site2/mod.rs +++ b/world/src/site2/mod.rs @@ -318,7 +318,7 @@ impl Site { }); site.blit_aabr(aabr, Tile { - kind: TileKind::Plaza, + kind: TileKind::Empty, plot: Some(plot), }); @@ -782,6 +782,19 @@ impl Site { } } } + + pub fn apply_supplement( + &self, + dynamic_rng: &mut impl Rng, + wpos2d: Vec2, + supplement: &mut crate::ChunkSupplement, + ) { + for (_, plot) in self.plots.iter() { + if let PlotKind::Dungeon(d) = &plot.kind { + d.apply_supplement(dynamic_rng, wpos2d, supplement); + } + } + } } pub fn test_site() -> Site { Site::generate_city(&Land::empty(), &mut thread_rng(), Vec2::zero()) } diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index 36448dedc0..e14c6edb00 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -200,7 +200,6 @@ impl Dungeon { // NOTE: Used only for dynamic elements like chests and entities! dynamic_rng: &mut impl Rng, wpos2d: Vec2, - _get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, supplement: &mut ChunkSupplement, ) { let rpos = wpos2d - self.origin; @@ -1175,15 +1174,9 @@ pub fn spiral_staircase( if (pos.xy().magnitude_squared() as f32) < inner_radius.powi(2) { true } else if (pos.xy().magnitude_squared() as f32) < radius.powi(2) { - if ((pos.x as f32).atan2(pos.y as f32) / (f32::consts::PI * 2.0) * stretch - + pos.z as f32) + ((pos.x as f32).atan2(pos.y as f32) / (f32::consts::PI * 2.0) * stretch + pos.z as f32) .rem_euclid(stretch) < 1.5 - { - true - } else { - false - } } else { false } @@ -1198,15 +1191,9 @@ pub fn wall_staircase( Box::new(move |pos: Vec3| { let pos = pos - origin; if (pos.x.abs().max(pos.y.abs())) as f32 > 0.6 * radius { - if ((pos.x as f32).atan2(pos.y as f32) / (f32::consts::PI * 2.0) * stretch - + pos.z as f32) + ((pos.x as f32).atan2(pos.y as f32) / (f32::consts::PI * 2.0) * stretch + pos.z as f32) .rem_euclid(stretch) < 1.0 - { - true - } else { - false - } } else { false } @@ -1237,6 +1224,25 @@ pub fn inscribed_polystar( }) } +pub fn make_wall_contours( + tiles: Arc>, + floor_corner: Vec2, + floor_z: i32, + wall_thickness: f32, + tunnel_height: f32, +) -> Box) -> bool> { + Box::new(move |pos| { + let rpos = pos.xy() - floor_corner; + let dist_to_wall = tilegrid_nearest_wall(&tiles, rpos) + .map(|nearest| (nearest.distance_squared(rpos) as f32).sqrt()) + .unwrap_or(TILE_SIZE as f32); + let tunnel_dist = + 1.0 - (dist_to_wall - wall_thickness).max(0.0) / (TILE_SIZE as f32 - wall_thickness); + dist_to_wall < wall_thickness + || ((pos.z - floor_z) as f32) >= tunnel_height * (1.0 - tunnel_dist.powi(4)) + }) +} + impl Floor { fn render Id, G: FnMut(Id, Fill)>( &self, @@ -1246,32 +1252,24 @@ impl Floor { floor_z: i32, ) { let floor_corner = dungeon.origin + TILE_SIZE * self.tile_offset; - let floor_aabb = prim(Primitive::Aabb(Aabb { + let floor_aabb = Aabb { min: floor_corner.with_z(floor_z), max: (floor_corner + TILE_SIZE * self.tiles.size()) .with_z(floor_z + self.total_depth()), - })); - //let rpos = pos - self.tile_offset * TILE_SIZE; - //let tile_pos = rpos.map(|e| e.div_euclid(TILE_SIZE)); - //let tile_center = tile_pos * TILE_SIZE + TILE_SIZE / 2; - //let rtile_pos = rpos - tile_center; + }; + let floor_prim = prim(Primitive::Aabb(floor_aabb)); let vacant = Block::air(SpriteKind::Empty); - let stone_red = Block::new(BlockKind::Rock, Rgb::new(255, 0, 0)); - //let stone_orange = Block::new(BlockKind::Rock, Rgb::new(255, 128, 0)); - let stone_purple = Block::new(BlockKind::Rock, Rgb::new(96, 0, 128)); - //let stone_green = Block::new(BlockKind::Rock, Rgb::new(0, 255, 0)); - //let stone_cyan = Block::new(BlockKind::Rock, Rgb::new(0, 255, 255)); - //let stone_blue = Block::new(BlockKind::Rock, Rgb::new(0, 0, 255)); - //let colors = &index.colors.site.dungeon; + let stone = Block::new(BlockKind::Rock, Rgb::new(150, 150, 175)); + let stone_purple = Block::new(BlockKind::GlowingRock, Rgb::new(96, 0, 128)); let floor_sprite = prim(Primitive::Sampling( - floor_aabb, - Box::new(|pos| RandomField::new(7331).chance(Vec3::from(pos), 0.001)), + floor_prim, + Box::new(|pos| RandomField::new(7331).chance(pos, 0.001)), )); let floor_sprite_fill = Fill::Sampling(Arc::new(|pos| { Some(Block::air( - match (RandomField::new(1337).get(Vec3::from(pos)) / 2) % 30 { + match (RandomField::new(1337).get(pos) / 2) % 30 { 0 => SpriteKind::Apple, 1 => SpriteKind::VeloriteFrag, 2 => SpriteKind::Velorite, @@ -1282,21 +1280,6 @@ impl Floor { )) })); - let aabb_edges = |prim: &mut F, aabb: Aabb<_>| { - let f = |prim: &mut F, ret, vec| { - let sub = prim(Primitive::Aabb(Aabb { - min: aabb.min + vec, - max: aabb.max - vec, - })); - prim(Primitive::Diff(ret, sub)) - }; - let mut ret = prim(Primitive::Aabb(aabb)); - ret = f(prim, ret, Vec3::new(1, 0, 0)); - ret = f(prim, ret, Vec3::new(0, 1, 0)); - ret = f(prim, ret, Vec3::new(0, 0, 1)); - ret - }; - fn aabr_with_z(aabr: Aabr, z: std::ops::Range) -> Aabb { Aabb { min: aabr.min.with_z(z.start), @@ -1309,40 +1292,65 @@ impl Floor { let pillar_thickness: i32 = 4; let tiles = Arc::new(self.tiles.clone()); - let wall_contours = prim(Primitive::Sampling(floor_aabb, { + let wall_contours = prim(Primitive::Sampling(floor_prim, { let tiles = Arc::clone(&tiles); - Box::new(move |pos| { - let rpos = pos.xy() - floor_corner; - let dist_to_wall = tilegrid_nearest_wall(&tiles, rpos) - .map(|nearest| (nearest.distance_squared(rpos) as f32).sqrt()) - .unwrap_or(TILE_SIZE as f32); - let tunnel_dist = 1.0 - - (dist_to_wall - wall_thickness).max(0.0) - / (TILE_SIZE as f32 - wall_thickness); - dist_to_wall >= wall_thickness - && ((pos.z - floor_z) as f32) < tunnel_height * (1.0 - tunnel_dist.powi(4)) - }) + make_wall_contours(tiles, floor_corner, floor_z, wall_thickness, tunnel_height) })); - let sconces_fill = Fill::Sampling(Arc::new(move |pos| { + let wall_contour_surface = prim(Primitive::Sampling(floor_prim, { + let tiles = Arc::clone(&tiles); + make_wall_contours( + tiles, + floor_corner, + floor_z, + wall_thickness + 1.0, + tunnel_height - 1.0, + ) + })); + + let light_offset: i32 = 7; + let sconces_wall = Fill::Sampling(Arc::new(move |pos| { + let rpos = pos.xy() - floor_corner; + let nearest = tilegrid_nearest_wall(&tiles, rpos); + let ori = Floor::relative_ori(rpos, nearest.unwrap_or_default()); + Block::air(SpriteKind::WallSconce).with_ori(ori) + })); + let sconces_inward = Fill::Sampling(Arc::new(move |pos| { let rpos = pos.xy() - floor_corner; let tile_pos = rpos.map(|e| e.div_euclid(TILE_SIZE)); let tile_center = tile_pos * TILE_SIZE + TILE_SIZE / 2; - let rtile_pos = rpos - tile_center; - let nearest = tilegrid_nearest_wall(&tiles, rpos); - let dist_to_wall = nearest - .map(|nearest| (nearest.distance_squared(rpos) as f32).sqrt()) - .unwrap_or(TILE_SIZE as f32); - let ori = Floor::relative_ori(rpos, nearest.unwrap_or_default()); - let light_offset: i32 = 7; - if (dist_to_wall - wall_thickness) as i32 == 1 - && rtile_pos.map(|e| e % light_offset == 0).reduce_bitxor() - { - Block::air(SpriteKind::WallSconce).with_ori(ori) - } else { - None - } + let ori = Floor::relative_ori(rpos, tile_center); + Block::air(SpriteKind::WallSconce).with_ori(ori) })); + let sconces_outward = Fill::Sampling(Arc::new(move |pos| { + let rpos = pos.xy() - floor_corner; + let tile_pos = rpos.map(|e| e.div_euclid(TILE_SIZE)); + let tile_center = tile_pos * TILE_SIZE + TILE_SIZE / 2; + let ori = Floor::relative_ori(tile_center, rpos); + Block::air(SpriteKind::WallSconce).with_ori(ori) + })); + + let mut lighting_mask_x = prim(Primitive::Empty); + let floor_w = floor_aabb.max.x - floor_aabb.min.x; + for i in 0..floor_w / light_offset { + let j = floor_corner.x + i * TILE_SIZE + light_offset; + let plane = prim(Primitive::Aabb(Aabb { + min: floor_aabb.min.with_x(j - 1), + max: floor_aabb.max.with_x(j), + })); + lighting_mask_x = prim(Primitive::Or(plane, lighting_mask_x)); + } + let mut lighting_mask_y = prim(Primitive::Empty); + let floor_h = floor_aabb.max.y - floor_aabb.min.y; + for i in 0..floor_h / light_offset { + let j = floor_corner.y + i * TILE_SIZE + light_offset; + let plane = prim(Primitive::Aabb(Aabb { + min: floor_aabb.min.with_y(j - 1), + max: floor_aabb.max.with_y(j), + })); + lighting_mask_y = prim(Primitive::Or(plane, lighting_mask_y)); + } + let lighting_mask = prim(Primitive::Xor(lighting_mask_x, lighting_mask_y)); let mut stairs_bb = Vec::new(); let mut stairs = Vec::new(); @@ -1355,7 +1363,7 @@ impl Floor { min: tile_corner, max: tile_corner + Vec2::broadcast(TILE_SIZE), }; - let tile_center = tile_corner + Vec2::broadcast(TILE_SIZE/2); + let tile_center = tile_corner + Vec2::broadcast(TILE_SIZE / 2); let (mut height, room) = match tile { Tile::UpStair(room, kind) => { let center = tile_center.with_z(floor_z); @@ -1369,8 +1377,25 @@ impl Floor { StairsKind::Spiral => spiral_staircase(center, radius, 0.5, 9.0), StairsKind::WallSpiral => wall_staircase(center, radius, 27.0), })); + let mut lights = prim(Primitive::Empty); + for i in self.hollow_depth..self.total_depth() { + if i % 9 == 0 { + let mut light = prim(Primitive::Aabb(Aabb { + min: aabb.min.with_z(floor_z + i), + max: aabb.max.with_z(floor_z + i + 1), + })); + let inner = prim(Primitive::Aabb(Aabb { + min: (aabb.min + Vec3::new(1, 1, 0)).with_z(floor_z + i), + max: (aabb.max - Vec3::new(1, 1, 0)).with_z(floor_z + i + 1), + })); + + light = prim(Primitive::Diff(light, inner)); + lights = prim(Primitive::Or(light, lights)); + } + } + lights = prim(Primitive::And(lights, lighting_mask)); stairs_bb.push(bb); - stairs.push(stair); + stairs.push((stair, lights)); (self.hollow_depth, Some(room)) }, Tile::DownStair(room) => (self.hollow_depth, Some(room)), @@ -1383,7 +1408,14 @@ impl Floor { tile_aabr, floor_z..floor_z + 1, ))); - let sprite_layer = prim(Primitive::And(sprite_layer, wall_contours)); + + let lighting_plane = prim(Primitive::Aabb(aabr_with_z( + tile_aabr, + floor_z + 1..floor_z + 2, + ))); + let lighting_plane = prim(Primitive::And(lighting_plane, lighting_mask)); + + let sprite_layer = prim(Primitive::Diff(sprite_layer, wall_contours)); let mut chests = None; @@ -1395,9 +1427,7 @@ impl Floor { let difficulty = room.difficulty; let chest_sprite = prim(Primitive::Sampling( sprite_layer, - Box::new(move |pos| { - RandomField::new(seed).chance(Vec3::from(pos), loot_density * 0.5) - }), + Box::new(move |pos| RandomField::new(seed).chance(pos, loot_density * 0.5)), )); let chest_sprite_fill = Fill::Block(Block::air(match difficulty { 0 => SpriteKind::DungeonChest0, @@ -1418,12 +1448,33 @@ impl Floor { .reduce_and() }) .unwrap_or(false) + && DIRS + .iter() + .map(|dir| tile_pos + *dir) + .all(|other_tile_pos| { + matches!(self.tiles.get(other_tile_pos), Some(Tile::Room(_))) + }) { - let pillar = prim(Primitive::Cylinder(Aabb { - min: (tile_center - Vec2::broadcast(pillar_thickness)).with_z(floor_z), - max: (tile_center + Vec2::broadcast(pillar_thickness)).with_z(floor_z + height) + let mut pillar = prim(Primitive::Cylinder(Aabb { + min: (tile_center - Vec2::broadcast(pillar_thickness - 1)) + .with_z(floor_z), + max: (tile_center + Vec2::broadcast(pillar_thickness)) + .with_z(floor_z + height), })); - pillars.push((tile_center, pillar)); + let base = prim(Primitive::Cylinder(Aabb { + min: (tile_center - Vec2::broadcast(1 + pillar_thickness - 1)) + .with_z(floor_z), + max: (tile_center + Vec2::broadcast(1 + pillar_thickness)) + .with_z(floor_z + 3), + })); + + let scale = (pillar_thickness + 2) as f32 / pillar_thickness as f32; + let mut lights = + prim(Primitive::Scale(pillar, Vec2::broadcast(scale).with_z(1.0))); + lights = prim(Primitive::And(lighting_plane, lights)); + lights = prim(Primitive::Translate(lights, 3 * Vec3::unit_z())); + pillar = prim(Primitive::Or(pillar, base)); + pillars.push((tile_center, pillar, lights)); } } @@ -1436,21 +1487,19 @@ impl Floor { tile_aabr, floor_z..floor_z + height, ))); - let tile_air = prim(Primitive::And(tile_air, wall_contours)); + let tile_air = prim(Primitive::Diff(tile_air, wall_contours)); fill(tile_air, Fill::Block(vacant)); - let sconces_layer = prim(Primitive::And(tile_air, sprite_layer)); - fill(sconces_layer, sconces_fill.clone()); + let sconces_layer = prim(Primitive::And(tile_air, lighting_plane)); + let sconces_layer = prim(Primitive::And(sconces_layer, wall_contour_surface)); + fill(sconces_layer, sconces_wall.clone()); if let Some((chest_sprite, chest_sprite_fill)) = chests { - let chest_sprite = prim(Primitive::And(chest_sprite, wall_contours)); + let chest_sprite = prim(Primitive::Diff(chest_sprite, wall_contours)); fill(chest_sprite, chest_sprite_fill); } - let tile_edges = - aabb_edges(&mut prim, aabr_with_z(tile_aabr, floor_z..floor_z + height)); let floor_sprite = prim(Primitive::And(sprite_layer, floor_sprite)); fill(floor_sprite, floor_sprite_fill.clone()); - //fill(tile_edges, Fill::Block(Block::air(SpriteKind::Lantern))); } if let Some(boss_room_center) = boss_room_center { @@ -1465,132 +1514,29 @@ impl Floor { fill(magic_circle, Fill::Block(stone_purple)); } - for (pos, pillar) in pillars.iter() { + for (pos, pillar, lights) in pillars.iter() { if let Some(boss_room_center) = boss_room_center { if pos.distance_squared(boss_room_center) < (2 * TILE_SIZE).pow(2) { - continue + continue; } } - //fill(*pillar, Fill::Block(Block::new(BlockKind::Lava, [0, 0, 0]))); - fill(*pillar, Fill::Block(stone_red)); + fill(*lights, sconces_inward.clone()); + fill(*pillar, Fill::Block(stone)); } for stair_bb in stairs_bb.iter() { fill(*stair_bb, Fill::Block(vacant)); } - for stair in stairs.iter() { - fill(*stair, Fill::Block(stone_red)); + for (stair, lights) in stairs.iter() { + fill(*lights, sconces_outward.clone()); + fill(*stair, Fill::Block(stone)); } - /* - move |z| match self.tiles.get(tile_pos) { - Some(Tile::Solid) => BlockMask::nothing(), - Some(Tile::Tunnel) => { - let light_offset: i32 = 7; - if (dist_to_wall - wall_thickness) as i32 == 1 - && rtile_pos.map(|e| e % light_offset == 0).reduce_bitxor() - && z == 1 - { - let ori = - Floor::relative_ori(rpos, self.nearest_wall(rpos).unwrap_or_default()); - let furniture = SpriteKind::WallSconce; - BlockMask::new(Block::air(furniture).with_ori(ori).unwrap(), 1) - } else if dist_to_wall >= wall_thickness - && (z as f32) < tunnel_height * (1.0 - tunnel_dist.powi(4)) - { - if z == 0 { floor_sprite } else { vacant } - } else { - BlockMask::nothing() - } - }, - Some(Tile::Room(room)) | Some(Tile::DownStair(room)) - if dist_to_wall < wall_thickness - || z as f32 - >= self.rooms[*room].height as f32 * (1.0 - tunnel_dist.powi(4)) => - { - BlockMask::nothing() - }, - - Some(Tile::Room(room)) | Some(Tile::DownStair(room)) - if self.rooms[*room] - .pillars - .map(|pillar_space| { - tile_pos - .map(|e| e.rem_euclid(pillar_space) == 0) - .reduce_and() - && rtile_pos.map(|e| e as f32).magnitude_squared() - < (pillar_thickness as f32 + 0.5).powi(2) - }) - .unwrap_or(false) => - { - if z == 1 && rtile_pos.product() == 0 && rtile_pos.sum().abs() == pillar_thickness { - let ori = Floor::relative_ori(rtile_pos, Vec2::zero()); - let furniture = SpriteKind::WallSconce; - BlockMask::new(Block::air(furniture).with_ori(ori).unwrap(), 1) - } else if z < self.rooms[*room].height - && rtile_pos.map(|e| e as f32).magnitude_squared() - > (pillar_thickness as f32 - 0.5).powi(2) - { - vacant - } else { - BlockMask::nothing() - } - } - - Some(Tile::Room(_)) => { - let light_offset = 7; - if z == 0 { - floor_sprite - } else if dist_to_wall as i32 == 4 - && rtile_pos.map(|e| e % light_offset == 0).reduce_bitxor() - && z == 1 - { - let ori = Floor::relative_ori( - rpos, - self.nearest_wall(rpos).unwrap_or_else(Vec2::zero), - ); - let furniture = SpriteKind::WallSconce; - BlockMask::new(Block::air(furniture).with_ori(ori).unwrap(), 1) - } else { - vacant - } - }, - Some(Tile::DownStair(_)) => vacant, - Some(Tile::UpStair(room, kind)) => { - let inner_radius: f32 = 0.5; - let stretch = 9; - let block = make_staircase( - kind, - Vec3::new(rtile_pos.x, rtile_pos.y, z), - TILE_SIZE as f32 / 2.0, - inner_radius, - stretch as f32, - self.total_depth(), - ); - let furniture = SpriteKind::WallSconce; - let ori = Floor::relative_ori(Vec2::zero(), rtile_pos); - if z < self.rooms[*room].height { - block.resolve_with(vacant) - } else if z % stretch == 0 && rtile_pos.x == 0 && rtile_pos.y == -TILE_SIZE / 2 { - BlockMask::new(Block::air(furniture).with_ori(ori).unwrap(), 1) - } else { - make_staircase( - kind, - Vec3::new(rtile_pos.x, rtile_pos.y, z), - TILE_SIZE as f32 / 2.0, - inner_radius, - stretch as f32, - self.total_depth(), - ) - } - }, - None => BlockMask::nothing(), - }*/ } } impl SiteStructure for Dungeon { fn render Id, G: FnMut(Id, Fill)>( &self, - site: &site2::Site, + _site: &site2::Site, mut prim: F, mut fill: G, ) { From 6af988b8065ab0d25ccd86a2bc343857a00df1c2 Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Thu, 24 Jun 2021 23:21:49 -0400 Subject: [PATCH 173/180] Fix tests and examples to point to site2 dungeons. --- world/examples/dungeon_voxel_export.rs | 6 +++--- world/src/sim2/mod.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/world/examples/dungeon_voxel_export.rs b/world/examples/dungeon_voxel_export.rs index 3f0b58d783..426dc9a80b 100644 --- a/world/examples/dungeon_voxel_export.rs +++ b/world/examples/dungeon_voxel_export.rs @@ -10,7 +10,7 @@ use common::{ vol::{BaseVol, ReadVol, RectSizedVol, WriteVol}, }; use vek::{Vec2, Vec3}; -use veloren_world::{index::Index, IndexOwned}; +use veloren_world::{index::Index, IndexOwned, Land}; /// This exports a dungeon (structure only, no entities or sprites) to a /// MagicaVoxel .vox file @@ -22,9 +22,9 @@ fn main() -> Result { println!("Saving into {}", export_path); let mut volume = ExportVol::new(); let index = IndexOwned::new(Index::new(seed)); - let dungeon = veloren_world::site::Dungeon::generate( + let dungeon = veloren_world::site2::plot::Dungeon::generate( volume.size_xy().map(|p| p as i32 / 2), - None, + &Land::empty(), &mut rand::thread_rng(), ); dungeon.apply_to(index.as_index_ref(), Vec2::new(0, 0), |_| None, &mut volume); diff --git a/world/src/sim2/mod.rs b/world/src/sim2/mod.rs index 4c27fa849c..754e7f9e47 100644 --- a/world/src/sim2/mod.rs +++ b/world/src/sim2/mod.rs @@ -1252,7 +1252,7 @@ mod tests { crate::site::Castle::generate(wpos, None, &mut rng), ), common::terrain::site::SitesKind::Dungeon => crate::site::Site::dungeon( - crate::site::Dungeon::generate(wpos, None, &mut rng), + crate::site2::Site::generate_dungeon(&crate::Land::empty(), &mut rng, wpos), ), // common::terrain::site::SitesKind::Settlement | _ => crate::site::Site::settlement(crate::site::Settlement::generate( From 7328b712431ccb530c89a5869e82d69e0bf0595a Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Fri, 25 Jun 2021 14:08:23 -0400 Subject: [PATCH 174/180] Add comments to dungeon code. Fix the radius calculation (which lead to dungeons getting truncated), and fix sprites spawning above staircases. --- Cargo.lock | 10 -- world/Cargo.toml | 2 +- world/src/site2/gen.rs | 7 ++ world/src/site2/mod.rs | 5 +- world/src/site2/plot/dungeon.rs | 177 +++++++++++++++++++++----------- 5 files changed, 127 insertions(+), 74 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 275329b5a3..43527b3c08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2492,15 +2492,6 @@ dependencies = [ "serde", ] -[[package]] -name = "inline_tweak" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7033e97b20277cc0d043226d1940fa7719ff08d2305d1fc7421e53066d00eb4b" -dependencies = [ - "lazy_static", -] - [[package]] name = "inotify" version = "0.7.1" @@ -6163,7 +6154,6 @@ dependencies = [ "fxhash", "hashbrown 0.11.2", "image", - "inline_tweak", "itertools 0.10.0", "lazy_static", "lz-fear", diff --git a/world/Cargo.toml b/world/Cargo.toml index 4ba4994243..7b8a288040 100644 --- a/world/Cargo.toml +++ b/world/Cargo.toml @@ -35,7 +35,7 @@ rayon = "1.5" serde = { version = "1.0.110", features = ["derive"] } ron = { version = "0.6", default-features = false } assets_manager = {version = "0.4.3", features = ["ron"]} -inline_tweak = "1.0.2" +# inline_tweak = "1.0.2" # compression benchmarks lz-fear = { version = "0.1.1", optional = true } diff --git a/world/src/site2/gen.rs b/world/src/site2/gen.rs index 879876cb14..b2192df466 100644 --- a/world/src/site2/gen.rs +++ b/world/src/site2/gen.rs @@ -283,6 +283,13 @@ pub trait Structure { (tree, fills) } } +/// Extend a 2d AABR to a 3d AABB +pub fn aabr_with_z(aabr: Aabr, z: std::ops::Range) -> Aabb { + Aabb { + min: aabr.min.with_z(z.start), + max: aabr.max.with_z(z.end), + } +} #[allow(dead_code)] /// Just the corners of an AABB, good for outlining stuff when debugging diff --git a/world/src/site2/mod.rs b/world/src/site2/mod.rs index 02044fde95..72348fed92 100644 --- a/world/src/site2/mod.rs +++ b/world/src/site2/mod.rs @@ -3,7 +3,7 @@ pub mod plot; mod tile; use self::{ - gen::{Fill, Primitive, Structure}, + gen::{aabr_with_z, Fill, Primitive, Structure}, plot::{Plot, PlotKind}, tile::{HazardKind, KeepKind, Ori, RoofKind, Tile, TileGrid, TileKind, TILE_SIZE}, }; @@ -302,8 +302,7 @@ impl Site { site.demarcate_obstacles(land); let dungeon = plot::Dungeon::generate(origin, land, &mut rng); site.name = dungeon.name().to_string(); - //let size = (2.0 + rng.gen::().powf(8.0) * 3.0).round() as i32; - let size = (dungeon.radius() / (2.0 * tile::TILE_SIZE as f32)).ceil() as i32; + let size = (dungeon.radius() / tile::TILE_SIZE as f32).ceil() as i32; let aabr = Aabr { min: Vec2::broadcast(-size), diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index e14c6edb00..46ad29a62c 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -3,7 +3,7 @@ use crate::{ block::block_from_structure, column::ColumnSample, site::{namegen::NameGen, BlockMask}, - site2::{self, Fill, Primitive, Structure as SiteStructure}, + site2::{self, aabr_with_z, Fill, Primitive, Structure as SiteStructure}, util::{attempt, Grid, RandomField, Sampler, CARDINALS, DIRS}, IndexRef, Land, }; @@ -109,7 +109,14 @@ impl Dungeon { pub fn get_origin(&self) -> Vec2 { self.origin } - pub fn radius(&self) -> f32 { 200.0 } + pub fn radius(&self) -> f32 { + self.floors + .iter() + .map(|floor| (TILE_SIZE * floor.tiles.size()).magnitude_squared()) + .max() + .map(|d| (d as f32).sqrt() / 2.0) + .unwrap_or(200.0) + } #[allow(clippy::needless_update)] // TODO: Pending review in #587 pub fn spawn_rules(&self, wpos: Vec2) -> SpawnRules { @@ -1251,6 +1258,7 @@ impl Floor { dungeon: &Dungeon, floor_z: i32, ) { + // Calculate an AABB and corner for the AABB that covers the current floor. let floor_corner = dungeon.origin + TILE_SIZE * self.tile_offset; let floor_aabb = Aabb { min: floor_corner.with_z(floor_z), @@ -1258,10 +1266,16 @@ impl Floor { .with_z(floor_z + self.total_depth()), }; let floor_prim = prim(Primitive::Aabb(floor_aabb)); + + // Declare the various kinds of blocks that will be used as fills let vacant = Block::air(SpriteKind::Empty); let stone = Block::new(BlockKind::Rock, Rgb::new(150, 150, 175)); let stone_purple = Block::new(BlockKind::GlowingRock, Rgb::new(96, 0, 128)); + // Sprites are randomly positioned and have random kinds, this primitive + // produces a box of dots that will later get truncated to just the + // floor, and the corresponding fill places the random kinds where the + // mask says to let floor_sprite = prim(Primitive::Sampling( floor_prim, Box::new(|pos| RandomField::new(7331).chance(pos, 0.001)), @@ -1280,23 +1294,24 @@ impl Floor { )) })); - fn aabr_with_z(aabr: Aabr, z: std::ops::Range) -> Aabb { - Aabb { - min: aabr.min.with_z(z.start), - max: aabr.max.with_z(z.end), - } - } - let wall_thickness = 3.0; let tunnel_height = if self.final_level { 16.0 } else { 8.0 }; let pillar_thickness: i32 = 4; + // Several primitives and fills use the tile information for finding the nearest + // wall, a copy of the tilegrid for the floor is stored in an Arc to + // avoid making a copy for each primitive let tiles = Arc::new(self.tiles.clone()); + + // The way the ceiling is curved around corners and near hallways is intricate + // enough that it's easiest to do with a sampling primitive, this gets + // masked per room so that it's more efficient to query let wall_contours = prim(Primitive::Sampling(floor_prim, { let tiles = Arc::clone(&tiles); make_wall_contours(tiles, floor_corner, floor_z, wall_thickness, tunnel_height) })); + // The surface 1 unit thicker than the walls is used to place the torches onto let wall_contour_surface = prim(Primitive::Sampling(floor_prim, { let tiles = Arc::clone(&tiles); make_wall_contours( @@ -1308,6 +1323,8 @@ impl Floor { ) })); + // The sconces use a sampling-based fill to orient them properly relative to the + // walls/staircases/pillars let light_offset: i32 = 7; let sconces_wall = Fill::Sampling(Arc::new(move |pos| { let rpos = pos.xy() - floor_corner; @@ -1330,33 +1347,43 @@ impl Floor { Block::air(SpriteKind::WallSconce).with_ori(ori) })); - let mut lighting_mask_x = prim(Primitive::Empty); - let floor_w = floor_aabb.max.x - floor_aabb.min.x; - for i in 0..floor_w / light_offset { - let j = floor_corner.x + i * TILE_SIZE + light_offset; - let plane = prim(Primitive::Aabb(Aabb { - min: floor_aabb.min.with_x(j - 1), - max: floor_aabb.max.with_x(j), - })); - lighting_mask_x = prim(Primitive::Or(plane, lighting_mask_x)); - } - let mut lighting_mask_y = prim(Primitive::Empty); - let floor_h = floor_aabb.max.y - floor_aabb.min.y; - for i in 0..floor_h / light_offset { - let j = floor_corner.y + i * TILE_SIZE + light_offset; - let plane = prim(Primitive::Aabb(Aabb { - min: floor_aabb.min.with_y(j - 1), - max: floor_aabb.max.with_y(j), - })); - lighting_mask_y = prim(Primitive::Or(plane, lighting_mask_y)); - } - let lighting_mask = prim(Primitive::Xor(lighting_mask_x, lighting_mask_y)); + // The lighting mask is a grid of thin AABB planes with the same period as the + // tile grid, but offset by `lighting_offset`, used to space the torches + // on the walls/pillars/staircases + let lighting_mask = { + let mut lighting_mask_x = prim(Primitive::Empty); + let floor_w = floor_aabb.max.x - floor_aabb.min.x; + for i in 0..floor_w / light_offset { + let j = floor_corner.x + i * TILE_SIZE + light_offset; + let plane = prim(Primitive::Aabb(Aabb { + min: floor_aabb.min.with_x(j - 1), + max: floor_aabb.max.with_x(j), + })); + lighting_mask_x = prim(Primitive::Or(plane, lighting_mask_x)); + } + let mut lighting_mask_y = prim(Primitive::Empty); + let floor_h = floor_aabb.max.y - floor_aabb.min.y; + for i in 0..floor_h / light_offset { + let j = floor_corner.y + i * TILE_SIZE + light_offset; + let plane = prim(Primitive::Aabb(Aabb { + min: floor_aabb.min.with_y(j - 1), + max: floor_aabb.max.with_y(j), + })); + lighting_mask_y = prim(Primitive::Or(plane, lighting_mask_y)); + } + prim(Primitive::Xor(lighting_mask_x, lighting_mask_y)) + }; + // Declare collections of various disjoint primitives that need postprocessing + // after handling all the local information per-tile let mut stairs_bb = Vec::new(); let mut stairs = Vec::new(); let mut pillars = Vec::new(); let mut boss_room_center = None; + let mut sprites = Vec::new(); + // This loop processes the tile grid, carving out rooms and tunnels and + // collecting stair/pillar/sprite info to place afterwards for (tile_pos, tile) in self.tiles.iter() { let tile_corner = dungeon.origin + TILE_SIZE * (self.tile_offset + tile_pos); let tile_aabr = Aabr { @@ -1365,7 +1392,35 @@ impl Floor { }; let tile_center = tile_corner + Vec2::broadcast(TILE_SIZE / 2); let (mut height, room) = match tile { - Tile::UpStair(room, kind) => { + Tile::UpStair(room, _) => (self.hollow_depth, Some(room)), + Tile::DownStair(room) => (self.hollow_depth, Some(room)), + Tile::Room(room) => (self.hollow_depth, Some(room)), + Tile::Tunnel => (tunnel_height as i32, None), + Tile::Solid => continue, + }; + + // Sprites are contained to the level above the floor, and not within walls + let sprite_layer = prim(Primitive::Aabb(aabr_with_z( + tile_aabr, + floor_z..floor_z + 1, + ))); + let sprite_layer = prim(Primitive::Diff(sprite_layer, wall_contours)); + + // Lights are 2 units above the floor, and aligned with the `lighting_mask` grid + let lighting_plane = prim(Primitive::Aabb(aabr_with_z( + tile_aabr, + floor_z + 1..floor_z + 2, + ))); + let lighting_plane = prim(Primitive::And(lighting_plane, lighting_mask)); + + let mut chests = None; + + if let Some(room) = room.map(|i| self.rooms.get(*i)) { + height = height.min(room.height); + if let Tile::UpStair(_, kind) = tile { + // Construct the staircase that connects this tile to the matching DownStair + // tile on the floor above (or to the surface if this is the top floor), and a + // hollow bounding box to place air in let center = tile_center.with_z(floor_z); let radius = TILE_SIZE as f32 / 2.0; let aabb = aabr_with_z(tile_aabr, floor_z..floor_z + self.total_depth()); @@ -1377,8 +1432,10 @@ impl Floor { StairsKind::Spiral => spiral_staircase(center, radius, 0.5, 9.0), StairsKind::WallSpiral => wall_staircase(center, radius, 27.0), })); + // Construct the lights that go inside the staircase, starting above the + // ceiling to avoid placing them floating in mid-air let mut lights = prim(Primitive::Empty); - for i in self.hollow_depth..self.total_depth() { + for i in height..self.total_depth() { if i % 9 == 0 { let mut light = prim(Primitive::Aabb(Aabb { min: aabb.min.with_z(floor_z + i), @@ -1396,35 +1453,13 @@ impl Floor { lights = prim(Primitive::And(lights, lighting_mask)); stairs_bb.push(bb); stairs.push((stair, lights)); - (self.hollow_depth, Some(room)) - }, - Tile::DownStair(room) => (self.hollow_depth, Some(room)), - Tile::Room(room) => (self.hollow_depth, Some(room)), - Tile::Tunnel => (tunnel_height as i32, None), - Tile::Solid => continue, - }; - - let sprite_layer = prim(Primitive::Aabb(aabr_with_z( - tile_aabr, - floor_z..floor_z + 1, - ))); - - let lighting_plane = prim(Primitive::Aabb(aabr_with_z( - tile_aabr, - floor_z + 1..floor_z + 2, - ))); - let lighting_plane = prim(Primitive::And(lighting_plane, lighting_mask)); - - let sprite_layer = prim(Primitive::Diff(sprite_layer, wall_contours)); - - let mut chests = None; - - if let Some(room) = room.map(|i| self.rooms.get(*i)) { - height = height.min(room.height); + } if matches!(tile, Tile::Room(_) | Tile::DownStair(_)) { let seed = room.seed; let loot_density = room.loot_density; let difficulty = room.difficulty; + // Place chests with a random distribution based on the room's loot density in + // valid sprite locations, filled based on the room's difficulty let chest_sprite = prim(Primitive::Sampling( sprite_layer, Box::new(move |pos| RandomField::new(seed).chance(pos, loot_density * 0.5)), @@ -1440,6 +1475,9 @@ impl Floor { })); chests = Some((chest_sprite, chest_sprite_fill)); + // If a room has pillars, the current tile aligns with the pillar spacing, and + // we're not too close to a wall (i.e. the adjacent tiles are rooms and not + // hallways/solid), place a pillar if room .pillars .map(|pillar_space| { @@ -1478,30 +1516,36 @@ impl Floor { } } + // Keep track of the boss room to be able to add decorations later if room.boss { boss_room_center = Some(floor_corner + TILE_SIZE * room.area.center()); } } + // Carve out the room's air inside the walls let tile_air = prim(Primitive::Aabb(aabr_with_z( tile_aabr, floor_z..floor_z + height, ))); let tile_air = prim(Primitive::Diff(tile_air, wall_contours)); fill(tile_air, Fill::Block(vacant)); + + // Place torches on the walls with the aforementioned spacing let sconces_layer = prim(Primitive::And(tile_air, lighting_plane)); let sconces_layer = prim(Primitive::And(sconces_layer, wall_contour_surface)); fill(sconces_layer, sconces_wall.clone()); + + // Defer chest/floor sprite placement if let Some((chest_sprite, chest_sprite_fill)) = chests { let chest_sprite = prim(Primitive::Diff(chest_sprite, wall_contours)); - fill(chest_sprite, chest_sprite_fill); + sprites.push((chest_sprite, chest_sprite_fill)); } let floor_sprite = prim(Primitive::And(sprite_layer, floor_sprite)); - - fill(floor_sprite, floor_sprite_fill.clone()); + sprites.push((floor_sprite, floor_sprite_fill.clone())); } + // Place a glowing purple septagonal star inscribed in a circle in the boss room if let Some(boss_room_center) = boss_room_center { let magic_circle_bb = prim(Primitive::Cylinder(Aabb { min: (boss_room_center - 3 * Vec2::broadcast(TILE_SIZE) / 2).with_z(floor_z - 1), @@ -1514,7 +1558,9 @@ impl Floor { fill(magic_circle, Fill::Block(stone_purple)); } + // Place pillars and pillar lights facing the pillars for (pos, pillar, lights) in pillars.iter() { + // Avoid placing pillars that would cover the septagonal star if let Some(boss_room_center) = boss_room_center { if pos.distance_squared(boss_room_center) < (2 * TILE_SIZE).pow(2) { continue; @@ -1523,13 +1569,24 @@ impl Floor { fill(*lights, sconces_inward.clone()); fill(*pillar, Fill::Block(stone)); } + // Carve out space for the stairs for stair_bb in stairs_bb.iter() { fill(*stair_bb, Fill::Block(vacant)); + // Prevent sprites from floating above the stairs + let stair_bb_up = prim(Primitive::Translate(*stair_bb, Vec3::unit_z())); + for (sprite, _) in sprites.iter_mut() { + *sprite = prim(Primitive::Diff(*sprite, stair_bb_up)); + } } + // Place the stairs themselves, and lights within the stairwells for (stair, lights) in stairs.iter() { fill(*lights, sconces_outward.clone()); fill(*stair, Fill::Block(stone)); } + // Place the sprites + for (sprite, sprite_fill) in sprites.into_iter() { + fill(sprite, sprite_fill); + } } } From 379e755006941a48f494a65c4dda65c8839d4352 Mon Sep 17 00:00:00 2001 From: Snowram Date: Fri, 25 Jun 2021 20:00:04 +0200 Subject: [PATCH 175/180] Increases base damage of husk attack, rename some cultist weapons --- .../abilities/custom/husk_brute/chargedmelee.ron | 4 ++-- assets/common/abilities/custom/minotaur/cleave.ron | 4 ++-- assets/common/entity/dungeon/tier-5/warlock.ron | 6 +++--- assets/common/entity/dungeon/tier-5/warlord.ron | 6 +++--- .../bow/bipedlarge-velorite.ron | 2 +- .../hammer/bipedlarge-cultist.ron} | 2 +- .../staff/bipedlarge-cultist.ron} | 2 +- .../sword/bipedlarge-cultist.ron | 2 +- .../common/skillset/dungeon/tier-5/sword_simple.ron | 13 ------------- assets/common/skillset/dungeon/tier-5/warlord.ron | 5 ----- assets/voxygen/voxel/biped_weapon_manifest.ron | 8 ++++---- common/src/comp/inventory/loadout_builder.rs | 4 ++-- 12 files changed, 20 insertions(+), 38 deletions(-) rename assets/common/items/{weapons => npc_weapons}/bow/bipedlarge-velorite.ron (90%) rename assets/common/items/{weapons/hammer/bipedlarge-cultist_purp_2h-0.ron => npc_weapons/hammer/bipedlarge-cultist.ron} (88%) rename assets/common/items/{weapons/staff/bipedlarge-cultist_staff.ron => npc_weapons/staff/bipedlarge-cultist.ron} (90%) rename assets/common/items/{weapons => npc_weapons}/sword/bipedlarge-cultist.ron (88%) delete mode 100644 assets/common/skillset/dungeon/tier-5/sword_simple.ron delete mode 100644 assets/common/skillset/dungeon/tier-5/warlord.ron diff --git a/assets/common/abilities/custom/husk_brute/chargedmelee.ron b/assets/common/abilities/custom/husk_brute/chargedmelee.ron index 94c388f71b..9b778e9af8 100644 --- a/assets/common/abilities/custom/husk_brute/chargedmelee.ron +++ b/assets/common/abilities/custom/husk_brute/chargedmelee.ron @@ -1,8 +1,8 @@ ChargedMelee( energy_cost: 0, energy_drain: 0, - initial_damage: 0, - scaled_damage: 500, + initial_damage: 50, + scaled_damage: 450, initial_poise_damage: 50, scaled_poise_damage: 150, initial_knockback: 0.0, diff --git a/assets/common/abilities/custom/minotaur/cleave.ron b/assets/common/abilities/custom/minotaur/cleave.ron index dff30a6fe4..3aa9a1b690 100644 --- a/assets/common/abilities/custom/minotaur/cleave.ron +++ b/assets/common/abilities/custom/minotaur/cleave.ron @@ -1,8 +1,8 @@ ChargedMelee( energy_cost: 0, energy_drain: 0, - initial_damage: 0, - scaled_damage: 500, + initial_damage: 50, + scaled_damage: 450, initial_poise_damage: 50, scaled_poise_damage: 150, initial_knockback: 0.0, diff --git a/assets/common/entity/dungeon/tier-5/warlock.ron b/assets/common/entity/dungeon/tier-5/warlock.ron index b6d9ae4608..369bd091cb 100644 --- a/assets/common/entity/dungeon/tier-5/warlock.ron +++ b/assets/common/entity/dungeon/tier-5/warlock.ron @@ -5,11 +5,11 @@ EntityConfig ( loot: Some(LootTable("common.loot_tables.dungeon.tier-5.enemy")), main_tool: Some(Choice([ - (1.5, Some(Item("common.items.weapons.staff.bipedlarge-cultist_staff"))), - (1.0, Some(Item("common.items.weapons.bow.bipedlarge-velorite"))), + (1.5, Some(Item("common.items.npc_weapons.staff.bipedlarge-cultist"))), + (1.0, Some(Item("common.items.npc_weapons.bow.bipedlarge-velorite"))), ])), second_tool: None, loadout_asset: None, - skillset_asset: Some("common.skillset.dungeon.tier-5.enemy"), + skillset_asset: None, ) diff --git a/assets/common/entity/dungeon/tier-5/warlord.ron b/assets/common/entity/dungeon/tier-5/warlord.ron index 01f7c29a71..fe2152e6e4 100644 --- a/assets/common/entity/dungeon/tier-5/warlord.ron +++ b/assets/common/entity/dungeon/tier-5/warlord.ron @@ -5,11 +5,11 @@ EntityConfig ( loot: Some(LootTable("common.loot_tables.dungeon.tier-5.enemy")), main_tool: Some(Choice([ - (1.5, Some(Item("common.items.weapons.sword.bipedlarge-cultist"))), - (1.0, Some(Item("common.items.weapons.hammer.bipedlarge-cultist_purp_2h-0"))), + (1.5, Some(Item("common.items.npc_weapons.sword.bipedlarge-cultist"))), + (1.0, Some(Item("common.items.npc_weapons.hammer.bipedlarge-cultist"))), ])), second_tool: None, loadout_asset: None, - skillset_asset: Some("common.skillset.dungeon.tier-5.warlord"), + skillset_asset: None, ) diff --git a/assets/common/items/weapons/bow/bipedlarge-velorite.ron b/assets/common/items/npc_weapons/bow/bipedlarge-velorite.ron similarity index 90% rename from assets/common/items/weapons/bow/bipedlarge-velorite.ron rename to assets/common/items/npc_weapons/bow/bipedlarge-velorite.ron index 800a221e8e..40c9d278dc 100644 --- a/assets/common/items/weapons/bow/bipedlarge-velorite.ron +++ b/assets/common/items/npc_weapons/bow/bipedlarge-velorite.ron @@ -1,5 +1,5 @@ ItemDef( - name: "Velorite Bow (bipedlarge)", + name: "Giant Velorite Bow", description: "Infused with Velorite power.", kind: Tool(( kind: Bow, diff --git a/assets/common/items/weapons/hammer/bipedlarge-cultist_purp_2h-0.ron b/assets/common/items/npc_weapons/hammer/bipedlarge-cultist.ron similarity index 88% rename from assets/common/items/weapons/hammer/bipedlarge-cultist_purp_2h-0.ron rename to assets/common/items/npc_weapons/hammer/bipedlarge-cultist.ron index 6161fa12a7..1493bb69a2 100644 --- a/assets/common/items/weapons/hammer/bipedlarge-cultist_purp_2h-0.ron +++ b/assets/common/items/npc_weapons/hammer/bipedlarge-cultist.ron @@ -1,5 +1,5 @@ ItemDef( - name: "Magical Cultist Warhammer (bipedlarge)", + name: "Giant Cultist Warhammer", description: "This belonged to an evil Cult Leader.", kind: Tool(( kind: Hammer, diff --git a/assets/common/items/weapons/staff/bipedlarge-cultist_staff.ron b/assets/common/items/npc_weapons/staff/bipedlarge-cultist.ron similarity index 90% rename from assets/common/items/weapons/staff/bipedlarge-cultist_staff.ron rename to assets/common/items/npc_weapons/staff/bipedlarge-cultist.ron index c709f9a11b..13139bd011 100644 --- a/assets/common/items/weapons/staff/bipedlarge-cultist_staff.ron +++ b/assets/common/items/npc_weapons/staff/bipedlarge-cultist.ron @@ -1,5 +1,5 @@ ItemDef( - name: "Cultist Staff (bipedlarge)", + name: "Giant Cultist Staff", description: "The fire gives off no heat.", kind: Tool(( kind: Staff, diff --git a/assets/common/items/weapons/sword/bipedlarge-cultist.ron b/assets/common/items/npc_weapons/sword/bipedlarge-cultist.ron similarity index 88% rename from assets/common/items/weapons/sword/bipedlarge-cultist.ron rename to assets/common/items/npc_weapons/sword/bipedlarge-cultist.ron index dd62980f60..06eae29d7a 100644 --- a/assets/common/items/weapons/sword/bipedlarge-cultist.ron +++ b/assets/common/items/npc_weapons/sword/bipedlarge-cultist.ron @@ -1,5 +1,5 @@ ItemDef( - name: "Magical Cultist Greatsword (bipedlarge)", + name: "Giant Cultist Greatsword", description: "This belonged to an evil Cult Leader.", kind: Tool(( kind: Sword, diff --git a/assets/common/skillset/dungeon/tier-5/sword_simple.ron b/assets/common/skillset/dungeon/tier-5/sword_simple.ron deleted file mode 100644 index 28d8bc547d..0000000000 --- a/assets/common/skillset/dungeon/tier-5/sword_simple.ron +++ /dev/null @@ -1,13 +0,0 @@ -([ - Group(Weapon(Sword)), - - // TripleStrike - Skill((Sword(TsCombo), None)), - Skill((Sword(TsDamage), Some(1))), - Skill((Sword(TsRegen), Some(1))), - - // Dash - Skill((Sword(DDamage), Some(1))), - Skill((Sword(DCost), Some(1))), - Skill((Sword(DDrain), Some(1))), -]) diff --git a/assets/common/skillset/dungeon/tier-5/warlord.ron b/assets/common/skillset/dungeon/tier-5/warlord.ron deleted file mode 100644 index 85c6edcab3..0000000000 --- a/assets/common/skillset/dungeon/tier-5/warlord.ron +++ /dev/null @@ -1,5 +0,0 @@ -([ - // Just gather everything - Tree("common.skillset.dungeon.tier-5.sword_simple"), - Tree("common.skillset.dungeon.tier-5.hammer"), -]) diff --git a/assets/voxygen/voxel/biped_weapon_manifest.ron b/assets/voxygen/voxel/biped_weapon_manifest.ron index 93ea6dc59f..04d8e11aea 100644 --- a/assets/voxygen/voxel/biped_weapon_manifest.ron +++ b/assets/voxygen/voxel/biped_weapon_manifest.ron @@ -1074,19 +1074,19 @@ vox_spec: ("weapon.hammer.2hhammer_oni_red", (-3.0, -4.5, -12.0)), color: None ), - "common.items.weapons.sword.bipedlarge-cultist": ( + "common.items.npc_weapons.sword.bipedlarge-cultist": ( vox_spec: ("weapon.sword.cultist", (-1.5, -4.0, -6.0)), color: None ), - "common.items.weapons.hammer.bipedlarge-cultist_purp_2h-0": ( + "common.items.npc_weapons.hammer.bipedlarge-cultist": ( vox_spec: ("weapon.hammer.cult_purp-0", (-3.5, -4.5, -5.0)), color: None ), - "common.items.weapons.bow.bipedlarge-velorite": ( + "common.items.npc_weapons.bow.bipedlarge-velorite": ( vox_spec: ("weapon.bow.velorite", (-1.5, -4.0, -15.0)), color: None ), - "common.items.weapons.staff.bipedlarge-cultist_staff": ( + "common.items.npc_weapons.staff.bipedlarge-cultist": ( vox_spec: ("weapon.staff.firestaff_cultist", (-2.5, -2.5, -4.0)), color: None ), diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index c3b6f5d3ff..2eb0a990d7 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -302,10 +302,10 @@ fn default_main_tool(body: &Body) -> Item { "common.items.npc_weapons.hammer.oni_red_hammer", )), (biped_large::Species::Cultistwarlord, _) => Some(Item::new_from_asset_expect( - "common.items.weapons.sword.bipedlarge-cultist", + "common.items.npc_weapons.sword.bipedlarge-cultist", )), (biped_large::Species::Cultistwarlock, _) => Some(Item::new_from_asset_expect( - "common.items.weapons.staff.bipedlarge-cultist_staff", + "common.items.npc_weapons.staff.bipedlarge-cultist", )), (biped_large::Species::Huskbrute, _) => Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.husk_brute", From 524dd02f4510b80ddad38b294774d2b2b9d2aba2 Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Fri, 25 Jun 2021 15:54:42 -0400 Subject: [PATCH 176/180] Tweaks to dungeon pillars. --- world/src/site2/plot/dungeon.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index 46ad29a62c..fd698b2141 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -441,7 +441,7 @@ impl Floor { boss: false, area, height: ctx.rng.gen_range(15..20), - pillars: Some(4), + pillars: Some(ctx.rng.gen_range(2..=4)), difficulty: self.difficulty, }), _ => self.create_room(Room { @@ -453,7 +453,7 @@ impl Floor { area, height: ctx.rng.gen_range(10..15), pillars: if ctx.rng.gen_range(0..4) == 0 { - Some(4) + Some(ctx.rng.gen_range(2..=4)) } else { None }, @@ -1510,15 +1510,19 @@ impl Floor { let mut lights = prim(Primitive::Scale(pillar, Vec2::broadcast(scale).with_z(1.0))); lights = prim(Primitive::And(lighting_plane, lights)); - lights = prim(Primitive::Translate(lights, 3 * Vec3::unit_z())); - pillar = prim(Primitive::Or(pillar, base)); + // Only add the base (and shift the lights up) for boss-room pillars + if room.boss { + lights = prim(Primitive::Translate(lights, 3 * Vec3::unit_z())); + pillar = prim(Primitive::Or(pillar, base)); + } pillars.push((tile_center, pillar, lights)); } } // Keep track of the boss room to be able to add decorations later if room.boss { - boss_room_center = Some(floor_corner + TILE_SIZE * room.area.center()); + boss_room_center = + Some(floor_corner + TILE_SIZE * room.area.center() + TILE_SIZE / 2); } } @@ -1562,7 +1566,7 @@ impl Floor { for (pos, pillar, lights) in pillars.iter() { // Avoid placing pillars that would cover the septagonal star if let Some(boss_room_center) = boss_room_center { - if pos.distance_squared(boss_room_center) < (2 * TILE_SIZE).pow(2) { + if pos.distance_squared(boss_room_center) < (3 * TILE_SIZE / 2).pow(2) { continue; } } From 0ac6305dfcbd726eab3931d5b161ed140d3de903 Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Fri, 25 Jun 2021 18:18:27 -0400 Subject: [PATCH 177/180] Rename difficulty to dungeon_difficulty. --- world/src/lib.rs | 2 +- world/src/site2/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/world/src/lib.rs b/world/src/lib.rs index 70479d03ee..1ed404ff76 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -144,7 +144,7 @@ impl World { civ::SiteKind::Settlement => world_msg::SiteKind::Town, civ::SiteKind::Dungeon => world_msg::SiteKind::Dungeon { difficulty: match site.site_tmp.map(|id| &index.sites[id].kind) { - Some(site::SiteKind::Dungeon(d)) => d.difficulty().unwrap_or(0), + Some(site::SiteKind::Dungeon(d)) => d.dungeon_difficulty().unwrap_or(0), _ => 0, }, }, diff --git a/world/src/site2/mod.rs b/world/src/site2/mod.rs index 72348fed92..73968d8934 100644 --- a/world/src/site2/mod.rs +++ b/world/src/site2/mod.rs @@ -278,7 +278,7 @@ impl Site { pub fn name(&self) -> &str { &self.name } - pub fn difficulty(&self) -> Option { + pub fn dungeon_difficulty(&self) -> Option { self.plots .iter() .filter_map(|(_, plot)| { From 8d0a1ad7b44df179da8fd1280f4225c8f6c1c45e Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sat, 26 Jun 2021 02:44:02 +0000 Subject: [PATCH 178/180] Gem Rings and small change to trading. --- CHANGELOG.md | 2 +- .../common/items/armor/misc/ring/amethyst.ron | 17 +++++++ .../common/items/armor/misc/ring/diamond.ron | 17 +++++++ .../common/items/armor/misc/ring/emerald.ron | 17 +++++++ assets/common/items/armor/misc/ring/gold.ron | 2 +- assets/common/items/armor/misc/ring/ruby.ron | 17 +++++++ .../common/items/armor/misc/ring/sapphire.ron | 17 +++++++ assets/common/items/armor/misc/ring/topaz.ron | 17 +++++++ .../loot_tables/food/wild_ingredients.ron | 1 + assets/common/loot_tables/trading.ron | 11 ++--- assets/common/recipe_book.ron | 48 +++++++++++++++++++ assets/server/manifests/kits.ron | 42 ++++++++++++++-- assets/voxygen/item_image_manifest.ron | 31 ++++++++++-- .../voxel/armor/misc/ring/amethyst.vox | 3 ++ .../voxygen/voxel/armor/misc/ring/diamond.vox | 3 ++ .../voxygen/voxel/armor/misc/ring/emerald.vox | 3 ++ assets/voxygen/voxel/armor/misc/ring/gold.vox | 4 +- assets/voxygen/voxel/armor/misc/ring/ruby.vox | 3 ++ .../voxel/armor/misc/ring/sapphire.vox | 3 ++ .../voxel/armor/misc/ring/scratched.vox | 4 +- .../voxygen/voxel/armor/misc/ring/topaz.vox | 3 ++ common/src/comp/inventory/trade_pricing.rs | 13 ++++- 22 files changed, 254 insertions(+), 24 deletions(-) create mode 100644 assets/common/items/armor/misc/ring/amethyst.ron create mode 100644 assets/common/items/armor/misc/ring/diamond.ron create mode 100644 assets/common/items/armor/misc/ring/emerald.ron create mode 100644 assets/common/items/armor/misc/ring/ruby.ron create mode 100644 assets/common/items/armor/misc/ring/sapphire.ron create mode 100644 assets/common/items/armor/misc/ring/topaz.ron create mode 100644 assets/voxygen/voxel/armor/misc/ring/amethyst.vox create mode 100644 assets/voxygen/voxel/armor/misc/ring/diamond.vox create mode 100644 assets/voxygen/voxel/armor/misc/ring/emerald.vox create mode 100644 assets/voxygen/voxel/armor/misc/ring/ruby.vox create mode 100644 assets/voxygen/voxel/armor/misc/ring/sapphire.vox create mode 100644 assets/voxygen/voxel/armor/misc/ring/topaz.vox diff --git a/CHANGELOG.md b/CHANGELOG.md index 31b7a1ec1d..ee02ecbc91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - NPCs and animals can now make sounds in response to certain events - Players can press H to greet others - Ability to toggle chat visibility - +- Added gem rings with various stat improvements. ### Changed - Entity-entity pushback is no longer applied in forced movement states like rolling and leaping. diff --git a/assets/common/items/armor/misc/ring/amethyst.ron b/assets/common/items/armor/misc/ring/amethyst.ron new file mode 100644 index 0000000000..5d72036ecf --- /dev/null +++ b/assets/common/items/armor/misc/ring/amethyst.ron @@ -0,0 +1,17 @@ +ItemDef( + name: "Amethyst Ring", + description: "A tin ring with an amethyst gem.", + kind: Armor(( + kind: Ring("Amethyst"), + stats: ( + protection: Normal(0.0), + poise_resilience: Normal(0.0), + energy_max: 0, + energy_reward: 0.025, + crit_power: 0.0, + stealth: 0.0, + ), + )), + quality: Moderate, + tags: [], +) \ No newline at end of file diff --git a/assets/common/items/armor/misc/ring/diamond.ron b/assets/common/items/armor/misc/ring/diamond.ron new file mode 100644 index 0000000000..a82fb13ea6 --- /dev/null +++ b/assets/common/items/armor/misc/ring/diamond.ron @@ -0,0 +1,17 @@ +ItemDef( + name: "Diamond Ring", + description: "A gold ring with an expensive diamond.", + kind: Armor(( + kind: Ring("Diamond"), + stats: ( + protection: Normal(0.0), + poise_resilience: Normal(0.0), + energy_max: 0, + energy_reward: -0.05, + crit_power: 0.1, + stealth: 0.0, + ), + )), + quality: Epic, + tags: [], +) \ No newline at end of file diff --git a/assets/common/items/armor/misc/ring/emerald.ron b/assets/common/items/armor/misc/ring/emerald.ron new file mode 100644 index 0000000000..16b7ba430c --- /dev/null +++ b/assets/common/items/armor/misc/ring/emerald.ron @@ -0,0 +1,17 @@ +ItemDef( + name: "Emerald Ring", + description: "A cobalt ring with an emerald gem.", + kind: Armor(( + kind: Ring("Emerald"), + stats: ( + protection: Normal(0.0), + poise_resilience: Normal(0.0), + energy_max: 100, + energy_reward: 0.0, + crit_power: -0.025, + stealth: 0.0, + ), + )), + quality: Moderate, + tags: [], +) \ No newline at end of file diff --git a/assets/common/items/armor/misc/ring/gold.ron b/assets/common/items/armor/misc/ring/gold.ron index 77bfc0c7d8..b8113d6b9c 100644 --- a/assets/common/items/armor/misc/ring/gold.ron +++ b/assets/common/items/armor/misc/ring/gold.ron @@ -1,6 +1,6 @@ ItemDef( name: "Gold Ring", - description: "Someone is surely missing it...", + description: "A plain gold ring... almost as if it is missing a gem.", kind: Armor(( kind: Ring("Gold"), stats: ( diff --git a/assets/common/items/armor/misc/ring/ruby.ron b/assets/common/items/armor/misc/ring/ruby.ron new file mode 100644 index 0000000000..9f2d8b147d --- /dev/null +++ b/assets/common/items/armor/misc/ring/ruby.ron @@ -0,0 +1,17 @@ +ItemDef( + name: "Ruby Ring", + description: "A silver ring with a ruby gem.", + kind: Armor(( + kind: Ring("Ruby"), + stats: ( + protection: Normal(0.0), + poise_resilience: Normal(0.0), + energy_max: -100, + energy_reward: 0.05, + crit_power: 0.0, + stealth: 0.0, + ), + )), + quality: High, + tags: [], +) \ No newline at end of file diff --git a/assets/common/items/armor/misc/ring/sapphire.ron b/assets/common/items/armor/misc/ring/sapphire.ron new file mode 100644 index 0000000000..8242516a44 --- /dev/null +++ b/assets/common/items/armor/misc/ring/sapphire.ron @@ -0,0 +1,17 @@ +ItemDef( + name: "Sapphire Ring", + description: "An iron ring with a sapphire gem.", + kind: Armor(( + kind: Ring("Sapphire"), + stats: ( + protection: Normal(0.0), + poise_resilience: Normal(0.0), + energy_max: 0, + energy_reward: 0.0, + crit_power: 0.05, + stealth: 0.0, + ), + )), + quality: Moderate, + tags: [], +) \ No newline at end of file diff --git a/assets/common/items/armor/misc/ring/topaz.ron b/assets/common/items/armor/misc/ring/topaz.ron new file mode 100644 index 0000000000..4156ca723a --- /dev/null +++ b/assets/common/items/armor/misc/ring/topaz.ron @@ -0,0 +1,17 @@ +ItemDef( + name: "Topaz Ring", + description: "A copper ring with a topaz gem.", + kind: Armor(( + kind: Ring("Topaz"), + stats: ( + protection: Normal(0.0), + poise_resilience: Normal(0.0), + energy_max: 50, + energy_reward: 0.00, + crit_power: 0.0, + stealth: 0.0, + ), + )), + quality: Moderate, + tags: [], +) \ No newline at end of file diff --git a/assets/common/loot_tables/food/wild_ingredients.ron b/assets/common/loot_tables/food/wild_ingredients.ron index cd99728eb1..3a915112bd 100644 --- a/assets/common/loot_tables/food/wild_ingredients.ron +++ b/assets/common/loot_tables/food/wild_ingredients.ron @@ -2,4 +2,5 @@ (1.0, Item("common.items.food.apple")), (1.0, Item("common.items.food.coconut")), (1.5, Item("common.items.food.mushroom")), + (1.0, Item("common.items.crafting_ing.honey")), ] diff --git a/assets/common/loot_tables/trading.ron b/assets/common/loot_tables/trading.ron index 559a023911..82679c07a7 100644 --- a/assets/common/loot_tables/trading.ron +++ b/assets/common/loot_tables/trading.ron @@ -2,11 +2,10 @@ // Please keep it sorting by rarity so it's easier to reason about things [ // Ores - // Uncomment when bug with crafting doesn't propagating can_sell will be fixed - // (0.03, Item("common.items.mineral.ore.gold")), - // (0.045, Item("common.items.mineral.ore.silver")), - // (0.1, Item("common.items.mineral.ore.bloodstone")), - // (0.2, Item("common.items.mineral.ore.cobalt")), + (0.03, Item("common.items.mineral.ore.gold")), + (0.045, Item("common.items.mineral.ore.silver")), + (0.1, Item("common.items.mineral.ore.bloodstone")), + (0.2, Item("common.items.mineral.ore.cobalt")), (0.25, Item("common.items.mineral.ore.coal")), (0.3, Item("common.items.mineral.ore.iron")), (0.5, Item("common.items.mineral.ore.velorite")), @@ -32,6 +31,4 @@ (1.0, Item("common.items.food.meat.beast_small_raw")), (1.2, Item("common.items.food.meat.fish_raw")), (1.3, Item("common.items.food.meat.bird_raw")), - // Others - (1.0, Item("common.items.crafting_ing.honey")), ] diff --git a/assets/common/recipe_book.ron b/assets/common/recipe_book.ron index 9f8f48e7dd..88727b29e0 100644 --- a/assets/common/recipe_book.ron +++ b/assets/common/recipe_book.ron @@ -1748,6 +1748,54 @@ ], craft_sprite: Some(Anvil), ), + "diamond ring": ( + output: ("common.items.armor.misc.ring.diamond", 1), + inputs: [ + (Item("common.items.mineral.ingot.gold"), 1), + (Item("common.items.mineral.gem.diamond"), 1), + ], + craft_sprite: Some(CraftingBench), + ), + "ruby ring": ( + output: ("common.items.armor.misc.ring.ruby", 1), + inputs: [ + (Item("common.items.mineral.ingot.silver"), 1), + (Item("common.items.mineral.gem.ruby"), 1), + ], + craft_sprite: Some(CraftingBench), + ), + "emerald ring": ( + output: ("common.items.armor.misc.ring.emerald", 1), + inputs: [ + (Item("common.items.mineral.ingot.cobalt"), 1), + (Item("common.items.mineral.gem.emerald"), 1), + ], + craft_sprite: Some(CraftingBench), + ), + "sapphire ring": ( + output: ("common.items.armor.misc.ring.sapphire", 1), + inputs: [ + (Item("common.items.mineral.ingot.iron"), 1), + (Item("common.items.mineral.gem.sapphire"), 1), + ], + craft_sprite: Some(CraftingBench), + ), + "topaz ring": ( + output: ("common.items.armor.misc.ring.topaz", 1), + inputs: [ + (Item("common.items.mineral.ingot.copper"), 1), + (Item("common.items.mineral.gem.topaz"), 1), + ], + craft_sprite: Some(CraftingBench), + ), + "amethyst ring": ( + output: ("common.items.armor.misc.ring.amethyst", 1), + inputs: [ + (Item("common.items.mineral.ingot.tin"), 1), + (Item("common.items.mineral.gem.amethyst"), 1), + ], + craft_sprite: Some(CraftingBench), + ), //"metal_blade": ( // output: ("common.items.crafting_ing.modular.damage.sword.metal_blade", 1), // inputs: [ diff --git a/assets/server/manifests/kits.ron b/assets/server/manifests/kits.ron index 61d7ffc46c..1c1e45d206 100644 --- a/assets/server/manifests/kits.ron +++ b/assets/server/manifests/kits.ron @@ -12,12 +12,44 @@ ("common.items.consumable.potion_big", 100), ("common.items.food.apple_mushroom_curry", 100), ], - "endgame": [ - // Biju - ("common.items.armor.misc.neck.plain_1",1), - ("common.items.armor.misc.ring.skull",1), - ("common.items.armor.misc.ring.skull",1), + "jewellery": [ + // TODO: remove duplicates and handle quantity of non-stackable items + // Necklace + ("common.items.armor.misc.neck.plain_1",1), + ("common.items.armor.misc.neck.shell",1), + + // Rings + // With effects + ("common.items.armor.misc.ring.diamond",1), + ("common.items.armor.misc.ring.diamond",1), + + ("common.items.armor.misc.ring.ruby",1), + ("common.items.armor.misc.ring.ruby",1), + + ("common.items.armor.misc.ring.emerald",1), + ("common.items.armor.misc.ring.emerald",1), + + ("common.items.armor.misc.ring.sapphire",1), + ("common.items.armor.misc.ring.sapphire",1), + + ("common.items.armor.misc.ring.topaz",1), + ("common.items.armor.misc.ring.topaz",1), + + ("common.items.armor.misc.ring.amethyst",1), + ("common.items.armor.misc.ring.amethyst",1), + + // Without effects + ("common.items.armor.misc.ring.scratched",1), + ("common.items.armor.misc.ring.scratched",1), + + ("common.items.armor.misc.ring.gold",1), + ("common.items.armor.misc.ring.gold",1), + + ("common.items.armor.misc.ring.skull",1), + ("common.items.armor.misc.ring.skull",1), + ], + "endgame": [ // Cultist weapons ("common.items.weapons.hammer.cultist_purp_2h-0",1), ("common.items.weapons.staff.cultist_staff",1), diff --git a/assets/voxygen/item_image_manifest.ron b/assets/voxygen/item_image_manifest.ron index 8159668653..5901e2a296 100644 --- a/assets/voxygen/item_image_manifest.ron +++ b/assets/voxygen/item_image_manifest.ron @@ -2194,17 +2194,40 @@ // Rings Armor(Ring("Scratched")): VoxTrans( "voxel.armor.misc.ring.scratched", - (0.0, 0.0, 0.0), (30.0, 0.0, 0.0), 0.9, + (0.0, 0.0, 0.0), (45.0, 15.0, 0.0), 0.9, ), Armor(Ring("Gold")): VoxTrans( "voxel.armor.misc.ring.gold", - (0.0, 0.0, 0.0), (30.0, 0.0, 0.0), 0.9, + (0.0, 0.0, 0.0), (45.0, 15.0, 0.0), 0.9, + ), + Armor(Ring("Topaz")): VoxTrans( + "voxel.armor.misc.ring.topaz", + (0.0, 0.0, 0.0), (45.0, 15.0, 0.0), 0.9, + ), + Armor(Ring("Amethyst")): VoxTrans( + "voxel.armor.misc.ring.amethyst", + (0.0, 0.0, 0.0), (45.0, 15.0, 0.0), 0.9, + ), + Armor(Ring("Sapphire")): VoxTrans( + "voxel.armor.misc.ring.sapphire", + (0.0, 0.0, 0.0), (45.0, 15.0, 0.0), 0.9, + ), + Armor(Ring("Emerald")): VoxTrans( + "voxel.armor.misc.ring.emerald", + (0.0, 0.0, 0.0), (45.0, 15.0, 0.0), 0.9, + ), + Armor(Ring("Ruby")): VoxTrans( + "voxel.armor.misc.ring.ruby", + (0.0, 0.0, 0.0), (45.0, 15.0, 0.0), 0.9, + ), + Armor(Ring("Diamond")): VoxTrans( + "voxel.armor.misc.ring.diamond", + (0.0, 0.0, 0.0), (45.0, 20.0, 0.0), 0.9, ), Armor(Ring("Skull")): VoxTrans( "voxel.armor.misc.ring.skull", - (0.0, 0.0, 0.0), (30.0, 0.0, 0.0), 0.9, + (0.0, 0.0, 0.0), (45.0, 15.0, 0.0), 0.9, ), - // Necks Armor(Neck("Plain0")): Png( "element.items.neck-0", diff --git a/assets/voxygen/voxel/armor/misc/ring/amethyst.vox b/assets/voxygen/voxel/armor/misc/ring/amethyst.vox new file mode 100644 index 0000000000..f19efe90ea --- /dev/null +++ b/assets/voxygen/voxel/armor/misc/ring/amethyst.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:50425f178ba8bf15f003e76a854662ab1f1a4ef7c3a920c7322b1096af75e037 +size 27414 diff --git a/assets/voxygen/voxel/armor/misc/ring/diamond.vox b/assets/voxygen/voxel/armor/misc/ring/diamond.vox new file mode 100644 index 0000000000..db5e30b046 --- /dev/null +++ b/assets/voxygen/voxel/armor/misc/ring/diamond.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b9d075cec317d5b00c3be31c359435133d1620f42d14fd87d5eed2558f5f7ef0 +size 27414 diff --git a/assets/voxygen/voxel/armor/misc/ring/emerald.vox b/assets/voxygen/voxel/armor/misc/ring/emerald.vox new file mode 100644 index 0000000000..5afe24d161 --- /dev/null +++ b/assets/voxygen/voxel/armor/misc/ring/emerald.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d455bdd68e3955c7cf626026e7bebb68e8d0f8ea27e550fefaaa3ab8b76ad924 +size 27414 diff --git a/assets/voxygen/voxel/armor/misc/ring/gold.vox b/assets/voxygen/voxel/armor/misc/ring/gold.vox index 0086958447..637116f91b 100644 --- a/assets/voxygen/voxel/armor/misc/ring/gold.vox +++ b/assets/voxygen/voxel/armor/misc/ring/gold.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1ce921e48cb12d03ae53b14da5e72175349cdd69f0bfe3fe869a80c1e47b1398 -size 1260 +oid sha256:33a964cab0c197e23be83e924159eaf3f0d13f0cb14b0e1908adfb76e4e9539c +size 27378 diff --git a/assets/voxygen/voxel/armor/misc/ring/ruby.vox b/assets/voxygen/voxel/armor/misc/ring/ruby.vox new file mode 100644 index 0000000000..fa1a98d7b4 --- /dev/null +++ b/assets/voxygen/voxel/armor/misc/ring/ruby.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e439ef7eb904260bd1f6f66093b55a7068cc009195c94935d541b5cacf48dc41 +size 27414 diff --git a/assets/voxygen/voxel/armor/misc/ring/sapphire.vox b/assets/voxygen/voxel/armor/misc/ring/sapphire.vox new file mode 100644 index 0000000000..c44cd17dd7 --- /dev/null +++ b/assets/voxygen/voxel/armor/misc/ring/sapphire.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:931051f7524c2a7c21823a1944b755c8cb7deb0982309a9b73c2a02121ef74b0 +size 27414 diff --git a/assets/voxygen/voxel/armor/misc/ring/scratched.vox b/assets/voxygen/voxel/armor/misc/ring/scratched.vox index e055f9d786..ac526b2c66 100644 --- a/assets/voxygen/voxel/armor/misc/ring/scratched.vox +++ b/assets/voxygen/voxel/armor/misc/ring/scratched.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d3b50a1478580f84711ebcad5f076f0465a261f6c573f2d4c72b5a1a18ea4ba -size 1236 +oid sha256:f333e00a6090e89361ad337d993d80e0ea10363492b6127b4fd3533f49f77596 +size 27378 diff --git a/assets/voxygen/voxel/armor/misc/ring/topaz.vox b/assets/voxygen/voxel/armor/misc/ring/topaz.vox new file mode 100644 index 0000000000..4def403756 --- /dev/null +++ b/assets/voxygen/voxel/armor/misc/ring/topaz.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ef47be70091859883d2eba7ba1168258becede06d006e47b39df2e200b6f997d +size 27414 diff --git a/common/src/comp/inventory/trade_pricing.rs b/common/src/comp/inventory/trade_pricing.rs index 78fc2b7dda..7dcff3bc56 100644 --- a/common/src/comp/inventory/trade_pricing.rs +++ b/common/src/comp/inventory/trade_pricing.rs @@ -376,11 +376,18 @@ impl TradePricing { false } else if recipe.material_cost < Self::UNAVAILABLE_PRICE { let actual_cost = result.calculate_material_cost(recipe, &eqset); + let output_tradeable = recipe.input.iter().all(|(input, _)| { + result + .get_list_by_path(&input) + .iter() + .find(|(item, _, _)| item == input) + .map_or(false, |(_, _, tradeable)| *tradeable) + }); result.get_list_by_path_mut(&recipe.output).add( &eqset, &recipe.output, (recipe.amount as f32) / actual_cost * Self::CRAFTING_FACTOR, - true, + output_tradeable, ); false } else { @@ -424,7 +431,9 @@ impl TradePricing { Some(Self::COIN_ITEM.into()) } else { let table = self.get_list(good); - if table.is_empty() { + if table.is_empty() + || (selling && table.iter().filter(|(_, _, can_sell)| *can_sell).count() == 0) + { warn!("Good: {:?}, was unreachable.", good); return None; } From 0076b8379cfa7b0b1ed0a154da662d93a0426ad0 Mon Sep 17 00:00:00 2001 From: Daan Vanoverloop Date: Sat, 26 Jun 2021 16:54:17 +0200 Subject: [PATCH 179/180] Fixing #1169 - use strum macros for BlockKind conversion --- common/src/cmd.rs | 5 ++--- common/src/terrain/block.rs | 26 +++++--------------------- server/src/cmd.rs | 3 ++- 3 files changed, 9 insertions(+), 25 deletions(-) diff --git a/common/src/cmd.rs b/common/src/cmd.rs index c9991a74c2..a7b8ca6670 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -218,9 +218,8 @@ lazy_static! { buff_pack }; - static ref BLOCK_KINDS: Vec = terrain::block::BLOCK_KINDS - .keys() - .cloned() + static ref BLOCK_KINDS: Vec = terrain::block::BlockKind::iter() + .map(|bk| bk.to_string()) .collect(); static ref SPRITE_KINDS: Vec = terrain::sprite::SPRITE_KINDS diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 69fc6e21f3..c2822b31b5 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -3,13 +3,11 @@ use crate::{ comp::{fluid_dynamics::LiquidKind, tool::ToolKind}, make_case_elim, }; -use enum_iterator::IntoEnumIterator; -use hashbrown::HashMap; -use lazy_static::lazy_static; use num_derive::FromPrimitive; use num_traits::FromPrimitive; use serde::{Deserialize, Serialize}; -use std::{convert::TryFrom, fmt, ops::Deref}; +use std::ops::Deref; +use strum_macros::{EnumIter, EnumString, ToString}; use vek::*; make_case_elim!( @@ -23,8 +21,10 @@ make_case_elim!( PartialEq, Serialize, Deserialize, - IntoEnumIterator, FromPrimitive, + EnumString, + EnumIter, + ToString, )] #[repr(u8)] pub enum BlockKind { @@ -88,22 +88,6 @@ impl BlockKind { pub const fn has_color(&self) -> bool { self.is_filled() } } -impl fmt::Display for BlockKind { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self) } -} - -lazy_static! { - pub static ref BLOCK_KINDS: HashMap = BlockKind::into_enum_iter() - .map(|bk| (bk.to_string(), bk)) - .collect(); -} - -impl<'a> TryFrom<&'a str> for BlockKind { - type Error = (); - - fn try_from(s: &'a str) -> Result { BLOCK_KINDS.get(s).copied().ok_or(()) } -} - #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct Block { kind: BlockKind, diff --git a/server/src/cmd.rs b/server/src/cmd.rs index a10e1276d1..254dcec5e8 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -43,6 +43,7 @@ use hashbrown::{HashMap, HashSet}; use humantime::Duration as HumanDuration; use rand::Rng; use specs::{storage::StorageEntry, Builder, Entity as EcsEntity, Join, WorldExt}; +use std::str::FromStr; use vek::*; use wiring::{Circuit, Wire, WiringAction, WiringActionEffect, WiringElement}; use world::util::Sampler; @@ -515,7 +516,7 @@ fn handle_make_block( action: &ChatCommand, ) -> CmdResult<()> { if let Some(block_name) = scan_fmt_some!(&args, &action.arg_fmt(), String) { - if let Ok(bk) = BlockKind::try_from(block_name.as_str()) { + if let Ok(bk) = BlockKind::from_str(block_name.as_str()) { let pos = position(server, target, "target")?; server.state.set_block( pos.0.map(|e| e.floor() as i32), From da319e71ece547f59c3c1f71a9f30e372a9eee60 Mon Sep 17 00:00:00 2001 From: Ben Wallis Date: Sat, 26 Jun 2021 16:43:39 +0100 Subject: [PATCH 180/180] Added default-publish feature and updated build scripts. --- .gitlab/CI/build.gitlab-ci.yml | 12 +++++++++--- .gitlab/CI/check.gitlab-ci.yml | 4 +++- voxygen/Cargo.toml | 7 ++++++- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/.gitlab/CI/build.gitlab-ci.yml b/.gitlab/CI/build.gitlab-ci.yml index fa6cf2ce33..e0eddc8959 100644 --- a/.gitlab/CI/build.gitlab-ci.yml +++ b/.gitlab/CI/build.gitlab-ci.yml @@ -61,7 +61,9 @@ coverage: script: - ln -s /dockercache/target target - rm -r target/release/incremental/veloren_* || echo "all good" # TMP FIX FOR 2021-03-22-nightly - - VELOREN_USERDATA_STRATEGY=executable cargo build --release + - export VELOREN_USERDATA_STRATEGY=executable + - cargo build --release -p veloren-voxygen --no-default-features --features default-publish + - cargo build --release -p veloren-server-cli - cp -r target/release/veloren-server-cli $CI_PROJECT_DIR - cp -r target/release/veloren-voxygen $CI_PROJECT_DIR artifacts: @@ -79,7 +81,9 @@ coverage: - update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix - ln -s /dockercache/target target - rm -r target/release/incremental/veloren_* || echo "all good" # TMP FIX FOR 2021-03-22-nightly - - VELOREN_USERDATA_STRATEGY=executable cargo build --target=x86_64-pc-windows-gnu --release + - export VELOREN_USERDATA_STRATEGY=executable + - cargo build --target=x86_64-pc-windows-gnu --release -p veloren-voxygen --no-default-features --features default-publish + - cargo build --target=x86_64-pc-windows-gnu --release -p veloren-server-cli - cp -r target/x86_64-pc-windows-gnu/release/veloren-server-cli.exe $CI_PROJECT_DIR - cp -r target/x86_64-pc-windows-gnu/release/veloren-voxygen.exe $CI_PROJECT_DIR - cp /usr/lib/gcc/x86_64-w64-mingw32/7.3-posix/libgcc_s_seh-1.dll $CI_PROJECT_DIR @@ -107,7 +111,9 @@ coverage: - export RUSTFLAGS="-D warnings" script: - export MACOSX_DEPLOYMENT_TARGET="10.13" - - VELOREN_USERDATA_STRATEGY=executable cargo build --release + - export VELOREN_USERDATA_STRATEGY=executable + - cargo build --release -p veloren-voxygen --no-default-features --features default-publish + - cargo build --release -p veloren-server-cli - cp -r target/release/veloren-server-cli $CI_PROJECT_DIR - cp -r target/release/veloren-voxygen $CI_PROJECT_DIR artifacts: diff --git a/.gitlab/CI/check.gitlab-ci.yml b/.gitlab/CI/check.gitlab-ci.yml index 51f306d8e8..2f5b0a1490 100644 --- a/.gitlab/CI/check.gitlab-ci.yml +++ b/.gitlab/CI/check.gitlab-ci.yml @@ -6,7 +6,9 @@ code-quality: script: - ln -s /dockercache/target target - rm -r target/debug/incremental/* || echo "all good" # TMP FIX FOR 2021-03-22-nightly - - cargo clippy --all-targets --locked --features="bin_csv,bin_graphviz,bin_bot,asset_tweak" -- -D warnings + - cargo clippy --all-targets --locked --features="bin_csv,bin_graphviz,bin_bot,asset_tweak" -- -D warnings + # Ensure that the veloren-voxygen default-publish feature builds as it excludes some default features + - cargo clippy -p veloren-voxygen --locked --no-default-features --features="default-publish" -- -D warnings - cargo fmt --all -- --check security: diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index dfc7fcb121..c24e505d36 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -28,7 +28,12 @@ simd = ["vek/platform_intrinsics"] tracy = ["profiling", "profiling/profile-with-tracy", "common-frontend/tracy"] plugins = ["client/plugins"] -default = ["singleplayer", "native-dialog", "plugins", "simd"] +# We don't ship egui with published release builds so a separate feature is required that excludes it. +# This feature has been added ahead of the egui merge to allow time for Flatpak, AUR and Nix to be updated +# to ensure there isn't any period of time where we're shipping the egui-ui feature enabled by default. +# This comment will be updated after MR 2253 merges. +default-publish = ["singleplayer", "native-dialog", "plugins", "simd"] +default = ["default-publish"] [dependencies] client = {package = "veloren-client", path = "../client"}