package sugarrush import ( "github.com/tomas-qstarrs/boost/mathx" "github.com/tomas-qstarrs/boost/randx" "mongo.games.com/game/gamesrv/slotspkg/internal/generic/key" "mongo.games.com/game/gamesrv/slotspkg/internal/module/shared" "mongo.games.com/game/gamesrv/slotspkg/slots/intf" "mongo.games.com/game/gamesrv/slotspkg/slots/plugin/generic" ) // PluginBaseSpin is derived from generic.PluginBase type PluginBaseSpin struct { generic.PluginBase } // Theme implements generic.PluginBase.Theme func (p *PluginBaseSpin) Theme() string { return key.SugarRush } type CustomEliminate struct { LinkPositions []*shared.LinkPositions AppendSymbols [][]int64 FormattedSymbols [][]int64 LinePays []float64 LineMultis []int64 WinCoins []int64 MaxWin bool } type CustomPay struct { Pay int64 } type CustomMulti struct { MultiTable [][]int64 } type Col struct { Values []int64 } type Formation struct { Cols []Col } // Customs implements generic.PluginBase.Customs func (p *PluginBaseSpin) Customs() []interface{} { return []interface{}{ &CustomPay{}, &CustomEliminate{}, &MultiTable{}, &CustomMulti{}, } } // AfterBaseSpin is called after base spin func (p *PluginBaseSpin) AfterBaseSpin(m intf.Master) { } func (f *Formation) AddFreeSpin(m intf.Master) { if m.Cursor().GetType() != key.BaseSpin { return } scatterCount := Descx(m).RandScatterCount() // get current count currentCount := int64(0) for _, col := range f.Cols { for _, v := range col.Values { if v == SymbolScatter { currentCount++ } } } // get col not has scatter colNotHasScatter := make([]int64, 0) for i, col := range f.Cols { hasScatter := false for _, v := range col.Values { if v == SymbolScatter { hasScatter = true break } } if !hasScatter { colNotHasScatter = append(colNotHasScatter, int64(i)) } } needCount := scatterCount - currentCount if needCount <= 0 { needCount = 0 } // rand new scatter for col not has scatter randx.RandShuffle(m.Randx(), colNotHasScatter) for i := int64(0); i < needCount; i++ { col := colNotHasScatter[i] f.Cols[col].Values[randx.RandRangeInt63n(m.Randx(), 0, 6)] = SymbolScatter } } func (f *Formation) fillCol(m intf.Master, col int64, eliminate bool, isFree bool) []int64 { appendValues := make([]int64, 0) values := f.Cols[col].Values left := ReelHeight - int64(len(values)) scatterCount := int64(0) for _, v := range values { if v == SymbolScatter { scatterCount++ break } } topItemID := int64(0) isTop := false if len(values) > 0 && eliminate { topItemID = values[0] isTop = true } for left > 0 { itemID := Descx(m).RandItemID(topItemID, isFree, isTop) if isTop { isTop = false } topItemID = itemID itemCount := Descx(m).RandItemCount(itemID, left, isFree) if SymbolScatter == itemID { itemCount = mathx.Min(itemCount, Descx(m).GetOtherConfig().MaxScatterPerCol-scatterCount) } if SymbolScatter == itemID { scatterCount += itemCount } for i := int64(0); i < itemCount; i++ { // append at head values = append([]int64{itemID}, values...) appendValues = append([]int64{itemID}, appendValues...) } left -= itemCount } f.Cols[col].Values = values return appendValues } func (f *Formation) fillCols(m intf.Master, eliminate bool, isFree bool) [][]int64 { newSymbols := make([][]int64, ReelWidth) for j := int64(0); j < ReelWidth; j++ { newSymbols[j] = f.fillCol(m, j, eliminate, isFree) } return newSymbols } func (f *Formation) GetValues() [][]int64 { values := make([][]int64, ReelHeight) for j := int64(0); j < ReelWidth; j++ { for i := int64(0); i < ReelHeight; i++ { values[j] = append(values[j], f.Cols[j].Values[i]) } } return values } func createFormation() *Formation { formation := &Formation{} for i := 0; i < 7; i++ { formation.Cols = make([]Col, 7) } return formation } func (p *PluginBaseSpin) RandFormation(m intf.Master) [][]int64 { formation := &Formation{ Cols: make([]Col, 7), } for i := 0; i < 7; i++ { formation.Cols[i] = Col{ Values: make([]int64, 0), } formation.fillCol(m, int64(i), false, m.Cursor().GetType() == key.FreeSpin) } // 购买freespin if m.Exists(key.MachineRoundType) && m.Value(key.MachineRoundType).(int64) == RoundTypeBuyFreeSpin { formation.AddFreeSpin(m) } return formation.GetValues() } func DumpReel(symbols [][]int64) { // str := "" // for row := int64(0); row < 7; row++ { // for col := int64(0); col < 7; col++ { // str += fmt.Sprintf("%d ", symbols[col][row]) // } // str += "\n" // } // log.Infof("reel: \n%s\n", str) } func IsPositionLinked(linkPositions []*shared.LinkPositions, row int64, col int64) bool { for _, positions := range linkPositions { for _, position := range positions.Positions { if position == col*7+row { return true } } } return false } type MultiTable struct { Multi [][]int64 Count [][]int64 } func (t *MultiTable) Clear() { for i := 0; i < 7; i++ { for j := 0; j < 7; j++ { t.Multi[i][j] = 0 t.Count[i][j] = 0 } } } func (t *MultiTable) Update(m intf.Master, linkPositions []*shared.LinkPositions) { for _, positions := range linkPositions { for _, position := range positions.Positions { t.Count[position/7][position%7]++ } } // update multi for i := 0; i < 7; i++ { for j := 0; j < 7; j++ { t.Multi[i][j] = Descx(m).GetMulti(t.Count[i][j]) } } } func calcMulti(linkPositions *shared.LinkPositions, multiTable *MultiTable) int64 { totalMulti := int64(0) for _, p := range linkPositions.Positions { if multiTable.Multi[p/7][p%7] > 1 { totalMulti += multiTable.Multi[p/7][p%7] } } if totalMulti < 1 { totalMulti = 1 } return totalMulti } func (p *PluginBaseSpin) Eliminate(m intf.Master, customPay *CustomPay, multiTable *MultiTable) bool { cursorFormation := m.CursorFormation() formattedSymbols := cursorFormation.GetReelFormattedSymbols() DumpReel(formattedSymbols) // 清空基础赢钱 cursorFormation.SetWin(0) // 根据赔付计算multi type linkPositions, _, linePays := m.TryLinkMatrixSymbols(1, formattedSymbols) DumpReel(multiTable.Multi) lineMultis := make([]int64, len(linePays)) symbols := cursorFormation.GetReelFormattedSymbols() success := mathx.Sum(linePays) > 0 // erase formation := createFormation() for j := int64(0); j < 7; j++ { for i := int64(0); i < 7; i++ { if !IsPositionLinked(linkPositions, i, j) { formation.Cols[j].Values = append(formation.Cols[j].Values, symbols[j][i]) } } } appendFormattedSymbols := formation.fillCols(m, true, m.Cursor().GetType() == key.FreeSpin) lineNum := len(linePays) winCoins := make([]int64, lineNum) totalPay := int64(0) for lineIdx, pay := range linePays { multi := calcMulti(linkPositions[lineIdx], multiTable) lineMultis[lineIdx] = multi winCoins[lineIdx] = int64(float64(m.Cursor().GetSingleBet()) * pay * float64(multi)) totalPay += winCoins[lineIdx] } isMaxWin := false maxValue := Descx(m).GetOtherConfig().FreespinMaxWin * m.Cursor().GetBet() currentWin := m.TotalWin() + customPay.Pay // log.Infof("Eliminate totalPay: %d, currentWin: %d, maxValue: %d", totalPay, currentWin, maxValue) if m.Cursor().GetType() == key.FreeSpin { if currentWin+totalPay >= maxValue { success = false isMaxWin = true totalPay = maxValue - currentWin m.SetProgressLeft(0) } } // 添加后续feature,这里是消除 m.AddCursorFeature(&CustomEliminate{ LinkPositions: linkPositions, AppendSymbols: appendFormattedSymbols, FormattedSymbols: formattedSymbols, LinePays: linePays, LineMultis: lineMultis, WinCoins: winCoins, MaxWin: isMaxWin, }).SetLifetime(1) DumpReel(formation.GetValues()) cursorFormation.SetFormattedSymbols(formation.GetValues()) // 累加pay customPay.Pay += totalPay multiTable.Update(m, linkPositions) // add new return success } func (p *PluginBaseSpin) BeforeSpin(m intf.Master) { m.AddCursorFeature(&CustomPay{}).SetLifetime(1) } func (p *PluginBaseSpin) Spin(m intf.Master, isFree bool) { // 生成轴 symbols := p.RandFormation(m) DumpReel(symbols) formation := m.CursorFormation() formation.SetFormattedSymbols(symbols) customPay := m.CursorCustom(&CustomPay{}).(*CustomPay) table := getMultiTable(m) // 存储 Formation元素 formation.SetFormattedDisplaySymbols(symbols) // 消除 n := 0 for p.Eliminate(m, customPay, table) { n++ if n > 1000 { break } } if customPay.Pay > 0 { m.CursorFeature(&CustomPay{}).SetWin(customPay.Pay) } m.AddCursorFeature(&CustomMulti{ MultiTable: table.Multi, }).SetLifetime(1) formattedSymbols := formation.GetReelFormattedSymbols() formation.SetFormattedFinalSymbols(formattedSymbols) } func (p *PluginBaseSpin) CheckFreeLimit(m intf.Master) { } // AfterSpin implements generic.PluginBase.AfterSpin func (p *PluginBaseSpin) AfterSpin(m intf.Master) { p.Spin(m, m.Cursor().GetType() == key.FreeSpin) switch m.Cursor().GetType() { case key.BaseSpin: p.AfterBaseSpin(m) case key.FreeSpin: p.AfterFreeSpin(m) p.CheckFreeLimit(m) } } // AfterFreeSpin is called after free spin func (p *PluginBaseSpin) AfterFreeSpin(m intf.Master) { } func (p *PluginBaseSpin) OnLeaveNode(m intf.Master) { if m.Next().GetType() == key.FreeSpin || m.Next().GetType() == key.BaseSpin { getMultiTable(m).Clear() } } func getMultiTable(m intf.Master) *MultiTable { if len(m.RootFeatures(&MultiTable{})) == 0 { table := &MultiTable{ Multi: make([][]int64, 7), Count: make([][]int64, 7), } for i := 0; i < 7; i++ { table.Multi[i] = make([]int64, 7) table.Count[i] = make([]int64, 7) } m.AddRootFeature(table) } return m.RootCustom(&MultiTable{}).(*MultiTable) }