diff options
| -rw-r--r-- | .pik/serve.sh | 2 | ||||
| -rw-r--r-- | data/cirno/cirno.go | 5 | ||||
| -rw-r--r-- | data/dummy/cards.go | 10 | ||||
| -rw-r--r-- | engine/card.go | 20 | ||||
| -rw-r--r-- | engine/game.go | 45 | ||||
| -rw-r--r-- | engine/player.go | 16 | ||||
| -rw-r--r-- | engine/stack.go | 6 | ||||
| -rw-r--r-- | server/api/game.go | 60 | ||||
| -rw-r--r-- | server/server.go | 3 |
9 files changed, 141 insertions, 26 deletions
diff --git a/.pik/serve.sh b/.pik/serve.sh new file mode 100644 index 0000000..3b47db1 --- /dev/null +++ b/.pik/serve.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +go run -tags server . diff --git a/data/cirno/cirno.go b/data/cirno/cirno.go index d0d89c3..612697a 100644 --- a/data/cirno/cirno.go +++ b/data/cirno/cirno.go @@ -1,11 +1,12 @@ package cirno import ( + "sakura/data/dummy" "sakura/engine" ) var Cirno = &engine.Character{ Name: "Cirno", - Cards: nil, - Ultimates: nil, + Cards: dummy.Dummy.Cards, + Ultimates: dummy.Dummy.Ultimates, } diff --git a/data/dummy/cards.go b/data/dummy/cards.go index 7fde0c8..10c438f 100644 --- a/data/dummy/cards.go +++ b/data/dummy/cards.go @@ -13,9 +13,11 @@ var Cards = []engine.IsCard{ Text: "Attack a little", Flavor: "", }, - MinDistance: card.NoMinimumRange, - MaxDistance: card.NoMaximumRange, - AuraDamage: 1, - LifeDamage: 1, + Range: &card.Range{ + MinimumRange: card.NoMinimumRange, + MaximumRange: card.NoMaximumRange, + }, + AuraDamage: 1, + LifeDamage: 1, }, } diff --git a/engine/card.go b/engine/card.go index 3eb6a8f..fce1af0 100644 --- a/engine/card.go +++ b/engine/card.go @@ -1,5 +1,7 @@ package engine +import "slices" + type Card struct { Name string AllOut bool @@ -19,3 +21,21 @@ type IsCard interface { Base() *Card CanPlay(g *Game) bool } + +type CardInstance struct { + *Card + Visible bool +} + +var secretCardStruct = &Card{ + Name: "?", +} + +var SecretCard = &CardInstance{ + Card: secretCardStruct, + Visible: false, +} + +func Secrets(size int) Stack { + return slices.Repeat([]IsCard{SecretCard}, size) +} diff --git a/engine/game.go b/engine/game.go index 8365869..0735958 100644 --- a/engine/game.go +++ b/engine/game.go @@ -5,22 +5,24 @@ import ( ) type Turn struct { - Player *Player + Player *Player `json:"-"` + PlayerSlot string `json:"Player"` ActionTaken bool - Actions []Action + Actions []Action } type Game struct { Shadow *pool.Pool Distance *pool.Pool - Yin *Player - Yang *Player + Yin *Player `json:"PlayerYin"` + Yang *Player `json:"PlayerYang"` Stack []IsCard Actions []Action - CurrentPlayer *Player + CurrentPlayer *Player `json:"-"` + CurrentPlayerSlot string `json:"CurrentPlayer"` History []Turn } @@ -59,18 +61,45 @@ func (g *Game) Winner() *Player { func (g *Game) NextTurn() { current := g.CurrentPlayer.Opponent(g) g.CurrentPlayer = current + playerRef := "UNKNOWN" + switch current { + case g.Yin: + playerRef = "YIN" + case g.Yang: + playerRef = "YANG" + } g.History = append(g.History, Turn{ - Player: current, + Player: current, ActionTaken: false, + PlayerSlot: playerRef, }) - g.Actions = Interactions(ShuffleDeckAction(g),DrawCardAction(g)), + g.CurrentPlayerSlot = playerRef + g.Actions = Interactions(ShuffleDeckAction(g), DrawCardAction(g)) } func (g *Game) CurrentTurn() Turn { - return g.History[len(g.History) - 1] + return g.History[len(g.History)-1] } func (g *Game) ActionToHistory(a Action) { thisTurn := g.History[len(g.History)-1] thisTurn.Actions = append(thisTurn.Actions, a) } + +func (g *Game) Present(p *Player) Game { + localGame := *g + switch p { + case g.Yin: + localGame.Yang.Hide() + case g.Yang: + localGame.Yin.Hide() + } + var acs []Action + for _, a := range g.Actions { + if a.Actor(g) == p { + acs = append(acs, a) + } + } + localGame.Actions = acs + return localGame +} diff --git a/engine/player.go b/engine/player.go index eb7ac49..a4f7c35 100644 --- a/engine/player.go +++ b/engine/player.go @@ -13,7 +13,15 @@ type Player struct { Hand Stack MasterDeck Stack DrawPile Stack - Ultimates []*Card + Ultimates []IsCard + Character *Character + Cookie string +} + +func (p *Player) SetCharacter(c *Character) { + p.Character = c + p.MasterDeck = c.Cards + p.Ultimates = c.Ultimates } func (p *Player) Opponent(g *Game) *Player { @@ -41,3 +49,9 @@ func NewPlayer(g *Game) *Player { p.propagate(g) return p } + +func (p *Player) Hide() { + p.MasterDeck = p.MasterDeck.Hidden() + p.Hand = p.Hand.Hidden() + p.DrawPile = p.DrawPile.Hidden() +} diff --git a/engine/stack.go b/engine/stack.go index 8bcff6f..dd10dd6 100644 --- a/engine/stack.go +++ b/engine/stack.go @@ -1,6 +1,6 @@ package engine -type Stack []*Card +type Stack []IsCard func (s Stack) Contains(predicate func(c IsCard) bool) bool { for _, c := range s { @@ -30,3 +30,7 @@ func (s Stack) Pop() IsCard { return c } } + +func (s Stack) Hidden() Stack { + return Secrets(len(s)) +} diff --git a/server/api/game.go b/server/api/game.go index 636ff66..d830d71 100644 --- a/server/api/game.go +++ b/server/api/game.go @@ -2,6 +2,7 @@ package api import ( "net/http" + "sakura/data/cirno" "sakura/engine" "slices" "uuid" @@ -20,7 +21,11 @@ var connections = make(map[uuid.UUID][]*Conn) func GameCreate(w http.ResponseWriter, r *http.Request) { id := uuid.NewV7() - games[id] = engine.NewGame() + g := engine.NewGame() + games[id] = g + g.Yin.SetCharacter(cirno.Cirno) + g.Yang.SetCharacter(cirno.Cirno) + w.Write([]byte(id.String())) } @@ -36,7 +41,10 @@ func GameConnect(w http.ResponseWriter, r *http.Request) { http.Error(w, "game not found", http.StatusNotFound) return } - wsConn, err := upgrader.Upgrade(w, r, nil) + p, c := Bind(game, w, r) + wsConn, err := upgrader.Upgrade(w, r, http.Header{ + "Set-Cookie": []string{c.String()}, + }) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return @@ -53,12 +61,47 @@ func GameConnect(w http.ResponseWriter, r *http.Request) { }) return nil }) - err = wsConn.WriteJSON(game) + go PumpRead(uid, game, p, wsConn) +} + +func Bind(g *engine.Game, w http.ResponseWriter, r *http.Request) (*engine.Player, *http.Cookie) { + cookie, err := r.Cookie("User") + newUser := false + + var id string if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return + newUser = true + uid := uuid.NewV7() + id = uid.String() + cookie = &http.Cookie{ + Name: "User", + Value: id, + Quoted: true, + Secure: true, + HttpOnly: false, + Path: "/", + SameSite: http.SameSiteStrictMode, + } + http.SetCookie(w, cookie) + } else { + id = cookie.Value } - go PumpRead(uid, game, wsConn) + + switch { + case newUser && g.Yin.Cookie == "": + g.Yin.Cookie = id + return g.Yin, cookie + case newUser && g.Yang.Cookie == "": + g.Yang.Cookie = id + return g.Yang, cookie + case g.Yang.Cookie == id: + return g.Yang, cookie + case g.Yin.Cookie == id: + return g.Yin, cookie + default: + return nil, cookie + } + } func Update(id uuid.UUID, g *engine.Game) { @@ -67,9 +110,11 @@ func Update(id uuid.UUID, g *engine.Game) { } } -func PumpRead(id uuid.UUID, g *engine.Game, conn *websocket.Conn) { +func PumpRead(id uuid.UUID, g *engine.Game, p *engine.Player, conn *websocket.Conn) { var interaction engine.InteractionID for { + strippedGame := g.Present(p) + Update(id, &strippedGame) err := conn.ReadJSON(&interaction) if err != nil { break @@ -80,6 +125,5 @@ func PumpRead(id uuid.UUID, g *engine.Game, conn *websocket.Conn) { } a.Then(g) g.ActionToHistory(a) - Update(id, g) } } diff --git a/server/server.go b/server/server.go index af8393d..f6773cb 100644 --- a/server/server.go +++ b/server/server.go @@ -21,11 +21,10 @@ func Start() { fmt.Println("Server started") addr := net.JoinHostPort(*cfg.Host, strconv.Itoa(*cfg.Port)) fmt.Printf("Listening on %s\n", addr) - var err error + sub, err := fs.Sub(web, "web") if err != nil { panic(err) } - sub, err := fs.Sub(web, "web") http.Handle("/", http.FileServer(http.FS(sub))) http.HandleFunc(cfg.CreateMethod+" "+cfg.CreatePath, api.GameCreate) http.HandleFunc(cfg.ConnectMethod+" "+cfg.ConnectPath, api.GameConnect) |
