package fishing import ( "fmt" "mongo.games.com/game/common" "mongo.games.com/game/gamerule/fishing" "mongo.games.com/game/gamesrv/base" "mongo.games.com/game/model" "mongo.games.com/game/proto" server_proto "mongo.games.com/game/protocol/server" "mongo.games.com/game/srvdata" "mongo.games.com/goserver/core/basic" "mongo.games.com/goserver/core/task" "os" "strconv" "time" ) type FishingPlayerEvent struct { FishId int32 // 唯一标识 PolicyId*1000000 + int32(value.GetId())*100 + int32(index+1) Event int32 // 鱼触发的事件id Power int32 // 炮台倍率 Ts int64 // 时间戳 DropCoin int32 // 鱼掉落金币 ExtFishId []int32 // 相关鱼的鱼标识 } type FishingPlayerData struct { *base.Player scene *base.Scene // 玩家当前所在场景 LastFireTime time.Time // 用来计时,3分钟不在线踢出房间 power int32 // 炮台倍率 CoinCache int64 // 实时余额 bullet map[int32]*Bullet // 子弹 BulletLimit [BULLETLIMIT]int64 // 检测开枪频率过大 fishEvent map[string]*FishingPlayerEvent // 事件标识:事件 LostCoin int64 // 进场之后消耗的金币(子弹消耗) LostCoinCache int64 // 保存游戏记录时的消耗金币,保存一次刷新一次,用来计算上次保存记录后消耗了多少金币 AgentParam int32 // 机器人该字段表示代理玩家的id;玩家该字段表示其代理的其中一个机器人的id RobotSnIds []int32 // 真实玩家绑定的机器人数组 AutoFishing int32 // 自动标记 SelTarget int32 // 瞄准数据? TargetFish int32 // 瞄准的鱼标识? FireRate int32 // 开炮频率 taxCoin float64 // 开炮税收 sTaxCoin float64 // 开炮税收(保存游戏记录时清零) enterTime time.Time // 玩家入场时间 SelVip int32 // 玩家选择的vip炮等级 powerType int32 // 玩家炮台的类型 FreePowerNum int32 // 免费炮台炮弹的数量 winCoin int64 // 入场后总赢分 realOdds int // 玩家的实际赔率? logBulletHitNums int32 // 命中计数,保存记录时清零 logFishCount map[int64]*model.FishCoinNum // key:鱼id和子弹倍率;游戏记录 EnterCoin int64 // 进场金币 统计使用 lockFishCount map[int32]int32 // 鱼id对应打死的数量(天天捕鱼) jackpotCoin float64 // 用来收集不足1的金币(天天捕鱼) Prana float64 // 蓄能能量(天天捕鱼) PranaPercent int32 // 蓄能能量百分比(天天捕鱼) MaxCoin int64 // 游戏场最大金币(天天捕鱼) MinCoin int64 // 游戏场最少金币(天天捕鱼) ExtraCoin int64 // 除捕鱼外额外获得的金币(天天捕鱼) TestHitNum int64 // 测试碰撞次数(天天捕鱼) SkillCd map[int32]int64 //技能CD SkillGCD map[int32]int64 //技能公共CD //SkillMutex sync.Mutex } /* 更新免费炮台相关状态 */ func (this *FishingPlayerData) UpdateFreePowerState() { this.powerType = FreePowerType this.FreePowerNum = 100 } /* 更新成普通炮台相关状态 */ func (this *FishingPlayerData) UpdateNormalPowerState() { this.powerType = NormalPowerType this.FreePowerNum = 0 } /* 更新成钻头炮台相关状态 */ func (this *FishingPlayerData) UpdateBitPowerState() { this.powerType = BitPowerType } func (this *FishingPlayerData) MakeLogKey(fishTemplateId, power int32) int64 { return common.MakeI64(fishTemplateId, power) } func (this *FishingPlayerData) SplitLogKey(key int64) (int32, int32) { return common.LowI32(key), common.HighI32(key) } func (this *FishingPlayerData) init(s *base.Scene) { //this.Player.extraData = this this.Player.SetExtraData(this) this.LostCoin = 0 this.LostCoinCache = 0 this.power = s.GetDBGameFree().GetIntuseCannonMin() this.AgentParam = 0 this.bullet = make(map[int32]*Bullet) this.BulletLimit = [BULLETLIMIT]int64{} this.fishEvent = make(map[string]*FishingPlayerEvent) this.logBulletHitNums = 0 this.lockFishCount = make(map[int32]int32) this.logFishCount = make(map[int64]*model.FishCoinNum) this.jackpotCoin = 0.0 this.TestHitNum = 0 this.scene = s this.SkillGCD = make(map[int32]int64) this.SkillCd = make(map[int32]int64) this.LastFireTime = time.Now() if this.GDatas == nil { this.GDatas = make(map[string]*model.PlayerGameInfo) } if !s.GetTesting() && !this.IsRob { key := s.KeyGamefreeId var pgd *model.PlayerGameInfo if data, exist := this.GDatas[key]; !exist { pgd = new(model.PlayerGameInfo) this.GDatas[key] = pgd } else { pgd = data } if pgd != nil { //参数确保 for i := len(pgd.Data); i < GDATAS_HPFISHING_MAX; i++ { pgd.Data = append(pgd.Data, 0) } this.Prana = float64(pgd.Data[GDATAS_HPFISHING_PRANA]) //this.SelVip = int32(pgd.Data[GDATAS_FISHING_SELVIP]) } //////////////////////// var npgd *model.PlayerGameInfo if ndata, exist := this.GDatas[s.KeyGameId]; !exist { npgd = new(model.PlayerGameInfo) this.GDatas[s.KeyGameId] = npgd } else { npgd = ndata } if npgd != nil { //参数确保 for i := len(npgd.Data); i < GDATAS_HPFISHING_MAX; i++ { npgd.Data = append(npgd.Data, 0) } this.SelVip = int32(npgd.Data[GDATAS_FISHING_SELVIP]) } } this.Clean() } func (this *FishingPlayerData) SetSelVip(keyGameId string) { if pgd, ok := this.GDatas[keyGameId]; ok { pgd.Data[GDATAS_FISHING_SELVIP] = int64(this.SelVip) } } func (this *FishingPlayerData) Clean() { this.bullet = make(map[int32]*Bullet) this.BulletLimit = [BULLETLIMIT]int64{} this.fishEvent = make(map[string]*FishingPlayerEvent) this.logBulletHitNums = 0 this.bullet = make(map[int32]*Bullet) this.logFishCount = make(map[int64]*model.FishCoinNum) } func (this *FishingPlayerData) CoinCheck(power int32) bool { return this.CoinCache >= int64(power) } func (this *FishingPlayerData) CurrentCoin() int64 { return this.CoinCache } func (this *FishingPlayerData) GetTodayGameData(gameId string) *model.PlayerGameStatics { if this.TodayGameData == nil { this.TodayGameData = model.NewPlayerGameCtrlData() } if this.TodayGameData.CtrlData == nil { this.TodayGameData.CtrlData = make(map[string]*model.PlayerGameStatics) } if _, ok := this.TodayGameData.CtrlData[gameId]; !ok { this.TodayGameData.CtrlData[gameId] = model.NewPlayerGameStatics() } return this.TodayGameData.CtrlData[gameId] } /* 设置当天数据 */ func (this *FishingPlayerData) SetTodayGameDate(gameId string, playerGameStatics *model.PlayerGameStatics) { this.TodayGameData.CtrlData[gameId] = playerGameStatics } /* 获取昨日得当天数据集合 */ func (this *FishingPlayerData) GetYestDayGameData(gameId string) *model.PlayerGameStatics { if this.YesterdayGameData == nil { this.YesterdayGameData = &model.PlayerGameCtrlData{} } if this.YesterdayGameData.CtrlData == nil { this.YesterdayGameData.CtrlData = make(map[string]*model.PlayerGameStatics) } if _, ok := this.YesterdayGameData.CtrlData[gameId]; !ok { this.YesterdayGameData.CtrlData[gameId] = model.NewPlayerGameStatics() } return this.YesterdayGameData.CtrlData[gameId] } func (this *FishingPlayerData) SaveDetailedLog(s *base.Scene) { if len(this.logFishCount) == 0 { return } if sceneEx, ok := s.GetExtraData().(*FishingSceneData); ok { if sceneEx.GetTesting() && this.IsRob { this.logFishCount = make(map[int64]*model.FishCoinNum) return } totalin := int64(0) totalout := int64(0) var fd model.FishDetiel logBulletCount := make(map[int32]int32) fcn := make([]model.FishCoinNum, 0) for k, v := range this.logFishCount { FishTemplateId, power := this.SplitLogKey(k) fcn = append(fcn, model.FishCoinNum{ID: FishTemplateId, Power: power, Num: v.Num, Coin: v.Coin, HitNum: v.HitNum}) totalout += int64(v.Coin) logBulletCount[power] += v.HitNum } fd.HitInfo = &fcn bt := make([]model.BulletLevelTimes, 0) for k, v := range logBulletCount { bt = append(bt, model.BulletLevelTimes{Level: k, Times: v}) totalin += int64(k * v) } fd.BulletInfo = &bt fp := &model.FishPlayerData{ UserId: this.SnId, UserIcon: this.Head, TotalIn: totalin, TotalOut: totalout, CurrCoin: this.CoinCache, } // 捕鱼不需要个人信息里的战绩 //win := totalout - totalin //var isWin int32 //if win > 0 { // isWin = 1 //} else if win < 0 { // isWin = -1 //} //sceneEx.SaveFriendRecord(this.SnId, isWin) fd.PlayData = fp info, err := model.MarshalGameNoteByFISH(&fd) if err == nil { logid, _ := model.AutoIncGameLogId() validFlow := totalin + totalout validBet := common.AbsI64(totalin - totalout) param := base.GetSaveGamePlayerListLogParam(this.Platform, this.Channel, this.BeUnderAgentCode, this.PackageID, logid, this.InviterId, totalin, totalout, int64(this.sTaxCoin), 0, totalin, totalout, validFlow, validBet, sceneEx.IsPlayerFirst(this.Player), false) sceneEx.SaveGamePlayerListLog(this.SnId, param) sceneEx.SaveGameDetailedLog(logid, info, &base.GameDetailedParam{}) } pack := &server_proto.GWFishRecord{ GameFreeId: proto.Int32(sceneEx.GetDBGameFree().GetId()), SnId: proto.Int32(this.SnId), } for _, v := range this.logFishCount { fishRecord := &server_proto.FishRecord{ FishId: proto.Int32(v.ID), Count: proto.Int32(v.Num), } pack.FishRecords = append(pack.FishRecords, fishRecord) } if len(pack.FishRecords) > 0 { this.SendToWorld(int(server_proto.SSPacketID_PACKET_GW_FISHRECORD), pack) } diffLostCoin := this.LostCoin - this.LostCoinCache this.LostCoinCache = this.LostCoin gain := totalout - totalin this.Statics(s.KeyGameId, s.KeyGamefreeId, gain, true) if diffLostCoin > 0 { playerBet := &server_proto.PlayerData{ SnId: proto.Int32(this.SnId), Bet: proto.Int64(totalin), Gain: proto.Int64(gain), Tax: proto.Int64(int64(this.sTaxCoin)), Coin: proto.Int64(this.Coin), GameCoinTs: proto.Int64(this.GameCoinTs), } gwPlayerData := &server_proto.GWPlayerData{ SceneId: sceneEx.SceneId, GameFreeId: proto.Int32(sceneEx.GetDBGameFree().GetId()), } gwPlayerData.Datas = append(gwPlayerData.Datas, playerBet) proto.SetDefaults(gwPlayerData) sceneEx.SendToWorld(int(server_proto.SSPacketID_PACKET_GW_PLAYERDATA), gwPlayerData) } } this.sTaxCoin = 0 this.logBulletHitNums = 0 this.logFishCount = make(map[int64]*model.FishCoinNum) } func (this *FishingPlayerData) SetMaxCoin() { if this.CoinCache > this.MaxCoin { this.MaxCoin = this.CoinCache } } func (this *FishingPlayerData) SetMinCoin() { if this.CoinCache < this.MinCoin || this.MinCoin == 0 { this.MinCoin = this.CoinCache } } func (this *FishingPlayerData) SaveFishingLog(curCoin int64, gameid string) { data := this.GDatas[gameid] log := fmt.Sprintf("%v,%v,%v,%v,%v\n", this.CurrentCoin(), data.Statics.TotalIn, data.Statics.TotalOut, curCoin, base.GetCoinPoolMgr().GetTax()) task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { fileName := fmt.Sprintf("fishdata-%v.csv", this.SnId) file, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm) defer file.Close() if err != nil { file, err = os.Create(fileName) if err != nil { return err } } file.WriteString(log) return nil }), nil, "SaveFishingLog").StartByFixExecutor("SaveFishingLog") } // NewStatics .b func (this *FishingPlayerData) NewStatics(betCoin, gain int64) { if this.scene == nil || this.scene.GetTesting() || this.IsRob { //测试场和机器人不统计 return } if betCoin == 0 && gain == 0 { return } // start 如果当前处于免费炮的情况,NewStatics 记录消费为0 if this.powerType == FreePowerType { betCoin = 0 } // end //黑白名单不参与投入产出统计,影响自己和他人体验 if this.WBLevel != 0 || this.WhiteFlag != 0 || this.GMLevel > 0 { return } fishlogger.Tracef("============snid=%v betCoin=%v gain=%v ", this.SnId, betCoin, gain) keyGlobal := fmt.Sprintf("%v_%v", this.scene.GetPlatform(), this.scene.GetGameFreeId()) if base.SysProfitCoinMgr.SysPfCoin != nil { if base.SysProfitCoinMgr.SysPfCoin.ProfitCoin == nil { base.SysProfitCoinMgr.SysPfCoin.ProfitCoin = make(map[string]*model.SysCoin) } var syscoin *model.SysCoin if data, exist := base.SysProfitCoinMgr.SysPfCoin.ProfitCoin[keyGlobal]; !exist { syscoin = new(model.SysCoin) base.SysProfitCoinMgr.SysPfCoin.ProfitCoin[keyGlobal] = syscoin } else { syscoin = data } syscoin.PlaysBet += betCoin syscoin.SysPushCoin += gain fishlogger.Tracef("============SysProfitCoinMgr key=%v PlaysBet:= %v SysPushCoin= %v ", keyGlobal, syscoin.PlaysBet, syscoin.SysPushCoin) } keyPlayer := strconv.Itoa(int(this.scene.GetGameFreeId())) var pgd *model.PlayerGameInfo if d, exist := this.GDatas[keyPlayer]; exist { FishGDataLen(len(d.Data), d) pgd = d } else { pgd = &model.PlayerGameInfo{ Data: make([]int64, GDATAS_HPFISHING_MAX, GDATAS_HPFISHING_MAX), } this.GDatas[keyPlayer] = pgd } low := pgd.Data[GDATAS_HPFISHING_ALLBET] high := pgd.Data[GDATAS_HPFISHING_ALLBET64] allBet := common.MakeI64(int32(low), int32(high)) + betCoin pgd.Data[GDATAS_HPFISHING_ALLBET], pgd.Data[GDATAS_HPFISHING_ALLBET64] = common.LowAndHighI64(allBet) pgd.Data[GDATAS_HPFISHING_CHANGEBET] += int64(gain) fishlogger.Tracef("============snid=%v total fish betCoin:= %v gain=%v ", this.SnId, allBet, pgd.Data[GDATAS_HPFISHING_CHANGEBET]) } // GetAllBet . func (this *FishingPlayerData) GetAllBet(key string) int64 { ret := int64(1) if d, exist := this.GDatas[key]; exist { FishGDataLen(len(d.Data), d) ret = common.MakeI64(int32(d.Data[GDATAS_HPFISHING_ALLBET]), int32(d.Data[GDATAS_HPFISHING_ALLBET64])) } return ret } // GetAllChangeBet . func (this *FishingPlayerData) GetAllChangeBet(key string) int64 { ret := int64(0) if d, exist := this.GDatas[key]; exist { FishGDataLen(len(d.Data), d) ret = int64(d.Data[GDATAS_HPFISHING_CHANGEBET]) } return ret } // 计算个人赔率和个人限制系数 func (this *FishingPlayerData) GetPlayerOdds(gameid string, ctroRate int32, fishlevel int32) (float64, float64) { if data, ok := this.GDatas[gameid]; ok { //总产出初始值 = 100*(1-调节频率)*100 (分) 初级场 //总产出初始值 = 1000*(1-调节频率)*100 (分) 中级场 //总产出初始值 = 10000*(1-调节频率)*100 (分) 高级场 //总投入初始值 = 100*100 (分) 初级场 //总投入初始值 = 1000*100 (分) 中级场 //总投入初始值 = 10000*100 (分) 高级场 initBaseValue := int64(10000) //1万分 if fishlevel == 2 { initBaseValue = 100000 } else if fishlevel == 3 { initBaseValue = 1000000 } totalInValue := initBaseValue + data.Statics.TotalIn totalOutValue := initBaseValue*(10000-int64(ctroRate))/10000 + data.Statics.TotalOut //个人限制系数 ratio := 1.0 if fishlevel == 1 && totalOutValue-totalInValue >= 20000 { ratio = 0.5 } else if fishlevel == 2 && totalOutValue-totalInValue >= 100000 { ratio = 0.5 } else if fishlevel == 3 && totalOutValue-totalInValue >= 500000 { ratio = 0.5 } return float64(totalOutValue) / float64(totalInValue), ratio } else { fishlogger.Errorf("player.GDatas[%v] is %v", gameid, this.GDatas[gameid]) return 0, 0 } } var FishGDataLen = func(flen int, pgd *model.PlayerGameInfo) { if flen < GDATAS_HPFISHING_MAX { for i := flen; i < GDATAS_HPFISHING_MAX; i++ { pgd.Data = append(pgd.Data, 0) } } } // 增加技能CD func (this *FishingPlayerData) AddSkillCD(skillId, skillType, cdTime, gcdTime int32) { /* this.SkillMutex.Lock() defer this.SkillMutex.Unlock()*/ this.SkillCd[skillId] = time.Now().UnixNano()/int64(time.Millisecond) + int64(cdTime*1000) this.SkillGCD[skillType] = time.Now().UnixNano()/int64(time.Millisecond) + int64(gcdTime*1000) } // 判断技能在不在CD中 func (this *FishingPlayerData) GetSkillCD(skillId, skillType int32) bool { _, exists := this.SkillCd[skillId] if exists { return false } _, exists = this.SkillGCD[skillType] if exists { return false } return true } // 玩家升级解锁炮倍 func (this *FishingPlayerData) PlayerEvent(eventType, id int64) { switch eventType { case fishing.Event_Player_UpLevel: //获取解锁的炮倍 power := srvdata.PlayerExpMgr.GetUnPower(int32(id)) if power != 0 { this.UnPlayerPowerEx(int64(power)) } break case fishing.Event_Player_UnMaxPower: //id 直接解锁炮倍 后期用 this.UnPlayerPowerEx(id) break case fishing.Event_Player_UnPowerList: //id 解锁的炮台ID this.UnPlayerPowerListEx(int32(id)) break default: fishlogger.Errorf("未处理的玩家事件 eventType = %v", eventType) break } }