package groups import ( "fmt" "time" ) // Group utility methods // GetGroupSize returns the size of a group func (m *Manager) GetGroupSize(groupID int32) int32 { group := m.GetGroup(groupID) if group == nil { return 0 } return group.GetSize() } // IsInGroup checks if an entity is in a specific group func (m *Manager) IsInGroup(groupID int32, member Entity) bool { group := m.GetGroup(groupID) if group == nil || member == nil { return false } members := group.GetMembers() for _, gmi := range members { if gmi.Member == member { return true } } return false } // IsPlayerInGroup checks if a player with the given character ID is in a group func (m *Manager) IsPlayerInGroup(groupID int32, charID int32) Entity { group := m.GetGroup(groupID) if group == nil { return nil } members := group.GetMembers() for _, gmi := range members { if gmi.IsClient && gmi.Member != nil { // TODO: Check character ID // if gmi.Member.GetCharacterID() == charID { // return gmi.Member // } } } return nil } // IsSpawnInGroup checks if a spawn with the given name is in a group func (m *Manager) IsSpawnInGroup(groupID int32, name string) bool { group := m.GetGroup(groupID) if group == nil { return false } members := group.GetMembers() for _, gmi := range members { if gmi.Name == name { return true } } return false } // GetGroupLeader returns the leader of a group func (m *Manager) GetGroupLeader(groupID int32) Entity { group := m.GetGroup(groupID) if group == nil { return nil } members := group.GetMembers() for _, gmi := range members { if gmi.Leader { return gmi.Member } } return nil } // MakeLeader changes the leader of a group func (m *Manager) MakeLeader(groupID int32, newLeader Entity) bool { group := m.GetGroup(groupID) if group == nil { return false } err := group.MakeLeader(newLeader) return err == nil } // Group messaging // SimpleGroupMessage sends a simple message to all members of a group func (m *Manager) SimpleGroupMessage(groupID int32, message string) { group := m.GetGroup(groupID) if group != nil { group.SimpleGroupMessage(message) } } // SendGroupMessage sends a formatted message to all members of a group func (m *Manager) SendGroupMessage(groupID int32, msgType int8, message string) { group := m.GetGroup(groupID) if group != nil { group.SendGroupMessage(msgType, message) } } // GroupMessage sends a message to all members of a group (alias for SimpleGroupMessage) func (m *Manager) GroupMessage(groupID int32, message string) { m.SimpleGroupMessage(groupID, message) } // GroupChatMessage sends a chat message from a member to the group func (m *Manager) GroupChatMessage(groupID int32, from Entity, language int32, message string, channel int16) { group := m.GetGroup(groupID) if group != nil { group.GroupChatMessage(from, language, message, channel) } } // GroupChatMessageFromName sends a chat message from a named sender to the group func (m *Manager) GroupChatMessageFromName(groupID int32, fromName string, language int32, message string, channel int16) { group := m.GetGroup(groupID) if group != nil { group.GroupChatMessageFromName(fromName, language, message, channel) } } // SendGroupChatMessage sends a formatted chat message to the group func (m *Manager) SendGroupChatMessage(groupID int32, channel int16, message string) { m.GroupChatMessageFromName(groupID, "System", 0, message, channel) } // Raid functionality // ClearGroupRaid clears raid associations for a group func (m *Manager) ClearGroupRaid(groupID int32) { group := m.GetGroup(groupID) if group != nil { group.ClearGroupRaid() } } // RemoveGroupFromRaid removes a group from a raid func (m *Manager) RemoveGroupFromRaid(groupID, targetGroupID int32) { group := m.GetGroup(groupID) if group != nil { group.RemoveGroupFromRaid(targetGroupID) } } // IsInRaidGroup checks if two groups are in the same raid func (m *Manager) IsInRaidGroup(groupID, targetGroupID int32, isLeaderGroup bool) bool { group := m.GetGroup(groupID) if group == nil { return false } return group.IsInRaidGroup(targetGroupID, isLeaderGroup) } // GetRaidGroups returns the raid groups for a specific group func (m *Manager) GetRaidGroups(groupID int32) []int32 { group := m.GetGroup(groupID) if group == nil { return []int32{} } return group.GetRaidGroups() } // ReplaceRaidGroups replaces the raid groups for a specific group func (m *Manager) ReplaceRaidGroups(groupID int32, newGroups []int32) { group := m.GetGroup(groupID) if group != nil { group.ReplaceRaidGroups(newGroups) } } // Group options // GetDefaultGroupOptions returns the default group options for a group func (m *Manager) GetDefaultGroupOptions(groupID int32) (GroupOptions, bool) { group := m.GetGroup(groupID) if group == nil { return GroupOptions{}, false } return group.GetGroupOptions(), true } // SetGroupOptions sets group options for a specific group func (m *Manager) SetGroupOptions(groupID int32, options *GroupOptions) error { group := m.GetGroup(groupID) if group == nil { return fmt.Errorf("group %d not found", groupID) } return group.SetGroupOptions(options) } // Background processing loops // updateGroupsLoop periodically updates all groups func (m *Manager) updateGroupsLoop() { defer m.wg.Done() ticker := time.NewTicker(m.Config.UpdateInterval) defer ticker.Stop() for { select { case <-ticker.C: m.processGroupUpdates() case <-m.stopChan: return } } } // updateBuffsLoop periodically updates group buffs func (m *Manager) updateBuffsLoop() { defer m.wg.Done() ticker := time.NewTicker(m.Config.BuffUpdateInterval) defer ticker.Stop() for { select { case <-ticker.C: m.updateGroupBuffs() case <-m.stopChan: return } } } // cleanupExpiredInvitesLoop periodically cleans up expired invites func (m *Manager) cleanupExpiredInvitesLoop() { defer m.wg.Done() ticker := time.NewTicker(30 * time.Second) // Check every 30 seconds defer ticker.Stop() for { select { case <-ticker.C: m.cleanupExpiredInvites() case <-m.stopChan: return } } } // updateStatsLoop periodically updates statistics func (m *Manager) updateStatsLoop() { defer m.wg.Done() ticker := time.NewTicker(1 * time.Minute) // Update stats every minute defer ticker.Stop() for { select { case <-ticker.C: m.updateStatistics() case <-m.stopChan: return } } } // processGroupUpdates processes periodic group updates func (m *Manager) processGroupUpdates() { groups := m.GetAllGroups() for _, group := range groups { if !group.IsDisbanded() { // Update member information members := group.GetMembers() for _, gmi := range members { if gmi.Member != nil { group.UpdateGroupMemberInfo(gmi.Member, false) } } } } } // updateGroupBuffs updates group buffs for all groups func (m *Manager) updateGroupBuffs() { // TODO: Implement group buff updates // This would require integration with the spell/buff system } // cleanupExpiredInvites removes expired invitations func (m *Manager) cleanupExpiredInvites() { m.invitesMutex.Lock() defer m.invitesMutex.Unlock() now := time.Now() expiredCount := 0 // Clean up regular invites for key, invite := range m.PendingInvites { if now.After(invite.ExpiresTime) { delete(m.PendingInvites, key) expiredCount++ } } // Clean up raid invites for key, invite := range m.RaidPendingInvites { if now.After(invite.ExpiresTime) { delete(m.RaidPendingInvites, key) expiredCount++ } } // Update statistics if expiredCount > 0 { m.statsMutex.Lock() m.Stats.ExpiredInvites += int64(expiredCount) m.statsMutex.Unlock() } } // updateStatistics updates manager statistics func (m *Manager) updateStatistics() { if !m.Config.EnableStatistics { return } m.statsMutex.Lock() defer m.statsMutex.Unlock() activeGroups := m.MasterList.GetActiveGroups() raidGroups := m.MasterList.GetRaidGroups() var totalMembers int64 var raidMembers int64 for _, group := range activeGroups { totalMembers += int64(group.GetSize()) if group.IsGroupRaid() { raidMembers += int64(group.GetSize()) } } m.Stats.ActiveGroups = int64(len(activeGroups)) m.Stats.ActiveRaids = int64(len(raidGroups)) if len(activeGroups) > 0 { m.Stats.AverageGroupSize = float64(totalMembers) / float64(len(activeGroups)) } else { m.Stats.AverageGroupSize = 0 } m.Stats.LastStatsUpdate = time.Now() } // Statistics update methods // updateStatsForNewGroup updates statistics when a new group is created func (m *Manager) updateStatsForNewGroup() { if !m.Config.EnableStatistics { return } m.statsMutex.Lock() defer m.statsMutex.Unlock() m.Stats.TotalGroups++ } // updateStatsForRemovedGroup updates statistics when a group is removed func (m *Manager) updateStatsForRemovedGroup() { // Statistics are primarily tracked in updateStatistics() } // updateStatsForInvite updates statistics when an invite is sent func (m *Manager) updateStatsForInvite() { if !m.Config.EnableStatistics { return } m.statsMutex.Lock() defer m.statsMutex.Unlock() m.Stats.TotalInvites++ } // updateStatsForAcceptedInvite updates statistics when an invite is accepted func (m *Manager) updateStatsForAcceptedInvite() { if !m.Config.EnableStatistics { return } m.statsMutex.Lock() defer m.statsMutex.Unlock() m.Stats.AcceptedInvites++ } // updateStatsForDeclinedInvite updates statistics when an invite is declined func (m *Manager) updateStatsForDeclinedInvite() { if !m.Config.EnableStatistics { return } m.statsMutex.Lock() defer m.statsMutex.Unlock() m.Stats.DeclinedInvites++ } // updateStatsForExpiredInvite updates statistics when an invite expires func (m *Manager) updateStatsForExpiredInvite() { if !m.Config.EnableStatistics { return } m.statsMutex.Lock() defer m.statsMutex.Unlock() m.Stats.ExpiredInvites++ } // Event system integration // AddEventHandler adds an event handler func (m *Manager) AddEventHandler(handler GroupEventHandler) { m.eventHandlersMutex.Lock() defer m.eventHandlersMutex.Unlock() m.EventHandlers = append(m.EventHandlers, handler) } // Integration interfaces // SetDatabase sets the database interface func (m *Manager) SetDatabase(db GroupDatabase) { m.database = db } // SetPacketHandler sets the packet handler interface func (m *Manager) SetPacketHandler(handler GroupPacketHandler) { m.packetHandler = handler } // SetValidator sets the validator interface func (m *Manager) SetValidator(validator GroupValidator) { m.validator = validator } // SetNotifier sets the notifier interface func (m *Manager) SetNotifier(notifier GroupNotifier) { m.notifier = notifier } // Event firing methods // fireGroupCreatedEvent fires a group created event func (m *Manager) fireGroupCreatedEvent(group *Group, leader Entity) { m.eventHandlersMutex.RLock() defer m.eventHandlersMutex.RUnlock() for _, handler := range m.EventHandlers { go handler.OnGroupCreated(group, leader) } } // fireGroupDisbandedEvent fires a group disbanded event func (m *Manager) fireGroupDisbandedEvent(group *Group) { m.eventHandlersMutex.RLock() defer m.eventHandlersMutex.RUnlock() for _, handler := range m.EventHandlers { go handler.OnGroupDisbanded(group) } } // fireGroupInviteSentEvent fires a group invite sent event func (m *Manager) fireGroupInviteSentEvent(leader, member Entity) { m.eventHandlersMutex.RLock() defer m.eventHandlersMutex.RUnlock() for _, handler := range m.EventHandlers { go handler.OnGroupInviteSent(leader, member) } } // fireGroupInviteAcceptedEvent fires a group invite accepted event func (m *Manager) fireGroupInviteAcceptedEvent(leader, member Entity, groupID int32) { m.eventHandlersMutex.RLock() defer m.eventHandlersMutex.RUnlock() for _, handler := range m.EventHandlers { go handler.OnGroupInviteAccepted(leader, member, groupID) } } // fireGroupInviteDeclinedEvent fires a group invite declined event func (m *Manager) fireGroupInviteDeclinedEvent(leader, member Entity) { m.eventHandlersMutex.RLock() defer m.eventHandlersMutex.RUnlock() for _, handler := range m.EventHandlers { go handler.OnGroupInviteDeclined(leader, member) } }