game_sync/gamesrv/slotspkg/slots/plugin/sugarrush/base.go

420 lines
9.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
}