Merge branch 'isse/small-fixes' into 'master'

Small fixes

See merge request veloren/veloren!3846
This commit is contained in:
Isse 2023-03-29 23:11:59 +00:00
commit 08ca360926
99 changed files with 952 additions and 411 deletions

View File

@ -0,0 +1,24 @@
#![enable(implicit_some)]
(
name: Name("Trickster"),
body: RandomWith("draugr"),
alignment: Alignment(Enemy),
loot: LootTable("common.loot_tables.calendar.april_fools.trickster"),
inventory: (
loadout: Inline((
inherit: Asset("common.loadout.calendar.halloween.trickster"),
active_hands: InHands((Choice([
(1, Item("common.items.tool.instruments.double_bass")),
(1, Item("common.items.tool.instruments.flute")),
(1, Item("common.items.tool.instruments.lyre")),
(1, Item("common.items.tool.instruments.kalimba")),
(1, Item("common.items.tool.instruments.sitar")),
(1, Item("common.items.tool.instruments.washboard")),
(1, Item("common.items.tool.instruments.lute")),
(1, Item("common.items.tool.instruments.guitar")),
(1, Item("common.items.tool.instruments.melodica")),
]), None)),
)),
),
meta: [],
)

View File

@ -3,7 +3,7 @@ ItemDef(
description: "A potent healing potion.",
kind: Consumable(
kind: Drink,
effects: [
effects: All([
Buff((
kind: Potion,
data: (
@ -21,7 +21,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
])
),
quality: High,
tags: [Potion],

View File

@ -0,0 +1,67 @@
ItemDef(
name: "Curious Potion",
description: "Wonder what this does...",
kind: Consumable(
kind: Drink,
effects: Any([
Buff((
kind: Polymorphed(QuadrupedSmall(( species: Frog, body_type: Female ))),
data: (
strength: 0.0,
duration: Some(60),
),
cat_ids: [Natural],
)),
Buff((
kind: Polymorphed(QuadrupedSmall(( species: Rabbit, body_type: Female ))),
data: (
strength: 0.0,
duration: Some(60),
),
cat_ids: [Natural],
)),
Buff((
kind: Polymorphed(QuadrupedSmall(( species: Rat, body_type: Female ))),
data: (
strength: 0.0,
duration: Some(60),
),
cat_ids: [Natural],
)),
Buff((
kind: Polymorphed(QuadrupedSmall(( species: Squirrel, body_type: Female ))),
data: (
strength: 0.0,
duration: Some(60),
),
cat_ids: [Natural],
)),
Buff((
kind: Polymorphed(QuadrupedSmall(( species: Cat, body_type: Female ))),
data: (
strength: 0.0,
duration: Some(60),
),
cat_ids: [Natural],
)),
Buff((
kind: Polymorphed(QuadrupedSmall(( species: Fungome, body_type: Female ))),
data: (
strength: 0.0,
duration: Some(60),
),
cat_ids: [Natural],
)),
Buff((
kind: Polymorphed(QuadrupedSmall(( species: Pig, body_type: Female ))),
data: (
strength: 0.0,
duration: Some(60),
),
cat_ids: [Natural],
)),
])
),
quality: Common,
tags: [Potion],
)

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Precious medicine, it makes for the largest rejuvenative flask yet.",
kind: Consumable(
kind: Drink,
effects: [
effects: All([
Buff((
kind: Potion,
data: (
@ -20,7 +20,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
])
),
quality: Common,
tags: [Potion],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "An innovative invention from an apothecary, better than its smaller precursors.",
kind: Consumable(
kind: Drink,
effects: [
effects: All([
Buff((
kind: Potion,
data: (
@ -20,7 +20,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
])
),
quality: Common,
tags: [Potion],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "A small potion concocted from apples and honey.",
kind: Consumable(
kind: Drink,
effects: [
effects: All([
Buff((
kind: Potion,
data: (
@ -20,7 +20,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
])
),
quality: Common,
tags: [Potion],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "They say gods eat it to get eternal youth.",
kind: Consumable(
kind: Drink,
effects: [
effects: All([
Buff((
kind: Regeneration,
data: (
@ -36,7 +36,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
])
),
quality: Debug,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Red and juicy",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Who could say no to that?",
kind: Consumable(
kind: ComplexFood,
effects: [
effects: All([
Buff((
kind: Saturation,
data: (
@ -20,7 +20,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
])
),
quality: Moderate,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "The stick makes it easier to carry!",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Pungent and filling",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: High,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Giving you that special prickle.",
kind: Consumable(
kind: Drink,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Moderate,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "An orange root vegetable. They say it'll improve your vision!",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Made from goat milk from the finest dwarven produce. Aromatic and nutritious!",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Reliable source of water and fat. Can often be found growing on palm trees.",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "A daisy-like flower often used in herbal teas.",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "A small, yellow flower. Uses the wind to spread its seeds.",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Make sure to brush your teeth after eating.",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Sweeet",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "A vibrant green leafy vegetable. Lettuce make some salads!",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Meat. The lifeblood of mankind.",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Medium Rare.",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Chunk of beastly animal meat, best after cooking.",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Medium Rare.",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Small hunk of beastly animal meat, best after cooking.",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Best enjoyed with one in each hand.",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Makes for a legendary meal.",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "It's magnificent.",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "A hefty drumstick.",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "A fresh cooked seafood steak.",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "A steak chopped from a fish, best after cooking.",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Tastes exotic.",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Peculiar bit of meat, best after cooking.",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Hopefully this one is not poisonous",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Roasted mushrooms on a stick for easy carrying",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "A vegetable that's made the toughest men cry.",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Literally just chopped lettuce. Does this even count as a salad?",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Brewed from moldy pumpkins.",
kind: Consumable(
kind: Drink,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Moderate,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "A herb commonly used in tea.",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "You feel an evil force pulsating within.\n\nIt may be unwise to hold on to it for too long...",
kind: Consumable(
kind: ComplexFood,
effects: [
effects: All([
Buff((
kind: Frenzied,
data: (
@ -20,7 +20,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
])
),
quality: Epic,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Brewed from freshly shelled sunflower seeds",
kind: Consumable(
kind: Drink,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Moderate,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "A red fruit. It's not actually a vegetable!",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -3,7 +3,7 @@ ItemDef(
description: "Leafy salad with some chopped, juicy tomatoes mixed in.",
kind: Consumable(
kind: Food,
effects: [
effects: One(
Buff((
kind: Saturation,
data: (
@ -12,7 +12,7 @@ ItemDef(
),
cat_ids: [Natural],
)),
]
)
),
quality: Common,
tags: [Food],

View File

@ -0,0 +1,4 @@
[
(1.0, Item("common.items.consumable.curious_potion")),
(19.0, Nothing),
]

BIN
assets/voxygen/element/de_buffs/debuff_polymorphed.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -76,7 +76,11 @@ buff-stat-potionsickness =
## Reckless
buff-title-reckless = Reckless
buff-desc-reckless = Your attacks are more powerful, however you are leaving your defenses open.
## Polymorped
buff-title-polymorphed = Polymorphed
buff-desc-polymorphed = Your body changes form.
## Util
buff-text-over_seconds = over { $dur_secs } seconds
buff-text-for_seconds = for { $dur_secs } seconds
buff-mysterious = Mysterious effect
buff-remove = Click to remove

View File

@ -3134,6 +3134,10 @@
"voxel.object.potion_red",
(0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.9,
),
Simple("common.items.consumable.curious_potion"): VoxTrans(
"voxel.object.curious_potion",
(0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.7,
),
Simple("common.items.food.cheese"): VoxTrans(
"voxel.object.cheese",
(0.0, 0.0, 0.0), (-60.0, 27.0, 17.0), 0.7,

View File

@ -794,6 +794,7 @@
Simple("common.items.consumable.potion_med"): "voxel.object.potion_red",
Simple("common.items.consumable.potion_minor"): "voxel.object.potion_red",
Simple("common.items.consumable.potion_big"): "voxel.object.potion_red",
Simple("common.items.consumable.curious_potion"): "voxel.object.curious_potion",
Simple("common.items.boss_drops.potions"): "voxel.object.potion_red",
Simple("common.items.food.cheese"): "voxel.object.cheese",
Simple("common.items.food.blue_cheese"): "voxel.object.blue_cheese",

BIN
assets/voxygen/voxel/object/curious_potion.vox (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,14 @@
SpawnEntry (
name: "April Fools Jungle Area",
note: "April Fools NPCs",
rules: [
Pack(
groups: [
(3, (3, 6, "common.entity.calendar.april_fools.aggressive.trickster")),
],
spawn_mode: Land,
calendar_events: Some([AprilFools]),
day_period: [Night, Morning, Noon, Evening],
),
],
)

View File

@ -0,0 +1,14 @@
SpawnEntry (
name: "April Fools Jungle Core",
note: "April Fools NPCs",
rules: [
Pack(
groups: [
(3, (3, 6, "common.entity.calendar.april_fools.aggressive.trickster")),
],
spawn_mode: Land,
calendar_events: Some([AprilFools]),
day_period: [Night, Morning, Noon, Evening],
),
],
)

View File

@ -0,0 +1,14 @@
SpawnEntry (
name: "April Fools Temperate Rainforest",
note: "April Fools NPCs",
rules: [
Pack(
groups: [
(3, (3, 6, "common.entity.calendar.april_fools.aggressive.trickster")),
],
spawn_mode: Land,
calendar_events: Some([AprilFools]),
day_period: [Night, Morning, Noon, Evening],
),
],
)

View File

@ -0,0 +1,14 @@
SpawnEntry (
name: "April Fools Tropical Rainforest",
note: "April Fools NPCs",
rules: [
Pack(
groups: [
(3, (3, 6, "common.entity.calendar.april_fools.aggressive.trickster")),
],
spawn_mode: Land,
calendar_events: Some([AprilFools]),
day_period: [Night, Morning, Noon, Evening],
),
],
)

View File

@ -0,0 +1,14 @@
SpawnEntry (
name: "April Fools Tundra Core",
note: "April Fools NPCs",
rules: [
Pack(
groups: [
(3, (3, 6, "common.entity.calendar.april_fools.aggressive.trickster")),
],
spawn_mode: Land,
calendar_events: Some([AprilFools]),
day_period: [Night, Morning, Noon, Evening],
),
],
)

View File

@ -0,0 +1,14 @@
SpawnEntry (
name: "April Fools Tundra Forest",
note: "April Fools NPCs",
rules: [
Pack(
groups: [
(3, (3, 6, "common.entity.calendar.april_fools.aggressive.trickster")),
],
spawn_mode: Land,
calendar_events: Some([AprilFools]),
day_period: [Night, Morning, Noon, Evening],
),
],
)

View File

@ -0,0 +1,14 @@
SpawnEntry (
name: "April Fools Tundra Snow",
note: "April Fools NPCs",
rules: [
Pack(
groups: [
(3, (3, 6, "common.entity.calendar.april_fools.aggressive.trickster")),
],
spawn_mode: Land,
calendar_events: Some([AprilFools]),
day_period: [Night, Morning, Noon, Evening],
),
],
)

View File

@ -8,6 +8,7 @@ use strum::EnumIter;
pub enum CalendarEvent {
Christmas = 0,
Halloween = 1,
AprilFools = 2,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
@ -43,6 +44,10 @@ impl Calendar {
this.events.push(CalendarEvent::Halloween);
}
if now.month() == 4 && now.day() == 1 {
this.events.push(CalendarEvent::AprilFools);
}
this
}
}

View File

@ -157,6 +157,7 @@ lazy_static! {
BuffKind::Parried => "parried",
BuffKind::PotionSickness => "potion_sickness",
BuffKind::Reckless => "reckless",
BuffKind::Polymorphed(_) => "polymorphed",
};
let mut buff_parser = HashMap::new();
for kind in BuffKind::iter() {

View File

@ -31,7 +31,7 @@ use super::{BuffKind, Collider, Density, Mass, Scale};
make_case_elim!(
body,
#[derive(Copy, Clone, Debug, Display, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, Display, PartialEq, PartialOrd, Ord, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Body {
Humanoid(body: humanoid::Body) = 0,
@ -54,6 +54,16 @@ make_case_elim!(
}
);
// Implemented for Buff, to be able to implement EnumIter.
impl Default for Body {
fn default() -> Self {
Body::QuadrupedSmall(quadruped_small::Body {
species: quadruped_small::Species::Frog,
body_type: quadruped_small::BodyType::Female,
})
}
}
/// Data representing data generic to the body together with per-species data.
///
/// NOTE: Deliberately don't (yet?) implement serialize.

View File

@ -1,7 +1,7 @@
use rand::{seq::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
@ -25,7 +25,7 @@ impl From<Body> for super::Body {
fn from(body: Body) -> Self { super::Body::Arthropod(body) }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Tarantula = 0,
@ -99,7 +99,7 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
fn into_iter(self) -> Self::IntoIter { ALL_SPECIES.iter().copied() }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,

View File

@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
@ -31,7 +31,7 @@ impl From<Body> for super::Body {
make_case_elim!(
species,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Ogre = 0,
@ -158,7 +158,7 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
make_case_elim!(
body_type,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,

View File

@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
@ -31,7 +31,7 @@ impl From<Body> for super::Body {
make_case_elim!(
species,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Gnome = 0,
@ -114,7 +114,7 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
make_case_elim!(
body_type,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,

View File

@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
@ -31,7 +31,7 @@ impl From<Body> for super::Body {
make_case_elim!(
species,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Phoenix = 0,
@ -98,7 +98,7 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
make_case_elim!(
body_type,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,

View File

@ -5,7 +5,7 @@ use strum::{Display, EnumString};
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
@ -33,7 +33,18 @@ impl From<Body> for super::Body {
make_case_elim!(
species,
#[derive(
Copy, Clone, Debug, Display, EnumString, PartialEq, Eq, Hash, Serialize, Deserialize,
Copy,
Clone,
Debug,
Display,
EnumString,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
)]
#[repr(u32)]
pub enum Species {
@ -134,7 +145,18 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
make_case_elim!(
body_type,
#[derive(
Copy, Clone, Debug, Display, EnumString, PartialEq, Eq, Hash, Serialize, Deserialize,
Copy,
Clone,
Debug,
Display,
EnumString,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
)]
#[repr(u32)]
pub enum BodyType {

View File

@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
@ -31,7 +31,7 @@ impl From<Body> for super::Body {
make_case_elim!(
species,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Reddragon = 0,
@ -68,7 +68,7 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
make_case_elim!(
body_type,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,

View File

@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
@ -31,7 +31,7 @@ impl From<Body> for super::Body {
make_case_elim!(
species,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Marlin = 0,
@ -71,7 +71,7 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
make_case_elim!(
body_type,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,

View File

@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
@ -31,7 +31,7 @@ impl From<Body> for super::Body {
make_case_elim!(
species,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Clownfish = 0,
@ -71,7 +71,7 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
make_case_elim!(
body_type,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,

View File

@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
@ -31,7 +31,7 @@ impl From<Body> for super::Body {
make_case_elim!(
species,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
StoneGolem = 0,
@ -82,7 +82,7 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
make_case_elim!(
body_type,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,

View File

@ -5,7 +5,7 @@ use serde_repr::{Deserialize_repr, Serialize_repr};
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
@ -90,7 +90,7 @@ impl From<Body> for super::Body {
make_case_elim!(
species,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Danari = 0,
@ -413,7 +413,7 @@ impl Species {
make_case_elim!(
body_type,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,

View File

@ -18,7 +18,7 @@ use vek::Vec3;
make_case_elim!(
armor,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum ItemDropArmorKind {
Shoulder = 0,
@ -38,7 +38,7 @@ make_case_elim!(
make_case_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Body {
Tool(tool: ToolKind) = 0,

View File

@ -9,7 +9,7 @@ use vek::Vec3;
make_case_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Body {
Arrow = 0,

View File

@ -5,7 +5,7 @@ use strum::{Display, EnumString};
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
@ -35,7 +35,18 @@ impl From<Body> for super::Body {
make_case_elim!(
species,
#[derive(
Copy, Clone, Debug, Display, EnumString, PartialEq, Eq, Hash, Serialize, Deserialize,
Copy,
Clone,
Debug,
Display,
EnumString,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
)]
#[repr(u32)]
pub enum Species {
@ -158,7 +169,18 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
make_case_elim!(
body_type,
#[derive(
Copy, Clone, Debug, Display, EnumString, PartialEq, Eq, Hash, Serialize, Deserialize,
Copy,
Clone,
Debug,
Display,
EnumString,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
)]
#[repr(u32)]
pub enum BodyType {

View File

@ -5,7 +5,7 @@ use strum::{Display, EnumString};
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
@ -32,7 +32,20 @@ impl From<Body> for super::Body {
// Renaming any enum entries here (re-ordering is fine) will require a
// database migration to ensure pets correctly de-serialize on player login.
#[derive(Copy, Clone, Debug, Display, EnumString, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(
Copy,
Clone,
Debug,
Display,
EnumString,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
)]
#[repr(u32)]
pub enum Species {
Grolgar = 0,
@ -213,7 +226,18 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
make_case_elim!(
body_type,
#[derive(
Copy, Clone, Debug, Display, EnumString, PartialEq, Eq, Hash, Serialize, Deserialize,
Copy,
Clone,
Debug,
Display,
EnumString,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
)]
#[repr(u32)]
pub enum BodyType {

View File

@ -5,7 +5,7 @@ use strum::{Display, EnumString};
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
@ -32,7 +32,20 @@ impl From<Body> for super::Body {
// Renaming any enum entries here (re-ordering is fine) will require a
// database migration to ensure pets correctly de-serialize on player login.
#[derive(Copy, Clone, Debug, Display, EnumString, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(
Copy,
Clone,
Debug,
Display,
EnumString,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
)]
#[repr(u32)]
pub enum Species {
Pig = 0,
@ -177,7 +190,18 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
make_case_elim!(
body_type,
#[derive(
Copy, Clone, Debug, Display, EnumString, PartialEq, Eq, Hash, Serialize, Deserialize,
Copy,
Clone,
Debug,
Display,
EnumString,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
)]
#[repr(u32)]
pub enum BodyType {

View File

@ -21,7 +21,7 @@ pub const ALL_SHIPS: [Body; 2] = [Body::SailBoat, Body::Galleon];
make_case_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Body {
DefaultAirship = 0,

View File

@ -1,7 +1,7 @@
use rand::{seq::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
@ -25,7 +25,7 @@ impl From<Body> for super::Body {
fn from(body: Body) -> Self { super::Body::Theropod(body) }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Archaeos = 0,
@ -95,7 +95,7 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
fn into_iter(self) -> Self::IntoIter { ALL_SPECIES.iter().copied() }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,

View File

@ -13,6 +13,8 @@ use serde::{Deserialize, Serialize};
use specs::{Component, DerefFlaggedStorage, VecStorage};
use strum::EnumIter;
use super::Body;
/// De/buff Kind.
/// This is used to determine what effects a buff will have
#[derive(
@ -107,6 +109,8 @@ pub enum BuffKind {
/// Results from drinking a potion.
/// Decreases the health gained from subsequent potions.
PotionSickness,
// Changed into another body.
Polymorphed(Body),
}
#[cfg(not(target_arch = "wasm32"))]
@ -136,7 +140,8 @@ impl BuffKind {
| BuffKind::Ensnared
| BuffKind::Poisoned
| BuffKind::Parried
| BuffKind::PotionSickness => false,
| BuffKind::PotionSickness
| BuffKind::Polymorphed(_) => false,
}
}
@ -150,149 +155,17 @@ impl BuffKind {
/// Checks if multiple instances of the buff should be processed, instead of
/// only the strongest.
pub fn stacks(self) -> bool { matches!(self, BuffKind::PotionSickness) }
}
// Struct used to store data relevant to a buff
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct BuffData {
pub strength: f32,
pub duration: Option<Secs>,
pub delay: Option<Secs>,
}
#[cfg(not(target_arch = "wasm32"))]
impl BuffData {
pub fn new(strength: f32, duration: Option<Secs>, delay: Option<Secs>) -> Self {
Self {
strength,
duration,
delay,
}
}
}
/// De/buff category ID.
/// Similar to `BuffKind`, but to mark a category (for more generic usage, like
/// positive/negative buffs).
#[derive(Clone, Copy, Eq, PartialEq, Debug, Serialize, Deserialize)]
pub enum BuffCategory {
Natural,
Physical,
Magical,
Divine,
PersistOnDeath,
FromActiveAura(Uid, AuraKey),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ModifierKind {
Additive,
Fractional,
}
/// Data indicating and configuring behaviour of a de/buff.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum BuffEffect {
/// Periodically damages or heals entity
HealthChangeOverTime {
rate: f32,
kind: ModifierKind,
instance: u64,
},
/// Periodically consume entity energy
EnergyChangeOverTime { rate: f32, kind: ModifierKind },
/// Changes maximum health by a certain amount
MaxHealthModifier { value: f32, kind: ModifierKind },
/// Changes maximum energy by a certain amount
MaxEnergyModifier { value: f32, kind: ModifierKind },
/// Reduces damage after armor is accounted for by this fraction
DamageReduction(f32),
/// Gradually changes an entities max health over time
MaxHealthChangeOverTime {
rate: f32,
kind: ModifierKind,
target_fraction: f32,
},
/// Modifies move speed of target
MovementSpeed(f32),
/// Modifies attack speed of target
AttackSpeed(f32),
/// Modifies ground friction of target
GroundFriction(f32),
/// Reduces poise damage taken after armor is accounted for by this fraction
PoiseReduction(f32),
/// Reduces amount healed by consumables
HealReduction(f32),
/// Increases poise damage dealt when health is lost
PoiseDamageFromLostHealth { initial_health: f32, strength: f32 },
/// Modifier to the amount of damage dealt with attacks
AttackDamage(f32),
/// Multiplies crit chance of attacks
CriticalChance(f32),
}
/// Actual de/buff.
/// Buff can timeout after some time if `time` is Some. If `time` is None,
/// Buff will last indefinitely, until removed manually (by some action, like
/// uncursing).
///
/// Buff has a kind, which is used to determine the effects in a builder
/// function.
///
/// To provide more classification info when needed,
/// buff can be in one or more buff category.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Buff {
pub kind: BuffKind,
pub data: BuffData,
pub cat_ids: Vec<BuffCategory>,
pub end_time: Option<Time>,
pub start_time: Time,
pub effects: Vec<BuffEffect>,
pub source: BuffSource,
}
/// Information about whether buff addition or removal was requested.
/// This to implement "on_add" and "on_remove" hooks for constant buffs.
#[derive(Clone, Debug)]
pub enum BuffChange {
/// Adds this buff.
Add(Buff),
/// Removes all buffs with this ID.
RemoveByKind(BuffKind),
/// Removes all buffs with this ID, but not debuffs.
RemoveFromController(BuffKind),
/// Removes buffs of these indices (first vec is for active buffs, second is
/// for inactive buffs), should only be called when buffs expire
RemoveById(Vec<BuffId>),
/// Removes buffs of these categories (first vec is of categories of which
/// all are required, second vec is of categories of which at least one is
/// required, third vec is of categories that will not be removed)
RemoveByCategory {
all_required: Vec<BuffCategory>,
any_required: Vec<BuffCategory>,
none_required: Vec<BuffCategory>,
},
// Refreshes durations of all buffs with this kind
Refresh(BuffKind),
}
#[cfg(not(target_arch = "wasm32"))]
impl Buff {
/// Builder function for buffs
pub fn new(
kind: BuffKind,
data: BuffData,
cat_ids: Vec<BuffCategory>,
source: BuffSource,
time: Time,
pub fn effects(
&self,
data: &BuffData,
stats: Option<&Stats>,
health: Option<&Health>,
) -> Self {
) -> Vec<BuffEffect> {
// Normalized nonlinear scaling
let nn_scaling = |a| a / (a + 0.5);
let instance = rand::random();
let effects = match kind {
match self {
BuffKind::Bleeding => vec![BuffEffect::HealthChangeOverTime {
rate: -data.strength,
kind: ModifierKind::Additive,
@ -397,7 +270,151 @@ impl Buff {
BuffEffect::DamageReduction(-data.strength),
BuffEffect::AttackDamage(1.0 + data.strength),
],
};
BuffKind::Polymorphed(body) => vec![BuffEffect::BodyChange(*body)],
}
}
}
// Struct used to store data relevant to a buff
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct BuffData {
pub strength: f32,
pub duration: Option<Secs>,
pub delay: Option<Secs>,
}
#[cfg(not(target_arch = "wasm32"))]
impl BuffData {
pub fn new(strength: f32, duration: Option<Secs>, delay: Option<Secs>) -> Self {
Self {
strength,
duration,
delay,
}
}
}
/// De/buff category ID.
/// Similar to `BuffKind`, but to mark a category (for more generic usage, like
/// positive/negative buffs).
#[derive(Clone, Copy, Eq, PartialEq, Debug, Serialize, Deserialize)]
pub enum BuffCategory {
Natural,
Physical,
Magical,
Divine,
PersistOnDeath,
FromActiveAura(Uid, AuraKey),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ModifierKind {
Additive,
Fractional,
}
/// Data indicating and configuring behaviour of a de/buff.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum BuffEffect {
/// Periodically damages or heals entity
HealthChangeOverTime {
rate: f32,
kind: ModifierKind,
instance: u64,
},
/// Periodically consume entity energy
EnergyChangeOverTime { rate: f32, kind: ModifierKind },
/// Changes maximum health by a certain amount
MaxHealthModifier { value: f32, kind: ModifierKind },
/// Changes maximum energy by a certain amount
MaxEnergyModifier { value: f32, kind: ModifierKind },
/// Reduces damage after armor is accounted for by this fraction
DamageReduction(f32),
/// Gradually changes an entities max health over time
MaxHealthChangeOverTime {
rate: f32,
kind: ModifierKind,
target_fraction: f32,
},
/// Modifies move speed of target
MovementSpeed(f32),
/// Modifies attack speed of target
AttackSpeed(f32),
/// Modifies ground friction of target
GroundFriction(f32),
/// Reduces poise damage taken after armor is accounted for by this fraction
PoiseReduction(f32),
/// Reduces amount healed by consumables
HealReduction(f32),
/// Increases poise damage dealt when health is lost
PoiseDamageFromLostHealth { initial_health: f32, strength: f32 },
/// Modifier to the amount of damage dealt with attacks
AttackDamage(f32),
/// Multiplies crit chance of attacks
CriticalChance(f32),
/// Changes body.
BodyChange(Body),
}
/// Actual de/buff.
/// Buff can timeout after some time if `time` is Some. If `time` is None,
/// Buff will last indefinitely, until removed manually (by some action, like
/// uncursing).
///
/// Buff has a kind, which is used to determine the effects in a builder
/// function.
///
/// To provide more classification info when needed,
/// buff can be in one or more buff category.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Buff {
pub kind: BuffKind,
pub data: BuffData,
pub cat_ids: Vec<BuffCategory>,
pub end_time: Option<Time>,
pub start_time: Time,
pub effects: Vec<BuffEffect>,
pub source: BuffSource,
}
/// Information about whether buff addition or removal was requested.
/// This to implement "on_add" and "on_remove" hooks for constant buffs.
#[derive(Clone, Debug)]
pub enum BuffChange {
/// Adds this buff.
Add(Buff),
/// Removes all buffs with this ID.
RemoveByKind(BuffKind),
/// Removes all buffs with this ID, but not debuffs.
RemoveFromController(BuffKind),
/// Removes buffs of these indices (first vec is for active buffs, second is
/// for inactive buffs), should only be called when buffs expire
RemoveById(Vec<BuffId>),
/// Removes buffs of these categories (first vec is of categories of which
/// all are required, second vec is of categories of which at least one is
/// required, third vec is of categories that will not be removed)
RemoveByCategory {
all_required: Vec<BuffCategory>,
any_required: Vec<BuffCategory>,
none_required: Vec<BuffCategory>,
},
// Refreshes durations of all buffs with this kind
Refresh(BuffKind),
}
#[cfg(not(target_arch = "wasm32"))]
impl Buff {
/// Builder function for buffs
pub fn new(
kind: BuffKind,
data: BuffData,
cat_ids: Vec<BuffCategory>,
source: BuffSource,
time: Time,
stats: Option<&Stats>,
health: Option<&Health>,
) -> Self {
let effects = kind.effects(&data, stats, health);
let start_time = Time(time.0 + data.delay.map_or(0.0, |delay| delay.0));
let end_time = if cat_ids
.iter()

View File

@ -265,6 +265,23 @@ impl TagExampleInfo for ItemTag {
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Effects {
Any(Vec<Effect>),
All(Vec<Effect>),
One(Effect),
}
impl Effects {
pub fn effects(&self) -> &[Effect] {
match self {
Effects::Any(effects) => effects,
Effects::All(effects) => effects,
Effects::One(effect) => std::slice::from_ref(effect),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ItemKind {
/// Something wieldable
@ -275,7 +292,7 @@ pub enum ItemKind {
Glider,
Consumable {
kind: ConsumableKind,
effects: Vec<Effect>,
effects: Effects,
},
Throwable {
kind: Throwable,

View File

@ -1046,6 +1046,7 @@ impl TradePricing {
} else if let ItemKind::Consumable { kind: _, effects } = &*i.kind() {
(
effects
.effects()
.iter()
.map(|e| {
if let crate::effect::Effect::Buff(b) = e {

View File

@ -2,6 +2,8 @@ use serde::{Deserialize, Serialize};
use specs::{Component, DerefFlaggedStorage};
use std::{error::Error, fmt};
use super::Body;
#[derive(Debug)]
#[allow(dead_code)] // TODO: remove once trade sim hits master
pub enum StatChangeError {
@ -48,6 +50,7 @@ impl Error for StatChangeError {}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Stats {
pub name: String,
pub original_body: Body,
pub damage_reduction: f32,
pub poise_reduction: f32,
pub heal_multiplier: f32,
@ -62,9 +65,10 @@ pub struct Stats {
}
impl Stats {
pub fn new(name: String) -> Self {
pub fn new(name: String, body: Body) -> Self {
Self {
name,
original_body: body,
damage_reduction: 0.0,
poise_reduction: 0.0,
heal_multiplier: 1.0,
@ -81,12 +85,14 @@ impl Stats {
/// Creates an empty `Stats` instance - used during character loading from
/// the database
pub fn empty() -> Self { Self::new("".to_string()) }
pub fn empty(body: Body) -> Self { Self::new("".to_string(), body) }
/// Resets temporary modifiers to default values
pub fn reset_temp_modifiers(&mut self) {
// TODO: Figure out how to avoid clone
*self = Self::new(self.name.clone());
let name = std::mem::take(&mut self.name);
let body = self.original_body;
*self = Self::new(name, body);
}
}

View File

@ -111,7 +111,7 @@ impl CharacterBehavior for Data {
}
};
let stats = comp::Stats::new("Summon".to_string());
let stats = comp::Stats::new("Summon".to_string(), body);
let health = self.static_data.summon_info.has_health.then(|| {
let health_level = skill_set

View File

@ -125,10 +125,11 @@ impl<'a> System<'a> for Sys {
}
}
for (entity, buff_comp, mut stat, health, energy, physics_state) in (
for (entity, buff_comp, mut stat, mut body, health, energy, physics_state) in (
&read_data.entities,
&read_data.buffs,
&mut stats,
&mut bodies,
&read_data.healths,
&read_data.energies,
read_data.physics_states.maybe(),
@ -330,6 +331,8 @@ impl<'a> System<'a> for Sys {
// Call to reset stats to base values
stat.reset_temp_modifiers();
let mut body_override = stat.original_body;
// Iterator over the lists of buffs by kind
let mut buff_kinds = buff_comp
.kinds
@ -368,6 +371,7 @@ impl<'a> System<'a> for Sys {
kind_start_time,
&read_data,
&mut stat,
&mut body_override,
health,
energy,
entity,
@ -382,6 +386,11 @@ impl<'a> System<'a> for Sys {
}
}
// Update body if needed.
if body_override != *body {
*body = body_override;
}
// Remove buffs that expire
if !expired_buffs.is_empty() {
server_emitter.emit(ServerEvent::Buff {
@ -414,6 +423,7 @@ fn execute_effect(
buff_kind_start_time: Time,
read_data: &ReadData,
stat: &mut Stats,
body_override: &mut Body,
health: &Health,
energy: &Energy,
entity: Entity,
@ -592,5 +602,6 @@ fn execute_effect(
BuffEffect::CriticalChance(cc) => {
stat.crit_chance_modifier *= *cc;
},
BuffEffect::BodyChange(b) => *body_override = *b,
};
}

View File

@ -69,7 +69,7 @@ mod tests {
.with(Poise::new(body))
.with(skill_set)
.with(PhysicsState::default())
.with(Stats::empty())
.with(Stats::empty(body))
.with(Uid(1))
.build()
}

View File

@ -133,7 +133,7 @@ pub fn create_player(state: &mut State) -> Entity {
))
.with(Health::new(body, body.base_health()))
.with(skill_set)
.with(Stats::empty())
.with(Stats::empty(body))
.build()
}

View File

@ -15,11 +15,10 @@ use common::{
comp::{
self,
agent::{Sound, SoundKind, Target},
buff::BuffKind,
inventory::slot::EquipSlot,
item::{
tool::{AbilitySpec, ToolKind},
ConsumableKind, Item, ItemDesc, ItemKind,
ConsumableKind, Effects, Item, ItemDesc, ItemKind,
},
item_drop,
projectile::ProjectileConstructor,
@ -541,39 +540,74 @@ impl<'a> AgentData<'a> {
if heal_multiplier < 0.5 {
return false;
}
// (healing_value, heal_reduction)
let effect_healing_value = |effect: &Effect| -> (f32, f32) {
let mut value = 0.0;
let mut heal_reduction = 0.0;
match effect {
Effect::Health(HealthChange { amount, .. }) => {
value += *amount;
},
Effect::Buff(BuffEffect { kind, data, .. }) => {
if let Some(duration) = data.duration {
for effect in kind.effects(data, self.stats, self.health) {
match effect {
comp::BuffEffect::HealthChangeOverTime { rate, kind, .. } => {
let amount = match kind {
comp::ModifierKind::Additive => rate * duration.0 as f32,
comp::ModifierKind::Fractional => {
(1.0 + rate).powf(duration.0 as f32)
},
};
value += amount;
},
comp::BuffEffect::HealReduction(amount) => {
heal_reduction =
heal_reduction + amount - heal_reduction * amount;
},
_ => {},
}
}
value += data.strength * data.duration.map_or(0.0, |d| d.0 as f32);
}
},
_ => {},
}
(value, heal_reduction)
};
let healing_value = |item: &Item| {
let mut value = 0.0;
let mut causes_potion_sickness = false;
let mut heal_multiplier_value = 1.0;
if let ItemKind::Consumable { kind, effects, .. } = &*item.kind() {
if matches!(kind, ConsumableKind::Drink)
|| (relaxed && matches!(kind, ConsumableKind::Food))
{
match effects {
Effects::Any(effects) => {
// Add the average of all effects.
for effect in effects.iter() {
use BuffKind::*;
match effect {
Effect::Health(HealthChange { amount, .. }) => {
value += *amount;
},
Effect::Buff(BuffEffect { kind, data, .. })
if matches!(kind, Regeneration | Saturation | Potion) =>
{
value += data.strength * data.duration.map_or(0.0, |d| d.0 as f32);
},
Effect::Buff(BuffEffect { kind, .. })
if matches!(kind, PotionSickness) =>
{
causes_potion_sickness = true;
},
_ => {},
let (add, red) = effect_healing_value(effect);
value += add / effects.len() as f32;
heal_multiplier_value *= 1.0 - red / effects.len() as f32;
}
},
Effects::All(_) | Effects::One(_) => {
for effect in effects.effects() {
let (add, red) = effect_healing_value(effect);
value += add;
heal_multiplier_value *= 1.0 - red;
}
},
}
}
}
// Prefer non-potion sources of healing when under at least one stack of potion
// sickness, or when incurring potion sickness is unnecessary
if causes_potion_sickness && (heal_multiplier < 1.0 || relaxed) {
if heal_multiplier_value < 1.0 && (heal_multiplier < 1.0 || relaxed) {
value *= 0.1;
}
value as i32

View File

@ -51,7 +51,7 @@ pub fn create_character(
.active_offhand(character_offhand.map(|x| Item::new_from_asset_expect(&x)))
.build();
let mut inventory = Inventory::with_loadout_humanoid(loadout);
let stats = Stats::new(character_alias.to_string());
let stats = Stats::new(character_alias.to_string(), body);
let skill_set = SkillSet::default();
// Default items for new characters
inventory

View File

@ -1220,7 +1220,7 @@ fn handle_spawn(
.state
.create_npc(
pos,
comp::Stats::new(get_npc_name(id, npc::BodyType::from_body(body))),
comp::Stats::new(get_npc_name(id, npc::BodyType::from_body(body)), body),
comp::SkillSet::default(),
Some(comp::Health::new(body, 0)),
comp::Poise::new(body),
@ -1294,7 +1294,7 @@ fn handle_spawn_training_dummy(
let body = comp::Body::Object(comp::object::Body::TrainingDummy);
let stats = comp::Stats::new("Training Dummy".to_string());
let stats = comp::Stats::new("Training Dummy".to_string(), body);
let skill_set = comp::SkillSet::default();
let health = comp::Health::new(body, 0);
let poise = comp::Poise::new(body);
@ -3823,6 +3823,15 @@ fn handle_body(
insert_or_replace_component(server, target, body.mass(), "mass")?;
insert_or_replace_component(server, target, body.density(), "density")?;
insert_or_replace_component(server, target, body.collider(), "collider")?;
if let Some(mut stat) = server
.state
.ecs_mut()
.write_storage::<comp::Stats>()
.get_mut(target)
{
stat.original_body = body;
}
Ok(())
} else {
Err(action.help_string())

View File

@ -1,5 +1,5 @@
use hashbrown::HashSet;
use rand::Rng;
use rand::{seq::IteratorRandom, Rng};
use specs::{join::Join, world::WorldExt, Builder, Entity as EcsEntity, WriteStorage};
use tracing::{debug, error, warn};
use vek::{Rgb, Vec3};
@ -542,9 +542,21 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
drop(inventories);
if let Some(effects) = maybe_effect {
match effects {
item::Effects::Any(effects) => {
if let Some(effect) = effects.into_iter().choose(&mut rand::thread_rng()) {
state.apply_effect(entity, effect, None);
}
},
item::Effects::All(effects) => {
for effect in effects {
state.apply_effect(entity, effect, None);
}
},
item::Effects::One(effect) => {
state.apply_effect(entity, effect, None);
},
}
}
if let Some(event) = event {
state
@ -950,7 +962,10 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
});
},
item::Throwable::TrainingDummy => {
new_entity = new_entity.with(comp::Stats::new("Training Dummy".to_string()));
new_entity = new_entity.with(comp::Stats::new(
"Training Dummy".to_string(),
Body::Object(common::comp::object::Body::TrainingDummy),
));
},
};

View File

@ -231,7 +231,7 @@ pub fn load_character_data(
let pet = comp::Pet::new_from_database(
NonZeroU64::new(db_pet.database_id as u64).unwrap(),
);
let pet_stats = comp::Stats::new(db_pet.name.to_owned());
let pet_stats = comp::Stats::new(db_pet.name.to_owned(), pet_body);
Some((pet, pet_body, pet_stats))
} else {
warn!(
@ -259,10 +259,11 @@ pub fn load_character_data(
let (skill_set, skill_set_persistence_load_error) =
convert_skill_set_from_database(&skill_group_data);
let body = convert_body_from_database(&body_data.variant, &body_data.body_data)?;
Ok((
PersistedComponents {
body: convert_body_from_database(&body_data.variant, &body_data.body_data)?,
stats: convert_stats_from_database(character_data.alias),
body,
stats: convert_stats_from_database(character_data.alias, body),
skill_set,
inventory: convert_inventory_from_database_items(
character_containers.inventory_container_id,

View File

@ -600,8 +600,8 @@ pub fn convert_character_from_database(character: &Character) -> common::charact
}
}
pub fn convert_stats_from_database(alias: String) -> Stats {
let mut new_stats = Stats::empty();
pub fn convert_stats_from_database(alias: String, body: Body) -> Stats {
let mut new_stats = Stats::empty(body);
new_stats.name = alias;
new_stats
}

View File

@ -356,7 +356,7 @@ impl StateExt for State {
// TODO: some of these are required in order for the character_behavior system to
// recognize a possesed airship; that system should be refactored to use `.maybe()`
.with(comp::Energy::new(ship.into(), 0))
.with(comp::Stats::new("Airship".to_string()))
.with(comp::Stats::new("Airship".to_string(), body))
.with(comp::SkillSet::default())
.with(comp::ActiveAbilities::default())
.with(comp::Combo::default());

View File

@ -454,7 +454,7 @@ impl NpcData {
}
let name = name.unwrap_or_else(|| "Unnamed".to_string());
let stats = comp::Stats::new(name);
let stats = comp::Stats::new(name, body);
let skill_set = {
let skillset_builder = SkillSetBuilder::default();

View File

@ -111,7 +111,8 @@ pub fn localize_chat_message(
| BuffKind::Ensnared
| BuffKind::Poisoned
| BuffKind::Parried
| BuffKind::PotionSickness => {
| BuffKind::PotionSickness
| BuffKind::Polymorphed(_) => {
tracing::error!("Player was killed by a debuff that doesn't do damage!");
"hud-outcome-mysterious"
},

View File

@ -531,6 +531,15 @@ impl MusicMgr {
soundtrack.tracks.push(track)
}
},
_ => {
for track in SoundtrackCollection::load_expect("voxygen.audio.soundtrack")
.read()
.tracks
.clone()
{
soundtrack.tracks.push(track)
}
},
}
}
}
@ -615,6 +624,7 @@ mod tests {
"voxygen.audio.calendar.christmas.soundtrack",
);
},
_ => {},
}
}
}

View File

@ -754,6 +754,7 @@ image_ids! {
debuff_poisoned_0: "voxygen.element.de_buffs.debuff_poisoned_0",
debuff_parried_0: "voxygen.element.de_buffs.debuff_parried_0",
debuff_potionsickness_0: "voxygen.element.de_buffs.debuff_potionsickness_0",
debuff_polymorphed: "voxygen.element.de_buffs.debuff_polymorphed",
// Animation Frames
// Buff Frame

View File

@ -4946,6 +4946,7 @@ pub fn get_buff_image(buff: BuffKind, imgs: &Imgs) -> conrod_core::image::Id {
BuffKind::Poisoned => imgs.debuff_poisoned_0,
BuffKind::Parried => imgs.debuff_parried_0,
BuffKind::PotionSickness => imgs.debuff_potionsickness_0,
BuffKind::Polymorphed(_) => imgs.debuff_polymorphed,
}
}
@ -4980,6 +4981,7 @@ pub fn get_buff_title(buff: BuffKind, localized_strings: &Localization) -> Cow<s
BuffKind::Poisoned { .. } => localized_strings.get_msg("buff-title-poisoned"),
BuffKind::Parried { .. } => localized_strings.get_msg("buff-title-parried"),
BuffKind::PotionSickness { .. } => localized_strings.get_msg("buff-title-potionsickness"),
BuffKind::Polymorphed { .. } => localized_strings.get_msg("buff-title-polymorphed"),
}
}
@ -5018,6 +5020,7 @@ pub fn get_buff_desc(buff: BuffKind, data: BuffData, localized_strings: &Localiz
BuffKind::Poisoned { .. } => localized_strings.get_msg("buff-desc-poisoned"),
BuffKind::Parried { .. } => localized_strings.get_msg("buff-desc-parried"),
BuffKind::PotionSickness { .. } => localized_strings.get_msg("buff-desc-potionsickness"),
BuffKind::Polymorphed { .. } => localized_strings.get_msg("buff-desc-polymorphed"),
}
}

View File

@ -5,7 +5,7 @@ use common::{
item::{
armor::{Armor, ArmorKind, Protection},
tool::{Hands, Tool, ToolKind},
ItemDefinitionId, ItemDesc, ItemKind, MaterialKind, MaterialStatManifest,
Effects, ItemDefinitionId, ItemDesc, ItemKind, MaterialKind, MaterialStatManifest,
},
BuffKind,
},
@ -112,7 +112,10 @@ pub fn stats_count(item: &dyn ItemDesc, msm: &MaterialStatManifest) -> usize {
+ (item.num_slots() > 0) as usize
},
ItemKind::Tool(_) => 7,
ItemKind::Consumable { effects, .. } => effects.len(),
ItemKind::Consumable { effects, .. } => match effects {
Effects::Any(_) | Effects::One(_) => 1,
Effects::All(effects) => effects.len(),
},
ItemKind::ModularComponent { .. } => 7,
_ => 0,
}
@ -138,10 +141,14 @@ pub fn line_count(item: &dyn ItemDesc, msm: &MaterialStatManifest, i18n: &Locali
///
/// FIXME: handle which effects should have description in `stats_count`
/// to not waste space in item box
pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> Vec<String> {
pub fn consumable_desc(effects: &Effects, i18n: &Localization) -> Vec<String> {
let mut descriptions = Vec::new();
for effect in effects {
match effects {
Effects::Any(_) => {
descriptions.push(i18n.get_msg("buff-mysterious").into_owned());
},
Effects::All(_) | Effects::One(_) => {
for effect in effects.effects() {
let mut description = String::new();
if let Effect::Buff(buff) = effect {
let strength = buff.data.strength;
@ -191,21 +198,26 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> Vec<String> {
| BuffKind::Hastened
| BuffKind::Fortitude
| BuffKind::Parried
| BuffKind::Reckless => Cow::Borrowed(""),
| BuffKind::Reckless
| BuffKind::Polymorphed(_) => Cow::Borrowed(""),
};
write!(&mut description, "{}", buff_desc).unwrap();
let dur_desc = if let Some(dur_secs) = dur_secs {
match buff.kind {
BuffKind::Saturation | BuffKind::Regeneration | BuffKind::EnergyRegen => i18n
.get_msg_ctx("buff-text-over_seconds", &i18n::fluent_args! {
BuffKind::Saturation
| BuffKind::Regeneration
| BuffKind::EnergyRegen => {
i18n.get_msg_ctx("buff-text-over_seconds", &i18n::fluent_args! {
"dur_secs" => dur_secs
}),
})
},
BuffKind::IncreaseMaxEnergy
| BuffKind::IncreaseMaxHealth
| BuffKind::Invulnerability
| BuffKind::PotionSickness => {
| BuffKind::PotionSickness
| BuffKind::Polymorphed(_) => {
i18n.get_msg_ctx("buff-text-for_seconds", &i18n::fluent_args! {
"dur_secs" => dur_secs
})
@ -227,8 +239,9 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> Vec<String> {
| BuffKind::Parried
| BuffKind::Reckless => Cow::Borrowed(""),
}
} else if let BuffKind::Saturation | BuffKind::Regeneration | BuffKind::EnergyRegen =
buff.kind
} else if let BuffKind::Saturation
| BuffKind::Regeneration
| BuffKind::EnergyRegen = buff.kind
{
i18n.get_msg("buff-text-every_second")
} else {
@ -239,7 +252,8 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> Vec<String> {
}
descriptions.push(description);
}
},
}
descriptions
}

View File

@ -1375,6 +1375,41 @@ impl ParticleMgr {
},
);
},
BuffKind::Polymorphed(_) => {
let mut multiplicity = 0;
// Only show particles for polymorph at the beginning, after the
// drinking animation finishes
if buff_ids.0
.iter()
.filter_map(|id| buffs.buffs.get(id))
.any(|buff| {
matches!(buff.elapsed(Time(time)), dur if (0.1..=0.3).contains(&dur.0))
})
{
multiplicity = 1;
}
self.particles.resize_with(
self.particles.len()
+ multiplicity
* self.scheduler.heartbeats(Duration::from_millis(3)) as usize,
|| {
let start_pos = pos + Vec3::unit_z() * body.eye_height() / 2.0;
let end_pos = start_pos
+ Vec3::<f32>::zero()
.map(|_| rng.gen_range(-1.0..1.0))
.normalized()
* 5.0;
Particle::new_directed(
Duration::from_secs(2),
time,
ParticleMode::Explosion,
start_pos,
end_pos,
)
},
)
},
_ => {},
}
}

View File

@ -171,6 +171,10 @@ pub fn spawn_manifest() -> Vec<(&'static str, DensityFn)> {
"world.wildlife.spawn.calendar.halloween.tundra.core",
|c, _col| close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * 0.5,
),
(
"world.wildlife.spawn.calendar.april_fools.tundra.core",
|c, _col| close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * 0.5,
),
// Snowy animals
("world.wildlife.spawn.tundra.snow", |c, col| {
close(c.temp, CONFIG.snow_temp, 0.3) * BASE_DENSITY * col.snow_cover as i32 as f32 * 1.0
@ -194,6 +198,15 @@ pub fn spawn_manifest() -> Vec<(&'static str, DensityFn)> {
* 1.0
},
),
(
"world.wildlife.spawn.calendar.april_fools.tundra.snow",
|c, col| {
close(c.temp, CONFIG.snow_temp, 0.3)
* BASE_DENSITY
* col.snow_cover as i32 as f32
* 1.0
},
),
// Forest animals
("world.wildlife.spawn.tundra.forest", |c, col| {
close(c.temp, CONFIG.snow_temp, 0.3) * col.tree_density * BASE_DENSITY * 1.4
@ -207,6 +220,10 @@ pub fn spawn_manifest() -> Vec<(&'static str, DensityFn)> {
"world.wildlife.spawn.calendar.halloween.tundra.forest",
|c, col| close(c.temp, CONFIG.snow_temp, 0.3) * col.tree_density * BASE_DENSITY * 1.4,
),
(
"world.wildlife.spawn.calendar.april_fools.tundra.forest",
|c, col| close(c.temp, CONFIG.snow_temp, 0.3) * col.tree_density * BASE_DENSITY * 1.4,
),
// **Taiga**
// Forest core animals
("world.wildlife.spawn.taiga.core_forest", |c, col| {
@ -225,6 +242,12 @@ pub fn spawn_manifest() -> Vec<(&'static str, DensityFn)> {
close(c.temp, CONFIG.snow_temp + 0.2, 0.2) * col.tree_density * BASE_DENSITY * 0.4
},
),
(
"world.wildlife.spawn.calendar.april_fools.taiga.core",
|c, col| {
close(c.temp, CONFIG.snow_temp + 0.2, 0.2) * col.tree_density * BASE_DENSITY * 0.4
},
),
// Core animals
("world.wildlife.spawn.taiga.core", |c, _col| {
close(c.temp, CONFIG.snow_temp + 0.2, 0.2) * BASE_DENSITY * 1.0
@ -276,6 +299,15 @@ pub fn spawn_manifest() -> Vec<(&'static str, DensityFn)> {
* 4.0
},
),
(
"world.wildlife.spawn.calendar.april_fools.temperate.rainforest",
|c, _col| {
close(c.temp, CONFIG.temperate_temp + 0.1, 0.6)
* close(c.humidity, CONFIG.forest_hum, 0.6)
* BASE_DENSITY
* 4.0
},
),
// Water animals
("world.wildlife.spawn.temperate.water", |c, col| {
close(c.temp, CONFIG.temperate_temp, 1.0) * col.tree_density * BASE_DENSITY * 5.0
@ -305,6 +337,15 @@ pub fn spawn_manifest() -> Vec<(&'static str, DensityFn)> {
* 8.0
},
),
(
"world.wildlife.spawn.calendar.april_fools.jungle.area",
|c, _col| {
close(c.temp, CONFIG.tropical_temp + 0.2, 0.3)
* close(c.humidity, CONFIG.jungle_hum, 0.2)
* BASE_DENSITY
* 8.0
},
),
// **Tropical**
// Rare river animals
("world.wildlife.spawn.tropical.river_rare", |_c, col| {
@ -361,6 +402,15 @@ pub fn spawn_manifest() -> Vec<(&'static str, DensityFn)> {
* 2.0
},
),
(
"world.wildlife.spawn.calendar.april_fools.tropical.rainforest",
|c, _col| {
close(c.temp, CONFIG.tropical_temp + 0.1, 0.4)
* close(c.humidity, CONFIG.desert_hum, 0.4)
* BASE_DENSITY
* 2.0
},
),
// Rock animals
("world.wildlife.spawn.tropical.rock", |c, col| {
close(c.temp, CONFIG.tropical_temp + 0.1, 0.5) * col.rock_density * BASE_DENSITY * 5.0