game_sync/gamesrv/slotspkg/slots/entity/feature.go

287 lines
7.4 KiB
Go

package entity
import (
"github.com/mohae/deepcopy"
"mongo.games.com/game/gamesrv/slotspkg/internal/generic/errors"
"mongo.games.com/game/gamesrv/slotspkg/internal/module/shared"
"mongo.games.com/game/gamesrv/slotspkg/slots/desc"
)
// GetFeature finds feature by walking all nodes in node tree
func (e *Entity) GetFeature(featureID int64) *shared.Feature {
var f *shared.Feature
e.Walk(func(node *shared.Node) bool {
for _, feature := range node.Features {
if feature.ID == featureID {
f = feature
return true
}
}
return false
})
if f != nil {
return f
}
for _, feature := range e.NodeTree.ImageFeatures {
if feature.ID == featureID {
return feature
}
}
return nil
}
// GetTypeFeatures finds features by walking all nodes in node tree
func (e *Entity) GetTypeFeatures(node *shared.Node, typ string) []*shared.Feature {
features := make([]*shared.Feature, 0)
for _, feature := range node.Features {
if feature.Type == typ {
features = append(features, feature)
}
}
return features
}
// AddFeature adds feature to node features
func (e *Entity) AddFeature(node *shared.Node) int64 {
e.NodeTree.Incr++
featureID := e.NodeTree.Incr
node.Features = append(node.Features, &shared.Feature{
NodeID: node.ID,
ID: featureID,
SpinType: node.SpinType,
NodeType: node.Type,
Lifetime: -1,
Visiable: true,
})
return featureID
}
// PruneNodeFeatures prune features that has no lifetime
func (e *Entity) PruneNodeFeatures(node *shared.Node) {
for index := 0; index < len(node.Features); index++ {
feature := node.Features[index]
if feature.Lifetime == 0 {
node.Features = append(node.Features[:index],
node.Features[index+1:]...)
e.DeleteCustom(feature.ID)
index--
}
}
}
// FeaturesPassTime decreases features' lifetime on specific node
func (e *Entity) FeaturesPassTime(node *shared.Node, n int64) {
features := node.Features
for _, feature := range features {
if feature.Lifetime > 0 {
if feature.Lifetime > n {
feature.Lifetime -= n
} else {
feature.Lifetime = 0
}
}
}
}
// UpdateImageFeatures saves image info from features to image features
func (e *Entity) UpdateImageFeatures() {
e.updateImageFeaturesByNode()
e.updateImageFeaturesByFormation()
}
func (e *Entity) updateImageFeaturesByNode() {
var features []*shared.Feature
for _, feature := range e.CursorNode().Features {
if feature.Imageable && feature.SeqID == 0 {
features = append(features, feature)
}
}
nodeType := e.CursorNode().Type
for index := 0; index < len(e.NodeTree.ImageFeatures); index++ {
imageFeature := e.NodeTree.ImageFeatures[index]
if imageFeature.SeqID == 0 && imageFeature.NodeType == nodeType {
e.NodeTree.ImageFeatures = append(
e.NodeTree.ImageFeatures[:index],
e.NodeTree.ImageFeatures[index+1:]...)
e.DeleteCustom(imageFeature.ID)
index--
}
}
for _, feature := range features {
e.copyFeatureToImage(feature)
}
}
func (e *Entity) updateImageFeaturesByFormation() {
var features []*shared.Feature
for _, feature := range e.CursorNode().Features {
if feature.Imageable && feature.SeqID > 0 {
features = append(features, feature)
}
}
for _, feature := range features {
formationID := feature.FormationID
for index := 0; index < len(e.NodeTree.ImageFeatures); index++ {
imageFeature := e.NodeTree.ImageFeatures[index]
if imageFeature.SeqID > 0 && imageFeature.FormationID == formationID {
e.NodeTree.ImageFeatures = append(
e.NodeTree.ImageFeatures[:index],
e.NodeTree.ImageFeatures[index+1:]...)
e.DeleteCustom(imageFeature.ID)
index--
}
}
}
for _, feature := range features {
e.copyFeatureToImage(feature)
}
}
func (e *Entity) copyFeatureToImage(feature *shared.Feature) {
custom := e.GetCustom(feature.ID)
e.NodeTree.Incr++
featureID := e.NodeTree.Incr
// deep copy feature
newFeature := deepcopy.Copy(feature).(*shared.Feature)
newFeature.ID = featureID
e.NodeTree.ImageFeatures = append(e.NodeTree.ImageFeatures, newFeature)
e.AddFeatureCustom(featureID, deepcopy.Copy(custom))
}
func (e *Entity) copyFeatureToNode(node *shared.Node, feature *shared.Feature) {
custom := e.GetCustom(feature.ID)
e.NodeTree.Incr++
featureID := e.NodeTree.Incr
// deep copy feature
newFeature := deepcopy.Copy(feature).(*shared.Feature)
newFeature.ID = featureID
newFeature.Imageable = false
newFeature.Lifetime = 0
node.Features = append(node.Features, newFeature)
e.AddFeatureCustom(featureID, deepcopy.Copy(custom))
}
// AttachFeatureToFormation attaches feature to formation by seq ID
func (e *Entity) AttachFeatureToFormation(feature *shared.Feature, seqID int64) {
formation := e.GetFormation(feature.NodeID, seqID)
feature.SeqID = seqID
feature.FormationID = formation.ID
}
// InitNextFeatures inits init symbols features for next node
func (e *Entity) InitNextFeatures() {
e.initNextFeaturesByNode()
e.initNextFeaturesByFormations()
}
func (e *Entity) initNextFeaturesByNode() {
nextNodeType := e.NextNode().Type
cursorNodeType := e.CursorNode().Type
if nextNodeType == cursorNodeType {
return
}
for _, imageFeature := range e.NodeTree.ImageFeatures {
if imageFeature.SeqID == 0 && imageFeature.NodeType == nextNodeType {
e.copyFeatureToNode(e.NextNode(), imageFeature)
}
}
}
func (e *Entity) initNextFeaturesByFormations() {
for _, nextFormation := range e.NextNode().Formations {
e.genFormationFeatures(nextFormation)
}
}
func (e *Entity) genFormationFeatures(nextFormation *shared.Formation) {
var initFeatures []*shared.Feature
defer func() {
for _, initFeature := range initFeatures {
e.copyFeatureToNode(e.NextNode(), initFeature)
}
}()
desc := e.getNextFormationDesc(nextFormation)
if desc == nil { // No formation in next node
return
}
// Check same formation id form cursor node, no need to copy feature
for _, cursorFormation := range e.CursorNode().Formations {
if nextFormation.ID == cursorFormation.ID {
return
}
}
// Next node is progressed, just read history
if e.NextNode().ProgressValue > 0 {
initFeatures = e.getImageFeatures(e.NextNode(), desc)
return
}
method := e.getNexFormationMethod(e.NextNode(), desc)
// Switch method to get all kinds of init symbols source
switch method {
case initSymbolsMethodNone:
// do nothing
case initSymbolsMethodConst:
// do nothing
case initSymbolsMethodRandom:
// do nothing
case initSymbolsMethodCopy:
initFeatures = e.getCopyFeatures(e.NextNode(), desc)
case initSymbolsMethodImage:
initFeatures = e.getImageFeatures(e.NextNode(), desc)
default:
panic(errors.With(method).Errorf("unsupported init symbols method"))
}
}
func (e *Entity) getCopyFeatures(node *shared.Node, desc *desc.FormationSeqDesc) []*shared.Feature {
for {
node = e.GetNode(node.Parent)
if node.ID == e.NodeTree.Root {
return nil
}
if desc.SeqID > int64(len(node.Formations)) {
continue
}
var features []*shared.Feature
for _, feature := range node.Features { // Use seq ID
if feature.SeqID == node.Formations[desc.SeqID-1].SeqID {
features = append(features, feature)
}
}
return features
}
}
func (e *Entity) getImageFeatures(node *shared.Node, desc *desc.FormationSeqDesc) []*shared.Feature {
var features []*shared.Feature
formation := node.Formations[desc.SeqID-1]
for _, imageFeature := range e.NodeTree.ImageFeatures { // Use formation ID
if imageFeature.SeqID > 0 && imageFeature.FormationID == formation.ID {
features = append(features, imageFeature)
}
}
return features
}