go_dreamfactory/modules/hero/model_hero.go

627 lines
17 KiB
Go

package hero
import (
"crypto/rand"
"errors"
"fmt"
"go_dreamfactory/comm"
"go_dreamfactory/lego/core"
"go_dreamfactory/modules"
"go_dreamfactory/pb"
cfg "go_dreamfactory/sys/configure/structs"
"math"
"math/big"
"reflect"
mengine "github.com/dengsgo/math-engine/engine"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/x/bsonx"
)
type ModelHero struct {
modules.MCompModel
moduleHero *Hero
}
func (this *ModelHero) Init(service core.IService, module core.IModule, comp core.IModuleComp, options core.IModuleOptions) (err error) {
this.TableName = comm.TableHero
err = this.MCompModel.Init(service, module, comp, options)
this.moduleHero = module.(*Hero)
// 通过uid创建索引
this.DB.CreateIndex(core.SqlTable(this.TableName), mongo.IndexModel{
Keys: bsonx.Doc{{Key: "uid", Value: bsonx.Int32(1)}},
})
return
}
//初始化英雄
func (this *ModelHero) InitHero(uid string, heroCfgId string) *pb.DBHero {
heroCfg := this.moduleHero.configure.GetHero(heroCfgId)
if heroCfg == nil {
this.moduleHero.Errorf("%v hero not found from config %v", heroCfgId)
return nil
}
objId := primitive.NewObjectID().Hex()
newHero := &pb.DBHero{
Id: objId,
Uid: uid,
HeroID: heroCfg.Hid,
Star: heroCfg.Star, //初始星级
Lv: 1, //初始等级
IsOverlying: true, //是否允许叠加,
Block: false, //未锁定
CardType: heroCfg.Type, //卡片类型
Skins: []int32{},
EquipID: make([]string, 6), //初始装备
SameCount: 1, //默认叠加数量
AddProperty: make(map[string]int32),
Energy: make(map[string]int32),
Property: make(map[string]int32),
EnergyProperty: make(map[string]int32),
JuexProperty: make(map[string]int32),
}
this.PropertyCompute(newHero)
this.initHeroSkill(newHero)
return newHero
}
//初始化英雄技能
func (this *ModelHero) initHeroSkill(hero *pb.DBHero) []*pb.SkillData {
heroCfg := this.moduleHero.configure.GetHero(hero.HeroID)
if heroCfg != nil {
if heroCfg.Skill != 0 {
hero.CaptainSkill = heroCfg.Skill
}
skills := []*pb.SkillData{}
if heroCfg.Skill1 != 0 {
skills = append(skills, &pb.SkillData{SkillID: heroCfg.Skill1, SkillLv: 1})
}
if heroCfg.Skill2 != 0 {
skills = append(skills, &pb.SkillData{SkillID: heroCfg.Skill2, SkillLv: 1})
}
if heroCfg.Skill3 != 0 {
skills = append(skills, &pb.SkillData{SkillID: heroCfg.Skill3, SkillLv: 1})
}
hero.NormalSkill = skills
}
return nil
}
//创建一个指定的英雄
func (this *ModelHero) createOneHero(uid string, heroCfgId string) (hero *pb.DBHero, err error) {
hero = this.InitHero(uid, heroCfgId)
if hero != nil {
if err = this.AddList(uid, hero.Id, hero); err != nil {
this.moduleHero.Errorf("%v", err)
return
}
}
return
}
// 拷贝一个指针对象
func (this *ModelHero) copyPoint(m *pb.DBHero) *pb.DBHero {
vt := reflect.TypeOf(m).Elem()
newoby := reflect.New(vt)
newoby.Elem().Set(reflect.ValueOf(m).Elem())
return newoby.Interface().(*pb.DBHero)
}
// 克隆一个英雄
// 调用此方法前注意有使用map 切片等指针类型数据是 克隆完成后自行初始化这个指针对象
func (this *ModelHero) CloneNewHero(hero *pb.DBHero) (newHero *pb.DBHero) {
newHero = new(pb.DBHero)
*newHero = *hero //*this.copyPoint(hero)
newHero.Id = primitive.NewObjectID().Hex()
this.AddList(newHero.Uid, newHero.Id, newHero)
return
}
//初始化可叠加的英雄
func (this *ModelHero) initHeroOverlying(uid string, heroCfgId string, count int32) (hero *pb.DBHero, err error) {
hero = this.InitHero(uid, heroCfgId)
if hero != nil {
hero.SameCount = count
if err = this.AddList(uid, hero.Id, hero); err != nil {
this.moduleHero.Errorf("%v", err)
return
}
}
return
}
// 该方法适用创建初始英雄 叠加英雄 count叠加数量
func (this *ModelHero) createHeroOverlying(uid string, heroCfgId string, count int32) (hero *pb.DBHero, err error) {
heroes := this.getHeroList(uid)
if len(heroes) == 0 {
return this.initHeroOverlying(uid, heroCfgId, count)
} else {
var isExist bool
for _, h := range heroes {
if h.HeroID == heroCfgId &&
h.IsOverlying {
isExist = true
h.SameCount += count
data := map[string]interface{}{
"sameCount": h.SameCount, //叠加数
}
hero = h
if err := this.ChangeList(uid, h.Id, data); err != nil {
return nil, err
}
}
}
if !isExist {
return this.initHeroOverlying(uid, heroCfgId, count)
}
}
return
}
//创建多个指定的英雄 heroCfgIds可填入多个英雄ID
func (this *ModelHero) createMultiHero(uid string, heroCfgIds ...string) error {
heroes := this.getHeroList(uid)
if len(heroes) == 0 {
for _, v := range heroCfgIds {
if _, err := this.createOneHero(uid, v); err != nil {
return err
}
}
} else {
findHero := func(heroId string) (*pb.DBHero, bool) {
for _, h := range heroes {
if h.HeroID == heroId {
return h, true
}
}
return nil, false
}
for _, v := range heroCfgIds {
if h, ok := findHero(v); ok {
//允许叠加
if h.IsOverlying {
h.SameCount++
data := map[string]interface{}{
"sameCount": h.SameCount, //叠加数
}
if err := this.ChangeList(uid, h.Id, data); err != nil {
return err
}
} else {
if _, err := this.createOneHero(uid, v); err != nil {
return err
}
}
} else {
if _, err := this.createOneHero(uid, v); err != nil {
return err
}
}
}
}
return nil
}
//获取一个英雄(参数唯一objID)
func (this *ModelHero) getOneHero(uid, heroId string) *pb.DBHero {
hero := &pb.DBHero{}
err := this.GetListObj(uid, heroId, hero)
if err != nil {
return nil
}
return hero
}
//消耗英雄卡
func (this *ModelHero) consumeHeroCard(uid string, hero *pb.DBHero, count int32) (err error) {
if count == 0 {
return
}
if hero == nil {
err = errors.New("hero no exist")
return
}
if hero.SameCount < count {
err = errors.New("hero card no enough")
return
}
hero.SameCount -= count // 数量-1
if hero.SameCount == 0 {
if err := this.DelListlds(uid, hero.Id); err != nil {
this.moduleHero.Errorf("%v", err)
}
} else {
update := map[string]interface{}{
"sameCount": hero.SameCount,
}
err = this.ChangeList(uid, hero.Id, update)
}
this.moduleHero.Debugf("删除一张卡牌uid:%s,卡牌ID:%s", uid, hero.Id)
return
}
//获取玩家的英雄列表
func (this *ModelHero) getHeroList(uid string) []*pb.DBHero {
heroes := make([]*pb.DBHero, 0)
err := this.GetList(uid, &heroes)
if err != nil {
return nil
}
return heroes
}
// 设置共鸣能量点数属性
func (this *ModelHero) setEnergyProperty(hero *pb.DBHero) {
resonConfig := this.moduleHero.configure.GetHeroResonanceConfig(hero.HeroID, hero.Star)
if resonConfig == nil {
return
}
EnergyProperty := make(map[string]int32) //副属性
for k, v := range hero.Energy {
if k == comm.ResonanceHpPro {
EnergyProperty[comm.Hp] += int32(math.Floor((1.0 + float64(resonConfig.Hppro*v)/1000) * float64(hero.Property[comm.Hp])))
} else if k == comm.ResonanceAtkPro {
EnergyProperty[comm.Atk] += int32(math.Floor((1.0 + float64(resonConfig.Atkpro*v)/1000) * float64(hero.Property[comm.Atk])))
} else if k == comm.ResonanceDefPro {
EnergyProperty[comm.Def] += int32(math.Floor((1.0 + float64(resonConfig.Defpro*v)/1000) * float64(hero.Property[comm.Def])))
}
}
this.mergeEnegryProperty(hero.Uid, hero, EnergyProperty)
}
// 设置装备属性
func (this *ModelHero) setEquipProperty(hero *pb.DBHero, equip []*pb.DB_Equipment) {
addProperty := make(map[string]int32) //副属性
for i, v := range equip {
if v == nil {
continue
}
hero.EquipID[i] = v.Id
addProperty[v.MainEntry.AttrName] += v.MainEntry.Value //主属性
for _, v := range v.AdverbEntry {
addProperty[v.AttrName] += v.Value //附加属性
}
for k, v := range addProperty {
switch k {
case comm.AtkPro:
addProperty[comm.Atk] += int32(math.Floor((1.0 + float64(v)/1000) * float64(hero.Property[comm.Atk])))
case comm.DefPro:
addProperty[comm.Def] += int32(math.Floor((1.0 + float64(v)/1000) * float64(hero.Property[comm.Def])))
case comm.HpPro:
addProperty[comm.Hp] += int32(math.Floor((1.0 + float64(v)/1000) * float64(hero.Property[comm.Hp])))
}
}
}
//this.mergeMainProperty(hero.Uid, hero, property)
this.mergeAddProperty(hero.Uid, hero, addProperty)
}
//设置装备
func (this *ModelHero) setEquipment(hero *pb.DBHero) (newHero *pb.DBHero, err error) {
if len(hero.EquipID) == 0 {
return
}
var (
_suiteId int32
_equipID []string
_suiteExtId int32
)
update := make(map[string]interface{})
_suiteId = hero.SuiteId
_equipID = hero.EquipID
_suiteExtId = hero.SuiteExtId
if hero.IsOverlying && hero.SameCount > 1 {
// 克隆一个新的
hero.SameCount -= 1
hero.SuiteId = 0
hero.EquipID = make([]string, 6)
hero.SuiteExtId = 0
newHero = this.CloneNewHero(hero)
hero.EquipID = make([]string, 6)
hero.SameCount = 1
hero.IsOverlying = false
update["isoverlying"] = false
update["sameCount"] = 1
}
// 修改装备属性 并更新
update["suiteId"] = _suiteId
update["suiteExtId"] = _suiteExtId
update["equipID"] = _equipID
if err = this.ChangeList(hero.Uid, hero.Id, update); err != nil {
this.moduleHero.Errorf("%v", err)
return
}
return
}
//合并属性即属性值累加 (data 额外加的属性)
func (this *ModelHero) mergeMainProperty(uid string, hero *pb.DBHero, data map[string]int32) (err error) {
for k, v := range hero.Property {
if v1, ok := data[k]; ok {
v += v1
}
}
if err = this.ChangeList(uid, hero.Id, map[string]interface{}{
"property": hero.Property,
}); err != nil {
this.moduleHero.Errorf("mergeMainProperty err %v", err)
}
return
}
//合并附加属性
func (this *ModelHero) mergeAddProperty(uid string, hero *pb.DBHero, data map[string]int32) {
hero.AddProperty = data
if err := this.ChangeList(uid, hero.Id, map[string]interface{}{
"addProperty": data,
}); err != nil {
this.moduleHero.Errorf("mergeAddProperty err %v", err)
}
}
// 合并附加能量属性
func (this *ModelHero) mergeEnegryProperty(uid string, hero *pb.DBHero, data map[string]int32) {
hero.EnergyProperty = data
if err := this.ChangeList(uid, hero.Id, map[string]interface{}{
"enegryProperty": data,
}); err != nil {
this.moduleHero.Errorf("mergeAddProperty err %v", err)
}
}
//属性计算
//英雄基础属性 + 英雄等级基础属性 * 英雄成长系数 + 英雄星级对应等级属性 * 英雄品质系数
func (this *ModelHero) PropertyCompute(hero *pb.DBHero) {
//英雄等级基础属性levelup
heroLvCfg := this.moduleHero.configure.GetHeroLv(hero.Lv)
if heroLvCfg == nil {
return
}
//英雄基础配置 newhero
heroCfg := this.moduleHero.configure.GetHero(hero.HeroID)
if heroCfg == nil {
return
}
//品质系数
stargrowCfg := this.moduleHero.configure.GetHeroStar(heroCfg.Star)
if stargrowCfg == nil {
return
}
//英雄星级对应等级属性
heroStarCfg := this.moduleHero.configure.GetHeroLv(heroCfg.Star * comm.HeroStarLvRatio)
if heroStarCfg == nil {
return
}
//成长系数
lvGrow := this.moduleHero.configure.GetHeroLvgrow(hero.HeroID)
if lvGrow == nil {
return
}
curHp := hero.Property[comm.Hp]
exprHp := fmt.Sprintf("%v + %v * %v/1000 + %v * %v/1000",
(curHp + lvGrow.Hp), heroLvCfg.Hp, lvGrow.Hpgrow, heroStarCfg.Hp, stargrowCfg.StarupHp)
hp, _ := mengine.ParseAndExec(exprHp)
curAtk := hero.Property[comm.Atk]
exprAtk := fmt.Sprintf("%v +%v * %v/1000 + %v * %v/1000",
(curAtk + lvGrow.Atk), heroLvCfg.Atk, lvGrow.Atkgrow, heroStarCfg.Atk, stargrowCfg.StarupAtk)
atk, _ := mengine.ParseAndExec(exprAtk)
curDef := hero.Property[comm.Def]
exprDef := fmt.Sprintf("%v +%v * %v/1000 + %v * %v/1000",
(curDef + lvGrow.Def), heroLvCfg.Def, lvGrow.Defgrow, heroStarCfg.Def, stargrowCfg.StarupDef)
def, _ := mengine.ParseAndExec(exprDef)
curSpeed := hero.Property[comm.Speed]
exprSpeed := fmt.Sprintf("%v +%v * %v/1000 + %v * %v/1000",
(curSpeed + lvGrow.Speed), 0, 0, 0, stargrowCfg.StarupSpeed)
speed, _ := mengine.ParseAndExec(exprSpeed)
hero.Property = map[string]int32{
comm.Hp: int32(math.Floor(hp)),
comm.Atk: int32(math.Floor(atk)),
comm.Def: int32(math.Floor(def)),
comm.Speed: int32(math.Floor(speed)),
}
}
//重新计算英雄属性
func (this *ModelHero) ChangeHeroProperty(session comm.IUserSession, hero *pb.DBHero) (err error) {
this.PropertyCompute(hero) //重新计算 property 的值
update := map[string]interface{}{
"property": hero.Property,
}
if err = this.ChangeList(session.GetUserId(), hero.Id, update); err != nil {
this.moduleHero.Errorf("ChangeHeroProperty err:%v", err)
return
}
return
}
func (this *ModelHero) cleanData(uid string) {
userList := this.moduleHero.GetHeroList(uid)
for _, v := range userList {
if err := this.DelListlds(uid, v.Id); err != nil {
this.moduleHero.Errorf("cleanData err:%v", err)
}
}
}
func (this *ModelHero) AddCardExp(uid string, hero *pb.DBHero, exp int32) (newhero *pb.DBHero, code pb.ErrorCode) {
var (
curExp int32
curLv int32
update map[string]interface{} // 属性变化
)
if hero == nil {
code = pb.ErrorCode_HeroNoExist
return
}
update = make(map[string]interface{}, 0)
curExp = hero.Exp
curLv = hero.Lv
var maxLv int32 // 校验等级达到上限
maxLv = hero.Star * comm.HeroStarLvRatio
_data := this.moduleHero.configure.GetHeroLv(curLv)
if _data != nil {
if maxLv <= hero.Lv && curExp >= _data.Heroexp[0].N { // 加经验之前校验是否达到最大等级
code = pb.ErrorCode_HeroMaxLv
return
}
curExp += exp // 先把经验加上
for { // 死循环判断一键升级
if len(_data.Heroexp) == 0 {
break
}
if maxLv <= hero.Lv && curExp >= _data.Heroexp[0].N { // 设置最大经验和等级
curLv = maxLv
curExp = _data.Heroexp[0].N
break
}
if _data.Heroexp[0].N > curExp { // 经验不够升级则不能执行升级操作
break
} else { // 升级操作
curExp -= _data.Heroexp[0].N
curLv += 1 // 经验够了 那么等级+1
_data = this.moduleHero.configure.GetHeroLv(curLv)
if _data == nil { // 等级加失败了 回到原来的等级
curLv -= 1
break
}
}
}
if hero.SameCount > 1 { //有堆叠的情况
hero.SameCount -= 1
newhero = this.CloneNewHero(hero) // 克隆一个新的
}
update["lv"] = curLv
update["exp"] = curExp
update["isOverlying"] = false
update["sameCount"] = 1
hero.Lv = curLv
hero.Exp = curExp
hero.IsOverlying = false
hero.SameCount = 1
if err := this.ChangeList(uid, hero.Id, update); err != nil {
code = pb.ErrorCode_DBError
}
}
return
}
// 玩家离线 清除缓存
func (this *ModelHero) RemoveUserHeroInfo(session comm.IUserSession) (err error) {
this.BatchDelLists(session.GetUserId())
return
}
func (this *ModelHero) CheckPool(drawCount int32, config *cfg.GameGlobalData) (pools string) {
if config.BasePool1.S <= drawCount && config.BasePool1.E >= drawCount {
return config.BasePool1.P
} else if config.BasePool2.S <= drawCount && config.BasePool2.E >= drawCount {
return config.BasePool2.P
} else if config.BasePool3.S <= drawCount && config.BasePool3.E >= drawCount {
return config.BasePool3.P
}
return config.BasePool4.P
}
// 通数组里的权重随机命中 返回值为数组的下标
func (this *ModelHero) GetRandW(sz []int32) int32 {
if len(sz) > 0 {
var _totalW int64 // 总权重
var _tmpW int64 // 临时权重
for _, v := range sz {
_totalW += int64(v)
}
// 随机权重
n, _ := rand.Int(rand.Reader, big.NewInt(_totalW))
for i, v := range sz {
_tmpW += int64(v)
if n.Int64() < _tmpW {
return int32(i)
}
}
}
return 0
}
// 通过卡池的权重 获取英雄
func (this *ModelHero) GetRandHeroIdBypool(sz []int32) int32 {
if len(sz) > 0 {
var _totalW int64 // 总权重
var _tmpW int64 // 临时权重
for _, v := range sz {
_totalW += int64(v)
}
// 随机权重
n, _ := rand.Int(rand.Reader, big.NewInt(_totalW))
for i, v := range sz {
_tmpW += int64(v)
if n.Int64() < _tmpW { // 种族保底卡池命中
return int32(i)
}
}
}
return 0
}
func (this *ModelHero) InitTempHero(heroCfgId string, star, lv int32) *pb.DBHero {
heroCfg := this.moduleHero.configure.GetHero(heroCfgId)
if heroCfg == nil {
this.moduleHero.Errorf("%v hero not found from config %v", heroCfgId)
return nil
}
newHero := &pb.DBHero{
HeroID: heroCfg.Hid,
Star: star, //初始星级
Lv: lv, //初始等级
IsOverlying: true, //是否允许叠加,
Block: false, //未锁定
CardType: heroCfg.Type, //卡片类型
Skins: []int32{},
EquipID: make([]string, 6), //初始装备
SameCount: 1, //默认叠加数量
AddProperty: make(map[string]int32),
Energy: make(map[string]int32),
Property: make(map[string]int32),
EnergyProperty: make(map[string]int32),
JuexProperty: make(map[string]int32),
}
this.PropertyCompute(newHero)
this.initHeroSkill(newHero)
return newHero
}