From 899ef7448222f0d087c194b3379b702f8dd2ff8c Mon Sep 17 00:00:00 2001 From: ewy Date: Mon, 14 Sep 2026 19:48:36 +0200 Subject: add server and websocket comms --- engine/action.go | 62 ++++++++++++++++++++++++++++++++++++++ engine/card.go | 16 ++++++++++ engine/card/attack.go | 26 ++++++++++++++-- engine/card/card.go | 16 ---------- engine/card/damage.go | 4 +++ engine/card/predicate/predicate.go | 18 +++++++++++ engine/card/reaction.go | 7 +++++ engine/character.go | 11 +++++++ engine/character/character.go | 15 --------- engine/game.go | 47 +++++++++++++++++++++++++++++ engine/game/game.go | 33 -------------------- engine/interaction.go | 3 ++ engine/play/play.go | 52 ++++++++++++++++++++++++++++++++ engine/player.go | 42 ++++++++++++++++++++++++++ engine/player/action.go | 12 -------- engine/player/player.go | 34 --------------------- engine/player/vigour.go | 52 -------------------------------- engine/stack.go | 22 ++++++++++++++ engine/vigour.go | 52 ++++++++++++++++++++++++++++++++ 19 files changed, 359 insertions(+), 165 deletions(-) create mode 100644 engine/action.go create mode 100644 engine/card.go delete mode 100644 engine/card/card.go create mode 100644 engine/card/predicate/predicate.go create mode 100644 engine/card/reaction.go create mode 100644 engine/character.go delete mode 100644 engine/character/character.go create mode 100644 engine/game.go delete mode 100644 engine/game/game.go create mode 100644 engine/interaction.go create mode 100644 engine/play/play.go create mode 100644 engine/player.go delete mode 100644 engine/player/action.go delete mode 100644 engine/player/player.go delete mode 100644 engine/player/vigour.go create mode 100644 engine/stack.go create mode 100644 engine/vigour.go (limited to 'engine') diff --git a/engine/action.go b/engine/action.go new file mode 100644 index 0000000..2a73982 --- /dev/null +++ b/engine/action.go @@ -0,0 +1,62 @@ +package engine + +type BaseAction struct { + User *Player + BaseSource IsCard +} + +func (b *BaseAction) Actor() *Player { + return b.User +} + +func (b *BaseAction) Source() IsCard { + return b.BaseSource +} + +func (b *BaseAction) Then(g *Game) { +} + +type Action interface { + Actor() *Player + Source() IsCard + Then(g *Game) +} + +func Interactions(a ...Action) map[InteractionID]Action { + m := make(map[InteractionID]Action, len(a)) + for i, ac := range a { + m[InteractionID(i)] = ac + } + return m +} + +type DamageDecision bool + +var ( + Aura DamageDecision = true + Life DamageDecision = false +) + +type DesignateDamage struct { + Attacker *Player + Defender *Player + SourceCard *Card +} + +func (d *DesignateDamage) Actor() *Player { + return d.Defender +} +func (d *DesignateDamage) Instigator() *Player { + return d.Attacker +} + +func (d *DesignateDamage) Then(g *Game) { +} + +func (d *DesignateDamage) Source() IsCard { + return d.SourceCard +} + +type PlayCard struct { + *BaseAction +} diff --git a/engine/card.go b/engine/card.go new file mode 100644 index 0000000..52d5a6e --- /dev/null +++ b/engine/card.go @@ -0,0 +1,16 @@ +package engine + +type Card struct { + Name string + AllOut bool + Text string + Flavor string +} + +func (c *Card) Base() *Card { + return c +} + +type IsCard interface { + Base() *Card +} diff --git a/engine/card/attack.go b/engine/card/attack.go index 19ad196..d05d44a 100644 --- a/engine/card/attack.go +++ b/engine/card/attack.go @@ -1,11 +1,31 @@ package card +import ( + "sakura/engine" + "sakura/engine/pool" +) + type Attack struct { - *Card + *engine.Card MinDistance RangeValue MaxDistance RangeValue - AuraDamage DamageValue - HealthDamage DamageValue + AuraDamage DamageValue + LifeDamage DamageValue + + Unreactable bool +} + +func (a *Attack) Attack() *Attack { + return a +} + +func (a *Attack) Do(g *engine.Game, to *pool.Pool) { +} + +type IsAttack interface { + Attack() *Attack + Prepare(g *engine.Game) + Do(g *engine.Game, to *pool.Pool) } diff --git a/engine/card/card.go b/engine/card/card.go deleted file mode 100644 index f2dce83..0000000 --- a/engine/card/card.go +++ /dev/null @@ -1,16 +0,0 @@ -package card - -type Card struct { - Name string - AllOut bool - Text string - Flavor string -} - -func (c *Card) Base() *Card { - return c -} - -type IsCard interface { - Base() *Card -} diff --git a/engine/card/damage.go b/engine/card/damage.go index fccb073..6d7dafd 100644 --- a/engine/card/damage.go +++ b/engine/card/damage.go @@ -3,3 +3,7 @@ package card type DamageValue int const Unselectable = DamageValue(-1) + +func (d DamageValue) Value() int { + return int(d) +} diff --git a/engine/card/predicate/predicate.go b/engine/card/predicate/predicate.go new file mode 100644 index 0000000..5770c3d --- /dev/null +++ b/engine/card/predicate/predicate.go @@ -0,0 +1,18 @@ +package predicate + +import ( + "sakura/engine" + "sakura/engine/card" +) + +func Reaction(c engine.IsCard) bool { + _, ok := c.(card.IsReaction) + return ok +} + +func ReactionApplicable(c engine.IsCard) bool { + r, isReaction := c.(card.IsReaction) + if isReaction { + return r.Applicable(c) + } +} diff --git a/engine/card/reaction.go b/engine/card/reaction.go new file mode 100644 index 0000000..3eed8c4 --- /dev/null +++ b/engine/card/reaction.go @@ -0,0 +1,7 @@ +package card + +import "sakura/engine" + +type IsReaction interface { + Applicable(c engine.IsCard) bool +} diff --git a/engine/character.go b/engine/character.go new file mode 100644 index 0000000..a526501 --- /dev/null +++ b/engine/character.go @@ -0,0 +1,11 @@ +package engine + +type Character struct { + Name string + Cards []IsCard + Ultimates []IsCard +} + +func (c *Character) String() string { + return c.Name +} diff --git a/engine/character/character.go b/engine/character/character.go deleted file mode 100644 index 66671ae..0000000 --- a/engine/character/character.go +++ /dev/null @@ -1,15 +0,0 @@ -package character - -import ( - "sakura/engine/card" -) - -type Character struct { - Name string - Cards []card.IsCard - Ultimates []card.IsCard -} - -func (c *Character) String() string { - return c.Name -} diff --git a/engine/game.go b/engine/game.go new file mode 100644 index 0000000..0ed93e2 --- /dev/null +++ b/engine/game.go @@ -0,0 +1,47 @@ +package engine + +import ( + "sakura/engine/pool" +) + +type Game struct { + Shadow *pool.Pool + Distance *pool.Pool + Yin *Player + Yang *Player + + Stack []IsCard + + Actions map[InteractionID]Action +} + +func (g *Game) propagate() { + g.Distance.OverflowsTo = g.Shadow +} + +func NewGame() *Game { + g := &Game{ + Shadow: pool.New(0, pool.Infinite), + Distance: pool.New(0, 10), + Actions: nil, + } + g.Yin = NewPlayer(g) + g.Yang = NewPlayer(g) + g.propagate() + return g +} + +var Tie = &Player{} + +func (g *Game) Winner() *Player { + if g.Yin.Life.Value <= 0 && g.Yang.Life.Value <= 0 { + return Tie + } + if g.Yin.Life.Value <= 0 { + return g.Yang + } + if g.Yang.Life.Value <= 0 { + return g.Yin + } + return nil +} diff --git a/engine/game/game.go b/engine/game/game.go deleted file mode 100644 index 7f3fa76..0000000 --- a/engine/game/game.go +++ /dev/null @@ -1,33 +0,0 @@ -package game - -import ( - "sakura/engine/player" - "sakura/engine/pool" -) - -type Game struct { - Shadow *pool.Pool - Distance *pool.Pool - Yin *player.Player - Yang *player.Player - - Must []player.PlayerAction - May []player.PlayerAction -} - -func (g *Game) propagate() { - g.Distance.OverflowsTo = g.Shadow -} - -func New() *Game { - g := &Game{ - Shadow: pool.New(0, pool.Infinite), - Distance: pool.New(0, 10), - Must: nil, - May: nil, - } - g.Yin = player.New(g) - g.Yang = player.New(g) - g.propagate() - return g -} diff --git a/engine/interaction.go b/engine/interaction.go new file mode 100644 index 0000000..0c01401 --- /dev/null +++ b/engine/interaction.go @@ -0,0 +1,3 @@ +package engine + +type InteractionID int diff --git a/engine/play/play.go b/engine/play/play.go new file mode 100644 index 0000000..42d9fa3 --- /dev/null +++ b/engine/play/play.go @@ -0,0 +1,52 @@ +package play + +import ( + "sakura/engine" + "sakura/engine/card" + "sakura/engine/card/predicate" +) + +func Card(g *engine.Game, p *engine.Player, anyCard engine.IsCard) { + switch c := anyCard.(type) { + case card.IsAttack: + Attack(g, p, c) + } + g.Stack = append(g.Stack, anyCard) +} + +func Attack(g *engine.Game, p *engine.Player, a card.IsAttack) { + att := a.Attack() + opp := p.Opponent(g) + if !att.Unreactable && opp.Hand.Contains(predicate.ReactionApplicable) { + reactions := opp.Hand.Filter(predicate.ReactionApplicable) + acs := make([]engine.Action, len(reactions), len(reactions)) + for i, r := range reactions { + acs[i] = &engine.PlayCard{ + BaseAction: &engine.BaseAction{ + User: p, + BaseSource: r, + }, + } + } + g.May = engine.Interactions(acs...) + } + switch { + case att.AuraDamage == card.Unselectable: + opp.Life.Subtract(att.LifeDamage.Value()) + case att.LifeDamage == card.Unselectable: + opp.Aura.Subtract(att.AuraDamage.Value()) + case att.AuraDamage.Value() > opp.Aura.Value: + opp.Life.Subtract(att.LifeDamage.Value()) + default: + g.Actions = engine.Interactions(&engine.DesignateDamage{ + Attacker: p, + Defender: opp, + SourceCard: a.Attack().Card, + }) + } +} + +func Vigor(g *engine.Game, p *engine.Player, a engine.VigorAction) { + a.From(p).Subtract(1) + a.To(p).Add(1) +} diff --git a/engine/player.go b/engine/player.go new file mode 100644 index 0000000..5f1d7ca --- /dev/null +++ b/engine/player.go @@ -0,0 +1,42 @@ +package engine + +import ( + "sakura/engine/pool" +) + +type Player struct { + Game *Game `json:"-"` + Vigour Vigour + Life *pool.Pool + Aura *pool.Pool + Flare *pool.Pool + Hand Stack + MasterDeck Stack + Ultimates []*Card +} + +func (p *Player) Opponent(g *Game) *Player { + if p == g.Yin { + return g.Yang + } else { + return g.Yin + } +} + +func (p *Player) propagate(g *Game) { + p.Game = g + p.Aura.SubtractsTo = g.Shadow + p.Flare.SubtractsTo = g.Shadow + p.Life.SubtractsTo = p.Flare +} + +func NewPlayer(g *Game) *Player { + p := &Player{ + Life: pool.New(pool.Empty, 10), + Aura: pool.New(3, 5), + Flare: pool.New(pool.Empty, pool.Infinite), + Vigour: Vigour(0), + } + p.propagate(g) + return p +} diff --git a/engine/player/action.go b/engine/player/action.go deleted file mode 100644 index 0d8aa58..0000000 --- a/engine/player/action.go +++ /dev/null @@ -1,12 +0,0 @@ -package player - -type Action int - -const ( - _ = iota -) - -type PlayerAction struct { - Player *Player - Action Action -} diff --git a/engine/player/player.go b/engine/player/player.go deleted file mode 100644 index 9dece55..0000000 --- a/engine/player/player.go +++ /dev/null @@ -1,34 +0,0 @@ -package player - -import ( - "sakura/engine/card" - "sakura/engine/game" - "sakura/engine/pool" -) - -type Player struct { - Game *game.Game - Vigour *Vigour - Life *pool.Pool - Aura *pool.Pool - Flare *pool.Pool - MasterDeck []*card.Card - Ultimates []*card.Card -} - -func (p *Player) propagate(g *game.Game) { - p.Game = g - p.Aura.SubtractsTo = g.Shadow - p.Flare.SubtractsTo = g.Shadow - p.Life.SubtractsTo = p.Flare -} - -func New(g *game.Game) *Player { - p := &Player{ - Life: pool.New(pool.Empty, 10), - Aura: pool.New(3, 5), - Flare: pool.New(pool.Empty, pool.Infinite), - } - p.propagate(g) - return p -} diff --git a/engine/player/vigour.go b/engine/player/vigour.go deleted file mode 100644 index 1d4022e..0000000 --- a/engine/player/vigour.go +++ /dev/null @@ -1,52 +0,0 @@ -package player - -import ( - "sakura/engine/pool" -) - -type Vigour int - -type VigorAction struct { - Name string - From func(p *Player) *pool.Pool - To func(p *Player) *pool.Pool -} - -var Actions = []*VigorAction{ - { - Name: "Approach", - From: func(p *Player) *pool.Pool { - return p.Game.Distance - }, - To: func(p *Player) *pool.Pool { - return p.Aura - }, - }, - { - Name: "Retreat", - From: func(p *Player) *pool.Pool { - return p.Aura - }, - To: func(p *Player) *pool.Pool { - return p.Game.Distance - }, - }, - { - Name: "Charge", - From: func(p *Player) *pool.Pool { - return p.Aura - }, - To: func(p *Player) *pool.Pool { - return p.Flare - }, - }, - { - Name: "Recover", - From: func(p *Player) *pool.Pool { - return p.Game.Shadow - }, - To: func(p *Player) *pool.Pool { - return p.Aura - }, - }, -} diff --git a/engine/stack.go b/engine/stack.go new file mode 100644 index 0000000..d7fc7be --- /dev/null +++ b/engine/stack.go @@ -0,0 +1,22 @@ +package engine + +type Stack []*Card + +func (s Stack) Contains(predicate func(c IsCard) bool) bool { + for _, c := range s { + if predicate(c) { + return true + } + } + return false +} + +func (s Stack) Filter(predicate func(c IsCard) bool) Stack { + res := make(Stack, 0, len(s)) + for _, c := range s { + if predicate(c) { + res = append(res, c) + } + } + return res +} diff --git a/engine/vigour.go b/engine/vigour.go new file mode 100644 index 0000000..62ececd --- /dev/null +++ b/engine/vigour.go @@ -0,0 +1,52 @@ +package engine + +import ( + "sakura/engine/pool" +) + +type Vigour int + +type VigorAction struct { + Name string + From func(p *Player) *pool.Pool + To func(p *Player) *pool.Pool +} + +var Actions = []*VigorAction{ + { + Name: "Approach", + From: func(p *Player) *pool.Pool { + return p.Game.Distance + }, + To: func(p *Player) *pool.Pool { + return p.Aura + }, + }, + { + Name: "Retreat", + From: func(p *Player) *pool.Pool { + return p.Aura + }, + To: func(p *Player) *pool.Pool { + return p.Game.Distance + }, + }, + { + Name: "Charge", + From: func(p *Player) *pool.Pool { + return p.Aura + }, + To: func(p *Player) *pool.Pool { + return p.Flare + }, + }, + { + Name: "Recover", + From: func(p *Player) *pool.Pool { + return p.Game.Shadow + }, + To: func(p *Player) *pool.Pool { + return p.Aura + }, + }, +} -- cgit v1.3.1