927 lines
21 KiB
Go
927 lines
21 KiB
Go
package guilds
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"eq2emu/internal/database"
|
|
)
|
|
|
|
// New creates a new guild instance
|
|
func New(db *database.Database) *Guild {
|
|
guild := &Guild{
|
|
db: db,
|
|
isNew: true,
|
|
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
|
|
}
|
|
|
|
// Load loads a guild from the database by ID
|
|
func Load(db *database.Database, id int32) (*Guild, error) {
|
|
guild := &Guild{
|
|
db: db,
|
|
isNew: false,
|
|
id: id,
|
|
}
|
|
|
|
if err := guild.Reload(); err != nil {
|
|
return nil, fmt.Errorf("failed to load guild %d: %w", id, err)
|
|
}
|
|
|
|
return guild, nil
|
|
}
|
|
|
|
// Save saves the guild to the database
|
|
func (g *Guild) Save() error {
|
|
ctx := context.Background()
|
|
|
|
if g.isNew {
|
|
return g.create(ctx)
|
|
}
|
|
return g.update(ctx)
|
|
}
|
|
|
|
// Delete removes the guild from the database
|
|
func (g *Guild) Delete() error {
|
|
ctx := context.Background()
|
|
return g.delete(ctx)
|
|
}
|
|
|
|
// Reload refreshes the guild data from the database
|
|
func (g *Guild) Reload() error {
|
|
ctx := context.Background()
|
|
return g.load(ctx)
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// Database operations
|
|
|
|
func (g *Guild) create(ctx context.Context) error {
|
|
// Use MySQL-compatible approach for both databases
|
|
result, err := g.db.Exec(`INSERT INTO guilds (name, motd, level, xp, xp_needed, formed_on) VALUES (?, ?, ?, ?, ?, ?)`,
|
|
g.name, g.motd, g.level, g.expCurrent, g.expToNextLevel, g.formedDate.Unix())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
id, err := result.LastInsertId()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
g.id = int32(id)
|
|
g.isNew = false
|
|
return nil
|
|
}
|
|
|
|
func (g *Guild) update(ctx context.Context) error {
|
|
// Use MySQL-compatible approach for both databases
|
|
_, err := g.db.Exec(`UPDATE guilds SET name = ?, motd = ?, level = ?, xp = ?, xp_needed = ? WHERE id = ?`,
|
|
g.name, g.motd, g.level, g.expCurrent, g.expToNextLevel, g.id)
|
|
return err
|
|
}
|
|
|
|
func (g *Guild) delete(ctx context.Context) error {
|
|
// Use MySQL-compatible approach for both databases
|
|
_, err := g.db.Exec(`DELETE FROM guilds WHERE id = ?`, g.id)
|
|
return err
|
|
}
|
|
|
|
func (g *Guild) load(ctx context.Context) error {
|
|
// Use MySQL-compatible approach for both databases
|
|
row := g.db.QueryRow(`SELECT name, motd, level, xp, xp_needed, formed_on FROM guilds WHERE id = ?`, g.id)
|
|
var formedUnix int64
|
|
err := row.Scan(&g.name, &g.motd, &g.level, &g.expCurrent, &g.expToNextLevel, &formedUnix)
|
|
if err != nil {
|
|
return fmt.Errorf("guild %d not found: %w", g.id, err)
|
|
}
|
|
|
|
g.formedDate = time.Unix(formedUnix, 0)
|
|
return g.loadMembers(ctx)
|
|
}
|
|
|
|
func (g *Guild) loadMembers(ctx context.Context) error {
|
|
g.members = make(map[int32]*GuildMember)
|
|
|
|
// Use MySQL-compatible approach for both databases
|
|
rows, err := g.db.Query(`SELECT char_id, name, rank, points, adventure_class, adventure_level,
|
|
tradeskill_class, tradeskill_level, join_date, last_login_date FROM guild_members WHERE guild_id = ?`, g.id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
member := &GuildMember{}
|
|
var joinUnix, lastLoginUnix int64
|
|
err := rows.Scan(&member.CharacterID, &member.Name, &member.Rank, &member.Points,
|
|
&member.AdventureClass, &member.AdventureLevel, &member.TradeskillClass, &member.TradeskillLevel,
|
|
&joinUnix, &lastLoginUnix)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
member.JoinDate = time.Unix(joinUnix, 0)
|
|
member.LastLoginDate = time.Unix(lastLoginUnix, 0)
|
|
g.members[member.CharacterID] = member
|
|
}
|
|
return rows.Err()
|
|
}
|