Files
bl/logic/service/space/space.go
昔念 1b6586aedc
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
```
feat(space): 添加地图模型配置支持并优化BOSS信息结构

添加MapModel字段到MapBossInfo结构体中,用于存储更完整的BOSS模型数据,
修改初始化逻辑从新的MapModel服务获取数据,并更新HP恢复逻辑使用新模型数据。

同时优化MapNode配置表结构,移除冗余字段并调整数据查询逻辑,
将IsBroadcast字段类型改为uint32以
2026-04-01 06:27:03 +08:00

320 lines
6.8 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) {
r := service.NewMapmodelService().GetDataByModelId(v.IsBroadcast)
if r == nil {
continue
}
info := info.MapBossInfo{
Id: uint32(r.ID),
Region: v.NodeID, //这个是注册的index
Hp: r.HP,
PosInfo: ParseCoordinateString(r.Pos),
Config: v,
Model: *r,
}
ret.MapBossSInfo.INFO = append(ret.MapBossSInfo.INFO, info)
}
if len(ret.MapBossSInfo.INFO) > 0 {
cool.Cron.ScheduleFunc(10*time.Second, func() {
ret.Broadcast(nil, 2022, ret.GenBoss(false))
})
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(isfrist bool) *info.MapBossSInfo {
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 isfrist {
ret.MapBossSInfo.INFO[i].IsShow = 2
}
res.INFO = append(res.INFO, ret.MapBossSInfo.INFO[i])
}
return &res
}
func (ret *Space) HealHP() {
for _, v := range ret.MapBossSInfo.INFO {
atomic.StoreInt32(&v.Hp, int32(v.Model.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.MapBossSInfo.Wer = int32(neww)
ret.Broadcast(nil, 2021, ret.GenBoss(true))
println(ret.Name, "change weather", neww)
}
//}
}
func (ret *Space) GetDrop() int64 {
if len(ret.DropItemIds) > 0 {
item := int64(ret.DropItemIds[grand.Intn(len(ret.DropItemIds))])
return int64(item)
}
return 0
}