1use crate::{comp::Alignment, uid::Uid};
2use hashbrown::HashMap;
3use serde::{Deserialize, Serialize};
4use slab::Slab;
5use specs::{Component, DerefFlaggedStorage, Join, LendJoin, storage::GenericReadStorage};
6use std::iter;
7use tracing::{error, warn};
8
9#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
19pub struct Group(u32);
20
21pub const ENEMY: Group = Group(u32::MAX);
24pub const NPC: Group = Group(u32::MAX - 1);
26
27impl Component for Group {
28 type Storage = DerefFlaggedStorage<Self, specs::VecStorage<Self>>;
29}
30
31#[derive(Clone, Debug)]
32pub struct GroupInfo {
33 pub leader: specs::Entity,
36 pub num_members: u32,
38 pub name: String,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
43pub enum Role {
44 Member,
45 Pet,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub enum ChangeNotification<E> {
50 Added(E, Role),
52 Removed(E),
54 NewLeader(E),
55 NewGroup { leader: E, members: Vec<(E, Role)> },
57 NoGroup,
59}
60impl<E> ChangeNotification<E> {
65 pub fn try_map_ref<T>(&self, f: impl Fn(&E) -> Option<T>) -> Option<ChangeNotification<T>> {
66 match self {
67 Self::Added(e, r) => f(e).map(|t| ChangeNotification::Added(t, *r)),
68 Self::Removed(e) => f(e).map(ChangeNotification::Removed),
69 Self::NewLeader(e) => f(e).map(ChangeNotification::NewLeader),
70 Self::NewGroup { leader, members } => {
72 f(leader).map(|leader| ChangeNotification::NewGroup {
73 leader,
74 members: members
75 .iter()
76 .filter_map(|(e, r)| f(e).map(|t| (t, *r)))
77 .collect(),
78 })
79 },
80 Self::NoGroup => Some(ChangeNotification::NoGroup),
81 }
82 }
83}
84
85type GroupsMut<'a> = specs::WriteStorage<'a, Group>;
86type Alignments<'a> = specs::ReadStorage<'a, Alignment>;
87type Uids<'a> = specs::ReadStorage<'a, Uid>;
88
89#[derive(Debug, Default)]
90pub struct GroupManager {
91 groups: Slab<GroupInfo>,
92}
93
94fn pets(
98 entity: specs::Entity,
99 uid: Uid,
100 alignments: &Alignments,
101 entities: &specs::world::EntitiesRes,
102) -> Vec<specs::Entity> {
103 (entities, alignments)
104 .join()
105 .filter_map(|(e, a)| {
106 matches!(a, Alignment::Owned(owner) if *owner == uid && e != entity).then_some(e)
107 })
108 .collect::<Vec<_>>()
109}
110
111pub fn members<'a>(
113 group: Group,
114 groups: impl Join<Type = &'a Group> + 'a,
115 entities: &'a specs::world::EntitiesRes,
116 alignments: &'a Alignments,
117 uids: &'a Uids,
118) -> impl Iterator<Item = (specs::Entity, Role)> + 'a {
119 (entities, groups, alignments, uids)
120 .join()
121 .filter(move |&(_e, g, _a, _u)| (*g == group))
122 .map(|(e, _g, a, u)| {
123 (
124 e,
125 if matches!(a, Alignment::Owned(owner) if owner != u) {
126 Role::Pet
127 } else {
128 Role::Member
129 },
130 )
131 })
132}
133
134impl GroupManager {
136 pub fn group_info(&self, group: Group) -> Option<&GroupInfo> {
137 self.groups.get(group.0 as usize)
138 }
139
140 fn group_info_mut(&mut self, group: Group) -> Option<&mut GroupInfo> {
141 self.groups.get_mut(group.0 as usize)
142 }
143
144 fn create_group(&mut self, leader: specs::Entity, num_members: u32) -> Group {
145 Group(self.groups.insert(GroupInfo {
146 leader,
147 num_members,
148 name: "Group".into(),
149 }) as u32)
150 }
151
152 fn remove_group(&mut self, group: Group) { self.groups.remove(group.0 as usize); }
153
154 pub fn add_group_member(
157 &mut self,
158 leader: specs::Entity,
159 new_member: specs::Entity,
160 entities: &specs::Entities,
161 groups: &mut GroupsMut,
162 alignments: &Alignments,
163 uids: &Uids,
164 mut notifier: impl FnMut(specs::Entity, ChangeNotification<specs::Entity>),
165 ) {
166 if leader == new_member {
168 warn!("Attempt to form group with leader as the only member (this is disallowed)");
169 return;
170 }
171
172 let new_member_uid = if let Some(uid) = uids.get(new_member) {
174 *uid
175 } else {
176 error!("Failed to retrieve uid for the new group member");
177 return;
178 };
179
180 if groups
182 .get(new_member)
183 .and_then(|g| self.group_info(*g))
184 .is_some()
185 {
186 self.leave_group(
187 new_member,
188 groups,
189 alignments,
190 uids,
191 entities,
192 &mut notifier,
193 )
194 }
195
196 let group = match groups.get(leader).copied() {
197 Some(id)
198 if self
199 .group_info(id)
200 .map(|info| info.leader == leader)
201 .unwrap_or(false) =>
202 {
203 Some(id)
204 },
205 Some(_) => {
208 self.leave_group(leader, groups, alignments, uids, entities, &mut notifier);
209 None
210 },
211 None => None,
212 };
213
214 let group = if let Some(group) = group {
215 self.group_info_mut(group).unwrap().num_members += 1;
218 group
219 } else {
220 let new_group = self.create_group(leader, 2);
221 groups.insert(leader, new_group).unwrap();
224 notifier(leader, ChangeNotification::NewLeader(leader));
226 new_group
227 };
228
229 let new_pets = pets(new_member, new_member_uid, alignments, entities);
230
231 members(group, &*groups, entities, alignments, uids).for_each(|(e, role)| match role {
233 Role::Member => {
234 notifier(e, ChangeNotification::Added(new_member, Role::Member));
235 notifier(new_member, ChangeNotification::Added(e, Role::Member));
236
237 new_pets.iter().for_each(|p| {
238 notifier(e, ChangeNotification::Added(*p, Role::Pet));
239 })
240 },
241 Role::Pet => {
242 notifier(new_member, ChangeNotification::Added(e, Role::Pet));
243 },
244 });
245 notifier(new_member, ChangeNotification::NewLeader(leader));
246
247 let _ = groups.insert(new_member, group).unwrap();
252 new_pets.iter().for_each(|e| {
253 let _ = groups.insert(*e, group).unwrap();
254 });
255 }
256
257 pub fn new_pet(
258 &mut self,
259 pet: specs::Entity,
260 owner: specs::Entity,
261 groups: &mut GroupsMut,
262 entities: &specs::Entities,
263 alignments: &Alignments,
264 uids: &Uids,
265 notifier: &mut impl FnMut(specs::Entity, ChangeNotification<specs::Entity>),
266 ) {
267 if !entities.is_alive(owner) {
268 warn!("Tried to create new pet for non-existent owner {owner:?}");
269 } else if !entities.is_alive(pet) {
270 warn!("Tried to create new pet for non-existent pet {pet:?}");
271 } else {
272 let group = match groups.get(owner).copied() {
273 Some(group) => group,
274 None => {
275 let new_group = self.create_group(owner, 1);
276 groups.insert(owner, new_group).unwrap();
278 notifier(owner, ChangeNotification::NewLeader(owner));
280 new_group
281 },
282 };
283
284 members(group, &*groups, entities, alignments, uids).for_each(|(e, role)| match role {
286 Role::Member => {
287 notifier(e, ChangeNotification::Added(pet, Role::Pet));
288 },
289 Role::Pet => {},
290 });
291
292 groups.insert(pet, group).unwrap();
295 }
296 }
297
298 pub fn leave_group(
299 &mut self,
300 member: specs::Entity,
301 groups: &mut GroupsMut,
302 alignments: &Alignments,
303 uids: &Uids,
304 entities: &specs::Entities,
305 notifier: &mut impl FnMut(specs::Entity, ChangeNotification<specs::Entity>),
306 ) {
307 if matches!(alignments.get(member), Some(Alignment::Owned(uid)) if uids.get(member) != Some(uid))
309 {
310 return;
311 }
312 self.remove_from_group(member, groups, alignments, uids, entities, notifier, false);
313 }
314
315 pub fn entity_deleted(
316 &mut self,
317 member: specs::Entity,
318 groups: &mut GroupsMut,
319 alignments: &Alignments,
320 uids: &Uids,
321 entities: &specs::world::EntitiesRes,
322 notifier: &mut impl FnMut(specs::Entity, ChangeNotification<specs::Entity>),
323 ) {
324 self.remove_from_group(member, groups, alignments, uids, entities, notifier, true);
325 }
326
327 fn remove_from_group(
331 &mut self,
332 member: specs::Entity,
333 groups: &mut GroupsMut,
334 alignments: &Alignments,
335 uids: &Uids,
336 entities: &specs::world::EntitiesRes,
337 notifier: &mut impl FnMut(specs::Entity, ChangeNotification<specs::Entity>),
338 to_be_deleted: bool,
339 ) {
340 let group = match groups.get(member) {
341 Some(group) => *group,
342 None => return,
343 };
344
345 if self
347 .group_info(group)
348 .map(|info| info.leader == member)
349 .unwrap_or(false)
350 {
351 self.remove_group(group);
353
354 (entities, uids, &*groups, alignments.maybe())
355 .join()
356 .filter(|(e, _, g, _)| **g == group && !(to_be_deleted && *e == member))
357 .fold(
358 HashMap::<Uid, (Option<specs::Entity>, Option<Alignment>, Vec<specs::Entity>)>::new(),
359 |mut acc, (e, uid, _, alignment)| {
360 if let Some(owner) = alignment.and_then(|a| match a {
361 Alignment::Owned(owner) if uid != owner => Some(owner),
362 _ => None,
363 }) {
364 acc.entry(*owner).or_default().2.push(e);
367 } else {
368 let entry = acc.entry(*uid).or_default();
372 entry.0 = Some(e);
373 entry.1 = alignment.copied();
374 }
375
376 acc
377 },
378 )
379 .into_iter()
380 .map(|(_, v)| v)
381 .for_each(|(owner, alignment, pets)| {
382 if let Some(owner) = owner {
383 if let Some(special_group) = alignment.and_then(|a| a.group()) {
384 for entity in iter::once(owner).chain(pets) {
386 groups.insert(entity, special_group)
387 .expect("entity from join above so it must exist");
388 }
389 } else if !pets.is_empty() {
390 let mut members =
391 pets.iter().map(|e| (*e, Role::Pet)).collect::<Vec<_>>();
392 members.push((owner, Role::Member));
393
394 let new_group = self.create_group(owner, 1);
396 for (member, _) in &members {
397 groups.insert(*member, new_group).unwrap();
398 }
399
400 notifier(owner, ChangeNotification::NewGroup {
401 leader: owner,
402 members,
403 });
404 } else {
405 groups.remove(owner);
407 notifier(owner, ChangeNotification::NoGroup)
408 }
409 } else {
410 pets.into_iter().for_each(|pet| {
412 groups.remove(pet);
413 });
414 }
415 });
416 } else {
418 let leaving_member_uid = if let Some(uid) = uids.get(member) {
419 *uid
420 } else {
421 error!("Failed to retrieve uid for the leaving member");
422 return;
423 };
424
425 let leaving_pets = pets(member, leaving_member_uid, alignments, entities);
426
427 if let Some(special_group) = alignments.get(member).and_then(|a| a.group())
429 && !to_be_deleted
430 {
431 for entity in iter::once(member).chain(leaving_pets.iter().copied()) {
433 groups
434 .insert(entity, special_group)
435 .expect("entity from join above so it must exist");
436 }
437 } else if !leaving_pets.is_empty()
442 && !to_be_deleted
443 && alignments.get(member).and_then(Alignment::group).is_none()
444 {
445 let new_group = self.create_group(member, 1);
446
447 notifier(member, ChangeNotification::NewGroup {
448 leader: member,
449 members: leaving_pets
450 .iter()
451 .map(|p| (*p, Role::Pet))
452 .chain(iter::once((member, Role::Member)))
453 .collect(),
454 });
455
456 let _ = groups.insert(member, new_group).unwrap();
457 leaving_pets.iter().for_each(|&e| {
458 let _ = groups.insert(e, new_group).unwrap();
459 });
460 } else {
461 let _ = groups.remove(member);
462 notifier(member, ChangeNotification::NoGroup);
463 leaving_pets.iter().for_each(|&e| {
464 let _ = groups.remove(e);
465 });
466 }
467
468 if let Some(info) = self.group_info_mut(group) {
471 if !matches!(alignments.get(member), Some(Alignment::Owned(owner)) if uids.get(member) != Some(owner))
473 {
474 if info.num_members > 0 {
475 info.num_members -= 1;
476 } else {
477 error!("Group with invalid number of members")
478 }
479 }
480
481 let mut remaining_count = 0; members(group, &*groups, entities, alignments, uids).for_each(|(e, role)| {
484 remaining_count += 1;
485 match role {
486 Role::Member => {
487 notifier(e, ChangeNotification::Removed(member));
488 leaving_pets.iter().for_each(|p| {
489 notifier(e, ChangeNotification::Removed(*p));
490 })
491 },
492 Role::Pet => {},
493 }
494 });
495 if remaining_count == 1 {
498 let leader = info.leader;
499 self.remove_group(group);
500 groups.remove(leader);
501 notifier(leader, ChangeNotification::NoGroup);
502 } else if remaining_count == 0 {
503 error!("Somehow group has no members")
504 }
505 }
506 }
507 }
508
509 pub fn assign_leader<'a>(
512 &mut self,
513 new_leader: specs::Entity,
514 groups: impl GenericReadStorage<Component = Group> + Join<Type = &'a Group> + 'a,
515 entities: &'a specs::Entities,
516 alignments: &'a Alignments,
517 uids: &'a Uids,
518 mut notifier: impl FnMut(specs::Entity, ChangeNotification<specs::Entity>),
519 ) {
520 let group = match groups.get(new_leader) {
521 Some(group) => *group,
522 None => return,
523 };
524
525 self.groups[group.0 as usize].leader = new_leader;
527
528 members(group, groups, entities, alignments, uids).for_each(|(e, role)| match role {
530 Role::Member => notifier(e, ChangeNotification::NewLeader(new_leader)),
531 Role::Pet => {},
532 });
533 }
534}
535
536impl Group {
537 pub fn is_special(&self) -> bool { *self == NPC || *self == ENEMY }
540}