Merge branch 'tame_and_mount_special_casing' into 'master'

Special case taming and mounting.

See merge request veloren/veloren!3354
This commit is contained in:
Samuel Keiffer 2022-05-24 02:09:45 +00:00
commit b9c324a183
7 changed files with 100 additions and 36 deletions

View File

@ -1,6 +1,7 @@
use crate::{make_case_elim, make_proj_elim};
use rand::{seq::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize};
use strum::{Display, EnumString};
make_proj_elim!(
body,
@ -31,7 +32,9 @@ impl From<Body> for super::Body {
make_case_elim!(
species,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(
Copy, Clone, Debug, Display, EnumString, PartialEq, Eq, Hash, Serialize, Deserialize,
)]
#[repr(u32)]
pub enum Species {
Duck = 0,
@ -98,7 +101,9 @@ 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, Display, EnumString, PartialEq, Eq, Hash, Serialize, Deserialize,
)]
#[repr(u32)]
pub enum BodyType {
Female = 0,

View File

@ -1,4 +1,4 @@
use crate::comp::body::Body;
use crate::comp::{body::Body, phys::Mass, quadruped_low, quadruped_medium, quadruped_small};
use crossbeam_utils::atomic::AtomicCell;
use serde::{Deserialize, Serialize};
use specs::Component;
@ -40,10 +40,62 @@ pub fn is_tameable(body: &Body) -> bool {
// Currently only Quadruped animals can be tamed pending further work
// on the pets feature (allowing larger animals to be tamed will
// require balance issues to be addressed).
matches!(
body,
Body::QuadrupedLow(_) | Body::QuadrupedMedium(_) | Body::QuadrupedSmall(_)
)
match body {
Body::QuadrupedMedium(quad_med) =>
// NOTE: the reason we ban mammoth from being tameable even though they're
// agressive anyway, is that UncomfySilence is going to make them
// peaceful after this MR gets merged. Please, remove this note in your MR,
// UncomfySilence!
{
!matches!(
quad_med.species,
quadruped_medium::Species::Catoblepas
| quadruped_medium::Species::Mammoth
| quadruped_medium::Species::Hirdrasil
)
},
Body::QuadrupedLow(_) | Body::QuadrupedSmall(_) | Body::BirdMedium(_) => true,
_ => false,
}
}
pub fn is_mountable(mount: &Body, rider: Option<&Body>) -> bool {
let is_light_enough =
|rider: Option<&Body>| -> bool { rider.map_or(false, |b| b.mass() <= Mass(500.0)) };
match mount {
Body::QuadrupedMedium(body) => match body.species {
quadruped_medium::Species::Alpaca
| quadruped_medium::Species::Antelope
| quadruped_medium::Species::Bear
| quadruped_medium::Species::Camel
| quadruped_medium::Species::Cattle
| quadruped_medium::Species::Deer
| quadruped_medium::Species::Donkey
| quadruped_medium::Species::Highland
| quadruped_medium::Species::Horse
| quadruped_medium::Species::Kelpie
| quadruped_medium::Species::Llama
| quadruped_medium::Species::Moose
| quadruped_medium::Species::Tuskram
| quadruped_medium::Species::Yak
| quadruped_medium::Species::Zebra => true,
quadruped_medium::Species::Mouflon => is_light_enough(rider),
_ => false,
},
Body::QuadrupedSmall(body) => match body.species {
quadruped_small::Species::Truffler => true,
quadruped_small::Species::Boar | quadruped_small::Species::Holladon => {
is_light_enough(rider)
},
_ => false,
},
Body::QuadrupedLow(body) => matches!(
body.species,
quadruped_low::Species::Salamander | quadruped_low::Species::Tortoise
),
_ => false,
}
}
impl Component for Pet {

View File

@ -81,7 +81,7 @@ impl Component for Scale {
}
// Mass
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct Mass(pub f32);
impl Default for Mass {

View File

@ -1,5 +1,6 @@
use crate::{
comp,
comp::{pet::is_mountable, Body},
link::{Is, Link, LinkHandle, Role},
terrain::TerrainGrid,
uid::{Uid, UidAllocator},
@ -38,6 +39,7 @@ impl Link for Mounting {
Read<'a, UidAllocator>,
WriteStorage<'a, Is<Mount>>,
WriteStorage<'a, Is<Rider>>,
WriteStorage<'a, Body>,
);
type DeleteData<'a> = (
Read<'a, UidAllocator>,
@ -58,7 +60,7 @@ impl Link for Mounting {
fn create(
this: &LinkHandle<Self>,
(uid_allocator, mut is_mounts, mut is_riders): Self::CreateData<'_>,
(uid_allocator, mut is_mounts, mut is_riders, body): Self::CreateData<'_>,
) -> Result<(), Self::Error> {
let entity = |uid: Uid| uid_allocator.retrieve_entity_internal(uid.into());
@ -66,15 +68,23 @@ impl Link for Mounting {
// Forbid self-mounting
Err(MountingError::NotMountable)
} else if let Some((mount, rider)) = entity(this.mount).zip(entity(this.rider)) {
let can_mount_with =
|entity| is_mounts.get(entity).is_none() && is_riders.get(entity).is_none();
if let Some(mount_body) = body.get(mount) {
if is_mountable(mount_body, body.get(rider)) {
let can_mount_with =
|entity| is_mounts.get(entity).is_none() && is_riders.get(entity).is_none();
// Ensure that neither mount or rider are already part of a mounting
// relationship
if can_mount_with(mount) && can_mount_with(rider) {
let _ = is_mounts.insert(mount, this.make_role());
let _ = is_riders.insert(rider, this.make_role());
Ok(())
// Ensure that neither mount or rider are already part of a mounting
// relationship
if can_mount_with(mount) && can_mount_with(rider) {
let _ = is_mounts.insert(mount, this.make_role());
let _ = is_riders.insert(rider, this.make_role());
Ok(())
} else {
Err(MountingError::NotMountable)
}
} else {
Err(MountingError::NotMountable)
}
} else {
Err(MountingError::NotMountable)
}

View File

@ -209,6 +209,10 @@ pub fn convert_body_to_database_json(
"quadruped_small",
serde_json::to_string(&GenericBody::from(body))?,
),
common::comp::Body::BirdMedium(body) => (
"bird_medium",
serde_json::to_string(&GenericBody::from(body))?,
),
_ => {
return Err(PersistenceError::ConversionError(format!(
"Unsupported body type for persistence: {:?}",
@ -577,6 +581,9 @@ pub fn convert_body_from_database(
"quadruped_small" => {
deserialize_body!(body_data, QuadrupedSmall, quadruped_small)
},
"bird_medium" => {
deserialize_body!(body_data, BirdMedium, bird_medium)
},
_ => {
return Err(PersistenceError::ConversionError(format!(
"{} is not a supported body type for deserialization",

View File

@ -59,6 +59,7 @@ macro_rules! generic_body_from_impl {
generic_body_from_impl!(comp::quadruped_low::Body);
generic_body_from_impl!(comp::quadruped_medium::Body);
generic_body_from_impl!(comp::quadruped_small::Body);
generic_body_from_impl!(comp::bird_medium::Body);
#[derive(Serialize, Deserialize)]
pub struct CharacterPosition {

View File

@ -82,6 +82,7 @@ use common::{
fluid_dynamics,
inventory::{slot::InvSlotId, trade_pricing::TradePricing},
item::{tool::ToolKind, ItemDesc, MaterialStatManifest, Quality},
pet::is_mountable,
skillset::{skills::Skill, SkillGroupKind},
BuffData, BuffKind, Item, MapMarkerChange,
},
@ -1929,7 +1930,8 @@ impl Hud {
_,
health,
_,
height_offset,
scale,
body,
hpfl,
in_group,
dist_sqr,
@ -2034,23 +2036,10 @@ impl Hud {
} else {
None
};
(info.is_some() || bubble.is_some()).then(|| {
(
entity,
pos,
info,
bubble,
stats,
skill_set,
health,
buffs,
body.height() * scale.map_or(1.0, |s| s.0) + 0.5,
hpfl,
in_group,
dist_sqr,
alignment,
is_mount,
entity, pos, info, bubble, stats, skill_set, health, buffs, scale,
body, hpfl, in_group, dist_sqr, alignment, is_mount,
)
})
},
@ -2060,10 +2049,9 @@ impl Hud {
&mut self.ids.overheads,
&mut ui_widgets.widget_id_generator(),
);
let ingame_pos = pos + Vec3::unit_z() * height_offset;
//
// * height_offset
let height_offset = body.height() * scale.map_or(1.0, |s| s.0) + 0.5;
let ingame_pos = pos + Vec3::unit_z() * height_offset;
// Speech bubble, name, level, and hp bars
overhead::Overhead::new(
@ -2093,6 +2081,7 @@ impl Hud {
if Some(*owner) == client.uid()
&& !client.is_riding()
&& is_mount.is_none()
&& is_mountable(body, bodies.get(client.entity()))
&& dist_sqr < common::consts::MAX_MOUNT_RANGE.powi(2) =>
{
vec![(GameInput::Mount, i18n.get("hud.mount").to_string())]