package game import ( "log" "regexp" "strconv" "strings" ) type dynamicCardImplementation struct { card *Card spawnTilesFunc func(*LocalState, *Player) []*Tile genFullActions func(*Unit) []*FullAction genFreeActions func(Permanent) []*FreeAction targetDesc TargetDesc _stateBasedActions func(*LocalState, Permanent) _additionalSpawnsFor func(Permanent, CardType) []*Tile _onEntering func(*Tile) _onLeaving func(*Tile) _onPlay func(*PlayAction) _onETB func(*LocalState, Permanent) _onPile func(Permanent) _onUnpile func(Permanent) _onDrop func(Permanent) } func (impl *dynamicCardImplementation) spawnTiles(s *LocalState, p *Player) []*Tile { if impl.spawnTilesFunc != nil { return impl.spawnTilesFunc(s, p) } return nil } func (impl *dynamicCardImplementation) fullActions(u *Unit) []*FullAction { if impl.genFullActions != nil { return impl.genFullActions(u) } return nil } func (impl *dynamicCardImplementation) freeActions(p Permanent) []*FreeAction { if impl.genFreeActions != nil { return impl.genFreeActions(p) } return nil } func (impl *dynamicCardImplementation) playTargets() TargetDesc { return impl.targetDesc } func (impl *dynamicCardImplementation) stateBasedActions(s *LocalState, p Permanent) { if impl._stateBasedActions != nil { impl._stateBasedActions(s, p) } } func (impl *dynamicCardImplementation) additionalSpawnsFor(p Permanent, ct CardType) []*Tile { if impl._additionalSpawnsFor != nil { return impl._additionalSpawnsFor(p, ct) } return nil } func (impl *dynamicCardImplementation) onEntering(t *Tile) { if impl._onEntering != nil { impl._onEntering(t) } } func (impl *dynamicCardImplementation) onLeaving(t *Tile) { if impl._onLeaving != nil { impl._onLeaving(t) } } func (impl *dynamicCardImplementation) onPlay(a *PlayAction) { if impl._onPlay != nil { impl._onPlay(a) } } func (impl *dynamicCardImplementation) onETB(s *LocalState, p Permanent) { if impl._onETB != nil { impl._onETB(s, p) } } func (impl *dynamicCardImplementation) onPile(p Permanent) { if impl._onPile != nil { impl._onPile(p) } } func (impl *dynamicCardImplementation) onUnpile(p Permanent) { if impl._onUnpile != nil { impl._onUnpile(p) } } func (impl *dynamicCardImplementation) onDrop(p Permanent) { if impl._onDrop != nil { impl._onDrop(p) } } func newParsedCardImplementation(c *Card) *dynamicCardImplementation { impl := &dynamicCardImplementation{card: c} for _, effect := range c.getEffects() { impl.parseEffect(effect) } for _, fullAction := range c.getCanonicalValues("full_action") { impl.parseFullAction(fullAction) } return impl } func (impl *dynamicCardImplementation) parseEffect(effect string) { if strings.Contains(effect, "can be played") { impl.parsePlayModification(effect) } } func (impl *dynamicCardImplementation) parseFullAction(fullAction string) { var resolveProtos []ActionFuncPrototype gainRessourceRegex := regexp.MustCompile(`gain [1-9][0-9]* resource`) if find := gainRessourceRegex.Find([]byte(fullAction)); find != nil { tokens := strings.SplitN(string(find), " ", 3) gain, err := strconv.Atoi(tokens[1]) if err != nil { log.Panicf("Failed to parse resource gain full action: %s", err) } resolveProto := func(a Action) ActionResolveFunc { u := a.Source().(*Unit) return func(*LocalState) { u.controller.gainResource(gain) } } resolveProtos = append(resolveProtos, resolveProto) } if resolveProtos != nil { impl.genFullActions = func(u *Unit) []*FullAction { fullActions := make([]*FullAction, 0, len(resolveProtos)) for _, proto := range resolveProtos { fullActions = append(fullActions, newFullAction(u, proto, "")) } return fullActions } } } func parseUnitConstrain(constrain string) func(ctx interface{}, u *Unit) bool { enemy := strings.Contains(constrain, "enemy") if enemy { return func(ctx interface{}, e *Unit) bool { p := ctx.(*Player) return p.IsEnemy(e.Controller()) } } else { log.Println("Only enemy unit constrains are implemented yet") } return nil } func parseLocation(location string) func(interface{}, *LocalState) []*Tile { r := -1 var what string if strings.Contains(location, "on") { r = 0 tokens := strings.SplitN(location, "on ", 2) what = tokens[1] } else if strings.Contains(location, "next to") { r = 1 tokens := strings.SplitN(location, "next to ", 2) what = tokens[1] } else if strings.Contains(location, "in range") { tokens := strings.SplitN(location, "in range ", 2) afterRangePattern := tokens[1] var rangeStr string tokens = strings.SplitN(afterRangePattern, " ", 2) rangeStr, what = tokens[0], tokens[1] var err error r, err = strconv.Atoi(rangeStr) if err != nil { log.Panicf("Unsupported range pattern \"%s\" in \"%s\"", "range "+rangeStr, location) } } if r == -1 || what == "" { return nil } if strings.Contains(what, "unit") { constrainFunc := parseUnitConstrain(what) if constrainFunc != nil { return func(ctx interface{}, s *LocalState) []*Tile { origins := s.Map().FilterTiles(func(t *Tile) bool { if t.Permanent != nil && t.Permanent.Card().Type == CardTypes.Unit && constrainFunc(ctx, t.Permanent.(*Unit)) { return true } return false }) if r == 0 { return origins } else { tiles := []*Tile{} for _, origin := range origins { tiles = append(tiles, TilesInRangeFromOrigin(s.Map(), origin.Position, r)...) } return tiles } } } } log.Println("Only unit location constrains are implemented yet") return nil } func (impl *dynamicCardImplementation) parsePlayModification(effect string) { tokens := strings.SplitN(effect, " can be played ", 2) what, how := tokens[0], tokens[1] if what == strings.ToLower(impl.card.Name) { if locationFunc := parseLocation(how); locationFunc != nil { impl.spawnTilesFunc = func(p *LocalState, s *Player) []*Tile { return locationFunc(s, p) } } else { log.Println("Play modification other than locations are not implemented ") } } else { log.Println("Play modification for other cards are not implemented") } }