package space import ( "blazing/cool" "math/rand" "strconv" "strings" "sync" "sync/atomic" "time" ) // TimeBossRule describes one timed boss spawn rule. type TimeBossRule struct { PetID uint32 Week int ShowHours []int ShowMinute int LastTime int MapIDs []uint32 } // Timed boss schedule config. var timeBossRules = []TimeBossRule{ { PetID: 261, Week: 1, ShowHours: []int{12, 17, 18, 24}, ShowMinute: 35, LastTime: 40, MapIDs: []uint32{15, 105, 54}, }, { PetID: 261, Week: 2, ShowHours: []int{17, 18, 24}, ShowMinute: 0, LastTime: 5, MapIDs: []uint32{15, 105, 54}, }, { PetID: 261, Week: 3, ShowHours: []int{17, 18, 24}, ShowMinute: 0, LastTime: 5, MapIDs: []uint32{15, 105, 54}, }, { PetID: 261, Week: 4, ShowHours: []int{12, 17, 18, 24}, ShowMinute: 35, LastTime: 40, MapIDs: []uint32{15, 105, 54}, }, { PetID: 261, Week: 5, ShowHours: []int{17, 18, 24}, ShowMinute: 0, LastTime: 5, MapIDs: []uint32{15, 105, 54}, }, { PetID: 261, Week: 6, ShowHours: []int{17, 18, 24}, ShowMinute: 0, LastTime: 5, MapIDs: []uint32{15, 105, 54}, }, { PetID: 261, Week: 7, ShowHours: generateHourRange(0, 23), ShowMinute: 0, LastTime: 10, MapIDs: []uint32{15, 105, 54}, }, } var ( registeredCronIDs = make(map[string]bool) cronMu sync.Mutex randSource = rand.New(rand.NewSource(time.Now().UnixNano())) ) // generateHourRange builds an inclusive hour range. func generateHourRange(start, end int) []int { hours := make([]int, 0, end-start+1) for i := start; i <= end; i++ { hours = append(hours, i) } return hours } func init() { for _, rule := range timeBossRules { for _, hour := range rule.ShowHours { cronID := genCronTaskID(rule.PetID, rule.Week, hour, rule.ShowMinute) cronMu.Lock() if registeredCronIDs[cronID] { cronMu.Unlock() continue } registeredCronIDs[cronID] = true cronMu.Unlock() cronExpr := genCronExpr(rule.Week, hour, rule.ShowMinute) currentRule := rule cool.Cron.AddFunc(cronExpr, func() { randomMapID := getRandomMapID(currentRule.MapIDs) if randomMapID == 0 { return } sp := GetSpace(randomMapID) if sp != nil { sp.refushgaiya(true) time.AfterFunc(time.Duration(currentRule.LastTime)*time.Minute, func() { sp.refushgaiya(false) }) } }) } } } // refushgaiya updates timed boss visibility and broadcasts it. func (s *Space) refushgaiya(vis bool) { if !vis { atomic.StoreUint32(&s.TimeBoss.Flag, 0) } else { atomic.StoreUint32(&s.TimeBoss.Flag, 1) } atomic.StoreUint32(&s.TimeBoss.ID, 261) s.Broadcast(nil, 2021, &s.TimeBoss) } // getRandomMapID picks one random map id from candidates. func getRandomMapID(mapIDs []uint32) uint32 { if len(mapIDs) == 0 { return 0 } return mapIDs[randSource.Intn(len(mapIDs))] } // genCronExpr builds a Quartz-style cron expression. func genCronExpr(week, hour, minute int) string { cronHour := hour if cronHour == 24 { cronHour = 0 } cronWeek := week if cronWeek == 7 { cronWeek = 0 } return strings.Join([]string{ "0", strconv.Itoa(minute), strconv.Itoa(cronHour), "?", "*", strconv.Itoa(cronWeek), }, " ") } // genCronTaskID builds a unique task id for dedupe. func genCronTaskID(petID uint32, week, hour, minute int) string { return strings.Join([]string{ strconv.FormatUint(uint64(petID), 10), strconv.Itoa(week), strconv.Itoa(hour), strconv.Itoa(minute), }, "_") } // containsMapID checks whether a map id exists in the list. func containsMapID(mapIDs []uint32, mapid uint32) bool { for _, id := range mapIDs { if id == mapid { return true } } return false }