mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Fixes and tweaks for groups
This commit is contained in:
parent
d9e3937a82
commit
0a8f148559
@ -1021,6 +1021,13 @@ impl Client {
|
||||
NewGroup { leader, members } => {
|
||||
self.group_leader = Some(leader);
|
||||
self.group_members = members.into_iter().collect();
|
||||
// Currently add/remove messages treat client as an implicit member
|
||||
// of the group whereas this message explicitly included them so to
|
||||
// be consistent for now we will remove the client from the
|
||||
// received hashset
|
||||
if let Some(uid) = self.uid() {
|
||||
self.group_members.remove(&uid);
|
||||
}
|
||||
},
|
||||
NoGroup => {
|
||||
self.group_leader = None;
|
||||
|
@ -89,7 +89,9 @@ fn with_pets(
|
||||
) -> Vec<specs::Entity> {
|
||||
let mut list = (entities, alignments)
|
||||
.join()
|
||||
.filter_map(|(e, a)| matches!(a, Alignment::Owned(owner) if *owner == uid).then_some(e))
|
||||
.filter_map(|(e, a)| {
|
||||
matches!(a, Alignment::Owned(owner) if *owner == uid && e != entity).then_some(e)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
list.push(entity);
|
||||
list
|
||||
@ -145,8 +147,12 @@ impl GroupManager {
|
||||
};
|
||||
|
||||
// If new member is a member of a different group remove that
|
||||
if groups.get(new_member).is_some() {
|
||||
self.remove_from_group(
|
||||
if groups
|
||||
.get(new_member)
|
||||
.and_then(|g| self.group_info(*g))
|
||||
.is_some()
|
||||
{
|
||||
self.leave_group(
|
||||
new_member,
|
||||
groups,
|
||||
alignments,
|
||||
@ -168,7 +174,7 @@ impl GroupManager {
|
||||
// Member of an existing group can't be a leader
|
||||
// If the lead is a member of another group leave that group first
|
||||
Some(_) => {
|
||||
self.remove_from_group(leader, groups, alignments, uids, entities, &mut notifier);
|
||||
self.leave_group(leader, groups, alignments, uids, entities, &mut notifier);
|
||||
None
|
||||
},
|
||||
None => None,
|
||||
@ -238,10 +244,7 @@ impl GroupManager {
|
||||
}
|
||||
}
|
||||
|
||||
// Remove someone from a group if they are in one
|
||||
// Don't need to check if they are in a group before calling this
|
||||
// Also removes pets (ie call this if the pet no longer exists)
|
||||
pub fn remove_from_group(
|
||||
pub fn leave_group(
|
||||
&mut self,
|
||||
member: specs::Entity,
|
||||
groups: &mut GroupsMut,
|
||||
@ -249,6 +252,52 @@ impl GroupManager {
|
||||
uids: &Uids,
|
||||
entities: &specs::Entities,
|
||||
notifier: &mut impl FnMut(specs::Entity, ChangeNotification<specs::Entity>),
|
||||
) {
|
||||
// Pets can't leave
|
||||
if matches!(alignments.get(member), Some(Alignment::Owned(uid)) if uids.get(member).map_or(true, |u| u != uid))
|
||||
{
|
||||
return;
|
||||
}
|
||||
self.remove_from_group(member, groups, alignments, uids, entities, notifier, false);
|
||||
|
||||
// Set NPC back to their group
|
||||
if let Some(alignment) = alignments.get(member) {
|
||||
match alignment {
|
||||
Alignment::Npc => {
|
||||
let _ = groups.insert(member, NPC);
|
||||
},
|
||||
Alignment::Enemy => {
|
||||
let _ = groups.insert(member, ENEMY);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn entity_deleted(
|
||||
&mut self,
|
||||
member: specs::Entity,
|
||||
groups: &mut GroupsMut,
|
||||
alignments: &Alignments,
|
||||
uids: &Uids,
|
||||
entities: &specs::Entities,
|
||||
notifier: &mut impl FnMut(specs::Entity, ChangeNotification<specs::Entity>),
|
||||
) {
|
||||
self.remove_from_group(member, groups, alignments, uids, entities, notifier, true);
|
||||
}
|
||||
|
||||
// Remove someone from a group if they are in one
|
||||
// Don't need to check if they are in a group before calling this
|
||||
// Also removes pets (ie call this if the pet no longer exists)
|
||||
fn remove_from_group(
|
||||
&mut self,
|
||||
member: specs::Entity,
|
||||
groups: &mut GroupsMut,
|
||||
alignments: &Alignments,
|
||||
uids: &Uids,
|
||||
entities: &specs::Entities,
|
||||
notifier: &mut impl FnMut(specs::Entity, ChangeNotification<specs::Entity>),
|
||||
to_be_deleted: bool,
|
||||
) {
|
||||
let group = match groups.get(member) {
|
||||
Some(group) => *group,
|
||||
@ -266,11 +315,14 @@ impl GroupManager {
|
||||
|
||||
(entities, uids, &*groups, alignments.maybe())
|
||||
.join()
|
||||
.filter(|(_, _, g, _)| **g == group)
|
||||
.filter(|(e, _, g, _)| **g == group && (!to_be_deleted || *e == member))
|
||||
.fold(
|
||||
HashMap::<Uid, (Option<specs::Entity>, Vec<specs::Entity>)>::new(),
|
||||
|mut acc, (e, uid, _, alignment)| {
|
||||
if let Some(Alignment::Owned(owner)) = alignment {
|
||||
if let Some(owner) = alignment.and_then(|a| match a {
|
||||
Alignment::Owned(owner) if uid != owner => Some(owner),
|
||||
_ => None,
|
||||
}) {
|
||||
// Assumes owner will be in the group
|
||||
acc.entry(*owner).or_default().1.push(e);
|
||||
} else {
|
||||
@ -309,10 +361,6 @@ impl GroupManager {
|
||||
notifier(owner, ChangeNotification::NoGroup)
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
"Something went wrong! The pet owner is missing from a group that the \
|
||||
pet is in"
|
||||
);
|
||||
pets.into_iter()
|
||||
.for_each(|pet| notifier(pet, ChangeNotification::NoGroup));
|
||||
}
|
||||
@ -328,47 +376,45 @@ impl GroupManager {
|
||||
|
||||
let leaving = with_pets(member, leaving_member_uid, alignments, entities);
|
||||
|
||||
// If pets form new group
|
||||
if leaving.len() > 1 {
|
||||
// If pets and not about to be deleted form new group
|
||||
if leaving.len() > 1 && !to_be_deleted {
|
||||
let new_group = self.create_group(member);
|
||||
|
||||
leaving.iter().for_each(|e| {
|
||||
let _ = groups.insert(*e, new_group).unwrap();
|
||||
});
|
||||
|
||||
let notification = ChangeNotification::NewGroup {
|
||||
leader: member,
|
||||
members: leaving.clone(),
|
||||
};
|
||||
|
||||
leaving
|
||||
.iter()
|
||||
.for_each(|e| notifier(*e, notification.clone()));
|
||||
leaving.iter().for_each(|&e| {
|
||||
let _ = groups.insert(e, new_group).unwrap();
|
||||
notifier(e, notification.clone());
|
||||
});
|
||||
} else {
|
||||
groups.remove(member);
|
||||
notifier(member, ChangeNotification::NoGroup)
|
||||
leaving.iter().for_each(|&e| {
|
||||
let _ = groups.remove(e);
|
||||
notifier(e, ChangeNotification::NoGroup);
|
||||
});
|
||||
}
|
||||
|
||||
// Inform remaining members
|
||||
let mut num_members = 0;
|
||||
members(group, &*groups, entities).for_each(|a| {
|
||||
num_members += 1;
|
||||
leaving.iter().for_each(|b| {
|
||||
notifier(a, ChangeNotification::Removed(*b));
|
||||
})
|
||||
});
|
||||
|
||||
// If leader is the last one left then disband the group
|
||||
// Assumes last member is the leader
|
||||
if num_members == 1 {
|
||||
if let Some(info) = self.group_info(group) {
|
||||
if let Some(info) = self.group_info(group) {
|
||||
// Inform remaining members
|
||||
let mut num_members = 0;
|
||||
members(group, &*groups, entities).for_each(|a| {
|
||||
num_members += 1;
|
||||
leaving.iter().for_each(|b| {
|
||||
notifier(a, ChangeNotification::Removed(*b));
|
||||
})
|
||||
});
|
||||
// If leader is the last one left then disband the group
|
||||
// Assumes last member is the leader
|
||||
if num_members == 1 {
|
||||
let leader = info.leader;
|
||||
self.remove_group(group);
|
||||
groups.remove(leader);
|
||||
notifier(leader, ChangeNotification::NoGroup);
|
||||
} else if num_members == 0 {
|
||||
error!("Somehow group has no members")
|
||||
}
|
||||
} else if num_members == 0 {
|
||||
error!("Somehow group has no members")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -392,7 +438,7 @@ impl GroupManager {
|
||||
|
||||
// Point to new leader
|
||||
members(group, groups, entities).for_each(|e| {
|
||||
notifier(e, ChangeNotification::NewLeader(e));
|
||||
notifier(e, ChangeNotification::NewLeader(new_leader));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -153,7 +153,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
let mut clients = state.ecs().write_storage::<Client>();
|
||||
let uids = state.ecs().read_storage::<sync::Uid>();
|
||||
let mut group_manager = state.ecs().write_resource::<GroupManager>();
|
||||
group_manager.remove_from_group(
|
||||
group_manager.leave_group(
|
||||
entity,
|
||||
&mut state.ecs().write_storage(),
|
||||
&state.ecs().read_storage(),
|
||||
@ -174,6 +174,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
GroupManip::Kick(uid) => {
|
||||
let mut clients = state.ecs().write_storage::<Client>();
|
||||
let uids = state.ecs().read_storage::<sync::Uid>();
|
||||
let alignments = state.ecs().read_storage::<comp::Alignment>();
|
||||
|
||||
let target = match state.ecs().entity_from_uid(uid.into()) {
|
||||
Some(t) => t,
|
||||
@ -188,6 +189,27 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
// Can't kick pet
|
||||
if matches!(alignments.get(target), Some(comp::Alignment::Owned(owner)) if uids.get(target).map_or(true, |u| u != owner))
|
||||
{
|
||||
if let Some(client) = clients.get_mut(entity) {
|
||||
client.notify(
|
||||
ChatType::Meta.server_msg("Kick failed, can't kick pet".to_owned()),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Can't kick yourself
|
||||
if uids.get(entity).map_or(false, |u| *u == uid) {
|
||||
if let Some(client) = clients.get_mut(entity) {
|
||||
client.notify(
|
||||
ChatType::Meta.server_msg("Kick failed, can't kick yourself".to_owned()),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let mut groups = state.ecs().write_storage::<group::Group>();
|
||||
let mut group_manager = state.ecs().write_resource::<GroupManager>();
|
||||
// Make sure kicker is the group leader
|
||||
@ -197,7 +219,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
{
|
||||
Some(info) if info.leader == entity => {
|
||||
// Remove target from group
|
||||
group_manager.remove_from_group(
|
||||
group_manager.leave_group(
|
||||
target,
|
||||
&mut groups,
|
||||
&state.ecs().read_storage(),
|
||||
|
@ -351,7 +351,7 @@ impl StateExt for State {
|
||||
let mut clients = self.ecs().write_storage::<Client>();
|
||||
let uids = self.ecs().read_storage::<Uid>();
|
||||
let mut group_manager = self.ecs().write_resource::<comp::group::GroupManager>();
|
||||
group_manager.remove_from_group(
|
||||
group_manager.entity_deleted(
|
||||
entity,
|
||||
&mut self.ecs().write_storage(),
|
||||
&self.ecs().read_storage(),
|
||||
|
@ -117,6 +117,8 @@ const UI_MAIN: Color = Color::Rgba(0.61, 0.70, 0.70, 1.0); // Greenish Blue
|
||||
const UI_HIGHLIGHT_0: Color = Color::Rgba(0.79, 1.09, 1.09, 1.0);
|
||||
//const UI_DARK_0: Color = Color::Rgba(0.25, 0.37, 0.37, 1.0);
|
||||
|
||||
/// Distance at which nametags are visible for group members
|
||||
const NAMETAG_GROUP_RANGE: f32 = 300.0;
|
||||
/// Distance at which nametags are visible
|
||||
const NAMETAG_RANGE: f32 = 40.0;
|
||||
/// Time nametags stay visible after doing damage even if they are out of range
|
||||
@ -1052,7 +1054,7 @@ impl Hud {
|
||||
}
|
||||
|
||||
// Render overhead name tags and health bars
|
||||
for (pos, name, stats, energy, height_offset, hpfl, uid) in (
|
||||
for (pos, name, stats, energy, height_offset, hpfl, uid, in_group) in (
|
||||
&entities,
|
||||
&pos,
|
||||
interpolated.maybe(),
|
||||
@ -1065,15 +1067,34 @@ impl Hud {
|
||||
&uids,
|
||||
)
|
||||
.join()
|
||||
.filter(|(entity, _, _, stats, _, _, _, _, _, _)| *entity != me && !stats.is_dead
|
||||
.map(|(a, b, c, d, e, f, g, h, i, uid)| {
|
||||
(
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
d,
|
||||
e,
|
||||
f,
|
||||
g,
|
||||
h,
|
||||
i,
|
||||
uid,
|
||||
client.group_members.contains(uid),
|
||||
)
|
||||
})
|
||||
.filter(|(entity, pos, _, stats, _, _, _, _, hpfl, uid, in_group)| {
|
||||
*entity != me && !stats.is_dead
|
||||
&& (stats.health.current() != stats.health.maximum()
|
||||
|| info.target_entity.map_or(false, |e| e == *entity)
|
||||
|| info.selected_entity.map_or(false, |s| s.0 == *entity)
|
||||
))
|
||||
// Don't show outside a certain range
|
||||
.filter(|(_, pos, _, _, _, _, _, _, hpfl, _)| {
|
||||
pos.0.distance_squared(player_pos)
|
||||
< (if hpfl
|
||||
|| *in_group
|
||||
)
|
||||
// Don't show outside a certain range
|
||||
&& pos.0.distance_squared(player_pos)
|
||||
< (if *in_group
|
||||
{
|
||||
NAMETAG_GROUP_RANGE
|
||||
} else if hpfl
|
||||
.time_since_last_dmg_by_me
|
||||
.map_or(false, |t| t < NAMETAG_DMG_TIME)
|
||||
{
|
||||
@ -1083,25 +1104,40 @@ impl Hud {
|
||||
})
|
||||
.powi(2)
|
||||
})
|
||||
.map(|(_, pos, interpolated, stats, energy, player, scale, body, hpfl, uid)| {
|
||||
// TODO: This is temporary
|
||||
// If the player used the default character name display their name instead
|
||||
let name = if stats.name == "Character Name" {
|
||||
player.map_or(&stats.name, |p| &p.alias)
|
||||
} else {
|
||||
&stats.name
|
||||
};
|
||||
(
|
||||
interpolated.map_or(pos.0, |i| i.pos),
|
||||
name,
|
||||
.map(
|
||||
|(
|
||||
_,
|
||||
pos,
|
||||
interpolated,
|
||||
stats,
|
||||
energy,
|
||||
// TODO: when body.height() is more accurate remove the 2.0
|
||||
body.height() * 2.0 * scale.map_or(1.0, |s| s.0),
|
||||
player,
|
||||
scale,
|
||||
body,
|
||||
hpfl,
|
||||
uid,
|
||||
)
|
||||
})
|
||||
in_group,
|
||||
)| {
|
||||
// TODO: This is temporary
|
||||
// If the player used the default character name display their name instead
|
||||
let name = if stats.name == "Character Name" {
|
||||
player.map_or(&stats.name, |p| &p.alias)
|
||||
} else {
|
||||
&stats.name
|
||||
};
|
||||
(
|
||||
interpolated.map_or(pos.0, |i| i.pos),
|
||||
name,
|
||||
stats,
|
||||
energy,
|
||||
// TODO: when body.height() is more accurate remove the 2.0
|
||||
body.height() * 2.0 * scale.map_or(1.0, |s| s.0),
|
||||
hpfl,
|
||||
uid,
|
||||
in_group,
|
||||
)
|
||||
},
|
||||
)
|
||||
{
|
||||
let bubble = self.speech_bubbles.get(uid);
|
||||
|
||||
@ -1118,6 +1154,7 @@ impl Hud {
|
||||
stats,
|
||||
energy,
|
||||
own_level,
|
||||
in_group,
|
||||
&global_state.settings.gameplay,
|
||||
self.pulse,
|
||||
&self.voxygen_i18n,
|
||||
|
@ -56,6 +56,7 @@ pub struct Overhead<'a> {
|
||||
stats: &'a Stats,
|
||||
energy: Option<&'a Energy>,
|
||||
own_level: u32,
|
||||
in_group: bool,
|
||||
settings: &'a GameplaySettings,
|
||||
pulse: f32,
|
||||
voxygen_i18n: &'a std::sync::Arc<VoxygenLocalization>,
|
||||
@ -73,6 +74,7 @@ impl<'a> Overhead<'a> {
|
||||
stats: &'a Stats,
|
||||
energy: Option<&'a Energy>,
|
||||
own_level: u32,
|
||||
in_group: bool,
|
||||
settings: &'a GameplaySettings,
|
||||
pulse: f32,
|
||||
voxygen_i18n: &'a std::sync::Arc<VoxygenLocalization>,
|
||||
@ -85,6 +87,7 @@ impl<'a> Overhead<'a> {
|
||||
stats,
|
||||
energy,
|
||||
own_level,
|
||||
in_group,
|
||||
settings,
|
||||
pulse,
|
||||
voxygen_i18n,
|
||||
@ -145,7 +148,11 @@ impl<'a> Widget for Overhead<'a> {
|
||||
Text::new(&self.name)
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.font_size(30)
|
||||
.color(Color::Rgba(0.61, 0.61, 0.89, 1.0))
|
||||
.color(if self.in_group {
|
||||
Color::Rgba(1.0, 0.5, 0.6, 1.0)
|
||||
} else {
|
||||
Color::Rgba(0.61, 0.61, 0.89, 1.0)
|
||||
})
|
||||
.x_y(0.0, MANA_BAR_Y + 50.0)
|
||||
.set(state.ids.name, ui);
|
||||
|
||||
|
@ -375,6 +375,9 @@ impl<'a> Widget for Social<'a> {
|
||||
{
|
||||
if let Some(uid) = selected {
|
||||
events.push(Event::Invite(uid));
|
||||
state.update(|s| {
|
||||
s.selected_uid = None;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -452,6 +455,9 @@ impl<'a> Widget for Social<'a> {
|
||||
{
|
||||
if let Some(uid) = selected {
|
||||
events.push(Event::Kick(uid));
|
||||
state.update(|s| {
|
||||
s.selected_member = None;
|
||||
});
|
||||
}
|
||||
}
|
||||
// Assign leader
|
||||
@ -474,6 +480,9 @@ impl<'a> Widget for Social<'a> {
|
||||
{
|
||||
if let Some(uid) = selected {
|
||||
events.push(Event::AssignLeader(uid));
|
||||
state.update(|s| {
|
||||
s.selected_member = None;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user