mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
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:
commit
b9c324a183
@ -1,6 +1,7 @@
|
|||||||
use crate::{make_case_elim, make_proj_elim};
|
use crate::{make_case_elim, make_proj_elim};
|
||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use strum::{Display, EnumString};
|
||||||
|
|
||||||
make_proj_elim!(
|
make_proj_elim!(
|
||||||
body,
|
body,
|
||||||
@ -31,7 +32,9 @@ impl From<Body> for super::Body {
|
|||||||
|
|
||||||
make_case_elim!(
|
make_case_elim!(
|
||||||
species,
|
species,
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(
|
||||||
|
Copy, Clone, Debug, Display, EnumString, PartialEq, Eq, Hash, Serialize, Deserialize,
|
||||||
|
)]
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
pub enum Species {
|
pub enum Species {
|
||||||
Duck = 0,
|
Duck = 0,
|
||||||
@ -98,7 +101,9 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
|
|||||||
|
|
||||||
make_case_elim!(
|
make_case_elim!(
|
||||||
body_type,
|
body_type,
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(
|
||||||
|
Copy, Clone, Debug, Display, EnumString, PartialEq, Eq, Hash, Serialize, Deserialize,
|
||||||
|
)]
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
pub enum BodyType {
|
pub enum BodyType {
|
||||||
Female = 0,
|
Female = 0,
|
||||||
|
@ -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 crossbeam_utils::atomic::AtomicCell;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specs::Component;
|
use specs::Component;
|
||||||
@ -40,10 +40,62 @@ pub fn is_tameable(body: &Body) -> bool {
|
|||||||
// Currently only Quadruped animals can be tamed pending further work
|
// Currently only Quadruped animals can be tamed pending further work
|
||||||
// on the pets feature (allowing larger animals to be tamed will
|
// on the pets feature (allowing larger animals to be tamed will
|
||||||
// require balance issues to be addressed).
|
// require balance issues to be addressed).
|
||||||
matches!(
|
match body {
|
||||||
body,
|
Body::QuadrupedMedium(quad_med) =>
|
||||||
Body::QuadrupedLow(_) | Body::QuadrupedMedium(_) | Body::QuadrupedSmall(_)
|
// 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 {
|
impl Component for Pet {
|
||||||
|
@ -81,7 +81,7 @@ impl Component for Scale {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mass
|
// Mass
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||||
pub struct Mass(pub f32);
|
pub struct Mass(pub f32);
|
||||||
|
|
||||||
impl Default for Mass {
|
impl Default for Mass {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
comp,
|
comp,
|
||||||
|
comp::{pet::is_mountable, Body},
|
||||||
link::{Is, Link, LinkHandle, Role},
|
link::{Is, Link, LinkHandle, Role},
|
||||||
terrain::TerrainGrid,
|
terrain::TerrainGrid,
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
@ -38,6 +39,7 @@ impl Link for Mounting {
|
|||||||
Read<'a, UidAllocator>,
|
Read<'a, UidAllocator>,
|
||||||
WriteStorage<'a, Is<Mount>>,
|
WriteStorage<'a, Is<Mount>>,
|
||||||
WriteStorage<'a, Is<Rider>>,
|
WriteStorage<'a, Is<Rider>>,
|
||||||
|
WriteStorage<'a, Body>,
|
||||||
);
|
);
|
||||||
type DeleteData<'a> = (
|
type DeleteData<'a> = (
|
||||||
Read<'a, UidAllocator>,
|
Read<'a, UidAllocator>,
|
||||||
@ -58,7 +60,7 @@ impl Link for Mounting {
|
|||||||
|
|
||||||
fn create(
|
fn create(
|
||||||
this: &LinkHandle<Self>,
|
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> {
|
) -> Result<(), Self::Error> {
|
||||||
let entity = |uid: Uid| uid_allocator.retrieve_entity_internal(uid.into());
|
let entity = |uid: Uid| uid_allocator.retrieve_entity_internal(uid.into());
|
||||||
|
|
||||||
@ -66,15 +68,23 @@ impl Link for Mounting {
|
|||||||
// Forbid self-mounting
|
// Forbid self-mounting
|
||||||
Err(MountingError::NotMountable)
|
Err(MountingError::NotMountable)
|
||||||
} else if let Some((mount, rider)) = entity(this.mount).zip(entity(this.rider)) {
|
} else if let Some((mount, rider)) = entity(this.mount).zip(entity(this.rider)) {
|
||||||
let can_mount_with =
|
if let Some(mount_body) = body.get(mount) {
|
||||||
|entity| is_mounts.get(entity).is_none() && is_riders.get(entity).is_none();
|
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
|
// Ensure that neither mount or rider are already part of a mounting
|
||||||
// relationship
|
// relationship
|
||||||
if can_mount_with(mount) && can_mount_with(rider) {
|
if can_mount_with(mount) && can_mount_with(rider) {
|
||||||
let _ = is_mounts.insert(mount, this.make_role());
|
let _ = is_mounts.insert(mount, this.make_role());
|
||||||
let _ = is_riders.insert(rider, this.make_role());
|
let _ = is_riders.insert(rider, this.make_role());
|
||||||
Ok(())
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(MountingError::NotMountable)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(MountingError::NotMountable)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(MountingError::NotMountable)
|
Err(MountingError::NotMountable)
|
||||||
}
|
}
|
||||||
|
@ -209,6 +209,10 @@ pub fn convert_body_to_database_json(
|
|||||||
"quadruped_small",
|
"quadruped_small",
|
||||||
serde_json::to_string(&GenericBody::from(body))?,
|
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!(
|
return Err(PersistenceError::ConversionError(format!(
|
||||||
"Unsupported body type for persistence: {:?}",
|
"Unsupported body type for persistence: {:?}",
|
||||||
@ -577,6 +581,9 @@ pub fn convert_body_from_database(
|
|||||||
"quadruped_small" => {
|
"quadruped_small" => {
|
||||||
deserialize_body!(body_data, QuadrupedSmall, quadruped_small)
|
deserialize_body!(body_data, QuadrupedSmall, quadruped_small)
|
||||||
},
|
},
|
||||||
|
"bird_medium" => {
|
||||||
|
deserialize_body!(body_data, BirdMedium, bird_medium)
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
return Err(PersistenceError::ConversionError(format!(
|
return Err(PersistenceError::ConversionError(format!(
|
||||||
"{} is not a supported body type for deserialization",
|
"{} is not a supported body type for deserialization",
|
||||||
|
@ -59,6 +59,7 @@ macro_rules! generic_body_from_impl {
|
|||||||
generic_body_from_impl!(comp::quadruped_low::Body);
|
generic_body_from_impl!(comp::quadruped_low::Body);
|
||||||
generic_body_from_impl!(comp::quadruped_medium::Body);
|
generic_body_from_impl!(comp::quadruped_medium::Body);
|
||||||
generic_body_from_impl!(comp::quadruped_small::Body);
|
generic_body_from_impl!(comp::quadruped_small::Body);
|
||||||
|
generic_body_from_impl!(comp::bird_medium::Body);
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct CharacterPosition {
|
pub struct CharacterPosition {
|
||||||
|
@ -82,6 +82,7 @@ use common::{
|
|||||||
fluid_dynamics,
|
fluid_dynamics,
|
||||||
inventory::{slot::InvSlotId, trade_pricing::TradePricing},
|
inventory::{slot::InvSlotId, trade_pricing::TradePricing},
|
||||||
item::{tool::ToolKind, ItemDesc, MaterialStatManifest, Quality},
|
item::{tool::ToolKind, ItemDesc, MaterialStatManifest, Quality},
|
||||||
|
pet::is_mountable,
|
||||||
skillset::{skills::Skill, SkillGroupKind},
|
skillset::{skills::Skill, SkillGroupKind},
|
||||||
BuffData, BuffKind, Item, MapMarkerChange,
|
BuffData, BuffKind, Item, MapMarkerChange,
|
||||||
},
|
},
|
||||||
@ -1929,7 +1930,8 @@ impl Hud {
|
|||||||
_,
|
_,
|
||||||
health,
|
health,
|
||||||
_,
|
_,
|
||||||
height_offset,
|
scale,
|
||||||
|
body,
|
||||||
hpfl,
|
hpfl,
|
||||||
in_group,
|
in_group,
|
||||||
dist_sqr,
|
dist_sqr,
|
||||||
@ -2034,23 +2036,10 @@ impl Hud {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
(info.is_some() || bubble.is_some()).then(|| {
|
(info.is_some() || bubble.is_some()).then(|| {
|
||||||
(
|
(
|
||||||
entity,
|
entity, pos, info, bubble, stats, skill_set, health, buffs, scale,
|
||||||
pos,
|
body, hpfl, in_group, dist_sqr, alignment, is_mount,
|
||||||
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,
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -2060,10 +2049,9 @@ impl Hud {
|
|||||||
&mut self.ids.overheads,
|
&mut self.ids.overheads,
|
||||||
&mut ui_widgets.widget_id_generator(),
|
&mut ui_widgets.widget_id_generator(),
|
||||||
);
|
);
|
||||||
let ingame_pos = pos + Vec3::unit_z() * height_offset;
|
|
||||||
|
|
||||||
//
|
let height_offset = body.height() * scale.map_or(1.0, |s| s.0) + 0.5;
|
||||||
// * height_offset
|
let ingame_pos = pos + Vec3::unit_z() * height_offset;
|
||||||
|
|
||||||
// Speech bubble, name, level, and hp bars
|
// Speech bubble, name, level, and hp bars
|
||||||
overhead::Overhead::new(
|
overhead::Overhead::new(
|
||||||
@ -2093,6 +2081,7 @@ impl Hud {
|
|||||||
if Some(*owner) == client.uid()
|
if Some(*owner) == client.uid()
|
||||||
&& !client.is_riding()
|
&& !client.is_riding()
|
||||||
&& is_mount.is_none()
|
&& is_mount.is_none()
|
||||||
|
&& is_mountable(body, bodies.get(client.entity()))
|
||||||
&& dist_sqr < common::consts::MAX_MOUNT_RANGE.powi(2) =>
|
&& dist_sqr < common::consts::MAX_MOUNT_RANGE.powi(2) =>
|
||||||
{
|
{
|
||||||
vec![(GameInput::Mount, i18n.get("hud.mount").to_string())]
|
vec![(GameInput::Mount, i18n.get("hud.mount").to_string())]
|
||||||
|
Loading…
Reference in New Issue
Block a user