Files
bl/logic/service/space/space.go
xinian 7bb0ef856a
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
feat: 新增战斗效果并优化boss逻辑
2026-03-07 09:55:06 +08:00

313 lines
6.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package space
import (
"blazing/common/data/xmlres"
"blazing/common/utils"
"blazing/cool"
"strconv"
"strings"
"sync/atomic"
"time"
"blazing/logic/service/common"
"blazing/logic/service/space/info"
"blazing/modules/config/model"
"blazing/modules/config/service"
infomodel "blazing/modules/player/model"
"github.com/gogf/gf/v2/util/grand"
csmap "github.com/mhmtszr/concurrent-swiss-map"
"github.com/samber/lo"
"github.com/tnnmigga/enum"
)
// 定义天气状态枚举实例
var WeatherStatus = enum.New[struct {
Normal uint32 `enum:"0"` // 正常
Rain uint32 `enum:"1"` // 下雨
Snow uint32 `enum:"2"` // 下雪
}]()
// Space 针对Player的并发安全map键为uint32类型
type Space struct {
User *csmap.CsMap[uint32, common.PlayerI] // 存储玩家数据的map键为玩家ID
UserInfo *csmap.CsMap[uint32, info.SimpleInfo]
CanRefresh bool //是否能够刷怪
Super uint32
//SuperValue *int32
ID uint32 // 地图ID
Name string //地图名称
Owner ARENA
info.MapBossSInfo
IsChange bool
WeatherType []uint32
TimeBoss info.S2C_2022
//Weather uint32
IsTime bool
DropItemIds []uint32
PitS *csmap.CsMap[int, []model.MapPit]
}
// NewSyncMap 创建一个新的玩家同步map
func NewSpace() *Space {
ret := &Space{
User: csmap.New[uint32, common.PlayerI](),
UserInfo: csmap.New[uint32, info.SimpleInfo](),
}
return ret
}
// 获取星球
func GetSpace(id uint32) *Space {
planet, ok := planetmap.Load(id)
if ok {
return planet
}
ret := NewSpace()
ret.ID = id
defer ret.init()
planetmap.Store(id, ret)
return ret
}
var planetmap = csmap.New[uint32, *Space]()
func ParseCoordinateString(s string) []infomodel.Pos {
// 存储解析后的坐标
var points []infomodel.Pos
// 空字符串处理
if strings.TrimSpace(s) == "" {
return points
}
// 第一步:按竖线分割成单个坐标字符串
coordStrs := strings.Split(s, "|")
for _, coordStr := range coordStrs {
// 去除首尾空格(兼容可能的格式不规范)
coordStr = strings.TrimSpace(coordStr)
if coordStr == "" {
return nil
}
// 第二步按逗号分割X、Y值
xy := strings.Split(coordStr, ",")
if len(xy) != 2 {
return nil
}
// 第三步:转换为整数
xStr := strings.TrimSpace(xy[0])
yStr := strings.TrimSpace(xy[1])
x, err := strconv.Atoi(xStr)
if err != nil {
return nil
}
y, err := strconv.Atoi(yStr)
if err != nil {
return nil
}
// 添加到切片
points = append(points, infomodel.Pos{X: uint32(x), Y: uint32(y)})
}
return points
}
func (t *Space) Next(time.Time) time.Time {
return time.Now().Add(grand.D(10*time.Minute, 30*time.Minute))
}
func (ret *Space) init() {
if ret.ID < 10000 { //说明是玩家地图GetSpace
for _, v := range xmlres.MapConfig.Maps {
if v.ID == int(ret.ID) { //找到这个地图
ret.Super = uint32(v.Super)
if ret.Super == 0 {
ret.Super = uint32(v.ID)
}
ret.ID = uint32(v.ID)
_, ok := maphot[ret.Super]
if !ok {
maphot[ret.Super] = &MapTip{}
maphot[ret.Super].TipInfoS = make(map[uint32]*TipInfo, 0)
}
ret.Name = v.Name
//ogreconfig := service.NewMapPitService().GetData(ret.ID, uint32(i))
break
}
}
tips := &TipInfo{}
r := service.NewMapService().GetData(ret.ID)
if r != nil {
tips.Diao = service.NewMapService().GetData(ret.ID).DropItemIds
}
pits := service.NewMapPitService().GetDataALL(ret.ID)
ret.PitS = csmap.New[int, []model.MapPit]()
for _, v := range pits {
tips.Pet = append(tips.Pet, v.RefreshID...)
for _, vp := range v.Pos {
t, ok := ret.PitS.Load(vp)
if ok {
t = append(t, v)
ret.PitS.Store(vp, t)
} else {
ret.PitS.Store(vp, []model.MapPit{v})
}
}
}
if ret.Super != 0 {
tips.Pet = lo.Union(tips.Pet)
tips.Talk = service.NewTalkConfigService().GetTip(ret.ID)
tips.Boss = service.NewMapNodeService().GetTip(ret.ID)
maphot[ret.Super].TipInfoS[ret.ID] = tips
}
}
r := service.NewMapService().GetData(ret.ID)
if r != nil {
ret.DropItemIds = r.DropItemIds
if r.IsTimeSpace != 0 {
ret.IsTime = true
}
ret.MapBossSInfo = info.MapBossSInfo{}
ret.MapBossSInfo.INFO = make([]info.MapBossInfo, 0)
if len(r.WeatherType) > 1 {
ret.WeatherType = r.WeatherType
// ret.CanWeather = 1
cool.Cron.CustomFunc(ret, ret.GenWer)
}
for _, v := range service.NewMapNodeService().GetDataB(ret.ID) {
info := info.MapBossInfo{
Id: v.TriggerID,
Region: v.NodeID, //这个是注册的index
Hp: v.HP,
PosInfo: ParseCoordinateString(v.Pos),
Config: v,
}
ret.MapBossSInfo.INFO = append(ret.MapBossSInfo.INFO, info)
}
if len(ret.MapBossSInfo.INFO) > 0 {
cool.Cron.ScheduleFunc(10*time.Second, ret.GenBoss)
cool.Cron.ScheduleFunc(300*time.Second, ret.HealHP)
}
}
}
func (p *Space) IsMatch(t model.Event) bool {
_, ok := lo.Find(t.Weather, func(item int32) bool {
return item == int32(p.MapBossSInfo.Wer)
})
if !ok {
// 不在同一天气下
return false
}
if t.StartTime != "" && t.EndTime != "" {
ok, _ := utils.IsCurrentTimeInRange(t.StartTime, t.EndTime)
if !ok {
return false
}
}
return true
}
func (ret *Space) GenBoss() {
var res info.MapBossSInfo
res.Wer = ret.Wer
for i := 0; i < len(ret.MapBossSInfo.INFO); i++ {
if !ret.IsMatch(*ret.MapBossSInfo.INFO[i].Config.Event) {
if ret.MapBossSInfo.INFO[i].IsShow != 0 {
ret.MapBossSInfo.INFO[i].IsShow = 0
res.INFO = append(res.INFO, ret.MapBossSInfo.INFO[i])
}
continue
}
s := len(ret.MapBossSInfo.INFO[i].PosInfo)
if s != 0 {
ret.MapBossSInfo.INFO[i].Pos = ret.MapBossSInfo.INFO[i].PosInfo[(grand.Intn(s-1)+1+int(ret.MapBossSInfo.INFO[i].PosIndex))%s]
}
ret.MapBossSInfo.INFO[i].IsShow = 1
if ret.IsChange {
ret.MapBossSInfo.INFO[i].IsShow = 2
}
res.INFO = append(res.INFO, ret.MapBossSInfo.INFO[i])
}
ret.Broadcast(nil, 2021, &res)
}
func (ret *Space) HealHP() {
for _, v := range ret.MapBossSInfo.INFO {
atomic.StoreInt32(&v.Hp, int32(v.Config.HP))
}
}
func (ret *Space) GenWer() {
//if ret.CanWeather == 1 {
var neww uint32 = 0
if len(ret.WeatherType) == 2 {
neww, _ = utils.RandomByWeight(ret.WeatherType, []uint32{9, 1})
} else {
neww, _ = utils.RandomByWeight(ret.WeatherType, []uint32{8, 1, 1})
}
if neww != uint32(ret.MapBossSInfo.Wer) {
ret.IsChange = true
ret.MapBossSInfo.Wer = int32(neww)
ret.GenBoss()
println(ret.Name, "change weather", neww)
} else {
ret.IsChange = false
}
//}
}
func (ret *Space) GetDrop() int64 {
if len(ret.DropItemIds) > 0 {
item := int64(ret.DropItemIds[grand.Intn(len(ret.DropItemIds))])
return int64(item)
}
return 0
}