aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2023-02-02 00:13:30 +0100
committerFlorian Fischer <florian.fischer@muhq.space>2025-01-27 16:43:46 +0100
commit6034149d014e999b17247af75b98913e4e4c68af (patch)
tree8a1a105b543b44307d3cbab9e0805d882448f02a
parentd9a41e478c59e03cc5409fdc8090319ada42957c (diff)
downloadmuhqs-game-6034149d014e999b17247af75b98913e4e4c68af.tar.gz
muhqs-game-6034149d014e999b17247af75b98913e4e4c68af.zip
change unit attack representation and implement sword
-rw-r--r--go/game/attack.go39
-rw-r--r--go/game/cardImplementations.go17
-rw-r--r--go/game/range.go18
-rw-r--r--go/game/state.go6
-rw-r--r--go/game/unit.go12
-rw-r--r--go/ui/textBox.go2
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())
}