package guilds import ( "fmt" "strings" "time" ) // NewGuild creates a new guild instance func NewGuild() *Guild { guild := &Guild{ members: make(map[int32]*GuildMember), guildEvents: make([]GuildEvent, 0), permissions: make(map[int8]map[int8]int8), eventFilters: make(map[int8]map[int8]int8), recruitingFlags: make(map[int8]int8), recruitingDescTags: make(map[int8]int8), ranks: make(map[int8]string), level: 1, expCurrent: 111, expToNextLevel: 2521, recruitingMinLevel: 1, recruitingPlayStyle: RecruitingPlayStyleNone, nextEventID: 1, lastModified: time.Now(), } // Initialize default recruiting flags guild.recruitingFlags[RecruitingFlagTraining] = 0 guild.recruitingFlags[RecruitingFlagFighters] = 0 guild.recruitingFlags[RecruitingFlagPriests] = 0 guild.recruitingFlags[RecruitingFlagScouts] = 0 guild.recruitingFlags[RecruitingFlagMages] = 0 guild.recruitingFlags[RecruitingFlagTradeskillers] = 0 // Initialize default description tags guild.recruitingDescTags[0] = RecruitingDescTagNone guild.recruitingDescTags[1] = RecruitingDescTagNone guild.recruitingDescTags[2] = RecruitingDescTagNone guild.recruitingDescTags[3] = RecruitingDescTagNone // Initialize default bank names guild.banks[0].Name = "Bank 1" guild.banks[1].Name = "Bank 2" guild.banks[2].Name = "Bank 3" guild.banks[3].Name = "Bank 4" // Initialize default rank names for rank, name := range DefaultRankNames { guild.ranks[rank] = name } return guild } // SetID sets the guild ID func (g *Guild) SetID(id int32) { g.mu.Lock() defer g.mu.Unlock() g.id = id } // SetName sets the guild name func (g *Guild) SetName(name string, sendPacket bool) { g.mu.Lock() defer g.mu.Unlock() if len(name) > MaxGuildNameLength { name = name[:MaxGuildNameLength] } g.name = name g.lastModified = time.Now() if sendPacket { g.saveNeeded = true } } // SetLevel sets the guild level func (g *Guild) SetLevel(level int8, sendPacket bool) { g.mu.Lock() defer g.mu.Unlock() if level > MaxGuildLevel { level = MaxGuildLevel } if level < 1 { level = 1 } g.level = level g.lastModified = time.Now() if sendPacket { g.saveNeeded = true } } // SetFormedDate sets the guild formation date func (g *Guild) SetFormedDate(formedDate time.Time) { g.mu.Lock() defer g.mu.Unlock() g.formedDate = formedDate } // SetMOTD sets the guild message of the day func (g *Guild) SetMOTD(motd string, sendPacket bool) { g.mu.Lock() defer g.mu.Unlock() if len(motd) > MaxMOTDLength { motd = motd[:MaxMOTDLength] } g.motd = motd g.lastModified = time.Now() if sendPacket { g.saveNeeded = true } } // GetID returns the guild ID func (g *Guild) GetID() int32 { g.mu.RLock() defer g.mu.RUnlock() return g.id } // GetName returns the guild name func (g *Guild) GetName() string { g.mu.RLock() defer g.mu.RUnlock() return g.name } // GetLevel returns the guild level func (g *Guild) GetLevel() int8 { g.mu.RLock() defer g.mu.RUnlock() return g.level } // GetFormedDate returns the guild formation date func (g *Guild) GetFormedDate() time.Time { g.mu.RLock() defer g.mu.RUnlock() return g.formedDate } // GetMOTD returns the guild message of the day func (g *Guild) GetMOTD() string { g.mu.RLock() defer g.mu.RUnlock() return g.motd } // SetEXPCurrent sets the current guild experience func (g *Guild) SetEXPCurrent(exp int64, sendPacket bool) { g.mu.Lock() defer g.mu.Unlock() g.expCurrent = exp g.lastModified = time.Now() if sendPacket { g.saveNeeded = true } } // AddEXPCurrent adds experience to the guild func (g *Guild) AddEXPCurrent(exp int64, sendPacket bool) { g.mu.Lock() defer g.mu.Unlock() g.expCurrent += exp g.lastModified = time.Now() if sendPacket { g.saveNeeded = true } } // GetEXPCurrent returns the current guild experience func (g *Guild) GetEXPCurrent() int64 { g.mu.RLock() defer g.mu.RUnlock() return g.expCurrent } // SetEXPToNextLevel sets the experience needed for next level func (g *Guild) SetEXPToNextLevel(exp int64, sendPacket bool) { g.mu.Lock() defer g.mu.Unlock() g.expToNextLevel = exp g.lastModified = time.Now() if sendPacket { g.saveNeeded = true } } // GetEXPToNextLevel returns the experience needed for next level func (g *Guild) GetEXPToNextLevel() int64 { g.mu.RLock() defer g.mu.RUnlock() return g.expToNextLevel } // SetRecruitingShortDesc sets the short recruiting description func (g *Guild) SetRecruitingShortDesc(desc string, sendPacket bool) { g.mu.Lock() defer g.mu.Unlock() g.recruitingShortDesc = desc g.lastModified = time.Now() if sendPacket { g.recruitingSaveNeeded = true } } // GetRecruitingShortDesc returns the short recruiting description func (g *Guild) GetRecruitingShortDesc() string { g.mu.RLock() defer g.mu.RUnlock() return g.recruitingShortDesc } // SetRecruitingFullDesc sets the full recruiting description func (g *Guild) SetRecruitingFullDesc(desc string, sendPacket bool) { g.mu.Lock() defer g.mu.Unlock() if len(desc) > MaxRecruitingDescLength { desc = desc[:MaxRecruitingDescLength] } g.recruitingFullDesc = desc g.lastModified = time.Now() if sendPacket { g.recruitingSaveNeeded = true } } // GetRecruitingFullDesc returns the full recruiting description func (g *Guild) GetRecruitingFullDesc() string { g.mu.RLock() defer g.mu.RUnlock() return g.recruitingFullDesc } // SetRecruitingMinLevel sets the minimum level for recruiting func (g *Guild) SetRecruitingMinLevel(level int8, sendPacket bool) { g.mu.Lock() defer g.mu.Unlock() g.recruitingMinLevel = level g.lastModified = time.Now() if sendPacket { g.recruitingSaveNeeded = true } } // GetRecruitingMinLevel returns the minimum level for recruiting func (g *Guild) GetRecruitingMinLevel() int8 { g.mu.RLock() defer g.mu.RUnlock() return g.recruitingMinLevel } // SetRecruitingPlayStyle sets the recruiting play style func (g *Guild) SetRecruitingPlayStyle(playStyle int8, sendPacket bool) { g.mu.Lock() defer g.mu.Unlock() g.recruitingPlayStyle = playStyle g.lastModified = time.Now() if sendPacket { g.recruitingSaveNeeded = true } } // GetRecruitingPlayStyle returns the recruiting play style func (g *Guild) GetRecruitingPlayStyle() int8 { g.mu.RLock() defer g.mu.RUnlock() return g.recruitingPlayStyle } // SetRecruitingDescTag sets a recruiting description tag func (g *Guild) SetRecruitingDescTag(index, tag int8, sendPacket bool) bool { g.mu.Lock() defer g.mu.Unlock() if index < 0 || index > 3 { return false } g.recruitingDescTags[index] = tag g.lastModified = time.Now() if sendPacket { g.recruitingSaveNeeded = true } return true } // GetRecruitingDescTag returns a recruiting description tag func (g *Guild) GetRecruitingDescTag(index int8) int8 { g.mu.RLock() defer g.mu.RUnlock() if tag, exists := g.recruitingDescTags[index]; exists { return tag } return RecruitingDescTagNone } // SetPermission sets a guild permission for a rank func (g *Guild) SetPermission(rank, permission, value int8, sendPacket, saveNeeded bool) bool { g.mu.Lock() defer g.mu.Unlock() if rank < RankLeader || rank > RankRecruit { return false } if g.permissions[rank] == nil { g.permissions[rank] = make(map[int8]int8) } g.permissions[rank][permission] = value g.lastModified = time.Now() if saveNeeded { g.ranksSaveNeeded = true } return true } // GetPermission returns a guild permission for a rank func (g *Guild) GetPermission(rank, permission int8) int8 { g.mu.RLock() defer g.mu.RUnlock() if rankPerms, exists := g.permissions[rank]; exists { if value, exists := rankPerms[permission]; exists { return value } } // Return default permission based on rank return g.getDefaultPermission(rank, permission) } // SetEventFilter sets an event filter func (g *Guild) SetEventFilter(eventID, category, value int8, sendPacket, saveNeeded bool) bool { g.mu.Lock() defer g.mu.Unlock() if g.eventFilters[eventID] == nil { g.eventFilters[eventID] = make(map[int8]int8) } g.eventFilters[eventID][category] = value g.lastModified = time.Now() if saveNeeded { g.eventFiltersSaveNeeded = true } return true } // GetEventFilter returns an event filter func (g *Guild) GetEventFilter(eventID, category int8) int8 { g.mu.RLock() defer g.mu.RUnlock() if eventFilters, exists := g.eventFilters[eventID]; exists { if value, exists := eventFilters[category]; exists { return value } } return 1 // Default to enabled } // GetNumUniqueAccounts returns the number of unique accounts in the guild func (g *Guild) GetNumUniqueAccounts() int32 { g.mu.RLock() defer g.mu.RUnlock() accounts := make(map[int32]bool) for _, member := range g.members { accounts[member.AccountID] = true } return int32(len(accounts)) } // GetNumRecruiters returns the number of recruiters in the guild func (g *Guild) GetNumRecruiters() int32 { g.mu.RLock() defer g.mu.RUnlock() count := int32(0) for _, member := range g.members { if member.MemberFlags&MemberFlagRecruitingForGuild != 0 { count++ } } return count } // GetNextRecruiterID returns the next available recruiter ID func (g *Guild) GetNextRecruiterID() int32 { g.mu.RLock() defer g.mu.RUnlock() maxID := int32(0) for _, member := range g.members { if member.RecruiterID > maxID { maxID = member.RecruiterID } } return maxID + 1 } // GetNextEventID returns the next available event ID func (g *Guild) GetNextEventID() int64 { g.mu.Lock() defer g.mu.Unlock() eventID := g.nextEventID g.nextEventID++ return eventID } // GetGuildMember returns a guild member by character ID func (g *Guild) GetGuildMember(characterID int32) *GuildMember { g.mu.RLock() defer g.mu.RUnlock() return g.members[characterID] } // GetGuildMemberByName returns a guild member by name func (g *Guild) GetGuildMemberByName(playerName string) *GuildMember { g.mu.RLock() defer g.mu.RUnlock() for _, member := range g.members { if strings.EqualFold(member.Name, playerName) { return member } } return nil } // GetGuildRecruiters returns all guild recruiters func (g *Guild) GetGuildRecruiters() []*GuildMember { g.mu.RLock() defer g.mu.RUnlock() var recruiters []*GuildMember for _, member := range g.members { if member.MemberFlags&MemberFlagRecruitingForGuild != 0 { recruiters = append(recruiters, member) } } return recruiters } // GetGuildEvent returns a guild event by ID func (g *Guild) GetGuildEvent(eventID int64) *GuildEvent { g.mu.RLock() defer g.mu.RUnlock() for i := range g.guildEvents { if g.guildEvents[i].EventID == eventID { return &g.guildEvents[i] } } return nil } // SetRankName sets a custom rank name func (g *Guild) SetRankName(rank int8, name string, sendPacket bool) bool { g.mu.Lock() defer g.mu.Unlock() if rank < RankLeader || rank > RankRecruit { return false } g.ranks[rank] = name g.lastModified = time.Now() if sendPacket { g.ranksSaveNeeded = true } return true } // GetRankName returns the name for a rank func (g *Guild) GetRankName(rank int8) string { g.mu.RLock() defer g.mu.RUnlock() if name, exists := g.ranks[rank]; exists { return name } // Return default rank name if defaultName, exists := DefaultRankNames[rank]; exists { return defaultName } return "Unknown" } // SetRecruitingFlag sets a recruiting flag func (g *Guild) SetRecruitingFlag(flag, value int8, sendPacket bool) bool { g.mu.Lock() defer g.mu.Unlock() if flag < RecruitingFlagTraining || flag > RecruitingFlagTradeskillers { return false } g.recruitingFlags[flag] = value g.lastModified = time.Now() if sendPacket { g.recruitingSaveNeeded = true } return true } // GetRecruitingFlag returns a recruiting flag func (g *Guild) GetRecruitingFlag(flag int8) int8 { g.mu.RLock() defer g.mu.RUnlock() if value, exists := g.recruitingFlags[flag]; exists { return value } return 0 } // AddNewGuildMember adds a new member to the guild func (g *Guild) AddNewGuildMember(characterID int32, invitedBy string, joinDate time.Time, rank int8) bool { g.mu.Lock() defer g.mu.Unlock() // Check if member already exists if _, exists := g.members[characterID]; exists { return false } member := &GuildMember{ CharacterID: characterID, Rank: rank, JoinDate: joinDate, PointHistory: make([]PointHistory, 0), } g.members[characterID] = member g.memberSaveNeeded = true g.lastModified = time.Now() // Add guild event g.addNewGuildEventNoLock(EventMemberJoins, fmt.Sprintf("%s has joined the guild", member.Name), time.Now(), true) return true } // RemoveGuildMember removes a member from the guild func (g *Guild) RemoveGuildMember(characterID int32, sendPacket bool) { g.mu.Lock() defer g.mu.Unlock() if member, exists := g.members[characterID]; exists { // Add guild event g.addNewGuildEventNoLock(EventMemberLeaves, fmt.Sprintf("%s has left the guild", member.Name), time.Now(), sendPacket) delete(g.members, characterID) g.memberSaveNeeded = true g.lastModified = time.Now() } } // PromoteGuildMember promotes a guild member func (g *Guild) PromoteGuildMember(characterID int32, promoterName string, sendPacket bool) bool { g.mu.Lock() defer g.mu.Unlock() member, exists := g.members[characterID] if !exists || member.Rank <= RankLeader { return false } oldRank := member.Rank member.Rank-- g.memberSaveNeeded = true g.lastModified = time.Now() // Add guild event g.addNewGuildEventNoLock(EventMemberPromoted, fmt.Sprintf("%s has been promoted from %s to %s by %s", member.Name, g.getRankNameNoLock(oldRank), g.getRankNameNoLock(member.Rank), promoterName), time.Now(), sendPacket) return true } // DemoteGuildMember demotes a guild member func (g *Guild) DemoteGuildMember(characterID int32, demoterName string, sendPacket bool) bool { g.mu.Lock() defer g.mu.Unlock() member, exists := g.members[characterID] if !exists || member.Rank >= RankRecruit { return false } oldRank := member.Rank member.Rank++ g.memberSaveNeeded = true g.lastModified = time.Now() // Add guild event g.addNewGuildEventNoLock(EventMemberDemoted, fmt.Sprintf("%s has been demoted from %s to %s by %s", member.Name, g.getRankNameNoLock(oldRank), g.getRankNameNoLock(member.Rank), demoterName), time.Now(), sendPacket) return true } // AddPointsToGuildMember adds points to a specific guild member func (g *Guild) AddPointsToGuildMember(characterID int32, points float64, modifiedBy, comment string, sendPacket bool) bool { g.mu.Lock() defer g.mu.Unlock() member, exists := g.members[characterID] if !exists { return false } member.Points += points // Add to point history if len(member.PointHistory) >= MaxPointHistory { // Remove oldest entry member.PointHistory = member.PointHistory[1:] } member.PointHistory = append(member.PointHistory, PointHistory{ Date: time.Now(), ModifiedBy: modifiedBy, Comment: comment, Points: points, SaveNeeded: true, }) g.pointsHistorySaveNeeded = true g.lastModified = time.Now() return true } // AddNewGuildEvent adds a new event to the guild func (g *Guild) AddNewGuildEvent(eventType int32, description string, date time.Time, sendPacket bool) { g.mu.Lock() defer g.mu.Unlock() g.addNewGuildEventNoLock(eventType, description, date, sendPacket) } // addNewGuildEventNoLock is the internal implementation without locking func (g *Guild) addNewGuildEventNoLock(eventType int32, description string, date time.Time, sendPacket bool) { event := GuildEvent{ EventID: g.nextEventID, Date: date, Type: eventType, Description: description, Locked: 0, SaveNeeded: true, } g.nextEventID++ // Add to front of events list (newest first) g.guildEvents = append([]GuildEvent{event}, g.guildEvents...) // Limit event history if len(g.guildEvents) > MaxEvents { g.guildEvents = g.guildEvents[:MaxEvents] } g.eventsSaveNeeded = true g.lastModified = time.Now() } // GetGuildInfo returns basic guild information func (g *Guild) GetGuildInfo() GuildInfo { g.mu.RLock() defer g.mu.RUnlock() return GuildInfo{ ID: g.id, Name: g.name, Level: g.level, FormedDate: g.formedDate, MOTD: g.motd, MemberCount: len(g.members), RecruiterCount: int(g.getNumRecruitersNoLock()), RecruitingShortDesc: g.recruitingShortDesc, RecruitingFullDesc: g.recruitingFullDesc, RecruitingMinLevel: g.recruitingMinLevel, RecruitingPlayStyle: g.recruitingPlayStyle, IsRecruiting: g.getNumRecruitersNoLock() > 0, } } // GetAllMembers returns all guild members func (g *Guild) GetAllMembers() []*GuildMember { g.mu.RLock() defer g.mu.RUnlock() members := make([]*GuildMember, 0, len(g.members)) for _, member := range g.members { members = append(members, member) } return members } // Save flag methods func (g *Guild) SetSaveNeeded(val bool) { g.mu.Lock() defer g.mu.Unlock() g.saveNeeded = val } func (g *Guild) GetSaveNeeded() bool { g.mu.RLock() defer g.mu.RUnlock() return g.saveNeeded } // Helper methods (internal, no lock versions) func (g *Guild) getDefaultPermission(rank, permission int8) int8 { // Leaders have all permissions by default if rank == RankLeader { return 1 } // Default permissions based on rank and permission type switch permission { case PermissionSeeGuildChat, PermissionSpeakInGuildChat: return 1 // All members can see and speak in guild chat case PermissionReceivePoints: return 1 // All members can receive points case PermissionSeeOfficerChat, PermissionSpeakInOfficerChat: if rank <= RankOfficer { return 1 } case PermissionInvite: if rank <= RankSeniorMember { return 1 } } return 0 // Default to no permission } func (g *Guild) getRankNameNoLock(rank int8) string { if name, exists := g.ranks[rank]; exists { return name } if defaultName, exists := DefaultRankNames[rank]; exists { return defaultName } return "Unknown" } func (g *Guild) getNumRecruitersNoLock() int32 { count := int32(0) for _, member := range g.members { if member.MemberFlags&MemberFlagRecruitingForGuild != 0 { count++ } } return count }