package fishing

import (
	"fmt"
	"github.com/cihub/seelog"
	"math"
	"math/rand"
	"mongo.games.com/game/gamesrv/base"
	fishing_proto "mongo.games.com/game/protocol/fishing"
	"mongo.games.com/game/srvdata"
	"mongo.games.com/goserver/core"
	"mongo.games.com/goserver/core/timer"
	"strconv"
	"strings"
	"sync"
	"time"

	"mongo.games.com/game/common"
	"mongo.games.com/game/gamerule/fishing"
	"mongo.games.com/game/model"
	"mongo.games.com/game/proto"
	"mongo.games.com/goserver/core/netlib"
	srvlibproto "mongo.games.com/goserver/srvlib/protocol"
)

var fishlogger seelog.LoggerInterface

func init() {
	core.RegisteHook(core.HOOK_BEFORE_START, func() error {
		fishlogger = common.GetLoggerInstanceByName("FishLogger")
		return nil
	})
}

type FishBattle struct {
	SnId      int32   // 玩家id
	Bullet    int32   // 子弹id
	Power     int32   // 炮台倍率
	FishsId   []int   // 被打鱼id
	ExtFishis []int32 // 关联鱼id
	LockFish  int32   // 用来提交捕捉到的指定选鱼(天天捕鱼)
	Multiple  int32   //子弹倍数
}
type Bullet struct {
	Id       int32
	Power    int32
	SrcId    int32
	LifeTime int32
	Multiple int32
}
type FishingSceneData struct {
	*base.Scene
	players            map[int32]*FishingPlayerData
	seats              [fishing.MaxPlayer]*FishingPlayerData
	BattleBuff         chan *FishBattle
	TimePoint          int32             // 帧同步中的帧序号，这里一帧是100毫秒
	NextTime           int64             // 下次模式切换的时间点
	fish_list          map[int32]*Fish   // 鱼标识 存活的鱼
	delFish_list       map[int32]int32   // 鱼标识:1 打死的鱼
	fish_Event         map[int32][]int32 // 事件id:鱼标识；鱼消失时要同步维护
	fishLevel          int32             // 场次
	frozenTick         int32             // 冰冻计时
	lastTick           int64             // 鱼同步计时
	remainder          int64             // 鱼同步计时
	lastBossTime       int64             // boss 上一次出现的时间
	lastLittleBossTime int64             // 小boss 上一次出现的时间
	BossId             int32             //当前场景的BOSS
	BossTag            int32             //当前场景BOSS是否
	LastID             int32             //当前场景鱼ID生成器
	hDestroy           timer.TimerHandle
	fishsMutex         sync.Mutex //同步鱼
	af                 AppearFish // 出鱼管理
	platform           string
	gameId             int
	sceneType          int
	sceneMode          int
	keyGameId          string //游戏ID
	testing            bool
	gamefreeId         int32
	groupId            int32
	agentor            int32
	fluctuateMaxMap    map[string]float64
	ChangeSceneId      int32 //切换场景Id 客户端用
}

func NewFishingSceneData(s *base.Scene) *FishingSceneData {
	return &FishingSceneData{
		Scene:           s,
		players:         make(map[int32]*FishingPlayerData),
		BattleBuff:      make(chan *FishBattle, 1000),
		fish_list:       make(map[int32]*Fish),
		fish_Event:      make(map[int32][]int32),
		delFish_list:    make(map[int32]int32),
		fluctuateMaxMap: make(map[string]float64),
	}
}
func (this *FishingSceneData) RebindPlayerSnId(oldSnId, newSnId int32) {
	if p, exist := this.players[oldSnId]; exist {
		delete(this.players, oldSnId)
		this.players[newSnId] = p
	}
}

func (this *FishingSceneData) init() bool {
	if this.GetDBGameFree() != nil {
		this.fishLevel = this.GetDBGameFree().GetSceneType() // 更新当前的场次
	}
	this.SetPlayerNum(4)
	this.gameId = this.GetGameId()
	this.platform = this.GetPlatform()
	this.sceneType = int(this.DbGameFree.GetSceneType())
	this.keyGameId = this.GetKeyGameId()
	this.testing = this.GetTesting()
	this.gamefreeId = this.GetGameFreeId()
	this.groupId = this.GetGroupId()
	this.agentor = this.GetAgentor()
	this.sceneMode = this.GetSceneMode()
	this.TimePoint = 0
	this.lastLittleBossTime = time.Now().Unix()
	this.lastBossTime = time.Now().Unix()
	/*	//随机一个初始点
		start := rand.Int31n(this.MaxTick * 4 / 5)
		for i := int32(0); i < start; i++ {
			this.fishFactory()
		}*/
	this.af.SceneEx = this
	//this.NextTime -= int64(time.Millisecond * time.Duration(start*100))
	fluctuateMaxStr := this.GetDBGameFree().FluctuateMax
	if len(fluctuateMaxStr) > 0 {
		pairs := strings.Split(fluctuateMaxStr, ";")
		for _, pair := range pairs {
			keyValue := strings.Split(pair, ",")
			key := keyValue[0]
			value, _ := strconv.ParseFloat(keyValue[1], 64)
			this.fluctuateMaxMap[key] = value
		}
	}
	//this.af.InitFishAppear()
	this.af.InitFishPath()
	this.af.Start()
	return true
}

func (this *FishingSceneData) Clean() {
	for _, p := range this.players {
		//5款捕鱼保持一致
		fishID, coin, taxc := int32(0), int32(0), int64(0)
		for _, v := range p.bullet {
			coin += v.Power
			taxc += int64(float64(this.GetDBGameFree().GetTaxRate()) / 10000 * float64(v.Power))
		}
		this.RetBulletCoin(p, fishID, coin, taxc, false) // 合并后发送
		p.bullet = make(map[int32]*Bullet)
		p.BulletLimit = [BULLETLIMIT]int64{}
		p.fishEvent = make(map[string]*FishingPlayerEvent)
		p.logFishCount = make(map[int64]*model.FishCoinNum)
	}
	this.fish_list = make(map[int32]*Fish)
	this.delFish_list = make(map[int32]int32)
	this.fish_Event = make(map[int32][]int32)
}
func (this *FishingSceneData) BroadcastPlayerLeave(p *base.Player, reason int) {
}
func (this *FishingSceneData) SceneDestroy(force bool) {
	this.Scene.Destroy(force)
}

// RetBulletCoin 返还子弹消耗的金币
// 子弹碰撞的鱼已经死了或者过期了，返还
func (this *FishingSceneData) RetBulletCoin(player *FishingPlayerData, fishID, coin int32, taxc int64, flag bool) {
	if player.IsRob && !model.GameParamData.IsRobFightTest {
		return
	}
	pack := &fishing_proto.SCFishDel{
		FishId:            proto.Int32(int32(fishID)),
		Coin:              proto.Int32(coin),
		CurrentPlayerCoin: proto.Int64(player.CoinCache),
	}
	proto.SetDefaults(pack)
	if !this.GetTesting() && !player.IsRob { //所有捕鱼统一
		player.NewStatics(int64(-coin), 0)
	}
	//taxc := int64(float64(this.GetDBGameFree().GetTaxRate()) / 10000 * float64(coin))
	player.LostCoin -= int64(coin)
	player.CoinCache += int64(coin)
	player.SetTotalBet(player.GetTotalBet() - int64(coin))
	//player.Statics(this.KeyGameId, this.KeyGamefreeId, int64(coin), false)
	player.LastRechargeWinCoin -= int64(coin)

	if !this.GetTesting() {
		player.taxCoin -= float64(taxc)
		player.sTaxCoin -= float64(taxc)
	}
	fishlogger.Trace("RetBulletCoin : ", fishID, player.IsRob, coin, player.LostCoin, player.CoinCache, player.GetTotalBet())
	//base.GetCoinPoolMgr().PopCoin(this.GetGameFreeId(), this.GetGroupId(), this.GetPlatform(), int64(coin)-taxc)
	tax := base.GetCoinPoolMgr().GetTax()
	base.GetCoinPoolMgr().SetTax(tax - float64(taxc))
	pack.CurrentPlayerCoin = proto.Int64(player.CoinCache)
	player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_FISHDEL), pack)
}

/*
 * 玩家相关
 */
func (this *FishingSceneData) EnterPlayer(player *FishingPlayerData) bool {
	pos := -1
	emptyPos := []int{}
	for i := 0; i < fishing.MaxPlayer; i++ {
		if this.seats[i] == nil {
			emptyPos = append(emptyPos, i)
		}
	}
	if len(emptyPos) > 0 {
		pos = emptyPos[common.RandInt(len(emptyPos))]
	} else {
		fishlogger.Error("Fishing enter player find pos error.")
	}
	player.SetPos(pos)
	player.CoinCache = player.GetCoin()
	player.LostCoin = 0
	player.LostCoinCache = 0
	//player.FishPoolKey = fmt.Sprintf("%v-%v", this.GetGameFreeId(), player.Platform)
	this.players[player.SnId] = player
	this.seats[pos] = player
	this.OnEnterPlayer(player)
	return true
}
func (this *FishingSceneData) OnEnterPlayer(player *FishingPlayerData) {
	/*
		如果进场的是机器人，寻找场内负载最小的玩家进行绑定
		如果进场的是玩家，寻找场内没有代理的机器人
	*/
	var tempPlayer *FishingPlayerData //新逻辑
	if player.IsRob {
		for i := 0; i < fishing.MaxPlayer; i++ {
			curSeatPlayer := this.seats[i]
			if curSeatPlayer != nil && !curSeatPlayer.IsRob {
				//新逻辑
				if tempPlayer == nil {
					tempPlayer = curSeatPlayer
				} else if len(tempPlayer.RobotSnIds) > len(curSeatPlayer.RobotSnIds) {
					tempPlayer = curSeatPlayer
				}

				//老逻辑
				if curSeatPlayer.AgentParam == 0 {
					curSeatPlayer.AgentParam = player.SnId
					player.AgentParam = curSeatPlayer.SnId
					curSeatPlayer.RobotSnIds = append(curSeatPlayer.RobotSnIds, player.SnId)
					break
				}
			}
		}

		//新逻辑 设置机器人的代理情况
		if tempPlayer != nil && player.AgentParam == 0 {
			player.AgentParam = tempPlayer.SnId
			tempPlayer.AgentParam = player.SnId
			tempPlayer.RobotSnIds = append(tempPlayer.RobotSnIds, player.SnId)
		}

	} else {
		for i := 0; i < fishing.MaxPlayer; i++ {
			curSeatRobot := this.seats[i]
			if curSeatRobot != nil && curSeatRobot.IsRob {
				if curSeatRobot.AgentParam == 0 { //无主机器人
					curSeatRobot.AgentParam = player.SnId
					player.AgentParam = curSeatRobot.SnId
					player.RobotSnIds = append(player.RobotSnIds, curSeatRobot.SnId) //新逻辑
				}
			}
		}
		if len(player.RobotSnIds) == 0 {
			for i := 0; i < fishing.MaxPlayer; i++ {
				curSeatRobot := this.seats[i]
				if curSeatRobot != nil && curSeatRobot.IsRob {
					if curSeatRobot.AgentParam != 0 {
						//需要重新平衡下机器人负载
						for j := 0; j < fishing.MaxPlayer; j++ {
							curSeatPlayer := this.seats[j]
							if curSeatPlayer != nil && !curSeatPlayer.IsRob && curSeatPlayer.SnId == curSeatRobot.AgentParam && len(curSeatPlayer.RobotSnIds) > 1 {
								//bind agent
								curSeatRobot.AgentParam = player.SnId
								player.AgentParam = curSeatRobot.SnId
								player.RobotSnIds = append(player.RobotSnIds, curSeatRobot.SnId)
								//unbind agent
								curSeatPlayer.RobotSnIds = common.DelSliceInt32(curSeatPlayer.RobotSnIds, curSeatRobot.SnId)
								curSeatPlayer.AgentParam = curSeatPlayer.RobotSnIds[0]
								break
							}
						}
					}
				}
				//分担一个就可以了
				if len(player.RobotSnIds) != 0 {
					break
				}
			}
		}
	}
	return
}
func (this *FishingSceneData) QuitPlayer(player *FishingPlayerData, reason int) bool {
	if _, ok := this.players[player.SnId]; ok {
		player.SetSelVip(this.KeyGameId)
		player.SaveDetailedLog(this.Scene)
		delete(this.players, player.SnId)
		this.seats[player.GetPos()] = nil
		this.BroadcastPlayerLeave(player.Player, 0)
		diffCoin := player.CoinCache - player.GetCoin()
		player.AddCoin(diffCoin, common.GainWay_Fishing, base.SyncFlag_ToClient, "system", this.GetSceneName())
		if diffCoin != 0 || player.LostCoin != 0 {
			if !player.IsRob && !this.GetTesting() {

				player.AddServiceFee(int64(player.taxCoin))
			}
			player.SetGameTimes(player.GetGameTimes() + 1)
			if diffCoin > 0 {
				//player.winTimes++
				player.SetWinTimes(player.GetWinTimes() + 1)
			} else {
				//player.lostTimes++
				player.SetLostTimes(player.GetLostTimes() + 1)
			}
		}
		this.OnQuitPlayer(player, reason)
		return true
	} else {
		return false
	}
}
func (this *FishingSceneData) OnQuitPlayer(player *FishingPlayerData, reason int) {
	/*
		如果离场的是机器人，从代理玩家身上将其删除
		如果离场的是玩家，为玩家身上代理的机器人寻找其他代理
	*/
	if player.IsRob { //机器人离场
		for i := 0; i < fishing.MaxPlayer; i++ {
			curSeatPlayer := this.seats[i]
			if curSeatPlayer != nil && !curSeatPlayer.IsRob {
				//老逻辑，不改动
				if curSeatPlayer.AgentParam == player.SnId {
					curSeatPlayer.AgentParam = 0
					player.AgentParam = 0
				}
				//新逻辑
				if common.InSliceInt32(curSeatPlayer.RobotSnIds, player.SnId) {
					curSeatPlayer.RobotSnIds = common.DelSliceInt32(curSeatPlayer.RobotSnIds, player.SnId)
					player.AgentParam = 0
					if len(curSeatPlayer.RobotSnIds) > 0 {
						curSeatPlayer.AgentParam = curSeatPlayer.RobotSnIds[0]
					} else {
						curSeatPlayer.AgentParam = 0
					}
				}
			}
		}
	} else { //玩家离场
		for i := 0; i < fishing.MaxPlayer; i++ {
			curSeatRobot := this.seats[i]
			if curSeatRobot != nil && curSeatRobot.IsRob {
				if curSeatRobot.AgentParam == player.SnId {
					curSeatRobot.AgentParam = 0
					player.AgentParam = 0

					var tempPlayer *FishingPlayerData //新逻辑 负载最小的玩家
					for j := 0; j < fishing.MaxPlayer; j++ {
						curSeatPlayer := this.seats[j]
						if curSeatPlayer != nil && !curSeatPlayer.IsRob && curSeatPlayer != player {
							if curSeatPlayer.AgentParam == 0 {
								curSeatPlayer.AgentParam = curSeatRobot.SnId
								curSeatRobot.AgentParam = curSeatPlayer.SnId
								curSeatPlayer.RobotSnIds = append(curSeatPlayer.RobotSnIds, curSeatRobot.SnId) //新逻辑
								pack := &fishing_proto.SCReBindAgent{
									PlayerSnid: proto.Int32(curSeatPlayer.SnId),
									RobSnid:    proto.Int32(curSeatRobot.SnId),
								}
								curSeatPlayer.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_REBINDAGENT), pack)
								break
							} else {
								if tempPlayer == nil {
									tempPlayer = curSeatPlayer
								} else if len(tempPlayer.RobotSnIds) > len(curSeatPlayer.RobotSnIds) {
									tempPlayer = curSeatPlayer
								}
							}
						}
					}

					//新逻辑
					if tempPlayer != nil && curSeatRobot.AgentParam == 0 {
						curSeatRobot.AgentParam = tempPlayer.SnId
						tempPlayer.AgentParam = curSeatRobot.SnId
						tempPlayer.RobotSnIds = append(tempPlayer.RobotSnIds, curSeatRobot.SnId)
						pack := &fishing_proto.SCReBindAgent{
							PlayerSnid: proto.Int32(tempPlayer.SnId),
							RobSnid:    proto.Int32(curSeatRobot.SnId),
						}
						tempPlayer.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_REBINDAGENT), pack)
					}
				}
			}
		}
		player.RobotSnIds = nil
	}
}
func (this *FishingSceneData) OnTick() {
	if this.TimePoint%10 == 0 { // TimePoint单位是百毫秒，所以是每秒执行一次 fishTimeOut()
		if this.frozenTick > 0 {
			this.frozenTick -= 1
			fishlogger.Trace("当前屏幕冰冻剩余时间：", this.frozenTick)
		}
		this.FishTimeOut()
		//检测技能CD
		this.CheckSkillCD()
	}
}

func (this *FishingSceneData) CheckSkillCD() {
	for _, player := range this.players {
		/*		player.SkillMutex.Lock()
				defer player.SkillMutex.Unlock()*/
		delSkillCD := []int32{}
		for skillId, endTime := range player.SkillCd {
			if time.Now().UnixNano()/int64(time.Millisecond) >= endTime {
				delSkillCD = append(delSkillCD, skillId)
			}
		}
		//删除技能cd
		for _, key := range delSkillCD {
			delete(player.SkillCd, key)
		}
		delSkillGCD := []int32{}
		for skillId, endTime := range player.SkillGCD {
			if time.Now().UnixNano()/int64(time.Millisecond) >= endTime {
				delSkillGCD = append(delSkillCD, skillId)
			}
		}
		//删除公共CD
		for _, key := range delSkillGCD {
			delete(player.SkillGCD, key)
		}
	}
}

// 获取当前场景是否存在世界BOSS
func (this *FishingSceneData) GetWorldBoss() bool {
	for _, value := range this.fish_list {
		if value.FishType == fishing.WorldBoss {
			return true
		}
	}
	return false
}

// fishTimeOut 清除过期的鱼
func (this *FishingSceneData) FishTimeOut() {
	//设置鱼的创建帧和结束帧
	this.fishsMutex.Lock()
	defer this.fishsMutex.Unlock()
	for key, value := range this.fish_list {
		if value == nil {
			delete(this.fish_list, key)
			fishlogger.Tracef("[fishTimeOut]1 delete fish:[%v]", key)
			continue
		}
		if value.LiveTick <= this.TimePoint {
			delete(this.fish_list, key)
			fishlogger.Tracef("[fishTimeOut]2 delete fish:[%v]", key)
			if value.Event != 0 {
				this.fish_Event[value.Event] = common.DelSliceInt32(this.fish_Event[value.Event], value.FishID)
			}
		}
	}
}

func (this *FishingSceneData) AddFish(fishs []*Fish, duration int32) {
	//设置鱼的创建帧和结束帧
	this.fishsMutex.Lock()
	defer this.fishsMutex.Unlock()
	for _, fish := range fishs {
		fish.BirthTick = this.TimePoint
		path := this.af.fishPath[fish.Path]
		if duration == 0 {
			fish.InitCrashDetect(0, path)
		}
		fish.LiveTick += this.TimePoint
		fishlogger.Tracef("计算鱼的结束帧 liveTick = %v,当前场景帧：%v", fish.LiveTick, this.TimePoint)
		this.fish_list[fish.FishID] = fish
		if fish.Event != 0 {
			eventFishs := this.fish_Event[fish.Event]
			if eventFishs == nil {
				eventFishs = []int32{}
			}
			this.fish_Event[fish.Event] = append(eventFishs, fish.FishID)
			fishlogger.Trace("出鱼 添加鱼事件！！！ fishType = %v,fishId = %v", fish.FishType, fish.FishID)
		}
	}
}
func (this *FishingSceneData) DelFish(id int32) {
	//加锁管理
	this.fishsMutex.Lock()
	defer this.fishsMutex.Unlock()

	if _, exist := this.fish_list[id]; exist {
		delete(this.fish_list, id)
		//fishlogger.Tracef("[DelFish] delete fish:[%v]", id)
		this.delFish_list[id] = 1
	}
}

// notifyAppearFish 通知出鱼
func (this *FishingSceneData) NotifyAppearFish(fishs []*Fish) {
	//创建队列
	array := make([]*fishing_proto.FishInfo, 0, len(fishs))

	//遍历出鱼
	for _, v := range fishs {
		fish := &fishing_proto.FishInfo{
			FishID:    v.FishID,
			FishType:  v.FishType,
			FishPath:  v.Path,
			FishSpeed: v.Speed,
			BirthTick: v.BirthTick,
			LiveTick:  v.LiveTick,
			FishChild: v.Child,
		}

		//加入队列
		array = append(array, fish)
	}

	//构造数据
	appearFish := &fishing_proto.SCNotifyAppearFish{
		OutFishType: 1,
		Fishs:       array,
	}
	//发送数据
	this.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_NOTIFYAPPEARFISH), appearFish, 0)
	fishlogger.Trace("通知客户端出鱼！！！！！！！！！！！！！！！！！appearFish = ", appearFish)
}

// 通知客户端切换场景
func (this *FishingSceneData) NotifyChangeScene() {
	this.ChangeSceneId += 1
	if this.ChangeSceneId >= ChangeSceneIdMax {
		this.ChangeSceneId = 0
	}
	date := &fishing_proto.SCNotifyChangeScene{
		SceneId: this.ChangeSceneId,
	}
	this.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_NOTIFYCHANGESCENE), date, 0)
	fishlogger.Trace("通知客户端切换场景并清空数据！！！！！！")
}

/*
 * 捕鱼相关
 */
func (this *FishingSceneData) fishBattle() {
	select {
	case data := <-this.BattleBuff:
		player := this.players[data.SnId]
		if player == nil {
			fishlogger.Tracef("Bullet %v owner %v offline.", data.Bullet, data.SnId)
			return
		}
		delete(player.bullet, data.Bullet) // 二次清楚 防止没有删掉
		var count = len(data.FishsId)
		if count > 0 && data.Power > 0 {
			this.fishProcess(player, data.FishsId, data.Power, data.Multiple, data.ExtFishis)
		}
	default:
		break
	}
}

// fishProcess 捕鱼击中的标准处理逻辑
// fishIds 被击中的鱼（目前只会是一条鱼）
// power 炮倍率
// extfishis 关联鱼
func (this *FishingSceneData) fishProcess(player *FishingPlayerData, fishIds []int, power, multiple int32, extfishis []int32) {
	if len(fishIds) == 0 {
		return
	}

	//调试辅助
	sendMiss := func(fishid, rate int32) {
		if player.GMLevel > 0 {
			pack := &fishing_proto.SCFireMiss{
				FishId: proto.Int32(fishid),
				Rate:   proto.Int32(rate),
			}
			proto.SetDefaults(pack)
			player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_FIREMISS), pack)
		}
	}

	// 去重，检查存活
	extFishMap := make(map[int32]struct{})
	dropcoinext := int32(0) // 掉落金币
	for _, v := range extfishis {
		if _, exist := extFishMap[v]; !exist {
			extFishMap[v] = struct{}{}
			var extfish = this.fish_list[v]
			if extfish == nil {
				continue
			}
			dropcoinext += extfish.DropCoin
		}
	}

	var killRate int32
	var death bool
	var robot = player.IsRob
	var ts = time.Now().Unix()
	hitFishMap := make(map[int32]struct{})
	for _, id := range fishIds {
		hitFishMap[int32(id)] = struct{}{}
	}
	for value, _ := range hitFishMap {
		var fish = this.fish_list[value] // 取出对应的鱼的概率
		//  判断当前的鱼是否有效
		if fish == nil {
			fishlogger.Tracef("[fishProcess] Be hit fish [%v] is disappear.", value)
			taxc := int64(float64(this.GetDBGameFree().GetTaxRate()) / 10000 * float64(power))
			if player.powerType != FreePowerType {
				// 不是免费炮期间返还
				this.RetBulletCoin(player, int32(value), power, taxc, true)
			}
			//}
			//鱼不存在
			sendMiss(int32(value), -1)
			continue
		}

		//特殊鱼处理
		if fish.TemplateID == fishing.Fish_CaiShen && fish.DropCoin < fish.MaxDropCoin {
			fish.DropCoin++
			this.syncFishCoin(fish)
		}

		//同组的鱼(一网打尽)
		var groupcoinex int32
		if fish.Event > 0 && fish.Event <= fishing.Event_Group_Max {
			groupFishs := this.fish_Event[fish.Event]
			for _, fishId := range groupFishs {
				if fishId == value { //去重
					continue
				}
				if _, exist := extFishMap[value]; exist { //去重
					continue
				}
				fishg := this.fish_list[fishId]
				if fishg == nil {
					continue
				}
				if fishg.IsDeath(this.TimePoint) {
					continue
				}
				if !fishg.IsBirth(this.TimePoint) {
					continue
				}
				groupcoinex += fishg.DropCoin
			}
		}
		//判断鱼是不是boss鱼 1 普通BOSS 2 世界BOSS
		if fish.IsBoss == fishing.Boss {
			//增加BOSS奖池
			this.AddBossPond(player, fish.TemplateID, power)
		}

		death = false
		if (robot && !model.GameParamData.IsRobFightTest) || this.GetTesting() {
			//体验场概率稍有提升
			killRate = 10000 / (fish.DropCoin + dropcoinext + groupcoinex)
			if this.GetTesting() {
				killRate *= 2
			}
			if rand.Int31n(10000) < killRate {
				death = true
			}
			// todo 强制设置机器人 鱼死不 (测试使用)
			//death = false
		} else { //欢乐捕鱼|李逵劈鱼...其他捕鱼都走这个算法
			// start 记录相关鱼的击中次数
			key := player.MakeLogKey(fish.TemplateID, power)
			if v, ok := player.logFishCount[key]; ok {
				v.HitNum++
			} else {
				player.logFishCount[key] = &model.FishCoinNum{
					HitNum: 1,
					ID:     fish.TemplateID,
					Power:  power,
				}
			}
			//击中计数
			player.logBulletHitNums++
			//判断鱼是否死亡
			dto := this.isDead(player, value, power, multiple, fish.DropCoin, 0, false)
			death = dto.IsResult
		}

		if !death {
			//鱼没死
			sendMiss(int32(value), killRate)
			continue
		}
		// 判断当前是否是 特殊鱼
		deathFishs := this.fishEvent(fish, player, power, ts, extfishis)
		if len(deathFishs) == 0 {
			//鱼没死
			sendMiss(int32(value), killRate)
			continue
		}

		this.fishSettlements(deathFishs, player, power, fish.Event, ts, 0, 0)
	}

	//写条记录
	if player.logBulletHitNums >= CountSaveNums {
		player.SaveDetailedLog(this.Scene)
	}
}

type GameSettleDto struct {
	HitRate  float64
	Suiji    float64
	IsResult bool
	Rate     interface{}
}

// 计算鱼死亡
func (this *FishingSceneData) isDead(player *FishingPlayerData, fishType int32, power int32, multiple int32, fishmu int32, totalfishProb float64, flag bool) *GameSettleDto {
	/**
	 *      取反正切函数：   ATAN(个人金币池的绝对值/(用户目前炮倍*炮数波动参数))/3.14*2*当前场次波动上限+1
	 */
	//判断鱼死亡
	// 获取鱼的配置信息和波动上限信息
	var fish = this.fish_list[fishType] // 取出对应的鱼的概率
	// 判断是否满足波动上限条件
	glp := this.checkIsmeet(this.fluctuateMaxMap, fishmu)

	var fishProb float64
	if !flag {
		// 如果不是标志位，使用鱼的默认概率
		fishProb = float64(fish.Rate) / 10000.0
	} else {
		// 否则使用总概率
		fishProb = totalfishProb
	}

	// 计算炮的加成概率
	canPro := float64(float64(power) * float64(this.GetDBGameFree().Fluctuate))

	// 获取捕鱼最大负面影响、最大命中率上限值和个人金币池的绝对值
	negativemax := float64(this.GetDBGameFree().NegativeMax) / 1000.0
	catchFishratioMax := float64(this.GetDBGameFree().RatioMax) / 1000.0
	scorepool := player.MoneyPond

	// 使用反正切函数计算命中率的变化率
	glp1, error := strconv.ParseFloat(glp, 64)
	if error != nil {
		dto := &GameSettleDto{
			IsResult: false,
		}
		return dto
	}
	rate := math.Atan(math.Abs(float64(scorepool))/canPro)/3.14*2*glp1 + 1
	var hitRate float64

	if scorepool > 0 {
		// 如果个人金币池大于0，则命中率为鱼的概率乘以变化率
		hitRate = fishProb * rate
	} else {
		if rate > negativemax {
			// 如果变化率超过了捕鱼最大负面影响，将变化率限制为最大负面影响值
			rate = negativemax
		}
		// 命中率为鱼的概率除以变化率，但不能超过最大命中率上限值
		hitRate = fishProb / rate
		if hitRate > catchFishratioMax {
			hitRate = catchFishratioMax
		}
	}

	// 根据倍数随机判断命中次数
	var suiji float64
	isresult := false
	num := multiple

	// 如果玩家有特殊状态，则命中次数为2次
	if player.powerType != 0 {
		num = 2
	}

	for i := 0; i < int(num); i++ {
		// 使用随机数判断命中结果
		suiji = rand.Float64()
		isresult = hitRate >= suiji
		if isresult {
			break
		}
	}
	// 构造游戏结算信息
	dto := &GameSettleDto{
		HitRate:  hitRate,
		Suiji:    suiji,
		IsResult: isresult,
		Rate:     rate,
	}
	fishlogger.Infof("玩家:{%v}金币池为:{%v}炮倍:{%v}波动上限glp:{%v}鱼的ID为:{%v}=鱼的概率为:{%v}=suiji值获取:{%v}=hitRate值:{%v}=rate值:{%v},鱼id = {%v}", player.GetSnId(), player.MoneyPond, power, glp, fish.FishID, fishProb, dto.Suiji, dto.HitRate, dto.Rate, fish.TemplateID)
	return dto
}

func (data *FishingSceneData) checkIsmeet(fluctuateMaxMap map[string]float64, fishmu int32) string {
	glc := ""
	for key, value := range fluctuateMaxMap {
		if len(key) == 0 {
			continue
		}
		str := strings.Split(key, "-")
		start, _ := strconv.Atoi(str[0])
		end, _ := strconv.Atoi(str[1])
		if fishmu >= int32(start) && fishmu <= int32(end) {
			glc = strconv.FormatFloat(value, 'f', -1, 64)
			break
		}
	}

	if fishmu > 10000 {
		glc = strconv.FormatFloat(getMaxValue(fluctuateMaxMap), 'f', -1, 64)
	}

	return glc
}

func getMaxValue(fluctuateMaxMap map[string]float64) float64 {
	maxValue := math.Inf(-1)
	for _, value := range fluctuateMaxMap {
		if value > maxValue {
			maxValue = value
		}
	}
	return maxValue
}

/*
event  对应得事件ID
*/
func (this *FishingSceneData) EventTreasureChestSettlements(power int32) (int32, []int32) {
	// 计算龙王多播相关得额外收益
	var totalWeight int32
	var eventCoin int32
	for _, weight := range fishing.TreasureChestWeight {
		totalWeight = totalWeight + weight
	}
	NowWeight := rand.Int31n(totalWeight)
	var cumulativeWeight int32
	for index, weight := range fishing.TreasureChestWeight {
		cumulativeWeight = cumulativeWeight + weight
		if NowWeight <= cumulativeWeight {
			treasureChestReward := fishing.TreasureChestReward[index]
			for _, value := range treasureChestReward {
				eventCoin = eventCoin + power*value
			}
			return eventCoin, treasureChestReward
		}
	}
	return 0, []int32{}
}

// fishSettlements 计算得分
// fishs 打死的鱼
// power 炮倍率
// event 事件
// ts 时间戳
// eventFishId 事件鱼标识
// eventFishCoin 事件鱼倍率
func (this *FishingSceneData) fishSettlements(fishs []*Fish, player *FishingPlayerData, power int32, event int32,
	ts int64, eventFishId int32, eventFishCoin int32) {
	var coin int64 // 鱼死亡本身得金币计算
	var treasureChestReward []int32
	pack := &fishing_proto.SCFireHit{
		Snid:      proto.Int32(player.SnId),
		Ts:        proto.Int64(ts),
		EventFish: proto.Int32(eventFishId),
		EventCoin: proto.Int32(eventFishCoin * power),
		Power:     proto.Int32(power),
		Event:     proto.Int32(event),
	}
	var sumExp int32 = 0
	for _, value := range fishs {
		var dropCoin int32
		if value.Event == fishing.Event_Bit {
			dropCoin = 0
		} else if value.Event == fishing.Event_TreasureChest {
			dropCoin, treasureChestReward = this.EventTreasureChestSettlements(power)
			fishlogger.Infof("Event_TreasureChest  eventCoin %v  treasureChestReward %v", dropCoin, treasureChestReward)
		} else if value.Event == fishing.Event_NewBoom {
			dropCoin = 0
		} else if value.Event == fishing.Event_FreePower {
			dropCoin = 0
		} else {
			dropCoin = value.DropCoin * power
		}
		//BOSS鱼死亡 更新BOSS池和个人池
		if value.IsBoss == fishing.Boss {
			bossPond := base.GetCoinPoolMgr().GetBossPond(this.DbGameFree.SceneType)
			this.isBossDie(player, int64(dropCoin), bossPond)
		}

		pack.FishId = append(pack.FishId, value.FishID)
		pack.Coin = append(pack.Coin, dropCoin)
		key := player.MakeLogKey(value.TemplateID, power)
		if v, ok := player.logFishCount[key]; ok {
			v.Coin += dropCoin
			v.Num++
		} else {
			player.logFishCount[key] = &model.FishCoinNum{
				Coin:  dropCoin,
				Num:   1,
				Power: power,
			}
		}
		fishlogger.Infof("logFishCount %v,%v,%v,%v", value.TemplateID, power, player.logFishCount[key].Coin, player.logFishCount[key].HitNum)
		coin = coin + int64(dropCoin)
		this.DelFish(value.FishID)
		value.SetDeath()
		//打死鱼增加玩家经验
		sumExp += value.Exp
	}
	if event == fishing.Event_FreePower {
		player.UpdateFreePowerState()
		fishlogger.Infof("snid %v 更新为免费炮台", player.SnId)
	}
	if !this.GetTesting() && !player.IsRob { //5款捕鱼保持统一
		player.NewStatics(0, coin)
	}
	player.winCoin += coin
	player.CoinCache += coin
	player.MoneyPond -= coin
	fishlogger.Infof("玩家：%v ,鱼死亡扣除金币池: %v ,当前金币池剩余：%v", player.SnId, coin, player.MoneyPond)
	fishlogger.Infof("fishSettlements player %v coin %v dropCoin %v , moneyPond = %v", player.SnId, player.CoinCache, coin, player.MoneyPond)
	pack.AddExp = sumExp
	oldLevel := player.FishLevel
	player.AddPlayerExp(int64(sumExp))
	if oldLevel != player.FishLevel {
		//触发炮倍解锁事件
		player.PlayerEvent(fishing.Event_Player_UpLevel, player.FishLevel)
	}
	pack.CurrentPlayerCoin = proto.Int64(player.CoinCache)
	proto.SetDefaults(pack)
	this.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_FIREHIT), pack, 0)

	// 连续开宝箱
	if event == fishing.Event_TreasureChest {
		eventTreasureChestPack := &fishing_proto.SCTreasureChestEvent{
			Snid:              proto.Int32(player.SnId),
			Reward:            treasureChestReward,
			CurrentPlayerCoin: proto.Int64(player.CoinCache),
		}
		proto.SetDefaults(eventTreasureChestPack)
		this.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_TREASURECHESTEVENT), eventTreasureChestPack, 0)
		fishlogger.Infof("Event_TreasureChest  BroadCastMessage  %v", fishing_proto.FIPacketID_FISHING_SC_TREASURECHESTEVENT)
	}
	/*	if !player.IsRob || model.GameParamData.IsRobFightTest  {
			base.GetCoinPoolMgr().PopCoin(this.GetGameFreeId(), this.GetGroupId(), this.GetPlatform(), int64(coin))
		}
	*/
}
func (this *FishingSceneData) PushBattle(player *FishingPlayerData, bulletid int32, fishs []int32, extfishis []int32) {
	bullet := player.bullet[bulletid]
	fishlogger.Infof("PushBattle player %v bullet %v fishs %v extfishis %v", player.SnId, bulletid, fishs, extfishis)
	if bullet == nil {
		fishlogger.Infof("%v not find in %v bullet buff. PushBattle player %v", bulletid, player.GetName(), player.SnId)
		return
	}

	battleData := &FishBattle{
		SnId:      bullet.SrcId,
		Bullet:    bullet.Id,
		Power:     bullet.Power,
		ExtFishis: extfishis,
		Multiple:  bullet.Multiple,
	}
	if len(fishs) > 0 {
		battleData.FishsId = append(battleData.FishsId, int(fishs[0]))
	}

	select {
	case this.BattleBuff <- battleData:
		{
			delete(player.bullet, battleData.Bullet)
		}
	default:
		{
			delete(player.bullet, battleData.Bullet)
			fishlogger.Error("Player battle buff full.")
		}
	}
}

/*
向前端推送 绑定机器人对应的行为
behaviorCode  0  是 机器人 静默行为
*/
func (this *FishingSceneData) SCRobotBehavior(snid, robotId int32, behaviorCode int32) {
	player := this.players[snid]
	if player == nil {
		fishlogger.Errorf("SCRobotBehavior player %v is empty,bullet will be droped.", snid)
		return
	}
	pack := &fishing_proto.SCRobotBehavior{
		Code:    proto.Int32(behaviorCode),
		RobotId: proto.Int32(robotId),
	}
	proto.SetDefaults(pack)
	player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_SCROBOTBEHAVIOR), pack)
}

// PushBullet 开炮
// 玩家开炮，记录税收和消息广播
func (this *FishingSceneData) PushBullet(snid, x, y, id, power, fishId, multiple int32) fishing_proto.OpResultCode {
	player := this.players[snid]
	if player == nil {
		fishlogger.Errorf("player %v is empty,bullet will be droped.", snid)
		return fishing_proto.OpResultCode_OPRC_Error
	}

	if power <= 0 || power != player.power {
		fishlogger.Tracef("[%v %v] power is invalid(%v) currpower(%v).", player.GetName(), player.SnId, power, player.power)
		return fishing_proto.OpResultCode_OPRC_Error
	}
	//判断玩家发炮倍数
	if multiple == 1 || multiple == 2 || multiple == 4 {
		if multiple == 4 {
			skillTemp := srvdata.PBDB_FishSkillMgr.GetData(103)
			if skillTemp == nil {
				return fishing_proto.OpResultCode_OPRC_Error
			}
			if player.VIP < skillTemp.Fury {
				fishlogger.Errorf("4倍炮弹，VIP等级不足 SnId = %v", player.SnId)
				return fishing_proto.OpResultCode_OPRC_Error
			}
		}
	} else {
		fishlogger.Errorf("炮弹倍数错误，SnId = %v,multiple = %v", player.SnId, multiple)
		return fishing_proto.OpResultCode_OPRC_Error
	}
	if (multiple == 2 || multiple == 4) && player.powerType != 0 {
		//狂暴下已经打死特属鱼了，再发狂暴子弹 直接return
		return fishing_proto.OpResultCode_OPRC_Error
	}

	// 检测当前玩家的的金币数够不够
	if !player.CoinCheck(power * multiple) {
		fishlogger.Tracef("%v no enough coin to fishing.", player.GetName())
		return fishing_proto.OpResultCode_OPRC_CoinNotEnough
	}

	curTime := time.Now().Unix() % BULLETLIMIT
	sbl := player.BulletLimit[curTime]
	// 子弹的数量限制,统一按最高倍速处理,配合10秒窗口期
	bulletCountLimit := int64(12)
	// start  玩家的开火率 》 0 的时候   子弹的数量限制 设置为 10
	//if player.FireRate > 0 {
	//	bulletCountLimit = 10
	//}
	// 判断子弹的数量 是否过大
	if sbl > bulletCountLimit {
		//10秒的窗口期，避免堆包误判
		total := sbl
		for i := 1; i < WINDOW_SIZE; i++ {
			total += player.BulletLimit[(int(curTime)-i+BULLETLIMIT)%BULLETLIMIT]
		}
		if total/WINDOW_SIZE > bulletCountLimit {
			fishlogger.Infof("Player bullet too fast.")
			//子弹打飞机了~~~
			key := player.MakeLogKey(0, power)
			if v, ok := player.logFishCount[key]; ok {
				v.HitNum++
			} else {
				player.logFishCount[key] = &model.FishCoinNum{
					HitNum: 1,
					ID:     0, //飞机???
					Power:  power,
				}
			}
			return fishing_proto.OpResultCode_OPRC_Error
		}
	} else {
		player.BulletLimit[curTime] = sbl + 1
	}

	player.bullet[id] = &Bullet{
		Id:       id,
		Power:    power,
		SrcId:    player.SnId,
		LifeTime: 0,
		Multiple: multiple,
	}

	if !this.GetTesting() && !player.IsRob { //5款捕鱼保持统一
		player.NewStatics(int64(power*multiple), 0)
	}

	// start 对玩家身上的金币变化进行变更
	if player.powerType != FreePowerType { // 只有当前炮台不是免费炮台的时候,才计算相关金额
		player.LostCoin += int64(power * multiple)
		player.CoinCache -= int64(power * multiple)
		//fishlogger.Infof("player %v coin %v", player.SnId, player.CoinCache)
		player.SetTotalBet(player.GetTotalBet() + int64(power*multiple))
		//if this.GetDBGameFree().GetGameId() != int32(common.GameId_NFishing) {
		//player.Statics(this.KeyGameId, this.KeyGamefreeId, -int64(power), true)
		player.LastRechargeWinCoin += int64(power * multiple)
		if _, ok := player.TodayGameData.CtrlData[this.GetKeyGameId()]; !ok {
			player.TodayGameData.CtrlData[this.GetKeyGameId()] = model.NewPlayerGameStatics()
		}
		player.TodayGameData.CtrlData[this.GetKeyGameId()].TotalIn += int64(power * multiple) // 添加每日写入数据
	} else {
		player.FreePowerNum--
		if player.FreePowerNum == 0 {
			player.UpdateNormalPowerState() // 状态变更
			fishlogger.Infof("snid %v , 更新为普通炮台", player.SnId)
		}
	}
	//更新玩家最后发炮时间
	player.LastFireTime = time.Now()
	// start 根据税收调整对应的比例 , 并且调整整个水池的逻辑
	//if !player.IsRob {
	//增加个人金币池
	//玩家发炮增加金币池数值
	//var multiple int32 = 1 //当前默认的倍数只有一倍  后期增加了再改
	var moneyScore = int64(float64(power) * float64(multiple) * float64(1.0-this.GetDraw()/1000))
	//var moneyScore = int64(float64(power) * float64(1.0-this.GetDraw()/1000))
	//普通跑增加个人金币池  其他状态的炮不增加
	if moneyScore > 0 && player.powerType == 0 {
		player.MoneyPond += moneyScore
	}
	fishlogger.Infof("玩家发炮snid= %v, 当前玩家金币池:%v,本次增加的金币moneyScore = %v", player.SnId, player.MoneyPond, moneyScore)
	//公共池
	//publicPond := int64(float64(power) * float64((this.GetPublicPondRatio())))
	playerCount := len(this.Players)
	roomCount := 1
	if math.Ceil(float64(playerCount/4)) != 0 {
		roomCount = int(math.Ceil(float64(playerCount / 4)))
	}
	fmt.Println("roomCount = ", roomCount)
	//增加公共奖池 暂时不要
	//base.GetCoinPoolMgr().AddPublicPond(l(this.gameId), publicPond)

	pack := &fishing_proto.SCFire{
		Snid:              proto.Int32(player.SnId),
		X:                 proto.Int32(x),
		Y:                 proto.Int32(y),
		Bulletid:          proto.Int32(id),
		Power:             proto.Int32(power),
		CurrentPlayerCoin: proto.Int64(player.CoinCache),
		FishId:            proto.Int32(fishId),
		Multiple:          proto.Int32(multiple),
	}
	proto.SetDefaults(pack)
	this.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_FIRE), pack, 0)
	//if player.GameData[this.GetKeyGameId()].GameTimes%15 == 0 {
	//	curCoin := coinPoolMgr.GetCoin(this.GetGameFreeId(), this.GetPlatform(), this.GetGroupId())
	//	player.SaveFishingLog(curCoin, this.GetKeyGameId())
	//}
	return fishing_proto.OpResultCode_OPRC_Sucess
}

// 同步屏幕存在的鱼
func (this *FishingSceneData) SyncFish(player *base.Player) {
	var cnt int
	var fishes []*fishing_proto.FishInfo
	syncFish := false
	pack := &fishing_proto.SCSyncFishCoin{}
	for _, fish := range this.fish_list {
		if fish.IsDeath(this.TimePoint) {
			continue
		}

		fishes = append(fishes, &fishing_proto.FishInfo{
			FishID:    fish.FishID,
			FishType:  fish.FishType,
			FishPath:  fish.Path,
			FishSpeed: fish.Speed,
			BirthTick: fish.BirthTick,
			LiveTick:  fish.LiveTick,
			FishChild: fish.Child,
		})
		cnt++
		if fish.TemplateID == fishing.Fish_CaiShen {
			pack.FishId = proto.Int32(fish.FishID)
			pack.Coin = proto.Int64(int64(fish.DropCoin))
			syncFish = true
		}
	}
	fishlogger.Trace("Current fish list count:", cnt)
	fishlogger.Trace("Current timePoint :", this.TimePoint)
	packFishes := &fishing_proto.SCFishesEnter{
		//PolicyId: proto.Int32(this.PolicyId),
		Fishes:   fishes,
		IceSec:   proto.Int32(int32(0)),
		TimeTick: proto.Int32(this.TimePoint),
	}
	proto.SetDefaults(packFishes)
	player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_FISHERENTER), packFishes)

	if syncFish {
		player.SendToClient(int(fishing_proto.FIPacketID_FISHING_SC_SCSYNCFISHCOIN), pack)
	}
}

// fishEvent 记录鱼触发的事件
func (this *FishingSceneData) fishEvent(fish *Fish, player *FishingPlayerData, power int32, ts int64, extfishs []int32) []*Fish {
	var deathFishs []*Fish
	if fish.Event == 0 {
		deathFishs = append(deathFishs, fish)
		return deathFishs
	}

	// 随机事件
	if fish.Event == fishing.Event_Rand {
		fish.Event = common.RandInt32Slice([]int32{int32(fishing.Event_Booms), int32(fishing.Event_Boom), int32(fishing.Event_Ring)})
	}

	// 普通事件
	switch fish.Event {
	case fishing.Event_Ring:
		{
			//冰冻鱼事件 后续做
			/*			deathFishs = append(deathFishs, fish)
						for _, value := range this.fish_list {
							value.LiveTick += 100
							if value.BirthTick >= this.TimePoint {
								value.BirthTick += 100
							}
						}
						this.NextTime += time.Second.Nanoseconds() * 10
						//冰冻鱼的冰冻时间 暂时设置为1秒
						this.frozenTick = 1
						pack := &fishing_proto.SCFreeze{SnId: proto.Int32(player.SnId), FishId: proto.Int32(fish.FishID)}
						this.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_FREEZE), pack, 0)*/
		}
	case fishing.Event_Booms, fishing.Event_Boom, fishing.Event_Lightning, fishing.Event_Same, fishing.Event_TreasureChest:
		{
			fishlogger.Tracef("Event fish %v-%v.", fish.TemplateID, fish.Event)
			fishlogger.Trace("Event ts:", ts)
			sign := fmt.Sprintf("%v;%v;%v;%v;", fish.Event, fish.FishID, ts, player.SnId)
			sign = common.MakeMd5String(sign)
			player.fishEvent[sign] = &FishingPlayerEvent{
				FishId:    fish.FishID,
				Event:     fish.Event,
				Power:     power,
				DropCoin:  fish.DropCoin,
				Ts:        ts,
				ExtFishId: extfishs,
			}
			fishlogger.Trace("Event sign:", sign)
			deathFishs = append(deathFishs, fish)
		}
	case fishing.Event_NewBoom, fishing.Event_Bit, fishing.Event_FreePower:
		{
			fishlogger.Tracef("Event fish %v-%v.", fish.TemplateID, fish.Event)
			fishlogger.Trace("Event ts:", ts)
			sign := fmt.Sprintf("%v;%v;%v;%v;", fish.Event, fish.FishID, ts, player.SnId)
			sign = common.MakeMd5String(sign)
			fishlogger.Infof("playerID %v ,sign %v , fishId %v , eventId %v", player.SnId, sign, fish.FishID, fish.Event)
			player.fishEvent[sign] = &FishingPlayerEvent{
				FishId:    fish.FishID,
				Event:     fish.Event,
				Power:     power,
				Ts:        ts,
				ExtFishId: extfishs,
			}
			fishlogger.Trace("Event sign:", sign)
			deathFishs = append(deathFishs, fish)
		}
	default: //一网打尽(同组的鱼)
		{
			fishlogger.Trace("Event fish:", fish)
			eventFishs := this.fish_Event[fish.Event]
			fishlogger.Trace("eventFishs:", eventFishs)
			for _, fishId := range eventFishs {
				fishLink := this.fish_list[fishId]
				if fishLink == nil {
					fishlogger.Trace("Event link fish is null:", fishId)
					continue
				}
				if fishLink.IsDeath(this.TimePoint) {
					fishlogger.Tracef("Event link fish is death:%v-%v", fishLink.FishID, fishId)
					continue
				}
				if !fishLink.IsBirth(this.TimePoint) {
					fishlogger.Tracef("Event link fish is not birth:%v-%v", fishLink.FishID, fishId)
					continue
				}
				fishlogger.Trace("Event link fish:", fishLink)
				fishlogger.Trace("Drop coin:", fishLink.DropCoin*power)
				deathFishs = append(deathFishs, fishLink)
			}
		}
	}
	return deathFishs
}

// PushEventFish 客户端触发鱼事件
// sign 事件唯一标识
// fishs 事件关联的鱼标识
// eventFish 产生事件的鱼标识
func (this *FishingSceneData) PushEventFish(player *FishingPlayerData, sign string, fishs []int32, eventFish int32) bool {
	// 获取事件
	fishEvent := player.fishEvent[sign] // 根据 Sign 确定触发鱼本身的事件
	if fishEvent == nil {
		fishlogger.Error("Recive event fish sign error.")
		fishlogger.Trace("Event sign:", sign)
		return false
	} else {
		fishlogger.Infof("PushEventFish fishEvent %v, %v, sign %v", fishEvent.Event, fishEvent.Ts, sign)
	}

	// 接收 Fish 的 相关事件 太晚了
	var timeout time.Duration
	if fishEvent.Event == fishing.Event_Bit {
		timeout = 15
	} else {
		timeout = 10
	}
	if fishEvent.Ts < time.Now().Add(-time.Second*timeout).Unix() {
		fishlogger.Error("Recive event fish list to late.")
		fishlogger.Infof("Event ts: %v", fishEvent.Ts)
		fishlogger.Infof("Event event:%v", fishEvent.Event)
		delete(player.fishEvent, sign) // 消费掉对应的sign
		return false
	}

	fishlogger.Infof("PushEventFish(client): %v", fishs)
	fishlogger.Infof("PushEventFish(srv): %v", fishEvent.ExtFishId)

	// selFishs 获取关联的所有鱼
	var selFishs []*Fish
	// start  获取当前场景 中的 Fish 对象
	for _, id := range fishEvent.ExtFishId { //用碰撞时带上来的id,防作弊
		fish := this.fish_list[id]
		if fish == nil || fish.IsDeath(this.TimePoint) {
			continue
		}
		if fishEvent.Event == fishing.Event_Bit && (fish.FishType == 7 || fish.FishType == 8 || fish.FishType == 6) {
			fishlogger.Infof("钻头贝事件,屏蔽的鱼 %v", fish.TemplateID)
			continue
		}
		selFishs = append(selFishs, fish)
	}
	if fishEvent.Event == fishing.Event_Bit {
		for _, id := range fishs { //用碰撞时带上来的id,防作弊
			fish := this.fish_list[id]
			if fish == nil || fish.IsDeath(this.TimePoint) {
				continue
			}
			if fish.FishType == 7 || fish.FishType == 8 || fish.FishType == 6 {
				fishlogger.Infof("钻头贝事件,屏蔽的鱼 %v", fish.TemplateID)
				continue
			}
			selFishs = append(selFishs, fish)
		}
	}
	// end
	if fishEvent.Event == fishing.Event_Bit {
		if len(fishs) == 0 {
			fishlogger.Errorf(" Event_Bit Event fish die all.")
			delete(player.fishEvent, sign) // 消费掉对应的sign
			this.fishSettlements(selFishs, player, fishEvent.Power, fishEvent.Event, time.Now().UnixNano(), eventFish, 0)
			return false
		}
	} else {
		if len(selFishs) == 0 {
			fishlogger.Errorf("Event fish die all.")
			delete(player.fishEvent, sign) // 消费掉对应的sign
			this.fishSettlements(selFishs, player, fishEvent.Power, fishEvent.Event, time.Now().UnixNano(), eventFish, fishEvent.DropCoin)
			return false
		}
	}

	if fishEvent.Event == fishing.Event_Bit && (len(selFishs) > 1) {
		delete(player.fishEvent, sign) // 消费掉对应的sign
	} else if fishEvent.Event != fishing.Event_Bit {
		delete(player.fishEvent, sign) // 消费掉对应的sign
	}
	// start  根据不同的场景截取 结算的 Fish
	if fishEvent.Event == fishing.Event_Boom && len(selFishs) > 15 {
		selFishs = selFishs[:15]
	}
	if fishEvent.Event == fishing.Event_Lightning && len(selFishs) > 35 {
		selFishs = selFishs[:35]
	}
	if fishEvent.Event == fishing.Event_Booms && len(selFishs) > 30 {
		selFishs = selFishs[:30]
	}
	if fishEvent.Event == fishing.Event_Same && len(selFishs) > 30 {
		selFishs = selFishs[:30]
	}
	// 新事件鱼代码处理相关
	if fishEvent.Event == fishing.Event_Bit && len(selFishs) > 50 {
		selFishs = selFishs[:50]
	}
	if fishEvent.Event == fishing.Event_NewBoom && len(selFishs) > 50 {
		selFishs = selFishs[:50]
	}
	// end
	// 进行 鱼类结算
	if fishEvent.Event == fishing.Event_Bit {
		if len(selFishs) == 1 {
		} else {
			this.fishSettlements(selFishs, player, fishEvent.Power, fishEvent.Event, time.Now().UnixNano(), eventFish, 0)
		}
	} else {
		this.fishSettlements(selFishs, player, fishEvent.Power, fishEvent.Event, time.Now().UnixNano(), eventFish, fishEvent.DropCoin)
	}
	return true
}

func (this *FishingSceneData) BroadCastMessage(packetid int, msg proto.Message, excludeSid int64) {
	mgs := make(map[*netlib.Session][]*srvlibproto.MCSessionUnion)
	for _, p := range this.players {
		if p == nil || p.GetGateSess() == nil {
			continue
		}
		if !p.IsOnLine() || p.IsMarkFlag(base.PlayerState_Leave) {
			continue
		}
		if p.GetSid() == excludeSid {
			continue
		}
		mgs[p.GetGateSess()] = append(mgs[p.GetGateSess()], &srvlibproto.MCSessionUnion{
			Mccs: &srvlibproto.MCClientSession{
				SId: proto.Int64(p.GetSid()),
			},
		})
	}
	audiences := this.GetAudiences()
	for _, p := range audiences {
		if p == nil || p.GetGateSess() == nil {
			continue
		}
		if !p.IsOnLine() || p.IsMarkFlag(base.PlayerState_Leave) {
			continue
		}
		if p.GetSid() == excludeSid {
			continue
		}
		mgs[p.GetGateSess()] = append(mgs[p.GetGateSess()], &srvlibproto.MCSessionUnion{
			Mccs: &srvlibproto.MCClientSession{
				SId: proto.Int64(p.GetSid()),
			},
		})
	}
	for gateSess, v := range mgs {
		if gateSess == nil || len(v) == 0 {
			continue
		}
		pack, err := base.MulticastMaker.CreateMulticastPacket(packetid, msg, v...)
		if err == nil {
			proto.SetDefaults(pack)
			gateSess.Send(int(srvlibproto.SrvlibPacketID_PACKET_SS_MULTICAST), pack)
		}
	}
}

func (this *FishingSceneData) syncFishCoin(fish *Fish) {
	pack := &fishing_proto.SCSyncFishCoin{
		FishId: proto.Int32(fish.FishID),
		Coin:   proto.Int64(int64(fish.DropCoin)),
	}
	proto.SetDefaults(pack)
	this.BroadCastMessage(int(fishing_proto.FIPacketID_FISHING_SC_SCSYNCFISHCOIN), pack, 0)
}

// GetDraw 获取金币池抽水百分比
func (this *FishingSceneData) GetDraw() float64 {
	draw := this.GetDBGameFree().GetDraw()
	return float64(draw / 100.0)
}

// GetBossDrainageBet 获取BOSS池抽水百分比
func (this *FishingSceneData) GetBossDrainageBet() float64 {
	bossDraw := this.GetDBGameFree().GetBossDrainageBet()
	return float64(bossDraw / 100.0)
}

// 获取公共池抽水百分比
func (this *FishingSceneData) GetPublicPondRatio() float64 {
	ratio := this.GetDBGameFree().GetRatio()
	return float64(ratio / 1000.0)
}

// 增加boss池
func (this *FishingSceneData) AddBossPond(player *FishingPlayerData, fishtype int32, bulletM int32) int64 {
	bossPond := int64(0)

	// boss抽水比例
	bossRatio := this.GetBossDrainageBet()
	score := int64(float64(bulletM) * bossRatio)
	// 打食人鱼BOSS
	if fishtype == fishing.SUNWUKONG {
		//bossPond = publicPandService.AddPiranhaBossPond(TableConfig.GetInc().GetServiceId(), tableInfo.TableId, score)
	} else {
		fishlogger.Infof("玩家：%v, 鱼的ID为：%v, Boss抽水比例：%v, Boss池增加的数值：%v\n", player.SnId, fishtype, bossRatio, score)
		// 减掉个人池数值
		if score > 0 {
			player.MoneyPond -= score
			base.GetCoinPoolMgr().AddBossPond(this.DbGameFree.SceneType, score)
		}
	}

	return bossPond
}

// Boss死亡 更新各种池
func (this *FishingSceneData) isBossDie(player *FishingPlayerData, score int64, bossPond int64) {
	minNum := score
	if minNum > bossPond {
		minNum = bossPond
	}
	player.MoneyPond += minNum
	base.GetCoinPoolMgr().AddBossPond(this.DbGameFree.SceneType, -minNum)
	fishlogger.Infof("玩家：%v，Boss奖池剩余金币数量：%v\n", player.SnId, bossPond)
}

// PathUsable 路径可用
func (this *FishingSceneData) PathUsable(path int32) bool {
	// 加锁管理
	/*	this.fishsMutex.Lock()
		defer this.fishsMutex.Unlock()
		// 当前时间
		now := time.Now().Unix()
		// 遍历鱼
		for _, v := range this.fish_list {
			if v.Path == path {
				//相同路径 比较出鱼时间 小于2秒 不让用
				if now-v.InitTime <= 2 {
					return false
				}
			}
		}*/
	return true
}

// 暂停出鱼
func (this *FishingSceneData) Freeze(sec int64) {
	this.af.Pause(sec * 1000)
	// 增加普通出鱼生命周期
	offset := int32(sec * 1000 / 100)
	for _, f := range this.fish_list {
		f.AddTime(offset)
	}
}

// 停止出鱼
func (this *FishingSceneData) Stop() bool {
	//停止出鱼
	this.af.Stop()
	return true
}
