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 }