1561 lines
49 KiB
Go
1561 lines
49 KiB
Go
package fishing
|
||
|
||
import (
|
||
"fmt"
|
||
"math"
|
||
"math/rand"
|
||
"strconv"
|
||
"strings"
|
||
"sync"
|
||
"time"
|
||
|
||
"github.com/cihub/seelog"
|
||
"mongo.games.com/goserver/core"
|
||
"mongo.games.com/goserver/core/netlib"
|
||
"mongo.games.com/goserver/core/timer"
|
||
"mongo.games.com/goserver/srvlib/action"
|
||
srvlibproto "mongo.games.com/goserver/srvlib/protocol"
|
||
|
||
"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"
|
||
fishing_proto "mongo.games.com/game/protocol/fishing"
|
||
"mongo.games.com/game/srvdata"
|
||
)
|
||
|
||
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.GetDBGameFree().GetSceneType())
|
||
this.keyGameId = this.GetKeyGameId()
|
||
this.testing = this.GetTesting()
|
||
this.gamefreeId = this.GetGameFreeId()
|
||
this.groupId = this.GetGroupId()
|
||
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.GetDBGameFree().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.Level
|
||
player.AddPlayerExp(int64(sumExp))
|
||
if oldLevel != player.Level {
|
||
//触发炮倍解锁事件
|
||
player.PlayerEvent(fishing.Event_Player_UpLevel, player.Level)
|
||
}
|
||
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
|
||
}
|
||
action.MulticastMessageToServer(gateSess, packetid, msg, v...)
|
||
}
|
||
}
|
||
|
||
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.GetDBGameFree().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.GetDBGameFree().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
|
||
}
|