package tome_game import ( "fmt" "log" "strconv" "strings" . "git.saintnet.tech/tomecraft/tome_lib" ) type Game struct { GameBoard *Board SentinalPlayer *Player ScourgePlayer *Player SentinalDeck *Deck ScourgeDeck *Deck SentinalGrave *Deck ScourgeGrave *Deck CurrentTurn int CardBuffer *Deck ScryBuffer int BottomBuffer int CanDraw bool HasDrawn bool QueuedEffect *Effect TargetReq TargetStatus Status GameStatus } func NewGame(sentDeck []int, scoDeck []int) *Game { deckA := LoadDeck(SentinalID, sentDeck) deckB := LoadDeck(ScourgeID, scoDeck) return &Game{ GameBoard: NewBoard(), SentinalPlayer: NewPlayer("Sentinal", SentinalID), ScourgePlayer: NewPlayer("Scourge", ScourgeID), SentinalDeck: deckA, ScourgeDeck: deckB, SentinalGrave: DeckFromCards([]*Card{}), ScourgeGrave: DeckFromCards([]*Card{}), CurrentTurn: 0, //start with no turn CardBuffer: DeckFromCards([]*Card{}), ScryBuffer: 0, BottomBuffer: 0, CanDraw: false, HasDrawn: false, QueuedEffect: nil, TargetReq: TargetNone, Status: StatusLobby, } } func (g *Game) String() string { return fmt.Sprintf("Sen(%v): %v\n\n%v\n\nSco(%v): %v\nStatus:%v Draw:%v Turn:%v\nBuffers\nCardBuffer:%v\nSB:%v BB:%v\nEffectTarget:%v\nQueuedEffect:%v\n", g.SentinalPlayer.Life, g.SentinalPlayer.Hand, g.GameBoard, g.ScourgePlayer.Life, g.ScourgePlayer.Hand, g.Status, g.CanDraw, g.CurrentTurn, g.CardBuffer, g.ScryBuffer, g.BottomBuffer, g.TargetReq, g.QueuedEffect) } func (g *Game) Parse(cmd *Command) *CommandResult { if g.Status == StatusDraw || g.Status == StatusScourgeWin || g.Status == StatusSentinalWin || g.Status == StatusStop { return &CommandResult{ PlayerID: cmd.PlayerID, ResultType: cmd.Type, StateResult: NewView(cmd.PlayerID, g), ActionResult: nil, DebugResult: g, } } var state_res *GameView var action_res *Deck var debug_res *Game var res_type CmdType if cmd.Type == ActCmd && g.Status == StatusPlaying { action_res = g.PlayerAct(cmd.PlayerID, cmd.Cmd) state_res = nil debug_res = nil res_type = ActCmd } else if cmd.Type == StateCmd { state_res = g.PlayerStateAct(cmd.PlayerID, cmd.Cmd) action_res = nil debug_res = nil res_type = StateCmd } else if cmd.Type == DebugCmd { state_res = nil action_res = nil debug_res = g res_type = DebugCmd } else { state_res = nil action_res = nil debug_res = nil res_type = InvalidCmd } if g.Status == StatusPlaying { g.StateChanges() } return &CommandResult{ PlayerID: cmd.PlayerID, ResultType: res_type, StateResult: state_res, ActionResult: action_res, DebugResult: debug_res, } } func (g *Game) StateChanges() { //check for no actions first if len(g.SentinalPlayer.Hand) == 0 && g.SentinalDeck.Size() == 0 { if g.GameBoard.Empty(SentinalID) { g.Status = StatusScourgeWin } } if len(g.ScourgePlayer.Hand) == 0 && g.ScourgeDeck.Size() == 0 { if g.GameBoard.Empty(ScourgeID) { g.Status = StatusSentinalWin } } //apply CurrentTurn player effects for i := 0; i < len(g.GetPlayer(g.CurrentTurn).Effects); i++ { OraclePlayerEffect(g.GetPlayer(g.CurrentTurn), g) } //reapply card effects g.GameBoard.ResetCards() for i := 0; i < len(g.GameBoard.Sentinal); i++ { OracleTick(g.GameBoard.Sentinal[i], g) //apply effect stacks first OracleTick(g.GameBoard.Scourge[i], g) } for i := 0; i < len(g.GameBoard.Sentinal); i++ { OracleEffect(g.GameBoard.Sentinal[i], g) //activate effects OracleEffect(g.GameBoard.Scourge[i], g) } //check life //this is second on purpose so that it shadows deck out win conditions //if you use your last action to win the game, you should actually win if g.SentinalPlayer.Life < 0 { g.Status = StatusScourgeWin } if g.ScourgePlayer.Life < 0 { g.Status = StatusSentinalWin } if g.ScourgePlayer.Life < 0 && g.SentinalPlayer.Life < 0 { g.Status = StatusDraw } } func (g *Game) PlayerStateAct(id int, cmd string) *GameView { switch cmd { case "d": Debug(g) case "g": //game state return NewView(id, g) case "b": //begin game if g.Status == StatusPlaying { break } if id == SentinalID { g.SentinalPlayer.Ready = true } else if id == ScourgeID { g.ScourgePlayer.Ready = true } if g.SentinalPlayer.Ready && g.ScourgePlayer.Ready && g.Status == StatusLobby { g.Status = StatusReady g.SentinalDeck.Shuffle() g.ScourgeDeck.Shuffle() if id == SentinalID { g.SentinalPlayer.Hand = g.SentinalDeck.Cards[len(g.SentinalDeck.Cards)-5 : len(g.SentinalDeck.Cards)] g.SentinalDeck.Cards = g.SentinalDeck.Cards[0 : len(g.SentinalDeck.Cards)-5] g.ScourgePlayer.Hand = g.ScourgeDeck.Cards[len(g.ScourgeDeck.Cards)-6 : len(g.ScourgeDeck.Cards)] g.ScourgeDeck.Cards = g.ScourgeDeck.Cards[0 : len(g.ScourgeDeck.Cards)-6] g.CurrentTurn = SentinalID } else { g.SentinalPlayer.Hand = g.SentinalDeck.Cards[len(g.SentinalDeck.Cards)-6 : len(g.SentinalDeck.Cards)] g.SentinalDeck.Cards = g.SentinalDeck.Cards[0 : len(g.SentinalDeck.Cards)-6] g.ScourgePlayer.Hand = g.ScourgeDeck.Cards[len(g.ScourgeDeck.Cards)-5 : len(g.ScourgeDeck.Cards)] g.ScourgeDeck.Cards = g.ScourgeDeck.Cards[0 : len(g.ScourgeDeck.Cards)-5] g.CurrentTurn = ScourgeID } } case "s": //start turn if g.Status == StatusReady { //first turn g.Status = StatusPlaying } //skip draw step when 0 life if g.GetPlayer(id).Life <= 0 { g.CanDraw = false g.HasDrawn = true } else { g.CanDraw = true g.HasDrawn = false } if id != g.CurrentTurn { return nil } for _, v := range g.GetBoard(id) { OracleUpkeep(v, g) } case "e": //end turn and clean up if id != g.CurrentTurn { return nil } if g.CanDraw == true || g.QueuedEffect != nil { log.Println("player tried to end turn while still actions available") return nil } g.CardBuffer = DeckFromCards([]*Card{}) for _, v := range g.GetBoard(id) { OracleEndstep(v, g) } if g.CurrentTurn == SentinalID { g.CurrentTurn = ScourgeID } else { g.CurrentTurn = SentinalID } } return NewView(id, g) } func (g *Game) PlayerAct(id int, cmd string) *Deck { if g.Status != StatusPlaying { return nil } if id != g.CurrentTurn { return nil } var curr *Player var opp *Player var currD *Deck curr = g.GetPlayer(id) opp = g.GetOpponent(id) currD = g.GetDeck(id) cmd_s := strings.Split(cmd, " ") if len(cmd_s) < 1 { return nil } switch cmd_s[0] { case "s": //scry: return scry options off top of deck if !g.CanDraw || g.QueuedEffect != nil { return nil } if g.ScryBuffer == 0 { g.ScryBuffer = curr.Life } if g.CardBuffer.Size() <= 0 { g.CardBuffer = currD.Scry(g.ScryBuffer) } g.ScryBuffer = 0 return g.CardBuffer case "d": //draw: return player hand if !g.CanDraw || g.QueuedEffect != nil || g.BottomBuffer != 0 { return nil } if len(cmd_s) != 2 || !g.CanDraw { return nil } if curr.Life == 0 { return DeckFromCards(curr.Hand) } x_i, err := strconv.Atoi(cmd_s[1]) if err != nil { log.Printf("error converting draw target %v", err) return nil } if x_i > g.CardBuffer.Size() || x_i < 0 { return nil } x := g.CardBuffer.Cards[x_i] if x.Owner != g.CurrentTurn { log.Println("drew a card from our deck that isn't our own") } g.CardBuffer = DeckFromCards(append(g.CardBuffer.Cards[:x_i], g.CardBuffer.Cards[x_i+1:]...)) currD.Bottom(g.CardBuffer) curr.Hand = append(curr.Hand, x) g.CanDraw = false g.HasDrawn = true g.CardBuffer.Reset() return DeckFromCards(curr.Hand) case "b": if g.BottomBuffer == 0 || g.ScryBuffer != 0 { return nil } if len(cmd_s) != 2 { return nil } x_i, err := strconv.Atoi(cmd_s[1]) if err != nil { log.Printf("error converting bottom target %v", err) return nil } if x_i > g.CardBuffer.Size() || x_i < 0 { return nil } bottomTarget := g.CardBuffer.Cards[x_i] g.CardBuffer.Cards = append(g.CardBuffer.Cards[:x_i], g.CardBuffer.Cards[x_i+1:]...) currD.Bottom(DeckFromCards([]*Card{bottomTarget})) g.BottomBuffer = g.BottomBuffer - 1 if g.BottomBuffer > 0 { return g.CardBuffer } else { g.BottomBuffer = 0 return nil } case "m": //move: return player board or [] if invalid if len(cmd_s) != 3 { return nil } if !g.HasDrawn || g.QueuedEffect != nil || g.BottomBuffer != 0 { return nil } x_i, err := strconv.Atoi(cmd_s[1]) if err != nil { log.Println(err) return nil } y_i, err := strconv.Atoi(cmd_s[2]) if err != nil { log.Println(err) } if g.GameBoard.CanMove(g.CurrentTurn, x_i, y_i) { OracleMove(g.GameBoard.GetCard(g.CurrentTurn, x_i), x_i, y_i, g) g.GameBoard.Move(g.CurrentTurn, x_i, y_i) return DeckFromCards(g.GameBoard.GetRow(g.CurrentTurn)) } else { return DeckFromCards([]*Card{}) } case "a": //attack if len(cmd_s) != 3 { return nil } if !g.HasDrawn || g.QueuedEffect != nil || g.BottomBuffer != 0 { return nil } x_i, err := strconv.Atoi(cmd_s[1]) if err != nil { log.Println(err) return nil } y_i, err := strconv.Atoi(cmd_s[2]) if err != nil { log.Println(err) return nil } if g.GameBoard.CanAttack(g.CurrentTurn, x_i, y_i) { OracleAttack(g.GameBoard.GetCard(g.CurrentTurn, x_i), x_i, y_i, g) res := g.GameBoard.Attack(g.CurrentTurn, x_i, y_i) var aid, did int aid = g.GetPlayer(id).Id did = g.GetOpponent(id).Id attacker := g.GetBoard(aid)[x_i] defender := g.GetBoard(did)[y_i] switch res { case 1: opp.Life = opp.Life - 1 case 2: OracleLeaves(defender, g) g.GameBoard.Remove(defender) g.Bury(defender) case 3: OracleLeaves(attacker, g) g.GameBoard.Remove(attacker) g.Bury(attacker) case 4: OracleLeaves(attacker, g) OracleLeaves(defender, g) g.GameBoard.Remove(attacker) g.GameBoard.Remove(defender) g.Bury(attacker) g.Bury(defender) } return DeckFromCards(g.GameBoard.GetRow(g.CurrentTurn)) } else { log.Println("can't attack") return DeckFromCards([]*Card{}) } case "p": //play: return player board or [] if invalid if len(cmd_s) != 3 { fmt.Println("not enough arguments") return nil } if !g.HasDrawn || g.QueuedEffect != nil || g.BottomBuffer != 0 { return nil } x_i, err := strconv.Atoi(cmd_s[1]) if err != nil { log.Println(err) return nil } y_i, err := strconv.Atoi(cmd_s[2]) if err != nil { log.Println(err) return nil } if x_i < 0 || x_i >= len(curr.Hand) { return nil } card := curr.Hand[x_i] shouldPlace := true if g.GameBoard.CanPlay(g.CurrentTurn, card, y_i) { shouldPlace = OracleCast(card, g) placed := g.GameBoard.Play(g.CurrentTurn, card, y_i, shouldPlace) if placed { OracleEnters(card, g) } curr.Hand = append(curr.Hand[:x_i], curr.Hand[x_i+1:]...) return DeckFromCards(g.GameBoard.GetRow(g.CurrentTurn)) } else { fmt.Println("couldn't play") return DeckFromCards([]*Card{}) } case "t": //target: apply queued effect to target,return affected board or nil if len(cmd_s) != 3 { fmt.Println("not enough arguments") return nil } if !g.HasDrawn || g.QueuedEffect == nil || g.BottomBuffer != 0 { return nil } board, err := strconv.Atoi(cmd_s[1]) if err != nil { log.Println(err) return nil } pos, err := strconv.Atoi(cmd_s[2]) if err != nil { log.Println(err) return nil } if pos == -1 { fmt.Println("no target requested, fizzling effect") g.QueuedEffect = nil g.TargetReq = TargetNone return DeckFromCards(g.GetBoard(board)) } if pos < 0 || pos >= 3 { fmt.Println("board position out of bounds") return nil } switch g.TargetReq { case TargetSelf: if board != g.CurrentTurn && g.GetBoard(board)[pos].Id != g.QueuedEffect.Owner { fmt.Println("self target not on self") return nil } case TargetOwn: if board != g.CurrentTurn { fmt.Println("own target not on own board") return nil } case TargetOwnEmpty: if board != g.CurrentTurn { fmt.Println("own target not on board") return nil } if g.GetBoard(board)[pos].Type != EmptyValue { fmt.Println("own empty target not empty") return nil } case TargetOpp: if board == g.CurrentTurn { fmt.Println("opponent target not on oponents board") return nil } case TargetOppEmpty: if board == g.CurrentTurn { fmt.Println("opponent target not on board") return nil } if g.GetBoard(board)[pos].Type != EmptyValue { fmt.Println("opponent empty target not empty") return nil } case TargetNone: fmt.Println("NoneTarget'd ability?") return nil } //we know the target is valid now, so modify the effect to have a valid target g.QueuedEffect.Target = g.GetBoard(board)[pos].Id AddEffect(g.GetBoard(board)[pos], g.QueuedEffect) g.QueuedEffect = nil g.TargetReq = TargetNone return DeckFromCards(g.GetBoard(board)) default: fmt.Println("Invalid act command") return nil } return nil } func Debug(g *Game) { return } func (g *Game) GetPlayer(id int) *Player { if id == SentinalID { return g.SentinalPlayer } else if id == ScourgeID { return g.ScourgePlayer } else { return nil } } func (g *Game) GetOpponent(id int) *Player { if id == SentinalID { return g.ScourgePlayer } else if id == ScourgeID { return g.SentinalPlayer } else { return nil } } func (g *Game) GetDeck(id int) *Deck { if id == SentinalID { return g.SentinalDeck } else if id == ScourgeID { return g.ScourgeDeck } else { return nil } } func (g *Game) GetOpponentDeck(id int) *Deck { if id == SentinalID { return g.ScourgeDeck } else if id == ScourgeID { return g.SentinalDeck } else { return nil } } func (g *Game) GetBoard(id int) []*Card { return g.GameBoard.GetRow(id) } func (g *Game) GetOpponentBoard(id int) []*Card { if id == SentinalID { return g.GameBoard.GetRow(ScourgeID) } else if id == ScourgeID { return g.GameBoard.GetRow(SentinalID) } else { return nil } } func (g *Game) GetGrave(id int) *Deck { if id == SentinalID { return g.SentinalGrave } else if id == ScourgeID { return g.ScourgeGrave } else { return nil } } func (g *Game) Bury(c *Card) { grave := g.GetGrave(c.Owner) grave.Cards = append(grave.Cards, c) }