feat(pet): 新增宠物功能和相关数据结构
- 新增宠物配置和自然属性配置的 XML 解析 - 实现宠物信息生成和属性计算逻辑 - 添加宠物数据库模型和相关服务 - 更新登录和任务完成逻辑,支持宠物相关操作
This commit is contained in:
@@ -30,7 +30,9 @@ var (
|
||||
//Monster MonsterRoot //野怪配置
|
||||
MonsterMap map[int]TMapConfig
|
||||
//Skill MovesTbl //技能配置
|
||||
SkillMap map[int]Move
|
||||
SkillMap map[int]Move
|
||||
PetMAP map[int]PetMM //宠物配置
|
||||
NatureRootMap map[int]NatureItem
|
||||
)
|
||||
|
||||
func initfile() {
|
||||
@@ -51,7 +53,17 @@ func initfile() {
|
||||
return m.ID
|
||||
|
||||
})
|
||||
pet := getXml[Monsters](path + "226.xml")
|
||||
PetMAP = utils.ToMap[PetMM, int](pet.Monsters, func(m PetMM) int {
|
||||
return m.ID
|
||||
|
||||
})
|
||||
NatureRootMap1 := getXml[NatureRoot](path + "nature.xml")
|
||||
|
||||
NatureRootMap = utils.ToMap[NatureItem, int](NatureRootMap1.Items, func(m NatureItem) int {
|
||||
return m.ID
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
21
common/data/xmlres/nature.go
Normal file
21
common/data/xmlres/nature.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package xmlres
|
||||
|
||||
import "github.com/ECUST-XX/xml"
|
||||
|
||||
// NatureItem 表示单个性格修正项
|
||||
type NatureItem struct {
|
||||
ID int `xml:"id,attr"`
|
||||
Name string `xml:"name,attr"`
|
||||
AttackCorrect float64 `xml:"m_attack,attr"` // 攻击修正
|
||||
DefenseCorrect float64 `xml:"m_defence,attr"` // 防御修正
|
||||
SaCorrect float64 `xml:"m_SA,attr"` // 特攻修正
|
||||
SdCorrect float64 `xml:"m_SD,attr"` // 特防修正
|
||||
SpeedCorrect float64 `xml:"m_speed,attr"` // 速度修正
|
||||
Desc string `xml:"desc,attr"` // 描述
|
||||
}
|
||||
|
||||
// NatureRoot 表示XML根节点
|
||||
type NatureRoot struct {
|
||||
XMLName xml.Name `xml:"root"`
|
||||
Items []NatureItem `xml:"item"`
|
||||
}
|
||||
50
common/data/xmlres/pet.go
Normal file
50
common/data/xmlres/pet.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package xmlres
|
||||
|
||||
import "github.com/ECUST-XX/xml"
|
||||
|
||||
// Move 表示怪物可学习的技能
|
||||
type PetMoves struct {
|
||||
ID int `xml:"ID,attr"`
|
||||
LearningLv int `xml:"LearningLv,attr"`
|
||||
}
|
||||
|
||||
// LearnableMoves 包含怪物可学习的技能列表
|
||||
type LearnableMoves struct {
|
||||
Moves []PetMoves `xml:"Move"`
|
||||
}
|
||||
|
||||
// PetMM 表示一个怪物的信息
|
||||
type PetMM struct {
|
||||
ID int `xml:"ID,attr"`
|
||||
DefName string `xml:"DefName,attr"`
|
||||
Type int `xml:"Type,attr"`
|
||||
GrowthType int `xml:"GrowthType,attr"`
|
||||
HP int `xml:"HP,attr"`
|
||||
Atk int `xml:"Atk,attr"`
|
||||
Def int `xml:"Def,attr"`
|
||||
SpAtk int `xml:"SpAtk,attr"`
|
||||
SpDef int `xml:"SpDef,attr"`
|
||||
Spd int `xml:"Spd,attr"`
|
||||
YieldingExp int `xml:"YieldingExp,attr"`
|
||||
CatchRate string `xml:"CatchRate,attr"`
|
||||
YieldingEV string `xml:"YieldingEV,attr"`
|
||||
EvolvesFrom int `xml:"EvolvesFrom,attr"`
|
||||
EvolvesTo int `xml:"EvolvesTo,attr"`
|
||||
EvolvingLv int `xml:"EvolvingLv,attr"`
|
||||
FreeForbidden int `xml:"FreeForbidden,attr"`
|
||||
FuseMaster int `xml:"FuseMaster,attr"`
|
||||
FuseSub int `xml:"FuseSub,attr"`
|
||||
Gender int `xml:"Gender,attr"`
|
||||
PetClass int `xml:"PetClass,attr"`
|
||||
FormParam float64 `xml:"FormParam,attr"`
|
||||
CharacterAttrParam int `xml:"CharacterAttrParam,attr"`
|
||||
GradeParam float64 `xml:"GradeParam,attr"`
|
||||
AddSeParam int `xml:"AddSeParam,attr"`
|
||||
LearnableMoves LearnableMoves `xml:"LearnableMoves"`
|
||||
}
|
||||
|
||||
// Monsters 表示所有怪物的集合
|
||||
type Monsters struct {
|
||||
XMLName xml.Name `xml:"Monsters"`
|
||||
Monsters []PetMM `xml:"Monster"`
|
||||
}
|
||||
@@ -35,7 +35,8 @@ func (h *Controller) Login(data *login.InInfo, c *socket.Conn) (result *login.Ou
|
||||
|
||||
result.PlayerInfo = *t.Info
|
||||
//result.TaskList = blservice.NewUserService(t.Info.UserID).GenTask()
|
||||
result.PetList = blservice.NewUserService(t.Info.UserID).GetPetList()
|
||||
result.PetList = blservice.NewUserService(t.Info.UserID).GetPetList(1)
|
||||
|
||||
tt := maps.NewOutInfo()
|
||||
//copier.Copy(t.Info, tt)
|
||||
t1 := handler.NewTomeeHeader(2001, t.Info.UserID)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"blazing/common/data/socket"
|
||||
"blazing/common/socket/errorcode"
|
||||
"blazing/logic/service/pet"
|
||||
"blazing/modules/blazing/service"
|
||||
)
|
||||
|
||||
// 获取精灵信息
|
||||
@@ -12,7 +13,10 @@ func (h *Controller) GetPetInfo(
|
||||
c *socket.Player) (result *pet.OutInfo,
|
||||
err errorcode.ErrorCode) { //这个时候player应该是空的
|
||||
|
||||
return nil, 0
|
||||
t := service.NewUserService(c.Info.UserID).GetPetInfo(data.CatchTime)
|
||||
return &pet.OutInfo{
|
||||
PetInfo: t,
|
||||
}, 0
|
||||
}
|
||||
|
||||
// 精灵背包仓库切换
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"blazing/logic/service/task"
|
||||
"blazing/modules/blazing/model"
|
||||
"blazing/modules/blazing/service"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -51,6 +52,15 @@ func (h Controller) AddTaskBuf(data *task.AddTaskBufInboundInfo, c *socket.Playe
|
||||
return &task.AddTaskBufOutboundInfo{}, 0
|
||||
}
|
||||
|
||||
// 生成0-24的随机整数
|
||||
func randInt0To24() int {
|
||||
// 初始化随机种子(仅需初始化一次)
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
// 生成0-24的随机数(Intn(n)返回[0, n)的整数)
|
||||
return rand.Intn(25)
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成任务
|
||||
*/
|
||||
@@ -72,23 +82,28 @@ func (h Controller) Complete_Task(data *task.CompleteTaskInboundInfo, c *socket.
|
||||
result.ItemList = append(result.ItemList, task.ItemInfo{ItemId: 500001, ItemCount: 1})
|
||||
result.ItemList = append(result.ItemList, task.ItemInfo{ItemId: 500502, ItemCount: 1})
|
||||
result.ItemList = append(result.ItemList, task.ItemInfo{ItemId: 500503, ItemCount: 1})
|
||||
// service.NewUserService(c.Info.UserID).ItemExec(func(tt []model.ItemE) []model.ItemE {
|
||||
|
||||
// return tt
|
||||
|
||||
// })
|
||||
}
|
||||
if data.TaskId == 86 { //新手注册任务
|
||||
|
||||
result.CaptureTime = uint32(time.Now().Unix())
|
||||
result.PetTypeId = 1
|
||||
|
||||
r := model.GenPetInfo(1, 1, 1, 1, 1, 5)
|
||||
result.CaptureTime = r.CatchTime
|
||||
result.PetTypeId = r.ID
|
||||
service.NewUserService(c.Info.UserID).PetAdd(r.CatchTime, 1, *r)
|
||||
}
|
||||
if data.TaskId == 87 { //新手注册任务
|
||||
|
||||
result.ItemList = append(result.ItemList, task.ItemInfo{ItemId: 300001, ItemCount: 10})
|
||||
result.ItemList = append(result.ItemList, task.ItemInfo{ItemId: 300001, ItemCount: 5})
|
||||
result.ItemList = append(result.ItemList, task.ItemInfo{ItemId: 300011, ItemCount: 5})
|
||||
|
||||
}
|
||||
if data.TaskId == 88 { //新手注册任务
|
||||
|
||||
result.ItemList = append(result.ItemList, task.ItemInfo{ItemId: 1, ItemCount: 1000})
|
||||
result.ItemList = append(result.ItemList, task.ItemInfo{ItemId: 1, ItemCount: 5000})
|
||||
|
||||
}
|
||||
return result, 0
|
||||
|
||||
@@ -7,12 +7,12 @@ import (
|
||||
const TableNamePlayerBagItem = "player_bag_item"
|
||||
|
||||
// PlayerBagItem mapped from table <player_bag_item>
|
||||
type PlayerBag struct {
|
||||
type Item struct {
|
||||
*cool.Model
|
||||
PlayerID uint64 `gorm:"not null;index:idx_player_bag_item_by_player_id;comment:'所属玩家ID'" json:"player_id"`
|
||||
Data string `gorm:"type:text;not null;comment:'全部数据'" json:"data"`
|
||||
}
|
||||
type PlayerBagItem struct {
|
||||
type ItemE struct {
|
||||
ID int32 `gorm:"not null;comment:'道具唯一编号'" json:"item_id"`
|
||||
Count int32 `gorm:"not null;default:0;comment:'拥有数量 '" json:"count"`
|
||||
Max int32 `gorm:"not null;default:0;comment:'最大数量 '" json:"max"`
|
||||
@@ -20,23 +20,23 @@ type PlayerBagItem struct {
|
||||
}
|
||||
|
||||
// TableName PlayerBagItem's table name
|
||||
func (*PlayerBagItem) TableName() string {
|
||||
func (*ItemE) TableName() string {
|
||||
return TableNamePlayerBagItem
|
||||
}
|
||||
|
||||
// GroupName PlayerBagItem's table group
|
||||
func (*PlayerBagItem) GroupName() string {
|
||||
func (*ItemE) GroupName() string {
|
||||
return "default"
|
||||
}
|
||||
|
||||
// NewPlayerBagItem create a new PlayerBagItem
|
||||
func NewPlayerBag() *PlayerBag {
|
||||
return &PlayerBag{
|
||||
func NewPlayerBag() *Item {
|
||||
return &Item{
|
||||
Model: cool.NewModel(),
|
||||
}
|
||||
}
|
||||
|
||||
// init 创建表
|
||||
func init() {
|
||||
cool.CreateTable(&PlayerBag{})
|
||||
cool.CreateTable(&Item{})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"blazing/common/data/xmlres"
|
||||
"blazing/cool"
|
||||
|
||||
"time"
|
||||
)
|
||||
|
||||
const TableNamePet = "pet"
|
||||
@@ -9,19 +12,126 @@ const TableNamePet = "pet"
|
||||
// Pet mapped from table <pet>
|
||||
type Pet struct {
|
||||
*cool.Model
|
||||
PlayerID uint32 `gorm:"not null;index:idx_pet_by_player_id;comment:'所属玩家ID'" json:"player_id"`
|
||||
InBag bool `gorm:"not null;comment:'是否在背包中'" json:"in_bag"` //"0为放入仓库,1为放入背包
|
||||
Data string `gorm:"type:text;not null;comment:'精灵全部数据'" json:"data"`
|
||||
PlayerID uint32 `gorm:"not null;index:idx_pet_by_player_id;comment:'所属玩家ID'" json:"player_id"`
|
||||
InBag int `gorm:"not null;comment:'是否在背包中'" json:"in_bag"` //"0为放入仓库,1为放入背包
|
||||
CatchTime uint32 `gorm:"not null;comment:'捕捉时间'" json:"catch_time"`
|
||||
Data string `gorm:"type:text;not null;comment:'精灵全部数据'" json:"data"`
|
||||
}
|
||||
|
||||
// * @param petTypeId 精灵类型ID
|
||||
// * @param individualValue 个体值
|
||||
// * @param natureId 性格ID
|
||||
// * @param abilityTypeEnum 特性类型
|
||||
// * @param isShiny 是否为闪光
|
||||
// * @param level 等级
|
||||
// * @return 生成的精灵实体
|
||||
func GenPetInfo(id, individual, natureId, abilityTypeEnum, shinyid, level uint32) *PetInfo {
|
||||
|
||||
p := &PetInfo{ID: id,
|
||||
Shiny: shinyid, //闪光
|
||||
Nature: natureId, //性格
|
||||
Dv: individual,
|
||||
AbilityType: abilityTypeEnum, //特性
|
||||
CatchTime: uint32(time.Now().Unix()),
|
||||
Level: level} //等级
|
||||
|
||||
petxml := xmlres.PetMAP[int(id)]
|
||||
naxml := xmlres.NatureRootMap[int(natureId)]
|
||||
tttt := make([]uint32, 0)
|
||||
for _, v := range petxml.LearnableMoves.Moves {
|
||||
if p.Level >= uint32(v.LearningLv) {
|
||||
tttt = append(tttt, uint32(v.ID))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
for i := 0; i < len(tttt); i++ {
|
||||
p.SkillList[i].ID = tttt[i]
|
||||
p.SkillList[i].Pp = uint32(xmlres.SkillMap[int(tttt[i])].MaxPP)
|
||||
|
||||
}
|
||||
p.SkillListLen = uint32(len(tttt))
|
||||
// 计算各项属性
|
||||
hp := p.CalculatePetHPPanelSize(
|
||||
uint32(petxml.HP),
|
||||
p.Dv,
|
||||
p.Level,
|
||||
p.EvHp,
|
||||
)
|
||||
|
||||
attack := p.CalculatePetPanelSize(
|
||||
p.Attack,
|
||||
p.Dv,
|
||||
p.Level,
|
||||
p.EvAttack,
|
||||
naxml.AttackCorrect,
|
||||
)
|
||||
|
||||
defense := p.CalculatePetPanelSize(
|
||||
p.Defence,
|
||||
p.Dv,
|
||||
p.Level,
|
||||
p.EvDefence,
|
||||
naxml.DefenseCorrect,
|
||||
)
|
||||
|
||||
specialAttack := p.CalculatePetPanelSize(
|
||||
p.SpecialAttack,
|
||||
p.Dv,
|
||||
p.Level,
|
||||
p.EvSpecialAttack,
|
||||
naxml.SaCorrect,
|
||||
)
|
||||
|
||||
specialDefense := p.CalculatePetPanelSize(
|
||||
p.SpecialDefence,
|
||||
p.Dv,
|
||||
p.Level,
|
||||
p.EvSpecialDefense,
|
||||
naxml.SdCorrect,
|
||||
)
|
||||
|
||||
speed := p.CalculatePetPanelSize(
|
||||
p.Speed,
|
||||
p.Dv,
|
||||
p.Level,
|
||||
p.EvSpeed,
|
||||
naxml.SpeedCorrect,
|
||||
)
|
||||
|
||||
// 设置计算结果
|
||||
p.MaxHp = hp
|
||||
p.Hp = hp
|
||||
p.Attack = attack
|
||||
p.Defence = defense
|
||||
p.SpecialAttack = specialAttack
|
||||
p.SpecialDefence = specialDefense
|
||||
p.Speed = speed
|
||||
return p
|
||||
}
|
||||
|
||||
// 计算HP面板值(无性格修正)
|
||||
func (c *PetInfo) CalculatePetHPPanelSize(base, iv, level, ev uint32) uint32 {
|
||||
// 实现具体计算逻辑,示例公式:(基础值 + 个体值) * 等级 / 100 + 等级 + 10 + 努力值/4
|
||||
return (base+iv)*level/100 + level + 10 + ev/4
|
||||
}
|
||||
|
||||
// 计算其他属性面板值(带性格修正)
|
||||
func (c *PetInfo) CalculatePetPanelSize(base, iv, level, ev uint32, natureCorrect float64) uint32 {
|
||||
// 实现具体计算逻辑,示例公式:((基础值 + 个体值) * 等级 / 100 + 5 + 努力值/4) * 性格修正
|
||||
baseValue := (base+iv)*level/100 + 5 + ev/4
|
||||
return uint32(float64(baseValue) * natureCorrect)
|
||||
}
|
||||
|
||||
// PetInfo 精灵信息结构(合并后的优化版本)
|
||||
type PetInfo struct {
|
||||
Owner uint32 `struc:"skip"` //仅作为存储
|
||||
Owner uint32 `struc:"skip"` //仅作为存储
|
||||
freedTime uint32 `struc:"skip"` //放生时间
|
||||
// 精灵编号(@UInt long → uint32)
|
||||
ID uint32 `fieldDesc:"精灵编号" `
|
||||
|
||||
// 名字:默认为全0,补齐到16字节(固定长度 → [16]byte)
|
||||
Name [16]byte `fieldDesc:"名字 默认为全0 但要补齐到16字节" serialize:"fixedLength=16,type=byteArray"`
|
||||
Name string `struc:"[16]byte" `
|
||||
|
||||
// 个体值(@UInt long → uint32)
|
||||
Dv uint32 `fieldDesc:"个体值" `
|
||||
@@ -81,7 +191,7 @@ type PetInfo struct {
|
||||
EvSpeed uint32 `fieldDesc:"速度学习力" `
|
||||
SkillListLen uint32
|
||||
// 技能信息:固定4条,空则赋值0(固定长度List → [4]SkillInfo,零值即符合“赋值0”)
|
||||
SkillList [4]SkillInfo `fieldDesc:"32字节 技能信息 必须插入4条skillInfo,若技能信息为空则要赋值成0" serialize:"fixedLength=4,type=structArray"`
|
||||
SkillList [4]SkillInfo
|
||||
|
||||
// 捕捉时间(@UInt long → 若为时间戳用uint32;若需时间类型可改为time.Time,需配合序列化处理)
|
||||
CatchTime uint32 `fieldDesc:"捕捉时间" `
|
||||
@@ -102,7 +212,8 @@ type PetInfo struct {
|
||||
SkinID uint32 `fieldDesc:"皮肤id默认为0" `
|
||||
|
||||
// 是否闪光(@UInt long → uint32,0=否,1=是)
|
||||
Shiny uint32 `fieldDesc:"是不是闪" `
|
||||
Shiny uint32 `fieldDesc:"是不是闪" `
|
||||
AbilityType uint32 `struc:"skip"` //特性
|
||||
}
|
||||
|
||||
// PetEffectInfo 精灵特性信息结构
|
||||
@@ -119,8 +230,8 @@ type PetEffectInfo struct {
|
||||
|
||||
// SkillInfo 精灵技能信息结构(SkillInfo)
|
||||
type SkillInfo struct {
|
||||
ID uint32 `struc:"uint32"` // 技能id(@UInt long)
|
||||
Pp uint32 `struc:"uint32"` // 剩余pp(@UInt long)
|
||||
ID uint32
|
||||
Pp uint32
|
||||
}
|
||||
|
||||
// TableName Pet's table name
|
||||
|
||||
21
modules/blazing/service/item.go
Normal file
21
modules/blazing/service/item.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"blazing/cool"
|
||||
"blazing/modules/blazing/model"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
func (s *UserService) ItemExec(t func([]model.ItemE) []model.ItemE) {
|
||||
//todo待测试
|
||||
var player model.Item
|
||||
m1 := cool.DBM(s.reg.Model).Where("player_id", s.userid)
|
||||
m1.Scan(&player)
|
||||
var tt []model.ItemE
|
||||
json.Unmarshal([]byte(player.Data), &tt)
|
||||
t(tt)
|
||||
tmep, _ := json.Marshal(tt)
|
||||
player.Data = string(tmep)
|
||||
m1.Save(player)
|
||||
|
||||
}
|
||||
@@ -6,10 +6,27 @@ import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// 获取精灵信息
|
||||
func (s *UserService) GetPetList() (ret []model.PetInfo) {
|
||||
// 获取精灵信息 0是仓库,1是背包
|
||||
func (s *UserService) GetPetList(flag int) (ret []model.PetInfo) {
|
||||
ret = make([]model.PetInfo, 0)
|
||||
m := cool.DBM(s.pet.Model).Where("player_id", s.userid).Where("in_bag", flag)
|
||||
var tt []model.Pet
|
||||
m.Scan(&tt)
|
||||
|
||||
m := cool.DBM(s.pet.Model).Where("player_id", s.userid)
|
||||
for _, v := range tt {
|
||||
var ret11 model.PetInfo
|
||||
json.Unmarshal([]byte(v.Data), &ret11)
|
||||
|
||||
ret = append(ret, ret11)
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (s *UserService) GetPetInfo(cachetime uint32) (ret model.PetInfo) {
|
||||
|
||||
m := cool.DBM(s.pet.Model).Where("player_id", s.userid).Where("catch_time", cachetime)
|
||||
var tt model.Pet
|
||||
m.Scan(&tt)
|
||||
json.Unmarshal([]byte(tt.Data), &ret)
|
||||
@@ -17,3 +34,28 @@ func (s *UserService) GetPetList() (ret []model.PetInfo) {
|
||||
return
|
||||
|
||||
}
|
||||
func (s *UserService) PetExec(ctime uint32, t func(uint32, model.PetInfo) model.PetInfo) {
|
||||
//todo待测试
|
||||
var player model.Pet
|
||||
m1 := cool.DBM(s.pet.Model).Where("player_id", s.userid).Where("catch_time", ctime)
|
||||
m1.Scan(&player)
|
||||
var tt model.PetInfo
|
||||
json.Unmarshal([]byte(player.Data), &tt)
|
||||
t(player.CatchTime, tt)
|
||||
tmep, _ := json.Marshal(tt)
|
||||
player.Data = string(tmep)
|
||||
m1.Save(player)
|
||||
|
||||
}
|
||||
func (s *UserService) PetAdd(ctime uint32, inbag int, y model.PetInfo) {
|
||||
|
||||
m1 := cool.DBM(s.pet.Model).Where("player_id", s.userid)
|
||||
var player model.Pet
|
||||
player.PlayerID = s.userid
|
||||
player.CatchTime = ctime
|
||||
player.InBag = inbag
|
||||
tmp, _ := json.Marshal(y)
|
||||
player.Data = string(tmp)
|
||||
m1.Insert(player)
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ type UserService struct {
|
||||
task *cool.Service //任务
|
||||
reg *cool.Service //注册
|
||||
pet *cool.Service //精灵
|
||||
item *cool.Service //物品
|
||||
}
|
||||
|
||||
func NewUserService(id uint32) *UserService {
|
||||
@@ -22,7 +23,8 @@ func NewUserService(id uint32) *UserService {
|
||||
reg: &cool.Service{
|
||||
Model: model.NewPlayer(),
|
||||
},
|
||||
pet: &cool.Service{Model: model.NewPet()},
|
||||
pet: &cool.Service{Model: model.NewPet()},
|
||||
item: &cool.Service{Model: model.NewPlayerBag()},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user