From 562538479916452a89fdda0bb77aa68a676b7f47 Mon Sep 17 00:00:00 2001 From: Florian Fischer Date: Thu, 2 Feb 2023 00:13:30 +0100 Subject: change unit attack representation and implement sword --- go/game/attack.go | 39 +++++++++++++++++++++++++++++---------- go/game/cardImplementations.go | 17 +++++++++++++++++ go/game/range.go | 18 ++++++++++++++++++ go/game/state.go | 6 +++--- go/game/unit.go | 12 +++++------- go/ui/textBox.go | 2 +- 6 files changed, 73 insertions(+), 21 deletions(-) diff --git a/go/game/attack.go b/go/game/attack.go index 97881d48..35af7ff2 100644 --- a/go/game/attack.go +++ b/go/game/attack.go @@ -8,28 +8,41 @@ import ( ) type Attack struct { - Damage int - Range int + attacks []int } -func INVALID_ATTACK() Attack { - return Attack{-1, -1} +func INVALID_ATTACK(a Attack) bool { + return a.attacks == nil +} + +func (a Attack) MaxRange() int { + return len(a.attacks) +} + +func (a Attack) DamageInRange(r int) int { + if r > a.MaxRange() { + return 0 + } + + return a.attacks[r-1] } func (a Attack) String() string { - if a == INVALID_ATTACK() { + if INVALID_ATTACK(a) { return "invalid attack" } - if a.Range == 1 { - return fmt.Sprintf("%d", a.Damage) + if len(a.attacks) == 1 { + return fmt.Sprintf("%d", a.attacks[0]) } - return fmt.Sprintf("%d range %d", a.Damage, a.Range) + + r := a.MaxRange() + return fmt.Sprintf("%d range %d", a.DamageInRange(r), r) } func parseAttack(attack interface{}) Attack { if damage, ok := attack.(int); ok { - return Attack{damage, 1} + return Attack{[]int{damage}} } attackStr := attack.(string) @@ -46,5 +59,11 @@ func parseAttack(attack interface{}) Attack { log.Fatalf("Attack %s has no valid Range value\n", attackStr) } } - return Attack{a, r} + + attacks := make([]int, 0, r) + for i := 0; i < r; i++ { + attacks = append(attacks, a) + } + + return Attack{attacks} } diff --git a/go/game/cardImplementations.go b/go/game/cardImplementations.go index 8024e4fc..b20b46bc 100644 --- a/go/game/cardImplementations.go +++ b/go/game/cardImplementations.go @@ -46,10 +46,27 @@ func (impl *missionaryImplementation) fullAction(u *Unit) []*FullAction { return []*FullAction{NewFullAction(u, resolvePrototype, "put faith ...")} } +type swordImplementation struct { + cardImplementationBase +} + +func (*swordImplementation) onPile(p Permanent) { + if u, ok := p.(*Unit); ok && !INVALID_ATTACK(u.Attack) { + u.Attack.attacks[0] += 1 + } +} + +func (*swordImplementation) onDrop(p Permanent) { + if u, ok := p.(*Unit); ok && !INVALID_ATTACK(u.Attack) { + u.Attack.attacks[0] -= 1 + } +} + func init() { cardImplementations = map[string]cardImplementation{ "base/advisor": &advisorImplementation{}, "base/missionary": &missionaryImplementation{}, + "base/sword": &swordImplementation{}, "misc/farmer": &farmerImplementation{}, } diff --git a/go/game/range.go b/go/game/range.go index ab86c44e..6e04d21f 100644 --- a/go/game/range.go +++ b/go/game/range.go @@ -4,6 +4,24 @@ import ( "math" ) +func DistanceBetweenPositions(origin, pos Position) int { + x, y := float64(pos.X-origin.X), float64(pos.Y-origin.Y) + ax, ay := math.Abs(x), math.Abs(y) + + var abs float64 + if pos.X > pos.Y { + abs = ax + math.Floor(ay/2) + } else { + abs = ay + math.Floor(ax/2) + } + + return int(abs) +} + +func DistanceBetweenPermanents(p1, p2 Permanent) int { + return DistanceBetweenPositions(p1.Tile().Position, p2.Tile().Position) +} + func IsPositionInRange(origin Position, pos Position, r int) bool { x, y := float64(pos.X-origin.X), float64(pos.Y-origin.Y) ax, ay := math.Abs(x), math.Abs(y) diff --git a/go/game/state.go b/go/game/state.go index 1d4b644f..6ca100c8 100644 --- a/go/game/state.go +++ b/go/game/state.go @@ -139,14 +139,14 @@ func (s *State) IsValidAttack(a *AttackAction) error { } unit := a.Source().(Unit) - if unit.Attack == INVALID_ATTACK() { + if INVALID_ATTACK(unit.Attack) { return fmt.Errorf("%s can not attack", unit.card.Name) } target := a.Targets().sel[0].(Permanent) - if !IsPositionInRange(unit.tile.Position, target.Tile().Position, unit.Attack.Range) { + if !IsPositionInRange(unit.tile.Position, target.Tile().Position, unit.Attack.MaxRange()) { return fmt.Errorf("Attack target on %s is not in attack range %d of %s on %s", - target.Tile().Position.String(), unit.Attack.Range, unit.card.Name, + target.Tile().Position.String(), unit.Attack.MaxRange(), unit.card.Name, unit.tile.Position.String()) } diff --git a/go/game/unit.go b/go/game/unit.go index 5d85ff4b..c24e3901 100644 --- a/go/game/unit.go +++ b/go/game/unit.go @@ -87,10 +87,7 @@ func NewUnit(card *Card, tile *Tile, owner *Player) *Unit { } var attack Attack - attackDesc, found := card.Values["attack"] - if !found { - attack = INVALID_ATTACK() - } else { + if attackDesc, found := card.Values["attack"]; found { attack = parseAttack(attackDesc) } @@ -154,7 +151,7 @@ func (u *Unit) MoveRangeTiles(m *Map) []*Tile { } func (u *Unit) AttackableTiles(m *Map) []*Tile { - return TilesInRange(m, u, u.Attack.Range) + return TilesInRange(m, u, u.Attack.MaxRange()) } func (u *Unit) AttackablePermanents(m *Map) []Permanent { @@ -181,7 +178,8 @@ func (u *Unit) AttackableEnemyPermanents(m *Map) []Permanent { } func (u *Unit) Fight(p Permanent) { - DealDamage(u, p, u.Attack.Damage) + r := DistanceBetweenPermanents(p, u) + DealDamage(u, p, u.Attack.DamageInRange(r)) } func (u *Unit) IsDestroyed() bool { @@ -205,7 +203,7 @@ func (u *Unit) AvailActions() (actions []Action) { } m := u.Controller().gameState.Map - if u.AvailAttackActions > 0 && u.Attack != INVALID_ATTACK() && + if u.AvailAttackActions > 0 && !INVALID_ATTACK(u.Attack) && len(u.AttackableEnemyPermanents(m)) > 0 { actions = append(actions, NewAttackAction(u)) diff --git a/go/ui/textBox.go b/go/ui/textBox.go index 78199410..b4a580bf 100644 --- a/go/ui/textBox.go +++ b/go/ui/textBox.go @@ -61,7 +61,7 @@ func NewUnitInfo(x, y int, u *game.Unit) *TextBox { info = fmt.Sprintf("%s\nMovement: %s", info, u.Movement.String()) } - if u.Attack != game.INVALID_ATTACK() { + if !game.INVALID_ATTACK(u.Attack) { info = fmt.Sprintf("%s\nAttack: %s", info, u.Attack.String()) } -- cgit v1.2.3